From 06369907ef9e7301161c1441df1441c6a95b7fa1 Mon Sep 17 00:00:00 2001 From: huaqo-games Date: Thu, 10 Apr 2025 12:39:52 +0200 Subject: [PATCH 1/3] appstate flag for fps drawing --- src/engine/applicationstate.h | 1 + src/game/game.h | 11 ++++++----- src/game/logo.h | 35 ++++++++++++++++++----------------- src/game/menu.h | 10 +++++++--- src/main.c | 7 ++++--- 5 files changed, 36 insertions(+), 28 deletions(-) diff --git a/src/engine/applicationstate.h b/src/engine/applicationstate.h index 2681416..89335f4 100644 --- a/src/engine/applicationstate.h +++ b/src/engine/applicationstate.h @@ -5,6 +5,7 @@ typedef struct { Screen currentScreen; bool running; bool gameStarted; + bool flagFPS; } State; #endif // APPLICATIONSTATE_H diff --git a/src/game/game.h b/src/game/game.h index a92385c..dd40816 100644 --- a/src/game/game.h +++ b/src/game/game.h @@ -82,8 +82,6 @@ void UpdateGame(GameState* g, State *state){ UpdateFloor(&g->floor, &g->camera); UpdatePlayer(&g->player); UpdateBee(&g->bee1, &g->flowers, &g->hive); - - } void RenderComponents(GameState* g){ @@ -92,10 +90,9 @@ void RenderComponents(GameState* g){ RenderObjectArray(&g->flowers); RenderSprite(&g->player.sprite); RenderSprite(&g->bee1.sprite); - } -void RenderGame(GameState* g){ +void RenderGame(GameState* g, const State *appState){ BeginTextureMode(g->target); ClearBackground(BLACK); BeginMode2D(g->camera); @@ -109,7 +106,11 @@ void RenderGame(GameState* g){ RenderPostFX(&g->postFX[i], &g->target); } DrawText( TextFormat("%d", g->player.inventar.seedCount), 10, 10, 50, WHITE); - //DrawFPS(10, 10); + + if (appState->flagFPS){ + DrawFPS(10, 10); + } + EndDrawing(); } diff --git a/src/game/logo.h b/src/game/logo.h index b90725f..8087125 100644 --- a/src/game/logo.h +++ b/src/game/logo.h @@ -18,7 +18,6 @@ typedef struct { void InitLogo(LogoState *l) { SetTargetFPS(60); - l->frameCounter = 0; l->positionX = GetScreenWidth() / 2 - 128; l->positionY = GetScreenHeight() / 2 - 128; @@ -37,15 +36,15 @@ void UpdateLogo(LogoState *l, State *state) { state->running = false; } - l->frameCounter++; // Increment the frame counter each frame + l->frameCounter++; - if (l->state == 0) { // State 0: Small box blinking for 120 frames (2 seconds at 60 FPS) + if (l->state == 0) { if (l->frameCounter >= 120) { l->state = 1; - l->frameCounter = 0; // Reset frame counter + l->frameCounter = 0; } } - else if (l->state == 1) { // State 1: Top and left bars growing + else if (l->state == 1) { l->topSideRecWidth += 4; l->leftSideRecHeight += 4; @@ -54,7 +53,7 @@ void UpdateLogo(LogoState *l, State *state) { l->frameCounter = 0; } } - else if (l->state == 2) { // State 2: Bottom and right bars growing + else if (l->state == 2) { l->bottomSideRecWidth += 4; l->rightSideRecHeight += 4; @@ -63,12 +62,12 @@ void UpdateLogo(LogoState *l, State *state) { l->frameCounter = 0; } } - else if (l->state == 3) { // State 3: Letters appearing - if (l->frameCounter % 12 == 0) { // Every 12 frames, one more letter appears + else if (l->state == 3) { + if (l->frameCounter % 12 == 0) { l->lettersCount++; } - if (l->lettersCount >= 10) { // Fade out animation + if (l->lettersCount >= 10) { l->alpha -= 0.02f; if (l->alpha <= 0.0f) { l->alpha = 0.0f; @@ -76,31 +75,31 @@ void UpdateLogo(LogoState *l, State *state) { } } } - else if (l->state == 4) { // State 4: Transition to GAMEPLAY + else if (l->state == 4) { state->currentScreen = MENU; } } -void RenderLogo(const LogoState *l) { +void RenderLogo(const LogoState *l, const State *appState) { BeginDrawing(); ClearBackground(RAYWHITE); - if (l->state == 0) { // Small box blinking - if ((l->frameCounter / 15) % 2 == 0) { // Blink every 15 frames + if (l->state == 0) { + if ((l->frameCounter / 15) % 2 == 0) { DrawRectangle(l->positionX, l->positionY, 16, 16, BLACK); } } - else if (l->state == 1) { // Top and left bars growing + else if (l->state == 1) { DrawRectangle(l->positionX, l->positionY, l->topSideRecWidth, 16, BLACK); DrawRectangle(l->positionX, l->positionY, 16, l->leftSideRecHeight, BLACK); } - else if (l->state == 2) { // Bottom and right bars growing + else if (l->state == 2) { DrawRectangle(l->positionX, l->positionY, l->topSideRecWidth, 16, BLACK); DrawRectangle(l->positionX, l->positionY, 16, l->leftSideRecHeight, BLACK); DrawRectangle(l->positionX + 240, l->positionY, 16, l->rightSideRecHeight, BLACK); DrawRectangle(l->positionX, l->positionY + 240, l->bottomSideRecWidth, 16, BLACK); } - else if (l->state == 3) { // Letters appearing + else if (l->state == 3) { DrawRectangle(l->positionX, l->positionY, l->topSideRecWidth, 16, Fade(BLACK, l->alpha)); DrawRectangle(l->positionX, l->positionY + 16, 16, l->leftSideRecHeight - 32, Fade(BLACK, l->alpha)); DrawRectangle(l->positionX + 240, l->positionY + 16, 16, l->rightSideRecHeight - 32, Fade(BLACK, l->alpha)); @@ -110,7 +109,9 @@ void RenderLogo(const LogoState *l) { DrawText(TextSubtext("raylib", 0, l->lettersCount), GetScreenWidth() / 2 - 44, GetScreenHeight() / 2 + 48, 50, Fade(BLACK, l->alpha)); } - DrawFPS(10, 10); + if (appState->flagFPS){ + DrawFPS(10, 10); + } EndDrawing(); } diff --git a/src/game/menu.h b/src/game/menu.h index 40506e5..eddcd81 100644 --- a/src/game/menu.h +++ b/src/game/menu.h @@ -124,7 +124,7 @@ void UpdateMenu(MenuState *m, State *state){ } -void RenderMenu(MenuState *m, Font *font, const char* name){ +void RenderMenu(MenuState *m, State *appState,Font *font, const char* name){ BeginDrawing(); ClearBackground(WHITE); RenderBackground(&m->background); @@ -142,8 +142,12 @@ void RenderMenu(MenuState *m, Font *font, const char* name){ (Vector2){0.0f, 0.0f}, 0.0f, WHITE - ); - DrawFPS(10, 10); + ); + + if (appState->flagFPS){ + DrawFPS(10, 10); + } + EndDrawing(); } diff --git a/src/main.c b/src/main.c index a375e58..168b15f 100644 --- a/src/main.c +++ b/src/main.c @@ -34,6 +34,7 @@ int main(void) app.state.currentScreen = LOGO; app.state.running = true; app.state.gameStarted = false; + app.state.flagFPS = true; app.display = display; app.font = LoadFont("assets/SproutLandsUI/fonts/pixelFont-7-8x14-sproutLands.fnt"); @@ -51,17 +52,17 @@ int main(void) case LOGO: { UpdateLogo(&app.logo, &app.state); - RenderLogo(&app.logo); + RenderLogo(&app.logo, &app.state); }break; case MENU: { UpdateMenu(&app.menu, &app.state); - RenderMenu(&app.menu, &app.font, app.display.name); + RenderMenu(&app.menu, &app.state, &app.font, app.display.name); }break; case GAMEPLAY: { UpdateGame(&app.game, &app.state); - RenderGame(&app.game); + RenderGame(&app.game, &app.state); }break; default: break; } From 68412a7d7a564deb59710e24432b8bd218aaf08d Mon Sep 17 00:00:00 2001 From: Huaqo Date: Fri, 11 Apr 2025 14:48:55 +0200 Subject: [PATCH 2/3] refactored app.h from main --- src/app.h | 33 ++++++++++++++++++++++++++++++ src/engine/applicationstate.h | 2 ++ src/main.c | 38 ++++++----------------------------- 3 files changed, 41 insertions(+), 32 deletions(-) create mode 100644 src/app.h diff --git a/src/app.h b/src/app.h new file mode 100644 index 0000000..f03ceb3 --- /dev/null +++ b/src/app.h @@ -0,0 +1,33 @@ +#ifndef APP_H +#define APP_H + +#include "engine/applicationstate.h" +#include "engine/display.h" +#include "engine/screen.h" +#include "game/logo.h" +#include "game/menu.h" +#include "game/game.h" + +typedef struct { + State state; + Display display; + Font font; + LogoState logo; + GameState game; + MenuState menu; +} Application; + +void InitApp(Application* app){ + app->state.currentScreen = LOGO; + app->state.running = true; + app->state.gameStarted = false; + app->state.flagFPS = true; + app->display = (Display){ + .name = "The Last Hive", + .width = 1280, + .height = 720 + }; + app->font = LoadFont("assets/SproutLandsUI/fonts/pixelFont-7-8x14-sproutLands.fnt"); +} + +#endif //APP_H diff --git a/src/engine/applicationstate.h b/src/engine/applicationstate.h index 89335f4..532b1ad 100644 --- a/src/engine/applicationstate.h +++ b/src/engine/applicationstate.h @@ -1,6 +1,8 @@ #ifndef APPLICATIONSTATE_H #define APPLICATIONSTATE_H +#include "screen.h" + typedef struct { Screen currentScreen; bool running; diff --git a/src/main.c b/src/main.c index 168b15f..a255b3f 100644 --- a/src/main.c +++ b/src/main.c @@ -1,42 +1,16 @@ #include -#include "../include/raymath.h" #include #include -#include "engine/screen.h" -#include "engine/display.h" -#include "engine/applicationstate.h" -#include "game/logo.h" -#include "game/menu.h" -#include "game/game.h" - -typedef struct { - State state; - Display display; - Font font; - LogoState logo; - GameState game; - MenuState menu; -} Application; +#include "../include/raymath.h" +#include "app.h" int main(void) { - - Display display = { - .name = "The Last Hive", - .width = 1280, - .height = 720 - }; - - InitWindow(display.width, display.height, display.name); - InitAudioDevice(); - Application app; - app.state.currentScreen = LOGO; - app.state.running = true; - app.state.gameStarted = false; - app.state.flagFPS = true; - app.display = display; - app.font = LoadFont("assets/SproutLandsUI/fonts/pixelFont-7-8x14-sproutLands.fnt"); + InitApp(&app); + + InitWindow(app.display.width, app.display.height, app.display.name); + InitAudioDevice(); SetTextureFilter(app.font.texture, TEXTURE_FILTER_POINT); SetTraceLogLevel(LOG_NONE); From 928842c4c2f3f9d45b5265232a09b7b786237a33 Mon Sep 17 00:00:00 2001 From: Huaqo Date: Fri, 11 Apr 2025 22:01:30 +0200 Subject: [PATCH 3/3] big refactor --- .gitignore | 1 - .vscode/c_cpp_properties.json | 27 + Makefile | 31 +- config/config.ini | 9 + {src => external}/engine/animation.h | 0 {src => external}/engine/asset.h | 0 {src => external}/engine/camera.h | 2 + external/engine/config.h | 67 + {src => external}/engine/display.h | 2 +- {src => external}/engine/gamegui.h | 0 {src => external}/engine/gui.h | 2 +- {src => external}/engine/input.h | 2 + {src => external}/engine/object.h | 0 {src => external}/engine/physics.h | 20 +- {src => external}/engine/postfx.h | 0 {src => external}/engine/soundtrack.h | 0 {src => external}/engine/sprite.h | 50 +- {src => external}/engine/vector.h | 0 {include => external/raygui}/raygui.h | 0 external/rini/rini.h | 842 ++++++ include/app.h | 73 + {src/engine => include}/applicationstate.h | 0 {src/game => include}/background.h | 4 +- {src/game => include}/bee.h | 8 +- {src/game => include}/button.h | 0 {src/game => include}/colors.h | 0 {src/game => include}/floor.h | 38 +- include/flowers.h | 30 + include/font.h | 13 + {src/game => include}/game.h | 131 +- {src/game => include}/gameassets.h | 21 +- {src/game => include}/hive.h | 13 +- {src/game => include}/inventar.h | 0 {src/game => include}/logo.h | 2 +- {src/game => include}/menu.h | 141 +- {src/game => include}/menuassets.h | 22 +- {src/game => include}/player.h | 114 +- include/raymath.h | 2966 -------------------- {src/engine => include}/screen.h | 4 +- src/app.h | 33 - src/game/flowers.h | 29 - src/main.c | 44 +- 42 files changed, 1429 insertions(+), 3312 deletions(-) create mode 100644 .vscode/c_cpp_properties.json create mode 100644 config/config.ini rename {src => external}/engine/animation.h (100%) rename {src => external}/engine/asset.h (100%) rename {src => external}/engine/camera.h (97%) create mode 100644 external/engine/config.h rename {src => external}/engine/display.h (83%) rename {src => external}/engine/gamegui.h (100%) rename {src => external}/engine/gui.h (94%) rename {src => external}/engine/input.h (98%) rename {src => external}/engine/object.h (100%) rename {src => external}/engine/physics.h (74%) rename {src => external}/engine/postfx.h (100%) rename {src => external}/engine/soundtrack.h (100%) rename {src => external}/engine/sprite.h (67%) rename {src => external}/engine/vector.h (100%) rename {include => external/raygui}/raygui.h (100%) create mode 100644 external/rini/rini.h create mode 100644 include/app.h rename {src/engine => include}/applicationstate.h (100%) rename {src/game => include}/background.h (97%) rename {src/game => include}/bee.h (95%) rename {src/game => include}/button.h (100%) rename {src/game => include}/colors.h (100%) rename {src/game => include}/floor.h (55%) create mode 100644 include/flowers.h create mode 100644 include/font.h rename {src/game => include}/game.h (54%) rename {src/game => include}/gameassets.h (88%) rename {src/game => include}/hive.h (63%) rename {src/game => include}/inventar.h (100%) rename {src/game => include}/logo.h (98%) rename {src/game => include}/menu.h (60%) rename {src/game => include}/menuassets.h (79%) rename {src/game => include}/player.h (55%) delete mode 100644 include/raymath.h rename {src/engine => include}/screen.h (76%) delete mode 100644 src/app.h delete mode 100644 src/game/flowers.h diff --git a/.gitignore b/.gitignore index 2ad5037..bce55da 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,2 @@ bin/ -.vscode/ .DS_STORE/ \ No newline at end of file diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..89633d9 --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,27 @@ +{ + "configurations": [ + { + "name": "macOS", + "includePath": [ + "${workspaceFolder}/include", + "${workspaceFolder}/external/engine", + "${workspaceFolder}/external/raygui", + "${workspaceFolder}/external/rini", + "/opt/homebrew/Cellar/raylib/5.5/include" + ], + "cStandard": "c99", + }, + { + "name": "Windows", + "includePath": [ + "${workspaceFolder}/include", + "${workspaceFolder}/external/engine", + "${workspaceFolder}/external/raygui", + "${workspaceFolder}/external/rini", + "C:/raylib/include" + ], + "cStandard": "c99", + } + ], + "version": 4 +} diff --git a/Makefile b/Makefile index 8b6de2f..e4493f1 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,32 @@ COMPILER = gcc -C_FLAGS = -std=c99 -g -Wall -Werror -Wpedantic -Wshadow -Wstrict-prototypes -Wno-unknown-pragmas + +C_FLAG_STD_C99 = -std=c99 # Use the C99 language standard +C_FLAG_DEBUG_INFO = -g # Include debug information +# C_FLAG_HEADERS = -H # Prints paths of all headers +C_FLAG_ALL_WARNINGS = -Wall # Enable most common warning +C_FLAG_PEDANTIC = -Wpedantic # Enforce ISO C standard strictly +C_FLAG_SHADOW = -Wshadow # Warn if a local variable shadows another +C_FLAG_STRICT_PROTOS = -Wstrict-prototypes # Warn if function declarations are missing parameter types +C_FLAG_NO_UNKNOWN_PRAGMAS = -Wno-unknown-pragmas # Suppress warnings about unknown pragmas +C_FLAGS = \ + $(C_FLAG_STD_C99) \ + $(C_FLAG_DEBUG_INFO) \ + $(C_FLAG_HEADERS) \ + $(C_FLAG_ALL_WARNINGS) \ + $(C_FLAG_PEDANTIC) \ + $(C_FLAG_SHADOW) \ + $(C_FLAG_STRICT_PROTOS) \ + $(C_FLAG_NO_UNKNOWN_PRAGMAS) + +INCLUDE_FLAGS = -Iinclude + +EXTERNAL_ENGINE = -Iexternal/engine +EXTERNAL_RAYGUI = -Iexternal/raygui +EXTERNAL_RINI = -Iexternal/rini +EXTERNAL_FLAGS = \ + $(EXTERNAL_ENGINE) \ + $(EXTERNAL_RINI) \ + $(EXTERNAL_RAYGUI) ifeq ($(OS), Windows_NT) SRC = src\main.c @@ -14,7 +41,7 @@ endif $(shell mkdir -p bin/osx bin/win) build: $(SRC) - $(COMPILER) $(SRC) -o $(TARGET) $(C_FLAGS) $(RAYLIB_FLAGS) + $(COMPILER) $(SRC) -o $(TARGET) $(C_FLAGS) $(INCLUDE_FLAGS) $(EXTERNAL_FLAGS) $(RAYLIB_FLAGS) clean: rm -f $(TARGET) diff --git a/config/config.ini b/config/config.ini new file mode 100644 index 0000000..90d130e --- /dev/null +++ b/config/config.ini @@ -0,0 +1,9 @@ +startScreen = 0 ; LOGO = 0, MENU = 1, GAMEPLAY = 2 + +display_title = The Last Hive +display_width = 1280 ; in Pixel +display_height = 720 ; in Pixel + +showFPS = 1 ; OFF = 0, ON = 1 + +font_path = assets/SproutLandsUI/fonts/pixelFont-7-8x14-sproutLands.fnt \ No newline at end of file diff --git a/src/engine/animation.h b/external/engine/animation.h similarity index 100% rename from src/engine/animation.h rename to external/engine/animation.h diff --git a/src/engine/asset.h b/external/engine/asset.h similarity index 100% rename from src/engine/asset.h rename to external/engine/asset.h diff --git a/src/engine/camera.h b/external/engine/camera.h similarity index 97% rename from src/engine/camera.h rename to external/engine/camera.h index 91061f7..58f1fe2 100644 --- a/src/engine/camera.h +++ b/external/engine/camera.h @@ -1,6 +1,8 @@ #ifndef CAMERA_H #define CAMERA_H + #include "input.h" +#include Camera2D CreateCamera(int screenWidth, int screenHeight, Vector2 playerPosition, float initZoom){ return (Camera2D){ diff --git a/external/engine/config.h b/external/engine/config.h new file mode 100644 index 0000000..0a554ca --- /dev/null +++ b/external/engine/config.h @@ -0,0 +1,67 @@ +#ifndef CONFIG_H +#define CONFIG_H + +#define RINI_IMPLEMENTATION +#define RINI_VALUE_DELIMITER '=' + +#include +#include +#include + +typedef struct { + char *path; + rini_config rini; +} Config; + +void InitConfig(Config *config, const char* path) { + config->rini = rini_load_config(path); + if (config->rini.count == 0) { + printf("Warning: Loaded config has no entries. Path: %s\n", config->path); + } +} + +int GetConfigInt(Config *config, const char *key) { + return rini_get_config_value_fallback(config->rini, key, 0); +} + +int GetConfigIntOrDefault(Config *config, const char *key, int default_value) { + return rini_get_config_value_fallback(config->rini, key, default_value); +} + +const char* GetConfigString(Config *config, const char *key) { + return rini_get_config_value_text(config->rini, key); +} + +const char* GetConfigStringOrDefault(Config *config, const char *key, const char *default_value) { + return rini_get_config_value_text_fallback(config->rini, key, default_value); +} + +const char* GetConfigDescription(Config *config, const char *key) { + return rini_get_config_value_description(config->rini, key); +} + +int SetConfigInt(Config *config, const char *key, int value, const char *desc) { + return rini_set_config_value(&config->rini, key, value, desc); +} + +int SetConfigString(Config *config, const char *key, const char *text, const char *desc) { + return rini_set_config_value_text(&config->rini, key, text, desc); +} + +int SetConfigDescription(Config *config, const char *key, const char *desc) { + return rini_set_config_value_description(&config->rini, key, desc); +} + +int AddCommentLine(Config *config, const char *comment) { + return rini_set_config_comment_line(&config->rini, comment); +} + +void SaveConfig(Config *config) { + rini_save_config(config->rini, config->path); +} + +void CleanUpConfig(Config *config) { + rini_unload_config(&config->rini); +} + +#endif // CONFIG_H diff --git a/src/engine/display.h b/external/engine/display.h similarity index 83% rename from src/engine/display.h rename to external/engine/display.h index f1764b6..30c954b 100644 --- a/src/engine/display.h +++ b/external/engine/display.h @@ -4,7 +4,7 @@ typedef struct { int width; int height; - const char* name; + const char* title; } Display; #endif // DISPLAY_H diff --git a/src/engine/gamegui.h b/external/engine/gamegui.h similarity index 100% rename from src/engine/gamegui.h rename to external/engine/gamegui.h diff --git a/src/engine/gui.h b/external/engine/gui.h similarity index 94% rename from src/engine/gui.h rename to external/engine/gui.h index ba69c01..da9b353 100644 --- a/src/engine/gui.h +++ b/external/engine/gui.h @@ -2,7 +2,7 @@ #define GUI_H #define RAYGUI_IMPLEMENTATION -#include "../../include/raygui.h" +#include typedef struct { diff --git a/src/engine/input.h b/external/engine/input.h similarity index 98% rename from src/engine/input.h rename to external/engine/input.h index 2d54549..cf0be60 100644 --- a/src/engine/input.h +++ b/external/engine/input.h @@ -1,6 +1,8 @@ #ifndef INPUT_H #define INPUT_H +#include + typedef struct { Vector2 screenPosition; Vector2 worldPosition; diff --git a/src/engine/object.h b/external/engine/object.h similarity index 100% rename from src/engine/object.h rename to external/engine/object.h diff --git a/src/engine/physics.h b/external/engine/physics.h similarity index 74% rename from src/engine/physics.h rename to external/engine/physics.h index 75a5374..ff7824c 100644 --- a/src/engine/physics.h +++ b/external/engine/physics.h @@ -1,30 +1,38 @@ #ifndef PHYSICS_H #define PHYSICS_H -typedef struct { +#include + +typedef struct +{ Vector2 position; Vector2 direction; Vector2 velocity; float speed; } Physics; -void UpdatePhysicsDirection(Vector2 *direction, Vector2 vector){ +void UpdatePhysicsDirection(Vector2 *direction, Vector2 vector) +{ *direction = Vector2Normalize(vector); } -void UpdatePhysicsVelocity(Vector2 *velocity, Vector2 *direction, float *speed){ +void UpdatePhysicsVelocity(Vector2 *velocity, Vector2 *direction, float *speed) +{ *velocity = Vector2Scale(*direction, *speed * GetFrameTime()); } -void UpdatePhysicsPosition(Vector2 *position, Vector2 *velocity) { +void UpdatePhysicsPosition(Vector2 *position, Vector2 *velocity) +{ *position = Vector2Add(*position, *velocity); } -void UpdatePhysicsSpeed(float *speed, float *newSpeed){ +void UpdatePhysicsSpeed(float *speed, float *newSpeed) +{ *speed = *newSpeed; } -void UpdatePhysics(Physics *physics, Vector2 direction){ +void UpdatePhysics(Physics *physics, Vector2 direction) +{ UpdatePhysicsDirection(&physics->direction, direction); UpdatePhysicsVelocity(&physics->velocity, &physics->direction, &physics->speed); UpdatePhysicsPosition(&physics->position, &physics->velocity); diff --git a/src/engine/postfx.h b/external/engine/postfx.h similarity index 100% rename from src/engine/postfx.h rename to external/engine/postfx.h diff --git a/src/engine/soundtrack.h b/external/engine/soundtrack.h similarity index 100% rename from src/engine/soundtrack.h rename to external/engine/soundtrack.h diff --git a/src/engine/sprite.h b/external/engine/sprite.h similarity index 67% rename from src/engine/sprite.h rename to external/engine/sprite.h index 8fff197..22f511b 100644 --- a/src/engine/sprite.h +++ b/external/engine/sprite.h @@ -1,46 +1,54 @@ #ifndef SPRITE_H #define SPRITE_H -typedef struct { - Texture2D texture; - Rectangle sourceRec; - Rectangle destRec; - Vector2 frameSize; - Vector2 origin; - float rotation; +#include + +typedef struct +{ + Texture2D texture; + Rectangle sourceRec; + Rectangle destRec; + Vector2 frameSize; + Vector2 origin; + float rotation; Color color; } Sprite; -void UpdateSpriteSourceRec(Sprite *sprite, const Vector2 *vector){ +void UpdateSpriteSourceRec(Sprite *sprite, const Vector2 *vector) +{ sprite->sourceRec.x = vector->x; sprite->sourceRec.y = vector->y; } -void UpdateSpriteDestRec(Sprite *sprite, const Vector2 *vector){ +void UpdateSpriteDestRec(Sprite *sprite, const Vector2 *vector) +{ sprite->destRec.x = vector->x; sprite->destRec.y = vector->y; } -void UpdateSpriteRotation(Sprite *sprite, Vector2 *vector){ - if(Vector2Length(*vector) <= 0.0f){ +void UpdateSpriteRotation(Sprite *sprite, Vector2 *vector) +{ + if (Vector2Length(*vector) <= 0.0f) + { return; } float targetRotation = (float)atan2(vector->y, vector->x) * (180.0f / PI) + 90.0f; - sprite->rotation = Lerp(sprite->rotation, targetRotation, 0.1f); + sprite->rotation = Lerp(sprite->rotation, targetRotation, 0.1f); } -void RenderSprite(const Sprite *sprite){ +void RenderSprite(const Sprite *sprite) +{ DrawTexturePro( - sprite->texture, - sprite->sourceRec, - sprite->destRec, - sprite->origin, - sprite->rotation, - sprite->color - ); + sprite->texture, + sprite->sourceRec, + sprite->destRec, + sprite->origin, + sprite->rotation, + sprite->color); } -void PrintSprite(const Sprite *sprite) { +void PrintSprite(const Sprite *sprite) +{ printf("Sprite:\n"); printf(" Sprite.texture: id = %d\n", sprite->texture.id); printf(" Sprite.sourceRec: x = %f, y = %f, width = %f, height = %f\n", sprite->sourceRec.x, sprite->sourceRec.y, sprite->sourceRec.width, sprite->sourceRec.height); diff --git a/src/engine/vector.h b/external/engine/vector.h similarity index 100% rename from src/engine/vector.h rename to external/engine/vector.h diff --git a/include/raygui.h b/external/raygui/raygui.h similarity index 100% rename from include/raygui.h rename to external/raygui/raygui.h diff --git a/external/rini/rini.h b/external/rini/rini.h new file mode 100644 index 0000000..e38b4ac --- /dev/null +++ b/external/rini/rini.h @@ -0,0 +1,842 @@ +/********************************************************************************************** +* +* rini v1.0 - A simple and easy-to-use config init files reader and writer +* +* DESCRIPTION: +* Load and save config init properties +* +* FEATURES: +* - Config files reading and writing +* - Supported value types: int, string +* - Support comment lines and empty lines +* - Support custom line comment delimiter +* - Support custom value delimiters +* - Support value description comments +* - Support custom description custom delimiter +* - Support multi-word text values w/o quote delimiters +* - Minimal C standard lib dependency (optional) +* - Customizable maximum config values capacity +* +* LIMITATIONS: +* - Config [sections] not supported +* - Saving config file requires complete rewrite +* +* POSSIBLE IMPROVEMENTS: +* - Support config [sections] +* - Support disabled entries recognition +* +* CONFIGURATION: +* #define RINI_IMPLEMENTATION +* Generates the implementation of the library into the included file. +* If not defined, the library is in header only mode and can be included in other headers +* or source files without problems. But only ONE file should hold the implementation. +* +* #define RINI_MAX_LINE_SIZE +* Defines the maximum size of line buffer to read from file. +* Default value: 512 bytes (considering [key + text + desc] is 256 max size by default) +* +* #define RINI_MAX_KEY_SIZE +* Defines the maximum size of value key +* Default value: 64 bytes +* +* #define RINI_MAX_TEXT_SIZE +* Defines the maximum size of value text +* Default value: 128 bytes +* +* #define RINI_MAX_DESC_SIZE +* Defines the maximum size of value description +* Default value: 128 bytes +* +* #define RINI_MAX_VALUE_CAPACITY +* Defines the maximum number of values supported +* Default value: 128 entries support +* +* #define RINI_LINE_COMMENT_DELIMITER +* Define character used to comment lines, placed at beginning of line +* Most .ini files use semicolon ';' but '#' is also used +* Default value: '#' +* +* #define RINI_LINE_SECTION_DELIMITER +* Defines section lines start character +* Sections loading is not supported, lines are just skipped for now +* Default value: '[' +* +* #define RINI_VALUE_DELIMITER +* Defines a key value delimiter, in case it is defined. +* Most .ini files use '=' as value delimiter but ':' is also used or just whitespace +* Default value: ' ' +* +* #define RINI_VALUE_QUOTATION_MARKS +* Defines quotation marks to be used around text values +* Text values are determined checking text with atoi(), only for integer values, +* in case of float values they are always considered as text +* Default value: '\"' +* +* #define RINI_DESCRIPTION_DELIMITER +* Defines a property line-end comment delimiter +* This implementation allows adding inline comments after the value. +* Default value: '#' +* +* DEPENDENCIES: C standard library: +* - stdio.h: fopen(), feof(), fgets(), fclose(), fprintf() +* - stdlib.h: malloc(), calloc(), free() +* - string.h: memset(), memcpy(), strcmp(), strlen() +* +* VERSIONS HISTORY: +* 2.0 (26-Jan-2023) ADDED: Support custom comment lines (as config entries) +* ADDED: Use of quotation-marks and marks customization +* ADDED: rini_set_config_comment_line() +* ADDED: rini_get_config_value_fallback(), with fallback return value +* ADDED: rini_get_config_text_fallback(), with fallback return value +* REMOVED: Config header requirement, use comments entries +* REVIEWED: Some configuration default values, capacities +* REVIEWED: rini_save_config(), removed parameter +* REVIEWED: rini_save_config_from_memory(), removed parameter +* +* 1.0 (18-May-2023) First release, basic read/write functionality +* +* +* LICENSE: zlib/libpng +* +* Copyright (c) 2023-2025 Ramon Santamaria (@raysan5) +* +* This software is provided "as-is", without any express or implied warranty. In no event +* will the authors be held liable for any damages arising from the use of this software. +* +* Permission is granted to anyone to use this software for any purpose, including commercial +* applications, and to alter it and redistribute it freely, subject to the following restrictions: +* +* 1. The origin of this software must not be misrepresented; you must not claim that you +* wrote the original software. If you use this software in a product, an acknowledgment +* in the product documentation would be appreciated but is not required. +* +* 2. Altered source versions must be plainly marked as such, and must not be misrepresented +* as being the original software. +* +* 3. This notice may not be removed or altered from any source distribution. +* +**********************************************************************************************/ + +#ifndef RINI_H +#define RINI_H + +#define RINI_VERSION "1.0" + +// Function specifiers in case library is build/used as a shared library (Windows) +// NOTE: Microsoft specifiers to tell compiler that symbols are imported/exported from a .dll +#if defined(_WIN32) + #if defined(BUILD_LIBTYPE_SHARED) + #define RINIAPI __declspec(dllexport) // We are building the library as a Win32 shared library (.dll) + #elif defined(USE_LIBTYPE_SHARED) + #define RINIAPI __declspec(dllimport) // We are using the library as a Win32 shared library (.dll) + #endif +#endif + +// Function specifiers definition +#ifndef RINIAPI + #define RINIAPI // Functions defined as 'extern' by default (implicit specifiers) +#endif + +//---------------------------------------------------------------------------------- +// Defines and Macros +//---------------------------------------------------------------------------------- +// Allow custom memory allocators +#ifndef RINI_MALLOC + #define RINI_MALLOC(sz) malloc(sz) +#endif +#ifndef RINI_CALLOC + #define RINI_CALLOC(n,sz) calloc(n,sz) +#endif +#ifndef RINI_FREE + #define RINI_FREE(p) free(p) +#endif + +// Simple log system to avoid printf() calls if required +// NOTE: Avoiding those calls, also avoids const strings memory usage +#define RINI_SUPPORT_LOG_INFO +#if defined(RINI_SUPPORT_LOG_INFO) + #define RINI_LOG(...) printf(__VA_ARGS__) +#else + #define RINI_LOG(...) +#endif + +#if !defined(RINI_MAX_LINE_SIZE) + #define RINI_MAX_LINE_SIZE 512 +#endif + +#if !defined(RINI_MAX_KEY_SIZE) + #define RINI_MAX_KEY_SIZE 64 +#endif + +#if !defined(RINI_MAX_TEXT_SIZE) + #define RINI_MAX_TEXT_SIZE 128 +#endif + +#if !defined(RINI_MAX_DESC_SIZE) + #define RINI_MAX_DESC_SIZE 128 +#endif + +#if !defined(RINI_MAX_VALUE_CAPACITY) + #define RINI_MAX_VALUE_CAPACITY 128 +#endif + +// Line comment delimiter (starting string) +#if !defined(RINI_LINE_COMMENT_DELIMITER) + #define RINI_LINE_COMMENT_DELIMITER '#' +#endif + +// Line section delimiter -NOT USED- +#if !defined(RINI_LINE_SECTION_DELIMITER) + #define RINI_LINE_SECTION_DELIMITER '[' +#endif + +// Value delimiter, separator between key and value +#if !defined(RINI_VALUE_DELIMITER) + #define RINI_VALUE_DELIMITER ' ' +#endif + +// Use quotation marks for text values +// NOTE: Integer values do not use quotation-marks +#define RINI_USE_TEXT_QUOTATION_MARKS 1 +// Text value quoation marks +#if !defined(RINI_VALUE_QUOTATION_MARKS) + #define RINI_VALUE_QUOTATION_MARKS '\"' +#endif + +// Description delimiter, separator between value and description +#if !defined(RINI_DESCRIPTION_DELIMITER) + #define RINI_DESCRIPTION_DELIMITER '#' +#endif + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- +// Config value entry +typedef struct { + char key[RINI_MAX_KEY_SIZE]; // Config value key identifier + char text[RINI_MAX_TEXT_SIZE]; // Config value text + char desc[RINI_MAX_DESC_SIZE]; // Config value description +} rini_config_value; + +// Config data +typedef struct { + rini_config_value *values; // Config values array + unsigned int count; // Config values count + unsigned int capacity; // Config values capacity +} rini_config; + +#if defined(__cplusplus) +extern "C" { // Prevents name mangling of functions +#endif + +//------------------------------------------------------------------------------------ +// Functions declaration +//------------------------------------------------------------------------------------ +RINIAPI rini_config rini_load_config(const char *file_name); // Load config from file (*.ini) or create a new config object (pass NULL) +RINIAPI rini_config rini_load_config_from_memory(const char *text); // Load config from text buffer +RINIAPI void rini_save_config(rini_config config, const char *file_name); // Save config to file, with custom header +RINIAPI char *rini_save_config_to_memory(rini_config config); // Save config to text buffer ('\0' EOL) +RINIAPI void rini_unload_config(rini_config *config); // Unload config data from memory + +RINIAPI int rini_get_config_value(rini_config config, const char *key); // Get config value int for provided key, returns 0 if not found +RINIAPI const char *rini_get_config_value_text(rini_config config, const char *key); // Get config value text for provided key +RINIAPI const char *rini_get_config_value_description(rini_config config, const char *key); // Get config value description for provided key + +RINIAPI int rini_get_config_value_fallback(rini_config config, const char *key, int fallback); // Get config value for provided key with default value fallback if not found or not valid +RINIAPI const char *rini_get_config_value_text_fallback(rini_config config, const char *key, const char *fallback); // Get config value text for provided key with fallback if not found or not valid + +RINIAPI int rini_set_config_comment_line(rini_config *config, const char *comment); // Set config comment line + +// Set config value int/text and description for existing key or create a new entry +// NOTE: When setting a text value, if id does not exist, a new entry is automatically created +RINIAPI int rini_set_config_value(rini_config *config, const char *key, int value, const char *desc); +RINIAPI int rini_set_config_value_text(rini_config *config, const char *key, const char *text, const char *desc); + +// Set config value description for existing key +// WARNING: Key must exist to add description, if a description exists, it is updated +RINIAPI int rini_set_config_value_description(rini_config *config, const char *key, const char *desc); + +#ifdef __cplusplus +} +#endif + +#endif // RINI_H + +/*********************************************************************************** +* +* RINI IMPLEMENTATION +* +************************************************************************************/ + +#if defined(RINI_IMPLEMENTATION) + +#include // Required for: fopen(), feof(), fgets(), fclose(), fprintf() +#include // Required for: malloc(), calloc(), free() +#include // Required for: memset(), memcpy(), strcmp(), strlen() + +//---------------------------------------------------------------------------------- +// Defines and macros +//---------------------------------------------------------------------------------- +// ... + +//---------------------------------------------------------------------------------- +// Global variables definition +//---------------------------------------------------------------------------------- +// ... + +//---------------------------------------------------------------------------------- +// Module internal functions declaration +//---------------------------------------------------------------------------------- +static int rini_read_config_key(const char *buffer, char *key); // Get key from a buffer line containing key-value-(description) +static int rini_read_config_value_text(const char *buffer, char *text, char *desc); // Get value text (and description) from a buffer line + +static int rini_text_to_int(const char *text); // Convert text to int value (if possible), same as atoi() + +//---------------------------------------------------------------------------------- +// Module functions definition +//---------------------------------------------------------------------------------- +// Load config from file (.ini) +rini_config rini_load_config(const char *file_name) +{ + rini_config config = { 0 }; + unsigned int value_counter = 0; + + // Init config data to max capacity + config.capacity = RINI_MAX_VALUE_CAPACITY; + config.values = (rini_config_value *)RINI_CALLOC(RINI_MAX_VALUE_CAPACITY, sizeof(rini_config_value)); + + if (file_name != NULL) + { + FILE *rini_file = fopen(file_name, "rt"); + + if (rini_file != NULL) + { + char buffer[RINI_MAX_LINE_SIZE] = { 0 }; // Buffer to read every text line + + // First pass to count valid config lines + while (fgets(buffer, RINI_MAX_LINE_SIZE, rini_file)) + { + // WARNING: fgets() keeps line endings, doesn't have any special options for converting line endings, + // but on Windows, when reading file 'rt', line endings are converted from \r\n to just \n + + // Skip commented lines and empty lines + // NOTE: We are also skipping sections delimiters + if ((buffer[0] != RINI_LINE_COMMENT_DELIMITER) && + (buffer[0] != RINI_LINE_SECTION_DELIMITER) && + (buffer[0] != '\n') && (buffer[0] != '\0')) value_counter++; + } + + // WARNING: We can't store more values than its max capacity + config.count = (value_counter > RINI_MAX_VALUE_CAPACITY)? RINI_MAX_VALUE_CAPACITY : value_counter; + + if (config.count > 0) + { + rewind(rini_file); + value_counter = 0; + + // Second pass to read config data + while (fgets(buffer, RINI_MAX_LINE_SIZE, rini_file)) + { + // WARNING: fgets() keeps line endings, doesn't have any special options for converting line endings, + // but on Windows, when reading file 'rt', line endings are converted from \r\n to just \n + + // Skip commented lines and empty lines + if ((buffer[0] != RINI_LINE_COMMENT_DELIMITER) && + (buffer[0] != RINI_LINE_SECTION_DELIMITER) && + (buffer[0] != '\n') && (buffer[0] != '\0')) + { + // Get key identifier string + memset(config.values[value_counter].key, 0, RINI_MAX_KEY_SIZE); + rini_read_config_key(buffer, config.values[value_counter].key); + rini_read_config_value_text(buffer, config.values[value_counter].text, config.values[value_counter].desc); + + value_counter++; + + // Stop reading if first count reached to avoid overflow in case count == RINI_MAX_VALUE_CAPACITY + if (value_counter >= config.count) break; + } + } + } + + fclose(rini_file); + } + } + + return config; +} + +// Load config from text buffer +// NOTE: Comments and empty lines are ignored +rini_config rini_load_config_from_memory(const char *text) +{ + #define RINI_MAX_TEXT_LINES RINI_MAX_VALUE_CAPACITY*2 // Consider possible comments and empty lines + + rini_config config = { 0 }; + unsigned int value_counter = 0; + + // Init config data to max capacity + config.capacity = RINI_MAX_VALUE_CAPACITY; + config.values = (rini_config_value *)RINI_CALLOC(RINI_MAX_VALUE_CAPACITY, sizeof(rini_config_value)); + + if (text != NULL) + { + // Split text by line-breaks + const char *lines[RINI_MAX_TEXT_LINES] = { 0 }; + int textSize = (int)strlen(text); + lines[0] = text; + int line_counter = 1; + + for (int i = 0, k = 1; (i < textSize) && (line_counter < RINI_MAX_TEXT_LINES); i++) + { + if (text[i] == '\n') + { + lines[k] = &text[i + 1]; // WARNING: next value is valid? + line_counter += 1; + k++; + } + } + + // Count possible values in lines + for (int l = 0; l < line_counter; l++) + { + // Skip commented lines and empty lines + // NOTE: We are also skipping sections delimiters + if ((lines[l][0] != RINI_LINE_COMMENT_DELIMITER) && + (lines[l][0] != RINI_LINE_SECTION_DELIMITER) && + (lines[l][0] != '\n') && (lines[l][0] != '\0')) value_counter++; + } + + // WARNING: We can't store more values than its max capacity + config.count = (value_counter > RINI_MAX_VALUE_CAPACITY)? RINI_MAX_VALUE_CAPACITY : value_counter; + + // Process lines to get keys and values + if (config.count > 0) + { + value_counter = 0; + + // Second pass to read config data + for (int l = 0; l < line_counter; l++) + { + // Skip commented lines and empty lines + if ((lines[l][0] != RINI_LINE_COMMENT_DELIMITER) && + (lines[l][0] != RINI_LINE_SECTION_DELIMITER) && + (lines[l][0] != '\n') && (lines[l][0] != '\0')) + { + // Get key identifier string + memset(config.values[value_counter].key, 0, RINI_MAX_KEY_SIZE); + rini_read_config_key(lines[l], config.values[value_counter].key); + rini_read_config_value_text(lines[l], config.values[value_counter].text, config.values[value_counter].desc); + + value_counter++; + + // Stop reading if first count reached to avoid overflow in case count == RINI_MAX_VALUE_CAPACITY + if (value_counter >= config.count) break; + } + } + } + } + + return config; +} + +// Save config to file (*.ini) +void rini_save_config(rini_config config, const char *file_name) +{ + FILE *rini_file = fopen(file_name, "wt"); + + if (rini_file != NULL) + { + for (unsigned int i = 0; i < config.count; i++) + { + if ((config.values[i].key[0] == '\0') && (config.values[i].text[0] == RINI_LINE_COMMENT_DELIMITER)) + { + if (config.values[i].desc[0] != '\0') fprintf(rini_file, "%c %s\n", RINI_LINE_COMMENT_DELIMITER, config.values[i].desc); + else fprintf(rini_file, "%c\n", RINI_LINE_COMMENT_DELIMITER); + } + else + { +#if RINI_USE_TEXT_QUOTATION_MARKS + // If text is not an integer value, append text quotation-marks + // TODO: Check also for float values? atof() + bool valueIsInt = false; + int value = atoi(config.values[i].text); // WARNING: Returns 0 if input not valid --> Check if (config.values[i].text len == 1 && config.values[i].text[0] == '0') + if (((value == 0) && (strlen(config.values[i].text) == 1) && (config.values[i].text[0] == '0')) || (value != 0)) valueIsInt = true; + + if (valueIsInt) + { + fprintf(rini_file, "%-28s %c %6s %c %s\n", config.values[i].key, RINI_VALUE_DELIMITER, + config.values[i].text, RINI_DESCRIPTION_DELIMITER, config.values[i].desc); + } + else + { + fprintf(rini_file, "%-28s %c %c%6s%c %c %s\n", config.values[i].key, RINI_VALUE_DELIMITER, + RINI_VALUE_QUOTATION_MARKS, config.values[i].text, RINI_VALUE_QUOTATION_MARKS, + RINI_DESCRIPTION_DELIMITER, config.values[i].desc); + } +#else + fprintf(rini_file, "%-28s %c %6s %c %s\n", config.values[i].key, RINI_VALUE_DELIMITER, + config.values[i].text, RINI_DESCRIPTION_DELIMITER, config.values[i].desc); +#endif + } + } + + fclose(rini_file); + } +} + +// Save config to text buffer ('\0' EOL) +char *rini_save_config_to_memory(rini_config config) +{ + #define RINI_MAX_TEXT_FILE_SIZE 4096 + + // Verify required config size is smaller than memory buffer size + // NOTE: We add 64 extra possible characters by entry line + int requiredSize = 0; + for (unsigned int i = 0; i < config.count; i++) requiredSize += ((int)strlen(config.values[i].key) + (int)strlen(config.values[i].text) + (int)strlen(config.values[i].desc) + 64); + if (requiredSize > RINI_MAX_TEXT_FILE_SIZE) RINI_LOG("WARNING: Required config.ini size is bigger than max supported memory size, increase RINI_MAX_TEXT_FILE_SIZE\n"); + + // NOTE: Using a static buffer to avoid de-allocation requirement on user side + static char text[RINI_MAX_TEXT_FILE_SIZE] = { 0 }; + memset(text, 0, RINI_MAX_TEXT_FILE_SIZE); + int offset = 0; + + for (unsigned int i = 0; i < config.count; i++) + { + if ((config.values[i].key[0] == '\0') && (config.values[i].text[0] == RINI_LINE_COMMENT_DELIMITER)) + { + if (config.values[i].desc[0] != '\0') offset += sprintf(text + offset, "%c %s\n", RINI_LINE_COMMENT_DELIMITER, config.values[i].desc); + else offset += sprintf(text + offset, "%c\n", RINI_LINE_COMMENT_DELIMITER); + } + else + { +#if RINI_USE_TEXT_QUOTATION_MARKS + // If text is not an integer value, append text quotation-marks + // TODO: Check also for float values? atof() + bool valueIsInt = false; + int value = atoi(config.values[i].text); // WARNING: Returns 0 if input not valid --> Check if (config.values[i].text len == 1 && config.values[i].text[0] == '0') + if (((value == 0) && (strlen(config.values[i].text) == 1) && (config.values[i].text[0] == '0')) || (value != 0)) valueIsInt = true; + + if (valueIsInt) + { + offset += sprintf(text + offset, "%-28s %c %6s %c %s\n", config.values[i].key, RINI_VALUE_DELIMITER, + config.values[i].text, RINI_DESCRIPTION_DELIMITER, config.values[i].desc); + } + else + { + offset += sprintf(text + offset, "%-28s %c %c%6s%c %c %s\n", config.values[i].key, RINI_VALUE_DELIMITER, + RINI_VALUE_QUOTATION_MARKS, config.values[i].text, RINI_VALUE_QUOTATION_MARKS, + RINI_DESCRIPTION_DELIMITER, config.values[i].desc); + } +#else + offset += sprintf(text + offset, "%-28s %c %6s %c %s\n", config.values[i].key, RINI_VALUE_DELIMITER, + config.values[i].text, RINI_DESCRIPTION_DELIMITER, config.values[i].desc); +#endif + } + } + + return text; +} + +// Unload config data +void rini_unload_config(rini_config *config) +{ + RINI_FREE(config->values); + + config->values = NULL; + config->count = 0; + config->capacity = 0; +} + +// Get config value for provided key, returns 0 if not found or not valid +int rini_get_config_value(rini_config config, const char *key) +{ + int value = 0; + + for (unsigned int i = 0; i < config.count; i++) + { + if (strcmp(key, config.values[i].key) == 0) // Key found + { + value = rini_text_to_int(config.values[i].text); + break; + } + } + + return value; +} + +// Get config value for provided key with default value fallback if not found or not valid +int rini_get_config_value_fallback(rini_config config, const char *key, int fallback) +{ + int value = fallback; + + for (unsigned int i = 0; i < config.count; i++) + { + if (strcmp(key, config.values[i].key) == 0) // Key found + { + // TODO: Detect if conversion fails... + value = rini_text_to_int(config.values[i].text); + break; + } + } + + return value; +} + +// Get config text for string id +const char *rini_get_config_value_text(rini_config config, const char *key) +{ + const char *text = NULL; + + for (unsigned int i = 0; i < config.count; i++) + { + if (strcmp(key, config.values[i].key) == 0) // Key found + { + text = config.values[i].text; + break; + } + } + + return text; +} + +// Get config value text for provided key with fallback if not found or not valid +RINIAPI const char *rini_get_config_value_text_fallback(rini_config config, const char *key, const char *fallback) +{ + const char *text = fallback; + + for (unsigned int i = 0; i < config.count; i++) + { + if (strcmp(key, config.values[i].key) == 0) // Key found + { + text = config.values[i].text; + break; + } + } + + return text; +} + +// Get config description for string id +const char *rini_get_config_value_description(rini_config config, const char *key) +{ + const char *desc = NULL; + + for (unsigned int i = 0; i < config.count; i++) + { + if (strcmp(key, config.values[i].key) == 0) // Key found + { + desc = config.values[i].desc; + break; + } + } + + return desc; +} + +// Set config comment line +int rini_set_config_comment_line(rini_config *config, const char *comment) +{ + int result = -1; + char text[2] = { RINI_LINE_COMMENT_DELIMITER, '\0' }; + + result = rini_set_config_value_text(config, NULL, text, comment); + + return result; +} + +// Set config value and description for existing key or create a new entry +int rini_set_config_value(rini_config *config, const char *key, int value, const char *desc) +{ + int result = -1; + char value_text[RINI_MAX_TEXT_SIZE] = { 0 }; + + sprintf(value_text, "%i", value); + + result = rini_set_config_value_text(config, key, value_text, desc); + + return result; +} + +// Set config value text and description for existing key or create a new entry +// NOTE: When setting a text value, if id does not exist, a new entry is automatically created +int rini_set_config_value_text(rini_config *config, const char *key, const char *text, const char *desc) +{ + int result = -1; + + if ((text == NULL) || (text[0] == '\0')) return result; + + if (key != NULL) + { + // Try to find key and update text and description + for (unsigned int i = 0; i < config->count; i++) + { + if (strcmp(key, config->values[i].key) == 0) // Key found + { + memset(config->values[i].text, 0, RINI_MAX_TEXT_SIZE); + memcpy(config->values[i].text, text, strlen(text)); + + memset(config->values[i].desc, 0, RINI_MAX_DESC_SIZE); + if (desc != NULL) memcpy(config->values[i].desc, desc, strlen(desc)); + result = 0; + break; + } + } + } + + // Key not found, we add a new entry if possible + if (result == -1) + { + if (config->count < config->capacity) + { + // NOTE: Supporting comment line entries + if ((key == NULL) && (text[0] == RINI_LINE_COMMENT_DELIMITER)) + { + config->values[config->count].key[0] = '\0'; + config->values[config->count].text[0] = RINI_LINE_COMMENT_DELIMITER; + if (desc != NULL) for (int i = 0; (i < RINI_MAX_DESC_SIZE) && (desc[i] != '\0'); i++) config->values[config->count].desc[i] = desc[i]; + else config->values[config->count].desc[0] = '\0'; + } + else + { + // NOTE: We do a manual copy to avoid possible overflows on input data + for (int i = 0; (i < RINI_MAX_KEY_SIZE) && (key[i] != '\0'); i++) config->values[config->count].key[i] = key[i]; + for (int i = 0; (i < RINI_MAX_TEXT_SIZE) && (text[i] != '\0'); i++) config->values[config->count].text[i] = text[i]; + for (int i = 0; (i < RINI_MAX_DESC_SIZE) && (desc[i] != '\0'); i++) config->values[config->count].desc[i] = desc[i]; + } + + config->count++; + result = 0; + } + } + + return result; +} + +// Set config value description for existing key +// WARNING: Key must exist to add description, if a description exists, it is updated +int rini_set_config_value_description(rini_config *config, const char *key, const char *desc) +{ + int result = 1; + + for (unsigned int i = 0; i < config->count; i++) + { + if (strcmp(key, config->values[i].key) == 0) // Key found + { + memset(config->values[i].desc, 0, RINI_MAX_DESC_SIZE); + if (desc != NULL) memcpy(config->values[i].desc, desc, strlen(desc)); + result = 0; + break; + } + } + + return result; +} + +//---------------------------------------------------------------------------------- +// Module internal functions declaration +//---------------------------------------------------------------------------------- +// Get string id from a buffer line containing id-value pair +static int rini_read_config_key(const char *buffer, char *key) +{ + int len = 0; + while ((buffer[len] != '\0') && (buffer[len] != ' ') && (buffer[len] != RINI_VALUE_DELIMITER)) len++; // Skip keyentifier + + memcpy(key, buffer, len); + + return len; +} + +// Get config string-value from a buffer line containing id-value pair +static int rini_read_config_value_text(const char *buffer, char *text, char *desc) +{ + char *buffer_ptr = (char *)buffer; + + // Expected config line structure: + // [key][spaces?][delimiter?][spaces?][quot-mark?][textValue][quot-mark?][spaces?][[;][#]description?] + // We need to skip spaces, check for delimiter (if required), skip spaces, and get text value + + while ((buffer_ptr[0] != '\0') && (buffer_ptr[0] != ' ')) buffer_ptr++; // Skip keyentifier + + while ((buffer_ptr[0] != '\0') && (buffer_ptr[0] == ' ')) buffer_ptr++; // Skip line spaces before text value or delimiter + +#if defined(RINI_VALUE_DELIMITER) + if (buffer_ptr[0] == RINI_VALUE_DELIMITER) + { + buffer_ptr++; // Skip delimiter + + while ((buffer_ptr[0] != '\0') && (buffer_ptr[0] == ' ')) buffer_ptr++; // Skip line spaces before text value + } +#endif + + // Now buffer_ptr should be pointing to the start of value + + int len = 0; + while ((buffer_ptr[len] != '\0') && + (buffer_ptr[len] != '\r') && + (buffer_ptr[len] != '\n')) len++; // Get text-value and description length (to the end of line) + + // Now we got the length from text-value start to end of line + + int value_len = len; + int desc_pos = 0; +#if defined(RINI_DESCRIPTION_DELIMITER) + // Scan text looking for text-value description (if used) + for (; desc_pos < len; desc_pos++) + { + if (buffer_ptr[desc_pos] == RINI_DESCRIPTION_DELIMITER) + { + value_len = desc_pos - 1; + while (buffer_ptr[value_len] == ' ') value_len--; + + value_len++; + + desc_pos++; // Skip delimiter and following spaces + while (buffer_ptr[desc_pos] == ' ') desc_pos++; + break; + } + } +#endif + +#if RINI_USE_TEXT_QUOTATION_MARKS + // Remove starting quotation-mark from text (if being used) + if (buffer_ptr[0] == RINI_VALUE_QUOTATION_MARKS) + { + buffer_ptr++; desc_pos--; len--; value_len--; + + // Remove ending quotation-mark from text (if being used) + if (buffer_ptr[value_len - 1] == RINI_VALUE_QUOTATION_MARKS) { value_len--; } + } +#endif + + // Clear text buffers to be updated + memset(text, 0, RINI_MAX_TEXT_SIZE); + memset(desc, 0, RINI_MAX_DESC_SIZE); + + // Copy value-text and description to provided pointers + memcpy(text, buffer_ptr, value_len); + memcpy(desc, buffer_ptr + desc_pos, ((len - desc_pos) > (RINI_MAX_DESC_SIZE - 1))? (RINI_MAX_DESC_SIZE - 1) : (len - desc_pos)); + + return len; +} + +// Convert text to int value (if possible), same as atoi() +static int rini_text_to_int(const char *text) +{ + int value = 0; + int sign = 1; + + if ((text[0] == '+') || (text[0] == '-')) + { + if (text[0] == '-') sign = -1; + text++; + } + + int i = 0; + for (; ((text[i] >= '0') && (text[i] <= '9')); i++) value = value*10 + (int)(text[i] - '0'); + + //if (strlen(text) != i) // Text starts with numbers but contains some text after that... + + return value*sign; +} + +#endif // RINI_IMPLEMENTATION diff --git a/include/app.h b/include/app.h new file mode 100644 index 0000000..22261b4 --- /dev/null +++ b/include/app.h @@ -0,0 +1,73 @@ +#ifndef APP_H +#define APP_H + +#include +#include + +#include "screen.h" +#include "applicationstate.h" +#include "font.h" +#include "logo.h" +#include "menu.h" +#include "game.h" + +typedef struct { + Config config; + State state; + Display display; + Font font; + LogoState logo; + GameState game; + MenuState menu; +} Application; + +void ConfigApp(Application* app){ + InitConfig(&app->config, "config/config.ini"); + app->state.currentScreen = GetConfigInt(&app->config, "startScreen"); + app->state.running = true; + app->state.gameStarted = false; + app->state.flagFPS = GetConfigInt(&app->config, "showFPS"); + app->display.title = GetConfigString(&app->config, "display_title"); + app->display.width = GetConfigInt(&app->config, "display_width"); + app->display.height = GetConfigInt(&app->config, "display_height"); +} + +void InitApp(Application *app){ + InitFont(&app->font); + InitLogo(&app->logo); + InitMenu(&app->menu); + InitGame(&app->game,&app->display); +} + +void UpdateApp(Application *app){ + while (app->state.running) + { + switch (app->state.currentScreen) + { + case LOGO: + { + UpdateLogo(&app->logo, &app->state); + RenderLogo(&app->logo, &app->state); + }break; + case MENU: + { + UpdateMenu(&app->menu, &app->state); + RenderMenu(&app->menu, &app->state, &app->font, app->display.title); + }break; + case GAMEPLAY: + { + UpdateGame(&app->game, &app->state); + RenderGame(&app->game, &app->state); + }break; + default: break; + } + } +} + +void CleanupApp(Application *app){ + CleanupMenu(&app->menu); + CleanupGame(&app->game); + CleanupFont(&app->font); +} + +#endif //APP_H diff --git a/src/engine/applicationstate.h b/include/applicationstate.h similarity index 100% rename from src/engine/applicationstate.h rename to include/applicationstate.h diff --git a/src/game/background.h b/include/background.h similarity index 97% rename from src/game/background.h rename to include/background.h index dceb670..9d335b8 100644 --- a/src/game/background.h +++ b/include/background.h @@ -1,7 +1,7 @@ #ifndef BACKGROUND_H #define BACKGROUND_H -#include "../engine/sprite.h" -#include "raylib.h" +#include +#include typedef struct { Sprite sprite; diff --git a/src/game/bee.h b/include/bee.h similarity index 95% rename from src/game/bee.h rename to include/bee.h index e90c0c7..4fd5fcd 100644 --- a/src/game/bee.h +++ b/include/bee.h @@ -1,9 +1,9 @@ #ifndef BEE_H #define BEE_H -#include "../engine/sprite.h" -#include "../engine/animation.h" -#include "../engine/physics.h" -#include "../engine/object.h" +#include +#include +#include +#include typedef enum { FLYING diff --git a/src/game/button.h b/include/button.h similarity index 100% rename from src/game/button.h rename to include/button.h diff --git a/src/game/colors.h b/include/colors.h similarity index 100% rename from src/game/colors.h rename to include/colors.h diff --git a/src/game/floor.h b/include/floor.h similarity index 55% rename from src/game/floor.h rename to include/floor.h index 104810e..3b6bf1a 100644 --- a/src/game/floor.h +++ b/include/floor.h @@ -1,44 +1,46 @@ #ifndef Floor_H #define Floor_H -#include "../engine/sprite.h" +#include -typedef struct { +typedef struct +{ Sprite sprite; Rectangle bounds; } Floor; -Floor CreateFloor(Texture2D texture, float textureWidth){ +Floor CreateFloor(Texture2D texture, float textureWidth) +{ return (Floor){ .sprite = { .texture = texture, - .frameSize = {textureWidth,textureWidth}, - .sourceRec = { 0.0f, 0.0f, textureWidth, textureWidth}, + .frameSize = {textureWidth, textureWidth}, + .sourceRec = {0.0f, 0.0f, textureWidth, textureWidth}, .destRec = {0.0f, 0.0f, textureWidth, textureWidth}, .origin = {textureWidth / 2, textureWidth / 2}, .rotation = 0.0f, - .color = WHITE - }, - .bounds = {0.0f,0.0f,0.0f,0.0f} - }; + .color = WHITE}, + .bounds = {0.0f, 0.0f, 0.0f, 0.0f}}; } -void UpdateFloor(Floor *floor, const Camera2D *camera){ +void UpdateFloor(Floor *floor, const Camera2D *camera) +{ floor->bounds.x = (int)((camera->target.x - (GetScreenWidth() / 2.0f / camera->zoom)) / floor->sprite.frameSize.x) - 1; floor->bounds.width = (int)((camera->target.x + (GetScreenWidth() / 2.0f / camera->zoom)) / floor->sprite.frameSize.x) + 1; floor->bounds.y = (int)((camera->target.y - (GetScreenHeight() / 2.0f / camera->zoom)) / floor->sprite.frameSize.y) - 1; floor->bounds.height = (int)((camera->target.y + (GetScreenHeight() / 2.0f / camera->zoom)) / floor->sprite.frameSize.y) + 1; } -void RenderFloor(Floor *floor){ +void RenderFloor(Floor *floor) +{ for (int y = (int)floor->bounds.y; y <= (int)floor->bounds.height; y++) + { + for (int x = (int)floor->bounds.x; x <= (int)floor->bounds.width; x++) { - for (int x = (int)floor->bounds.x; x <= (int)floor->bounds.width; x++) - { - Vector2 floorPosition = {x * floor->sprite.frameSize.x, y * floor->sprite.frameSize.y}; - UpdateSpriteDestRec(&floor->sprite, &floorPosition); - RenderSprite(&floor->sprite); - } + Vector2 floorPosition = {x * floor->sprite.frameSize.x, y * floor->sprite.frameSize.y}; + UpdateSpriteDestRec(&floor->sprite, &floorPosition); + RenderSprite(&floor->sprite); } + } } -#endif //Floor_H +#endif // Floor_H diff --git a/include/flowers.h b/include/flowers.h new file mode 100644 index 0000000..d4e3ecf --- /dev/null +++ b/include/flowers.h @@ -0,0 +1,30 @@ +#ifndef FLOWERS_H +#define FLOWERS_H + +#include + +ObjectArray CreateFlowers(Texture2D texture, float frameWidth, float rotation, float fieldSize) +{ + ObjectArray flowers = CreateObjectArray(&(Object){ + .sprite = { + .texture = texture, + .sourceRec = {0.0f, 0.0f, frameWidth, frameWidth}, + .destRec = {0.0f, 0.0f, frameWidth, frameWidth}, + .origin = {frameWidth / 2, frameWidth / 2}, + .rotation = rotation, + .color = WHITE}, + .position = {0.0f, 0.0f}}, + 10); + + if (!flowers.data) + { + printf("Memory allocation failed.\n"); + exit(1); + } + + SetRandomPositionForObjectArray(&flowers, (Rectangle){-fieldSize, -fieldSize, fieldSize * 2, fieldSize * 2}); + + return flowers; +} + +#endif // FLOWERS_H diff --git a/include/font.h b/include/font.h new file mode 100644 index 0000000..1b8edf4 --- /dev/null +++ b/include/font.h @@ -0,0 +1,13 @@ +#ifndef FONT_H +#define FONT_H + +void InitFont(Font *font){ + *font = LoadFont("assets/SproutLandsUI/fonts/pixelFont-7-8x14-sproutLands.fnt"); + SetTextureFilter(font->texture, TEXTURE_FILTER_POINT); +} + +void CleanupFont(Font *font){ + UnloadFont(*font); +} + +#endif //FONT_H diff --git a/src/game/game.h b/include/game.h similarity index 54% rename from src/game/game.h rename to include/game.h index dd40816..d85a7df 100644 --- a/src/game/game.h +++ b/include/game.h @@ -1,12 +1,14 @@ #ifndef GAME_H #define GAME_H -#include "../engine/object.h" -#include "../engine/camera.h" -#include "../engine/input.h" -#include "../engine/postfx.h" -#include "../engine/soundtrack.h" -#include "../engine/asset.h" -#include "../engine/applicationstate.h" + +#include +#include +#include +#include +#include +#include + +#include "applicationstate.h" #include "gameassets.h" #include "floor.h" #include "player.h" @@ -14,7 +16,8 @@ #include "hive.h" #include "flowers.h" -typedef struct { +typedef struct +{ Assets assets; Texture2D textures[TEX_COUNT]; PostProcessing postFX[SHADER_COUNT]; @@ -30,52 +33,69 @@ typedef struct { Image image; } GameState; -void InitGame(GameState* g, Display *display){ +void InitGame(GameState *g, Display *display) +{ g->assets = (Assets){ .textureAssets = texAssets, .shaderAssets = shaderAssets, - .soundtrackAssets = soundtrackAssets - }; + .soundtrackAssets = soundtrackAssets}; - for (int i = 0; i < TEX_COUNT; i++){ + for (int i = 0; i < TEX_COUNT; i++) + { g->textures[i] = LoadTexture(g->assets.textureAssets[i].path); } - for (int i = 0; i < SHADER_COUNT; i++){ + for (int i = 0; i < SHADER_COUNT; i++) + { g->postFX[i] = CreatePostFX(g->assets.shaderAssets[i].path); } - for (int i = 0; i < SOUNDTRACK_COUNT; i++){ + for (int i = 0; i < SOUNDTRACK_COUNT; i++) + { g->soundtracks[i] = LoadSoundtrack(g->assets.soundtrackAssets[i].path); } // Target texture g->target = LoadRenderTexture(display->width, display->height); - + // Static Entities - g->flowers = CreateFlowers(g->textures[FLOWER], g->assets.textureAssets[FLOWER].frameWidth, g->assets.textureAssets[FLOWER].rotation, 50.0f); + g->flowers = CreateFlowers(g->textures[FLOWER], g->assets.textureAssets[FLOWER].frameWidth, g->assets.textureAssets[FLOWER].rotation, 50.0f); g->floor = CreateFloor(g->textures[GRASS], g->assets.textureAssets[GRASS].frameWidth); g->hive = CreateHive(g->textures[HIVE], g->assets.textureAssets[HIVE].frameWidth); - + // Animated Entities g->player = CreatePlayer(IDLE_DOWN, HOE, g->textures[PLAYER], g->assets.textureAssets[PLAYER].frameWidth, 4, 2, g->hive.position, g->assets.textureAssets[PLAYER].rotation, 50.0f); g->bee1 = CreateBee(FLYING, g->textures[BEE], g->assets.textureAssets[BEE].frameWidth, 4, 10, g->hive.position, 10.0f, &g->flowers, &g->hive); - + // I/O g->camera = CreateCamera(display->width, display->height, g->player.physics.position, 10.0f); - - - g->mouse = CreateMouse(0.10f, 5.0f, 10.0f, &g->textures[CURSOR]); + g->mouse = CreateMouse(0.10f, 5.0f, 10.0f, &g->textures[CURSOR]); } -void UpdateGame(GameState* g, State *state){ - if (GetFPS() <= 60){ SetTargetFPS(0); } - if (IsKeyPressed(KEY_ESCAPE)){ state->currentScreen = MENU; } - if (WindowShouldClose() && !IsKeyPressed(KEY_ESCAPE)) { state->running = false; } - for (int i = 0; i < SHADER_COUNT; i++){ UpdatePostFX(&g->postFX[i]); } - for (int i = 0; i < SOUNDTRACK_COUNT; i++){ UpdateSoundtrack(&g->soundtracks[i]); } +void UpdateGame(GameState *g, State *state) +{ + if (GetFPS() <= 60) + { + SetTargetFPS(0); + } + if (IsKeyPressed(KEY_ESCAPE)) + { + state->currentScreen = MENU; + } + if (WindowShouldClose() && !IsKeyPressed(KEY_ESCAPE)) + { + state->running = false; + } + for (int i = 0; i < SHADER_COUNT; i++) + { + UpdatePostFX(&g->postFX[i]); + } + for (int i = 0; i < SOUNDTRACK_COUNT; i++) + { + UpdateSoundtrack(&g->soundtracks[i]); + } UpdateMouse(&g->mouse, &g->camera); UpdateCamera2D(&g->camera, &g->player.physics.position, &g->mouse); @@ -84,7 +104,8 @@ void UpdateGame(GameState* g, State *state){ UpdateBee(&g->bee1, &g->flowers, &g->hive); } -void RenderComponents(GameState* g){ +void RenderComponents(GameState *g) +{ RenderFloor(&g->floor); RenderSprite(&g->hive.sprite); RenderObjectArray(&g->flowers); @@ -92,32 +113,48 @@ void RenderComponents(GameState* g){ RenderSprite(&g->bee1.sprite); } -void RenderGame(GameState* g, const State *appState){ +void RenderGame(GameState *g, const State *appState) +{ BeginTextureMode(g->target); - ClearBackground(BLACK); - BeginMode2D(g->camera); - RenderComponents(g); - EndMode2D(); - DrawTextureRec(g->mouse.cursorTexture, (Rectangle){ 0, 0, g->mouse.cursorTexture.width, g->mouse.cursorTexture.height }, (Vector2){ g->mouse.screenPosition.x, g->mouse.screenPosition.y}, WHITE); + ClearBackground(BLACK); + BeginMode2D(g->camera); + RenderComponents(g); + EndMode2D(); + DrawTextureRec(g->mouse.cursorTexture, (Rectangle){0, 0, g->mouse.cursorTexture.width, g->mouse.cursorTexture.height}, (Vector2){g->mouse.screenPosition.x, g->mouse.screenPosition.y}, WHITE); EndTextureMode(); BeginDrawing(); - ClearBackground(BLACK); - for (int i = 0; i < SHADER_COUNT; i++){ - RenderPostFX(&g->postFX[i], &g->target); - } - DrawText( TextFormat("%d", g->player.inventar.seedCount), 10, 10, 50, WHITE); - - if (appState->flagFPS){ - DrawFPS(10, 10); - } + ClearBackground(BLACK); + for (int i = 0; i < SHADER_COUNT; i++) + { + RenderPostFX(&g->postFX[i], &g->target); + } + DrawText(TextFormat("%d", g->player.inventar.seedCount), 10, 10, 50, WHITE); + + if (appState->flagFPS) + { + DrawFPS(10, 10); + } EndDrawing(); } -void CleanupGame(GameState* g){ - for (int i = 0; i < SHADER_COUNT; i++){ UnloadPostFX(&g->postFX[i]); } - for (int i = 0; i < TEX_COUNT; i++){ if (g->textures[i].id > 0) { UnloadTexture(g->textures[i]); } } - for (int i = 0; i < SOUNDTRACK_COUNT; i++){ UnloadMusicStream(g->soundtracks[i].music); } +void CleanupGame(GameState *g) +{ + for (int i = 0; i < SHADER_COUNT; i++) + { + UnloadPostFX(&g->postFX[i]); + } + for (int i = 0; i < TEX_COUNT; i++) + { + if (g->textures[i].id > 0) + { + UnloadTexture(g->textures[i]); + } + } + for (int i = 0; i < SOUNDTRACK_COUNT; i++) + { + UnloadMusicStream(g->soundtracks[i].music); + } UnloadRenderTexture(g->target); free(g->flowers.data); } diff --git a/src/game/gameassets.h b/include/gameassets.h similarity index 88% rename from src/game/gameassets.h rename to include/gameassets.h index ae1a219..1b5bc43 100644 --- a/src/game/gameassets.h +++ b/include/gameassets.h @@ -1,10 +1,11 @@ #ifndef GAMEASSETS_H #define GAMEASSETS_H -#include "../engine/asset.h" +#include //////////////////////////////////////////////////////////////////////////// -typedef enum { +typedef enum +{ PLAYER, PLAYER_ACTIONS, TOOLS, @@ -24,31 +25,29 @@ const TextureAsset texAssets[TEX_COUNT] = { {"assets/character_bee.png", 16.0f, 16.0f, 0.0f}, {"assets/grass.png", 16.0f, 16.0f, 0.0f}, {"assets/hive.png", 32.0f, 32.0f, 0.0f}, - {"assets/SproutLandsUI/Spritesheets/Mouse sprites/TriangleMouseicon1.png", 16.0f, 16.0f, 0.0f } -}; + {"assets/SproutLandsUI/Spritesheets/Mouse sprites/TriangleMouseicon1.png", 16.0f, 16.0f, 0.0f}}; //////////////////////////////////////////////////////////////////////////// -typedef enum { +typedef enum +{ POST_FX, SHADER_COUNT } ShaderID; - const ShaderAsset shaderAssets[SHADER_COUNT] = { - {"shaders/postfx.fs"} -}; + {"shaders/postfx.fs"}}; //////////////////////////////////////////////////////////////////////////// -typedef enum { +typedef enum +{ BEE_AMBIENT, SOUNDTRACK_COUNT } SoundtrackID; const SoundtrackAsset soundtrackAssets[SOUNDTRACK_COUNT] = { - {"assets/beez.mp3"} -}; + {"assets/beez.mp3"}}; //////////////////////////////////////////////////////////////////////////// diff --git a/src/game/hive.h b/include/hive.h similarity index 63% rename from src/game/hive.h rename to include/hive.h index 10b61f0..5c4e9ca 100644 --- a/src/game/hive.h +++ b/include/hive.h @@ -1,19 +1,18 @@ #ifndef HIVE_H #define HIVE_H -#include "../engine/object.h" +#include -Object CreateHive(Texture2D texture, float frameWidth){ - return (Object) { +Object CreateHive(Texture2D texture, float frameWidth) +{ + return (Object){ .sprite = { .texture = texture, .sourceRec = (Rectangle){0.0f, 0.0f, frameWidth, frameWidth}, .destRec = (Rectangle){0.0f, 0.0f, frameWidth, frameWidth}, .origin = (Vector2){frameWidth / 2, frameWidth / 2}, .rotation = 0.0f, - .color = WHITE - }, - .position = (Vector2){0.0f, 0.0f} - }; + .color = WHITE}, + .position = (Vector2){0.0f, 0.0f}}; } #endif // HIVE_H diff --git a/src/game/inventar.h b/include/inventar.h similarity index 100% rename from src/game/inventar.h rename to include/inventar.h diff --git a/src/game/logo.h b/include/logo.h similarity index 98% rename from src/game/logo.h rename to include/logo.h index 8087125..99fbb19 100644 --- a/src/game/logo.h +++ b/include/logo.h @@ -1,7 +1,7 @@ #ifndef LOGO_H #define LOGO_H -#include "../engine/applicationstate.h" +#include "applicationstate.h" typedef struct { int frameCounter; diff --git a/src/game/menu.h b/include/menu.h similarity index 60% rename from src/game/menu.h rename to include/menu.h index eddcd81..0ad7e54 100644 --- a/src/game/menu.h +++ b/include/menu.h @@ -1,17 +1,19 @@ #ifndef MENU_H #define MENU_H -#include "../engine/gui.h" -#include "../engine/applicationstate.h" -#include "../engine/asset.h" -#include "../engine/gamegui.h" +#include +#include +#include +#include +#include + +#include "applicationstate.h" #include "menuassets.h" #include "colors.h" -#include "../engine/input.h" #include "background.h" -#include "../engine/soundtrack.h" -typedef struct { +typedef struct +{ Assets assets; Mouse mouse; Texture2D textures[MENU_TEX_COUNT]; @@ -26,23 +28,26 @@ typedef struct { Background foreground; } MenuState; -void InitMenu(MenuState *m){ +void InitMenu(MenuState *m) +{ m->assets = (Assets){ .textureAssets = menuTextureAssets, .soundAssets = menuSoundAssets, - .soundtrackAssets = menuSoundtrackAssets - }; + .soundtrackAssets = menuSoundtrackAssets}; - for (int i = 0; i < MENU_TEX_COUNT; i++){ + for (int i = 0; i < MENU_TEX_COUNT; i++) + { m->textures[i] = LoadTexture(m->assets.textureAssets[i].path); } - for (int i = 0; i < MENU_SOUND_COUNT; i++){ + for (int i = 0; i < MENU_SOUND_COUNT; i++) + { m->sounds[i] = LoadSound(m->assets.soundAssets[i].path); } - for (int i = 0; i < MENU_SOUNDTRACK_COUNT; i++){ + for (int i = 0; i < MENU_SOUNDTRACK_COUNT; i++) + { m->soundtracks[i] = LoadSoundtrack(m->assets.soundtrackAssets[i].path); } @@ -57,55 +62,54 @@ void InitMenu(MenuState *m){ int screenHeight = GetScreenHeight(); Vector2 screenCenter = {screenWidth / 2, screenHeight / 2}; - - int boardWidth = screenWidth / m->menuScale * 2.5f; float boardScaleFactor = (float)boardWidth / m->assets.textureAssets[MENU_BOARD_TEX].frameWidth; int boardHeight = (int)(m->assets.textureAssets[MENU_BOARD_TEX].frameHeight * boardScaleFactor); - - Vector2 boardSource = {128.0f,0.0f}; + + Vector2 boardSource = {128.0f, 0.0f}; Rectangle boardDestRec = { .x = screenCenter.x - (boardWidth / 2), .y = screenCenter.y - (boardHeight / 2), .width = boardWidth, - .height = boardHeight - }; + .height = boardHeight}; m->board = CreateImageElement(&m->textures[MENU_BOARD_TEX], &m->assets.textureAssets[MENU_BOARD_TEX], &boardSource, &boardDestRec); - + int buttonWidth = screenWidth / m->menuScale; float buttonScaleFactor = (float)buttonWidth / m->assets.textureAssets[MENU_BIG_BUTTONS_TEX].frameWidth; int buttonHeight = (int)(m->assets.textureAssets[MENU_BIG_BUTTONS_TEX].frameHeight * buttonScaleFactor); int buttonSpacing = buttonHeight; - Vector2 playButtonNotPressed = { 0.0f, 96.0f}; - Vector2 quitButtonNotPressed = { 0.0f, 0.0f}; - Vector2 playButtonHover = { 96.0f, 96.0f}; - Vector2 quitButtonHover = { 96.0f, 0.0f}; - Vector2 playButtonPressed = { 192.0f, 96.0f}; - Vector2 quitButtonPressed = { 192.0f, 0.0f}; + Vector2 playButtonNotPressed = {0.0f, 96.0f}; + Vector2 quitButtonNotPressed = {0.0f, 0.0f}; + Vector2 playButtonHover = {96.0f, 96.0f}; + Vector2 quitButtonHover = {96.0f, 0.0f}; + Vector2 playButtonPressed = {192.0f, 96.0f}; + Vector2 quitButtonPressed = {192.0f, 0.0f}; Rectangle playButtonDestRec = { .x = screenCenter.x - (buttonWidth / 2), .y = screenCenter.y - (buttonHeight / 2), .width = buttonWidth, - .height = buttonHeight - }; + .height = buttonHeight}; Rectangle quitButtonDestRec = { .x = screenCenter.x - (buttonWidth / 2), .y = screenCenter.y - (buttonHeight / 2) + buttonSpacing, .width = buttonWidth, - .height = buttonHeight - }; + .height = buttonHeight}; m->playButton = CreateImageButton(NOT_CLICKED, &m->textures[MENU_BIG_BUTTONS_TEX], &m->assets.textureAssets[MENU_BIG_BUTTONS_TEX], &playButtonNotPressed, &playButtonPressed, &playButtonHover, &playButtonDestRec); m->quitButton = CreateImageButton(NOT_CLICKED, &m->textures[MENU_BIG_BUTTONS_TEX], &m->assets.textureAssets[MENU_BIG_BUTTONS_TEX], &quitButtonNotPressed, &quitButtonPressed, &quitButtonHover, &quitButtonDestRec); } -void UpdateMenu(MenuState *m, State *state){ +void UpdateMenu(MenuState *m, State *state) +{ - for (int i = 0; i < MENU_SOUNDTRACK_COUNT; i++){ UpdateSoundtrack(&m->soundtracks[i]); } + for (int i = 0; i < MENU_SOUNDTRACK_COUNT; i++) + { + UpdateSoundtrack(&m->soundtracks[i]); + } UpdateBackground(&m->background); UpdateBackground(&m->midground); @@ -113,48 +117,63 @@ void UpdateMenu(MenuState *m, State *state){ UpdateMouseScreen(&m->mouse); - if (isImageButtonClicked(&m->playButton, m->sounds[HOVER_SOUND], m->sounds[CLICK_SOUND])){ + if (isImageButtonClicked(&m->playButton, m->sounds[HOVER_SOUND], m->sounds[CLICK_SOUND])) + { state->gameStarted = true; state->currentScreen = GAMEPLAY; } - if (isImageButtonClicked(&m->quitButton, m->sounds[HOVER_SOUND], m->sounds[CLICK_SOUND]) || (WindowShouldClose() && !IsKeyPressed(KEY_ESCAPE))) { + if (isImageButtonClicked(&m->quitButton, m->sounds[HOVER_SOUND], m->sounds[CLICK_SOUND]) || (WindowShouldClose() && !IsKeyPressed(KEY_ESCAPE))) + { state->running = false; } - } -void RenderMenu(MenuState *m, State *appState,Font *font, const char* name){ +void RenderMenu(MenuState *m, State *appState, Font *font, const char *title) +{ BeginDrawing(); - ClearBackground(WHITE); - RenderBackground(&m->background); - RenderBackground(&m->midground); - RenderBackground(&m->foreground); - renderImageElement(&m->board); - renderImageButton(&m->playButton); - renderImageButton(&m->quitButton); - int textWidth = MeasureText(name, font->baseSize); - DrawTextEx(*font, name, (Vector2){ GetScreenWidth() / 2 - textWidth / 2, textWidth / 4}, (float)font->baseSize, 2, WHITE); - DrawTexturePro( - m->mouse.cursorTexture, - (Rectangle){ 0, 0, m->mouse.cursorTexture.width, m->mouse.cursorTexture.height}, - (Rectangle){m->mouse.screenPosition.x, m->mouse.screenPosition.y, m->mouse.cursorTexture.width, m->mouse.cursorTexture.height}, - (Vector2){0.0f, 0.0f}, - 0.0f, - WHITE - ); + ClearBackground(WHITE); + RenderBackground(&m->background); + RenderBackground(&m->midground); + RenderBackground(&m->foreground); + renderImageElement(&m->board); + renderImageButton(&m->playButton); + renderImageButton(&m->quitButton); + int textWidth = MeasureText(title, font->baseSize); + DrawTextEx(*font, title, (Vector2){GetScreenWidth() / 2 - textWidth / 2, textWidth / 4}, (float)font->baseSize, 2, WHITE); + DrawTexturePro( + m->mouse.cursorTexture, + (Rectangle){0, 0, m->mouse.cursorTexture.width, m->mouse.cursorTexture.height}, + (Rectangle){m->mouse.screenPosition.x, m->mouse.screenPosition.y, m->mouse.cursorTexture.width, m->mouse.cursorTexture.height}, + (Vector2){0.0f, 0.0f}, + 0.0f, + WHITE); + + if (appState->flagFPS) + { + DrawFPS(10, 10); + } - if (appState->flagFPS){ - DrawFPS(10, 10); - } - EndDrawing(); } -void CleanupMenu(MenuState *m){ - for (int i = 0; i < MENU_SOUND_COUNT; i++){ UnloadSound(m->sounds[i]); } - for (int i = 0; i < MENU_TEX_COUNT; i++){ if (m->textures[i].id > 0) { UnloadTexture(m->textures[i]); } } - for (int i = 0; i < MENU_SOUNDTRACK_COUNT; i++){ UnloadMusicStream(m->soundtracks[i].music); } +void CleanupMenu(MenuState *m) +{ + for (int i = 0; i < MENU_SOUND_COUNT; i++) + { + UnloadSound(m->sounds[i]); + } + for (int i = 0; i < MENU_TEX_COUNT; i++) + { + if (m->textures[i].id > 0) + { + UnloadTexture(m->textures[i]); + } + } + for (int i = 0; i < MENU_SOUNDTRACK_COUNT; i++) + { + UnloadMusicStream(m->soundtracks[i].music); + } } #endif // MENU_H diff --git a/src/game/menuassets.h b/include/menuassets.h similarity index 79% rename from src/game/menuassets.h rename to include/menuassets.h index c1f1962..235cf4b 100644 --- a/src/game/menuassets.h +++ b/include/menuassets.h @@ -1,8 +1,9 @@ #ifndef MENUASSETS_H #define MENUASSETS_H -#include "../engine/asset.h" +#include -typedef enum { +typedef enum +{ MENU_CURSOR_TEX, MENU_BIG_BUTTONS_TEX, MENU_BOARD_TEX, @@ -13,24 +14,24 @@ typedef enum { } MenuTextureID; const TextureAsset menuTextureAssets[MENU_TEX_COUNT] = { - {"assets/SproutLandsUI/Spritesheets/Mouse sprites/TriangleMouseicon1.png", 16.0f, 16.0f, 0.0f }, + {"assets/SproutLandsUI/Spritesheets/Mouse sprites/TriangleMouseicon1.png", 16.0f, 16.0f, 0.0f}, {"assets/SproutLandsUI/Spritesheets/UIBigPlayButtonHover.png", 96.0f, 32.0f, 0.0f}, {"assets/SproutLandsUI/Spritesheets/Settingmenu.png", 128.0f, 144.0f, 0.0f}, {"assets/Clouds/Clouds1/1.png", 576.0f, 324.0f, 0.0f}, {"assets/Clouds/Clouds1/2.png", 576.0f, 324.0f, 0.0f}, - {"assets/Clouds/Clouds1/4.png", 576.0f, 324.0f, 0.0f} -}; + {"assets/Clouds/Clouds1/4.png", 576.0f, 324.0f, 0.0f}}; -typedef enum { +typedef enum +{ MENU_MUSIC, MENU_SOUNDTRACK_COUNT } MenuSoundtrackID; const SoundtrackAsset menuSoundtrackAssets[MENU_SOUNDTRACK_COUNT] = { - {"assets/merrygoround.wav"} -}; + {"assets/merrygoround.wav"}}; -typedef enum { +typedef enum +{ HOVER_SOUND, CLICK_SOUND, MENU_SOUND_COUNT @@ -38,7 +39,6 @@ typedef enum { const SoundAsset menuSoundAssets[MENU_SOUND_COUNT] = { {"assets/audio/WAV/Minimalist1.wav"}, - {"assets/audio/WAV/Abstract1.wav"} -}; + {"assets/audio/WAV/Abstract1.wav"}}; #endif // MENUASSETS_H diff --git a/src/game/player.h b/include/player.h similarity index 55% rename from src/game/player.h rename to include/player.h index bdceee6..0d48220 100644 --- a/src/game/player.h +++ b/include/player.h @@ -1,25 +1,30 @@ #ifndef PLAYER_H #define PLAYER_H -#include "../engine/sprite.h" -#include "../engine/animation.h" -#include "../engine/physics.h" -#include "../engine/input.h" + +#include +#include +#include +#include + #include "inventar.h" -typedef enum { +typedef enum +{ IDLE, WALKING, USING_TOOL } PlayerState; -typedef enum { +typedef enum +{ NO_TOOL, HOE, AXT, CAN } SelectedTool; -typedef enum { +typedef enum +{ IDLE_DOWN, IDLE_UP, IDLE_LEFT, @@ -42,7 +47,8 @@ typedef enum { CAN_LEFT, } PlayerAnimationState; -typedef struct { +typedef struct +{ SelectedTool selectedTool; Sprite sprite; Animation animation; @@ -50,7 +56,8 @@ typedef struct { Inventar inventar; } Player; -Player CreatePlayer(PlayerAnimationState animState, SelectedTool selectedTool, Texture2D texture, float frameWidth, int maxFrame, int framesSpeed, Vector2 startPosition, float rotation, float speed){ +Player CreatePlayer(PlayerAnimationState animState, SelectedTool selectedTool, Texture2D texture, float frameWidth, int maxFrame, int framesSpeed, Vector2 startPosition, float rotation, float speed) +{ return (Player){ .selectedTool = selectedTool, .sprite = { @@ -60,71 +67,80 @@ Player CreatePlayer(PlayerAnimationState animState, SelectedTool selectedTool, T .destRec = {0.0f, 0.0f, frameWidth, frameWidth}, .origin = {frameWidth / 2, frameWidth / 2}, .rotation = rotation, - .color = WHITE - }, - .animation = { - .state = animState, - .currentFrame = 0, - .maxFrame = maxFrame, - .framesCounter = 0, - .framesSpeed = framesSpeed, - .animTimer = 0.0f - }, - .physics = { - .position = startPosition, - .direction = (Vector2){0.0f,0.0f}, - .speed = speed - }, - .inventar = { - .seedCount = 6 - } - }; + .color = WHITE}, + .animation = {.state = animState, .currentFrame = 0, .maxFrame = maxFrame, .framesCounter = 0, .framesSpeed = framesSpeed, .animTimer = 0.0f}, + .physics = {.position = startPosition, .direction = (Vector2){0.0f, 0.0f}, .speed = speed}, + .inventar = {.seedCount = 6}}; } -void UpdatePlayerAnimationState(int *state, SelectedTool *selectedTool, Vector2 *dir) { +void UpdatePlayerAnimationState(int *state, SelectedTool *selectedTool, Vector2 *dir) +{ - if (IsKeyPressed(KEY_ZERO)){ + if (IsKeyPressed(KEY_ZERO)) + { *selectedTool = NO_TOOL; } - if (IsKeyPressed(KEY_ONE)){ + if (IsKeyPressed(KEY_ONE)) + { *selectedTool = HOE; } - if (IsKeyPressed(KEY_TWO)){ + if (IsKeyPressed(KEY_TWO)) + { *selectedTool = AXT; } - if (IsKeyPressed(KEY_THREE)){ + if (IsKeyPressed(KEY_THREE)) + { *selectedTool = CAN; } - if (dir->x == 0 && dir->y == 0) { - switch (*state) { - case WALKING_DOWN: *state = IDLE_DOWN; break; - case WALKING_UP: *state = IDLE_UP; break; - case WALKING_LEFT: *state = IDLE_LEFT; break; - case WALKING_RIGHT: *state = IDLE_RIGHT; break; - default: break; + if (dir->x == 0 && dir->y == 0) + { + switch (*state) + { + case WALKING_DOWN: + *state = IDLE_DOWN; + break; + case WALKING_UP: + *state = IDLE_UP; + break; + case WALKING_LEFT: + *state = IDLE_LEFT; + break; + case WALKING_RIGHT: + *state = IDLE_RIGHT; + break; + default: + break; } - } else { - if (dir->y == 1) *state = WALKING_DOWN; - else if (dir->y == -1) *state = WALKING_UP; - else if (dir->x == -1) *state = WALKING_LEFT; - else if (dir->x == 1) *state = WALKING_RIGHT; + } + else + { + if (dir->y == 1) + *state = WALKING_DOWN; + else if (dir->y == -1) + *state = WALKING_UP; + else if (dir->x == -1) + *state = WALKING_LEFT; + else if (dir->x == 1) + *state = WALKING_RIGHT; } } -void UpdatePlayer(Player* player){ - if (IsKeyPressed(KEY_SPACE) && player->inventar.seedCount >0){ +void UpdatePlayer(Player *player) +{ + if (IsKeyPressed(KEY_SPACE) && player->inventar.seedCount > 0) + { player->inventar.seedCount--; - } + } Vector2 dir = GetDirectionVector(); UpdatePlayerAnimationState(&player->animation.state, &player->selectedTool, &dir); UpdatePhysics(&player->physics, dir); UpdateSpriteDestRec(&player->sprite, &player->physics.position); UpdateAnimation(&player->animation, GetFrameTime()); - UpdateSpriteSourceRec(&player->sprite, &(Vector2){player->sprite.frameSize.x * player->animation.currentFrame, player->sprite.frameSize.y * player->animation.state}); + UpdateSpriteSourceRec(&player->sprite, &(Vector2){player->sprite.frameSize.x * player->animation.currentFrame, player->sprite.frameSize.y * player->animation.state}); } #endif // PLAYER_H diff --git a/include/raymath.h b/include/raymath.h deleted file mode 100644 index a531b3a..0000000 --- a/include/raymath.h +++ /dev/null @@ -1,2966 +0,0 @@ -/********************************************************************************************** -* -* raymath v2.0 - Math functions to work with Vector2, Vector3, Matrix and Quaternions -* -* CONVENTIONS: -* - Matrix structure is defined as row-major (memory layout) but parameters naming AND all -* math operations performed by the library consider the structure as it was column-major -* It is like transposed versions of the matrices are used for all the maths -* It benefits some functions making them cache-friendly and also avoids matrix -* transpositions sometimes required by OpenGL -* Example: In memory order, row0 is [m0 m4 m8 m12] but in semantic math row0 is [m0 m1 m2 m3] -* - Functions are always self-contained, no function use another raymath function inside, -* required code is directly re-implemented inside -* - Functions input parameters are always received by value (2 unavoidable exceptions) -* - Functions use always a "result" variable for return (except C++ operators) -* - Functions are always defined inline -* - Angles are always in radians (DEG2RAD/RAD2DEG macros provided for convenience) -* - No compound literals used to make sure libray is compatible with C++ -* -* CONFIGURATION: -* #define RAYMATH_IMPLEMENTATION -* Generates the implementation of the library into the included file. -* If not defined, the library is in header only mode and can be included in other headers -* or source files without problems. But only ONE file should hold the implementation. -* -* #define RAYMATH_STATIC_INLINE -* Define static inline functions code, so #include header suffices for use. -* This may use up lots of memory. -* -* #define RAYMATH_DISABLE_CPP_OPERATORS -* Disables C++ operator overloads for raymath types. -* -* LICENSE: zlib/libpng -* -* Copyright (c) 2015-2025 Ramon Santamaria (@raysan5) -* -* This software is provided "as-is", without any express or implied warranty. In no event -* will the authors be held liable for any damages arising from the use of this software. -* -* Permission is granted to anyone to use this software for any purpose, including commercial -* applications, and to alter it and redistribute it freely, subject to the following restrictions: -* -* 1. The origin of this software must not be misrepresented; you must not claim that you -* wrote the original software. If you use this software in a product, an acknowledgment -* in the product documentation would be appreciated but is not required. -* -* 2. Altered source versions must be plainly marked as such, and must not be misrepresented -* as being the original software. -* -* 3. This notice may not be removed or altered from any source distribution. -* -**********************************************************************************************/ - -#ifndef RAYMATH_H -#define RAYMATH_H - -#if defined(RAYMATH_IMPLEMENTATION) && defined(RAYMATH_STATIC_INLINE) - #error "Specifying both RAYMATH_IMPLEMENTATION and RAYMATH_STATIC_INLINE is contradictory" -#endif - -// Function specifiers definition -#if defined(RAYMATH_IMPLEMENTATION) - #if defined(_WIN32) && defined(BUILD_LIBTYPE_SHARED) - #define RMAPI __declspec(dllexport) extern inline // We are building raylib as a Win32 shared library (.dll) - #elif defined(BUILD_LIBTYPE_SHARED) - #define RMAPI __attribute__((visibility("default"))) // We are building raylib as a Unix shared library (.so/.dylib) - #elif defined(_WIN32) && defined(USE_LIBTYPE_SHARED) - #define RMAPI __declspec(dllimport) // We are using raylib as a Win32 shared library (.dll) - #else - #define RMAPI extern inline // Provide external definition - #endif -#elif defined(RAYMATH_STATIC_INLINE) - #define RMAPI static inline // Functions may be inlined, no external out-of-line definition -#else - #if defined(__TINYC__) - #define RMAPI static inline // plain inline not supported by tinycc (See issue #435) - #else - #define RMAPI inline // Functions may be inlined or external definition used - #endif -#endif - - -//---------------------------------------------------------------------------------- -// Defines and Macros -//---------------------------------------------------------------------------------- -#ifndef PI - #define PI 3.14159265358979323846f -#endif - -#ifndef EPSILON - #define EPSILON 0.000001f -#endif - -#ifndef DEG2RAD - #define DEG2RAD (PI/180.0f) -#endif - -#ifndef RAD2DEG - #define RAD2DEG (180.0f/PI) -#endif - -// Get float vector for Matrix -#ifndef MatrixToFloat - #define MatrixToFloat(mat) (MatrixToFloatV(mat).v) -#endif - -// Get float vector for Vector3 -#ifndef Vector3ToFloat - #define Vector3ToFloat(vec) (Vector3ToFloatV(vec).v) -#endif - -//---------------------------------------------------------------------------------- -// Types and Structures Definition -//---------------------------------------------------------------------------------- -#if !defined(RL_VECTOR2_TYPE) -// Vector2 type -typedef struct Vector2 { - float x; - float y; -} Vector2; -#define RL_VECTOR2_TYPE -#endif - -#if !defined(RL_VECTOR3_TYPE) -// Vector3 type -typedef struct Vector3 { - float x; - float y; - float z; -} Vector3; -#define RL_VECTOR3_TYPE -#endif - -#if !defined(RL_VECTOR4_TYPE) -// Vector4 type -typedef struct Vector4 { - float x; - float y; - float z; - float w; -} Vector4; -#define RL_VECTOR4_TYPE -#endif - -#if !defined(RL_QUATERNION_TYPE) -// Quaternion type -typedef Vector4 Quaternion; -#define RL_QUATERNION_TYPE -#endif - -#if !defined(RL_MATRIX_TYPE) -// Matrix type (OpenGL style 4x4 - right handed, column major) -typedef struct Matrix { - float m0, m4, m8, m12; // Matrix first row (4 components) - float m1, m5, m9, m13; // Matrix second row (4 components) - float m2, m6, m10, m14; // Matrix third row (4 components) - float m3, m7, m11, m15; // Matrix fourth row (4 components) -} Matrix; -#define RL_MATRIX_TYPE -#endif - -// NOTE: Helper types to be used instead of array return types for *ToFloat functions -typedef struct float3 { - float v[3]; -} float3; - -typedef struct float16 { - float v[16]; -} float16; - -#include // Required for: sinf(), cosf(), tan(), atan2f(), sqrtf(), floor(), fminf(), fmaxf(), fabsf() - -//---------------------------------------------------------------------------------- -// Module Functions Definition - Utils math -//---------------------------------------------------------------------------------- - -// Clamp float value -RMAPI float Clamp(float value, float min, float max) -{ - float result = (value < min)? min : value; - - if (result > max) result = max; - - return result; -} - -// Calculate linear interpolation between two floats -RMAPI float Lerp(float start, float end, float amount) -{ - float result = start + amount*(end - start); - - return result; -} - -// Normalize input value within input range -RMAPI float Normalize(float value, float start, float end) -{ - float result = (value - start)/(end - start); - - return result; -} - -// Remap input value within input range to output range -RMAPI float Remap(float value, float inputStart, float inputEnd, float outputStart, float outputEnd) -{ - float result = (value - inputStart)/(inputEnd - inputStart)*(outputEnd - outputStart) + outputStart; - - return result; -} - -// Wrap input value from min to max -RMAPI float Wrap(float value, float min, float max) -{ - float result = value - (max - min)*floorf((value - min)/(max - min)); - - return result; -} - -// Check whether two given floats are almost equal -RMAPI int FloatEquals(float x, float y) -{ -#if !defined(EPSILON) - #define EPSILON 0.000001f -#endif - - int result = (fabsf(x - y)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(x), fabsf(y)))); - - return result; -} - -//---------------------------------------------------------------------------------- -// Module Functions Definition - Vector2 math -//---------------------------------------------------------------------------------- - -// Vector with components value 0.0f -RMAPI Vector2 Vector2Zero(void) -{ - Vector2 result = { 0.0f, 0.0f }; - - return result; -} - -// Vector with components value 1.0f -RMAPI Vector2 Vector2One(void) -{ - Vector2 result = { 1.0f, 1.0f }; - - return result; -} - -// Add two vectors (v1 + v2) -RMAPI Vector2 Vector2Add(Vector2 v1, Vector2 v2) -{ - Vector2 result = { v1.x + v2.x, v1.y + v2.y }; - - return result; -} - -// Add vector and float value -RMAPI Vector2 Vector2AddValue(Vector2 v, float add) -{ - Vector2 result = { v.x + add, v.y + add }; - - return result; -} - -// Subtract two vectors (v1 - v2) -RMAPI Vector2 Vector2Subtract(Vector2 v1, Vector2 v2) -{ - Vector2 result = { v1.x - v2.x, v1.y - v2.y }; - - return result; -} - -// Subtract vector by float value -RMAPI Vector2 Vector2SubtractValue(Vector2 v, float sub) -{ - Vector2 result = { v.x - sub, v.y - sub }; - - return result; -} - -// Calculate vector length -RMAPI float Vector2Length(Vector2 v) -{ - float result = sqrtf((v.x*v.x) + (v.y*v.y)); - - return result; -} - -// Calculate vector square length -RMAPI float Vector2LengthSqr(Vector2 v) -{ - float result = (v.x*v.x) + (v.y*v.y); - - return result; -} - -// Calculate two vectors dot product -RMAPI float Vector2DotProduct(Vector2 v1, Vector2 v2) -{ - float result = (v1.x*v2.x + v1.y*v2.y); - - return result; -} - -// Calculate two vectors cross product -RMAPI float Vector2CrossProduct(Vector2 v1, Vector2 v2) -{ - float result = (v1.x*v2.y - v1.y*v2.x); - - return result; -} - -// Calculate distance between two vectors -RMAPI float Vector2Distance(Vector2 v1, Vector2 v2) -{ - float result = sqrtf((v1.x - v2.x)*(v1.x - v2.x) + (v1.y - v2.y)*(v1.y - v2.y)); - - return result; -} - -// Calculate square distance between two vectors -RMAPI float Vector2DistanceSqr(Vector2 v1, Vector2 v2) -{ - float result = ((v1.x - v2.x)*(v1.x - v2.x) + (v1.y - v2.y)*(v1.y - v2.y)); - - return result; -} - -// Calculate the signed angle from v1 to v2, relative to the origin (0, 0) -// NOTE: Coordinate system convention: positive X right, positive Y down, -// positive angles appear clockwise, and negative angles appear counterclockwise -RMAPI float Vector2Angle(Vector2 v1, Vector2 v2) -{ - float result = 0.0f; - - float dot = v1.x*v2.x + v1.y*v2.y; - float det = v1.x*v2.y - v1.y*v2.x; - - result = atan2f(det, dot); - - return result; -} - -// Calculate angle defined by a two vectors line -// NOTE: Parameters need to be normalized -// Current implementation should be aligned with glm::angle -RMAPI float Vector2LineAngle(Vector2 start, Vector2 end) -{ - float result = 0.0f; - - // TODO(10/9/2023): Currently angles move clockwise, determine if this is wanted behavior - result = -atan2f(end.y - start.y, end.x - start.x); - - return result; -} - -// Scale vector (multiply by value) -RMAPI Vector2 Vector2Scale(Vector2 v, float scale) -{ - Vector2 result = { v.x*scale, v.y*scale }; - - return result; -} - -// Multiply vector by vector -RMAPI Vector2 Vector2Multiply(Vector2 v1, Vector2 v2) -{ - Vector2 result = { v1.x*v2.x, v1.y*v2.y }; - - return result; -} - -// Negate vector -RMAPI Vector2 Vector2Negate(Vector2 v) -{ - Vector2 result = { -v.x, -v.y }; - - return result; -} - -// Divide vector by vector -RMAPI Vector2 Vector2Divide(Vector2 v1, Vector2 v2) -{ - Vector2 result = { v1.x/v2.x, v1.y/v2.y }; - - return result; -} - -// Normalize provided vector -RMAPI Vector2 Vector2Normalize(Vector2 v) -{ - Vector2 result = { 0 }; - float length = sqrtf((v.x*v.x) + (v.y*v.y)); - - if (length > 0) - { - float ilength = 1.0f/length; - result.x = v.x*ilength; - result.y = v.y*ilength; - } - - return result; -} - -// Transforms a Vector2 by a given Matrix -RMAPI Vector2 Vector2Transform(Vector2 v, Matrix mat) -{ - Vector2 result = { 0 }; - - float x = v.x; - float y = v.y; - float z = 0; - - result.x = mat.m0*x + mat.m4*y + mat.m8*z + mat.m12; - result.y = mat.m1*x + mat.m5*y + mat.m9*z + mat.m13; - - return result; -} - -// Calculate linear interpolation between two vectors -RMAPI Vector2 Vector2Lerp(Vector2 v1, Vector2 v2, float amount) -{ - Vector2 result = { 0 }; - - result.x = v1.x + amount*(v2.x - v1.x); - result.y = v1.y + amount*(v2.y - v1.y); - - return result; -} - -// Calculate reflected vector to normal -RMAPI Vector2 Vector2Reflect(Vector2 v, Vector2 normal) -{ - Vector2 result = { 0 }; - - float dotProduct = (v.x*normal.x + v.y*normal.y); // Dot product - - result.x = v.x - (2.0f*normal.x)*dotProduct; - result.y = v.y - (2.0f*normal.y)*dotProduct; - - return result; -} - -// Get min value for each pair of components -RMAPI Vector2 Vector2Min(Vector2 v1, Vector2 v2) -{ - Vector2 result = { 0 }; - - result.x = fminf(v1.x, v2.x); - result.y = fminf(v1.y, v2.y); - - return result; -} - -// Get max value for each pair of components -RMAPI Vector2 Vector2Max(Vector2 v1, Vector2 v2) -{ - Vector2 result = { 0 }; - - result.x = fmaxf(v1.x, v2.x); - result.y = fmaxf(v1.y, v2.y); - - return result; -} - -// Rotate vector by angle -RMAPI Vector2 Vector2Rotate(Vector2 v, float angle) -{ - Vector2 result = { 0 }; - - float cosres = cosf(angle); - float sinres = sinf(angle); - - result.x = v.x*cosres - v.y*sinres; - result.y = v.x*sinres + v.y*cosres; - - return result; -} - -// Move Vector towards target -RMAPI Vector2 Vector2MoveTowards(Vector2 v, Vector2 target, float maxDistance) -{ - Vector2 result = { 0 }; - - float dx = target.x - v.x; - float dy = target.y - v.y; - float value = (dx*dx) + (dy*dy); - - if ((value == 0) || ((maxDistance >= 0) && (value <= maxDistance*maxDistance))) return target; - - float dist = sqrtf(value); - - result.x = v.x + dx/dist*maxDistance; - result.y = v.y + dy/dist*maxDistance; - - return result; -} - -// Invert the given vector -RMAPI Vector2 Vector2Invert(Vector2 v) -{ - Vector2 result = { 1.0f/v.x, 1.0f/v.y }; - - return result; -} - -// Clamp the components of the vector between -// min and max values specified by the given vectors -RMAPI Vector2 Vector2Clamp(Vector2 v, Vector2 min, Vector2 max) -{ - Vector2 result = { 0 }; - - result.x = fminf(max.x, fmaxf(min.x, v.x)); - result.y = fminf(max.y, fmaxf(min.y, v.y)); - - return result; -} - -// Clamp the magnitude of the vector between two min and max values -RMAPI Vector2 Vector2ClampValue(Vector2 v, float min, float max) -{ - Vector2 result = v; - - float length = (v.x*v.x) + (v.y*v.y); - if (length > 0.0f) - { - length = sqrtf(length); - - float scale = 1; // By default, 1 as the neutral element. - if (length < min) - { - scale = min/length; - } - else if (length > max) - { - scale = max/length; - } - - result.x = v.x*scale; - result.y = v.y*scale; - } - - return result; -} - -// Check whether two given vectors are almost equal -RMAPI int Vector2Equals(Vector2 p, Vector2 q) -{ -#if !defined(EPSILON) - #define EPSILON 0.000001f -#endif - - int result = ((fabsf(p.x - q.x)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.x), fabsf(q.x))))) && - ((fabsf(p.y - q.y)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.y), fabsf(q.y))))); - - return result; -} - -// Compute the direction of a refracted ray -// v: normalized direction of the incoming ray -// n: normalized normal vector of the interface of two optical media -// r: ratio of the refractive index of the medium from where the ray comes -// to the refractive index of the medium on the other side of the surface -RMAPI Vector2 Vector2Refract(Vector2 v, Vector2 n, float r) -{ - Vector2 result = { 0 }; - - float dot = v.x*n.x + v.y*n.y; - float d = 1.0f - r*r*(1.0f - dot*dot); - - if (d >= 0.0f) - { - d = sqrtf(d); - v.x = r*v.x - (r*dot + d)*n.x; - v.y = r*v.y - (r*dot + d)*n.y; - - result = v; - } - - return result; -} - - -//---------------------------------------------------------------------------------- -// Module Functions Definition - Vector3 math -//---------------------------------------------------------------------------------- - -// Vector with components value 0.0f -RMAPI Vector3 Vector3Zero(void) -{ - Vector3 result = { 0.0f, 0.0f, 0.0f }; - - return result; -} - -// Vector with components value 1.0f -RMAPI Vector3 Vector3One(void) -{ - Vector3 result = { 1.0f, 1.0f, 1.0f }; - - return result; -} - -// Add two vectors -RMAPI Vector3 Vector3Add(Vector3 v1, Vector3 v2) -{ - Vector3 result = { v1.x + v2.x, v1.y + v2.y, v1.z + v2.z }; - - return result; -} - -// Add vector and float value -RMAPI Vector3 Vector3AddValue(Vector3 v, float add) -{ - Vector3 result = { v.x + add, v.y + add, v.z + add }; - - return result; -} - -// Subtract two vectors -RMAPI Vector3 Vector3Subtract(Vector3 v1, Vector3 v2) -{ - Vector3 result = { v1.x - v2.x, v1.y - v2.y, v1.z - v2.z }; - - return result; -} - -// Subtract vector by float value -RMAPI Vector3 Vector3SubtractValue(Vector3 v, float sub) -{ - Vector3 result = { v.x - sub, v.y - sub, v.z - sub }; - - return result; -} - -// Multiply vector by scalar -RMAPI Vector3 Vector3Scale(Vector3 v, float scalar) -{ - Vector3 result = { v.x*scalar, v.y*scalar, v.z*scalar }; - - return result; -} - -// Multiply vector by vector -RMAPI Vector3 Vector3Multiply(Vector3 v1, Vector3 v2) -{ - Vector3 result = { v1.x*v2.x, v1.y*v2.y, v1.z*v2.z }; - - return result; -} - -// Calculate two vectors cross product -RMAPI Vector3 Vector3CrossProduct(Vector3 v1, Vector3 v2) -{ - 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 }; - - return result; -} - -// Calculate one vector perpendicular vector -RMAPI Vector3 Vector3Perpendicular(Vector3 v) -{ - Vector3 result = { 0 }; - - float min = fabsf(v.x); - Vector3 cardinalAxis = {1.0f, 0.0f, 0.0f}; - - if (fabsf(v.y) < min) - { - min = fabsf(v.y); - Vector3 tmp = {0.0f, 1.0f, 0.0f}; - cardinalAxis = tmp; - } - - if (fabsf(v.z) < min) - { - Vector3 tmp = {0.0f, 0.0f, 1.0f}; - cardinalAxis = tmp; - } - - // Cross product between vectors - result.x = v.y*cardinalAxis.z - v.z*cardinalAxis.y; - result.y = v.z*cardinalAxis.x - v.x*cardinalAxis.z; - result.z = v.x*cardinalAxis.y - v.y*cardinalAxis.x; - - return result; -} - -// Calculate vector length -RMAPI float Vector3Length(const Vector3 v) -{ - float result = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z); - - return result; -} - -// Calculate vector square length -RMAPI float Vector3LengthSqr(const Vector3 v) -{ - float result = v.x*v.x + v.y*v.y + v.z*v.z; - - return result; -} - -// Calculate two vectors dot product -RMAPI float Vector3DotProduct(Vector3 v1, Vector3 v2) -{ - float result = (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z); - - return result; -} - -// Calculate distance between two vectors -RMAPI float Vector3Distance(Vector3 v1, Vector3 v2) -{ - float result = 0.0f; - - float dx = v2.x - v1.x; - float dy = v2.y - v1.y; - float dz = v2.z - v1.z; - result = sqrtf(dx*dx + dy*dy + dz*dz); - - return result; -} - -// Calculate square distance between two vectors -RMAPI float Vector3DistanceSqr(Vector3 v1, Vector3 v2) -{ - float result = 0.0f; - - float dx = v2.x - v1.x; - float dy = v2.y - v1.y; - float dz = v2.z - v1.z; - result = dx*dx + dy*dy + dz*dz; - - return result; -} - -// Calculate angle between two vectors -RMAPI float Vector3Angle(Vector3 v1, Vector3 v2) -{ - float result = 0.0f; - - 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 }; - float len = sqrtf(cross.x*cross.x + cross.y*cross.y + cross.z*cross.z); - float dot = (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z); - result = atan2f(len, dot); - - return result; -} - -// Negate provided vector (invert direction) -RMAPI Vector3 Vector3Negate(Vector3 v) -{ - Vector3 result = { -v.x, -v.y, -v.z }; - - return result; -} - -// Divide vector by vector -RMAPI Vector3 Vector3Divide(Vector3 v1, Vector3 v2) -{ - Vector3 result = { v1.x/v2.x, v1.y/v2.y, v1.z/v2.z }; - - return result; -} - -// Normalize provided vector -RMAPI Vector3 Vector3Normalize(Vector3 v) -{ - Vector3 result = v; - - float length = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z); - if (length != 0.0f) - { - float ilength = 1.0f/length; - - result.x *= ilength; - result.y *= ilength; - result.z *= ilength; - } - - return result; -} - -//Calculate the projection of the vector v1 on to v2 -RMAPI Vector3 Vector3Project(Vector3 v1, Vector3 v2) -{ - Vector3 result = { 0 }; - - float v1dv2 = (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z); - float v2dv2 = (v2.x*v2.x + v2.y*v2.y + v2.z*v2.z); - - float mag = v1dv2/v2dv2; - - result.x = v2.x*mag; - result.y = v2.y*mag; - result.z = v2.z*mag; - - return result; -} - -//Calculate the rejection of the vector v1 on to v2 -RMAPI Vector3 Vector3Reject(Vector3 v1, Vector3 v2) -{ - Vector3 result = { 0 }; - - float v1dv2 = (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z); - float v2dv2 = (v2.x*v2.x + v2.y*v2.y + v2.z*v2.z); - - float mag = v1dv2/v2dv2; - - result.x = v1.x - (v2.x*mag); - result.y = v1.y - (v2.y*mag); - result.z = v1.z - (v2.z*mag); - - return result; -} - -// Orthonormalize provided vectors -// Makes vectors normalized and orthogonal to each other -// Gram-Schmidt function implementation -RMAPI void Vector3OrthoNormalize(Vector3 *v1, Vector3 *v2) -{ - float length = 0.0f; - float ilength = 0.0f; - - // Vector3Normalize(*v1); - Vector3 v = *v1; - length = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z); - if (length == 0.0f) length = 1.0f; - ilength = 1.0f/length; - v1->x *= ilength; - v1->y *= ilength; - v1->z *= ilength; - - // Vector3CrossProduct(*v1, *v2) - 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 }; - - // Vector3Normalize(vn1); - v = vn1; - length = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z); - if (length == 0.0f) length = 1.0f; - ilength = 1.0f/length; - vn1.x *= ilength; - vn1.y *= ilength; - vn1.z *= ilength; - - // Vector3CrossProduct(vn1, *v1) - 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 }; - - *v2 = vn2; -} - -// Transforms a Vector3 by a given Matrix -RMAPI Vector3 Vector3Transform(Vector3 v, Matrix mat) -{ - Vector3 result = { 0 }; - - float x = v.x; - float y = v.y; - float z = v.z; - - result.x = mat.m0*x + mat.m4*y + mat.m8*z + mat.m12; - result.y = mat.m1*x + mat.m5*y + mat.m9*z + mat.m13; - result.z = mat.m2*x + mat.m6*y + mat.m10*z + mat.m14; - - return result; -} - -// Transform a vector by quaternion rotation -RMAPI Vector3 Vector3RotateByQuaternion(Vector3 v, Quaternion q) -{ - Vector3 result = { 0 }; - - result.x = v.x*(q.x*q.x + q.w*q.w - q.y*q.y - q.z*q.z) + v.y*(2*q.x*q.y - 2*q.w*q.z) + v.z*(2*q.x*q.z + 2*q.w*q.y); - result.y = v.x*(2*q.w*q.z + 2*q.x*q.y) + v.y*(q.w*q.w - q.x*q.x + q.y*q.y - q.z*q.z) + v.z*(-2*q.w*q.x + 2*q.y*q.z); - result.z = v.x*(-2*q.w*q.y + 2*q.x*q.z) + v.y*(2*q.w*q.x + 2*q.y*q.z)+ v.z*(q.w*q.w - q.x*q.x - q.y*q.y + q.z*q.z); - - return result; -} - -// Rotates a vector around an axis -RMAPI Vector3 Vector3RotateByAxisAngle(Vector3 v, Vector3 axis, float angle) -{ - // Using Euler-Rodrigues Formula - // Ref.: https://en.wikipedia.org/w/index.php?title=Euler%E2%80%93Rodrigues_formula - - Vector3 result = v; - - // Vector3Normalize(axis); - float length = sqrtf(axis.x*axis.x + axis.y*axis.y + axis.z*axis.z); - if (length == 0.0f) length = 1.0f; - float ilength = 1.0f/length; - axis.x *= ilength; - axis.y *= ilength; - axis.z *= ilength; - - angle /= 2.0f; - float a = sinf(angle); - float b = axis.x*a; - float c = axis.y*a; - float d = axis.z*a; - a = cosf(angle); - Vector3 w = { b, c, d }; - - // Vector3CrossProduct(w, v) - 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 }; - - // Vector3CrossProduct(w, wv) - 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 }; - - // Vector3Scale(wv, 2*a) - a *= 2; - wv.x *= a; - wv.y *= a; - wv.z *= a; - - // Vector3Scale(wwv, 2) - wwv.x *= 2; - wwv.y *= 2; - wwv.z *= 2; - - result.x += wv.x; - result.y += wv.y; - result.z += wv.z; - - result.x += wwv.x; - result.y += wwv.y; - result.z += wwv.z; - - return result; -} - -// Move Vector towards target -RMAPI Vector3 Vector3MoveTowards(Vector3 v, Vector3 target, float maxDistance) -{ - Vector3 result = { 0 }; - - float dx = target.x - v.x; - float dy = target.y - v.y; - float dz = target.z - v.z; - float value = (dx*dx) + (dy*dy) + (dz*dz); - - if ((value == 0) || ((maxDistance >= 0) && (value <= maxDistance*maxDistance))) return target; - - float dist = sqrtf(value); - - result.x = v.x + dx/dist*maxDistance; - result.y = v.y + dy/dist*maxDistance; - result.z = v.z + dz/dist*maxDistance; - - return result; -} - -// Calculate linear interpolation between two vectors -RMAPI Vector3 Vector3Lerp(Vector3 v1, Vector3 v2, float amount) -{ - Vector3 result = { 0 }; - - result.x = v1.x + amount*(v2.x - v1.x); - result.y = v1.y + amount*(v2.y - v1.y); - result.z = v1.z + amount*(v2.z - v1.z); - - return result; -} - -// Calculate cubic hermite interpolation between two vectors and their tangents -// as described in the GLTF 2.0 specification: https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#interpolation-cubic -RMAPI Vector3 Vector3CubicHermite(Vector3 v1, Vector3 tangent1, Vector3 v2, Vector3 tangent2, float amount) -{ - Vector3 result = { 0 }; - - float amountPow2 = amount*amount; - float amountPow3 = amount*amount*amount; - - result.x = (2*amountPow3 - 3*amountPow2 + 1)*v1.x + (amountPow3 - 2*amountPow2 + amount)*tangent1.x + (-2*amountPow3 + 3*amountPow2)*v2.x + (amountPow3 - amountPow2)*tangent2.x; - result.y = (2*amountPow3 - 3*amountPow2 + 1)*v1.y + (amountPow3 - 2*amountPow2 + amount)*tangent1.y + (-2*amountPow3 + 3*amountPow2)*v2.y + (amountPow3 - amountPow2)*tangent2.y; - result.z = (2*amountPow3 - 3*amountPow2 + 1)*v1.z + (amountPow3 - 2*amountPow2 + amount)*tangent1.z + (-2*amountPow3 + 3*amountPow2)*v2.z + (amountPow3 - amountPow2)*tangent2.z; - - return result; -} - -// Calculate reflected vector to normal -RMAPI Vector3 Vector3Reflect(Vector3 v, Vector3 normal) -{ - Vector3 result = { 0 }; - - // I is the original vector - // N is the normal of the incident plane - // R = I - (2*N*(DotProduct[I, N])) - - float dotProduct = (v.x*normal.x + v.y*normal.y + v.z*normal.z); - - result.x = v.x - (2.0f*normal.x)*dotProduct; - result.y = v.y - (2.0f*normal.y)*dotProduct; - result.z = v.z - (2.0f*normal.z)*dotProduct; - - return result; -} - -// Get min value for each pair of components -RMAPI Vector3 Vector3Min(Vector3 v1, Vector3 v2) -{ - Vector3 result = { 0 }; - - result.x = fminf(v1.x, v2.x); - result.y = fminf(v1.y, v2.y); - result.z = fminf(v1.z, v2.z); - - return result; -} - -// Get max value for each pair of components -RMAPI Vector3 Vector3Max(Vector3 v1, Vector3 v2) -{ - Vector3 result = { 0 }; - - result.x = fmaxf(v1.x, v2.x); - result.y = fmaxf(v1.y, v2.y); - result.z = fmaxf(v1.z, v2.z); - - return result; -} - -// Compute barycenter coordinates (u, v, w) for point p with respect to triangle (a, b, c) -// NOTE: Assumes P is on the plane of the triangle -RMAPI Vector3 Vector3Barycenter(Vector3 p, Vector3 a, Vector3 b, Vector3 c) -{ - Vector3 result = { 0 }; - - Vector3 v0 = { b.x - a.x, b.y - a.y, b.z - a.z }; // Vector3Subtract(b, a) - Vector3 v1 = { c.x - a.x, c.y - a.y, c.z - a.z }; // Vector3Subtract(c, a) - Vector3 v2 = { p.x - a.x, p.y - a.y, p.z - a.z }; // Vector3Subtract(p, a) - float d00 = (v0.x*v0.x + v0.y*v0.y + v0.z*v0.z); // Vector3DotProduct(v0, v0) - float d01 = (v0.x*v1.x + v0.y*v1.y + v0.z*v1.z); // Vector3DotProduct(v0, v1) - float d11 = (v1.x*v1.x + v1.y*v1.y + v1.z*v1.z); // Vector3DotProduct(v1, v1) - float d20 = (v2.x*v0.x + v2.y*v0.y + v2.z*v0.z); // Vector3DotProduct(v2, v0) - float d21 = (v2.x*v1.x + v2.y*v1.y + v2.z*v1.z); // Vector3DotProduct(v2, v1) - - float denom = d00*d11 - d01*d01; - - result.y = (d11*d20 - d01*d21)/denom; - result.z = (d00*d21 - d01*d20)/denom; - result.x = 1.0f - (result.z + result.y); - - return result; -} - -// Projects a Vector3 from screen space into object space -// NOTE: We are avoiding calling other raymath functions despite available -RMAPI Vector3 Vector3Unproject(Vector3 source, Matrix projection, Matrix view) -{ - Vector3 result = { 0 }; - - // Calculate unprojected matrix (multiply view matrix by projection matrix) and invert it - Matrix matViewProj = { // MatrixMultiply(view, projection); - view.m0*projection.m0 + view.m1*projection.m4 + view.m2*projection.m8 + view.m3*projection.m12, - view.m0*projection.m1 + view.m1*projection.m5 + view.m2*projection.m9 + view.m3*projection.m13, - view.m0*projection.m2 + view.m1*projection.m6 + view.m2*projection.m10 + view.m3*projection.m14, - view.m0*projection.m3 + view.m1*projection.m7 + view.m2*projection.m11 + view.m3*projection.m15, - view.m4*projection.m0 + view.m5*projection.m4 + view.m6*projection.m8 + view.m7*projection.m12, - view.m4*projection.m1 + view.m5*projection.m5 + view.m6*projection.m9 + view.m7*projection.m13, - view.m4*projection.m2 + view.m5*projection.m6 + view.m6*projection.m10 + view.m7*projection.m14, - view.m4*projection.m3 + view.m5*projection.m7 + view.m6*projection.m11 + view.m7*projection.m15, - view.m8*projection.m0 + view.m9*projection.m4 + view.m10*projection.m8 + view.m11*projection.m12, - view.m8*projection.m1 + view.m9*projection.m5 + view.m10*projection.m9 + view.m11*projection.m13, - view.m8*projection.m2 + view.m9*projection.m6 + view.m10*projection.m10 + view.m11*projection.m14, - view.m8*projection.m3 + view.m9*projection.m7 + view.m10*projection.m11 + view.m11*projection.m15, - view.m12*projection.m0 + view.m13*projection.m4 + view.m14*projection.m8 + view.m15*projection.m12, - view.m12*projection.m1 + view.m13*projection.m5 + view.m14*projection.m9 + view.m15*projection.m13, - view.m12*projection.m2 + view.m13*projection.m6 + view.m14*projection.m10 + view.m15*projection.m14, - view.m12*projection.m3 + view.m13*projection.m7 + view.m14*projection.m11 + view.m15*projection.m15 }; - - // Calculate inverted matrix -> MatrixInvert(matViewProj); - // Cache the matrix values (speed optimization) - float a00 = matViewProj.m0, a01 = matViewProj.m1, a02 = matViewProj.m2, a03 = matViewProj.m3; - float a10 = matViewProj.m4, a11 = matViewProj.m5, a12 = matViewProj.m6, a13 = matViewProj.m7; - float a20 = matViewProj.m8, a21 = matViewProj.m9, a22 = matViewProj.m10, a23 = matViewProj.m11; - float a30 = matViewProj.m12, a31 = matViewProj.m13, a32 = matViewProj.m14, a33 = matViewProj.m15; - - float b00 = a00*a11 - a01*a10; - float b01 = a00*a12 - a02*a10; - float b02 = a00*a13 - a03*a10; - float b03 = a01*a12 - a02*a11; - float b04 = a01*a13 - a03*a11; - float b05 = a02*a13 - a03*a12; - float b06 = a20*a31 - a21*a30; - float b07 = a20*a32 - a22*a30; - float b08 = a20*a33 - a23*a30; - float b09 = a21*a32 - a22*a31; - float b10 = a21*a33 - a23*a31; - float b11 = a22*a33 - a23*a32; - - // Calculate the invert determinant (inlined to avoid double-caching) - float invDet = 1.0f/(b00*b11 - b01*b10 + b02*b09 + b03*b08 - b04*b07 + b05*b06); - - Matrix matViewProjInv = { - (a11*b11 - a12*b10 + a13*b09)*invDet, - (-a01*b11 + a02*b10 - a03*b09)*invDet, - (a31*b05 - a32*b04 + a33*b03)*invDet, - (-a21*b05 + a22*b04 - a23*b03)*invDet, - (-a10*b11 + a12*b08 - a13*b07)*invDet, - (a00*b11 - a02*b08 + a03*b07)*invDet, - (-a30*b05 + a32*b02 - a33*b01)*invDet, - (a20*b05 - a22*b02 + a23*b01)*invDet, - (a10*b10 - a11*b08 + a13*b06)*invDet, - (-a00*b10 + a01*b08 - a03*b06)*invDet, - (a30*b04 - a31*b02 + a33*b00)*invDet, - (-a20*b04 + a21*b02 - a23*b00)*invDet, - (-a10*b09 + a11*b07 - a12*b06)*invDet, - (a00*b09 - a01*b07 + a02*b06)*invDet, - (-a30*b03 + a31*b01 - a32*b00)*invDet, - (a20*b03 - a21*b01 + a22*b00)*invDet }; - - // Create quaternion from source point - Quaternion quat = { source.x, source.y, source.z, 1.0f }; - - // Multiply quat point by unprojecte matrix - Quaternion qtransformed = { // QuaternionTransform(quat, matViewProjInv) - matViewProjInv.m0*quat.x + matViewProjInv.m4*quat.y + matViewProjInv.m8*quat.z + matViewProjInv.m12*quat.w, - matViewProjInv.m1*quat.x + matViewProjInv.m5*quat.y + matViewProjInv.m9*quat.z + matViewProjInv.m13*quat.w, - matViewProjInv.m2*quat.x + matViewProjInv.m6*quat.y + matViewProjInv.m10*quat.z + matViewProjInv.m14*quat.w, - matViewProjInv.m3*quat.x + matViewProjInv.m7*quat.y + matViewProjInv.m11*quat.z + matViewProjInv.m15*quat.w }; - - // Normalized world points in vectors - result.x = qtransformed.x/qtransformed.w; - result.y = qtransformed.y/qtransformed.w; - result.z = qtransformed.z/qtransformed.w; - - return result; -} - -// Get Vector3 as float array -RMAPI float3 Vector3ToFloatV(Vector3 v) -{ - float3 buffer = { 0 }; - - buffer.v[0] = v.x; - buffer.v[1] = v.y; - buffer.v[2] = v.z; - - return buffer; -} - -// Invert the given vector -RMAPI Vector3 Vector3Invert(Vector3 v) -{ - Vector3 result = { 1.0f/v.x, 1.0f/v.y, 1.0f/v.z }; - - return result; -} - -// Clamp the components of the vector between -// min and max values specified by the given vectors -RMAPI Vector3 Vector3Clamp(Vector3 v, Vector3 min, Vector3 max) -{ - Vector3 result = { 0 }; - - result.x = fminf(max.x, fmaxf(min.x, v.x)); - result.y = fminf(max.y, fmaxf(min.y, v.y)); - result.z = fminf(max.z, fmaxf(min.z, v.z)); - - return result; -} - -// Clamp the magnitude of the vector between two values -RMAPI Vector3 Vector3ClampValue(Vector3 v, float min, float max) -{ - Vector3 result = v; - - float length = (v.x*v.x) + (v.y*v.y) + (v.z*v.z); - if (length > 0.0f) - { - length = sqrtf(length); - - float scale = 1; // By default, 1 as the neutral element. - if (length < min) - { - scale = min/length; - } - else if (length > max) - { - scale = max/length; - } - - result.x = v.x*scale; - result.y = v.y*scale; - result.z = v.z*scale; - } - - return result; -} - -// Check whether two given vectors are almost equal -RMAPI int Vector3Equals(Vector3 p, Vector3 q) -{ -#if !defined(EPSILON) - #define EPSILON 0.000001f -#endif - - int result = ((fabsf(p.x - q.x)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.x), fabsf(q.x))))) && - ((fabsf(p.y - q.y)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.y), fabsf(q.y))))) && - ((fabsf(p.z - q.z)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.z), fabsf(q.z))))); - - return result; -} - -// Compute the direction of a refracted ray -// v: normalized direction of the incoming ray -// n: normalized normal vector of the interface of two optical media -// r: ratio of the refractive index of the medium from where the ray comes -// to the refractive index of the medium on the other side of the surface -RMAPI Vector3 Vector3Refract(Vector3 v, Vector3 n, float r) -{ - Vector3 result = { 0 }; - - float dot = v.x*n.x + v.y*n.y + v.z*n.z; - float d = 1.0f - r*r*(1.0f - dot*dot); - - if (d >= 0.0f) - { - d = sqrtf(d); - v.x = r*v.x - (r*dot + d)*n.x; - v.y = r*v.y - (r*dot + d)*n.y; - v.z = r*v.z - (r*dot + d)*n.z; - - result = v; - } - - return result; -} - - -//---------------------------------------------------------------------------------- -// Module Functions Definition - Vector4 math -//---------------------------------------------------------------------------------- - -RMAPI Vector4 Vector4Zero(void) -{ - Vector4 result = { 0.0f, 0.0f, 0.0f, 0.0f }; - return result; -} - -RMAPI Vector4 Vector4One(void) -{ - Vector4 result = { 1.0f, 1.0f, 1.0f, 1.0f }; - return result; -} - -RMAPI Vector4 Vector4Add(Vector4 v1, Vector4 v2) -{ - Vector4 result = { - v1.x + v2.x, - v1.y + v2.y, - v1.z + v2.z, - v1.w + v2.w - }; - return result; -} - -RMAPI Vector4 Vector4AddValue(Vector4 v, float add) -{ - Vector4 result = { - v.x + add, - v.y + add, - v.z + add, - v.w + add - }; - return result; -} - -RMAPI Vector4 Vector4Subtract(Vector4 v1, Vector4 v2) -{ - Vector4 result = { - v1.x - v2.x, - v1.y - v2.y, - v1.z - v2.z, - v1.w - v2.w - }; - return result; -} - -RMAPI Vector4 Vector4SubtractValue(Vector4 v, float add) -{ - Vector4 result = { - v.x - add, - v.y - add, - v.z - add, - v.w - add - }; - return result; -} - -RMAPI float Vector4Length(Vector4 v) -{ - float result = sqrtf((v.x*v.x) + (v.y*v.y) + (v.z*v.z) + (v.w*v.w)); - return result; -} - -RMAPI float Vector4LengthSqr(Vector4 v) -{ - float result = (v.x*v.x) + (v.y*v.y) + (v.z*v.z) + (v.w*v.w); - return result; -} - -RMAPI float Vector4DotProduct(Vector4 v1, Vector4 v2) -{ - float result = (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z + v1.w*v2.w); - return result; -} - -// Calculate distance between two vectors -RMAPI float Vector4Distance(Vector4 v1, Vector4 v2) -{ - float result = sqrtf( - (v1.x - v2.x)*(v1.x - v2.x) + (v1.y - v2.y)*(v1.y - v2.y) + - (v1.z - v2.z)*(v1.z - v2.z) + (v1.w - v2.w)*(v1.w - v2.w)); - return result; -} - -// Calculate square distance between two vectors -RMAPI float Vector4DistanceSqr(Vector4 v1, Vector4 v2) -{ - float result = - (v1.x - v2.x)*(v1.x - v2.x) + (v1.y - v2.y)*(v1.y - v2.y) + - (v1.z - v2.z)*(v1.z - v2.z) + (v1.w - v2.w)*(v1.w - v2.w); - - return result; -} - -RMAPI Vector4 Vector4Scale(Vector4 v, float scale) -{ - Vector4 result = { v.x*scale, v.y*scale, v.z*scale, v.w*scale }; - return result; -} - -// Multiply vector by vector -RMAPI Vector4 Vector4Multiply(Vector4 v1, Vector4 v2) -{ - Vector4 result = { v1.x*v2.x, v1.y*v2.y, v1.z*v2.z, v1.w*v2.w }; - return result; -} - -// Negate vector -RMAPI Vector4 Vector4Negate(Vector4 v) -{ - Vector4 result = { -v.x, -v.y, -v.z, -v.w }; - return result; -} - -// Divide vector by vector -RMAPI Vector4 Vector4Divide(Vector4 v1, Vector4 v2) -{ - Vector4 result = { v1.x/v2.x, v1.y/v2.y, v1.z/v2.z, v1.w/v2.w }; - return result; -} - -// Normalize provided vector -RMAPI Vector4 Vector4Normalize(Vector4 v) -{ - Vector4 result = { 0 }; - float length = sqrtf((v.x*v.x) + (v.y*v.y) + (v.z*v.z) + (v.w*v.w)); - - if (length > 0) - { - float ilength = 1.0f/length; - result.x = v.x*ilength; - result.y = v.y*ilength; - result.z = v.z*ilength; - result.w = v.w*ilength; - } - - return result; -} - -// Get min value for each pair of components -RMAPI Vector4 Vector4Min(Vector4 v1, Vector4 v2) -{ - Vector4 result = { 0 }; - - result.x = fminf(v1.x, v2.x); - result.y = fminf(v1.y, v2.y); - result.z = fminf(v1.z, v2.z); - result.w = fminf(v1.w, v2.w); - - return result; -} - -// Get max value for each pair of components -RMAPI Vector4 Vector4Max(Vector4 v1, Vector4 v2) -{ - Vector4 result = { 0 }; - - result.x = fmaxf(v1.x, v2.x); - result.y = fmaxf(v1.y, v2.y); - result.z = fmaxf(v1.z, v2.z); - result.w = fmaxf(v1.w, v2.w); - - return result; -} - -// Calculate linear interpolation between two vectors -RMAPI Vector4 Vector4Lerp(Vector4 v1, Vector4 v2, float amount) -{ - Vector4 result = { 0 }; - - result.x = v1.x + amount*(v2.x - v1.x); - result.y = v1.y + amount*(v2.y - v1.y); - result.z = v1.z + amount*(v2.z - v1.z); - result.w = v1.w + amount*(v2.w - v1.w); - - return result; -} - -// Move Vector towards target -RMAPI Vector4 Vector4MoveTowards(Vector4 v, Vector4 target, float maxDistance) -{ - Vector4 result = { 0 }; - - float dx = target.x - v.x; - float dy = target.y - v.y; - float dz = target.z - v.z; - float dw = target.w - v.w; - float value = (dx*dx) + (dy*dy) + (dz*dz) + (dw*dw); - - if ((value == 0) || ((maxDistance >= 0) && (value <= maxDistance*maxDistance))) return target; - - float dist = sqrtf(value); - - result.x = v.x + dx/dist*maxDistance; - result.y = v.y + dy/dist*maxDistance; - result.z = v.z + dz/dist*maxDistance; - result.w = v.w + dw/dist*maxDistance; - - return result; -} - -// Invert the given vector -RMAPI Vector4 Vector4Invert(Vector4 v) -{ - Vector4 result = { 1.0f/v.x, 1.0f/v.y, 1.0f/v.z, 1.0f/v.w }; - return result; -} - -// Check whether two given vectors are almost equal -RMAPI int Vector4Equals(Vector4 p, Vector4 q) -{ -#if !defined(EPSILON) - #define EPSILON 0.000001f -#endif - - int result = ((fabsf(p.x - q.x)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.x), fabsf(q.x))))) && - ((fabsf(p.y - q.y)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.y), fabsf(q.y))))) && - ((fabsf(p.z - q.z)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.z), fabsf(q.z))))) && - ((fabsf(p.w - q.w)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.w), fabsf(q.w))))); - return result; -} - - -//---------------------------------------------------------------------------------- -// Module Functions Definition - Matrix math -//---------------------------------------------------------------------------------- - -// Compute matrix determinant -RMAPI float MatrixDeterminant(Matrix mat) -{ - float result = 0.0f; -/* - // Cache the matrix values (speed optimization) - float a00 = mat.m0, a01 = mat.m1, a02 = mat.m2, a03 = mat.m3; - float a10 = mat.m4, a11 = mat.m5, a12 = mat.m6, a13 = mat.m7; - float a20 = mat.m8, a21 = mat.m9, a22 = mat.m10, a23 = mat.m11; - float a30 = mat.m12, a31 = mat.m13, a32 = mat.m14, a33 = mat.m15; - - // NOTE: It takes 72 multiplication to calculate 4x4 matrix determinant - result = a30*a21*a12*a03 - a20*a31*a12*a03 - a30*a11*a22*a03 + a10*a31*a22*a03 + - a20*a11*a32*a03 - a10*a21*a32*a03 - a30*a21*a02*a13 + a20*a31*a02*a13 + - a30*a01*a22*a13 - a00*a31*a22*a13 - a20*a01*a32*a13 + a00*a21*a32*a13 + - a30*a11*a02*a23 - a10*a31*a02*a23 - a30*a01*a12*a23 + a00*a31*a12*a23 + - a10*a01*a32*a23 - a00*a11*a32*a23 - a20*a11*a02*a33 + a10*a21*a02*a33 + - a20*a01*a12*a33 - a00*a21*a12*a33 - a10*a01*a22*a33 + a00*a11*a22*a33; -*/ - // Using Laplace expansion (https://en.wikipedia.org/wiki/Laplace_expansion), - // previous operation can be simplified to 40 multiplications, decreasing matrix - // size from 4x4 to 2x2 using minors - - // Cache the matrix values (speed optimization) - float m0 = mat.m0, m1 = mat.m1, m2 = mat.m2, m3 = mat.m3; - float m4 = mat.m4, m5 = mat.m5, m6 = mat.m6, m7 = mat.m7; - float m8 = mat.m8, m9 = mat.m9, m10 = mat.m10, m11 = mat.m11; - float m12 = mat.m12, m13 = mat.m13, m14 = mat.m14, m15 = mat.m15; - - result = (m0*((m5*(m10*m15 - m11*m14) - m9*(m6*m15 - m7*m14) + m13*(m6*m11 - m7*m10))) - - m4*((m1*(m10*m15 - m11*m14) - m9*(m2*m15 - m3*m14) + m13*(m2*m11 - m3*m10))) + - m8*((m1*(m6*m15 - m7*m14) - m5*(m2*m15 - m3*m14) + m13*(m2*m7 - m3*m6))) - - m12*((m1*(m6*m11 - m7*m10) - m5*(m2*m11 - m3*m10) + m9*(m2*m7 - m3*m6)))); - - return result; -} - -// Get the trace of the matrix (sum of the values along the diagonal) -RMAPI float MatrixTrace(Matrix mat) -{ - float result = (mat.m0 + mat.m5 + mat.m10 + mat.m15); - - return result; -} - -// Transposes provided matrix -RMAPI Matrix MatrixTranspose(Matrix mat) -{ - Matrix result = { 0 }; - - result.m0 = mat.m0; - result.m1 = mat.m4; - result.m2 = mat.m8; - result.m3 = mat.m12; - result.m4 = mat.m1; - result.m5 = mat.m5; - result.m6 = mat.m9; - result.m7 = mat.m13; - result.m8 = mat.m2; - result.m9 = mat.m6; - result.m10 = mat.m10; - result.m11 = mat.m14; - result.m12 = mat.m3; - result.m13 = mat.m7; - result.m14 = mat.m11; - result.m15 = mat.m15; - - return result; -} - -// Invert provided matrix -RMAPI Matrix MatrixInvert(Matrix mat) -{ - Matrix result = { 0 }; - - // Cache the matrix values (speed optimization) - float a00 = mat.m0, a01 = mat.m1, a02 = mat.m2, a03 = mat.m3; - float a10 = mat.m4, a11 = mat.m5, a12 = mat.m6, a13 = mat.m7; - float a20 = mat.m8, a21 = mat.m9, a22 = mat.m10, a23 = mat.m11; - float a30 = mat.m12, a31 = mat.m13, a32 = mat.m14, a33 = mat.m15; - - float b00 = a00*a11 - a01*a10; - float b01 = a00*a12 - a02*a10; - float b02 = a00*a13 - a03*a10; - float b03 = a01*a12 - a02*a11; - float b04 = a01*a13 - a03*a11; - float b05 = a02*a13 - a03*a12; - float b06 = a20*a31 - a21*a30; - float b07 = a20*a32 - a22*a30; - float b08 = a20*a33 - a23*a30; - float b09 = a21*a32 - a22*a31; - float b10 = a21*a33 - a23*a31; - float b11 = a22*a33 - a23*a32; - - // Calculate the invert determinant (inlined to avoid double-caching) - float invDet = 1.0f/(b00*b11 - b01*b10 + b02*b09 + b03*b08 - b04*b07 + b05*b06); - - result.m0 = (a11*b11 - a12*b10 + a13*b09)*invDet; - result.m1 = (-a01*b11 + a02*b10 - a03*b09)*invDet; - result.m2 = (a31*b05 - a32*b04 + a33*b03)*invDet; - result.m3 = (-a21*b05 + a22*b04 - a23*b03)*invDet; - result.m4 = (-a10*b11 + a12*b08 - a13*b07)*invDet; - result.m5 = (a00*b11 - a02*b08 + a03*b07)*invDet; - result.m6 = (-a30*b05 + a32*b02 - a33*b01)*invDet; - result.m7 = (a20*b05 - a22*b02 + a23*b01)*invDet; - result.m8 = (a10*b10 - a11*b08 + a13*b06)*invDet; - result.m9 = (-a00*b10 + a01*b08 - a03*b06)*invDet; - result.m10 = (a30*b04 - a31*b02 + a33*b00)*invDet; - result.m11 = (-a20*b04 + a21*b02 - a23*b00)*invDet; - result.m12 = (-a10*b09 + a11*b07 - a12*b06)*invDet; - result.m13 = (a00*b09 - a01*b07 + a02*b06)*invDet; - result.m14 = (-a30*b03 + a31*b01 - a32*b00)*invDet; - result.m15 = (a20*b03 - a21*b01 + a22*b00)*invDet; - - return result; -} - -// Get identity matrix -RMAPI Matrix MatrixIdentity(void) -{ - Matrix result = { 1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 1.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 1.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f }; - - return result; -} - -// Add two matrices -RMAPI Matrix MatrixAdd(Matrix left, Matrix right) -{ - Matrix result = { 0 }; - - result.m0 = left.m0 + right.m0; - result.m1 = left.m1 + right.m1; - result.m2 = left.m2 + right.m2; - result.m3 = left.m3 + right.m3; - result.m4 = left.m4 + right.m4; - result.m5 = left.m5 + right.m5; - result.m6 = left.m6 + right.m6; - result.m7 = left.m7 + right.m7; - result.m8 = left.m8 + right.m8; - result.m9 = left.m9 + right.m9; - result.m10 = left.m10 + right.m10; - result.m11 = left.m11 + right.m11; - result.m12 = left.m12 + right.m12; - result.m13 = left.m13 + right.m13; - result.m14 = left.m14 + right.m14; - result.m15 = left.m15 + right.m15; - - return result; -} - -// Subtract two matrices (left - right) -RMAPI Matrix MatrixSubtract(Matrix left, Matrix right) -{ - Matrix result = { 0 }; - - result.m0 = left.m0 - right.m0; - result.m1 = left.m1 - right.m1; - result.m2 = left.m2 - right.m2; - result.m3 = left.m3 - right.m3; - result.m4 = left.m4 - right.m4; - result.m5 = left.m5 - right.m5; - result.m6 = left.m6 - right.m6; - result.m7 = left.m7 - right.m7; - result.m8 = left.m8 - right.m8; - result.m9 = left.m9 - right.m9; - result.m10 = left.m10 - right.m10; - result.m11 = left.m11 - right.m11; - result.m12 = left.m12 - right.m12; - result.m13 = left.m13 - right.m13; - result.m14 = left.m14 - right.m14; - result.m15 = left.m15 - right.m15; - - return result; -} - -// Get two matrix multiplication -// NOTE: When multiplying matrices... the order matters! -RMAPI Matrix MatrixMultiply(Matrix left, Matrix right) -{ - Matrix result = { 0 }; - - result.m0 = left.m0*right.m0 + left.m1*right.m4 + left.m2*right.m8 + left.m3*right.m12; - result.m1 = left.m0*right.m1 + left.m1*right.m5 + left.m2*right.m9 + left.m3*right.m13; - result.m2 = left.m0*right.m2 + left.m1*right.m6 + left.m2*right.m10 + left.m3*right.m14; - result.m3 = left.m0*right.m3 + left.m1*right.m7 + left.m2*right.m11 + left.m3*right.m15; - result.m4 = left.m4*right.m0 + left.m5*right.m4 + left.m6*right.m8 + left.m7*right.m12; - result.m5 = left.m4*right.m1 + left.m5*right.m5 + left.m6*right.m9 + left.m7*right.m13; - result.m6 = left.m4*right.m2 + left.m5*right.m6 + left.m6*right.m10 + left.m7*right.m14; - result.m7 = left.m4*right.m3 + left.m5*right.m7 + left.m6*right.m11 + left.m7*right.m15; - result.m8 = left.m8*right.m0 + left.m9*right.m4 + left.m10*right.m8 + left.m11*right.m12; - result.m9 = left.m8*right.m1 + left.m9*right.m5 + left.m10*right.m9 + left.m11*right.m13; - result.m10 = left.m8*right.m2 + left.m9*right.m6 + left.m10*right.m10 + left.m11*right.m14; - result.m11 = left.m8*right.m3 + left.m9*right.m7 + left.m10*right.m11 + left.m11*right.m15; - result.m12 = left.m12*right.m0 + left.m13*right.m4 + left.m14*right.m8 + left.m15*right.m12; - result.m13 = left.m12*right.m1 + left.m13*right.m5 + left.m14*right.m9 + left.m15*right.m13; - result.m14 = left.m12*right.m2 + left.m13*right.m6 + left.m14*right.m10 + left.m15*right.m14; - result.m15 = left.m12*right.m3 + left.m13*right.m7 + left.m14*right.m11 + left.m15*right.m15; - - return result; -} - -// Get translation matrix -RMAPI Matrix MatrixTranslate(float x, float y, float z) -{ - Matrix result = { 1.0f, 0.0f, 0.0f, x, - 0.0f, 1.0f, 0.0f, y, - 0.0f, 0.0f, 1.0f, z, - 0.0f, 0.0f, 0.0f, 1.0f }; - - return result; -} - -// Create rotation matrix from axis and angle -// NOTE: Angle should be provided in radians -RMAPI Matrix MatrixRotate(Vector3 axis, float angle) -{ - Matrix result = { 0 }; - - float x = axis.x, y = axis.y, z = axis.z; - - float lengthSquared = x*x + y*y + z*z; - - if ((lengthSquared != 1.0f) && (lengthSquared != 0.0f)) - { - float ilength = 1.0f/sqrtf(lengthSquared); - x *= ilength; - y *= ilength; - z *= ilength; - } - - float sinres = sinf(angle); - float cosres = cosf(angle); - float t = 1.0f - cosres; - - result.m0 = x*x*t + cosres; - result.m1 = y*x*t + z*sinres; - result.m2 = z*x*t - y*sinres; - result.m3 = 0.0f; - - result.m4 = x*y*t - z*sinres; - result.m5 = y*y*t + cosres; - result.m6 = z*y*t + x*sinres; - result.m7 = 0.0f; - - result.m8 = x*z*t + y*sinres; - result.m9 = y*z*t - x*sinres; - result.m10 = z*z*t + cosres; - result.m11 = 0.0f; - - result.m12 = 0.0f; - result.m13 = 0.0f; - result.m14 = 0.0f; - result.m15 = 1.0f; - - return result; -} - -// Get x-rotation matrix -// NOTE: Angle must be provided in radians -RMAPI Matrix MatrixRotateX(float angle) -{ - Matrix result = { 1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 1.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 1.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f }; // MatrixIdentity() - - float cosres = cosf(angle); - float sinres = sinf(angle); - - result.m5 = cosres; - result.m6 = sinres; - result.m9 = -sinres; - result.m10 = cosres; - - return result; -} - -// Get y-rotation matrix -// NOTE: Angle must be provided in radians -RMAPI Matrix MatrixRotateY(float angle) -{ - Matrix result = { 1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 1.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 1.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f }; // MatrixIdentity() - - float cosres = cosf(angle); - float sinres = sinf(angle); - - result.m0 = cosres; - result.m2 = -sinres; - result.m8 = sinres; - result.m10 = cosres; - - return result; -} - -// Get z-rotation matrix -// NOTE: Angle must be provided in radians -RMAPI Matrix MatrixRotateZ(float angle) -{ - Matrix result = { 1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 1.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 1.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f }; // MatrixIdentity() - - float cosres = cosf(angle); - float sinres = sinf(angle); - - result.m0 = cosres; - result.m1 = sinres; - result.m4 = -sinres; - result.m5 = cosres; - - return result; -} - - -// Get xyz-rotation matrix -// NOTE: Angle must be provided in radians -RMAPI Matrix MatrixRotateXYZ(Vector3 angle) -{ - Matrix result = { 1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 1.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 1.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f }; // MatrixIdentity() - - float cosz = cosf(-angle.z); - float sinz = sinf(-angle.z); - float cosy = cosf(-angle.y); - float siny = sinf(-angle.y); - float cosx = cosf(-angle.x); - float sinx = sinf(-angle.x); - - result.m0 = cosz*cosy; - result.m1 = (cosz*siny*sinx) - (sinz*cosx); - result.m2 = (cosz*siny*cosx) + (sinz*sinx); - - result.m4 = sinz*cosy; - result.m5 = (sinz*siny*sinx) + (cosz*cosx); - result.m6 = (sinz*siny*cosx) - (cosz*sinx); - - result.m8 = -siny; - result.m9 = cosy*sinx; - result.m10= cosy*cosx; - - return result; -} - -// Get zyx-rotation matrix -// NOTE: Angle must be provided in radians -RMAPI Matrix MatrixRotateZYX(Vector3 angle) -{ - Matrix result = { 0 }; - - float cz = cosf(angle.z); - float sz = sinf(angle.z); - float cy = cosf(angle.y); - float sy = sinf(angle.y); - float cx = cosf(angle.x); - float sx = sinf(angle.x); - - result.m0 = cz*cy; - result.m4 = cz*sy*sx - cx*sz; - result.m8 = sz*sx + cz*cx*sy; - result.m12 = 0; - - result.m1 = cy*sz; - result.m5 = cz*cx + sz*sy*sx; - result.m9 = cx*sz*sy - cz*sx; - result.m13 = 0; - - result.m2 = -sy; - result.m6 = cy*sx; - result.m10 = cy*cx; - result.m14 = 0; - - result.m3 = 0; - result.m7 = 0; - result.m11 = 0; - result.m15 = 1; - - return result; -} - -// Get scaling matrix -RMAPI Matrix MatrixScale(float x, float y, float z) -{ - Matrix result = { x, 0.0f, 0.0f, 0.0f, - 0.0f, y, 0.0f, 0.0f, - 0.0f, 0.0f, z, 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f }; - - return result; -} - -// Get perspective projection matrix -RMAPI Matrix MatrixFrustum(double left, double right, double bottom, double top, double nearPlane, double farPlane) -{ - Matrix result = { 0 }; - - float rl = (float)(right - left); - float tb = (float)(top - bottom); - float fn = (float)(farPlane - nearPlane); - - result.m0 = ((float)nearPlane*2.0f)/rl; - result.m1 = 0.0f; - result.m2 = 0.0f; - result.m3 = 0.0f; - - result.m4 = 0.0f; - result.m5 = ((float)nearPlane*2.0f)/tb; - result.m6 = 0.0f; - result.m7 = 0.0f; - - result.m8 = ((float)right + (float)left)/rl; - result.m9 = ((float)top + (float)bottom)/tb; - result.m10 = -((float)farPlane + (float)nearPlane)/fn; - result.m11 = -1.0f; - - result.m12 = 0.0f; - result.m13 = 0.0f; - result.m14 = -((float)farPlane*(float)nearPlane*2.0f)/fn; - result.m15 = 0.0f; - - return result; -} - -// Get perspective projection matrix -// NOTE: Fovy angle must be provided in radians -RMAPI Matrix MatrixPerspective(double fovY, double aspect, double nearPlane, double farPlane) -{ - Matrix result = { 0 }; - - double top = nearPlane*tan(fovY*0.5); - double bottom = -top; - double right = top*aspect; - double left = -right; - - // MatrixFrustum(-right, right, -top, top, near, far); - float rl = (float)(right - left); - float tb = (float)(top - bottom); - float fn = (float)(farPlane - nearPlane); - - result.m0 = ((float)nearPlane*2.0f)/rl; - result.m5 = ((float)nearPlane*2.0f)/tb; - result.m8 = ((float)right + (float)left)/rl; - result.m9 = ((float)top + (float)bottom)/tb; - result.m10 = -((float)farPlane + (float)nearPlane)/fn; - result.m11 = -1.0f; - result.m14 = -((float)farPlane*(float)nearPlane*2.0f)/fn; - - return result; -} - -// Get orthographic projection matrix -RMAPI Matrix MatrixOrtho(double left, double right, double bottom, double top, double nearPlane, double farPlane) -{ - Matrix result = { 0 }; - - float rl = (float)(right - left); - float tb = (float)(top - bottom); - float fn = (float)(farPlane - nearPlane); - - result.m0 = 2.0f/rl; - result.m1 = 0.0f; - result.m2 = 0.0f; - result.m3 = 0.0f; - result.m4 = 0.0f; - result.m5 = 2.0f/tb; - result.m6 = 0.0f; - result.m7 = 0.0f; - result.m8 = 0.0f; - result.m9 = 0.0f; - result.m10 = -2.0f/fn; - result.m11 = 0.0f; - result.m12 = -((float)left + (float)right)/rl; - result.m13 = -((float)top + (float)bottom)/tb; - result.m14 = -((float)farPlane + (float)nearPlane)/fn; - result.m15 = 1.0f; - - return result; -} - -// Get camera look-at matrix (view matrix) -RMAPI Matrix MatrixLookAt(Vector3 eye, Vector3 target, Vector3 up) -{ - Matrix result = { 0 }; - - float length = 0.0f; - float ilength = 0.0f; - - // Vector3Subtract(eye, target) - Vector3 vz = { eye.x - target.x, eye.y - target.y, eye.z - target.z }; - - // Vector3Normalize(vz) - Vector3 v = vz; - length = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z); - if (length == 0.0f) length = 1.0f; - ilength = 1.0f/length; - vz.x *= ilength; - vz.y *= ilength; - vz.z *= ilength; - - // Vector3CrossProduct(up, vz) - 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 }; - - // Vector3Normalize(x) - v = vx; - length = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z); - if (length == 0.0f) length = 1.0f; - ilength = 1.0f/length; - vx.x *= ilength; - vx.y *= ilength; - vx.z *= ilength; - - // Vector3CrossProduct(vz, vx) - 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 }; - - result.m0 = vx.x; - result.m1 = vy.x; - result.m2 = vz.x; - result.m3 = 0.0f; - result.m4 = vx.y; - result.m5 = vy.y; - result.m6 = vz.y; - result.m7 = 0.0f; - result.m8 = vx.z; - result.m9 = vy.z; - result.m10 = vz.z; - result.m11 = 0.0f; - result.m12 = -(vx.x*eye.x + vx.y*eye.y + vx.z*eye.z); // Vector3DotProduct(vx, eye) - result.m13 = -(vy.x*eye.x + vy.y*eye.y + vy.z*eye.z); // Vector3DotProduct(vy, eye) - result.m14 = -(vz.x*eye.x + vz.y*eye.y + vz.z*eye.z); // Vector3DotProduct(vz, eye) - result.m15 = 1.0f; - - return result; -} - -// Get float array of matrix data -RMAPI float16 MatrixToFloatV(Matrix mat) -{ - float16 result = { 0 }; - - result.v[0] = mat.m0; - result.v[1] = mat.m1; - result.v[2] = mat.m2; - result.v[3] = mat.m3; - result.v[4] = mat.m4; - result.v[5] = mat.m5; - result.v[6] = mat.m6; - result.v[7] = mat.m7; - result.v[8] = mat.m8; - result.v[9] = mat.m9; - result.v[10] = mat.m10; - result.v[11] = mat.m11; - result.v[12] = mat.m12; - result.v[13] = mat.m13; - result.v[14] = mat.m14; - result.v[15] = mat.m15; - - return result; -} - -//---------------------------------------------------------------------------------- -// Module Functions Definition - Quaternion math -//---------------------------------------------------------------------------------- - -// Add two quaternions -RMAPI Quaternion QuaternionAdd(Quaternion q1, Quaternion q2) -{ - Quaternion result = {q1.x + q2.x, q1.y + q2.y, q1.z + q2.z, q1.w + q2.w}; - - return result; -} - -// Add quaternion and float value -RMAPI Quaternion QuaternionAddValue(Quaternion q, float add) -{ - Quaternion result = {q.x + add, q.y + add, q.z + add, q.w + add}; - - return result; -} - -// Subtract two quaternions -RMAPI Quaternion QuaternionSubtract(Quaternion q1, Quaternion q2) -{ - Quaternion result = {q1.x - q2.x, q1.y - q2.y, q1.z - q2.z, q1.w - q2.w}; - - return result; -} - -// Subtract quaternion and float value -RMAPI Quaternion QuaternionSubtractValue(Quaternion q, float sub) -{ - Quaternion result = {q.x - sub, q.y - sub, q.z - sub, q.w - sub}; - - return result; -} - -// Get identity quaternion -RMAPI Quaternion QuaternionIdentity(void) -{ - Quaternion result = { 0.0f, 0.0f, 0.0f, 1.0f }; - - return result; -} - -// Computes the length of a quaternion -RMAPI float QuaternionLength(Quaternion q) -{ - float result = sqrtf(q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w); - - return result; -} - -// Normalize provided quaternion -RMAPI Quaternion QuaternionNormalize(Quaternion q) -{ - Quaternion result = { 0 }; - - float length = sqrtf(q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w); - if (length == 0.0f) length = 1.0f; - float ilength = 1.0f/length; - - result.x = q.x*ilength; - result.y = q.y*ilength; - result.z = q.z*ilength; - result.w = q.w*ilength; - - return result; -} - -// Invert provided quaternion -RMAPI Quaternion QuaternionInvert(Quaternion q) -{ - Quaternion result = q; - - float lengthSq = q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w; - - if (lengthSq != 0.0f) - { - float invLength = 1.0f/lengthSq; - - result.x *= -invLength; - result.y *= -invLength; - result.z *= -invLength; - result.w *= invLength; - } - - return result; -} - -// Calculate two quaternion multiplication -RMAPI Quaternion QuaternionMultiply(Quaternion q1, Quaternion q2) -{ - Quaternion result = { 0 }; - - float qax = q1.x, qay = q1.y, qaz = q1.z, qaw = q1.w; - float qbx = q2.x, qby = q2.y, qbz = q2.z, qbw = q2.w; - - result.x = qax*qbw + qaw*qbx + qay*qbz - qaz*qby; - result.y = qay*qbw + qaw*qby + qaz*qbx - qax*qbz; - result.z = qaz*qbw + qaw*qbz + qax*qby - qay*qbx; - result.w = qaw*qbw - qax*qbx - qay*qby - qaz*qbz; - - return result; -} - -// Scale quaternion by float value -RMAPI Quaternion QuaternionScale(Quaternion q, float mul) -{ - Quaternion result = { 0 }; - - result.x = q.x*mul; - result.y = q.y*mul; - result.z = q.z*mul; - result.w = q.w*mul; - - return result; -} - -// Divide two quaternions -RMAPI Quaternion QuaternionDivide(Quaternion q1, Quaternion q2) -{ - Quaternion result = { q1.x/q2.x, q1.y/q2.y, q1.z/q2.z, q1.w/q2.w }; - - return result; -} - -// Calculate linear interpolation between two quaternions -RMAPI Quaternion QuaternionLerp(Quaternion q1, Quaternion q2, float amount) -{ - Quaternion result = { 0 }; - - result.x = q1.x + amount*(q2.x - q1.x); - result.y = q1.y + amount*(q2.y - q1.y); - result.z = q1.z + amount*(q2.z - q1.z); - result.w = q1.w + amount*(q2.w - q1.w); - - return result; -} - -// Calculate slerp-optimized interpolation between two quaternions -RMAPI Quaternion QuaternionNlerp(Quaternion q1, Quaternion q2, float amount) -{ - Quaternion result = { 0 }; - - // QuaternionLerp(q1, q2, amount) - result.x = q1.x + amount*(q2.x - q1.x); - result.y = q1.y + amount*(q2.y - q1.y); - result.z = q1.z + amount*(q2.z - q1.z); - result.w = q1.w + amount*(q2.w - q1.w); - - // QuaternionNormalize(q); - Quaternion q = result; - float length = sqrtf(q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w); - if (length == 0.0f) length = 1.0f; - float ilength = 1.0f/length; - - result.x = q.x*ilength; - result.y = q.y*ilength; - result.z = q.z*ilength; - result.w = q.w*ilength; - - return result; -} - -// Calculates spherical linear interpolation between two quaternions -RMAPI Quaternion QuaternionSlerp(Quaternion q1, Quaternion q2, float amount) -{ - Quaternion result = { 0 }; - -#if !defined(EPSILON) - #define EPSILON 0.000001f -#endif - - float cosHalfTheta = q1.x*q2.x + q1.y*q2.y + q1.z*q2.z + q1.w*q2.w; - - if (cosHalfTheta < 0) - { - q2.x = -q2.x; q2.y = -q2.y; q2.z = -q2.z; q2.w = -q2.w; - cosHalfTheta = -cosHalfTheta; - } - - if (fabsf(cosHalfTheta) >= 1.0f) result = q1; - else if (cosHalfTheta > 0.95f) result = QuaternionNlerp(q1, q2, amount); - else - { - float halfTheta = acosf(cosHalfTheta); - float sinHalfTheta = sqrtf(1.0f - cosHalfTheta*cosHalfTheta); - - if (fabsf(sinHalfTheta) < EPSILON) - { - result.x = (q1.x*0.5f + q2.x*0.5f); - result.y = (q1.y*0.5f + q2.y*0.5f); - result.z = (q1.z*0.5f + q2.z*0.5f); - result.w = (q1.w*0.5f + q2.w*0.5f); - } - else - { - float ratioA = sinf((1 - amount)*halfTheta)/sinHalfTheta; - float ratioB = sinf(amount*halfTheta)/sinHalfTheta; - - result.x = (q1.x*ratioA + q2.x*ratioB); - result.y = (q1.y*ratioA + q2.y*ratioB); - result.z = (q1.z*ratioA + q2.z*ratioB); - result.w = (q1.w*ratioA + q2.w*ratioB); - } - } - - return result; -} - -// Calculate quaternion cubic spline interpolation using Cubic Hermite Spline algorithm -// as described in the GLTF 2.0 specification: https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#interpolation-cubic -RMAPI Quaternion QuaternionCubicHermiteSpline(Quaternion q1, Quaternion outTangent1, Quaternion q2, Quaternion inTangent2, float t) -{ - float t2 = t*t; - float t3 = t2*t; - float h00 = 2*t3 - 3*t2 + 1; - float h10 = t3 - 2*t2 + t; - float h01 = -2*t3 + 3*t2; - float h11 = t3 - t2; - - Quaternion p0 = QuaternionScale(q1, h00); - Quaternion m0 = QuaternionScale(outTangent1, h10); - Quaternion p1 = QuaternionScale(q2, h01); - Quaternion m1 = QuaternionScale(inTangent2, h11); - - Quaternion result = { 0 }; - - result = QuaternionAdd(p0, m0); - result = QuaternionAdd(result, p1); - result = QuaternionAdd(result, m1); - result = QuaternionNormalize(result); - - return result; -} - -// Calculate quaternion based on the rotation from one vector to another -RMAPI Quaternion QuaternionFromVector3ToVector3(Vector3 from, Vector3 to) -{ - Quaternion result = { 0 }; - - float cos2Theta = (from.x*to.x + from.y*to.y + from.z*to.z); // Vector3DotProduct(from, to) - 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) - - result.x = cross.x; - result.y = cross.y; - result.z = cross.z; - result.w = 1.0f + cos2Theta; - - // QuaternionNormalize(q); - // NOTE: Normalize to essentially nlerp the original and identity to 0.5 - Quaternion q = result; - float length = sqrtf(q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w); - if (length == 0.0f) length = 1.0f; - float ilength = 1.0f/length; - - result.x = q.x*ilength; - result.y = q.y*ilength; - result.z = q.z*ilength; - result.w = q.w*ilength; - - return result; -} - -// Get a quaternion for a given rotation matrix -RMAPI Quaternion QuaternionFromMatrix(Matrix mat) -{ - Quaternion result = { 0 }; - - float fourWSquaredMinus1 = mat.m0 + mat.m5 + mat.m10; - float fourXSquaredMinus1 = mat.m0 - mat.m5 - mat.m10; - float fourYSquaredMinus1 = mat.m5 - mat.m0 - mat.m10; - float fourZSquaredMinus1 = mat.m10 - mat.m0 - mat.m5; - - int biggestIndex = 0; - float fourBiggestSquaredMinus1 = fourWSquaredMinus1; - if (fourXSquaredMinus1 > fourBiggestSquaredMinus1) - { - fourBiggestSquaredMinus1 = fourXSquaredMinus1; - biggestIndex = 1; - } - - if (fourYSquaredMinus1 > fourBiggestSquaredMinus1) - { - fourBiggestSquaredMinus1 = fourYSquaredMinus1; - biggestIndex = 2; - } - - if (fourZSquaredMinus1 > fourBiggestSquaredMinus1) - { - fourBiggestSquaredMinus1 = fourZSquaredMinus1; - biggestIndex = 3; - } - - float biggestVal = sqrtf(fourBiggestSquaredMinus1 + 1.0f)*0.5f; - float mult = 0.25f/biggestVal; - - switch (biggestIndex) - { - case 0: - result.w = biggestVal; - result.x = (mat.m6 - mat.m9)*mult; - result.y = (mat.m8 - mat.m2)*mult; - result.z = (mat.m1 - mat.m4)*mult; - break; - case 1: - result.x = biggestVal; - result.w = (mat.m6 - mat.m9)*mult; - result.y = (mat.m1 + mat.m4)*mult; - result.z = (mat.m8 + mat.m2)*mult; - break; - case 2: - result.y = biggestVal; - result.w = (mat.m8 - mat.m2)*mult; - result.x = (mat.m1 + mat.m4)*mult; - result.z = (mat.m6 + mat.m9)*mult; - break; - case 3: - result.z = biggestVal; - result.w = (mat.m1 - mat.m4)*mult; - result.x = (mat.m8 + mat.m2)*mult; - result.y = (mat.m6 + mat.m9)*mult; - break; - } - - return result; -} - -// Get a matrix for a given quaternion -RMAPI Matrix QuaternionToMatrix(Quaternion q) -{ - Matrix result = { 1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 1.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 1.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f }; // MatrixIdentity() - - float a2 = q.x*q.x; - float b2 = q.y*q.y; - float c2 = q.z*q.z; - float ac = q.x*q.z; - float ab = q.x*q.y; - float bc = q.y*q.z; - float ad = q.w*q.x; - float bd = q.w*q.y; - float cd = q.w*q.z; - - result.m0 = 1 - 2*(b2 + c2); - result.m1 = 2*(ab + cd); - result.m2 = 2*(ac - bd); - - result.m4 = 2*(ab - cd); - result.m5 = 1 - 2*(a2 + c2); - result.m6 = 2*(bc + ad); - - result.m8 = 2*(ac + bd); - result.m9 = 2*(bc - ad); - result.m10 = 1 - 2*(a2 + b2); - - return result; -} - -// Get rotation quaternion for an angle and axis -// NOTE: Angle must be provided in radians -RMAPI Quaternion QuaternionFromAxisAngle(Vector3 axis, float angle) -{ - Quaternion result = { 0.0f, 0.0f, 0.0f, 1.0f }; - - float axisLength = sqrtf(axis.x*axis.x + axis.y*axis.y + axis.z*axis.z); - - if (axisLength != 0.0f) - { - angle *= 0.5f; - - float length = 0.0f; - float ilength = 0.0f; - - // Vector3Normalize(axis) - length = axisLength; - if (length == 0.0f) length = 1.0f; - ilength = 1.0f/length; - axis.x *= ilength; - axis.y *= ilength; - axis.z *= ilength; - - float sinres = sinf(angle); - float cosres = cosf(angle); - - result.x = axis.x*sinres; - result.y = axis.y*sinres; - result.z = axis.z*sinres; - result.w = cosres; - - // QuaternionNormalize(q); - Quaternion q = result; - length = sqrtf(q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w); - if (length == 0.0f) length = 1.0f; - ilength = 1.0f/length; - result.x = q.x*ilength; - result.y = q.y*ilength; - result.z = q.z*ilength; - result.w = q.w*ilength; - } - - return result; -} - -// Get the rotation angle and axis for a given quaternion -RMAPI void QuaternionToAxisAngle(Quaternion q, Vector3 *outAxis, float *outAngle) -{ - if (fabsf(q.w) > 1.0f) - { - // QuaternionNormalize(q); - float length = sqrtf(q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w); - if (length == 0.0f) length = 1.0f; - float ilength = 1.0f/length; - - q.x = q.x*ilength; - q.y = q.y*ilength; - q.z = q.z*ilength; - q.w = q.w*ilength; - } - - Vector3 resAxis = { 0.0f, 0.0f, 0.0f }; - float resAngle = 2.0f*acosf(q.w); - float den = sqrtf(1.0f - q.w*q.w); - - if (den > EPSILON) - { - resAxis.x = q.x/den; - resAxis.y = q.y/den; - resAxis.z = q.z/den; - } - else - { - // This occurs when the angle is zero. - // Not a problem: just set an arbitrary normalized axis. - resAxis.x = 1.0f; - } - - *outAxis = resAxis; - *outAngle = resAngle; -} - -// Get the quaternion equivalent to Euler angles -// NOTE: Rotation order is ZYX -RMAPI Quaternion QuaternionFromEuler(float pitch, float yaw, float roll) -{ - Quaternion result = { 0 }; - - float x0 = cosf(pitch*0.5f); - float x1 = sinf(pitch*0.5f); - float y0 = cosf(yaw*0.5f); - float y1 = sinf(yaw*0.5f); - float z0 = cosf(roll*0.5f); - float z1 = sinf(roll*0.5f); - - result.x = x1*y0*z0 - x0*y1*z1; - result.y = x0*y1*z0 + x1*y0*z1; - result.z = x0*y0*z1 - x1*y1*z0; - result.w = x0*y0*z0 + x1*y1*z1; - - return result; -} - -// Get the Euler angles equivalent to quaternion (roll, pitch, yaw) -// NOTE: Angles are returned in a Vector3 struct in radians -RMAPI Vector3 QuaternionToEuler(Quaternion q) -{ - Vector3 result = { 0 }; - - // Roll (x-axis rotation) - float x0 = 2.0f*(q.w*q.x + q.y*q.z); - float x1 = 1.0f - 2.0f*(q.x*q.x + q.y*q.y); - result.x = atan2f(x0, x1); - - // Pitch (y-axis rotation) - float y0 = 2.0f*(q.w*q.y - q.z*q.x); - y0 = y0 > 1.0f ? 1.0f : y0; - y0 = y0 < -1.0f ? -1.0f : y0; - result.y = asinf(y0); - - // Yaw (z-axis rotation) - float z0 = 2.0f*(q.w*q.z + q.x*q.y); - float z1 = 1.0f - 2.0f*(q.y*q.y + q.z*q.z); - result.z = atan2f(z0, z1); - - return result; -} - -// Transform a quaternion given a transformation matrix -RMAPI Quaternion QuaternionTransform(Quaternion q, Matrix mat) -{ - Quaternion result = { 0 }; - - result.x = mat.m0*q.x + mat.m4*q.y + mat.m8*q.z + mat.m12*q.w; - result.y = mat.m1*q.x + mat.m5*q.y + mat.m9*q.z + mat.m13*q.w; - result.z = mat.m2*q.x + mat.m6*q.y + mat.m10*q.z + mat.m14*q.w; - result.w = mat.m3*q.x + mat.m7*q.y + mat.m11*q.z + mat.m15*q.w; - - return result; -} - -// Check whether two given quaternions are almost equal -RMAPI int QuaternionEquals(Quaternion p, Quaternion q) -{ -#if !defined(EPSILON) - #define EPSILON 0.000001f -#endif - - int result = (((fabsf(p.x - q.x)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.x), fabsf(q.x))))) && - ((fabsf(p.y - q.y)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.y), fabsf(q.y))))) && - ((fabsf(p.z - q.z)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.z), fabsf(q.z))))) && - ((fabsf(p.w - q.w)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.w), fabsf(q.w)))))) || - (((fabsf(p.x + q.x)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.x), fabsf(q.x))))) && - ((fabsf(p.y + q.y)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.y), fabsf(q.y))))) && - ((fabsf(p.z + q.z)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.z), fabsf(q.z))))) && - ((fabsf(p.w + q.w)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.w), fabsf(q.w)))))); - - return result; -} - -// Decompose a transformation matrix into its rotational, translational and scaling components -RMAPI void MatrixDecompose(Matrix mat, Vector3 *translation, Quaternion *rotation, Vector3 *scale) -{ - // Extract translation. - translation->x = mat.m12; - translation->y = mat.m13; - translation->z = mat.m14; - - // Extract upper-left for determinant computation - const float a = mat.m0; - const float b = mat.m4; - const float c = mat.m8; - const float d = mat.m1; - const float e = mat.m5; - const float f = mat.m9; - const float g = mat.m2; - const float h = mat.m6; - const float i = mat.m10; - const float A = e*i - f*h; - const float B = f*g - d*i; - const float C = d*h - e*g; - - // Extract scale - const float det = a*A + b*B + c*C; - Vector3 abc = { a, b, c }; - Vector3 def = { d, e, f }; - Vector3 ghi = { g, h, i }; - - float scalex = Vector3Length(abc); - float scaley = Vector3Length(def); - float scalez = Vector3Length(ghi); - Vector3 s = { scalex, scaley, scalez }; - - if (det < 0) s = Vector3Negate(s); - - *scale = s; - - // Remove scale from the matrix if it is not close to zero - Matrix clone = mat; - if (!FloatEquals(det, 0)) - { - clone.m0 /= s.x; - clone.m4 /= s.x; - clone.m8 /= s.x; - clone.m1 /= s.y; - clone.m5 /= s.y; - clone.m9 /= s.y; - clone.m2 /= s.z; - clone.m6 /= s.z; - clone.m10 /= s.z; - - // Extract rotation - *rotation = QuaternionFromMatrix(clone); - } - else - { - // Set to identity if close to zero - *rotation = QuaternionIdentity(); - } -} - -#if defined(__cplusplus) && !defined(RAYMATH_DISABLE_CPP_OPERATORS) - -// Optional C++ math operators -//------------------------------------------------------------------------------- - -// Vector2 operators -static constexpr Vector2 Vector2Zeros = { 0, 0 }; -static constexpr Vector2 Vector2Ones = { 1, 1 }; -static constexpr Vector2 Vector2UnitX = { 1, 0 }; -static constexpr Vector2 Vector2UnitY = { 0, 1 }; - -inline Vector2 operator + (const Vector2& lhs, const Vector2& rhs) -{ - return Vector2Add(lhs, rhs); -} - -inline const Vector2& operator += (Vector2& lhs, const Vector2& rhs) -{ - lhs = Vector2Add(lhs, rhs); - return lhs; -} - -inline Vector2 operator - (const Vector2& lhs, const Vector2& rhs) -{ - return Vector2Subtract(lhs, rhs); -} - -inline const Vector2& operator -= (Vector2& lhs, const Vector2& rhs) -{ - lhs = Vector2Subtract(lhs, rhs); - return lhs; -} - -inline Vector2 operator * (const Vector2& lhs, const float& rhs) -{ - return Vector2Scale(lhs, rhs); -} - -inline const Vector2& operator *= (Vector2& lhs, const float& rhs) -{ - lhs = Vector2Scale(lhs, rhs); - return lhs; -} - -inline Vector2 operator * (const Vector2& lhs, const Vector2& rhs) -{ - return Vector2Multiply(lhs, rhs); -} - -inline const Vector2& operator *= (Vector2& lhs, const Vector2& rhs) -{ - lhs = Vector2Multiply(lhs, rhs); - return lhs; -} - -inline Vector2 operator * (const Vector2& lhs, const Matrix& rhs) -{ - return Vector2Transform(lhs, rhs); -} - -inline const Vector2& operator *= (Vector2& lhs, const Matrix& rhs) -{ - lhs = Vector2Transform(lhs, rhs); - return lhs; -} - -inline Vector2 operator / (const Vector2& lhs, const float& rhs) -{ - return Vector2Scale(lhs, 1.0f/rhs); -} - -inline const Vector2& operator /= (Vector2& lhs, const float& rhs) -{ - lhs = Vector2Scale(lhs, 1.0f/rhs); - return lhs; -} - -inline Vector2 operator / (const Vector2& lhs, const Vector2& rhs) -{ - return Vector2Divide(lhs, rhs); -} - -inline const Vector2& operator /= (Vector2& lhs, const Vector2& rhs) -{ - lhs = Vector2Divide(lhs, rhs); - return lhs; -} - -inline bool operator == (const Vector2& lhs, const Vector2& rhs) -{ - return FloatEquals(lhs.x, rhs.x) && FloatEquals(lhs.y, rhs.y); -} - -inline bool operator != (const Vector2& lhs, const Vector2& rhs) -{ - return !FloatEquals(lhs.x, rhs.x) || !FloatEquals(lhs.y, rhs.y); -} - -// Vector3 operators -static constexpr Vector3 Vector3Zeros = { 0, 0, 0 }; -static constexpr Vector3 Vector3Ones = { 1, 1, 1 }; -static constexpr Vector3 Vector3UnitX = { 1, 0, 0 }; -static constexpr Vector3 Vector3UnitY = { 0, 1, 0 }; -static constexpr Vector3 Vector3UnitZ = { 0, 0, 1 }; - -inline Vector3 operator + (const Vector3& lhs, const Vector3& rhs) -{ - return Vector3Add(lhs, rhs); -} - -inline const Vector3& operator += (Vector3& lhs, const Vector3& rhs) -{ - lhs = Vector3Add(lhs, rhs); - return lhs; -} - -inline Vector3 operator - (const Vector3& lhs, const Vector3& rhs) -{ - return Vector3Subtract(lhs, rhs); -} - -inline const Vector3& operator -= (Vector3& lhs, const Vector3& rhs) -{ - lhs = Vector3Subtract(lhs, rhs); - return lhs; -} - -inline Vector3 operator * (const Vector3& lhs, const float& rhs) -{ - return Vector3Scale(lhs, rhs); -} - -inline const Vector3& operator *= (Vector3& lhs, const float& rhs) -{ - lhs = Vector3Scale(lhs, rhs); - return lhs; -} - -inline Vector3 operator * (const Vector3& lhs, const Vector3& rhs) -{ - return Vector3Multiply(lhs, rhs); -} - -inline const Vector3& operator *= (Vector3& lhs, const Vector3& rhs) -{ - lhs = Vector3Multiply(lhs, rhs); - return lhs; -} - -inline Vector3 operator * (const Vector3& lhs, const Matrix& rhs) -{ - return Vector3Transform(lhs, rhs); -} - -inline const Vector3& operator *= (Vector3& lhs, const Matrix& rhs) -{ - lhs = Vector3Transform(lhs, rhs); - return lhs; -} - -inline Vector3 operator / (const Vector3& lhs, const float& rhs) -{ - return Vector3Scale(lhs, 1.0f/rhs); -} - -inline const Vector3& operator /= (Vector3& lhs, const float& rhs) -{ - lhs = Vector3Scale(lhs, 1.0f/rhs); - return lhs; -} - -inline Vector3 operator / (const Vector3& lhs, const Vector3& rhs) -{ - return Vector3Divide(lhs, rhs); -} - -inline const Vector3& operator /= (Vector3& lhs, const Vector3& rhs) -{ - lhs = Vector3Divide(lhs, rhs); - return lhs; -} - -inline bool operator == (const Vector3& lhs, const Vector3& rhs) -{ - return FloatEquals(lhs.x, rhs.x) && FloatEquals(lhs.y, rhs.y) && FloatEquals(lhs.z, rhs.z); -} - -inline bool operator != (const Vector3& lhs, const Vector3& rhs) -{ - return !FloatEquals(lhs.x, rhs.x) || !FloatEquals(lhs.y, rhs.y) || !FloatEquals(lhs.z, rhs.z); -} - -// Vector4 operators -static constexpr Vector4 Vector4Zeros = { 0, 0, 0, 0 }; -static constexpr Vector4 Vector4Ones = { 1, 1, 1, 1 }; -static constexpr Vector4 Vector4UnitX = { 1, 0, 0, 0 }; -static constexpr Vector4 Vector4UnitY = { 0, 1, 0, 0 }; -static constexpr Vector4 Vector4UnitZ = { 0, 0, 1, 0 }; -static constexpr Vector4 Vector4UnitW = { 0, 0, 0, 1 }; - -inline Vector4 operator + (const Vector4& lhs, const Vector4& rhs) -{ - return Vector4Add(lhs, rhs); -} - -inline const Vector4& operator += (Vector4& lhs, const Vector4& rhs) -{ - lhs = Vector4Add(lhs, rhs); - return lhs; -} - -inline Vector4 operator - (const Vector4& lhs, const Vector4& rhs) -{ - return Vector4Subtract(lhs, rhs); -} - -inline const Vector4& operator -= (Vector4& lhs, const Vector4& rhs) -{ - lhs = Vector4Subtract(lhs, rhs); - return lhs; -} - -inline Vector4 operator * (const Vector4& lhs, const float& rhs) -{ - return Vector4Scale(lhs, rhs); -} - -inline const Vector4& operator *= (Vector4& lhs, const float& rhs) -{ - lhs = Vector4Scale(lhs, rhs); - return lhs; -} - -inline Vector4 operator * (const Vector4& lhs, const Vector4& rhs) -{ - return Vector4Multiply(lhs, rhs); -} - -inline const Vector4& operator *= (Vector4& lhs, const Vector4& rhs) -{ - lhs = Vector4Multiply(lhs, rhs); - return lhs; -} - -inline Vector4 operator / (const Vector4& lhs, const float& rhs) -{ - return Vector4Scale(lhs, 1.0f/rhs); -} - -inline const Vector4& operator /= (Vector4& lhs, const float& rhs) -{ - lhs = Vector4Scale(lhs, 1.0f/rhs); - return lhs; -} - -inline Vector4 operator / (const Vector4& lhs, const Vector4& rhs) -{ - return Vector4Divide(lhs, rhs); -} - -inline const Vector4& operator /= (Vector4& lhs, const Vector4& rhs) -{ - lhs = Vector4Divide(lhs, rhs); - return lhs; -} - -inline bool operator == (const Vector4& lhs, const Vector4& rhs) -{ - return FloatEquals(lhs.x, rhs.x) && FloatEquals(lhs.y, rhs.y) && FloatEquals(lhs.z, rhs.z) && FloatEquals(lhs.w, rhs.w); -} - -inline bool operator != (const Vector4& lhs, const Vector4& rhs) -{ - return !FloatEquals(lhs.x, rhs.x) || !FloatEquals(lhs.y, rhs.y) || !FloatEquals(lhs.z, rhs.z) || !FloatEquals(lhs.w, rhs.w); -} - -// Quaternion operators -static constexpr Quaternion QuaternionZeros = { 0, 0, 0, 0 }; -static constexpr Quaternion QuaternionOnes = { 1, 1, 1, 1 }; -static constexpr Quaternion QuaternionUnitX = { 0, 0, 0, 1 }; - -inline Quaternion operator + (const Quaternion& lhs, const float& rhs) -{ - return QuaternionAddValue(lhs, rhs); -} - -inline const Quaternion& operator += (Quaternion& lhs, const float& rhs) -{ - lhs = QuaternionAddValue(lhs, rhs); - return lhs; -} - -inline Quaternion operator - (const Quaternion& lhs, const float& rhs) -{ - return QuaternionSubtractValue(lhs, rhs); -} - -inline const Quaternion& operator -= (Quaternion& lhs, const float& rhs) -{ - lhs = QuaternionSubtractValue(lhs, rhs); - return lhs; -} - -inline Quaternion operator * (const Quaternion& lhs, const Matrix& rhs) -{ - return QuaternionTransform(lhs, rhs); -} - -inline const Quaternion& operator *= (Quaternion& lhs, const Matrix& rhs) -{ - lhs = QuaternionTransform(lhs, rhs); - return lhs; -} - -// Matrix operators -inline Matrix operator + (const Matrix& lhs, const Matrix& rhs) -{ - return MatrixAdd(lhs, rhs); -} - -inline const Matrix& operator += (Matrix& lhs, const Matrix& rhs) -{ - lhs = MatrixAdd(lhs, rhs); - return lhs; -} - -inline Matrix operator - (const Matrix& lhs, const Matrix& rhs) -{ - return MatrixSubtract(lhs, rhs); -} - -inline const Matrix& operator -= (Matrix& lhs, const Matrix& rhs) -{ - lhs = MatrixSubtract(lhs, rhs); - return lhs; -} - -inline Matrix operator * (const Matrix& lhs, const Matrix& rhs) -{ - return MatrixMultiply(lhs, rhs); -} - -inline const Matrix& operator *= (Matrix& lhs, const Matrix& rhs) -{ - lhs = MatrixMultiply(lhs, rhs); - return lhs; -} -//------------------------------------------------------------------------------- -#endif // C++ operators - -#endif // RAYMATH_H diff --git a/src/engine/screen.h b/include/screen.h similarity index 76% rename from src/engine/screen.h rename to include/screen.h index 8c48788..9110aa5 100644 --- a/src/engine/screen.h +++ b/include/screen.h @@ -3,8 +3,8 @@ typedef enum Screen { LOGO = 0, - MENU, - GAMEPLAY + MENU = 1, + GAMEPLAY = 2 } Screen; #endif // SCREEN_H diff --git a/src/app.h b/src/app.h deleted file mode 100644 index f03ceb3..0000000 --- a/src/app.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef APP_H -#define APP_H - -#include "engine/applicationstate.h" -#include "engine/display.h" -#include "engine/screen.h" -#include "game/logo.h" -#include "game/menu.h" -#include "game/game.h" - -typedef struct { - State state; - Display display; - Font font; - LogoState logo; - GameState game; - MenuState menu; -} Application; - -void InitApp(Application* app){ - app->state.currentScreen = LOGO; - app->state.running = true; - app->state.gameStarted = false; - app->state.flagFPS = true; - app->display = (Display){ - .name = "The Last Hive", - .width = 1280, - .height = 720 - }; - app->font = LoadFont("assets/SproutLandsUI/fonts/pixelFont-7-8x14-sproutLands.fnt"); -} - -#endif //APP_H diff --git a/src/game/flowers.h b/src/game/flowers.h deleted file mode 100644 index db64e8f..0000000 --- a/src/game/flowers.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef FLOWERS_H -#define FLOWERS_H - -#include "../engine/object.h" - -ObjectArray CreateFlowers(Texture2D texture,float frameWidth, float rotation, float fieldSize) { - ObjectArray flowers = CreateObjectArray(&(Object){ - .sprite = { - .texture = texture, - .sourceRec = {0.0f, 0.0f, frameWidth, frameWidth}, - .destRec = {0.0f, 0.0f, frameWidth, frameWidth}, - .origin = {frameWidth / 2, frameWidth / 2}, - .rotation = rotation, - .color = WHITE - }, - .position = {0.0f, 0.0f} - }, 10); - - if (!flowers.data) { - printf("Memory allocation failed.\n"); - exit(1); - } - - SetRandomPositionForObjectArray(&flowers, (Rectangle){-fieldSize, -fieldSize, fieldSize * 2, fieldSize * 2}); - - return flowers; -} - -#endif // FLOWERS_H diff --git a/src/main.c b/src/main.c index a255b3f..ff39f43 100644 --- a/src/main.c +++ b/src/main.c @@ -1,49 +1,15 @@ #include -#include -#include -#include "../include/raymath.h" #include "app.h" int main(void) { Application app; - InitApp(&app); - - InitWindow(app.display.width, app.display.height, app.display.name); + ConfigApp(&app); + InitWindow(app.display.width, app.display.height, app.display.title); InitAudioDevice(); - - SetTextureFilter(app.font.texture, TEXTURE_FILTER_POINT); - SetTraceLogLevel(LOG_NONE); - - InitLogo(&app.logo); - InitMenu(&app.menu); - InitGame(&app.game,&app.display); - - while (app.state.running) - { - switch (app.state.currentScreen) - { - case LOGO: - { - UpdateLogo(&app.logo, &app.state); - RenderLogo(&app.logo, &app.state); - }break; - case MENU: - { - UpdateMenu(&app.menu, &app.state); - RenderMenu(&app.menu, &app.state, &app.font, app.display.name); - }break; - case GAMEPLAY: - { - UpdateGame(&app.game, &app.state); - RenderGame(&app.game, &app.state); - }break; - default: break; - } - } - CleanupMenu(&app.menu); - CleanupGame(&app.game); - UnloadFont(app.font); + InitApp(&app); + UpdateApp(&app); + CleanupApp(&app); CloseWindow(); return 0; }