From d624bee1511eedd08f0949e5d8b6b1e06a84babd Mon Sep 17 00:00:00 2001 From: Judah Caruso Date: Wed, 14 May 2025 16:52:27 -0600 Subject: [PATCH] added handmademath bindings --- hmm/HandmadeMath.h | 3939 ++++++++++++++++++++++++++++++ hmm/README | 49 + hmm/examples/degrees.jai | 14 + hmm/examples/radians.jai | 14 + hmm/examples/turns.jai | 14 + hmm/generate.jai | 195 ++ hmm/hmm_nosimd.jai | 595 +++++ hmm/hmm_simd.jai | 508 ++++ hmm/linux/.need-to-run-generate | 0 hmm/mac/hmm_degrees_nosimd.a | Bin 0 -> 24136 bytes hmm/mac/hmm_degrees_nosimd.dylib | Bin 0 -> 40456 bytes hmm/mac/hmm_degrees_simd.a | Bin 0 -> 24648 bytes hmm/mac/hmm_degrees_simd.dylib | Bin 0 -> 40456 bytes hmm/mac/hmm_radians_nosimd.a | Bin 0 -> 23880 bytes hmm/mac/hmm_radians_nosimd.dylib | Bin 0 -> 40456 bytes hmm/mac/hmm_radians_simd.a | Bin 0 -> 24360 bytes hmm/mac/hmm_radians_simd.dylib | Bin 0 -> 40456 bytes hmm/mac/hmm_turns_nosimd.a | Bin 0 -> 24136 bytes hmm/mac/hmm_turns_nosimd.dylib | Bin 0 -> 40456 bytes hmm/mac/hmm_turns_simd.a | Bin 0 -> 24648 bytes hmm/mac/hmm_turns_simd.dylib | Bin 0 -> 40456 bytes hmm/module.jai | 125 + hmm/win/.need-to-run-generate | 0 23 files changed, 5453 insertions(+) create mode 100644 hmm/HandmadeMath.h create mode 100644 hmm/README create mode 100644 hmm/examples/degrees.jai create mode 100644 hmm/examples/radians.jai create mode 100644 hmm/examples/turns.jai create mode 100644 hmm/generate.jai create mode 100644 hmm/hmm_nosimd.jai create mode 100644 hmm/hmm_simd.jai create mode 100644 hmm/linux/.need-to-run-generate create mode 100644 hmm/mac/hmm_degrees_nosimd.a create mode 100755 hmm/mac/hmm_degrees_nosimd.dylib create mode 100644 hmm/mac/hmm_degrees_simd.a create mode 100755 hmm/mac/hmm_degrees_simd.dylib create mode 100644 hmm/mac/hmm_radians_nosimd.a create mode 100755 hmm/mac/hmm_radians_nosimd.dylib create mode 100644 hmm/mac/hmm_radians_simd.a create mode 100755 hmm/mac/hmm_radians_simd.dylib create mode 100644 hmm/mac/hmm_turns_nosimd.a create mode 100755 hmm/mac/hmm_turns_nosimd.dylib create mode 100644 hmm/mac/hmm_turns_simd.a create mode 100755 hmm/mac/hmm_turns_simd.dylib create mode 100644 hmm/module.jai create mode 100644 hmm/win/.need-to-run-generate diff --git a/hmm/HandmadeMath.h b/hmm/HandmadeMath.h new file mode 100644 index 0000000..8e7b8cd --- /dev/null +++ b/hmm/HandmadeMath.h @@ -0,0 +1,3939 @@ +/* + HandmadeMath.h v2.0.0 + + This is a single header file with a bunch of useful types and functions for + games and graphics. Consider it a lightweight alternative to GLM that works + both C and C++. + + ============================================================================= + CONFIG + ============================================================================= + + By default, all angles in Handmade Math are specified in radians. However, it + can be configured to use degrees or turns instead. Use one of the following + defines to specify the default unit for angles: + + #define HANDMADE_MATH_USE_RADIANS + #define HANDMADE_MATH_USE_DEGREES + #define HANDMADE_MATH_USE_TURNS + + Regardless of the default angle, you can use the following functions to + specify an angle in a particular unit: + + HMM_AngleRad(radians) + HMM_AngleDeg(degrees) + HMM_AngleTurn(turns) + + The definitions of these functions change depending on the default unit. + + ----------------------------------------------------------------------------- + + Handmade Math ships with SSE (SIMD) implementations of several common + operations. To disable the use of SSE intrinsics, you must define + HANDMADE_MATH_NO_SSE before including this file: + + #define HANDMADE_MATH_NO_SSE + #include "HandmadeMath.h" + + ----------------------------------------------------------------------------- + + To use Handmade Math without the C runtime library, you must provide your own + implementations of basic math functions. Otherwise, HandmadeMath.h will use + the runtime library implementation of these functions. + + Define HANDMADE_MATH_PROVIDE_MATH_FUNCTIONS and provide your own + implementations of HMM_SINF, HMM_COSF, HMM_TANF, HMM_ACOSF, and HMM_SQRTF + before including HandmadeMath.h, like so: + + #define HANDMADE_MATH_PROVIDE_MATH_FUNCTIONS + #define HMM_SINF MySinF + #define HMM_COSF MyCosF + #define HMM_TANF MyTanF + #define HMM_ACOSF MyACosF + #define HMM_SQRTF MySqrtF + #include "HandmadeMath.h" + + By default, it is assumed that your math functions take radians. To use + different units, you must define HMM_ANGLE_USER_TO_INTERNAL and + HMM_ANGLE_INTERNAL_TO_USER. For example, if you want to use degrees in your + code but your math functions use turns: + + #define HMM_ANGLE_USER_TO_INTERNAL(a) ((a)*HMM_DegToTurn) + #define HMM_ANGLE_INTERNAL_TO_USER(a) ((a)*HMM_TurnToDeg) + + ============================================================================= + + LICENSE + + This software is in the public domain. Where that dedication is not + recognized, you are granted a perpetual, irrevocable license to copy, + distribute, and modify this file as you see fit. + + ============================================================================= + + CREDITS + + Originally written by Zakary Strange. + + Functionality: + Zakary Strange (strangezak@protonmail.com && @strangezak) + Matt Mascarenhas (@miblo_) + Aleph + FieryDrake (@fierydrake) + Gingerbill (@TheGingerBill) + Ben Visness (@bvisness) + Trinton Bullard (@Peliex_Dev) + @AntonDan + Logan Forman (@dev_dwarf) + + Fixes: + Jeroen van Rijn (@J_vanRijn) + Kiljacken (@Kiljacken) + Insofaras (@insofaras) + Daniel Gibson (@DanielGibson) +*/ + +#ifndef HANDMADE_MATH_H +#define HANDMADE_MATH_H + +// Dummy macros for when test framework is not present. +#ifndef COVERAGE +# define COVERAGE(a, b) +#endif + +#ifndef ASSERT_COVERED +# define ASSERT_COVERED(a) +#endif + +#ifdef HANDMADE_MATH_NO_SSE +# warning "HANDMADE_MATH_NO_SSE is deprecated, use HANDMADE_MATH_NO_SIMD instead" +# define HANDMADE_MATH_NO_SIMD +#endif + +/* let's figure out if SSE is really available (unless disabled anyway) + (it isn't on non-x86/x86_64 platforms or even x86 without explicit SSE support) + => only use "#ifdef HANDMADE_MATH__USE_SSE" to check for SSE support below this block! */ +#ifndef HANDMADE_MATH_NO_SIMD +# ifdef _MSC_VER /* MSVC supports SSE in amd64 mode or _M_IX86_FP >= 1 (2 means SSE2) */ +# if defined(_M_AMD64) || ( defined(_M_IX86_FP) && _M_IX86_FP >= 1 ) +# define HANDMADE_MATH__USE_SSE 1 +# endif +# else /* not MSVC, probably GCC, clang, icc or something that doesn't support SSE anyway */ +# ifdef __SSE__ /* they #define __SSE__ if it's supported */ +# define HANDMADE_MATH__USE_SSE 1 +# endif /* __SSE__ */ +# endif /* not _MSC_VER */ +# ifdef __ARM_NEON +# define HANDMADE_MATH__USE_NEON 1 +# endif /* NEON Supported */ +#endif /* #ifndef HANDMADE_MATH_NO_SIMD */ + +#if (!defined(__cplusplus) && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L) +# define HANDMADE_MATH__USE_C11_GENERICS 1 +#endif + +#ifdef HANDMADE_MATH__USE_SSE +# include +#endif + +#ifdef HANDMADE_MATH__USE_NEON +# include +#endif + +#ifdef _MSC_VER +#pragma warning(disable:4201) +#endif + +#if defined(__GNUC__) || defined(__clang__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wfloat-equal" +# pragma GCC diagnostic ignored "-Wmissing-braces" +# ifdef __clang__ +# pragma GCC diagnostic ignored "-Wgnu-anonymous-struct" +# pragma GCC diagnostic ignored "-Wmissing-field-initializers" +# endif +#endif + +#if defined(__GNUC__) || defined(__clang__) +# define HMM_DEPRECATED(msg) __attribute__((deprecated(msg))) +#elif defined(_MSC_VER) +# define HMM_DEPRECATED(msg) __declspec(deprecated(msg)) +#else +# define HMM_DEPRECATED(msg) +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + +#if !defined(HANDMADE_MATH_USE_DEGREES) \ + && !defined(HANDMADE_MATH_USE_TURNS) \ + && !defined(HANDMADE_MATH_USE_RADIANS) +# define HANDMADE_MATH_USE_RADIANS +#endif + +#define HMM_PI 3.14159265358979323846 +#define HMM_PI32 3.14159265359f +#define HMM_DEG180 180.0 +#define HMM_DEG18032 180.0f +#define HMM_TURNHALF 0.5 +#define HMM_TURNHALF32 0.5f +#define HMM_RadToDeg ((float)(HMM_DEG180/HMM_PI)) +#define HMM_RadToTurn ((float)(HMM_TURNHALF/HMM_PI)) +#define HMM_DegToRad ((float)(HMM_PI/HMM_DEG180)) +#define HMM_DegToTurn ((float)(HMM_TURNHALF/HMM_DEG180)) +#define HMM_TurnToRad ((float)(HMM_PI/HMM_TURNHALF)) +#define HMM_TurnToDeg ((float)(HMM_DEG180/HMM_TURNHALF)) + +#if defined(HANDMADE_MATH_USE_RADIANS) +# define HMM_AngleRad(a) (a) +# define HMM_AngleDeg(a) ((a)*HMM_DegToRad) +# define HMM_AngleTurn(a) ((a)*HMM_TurnToRad) +#elif defined(HANDMADE_MATH_USE_DEGREES) +# define HMM_AngleRad(a) ((a)*HMM_RadToDeg) +# define HMM_AngleDeg(a) (a) +# define HMM_AngleTurn(a) ((a)*HMM_TurnToDeg) +#elif defined(HANDMADE_MATH_USE_TURNS) +# define HMM_AngleRad(a) ((a)*HMM_RadToTurn) +# define HMM_AngleDeg(a) ((a)*HMM_DegToTurn) +# define HMM_AngleTurn(a) (a) +#endif + +#if !defined(HANDMADE_MATH_PROVIDE_MATH_FUNCTIONS) +# include +# define HMM_SINF sinf +# define HMM_COSF cosf +# define HMM_TANF tanf +# define HMM_SQRTF sqrtf +# define HMM_ACOSF acosf +#endif + +#if !defined(HMM_ANGLE_USER_TO_INTERNAL) +# define HMM_ANGLE_USER_TO_INTERNAL(a) (HMM_ToRad(a)) +#endif + +#if !defined(HMM_ANGLE_INTERNAL_TO_USER) +# if defined(HANDMADE_MATH_USE_RADIANS) +# define HMM_ANGLE_INTERNAL_TO_USER(a) (a) +# elif defined(HANDMADE_MATH_USE_DEGREES) +# define HMM_ANGLE_INTERNAL_TO_USER(a) ((a)*HMM_RadToDeg) +# elif defined(HANDMADE_MATH_USE_TURNS) +# define HMM_ANGLE_INTERNAL_TO_USER(a) ((a)*HMM_RadToTurn) +# endif +#endif + +#define HMM_MIN(a, b) ((a) > (b) ? (b) : (a)) +#define HMM_MAX(a, b) ((a) < (b) ? (b) : (a)) +#define HMM_ABS(a) ((a) > 0 ? (a) : -(a)) +#define HMM_MOD(a, m) (((a) % (m)) >= 0 ? ((a) % (m)) : (((a) % (m)) + (m))) +#define HMM_SQUARE(x) ((x) * (x)) + +typedef union HMM_Vec2 +{ + struct + { + float X, Y; + }; + + struct + { + float U, V; + }; + + struct + { + float Left, Right; + }; + + struct + { + float Width, Height; + }; + + float Elements[2]; + +#ifdef __cplusplus + inline float &operator[](int Index) { return Elements[Index]; } + inline const float& operator[](int Index) const { return Elements[Index]; } +#endif +} HMM_Vec2; + +typedef union HMM_Vec3 +{ + struct + { + float X, Y, Z; + }; + + struct + { + float U, V, W; + }; + + struct + { + float R, G, B; + }; + + struct + { + HMM_Vec2 XY; + float _Ignored0; + }; + + struct + { + float _Ignored1; + HMM_Vec2 YZ; + }; + + struct + { + HMM_Vec2 UV; + float _Ignored2; + }; + + struct + { + float _Ignored3; + HMM_Vec2 VW; + }; + + float Elements[3]; + +#ifdef __cplusplus + inline float &operator[](int Index) { return Elements[Index]; } + inline const float &operator[](int Index) const { return Elements[Index]; } +#endif +} HMM_Vec3; + +typedef union HMM_Vec4 +{ + struct + { + union + { + HMM_Vec3 XYZ; + struct + { + float X, Y, Z; + }; + }; + + float W; + }; + struct + { + union + { + HMM_Vec3 RGB; + struct + { + float R, G, B; + }; + }; + + float A; + }; + + struct + { + HMM_Vec2 XY; + float _Ignored0; + float _Ignored1; + }; + + struct + { + float _Ignored2; + HMM_Vec2 YZ; + float _Ignored3; + }; + + struct + { + float _Ignored4; + float _Ignored5; + HMM_Vec2 ZW; + }; + + float Elements[4]; + +#ifdef HANDMADE_MATH__USE_SSE + __m128 SSE; +#endif + +#ifdef HANDMADE_MATH__USE_NEON + float32x4_t NEON; +#endif + +#ifdef __cplusplus + inline float &operator[](int Index) { return Elements[Index]; } + inline const float &operator[](int Index) const { return Elements[Index]; } +#endif +} HMM_Vec4; + +typedef union HMM_Mat2 +{ + float Elements[2][2]; + HMM_Vec2 Columns[2]; + +#ifdef __cplusplus + inline HMM_Vec2 &operator[](int Index) { return Columns[Index]; } + inline const HMM_Vec2 &operator[](int Index) const { return Columns[Index]; } +#endif +} HMM_Mat2; + +typedef union HMM_Mat3 +{ + float Elements[3][3]; + HMM_Vec3 Columns[3]; + +#ifdef __cplusplus + inline HMM_Vec3 &operator[](int Index) { return Columns[Index]; } + inline const HMM_Vec3 &operator[](int Index) const { return Columns[Index]; } +#endif +} HMM_Mat3; + +typedef union HMM_Mat4 +{ + float Elements[4][4]; + HMM_Vec4 Columns[4]; + +#ifdef __cplusplus + inline HMM_Vec4 &operator[](int Index) { return Columns[Index]; } + inline const HMM_Vec4 &operator[](int Index) const { return Columns[Index]; } +#endif +} HMM_Mat4; + +typedef union HMM_Quat +{ + struct + { + union + { + HMM_Vec3 XYZ; + struct + { + float X, Y, Z; + }; + }; + + float W; + }; + + float Elements[4]; + +#ifdef HANDMADE_MATH__USE_SSE + __m128 SSE; +#endif +#ifdef HANDMADE_MATH__USE_NEON + float32x4_t NEON; +#endif +} HMM_Quat; + +typedef signed int HMM_Bool; + +/* + * Angle unit conversion functions + */ +static inline float HMM_ToRad(float Angle) +{ +#if defined(HANDMADE_MATH_USE_RADIANS) + float Result = Angle; +#elif defined(HANDMADE_MATH_USE_DEGREES) + float Result = Angle * HMM_DegToRad; +#elif defined(HANDMADE_MATH_USE_TURNS) + float Result = Angle * HMM_TurnToRad; +#endif + + return Result; +} + +static inline float HMM_ToDeg(float Angle) +{ +#if defined(HANDMADE_MATH_USE_RADIANS) + float Result = Angle * HMM_RadToDeg; +#elif defined(HANDMADE_MATH_USE_DEGREES) + float Result = Angle; +#elif defined(HANDMADE_MATH_USE_TURNS) + float Result = Angle * HMM_TurnToDeg; +#endif + + return Result; +} + +static inline float HMM_ToTurn(float Angle) +{ +#if defined(HANDMADE_MATH_USE_RADIANS) + float Result = Angle * HMM_RadToTurn; +#elif defined(HANDMADE_MATH_USE_DEGREES) + float Result = Angle * HMM_DegToTurn; +#elif defined(HANDMADE_MATH_USE_TURNS) + float Result = Angle; +#endif + + return Result; +} + +/* + * Floating-point math functions + */ + +COVERAGE(HMM_SinF, 1) +static inline float HMM_SinF(float Angle) +{ + ASSERT_COVERED(HMM_SinF); + return HMM_SINF(HMM_ANGLE_USER_TO_INTERNAL(Angle)); +} + +COVERAGE(HMM_CosF, 1) +static inline float HMM_CosF(float Angle) +{ + ASSERT_COVERED(HMM_CosF); + return HMM_COSF(HMM_ANGLE_USER_TO_INTERNAL(Angle)); +} + +COVERAGE(HMM_TanF, 1) +static inline float HMM_TanF(float Angle) +{ + ASSERT_COVERED(HMM_TanF); + return HMM_TANF(HMM_ANGLE_USER_TO_INTERNAL(Angle)); +} + +COVERAGE(HMM_ACosF, 1) +static inline float HMM_ACosF(float Arg) +{ + ASSERT_COVERED(HMM_ACosF); + return HMM_ANGLE_INTERNAL_TO_USER(HMM_ACOSF(Arg)); +} + +COVERAGE(HMM_SqrtF, 1) +static inline float HMM_SqrtF(float Float) +{ + ASSERT_COVERED(HMM_SqrtF); + + float Result; + +#ifdef HANDMADE_MATH__USE_SSE + __m128 In = _mm_set_ss(Float); + __m128 Out = _mm_sqrt_ss(In); + Result = _mm_cvtss_f32(Out); +#elif defined(HANDMADE_MATH__USE_NEON) + float32x4_t In = vdupq_n_f32(Float); + float32x4_t Out = vsqrtq_f32(In); + Result = vgetq_lane_f32(Out, 0); +#else + Result = HMM_SQRTF(Float); +#endif + + return Result; +} + +COVERAGE(HMM_InvSqrtF, 1) +static inline float HMM_InvSqrtF(float Float) +{ + ASSERT_COVERED(HMM_InvSqrtF); + + float Result; + + Result = 1.0f/HMM_SqrtF(Float); + + return Result; +} + + +/* + * Utility functions + */ + +COVERAGE(HMM_Lerp, 1) +static inline float HMM_Lerp(float A, float Time, float B) +{ + ASSERT_COVERED(HMM_Lerp); + return (1.0f - Time) * A + Time * B; +} + +COVERAGE(HMM_Clamp, 1) +static inline float HMM_Clamp(float Min, float Value, float Max) +{ + ASSERT_COVERED(HMM_Clamp); + + float Result = Value; + + if (Result < Min) + { + Result = Min; + } + + if (Result > Max) + { + Result = Max; + } + + return Result; +} + + +/* + * Vector initialization + */ + +COVERAGE(HMM_V2, 1) +static inline HMM_Vec2 HMM_V2(float X, float Y) +{ + ASSERT_COVERED(HMM_V2); + + HMM_Vec2 Result; + Result.X = X; + Result.Y = Y; + + return Result; +} + +COVERAGE(HMM_V3, 1) +static inline HMM_Vec3 HMM_V3(float X, float Y, float Z) +{ + ASSERT_COVERED(HMM_V3); + + HMM_Vec3 Result; + Result.X = X; + Result.Y = Y; + Result.Z = Z; + + return Result; +} + +COVERAGE(HMM_V4, 1) +static inline HMM_Vec4 HMM_V4(float X, float Y, float Z, float W) +{ + ASSERT_COVERED(HMM_V4); + + HMM_Vec4 Result; + +#ifdef HANDMADE_MATH__USE_SSE + Result.SSE = _mm_setr_ps(X, Y, Z, W); +#elif defined(HANDMADE_MATH__USE_NEON) + float32x4_t v = {X, Y, Z, W}; + Result.NEON = v; +#else + Result.X = X; + Result.Y = Y; + Result.Z = Z; + Result.W = W; +#endif + + return Result; +} + +COVERAGE(HMM_V4V, 1) +static inline HMM_Vec4 HMM_V4V(HMM_Vec3 Vector, float W) +{ + ASSERT_COVERED(HMM_V4V); + + HMM_Vec4 Result; + +#ifdef HANDMADE_MATH__USE_SSE + Result.SSE = _mm_setr_ps(Vector.X, Vector.Y, Vector.Z, W); +#elif defined(HANDMADE_MATH__USE_NEON) + float32x4_t v = {Vector.X, Vector.Y, Vector.Z, W}; + Result.NEON = v; +#else + Result.XYZ = Vector; + Result.W = W; +#endif + + return Result; +} + + +/* + * Binary vector operations + */ + +COVERAGE(HMM_AddV2, 1) +static inline HMM_Vec2 HMM_AddV2(HMM_Vec2 Left, HMM_Vec2 Right) +{ + ASSERT_COVERED(HMM_AddV2); + + HMM_Vec2 Result; + Result.X = Left.X + Right.X; + Result.Y = Left.Y + Right.Y; + + return Result; +} + +COVERAGE(HMM_AddV3, 1) +static inline HMM_Vec3 HMM_AddV3(HMM_Vec3 Left, HMM_Vec3 Right) +{ + ASSERT_COVERED(HMM_AddV3); + + HMM_Vec3 Result; + Result.X = Left.X + Right.X; + Result.Y = Left.Y + Right.Y; + Result.Z = Left.Z + Right.Z; + + return Result; +} + +COVERAGE(HMM_AddV4, 1) +static inline HMM_Vec4 HMM_AddV4(HMM_Vec4 Left, HMM_Vec4 Right) +{ + ASSERT_COVERED(HMM_AddV4); + + HMM_Vec4 Result; + +#ifdef HANDMADE_MATH__USE_SSE + Result.SSE = _mm_add_ps(Left.SSE, Right.SSE); +#elif defined(HANDMADE_MATH__USE_NEON) + Result.NEON = vaddq_f32(Left.NEON, Right.NEON); +#else + Result.X = Left.X + Right.X; + Result.Y = Left.Y + Right.Y; + Result.Z = Left.Z + Right.Z; + Result.W = Left.W + Right.W; +#endif + + return Result; +} + +COVERAGE(HMM_SubV2, 1) +static inline HMM_Vec2 HMM_SubV2(HMM_Vec2 Left, HMM_Vec2 Right) +{ + ASSERT_COVERED(HMM_SubV2); + + HMM_Vec2 Result; + Result.X = Left.X - Right.X; + Result.Y = Left.Y - Right.Y; + + return Result; +} + +COVERAGE(HMM_SubV3, 1) +static inline HMM_Vec3 HMM_SubV3(HMM_Vec3 Left, HMM_Vec3 Right) +{ + ASSERT_COVERED(HMM_SubV3); + + HMM_Vec3 Result; + Result.X = Left.X - Right.X; + Result.Y = Left.Y - Right.Y; + Result.Z = Left.Z - Right.Z; + + return Result; +} + +COVERAGE(HMM_SubV4, 1) +static inline HMM_Vec4 HMM_SubV4(HMM_Vec4 Left, HMM_Vec4 Right) +{ + ASSERT_COVERED(HMM_SubV4); + + HMM_Vec4 Result; + +#ifdef HANDMADE_MATH__USE_SSE + Result.SSE = _mm_sub_ps(Left.SSE, Right.SSE); +#elif defined(HANDMADE_MATH__USE_NEON) + Result.NEON = vsubq_f32(Left.NEON, Right.NEON); +#else + Result.X = Left.X - Right.X; + Result.Y = Left.Y - Right.Y; + Result.Z = Left.Z - Right.Z; + Result.W = Left.W - Right.W; +#endif + + return Result; +} + +COVERAGE(HMM_MulV2, 1) +static inline HMM_Vec2 HMM_MulV2(HMM_Vec2 Left, HMM_Vec2 Right) +{ + ASSERT_COVERED(HMM_MulV2); + + HMM_Vec2 Result; + Result.X = Left.X * Right.X; + Result.Y = Left.Y * Right.Y; + + return Result; +} + +COVERAGE(HMM_MulV2F, 1) +static inline HMM_Vec2 HMM_MulV2F(HMM_Vec2 Left, float Right) +{ + ASSERT_COVERED(HMM_MulV2F); + + HMM_Vec2 Result; + Result.X = Left.X * Right; + Result.Y = Left.Y * Right; + + return Result; +} + +COVERAGE(HMM_MulV3, 1) +static inline HMM_Vec3 HMM_MulV3(HMM_Vec3 Left, HMM_Vec3 Right) +{ + ASSERT_COVERED(HMM_MulV3); + + HMM_Vec3 Result; + Result.X = Left.X * Right.X; + Result.Y = Left.Y * Right.Y; + Result.Z = Left.Z * Right.Z; + + return Result; +} + +COVERAGE(HMM_MulV3F, 1) +static inline HMM_Vec3 HMM_MulV3F(HMM_Vec3 Left, float Right) +{ + ASSERT_COVERED(HMM_MulV3F); + + HMM_Vec3 Result; + Result.X = Left.X * Right; + Result.Y = Left.Y * Right; + Result.Z = Left.Z * Right; + + return Result; +} + +COVERAGE(HMM_MulV4, 1) +static inline HMM_Vec4 HMM_MulV4(HMM_Vec4 Left, HMM_Vec4 Right) +{ + ASSERT_COVERED(HMM_MulV4); + + HMM_Vec4 Result; + +#ifdef HANDMADE_MATH__USE_SSE + Result.SSE = _mm_mul_ps(Left.SSE, Right.SSE); +#elif defined(HANDMADE_MATH__USE_NEON) + Result.NEON = vmulq_f32(Left.NEON, Right.NEON); +#else + Result.X = Left.X * Right.X; + Result.Y = Left.Y * Right.Y; + Result.Z = Left.Z * Right.Z; + Result.W = Left.W * Right.W; +#endif + + return Result; +} + +COVERAGE(HMM_MulV4F, 1) +static inline HMM_Vec4 HMM_MulV4F(HMM_Vec4 Left, float Right) +{ + ASSERT_COVERED(HMM_MulV4F); + + HMM_Vec4 Result; + +#ifdef HANDMADE_MATH__USE_SSE + __m128 Scalar = _mm_set1_ps(Right); + Result.SSE = _mm_mul_ps(Left.SSE, Scalar); +#elif defined(HANDMADE_MATH__USE_NEON) + Result.NEON = vmulq_n_f32(Left.NEON, Right); +#else + Result.X = Left.X * Right; + Result.Y = Left.Y * Right; + Result.Z = Left.Z * Right; + Result.W = Left.W * Right; +#endif + + return Result; +} + +COVERAGE(HMM_DivV2, 1) +static inline HMM_Vec2 HMM_DivV2(HMM_Vec2 Left, HMM_Vec2 Right) +{ + ASSERT_COVERED(HMM_DivV2); + + HMM_Vec2 Result; + Result.X = Left.X / Right.X; + Result.Y = Left.Y / Right.Y; + + return Result; +} + +COVERAGE(HMM_DivV2F, 1) +static inline HMM_Vec2 HMM_DivV2F(HMM_Vec2 Left, float Right) +{ + ASSERT_COVERED(HMM_DivV2F); + + HMM_Vec2 Result; + Result.X = Left.X / Right; + Result.Y = Left.Y / Right; + + return Result; +} + +COVERAGE(HMM_DivV3, 1) +static inline HMM_Vec3 HMM_DivV3(HMM_Vec3 Left, HMM_Vec3 Right) +{ + ASSERT_COVERED(HMM_DivV3); + + HMM_Vec3 Result; + Result.X = Left.X / Right.X; + Result.Y = Left.Y / Right.Y; + Result.Z = Left.Z / Right.Z; + + return Result; +} + +COVERAGE(HMM_DivV3F, 1) +static inline HMM_Vec3 HMM_DivV3F(HMM_Vec3 Left, float Right) +{ + ASSERT_COVERED(HMM_DivV3F); + + HMM_Vec3 Result; + Result.X = Left.X / Right; + Result.Y = Left.Y / Right; + Result.Z = Left.Z / Right; + + return Result; +} + +COVERAGE(HMM_DivV4, 1) +static inline HMM_Vec4 HMM_DivV4(HMM_Vec4 Left, HMM_Vec4 Right) +{ + ASSERT_COVERED(HMM_DivV4); + + HMM_Vec4 Result; + +#ifdef HANDMADE_MATH__USE_SSE + Result.SSE = _mm_div_ps(Left.SSE, Right.SSE); +#elif defined(HANDMADE_MATH__USE_NEON) + Result.NEON = vdivq_f32(Left.NEON, Right.NEON); +#else + Result.X = Left.X / Right.X; + Result.Y = Left.Y / Right.Y; + Result.Z = Left.Z / Right.Z; + Result.W = Left.W / Right.W; +#endif + + return Result; +} + +COVERAGE(HMM_DivV4F, 1) +static inline HMM_Vec4 HMM_DivV4F(HMM_Vec4 Left, float Right) +{ + ASSERT_COVERED(HMM_DivV4F); + + HMM_Vec4 Result; + +#ifdef HANDMADE_MATH__USE_SSE + __m128 Scalar = _mm_set1_ps(Right); + Result.SSE = _mm_div_ps(Left.SSE, Scalar); +#elif defined(HANDMADE_MATH__USE_NEON) + float32x4_t Scalar = vdupq_n_f32(Right); + Result.NEON = vdivq_f32(Left.NEON, Scalar); +#else + Result.X = Left.X / Right; + Result.Y = Left.Y / Right; + Result.Z = Left.Z / Right; + Result.W = Left.W / Right; +#endif + + return Result; +} + +COVERAGE(HMM_EqV2, 1) +static inline HMM_Bool HMM_EqV2(HMM_Vec2 Left, HMM_Vec2 Right) +{ + ASSERT_COVERED(HMM_EqV2); + return Left.X == Right.X && Left.Y == Right.Y; +} + +COVERAGE(HMM_EqV3, 1) +static inline HMM_Bool HMM_EqV3(HMM_Vec3 Left, HMM_Vec3 Right) +{ + ASSERT_COVERED(HMM_EqV3); + return Left.X == Right.X && Left.Y == Right.Y && Left.Z == Right.Z; +} + +COVERAGE(HMM_EqV4, 1) +static inline HMM_Bool HMM_EqV4(HMM_Vec4 Left, HMM_Vec4 Right) +{ + ASSERT_COVERED(HMM_EqV4); + return Left.X == Right.X && Left.Y == Right.Y && Left.Z == Right.Z && Left.W == Right.W; +} + +COVERAGE(HMM_DotV2, 1) +static inline float HMM_DotV2(HMM_Vec2 Left, HMM_Vec2 Right) +{ + ASSERT_COVERED(HMM_DotV2); + return (Left.X * Right.X) + (Left.Y * Right.Y); +} + +COVERAGE(HMM_DotV3, 1) +static inline float HMM_DotV3(HMM_Vec3 Left, HMM_Vec3 Right) +{ + ASSERT_COVERED(HMM_DotV3); + return (Left.X * Right.X) + (Left.Y * Right.Y) + (Left.Z * Right.Z); +} + +COVERAGE(HMM_DotV4, 1) +static inline float HMM_DotV4(HMM_Vec4 Left, HMM_Vec4 Right) +{ + ASSERT_COVERED(HMM_DotV4); + + float Result; + + // NOTE(zak): IN the future if we wanna check what version SSE is support + // we can use _mm_dp_ps (4.3) but for now we will use the old way. + // Or a r = _mm_mul_ps(v1, v2), r = _mm_hadd_ps(r, r), r = _mm_hadd_ps(r, r) for SSE3 +#ifdef HANDMADE_MATH__USE_SSE + __m128 SSEResultOne = _mm_mul_ps(Left.SSE, Right.SSE); + __m128 SSEResultTwo = _mm_shuffle_ps(SSEResultOne, SSEResultOne, _MM_SHUFFLE(2, 3, 0, 1)); + SSEResultOne = _mm_add_ps(SSEResultOne, SSEResultTwo); + SSEResultTwo = _mm_shuffle_ps(SSEResultOne, SSEResultOne, _MM_SHUFFLE(0, 1, 2, 3)); + SSEResultOne = _mm_add_ps(SSEResultOne, SSEResultTwo); + _mm_store_ss(&Result, SSEResultOne); +#elif defined(HANDMADE_MATH__USE_NEON) + float32x4_t NEONMultiplyResult = vmulq_f32(Left.NEON, Right.NEON); + float32x4_t NEONHalfAdd = vpaddq_f32(NEONMultiplyResult, NEONMultiplyResult); + float32x4_t NEONFullAdd = vpaddq_f32(NEONHalfAdd, NEONHalfAdd); + Result = vgetq_lane_f32(NEONFullAdd, 0); +#else + Result = ((Left.X * Right.X) + (Left.Z * Right.Z)) + ((Left.Y * Right.Y) + (Left.W * Right.W)); +#endif + + return Result; +} + +COVERAGE(HMM_Cross, 1) +static inline HMM_Vec3 HMM_Cross(HMM_Vec3 Left, HMM_Vec3 Right) +{ + ASSERT_COVERED(HMM_Cross); + + HMM_Vec3 Result; + Result.X = (Left.Y * Right.Z) - (Left.Z * Right.Y); + Result.Y = (Left.Z * Right.X) - (Left.X * Right.Z); + Result.Z = (Left.X * Right.Y) - (Left.Y * Right.X); + + return Result; +} + + +/* + * Unary vector operations + */ + +COVERAGE(HMM_LenSqrV2, 1) +static inline float HMM_LenSqrV2(HMM_Vec2 A) +{ + ASSERT_COVERED(HMM_LenSqrV2); + return HMM_DotV2(A, A); +} + +COVERAGE(HMM_LenSqrV3, 1) +static inline float HMM_LenSqrV3(HMM_Vec3 A) +{ + ASSERT_COVERED(HMM_LenSqrV3); + return HMM_DotV3(A, A); +} + +COVERAGE(HMM_LenSqrV4, 1) +static inline float HMM_LenSqrV4(HMM_Vec4 A) +{ + ASSERT_COVERED(HMM_LenSqrV4); + return HMM_DotV4(A, A); +} + +COVERAGE(HMM_LenV2, 1) +static inline float HMM_LenV2(HMM_Vec2 A) +{ + ASSERT_COVERED(HMM_LenV2); + return HMM_SqrtF(HMM_LenSqrV2(A)); +} + +COVERAGE(HMM_LenV3, 1) +static inline float HMM_LenV3(HMM_Vec3 A) +{ + ASSERT_COVERED(HMM_LenV3); + return HMM_SqrtF(HMM_LenSqrV3(A)); +} + +COVERAGE(HMM_LenV4, 1) +static inline float HMM_LenV4(HMM_Vec4 A) +{ + ASSERT_COVERED(HMM_LenV4); + return HMM_SqrtF(HMM_LenSqrV4(A)); +} + +COVERAGE(HMM_NormV2, 1) +static inline HMM_Vec2 HMM_NormV2(HMM_Vec2 A) +{ + ASSERT_COVERED(HMM_NormV2); + return HMM_MulV2F(A, HMM_InvSqrtF(HMM_DotV2(A, A))); +} + +COVERAGE(HMM_NormV3, 1) +static inline HMM_Vec3 HMM_NormV3(HMM_Vec3 A) +{ + ASSERT_COVERED(HMM_NormV3); + return HMM_MulV3F(A, HMM_InvSqrtF(HMM_DotV3(A, A))); +} + +COVERAGE(HMM_NormV4, 1) +static inline HMM_Vec4 HMM_NormV4(HMM_Vec4 A) +{ + ASSERT_COVERED(HMM_NormV4); + return HMM_MulV4F(A, HMM_InvSqrtF(HMM_DotV4(A, A))); +} + +/* + * Utility vector functions + */ + +COVERAGE(HMM_LerpV2, 1) +static inline HMM_Vec2 HMM_LerpV2(HMM_Vec2 A, float Time, HMM_Vec2 B) +{ + ASSERT_COVERED(HMM_LerpV2); + return HMM_AddV2(HMM_MulV2F(A, 1.0f - Time), HMM_MulV2F(B, Time)); +} + +COVERAGE(HMM_LerpV3, 1) +static inline HMM_Vec3 HMM_LerpV3(HMM_Vec3 A, float Time, HMM_Vec3 B) +{ + ASSERT_COVERED(HMM_LerpV3); + return HMM_AddV3(HMM_MulV3F(A, 1.0f - Time), HMM_MulV3F(B, Time)); +} + +COVERAGE(HMM_LerpV4, 1) +static inline HMM_Vec4 HMM_LerpV4(HMM_Vec4 A, float Time, HMM_Vec4 B) +{ + ASSERT_COVERED(HMM_LerpV4); + return HMM_AddV4(HMM_MulV4F(A, 1.0f - Time), HMM_MulV4F(B, Time)); +} + +/* + * SSE stuff + */ + +COVERAGE(HMM_LinearCombineV4M4, 1) +static inline HMM_Vec4 HMM_LinearCombineV4M4(HMM_Vec4 Left, HMM_Mat4 Right) +{ + ASSERT_COVERED(HMM_LinearCombineV4M4); + + HMM_Vec4 Result; +#ifdef HANDMADE_MATH__USE_SSE + Result.SSE = _mm_mul_ps(_mm_shuffle_ps(Left.SSE, Left.SSE, 0x00), Right.Columns[0].SSE); + Result.SSE = _mm_add_ps(Result.SSE, _mm_mul_ps(_mm_shuffle_ps(Left.SSE, Left.SSE, 0x55), Right.Columns[1].SSE)); + Result.SSE = _mm_add_ps(Result.SSE, _mm_mul_ps(_mm_shuffle_ps(Left.SSE, Left.SSE, 0xaa), Right.Columns[2].SSE)); + Result.SSE = _mm_add_ps(Result.SSE, _mm_mul_ps(_mm_shuffle_ps(Left.SSE, Left.SSE, 0xff), Right.Columns[3].SSE)); +#elif defined(HANDMADE_MATH__USE_NEON) + Result.NEON = vmulq_laneq_f32(Right.Columns[0].NEON, Left.NEON, 0); + Result.NEON = vfmaq_laneq_f32(Result.NEON, Right.Columns[1].NEON, Left.NEON, 1); + Result.NEON = vfmaq_laneq_f32(Result.NEON, Right.Columns[2].NEON, Left.NEON, 2); + Result.NEON = vfmaq_laneq_f32(Result.NEON, Right.Columns[3].NEON, Left.NEON, 3); +#else + Result.X = Left.Elements[0] * Right.Columns[0].X; + Result.Y = Left.Elements[0] * Right.Columns[0].Y; + Result.Z = Left.Elements[0] * Right.Columns[0].Z; + Result.W = Left.Elements[0] * Right.Columns[0].W; + + Result.X += Left.Elements[1] * Right.Columns[1].X; + Result.Y += Left.Elements[1] * Right.Columns[1].Y; + Result.Z += Left.Elements[1] * Right.Columns[1].Z; + Result.W += Left.Elements[1] * Right.Columns[1].W; + + Result.X += Left.Elements[2] * Right.Columns[2].X; + Result.Y += Left.Elements[2] * Right.Columns[2].Y; + Result.Z += Left.Elements[2] * Right.Columns[2].Z; + Result.W += Left.Elements[2] * Right.Columns[2].W; + + Result.X += Left.Elements[3] * Right.Columns[3].X; + Result.Y += Left.Elements[3] * Right.Columns[3].Y; + Result.Z += Left.Elements[3] * Right.Columns[3].Z; + Result.W += Left.Elements[3] * Right.Columns[3].W; +#endif + + return Result; +} + +/* + * 2x2 Matrices + */ + +COVERAGE(HMM_M2, 1) +static inline HMM_Mat2 HMM_M2(void) +{ + ASSERT_COVERED(HMM_M2); + HMM_Mat2 Result = {0}; + return Result; +} + +COVERAGE(HMM_M2D, 1) +static inline HMM_Mat2 HMM_M2D(float Diagonal) +{ + ASSERT_COVERED(HMM_M2D); + + HMM_Mat2 Result = {0}; + Result.Elements[0][0] = Diagonal; + Result.Elements[1][1] = Diagonal; + + return Result; +} + +COVERAGE(HMM_TransposeM2, 1) +static inline HMM_Mat2 HMM_TransposeM2(HMM_Mat2 Matrix) +{ + ASSERT_COVERED(HMM_TransposeM2); + + HMM_Mat2 Result = Matrix; + + Result.Elements[0][1] = Matrix.Elements[1][0]; + Result.Elements[1][0] = Matrix.Elements[0][1]; + + return Result; +} + +COVERAGE(HMM_AddM2, 1) +static inline HMM_Mat2 HMM_AddM2(HMM_Mat2 Left, HMM_Mat2 Right) +{ + ASSERT_COVERED(HMM_AddM2); + + HMM_Mat2 Result; + + Result.Elements[0][0] = Left.Elements[0][0] + Right.Elements[0][0]; + Result.Elements[0][1] = Left.Elements[0][1] + Right.Elements[0][1]; + Result.Elements[1][0] = Left.Elements[1][0] + Right.Elements[1][0]; + Result.Elements[1][1] = Left.Elements[1][1] + Right.Elements[1][1]; + + return Result; +} + +COVERAGE(HMM_SubM2, 1) +static inline HMM_Mat2 HMM_SubM2(HMM_Mat2 Left, HMM_Mat2 Right) +{ + ASSERT_COVERED(HMM_SubM2); + + HMM_Mat2 Result; + + Result.Elements[0][0] = Left.Elements[0][0] - Right.Elements[0][0]; + Result.Elements[0][1] = Left.Elements[0][1] - Right.Elements[0][1]; + Result.Elements[1][0] = Left.Elements[1][0] - Right.Elements[1][0]; + Result.Elements[1][1] = Left.Elements[1][1] - Right.Elements[1][1]; + + return Result; +} + +COVERAGE(HMM_MulM2V2, 1) +static inline HMM_Vec2 HMM_MulM2V2(HMM_Mat2 Matrix, HMM_Vec2 Vector) +{ + ASSERT_COVERED(HMM_MulM2V2); + + HMM_Vec2 Result; + + Result.X = Vector.Elements[0] * Matrix.Columns[0].X; + Result.Y = Vector.Elements[0] * Matrix.Columns[0].Y; + + Result.X += Vector.Elements[1] * Matrix.Columns[1].X; + Result.Y += Vector.Elements[1] * Matrix.Columns[1].Y; + + return Result; +} + +COVERAGE(HMM_MulM2, 1) +static inline HMM_Mat2 HMM_MulM2(HMM_Mat2 Left, HMM_Mat2 Right) +{ + ASSERT_COVERED(HMM_MulM2); + + HMM_Mat2 Result; + Result.Columns[0] = HMM_MulM2V2(Left, Right.Columns[0]); + Result.Columns[1] = HMM_MulM2V2(Left, Right.Columns[1]); + + return Result; +} + +COVERAGE(HMM_MulM2F, 1) +static inline HMM_Mat2 HMM_MulM2F(HMM_Mat2 Matrix, float Scalar) +{ + ASSERT_COVERED(HMM_MulM2F); + + HMM_Mat2 Result; + + Result.Elements[0][0] = Matrix.Elements[0][0] * Scalar; + Result.Elements[0][1] = Matrix.Elements[0][1] * Scalar; + Result.Elements[1][0] = Matrix.Elements[1][0] * Scalar; + Result.Elements[1][1] = Matrix.Elements[1][1] * Scalar; + + return Result; +} + +COVERAGE(HMM_DivM2F, 1) +static inline HMM_Mat2 HMM_DivM2F(HMM_Mat2 Matrix, float Scalar) +{ + ASSERT_COVERED(HMM_DivM2F); + + HMM_Mat2 Result; + + Result.Elements[0][0] = Matrix.Elements[0][0] / Scalar; + Result.Elements[0][1] = Matrix.Elements[0][1] / Scalar; + Result.Elements[1][0] = Matrix.Elements[1][0] / Scalar; + Result.Elements[1][1] = Matrix.Elements[1][1] / Scalar; + + return Result; +} + +COVERAGE(HMM_DeterminantM2, 1) +static inline float HMM_DeterminantM2(HMM_Mat2 Matrix) +{ + ASSERT_COVERED(HMM_DeterminantM2); + return Matrix.Elements[0][0]*Matrix.Elements[1][1] - Matrix.Elements[0][1]*Matrix.Elements[1][0]; +} + + +COVERAGE(HMM_InvGeneralM2, 1) +static inline HMM_Mat2 HMM_InvGeneralM2(HMM_Mat2 Matrix) +{ + ASSERT_COVERED(HMM_InvGeneralM2); + + HMM_Mat2 Result; + float InvDeterminant = 1.0f / HMM_DeterminantM2(Matrix); + Result.Elements[0][0] = InvDeterminant * +Matrix.Elements[1][1]; + Result.Elements[1][1] = InvDeterminant * +Matrix.Elements[0][0]; + Result.Elements[0][1] = InvDeterminant * -Matrix.Elements[0][1]; + Result.Elements[1][0] = InvDeterminant * -Matrix.Elements[1][0]; + + return Result; +} + +/* + * 3x3 Matrices + */ + +COVERAGE(HMM_M3, 1) +static inline HMM_Mat3 HMM_M3(void) +{ + ASSERT_COVERED(HMM_M3); + HMM_Mat3 Result = {0}; + return Result; +} + +COVERAGE(HMM_M3D, 1) +static inline HMM_Mat3 HMM_M3D(float Diagonal) +{ + ASSERT_COVERED(HMM_M3D); + + HMM_Mat3 Result = {0}; + Result.Elements[0][0] = Diagonal; + Result.Elements[1][1] = Diagonal; + Result.Elements[2][2] = Diagonal; + + return Result; +} + +COVERAGE(HMM_TransposeM3, 1) +static inline HMM_Mat3 HMM_TransposeM3(HMM_Mat3 Matrix) +{ + ASSERT_COVERED(HMM_TransposeM3); + + HMM_Mat3 Result = Matrix; + + Result.Elements[0][1] = Matrix.Elements[1][0]; + Result.Elements[0][2] = Matrix.Elements[2][0]; + Result.Elements[1][0] = Matrix.Elements[0][1]; + Result.Elements[1][2] = Matrix.Elements[2][1]; + Result.Elements[2][1] = Matrix.Elements[1][2]; + Result.Elements[2][0] = Matrix.Elements[0][2]; + + return Result; +} + +COVERAGE(HMM_AddM3, 1) +static inline HMM_Mat3 HMM_AddM3(HMM_Mat3 Left, HMM_Mat3 Right) +{ + ASSERT_COVERED(HMM_AddM3); + + HMM_Mat3 Result; + + Result.Elements[0][0] = Left.Elements[0][0] + Right.Elements[0][0]; + Result.Elements[0][1] = Left.Elements[0][1] + Right.Elements[0][1]; + Result.Elements[0][2] = Left.Elements[0][2] + Right.Elements[0][2]; + Result.Elements[1][0] = Left.Elements[1][0] + Right.Elements[1][0]; + Result.Elements[1][1] = Left.Elements[1][1] + Right.Elements[1][1]; + Result.Elements[1][2] = Left.Elements[1][2] + Right.Elements[1][2]; + Result.Elements[2][0] = Left.Elements[2][0] + Right.Elements[2][0]; + Result.Elements[2][1] = Left.Elements[2][1] + Right.Elements[2][1]; + Result.Elements[2][2] = Left.Elements[2][2] + Right.Elements[2][2]; + + return Result; +} + +COVERAGE(HMM_SubM3, 1) +static inline HMM_Mat3 HMM_SubM3(HMM_Mat3 Left, HMM_Mat3 Right) +{ + ASSERT_COVERED(HMM_SubM3); + + HMM_Mat3 Result; + + Result.Elements[0][0] = Left.Elements[0][0] - Right.Elements[0][0]; + Result.Elements[0][1] = Left.Elements[0][1] - Right.Elements[0][1]; + Result.Elements[0][2] = Left.Elements[0][2] - Right.Elements[0][2]; + Result.Elements[1][0] = Left.Elements[1][0] - Right.Elements[1][0]; + Result.Elements[1][1] = Left.Elements[1][1] - Right.Elements[1][1]; + Result.Elements[1][2] = Left.Elements[1][2] - Right.Elements[1][2]; + Result.Elements[2][0] = Left.Elements[2][0] - Right.Elements[2][0]; + Result.Elements[2][1] = Left.Elements[2][1] - Right.Elements[2][1]; + Result.Elements[2][2] = Left.Elements[2][2] - Right.Elements[2][2]; + + return Result; +} + +COVERAGE(HMM_MulM3V3, 1) +static inline HMM_Vec3 HMM_MulM3V3(HMM_Mat3 Matrix, HMM_Vec3 Vector) +{ + ASSERT_COVERED(HMM_MulM3V3); + + HMM_Vec3 Result; + + Result.X = Vector.Elements[0] * Matrix.Columns[0].X; + Result.Y = Vector.Elements[0] * Matrix.Columns[0].Y; + Result.Z = Vector.Elements[0] * Matrix.Columns[0].Z; + + Result.X += Vector.Elements[1] * Matrix.Columns[1].X; + Result.Y += Vector.Elements[1] * Matrix.Columns[1].Y; + Result.Z += Vector.Elements[1] * Matrix.Columns[1].Z; + + Result.X += Vector.Elements[2] * Matrix.Columns[2].X; + Result.Y += Vector.Elements[2] * Matrix.Columns[2].Y; + Result.Z += Vector.Elements[2] * Matrix.Columns[2].Z; + + return Result; +} + +COVERAGE(HMM_MulM3, 1) +static inline HMM_Mat3 HMM_MulM3(HMM_Mat3 Left, HMM_Mat3 Right) +{ + ASSERT_COVERED(HMM_MulM3); + + HMM_Mat3 Result; + Result.Columns[0] = HMM_MulM3V3(Left, Right.Columns[0]); + Result.Columns[1] = HMM_MulM3V3(Left, Right.Columns[1]); + Result.Columns[2] = HMM_MulM3V3(Left, Right.Columns[2]); + + return Result; +} + +COVERAGE(HMM_MulM3F, 1) +static inline HMM_Mat3 HMM_MulM3F(HMM_Mat3 Matrix, float Scalar) +{ + ASSERT_COVERED(HMM_MulM3F); + + HMM_Mat3 Result; + + Result.Elements[0][0] = Matrix.Elements[0][0] * Scalar; + Result.Elements[0][1] = Matrix.Elements[0][1] * Scalar; + Result.Elements[0][2] = Matrix.Elements[0][2] * Scalar; + Result.Elements[1][0] = Matrix.Elements[1][0] * Scalar; + Result.Elements[1][1] = Matrix.Elements[1][1] * Scalar; + Result.Elements[1][2] = Matrix.Elements[1][2] * Scalar; + Result.Elements[2][0] = Matrix.Elements[2][0] * Scalar; + Result.Elements[2][1] = Matrix.Elements[2][1] * Scalar; + Result.Elements[2][2] = Matrix.Elements[2][2] * Scalar; + + return Result; +} + +COVERAGE(HMM_DivM3F, 1) +static inline HMM_Mat3 HMM_DivM3F(HMM_Mat3 Matrix, float Scalar) +{ + ASSERT_COVERED(HMM_DivM3F); + + HMM_Mat3 Result; + + Result.Elements[0][0] = Matrix.Elements[0][0] / Scalar; + Result.Elements[0][1] = Matrix.Elements[0][1] / Scalar; + Result.Elements[0][2] = Matrix.Elements[0][2] / Scalar; + Result.Elements[1][0] = Matrix.Elements[1][0] / Scalar; + Result.Elements[1][1] = Matrix.Elements[1][1] / Scalar; + Result.Elements[1][2] = Matrix.Elements[1][2] / Scalar; + Result.Elements[2][0] = Matrix.Elements[2][0] / Scalar; + Result.Elements[2][1] = Matrix.Elements[2][1] / Scalar; + Result.Elements[2][2] = Matrix.Elements[2][2] / Scalar; + + return Result; +} + +COVERAGE(HMM_DeterminantM3, 1) +static inline float HMM_DeterminantM3(HMM_Mat3 Matrix) +{ + ASSERT_COVERED(HMM_DeterminantM3); + + HMM_Mat3 Cross; + Cross.Columns[0] = HMM_Cross(Matrix.Columns[1], Matrix.Columns[2]); + Cross.Columns[1] = HMM_Cross(Matrix.Columns[2], Matrix.Columns[0]); + Cross.Columns[2] = HMM_Cross(Matrix.Columns[0], Matrix.Columns[1]); + + return HMM_DotV3(Cross.Columns[2], Matrix.Columns[2]); +} + +COVERAGE(HMM_InvGeneralM3, 1) +static inline HMM_Mat3 HMM_InvGeneralM3(HMM_Mat3 Matrix) +{ + ASSERT_COVERED(HMM_InvGeneralM3); + + HMM_Mat3 Cross; + Cross.Columns[0] = HMM_Cross(Matrix.Columns[1], Matrix.Columns[2]); + Cross.Columns[1] = HMM_Cross(Matrix.Columns[2], Matrix.Columns[0]); + Cross.Columns[2] = HMM_Cross(Matrix.Columns[0], Matrix.Columns[1]); + + float InvDeterminant = 1.0f / HMM_DotV3(Cross.Columns[2], Matrix.Columns[2]); + + HMM_Mat3 Result; + Result.Columns[0] = HMM_MulV3F(Cross.Columns[0], InvDeterminant); + Result.Columns[1] = HMM_MulV3F(Cross.Columns[1], InvDeterminant); + Result.Columns[2] = HMM_MulV3F(Cross.Columns[2], InvDeterminant); + + return HMM_TransposeM3(Result); +} + +/* + * 4x4 Matrices + */ + +COVERAGE(HMM_M4, 1) +static inline HMM_Mat4 HMM_M4(void) +{ + ASSERT_COVERED(HMM_M4); + HMM_Mat4 Result = {0}; + return Result; +} + +COVERAGE(HMM_M4D, 1) +static inline HMM_Mat4 HMM_M4D(float Diagonal) +{ + ASSERT_COVERED(HMM_M4D); + + HMM_Mat4 Result = {0}; + Result.Elements[0][0] = Diagonal; + Result.Elements[1][1] = Diagonal; + Result.Elements[2][2] = Diagonal; + Result.Elements[3][3] = Diagonal; + + return Result; +} + +COVERAGE(HMM_TransposeM4, 1) +static inline HMM_Mat4 HMM_TransposeM4(HMM_Mat4 Matrix) +{ + ASSERT_COVERED(HMM_TransposeM4); + + HMM_Mat4 Result; +#ifdef HANDMADE_MATH__USE_SSE + Result = Matrix; + _MM_TRANSPOSE4_PS(Result.Columns[0].SSE, Result.Columns[1].SSE, Result.Columns[2].SSE, Result.Columns[3].SSE); +#elif defined(HANDMADE_MATH__USE_NEON) + float32x4x4_t Transposed = vld4q_f32((float*)Matrix.Columns); + Result.Columns[0].NEON = Transposed.val[0]; + Result.Columns[1].NEON = Transposed.val[1]; + Result.Columns[2].NEON = Transposed.val[2]; + Result.Columns[3].NEON = Transposed.val[3]; +#else + Result.Elements[0][0] = Matrix.Elements[0][0]; + Result.Elements[0][1] = Matrix.Elements[1][0]; + Result.Elements[0][2] = Matrix.Elements[2][0]; + Result.Elements[0][3] = Matrix.Elements[3][0]; + Result.Elements[1][0] = Matrix.Elements[0][1]; + Result.Elements[1][1] = Matrix.Elements[1][1]; + Result.Elements[1][2] = Matrix.Elements[2][1]; + Result.Elements[1][3] = Matrix.Elements[3][1]; + Result.Elements[2][0] = Matrix.Elements[0][2]; + Result.Elements[2][1] = Matrix.Elements[1][2]; + Result.Elements[2][2] = Matrix.Elements[2][2]; + Result.Elements[2][3] = Matrix.Elements[3][2]; + Result.Elements[3][0] = Matrix.Elements[0][3]; + Result.Elements[3][1] = Matrix.Elements[1][3]; + Result.Elements[3][2] = Matrix.Elements[2][3]; + Result.Elements[3][3] = Matrix.Elements[3][3]; +#endif + + return Result; +} + +COVERAGE(HMM_AddM4, 1) +static inline HMM_Mat4 HMM_AddM4(HMM_Mat4 Left, HMM_Mat4 Right) +{ + ASSERT_COVERED(HMM_AddM4); + + HMM_Mat4 Result; + + Result.Columns[0] = HMM_AddV4(Left.Columns[0], Right.Columns[0]); + Result.Columns[1] = HMM_AddV4(Left.Columns[1], Right.Columns[1]); + Result.Columns[2] = HMM_AddV4(Left.Columns[2], Right.Columns[2]); + Result.Columns[3] = HMM_AddV4(Left.Columns[3], Right.Columns[3]); + + return Result; +} + +COVERAGE(HMM_SubM4, 1) +static inline HMM_Mat4 HMM_SubM4(HMM_Mat4 Left, HMM_Mat4 Right) +{ + ASSERT_COVERED(HMM_SubM4); + + HMM_Mat4 Result; + + Result.Columns[0] = HMM_SubV4(Left.Columns[0], Right.Columns[0]); + Result.Columns[1] = HMM_SubV4(Left.Columns[1], Right.Columns[1]); + Result.Columns[2] = HMM_SubV4(Left.Columns[2], Right.Columns[2]); + Result.Columns[3] = HMM_SubV4(Left.Columns[3], Right.Columns[3]); + + return Result; +} + +COVERAGE(HMM_MulM4, 1) +static inline HMM_Mat4 HMM_MulM4(HMM_Mat4 Left, HMM_Mat4 Right) +{ + ASSERT_COVERED(HMM_MulM4); + + HMM_Mat4 Result; + Result.Columns[0] = HMM_LinearCombineV4M4(Right.Columns[0], Left); + Result.Columns[1] = HMM_LinearCombineV4M4(Right.Columns[1], Left); + Result.Columns[2] = HMM_LinearCombineV4M4(Right.Columns[2], Left); + Result.Columns[3] = HMM_LinearCombineV4M4(Right.Columns[3], Left); + + return Result; +} + +COVERAGE(HMM_MulM4F, 1) +static inline HMM_Mat4 HMM_MulM4F(HMM_Mat4 Matrix, float Scalar) +{ + ASSERT_COVERED(HMM_MulM4F); + + HMM_Mat4 Result; + + +#ifdef HANDMADE_MATH__USE_SSE + __m128 SSEScalar = _mm_set1_ps(Scalar); + Result.Columns[0].SSE = _mm_mul_ps(Matrix.Columns[0].SSE, SSEScalar); + Result.Columns[1].SSE = _mm_mul_ps(Matrix.Columns[1].SSE, SSEScalar); + Result.Columns[2].SSE = _mm_mul_ps(Matrix.Columns[2].SSE, SSEScalar); + Result.Columns[3].SSE = _mm_mul_ps(Matrix.Columns[3].SSE, SSEScalar); +#elif defined(HANDMADE_MATH__USE_NEON) + Result.Columns[0].NEON = vmulq_n_f32(Matrix.Columns[0].NEON, Scalar); + Result.Columns[1].NEON = vmulq_n_f32(Matrix.Columns[1].NEON, Scalar); + Result.Columns[2].NEON = vmulq_n_f32(Matrix.Columns[2].NEON, Scalar); + Result.Columns[3].NEON = vmulq_n_f32(Matrix.Columns[3].NEON, Scalar); +#else + Result.Elements[0][0] = Matrix.Elements[0][0] * Scalar; + Result.Elements[0][1] = Matrix.Elements[0][1] * Scalar; + Result.Elements[0][2] = Matrix.Elements[0][2] * Scalar; + Result.Elements[0][3] = Matrix.Elements[0][3] * Scalar; + Result.Elements[1][0] = Matrix.Elements[1][0] * Scalar; + Result.Elements[1][1] = Matrix.Elements[1][1] * Scalar; + Result.Elements[1][2] = Matrix.Elements[1][2] * Scalar; + Result.Elements[1][3] = Matrix.Elements[1][3] * Scalar; + Result.Elements[2][0] = Matrix.Elements[2][0] * Scalar; + Result.Elements[2][1] = Matrix.Elements[2][1] * Scalar; + Result.Elements[2][2] = Matrix.Elements[2][2] * Scalar; + Result.Elements[2][3] = Matrix.Elements[2][3] * Scalar; + Result.Elements[3][0] = Matrix.Elements[3][0] * Scalar; + Result.Elements[3][1] = Matrix.Elements[3][1] * Scalar; + Result.Elements[3][2] = Matrix.Elements[3][2] * Scalar; + Result.Elements[3][3] = Matrix.Elements[3][3] * Scalar; +#endif + + return Result; +} + +COVERAGE(HMM_MulM4V4, 1) +static inline HMM_Vec4 HMM_MulM4V4(HMM_Mat4 Matrix, HMM_Vec4 Vector) +{ + ASSERT_COVERED(HMM_MulM4V4); + return HMM_LinearCombineV4M4(Vector, Matrix); +} + +COVERAGE(HMM_DivM4F, 1) +static inline HMM_Mat4 HMM_DivM4F(HMM_Mat4 Matrix, float Scalar) +{ + ASSERT_COVERED(HMM_DivM4F); + + HMM_Mat4 Result; + +#ifdef HANDMADE_MATH__USE_SSE + __m128 SSEScalar = _mm_set1_ps(Scalar); + Result.Columns[0].SSE = _mm_div_ps(Matrix.Columns[0].SSE, SSEScalar); + Result.Columns[1].SSE = _mm_div_ps(Matrix.Columns[1].SSE, SSEScalar); + Result.Columns[2].SSE = _mm_div_ps(Matrix.Columns[2].SSE, SSEScalar); + Result.Columns[3].SSE = _mm_div_ps(Matrix.Columns[3].SSE, SSEScalar); +#elif defined(HANDMADE_MATH__USE_NEON) + float32x4_t NEONScalar = vdupq_n_f32(Scalar); + Result.Columns[0].NEON = vdivq_f32(Matrix.Columns[0].NEON, NEONScalar); + Result.Columns[1].NEON = vdivq_f32(Matrix.Columns[1].NEON, NEONScalar); + Result.Columns[2].NEON = vdivq_f32(Matrix.Columns[2].NEON, NEONScalar); + Result.Columns[3].NEON = vdivq_f32(Matrix.Columns[3].NEON, NEONScalar); +#else + Result.Elements[0][0] = Matrix.Elements[0][0] / Scalar; + Result.Elements[0][1] = Matrix.Elements[0][1] / Scalar; + Result.Elements[0][2] = Matrix.Elements[0][2] / Scalar; + Result.Elements[0][3] = Matrix.Elements[0][3] / Scalar; + Result.Elements[1][0] = Matrix.Elements[1][0] / Scalar; + Result.Elements[1][1] = Matrix.Elements[1][1] / Scalar; + Result.Elements[1][2] = Matrix.Elements[1][2] / Scalar; + Result.Elements[1][3] = Matrix.Elements[1][3] / Scalar; + Result.Elements[2][0] = Matrix.Elements[2][0] / Scalar; + Result.Elements[2][1] = Matrix.Elements[2][1] / Scalar; + Result.Elements[2][2] = Matrix.Elements[2][2] / Scalar; + Result.Elements[2][3] = Matrix.Elements[2][3] / Scalar; + Result.Elements[3][0] = Matrix.Elements[3][0] / Scalar; + Result.Elements[3][1] = Matrix.Elements[3][1] / Scalar; + Result.Elements[3][2] = Matrix.Elements[3][2] / Scalar; + Result.Elements[3][3] = Matrix.Elements[3][3] / Scalar; +#endif + + return Result; +} + +COVERAGE(HMM_DeterminantM4, 1) +static inline float HMM_DeterminantM4(HMM_Mat4 Matrix) +{ + ASSERT_COVERED(HMM_DeterminantM4); + + HMM_Vec3 C01 = HMM_Cross(Matrix.Columns[0].XYZ, Matrix.Columns[1].XYZ); + HMM_Vec3 C23 = HMM_Cross(Matrix.Columns[2].XYZ, Matrix.Columns[3].XYZ); + HMM_Vec3 B10 = HMM_SubV3(HMM_MulV3F(Matrix.Columns[0].XYZ, Matrix.Columns[1].W), HMM_MulV3F(Matrix.Columns[1].XYZ, Matrix.Columns[0].W)); + HMM_Vec3 B32 = HMM_SubV3(HMM_MulV3F(Matrix.Columns[2].XYZ, Matrix.Columns[3].W), HMM_MulV3F(Matrix.Columns[3].XYZ, Matrix.Columns[2].W)); + + return HMM_DotV3(C01, B32) + HMM_DotV3(C23, B10); +} + +COVERAGE(HMM_InvGeneralM4, 1) +// Returns a general-purpose inverse of an HMM_Mat4. Note that special-purpose inverses of many transformations +// are available and will be more efficient. +static inline HMM_Mat4 HMM_InvGeneralM4(HMM_Mat4 Matrix) +{ + ASSERT_COVERED(HMM_InvGeneralM4); + + HMM_Vec3 C01 = HMM_Cross(Matrix.Columns[0].XYZ, Matrix.Columns[1].XYZ); + HMM_Vec3 C23 = HMM_Cross(Matrix.Columns[2].XYZ, Matrix.Columns[3].XYZ); + HMM_Vec3 B10 = HMM_SubV3(HMM_MulV3F(Matrix.Columns[0].XYZ, Matrix.Columns[1].W), HMM_MulV3F(Matrix.Columns[1].XYZ, Matrix.Columns[0].W)); + HMM_Vec3 B32 = HMM_SubV3(HMM_MulV3F(Matrix.Columns[2].XYZ, Matrix.Columns[3].W), HMM_MulV3F(Matrix.Columns[3].XYZ, Matrix.Columns[2].W)); + + float InvDeterminant = 1.0f / (HMM_DotV3(C01, B32) + HMM_DotV3(C23, B10)); + C01 = HMM_MulV3F(C01, InvDeterminant); + C23 = HMM_MulV3F(C23, InvDeterminant); + B10 = HMM_MulV3F(B10, InvDeterminant); + B32 = HMM_MulV3F(B32, InvDeterminant); + + HMM_Mat4 Result; + Result.Columns[0] = HMM_V4V(HMM_AddV3(HMM_Cross(Matrix.Columns[1].XYZ, B32), HMM_MulV3F(C23, Matrix.Columns[1].W)), -HMM_DotV3(Matrix.Columns[1].XYZ, C23)); + Result.Columns[1] = HMM_V4V(HMM_SubV3(HMM_Cross(B32, Matrix.Columns[0].XYZ), HMM_MulV3F(C23, Matrix.Columns[0].W)), +HMM_DotV3(Matrix.Columns[0].XYZ, C23)); + Result.Columns[2] = HMM_V4V(HMM_AddV3(HMM_Cross(Matrix.Columns[3].XYZ, B10), HMM_MulV3F(C01, Matrix.Columns[3].W)), -HMM_DotV3(Matrix.Columns[3].XYZ, C01)); + Result.Columns[3] = HMM_V4V(HMM_SubV3(HMM_Cross(B10, Matrix.Columns[2].XYZ), HMM_MulV3F(C01, Matrix.Columns[2].W)), +HMM_DotV3(Matrix.Columns[2].XYZ, C01)); + + return HMM_TransposeM4(Result); +} + +/* + * Common graphics transformations + */ + +COVERAGE(HMM_Orthographic_RH_NO, 1) +// Produces a right-handed orthographic projection matrix with Z ranging from -1 to 1 (the GL convention). +// Left, Right, Bottom, and Top specify the coordinates of their respective clipping planes. +// Near and Far specify the distances to the near and far clipping planes. +static inline HMM_Mat4 HMM_Orthographic_RH_NO(float Left, float Right, float Bottom, float Top, float Near, float Far) +{ + ASSERT_COVERED(HMM_Orthographic_RH_NO); + + HMM_Mat4 Result = {0}; + + Result.Elements[0][0] = 2.0f / (Right - Left); + Result.Elements[1][1] = 2.0f / (Top - Bottom); + Result.Elements[2][2] = 2.0f / (Near - Far); + Result.Elements[3][3] = 1.0f; + + Result.Elements[3][0] = (Left + Right) / (Left - Right); + Result.Elements[3][1] = (Bottom + Top) / (Bottom - Top); + Result.Elements[3][2] = (Near + Far) / (Near - Far); + + return Result; +} + +COVERAGE(HMM_Orthographic_RH_ZO, 1) +// Produces a right-handed orthographic projection matrix with Z ranging from 0 to 1 (the DirectX convention). +// Left, Right, Bottom, and Top specify the coordinates of their respective clipping planes. +// Near and Far specify the distances to the near and far clipping planes. +static inline HMM_Mat4 HMM_Orthographic_RH_ZO(float Left, float Right, float Bottom, float Top, float Near, float Far) +{ + ASSERT_COVERED(HMM_Orthographic_RH_ZO); + + HMM_Mat4 Result = {0}; + + Result.Elements[0][0] = 2.0f / (Right - Left); + Result.Elements[1][1] = 2.0f / (Top - Bottom); + Result.Elements[2][2] = 1.0f / (Near - Far); + Result.Elements[3][3] = 1.0f; + + Result.Elements[3][0] = (Left + Right) / (Left - Right); + Result.Elements[3][1] = (Bottom + Top) / (Bottom - Top); + Result.Elements[3][2] = (Near) / (Near - Far); + + return Result; +} + +COVERAGE(HMM_Orthographic_LH_NO, 1) +// Produces a left-handed orthographic projection matrix with Z ranging from -1 to 1 (the GL convention). +// Left, Right, Bottom, and Top specify the coordinates of their respective clipping planes. +// Near and Far specify the distances to the near and far clipping planes. +static inline HMM_Mat4 HMM_Orthographic_LH_NO(float Left, float Right, float Bottom, float Top, float Near, float Far) +{ + ASSERT_COVERED(HMM_Orthographic_LH_NO); + + HMM_Mat4 Result = HMM_Orthographic_RH_NO(Left, Right, Bottom, Top, Near, Far); + Result.Elements[2][2] = -Result.Elements[2][2]; + + return Result; +} + +COVERAGE(HMM_Orthographic_LH_ZO, 1) +// Produces a left-handed orthographic projection matrix with Z ranging from 0 to 1 (the DirectX convention). +// Left, Right, Bottom, and Top specify the coordinates of their respective clipping planes. +// Near and Far specify the distances to the near and far clipping planes. +static inline HMM_Mat4 HMM_Orthographic_LH_ZO(float Left, float Right, float Bottom, float Top, float Near, float Far) +{ + ASSERT_COVERED(HMM_Orthographic_LH_ZO); + + HMM_Mat4 Result = HMM_Orthographic_RH_ZO(Left, Right, Bottom, Top, Near, Far); + Result.Elements[2][2] = -Result.Elements[2][2]; + + return Result; +} + +COVERAGE(HMM_InvOrthographic, 1) +// Returns an inverse for the given orthographic projection matrix. Works for all orthographic +// projection matrices, regardless of handedness or NDC convention. +static inline HMM_Mat4 HMM_InvOrthographic(HMM_Mat4 OrthoMatrix) +{ + ASSERT_COVERED(HMM_InvOrthographic); + + HMM_Mat4 Result = {0}; + Result.Elements[0][0] = 1.0f / OrthoMatrix.Elements[0][0]; + Result.Elements[1][1] = 1.0f / OrthoMatrix.Elements[1][1]; + Result.Elements[2][2] = 1.0f / OrthoMatrix.Elements[2][2]; + Result.Elements[3][3] = 1.0f; + + Result.Elements[3][0] = -OrthoMatrix.Elements[3][0] * Result.Elements[0][0]; + Result.Elements[3][1] = -OrthoMatrix.Elements[3][1] * Result.Elements[1][1]; + Result.Elements[3][2] = -OrthoMatrix.Elements[3][2] * Result.Elements[2][2]; + + return Result; +} + +COVERAGE(HMM_Perspective_RH_NO, 1) +static inline HMM_Mat4 HMM_Perspective_RH_NO(float FOV, float AspectRatio, float Near, float Far) +{ + ASSERT_COVERED(HMM_Perspective_RH_NO); + + HMM_Mat4 Result = {0}; + + // See https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/gluPerspective.xml + + float Cotangent = 1.0f / HMM_TanF(FOV / 2.0f); + Result.Elements[0][0] = Cotangent / AspectRatio; + Result.Elements[1][1] = Cotangent; + Result.Elements[2][3] = -1.0f; + + Result.Elements[2][2] = (Near + Far) / (Near - Far); + Result.Elements[3][2] = (2.0f * Near * Far) / (Near - Far); + + return Result; +} + +COVERAGE(HMM_Perspective_RH_ZO, 1) +static inline HMM_Mat4 HMM_Perspective_RH_ZO(float FOV, float AspectRatio, float Near, float Far) +{ + ASSERT_COVERED(HMM_Perspective_RH_ZO); + + HMM_Mat4 Result = {0}; + + // See https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/gluPerspective.xml + + float Cotangent = 1.0f / HMM_TanF(FOV / 2.0f); + Result.Elements[0][0] = Cotangent / AspectRatio; + Result.Elements[1][1] = Cotangent; + Result.Elements[2][3] = -1.0f; + + Result.Elements[2][2] = (Far) / (Near - Far); + Result.Elements[3][2] = (Near * Far) / (Near - Far); + + return Result; +} + +COVERAGE(HMM_Perspective_LH_NO, 1) +static inline HMM_Mat4 HMM_Perspective_LH_NO(float FOV, float AspectRatio, float Near, float Far) +{ + ASSERT_COVERED(HMM_Perspective_LH_NO); + + HMM_Mat4 Result = HMM_Perspective_RH_NO(FOV, AspectRatio, Near, Far); + Result.Elements[2][2] = -Result.Elements[2][2]; + Result.Elements[2][3] = -Result.Elements[2][3]; + + return Result; +} + +COVERAGE(HMM_Perspective_LH_ZO, 1) +static inline HMM_Mat4 HMM_Perspective_LH_ZO(float FOV, float AspectRatio, float Near, float Far) +{ + ASSERT_COVERED(HMM_Perspective_LH_ZO); + + HMM_Mat4 Result = HMM_Perspective_RH_ZO(FOV, AspectRatio, Near, Far); + Result.Elements[2][2] = -Result.Elements[2][2]; + Result.Elements[2][3] = -Result.Elements[2][3]; + + return Result; +} + +COVERAGE(HMM_InvPerspective_RH, 1) +static inline HMM_Mat4 HMM_InvPerspective_RH(HMM_Mat4 PerspectiveMatrix) +{ + ASSERT_COVERED(HMM_InvPerspective_RH); + + HMM_Mat4 Result = {0}; + Result.Elements[0][0] = 1.0f / PerspectiveMatrix.Elements[0][0]; + Result.Elements[1][1] = 1.0f / PerspectiveMatrix.Elements[1][1]; + Result.Elements[2][2] = 0.0f; + + Result.Elements[2][3] = 1.0f / PerspectiveMatrix.Elements[3][2]; + Result.Elements[3][3] = PerspectiveMatrix.Elements[2][2] * Result.Elements[2][3]; + Result.Elements[3][2] = PerspectiveMatrix.Elements[2][3]; + + return Result; +} + +COVERAGE(HMM_InvPerspective_LH, 1) +static inline HMM_Mat4 HMM_InvPerspective_LH(HMM_Mat4 PerspectiveMatrix) +{ + ASSERT_COVERED(HMM_InvPerspective_LH); + + HMM_Mat4 Result = {0}; + Result.Elements[0][0] = 1.0f / PerspectiveMatrix.Elements[0][0]; + Result.Elements[1][1] = 1.0f / PerspectiveMatrix.Elements[1][1]; + Result.Elements[2][2] = 0.0f; + + Result.Elements[2][3] = 1.0f / PerspectiveMatrix.Elements[3][2]; + Result.Elements[3][3] = PerspectiveMatrix.Elements[2][2] * -Result.Elements[2][3]; + Result.Elements[3][2] = PerspectiveMatrix.Elements[2][3]; + + return Result; +} + +COVERAGE(HMM_Translate, 1) +static inline HMM_Mat4 HMM_Translate(HMM_Vec3 Translation) +{ + ASSERT_COVERED(HMM_Translate); + + HMM_Mat4 Result = HMM_M4D(1.0f); + Result.Elements[3][0] = Translation.X; + Result.Elements[3][1] = Translation.Y; + Result.Elements[3][2] = Translation.Z; + + return Result; +} + +COVERAGE(HMM_InvTranslate, 1) +static inline HMM_Mat4 HMM_InvTranslate(HMM_Mat4 TranslationMatrix) +{ + ASSERT_COVERED(HMM_InvTranslate); + + HMM_Mat4 Result = TranslationMatrix; + Result.Elements[3][0] = -Result.Elements[3][0]; + Result.Elements[3][1] = -Result.Elements[3][1]; + Result.Elements[3][2] = -Result.Elements[3][2]; + + return Result; +} + +COVERAGE(HMM_Rotate_RH, 1) +static inline HMM_Mat4 HMM_Rotate_RH(float Angle, HMM_Vec3 Axis) +{ + ASSERT_COVERED(HMM_Rotate_RH); + + HMM_Mat4 Result = HMM_M4D(1.0f); + + Axis = HMM_NormV3(Axis); + + float SinTheta = HMM_SinF(Angle); + float CosTheta = HMM_CosF(Angle); + float CosValue = 1.0f - CosTheta; + + Result.Elements[0][0] = (Axis.X * Axis.X * CosValue) + CosTheta; + Result.Elements[0][1] = (Axis.X * Axis.Y * CosValue) + (Axis.Z * SinTheta); + Result.Elements[0][2] = (Axis.X * Axis.Z * CosValue) - (Axis.Y * SinTheta); + + Result.Elements[1][0] = (Axis.Y * Axis.X * CosValue) - (Axis.Z * SinTheta); + Result.Elements[1][1] = (Axis.Y * Axis.Y * CosValue) + CosTheta; + Result.Elements[1][2] = (Axis.Y * Axis.Z * CosValue) + (Axis.X * SinTheta); + + Result.Elements[2][0] = (Axis.Z * Axis.X * CosValue) + (Axis.Y * SinTheta); + Result.Elements[2][1] = (Axis.Z * Axis.Y * CosValue) - (Axis.X * SinTheta); + Result.Elements[2][2] = (Axis.Z * Axis.Z * CosValue) + CosTheta; + + return Result; +} + +COVERAGE(HMM_Rotate_LH, 1) +static inline HMM_Mat4 HMM_Rotate_LH(float Angle, HMM_Vec3 Axis) +{ + ASSERT_COVERED(HMM_Rotate_LH); + /* NOTE(lcf): Matrix will be inverse/transpose of RH. */ + return HMM_Rotate_RH(-Angle, Axis); +} + +COVERAGE(HMM_InvRotate, 1) +static inline HMM_Mat4 HMM_InvRotate(HMM_Mat4 RotationMatrix) +{ + ASSERT_COVERED(HMM_InvRotate); + return HMM_TransposeM4(RotationMatrix); +} + +COVERAGE(HMM_Scale, 1) +static inline HMM_Mat4 HMM_Scale(HMM_Vec3 Scale) +{ + ASSERT_COVERED(HMM_Scale); + + HMM_Mat4 Result = HMM_M4D(1.0f); + Result.Elements[0][0] = Scale.X; + Result.Elements[1][1] = Scale.Y; + Result.Elements[2][2] = Scale.Z; + + return Result; +} + +COVERAGE(HMM_InvScale, 1) +static inline HMM_Mat4 HMM_InvScale(HMM_Mat4 ScaleMatrix) +{ + ASSERT_COVERED(HMM_InvScale); + + HMM_Mat4 Result = ScaleMatrix; + Result.Elements[0][0] = 1.0f / Result.Elements[0][0]; + Result.Elements[1][1] = 1.0f / Result.Elements[1][1]; + Result.Elements[2][2] = 1.0f / Result.Elements[2][2]; + + return Result; +} + +static inline HMM_Mat4 _HMM_LookAt(HMM_Vec3 F, HMM_Vec3 S, HMM_Vec3 U, HMM_Vec3 Eye) +{ + HMM_Mat4 Result; + + Result.Elements[0][0] = S.X; + Result.Elements[0][1] = U.X; + Result.Elements[0][2] = -F.X; + Result.Elements[0][3] = 0.0f; + + Result.Elements[1][0] = S.Y; + Result.Elements[1][1] = U.Y; + Result.Elements[1][2] = -F.Y; + Result.Elements[1][3] = 0.0f; + + Result.Elements[2][0] = S.Z; + Result.Elements[2][1] = U.Z; + Result.Elements[2][2] = -F.Z; + Result.Elements[2][3] = 0.0f; + + Result.Elements[3][0] = -HMM_DotV3(S, Eye); + Result.Elements[3][1] = -HMM_DotV3(U, Eye); + Result.Elements[3][2] = HMM_DotV3(F, Eye); + Result.Elements[3][3] = 1.0f; + + return Result; +} + +COVERAGE(HMM_LookAt_RH, 1) +static inline HMM_Mat4 HMM_LookAt_RH(HMM_Vec3 Eye, HMM_Vec3 Center, HMM_Vec3 Up) +{ + ASSERT_COVERED(HMM_LookAt_RH); + + HMM_Vec3 F = HMM_NormV3(HMM_SubV3(Center, Eye)); + HMM_Vec3 S = HMM_NormV3(HMM_Cross(F, Up)); + HMM_Vec3 U = HMM_Cross(S, F); + + return _HMM_LookAt(F, S, U, Eye); +} + +COVERAGE(HMM_LookAt_LH, 1) +static inline HMM_Mat4 HMM_LookAt_LH(HMM_Vec3 Eye, HMM_Vec3 Center, HMM_Vec3 Up) +{ + ASSERT_COVERED(HMM_LookAt_LH); + + HMM_Vec3 F = HMM_NormV3(HMM_SubV3(Eye, Center)); + HMM_Vec3 S = HMM_NormV3(HMM_Cross(F, Up)); + HMM_Vec3 U = HMM_Cross(S, F); + + return _HMM_LookAt(F, S, U, Eye); +} + +COVERAGE(HMM_InvLookAt, 1) +static inline HMM_Mat4 HMM_InvLookAt(HMM_Mat4 Matrix) +{ + ASSERT_COVERED(HMM_InvLookAt); + HMM_Mat4 Result; + + HMM_Mat3 Rotation = {0}; + Rotation.Columns[0] = Matrix.Columns[0].XYZ; + Rotation.Columns[1] = Matrix.Columns[1].XYZ; + Rotation.Columns[2] = Matrix.Columns[2].XYZ; + Rotation = HMM_TransposeM3(Rotation); + + Result.Columns[0] = HMM_V4V(Rotation.Columns[0], 0.0f); + Result.Columns[1] = HMM_V4V(Rotation.Columns[1], 0.0f); + Result.Columns[2] = HMM_V4V(Rotation.Columns[2], 0.0f); + Result.Columns[3] = HMM_MulV4F(Matrix.Columns[3], -1.0f); + Result.Elements[3][0] = -1.0f * Matrix.Elements[3][0] / + (Rotation.Elements[0][0] + Rotation.Elements[0][1] + Rotation.Elements[0][2]); + Result.Elements[3][1] = -1.0f * Matrix.Elements[3][1] / + (Rotation.Elements[1][0] + Rotation.Elements[1][1] + Rotation.Elements[1][2]); + Result.Elements[3][2] = -1.0f * Matrix.Elements[3][2] / + (Rotation.Elements[2][0] + Rotation.Elements[2][1] + Rotation.Elements[2][2]); + Result.Elements[3][3] = 1.0f; + + return Result; +} + +/* + * Quaternion operations + */ + +COVERAGE(HMM_Q, 1) +static inline HMM_Quat HMM_Q(float X, float Y, float Z, float W) +{ + ASSERT_COVERED(HMM_Q); + + HMM_Quat Result; + +#ifdef HANDMADE_MATH__USE_SSE + Result.SSE = _mm_setr_ps(X, Y, Z, W); +#elif defined(HANDMADE_MATH__USE_NEON) + float32x4_t v = { X, Y, Z, W }; + Result.NEON = v; +#else + Result.X = X; + Result.Y = Y; + Result.Z = Z; + Result.W = W; +#endif + + return Result; +} + +COVERAGE(HMM_QV4, 1) +static inline HMM_Quat HMM_QV4(HMM_Vec4 Vector) +{ + ASSERT_COVERED(HMM_QV4); + + HMM_Quat Result; + +#ifdef HANDMADE_MATH__USE_SSE + Result.SSE = Vector.SSE; +#elif defined(HANDMADE_MATH__USE_NEON) + Result.NEON = Vector.NEON; +#else + Result.X = Vector.X; + Result.Y = Vector.Y; + Result.Z = Vector.Z; + Result.W = Vector.W; +#endif + + return Result; +} + +COVERAGE(HMM_AddQ, 1) +static inline HMM_Quat HMM_AddQ(HMM_Quat Left, HMM_Quat Right) +{ + ASSERT_COVERED(HMM_AddQ); + + HMM_Quat Result; + +#ifdef HANDMADE_MATH__USE_SSE + Result.SSE = _mm_add_ps(Left.SSE, Right.SSE); +#elif defined(HANDMADE_MATH__USE_NEON) + Result.NEON = vaddq_f32(Left.NEON, Right.NEON); +#else + + Result.X = Left.X + Right.X; + Result.Y = Left.Y + Right.Y; + Result.Z = Left.Z + Right.Z; + Result.W = Left.W + Right.W; +#endif + + return Result; +} + +COVERAGE(HMM_SubQ, 1) +static inline HMM_Quat HMM_SubQ(HMM_Quat Left, HMM_Quat Right) +{ + ASSERT_COVERED(HMM_SubQ); + + HMM_Quat Result; + +#ifdef HANDMADE_MATH__USE_SSE + Result.SSE = _mm_sub_ps(Left.SSE, Right.SSE); +#elif defined(HANDMADE_MATH__USE_NEON) + Result.NEON = vsubq_f32(Left.NEON, Right.NEON); +#else + Result.X = Left.X - Right.X; + Result.Y = Left.Y - Right.Y; + Result.Z = Left.Z - Right.Z; + Result.W = Left.W - Right.W; +#endif + + return Result; +} + +COVERAGE(HMM_MulQ, 1) +static inline HMM_Quat HMM_MulQ(HMM_Quat Left, HMM_Quat Right) +{ + ASSERT_COVERED(HMM_MulQ); + + HMM_Quat Result; + +#ifdef HANDMADE_MATH__USE_SSE + __m128 SSEResultOne = _mm_xor_ps(_mm_shuffle_ps(Left.SSE, Left.SSE, _MM_SHUFFLE(0, 0, 0, 0)), _mm_setr_ps(0.f, -0.f, 0.f, -0.f)); + __m128 SSEResultTwo = _mm_shuffle_ps(Right.SSE, Right.SSE, _MM_SHUFFLE(0, 1, 2, 3)); + __m128 SSEResultThree = _mm_mul_ps(SSEResultTwo, SSEResultOne); + + SSEResultOne = _mm_xor_ps(_mm_shuffle_ps(Left.SSE, Left.SSE, _MM_SHUFFLE(1, 1, 1, 1)) , _mm_setr_ps(0.f, 0.f, -0.f, -0.f)); + SSEResultTwo = _mm_shuffle_ps(Right.SSE, Right.SSE, _MM_SHUFFLE(1, 0, 3, 2)); + SSEResultThree = _mm_add_ps(SSEResultThree, _mm_mul_ps(SSEResultTwo, SSEResultOne)); + + SSEResultOne = _mm_xor_ps(_mm_shuffle_ps(Left.SSE, Left.SSE, _MM_SHUFFLE(2, 2, 2, 2)), _mm_setr_ps(-0.f, 0.f, 0.f, -0.f)); + SSEResultTwo = _mm_shuffle_ps(Right.SSE, Right.SSE, _MM_SHUFFLE(2, 3, 0, 1)); + SSEResultThree = _mm_add_ps(SSEResultThree, _mm_mul_ps(SSEResultTwo, SSEResultOne)); + + SSEResultOne = _mm_shuffle_ps(Left.SSE, Left.SSE, _MM_SHUFFLE(3, 3, 3, 3)); + SSEResultTwo = _mm_shuffle_ps(Right.SSE, Right.SSE, _MM_SHUFFLE(3, 2, 1, 0)); + Result.SSE = _mm_add_ps(SSEResultThree, _mm_mul_ps(SSEResultTwo, SSEResultOne)); +#elif defined(HANDMADE_MATH__USE_NEON) + float32x4_t Right1032 = vrev64q_f32(Right.NEON); + float32x4_t Right3210 = vcombine_f32(vget_high_f32(Right1032), vget_low_f32(Right1032)); + float32x4_t Right2301 = vrev64q_f32(Right3210); + + float32x4_t FirstSign = {1.0f, -1.0f, 1.0f, -1.0f}; + Result.NEON = vmulq_f32(Right3210, vmulq_f32(vdupq_laneq_f32(Left.NEON, 0), FirstSign)); + float32x4_t SecondSign = {1.0f, 1.0f, -1.0f, -1.0f}; + Result.NEON = vfmaq_f32(Result.NEON, Right2301, vmulq_f32(vdupq_laneq_f32(Left.NEON, 1), SecondSign)); + float32x4_t ThirdSign = {-1.0f, 1.0f, 1.0f, -1.0f}; + Result.NEON = vfmaq_f32(Result.NEON, Right1032, vmulq_f32(vdupq_laneq_f32(Left.NEON, 2), ThirdSign)); + Result.NEON = vfmaq_laneq_f32(Result.NEON, Right.NEON, Left.NEON, 3); + +#else + Result.X = Right.Elements[3] * +Left.Elements[0]; + Result.Y = Right.Elements[2] * -Left.Elements[0]; + Result.Z = Right.Elements[1] * +Left.Elements[0]; + Result.W = Right.Elements[0] * -Left.Elements[0]; + + Result.X += Right.Elements[2] * +Left.Elements[1]; + Result.Y += Right.Elements[3] * +Left.Elements[1]; + Result.Z += Right.Elements[0] * -Left.Elements[1]; + Result.W += Right.Elements[1] * -Left.Elements[1]; + + Result.X += Right.Elements[1] * -Left.Elements[2]; + Result.Y += Right.Elements[0] * +Left.Elements[2]; + Result.Z += Right.Elements[3] * +Left.Elements[2]; + Result.W += Right.Elements[2] * -Left.Elements[2]; + + Result.X += Right.Elements[0] * +Left.Elements[3]; + Result.Y += Right.Elements[1] * +Left.Elements[3]; + Result.Z += Right.Elements[2] * +Left.Elements[3]; + Result.W += Right.Elements[3] * +Left.Elements[3]; +#endif + + return Result; +} + +COVERAGE(HMM_MulQF, 1) +static inline HMM_Quat HMM_MulQF(HMM_Quat Left, float Multiplicative) +{ + ASSERT_COVERED(HMM_MulQF); + + HMM_Quat Result; + +#ifdef HANDMADE_MATH__USE_SSE + __m128 Scalar = _mm_set1_ps(Multiplicative); + Result.SSE = _mm_mul_ps(Left.SSE, Scalar); +#elif defined(HANDMADE_MATH__USE_NEON) + Result.NEON = vmulq_n_f32(Left.NEON, Multiplicative); +#else + Result.X = Left.X * Multiplicative; + Result.Y = Left.Y * Multiplicative; + Result.Z = Left.Z * Multiplicative; + Result.W = Left.W * Multiplicative; +#endif + + return Result; +} + +COVERAGE(HMM_DivQF, 1) +static inline HMM_Quat HMM_DivQF(HMM_Quat Left, float Divnd) +{ + ASSERT_COVERED(HMM_DivQF); + + HMM_Quat Result; + +#ifdef HANDMADE_MATH__USE_SSE + __m128 Scalar = _mm_set1_ps(Divnd); + Result.SSE = _mm_div_ps(Left.SSE, Scalar); +#elif defined(HANDMADE_MATH__USE_NEON) + float32x4_t Scalar = vdupq_n_f32(Divnd); + Result.NEON = vdivq_f32(Left.NEON, Scalar); +#else + Result.X = Left.X / Divnd; + Result.Y = Left.Y / Divnd; + Result.Z = Left.Z / Divnd; + Result.W = Left.W / Divnd; +#endif + + return Result; +} + +COVERAGE(HMM_DotQ, 1) +static inline float HMM_DotQ(HMM_Quat Left, HMM_Quat Right) +{ + ASSERT_COVERED(HMM_DotQ); + + float Result; + +#ifdef HANDMADE_MATH__USE_SSE + __m128 SSEResultOne = _mm_mul_ps(Left.SSE, Right.SSE); + __m128 SSEResultTwo = _mm_shuffle_ps(SSEResultOne, SSEResultOne, _MM_SHUFFLE(2, 3, 0, 1)); + SSEResultOne = _mm_add_ps(SSEResultOne, SSEResultTwo); + SSEResultTwo = _mm_shuffle_ps(SSEResultOne, SSEResultOne, _MM_SHUFFLE(0, 1, 2, 3)); + SSEResultOne = _mm_add_ps(SSEResultOne, SSEResultTwo); + _mm_store_ss(&Result, SSEResultOne); +#elif defined(HANDMADE_MATH__USE_NEON) + float32x4_t NEONMultiplyResult = vmulq_f32(Left.NEON, Right.NEON); + float32x4_t NEONHalfAdd = vpaddq_f32(NEONMultiplyResult, NEONMultiplyResult); + float32x4_t NEONFullAdd = vpaddq_f32(NEONHalfAdd, NEONHalfAdd); + Result = vgetq_lane_f32(NEONFullAdd, 0); +#else + Result = ((Left.X * Right.X) + (Left.Z * Right.Z)) + ((Left.Y * Right.Y) + (Left.W * Right.W)); +#endif + + return Result; +} + +COVERAGE(HMM_InvQ, 1) +static inline HMM_Quat HMM_InvQ(HMM_Quat Left) +{ + ASSERT_COVERED(HMM_InvQ); + + HMM_Quat Result; + Result.X = -Left.X; + Result.Y = -Left.Y; + Result.Z = -Left.Z; + Result.W = Left.W; + + return HMM_DivQF(Result, (HMM_DotQ(Left, Left))); +} + +COVERAGE(HMM_NormQ, 1) +static inline HMM_Quat HMM_NormQ(HMM_Quat Quat) +{ + ASSERT_COVERED(HMM_NormQ); + + /* NOTE(lcf): Take advantage of SSE implementation in HMM_NormV4 */ + HMM_Vec4 Vec = {Quat.X, Quat.Y, Quat.Z, Quat.W}; + Vec = HMM_NormV4(Vec); + HMM_Quat Result = {Vec.X, Vec.Y, Vec.Z, Vec.W}; + + return Result; +} + +static inline HMM_Quat _HMM_MixQ(HMM_Quat Left, float MixLeft, HMM_Quat Right, float MixRight) { + HMM_Quat Result; + +#ifdef HANDMADE_MATH__USE_SSE + __m128 ScalarLeft = _mm_set1_ps(MixLeft); + __m128 ScalarRight = _mm_set1_ps(MixRight); + __m128 SSEResultOne = _mm_mul_ps(Left.SSE, ScalarLeft); + __m128 SSEResultTwo = _mm_mul_ps(Right.SSE, ScalarRight); + Result.SSE = _mm_add_ps(SSEResultOne, SSEResultTwo); +#elif defined(HANDMADE_MATH__USE_NEON) + float32x4_t ScaledLeft = vmulq_n_f32(Left.NEON, MixLeft); + float32x4_t ScaledRight = vmulq_n_f32(Right.NEON, MixRight); + Result.NEON = vaddq_f32(ScaledLeft, ScaledRight); +#else + Result.X = Left.X*MixLeft + Right.X*MixRight; + Result.Y = Left.Y*MixLeft + Right.Y*MixRight; + Result.Z = Left.Z*MixLeft + Right.Z*MixRight; + Result.W = Left.W*MixLeft + Right.W*MixRight; +#endif + + return Result; +} + +COVERAGE(HMM_NLerp, 1) +static inline HMM_Quat HMM_NLerp(HMM_Quat Left, float Time, HMM_Quat Right) +{ + ASSERT_COVERED(HMM_NLerp); + + HMM_Quat Result = _HMM_MixQ(Left, 1.0f-Time, Right, Time); + Result = HMM_NormQ(Result); + + return Result; +} + +COVERAGE(HMM_SLerp, 1) +static inline HMM_Quat HMM_SLerp(HMM_Quat Left, float Time, HMM_Quat Right) +{ + ASSERT_COVERED(HMM_SLerp); + + HMM_Quat Result; + + float Cos_Theta = HMM_DotQ(Left, Right); + + if (Cos_Theta < 0.0f) { /* NOTE(lcf): Take shortest path on Hyper-sphere */ + Cos_Theta = -Cos_Theta; + Right = HMM_Q(-Right.X, -Right.Y, -Right.Z, -Right.W); + } + + /* NOTE(lcf): Use Normalized Linear interpolation when vectors are roughly not L.I. */ + if (Cos_Theta > 0.9995f) { + Result = HMM_NLerp(Left, Time, Right); + } else { + float Angle = HMM_ACosF(Cos_Theta); + float MixLeft = HMM_SinF((1.0f - Time) * Angle); + float MixRight = HMM_SinF(Time * Angle); + + Result = _HMM_MixQ(Left, MixLeft, Right, MixRight); + Result = HMM_NormQ(Result); + } + + return Result; +} + +COVERAGE(HMM_QToM4, 1) +static inline HMM_Mat4 HMM_QToM4(HMM_Quat Left) +{ + ASSERT_COVERED(HMM_QToM4); + + HMM_Mat4 Result; + + HMM_Quat NormalizedQ = HMM_NormQ(Left); + + float XX, YY, ZZ, + XY, XZ, YZ, + WX, WY, WZ; + + XX = NormalizedQ.X * NormalizedQ.X; + YY = NormalizedQ.Y * NormalizedQ.Y; + ZZ = NormalizedQ.Z * NormalizedQ.Z; + XY = NormalizedQ.X * NormalizedQ.Y; + XZ = NormalizedQ.X * NormalizedQ.Z; + YZ = NormalizedQ.Y * NormalizedQ.Z; + WX = NormalizedQ.W * NormalizedQ.X; + WY = NormalizedQ.W * NormalizedQ.Y; + WZ = NormalizedQ.W * NormalizedQ.Z; + + Result.Elements[0][0] = 1.0f - 2.0f * (YY + ZZ); + Result.Elements[0][1] = 2.0f * (XY + WZ); + Result.Elements[0][2] = 2.0f * (XZ - WY); + Result.Elements[0][3] = 0.0f; + + Result.Elements[1][0] = 2.0f * (XY - WZ); + Result.Elements[1][1] = 1.0f - 2.0f * (XX + ZZ); + Result.Elements[1][2] = 2.0f * (YZ + WX); + Result.Elements[1][3] = 0.0f; + + Result.Elements[2][0] = 2.0f * (XZ + WY); + Result.Elements[2][1] = 2.0f * (YZ - WX); + Result.Elements[2][2] = 1.0f - 2.0f * (XX + YY); + Result.Elements[2][3] = 0.0f; + + Result.Elements[3][0] = 0.0f; + Result.Elements[3][1] = 0.0f; + Result.Elements[3][2] = 0.0f; + Result.Elements[3][3] = 1.0f; + + return Result; +} + +// This method taken from Mike Day at Insomniac Games. +// https://d3cw3dd2w32x2b.cloudfront.net/wp-content/uploads/2015/01/matrix-to-quat.pdf +// +// Note that as mentioned at the top of the paper, the paper assumes the matrix +// would be *post*-multiplied to a vector to rotate it, meaning the matrix is +// the transpose of what we're dealing with. But, because our matrices are +// stored in column-major order, the indices *appear* to match the paper. +// +// For example, m12 in the paper is row 1, column 2. We need to transpose it to +// row 2, column 1. But, because the column comes first when referencing +// elements, it looks like M.Elements[1][2]. +// +// Don't be confused! Or if you must be confused, at least trust this +// comment. :) +COVERAGE(HMM_M4ToQ_RH, 4) +static inline HMM_Quat HMM_M4ToQ_RH(HMM_Mat4 M) +{ + float T; + HMM_Quat Q; + + if (M.Elements[2][2] < 0.0f) { + if (M.Elements[0][0] > M.Elements[1][1]) { + ASSERT_COVERED(HMM_M4ToQ_RH); + + T = 1 + M.Elements[0][0] - M.Elements[1][1] - M.Elements[2][2]; + Q = HMM_Q( + T, + M.Elements[0][1] + M.Elements[1][0], + M.Elements[2][0] + M.Elements[0][2], + M.Elements[1][2] - M.Elements[2][1] + ); + } else { + ASSERT_COVERED(HMM_M4ToQ_RH); + + T = 1 - M.Elements[0][0] + M.Elements[1][1] - M.Elements[2][2]; + Q = HMM_Q( + M.Elements[0][1] + M.Elements[1][0], + T, + M.Elements[1][2] + M.Elements[2][1], + M.Elements[2][0] - M.Elements[0][2] + ); + } + } else { + if (M.Elements[0][0] < -M.Elements[1][1]) { + ASSERT_COVERED(HMM_M4ToQ_RH); + + T = 1 - M.Elements[0][0] - M.Elements[1][1] + M.Elements[2][2]; + Q = HMM_Q( + M.Elements[2][0] + M.Elements[0][2], + M.Elements[1][2] + M.Elements[2][1], + T, + M.Elements[0][1] - M.Elements[1][0] + ); + } else { + ASSERT_COVERED(HMM_M4ToQ_RH); + + T = 1 + M.Elements[0][0] + M.Elements[1][1] + M.Elements[2][2]; + Q = HMM_Q( + M.Elements[1][2] - M.Elements[2][1], + M.Elements[2][0] - M.Elements[0][2], + M.Elements[0][1] - M.Elements[1][0], + T + ); + } + } + + Q = HMM_MulQF(Q, 0.5f / HMM_SqrtF(T)); + + return Q; +} + +COVERAGE(HMM_M4ToQ_LH, 4) +static inline HMM_Quat HMM_M4ToQ_LH(HMM_Mat4 M) +{ + float T; + HMM_Quat Q; + + if (M.Elements[2][2] < 0.0f) { + if (M.Elements[0][0] > M.Elements[1][1]) { + ASSERT_COVERED(HMM_M4ToQ_LH); + + T = 1 + M.Elements[0][0] - M.Elements[1][1] - M.Elements[2][2]; + Q = HMM_Q( + T, + M.Elements[0][1] + M.Elements[1][0], + M.Elements[2][0] + M.Elements[0][2], + M.Elements[2][1] - M.Elements[1][2] + ); + } else { + ASSERT_COVERED(HMM_M4ToQ_LH); + + T = 1 - M.Elements[0][0] + M.Elements[1][1] - M.Elements[2][2]; + Q = HMM_Q( + M.Elements[0][1] + M.Elements[1][0], + T, + M.Elements[1][2] + M.Elements[2][1], + M.Elements[0][2] - M.Elements[2][0] + ); + } + } else { + if (M.Elements[0][0] < -M.Elements[1][1]) { + ASSERT_COVERED(HMM_M4ToQ_LH); + + T = 1 - M.Elements[0][0] - M.Elements[1][1] + M.Elements[2][2]; + Q = HMM_Q( + M.Elements[2][0] + M.Elements[0][2], + M.Elements[1][2] + M.Elements[2][1], + T, + M.Elements[1][0] - M.Elements[0][1] + ); + } else { + ASSERT_COVERED(HMM_M4ToQ_LH); + + T = 1 + M.Elements[0][0] + M.Elements[1][1] + M.Elements[2][2]; + Q = HMM_Q( + M.Elements[2][1] - M.Elements[1][2], + M.Elements[0][2] - M.Elements[2][0], + M.Elements[1][0] - M.Elements[0][2], + T + ); + } + } + + Q = HMM_MulQF(Q, 0.5f / HMM_SqrtF(T)); + + return Q; +} + + +COVERAGE(HMM_QFromAxisAngle_RH, 1) +static inline HMM_Quat HMM_QFromAxisAngle_RH(HMM_Vec3 Axis, float Angle) +{ + ASSERT_COVERED(HMM_QFromAxisAngle_RH); + + HMM_Quat Result; + + HMM_Vec3 AxisNormalized = HMM_NormV3(Axis); + float SineOfRotation = HMM_SinF(Angle / 2.0f); + + Result.XYZ = HMM_MulV3F(AxisNormalized, SineOfRotation); + Result.W = HMM_CosF(Angle / 2.0f); + + return Result; +} + +COVERAGE(HMM_QFromAxisAngle_LH, 1) +static inline HMM_Quat HMM_QFromAxisAngle_LH(HMM_Vec3 Axis, float Angle) +{ + ASSERT_COVERED(HMM_QFromAxisAngle_LH); + + return HMM_QFromAxisAngle_RH(Axis, -Angle); +} + +COVERAGE(HMM_QFromNormPair, 1) +static inline HMM_Quat HMM_QFromNormPair(HMM_Vec3 Left, HMM_Vec3 Right) +{ + ASSERT_COVERED(HMM_QFromNormPair); + + HMM_Quat Result; + + Result.XYZ = HMM_Cross(Left, Right); + Result.W = 1.0f + HMM_DotV3(Left, Right); + + return HMM_NormQ(Result); +} + +COVERAGE(HMM_QFromVecPair, 1) +static inline HMM_Quat HMM_QFromVecPair(HMM_Vec3 Left, HMM_Vec3 Right) +{ + ASSERT_COVERED(HMM_QFromVecPair); + + return HMM_QFromNormPair(HMM_NormV3(Left), HMM_NormV3(Right)); +} + +COVERAGE(HMM_RotateV2, 1) +static inline HMM_Vec2 HMM_RotateV2(HMM_Vec2 V, float Angle) +{ + ASSERT_COVERED(HMM_RotateV2) + + float sinA = HMM_SinF(Angle); + float cosA = HMM_CosF(Angle); + + return HMM_V2(V.X * cosA - V.Y * sinA, V.X * sinA + V.Y * cosA); +} + +// implementation from +// https://blog.molecular-matters.com/2013/05/24/a-faster-quaternion-vector-multiplication/ +COVERAGE(HMM_RotateV3Q, 1) +static inline HMM_Vec3 HMM_RotateV3Q(HMM_Vec3 V, HMM_Quat Q) +{ + ASSERT_COVERED(HMM_RotateV3Q); + + HMM_Vec3 t = HMM_MulV3F(HMM_Cross(Q.XYZ, V), 2); + return HMM_AddV3(V, HMM_AddV3(HMM_MulV3F(t, Q.W), HMM_Cross(Q.XYZ, t))); +} + +COVERAGE(HMM_RotateV3AxisAngle_LH, 1) +static inline HMM_Vec3 HMM_RotateV3AxisAngle_LH(HMM_Vec3 V, HMM_Vec3 Axis, float Angle) { + ASSERT_COVERED(HMM_RotateV3AxisAngle_LH); + + return HMM_RotateV3Q(V, HMM_QFromAxisAngle_LH(Axis, Angle)); +} + +COVERAGE(HMM_RotateV3AxisAngle_RH, 1) +static inline HMM_Vec3 HMM_RotateV3AxisAngle_RH(HMM_Vec3 V, HMM_Vec3 Axis, float Angle) { + ASSERT_COVERED(HMM_RotateV3AxisAngle_RH); + + return HMM_RotateV3Q(V, HMM_QFromAxisAngle_RH(Axis, Angle)); +} + + +#ifdef __cplusplus +} +#endif + +#ifdef __cplusplus + +COVERAGE(HMM_LenV2CPP, 1) +static inline float HMM_Len(HMM_Vec2 A) +{ + ASSERT_COVERED(HMM_LenV2CPP); + return HMM_LenV2(A); +} + +COVERAGE(HMM_LenV3CPP, 1) +static inline float HMM_Len(HMM_Vec3 A) +{ + ASSERT_COVERED(HMM_LenV3CPP); + return HMM_LenV3(A); +} + +COVERAGE(HMM_LenV4CPP, 1) +static inline float HMM_Len(HMM_Vec4 A) +{ + ASSERT_COVERED(HMM_LenV4CPP); + return HMM_LenV4(A); +} + +COVERAGE(HMM_LenSqrV2CPP, 1) +static inline float HMM_LenSqr(HMM_Vec2 A) +{ + ASSERT_COVERED(HMM_LenSqrV2CPP); + return HMM_LenSqrV2(A); +} + +COVERAGE(HMM_LenSqrV3CPP, 1) +static inline float HMM_LenSqr(HMM_Vec3 A) +{ + ASSERT_COVERED(HMM_LenSqrV3CPP); + return HMM_LenSqrV3(A); +} + +COVERAGE(HMM_LenSqrV4CPP, 1) +static inline float HMM_LenSqr(HMM_Vec4 A) +{ + ASSERT_COVERED(HMM_LenSqrV4CPP); + return HMM_LenSqrV4(A); +} + +COVERAGE(HMM_NormV2CPP, 1) +static inline HMM_Vec2 HMM_Norm(HMM_Vec2 A) +{ + ASSERT_COVERED(HMM_NormV2CPP); + return HMM_NormV2(A); +} + +COVERAGE(HMM_NormV3CPP, 1) +static inline HMM_Vec3 HMM_Norm(HMM_Vec3 A) +{ + ASSERT_COVERED(HMM_NormV3CPP); + return HMM_NormV3(A); +} + +COVERAGE(HMM_NormV4CPP, 1) +static inline HMM_Vec4 HMM_Norm(HMM_Vec4 A) +{ + ASSERT_COVERED(HMM_NormV4CPP); + return HMM_NormV4(A); +} + +COVERAGE(HMM_NormQCPP, 1) +static inline HMM_Quat HMM_Norm(HMM_Quat A) +{ + ASSERT_COVERED(HMM_NormQCPP); + return HMM_NormQ(A); +} + +COVERAGE(HMM_DotV2CPP, 1) +static inline float HMM_Dot(HMM_Vec2 Left, HMM_Vec2 VecTwo) +{ + ASSERT_COVERED(HMM_DotV2CPP); + return HMM_DotV2(Left, VecTwo); +} + +COVERAGE(HMM_DotV3CPP, 1) +static inline float HMM_Dot(HMM_Vec3 Left, HMM_Vec3 VecTwo) +{ + ASSERT_COVERED(HMM_DotV3CPP); + return HMM_DotV3(Left, VecTwo); +} + +COVERAGE(HMM_DotV4CPP, 1) +static inline float HMM_Dot(HMM_Vec4 Left, HMM_Vec4 VecTwo) +{ + ASSERT_COVERED(HMM_DotV4CPP); + return HMM_DotV4(Left, VecTwo); +} + +COVERAGE(HMM_LerpV2CPP, 1) +static inline HMM_Vec2 HMM_Lerp(HMM_Vec2 Left, float Time, HMM_Vec2 Right) +{ + ASSERT_COVERED(HMM_LerpV2CPP); + return HMM_LerpV2(Left, Time, Right); +} + +COVERAGE(HMM_LerpV3CPP, 1) +static inline HMM_Vec3 HMM_Lerp(HMM_Vec3 Left, float Time, HMM_Vec3 Right) +{ + ASSERT_COVERED(HMM_LerpV3CPP); + return HMM_LerpV3(Left, Time, Right); +} + +COVERAGE(HMM_LerpV4CPP, 1) +static inline HMM_Vec4 HMM_Lerp(HMM_Vec4 Left, float Time, HMM_Vec4 Right) +{ + ASSERT_COVERED(HMM_LerpV4CPP); + return HMM_LerpV4(Left, Time, Right); +} + +COVERAGE(HMM_TransposeM2CPP, 1) +static inline HMM_Mat2 HMM_Transpose(HMM_Mat2 Matrix) +{ + ASSERT_COVERED(HMM_TransposeM2CPP); + return HMM_TransposeM2(Matrix); +} + +COVERAGE(HMM_TransposeM3CPP, 1) +static inline HMM_Mat3 HMM_Transpose(HMM_Mat3 Matrix) +{ + ASSERT_COVERED(HMM_TransposeM3CPP); + return HMM_TransposeM3(Matrix); +} + +COVERAGE(HMM_TransposeM4CPP, 1) +static inline HMM_Mat4 HMM_Transpose(HMM_Mat4 Matrix) +{ + ASSERT_COVERED(HMM_TransposeM4CPP); + return HMM_TransposeM4(Matrix); +} + +COVERAGE(HMM_DeterminantM2CPP, 1) +static inline float HMM_Determinant(HMM_Mat2 Matrix) +{ + ASSERT_COVERED(HMM_DeterminantM2CPP); + return HMM_DeterminantM2(Matrix); +} + +COVERAGE(HMM_DeterminantM3CPP, 1) +static inline float HMM_Determinant(HMM_Mat3 Matrix) +{ + ASSERT_COVERED(HMM_DeterminantM3CPP); + return HMM_DeterminantM3(Matrix); +} + +COVERAGE(HMM_DeterminantM4CPP, 1) +static inline float HMM_Determinant(HMM_Mat4 Matrix) +{ + ASSERT_COVERED(HMM_DeterminantM4CPP); + return HMM_DeterminantM4(Matrix); +} + +COVERAGE(HMM_InvGeneralM2CPP, 1) +static inline HMM_Mat2 HMM_InvGeneral(HMM_Mat2 Matrix) +{ + ASSERT_COVERED(HMM_InvGeneralM2CPP); + return HMM_InvGeneralM2(Matrix); +} + +COVERAGE(HMM_InvGeneralM3CPP, 1) +static inline HMM_Mat3 HMM_InvGeneral(HMM_Mat3 Matrix) +{ + ASSERT_COVERED(HMM_InvGeneralM3CPP); + return HMM_InvGeneralM3(Matrix); +} + +COVERAGE(HMM_InvGeneralM4CPP, 1) +static inline HMM_Mat4 HMM_InvGeneral(HMM_Mat4 Matrix) +{ + ASSERT_COVERED(HMM_InvGeneralM4CPP); + return HMM_InvGeneralM4(Matrix); +} + +COVERAGE(HMM_DotQCPP, 1) +static inline float HMM_Dot(HMM_Quat QuatOne, HMM_Quat QuatTwo) +{ + ASSERT_COVERED(HMM_DotQCPP); + return HMM_DotQ(QuatOne, QuatTwo); +} + +COVERAGE(HMM_AddV2CPP, 1) +static inline HMM_Vec2 HMM_Add(HMM_Vec2 Left, HMM_Vec2 Right) +{ + ASSERT_COVERED(HMM_AddV2CPP); + return HMM_AddV2(Left, Right); +} + +COVERAGE(HMM_AddV3CPP, 1) +static inline HMM_Vec3 HMM_Add(HMM_Vec3 Left, HMM_Vec3 Right) +{ + ASSERT_COVERED(HMM_AddV3CPP); + return HMM_AddV3(Left, Right); +} + +COVERAGE(HMM_AddV4CPP, 1) +static inline HMM_Vec4 HMM_Add(HMM_Vec4 Left, HMM_Vec4 Right) +{ + ASSERT_COVERED(HMM_AddV4CPP); + return HMM_AddV4(Left, Right); +} + +COVERAGE(HMM_AddM2CPP, 1) +static inline HMM_Mat2 HMM_Add(HMM_Mat2 Left, HMM_Mat2 Right) +{ + ASSERT_COVERED(HMM_AddM2CPP); + return HMM_AddM2(Left, Right); +} + +COVERAGE(HMM_AddM3CPP, 1) +static inline HMM_Mat3 HMM_Add(HMM_Mat3 Left, HMM_Mat3 Right) +{ + ASSERT_COVERED(HMM_AddM3CPP); + return HMM_AddM3(Left, Right); +} + +COVERAGE(HMM_AddM4CPP, 1) +static inline HMM_Mat4 HMM_Add(HMM_Mat4 Left, HMM_Mat4 Right) +{ + ASSERT_COVERED(HMM_AddM4CPP); + return HMM_AddM4(Left, Right); +} + +COVERAGE(HMM_AddQCPP, 1) +static inline HMM_Quat HMM_Add(HMM_Quat Left, HMM_Quat Right) +{ + ASSERT_COVERED(HMM_AddQCPP); + return HMM_AddQ(Left, Right); +} + +COVERAGE(HMM_SubV2CPP, 1) +static inline HMM_Vec2 HMM_Sub(HMM_Vec2 Left, HMM_Vec2 Right) +{ + ASSERT_COVERED(HMM_SubV2CPP); + return HMM_SubV2(Left, Right); +} + +COVERAGE(HMM_SubV3CPP, 1) +static inline HMM_Vec3 HMM_Sub(HMM_Vec3 Left, HMM_Vec3 Right) +{ + ASSERT_COVERED(HMM_SubV3CPP); + return HMM_SubV3(Left, Right); +} + +COVERAGE(HMM_SubV4CPP, 1) +static inline HMM_Vec4 HMM_Sub(HMM_Vec4 Left, HMM_Vec4 Right) +{ + ASSERT_COVERED(HMM_SubV4CPP); + return HMM_SubV4(Left, Right); +} + +COVERAGE(HMM_SubM2CPP, 1) +static inline HMM_Mat2 HMM_Sub(HMM_Mat2 Left, HMM_Mat2 Right) +{ + ASSERT_COVERED(HMM_SubM2CPP); + return HMM_SubM2(Left, Right); +} + +COVERAGE(HMM_SubM3CPP, 1) +static inline HMM_Mat3 HMM_Sub(HMM_Mat3 Left, HMM_Mat3 Right) +{ + ASSERT_COVERED(HMM_SubM3CPP); + return HMM_SubM3(Left, Right); +} + +COVERAGE(HMM_SubM4CPP, 1) +static inline HMM_Mat4 HMM_Sub(HMM_Mat4 Left, HMM_Mat4 Right) +{ + ASSERT_COVERED(HMM_SubM4CPP); + return HMM_SubM4(Left, Right); +} + +COVERAGE(HMM_SubQCPP, 1) +static inline HMM_Quat HMM_Sub(HMM_Quat Left, HMM_Quat Right) +{ + ASSERT_COVERED(HMM_SubQCPP); + return HMM_SubQ(Left, Right); +} + +COVERAGE(HMM_MulV2CPP, 1) +static inline HMM_Vec2 HMM_Mul(HMM_Vec2 Left, HMM_Vec2 Right) +{ + ASSERT_COVERED(HMM_MulV2CPP); + return HMM_MulV2(Left, Right); +} + +COVERAGE(HMM_MulV2FCPP, 1) +static inline HMM_Vec2 HMM_Mul(HMM_Vec2 Left, float Right) +{ + ASSERT_COVERED(HMM_MulV2FCPP); + return HMM_MulV2F(Left, Right); +} + +COVERAGE(HMM_MulV3CPP, 1) +static inline HMM_Vec3 HMM_Mul(HMM_Vec3 Left, HMM_Vec3 Right) +{ + ASSERT_COVERED(HMM_MulV3CPP); + return HMM_MulV3(Left, Right); +} + +COVERAGE(HMM_MulV3FCPP, 1) +static inline HMM_Vec3 HMM_Mul(HMM_Vec3 Left, float Right) +{ + ASSERT_COVERED(HMM_MulV3FCPP); + return HMM_MulV3F(Left, Right); +} + +COVERAGE(HMM_MulV4CPP, 1) +static inline HMM_Vec4 HMM_Mul(HMM_Vec4 Left, HMM_Vec4 Right) +{ + ASSERT_COVERED(HMM_MulV4CPP); + return HMM_MulV4(Left, Right); +} + +COVERAGE(HMM_MulV4FCPP, 1) +static inline HMM_Vec4 HMM_Mul(HMM_Vec4 Left, float Right) +{ + ASSERT_COVERED(HMM_MulV4FCPP); + return HMM_MulV4F(Left, Right); +} + +COVERAGE(HMM_MulM2CPP, 1) +static inline HMM_Mat2 HMM_Mul(HMM_Mat2 Left, HMM_Mat2 Right) +{ + ASSERT_COVERED(HMM_MulM2CPP); + return HMM_MulM2(Left, Right); +} + +COVERAGE(HMM_MulM3CPP, 1) +static inline HMM_Mat3 HMM_Mul(HMM_Mat3 Left, HMM_Mat3 Right) +{ + ASSERT_COVERED(HMM_MulM3CPP); + return HMM_MulM3(Left, Right); +} + +COVERAGE(HMM_MulM4CPP, 1) +static inline HMM_Mat4 HMM_Mul(HMM_Mat4 Left, HMM_Mat4 Right) +{ + ASSERT_COVERED(HMM_MulM4CPP); + return HMM_MulM4(Left, Right); +} + +COVERAGE(HMM_MulM2FCPP, 1) +static inline HMM_Mat2 HMM_Mul(HMM_Mat2 Left, float Right) +{ + ASSERT_COVERED(HMM_MulM2FCPP); + return HMM_MulM2F(Left, Right); +} + +COVERAGE(HMM_MulM3FCPP, 1) +static inline HMM_Mat3 HMM_Mul(HMM_Mat3 Left, float Right) +{ + ASSERT_COVERED(HMM_MulM3FCPP); + return HMM_MulM3F(Left, Right); +} + +COVERAGE(HMM_MulM4FCPP, 1) +static inline HMM_Mat4 HMM_Mul(HMM_Mat4 Left, float Right) +{ + ASSERT_COVERED(HMM_MulM4FCPP); + return HMM_MulM4F(Left, Right); +} + +COVERAGE(HMM_MulM2V2CPP, 1) +static inline HMM_Vec2 HMM_Mul(HMM_Mat2 Matrix, HMM_Vec2 Vector) +{ + ASSERT_COVERED(HMM_MulM2V2CPP); + return HMM_MulM2V2(Matrix, Vector); +} + +COVERAGE(HMM_MulM3V3CPP, 1) +static inline HMM_Vec3 HMM_Mul(HMM_Mat3 Matrix, HMM_Vec3 Vector) +{ + ASSERT_COVERED(HMM_MulM3V3CPP); + return HMM_MulM3V3(Matrix, Vector); +} + +COVERAGE(HMM_MulM4V4CPP, 1) +static inline HMM_Vec4 HMM_Mul(HMM_Mat4 Matrix, HMM_Vec4 Vector) +{ + ASSERT_COVERED(HMM_MulM4V4CPP); + return HMM_MulM4V4(Matrix, Vector); +} + +COVERAGE(HMM_MulQCPP, 1) +static inline HMM_Quat HMM_Mul(HMM_Quat Left, HMM_Quat Right) +{ + ASSERT_COVERED(HMM_MulQCPP); + return HMM_MulQ(Left, Right); +} + +COVERAGE(HMM_MulQFCPP, 1) +static inline HMM_Quat HMM_Mul(HMM_Quat Left, float Right) +{ + ASSERT_COVERED(HMM_MulQFCPP); + return HMM_MulQF(Left, Right); +} + +COVERAGE(HMM_DivV2CPP, 1) +static inline HMM_Vec2 HMM_Div(HMM_Vec2 Left, HMM_Vec2 Right) +{ + ASSERT_COVERED(HMM_DivV2CPP); + return HMM_DivV2(Left, Right); +} + +COVERAGE(HMM_DivV2FCPP, 1) +static inline HMM_Vec2 HMM_Div(HMM_Vec2 Left, float Right) +{ + ASSERT_COVERED(HMM_DivV2FCPP); + return HMM_DivV2F(Left, Right); +} + +COVERAGE(HMM_DivV3CPP, 1) +static inline HMM_Vec3 HMM_Div(HMM_Vec3 Left, HMM_Vec3 Right) +{ + ASSERT_COVERED(HMM_DivV3CPP); + return HMM_DivV3(Left, Right); +} + +COVERAGE(HMM_DivV3FCPP, 1) +static inline HMM_Vec3 HMM_Div(HMM_Vec3 Left, float Right) +{ + ASSERT_COVERED(HMM_DivV3FCPP); + return HMM_DivV3F(Left, Right); +} + +COVERAGE(HMM_DivV4CPP, 1) +static inline HMM_Vec4 HMM_Div(HMM_Vec4 Left, HMM_Vec4 Right) +{ + ASSERT_COVERED(HMM_DivV4CPP); + return HMM_DivV4(Left, Right); +} + +COVERAGE(HMM_DivV4FCPP, 1) +static inline HMM_Vec4 HMM_Div(HMM_Vec4 Left, float Right) +{ + ASSERT_COVERED(HMM_DivV4FCPP); + return HMM_DivV4F(Left, Right); +} + +COVERAGE(HMM_DivM2FCPP, 1) +static inline HMM_Mat2 HMM_Div(HMM_Mat2 Left, float Right) +{ + ASSERT_COVERED(HMM_DivM2FCPP); + return HMM_DivM2F(Left, Right); +} + +COVERAGE(HMM_DivM3FCPP, 1) +static inline HMM_Mat3 HMM_Div(HMM_Mat3 Left, float Right) +{ + ASSERT_COVERED(HMM_DivM3FCPP); + return HMM_DivM3F(Left, Right); +} + +COVERAGE(HMM_DivM4FCPP, 1) +static inline HMM_Mat4 HMM_Div(HMM_Mat4 Left, float Right) +{ + ASSERT_COVERED(HMM_DivM4FCPP); + return HMM_DivM4F(Left, Right); +} + +COVERAGE(HMM_DivQFCPP, 1) +static inline HMM_Quat HMM_Div(HMM_Quat Left, float Right) +{ + ASSERT_COVERED(HMM_DivQFCPP); + return HMM_DivQF(Left, Right); +} + +COVERAGE(HMM_EqV2CPP, 1) +static inline HMM_Bool HMM_Eq(HMM_Vec2 Left, HMM_Vec2 Right) +{ + ASSERT_COVERED(HMM_EqV2CPP); + return HMM_EqV2(Left, Right); +} + +COVERAGE(HMM_EqV3CPP, 1) +static inline HMM_Bool HMM_Eq(HMM_Vec3 Left, HMM_Vec3 Right) +{ + ASSERT_COVERED(HMM_EqV3CPP); + return HMM_EqV3(Left, Right); +} + +COVERAGE(HMM_EqV4CPP, 1) +static inline HMM_Bool HMM_Eq(HMM_Vec4 Left, HMM_Vec4 Right) +{ + ASSERT_COVERED(HMM_EqV4CPP); + return HMM_EqV4(Left, Right); +} + +COVERAGE(HMM_AddV2Op, 1) +static inline HMM_Vec2 operator+(HMM_Vec2 Left, HMM_Vec2 Right) +{ + ASSERT_COVERED(HMM_AddV2Op); + return HMM_AddV2(Left, Right); +} + +COVERAGE(HMM_AddV3Op, 1) +static inline HMM_Vec3 operator+(HMM_Vec3 Left, HMM_Vec3 Right) +{ + ASSERT_COVERED(HMM_AddV3Op); + return HMM_AddV3(Left, Right); +} + +COVERAGE(HMM_AddV4Op, 1) +static inline HMM_Vec4 operator+(HMM_Vec4 Left, HMM_Vec4 Right) +{ + ASSERT_COVERED(HMM_AddV4Op); + return HMM_AddV4(Left, Right); +} + +COVERAGE(HMM_AddM2Op, 1) +static inline HMM_Mat2 operator+(HMM_Mat2 Left, HMM_Mat2 Right) +{ + ASSERT_COVERED(HMM_AddM2Op); + return HMM_AddM2(Left, Right); +} + +COVERAGE(HMM_AddM3Op, 1) +static inline HMM_Mat3 operator+(HMM_Mat3 Left, HMM_Mat3 Right) +{ + ASSERT_COVERED(HMM_AddM3Op); + return HMM_AddM3(Left, Right); +} + +COVERAGE(HMM_AddM4Op, 1) +static inline HMM_Mat4 operator+(HMM_Mat4 Left, HMM_Mat4 Right) +{ + ASSERT_COVERED(HMM_AddM4Op); + return HMM_AddM4(Left, Right); +} + +COVERAGE(HMM_AddQOp, 1) +static inline HMM_Quat operator+(HMM_Quat Left, HMM_Quat Right) +{ + ASSERT_COVERED(HMM_AddQOp); + return HMM_AddQ(Left, Right); +} + +COVERAGE(HMM_SubV2Op, 1) +static inline HMM_Vec2 operator-(HMM_Vec2 Left, HMM_Vec2 Right) +{ + ASSERT_COVERED(HMM_SubV2Op); + return HMM_SubV2(Left, Right); +} + +COVERAGE(HMM_SubV3Op, 1) +static inline HMM_Vec3 operator-(HMM_Vec3 Left, HMM_Vec3 Right) +{ + ASSERT_COVERED(HMM_SubV3Op); + return HMM_SubV3(Left, Right); +} + +COVERAGE(HMM_SubV4Op, 1) +static inline HMM_Vec4 operator-(HMM_Vec4 Left, HMM_Vec4 Right) +{ + ASSERT_COVERED(HMM_SubV4Op); + return HMM_SubV4(Left, Right); +} + +COVERAGE(HMM_SubM2Op, 1) +static inline HMM_Mat2 operator-(HMM_Mat2 Left, HMM_Mat2 Right) +{ + ASSERT_COVERED(HMM_SubM2Op); + return HMM_SubM2(Left, Right); +} + +COVERAGE(HMM_SubM3Op, 1) +static inline HMM_Mat3 operator-(HMM_Mat3 Left, HMM_Mat3 Right) +{ + ASSERT_COVERED(HMM_SubM3Op); + return HMM_SubM3(Left, Right); +} + +COVERAGE(HMM_SubM4Op, 1) +static inline HMM_Mat4 operator-(HMM_Mat4 Left, HMM_Mat4 Right) +{ + ASSERT_COVERED(HMM_SubM4Op); + return HMM_SubM4(Left, Right); +} + +COVERAGE(HMM_SubQOp, 1) +static inline HMM_Quat operator-(HMM_Quat Left, HMM_Quat Right) +{ + ASSERT_COVERED(HMM_SubQOp); + return HMM_SubQ(Left, Right); +} + +COVERAGE(HMM_MulV2Op, 1) +static inline HMM_Vec2 operator*(HMM_Vec2 Left, HMM_Vec2 Right) +{ + ASSERT_COVERED(HMM_MulV2Op); + return HMM_MulV2(Left, Right); +} + +COVERAGE(HMM_MulV3Op, 1) +static inline HMM_Vec3 operator*(HMM_Vec3 Left, HMM_Vec3 Right) +{ + ASSERT_COVERED(HMM_MulV3Op); + return HMM_MulV3(Left, Right); +} + +COVERAGE(HMM_MulV4Op, 1) +static inline HMM_Vec4 operator*(HMM_Vec4 Left, HMM_Vec4 Right) +{ + ASSERT_COVERED(HMM_MulV4Op); + return HMM_MulV4(Left, Right); +} + +COVERAGE(HMM_MulM2Op, 1) +static inline HMM_Mat2 operator*(HMM_Mat2 Left, HMM_Mat2 Right) +{ + ASSERT_COVERED(HMM_MulM2Op); + return HMM_MulM2(Left, Right); +} + +COVERAGE(HMM_MulM3Op, 1) +static inline HMM_Mat3 operator*(HMM_Mat3 Left, HMM_Mat3 Right) +{ + ASSERT_COVERED(HMM_MulM3Op); + return HMM_MulM3(Left, Right); +} + +COVERAGE(HMM_MulM4Op, 1) +static inline HMM_Mat4 operator*(HMM_Mat4 Left, HMM_Mat4 Right) +{ + ASSERT_COVERED(HMM_MulM4Op); + return HMM_MulM4(Left, Right); +} + +COVERAGE(HMM_MulQOp, 1) +static inline HMM_Quat operator*(HMM_Quat Left, HMM_Quat Right) +{ + ASSERT_COVERED(HMM_MulQOp); + return HMM_MulQ(Left, Right); +} + +COVERAGE(HMM_MulV2FOp, 1) +static inline HMM_Vec2 operator*(HMM_Vec2 Left, float Right) +{ + ASSERT_COVERED(HMM_MulV2FOp); + return HMM_MulV2F(Left, Right); +} + +COVERAGE(HMM_MulV3FOp, 1) +static inline HMM_Vec3 operator*(HMM_Vec3 Left, float Right) +{ + ASSERT_COVERED(HMM_MulV3FOp); + return HMM_MulV3F(Left, Right); +} + +COVERAGE(HMM_MulV4FOp, 1) +static inline HMM_Vec4 operator*(HMM_Vec4 Left, float Right) +{ + ASSERT_COVERED(HMM_MulV4FOp); + return HMM_MulV4F(Left, Right); +} + +COVERAGE(HMM_MulM2FOp, 1) +static inline HMM_Mat2 operator*(HMM_Mat2 Left, float Right) +{ + ASSERT_COVERED(HMM_MulM2FOp); + return HMM_MulM2F(Left, Right); +} + +COVERAGE(HMM_MulM3FOp, 1) +static inline HMM_Mat3 operator*(HMM_Mat3 Left, float Right) +{ + ASSERT_COVERED(HMM_MulM3FOp); + return HMM_MulM3F(Left, Right); +} + +COVERAGE(HMM_MulM4FOp, 1) +static inline HMM_Mat4 operator*(HMM_Mat4 Left, float Right) +{ + ASSERT_COVERED(HMM_MulM4FOp); + return HMM_MulM4F(Left, Right); +} + +COVERAGE(HMM_MulQFOp, 1) +static inline HMM_Quat operator*(HMM_Quat Left, float Right) +{ + ASSERT_COVERED(HMM_MulQFOp); + return HMM_MulQF(Left, Right); +} + +COVERAGE(HMM_MulV2FOpLeft, 1) +static inline HMM_Vec2 operator*(float Left, HMM_Vec2 Right) +{ + ASSERT_COVERED(HMM_MulV2FOpLeft); + return HMM_MulV2F(Right, Left); +} + +COVERAGE(HMM_MulV3FOpLeft, 1) +static inline HMM_Vec3 operator*(float Left, HMM_Vec3 Right) +{ + ASSERT_COVERED(HMM_MulV3FOpLeft); + return HMM_MulV3F(Right, Left); +} + +COVERAGE(HMM_MulV4FOpLeft, 1) +static inline HMM_Vec4 operator*(float Left, HMM_Vec4 Right) +{ + ASSERT_COVERED(HMM_MulV4FOpLeft); + return HMM_MulV4F(Right, Left); +} + +COVERAGE(HMM_MulM2FOpLeft, 1) +static inline HMM_Mat2 operator*(float Left, HMM_Mat2 Right) +{ + ASSERT_COVERED(HMM_MulM2FOpLeft); + return HMM_MulM2F(Right, Left); +} + +COVERAGE(HMM_MulM3FOpLeft, 1) +static inline HMM_Mat3 operator*(float Left, HMM_Mat3 Right) +{ + ASSERT_COVERED(HMM_MulM3FOpLeft); + return HMM_MulM3F(Right, Left); +} + +COVERAGE(HMM_MulM4FOpLeft, 1) +static inline HMM_Mat4 operator*(float Left, HMM_Mat4 Right) +{ + ASSERT_COVERED(HMM_MulM4FOpLeft); + return HMM_MulM4F(Right, Left); +} + +COVERAGE(HMM_MulQFOpLeft, 1) +static inline HMM_Quat operator*(float Left, HMM_Quat Right) +{ + ASSERT_COVERED(HMM_MulQFOpLeft); + return HMM_MulQF(Right, Left); +} + +COVERAGE(HMM_MulM2V2Op, 1) +static inline HMM_Vec2 operator*(HMM_Mat2 Matrix, HMM_Vec2 Vector) +{ + ASSERT_COVERED(HMM_MulM2V2Op); + return HMM_MulM2V2(Matrix, Vector); +} + +COVERAGE(HMM_MulM3V3Op, 1) +static inline HMM_Vec3 operator*(HMM_Mat3 Matrix, HMM_Vec3 Vector) +{ + ASSERT_COVERED(HMM_MulM3V3Op); + return HMM_MulM3V3(Matrix, Vector); +} + +COVERAGE(HMM_MulM4V4Op, 1) +static inline HMM_Vec4 operator*(HMM_Mat4 Matrix, HMM_Vec4 Vector) +{ + ASSERT_COVERED(HMM_MulM4V4Op); + return HMM_MulM4V4(Matrix, Vector); +} + +COVERAGE(HMM_DivV2Op, 1) +static inline HMM_Vec2 operator/(HMM_Vec2 Left, HMM_Vec2 Right) +{ + ASSERT_COVERED(HMM_DivV2Op); + return HMM_DivV2(Left, Right); +} + +COVERAGE(HMM_DivV3Op, 1) +static inline HMM_Vec3 operator/(HMM_Vec3 Left, HMM_Vec3 Right) +{ + ASSERT_COVERED(HMM_DivV3Op); + return HMM_DivV3(Left, Right); +} + +COVERAGE(HMM_DivV4Op, 1) +static inline HMM_Vec4 operator/(HMM_Vec4 Left, HMM_Vec4 Right) +{ + ASSERT_COVERED(HMM_DivV4Op); + return HMM_DivV4(Left, Right); +} + +COVERAGE(HMM_DivV2FOp, 1) +static inline HMM_Vec2 operator/(HMM_Vec2 Left, float Right) +{ + ASSERT_COVERED(HMM_DivV2FOp); + return HMM_DivV2F(Left, Right); +} + +COVERAGE(HMM_DivV3FOp, 1) +static inline HMM_Vec3 operator/(HMM_Vec3 Left, float Right) +{ + ASSERT_COVERED(HMM_DivV3FOp); + return HMM_DivV3F(Left, Right); +} + +COVERAGE(HMM_DivV4FOp, 1) +static inline HMM_Vec4 operator/(HMM_Vec4 Left, float Right) +{ + ASSERT_COVERED(HMM_DivV4FOp); + return HMM_DivV4F(Left, Right); +} + +COVERAGE(HMM_DivM4FOp, 1) +static inline HMM_Mat4 operator/(HMM_Mat4 Left, float Right) +{ + ASSERT_COVERED(HMM_DivM4FOp); + return HMM_DivM4F(Left, Right); +} + +COVERAGE(HMM_DivM3FOp, 1) +static inline HMM_Mat3 operator/(HMM_Mat3 Left, float Right) +{ + ASSERT_COVERED(HMM_DivM3FOp); + return HMM_DivM3F(Left, Right); +} + +COVERAGE(HMM_DivM2FOp, 1) +static inline HMM_Mat2 operator/(HMM_Mat2 Left, float Right) +{ + ASSERT_COVERED(HMM_DivM2FOp); + return HMM_DivM2F(Left, Right); +} + +COVERAGE(HMM_DivQFOp, 1) +static inline HMM_Quat operator/(HMM_Quat Left, float Right) +{ + ASSERT_COVERED(HMM_DivQFOp); + return HMM_DivQF(Left, Right); +} + +COVERAGE(HMM_AddV2Assign, 1) +static inline HMM_Vec2 &operator+=(HMM_Vec2 &Left, HMM_Vec2 Right) +{ + ASSERT_COVERED(HMM_AddV2Assign); + return Left = Left + Right; +} + +COVERAGE(HMM_AddV3Assign, 1) +static inline HMM_Vec3 &operator+=(HMM_Vec3 &Left, HMM_Vec3 Right) +{ + ASSERT_COVERED(HMM_AddV3Assign); + return Left = Left + Right; +} + +COVERAGE(HMM_AddV4Assign, 1) +static inline HMM_Vec4 &operator+=(HMM_Vec4 &Left, HMM_Vec4 Right) +{ + ASSERT_COVERED(HMM_AddV4Assign); + return Left = Left + Right; +} + +COVERAGE(HMM_AddM2Assign, 1) +static inline HMM_Mat2 &operator+=(HMM_Mat2 &Left, HMM_Mat2 Right) +{ + ASSERT_COVERED(HMM_AddM2Assign); + return Left = Left + Right; +} + +COVERAGE(HMM_AddM3Assign, 1) +static inline HMM_Mat3 &operator+=(HMM_Mat3 &Left, HMM_Mat3 Right) +{ + ASSERT_COVERED(HMM_AddM3Assign); + return Left = Left + Right; +} + +COVERAGE(HMM_AddM4Assign, 1) +static inline HMM_Mat4 &operator+=(HMM_Mat4 &Left, HMM_Mat4 Right) +{ + ASSERT_COVERED(HMM_AddM4Assign); + return Left = Left + Right; +} + +COVERAGE(HMM_AddQAssign, 1) +static inline HMM_Quat &operator+=(HMM_Quat &Left, HMM_Quat Right) +{ + ASSERT_COVERED(HMM_AddQAssign); + return Left = Left + Right; +} + +COVERAGE(HMM_SubV2Assign, 1) +static inline HMM_Vec2 &operator-=(HMM_Vec2 &Left, HMM_Vec2 Right) +{ + ASSERT_COVERED(HMM_SubV2Assign); + return Left = Left - Right; +} + +COVERAGE(HMM_SubV3Assign, 1) +static inline HMM_Vec3 &operator-=(HMM_Vec3 &Left, HMM_Vec3 Right) +{ + ASSERT_COVERED(HMM_SubV3Assign); + return Left = Left - Right; +} + +COVERAGE(HMM_SubV4Assign, 1) +static inline HMM_Vec4 &operator-=(HMM_Vec4 &Left, HMM_Vec4 Right) +{ + ASSERT_COVERED(HMM_SubV4Assign); + return Left = Left - Right; +} + +COVERAGE(HMM_SubM2Assign, 1) +static inline HMM_Mat2 &operator-=(HMM_Mat2 &Left, HMM_Mat2 Right) +{ + ASSERT_COVERED(HMM_SubM2Assign); + return Left = Left - Right; +} + +COVERAGE(HMM_SubM3Assign, 1) +static inline HMM_Mat3 &operator-=(HMM_Mat3 &Left, HMM_Mat3 Right) +{ + ASSERT_COVERED(HMM_SubM3Assign); + return Left = Left - Right; +} + +COVERAGE(HMM_SubM4Assign, 1) +static inline HMM_Mat4 &operator-=(HMM_Mat4 &Left, HMM_Mat4 Right) +{ + ASSERT_COVERED(HMM_SubM4Assign); + return Left = Left - Right; +} + +COVERAGE(HMM_SubQAssign, 1) +static inline HMM_Quat &operator-=(HMM_Quat &Left, HMM_Quat Right) +{ + ASSERT_COVERED(HMM_SubQAssign); + return Left = Left - Right; +} + +COVERAGE(HMM_MulV2Assign, 1) +static inline HMM_Vec2 &operator*=(HMM_Vec2 &Left, HMM_Vec2 Right) +{ + ASSERT_COVERED(HMM_MulV2Assign); + return Left = Left * Right; +} + +COVERAGE(HMM_MulV3Assign, 1) +static inline HMM_Vec3 &operator*=(HMM_Vec3 &Left, HMM_Vec3 Right) +{ + ASSERT_COVERED(HMM_MulV3Assign); + return Left = Left * Right; +} + +COVERAGE(HMM_MulV4Assign, 1) +static inline HMM_Vec4 &operator*=(HMM_Vec4 &Left, HMM_Vec4 Right) +{ + ASSERT_COVERED(HMM_MulV4Assign); + return Left = Left * Right; +} + +COVERAGE(HMM_MulV2FAssign, 1) +static inline HMM_Vec2 &operator*=(HMM_Vec2 &Left, float Right) +{ + ASSERT_COVERED(HMM_MulV2FAssign); + return Left = Left * Right; +} + +COVERAGE(HMM_MulV3FAssign, 1) +static inline HMM_Vec3 &operator*=(HMM_Vec3 &Left, float Right) +{ + ASSERT_COVERED(HMM_MulV3FAssign); + return Left = Left * Right; +} + +COVERAGE(HMM_MulV4FAssign, 1) +static inline HMM_Vec4 &operator*=(HMM_Vec4 &Left, float Right) +{ + ASSERT_COVERED(HMM_MulV4FAssign); + return Left = Left * Right; +} + +COVERAGE(HMM_MulM2FAssign, 1) +static inline HMM_Mat2 &operator*=(HMM_Mat2 &Left, float Right) +{ + ASSERT_COVERED(HMM_MulM2FAssign); + return Left = Left * Right; +} + +COVERAGE(HMM_MulM3FAssign, 1) +static inline HMM_Mat3 &operator*=(HMM_Mat3 &Left, float Right) +{ + ASSERT_COVERED(HMM_MulM3FAssign); + return Left = Left * Right; +} + +COVERAGE(HMM_MulM4FAssign, 1) +static inline HMM_Mat4 &operator*=(HMM_Mat4 &Left, float Right) +{ + ASSERT_COVERED(HMM_MulM4FAssign); + return Left = Left * Right; +} + +COVERAGE(HMM_MulQFAssign, 1) +static inline HMM_Quat &operator*=(HMM_Quat &Left, float Right) +{ + ASSERT_COVERED(HMM_MulQFAssign); + return Left = Left * Right; +} + +COVERAGE(HMM_DivV2Assign, 1) +static inline HMM_Vec2 &operator/=(HMM_Vec2 &Left, HMM_Vec2 Right) +{ + ASSERT_COVERED(HMM_DivV2Assign); + return Left = Left / Right; +} + +COVERAGE(HMM_DivV3Assign, 1) +static inline HMM_Vec3 &operator/=(HMM_Vec3 &Left, HMM_Vec3 Right) +{ + ASSERT_COVERED(HMM_DivV3Assign); + return Left = Left / Right; +} + +COVERAGE(HMM_DivV4Assign, 1) +static inline HMM_Vec4 &operator/=(HMM_Vec4 &Left, HMM_Vec4 Right) +{ + ASSERT_COVERED(HMM_DivV4Assign); + return Left = Left / Right; +} + +COVERAGE(HMM_DivV2FAssign, 1) +static inline HMM_Vec2 &operator/=(HMM_Vec2 &Left, float Right) +{ + ASSERT_COVERED(HMM_DivV2FAssign); + return Left = Left / Right; +} + +COVERAGE(HMM_DivV3FAssign, 1) +static inline HMM_Vec3 &operator/=(HMM_Vec3 &Left, float Right) +{ + ASSERT_COVERED(HMM_DivV3FAssign); + return Left = Left / Right; +} + +COVERAGE(HMM_DivV4FAssign, 1) +static inline HMM_Vec4 &operator/=(HMM_Vec4 &Left, float Right) +{ + ASSERT_COVERED(HMM_DivV4FAssign); + return Left = Left / Right; +} + +COVERAGE(HMM_DivM4FAssign, 1) +static inline HMM_Mat4 &operator/=(HMM_Mat4 &Left, float Right) +{ + ASSERT_COVERED(HMM_DivM4FAssign); + return Left = Left / Right; +} + +COVERAGE(HMM_DivQFAssign, 1) +static inline HMM_Quat &operator/=(HMM_Quat &Left, float Right) +{ + ASSERT_COVERED(HMM_DivQFAssign); + return Left = Left / Right; +} + +COVERAGE(HMM_EqV2Op, 1) +static inline HMM_Bool operator==(HMM_Vec2 Left, HMM_Vec2 Right) +{ + ASSERT_COVERED(HMM_EqV2Op); + return HMM_EqV2(Left, Right); +} + +COVERAGE(HMM_EqV3Op, 1) +static inline HMM_Bool operator==(HMM_Vec3 Left, HMM_Vec3 Right) +{ + ASSERT_COVERED(HMM_EqV3Op); + return HMM_EqV3(Left, Right); +} + +COVERAGE(HMM_EqV4Op, 1) +static inline HMM_Bool operator==(HMM_Vec4 Left, HMM_Vec4 Right) +{ + ASSERT_COVERED(HMM_EqV4Op); + return HMM_EqV4(Left, Right); +} + +COVERAGE(HMM_EqV2OpNot, 1) +static inline HMM_Bool operator!=(HMM_Vec2 Left, HMM_Vec2 Right) +{ + ASSERT_COVERED(HMM_EqV2OpNot); + return !HMM_EqV2(Left, Right); +} + +COVERAGE(HMM_EqV3OpNot, 1) +static inline HMM_Bool operator!=(HMM_Vec3 Left, HMM_Vec3 Right) +{ + ASSERT_COVERED(HMM_EqV3OpNot); + return !HMM_EqV3(Left, Right); +} + +COVERAGE(HMM_EqV4OpNot, 1) +static inline HMM_Bool operator!=(HMM_Vec4 Left, HMM_Vec4 Right) +{ + ASSERT_COVERED(HMM_EqV4OpNot); + return !HMM_EqV4(Left, Right); +} + +COVERAGE(HMM_UnaryMinusV2, 1) +static inline HMM_Vec2 operator-(HMM_Vec2 In) +{ + ASSERT_COVERED(HMM_UnaryMinusV2); + + HMM_Vec2 Result; + Result.X = -In.X; + Result.Y = -In.Y; + + return Result; +} + +COVERAGE(HMM_UnaryMinusV3, 1) +static inline HMM_Vec3 operator-(HMM_Vec3 In) +{ + ASSERT_COVERED(HMM_UnaryMinusV3); + + HMM_Vec3 Result; + Result.X = -In.X; + Result.Y = -In.Y; + Result.Z = -In.Z; + + return Result; +} + +COVERAGE(HMM_UnaryMinusV4, 1) +static inline HMM_Vec4 operator-(HMM_Vec4 In) +{ + ASSERT_COVERED(HMM_UnaryMinusV4); + + HMM_Vec4 Result; +#if HANDMADE_MATH__USE_SSE + Result.SSE = _mm_xor_ps(In.SSE, _mm_set1_ps(-0.0f)); +#elif defined(HANDMADE_MATH__USE_NEON) + float32x4_t Zero = vdupq_n_f32(0.0f); + Result.NEON = vsubq_f32(Zero, In.NEON); +#else + Result.X = -In.X; + Result.Y = -In.Y; + Result.Z = -In.Z; + Result.W = -In.W; +#endif + + return Result; +} + +#endif /* __cplusplus*/ + +#ifdef HANDMADE_MATH__USE_C11_GENERICS + +void __hmm_invalid_generic(); + +#define HMM_Add(A, B) _Generic((A), \ + HMM_Vec2: HMM_AddV2, \ + HMM_Vec3: HMM_AddV3, \ + HMM_Vec4: HMM_AddV4, \ + HMM_Mat2: HMM_AddM2, \ + HMM_Mat3: HMM_AddM3, \ + HMM_Mat4: HMM_AddM4, \ + HMM_Quat: HMM_AddQ \ +)(A, B) + +#define HMM_Sub(A, B) _Generic((A), \ + HMM_Vec2: HMM_SubV2, \ + HMM_Vec3: HMM_SubV3, \ + HMM_Vec4: HMM_SubV4, \ + HMM_Mat2: HMM_SubM2, \ + HMM_Mat3: HMM_SubM3, \ + HMM_Mat4: HMM_SubM4, \ + HMM_Quat: HMM_SubQ \ +)(A, B) + +#define HMM_Mul(A, B) _Generic((B), \ + float: _Generic((A), \ + HMM_Vec2: HMM_MulV2F, \ + HMM_Vec3: HMM_MulV3F, \ + HMM_Vec4: HMM_MulV4F, \ + HMM_Mat2: HMM_MulM2F, \ + HMM_Mat3: HMM_MulM3F, \ + HMM_Mat4: HMM_MulM4F, \ + HMM_Quat: HMM_MulQF, \ + default: __hmm_invalid_generic \ + ), \ + HMM_Vec2: _Generic((A), \ + HMM_Vec2: HMM_MulV2, \ + HMM_Mat2: HMM_MulM2V2, \ + default: __hmm_invalid_generic \ + ), \ + HMM_Vec3: _Generic((A), \ + HMM_Vec3: HMM_MulV3, \ + HMM_Mat3: HMM_MulM3V3, \ + default: __hmm_invalid_generic \ + ), \ + HMM_Vec4: _Generic((A), \ + HMM_Vec4: HMM_MulV4, \ + HMM_Mat4: HMM_MulM4V4, \ + default: __hmm_invalid_generic \ + ), \ + HMM_Mat2: HMM_MulM2, \ + HMM_Mat3: HMM_MulM3, \ + HMM_Mat4: HMM_MulM4, \ + HMM_Quat: HMM_MulQ \ +)(A, B) + +#define HMM_Div(A, B) _Generic((B), \ + float: _Generic((A), \ + HMM_Vec2: HMM_DivV2F, \ + HMM_Vec3: HMM_DivV3F, \ + HMM_Vec4: HMM_DivV4F, \ + HMM_Mat2: HMM_DivM2F, \ + HMM_Mat3: HMM_DivM3F, \ + HMM_Mat4: HMM_DivM4F, \ + HMM_Quat: HMM_DivQF \ + ), \ + HMM_Vec2: HMM_DivV2, \ + HMM_Vec3: HMM_DivV3, \ + HMM_Vec4: HMM_DivV4 \ +)(A, B) + +#define HMM_Len(A) _Generic((A), \ + HMM_Vec2: HMM_LenV2, \ + HMM_Vec3: HMM_LenV3, \ + HMM_Vec4: HMM_LenV4 \ +)(A) + +#define HMM_LenSqr(A) _Generic((A), \ + HMM_Vec2: HMM_LenSqrV2, \ + HMM_Vec3: HMM_LenSqrV3, \ + HMM_Vec4: HMM_LenSqrV4 \ +)(A) + +#define HMM_Norm(A) _Generic((A), \ + HMM_Vec2: HMM_NormV2, \ + HMM_Vec3: HMM_NormV3, \ + HMM_Vec4: HMM_NormV4, \ + HMM_Quat: HMM_NormQ \ +)(A) + +#define HMM_Dot(A, B) _Generic((A), \ + HMM_Vec2: HMM_DotV2, \ + HMM_Vec3: HMM_DotV3, \ + HMM_Vec4: HMM_DotV4, \ + HMM_Quat: HMM_DotQ \ +)(A, B) + +#define HMM_Lerp(A, T, B) _Generic((A), \ + float: HMM_Lerp, \ + HMM_Vec2: HMM_LerpV2, \ + HMM_Vec3: HMM_LerpV3, \ + HMM_Vec4: HMM_LerpV4 \ +)(A, T, B) + +#define HMM_Eq(A, B) _Generic((A), \ + HMM_Vec2: HMM_EqV2, \ + HMM_Vec3: HMM_EqV3, \ + HMM_Vec4: HMM_EqV4 \ +)(A, B) + +#define HMM_Transpose(M) _Generic((M), \ + HMM_Mat2: HMM_TransposeM2, \ + HMM_Mat3: HMM_TransposeM3, \ + HMM_Mat4: HMM_TransposeM4 \ +)(M) + +#define HMM_Determinant(M) _Generic((M), \ + HMM_Mat2: HMM_DeterminantM2, \ + HMM_Mat3: HMM_DeterminantM3, \ + HMM_Mat4: HMM_DeterminantM4 \ +)(M) + +#define HMM_InvGeneral(M) _Generic((M), \ + HMM_Mat2: HMM_InvGeneralM2, \ + HMM_Mat3: HMM_InvGeneralM3, \ + HMM_Mat4: HMM_InvGeneralM4 \ +)(M) + +#endif + +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic pop +#endif + +#endif /* HANDMADE_MATH_H */ diff --git a/hmm/README b/hmm/README new file mode 100644 index 0000000..857494e --- /dev/null +++ b/hmm/README @@ -0,0 +1,49 @@ +---------------------------------------------------------- +Handmade Math +---------------------------------------------------------- + + jai ./generate.jai # generate the bindings (not required) + + #import "jc/hmm"( + STATIC = true, # if HMM should be linked statically (default: true) + SIMD = true, # if SIMD should be used (default: true) + UNITS = .radians, # angle units to use [radians, degrees, turns] (default: radians) + ); + +What +---- + +Configurable, auto-generated bindings for Handmade Math + +How +--- + +These are generated from HandmadeMath.h using Jai's +Bindings_Generator module. Because HandmadeMath is a +header-only library, we need to compile it into a +static/dynamic library that can be used with Jai's FFI +system. 'generate.jai' creates both static and dynamic +libraries for each angle unit in HandmadeMath +(radians, degrees, turns) +- SIMD support. These are +placed in the corresponding 'win', 'mac', or 'linux' +directories. + +'module.jai' conditionally links one of these libraries + based on the module parameters set. + +Why +--- + +A few liberties were taken during the binding process to +either fix issues with automatic binding generation, or +improve the usability of these bindings. + +Here are the main changes: + + - Converted procedure argument names from PascalCase + to snake_case + + - Converted struct field names from PascalCase to + snake_case + + - Procedure names still use PascalCase diff --git a/hmm/examples/degrees.jai b/hmm/examples/degrees.jai new file mode 100644 index 0000000..566bbde --- /dev/null +++ b/hmm/examples/degrees.jai @@ -0,0 +1,14 @@ +main :: () { + angle := hmm.HMM_DEG180.(float); + assert(angle == hmm.ToDeg(angle)); + assert(angle != hmm.ToRad(angle)); + assert(angle != hmm.ToTurn(angle)); +} + +hmm :: #import,file "../module.jai"( + STATIC = true, + SIMD = true, + UNITS = .degrees, +); + +#import "Basic"; diff --git a/hmm/examples/radians.jai b/hmm/examples/radians.jai new file mode 100644 index 0000000..00b540a --- /dev/null +++ b/hmm/examples/radians.jai @@ -0,0 +1,14 @@ +main :: () { + angle := hmm.HMM_PI.(float); + assert(angle == hmm.ToRad(angle)); + assert(angle != hmm.ToDeg(angle)); + assert(angle != hmm.ToTurn(angle)); +} + +hmm :: #import,file "../module.jai"( + STATIC = true, + SIMD = true, + UNITS = .radians, +); + +#import "Basic"; diff --git a/hmm/examples/turns.jai b/hmm/examples/turns.jai new file mode 100644 index 0000000..898a87d --- /dev/null +++ b/hmm/examples/turns.jai @@ -0,0 +1,14 @@ +main :: () { + angle := hmm.HMM_TURNHALF.(float); + assert(angle == hmm.ToTurn(angle)); + assert(angle != hmm.ToRad(angle)); + assert(angle != hmm.ToDeg(angle)); +} + +hmm :: #import,file "../module.jai"( + STATIC = true, + SIMD = true, + UNITS = .turns, +); + +#import "Basic"; diff --git a/hmm/generate.jai b/hmm/generate.jai new file mode 100644 index 0000000..b0da785 --- /dev/null +++ b/hmm/generate.jai @@ -0,0 +1,195 @@ +#scope_file; + +LOWERCASE_FIELD_NAMES :: true; + +#run { + set_build_options_dc(.{ do_output = false }); + + files_to_generate :: string.[ + "radians", + "degrees", + "turns", + ]; + + for files_to_generate { + print("building library for unit: %\n", it); + + assert(build(it, dynamic = true, simd = true), "failed to build library, unit: %", it); + assert(build(it, dynamic = true, simd = false), "failed to build library, unit: %", it); + assert(build(it, dynamic = false, simd = false), "failed to build library, unit: %", it); + assert(build(it, dynamic = false, simd = true), "failed to build library, unit: %", it); + } + + print("generating bindings\n"); + assert(generate(simd = true), "failed to generate bindings"); + assert(generate(simd = false), "failed to generate bindings"); +} + +build :: (unit: string, dynamic: bool, simd: bool) -> bool { + #if OS == { + case .WINDOWS; + export_attribute :: "__declspec(dllexport)"; + case; + export_attribute :: "__attribute__((visibility(\"default\")))"; + } + + tmp_data := tprint(#string END + #define static + #define inline + #define COVERAGE(a, b) %1 + + #define HANDMADE_MATH_USE_%2 + %3 + #include "../HandmadeMath.h" + END, + export_attribute, + to_upper_copy(unit), + ifx !simd then "#define HANDMADE_MATH_NO_SIMD" else "", + ); + + tmp_file := tprint(".build/tmp_%.c", ifx simd then "simd" else "nosimd"); + if !write_entire_file(tmp_file, tmp_data) return false; + + #if OS == { + case .WINDOWS; out_base :: "win"; + case .MACOS; out_base :: "mac"; + case .LINUX; out_base :: "linux"; + } + + lib_name := tprint("hmm_%", unit); + if simd lib_name = tprint("%_simd", lib_name); + else lib_name = tprint("%_nosimd", lib_name); + + out_path := tprint("%/%", out_base, lib_name); + + lib_type := Build_Type.STATIC_LIBRARY; + if dynamic lib_type = .DYNAMIC_LIBRARY; + + return build_cpp(out_path, tmp_file, type = lib_type); +} + +generate :: (simd: bool) -> bool { + #if OS == { + case .WINDOWS; + lib_ext :: "dll"; + case .MACOS; + lib_ext :: "dylib"; + case .LINUX; + lib_ext :: "so"; + } + + #if OS == { + case .WINDOWS; out_base :: "win"; + case .MACOS; out_base :: "mac"; + case .LINUX; out_base :: "linux"; + } + + suffix := "simd"; + if !simd suffix = "nosimd"; + + simd_support = simd; + + opts: Generate_Bindings_Options; + opts.add_generator_command = false; + opts.generate_library_declarations = false; + opts.visitor = fixup_wrong_types; + array_add(*opts.strip_prefixes, "HMM_", "_HMM_"); + array_add(*opts.extra_clang_arguments, "-x", "c"); + array_add(*opts.libpaths, out_base); + array_add(*opts.libnames, tprint("hmm_radians_%.%", suffix, lib_ext)); + array_add(*opts.source_files, tprint(".build/tmp_%.c", suffix)); + + if !generate_bindings(opts, tprint("hmm_%.jai", suffix)) { + return false; + } + + return true; +} + + +simd_support := false; +fixup_wrong_types :: (decl: *Declaration, parent: *Declaration) -> Declaration_Visit_Result { + if decl.kind == { + case .TYPEDEF; + td := decl.(*Typedef); + if td.name == { + // when simd support is specified, these are generated incorrectly so we create them manually + case "HMM_Vec4"; #through; + case "HMM_Quat"; + if simd_support { + td.decl_flags = .OMIT_FROM_OUTPUT; + td.type.type_of_struct.decl_flags = .OMIT_FROM_OUTPUT; + } + } + + #if !LOWERCASE_FIELD_NAMES return .STOP; + + lower_names_recursive :: (s: *Struct) { + for s.declarations { + it.output_name = pascal_to_snake(it.name); + if it.type.type_of_struct != null { + lower_names_recursive(it.type.type_of_struct); + } + } + } + + + if td.type.type_of_struct != null { + lower_names_recursive(td.type.type_of_struct); + } + + return .RECURSE; + + case .FUNCTION; + func := decl.(*Function); + if func.name == { + case "__hmm_invalid_generic"; + func.decl_flags = .OMIT_FROM_OUTPUT; + return .STOP; + } + + // snake_case argument names + funct := func.type.type_of_function; + for funct.arguments { + it.output_name = pascal_to_snake(it.name); + } + } + + return .STOP; +} + +pascal_to_snake :: (name: string) -> string { + is_upper :: (byte: u8) -> bool { + return byte >= #char "A" && byte <= #char "Z"; + } + + b: String_Builder; + add_underscore := false; + for 0..name.count - 1 { + if is_upper(name[it]) { + if it > 0 && name[it-1] != #char "_" { + add_underscore = true; + } + + if it-1 >= 0 && is_upper(name[it-1]) { + add_underscore = false; + } + } + + if add_underscore { + add_underscore = false; + append(*b, "_"); + } + + append(*b, to_lower(name[it])); + } + + return builder_to_string(*b); +} + +#import "File"; +#import "Basic"; +#import "String"; +#import "Compiler"; +#import "BuildCpp"; +#import "Bindings_Generator"; diff --git a/hmm/hmm_nosimd.jai b/hmm/hmm_nosimd.jai new file mode 100644 index 0000000..dcdc986 --- /dev/null +++ b/hmm/hmm_nosimd.jai @@ -0,0 +1,595 @@ +// +// This file was autogenerated. +// + + + +HANDMADE_MATH__USE_C11_GENERICS :: 1; + +HMM_PI :: 3.14159265358979323846; +HMM_PI32 :: 3.14159265359; +HMM_DEG180 :: 180.0; +HMM_DEG18032 :: 180.0; +HMM_TURNHALF :: 0.5; +HMM_TURNHALF32 :: 0.5; + +Vec2 :: union { + struct { + x: float; + y: float; + } + + struct { + u: float; + v: float; + } + + struct { + left: float; + right: float; + } + + struct { + width: float; + height: float; + } + + elements: [2] float; +} + +Vec3 :: union { + struct { + x: float; + y: float; + z: float; + } + + struct { + u: float; + v: float; + w: float; + } + + struct { + r: float; + g: float; + b: float; + } + + struct { + xy: Vec2; + _ignored0: float; + } + + struct { + _ignored1: float; + yz: Vec2; + } + + struct { + uv: Vec2; + _ignored2: float; + } + + struct { + _ignored3: float; + vw: Vec2; + } + + elements: [3] float; +} + +Vec4 :: union { + struct { + union { + xyz: Vec3; + struct { + x: float; + y: float; + z: float; + } + } + + w: float; + } + + struct { + union { + rgb: Vec3; + struct { + r: float; + g: float; + b: float; + } + } + + a: float; + } + + struct { + xy: Vec2; + _ignored0: float; + _ignored1: float; + } + + struct { + _ignored2: float; + yz: Vec2; + _ignored3: float; + } + + struct { + _ignored4: float; + _ignored5: float; + zw: Vec2; + } + + elements: [4] float; +} + +Mat2 :: union { + elements: [2] [2] float; + columns: [2] Vec2; +} + +Mat3 :: union { + elements: [3] [3] float; + columns: [3] Vec3; +} + +Mat4 :: union { + elements: [4] [4] float; + columns: [4] Vec4; +} + +Quat :: union { + struct { + union { + xyz: Vec3; + struct { + x: float; + y: float; + z: float; + } + } + + w: float; + } + + elements: [4] float; +} + +Bool :: s32; + +/* +* Angle unit conversion functions +*/ +ToRad :: (angle: float) -> float #foreign hmm_radians_nosimd "HMM_ToRad"; + +ToDeg :: (angle: float) -> float #foreign hmm_radians_nosimd "HMM_ToDeg"; + +ToTurn :: (angle: float) -> float #foreign hmm_radians_nosimd "HMM_ToTurn"; + +/* +* Floating-point math functions +*/ +SinF :: (angle: float) -> float #foreign hmm_radians_nosimd "HMM_SinF"; + +CosF :: (angle: float) -> float #foreign hmm_radians_nosimd "HMM_CosF"; + +TanF :: (angle: float) -> float #foreign hmm_radians_nosimd "HMM_TanF"; + +ACosF :: (arg: float) -> float #foreign hmm_radians_nosimd "HMM_ACosF"; + +SqrtF :: (_float: float) -> float #foreign hmm_radians_nosimd "HMM_SqrtF"; + +InvSqrtF :: (_float: float) -> float #foreign hmm_radians_nosimd "HMM_InvSqrtF"; + +/* +* Utility functions +*/ +Lerp :: (a: float, time: float, b: float) -> float #foreign hmm_radians_nosimd "HMM_Lerp"; + +Clamp :: (min: float, value: float, max: float) -> float #foreign hmm_radians_nosimd "HMM_Clamp"; + +/* +* Vector initialization +*/ +V2 :: (x: float, y: float) -> Vec2 #foreign hmm_radians_nosimd "HMM_V2"; + +V3 :: (x: float, y: float, z: float) -> Vec3 #foreign hmm_radians_nosimd "HMM_V3"; + +V4 :: (x: float, y: float, z: float, w: float) -> Vec4 #foreign hmm_radians_nosimd "HMM_V4"; + +V4V :: (vector: Vec3, w: float) -> Vec4 #foreign hmm_radians_nosimd "HMM_V4V"; + +/* +* Binary vector operations +*/ +AddV2 :: (left: Vec2, right: Vec2) -> Vec2 #foreign hmm_radians_nosimd "HMM_AddV2"; + +AddV3 :: (left: Vec3, right: Vec3) -> Vec3 #foreign hmm_radians_nosimd "HMM_AddV3"; + +AddV4 :: (left: Vec4, right: Vec4) -> Vec4 #foreign hmm_radians_nosimd "HMM_AddV4"; + +SubV2 :: (left: Vec2, right: Vec2) -> Vec2 #foreign hmm_radians_nosimd "HMM_SubV2"; + +SubV3 :: (left: Vec3, right: Vec3) -> Vec3 #foreign hmm_radians_nosimd "HMM_SubV3"; + +SubV4 :: (left: Vec4, right: Vec4) -> Vec4 #foreign hmm_radians_nosimd "HMM_SubV4"; + +MulV2 :: (left: Vec2, right: Vec2) -> Vec2 #foreign hmm_radians_nosimd "HMM_MulV2"; + +MulV2F :: (left: Vec2, right: float) -> Vec2 #foreign hmm_radians_nosimd "HMM_MulV2F"; + +MulV3 :: (left: Vec3, right: Vec3) -> Vec3 #foreign hmm_radians_nosimd "HMM_MulV3"; + +MulV3F :: (left: Vec3, right: float) -> Vec3 #foreign hmm_radians_nosimd "HMM_MulV3F"; + +MulV4 :: (left: Vec4, right: Vec4) -> Vec4 #foreign hmm_radians_nosimd "HMM_MulV4"; + +MulV4F :: (left: Vec4, right: float) -> Vec4 #foreign hmm_radians_nosimd "HMM_MulV4F"; + +DivV2 :: (left: Vec2, right: Vec2) -> Vec2 #foreign hmm_radians_nosimd "HMM_DivV2"; + +DivV2F :: (left: Vec2, right: float) -> Vec2 #foreign hmm_radians_nosimd "HMM_DivV2F"; + +DivV3 :: (left: Vec3, right: Vec3) -> Vec3 #foreign hmm_radians_nosimd "HMM_DivV3"; + +DivV3F :: (left: Vec3, right: float) -> Vec3 #foreign hmm_radians_nosimd "HMM_DivV3F"; + +DivV4 :: (left: Vec4, right: Vec4) -> Vec4 #foreign hmm_radians_nosimd "HMM_DivV4"; + +DivV4F :: (left: Vec4, right: float) -> Vec4 #foreign hmm_radians_nosimd "HMM_DivV4F"; + +EqV2 :: (left: Vec2, right: Vec2) -> Bool #foreign hmm_radians_nosimd "HMM_EqV2"; + +EqV3 :: (left: Vec3, right: Vec3) -> Bool #foreign hmm_radians_nosimd "HMM_EqV3"; + +EqV4 :: (left: Vec4, right: Vec4) -> Bool #foreign hmm_radians_nosimd "HMM_EqV4"; + +DotV2 :: (left: Vec2, right: Vec2) -> float #foreign hmm_radians_nosimd "HMM_DotV2"; + +DotV3 :: (left: Vec3, right: Vec3) -> float #foreign hmm_radians_nosimd "HMM_DotV3"; + +DotV4 :: (left: Vec4, right: Vec4) -> float #foreign hmm_radians_nosimd "HMM_DotV4"; + +Cross :: (left: Vec3, right: Vec3) -> Vec3 #foreign hmm_radians_nosimd "HMM_Cross"; + +/* +* Unary vector operations +*/ +LenSqrV2 :: (a: Vec2) -> float #foreign hmm_radians_nosimd "HMM_LenSqrV2"; + +LenSqrV3 :: (a: Vec3) -> float #foreign hmm_radians_nosimd "HMM_LenSqrV3"; + +LenSqrV4 :: (a: Vec4) -> float #foreign hmm_radians_nosimd "HMM_LenSqrV4"; + +LenV2 :: (a: Vec2) -> float #foreign hmm_radians_nosimd "HMM_LenV2"; + +LenV3 :: (a: Vec3) -> float #foreign hmm_radians_nosimd "HMM_LenV3"; + +LenV4 :: (a: Vec4) -> float #foreign hmm_radians_nosimd "HMM_LenV4"; + +NormV2 :: (a: Vec2) -> Vec2 #foreign hmm_radians_nosimd "HMM_NormV2"; + +NormV3 :: (a: Vec3) -> Vec3 #foreign hmm_radians_nosimd "HMM_NormV3"; + +NormV4 :: (a: Vec4) -> Vec4 #foreign hmm_radians_nosimd "HMM_NormV4"; + +/* +* Utility vector functions +*/ +LerpV2 :: (a: Vec2, time: float, b: Vec2) -> Vec2 #foreign hmm_radians_nosimd "HMM_LerpV2"; + +LerpV3 :: (a: Vec3, time: float, b: Vec3) -> Vec3 #foreign hmm_radians_nosimd "HMM_LerpV3"; + +LerpV4 :: (a: Vec4, time: float, b: Vec4) -> Vec4 #foreign hmm_radians_nosimd "HMM_LerpV4"; + +/* +* SSE stuff +*/ +LinearCombineV4M4 :: (left: Vec4, right: Mat4) -> Vec4 #foreign hmm_radians_nosimd "HMM_LinearCombineV4M4"; + +/* +* 2x2 Matrices +*/ +M2 :: () -> Mat2 #foreign hmm_radians_nosimd "HMM_M2"; + +M2D :: (diagonal: float) -> Mat2 #foreign hmm_radians_nosimd "HMM_M2D"; + +TransposeM2 :: (matrix: Mat2) -> Mat2 #foreign hmm_radians_nosimd "HMM_TransposeM2"; + +AddM2 :: (left: Mat2, right: Mat2) -> Mat2 #foreign hmm_radians_nosimd "HMM_AddM2"; + +SubM2 :: (left: Mat2, right: Mat2) -> Mat2 #foreign hmm_radians_nosimd "HMM_SubM2"; + +MulM2V2 :: (matrix: Mat2, vector: Vec2) -> Vec2 #foreign hmm_radians_nosimd "HMM_MulM2V2"; + +MulM2 :: (left: Mat2, right: Mat2) -> Mat2 #foreign hmm_radians_nosimd "HMM_MulM2"; + +MulM2F :: (matrix: Mat2, scalar: float) -> Mat2 #foreign hmm_radians_nosimd "HMM_MulM2F"; + +DivM2F :: (matrix: Mat2, scalar: float) -> Mat2 #foreign hmm_radians_nosimd "HMM_DivM2F"; + +DeterminantM2 :: (matrix: Mat2) -> float #foreign hmm_radians_nosimd "HMM_DeterminantM2"; + +InvGeneralM2 :: (matrix: Mat2) -> Mat2 #foreign hmm_radians_nosimd "HMM_InvGeneralM2"; + +/* +* 3x3 Matrices +*/ +M3 :: () -> Mat3 #foreign hmm_radians_nosimd "HMM_M3"; + +M3D :: (diagonal: float) -> Mat3 #foreign hmm_radians_nosimd "HMM_M3D"; + +TransposeM3 :: (matrix: Mat3) -> Mat3 #foreign hmm_radians_nosimd "HMM_TransposeM3"; + +AddM3 :: (left: Mat3, right: Mat3) -> Mat3 #foreign hmm_radians_nosimd "HMM_AddM3"; + +SubM3 :: (left: Mat3, right: Mat3) -> Mat3 #foreign hmm_radians_nosimd "HMM_SubM3"; + +MulM3V3 :: (matrix: Mat3, vector: Vec3) -> Vec3 #foreign hmm_radians_nosimd "HMM_MulM3V3"; + +MulM3 :: (left: Mat3, right: Mat3) -> Mat3 #foreign hmm_radians_nosimd "HMM_MulM3"; + +MulM3F :: (matrix: Mat3, scalar: float) -> Mat3 #foreign hmm_radians_nosimd "HMM_MulM3F"; + +DivM3F :: (matrix: Mat3, scalar: float) -> Mat3 #foreign hmm_radians_nosimd "HMM_DivM3F"; + +DeterminantM3 :: (matrix: Mat3) -> float #foreign hmm_radians_nosimd "HMM_DeterminantM3"; + +InvGeneralM3 :: (matrix: Mat3) -> Mat3 #foreign hmm_radians_nosimd "HMM_InvGeneralM3"; + +/* +* 4x4 Matrices +*/ +M4 :: () -> Mat4 #foreign hmm_radians_nosimd "HMM_M4"; + +M4D :: (diagonal: float) -> Mat4 #foreign hmm_radians_nosimd "HMM_M4D"; + +TransposeM4 :: (matrix: Mat4) -> Mat4 #foreign hmm_radians_nosimd "HMM_TransposeM4"; + +AddM4 :: (left: Mat4, right: Mat4) -> Mat4 #foreign hmm_radians_nosimd "HMM_AddM4"; + +SubM4 :: (left: Mat4, right: Mat4) -> Mat4 #foreign hmm_radians_nosimd "HMM_SubM4"; + +MulM4 :: (left: Mat4, right: Mat4) -> Mat4 #foreign hmm_radians_nosimd "HMM_MulM4"; + +MulM4F :: (matrix: Mat4, scalar: float) -> Mat4 #foreign hmm_radians_nosimd "HMM_MulM4F"; + +MulM4V4 :: (matrix: Mat4, vector: Vec4) -> Vec4 #foreign hmm_radians_nosimd "HMM_MulM4V4"; + +DivM4F :: (matrix: Mat4, scalar: float) -> Mat4 #foreign hmm_radians_nosimd "HMM_DivM4F"; + +DeterminantM4 :: (matrix: Mat4) -> float #foreign hmm_radians_nosimd "HMM_DeterminantM4"; + +// Returns a general-purpose inverse of an HMM_Mat4. Note that special-purpose inverses of many transformations +// are available and will be more efficient. +InvGeneralM4 :: (matrix: Mat4) -> Mat4 #foreign hmm_radians_nosimd "HMM_InvGeneralM4"; + +// Produces a right-handed orthographic projection matrix with Z ranging from -1 to 1 (the GL convention). +// Left, Right, Bottom, and Top specify the coordinates of their respective clipping planes. +// Near and Far specify the distances to the near and far clipping planes. +Orthographic_RH_NO :: (left: float, right: float, bottom: float, top: float, near: float, far: float) -> Mat4 #foreign hmm_radians_nosimd "HMM_Orthographic_RH_NO"; + +// Produces a right-handed orthographic projection matrix with Z ranging from 0 to 1 (the DirectX convention). +// Left, Right, Bottom, and Top specify the coordinates of their respective clipping planes. +// Near and Far specify the distances to the near and far clipping planes. +Orthographic_RH_ZO :: (left: float, right: float, bottom: float, top: float, near: float, far: float) -> Mat4 #foreign hmm_radians_nosimd "HMM_Orthographic_RH_ZO"; + +// Produces a left-handed orthographic projection matrix with Z ranging from -1 to 1 (the GL convention). +// Left, Right, Bottom, and Top specify the coordinates of their respective clipping planes. +// Near and Far specify the distances to the near and far clipping planes. +Orthographic_LH_NO :: (left: float, right: float, bottom: float, top: float, near: float, far: float) -> Mat4 #foreign hmm_radians_nosimd "HMM_Orthographic_LH_NO"; + +// Produces a left-handed orthographic projection matrix with Z ranging from 0 to 1 (the DirectX convention). +// Left, Right, Bottom, and Top specify the coordinates of their respective clipping planes. +// Near and Far specify the distances to the near and far clipping planes. +Orthographic_LH_ZO :: (left: float, right: float, bottom: float, top: float, near: float, far: float) -> Mat4 #foreign hmm_radians_nosimd "HMM_Orthographic_LH_ZO"; + +// Returns an inverse for the given orthographic projection matrix. Works for all orthographic +// projection matrices, regardless of handedness or NDC convention. +InvOrthographic :: (ortho_matrix: Mat4) -> Mat4 #foreign hmm_radians_nosimd "HMM_InvOrthographic"; + +Perspective_RH_NO :: (fov: float, aspect_ratio: float, near: float, far: float) -> Mat4 #foreign hmm_radians_nosimd "HMM_Perspective_RH_NO"; + +Perspective_RH_ZO :: (fov: float, aspect_ratio: float, near: float, far: float) -> Mat4 #foreign hmm_radians_nosimd "HMM_Perspective_RH_ZO"; + +Perspective_LH_NO :: (fov: float, aspect_ratio: float, near: float, far: float) -> Mat4 #foreign hmm_radians_nosimd "HMM_Perspective_LH_NO"; + +Perspective_LH_ZO :: (fov: float, aspect_ratio: float, near: float, far: float) -> Mat4 #foreign hmm_radians_nosimd "HMM_Perspective_LH_ZO"; + +InvPerspective_RH :: (perspective_matrix: Mat4) -> Mat4 #foreign hmm_radians_nosimd "HMM_InvPerspective_RH"; + +InvPerspective_LH :: (perspective_matrix: Mat4) -> Mat4 #foreign hmm_radians_nosimd "HMM_InvPerspective_LH"; + +Translate :: (translation: Vec3) -> Mat4 #foreign hmm_radians_nosimd "HMM_Translate"; + +InvTranslate :: (translation_matrix: Mat4) -> Mat4 #foreign hmm_radians_nosimd "HMM_InvTranslate"; + +Rotate_RH :: (angle: float, axis: Vec3) -> Mat4 #foreign hmm_radians_nosimd "HMM_Rotate_RH"; + +Rotate_LH :: (angle: float, axis: Vec3) -> Mat4 #foreign hmm_radians_nosimd "HMM_Rotate_LH"; + +InvRotate :: (rotation_matrix: Mat4) -> Mat4 #foreign hmm_radians_nosimd "HMM_InvRotate"; + +Scale :: (scale: Vec3) -> Mat4 #foreign hmm_radians_nosimd "HMM_Scale"; + +InvScale :: (scale_matrix: Mat4) -> Mat4 #foreign hmm_radians_nosimd "HMM_InvScale"; + +LookAt :: (f: Vec3, s: Vec3, u: Vec3, eye: Vec3) -> Mat4 #foreign hmm_radians_nosimd "_HMM_LookAt"; + +LookAt_RH :: (eye: Vec3, center: Vec3, up: Vec3) -> Mat4 #foreign hmm_radians_nosimd "HMM_LookAt_RH"; + +LookAt_LH :: (eye: Vec3, center: Vec3, up: Vec3) -> Mat4 #foreign hmm_radians_nosimd "HMM_LookAt_LH"; + +InvLookAt :: (matrix: Mat4) -> Mat4 #foreign hmm_radians_nosimd "HMM_InvLookAt"; + +/* +* Quaternion operations +*/ +Q :: (x: float, y: float, z: float, w: float) -> Quat #foreign hmm_radians_nosimd "HMM_Q"; + +QV4 :: (vector: Vec4) -> Quat #foreign hmm_radians_nosimd "HMM_QV4"; + +AddQ :: (left: Quat, right: Quat) -> Quat #foreign hmm_radians_nosimd "HMM_AddQ"; + +SubQ :: (left: Quat, right: Quat) -> Quat #foreign hmm_radians_nosimd "HMM_SubQ"; + +MulQ :: (left: Quat, right: Quat) -> Quat #foreign hmm_radians_nosimd "HMM_MulQ"; + +MulQF :: (left: Quat, multiplicative: float) -> Quat #foreign hmm_radians_nosimd "HMM_MulQF"; + +DivQF :: (left: Quat, divnd: float) -> Quat #foreign hmm_radians_nosimd "HMM_DivQF"; + +DotQ :: (left: Quat, right: Quat) -> float #foreign hmm_radians_nosimd "HMM_DotQ"; + +InvQ :: (left: Quat) -> Quat #foreign hmm_radians_nosimd "HMM_InvQ"; + +NormQ :: (quat: Quat) -> Quat #foreign hmm_radians_nosimd "HMM_NormQ"; + +MixQ :: (left: Quat, mix_left: float, right: Quat, mix_right: float) -> Quat #foreign hmm_radians_nosimd "_HMM_MixQ"; + +NLerp :: (left: Quat, time: float, right: Quat) -> Quat #foreign hmm_radians_nosimd "HMM_NLerp"; + +SLerp :: (left: Quat, time: float, right: Quat) -> Quat #foreign hmm_radians_nosimd "HMM_SLerp"; + +QToM4 :: (left: Quat) -> Mat4 #foreign hmm_radians_nosimd "HMM_QToM4"; + +// This method taken from Mike Day at Insomniac Games. +// https://d3cw3dd2w32x2b.cloudfront.net/wp-content/uploads/2015/01/matrix-to-quat.pdf +// +// Note that as mentioned at the top of the paper, the paper assumes the matrix +// would be *post*-multiplied to a vector to rotate it, meaning the matrix is +// the transpose of what we're dealing with. But, because our matrices are +// stored in column-major order, the indices *appear* to match the paper. +// +// For example, m12 in the paper is row 1, column 2. We need to transpose it to +// row 2, column 1. But, because the column comes first when referencing +// elements, it looks like M.Elements[1][2]. +// +// Don't be confused! Or if you must be confused, at least trust this +// comment. :) +M4ToQ_RH :: (m: Mat4) -> Quat #foreign hmm_radians_nosimd "HMM_M4ToQ_RH"; + +M4ToQ_LH :: (m: Mat4) -> Quat #foreign hmm_radians_nosimd "HMM_M4ToQ_LH"; + +QFromAxisAngle_RH :: (axis: Vec3, angle: float) -> Quat #foreign hmm_radians_nosimd "HMM_QFromAxisAngle_RH"; + +QFromAxisAngle_LH :: (axis: Vec3, angle: float) -> Quat #foreign hmm_radians_nosimd "HMM_QFromAxisAngle_LH"; + +QFromNormPair :: (left: Vec3, right: Vec3) -> Quat #foreign hmm_radians_nosimd "HMM_QFromNormPair"; + +QFromVecPair :: (left: Vec3, right: Vec3) -> Quat #foreign hmm_radians_nosimd "HMM_QFromVecPair"; + +RotateV2 :: (v: Vec2, angle: float) -> Vec2 #foreign hmm_radians_nosimd "HMM_RotateV2"; + +// implementation from +// https://blog.molecular-matters.com/2013/05/24/a-faster-quaternion-vector-multiplication/ +RotateV3Q :: (v: Vec3, q: Quat) -> Vec3 #foreign hmm_radians_nosimd "HMM_RotateV3Q"; + +RotateV3AxisAngle_LH :: (v: Vec3, axis: Vec3, angle: float) -> Vec3 #foreign hmm_radians_nosimd "HMM_RotateV3AxisAngle_LH"; + +RotateV3AxisAngle_RH :: (v: Vec3, axis: Vec3, angle: float) -> Vec3 #foreign hmm_radians_nosimd "HMM_RotateV3AxisAngle_RH"; + +#scope_file + +#import "Basic"; // For assert + + +#run { + { + info := type_info(Vec2); + for info.members { + if it.name == { + case "elements"; + assert(it.offset_in_bytes == 0, "Vec2.elements has unexpected offset % instead of 0", it.offset_in_bytes); + assert(it.type.runtime_size == 8, "Vec2.elements has unexpected size % instead of 8", it.type.runtime_size); + } + } + assert(size_of(Vec2) == 8, "Vec2 has size % instead of 8", size_of(Vec2)); + } + + { + info := type_info(Vec3); + for info.members { + if it.name == { + case "elements"; + assert(it.offset_in_bytes == 0, "Vec3.elements has unexpected offset % instead of 0", it.offset_in_bytes); + assert(it.type.runtime_size == 12, "Vec3.elements has unexpected size % instead of 12", it.type.runtime_size); + } + } + assert(size_of(Vec3) == 12, "Vec3 has size % instead of 12", size_of(Vec3)); + } + + { + info := type_info(Vec4); + for info.members { + if it.name == { + case "elements"; + assert(it.offset_in_bytes == 0, "Vec4.elements has unexpected offset % instead of 0", it.offset_in_bytes); + assert(it.type.runtime_size == 16, "Vec4.elements has unexpected size % instead of 16", it.type.runtime_size); + } + } + assert(size_of(Vec4) == 16, "Vec4 has size % instead of 16", size_of(Vec4)); + } + + { + info := type_info(Mat2); + for info.members { + if it.name == { + case "elements"; + assert(it.offset_in_bytes == 0, "Mat2.elements has unexpected offset % instead of 0", it.offset_in_bytes); + assert(it.type.runtime_size == 16, "Mat2.elements has unexpected size % instead of 16", it.type.runtime_size); + case "columns"; + assert(it.offset_in_bytes == 0, "Mat2.columns has unexpected offset % instead of 0", it.offset_in_bytes); + assert(it.type.runtime_size == 16, "Mat2.columns has unexpected size % instead of 16", it.type.runtime_size); + } + } + assert(size_of(Mat2) == 16, "Mat2 has size % instead of 16", size_of(Mat2)); + } + + { + info := type_info(Mat3); + for info.members { + if it.name == { + case "elements"; + assert(it.offset_in_bytes == 0, "Mat3.elements has unexpected offset % instead of 0", it.offset_in_bytes); + assert(it.type.runtime_size == 36, "Mat3.elements has unexpected size % instead of 36", it.type.runtime_size); + case "columns"; + assert(it.offset_in_bytes == 0, "Mat3.columns has unexpected offset % instead of 0", it.offset_in_bytes); + assert(it.type.runtime_size == 36, "Mat3.columns has unexpected size % instead of 36", it.type.runtime_size); + } + } + assert(size_of(Mat3) == 36, "Mat3 has size % instead of 36", size_of(Mat3)); + } + + { + info := type_info(Mat4); + for info.members { + if it.name == { + case "elements"; + assert(it.offset_in_bytes == 0, "Mat4.elements has unexpected offset % instead of 0", it.offset_in_bytes); + assert(it.type.runtime_size == 64, "Mat4.elements has unexpected size % instead of 64", it.type.runtime_size); + case "columns"; + assert(it.offset_in_bytes == 0, "Mat4.columns has unexpected offset % instead of 0", it.offset_in_bytes); + assert(it.type.runtime_size == 64, "Mat4.columns has unexpected size % instead of 64", it.type.runtime_size); + } + } + assert(size_of(Mat4) == 64, "Mat4 has size % instead of 64", size_of(Mat4)); + } + + { + info := type_info(Quat); + for info.members { + if it.name == { + case "elements"; + assert(it.offset_in_bytes == 0, "Quat.elements has unexpected offset % instead of 0", it.offset_in_bytes); + assert(it.type.runtime_size == 16, "Quat.elements has unexpected size % instead of 16", it.type.runtime_size); + } + } + assert(size_of(Quat) == 16, "Quat has size % instead of 16", size_of(Quat)); + } +} + diff --git a/hmm/hmm_simd.jai b/hmm/hmm_simd.jai new file mode 100644 index 0000000..4e92405 --- /dev/null +++ b/hmm/hmm_simd.jai @@ -0,0 +1,508 @@ +// +// This file was autogenerated. +// + + + +HANDMADE_MATH__USE_NEON :: 1; + +HANDMADE_MATH__USE_C11_GENERICS :: 1; + +HMM_PI :: 3.14159265358979323846; +HMM_PI32 :: 3.14159265359; +HMM_DEG180 :: 180.0; +HMM_DEG18032 :: 180.0; +HMM_TURNHALF :: 0.5; +HMM_TURNHALF32 :: 0.5; + +Vec2 :: union { + struct { + x: float; + y: float; + } + + struct { + u: float; + v: float; + } + + struct { + left: float; + right: float; + } + + struct { + width: float; + height: float; + } + + elements: [2] float; +} + +Vec3 :: union { + struct { + x: float; + y: float; + z: float; + } + + struct { + u: float; + v: float; + w: float; + } + + struct { + r: float; + g: float; + b: float; + } + + struct { + xy: Vec2; + _ignored0: float; + } + + struct { + _ignored1: float; + yz: Vec2; + } + + struct { + uv: Vec2; + _ignored2: float; + } + + struct { + _ignored3: float; + vw: Vec2; + } + + elements: [3] float; +} + +Mat2 :: union { + elements: [2] [2] float; + columns: [2] Vec2; +} + +Mat3 :: union { + elements: [3] [3] float; + columns: [3] Vec3; +} + +Mat4 :: union { + elements: [4] [4] float; + columns: [4] Vec4; +} + +Bool :: s32; + +/* +* Angle unit conversion functions +*/ +ToRad :: (angle: float) -> float #foreign hmm_radians_simd "HMM_ToRad"; + +ToDeg :: (angle: float) -> float #foreign hmm_radians_simd "HMM_ToDeg"; + +ToTurn :: (angle: float) -> float #foreign hmm_radians_simd "HMM_ToTurn"; + +/* +* Floating-point math functions +*/ +SinF :: (angle: float) -> float #foreign hmm_radians_simd "HMM_SinF"; + +CosF :: (angle: float) -> float #foreign hmm_radians_simd "HMM_CosF"; + +TanF :: (angle: float) -> float #foreign hmm_radians_simd "HMM_TanF"; + +ACosF :: (arg: float) -> float #foreign hmm_radians_simd "HMM_ACosF"; + +SqrtF :: (_float: float) -> float #foreign hmm_radians_simd "HMM_SqrtF"; + +InvSqrtF :: (_float: float) -> float #foreign hmm_radians_simd "HMM_InvSqrtF"; + +/* +* Utility functions +*/ +Lerp :: (a: float, time: float, b: float) -> float #foreign hmm_radians_simd "HMM_Lerp"; + +Clamp :: (min: float, value: float, max: float) -> float #foreign hmm_radians_simd "HMM_Clamp"; + +/* +* Vector initialization +*/ +V2 :: (x: float, y: float) -> Vec2 #foreign hmm_radians_simd "HMM_V2"; + +V3 :: (x: float, y: float, z: float) -> Vec3 #foreign hmm_radians_simd "HMM_V3"; + +V4 :: (x: float, y: float, z: float, w: float) -> Vec4 #foreign hmm_radians_simd "HMM_V4"; + +V4V :: (vector: Vec3, w: float) -> Vec4 #foreign hmm_radians_simd "HMM_V4V"; + +/* +* Binary vector operations +*/ +AddV2 :: (left: Vec2, right: Vec2) -> Vec2 #foreign hmm_radians_simd "HMM_AddV2"; + +AddV3 :: (left: Vec3, right: Vec3) -> Vec3 #foreign hmm_radians_simd "HMM_AddV3"; + +AddV4 :: (left: Vec4, right: Vec4) -> Vec4 #foreign hmm_radians_simd "HMM_AddV4"; + +SubV2 :: (left: Vec2, right: Vec2) -> Vec2 #foreign hmm_radians_simd "HMM_SubV2"; + +SubV3 :: (left: Vec3, right: Vec3) -> Vec3 #foreign hmm_radians_simd "HMM_SubV3"; + +SubV4 :: (left: Vec4, right: Vec4) -> Vec4 #foreign hmm_radians_simd "HMM_SubV4"; + +MulV2 :: (left: Vec2, right: Vec2) -> Vec2 #foreign hmm_radians_simd "HMM_MulV2"; + +MulV2F :: (left: Vec2, right: float) -> Vec2 #foreign hmm_radians_simd "HMM_MulV2F"; + +MulV3 :: (left: Vec3, right: Vec3) -> Vec3 #foreign hmm_radians_simd "HMM_MulV3"; + +MulV3F :: (left: Vec3, right: float) -> Vec3 #foreign hmm_radians_simd "HMM_MulV3F"; + +MulV4 :: (left: Vec4, right: Vec4) -> Vec4 #foreign hmm_radians_simd "HMM_MulV4"; + +MulV4F :: (left: Vec4, right: float) -> Vec4 #foreign hmm_radians_simd "HMM_MulV4F"; + +DivV2 :: (left: Vec2, right: Vec2) -> Vec2 #foreign hmm_radians_simd "HMM_DivV2"; + +DivV2F :: (left: Vec2, right: float) -> Vec2 #foreign hmm_radians_simd "HMM_DivV2F"; + +DivV3 :: (left: Vec3, right: Vec3) -> Vec3 #foreign hmm_radians_simd "HMM_DivV3"; + +DivV3F :: (left: Vec3, right: float) -> Vec3 #foreign hmm_radians_simd "HMM_DivV3F"; + +DivV4 :: (left: Vec4, right: Vec4) -> Vec4 #foreign hmm_radians_simd "HMM_DivV4"; + +DivV4F :: (left: Vec4, right: float) -> Vec4 #foreign hmm_radians_simd "HMM_DivV4F"; + +EqV2 :: (left: Vec2, right: Vec2) -> Bool #foreign hmm_radians_simd "HMM_EqV2"; + +EqV3 :: (left: Vec3, right: Vec3) -> Bool #foreign hmm_radians_simd "HMM_EqV3"; + +EqV4 :: (left: Vec4, right: Vec4) -> Bool #foreign hmm_radians_simd "HMM_EqV4"; + +DotV2 :: (left: Vec2, right: Vec2) -> float #foreign hmm_radians_simd "HMM_DotV2"; + +DotV3 :: (left: Vec3, right: Vec3) -> float #foreign hmm_radians_simd "HMM_DotV3"; + +DotV4 :: (left: Vec4, right: Vec4) -> float #foreign hmm_radians_simd "HMM_DotV4"; + +Cross :: (left: Vec3, right: Vec3) -> Vec3 #foreign hmm_radians_simd "HMM_Cross"; + +/* +* Unary vector operations +*/ +LenSqrV2 :: (a: Vec2) -> float #foreign hmm_radians_simd "HMM_LenSqrV2"; + +LenSqrV3 :: (a: Vec3) -> float #foreign hmm_radians_simd "HMM_LenSqrV3"; + +LenSqrV4 :: (a: Vec4) -> float #foreign hmm_radians_simd "HMM_LenSqrV4"; + +LenV2 :: (a: Vec2) -> float #foreign hmm_radians_simd "HMM_LenV2"; + +LenV3 :: (a: Vec3) -> float #foreign hmm_radians_simd "HMM_LenV3"; + +LenV4 :: (a: Vec4) -> float #foreign hmm_radians_simd "HMM_LenV4"; + +NormV2 :: (a: Vec2) -> Vec2 #foreign hmm_radians_simd "HMM_NormV2"; + +NormV3 :: (a: Vec3) -> Vec3 #foreign hmm_radians_simd "HMM_NormV3"; + +NormV4 :: (a: Vec4) -> Vec4 #foreign hmm_radians_simd "HMM_NormV4"; + +/* +* Utility vector functions +*/ +LerpV2 :: (a: Vec2, time: float, b: Vec2) -> Vec2 #foreign hmm_radians_simd "HMM_LerpV2"; + +LerpV3 :: (a: Vec3, time: float, b: Vec3) -> Vec3 #foreign hmm_radians_simd "HMM_LerpV3"; + +LerpV4 :: (a: Vec4, time: float, b: Vec4) -> Vec4 #foreign hmm_radians_simd "HMM_LerpV4"; + +/* +* SSE stuff +*/ +LinearCombineV4M4 :: (left: Vec4, right: Mat4) -> Vec4 #foreign hmm_radians_simd "HMM_LinearCombineV4M4"; + +/* +* 2x2 Matrices +*/ +M2 :: () -> Mat2 #foreign hmm_radians_simd "HMM_M2"; + +M2D :: (diagonal: float) -> Mat2 #foreign hmm_radians_simd "HMM_M2D"; + +TransposeM2 :: (matrix: Mat2) -> Mat2 #foreign hmm_radians_simd "HMM_TransposeM2"; + +AddM2 :: (left: Mat2, right: Mat2) -> Mat2 #foreign hmm_radians_simd "HMM_AddM2"; + +SubM2 :: (left: Mat2, right: Mat2) -> Mat2 #foreign hmm_radians_simd "HMM_SubM2"; + +MulM2V2 :: (matrix: Mat2, vector: Vec2) -> Vec2 #foreign hmm_radians_simd "HMM_MulM2V2"; + +MulM2 :: (left: Mat2, right: Mat2) -> Mat2 #foreign hmm_radians_simd "HMM_MulM2"; + +MulM2F :: (matrix: Mat2, scalar: float) -> Mat2 #foreign hmm_radians_simd "HMM_MulM2F"; + +DivM2F :: (matrix: Mat2, scalar: float) -> Mat2 #foreign hmm_radians_simd "HMM_DivM2F"; + +DeterminantM2 :: (matrix: Mat2) -> float #foreign hmm_radians_simd "HMM_DeterminantM2"; + +InvGeneralM2 :: (matrix: Mat2) -> Mat2 #foreign hmm_radians_simd "HMM_InvGeneralM2"; + +/* +* 3x3 Matrices +*/ +M3 :: () -> Mat3 #foreign hmm_radians_simd "HMM_M3"; + +M3D :: (diagonal: float) -> Mat3 #foreign hmm_radians_simd "HMM_M3D"; + +TransposeM3 :: (matrix: Mat3) -> Mat3 #foreign hmm_radians_simd "HMM_TransposeM3"; + +AddM3 :: (left: Mat3, right: Mat3) -> Mat3 #foreign hmm_radians_simd "HMM_AddM3"; + +SubM3 :: (left: Mat3, right: Mat3) -> Mat3 #foreign hmm_radians_simd "HMM_SubM3"; + +MulM3V3 :: (matrix: Mat3, vector: Vec3) -> Vec3 #foreign hmm_radians_simd "HMM_MulM3V3"; + +MulM3 :: (left: Mat3, right: Mat3) -> Mat3 #foreign hmm_radians_simd "HMM_MulM3"; + +MulM3F :: (matrix: Mat3, scalar: float) -> Mat3 #foreign hmm_radians_simd "HMM_MulM3F"; + +DivM3F :: (matrix: Mat3, scalar: float) -> Mat3 #foreign hmm_radians_simd "HMM_DivM3F"; + +DeterminantM3 :: (matrix: Mat3) -> float #foreign hmm_radians_simd "HMM_DeterminantM3"; + +InvGeneralM3 :: (matrix: Mat3) -> Mat3 #foreign hmm_radians_simd "HMM_InvGeneralM3"; + +/* +* 4x4 Matrices +*/ +M4 :: () -> Mat4 #foreign hmm_radians_simd "HMM_M4"; + +M4D :: (diagonal: float) -> Mat4 #foreign hmm_radians_simd "HMM_M4D"; + +TransposeM4 :: (matrix: Mat4) -> Mat4 #foreign hmm_radians_simd "HMM_TransposeM4"; + +AddM4 :: (left: Mat4, right: Mat4) -> Mat4 #foreign hmm_radians_simd "HMM_AddM4"; + +SubM4 :: (left: Mat4, right: Mat4) -> Mat4 #foreign hmm_radians_simd "HMM_SubM4"; + +MulM4 :: (left: Mat4, right: Mat4) -> Mat4 #foreign hmm_radians_simd "HMM_MulM4"; + +MulM4F :: (matrix: Mat4, scalar: float) -> Mat4 #foreign hmm_radians_simd "HMM_MulM4F"; + +MulM4V4 :: (matrix: Mat4, vector: Vec4) -> Vec4 #foreign hmm_radians_simd "HMM_MulM4V4"; + +DivM4F :: (matrix: Mat4, scalar: float) -> Mat4 #foreign hmm_radians_simd "HMM_DivM4F"; + +DeterminantM4 :: (matrix: Mat4) -> float #foreign hmm_radians_simd "HMM_DeterminantM4"; + +// Returns a general-purpose inverse of an HMM_Mat4. Note that special-purpose inverses of many transformations +// are available and will be more efficient. +InvGeneralM4 :: (matrix: Mat4) -> Mat4 #foreign hmm_radians_simd "HMM_InvGeneralM4"; + +// Produces a right-handed orthographic projection matrix with Z ranging from -1 to 1 (the GL convention). +// Left, Right, Bottom, and Top specify the coordinates of their respective clipping planes. +// Near and Far specify the distances to the near and far clipping planes. +Orthographic_RH_NO :: (left: float, right: float, bottom: float, top: float, near: float, far: float) -> Mat4 #foreign hmm_radians_simd "HMM_Orthographic_RH_NO"; + +// Produces a right-handed orthographic projection matrix with Z ranging from 0 to 1 (the DirectX convention). +// Left, Right, Bottom, and Top specify the coordinates of their respective clipping planes. +// Near and Far specify the distances to the near and far clipping planes. +Orthographic_RH_ZO :: (left: float, right: float, bottom: float, top: float, near: float, far: float) -> Mat4 #foreign hmm_radians_simd "HMM_Orthographic_RH_ZO"; + +// Produces a left-handed orthographic projection matrix with Z ranging from -1 to 1 (the GL convention). +// Left, Right, Bottom, and Top specify the coordinates of their respective clipping planes. +// Near and Far specify the distances to the near and far clipping planes. +Orthographic_LH_NO :: (left: float, right: float, bottom: float, top: float, near: float, far: float) -> Mat4 #foreign hmm_radians_simd "HMM_Orthographic_LH_NO"; + +// Produces a left-handed orthographic projection matrix with Z ranging from 0 to 1 (the DirectX convention). +// Left, Right, Bottom, and Top specify the coordinates of their respective clipping planes. +// Near and Far specify the distances to the near and far clipping planes. +Orthographic_LH_ZO :: (left: float, right: float, bottom: float, top: float, near: float, far: float) -> Mat4 #foreign hmm_radians_simd "HMM_Orthographic_LH_ZO"; + +// Returns an inverse for the given orthographic projection matrix. Works for all orthographic +// projection matrices, regardless of handedness or NDC convention. +InvOrthographic :: (ortho_matrix: Mat4) -> Mat4 #foreign hmm_radians_simd "HMM_InvOrthographic"; + +Perspective_RH_NO :: (fov: float, aspect_ratio: float, near: float, far: float) -> Mat4 #foreign hmm_radians_simd "HMM_Perspective_RH_NO"; + +Perspective_RH_ZO :: (fov: float, aspect_ratio: float, near: float, far: float) -> Mat4 #foreign hmm_radians_simd "HMM_Perspective_RH_ZO"; + +Perspective_LH_NO :: (fov: float, aspect_ratio: float, near: float, far: float) -> Mat4 #foreign hmm_radians_simd "HMM_Perspective_LH_NO"; + +Perspective_LH_ZO :: (fov: float, aspect_ratio: float, near: float, far: float) -> Mat4 #foreign hmm_radians_simd "HMM_Perspective_LH_ZO"; + +InvPerspective_RH :: (perspective_matrix: Mat4) -> Mat4 #foreign hmm_radians_simd "HMM_InvPerspective_RH"; + +InvPerspective_LH :: (perspective_matrix: Mat4) -> Mat4 #foreign hmm_radians_simd "HMM_InvPerspective_LH"; + +Translate :: (translation: Vec3) -> Mat4 #foreign hmm_radians_simd "HMM_Translate"; + +InvTranslate :: (translation_matrix: Mat4) -> Mat4 #foreign hmm_radians_simd "HMM_InvTranslate"; + +Rotate_RH :: (angle: float, axis: Vec3) -> Mat4 #foreign hmm_radians_simd "HMM_Rotate_RH"; + +Rotate_LH :: (angle: float, axis: Vec3) -> Mat4 #foreign hmm_radians_simd "HMM_Rotate_LH"; + +InvRotate :: (rotation_matrix: Mat4) -> Mat4 #foreign hmm_radians_simd "HMM_InvRotate"; + +Scale :: (scale: Vec3) -> Mat4 #foreign hmm_radians_simd "HMM_Scale"; + +InvScale :: (scale_matrix: Mat4) -> Mat4 #foreign hmm_radians_simd "HMM_InvScale"; + +LookAt :: (f: Vec3, s: Vec3, u: Vec3, eye: Vec3) -> Mat4 #foreign hmm_radians_simd "_HMM_LookAt"; + +LookAt_RH :: (eye: Vec3, center: Vec3, up: Vec3) -> Mat4 #foreign hmm_radians_simd "HMM_LookAt_RH"; + +LookAt_LH :: (eye: Vec3, center: Vec3, up: Vec3) -> Mat4 #foreign hmm_radians_simd "HMM_LookAt_LH"; + +InvLookAt :: (matrix: Mat4) -> Mat4 #foreign hmm_radians_simd "HMM_InvLookAt"; + +/* +* Quaternion operations +*/ +Q :: (x: float, y: float, z: float, w: float) -> Quat #foreign hmm_radians_simd "HMM_Q"; + +QV4 :: (vector: Vec4) -> Quat #foreign hmm_radians_simd "HMM_QV4"; + +AddQ :: (left: Quat, right: Quat) -> Quat #foreign hmm_radians_simd "HMM_AddQ"; + +SubQ :: (left: Quat, right: Quat) -> Quat #foreign hmm_radians_simd "HMM_SubQ"; + +MulQ :: (left: Quat, right: Quat) -> Quat #foreign hmm_radians_simd "HMM_MulQ"; + +MulQF :: (left: Quat, multiplicative: float) -> Quat #foreign hmm_radians_simd "HMM_MulQF"; + +DivQF :: (left: Quat, divnd: float) -> Quat #foreign hmm_radians_simd "HMM_DivQF"; + +DotQ :: (left: Quat, right: Quat) -> float #foreign hmm_radians_simd "HMM_DotQ"; + +InvQ :: (left: Quat) -> Quat #foreign hmm_radians_simd "HMM_InvQ"; + +NormQ :: (quat: Quat) -> Quat #foreign hmm_radians_simd "HMM_NormQ"; + +MixQ :: (left: Quat, mix_left: float, right: Quat, mix_right: float) -> Quat #foreign hmm_radians_simd "_HMM_MixQ"; + +NLerp :: (left: Quat, time: float, right: Quat) -> Quat #foreign hmm_radians_simd "HMM_NLerp"; + +SLerp :: (left: Quat, time: float, right: Quat) -> Quat #foreign hmm_radians_simd "HMM_SLerp"; + +QToM4 :: (left: Quat) -> Mat4 #foreign hmm_radians_simd "HMM_QToM4"; + +// This method taken from Mike Day at Insomniac Games. +// https://d3cw3dd2w32x2b.cloudfront.net/wp-content/uploads/2015/01/matrix-to-quat.pdf +// +// Note that as mentioned at the top of the paper, the paper assumes the matrix +// would be *post*-multiplied to a vector to rotate it, meaning the matrix is +// the transpose of what we're dealing with. But, because our matrices are +// stored in column-major order, the indices *appear* to match the paper. +// +// For example, m12 in the paper is row 1, column 2. We need to transpose it to +// row 2, column 1. But, because the column comes first when referencing +// elements, it looks like M.Elements[1][2]. +// +// Don't be confused! Or if you must be confused, at least trust this +// comment. :) +M4ToQ_RH :: (m: Mat4) -> Quat #foreign hmm_radians_simd "HMM_M4ToQ_RH"; + +M4ToQ_LH :: (m: Mat4) -> Quat #foreign hmm_radians_simd "HMM_M4ToQ_LH"; + +QFromAxisAngle_RH :: (axis: Vec3, angle: float) -> Quat #foreign hmm_radians_simd "HMM_QFromAxisAngle_RH"; + +QFromAxisAngle_LH :: (axis: Vec3, angle: float) -> Quat #foreign hmm_radians_simd "HMM_QFromAxisAngle_LH"; + +QFromNormPair :: (left: Vec3, right: Vec3) -> Quat #foreign hmm_radians_simd "HMM_QFromNormPair"; + +QFromVecPair :: (left: Vec3, right: Vec3) -> Quat #foreign hmm_radians_simd "HMM_QFromVecPair"; + +RotateV2 :: (v: Vec2, angle: float) -> Vec2 #foreign hmm_radians_simd "HMM_RotateV2"; + +// implementation from +// https://blog.molecular-matters.com/2013/05/24/a-faster-quaternion-vector-multiplication/ +RotateV3Q :: (v: Vec3, q: Quat) -> Vec3 #foreign hmm_radians_simd "HMM_RotateV3Q"; + +RotateV3AxisAngle_LH :: (v: Vec3, axis: Vec3, angle: float) -> Vec3 #foreign hmm_radians_simd "HMM_RotateV3AxisAngle_LH"; + +RotateV3AxisAngle_RH :: (v: Vec3, axis: Vec3, angle: float) -> Vec3 #foreign hmm_radians_simd "HMM_RotateV3AxisAngle_RH"; + +#scope_file + +#import "Basic"; // For assert + + +#run { + { + info := type_info(Vec2); + for info.members { + if it.name == { + case "elements"; + assert(it.offset_in_bytes == 0, "Vec2.elements has unexpected offset % instead of 0", it.offset_in_bytes); + assert(it.type.runtime_size == 8, "Vec2.elements has unexpected size % instead of 8", it.type.runtime_size); + } + } + assert(size_of(Vec2) == 8, "Vec2 has size % instead of 8", size_of(Vec2)); + } + + { + info := type_info(Vec3); + for info.members { + if it.name == { + case "elements"; + assert(it.offset_in_bytes == 0, "Vec3.elements has unexpected offset % instead of 0", it.offset_in_bytes); + assert(it.type.runtime_size == 12, "Vec3.elements has unexpected size % instead of 12", it.type.runtime_size); + } + } + assert(size_of(Vec3) == 12, "Vec3 has size % instead of 12", size_of(Vec3)); + } + + { + info := type_info(Mat2); + for info.members { + if it.name == { + case "elements"; + assert(it.offset_in_bytes == 0, "Mat2.elements has unexpected offset % instead of 0", it.offset_in_bytes); + assert(it.type.runtime_size == 16, "Mat2.elements has unexpected size % instead of 16", it.type.runtime_size); + case "columns"; + assert(it.offset_in_bytes == 0, "Mat2.columns has unexpected offset % instead of 0", it.offset_in_bytes); + assert(it.type.runtime_size == 16, "Mat2.columns has unexpected size % instead of 16", it.type.runtime_size); + } + } + assert(size_of(Mat2) == 16, "Mat2 has size % instead of 16", size_of(Mat2)); + } + + { + info := type_info(Mat3); + for info.members { + if it.name == { + case "elements"; + assert(it.offset_in_bytes == 0, "Mat3.elements has unexpected offset % instead of 0", it.offset_in_bytes); + assert(it.type.runtime_size == 36, "Mat3.elements has unexpected size % instead of 36", it.type.runtime_size); + case "columns"; + assert(it.offset_in_bytes == 0, "Mat3.columns has unexpected offset % instead of 0", it.offset_in_bytes); + assert(it.type.runtime_size == 36, "Mat3.columns has unexpected size % instead of 36", it.type.runtime_size); + } + } + assert(size_of(Mat3) == 36, "Mat3 has size % instead of 36", size_of(Mat3)); + } + + { + info := type_info(Mat4); + for info.members { + if it.name == { + case "elements"; + assert(it.offset_in_bytes == 0, "Mat4.elements has unexpected offset % instead of 0", it.offset_in_bytes); + assert(it.type.runtime_size == 64, "Mat4.elements has unexpected size % instead of 64", it.type.runtime_size); + case "columns"; + assert(it.offset_in_bytes == 0, "Mat4.columns has unexpected offset % instead of 0", it.offset_in_bytes); + assert(it.type.runtime_size == 64, "Mat4.columns has unexpected size % instead of 64", it.type.runtime_size); + } + } + assert(size_of(Mat4) == 64, "Mat4 has size % instead of 64", size_of(Mat4)); + } +} + diff --git a/hmm/linux/.need-to-run-generate b/hmm/linux/.need-to-run-generate new file mode 100644 index 0000000..e69de29 diff --git a/hmm/mac/hmm_degrees_nosimd.a b/hmm/mac/hmm_degrees_nosimd.a new file mode 100644 index 0000000000000000000000000000000000000000..2b82c279d6fb5f19da18abf9b70ebea216378d17 GIT binary patch literal 24136 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 Q^=cPxDy(z=>TRO>KV(Ving9R* literal 0 HcmV?d00001 diff --git a/hmm/mac/hmm_degrees_nosimd.dylib b/hmm/mac/hmm_degrees_nosimd.dylib new file mode 100755 index 0000000000000000000000000000000000000000..be1d4966df69592ae5e8e88e07f182f3b3de18ca GIT binary patch literal 40456 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 literal 0 HcmV?d00001 diff --git a/hmm/mac/hmm_degrees_simd.a b/hmm/mac/hmm_degrees_simd.a new file mode 100644 index 0000000000000000000000000000000000000000..69eabb38c551285075f6b2f760fbd7bd8a654f2b GIT binary patch literal 24648 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 literal 0 HcmV?d00001 diff --git a/hmm/mac/hmm_degrees_simd.dylib b/hmm/mac/hmm_degrees_simd.dylib new file mode 100755 index 0000000000000000000000000000000000000000..8a01805dbc88d521a3706f545f02e08e65ec11cb GIT binary patch literal 40456 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

