Add new mat4 functions and some external testing

This commit is contained in:
Doyle Thai 2017-06-08 16:41:24 +10:00
parent fceac54ecb
commit 65ee7bab4b
3 changed files with 2944 additions and 81 deletions

211
dqn.h
View File

@ -96,24 +96,18 @@ DQN_FILE_SCOPE void DqnMem_Free (void *memory);
// BeginTempRegion and EndTempRegion functions. Specifically freeing // BeginTempRegion and EndTempRegion functions. Specifically freeing
// individual items is typically not generalisable in this scheme. // individual items is typically not generalisable in this scheme.
typedef struct DqnMemStackBlock
{
u8 *memory;
size_t used;
size_t size;
DqnMemStackBlock *prevBlock;
} DqnMemStackBlock;
enum DqnMemStackFlag enum DqnMemStackFlag
{ {
DqnMemStackFlag_IsNotExpandable = (1 << 0), DqnMemStackFlag_IsNotExpandable = (1 << 0),
DqnMemStackFlag_IsFixedMemoryFromUser = (1 << 1), // NOTE(doyle): Required to indicate we CAN'T free this memory when free is called. DqnMemStackFlag_IsFixedMemoryFromUser = (1 << 1), // NOTE(doyle): Required to indicate we CAN'T free this memory when free is called.
}; };
////////////////////////////////////////////////////////////////////////////////
// Advanced API Structs
////////////////////////////////////////////////////////////////////////////////
typedef struct DqnMemStack typedef struct DqnMemStack
{ {
DqnMemStackBlock *block; struct DqnMemStackBlock *block;
u32 flags; u32 flags;
i32 tempStackCount; i32 tempStackCount;
@ -123,7 +117,7 @@ typedef struct DqnMemStack
typedef struct DqnTempMemStack typedef struct DqnTempMemStack
{ {
DqnMemStack *stack; DqnMemStack *stack;
DqnMemStackBlock *startingBlock; struct DqnMemStackBlock *startingBlock;
size_t used; size_t used;
} DqnTempMemStack; } DqnTempMemStack;
@ -132,12 +126,12 @@ DQN_FILE_SCOPE bool DqnMemStack_InitWithFixedMem (DqnMemStack *const stack, u8 *
DQN_FILE_SCOPE bool DqnMemStack_InitWithFixedSize(DqnMemStack *const stack, size_t size, const bool zeroClear, const u32 byteAlign = 4); // Single allocation from platform, no further allocations, returns NULL of allocate if out of space DQN_FILE_SCOPE bool DqnMemStack_InitWithFixedSize(DqnMemStack *const stack, size_t size, const bool zeroClear, const u32 byteAlign = 4); // Single allocation from platform, no further allocations, returns NULL of allocate if out of space
DQN_FILE_SCOPE bool DqnMemStack_Init (DqnMemStack *const stack, size_t size, const bool zeroClear, const u32 byteAlign = 4); // Allocates from platform dynamically as space runs out DQN_FILE_SCOPE bool DqnMemStack_Init (DqnMemStack *const stack, size_t size, const bool zeroClear, const u32 byteAlign = 4); // Allocates from platform dynamically as space runs out
DQN_FILE_SCOPE void *DqnMemStack_Push (DqnMemStack *const stack, size_t size); // Returns NULL if out of space and stack is using fixed memory/size, or platform allocation fails DQN_FILE_SCOPE void *DqnMemStack_Push (DqnMemStack *const stack, size_t size); // Returns NULL if out of space and stack is using fixed memory/size, or platform allocation fails.
DQN_FILE_SCOPE bool DqnMemStack_Pop (DqnMemStack *const stack, void *ptr, size_t size); // Frees the given ptr. It MUST be the last allocated item in the stack DQN_FILE_SCOPE bool DqnMemStack_Pop (DqnMemStack *const stack, void *ptr, size_t size); // Frees the given ptr. It MUST be the last allocated item in the stack.
DQN_FILE_SCOPE void DqnMemStack_Free (DqnMemStack *const stack); // Frees all blocks belonging to this stack DQN_FILE_SCOPE void DqnMemStack_Free (DqnMemStack *const stack); // Frees all blocks belonging to this stack.
DQN_FILE_SCOPE bool DqnMemStack_FreeStackBlock(DqnMemStack *const stack, DqnMemStackBlock *block); // Frees the specified block, returns false if block doesn't belong DQN_FILE_SCOPE bool DqnMemStack_FreeStackBlock(DqnMemStack *const stack, DqnMemStackBlock *block); // Frees the specified block, returns false if block doesn't belong, calls DqnMem_Free().
DQN_FILE_SCOPE bool DqnMemStack_FreeLastBlock (DqnMemStack *const stack); // Frees the last-most memory block. If last block, free that block, next allocate will attach a block. DQN_FILE_SCOPE bool DqnMemStack_FreeLastBlock (DqnMemStack *const stack); // Frees the last-most memory block. If last block, free that block, next allocate will attach a block.
DQN_FILE_SCOPE void DqnMemStack_ClearCurrBlock(DqnMemStack *const stack, const bool zeroClear); // Reset the current memory block usage to 0 DQN_FILE_SCOPE void DqnMemStack_ClearCurrBlock(DqnMemStack *const stack, const bool zeroClear); // Reset the current memory block usage to 0.
// TempMemStack is only required for the function. Once BeginTempRegion() is called, subsequent allocation calls can be made using the original stack. // TempMemStack is only required for the function. Once BeginTempRegion() is called, subsequent allocation calls can be made using the original stack.
// Upon EndTempRegion() the original stack will free any additional blocks it allocated during the temp region and revert to the original // Upon EndTempRegion() the original stack will free any additional blocks it allocated during the temp region and revert to the original
@ -147,12 +141,26 @@ DQN_FILE_SCOPE void DqnMemStack_ClearCurrBlock(DqnMemStack *const stack, const
DQN_FILE_SCOPE DqnTempMemStack DqnMemStack_BeginTempRegion(DqnMemStack *const stack); DQN_FILE_SCOPE DqnTempMemStack DqnMemStack_BeginTempRegion(DqnMemStack *const stack);
DQN_FILE_SCOPE void DqnMemStack_EndTempRegion (DqnTempMemStack tempstack); DQN_FILE_SCOPE void DqnMemStack_EndTempRegion (DqnTempMemStack tempstack);
////////////////////////////////////////////////////////////////////////////////
// (OPTIONAL) DqnMemStack Advanced API // (OPTIONAL) DqnMemStack Advanced API
// Blocks are freely modifiable if you want fine grained control. Size value and
// memory ptr should _NOT_ be modified directly, only indirectly through the
// regular API.
typedef struct DqnMemStackBlock
{
u8 *memory;
size_t size;
size_t used;
DqnMemStackBlock *prevBlock;
} DqnMemStackBlock;
// This is useful for forcing a new block to be used. AllocateCompatibleBlock // This is useful for forcing a new block to be used. AllocateCompatibleBlock
// will fail if the supplied stack has flags set such that the stack is not // will fail if the supplied stack has flags set such that the stack is not
// allowed to have new blocks. // allowed to have new blocks.
DQN_FILE_SCOPE DqnMemStackBlock *DqnMemStack_AllocateCompatibleBlock(const DqnMemStack *const stack, size_t size); DQN_FILE_SCOPE DqnMemStackBlock *DqnMemStack_AllocateCompatibleBlock(const DqnMemStack *const stack, size_t size);
DQN_FILE_SCOPE bool DqnMemStack_AttachBlock (DqnMemStack *const stack, DqnMemStackBlock *const newBlock); DQN_FILE_SCOPE bool DqnMemStack_AttachBlock (DqnMemStack *const stack, DqnMemStackBlock *const newBlock);
DQN_FILE_SCOPE bool DqnMemStack_DetachBlock (DqnMemStack *const stack, DqnMemStackBlock *const detachBlock);
// (IMPORTANT) Should only be used to free blocks that haven't been attached! // (IMPORTANT) Should only be used to free blocks that haven't been attached!
// Attached blocks should be freed using FreeStackBlock(). // Attached blocks should be freed using FreeStackBlock().
@ -521,8 +529,9 @@ typedef union DqnV3i
} DqnV3i; } DqnV3i;
// DqnV3 // DqnV3
DQN_FILE_SCOPE DqnV3 DqnV3_3i(i32 x, i32 y, i32 z); // Create a vector using ints and typecast to floats DQN_FILE_SCOPE DqnV3 DqnV3_1f(f32 xyz);
DQN_FILE_SCOPE DqnV3 DqnV3_3f(f32 x, f32 y, f32 z); DQN_FILE_SCOPE DqnV3 DqnV3_3f(f32 x, f32 y, f32 z);
DQN_FILE_SCOPE DqnV3 DqnV3_3i(i32 x, i32 y, i32 z); // Create a vector using ints and typecast to floats
DQN_FILE_SCOPE DqnV3 DqnV3_Add (DqnV3 a, DqnV3 b); DQN_FILE_SCOPE DqnV3 DqnV3_Add (DqnV3 a, DqnV3 b);
DQN_FILE_SCOPE DqnV3 DqnV3_Sub (DqnV3 a, DqnV3 b); DQN_FILE_SCOPE DqnV3 DqnV3_Sub (DqnV3 a, DqnV3 b);
@ -534,12 +543,16 @@ DQN_FILE_SCOPE bool DqnV3_Equals (DqnV3 a, DqnV3 b);
DQN_FILE_SCOPE DqnV3 DqnV3_Cross (DqnV3 a, DqnV3 b); DQN_FILE_SCOPE DqnV3 DqnV3_Cross (DqnV3 a, DqnV3 b);
DQN_FILE_SCOPE DqnV3 DqnV3_Normalise (DqnV3 a); DQN_FILE_SCOPE DqnV3 DqnV3_Normalise (DqnV3 a);
DQN_FILE_SCOPE f32 DqnV3_Length (DqnV3 a, DqnV3 b);
DQN_FILE_SCOPE f32 DqnV3_LengthSquared(DqnV3 a, DqnV3 b);
DQN_FILE_SCOPE inline DqnV3 operator- (DqnV3 a, DqnV3 b) { return DqnV3_Sub (a, b); } DQN_FILE_SCOPE inline DqnV3 operator- (DqnV3 a, DqnV3 b) { return DqnV3_Sub (a, b); }
DQN_FILE_SCOPE inline DqnV3 operator+ (DqnV3 a, DqnV3 b) { return DqnV3_Add (a, b); } DQN_FILE_SCOPE inline DqnV3 operator+ (DqnV3 a, DqnV3 b) { return DqnV3_Add (a, b); }
DQN_FILE_SCOPE inline DqnV3 operator+ (DqnV3 a, f32 b) { return DqnV3_Add (a, DqnV3_1f(b)); }
DQN_FILE_SCOPE inline DqnV3 operator* (DqnV3 a, DqnV3 b) { return DqnV3_Hadamard(a, b); } DQN_FILE_SCOPE inline DqnV3 operator* (DqnV3 a, DqnV3 b) { return DqnV3_Hadamard(a, b); }
DQN_FILE_SCOPE inline DqnV3 operator* (DqnV3 a, f32 b) { return DqnV3_Scalef (a, b); } DQN_FILE_SCOPE inline DqnV3 operator* (DqnV3 a, f32 b) { return DqnV3_Scalef (a, b); }
DQN_FILE_SCOPE inline DqnV3 operator* (DqnV3 a, i32 b) { return DqnV3_Scalei (a, b); } DQN_FILE_SCOPE inline DqnV3 operator* (DqnV3 a, i32 b) { return DqnV3_Scalei (a, b); }
DQN_FILE_SCOPE inline DqnV3 operator/ (DqnV3 a, f32 b) { return DqnV3_Scalef (a, (1.0f/b)); }
DQN_FILE_SCOPE inline DqnV3 &operator*=(DqnV3 &a, DqnV3 b) { return (a = DqnV3_Hadamard(a, b)); } DQN_FILE_SCOPE inline DqnV3 &operator*=(DqnV3 &a, DqnV3 b) { return (a = DqnV3_Hadamard(a, b)); }
DQN_FILE_SCOPE inline DqnV3 &operator*=(DqnV3 &a, f32 b) { return (a = DqnV3_Scalef (a, b)); } DQN_FILE_SCOPE inline DqnV3 &operator*=(DqnV3 &a, f32 b) { return (a = DqnV3_Scalef (a, b)); }
DQN_FILE_SCOPE inline DqnV3 &operator*=(DqnV3 &a, i32 b) { return (a = DqnV3_Scalei (a, b)); } DQN_FILE_SCOPE inline DqnV3 &operator*=(DqnV3 &a, i32 b) { return (a = DqnV3_Scalei (a, b)); }
@ -572,6 +585,7 @@ typedef union DqnV4 {
// Create a vector using ints and typecast to floats // Create a vector using ints and typecast to floats
DQN_FILE_SCOPE DqnV4 DqnV4_4i(i32 x, i32 y, i32 z, f32 w); DQN_FILE_SCOPE DqnV4 DqnV4_4i(i32 x, i32 y, i32 z, f32 w);
DQN_FILE_SCOPE DqnV4 DqnV4_4f(f32 x, f32 y, f32 z, f32 w); DQN_FILE_SCOPE DqnV4 DqnV4_4f(f32 x, f32 y, f32 z, f32 w);
DQN_FILE_SCOPE DqnV4 DqnV4_V3(DqnV3 a, f32 w);
DQN_FILE_SCOPE DqnV4 DqnV4_1f(f32 xyzw); DQN_FILE_SCOPE DqnV4 DqnV4_1f(f32 xyzw);
DQN_FILE_SCOPE DqnV4 DqnV4_Add (DqnV4 a, DqnV4 b); DQN_FILE_SCOPE DqnV4 DqnV4_Add (DqnV4 a, DqnV4 b);
@ -600,13 +614,18 @@ DQN_FILE_SCOPE inline bool operator==(DqnV4 a, DqnV4 b) { return DqnV4_E
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
typedef union DqnMat4 typedef union DqnMat4
{ {
// TODO(doyle): Row/column instead? More cache friendly since multiplication
// prefers rows.
DqnV4 col[4]; DqnV4 col[4];
// Column/row f32 e[4][4]; // Column/row
f32 e[4][4];
} DqnMat4; } DqnMat4;
DQN_FILE_SCOPE DqnMat4 DqnMat4_Identity (); DQN_FILE_SCOPE DqnMat4 DqnMat4_Identity ();
DQN_FILE_SCOPE DqnMat4 DqnMat4_Ortho (f32 left, f32 right, f32 bottom, f32 top, f32 zNear, f32 zFar);
DQN_FILE_SCOPE DqnMat4 DqnMat4_Orthographic(f32 left, f32 right, f32 bottom, f32 top, f32 zNear, f32 zFar);
DQN_FILE_SCOPE DqnMat4 DqnMat4_Perspective (f32 fovYDegrees, f32 aspectRatio, f32 zNear, f32 zFar);
DQN_FILE_SCOPE DqnMat4 DqnMat4_LookAt (DqnV3 eye, DqnV3 center, DqnV3 up);
DQN_FILE_SCOPE DqnMat4 DqnMat4_Translate (f32 x, f32 y, f32 z); DQN_FILE_SCOPE DqnMat4 DqnMat4_Translate (f32 x, f32 y, f32 z);
DQN_FILE_SCOPE DqnMat4 DqnMat4_Rotate (f32 radians, f32 x, f32 y, f32 z); DQN_FILE_SCOPE DqnMat4 DqnMat4_Rotate (f32 radians, f32 x, f32 y, f32 z);
DQN_FILE_SCOPE DqnMat4 DqnMat4_Scale (f32 x, f32 y, f32 z); DQN_FILE_SCOPE DqnMat4 DqnMat4_Scale (f32 x, f32 y, f32 z);
@ -1394,6 +1413,31 @@ DQN_FILE_SCOPE bool DqnMemStack_AttachBlock(DqnMemStack *const stack,
return true; return true;
} }
DQN_FILE_SCOPE bool DqnMemStack_DetachBlock(DqnMemStack *const stack,
DqnMemStackBlock *const detachBlock)
{
if (!stack || !detachBlock) return false;
if (stack->flags & DqnMemStackFlag_IsFixedMemoryFromUser) return false;
if (stack->flags & DqnMemStackFlag_IsNotExpandable) return false;
DqnMemStackBlock **blockPtr = &stack->block;
while (*blockPtr && *blockPtr != detachBlock)
blockPtr = &((*blockPtr)->prevBlock);
if (*blockPtr)
{
*blockPtr = detachBlock->prevBlock;
detachBlock->prevBlock = NULL;
}
else
{
return false;
}
return true;
}
DQN_FILE_SCOPE void DqnMemStack_FreeBlock(DqnMemStackBlock *block) DQN_FILE_SCOPE void DqnMemStack_FreeBlock(DqnMemStackBlock *block)
{ {
if (!block) return; if (!block) return;
@ -1406,7 +1450,6 @@ DQN_FILE_SCOPE bool DqnMemStack_InitWithFixedMem(DqnMemStack *const stack,
const u32 byteAlign) const u32 byteAlign)
{ {
if (!stack || !mem) return false; if (!stack || !mem) return false;
DQN_ASSERT(!stack->block);
// TODO(doyle): Better logging // TODO(doyle): Better logging
if (memSize < sizeof(DqnMemStackBlock)) if (memSize < sizeof(DqnMemStackBlock))
@ -1416,6 +1459,7 @@ DQN_FILE_SCOPE bool DqnMemStack_InitWithFixedMem(DqnMemStack *const stack,
stack->block->memory = mem + sizeof(DqnMemStackBlock); stack->block->memory = mem + sizeof(DqnMemStackBlock);
stack->block->used = 0; stack->block->used = 0;
stack->block->size = memSize - sizeof(DqnMemStackBlock); stack->block->size = memSize - sizeof(DqnMemStackBlock);
stack->block->prevBlock = NULL;
stack->flags = (DqnMemStackFlag_IsFixedMemoryFromUser | DqnMemStackFlag_IsNotExpandable); stack->flags = (DqnMemStackFlag_IsFixedMemoryFromUser | DqnMemStackFlag_IsNotExpandable);
const u32 DEFAULT_ALIGNMENT = 4; const u32 DEFAULT_ALIGNMENT = 4;
@ -2032,18 +2076,21 @@ DQN_FILE_SCOPE bool DqnV2i_Equals(DqnV2i a, DqnV2i b)
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Vec3 // Vec3
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
DQN_FILE_SCOPE DqnV3 DqnV3_1f(f32 xyz)
{
DqnV3 result = {xyz, xyz, xyz};
return result;
}
DQN_FILE_SCOPE DqnV3 DqnV3_3f(f32 x, f32 y, f32 z) DQN_FILE_SCOPE DqnV3 DqnV3_3f(f32 x, f32 y, f32 z)
{ {
DqnV3 result = {}; DqnV3 result = {x, y, z};
result.x = x;
result.y = y;
result.z = z;
return result; return result;
} }
DQN_FILE_SCOPE DqnV3 DqnV3_3i(i32 x, i32 y, i32 z) DQN_FILE_SCOPE DqnV3 DqnV3_3i(i32 x, i32 y, i32 z)
{ {
DqnV3 result = DqnV3_3f((f32)x, (f32)y, (f32)z); DqnV3 result = {(f32)x, (f32)y, (f32)z};
return result; return result;
} }
@ -2141,6 +2188,24 @@ DQN_FILE_SCOPE DqnV3 DqnV3_Normalise(DqnV3 a)
return result; return result;
} }
DQN_FILE_SCOPE f32 DqnV3_LengthSquared(DqnV3 a, DqnV3 b)
{
f32 x = b.x - a.x;
f32 y = b.y - a.y;
f32 z = b.z - a.z;
f32 result = (DQN_SQUARED(x) + DQN_SQUARED(y) + DQN_SQUARED(z));
return result;
}
DQN_FILE_SCOPE f32 DqnV3_Length(DqnV3 a, DqnV3 b)
{
f32 lengthSq = DqnV3_LengthSquared(a, b);
if (lengthSq == 0) return 0;
f32 result = DqnMath_Sqrtf(lengthSq);
return result;
}
DQN_FILE_SCOPE DqnV3i DqnV3i_3i(i32 x, i32 y, i32 z) DQN_FILE_SCOPE DqnV3i DqnV3i_3i(i32 x, i32 y, i32 z)
{ {
DqnV3i result = {x, y, z}; DqnV3i result = {x, y, z};
@ -2168,6 +2233,14 @@ DQN_FILE_SCOPE DqnV4 DqnV4_4i(i32 x, i32 y, i32 z, i32 w)
return result; return result;
} }
DQN_FILE_SCOPE DqnV4 DqnV4_V3(DqnV3 a, f32 w)
{
DqnV4 result;
result.xyz = a;
result.w = w;
return result;
}
DQN_FILE_SCOPE DqnV4 DqnV4_1f(f32 xyzw) DQN_FILE_SCOPE DqnV4 DqnV4_1f(f32 xyzw)
{ {
DqnV4 result = {xyzw, xyzw, xyzw, xyzw}; DqnV4 result = {xyzw, xyzw, xyzw, xyzw};
@ -2256,8 +2329,8 @@ DQN_FILE_SCOPE DqnMat4 DqnMat4_Identity()
return result; return result;
} }
DQN_FILE_SCOPE DqnMat4 DQN_FILE_SCOPE DqnMat4 DqnMat4_Orthographic(f32 left, f32 right, f32 bottom, f32 top, f32 zNear,
DqnMat4_Ortho(f32 left, f32 right, f32 bottom, f32 top, f32 zNear, f32 zFar) f32 zFar)
{ {
DqnMat4 result = DqnMat4_Identity(); DqnMat4 result = DqnMat4_Identity();
result.e[0][0] = +2.0f / (right - left); result.e[0][0] = +2.0f / (right - left);
@ -2271,6 +2344,51 @@ DqnMat4_Ortho(f32 left, f32 right, f32 bottom, f32 top, f32 zNear, f32 zFar)
return result; return result;
} }
DQN_FILE_SCOPE DqnMat4 DqnMat4_Perspective(f32 fovYDegrees, f32 aspectRatio, f32 zNear, f32 zFar)
{
f32 fovYRadians = DQN_DEGREES_TO_RADIANS(fovYDegrees);
f32 fovYRadiansOver2 = fovYRadians * 0.5f;
f32 tanFovYRadiansOver2 = tanf(fovYRadiansOver2);
f32 zNearSubZFar = zNear - zFar;
DqnMat4 result = DqnMat4_Identity();
result.e[0][0] = 1.0f / (tanFovYRadiansOver2 * aspectRatio);
result.e[1][1] = 1.0f / tanFovYRadiansOver2;
result.e[2][2] = (zNear + zFar) / zNearSubZFar;
result.e[2][3] = -1.0f;
result.e[3][2] = (2.0f * zNear * zFar) / zNearSubZFar;
result.e[3][3] = 0.0f;
return result;
}
DQN_FILE_SCOPE DqnMat4 DqnMat4_LookAt(DqnV3 eye, DqnV3 center, DqnV3 up)
{
DqnMat4 result = {0};
DqnV3 f = DqnV3_Normalise(DqnV3_Sub(eye, center));
DqnV3 s = DqnV3_Normalise(DqnV3_Cross(up, f));
DqnV3 u = DqnV3_Cross(f, s);
result.e[0][0] = s.x;
result.e[0][1] = u.x;
result.e[0][2] = f.x;
result.e[1][0] = s.y;
result.e[1][1] = u.y;
result.e[1][2] = f.y;
result.e[2][0] = s.z;
result.e[2][1] = u.z;
result.e[2][2] = f.z;
result.e[3][0] = DqnV3_Dot(s, eye);
result.e[3][1] = DqnV3_Dot(u, eye);
result.e[3][2] = -DqnV3_Dot(f, eye);
result.e[3][3] = 1.0f;
return result;
}
DQN_FILE_SCOPE DqnMat4 DqnMat4_Translate(f32 x, f32 y, f32 z) DQN_FILE_SCOPE DqnMat4 DqnMat4_Translate(f32 x, f32 y, f32 z)
{ {
DqnMat4 result = DqnMat4_Identity(); DqnMat4 result = DqnMat4_Identity();
@ -2285,20 +2403,19 @@ DQN_FILE_SCOPE DqnMat4 DqnMat4_Rotate(f32 radians, f32 x, f32 y, f32 z)
DqnMat4 result = DqnMat4_Identity(); DqnMat4 result = DqnMat4_Identity();
f32 sinVal = sinf(radians); f32 sinVal = sinf(radians);
f32 cosVal = cosf(radians); f32 cosVal = cosf(radians);
f32 oneMinusCosVal = 1 - cosVal;
result.e[0][0] = (cosVal + (DQN_SQUARED(x) * (1.0f - cosVal))); result.e[0][0] = (DQN_SQUARED(x) * oneMinusCosVal) + cosVal;
result.e[0][1] = ((y * z * (1.0f - cosVal)) + (z * sinVal)); result.e[0][1] = (x * y * oneMinusCosVal) + (z * sinVal);
result.e[0][2] = ((z * x * (1.0f - cosVal)) - (y * sinVal)); result.e[0][2] = (x * z * oneMinusCosVal) - (y * sinVal);
result.e[1][0] = ((x * y * (1.0f - cosVal)) - (z * sinVal)); result.e[1][0] = (y * x * oneMinusCosVal) - (z * sinVal);
result.e[1][1] = (cosVal + (DQN_SQUARED(y) * (1.0f - cosVal))); result.e[1][1] = (DQN_SQUARED(y) * oneMinusCosVal) + cosVal;
result.e[1][2] = ((z * y * (1.0f - cosVal)) + (x * sinVal)); result.e[1][2] = (y * z * oneMinusCosVal) + (x * sinVal);
result.e[2][0] = ((x * z * (1.0f - cosVal)) + (y * sinVal)); result.e[2][0] = (z * x * oneMinusCosVal) + (y * sinVal);
result.e[2][1] = ((y * z * (1.0f - cosVal)) - (x * sinVal)); result.e[2][1] = (z * y * oneMinusCosVal) - (x * sinVal);
result.e[2][2] = (cosVal + (DQN_SQUARED(z) * (1.0f - cosVal))); result.e[2][2] = (DQN_SQUARED(z) * oneMinusCosVal) + cosVal;
result.e[3][3] = 1;
return result; return result;
} }
@ -2317,7 +2434,8 @@ DQN_FILE_SCOPE DqnMat4 DqnMat4_Mul(DqnMat4 a, DqnMat4 b)
{ {
DqnMat4 result = {0}; DqnMat4 result = {0};
for (i32 j = 0; j < 4; j++) { for (i32 j = 0; j < 4; j++) {
for (i32 i = 0; i < 4; i++) { for (i32 i = 0; i < 4; i++)
{
result.e[j][i] = a.e[0][i] * b.e[j][0] result.e[j][i] = a.e[0][i] * b.e[j][0]
+ a.e[1][i] * b.e[j][1] + a.e[1][i] * b.e[j][1]
+ a.e[2][i] * b.e[j][2] + a.e[2][i] * b.e[j][2]
@ -2331,15 +2449,10 @@ DQN_FILE_SCOPE DqnMat4 DqnMat4_Mul(DqnMat4 a, DqnMat4 b)
DQN_FILE_SCOPE DqnV4 DqnMat4_MulV4(DqnMat4 a, DqnV4 b) DQN_FILE_SCOPE DqnV4 DqnMat4_MulV4(DqnMat4 a, DqnV4 b)
{ {
DqnV4 result = {0}; DqnV4 result = {0};
result.x = (a.e[0][0] * b.x) + (a.e[1][0] * b.y) + (a.e[2][0] * b.z) + (a.e[3][0] * b.w);
result.x = (a.e[0][0] * b.x) + (a.e[1][0] * b.y) + (a.e[2][0] * b.z) + result.y = (a.e[0][1] * b.x) + (a.e[1][1] * b.y) + (a.e[2][1] * b.z) + (a.e[3][1] * b.w);
(a.e[3][0] * b.w); result.z = (a.e[0][2] * b.x) + (a.e[1][2] * b.y) + (a.e[2][2] * b.z) + (a.e[3][2] * b.w);
result.y = (a.e[0][1] * b.x) + (a.e[1][1] * b.y) + (a.e[2][1] * b.z) + result.w = (a.e[0][3] * b.x) + (a.e[1][3] * b.y) + (a.e[2][3] * b.z) + (a.e[3][3] * b.w);
(a.e[3][1] * b.w);
result.z = (a.e[0][2] * b.x) + (a.e[1][2] * b.y) + (a.e[2][2] * b.z) +
(a.e[3][2] * b.w);
result.w = (a.e[0][3] * b.x) + (a.e[1][3] * b.y) + (a.e[2][3] * b.z) +
(a.e[3][3] * b.w);
return result; return result;
} }

View File

@ -2,7 +2,97 @@
#define DQN_IMPLEMENTATION #define DQN_IMPLEMENTATION
#include "dqn.h" #include "dqn.h"
#define HANDMADE_MATH_IMPLEMENTATION
#define HANDMADE_MATH_CPP_MODE
#include "tests/HandmadeMath.h"
#include <stdio.h> #include <stdio.h>
void HandmadeMathVerifyMat4(DqnMat4 dqnMat, hmm_mat4 hmmMat)
{
f32 *hmmMatf = (f32 *)&hmmMat;
f32 *dqnMatf = (f32 *)&dqnMat;
const u32 EXPECTED_SIZE = 16;
u32 totalSize = DQN_ARRAY_COUNT(dqnMat.e) * DQN_ARRAY_COUNT(dqnMat.e[0]);
DQN_ASSERT(totalSize == EXPECTED_SIZE);
DQN_ASSERT(totalSize == (DQN_ARRAY_COUNT(hmmMat.Elements) * DQN_ARRAY_COUNT(hmmMat.Elements[0])));
for (i32 i = 0; i < EXPECTED_SIZE; i++)
DQN_ASSERT(hmmMatf[i] == dqnMatf[i]);
}
void HandmadeMathTest()
{
// Test Perspective/Projection matrix values
{
f32 aspectRatio = 1;
DqnMat4 dqnPerspective = DqnMat4_Perspective(90, aspectRatio, 100, 1000);
hmm_mat4 hmmPerspective = HMM_Perspective(90, aspectRatio, 100, 1000);
HandmadeMathVerifyMat4(dqnPerspective, hmmPerspective);
printf("HandmadeMathTest(): Perspective: Completed successfully\n");
}
// Test Mat4 translate * scale
{
hmm_vec3 hmmVec = HMM_Vec3i(1, 2, 3);
DqnV3 dqnVec = DqnV3_3i(1, 2, 3);
DqnMat4 dqnTranslate = DqnMat4_Translate(dqnVec.x, dqnVec.y, dqnVec.z);
hmm_mat4 hmmTranslate = HMM_Translate(hmmVec);
HandmadeMathVerifyMat4(dqnTranslate, hmmTranslate);
hmm_vec3 hmmAxis = HMM_Vec3(0.5f, 0.2f, 0.7f);
DqnV3 dqnAxis = DqnV3_3f(0.5f, 0.2f, 0.7f);
f32 rotationInDegrees = 80.0f;
// TODO(doyle): ?? Handmade Math does it a rotations in a different way
// way, they normalise the given axis producing different results.
// HandmadeMathVerifyMat4(dqnRotate, hmmRotate);
dqnVec *= 2;
hmmVec *= 2;
DqnMat4 dqnScale = DqnMat4_Scale(dqnVec.x, dqnVec.y, dqnVec.z);
hmm_mat4 hmmScale = HMM_Scale(hmmVec);
HandmadeMathVerifyMat4(dqnScale, hmmScale);
DqnMat4 dqnTSMatrix = DqnMat4_Mul(dqnTranslate, dqnScale);
hmm_mat4 hmmTSMatrix = HMM_MultiplyMat4(hmmTranslate, hmmScale);
HandmadeMathVerifyMat4(dqnTSMatrix, hmmTSMatrix);
// Test Mat4 * MulV4
{
DqnV4 dqnV4 = DqnV4_4f(1, 2, 3, 4);
hmm_vec4 hmmV4 = HMM_Vec4(1, 2, 3, 4);
DqnV4 dqnResult = DqnMat4_MulV4(dqnTSMatrix, dqnV4);
hmm_vec4 hmmResult = HMM_MultiplyMat4ByVec4(hmmTSMatrix, hmmV4);
DQN_ASSERT(dqnResult.x == hmmResult.X);
DQN_ASSERT(dqnResult.y == hmmResult.Y);
DQN_ASSERT(dqnResult.z == hmmResult.Z);
DQN_ASSERT(dqnResult.w == hmmResult.W);
printf(
"HandmadeMathTest(): Mat4 * MulV4: Completed successfully\n");
}
printf("HandmadeMathTest(): Translate/Scale/Rotate Mat4_Mul: Completed successfully\n");
}
// Test LookAt/Camera/View matrix returns same results
{
DqnMat4 dqnViewMatrix = DqnMat4_LookAt(DqnV3_3f(4, 3, 3), DqnV3_1f(0), DqnV3_3f(0, 1, 0));
hmm_mat4 hmmViewMatrix =
HMM_LookAt(HMM_Vec3(4, 3, 3), HMM_Vec3(0, 0, 0), HMM_Vec3(0, 1, 0));
HandmadeMathVerifyMat4(dqnViewMatrix, hmmViewMatrix);
printf("HandmadeMathTest(): LookAt: Completed successfully\n");
}
}
void StringsTest() void StringsTest()
{ {
{ // Char Checks { // Char Checks
@ -1404,6 +1494,7 @@ int main(void)
StringsTest(); StringsTest();
RandomTest(); RandomTest();
MathTest(); MathTest();
HandmadeMathTest();
VecTest(); VecTest();
OtherTest(); OtherTest();
ArrayTest(); ArrayTest();

2659
tests/HandmadeMath.h Normal file

File diff suppressed because it is too large Load Diff