Compare commits

...

40 Commits

Author SHA1 Message Date
Ben Visness
ea1b8c3fdf Add models 2020-04-26 15:15:07 -05:00
Ben Visness
066df8dc02 Get a matrix in there 2020-04-25 11:33:49 -05:00
Ben Visness
cee57ba573 Start of new example stuff 2020-04-25 11:33:49 -05:00
Ben Visness
c66971850a Update version and release notes 2020-04-25 11:33:48 -05:00
Ben Visness
43b5686636 Add ability to customize HMM_ prefix (#114)
* Add prefix macro and use it everywhere

* Add lightweight test for prefix

* Add a little doc blurb
2020-04-25 11:33:48 -05:00
Ben Visness
49f274249f Suppress float equality warnings (#113)
* Add a macro to help with deprecations

* Suppress warnings about float equality
2020-04-25 11:33:47 -05:00
Zak Strange
f1297e7f31 Removed use of stdint.h (#110) 2020-04-25 11:33:47 -05:00
Ben Visness
d3d09f8352 Make all functions static inline (#117)
* Make all functions static inline

* Updated email in credits

Co-authored-by: Zakary Strange <ZakaryStrange@gmail.com>
2020-04-11 18:41:26 -05:00
Ben Visness
68d2af495c Make everything use radians (#107)
* Make everything use radians (and provide an escape hatch)

* Fix tests

* Add tests for forcing degrees

* Add degrees tests for windows

* Remove coverage macro; just always use radians

* Update forward declarations and clean up tests

* Tell the user that angles are in radians
2020-04-06 09:56:43 -05:00
Ben Visness
a9b08b9147 Run tests on Linux, macOS, and Windows (#105)
* Try running tests on all three operating systems

* Try adding an MSVC build

* Make tests work on Windows

* Try reconfiguring Travis for this

* Maybe it's because we're in git bash

* Try explicitly doing something else for travis

* Remove a part I think is unnecessary

* Run the test EXEs as they compile
2019-07-31 17:22:33 -05:00
Ben Visness
f376f2a2a7 Add test coverage macros (#104)
* Add coverage features and add it, laboriously, to everything

* Fix easy tests

* Add tests for != operators

* Clean up test framework a little

* Add documentation of coverage macros

* Fix tests for mat4 to quaternion

* Slightly improve formatting of coverage output

* Trailing whitespace must die
2019-07-31 16:43:56 -05:00
Ben Visness
78e6feea82 Add HMM_Mat4ToQuaternion (#103)
* Add mat4 to quaternion method

* Capitalize variables
2019-07-31 16:38:03 -05:00
Zak Strange
21aa828a08 Fixed issue related to unsigned/signed-ness of HMM_Power (#102)
* Fixed issue related to unsigned/signed-ness of HMM_Power

* Fixes missing braces around initializer warning with -Weverything with GCC.
2019-07-17 14:48:59 -07:00
Ben Visness
93e56be543 Use vertical instead of horizontal FOV in HMM_Perspective (#101)
* Use vertical instead of horizontal FOV

* Update readme

* Fix tests
2019-07-10 11:29:51 -05:00
Ben Visness
45c91702a9 Added SSE support for Quaternion operations (#97) (#98)
* Added SSE support for Quaternion operations (#97)

* Added SSE support for Quaternion operations

O2
| Function    |     SSE         |      NO SSE      |
====================================================
| Inverse     |     163 (0.89s) |      165 (1.89s) |
| NLerp       |     330 (1.70s) |      330 (1.75s) |
| Normalize   |     169 (1.03s) |      169 (1.06s) |
| Dot         |     22  (1.15s) |      23  (1.14s) |
| DivF        |     23  (0.72s) |      23  (0.82s) |
| MulF        |     22  (0.75s) |      22  (0.79s) |
| Mul         |     24  (1.14s) |      23  (1.24s) |
| Sub         |     23  (1.17s) |      37  (1.20s) |
| Add         |     23  (1.20s) |      24  (1.19s) |



O0
| Function    |     SSE         |      NO SSE      |
====================================================
| Inverse     |     394 (1.62s) |      430 (3.05s) |
| NLerp       |     694 (2.71s) |      1035(4.81s) |
| Normalize   |     374 (1.58s) |      412 (2.95s) |
| Dot         |     81  (1.83s) |      23  (2.50s) |
| DivF        |     61  (1.12s) |      25  (2.37s) |
| MulF        |     58  (1.09s) |      23  (2.31s) |
| Mul         |     94  (1.97s) |      42  (2.88s) |
| Sub         |     75  (1.83s) |      23  (2.82s) |
| Add         |     75  (1.81s) |      23  (2.81s) |

* Fixed quaternion multiplication

Old quaternion multiplication had a bug, this is a different approach.

* Added release notes and version for 1.9.0
2019-03-11 13:12:48 -05:00
Ben Visness
f7c8e1f7d1 Add fast vector normalization (#94)
* Add fast normalization routines

* Update readme and remove version history from main file

* Update version at top of file
2018-11-29 22:02:41 -08:00
Ben Visness
5ca1d58b36 Improve grammar/spelling 2018-11-29 13:21:05 -06:00
Zak Strange
5bf727dbd5 Removed copy in operator[] (#93)
* Removed copy in operator[]

* Updated version info
2018-11-29 09:32:12 -08:00
Ben Visness
295f6c476f Rename Rows to Columns on hmm_mat4 (#91) 2018-08-17 11:02:44 -07:00
Ben Visness
e095aefaf7 Bump file version 2018-06-10 15:32:12 -04:00
Ben Visness
4e2f47db55 Add array subscript operators for all types (#88)
* Add array subscript operators for all types

* Taking the parameter for the operator[] as a reference. This should allow it to be inlined

* I guess you can't do that.

* Update version and readme
2018-06-10 15:26:48 -04:00
Ben Visness
bee0e0c569 WIP: Properly initialize all elements of LookAt matrix (#84)
* Properly initialize all elements of LookAt matrix

* Update version and readme

* Add a test for LookAt

good enough
2018-06-03 18:42:09 -05:00
Ben Visness
f8b3a84cec WIP: Make tests quite a lot nicer (#81)
Make tests quite a lot nicer
2018-02-18 14:19:31 -06:00
IJzerbaard
77914405c3 Use SSE in HMM_MultiplyMat4ByVec4 (#77)
Implements HMM_MultiplyMat4ByVec4 with HMM_LinearCombineSSE if SSE is
enabled.
2018-02-11 13:24:39 -06:00
IJzerbaard
eb5c659148 Improve HMM_MultiplyMat4 (#76)
Remove all transposes, (AT BT)T = BA
2018-02-10 17:59:07 -06:00
Ben Visness
52fd5cceb4 Update README.md 2017-10-31 19:28:59 -05:00
Ben Visness
8e67482295 Update README.md 2017-10-31 11:07:07 -05:00
Ben Visness
c508ce342b Create LICENSE 2017-10-31 11:04:56 -05:00
Ben Visness
250c38e845 Update README.md 2017-10-31 11:01:31 -05:00
Ben Visness
4981d5ab89 Convert everything to new inline scheme (#57) (#72)
* Convert everything to new inline scheme

* Add both extern and inline for some SSE vs. non-SSE functions

Some functions had nice, compact SSE implementations but bulky non-SSE
implementations, so this commit inlines just the implementations that make
sense. Also updated documentation.

* Convert HINLINE and HEXTERN to HMM_INLINE and HMM_EXTERN
2017-10-31 10:16:36 -05:00
Ben Visness
5f173e0176 Update CONTRIBUTING.md 2017-10-31 10:09:27 -05:00
Ben Visness
575fcb767d Update README.md 2017-10-31 10:06:22 -05:00
strangezak
a08262b2d9 Removed old instructions 2017-10-14 19:34:51 -07:00
strangezak
53bc939d8e Updated email 2017-10-04 09:40:31 -07:00
Ben Visness
7eb4ae1846 Update README for 1.4.0 2017-10-02 11:17:11 -05:00
StrangeZak
48bd24b05e Updated docs 2017-10-01 10:32:53 -07:00
Zak Strange
064baeb5b9 V1.4 - SSE upgrades and bug fixes (#70)
* Fixed bug when using handmademath in C mode

* SSEd vec4 operations

* Fixed hmm_vec4 for non-sse builds. Added SSE option for HMM_Vec4 to load in one instruction

* Whoops. We were loading in the wrong order

* SSEd more things

* SSEd more functions

* Minor fixups

* SSE'd hmm_vec4 initialization

* Removed zeroing

* Vector normalization should be zero'd

* Removed old comments
2017-09-30 14:38:28 -07:00
Ben Visness
afd726ab0b Automatically include C++ definitions (#67) 2017-08-02 10:12:06 -05:00
strangezak
98fffbd7cc Forgot to update version 2017-07-16 21:29:26 -07:00
Ben Visness
efd9f2f4b7 Matrix Multiply SSE (#65)
* SSEd HMM_MultiplyMat4 and HMM_Transpose. And added HMM_LinearCombineSSE

* Maybe Travis doesn't support SSE?

* Fix compile process so the SSE option is consistently defined

* Fix link error

* Documentation

* Added function prototype for operator ==

* Added != operator for hmm_vec2, hmm_vec3, hmm_vec4

* Add C versions of equality checks

Also made the C++ tests actually run...😳

* Update documentation
2017-07-16 21:19:34 -07:00
42 changed files with 24081 additions and 4045 deletions

9
.editorconfig Normal file
View File

@@ -0,0 +1,9 @@
root = true
[*.{c,cpp,h}]
charset = utf-8
indent_style = space
indent_size = 4
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true

6
.gitignore vendored
View File

@@ -31,5 +31,7 @@
*.exe
*.out
*.app
hmm_test
hmm_test*
test/build
example/build
example/lib/flextgl/*

9
.gitmodules vendored
View File

@@ -0,0 +1,9 @@
[submodule "example/lib/glfw"]
path = example/lib/glfw
url = git@github.com:glfw/glfw.git
[submodule "example/lib/flextgl-gen"]
path = example/lib/flextgl-gen
url = git@github.com:mosra/flextgl.git
[submodule "example/lib/EGL-Registry"]
path = example/lib/EGL-Registry
url = git@github.com:KhronosGroup/EGL-Registry.git

View File

@@ -1,12 +1,23 @@
language: cpp
os:
- linux
- osx
compiler:
- clang
- gcc
matrix:
include:
# Windows x64 builds (MSVC)
- os: windows
script:
- ./test.bat travis
before_install:
- eval "${MATRIX_EVAL}"
install:
- cd test
- make
script:
- ./hmm_test_c
- ./hmm_test_c_no_sse
- ./hmm_test_cpp
- ./hmm_test_cpp_no_sse
- make c
- make c_no_sse
- make cpp
- make cpp_no_sse

View File

@@ -1,3 +1,7 @@
# Understanding the structure of Handmade Math
Most of the functions in Handmade Math are very short, and all are the kind of functions you want to be easily inlined for performance. Because of this, all functions in Handmade Math are defined with `HMM_INLINE`, which is defined as `static inline`.
# Quick style guide
* Put braces on a new line
@@ -8,28 +12,11 @@
0.5f;
1.0f;
3.14159f;
// Bad
1.f
.0f
```
* Put macros and return types on a separate line from the function definition:
```cpp
HINLINE float
HMM_MyFunction()
{
// ...
}
```
* Explicitly initialize variables to zero:
```cpp
HINLINE float
HMM_MyFunction()
{
float MyFloat = 0.0f;
hmm_vec3 MyVector = {0};
}
```
* Put parentheses around the returned value:
```cpp
HINLINE float
@@ -40,7 +27,7 @@
```
# Other notes
## Other style notes
* If a new function is defined with different names for different datatypes, also add C++ overloaded versions of the functions. For example, if you have `HMM_LengthVec2(hmm_vec2)` and `HMM_LengthVec3(hmm_vec3)`, also provide `HMM_Length(hmm_vec2)` and `HMM_Length(hmm_vec3)`.
@@ -49,3 +36,6 @@
* Try to define functions in the same order as the prototypes.
* Don't forget that Handmade Math uses column-major order for matrices!
# Versioning
We use [semantic versioning](http://semver.org/) because it's reasonable.

File diff suppressed because it is too large Load Diff

116
LICENSE Normal file
View File

@@ -0,0 +1,116 @@
CC0 1.0 Universal
Statement of Purpose
The laws of most jurisdictions throughout the world automatically confer
exclusive Copyright and Related Rights (defined below) upon the creator and
subsequent owner(s) (each and all, an "owner") of an original work of
authorship and/or a database (each, a "Work").
Certain owners wish to permanently relinquish those rights to a Work for the
purpose of contributing to a commons of creative, cultural and scientific
works ("Commons") that the public can reliably and without fear of later
claims of infringement build upon, modify, incorporate in other works, reuse
and redistribute as freely as possible in any form whatsoever and for any
purposes, including without limitation commercial purposes. These owners may
contribute to the Commons to promote the ideal of a free culture and the
further production of creative, cultural and scientific works, or to gain
reputation or greater distribution for their Work in part through the use and
efforts of others.
For these and/or other purposes and motivations, and without any expectation
of additional consideration or compensation, the person associating CC0 with a
Work (the "Affirmer"), to the extent that he or she is an owner of Copyright
and Related Rights in the Work, voluntarily elects to apply CC0 to the Work
and publicly distribute the Work under its terms, with knowledge of his or her
Copyright and Related Rights in the Work and the meaning and intended legal
effect of CC0 on those rights.
1. Copyright and Related Rights. A Work made available under CC0 may be
protected by copyright and related or neighboring rights ("Copyright and
Related Rights"). Copyright and Related Rights include, but are not limited
to, the following:
i. the right to reproduce, adapt, distribute, perform, display, communicate,
and translate a Work;
ii. moral rights retained by the original author(s) and/or performer(s);
iii. publicity and privacy rights pertaining to a person's image or likeness
depicted in a Work;
iv. rights protecting against unfair competition in regards to a Work,
subject to the limitations in paragraph 4(a), below;
v. rights protecting the extraction, dissemination, use and reuse of data in
a Work;
vi. database rights (such as those arising under Directive 96/9/EC of the
European Parliament and of the Council of 11 March 1996 on the legal
protection of databases, and under any national implementation thereof,
including any amended or successor version of such directive); and
vii. other similar, equivalent or corresponding rights throughout the world
based on applicable law or treaty, and any national implementations thereof.
2. Waiver. To the greatest extent permitted by, but not in contravention of,
applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and
unconditionally waives, abandons, and surrenders all of Affirmer's Copyright
and Related Rights and associated claims and causes of action, whether now
known or unknown (including existing as well as future claims and causes of
action), in the Work (i) in all territories worldwide, (ii) for the maximum
duration provided by applicable law or treaty (including future time
extensions), (iii) in any current or future medium and for any number of
copies, and (iv) for any purpose whatsoever, including without limitation
commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes
the Waiver for the benefit of each member of the public at large and to the
detriment of Affirmer's heirs and successors, fully intending that such Waiver
shall not be subject to revocation, rescission, cancellation, termination, or
any other legal or equitable action to disrupt the quiet enjoyment of the Work
by the public as contemplated by Affirmer's express Statement of Purpose.
3. Public License Fallback. Should any part of the Waiver for any reason be
judged legally invalid or ineffective under applicable law, then the Waiver
shall be preserved to the maximum extent permitted taking into account
Affirmer's express Statement of Purpose. In addition, to the extent the Waiver
is so judged Affirmer hereby grants to each affected person a royalty-free,
non transferable, non sublicensable, non exclusive, irrevocable and
unconditional license to exercise Affirmer's Copyright and Related Rights in
the Work (i) in all territories worldwide, (ii) for the maximum duration
provided by applicable law or treaty (including future time extensions), (iii)
in any current or future medium and for any number of copies, and (iv) for any
purpose whatsoever, including without limitation commercial, advertising or
promotional purposes (the "License"). The License shall be deemed effective as
of the date CC0 was applied by Affirmer to the Work. Should any part of the
License for any reason be judged legally invalid or ineffective under
applicable law, such partial invalidity or ineffectiveness shall not
invalidate the remainder of the License, and in such case Affirmer hereby
affirms that he or she will not (i) exercise any of his or her remaining
Copyright and Related Rights in the Work or (ii) assert any associated claims
and causes of action with respect to the Work, in either case contrary to
Affirmer's express Statement of Purpose.
4. Limitations and Disclaimers.
a. No trademark or patent rights held by Affirmer are waived, abandoned,
surrendered, licensed or otherwise affected by this document.
b. Affirmer offers the Work as-is and makes no representations or warranties
of any kind concerning the Work, express, implied, statutory or otherwise,
including without limitation warranties of title, merchantability, fitness
for a particular purpose, non infringement, or the absence of latent or
other defects, accuracy, or the present or absence of errors, whether or not
discoverable, all to the greatest extent permissible under applicable law.
c. Affirmer disclaims responsibility for clearing rights of other persons
that may apply to the Work or any use thereof, including without limitation
any person's Copyright and Related Rights in the Work. Further, Affirmer
disclaims responsibility for obtaining any necessary consents, permissions
or other rights required for any use of the Work.
d. Affirmer understands and acknowledges that Creative Commons is not a
party to this document and has no duty or obligation with respect to this
CC0 or use of the Work.
For more information, please see
<http://creativecommons.org/publicdomain/zero/1.0/>

View File

@@ -1,17 +1,28 @@
# Handmade-Math
------
# Handmade Math
[![Build Status](https://travis-ci.org/StrangeZak/Handmade-Math.svg?branch=master)](https://travis-ci.org/StrangeZak/Handmade-Math)
[![Build Status](https://travis-ci.org/HandmadeMath/Handmade-Math.svg?branch=master)](https://travis-ci.org/StrangeZak/Handmade-Math)
Single-file cross-platform public domain game math library for C/C++
A single-file, cross-platform, public domain game math library for C/C++.
_This library is free and will stay free, but if you would like to support development, or you are a company using HandmadeMath, please consider financial support._
To get started, go download [the latest release](https://github.com/HandmadeMath/Handmade-Math/releases).
[![Patreon](https://cloud.githubusercontent.com/assets/8225057/5990484/70413560-a9ab-11e4-8942-1a63607c0b00.png)](http://www.patreon.com/strangezak) [![PayPal](https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif)](https://www.paypal.me/zakarystrange)
-----
Version | Changes |
Version | Changes |
----------------|----------------|
**1.11.0** | Added ability to customize or remove the default `HMM_` prefix on function names by defining a macro called `HMM_PREFIX(name)`. |
**1.10.1** | Removed stdint.h, this doesn't exist on some really old compilers and we didn't really use it anyways. |
**1.10.0** | Made HMM_Perspective use vertical FOV instead of horizontal FOV for consistency with other graphics APIs. |
**1.9.0** | Added SSE versions of quaternion operations. |
**1.8.0** | Added fast vector normalization routines that use fast inverse square roots.
**1.7.1** | Changed operator[] to take a const ref int instead of an int.
**1.7.0** | Renamed the 'Rows' member of hmm_mat4 to 'Columns'. Since our matrices are column-major, this should have been named 'Columns' from the start. 'Rows' is still present, but has been deprecated.
**1.6.0** | Added array subscript operators for vector and matrix types in C++. This is provided as a convenience, but be aware that it may incur an extra function call in unoptimized builds.
**1.5.1** | Fixed a bug with uninitialized elements in HMM_LookAt.
**1.5.0** | Changed internal structure for better performance and inlining. As a result, `HANDMADE_MATH_NO_INLINE` has been removed and no longer has any effect.
**1.4.0** | Fixed bug when using C mode. SSE'd all vec4 operations. Removed zeroing for better performance.
**1.3.0** | Removed need to `#define HANDMADE_MATH_CPP_MODE`. C++ definitions are now included automatically in C++ environments.
**1.2.0** | Added equality functions for `HMM_Vec2`, `HMM_Vec3`, and `HMM_Vec4`, and SSE'd `HMM_MultiplyMat4` and `HMM_Transpose`.
**1.1.5** | Added `Width` and `Height` to `HMM_Vec2`, and made it so you can supply your own `SqrtF`.
**1.1.4** | Fixed SSE being included on platforms that don't support it, and fixed divide-by-zero errors when normalizing zero vectors.
**1.1.3** | Fixed compile error in C mode
@@ -32,7 +43,6 @@ Version | Changes |
**0.1** | Initial Version |
-----
_This library is free and will stay free, but if you would like to support development, or you are a company using HandmadeMath, please consider financial support._
## FAQ
@@ -42,4 +52,4 @@ This library is in the public domain. You can do whatever you want with it.
**Where can I contact you to ask questions?**
You can email me at: Zak@Handmade.Network
Feel free to make Github issues for any questions, concerns, or problems you encounter.

34
example/Makefile Normal file
View File

@@ -0,0 +1,34 @@
ifeq ($(OS),Windows_NT)
RM = del /Q /F
RMDIR = rmdir /Q /S
CP = copy /Y
PYTHON = python
PIP = pip
else
RM = rm -rf
RMDIR = rm -rf
CP = cp -f
PYTHON = python3
PIP = pip3
endif
BUILD_DIR=build
all: example
example: lib/flextgl/flextgl.h
$(RMDIR) $(BUILD_DIR)
mkdir $(BUILD_DIR)
cd $(BUILD_DIR) \
&& $(CC) -std=c99 -c -lm \
-I../lib/glfw/include \
-I../lib/flextgl \
-I../lib/EGL-Registry/api \
../src/main.c ../lib/flextgl/flextGL.c \
&& $(CC) -ohmm_example.exe main.o -lm
flextgl-deps:
$(PIP) install --user wheezy.template
lib/flextgl/flextgl.h lib/flextgl/flextgl.c: flextgl-deps
$(PYTHON) lib/flextgl-gen/flextGLgen.py -D lib/flextgl -T glfw3 flextgl-profile.txt

19
example/build.bat Normal file
View File

@@ -0,0 +1,19 @@
@echo off
where /q cl
if ERRORLEVEL 1 (
for /f "delims=" %%a in ('"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -find VC\Auxiliary\Build\vcvarsall.bat') do (%%a x64)
)
python lib\flextgl-gen\flextGLgen.py -D lib\flextgl -T glfw3 flextgl-profile.txt
if not exist "build" mkdir build
pushd build
cl ^
/Feexample.exe /MD ^
/I..\lib\glfw\include /I..\lib\flextgl /I..\lib\EGL-Registry\api ^
..\src\main.c ..\lib\flextgl\flextGL.c ^
/link user32.lib shell32.lib gdi32.lib opengl32.lib ..\lib\glfw\lib-vc2017\glfw3.lib
popd

View File

@@ -0,0 +1 @@
version 3.3 core

3
example/install.bat Normal file
View File

@@ -0,0 +1,3 @@
@echo off
pip install --user wheezy.template

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,525 @@
/*************************************************************************
* GLFW 3.3 - www.glfw.org
* A library for OpenGL, window and input
*------------------------------------------------------------------------
* Copyright (c) 2002-2006 Marcus Geelnard
* Copyright (c) 2006-2018 Camilla Löwy <elmindreda@glfw.org>
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would
* be appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not
* be misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source
* distribution.
*
*************************************************************************/
#ifndef _glfw3_native_h_
#define _glfw3_native_h_
#ifdef __cplusplus
extern "C" {
#endif
/*************************************************************************
* Doxygen documentation
*************************************************************************/
/*! @file glfw3native.h
* @brief The header of the native access functions.
*
* This is the header file of the native access functions. See @ref native for
* more information.
*/
/*! @defgroup native Native access
* @brief Functions related to accessing native handles.
*
* **By using the native access functions you assert that you know what you're
* doing and how to fix problems caused by using them. If you don't, you
* shouldn't be using them.**
*
* Before the inclusion of @ref glfw3native.h, you may define zero or more
* window system API macro and zero or more context creation API macros.
*
* The chosen backends must match those the library was compiled for. Failure
* to do this will cause a link-time error.
*
* The available window API macros are:
* * `GLFW_EXPOSE_NATIVE_WIN32`
* * `GLFW_EXPOSE_NATIVE_COCOA`
* * `GLFW_EXPOSE_NATIVE_X11`
* * `GLFW_EXPOSE_NATIVE_WAYLAND`
*
* The available context API macros are:
* * `GLFW_EXPOSE_NATIVE_WGL`
* * `GLFW_EXPOSE_NATIVE_NSGL`
* * `GLFW_EXPOSE_NATIVE_GLX`
* * `GLFW_EXPOSE_NATIVE_EGL`
* * `GLFW_EXPOSE_NATIVE_OSMESA`
*
* These macros select which of the native access functions that are declared
* and which platform-specific headers to include. It is then up your (by
* definition platform-specific) code to handle which of these should be
* defined.
*/
/*************************************************************************
* System headers and types
*************************************************************************/
#if defined(GLFW_EXPOSE_NATIVE_WIN32) || defined(GLFW_EXPOSE_NATIVE_WGL)
// This is a workaround for the fact that glfw3.h needs to export APIENTRY (for
// example to allow applications to correctly declare a GL_ARB_debug_output
// callback) but windows.h assumes no one will define APIENTRY before it does
#if defined(GLFW_APIENTRY_DEFINED)
#undef APIENTRY
#undef GLFW_APIENTRY_DEFINED
#endif
#include <windows.h>
#elif defined(GLFW_EXPOSE_NATIVE_COCOA) || defined(GLFW_EXPOSE_NATIVE_NSGL)
#if defined(__OBJC__)
#import <Cocoa/Cocoa.h>
#else
#include <ApplicationServices/ApplicationServices.h>
typedef void* id;
#endif
#elif defined(GLFW_EXPOSE_NATIVE_X11) || defined(GLFW_EXPOSE_NATIVE_GLX)
#include <X11/Xlib.h>
#include <X11/extensions/Xrandr.h>
#elif defined(GLFW_EXPOSE_NATIVE_WAYLAND)
#include <wayland-client.h>
#endif
#if defined(GLFW_EXPOSE_NATIVE_WGL)
/* WGL is declared by windows.h */
#endif
#if defined(GLFW_EXPOSE_NATIVE_NSGL)
/* NSGL is declared by Cocoa.h */
#endif
#if defined(GLFW_EXPOSE_NATIVE_GLX)
#include <GL/glx.h>
#endif
#if defined(GLFW_EXPOSE_NATIVE_EGL)
#include <EGL/egl.h>
#endif
#if defined(GLFW_EXPOSE_NATIVE_OSMESA)
#include <GL/osmesa.h>
#endif
/*************************************************************************
* Functions
*************************************************************************/
#if defined(GLFW_EXPOSE_NATIVE_WIN32)
/*! @brief Returns the adapter device name of the specified monitor.
*
* @return The UTF-8 encoded adapter device name (for example `\\.\DISPLAY1`)
* of the specified monitor, or `NULL` if an [error](@ref error_handling)
* occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.1.
*
* @ingroup native
*/
GLFWAPI const char* glfwGetWin32Adapter(GLFWmonitor* monitor);
/*! @brief Returns the display device name of the specified monitor.
*
* @return The UTF-8 encoded display device name (for example
* `\\.\DISPLAY1\Monitor0`) of the specified monitor, or `NULL` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.1.
*
* @ingroup native
*/
GLFWAPI const char* glfwGetWin32Monitor(GLFWmonitor* monitor);
/*! @brief Returns the `HWND` of the specified window.
*
* @return The `HWND` of the specified window, or `NULL` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.0.
*
* @ingroup native
*/
GLFWAPI HWND glfwGetWin32Window(GLFWwindow* window);
#endif
#if defined(GLFW_EXPOSE_NATIVE_WGL)
/*! @brief Returns the `HGLRC` of the specified window.
*
* @return The `HGLRC` of the specified window, or `NULL` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.0.
*
* @ingroup native
*/
GLFWAPI HGLRC glfwGetWGLContext(GLFWwindow* window);
#endif
#if defined(GLFW_EXPOSE_NATIVE_COCOA)
/*! @brief Returns the `CGDirectDisplayID` of the specified monitor.
*
* @return The `CGDirectDisplayID` of the specified monitor, or
* `kCGNullDirectDisplay` if an [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.1.
*
* @ingroup native
*/
GLFWAPI CGDirectDisplayID glfwGetCocoaMonitor(GLFWmonitor* monitor);
/*! @brief Returns the `NSWindow` of the specified window.
*
* @return The `NSWindow` of the specified window, or `nil` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.0.
*
* @ingroup native
*/
GLFWAPI id glfwGetCocoaWindow(GLFWwindow* window);
#endif
#if defined(GLFW_EXPOSE_NATIVE_NSGL)
/*! @brief Returns the `NSOpenGLContext` of the specified window.
*
* @return The `NSOpenGLContext` of the specified window, or `nil` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.0.
*
* @ingroup native
*/
GLFWAPI id glfwGetNSGLContext(GLFWwindow* window);
#endif
#if defined(GLFW_EXPOSE_NATIVE_X11)
/*! @brief Returns the `Display` used by GLFW.
*
* @return The `Display` used by GLFW, or `NULL` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.0.
*
* @ingroup native
*/
GLFWAPI Display* glfwGetX11Display(void);
/*! @brief Returns the `RRCrtc` of the specified monitor.
*
* @return The `RRCrtc` of the specified monitor, or `None` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.1.
*
* @ingroup native
*/
GLFWAPI RRCrtc glfwGetX11Adapter(GLFWmonitor* monitor);
/*! @brief Returns the `RROutput` of the specified monitor.
*
* @return The `RROutput` of the specified monitor, or `None` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.1.
*
* @ingroup native
*/
GLFWAPI RROutput glfwGetX11Monitor(GLFWmonitor* monitor);
/*! @brief Returns the `Window` of the specified window.
*
* @return The `Window` of the specified window, or `None` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.0.
*
* @ingroup native
*/
GLFWAPI Window glfwGetX11Window(GLFWwindow* window);
/*! @brief Sets the current primary selection to the specified string.
*
* @param[in] string A UTF-8 encoded string.
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
* GLFW_PLATFORM_ERROR.
*
* @pointer_lifetime The specified string is copied before this function
* returns.
*
* @thread_safety This function must only be called from the main thread.
*
* @sa @ref clipboard
* @sa glfwGetX11SelectionString
* @sa glfwSetClipboardString
*
* @since Added in version 3.3.
*
* @ingroup native
*/
GLFWAPI void glfwSetX11SelectionString(const char* string);
/*! @brief Returns the contents of the current primary selection as a string.
*
* If the selection is empty or if its contents cannot be converted, `NULL`
* is returned and a @ref GLFW_FORMAT_UNAVAILABLE error is generated.
*
* @return The contents of the selection as a UTF-8 encoded string, or `NULL`
* if an [error](@ref error_handling) occurred.
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
* GLFW_PLATFORM_ERROR.
*
* @pointer_lifetime The returned string is allocated and freed by GLFW. You
* should not free it yourself. It is valid until the next call to @ref
* glfwGetX11SelectionString or @ref glfwSetX11SelectionString, or until the
* library is terminated.
*
* @thread_safety This function must only be called from the main thread.
*
* @sa @ref clipboard
* @sa glfwSetX11SelectionString
* @sa glfwGetClipboardString
*
* @since Added in version 3.3.
*
* @ingroup native
*/
GLFWAPI const char* glfwGetX11SelectionString(void);
#endif
#if defined(GLFW_EXPOSE_NATIVE_GLX)
/*! @brief Returns the `GLXContext` of the specified window.
*
* @return The `GLXContext` of the specified window, or `NULL` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.0.
*
* @ingroup native
*/
GLFWAPI GLXContext glfwGetGLXContext(GLFWwindow* window);
/*! @brief Returns the `GLXWindow` of the specified window.
*
* @return The `GLXWindow` of the specified window, or `None` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.2.
*
* @ingroup native
*/
GLFWAPI GLXWindow glfwGetGLXWindow(GLFWwindow* window);
#endif
#if defined(GLFW_EXPOSE_NATIVE_WAYLAND)
/*! @brief Returns the `struct wl_display*` used by GLFW.
*
* @return The `struct wl_display*` used by GLFW, or `NULL` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.2.
*
* @ingroup native
*/
GLFWAPI struct wl_display* glfwGetWaylandDisplay(void);
/*! @brief Returns the `struct wl_output*` of the specified monitor.
*
* @return The `struct wl_output*` of the specified monitor, or `NULL` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.2.
*
* @ingroup native
*/
GLFWAPI struct wl_output* glfwGetWaylandMonitor(GLFWmonitor* monitor);
/*! @brief Returns the main `struct wl_surface*` of the specified window.
*
* @return The main `struct wl_surface*` of the specified window, or `NULL` if
* an [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.2.
*
* @ingroup native
*/
GLFWAPI struct wl_surface* glfwGetWaylandWindow(GLFWwindow* window);
#endif
#if defined(GLFW_EXPOSE_NATIVE_EGL)
/*! @brief Returns the `EGLDisplay` used by GLFW.
*
* @return The `EGLDisplay` used by GLFW, or `EGL_NO_DISPLAY` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.0.
*
* @ingroup native
*/
GLFWAPI EGLDisplay glfwGetEGLDisplay(void);
/*! @brief Returns the `EGLContext` of the specified window.
*
* @return The `EGLContext` of the specified window, or `EGL_NO_CONTEXT` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.0.
*
* @ingroup native
*/
GLFWAPI EGLContext glfwGetEGLContext(GLFWwindow* window);
/*! @brief Returns the `EGLSurface` of the specified window.
*
* @return The `EGLSurface` of the specified window, or `EGL_NO_SURFACE` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.0.
*
* @ingroup native
*/
GLFWAPI EGLSurface glfwGetEGLSurface(GLFWwindow* window);
#endif
#if defined(GLFW_EXPOSE_NATIVE_OSMESA)
/*! @brief Retrieves the color buffer associated with the specified window.
*
* @param[in] window The window whose color buffer to retrieve.
* @param[out] width Where to store the width of the color buffer, or `NULL`.
* @param[out] height Where to store the height of the color buffer, or `NULL`.
* @param[out] format Where to store the OSMesa pixel format of the color
* buffer, or `NULL`.
* @param[out] buffer Where to store the address of the color buffer, or
* `NULL`.
* @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.3.
*
* @ingroup native
*/
GLFWAPI int glfwGetOSMesaColorBuffer(GLFWwindow* window, int* width, int* height, int* format, void** buffer);
/*! @brief Retrieves the depth buffer associated with the specified window.
*
* @param[in] window The window whose depth buffer to retrieve.
* @param[out] width Where to store the width of the depth buffer, or `NULL`.
* @param[out] height Where to store the height of the depth buffer, or `NULL`.
* @param[out] bytesPerValue Where to store the number of bytes per depth
* buffer element, or `NULL`.
* @param[out] buffer Where to store the address of the depth buffer, or
* `NULL`.
* @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.3.
*
* @ingroup native
*/
GLFWAPI int glfwGetOSMesaDepthBuffer(GLFWwindow* window, int* width, int* height, int* bytesPerValue, void** buffer);
/*! @brief Returns the `OSMesaContext` of the specified window.
*
* @return The `OSMesaContext` of the specified window, or `NULL` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.3.
*
* @ingroup native
*/
GLFWAPI OSMesaContext glfwGetOSMesaContext(GLFWwindow* window);
#endif
#ifdef __cplusplus
}
#endif
#endif /* _glfw3_native_h_ */

315
example/src/main.c Normal file
View File

@@ -0,0 +1,315 @@
#define GLFW_INCLUDE_NONE
#include "GLFW/glfw3.h"
#include "flextGL.h"
#define SOKOL_IMPL
#define SOKOL_GLCORE33
#include "sokol_gfx.h"
#include "../../HandmadeMath.h"
// TODO: Remove this
#include <stdio.h>
typedef struct {
hmm_mat4 mvp;
hmm_vec4 color;
} uniforms_t;
typedef struct {
sg_bindings bindings;
int numVerts;
} model_t;
model_t triangle;
model_t cube;
model_t cylinder;
model_t initModel(sg_buffer vbuf, sg_buffer ibuf, size_t sizeOfIndices) {
model_t result = {
.bindings = (sg_bindings) {
.vertex_buffers[0] = vbuf,
.index_buffer = ibuf,
},
.numVerts = sizeOfIndices / sizeof(uint16_t),
};
return result;
}
#define NUM_CYLINDER_SIDES 32
void initModels() {
{
// triangle
const hmm_vec3 verts[] = {
HMM_Vec3( 0.0f, 0.5f, 0.0f), HMM_Vec3(0.0f, 0.0f, 1.0f),
HMM_Vec3( 0.5f, -0.5f, 0.0f), HMM_Vec3(0.0f, 0.0f, 1.0f),
HMM_Vec3(-0.5f, -0.5f, 0.0f), HMM_Vec3(0.0f, 0.0f, 1.0f),
};
const uint16_t indices[] = { 0, 1, 2 };
sg_buffer vbuf = sg_make_buffer(&(sg_buffer_desc){
.size = sizeof(verts),
.content = verts,
});
sg_buffer ibuf = sg_make_buffer(&(sg_buffer_desc){
.type = SG_BUFFERTYPE_INDEXBUFFER,
.size = sizeof(indices),
.content = indices,
});
triangle = initModel(vbuf, ibuf, sizeof(indices));
}
{
// cube
const hmm_vec3 verts[] = {
// front
HMM_Vec3(-0.5f, 0.5f, 0.5f), HMM_Vec3(0.0f, 0.0f, 1.0f),
HMM_Vec3(-0.5f, -0.5f, 0.5f), HMM_Vec3(0.0f, 0.0f, 1.0f),
HMM_Vec3( 0.5f, 0.5f, 0.5f), HMM_Vec3(0.0f, 0.0f, 1.0f),
HMM_Vec3( 0.5f, -0.5f, 0.5f), HMM_Vec3(0.0f, 0.0f, 1.0f),
// back
HMM_Vec3( 0.5f, 0.5f, -0.5f), HMM_Vec3(0.0f, 0.0f, -1.0f),
HMM_Vec3( 0.5f, -0.5f, -0.5f), HMM_Vec3(0.0f, 0.0f, -1.0f),
HMM_Vec3(-0.5f, 0.5f, -0.5f), HMM_Vec3(0.0f, 0.0f, -1.0f),
HMM_Vec3(-0.5f, -0.5f, -0.5f), HMM_Vec3(0.0f, 0.0f, -1.0f),
// left
HMM_Vec3(-0.5f, 0.5f, -0.5f), HMM_Vec3(-1.0f, 0.0f, 0.0f),
HMM_Vec3(-0.5f, -0.5f, -0.5f), HMM_Vec3(-1.0f, 0.0f, 0.0f),
HMM_Vec3(-0.5f, 0.5f, 0.5f), HMM_Vec3(-1.0f, 0.0f, 0.0f),
HMM_Vec3(-0.5f, -0.5f, 0.5f), HMM_Vec3(-1.0f, 0.0f, 0.0f),
// right
HMM_Vec3(0.5f, 0.5f, 0.5f), HMM_Vec3(1.0f, 0.0f, 0.0f),
HMM_Vec3(0.5f, -0.5f, 0.5f), HMM_Vec3(1.0f, 0.0f, 0.0f),
HMM_Vec3(0.5f, 0.5f, -0.5f), HMM_Vec3(1.0f, 0.0f, 0.0f),
HMM_Vec3(0.5f, -0.5f, -0.5f), HMM_Vec3(1.0f, 0.0f, 0.0f),
// top
HMM_Vec3(-0.5f, 0.5f, -0.5f), HMM_Vec3(0.0f, 1.0f, 0.0f),
HMM_Vec3(-0.5f, 0.5f, 0.5f), HMM_Vec3(0.0f, 1.0f, 0.0f),
HMM_Vec3( 0.5f, 0.5f, -0.5f), HMM_Vec3(0.0f, 1.0f, 0.0f),
HMM_Vec3( 0.5f, 0.5f, 0.5f), HMM_Vec3(0.0f, 1.0f, 0.0f),
// bottom
HMM_Vec3(-0.5f, -0.5f, 0.5f), HMM_Vec3(0.0f, -1.0f, 0.0f),
HMM_Vec3(-0.5f, -0.5f, -0.5f), HMM_Vec3(0.0f, -1.0f, 0.0f),
HMM_Vec3( 0.5f, -0.5f, 0.5f), HMM_Vec3(0.0f, -1.0f, 0.0f),
HMM_Vec3( 0.5f, -0.5f, -0.5f), HMM_Vec3(0.0f, -1.0f, 0.0f),
};
const uint16_t indices[] = {
0, 1, 2, 2, 1, 3,
4, 5, 6, 6, 5, 7,
8, 9, 10, 10, 9, 11,
12, 13, 14, 14, 13, 15,
16, 17, 18, 18, 17, 19,
20, 21, 22, 22, 21, 23,
};
sg_buffer vbuf = sg_make_buffer(&(sg_buffer_desc){
.size = sizeof(verts),
.content = verts,
});
sg_buffer ibuf = sg_make_buffer(&(sg_buffer_desc){
.type = SG_BUFFERTYPE_INDEXBUFFER,
.size = sizeof(indices),
.content = indices,
});
cube = initModel(vbuf, ibuf, sizeof(indices));
}
{
// cylinder
// the vertex order will be:
// top middle, bottom middle, top cap ring, bottom cap ring, top side ring, bottom side ring
hmm_vec3 verts[(2 * (1 + NUM_CYLINDER_SIDES) + (2 * NUM_CYLINDER_SIDES)) * 2];
verts[0] = HMM_Vec3(0.0f, 0.5f, 0.0f); verts[1] = HMM_Vec3(0.0f, 1.0f, 0.0f);
verts[2] = HMM_Vec3(0.0f, -0.5f, 0.0f); verts[3] = HMM_Vec3(0.0f, -1.0f, 0.0f);
const int baseVertIndexTopCapRing = 4; // middles are vert, normal, vert, normal
const int baseVertIndexBottomCapRing = baseVertIndexTopCapRing + (NUM_CYLINDER_SIDES * 2);
const int baseVertIndexTopSideRing = baseVertIndexBottomCapRing + (NUM_CYLINDER_SIDES * 2);
const int baseVertIndexBottomSideRing = baseVertIndexTopSideRing + (NUM_CYLINDER_SIDES * 2);
for (int i = 0; i < NUM_CYLINDER_SIDES; i++) {
float t = 2 * HMM_PI32 * (i / (float)NUM_CYLINDER_SIDES);
float x = HMM_CosF(t);
float z = HMM_SinF(t);
hmm_vec3 top = HMM_Vec3(x, 0.5f, z);
hmm_vec3 bottom = HMM_Vec3(x, -0.5f, z);
hmm_vec3 sideNormal = HMM_NormalizeVec3(HMM_Vec3(x, 0.0f, z));
verts[baseVertIndexTopCapRing + (2 * i) ] = top;
verts[baseVertIndexTopCapRing + (2 * i) + 1] = HMM_Vec3(0.0f, 1.0f, 0.0f);
verts[baseVertIndexBottomCapRing + (2 * i) ] = bottom;
verts[baseVertIndexBottomCapRing + (2 * i) + 1] = HMM_Vec3(0.0f, -1.0f, 0.0f);
verts[baseVertIndexTopSideRing + (2 * i) ] = top;
verts[baseVertIndexTopSideRing + (2 * i) + 1] = sideNormal;
verts[baseVertIndexBottomSideRing + (2 * i) ] = bottom;
verts[baseVertIndexBottomSideRing + (2 * i) + 1] = sideNormal;
}
uint16_t indices[3 * NUM_CYLINDER_SIDES + 3 * NUM_CYLINDER_SIDES + 3 * 2 * NUM_CYLINDER_SIDES];
// top cap
for (int i = 0; i < NUM_CYLINDER_SIDES; i++) {
indices[3 * i] = 0;
indices[3 * i + 1] = 2 + i;
indices[3 * i + 2] = 2 + ((i + 1) % NUM_CYLINDER_SIDES);
}
// bottom cap
const int bottomCapBaseIndex = 3 * NUM_CYLINDER_SIDES;
for (int i = 0; i < NUM_CYLINDER_SIDES; i++) {
indices[bottomCapBaseIndex + 3 * i] = 1;
indices[bottomCapBaseIndex + 3 * i + 1] = (2 + NUM_CYLINDER_SIDES) + ((i + 1) % NUM_CYLINDER_SIDES);
indices[bottomCapBaseIndex + 3 * i + 2] = (2 + NUM_CYLINDER_SIDES) + i;
}
const int sideBaseIndex = bottomCapBaseIndex + 3 * NUM_CYLINDER_SIDES;
const int topSideRingStart = 2 + NUM_CYLINDER_SIDES + NUM_CYLINDER_SIDES;
const int bottomSideRingStart = topSideRingStart + NUM_CYLINDER_SIDES;
for (int i = 0; i < NUM_CYLINDER_SIDES; i++) {
indices[sideBaseIndex + 6 * i] = topSideRingStart + i;
indices[sideBaseIndex + 6 * i + 1] = bottomSideRingStart + i;
indices[sideBaseIndex + 6 * i + 2] = topSideRingStart + ((i + 1) % NUM_CYLINDER_SIDES);
indices[sideBaseIndex + 6 * i + 3] = topSideRingStart + ((i + 1) % NUM_CYLINDER_SIDES);
indices[sideBaseIndex + 6 * i + 4] = bottomSideRingStart + i;
indices[sideBaseIndex + 6 * i + 5] = bottomSideRingStart + ((i + 1) % NUM_CYLINDER_SIDES);
}
sg_buffer vbuf = sg_make_buffer(&(sg_buffer_desc){
.size = sizeof(verts),
.content = verts,
});
sg_buffer ibuf = sg_make_buffer(&(sg_buffer_desc){
.type = SG_BUFFERTYPE_INDEXBUFFER,
.size = sizeof(indices),
.content = indices,
});
cylinder = initModel(vbuf, ibuf, sizeof(indices));
}
}
int main() {
/* create window and GL context via GLFW */
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
GLFWwindow* w = glfwCreateWindow(640, 480, "Sokol Triangle GLFW", 0, 0);
glfwMakeContextCurrent(w);
glfwSwapInterval(1);
flextInit(w);
/* setup sokol_gfx */
sg_setup(&(sg_desc){0});
initModels();
sg_layout_desc layout = {
.attrs = {
[0].format=SG_VERTEXFORMAT_FLOAT3,
[1].format=SG_VERTEXFORMAT_FLOAT3,
}
};
sg_shader shd = sg_make_shader(&(sg_shader_desc){
.vs = {
.source =
"#version 330\n"
"uniform mat4 mvp;"
"uniform vec4 color0;"
"layout(location=0) in vec4 position;\n"
"out vec4 color;\n"
"void main() {\n"
" gl_Position = mvp * position;\n"
" color = color0;\n"
"}\n",
.uniform_blocks[0] = {
.size = sizeof(uniforms_t),
.uniforms = {
[0] = { .name="mvp", .type=SG_UNIFORMTYPE_MAT4 },
[1] = { .name="color0", .type=SG_UNIFORMTYPE_FLOAT4 },
},
},
},
.fs.source =
"#version 330\n"
"in vec4 color;\n"
"out vec4 frag_color;\n"
"void main() {\n"
" frag_color = color;\n"
"}\n",
});
/* a pipeline state object (default render states are fine for triangle) */
sg_pipeline pip = sg_make_pipeline(&(sg_pipeline_desc){
.shader = shd,
.layout = layout,
.index_type = SG_INDEXTYPE_UINT16,
.depth_stencil = {
.depth_compare_func = SG_COMPAREFUNC_LESS_EQUAL,
.depth_write_enabled = true,
},
.rasterizer = (sg_rasterizer_state) {
.cull_mode = SG_CULLMODE_BACK,
.face_winding = SG_FACEWINDING_CCW,
},
});
/* default pass action (clear to grey) */
sg_pass_action pass_action = {0};
double previousTime = glfwGetTime();
/* draw loop */
while (!glfwWindowShouldClose(w)) {
double time = glfwGetTime();
double deltaTime = time - previousTime;
double previousTime = time;
int cur_width, cur_height;
glfwGetFramebufferSize(w, &cur_width, &cur_height);
sg_begin_default_pass(&pass_action, cur_width, cur_height);
for (int j = 0; j < 3; j++) {
for (int i = 0; i < 3; i++) {
model_t model = cylinder;
hmm_mat4 scale = HMM_Scale(HMM_Vec3(0.25f, 0.5f, 0.25f));
hmm_mat4 rotateZ = HMM_Rotate(HMM_PI32 * i, HMM_Vec3(0.0f, 0.0f, 1.0f));
hmm_mat4 rotateY = HMM_Rotate(HMM_PI32 / 2.0f * time, HMM_Vec3(0.0f, 1.0f, 0.0f));
hmm_mat4 rotate = HMM_MultiplyMat4(rotateY, rotateZ);
hmm_mat4 translate = HMM_Translate(HMM_Vec3(-0.5f + 0.5f * i, -0.5f + 0.5f * j, 0.0f));
hmm_mat4 m = HMM_MultiplyMat4(translate, HMM_MultiplyMat4(rotate, scale));
hmm_mat4 v = HMM_Translate(HMM_Vec3(0.0f, 0.0f, -1.0f));
hmm_mat4 p = HMM_Perspective(HMM_PI32 / 2.0f, 1.0f, 0.01f, 10.0f);
uniforms_t uniforms = {
.mvp = HMM_MultiplyMat4(p, HMM_MultiplyMat4(v, m)),
.color = HMM_Vec4((i + j) + 1 & 2, ((i + j) % 2) == 1, ((i + j) % 4) == 2, 1.0f),
};
sg_apply_pipeline(pip);
sg_apply_bindings(&model.bindings);
sg_apply_uniforms(SG_SHADERSTAGE_VS, 0, &uniforms, sizeof(uniforms));
sg_draw(0, model.numVerts, 1);
}
}
sg_end_pass();
sg_commit();
glfwSwapBuffers(w);
glfwPollEvents();
}
/* cleanup */
sg_shutdown();
glfwTerminate();
return 0;
}

11943
example/src/sokol_gfx.h Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,5 @@
#ifndef WITHOUT_COVERAGE
#include "HandmadeTest.h"
#endif
#define HANDMADE_MATH_IMPLEMENTATION
#define HANDMADE_MATH_NO_INLINE
#include "../HandmadeMath.h"

View File

@@ -1,5 +1,2 @@
#define HANDMADE_MATH_IMPLEMENTATION
#define HANDMADE_MATH_CPP_MODE
#define HANDMADE_MATH_NO_INLINE
#include "../HandmadeMath.h"
#include "HandmadeMath.c"
// C++ compilers complain when compiling a .c file...

View File

@@ -0,0 +1,12 @@
#define HMM_PREFIX(name) WOW_##name
#define HANDMADE_MATH_IMPLEMENTATION
#define HANDMADE_MATH_NO_INLINE
#include "../HandmadeMath.h"
int main() {
hmm_vec4 a = WOW_Vec4(1, 2, 3, 4);
hmm_vec4 b = WOW_Vec4(5, 6, 7, 8);
WOW_Add(a, b);
}

View File

@@ -1,5 +0,0 @@
#define HANDMADE_MATH_IMPLEMENTATION
#define HANDMADE_MATH_NO_SSE
#define HANDMADE_MATH_NO_INLINE
#include "../HandmadeMath.h"

View File

@@ -1,6 +0,0 @@
#define HANDMADE_MATH_IMPLEMENTATION
#define HANDMADE_MATH_CPP_MODE
#define HANDMADE_MATH_NO_SSE
#define HANDMADE_MATH_NO_INLINE
#include "../HandmadeMath.h"

View File

@@ -1,80 +1,398 @@
/*
HandmadeTest.h
This is Handmade Math's test framework. It is fully compatible with both C
and C++, although it requires some compiler-specific features.
To use Handmade Test, you must #define HANDMADE_TEST_IMPLEMENTATION in
exactly one C or C++ file that includes the header, like this:
#define HANDMADE_TEST_IMPLEMENTATION
#include "HandmadeTest.h"
The basic way of creating a test is using the TEST macro, which registers a
single test to be run:
TEST(MyCategory, MyTestName) {
// test code, including asserts/expects
}
Handmade Test also provides macros you can use to check the coverage of
important parts of your code. Define a coverage case by using the COVERAGE
macro outside the function you wish to test, providing both a name and the
number of asserts you expect to see covered over the course of your test.
Then use the ASSERT_COVERED macro in every part of the function you wish to
check coverage on. For example:
COVERAGE(MyCoverageCase, 3)
void MyFunction(int a, int b) {
if (a > b) {
ASSERT_COVERED(MyCoverageCase);
return 10;
} else if (a < b) {
ASSERT_COVERED(MyCoverageCase);
return -10;
}
ASSERT_COVERED(MyCoverageCase);
return 0;
}
The main function of your test code should then call hmt_run_all_tests (and
optionally hmt_check_all_coverage) and return the result:
int main() {
return hmt_run_all_tests() || hmt_check_all_coverage();
}
=============================================================================
If Handmade Test's macros are conflicting with existing macros in your
project, you may define HMT_SAFE_MACROS before you include HandmadeTest.h.
You may then prefix each macro with HMT_. For example, you may use HMT_TEST
instead of TEST and HMT_EXPECT_TRUE instead of EXPECT_TRUE.
*/
#ifndef HANDMADETEST_H
#define HANDMADETEST_H
#include <float.h>
#include <stdio.h>
#include <string.h>
int hmt_count_tests = 0;
int hmt_count_failedtests = 0;
int hmt_count_failures = 0;
#include "initializer.h"
#define RESET "\033[0m"
#define RED "\033[31m"
#define GREEN "\033[32m"
#define HMT_RESET "\033[0m"
#define HMT_RED "\033[31m"
#define HMT_GREEN "\033[32m"
#define CATEGORY_BEGIN(name) { \
int count_categorytests = 0; \
int count_categoryfailedtests = 0; \
int count_categoryfailures = 0; \
printf("\n" #name ":\n");
#define CATEGORY_END(name) \
hmt_count_tests += count_categorytests; \
hmt_count_failedtests += count_categoryfailedtests; \
hmt_count_failures += count_categoryfailures; \
printf("%d/%d tests passed, %d failures\n", count_categorytests - count_categoryfailedtests, count_categorytests, count_categoryfailures); \
}
#define HMT_ARRAY_SIZE 1024
#define TEST_BEGIN(name) { \
int count_testfailures = 0; \
count_categorytests++; \
printf(" " #name ":");
#define TEST_END() \
count_categoryfailures += count_testfailures; \
if (count_testfailures > 0) { \
count_categoryfailedtests++; \
printf("\n"); \
} else { \
printf(GREEN " [PASS]\n" RESET); \
} \
}
typedef struct hmt_testresult_struct {
int count_cases;
int count_failures;
} hmt_testresult;
#define CASE_FAIL() \
count_testfailures++; \
printf("\n - " RED "[FAIL] (%d) " RESET, __LINE__)
typedef void (*hmt_test_func)(hmt_testresult*);
typedef struct hmt_test_struct {
const char* name;
hmt_test_func func;
} hmt_test;
typedef struct hmt_category_struct {
const char* name;
int num_tests;
hmt_test* tests;
} hmt_category;
typedef struct hmt_covercase_struct {
const char* name;
int expected_asserts;
int actual_asserts;
int* asserted_lines;
} hmt_covercase;
hmt_category _hmt_new_category(const char* name);
hmt_test _hmt_new_test(const char* name, hmt_test_func func);
hmt_covercase _hmt_new_covercase(const char* name, int expected);
void _hmt_register_test(const char* category, const char* name, hmt_test_func func);
void _hmt_register_covercase(const char* name, const char* expected_asserts);
void _hmt_count_cover(const char* name, int line);
#define _HMT_TEST_FUNCNAME(category, name) _hmt_test_ ## category ## _ ## name
#define _HMT_TEST_FUNCNAME_INIT(category, name) _hmt_test_ ## category ## _ ## name ## _init
#define _HMT_COVERCASE_FUNCNAME_INIT(name) _hmt_covercase_ ## name ## _init
#define HMT_TEST(category, name) \
void _HMT_TEST_FUNCNAME(category, name)(hmt_testresult* _result); \
INITIALIZER(_HMT_TEST_FUNCNAME_INIT(category, name)) { \
_hmt_register_test(#category, #name, _HMT_TEST_FUNCNAME(category, name)); \
} \
void _HMT_TEST_FUNCNAME(category, name)(hmt_testresult* _result)
#define _HMT_CASE_START() \
_result->count_cases++;
#define _HMT_CASE_FAIL() \
_result->count_failures++; \
printf("\n - " HMT_RED "[FAIL] (line %d) " HMT_RESET, __LINE__);
#define HMT_COVERAGE(name, num_asserts) \
INITIALIZER(_HMT_COVERCASE_FUNCNAME_INIT(name)) { \
_hmt_register_covercase(#name, #num_asserts); \
} \
#define HMT_ASSERT_COVERED(name) \
{ \
_hmt_count_cover(#name, __LINE__); \
} \
/*
* Asserts and expects
*/
#define EXPECT_FLOAT_EQ(_actual, _expected) do { \
#define HMT_EXPECT_TRUE(_actual) { \
_HMT_CASE_START(); \
if (!(_actual)) { \
_HMT_CASE_FAIL(); \
printf("Expected true but got something false"); \
} \
} \
#define HMT_EXPECT_FALSE(_actual) { \
_HMT_CASE_START(); \
if (_actual) { \
_HMT_CASE_FAIL(); \
printf("Expected false but got something true"); \
} \
} \
#define HMT_EXPECT_FLOAT_EQ(_actual, _expected) { \
_HMT_CASE_START(); \
float actual = (_actual); \
float diff = actual - (_expected); \
if (diff < -FLT_EPSILON || FLT_EPSILON < diff) { \
CASE_FAIL(); \
_HMT_CASE_FAIL(); \
printf("Expected %f, got %f", (_expected), actual); \
} \
} while (0)
} \
#define EXPECT_NEAR(_actual, _expected, _epsilon) do { \
#define HMT_EXPECT_NEAR(_actual, _expected, _epsilon) { \
_HMT_CASE_START(); \
float actual = (_actual); \
float diff = actual - (_expected); \
if (diff < -(_epsilon) || (_epsilon) < diff) { \
CASE_FAIL(); \
_HMT_CASE_FAIL(); \
printf("Expected %f, got %f", (_expected), actual); \
} \
} while (0)
} \
#define EXPECT_LT(_actual, _expected) do { \
#define HMT_EXPECT_LT(_actual, _expected) { \
_HMT_CASE_START(); \
if ((_actual) >= (_expected)) { \
CASE_FAIL(); \
_HMT_CASE_FAIL(); \
printf("Expected %f to be less than %f", (_actual), (_expected)); \
} \
} while (0)
} \
#define EXPECT_GT(_actual, _expected) do { \
#define HMT_EXPECT_GT(_actual, _expected) { \
_HMT_CASE_START(); \
if ((_actual) <= (_expected)) { \
CASE_FAIL(); \
_HMT_CASE_FAIL(); \
printf("Expected %f to be greater than %f", (_actual), (_expected)); \
} \
} while (0)
} \
#endif
#ifndef HMT_SAFE_MACROS
// Friendly defines
#define TEST(category, name) HMT_TEST(category, name)
#define COVERAGE(name, expected_asserts) HMT_COVERAGE(name, expected_asserts)
#define ASSERT_COVERED(name) HMT_ASSERT_COVERED(name)
#define EXPECT_TRUE(_actual) HMT_EXPECT_TRUE(_actual)
#define EXPECT_FALSE(_actual) HMT_EXPECT_FALSE(_actual)
#define EXPECT_FLOAT_EQ(_actual, _expected) HMT_EXPECT_FLOAT_EQ(_actual, _expected)
#define EXPECT_NEAR(_actual, _expected, _epsilon) HMT_EXPECT_NEAR(_actual, _expected, _epsilon)
#define EXPECT_LT(_actual, _expected) HMT_EXPECT_LT(_actual, _expected)
#define EXPECT_GT(_actual, _expected) HMT_EXPECT_GT(_actual, _expected)
#endif // HMT_SAFE_MACROS
#endif // HANDMADETEST_H
#ifdef HANDMADE_TEST_IMPLEMENTATION
#ifndef HANDMADE_TEST_IMPLEMENTATION_GUARD
#define HANDMADE_TEST_IMPLEMENTATION_GUARD
int _hmt_num_categories = 0;
hmt_category* _hmt_categories = 0;
int _hmt_num_covercases = 0;
hmt_covercase* _hmt_covercases = 0;
hmt_category _hmt_new_category(const char* name) {
hmt_category cat = {
name, // name
0, // num_tests
(hmt_test*) malloc(HMT_ARRAY_SIZE * sizeof(hmt_test)), // tests
};
return cat;
}
hmt_test _hmt_new_test(const char* name, hmt_test_func func) {
hmt_test test = {
name, // name
func, // func
};
return test;
}
hmt_covercase _hmt_new_covercase(const char* name, int expected) {
hmt_covercase covercase = {
name, // name
expected, // expected_asserts
0, // actual_asserts
(int*) malloc(HMT_ARRAY_SIZE * sizeof(int)), // asserted_lines
};
return covercase;
}
void _hmt_register_test(const char* category, const char* name, hmt_test_func func) {
// initialize categories array if not initialized
if (!_hmt_categories) {
_hmt_categories = (hmt_category*) malloc(HMT_ARRAY_SIZE * sizeof(hmt_category));
}
// Find the matching category, if possible
int cat_index;
for (cat_index = 0; cat_index < _hmt_num_categories; cat_index++) {
if (strcmp(_hmt_categories[cat_index].name, category) == 0) {
break;
}
}
// Add a new category if necessary
if (cat_index >= _hmt_num_categories) {
_hmt_categories[cat_index] = _hmt_new_category(category);
_hmt_num_categories++;
}
hmt_category* cat = &_hmt_categories[cat_index];
// Add the test to the category
cat->tests[cat->num_tests] = _hmt_new_test(name, func);
cat->num_tests++;
}
void _hmt_register_covercase(const char* name, const char* expected_asserts) {
// initialize cases array if not initialized
if (!_hmt_covercases) {
_hmt_covercases = (hmt_covercase*) malloc(HMT_ARRAY_SIZE * sizeof(hmt_covercase));
}
// check for existing case with that name, because the macro can run multiple
// times in different translation units
for (int i = 0; i < _hmt_num_covercases; i++) {
if (strcmp(_hmt_covercases[i].name, name) == 0) {
return;
}
}
_hmt_covercases[_hmt_num_covercases] = _hmt_new_covercase(name, atoi(expected_asserts));
_hmt_num_covercases++;
}
hmt_covercase* _hmt_find_covercase(const char* name) {
for (int i = 0; i < _hmt_num_covercases; i++) {
if (strcmp(_hmt_covercases[i].name, name) == 0) {
return &_hmt_covercases[i];
}
}
return 0;
}
void _hmt_count_cover(const char* name, int line) {
hmt_covercase* covercase = _hmt_find_covercase(name);
if (covercase == 0) {
printf(HMT_RED "ERROR (line %d): Could not find coverage case with name \"%s\".\n" HMT_RESET, line, name);
return;
}
// see if this line has already been covered
for (int i = 0; i < covercase->actual_asserts; i++) {
if (covercase->asserted_lines[i] == line) {
return;
}
}
covercase->asserted_lines[covercase->actual_asserts] = line;
covercase->actual_asserts++;
}
int hmt_run_all_tests() {
int count_alltests = 0;
int count_allfailedtests = 0; // failed test cases
int count_allfailures = 0; // failed asserts
for (int i = 0; i < _hmt_num_categories; i++) {
hmt_category cat = _hmt_categories[i];
int count_catfailedtests = 0;
int count_catfailures = 0;
printf("\n%s:\n", cat.name);
for (int j = 0; j < cat.num_tests; j++) {
hmt_test test = cat.tests[j];
printf(" %s:", test.name);
hmt_testresult result = {
0, // count_cases
0, // count_failures
};
test.func(&result);
count_catfailures += result.count_failures;
if (result.count_failures > 0) {
count_catfailedtests++;
printf("\n " HMT_RED "(%d/%d passed)" HMT_RESET, result.count_cases - result.count_failures, result.count_cases);
printf("\n");
} else {
printf(HMT_GREEN " [PASS] (%d/%d passed) \n" HMT_RESET, result.count_cases - result.count_failures, result.count_cases);
}
}
count_alltests += cat.num_tests;
count_allfailedtests += count_catfailedtests;
count_allfailures += count_catfailures;
printf("%d/%d tests passed, %d failures\n", cat.num_tests - count_catfailedtests, cat.num_tests, count_catfailures);
}
if (count_allfailedtests > 0) {
printf(HMT_RED);
} else {
printf(HMT_GREEN);
}
printf("\n%d/%d tests passed overall, %d failures\n" HMT_RESET, count_alltests - count_allfailedtests, count_alltests, count_allfailures);
printf("\n");
return (count_allfailedtests > 0);
}
int hmt_check_all_coverage() {
printf("Coverage:\n");
int count_failures = 0;
for (int i = 0; i < _hmt_num_covercases; i++) {
hmt_covercase covercase = _hmt_covercases[i];
if (covercase.expected_asserts != covercase.actual_asserts) {
count_failures++;
printf("%s: " HMT_RED "FAIL (expected %d asserts, got %d)\n" HMT_RESET, covercase.name, covercase.expected_asserts, covercase.actual_asserts);
}
}
if (count_failures > 0) {
printf("\n");
printf(HMT_RED);
} else {
printf(HMT_GREEN);
}
printf("%d coverage cases tested, %d failures\n", _hmt_num_covercases, count_failures);
printf(HMT_RESET);
printf("\n");
return (count_failures > 0);
}
#endif // HANDMADE_TEST_IMPLEMENTATION_GUARD
#endif // HANDMADE_TEST_IMPLEMENTATION

View File

@@ -1,24 +1,85 @@
ROOT_DIR = ..
BUILD_DIR=./build
CXXFLAGS += -g -Wall -Wextra -pthread -Wno-missing-braces -Wno-missing-field-initializers
CXXFLAGS+=-g -Wall -Wextra -pthread -Wno-missing-braces -Wno-missing-field-initializers -Wfloat-equal
all: c c_no_sse cpp cpp_no_sse
all: c c_no_sse cpp cpp_no_sse build_c_without_coverage build_cpp_without_coverage build_cpp_different_prefix
build_all: build_c build_c_no_sse build_cpp build_cpp_no_sse
clean:
rm -f hmm_test_c hmm_test_cpp hmm_test_c_no_sse hmm_test_cpp_no_sse *.o
rm -rf $(BUILD_DIR)
c: $(ROOT_DIR)/test/HandmadeMath.c test_impl
$(CC) $(CPPFLAGS) $(CXXFLAGS) -std=c99 -c $(ROOT_DIR)/test/HandmadeMath.c $(ROOT_DIR)/test/hmm_test.c -lm
$(CC) -ohmm_test_c HandmadeMath.o hmm_test.o -lm
c: build_c
$(BUILD_DIR)/hmm_test_c
c_no_sse: $(ROOT_DIR)/test/HandmadeMath_NoSSE.c test_impl
$(CC) $(CPPFLAGS) $(CXXFLAGS) -std=c99 -c $(ROOT_DIR)/test/HandmadeMath_NoSSE.c $(ROOT_DIR)/test/hmm_test.c -lm
$(CC) -ohmm_test_c_no_sse HandmadeMath_NoSSE.o hmm_test.o -lm
build_c: HandmadeMath.c test_impl
@echo "\nCompiling in C mode"
mkdir -p $(BUILD_DIR)
cd $(BUILD_DIR)\
&& $(CC) $(CPPFLAGS) $(CXXFLAGS) -std=c99 \
-c ../HandmadeMath.c ../hmm_test.c \
-lm \
&& $(CC) -ohmm_test_c HandmadeMath.o hmm_test.o -lm
cpp: $(ROOT_DIR)/test/HandmadeMath.cpp test_impl
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -ohmm_test_cpp $(ROOT_DIR)/test/HandmadeMath.cpp $(ROOT_DIR)/test/hmm_test.cpp
c_no_sse: build_c_no_sse
$(BUILD_DIR)/hmm_test_c_no_sse
cpp_no_sse: $(ROOT_DIR)/test/HandmadeMath_NoSSE.cpp test_impl
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -ohmm_test_cpp_no_sse $(ROOT_DIR)/test/HandmadeMath_NoSSE.cpp $(ROOT_DIR)/test/hmm_test.cpp
build_c_no_sse: HandmadeMath.c test_impl
@echo "\nCompiling in C mode (no SSE)"
mkdir -p $(BUILD_DIR)
cd $(BUILD_DIR) \
&& $(CC) $(CPPFLAGS) $(CXXFLAGS) -std=c99 \
-DHANDMADE_MATH_NO_SSE \
-c ../HandmadeMath.c ../hmm_test.c \
-lm \
&& $(CC) -ohmm_test_c_no_sse HandmadeMath.o hmm_test.o -lm
test_impl: $(ROOT_DIR)/test/hmm_test.cpp $(ROOT_DIR)/test/hmm_test.c
cpp: build_cpp
$(BUILD_DIR)/hmm_test_cpp
build_cpp: HandmadeMath.cpp test_impl
@echo "\nCompiling in C++ mode"
mkdir -p $(BUILD_DIR)
cd $(BUILD_DIR) \
&& $(CXX) $(CPPFLAGS) $(CXXFLAGS) -ohmm_test_cpp \
-DHANDMADE_MATH_CPP_MODE \
../HandmadeMath.cpp ../hmm_test.cpp
cpp_no_sse: build_cpp_no_sse
$(BUILD_DIR)/hmm_test_cpp_no_sse
build_cpp_no_sse: HandmadeMath.cpp test_impl
@echo "\nCompiling in C++ mode (no SSE)"
mkdir -p $(BUILD_DIR)
cd $(BUILD_DIR) \
&& $(CXX) $(CPPFLAGS) $(CXXFLAGS) -ohmm_test_cpp_no_sse \
-DHANDMADE_MATH_CPP_MODE -DHANDMADE_MATH_NO_SSE \
../HandmadeMath.cpp ../hmm_test.cpp
test_impl: hmm_test.cpp hmm_test.c
build_c_without_coverage: HandmadeMath.c test_impl
@echo "\nCompiling in C mode"
mkdir -p $(BUILD_DIR)
cd $(BUILD_DIR)\
&& $(CC) $(CPPFLAGS) $(CXXFLAGS) -std=c99 \
-DWITHOUT_COVERAGE \
-c ../HandmadeMath.c ../hmm_test.c \
-lm \
&& $(CC) -ohmm_test_c HandmadeMath.o hmm_test.o -lm
build_cpp_without_coverage: HandmadeMath.cpp test_impl
@echo "\nCompiling in C++ mode (no SSE)"
mkdir -p $(BUILD_DIR)
cd $(BUILD_DIR) \
&& $(CXX) $(CPPFLAGS) $(CXXFLAGS) -ohmm_test_cpp_no_sse \
-DHANDMADE_MATH_CPP_MODE -DWITHOUT_COVERAGE \
../HandmadeMath.cpp ../hmm_test.cpp
build_cpp_different_prefix: HandmadeMath.cpp
@echo "\nCompiling C++ with different prefix"
mkdir -p $(BUILD_DIR)
cd $(BUILD_DIR) \
&& $(CXX) $(CPPFLAGS) $(CXXFLAGS) -ohmm_test_cpp_different_prefix \
-DHANDMADE_MATH_CPP_MODE -DDIFFERENT_PREFIX \
../HandmadeMathDifferentPrefix.cpp

16
test/README.md Normal file
View File

@@ -0,0 +1,16 @@
# Testing
You can compile and run the tests yourself by running:
```
make
```
To run a specific test configuration, run one of:
```
make c
make c_no_sse
make cpp
make cpp_no_sse
```

209
test/categories/Addition.h Normal file
View File

@@ -0,0 +1,209 @@
#include "../HandmadeTest.h"
TEST(Addition, Vec2)
{
hmm_vec2 v2_1 = HMM_Vec2(1.0f, 2.0f);
hmm_vec2 v2_2 = HMM_Vec2(3.0f, 4.0f);
{
hmm_vec2 result = HMM_AddVec2(v2_1, v2_2);
EXPECT_FLOAT_EQ(result.X, 4.0f);
EXPECT_FLOAT_EQ(result.Y, 6.0f);
}
#ifdef __cplusplus
{
hmm_vec2 result = HMM_Add(v2_1, v2_2);
EXPECT_FLOAT_EQ(result.X, 4.0f);
EXPECT_FLOAT_EQ(result.Y, 6.0f);
}
{
hmm_vec2 result = v2_1 + v2_2;
EXPECT_FLOAT_EQ(result.X, 4.0f);
EXPECT_FLOAT_EQ(result.Y, 6.0f);
}
v2_1 += v2_2;
EXPECT_FLOAT_EQ(v2_1.X, 4.0f);
EXPECT_FLOAT_EQ(v2_1.Y, 6.0f);
#endif
}
TEST(Addition, Vec3)
{
hmm_vec3 v3_1 = HMM_Vec3(1.0f, 2.0f, 3.0f);
hmm_vec3 v3_2 = HMM_Vec3(4.0f, 5.0f, 6.0f);
{
hmm_vec3 result = HMM_AddVec3(v3_1, v3_2);
EXPECT_FLOAT_EQ(result.X, 5.0f);
EXPECT_FLOAT_EQ(result.Y, 7.0f);
EXPECT_FLOAT_EQ(result.Z, 9.0f);
}
#ifdef __cplusplus
{
hmm_vec3 result = HMM_Add(v3_1, v3_2);
EXPECT_FLOAT_EQ(result.X, 5.0f);
EXPECT_FLOAT_EQ(result.Y, 7.0f);
EXPECT_FLOAT_EQ(result.Z, 9.0f);
}
{
hmm_vec3 result = v3_1 + v3_2;
EXPECT_FLOAT_EQ(result.X, 5.0f);
EXPECT_FLOAT_EQ(result.Y, 7.0f);
EXPECT_FLOAT_EQ(result.Z, 9.0f);
}
v3_1 += v3_2;
EXPECT_FLOAT_EQ(v3_1.X, 5.0f);
EXPECT_FLOAT_EQ(v3_1.Y, 7.0f);
EXPECT_FLOAT_EQ(v3_1.Z, 9.0f);
#endif
}
TEST(Addition, Vec4)
{
hmm_vec4 v4_1 = HMM_Vec4(1.0f, 2.0f, 3.0f, 4.0f);
hmm_vec4 v4_2 = HMM_Vec4(5.0f, 6.0f, 7.0f, 8.0f);
{
hmm_vec4 result = HMM_AddVec4(v4_1, v4_2);
EXPECT_FLOAT_EQ(result.X, 6.0f);
EXPECT_FLOAT_EQ(result.Y, 8.0f);
EXPECT_FLOAT_EQ(result.Z, 10.0f);
EXPECT_FLOAT_EQ(result.W, 12.0f);
}
#ifdef __cplusplus
{
hmm_vec4 result = HMM_Add(v4_1, v4_2);
EXPECT_FLOAT_EQ(result.X, 6.0f);
EXPECT_FLOAT_EQ(result.Y, 8.0f);
EXPECT_FLOAT_EQ(result.Z, 10.0f);
EXPECT_FLOAT_EQ(result.W, 12.0f);
}
{
hmm_vec4 result = v4_1 + v4_2;
EXPECT_FLOAT_EQ(result.X, 6.0f);
EXPECT_FLOAT_EQ(result.Y, 8.0f);
EXPECT_FLOAT_EQ(result.Z, 10.0f);
EXPECT_FLOAT_EQ(result.W, 12.0f);
}
v4_1 += v4_2;
EXPECT_FLOAT_EQ(v4_1.X, 6.0f);
EXPECT_FLOAT_EQ(v4_1.Y, 8.0f);
EXPECT_FLOAT_EQ(v4_1.Z, 10.0f);
EXPECT_FLOAT_EQ(v4_1.W, 12.0f);
#endif
}
TEST(Addition, Mat4)
{
hmm_mat4 m4_1 = HMM_Mat4(); // will have 1 - 16
hmm_mat4 m4_2 = HMM_Mat4(); // will have 17 - 32
// Fill the matrices
int Counter = 1;
for (int Column = 0; Column < 4; ++Column)
{
for (int Row = 0; Row < 4; ++Row)
{
m4_1.Elements[Column][Row] = Counter;
++Counter;
}
}
for (int Column = 0; Column < 4; ++Column)
{
for (int Row = 0; Row < 4; ++Row)
{
m4_2.Elements[Column][Row] = Counter;
++Counter;
}
}
// Test the results
{
hmm_mat4 result = HMM_AddMat4(m4_1, m4_2);
float Expected = 18.0f;
for (int Column = 0; Column < 4; ++Column)
{
for (int Row = 0; Row < 4; ++Row)
{
EXPECT_FLOAT_EQ(result.Elements[Column][Row], Expected);
Expected += 2.0f;
}
}
}
#ifdef __cplusplus
{
hmm_mat4 result = HMM_Add(m4_1, m4_2);
float Expected = 18.0f;
for (int Column = 0; Column < 4; ++Column)
{
for (int Row = 0; Row < 4; ++Row)
{
EXPECT_FLOAT_EQ(result.Elements[Column][Row], Expected);
Expected += 2.0f;
}
}
}
{
hmm_mat4 result = m4_1 + m4_2;
float Expected = 18.0f;
for (int Column = 0; Column < 4; ++Column)
{
for (int Row = 0; Row < 4; ++Row)
{
EXPECT_FLOAT_EQ(result.Elements[Column][Row], Expected);
Expected += 2.0f;
}
}
}
m4_1 += m4_2;
float Expected = 18.0f;
for (int Column = 0; Column < 4; ++Column)
{
for (int Row = 0; Row < 4; ++Row)
{
EXPECT_FLOAT_EQ(m4_1.Elements[Column][Row], Expected);
Expected += 2.0f;
}
}
#endif
}
TEST(Addition, Quaternion)
{
hmm_quaternion q1 = HMM_Quaternion(1.0f, 2.0f, 3.0f, 4.0f);
hmm_quaternion q2 = HMM_Quaternion(5.0f, 6.0f, 7.0f, 8.0f);
{
hmm_quaternion result = HMM_AddQuaternion(q1, q2);
EXPECT_FLOAT_EQ(result.X, 6.0f);
EXPECT_FLOAT_EQ(result.Y, 8.0f);
EXPECT_FLOAT_EQ(result.Z, 10.0f);
EXPECT_FLOAT_EQ(result.W, 12.0f);
}
#ifdef __cplusplus
{
hmm_quaternion result = HMM_Add(q1, q2);
EXPECT_FLOAT_EQ(result.X, 6.0f);
EXPECT_FLOAT_EQ(result.Y, 8.0f);
EXPECT_FLOAT_EQ(result.Z, 10.0f);
EXPECT_FLOAT_EQ(result.W, 12.0f);
}
{
hmm_quaternion result = q1 + q2;
EXPECT_FLOAT_EQ(result.X, 6.0f);
EXPECT_FLOAT_EQ(result.Y, 8.0f);
EXPECT_FLOAT_EQ(result.Z, 10.0f);
EXPECT_FLOAT_EQ(result.W, 12.0f);
}
q1 += q2;
EXPECT_FLOAT_EQ(q1.X, 6.0f);
EXPECT_FLOAT_EQ(q1.Y, 8.0f);
EXPECT_FLOAT_EQ(q1.Z, 10.0f);
EXPECT_FLOAT_EQ(q1.W, 12.0f);
#endif
}

325
test/categories/Division.h Normal file
View File

@@ -0,0 +1,325 @@
#include "../HandmadeTest.h"
TEST(Division, Vec2Vec2)
{
hmm_vec2 v2_1 = HMM_Vec2(1.0f, 3.0f);
hmm_vec2 v2_2 = HMM_Vec2(2.0f, 4.0f);
{
hmm_vec2 result = HMM_DivideVec2(v2_1, v2_2);
EXPECT_FLOAT_EQ(result.X, 0.5f);
EXPECT_FLOAT_EQ(result.Y, 0.75f);
}
#ifdef __cplusplus
{
hmm_vec2 result = HMM_Divide(v2_1, v2_2);
EXPECT_FLOAT_EQ(result.X, 0.5f);
EXPECT_FLOAT_EQ(result.Y, 0.75f);
}
{
hmm_vec2 result = v2_1 / v2_2;
EXPECT_FLOAT_EQ(result.X, 0.5f);
EXPECT_FLOAT_EQ(result.Y, 0.75f);
}
v2_1 /= v2_2;
EXPECT_FLOAT_EQ(v2_1.X, 0.5f);
EXPECT_FLOAT_EQ(v2_1.Y, 0.75f);
#endif
}
TEST(Division, Vec2Scalar)
{
hmm_vec2 v2 = HMM_Vec2(1.0f, 2.0f);
float s = 2;
{
hmm_vec2 result = HMM_DivideVec2f(v2, s);
EXPECT_FLOAT_EQ(result.X, 0.5f);
EXPECT_FLOAT_EQ(result.Y, 1.0f);
}
#ifdef __cplusplus
{
hmm_vec2 result = HMM_Divide(v2, s);
EXPECT_FLOAT_EQ(result.X, 0.5f);
EXPECT_FLOAT_EQ(result.Y, 1.0f);
}
{
hmm_vec2 result = v2 / s;
EXPECT_FLOAT_EQ(result.X, 0.5f);
EXPECT_FLOAT_EQ(result.Y, 1.0f);
}
v2 /= s;
EXPECT_FLOAT_EQ(v2.X, 0.5f);
EXPECT_FLOAT_EQ(v2.Y, 1.0f);
#endif
}
TEST(Division, Vec3Vec3)
{
hmm_vec3 v3_1 = HMM_Vec3(1.0f, 3.0f, 5.0f);
hmm_vec3 v3_2 = HMM_Vec3(2.0f, 4.0f, 0.5f);
{
hmm_vec3 result = HMM_DivideVec3(v3_1, v3_2);
EXPECT_FLOAT_EQ(result.X, 0.5f);
EXPECT_FLOAT_EQ(result.Y, 0.75f);
EXPECT_FLOAT_EQ(result.Z, 10.0f);
}
#ifdef __cplusplus
{
hmm_vec3 result = HMM_Divide(v3_1, v3_2);
EXPECT_FLOAT_EQ(result.X, 0.5f);
EXPECT_FLOAT_EQ(result.Y, 0.75f);
EXPECT_FLOAT_EQ(result.Z, 10.0f);
}
{
hmm_vec3 result = v3_1 / v3_2;
EXPECT_FLOAT_EQ(result.X, 0.5f);
EXPECT_FLOAT_EQ(result.Y, 0.75f);
EXPECT_FLOAT_EQ(result.Z, 10.0f);
}
v3_1 /= v3_2;
EXPECT_FLOAT_EQ(v3_1.X, 0.5f);
EXPECT_FLOAT_EQ(v3_1.Y, 0.75f);
EXPECT_FLOAT_EQ(v3_1.Z, 10.0f);
#endif
}
TEST(Division, Vec3Scalar)
{
hmm_vec3 v3 = HMM_Vec3(1.0f, 2.0f, 3.0f);
float s = 2;
{
hmm_vec3 result = HMM_DivideVec3f(v3, s);
EXPECT_FLOAT_EQ(result.X, 0.5f);
EXPECT_FLOAT_EQ(result.Y, 1.0f);
EXPECT_FLOAT_EQ(result.Z, 1.5f);
}
#ifdef __cplusplus
{
hmm_vec3 result = HMM_Divide(v3, s);
EXPECT_FLOAT_EQ(result.X, 0.5f);
EXPECT_FLOAT_EQ(result.Y, 1.0f);
EXPECT_FLOAT_EQ(result.Z, 1.5f);
}
{
hmm_vec3 result = v3 / s;
EXPECT_FLOAT_EQ(result.X, 0.5f);
EXPECT_FLOAT_EQ(result.Y, 1.0f);
EXPECT_FLOAT_EQ(result.Z, 1.5f);
}
v3 /= s;
EXPECT_FLOAT_EQ(v3.X, 0.5f);
EXPECT_FLOAT_EQ(v3.Y, 1.0f);
EXPECT_FLOAT_EQ(v3.Z, 1.5f);
#endif
}
TEST(Division, Vec4Vec4)
{
hmm_vec4 v4_1 = HMM_Vec4(1.0f, 3.0f, 5.0f, 1.0f);
hmm_vec4 v4_2 = HMM_Vec4(2.0f, 4.0f, 0.5f, 4.0f);
{
hmm_vec4 result = HMM_DivideVec4(v4_1, v4_2);
EXPECT_FLOAT_EQ(result.X, 0.5f);
EXPECT_FLOAT_EQ(result.Y, 0.75f);
EXPECT_FLOAT_EQ(result.Z, 10.0f);
EXPECT_FLOAT_EQ(result.W, 0.25f);
}
#ifdef __cplusplus
{
hmm_vec4 result = HMM_Divide(v4_1, v4_2);
EXPECT_FLOAT_EQ(result.X, 0.5f);
EXPECT_FLOAT_EQ(result.Y, 0.75f);
EXPECT_FLOAT_EQ(result.Z, 10.0f);
EXPECT_FLOAT_EQ(result.W, 0.25f);
}
{
hmm_vec4 result = v4_1 / v4_2;
EXPECT_FLOAT_EQ(result.X, 0.5f);
EXPECT_FLOAT_EQ(result.Y, 0.75f);
EXPECT_FLOAT_EQ(result.Z, 10.0f);
EXPECT_FLOAT_EQ(result.W, 0.25f);
}
v4_1 /= v4_2;
EXPECT_FLOAT_EQ(v4_1.X, 0.5f);
EXPECT_FLOAT_EQ(v4_1.Y, 0.75f);
EXPECT_FLOAT_EQ(v4_1.Z, 10.0f);
EXPECT_FLOAT_EQ(v4_1.W, 0.25f);
#endif
}
TEST(Division, Vec4Scalar)
{
hmm_vec4 v4 = HMM_Vec4(1.0f, 2.0f, 3.0f, 4.0f);
float s = 2;
{
hmm_vec4 result = HMM_DivideVec4f(v4, s);
EXPECT_FLOAT_EQ(result.X, 0.5f);
EXPECT_FLOAT_EQ(result.Y, 1.0f);
EXPECT_FLOAT_EQ(result.Z, 1.5f);
EXPECT_FLOAT_EQ(result.W, 2.0f);
}
#ifdef __cplusplus
{
hmm_vec4 result = HMM_Divide(v4, s);
EXPECT_FLOAT_EQ(result.X, 0.5f);
EXPECT_FLOAT_EQ(result.Y, 1.0f);
EXPECT_FLOAT_EQ(result.Z, 1.5f);
EXPECT_FLOAT_EQ(result.W, 2.0f);
}
{
hmm_vec4 result = v4 / s;
EXPECT_FLOAT_EQ(result.X, 0.5f);
EXPECT_FLOAT_EQ(result.Y, 1.0f);
EXPECT_FLOAT_EQ(result.Z, 1.5f);
EXPECT_FLOAT_EQ(result.W, 2.0f);
}
v4 /= s;
EXPECT_FLOAT_EQ(v4.X, 0.5f);
EXPECT_FLOAT_EQ(v4.Y, 1.0f);
EXPECT_FLOAT_EQ(v4.Z, 1.5f);
EXPECT_FLOAT_EQ(v4.W, 2.0f);
#endif
}
TEST(Division, Mat4Scalar)
{
hmm_mat4 m4 = HMM_Mat4(); // will have 1 - 16
float s = 2;
// Fill the matrix
int Counter = 1;
for (int Column = 0; Column < 4; ++Column)
{
for (int Row = 0; Row < 4; ++Row)
{
m4.Elements[Column][Row] = Counter;
++Counter;
}
}
// Test the results
{
hmm_mat4 result = HMM_DivideMat4f(m4, s);
EXPECT_FLOAT_EQ(result.Elements[0][0], 0.5f);
EXPECT_FLOAT_EQ(result.Elements[0][1], 1.0f);
EXPECT_FLOAT_EQ(result.Elements[0][2], 1.5f);
EXPECT_FLOAT_EQ(result.Elements[0][3], 2.0f);
EXPECT_FLOAT_EQ(result.Elements[1][0], 2.5f);
EXPECT_FLOAT_EQ(result.Elements[1][1], 3.0f);
EXPECT_FLOAT_EQ(result.Elements[1][2], 3.5f);
EXPECT_FLOAT_EQ(result.Elements[1][3], 4.0f);
EXPECT_FLOAT_EQ(result.Elements[2][0], 4.5f);
EXPECT_FLOAT_EQ(result.Elements[2][1], 5.0f);
EXPECT_FLOAT_EQ(result.Elements[2][2], 5.5f);
EXPECT_FLOAT_EQ(result.Elements[2][3], 6.0f);
EXPECT_FLOAT_EQ(result.Elements[3][0], 6.5f);
EXPECT_FLOAT_EQ(result.Elements[3][1], 7.0f);
EXPECT_FLOAT_EQ(result.Elements[3][2], 7.5f);
EXPECT_FLOAT_EQ(result.Elements[3][3], 8.0f);
}
#ifdef __cplusplus
{
hmm_mat4 result = HMM_Divide(m4, s);
EXPECT_FLOAT_EQ(result.Elements[0][0], 0.5f);
EXPECT_FLOAT_EQ(result.Elements[0][1], 1.0f);
EXPECT_FLOAT_EQ(result.Elements[0][2], 1.5f);
EXPECT_FLOAT_EQ(result.Elements[0][3], 2.0f);
EXPECT_FLOAT_EQ(result.Elements[1][0], 2.5f);
EXPECT_FLOAT_EQ(result.Elements[1][1], 3.0f);
EXPECT_FLOAT_EQ(result.Elements[1][2], 3.5f);
EXPECT_FLOAT_EQ(result.Elements[1][3], 4.0f);
EXPECT_FLOAT_EQ(result.Elements[2][0], 4.5f);
EXPECT_FLOAT_EQ(result.Elements[2][1], 5.0f);
EXPECT_FLOAT_EQ(result.Elements[2][2], 5.5f);
EXPECT_FLOAT_EQ(result.Elements[2][3], 6.0f);
EXPECT_FLOAT_EQ(result.Elements[3][0], 6.5f);
EXPECT_FLOAT_EQ(result.Elements[3][1], 7.0f);
EXPECT_FLOAT_EQ(result.Elements[3][2], 7.5f);
EXPECT_FLOAT_EQ(result.Elements[3][3], 8.0f);
}
{
hmm_mat4 result = m4 / s;
EXPECT_FLOAT_EQ(result.Elements[0][0], 0.5f);
EXPECT_FLOAT_EQ(result.Elements[0][1], 1.0f);
EXPECT_FLOAT_EQ(result.Elements[0][2], 1.5f);
EXPECT_FLOAT_EQ(result.Elements[0][3], 2.0f);
EXPECT_FLOAT_EQ(result.Elements[1][0], 2.5f);
EXPECT_FLOAT_EQ(result.Elements[1][1], 3.0f);
EXPECT_FLOAT_EQ(result.Elements[1][2], 3.5f);
EXPECT_FLOAT_EQ(result.Elements[1][3], 4.0f);
EXPECT_FLOAT_EQ(result.Elements[2][0], 4.5f);
EXPECT_FLOAT_EQ(result.Elements[2][1], 5.0f);
EXPECT_FLOAT_EQ(result.Elements[2][2], 5.5f);
EXPECT_FLOAT_EQ(result.Elements[2][3], 6.0f);
EXPECT_FLOAT_EQ(result.Elements[3][0], 6.5f);
EXPECT_FLOAT_EQ(result.Elements[3][1], 7.0f);
EXPECT_FLOAT_EQ(result.Elements[3][2], 7.5f);
EXPECT_FLOAT_EQ(result.Elements[3][3], 8.0f);
}
m4 /= s;
EXPECT_FLOAT_EQ(m4.Elements[0][0], 0.5f);
EXPECT_FLOAT_EQ(m4.Elements[0][1], 1.0f);
EXPECT_FLOAT_EQ(m4.Elements[0][2], 1.5f);
EXPECT_FLOAT_EQ(m4.Elements[0][3], 2.0f);
EXPECT_FLOAT_EQ(m4.Elements[1][0], 2.5f);
EXPECT_FLOAT_EQ(m4.Elements[1][1], 3.0f);
EXPECT_FLOAT_EQ(m4.Elements[1][2], 3.5f);
EXPECT_FLOAT_EQ(m4.Elements[1][3], 4.0f);
EXPECT_FLOAT_EQ(m4.Elements[2][0], 4.5f);
EXPECT_FLOAT_EQ(m4.Elements[2][1], 5.0f);
EXPECT_FLOAT_EQ(m4.Elements[2][2], 5.5f);
EXPECT_FLOAT_EQ(m4.Elements[2][3], 6.0f);
EXPECT_FLOAT_EQ(m4.Elements[3][0], 6.5f);
EXPECT_FLOAT_EQ(m4.Elements[3][1], 7.0f);
EXPECT_FLOAT_EQ(m4.Elements[3][2], 7.5f);
EXPECT_FLOAT_EQ(m4.Elements[3][3], 8.0f);
#endif
}
TEST(Division, QuaternionScalar)
{
hmm_quaternion q = HMM_Quaternion(1.0f, 2.0f, 3.0f, 4.0f);
float f = 2.0f;
{
hmm_quaternion result = HMM_DivideQuaternionF(q, f);
EXPECT_FLOAT_EQ(result.X, 0.5f);
EXPECT_FLOAT_EQ(result.Y, 1.0f);
EXPECT_FLOAT_EQ(result.Z, 1.5f);
EXPECT_FLOAT_EQ(result.W, 2.0f);
}
#ifdef __cplusplus
{
hmm_quaternion result = HMM_Divide(q, f);
EXPECT_FLOAT_EQ(result.X, 0.5f);
EXPECT_FLOAT_EQ(result.Y, 1.0f);
EXPECT_FLOAT_EQ(result.Z, 1.5f);
EXPECT_FLOAT_EQ(result.W, 2.0f);
}
{
hmm_quaternion result = q / f;
EXPECT_FLOAT_EQ(result.X, 0.5f);
EXPECT_FLOAT_EQ(result.Y, 1.0f);
EXPECT_FLOAT_EQ(result.Z, 1.5f);
EXPECT_FLOAT_EQ(result.W, 2.0f);
}
q /= f;
EXPECT_FLOAT_EQ(q.X, 0.5f);
EXPECT_FLOAT_EQ(q.Y, 1.0f);
EXPECT_FLOAT_EQ(q.Z, 1.5f);
EXPECT_FLOAT_EQ(q.W, 2.0f);
#endif
}

View File

@@ -0,0 +1,64 @@
#include "../HandmadeTest.h"
TEST(Equality, Vec2)
{
hmm_vec2 a = HMM_Vec2(1.0f, 2.0f);
hmm_vec2 b = HMM_Vec2(1.0f, 2.0f);
hmm_vec2 c = HMM_Vec2(3.0f, 4.0f);
EXPECT_TRUE(HMM_EqualsVec2(a, b));
EXPECT_FALSE(HMM_EqualsVec2(a, c));
#ifdef __cplusplus
EXPECT_TRUE(HMM_Equals(a, b));
EXPECT_FALSE(HMM_Equals(a, c));
EXPECT_TRUE(a == b);
EXPECT_FALSE(a == c);
EXPECT_FALSE(a != b);
EXPECT_TRUE(a != c);
#endif
}
TEST(Equality, Vec3)
{
hmm_vec3 a = HMM_Vec3(1.0f, 2.0f, 3.0f);
hmm_vec3 b = HMM_Vec3(1.0f, 2.0f, 3.0f);
hmm_vec3 c = HMM_Vec3(4.0f, 5.0f, 6.0f);
EXPECT_TRUE(HMM_EqualsVec3(a, b));
EXPECT_FALSE(HMM_EqualsVec3(a, c));
#ifdef __cplusplus
EXPECT_TRUE(HMM_Equals(a, b));
EXPECT_FALSE(HMM_Equals(a, c));
EXPECT_TRUE(a == b);
EXPECT_FALSE(a == c);
EXPECT_FALSE(a != b);
EXPECT_TRUE(a != c);
#endif
}
TEST(Equality, Vec4)
{
hmm_vec4 a = HMM_Vec4(1.0f, 2.0f, 3.0f, 4.0f);
hmm_vec4 b = HMM_Vec4(1.0f, 2.0f, 3.0f, 4.0f);
hmm_vec4 c = HMM_Vec4(5.0f, 6.0f, 7.0f, 8.0f);
EXPECT_TRUE(HMM_EqualsVec4(a, b));
EXPECT_FALSE(HMM_EqualsVec4(a, c));
#ifdef __cplusplus
EXPECT_TRUE(HMM_Equals(a, b));
EXPECT_FALSE(HMM_Equals(a, c));
EXPECT_TRUE(a == b);
EXPECT_FALSE(a == c);
EXPECT_FALSE(a != b);
EXPECT_TRUE(a != c);
#endif
}

View File

@@ -0,0 +1,246 @@
#include "../HandmadeTest.h"
TEST(Initialization, Vectors)
{
//
// Test vec2
//
hmm_vec2 v2 = HMM_Vec2(1.0f, 2.0f);
hmm_vec2 v2i = HMM_Vec2i(1, 2);
EXPECT_FLOAT_EQ(v2.X, 1.0f);
EXPECT_FLOAT_EQ(v2.Y, 2.0f);
EXPECT_FLOAT_EQ(v2.U, 1.0f);
EXPECT_FLOAT_EQ(v2.V, 2.0f);
EXPECT_FLOAT_EQ(v2.Left, 1.0f);
EXPECT_FLOAT_EQ(v2.Right, 2.0f);
EXPECT_FLOAT_EQ(v2.Width, 1.0f);
EXPECT_FLOAT_EQ(v2.Height, 2.0f);
EXPECT_FLOAT_EQ(v2.Elements[0], 1.0f);
EXPECT_FLOAT_EQ(v2.Elements[1], 2.0f);
#ifdef __cplusplus
EXPECT_FLOAT_EQ(v2[0], 1.0f);
EXPECT_FLOAT_EQ(v2[1], 2.0f);
#endif
EXPECT_FLOAT_EQ(v2i.X, 1.0f);
EXPECT_FLOAT_EQ(v2i.Y, 2.0f);
EXPECT_FLOAT_EQ(v2i.U, 1.0f);
EXPECT_FLOAT_EQ(v2i.V, 2.0f);
EXPECT_FLOAT_EQ(v2i.Left, 1.0f);
EXPECT_FLOAT_EQ(v2i.Right, 2.0f);
EXPECT_FLOAT_EQ(v2i.Width, 1.0f);
EXPECT_FLOAT_EQ(v2i.Height, 2.0f);
EXPECT_FLOAT_EQ(v2i.Elements[0], 1.0f);
EXPECT_FLOAT_EQ(v2i.Elements[1], 2.0f);
#ifdef __cplusplus
EXPECT_FLOAT_EQ(v2i[0], 1.0f);
EXPECT_FLOAT_EQ(v2i[1], 2.0f);
#endif
//
// Test vec3
//
hmm_vec3 v3 = HMM_Vec3(1.0f, 2.0f, 3.0f);
hmm_vec3 v3i = HMM_Vec3i(1, 2, 3);
EXPECT_FLOAT_EQ(v3.X, 1.0f);
EXPECT_FLOAT_EQ(v3.Y, 2.0f);
EXPECT_FLOAT_EQ(v3.Z, 3.0f);
EXPECT_FLOAT_EQ(v3.U, 1.0f);
EXPECT_FLOAT_EQ(v3.V, 2.0f);
EXPECT_FLOAT_EQ(v3.W, 3.0f);
EXPECT_FLOAT_EQ(v3.R, 1.0f);
EXPECT_FLOAT_EQ(v3.G, 2.0f);
EXPECT_FLOAT_EQ(v3.B, 3.0f);
EXPECT_FLOAT_EQ(v3.Elements[0], 1.0f);
EXPECT_FLOAT_EQ(v3.Elements[1], 2.0f);
EXPECT_FLOAT_EQ(v3.Elements[2], 3.0f);
EXPECT_FLOAT_EQ(v3.XY.Elements[0], 1.0f);
EXPECT_FLOAT_EQ(v3.XY.Elements[1], 2.0f);
EXPECT_FLOAT_EQ(v3.YZ.Elements[0], 2.0f);
EXPECT_FLOAT_EQ(v3.YZ.Elements[1], 3.0f);
EXPECT_FLOAT_EQ(v3.UV.Elements[0], 1.0f);
EXPECT_FLOAT_EQ(v3.UV.Elements[1], 2.0f);
EXPECT_FLOAT_EQ(v3.VW.Elements[0], 2.0f);
EXPECT_FLOAT_EQ(v3.VW.Elements[1], 3.0f);
#ifdef __cplusplus
EXPECT_FLOAT_EQ(v3[0], 1.0f);
EXPECT_FLOAT_EQ(v3[1], 2.0f);
EXPECT_FLOAT_EQ(v3[2], 3.0f);
#endif
EXPECT_FLOAT_EQ(v3i.X, 1.0f);
EXPECT_FLOAT_EQ(v3i.Y, 2.0f);
EXPECT_FLOAT_EQ(v3i.Z, 3.0f);
EXPECT_FLOAT_EQ(v3i.U, 1.0f);
EXPECT_FLOAT_EQ(v3i.V, 2.0f);
EXPECT_FLOAT_EQ(v3i.W, 3.0f);
EXPECT_FLOAT_EQ(v3i.R, 1.0f);
EXPECT_FLOAT_EQ(v3i.G, 2.0f);
EXPECT_FLOAT_EQ(v3i.B, 3.0f);
EXPECT_FLOAT_EQ(v3i.Elements[0], 1.0f);
EXPECT_FLOAT_EQ(v3i.Elements[1], 2.0f);
EXPECT_FLOAT_EQ(v3i.Elements[2], 3.0f);
EXPECT_FLOAT_EQ(v3i.XY.Elements[0], 1.0f);
EXPECT_FLOAT_EQ(v3i.XY.Elements[1], 2.0f);
EXPECT_FLOAT_EQ(v3i.YZ.Elements[0], 2.0f);
EXPECT_FLOAT_EQ(v3i.YZ.Elements[1], 3.0f);
EXPECT_FLOAT_EQ(v3i.UV.Elements[0], 1.0f);
EXPECT_FLOAT_EQ(v3i.UV.Elements[1], 2.0f);
EXPECT_FLOAT_EQ(v3i.VW.Elements[0], 2.0f);
EXPECT_FLOAT_EQ(v3i.VW.Elements[1], 3.0f);
#ifdef __cplusplus
EXPECT_FLOAT_EQ(v3i[0], 1.0f);
EXPECT_FLOAT_EQ(v3i[1], 2.0f);
EXPECT_FLOAT_EQ(v3i[2], 3.0f);
#endif
//
// Test vec4
//
hmm_vec4 v4 = HMM_Vec4(1.0f, 2.0f, 3.0f, 4.0f);
hmm_vec4 v4i = HMM_Vec4i(1, 2, 3, 4);
hmm_vec4 v4v = HMM_Vec4v(v3, 4.0f);
EXPECT_FLOAT_EQ(v4.X, 1.0f);
EXPECT_FLOAT_EQ(v4.Y, 2.0f);
EXPECT_FLOAT_EQ(v4.Z, 3.0f);
EXPECT_FLOAT_EQ(v4.W, 4.0f);
EXPECT_FLOAT_EQ(v4.R, 1.0f);
EXPECT_FLOAT_EQ(v4.G, 2.0f);
EXPECT_FLOAT_EQ(v4.B, 3.0f);
EXPECT_FLOAT_EQ(v4.A, 4.0f);
EXPECT_FLOAT_EQ(v4.XY.Elements[0], 1.0f);
EXPECT_FLOAT_EQ(v4.XY.Elements[1], 2.0f);
EXPECT_FLOAT_EQ(v4.YZ.Elements[0], 2.0f);
EXPECT_FLOAT_EQ(v4.YZ.Elements[1], 3.0f);
EXPECT_FLOAT_EQ(v4.ZW.Elements[0], 3.0f);
EXPECT_FLOAT_EQ(v4.ZW.Elements[1], 4.0f);
EXPECT_FLOAT_EQ(v4.XY.Elements[0], 1.0f);
EXPECT_FLOAT_EQ(v4.XY.Elements[1], 2.0f);
EXPECT_FLOAT_EQ(v4.XYZ.Elements[0], 1.0f);
EXPECT_FLOAT_EQ(v4.XYZ.Elements[1], 2.0f);
EXPECT_FLOAT_EQ(v4.XYZ.Elements[2], 3.0f);
EXPECT_FLOAT_EQ(v4.RGB.Elements[0], 1.0f);
EXPECT_FLOAT_EQ(v4.RGB.Elements[1], 2.0f);
EXPECT_FLOAT_EQ(v4.RGB.Elements[2], 3.0f);
#ifdef __cplusplus
EXPECT_FLOAT_EQ(v4[0], 1.0f);
EXPECT_FLOAT_EQ(v4[1], 2.0f);
EXPECT_FLOAT_EQ(v4[2], 3.0f);
EXPECT_FLOAT_EQ(v4[3], 4.0f);
#endif
EXPECT_FLOAT_EQ(v4i.X, 1.0f);
EXPECT_FLOAT_EQ(v4i.Y, 2.0f);
EXPECT_FLOAT_EQ(v4i.Z, 3.0f);
EXPECT_FLOAT_EQ(v4i.W, 4.0f);
EXPECT_FLOAT_EQ(v4i.R, 1.0f);
EXPECT_FLOAT_EQ(v4i.G, 2.0f);
EXPECT_FLOAT_EQ(v4i.B, 3.0f);
EXPECT_FLOAT_EQ(v4i.A, 4.0f);
EXPECT_FLOAT_EQ(v4i.XY.Elements[0], 1.0f);
EXPECT_FLOAT_EQ(v4i.XY.Elements[1], 2.0f);
EXPECT_FLOAT_EQ(v4i.YZ.Elements[0], 2.0f);
EXPECT_FLOAT_EQ(v4i.YZ.Elements[1], 3.0f);
EXPECT_FLOAT_EQ(v4i.ZW.Elements[0], 3.0f);
EXPECT_FLOAT_EQ(v4i.ZW.Elements[1], 4.0f);
EXPECT_FLOAT_EQ(v4i.XY.Elements[0], 1.0f);
EXPECT_FLOAT_EQ(v4i.XY.Elements[1], 2.0f);
EXPECT_FLOAT_EQ(v4i.XYZ.Elements[0], 1.0f);
EXPECT_FLOAT_EQ(v4i.XYZ.Elements[1], 2.0f);
EXPECT_FLOAT_EQ(v4i.XYZ.Elements[2], 3.0f);
EXPECT_FLOAT_EQ(v4i.RGB.Elements[0], 1.0f);
EXPECT_FLOAT_EQ(v4i.RGB.Elements[1], 2.0f);
EXPECT_FLOAT_EQ(v4i.RGB.Elements[2], 3.0f);
#ifdef __cplusplus
EXPECT_FLOAT_EQ(v4i[0], 1.0f);
EXPECT_FLOAT_EQ(v4i[1], 2.0f);
EXPECT_FLOAT_EQ(v4i[2], 3.0f);
EXPECT_FLOAT_EQ(v4i[3], 4.0f);
#endif
EXPECT_FLOAT_EQ(v4v.X, 1.0f);
EXPECT_FLOAT_EQ(v4v.Y, 2.0f);
EXPECT_FLOAT_EQ(v4v.Z, 3.0f);
EXPECT_FLOAT_EQ(v4v.W, 4.0f);
EXPECT_FLOAT_EQ(v4v.R, 1.0f);
EXPECT_FLOAT_EQ(v4v.G, 2.0f);
EXPECT_FLOAT_EQ(v4v.B, 3.0f);
EXPECT_FLOAT_EQ(v4v.A, 4.0f);
EXPECT_FLOAT_EQ(v4v.XY.Elements[0], 1.0f);
EXPECT_FLOAT_EQ(v4v.XY.Elements[1], 2.0f);
EXPECT_FLOAT_EQ(v4v.YZ.Elements[0], 2.0f);
EXPECT_FLOAT_EQ(v4v.YZ.Elements[1], 3.0f);
EXPECT_FLOAT_EQ(v4v.ZW.Elements[0], 3.0f);
EXPECT_FLOAT_EQ(v4v.ZW.Elements[1], 4.0f);
EXPECT_FLOAT_EQ(v4v.XY.Elements[0], 1.0f);
EXPECT_FLOAT_EQ(v4v.XY.Elements[1], 2.0f);
EXPECT_FLOAT_EQ(v4v.XYZ.Elements[0], 1.0f);
EXPECT_FLOAT_EQ(v4v.XYZ.Elements[1], 2.0f);
EXPECT_FLOAT_EQ(v4v.XYZ.Elements[2], 3.0f);
EXPECT_FLOAT_EQ(v4v.RGB.Elements[0], 1.0f);
EXPECT_FLOAT_EQ(v4v.RGB.Elements[1], 2.0f);
EXPECT_FLOAT_EQ(v4v.RGB.Elements[2], 3.0f);
#ifdef __cplusplus
EXPECT_FLOAT_EQ(v4v[0], 1.0f);
EXPECT_FLOAT_EQ(v4v[1], 2.0f);
EXPECT_FLOAT_EQ(v4v[2], 3.0f);
EXPECT_FLOAT_EQ(v4v[3], 4.0f);
#endif
}
TEST(Initialization, MatrixEmpty)
{
hmm_mat4 m4 = HMM_Mat4();
for (int Column = 0; Column < 4; ++Column)
{
for (int Row = 0; Row < 4; ++Row)
{
EXPECT_FLOAT_EQ(m4.Elements[Column][Row], 0.0f);
#ifdef __cplusplus
EXPECT_FLOAT_EQ(m4[Column][Row], 0.0f);
#endif
}
}
}
TEST(Initialization, MatrixDiagonal)
{
hmm_mat4 m4d = HMM_Mat4d(1.0f);
for (int Column = 0; Column < 4; ++Column)
{
for (int Row = 0; Row < 4; ++Row)
{
if (Column == Row) {
EXPECT_FLOAT_EQ(m4d.Elements[Column][Row], 1.0f);
} else {
EXPECT_FLOAT_EQ(m4d.Elements[Column][Row], 0.0f);
}
}
}
}
TEST(Initialization, Quaternion)
{
hmm_quaternion q = HMM_Quaternion(1.0f, 2.0f, 3.0f, 4.0f);
EXPECT_FLOAT_EQ(q.X, 1.0f);
EXPECT_FLOAT_EQ(q.Y, 2.0f);
EXPECT_FLOAT_EQ(q.Z, 3.0f);
EXPECT_FLOAT_EQ(q.W, 4.0f);
EXPECT_FLOAT_EQ(q.Elements[0], 1.0f);
EXPECT_FLOAT_EQ(q.Elements[1], 2.0f);
EXPECT_FLOAT_EQ(q.Elements[2], 3.0f);
EXPECT_FLOAT_EQ(q.Elements[3], 4.0f);
hmm_vec4 v = HMM_Vec4(1.0f, 2.0f, 3.0f, 4.0f);
hmm_quaternion qv = HMM_QuaternionV4(v);
EXPECT_FLOAT_EQ(qv.X, 1.0f);
EXPECT_FLOAT_EQ(qv.Y, 2.0f);
EXPECT_FLOAT_EQ(qv.Z, 3.0f);
EXPECT_FLOAT_EQ(qv.W, 4.0f);
}

View File

@@ -0,0 +1,536 @@
#include "../HandmadeTest.h"
TEST(Multiplication, Vec2Vec2)
{
hmm_vec2 v2_1 = HMM_Vec2(1.0f, 2.0f);
hmm_vec2 v2_2 = HMM_Vec2(3.0f, 4.0f);
{
hmm_vec2 result = HMM_MultiplyVec2(v2_1, v2_2);
EXPECT_FLOAT_EQ(result.X, 3.0f);
EXPECT_FLOAT_EQ(result.Y, 8.0f);
}
#ifdef __cplusplus
{
hmm_vec2 result = HMM_Multiply(v2_1, v2_2);
EXPECT_FLOAT_EQ(result.X, 3.0f);
EXPECT_FLOAT_EQ(result.Y, 8.0f);
}
{
hmm_vec2 result = v2_1 * v2_2;
EXPECT_FLOAT_EQ(result.X, 3.0f);
EXPECT_FLOAT_EQ(result.Y, 8.0f);
}
v2_1 *= v2_2;
EXPECT_FLOAT_EQ(v2_1.X, 3.0f);
EXPECT_FLOAT_EQ(v2_1.Y, 8.0f);
#endif
}
TEST(Multiplication, Vec2Scalar)
{
hmm_vec2 v2 = HMM_Vec2(1.0f, 2.0f);
float s = 3.0f;
{
hmm_vec2 result = HMM_MultiplyVec2f(v2, s);
EXPECT_FLOAT_EQ(result.X, 3.0f);
EXPECT_FLOAT_EQ(result.Y, 6.0f);
}
#ifdef __cplusplus
{
hmm_vec2 result = HMM_Multiply(v2, s);
EXPECT_FLOAT_EQ(result.X, 3.0f);
EXPECT_FLOAT_EQ(result.Y, 6.0f);
}
{
hmm_vec2 result = v2 * s;
EXPECT_FLOAT_EQ(result.X, 3.0f);
EXPECT_FLOAT_EQ(result.Y, 6.0f);
}
{
hmm_vec2 result = s * v2;
EXPECT_FLOAT_EQ(result.X, 3.0f);
EXPECT_FLOAT_EQ(result.Y, 6.0f);
}
v2 *= s;
EXPECT_FLOAT_EQ(v2.X, 3.0f);
EXPECT_FLOAT_EQ(v2.Y, 6.0f);
#endif
}
TEST(Multiplication, Vec3Vec3)
{
hmm_vec3 v3_1 = HMM_Vec3(1.0f, 2.0f, 3.0f);
hmm_vec3 v3_2 = HMM_Vec3(4.0f, 5.0f, 6.0f);
{
hmm_vec3 result = HMM_MultiplyVec3(v3_1, v3_2);
EXPECT_FLOAT_EQ(result.X, 4.0f);
EXPECT_FLOAT_EQ(result.Y, 10.0f);
EXPECT_FLOAT_EQ(result.Z, 18.0f);
}
#ifdef __cplusplus
{
hmm_vec3 result = HMM_Multiply(v3_1, v3_2);
EXPECT_FLOAT_EQ(result.X, 4.0f);
EXPECT_FLOAT_EQ(result.Y, 10.0f);
EXPECT_FLOAT_EQ(result.Z, 18.0f);
}
{
hmm_vec3 result = v3_1 * v3_2;
EXPECT_FLOAT_EQ(result.X, 4.0f);
EXPECT_FLOAT_EQ(result.Y, 10.0f);
EXPECT_FLOAT_EQ(result.Z, 18.0f);
}
v3_1 *= v3_2;
EXPECT_FLOAT_EQ(v3_1.X, 4.0f);
EXPECT_FLOAT_EQ(v3_1.Y, 10.0f);
EXPECT_FLOAT_EQ(v3_1.Z, 18.0f);
#endif
}
TEST(Multiplication, Vec3Scalar)
{
hmm_vec3 v3 = HMM_Vec3(1.0f, 2.0f, 3.0f);
float s = 3.0f;
{
hmm_vec3 result = HMM_MultiplyVec3f(v3, s);
EXPECT_FLOAT_EQ(result.X, 3.0f);
EXPECT_FLOAT_EQ(result.Y, 6.0f);
EXPECT_FLOAT_EQ(result.Z, 9.0f);
}
#ifdef __cplusplus
{
hmm_vec3 result = HMM_Multiply(v3, s);
EXPECT_FLOAT_EQ(result.X, 3.0f);
EXPECT_FLOAT_EQ(result.Y, 6.0f);
EXPECT_FLOAT_EQ(result.Z, 9.0f);
}
{
hmm_vec3 result = v3 * s;
EXPECT_FLOAT_EQ(result.X, 3.0f);
EXPECT_FLOAT_EQ(result.Y, 6.0f);
EXPECT_FLOAT_EQ(result.Z, 9.0f);
}
{
hmm_vec3 result = s * v3;
EXPECT_FLOAT_EQ(result.X, 3.0f);
EXPECT_FLOAT_EQ(result.Y, 6.0f);
EXPECT_FLOAT_EQ(result.Z, 9.0f);
}
v3 *= s;
EXPECT_FLOAT_EQ(v3.X, 3.0f);
EXPECT_FLOAT_EQ(v3.Y, 6.0f);
EXPECT_FLOAT_EQ(v3.Z, 9.0f);
#endif
}
TEST(Multiplication, Vec4Vec4)
{
hmm_vec4 v4_1 = HMM_Vec4(1.0f, 2.0f, 3.0f, 4.0f);
hmm_vec4 v4_2 = HMM_Vec4(5.0f, 6.0f, 7.0f, 8.0f);
{
hmm_vec4 result = HMM_MultiplyVec4(v4_1, v4_2);
EXPECT_FLOAT_EQ(result.X, 5.0f);
EXPECT_FLOAT_EQ(result.Y, 12.0f);
EXPECT_FLOAT_EQ(result.Z, 21.0f);
EXPECT_FLOAT_EQ(result.W, 32.0f);
}
#ifdef __cplusplus
{
hmm_vec4 result = HMM_Multiply(v4_1, v4_2);
EXPECT_FLOAT_EQ(result.X, 5.0f);
EXPECT_FLOAT_EQ(result.Y, 12.0f);
EXPECT_FLOAT_EQ(result.Z, 21.0f);
EXPECT_FLOAT_EQ(result.W, 32.0f);
}
{
hmm_vec4 result = v4_1 * v4_2;
EXPECT_FLOAT_EQ(result.X, 5.0f);
EXPECT_FLOAT_EQ(result.Y, 12.0f);
EXPECT_FLOAT_EQ(result.Z, 21.0f);
EXPECT_FLOAT_EQ(result.W, 32.0f);
}
v4_1 *= v4_2;
EXPECT_FLOAT_EQ(v4_1.X, 5.0f);
EXPECT_FLOAT_EQ(v4_1.Y, 12.0f);
EXPECT_FLOAT_EQ(v4_1.Z, 21.0f);
EXPECT_FLOAT_EQ(v4_1.W, 32.0f);
#endif
}
TEST(Multiplication, Vec4Scalar)
{
hmm_vec4 v4 = HMM_Vec4(1.0f, 2.0f, 3.0f, 4.0f);
float s = 3.0f;
{
hmm_vec4 result = HMM_MultiplyVec4f(v4, s);
EXPECT_FLOAT_EQ(result.X, 3.0f);
EXPECT_FLOAT_EQ(result.Y, 6.0f);
EXPECT_FLOAT_EQ(result.Z, 9.0f);
EXPECT_FLOAT_EQ(result.W, 12.0f);
}
#ifdef __cplusplus
{
hmm_vec4 result = HMM_Multiply(v4, s);
EXPECT_FLOAT_EQ(result.X, 3.0f);
EXPECT_FLOAT_EQ(result.Y, 6.0f);
EXPECT_FLOAT_EQ(result.Z, 9.0f);
EXPECT_FLOAT_EQ(result.W, 12.0f);
}
{
hmm_vec4 result = v4 * s;
EXPECT_FLOAT_EQ(result.X, 3.0f);
EXPECT_FLOAT_EQ(result.Y, 6.0f);
EXPECT_FLOAT_EQ(result.Z, 9.0f);
EXPECT_FLOAT_EQ(result.W, 12.0f);
}
{
hmm_vec4 result = s * v4;
EXPECT_FLOAT_EQ(result.X, 3.0f);
EXPECT_FLOAT_EQ(result.Y, 6.0f);
EXPECT_FLOAT_EQ(result.Z, 9.0f);
EXPECT_FLOAT_EQ(result.W, 12.0f);
}
v4 *= s;
EXPECT_FLOAT_EQ(v4.X, 3.0f);
EXPECT_FLOAT_EQ(v4.Y, 6.0f);
EXPECT_FLOAT_EQ(v4.Z, 9.0f);
#endif
}
TEST(Multiplication, Mat4Mat4)
{
hmm_mat4 m4_1 = HMM_Mat4(); // will have 1 - 16
hmm_mat4 m4_2 = HMM_Mat4(); // will have 17 - 32
// Fill the matrices
int Counter = 1;
for (int Column = 0; Column < 4; ++Column)
{
for (int Row = 0; Row < 4; ++Row)
{
m4_1.Elements[Column][Row] = Counter;
++Counter;
}
}
for (int Column = 0; Column < 4; ++Column)
{
for (int Row = 0; Row < 4; ++Row)
{
m4_2.Elements[Column][Row] = Counter;
++Counter;
}
}
// Test the results
{
hmm_mat4 result = HMM_MultiplyMat4(m4_1, m4_2);
EXPECT_FLOAT_EQ(result.Elements[0][0], 538.0f);
EXPECT_FLOAT_EQ(result.Elements[0][1], 612.0f);
EXPECT_FLOAT_EQ(result.Elements[0][2], 686.0f);
EXPECT_FLOAT_EQ(result.Elements[0][3], 760.0f);
EXPECT_FLOAT_EQ(result.Elements[1][0], 650.0f);
EXPECT_FLOAT_EQ(result.Elements[1][1], 740.0f);
EXPECT_FLOAT_EQ(result.Elements[1][2], 830.0f);
EXPECT_FLOAT_EQ(result.Elements[1][3], 920.0f);
EXPECT_FLOAT_EQ(result.Elements[2][0], 762.0f);
EXPECT_FLOAT_EQ(result.Elements[2][1], 868.0f);
EXPECT_FLOAT_EQ(result.Elements[2][2], 974.0f);
EXPECT_FLOAT_EQ(result.Elements[2][3], 1080.0f);
EXPECT_FLOAT_EQ(result.Elements[3][0], 874.0f);
EXPECT_FLOAT_EQ(result.Elements[3][1], 996.0f);
EXPECT_FLOAT_EQ(result.Elements[3][2], 1118.0f);
EXPECT_FLOAT_EQ(result.Elements[3][3], 1240.0f);
}
#ifdef __cplusplus
{
hmm_mat4 result = HMM_Multiply(m4_1, m4_2);
EXPECT_FLOAT_EQ(result.Elements[0][0], 538.0f);
EXPECT_FLOAT_EQ(result.Elements[0][1], 612.0f);
EXPECT_FLOAT_EQ(result.Elements[0][2], 686.0f);
EXPECT_FLOAT_EQ(result.Elements[0][3], 760.0f);
EXPECT_FLOAT_EQ(result.Elements[1][0], 650.0f);
EXPECT_FLOAT_EQ(result.Elements[1][1], 740.0f);
EXPECT_FLOAT_EQ(result.Elements[1][2], 830.0f);
EXPECT_FLOAT_EQ(result.Elements[1][3], 920.0f);
EXPECT_FLOAT_EQ(result.Elements[2][0], 762.0f);
EXPECT_FLOAT_EQ(result.Elements[2][1], 868.0f);
EXPECT_FLOAT_EQ(result.Elements[2][2], 974.0f);
EXPECT_FLOAT_EQ(result.Elements[2][3], 1080.0f);
EXPECT_FLOAT_EQ(result.Elements[3][0], 874.0f);
EXPECT_FLOAT_EQ(result.Elements[3][1], 996.0f);
EXPECT_FLOAT_EQ(result.Elements[3][2], 1118.0f);
EXPECT_FLOAT_EQ(result.Elements[3][3], 1240.0f);
}
{
hmm_mat4 result = m4_1 * m4_2;
EXPECT_FLOAT_EQ(result.Elements[0][0], 538.0f);
EXPECT_FLOAT_EQ(result.Elements[0][1], 612.0f);
EXPECT_FLOAT_EQ(result.Elements[0][2], 686.0f);
EXPECT_FLOAT_EQ(result.Elements[0][3], 760.0f);
EXPECT_FLOAT_EQ(result.Elements[1][0], 650.0f);
EXPECT_FLOAT_EQ(result.Elements[1][1], 740.0f);
EXPECT_FLOAT_EQ(result.Elements[1][2], 830.0f);
EXPECT_FLOAT_EQ(result.Elements[1][3], 920.0f);
EXPECT_FLOAT_EQ(result.Elements[2][0], 762.0f);
EXPECT_FLOAT_EQ(result.Elements[2][1], 868.0f);
EXPECT_FLOAT_EQ(result.Elements[2][2], 974.0f);
EXPECT_FLOAT_EQ(result.Elements[2][3], 1080.0f);
EXPECT_FLOAT_EQ(result.Elements[3][0], 874.0f);
EXPECT_FLOAT_EQ(result.Elements[3][1], 996.0f);
EXPECT_FLOAT_EQ(result.Elements[3][2], 1118.0f);
EXPECT_FLOAT_EQ(result.Elements[3][3], 1240.0f);
}
// At the time I wrote this, I intentionally omitted
// the *= operator for matrices because matrix
// multiplication is not commutative. (bvisness)
#endif
}
TEST(Multiplication, Mat4Scalar)
{
hmm_mat4 m4 = HMM_Mat4(); // will have 1 - 16
float s = 3;
// Fill the matrix
int Counter = 1;
for (int Column = 0; Column < 4; ++Column)
{
for (int Row = 0; Row < 4; ++Row)
{
m4.Elements[Column][Row] = Counter;
++Counter;
}
}
// Test the results
{
hmm_mat4 result = HMM_MultiplyMat4f(m4, s);
EXPECT_FLOAT_EQ(result.Elements[0][0], 3.0f);
EXPECT_FLOAT_EQ(result.Elements[0][1], 6.0f);
EXPECT_FLOAT_EQ(result.Elements[0][2], 9.0f);
EXPECT_FLOAT_EQ(result.Elements[0][3], 12.0f);
EXPECT_FLOAT_EQ(result.Elements[1][0], 15.0f);
EXPECT_FLOAT_EQ(result.Elements[1][1], 18.0f);
EXPECT_FLOAT_EQ(result.Elements[1][2], 21.0f);
EXPECT_FLOAT_EQ(result.Elements[1][3], 24.0f);
EXPECT_FLOAT_EQ(result.Elements[2][0], 27.0f);
EXPECT_FLOAT_EQ(result.Elements[2][1], 30.0f);
EXPECT_FLOAT_EQ(result.Elements[2][2], 33.0f);
EXPECT_FLOAT_EQ(result.Elements[2][3], 36.0f);
EXPECT_FLOAT_EQ(result.Elements[3][0], 39.0f);
EXPECT_FLOAT_EQ(result.Elements[3][1], 42.0f);
EXPECT_FLOAT_EQ(result.Elements[3][2], 45.0f);
EXPECT_FLOAT_EQ(result.Elements[3][3], 48.0f);
}
#ifdef __cplusplus
{
hmm_mat4 result = HMM_Multiply(m4, s);
EXPECT_FLOAT_EQ(result.Elements[0][0], 3.0f);
EXPECT_FLOAT_EQ(result.Elements[0][1], 6.0f);
EXPECT_FLOAT_EQ(result.Elements[0][2], 9.0f);
EXPECT_FLOAT_EQ(result.Elements[0][3], 12.0f);
EXPECT_FLOAT_EQ(result.Elements[1][0], 15.0f);
EXPECT_FLOAT_EQ(result.Elements[1][1], 18.0f);
EXPECT_FLOAT_EQ(result.Elements[1][2], 21.0f);
EXPECT_FLOAT_EQ(result.Elements[1][3], 24.0f);
EXPECT_FLOAT_EQ(result.Elements[2][0], 27.0f);
EXPECT_FLOAT_EQ(result.Elements[2][1], 30.0f);
EXPECT_FLOAT_EQ(result.Elements[2][2], 33.0f);
EXPECT_FLOAT_EQ(result.Elements[2][3], 36.0f);
EXPECT_FLOAT_EQ(result.Elements[3][0], 39.0f);
EXPECT_FLOAT_EQ(result.Elements[3][1], 42.0f);
EXPECT_FLOAT_EQ(result.Elements[3][2], 45.0f);
EXPECT_FLOAT_EQ(result.Elements[3][3], 48.0f);
}
{
hmm_mat4 result = m4 * s;
EXPECT_FLOAT_EQ(result.Elements[0][0], 3.0f);
EXPECT_FLOAT_EQ(result.Elements[0][1], 6.0f);
EXPECT_FLOAT_EQ(result.Elements[0][2], 9.0f);
EXPECT_FLOAT_EQ(result.Elements[0][3], 12.0f);
EXPECT_FLOAT_EQ(result.Elements[1][0], 15.0f);
EXPECT_FLOAT_EQ(result.Elements[1][1], 18.0f);
EXPECT_FLOAT_EQ(result.Elements[1][2], 21.0f);
EXPECT_FLOAT_EQ(result.Elements[1][3], 24.0f);
EXPECT_FLOAT_EQ(result.Elements[2][0], 27.0f);
EXPECT_FLOAT_EQ(result.Elements[2][1], 30.0f);
EXPECT_FLOAT_EQ(result.Elements[2][2], 33.0f);
EXPECT_FLOAT_EQ(result.Elements[2][3], 36.0f);
EXPECT_FLOAT_EQ(result.Elements[3][0], 39.0f);
EXPECT_FLOAT_EQ(result.Elements[3][1], 42.0f);
EXPECT_FLOAT_EQ(result.Elements[3][2], 45.0f);
EXPECT_FLOAT_EQ(result.Elements[3][3], 48.0f);
}
{
hmm_mat4 result = s * m4;
EXPECT_FLOAT_EQ(result.Elements[0][0], 3.0f);
EXPECT_FLOAT_EQ(result.Elements[0][1], 6.0f);
EXPECT_FLOAT_EQ(result.Elements[0][2], 9.0f);
EXPECT_FLOAT_EQ(result.Elements[0][3], 12.0f);
EXPECT_FLOAT_EQ(result.Elements[1][0], 15.0f);
EXPECT_FLOAT_EQ(result.Elements[1][1], 18.0f);
EXPECT_FLOAT_EQ(result.Elements[1][2], 21.0f);
EXPECT_FLOAT_EQ(result.Elements[1][3], 24.0f);
EXPECT_FLOAT_EQ(result.Elements[2][0], 27.0f);
EXPECT_FLOAT_EQ(result.Elements[2][1], 30.0f);
EXPECT_FLOAT_EQ(result.Elements[2][2], 33.0f);
EXPECT_FLOAT_EQ(result.Elements[2][3], 36.0f);
EXPECT_FLOAT_EQ(result.Elements[3][0], 39.0f);
EXPECT_FLOAT_EQ(result.Elements[3][1], 42.0f);
EXPECT_FLOAT_EQ(result.Elements[3][2], 45.0f);
EXPECT_FLOAT_EQ(result.Elements[3][3], 48.0f);
}
m4 *= s;
EXPECT_FLOAT_EQ(m4.Elements[0][0], 3.0f);
EXPECT_FLOAT_EQ(m4.Elements[0][1], 6.0f);
EXPECT_FLOAT_EQ(m4.Elements[0][2], 9.0f);
EXPECT_FLOAT_EQ(m4.Elements[0][3], 12.0f);
EXPECT_FLOAT_EQ(m4.Elements[1][0], 15.0f);
EXPECT_FLOAT_EQ(m4.Elements[1][1], 18.0f);
EXPECT_FLOAT_EQ(m4.Elements[1][2], 21.0f);
EXPECT_FLOAT_EQ(m4.Elements[1][3], 24.0f);
EXPECT_FLOAT_EQ(m4.Elements[2][0], 27.0f);
EXPECT_FLOAT_EQ(m4.Elements[2][1], 30.0f);
EXPECT_FLOAT_EQ(m4.Elements[2][2], 33.0f);
EXPECT_FLOAT_EQ(m4.Elements[2][3], 36.0f);
EXPECT_FLOAT_EQ(m4.Elements[3][0], 39.0f);
EXPECT_FLOAT_EQ(m4.Elements[3][1], 42.0f);
EXPECT_FLOAT_EQ(m4.Elements[3][2], 45.0f);
EXPECT_FLOAT_EQ(m4.Elements[3][3], 48.0f);
#endif
}
TEST(Multiplication, Mat4Vec4)
{
hmm_mat4 m4 = HMM_Mat4(); // will have 1 - 16
hmm_vec4 v4 = HMM_Vec4(1.0f, 2.0f, 3.0f, 4.0f);
// Fill the matrix
int Counter = 1;
for (int Column = 0; Column < 4; ++Column)
{
for (int Row = 0; Row < 4; ++Row)
{
m4.Elements[Column][Row] = Counter;
++Counter;
}
}
// Test the results
{
hmm_vec4 result = HMM_MultiplyMat4ByVec4(m4, v4);
EXPECT_FLOAT_EQ(result.X, 90.0f);
EXPECT_FLOAT_EQ(result.Y, 100.0f);
EXPECT_FLOAT_EQ(result.Z, 110.0f);
EXPECT_FLOAT_EQ(result.W, 120.0f);
}
#ifdef __cplusplus
{
hmm_vec4 result = HMM_Multiply(m4, v4);
EXPECT_FLOAT_EQ(result.X, 90.0f);
EXPECT_FLOAT_EQ(result.Y, 100.0f);
EXPECT_FLOAT_EQ(result.Z, 110.0f);
EXPECT_FLOAT_EQ(result.W, 120.0f);
}
{
hmm_vec4 result = m4 * v4;
EXPECT_FLOAT_EQ(result.X, 90.0f);
EXPECT_FLOAT_EQ(result.Y, 100.0f);
EXPECT_FLOAT_EQ(result.Z, 110.0f);
EXPECT_FLOAT_EQ(result.W, 120.0f);
}
// *= makes no sense for this particular case.
#endif
}
TEST(Multiplication, QuaternionQuaternion)
{
hmm_quaternion q1 = HMM_Quaternion(1.0f, 2.0f, 3.0f, 4.0f);
hmm_quaternion q2 = HMM_Quaternion(5.0f, 6.0f, 7.0f, 8.0f);
{
hmm_quaternion result = HMM_MultiplyQuaternion(q1, q2);
EXPECT_FLOAT_EQ(result.X, 24.0f);
EXPECT_FLOAT_EQ(result.Y, 48.0f);
EXPECT_FLOAT_EQ(result.Z, 48.0f);
EXPECT_FLOAT_EQ(result.W, -6.0f);
}
#ifdef __cplusplus
{
hmm_quaternion result = HMM_Multiply(q1, q2);
EXPECT_FLOAT_EQ(result.X, 24.0f);
EXPECT_FLOAT_EQ(result.Y, 48.0f);
EXPECT_FLOAT_EQ(result.Z, 48.0f);
EXPECT_FLOAT_EQ(result.W, -6.0f);
}
{
hmm_quaternion result = q1 * q2;
EXPECT_FLOAT_EQ(result.X, 24.0f);
EXPECT_FLOAT_EQ(result.Y, 48.0f);
EXPECT_FLOAT_EQ(result.Z, 48.0f);
EXPECT_FLOAT_EQ(result.W, -6.0f);
}
// Like with matrices, we're not implementing the *=
// operator for quaternions because quaternion multiplication
// is not commutative.
#endif
}
TEST(Multiplication, QuaternionScalar)
{
hmm_quaternion q = HMM_Quaternion(1.0f, 2.0f, 3.0f, 4.0f);
float f = 2.0f;
{
hmm_quaternion result = HMM_MultiplyQuaternionF(q, f);
EXPECT_FLOAT_EQ(result.X, 2.0f);
EXPECT_FLOAT_EQ(result.Y, 4.0f);
EXPECT_FLOAT_EQ(result.Z, 6.0f);
EXPECT_FLOAT_EQ(result.W, 8.0f);
}
#ifdef __cplusplus
{
hmm_quaternion result = HMM_Multiply(q, f);
EXPECT_FLOAT_EQ(result.X, 2.0f);
EXPECT_FLOAT_EQ(result.Y, 4.0f);
EXPECT_FLOAT_EQ(result.Z, 6.0f);
EXPECT_FLOAT_EQ(result.W, 8.0f);
}
{
hmm_quaternion result = q * f;
EXPECT_FLOAT_EQ(result.X, 2.0f);
EXPECT_FLOAT_EQ(result.Y, 4.0f);
EXPECT_FLOAT_EQ(result.Z, 6.0f);
EXPECT_FLOAT_EQ(result.W, 8.0f);
}
{
hmm_quaternion result = f * q;
EXPECT_FLOAT_EQ(result.X, 2.0f);
EXPECT_FLOAT_EQ(result.Y, 4.0f);
EXPECT_FLOAT_EQ(result.Z, 6.0f);
EXPECT_FLOAT_EQ(result.W, 8.0f);
}
q *= f;
EXPECT_FLOAT_EQ(q.X, 2.0f);
EXPECT_FLOAT_EQ(q.Y, 4.0f);
EXPECT_FLOAT_EQ(q.Z, 6.0f);
EXPECT_FLOAT_EQ(q.W, 8.0f);
#endif
}

View File

@@ -0,0 +1,36 @@
#include "../HandmadeTest.h"
TEST(Projection, Orthographic)
{
hmm_mat4 projection = HMM_Orthographic(-10.0f, 10.0f, -5.0f, 5.0f, 0.0f, -10.0f);
hmm_vec3 original = HMM_Vec3(5.0f, 5.0f, -5.0f);
hmm_vec4 projected = HMM_MultiplyMat4ByVec4(projection, HMM_Vec4v(original, 1));
EXPECT_FLOAT_EQ(projected.X, 0.5f);
EXPECT_FLOAT_EQ(projected.Y, 1.0f);
EXPECT_FLOAT_EQ(projected.Z, -2.0f);
EXPECT_FLOAT_EQ(projected.W, 1.0f);
}
TEST(Projection, Perspective)
{
hmm_mat4 projection = HMM_Perspective(HMM_ToRadians(90.0f), 2.0f, 5.0f, 15.0f);
{
hmm_vec3 original = HMM_Vec3(5.0f, 5.0f, -15.0f);
hmm_vec4 projected = HMM_MultiplyMat4ByVec4(projection, HMM_Vec4v(original, 1));
EXPECT_FLOAT_EQ(projected.X, 2.5f);
EXPECT_FLOAT_EQ(projected.Y, 5.0f);
EXPECT_FLOAT_EQ(projected.Z, 15.0f);
EXPECT_FLOAT_EQ(projected.W, 15.0f);
}
{
hmm_vec3 original = HMM_Vec3(5.0f, 5.0f, -5.0f);
hmm_vec4 projected = HMM_MultiplyMat4ByVec4(projection, HMM_Vec4v(original, 1));
EXPECT_FLOAT_EQ(projected.X, 2.5f);
EXPECT_FLOAT_EQ(projected.Y, 5.0f);
EXPECT_FLOAT_EQ(projected.Z, -5.0f);
EXPECT_FLOAT_EQ(projected.W, 5.0f);
}
}

View File

@@ -0,0 +1,179 @@
#include "../HandmadeTest.h"
TEST(QuaternionOps, Inverse)
{
hmm_quaternion q1 = HMM_Quaternion(1.0f, 2.0f, 3.0f, 4.0f);
hmm_quaternion inverse = HMM_InverseQuaternion(q1);
hmm_quaternion result = HMM_MultiplyQuaternion(q1, inverse);
EXPECT_FLOAT_EQ(result.X, 0.0f);
EXPECT_FLOAT_EQ(result.Y, 0.0f);
EXPECT_FLOAT_EQ(result.Z, 0.0f);
EXPECT_FLOAT_EQ(result.W, 1.0f);
}
TEST(QuaternionOps, Dot)
{
hmm_quaternion q1 = HMM_Quaternion(1.0f, 2.0f, 3.0f, 4.0f);
hmm_quaternion q2 = HMM_Quaternion(5.0f, 6.0f, 7.0f, 8.0f);
{
float result = HMM_DotQuaternion(q1, q2);
EXPECT_FLOAT_EQ(result, 70.0f);
}
#ifdef __cplusplus
{
float result = HMM_Dot(q1, q2);
EXPECT_FLOAT_EQ(result, 70.0f);
}
#endif
}
TEST(QuaternionOps, Normalize)
{
hmm_quaternion q = HMM_Quaternion(1.0f, 2.0f, 3.0f, 4.0f);
{
hmm_quaternion result = HMM_NormalizeQuaternion(q);
EXPECT_FLOAT_EQ(result.X, 0.1825741858f);
EXPECT_FLOAT_EQ(result.Y, 0.3651483717f);
EXPECT_FLOAT_EQ(result.Z, 0.5477225575f);
EXPECT_FLOAT_EQ(result.W, 0.7302967433f);
}
#ifdef __cplusplus
{
hmm_quaternion result = HMM_Normalize(q);
EXPECT_FLOAT_EQ(result.X, 0.1825741858f);
EXPECT_FLOAT_EQ(result.Y, 0.3651483717f);
EXPECT_FLOAT_EQ(result.Z, 0.5477225575f);
EXPECT_FLOAT_EQ(result.W, 0.7302967433f);
}
#endif
}
TEST(QuaternionOps, NLerp)
{
hmm_quaternion from = HMM_Quaternion(0.0f, 0.0f, 0.0f, 1.0f);
hmm_quaternion to = HMM_Quaternion(0.5f, 0.5f, -0.5f, 0.5f);
hmm_quaternion result = HMM_NLerp(from, 0.5f, to);
EXPECT_FLOAT_EQ(result.X, 0.28867513f);
EXPECT_FLOAT_EQ(result.Y, 0.28867513f);
EXPECT_FLOAT_EQ(result.Z, -0.28867513f);
EXPECT_FLOAT_EQ(result.W, 0.86602540f);
}
TEST(QuaternionOps, Slerp)
{
hmm_quaternion from = HMM_Quaternion(0.0f, 0.0f, 0.0f, 1.0f);
hmm_quaternion to = HMM_Quaternion(0.5f, 0.5f, -0.5f, 0.5f);
hmm_quaternion result = HMM_Slerp(from, 0.5f, to);
EXPECT_FLOAT_EQ(result.X, 0.28867513f);
EXPECT_FLOAT_EQ(result.Y, 0.28867513f);
EXPECT_FLOAT_EQ(result.Z, -0.28867513f);
EXPECT_FLOAT_EQ(result.W, 0.86602540f);
}
TEST(QuaternionOps, QuatToMat4)
{
const float abs_error = 0.0001f;
hmm_quaternion rot = HMM_Quaternion(0.707107f, 0.0f, 0.0f, 0.707107f);
hmm_mat4 result = HMM_QuaternionToMat4(rot);
EXPECT_NEAR(result.Elements[0][0], 1.0f, abs_error);
EXPECT_NEAR(result.Elements[0][1], 0.0f, abs_error);
EXPECT_NEAR(result.Elements[0][2], 0.0f, abs_error);
EXPECT_NEAR(result.Elements[0][3], 0.0f, abs_error);
EXPECT_NEAR(result.Elements[1][0], 0.0f, abs_error);
EXPECT_NEAR(result.Elements[1][1], 0.0f, abs_error);
EXPECT_NEAR(result.Elements[1][2], 1.0f, abs_error);
EXPECT_NEAR(result.Elements[1][3], 0.0f, abs_error);
EXPECT_NEAR(result.Elements[2][0], 0.0f, abs_error);
EXPECT_NEAR(result.Elements[2][1], -1.0f, abs_error);
EXPECT_NEAR(result.Elements[2][2], 0.0f, abs_error);
EXPECT_NEAR(result.Elements[2][3], 0.0f, abs_error);
EXPECT_NEAR(result.Elements[3][0], 0.0f, abs_error);
EXPECT_NEAR(result.Elements[3][1], 0.0f, abs_error);
EXPECT_NEAR(result.Elements[3][2], 0.0f, abs_error);
EXPECT_NEAR(result.Elements[3][3], 1.0f, abs_error);
}
TEST(QuaternionOps, Mat4ToQuat)
{
const float abs_error = 0.0001f;
// Rotate 90 degrees on the X axis
{
hmm_mat4 m = HMM_Rotate(HMM_ToRadians(90.0f), HMM_Vec3(1, 0, 0));
hmm_quaternion result = HMM_Mat4ToQuaternion(m);
float cosf = 0.707107f; // cos(90/2 degrees)
float sinf = 0.707107f; // sin(90/2 degrees)
EXPECT_NEAR(result.X, sinf, abs_error);
EXPECT_NEAR(result.Y, 0.0f, abs_error);
EXPECT_NEAR(result.Z, 0.0f, abs_error);
EXPECT_NEAR(result.W, cosf, abs_error);
}
// Rotate 90 degrees on the Y axis (axis not normalized, just for fun)
{
hmm_mat4 m = HMM_Rotate(HMM_ToRadians(90.0f), HMM_Vec3(0, 2, 0));
hmm_quaternion result = HMM_Mat4ToQuaternion(m);
float cosf = 0.707107f; // cos(90/2 degrees)
float sinf = 0.707107f; // sin(90/2 degrees)
EXPECT_NEAR(result.X, 0.0f, abs_error);
EXPECT_NEAR(result.Y, sinf, abs_error);
EXPECT_NEAR(result.Z, 0.0f, abs_error);
EXPECT_NEAR(result.W, cosf, abs_error);
}
// Rotate 90 degrees on the Z axis
{
hmm_mat4 m = HMM_Rotate(HMM_ToRadians(90.0f), HMM_Vec3(0, 0, 1));
hmm_quaternion result = HMM_Mat4ToQuaternion(m);
float cosf = 0.707107f; // cos(90/2 degrees)
float sinf = 0.707107f; // sin(90/2 degrees)
EXPECT_NEAR(result.X, 0.0f, abs_error);
EXPECT_NEAR(result.Y, 0.0f, abs_error);
EXPECT_NEAR(result.Z, sinf, abs_error);
EXPECT_NEAR(result.W, cosf, abs_error);
}
// Rotate 45 degrees on the X axis (this hits case 4)
{
hmm_mat4 m = HMM_Rotate(HMM_ToRadians(45.0f), HMM_Vec3(1, 0, 0));
hmm_quaternion result = HMM_Mat4ToQuaternion(m);
float cosf = 0.9238795325f; // cos(90/2 degrees)
float sinf = 0.3826834324f; // sin(90/2 degrees)
EXPECT_NEAR(result.X, sinf, abs_error);
EXPECT_NEAR(result.Y, 0.0f, abs_error);
EXPECT_NEAR(result.Z, 0.0f, abs_error);
EXPECT_NEAR(result.W, cosf, abs_error);
}
}
TEST(QuaternionOps, FromAxisAngle)
{
hmm_vec3 axis = HMM_Vec3(1.0f, 0.0f, 0.0f);
float angle = HMM_PI32 / 2.0f;
hmm_quaternion result = HMM_QuaternionFromAxisAngle(axis, angle);
EXPECT_NEAR(result.X, 0.707107f, FLT_EPSILON * 2);
EXPECT_FLOAT_EQ(result.Y, 0.0f);
EXPECT_FLOAT_EQ(result.Z, 0.0f);
EXPECT_NEAR(result.W, 0.707107f, FLT_EPSILON * 2);
}

39
test/categories/SSE.h Normal file
View File

@@ -0,0 +1,39 @@
#include "../HandmadeTest.h"
#ifdef HANDMADE_MATH__USE_SSE
TEST(SSE, LinearCombine)
{
hmm_mat4 MatrixOne = HMM_Mat4d(2.0f);
hmm_mat4 MatrixTwo = HMM_Mat4d(4.0f);
hmm_mat4 Result;
Result.Columns[0] = HMM_LinearCombineSSE(MatrixOne.Columns[0], MatrixTwo);
Result.Columns[1] = HMM_LinearCombineSSE(MatrixOne.Columns[1], MatrixTwo);
Result.Columns[2] = HMM_LinearCombineSSE(MatrixOne.Columns[2], MatrixTwo);
Result.Columns[3] = HMM_LinearCombineSSE(MatrixOne.Columns[3], MatrixTwo);
{
EXPECT_FLOAT_EQ(Result.Elements[0][0], 8.0f);
EXPECT_FLOAT_EQ(Result.Elements[0][1], 0.0f);
EXPECT_FLOAT_EQ(Result.Elements[0][2], 0.0f);
EXPECT_FLOAT_EQ(Result.Elements[0][3], 0.0f);
EXPECT_FLOAT_EQ(Result.Elements[1][0], 0.0f);
EXPECT_FLOAT_EQ(Result.Elements[1][1], 8.0f);
EXPECT_FLOAT_EQ(Result.Elements[1][2], 0.0f);
EXPECT_FLOAT_EQ(Result.Elements[1][3], 0.0f);
EXPECT_FLOAT_EQ(Result.Elements[2][0], 0.0f);
EXPECT_FLOAT_EQ(Result.Elements[2][1], 0.0f);
EXPECT_FLOAT_EQ(Result.Elements[2][2], 8.0f);
EXPECT_FLOAT_EQ(Result.Elements[2][3], 0.0f);
EXPECT_FLOAT_EQ(Result.Elements[3][0], 0.0f);
EXPECT_FLOAT_EQ(Result.Elements[3][1], 0.0f);
EXPECT_FLOAT_EQ(Result.Elements[3][2], 0.0f);
EXPECT_FLOAT_EQ(Result.Elements[3][3], 8.0f);
}
}
#endif

View File

@@ -0,0 +1,101 @@
#include "../HandmadeTest.h"
TEST(ScalarMath, Trigonometry)
{
// We have to be a little looser with our equality constraint
// because of floating-point precision issues.
const float trigAbsError = 0.0001f;
EXPECT_NEAR(HMM_SinF(0.0f), 0.0f, trigAbsError);
EXPECT_NEAR(HMM_SinF(HMM_PI32 / 2), 1.0f, trigAbsError);
EXPECT_NEAR(HMM_SinF(HMM_PI32), 0.0f, trigAbsError);
EXPECT_NEAR(HMM_SinF(3 * HMM_PI32 / 2), -1.0f, trigAbsError);
EXPECT_NEAR(HMM_SinF(-HMM_PI32 / 2), -1.0f, trigAbsError);
EXPECT_NEAR(HMM_CosF(0.0f), 1.0f, trigAbsError);
EXPECT_NEAR(HMM_CosF(HMM_PI32 / 2), 0.0f, trigAbsError);
EXPECT_NEAR(HMM_CosF(HMM_PI32), -1.0f, trigAbsError);
EXPECT_NEAR(HMM_CosF(3 * HMM_PI32 / 2), 0.0f, trigAbsError);
EXPECT_NEAR(HMM_CosF(-HMM_PI32), -1.0f, trigAbsError);
EXPECT_NEAR(HMM_TanF(0.0f), 0.0f, trigAbsError);
EXPECT_NEAR(HMM_TanF(HMM_PI32 / 4), 1.0f, trigAbsError);
EXPECT_NEAR(HMM_TanF(3 * HMM_PI32 / 4), -1.0f, trigAbsError);
EXPECT_NEAR(HMM_TanF(HMM_PI32), 0.0f, trigAbsError);
EXPECT_NEAR(HMM_TanF(-HMM_PI32 / 4), -1.0f, trigAbsError);
EXPECT_NEAR(HMM_ATanF(0.0f), 0.0f, trigAbsError);
EXPECT_NEAR(HMM_ATanF(HMM_PI32), 1.2626272557f, trigAbsError);
EXPECT_NEAR(HMM_ATanF(-HMM_PI32), -1.2626272557f, trigAbsError);
EXPECT_NEAR(HMM_ATan2F(0.0f, 1.0f), 0.0f, trigAbsError);
EXPECT_NEAR(HMM_ATan2F(1.0f, 1.0f), HMM_PI32 / 4.0f, trigAbsError);
EXPECT_NEAR(HMM_ATan2F(1.0f, 0.0f), HMM_PI32 / 2.0f, trigAbsError);
// This isn't the most rigorous because we're really just sanity-
// checking that things work by default.
}
TEST(ScalarMath, ToDegrees)
{
EXPECT_FLOAT_EQ(HMM_ToDegrees(0.0f), 0.0f);
EXPECT_FLOAT_EQ(HMM_ToDegrees(HMM_PI32), 180.0f);
EXPECT_FLOAT_EQ(HMM_ToDegrees(-HMM_PI32), -180.0f);
}
TEST(ScalarMath, ToRadians)
{
EXPECT_FLOAT_EQ(HMM_ToRadians(0.0f), 0.0f);
EXPECT_FLOAT_EQ(HMM_ToRadians(180.0f), HMM_PI32);
EXPECT_FLOAT_EQ(HMM_ToRadians(-180.0f), -HMM_PI32);
}
TEST(ScalarMath, ExpF)
{
EXPECT_NEAR(HMM_ExpF(0.0f), 1.0f, 0.0001f);
EXPECT_NEAR(HMM_ExpF(1.0f), 2.7182818285f, 0.0001f);
}
TEST(ScalarMath, LogF)
{
EXPECT_NEAR(HMM_LogF(1.0f), 0.0f, 0.0001f);
EXPECT_NEAR(HMM_LogF(2.7182818285f), 1.0f, 0.0001f);
}
TEST(ScalarMath, SquareRoot)
{
EXPECT_FLOAT_EQ(HMM_SquareRootF(16.0f), 4.0f);
}
TEST(ScalarMath, RSquareRootF)
{
EXPECT_NEAR(HMM_RSquareRootF(10.0f), 0.31616211f, 0.0001f);
}
TEST(ScalarMath, Power)
{
EXPECT_FLOAT_EQ(HMM_Power(2.0f, 0), 1.0f);
EXPECT_FLOAT_EQ(HMM_Power(2.0f, 4), 16.0f);
EXPECT_FLOAT_EQ(HMM_Power(2.0f, -2), 0.25f);
}
TEST(ScalarMath, PowerF)
{
EXPECT_FLOAT_EQ(HMM_PowerF(2.0f, 0.0f), 1.0f);
EXPECT_NEAR(HMM_PowerF(2.0f, 4.1f), 17.148376f, 0.0001f);
EXPECT_NEAR(HMM_PowerF(2.0f, -2.5f), 0.176777f, 0.0001f);
}
TEST(ScalarMath, Lerp)
{
EXPECT_FLOAT_EQ(HMM_Lerp(-2.0f, 0.0f, 2.0f), -2.0f);
EXPECT_FLOAT_EQ(HMM_Lerp(-2.0f, 0.5f, 2.0f), 0.0f);
EXPECT_FLOAT_EQ(HMM_Lerp(-2.0f, 1.0f, 2.0f), 2.0f);
}
TEST(ScalarMath, Clamp)
{
EXPECT_FLOAT_EQ(HMM_Clamp(-2.0f, 0.0f, 2.0f), 0.0f);
EXPECT_FLOAT_EQ(HMM_Clamp(-2.0f, -3.0f, 2.0f), -2.0f);
EXPECT_FLOAT_EQ(HMM_Clamp(-2.0f, 3.0f, 2.0f), 2.0f);
}

View File

@@ -0,0 +1,201 @@
#include "../HandmadeTest.h"
TEST(Subtraction, Vec2)
{
hmm_vec2 v2_1 = HMM_Vec2(1.0f, 2.0f);
hmm_vec2 v2_2 = HMM_Vec2(3.0f, 4.0f);
{
hmm_vec2 result = HMM_SubtractVec2(v2_1, v2_2);
EXPECT_FLOAT_EQ(result.X, -2.0f);
EXPECT_FLOAT_EQ(result.Y, -2.0f);
}
#ifdef __cplusplus
{
hmm_vec2 result = HMM_Subtract(v2_1, v2_2);
EXPECT_FLOAT_EQ(result.X, -2.0f);
EXPECT_FLOAT_EQ(result.Y, -2.0f);
}
{
hmm_vec2 result = v2_1 - v2_2;
EXPECT_FLOAT_EQ(result.X, -2.0f);
EXPECT_FLOAT_EQ(result.Y, -2.0f);
}
v2_1 -= v2_2;
EXPECT_FLOAT_EQ(v2_1.X, -2.0f);
EXPECT_FLOAT_EQ(v2_1.Y, -2.0f);
#endif
}
TEST(Subtraction, Vec3)
{
hmm_vec3 v3_1 = HMM_Vec3(1.0f, 2.0f, 3.0f);
hmm_vec3 v3_2 = HMM_Vec3(4.0f, 5.0f, 6.0f);
{
hmm_vec3 result = HMM_SubtractVec3(v3_1, v3_2);
EXPECT_FLOAT_EQ(result.X, -3.0f);
EXPECT_FLOAT_EQ(result.Y, -3.0f);
EXPECT_FLOAT_EQ(result.Z, -3.0f);
}
#ifdef __cplusplus
{
hmm_vec3 result = HMM_Subtract(v3_1, v3_2);
EXPECT_FLOAT_EQ(result.X, -3.0f);
EXPECT_FLOAT_EQ(result.Y, -3.0f);
EXPECT_FLOAT_EQ(result.Z, -3.0f);
}
{
hmm_vec3 result = v3_1 - v3_2;
EXPECT_FLOAT_EQ(result.X, -3.0f);
EXPECT_FLOAT_EQ(result.Y, -3.0f);
EXPECT_FLOAT_EQ(result.Z, -3.0f);
}
v3_1 -= v3_2;
EXPECT_FLOAT_EQ(v3_1.X, -3.0f);
EXPECT_FLOAT_EQ(v3_1.Y, -3.0f);
EXPECT_FLOAT_EQ(v3_1.Z, -3.0f);
#endif
}
TEST(Subtraction, Vec4)
{
hmm_vec4 v4_1 = HMM_Vec4(1.0f, 2.0f, 3.0f, 4.0f);
hmm_vec4 v4_2 = HMM_Vec4(5.0f, 6.0f, 7.0f, 8.0f);
{
hmm_vec4 result = HMM_SubtractVec4(v4_1, v4_2);
EXPECT_FLOAT_EQ(result.X, -4.0f);
EXPECT_FLOAT_EQ(result.Y, -4.0f);
EXPECT_FLOAT_EQ(result.Z, -4.0f);
EXPECT_FLOAT_EQ(result.W, -4.0f);
}
#ifdef __cplusplus
{
hmm_vec4 result = HMM_Subtract(v4_1, v4_2);
EXPECT_FLOAT_EQ(result.X, -4.0f);
EXPECT_FLOAT_EQ(result.Y, -4.0f);
EXPECT_FLOAT_EQ(result.Z, -4.0f);
EXPECT_FLOAT_EQ(result.W, -4.0f);
}
{
hmm_vec4 result = v4_1 - v4_2;
EXPECT_FLOAT_EQ(result.X, -4.0f);
EXPECT_FLOAT_EQ(result.Y, -4.0f);
EXPECT_FLOAT_EQ(result.Z, -4.0f);
EXPECT_FLOAT_EQ(result.W, -4.0f);
}
v4_1 -= v4_2;
EXPECT_FLOAT_EQ(v4_1.X, -4.0f);
EXPECT_FLOAT_EQ(v4_1.Y, -4.0f);
EXPECT_FLOAT_EQ(v4_1.Z, -4.0f);
EXPECT_FLOAT_EQ(v4_1.W, -4.0f);
#endif
}
TEST(Subtraction, Mat4)
{
hmm_mat4 m4_1 = HMM_Mat4(); // will have 1 - 16
hmm_mat4 m4_2 = HMM_Mat4(); // will have 17 - 32
// Fill the matrices
int Counter = 1;
for (int Column = 0; Column < 4; ++Column)
{
for (int Row = 0; Row < 4; ++Row)
{
m4_1.Elements[Column][Row] = Counter;
++Counter;
}
}
for (int Column = 0; Column < 4; ++Column)
{
for (int Row = 0; Row < 4; ++Row)
{
m4_2.Elements[Column][Row] = Counter;
++Counter;
}
}
// Test the results
{
hmm_mat4 result = HMM_SubtractMat4(m4_1, m4_2);
for (int Column = 0; Column < 4; ++Column)
{
for (int Row = 0; Row < 4; ++Row)
{
EXPECT_FLOAT_EQ(result.Elements[Column][Row], -16.0f);
}
}
}
#ifdef __cplusplus
{
hmm_mat4 result = HMM_Subtract(m4_1, m4_2);
for (int Column = 0; Column < 4; ++Column)
{
for (int Row = 0; Row < 4; ++Row)
{
EXPECT_FLOAT_EQ(result.Elements[Column][Row], -16.0f);
}
}
}
{
hmm_mat4 result = m4_1 - m4_2;
for (int Column = 0; Column < 4; ++Column)
{
for (int Row = 0; Row < 4; ++Row)
{
EXPECT_FLOAT_EQ(result.Elements[Column][Row], -16.0f);
}
}
}
m4_1 -= m4_2;
for (int Column = 0; Column < 4; ++Column)
{
for (int Row = 0; Row < 4; ++Row)
{
EXPECT_FLOAT_EQ(m4_1.Elements[Column][Row], -16.0f);
}
}
#endif
}
TEST(Subtraction, Quaternion)
{
hmm_quaternion q1 = HMM_Quaternion(1.0f, 2.0f, 3.0f, 4.0f);
hmm_quaternion q2 = HMM_Quaternion(5.0f, 6.0f, 7.0f, 8.0f);
{
hmm_quaternion result = HMM_SubtractQuaternion(q1, q2);
EXPECT_FLOAT_EQ(result.X, -4.0f);
EXPECT_FLOAT_EQ(result.Y, -4.0f);
EXPECT_FLOAT_EQ(result.Z, -4.0f);
EXPECT_FLOAT_EQ(result.W, -4.0f);
}
#ifdef __cplusplus
{
hmm_quaternion result = HMM_Subtract(q1, q2);
EXPECT_FLOAT_EQ(result.X, -4.0f);
EXPECT_FLOAT_EQ(result.Y, -4.0f);
EXPECT_FLOAT_EQ(result.Z, -4.0f);
EXPECT_FLOAT_EQ(result.W, -4.0f);
}
{
hmm_quaternion result = q1 - q2;
EXPECT_FLOAT_EQ(result.X, -4.0f);
EXPECT_FLOAT_EQ(result.Y, -4.0f);
EXPECT_FLOAT_EQ(result.Z, -4.0f);
EXPECT_FLOAT_EQ(result.W, -4.0f);
}
q1 -= q2;
EXPECT_FLOAT_EQ(q1.X, -4.0f);
EXPECT_FLOAT_EQ(q1.Y, -4.0f);
EXPECT_FLOAT_EQ(q1.Z, -4.0f);
EXPECT_FLOAT_EQ(q1.W, -4.0f);
#endif
}

View File

@@ -0,0 +1,79 @@
#include "../HandmadeTest.h"
TEST(Transformations, Translate)
{
hmm_mat4 translate = HMM_Translate(HMM_Vec3(1.0f, -3.0f, 6.0f));
hmm_vec3 original = HMM_Vec3(1.0f, 2.0f, 3.0f);
hmm_vec4 translated = HMM_MultiplyMat4ByVec4(translate, HMM_Vec4v(original, 1));
EXPECT_FLOAT_EQ(translated.X, 2.0f);
EXPECT_FLOAT_EQ(translated.Y, -1.0f);
EXPECT_FLOAT_EQ(translated.Z, 9.0f);
EXPECT_FLOAT_EQ(translated.W, 1.0f);
}
TEST(Transformations, Rotate)
{
hmm_vec3 original = HMM_Vec3(1.0f, 1.0f, 1.0f);
float angle = HMM_ToRadians(90.0f);
hmm_mat4 rotateX = HMM_Rotate(angle, HMM_Vec3(1, 0, 0));
hmm_vec4 rotatedX = HMM_MultiplyMat4ByVec4(rotateX, HMM_Vec4v(original, 1));
EXPECT_FLOAT_EQ(rotatedX.X, 1.0f);
EXPECT_FLOAT_EQ(rotatedX.Y, -1.0f);
EXPECT_FLOAT_EQ(rotatedX.Z, 1.0f);
EXPECT_FLOAT_EQ(rotatedX.W, 1.0f);
hmm_mat4 rotateY = HMM_Rotate(angle, HMM_Vec3(0, 1, 0));
hmm_vec4 rotatedY = HMM_MultiplyMat4ByVec4(rotateY, HMM_Vec4v(original, 1));
EXPECT_FLOAT_EQ(rotatedY.X, 1.0f);
EXPECT_FLOAT_EQ(rotatedY.Y, 1.0f);
EXPECT_FLOAT_EQ(rotatedY.Z, -1.0f);
EXPECT_FLOAT_EQ(rotatedY.W, 1.0f);
hmm_mat4 rotateZ = HMM_Rotate(angle, HMM_Vec3(0, 0, 1));
hmm_vec4 rotatedZ = HMM_MultiplyMat4ByVec4(rotateZ, HMM_Vec4v(original, 1));
EXPECT_FLOAT_EQ(rotatedZ.X, -1.0f);
EXPECT_FLOAT_EQ(rotatedZ.Y, 1.0f);
EXPECT_FLOAT_EQ(rotatedZ.Z, 1.0f);
EXPECT_FLOAT_EQ(rotatedZ.W, 1.0f);
}
TEST(Transformations, Scale)
{
hmm_mat4 scale = HMM_Scale(HMM_Vec3(2.0f, -3.0f, 0.5f));
hmm_vec3 original = HMM_Vec3(1.0f, 2.0f, 3.0f);
hmm_vec4 scaled = HMM_MultiplyMat4ByVec4(scale, HMM_Vec4v(original, 1));
EXPECT_FLOAT_EQ(scaled.X, 2.0f);
EXPECT_FLOAT_EQ(scaled.Y, -6.0f);
EXPECT_FLOAT_EQ(scaled.Z, 1.5f);
EXPECT_FLOAT_EQ(scaled.W, 1.0f);
}
TEST(Transformations, LookAt)
{
const float abs_error = 0.0001f;
hmm_mat4 result = HMM_LookAt(HMM_Vec3(1.0f, 0.0f, 0.0f), HMM_Vec3(0.0f, 2.0f, 1.0f), HMM_Vec3(2.0f, 1.0f, 1.0f));
EXPECT_NEAR(result.Elements[0][0], 0.169031f, abs_error);
EXPECT_NEAR(result.Elements[0][1], 0.897085f, abs_error);
EXPECT_NEAR(result.Elements[0][2], 0.408248f, abs_error);
EXPECT_FLOAT_EQ(result.Elements[0][3], 0.0f);
EXPECT_NEAR(result.Elements[1][0], 0.507093f, abs_error);
EXPECT_NEAR(result.Elements[1][1], 0.276026f, abs_error);
EXPECT_NEAR(result.Elements[1][2], -0.816497f, abs_error);
EXPECT_FLOAT_EQ(result.Elements[1][3], 0.0f);
EXPECT_NEAR(result.Elements[2][0], -0.845154f, abs_error);
EXPECT_NEAR(result.Elements[2][1], 0.345033f, abs_error);
EXPECT_NEAR(result.Elements[2][2], -0.408248f, abs_error);
EXPECT_FLOAT_EQ(result.Elements[2][3], 0.0f);
EXPECT_NEAR(result.Elements[3][0], -0.169031f, abs_error);
EXPECT_NEAR(result.Elements[3][1], -0.897085f, abs_error);
EXPECT_NEAR(result.Elements[3][2], -0.408248f, abs_error);
EXPECT_FLOAT_EQ(result.Elements[3][3], 1.0f);
}

320
test/categories/VectorOps.h Normal file
View File

@@ -0,0 +1,320 @@
#include "../HandmadeTest.h"
TEST(VectorOps, LengthSquared)
{
hmm_vec2 v2 = HMM_Vec2(1.0f, -2.0f);
hmm_vec3 v3 = HMM_Vec3(1.0f, -2.0f, 3.0f);
hmm_vec4 v4 = HMM_Vec4(1.0f, -2.0f, 3.0f, 1.0f);
EXPECT_FLOAT_EQ(HMM_LengthSquaredVec2(v2), 5.0f);
EXPECT_FLOAT_EQ(HMM_LengthSquaredVec3(v3), 14.0f);
EXPECT_FLOAT_EQ(HMM_LengthSquaredVec4(v4), 15.0f);
#ifdef __cplusplus
EXPECT_FLOAT_EQ(HMM_LengthSquared(v2), 5.0f);
EXPECT_FLOAT_EQ(HMM_LengthSquared(v3), 14.0f);
EXPECT_FLOAT_EQ(HMM_LengthSquared(v4), 15.0f);
#endif
}
TEST(VectorOps, Length)
{
hmm_vec2 v2 = HMM_Vec2(1.0f, -9.0f);
hmm_vec3 v3 = HMM_Vec3(2.0f, -3.0f, 6.0f);
hmm_vec4 v4 = HMM_Vec4(2.0f, -3.0f, 6.0f, 12.0f);
EXPECT_FLOAT_EQ(HMM_LengthVec2(v2), 9.0553856f);
EXPECT_FLOAT_EQ(HMM_LengthVec3(v3), 7.0f);
EXPECT_FLOAT_EQ(HMM_LengthVec4(v4), 13.892444f);
#ifdef __cplusplus
EXPECT_FLOAT_EQ(HMM_Length(v2), 9.0553856f);
EXPECT_FLOAT_EQ(HMM_Length(v3), 7.0f);
EXPECT_FLOAT_EQ(HMM_Length(v4), 13.892444f);
#endif
}
TEST(VectorOps, Normalize)
{
hmm_vec2 v2 = HMM_Vec2(1.0f, -2.0f);
hmm_vec3 v3 = HMM_Vec3(1.0f, -2.0f, 3.0f);
hmm_vec4 v4 = HMM_Vec4(1.0f, -2.0f, 3.0f, -1.0f);
{
hmm_vec2 result = HMM_NormalizeVec2(v2);
EXPECT_FLOAT_EQ(HMM_LengthVec2(result), 1.0f);
EXPECT_GT(result.X, 0.0f);
EXPECT_LT(result.Y, 0.0f);
}
{
hmm_vec3 result = HMM_NormalizeVec3(v3);
EXPECT_FLOAT_EQ(HMM_LengthVec3(result), 1.0f);
EXPECT_GT(result.X, 0.0f);
EXPECT_LT(result.Y, 0.0f);
EXPECT_GT(result.Z, 0.0f);
}
{
hmm_vec4 result = HMM_NormalizeVec4(v4);
EXPECT_FLOAT_EQ(HMM_LengthVec4(result), 1.0f);
EXPECT_GT(result.X, 0.0f);
EXPECT_LT(result.Y, 0.0f);
EXPECT_GT(result.Z, 0.0f);
EXPECT_LT(result.W, 0.0f);
}
#ifdef __cplusplus
{
hmm_vec2 result = HMM_Normalize(v2);
EXPECT_FLOAT_EQ(HMM_LengthVec2(result), 1.0f);
EXPECT_GT(result.X, 0.0f);
EXPECT_LT(result.Y, 0.0f);
}
{
hmm_vec3 result = HMM_Normalize(v3);
EXPECT_FLOAT_EQ(HMM_LengthVec3(result), 1.0f);
EXPECT_GT(result.X, 0.0f);
EXPECT_LT(result.Y, 0.0f);
EXPECT_GT(result.Z, 0.0f);
}
{
hmm_vec4 result = HMM_Normalize(v4);
EXPECT_FLOAT_EQ(HMM_LengthVec4(result), 1.0f);
EXPECT_GT(result.X, 0.0f);
EXPECT_LT(result.Y, 0.0f);
EXPECT_GT(result.Z, 0.0f);
EXPECT_LT(result.W, 0.0f);
}
#endif
}
TEST(VectorOps, NormalizeZero)
{
hmm_vec2 v2 = HMM_Vec2(0.0f, 0.0f);
hmm_vec3 v3 = HMM_Vec3(0.0f, 0.0f, 0.0f);
hmm_vec4 v4 = HMM_Vec4(0.0f, 0.0f, 0.0f, 0.0f);
{
hmm_vec2 result = HMM_NormalizeVec2(v2);
EXPECT_FLOAT_EQ(result.X, 0.0f);
EXPECT_FLOAT_EQ(result.Y, 0.0f);
}
{
hmm_vec3 result = HMM_NormalizeVec3(v3);
EXPECT_FLOAT_EQ(result.X, 0.0f);
EXPECT_FLOAT_EQ(result.Y, 0.0f);
EXPECT_FLOAT_EQ(result.Z, 0.0f);
}
{
hmm_vec4 result = HMM_NormalizeVec4(v4);
EXPECT_FLOAT_EQ(result.X, 0.0f);
EXPECT_FLOAT_EQ(result.Y, 0.0f);
EXPECT_FLOAT_EQ(result.Z, 0.0f);
EXPECT_FLOAT_EQ(result.W, 0.0f);
}
#ifdef __cplusplus
{
hmm_vec2 result = HMM_Normalize(v2);
EXPECT_FLOAT_EQ(result.X, 0.0f);
EXPECT_FLOAT_EQ(result.Y, 0.0f);
}
{
hmm_vec3 result = HMM_Normalize(v3);
EXPECT_FLOAT_EQ(result.X, 0.0f);
EXPECT_FLOAT_EQ(result.Y, 0.0f);
EXPECT_FLOAT_EQ(result.Z, 0.0f);
}
{
hmm_vec4 result = HMM_Normalize(v4);
EXPECT_FLOAT_EQ(result.X, 0.0f);
EXPECT_FLOAT_EQ(result.Y, 0.0f);
EXPECT_FLOAT_EQ(result.Z, 0.0f);
EXPECT_FLOAT_EQ(result.W, 0.0f);
}
#endif
}
TEST(VectorOps, FastNormalize)
{
hmm_vec2 v2 = HMM_Vec2(1.0f, -2.0f);
hmm_vec3 v3 = HMM_Vec3(1.0f, -2.0f, 3.0f);
hmm_vec4 v4 = HMM_Vec4(1.0f, -2.0f, 3.0f, -1.0f);
{
hmm_vec2 result = HMM_FastNormalizeVec2(v2);
EXPECT_NEAR(HMM_LengthVec2(result), 1.0f, 0.001f);
EXPECT_GT(result.X, 0.0f);
EXPECT_LT(result.Y, 0.0f);
}
{
hmm_vec3 result = HMM_FastNormalizeVec3(v3);
EXPECT_NEAR(HMM_LengthVec3(result), 1.0f, 0.001f);
EXPECT_GT(result.X, 0.0f);
EXPECT_LT(result.Y, 0.0f);
EXPECT_GT(result.Z, 0.0f);
}
{
hmm_vec4 result = HMM_FastNormalizeVec4(v4);
EXPECT_NEAR(HMM_LengthVec4(result), 1.0f, 0.001f);
EXPECT_GT(result.X, 0.0f);
EXPECT_LT(result.Y, 0.0f);
EXPECT_GT(result.Z, 0.0f);
EXPECT_LT(result.W, 0.0f);
}
#ifdef __cplusplus
{
hmm_vec2 result = HMM_FastNormalize(v2);
EXPECT_NEAR(HMM_LengthVec2(result), 1.0f, 0.001f);
EXPECT_GT(result.X, 0.0f);
EXPECT_LT(result.Y, 0.0f);
}
{
hmm_vec3 result = HMM_FastNormalize(v3);
EXPECT_NEAR(HMM_LengthVec3(result), 1.0f, 0.001f);
EXPECT_GT(result.X, 0.0f);
EXPECT_LT(result.Y, 0.0f);
EXPECT_GT(result.Z, 0.0f);
}
{
hmm_vec4 result = HMM_FastNormalize(v4);
EXPECT_NEAR(HMM_LengthVec4(result), 1.0f, 0.001f);
EXPECT_GT(result.X, 0.0f);
EXPECT_LT(result.Y, 0.0f);
EXPECT_GT(result.Z, 0.0f);
EXPECT_LT(result.W, 0.0f);
}
#endif
}
TEST(VectorOps, FastNormalizeZero)
{
hmm_vec2 v2 = HMM_Vec2(0.0f, 0.0f);
hmm_vec3 v3 = HMM_Vec3(0.0f, 0.0f, 0.0f);
hmm_vec4 v4 = HMM_Vec4(0.0f, 0.0f, 0.0f, 0.0f);
{
hmm_vec2 result = HMM_FastNormalizeVec2(v2);
EXPECT_FLOAT_EQ(result.X, 0.0f);
EXPECT_FLOAT_EQ(result.Y, 0.0f);
}
{
hmm_vec3 result = HMM_FastNormalizeVec3(v3);
EXPECT_FLOAT_EQ(result.X, 0.0f);
EXPECT_FLOAT_EQ(result.Y, 0.0f);
EXPECT_FLOAT_EQ(result.Z, 0.0f);
}
{
hmm_vec4 result = HMM_FastNormalizeVec4(v4);
EXPECT_FLOAT_EQ(result.X, 0.0f);
EXPECT_FLOAT_EQ(result.Y, 0.0f);
EXPECT_FLOAT_EQ(result.Z, 0.0f);
EXPECT_FLOAT_EQ(result.W, 0.0f);
}
#ifdef __cplusplus
{
hmm_vec2 result = HMM_FastNormalize(v2);
EXPECT_FLOAT_EQ(result.X, 0.0f);
EXPECT_FLOAT_EQ(result.Y, 0.0f);
}
{
hmm_vec3 result = HMM_FastNormalize(v3);
EXPECT_FLOAT_EQ(result.X, 0.0f);
EXPECT_FLOAT_EQ(result.Y, 0.0f);
EXPECT_FLOAT_EQ(result.Z, 0.0f);
}
{
hmm_vec4 result = HMM_FastNormalize(v4);
EXPECT_FLOAT_EQ(result.X, 0.0f);
EXPECT_FLOAT_EQ(result.Y, 0.0f);
EXPECT_FLOAT_EQ(result.Z, 0.0f);
EXPECT_FLOAT_EQ(result.W, 0.0f);
}
#endif
}
TEST(VectorOps, Cross)
{
hmm_vec3 v1 = HMM_Vec3(1.0f, 2.0f, 3.0f);
hmm_vec3 v2 = HMM_Vec3(4.0f, 5.0f, 6.0f);
hmm_vec3 result = HMM_Cross(v1, v2);
EXPECT_FLOAT_EQ(result.X, -3.0f);
EXPECT_FLOAT_EQ(result.Y, 6.0f);
EXPECT_FLOAT_EQ(result.Z, -3.0f);
}
TEST(VectorOps, DotVec2)
{
hmm_vec2 v1 = HMM_Vec2(1.0f, 2.0f);
hmm_vec2 v2 = HMM_Vec2(3.0f, 4.0f);
EXPECT_FLOAT_EQ(HMM_DotVec2(v1, v2), 11.0f);
#ifdef __cplusplus
EXPECT_FLOAT_EQ(HMM_Dot(v1, v2), 11.0f);
#endif
}
TEST(VectorOps, DotVec3)
{
hmm_vec3 v1 = HMM_Vec3(1.0f, 2.0f, 3.0f);
hmm_vec3 v2 = HMM_Vec3(4.0f, 5.0f, 6.0f);
EXPECT_FLOAT_EQ(HMM_DotVec3(v1, v2), 32.0f);
#ifdef __cplusplus
EXPECT_FLOAT_EQ(HMM_Dot(v1, v2), 32.0f);
#endif
}
TEST(VectorOps, DotVec4)
{
hmm_vec4 v1 = HMM_Vec4(1.0f, 2.0f, 3.0f, 4.0f);
hmm_vec4 v2 = HMM_Vec4(5.0f, 6.0f, 7.0f, 8.0f);
EXPECT_FLOAT_EQ(HMM_DotVec4(v1, v2), 70.0f);
#ifdef __cplusplus
EXPECT_FLOAT_EQ(HMM_Dot(v1, v2), 70.0f);
#endif
}
/*
* MatrixOps tests
*/
TEST(MatrixOps, Transpose)
{
hmm_mat4 m4 = HMM_Mat4(); // will have 1 - 16
// Fill the matrix
int Counter = 1;
for (int Column = 0; Column < 4; ++Column)
{
for (int Row = 0; Row < 4; ++Row)
{
m4.Elements[Column][Row] = Counter;
++Counter;
}
}
// Test the matrix
hmm_mat4 result = HMM_Transpose(m4);
EXPECT_FLOAT_EQ(result.Elements[0][0], 1.0f);
EXPECT_FLOAT_EQ(result.Elements[0][1], 5.0f);
EXPECT_FLOAT_EQ(result.Elements[0][2], 9.0f);
EXPECT_FLOAT_EQ(result.Elements[0][3], 13.0f);
EXPECT_FLOAT_EQ(result.Elements[1][0], 2.0f);
EXPECT_FLOAT_EQ(result.Elements[1][1], 6.0f);
EXPECT_FLOAT_EQ(result.Elements[1][2], 10.0f);
EXPECT_FLOAT_EQ(result.Elements[1][3], 14.0f);
EXPECT_FLOAT_EQ(result.Elements[2][0], 3.0f);
EXPECT_FLOAT_EQ(result.Elements[2][1], 7.0f);
EXPECT_FLOAT_EQ(result.Elements[2][2], 11.0f);
EXPECT_FLOAT_EQ(result.Elements[2][3], 15.0f);
EXPECT_FLOAT_EQ(result.Elements[3][0], 4.0f);
EXPECT_FLOAT_EQ(result.Elements[3][1], 8.0f);
EXPECT_FLOAT_EQ(result.Elements[3][2], 12.0f);
EXPECT_FLOAT_EQ(result.Elements[3][3], 16.0f);
}

File diff suppressed because it is too large Load Diff

20
test/hmm_test.h Normal file
View File

@@ -0,0 +1,20 @@
#include <float.h>
#define HANDMADE_TEST_IMPLEMENTATION
#include "HandmadeTest.h"
#undef COVERAGE // Make sure we don't double-define initializers from the header part
#include "../HandmadeMath.h"
#include "categories/ScalarMath.h"
#include "categories/Initialization.h"
#include "categories/VectorOps.h"
#include "categories/QuaternionOps.h"
#include "categories/Addition.h"
#include "categories/Subtraction.h"
#include "categories/Multiplication.h"
#include "categories/Division.h"
#include "categories/Equality.h"
#include "categories/Projection.h"
#include "categories/Transformation.h"
#include "categories/SSE.h"

29
test/initializer.h Normal file
View File

@@ -0,0 +1,29 @@
// Initializer/finalizer sample for MSVC and GCC/Clang.
// 2010-2016 Joe Lowe. Released into the public domain.
#include <stdio.h>
#include <stdlib.h>
#ifdef __cplusplus
#define _INITIALIZER_T(f) f##_t_
#define _INITIALIZER_U(f) f##_
#define INITIALIZER(f) \
static void f(void); \
struct _INITIALIZER_T(f) { _INITIALIZER_T(f)(void) { f(); } }; static _INITIALIZER_T(f) _INITIALIZER_U(f); \
static void f(void)
#elif defined(_MSC_VER)
#pragma section(".CRT$XCU",read)
#define INITIALIZER2_(f,p) \
static void f(void); \
__declspec(allocate(".CRT$XCU")) void (*f##_)(void) = f; \
__pragma(comment(linker,"/include:" p #f "_")) \
static void f(void)
#ifdef _WIN64
#define INITIALIZER(f) INITIALIZER2_(f,"")
#else
#define INITIALIZER(f) INITIALIZER2_(f,"_")
#endif
#else
#define INITIALIZER(f) \
static void f(void) __attribute__((constructor)); \
static void f(void)
#endif

27
test/test.bat Normal file
View File

@@ -0,0 +1,27 @@
@echo off
if "%1%"=="travis" (
call "C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\Common7\Tools\VsDevCmd.bat" -host_arch=amd64 -arch=amd64
) else (
where /q cl
if ERRORLEVEL 1 (
for /f "delims=" %%a in ('"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -find VC\Auxiliary\Build\vcvarsall.bat') do (%%a x64)
)
)
if not exist "build" mkdir build
pushd build
cl /Fehmm_test_c.exe ..\HandmadeMath.c ..\hmm_test.c || popd && exit /B
hmm_test_c
cl /Fehmm_test_c_no_sse.exe /DHANDMADE_MATH_NO_SSE ..\HandmadeMath.c ..\hmm_test.c || popd && exit /B
hmm_test_c_no_sse
cl /Fehmm_test_cpp.exe ..\HandmadeMath.cpp ..\hmm_test.cpp || popd && exit /B
hmm_test_cpp
cl /Fehmm_test_cpp_no_sse.exe /DHANDMADE_MATH_NO_SSE ..\HandmadeMath.cpp ..\hmm_test.cpp || popd && exit /B
hmm_test_cpp_no_sse
popd