gAHAGUL`6tL3eWm&h$;SIfp5XBmxaR~t?D*2uvd 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 literal 0 HcmV?d00001 diff --git a/hmm/mac/hmm_radians_simd.a b/hmm/mac/hmm_radians_simd.a new file mode 100644 index 0000000000000000000000000000000000000000..fd0a59e55f99ffec00085e590485092dd2592bc3 GIT binary patch literal 24360 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¥AUl*sKSI9p!;Q4bGa`c-d-Ag~mNcXD$Inuqx_af;}der|I z4SSum?5}g}Pr3iTp}IZFcTYiomh|l&QdB*oQdfi7BG&e^4^#3V6Jw22l`5l69)bjlM`#3V6J zw22l`5l2HTPfQV$#5mC=T0})0#XjDtpO_*hiE*M$w1|p0igS`vo|qygiE*M$w1|p0 zigTt@o|qygiE*M$w1|p0iWj6)o|qygiE*M$w1|o*eh{ZTF-1%g<3yWi5fyP1>w{CC zm?9>LaiUGMh>AFh!cKW&ikKwEi8j$9D&nYOd18u~B*uw0(INswM84nuc>R<0KL4@$ z$Lsz0`*6Kbl=?f^#|$v&=PQy%n^lFI^Nk7VqfCcMKMMQ64=|MeKkfexeb0nE?YI3L z_$95+;rqcaX|YRr?t*_~bcF`P%RH1m$Tz+mFew z{er(Fe=*Ddg#6m?cMJ8W{SdN_h<^0Bxr^oXIqycaLFjtlEO(emTA#C;*&lsw`3d?Z zbbZcwjQo0E`2uJ{*XM|Q()GFEA)QyY$X30y!Yg}hIH*Gx|!|MevNYSC-I(^Cq}yVTl|#vpwB@U z*j|0^xk&l?JH)M&m;UZRdM)SIGTk4h|HSkF5uu|s(tf?ScO{Teqht^F8#nU;NF0g(2T=PlNwIAX&wom&l9%6g+IZ@sdj(%~jkmnaHFXsb!o?u$u@A8P< z3cvOv{}sni`#le_KiW_C3gzo_yWCMH<@I;AGM3lh)n@7PtnZhsUw=1UfwEFwf5-hS zGqH=vZ)e#-ge*WW#3)QK94B3$^F^-IM}LHRhV2zy%X2l;wQf>O08vd7z{pX1ay^7nnXse)zmV|j-vOB9A0fY-8-@Nj({hfM^ckiTOvjNY z&wnJp{@p+w)A~FAF{btJ0;HG({gfk;oyPHTT)JUGdMlW-`Q?t&?DU=q>A#$i&Rmq# zv1ed+&p;;K-#am9>%`&!2EY%vrBI8R zaAzdcIKDntkrQs*^SCfjt0i;OpT44R{7sUq)8o!goy|IRazdRtJGV|vaqHA6G0?1g z05j2+%yqDt=*INrftg$Wa6xL;O_VyCeQm09<>V??W5^$WwPhxM+!dAjicI0xamY!> zMSj**o9VdlXXR(&>8m``mwf8$KSdO86O?OR86AIbY3AxySueAAbTs=$%&aRmFk3C5)wpKz>r+J`yl+ty z!YCEOROVJ&M(ARW+_MNopt8AUVl12K;DCZzk(sM?o=ZtEUo-fgf u?4Fa)q2~-ciJ6`=Lx!DE>A9A%#OZmR+R?J^rn>sN4#c2|+H{&5$o~V)QkrxC literal 0 HcmV?d00001 diff --git a/hmm/mac/hmm_radians_simd.dylib b/hmm/mac/hmm_radians_simd.dylib new file mode 100755 index 0000000000000000000000000000000000000000..418223e8e661feb4dd8a842e99b9540fef1b54f1 GIT binary patch literal 40456 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 literal 0 HcmV?d00001 diff --git a/hmm/mac/hmm_turns_nosimd.a b/hmm/mac/hmm_turns_nosimd.a new file mode 100644 index 0000000000000000000000000000000000000000..fd90426da333d026629ed5778864008d58bc9a1c GIT binary patch literal 24136 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 z z6?nD4odUNB{D8p!OJFQa`gOO!J4OBX3A`LD5?>Dse7nHk6_{5nzJ4U|34woMmggVG z!1X(UYcDnM8G)T*JkAN6F7S}R_(2T)!ih?^pI2b4dOAKU#&5E~B<80k@bXNfd`RGV z83vv!@GYXga)Gff>emv18w8FDd`8s&6@mAO`E--O`$hf+fpH|$uX_YODe$)ic8dA; zn85fYZ2kJaz}p3WTHxEo{QH@}odV-Wb#!}d6ZmC;alF&7(*o}o_&tI91(p<}{BF&V z_XxaD;7bJV7ygnd@Swo61kMuUKUd&9F<+JnyiMR61a81tpRWx9KQHk80&l@@oAC97 zzeY11*U8xf3d&~ z0Rvwz@L8e1QQ+M|zD?jY(+&Br3A`w1;K$7JLjH#WcT6$zpA&eenE(9(pA+Np9|CU? z>&IzAyE3ufXIAp!)ZY$6-a<0e~l2<60MqnWW(e|8>5`opt{MuJ~D zXJKQE!&ss#0*P)2B)R~UAZ8kdxx)f@ZWl^h*N(^{J**IEQ~H7cmr>LRa+#esj21OJ zZTfa)LBy!Z^zh0;lOfbs77a5v+GXmiEMhdQZrz$;jhqw?SIKF!{R~9&^<;ufq1i9a zFze-Hf=rRwU&x5@;?#&lLOsK-4T%KR!>-Njk!otpYO3^riEcfP7>QaqY3kO~q7!;r zXi4-zm?(;fm#$_kAEB%Z`3QzxR33a&KGAOz)Q@3bOgce%qI808(apsi02_&Cnd3Z$ zZ4S83W((-SG`&wc%M7m>96gw3Bu#r86U?-yK!q39dtr`QwV5N@ZNOo_6wnl2Ix?gQ zb9pJp!t{1w%C?)ybt_CCUW%j`WloAgMpD#?l-UDLj)cyq-!Gd-8@{{BB-fB)=vuA4bm{aU0p z`_=bWEo%vsk3Vzm_Qmf%a^kt=wYR*sX~X;OZ}ngI)!N?V_UB`^q=N7K>8tB(@BQNY zZ@qc@cRrf6<5?0sT+%b_<{_9?$Udh`aldb$1M+xOr8KR5oicXP$vd5_tu^Vk0C zgJ0Qj$+_=MKQMUx$ER=ndeYaPd}GcNSFBBH_|_FKe_HwfYW|Vu>I1K>cPx54Jbs@t zv*^gRk-lqxeQuolKmO@Afw%wVx-ECEEnfKEb;`Z|C$4Vqzx4Or51)Hz!d string { + b: String_Builder; + append(*b, "hmm_"); + #if UNITS == { + case .radians; append(*b, "radians_"); + case .degrees; append(*b, "degrees_"); + case .turns; append(*b, "turns_"); + } + + #if SIMD { + append(*b, "simd"); + } + else { + append(*b, "nosimd"); + } + + return builder_to_string(*b); +}; + +build_specific_library_directive :: () -> string #compile_time { + b: String_Builder; + + // @todo(judah): I'm not sure how to override the internal libname used by + // the bindings generator for #library directives. Because of this, + // we do a dumb thing and always use hmm_radians as the libname, even though + // we redirect it to the proper radians/degrees/turns library (based on the module params). + append(*b, "hmm_radians_"); + #if SIMD { + append(*b, "simd"); + } + else { + append(*b, "nosimd"); + } + append(*b, " :: "); + + append(*b, "#library"); + #if STATIC append(*b, ",no_dll,link_always"); + + append(*b, " \""); + + #if OS == { + case .WINDOWS; append(*b, "win/"); + case .MACOS; append(*b, "mac/"); + case .LINUX; append(*b, "linux/"); + } + + append(*b, name); + append(*b, "\"; "); + + return builder_to_string(*b); +} + +#import "Basic"; diff --git a/hmm/win/.need-to-run-generate b/hmm/win/.need-to-run-generate new file mode 100644 index 0000000..e69de29