MATHC - Pure C math library for 2D and 3D programming

As I was working on my own game framework, I decided to decouple the math source on its own library:

GitHub link
Documentation: https://github.com/ferreiradaselva/mathc/blob/master/REFERENCE.md

This is mainly for OpenGL.

Features
  • 2D vectors
  • 3D vectors
  • Quaternions
  • Matrices
  • Easing functions

Planned Features
Currently the library reached the first major release, but I'm adding features that don't break compatibility, and suggestions for features that break compatibility are welcome, saved for the next major release (2.0.0).

The next feature I'm planning is detection for 2D and 3D primitives (circles, lines and triangles, both 2D and 3D).

Pointers vs. Values
There are two versions of the same function, one that take pointer arguments and other that take value arguments:

1
2
pmatrix_look_at(&pos, &target, &view);
view = matrix_look_at(pos, target);

Other examples in the GitHub page.

Taking pointers is certainly faster, but you can use the function that take values if performance is not critical for you, as they are more readable.

Single Vector Structure
There's only one vector structure, used for 2D vectors, 3D vectors and quaternions. When I had to use 2D vectors, sometimes I would want to use them as 3D vectors and vice-versa. So I decided to use the same structure to make this possible.

Easing Functions
The easing functions are great for animations: it makes easier to make smooth and organic motions.

Why another math library?
This is not intended to be a competitor with other math libraries, but a small and simple alternative.

Edited by Pengo on
A few comments about the quaternion part:

The null quaternion is 0,0,0,1 instead of all 0. There is no construction function for that.

The way to get the shortest rotation between 2 vectors can be faster if you skip the acos into sincos by doing:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
void pquaternion_from_2_vectors(struct vec *a, struct vec *b, struct vec *result){
    float dot = pvector3_dot(a, b);
    struct vec cross;

    float a_length_sq = pvector3_length_squared(a);
    float b_length_sq = pvector3_length_squared(a);

    pvector3_cross(a, b, &cross);
    to_pquaternion(cross.x, cross.y, cross.z, dot+sqrtf(a_length_sq*b_length_sq), result);
    pquaternion_normalize(result, result);
}


Your quat to matrix codes expects normalized quaternions, if you replace the 1.0f with the length squared or divide the xx, yy, zz, etd. with that you can fix that.
ratchetfreak
A few comments about the quaternion part:

The null quaternion is 0,0,0,1 instead of all 0. There is no construction function for that.

The way to get the shortest rotation between 2 vectors can be faster if you skip the acos into sincos by doing:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
void pquaternion_from_2_vectors(struct vec *a, struct vec *b, struct vec *result){
    float dot = pvector3_dot(a, b);
    struct vec cross;

    float a_length_sq = pvector3_length_squared(a);
    float b_length_sq = pvector3_length_squared(a);

    pvector3_cross(a, b, &cross);
    to_pquaternion(cross.x, cross.y, cross.z, dot+sqrtf(a_length_sq*b_length_sq), result);
    pquaternion_normalize(result, result);
}


Your quat to matrix codes expects normalized quaternions, if you replace the 1.0f with the length squared or divide the xx, yy, zz, etd. with that you can fix that.


Thanks for those inputs (and the ones on GitHub)! I will work on the fix for that :)
one more comment, the reason most slerp implementations use 0.95 as the cutoff for switching to normalized lerp. Is because there is a pole of inaccuracy when the input of acos nears 1 and less bits have useful information.

I've derived that using the normalized lerp to 0.5 is the exact same as the slerp to 0.5. So you can binary search into that for up to 5 iterations and then lerp to finish off. The 5 is based on how quickly the 2 boundaries converge from nearly opposite (dot~-1) to above the traditional cutoff (dot~0.95).
I don't think this is link to what you want: http://reference.md/
I created a similar library for raylib, it's a single-file header-only Vector2-Vector3-Quaternion-Matrix math library: raymath.h.

Maybe it could be useful for you as a reference.

And also created easings.h based on Robert Penner's easing functions.

ratchetfreak
one more comment, the reason most slerp implementations use 0.95 as the cutoff for switching to normalized lerp. Is because there is a pole of inaccuracy when the input of acos nears 1 and less bits have useful information.

I've derived that using the normalized lerp to 0.5 is the exact same as the slerp to 0.5. So you can binary search into that for up to 5 iterations and then lerp to finish off. The 5 is based on how quickly the 2 boundaries converge from nearly opposite (dot~-1) to above the traditional cutoff (dot~0.95).

And I thought that was something arbitrary! :P Thanks for the info, I fixed that.

mmozeiko
I don't think this is link to what you want: http://reference.md/

Fixed.

raysan5
I created a similar library for raylib, it's a single-file header-only Vector2-Vector3-Quaternion-Matrix math library: raymath.h.

Maybe it could be useful for you as a reference.


Thanks! I will make some comparisons, indeed 😄

raysan5

And also created easings.h based on Robert Penner's easing functions.

We both used the same reference 😄 I'm really thinking about expanding that list of easing functions. I currently use some of the functions in a game I'm making and they are great to add some juicyness to the game!
UPDATE

Added some functions to test if a pointer is inside or outside a circle or triangle:

1
2
3
4
5
bool pvector2_in_circle(struct vec *v, struct vec *circle_position, float radius);
bool pvector2_in_triangle(struct vec *v, struct vec *a, struct vec *b, struct vec *c);

bool vector2_in_circle(struct vec v, struct vec circle_position, float radius);
bool vector2_in_triangle(struct vec v, struct vec a, struct vec b, struct vec c);


I will soon add some functions to test circle-circle, circle-triangle, triangle-triangle intersection both 2D and 3D (sphere in the case of 3D). That's how far I will go with intersections, because I want to provide only test with primitives.

I'm working on the second version of my library, if anyone is in need of a math library.

Github branch: https://github.com/ferreiradaselva/mathc/tree/mathc2

The updates are:

  • Many configurations added
  • Each vector type has their own structure type: struct vec2, struct vec3, struct vec4
  • Integer vectors added: struct vec2i, struct vec3i, struct vec4i
  • Besides the structures, the types can be declared as arrays. Example: `mfloat_t position[VEC3_SIZE]`
  • More functions for quaternions
  • Euler angles (in-progress)
  • 2D and 3D matrices added
  • Structures have unions (can be disabled)
  • Overall improvement in the functions:
    • If you want to work with array, like in the example above, there are functions for them
    • If you want to work with structures, there are functions for them (can be disabled)
    • If you want to work with pointer to the structures, there are functions for them (can be disabled)

I covered almost all feedback that I got around, except for fixed-point arithmetic. I probably will never add to the library, since it would escalate the maintenance requirements drastically.

Edited by Pengo on
Newer version out!

Github repo

- Added integer vectors.
- Function names more consistent.
- Much, much more configurable, and you can change the type of `mfloat_t` and `mint_t`, which was something requested a lot.
- That same scheme still exists: there are functions for working with arrays, structures or pointer to structures. You choose according with your preference.
- The library now uses calendar versioning (CalVer).

What isn't in:

- I dropped the `euler` type, as working with matrices can give the same result and is better to maintain, since there are many euler conventions.
- Dropped test unit. A burden to maintain as a solo developer.
- No fixed-point for representing real numbers. Another burden to maintain as a solo developer. (I have to prioritize my mental health :p)

Edited by Pengo on