From 344056744d4045779731e8f9e65717d2964c82c7 Mon Sep 17 00:00:00 2001 From: Daniel Ledda Date: Tue, 11 Nov 2025 04:31:20 +0100 Subject: [PATCH] mostly migrated for c compatibility --- assets/shaders/2d-solid.vertex.glsl | 2 +- build | 2 +- src/SomaSolve.cpp | 146 +- src/SomaSolve.h | 11 +- src/VoxelSpace.h | 3 +- src/gfx/Color.cpp | 2 +- src/gfx/Shader.cpp | 36 +- src/gfx/Shader.h | 18 +- src/gfx/geometry.h | 2 +- src/lib/raymath.h | 2670 +++++++++++++++++++++++++++ src/main.cpp | 197 +- src/tests.cpp | 220 +-- src/world/scene.cpp | 70 +- src/world/scene.h | 26 +- 14 files changed, 3050 insertions(+), 355 deletions(-) create mode 100644 src/lib/raymath.h diff --git a/assets/shaders/2d-solid.vertex.glsl b/assets/shaders/2d-solid.vertex.glsl index 381f21e..32420e2 100644 --- a/assets/shaders/2d-solid.vertex.glsl +++ b/assets/shaders/2d-solid.vertex.glsl @@ -4,7 +4,7 @@ layout (location = 1) in vec2 p1; layout (location = 2) in vec4 color; layout (location = 3) in float border_radius; layout (location = 4) in float border_thickness; -layout (location = 6) in float edge_softness; +layout (location = 5) in float edge_softness; uniform mat4 projection; diff --git a/build b/build index 4bc4b75..a14914d 100755 --- a/build +++ b/build @@ -1,6 +1,6 @@ #!/bin/bash -LIB_INCLUDE="-lglfw -lGL" +LIB_INCLUDE="-lglfw -lGL -lm" g++ -g -g3 -DOS_LINUX=1 -DENABLE_ASSERT=1 ./src/main.cpp -o ./target/somaesque $LIB_INCLUDE ./target/somaesque diff --git a/src/SomaSolve.cpp b/src/SomaSolve.cpp index c35a256..a4c9bd1 100644 --- a/src/SomaSolve.cpp +++ b/src/SomaSolve.cpp @@ -1,11 +1,9 @@ #include -#include -#include -#include #include "VoxelSpace.h" +/* void get_dims_input(int dims[3]) { - std::cout << "Enter dimensions separated by newlines. (x*y*z must not exceed 64)\n"; + print("Enter dimensions separated by newlines. (x*y*z must not exceed 64)\n"); bool success = false; while (!success) { std::cout << "x: "; @@ -19,14 +17,16 @@ void get_dims_input(int dims[3]) { if (size <= 64) { success = true; } else { - std::cout << "That resulted in " << size << " units. Try again.\n"; + print("That resulted in %zu units. Try again", size); } std::cin.ignore(); } } +*/ +/* std::vector get_reprs_input(int units_required) { - std::cout << "Enter bit-representations (big endian, max 64 bits, total 1s must add up to " << units_required << "). press ENTER twice to finish input.\n"; + print("Enter bit-representations (big endian, max 64 bits, total 1s must add up to %zu). press ENTER twice to finish input.\n", units_required ); std::vector reprs = std::vector(); int total_units = 0; while (true) { @@ -60,17 +60,19 @@ std::vector get_reprs_input(int units_required) { } return reprs; } +*/ -typedef std::vector SomaSolution; +typedef list SomaSolution; -struct Solver { +typedef struct Solver { list* input; list* offsets; - std::vector* solutions; -}; + list* solutions; +} Solver; uint64 STD_SOMA[] = { 23ul, 30ul, 15ul, 1043ul, 24594ul, 12306ul, 11ul }; +/* void backtrack_solve_iter(std::vector *polycube_input, std::vector *offsets) { size_t num_inputs = offsets->size() - 1; @@ -118,94 +120,117 @@ void backtrack_solve_iter(std::vector *polycube_input, std::vector } std::cout << "Done. Found " << solns.size() << " solutions." << std::endl; } +*/ -void backtrack_solve(Solver *solver, uint64 working_solution = 0, size_t curr_piece = 0) { +void backtrackSolve(Arena *arena, Solver *solver, uint64 working_solution = 0, size_t curr_piece = 0) { list *input = solver->input; list *offsets = solver->offsets; - std::vector *solutions = solver->solutions; + list *solutions = solver->solutions; size_t start = offsets->data[curr_piece]; size_t end = offsets->data[curr_piece + 1]; - size_t num_pieces = offsets->length - 1; + size_t num_pieces = offsets->head - 1; for (size_t i = start; i < end; i++) { bool successful_fuse = !collides(working_solution, input->data[i]); if (successful_fuse) { uint64 new_working_solution = working_solution | input->data[i]; - solutions->back().at(curr_piece) = input->data[i]; + solutions->data[solutions->head - 1].data[curr_piece] = input->data[i]; if (curr_piece == num_pieces - 1) { - std::vector last_soln = solutions->back(); - solutions->push_back(SomaSolution(last_soln.begin(), last_soln.end())); + list last_soln = solutions->data[solutions->head - 1]; + list last_soln_copy = PushList(arena, uint64, last_soln.head); + last_soln_copy.length = last_soln.head; + last_soln_copy.head = last_soln.head; + memcpy(last_soln_copy.data, last_soln.data, last_soln.head * sizeof(uint64)); + appendList(solutions, last_soln_copy); return; } else { - backtrack_solve(solver, new_working_solution, curr_piece + 1); + backtrackSolve(arena, solver, new_working_solution, curr_piece + 1); } } } if (curr_piece == 0) { - solutions->pop_back(); - } + solutions->head -= 1; + } } -std::vector get_solution_rotations(Arena *arena, SomaSolution *solution, int dims[3]) { - std::vector result = std::vector(NUM_ROTS_3D); - for (int piece_i = 0; piece_i < solution->size(); piece_i++) { - Space space = { - solution->at(piece_i), +list getSolutionRotations(Arena *arena, SomaSolution *solution, int dims[3]) { + list result = PushFullList(arena, SomaSolution, NUM_ROTS_3D); + for (EachIn(result, i)) { + result.data[i] = PushList(arena, uint64, solution->head); + } + for (int piece_i = 0; piece_i < solution->head; piece_i++) { + Space space = { + solution->data[piece_i], dims[0], dims[1], dims[2], }; - list piece_rotations = getAllRotations(arena, &space); - for (int rot_i = 0; rot_i < piece_rotations.length; rot_i++) { - result[rot_i].push_back(piece_rotations.data[rot_i].space); + list pieceRotations = getAllRotations(arena, &space); + for (int rot_i = 0; rot_i < pieceRotations.head; rot_i++) { + appendList(&result.data[rot_i], pieceRotations.data[rot_i].space); } } return result; } -std::vector filter_unique(Arena *arena, std::vector *solutions, int dims[3]) { - if (solutions->size() == 0) { - return std::vector(); +list filterUnique(Arena *arena, list *solutions, int dims[3]) { + if (solutions->head == 0) { + return list{NULL,0,0}; } - std::vector unique_solns = std::vector{}; - for (std::vector &solution : *solutions) { - bool found_match = false; + list uniqueSolns = PushList(arena, SomaSolution, solutions->head); + for (EachIn(*solutions, i)) { + SomaSolution solution = solutions->data[i]; + bool foundMatch = false; Scratch temp = scratchStart(&arena, 1); - std::vector rots = get_solution_rotations(temp.arena, &solution, dims); - for (SomaSolution &rotation : rots) { - for (std::vector &unique_soln : unique_solns) { - bool is_match = true; - for (int piece_i = 0; piece_i < unique_soln.size(); piece_i++) { - if (rotation[piece_i] != unique_soln[piece_i]) { - is_match = false; + list rots = getSolutionRotations(temp.arena, &solution, dims); + for (EachIn(rots, j)) { + SomaSolution rotation = rots.data[j]; + for (EachIn(uniqueSolns, k)) { + SomaSolution unique_soln = uniqueSolns.data[k]; + bool isMatch = true; + for (EachIn(unique_soln, piece_i)) { + if (rotation.data[piece_i] != unique_soln.data[piece_i]) { + isMatch = false; break; } } - if (is_match) { - found_match = true; + if (isMatch) { + foundMatch = true; break; } } - if (found_match) { + if (foundMatch) { break; } } scratchEnd(temp); - if (!found_match) { - unique_solns.push_back(SomaSolution(solution)); + if (!foundMatch) { + SomaSolution solutionCopy = PushList(arena, uint64, solution.head); + solutionCopy.length = solution.head; + solutionCopy.head = solution.head; + memcpy(solutionCopy.data, solution.data, solution.head * sizeof(uint64)); + appendList(&uniqueSolns, solutionCopy); } } - return unique_solns; + return uniqueSolns; } -std::vector solve(uint64 *reprs_in, uint32 reprs_in_count, int dims[3]) { +uint64 factorial(int n) { + uint64 result = 1; + for (int i = 1; i <= n; i++) { + result *= i; + } + return result; +} + +list solve(uint64 *reprs_in, uint32 reprs_in_count, int dims[3]) { Arena *arena = arenaAlloc(Megabytes(64)); - Arena *permsArena = arenaAlloc(Megabytes(64)); + Arena *permsArena = arenaAlloc(Megabytes(128)); list offsets = PushList(arena, size_t, reprs_in_count + 1); list polycubes = PushList(arena, uint64, 0); - Space empty_voxel_space = { + Space empty_voxel_space = { {}, dims[0], dims[1], @@ -214,12 +239,15 @@ std::vector solve(uint64 *reprs_in, uint32 reprs_in_count, int dim appendList(&offsets, (size_t)0); - list positions = {}; + uint64 possibleCombos = 0; + { + list positions = {}; Space space = empty_voxel_space; space.space = reprs_in[0]; cullEmptySpace(&space); positions = getAllPositionsInPrism(permsArena, &space, dims); + possibleCombos += positions.head; uint64 *insertion = PushArray(arena, uint64, positions.length); polycubes.length += positions.length; polycubes.head += positions.head; @@ -232,24 +260,28 @@ std::vector solve(uint64 *reprs_in, uint32 reprs_in_count, int dim space.space = reprs_in[i]; cullEmptySpace(&space); list perms = getAllPermutationsInPrism(permsArena, &space, dims); + possibleCombos *= perms.head; uint64 *insertion = PushArray(arena, uint64, perms.length); polycubes.length += perms.length; polycubes.head += perms.head; memcpy(insertion, perms.data, perms.length * sizeof(uint64)); } - appendList(&offsets, polycubes.length); + appendList(&offsets, polycubes.head); + + list solutions = PushList(permsArena, SomaSolution, (size_t)floor(sqrt(possibleCombos))); + SomaSolution initialSoln = PushFullList(permsArena, uint64, reprs_in_count); + appendList(&solutions, initialSoln); - std::vector solutions = {std::vector(reprs_in_count)}; Solver solver = { &polycubes, &offsets, &solutions, }; - backtrack_solve(&solver); + backtrackSolve(permsArena, &solver); - return filter_unique(arena, solver.solutions, dims); + return filterUnique(permsArena, solver.solutions, dims); } @@ -258,7 +290,7 @@ void interactive_cmd_line_solve_soma() { //get_dims_input(dims); //std::cout << '\n'; //std::vector reprs = get_reprs_input(dims[0]*dims[1]*dims[2]); - std::cout << "Great. Calculating solutions...\n"; - std::vector solutions = solve(STD_SOMA, ArrayCount(STD_SOMA), dims); - std::cout << solutions.size() << " solutions found." << std::endl; + print("Great. Calculating solutions...\n"); + list solutions = solve(STD_SOMA, ArrayCount(STD_SOMA), dims); + print("%zu solutions found.\n", solutions.head); } diff --git a/src/SomaSolve.h b/src/SomaSolve.h index 3f69d34..cb7dcbb 100644 --- a/src/SomaSolve.h +++ b/src/SomaSolve.h @@ -1,7 +1,6 @@ -#include -#include +#include "lib/djstdlib/core.h" -extern std::vector STD_SOMA; -typedef std::vector SomaSolution; -std::vector solve(std::vector *reprs_in, int dims[3]); -void interactive_cmd_line_solve_soma(); +extern uint64 STD_SOMA[]; +typedef list SomaSolution; +list solve(list *reprs_in, int dims[3]); +void interactive_cmd_line_solve_soma(); diff --git a/src/VoxelSpace.h b/src/VoxelSpace.h index dea23cd..2cdd8a5 100644 --- a/src/VoxelSpace.h +++ b/src/VoxelSpace.h @@ -1,7 +1,6 @@ #ifndef VOXELSPACE_H #define VOXELSPACE_H -#include #include "lib/djstdlib/core.h" constexpr int NUM_ROTS_3D = 24; @@ -51,7 +50,7 @@ void rotate90Y(Space *space); void rotate90Z(Space *space); -void pushNewUniqueSpins(std::vector *existingSpaces, Space* spaceToSpin); +void pushNewUniqueSpins(list *existingSpaces, Space* spaceToSpin); list getUniqueRotations(Arena *arena, Space *space); diff --git a/src/gfx/Color.cpp b/src/gfx/Color.cpp index 23d7526..fabcc3f 100644 --- a/src/gfx/Color.cpp +++ b/src/gfx/Color.cpp @@ -1,4 +1,4 @@ -#include +#include "math.h" #include "Color.h" real32 hueToRGB(real32 p, real32 q, real32 t) { diff --git a/src/gfx/Shader.cpp b/src/gfx/Shader.cpp index 3f6d7a7..6c1392f 100644 --- a/src/gfx/Shader.cpp +++ b/src/gfx/Shader.cpp @@ -1,8 +1,8 @@ -#include #include "Shader.h" #include "../lib/djstdlib/core.h" #include "../lib/djstdlib/os.h" #include "../lib/glad/glad.h" +#include "../lib/raymath.h" enum ShaderType { fragment=GL_FRAGMENT_SHADER, @@ -60,43 +60,43 @@ Shader createShader(string vertex_path, string fragment_path) { return result; } -void setUniformMat4fv(Shader *s, const char *uniformName, glm::mat4 *matrix) { +void setUniformMat4fv(Shader *s, const char *uniformName, Matrix *matrix) { glUniformMatrix4fv( - glGetUniformLocation(s->progId, uniformName), - 1, GL_FALSE, glm::value_ptr(*matrix)); + glGetUniformLocation(s->progId, uniformName), + 1, GL_FALSE, MatrixToFloat(*matrix)); } -void setUniformMat4fv(int uniformLocation, glm::mat4 *matrix) { - glUniformMatrix4fv(uniformLocation, 1, GL_FALSE, glm::value_ptr(*matrix)); +void setUniformMat4fv(int uniformLocation, Matrix *matrix) { + glUniformMatrix4fv(uniformLocation, 1, GL_FALSE, MatrixToFloat(*matrix)); } void setUniform4fv(Shader *s, const char *uniformName, Vector4 *vector) { glUniform4fv(glGetUniformLocation(s->progId, uniformName), 1, vector->vec); } -void setUniform4fv(Shader *s, const char *uniformName, glm::vec4 *vector) { - glUniform4fv(glGetUniformLocation(s->progId, uniformName), 1, glm::value_ptr(*vector)); +void setUniform4fv(Shader *s, const char *uniformName, RLVector4 *vector) { + glUniform4fv(glGetUniformLocation(s->progId, uniformName), 1, (const GLfloat *)vector); } -void setUniform4fv(int uniformLocation, glm::vec4 *vector) { - glUniform4fv(uniformLocation, 1, glm::value_ptr(*vector)); +void setUniform4fv(int uniformLocation, RLVector4 *vector) { + glUniform4fv(uniformLocation, 1, (const GLfloat *)vector); } void setUniform3fv(Shader *s, const char *uniformName, Vector3 *vector) { glUniform3fv(glGetUniformLocation(s->progId, uniformName), 1, vector->vec); } -void setUniform3fv(Shader *s, const char *uniformName, glm::vec3 *vector) { - glUniform3fv(glGetUniformLocation(s->progId, uniformName), 1, glm::value_ptr(*vector)); +void setUniform3fv(Shader *s, const char *uniformName, RLVector3 *vector) { + glUniform3fv(glGetUniformLocation(s->progId, uniformName), 1, Vector3ToFloat(*vector)); } -void setUniform3fv(int uniformLocation, glm::vec3 *vector) { - glUniform3fv(uniformLocation, 1, glm::value_ptr(*vector)); +void setUniform3fv(int uniformLocation, RLVector3 *vector) { + glUniform3fv(uniformLocation, 1, Vector3ToFloat(*vector)); } void setUniform2fv(Shader *s, const char *uniformName, Vector2 *vector) { glUniform2fv(glGetUniformLocation(s->progId, uniformName), 1, vector->vec); } -void setUniform2fv(Shader *s, const char *uniformName, glm::vec2 *vector) { - glUniform2fv(glGetUniformLocation(s->progId, uniformName), 1, glm::value_ptr(*vector)); +void setUniform2fv(Shader *s, const char *uniformName, RLVector2 *vector) { + glUniform2fv(glGetUniformLocation(s->progId, uniformName), 1, (const GLfloat *)vector); } -void setUniform2fv(int uniformLocation, glm::vec2 *vector) { - glUniform2fv(uniformLocation, 1, glm::value_ptr(*vector)); +void setUniform2fv(int uniformLocation, RLVector2 *vector) { + glUniform2fv(uniformLocation, 1, (const GLfloat *)vector); } int getUniformLocation(Shader *s, const char *uniformName) { diff --git a/src/gfx/Shader.h b/src/gfx/Shader.h index 9a4d6d6..ea67384 100644 --- a/src/gfx/Shader.h +++ b/src/gfx/Shader.h @@ -1,7 +1,7 @@ #ifndef LEDDA_SHADER_H #define LEDDA_SHADER_H -#include "glm/glm.hpp" +#include "../lib/raymath.h" #include "../lib/djstdlib/core.h" struct Shader { @@ -10,17 +10,17 @@ struct Shader { Shader createShader(string vertex_path, string fragment_path); -void setUniformMat4fv(Shader *s, const char *uniformName, glm::mat4 *matrix); -void setUniformMat4fv(int uniformLocation, glm::mat4 *matrix); +void setUniformMat4fv(Shader *s, const char *uniformName, Matrix *matrix); +void setUniformMat4fv(int uniformLocation, Matrix *matrix); -void setUniform4fv(Shader *s, const char *uniformName, glm::vec4 *vector); -void setUniform4fv(int uniformLocation, glm::vec4 *vector); +void setUniform4fv(Shader *s, const char *uniformName, RLVector4 *vector); +void setUniform4fv(int uniformLocation, RLVector4 *vector); -void setUniform3fv(Shader *s, const char *uniformName, glm::vec3 *vector); -void setUniform3fv(int uniformLocation, glm::vec3 *vector); +void setUniform3fv(Shader *s, const char *uniformName, RLVector3 *vector); +void setUniform3fv(int uniformLocation, RLVector3 *vector); -void setUniform2fv(Shader *s, const char *uniformName, glm::vec2 *vector); -void setUniform2fv(int uniformLocation, glm::vec2 *vector); +void setUniform2fv(Shader *s, const char *uniformName, RLVector2 *vector); +void setUniform2fv(int uniformLocation, RLVector2 *vector); int getUniformLocation(Shader *s, const char *uniformName); diff --git a/src/gfx/geometry.h b/src/gfx/geometry.h index d18d069..eb7ab18 100644 --- a/src/gfx/geometry.h +++ b/src/gfx/geometry.h @@ -1,7 +1,7 @@ #ifndef LEDDA_GEOMETRY_H #define LEDDA_GEOMETRY_H -#include +#include "stddef.h" struct Shape { unsigned int* indices; diff --git a/src/lib/raymath.h b/src/lib/raymath.h new file mode 100644 index 0000000..6dfd176 --- /dev/null +++ b/src/lib/raymath.h @@ -0,0 +1,2670 @@ +/********************************************************************************************** +* +* raymath v2.0 - Math functions to work with Vector2, Vector3, Matrix and Quaternions +* +* CONVENTIONS: +* - Matrix structure is defined as row-major (memory layout) but parameters naming AND all +* math operations performed by the library consider the structure as it was column-major +* It is like transposed versions of the matrices are used for all the maths +* It benefits some functions making them cache-friendly and also avoids matrix +* transpositions sometimes required by OpenGL +* Example: In memory order, row0 is [m0 m4 m8 m12] but in semantic math row0 is [m0 m1 m2 m3] +* - Functions are always self-contained, no function use another raymath function inside, +* required code is directly re-implemented inside +* - Functions input parameters are always received by value (2 unavoidable exceptions) +* - Functions use always a "result" variable for return (except C++ operators) +* - Functions are always defined inline +* - Angles are always in radians (DEG2RAD/RAD2DEG macros provided for convenience) +* - No compound literals used to make sure libray is compatible with C++ +* +* CONFIGURATION: +* #define RAYMATH_IMPLEMENTATION +* Generates the implementation of the library into the included file. +* If not defined, the library is in header only mode and can be included in other headers +* or source files without problems. But only ONE file should hold the implementation. +* +* #define RAYMATH_STATIC_INLINE +* Define static inline functions code, so #include header suffices for use. +* This may use up lots of memory. +* +* #define RAYMATH_DISABLE_CPP_OPERATORS +* Disables C++ operator overloads for raymath types. +* +* LICENSE: zlib/libpng +* +* Copyright (c) 2015-2025 Ramon Santamaria (@raysan5) +* +* 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 RAYMATH_H +#define RAYMATH_H + +#if defined(RAYMATH_IMPLEMENTATION) && defined(RAYMATH_STATIC_INLINE) + #error "Specifying both RAYMATH_IMPLEMENTATION and RAYMATH_STATIC_INLINE is contradictory" +#endif + +// Function specifiers definition +#if defined(RAYMATH_IMPLEMENTATION) + #if defined(_WIN32) && defined(BUILD_LIBTYPE_SHARED) + #define RMAPI __declspec(dllexport) extern inline // We are building raylib as a Win32 shared library (.dll) + #elif defined(BUILD_LIBTYPE_SHARED) + #define RMAPI __attribute__((visibility("default"))) // We are building raylib as a Unix shared library (.so/.dylib) + #elif defined(_WIN32) && defined(USE_LIBTYPE_SHARED) + #define RMAPI __declspec(dllimport) // We are using raylib as a Win32 shared library (.dll) + #else + #define RMAPI extern inline // Provide external definition + #endif +#elif defined(RAYMATH_STATIC_INLINE) + #define RMAPI static inline // Functions may be inlined, no external out-of-line definition +#else + #if defined(__TINYC__) + #define RMAPI static inline // plain inline not supported by tinycc (See issue #435) + #else + #define RMAPI inline // Functions may be inlined or external definition used + #endif +#endif + + +//---------------------------------------------------------------------------------- +// Defines and Macros +//---------------------------------------------------------------------------------- +#ifndef PI + #define PI 3.14159265358979323846f +#endif + +#ifndef EPSILON + #define EPSILON 0.000001f +#endif + +#ifndef DEG2RAD + #define DEG2RAD (PI/180.0f) +#endif + +#ifndef RAD2DEG + #define RAD2DEG (180.0f/PI) +#endif + +// Get float vector for Matrix +#ifndef MatrixToFloat + #define MatrixToFloat(mat) (MatrixToFloatV(mat).v) +#endif + +// Get float vector for Vector3 +#ifndef Vector3ToFloat + #define Vector3ToFloat(vec) (Vector3ToFloatV(vec).v) +#endif + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- +#if !defined(RL_VECTOR2_TYPE) +// Vector2 type +typedef struct RLVector2 { + float x; + float y; +} RMVector2; +#define RL_VECTOR2_TYPE +#endif + +#if !defined(RL_VECTOR3_TYPE) +// Vector3 type +typedef struct RLVector3 { + float x; + float y; + float z; +} RLVector3; +#define RL_VECTOR3_TYPE +#endif + +#if !defined(RL_VECTOR4_TYPE) +// Vector4 type +typedef struct RLVector4 { + float x; + float y; + float z; + float w; +} RLVector4; +#define RL_VECTOR4_TYPE +#endif + +#if !defined(RL_QUATERNION_TYPE) +// Quaternion type +typedef RLVector4 Quaternion; +#define RL_QUATERNION_TYPE +#endif + +#if !defined(RL_MATRIX_TYPE) +// Matrix type (OpenGL style 4x4 - right handed, column major) +typedef struct Matrix { + float m0, m4, m8, m12; // Matrix first row (4 components) + float m1, m5, m9, m13; // Matrix second row (4 components) + float m2, m6, m10, m14; // Matrix third row (4 components) + float m3, m7, m11, m15; // Matrix fourth row (4 components) +} Matrix; +#define RL_MATRIX_TYPE +#endif + +// NOTE: Helper types to be used instead of array return types for *ToFloat functions +typedef struct float3 { + float v[3]; +} float3; + +typedef struct float16 { + float v[16]; +} float16; + +#include // Required for: sinf(), cosf(), tan(), atan2f(), sqrtf(), floor(), fminf(), fmaxf(), fabsf() + +//---------------------------------------------------------------------------------- +// Module Functions Definition - Utils math +//---------------------------------------------------------------------------------- + +// Clamp float value +RMAPI float Clamp(float value, float min, float max) +{ + float result = (value < min)? min : value; + + if (result > max) result = max; + + return result; +} + +// Calculate linear interpolation between two floats +RMAPI float Lerp(float start, float end, float amount) +{ + float result = start + amount*(end - start); + + return result; +} + +// Normalize input value within input range +RMAPI float Normalize(float value, float start, float end) +{ + float result = (value - start)/(end - start); + + return result; +} + +// Remap input value within input range to output range +RMAPI float Remap(float value, float inputStart, float inputEnd, float outputStart, float outputEnd) +{ + float result = (value - inputStart)/(inputEnd - inputStart)*(outputEnd - outputStart) + outputStart; + + return result; +} + +// Wrap input value from min to max +RMAPI float Wrap(float value, float min, float max) +{ + float result = value - (max - min)*floorf((value - min)/(max - min)); + + return result; +} + +// Check whether two given floats are almost equal +RMAPI int FloatEquals(float x, float y) +{ +#if !defined(EPSILON) + #define EPSILON 0.000001f +#endif + + int result = (fabsf(x - y)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(x), fabsf(y)))); + + return result; +} + +//---------------------------------------------------------------------------------- +// Module Functions Definition - Vector2 math +//---------------------------------------------------------------------------------- + +// Vector with components value 0.0f +RMAPI RMVector2 Vector2Zero(void) +{ + RMVector2 result = { 0.0f, 0.0f }; + + return result; +} + +// Vector with components value 1.0f +RMAPI RMVector2 Vector2One(void) +{ + RMVector2 result = { 1.0f, 1.0f }; + + return result; +} + +// Add two vectors (v1 + v2) +RMAPI RMVector2 Vector2Add(RMVector2 v1, RMVector2 v2) +{ + RMVector2 result = { v1.x + v2.x, v1.y + v2.y }; + + return result; +} + +// Add vector and float value +RMAPI RMVector2 Vector2AddValue(RMVector2 v, float add) +{ + RMVector2 result = { v.x + add, v.y + add }; + + return result; +} + +// Subtract two vectors (v1 - v2) +RMAPI RMVector2 Vector2Subtract(RMVector2 v1, RMVector2 v2) +{ + RMVector2 result = { v1.x - v2.x, v1.y - v2.y }; + + return result; +} + +// Subtract vector by float value +RMAPI RMVector2 Vector2SubtractValue(RMVector2 v, float sub) +{ + RMVector2 result = { v.x - sub, v.y - sub }; + + return result; +} + +// Calculate vector length +RMAPI float Vector2Length(RMVector2 v) +{ + float result = sqrtf((v.x*v.x) + (v.y*v.y)); + + return result; +} + +// Calculate vector square length +RMAPI float Vector2LengthSqr(RMVector2 v) +{ + float result = (v.x*v.x) + (v.y*v.y); + + return result; +} + +// Calculate two vectors dot product +RMAPI float Vector2DotProduct(RMVector2 v1, RMVector2 v2) +{ + float result = (v1.x*v2.x + v1.y*v2.y); + + return result; +} + +// Calculate two vectors cross product +RMAPI float Vector2CrossProduct(RMVector2 v1, RMVector2 v2) +{ + float result = (v1.x*v2.y - v1.y*v2.x); + + return result; +} + +// Calculate distance between two vectors +RMAPI float Vector2Distance(RMVector2 v1, RMVector2 v2) +{ + float result = sqrtf((v1.x - v2.x)*(v1.x - v2.x) + (v1.y - v2.y)*(v1.y - v2.y)); + + return result; +} + +// Calculate square distance between two vectors +RMAPI float Vector2DistanceSqr(RMVector2 v1, RMVector2 v2) +{ + float result = ((v1.x - v2.x)*(v1.x - v2.x) + (v1.y - v2.y)*(v1.y - v2.y)); + + return result; +} + +// Calculate the signed angle from v1 to v2, relative to the origin (0, 0) +// NOTE: Coordinate system convention: positive X right, positive Y down +// positive angles appear clockwise, and negative angles appear counterclockwise +RMAPI float Vector2Angle(RMVector2 v1, RMVector2 v2) +{ + float result = 0.0f; + + float dot = v1.x*v2.x + v1.y*v2.y; + float det = v1.x*v2.y - v1.y*v2.x; + + result = atan2f(det, dot); + + return result; +} + +// Calculate angle defined by a two vectors line +// NOTE: Parameters need to be normalized +// Current implementation should be aligned with glm::angle +RMAPI float Vector2LineAngle(RMVector2 start, RMVector2 end) +{ + float result = 0.0f; + + // TODO(10/9/2023): Currently angles move clockwise, determine if this is wanted behavior + result = -atan2f(end.y - start.y, end.x - start.x); + + return result; +} + +// Scale vector (multiply by value) +RMAPI RMVector2 Vector2Scale(RMVector2 v, float scale) +{ + RMVector2 result = { v.x*scale, v.y*scale }; + + return result; +} + +// Multiply vector by vector +RMAPI RMVector2 Vector2Multiply(RMVector2 v1, RMVector2 v2) +{ + RMVector2 result = { v1.x*v2.x, v1.y*v2.y }; + + return result; +} + +// Negate vector +RMAPI RMVector2 Vector2Negate(RMVector2 v) +{ + RMVector2 result = { -v.x, -v.y }; + + return result; +} + +// Divide vector by vector +RMAPI RMVector2 Vector2Divide(RMVector2 v1, RMVector2 v2) +{ + RMVector2 result = { v1.x/v2.x, v1.y/v2.y }; + + return result; +} + +// Normalize provided vector +RMAPI RMVector2 Vector2Normalize(RMVector2 v) +{ + RMVector2 result = { 0 }; + float length = sqrtf((v.x*v.x) + (v.y*v.y)); + + if (length > 0) + { + float ilength = 1.0f/length; + result.x = v.x*ilength; + result.y = v.y*ilength; + } + + return result; +} + +// Transforms a Vector2 by a given Matrix +RMAPI RMVector2 Vector2Transform(RMVector2 v, Matrix mat) +{ + RMVector2 result = { 0 }; + + float x = v.x; + float y = v.y; + float z = 0; + + result.x = mat.m0*x + mat.m4*y + mat.m8*z + mat.m12; + result.y = mat.m1*x + mat.m5*y + mat.m9*z + mat.m13; + + return result; +} + +// Calculate linear interpolation between two vectors +RMAPI RMVector2 Vector2Lerp(RMVector2 v1, RMVector2 v2, float amount) +{ + RMVector2 result = { 0 }; + + result.x = v1.x + amount*(v2.x - v1.x); + result.y = v1.y + amount*(v2.y - v1.y); + + return result; +} + +// Calculate reflected vector to normal +RMAPI RMVector2 Vector2Reflect(RMVector2 v, RMVector2 normal) +{ + RMVector2 result = { 0 }; + + float dotProduct = (v.x*normal.x + v.y*normal.y); // Dot product + + result.x = v.x - (2.0f*normal.x)*dotProduct; + result.y = v.y - (2.0f*normal.y)*dotProduct; + + return result; +} + +// Get min value for each pair of components +RMAPI RMVector2 Vector2Min(RMVector2 v1, RMVector2 v2) +{ + RMVector2 result = { 0 }; + + result.x = fminf(v1.x, v2.x); + result.y = fminf(v1.y, v2.y); + + return result; +} + +// Get max value for each pair of components +RMAPI RMVector2 Vector2Max(RMVector2 v1, RMVector2 v2) +{ + RMVector2 result = { 0 }; + + result.x = fmaxf(v1.x, v2.x); + result.y = fmaxf(v1.y, v2.y); + + return result; +} + +// Rotate vector by angle +RMAPI RMVector2 Vector2Rotate(RMVector2 v, float angle) +{ + RMVector2 result = { 0 }; + + float cosres = cosf(angle); + float sinres = sinf(angle); + + result.x = v.x*cosres - v.y*sinres; + result.y = v.x*sinres + v.y*cosres; + + return result; +} + +// Move Vector towards target +RMAPI RMVector2 Vector2MoveTowards(RMVector2 v, RMVector2 target, float maxDistance) +{ + RMVector2 result = { 0 }; + + float dx = target.x - v.x; + float dy = target.y - v.y; + float value = (dx*dx) + (dy*dy); + + if ((value == 0) || ((maxDistance >= 0) && (value <= maxDistance*maxDistance))) return target; + + float dist = sqrtf(value); + + result.x = v.x + dx/dist*maxDistance; + result.y = v.y + dy/dist*maxDistance; + + return result; +} + +// Invert the given vector +RMAPI RMVector2 Vector2Invert(RMVector2 v) +{ + RMVector2 result = { 1.0f/v.x, 1.0f/v.y }; + + return result; +} + +// Clamp the components of the vector between +// min and max values specified by the given vectors +RMAPI RMVector2 Vector2Clamp(RMVector2 v, RMVector2 min, RMVector2 max) +{ + RMVector2 result = { 0 }; + + result.x = fminf(max.x, fmaxf(min.x, v.x)); + result.y = fminf(max.y, fmaxf(min.y, v.y)); + + return result; +} + +// Clamp the magnitude of the vector between two min and max values +RMAPI RMVector2 Vector2ClampValue(RMVector2 v, float min, float max) +{ + RMVector2 result = v; + + float length = (v.x*v.x) + (v.y*v.y); + if (length > 0.0f) + { + length = sqrtf(length); + + float scale = 1; // By default, 1 as the neutral element. + if (length < min) + { + scale = min/length; + } + else if (length > max) + { + scale = max/length; + } + + result.x = v.x*scale; + result.y = v.y*scale; + } + + return result; +} + +// Check whether two given vectors are almost equal +RMAPI int Vector2Equals(RMVector2 p, RMVector2 q) +{ +#if !defined(EPSILON) + #define EPSILON 0.000001f +#endif + + int result = ((fabsf(p.x - q.x)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.x), fabsf(q.x))))) && + ((fabsf(p.y - q.y)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.y), fabsf(q.y))))); + + return result; +} + +// Compute the direction of a refracted ray +// v: normalized direction of the incoming ray +// n: normalized normal vector of the interface of two optical media +// r: ratio of the refractive index of the medium from where the ray comes +// to the refractive index of the medium on the other side of the surface +RMAPI RMVector2 Vector2Refract(RMVector2 v, RMVector2 n, float r) +{ + RMVector2 result = { 0 }; + + float dot = v.x*n.x + v.y*n.y; + float d = 1.0f - r*r*(1.0f - dot*dot); + + if (d >= 0.0f) + { + d = sqrtf(d); + v.x = r*v.x - (r*dot + d)*n.x; + v.y = r*v.y - (r*dot + d)*n.y; + + result = v; + } + + return result; +} + + +//---------------------------------------------------------------------------------- +// Module Functions Definition - Vector3 math +//---------------------------------------------------------------------------------- + +// Vector with components value 0.0f +RMAPI RLVector3 Vector3Zero(void) +{ + RLVector3 result = { 0.0f, 0.0f, 0.0f }; + + return result; +} + +// Vector with components value 1.0f +RMAPI RLVector3 Vector3One(void) +{ + RLVector3 result = { 1.0f, 1.0f, 1.0f }; + + return result; +} + +// Add two vectors +RMAPI RLVector3 Vector3Add(RLVector3 v1, RLVector3 v2) +{ + RLVector3 result = { v1.x + v2.x, v1.y + v2.y, v1.z + v2.z }; + + return result; +} + +// Add vector and float value +RMAPI RLVector3 Vector3AddValue(RLVector3 v, float add) +{ + RLVector3 result = { v.x + add, v.y + add, v.z + add }; + + return result; +} + +// Subtract two vectors +RMAPI RLVector3 Vector3Subtract(RLVector3 v1, RLVector3 v2) +{ + RLVector3 result = { v1.x - v2.x, v1.y - v2.y, v1.z - v2.z }; + + return result; +} + +// Subtract vector by float value +RMAPI RLVector3 Vector3SubtractValue(RLVector3 v, float sub) +{ + RLVector3 result = { v.x - sub, v.y - sub, v.z - sub }; + + return result; +} + +// Multiply vector by scalar +RMAPI RLVector3 Vector3Scale(RLVector3 v, float scalar) +{ + RLVector3 result = { v.x*scalar, v.y*scalar, v.z*scalar }; + + return result; +} + +// Multiply vector by vector +RMAPI RLVector3 Vector3Multiply(RLVector3 v1, RLVector3 v2) +{ + RLVector3 result = { v1.x*v2.x, v1.y*v2.y, v1.z*v2.z }; + + return result; +} + +// Calculate two vectors cross product +RMAPI RLVector3 Vector3CrossProduct(RLVector3 v1, RLVector3 v2) +{ + RLVector3 result = { v1.y*v2.z - v1.z*v2.y, v1.z*v2.x - v1.x*v2.z, v1.x*v2.y - v1.y*v2.x }; + + return result; +} + +// Calculate one vector perpendicular vector +RMAPI RLVector3 Vector3Perpendicular(RLVector3 v) +{ + RLVector3 result = { 0 }; + + float min = fabsf(v.x); + RLVector3 cardinalAxis = {1.0f, 0.0f, 0.0f}; + + if (fabsf(v.y) < min) + { + min = fabsf(v.y); + RLVector3 tmp = {0.0f, 1.0f, 0.0f}; + cardinalAxis = tmp; + } + + if (fabsf(v.z) < min) + { + RLVector3 tmp = {0.0f, 0.0f, 1.0f}; + cardinalAxis = tmp; + } + + // Cross product between vectors + result.x = v.y*cardinalAxis.z - v.z*cardinalAxis.y; + result.y = v.z*cardinalAxis.x - v.x*cardinalAxis.z; + result.z = v.x*cardinalAxis.y - v.y*cardinalAxis.x; + + return result; +} + +// Calculate vector length +RMAPI float Vector3Length(const RLVector3 v) +{ + float result = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z); + + return result; +} + +// Calculate vector square length +RMAPI float Vector3LengthSqr(const RLVector3 v) +{ + float result = v.x*v.x + v.y*v.y + v.z*v.z; + + return result; +} + +// Calculate two vectors dot product +RMAPI float Vector3DotProduct(RLVector3 v1, RLVector3 v2) +{ + float result = (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z); + + return result; +} + +// Calculate distance between two vectors +RMAPI float Vector3Distance(RLVector3 v1, RLVector3 v2) +{ + float result = 0.0f; + + float dx = v2.x - v1.x; + float dy = v2.y - v1.y; + float dz = v2.z - v1.z; + result = sqrtf(dx*dx + dy*dy + dz*dz); + + return result; +} + +// Calculate square distance between two vectors +RMAPI float Vector3DistanceSqr(RLVector3 v1, RLVector3 v2) +{ + float result = 0.0f; + + float dx = v2.x - v1.x; + float dy = v2.y - v1.y; + float dz = v2.z - v1.z; + result = dx*dx + dy*dy + dz*dz; + + return result; +} + +// Calculate angle between two vectors +RMAPI float Vector3Angle(RLVector3 v1, RLVector3 v2) +{ + float result = 0.0f; + + RLVector3 cross = { v1.y*v2.z - v1.z*v2.y, v1.z*v2.x - v1.x*v2.z, v1.x*v2.y - v1.y*v2.x }; + float len = sqrtf(cross.x*cross.x + cross.y*cross.y + cross.z*cross.z); + float dot = (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z); + result = atan2f(len, dot); + + return result; +} + +// Negate provided vector (invert direction) +RMAPI RLVector3 Vector3Negate(RLVector3 v) +{ + RLVector3 result = { -v.x, -v.y, -v.z }; + + return result; +} + +// Divide vector by vector +RMAPI RLVector3 Vector3Divide(RLVector3 v1, RLVector3 v2) +{ + RLVector3 result = { v1.x/v2.x, v1.y/v2.y, v1.z/v2.z }; + + return result; +} + +// Normalize provided vector +RMAPI RLVector3 Vector3Normalize(RLVector3 v) +{ + RLVector3 result = v; + + float length = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z); + if (length != 0.0f) + { + float ilength = 1.0f/length; + + result.x *= ilength; + result.y *= ilength; + result.z *= ilength; + } + + return result; +} + +//Calculate the projection of the vector v1 on to v2 +RMAPI RLVector3 Vector3Project(RLVector3 v1, RLVector3 v2) +{ + RLVector3 result = { 0 }; + + float v1dv2 = (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z); + float v2dv2 = (v2.x*v2.x + v2.y*v2.y + v2.z*v2.z); + + float mag = v1dv2/v2dv2; + + result.x = v2.x*mag; + result.y = v2.y*mag; + result.z = v2.z*mag; + + return result; +} + +//Calculate the rejection of the vector v1 on to v2 +RMAPI RLVector3 Vector3Reject(RLVector3 v1, RLVector3 v2) +{ + RLVector3 result = { 0 }; + + float v1dv2 = (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z); + float v2dv2 = (v2.x*v2.x + v2.y*v2.y + v2.z*v2.z); + + float mag = v1dv2/v2dv2; + + result.x = v1.x - (v2.x*mag); + result.y = v1.y - (v2.y*mag); + result.z = v1.z - (v2.z*mag); + + return result; +} + +// Orthonormalize provided vectors +// Makes vectors normalized and orthogonal to each other +// Gram-Schmidt function implementation +RMAPI void Vector3OrthoNormalize(RLVector3 *v1, RLVector3 *v2) +{ + float length = 0.0f; + float ilength = 0.0f; + + // Vector3Normalize(*v1); + RLVector3 v = *v1; + length = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z); + if (length == 0.0f) length = 1.0f; + ilength = 1.0f/length; + v1->x *= ilength; + v1->y *= ilength; + v1->z *= ilength; + + // Vector3CrossProduct(*v1, *v2) + RLVector3 vn1 = { v1->y*v2->z - v1->z*v2->y, v1->z*v2->x - v1->x*v2->z, v1->x*v2->y - v1->y*v2->x }; + + // Vector3Normalize(vn1); + v = vn1; + length = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z); + if (length == 0.0f) length = 1.0f; + ilength = 1.0f/length; + vn1.x *= ilength; + vn1.y *= ilength; + vn1.z *= ilength; + + // Vector3CrossProduct(vn1, *v1) + RLVector3 vn2 = { vn1.y*v1->z - vn1.z*v1->y, vn1.z*v1->x - vn1.x*v1->z, vn1.x*v1->y - vn1.y*v1->x }; + + *v2 = vn2; +} + +// Transforms a Vector3 by a given Matrix +RMAPI RLVector3 Vector3Transform(RLVector3 v, Matrix mat) +{ + RLVector3 result = { 0 }; + + float x = v.x; + float y = v.y; + float z = v.z; + + result.x = mat.m0*x + mat.m4*y + mat.m8*z + mat.m12; + result.y = mat.m1*x + mat.m5*y + mat.m9*z + mat.m13; + result.z = mat.m2*x + mat.m6*y + mat.m10*z + mat.m14; + + return result; +} + +// Transform a vector by quaternion rotation +RMAPI RLVector3 Vector3RotateByQuaternion(RLVector3 v, Quaternion q) +{ + RLVector3 result = { 0 }; + + result.x = v.x*(q.x*q.x + q.w*q.w - q.y*q.y - q.z*q.z) + v.y*(2*q.x*q.y - 2*q.w*q.z) + v.z*(2*q.x*q.z + 2*q.w*q.y); + result.y = v.x*(2*q.w*q.z + 2*q.x*q.y) + v.y*(q.w*q.w - q.x*q.x + q.y*q.y - q.z*q.z) + v.z*(-2*q.w*q.x + 2*q.y*q.z); + result.z = v.x*(-2*q.w*q.y + 2*q.x*q.z) + v.y*(2*q.w*q.x + 2*q.y*q.z)+ v.z*(q.w*q.w - q.x*q.x - q.y*q.y + q.z*q.z); + + return result; +} + +// Rotates a vector around an axis +RMAPI RLVector3 Vector3RotateByAxisAngle(RLVector3 v, RLVector3 axis, float angle) +{ + // Using Euler-Rodrigues Formula + // Ref.: https://en.wikipedia.org/w/index.php?title=Euler%E2%80%93Rodrigues_formula + + RLVector3 result = v; + + // Vector3Normalize(axis); + float length = sqrtf(axis.x*axis.x + axis.y*axis.y + axis.z*axis.z); + if (length == 0.0f) length = 1.0f; + float ilength = 1.0f/length; + axis.x *= ilength; + axis.y *= ilength; + axis.z *= ilength; + + angle /= 2.0f; + float a = sinf(angle); + float b = axis.x*a; + float c = axis.y*a; + float d = axis.z*a; + a = cosf(angle); + RLVector3 w = { b, c, d }; + + // Vector3CrossProduct(w, v) + RLVector3 wv = { w.y*v.z - w.z*v.y, w.z*v.x - w.x*v.z, w.x*v.y - w.y*v.x }; + + // Vector3CrossProduct(w, wv) + RLVector3 wwv = { w.y*wv.z - w.z*wv.y, w.z*wv.x - w.x*wv.z, w.x*wv.y - w.y*wv.x }; + + // Vector3Scale(wv, 2*a) + a *= 2; + wv.x *= a; + wv.y *= a; + wv.z *= a; + + // Vector3Scale(wwv, 2) + wwv.x *= 2; + wwv.y *= 2; + wwv.z *= 2; + + result.x += wv.x; + result.y += wv.y; + result.z += wv.z; + + result.x += wwv.x; + result.y += wwv.y; + result.z += wwv.z; + + return result; +} + +// Move Vector towards target +RMAPI RLVector3 Vector3MoveTowards(RLVector3 v, RLVector3 target, float maxDistance) +{ + RLVector3 result = { 0 }; + + float dx = target.x - v.x; + float dy = target.y - v.y; + float dz = target.z - v.z; + float value = (dx*dx) + (dy*dy) + (dz*dz); + + if ((value == 0) || ((maxDistance >= 0) && (value <= maxDistance*maxDistance))) return target; + + float dist = sqrtf(value); + + result.x = v.x + dx/dist*maxDistance; + result.y = v.y + dy/dist*maxDistance; + result.z = v.z + dz/dist*maxDistance; + + return result; +} + +// Calculate linear interpolation between two vectors +RMAPI RLVector3 Vector3Lerp(RLVector3 v1, RLVector3 v2, float amount) +{ + RLVector3 result = { 0 }; + + result.x = v1.x + amount*(v2.x - v1.x); + result.y = v1.y + amount*(v2.y - v1.y); + result.z = v1.z + amount*(v2.z - v1.z); + + return result; +} + +// Calculate cubic hermite interpolation between two vectors and their tangents +// as described in the GLTF 2.0 specification: https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#interpolation-cubic +RMAPI RLVector3 Vector3CubicHermite(RLVector3 v1, RLVector3 tangent1, RLVector3 v2, RLVector3 tangent2, float amount) +{ + RLVector3 result = { 0 }; + + float amountPow2 = amount*amount; + float amountPow3 = amount*amount*amount; + + result.x = (2*amountPow3 - 3*amountPow2 + 1)*v1.x + (amountPow3 - 2*amountPow2 + amount)*tangent1.x + (-2*amountPow3 + 3*amountPow2)*v2.x + (amountPow3 - amountPow2)*tangent2.x; + result.y = (2*amountPow3 - 3*amountPow2 + 1)*v1.y + (amountPow3 - 2*amountPow2 + amount)*tangent1.y + (-2*amountPow3 + 3*amountPow2)*v2.y + (amountPow3 - amountPow2)*tangent2.y; + result.z = (2*amountPow3 - 3*amountPow2 + 1)*v1.z + (amountPow3 - 2*amountPow2 + amount)*tangent1.z + (-2*amountPow3 + 3*amountPow2)*v2.z + (amountPow3 - amountPow2)*tangent2.z; + + return result; +} + +// Calculate reflected vector to normal +RMAPI RLVector3 Vector3Reflect(RLVector3 v, RLVector3 normal) +{ + RLVector3 result = { 0 }; + + // I is the original vector + // N is the normal of the incident plane + // R = I - (2*N*(DotProduct[I, N])) + + float dotProduct = (v.x*normal.x + v.y*normal.y + v.z*normal.z); + + result.x = v.x - (2.0f*normal.x)*dotProduct; + result.y = v.y - (2.0f*normal.y)*dotProduct; + result.z = v.z - (2.0f*normal.z)*dotProduct; + + return result; +} + +// Get min value for each pair of components +RMAPI RLVector3 Vector3Min(RLVector3 v1, RLVector3 v2) +{ + RLVector3 result = { 0 }; + + result.x = fminf(v1.x, v2.x); + result.y = fminf(v1.y, v2.y); + result.z = fminf(v1.z, v2.z); + + return result; +} + +// Get max value for each pair of components +RMAPI RLVector3 Vector3Max(RLVector3 v1, RLVector3 v2) +{ + RLVector3 result = { 0 }; + + result.x = fmaxf(v1.x, v2.x); + result.y = fmaxf(v1.y, v2.y); + result.z = fmaxf(v1.z, v2.z); + + return result; +} + +// Compute barycenter coordinates (u, v, w) for point p with respect to triangle (a, b, c) +// NOTE: Assumes P is on the plane of the triangle +RMAPI RLVector3 Vector3Barycenter(RLVector3 p, RLVector3 a, RLVector3 b, RLVector3 c) +{ + RLVector3 result = { 0 }; + + RLVector3 v0 = { b.x - a.x, b.y - a.y, b.z - a.z }; // Vector3Subtract(b, a) + RLVector3 v1 = { c.x - a.x, c.y - a.y, c.z - a.z }; // Vector3Subtract(c, a) + RLVector3 v2 = { p.x - a.x, p.y - a.y, p.z - a.z }; // Vector3Subtract(p, a) + float d00 = (v0.x*v0.x + v0.y*v0.y + v0.z*v0.z); // Vector3DotProduct(v0, v0) + float d01 = (v0.x*v1.x + v0.y*v1.y + v0.z*v1.z); // Vector3DotProduct(v0, v1) + float d11 = (v1.x*v1.x + v1.y*v1.y + v1.z*v1.z); // Vector3DotProduct(v1, v1) + float d20 = (v2.x*v0.x + v2.y*v0.y + v2.z*v0.z); // Vector3DotProduct(v2, v0) + float d21 = (v2.x*v1.x + v2.y*v1.y + v2.z*v1.z); // Vector3DotProduct(v2, v1) + + float denom = d00*d11 - d01*d01; + + result.y = (d11*d20 - d01*d21)/denom; + result.z = (d00*d21 - d01*d20)/denom; + result.x = 1.0f - (result.z + result.y); + + return result; +} + +// Projects a Vector3 from screen space into object space +// NOTE: We are avoiding calling other raymath functions despite available +RMAPI RLVector3 Vector3Unproject(RLVector3 source, Matrix projection, Matrix view) +{ + RLVector3 result = { 0 }; + + // Calculate unprojected matrix (multiply view matrix by projection matrix) and invert it + Matrix matViewProj = { // MatrixMultiply(view, projection); + view.m0*projection.m0 + view.m1*projection.m4 + view.m2*projection.m8 + view.m3*projection.m12, + view.m0*projection.m1 + view.m1*projection.m5 + view.m2*projection.m9 + view.m3*projection.m13, + view.m0*projection.m2 + view.m1*projection.m6 + view.m2*projection.m10 + view.m3*projection.m14, + view.m0*projection.m3 + view.m1*projection.m7 + view.m2*projection.m11 + view.m3*projection.m15, + view.m4*projection.m0 + view.m5*projection.m4 + view.m6*projection.m8 + view.m7*projection.m12, + view.m4*projection.m1 + view.m5*projection.m5 + view.m6*projection.m9 + view.m7*projection.m13, + view.m4*projection.m2 + view.m5*projection.m6 + view.m6*projection.m10 + view.m7*projection.m14, + view.m4*projection.m3 + view.m5*projection.m7 + view.m6*projection.m11 + view.m7*projection.m15, + view.m8*projection.m0 + view.m9*projection.m4 + view.m10*projection.m8 + view.m11*projection.m12, + view.m8*projection.m1 + view.m9*projection.m5 + view.m10*projection.m9 + view.m11*projection.m13, + view.m8*projection.m2 + view.m9*projection.m6 + view.m10*projection.m10 + view.m11*projection.m14, + view.m8*projection.m3 + view.m9*projection.m7 + view.m10*projection.m11 + view.m11*projection.m15, + view.m12*projection.m0 + view.m13*projection.m4 + view.m14*projection.m8 + view.m15*projection.m12, + view.m12*projection.m1 + view.m13*projection.m5 + view.m14*projection.m9 + view.m15*projection.m13, + view.m12*projection.m2 + view.m13*projection.m6 + view.m14*projection.m10 + view.m15*projection.m14, + view.m12*projection.m3 + view.m13*projection.m7 + view.m14*projection.m11 + view.m15*projection.m15 }; + + // Calculate inverted matrix -> MatrixInvert(matViewProj); + // Cache the matrix values (speed optimization) + float a00 = matViewProj.m0, a01 = matViewProj.m1, a02 = matViewProj.m2, a03 = matViewProj.m3; + float a10 = matViewProj.m4, a11 = matViewProj.m5, a12 = matViewProj.m6, a13 = matViewProj.m7; + float a20 = matViewProj.m8, a21 = matViewProj.m9, a22 = matViewProj.m10, a23 = matViewProj.m11; + float a30 = matViewProj.m12, a31 = matViewProj.m13, a32 = matViewProj.m14, a33 = matViewProj.m15; + + float b00 = a00*a11 - a01*a10; + float b01 = a00*a12 - a02*a10; + float b02 = a00*a13 - a03*a10; + float b03 = a01*a12 - a02*a11; + float b04 = a01*a13 - a03*a11; + float b05 = a02*a13 - a03*a12; + float b06 = a20*a31 - a21*a30; + float b07 = a20*a32 - a22*a30; + float b08 = a20*a33 - a23*a30; + float b09 = a21*a32 - a22*a31; + float b10 = a21*a33 - a23*a31; + float b11 = a22*a33 - a23*a32; + + // Calculate the invert determinant (inlined to avoid double-caching) + float invDet = 1.0f/(b00*b11 - b01*b10 + b02*b09 + b03*b08 - b04*b07 + b05*b06); + + Matrix matViewProjInv = { + (a11*b11 - a12*b10 + a13*b09)*invDet, + (-a01*b11 + a02*b10 - a03*b09)*invDet, + (a31*b05 - a32*b04 + a33*b03)*invDet, + (-a21*b05 + a22*b04 - a23*b03)*invDet, + (-a10*b11 + a12*b08 - a13*b07)*invDet, + (a00*b11 - a02*b08 + a03*b07)*invDet, + (-a30*b05 + a32*b02 - a33*b01)*invDet, + (a20*b05 - a22*b02 + a23*b01)*invDet, + (a10*b10 - a11*b08 + a13*b06)*invDet, + (-a00*b10 + a01*b08 - a03*b06)*invDet, + (a30*b04 - a31*b02 + a33*b00)*invDet, + (-a20*b04 + a21*b02 - a23*b00)*invDet, + (-a10*b09 + a11*b07 - a12*b06)*invDet, + (a00*b09 - a01*b07 + a02*b06)*invDet, + (-a30*b03 + a31*b01 - a32*b00)*invDet, + (a20*b03 - a21*b01 + a22*b00)*invDet }; + + // Create quaternion from source point + Quaternion quat = { source.x, source.y, source.z, 1.0f }; + + // Multiply quat point by unprojecte matrix + Quaternion qtransformed = { // QuaternionTransform(quat, matViewProjInv) + matViewProjInv.m0*quat.x + matViewProjInv.m4*quat.y + matViewProjInv.m8*quat.z + matViewProjInv.m12*quat.w, + matViewProjInv.m1*quat.x + matViewProjInv.m5*quat.y + matViewProjInv.m9*quat.z + matViewProjInv.m13*quat.w, + matViewProjInv.m2*quat.x + matViewProjInv.m6*quat.y + matViewProjInv.m10*quat.z + matViewProjInv.m14*quat.w, + matViewProjInv.m3*quat.x + matViewProjInv.m7*quat.y + matViewProjInv.m11*quat.z + matViewProjInv.m15*quat.w }; + + // Normalized world points in vectors + result.x = qtransformed.x/qtransformed.w; + result.y = qtransformed.y/qtransformed.w; + result.z = qtransformed.z/qtransformed.w; + + return result; +} + +// Get Vector3 as float array +RMAPI float3 Vector3ToFloatV(RLVector3 v) +{ + float3 buffer = { 0 }; + + buffer.v[0] = v.x; + buffer.v[1] = v.y; + buffer.v[2] = v.z; + + return buffer; +} + +// Invert the given vector +RMAPI RLVector3 Vector3Invert(RLVector3 v) +{ + RLVector3 result = { 1.0f/v.x, 1.0f/v.y, 1.0f/v.z }; + + return result; +} + +// Clamp the components of the vector between +// min and max values specified by the given vectors +RMAPI RLVector3 Vector3Clamp(RLVector3 v, RLVector3 min, RLVector3 max) +{ + RLVector3 result = { 0 }; + + result.x = fminf(max.x, fmaxf(min.x, v.x)); + result.y = fminf(max.y, fmaxf(min.y, v.y)); + result.z = fminf(max.z, fmaxf(min.z, v.z)); + + return result; +} + +// Clamp the magnitude of the vector between two values +RMAPI RLVector3 Vector3ClampValue(RLVector3 v, float min, float max) +{ + RLVector3 result = v; + + float length = (v.x*v.x) + (v.y*v.y) + (v.z*v.z); + if (length > 0.0f) + { + length = sqrtf(length); + + float scale = 1; // By default, 1 as the neutral element. + if (length < min) + { + scale = min/length; + } + else if (length > max) + { + scale = max/length; + } + + result.x = v.x*scale; + result.y = v.y*scale; + result.z = v.z*scale; + } + + return result; +} + +// Check whether two given vectors are almost equal +RMAPI int Vector3Equals(RLVector3 p, RLVector3 q) +{ +#if !defined(EPSILON) + #define EPSILON 0.000001f +#endif + + int result = ((fabsf(p.x - q.x)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.x), fabsf(q.x))))) && + ((fabsf(p.y - q.y)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.y), fabsf(q.y))))) && + ((fabsf(p.z - q.z)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.z), fabsf(q.z))))); + + return result; +} + +// Compute the direction of a refracted ray +// v: normalized direction of the incoming ray +// n: normalized normal vector of the interface of two optical media +// r: ratio of the refractive index of the medium from where the ray comes +// to the refractive index of the medium on the other side of the surface +RMAPI RLVector3 Vector3Refract(RLVector3 v, RLVector3 n, float r) +{ + RLVector3 result = { 0 }; + + float dot = v.x*n.x + v.y*n.y + v.z*n.z; + float d = 1.0f - r*r*(1.0f - dot*dot); + + if (d >= 0.0f) + { + d = sqrtf(d); + v.x = r*v.x - (r*dot + d)*n.x; + v.y = r*v.y - (r*dot + d)*n.y; + v.z = r*v.z - (r*dot + d)*n.z; + + result = v; + } + + return result; +} + + +//---------------------------------------------------------------------------------- +// Module Functions Definition - Vector4 math +//---------------------------------------------------------------------------------- + +RMAPI RLVector4 Vector4Zero(void) +{ + RLVector4 result = { 0.0f, 0.0f, 0.0f, 0.0f }; + return result; +} + +RMAPI RLVector4 Vector4One(void) +{ + RLVector4 result = { 1.0f, 1.0f, 1.0f, 1.0f }; + return result; +} + +RMAPI RLVector4 Vector4Add(RLVector4 v1, RLVector4 v2) +{ + RLVector4 result = { + v1.x + v2.x, + v1.y + v2.y, + v1.z + v2.z, + v1.w + v2.w + }; + return result; +} + +RMAPI RLVector4 Vector4AddValue(RLVector4 v, float add) +{ + RLVector4 result = { + v.x + add, + v.y + add, + v.z + add, + v.w + add + }; + return result; +} + +RMAPI RLVector4 Vector4Subtract(RLVector4 v1, RLVector4 v2) +{ + RLVector4 result = { + v1.x - v2.x, + v1.y - v2.y, + v1.z - v2.z, + v1.w - v2.w + }; + return result; +} + +RMAPI RLVector4 Vector4SubtractValue(RLVector4 v, float add) +{ + RLVector4 result = { + v.x - add, + v.y - add, + v.z - add, + v.w - add + }; + return result; +} + +RMAPI float Vector4Length(RLVector4 v) +{ + float result = sqrtf((v.x*v.x) + (v.y*v.y) + (v.z*v.z) + (v.w*v.w)); + return result; +} + +RMAPI float Vector4LengthSqr(RLVector4 v) +{ + float result = (v.x*v.x) + (v.y*v.y) + (v.z*v.z) + (v.w*v.w); + return result; +} + +RMAPI float Vector4DotProduct(RLVector4 v1, RLVector4 v2) +{ + float result = (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z + v1.w*v2.w); + return result; +} + +// Calculate distance between two vectors +RMAPI float Vector4Distance(RLVector4 v1, RLVector4 v2) +{ + float result = sqrtf( + (v1.x - v2.x)*(v1.x - v2.x) + (v1.y - v2.y)*(v1.y - v2.y) + + (v1.z - v2.z)*(v1.z - v2.z) + (v1.w - v2.w)*(v1.w - v2.w)); + return result; +} + +// Calculate square distance between two vectors +RMAPI float Vector4DistanceSqr(RLVector4 v1, RLVector4 v2) +{ + float result = + (v1.x - v2.x)*(v1.x - v2.x) + (v1.y - v2.y)*(v1.y - v2.y) + + (v1.z - v2.z)*(v1.z - v2.z) + (v1.w - v2.w)*(v1.w - v2.w); + + return result; +} + +RMAPI RLVector4 Vector4Scale(RLVector4 v, float scale) +{ + RLVector4 result = { v.x*scale, v.y*scale, v.z*scale, v.w*scale }; + return result; +} + +// Multiply vector by vector +RMAPI RLVector4 Vector4Multiply(RLVector4 v1, RLVector4 v2) +{ + RLVector4 result = { v1.x*v2.x, v1.y*v2.y, v1.z*v2.z, v1.w*v2.w }; + return result; +} + +// Negate vector +RMAPI RLVector4 Vector4Negate(RLVector4 v) +{ + RLVector4 result = { -v.x, -v.y, -v.z, -v.w }; + return result; +} + +// Divide vector by vector +RMAPI RLVector4 Vector4Divide(RLVector4 v1, RLVector4 v2) +{ + RLVector4 result = { v1.x/v2.x, v1.y/v2.y, v1.z/v2.z, v1.w/v2.w }; + return result; +} + +// Normalize provided vector +RMAPI RLVector4 Vector4Normalize(RLVector4 v) +{ + RLVector4 result = { 0 }; + float length = sqrtf((v.x*v.x) + (v.y*v.y) + (v.z*v.z) + (v.w*v.w)); + + if (length > 0) + { + float ilength = 1.0f/length; + result.x = v.x*ilength; + result.y = v.y*ilength; + result.z = v.z*ilength; + result.w = v.w*ilength; + } + + return result; +} + +// Get min value for each pair of components +RMAPI RLVector4 Vector4Min(RLVector4 v1, RLVector4 v2) +{ + RLVector4 result = { 0 }; + + result.x = fminf(v1.x, v2.x); + result.y = fminf(v1.y, v2.y); + result.z = fminf(v1.z, v2.z); + result.w = fminf(v1.w, v2.w); + + return result; +} + +// Get max value for each pair of components +RMAPI RLVector4 Vector4Max(RLVector4 v1, RLVector4 v2) +{ + RLVector4 result = { 0 }; + + result.x = fmaxf(v1.x, v2.x); + result.y = fmaxf(v1.y, v2.y); + result.z = fmaxf(v1.z, v2.z); + result.w = fmaxf(v1.w, v2.w); + + return result; +} + +// Calculate linear interpolation between two vectors +RMAPI RLVector4 Vector4Lerp(RLVector4 v1, RLVector4 v2, float amount) +{ + RLVector4 result = { 0 }; + + result.x = v1.x + amount*(v2.x - v1.x); + result.y = v1.y + amount*(v2.y - v1.y); + result.z = v1.z + amount*(v2.z - v1.z); + result.w = v1.w + amount*(v2.w - v1.w); + + return result; +} + +// Move Vector towards target +RMAPI RLVector4 Vector4MoveTowards(RLVector4 v, RLVector4 target, float maxDistance) +{ + RLVector4 result = { 0 }; + + float dx = target.x - v.x; + float dy = target.y - v.y; + float dz = target.z - v.z; + float dw = target.w - v.w; + float value = (dx*dx) + (dy*dy) + (dz*dz) + (dw*dw); + + if ((value == 0) || ((maxDistance >= 0) && (value <= maxDistance*maxDistance))) return target; + + float dist = sqrtf(value); + + result.x = v.x + dx/dist*maxDistance; + result.y = v.y + dy/dist*maxDistance; + result.z = v.z + dz/dist*maxDistance; + result.w = v.w + dw/dist*maxDistance; + + return result; +} + +// Invert the given vector +RMAPI RLVector4 Vector4Invert(RLVector4 v) +{ + RLVector4 result = { 1.0f/v.x, 1.0f/v.y, 1.0f/v.z, 1.0f/v.w }; + return result; +} + +// Check whether two given vectors are almost equal +RMAPI int Vector4Equals(RLVector4 p, RLVector4 q) +{ +#if !defined(EPSILON) + #define EPSILON 0.000001f +#endif + + int result = ((fabsf(p.x - q.x)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.x), fabsf(q.x))))) && + ((fabsf(p.y - q.y)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.y), fabsf(q.y))))) && + ((fabsf(p.z - q.z)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.z), fabsf(q.z))))) && + ((fabsf(p.w - q.w)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.w), fabsf(q.w))))); + return result; +} + + +//---------------------------------------------------------------------------------- +// Module Functions Definition - Matrix math +//---------------------------------------------------------------------------------- + +// Compute matrix determinant +RMAPI float MatrixDeterminant(Matrix mat) +{ + float result = 0.0f; +/* + // Cache the matrix values (speed optimization) + float a00 = mat.m0, a01 = mat.m1, a02 = mat.m2, a03 = mat.m3; + float a10 = mat.m4, a11 = mat.m5, a12 = mat.m6, a13 = mat.m7; + float a20 = mat.m8, a21 = mat.m9, a22 = mat.m10, a23 = mat.m11; + float a30 = mat.m12, a31 = mat.m13, a32 = mat.m14, a33 = mat.m15; + + // NOTE: It takes 72 multiplication to calculate 4x4 matrix determinant + result = a30*a21*a12*a03 - a20*a31*a12*a03 - a30*a11*a22*a03 + a10*a31*a22*a03 + + a20*a11*a32*a03 - a10*a21*a32*a03 - a30*a21*a02*a13 + a20*a31*a02*a13 + + a30*a01*a22*a13 - a00*a31*a22*a13 - a20*a01*a32*a13 + a00*a21*a32*a13 + + a30*a11*a02*a23 - a10*a31*a02*a23 - a30*a01*a12*a23 + a00*a31*a12*a23 + + a10*a01*a32*a23 - a00*a11*a32*a23 - a20*a11*a02*a33 + a10*a21*a02*a33 + + a20*a01*a12*a33 - a00*a21*a12*a33 - a10*a01*a22*a33 + a00*a11*a22*a33; +*/ + // Using Laplace expansion (https://en.wikipedia.org/wiki/Laplace_expansion), + // previous operation can be simplified to 40 multiplications, decreasing matrix + // size from 4x4 to 2x2 using minors + + // Cache the matrix values (speed optimization) + float m0 = mat.m0, m1 = mat.m1, m2 = mat.m2, m3 = mat.m3; + float m4 = mat.m4, m5 = mat.m5, m6 = mat.m6, m7 = mat.m7; + float m8 = mat.m8, m9 = mat.m9, m10 = mat.m10, m11 = mat.m11; + float m12 = mat.m12, m13 = mat.m13, m14 = mat.m14, m15 = mat.m15; + + result = (m0*((m5*(m10*m15 - m11*m14) - m9*(m6*m15 - m7*m14) + m13*(m6*m11 - m7*m10))) - + m4*((m1*(m10*m15 - m11*m14) - m9*(m2*m15 - m3*m14) + m13*(m2*m11 - m3*m10))) + + m8*((m1*(m6*m15 - m7*m14) - m5*(m2*m15 - m3*m14) + m13*(m2*m7 - m3*m6))) - + m12*((m1*(m6*m11 - m7*m10) - m5*(m2*m11 - m3*m10) + m9*(m2*m7 - m3*m6)))); + + return result; +} + +// Get the trace of the matrix (sum of the values along the diagonal) +RMAPI float MatrixTrace(Matrix mat) +{ + float result = (mat.m0 + mat.m5 + mat.m10 + mat.m15); + + return result; +} + +// Transposes provided matrix +RMAPI Matrix MatrixTranspose(Matrix mat) +{ + Matrix result = { 0 }; + + result.m0 = mat.m0; + result.m1 = mat.m4; + result.m2 = mat.m8; + result.m3 = mat.m12; + result.m4 = mat.m1; + result.m5 = mat.m5; + result.m6 = mat.m9; + result.m7 = mat.m13; + result.m8 = mat.m2; + result.m9 = mat.m6; + result.m10 = mat.m10; + result.m11 = mat.m14; + result.m12 = mat.m3; + result.m13 = mat.m7; + result.m14 = mat.m11; + result.m15 = mat.m15; + + return result; +} + +// Invert provided matrix +RMAPI Matrix MatrixInvert(Matrix mat) +{ + Matrix result = { 0 }; + + // Cache the matrix values (speed optimization) + float a00 = mat.m0, a01 = mat.m1, a02 = mat.m2, a03 = mat.m3; + float a10 = mat.m4, a11 = mat.m5, a12 = mat.m6, a13 = mat.m7; + float a20 = mat.m8, a21 = mat.m9, a22 = mat.m10, a23 = mat.m11; + float a30 = mat.m12, a31 = mat.m13, a32 = mat.m14, a33 = mat.m15; + + float b00 = a00*a11 - a01*a10; + float b01 = a00*a12 - a02*a10; + float b02 = a00*a13 - a03*a10; + float b03 = a01*a12 - a02*a11; + float b04 = a01*a13 - a03*a11; + float b05 = a02*a13 - a03*a12; + float b06 = a20*a31 - a21*a30; + float b07 = a20*a32 - a22*a30; + float b08 = a20*a33 - a23*a30; + float b09 = a21*a32 - a22*a31; + float b10 = a21*a33 - a23*a31; + float b11 = a22*a33 - a23*a32; + + // Calculate the invert determinant (inlined to avoid double-caching) + float invDet = 1.0f/(b00*b11 - b01*b10 + b02*b09 + b03*b08 - b04*b07 + b05*b06); + + result.m0 = (a11*b11 - a12*b10 + a13*b09)*invDet; + result.m1 = (-a01*b11 + a02*b10 - a03*b09)*invDet; + result.m2 = (a31*b05 - a32*b04 + a33*b03)*invDet; + result.m3 = (-a21*b05 + a22*b04 - a23*b03)*invDet; + result.m4 = (-a10*b11 + a12*b08 - a13*b07)*invDet; + result.m5 = (a00*b11 - a02*b08 + a03*b07)*invDet; + result.m6 = (-a30*b05 + a32*b02 - a33*b01)*invDet; + result.m7 = (a20*b05 - a22*b02 + a23*b01)*invDet; + result.m8 = (a10*b10 - a11*b08 + a13*b06)*invDet; + result.m9 = (-a00*b10 + a01*b08 - a03*b06)*invDet; + result.m10 = (a30*b04 - a31*b02 + a33*b00)*invDet; + result.m11 = (-a20*b04 + a21*b02 - a23*b00)*invDet; + result.m12 = (-a10*b09 + a11*b07 - a12*b06)*invDet; + result.m13 = (a00*b09 - a01*b07 + a02*b06)*invDet; + result.m14 = (-a30*b03 + a31*b01 - a32*b00)*invDet; + result.m15 = (a20*b03 - a21*b01 + a22*b00)*invDet; + + return result; +} + +// Get identity matrix +RMAPI Matrix MatrixIdentity(void) +{ + Matrix result = { 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f }; + + return result; +} + +// Add two matrices +RMAPI Matrix MatrixAdd(Matrix left, Matrix right) +{ + Matrix result = { 0 }; + + result.m0 = left.m0 + right.m0; + result.m1 = left.m1 + right.m1; + result.m2 = left.m2 + right.m2; + result.m3 = left.m3 + right.m3; + result.m4 = left.m4 + right.m4; + result.m5 = left.m5 + right.m5; + result.m6 = left.m6 + right.m6; + result.m7 = left.m7 + right.m7; + result.m8 = left.m8 + right.m8; + result.m9 = left.m9 + right.m9; + result.m10 = left.m10 + right.m10; + result.m11 = left.m11 + right.m11; + result.m12 = left.m12 + right.m12; + result.m13 = left.m13 + right.m13; + result.m14 = left.m14 + right.m14; + result.m15 = left.m15 + right.m15; + + return result; +} + +// Subtract two matrices (left - right) +RMAPI Matrix MatrixSubtract(Matrix left, Matrix right) +{ + Matrix result = { 0 }; + + result.m0 = left.m0 - right.m0; + result.m1 = left.m1 - right.m1; + result.m2 = left.m2 - right.m2; + result.m3 = left.m3 - right.m3; + result.m4 = left.m4 - right.m4; + result.m5 = left.m5 - right.m5; + result.m6 = left.m6 - right.m6; + result.m7 = left.m7 - right.m7; + result.m8 = left.m8 - right.m8; + result.m9 = left.m9 - right.m9; + result.m10 = left.m10 - right.m10; + result.m11 = left.m11 - right.m11; + result.m12 = left.m12 - right.m12; + result.m13 = left.m13 - right.m13; + result.m14 = left.m14 - right.m14; + result.m15 = left.m15 - right.m15; + + return result; +} + +// Get two matrix multiplication +// NOTE: When multiplying matrices... the order matters! +RMAPI Matrix MatrixMultiply(Matrix left, Matrix right) +{ + Matrix result = { 0 }; + + result.m0 = left.m0*right.m0 + left.m1*right.m4 + left.m2*right.m8 + left.m3*right.m12; + result.m1 = left.m0*right.m1 + left.m1*right.m5 + left.m2*right.m9 + left.m3*right.m13; + result.m2 = left.m0*right.m2 + left.m1*right.m6 + left.m2*right.m10 + left.m3*right.m14; + result.m3 = left.m0*right.m3 + left.m1*right.m7 + left.m2*right.m11 + left.m3*right.m15; + result.m4 = left.m4*right.m0 + left.m5*right.m4 + left.m6*right.m8 + left.m7*right.m12; + result.m5 = left.m4*right.m1 + left.m5*right.m5 + left.m6*right.m9 + left.m7*right.m13; + result.m6 = left.m4*right.m2 + left.m5*right.m6 + left.m6*right.m10 + left.m7*right.m14; + result.m7 = left.m4*right.m3 + left.m5*right.m7 + left.m6*right.m11 + left.m7*right.m15; + result.m8 = left.m8*right.m0 + left.m9*right.m4 + left.m10*right.m8 + left.m11*right.m12; + result.m9 = left.m8*right.m1 + left.m9*right.m5 + left.m10*right.m9 + left.m11*right.m13; + result.m10 = left.m8*right.m2 + left.m9*right.m6 + left.m10*right.m10 + left.m11*right.m14; + result.m11 = left.m8*right.m3 + left.m9*right.m7 + left.m10*right.m11 + left.m11*right.m15; + result.m12 = left.m12*right.m0 + left.m13*right.m4 + left.m14*right.m8 + left.m15*right.m12; + result.m13 = left.m12*right.m1 + left.m13*right.m5 + left.m14*right.m9 + left.m15*right.m13; + result.m14 = left.m12*right.m2 + left.m13*right.m6 + left.m14*right.m10 + left.m15*right.m14; + result.m15 = left.m12*right.m3 + left.m13*right.m7 + left.m14*right.m11 + left.m15*right.m15; + + return result; +} + +// Get translation matrix +RMAPI Matrix MatrixTranslate(float x, float y, float z) +{ + Matrix result = { 1.0f, 0.0f, 0.0f, x, + 0.0f, 1.0f, 0.0f, y, + 0.0f, 0.0f, 1.0f, z, + 0.0f, 0.0f, 0.0f, 1.0f }; + + return result; +} + +// Create rotation matrix from axis and angle +// NOTE: Angle should be provided in radians +RMAPI Matrix MatrixRotate(RLVector3 axis, float angle) +{ + Matrix result = { 0 }; + + float x = axis.x, y = axis.y, z = axis.z; + + float lengthSquared = x*x + y*y + z*z; + + if ((lengthSquared != 1.0f) && (lengthSquared != 0.0f)) + { + float ilength = 1.0f/sqrtf(lengthSquared); + x *= ilength; + y *= ilength; + z *= ilength; + } + + float sinres = sinf(angle); + float cosres = cosf(angle); + float t = 1.0f - cosres; + + result.m0 = x*x*t + cosres; + result.m1 = y*x*t + z*sinres; + result.m2 = z*x*t - y*sinres; + result.m3 = 0.0f; + + result.m4 = x*y*t - z*sinres; + result.m5 = y*y*t + cosres; + result.m6 = z*y*t + x*sinres; + result.m7 = 0.0f; + + result.m8 = x*z*t + y*sinres; + result.m9 = y*z*t - x*sinres; + result.m10 = z*z*t + cosres; + result.m11 = 0.0f; + + result.m12 = 0.0f; + result.m13 = 0.0f; + result.m14 = 0.0f; + result.m15 = 1.0f; + + return result; +} + +// Get x-rotation matrix +// NOTE: Angle must be provided in radians +RMAPI Matrix MatrixRotateX(float angle) +{ + Matrix result = { 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f }; // MatrixIdentity() + + float cosres = cosf(angle); + float sinres = sinf(angle); + + result.m5 = cosres; + result.m6 = sinres; + result.m9 = -sinres; + result.m10 = cosres; + + return result; +} + +// Get y-rotation matrix +// NOTE: Angle must be provided in radians +RMAPI Matrix MatrixRotateY(float angle) +{ + Matrix result = { 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f }; // MatrixIdentity() + + float cosres = cosf(angle); + float sinres = sinf(angle); + + result.m0 = cosres; + result.m2 = -sinres; + result.m8 = sinres; + result.m10 = cosres; + + return result; +} + +// Get z-rotation matrix +// NOTE: Angle must be provided in radians +RMAPI Matrix MatrixRotateZ(float angle) +{ + Matrix result = { 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f }; // MatrixIdentity() + + float cosres = cosf(angle); + float sinres = sinf(angle); + + result.m0 = cosres; + result.m1 = sinres; + result.m4 = -sinres; + result.m5 = cosres; + + return result; +} + + +// Get xyz-rotation matrix +// NOTE: Angle must be provided in radians +RMAPI Matrix MatrixRotateXYZ(RLVector3 angle) +{ + Matrix result = { 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f }; // MatrixIdentity() + + float cosz = cosf(-angle.z); + float sinz = sinf(-angle.z); + float cosy = cosf(-angle.y); + float siny = sinf(-angle.y); + float cosx = cosf(-angle.x); + float sinx = sinf(-angle.x); + + result.m0 = cosz*cosy; + result.m1 = (cosz*siny*sinx) - (sinz*cosx); + result.m2 = (cosz*siny*cosx) + (sinz*sinx); + + result.m4 = sinz*cosy; + result.m5 = (sinz*siny*sinx) + (cosz*cosx); + result.m6 = (sinz*siny*cosx) - (cosz*sinx); + + result.m8 = -siny; + result.m9 = cosy*sinx; + result.m10= cosy*cosx; + + return result; +} + +// Get zyx-rotation matrix +// NOTE: Angle must be provided in radians +RMAPI Matrix MatrixRotateZYX(RLVector3 angle) +{ + Matrix result = { 0 }; + + float cz = cosf(angle.z); + float sz = sinf(angle.z); + float cy = cosf(angle.y); + float sy = sinf(angle.y); + float cx = cosf(angle.x); + float sx = sinf(angle.x); + + result.m0 = cz*cy; + result.m4 = cz*sy*sx - cx*sz; + result.m8 = sz*sx + cz*cx*sy; + result.m12 = 0; + + result.m1 = cy*sz; + result.m5 = cz*cx + sz*sy*sx; + result.m9 = cx*sz*sy - cz*sx; + result.m13 = 0; + + result.m2 = -sy; + result.m6 = cy*sx; + result.m10 = cy*cx; + result.m14 = 0; + + result.m3 = 0; + result.m7 = 0; + result.m11 = 0; + result.m15 = 1; + + return result; +} + +// Get scaling matrix +RMAPI Matrix MatrixScale(float x, float y, float z) +{ + Matrix result = { x, 0.0f, 0.0f, 0.0f, + 0.0f, y, 0.0f, 0.0f, + 0.0f, 0.0f, z, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f }; + + return result; +} + +// Get perspective projection matrix +RMAPI Matrix MatrixFrustum(double left, double right, double bottom, double top, double nearPlane, double farPlane) +{ + Matrix result = { 0 }; + + float rl = (float)(right - left); + float tb = (float)(top - bottom); + float fn = (float)(farPlane - nearPlane); + + result.m0 = ((float)nearPlane*2.0f)/rl; + result.m1 = 0.0f; + result.m2 = 0.0f; + result.m3 = 0.0f; + + result.m4 = 0.0f; + result.m5 = ((float)nearPlane*2.0f)/tb; + result.m6 = 0.0f; + result.m7 = 0.0f; + + result.m8 = ((float)right + (float)left)/rl; + result.m9 = ((float)top + (float)bottom)/tb; + result.m10 = -((float)farPlane + (float)nearPlane)/fn; + result.m11 = -1.0f; + + result.m12 = 0.0f; + result.m13 = 0.0f; + result.m14 = -((float)farPlane*(float)nearPlane*2.0f)/fn; + result.m15 = 0.0f; + + return result; +} + +// Get perspective projection matrix +// NOTE: Fovy angle must be provided in radians +RMAPI Matrix MatrixPerspective(double fovY, double aspect, double nearPlane, double farPlane) +{ + Matrix result = { 0 }; + + double top = nearPlane*tan(fovY*0.5); + double bottom = -top; + double right = top*aspect; + double left = -right; + + // MatrixFrustum(-right, right, -top, top, near, far); + float rl = (float)(right - left); + float tb = (float)(top - bottom); + float fn = (float)(farPlane - nearPlane); + + result.m0 = ((float)nearPlane*2.0f)/rl; + result.m5 = ((float)nearPlane*2.0f)/tb; + result.m8 = ((float)right + (float)left)/rl; + result.m9 = ((float)top + (float)bottom)/tb; + result.m10 = -((float)farPlane + (float)nearPlane)/fn; + result.m11 = -1.0f; + result.m14 = -((float)farPlane*(float)nearPlane*2.0f)/fn; + + return result; +} + +// Get orthographic projection matrix +RMAPI Matrix MatrixOrtho(double left, double right, double bottom, double top, double nearPlane, double farPlane) +{ + Matrix result = { 0 }; + + float rl = (float)(right - left); + float tb = (float)(top - bottom); + float fn = (float)(farPlane - nearPlane); + + result.m0 = 2.0f/rl; + result.m1 = 0.0f; + result.m2 = 0.0f; + result.m3 = 0.0f; + result.m4 = 0.0f; + result.m5 = 2.0f/tb; + result.m6 = 0.0f; + result.m7 = 0.0f; + result.m8 = 0.0f; + result.m9 = 0.0f; + result.m10 = -2.0f/fn; + result.m11 = 0.0f; + result.m12 = -((float)left + (float)right)/rl; + result.m13 = -((float)top + (float)bottom)/tb; + result.m14 = -((float)farPlane + (float)nearPlane)/fn; + result.m15 = 1.0f; + + return result; +} + +// Get camera look-at matrix (view matrix) +RMAPI Matrix MatrixLookAt(RLVector3 eye, RLVector3 target, RLVector3 up) +{ + Matrix result = { 0 }; + + float length = 0.0f; + float ilength = 0.0f; + + // Vector3Subtract(eye, target) + RLVector3 vz = { eye.x - target.x, eye.y - target.y, eye.z - target.z }; + + // Vector3Normalize(vz) + RLVector3 v = vz; + length = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z); + if (length == 0.0f) length = 1.0f; + ilength = 1.0f/length; + vz.x *= ilength; + vz.y *= ilength; + vz.z *= ilength; + + // Vector3CrossProduct(up, vz) + RLVector3 vx = { up.y*vz.z - up.z*vz.y, up.z*vz.x - up.x*vz.z, up.x*vz.y - up.y*vz.x }; + + // Vector3Normalize(x) + v = vx; + length = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z); + if (length == 0.0f) length = 1.0f; + ilength = 1.0f/length; + vx.x *= ilength; + vx.y *= ilength; + vx.z *= ilength; + + // Vector3CrossProduct(vz, vx) + RLVector3 vy = { vz.y*vx.z - vz.z*vx.y, vz.z*vx.x - vz.x*vx.z, vz.x*vx.y - vz.y*vx.x }; + + result.m0 = vx.x; + result.m1 = vy.x; + result.m2 = vz.x; + result.m3 = 0.0f; + result.m4 = vx.y; + result.m5 = vy.y; + result.m6 = vz.y; + result.m7 = 0.0f; + result.m8 = vx.z; + result.m9 = vy.z; + result.m10 = vz.z; + result.m11 = 0.0f; + result.m12 = -(vx.x*eye.x + vx.y*eye.y + vx.z*eye.z); // Vector3DotProduct(vx, eye) + result.m13 = -(vy.x*eye.x + vy.y*eye.y + vy.z*eye.z); // Vector3DotProduct(vy, eye) + result.m14 = -(vz.x*eye.x + vz.y*eye.y + vz.z*eye.z); // Vector3DotProduct(vz, eye) + result.m15 = 1.0f; + + return result; +} + +// Get float array of matrix data +RMAPI float16 MatrixToFloatV(Matrix mat) +{ + float16 result = { 0 }; + + result.v[0] = mat.m0; + result.v[1] = mat.m1; + result.v[2] = mat.m2; + result.v[3] = mat.m3; + result.v[4] = mat.m4; + result.v[5] = mat.m5; + result.v[6] = mat.m6; + result.v[7] = mat.m7; + result.v[8] = mat.m8; + result.v[9] = mat.m9; + result.v[10] = mat.m10; + result.v[11] = mat.m11; + result.v[12] = mat.m12; + result.v[13] = mat.m13; + result.v[14] = mat.m14; + result.v[15] = mat.m15; + + return result; +} + +//---------------------------------------------------------------------------------- +// Module Functions Definition - Quaternion math +//---------------------------------------------------------------------------------- + +// Add two quaternions +RMAPI Quaternion QuaternionAdd(Quaternion q1, Quaternion q2) +{ + Quaternion result = {q1.x + q2.x, q1.y + q2.y, q1.z + q2.z, q1.w + q2.w}; + + return result; +} + +// Add quaternion and float value +RMAPI Quaternion QuaternionAddValue(Quaternion q, float add) +{ + Quaternion result = {q.x + add, q.y + add, q.z + add, q.w + add}; + + return result; +} + +// Subtract two quaternions +RMAPI Quaternion QuaternionSubtract(Quaternion q1, Quaternion q2) +{ + Quaternion result = {q1.x - q2.x, q1.y - q2.y, q1.z - q2.z, q1.w - q2.w}; + + return result; +} + +// Subtract quaternion and float value +RMAPI Quaternion QuaternionSubtractValue(Quaternion q, float sub) +{ + Quaternion result = {q.x - sub, q.y - sub, q.z - sub, q.w - sub}; + + return result; +} + +// Get identity quaternion +RMAPI Quaternion QuaternionIdentity(void) +{ + Quaternion result = { 0.0f, 0.0f, 0.0f, 1.0f }; + + return result; +} + +// Computes the length of a quaternion +RMAPI float QuaternionLength(Quaternion q) +{ + float result = sqrtf(q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w); + + return result; +} + +// Normalize provided quaternion +RMAPI Quaternion QuaternionNormalize(Quaternion q) +{ + Quaternion result = { 0 }; + + float length = sqrtf(q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w); + if (length == 0.0f) length = 1.0f; + float ilength = 1.0f/length; + + result.x = q.x*ilength; + result.y = q.y*ilength; + result.z = q.z*ilength; + result.w = q.w*ilength; + + return result; +} + +// Invert provided quaternion +RMAPI Quaternion QuaternionInvert(Quaternion q) +{ + Quaternion result = q; + + float lengthSq = q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w; + + if (lengthSq != 0.0f) + { + float invLength = 1.0f/lengthSq; + + result.x *= -invLength; + result.y *= -invLength; + result.z *= -invLength; + result.w *= invLength; + } + + return result; +} + +// Calculate two quaternion multiplication +RMAPI Quaternion QuaternionMultiply(Quaternion q1, Quaternion q2) +{ + Quaternion result = { 0 }; + + float qax = q1.x, qay = q1.y, qaz = q1.z, qaw = q1.w; + float qbx = q2.x, qby = q2.y, qbz = q2.z, qbw = q2.w; + + result.x = qax*qbw + qaw*qbx + qay*qbz - qaz*qby; + result.y = qay*qbw + qaw*qby + qaz*qbx - qax*qbz; + result.z = qaz*qbw + qaw*qbz + qax*qby - qay*qbx; + result.w = qaw*qbw - qax*qbx - qay*qby - qaz*qbz; + + return result; +} + +// Scale quaternion by float value +RMAPI Quaternion QuaternionScale(Quaternion q, float mul) +{ + Quaternion result = { 0 }; + + result.x = q.x*mul; + result.y = q.y*mul; + result.z = q.z*mul; + result.w = q.w*mul; + + return result; +} + +// Divide two quaternions +RMAPI Quaternion QuaternionDivide(Quaternion q1, Quaternion q2) +{ + Quaternion result = { q1.x/q2.x, q1.y/q2.y, q1.z/q2.z, q1.w/q2.w }; + + return result; +} + +// Calculate linear interpolation between two quaternions +RMAPI Quaternion QuaternionLerp(Quaternion q1, Quaternion q2, float amount) +{ + Quaternion result = { 0 }; + + result.x = q1.x + amount*(q2.x - q1.x); + result.y = q1.y + amount*(q2.y - q1.y); + result.z = q1.z + amount*(q2.z - q1.z); + result.w = q1.w + amount*(q2.w - q1.w); + + return result; +} + +// Calculate slerp-optimized interpolation between two quaternions +RMAPI Quaternion QuaternionNlerp(Quaternion q1, Quaternion q2, float amount) +{ + Quaternion result = { 0 }; + + // QuaternionLerp(q1, q2, amount) + result.x = q1.x + amount*(q2.x - q1.x); + result.y = q1.y + amount*(q2.y - q1.y); + result.z = q1.z + amount*(q2.z - q1.z); + result.w = q1.w + amount*(q2.w - q1.w); + + // QuaternionNormalize(q); + Quaternion q = result; + float length = sqrtf(q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w); + if (length == 0.0f) length = 1.0f; + float ilength = 1.0f/length; + + result.x = q.x*ilength; + result.y = q.y*ilength; + result.z = q.z*ilength; + result.w = q.w*ilength; + + return result; +} + +// Calculates spherical linear interpolation between two quaternions +RMAPI Quaternion QuaternionSlerp(Quaternion q1, Quaternion q2, float amount) +{ + Quaternion result = { 0 }; + +#if !defined(EPSILON) + #define EPSILON 0.000001f +#endif + + float cosHalfTheta = q1.x*q2.x + q1.y*q2.y + q1.z*q2.z + q1.w*q2.w; + + if (cosHalfTheta < 0) + { + q2.x = -q2.x; q2.y = -q2.y; q2.z = -q2.z; q2.w = -q2.w; + cosHalfTheta = -cosHalfTheta; + } + + if (fabsf(cosHalfTheta) >= 1.0f) result = q1; + else if (cosHalfTheta > 0.95f) result = QuaternionNlerp(q1, q2, amount); + else + { + float halfTheta = acosf(cosHalfTheta); + float sinHalfTheta = sqrtf(1.0f - cosHalfTheta*cosHalfTheta); + + if (fabsf(sinHalfTheta) < EPSILON) + { + result.x = (q1.x*0.5f + q2.x*0.5f); + result.y = (q1.y*0.5f + q2.y*0.5f); + result.z = (q1.z*0.5f + q2.z*0.5f); + result.w = (q1.w*0.5f + q2.w*0.5f); + } + else + { + float ratioA = sinf((1 - amount)*halfTheta)/sinHalfTheta; + float ratioB = sinf(amount*halfTheta)/sinHalfTheta; + + result.x = (q1.x*ratioA + q2.x*ratioB); + result.y = (q1.y*ratioA + q2.y*ratioB); + result.z = (q1.z*ratioA + q2.z*ratioB); + result.w = (q1.w*ratioA + q2.w*ratioB); + } + } + + return result; +} + +// Calculate quaternion cubic spline interpolation using Cubic Hermite Spline algorithm +// as described in the GLTF 2.0 specification: https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#interpolation-cubic +RMAPI Quaternion QuaternionCubicHermiteSpline(Quaternion q1, Quaternion outTangent1, Quaternion q2, Quaternion inTangent2, float t) +{ + float t2 = t*t; + float t3 = t2*t; + float h00 = 2*t3 - 3*t2 + 1; + float h10 = t3 - 2*t2 + t; + float h01 = -2*t3 + 3*t2; + float h11 = t3 - t2; + + Quaternion p0 = QuaternionScale(q1, h00); + Quaternion m0 = QuaternionScale(outTangent1, h10); + Quaternion p1 = QuaternionScale(q2, h01); + Quaternion m1 = QuaternionScale(inTangent2, h11); + + Quaternion result = { 0 }; + + result = QuaternionAdd(p0, m0); + result = QuaternionAdd(result, p1); + result = QuaternionAdd(result, m1); + result = QuaternionNormalize(result); + + return result; +} + +// Calculate quaternion based on the rotation from one vector to another +RMAPI Quaternion QuaternionFromVector3ToVector3(RLVector3 from, RLVector3 to) +{ + Quaternion result = { 0 }; + + float cos2Theta = (from.x*to.x + from.y*to.y + from.z*to.z); // Vector3DotProduct(from, to) + RLVector3 cross = { from.y*to.z - from.z*to.y, from.z*to.x - from.x*to.z, from.x*to.y - from.y*to.x }; // Vector3CrossProduct(from, to) + + result.x = cross.x; + result.y = cross.y; + result.z = cross.z; + result.w = 1.0f + cos2Theta; + + // QuaternionNormalize(q); + // NOTE: Normalize to essentially nlerp the original and identity to 0.5 + Quaternion q = result; + float length = sqrtf(q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w); + if (length == 0.0f) length = 1.0f; + float ilength = 1.0f/length; + + result.x = q.x*ilength; + result.y = q.y*ilength; + result.z = q.z*ilength; + result.w = q.w*ilength; + + return result; +} + +// Get a quaternion for a given rotation matrix +RMAPI Quaternion QuaternionFromMatrix(Matrix mat) +{ + Quaternion result = { 0 }; + + float fourWSquaredMinus1 = mat.m0 + mat.m5 + mat.m10; + float fourXSquaredMinus1 = mat.m0 - mat.m5 - mat.m10; + float fourYSquaredMinus1 = mat.m5 - mat.m0 - mat.m10; + float fourZSquaredMinus1 = mat.m10 - mat.m0 - mat.m5; + + int biggestIndex = 0; + float fourBiggestSquaredMinus1 = fourWSquaredMinus1; + if (fourXSquaredMinus1 > fourBiggestSquaredMinus1) + { + fourBiggestSquaredMinus1 = fourXSquaredMinus1; + biggestIndex = 1; + } + + if (fourYSquaredMinus1 > fourBiggestSquaredMinus1) + { + fourBiggestSquaredMinus1 = fourYSquaredMinus1; + biggestIndex = 2; + } + + if (fourZSquaredMinus1 > fourBiggestSquaredMinus1) + { + fourBiggestSquaredMinus1 = fourZSquaredMinus1; + biggestIndex = 3; + } + + float biggestVal = sqrtf(fourBiggestSquaredMinus1 + 1.0f)*0.5f; + float mult = 0.25f/biggestVal; + + switch (biggestIndex) + { + case 0: + result.w = biggestVal; + result.x = (mat.m6 - mat.m9)*mult; + result.y = (mat.m8 - mat.m2)*mult; + result.z = (mat.m1 - mat.m4)*mult; + break; + case 1: + result.x = biggestVal; + result.w = (mat.m6 - mat.m9)*mult; + result.y = (mat.m1 + mat.m4)*mult; + result.z = (mat.m8 + mat.m2)*mult; + break; + case 2: + result.y = biggestVal; + result.w = (mat.m8 - mat.m2)*mult; + result.x = (mat.m1 + mat.m4)*mult; + result.z = (mat.m6 + mat.m9)*mult; + break; + case 3: + result.z = biggestVal; + result.w = (mat.m1 - mat.m4)*mult; + result.x = (mat.m8 + mat.m2)*mult; + result.y = (mat.m6 + mat.m9)*mult; + break; + } + + return result; +} + +// Get a matrix for a given quaternion +RMAPI Matrix QuaternionToMatrix(Quaternion q) +{ + Matrix result = { 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f }; // MatrixIdentity() + + float a2 = q.x*q.x; + float b2 = q.y*q.y; + float c2 = q.z*q.z; + float ac = q.x*q.z; + float ab = q.x*q.y; + float bc = q.y*q.z; + float ad = q.w*q.x; + float bd = q.w*q.y; + float cd = q.w*q.z; + + result.m0 = 1 - 2*(b2 + c2); + result.m1 = 2*(ab + cd); + result.m2 = 2*(ac - bd); + + result.m4 = 2*(ab - cd); + result.m5 = 1 - 2*(a2 + c2); + result.m6 = 2*(bc + ad); + + result.m8 = 2*(ac + bd); + result.m9 = 2*(bc - ad); + result.m10 = 1 - 2*(a2 + b2); + + return result; +} + +// Get rotation quaternion for an angle and axis +// NOTE: Angle must be provided in radians +RMAPI Quaternion QuaternionFromAxisAngle(RLVector3 axis, float angle) +{ + Quaternion result = { 0.0f, 0.0f, 0.0f, 1.0f }; + + float axisLength = sqrtf(axis.x*axis.x + axis.y*axis.y + axis.z*axis.z); + + if (axisLength != 0.0f) + { + angle *= 0.5f; + + float length = 0.0f; + float ilength = 0.0f; + + // Vector3Normalize(axis) + length = axisLength; + if (length == 0.0f) length = 1.0f; + ilength = 1.0f/length; + axis.x *= ilength; + axis.y *= ilength; + axis.z *= ilength; + + float sinres = sinf(angle); + float cosres = cosf(angle); + + result.x = axis.x*sinres; + result.y = axis.y*sinres; + result.z = axis.z*sinres; + result.w = cosres; + + // QuaternionNormalize(q); + Quaternion q = result; + length = sqrtf(q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w); + if (length == 0.0f) length = 1.0f; + ilength = 1.0f/length; + result.x = q.x*ilength; + result.y = q.y*ilength; + result.z = q.z*ilength; + result.w = q.w*ilength; + } + + return result; +} + +// Get the rotation angle and axis for a given quaternion +RMAPI void QuaternionToAxisAngle(Quaternion q, RLVector3 *outAxis, float *outAngle) +{ + if (fabsf(q.w) > 1.0f) + { + // QuaternionNormalize(q); + float length = sqrtf(q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w); + if (length == 0.0f) length = 1.0f; + float ilength = 1.0f/length; + + q.x = q.x*ilength; + q.y = q.y*ilength; + q.z = q.z*ilength; + q.w = q.w*ilength; + } + + RLVector3 resAxis = { 0.0f, 0.0f, 0.0f }; + float resAngle = 2.0f*acosf(q.w); + float den = sqrtf(1.0f - q.w*q.w); + + if (den > EPSILON) + { + resAxis.x = q.x/den; + resAxis.y = q.y/den; + resAxis.z = q.z/den; + } + else + { + // This occurs when the angle is zero. + // Not a problem: just set an arbitrary normalized axis. + resAxis.x = 1.0f; + } + + *outAxis = resAxis; + *outAngle = resAngle; +} + +// Get the quaternion equivalent to Euler angles +// NOTE: Rotation order is ZYX +RMAPI Quaternion QuaternionFromEuler(float pitch, float yaw, float roll) +{ + Quaternion result = { 0 }; + + float x0 = cosf(pitch*0.5f); + float x1 = sinf(pitch*0.5f); + float y0 = cosf(yaw*0.5f); + float y1 = sinf(yaw*0.5f); + float z0 = cosf(roll*0.5f); + float z1 = sinf(roll*0.5f); + + result.x = x1*y0*z0 - x0*y1*z1; + result.y = x0*y1*z0 + x1*y0*z1; + result.z = x0*y0*z1 - x1*y1*z0; + result.w = x0*y0*z0 + x1*y1*z1; + + return result; +} + +// Get the Euler angles equivalent to quaternion (roll, pitch, yaw) +// NOTE: Angles are returned in a Vector3 struct in radians +RMAPI RLVector3 QuaternionToEuler(Quaternion q) +{ + RLVector3 result = { 0 }; + + // Roll (x-axis rotation) + float x0 = 2.0f*(q.w*q.x + q.y*q.z); + float x1 = 1.0f - 2.0f*(q.x*q.x + q.y*q.y); + result.x = atan2f(x0, x1); + + // Pitch (y-axis rotation) + float y0 = 2.0f*(q.w*q.y - q.z*q.x); + y0 = y0 > 1.0f ? 1.0f : y0; + y0 = y0 < -1.0f ? -1.0f : y0; + result.y = asinf(y0); + + // Yaw (z-axis rotation) + float z0 = 2.0f*(q.w*q.z + q.x*q.y); + float z1 = 1.0f - 2.0f*(q.y*q.y + q.z*q.z); + result.z = atan2f(z0, z1); + + return result; +} + +// Transform a quaternion given a transformation matrix +RMAPI Quaternion QuaternionTransform(Quaternion q, Matrix mat) +{ + Quaternion result = { 0 }; + + result.x = mat.m0*q.x + mat.m4*q.y + mat.m8*q.z + mat.m12*q.w; + result.y = mat.m1*q.x + mat.m5*q.y + mat.m9*q.z + mat.m13*q.w; + result.z = mat.m2*q.x + mat.m6*q.y + mat.m10*q.z + mat.m14*q.w; + result.w = mat.m3*q.x + mat.m7*q.y + mat.m11*q.z + mat.m15*q.w; + + return result; +} + +// Check whether two given quaternions are almost equal +RMAPI int QuaternionEquals(Quaternion p, Quaternion q) +{ +#if !defined(EPSILON) + #define EPSILON 0.000001f +#endif + + int result = (((fabsf(p.x - q.x)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.x), fabsf(q.x))))) && + ((fabsf(p.y - q.y)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.y), fabsf(q.y))))) && + ((fabsf(p.z - q.z)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.z), fabsf(q.z))))) && + ((fabsf(p.w - q.w)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.w), fabsf(q.w)))))) || + (((fabsf(p.x + q.x)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.x), fabsf(q.x))))) && + ((fabsf(p.y + q.y)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.y), fabsf(q.y))))) && + ((fabsf(p.z + q.z)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.z), fabsf(q.z))))) && + ((fabsf(p.w + q.w)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.w), fabsf(q.w)))))); + + return result; +} + +// Compose a transformation matrix from rotational, translational and scaling components +// TODO: This function is not following raymath conventions defined in header: NOT self-contained +RMAPI Matrix MatrixCompose(RLVector3 translation, Quaternion rotation, RLVector3 scale) +{ + // Initialize vectors + RLVector3 right = { 1.0f, 0.0f, 0.0f }; + RLVector3 up = { 0.0f, 1.0f, 0.0f }; + RLVector3 forward = { 0.0f, 0.0f, 1.0f }; + + // Scale vectors + right = Vector3Scale(right, scale.x); + up = Vector3Scale(up, scale.y); + forward = Vector3Scale(forward , scale.z); + + // Rotate vectors + right = Vector3RotateByQuaternion(right, rotation); + up = Vector3RotateByQuaternion(up, rotation); + forward = Vector3RotateByQuaternion(forward, rotation); + + // Set result matrix output + Matrix result = { + right.x, up.x, forward.x, translation.x, + right.y, up.y, forward.y, translation.y, + right.z, up.z, forward.z, translation.z, + 0.0f, 0.0f, 0.0f, 1.0f + }; + + return result; +} + +// Decompose a transformation matrix into its rotational, translational and scaling components and remove shear +// TODO: This function is not following raymath conventions defined in header: NOT self-contained +RMAPI void MatrixDecompose(Matrix mat, RLVector3 *translation, Quaternion *rotation, RLVector3 *scale) +{ + float eps = (float)1e-9; + + // Extract Translation + translation->x = mat.m12; + translation->y = mat.m13; + translation->z = mat.m14; + + // Matrix Columns - Rotation will be extracted into here. + RLVector3 matColumns[3] = { { mat.m0, mat.m4, mat.m8 }, + { mat.m1, mat.m5, mat.m9 }, + { mat.m2, mat.m6, mat.m10 } }; + + // Shear Parameters XY, XZ, and YZ (extract and ignored) + float shear[3] = { 0 }; + + // Normalized Scale Parameters + RLVector3 scl = { 0 }; + + // Max-Normalizing helps numerical stability + float stabilizer = eps; + for (int i = 0; i < 3; i++) + { + stabilizer = fmaxf(stabilizer, fabsf(matColumns[i].x)); + stabilizer = fmaxf(stabilizer, fabsf(matColumns[i].y)); + stabilizer = fmaxf(stabilizer, fabsf(matColumns[i].z)); + }; + matColumns[0] = Vector3Scale(matColumns[0], 1.0f / stabilizer); + matColumns[1] = Vector3Scale(matColumns[1], 1.0f / stabilizer); + matColumns[2] = Vector3Scale(matColumns[2], 1.0f / stabilizer); + + // X Scale + scl.x = Vector3Length(matColumns[0]); + if (scl.x > eps) matColumns[0] = Vector3Scale(matColumns[0], 1.0f / scl.x); + + // Compute XY shear and make col2 orthogonal + shear[0] = Vector3DotProduct(matColumns[0], matColumns[1]); + matColumns[1] = Vector3Subtract(matColumns[1], Vector3Scale(matColumns[0], shear[0])); + + // Y Scale + scl.y = Vector3Length(matColumns[1]); + if (scl.y > eps) + { + matColumns[1] = Vector3Scale(matColumns[1], 1.0f / scl.y); + shear[0] /= scl.y; // Correct XY shear + } + + // Compute XZ and YZ shears and make col3 orthogonal + shear[1] = Vector3DotProduct(matColumns[0], matColumns[2]); + matColumns[2] = Vector3Subtract(matColumns[2], Vector3Scale(matColumns[0], shear[1])); + shear[2] = Vector3DotProduct(matColumns[1], matColumns[2]); + matColumns[2] = Vector3Subtract(matColumns[2], Vector3Scale(matColumns[1], shear[2])); + + // Z Scale + scl.z = Vector3Length(matColumns[2]); + if (scl.z > eps) + { + matColumns[2] = Vector3Scale(matColumns[2], 1.0f / scl.z); + shear[1] /= scl.z; // Correct XZ shear + shear[2] /= scl.z; // Correct YZ shear + } + + // matColumns are now orthonormal in O(3). Now ensure its in SO(3) by enforcing det = 1. + if (Vector3DotProduct(matColumns[0], Vector3CrossProduct(matColumns[1], matColumns[2])) < 0) + { + scl = Vector3Negate(scl); + matColumns[0] = Vector3Negate(matColumns[0]); + matColumns[1] = Vector3Negate(matColumns[1]); + matColumns[2] = Vector3Negate(matColumns[2]); + } + + // Set Scale + *scale = Vector3Scale(scl, stabilizer); + + // Extract Rotation + Matrix rotationMatrix = { matColumns[0].x, matColumns[0].y, matColumns[0].z, 0, + matColumns[1].x, matColumns[1].y, matColumns[1].z, 0, + matColumns[2].x, matColumns[2].y, matColumns[2].z, 0, + 0, 0, 0, 1 }; + *rotation = QuaternionFromMatrix(rotationMatrix); +} + +#endif // RAYMATH_H diff --git a/src/main.cpp b/src/main.cpp index cb28ae5..2aa0f3a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,81 +1,71 @@ -// stdlib -// TODO(djledda): get rid of this -#include - -// Graphics bindings and libs -#include "lib/glad/glad.c" -#include -#define GLM_ENABLE_EXPERIMENTAL -#include -#include -#include -#include -#include - // Project -#include "SomaSolve.cpp" // errors from iostream also defining the keyword `global` +#include "SomaSolve.cpp" #include "gfx/gfx.cpp" #include "world/world.cpp" #include "VoxelSpace.cpp" #include "./tests.cpp" #include "lib/djstdlib/core.cpp" +// Graphics bindings and libs +#include "lib/glad/glad.c" +#include + // Library initialisation #define STB_IMAGE_IMPLEMENTATION #include "lib/loaders/stb_image.h" #define TINYOBJ_LOADER_C_IMPLEMENTATION #include "lib/loaders/tinyobj.h" +#define RAYMATH_IMPLEMENTATION +#include "lib/raymath.h" -#define PI (real32)3.14159265358979323846264338327950288 - -void print(glm::vec3* vector) { - glm::vec3 vec = *vector; +void print(RLVector3* vector) { + RLVector3 vec = *vector; print( "┌ ┐\n" "│%7.2f%, %7.2f, %7.2f │\n" - "└ ┘\n", - vec[0], vec[1], vec[2]); + "└ ┘\n", + vec.x, vec.y, vec.z); } -void print(glm::mat4* matrix) { - glm::mat4 mat = *matrix; +void print(Matrix* matrix) { + Matrix mat = *matrix; print( "┌ ┐\n" "│%7.2f%, %7.2f, %7.2f, %7.2f │\n" "│%7.2f%, %7.2f, %7.2f, %7.2f │\n" "│%7.2f%, %7.2f, %7.2f, %7.2f │\n" "│%7.2f%, %7.2f, %7.2f, %7.2f │\n" - "└ ┘\n", - mat[0][0], mat[0][1], mat[0][2], mat[0][3], - mat[1][0], mat[1][1], mat[1][2], mat[1][3], - mat[2][0], mat[2][1], mat[2][2], mat[2][3], - mat[3][0], mat[3][1], mat[3][2], mat[3][3]); + "└ ┘\n", + mat.m0, mat.m1, mat.m2, mat.m3, + mat.m4, mat.m5, mat.m6, mat.m7, + mat.m8, mat.m9, mat.m10, mat.m11, + mat.m12, mat.m13, mat.m14, mat.m15); } struct Camera { - glm::mat4 view; - glm::mat4 proj; - glm::vec3 pos; - glm::vec3 up; - glm::vec3 target; + Matrix view; + Matrix proj; + RLVector3 pos; + RLVector3 up; + RLVector3 target; }; Camera *createCamera(Arena *arena, real32 aspect_ratio = 800.0f / 600.0f) { Camera *result = PushStruct(arena, Camera); - result->view = glm::mat4(); - result->proj = glm::perspective(glm::radians(45.0f), aspect_ratio, 0.1f, 100.0f); - result->pos = glm::vec3(0.0f); - result->up = glm::vec3(0.0f, 1.0f, 0.0f); + result->view = (Matrix){0}; + result->proj = MatrixPerspective(DEG2RAD * 45.0f, aspect_ratio, 0.1f, 100.0f); + result->pos = (RLVector3){0}; + result->up = (RLVector3){0,1,0}; return result; } void cameraLookAt(Camera *c, float x, float y, float z) { - c->target = glm::vec3(x, y, z); - c->view = glm::lookAt(c->pos, c->target, c->up); + c->target = (RLVector3){x, y, z}; + c->view = MatrixLookAt(c->pos, c->target, c->up); } -void camera_set_up(Camera *c, real32 up_x, real32 up_y, real32 up_z) { - c->up = glm::vec3(up_x, up_y, up_z); +void cameraSetUp(Camera *c, real32 up_x, real32 up_y, real32 up_z) { + c->up = (RLVector3){up_x, up_y, up_z}; } struct Frame { @@ -146,7 +136,7 @@ struct Renderer { struct PolycubeInput { Space repr; - Vector4 color; + Vector4 color; }; struct SomaState { @@ -157,8 +147,8 @@ struct SomaState { uint32 light; list polycubes; Camera* camera; - glm::vec3 rotAxisX; - glm::vec3 rotAxisY; + RLVector3 rotAxisX; + RLVector3 rotAxisY; list polycubeInput; }; @@ -177,7 +167,8 @@ struct Soma { void showEntity(Scene *scene, uint32 entityHandle) { SceneGraphNode *node = getSceneGraphNodeForEntity(scene, entityHandle); - for (uint32 &child : node->children) { + for (EachIn(node->children, i)) { + uint32 child = node->children.data[i]; SceneGraphNode *subNode = getSceneGraphNode(scene, child); if (subNode->entityHandle) { getEntity(scene, subNode->entityHandle)->flags |= EntityFlags_Visible; @@ -187,7 +178,8 @@ void showEntity(Scene *scene, uint32 entityHandle) { void hideEntity(Scene *scene, uint32 entityHandle) { SceneGraphNode *node = getSceneGraphNodeForEntity(scene, entityHandle); - for (uint32 &child : node->children) { + for (EachIn(node->children, i)) { + uint32 child = node->children.data[i]; SceneGraphNode *subNode = getSceneGraphNode(scene, child); if (subNode->entityHandle) { getEntity(scene, subNode->entityHandle)->flags &= ~EntityFlags_Visible; @@ -195,12 +187,14 @@ void hideEntity(Scene *scene, uint32 entityHandle) { } } -glm::vec3 centreFromPolycube(Scene *scene, Polycube *p) { - glm::vec3 centre = glm::vec3(0.0f); - for (uint32 &child : getSceneGraphNode(scene, p->entityHandle)->children) { - centre += getSceneGraphNode(scene, child)->translation; +RLVector3 centreFromPolycube(Scene *scene, Polycube *p) { + RLVector3 centre = (RLVector3){0,0,0}; + list *children = &getSceneGraphNode(scene, p->entityHandle)->children; + for (EachIn(*children, i)) { + uint32 child = children->data[i]; + centre = Vector3Add(centre, getSceneGraphNode(scene, child)->translation); } - centre /= getSceneGraphNodeForEntity(scene, p->entityHandle)->children.size(); + centre = Vector3Scale(centre, 1.0f/(getSceneGraphNodeForEntity(scene, p->entityHandle)->children.head)); return centre; } @@ -315,7 +309,7 @@ void processInput(Soma *soma) { if (input->keyboard.escape) { glfwSetWindowShouldClose(soma->window.handle, true); - } + } if (input->keyboard.space && !prevInput->keyboard.space) { glPolygonMode(GL_FRONT_AND_BACK, !soma->state.wireframe ? GL_LINE : GL_FILL); @@ -343,33 +337,33 @@ void processInput(Soma *soma) { real64 deltaX = (input->mouse.x - prevInput->mouse.x) * 0.005; if (deltaX > 0.00000001 || deltaX < -0.00000001) { - polycubeGraphNode->rotation *= glm::angleAxis((real32)deltaX, soma->state.rotAxisY); + polycubeGraphNode->rotation = QuaternionMultiply(polycubeGraphNode->rotation, QuaternionFromAxisAngle(soma->state.rotAxisY, -(real32)deltaX)); } real64 deltaY = (input->mouse.y - prevInput->mouse.y) * 0.005; if (deltaY > 0.00000001 || deltaY < -0.00000001) { - polycubeGraphNode->rotation = glm::angleAxis(-(real32)deltaY, soma->state.rotAxisX) * polycubeGraphNode->rotation; + polycubeGraphNode->rotation = QuaternionMultiply(QuaternionFromAxisAngle(soma->state.rotAxisX, -(real32)deltaY), polycubeGraphNode->rotation); } } } -Polycube createPolycubeFromRepr(Scene *s, Space *repr, Vector4 color) { - uint32 polycubeMainEntityHandle = createEntity(s); +Polycube createPolycubeFromRepr(Arena *arena, Scene *s, Space *repr, Vector4 color) { + uint32 polycubeMainEntityHandle = createEntity(arena, s); Entity *polycubeMainEntity = getEntity(s, polycubeMainEntityHandle); for (int x = 0; x < repr->dim_x; x++) { for (int y = 0; y < repr->dim_y; y++) { for (int z = 0; z < repr->dim_z; z++) { if (filledAt(repr, x, y, z)) { - uint32 segmentEntityHandle = createEntity(s); + uint32 segmentEntityHandle = createEntity(arena, s); Entity *polycubeSegment = getEntity(s, segmentEntityHandle); polycubeSegment->mesh = &cubeMesh; polycubeSegment->tex = &wallTex; SceneGraphNode *graphNode = getSceneGraphNode(s, polycubeSegment->graphNodeHandle); - graphNode->translation = glm::vec3( + graphNode->translation = RLVector3{ -((repr->dim_z - 1)/2.0f) + z, - ((repr->dim_x - 1)/2.0f) - x, - -((repr->dim_y - 1)/2.0f) + y - ); + -((repr->dim_x - 1)/2.0f) + x, + ((repr->dim_y - 1)/2.0f) - y + }; sceneNodeAddEntity(s, polycubeMainEntity->graphNodeHandle, segmentEntityHandle); } } @@ -380,8 +374,8 @@ Polycube createPolycubeFromRepr(Scene *s, Space *repr, Vector4 color) { result.color = color; result.repr = *repr; SceneGraphNode *graphNode = getSceneGraphNodeForEntity(s, polycubeMainEntityHandle); - graphNode->rotation *= glm::angleAxis(PI / 4, glm::vec3(1, 0, 0)); - graphNode->rotation *= glm::angleAxis(PI / 4, glm::vec3(0, 1, 0)); + graphNode->rotation = QuaternionMultiply(graphNode->rotation, QuaternionFromAxisAngle((RLVector3){1, 0, 0}, PI / 4)); + graphNode->rotation = QuaternionMultiply(graphNode->rotation, QuaternionFromAxisAngle((RLVector3){0, 1, 0}, -PI / 4)); return result; } @@ -471,7 +465,7 @@ void reinitRectangleObjectBuffers(Renderer *r) { Renderer createRenderer(Arena *arena, Scene *scene) { Renderer result = {0}; result.scene = scene; - result.scene->sceneRoot = createSceneGraphNode(scene); + result.scene->sceneRoot = createSceneGraphNode(arena, scene); result.rects = createRectangleObjects(arena, 100); initGraphNode(getSceneGraphNode(scene, scene->sceneRoot)); return result; @@ -505,12 +499,13 @@ void renderEnd(Soma *soma, Renderer *renderer) { setUniform4fv(&phongShader, "solid_color", ¤tPolycube->color); int model_uniform = getUniformLocation(&phongShader, "model"); - for (Entity &entity : renderer->scene->entities) { - if (entity.flags & EntityFlags_Render && entity.flags & EntityFlags_Visible) { - setUniformMat4fv(model_uniform, &getSceneGraphNode(renderer->scene, entity.graphNodeHandle)->world); - glBindTexture(GL_TEXTURE_2D, entity.tex->tex_id); - glDrawArrays(GL_TRIANGLES, 0, (GLsizei)entity.mesh->num_indices); - entity.flags &= ~EntityFlags_Render; + for (EachIn(renderer->scene->entities, i)) { + Entity *entity = &renderer->scene->entities.data[i]; + if (entity->flags & EntityFlags_Render && entity->flags & EntityFlags_Visible) { + setUniformMat4fv(model_uniform, &getSceneGraphNode(renderer->scene, entity->graphNodeHandle)->world); + glBindTexture(GL_TEXTURE_2D, entity->tex->tex_id); + glDrawArrays(GL_TRIANGLES, 0, (GLsizei)entity->mesh->num_indices); + entity->flags &= ~EntityFlags_Render; } } @@ -519,7 +514,7 @@ void renderEnd(Soma *soma, Renderer *renderer) { reinitRectangleObjectBuffers(renderer); - glm::mat4 ortho = glm::ortho(0.0, 800.0, 600.0, 0.0, -1.0, 1.0); + Matrix ortho = MatrixOrtho(0.0, 800.0, 600.0, 0.0, -1.0, 1.0); setUniformMat4fv(&solidColorShader, "projection", &ortho); glBindVertexArray(renderer->rects.vao); @@ -549,22 +544,17 @@ bool ui_checkboxRect(UI_Context *ui, bool *value, UI_Rect rect) { } } if (*value) { - rendererPlaceRectangle(ui->renderer, - rect.x, rect.y, - rect.width, rect.height, - rect.color, - 5, 0); - } else { - rendererPlaceRectangle(ui->renderer, - rect.x, rect.y, - rect.width, rect.height, + rendererPlaceRectangle(ui->renderer, + rect.x, rect.y, + rect.width, rect.height, rect.color, 5, 2); - rendererPlaceRectangle(ui->renderer, - rect.x, rect.y, - rect.width, rect.height, + } else { + rendererPlaceRectangle(ui->renderer, + rect.x, rect.y, + rect.width, rect.height, COLOR_WHITE, - 5, 0); + 5, 2); } return clicked; @@ -583,10 +573,10 @@ void uiPass(Soma *soma, UI_Context *ui, Renderer *renderer) { for (int z = 0; z < currentPolycube->repr.dim_z; z++) { bool cellActive = filledAt(¤tPolycube->repr, x, y, z); UI_Rect rect = { - currX, - currY, - boxSize, - boxSize, + currX, + currY, + boxSize, + boxSize, 0, currentPolycube->color, }; @@ -602,7 +592,7 @@ void uiPass(Soma *soma, UI_Context *ui, Renderer *renderer) { } } -int main_cmd() { +int mainCmd() { interactive_cmd_line_solve_soma(); return 0; } @@ -610,7 +600,7 @@ int main_cmd() { int mainGfx() { Arena *arena = arenaAlloc(Megabytes(128)); - Scene mainScene = createScene(); + Scene mainScene = createScene(arena); Soma soma = {}; soma.window.width = 800; @@ -634,47 +624,48 @@ int mainGfx() { soma.state.lastPolycubeVisible = 6; soma.state.polycubeInput = PushListZero(arena, PolycubeInput, 64); soma.state.polycubes = PushListZero(arena, Polycube, 64); - soma.state.light = createEntity(&mainScene); + soma.state.light = createEntity(arena, &mainScene); soma.state.camera = mainFrame.cam; SceneGraphNode *light = getSceneGraphNode(&mainScene, getEntity(&mainScene, soma.state.light)->graphNodeHandle); - light->translation = glm::vec3(4.0f, 6.0f, 24.0f); + light->translation = RLVector3{4.0f, 6.0f, 24.0f}; /* Shader solid_texture_shader = createShader( - "./assets/shaders/2d.vertex.glsl"_s, + "./assets/shaders/2d.vertex.glsl"_s, "./assets/shaders/2d-tex.fragment.glsl"_s); */ solidColorShader = createShader( - "./assets/shaders/2d-solid.vertex.glsl"_s, + "./assets/shaders/2d-solid.vertex.glsl"_s, "./assets/shaders/2d-solid.fragment.glsl"_s); phongShader = createShader( - "./assets/shaders/phong-solid.vertex.glsl"_s, + "./assets/shaders/phong-solid.vertex.glsl"_s, "./assets/shaders/phong-solid.fragment.glsl"_s); cubeMesh = createMesh("./assets/models/cube.obj"); wallTex = createTexture("./assets/textures/brick-wall.jpg"); - for (int i = 0; i < ArrayCount(STD_SOMA); i++) { + for (EachInArray(STD_SOMA, i)) { Space voxelSpace = { STD_SOMA[i], 3, 3, 3 }; Vector4 color = colorFromIndex(i); appendList(&soma.state.polycubeInput, PolycubeInput{ voxelSpace, color }); cullEmptySpace(&voxelSpace); - Polycube polycube = createPolycubeFromRepr(soma.scene, &voxelSpace, color); + Polycube polycube = createPolycubeFromRepr(arena, soma.scene, &voxelSpace, color); polycube.color = color; appendList(&soma.state.polycubes, polycube); sceneNodeAddEntity(renderer.scene, renderer.scene->sceneRoot, polycube.entityHandle); } - soma.state.camera->pos = glm::vec3(0.0f, 0.0f, 8.0f); + soma.state.camera->pos = (RLVector3){0.0f, 0.0f, 8.0f}; cameraLookAt(soma.state.camera, 0.0f, 0.0f, 0.0f); SceneGraphNode *reference_polycube_gn = getSceneGraphNodeForEntity(soma.scene, soma.state.polycubes.data[0].entityHandle); - soma.state.rotAxisY = glm::normalize(glm::vec4(0, 1, 0, 0) * glm::inverse(reference_polycube_gn->world)); - glm::vec3 eyes = glm::normalize(soma.state.camera->pos - reference_polycube_gn->translation); - soma.state.rotAxisX = glm::normalize(glm::cross(eyes, soma.state.rotAxisY)); + Matrix worldInverse = MatrixInvert(reference_polycube_gn->world); + soma.state.rotAxisY = Vector3Normalize(RLVector3{worldInverse.m4, worldInverse.m5, worldInverse.m6}); + RLVector3 eyes = Vector3Normalize(Vector3Subtract(soma.state.camera->pos, reference_polycube_gn->translation)); + soma.state.rotAxisX = Vector3Normalize(Vector3CrossProduct(eyes, soma.state.rotAxisY)); real64 lastFrame = glfwGetTime(); real64 timeDelta = 1.0f/60.0f; @@ -695,9 +686,9 @@ int mainGfx() { Polycube *currentPolycube = &soma.state.polycubes.data[soma.state.currentPolycube]; PolycubeInput *pinput = &soma.state.polycubeInput.data[soma.state.currentPolycube]; removeEntity(soma.scene, currentPolycube->entityHandle); - Space culledRepr = pinput->repr; + Space culledRepr = pinput->repr; cullEmptySpace(&culledRepr); - Polycube newPolycube = createPolycubeFromRepr(soma.scene, &culledRepr, pinput->color); + Polycube newPolycube = createPolycubeFromRepr(arena, soma.scene, &culledRepr, pinput->color); SceneGraphNode *graphNode = getSceneGraphNodeForEntity(soma.scene, newPolycube.entityHandle); graphNode->rotation = getSceneGraphNodeForEntity(soma.scene, currentPolycube->entityHandle)->rotation; soma.state.polycubes.data[soma.state.currentPolycube] = newPolycube; diff --git a/src/tests.cpp b/src/tests.cpp index c3a54a7..9c7b12d 100644 --- a/src/tests.cpp +++ b/src/tests.cpp @@ -1,13 +1,16 @@ -#include -#include -#include #include "VoxelSpace.h" #include "lib/djstdlib/core.h" +typedef struct MismatchData { + int i; + uint64 actual; + uint64 expected; +} MismatchData; + void test() { { Space space = {}; - space.space=23ull; + space.space=23ull; space.dim_x=3; space.dim_y=3; space.dim_z=3; @@ -107,86 +110,86 @@ void test() { int prism_dims[] = { 3, 3, 3 }; list perms = getAllPermutationsInPrism(arena, &space1, prism_dims); - auto expected_perms = std::vector{ - 30ull, - 240ull, - 15360ull, - 122880ull, - 7864320ull, - 62914560ull, - 153ull, - 306ull, - 78336ull, - 156672ull, - 40108032ull, - 80216064ull, - 266760ull, - 533520ull, - 1067040ull, - 2134080ull, - 4268160ull, - 8536320ull, - 263682ull, - 527364ull, - 2109456ull, - 4218912ull, - 16875648ull, - 33751296ull, - 2101761ull, - 4203522ull, - 8407044ull, - 16814088ull, - 33628176ull, - 67256352ull, - 525825ull, - 1051650ull, - 4206600ull, - 8413200ull, - 33652800ull, - 67305600ull, - 51ull, - 408ull, - 26112ull, - 208896ull, - 13369344ull, - 106954752ull, - 90ull, - 180ull, - 46080ull, - 92160ull, - 23592960ull, - 47185920ull, - 4680ull, - 9360ull, - 18720ull, - 2396160ull, - 4792320ull, - 9584640ull, - 1542ull, - 12336ull, - 98688ull, - 789504ull, - 6316032ull, - 50528256ull, - 36873ull, - 73746ull, - 147492ull, - 18878976ull, - 37757952ull, - 75515904ull, - 3075ull, - 24600ull, - 196800ull, - 1574400ull, - 12595200ull, - 100761600ull, + uint64 expected_perms[] = { + 30ull, + 240ull, + 15360ull, + 122880ull, + 7864320ull, + 62914560ull, + 153ull, + 306ull, + 78336ull, + 156672ull, + 40108032ull, + 80216064ull, + 266760ull, + 533520ull, + 1067040ull, + 2134080ull, + 4268160ull, + 8536320ull, + 263682ull, + 527364ull, + 2109456ull, + 4218912ull, + 16875648ull, + 33751296ull, + 2101761ull, + 4203522ull, + 8407044ull, + 16814088ull, + 33628176ull, + 67256352ull, + 525825ull, + 1051650ull, + 4206600ull, + 8413200ull, + 33652800ull, + 67305600ull, + 51ull, + 408ull, + 26112ull, + 208896ull, + 13369344ull, + 106954752ull, + 90ull, + 180ull, + 46080ull, + 92160ull, + 23592960ull, + 47185920ull, + 4680ull, + 9360ull, + 18720ull, + 2396160ull, + 4792320ull, + 9584640ull, + 1542ull, + 12336ull, + 98688ull, + 789504ull, + 6316032ull, + 50528256ull, + 36873ull, + 73746ull, + 147492ull, + 18878976ull, + 37757952ull, + 75515904ull, + 3075ull, + 24600ull, + 196800ull, + 1574400ull, + 12595200ull, + 100761600ull, }; - Assert(expected_perms.size() == perms.length); + Assert(ArrayCount(expected_perms) == perms.length); for (int i = 0; i < perms.length; i++) { - if (i <= expected_perms.size()) { - Assert(expected_perms.at(i) == perms.data[i]); + if (i <= ArrayCount(expected_perms)) { + Assert(expected_perms[i] == perms.data[i]); } } @@ -194,20 +197,20 @@ void test() { } { - Space space1 = {}; - space1.space=30ull; + Space space1 = {}; + space1.space=30ull; space1.dim_x=3; space1.dim_y=3; space1.dim_z=3; - Space space2 = {}; - space2.space=30ull; + Space space2 = {}; + space2.space=30ull; space2.dim_x=3; space2.dim_y=3; space2.dim_z=3; - Space space3 = {}; - space3.space=30ull; + Space space3 = {}; + space3.space=30ull; space3.dim_x=3; space3.dim_y=3; space3.dim_z=3; @@ -258,7 +261,7 @@ void test() { }; Space space3 = {}; - space3.space=15ull; + space3.space=15ull; space3.dim_x=3; space3.dim_y=3; space3.dim_z=3; @@ -276,40 +279,41 @@ void test() { list positions1 = getAllPositionsInPrism(arena, &space1, dims); list positions2 = getAllPositionsInPrism(arena, &space2, dims); list positions3 = getAllPositionsInPrism(arena, &space3, dims); - auto mismatches1 = std::vector>(); - auto mismatches2 = std::vector>(); - auto mismatches3 = std::vector>(); + + list mismatches1 = PushList(arena, MismatchData, 6); + list mismatches2 = PushList(arena, MismatchData, 6); + list mismatches3 = PushList(arena, MismatchData, 6); for (int i = 0; i < 6; i++) { if (positions1.data[i] != expected_results1[i]) { - mismatches1.push_back({ i, positions1.data[i], expected_results1[i] }); + appendList(&mismatches1, { i, positions1.data[i], expected_results1[i] }); } if (positions2.data[i] != expected_results2[i]) { - mismatches2.push_back({ i, positions2.data[i], expected_results2[i] }); + appendList(&mismatches2, { i, positions2.data[i], expected_results2[i] }); } if (positions3.data[i] != expected_results3[i]) { - mismatches3.push_back({ i, positions3.data[i], expected_results3[i] }); + appendList(&mismatches3, { i, positions3.data[i], expected_results3[i] }); } } - Assert(mismatches1.size() == 0); - if (mismatches1.size() > 0) { - std::cout << "Index - Actual - Expected" << std::endl; - for (auto &mismatch : mismatches1) { - std::cout << std::get<0>(mismatch) << " - " << std::get<1>(mismatch) << " - " << std::get<2>(mismatch) << std::endl; + Assert(mismatches1.head == 0); + if (mismatches1.head > 0) { + print("Index - Actual - Expected\n"); + for (EachIn(mismatches1, i)) { + print("%zu - %zu - %zu\n", mismatches1.data[i].i, mismatches1.data[i].actual, mismatches1.data[i].expected); } } - Assert(mismatches2.size() == 0); - if (mismatches2.size() > 0) { - std::cout << "Index - Actual - Expected" << std::endl; - for (auto &mismatch : mismatches2) { - std::cout << std::get<0>(mismatch) << " - " << std::get<1>(mismatch) << " - " << std::get<2>(mismatch) << std::endl; + Assert(mismatches2.head == 0); + if (mismatches2.head > 0) { + print("Index - Actual - Expected\n"); + for (EachIn(mismatches2, i)) { + print("%zu - %zu - %zu\n", mismatches2.data[i].i, mismatches2.data[i].actual, mismatches2.data[i].expected); } } - Assert(mismatches3.size() == 0); - if (mismatches3.size() > 0) { - std::cout << "Index - Actual - Expected" << std::endl; - for (auto &mismatch : mismatches3) { - std::cout << "At " << std::get<0>(mismatch) << ": " << std::get<1>(mismatch) << " != " << std::get<2>(mismatch) << std::endl; + Assert(mismatches3.head == 0); + if (mismatches3.head > 0) { + print("Index - Actual - Expected\n"); + for (EachIn(mismatches3, i)) { + print("%zu - %zu - %zu\n", mismatches3.data[i].i, mismatches3.data[i].actual, mismatches3.data[i].expected); } } diff --git a/src/world/scene.cpp b/src/world/scene.cpp index e4ef401..0aeef5d 100644 --- a/src/world/scene.cpp +++ b/src/world/scene.cpp @@ -1,54 +1,52 @@ +#include #include "scene.h" Entity *getEntity(Scene *s, uint32 id) { - return &s->entities[id - 1]; + return &s->entities.data[id - 1]; } SceneGraphNode *getSceneGraphNodeForEntity(Scene *s, uint32 entityHandle) { - return &s->graphNodes[s->entities[entityHandle - 1].graphNodeHandle - 1]; + return &s->graphNodes.data[s->entities.data[entityHandle - 1].graphNodeHandle - 1]; } SceneGraphNode *getSceneGraphNode(Scene *s, uint32 sceneGraphNodeHandle) { - return &s->graphNodes[sceneGraphNodeHandle - 1]; + return &s->graphNodes.data[sceneGraphNodeHandle - 1]; } -uint32 createEntity(Scene *s) { - s->entities.emplace_back(); - s->graphNodes.emplace_back(); - s->entities.back().graphNodeHandle = (uint32)s->graphNodes.size(); - s->graphNodes.back().entityHandle = (uint32)s->entities.size(); - uint32 handle = (uint32)s->entities.size(); - uint32 graphNodeHandle = (uint32)s->graphNodes.size(); +uint32 createSceneGraphNode(Arena *arena, Scene *s) { + appendList(&s->graphNodes, (SceneGraphNode){0}); + s->graphNodes.data[s->graphNodes.head - 1].children = PushList(arena, uint32, 64); + return (uint32)s->graphNodes.head; +} + +uint32 createEntity(Arena *arena, Scene *s) { + appendList(&s->entities, (Entity){0}); + uint32 graphNodeId = createSceneGraphNode(arena, s); + s->entities.data[s->entities.head - 1].graphNodeHandle = graphNodeId; + getSceneGraphNode(s, graphNodeId)->entityHandle = (uint32)s->entities.head; + uint32 handle = (uint32)s->entities.head; + uint32 graphNodeHandle = (uint32)s->graphNodes.head; initGraphNode(getSceneGraphNode(s, graphNodeHandle)); return handle; } -uint32 createSceneGraphNode(Scene *s) { - s->graphNodes.emplace_back(); - return (uint32)s->graphNodes.size(); -} - void initGraphNode(SceneGraphNode *n) { - n->scale = glm::vec3(1.0f, 1.0f, 1.0f); - n->translation = glm::vec3(0.0f, 0.0f, 0.0f); - n->rotation = glm::quat(1.0f, 0.0f, 0.0f, 0.0f); - n->local = glm::mat4(1.0f); + n->scale = RLVector3{1.0f, 1.0f, 1.0f}; + n->translation = RLVector3{0.0f, 0.0f, 0.0f}; + n->rotation = Quaternion{1.0f, 0.0f, 0.0f, 0.0f}; + n->local = MatrixIdentity(); n->world = n->local; } void recalcGraphNode(SceneGraphNode *n) { - n->local = glm::scale( - glm::translate( - glm::mat4(1.0f), - n->translation - ) * glm::toMat4(n->rotation), - n->scale - ); + n->local = MatrixCompose(n->translation, n->rotation, n->scale); } -Scene createScene() { +Scene createScene(Arena *arena) { Scene result = {}; - result.sceneRoot = createSceneGraphNode(&result); + result.entities = PushList(arena, Entity, 1024); + result.graphNodes = PushList(arena, SceneGraphNode, 1024); + result.sceneRoot = createSceneGraphNode(arena, &result); return result; } @@ -57,11 +55,12 @@ void recalcSceneGraphNode(Scene *s, uint32 parentHandle) { if (node->entityHandle) { getEntity(s, node->entityHandle)->flags |= EntityFlags_Render; } - for (uint32 &nodeId : node->children) { + for (EachIn(node->children, i)) { + uint32 nodeId = node->children.data[i]; SceneGraphNode *graphNode = getSceneGraphNode(s, nodeId); graphNode->parentHandle = parentHandle; recalcGraphNode(graphNode); - graphNode->world = node->world * graphNode->local; + graphNode->world = MatrixMultiply(graphNode->local, node->world); recalcSceneGraphNode(s, nodeId); } } @@ -76,9 +75,10 @@ void removeEntity(Scene *s, uint32 entityHandle) { SceneGraphNode *graphNode = getSceneGraphNode(s, entity->graphNodeHandle); if (graphNode->parentHandle) { SceneGraphNode *parentNode = getSceneGraphNode(s, graphNode->parentHandle); - for (int i = 0; i < parentNode->children.size(); i++) { - if (parentNode->children.at(i) == entity->graphNodeHandle) { - parentNode->children.erase(parentNode->children.begin() + i); + for (int i = 0; i < parentNode->children.head; i++) { + if (parentNode->children.data[i] == entity->graphNodeHandle) { + memcpy(&parentNode->children.data[i], &parentNode->children.data[i + 1], parentNode->children.head - i); + parentNode->children.head -= 1; graphNode->parentHandle = 0; break; } @@ -87,5 +87,7 @@ void removeEntity(Scene *s, uint32 entityHandle) { } void sceneNodeAddEntity(Scene *s, uint32 graphNodeHandle, uint32 entityHandle) { - getSceneGraphNode(s, graphNodeHandle)->children.push_back(getEntity(s, entityHandle)->graphNodeHandle); + list *childList = &getSceneGraphNode(s, graphNodeHandle)->children; + appendList(childList, getEntity(s, entityHandle)->graphNodeHandle); + print("%zu HI\n", childList->length); } diff --git a/src/world/scene.h b/src/world/scene.h index 9785091..f5a56ca 100644 --- a/src/world/scene.h +++ b/src/world/scene.h @@ -1,7 +1,5 @@ -#include -#include "glm/glm.hpp" -#include #include "../gfx/gfx.h" +#include "../lib/raymath.h" enum EntityFlags { EntityFlags_Visible=1<<0, @@ -17,27 +15,27 @@ struct Entity { }; struct SceneGraphNode { - glm::mat4 local; - glm::mat4 world; - glm::vec3 translation; - glm::quat rotation; - glm::vec3 scale; - std::vector children; + Matrix local; + Matrix world; + RLVector3 translation; + Quaternion rotation; + RLVector3 scale; + list children; uint32 entityHandle; uint32 parentHandle; }; struct Scene { uint32 sceneRoot; - std::vector entities; - std::vector graphNodes; + list entities; + list graphNodes; }; -uint32 createEntity(Scene *s); +uint32 createEntity(Arena *arena, Scene *s); Entity *getEntity(Scene *s); SceneGraphNode *getSceneGraphNode(Scene *s, int id); -uint32 createSceneGraphNode(Scene *s); -Scene createScene(Scene *s); +uint32 createSceneGraphNode(Arena *arena, Scene *s); +Scene createScene(Arena *arena); void initGraphNode(SceneGraphNode *n); void recalcGraphNode(SceneGraphNode *n); void recalcSceneGraphNode(Scene *s, uint32 parentHandle);