├── .gitignore ├── LICENSE ├── README.md ├── assets ├── data │ └── nodes.bin └── images │ ├── Screenshot 2024-06-05 16-13-57.png │ ├── Screenshot 2024-06-05 17-28-51.png │ └── Screenshot 2024-06-05 18-17-39.png ├── nob.c ├── raylib └── raylib-5.0_linux_amd64 │ ├── CHANGELOG │ ├── LICENSE │ ├── README.md │ ├── include │ ├── raylib.h │ ├── raymath.h │ └── rlgl.h │ └── lib │ ├── libraylib.a │ ├── libraylib.so │ ├── libraylib.so.5.0.0 │ └── libraylib.so.500 ├── src ├── main.c ├── nob.h ├── plug.c └── plug.h └── thumbnail.png /.gitignore: -------------------------------------------------------------------------------- 1 | nob 2 | nob.old 3 | build/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2024 Alexey Kutepov 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Cracking Secret Message 2 | 3 | ![thumbnail](./thumbnail.png) 4 | 5 | Artifacts of the "Computer Vision" stream. Originally developed as part of the Panim Animation Engine. But since the engine was not publically available at the time the implementation was extracted as a standalone project. 6 | 7 | ## Quick Start 8 | 9 | ```console 10 | $ cc -o nob nob.c 11 | $ ./nob 12 | $ ./build/secret ./build/libplug.so 13 | ``` 14 | -------------------------------------------------------------------------------- /assets/data/nodes.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsoding/secret/fdc592ceda00a9eb724aa19d7402de6606a76747/assets/data/nodes.bin -------------------------------------------------------------------------------- /assets/images/Screenshot 2024-06-05 16-13-57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsoding/secret/fdc592ceda00a9eb724aa19d7402de6606a76747/assets/images/Screenshot 2024-06-05 16-13-57.png -------------------------------------------------------------------------------- /assets/images/Screenshot 2024-06-05 17-28-51.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsoding/secret/fdc592ceda00a9eb724aa19d7402de6606a76747/assets/images/Screenshot 2024-06-05 17-28-51.png -------------------------------------------------------------------------------- /assets/images/Screenshot 2024-06-05 18-17-39.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsoding/secret/fdc592ceda00a9eb724aa19d7402de6606a76747/assets/images/Screenshot 2024-06-05 18-17-39.png -------------------------------------------------------------------------------- /nob.c: -------------------------------------------------------------------------------- 1 | #define NOB_IMPLEMENTATION 2 | #include "./src/nob.h" 3 | 4 | #define BUILD_DIR "./build" 5 | #define SRC_DIR "./src" 6 | 7 | void cflags(Nob_Cmd *cmd) 8 | { 9 | nob_cmd_append(cmd, "-Wall", "-Wextra", "-ggdb"); 10 | nob_cmd_append(cmd, "-I./raylib/raylib-5.0_linux_amd64/include"); 11 | nob_cmd_append(cmd, "-I./src/"); 12 | } 13 | 14 | void cc(Nob_Cmd *cmd) 15 | { 16 | nob_cmd_append(cmd, "cc"); 17 | cflags(cmd); 18 | } 19 | 20 | void libs(Nob_Cmd *cmd) 21 | { 22 | nob_cmd_append(cmd, "-Wl,-rpath=./raylib/raylib-5.0_linux_amd64/lib/"); 23 | nob_cmd_append(cmd, "-Wl,-rpath="BUILD_DIR); 24 | nob_cmd_append(cmd, "-L./raylib/raylib-5.0_linux_amd64/lib"); 25 | nob_cmd_append(cmd, "-l:libraylib.so", "-lm", "-ldl", "-lpthread"); 26 | } 27 | 28 | bool build_plug_c(bool force, Nob_Cmd *cmd, const char *source_path, const char *output_path) 29 | { 30 | int rebuild_is_needed = nob_needs_rebuild1(output_path, source_path); 31 | if (rebuild_is_needed < 0) return false; 32 | 33 | if (force || rebuild_is_needed) { 34 | cmd->count = 0; 35 | cc(cmd); 36 | nob_cmd_append(cmd, "-fPIC", "-shared", "-Wl,--no-undefined"); 37 | nob_cmd_append(cmd, source_path); 38 | nob_cmd_append(cmd, "-o", output_path); 39 | libs(cmd); 40 | return nob_cmd_run_sync(*cmd); 41 | } 42 | 43 | nob_log(NOB_INFO, "%s is up-to-date", output_path); 44 | return true; 45 | } 46 | 47 | bool build_exe(bool force, Nob_Cmd *cmd, const char **input_paths, size_t input_paths_len, const char *output_path) 48 | { 49 | int rebuild_is_needed = nob_needs_rebuild(output_path, input_paths, input_paths_len); 50 | if (rebuild_is_needed < 0) return false; 51 | 52 | if (force || rebuild_is_needed) { 53 | cmd->count = 0; 54 | cc(cmd); 55 | nob_cmd_append(cmd, "-o", output_path); 56 | nob_da_append_many(cmd, input_paths, input_paths_len); 57 | libs(cmd); 58 | return nob_cmd_run_sync(*cmd); 59 | } 60 | 61 | nob_log(NOB_INFO, "%s is up-to-date", output_path); 62 | return true; 63 | } 64 | 65 | int main(int argc, char **argv) 66 | { 67 | NOB_GO_REBUILD_URSELF(argc, argv); 68 | 69 | const char *program_name = nob_shift_args(&argc, &argv); 70 | (void) program_name; 71 | 72 | bool force = false; 73 | while (argc > 0) { 74 | const char *flag = nob_shift_args(&argc, &argv); 75 | if (strcmp(flag, "-f") == 0) { 76 | force = true; 77 | } else { 78 | nob_log(NOB_ERROR, "Unknown flag %s", flag); 79 | return 1; 80 | } 81 | } 82 | 83 | if (!nob_mkdir_if_not_exists(BUILD_DIR)) return 1; 84 | 85 | Nob_Cmd cmd = {0}; 86 | if (!build_plug_c(force, &cmd, SRC_DIR"/plug.c", BUILD_DIR"/libplug.so")) return 1; 87 | 88 | { 89 | const char *output_path = BUILD_DIR"/secret"; 90 | const char *input_paths[] = { 91 | SRC_DIR"/main.c", 92 | }; 93 | size_t input_paths_len = NOB_ARRAY_LEN(input_paths); 94 | if (!build_exe(force, &cmd, input_paths, input_paths_len, output_path)) return 1; 95 | } 96 | 97 | return 0; 98 | } 99 | -------------------------------------------------------------------------------- /raylib/raylib-5.0_linux_amd64/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013-2023 Ramon Santamaria (@raysan5) 2 | 3 | This software is provided "as-is", without any express or implied warranty. In no event 4 | will the authors be held liable for any damages arising from the use of this software. 5 | 6 | Permission is granted to anyone to use this software for any purpose, including commercial 7 | applications, and to alter it and redistribute it freely, subject to the following restrictions: 8 | 9 | 1. The origin of this software must not be misrepresented; you must not claim that you 10 | wrote the original software. If you use this software in a product, an acknowledgment 11 | in the product documentation would be appreciated but is not required. 12 | 13 | 2. Altered source versions must be plainly marked as such, and must not be misrepresented 14 | as being the original software. 15 | 16 | 3. This notice may not be removed or altered from any source distribution. 17 | -------------------------------------------------------------------------------- /raylib/raylib-5.0_linux_amd64/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | **raylib is a simple and easy-to-use library to enjoy videogames programming.** 4 | 5 | raylib is highly inspired by Borland BGI graphics lib and by XNA framework and it's especially well suited for prototyping, tooling, graphical applications, embedded systems and education. 6 | 7 | *NOTE for ADVENTURERS: raylib is a programming library to enjoy videogames programming; no fancy interface, no visual helpers, no debug button... just coding in the most pure spartan-programmers way.* 8 | 9 | Ready to learn? Jump to [code examples!](https://www.raylib.com/examples.html) 10 | 11 | --- 12 | 13 |
14 | 15 | [![GitHub Releases Downloads](https://img.shields.io/github/downloads/raysan5/raylib/total)](https://github.com/raysan5/raylib/releases) 16 | [![GitHub Stars](https://img.shields.io/github/stars/raysan5/raylib?style=flat&label=stars)](https://github.com/raysan5/raylib/stargazers) 17 | [![GitHub commits since tagged version](https://img.shields.io/github/commits-since/raysan5/raylib/4.5.0)](https://github.com/raysan5/raylib/commits/master) 18 | [![GitHub Sponsors](https://img.shields.io/github/sponsors/raysan5?label=sponsors)](https://github.com/sponsors/raysan5) 19 | [![Packaging Status](https://repology.org/badge/tiny-repos/raylib.svg)](https://repology.org/project/raylib/versions) 20 | [![License](https://img.shields.io/badge/license-zlib%2Flibpng-blue.svg)](LICENSE) 21 | 22 | [![Discord Members](https://img.shields.io/discord/426912293134270465.svg?label=Discord&logo=discord)](https://discord.gg/raylib) 23 | [![Subreddit Subscribers](https://img.shields.io/reddit/subreddit-subscribers/raylib?label=reddit%20r%2Fraylib&logo=reddit)](https://www.reddit.com/r/raylib/) 24 | [![Youtube Subscribers](https://img.shields.io/youtube/channel/subscribers/UC8WIBkhYb5sBNqXO1mZ7WSQ?style=flat&label=Youtube&logo=youtube)](https://www.youtube.com/c/raylib) 25 | [![Twitch Status](https://img.shields.io/twitch/status/raysan5?style=flat&label=Twitch&logo=twitch)](https://www.twitch.tv/raysan5) 26 | 27 | [![Windows](https://github.com/raysan5/raylib/workflows/Windows/badge.svg)](https://github.com/raysan5/raylib/actions?query=workflow%3AWindows) 28 | [![Linux](https://github.com/raysan5/raylib/workflows/Linux/badge.svg)](https://github.com/raysan5/raylib/actions?query=workflow%3ALinux) 29 | [![macOS](https://github.com/raysan5/raylib/workflows/macOS/badge.svg)](https://github.com/raysan5/raylib/actions?query=workflow%3AmacOS) 30 | [![WebAssembly](https://github.com/raysan5/raylib/workflows/WebAssembly/badge.svg)](https://github.com/raysan5/raylib/actions?query=workflow%3AWebAssembly) 31 | 32 | [![CMakeBuilds](https://github.com/raysan5/raylib/workflows/CMakeBuilds/badge.svg)](https://github.com/raysan5/raylib/actions?query=workflow%3ACMakeBuilds) 33 | [![Windows Examples](https://github.com/raysan5/raylib/actions/workflows/windows_examples.yml/badge.svg)](https://github.com/raysan5/raylib/actions/workflows/windows_examples.yml) 34 | [![Linux Examples](https://github.com/raysan5/raylib/actions/workflows/linux_examples.yml/badge.svg)](https://github.com/raysan5/raylib/actions/workflows/linux_examples.yml) 35 | 36 | features 37 | -------- 38 | - **NO external dependencies**, all required libraries are [bundled into raylib](https://github.com/raysan5/raylib/tree/master/src/external) 39 | - Multiple platforms supported: **Windows, Linux, MacOS, RPI, Android, HTML5... and more!** 40 | - Written in plain C code (C99) using PascalCase/camelCase notation 41 | - Hardware accelerated with OpenGL (**1.1, 2.1, 3.3, 4.3, ES 2.0, ES 3.0**) 42 | - **Unique OpenGL abstraction layer** (usable as standalone module): [rlgl](https://github.com/raysan5/raylib/blob/master/src/rlgl.h) 43 | - Multiple **Fonts** formats supported (TTF, OTF, Image fonts, AngelCode fonts) 44 | - Multiple texture formats supported, including **compressed formats** (DXT, ETC, ASTC) 45 | - **Full 3D support**, including 3D Shapes, Models, Billboards, Heightmaps and more! 46 | - Flexible Materials system, supporting classic maps and **PBR maps** 47 | - **Animated 3D models** supported (skeletal bones animation) (IQM, M3D, glTF) 48 | - Shaders support, including model shaders and **postprocessing** shaders 49 | - **Powerful math module** for Vector, Matrix and Quaternion operations: [raymath](https://github.com/raysan5/raylib/blob/master/src/raymath.h) 50 | - Audio loading and playing with streaming support (WAV, QOA, OGG, MP3, FLAC, XM, MOD) 51 | - **VR stereo rendering** support with configurable HMD device parameters 52 | - Huge examples collection with [+140 code examples](https://github.com/raysan5/raylib/tree/master/examples)! 53 | - Bindings to [+70 programming languages](https://github.com/raysan5/raylib/blob/master/BINDINGS.md)! 54 | - **Free and open source** 55 | 56 | basic example 57 | -------------- 58 | This is a basic raylib example, it creates a window and draws the text `"Congrats! You created your first window!"` in the middle of the screen. Check this example [running live on web here](https://www.raylib.com/examples/core/loader.html?name=core_basic_window). 59 | ```c 60 | #include "raylib.h" 61 | 62 | int main(void) 63 | { 64 | InitWindow(800, 450, "raylib [core] example - basic window"); 65 | 66 | while (!WindowShouldClose()) 67 | { 68 | BeginDrawing(); 69 | ClearBackground(RAYWHITE); 70 | DrawText("Congrats! You created your first window!", 190, 200, 20, LIGHTGRAY); 71 | EndDrawing(); 72 | } 73 | 74 | CloseWindow(); 75 | 76 | return 0; 77 | } 78 | ``` 79 | 80 | build and installation 81 | ---------------------- 82 | 83 | raylib binary releases for Windows, Linux, macOS, Android and HTML5 are available at the [Github Releases page](https://github.com/raysan5/raylib/releases). 84 | 85 | raylib is also available via multiple [package managers](https://github.com/raysan5/raylib/issues/613) on multiple OS distributions. 86 | 87 | #### Installing and building raylib on multiple platforms 88 | 89 | [raylib Wiki](https://github.com/raysan5/raylib/wiki#development-platforms) contains detailed instructions on building and usage on multiple platforms. 90 | 91 | - [Working on Windows](https://github.com/raysan5/raylib/wiki/Working-on-Windows) 92 | - [Working on macOS](https://github.com/raysan5/raylib/wiki/Working-on-macOS) 93 | - [Working on GNU Linux](https://github.com/raysan5/raylib/wiki/Working-on-GNU-Linux) 94 | - [Working on Chrome OS](https://github.com/raysan5/raylib/wiki/Working-on-Chrome-OS) 95 | - [Working on FreeBSD](https://github.com/raysan5/raylib/wiki/Working-on-FreeBSD) 96 | - [Working on Raspberry Pi](https://github.com/raysan5/raylib/wiki/Working-on-Raspberry-Pi) 97 | - [Working for Android](https://github.com/raysan5/raylib/wiki/Working-for-Android) 98 | - [Working for Web (HTML5)](https://github.com/raysan5/raylib/wiki/Working-for-Web-(HTML5)) 99 | - [Working anywhere with CMake](https://github.com/raysan5/raylib/wiki/Working-with-CMake) 100 | 101 | *Note that the Wiki is open for edit, if you find some issues while building raylib for your target platform, feel free to edit the Wiki or open an issue related to it.* 102 | 103 | #### Setup raylib with multiple IDEs 104 | 105 | raylib has been developed on Windows platform using [Notepad++](https://notepad-plus-plus.org/) and [MinGW GCC](https://www.mingw-w64.org/) compiler but it can be used with other IDEs on multiple platforms. 106 | 107 | [Projects directory](https://github.com/raysan5/raylib/tree/master/projects) contains several ready-to-use **project templates** to build raylib and code examples with multiple IDEs. 108 | 109 | *Note that there are lots of IDEs supported, some of the provided templates could require some review, so please, if you find some issue with a template or you think they could be improved, feel free to send a PR or open a related issue.* 110 | 111 | learning and docs 112 | ------------------ 113 | 114 | raylib is designed to be learned using [the examples](https://github.com/raysan5/raylib/tree/master/examples) as the main reference. There is no standard API documentation but there is a [**cheatsheet**](https://www.raylib.com/cheatsheet/cheatsheet.html) containing all the functions available on the library a short description of each one of them, input parameters and result value names should be intuitive enough to understand how each function works. 115 | 116 | Some additional documentation about raylib design can be found in [raylib GitHub Wiki](https://github.com/raysan5/raylib/wiki). Here are the relevant links: 117 | 118 | - [raylib cheatsheet](https://www.raylib.com/cheatsheet/cheatsheet.html) 119 | - [raylib architecture](https://github.com/raysan5/raylib/wiki/raylib-architecture) 120 | - [raylib library design](https://github.com/raysan5/raylib/wiki) 121 | - [raylib examples collection](https://github.com/raysan5/raylib/tree/master/examples) 122 | - [raylib games collection](https://github.com/raysan5/raylib-games) 123 | 124 | 125 | contact and networks 126 | --------------------- 127 | 128 | raylib is present in several networks and raylib community is growing everyday. If you are using raylib and enjoying it, feel free to join us in any of these networks. The most active network is our [Discord server](https://discord.gg/raylib)! :) 129 | 130 | - Webpage: [https://www.raylib.com](https://www.raylib.com) 131 | - Discord: [https://discord.gg/raylib](https://discord.gg/raylib) 132 | - Twitter: [https://www.twitter.com/raysan5](https://www.twitter.com/raysan5) 133 | - Twitch: [https://www.twitch.tv/raysan5](https://www.twitch.tv/raysan5) 134 | - Reddit: [https://www.reddit.com/r/raylib](https://www.reddit.com/r/raylib) 135 | - Patreon: [https://www.patreon.com/raylib](https://www.patreon.com/raylib) 136 | - YouTube: [https://www.youtube.com/channel/raylib](https://www.youtube.com/c/raylib) 137 | 138 | contributors 139 | ------------ 140 | 141 | 142 | 143 | 144 | 145 | license 146 | ------- 147 | 148 | raylib is licensed under an unmodified zlib/libpng license, which is an OSI-certified, BSD-like license that allows static linking with closed source software. Check [LICENSE](LICENSE) for further details. 149 | 150 | raylib uses internally some libraries for window/graphics/inputs management and also to support different file formats loading, all those libraries are embedded with and are available in [src/external](https://github.com/raysan5/raylib/tree/master/src/external) directory. Check [raylib dependencies LICENSES](https://github.com/raysan5/raylib/wiki/raylib-dependencies) on [raylib Wiki](https://github.com/raysan5/raylib/wiki) for details. 151 | -------------------------------------------------------------------------------- /raylib/raylib-5.0_linux_amd64/include/raymath.h: -------------------------------------------------------------------------------- 1 | /********************************************************************************************** 2 | * 3 | * raymath v1.5 - Math functions to work with Vector2, Vector3, Matrix and Quaternions 4 | * 5 | * CONVENTIONS: 6 | * - Matrix structure is defined as row-major (memory layout) but parameters naming AND all 7 | * math operations performed by the library consider the structure as it was column-major 8 | * It is like transposed versions of the matrices are used for all the maths 9 | * It benefits some functions making them cache-friendly and also avoids matrix 10 | * transpositions sometimes required by OpenGL 11 | * Example: In memory order, row0 is [m0 m4 m8 m12] but in semantic math row0 is [m0 m1 m2 m3] 12 | * - Functions are always self-contained, no function use another raymath function inside, 13 | * required code is directly re-implemented inside 14 | * - Functions input parameters are always received by value (2 unavoidable exceptions) 15 | * - Functions use always a "result" variable for return 16 | * - Functions are always defined inline 17 | * - Angles are always in radians (DEG2RAD/RAD2DEG macros provided for convenience) 18 | * - No compound literals used to make sure libray is compatible with C++ 19 | * 20 | * CONFIGURATION: 21 | * #define RAYMATH_IMPLEMENTATION 22 | * Generates the implementation of the library into the included file. 23 | * If not defined, the library is in header only mode and can be included in other headers 24 | * or source files without problems. But only ONE file should hold the implementation. 25 | * 26 | * #define RAYMATH_STATIC_INLINE 27 | * Define static inline functions code, so #include header suffices for use. 28 | * This may use up lots of memory. 29 | * 30 | * 31 | * LICENSE: zlib/libpng 32 | * 33 | * Copyright (c) 2015-2023 Ramon Santamaria (@raysan5) 34 | * 35 | * This software is provided "as-is", without any express or implied warranty. In no event 36 | * will the authors be held liable for any damages arising from the use of this software. 37 | * 38 | * Permission is granted to anyone to use this software for any purpose, including commercial 39 | * applications, and to alter it and redistribute it freely, subject to the following restrictions: 40 | * 41 | * 1. The origin of this software must not be misrepresented; you must not claim that you 42 | * wrote the original software. If you use this software in a product, an acknowledgment 43 | * in the product documentation would be appreciated but is not required. 44 | * 45 | * 2. Altered source versions must be plainly marked as such, and must not be misrepresented 46 | * as being the original software. 47 | * 48 | * 3. This notice may not be removed or altered from any source distribution. 49 | * 50 | **********************************************************************************************/ 51 | 52 | #ifndef RAYMATH_H 53 | #define RAYMATH_H 54 | 55 | #if defined(RAYMATH_IMPLEMENTATION) && defined(RAYMATH_STATIC_INLINE) 56 | #error "Specifying both RAYMATH_IMPLEMENTATION and RAYMATH_STATIC_INLINE is contradictory" 57 | #endif 58 | 59 | // Function specifiers definition 60 | #if defined(RAYMATH_IMPLEMENTATION) 61 | #if defined(_WIN32) && defined(BUILD_LIBTYPE_SHARED) 62 | #define RMAPI __declspec(dllexport) extern inline // We are building raylib as a Win32 shared library (.dll). 63 | #elif defined(_WIN32) && defined(USE_LIBTYPE_SHARED) 64 | #define RMAPI __declspec(dllimport) // We are using raylib as a Win32 shared library (.dll) 65 | #else 66 | #define RMAPI extern inline // Provide external definition 67 | #endif 68 | #elif defined(RAYMATH_STATIC_INLINE) 69 | #define RMAPI static inline // Functions may be inlined, no external out-of-line definition 70 | #else 71 | #if defined(__TINYC__) 72 | #define RMAPI static inline // plain inline not supported by tinycc (See issue #435) 73 | #else 74 | #define RMAPI inline // Functions may be inlined or external definition used 75 | #endif 76 | #endif 77 | 78 | //---------------------------------------------------------------------------------- 79 | // Defines and Macros 80 | //---------------------------------------------------------------------------------- 81 | #ifndef PI 82 | #define PI 3.14159265358979323846f 83 | #endif 84 | 85 | #ifndef EPSILON 86 | #define EPSILON 0.000001f 87 | #endif 88 | 89 | #ifndef DEG2RAD 90 | #define DEG2RAD (PI/180.0f) 91 | #endif 92 | 93 | #ifndef RAD2DEG 94 | #define RAD2DEG (180.0f/PI) 95 | #endif 96 | 97 | // Get float vector for Matrix 98 | #ifndef MatrixToFloat 99 | #define MatrixToFloat(mat) (MatrixToFloatV(mat).v) 100 | #endif 101 | 102 | // Get float vector for Vector3 103 | #ifndef Vector3ToFloat 104 | #define Vector3ToFloat(vec) (Vector3ToFloatV(vec).v) 105 | #endif 106 | 107 | //---------------------------------------------------------------------------------- 108 | // Types and Structures Definition 109 | //---------------------------------------------------------------------------------- 110 | #if !defined(RL_VECTOR2_TYPE) 111 | // Vector2 type 112 | typedef struct Vector2 { 113 | float x; 114 | float y; 115 | } Vector2; 116 | #define RL_VECTOR2_TYPE 117 | #endif 118 | 119 | #if !defined(RL_VECTOR3_TYPE) 120 | // Vector3 type 121 | typedef struct Vector3 { 122 | float x; 123 | float y; 124 | float z; 125 | } Vector3; 126 | #define RL_VECTOR3_TYPE 127 | #endif 128 | 129 | #if !defined(RL_VECTOR4_TYPE) 130 | // Vector4 type 131 | typedef struct Vector4 { 132 | float x; 133 | float y; 134 | float z; 135 | float w; 136 | } Vector4; 137 | #define RL_VECTOR4_TYPE 138 | #endif 139 | 140 | #if !defined(RL_QUATERNION_TYPE) 141 | // Quaternion type 142 | typedef Vector4 Quaternion; 143 | #define RL_QUATERNION_TYPE 144 | #endif 145 | 146 | #if !defined(RL_MATRIX_TYPE) 147 | // Matrix type (OpenGL style 4x4 - right handed, column major) 148 | typedef struct Matrix { 149 | float m0, m4, m8, m12; // Matrix first row (4 components) 150 | float m1, m5, m9, m13; // Matrix second row (4 components) 151 | float m2, m6, m10, m14; // Matrix third row (4 components) 152 | float m3, m7, m11, m15; // Matrix fourth row (4 components) 153 | } Matrix; 154 | #define RL_MATRIX_TYPE 155 | #endif 156 | 157 | // NOTE: Helper types to be used instead of array return types for *ToFloat functions 158 | typedef struct float3 { 159 | float v[3]; 160 | } float3; 161 | 162 | typedef struct float16 { 163 | float v[16]; 164 | } float16; 165 | 166 | #include // Required for: sinf(), cosf(), tan(), atan2f(), sqrtf(), floor(), fminf(), fmaxf(), fabs() 167 | 168 | //---------------------------------------------------------------------------------- 169 | // Module Functions Definition - Utils math 170 | //---------------------------------------------------------------------------------- 171 | 172 | // Clamp float value 173 | RMAPI float Clamp(float value, float min, float max) 174 | { 175 | float result = (value < min)? min : value; 176 | 177 | if (result > max) result = max; 178 | 179 | return result; 180 | } 181 | 182 | // Calculate linear interpolation between two floats 183 | RMAPI float Lerp(float start, float end, float amount) 184 | { 185 | float result = start + amount*(end - start); 186 | 187 | return result; 188 | } 189 | 190 | // Normalize input value within input range 191 | RMAPI float Normalize(float value, float start, float end) 192 | { 193 | float result = (value - start)/(end - start); 194 | 195 | return result; 196 | } 197 | 198 | // Remap input value within input range to output range 199 | RMAPI float Remap(float value, float inputStart, float inputEnd, float outputStart, float outputEnd) 200 | { 201 | float result = (value - inputStart)/(inputEnd - inputStart)*(outputEnd - outputStart) + outputStart; 202 | 203 | return result; 204 | } 205 | 206 | // Wrap input value from min to max 207 | RMAPI float Wrap(float value, float min, float max) 208 | { 209 | float result = value - (max - min)*floorf((value - min)/(max - min)); 210 | 211 | return result; 212 | } 213 | 214 | // Check whether two given floats are almost equal 215 | RMAPI int FloatEquals(float x, float y) 216 | { 217 | #if !defined(EPSILON) 218 | #define EPSILON 0.000001f 219 | #endif 220 | 221 | int result = (fabsf(x - y)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(x), fabsf(y)))); 222 | 223 | return result; 224 | } 225 | 226 | //---------------------------------------------------------------------------------- 227 | // Module Functions Definition - Vector2 math 228 | //---------------------------------------------------------------------------------- 229 | 230 | // Vector with components value 0.0f 231 | RMAPI Vector2 Vector2Zero(void) 232 | { 233 | Vector2 result = { 0.0f, 0.0f }; 234 | 235 | return result; 236 | } 237 | 238 | // Vector with components value 1.0f 239 | RMAPI Vector2 Vector2One(void) 240 | { 241 | Vector2 result = { 1.0f, 1.0f }; 242 | 243 | return result; 244 | } 245 | 246 | // Add two vectors (v1 + v2) 247 | RMAPI Vector2 Vector2Add(Vector2 v1, Vector2 v2) 248 | { 249 | Vector2 result = { v1.x + v2.x, v1.y + v2.y }; 250 | 251 | return result; 252 | } 253 | 254 | // Add vector and float value 255 | RMAPI Vector2 Vector2AddValue(Vector2 v, float add) 256 | { 257 | Vector2 result = { v.x + add, v.y + add }; 258 | 259 | return result; 260 | } 261 | 262 | // Subtract two vectors (v1 - v2) 263 | RMAPI Vector2 Vector2Subtract(Vector2 v1, Vector2 v2) 264 | { 265 | Vector2 result = { v1.x - v2.x, v1.y - v2.y }; 266 | 267 | return result; 268 | } 269 | 270 | // Subtract vector by float value 271 | RMAPI Vector2 Vector2SubtractValue(Vector2 v, float sub) 272 | { 273 | Vector2 result = { v.x - sub, v.y - sub }; 274 | 275 | return result; 276 | } 277 | 278 | // Calculate vector length 279 | RMAPI float Vector2Length(Vector2 v) 280 | { 281 | float result = sqrtf((v.x*v.x) + (v.y*v.y)); 282 | 283 | return result; 284 | } 285 | 286 | // Calculate vector square length 287 | RMAPI float Vector2LengthSqr(Vector2 v) 288 | { 289 | float result = (v.x*v.x) + (v.y*v.y); 290 | 291 | return result; 292 | } 293 | 294 | // Calculate two vectors dot product 295 | RMAPI float Vector2DotProduct(Vector2 v1, Vector2 v2) 296 | { 297 | float result = (v1.x*v2.x + v1.y*v2.y); 298 | 299 | return result; 300 | } 301 | 302 | // Calculate distance between two vectors 303 | RMAPI float Vector2Distance(Vector2 v1, Vector2 v2) 304 | { 305 | float result = sqrtf((v1.x - v2.x)*(v1.x - v2.x) + (v1.y - v2.y)*(v1.y - v2.y)); 306 | 307 | return result; 308 | } 309 | 310 | // Calculate square distance between two vectors 311 | RMAPI float Vector2DistanceSqr(Vector2 v1, Vector2 v2) 312 | { 313 | float result = ((v1.x - v2.x)*(v1.x - v2.x) + (v1.y - v2.y)*(v1.y - v2.y)); 314 | 315 | return result; 316 | } 317 | 318 | // Calculate angle between two vectors 319 | // NOTE: Angle is calculated from origin point (0, 0) 320 | RMAPI float Vector2Angle(Vector2 v1, Vector2 v2) 321 | { 322 | float result = 0.0f; 323 | 324 | float dot = v1.x*v2.x + v1.y*v2.y; 325 | float det = v1.x*v2.y - v1.y*v2.x; 326 | 327 | result = atan2f(det, dot); 328 | 329 | return result; 330 | } 331 | 332 | // Calculate angle defined by a two vectors line 333 | // NOTE: Parameters need to be normalized 334 | // Current implementation should be aligned with glm::angle 335 | RMAPI float Vector2LineAngle(Vector2 start, Vector2 end) 336 | { 337 | float result = 0.0f; 338 | 339 | // TODO(10/9/2023): Currently angles move clockwise, determine if this is wanted behavior 340 | result = -atan2f(end.y - start.y, end.x - start.x); 341 | 342 | return result; 343 | } 344 | 345 | // Scale vector (multiply by value) 346 | RMAPI Vector2 Vector2Scale(Vector2 v, float scale) 347 | { 348 | Vector2 result = { v.x*scale, v.y*scale }; 349 | 350 | return result; 351 | } 352 | 353 | // Multiply vector by vector 354 | RMAPI Vector2 Vector2Multiply(Vector2 v1, Vector2 v2) 355 | { 356 | Vector2 result = { v1.x*v2.x, v1.y*v2.y }; 357 | 358 | return result; 359 | } 360 | 361 | // Negate vector 362 | RMAPI Vector2 Vector2Negate(Vector2 v) 363 | { 364 | Vector2 result = { -v.x, -v.y }; 365 | 366 | return result; 367 | } 368 | 369 | // Divide vector by vector 370 | RMAPI Vector2 Vector2Divide(Vector2 v1, Vector2 v2) 371 | { 372 | Vector2 result = { v1.x/v2.x, v1.y/v2.y }; 373 | 374 | return result; 375 | } 376 | 377 | // Normalize provided vector 378 | RMAPI Vector2 Vector2Normalize(Vector2 v) 379 | { 380 | Vector2 result = { 0 }; 381 | float length = sqrtf((v.x*v.x) + (v.y*v.y)); 382 | 383 | if (length > 0) 384 | { 385 | float ilength = 1.0f/length; 386 | result.x = v.x*ilength; 387 | result.y = v.y*ilength; 388 | } 389 | 390 | return result; 391 | } 392 | 393 | // Transforms a Vector2 by a given Matrix 394 | RMAPI Vector2 Vector2Transform(Vector2 v, Matrix mat) 395 | { 396 | Vector2 result = { 0 }; 397 | 398 | float x = v.x; 399 | float y = v.y; 400 | float z = 0; 401 | 402 | result.x = mat.m0*x + mat.m4*y + mat.m8*z + mat.m12; 403 | result.y = mat.m1*x + mat.m5*y + mat.m9*z + mat.m13; 404 | 405 | return result; 406 | } 407 | 408 | // Calculate linear interpolation between two vectors 409 | RMAPI Vector2 Vector2Lerp(Vector2 v1, Vector2 v2, float amount) 410 | { 411 | Vector2 result = { 0 }; 412 | 413 | result.x = v1.x + amount*(v2.x - v1.x); 414 | result.y = v1.y + amount*(v2.y - v1.y); 415 | 416 | return result; 417 | } 418 | 419 | // Calculate reflected vector to normal 420 | RMAPI Vector2 Vector2Reflect(Vector2 v, Vector2 normal) 421 | { 422 | Vector2 result = { 0 }; 423 | 424 | float dotProduct = (v.x*normal.x + v.y*normal.y); // Dot product 425 | 426 | result.x = v.x - (2.0f*normal.x)*dotProduct; 427 | result.y = v.y - (2.0f*normal.y)*dotProduct; 428 | 429 | return result; 430 | } 431 | 432 | // Rotate vector by angle 433 | RMAPI Vector2 Vector2Rotate(Vector2 v, float angle) 434 | { 435 | Vector2 result = { 0 }; 436 | 437 | float cosres = cosf(angle); 438 | float sinres = sinf(angle); 439 | 440 | result.x = v.x*cosres - v.y*sinres; 441 | result.y = v.x*sinres + v.y*cosres; 442 | 443 | return result; 444 | } 445 | 446 | // Move Vector towards target 447 | RMAPI Vector2 Vector2MoveTowards(Vector2 v, Vector2 target, float maxDistance) 448 | { 449 | Vector2 result = { 0 }; 450 | 451 | float dx = target.x - v.x; 452 | float dy = target.y - v.y; 453 | float value = (dx*dx) + (dy*dy); 454 | 455 | if ((value == 0) || ((maxDistance >= 0) && (value <= maxDistance*maxDistance))) return target; 456 | 457 | float dist = sqrtf(value); 458 | 459 | result.x = v.x + dx/dist*maxDistance; 460 | result.y = v.y + dy/dist*maxDistance; 461 | 462 | return result; 463 | } 464 | 465 | // Invert the given vector 466 | RMAPI Vector2 Vector2Invert(Vector2 v) 467 | { 468 | Vector2 result = { 1.0f/v.x, 1.0f/v.y }; 469 | 470 | return result; 471 | } 472 | 473 | // Clamp the components of the vector between 474 | // min and max values specified by the given vectors 475 | RMAPI Vector2 Vector2Clamp(Vector2 v, Vector2 min, Vector2 max) 476 | { 477 | Vector2 result = { 0 }; 478 | 479 | result.x = fminf(max.x, fmaxf(min.x, v.x)); 480 | result.y = fminf(max.y, fmaxf(min.y, v.y)); 481 | 482 | return result; 483 | } 484 | 485 | // Clamp the magnitude of the vector between two min and max values 486 | RMAPI Vector2 Vector2ClampValue(Vector2 v, float min, float max) 487 | { 488 | Vector2 result = v; 489 | 490 | float length = (v.x*v.x) + (v.y*v.y); 491 | if (length > 0.0f) 492 | { 493 | length = sqrtf(length); 494 | 495 | if (length < min) 496 | { 497 | float scale = min/length; 498 | result.x = v.x*scale; 499 | result.y = v.y*scale; 500 | } 501 | else if (length > max) 502 | { 503 | float scale = max/length; 504 | result.x = v.x*scale; 505 | result.y = v.y*scale; 506 | } 507 | } 508 | 509 | return result; 510 | } 511 | 512 | // Check whether two given vectors are almost equal 513 | RMAPI int Vector2Equals(Vector2 p, Vector2 q) 514 | { 515 | #if !defined(EPSILON) 516 | #define EPSILON 0.000001f 517 | #endif 518 | 519 | int result = ((fabsf(p.x - q.x)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.x), fabsf(q.x))))) && 520 | ((fabsf(p.y - q.y)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.y), fabsf(q.y))))); 521 | 522 | return result; 523 | } 524 | 525 | //---------------------------------------------------------------------------------- 526 | // Module Functions Definition - Vector3 math 527 | //---------------------------------------------------------------------------------- 528 | 529 | // Vector with components value 0.0f 530 | RMAPI Vector3 Vector3Zero(void) 531 | { 532 | Vector3 result = { 0.0f, 0.0f, 0.0f }; 533 | 534 | return result; 535 | } 536 | 537 | // Vector with components value 1.0f 538 | RMAPI Vector3 Vector3One(void) 539 | { 540 | Vector3 result = { 1.0f, 1.0f, 1.0f }; 541 | 542 | return result; 543 | } 544 | 545 | // Add two vectors 546 | RMAPI Vector3 Vector3Add(Vector3 v1, Vector3 v2) 547 | { 548 | Vector3 result = { v1.x + v2.x, v1.y + v2.y, v1.z + v2.z }; 549 | 550 | return result; 551 | } 552 | 553 | // Add vector and float value 554 | RMAPI Vector3 Vector3AddValue(Vector3 v, float add) 555 | { 556 | Vector3 result = { v.x + add, v.y + add, v.z + add }; 557 | 558 | return result; 559 | } 560 | 561 | // Subtract two vectors 562 | RMAPI Vector3 Vector3Subtract(Vector3 v1, Vector3 v2) 563 | { 564 | Vector3 result = { v1.x - v2.x, v1.y - v2.y, v1.z - v2.z }; 565 | 566 | return result; 567 | } 568 | 569 | // Subtract vector by float value 570 | RMAPI Vector3 Vector3SubtractValue(Vector3 v, float sub) 571 | { 572 | Vector3 result = { v.x - sub, v.y - sub, v.z - sub }; 573 | 574 | return result; 575 | } 576 | 577 | // Multiply vector by scalar 578 | RMAPI Vector3 Vector3Scale(Vector3 v, float scalar) 579 | { 580 | Vector3 result = { v.x*scalar, v.y*scalar, v.z*scalar }; 581 | 582 | return result; 583 | } 584 | 585 | // Multiply vector by vector 586 | RMAPI Vector3 Vector3Multiply(Vector3 v1, Vector3 v2) 587 | { 588 | Vector3 result = { v1.x*v2.x, v1.y*v2.y, v1.z*v2.z }; 589 | 590 | return result; 591 | } 592 | 593 | // Calculate two vectors cross product 594 | RMAPI Vector3 Vector3CrossProduct(Vector3 v1, Vector3 v2) 595 | { 596 | Vector3 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 }; 597 | 598 | return result; 599 | } 600 | 601 | // Calculate one vector perpendicular vector 602 | RMAPI Vector3 Vector3Perpendicular(Vector3 v) 603 | { 604 | Vector3 result = { 0 }; 605 | 606 | float min = (float) fabs(v.x); 607 | Vector3 cardinalAxis = {1.0f, 0.0f, 0.0f}; 608 | 609 | if (fabsf(v.y) < min) 610 | { 611 | min = (float) fabs(v.y); 612 | Vector3 tmp = {0.0f, 1.0f, 0.0f}; 613 | cardinalAxis = tmp; 614 | } 615 | 616 | if (fabsf(v.z) < min) 617 | { 618 | Vector3 tmp = {0.0f, 0.0f, 1.0f}; 619 | cardinalAxis = tmp; 620 | } 621 | 622 | // Cross product between vectors 623 | result.x = v.y*cardinalAxis.z - v.z*cardinalAxis.y; 624 | result.y = v.z*cardinalAxis.x - v.x*cardinalAxis.z; 625 | result.z = v.x*cardinalAxis.y - v.y*cardinalAxis.x; 626 | 627 | return result; 628 | } 629 | 630 | // Calculate vector length 631 | RMAPI float Vector3Length(const Vector3 v) 632 | { 633 | float result = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z); 634 | 635 | return result; 636 | } 637 | 638 | // Calculate vector square length 639 | RMAPI float Vector3LengthSqr(const Vector3 v) 640 | { 641 | float result = v.x*v.x + v.y*v.y + v.z*v.z; 642 | 643 | return result; 644 | } 645 | 646 | // Calculate two vectors dot product 647 | RMAPI float Vector3DotProduct(Vector3 v1, Vector3 v2) 648 | { 649 | float result = (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z); 650 | 651 | return result; 652 | } 653 | 654 | // Calculate distance between two vectors 655 | RMAPI float Vector3Distance(Vector3 v1, Vector3 v2) 656 | { 657 | float result = 0.0f; 658 | 659 | float dx = v2.x - v1.x; 660 | float dy = v2.y - v1.y; 661 | float dz = v2.z - v1.z; 662 | result = sqrtf(dx*dx + dy*dy + dz*dz); 663 | 664 | return result; 665 | } 666 | 667 | // Calculate square distance between two vectors 668 | RMAPI float Vector3DistanceSqr(Vector3 v1, Vector3 v2) 669 | { 670 | float result = 0.0f; 671 | 672 | float dx = v2.x - v1.x; 673 | float dy = v2.y - v1.y; 674 | float dz = v2.z - v1.z; 675 | result = dx*dx + dy*dy + dz*dz; 676 | 677 | return result; 678 | } 679 | 680 | // Calculate angle between two vectors 681 | RMAPI float Vector3Angle(Vector3 v1, Vector3 v2) 682 | { 683 | float result = 0.0f; 684 | 685 | Vector3 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 }; 686 | float len = sqrtf(cross.x*cross.x + cross.y*cross.y + cross.z*cross.z); 687 | float dot = (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z); 688 | result = atan2f(len, dot); 689 | 690 | return result; 691 | } 692 | 693 | // Negate provided vector (invert direction) 694 | RMAPI Vector3 Vector3Negate(Vector3 v) 695 | { 696 | Vector3 result = { -v.x, -v.y, -v.z }; 697 | 698 | return result; 699 | } 700 | 701 | // Divide vector by vector 702 | RMAPI Vector3 Vector3Divide(Vector3 v1, Vector3 v2) 703 | { 704 | Vector3 result = { v1.x/v2.x, v1.y/v2.y, v1.z/v2.z }; 705 | 706 | return result; 707 | } 708 | 709 | // Normalize provided vector 710 | RMAPI Vector3 Vector3Normalize(Vector3 v) 711 | { 712 | Vector3 result = v; 713 | 714 | float length = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z); 715 | if (length != 0.0f) 716 | { 717 | float ilength = 1.0f/length; 718 | 719 | result.x *= ilength; 720 | result.y *= ilength; 721 | result.z *= ilength; 722 | } 723 | 724 | return result; 725 | } 726 | 727 | //Calculate the projection of the vector v1 on to v2 728 | RMAPI Vector3 Vector3Project(Vector3 v1, Vector3 v2) 729 | { 730 | Vector3 result = { 0 }; 731 | 732 | float v1dv2 = (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z); 733 | float v2dv2 = (v2.x*v2.x + v2.y*v2.y + v2.z*v2.z); 734 | 735 | float mag = v1dv2/v2dv2; 736 | 737 | result.x = v2.x*mag; 738 | result.y = v2.y*mag; 739 | result.z = v2.z*mag; 740 | 741 | return result; 742 | } 743 | 744 | //Calculate the rejection of the vector v1 on to v2 745 | RMAPI Vector3 Vector3Reject(Vector3 v1, Vector3 v2) 746 | { 747 | Vector3 result = { 0 }; 748 | 749 | float v1dv2 = (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z); 750 | float v2dv2 = (v2.x*v2.x + v2.y*v2.y + v2.z*v2.z); 751 | 752 | float mag = v1dv2/v2dv2; 753 | 754 | result.x = v1.x - (v2.x*mag); 755 | result.y = v1.y - (v2.y*mag); 756 | result.z = v1.z - (v2.z*mag); 757 | 758 | return result; 759 | } 760 | 761 | // Orthonormalize provided vectors 762 | // Makes vectors normalized and orthogonal to each other 763 | // Gram-Schmidt function implementation 764 | RMAPI void Vector3OrthoNormalize(Vector3 *v1, Vector3 *v2) 765 | { 766 | float length = 0.0f; 767 | float ilength = 0.0f; 768 | 769 | // Vector3Normalize(*v1); 770 | Vector3 v = *v1; 771 | length = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z); 772 | if (length == 0.0f) length = 1.0f; 773 | ilength = 1.0f/length; 774 | v1->x *= ilength; 775 | v1->y *= ilength; 776 | v1->z *= ilength; 777 | 778 | // Vector3CrossProduct(*v1, *v2) 779 | Vector3 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 }; 780 | 781 | // Vector3Normalize(vn1); 782 | v = vn1; 783 | length = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z); 784 | if (length == 0.0f) length = 1.0f; 785 | ilength = 1.0f/length; 786 | vn1.x *= ilength; 787 | vn1.y *= ilength; 788 | vn1.z *= ilength; 789 | 790 | // Vector3CrossProduct(vn1, *v1) 791 | Vector3 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 }; 792 | 793 | *v2 = vn2; 794 | } 795 | 796 | // Transforms a Vector3 by a given Matrix 797 | RMAPI Vector3 Vector3Transform(Vector3 v, Matrix mat) 798 | { 799 | Vector3 result = { 0 }; 800 | 801 | float x = v.x; 802 | float y = v.y; 803 | float z = v.z; 804 | 805 | result.x = mat.m0*x + mat.m4*y + mat.m8*z + mat.m12; 806 | result.y = mat.m1*x + mat.m5*y + mat.m9*z + mat.m13; 807 | result.z = mat.m2*x + mat.m6*y + mat.m10*z + mat.m14; 808 | 809 | return result; 810 | } 811 | 812 | // Transform a vector by quaternion rotation 813 | RMAPI Vector3 Vector3RotateByQuaternion(Vector3 v, Quaternion q) 814 | { 815 | Vector3 result = { 0 }; 816 | 817 | 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); 818 | 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); 819 | 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); 820 | 821 | return result; 822 | } 823 | 824 | // Rotates a vector around an axis 825 | RMAPI Vector3 Vector3RotateByAxisAngle(Vector3 v, Vector3 axis, float angle) 826 | { 827 | // Using Euler-Rodrigues Formula 828 | // Ref.: https://en.wikipedia.org/w/index.php?title=Euler%E2%80%93Rodrigues_formula 829 | 830 | Vector3 result = v; 831 | 832 | // Vector3Normalize(axis); 833 | float length = sqrtf(axis.x*axis.x + axis.y*axis.y + axis.z*axis.z); 834 | if (length == 0.0f) length = 1.0f; 835 | float ilength = 1.0f / length; 836 | axis.x *= ilength; 837 | axis.y *= ilength; 838 | axis.z *= ilength; 839 | 840 | angle /= 2.0f; 841 | float a = sinf(angle); 842 | float b = axis.x*a; 843 | float c = axis.y*a; 844 | float d = axis.z*a; 845 | a = cosf(angle); 846 | Vector3 w = { b, c, d }; 847 | 848 | // Vector3CrossProduct(w, v) 849 | Vector3 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 }; 850 | 851 | // Vector3CrossProduct(w, wv) 852 | Vector3 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 }; 853 | 854 | // Vector3Scale(wv, 2*a) 855 | a *= 2; 856 | wv.x *= a; 857 | wv.y *= a; 858 | wv.z *= a; 859 | 860 | // Vector3Scale(wwv, 2) 861 | wwv.x *= 2; 862 | wwv.y *= 2; 863 | wwv.z *= 2; 864 | 865 | result.x += wv.x; 866 | result.y += wv.y; 867 | result.z += wv.z; 868 | 869 | result.x += wwv.x; 870 | result.y += wwv.y; 871 | result.z += wwv.z; 872 | 873 | return result; 874 | } 875 | 876 | // Calculate linear interpolation between two vectors 877 | RMAPI Vector3 Vector3Lerp(Vector3 v1, Vector3 v2, float amount) 878 | { 879 | Vector3 result = { 0 }; 880 | 881 | result.x = v1.x + amount*(v2.x - v1.x); 882 | result.y = v1.y + amount*(v2.y - v1.y); 883 | result.z = v1.z + amount*(v2.z - v1.z); 884 | 885 | return result; 886 | } 887 | 888 | // Calculate reflected vector to normal 889 | RMAPI Vector3 Vector3Reflect(Vector3 v, Vector3 normal) 890 | { 891 | Vector3 result = { 0 }; 892 | 893 | // I is the original vector 894 | // N is the normal of the incident plane 895 | // R = I - (2*N*(DotProduct[I, N])) 896 | 897 | float dotProduct = (v.x*normal.x + v.y*normal.y + v.z*normal.z); 898 | 899 | result.x = v.x - (2.0f*normal.x)*dotProduct; 900 | result.y = v.y - (2.0f*normal.y)*dotProduct; 901 | result.z = v.z - (2.0f*normal.z)*dotProduct; 902 | 903 | return result; 904 | } 905 | 906 | // Get min value for each pair of components 907 | RMAPI Vector3 Vector3Min(Vector3 v1, Vector3 v2) 908 | { 909 | Vector3 result = { 0 }; 910 | 911 | result.x = fminf(v1.x, v2.x); 912 | result.y = fminf(v1.y, v2.y); 913 | result.z = fminf(v1.z, v2.z); 914 | 915 | return result; 916 | } 917 | 918 | // Get max value for each pair of components 919 | RMAPI Vector3 Vector3Max(Vector3 v1, Vector3 v2) 920 | { 921 | Vector3 result = { 0 }; 922 | 923 | result.x = fmaxf(v1.x, v2.x); 924 | result.y = fmaxf(v1.y, v2.y); 925 | result.z = fmaxf(v1.z, v2.z); 926 | 927 | return result; 928 | } 929 | 930 | // Compute barycenter coordinates (u, v, w) for point p with respect to triangle (a, b, c) 931 | // NOTE: Assumes P is on the plane of the triangle 932 | RMAPI Vector3 Vector3Barycenter(Vector3 p, Vector3 a, Vector3 b, Vector3 c) 933 | { 934 | Vector3 result = { 0 }; 935 | 936 | Vector3 v0 = { b.x - a.x, b.y - a.y, b.z - a.z }; // Vector3Subtract(b, a) 937 | Vector3 v1 = { c.x - a.x, c.y - a.y, c.z - a.z }; // Vector3Subtract(c, a) 938 | Vector3 v2 = { p.x - a.x, p.y - a.y, p.z - a.z }; // Vector3Subtract(p, a) 939 | float d00 = (v0.x*v0.x + v0.y*v0.y + v0.z*v0.z); // Vector3DotProduct(v0, v0) 940 | float d01 = (v0.x*v1.x + v0.y*v1.y + v0.z*v1.z); // Vector3DotProduct(v0, v1) 941 | float d11 = (v1.x*v1.x + v1.y*v1.y + v1.z*v1.z); // Vector3DotProduct(v1, v1) 942 | float d20 = (v2.x*v0.x + v2.y*v0.y + v2.z*v0.z); // Vector3DotProduct(v2, v0) 943 | float d21 = (v2.x*v1.x + v2.y*v1.y + v2.z*v1.z); // Vector3DotProduct(v2, v1) 944 | 945 | float denom = d00*d11 - d01*d01; 946 | 947 | result.y = (d11*d20 - d01*d21)/denom; 948 | result.z = (d00*d21 - d01*d20)/denom; 949 | result.x = 1.0f - (result.z + result.y); 950 | 951 | return result; 952 | } 953 | 954 | // Projects a Vector3 from screen space into object space 955 | // NOTE: We are avoiding calling other raymath functions despite available 956 | RMAPI Vector3 Vector3Unproject(Vector3 source, Matrix projection, Matrix view) 957 | { 958 | Vector3 result = { 0 }; 959 | 960 | // Calculate unprojected matrix (multiply view matrix by projection matrix) and invert it 961 | Matrix matViewProj = { // MatrixMultiply(view, projection); 962 | view.m0*projection.m0 + view.m1*projection.m4 + view.m2*projection.m8 + view.m3*projection.m12, 963 | view.m0*projection.m1 + view.m1*projection.m5 + view.m2*projection.m9 + view.m3*projection.m13, 964 | view.m0*projection.m2 + view.m1*projection.m6 + view.m2*projection.m10 + view.m3*projection.m14, 965 | view.m0*projection.m3 + view.m1*projection.m7 + view.m2*projection.m11 + view.m3*projection.m15, 966 | view.m4*projection.m0 + view.m5*projection.m4 + view.m6*projection.m8 + view.m7*projection.m12, 967 | view.m4*projection.m1 + view.m5*projection.m5 + view.m6*projection.m9 + view.m7*projection.m13, 968 | view.m4*projection.m2 + view.m5*projection.m6 + view.m6*projection.m10 + view.m7*projection.m14, 969 | view.m4*projection.m3 + view.m5*projection.m7 + view.m6*projection.m11 + view.m7*projection.m15, 970 | view.m8*projection.m0 + view.m9*projection.m4 + view.m10*projection.m8 + view.m11*projection.m12, 971 | view.m8*projection.m1 + view.m9*projection.m5 + view.m10*projection.m9 + view.m11*projection.m13, 972 | view.m8*projection.m2 + view.m9*projection.m6 + view.m10*projection.m10 + view.m11*projection.m14, 973 | view.m8*projection.m3 + view.m9*projection.m7 + view.m10*projection.m11 + view.m11*projection.m15, 974 | view.m12*projection.m0 + view.m13*projection.m4 + view.m14*projection.m8 + view.m15*projection.m12, 975 | view.m12*projection.m1 + view.m13*projection.m5 + view.m14*projection.m9 + view.m15*projection.m13, 976 | view.m12*projection.m2 + view.m13*projection.m6 + view.m14*projection.m10 + view.m15*projection.m14, 977 | view.m12*projection.m3 + view.m13*projection.m7 + view.m14*projection.m11 + view.m15*projection.m15 }; 978 | 979 | // Calculate inverted matrix -> MatrixInvert(matViewProj); 980 | // Cache the matrix values (speed optimization) 981 | float a00 = matViewProj.m0, a01 = matViewProj.m1, a02 = matViewProj.m2, a03 = matViewProj.m3; 982 | float a10 = matViewProj.m4, a11 = matViewProj.m5, a12 = matViewProj.m6, a13 = matViewProj.m7; 983 | float a20 = matViewProj.m8, a21 = matViewProj.m9, a22 = matViewProj.m10, a23 = matViewProj.m11; 984 | float a30 = matViewProj.m12, a31 = matViewProj.m13, a32 = matViewProj.m14, a33 = matViewProj.m15; 985 | 986 | float b00 = a00*a11 - a01*a10; 987 | float b01 = a00*a12 - a02*a10; 988 | float b02 = a00*a13 - a03*a10; 989 | float b03 = a01*a12 - a02*a11; 990 | float b04 = a01*a13 - a03*a11; 991 | float b05 = a02*a13 - a03*a12; 992 | float b06 = a20*a31 - a21*a30; 993 | float b07 = a20*a32 - a22*a30; 994 | float b08 = a20*a33 - a23*a30; 995 | float b09 = a21*a32 - a22*a31; 996 | float b10 = a21*a33 - a23*a31; 997 | float b11 = a22*a33 - a23*a32; 998 | 999 | // Calculate the invert determinant (inlined to avoid double-caching) 1000 | float invDet = 1.0f/(b00*b11 - b01*b10 + b02*b09 + b03*b08 - b04*b07 + b05*b06); 1001 | 1002 | Matrix matViewProjInv = { 1003 | (a11*b11 - a12*b10 + a13*b09)*invDet, 1004 | (-a01*b11 + a02*b10 - a03*b09)*invDet, 1005 | (a31*b05 - a32*b04 + a33*b03)*invDet, 1006 | (-a21*b05 + a22*b04 - a23*b03)*invDet, 1007 | (-a10*b11 + a12*b08 - a13*b07)*invDet, 1008 | (a00*b11 - a02*b08 + a03*b07)*invDet, 1009 | (-a30*b05 + a32*b02 - a33*b01)*invDet, 1010 | (a20*b05 - a22*b02 + a23*b01)*invDet, 1011 | (a10*b10 - a11*b08 + a13*b06)*invDet, 1012 | (-a00*b10 + a01*b08 - a03*b06)*invDet, 1013 | (a30*b04 - a31*b02 + a33*b00)*invDet, 1014 | (-a20*b04 + a21*b02 - a23*b00)*invDet, 1015 | (-a10*b09 + a11*b07 - a12*b06)*invDet, 1016 | (a00*b09 - a01*b07 + a02*b06)*invDet, 1017 | (-a30*b03 + a31*b01 - a32*b00)*invDet, 1018 | (a20*b03 - a21*b01 + a22*b00)*invDet }; 1019 | 1020 | // Create quaternion from source point 1021 | Quaternion quat = { source.x, source.y, source.z, 1.0f }; 1022 | 1023 | // Multiply quat point by unprojecte matrix 1024 | Quaternion qtransformed = { // QuaternionTransform(quat, matViewProjInv) 1025 | matViewProjInv.m0*quat.x + matViewProjInv.m4*quat.y + matViewProjInv.m8*quat.z + matViewProjInv.m12*quat.w, 1026 | matViewProjInv.m1*quat.x + matViewProjInv.m5*quat.y + matViewProjInv.m9*quat.z + matViewProjInv.m13*quat.w, 1027 | matViewProjInv.m2*quat.x + matViewProjInv.m6*quat.y + matViewProjInv.m10*quat.z + matViewProjInv.m14*quat.w, 1028 | matViewProjInv.m3*quat.x + matViewProjInv.m7*quat.y + matViewProjInv.m11*quat.z + matViewProjInv.m15*quat.w }; 1029 | 1030 | // Normalized world points in vectors 1031 | result.x = qtransformed.x/qtransformed.w; 1032 | result.y = qtransformed.y/qtransformed.w; 1033 | result.z = qtransformed.z/qtransformed.w; 1034 | 1035 | return result; 1036 | } 1037 | 1038 | // Get Vector3 as float array 1039 | RMAPI float3 Vector3ToFloatV(Vector3 v) 1040 | { 1041 | float3 buffer = { 0 }; 1042 | 1043 | buffer.v[0] = v.x; 1044 | buffer.v[1] = v.y; 1045 | buffer.v[2] = v.z; 1046 | 1047 | return buffer; 1048 | } 1049 | 1050 | // Invert the given vector 1051 | RMAPI Vector3 Vector3Invert(Vector3 v) 1052 | { 1053 | Vector3 result = { 1.0f/v.x, 1.0f/v.y, 1.0f/v.z }; 1054 | 1055 | return result; 1056 | } 1057 | 1058 | // Clamp the components of the vector between 1059 | // min and max values specified by the given vectors 1060 | RMAPI Vector3 Vector3Clamp(Vector3 v, Vector3 min, Vector3 max) 1061 | { 1062 | Vector3 result = { 0 }; 1063 | 1064 | result.x = fminf(max.x, fmaxf(min.x, v.x)); 1065 | result.y = fminf(max.y, fmaxf(min.y, v.y)); 1066 | result.z = fminf(max.z, fmaxf(min.z, v.z)); 1067 | 1068 | return result; 1069 | } 1070 | 1071 | // Clamp the magnitude of the vector between two values 1072 | RMAPI Vector3 Vector3ClampValue(Vector3 v, float min, float max) 1073 | { 1074 | Vector3 result = v; 1075 | 1076 | float length = (v.x*v.x) + (v.y*v.y) + (v.z*v.z); 1077 | if (length > 0.0f) 1078 | { 1079 | length = sqrtf(length); 1080 | 1081 | if (length < min) 1082 | { 1083 | float scale = min/length; 1084 | result.x = v.x*scale; 1085 | result.y = v.y*scale; 1086 | result.z = v.z*scale; 1087 | } 1088 | else if (length > max) 1089 | { 1090 | float scale = max/length; 1091 | result.x = v.x*scale; 1092 | result.y = v.y*scale; 1093 | result.z = v.z*scale; 1094 | } 1095 | } 1096 | 1097 | return result; 1098 | } 1099 | 1100 | // Check whether two given vectors are almost equal 1101 | RMAPI int Vector3Equals(Vector3 p, Vector3 q) 1102 | { 1103 | #if !defined(EPSILON) 1104 | #define EPSILON 0.000001f 1105 | #endif 1106 | 1107 | int result = ((fabsf(p.x - q.x)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.x), fabsf(q.x))))) && 1108 | ((fabsf(p.y - q.y)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.y), fabsf(q.y))))) && 1109 | ((fabsf(p.z - q.z)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.z), fabsf(q.z))))); 1110 | 1111 | return result; 1112 | } 1113 | 1114 | // Compute the direction of a refracted ray 1115 | // v: normalized direction of the incoming ray 1116 | // n: normalized normal vector of the interface of two optical media 1117 | // r: ratio of the refractive index of the medium from where the ray comes 1118 | // to the refractive index of the medium on the other side of the surface 1119 | RMAPI Vector3 Vector3Refract(Vector3 v, Vector3 n, float r) 1120 | { 1121 | Vector3 result = { 0 }; 1122 | 1123 | float dot = v.x*n.x + v.y*n.y + v.z*n.z; 1124 | float d = 1.0f - r*r*(1.0f - dot*dot); 1125 | 1126 | if (d >= 0.0f) 1127 | { 1128 | d = sqrtf(d); 1129 | v.x = r*v.x - (r*dot + d)*n.x; 1130 | v.y = r*v.y - (r*dot + d)*n.y; 1131 | v.z = r*v.z - (r*dot + d)*n.z; 1132 | 1133 | result = v; 1134 | } 1135 | 1136 | return result; 1137 | } 1138 | 1139 | //---------------------------------------------------------------------------------- 1140 | // Module Functions Definition - Matrix math 1141 | //---------------------------------------------------------------------------------- 1142 | 1143 | // Compute matrix determinant 1144 | RMAPI float MatrixDeterminant(Matrix mat) 1145 | { 1146 | float result = 0.0f; 1147 | 1148 | // Cache the matrix values (speed optimization) 1149 | float a00 = mat.m0, a01 = mat.m1, a02 = mat.m2, a03 = mat.m3; 1150 | float a10 = mat.m4, a11 = mat.m5, a12 = mat.m6, a13 = mat.m7; 1151 | float a20 = mat.m8, a21 = mat.m9, a22 = mat.m10, a23 = mat.m11; 1152 | float a30 = mat.m12, a31 = mat.m13, a32 = mat.m14, a33 = mat.m15; 1153 | 1154 | result = a30*a21*a12*a03 - a20*a31*a12*a03 - a30*a11*a22*a03 + a10*a31*a22*a03 + 1155 | a20*a11*a32*a03 - a10*a21*a32*a03 - a30*a21*a02*a13 + a20*a31*a02*a13 + 1156 | a30*a01*a22*a13 - a00*a31*a22*a13 - a20*a01*a32*a13 + a00*a21*a32*a13 + 1157 | a30*a11*a02*a23 - a10*a31*a02*a23 - a30*a01*a12*a23 + a00*a31*a12*a23 + 1158 | a10*a01*a32*a23 - a00*a11*a32*a23 - a20*a11*a02*a33 + a10*a21*a02*a33 + 1159 | a20*a01*a12*a33 - a00*a21*a12*a33 - a10*a01*a22*a33 + a00*a11*a22*a33; 1160 | 1161 | return result; 1162 | } 1163 | 1164 | // Get the trace of the matrix (sum of the values along the diagonal) 1165 | RMAPI float MatrixTrace(Matrix mat) 1166 | { 1167 | float result = (mat.m0 + mat.m5 + mat.m10 + mat.m15); 1168 | 1169 | return result; 1170 | } 1171 | 1172 | // Transposes provided matrix 1173 | RMAPI Matrix MatrixTranspose(Matrix mat) 1174 | { 1175 | Matrix result = { 0 }; 1176 | 1177 | result.m0 = mat.m0; 1178 | result.m1 = mat.m4; 1179 | result.m2 = mat.m8; 1180 | result.m3 = mat.m12; 1181 | result.m4 = mat.m1; 1182 | result.m5 = mat.m5; 1183 | result.m6 = mat.m9; 1184 | result.m7 = mat.m13; 1185 | result.m8 = mat.m2; 1186 | result.m9 = mat.m6; 1187 | result.m10 = mat.m10; 1188 | result.m11 = mat.m14; 1189 | result.m12 = mat.m3; 1190 | result.m13 = mat.m7; 1191 | result.m14 = mat.m11; 1192 | result.m15 = mat.m15; 1193 | 1194 | return result; 1195 | } 1196 | 1197 | // Invert provided matrix 1198 | RMAPI Matrix MatrixInvert(Matrix mat) 1199 | { 1200 | Matrix result = { 0 }; 1201 | 1202 | // Cache the matrix values (speed optimization) 1203 | float a00 = mat.m0, a01 = mat.m1, a02 = mat.m2, a03 = mat.m3; 1204 | float a10 = mat.m4, a11 = mat.m5, a12 = mat.m6, a13 = mat.m7; 1205 | float a20 = mat.m8, a21 = mat.m9, a22 = mat.m10, a23 = mat.m11; 1206 | float a30 = mat.m12, a31 = mat.m13, a32 = mat.m14, a33 = mat.m15; 1207 | 1208 | float b00 = a00*a11 - a01*a10; 1209 | float b01 = a00*a12 - a02*a10; 1210 | float b02 = a00*a13 - a03*a10; 1211 | float b03 = a01*a12 - a02*a11; 1212 | float b04 = a01*a13 - a03*a11; 1213 | float b05 = a02*a13 - a03*a12; 1214 | float b06 = a20*a31 - a21*a30; 1215 | float b07 = a20*a32 - a22*a30; 1216 | float b08 = a20*a33 - a23*a30; 1217 | float b09 = a21*a32 - a22*a31; 1218 | float b10 = a21*a33 - a23*a31; 1219 | float b11 = a22*a33 - a23*a32; 1220 | 1221 | // Calculate the invert determinant (inlined to avoid double-caching) 1222 | float invDet = 1.0f/(b00*b11 - b01*b10 + b02*b09 + b03*b08 - b04*b07 + b05*b06); 1223 | 1224 | result.m0 = (a11*b11 - a12*b10 + a13*b09)*invDet; 1225 | result.m1 = (-a01*b11 + a02*b10 - a03*b09)*invDet; 1226 | result.m2 = (a31*b05 - a32*b04 + a33*b03)*invDet; 1227 | result.m3 = (-a21*b05 + a22*b04 - a23*b03)*invDet; 1228 | result.m4 = (-a10*b11 + a12*b08 - a13*b07)*invDet; 1229 | result.m5 = (a00*b11 - a02*b08 + a03*b07)*invDet; 1230 | result.m6 = (-a30*b05 + a32*b02 - a33*b01)*invDet; 1231 | result.m7 = (a20*b05 - a22*b02 + a23*b01)*invDet; 1232 | result.m8 = (a10*b10 - a11*b08 + a13*b06)*invDet; 1233 | result.m9 = (-a00*b10 + a01*b08 - a03*b06)*invDet; 1234 | result.m10 = (a30*b04 - a31*b02 + a33*b00)*invDet; 1235 | result.m11 = (-a20*b04 + a21*b02 - a23*b00)*invDet; 1236 | result.m12 = (-a10*b09 + a11*b07 - a12*b06)*invDet; 1237 | result.m13 = (a00*b09 - a01*b07 + a02*b06)*invDet; 1238 | result.m14 = (-a30*b03 + a31*b01 - a32*b00)*invDet; 1239 | result.m15 = (a20*b03 - a21*b01 + a22*b00)*invDet; 1240 | 1241 | return result; 1242 | } 1243 | 1244 | // Get identity matrix 1245 | RMAPI Matrix MatrixIdentity(void) 1246 | { 1247 | Matrix result = { 1.0f, 0.0f, 0.0f, 0.0f, 1248 | 0.0f, 1.0f, 0.0f, 0.0f, 1249 | 0.0f, 0.0f, 1.0f, 0.0f, 1250 | 0.0f, 0.0f, 0.0f, 1.0f }; 1251 | 1252 | return result; 1253 | } 1254 | 1255 | // Add two matrices 1256 | RMAPI Matrix MatrixAdd(Matrix left, Matrix right) 1257 | { 1258 | Matrix result = { 0 }; 1259 | 1260 | result.m0 = left.m0 + right.m0; 1261 | result.m1 = left.m1 + right.m1; 1262 | result.m2 = left.m2 + right.m2; 1263 | result.m3 = left.m3 + right.m3; 1264 | result.m4 = left.m4 + right.m4; 1265 | result.m5 = left.m5 + right.m5; 1266 | result.m6 = left.m6 + right.m6; 1267 | result.m7 = left.m7 + right.m7; 1268 | result.m8 = left.m8 + right.m8; 1269 | result.m9 = left.m9 + right.m9; 1270 | result.m10 = left.m10 + right.m10; 1271 | result.m11 = left.m11 + right.m11; 1272 | result.m12 = left.m12 + right.m12; 1273 | result.m13 = left.m13 + right.m13; 1274 | result.m14 = left.m14 + right.m14; 1275 | result.m15 = left.m15 + right.m15; 1276 | 1277 | return result; 1278 | } 1279 | 1280 | // Subtract two matrices (left - right) 1281 | RMAPI Matrix MatrixSubtract(Matrix left, Matrix right) 1282 | { 1283 | Matrix result = { 0 }; 1284 | 1285 | result.m0 = left.m0 - right.m0; 1286 | result.m1 = left.m1 - right.m1; 1287 | result.m2 = left.m2 - right.m2; 1288 | result.m3 = left.m3 - right.m3; 1289 | result.m4 = left.m4 - right.m4; 1290 | result.m5 = left.m5 - right.m5; 1291 | result.m6 = left.m6 - right.m6; 1292 | result.m7 = left.m7 - right.m7; 1293 | result.m8 = left.m8 - right.m8; 1294 | result.m9 = left.m9 - right.m9; 1295 | result.m10 = left.m10 - right.m10; 1296 | result.m11 = left.m11 - right.m11; 1297 | result.m12 = left.m12 - right.m12; 1298 | result.m13 = left.m13 - right.m13; 1299 | result.m14 = left.m14 - right.m14; 1300 | result.m15 = left.m15 - right.m15; 1301 | 1302 | return result; 1303 | } 1304 | 1305 | // Get two matrix multiplication 1306 | // NOTE: When multiplying matrices... the order matters! 1307 | RMAPI Matrix MatrixMultiply(Matrix left, Matrix right) 1308 | { 1309 | Matrix result = { 0 }; 1310 | 1311 | result.m0 = left.m0*right.m0 + left.m1*right.m4 + left.m2*right.m8 + left.m3*right.m12; 1312 | result.m1 = left.m0*right.m1 + left.m1*right.m5 + left.m2*right.m9 + left.m3*right.m13; 1313 | result.m2 = left.m0*right.m2 + left.m1*right.m6 + left.m2*right.m10 + left.m3*right.m14; 1314 | result.m3 = left.m0*right.m3 + left.m1*right.m7 + left.m2*right.m11 + left.m3*right.m15; 1315 | result.m4 = left.m4*right.m0 + left.m5*right.m4 + left.m6*right.m8 + left.m7*right.m12; 1316 | result.m5 = left.m4*right.m1 + left.m5*right.m5 + left.m6*right.m9 + left.m7*right.m13; 1317 | result.m6 = left.m4*right.m2 + left.m5*right.m6 + left.m6*right.m10 + left.m7*right.m14; 1318 | result.m7 = left.m4*right.m3 + left.m5*right.m7 + left.m6*right.m11 + left.m7*right.m15; 1319 | result.m8 = left.m8*right.m0 + left.m9*right.m4 + left.m10*right.m8 + left.m11*right.m12; 1320 | result.m9 = left.m8*right.m1 + left.m9*right.m5 + left.m10*right.m9 + left.m11*right.m13; 1321 | result.m10 = left.m8*right.m2 + left.m9*right.m6 + left.m10*right.m10 + left.m11*right.m14; 1322 | result.m11 = left.m8*right.m3 + left.m9*right.m7 + left.m10*right.m11 + left.m11*right.m15; 1323 | result.m12 = left.m12*right.m0 + left.m13*right.m4 + left.m14*right.m8 + left.m15*right.m12; 1324 | result.m13 = left.m12*right.m1 + left.m13*right.m5 + left.m14*right.m9 + left.m15*right.m13; 1325 | result.m14 = left.m12*right.m2 + left.m13*right.m6 + left.m14*right.m10 + left.m15*right.m14; 1326 | result.m15 = left.m12*right.m3 + left.m13*right.m7 + left.m14*right.m11 + left.m15*right.m15; 1327 | 1328 | return result; 1329 | } 1330 | 1331 | // Get translation matrix 1332 | RMAPI Matrix MatrixTranslate(float x, float y, float z) 1333 | { 1334 | Matrix result = { 1.0f, 0.0f, 0.0f, x, 1335 | 0.0f, 1.0f, 0.0f, y, 1336 | 0.0f, 0.0f, 1.0f, z, 1337 | 0.0f, 0.0f, 0.0f, 1.0f }; 1338 | 1339 | return result; 1340 | } 1341 | 1342 | // Create rotation matrix from axis and angle 1343 | // NOTE: Angle should be provided in radians 1344 | RMAPI Matrix MatrixRotate(Vector3 axis, float angle) 1345 | { 1346 | Matrix result = { 0 }; 1347 | 1348 | float x = axis.x, y = axis.y, z = axis.z; 1349 | 1350 | float lengthSquared = x*x + y*y + z*z; 1351 | 1352 | if ((lengthSquared != 1.0f) && (lengthSquared != 0.0f)) 1353 | { 1354 | float ilength = 1.0f/sqrtf(lengthSquared); 1355 | x *= ilength; 1356 | y *= ilength; 1357 | z *= ilength; 1358 | } 1359 | 1360 | float sinres = sinf(angle); 1361 | float cosres = cosf(angle); 1362 | float t = 1.0f - cosres; 1363 | 1364 | result.m0 = x*x*t + cosres; 1365 | result.m1 = y*x*t + z*sinres; 1366 | result.m2 = z*x*t - y*sinres; 1367 | result.m3 = 0.0f; 1368 | 1369 | result.m4 = x*y*t - z*sinres; 1370 | result.m5 = y*y*t + cosres; 1371 | result.m6 = z*y*t + x*sinres; 1372 | result.m7 = 0.0f; 1373 | 1374 | result.m8 = x*z*t + y*sinres; 1375 | result.m9 = y*z*t - x*sinres; 1376 | result.m10 = z*z*t + cosres; 1377 | result.m11 = 0.0f; 1378 | 1379 | result.m12 = 0.0f; 1380 | result.m13 = 0.0f; 1381 | result.m14 = 0.0f; 1382 | result.m15 = 1.0f; 1383 | 1384 | return result; 1385 | } 1386 | 1387 | // Get x-rotation matrix 1388 | // NOTE: Angle must be provided in radians 1389 | RMAPI Matrix MatrixRotateX(float angle) 1390 | { 1391 | Matrix result = { 1.0f, 0.0f, 0.0f, 0.0f, 1392 | 0.0f, 1.0f, 0.0f, 0.0f, 1393 | 0.0f, 0.0f, 1.0f, 0.0f, 1394 | 0.0f, 0.0f, 0.0f, 1.0f }; // MatrixIdentity() 1395 | 1396 | float cosres = cosf(angle); 1397 | float sinres = sinf(angle); 1398 | 1399 | result.m5 = cosres; 1400 | result.m6 = sinres; 1401 | result.m9 = -sinres; 1402 | result.m10 = cosres; 1403 | 1404 | return result; 1405 | } 1406 | 1407 | // Get y-rotation matrix 1408 | // NOTE: Angle must be provided in radians 1409 | RMAPI Matrix MatrixRotateY(float angle) 1410 | { 1411 | Matrix result = { 1.0f, 0.0f, 0.0f, 0.0f, 1412 | 0.0f, 1.0f, 0.0f, 0.0f, 1413 | 0.0f, 0.0f, 1.0f, 0.0f, 1414 | 0.0f, 0.0f, 0.0f, 1.0f }; // MatrixIdentity() 1415 | 1416 | float cosres = cosf(angle); 1417 | float sinres = sinf(angle); 1418 | 1419 | result.m0 = cosres; 1420 | result.m2 = -sinres; 1421 | result.m8 = sinres; 1422 | result.m10 = cosres; 1423 | 1424 | return result; 1425 | } 1426 | 1427 | // Get z-rotation matrix 1428 | // NOTE: Angle must be provided in radians 1429 | RMAPI Matrix MatrixRotateZ(float angle) 1430 | { 1431 | Matrix result = { 1.0f, 0.0f, 0.0f, 0.0f, 1432 | 0.0f, 1.0f, 0.0f, 0.0f, 1433 | 0.0f, 0.0f, 1.0f, 0.0f, 1434 | 0.0f, 0.0f, 0.0f, 1.0f }; // MatrixIdentity() 1435 | 1436 | float cosres = cosf(angle); 1437 | float sinres = sinf(angle); 1438 | 1439 | result.m0 = cosres; 1440 | result.m1 = sinres; 1441 | result.m4 = -sinres; 1442 | result.m5 = cosres; 1443 | 1444 | return result; 1445 | } 1446 | 1447 | 1448 | // Get xyz-rotation matrix 1449 | // NOTE: Angle must be provided in radians 1450 | RMAPI Matrix MatrixRotateXYZ(Vector3 angle) 1451 | { 1452 | Matrix result = { 1.0f, 0.0f, 0.0f, 0.0f, 1453 | 0.0f, 1.0f, 0.0f, 0.0f, 1454 | 0.0f, 0.0f, 1.0f, 0.0f, 1455 | 0.0f, 0.0f, 0.0f, 1.0f }; // MatrixIdentity() 1456 | 1457 | float cosz = cosf(-angle.z); 1458 | float sinz = sinf(-angle.z); 1459 | float cosy = cosf(-angle.y); 1460 | float siny = sinf(-angle.y); 1461 | float cosx = cosf(-angle.x); 1462 | float sinx = sinf(-angle.x); 1463 | 1464 | result.m0 = cosz*cosy; 1465 | result.m1 = (cosz*siny*sinx) - (sinz*cosx); 1466 | result.m2 = (cosz*siny*cosx) + (sinz*sinx); 1467 | 1468 | result.m4 = sinz*cosy; 1469 | result.m5 = (sinz*siny*sinx) + (cosz*cosx); 1470 | result.m6 = (sinz*siny*cosx) - (cosz*sinx); 1471 | 1472 | result.m8 = -siny; 1473 | result.m9 = cosy*sinx; 1474 | result.m10= cosy*cosx; 1475 | 1476 | return result; 1477 | } 1478 | 1479 | // Get zyx-rotation matrix 1480 | // NOTE: Angle must be provided in radians 1481 | RMAPI Matrix MatrixRotateZYX(Vector3 angle) 1482 | { 1483 | Matrix result = { 0 }; 1484 | 1485 | float cz = cosf(angle.z); 1486 | float sz = sinf(angle.z); 1487 | float cy = cosf(angle.y); 1488 | float sy = sinf(angle.y); 1489 | float cx = cosf(angle.x); 1490 | float sx = sinf(angle.x); 1491 | 1492 | result.m0 = cz*cy; 1493 | result.m4 = cz*sy*sx - cx*sz; 1494 | result.m8 = sz*sx + cz*cx*sy; 1495 | result.m12 = 0; 1496 | 1497 | result.m1 = cy*sz; 1498 | result.m5 = cz*cx + sz*sy*sx; 1499 | result.m9 = cx*sz*sy - cz*sx; 1500 | result.m13 = 0; 1501 | 1502 | result.m2 = -sy; 1503 | result.m6 = cy*sx; 1504 | result.m10 = cy*cx; 1505 | result.m14 = 0; 1506 | 1507 | result.m3 = 0; 1508 | result.m7 = 0; 1509 | result.m11 = 0; 1510 | result.m15 = 1; 1511 | 1512 | return result; 1513 | } 1514 | 1515 | // Get scaling matrix 1516 | RMAPI Matrix MatrixScale(float x, float y, float z) 1517 | { 1518 | Matrix result = { x, 0.0f, 0.0f, 0.0f, 1519 | 0.0f, y, 0.0f, 0.0f, 1520 | 0.0f, 0.0f, z, 0.0f, 1521 | 0.0f, 0.0f, 0.0f, 1.0f }; 1522 | 1523 | return result; 1524 | } 1525 | 1526 | // Get perspective projection matrix 1527 | RMAPI Matrix MatrixFrustum(double left, double right, double bottom, double top, double near, double far) 1528 | { 1529 | Matrix result = { 0 }; 1530 | 1531 | float rl = (float)(right - left); 1532 | float tb = (float)(top - bottom); 1533 | float fn = (float)(far - near); 1534 | 1535 | result.m0 = ((float)near*2.0f)/rl; 1536 | result.m1 = 0.0f; 1537 | result.m2 = 0.0f; 1538 | result.m3 = 0.0f; 1539 | 1540 | result.m4 = 0.0f; 1541 | result.m5 = ((float)near*2.0f)/tb; 1542 | result.m6 = 0.0f; 1543 | result.m7 = 0.0f; 1544 | 1545 | result.m8 = ((float)right + (float)left)/rl; 1546 | result.m9 = ((float)top + (float)bottom)/tb; 1547 | result.m10 = -((float)far + (float)near)/fn; 1548 | result.m11 = -1.0f; 1549 | 1550 | result.m12 = 0.0f; 1551 | result.m13 = 0.0f; 1552 | result.m14 = -((float)far*(float)near*2.0f)/fn; 1553 | result.m15 = 0.0f; 1554 | 1555 | return result; 1556 | } 1557 | 1558 | // Get perspective projection matrix 1559 | // NOTE: Fovy angle must be provided in radians 1560 | RMAPI Matrix MatrixPerspective(double fovY, double aspect, double nearPlane, double farPlane) 1561 | { 1562 | Matrix result = { 0 }; 1563 | 1564 | double top = nearPlane*tan(fovY*0.5); 1565 | double bottom = -top; 1566 | double right = top*aspect; 1567 | double left = -right; 1568 | 1569 | // MatrixFrustum(-right, right, -top, top, near, far); 1570 | float rl = (float)(right - left); 1571 | float tb = (float)(top - bottom); 1572 | float fn = (float)(farPlane - nearPlane); 1573 | 1574 | result.m0 = ((float)nearPlane*2.0f)/rl; 1575 | result.m5 = ((float)nearPlane*2.0f)/tb; 1576 | result.m8 = ((float)right + (float)left)/rl; 1577 | result.m9 = ((float)top + (float)bottom)/tb; 1578 | result.m10 = -((float)farPlane + (float)nearPlane)/fn; 1579 | result.m11 = -1.0f; 1580 | result.m14 = -((float)farPlane*(float)nearPlane*2.0f)/fn; 1581 | 1582 | return result; 1583 | } 1584 | 1585 | // Get orthographic projection matrix 1586 | RMAPI Matrix MatrixOrtho(double left, double right, double bottom, double top, double nearPlane, double farPlane) 1587 | { 1588 | Matrix result = { 0 }; 1589 | 1590 | float rl = (float)(right - left); 1591 | float tb = (float)(top - bottom); 1592 | float fn = (float)(farPlane - nearPlane); 1593 | 1594 | result.m0 = 2.0f/rl; 1595 | result.m1 = 0.0f; 1596 | result.m2 = 0.0f; 1597 | result.m3 = 0.0f; 1598 | result.m4 = 0.0f; 1599 | result.m5 = 2.0f/tb; 1600 | result.m6 = 0.0f; 1601 | result.m7 = 0.0f; 1602 | result.m8 = 0.0f; 1603 | result.m9 = 0.0f; 1604 | result.m10 = -2.0f/fn; 1605 | result.m11 = 0.0f; 1606 | result.m12 = -((float)left + (float)right)/rl; 1607 | result.m13 = -((float)top + (float)bottom)/tb; 1608 | result.m14 = -((float)farPlane + (float)nearPlane)/fn; 1609 | result.m15 = 1.0f; 1610 | 1611 | return result; 1612 | } 1613 | 1614 | // Get camera look-at matrix (view matrix) 1615 | RMAPI Matrix MatrixLookAt(Vector3 eye, Vector3 target, Vector3 up) 1616 | { 1617 | Matrix result = { 0 }; 1618 | 1619 | float length = 0.0f; 1620 | float ilength = 0.0f; 1621 | 1622 | // Vector3Subtract(eye, target) 1623 | Vector3 vz = { eye.x - target.x, eye.y - target.y, eye.z - target.z }; 1624 | 1625 | // Vector3Normalize(vz) 1626 | Vector3 v = vz; 1627 | length = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z); 1628 | if (length == 0.0f) length = 1.0f; 1629 | ilength = 1.0f/length; 1630 | vz.x *= ilength; 1631 | vz.y *= ilength; 1632 | vz.z *= ilength; 1633 | 1634 | // Vector3CrossProduct(up, vz) 1635 | Vector3 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 }; 1636 | 1637 | // Vector3Normalize(x) 1638 | v = vx; 1639 | length = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z); 1640 | if (length == 0.0f) length = 1.0f; 1641 | ilength = 1.0f/length; 1642 | vx.x *= ilength; 1643 | vx.y *= ilength; 1644 | vx.z *= ilength; 1645 | 1646 | // Vector3CrossProduct(vz, vx) 1647 | Vector3 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 }; 1648 | 1649 | result.m0 = vx.x; 1650 | result.m1 = vy.x; 1651 | result.m2 = vz.x; 1652 | result.m3 = 0.0f; 1653 | result.m4 = vx.y; 1654 | result.m5 = vy.y; 1655 | result.m6 = vz.y; 1656 | result.m7 = 0.0f; 1657 | result.m8 = vx.z; 1658 | result.m9 = vy.z; 1659 | result.m10 = vz.z; 1660 | result.m11 = 0.0f; 1661 | result.m12 = -(vx.x*eye.x + vx.y*eye.y + vx.z*eye.z); // Vector3DotProduct(vx, eye) 1662 | result.m13 = -(vy.x*eye.x + vy.y*eye.y + vy.z*eye.z); // Vector3DotProduct(vy, eye) 1663 | result.m14 = -(vz.x*eye.x + vz.y*eye.y + vz.z*eye.z); // Vector3DotProduct(vz, eye) 1664 | result.m15 = 1.0f; 1665 | 1666 | return result; 1667 | } 1668 | 1669 | // Get float array of matrix data 1670 | RMAPI float16 MatrixToFloatV(Matrix mat) 1671 | { 1672 | float16 result = { 0 }; 1673 | 1674 | result.v[0] = mat.m0; 1675 | result.v[1] = mat.m1; 1676 | result.v[2] = mat.m2; 1677 | result.v[3] = mat.m3; 1678 | result.v[4] = mat.m4; 1679 | result.v[5] = mat.m5; 1680 | result.v[6] = mat.m6; 1681 | result.v[7] = mat.m7; 1682 | result.v[8] = mat.m8; 1683 | result.v[9] = mat.m9; 1684 | result.v[10] = mat.m10; 1685 | result.v[11] = mat.m11; 1686 | result.v[12] = mat.m12; 1687 | result.v[13] = mat.m13; 1688 | result.v[14] = mat.m14; 1689 | result.v[15] = mat.m15; 1690 | 1691 | return result; 1692 | } 1693 | 1694 | //---------------------------------------------------------------------------------- 1695 | // Module Functions Definition - Quaternion math 1696 | //---------------------------------------------------------------------------------- 1697 | 1698 | // Add two quaternions 1699 | RMAPI Quaternion QuaternionAdd(Quaternion q1, Quaternion q2) 1700 | { 1701 | Quaternion result = {q1.x + q2.x, q1.y + q2.y, q1.z + q2.z, q1.w + q2.w}; 1702 | 1703 | return result; 1704 | } 1705 | 1706 | // Add quaternion and float value 1707 | RMAPI Quaternion QuaternionAddValue(Quaternion q, float add) 1708 | { 1709 | Quaternion result = {q.x + add, q.y + add, q.z + add, q.w + add}; 1710 | 1711 | return result; 1712 | } 1713 | 1714 | // Subtract two quaternions 1715 | RMAPI Quaternion QuaternionSubtract(Quaternion q1, Quaternion q2) 1716 | { 1717 | Quaternion result = {q1.x - q2.x, q1.y - q2.y, q1.z - q2.z, q1.w - q2.w}; 1718 | 1719 | return result; 1720 | } 1721 | 1722 | // Subtract quaternion and float value 1723 | RMAPI Quaternion QuaternionSubtractValue(Quaternion q, float sub) 1724 | { 1725 | Quaternion result = {q.x - sub, q.y - sub, q.z - sub, q.w - sub}; 1726 | 1727 | return result; 1728 | } 1729 | 1730 | // Get identity quaternion 1731 | RMAPI Quaternion QuaternionIdentity(void) 1732 | { 1733 | Quaternion result = { 0.0f, 0.0f, 0.0f, 1.0f }; 1734 | 1735 | return result; 1736 | } 1737 | 1738 | // Computes the length of a quaternion 1739 | RMAPI float QuaternionLength(Quaternion q) 1740 | { 1741 | float result = sqrtf(q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w); 1742 | 1743 | return result; 1744 | } 1745 | 1746 | // Normalize provided quaternion 1747 | RMAPI Quaternion QuaternionNormalize(Quaternion q) 1748 | { 1749 | Quaternion result = { 0 }; 1750 | 1751 | float length = sqrtf(q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w); 1752 | if (length == 0.0f) length = 1.0f; 1753 | float ilength = 1.0f/length; 1754 | 1755 | result.x = q.x*ilength; 1756 | result.y = q.y*ilength; 1757 | result.z = q.z*ilength; 1758 | result.w = q.w*ilength; 1759 | 1760 | return result; 1761 | } 1762 | 1763 | // Invert provided quaternion 1764 | RMAPI Quaternion QuaternionInvert(Quaternion q) 1765 | { 1766 | Quaternion result = q; 1767 | 1768 | float lengthSq = q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w; 1769 | 1770 | if (lengthSq != 0.0f) 1771 | { 1772 | float invLength = 1.0f/lengthSq; 1773 | 1774 | result.x *= -invLength; 1775 | result.y *= -invLength; 1776 | result.z *= -invLength; 1777 | result.w *= invLength; 1778 | } 1779 | 1780 | return result; 1781 | } 1782 | 1783 | // Calculate two quaternion multiplication 1784 | RMAPI Quaternion QuaternionMultiply(Quaternion q1, Quaternion q2) 1785 | { 1786 | Quaternion result = { 0 }; 1787 | 1788 | float qax = q1.x, qay = q1.y, qaz = q1.z, qaw = q1.w; 1789 | float qbx = q2.x, qby = q2.y, qbz = q2.z, qbw = q2.w; 1790 | 1791 | result.x = qax*qbw + qaw*qbx + qay*qbz - qaz*qby; 1792 | result.y = qay*qbw + qaw*qby + qaz*qbx - qax*qbz; 1793 | result.z = qaz*qbw + qaw*qbz + qax*qby - qay*qbx; 1794 | result.w = qaw*qbw - qax*qbx - qay*qby - qaz*qbz; 1795 | 1796 | return result; 1797 | } 1798 | 1799 | // Scale quaternion by float value 1800 | RMAPI Quaternion QuaternionScale(Quaternion q, float mul) 1801 | { 1802 | Quaternion result = { 0 }; 1803 | 1804 | result.x = q.x*mul; 1805 | result.y = q.y*mul; 1806 | result.z = q.z*mul; 1807 | result.w = q.w*mul; 1808 | 1809 | return result; 1810 | } 1811 | 1812 | // Divide two quaternions 1813 | RMAPI Quaternion QuaternionDivide(Quaternion q1, Quaternion q2) 1814 | { 1815 | Quaternion result = { q1.x/q2.x, q1.y/q2.y, q1.z/q2.z, q1.w/q2.w }; 1816 | 1817 | return result; 1818 | } 1819 | 1820 | // Calculate linear interpolation between two quaternions 1821 | RMAPI Quaternion QuaternionLerp(Quaternion q1, Quaternion q2, float amount) 1822 | { 1823 | Quaternion result = { 0 }; 1824 | 1825 | result.x = q1.x + amount*(q2.x - q1.x); 1826 | result.y = q1.y + amount*(q2.y - q1.y); 1827 | result.z = q1.z + amount*(q2.z - q1.z); 1828 | result.w = q1.w + amount*(q2.w - q1.w); 1829 | 1830 | return result; 1831 | } 1832 | 1833 | // Calculate slerp-optimized interpolation between two quaternions 1834 | RMAPI Quaternion QuaternionNlerp(Quaternion q1, Quaternion q2, float amount) 1835 | { 1836 | Quaternion result = { 0 }; 1837 | 1838 | // QuaternionLerp(q1, q2, amount) 1839 | result.x = q1.x + amount*(q2.x - q1.x); 1840 | result.y = q1.y + amount*(q2.y - q1.y); 1841 | result.z = q1.z + amount*(q2.z - q1.z); 1842 | result.w = q1.w + amount*(q2.w - q1.w); 1843 | 1844 | // QuaternionNormalize(q); 1845 | Quaternion q = result; 1846 | float length = sqrtf(q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w); 1847 | if (length == 0.0f) length = 1.0f; 1848 | float ilength = 1.0f/length; 1849 | 1850 | result.x = q.x*ilength; 1851 | result.y = q.y*ilength; 1852 | result.z = q.z*ilength; 1853 | result.w = q.w*ilength; 1854 | 1855 | return result; 1856 | } 1857 | 1858 | // Calculates spherical linear interpolation between two quaternions 1859 | RMAPI Quaternion QuaternionSlerp(Quaternion q1, Quaternion q2, float amount) 1860 | { 1861 | Quaternion result = { 0 }; 1862 | 1863 | #if !defined(EPSILON) 1864 | #define EPSILON 0.000001f 1865 | #endif 1866 | 1867 | float cosHalfTheta = q1.x*q2.x + q1.y*q2.y + q1.z*q2.z + q1.w*q2.w; 1868 | 1869 | if (cosHalfTheta < 0) 1870 | { 1871 | q2.x = -q2.x; q2.y = -q2.y; q2.z = -q2.z; q2.w = -q2.w; 1872 | cosHalfTheta = -cosHalfTheta; 1873 | } 1874 | 1875 | if (fabsf(cosHalfTheta) >= 1.0f) result = q1; 1876 | else if (cosHalfTheta > 0.95f) result = QuaternionNlerp(q1, q2, amount); 1877 | else 1878 | { 1879 | float halfTheta = acosf(cosHalfTheta); 1880 | float sinHalfTheta = sqrtf(1.0f - cosHalfTheta*cosHalfTheta); 1881 | 1882 | if (fabsf(sinHalfTheta) < EPSILON) 1883 | { 1884 | result.x = (q1.x*0.5f + q2.x*0.5f); 1885 | result.y = (q1.y*0.5f + q2.y*0.5f); 1886 | result.z = (q1.z*0.5f + q2.z*0.5f); 1887 | result.w = (q1.w*0.5f + q2.w*0.5f); 1888 | } 1889 | else 1890 | { 1891 | float ratioA = sinf((1 - amount)*halfTheta)/sinHalfTheta; 1892 | float ratioB = sinf(amount*halfTheta)/sinHalfTheta; 1893 | 1894 | result.x = (q1.x*ratioA + q2.x*ratioB); 1895 | result.y = (q1.y*ratioA + q2.y*ratioB); 1896 | result.z = (q1.z*ratioA + q2.z*ratioB); 1897 | result.w = (q1.w*ratioA + q2.w*ratioB); 1898 | } 1899 | } 1900 | 1901 | return result; 1902 | } 1903 | 1904 | // Calculate quaternion based on the rotation from one vector to another 1905 | RMAPI Quaternion QuaternionFromVector3ToVector3(Vector3 from, Vector3 to) 1906 | { 1907 | Quaternion result = { 0 }; 1908 | 1909 | float cos2Theta = (from.x*to.x + from.y*to.y + from.z*to.z); // Vector3DotProduct(from, to) 1910 | Vector3 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) 1911 | 1912 | result.x = cross.x; 1913 | result.y = cross.y; 1914 | result.z = cross.z; 1915 | result.w = 1.0f + cos2Theta; 1916 | 1917 | // QuaternionNormalize(q); 1918 | // NOTE: Normalize to essentially nlerp the original and identity to 0.5 1919 | Quaternion q = result; 1920 | float length = sqrtf(q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w); 1921 | if (length == 0.0f) length = 1.0f; 1922 | float ilength = 1.0f/length; 1923 | 1924 | result.x = q.x*ilength; 1925 | result.y = q.y*ilength; 1926 | result.z = q.z*ilength; 1927 | result.w = q.w*ilength; 1928 | 1929 | return result; 1930 | } 1931 | 1932 | // Get a quaternion for a given rotation matrix 1933 | RMAPI Quaternion QuaternionFromMatrix(Matrix mat) 1934 | { 1935 | Quaternion result = { 0 }; 1936 | 1937 | float fourWSquaredMinus1 = mat.m0 + mat.m5 + mat.m10; 1938 | float fourXSquaredMinus1 = mat.m0 - mat.m5 - mat.m10; 1939 | float fourYSquaredMinus1 = mat.m5 - mat.m0 - mat.m10; 1940 | float fourZSquaredMinus1 = mat.m10 - mat.m0 - mat.m5; 1941 | 1942 | int biggestIndex = 0; 1943 | float fourBiggestSquaredMinus1 = fourWSquaredMinus1; 1944 | if (fourXSquaredMinus1 > fourBiggestSquaredMinus1) 1945 | { 1946 | fourBiggestSquaredMinus1 = fourXSquaredMinus1; 1947 | biggestIndex = 1; 1948 | } 1949 | 1950 | if (fourYSquaredMinus1 > fourBiggestSquaredMinus1) 1951 | { 1952 | fourBiggestSquaredMinus1 = fourYSquaredMinus1; 1953 | biggestIndex = 2; 1954 | } 1955 | 1956 | if (fourZSquaredMinus1 > fourBiggestSquaredMinus1) 1957 | { 1958 | fourBiggestSquaredMinus1 = fourZSquaredMinus1; 1959 | biggestIndex = 3; 1960 | } 1961 | 1962 | float biggestVal = sqrtf(fourBiggestSquaredMinus1 + 1.0f)*0.5f; 1963 | float mult = 0.25f / biggestVal; 1964 | 1965 | switch (biggestIndex) 1966 | { 1967 | case 0: 1968 | result.w = biggestVal; 1969 | result.x = (mat.m6 - mat.m9)*mult; 1970 | result.y = (mat.m8 - mat.m2)*mult; 1971 | result.z = (mat.m1 - mat.m4)*mult; 1972 | break; 1973 | case 1: 1974 | result.x = biggestVal; 1975 | result.w = (mat.m6 - mat.m9)*mult; 1976 | result.y = (mat.m1 + mat.m4)*mult; 1977 | result.z = (mat.m8 + mat.m2)*mult; 1978 | break; 1979 | case 2: 1980 | result.y = biggestVal; 1981 | result.w = (mat.m8 - mat.m2)*mult; 1982 | result.x = (mat.m1 + mat.m4)*mult; 1983 | result.z = (mat.m6 + mat.m9)*mult; 1984 | break; 1985 | case 3: 1986 | result.z = biggestVal; 1987 | result.w = (mat.m1 - mat.m4)*mult; 1988 | result.x = (mat.m8 + mat.m2)*mult; 1989 | result.y = (mat.m6 + mat.m9)*mult; 1990 | break; 1991 | } 1992 | 1993 | return result; 1994 | } 1995 | 1996 | // Get a matrix for a given quaternion 1997 | RMAPI Matrix QuaternionToMatrix(Quaternion q) 1998 | { 1999 | Matrix result = { 1.0f, 0.0f, 0.0f, 0.0f, 2000 | 0.0f, 1.0f, 0.0f, 0.0f, 2001 | 0.0f, 0.0f, 1.0f, 0.0f, 2002 | 0.0f, 0.0f, 0.0f, 1.0f }; // MatrixIdentity() 2003 | 2004 | float a2 = q.x*q.x; 2005 | float b2 = q.y*q.y; 2006 | float c2 = q.z*q.z; 2007 | float ac = q.x*q.z; 2008 | float ab = q.x*q.y; 2009 | float bc = q.y*q.z; 2010 | float ad = q.w*q.x; 2011 | float bd = q.w*q.y; 2012 | float cd = q.w*q.z; 2013 | 2014 | result.m0 = 1 - 2*(b2 + c2); 2015 | result.m1 = 2*(ab + cd); 2016 | result.m2 = 2*(ac - bd); 2017 | 2018 | result.m4 = 2*(ab - cd); 2019 | result.m5 = 1 - 2*(a2 + c2); 2020 | result.m6 = 2*(bc + ad); 2021 | 2022 | result.m8 = 2*(ac + bd); 2023 | result.m9 = 2*(bc - ad); 2024 | result.m10 = 1 - 2*(a2 + b2); 2025 | 2026 | return result; 2027 | } 2028 | 2029 | // Get rotation quaternion for an angle and axis 2030 | // NOTE: Angle must be provided in radians 2031 | RMAPI Quaternion QuaternionFromAxisAngle(Vector3 axis, float angle) 2032 | { 2033 | Quaternion result = { 0.0f, 0.0f, 0.0f, 1.0f }; 2034 | 2035 | float axisLength = sqrtf(axis.x*axis.x + axis.y*axis.y + axis.z*axis.z); 2036 | 2037 | if (axisLength != 0.0f) 2038 | { 2039 | angle *= 0.5f; 2040 | 2041 | float length = 0.0f; 2042 | float ilength = 0.0f; 2043 | 2044 | // Vector3Normalize(axis) 2045 | Vector3 v = axis; 2046 | length = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z); 2047 | if (length == 0.0f) length = 1.0f; 2048 | ilength = 1.0f/length; 2049 | axis.x *= ilength; 2050 | axis.y *= ilength; 2051 | axis.z *= ilength; 2052 | 2053 | float sinres = sinf(angle); 2054 | float cosres = cosf(angle); 2055 | 2056 | result.x = axis.x*sinres; 2057 | result.y = axis.y*sinres; 2058 | result.z = axis.z*sinres; 2059 | result.w = cosres; 2060 | 2061 | // QuaternionNormalize(q); 2062 | Quaternion q = result; 2063 | length = sqrtf(q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w); 2064 | if (length == 0.0f) length = 1.0f; 2065 | ilength = 1.0f/length; 2066 | result.x = q.x*ilength; 2067 | result.y = q.y*ilength; 2068 | result.z = q.z*ilength; 2069 | result.w = q.w*ilength; 2070 | } 2071 | 2072 | return result; 2073 | } 2074 | 2075 | // Get the rotation angle and axis for a given quaternion 2076 | RMAPI void QuaternionToAxisAngle(Quaternion q, Vector3 *outAxis, float *outAngle) 2077 | { 2078 | if (fabsf(q.w) > 1.0f) 2079 | { 2080 | // QuaternionNormalize(q); 2081 | float length = sqrtf(q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w); 2082 | if (length == 0.0f) length = 1.0f; 2083 | float ilength = 1.0f/length; 2084 | 2085 | q.x = q.x*ilength; 2086 | q.y = q.y*ilength; 2087 | q.z = q.z*ilength; 2088 | q.w = q.w*ilength; 2089 | } 2090 | 2091 | Vector3 resAxis = { 0.0f, 0.0f, 0.0f }; 2092 | float resAngle = 2.0f*acosf(q.w); 2093 | float den = sqrtf(1.0f - q.w*q.w); 2094 | 2095 | if (den > EPSILON) 2096 | { 2097 | resAxis.x = q.x/den; 2098 | resAxis.y = q.y/den; 2099 | resAxis.z = q.z/den; 2100 | } 2101 | else 2102 | { 2103 | // This occurs when the angle is zero. 2104 | // Not a problem: just set an arbitrary normalized axis. 2105 | resAxis.x = 1.0f; 2106 | } 2107 | 2108 | *outAxis = resAxis; 2109 | *outAngle = resAngle; 2110 | } 2111 | 2112 | // Get the quaternion equivalent to Euler angles 2113 | // NOTE: Rotation order is ZYX 2114 | RMAPI Quaternion QuaternionFromEuler(float pitch, float yaw, float roll) 2115 | { 2116 | Quaternion result = { 0 }; 2117 | 2118 | float x0 = cosf(pitch*0.5f); 2119 | float x1 = sinf(pitch*0.5f); 2120 | float y0 = cosf(yaw*0.5f); 2121 | float y1 = sinf(yaw*0.5f); 2122 | float z0 = cosf(roll*0.5f); 2123 | float z1 = sinf(roll*0.5f); 2124 | 2125 | result.x = x1*y0*z0 - x0*y1*z1; 2126 | result.y = x0*y1*z0 + x1*y0*z1; 2127 | result.z = x0*y0*z1 - x1*y1*z0; 2128 | result.w = x0*y0*z0 + x1*y1*z1; 2129 | 2130 | return result; 2131 | } 2132 | 2133 | // Get the Euler angles equivalent to quaternion (roll, pitch, yaw) 2134 | // NOTE: Angles are returned in a Vector3 struct in radians 2135 | RMAPI Vector3 QuaternionToEuler(Quaternion q) 2136 | { 2137 | Vector3 result = { 0 }; 2138 | 2139 | // Roll (x-axis rotation) 2140 | float x0 = 2.0f*(q.w*q.x + q.y*q.z); 2141 | float x1 = 1.0f - 2.0f*(q.x*q.x + q.y*q.y); 2142 | result.x = atan2f(x0, x1); 2143 | 2144 | // Pitch (y-axis rotation) 2145 | float y0 = 2.0f*(q.w*q.y - q.z*q.x); 2146 | y0 = y0 > 1.0f ? 1.0f : y0; 2147 | y0 = y0 < -1.0f ? -1.0f : y0; 2148 | result.y = asinf(y0); 2149 | 2150 | // Yaw (z-axis rotation) 2151 | float z0 = 2.0f*(q.w*q.z + q.x*q.y); 2152 | float z1 = 1.0f - 2.0f*(q.y*q.y + q.z*q.z); 2153 | result.z = atan2f(z0, z1); 2154 | 2155 | return result; 2156 | } 2157 | 2158 | // Transform a quaternion given a transformation matrix 2159 | RMAPI Quaternion QuaternionTransform(Quaternion q, Matrix mat) 2160 | { 2161 | Quaternion result = { 0 }; 2162 | 2163 | result.x = mat.m0*q.x + mat.m4*q.y + mat.m8*q.z + mat.m12*q.w; 2164 | result.y = mat.m1*q.x + mat.m5*q.y + mat.m9*q.z + mat.m13*q.w; 2165 | result.z = mat.m2*q.x + mat.m6*q.y + mat.m10*q.z + mat.m14*q.w; 2166 | result.w = mat.m3*q.x + mat.m7*q.y + mat.m11*q.z + mat.m15*q.w; 2167 | 2168 | return result; 2169 | } 2170 | 2171 | // Check whether two given quaternions are almost equal 2172 | RMAPI int QuaternionEquals(Quaternion p, Quaternion q) 2173 | { 2174 | #if !defined(EPSILON) 2175 | #define EPSILON 0.000001f 2176 | #endif 2177 | 2178 | int result = (((fabsf(p.x - q.x)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.x), fabsf(q.x))))) && 2179 | ((fabsf(p.y - q.y)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.y), fabsf(q.y))))) && 2180 | ((fabsf(p.z - q.z)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.z), fabsf(q.z))))) && 2181 | ((fabsf(p.w - q.w)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.w), fabsf(q.w)))))) || 2182 | (((fabsf(p.x + q.x)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.x), fabsf(q.x))))) && 2183 | ((fabsf(p.y + q.y)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.y), fabsf(q.y))))) && 2184 | ((fabsf(p.z + q.z)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.z), fabsf(q.z))))) && 2185 | ((fabsf(p.w + q.w)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.w), fabsf(q.w)))))); 2186 | 2187 | return result; 2188 | } 2189 | 2190 | #endif // RAYMATH_H 2191 | -------------------------------------------------------------------------------- /raylib/raylib-5.0_linux_amd64/lib/libraylib.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsoding/secret/fdc592ceda00a9eb724aa19d7402de6606a76747/raylib/raylib-5.0_linux_amd64/lib/libraylib.a -------------------------------------------------------------------------------- /raylib/raylib-5.0_linux_amd64/lib/libraylib.so: -------------------------------------------------------------------------------- 1 | libraylib.so.500 -------------------------------------------------------------------------------- /raylib/raylib-5.0_linux_amd64/lib/libraylib.so.5.0.0: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsoding/secret/fdc592ceda00a9eb724aa19d7402de6606a76747/raylib/raylib-5.0_linux_amd64/lib/libraylib.so.5.0.0 -------------------------------------------------------------------------------- /raylib/raylib-5.0_linux_amd64/lib/libraylib.so.500: -------------------------------------------------------------------------------- 1 | libraylib.so.5.0.0 -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | #define NOB_IMPLEMENTATION 11 | #include "nob.h" 12 | #include "plug.h" 13 | 14 | static void *libplug = NULL; 15 | 16 | #define PLUG(name, ret, ...) static ret (*name)(__VA_ARGS__); 17 | LIST_OF_PLUGS 18 | #undef PLUG 19 | 20 | static bool reload_libplug(const char *libplug_path) 21 | { 22 | if (libplug != NULL) { 23 | dlclose(libplug); 24 | } 25 | 26 | libplug = dlopen(libplug_path, RTLD_NOW); 27 | if (libplug == NULL) { 28 | fprintf(stderr, "ERROR: %s\n", dlerror()); 29 | return false; 30 | } 31 | 32 | #define PLUG(name, ...) \ 33 | name = dlsym(libplug, #name); \ 34 | if (name == NULL) { \ 35 | fprintf(stderr, "ERROR: %s\n", dlerror()); \ 36 | return false; \ 37 | } 38 | LIST_OF_PLUGS 39 | #undef PLUG 40 | 41 | return true; 42 | } 43 | 44 | int main(int argc, char **argv) 45 | { 46 | const char *program_name = nob_shift_args(&argc, &argv); 47 | 48 | if (argc <= 0) { 49 | fprintf(stderr, "Usage: %s \n", program_name); 50 | fprintf(stderr, "ERROR: no animation dynamic library is provided\n"); 51 | return 1; 52 | } 53 | 54 | const char *libplug_path = nob_shift_args(&argc, &argv); 55 | 56 | if (!reload_libplug(libplug_path)) return 1; 57 | 58 | float factor = 100.0f; 59 | SetConfigFlags(FLAG_WINDOW_RESIZABLE); 60 | InitWindow(16*factor, 9*factor, "Secret"); 61 | InitAudioDevice(); 62 | SetTargetFPS(60); 63 | SetExitKey(KEY_NULL); 64 | plug_init(); 65 | 66 | while (!WindowShouldClose()) { 67 | BeginDrawing(); 68 | { 69 | if (IsKeyPressed(KEY_H)) { 70 | void *state = plug_pre_reload(); 71 | reload_libplug(libplug_path); 72 | plug_post_reload(state); 73 | } 74 | if (IsKeyPressed(KEY_Q)) { 75 | plug_reset(); 76 | } 77 | 78 | plug_update(); 79 | } 80 | EndDrawing(); 81 | } 82 | 83 | CloseWindow(); 84 | 85 | return 0; 86 | } 87 | -------------------------------------------------------------------------------- /src/nob.h: -------------------------------------------------------------------------------- 1 | // This is a complete backward incompatible rewrite of https://github.com/tsoding/nobuild 2 | // because I'm really unhappy with the direction it is going. It's gonna sit in this repo 3 | // until it's matured enough and then I'll probably extract it to its own repo. 4 | 5 | // Copyright 2023 Alexey Kutepov 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining 8 | // a copy of this software and associated documentation files (the 9 | // "Software"), to deal in the Software without restriction, including 10 | // without limitation the rights to use, copy, modify, merge, publish, 11 | // distribute, sublicense, and/or sell copies of the Software, and to 12 | // permit persons to whom the Software is furnished to do so, subject to 13 | // the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 22 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 24 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | 26 | #ifndef NOB_H_ 27 | #define NOB_H_ 28 | 29 | #define NOB_ASSERT assert 30 | #define NOB_REALLOC realloc 31 | #define NOB_FREE free 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | 42 | #ifdef _WIN32 43 | # define WIN32_LEAN_AND_MEAN 44 | # define _WINUSER_ 45 | # define _WINGDI_ 46 | # define _IMM_ 47 | # define _WINCON_ 48 | # include 49 | # include 50 | # include 51 | #else 52 | # include 53 | # include 54 | # include 55 | # include 56 | # include 57 | #endif 58 | 59 | #ifdef _WIN32 60 | # define NOB_LINE_END "\r\n" 61 | #else 62 | # define NOB_LINE_END "\n" 63 | #endif 64 | 65 | #define NOB_ARRAY_LEN(array) (sizeof(array)/sizeof(array[0])) 66 | #define NOB_ARRAY_GET(array, index) \ 67 | (NOB_ASSERT((size_t)index < NOB_ARRAY_LEN(array)), array[(size_t)index]) 68 | 69 | typedef enum { 70 | NOB_INFO, 71 | NOB_WARNING, 72 | NOB_ERROR, 73 | } Nob_Log_Level; 74 | 75 | void nob_log(Nob_Log_Level level, const char *fmt, ...); 76 | 77 | // It is an equivalent of shift command from bash. It basically pops a command line 78 | // argument from the beginning. 79 | char *nob_shift_args(int *argc, char ***argv); 80 | 81 | typedef struct { 82 | const char **items; 83 | size_t count; 84 | size_t capacity; 85 | } Nob_File_Paths; 86 | 87 | typedef enum { 88 | NOB_FILE_REGULAR = 0, 89 | NOB_FILE_DIRECTORY, 90 | NOB_FILE_SYMLINK, 91 | NOB_FILE_OTHER, 92 | } Nob_File_Type; 93 | 94 | bool nob_mkdir_if_not_exists(const char *path); 95 | bool nob_copy_file(const char *src_path, const char *dst_path); 96 | bool nob_copy_directory_recursively(const char *src_path, const char *dst_path); 97 | bool nob_read_entire_dir(const char *parent, Nob_File_Paths *children); 98 | bool nob_write_entire_file(const char *path, const void *data, size_t size); 99 | Nob_File_Type nob_get_file_type(const char *path); 100 | 101 | #define nob_return_defer(value) do { result = (value); goto defer; } while(0) 102 | 103 | // Initial capacity of a dynamic array 104 | #define NOB_DA_INIT_CAP 256 105 | 106 | // Append an item to a dynamic array 107 | #define nob_da_append(da, item) \ 108 | do { \ 109 | if ((da)->count >= (da)->capacity) { \ 110 | (da)->capacity = (da)->capacity == 0 ? NOB_DA_INIT_CAP : (da)->capacity*2; \ 111 | (da)->items = NOB_REALLOC((da)->items, (da)->capacity*sizeof(*(da)->items)); \ 112 | NOB_ASSERT((da)->items != NULL && "Buy more RAM lol"); \ 113 | } \ 114 | \ 115 | (da)->items[(da)->count++] = (item); \ 116 | } while (0) 117 | 118 | #define nob_da_free(da) NOB_FREE((da).items) 119 | 120 | // Append several items to a dynamic array 121 | #define nob_da_append_many(da, new_items, new_items_count) \ 122 | do { \ 123 | if ((da)->count + (new_items_count) > (da)->capacity) { \ 124 | if ((da)->capacity == 0) { \ 125 | (da)->capacity = NOB_DA_INIT_CAP; \ 126 | } \ 127 | while ((da)->count + (new_items_count) > (da)->capacity) { \ 128 | (da)->capacity *= 2; \ 129 | } \ 130 | (da)->items = NOB_REALLOC((da)->items, (da)->capacity*sizeof(*(da)->items)); \ 131 | NOB_ASSERT((da)->items != NULL && "Buy more RAM lol"); \ 132 | } \ 133 | memcpy((da)->items + (da)->count, (new_items), (new_items_count)*sizeof(*(da)->items)); \ 134 | (da)->count += (new_items_count); \ 135 | } while (0) 136 | 137 | typedef struct { 138 | char *items; 139 | size_t count; 140 | size_t capacity; 141 | } Nob_String_Builder; 142 | 143 | bool nob_read_entire_file(const char *path, Nob_String_Builder *sb); 144 | 145 | // Append a sized buffer to a string builder 146 | #define nob_sb_append_buf(sb, buf, size) nob_da_append_many(sb, buf, size) 147 | 148 | // Append a NULL-terminated string to a string builder 149 | #define nob_sb_append_cstr(sb, cstr) \ 150 | do { \ 151 | const char *s = (cstr); \ 152 | size_t n = strlen(s); \ 153 | nob_da_append_many(sb, s, n); \ 154 | } while (0) 155 | 156 | // Append a single NULL character at the end of a string builder. So then you can 157 | // use it a NULL-terminated C string 158 | #define nob_sb_append_null(sb) nob_da_append_many(sb, "", 1) 159 | 160 | // Free the memory allocated by a string builder 161 | #define nob_sb_free(sb) NOB_FREE((sb).items) 162 | 163 | // Process handle 164 | #ifdef _WIN32 165 | typedef HANDLE Nob_Proc; 166 | #define NOB_INVALID_PROC INVALID_HANDLE_VALUE 167 | #else 168 | typedef int Nob_Proc; 169 | #define NOB_INVALID_PROC (-1) 170 | #endif // _WIN32 171 | 172 | typedef struct { 173 | Nob_Proc *items; 174 | size_t count; 175 | size_t capacity; 176 | } Nob_Procs; 177 | 178 | bool nob_procs_wait(Nob_Procs procs); 179 | 180 | // Wait until the process has finished 181 | bool nob_proc_wait(Nob_Proc proc); 182 | 183 | // A command - the main workhorse of Nob. Nob is all about building commands an running them 184 | typedef struct { 185 | const char **items; 186 | size_t count; 187 | size_t capacity; 188 | } Nob_Cmd; 189 | 190 | // Render a string representation of a command into a string builder. Keep in mind the the 191 | // string builder is not NULL-terminated by default. Use nob_sb_append_null if you plan to 192 | // use it as a C string. 193 | void nob_cmd_render(Nob_Cmd cmd, Nob_String_Builder *render); 194 | 195 | #define nob_cmd_append(cmd, ...) \ 196 | nob_da_append_many(cmd, \ 197 | ((const char*[]){__VA_ARGS__}), \ 198 | (sizeof((const char*[]){__VA_ARGS__})/sizeof(const char*))) 199 | 200 | // Free all the memory allocated by command arguments 201 | #define nob_cmd_free(cmd) NOB_FREE(cmd.items) 202 | 203 | // Run command asynchronously 204 | Nob_Proc nob_cmd_run_async(Nob_Cmd cmd); 205 | 206 | // Run command synchronously 207 | bool nob_cmd_run_sync(Nob_Cmd cmd); 208 | 209 | #ifndef NOB_TEMP_CAPACITY 210 | #define NOB_TEMP_CAPACITY (8*1024*1024) 211 | #endif // NOB_TEMP_CAPACITY 212 | char *nob_temp_strdup(const char *cstr); 213 | void *nob_temp_alloc(size_t size); 214 | char *nob_temp_sprintf(const char *format, ...); 215 | void nob_temp_reset(void); 216 | size_t nob_temp_save(void); 217 | void nob_temp_rewind(size_t checkpoint); 218 | 219 | int is_path1_modified_after_path2(const char *path1, const char *path2); 220 | bool nob_rename(const char *old_path, const char *new_path); 221 | int nob_needs_rebuild(const char *output_path, const char **input_paths, size_t input_paths_count); 222 | int nob_needs_rebuild1(const char *output_path, const char *input_path); 223 | int nob_file_exists(const char *file_path); 224 | 225 | // TODO: add MinGW support for Go Rebuild Urself™ Technology 226 | #ifndef NOB_REBUILD_URSELF 227 | # if _WIN32 228 | # if defined(__GNUC__) 229 | # define NOB_REBUILD_URSELF(binary_path, source_path) "gcc", "-o", binary_path, source_path 230 | # elif defined(__clang__) 231 | # define NOB_REBUILD_URSELF(binary_path, source_path) "clang", "-o", binary_path, source_path 232 | # elif defined(_MSC_VER) 233 | # define NOB_REBUILD_URSELF(binary_path, source_path) "cl.exe", nob_temp_sprintf("/Fe:%s", (binary_path)), source_path 234 | # endif 235 | # else 236 | # define NOB_REBUILD_URSELF(binary_path, source_path) "cc", "-o", binary_path, source_path 237 | # endif 238 | #endif 239 | 240 | // Go Rebuild Urself™ Technology 241 | // 242 | // How to use it: 243 | // int main(int argc, char** argv) { 244 | // GO_REBUILD_URSELF(argc, argv); 245 | // // actual work 246 | // return 0; 247 | // } 248 | // 249 | // After your added this macro every time you run ./nobuild it will detect 250 | // that you modified its original source code and will try to rebuild itself 251 | // before doing any actual work. So you only need to bootstrap your build system 252 | // once. 253 | // 254 | // The modification is detected by comparing the last modified times of the executable 255 | // and its source code. The same way the make utility usually does it. 256 | // 257 | // The rebuilding is done by using the REBUILD_URSELF macro which you can redefine 258 | // if you need a special way of bootstraping your build system. (which I personally 259 | // do not recommend since the whole idea of nobuild is to keep the process of bootstrapping 260 | // as simple as possible and doing all of the actual work inside of the nobuild) 261 | // 262 | #define NOB_GO_REBUILD_URSELF(argc, argv) \ 263 | do { \ 264 | const char *source_path = __FILE__; \ 265 | assert(argc >= 1); \ 266 | const char *binary_path = argv[0]; \ 267 | \ 268 | int rebuild_is_needed = nob_needs_rebuild(binary_path, &source_path, 1); \ 269 | if (rebuild_is_needed < 0) exit(1); \ 270 | if (rebuild_is_needed) { \ 271 | Nob_String_Builder sb = {0}; \ 272 | nob_sb_append_cstr(&sb, binary_path); \ 273 | nob_sb_append_cstr(&sb, ".old"); \ 274 | nob_sb_append_null(&sb); \ 275 | \ 276 | if (!nob_rename(binary_path, sb.items)) exit(1); \ 277 | Nob_Cmd rebuild = {0}; \ 278 | nob_cmd_append(&rebuild, NOB_REBUILD_URSELF(binary_path, source_path)); \ 279 | bool rebuild_succeeded = nob_cmd_run_sync(rebuild); \ 280 | nob_cmd_free(rebuild); \ 281 | if (!rebuild_succeeded) { \ 282 | nob_rename(sb.items, binary_path); \ 283 | exit(1); \ 284 | } \ 285 | \ 286 | Nob_Cmd cmd = {0}; \ 287 | nob_da_append_many(&cmd, argv, argc); \ 288 | if (!nob_cmd_run_sync(cmd)) exit(1); \ 289 | exit(0); \ 290 | } \ 291 | } while(0) 292 | // The implementation idea is stolen from https://github.com/zhiayang/nabs 293 | 294 | typedef struct { 295 | size_t count; 296 | const char *data; 297 | } Nob_String_View; 298 | 299 | const char *nob_temp_sv_to_cstr(Nob_String_View sv); 300 | 301 | Nob_String_View nob_sv_chop_by_delim(Nob_String_View *sv, char delim); 302 | Nob_String_View nob_sv_trim(Nob_String_View sv); 303 | bool nob_sv_eq(Nob_String_View a, Nob_String_View b); 304 | Nob_String_View nob_sv_from_cstr(const char *cstr); 305 | Nob_String_View nob_sv_from_parts(const char *data, size_t count); 306 | 307 | // printf macros for String_View 308 | #ifndef SV_Fmt 309 | #define SV_Fmt "%.*s" 310 | #endif // SV_Fmt 311 | #ifndef SV_Arg 312 | #define SV_Arg(sv) (int) (sv).count, (sv).data 313 | #endif // SV_Arg 314 | // USAGE: 315 | // String_View name = ...; 316 | // printf("Name: "SV_Fmt"\n", SV_Arg(name)); 317 | 318 | 319 | // minirent.h HEADER BEGIN //////////////////////////////////////// 320 | // Copyright 2021 Alexey Kutepov 321 | // 322 | // Permission is hereby granted, free of charge, to any person obtaining 323 | // a copy of this software and associated documentation files (the 324 | // "Software"), to deal in the Software without restriction, including 325 | // without limitation the rights to use, copy, modify, merge, publish, 326 | // distribute, sublicense, and/or sell copies of the Software, and to 327 | // permit persons to whom the Software is furnished to do so, subject to 328 | // the following conditions: 329 | // 330 | // The above copyright notice and this permission notice shall be 331 | // included in all copies or substantial portions of the Software. 332 | // 333 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 334 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 335 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 336 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 337 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 338 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 339 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 340 | // 341 | // ============================================================ 342 | // 343 | // minirent — 0.0.1 — A subset of dirent interface for Windows. 344 | // 345 | // https://github.com/tsoding/minirent 346 | // 347 | // ============================================================ 348 | // 349 | // ChangeLog (https://semver.org/ is implied) 350 | // 351 | // 0.0.2 Automatically include dirent.h on non-Windows 352 | // platforms 353 | // 0.0.1 First Official Release 354 | 355 | #ifndef _WIN32 356 | #include 357 | #else // _WIN32 358 | 359 | #define WIN32_LEAN_AND_MEAN 360 | #include "windows.h" 361 | 362 | struct dirent 363 | { 364 | char d_name[MAX_PATH+1]; 365 | }; 366 | 367 | typedef struct DIR DIR; 368 | 369 | static DIR *opendir(const char *dirpath); 370 | static struct dirent *readdir(DIR *dirp); 371 | static int closedir(DIR *dirp); 372 | #endif // _WIN32 373 | // minirent.h HEADER END //////////////////////////////////////// 374 | 375 | #endif // NOB_H_ 376 | 377 | #ifdef NOB_IMPLEMENTATION 378 | 379 | static size_t nob_temp_size = 0; 380 | static char nob_temp[NOB_TEMP_CAPACITY] = {0}; 381 | 382 | bool nob_mkdir_if_not_exists(const char *path) 383 | { 384 | #ifdef _WIN32 385 | int result = mkdir(path); 386 | #else 387 | int result = mkdir(path, 0755); 388 | #endif 389 | if (result < 0) { 390 | if (errno == EEXIST) { 391 | nob_log(NOB_INFO, "directory `%s` already exists", path); 392 | return true; 393 | } 394 | nob_log(NOB_ERROR, "could not create directory `%s`: %s", path, strerror(errno)); 395 | return false; 396 | } 397 | 398 | nob_log(NOB_INFO, "created directory `%s`", path); 399 | return true; 400 | } 401 | 402 | bool nob_copy_file(const char *src_path, const char *dst_path) 403 | { 404 | nob_log(NOB_INFO, "copying %s -> %s", src_path, dst_path); 405 | #ifdef _WIN32 406 | if (!CopyFile(src_path, dst_path, FALSE)) { 407 | nob_log(NOB_ERROR, "Could not copy file: %lu", GetLastError()); 408 | return false; 409 | } 410 | return true; 411 | #else 412 | int src_fd = -1; 413 | int dst_fd = -1; 414 | size_t buf_size = 32*1024; 415 | char *buf = NOB_REALLOC(NULL, buf_size); 416 | NOB_ASSERT(buf != NULL && "Buy more RAM lol!!"); 417 | bool result = true; 418 | 419 | src_fd = open(src_path, O_RDONLY); 420 | if (src_fd < 0) { 421 | nob_log(NOB_ERROR, "Could not open file %s: %s", src_path, strerror(errno)); 422 | nob_return_defer(false); 423 | } 424 | 425 | struct stat src_stat; 426 | if (fstat(src_fd, &src_stat) < 0) { 427 | nob_log(NOB_ERROR, "Could not get mode of file %s: %s", src_path, strerror(errno)); 428 | nob_return_defer(false); 429 | } 430 | 431 | dst_fd = open(dst_path, O_CREAT | O_TRUNC | O_WRONLY, src_stat.st_mode); 432 | if (dst_fd < 0) { 433 | nob_log(NOB_ERROR, "Could not create file %s: %s", dst_path, strerror(errno)); 434 | nob_return_defer(false); 435 | } 436 | 437 | for (;;) { 438 | ssize_t n = read(src_fd, buf, buf_size); 439 | if (n == 0) break; 440 | if (n < 0) { 441 | nob_log(NOB_ERROR, "Could not read from file %s: %s", src_path, strerror(errno)); 442 | nob_return_defer(false); 443 | } 444 | char *buf2 = buf; 445 | while (n > 0) { 446 | ssize_t m = write(dst_fd, buf2, n); 447 | if (m < 0) { 448 | nob_log(NOB_ERROR, "Could not write to file %s: %s", dst_path, strerror(errno)); 449 | nob_return_defer(false); 450 | } 451 | n -= m; 452 | buf2 += m; 453 | } 454 | } 455 | 456 | defer: 457 | free(buf); 458 | close(src_fd); 459 | close(dst_fd); 460 | return result; 461 | #endif 462 | } 463 | 464 | void nob_cmd_render(Nob_Cmd cmd, Nob_String_Builder *render) 465 | { 466 | for (size_t i = 0; i < cmd.count; ++i) { 467 | const char *arg = cmd.items[i]; 468 | if (arg == NULL) break; 469 | if (i > 0) nob_sb_append_cstr(render, " "); 470 | if (!strchr(arg, ' ')) { 471 | nob_sb_append_cstr(render, arg); 472 | } else { 473 | nob_da_append(render, '\''); 474 | nob_sb_append_cstr(render, arg); 475 | nob_da_append(render, '\''); 476 | } 477 | } 478 | } 479 | 480 | Nob_Proc nob_cmd_run_async(Nob_Cmd cmd) 481 | { 482 | if (cmd.count < 1) { 483 | nob_log(NOB_ERROR, "Could not run empty command"); 484 | return NOB_INVALID_PROC; 485 | } 486 | 487 | Nob_String_Builder sb = {0}; 488 | nob_cmd_render(cmd, &sb); 489 | nob_sb_append_null(&sb); 490 | nob_log(NOB_INFO, "CMD: %s", sb.items); 491 | nob_sb_free(sb); 492 | memset(&sb, 0, sizeof(sb)); 493 | 494 | #ifdef _WIN32 495 | // https://docs.microsoft.com/en-us/windows/win32/procthread/creating-a-child-process-with-redirected-input-and-output 496 | 497 | STARTUPINFO siStartInfo; 498 | ZeroMemory(&siStartInfo, sizeof(siStartInfo)); 499 | siStartInfo.cb = sizeof(STARTUPINFO); 500 | // NOTE: theoretically setting NULL to std handles should not be a problem 501 | // https://docs.microsoft.com/en-us/windows/console/getstdhandle?redirectedfrom=MSDN#attachdetach-behavior 502 | // TODO: check for errors in GetStdHandle 503 | siStartInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE); 504 | siStartInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); 505 | siStartInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE); 506 | siStartInfo.dwFlags |= STARTF_USESTDHANDLES; 507 | 508 | PROCESS_INFORMATION piProcInfo; 509 | ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION)); 510 | 511 | // TODO: use a more reliable rendering of the command instead of cmd_render 512 | // cmd_render is for logging primarily 513 | nob_cmd_render(cmd, &sb); 514 | nob_sb_append_null(&sb); 515 | BOOL bSuccess = CreateProcessA(NULL, sb.items, NULL, NULL, TRUE, 0, NULL, NULL, &siStartInfo, &piProcInfo); 516 | nob_sb_free(sb); 517 | 518 | if (!bSuccess) { 519 | nob_log(NOB_ERROR, "Could not create child process: %lu", GetLastError()); 520 | return NOB_INVALID_PROC; 521 | } 522 | 523 | CloseHandle(piProcInfo.hThread); 524 | 525 | return piProcInfo.hProcess; 526 | #else 527 | pid_t cpid = fork(); 528 | if (cpid < 0) { 529 | nob_log(NOB_ERROR, "Could not fork child process: %s", strerror(errno)); 530 | return NOB_INVALID_PROC; 531 | } 532 | 533 | if (cpid == 0) { 534 | // NOTE: This leaks a bit of memory in the child process. 535 | // But do we actually care? It's a one off leak anyway... 536 | Nob_Cmd cmd_null = {0}; 537 | nob_da_append_many(&cmd_null, cmd.items, cmd.count); 538 | nob_cmd_append(&cmd_null, NULL); 539 | 540 | if (execvp(cmd.items[0], (char * const*) cmd_null.items) < 0) { 541 | nob_log(NOB_ERROR, "Could not exec child process: %s", strerror(errno)); 542 | exit(1); 543 | } 544 | NOB_ASSERT(0 && "unreachable"); 545 | } 546 | 547 | return cpid; 548 | #endif 549 | } 550 | 551 | bool nob_procs_wait(Nob_Procs procs) 552 | { 553 | bool success = true; 554 | for (size_t i = 0; i < procs.count; ++i) { 555 | success = nob_proc_wait(procs.items[i]) && success; 556 | } 557 | return success; 558 | } 559 | 560 | bool nob_proc_wait(Nob_Proc proc) 561 | { 562 | if (proc == NOB_INVALID_PROC) return false; 563 | 564 | #ifdef _WIN32 565 | DWORD result = WaitForSingleObject( 566 | proc, // HANDLE hHandle, 567 | INFINITE // DWORD dwMilliseconds 568 | ); 569 | 570 | if (result == WAIT_FAILED) { 571 | nob_log(NOB_ERROR, "could not wait on child process: %lu", GetLastError()); 572 | return false; 573 | } 574 | 575 | DWORD exit_status; 576 | if (!GetExitCodeProcess(proc, &exit_status)) { 577 | nob_log(NOB_ERROR, "could not get process exit code: %lu", GetLastError()); 578 | return false; 579 | } 580 | 581 | if (exit_status != 0) { 582 | nob_log(NOB_ERROR, "command exited with exit code %lu", exit_status); 583 | return false; 584 | } 585 | 586 | CloseHandle(proc); 587 | 588 | return true; 589 | #else 590 | for (;;) { 591 | int wstatus = 0; 592 | if (waitpid(proc, &wstatus, 0) < 0) { 593 | nob_log(NOB_ERROR, "could not wait on command (pid %d): %s", proc, strerror(errno)); 594 | return false; 595 | } 596 | 597 | if (WIFEXITED(wstatus)) { 598 | int exit_status = WEXITSTATUS(wstatus); 599 | if (exit_status != 0) { 600 | nob_log(NOB_ERROR, "command exited with exit code %d", exit_status); 601 | return false; 602 | } 603 | 604 | break; 605 | } 606 | 607 | if (WIFSIGNALED(wstatus)) { 608 | nob_log(NOB_ERROR, "command process was terminated by %s", strsignal(WTERMSIG(wstatus))); 609 | return false; 610 | } 611 | } 612 | 613 | return true; 614 | #endif 615 | } 616 | 617 | bool nob_cmd_run_sync(Nob_Cmd cmd) 618 | { 619 | Nob_Proc p = nob_cmd_run_async(cmd); 620 | if (p == NOB_INVALID_PROC) return false; 621 | return nob_proc_wait(p); 622 | } 623 | 624 | char *nob_shift_args(int *argc, char ***argv) 625 | { 626 | NOB_ASSERT(*argc > 0); 627 | char *result = **argv; 628 | (*argv) += 1; 629 | (*argc) -= 1; 630 | return result; 631 | } 632 | 633 | void nob_log(Nob_Log_Level level, const char *fmt, ...) 634 | { 635 | switch (level) { 636 | case NOB_INFO: 637 | fprintf(stderr, "[INFO] "); 638 | break; 639 | case NOB_WARNING: 640 | fprintf(stderr, "[WARNING] "); 641 | break; 642 | case NOB_ERROR: 643 | fprintf(stderr, "[ERROR] "); 644 | break; 645 | default: 646 | NOB_ASSERT(0 && "unreachable"); 647 | } 648 | 649 | va_list args; 650 | va_start(args, fmt); 651 | vfprintf(stderr, fmt, args); 652 | va_end(args); 653 | fprintf(stderr, "\n"); 654 | } 655 | 656 | bool nob_read_entire_dir(const char *parent, Nob_File_Paths *children) 657 | { 658 | bool result = true; 659 | DIR *dir = NULL; 660 | 661 | dir = opendir(parent); 662 | if (dir == NULL) { 663 | nob_log(NOB_ERROR, "Could not open directory %s: %s", parent, strerror(errno)); 664 | nob_return_defer(false); 665 | } 666 | 667 | errno = 0; 668 | struct dirent *ent = readdir(dir); 669 | while (ent != NULL) { 670 | nob_da_append(children, nob_temp_strdup(ent->d_name)); 671 | ent = readdir(dir); 672 | } 673 | 674 | if (errno != 0) { 675 | nob_log(NOB_ERROR, "Could not read directory %s: %s", parent, strerror(errno)); 676 | nob_return_defer(false); 677 | } 678 | 679 | defer: 680 | if (dir) closedir(dir); 681 | return result; 682 | } 683 | 684 | bool nob_write_entire_file(const char *path, const void *data, size_t size) 685 | { 686 | bool result = true; 687 | 688 | FILE *f = fopen(path, "wb"); 689 | if (f == NULL) { 690 | nob_log(NOB_ERROR, "Could not open file %s for writing: %s\n", path, strerror(errno)); 691 | nob_return_defer(false); 692 | } 693 | 694 | // len 695 | // v 696 | // aaaaaaaaaa 697 | // ^ 698 | // data 699 | 700 | const char *buf = data; 701 | while (size > 0) { 702 | size_t n = fwrite(buf, 1, size, f); 703 | if (ferror(f)) { 704 | nob_log(NOB_ERROR, "Could not write into file %s: %s\n", path, strerror(errno)); 705 | nob_return_defer(false); 706 | } 707 | size -= n; 708 | buf += n; 709 | } 710 | 711 | defer: 712 | if (f) fclose(f); 713 | return result; 714 | } 715 | 716 | Nob_File_Type nob_get_file_type(const char *path) 717 | { 718 | #ifdef _WIN32 719 | DWORD attr = GetFileAttributesA(path); 720 | if (attr == INVALID_FILE_ATTRIBUTES) { 721 | nob_log(NOB_ERROR, "Could not get file attributes of %s: %lu", path, GetLastError()); 722 | return -1; 723 | } 724 | 725 | if (attr & FILE_ATTRIBUTE_DIRECTORY) return NOB_FILE_DIRECTORY; 726 | // TODO: detect symlinks on Windows (whatever that means on Windows anyway) 727 | return NOB_FILE_REGULAR; 728 | #else // _WIN32 729 | struct stat statbuf; 730 | if (stat(path, &statbuf) < 0) { 731 | nob_log(NOB_ERROR, "Could not get stat of %s: %s", path, strerror(errno)); 732 | return -1; 733 | } 734 | 735 | switch (statbuf.st_mode & S_IFMT) { 736 | case S_IFDIR: return NOB_FILE_DIRECTORY; 737 | case S_IFREG: return NOB_FILE_REGULAR; 738 | case S_IFLNK: return NOB_FILE_SYMLINK; 739 | default: return NOB_FILE_OTHER; 740 | } 741 | #endif // _WIN32 742 | } 743 | 744 | bool nob_copy_directory_recursively(const char *src_path, const char *dst_path) 745 | { 746 | bool result = true; 747 | Nob_File_Paths children = {0}; 748 | Nob_String_Builder src_sb = {0}; 749 | Nob_String_Builder dst_sb = {0}; 750 | size_t temp_checkpoint = nob_temp_save(); 751 | 752 | Nob_File_Type type = nob_get_file_type(src_path); 753 | if (type < 0) return false; 754 | 755 | switch (type) { 756 | case NOB_FILE_DIRECTORY: { 757 | if (!nob_mkdir_if_not_exists(dst_path)) nob_return_defer(false); 758 | if (!nob_read_entire_dir(src_path, &children)) nob_return_defer(false); 759 | 760 | for (size_t i = 0; i < children.count; ++i) { 761 | if (strcmp(children.items[i], ".") == 0) continue; 762 | if (strcmp(children.items[i], "..") == 0) continue; 763 | 764 | src_sb.count = 0; 765 | nob_sb_append_cstr(&src_sb, src_path); 766 | nob_sb_append_cstr(&src_sb, "/"); 767 | nob_sb_append_cstr(&src_sb, children.items[i]); 768 | nob_sb_append_null(&src_sb); 769 | 770 | dst_sb.count = 0; 771 | nob_sb_append_cstr(&dst_sb, dst_path); 772 | nob_sb_append_cstr(&dst_sb, "/"); 773 | nob_sb_append_cstr(&dst_sb, children.items[i]); 774 | nob_sb_append_null(&dst_sb); 775 | 776 | if (!nob_copy_directory_recursively(src_sb.items, dst_sb.items)) { 777 | nob_return_defer(false); 778 | } 779 | } 780 | } break; 781 | 782 | case NOB_FILE_REGULAR: { 783 | if (!nob_copy_file(src_path, dst_path)) { 784 | nob_return_defer(false); 785 | } 786 | } break; 787 | 788 | case NOB_FILE_SYMLINK: { 789 | nob_log(NOB_WARNING, "TODO: Copying symlinks is not supported yet"); 790 | } break; 791 | 792 | case NOB_FILE_OTHER: { 793 | nob_log(NOB_ERROR, "Unsupported type of file %s", src_path); 794 | nob_return_defer(false); 795 | } break; 796 | 797 | default: NOB_ASSERT(0 && "unreachable"); 798 | } 799 | 800 | defer: 801 | nob_temp_rewind(temp_checkpoint); 802 | nob_da_free(src_sb); 803 | nob_da_free(dst_sb); 804 | nob_da_free(children); 805 | return result; 806 | } 807 | 808 | char *nob_temp_strdup(const char *cstr) 809 | { 810 | size_t n = strlen(cstr); 811 | char *result = nob_temp_alloc(n + 1); 812 | NOB_ASSERT(result != NULL && "Increase NOB_TEMP_CAPACITY"); 813 | memcpy(result, cstr, n); 814 | result[n] = '\0'; 815 | return result; 816 | } 817 | 818 | void *nob_temp_alloc(size_t size) 819 | { 820 | if (nob_temp_size + size > NOB_TEMP_CAPACITY) return NULL; 821 | void *result = &nob_temp[nob_temp_size]; 822 | nob_temp_size += size; 823 | return result; 824 | } 825 | 826 | char *nob_temp_sprintf(const char *format, ...) 827 | { 828 | va_list args; 829 | va_start(args, format); 830 | int n = vsnprintf(NULL, 0, format, args); 831 | va_end(args); 832 | 833 | NOB_ASSERT(n >= 0); 834 | char *result = nob_temp_alloc(n + 1); 835 | NOB_ASSERT(result != NULL && "Extend the size of the temporary allocator"); 836 | // TODO: use proper arenas for the temporary allocator; 837 | va_start(args, format); 838 | vsnprintf(result, n + 1, format, args); 839 | va_end(args); 840 | 841 | return result; 842 | } 843 | 844 | void nob_temp_reset(void) 845 | { 846 | nob_temp_size = 0; 847 | } 848 | 849 | size_t nob_temp_save(void) 850 | { 851 | return nob_temp_size; 852 | } 853 | 854 | void nob_temp_rewind(size_t checkpoint) 855 | { 856 | nob_temp_size = checkpoint; 857 | } 858 | 859 | const char *nob_temp_sv_to_cstr(Nob_String_View sv) 860 | { 861 | char *result = nob_temp_alloc(sv.count + 1); 862 | NOB_ASSERT(result != NULL && "Extend the size of the temporary allocator"); 863 | memcpy(result, sv.data, sv.count); 864 | result[sv.count] = '\0'; 865 | return result; 866 | } 867 | 868 | int nob_needs_rebuild(const char *output_path, const char **input_paths, size_t input_paths_count) 869 | { 870 | #ifdef _WIN32 871 | BOOL bSuccess; 872 | 873 | HANDLE output_path_fd = CreateFile(output_path, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL); 874 | if (output_path_fd == INVALID_HANDLE_VALUE) { 875 | // NOTE: if output does not exist it 100% must be rebuilt 876 | if (GetLastError() == ERROR_FILE_NOT_FOUND) return 1; 877 | nob_log(NOB_ERROR, "Could not open file %s: %lu", output_path, GetLastError()); 878 | return -1; 879 | } 880 | FILETIME output_path_time; 881 | bSuccess = GetFileTime(output_path_fd, NULL, NULL, &output_path_time); 882 | CloseHandle(output_path_fd); 883 | if (!bSuccess) { 884 | nob_log(NOB_ERROR, "Could not get time of %s: %lu", output_path, GetLastError()); 885 | return -1; 886 | } 887 | 888 | for (size_t i = 0; i < input_paths_count; ++i) { 889 | const char *input_path = input_paths[i]; 890 | HANDLE input_path_fd = CreateFile(input_path, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL); 891 | if (input_path_fd == INVALID_HANDLE_VALUE) { 892 | // NOTE: non-existing input is an error cause it is needed for building in the first place 893 | nob_log(NOB_ERROR, "Could not open file %s: %lu", input_path, GetLastError()); 894 | return -1; 895 | } 896 | FILETIME input_path_time; 897 | bSuccess = GetFileTime(input_path_fd, NULL, NULL, &input_path_time); 898 | CloseHandle(input_path_fd); 899 | if (!bSuccess) { 900 | nob_log(NOB_ERROR, "Could not get time of %s: %lu", input_path, GetLastError()); 901 | return -1; 902 | } 903 | 904 | // NOTE: if even a single input_path is fresher than output_path that's 100% rebuild 905 | if (CompareFileTime(&input_path_time, &output_path_time) == 1) return 1; 906 | } 907 | 908 | return 0; 909 | #else 910 | struct stat statbuf = {0}; 911 | 912 | if (stat(output_path, &statbuf) < 0) { 913 | // NOTE: if output does not exist it 100% must be rebuilt 914 | if (errno == ENOENT) return 1; 915 | nob_log(NOB_ERROR, "could not stat %s: %s", output_path, strerror(errno)); 916 | return -1; 917 | } 918 | int output_path_time = statbuf.st_mtime; 919 | 920 | for (size_t i = 0; i < input_paths_count; ++i) { 921 | const char *input_path = input_paths[i]; 922 | if (stat(input_path, &statbuf) < 0) { 923 | // NOTE: non-existing input is an error cause it is needed for building in the first place 924 | nob_log(NOB_ERROR, "could not stat %s: %s", input_path, strerror(errno)); 925 | return -1; 926 | } 927 | int input_path_time = statbuf.st_mtime; 928 | // NOTE: if even a single input_path is fresher than output_path that's 100% rebuild 929 | if (input_path_time > output_path_time) return 1; 930 | } 931 | 932 | return 0; 933 | #endif 934 | } 935 | 936 | int nob_needs_rebuild1(const char *output_path, const char *input_path) 937 | { 938 | return nob_needs_rebuild(output_path, &input_path, 1); 939 | } 940 | 941 | bool nob_rename(const char *old_path, const char *new_path) 942 | { 943 | nob_log(NOB_INFO, "renaming %s -> %s", old_path, new_path); 944 | #ifdef _WIN32 945 | if (!MoveFileEx(old_path, new_path, MOVEFILE_REPLACE_EXISTING)) { 946 | nob_log(NOB_ERROR, "could not rename %s to %s: %lu", old_path, new_path, GetLastError()); 947 | return false; 948 | } 949 | #else 950 | if (rename(old_path, new_path) < 0) { 951 | nob_log(NOB_ERROR, "could not rename %s to %s: %s", old_path, new_path, strerror(errno)); 952 | return false; 953 | } 954 | #endif // _WIN32 955 | return true; 956 | } 957 | 958 | bool nob_read_entire_file(const char *path, Nob_String_Builder *sb) 959 | { 960 | bool result = true; 961 | 962 | FILE *f = fopen(path, "rb"); 963 | if (f == NULL) nob_return_defer(false); 964 | if (fseek(f, 0, SEEK_END) < 0) nob_return_defer(false); 965 | long m = ftell(f); 966 | if (m < 0) nob_return_defer(false); 967 | if (fseek(f, 0, SEEK_SET) < 0) nob_return_defer(false); 968 | 969 | size_t new_count = sb->count + m; 970 | if (new_count > sb->capacity) { 971 | sb->items = realloc(sb->items, new_count); 972 | NOB_ASSERT(sb->items != NULL && "Buy more RAM lool!!"); 973 | sb->capacity = new_count; 974 | } 975 | 976 | fread(sb->items + sb->count, m, 1, f); 977 | if (ferror(f)) { 978 | // TODO: Afaik, ferror does not set errno. So the error reporting in defer is not correct in this case. 979 | nob_return_defer(false); 980 | } 981 | sb->count = new_count; 982 | 983 | defer: 984 | if (!result) nob_log(NOB_ERROR, "Could not read file %s: %s", path, strerror(errno)); 985 | if (f) fclose(f); 986 | return result; 987 | } 988 | 989 | Nob_String_View nob_sv_chop_by_delim(Nob_String_View *sv, char delim) 990 | { 991 | size_t i = 0; 992 | while (i < sv->count && sv->data[i] != delim) { 993 | i += 1; 994 | } 995 | 996 | Nob_String_View result = nob_sv_from_parts(sv->data, i); 997 | 998 | if (i < sv->count) { 999 | sv->count -= i + 1; 1000 | sv->data += i + 1; 1001 | } else { 1002 | sv->count -= i; 1003 | sv->data += i; 1004 | } 1005 | 1006 | return result; 1007 | } 1008 | 1009 | Nob_String_View nob_sv_from_parts(const char *data, size_t count) 1010 | { 1011 | Nob_String_View sv; 1012 | sv.count = count; 1013 | sv.data = data; 1014 | return sv; 1015 | } 1016 | 1017 | Nob_String_View nob_sv_trim_left(Nob_String_View sv) 1018 | { 1019 | size_t i = 0; 1020 | while (i < sv.count && isspace(sv.data[i])) { 1021 | i += 1; 1022 | } 1023 | 1024 | return nob_sv_from_parts(sv.data + i, sv.count - i); 1025 | } 1026 | 1027 | Nob_String_View nob_sv_trim_right(Nob_String_View sv) 1028 | { 1029 | size_t i = 0; 1030 | while (i < sv.count && isspace(sv.data[sv.count - 1 - i])) { 1031 | i += 1; 1032 | } 1033 | 1034 | return nob_sv_from_parts(sv.data, sv.count - i); 1035 | } 1036 | 1037 | Nob_String_View nob_sv_trim(Nob_String_View sv) 1038 | { 1039 | return nob_sv_trim_right(nob_sv_trim_left(sv)); 1040 | } 1041 | 1042 | Nob_String_View nob_sv_from_cstr(const char *cstr) 1043 | { 1044 | return nob_sv_from_parts(cstr, strlen(cstr)); 1045 | } 1046 | 1047 | bool nob_sv_eq(Nob_String_View a, Nob_String_View b) 1048 | { 1049 | if (a.count != b.count) { 1050 | return false; 1051 | } else { 1052 | return memcmp(a.data, b.data, a.count) == 0; 1053 | } 1054 | } 1055 | 1056 | // RETURNS: 1057 | // 0 - file does not exists 1058 | // 1 - file exists 1059 | // -1 - error while checking if file exists. The error is logged 1060 | int nob_file_exists(const char *file_path) 1061 | { 1062 | #if _WIN32 1063 | // TODO: distinguish between "does not exists" and other errors 1064 | DWORD dwAttrib = GetFileAttributesA(file_path); 1065 | return dwAttrib != INVALID_FILE_ATTRIBUTES; 1066 | #else 1067 | struct stat statbuf; 1068 | if (stat(file_path, &statbuf) < 0) { 1069 | if (errno == ENOENT) return 0; 1070 | nob_log(NOB_ERROR, "Could not check if file %s exists: %s", file_path, strerror(errno)); 1071 | return -1; 1072 | } 1073 | return 1; 1074 | #endif 1075 | } 1076 | 1077 | // minirent.h SOURCE BEGIN //////////////////////////////////////// 1078 | #ifdef _WIN32 1079 | struct DIR 1080 | { 1081 | HANDLE hFind; 1082 | WIN32_FIND_DATA data; 1083 | struct dirent *dirent; 1084 | }; 1085 | 1086 | DIR *opendir(const char *dirpath) 1087 | { 1088 | assert(dirpath); 1089 | 1090 | char buffer[MAX_PATH]; 1091 | snprintf(buffer, MAX_PATH, "%s\\*", dirpath); 1092 | 1093 | DIR *dir = (DIR*)calloc(1, sizeof(DIR)); 1094 | 1095 | dir->hFind = FindFirstFile(buffer, &dir->data); 1096 | if (dir->hFind == INVALID_HANDLE_VALUE) { 1097 | // TODO: opendir should set errno accordingly on FindFirstFile fail 1098 | // https://docs.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-getlasterror 1099 | errno = ENOSYS; 1100 | goto fail; 1101 | } 1102 | 1103 | return dir; 1104 | 1105 | fail: 1106 | if (dir) { 1107 | free(dir); 1108 | } 1109 | 1110 | return NULL; 1111 | } 1112 | 1113 | struct dirent *readdir(DIR *dirp) 1114 | { 1115 | assert(dirp); 1116 | 1117 | if (dirp->dirent == NULL) { 1118 | dirp->dirent = (struct dirent*)calloc(1, sizeof(struct dirent)); 1119 | } else { 1120 | if(!FindNextFile(dirp->hFind, &dirp->data)) { 1121 | if (GetLastError() != ERROR_NO_MORE_FILES) { 1122 | // TODO: readdir should set errno accordingly on FindNextFile fail 1123 | // https://docs.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-getlasterror 1124 | errno = ENOSYS; 1125 | } 1126 | 1127 | return NULL; 1128 | } 1129 | } 1130 | 1131 | memset(dirp->dirent->d_name, 0, sizeof(dirp->dirent->d_name)); 1132 | 1133 | strncpy( 1134 | dirp->dirent->d_name, 1135 | dirp->data.cFileName, 1136 | sizeof(dirp->dirent->d_name) - 1); 1137 | 1138 | return dirp->dirent; 1139 | } 1140 | 1141 | int closedir(DIR *dirp) 1142 | { 1143 | assert(dirp); 1144 | 1145 | if(!FindClose(dirp->hFind)) { 1146 | // TODO: closedir should set errno accordingly on FindClose fail 1147 | // https://docs.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-getlasterror 1148 | errno = ENOSYS; 1149 | return -1; 1150 | } 1151 | 1152 | if (dirp->dirent) { 1153 | free(dirp->dirent); 1154 | } 1155 | free(dirp); 1156 | 1157 | return 0; 1158 | } 1159 | #endif // _WIN32 1160 | // minirent.h SOURCE END //////////////////////////////////////// 1161 | 1162 | #endif 1163 | -------------------------------------------------------------------------------- /src/plug.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | #include "plug.h" 8 | #include "nob.h" 9 | 10 | #define PLUG(name, ret, ...) ret name(__VA_ARGS__); 11 | LIST_OF_PLUGS 12 | #undef PLUG 13 | 14 | #define NODE_RADIUS 3 15 | #define NODES_BIN_PATH "./assets/data/nodes.bin" 16 | #define GRID_ROWS 22 17 | #define GRID_COLS 18 18 | #define SAMPLE_COUNT 20 19 | #define SAMPLE_AREA 0.08 20 | #define SAMPLE_THRESHOLD 15 21 | #define BITS_COUNT 6 22 | 23 | typedef struct { 24 | float *items; 25 | int width, height, stride; 26 | } Mat; 27 | 28 | typedef struct { 29 | size_t size; 30 | Image image; 31 | Texture2D texture; 32 | float zoom; 33 | bool dragging; 34 | Vector2 target; 35 | Vector2 anchor; 36 | Vector2 nodes[4]; 37 | Mat lum; 38 | Mat grad; 39 | } Plug; 40 | 41 | static Plug *p; 42 | 43 | #define MAT_AT(mat, row, col) (mat).items[(row)*(mat).stride + (col)] 44 | 45 | static Mat mat_alloc(int width, int height) 46 | { 47 | Mat mat = {0}; 48 | mat.items = malloc(sizeof(float)*width*height); 49 | assert(mat.items != NULL); 50 | mat.width = width; 51 | mat.height = height; 52 | mat.stride = width; 53 | return mat; 54 | } 55 | 56 | static float rgb_to_lum(Color color) 57 | { 58 | Vector4 n = ColorNormalize(color); 59 | return 0.2126*n.x + 0.7152*n.y + 0.0722*n.z; 60 | } 61 | 62 | static void luminance(Image image, Mat lum) 63 | { 64 | assert(image.width == lum.width); 65 | assert(image.height == lum.height); 66 | Color *pixels = image.data; 67 | for (int y = 0; y < lum.height; ++y) { 68 | for (int x = 0; x < lum.width; ++x) { 69 | MAT_AT(lum, y, x) = rgb_to_lum(pixels[y*image.width + x]); 70 | } 71 | } 72 | } 73 | 74 | static float sobel_filter_at(Mat mat, int cx, int cy) 75 | { 76 | static float gx[3][3] = { 77 | {1.0, 0.0, -1.0}, 78 | {2.0, 0.0, -2.0}, 79 | {1.0, 0.0, -1.0}, 80 | }; 81 | 82 | static float gy[3][3] = { 83 | {1.0, 2.0, 1.0}, 84 | {0.0, 0.0, 0.0}, 85 | {-1.0, -2.0, -1.0}, 86 | }; 87 | 88 | float sx = 0.0; 89 | float sy = 0.0; 90 | for (int dy = -1; dy <= 1; ++dy) { 91 | for (int dx = -1; dx <= 1; ++dx) { 92 | int x = cx + dx; 93 | int y = cy + dy; 94 | float c = 0 <= x && x < mat.width && 0 <= y && y < mat.height ? MAT_AT(mat, y, x) : 0.0; 95 | sx += c*gx[dy + 1][dx + 1]; 96 | sy += c*gy[dy + 1][dx + 1]; 97 | } 98 | } 99 | return sqrtf(sx*sx + sy*sy); 100 | } 101 | 102 | static void sobel_filter(Mat mat, Mat grad) 103 | { 104 | assert(mat.width == grad.width); 105 | assert(mat.height == grad.height); 106 | 107 | for (int cy = 0; cy < mat.height; ++cy) { 108 | for (int cx = 0; cx < mat.width; ++cx) { 109 | MAT_AT(grad, cy, cx) = sobel_filter_at(mat, cx, cy); 110 | } 111 | } 112 | } 113 | 114 | static void load_assets(void) 115 | { 116 | // p->image = LoadImage("./assets/images/Screenshot 2024-06-05 16-13-57.png"); 117 | // p->image = LoadImage("./assets/images/Screenshot 2024-06-05 17-28-51.png"); 118 | p->image = LoadImage("./assets/images/Screenshot 2024-06-05 18-17-39.png"); 119 | ImageFormat(&p->image, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8); 120 | 121 | p->lum = mat_alloc(p->image.width, p->image.height); 122 | luminance(p->image, p->lum); 123 | 124 | // p->grad = mat_alloc(p->image.width, p->image.height); 125 | // sobel_filter(p->lum, p->grad); 126 | 127 | Color *pixels = p->image.data; 128 | for (int y = 0; y < p->image.height; ++y) { 129 | for (int x = 0; x < p->image.width; ++x) { 130 | pixels[y*p->image.width + x] = ColorFromHSV(0, 0, MAT_AT(p->lum, y, x)); 131 | } 132 | } 133 | 134 | p->texture = LoadTextureFromImage(p->image); 135 | 136 | int dataSize; 137 | unsigned char *data = LoadFileData(NODES_BIN_PATH, &dataSize); 138 | if (data) { 139 | if (dataSize == sizeof(p->nodes)) { 140 | memcpy(p->nodes, data, sizeof(p->nodes)); 141 | } else { 142 | TraceLog(LOG_ERROR, "Unexpected size of %s. Expected %d, but got %d.", NODES_BIN_PATH, sizeof(p->nodes), dataSize); 143 | } 144 | } 145 | } 146 | 147 | static void unload_assets(void) 148 | { 149 | UnloadImage(p->image); 150 | UnloadTexture(p->texture); 151 | free(p->lum.items); 152 | free(p->grad.items); 153 | } 154 | 155 | void plug_reset(void) 156 | { 157 | p->zoom = 1.0; 158 | p->target = (Vector2){0}; 159 | } 160 | 161 | void plug_init(void) 162 | { 163 | p = malloc(sizeof(*p)); 164 | assert(p != NULL); 165 | memset(p, 0, sizeof(*p)); 166 | p->size = sizeof(*p); 167 | 168 | load_assets(); 169 | plug_reset(); 170 | } 171 | 172 | void *plug_pre_reload(void) 173 | { 174 | unload_assets(); 175 | return p; 176 | } 177 | 178 | void plug_post_reload(void *state) 179 | { 180 | p = state; 181 | if (p->size < sizeof(*p)) { 182 | TraceLog(LOG_INFO, "Migrating plug state schema %zu bytes -> %zu bytes", p->size, sizeof(*p)); 183 | p = realloc(p, sizeof(*p)); 184 | p->size = sizeof(*p); 185 | } 186 | 187 | load_assets(); 188 | } 189 | 190 | Vector2 map_point(float x, float y) 191 | { 192 | Vector2 v1 = Vector2Lerp(p->nodes[0], p->nodes[1], x); 193 | Vector2 v2 = Vector2Lerp(p->nodes[2], p->nodes[3], x); 194 | return Vector2Lerp(v1, v2, y); 195 | } 196 | 197 | float rand_float(void) 198 | { 199 | return (float) rand() / (float) RAND_MAX; 200 | } 201 | 202 | float sample_cell(size_t row, size_t col) 203 | { 204 | float cell_width = 1.0/GRID_COLS; 205 | float cell_height = 1.0/GRID_ROWS; 206 | float x = col*cell_width; 207 | float y = row*cell_height; 208 | float sum = 0.0; 209 | for (size_t i = 0; i < SAMPLE_COUNT; ++i) { 210 | float sx = x + cell_width*0.5 - cell_width*SAMPLE_AREA*0.5 + rand_float()*cell_width*SAMPLE_AREA; 211 | float sy = y + cell_height*0.5 - cell_height*SAMPLE_AREA*0.5 + rand_float()*cell_height*SAMPLE_AREA; 212 | Vector2 s = map_point(sx, sy); 213 | sum += MAT_AT(p->lum, (int)s.y, (int)s.x); 214 | } 215 | return sum; 216 | } 217 | 218 | void plug_update(void) 219 | { 220 | Color background_color = ColorFromHSV(0, 0, 0.05); 221 | 222 | ClearBackground(background_color); 223 | 224 | Camera2D camera = { 225 | .target = p->target, 226 | .zoom = p->zoom, 227 | .offset = {GetScreenWidth()*0.5, GetScreenHeight()*0.5}, 228 | }; 229 | float wheel = GetMouseWheelMove(); 230 | if (wheel < 0) { 231 | p->zoom -= 0.1; 232 | } else if (wheel > 0) { 233 | p->zoom += 0.1; 234 | } 235 | Vector2 mouse = GetScreenToWorld2D(GetMousePosition(), camera); 236 | if (p->dragging) { 237 | if (IsMouseButtonReleased(MOUSE_BUTTON_RIGHT)) { 238 | p->dragging = false; 239 | } 240 | } else { 241 | if (IsMouseButtonPressed(MOUSE_BUTTON_RIGHT)) { 242 | p->dragging = true; 243 | p->anchor = mouse; 244 | } 245 | } 246 | if (IsKeyPressed(KEY_S)) { 247 | SaveFileData("./assets/data/nodes.bin", p->nodes, sizeof(p->nodes)); 248 | } 249 | if (IsKeyPressed(KEY_P)) { 250 | for (size_t bit_col = 0; bit_col < GRID_COLS/BITS_COUNT; ++bit_col) { 251 | for (size_t row = 0; row < GRID_ROWS; ++row) { 252 | int value = 1; 253 | for (int offset = BITS_COUNT-1; offset >= 0; --offset) { 254 | int bit = sample_cell(row, bit_col*BITS_COUNT + offset) < SAMPLE_THRESHOLD; 255 | value = (value << 1) | bit; 256 | } 257 | printf("%c", value); 258 | } 259 | } 260 | printf("\n"); 261 | } 262 | BeginMode2D(camera); 263 | Vector2 position = {0}; 264 | DrawTextureV(p->texture, position, WHITE); 265 | for (size_t i = 0; i < NOB_ARRAY_LEN(p->nodes); ++i) { 266 | bool selected = IsKeyDown(KEY_ONE + i); 267 | if (selected && IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) { 268 | p->nodes[i] = mouse; 269 | } 270 | if (selected) { 271 | DrawCircleV(p->nodes[i], NODE_RADIUS, ColorAlpha(YELLOW, 0.75)); 272 | } else { 273 | DrawCircleV(p->nodes[i], NODE_RADIUS, ColorAlpha(RED, 0.75)); 274 | } 275 | } 276 | 277 | float cell_width = 1.0/GRID_COLS; 278 | float cell_height = 1.0/GRID_ROWS; 279 | for (size_t row = 0; row < GRID_ROWS; ++row) { 280 | for (size_t col = 0; col < GRID_COLS; ++col) { 281 | float x = col*cell_width; 282 | float y = row*cell_height; 283 | if (sample_cell(row, col) < SAMPLE_THRESHOLD) { 284 | DrawCircleV(map_point(x + cell_width*0.5, y + cell_height*0.5), 2, RED); 285 | } 286 | } 287 | } 288 | if (p->dragging) { 289 | p->target = Vector2Subtract(p->target, Vector2Subtract(mouse, p->anchor)); 290 | } 291 | EndMode2D(); 292 | } 293 | -------------------------------------------------------------------------------- /src/plug.h: -------------------------------------------------------------------------------- 1 | #ifndef PLUG_H_ 2 | #define PLUG_H_ 3 | 4 | // void plug_init(void) 5 | // void *plug_pre_reload(void) 6 | // void plug_post_reload(void *state) 7 | // void plug_update(void) 8 | // void plug_reset(void) 9 | 10 | #define LIST_OF_PLUGS \ 11 | PLUG(plug_init, void, void) /* Initialize the plugin */ \ 12 | PLUG(plug_pre_reload, void*, void) /* Notify the plugin that it's about to get reloaded */ \ 13 | PLUG(plug_post_reload, void, void*) /* Notify the plugin that it got reloaded */ \ 14 | PLUG(plug_update, void, void) /* Render next frame of the animation */ \ 15 | PLUG(plug_reset, void, void) /* Reset the state of the animation */ \ 16 | 17 | #endif // PLUG_H_ 18 | -------------------------------------------------------------------------------- /thumbnail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsoding/secret/fdc592ceda00a9eb724aa19d7402de6606a76747/thumbnail.png --------------------------------------------------------------------------------