diff --git a/CMakeOptions.txt b/CMakeOptions.txt index 3c1c12742..32cfb814e 100644 --- a/CMakeOptions.txt +++ b/CMakeOptions.txt @@ -8,7 +8,7 @@ if(EMSCRIPTEN) endif() enum_option(PLATFORM "Desktop;Web;Android;Raspberry Pi;DRM;SDL" "Platform to build for.") -enum_option(OPENGL_VERSION "OFF;4.3;3.3;2.1;1.1;ES 2.0;ES 3.0" "Force a specific OpenGL Version?") +enum_option(OPENGL_VERSION "OFF;4.3;3.3;2.1;1.1;ES 2.0;ES 3.0;Software" "Force a specific OpenGL Version?") # Configuration options option(BUILD_EXAMPLES "Build the examples." ${PROJECT_IS_TOP_LEVEL}) diff --git a/cmake/LibraryConfigurations.cmake b/cmake/LibraryConfigurations.cmake index 98c7f2362..8f127fc23 100644 --- a/cmake/LibraryConfigurations.cmake +++ b/cmake/LibraryConfigurations.cmake @@ -158,6 +158,8 @@ if (NOT ${OPENGL_VERSION} MATCHES "OFF") set(GRAPHICS "GRAPHICS_API_OPENGL_ES2") elseif (${OPENGL_VERSION} MATCHES "ES 3.0") set(GRAPHICS "GRAPHICS_API_OPENGL_ES3") + elseif (${OPENGL_VERSION} MATCHES "Software") + set(GRAPHICS "GRAPHICS_API_OPENGL_11_SOFTWARE") endif () if (NOT "${SUGGESTED_GRAPHICS}" STREQUAL "" AND NOT "${SUGGESTED_GRAPHICS}" STREQUAL "${GRAPHICS}") message(WARNING "You are overriding the suggested GRAPHICS=${SUGGESTED_GRAPHICS} with ${GRAPHICS}! This may fail.") diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index e63c1a7c8..b482df0fc 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -105,6 +105,10 @@ elseif ("${PLATFORM}" STREQUAL "DRM") list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/others/rlgl_standalone.c) list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/others/raylib_opengl_interop.c) +elseif ("${OPENGL_VERSION}" STREQUAL "Software") + list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/others/rlgl_standalone.c) + list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/others/raylib_opengl_interop.c) + elseif (NOT SUPPORT_GESTURES_SYSTEM) # Items requiring gestures system list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/textures/textures_mouse_painting.c) diff --git a/examples/Makefile b/examples/Makefile index ee17bb350..f594e2867 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -355,7 +355,7 @@ ifeq ($(TARGET_PLATFORM),$(filter $(TARGET_PLATFORM),PLATFORM_WEB PLATFORM_WEB_R ifeq ($(BUILD_WEB_WEBGL2),TRUE) LDFLAGS += -sMIN_WEBGL_VERSION=2 -sMAX_WEBGL_VERSION=2 endif - + # Add resources building if required ifeq ($(BUILD_WEB_RESOURCES),TRUE) LDFLAGS += --preload-file $(BUILD_WEB_RESOURCES_PATH) @@ -516,6 +516,7 @@ CORE = \ core/core_custom_logging \ core/core_drop_files \ core/core_high_dpi \ + core/core_input_actions \ core/core_input_gamepad \ core/core_input_gestures \ core/core_input_gestures_testbed \ @@ -540,6 +541,7 @@ CORE = \ SHAPES = \ shapes/shapes_basic_shapes \ shapes/shapes_bouncing_ball \ + shapes/shapes_bullet_hell \ shapes/shapes_circle_sector_drawing \ shapes/shapes_collision_area \ shapes/shapes_colors_palette \ @@ -557,7 +559,8 @@ SHAPES = \ shapes/shapes_ring_drawing \ shapes/shapes_rounded_rectangle_drawing \ shapes/shapes_splines_drawing \ - shapes/shapes_top_down_lights + shapes/shapes_top_down_lights \ + shapes/shapes_dashed_line TEXTURES = \ textures/textures_background_scrolling \ @@ -606,12 +609,14 @@ TEXT = \ MODELS = \ models/models_animation_gpu_skinning \ models/models_animation_playing \ + models/models_basic_voxel \ models/models_billboard_rendering \ models/models_bone_socket \ models/models_box_collisions \ models/models_cubicmap_rendering \ models/models_first_person_maze \ models/models_geometric_shapes \ + models/models_geometry_textures_cube \ models/models_heightmap_rendering \ models/models_loading \ models/models_loading_gltf \ diff --git a/examples/Makefile.Web b/examples/Makefile.Web index a16d57853..e5dad0430 100644 --- a/examples/Makefile.Web +++ b/examples/Makefile.Web @@ -355,7 +355,7 @@ ifeq ($(TARGET_PLATFORM),$(filter $(TARGET_PLATFORM),PLATFORM_WEB PLATFORM_WEB_R ifeq ($(BUILD_WEB_WEBGL2),TRUE) LDFLAGS += -sMIN_WEBGL_VERSION=2 -sMAX_WEBGL_VERSION=2 endif - + # Add resources building if required ifeq ($(BUILD_WEB_RESOURCES),TRUE) LDFLAGS += --preload-file $(BUILD_WEB_RESOURCES_PATH) @@ -516,6 +516,7 @@ CORE = \ core/core_custom_logging \ core/core_drop_files \ core/core_high_dpi \ + core/core_input_actions \ core/core_input_gamepad \ core/core_input_gestures \ core/core_input_gestures_testbed \ @@ -540,6 +541,7 @@ CORE = \ SHAPES = \ shapes/shapes_basic_shapes \ shapes/shapes_bouncing_ball \ + shapes/shapes_bullet_hell \ shapes/shapes_circle_sector_drawing \ shapes/shapes_collision_area \ shapes/shapes_colors_palette \ @@ -557,7 +559,8 @@ SHAPES = \ shapes/shapes_ring_drawing \ shapes/shapes_rounded_rectangle_drawing \ shapes/shapes_splines_drawing \ - shapes/shapes_top_down_lights + shapes/shapes_top_down_lights \ + shapes/shapes_dashed_line TEXTURES = \ textures/textures_background_scrolling \ @@ -606,12 +609,14 @@ TEXT = \ MODELS = \ models/models_animation_gpu_skinning \ models/models_animation_playing \ + models/models_basic_voxel \ models/models_billboard_rendering \ models/models_bone_socket \ models/models_box_collisions \ models/models_cubicmap_rendering \ models/models_first_person_maze \ models/models_geometric_shapes \ + models/models_geometry_textures_cube \ models/models_heightmap_rendering \ models/models_loading \ models/models_loading_gltf \ @@ -732,6 +737,9 @@ core/core_drop_files: core/core_drop_files.c core/core_high_dpi: core/core_high_dpi.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) +core/core_input_actions: core/core_input_actions.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) + core/core_input_gamepad: core/core_input_gamepad.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file core/resources/ps3.png@resources/ps3.png \ @@ -802,6 +810,9 @@ shapes/shapes_basic_shapes: shapes/shapes_basic_shapes.c shapes/shapes_bouncing_ball: shapes/shapes_bouncing_ball.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) +shapes/shapes_bullet_hell: shapes/shapes_bullet_hell.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) + shapes/shapes_circle_sector_drawing: shapes/shapes_circle_sector_drawing.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) @@ -1045,6 +1056,9 @@ models/models_animation_playing: models/models_animation_playing.c --preload-file models/resources/models/iqm/guytex.png@resources/models/iqm/guytex.png \ --preload-file models/resources/models/iqm/guyanim.iqm@resources/models/iqm/guyanim.iqm +models/models_basic_voxel: models/models_basic_voxel.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) + models/models_billboard_rendering: models/models_billboard_rendering.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file models/resources/billboard.png@resources/billboard.png @@ -1072,6 +1086,10 @@ models/models_first_person_maze: models/models_first_person_maze.c models/models_geometric_shapes: models/models_geometric_shapes.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) +models/models_geometry_textures_cube: models/models_geometry_textures_cube.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ + --preload-file models/resources/cubicmap_atlas.png@resources/cubicmap_atlas.png + models/models_heightmap_rendering: models/models_heightmap_rendering.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file models/resources/heightmap.png@resources/heightmap.png diff --git a/examples/README.md b/examples/README.md index 1753a31bc..d52f96280 100644 --- a/examples/README.md +++ b/examples/README.md @@ -17,9 +17,9 @@ You may find it easier to use than other toolchains, especially when it comes to - `zig build [module]` to compile all examples for a module (e.g. `zig build core`) - `zig build [example]` to compile _and run_ a particular example (e.g. `zig build core_basic_window`) -## EXAMPLES COLLECTION [TOTAL: 163] +## EXAMPLES COLLECTION [TOTAL: 166] -### category: core [37] +### category: core [38] Examples using raylib[core](../src/rcore.c) platform functionality like window creation, inputs, drawing modes and system functionality. @@ -62,8 +62,9 @@ Examples using raylib[core](../src/rcore.c) platform functionality like window c | [core_high_dpi](core/core_high_dpi.c) | core_high_dpi | ⭐⭐☆☆ | 5.0 | 5.5 | [Jonathan Marler](https://github.com/marler8997) | | [core_render_texture](core/core_render_texture.c) | core_render_texture | ⭐☆☆☆ | 5.6-dev | 5.6-dev | [Ramon Santamaria](https://github.com/raysan5) | | [core_undo_redo](core/core_undo_redo.c) | core_undo_redo | ⭐⭐⭐☆ | 5.5 | 5.6 | [Ramon Santamaria](https://github.com/raysan5) | +| [core_input_actions](core/core_input_actions.c) | core_input_actions | ⭐⭐☆☆ | 5.5 | 5.6 | [Jett](https://github.com/JettMonstersGoBoom) | -### category: shapes [20] +### category: shapes [21] Examples using raylib shapes drawing functionality, provided by raylib [shapes](../src/rshapes.c) module. @@ -71,6 +72,7 @@ Examples using raylib shapes drawing functionality, provided by raylib [shapes]( |-----------|--------|:-------------------:|:------------------:|:-----------------------:|:----------------------| | [shapes_basic_shapes](shapes/shapes_basic_shapes.c) | shapes_basic_shapes | ⭐☆☆☆ | 1.0 | 4.2 | [Ramon Santamaria](https://github.com/raysan5) | | [shapes_bouncing_ball](shapes/shapes_bouncing_ball.c) | shapes_bouncing_ball | ⭐☆☆☆ | 2.5 | 2.5 | [Ramon Santamaria](https://github.com/raysan5) | +| [shapes_bullet_hell](shapes/shapes_bullet_hell.c) | shapes_bullet_hell | ⭐☆☆☆ | 5.6 | 5.6 | [Zero](https://github.com/zerohorsepower) | | [shapes_colors_palette](shapes/shapes_colors_palette.c) | shapes_colors_palette | ⭐⭐☆☆ | 1.0 | 2.5 | [Ramon Santamaria](https://github.com/raysan5) | | [shapes_logo_raylib](shapes/shapes_logo_raylib.c) | shapes_logo_raylib | ⭐☆☆☆ | 1.0 | 1.0 | [Ramon Santamaria](https://github.com/raysan5) | | [shapes_logo_raylib_anim](shapes/shapes_logo_raylib_anim.c) | shapes_logo_raylib_anim | ⭐⭐☆☆ | 2.5 | 4.0 | [Ramon Santamaria](https://github.com/raysan5) | @@ -89,6 +91,7 @@ Examples using raylib shapes drawing functionality, provided by raylib [shapes]( | [shapes_splines_drawing](shapes/shapes_splines_drawing.c) | shapes_splines_drawing | ⭐⭐⭐☆ | 5.0 | 5.0 | [Ramon Santamaria](https://github.com/raysan5) | | [shapes_digital_clock](shapes/shapes_digital_clock.c) | shapes_digital_clock | ⭐⭐⭐⭐️ | 5.5 | 5.6 | [Hamza RAHAL](https://github.com/hmz-rhl) | | [shapes_double_pendulum](shapes/shapes_double_pendulum.c) | shapes_double_pendulum | ⭐⭐☆☆ | 5.5 | 5.5 | [JoeCheong](https://github.com/Joecheong2006) | +| [shapes_dashed_line](shapes/shapes_dashed_line.c) | shapes_dashed_line | ⭐☆☆☆ | 5.5 | 5.5 | [Luís Almeida](https://github.com/luis605) | ### category: textures [26] @@ -144,7 +147,7 @@ Examples using raylib text functionality, including sprite fonts loading/generat | [text_codepoints_loading](text/text_codepoints_loading.c) | text_codepoints_loading | ⭐⭐⭐☆ | 4.2 | 4.2 | [Ramon Santamaria](https://github.com/raysan5) | | [text_inline_styling](text/text_inline_styling.c) | text_inline_styling | ⭐⭐⭐☆ | 5.6-dev | 5.6-dev | [Wagner Barongello](https://github.com/SultansOfCode) | -### category: models [23] +### category: models [25] Examples using raylib models functionality, including models loading/generation and drawing, provided by raylib [models](../src/rmodels.c) module. @@ -173,6 +176,8 @@ Examples using raylib models functionality, including models loading/generation | [models_animation_gpu_skinning](models/models_animation_gpu_skinning.c) | models_animation_gpu_skinning | ⭐⭐⭐☆ | 4.5 | 4.5 | [Daniel Holden](https://github.com/orangeduck) | | [models_bone_socket](models/models_bone_socket.c) | models_bone_socket | ⭐⭐⭐⭐️ | 4.5 | 4.5 | [iP](https://github.com/ipzaur) | | [models_tesseract_view](models/models_tesseract_view.c) | models_tesseract_view | ⭐⭐☆☆ | 5.6-dev | 5.6-dev | [Timothy van der Valk](https://github.com/arceryz) | +| [models_basic_voxel](models/models_basic_voxel.c) | models_basic_voxel | ⭐⭐☆☆ | 5.5 | 5.5 | [Tim Little](https://github.com/timlittle) | +| [models_geometry_textures_cube](models/models_geometry_textures_cube.c) | models_geometry_textures_cube | ⭐☆☆☆ | 5.6-dev | 5.6-dev | [Jopestpe](https://github.com/jopestpe) | ### category: shaders [29] diff --git a/examples/core/core_3d_camera_free.c b/examples/core/core_3d_camera_free.c index dde6a6c7d..448eb583e 100644 --- a/examples/core/core_3d_camera_free.c +++ b/examples/core/core_3d_camera_free.c @@ -49,7 +49,7 @@ int main(void) //---------------------------------------------------------------------------------- UpdateCamera(&camera, CAMERA_FREE); - if (IsKeyPressed('Z')) camera.target = (Vector3){ 0.0f, 0.0f, 0.0f }; + if (IsKeyPressed(KEY_Z)) camera.target = (Vector3){ 0.0f, 0.0f, 0.0f }; //---------------------------------------------------------------------------------- // Draw diff --git a/examples/core/core_delta_time.c b/examples/core/core_delta_time.c new file mode 100644 index 000000000..86bc969b3 --- /dev/null +++ b/examples/core/core_delta_time.c @@ -0,0 +1,112 @@ +/******************************************************************************************* +* +* raylib [core] example - delta time +* +* Example complexity rating: [★☆☆☆] 1/4 +* +* Example originally created with raylib 5.5 +* +* Example contributed by Robin (@RobinsAviary) and reviewed by Ramon Santamaria (@raysan5) +* +* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, +* BSD-like license that allows static linking with closed source software +* +* Copyright (c) 2025-2025 Robin (@RobinsAviary) +* +********************************************************************************************/ + +#include "raylib.h" + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [core] example - delta time"); + + int currentFps = 60; + + // Store the position for the both of the circles + Vector2 deltaCircle = { 0, (float)screenHeight/3.0f }; + Vector2 frameCircle = { 0, (float)screenHeight*(2.0f/3.0f) }; + + // The speed applied to both circles + const float speed = 10.0f; + const float circleRadius = 32.0f; + + SetTargetFPS(currentFps); + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + // Adjust the FPS target based on the mouse wheel + float mouseWheel = GetMouseWheelMove(); + if (mouseWheel != 0) + { + currentFps += (int)mouseWheel; + if (currentFps < 0) currentFps = 0; + SetTargetFPS(currentFps); + } + + // GetFrameTime() returns the time it took to draw the last frame, in seconds (usually called delta time) + // Uses the delta time to make the circle look like it's moving at a "consistent" speed regardless of FPS + + // Multiply by 6.0 (an arbitrary value) in order to make the speed + // visually closer to the other circle (at 60 fps), for comparison + deltaCircle.x += GetFrameTime()*6.0f*speed; + // This circle can move faster or slower visually depending on the FPS + frameCircle.x += 0.1f*speed; + + // If either circle is off the screen, reset it back to the start + if (deltaCircle.x > screenWidth) deltaCircle.x = 0; + if (frameCircle.x > screenWidth) frameCircle.x = 0; + + // Reset both circles positions + if (IsKeyPressed(KEY_R)) + { + deltaCircle.x = 0; + frameCircle.x = 0; + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(RAYWHITE); + + // Draw both circles to the screen + DrawCircleV(deltaCircle, circleRadius, RED); + DrawCircleV(frameCircle, circleRadius, BLUE); + + // Draw the help text + // Determine what help text to show depending on the current FPS target + const char *fpsText = 0; + if (currentFps <= 0) fpsText = TextFormat("FPS: unlimited (%i)", GetFPS()); + else fpsText = TextFormat("FPS: %i (target: %i)", GetFPS(), currentFps); + DrawText(fpsText, 10, 10, 20, DARKGRAY); + DrawText(TextFormat("Frame time: %02.02f ms", GetFrameTime()), 10, 30, 20, DARKGRAY); + DrawText("Use the scroll wheel to change the fps limit, r to reset", 10, 50, 20, DARKGRAY); + + // Draw the text above the circles + DrawText("FUNC: x += GetFrameTime()*speed", 10, 90, 20, RED); + DrawText("FUNC: x += speed", 10, 240, 20, BLUE); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} \ No newline at end of file diff --git a/examples/core/core_delta_time.png b/examples/core/core_delta_time.png new file mode 100644 index 000000000..cf8aa7c54 Binary files /dev/null and b/examples/core/core_delta_time.png differ diff --git a/examples/core/core_input_actions.c b/examples/core/core_input_actions.c new file mode 100644 index 000000000..aff356087 --- /dev/null +++ b/examples/core/core_input_actions.c @@ -0,0 +1,197 @@ +/******************************************************************************************* +* +* raylib [core] example - input actions +* +* Example complexity rating: [★★☆☆] 2/4 +* +* Example originally created with raylib 5.5, last time updated with raylib 5.6 +* +* Example contributed by Jett (@JettMonstersGoBoom) and reviewed by Ramon Santamaria (@raysan5) +* +* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, +* BSD-like license that allows static linking with closed source software +* +* Copyright (c) 2025 Jett (@JettMonstersGoBoom) +* +********************************************************************************************/ + +// Simple example for decoding input as actions, allowing remapping of input to different keys or gamepad buttons +// For example instead of using `IsKeyDown(KEY_LEFT)`, you can use `IsActionDown(ACTION_LEFT)` +// which can be reassigned to e.g. KEY_A and also assigned to a gamepad button. the action will trigger with either gamepad or keys + +#include "raylib.h" + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- +typedef enum ActionType { + NO_ACTION = 0, + ACTION_UP, + ACTION_DOWN, + ACTION_LEFT, + ACTION_RIGHT, + ACTION_FIRE, + MAX_ACTION +} ActionType; + +// Key and button inputs +typedef struct ActionInput { + int key; + int button; +} ActionInput; + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +static int gamepadIndex = 0; // Gamepad default index +static ActionInput actionInputs[MAX_ACTION] = { 0 }; + +//---------------------------------------------------------------------------------- +// Module Functions Declaration +//---------------------------------------------------------------------------------- +static bool IsActionPressed(int action); // Check action key/button pressed +static bool IsActionReleased(int action); // Check action key/button released +static bool IsActionDown(int action); // Check action key/button down + +static void SetActionsDefault(void); // Set the "default" keyset +static void SetActionsCursor(void); // Set the "alternate" keyset + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [core] example - input actions"); + + // Set default actions + char actionSet = 0; + SetActionsDefault(); + + Vector2 position = (Vector2){ 400.0f, 200.0f }; + Vector2 size = (Vector2){ 40.0f, 40.0f }; + + SetTargetFPS(60); + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + gamepadIndex = 0; // set this to gamepad being checked + if (IsActionDown(ACTION_UP)) position.y -= 2; + if (IsActionDown(ACTION_DOWN)) position.y += 2; + if (IsActionDown(ACTION_LEFT)) position.x -= 2; + if (IsActionDown(ACTION_RIGHT)) position.x += 2; + if (IsActionPressed(ACTION_FIRE)) + { + position.x = (screenWidth-size.x)/2; + position.y = (screenHeight-size.y)/2; + } + + // Switch control scheme by pressing TAB + if (IsKeyPressed(KEY_TAB)) + { + actionSet = !actionSet; + if (actionSet == 0) SetActionsDefault(); + else SetActionsCursor(); + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(GRAY); + + DrawRectangleV(position, size, RED); + + DrawText((actionSet == 0)? "Current input set: WASD (default)" : "Current input set: Cursor", 10, 10, 20, WHITE); + DrawText("Use TAB key to toggles Actions keyset", 10, 50, 20, GREEN); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} + +//---------------------------------------------------------------------------------- +// Module Functions Definition +//---------------------------------------------------------------------------------- +// Check action key/button pressed +// NOTE: Combines key pressed and gamepad button pressed in one action +static bool IsActionPressed(int action) +{ + bool result = false; + + if (action < MAX_ACTION) result = (IsKeyPressed(actionInputs[action].key) || IsGamepadButtonPressed(gamepadIndex, actionInputs[action].button)); + + return result; +} + +// Check action key/button released +// NOTE: Combines key released and gamepad button released in one action +static bool IsActionReleased(int action) +{ + bool result = false; + + if (action < MAX_ACTION) result = (IsKeyReleased(actionInputs[action].key) || IsGamepadButtonReleased(gamepadIndex, actionInputs[action].button)); + + return result; +} + +// Check action key/button down +// NOTE: Combines key down and gamepad button down in one action +static bool IsActionDown(int action) +{ + bool result = false; + + if (action < MAX_ACTION) result = (IsKeyDown(actionInputs[action].key) || IsGamepadButtonDown(gamepadIndex, actionInputs[action].button)); + + return result; +} + +// Set the "default" keyset +// NOTE: Here WASD and gamepad buttons on the left side for movement +static void SetActionsDefault(void) +{ + actionInputs[ACTION_UP].key = KEY_W; + actionInputs[ACTION_DOWN].key = KEY_S; + actionInputs[ACTION_LEFT].key = KEY_A; + actionInputs[ACTION_RIGHT].key = KEY_D; + actionInputs[ACTION_FIRE].key = KEY_SPACE; + + actionInputs[ACTION_UP].button = GAMEPAD_BUTTON_LEFT_FACE_UP; + actionInputs[ACTION_DOWN].button = GAMEPAD_BUTTON_LEFT_FACE_DOWN; + actionInputs[ACTION_LEFT].button = GAMEPAD_BUTTON_LEFT_FACE_LEFT; + actionInputs[ACTION_RIGHT].button = GAMEPAD_BUTTON_LEFT_FACE_RIGHT; + actionInputs[ACTION_FIRE].button = GAMEPAD_BUTTON_RIGHT_FACE_DOWN; +} + +// Set the "alternate" keyset +// NOTE: Here cursor keys and gamepad buttons on the right side for movement +static void SetActionsCursor(void) +{ + actionInputs[ACTION_UP].key = KEY_UP; + actionInputs[ACTION_DOWN].key = KEY_DOWN; + actionInputs[ACTION_LEFT].key = KEY_LEFT; + actionInputs[ACTION_RIGHT].key = KEY_RIGHT; + actionInputs[ACTION_FIRE].key = KEY_SPACE; + + actionInputs[ACTION_UP].button = GAMEPAD_BUTTON_RIGHT_FACE_UP; + actionInputs[ACTION_DOWN].button = GAMEPAD_BUTTON_RIGHT_FACE_DOWN; + actionInputs[ACTION_LEFT].button = GAMEPAD_BUTTON_RIGHT_FACE_LEFT; + actionInputs[ACTION_RIGHT].button = GAMEPAD_BUTTON_RIGHT_FACE_RIGHT; + actionInputs[ACTION_FIRE].button = GAMEPAD_BUTTON_LEFT_FACE_DOWN; +} diff --git a/examples/core/core_input_actions.png b/examples/core/core_input_actions.png new file mode 100644 index 000000000..757b725f6 Binary files /dev/null and b/examples/core/core_input_actions.png differ diff --git a/examples/core/core_input_mouse.c b/examples/core/core_input_mouse.c index bbe519f82..f429018b0 100644 --- a/examples/core/core_input_mouse.c +++ b/examples/core/core_input_mouse.c @@ -72,7 +72,7 @@ int main(void) DrawText("move ball with mouse and click mouse button to change color", 10, 10, 20, DARKGRAY); DrawText("Press 'H' to toggle cursor visibility", 10, 30, 20, DARKGRAY); - if (!IsCursorHidden()) DrawText("CURSOR HIDDEN", 20, 60, 20, RED); + if (IsCursorHidden()) DrawText("CURSOR HIDDEN", 20, 60, 20, RED); else DrawText("CURSOR VISIBLE", 20, 60, 20, LIME); EndDrawing(); diff --git a/examples/core/core_monitor_change.c b/examples/core/core_monitor_change.c new file mode 100644 index 000000000..8983556af --- /dev/null +++ b/examples/core/core_monitor_change.c @@ -0,0 +1,170 @@ +/******************************************************************************************* +* +* raylib [core] example - monitor change +* +* Example complexity rating: [★☆☆☆] 1/4 +* +* Example originally created with raylib 5.5, last time updated with raylib 5.6 +* +* Example contributed by Maicon Santana (@maiconpintoabreu) and reviewed by Ramon Santamaria (@raysan5) +* +* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, +* BSD-like license that allows static linking with closed source software +* +* Copyright (c) 2025-2025 Maicon Santana (@maiconpintoabreu) +* +********************************************************************************************/ + +#include "raylib.h" + +#define MAX_MONITORS 10 + +// Monitor Details +typedef struct Monitor { + Vector2 position; + char *name; + int width; + int height; + int physicalWidth; + int physicalHeight; + int refreshRate; +} Monitor; + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + Monitor monitors[MAX_MONITORS] = { 0 }; + + InitWindow(screenWidth, screenHeight, "raylib [core] example - monitor change"); + + int currentMonitorIndex = GetCurrentMonitor(); + int monitorCount = 0; + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + + // Variables to find the max x and Y to calculate the scale + int maxWidth = 1; + int maxHeight = 1; + + // Monitor offset is to fix when monitor position x is negative + int monitorOffsetX = 0; + + // Rebuild monitors array every frame + monitorCount = GetMonitorCount(); + for (int i = 0; i < monitorCount; i++) + { + monitors[i] = (Monitor){ + GetMonitorPosition(i), + GetMonitorName(i), + GetMonitorWidth(i), + GetMonitorHeight(i), + GetMonitorPhysicalWidth(i), + GetMonitorPhysicalHeight(i), + GetMonitorRefreshRate(i) + }; + if (monitors[i].position.x < monitorOffsetX) monitorOffsetX = monitors[i].position.x*-1; + + const int width = monitors[i].position.x + monitors[i].width; + const int height = monitors[i].position.y + monitors[i].height; + + if (maxWidth < width) maxWidth = width; + if (maxHeight < height) maxHeight = height; + } + + if (IsKeyPressed(KEY_ENTER) && monitorCount > 1) + { + currentMonitorIndex += 1; + + // Set index to 0 if the last one + if(currentMonitorIndex == monitorCount) currentMonitorIndex = 0; + + SetWindowMonitor(currentMonitorIndex); // Move window to currentMonitorIndex + } + else + { + // Get currentMonitorIndex if manually moved + currentMonitorIndex = GetCurrentMonitor(); + } + const Monitor currentMonitor = monitors[currentMonitorIndex]; + + float monitorScale = 0.6; + + if(maxHeight > maxWidth + monitorOffsetX) monitorScale *= ((float)screenHeight/(float)maxHeight); + else monitorScale *= ((float)screenWidth/(float)(maxWidth + monitorOffsetX)); + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + DrawText("Press [Enter] to move window to next monitor available", 20, 20, 20, DARKGRAY); + + DrawRectangleLines(20, 60, screenWidth - 40, screenHeight - 100, DARKGRAY); + + // Draw Monitor Rectangles with information inside + for (int i = 0; i < monitorCount; i++) + { + // Calculate retangle position and size using monitorScale + const Rectangle rec = (Rectangle){ + (monitors[i].position.x + monitorOffsetX) * monitorScale + 140, + monitors[i].position.y * monitorScale + 80, + monitors[i].width * monitorScale, + monitors[i].height * monitorScale + }; + + // Draw monitor name and information inside the rectangle + DrawText(TextFormat("[%i] %s", i, monitors[i].name), rec.x + 10, rec.y + (int)(100*monitorScale), (int)(120*monitorScale), BLUE); + DrawText( + TextFormat("Resolution: [%ipx x %ipx]\nRefreshRate: [%ihz]\nPhysical Size: [%imm x %imm]\nPosition: %3.0f x %3.0f", + monitors[i].width, + monitors[i].height, + monitors[i].refreshRate, + monitors[i].physicalWidth, + monitors[i].physicalHeight, + monitors[i].position.x, + monitors[i].position.y + ), rec.x + 10, rec.y + (int)(200*monitorScale), (int)(120*monitorScale), DARKGRAY); + + // Highlight current monitor + if (i == currentMonitorIndex) + { + DrawRectangleLinesEx(rec, 5, RED); + Vector2 windowPosition = (Vector2){ (GetWindowPosition().x + monitorOffsetX)*monitorScale + 140, GetWindowPosition().y*monitorScale + 80 }; + + // Draw window position based on monitors + DrawRectangleV(windowPosition, (Vector2){screenWidth * monitorScale, screenHeight * monitorScale}, Fade(GREEN, 0.5)); + } + else + { + DrawRectangleLinesEx(rec, 5, GRAY); + } + + } + + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} \ No newline at end of file diff --git a/examples/core/core_monitor_change.png b/examples/core/core_monitor_change.png new file mode 100644 index 000000000..81f9eed87 Binary files /dev/null and b/examples/core/core_monitor_change.png differ diff --git a/examples/examples_list.txt b/examples/examples_list.txt index ead1bf220..52104fbd9 100644 --- a/examples/examples_list.txt +++ b/examples/examples_list.txt @@ -44,8 +44,10 @@ core;core_automation_events;★★★☆;5.0;5.0;2023;2025;"Ramon Santamaria";@r core;core_high_dpi;★★☆☆;5.0;5.5;2025;2025;"Jonathan Marler";@marler8997 core;core_render_texture;★☆☆☆;5.6-dev;5.6-dev;2025;2025;"Ramon Santamaria";@raysan5 core;core_undo_redo;★★★☆;5.5;5.6;2025;2025;"Ramon Santamaria";@raysan5 +core;core_input_actions;★★☆☆;5.5;5.6;2025;2025;"Jett";@JettMonstersGoBoom shapes;shapes_basic_shapes;★☆☆☆;1.0;4.2;2014;2025;"Ramon Santamaria";@raysan5 shapes;shapes_bouncing_ball;★☆☆☆;2.5;2.5;2013;2025;"Ramon Santamaria";@raysan5 +shapes;shapes_bullet_hell;★☆☆☆;5.6;5.6;2025;2025;"Zero";@zerohorsepower shapes;shapes_colors_palette;★★☆☆;1.0;2.5;2014;2025;"Ramon Santamaria";@raysan5 shapes;shapes_logo_raylib;★☆☆☆;1.0;1.0;2014;2025;"Ramon Santamaria";@raysan5 shapes;shapes_logo_raylib_anim;★★☆☆;2.5;4.0;2014;2025;"Ramon Santamaria";@raysan5 @@ -64,6 +66,7 @@ shapes;shapes_rectangle_advanced;★★★★;5.5;5.5;2024;2025;"Everton Jr.";@e shapes;shapes_splines_drawing;★★★☆;5.0;5.0;2023;2025;"Ramon Santamaria";@raysan5 shapes;shapes_digital_clock;★★★★;5.5;5.6;2025;2025;"Hamza RAHAL";@hmz-rhl shapes;shapes_double_pendulum;★★☆☆;5.5;5.5;2025;2025;"JoeCheong";@Joecheong2006 +shapes;shapes_dashed_line;★☆☆☆;5.5;5.5;2025;2025;"Luís Almeida";@luis605 textures;textures_logo_raylib;★☆☆☆;1.0;1.0;2014;2025;"Ramon Santamaria";@raysan5 textures;textures_srcrec_dstrec;★★★☆;1.3;1.3;2015;2025;"Ramon Santamaria";@raysan5 textures;textures_image_drawing;★★☆☆;1.4;1.4;2016;2025;"Ramon Santamaria";@raysan5 @@ -127,6 +130,8 @@ models;models_textured_cube;★★☆☆;4.5;4.5;2022;2025;"Ramon Santamaria";@r models;models_animation_gpu_skinning;★★★☆;4.5;4.5;2024;2025;"Daniel Holden";@orangeduck models;models_bone_socket;★★★★;4.5;4.5;2024;2025;"iP";@ipzaur models;models_tesseract_view;★★☆☆;5.6-dev;5.6-dev;2024;2025;"Timothy van der Valk";@arceryz +models;models_basic_voxel;★★☆☆;5.5;5.5;2025;2025;"Tim Little";@timlittle +models;models_geometry_textures_cube;★☆☆☆;5.6-dev;5.6-dev;2025;2025;"Jopestpe";@jopestpe shaders;shaders_basic_lighting;★★★★;3.0;4.2;2019;2025;"Chris Camacho";@chriscamacho shaders;shaders_model_shader;★★☆☆;1.3;3.7;2014;2025;"Ramon Santamaria";@raysan5 shaders;shaders_shapes_textures;★★☆☆;1.7;3.7;2015;2025;"Ramon Santamaria";@raysan5 diff --git a/examples/models/models_basic_voxel.c b/examples/models/models_basic_voxel.c new file mode 100644 index 000000000..8df15d73d --- /dev/null +++ b/examples/models/models_basic_voxel.c @@ -0,0 +1,152 @@ +/******************************************************************************************* +* +* raylib [models] example - basic voxel +* +* Example complexity rating: [★★☆☆] 2/4 +* +* Example originally created with raylib 5.5, last time updated with raylib 5.5 +* +* Example contributed by Tim Little (@timlittle) and reviewed by Ramon Santamaria (@raysan5) +* +* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, +* BSD-like license that allows static linking with closed source software +* +* Copyright (c) 2025 Tim Little (@timlittle) +* +********************************************************************************************/ + +#include "raylib.h" +#include "raymath.h" + +#define WORLD_SIZE 8 // Size of our voxel world (8x8x8 cubes) + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [models] example - basic voxel"); + + DisableCursor(); // Lock mouse to window center + + // Define the camera to look into our 3d world (first person) + Camera3D camera = { 0 }; + camera.position = (Vector3){ -2.0f, 0.0f, -2.0f }; // Camera position at ground level + camera.target = (Vector3){ 0.0f, 0.0f, 0.0f }; // Camera looking at point + camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera up vector + camera.fovy = 45.0f; // Camera field-of-view Y + camera.projection = CAMERA_PERSPECTIVE; // Camera projection type + + // Create a cube model + Mesh cubeMesh = GenMeshCube(1.0f, 1.0f, 1.0f); // Create a unit cube mesh + Model cubeModel = LoadModelFromMesh(cubeMesh); // Convert mesh to a model + cubeModel.materials[0].maps[MATERIAL_MAP_DIFFUSE].color = BEIGE; + + // Initialize voxel world - fill with voxels + bool voxels[WORLD_SIZE][WORLD_SIZE][WORLD_SIZE] = { false }; + for (int x = 0; x < WORLD_SIZE; x++) + { + for (int y = 0; y < WORLD_SIZE; y++) + { + for (int z = 0; z < WORLD_SIZE; z++) + { + voxels[x][y][z] = true; + } + } + } + + SetTargetFPS(60); + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + UpdateCamera(&camera, CAMERA_FIRST_PERSON); + + // Handle voxel removal with mouse click + if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) + { + // Cast a ray from the screen center (where crosshair would be) + Vector2 screenCenter = { screenWidth/2.0f, screenHeight/2.0f }; + Ray ray = GetMouseRay(screenCenter, camera); + + // Check ray collision with all voxels + bool voxelRemoved = false; + for (int x = 0; (x < WORLD_SIZE) && !voxelRemoved; x++) + { + for (int y = 0; (y < WORLD_SIZE) && !voxelRemoved; y++) + { + for (int z = 0; (z < WORLD_SIZE) && !voxelRemoved; z++) + { + if (!voxels[x][y][z]) continue; // Skip empty voxels + + // Build a bounding box for this voxel + Vector3 position = { x, y, z }; + BoundingBox box = { + (Vector3){ position.x - 0.5f, position.y - 0.5f, position.z - 0.5f }, + (Vector3){ position.x + 0.5f, position.y + 0.5f, position.z + 0.5f } + }; + + // Check ray-box collision + RayCollision collision = GetRayCollisionBox(ray, box); + if (collision.hit) + { + voxels[x][y][z] = false; // Remove this voxel + voxelRemoved = true; // Exit all loops + } + } + } + } + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + BeginMode3D(camera); + + DrawGrid(10, 1.0f); + + // Draw all voxels + for (int x = 0; x < WORLD_SIZE; x++) + { + for (int y = 0; y < WORLD_SIZE; y++) + { + for (int z = 0; z < WORLD_SIZE; z++) + { + if (!voxels[x][y][z]) continue; + + Vector3 position = { x, y, z }; + DrawModel(cubeModel, position, 1.0f, BEIGE); + DrawCubeWires(position, 1.0f, 1.0f, 1.0f, BLACK); + } + } + } + + EndMode3D(); + + DrawText("Left-click a voxel to remove it!", 10, 10, 20, DARKGRAY); + DrawText("WASD to move, mouse to look around", 10, 35, 10, GRAY); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + UnloadModel(cubeModel); + CloseWindow(); + //-------------------------------------------------------------------------------------- + + return 0; +} diff --git a/examples/models/models_basic_voxel.png b/examples/models/models_basic_voxel.png new file mode 100644 index 000000000..8bbcaf8da Binary files /dev/null and b/examples/models/models_basic_voxel.png differ diff --git a/examples/models/models_geometry_textures_cube.c b/examples/models/models_geometry_textures_cube.c new file mode 100644 index 000000000..4608ad951 --- /dev/null +++ b/examples/models/models_geometry_textures_cube.c @@ -0,0 +1,88 @@ +/******************************************************************************************* +* +* raylib [models] example - geometry textures cube +* +* Example complexity rating: [★☆☆☆] 1/4 +* +* Example originally created with raylib 5.6-dev, last time updated with raylib 5.6-dev +* +* Example contributed by Jopestpe (@jopestpe) +* +* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, +* BSD-like license that allows static linking with closed source software +* +* Copyright (c) 2025-2025 Jopestpe (@jopestpe) +* +********************************************************************************************/ + +#include "raylib.h" + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [models] example - geometry textures cube"); + + // Define the camera to look into our 3d world + Camera camera = { 0 }; + camera.position = (Vector3){ 0.0f, 0.0f, 4.0f }; + camera.target = (Vector3){ 0.0f, 0.0f, 0.0f }; + camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; + camera.fovy = 45.0f; + camera.projection = CAMERA_PERSPECTIVE; + + // Load image to create texture for the cube + Model model = LoadModelFromMesh(GenMeshCube(1.0f, 1.0f, 1.0f)); + Image img = LoadImage("resources/cubicmap_atlas.png"); + Image crop = ImageFromImage(img, (Rectangle){0, img.height/2, img.width/2, img.height/2}); + Texture2D texture = LoadTextureFromImage(crop); + UnloadImage(img); + UnloadImage(crop); + + model.materials[0].maps[MATERIAL_MAP_DIFFUSE].texture = texture; + + float rotation = 0.0f; + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + rotation += 1.0f; + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + BeginMode3D(camera); + + DrawModelEx(model, (Vector3){0,0,0}, (Vector3){0.5f,1,0}, rotation, (Vector3){1,1,1}, WHITE); + + EndMode3D(); + + DrawFPS(10, 10); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + UnloadTexture(texture); // Unload texture + UnloadModel(model); // Unload model + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} diff --git a/examples/models/models_geometry_textures_cube.png b/examples/models/models_geometry_textures_cube.png new file mode 100644 index 000000000..4c199bdc4 Binary files /dev/null and b/examples/models/models_geometry_textures_cube.png differ diff --git a/examples/shaders/resources/shaders/glsl100/ascii.fs b/examples/shaders/resources/shaders/glsl100/ascii.fs new file mode 100644 index 000000000..54e20bf66 --- /dev/null +++ b/examples/shaders/resources/shaders/glsl100/ascii.fs @@ -0,0 +1,80 @@ +#version 100 + +precision mediump float; + +// Input from the vertex shader +varying vec2 fragTexCoord; + +// Output color for the screen +varying vec4 finalColor; + +uniform sampler2D texture0; +uniform vec2 resolution; + +// Fontsize less then 9 may be not complete +uniform float fontSize; + +float GreyScale(in vec3 col) +{ + return dot(col, vec3(0.2126, 0.7152, 0.0722)); +} + +float GetCharacter(float n, vec2 p) +{ + p = floor(p*vec2(-4.0, 4.0) + 2.5); + + // Check if the calculated coordinate is inside the 5x5 grid (from 0.0 to 4.0) + if (clamp(p.x, 0.0, 4.0) == p.x && clamp(p.y, 0.0, 4.0) == p.y) + { + float a = floor(p.x + 0.5) + 5.0*floor(p.y + 0.5); + + // This checked if the 'a'-th bit of 'n' was set + float shiftedN = floor(n/pow(2.0, a)); + + if (mod(shiftedN, 2.0) == 1.0) + { + return 1.0; // The bit is on + } + } + + return 0.0; // The bit is off, or we are outside the grid +} + +// ----------------------------------------------------------------------------- +// Main shader logic +// ----------------------------------------------------------------------------- + +void main() +{ + vec2 charPixelSize = vec2(fontSize, fontSize); + vec2 uvCellSize = charPixelSize/resolution; + + // The cell size is based on the fontSize set by application + vec2 cellUV = floor(fragTexCoord/uvCellSize)*uvCellSize; + + vec3 cellColor = texture2D(texture0, cellUV).rgb; + + // Gray is used to define what character will be selected to draw + float gray = GreyScale(cellColor); + + float n = 4096.0; + + // Character set from https://www.shadertoy.com/view/lssGDj + // Create new bitmaps https://thrill-project.com/archiv/coding/bitmap/ + if (gray > 0.2) n = 65600.0; // : + if (gray > 0.3) n = 18725316.0; // v + if (gray > 0.4) n = 15255086.0; // o + if (gray > 0.5) n = 13121101.0; // & + if (gray > 0.6) n = 15252014.0; // 8 + if (gray > 0.7) n = 13195790.0; // @ + if (gray > 0.8) n = 11512810.0; // # + + vec2 localUV = (fragTexCoord - cellUV)/uvCellSize; // Range [0.0, 1.0] + + vec2 p = localUV*2.0 - 1.0; // Range [-1.0, 1.0] + + // cellColor and charShape will define the color of the char + vec3 color = cellColor*GetCharacter(n, p); + + gl_FragColor = vec4(color, 1.0); +} \ No newline at end of file diff --git a/examples/shaders/resources/shaders/glsl120/ascii.fs b/examples/shaders/resources/shaders/glsl120/ascii.fs new file mode 100644 index 000000000..09c572ae5 --- /dev/null +++ b/examples/shaders/resources/shaders/glsl120/ascii.fs @@ -0,0 +1,78 @@ +#version 120 + +// Input from the vertex shader +varying vec2 fragTexCoord; + +// Output color for the screen +varying vec4 finalColor; + +uniform sampler2D texture0; +uniform vec2 resolution; + +// Fontsize less then 9 may be not complete +uniform float fontSize; + +float GreyScale(in vec3 col) +{ + return dot(col, vec3(0.2126, 0.7152, 0.0722)); +} + +float GetCharacter(float n, vec2 p) +{ + p = floor(p*vec2(-4.0, 4.0) + 2.5); + + // Check if the calculated coordinate is inside the 5x5 grid (from 0.0 to 4.0) + if (clamp(p.x, 0.0, 4.0) == p.x && clamp(p.y, 0.0, 4.0) == p.y) + { + float a = floor(p.x + 0.5) + 5.0*floor(p.y + 0.5); + + // This checked if the 'a'-th bit of 'n' was set + float shiftedN = floor(n/pow(2.0, a)); + + if (mod(shiftedN, 2.0) == 1.0) + { + return 1.0; // The bit is on + } + } + + return 0.0; // The bit is off, or we are outside the grid +} + +// ----------------------------------------------------------------------------- +// Main shader logic +// ----------------------------------------------------------------------------- + +void main() +{ + vec2 charPixelSize = vec2(fontSize, fontSize); + vec2 uvCellSize = charPixelSize / resolution; + + // The cell size is based on the fontSize set by application + vec2 cellUV = floor(fragTexCoord / uvCellSize)*uvCellSize; + + vec3 cellColor = texture2D(texture0, cellUV).rgb; + + // Gray is used to define what character will be selected to draw + float gray = GreyScale(cellColor); + + float n = 4096.0; + + // Character set from https://www.shadertoy.com/view/lssGDj + // Create new bitmaps https://thrill-project.com/archiv/coding/bitmap/ + if (gray > 0.2) n = 65600.0; // : + if (gray > 0.3) n = 18725316.0; // v + if (gray > 0.4) n = 15255086.0; // o + if (gray > 0.5) n = 13121101.0; // & + if (gray > 0.6) n = 15252014.0; // 8 + if (gray > 0.7) n = 13195790.0; // @ + if (gray > 0.8) n = 11512810.0; // # + + vec2 localUV = (fragTexCoord - cellUV)/uvCellSize; // Range [0.0, 1.0] + + vec2 p = localUV*2.0 - 1.0; // Range [-1.0, 1.0] + + // cellColor and charShape will define the color of the char + vec3 color = cellColor*GetCharacter(n, p); + + gl_FragColor = vec4(color, 1.0); +} \ No newline at end of file diff --git a/examples/shaders/resources/shaders/glsl330/ascii.fs b/examples/shaders/resources/shaders/glsl330/ascii.fs new file mode 100644 index 000000000..3f73bf288 --- /dev/null +++ b/examples/shaders/resources/shaders/glsl330/ascii.fs @@ -0,0 +1,73 @@ +#version 330 + +// Input from the vertex shader +in vec2 fragTexCoord; + +// Output color for the screen +out vec4 finalColor; + +uniform sampler2D texture0; +uniform vec2 resolution; + +// Fontsize less then 9 may be not complete +uniform float fontSize; + +float GreyScale(in vec3 col) +{ + return dot(col, vec3(0.2126, 0.7152, 0.0722)); +} + +float GetCharacter(int n, vec2 p) +{ + p = floor(p*vec2(-4.0, 4.0) + 2.5); + + // Check if the coordinate is inside the 5x5 grid (0 to 4) + if (clamp(p.x, 0.0, 4.0) == p.x && clamp(p.y, 0.0, 4.0) == p.y) + { + int a = int(round(p.x) + 5.0*round(p.y)); + if (((n >> a) & 1) == 1) + { + return 1.0; + } + } + + return 0.0; // The bit is off, or we are outside the grid +} + +// ----------------------------------------------------------------------------- +// Main shader logic +// ----------------------------------------------------------------------------- + +void main() +{ + vec2 charPixelSize = vec2(fontSize, fontSize); + vec2 uvCellSize = charPixelSize/resolution; + + // The cell size is based on the fontSize set by application + vec2 cellUV = floor(fragTexCoord/uvCellSize)*uvCellSize; + + vec3 cellColor = texture(texture0, cellUV).rgb; + + // Gray is used to define what character will be selected to draw + float gray = GreyScale(cellColor); + + int n = 4096; + + // Character set from https://www.shadertoy.com/view/lssGDj + // Create new bitmaps https://thrill-project.com/archiv/coding/bitmap/ + if (gray > 0.2) n = 65600; // : + if (gray > 0.3) n = 18725316; // v + if (gray > 0.4) n = 15255086; // o + if (gray > 0.5) n = 13121101; // & + if (gray > 0.6) n = 15252014; // 8 + if (gray > 0.7) n = 13195790; // @ + if (gray > 0.8) n = 11512810; // # + + vec2 localUV = (fragTexCoord - cellUV)/uvCellSize; // Range [0.0, 1.0] + + vec2 p = localUV*2.0 - 1.0; // Range [-1.0, 1.0] + + vec3 color = cellColor*GetCharacter(n, p); + + finalColor = vec4(color, 1.0); +} \ No newline at end of file diff --git a/examples/shaders/shaders_ascii_rendering.c b/examples/shaders/shaders_ascii_rendering.c new file mode 100644 index 000000000..33fb052f7 --- /dev/null +++ b/examples/shaders/shaders_ascii_rendering.c @@ -0,0 +1,121 @@ +/******************************************************************************************* +* +* raylib [shaders] example - ascii rendering +* +* Example complexity rating: [★★☆☆] 2/4 +* +* Example originally created with raylib 5.5, last time updated with raylib 5.6 +* +* Example contributed by Maicon Santana (@maiconpintoabreu) and reviewed by Ramon Santamaria (@raysan5) +* +* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, +* BSD-like license that allows static linking with closed source software +* +* Copyright (c) 2025-2025 Maicon Santana (@maiconpintoabreu) +* +********************************************************************************************/ + +#include "raylib.h" + +#if defined(PLATFORM_DESKTOP) + #define GLSL_VERSION 330 +#else // PLATFORM_ANDROID, PLATFORM_WEB + #define GLSL_VERSION 100 +#endif + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [shaders] example - ascii rendering"); + + // Texture to test static drawing + Texture2D fudesumi = LoadTexture("resources/fudesumi.png"); + // Texture to test moving drawing + Texture2D raysan = LoadTexture("resources/raysan.png"); + + // Load shader to be used on postprocessing + Shader shader = LoadShader(0, TextFormat("resources/shaders/glsl%i/ascii.fs", GLSL_VERSION)); + + // These locations are used to send data to the GPU + int resolutionLoc = GetShaderLocation(shader, "resolution"); + int fontSizeLoc = GetShaderLocation(shader, "fontSize"); + + // Set the character size for the ASCII effect + // Fontsize should be 9 or more + float fontSize = 9.0f; + + // Send the updated values to the shader + float resolution[2] = { (float)screenWidth, (float)screenHeight }; + SetShaderValue(shader, resolutionLoc, resolution, SHADER_UNIFORM_VEC2); + + Vector2 circlePos = (Vector2){40.0f, (float)screenHeight*0.5f}; + float circleSpeed = 1.0f; + + // RenderTexture to apply the postprocessing later + RenderTexture2D target = LoadRenderTexture(screenWidth, screenHeight); + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + circlePos.x += circleSpeed; + if ((circlePos.x > 200.0f) || (circlePos.x < 40.0f)) circleSpeed *= -1; // Revert speed + + if (IsKeyPressed(KEY_LEFT) && (fontSize > 9.0)) fontSize -= 1; // Reduce fontSize + if (IsKeyPressed(KEY_RIGHT) && (fontSize < 15.0)) fontSize += 1; // Increase fontSize + + // Set fontsize for the shader + SetShaderValue(shader, fontSizeLoc, &fontSize, SHADER_UNIFORM_FLOAT); + + // Draw + //---------------------------------------------------------------------------------- + BeginTextureMode(target); + ClearBackground(WHITE); + + // Draw scene in our render texture + DrawTexture(fudesumi, 500, -30, WHITE); + DrawTextureV(raysan, circlePos, WHITE); + EndTextureMode(); + + BeginDrawing(); + ClearBackground(RAYWHITE); + + BeginShaderMode(shader); + // Draw the scene texture (that we rendered earlier) to the screen + // The shader will process every pixel of this texture + DrawTextureRec(target.texture, + (Rectangle){ 0, 0, (float)target.texture.width, (float)-target.texture.height }, + (Vector2){ 0, 0 }, WHITE); + EndShaderMode(); + + DrawRectangle(0, 0, screenWidth, 40, BLACK); + DrawText(TextFormat("Ascii effect - FontSize:%2.0f - [Left] -1 [Right] +1 ", fontSize), 120, 10, 20, LIGHTGRAY); + DrawFPS(10, 10); + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + UnloadRenderTexture(target); // Unload render texture + + UnloadShader(shader); // Unload shader + UnloadTexture(fudesumi); // Unload texture + UnloadTexture(raysan); // Unload texture + + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} diff --git a/examples/shaders/shaders_ascii_rendering.png b/examples/shaders/shaders_ascii_rendering.png new file mode 100644 index 000000000..59ecc8395 Binary files /dev/null and b/examples/shaders/shaders_ascii_rendering.png differ diff --git a/examples/shapes/shapes_bouncing_ball.c b/examples/shapes/shapes_bouncing_ball.c index 187cd41af..73dca0011 100644 --- a/examples/shapes/shapes_bouncing_ball.c +++ b/examples/shapes/shapes_bouncing_ball.c @@ -4,8 +4,10 @@ * * Example complexity rating: [★☆☆☆] 1/4 * -* Example originally created with raylib 2.5, last time updated with raylib 2.5 +* Example originally created with raylib 2.5, last time updated with raylib 5.6 * +* Example contributed by Jopestpe (@jopestpe) +* * Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, * BSD-like license that allows static linking with closed source software * @@ -31,7 +33,9 @@ int main(void) Vector2 ballPosition = { GetScreenWidth()/2.0f, GetScreenHeight()/2.0f }; Vector2 ballSpeed = { 5.0f, 4.0f }; int ballRadius = 20; + float gravity = 0.2f; + bool useGravity = true; bool pause = 0; int framesCounter = 0; @@ -43,16 +47,19 @@ int main(void) { // Update //----------------------------------------------------- + if (IsKeyPressed(KEY_G)) useGravity = !useGravity; if (IsKeyPressed(KEY_SPACE)) pause = !pause; - + if (!pause) { ballPosition.x += ballSpeed.x; ballPosition.y += ballSpeed.y; + if (useGravity) ballSpeed.y += gravity; + // Check walls collision for bouncing if ((ballPosition.x >= (GetScreenWidth() - ballRadius)) || (ballPosition.x <= ballRadius)) ballSpeed.x *= -1.0f; - if ((ballPosition.y >= (GetScreenHeight() - ballRadius)) || (ballPosition.y <= ballRadius)) ballSpeed.y *= -1.0f; + if ((ballPosition.y >= (GetScreenHeight() - ballRadius)) || (ballPosition.y <= ballRadius)) ballSpeed.y *= -0.95f; } else framesCounter++; //----------------------------------------------------- @@ -65,12 +72,15 @@ int main(void) DrawCircleV(ballPosition, (float)ballRadius, MAROON); DrawText("PRESS SPACE to PAUSE BALL MOVEMENT", 10, GetScreenHeight() - 25, 20, LIGHTGRAY); + + if (useGravity) DrawText("GRAVITY: ON (Press G to disable)", 10, GetScreenHeight() - 50, 20, DARKGREEN); + else DrawText("GRAVITY: OFF (Press G to enable)", 10, GetScreenHeight() - 50, 20, RED); // On pause, we draw a blinking message if (pause && ((framesCounter/30)%2)) DrawText("PAUSED", 350, 200, 30, GRAY); DrawFPS(10, 10); - + EndDrawing(); //----------------------------------------------------- } diff --git a/examples/shapes/shapes_bullet_hell.c b/examples/shapes/shapes_bullet_hell.c new file mode 100644 index 000000000..2635ee02c --- /dev/null +++ b/examples/shapes/shapes_bullet_hell.c @@ -0,0 +1,248 @@ +/******************************************************************************************* +* +* raylib [shapes] example - bullet hell +* +* Example complexity rating: [★☆☆☆] 1/4 +* +* Example originally created with raylib 5.6, last time updated with raylib 5.6 +* +* Example contributed by Zero (@zerohorsepower) and reviewed by Ramon Santamaria (@raysan5) +* +* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, +* BSD-like license that allows static linking with closed source software +* +* Copyright (c) 2025-2025 Zero (@zerohorsepower) +* +********************************************************************************************/ + +#include "raylib.h" + +#include // Required for: calloc(), free() +#include // Required for: cosf(), sinf() + +#define MAX_BULLETS 500000 // Max bullets to be processed + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- +typedef struct Bullet { + Vector2 position; // Bullet position on screen + Vector2 acceleration; // Amount of pixels to be incremented to position every frame + bool disabled; // Skip processing and draw case out of screen + Color color; // Bullet color +} Bullet; + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [shapes] example - bullet hell"); + + // Bullets definition + Bullet *bullets = (Bullet *)RL_CALLOC(MAX_BULLETS, sizeof(Bullet)); // Bullets array + int bulletCount = 0; + int bulletDisabledCount = 0; // Used to calculate how many bullets are on screen + int bulletRadius = 10; + float bulletSpeed = 3.0f; + int bulletRows = 6; + Color bulletColor[2] = { RED, BLUE }; + + // Spawner variables + float baseDirection = 0; + int angleIncrement = 5; // After spawn all bullet rows, increment this value on the baseDirection for next the frame + float spawnCooldown = 2; + float spawnCooldownTimer = spawnCooldown; + + // Magic circle + float magicCircleRotation = 0; + + // Used on performance drawing + RenderTexture bulletTexture = LoadRenderTexture(24, 24); + + // Draw circle to bullet texture, then draw bullet using DrawTexture() + // NOTE: This is done to improve the performance, since DrawCircle() is very slow + BeginTextureMode(bulletTexture); + DrawCircle(12, 12, bulletRadius, WHITE); + DrawCircleLines(12, 12, bulletRadius, BLACK); + EndTextureMode(); + + bool drawInPerformanceMode = true; // Switch between DrawCircle() and DrawTexture() + + SetTargetFPS(60); + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + // Reset the bullet index + // New bullets will replace the old ones that are already disabled due to out-of-screen + if (bulletCount >= MAX_BULLETS) + { + bulletCount = 0; + bulletDisabledCount = 0; + } + + spawnCooldownTimer--; + if (spawnCooldownTimer < 0) + { + spawnCooldownTimer = spawnCooldown; + + // Spawn bullets + float degreesPerRow = 360.0f/bulletRows; + for (int row = 0; row < bulletRows; row++) + { + if (bulletCount < MAX_BULLETS) + { + bullets[bulletCount].position = (Vector2){(float) screenWidth/2, (float) screenHeight/2}; + bullets[bulletCount].disabled = false; + bullets[bulletCount].color = bulletColor[row%2]; + + float bulletDirection = baseDirection + (degreesPerRow*row); + + // Bullet speed * bullet direction, this will determine how much pixels will be incremented/decremented + // from the bullet position every frame. Since the bullets doesn't change its direction and speed, + // only need to calculate it at the spawning time + // 0 degrees = right, 90 degrees = down, 180 degrees = left and 270 degrees = up, basically clockwise + // Case you want it to be anti-clockwise, add "* -1" at the y acceleration + bullets[bulletCount].acceleration = (Vector2){ + bulletSpeed*cosf(bulletDirection*DEG2RAD), + bulletSpeed*sinf(bulletDirection*DEG2RAD) + }; + + bulletCount++; + } + } + + baseDirection += angleIncrement; + } + + // Update bullets position based on its acceleration + for (int i = 0; i < bulletCount; i++) + { + // Only update bullet if inside the screen + if (!bullets[i].disabled) + { + bullets[i].position.x += bullets[i].acceleration.x; + bullets[i].position.y += bullets[i].acceleration.y; + + // Disable bullet if out of screen + if ((bullets[i].position.x < -bulletRadius*2) || + (bullets[i].position.x > screenWidth + bulletRadius*2) || + (bullets[i].position.y < -bulletRadius*2) || + (bullets[i].position.y > screenHeight + bulletRadius*2)) + { + bullets[i].disabled = true; + bulletDisabledCount++; + } + } + } + + // Input logic + if ((IsKeyPressed(KEY_RIGHT) || IsKeyPressed(KEY_D)) && (bulletRows < 359)) bulletRows++; + if ((IsKeyPressed(KEY_LEFT) || IsKeyPressed(KEY_A)) && (bulletRows > 1)) bulletRows--; + if (IsKeyPressed(KEY_UP) || IsKeyPressed(KEY_W)) bulletSpeed += 0.25f; + if ((IsKeyPressed(KEY_DOWN) || IsKeyPressed(KEY_S)) && (bulletSpeed > 0.50f)) bulletSpeed -= 0.25f; + if (IsKeyPressed(KEY_Z) && (spawnCooldown > 1)) spawnCooldown--; + if (IsKeyPressed(KEY_X)) spawnCooldown++; + if (IsKeyPressed(KEY_ENTER)) drawInPerformanceMode = !drawInPerformanceMode; + + if (IsKeyDown(KEY_SPACE)) + { + angleIncrement += 1; + angleIncrement %= 360; + } + + if (IsKeyPressed(KEY_C)) + { + bulletCount = 0; + bulletDisabledCount = 0; + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(RAYWHITE); + + // Draw magic circle + magicCircleRotation++; + DrawRectanglePro((Rectangle){ (float)screenWidth/2, (float)screenHeight/2, 120, 120 }, + (Vector2){ 60.0f, 60.0f }, magicCircleRotation, PURPLE); + DrawRectanglePro((Rectangle){ (float)screenWidth/2, (float)screenHeight/2, 120, 120 }, + (Vector2){ 60.0f, 60.0f }, magicCircleRotation + 45, PURPLE); + DrawCircleLines(screenWidth/2, screenHeight/2, 70, BLACK); + DrawCircleLines(screenWidth/2, screenHeight/2, 50, BLACK); + DrawCircleLines(screenWidth/2, screenHeight/2, 30, BLACK); + + // Draw bullets + if (drawInPerformanceMode) + { + // Draw bullets using pre-rendered texture containing circle + for (int i = 0; i < bulletCount; i++) + { + // Do not draw disabled bullets (out of screen) + if (!bullets[i].disabled) + { + DrawTexture(bulletTexture.texture, + bullets[i].position.x - bulletTexture.texture.width*0.5f, + bullets[i].position.y - bulletTexture.texture.height*0.5f, + bullets[i].color); + } + } + } + else + { + // Draw bullets using DrawCircle(), less performant + for (int i = 0; i < bulletCount; i++) + { + // Do not draw disabled bullets (out of screen) + if (!bullets[i].disabled) + { + DrawCircleV(bullets[i].position, bulletRadius, bullets[i].color); + DrawCircleLinesV(bullets[i].position, bulletRadius, BLACK); + } + } + } + + // Draw UI + DrawRectangle(10, 10, 280, 150, (Color){0,0, 0, 200 }); + DrawText("Controls:", 20, 20, 10, LIGHTGRAY); + DrawText("- Right/Left or A/D: Change rows number", 40, 40, 10, LIGHTGRAY); + DrawText("- Up/Down or W/S: Change bullet speed", 40, 60, 10, LIGHTGRAY); + DrawText("- Z or X: Change spawn cooldown", 40, 80, 10, LIGHTGRAY); + DrawText("- Space (Hold): Change the angle increment", 40, 100, 10, LIGHTGRAY); + DrawText("- Enter: Switch draw method (Performance)", 40, 120, 10, LIGHTGRAY); + DrawText("- C: Clear bullets", 40, 140, 10, LIGHTGRAY); + + DrawRectangle(610, 10, 170, 30, (Color){0,0, 0, 200 }); + if (drawInPerformanceMode) DrawText("Draw method: DrawTexture(*)", 620, 20, 10, GREEN); + else DrawText("Draw method: DrawCircle(*)", 620, 20, 10, RED); + + DrawRectangle(135, 410, 530, 30, (Color){0,0, 0, 200 }); + DrawText(TextFormat("[ FPS: %d, Bullets: %d, Rows: %d, Bullet speed: %.2f, Angle increment per frame: %d, Cooldown: %.0f ]", + GetFPS(), bulletCount - bulletDisabledCount, bulletRows, bulletSpeed, angleIncrement, spawnCooldown), + 155, 420, 10, GREEN); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + UnloadRenderTexture(bulletTexture); // Unload bullet texture + + RL_FREE(bullets); // Free bullets array data + + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} \ No newline at end of file diff --git a/examples/shapes/shapes_bullet_hell.png b/examples/shapes/shapes_bullet_hell.png new file mode 100644 index 000000000..cf8e5d8f6 Binary files /dev/null and b/examples/shapes/shapes_bullet_hell.png differ diff --git a/examples/shapes/shapes_collision_area.c b/examples/shapes/shapes_collision_area.c index f492cf250..426d56536 100644 --- a/examples/shapes/shapes_collision_area.c +++ b/examples/shapes/shapes_collision_area.c @@ -15,8 +15,6 @@ #include "raylib.h" -#include // Required for: abs() - //------------------------------------------------------------------------------------ // Program main entry point //------------------------------------------------------------------------------------ diff --git a/examples/shapes/shapes_dashed_line.c b/examples/shapes/shapes_dashed_line.c new file mode 100644 index 000000000..fd017562f --- /dev/null +++ b/examples/shapes/shapes_dashed_line.c @@ -0,0 +1,103 @@ +/******************************************************************************************* +* +* raylib [shapes] example - dashed line drawing +* +* Example complexity rating: [★☆☆☆] 1/4 +* +* Example originally created with raylib 2.5, last time updated with raylib 2.5 +* +* Example contributed by Luís Almeida (@luis605) +* +* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, +* BSD-like license that allows static linking with closed source software +* +* Copyright (c) 2025 Luís Almeida (@luis605) +* +********************************************************************************************/ + +#include "raylib.h" +#define RAYGUI_IMPLEMENTATION +#include "raygui.h" // Required for GUI controls + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [shapes] example - interactive dashed line"); + + // Line Properties + Vector2 lineStartPosition = { 20.0f, 50.0f }; + Vector2 lineEndPosition = { 780.0f, 400.0f }; + float dashLength = 25.0f; + float blankLength = 15.0f; + + // Color selection + Color lineColors[] = { RED, ORANGE, GOLD, GREEN, BLUE, VIOLET, PINK, BLACK }; + int colorIndex = 0; + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + lineEndPosition = GetMousePosition(); // Line endpoint follows the mouse + + // --- Keyboard Controls --- + + // Change Dash Length (UP/DOWN arrows) + if (IsKeyDown(KEY_UP)) dashLength += 1.0f; + if (IsKeyDown(KEY_DOWN) && dashLength > 1.0f) dashLength -= 1.0f; + + // Change Space Length (LEFT/RIGHT arrows) + if (IsKeyDown(KEY_RIGHT)) blankLength += 1.0f; + if (IsKeyDown(KEY_LEFT) && blankLength > 1.0f) blankLength -= 1.0f; + + // Cycle through colors ('C' key) + if (IsKeyPressed(KEY_C)) + { + colorIndex = (colorIndex + 1) % (sizeof(lineColors)/sizeof(Color)); + } + + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + // Draw the dashed line with the current properties + DrawLineDashed(lineStartPosition, lineEndPosition, dashLength, blankLength, lineColors[colorIndex]); + + // Draw UI and Instructions + DrawRectangle(5, 5, 265, 95, Fade(SKYBLUE, 0.5f)); + DrawRectangleLines(5, 5, 265, 95, BLUE); + + DrawText("CONTROLS:", 15, 15, 10, BLACK); + DrawText("UP/DOWN: Change Dash Length", 15, 35, 10, BLACK); + DrawText("LEFT/RIGHT: Change Space Length", 15, 55, 10, BLACK); + DrawText("C: Cycle Color", 15, 75, 10, BLACK); + + DrawText(TextFormat("Dash: %.0f | Space: %.0f", dashLength, blankLength), 15, 115, 10, DARKGRAY); + + DrawFPS(screenWidth - 80, 10); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} \ No newline at end of file diff --git a/examples/shapes/shapes_dashed_line.png b/examples/shapes/shapes_dashed_line.png new file mode 100644 index 000000000..5c3ce3ef3 Binary files /dev/null and b/examples/shapes/shapes_dashed_line.png differ diff --git a/projects/VS2022/examples/core_input_actions.vcxproj b/projects/VS2022/examples/core_input_actions.vcxproj new file mode 100644 index 000000000..9b43826f8 --- /dev/null +++ b/projects/VS2022/examples/core_input_actions.vcxproj @@ -0,0 +1,569 @@ + + + + + Debug.DLL + ARM64 + + + Debug.DLL + Win32 + + + Debug.DLL + x64 + + + Debug + ARM64 + + + Debug + Win32 + + + Debug + x64 + + + Release.DLL + ARM64 + + + Release.DLL + Win32 + + + Release.DLL + x64 + + + Release + ARM64 + + + Release + Win32 + + + Release + x64 + + + + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B} + Win32Proj + core_input_actions + 10.0 + core_input_actions + + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + /FS %(AdditionalOptions) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;shcore.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + /FS %(AdditionalOptions) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + + + + + + + {e89d61ac-55de-4482-afd4-df7242ebc859} + + + + + + \ No newline at end of file diff --git a/projects/VS2022/raylib.sln b/projects/VS2022/raylib.sln index fe2241434..c0295d4c6 100644 --- a/projects/VS2022/raylib.sln +++ b/projects/VS2022/raylib.sln @@ -345,6 +345,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "text_inline_styling", "exam EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_undo_redo", "examples\core_undo_redo.vcxproj", "{3B27F358-2679-4F38-B297-17B536F580BB}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_input_actions", "examples\core_input_actions.vcxproj", "{718FCBD0-591D-448C-B7D5-9F1CA8544E7B}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug.DLL|ARM64 = Debug.DLL|ARM64 @@ -4247,6 +4249,30 @@ Global {3B27F358-2679-4F38-B297-17B536F580BB}.Release|x64.Build.0 = Release|x64 {3B27F358-2679-4F38-B297-17B536F580BB}.Release|x86.ActiveCfg = Release|Win32 {3B27F358-2679-4F38-B297-17B536F580BB}.Release|x86.Build.0 = Release|Win32 + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Debug|ARM64.Build.0 = Debug|ARM64 + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Debug|x64.ActiveCfg = Debug|x64 + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Debug|x64.Build.0 = Debug|x64 + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Debug|x86.ActiveCfg = Debug|Win32 + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Debug|x86.Build.0 = Debug|Win32 + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Release|ARM64.ActiveCfg = Release|ARM64 + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Release|ARM64.Build.0 = Release|ARM64 + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Release|x64.ActiveCfg = Release|x64 + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Release|x64.Build.0 = Release|x64 + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Release|x86.ActiveCfg = Release|Win32 + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -4421,6 +4447,7 @@ Global {49C67F03-1A56-4F96-B278-39B66EC93678} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} {D496308F-3C3C-40B3-A3ED-EA327D244B3E} = {8D3C83B7-F1E0-4C2E-9E34-EE5F6AB2502A} {3B27F358-2679-4F38-B297-17B536F580BB} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E926C768-6307-4423-A1EC-57E95B1FAB29} diff --git a/src/Makefile b/src/Makefile index f2bdd811e..e767d21d0 100644 --- a/src/Makefile +++ b/src/Makefile @@ -235,20 +235,22 @@ endif # NOTE: By default use OpenGL 3.3 on desktop platforms ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_GLFW) GRAPHICS ?= GRAPHICS_API_OPENGL_33 - #GRAPHICS = GRAPHICS_API_OPENGL_11 # Uncomment to use OpenGL 1.1 - #GRAPHICS = GRAPHICS_API_OPENGL_21 # Uncomment to use OpenGL 2.1 - #GRAPHICS = GRAPHICS_API_OPENGL_43 # Uncomment to use OpenGL 4.3 - #GRAPHICS = GRAPHICS_API_OPENGL_ES2 # Uncomment to use OpenGL ES 2.0 (ANGLE) + #GRAPHICS = GRAPHICS_API_OPENGL_11_SOFTWARE # Uncomment to use software rendering + #GRAPHICS = GRAPHICS_API_OPENGL_11 # Uncomment to use OpenGL 1.1 + #GRAPHICS = GRAPHICS_API_OPENGL_21 # Uncomment to use OpenGL 2.1 + #GRAPHICS = GRAPHICS_API_OPENGL_43 # Uncomment to use OpenGL 4.3 + #GRAPHICS = GRAPHICS_API_OPENGL_ES2 # Uncomment to use OpenGL ES 2.0 (ANGLE) endif ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_SDL) GRAPHICS ?= GRAPHICS_API_OPENGL_33 endif ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_RGFW) GRAPHICS ?= GRAPHICS_API_OPENGL_33 - #GRAPHICS = GRAPHICS_API_OPENGL_11 # Uncomment to use OpenGL 1.1 - #GRAPHICS = GRAPHICS_API_OPENGL_21 # Uncomment to use OpenGL 2.1 - #GRAPHICS = GRAPHICS_API_OPENGL_43 # Uncomment to use OpenGL 4.3 - #GRAPHICS = GRAPHICS_API_OPENGL_ES2 # Uncomment to use OpenGL ES 2.0 (ANGLE) + #GRAPHICS = GRAPHICS_API_OPENGL_11_SOFTWARE # Uncomment to use software rendering + #GRAPHICS = GRAPHICS_API_OPENGL_11 # Uncomment to use OpenGL 1.1 + #GRAPHICS = GRAPHICS_API_OPENGL_21 # Uncomment to use OpenGL 2.1 + #GRAPHICS = GRAPHICS_API_OPENGL_43 # Uncomment to use OpenGL 4.3 + #GRAPHICS = GRAPHICS_API_OPENGL_ES2 # Uncomment to use OpenGL ES 2.0 (ANGLE) endif ifeq ($(TARGET_PLATFORM),PLATFORM_DRM) # On DRM OpenGL ES 2.0 must be used diff --git a/src/external/rlsw.h b/src/external/rlsw.h new file mode 100644 index 000000000..49050f848 --- /dev/null +++ b/src/external/rlsw.h @@ -0,0 +1,4938 @@ +/********************************************************************************************** +* +* rlsw v1.0 - An OpenGL 1.1-style software renderer implementation +* +* DESCRIPTION: +* rlsw is a custom OpenGL 1.1-style implementation on software, intended to provide all +* functionality available on rlgl.h library used by raylib, becoming a direct software +* rendering replacement for OpenGL 1.1 backend and allowing to run raylib on GPU-less +* devices when required. +* +* FEATURES: +* - Rendering to custom internal framebuffer with multiple color modes supported: +* - Color buffer: RGB - 8-bit (3:3:2) | RGB - 16-bit (5:6:5) | RGB - 24-bit (8:8:8) +* - Depth buffer: D - 8-bit (unorm) | D - 16-bit (unorm) | D - 24-bit (unorm) +* - Rendering modes supported: POINT, LINES, TRIANGLE, QUADS +* - Additional features: Polygon modes, Point width, Line width +* - Clipping support for all rendering modes +* - Texture features supported: +* - All uncompressed texture formats supported by raylib +* - Texture Minification/Magnification checks +* - Point and Bilinear filtering +* - Texture Wrap Modes with separate checks for S/T coordinates +* - Vertex Arrays support with direct primitive drawing mode +* - Matrix Stack support (Matrix Push/Pop) +* - Other GL misc features: +* - GL-style getter functions +* - Framebuffer resizing +* - Perspective correction +* - Scissor clipping +* - Depth testing +* - Blend modes +* - Face culling +* +* ADDITIONAL NOTES: +* Check PR for more info: https://github.com/raysan5/raylib/pull/4832 +* +* CONFIGURATION: +* #define RLSW_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 +* +* rlsw capabilities could be customized just defining some internal +* values before library inclusion (default values listed): +* +* #define SW_GL_FRAMEBUFFER_COPY_BGRA true +* #define SW_GL_BINDING_COPY_TEXTURE true +* #define SW_COLOR_BUFFER_BITS 24 +* #define SW_DEPTH_BUFFER_BITS 16 +* #define SW_MAX_PROJECTION_STACK_SIZE 2 +* #define SW_MAX_MODELVIEW_STACK_SIZE 8 +* #define SW_MAX_TEXTURE_STACK_SIZE 2 +* #define SW_MAX_TEXTURES 128 +* +* +* LICENSE: MIT +* +* Copyright (c) 2025-2026 Le Juez Victor (@Bigfoot71), reviewed by Ramon Santamaria (@raysan5) +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +* +**********************************************************************************************/ + +#ifndef RLSW_H +#define RLSW_H + +#include +#include + +//---------------------------------------------------------------------------------- +// Defines and Macros +//---------------------------------------------------------------------------------- +// Function specifiers definition +#ifndef SWAPI + #define SWAPI // Functions defined as 'extern' by default (implicit specifiers) +#endif + +#ifndef SW_MALLOC + #define SW_MALLOC(sz) malloc(sz) +#endif + +#ifndef SW_REALLOC + #define SW_REALLOC(ptr, newSz) realloc(ptr, newSz) +#endif + +#ifndef SW_FREE + #define SW_FREE(ptr) free(ptr) +#endif + +#ifndef SW_RESTRICT + #ifdef _MSC_VER + #define SW_RESTRICT __restrict + #else + #define SW_RESTRICT restrict + #endif +#endif + +#ifndef SW_GL_FRAMEBUFFER_COPY_BGRA + #define SW_GL_FRAMEBUFFER_COPY_BGRA true +#endif + +#ifndef SW_GL_BINDING_COPY_TEXTURE + #define SW_GL_BINDING_COPY_TEXTURE true +#endif + +#ifndef SW_COLOR_BUFFER_BITS + #define SW_COLOR_BUFFER_BITS 24 +#endif + +#ifndef SW_DEPTH_BUFFER_BITS + #define SW_DEPTH_BUFFER_BITS 16 +#endif + +#ifndef SW_MAX_PROJECTION_STACK_SIZE + #define SW_MAX_PROJECTION_STACK_SIZE 2 +#endif + +#ifndef SW_MAX_MODELVIEW_STACK_SIZE + #define SW_MAX_MODELVIEW_STACK_SIZE 8 +#endif + +#ifndef SW_MAX_TEXTURE_STACK_SIZE + #define SW_MAX_TEXTURE_STACK_SIZE 2 +#endif + +#ifndef SW_MAX_TEXTURES + #define SW_MAX_TEXTURES 128 +#endif + +// Under normal circumstances, clipping a polygon can add at most one vertex per clipping plane. +// Considering the largest polygon involved is a quadrilateral (4 vertices), +// and that clipping occurs against both the frustum (6 planes) and the scissors (4 planes), +// the maximum number of vertices after clipping is: +// 4 (original vertices) + 6 (frustum planes) + 4 (scissors planes) = 14 +#ifndef SW_MAX_CLIPPED_POLYGON_VERTICES + #define SW_MAX_CLIPPED_POLYGON_VERTICES 14 +#endif + +#ifndef SW_CLIP_EPSILON + #define SW_CLIP_EPSILON 1e-4f +#endif + +//---------------------------------------------------------------------------------- +// OpenGL Compatibility Types +//---------------------------------------------------------------------------------- +typedef unsigned int GLenum; +typedef unsigned char GLboolean; +typedef unsigned int GLbitfield; +typedef void GLvoid; +typedef signed char GLbyte; +typedef short GLshort; +typedef int GLint; +typedef unsigned char GLubyte; +typedef unsigned short GLushort; +typedef unsigned int GLuint; +typedef int GLsizei; +typedef float GLfloat; +typedef float GLclampf; +typedef double GLdouble; +typedef double GLclampd; + +//---------------------------------------------------------------------------------- +// OpenGL Definitions +// NOTE: Not used/supported definitions are commented +//---------------------------------------------------------------------------------- +#define GL_FALSE 0 +#define GL_TRUE 1 + +#define GL_SCISSOR_TEST 0x0C11 +#define GL_TEXTURE_2D 0x0DE1 +#define GL_DEPTH_TEST 0x0B71 +#define GL_CULL_FACE 0x0B44 +#define GL_BLEND 0x0BE2 + +#define GL_VENDOR 0x1F00 +#define GL_RENDERER 0x1F01 +#define GL_VERSION 0x1F02 +#define GL_EXTENSIONS 0x1F03 + +//#define GL_ATTRIB_STACK_DEPTH 0x0BB0 +//#define GL_CLIENT_ATTRIB_STACK_DEPTH 0x0BB1 +#define GL_COLOR_CLEAR_VALUE 0x0C22 +//#define GL_COLOR_WRITEMASK 0x0C23 +//#define GL_CURRENT_INDEX 0x0B01 +#define GL_CURRENT_COLOR 0x0B00 +//#define GL_CURRENT_NORMAL 0x0B02 +//#define GL_CURRENT_RASTER_COLOR 0x0B04 +//#define GL_CURRENT_RASTER_DISTANCE 0x0B09 +//#define GL_CURRENT_RASTER_INDEX 0x0B05 +//#define GL_CURRENT_RASTER_POSITION 0x0B07 +//#define GL_CURRENT_RASTER_TEXTURE_COORDS 0x0B06 +//#define GL_CURRENT_RASTER_POSITION_VALID 0x0B08 +#define GL_CURRENT_TEXTURE_COORDS 0x0B03 +#define GL_POINT_SIZE 0x0B11 +#define GL_LINE_WIDTH 0x0B21 +//#define GL_INDEX_CLEAR_VALUE 0x0C20 +//#define GL_INDEX_MODE 0x0C30 +//#define GL_INDEX_WRITEMASK 0x0C21 +#define GL_MODELVIEW_MATRIX 0x0BA6 +#define GL_MODELVIEW_STACK_DEPTH 0x0BA3 +//#define GL_NAME_STACK_DEPTH 0x0D70 +#define GL_PROJECTION_MATRIX 0x0BA7 +#define GL_PROJECTION_STACK_DEPTH 0x0BA4 +//#define GL_RENDER_MODE 0x0C40 +//#define GL_RGBA_MODE 0x0C31 +#define GL_TEXTURE_MATRIX 0x0BA8 +#define GL_TEXTURE_STACK_DEPTH 0x0BA5 +#define GL_VIEWPORT 0x0BA2 + +#define GL_COLOR_BUFFER_BIT 0x00004000 +#define GL_DEPTH_BUFFER_BIT 0x00000100 + +#define GL_MODELVIEW 0x1700 +#define GL_PROJECTION 0x1701 +#define GL_TEXTURE 0x1702 + +#define GL_VERTEX_ARRAY 0x8074 +#define GL_NORMAL_ARRAY 0x8075 // WARNING: Not implemented (defined for RLGL) +#define GL_COLOR_ARRAY 0x8076 +//#define GL_INDEX_ARRAY 0x8077 +#define GL_TEXTURE_COORD_ARRAY 0x8078 + +#define GL_POINTS 0x0000 +#define GL_LINES 0x0001 +//#define GL_LINE_LOOP 0x0002 +//#define GL_LINE_STRIP 0x0003 +#define GL_TRIANGLES 0x0004 +//#define GL_TRIANGLE_STRIP 0x0005 +//#define GL_TRIANGLE_FAN 0x0006 +#define GL_QUADS 0x0007 +//#define GL_QUAD_STRIP 0x0008 +//#define GL_POLYGON 0x0009 + +#define GL_POINT 0x1B00 +#define GL_LINE 0x1B01 +#define GL_FILL 0x1B02 + +#define GL_FRONT 0x0404 +#define GL_BACK 0x0405 + +#define GL_ZERO 0 +#define GL_ONE 1 +#define GL_SRC_COLOR 0x0300 +#define GL_ONE_MINUS_SRC_COLOR 0x0301 +#define GL_SRC_ALPHA 0x0302 +#define GL_ONE_MINUS_SRC_ALPHA 0x0303 +#define GL_DST_ALPHA 0x0304 +#define GL_ONE_MINUS_DST_ALPHA 0x0305 +#define GL_DST_COLOR 0x0306 +#define GL_ONE_MINUS_DST_COLOR 0x0307 +#define GL_SRC_ALPHA_SATURATE 0x0308 + +#define GL_NEAREST 0x2600 +#define GL_LINEAR 0x2601 + +#define GL_REPEAT 0x2901 +#define GL_CLAMP 0x2900 + +#define GL_TEXTURE_MAG_FILTER 0x2800 +#define GL_TEXTURE_MIN_FILTER 0x2801 + +#define GL_TEXTURE_WRAP_S 0x2802 +#define GL_TEXTURE_WRAP_T 0x2803 + +#define GL_NO_ERROR 0 +#define GL_INVALID_ENUM 0x0500 +#define GL_INVALID_VALUE 0x0501 +#define GL_INVALID_OPERATION 0x0502 +#define GL_STACK_OVERFLOW 0x0503 +#define GL_STACK_UNDERFLOW 0x0504 +#define GL_OUT_OF_MEMORY 0x0505 + +#define GL_ALPHA 0x1906 +#define GL_LUMINANCE 0x1909 +#define GL_LUMINANCE_ALPHA 0x190A +#define GL_RGB 0x1907 +#define GL_RGBA 0x1908 + +#define GL_BYTE 0x1400 +#define GL_UNSIGNED_BYTE 0x1401 +#define GL_SHORT 0x1402 +#define GL_UNSIGNED_SHORT 0x1403 +#define GL_INT 0x1404 +#define GL_UNSIGNED_INT 0x1405 +#define GL_FLOAT 0x1406 + +// OpenGL Definitions NOT USED +#define GL_PERSPECTIVE_CORRECTION_HINT 0x0C50 +#define GL_PACK_ALIGNMENT 0x0D05 +#define GL_UNPACK_ALIGNMENT 0x0CF5 +#define GL_LINE_SMOOTH 0x0B20 +#define GL_SMOOTH 0x1D01 +#define GL_NICEST 0x1102 +#define GL_CCW 0x0901 +#define GL_CW 0x0900 +#define GL_NEVER 0x0200 +#define GL_LESS 0x0201 +#define GL_EQUAL 0x0202 +#define GL_LEQUAL 0x0203 +#define GL_GREATER 0x0204 +#define GL_NOTEQUAL 0x0205 +#define GL_GEQUAL 0x0206 +#define GL_ALWAYS 0x0207 + +//---------------------------------------------------------------------------------- +// OpenGL Bindings to rlsw +//---------------------------------------------------------------------------------- +#define glReadPixels(x, y, w, h, f, t, p) swCopyFramebuffer((x), (y), (w), (h), (f), (t), (p)) +#define glEnable(state) swEnable((state)) +#define glDisable(state) swDisable((state)) +#define glGetFloatv(pname, params) swGetFloatv((pname), (params)) +#define glGetString(pname) swGetString((pname)) +#define glGetError() swGetError() +#define glViewport(x, y, w, h) swViewport((x), (y), (w), (h)) +#define glScissor(x, y, w, h) swScissor((x), (y), (w), (h)) +#define glClearColor(r, g, b, a) swClearColor((r), (g), (b), (a)) +#define glClear(bitmask) swClear((bitmask)) +#define glBlendFunc(sfactor, dfactor) swBlendFunc((sfactor), (dfactor)) +#define glPolygonMode(face, mode) swPolygonMode((mode)) +#define glCullFace(face) swCullFace((face)) +#define glPointSize(size) swPointSize((size)) +#define glLineWidth(width) swLineWidth((width)) +#define glMatrixMode(mode) swMatrixMode((mode)) +#define glPushMatrix() swPushMatrix() +#define glPopMatrix() swPopMatrix() +#define glLoadIdentity() swLoadIdentity() +#define glTranslatef(x, y, z) swTranslatef((x), (y), (z)) +#define glRotatef(a, x, y, z) swRotatef((a), (x), (y), (z)) +#define glScalef(x, y, z) swScalef((x), (y), (z)) +#define glMultMatrixf(v) swMultMatrixf((v)) +#define glFrustum(l, r, b, t, n, f) swFrustum((l), (r), (b), (t), (n), (f)) +#define glOrtho(l, r, b, t, n, f) swOrtho((l), (r), (b), (t), (n), (f)) +#define glBegin(mode) swBegin((mode)) +#define glEnd() swEnd() +#define glVertex2i(x, y) swVertex2i((x), (y)) +#define glVertex2f(x, y) swVertex2f((x), (y)) +#define glVertex2fv(v) swVertex2fv((v)) +#define glVertex3i(x, y, z) swVertex3i((x), (y), (z)) +#define glVertex3f(x, y, z) swVertex3f((x), (y), (z)) +#define glvertex3fv(v) swVertex3fv((v)) +#define glVertex4i(x, y, z, w) swVertex4i((x), (y), (z), (w)) +#define glVertex4f(x, y, z, w) swVertex4f((x), (y), (z), (w)) +#define glVertex4fv(v) swVertex4fv((v)) +#define glColor3ub(r, g, b) swColor3ub((r), (g), (b)) +#define glColor3ubv(v) swColor3ubv((v)) +#define glColor3f(r, g, b) swColor3f((r), (g), (b)) +#define glColor3fv(v) swColor3fv((v)) +#define glColor4ub(r, g, b, a) swColor4ub((r), (g), (b), (a)) +#define glColor4ubv(v) swColor4ubv((v)) +#define glColor4f(r, g, b, a) swColor4f((r), (g), (b), (a)) +#define glColor4fv(v) swColor4fv((v)) +#define glTexCoord2f(u, v) swTexCoord2f((u), (v)) +#define glTexCoord2fv(v) swTexCoord2fv((v)) + +#define glEnableClientState(t) ((void)(t)) +#define glDisableClientState(t) swBindArray((t), 0) +#define glVertexPointer(sz, t, s, p) swBindArray(SW_VERTEX_ARRAY, (p)) +#define glTexCoordPointer(sz, t, s, p) swBindArray(SW_TEXTURE_COORD_ARRAY, (p)) +#define glColorPointer(sz, t, s, p) swBindArray(SW_COLOR_ARRAY, (p)) +#define glDrawArrays(m, o, c) swDrawArrays((m), (o), (c)) +#define glGenTextures(c, v) swGenTextures((c), (v)) +#define glDeleteTextures(c, v) swDeleteTextures((c), (v)) +#define glTexImage2D(tr, l, if, w, h, b, f, t, p) swTexImage2D((w), (h), (f), (t), SW_GL_BINDING_COPY_TEXTURE, (p)) +#define glTexParameteri(tr, pname, param) swTexParameteri((pname), (param)) +#define glBindTexture(tr, id) swBindTexture((id)) + +// OpenGL functions NOT IMPLEMENTED by rlsw +#define glClearDepth(X) ((void)(X)) +#define glDepthMask(X) ((void)(X)) +#define glColorMask(X,Y,Z,W) ((void)(X),(void)(Y),(void)(Z),(void)(W)) +#define glPixelStorei(X,Y) ((void)(X),(void)(Y)) +#define glHint(X,Y) ((void)(X),(void)(Y)) +#define glShadeModel(X) ((void)(X)) +#define glFrontFace(X) ((void)(X)) +#define glDepthFunc(X) ((void)(X)) +#define glTexSubImage2D(X,Y,Z,W,A,B,C,D,E) ((void)(X),(void)(Y),(void)(Z),(void)(W),(void)(A),(void)(B),(void)(C),(void)(D),(void)(E)) +#define glGetTexImage(X,Y,Z,W,A) ((void)(X),(void)(Y),(void)(Z),(void)(W),(void)(A)) +#define glDrawElements(X,Y,Z,W) ((void)(X),(void)(Y),(void)(Z),(void)(W)) +#define glNormal3f(X,Y,Z) ((void)(X),(void)(Y),(void)(Z)) +#define glNormal3fv(X) ((void)(X)) +#define glNormalPointer(X,Y,Z) ((void)(X),(void)(Y),(void)(Z)) + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- +typedef enum { + SW_SCISSOR_TEST = GL_SCISSOR_TEST, + SW_TEXTURE_2D = GL_TEXTURE_2D, + SW_DEPTH_TEST = GL_DEPTH_TEST, + SW_CULL_FACE = GL_CULL_FACE, + SW_BLEND = GL_BLEND +} SWstate; + +typedef enum { + SW_VENDOR = GL_VENDOR, + SW_RENDERER = GL_RENDERER, + SW_VERSION = GL_VERSION, + SW_EXTENSIONS = GL_EXTENSIONS, + SW_COLOR_CLEAR_VALUE = GL_COLOR_CLEAR_VALUE, + SW_CURRENT_COLOR = GL_CURRENT_COLOR, + SW_CURRENT_TEXTURE_COORDS = GL_CURRENT_TEXTURE_COORDS, + SW_POINT_SIZE = GL_POINT_SIZE, + SW_LINE_WIDTH = GL_LINE_WIDTH, + SW_MODELVIEW_MATRIX = GL_MODELVIEW_MATRIX, + SW_MODELVIEW_STACK_DEPTH = GL_MODELVIEW_STACK_DEPTH, + SW_PROJECTION_MATRIX = GL_PROJECTION_MATRIX, + SW_PROJECTION_STACK_DEPTH = GL_PROJECTION_STACK_DEPTH, + SW_TEXTURE_MATRIX = GL_TEXTURE_MATRIX, + SW_TEXTURE_STACK_DEPTH = GL_TEXTURE_STACK_DEPTH, + SW_VIEWPORT = GL_VIEWPORT +} SWget; + +typedef enum { + SW_COLOR_BUFFER_BIT = GL_COLOR_BUFFER_BIT, + SW_DEPTH_BUFFER_BIT = GL_DEPTH_BUFFER_BIT +} SWbuffer; + +typedef enum { + SW_PROJECTION = GL_PROJECTION, + SW_MODELVIEW = GL_MODELVIEW, + SW_TEXTURE = GL_TEXTURE +} SWmatrix; + +typedef enum { + SW_VERTEX_ARRAY = GL_VERTEX_ARRAY, + SW_TEXTURE_COORD_ARRAY = GL_TEXTURE_COORD_ARRAY, + SW_COLOR_ARRAY = GL_COLOR_ARRAY +} SWarray; + +typedef enum { + SW_POINTS = GL_POINTS, + SW_LINES = GL_LINES, + SW_TRIANGLES = GL_TRIANGLES, + SW_QUADS = GL_QUADS +} SWdraw; + +typedef enum { + SW_POINT = GL_POINT, + SW_LINE = GL_LINE, + SW_FILL = GL_FILL +} SWpoly; + +typedef enum { + SW_FRONT = GL_FRONT, + SW_BACK = GL_BACK, +} SWface; + +typedef enum { + SW_ZERO = GL_ZERO, + SW_ONE = GL_ONE, + SW_SRC_COLOR = GL_SRC_COLOR, + SW_ONE_MINUS_SRC_COLOR = GL_ONE_MINUS_SRC_COLOR, + SW_SRC_ALPHA = GL_SRC_ALPHA, + SW_ONE_MINUS_SRC_ALPHA = GL_ONE_MINUS_SRC_ALPHA, + SW_DST_ALPHA = GL_DST_ALPHA, + SW_ONE_MINUS_DST_ALPHA = GL_ONE_MINUS_DST_ALPHA, + SW_DST_COLOR = GL_DST_COLOR, + SW_ONE_MINUS_DST_COLOR = GL_ONE_MINUS_DST_COLOR, + SW_SRC_ALPHA_SATURATE = GL_SRC_ALPHA_SATURATE +} SWfactor; + +typedef enum { + SW_LUMINANCE = GL_LUMINANCE, + SW_LUMINANCE_ALPHA = GL_LUMINANCE_ALPHA, + SW_RGB = GL_RGB, + SW_RGBA = GL_RGBA, +} SWformat; + +typedef enum { + SW_UNSIGNED_BYTE = GL_UNSIGNED_BYTE, + SW_BYTE = GL_BYTE, + SW_UNSIGNED_SHORT = GL_UNSIGNED_SHORT, + SW_SHORT = GL_SHORT, + SW_UNSIGNED_INT = GL_UNSIGNED_INT, + SW_INT = GL_INT, + SW_FLOAT = GL_FLOAT +} SWtype; + +typedef enum { + SW_NEAREST = GL_NEAREST, + SW_LINEAR = GL_LINEAR +} SWfilter; + +typedef enum { + SW_REPEAT = GL_REPEAT, + SW_CLAMP = GL_CLAMP, +} SWwrap; + +typedef enum { + SW_TEXTURE_MIN_FILTER = GL_TEXTURE_MIN_FILTER, + SW_TEXTURE_MAG_FILTER = GL_TEXTURE_MAG_FILTER, + SW_TEXTURE_WRAP_S = GL_TEXTURE_WRAP_S, + SW_TEXTURE_WRAP_T = GL_TEXTURE_WRAP_T +} SWtexparam; + +typedef enum { + SW_NO_ERROR = GL_NO_ERROR, + SW_INVALID_ENUM = GL_INVALID_ENUM, + SW_INVALID_VALUE = GL_INVALID_VALUE, + SW_STACK_OVERFLOW = GL_STACK_OVERFLOW, + SW_STACK_UNDERFLOW = GL_STACK_UNDERFLOW, + SW_INVALID_OPERATION = GL_INVALID_OPERATION, +} SWerrcode; + +//------------------------------------------------------------------------------------ +// Functions Declaration - Public API +//------------------------------------------------------------------------------------ +SWAPI bool swInit(int w, int h); +SWAPI void swClose(void); + +SWAPI bool swResizeFramebuffer(int w, int h); +SWAPI void swCopyFramebuffer(int x, int y, int w, int h, SWformat format, SWtype type, void *pixels); +SWAPI void swBlitFramebuffer(int xDst, int yDst, int wDst, int hDst, int xSrc, int ySrc, int wSrc, int hSrc, SWformat format, SWtype type, void *pixels); +SWAPI void *swGetColorBuffer(int *w, int *h); + +SWAPI void swEnable(SWstate state); +SWAPI void swDisable(SWstate state); + +SWAPI void swGetFloatv(SWget name, float *v); +SWAPI const char *swGetString(SWget name); +SWAPI SWerrcode swGetError(void); + +SWAPI void swViewport(int x, int y, int width, int height); +SWAPI void swScissor(int x, int y, int width, int height); + +SWAPI void swClearColor(float r, float g, float b, float a); +SWAPI void swClear(uint32_t bitmask); + +SWAPI void swBlendFunc(SWfactor sfactor, SWfactor dfactor); +SWAPI void swPolygonMode(SWpoly mode); +SWAPI void swCullFace(SWface face); + +SWAPI void swPointSize(float size); +SWAPI void swLineWidth(float width); + +SWAPI void swMatrixMode(SWmatrix mode); +SWAPI void swPushMatrix(void); +SWAPI void swPopMatrix(void); +SWAPI void swLoadIdentity(void); +SWAPI void swTranslatef(float x, float y, float z); +SWAPI void swRotatef(float angle, float x, float y, float z); +SWAPI void swScalef(float x, float y, float z); +SWAPI void swMultMatrixf(const float *mat); +SWAPI void swFrustum(double left, double right, double bottom, double top, double znear, double zfar); +SWAPI void swOrtho(double left, double right, double bottom, double top, double znear, double zfar); + +SWAPI void swBegin(SWdraw mode); +SWAPI void swEnd(void); + +SWAPI void swVertex2i(int x, int y); +SWAPI void swVertex2f(float x, float y); +SWAPI void swVertex2fv(const float *v); +SWAPI void swVertex3i(int x, int y, int z); +SWAPI void swVertex3f(float x, float y, float z); +SWAPI void swVertex3fv(const float *v); +SWAPI void swVertex4i(int x, int y, int z, int w); +SWAPI void swVertex4f(float x, float y, float z, float w); +SWAPI void swVertex4fv(const float *v); + +SWAPI void swColor3ub(uint8_t r, uint8_t g, uint8_t b); +SWAPI void swColor3ubv(const uint8_t *v); +SWAPI void swColor3f(float r, float g, float b); +SWAPI void swColor3fv(const float *v); +SWAPI void swColor4ub(uint8_t r, uint8_t g, uint8_t b, uint8_t a); +SWAPI void swColor4ubv(const uint8_t *v); +SWAPI void swColor4f(float r, float g, float b, float a); +SWAPI void swColor4fv(const float *v); + +SWAPI void swTexCoord2f(float u, float v); +SWAPI void swTexCoord2fv(const float *v); + +SWAPI void swBindArray(SWarray type, void *buffer); +SWAPI void swDrawArrays(SWdraw mode, int offset, int count); + +SWAPI void swGenTextures(int count, uint32_t *textures); +SWAPI void swDeleteTextures(int count, uint32_t *textures); + +SWAPI void swTexImage2D(int width, int height, SWformat format, SWtype type, bool copy, const void *data); +SWAPI void swTexParameteri(int param, int value); +SWAPI void swBindTexture(uint32_t id); + +#endif // RLSW_H + +/*********************************************************************************** +* +* RLSW IMPLEMENTATION +* +************************************************************************************/ +#define RLSW_IMPLEMENTATION +#if defined(RLSW_IMPLEMENTATION) + +#include +#include +#include // Required for: floorf(), fabsf() + +//---------------------------------------------------------------------------------- +// Defines and Macros +//---------------------------------------------------------------------------------- +#define SW_PI 3.14159265358979323846f +#define SW_DEG2RAD (SW_PI/180.0f) +#define SW_RAD2DEG (180.0f/SW_PI) + +#define SW_COLOR_PIXEL_SIZE (SW_COLOR_BUFFER_BITS/8) +#define SW_DEPTH_PIXEL_SIZE (SW_DEPTH_BUFFER_BITS/8) + +#define SW_STATE_CHECK(flags) ((RLSW.stateFlags & (flags)) == (flags)) + +#define SW_STATE_SCISSOR_TEST (1 << 0) +#define SW_STATE_TEXTURE_2D (1 << 1) +#define SW_STATE_DEPTH_TEST (1 << 2) +#define SW_STATE_CULL_FACE (1 << 3) +#define SW_STATE_BLEND (1 << 4) + +//---------------------------------------------------------------------------------- +// Module Types and Structures Definition +//---------------------------------------------------------------------------------- +// Pixel data format type +// NOTE: Enum aligned with raylib PixelFormat +typedef enum { + SW_PIXELFORMAT_UNKNOWN = 0, + SW_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE, // 8 bit per pixel (no alpha) + SW_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA, // 8*2 bpp (2 channels) + SW_PIXELFORMAT_UNCOMPRESSED_R5G6B5, // 16 bpp + SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8, // 24 bpp + SW_PIXELFORMAT_UNCOMPRESSED_R5G5B5A1, // 16 bpp (1 bit alpha) + SW_PIXELFORMAT_UNCOMPRESSED_R4G4B4A4, // 16 bpp (4 bit alpha) + SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, // 32 bpp + SW_PIXELFORMAT_UNCOMPRESSED_R32, // 32 bpp (1 channel - float) + SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32, // 32*3 bpp (3 channels - float) + SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32, // 32*4 bpp (4 channels - float) + SW_PIXELFORMAT_UNCOMPRESSED_R16, // 16 bpp (1 channel - half float) + SW_PIXELFORMAT_UNCOMPRESSED_R16G16B16, // 16*3 bpp (3 channels - half float) + SW_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16, // 16*4 bpp (4 channels - half float) +} sw_pixelformat_t; + +typedef void (*sw_factor_f)( + float *SW_RESTRICT factor, + const float *SW_RESTRICT src, + const float *SW_RESTRICT dst +); + +typedef float sw_matrix_t[4*4]; +typedef uint16_t sw_half_t; + +typedef struct { + float position[4]; // Position coordinates + float texcoord[2]; // Texture coordinates + float color[4]; // Color value (RGBA) + + float homogeneous[4]; // Homogeneous coordinates + float screen[2]; // Screen coordinates +} sw_vertex_t; + +typedef struct { + // Dirty hack for copied data + // TODO: Rework copied image handling + union { + const void *cptr; // NOTE: Is used for all data reads + void *ptr; // WARNING: Should only be used to allocate and free data + } pixels; + + int width, height; // Dimensions of the texture + int wMinus1, hMinus1; // Dimensions minus one + sw_pixelformat_t format; // Pixel format (internal representation) + + SWfilter minFilter; // Minification filter + SWfilter magFilter; // Magnification filter + + SWwrap sWrap; // texcoord.x wrap mode + SWwrap tWrap; // texcoord.y wrap mode + + float tx; // Texel width + float ty; // Texel height + + bool copy; // Flag indicating whether memory has been allocated + +} sw_texture_t; + +typedef struct { + void *color; + void *depth; + int width; + int height; + int allocSz; +} sw_framebuffer_t; + +typedef struct { + sw_framebuffer_t framebuffer; // Main framebuffer + float clearColor[4]; // Color used to clear the screen + float clearDepth; // Depth value used to clear the screen + + int vpCenter[2]; // Viewport center + int vpHalfSize[2]; // Viewport half dimensions + int vpSize[2]; // Viewport dimensions (minus one) + int vpMin[2]; // Viewport minimum renderable point (top-left) + int vpMax[2]; // Viewport maximum renderable point (bottom-right) + + int scMin[2]; // Scissor rectangle minimum renderable point (top-left) + int scMax[2]; // Scissor rectangle maximum renderable point (bottom-right) + float scClipMin[2]; // Scissor rectangle minimum renderable point in clip space + float scClipMax[2]; // Scissor rectangle maximum renderable point in clip space + + uint32_t currentTexture; // Current active texture id + + struct { + float *positions; + float *texcoords; + uint8_t *colors; + } array; + + struct { + float texcoord[2]; + float color[4]; + } current; + + sw_vertex_t vertexBuffer[SW_MAX_CLIPPED_POLYGON_VERTICES]; // Buffer used for storing primitive vertices, used for processing and rendering + int vertexCounter; // Number of vertices in 'ctx.vertexBuffer' + + SWdraw drawMode; // Current primitive mode (e.g., lines, triangles) + SWpoly polyMode; // Current polygon filling mode (e.g., lines, triangles) + int reqVertices; // Number of vertices required for the primitive being drawn + float pointRadius; // Rasterized point radius + float lineWidth; // Rasterized line width + + sw_matrix_t stackProjection[SW_MAX_PROJECTION_STACK_SIZE]; // Projection matrix stack for push/pop operations + sw_matrix_t stackModelview[SW_MAX_MODELVIEW_STACK_SIZE]; // Modelview matrix stack for push/pop operations + sw_matrix_t stackTexture[SW_MAX_TEXTURE_STACK_SIZE]; // Texture matrix stack for push/pop operations + uint32_t stackProjectionCounter; // Counter for matrix stack operations + uint32_t stackModelviewCounter; // Counter for matrix stack operations + uint32_t stackTextureCounter; // Counter for matrix stack operations + SWmatrix currentMatrixMode; // Current matrix mode (e.g., sw_MODELVIEW, sw_PROJECTION) + sw_matrix_t *currentMatrix; // Pointer to the currently used matrix according to the mode + sw_matrix_t matMVP; // Model view projection matrix, calculated and used internally + bool isDirtyMVP; // Indicates if the MVP matrix should be rebuilt + + SWfactor srcFactor; + SWfactor dstFactor; + + sw_factor_f srcFactorFunc; + sw_factor_f dstFactorFunc; + + SWface cullFace; // Faces to cull + SWerrcode errCode; // Last error code + + sw_texture_t *loadedTextures; + int loadedTextureCount; + + uint32_t *freeTextureIds; + int freeTextureIdCount; + + uint32_t stateFlags; + +} sw_context_t; + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +static sw_context_t RLSW = { 0 }; + +//---------------------------------------------------------------------------------- +// Module Functions Declaration +//---------------------------------------------------------------------------------- +static inline void sw_matrix_id(sw_matrix_t dst) +{ + dst[0] = 1, dst[1] = 0, dst[2] = 0, dst[3] = 0; + dst[4] = 0, dst[5] = 1, dst[6] = 0, dst[7] = 0; + dst[8] = 0, dst[9] = 0, dst[10] = 1, dst[11] = 0; + dst[12] = 0, dst[13] = 0, dst[14] = 0, dst[15] = 1; +} + +static inline void sw_matrix_mul_rst(float *SW_RESTRICT dst, const float *SW_RESTRICT left, const float *SW_RESTRICT right) +{ + float l00 = left[0], l01 = left[1], l02 = left[2], l03 = left[3]; + float l10 = left[4], l11 = left[5], l12 = left[6], l13 = left[7]; + float l20 = left[8], l21 = left[9], l22 = left[10], l23 = left[11]; + float l30 = left[12], l31 = left[13], l32 = left[14], l33 = left[15]; + + dst[0] = l00*right[0] + l01*right[4] + l02*right[8] + l03*right[12]; + dst[4] = l10*right[0] + l11*right[4] + l12*right[8] + l13*right[12]; + dst[8] = l20*right[0] + l21*right[4] + l22*right[8] + l23*right[12]; + dst[12] = l30*right[0] + l31*right[4] + l32*right[8] + l33*right[12]; + + dst[1] = l00*right[1] + l01*right[5] + l02*right[9] + l03*right[13]; + dst[5] = l10*right[1] + l11*right[5] + l12*right[9] + l13*right[13]; + dst[9] = l20*right[1] + l21*right[5] + l22*right[9] + l23*right[13]; + dst[13] = l30*right[1] + l31*right[5] + l32*right[9] + l33*right[13]; + + dst[2] = l00*right[2] + l01*right[6] + l02*right[10] + l03*right[14]; + dst[6] = l10*right[2] + l11*right[6] + l12*right[10] + l13*right[14]; + dst[10] = l20*right[2] + l21*right[6] + l22*right[10] + l23*right[14]; + dst[14] = l30*right[2] + l31*right[6] + l32*right[10] + l33*right[14]; + + dst[3] = l00*right[3] + l01*right[7] + l02*right[11] + l03*right[15]; + dst[7] = l10*right[3] + l11*right[7] + l12*right[11] + l13*right[15]; + dst[11] = l20*right[3] + l21*right[7] + l22*right[11] + l23*right[15]; + dst[15] = l30*right[3] + l31*right[7] + l32*right[11] + l33*right[15]; +} + +static inline void sw_matrix_mul(sw_matrix_t dst, const sw_matrix_t left, const sw_matrix_t right) +{ + float result[16]; + + sw_matrix_mul_rst(result, left, right); + + for (int i = 0; i < 16; i++) dst[i] = result[i]; +} + +static inline float sw_saturate(float x) +{ + union { float f; uint32_t u; } fb; + fb.f = x; + + // Check if x < 0.0f + // If sign bit is set (MSB), x is negative + if ((fb.u & 0x80000000) != 0) return 0.0f; + + // Check if x > 1.0f + // Works for positive floats: IEEE 754 ordering matches integer ordering + if (fb.u > 0x3F800000) return 1.0f; + + // x is in [0.0f, 1.0f] + return x; +} + +static inline float sw_fract(float x) +{ + return (x - floorf(x)); +} + +static inline int sw_clampi(int v, int min, int max) +{ + if (v < min) return min; + if (v > max) return max; + return v; +} + +static inline void sw_lerp_vertex_PTCH( + sw_vertex_t *SW_RESTRICT out, + const sw_vertex_t *SW_RESTRICT a, + const sw_vertex_t *SW_RESTRICT b, + float t) +{ + const float tInv = 1.0f - t; + + // Position interpolation (4 components) + out->position[0] = a->position[0]*tInv + b->position[0]*t; + out->position[1] = a->position[1]*tInv + b->position[1]*t; + out->position[2] = a->position[2]*tInv + b->position[2]*t; + out->position[3] = a->position[3]*tInv + b->position[3]*t; + + // Texture coordinate interpolation (2 components) + out->texcoord[0] = a->texcoord[0]*tInv + b->texcoord[0]*t; + out->texcoord[1] = a->texcoord[1]*tInv + b->texcoord[1]*t; + + // Color interpolation (4 components) + out->color[0] = a->color[0]*tInv + b->color[0]*t; + out->color[1] = a->color[1]*tInv + b->color[1]*t; + out->color[2] = a->color[2]*tInv + b->color[2]*t; + out->color[3] = a->color[3]*tInv + b->color[3]*t; + + // Homogeneous coordinate interpolation (4 components) + out->homogeneous[0] = a->homogeneous[0]*tInv + b->homogeneous[0]*t; + out->homogeneous[1] = a->homogeneous[1]*tInv + b->homogeneous[1]*t; + out->homogeneous[2] = a->homogeneous[2]*tInv + b->homogeneous[2]*t; + out->homogeneous[3] = a->homogeneous[3]*tInv + b->homogeneous[3]*t; +} + +static inline void sw_get_vertex_grad_PTCH( + sw_vertex_t *SW_RESTRICT out, + const sw_vertex_t *SW_RESTRICT a, + const sw_vertex_t *SW_RESTRICT b, + float scale) +{ + // Calculate gradients for Position + out->position[0] = (b->position[0] - a->position[0])*scale; + out->position[1] = (b->position[1] - a->position[1])*scale; + out->position[2] = (b->position[2] - a->position[2])*scale; + out->position[3] = (b->position[3] - a->position[3])*scale; + + // Calculate gradients for Texture coordinates + out->texcoord[0] = (b->texcoord[0] - a->texcoord[0])*scale; + out->texcoord[1] = (b->texcoord[1] - a->texcoord[1])*scale; + + // Calculate gradients for Color + out->color[0] = (b->color[0] - a->color[0])*scale; + out->color[1] = (b->color[1] - a->color[1])*scale; + out->color[2] = (b->color[2] - a->color[2])*scale; + out->color[3] = (b->color[3] - a->color[3])*scale; + + // Calculate gradients for Homogeneous coordinates + out->homogeneous[0] = (b->homogeneous[0] - a->homogeneous[0])*scale; + out->homogeneous[1] = (b->homogeneous[1] - a->homogeneous[1])*scale; + out->homogeneous[2] = (b->homogeneous[2] - a->homogeneous[2])*scale; + out->homogeneous[3] = (b->homogeneous[3] - a->homogeneous[3])*scale; +} + +static inline void sw_add_vertex_grad_PTCH( + sw_vertex_t *SW_RESTRICT out, + const sw_vertex_t *SW_RESTRICT gradients) +{ + // Add gradients to Position + out->position[0] += gradients->position[0]; + out->position[1] += gradients->position[1]; + out->position[2] += gradients->position[2]; + out->position[3] += gradients->position[3]; + + // Add gradients to Texture coordinates + out->texcoord[0] += gradients->texcoord[0]; + out->texcoord[1] += gradients->texcoord[1]; + + // Add gradients to Color + out->color[0] += gradients->color[0]; + out->color[1] += gradients->color[1]; + out->color[2] += gradients->color[2]; + out->color[3] += gradients->color[3]; + + // Add gradients to Homogeneous coordinates + out->homogeneous[0] += gradients->homogeneous[0]; + out->homogeneous[1] += gradients->homogeneous[1]; + out->homogeneous[2] += gradients->homogeneous[2]; + out->homogeneous[3] += gradients->homogeneous[3]; +} + +// Half floating point management functions +static inline uint32_t sw_f16_to_f32_ui(uint16_t h) +{ + uint32_t s = (uint32_t)(h & 0x8000) << 16; + int32_t em = h & 0x7fff; + + // bias exponent and pad mantissa with 0; 112 is relative exponent bias (127-15) + int32_t r = (em + (112 << 10)) << 13; + + // denormal: flush to zero + r = (em < (1 << 10))? 0 : r; + + // infinity/NaN; note that we preserve NaN payload as a byproduct of unifying inf/nan cases + // 112 is an exponent bias fixup; since we already applied it once, applying it twice converts 31 to 255 + r += (em >= (31 << 10))? (112 << 23) : 0; + + return s | r; +} + +static inline float sw_f16_to_f32(sw_half_t y) +{ + union { float f; uint32_t i; } v = { .i = sw_f16_to_f32_ui(y) }; + + return v.f; +} + +static inline uint16_t sw_f16_from_f32_ui(uint32_t ui) +{ + int32_t s = (ui >> 16) & 0x8000; + int32_t em = ui & 0x7fffffff; + + // Bias exponent and round to nearest; 112 is relative exponent bias (127-15) + int32_t h = (em - (112 << 23) + (1 << 12)) >> 13; + + // Underflow: flush to zero; 113 encodes exponent -14 + h = (em < (113 << 23))? 0 : h; + + // Overflow: infinity; 143 encodes exponent 16 + h = (em >= (143 << 23))? 0x7c00 : h; + + // NaN; note that we convert all types of NaN to qNaN + h = (em > (255 << 23))? 0x7e00 : h; + + return (uint16_t)(s | h); +} + +static inline sw_half_t sw_f16_from_f32(float i) +{ + union { float f; uint32_t i; } v; + v.f = i; + return sw_f16_from_f32_ui(v.i); +} + +// Framebuffer management functions +static inline bool sw_framebuffer_load(int w, int h) +{ + int size = w*h; + + RLSW.framebuffer.color = SW_MALLOC(SW_COLOR_PIXEL_SIZE*size); + if (RLSW.framebuffer.color == NULL) return false; + + RLSW.framebuffer.depth = SW_MALLOC(SW_DEPTH_PIXEL_SIZE*size); + if (RLSW.framebuffer.depth == NULL) return false; + + RLSW.framebuffer.width = w; + RLSW.framebuffer.height = h; + RLSW.framebuffer.allocSz = size; + + return true; +} + +static inline bool sw_framebuffer_resize(int w, int h) +{ + int newSize = w*h; + + if (newSize <= RLSW.framebuffer.allocSz) + { + RLSW.framebuffer.width = w; + RLSW.framebuffer.height = h; + return true; + } + + void *newColor = SW_REALLOC(RLSW.framebuffer.color, newSize); + if (newColor == NULL) return false; + + void *newDepth = SW_REALLOC(RLSW.framebuffer.depth, newSize); + if (newDepth == NULL) return false; + + RLSW.framebuffer.color = newColor; + RLSW.framebuffer.depth = newDepth; + + RLSW.framebuffer.width = w; + RLSW.framebuffer.height = h; + RLSW.framebuffer.allocSz = newSize; + + return true; +} + +static inline void *sw_framebuffer_get_color_addr(const void *ptr, uint32_t offset) +{ + return (uint8_t *)ptr + offset*SW_COLOR_PIXEL_SIZE; +} + +static inline void sw_framebuffer_inc_color_addr(void **ptr) +{ + *ptr = (void *)(((uint8_t *)*ptr) + SW_COLOR_PIXEL_SIZE); +} + +static inline void sw_framebuffer_inc_const_color_addr(const void **ptr) +{ + *ptr = (const void *)(((const uint8_t *)*ptr) + SW_COLOR_PIXEL_SIZE); +} + +static inline void *sw_framebuffer_get_depth_addr(const void *ptr, uint32_t offset) +{ + return (uint8_t *)ptr + offset*SW_DEPTH_PIXEL_SIZE; +} + +static inline void sw_framebuffer_inc_depth_addr(void* *ptr) +{ + *ptr = (void*)(((uint8_t *)*ptr) + SW_DEPTH_PIXEL_SIZE); +} + +#if (SW_COLOR_BUFFER_BITS == 8) // RGB - (3:3:2) +static inline void sw_framebuffer_read_color(float dst[4], const void *src) +{ + uint8_t pixel = ((uint8_t *)src)[0]; + + dst[0] = ((pixel >> 5) & 0x07)*(1.0f/7.0f); + dst[1] = ((pixel >> 2) & 0x07)*(1.0f/7.0f); + dst[2] = (pixel & 0x03)*(1.0f/3.0f); + dst[3] = 1.0f; +} + +static inline void sw_framebuffer_read_color8(uint8_t dst[4], const void *src) +{ + uint8_t pixel = ((const uint8_t *)src)[0]; + + uint8_t r = (pixel >> 5) & 0x07; + uint8_t g = (pixel >> 2) & 0x07; + uint8_t b = pixel & 0x03; + + dst[0] = (r*255 + 3)/7; + dst[1] = (g*255 + 3)/7; + dst[2] = (b*255 + 1)/3; + dst[3] = 255; +} + +static inline void sw_framebuffer_write_color(void *dst, const float color[3]) +{ + uint8_t r = ((uint8_t)(color[0]*UINT8_MAX) >> 5) & 0x07; + uint8_t g = ((uint8_t)(color[1]*UINT8_MAX) >> 5) & 0x07; + uint8_t b = ((uint8_t)(color[2]*UINT8_MAX) >> 6) & 0x03; + + ((uint8_t *)dst)[0] = (r << 5) | (g << 2) | b; +} + +static inline void sw_framebuffer_fill_color(void *ptr, int size, const float color[3]) +{ + uint8_t r8 = (uint8_t)(color[0]*7.0f + 0.5f); + uint8_t g8 = (uint8_t)(color[1]*7.0f + 0.5f); + uint8_t b8 = (uint8_t)(color[2]*3.0f + 0.5f); + + uint8_t packedColor = ((r8 & 0x07) << 5) | ((g8 & 0x07) << 2) | (b8 & 0x03); + + uint8_t *p = (uint8_t *)ptr; + + if (RLSW.stateFlags & SW_STATE_SCISSOR_TEST) + { + int wScissor = RLSW.scMax[0] - RLSW.scMin[0] + 1; + for (int y = RLSW.scMin[1]; y <= RLSW.scMax[1]; y++) + { + uint8_t *curPtr = p + y*RLSW.framebuffer.width + RLSW.scMin[0]; + for (int xCount = 0; xCount < wScissor; xCount++) *curPtr++ = packedColor; + } + } + else + { + for (int i = 0; i < size; i++) *p++ = packedColor; + } +} + +#elif (SW_COLOR_BUFFER_BITS == 16) // RGB - (5:6:5) + +static inline void sw_framebuffer_read_color(float dst[4], const void *src) +{ + uint16_t pixel = ((uint16_t *)src)[0]; + + dst[0] = ((pixel >> 11) & 0x1F)*(1.0f/31.0f); + dst[1] = ((pixel >> 5) & 0x3F)*(1.0f/63.0f); + dst[2] = (pixel & 0x1F)*(1.0f/31.0f); + dst[3] = 1.0f; +} + +static inline void sw_framebuffer_read_color8(uint8_t dst[4], const void *src) +{ + uint16_t pixel = ((const uint16_t *)src)[0]; + + uint8_t r = (pixel >> 11) & 0x1F; + uint8_t g = (pixel >> 5) & 0x3F; + uint8_t b = pixel & 0x1F; + + dst[0] = (r*255 + 15)/31; + dst[1] = (g*255 + 31)/63; + dst[2] = (b*255 + 15)/31; + dst[3] = 255; +} + +static inline void sw_framebuffer_write_color(void *dst, const float color[3]) +{ + uint8_t r = (uint8_t)(color[0]*31.0f + 0.5f) & 0x1F; + uint8_t g = (uint8_t)(color[1]*63.0f + 0.5f) & 0x3F; + uint8_t b = (uint8_t)(color[2]*31.0f + 0.5f) & 0x1F; + + ((uint16_t *)dst)[0] = (r << 11) | (g << 5) | b; +} + +static inline void sw_framebuffer_fill_color(void *ptr, int size, const float color[3]) +{ + uint16_t r16 = (uint16_t)(color[0]*31.0f + 0.5f); + uint16_t g_16 = (uint16_t)(color[1]*63.0f + 0.5f); + uint16_t b_16 = (uint16_t)(color[2]*31.0f + 0.5f); + + uint16_t packedColor = ((r16 & 0x1F) << 11) | ((g_16 & 0x3F) << 5) | (b_16 & 0x1F); + + uint16_t *p = (uint16_t *)ptr; + + if (RLSW.stateFlags & SW_STATE_SCISSOR_TEST) + { + int wScissor = RLSW.scMax[0] - RLSW.scMin[0] + 1; + for (int y = RLSW.scMin[1]; y <= RLSW.scMax[1]; y++) + { + uint16_t *curPtr = p + y*RLSW.framebuffer.width + RLSW.scMin[0]; + for (int xCount = 0; xCount < wScissor; xCount++) *curPtr++ = packedColor; + } + } + else + { + for (int i = 0; i < size; i++) *p++ = packedColor; + } +} + +#elif (SW_COLOR_BUFFER_BITS == 24) // RGB - (8:8:8) + +static inline void sw_framebuffer_read_color(float dst[4], const void *src) +{ + dst[0] = ((uint8_t *)src)[0]*(1.0f/255.0f); + dst[1] = ((uint8_t *)src)[1]*(1.0f/255.0f); + dst[2] = ((uint8_t *)src)[2]*(1.0f/255.0f); + dst[3] = 1.0f; +} + +static inline void sw_framebuffer_read_color8(uint8_t dst[4], const void *src) +{ + dst[0] = ((uint8_t *)src)[0]; + dst[1] = ((uint8_t *)src)[1]; + dst[2] = ((uint8_t *)src)[2]; + dst[3] = 255; +} + +static inline void sw_framebuffer_write_color(void *dst, const float color[3]) +{ + ((uint8_t *)dst)[0] = (uint8_t)(color[0]*UINT8_MAX); + ((uint8_t *)dst)[1] = (uint8_t)(color[1]*UINT8_MAX); + ((uint8_t *)dst)[2] = (uint8_t)(color[2]*UINT8_MAX); +} + +static inline void sw_framebuffer_fill_color(void *ptr, int size, const float color[3]) +{ + uint8_t r = (uint8_t)(color[0]*255.0f); + uint8_t g = (uint8_t)(color[1]*255.0f); + uint8_t b = (uint8_t)(color[2]*255.0f); + + uint8_t *p = (uint8_t *)ptr; + + if (RLSW.stateFlags & SW_STATE_SCISSOR_TEST) + { + int wScissor = RLSW.scMax[0] - RLSW.scMin[0] + 1; + for (int y = RLSW.scMin[1]; y <= RLSW.scMax[1]; y++) + { + uint8_t *curPtr = p + 3*(y*RLSW.framebuffer.width + RLSW.scMin[0]); + for (int xCount = 0; xCount < wScissor; xCount++) + { + *curPtr++ = r; + *curPtr++ = g; + *curPtr++ = b; + } + } + } + else + { + for (int i = 0; i < size; i++) + { + *p++ = r; + *p++ = g; + *p++ = b; + } + } +} + +#endif // SW_COLOR_BUFFER_BITS + +#if (SW_DEPTH_BUFFER_BITS == 8) + +static inline float sw_framebuffer_read_depth(const void *src) +{ + return (float)((uint8_t *)src)[0]*(1.0f/UINT8_MAX); +} + +static inline void sw_framebuffer_write_depth(void *dst, float depth) +{ + ((uint8_t *)dst)[0] = (uint8_t)(depth*UINT8_MAX); +} + +static inline void sw_framebuffer_fill_depth(void *ptr, int size, float value) +{ + uint8_t d8 = (uint8_t)(value*UINT8_MAX); + uint8_t *p = (uint8_t *)ptr; + + if (RLSW.stateFlags & SW_STATE_SCISSOR_TEST) + { + int wScissor = RLSW.scMax[0] - RLSW.scMin[0] + 1; + for (int y = RLSW.scMin[1]; y <= RLSW.scMax[1]; y++) + { + uint8_t *curPtr = p + y*RLSW.framebuffer.width + RLSW.scMin[0]; + for (int xCount = 0; xCount < wScissor; xCount++) *curPtr++ = d8; + } + } + else + { + for (int i = 0; i < size; i++) *p++ = d8; + } +} + +#elif (SW_DEPTH_BUFFER_BITS == 16) + +static inline float sw_framebuffer_read_depth(const void *src) +{ + return (float)((uint16_t *)src)[0]*(1.0f/UINT16_MAX); +} + +static inline void sw_framebuffer_write_depth(void *dst, float depth) +{ + ((uint16_t *)dst)[0] = (uint16_t)(depth*UINT16_MAX); +} + +static inline void sw_framebuffer_fill_depth(void *ptr, int size, float value) +{ + uint16_t d16 = (uint16_t)(value*UINT16_MAX); + uint16_t *p = (uint16_t *)ptr; + + if (RLSW.stateFlags & SW_STATE_SCISSOR_TEST) + { + int wScissor = RLSW.scMax[0] - RLSW.scMin[0] + 1; + for (int y = RLSW.scMin[1]; y <= RLSW.scMax[1]; y++) + { + uint16_t *curPtr = p + y*RLSW.framebuffer.width + RLSW.scMin[0]; + for (int xCount = 0; xCount < wScissor; xCount++) *curPtr++ = d16; + } + } + else + { + for (int i = 0; i < size; i++) *p++ = d16; + } +} + +#elif (SW_DEPTH_BUFFER_BITS == 24) + +static inline float sw_framebuffer_read_depth(const void *src) +{ + uint32_t depth24 = (((uint8_t *)src)[0] << 16) | + (((uint8_t *)src)[1] << 8) | + ((uint8_t *)src)[2]; + + return depth24/(float)0xFFFFFF; +} + +static inline void sw_framebuffer_write_depth(void *dst, float depth) +{ + uint32_t depth24 = (uint32_t)(depth*0xFFFFFF); + + ((uint8_t *)dst)[0] = (depth24 >> 16) & 0xFF; + ((uint8_t *)dst)[1] = (depth24 >> 8) & 0xFF; + ((uint8_t *)dst)[2] = depth24 & 0xFF; +} + +static inline void sw_framebuffer_fill_depth(void *ptr, int size, float value) +{ + uint32_t d32 = (uint32_t)(value*UINT32_MAX); + uint8_t d_byte0 = (uint8_t)((d32 >> 16) & 0xFF); + uint8_t d_byte1 = (uint8_t)((d32 >> 8) & 0xFF); + uint8_t d_byte2 = (uint8_t)(d32 & 0xFF); + + uint8_t *p = (uint8_t *)ptr; + + if (RLSW.stateFlags & SW_STATE_SCISSOR_TEST) + { + int wScissor = RLSW.scMax[0] - RLSW.scMin[0] + 1; + for (int y = RLSW.scMin[1]; y <= RLSW.scMax[1]; y++) + { + uint8_t *curPtr = p + 3*(y*RLSW.framebuffer.width + RLSW.scMin[0]); + for (int xCount = 0; xCount < wScissor; xCount++) + { + *curPtr++ = d_byte0; + *curPtr++ = d_byte1; + *curPtr++ = d_byte2; + } + } + } + else + { + for (int i = 0; i < size; i++) + { + *p++ = d_byte0; + *p++ = d_byte1; + *p++ = d_byte2; + } + } +} + +#endif // SW_DEPTH_BUFFER_BITS + +static inline void sw_framebuffer_fill(void *colorPtr, void *depthPtr, int size, float color[4], float depth_value) +{ +#if (SW_COLOR_BUFFER_BITS == 8) + // Calculate and pack 3:3:2 color + // Scale color components to the max value for each bit depth and round + uint8_t r8 = (uint8_t)(color[0]*7.0f + 0.5f); + uint8_t g8 = (uint8_t)(color[1]*7.0f + 0.5f); + uint8_t b8 = (uint8_t)(color[2]*3.0f + 0.5f); + // Pack the components into a single byte + uint8_t packedColor = ((r8 & 0x07) << 5) | ((g8 & 0x07) << 2) | (b8 & 0x03); + uint8_t *cptr = (uint8_t *)colorPtr; +#elif (SW_COLOR_BUFFER_BITS == 16) + // Calculate and pack 5:6:5 color + // Scale color components to the max value for each bit depth and round + uint16_t r16 = (uint16_t)(color[0]*31.0f + 0.5f); + uint16_t g16 = (uint16_t)(color[1]*63.0f + 0.5f); + uint16_t b16 = (uint16_t)(color[2]*31.0f + 0.5f); + // Pack the components into a 16-bit value + uint16_t packedColor = ((r16 & 0x1F) << 11) | ((g16 & 0x3F) << 5) | (b16 & 0x1F); + uint16_t *cptr = (uint16_t *)colorPtr; +#elif (SW_COLOR_BUFFER_BITS == 24) + // Calculate 8:8:8 color components + uint8_t r24 = (uint8_t)(color[0]*255.0f); + uint8_t g24 = (uint8_t)(color[1]*255.0f); + uint8_t b24 = (uint8_t)(color[2]*255.0f); + uint8_t *cptr = (uint8_t *)colorPtr; +#endif + +#if (SW_DEPTH_BUFFER_BITS == 8) + // Calculate 8-bit depth + uint8_t d8 = (uint8_t)(depth_value*UINT8_MAX); + uint8_t *dptr = (uint8_t *)depthPtr; +#elif (SW_DEPTH_BUFFER_BITS == 16) + // Calculate 16-bit depth + uint16_t d16 = (uint16_t)(depth_value*UINT16_MAX); + uint16_t *dptr = (uint16_t *)depthPtr; +#elif (SW_DEPTH_BUFFER_BITS == 24) + // Calculate 24-bit depth and pre-calculate bytes + uint32_t d32 = (uint32_t)(depth_value*UINT32_MAX); + uint8_t dByte0 = (uint8_t)((d32 >> 16) & 0xFF); + uint8_t dByte1 = (uint8_t)((d32 >> 8) & 0xFF); + uint8_t dByte2 = (uint8_t)(d32 & 0xFF); + uint8_t *dptr = (uint8_t *)depthPtr; +#endif + + if (RLSW.stateFlags & SW_STATE_SCISSOR_TEST) + { + int wScissor = RLSW.scMax[0] - RLSW.scMin[0] + 1; + for (int y = RLSW.scMin[1]; y <= RLSW.scMax[1]; y++) + { + int rowStartIdx = y*RLSW.framebuffer.width + RLSW.scMin[0]; + + // Calculate starting pointers for the current row within the scissor rectangle + #if (SW_COLOR_BUFFER_BITS == 8) + uint8_t *curCPtr = cptr + rowStartIdx; + #elif (SW_COLOR_BUFFER_BITS == 16) + uint16_t *curCPtr = cptr + rowStartIdx; + #elif (SW_COLOR_BUFFER_BITS == 24) + uint8_t *curCPtr = cptr + 3*rowStartIdx; + #endif + + #if (SW_DEPTH_BUFFER_BITS == 8) + uint8_t *curDPtr = dptr + rowStartIdx; + #elif (SW_DEPTH_BUFFER_BITS == 16) + uint16_t *curDPtr = dptr + rowStartIdx; + #elif (SW_DEPTH_BUFFER_BITS == 24) + uint8_t *curDPtr = dptr + 3*rowStartIdx; + #endif + + // Fill the current row within the scissor rectangle + for (int xCount = 0; xCount < wScissor; xCount++) + { + // Write color + #if (SW_COLOR_BUFFER_BITS == 8) + *curCPtr++ = packedColor; + #elif (SW_COLOR_BUFFER_BITS == 16) + *curCPtr++ = packedColor; + #elif (SW_COLOR_BUFFER_BITS == 24) + *curCPtr++ = r24; + *curCPtr++ = g24; + *curCPtr++ = b24; + #endif + + // Write depth + #if (SW_DEPTH_BUFFER_BITS == 8) + *curDPtr++ = d8; + #elif (SW_DEPTH_BUFFER_BITS == 16) + *curDPtr++ = d16; + #elif (SW_DEPTH_BUFFER_BITS == 24) + *curDPtr++ = dByte0; + *curDPtr++ = dByte1; + *curDPtr++ = dByte2; + #endif + } + } + return; + } + + for (int i = 0; i < size; i++) + { + // Write color + #if (SW_COLOR_BUFFER_BITS == 8) + *cptr++ = packedColor; + #elif (SW_COLOR_BUFFER_BITS == 16) + *cptr++ = packedColor; + #elif (SW_COLOR_BUFFER_BITS == 24) + *cptr++ = r24; + *cptr++ = g24; + *cptr++ = b24; + #endif + + // Write depth + #if (SW_DEPTH_BUFFER_BITS == 8) + *dptr++ = d8; + #elif (SW_DEPTH_BUFFER_BITS == 16) + *dptr++ = d16; + #elif (SW_DEPTH_BUFFER_BITS == 24) + *dptr++ = dByte0; + *dptr++ = dByte1; + *dptr++ = dByte2; + #endif + } +} + +#define DEFINE_FRAMEBUFFER_COPY_U32_BEGIN(name, DST_PTR_T) \ +static inline void sw_framebuffer_copy_to_##name(int x, int y, int w, int h, DST_PTR_T *dst) \ +{ \ + const void *src = RLSW.framebuffer.color; \ + \ + for (int iy = y; iy < h; iy++) { \ + for (int ix = x; ix < w; ix++) { \ + uint8_t color[4]; \ + sw_framebuffer_read_color8(color, src); \ + +#define DEFINE_FRAMEBUFFER_COPY_F32_BEGIN(name, DST_PTR_T) \ +static inline void sw_framebuffer_copy_to_##name(int x, int y, int w, int h, DST_PTR_T *dst) \ +{ \ + const void *src = RLSW.framebuffer.color; \ + \ + for (int iy = y; iy < h; iy++) { \ + for (int ix = x; ix < w; ix++) { \ + float color[4]; \ + sw_framebuffer_read_color(color, src); \ + +#define DEFINE_FRAMEBUFFER_COPY_END() \ + sw_framebuffer_inc_const_color_addr(&src); \ + } \ + } \ +} + +DEFINE_FRAMEBUFFER_COPY_U32_BEGIN(GRAYSCALE, uint8_t) +{ + // NTSC grayscale conversion: Y = 0.299R + 0.587G + 0.114B + uint8_t gray = (uint8_t)((color[0]*299 + color[1]*587 + color[2]*114 + 500)/1000); + *dst++ = gray; +} +DEFINE_FRAMEBUFFER_COPY_END() + +DEFINE_FRAMEBUFFER_COPY_U32_BEGIN(GRAYALPHA, uint8_t) +{ + // Convert RGB to grayscale using NTSC formula + uint8_t gray = (uint8_t)((color[0]*299 + color[1]*587 + color[2]*114 + 500)/1000); + + dst[0] = gray; + dst[1] = color[3]; // alpha + + dst += 2; +} +DEFINE_FRAMEBUFFER_COPY_END() + +DEFINE_FRAMEBUFFER_COPY_U32_BEGIN(R5G6B5, uint16_t) +{ + // Convert 8-bit RGB to 5:6:5 format + uint8_t r5 = (color[0]*31 + 127)/255; + uint8_t g6 = (color[1]*63 + 127)/255; + uint8_t b5 = (color[2]*31 + 127)/255; + +#if SW_GL_FRAMEBUFFER_COPY_BGRA + uint16_t rgb565 = (b5 << 11) | (g6 << 5) | r5; +#else // RGBA + uint16_t rgb565 = (r5 << 11) | (g6 << 5) | b5; +#endif + + *dst++ = rgb565; +} +DEFINE_FRAMEBUFFER_COPY_END() + +DEFINE_FRAMEBUFFER_COPY_U32_BEGIN(R8G8B8, uint8_t) +{ +#if SW_GL_FRAMEBUFFER_COPY_BGRA + dst[0] = color[2]; + dst[1] = color[1]; + dst[2] = color[0]; +#else // RGBA + dst[0] = color[0]; + dst[1] = color[1]; + dst[2] = color[2]; +#endif + + dst += 3; +} +DEFINE_FRAMEBUFFER_COPY_END() + +DEFINE_FRAMEBUFFER_COPY_U32_BEGIN(R5G5B5A1, uint16_t) +{ + uint8_t r5 = (color[0]*31 + 127)/255; + uint8_t g5 = (color[1]*31 + 127)/255; + uint8_t b5 = (color[2]*31 + 127)/255; + uint8_t a1 = color[3] >= 128 ? 1 : 0; + +#if SW_GL_FRAMEBUFFER_COPY_BGRA + uint16_t pixel = (b5 << 11) | (g5 << 6) | (r5 << 1) | a1; +#else // RGBA + uint16_t pixel = (r5 << 11) | (g5 << 6) | (b5 << 1) | a1; +#endif + + *dst++ = pixel; +} +DEFINE_FRAMEBUFFER_COPY_END() + +DEFINE_FRAMEBUFFER_COPY_U32_BEGIN(R4G4B4A4, uint16_t) +{ + uint8_t r4 = (color[0]*15 + 127)/255; + uint8_t g4 = (color[1]*15 + 127)/255; + uint8_t b4 = (color[2]*15 + 127)/255; + uint8_t a4 = (color[3]*15 + 127)/255; + +#if SW_GL_FRAMEBUFFER_COPY_BGRA + uint16_t pixel = (b4 << 12) | (g4 << 8) | (r4 << 4) | a4; +#else // RGBA + uint16_t pixel = (r4 << 12) | (g4 << 8) | (b4 << 4) | a4; +#endif + + *dst++ = pixel; +} +DEFINE_FRAMEBUFFER_COPY_END() + +DEFINE_FRAMEBUFFER_COPY_U32_BEGIN(R8G8B8A8, uint8_t) +{ +#if SW_GL_FRAMEBUFFER_COPY_BGRA + dst[0] = color[2]; + dst[1] = color[1]; + dst[2] = color[0]; +#else // RGBA + dst[0] = color[0]; + dst[1] = color[1]; + dst[2] = color[2]; +#endif + dst[3] = color[3]; + + dst += 4; +} +DEFINE_FRAMEBUFFER_COPY_END() + +DEFINE_FRAMEBUFFER_COPY_F32_BEGIN(R32, float) +{ + dst[0] = color[0]; + dst++; +} +DEFINE_FRAMEBUFFER_COPY_END() + +DEFINE_FRAMEBUFFER_COPY_F32_BEGIN(R32G32B32, float) +{ +#if SW_GL_FRAMEBUFFER_COPY_BGRA + dst[0] = color[2]; + dst[1] = color[1]; + dst[2] = color[0]; +#else // RGBA + dst[0] = color[0]; + dst[1] = color[1]; + dst[2] = color[2]; +#endif + + dst += 3; +} +DEFINE_FRAMEBUFFER_COPY_END() + +DEFINE_FRAMEBUFFER_COPY_F32_BEGIN(R32G32B32A32, float) +{ +#if SW_GL_FRAMEBUFFER_COPY_BGRA + dst[0] = color[2]; + dst[1] = color[1]; + dst[2] = color[0]; +#else // RGBA + dst[0] = color[0]; + dst[1] = color[1]; + dst[2] = color[2]; +#endif + dst[3] = color[3]; + + dst += 4; +} +DEFINE_FRAMEBUFFER_COPY_END() + +DEFINE_FRAMEBUFFER_COPY_F32_BEGIN(R16, sw_half_t) +{ + dst[0] = sw_f16_from_f32(color[0]); + dst++; +} +DEFINE_FRAMEBUFFER_COPY_END() + +DEFINE_FRAMEBUFFER_COPY_F32_BEGIN(R16G16B16, sw_half_t) +{ +#if SW_GL_FRAMEBUFFER_COPY_BGRA + dst[0] = sw_f16_from_f32(color[2]); + dst[1] = sw_f16_from_f32(color[1]); + dst[2] = sw_f16_from_f32(color[0]); +#else // RGBA + dst[0] = sw_f16_from_f32(color[0]); + dst[1] = sw_f16_from_f32(color[1]); + dst[2] = sw_f16_from_f32(color[2]); +#endif + + dst += 3; +} +DEFINE_FRAMEBUFFER_COPY_END() + +DEFINE_FRAMEBUFFER_COPY_F32_BEGIN(R16G16B16A16, sw_half_t) +{ +#if SW_GL_FRAMEBUFFER_COPY_BGRA + dst[0] = sw_f16_from_f32(color[2]); + dst[1] = sw_f16_from_f32(color[1]); + dst[2] = sw_f16_from_f32(color[0]); +#else // RGBA + dst[0] = sw_f16_from_f32(color[0]); + dst[1] = sw_f16_from_f32(color[1]); + dst[2] = sw_f16_from_f32(color[2]); +#endif + dst[3] = sw_f16_from_f32(color[3]); + + dst += 4; +} +DEFINE_FRAMEBUFFER_COPY_END() + +#define DEFINE_FRAMEBUFFER_BLIT_U32_BEGIN(name, DST_PTR_T) \ +static inline void sw_framebuffer_blit_to_##name( \ + int xDst, int yDst, int wDst, int hDst, \ + int xSrc, int ySrc, int wSrc, int hSrc, \ + DST_PTR_T *dst) \ +{ \ + const uint8_t *srcBase = RLSW.framebuffer.color; \ + int fbWidth = RLSW.framebuffer.width; \ + \ + uint32_t xScale = ((uint32_t)wSrc << 16)/(uint32_t)wDst; \ + uint32_t yScale = ((uint32_t)hSrc << 16)/(uint32_t)hDst; \ + \ + for (int dy = 0; dy < hDst; dy++) { \ + uint32_t yFix = ((uint32_t)ySrc << 16) + dy*yScale; \ + int sy = yFix >> 16; \ + \ + for (int dx = 0; dx < wDst; dx++) { \ + uint32_t xFix = dx*xScale; \ + int sx = xFix >> 16; \ + const void *srcPtr = sw_framebuffer_get_color_addr(srcBase, sy*fbWidth + sx); \ + uint8_t color[4]; \ + sw_framebuffer_read_color8(color, srcPtr); \ + +#define DEFINE_FRAMEBUFFER_BLIT_F32_BEGIN(name, DST_PTR_T) \ +static inline void sw_framebuffer_blit_to_##name( \ + int xDst, int yDst, int wDst, int hDst, \ + int xSrc, int ySrc, int wSrc, int hSrc, \ + DST_PTR_T *dst) \ +{ \ + const uint8_t *srcBase = RLSW.framebuffer.color; \ + int fbWidth = RLSW.framebuffer.width; \ + \ + uint32_t xScale = ((uint32_t)wSrc << 16)/(uint32_t)wDst; \ + uint32_t yScale = ((uint32_t)hSrc << 16)/(uint32_t)hDst; \ + \ + for (int dy = 0; dy < hDst; dy++) { \ + uint32_t yFix = ((uint32_t)ySrc << 16) + dy*yScale; \ + int sy = yFix >> 16; \ + \ + for (int dx = 0; dx < wDst; dx++) { \ + uint32_t xFix = dx*xScale; \ + int sx = xFix >> 16; \ + const void *srcPtr = sw_framebuffer_get_color_addr(srcBase, sy*fbWidth + sx); \ + float color[4]; \ + sw_framebuffer_read_color(color, srcPtr); \ + +#define DEFINE_FRAMEBUFFER_BLIT_END() \ + } \ + } \ +} + +DEFINE_FRAMEBUFFER_BLIT_U32_BEGIN(GRAYSCALE, uint8_t) +{ + uint8_t gray = (uint8_t)((color[0]*299 + color[1]*587 + color[2]*114 + 500)/1000); + *dst++ = gray; +} +DEFINE_FRAMEBUFFER_BLIT_END() + +DEFINE_FRAMEBUFFER_BLIT_U32_BEGIN(GRAYALPHA, uint8_t) +{ + uint8_t gray = (uint8_t)((color[0]*299 + color[1]*587 + color[2]*114 + 500)/1000); + + dst[0] = gray; + dst[1] = color[3]; // alpha + + dst += 2; +} +DEFINE_FRAMEBUFFER_BLIT_END() + +DEFINE_FRAMEBUFFER_BLIT_U32_BEGIN(R5G6B5, uint16_t) +{ + uint8_t r5 = (color[0]*31 + 127)/255; + uint8_t g6 = (color[1]*63 + 127)/255; + uint8_t b5 = (color[2]*31 + 127)/255; + +#if SW_GL_FRAMEBUFFER_COPY_BGRA + uint16_t rgb565 = (b5 << 11) | (g6 << 5) | r5; +#else // RGBA + uint16_t rgb565 = (r5 << 11) | (g6 << 5) | b5; +#endif + + *dst++ = rgb565; +} +DEFINE_FRAMEBUFFER_BLIT_END() + +DEFINE_FRAMEBUFFER_BLIT_U32_BEGIN(R8G8B8, uint8_t) +{ +#if SW_GL_FRAMEBUFFER_COPY_BGRA + dst[0] = color[2]; + dst[1] = color[1]; + dst[2] = color[0]; +#else // RGBA + dst[0] = color[0]; + dst[1] = color[1]; + dst[2] = color[2]; +#endif + + dst += 3; +} +DEFINE_FRAMEBUFFER_BLIT_END() + +DEFINE_FRAMEBUFFER_BLIT_U32_BEGIN(R5G5B5A1, uint16_t) +{ + uint8_t r5 = (color[0]*31 + 127)/255; + uint8_t g5 = (color[1]*31 + 127)/255; + uint8_t b5 = (color[2]*31 + 127)/255; + uint8_t a1 = color[3] >= 128 ? 1 : 0; + +#if SW_GL_FRAMEBUFFER_COPY_BGRA + uint16_t pixel = (b5 << 11) | (g5 << 6) | (r5 << 1) | a1; +#else // RGBA + uint16_t pixel = (r5 << 11) | (g5 << 6) | (b5 << 1) | a1; +#endif + + *dst++ = pixel; +} +DEFINE_FRAMEBUFFER_BLIT_END() + +DEFINE_FRAMEBUFFER_BLIT_U32_BEGIN(R4G4B4A4, uint16_t) +{ + uint8_t r4 = (color[0]*15 + 127)/255; + uint8_t g4 = (color[1]*15 + 127)/255; + uint8_t b4 = (color[2]*15 + 127)/255; + uint8_t a4 = (color[3]*15 + 127)/255; + +#if SW_GL_FRAMEBUFFER_COPY_BGRA + uint16_t pixel = (b4 << 12) | (g4 << 8) | (r4 << 4) | a4; +#else // RGBA + uint16_t pixel = (r4 << 12) | (g4 << 8) | (b4 << 4) | a4; +#endif + + *dst++ = pixel; +} +DEFINE_FRAMEBUFFER_BLIT_END() + +DEFINE_FRAMEBUFFER_BLIT_U32_BEGIN(R8G8B8A8, uint8_t) +{ +#if SW_GL_FRAMEBUFFER_COPY_BGRA + dst[0] = color[2]; + dst[1] = color[1]; + dst[2] = color[0]; +#else // RGBA + dst[0] = color[0]; + dst[1] = color[1]; + dst[2] = color[2]; +#endif + dst[3] = color[3]; + + dst += 4; +} +DEFINE_FRAMEBUFFER_BLIT_END() + +DEFINE_FRAMEBUFFER_BLIT_F32_BEGIN(R32, uint8_t) +{ + dst[0] = color[0]; + dst++; +} +DEFINE_FRAMEBUFFER_BLIT_END() + +DEFINE_FRAMEBUFFER_BLIT_F32_BEGIN(R32G32B32, float) +{ +#if SW_GL_FRAMEBUFFER_COPY_BGRA + dst[0] = color[2]; + dst[1] = color[1]; + dst[2] = color[0]; +#else // RGBA + dst[0] = color[0]; + dst[1] = color[1]; + dst[2] = color[2]; +#endif + + dst += 3; +} +DEFINE_FRAMEBUFFER_BLIT_END() + +DEFINE_FRAMEBUFFER_BLIT_F32_BEGIN(R32G32B32A32, float) +{ +#if SW_GL_FRAMEBUFFER_COPY_BGRA + dst[0] = color[2]; + dst[1] = color[1]; + dst[2] = color[0]; +#else // RGBA + dst[0] = color[0]; + dst[1] = color[1]; + dst[2] = color[2]; +#endif + dst[3] = color[3]; + + dst += 4; +} +DEFINE_FRAMEBUFFER_BLIT_END() + +DEFINE_FRAMEBUFFER_BLIT_F32_BEGIN(R16, sw_half_t) +{ + dst[0] = sw_f16_from_f32(color[0]); + dst++; +} +DEFINE_FRAMEBUFFER_BLIT_END() + +DEFINE_FRAMEBUFFER_BLIT_F32_BEGIN(R16G16B16, sw_half_t) +{ +#if SW_GL_FRAMEBUFFER_COPY_BGRA + dst[0] = sw_f16_from_f32(color[2]); + dst[1] = sw_f16_from_f32(color[1]); + dst[2] = sw_f16_from_f32(color[0]); +#else // RGBA + dst[0] = sw_f16_from_f32(color[0]); + dst[1] = sw_f16_from_f32(color[1]); + dst[2] = sw_f16_from_f32(color[2]); +#endif + + dst += 3; +} +DEFINE_FRAMEBUFFER_BLIT_END() + +DEFINE_FRAMEBUFFER_BLIT_F32_BEGIN(R16G16B16A16, sw_half_t) +{ +#if SW_GL_FRAMEBUFFER_COPY_BGRA + dst[0] = sw_f16_from_f32(color[2]); + dst[1] = sw_f16_from_f32(color[1]); + dst[2] = sw_f16_from_f32(color[0]); +#else // RGBA + dst[0] = sw_f16_from_f32(color[0]); + dst[1] = sw_f16_from_f32(color[1]); + dst[2] = sw_f16_from_f32(color[2]); +#endif + dst[3] = sw_f16_from_f32(color[3]); + + dst += 4; +} +DEFINE_FRAMEBUFFER_BLIT_END() + +// Pixel format management functions + +static inline int sw_get_pixel_format(SWformat format, SWtype type) +{ + int channels = 0; + int bitsPerChannel = 8; // Default: 8 bits per channel + + // Determine the number of channels (format) + switch (format) + { + case SW_LUMINANCE: channels = 1; break; + case SW_LUMINANCE_ALPHA: channels = 2; break; + case SW_RGB: channels = 3; break; + case SW_RGBA: channels = 4; break; + default: return SW_PIXELFORMAT_UNKNOWN; + } + + // Determine the depth of each channel (type) + switch (type) + { + case SW_UNSIGNED_BYTE: bitsPerChannel = 8; break; + case SW_BYTE: bitsPerChannel = 8; break; + case SW_UNSIGNED_SHORT: bitsPerChannel = 16; break; + case SW_SHORT: bitsPerChannel = 16; break; + case SW_UNSIGNED_INT: bitsPerChannel = 32; break; + case SW_INT: bitsPerChannel = 32; break; + case SW_FLOAT: bitsPerChannel = 32; break; + default: return SW_PIXELFORMAT_UNKNOWN; + } + + // Map the format and type to the correct internal format + if (bitsPerChannel == 8) + { + if (channels == 1) return SW_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE; + if (channels == 2) return SW_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA; + if (channels == 3) return SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8; + if (channels == 4) return SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8; + } + else if (bitsPerChannel == 16) + { + if (channels == 1) return SW_PIXELFORMAT_UNCOMPRESSED_R16; + if (channels == 3) return SW_PIXELFORMAT_UNCOMPRESSED_R16G16B16; + if (channels == 4) return SW_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16; + } + else if (bitsPerChannel == 32) + { + if (channels == 1) return SW_PIXELFORMAT_UNCOMPRESSED_R32; + if (channels == 3) return SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32; + if (channels == 4) return SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32; + } + + return SW_PIXELFORMAT_UNKNOWN; +} + +int sw_get_pixel_bytes(sw_pixelformat_t format) +{ + int bpp = 0; + + switch (format) + { + case SW_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: bpp = 1; break; + case SW_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: + case SW_PIXELFORMAT_UNCOMPRESSED_R5G6B5: + case SW_PIXELFORMAT_UNCOMPRESSED_R5G5B5A1: + case SW_PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: bpp = 2; break; + case SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: bpp = 4; break; + case SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8: bpp = 3; break; + case SW_PIXELFORMAT_UNCOMPRESSED_R32: bpp = 4; break; + case SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32: bpp = 4*3; break; + case SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: bpp = 4*4; break; + case SW_PIXELFORMAT_UNCOMPRESSED_R16: bpp = 2; break; + case SW_PIXELFORMAT_UNCOMPRESSED_R16G16B16: bpp = 2*3; break; + case SW_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: bpp = 2*4; break; + default: break; + } + + return bpp; +} + +static inline void sw_get_pixel_grayscale(float *color, const void *pixels, uint32_t offset) +{ + float gray = (float)((uint8_t *)pixels)[offset]*(1.0f/255); + + color[0] = gray; + color[1] = gray; + color[2] = gray; + color[3] = 1.0f; +} + +static inline void sw_get_pixel_red_16(float *color, const void *pixels, uint32_t offset) +{ + float value = sw_f16_to_f32(((sw_half_t *)pixels)[offset]); + + color[0] = value; + color[1] = value; + color[2] = value; + color[3] = 1.0f; +} + +static inline void sw_get_pixel_red_32(float *color, const void *pixels, uint32_t offset) +{ + float value = ((float *)pixels)[offset]; + + color[0] = value; + color[1] = value; + color[2] = value; + color[3] = 1.0f; +} + +static inline void sw_get_pixel_grayscale_alpha(float *color, const void *pixels, uint32_t offset) +{ + const uint8_t *pixelData = (const uint8_t *)pixels + 2*offset; + + color[0] = color[1] = color[2] = (float)pixelData[0]*(1.0f/255); + color[3] = (float)pixelData[1]*(1.0f/255); +} + +static inline void sw_get_pixel_rgb_565(float *color, const void *pixels, uint32_t offset) +{ + uint16_t pixel = ((uint16_t *)pixels)[offset]; + + color[0] = (float)((pixel & 0xF800) >> 11)/31; + color[1] = (float)((pixel & 0x7E0) >> 5)/63; + color[2] = (float)(pixel & 0x1F)/31; + color[3] = 1.0f; +} + +static inline void sw_get_pixel_rgb_888(float *color, const void *pixels, uint32_t offset) +{ + const uint8_t *pixel = (const uint8_t *)pixels + 3*offset; + + color[0] = (float)pixel[0]*(1.0f/255); + color[1] = (float)pixel[1]*(1.0f/255); + color[2] = (float)pixel[2]*(1.0f/255); + color[3] = 1.0f; +} + +static inline void sw_get_pixel_rgb_161616(float *color, const void *pixels, uint32_t offset) +{ + const sw_half_t *pixel = (sw_half_t *)pixels + 3*offset; + + color[0] = sw_f16_to_f32(pixel[0]); + color[1] = sw_f16_to_f32(pixel[1]); + color[2] = sw_f16_to_f32(pixel[2]); + color[3] = 1.0f; +} + +static inline void sw_get_pixel_rgb_323232(float *color, const void *pixels, uint32_t offset) +{ + const float *pixel = (float *)pixels + 3*offset; + + color[0] = pixel[0]; + color[1] = pixel[1]; + color[2] = pixel[2]; + color[3] = 1.0f; +} + +static inline void sw_get_pixel_rgba_5551(float *color, const void *pixels, uint32_t offset) +{ + uint16_t pixel = ((uint16_t *)pixels)[offset]; + + color[0] = (float)((pixel & 0xF800) >> 11)/31; + color[1] = (float)((pixel & 0x7C0) >> 6)/31; + color[2] = (float)((pixel & 0x3E) >> 1)/31; + color[3] = (float)(pixel & 0x1); +} + +static inline void sw_get_pixel_rgba_4444(float *color, const void *pixels, uint32_t offset) +{ + uint16_t pixel = ((uint16_t *)pixels)[offset]; + + color[0] = (float)((pixel & 0xF000) >> 12)/15; + color[1] = (float)((pixel & 0xF00) >> 8)/15; + color[2] = (float)((pixel & 0xF0) >> 4)/15; + color[3] = (float)(pixel & 0xF)/15; +} + +static inline void sw_get_pixel_rgba_8888(float *color, const void *pixels, uint32_t offset) +{ + const uint8_t *pixel = (uint8_t *)pixels + 4*offset; + + color[0] = (float)pixel[0]*(1.0f/255); + color[1] = (float)pixel[1]*(1.0f/255); + color[2] = (float)pixel[2]*(1.0f/255); + color[3] = (float)pixel[3]*(1.0f/255); +} + +static inline void sw_get_pixel_rgba_16161616(float *color, const void *pixels, uint32_t offset) +{ + const sw_half_t *pixel = (sw_half_t *)pixels + 4*offset; + + color[0] = sw_f16_to_f32(pixel[0]); + color[1] = sw_f16_to_f32(pixel[1]); + color[2] = sw_f16_to_f32(pixel[2]); + color[3] = sw_f16_to_f32(pixel[3]); +} + +static inline void sw_get_pixel_rgba_32323232(float *color, const void *pixels, uint32_t offset) +{ + const float *pixel = (float *)pixels + 4*offset; + + color[0] = pixel[0]; + color[1] = pixel[1]; + color[2] = pixel[2]; + color[3] = pixel[3]; +} + +static inline void sw_get_pixel(float *color, const void *pixels, uint32_t offset, sw_pixelformat_t format) +{ + switch (format) + { + case SW_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: sw_get_pixel_grayscale(color, pixels, offset); break; + case SW_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: sw_get_pixel_grayscale_alpha(color, pixels, offset); break; + case SW_PIXELFORMAT_UNCOMPRESSED_R5G6B5: sw_get_pixel_rgb_565(color, pixels, offset); break; + case SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8: sw_get_pixel_rgb_888(color, pixels, offset); break; + case SW_PIXELFORMAT_UNCOMPRESSED_R5G5B5A1: sw_get_pixel_rgba_5551(color, pixels, offset); break; + case SW_PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: sw_get_pixel_rgba_4444(color, pixels, offset); break; + case SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: sw_get_pixel_rgba_8888(color, pixels, offset); break; + case SW_PIXELFORMAT_UNCOMPRESSED_R32: sw_get_pixel_red_32(color, pixels, offset); break; + case SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32: sw_get_pixel_rgb_323232(color, pixels, offset); break; + case SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: sw_get_pixel_rgba_32323232(color, pixels, offset); break; + case SW_PIXELFORMAT_UNCOMPRESSED_R16: sw_get_pixel_red_16(color, pixels, offset); break; + case SW_PIXELFORMAT_UNCOMPRESSED_R16G16B16: sw_get_pixel_rgb_161616(color, pixels, offset); break; + case SW_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: sw_get_pixel_rgba_16161616(color, pixels, offset); break; + case SW_PIXELFORMAT_UNKNOWN: break; + default: break; + } +} + +// Texture sampling functionality + +static inline void sw_texture_sample_nearest(float *color, const sw_texture_t *tex, float u, float v) +{ + u = (tex->sWrap == SW_REPEAT)? sw_fract(u) : sw_saturate(u); + v = (tex->tWrap == SW_REPEAT)? sw_fract(v) : sw_saturate(v); + + int x = u*tex->width, y = v*tex->height; + + sw_get_pixel(color, tex->pixels.cptr, y*tex->width + x, tex->format); +} + +static inline void sw_texture_sample_linear(float *color, const sw_texture_t *tex, float u, float v) +{ + // TODO: REVIEW: With a bit more cleverness we could clearly reduce the + // number of operations here, but for now it works fine. + + float xf = u*tex->width - 0.5f; + float yf = v*tex->height - 0.5f; + + int x0 = (int)floorf(xf); + int y0 = (int)floorf(yf); + + float fx = xf - x0; + float fy = yf - y0; + + int x1 = x0 + 1; + int y1 = y0 + 1; + + if (tex->sWrap == SW_CLAMP) + { + x0 = (x0 > tex->wMinus1)? tex->wMinus1 : x0; + x1 = (x1 > tex->wMinus1)? tex->wMinus1 : x1; + } + else + { + x0 = (x0%tex->width + tex->width)%tex->width; + x1 = (x1%tex->width + tex->width)%tex->width; + } + + if (tex->tWrap == SW_CLAMP) + { + y0 = (y0 > tex->hMinus1)? tex->hMinus1 : y0; + y1 = (y1 > tex->hMinus1)? tex->hMinus1 : y1; + } + else + { + y0 = (y0%tex->height + tex->height)%tex->height; + y1 = (y1%tex->height + tex->height)%tex->height; + } + + float c00[4], c10[4], c01[4], c11[4]; + sw_get_pixel(c00, tex->pixels.cptr, y0*tex->width + x0, tex->format); + sw_get_pixel(c10, tex->pixels.cptr, y0*tex->width + x1, tex->format); + sw_get_pixel(c01, tex->pixels.cptr, y1*tex->width + x0, tex->format); + sw_get_pixel(c11, tex->pixels.cptr, y1*tex->width + x1, tex->format); + + for (int i = 0; i < 4; i++) + { + float t = c00[i] + fx*(c10[i] - c00[i]); + float b = c01[i] + fx*(c11[i] - c01[i]); + color[i] = t + fy*(b - t); + } +} + +static inline void sw_texture_sample(float *color, const sw_texture_t *tex, float u, float v, float duDx, float duDy, float dvDx, float dvDy) +{ + // Previous method: There is no need to compute the square root + // because using the squared value, the comparison remains `L2 > 1.0f*1.0f` + //float du = sqrtf(duDx*duDx + duDy*duDy); + //float dv = sqrtf(dvDx*dvDx + dvDy*dvDy); + //float L = (du > dv)? du : dv; + + // Calculate the derivatives for each axis + float du2 = duDx*duDx + duDy*duDy; + float dv2 = dvDx*dvDx + dvDy*dvDy; + float L2 = (du2 > dv2)? du2 : dv2; + + SWfilter filter = (L2 > 1.0f)? tex->minFilter : tex->magFilter; + + switch (filter) + { + case SW_NEAREST: sw_texture_sample_nearest(color, tex, u, v); break; + case SW_LINEAR: sw_texture_sample_linear(color, tex, u, v); break; + default: break; + } +} + +// Color Blending functionality + +static inline void sw_factor_zero(float *SW_RESTRICT factor, const float *SW_RESTRICT src, const float *SW_RESTRICT dst) +{ + factor[0] = factor[1] = factor[2] = factor[3] = 0.0f; +} + +static inline void sw_factor_one(float *SW_RESTRICT factor, const float *SW_RESTRICT src, const float *SW_RESTRICT dst) +{ + factor[0] = factor[1] = factor[2] = factor[3] = 1.0f; +} + +static inline void sw_factor_src_color(float *SW_RESTRICT factor, const float *SW_RESTRICT src, const float *SW_RESTRICT dst) +{ + factor[0] = src[0]; factor[1] = src[1]; factor[2] = src[2]; factor[3] = src[3]; +} + +static inline void sw_factor_one_minus_src_color(float *SW_RESTRICT factor, const float *SW_RESTRICT src, const float *SW_RESTRICT dst) +{ + factor[0] = 1.0f - src[0]; factor[1] = 1.0f - src[1]; + factor[2] = 1.0f - src[2]; factor[3] = 1.0f - src[3]; +} + +static inline void sw_factor_src_alpha(float *SW_RESTRICT factor, const float *SW_RESTRICT src, const float *SW_RESTRICT dst) +{ + factor[0] = factor[1] = factor[2] = factor[3] = src[3]; +} + +static inline void sw_factor_one_minus_src_alpha(float *SW_RESTRICT factor, const float *SW_RESTRICT src, const float *SW_RESTRICT dst) +{ + float invAlpha = 1.0f - src[3]; + factor[0] = factor[1] = factor[2] = factor[3] = invAlpha; +} + +static inline void sw_factor_dst_alpha(float *SW_RESTRICT factor, const float *SW_RESTRICT src, const float *SW_RESTRICT dst) +{ + factor[0] = factor[1] = factor[2] = factor[3] = dst[3]; +} + +static inline void sw_factor_one_minus_dst_alpha(float *SW_RESTRICT factor, const float *SW_RESTRICT src, const float *SW_RESTRICT dst) +{ + float invAlpha = 1.0f - dst[3]; + factor[0] = factor[1] = factor[2] = factor[3] = invAlpha; +} + +static inline void sw_factor_dst_color(float *SW_RESTRICT factor, const float *SW_RESTRICT src, const float *SW_RESTRICT dst) +{ + factor[0] = dst[0]; factor[1] = dst[1]; factor[2] = dst[2]; factor[3] = dst[3]; +} + +static inline void sw_factor_one_minus_dst_color(float *SW_RESTRICT factor, const float *SW_RESTRICT src, const float *SW_RESTRICT dst) +{ + factor[0] = 1.0f - dst[0]; factor[1] = 1.0f - dst[1]; + factor[2] = 1.0f - dst[2]; factor[3] = 1.0f - dst[3]; +} + +static inline void sw_factor_src_alpha_saturate(float *SW_RESTRICT factor, const float *SW_RESTRICT src, const float *SW_RESTRICT dst) +{ + factor[0] = factor[1] = factor[2] = 1.0f; + factor[3] = (src[3] < 1.0f)? src[3] : 1.0f; +} + +static inline void sw_blend_colors(float *SW_RESTRICT dst/*[4]*/, const float *SW_RESTRICT src/*[4]*/) +{ + float srcFactor[4], dstFactor[4]; + + RLSW.srcFactorFunc(srcFactor, src, dst); + RLSW.dstFactorFunc(dstFactor, src, dst); + + dst[0] = srcFactor[0]*src[0] + dstFactor[0]*dst[0]; + dst[1] = srcFactor[1]*src[1] + dstFactor[1]*dst[1]; + dst[2] = srcFactor[2]*src[2] + dstFactor[2]*dst[2]; + dst[3] = srcFactor[3]*src[3] + dstFactor[3]*dst[3]; +} + +// Projection helper functions + +static inline void sw_project_ndc_to_screen(float screen[2], const float ndc[4]) +{ + screen[0] = RLSW.vpCenter[0] + ndc[0]*RLSW.vpHalfSize[0]; + screen[1] = RLSW.vpCenter[1] - ndc[1]*RLSW.vpHalfSize[1]; +} + +// Polygon Clipping management + +#define DEFINE_CLIP_FUNC(name, FUNC_IS_INSIDE, FUNC_COMPUTE_T) \ +static inline int sw_clip_##name( \ + sw_vertex_t output[SW_MAX_CLIPPED_POLYGON_VERTICES], \ + const sw_vertex_t input[SW_MAX_CLIPPED_POLYGON_VERTICES], \ + int n) \ +{ \ + const sw_vertex_t *prev = &input[n - 1]; \ + int prevInside = FUNC_IS_INSIDE(prev->homogeneous); \ + int outputCount = 0; \ + \ + for (int i = 0; i < n; i++) { \ + const sw_vertex_t *curr = &input[i]; \ + int currInside = FUNC_IS_INSIDE(curr->homogeneous); \ + \ + /* If transition between interior/exterior, calculate intersection point */ \ + if (prevInside != currInside) { \ + float t = FUNC_COMPUTE_T(prev->homogeneous, curr->homogeneous); \ + sw_lerp_vertex_PTCH(&output[outputCount++], prev, curr, t); \ + } \ + \ + /* If current vertex inside, add it */ \ + if (currInside) { \ + output[outputCount++] = *curr; \ + } \ + \ + prev = curr; \ + prevInside = currInside; \ + } \ + \ + return outputCount; \ +} + +// Frustum cliping functions + +#define IS_INSIDE_PLANE_W(h) ((h)[3] >= SW_CLIP_EPSILON) +#define IS_INSIDE_PLANE_X_POS(h) ((h)[0] <= (h)[3]) +#define IS_INSIDE_PLANE_X_NEG(h) (-(h)[0] <= (h)[3]) +#define IS_INSIDE_PLANE_Y_POS(h) ((h)[1] <= (h)[3]) +#define IS_INSIDE_PLANE_Y_NEG(h) (-(h)[1] <= (h)[3]) +#define IS_INSIDE_PLANE_Z_POS(h) ((h)[2] <= (h)[3]) +#define IS_INSIDE_PLANE_Z_NEG(h) (-(h)[2] <= (h)[3]) + +#define COMPUTE_T_PLANE_W(hPrev, hCurr) ((SW_CLIP_EPSILON - (hPrev)[3])/((hCurr)[3] - (hPrev)[3])) +#define COMPUTE_T_PLANE_X_POS(hPrev, hCurr) (((hPrev)[3] - (hPrev)[0])/(((hPrev)[3] - (hPrev)[0]) - ((hCurr)[3] - (hCurr)[0]))) +#define COMPUTE_T_PLANE_X_NEG(hPrev, hCurr) (((hPrev)[3] + (hPrev)[0])/(((hPrev)[3] + (hPrev)[0]) - ((hCurr)[3] + (hCurr)[0]))) +#define COMPUTE_T_PLANE_Y_POS(hPrev, hCurr) (((hPrev)[3] - (hPrev)[1])/(((hPrev)[3] - (hPrev)[1]) - ((hCurr)[3] - (hCurr)[1]))) +#define COMPUTE_T_PLANE_Y_NEG(hPrev, hCurr) (((hPrev)[3] + (hPrev)[1])/(((hPrev)[3] + (hPrev)[1]) - ((hCurr)[3] + (hCurr)[1]))) +#define COMPUTE_T_PLANE_Z_POS(hPrev, hCurr) (((hPrev)[3] - (hPrev)[2])/(((hPrev)[3] - (hPrev)[2]) - ((hCurr)[3] - (hCurr)[2]))) +#define COMPUTE_T_PLANE_Z_NEG(hPrev, hCurr) (((hPrev)[3] + (hPrev)[2])/(((hPrev)[3] + (hPrev)[2]) - ((hCurr)[3] + (hCurr)[2]))) + +DEFINE_CLIP_FUNC(w, IS_INSIDE_PLANE_W, COMPUTE_T_PLANE_W) +DEFINE_CLIP_FUNC(x_pos, IS_INSIDE_PLANE_X_POS, COMPUTE_T_PLANE_X_POS) +DEFINE_CLIP_FUNC(x_neg, IS_INSIDE_PLANE_X_NEG, COMPUTE_T_PLANE_X_NEG) +DEFINE_CLIP_FUNC(y_pos, IS_INSIDE_PLANE_Y_POS, COMPUTE_T_PLANE_Y_POS) +DEFINE_CLIP_FUNC(y_neg, IS_INSIDE_PLANE_Y_NEG, COMPUTE_T_PLANE_Y_NEG) +DEFINE_CLIP_FUNC(z_pos, IS_INSIDE_PLANE_Z_POS, COMPUTE_T_PLANE_Z_POS) +DEFINE_CLIP_FUNC(z_neg, IS_INSIDE_PLANE_Z_NEG, COMPUTE_T_PLANE_Z_NEG) + +// Scissor clip functions + +#define COMPUTE_T_SCISSOR_X_MIN(hPrev, hCurr) (((RLSW.scClipMin[0])*(hPrev)[3] - (hPrev)[0])/(((hCurr)[0] - (RLSW.scClipMin[0])*(hCurr)[3]) - ((hPrev)[0] - (RLSW.scClipMin[0])*(hPrev)[3]))) +#define COMPUTE_T_SCISSOR_X_MAX(hPrev, hCurr) (((RLSW.scClipMax[0])*(hPrev)[3] - (hPrev)[0])/(((hCurr)[0] - (RLSW.scClipMax[0])*(hCurr)[3]) - ((hPrev)[0] - (RLSW.scClipMax[0])*(hPrev)[3]))) +#define COMPUTE_T_SCISSOR_Y_MIN(hPrev, hCurr) (((RLSW.scClipMin[1])*(hPrev)[3] - (hPrev)[1])/(((hCurr)[1] - (RLSW.scClipMin[1])*(hCurr)[3]) - ((hPrev)[1] - (RLSW.scClipMin[1])*(hPrev)[3]))) +#define COMPUTE_T_SCISSOR_Y_MAX(hPrev, hCurr) (((RLSW.scClipMax[1])*(hPrev)[3] - (hPrev)[1])/(((hCurr)[1] - (RLSW.scClipMax[1])*(hCurr)[3]) - ((hPrev)[1] - (RLSW.scClipMax[1])*(hPrev)[3]))) + +#define IS_INSIDE_SCISSOR_X_MIN(h) ((h)[0] >= (RLSW.scClipMin[0])*(h)[3]) +#define IS_INSIDE_SCISSOR_X_MAX(h) ((h)[0] <= (RLSW.scClipMax[0])*(h)[3]) +#define IS_INSIDE_SCISSOR_Y_MIN(h) ((h)[1] >= (RLSW.scClipMin[1])*(h)[3]) +#define IS_INSIDE_SCISSOR_Y_MAX(h) ((h)[1] <= (RLSW.scClipMax[1])*(h)[3]) + +DEFINE_CLIP_FUNC(scissor_x_min, IS_INSIDE_SCISSOR_X_MIN, COMPUTE_T_SCISSOR_X_MIN) +DEFINE_CLIP_FUNC(scissor_x_max, IS_INSIDE_SCISSOR_X_MAX, COMPUTE_T_SCISSOR_X_MAX) +DEFINE_CLIP_FUNC(scissor_y_min, IS_INSIDE_SCISSOR_Y_MIN, COMPUTE_T_SCISSOR_Y_MIN) +DEFINE_CLIP_FUNC(scissor_y_max, IS_INSIDE_SCISSOR_Y_MAX, COMPUTE_T_SCISSOR_Y_MAX) + +// Main clip function + +static inline bool sw_polygon_clip(sw_vertex_t polygon[SW_MAX_CLIPPED_POLYGON_VERTICES], int *vertexCounter) +{ + static sw_vertex_t tmp[SW_MAX_CLIPPED_POLYGON_VERTICES]; + + int n = *vertexCounter; + + #define CLIP_AGAINST_PLANE(FUNC_CLIP) \ + { \ + n = FUNC_CLIP(tmp, polygon, n); \ + if (n < 3) \ + { \ + *vertexCounter = 0; \ + return false; \ + } \ + for (int i = 0; i < n; i++) polygon[i] = tmp[i]; \ + } + + CLIP_AGAINST_PLANE(sw_clip_w); + CLIP_AGAINST_PLANE(sw_clip_x_pos); + CLIP_AGAINST_PLANE(sw_clip_x_neg); + CLIP_AGAINST_PLANE(sw_clip_y_pos); + CLIP_AGAINST_PLANE(sw_clip_y_neg); + CLIP_AGAINST_PLANE(sw_clip_z_pos); + CLIP_AGAINST_PLANE(sw_clip_z_neg); + + if (RLSW.stateFlags & SW_STATE_SCISSOR_TEST) + { + CLIP_AGAINST_PLANE(sw_clip_scissor_x_min); + CLIP_AGAINST_PLANE(sw_clip_scissor_x_max); + CLIP_AGAINST_PLANE(sw_clip_scissor_y_min); + CLIP_AGAINST_PLANE(sw_clip_scissor_y_max); + } + + *vertexCounter = n; + + return (n >= 3); +} + +// Triangle rendering logic + +static inline bool sw_triangle_face_culling(void) +{ + // NOTE: Face culling is done before clipping to avoid unnecessary computations. + // To handle triangles crossing the w=0 plane correctly, + // we perform the winding order test in homogeneous coordinates directly, + // before the perspective division (division by w). + // This test determines the orientation of the triangle in the (x,y,w) plane, + // which corresponds to the projected 2D winding order sign, + // even with negative w values. + + // Preload homogeneous coordinates into local variables + const float *h0 = RLSW.vertexBuffer[0].homogeneous; + const float *h1 = RLSW.vertexBuffer[1].homogeneous; + const float *h2 = RLSW.vertexBuffer[2].homogeneous; + + // Compute a value proportional to the signed area in the projected 2D plane, + // calculated directly using homogeneous coordinates BEFORE division by w. + // This is the determinant of the matrix formed by the (x, y, w) components + // of the vertices, which correctly captures the winding order in homogeneous + // space and its relationship to the projected 2D winding order, even with + // negative w values. + // The determinant formula used here is: + // h0.x*(h1.y*h2.w - h2.y*h1.w) + + // h1.x*(h2.y*h0.w - h0.y*h2.w) + + // h2.x*(h0.y*h1.w - h1.y*h0.w) + + const float hSgnArea = + h0[0]*(h1[1]*h2[3] - h2[1]*h1[3]) + + h1[0]*(h2[1]*h0[3] - h0[1]*h2[3]) + + h2[0]*(h0[1]*h1[3] - h1[1]*h0[3]); + + // Discard the triangle if its winding order (determined by the sign + // of the homogeneous area/determinant) matches the culled direction. + // A positive hSgnArea typically corresponds to a counter-clockwise + // winding in the projected space when all w > 0. + // This test is robust for points with w > 0 or w < 0, correctly + // capturing the change in orientation when crossing the w=0 plane. + + // The culling logic remains the same based on the signed area/determinant. + // A value of 0 for hSgnArea means the points are collinear in (x, y, w) + // space, which corresponds to a degenerate triangle projection. + // Such triangles are typically not culled by this test (0 < 0 is false, 0 > 0 is false) + // and should be handled by the clipper if necessary. + return (RLSW.cullFace == SW_FRONT) + ? (hSgnArea < 0) // Cull if winding is "clockwise" in the projected sense + : (hSgnArea > 0); // Cull if winding is "counter-clockwise" in the projected sense +} + +static inline void sw_triangle_clip_and_project(void) +{ + sw_vertex_t *polygon = RLSW.vertexBuffer; + int *vertexCounter = &RLSW.vertexCounter; + + if (sw_polygon_clip(polygon, vertexCounter)) + { + // Transformation to screen space and normalization + for (int i = 0; i < *vertexCounter; i++) + { + sw_vertex_t *v = &polygon[i]; + + // Calculation of the reciprocal of W for normalization + // as well as perspective-correct attributes + const float invW = 1.0f/v->homogeneous[3]; + v->homogeneous[3] = invW; + + // Division of XYZ coordinates by weight + v->homogeneous[0] *= invW; + v->homogeneous[1] *= invW; + v->homogeneous[2] *= invW; + + // Division of texture coordinates (perspective-correct) + v->texcoord[0] *= invW; + v->texcoord[1] *= invW; + + // Division of colors (perspective-correct) + v->color[0] *= invW; + v->color[1] *= invW; + v->color[2] *= invW; + v->color[3] *= invW; + + // Transformation to screen space + sw_project_ndc_to_screen(v->screen, v->homogeneous); + } + } +} + +#define DEFINE_TRIANGLE_RASTER_SCANLINE(FUNC_NAME, ENABLE_TEXTURE, ENABLE_DEPTH_TEST, ENABLE_COLOR_BLEND) \ +static inline void FUNC_NAME(const sw_texture_t *tex, const sw_vertex_t *start, \ + const sw_vertex_t *end, float duDy, float dvDy) \ +{ \ + /* Convert and center the screen coordinates */ \ + int xStart = (int)(start->screen[0] + 0.5f); \ + int xEnd = (int)(end->screen[0] + 0.5f); \ + int y = (int)start->screen[1]; \ + \ + /* Compute the inverse horizontal distance along the X axis */ \ + float dx = end->screen[0] - start->screen[0]; \ + if (fabsf(dx) < 1e-6f) return; \ + float dxRcp = 1.0f/dx; \ + \ + /* Compute the interpolation steps along the X axis */ \ + float dzDx = (end->homogeneous[2] - start->homogeneous[2])*dxRcp; \ + float dwDx = (end->homogeneous[3] - start->homogeneous[3])*dxRcp; \ + \ + float dcDx[4] = { 0 }; \ + dcDx[0] = (end->color[0] - start->color[0])*dxRcp; \ + dcDx[1] = (end->color[1] - start->color[1])*dxRcp; \ + dcDx[2] = (end->color[2] - start->color[2])*dxRcp; \ + dcDx[3] = (end->color[3] - start->color[3])*dxRcp; \ + \ + float duDx = 0.0f, dvDx = 0.0f; \ + if (ENABLE_TEXTURE) { \ + duDx = (end->texcoord[0] - start->texcoord[0])*dxRcp; \ + dvDx = (end->texcoord[1] - start->texcoord[1])*dxRcp; \ + } \ + \ + /* Initializing the interpolation starting values */ \ + float z = start->homogeneous[2]; \ + float w = start->homogeneous[3]; \ + \ + float color[4] = { 0 }; \ + color[0] = start->color[0]; \ + color[1] = start->color[1]; \ + color[2] = start->color[2]; \ + color[3] = start->color[3]; \ + \ + float u = 0.0f, v = 0.0f; \ + if (ENABLE_TEXTURE) { \ + u = start->texcoord[0]; \ + v = start->texcoord[1]; \ + } \ + \ + /* Pre-calculate the starting pointers for the framebuffer row */ \ + void *cptr = sw_framebuffer_get_color_addr(RLSW.framebuffer.color, y*RLSW.framebuffer.width + xStart); \ + void *dptr = sw_framebuffer_get_depth_addr(RLSW.framebuffer.depth, y*RLSW.framebuffer.width + xStart); \ + \ + /* Scanline rasterization */ \ + for (int x = xStart; x < xEnd; x++) \ + { \ + /* Test and write depth */ \ + if (ENABLE_DEPTH_TEST) \ + { \ + /* TODO: Implement different depth funcs? */ \ + float depth = sw_framebuffer_read_depth(dptr); \ + if (z > depth) goto discard; \ + } \ + \ + sw_framebuffer_write_depth(dptr, z); \ + \ + /* Pixel color computation */ \ + float wRcp = 1.0f/w; \ + float srcColor[4] = { \ + color[0]*wRcp, \ + color[1]*wRcp, \ + color[2]*wRcp, \ + color[3]*wRcp \ + }; \ + \ + if (ENABLE_TEXTURE) \ + { \ + float texColor[4]; \ + float s = u*wRcp; \ + float t = v*wRcp; \ + sw_texture_sample(texColor, tex, s, t, duDx, duDy, dvDx, dvDy); \ + srcColor[0] *= texColor[0]; \ + srcColor[1] *= texColor[1]; \ + srcColor[2] *= texColor[2]; \ + srcColor[3] *= texColor[3]; \ + } \ + \ + if (ENABLE_COLOR_BLEND) \ + { \ + float dstColor[4]; \ + sw_framebuffer_read_color(dstColor, cptr); \ + \ + sw_blend_colors(dstColor, srcColor); \ + dstColor[0] = sw_saturate(dstColor[0]); \ + dstColor[1] = sw_saturate(dstColor[1]); \ + dstColor[2] = sw_saturate(dstColor[2]); \ + \ + sw_framebuffer_write_color(cptr, dstColor); \ + } \ + else \ + { \ + sw_framebuffer_write_color(cptr, srcColor); \ + } \ + \ + /* Increment the interpolation parameter, UVs, and pointers */ \ + discard: \ + z += dzDx; \ + w += dwDx; \ + color[0] += dcDx[0]; \ + color[1] += dcDx[1]; \ + color[2] += dcDx[2]; \ + color[3] += dcDx[3]; \ + if (ENABLE_TEXTURE) \ + { \ + u += duDx; \ + v += dvDx; \ + } \ + \ + sw_framebuffer_inc_color_addr(&cptr); \ + sw_framebuffer_inc_depth_addr(&dptr); \ + } \ +} + +#define DEFINE_TRIANGLE_RASTER(FUNC_NAME, FUNC_SCANLINE, ENABLE_TEXTURE) \ +static inline void FUNC_NAME(const sw_vertex_t *v0, const sw_vertex_t *v1, \ + const sw_vertex_t *v2, const sw_texture_t *tex) \ +{ \ + /* Swap vertices by increasing y */ \ + if (v0->screen[1] > v1->screen[1]) { const sw_vertex_t *tmp = v0; v0 = v1; v1 = tmp; } \ + if (v1->screen[1] > v2->screen[1]) { const sw_vertex_t *tmp = v1; v1 = v2; v2 = tmp; } \ + if (v0->screen[1] > v1->screen[1]) { const sw_vertex_t *tmp = v0; v0 = v1; v1 = tmp; } \ + \ + /* Extracting coordinates from the sorted vertices */ \ + float x0 = v0->screen[0], y0 = v0->screen[1]; \ + float x1 = v1->screen[0], y1 = v1->screen[1]; \ + float x2 = v2->screen[0], y2 = v2->screen[1]; \ + \ + /* Compute height differences */ \ + float h02 = y2 - y0; \ + float h01 = y1 - y0; \ + float h12 = y2 - y1; \ + \ + if (h02 < 1e-6f) return; \ + \ + /* Precompute the inverse values without additional checks */ \ + float invH02 = 1.0f/h02; \ + float invH01 = (h01 > 1e-6f)? 1.0f/h01 : 0.0f; \ + float invH12 = (h12 > 1e-6f)? 1.0f/h12 : 0.0f; \ + \ + /* Pre-calculation of slopes */ \ + float dx02 = (x2 - x0)*invH02; \ + float dx01 = (x1 - x0)*invH01; \ + float dx12 = (x2 - x1)*invH12; \ + \ + /* Y bounds (vertical clipping) */ \ + int yTop = (int)(y0 + 0.5f); \ + int yMiddle = (int)(y1 + 0.5f); \ + int yBottom = (int)(y2 + 0.5f); \ + \ + /* Compute gradients for each side of the triangle */ \ + sw_vertex_t vDy02, vDy01, vDy12; \ + sw_get_vertex_grad_PTCH(&vDy02, v0, v2, invH02); \ + sw_get_vertex_grad_PTCH(&vDy01, v0, v1, invH01); \ + sw_get_vertex_grad_PTCH(&vDy12, v1, v2, invH12); \ + \ + /* Initializing scanline variables */ \ + sw_vertex_t vLeft = *v0; \ + vLeft.screen[0] = x0; \ + sw_vertex_t vRight = *v0; \ + vRight.screen[0] = x0; \ + \ + /* Scanline for the upper part of the triangle */ \ + for (int y = yTop; y < yMiddle; y++) \ + { \ + vLeft.screen[1] = vRight.screen[1] = y; \ + \ + if (vLeft.screen[0] < vRight.screen[0]) FUNC_SCANLINE(tex, &vLeft, &vRight, vDy02.texcoord[0], vDy02.texcoord[1]); \ + else FUNC_SCANLINE(tex, &vRight, &vLeft, vDy02.texcoord[0], vDy02.texcoord[1]); \ + \ + sw_add_vertex_grad_PTCH(&vLeft, &vDy02); \ + vLeft.screen[0] += dx02; \ + sw_add_vertex_grad_PTCH(&vRight, &vDy01); \ + vRight.screen[0] += dx01; \ + } \ + \ + /* Scanline for the lower part of the triangle */ \ + vRight = *v1, vRight.screen[0] = x1; \ + \ + for (int y = yMiddle; y < yBottom; y++) \ + { \ + vLeft.screen[1] = vRight.screen[1] = y; \ + \ + if (vLeft.screen[0] < vRight.screen[0]) FUNC_SCANLINE(tex, &vLeft, &vRight, vDy02.texcoord[0], vDy02.texcoord[1]); \ + else FUNC_SCANLINE(tex, &vRight, &vLeft, vDy02.texcoord[0], vDy02.texcoord[1]); \ + \ + sw_add_vertex_grad_PTCH(&vLeft, &vDy02); \ + vLeft.screen[0] += dx02; \ + sw_add_vertex_grad_PTCH(&vRight, &vDy12); \ + vRight.screen[0] += dx12; \ + } \ +} + +DEFINE_TRIANGLE_RASTER_SCANLINE(sw_triangle_raster_scanline, 0, 0, 0) +DEFINE_TRIANGLE_RASTER_SCANLINE(sw_triangle_raster_scanline_TEX, 1, 0, 0) +DEFINE_TRIANGLE_RASTER_SCANLINE(sw_triangle_raster_scanline_DEPTH, 0, 1, 0) +DEFINE_TRIANGLE_RASTER_SCANLINE(sw_triangle_raster_scanline_BLEND, 0, 0, 1) +DEFINE_TRIANGLE_RASTER_SCANLINE(sw_triangle_raster_scanline_TEX_DEPTH, 1, 1, 0) +DEFINE_TRIANGLE_RASTER_SCANLINE(sw_triangle_raster_scanline_TEX_BLEND, 1, 0, 1) +DEFINE_TRIANGLE_RASTER_SCANLINE(sw_triangle_raster_scanline_DEPTH_BLEND, 0, 1, 1) +DEFINE_TRIANGLE_RASTER_SCANLINE(sw_triangle_raster_scanline_TEX_DEPTH_BLEND, 1, 1, 1) + +DEFINE_TRIANGLE_RASTER(sw_triangle_raster, sw_triangle_raster_scanline, false) +DEFINE_TRIANGLE_RASTER(sw_triangle_raster_TEX, sw_triangle_raster_scanline_TEX, true) +DEFINE_TRIANGLE_RASTER(sw_triangle_raster_DEPTH, sw_triangle_raster_scanline_DEPTH, false) +DEFINE_TRIANGLE_RASTER(sw_triangle_raster_BLEND, sw_triangle_raster_scanline_BLEND, false) +DEFINE_TRIANGLE_RASTER(sw_triangle_raster_TEX_DEPTH, sw_triangle_raster_scanline_TEX_DEPTH, true) +DEFINE_TRIANGLE_RASTER(sw_triangle_raster_TEX_BLEND, sw_triangle_raster_scanline_TEX_BLEND, true) +DEFINE_TRIANGLE_RASTER(sw_triangle_raster_DEPTH_BLEND, sw_triangle_raster_scanline_DEPTH_BLEND, false) +DEFINE_TRIANGLE_RASTER(sw_triangle_raster_TEX_DEPTH_BLEND, sw_triangle_raster_scanline_TEX_DEPTH_BLEND, true) + +static inline void sw_triangle_render(void) +{ + if (RLSW.stateFlags & SW_STATE_CULL_FACE) + { + if (!sw_triangle_face_culling()) return; + } + + sw_triangle_clip_and_project(); + + if (RLSW.vertexCounter < 3) return; + + #define TRIANGLE_RASTER(RASTER_FUNC) \ + { \ + for (int i = 0; i < RLSW.vertexCounter - 2; i++) \ + { \ + RASTER_FUNC( \ + &RLSW.vertexBuffer[0], \ + &RLSW.vertexBuffer[i + 1], \ + &RLSW.vertexBuffer[i + 2], \ + &RLSW.loadedTextures[RLSW.currentTexture] \ + ); \ + } \ + } + + if (SW_STATE_CHECK(SW_STATE_TEXTURE_2D | SW_STATE_DEPTH_TEST | SW_STATE_BLEND)) TRIANGLE_RASTER(sw_triangle_raster_TEX_DEPTH_BLEND) + else if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST | SW_STATE_BLEND)) TRIANGLE_RASTER(sw_triangle_raster_DEPTH_BLEND) + else if (SW_STATE_CHECK(SW_STATE_TEXTURE_2D | SW_STATE_BLEND)) TRIANGLE_RASTER(sw_triangle_raster_TEX_BLEND) + else if (SW_STATE_CHECK(SW_STATE_TEXTURE_2D | SW_STATE_DEPTH_TEST)) TRIANGLE_RASTER(sw_triangle_raster_TEX_DEPTH) + else if (SW_STATE_CHECK(SW_STATE_BLEND)) TRIANGLE_RASTER(sw_triangle_raster_BLEND) + else if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST)) TRIANGLE_RASTER(sw_triangle_raster_DEPTH) + else if (SW_STATE_CHECK(SW_STATE_TEXTURE_2D)) TRIANGLE_RASTER(sw_triangle_raster_TEX) + else TRIANGLE_RASTER(sw_triangle_raster) + + #undef TRIANGLE_RASTER +} + +// Quad rendering logic + +static inline bool sw_quad_face_culling(void) +{ + // NOTE: Face culling is done before clipping to avoid unnecessary computations. + // To handle quads crossing the w=0 plane correctly, + // we perform the winding order test in homogeneous coordinates directly, + // before the perspective division (division by w). + // For a convex quad with vertices P0, P1, P2, P3 in sequential order, + // the winding order of the quad is the same as the winding order + // of the triangle P0 P1 P2. We use the homogeneous triangle + // winding test on this first triangle. + + // Preload homogeneous coordinates into local variables + const float *h0 = RLSW.vertexBuffer[0].homogeneous; + const float *h1 = RLSW.vertexBuffer[1].homogeneous; + const float *h2 = RLSW.vertexBuffer[2].homogeneous; + + // NOTE: h3 is not needed for this test + // const float *h3 = RLSW.vertexBuffer[3].homogeneous; + + // Compute a value proportional to the signed area of the triangle P0 P1 P2 + // in the projected 2D plane, calculated directly using homogeneous coordinates + // BEFORE division by w. + // This is the determinant of the matrix formed by the (x, y, w) components + // of the vertices P0, P1, and P2. Its sign correctly indicates the winding order + // in homogeneous space and its relationship to the projected 2D winding order, + // even with negative w values. + // The determinant formula used here is: + // h0.x*(h1.y*h2.w - h2.y*h1.w) + + // h1.x*(h2.y*h0.w - h0.y*h2.w) + + // h2.x*(h0.y*h1.w - h1.y*h0.w) + + const float hSgnArea = + h0[0]*(h1[1]*h2[3] - h2[1]*h1[3]) + + h1[0]*(h2[1]*h0[3] - h0[1]*h2[3]) + + h2[0]*(h0[1]*h1[3] - h1[1]*h0[3]); + + // Perform face culling based on the winding order determined by the sign + // of the homogeneous area/determinant of triangle P0 P1 P2. + // This test is robust for points with w > 0 or w < 0 within the triangle, + // correctly capturing the change in orientation when crossing the w=0 plane. + + // A positive hSgnArea typically corresponds to a counter-clockwise + // winding in the projected space when all w > 0. + // A value of 0 for hSgnArea means P0, P1, P2 are collinear in (x, y, w) + // space, which corresponds to a degenerate triangle projection. + // Such quads might also be degenerate or non-planar. They are typically + // not culled by this test (0 < 0 is false, 0 > 0 is false) + // and should be handled by the clipper if necessary. + + return (RLSW.cullFace == SW_FRONT) + ? (hSgnArea < 0.0f) // Cull if winding is "clockwise" in the projected sense + : (hSgnArea > 0.0f); // Cull if winding is "counter-clockwise" in the projected sense +} + +static inline void sw_quad_clip_and_project(void) +{ + sw_vertex_t *polygon = RLSW.vertexBuffer; + int *vertexCounter = &RLSW.vertexCounter; + + if (sw_polygon_clip(polygon, vertexCounter)) + { + // Transformation to screen space and normalization + for (int i = 0; i < *vertexCounter; i++) + { + sw_vertex_t *v = &polygon[i]; + + // Calculation of the reciprocal of W for normalization + // as well as perspective-correct attributes + const float invW = 1.0f/v->homogeneous[3]; + v->homogeneous[3] = invW; + + // Division of XYZ coordinates by weight + v->homogeneous[0] *= invW; + v->homogeneous[1] *= invW; + v->homogeneous[2] *= invW; + + // Division of texture coordinates (perspective-correct) + v->texcoord[0] *= invW; + v->texcoord[1] *= invW; + + // Division of colors (perspective-correct) + v->color[0] *= invW; + v->color[1] *= invW; + v->color[2] *= invW; + v->color[3] *= invW; + + // Transformation to screen space + sw_project_ndc_to_screen(v->screen, v->homogeneous); + } + } +} + +static inline bool sw_quad_is_axis_aligned(void) +{ + int horizontal = 0; + int vertical = 0; + + for (int i = 0; i < 4; i++) + { + if (RLSW.vertexBuffer[i].homogeneous[3] != 1.0f) return false; + + const float *v0 = RLSW.vertexBuffer[i].position; + const float *v1 = RLSW.vertexBuffer[(i + 1)%4].position; + + float dx = v1[0] - v0[0]; + float dy = v1[1] - v0[1]; + + if (fabsf(dx) > 1e-6f && fabsf(dy) < 1e-6f) horizontal++; + else if (fabsf(dy) > 1e-6f && fabsf(dx) < 1e-6f) vertical++; + else return false; // Diagonal edge -> not axis-aligned + } + + return ((horizontal == 2) && (vertical == 2)); +} + +static inline void sw_quad_sort_cw(const sw_vertex_t* *output) +{ + // Sort 4 quad vertices into clockwise order with fixed layout: + // + // v0 -- v1 + // | | + // v3 -- v2 + // + // The goal is: + // - v0: top-left (minimum Y, then minimum X) + // - v1: top-right (minimum Y row, maximum X) + // - v2: bottom-right (maximum Y, maximum X) + // - v3: bottom-left (maximum Y, minimum X) + const sw_vertex_t *input = RLSW.vertexBuffer; + + // Separate vertices into top and bottom based on Y-coordinate + const sw_vertex_t *top[2] = {NULL, NULL}; + const sw_vertex_t *bottom[2] = {NULL, NULL}; + int topCount = 0, bottomCount = 0; + + // Find minimum and maximum Y + float minY = input[0].screen[1]; + float maxY = input[0].screen[1]; + + for (int i = 1; i < 4; i++) + { + if (input[i].screen[1] < minY) minY = input[i].screen[1]; + if (input[i].screen[1] > maxY) maxY = input[i].screen[1]; + } + + // Separate vertices based on Y-coordinate + for (int i = 0; i < 4; i++) + { + if (input[i].screen[1] == minY && topCount < 2) top[topCount++] = &input[i]; + else if (input[i].screen[1] == maxY && bottomCount < 2) bottom[bottomCount++] = &input[i]; + } + + // If we don't have enough top/bottom vertices (e.g., Y values are all different), + // classify vertices as top or bottom based on whether they're closer to minY or maxY + for (int i = 0; i < 4; i++) + { + if ((topCount < 2) && (&input[i] != top[0]) && (&input[i] != bottom[0]) && (&input[i] != bottom[1])) + { + if (fabsf(input[i].screen[1] - minY) <= fabsf(input[i].screen[1] - maxY)) top[topCount++] = &input[i]; + } + if ((bottomCount < 2) && (&input[i] != top[0]) && (&input[i] != top[1]) && (&input[i] != bottom[0])) + { + if (fabsf(input[i].screen[1] - maxY) < fabsf(input[i].screen[1] - minY)) bottom[bottomCount++] = &input[i]; + } + } + + // Sort top vertices by X (left to right) + if ((topCount == 2) && (top[0]->screen[0] > top[1]->screen[0])) + { + const sw_vertex_t *temp = top[0]; + top[0] = top[1]; + top[1] = temp; + } + + // Sort bottom vertices by X (left to right) + if (bottomCount == 2 && bottom[0]->screen[0] > bottom[1]->screen[0]) + { + const sw_vertex_t *temp = bottom[0]; + bottom[0] = bottom[1]; + bottom[1] = temp; + } + + // Assign vertices in clockwise order as per the required layout + output[0] = top[0]; // v0: top-left + output[1] = top[topCount-1]; // v1: top-right + output[2] = bottom[bottomCount-1]; // v2: bottom-right + output[3] = bottom[0]; // v3: bottom-left +} + +// TODO: REVIEW: Could a perfectly aligned quad, where one of the four points has a different depth, +// still appear perfectly aligned from a certain point of view? +// Because in that case, we would still need to perform perspective division for textures and colors... +#define DEFINE_QUAD_RASTER_AXIS_ALIGNED(FUNC_NAME, ENABLE_TEXTURE, ENABLE_DEPTH_TEST, ENABLE_COLOR_BLEND) \ +static inline void FUNC_NAME(void) \ +{ \ + const sw_vertex_t *sortedVerts[4]; \ + sw_quad_sort_cw(sortedVerts); \ + \ + const sw_vertex_t *v0 = sortedVerts[0]; \ + const sw_vertex_t *v1 = sortedVerts[1]; \ + const sw_vertex_t *v2 = sortedVerts[2]; \ + const sw_vertex_t *v3 = sortedVerts[3]; \ + \ + /* Screen bounds (axis-aligned) */ \ + int xMin = (int)(v0->screen[0] + 0.5f); \ + int yMin = (int)(v0->screen[1] + 0.5f); \ + int xMax = (int)(v2->screen[0] + 0.5f); \ + int yMax = (int)(v2->screen[1] + 0.5f); \ + \ + int width = xMax - xMin; \ + int height = yMax - yMin; \ + \ + if (width == 0 || height == 0) return; \ + \ + float wRcp = (width > 0.0f)? 1.0f/width : 0.0f; \ + float hRcp = (height > 0.0f)? 1.0f/height : 0.0f; \ + \ + /* Calculation of vertex gradients in X and Y */ \ + float tcDx[2], tcDy[2]; \ + if (ENABLE_TEXTURE) { \ + tcDx[0] = (v1->texcoord[0] - v0->texcoord[0])*wRcp; \ + tcDx[1] = (v1->texcoord[1] - v0->texcoord[1])*wRcp; \ + tcDy[0] = (v3->texcoord[0] - v0->texcoord[0])*hRcp; \ + tcDy[1] = (v3->texcoord[1] - v0->texcoord[1])*hRcp; \ + } \ + \ + float cDx[4], cDy[4]; \ + cDx[0] = (v1->color[0] - v0->color[0])*wRcp; \ + cDx[1] = (v1->color[1] - v0->color[1])*wRcp; \ + cDx[2] = (v1->color[2] - v0->color[2])*wRcp; \ + cDx[3] = (v1->color[3] - v0->color[3])*wRcp; \ + cDy[0] = (v3->color[0] - v0->color[0])*hRcp; \ + cDy[1] = (v3->color[1] - v0->color[1])*hRcp; \ + cDy[2] = (v3->color[2] - v0->color[2])*hRcp; \ + cDy[3] = (v3->color[3] - v0->color[3])*hRcp; \ + \ + float zDx, zDy; \ + zDx = (v1->homogeneous[2] - v0->homogeneous[2])*wRcp; \ + zDy = (v3->homogeneous[2] - v0->homogeneous[2])*hRcp; \ + \ + /* Start of quad rasterization */ \ + const sw_texture_t *tex; \ + if (ENABLE_TEXTURE) tex = &RLSW.loadedTextures[RLSW.currentTexture]; \ + \ + void *cDstBase = RLSW.framebuffer.color; \ + void *dDstBase = RLSW.framebuffer.depth; \ + int wDst = RLSW.framebuffer.width; \ + \ + float zScanline = v0->homogeneous[2]; \ + float uScanline = v0->texcoord[0]; \ + float vScanline = v0->texcoord[1]; \ + \ + float colorScanline[4] = { \ + v0->color[0], \ + v0->color[1], \ + v0->color[2], \ + v0->color[3] \ + }; \ + \ + for (int y = yMin; y < yMax; y++) \ + { \ + void *cptr = sw_framebuffer_get_color_addr(cDstBase, y*wDst + xMin); \ + void *dptr = sw_framebuffer_get_depth_addr(dDstBase, y*wDst + xMin); \ + \ + float z = zScanline; \ + float u = uScanline; \ + float v = vScanline; \ + \ + float color[4] = { \ + colorScanline[0], \ + colorScanline[1], \ + colorScanline[2], \ + colorScanline[3] \ + }; \ + \ + /* Scanline rasterization */ \ + for (int x = xMin; x < xMax; x++) \ + { \ + /* Test and write depth */ \ + if (ENABLE_DEPTH_TEST) \ + { \ + /* TODO: Implement different depth funcs? */ \ + float depth = sw_framebuffer_read_depth(dptr); \ + if (z > depth) goto discard; \ + } \ + \ + sw_framebuffer_write_depth(dptr, z); \ + \ + /* Pixel color computation */ \ + float srcColor[4] = { \ + color[0], \ + color[1], \ + color[2], \ + color[3] \ + }; \ + \ + if (ENABLE_TEXTURE) \ + { \ + float texColor[4]; \ + sw_texture_sample(texColor, tex, u, v, tcDx[0], tcDy[0], tcDx[1], tcDy[1]); \ + srcColor[0] *= texColor[0]; \ + srcColor[1] *= texColor[1]; \ + srcColor[2] *= texColor[2]; \ + srcColor[3] *= texColor[3]; \ + } \ + \ + if (ENABLE_COLOR_BLEND) \ + { \ + float dstColor[4]; \ + sw_framebuffer_read_color(dstColor, cptr); \ + \ + sw_blend_colors(dstColor, srcColor); \ + dstColor[0] = sw_saturate(dstColor[0]); \ + dstColor[1] = sw_saturate(dstColor[1]); \ + dstColor[2] = sw_saturate(dstColor[2]); \ + dstColor[3] = sw_saturate(dstColor[3]); \ + \ + sw_framebuffer_write_color(cptr, dstColor); \ + } \ + else sw_framebuffer_write_color(cptr, srcColor); \ + \ + discard: \ + z += zDx; \ + color[0] += cDx[0]; \ + color[1] += cDx[1]; \ + color[2] += cDx[2]; \ + color[3] += cDx[3]; \ + \ + if (ENABLE_TEXTURE) \ + { \ + u += tcDx[0]; \ + v += tcDx[1]; \ + } \ + \ + sw_framebuffer_inc_color_addr(&cptr); \ + sw_framebuffer_inc_depth_addr(&dptr); \ + } \ + \ + zScanline += zDy; \ + colorScanline[0] += cDy[0]; \ + colorScanline[1] += cDy[1]; \ + colorScanline[2] += cDy[2]; \ + colorScanline[3] += cDy[3]; \ + \ + if (ENABLE_TEXTURE) \ + { \ + uScanline += tcDy[0]; \ + vScanline += tcDy[1]; \ + } \ + } \ +} + +DEFINE_QUAD_RASTER_AXIS_ALIGNED(sw_quad_raster_axis_aligned, 0, 0, 0) +DEFINE_QUAD_RASTER_AXIS_ALIGNED(sw_quad_raster_axis_aligned_TEX, 1, 0, 0) +DEFINE_QUAD_RASTER_AXIS_ALIGNED(sw_quad_raster_axis_aligned_DEPTH, 0, 1, 0) +DEFINE_QUAD_RASTER_AXIS_ALIGNED(sw_quad_raster_axis_aligned_BLEND, 0, 0, 1) +DEFINE_QUAD_RASTER_AXIS_ALIGNED(sw_quad_raster_axis_aligned_TEX_DEPTH, 1, 1, 0) +DEFINE_QUAD_RASTER_AXIS_ALIGNED(sw_quad_raster_axis_aligned_TEX_BLEND, 1, 0, 1) +DEFINE_QUAD_RASTER_AXIS_ALIGNED(sw_quad_raster_axis_aligned_DEPTH_BLEND, 0, 1, 1) +DEFINE_QUAD_RASTER_AXIS_ALIGNED(sw_quad_raster_axis_aligned_TEX_DEPTH_BLEND, 1, 1, 1) + +static inline void sw_quad_render(void) +{ + if (RLSW.stateFlags & SW_STATE_CULL_FACE) + { + if (!sw_quad_face_culling()) return; + } + + sw_quad_clip_and_project(); + + if (RLSW.vertexCounter < 3) return; + + if (RLSW.vertexCounter == 4 && sw_quad_is_axis_aligned()) + { + if (SW_STATE_CHECK(SW_STATE_TEXTURE_2D | SW_STATE_DEPTH_TEST | SW_STATE_BLEND)) sw_quad_raster_axis_aligned_TEX_DEPTH_BLEND(); + else if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST | SW_STATE_BLEND)) sw_quad_raster_axis_aligned_DEPTH_BLEND(); + else if (SW_STATE_CHECK(SW_STATE_TEXTURE_2D | SW_STATE_BLEND)) sw_quad_raster_axis_aligned_TEX_BLEND(); + else if (SW_STATE_CHECK(SW_STATE_TEXTURE_2D | SW_STATE_DEPTH_TEST)) sw_quad_raster_axis_aligned_TEX_DEPTH(); + else if (SW_STATE_CHECK(SW_STATE_BLEND)) sw_quad_raster_axis_aligned_BLEND(); + else if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST)) sw_quad_raster_axis_aligned_DEPTH(); + else if (SW_STATE_CHECK(SW_STATE_TEXTURE_2D)) sw_quad_raster_axis_aligned_TEX(); + else sw_quad_raster_axis_aligned(); + return; + } + + #define TRIANGLE_RASTER(RASTER_FUNC) \ + { \ + for (int i = 0; i < RLSW.vertexCounter - 2; i++) \ + { \ + RASTER_FUNC( \ + &RLSW.vertexBuffer[0], \ + &RLSW.vertexBuffer[i + 1], \ + &RLSW.vertexBuffer[i + 2], \ + &RLSW.loadedTextures[RLSW.currentTexture] \ + ); \ + } \ + } + + if (SW_STATE_CHECK(SW_STATE_TEXTURE_2D | SW_STATE_DEPTH_TEST | SW_STATE_BLEND)) TRIANGLE_RASTER(sw_triangle_raster_TEX_DEPTH_BLEND) + else if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST | SW_STATE_BLEND)) TRIANGLE_RASTER(sw_triangle_raster_DEPTH_BLEND) + else if (SW_STATE_CHECK(SW_STATE_TEXTURE_2D | SW_STATE_BLEND)) TRIANGLE_RASTER(sw_triangle_raster_TEX_BLEND) + else if (SW_STATE_CHECK(SW_STATE_TEXTURE_2D | SW_STATE_DEPTH_TEST)) TRIANGLE_RASTER(sw_triangle_raster_TEX_DEPTH) + else if (SW_STATE_CHECK(SW_STATE_BLEND)) TRIANGLE_RASTER(sw_triangle_raster_BLEND) + else if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST)) TRIANGLE_RASTER(sw_triangle_raster_DEPTH) + else if (SW_STATE_CHECK(SW_STATE_TEXTURE_2D)) TRIANGLE_RASTER(sw_triangle_raster_TEX) + else TRIANGLE_RASTER(sw_triangle_raster) + + #undef TRIANGLE_RASTER +} + +// Line rendering logic + +static inline bool sw_line_clip_coord(float q, float p, float *t0, float *t1) +{ + if (fabsf(p) < SW_CLIP_EPSILON) + { + // Check if the line is entirely outside the window + if (q < -SW_CLIP_EPSILON) return 0; // Completely outside + return 1; // Completely inside or on the edges + } + + const float r = q/p; + + if (p < 0) + { + if (r > *t1) return 0; + if (r > *t0) *t0 = r; + } + else + { + if (r < *t0) return 0; + if (r < *t1) *t1 = r; + } + + return 1; +} + +static inline bool sw_line_clip(sw_vertex_t *v0, sw_vertex_t *v1) +{ + float t0 = 0.0f, t1 = 1.0f; + float dH[4], dC[4]; + + for (int i = 0; i < 4; i++) + { + dH[i] = v1->homogeneous[i] - v0->homogeneous[i]; + dC[i] = v1->color[i] - v0->color[i]; + } + + // Clipping Liang-Barsky + if (!sw_line_clip_coord(v0->homogeneous[3] - v0->homogeneous[0], -dH[3] + dH[0], &t0, &t1)) return false; + if (!sw_line_clip_coord(v0->homogeneous[3] + v0->homogeneous[0], -dH[3] - dH[0], &t0, &t1)) return false; + if (!sw_line_clip_coord(v0->homogeneous[3] - v0->homogeneous[1], -dH[3] + dH[1], &t0, &t1)) return false; + if (!sw_line_clip_coord(v0->homogeneous[3] + v0->homogeneous[1], -dH[3] - dH[1], &t0, &t1)) return false; + if (!sw_line_clip_coord(v0->homogeneous[3] - v0->homogeneous[2], -dH[3] + dH[2], &t0, &t1)) return false; + if (!sw_line_clip_coord(v0->homogeneous[3] + v0->homogeneous[2], -dH[3] - dH[2], &t0, &t1)) return false; + + // Clipping Scissor + if (RLSW.stateFlags & SW_STATE_SCISSOR_TEST) + { + if (!sw_line_clip_coord(v0->homogeneous[0] - RLSW.scClipMin[0]*v0->homogeneous[3], RLSW.scClipMin[0]*dH[3] - dH[0], &t0, &t1)) return false; + if (!sw_line_clip_coord(RLSW.scClipMax[0]*v0->homogeneous[3] - v0->homogeneous[0], dH[0] - RLSW.scClipMax[0]*dH[3], &t0, &t1)) return false; + if (!sw_line_clip_coord(v0->homogeneous[1] - RLSW.scClipMin[1]*v0->homogeneous[3], RLSW.scClipMin[1]*dH[3] - dH[1], &t0, &t1)) return false; + if (!sw_line_clip_coord(RLSW.scClipMax[1]*v0->homogeneous[3] - v0->homogeneous[1], dH[1] - RLSW.scClipMax[1]*dH[3], &t0, &t1)) return false; + } + + // Interpolation of new coordinates + if (t1 < 1.0f) + { + for (int i = 0; i < 4; i++) + { + v1->homogeneous[i] = v0->homogeneous[i] + t1*dH[i]; + v1->color[i] = v0->color[i] + t1*dC[i]; + } + } + + if (t0 > 0.0f) + { + for (int i = 0; i < 4; i++) + { + v0->homogeneous[i] += t0*dH[i]; + v0->color[i] += t0*dC[i]; + } + } + + return true; +} + +static inline bool sw_line_clip_and_project(sw_vertex_t *v0, sw_vertex_t *v1) +{ + if (!sw_line_clip(v0, v1)) return false; + + // Convert homogeneous coordinates to NDC + v0->homogeneous[3] = 1.0f/v0->homogeneous[3]; + v1->homogeneous[3] = 1.0f/v1->homogeneous[3]; + for (int i = 0; i < 3; i++) + { + v0->homogeneous[i] *= v0->homogeneous[3]; + v1->homogeneous[i] *= v1->homogeneous[3]; + } + + // Convert NDC coordinates to screen space + sw_project_ndc_to_screen(v0->screen, v0->homogeneous); + sw_project_ndc_to_screen(v1->screen, v1->homogeneous); + + return true; +} + +#define DEFINE_LINE_RASTER(FUNC_NAME, ENABLE_DEPTH_TEST, ENABLE_COLOR_BLEND) \ +static inline void FUNC_NAME(const sw_vertex_t *v0, const sw_vertex_t *v1) \ +{ \ + int x1 = (int)(v0->screen[0] + 0.5f); \ + int y1 = (int)(v0->screen[1] + 0.5f); \ + int x2 = (int)(v1->screen[0] + 0.5f); \ + int y2 = (int)(v1->screen[1] + 0.5f); \ + \ + int dx = x2 - x1; \ + int dy = y2 - y1; \ + \ + /* Handling of lines that are more horizontal or vertical */ \ + if (dx == 0 && dy == 0) \ + { \ + /* TODO: A point should be rendered here */ \ + return; \ + } \ + \ + bool yLonger = (abs(dy) > abs(dx)); \ + int longLen, shortLen; \ + \ + if (yLonger) \ + { \ + longLen = dy; \ + shortLen = dx; \ + } \ + else \ + { \ + longLen = dx; \ + shortLen = dy; \ + } \ + \ + /* Handling of traversal direction */ \ + int sgnInc = (longLen < 0)? -1 : 1; \ + int abslongLen = abs(longLen); \ + \ + /* Calculation of the increment step for the shorter coordinate */ \ + int decInc = 0; \ + if (abslongLen != 0) decInc = (shortLen << 16)/abslongLen; \ + \ + float longLenRcp = (abslongLen != 0)? (1.0f/abslongLen) : 0.0f; \ + \ + /* Calculation of interpolation steps */ \ + const float zStep = (v1->homogeneous[2] - v0->homogeneous[2])*longLenRcp; \ + const float rStep = (v1->color[0] - v0->color[0])*longLenRcp; \ + const float gStep = (v1->color[1] - v0->color[1])*longLenRcp; \ + const float bStep = (v1->color[2] - v0->color[2])*longLenRcp; \ + const float aStep = (v1->color[3] - v0->color[3])*longLenRcp; \ + \ + float z = v0->homogeneous[2]; \ + \ + float color[4] = { \ + v0->color[0], \ + v0->color[1], \ + v0->color[2], \ + v0->color[3] \ + }; \ + \ + const int fbWidth = RLSW.framebuffer.width; \ + void *cBuffer = RLSW.framebuffer.color; \ + void *dBuffer = RLSW.framebuffer.depth; \ + \ + int j = 0; \ + if (yLonger) \ + { \ + for (int i = 0; i != longLen; i += sgnInc) \ + { \ + int offset = (y1 + i)*fbWidth + (x1 + (j >> 16)); \ + void *dptr = sw_framebuffer_get_depth_addr(dBuffer, offset); \ + \ + if (ENABLE_DEPTH_TEST) \ + { \ + float depth = sw_framebuffer_read_depth(dptr); \ + if (z > depth) goto discardA; \ + } \ + \ + sw_framebuffer_write_depth(dptr, z); \ + \ + void *cptr = sw_framebuffer_get_color_addr(cBuffer, offset); \ + \ + if (ENABLE_COLOR_BLEND) \ + { \ + float dstColor[4]; \ + sw_framebuffer_read_color(dstColor, cptr); \ + \ + sw_blend_colors(dstColor, color); \ + sw_framebuffer_write_color(cptr, dstColor); \ + } \ + else sw_framebuffer_write_color(cptr, color); \ + \ + discardA: \ + j += decInc; \ + z += zStep; \ + color[0] += rStep; \ + color[1] += gStep; \ + color[2] += bStep; \ + color[3] += aStep; \ + } \ + } \ + else \ + { \ + for (int i = 0; i != longLen; i += sgnInc) \ + { \ + int offset = (y1 + (j >> 16))*fbWidth + (x1 + i); \ + void *dptr = sw_framebuffer_get_depth_addr(dBuffer, offset); \ + \ + if (ENABLE_DEPTH_TEST) \ + { \ + float depth = sw_framebuffer_read_depth(dptr); \ + if (z > depth) goto discardB; \ + } \ + \ + sw_framebuffer_write_depth(dptr, z); \ + \ + void *cptr = sw_framebuffer_get_color_addr(cBuffer, offset); \ + \ + if (ENABLE_COLOR_BLEND) \ + { \ + float dstColor[4]; \ + sw_framebuffer_read_color(dstColor, cptr); \ + \ + sw_blend_colors(dstColor, color); \ + sw_framebuffer_write_color(cptr, dstColor); \ + } \ + else sw_framebuffer_write_color(cptr, color); \ + \ + discardB: \ + j += decInc; \ + z += zStep; \ + color[0] += rStep; \ + color[1] += gStep; \ + color[2] += bStep; \ + color[3] += aStep; \ + } \ + } \ +} + +#define DEFINE_LINE_THICK_RASTER(FUNC_NAME, RASTER_FUNC) \ +void FUNC_NAME(const sw_vertex_t *v1, const sw_vertex_t *v2) \ +{ \ + sw_vertex_t tv1, tv2; \ + \ + int x1 = (int)v1->screen[0]; \ + int y1 = (int)v1->screen[1]; \ + int x2 = (int)v2->screen[0]; \ + int y2 = (int)v2->screen[1]; \ + \ + int dx = x2 - x1; \ + int dy = y2 - y1; \ + \ + RASTER_FUNC(v1, v2); \ + \ + if (dx != 0 && abs(dy/dx) < 1) \ + { \ + int wy = (int)((RLSW.lineWidth - 1.0f)*abs(dx)/sqrtf(dx*dx + dy*dy)); \ + wy >>= 1; \ + for (int i = 1; i <= wy; i++) \ + { \ + tv1 = *v1, tv2 = *v2; \ + tv1.screen[1] -= i; \ + tv2.screen[1] -= i; \ + RASTER_FUNC(&tv1, &tv2); \ + tv1 = *v1, tv2 = *v2; \ + tv1.screen[1] += i; \ + tv2.screen[1] += i; \ + RASTER_FUNC(&tv1, &tv2); \ + } \ + } \ + else if (dy != 0) \ + { \ + int wx = (int)((RLSW.lineWidth - 1.0f)*abs(dy)/sqrtf(dx*dx + dy*dy)); \ + wx >>= 1; \ + for (int i = 1; i <= wx; i++) \ + { \ + tv1 = *v1, tv2 = *v2; \ + tv1.screen[0] -= i; \ + tv2.screen[0] -= i; \ + RASTER_FUNC(&tv1, &tv2); \ + tv1 = *v1, tv2 = *v2; \ + tv1.screen[0] += i; \ + tv2.screen[0] += i; \ + RASTER_FUNC(&tv1, &tv2); \ + } \ + } \ +} + +DEFINE_LINE_RASTER(sw_line_raster, 0, 0) +DEFINE_LINE_RASTER(sw_line_raster_DEPTH, 1, 0) +DEFINE_LINE_RASTER(sw_line_raster_BLEND, 0, 1) +DEFINE_LINE_RASTER(sw_line_raster_DEPTH_BLEND, 1, 1) + +DEFINE_LINE_THICK_RASTER(sw_line_thick_raster, sw_line_raster) +DEFINE_LINE_THICK_RASTER(sw_line_thick_raster_DEPTH, sw_line_raster_DEPTH) +DEFINE_LINE_THICK_RASTER(sw_line_thick_raster_BLEND, sw_line_raster_BLEND) +DEFINE_LINE_THICK_RASTER(sw_line_thick_raster_DEPTH_BLEND, sw_line_raster_DEPTH_BLEND) + +static inline void sw_line_render(sw_vertex_t *vertices) +{ + if (!sw_line_clip_and_project(&vertices[0], &vertices[1])) return; + + if (RLSW.lineWidth >= 2.0f) + { + if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST | SW_STATE_BLEND)) sw_line_thick_raster_DEPTH_BLEND(&vertices[0], &vertices[1]); + else if (SW_STATE_CHECK(SW_STATE_BLEND)) sw_line_thick_raster_BLEND(&vertices[0], &vertices[1]); + else if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST)) sw_line_thick_raster_DEPTH(&vertices[0], &vertices[1]); + else sw_line_thick_raster(&vertices[0], &vertices[1]); + } + else + { + if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST | SW_STATE_BLEND)) sw_line_raster_DEPTH_BLEND(&vertices[0], &vertices[1]); + else if (SW_STATE_CHECK(SW_STATE_BLEND)) sw_line_raster_BLEND(&vertices[0], &vertices[1]); + else if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST)) sw_line_raster_DEPTH(&vertices[0], &vertices[1]); + else sw_line_raster(&vertices[0], &vertices[1]); + } +} + +// Point rendering logic + +static inline bool sw_point_clip_and_project(sw_vertex_t *v) +{ + if (v->homogeneous[3] != 1.0f) + { + for (int_fast8_t i = 0; i < 3; i++) + { + if ((v->homogeneous[i] < -v->homogeneous[3]) || (v->homogeneous[i] > v->homogeneous[3])) return false; + } + + v->homogeneous[3] = 1.0f/v->homogeneous[3]; + v->homogeneous[0] *= v->homogeneous[3]; + v->homogeneous[1] *= v->homogeneous[3]; + v->homogeneous[2] *= v->homogeneous[3]; + } + + sw_project_ndc_to_screen(v->screen, v->homogeneous); + + const int *min = NULL, *max = NULL; + + if (RLSW.stateFlags & SW_STATE_SCISSOR_TEST) + { + min = RLSW.scMin; + max = RLSW.scMax; + } + else + { + min = RLSW.vpMin; + max = RLSW.vpMax; + } + + bool insideX = (v->screen[0] - RLSW.pointRadius < max[0]) && (v->screen[0] + RLSW.pointRadius > min[0]); + bool insideY = (v->screen[1] - RLSW.pointRadius < max[1]) && (v->screen[1] + RLSW.pointRadius > min[1]); + + return (insideX && insideY); +} + +#define DEFINE_POINT_RASTER(FUNC_NAME, ENABLE_DEPTH_TEST, ENABLE_COLOR_BLEND, CHECK_BOUNDS) \ +static inline void FUNC_NAME(int x, int y, float z, const float color[4]) \ +{ \ + if (CHECK_BOUNDS == 1) \ + { \ + if (x < RLSW.vpMin[0] || x >= RLSW.vpMax[0]) return; \ + if (y < RLSW.vpMin[1] || y >= RLSW.vpMax[1]) return; \ + } \ + else if (CHECK_BOUNDS == SW_SCISSOR_TEST) \ + { \ + if (x < RLSW.scMin[0] || x >= RLSW.scMax[0]) return; \ + if (y < RLSW.scMin[1] || y >= RLSW.scMax[1]) return; \ + } \ + \ + int offset = y*RLSW.framebuffer.width + x; \ + \ + void *dptr = sw_framebuffer_get_depth_addr(RLSW.framebuffer.depth, offset); \ + \ + if (ENABLE_DEPTH_TEST) \ + { \ + float depth = sw_framebuffer_read_depth(dptr); \ + if (z > depth) return; \ + } \ + \ + sw_framebuffer_write_depth(dptr, z); \ + \ + void *cptr = sw_framebuffer_get_color_addr(RLSW.framebuffer.color, offset); \ + \ + if (ENABLE_COLOR_BLEND) \ + { \ + float dstColor[4]; \ + sw_framebuffer_read_color(dstColor, cptr); \ + \ + sw_blend_colors(dstColor, color); \ + sw_framebuffer_write_color(cptr, dstColor); \ + } \ + else sw_framebuffer_write_color(cptr, color); \ +} + +#define DEFINE_POINT_THICK_RASTER(FUNC_NAME, RASTER_FUNC) \ +static inline void FUNC_NAME(sw_vertex_t *v) \ +{ \ + int cx = v->screen[0]; \ + int cy = v->screen[1]; \ + float cz = v->homogeneous[2]; \ + int radius = RLSW.pointRadius; \ + const float *color = v->color; \ + \ + int x = 0; \ + int y = radius; \ + int d = 3 - 2*radius; \ + \ + while (x <= y) \ + { \ + for (int i = -x; i <= x; i++) \ + { \ + RASTER_FUNC(cx + i, cy + y, cz, color); \ + RASTER_FUNC(cx + i, cy - y, cz, color); \ + } \ + for (int i = -y; i <= y; i++) \ + { \ + RASTER_FUNC(cx + i, cy + x, cz, color); \ + RASTER_FUNC(cx + i, cy - x, cz, color); \ + } \ + if (d > 0) \ + { \ + y--; \ + d = d + 4*(x - y) + 10; \ + } \ + else d = d + 4*x + 6; \ + x++; \ + } \ +} + +DEFINE_POINT_RASTER(sw_point_raster, 0, 0, 0) +DEFINE_POINT_RASTER(sw_point_raster_DEPTH, 1, 0, 0) +DEFINE_POINT_RASTER(sw_point_raster_BLEND, 0, 1, 0) +DEFINE_POINT_RASTER(sw_point_raster_DEPTH_BLEND, 1, 1, 0) + +DEFINE_POINT_RASTER(sw_point_raster_CHECK, 0, 0, 1) +DEFINE_POINT_RASTER(sw_point_raster_DEPTH_CHECK, 1, 0, 1) +DEFINE_POINT_RASTER(sw_point_raster_BLEND_CHECK, 0, 1, 1) +DEFINE_POINT_RASTER(sw_point_raster_DEPTH_BLEND_CHECK, 1, 1, 1) + +DEFINE_POINT_RASTER(sw_point_raster_CHECK_SCISSOR, 0, 0, SW_SCISSOR_TEST) +DEFINE_POINT_RASTER(sw_point_raster_DEPTH_CHECK_SCISSOR, 1, 0, SW_SCISSOR_TEST) +DEFINE_POINT_RASTER(sw_point_raster_BLEND_CHECK_SCISSOR, 0, 1, SW_SCISSOR_TEST) +DEFINE_POINT_RASTER(sw_point_raster_DEPTH_BLEND_CHECK_SCISSOR, 1, 1, SW_SCISSOR_TEST) + +DEFINE_POINT_THICK_RASTER(sw_point_thick_raster, sw_point_raster_CHECK) +DEFINE_POINT_THICK_RASTER(sw_point_thick_raster_DEPTH, sw_point_raster_DEPTH_CHECK) +DEFINE_POINT_THICK_RASTER(sw_point_thick_raster_BLEND, sw_point_raster_BLEND_CHECK) +DEFINE_POINT_THICK_RASTER(sw_point_thick_raster_DEPTH_BLEND, sw_point_raster_DEPTH_BLEND_CHECK) + +DEFINE_POINT_THICK_RASTER(sw_point_thick_raster_SCISSOR, sw_point_raster_CHECK_SCISSOR) +DEFINE_POINT_THICK_RASTER(sw_point_thick_raster_DEPTH_SCISSOR, sw_point_raster_DEPTH_CHECK_SCISSOR) +DEFINE_POINT_THICK_RASTER(sw_point_thick_raster_BLEND_SCISSOR, sw_point_raster_BLEND_CHECK_SCISSOR) +DEFINE_POINT_THICK_RASTER(sw_point_thick_raster_DEPTH_BLEND_SCISSOR, sw_point_raster_DEPTH_BLEND_CHECK_SCISSOR) + +static inline void sw_point_render(sw_vertex_t *v) +{ + if (!sw_point_clip_and_project(v)) return; + + if (RLSW.pointRadius >= 1.0f) + { + if (SW_STATE_CHECK(SW_STATE_SCISSOR_TEST)) + { + if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST | SW_STATE_BLEND)) sw_point_thick_raster_DEPTH_BLEND_SCISSOR(v); + else if (SW_STATE_CHECK(SW_STATE_BLEND)) sw_point_thick_raster_BLEND_SCISSOR(v); + else if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST)) sw_point_thick_raster_DEPTH_SCISSOR(v); + else sw_point_thick_raster_SCISSOR(v); + } + else + { + if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST | SW_STATE_BLEND)) sw_point_thick_raster_DEPTH_BLEND(v); + else if (SW_STATE_CHECK(SW_STATE_BLEND)) sw_point_thick_raster_BLEND(v); + else if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST)) sw_point_thick_raster_DEPTH(v); + else sw_point_thick_raster(v); + } + } + else + { + if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST | SW_STATE_BLEND)) sw_point_raster_DEPTH_BLEND(v->screen[0], v->screen[1], v->homogeneous[2], v->color); + else if (SW_STATE_CHECK(SW_STATE_BLEND)) sw_point_raster_BLEND(v->screen[0], v->screen[1], v->homogeneous[2], v->color); + else if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST)) sw_point_raster_DEPTH(v->screen[0], v->screen[1], v->homogeneous[2], v->color); + else sw_point_raster(v->screen[0], v->screen[1], v->homogeneous[2], v->color); + } +} + +// Polygon modes mendering logic + +static inline void sw_poly_point_render(void) +{ + for (int i = 0; i < RLSW.vertexCounter; i++) sw_point_render(&RLSW.vertexBuffer[i]); +} + +static inline void sw_poly_line_render(void) +{ + const sw_vertex_t *vertices = RLSW.vertexBuffer; + int cm1 = RLSW.vertexCounter - 1; + + for (int i = 0; i < cm1; i++) sw_line_render((sw_vertex_t[2]){ vertices[i], vertices[i + 1] }); + + sw_line_render((sw_vertex_t[2]){ vertices[cm1], vertices[0] }); +} + +static inline void sw_poly_fill_render(void) +{ + switch (RLSW.drawMode) + { + case SW_POINTS: sw_point_render(&RLSW.vertexBuffer[0]); break; + case SW_LINES: sw_line_render(RLSW.vertexBuffer); break; + case SW_TRIANGLES: sw_triangle_render(); break; + case SW_QUADS: sw_quad_render(); break; + } +} + +// Validity check helper functions + +static inline bool sw_is_texture_valid(uint32_t id) +{ + bool valid = true; + + if (id == 0) valid = false; + else if (id >= SW_MAX_TEXTURES) valid = false; + else if (RLSW.loadedTextures[id].pixels.cptr == 0) valid = false; + + return true; +} + +static inline bool sw_is_texture_filter_valid(int filter) +{ + return (filter == SW_NEAREST || filter == SW_LINEAR); +} + +static inline bool sw_is_texture_wrap_valid(int wrap) +{ + return (wrap == SW_REPEAT || wrap == SW_CLAMP); +} + +static inline bool sw_is_draw_mode_valid(int mode) +{ + bool result = false; + + switch (mode) + { + case SW_POINTS: + case SW_LINES: + case SW_TRIANGLES: + case SW_QUADS: result = true; break; + default: break; + } + + return result; +} + +static inline bool sw_is_poly_mode_valid(int mode) +{ + bool result = false; + + switch (mode) + { + case SW_POINT: + case SW_LINE: + case SW_FILL: result = true; break; + default: break; + } + + return result; +} + +static inline bool sw_is_face_valid(int face) +{ + return (face == SW_FRONT || face == SW_BACK); +} + +static inline bool sw_is_blend_src_factor_valid(int blend) +{ + bool result = false; + + switch (blend) + { + case SW_ZERO: + case SW_ONE: + case SW_SRC_COLOR: + case SW_ONE_MINUS_SRC_COLOR: + case SW_SRC_ALPHA: + case SW_ONE_MINUS_SRC_ALPHA: + case SW_DST_ALPHA: + case SW_ONE_MINUS_DST_ALPHA: + case SW_DST_COLOR: + case SW_ONE_MINUS_DST_COLOR: + case SW_SRC_ALPHA_SATURATE: result = true; break; + default: break; + } + + return result; +} + +static inline bool sw_is_blend_dst_factor_valid(int blend) +{ + bool result = false; + + switch (blend) + { + case SW_ZERO: + case SW_ONE: + case SW_SRC_COLOR: + case SW_ONE_MINUS_SRC_COLOR: + case SW_SRC_ALPHA: + case SW_ONE_MINUS_SRC_ALPHA: + case SW_DST_ALPHA: + case SW_ONE_MINUS_DST_ALPHA: + case SW_DST_COLOR: + case SW_ONE_MINUS_DST_COLOR: result = true; break; + default: break; + } + + return result; +} + +//---------------------------------------------------------------------------------- +// Module Functions Definition +//---------------------------------------------------------------------------------- +bool swInit(int w, int h) +{ + if (!sw_framebuffer_load(w, h)) { swClose(); return false; } + + swViewport(0, 0, w, h); + swScissor(0, 0, w, h); + + RLSW.loadedTextures = SW_MALLOC(SW_MAX_TEXTURES*sizeof(sw_texture_t)); + if (RLSW.loadedTextures == NULL) { swClose(); return false; } + + RLSW.freeTextureIds = SW_MALLOC(SW_MAX_TEXTURES*sizeof(uint32_t)); + if (RLSW.loadedTextures == NULL) { swClose(); return false; } + + RLSW.clearColor[0] = 0.0f; + RLSW.clearColor[1] = 0.0f; + RLSW.clearColor[2] = 0.0f; + RLSW.clearColor[3] = 1.0f; + RLSW.clearDepth = 1.0f; + + RLSW.currentMatrixMode = SW_MODELVIEW; + RLSW.currentMatrix = &RLSW.stackModelview[0]; + + sw_matrix_id(RLSW.stackProjection[0]); + sw_matrix_id(RLSW.stackModelview[0]); + sw_matrix_id(RLSW.stackTexture[0]); + sw_matrix_id(RLSW.matMVP); + + RLSW.stackProjectionCounter = 1; + RLSW.stackModelviewCounter = 1; + RLSW.stackTextureCounter = 1; + RLSW.isDirtyMVP = false; + + RLSW.current.texcoord[0] = 0.0f; + RLSW.current.texcoord[1] = 0.0f; + + RLSW.current.color[0] = 1.0f; + RLSW.current.color[1] = 1.0f; + RLSW.current.color[2] = 1.0f; + RLSW.current.color[3] = 1.0f; + + RLSW.srcFactor = SW_SRC_ALPHA; + RLSW.dstFactor = SW_ONE_MINUS_SRC_ALPHA; + + RLSW.srcFactorFunc = sw_factor_src_alpha; + RLSW.dstFactorFunc = sw_factor_one_minus_src_alpha; + + RLSW.polyMode = SW_FILL; + RLSW.cullFace = SW_BACK; + + static const float defTex[3*2*2] = { + 1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, + }; + + RLSW.loadedTextures[0].pixels.cptr = defTex; + RLSW.loadedTextures[0].width = 2; + RLSW.loadedTextures[0].height = 2; + RLSW.loadedTextures[0].wMinus1 = 1; + RLSW.loadedTextures[0].hMinus1 = 1; + RLSW.loadedTextures[0].format = SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32; + RLSW.loadedTextures[0].minFilter = SW_NEAREST; + RLSW.loadedTextures[0].magFilter = SW_NEAREST; + RLSW.loadedTextures[0].sWrap = SW_REPEAT; + RLSW.loadedTextures[0].tWrap = SW_REPEAT; + RLSW.loadedTextures[0].tx = 0.5f; + RLSW.loadedTextures[0].ty = 0.5f; + RLSW.loadedTextures[0].copy = false; + + RLSW.loadedTextureCount = 1; + + return true; +} + +void swClose(void) +{ + for (int i = 1; i < RLSW.loadedTextureCount; i++) + { + sw_texture_t *texture = &RLSW.loadedTextures[i]; + if (sw_is_texture_valid(i) && texture->copy) SW_FREE(texture->pixels.ptr); + } + + SW_FREE(RLSW.framebuffer.color); + SW_FREE(RLSW.framebuffer.depth); + SW_FREE(RLSW.loadedTextures); + SW_FREE(RLSW.freeTextureIds); + + RLSW = (sw_context_t) { 0 }; +} + +bool swResizeFramebuffer(int w, int h) +{ + return sw_framebuffer_resize(w, h); +} + +void swCopyFramebuffer(int x, int y, int w, int h, SWformat format, SWtype type, void *pixels) +{ + sw_pixelformat_t pFormat = sw_get_pixel_format(format, type); + + if (w <= 0) + { + RLSW.errCode = SW_INVALID_VALUE; + return; + } + + if (h <= 0) + { + RLSW.errCode = SW_INVALID_VALUE; + return; + } + + if (w > RLSW.framebuffer.width) w = RLSW.framebuffer.width; + if (h > RLSW.framebuffer.height) h = RLSW.framebuffer.height; + + x = sw_clampi(x, 0, w); + y = sw_clampi(y, 0, h); + + switch (pFormat) + { + case SW_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: sw_framebuffer_copy_to_GRAYALPHA(x, y, w, h, pixels); break; + case SW_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: sw_framebuffer_copy_to_GRAYALPHA(x, y, w, h, pixels); break; + case SW_PIXELFORMAT_UNCOMPRESSED_R5G6B5: sw_framebuffer_copy_to_R5G6B5(x, y, w, h, pixels); break; + case SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8: sw_framebuffer_copy_to_R8G8B8(x, y, w, h, pixels); break; + case SW_PIXELFORMAT_UNCOMPRESSED_R5G5B5A1: sw_framebuffer_copy_to_R5G5B5A1(x, y, w, h, pixels); break; + case SW_PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: sw_framebuffer_copy_to_R4G4B4A4(x, y, w, h, pixels); break; + case SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: sw_framebuffer_copy_to_R8G8B8A8(x, y, w, h, pixels); break; + case SW_PIXELFORMAT_UNCOMPRESSED_R32: sw_framebuffer_copy_to_R32(x, y, w, h, pixels); break; + case SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32: sw_framebuffer_copy_to_R32G32B32(x, y, w, h, pixels); break; + case SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: sw_framebuffer_copy_to_R32G32B32A32(x, y, w, h, pixels); break; + case SW_PIXELFORMAT_UNCOMPRESSED_R16: sw_framebuffer_copy_to_R16(x, y, w, h, pixels); break; + case SW_PIXELFORMAT_UNCOMPRESSED_R16G16B16: sw_framebuffer_copy_to_R16G16B16(x, y, w, h, pixels); break; + case SW_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: sw_framebuffer_copy_to_R16G16B16A16(x, y, w, h, pixels); break; + default: RLSW.errCode = SW_INVALID_ENUM; break; + } +} + +void swBlitFramebuffer(int xDst, int yDst, int wDst, int hDst, int xSrc, int ySrc, int wSrc, int hSrc, SWformat format, SWtype type, void *pixels) +{ + sw_pixelformat_t pFormat = sw_get_pixel_format(format, type); + + if (wSrc <= 0) + { + RLSW.errCode = SW_INVALID_VALUE; + return; + } + + if (hSrc <= 0) + { + RLSW.errCode = SW_INVALID_VALUE; + return; + } + + if (wSrc > RLSW.framebuffer.width) wSrc = RLSW.framebuffer.width; + if (hSrc > RLSW.framebuffer.height) hSrc = RLSW.framebuffer.height; + + xSrc = sw_clampi(xSrc, 0, wSrc); + ySrc = sw_clampi(ySrc, 0, hSrc); + + switch (pFormat) + { + case SW_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: sw_framebuffer_blit_to_GRAYALPHA(xDst, yDst, wDst, hDst, xSrc, ySrc, wSrc, hSrc, pixels); break; + case SW_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: sw_framebuffer_blit_to_GRAYALPHA(xDst, yDst, wDst, hDst, xSrc, ySrc, wSrc, hSrc, pixels); break; + case SW_PIXELFORMAT_UNCOMPRESSED_R5G6B5: sw_framebuffer_blit_to_R5G6B5(xDst, yDst, wDst, hDst, xSrc, ySrc, wSrc, hSrc, pixels); break; + case SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8: sw_framebuffer_blit_to_R8G8B8(xDst, yDst, wDst, hDst, xSrc, ySrc, wSrc, hSrc, pixels); break; + case SW_PIXELFORMAT_UNCOMPRESSED_R5G5B5A1: sw_framebuffer_blit_to_R5G5B5A1(xDst, yDst, wDst, hDst, xSrc, ySrc, wSrc, hSrc, pixels); break; + case SW_PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: sw_framebuffer_blit_to_R4G4B4A4(xDst, yDst, wDst, hDst, xSrc, ySrc, wSrc, hSrc, pixels); break; + case SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: sw_framebuffer_blit_to_R8G8B8A8(xDst, yDst, wDst, hDst, xSrc, ySrc, wSrc, hSrc, pixels); break; + case SW_PIXELFORMAT_UNCOMPRESSED_R32: sw_framebuffer_blit_to_R32(xDst, yDst, wDst, hDst, xSrc, ySrc, wSrc, hSrc, pixels); break; + case SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32: sw_framebuffer_blit_to_R32G32B32(xDst, yDst, wDst, hDst, xSrc, ySrc, wSrc, hSrc, pixels); break; + case SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: sw_framebuffer_blit_to_R32G32B32A32(xDst, yDst, wDst, hDst, xSrc, ySrc, wSrc, hSrc, pixels); break; + case SW_PIXELFORMAT_UNCOMPRESSED_R16: sw_framebuffer_blit_to_R16(xDst, yDst, wDst, hDst, xSrc, ySrc, wSrc, hSrc, pixels); break; + case SW_PIXELFORMAT_UNCOMPRESSED_R16G16B16: sw_framebuffer_blit_to_R16G16B16(xDst, yDst, wDst, hDst, xSrc, ySrc, wSrc, hSrc, pixels); break; + case SW_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: sw_framebuffer_blit_to_R16G16B16A16(xDst, yDst, wDst, hDst, xSrc, ySrc, wSrc, hSrc, pixels); break; + default: RLSW.errCode = SW_INVALID_ENUM; break; + } +} + +void *swGetColorBuffer(int *w, int *h) +{ + if (w) *w = RLSW.framebuffer.width; + if (h) *h = RLSW.framebuffer.height; + + return RLSW.framebuffer.color; +} + +void swEnable(SWstate state) +{ + switch (state) + { + case SW_SCISSOR_TEST: RLSW.stateFlags |= SW_STATE_SCISSOR_TEST; break; + case SW_TEXTURE_2D: RLSW.stateFlags |= SW_STATE_TEXTURE_2D; break; + case SW_DEPTH_TEST: RLSW.stateFlags |= SW_STATE_DEPTH_TEST; break; + case SW_CULL_FACE: RLSW.stateFlags |= SW_STATE_CULL_FACE; break; + case SW_BLEND: RLSW.stateFlags |= SW_STATE_BLEND; break; + default: RLSW.errCode = SW_INVALID_ENUM; break; + } +} + +void swDisable(SWstate state) +{ + switch (state) + { + case SW_SCISSOR_TEST: RLSW.stateFlags &= ~SW_STATE_SCISSOR_TEST; break; + case SW_TEXTURE_2D: RLSW.stateFlags &= ~SW_STATE_TEXTURE_2D; break; + case SW_DEPTH_TEST: RLSW.stateFlags &= ~SW_STATE_DEPTH_TEST; break; + case SW_CULL_FACE: RLSW.stateFlags &= ~SW_STATE_CULL_FACE; break; + case SW_BLEND: RLSW.stateFlags &= ~SW_STATE_BLEND; break; + default: RLSW.errCode = SW_INVALID_ENUM; break; + } +} + +void swGetIntegerv(SWget name, int *v) +{ + switch (name) + { + case SW_MODELVIEW_STACK_DEPTH: *v = SW_MODELVIEW_STACK_DEPTH; break; + case SW_PROJECTION_STACK_DEPTH: *v = SW_PROJECTION_STACK_DEPTH; break; + case SW_TEXTURE_STACK_DEPTH: *v = SW_TEXTURE_STACK_DEPTH; break; + default: RLSW.errCode = SW_INVALID_ENUM; break; + } +} + +void swGetFloatv(SWget name, float *v) +{ + switch (name) + { + case SW_COLOR_CLEAR_VALUE: + { + v[0] = RLSW.clearColor[0]; + v[1] = RLSW.clearColor[1]; + v[2] = RLSW.clearColor[2]; + v[3] = RLSW.clearColor[3]; + } break; + case SW_CURRENT_COLOR: + { + v[0] = RLSW.vertexBuffer[RLSW.vertexCounter - 1].color[0]; + v[1] = RLSW.vertexBuffer[RLSW.vertexCounter - 1].color[1]; + v[2] = RLSW.vertexBuffer[RLSW.vertexCounter - 1].color[2]; + v[3] = RLSW.vertexBuffer[RLSW.vertexCounter - 1].color[3]; + } break; + case SW_CURRENT_TEXTURE_COORDS: + { + v[0] = RLSW.vertexBuffer[RLSW.vertexCounter - 1].texcoord[0]; + v[1] = RLSW.vertexBuffer[RLSW.vertexCounter - 1].texcoord[1]; + } break; + case SW_POINT_SIZE: + { + v[0] = 2.0f*RLSW.pointRadius; + } break; + case SW_LINE_WIDTH: + { + v[0] = RLSW.lineWidth; + } break; + case SW_MODELVIEW_MATRIX: + { + for (int i = 0; i < 16; i++) v[i] = RLSW.stackModelview[RLSW.stackModelviewCounter - 1][i]; + + } break; + case SW_PROJECTION_MATRIX: + { + for (int i = 0; i < 16; i++) v[i] = RLSW.stackProjection[RLSW.stackProjectionCounter - 1][i]; + + } break; + case SW_TEXTURE_MATRIX: + { + for (int i = 0; i < 16; i++) v[i] = RLSW.stackTexture[RLSW.stackTextureCounter - 1][i]; + + } break; + default: RLSW.errCode = SW_INVALID_ENUM; break; + } +} + +const char *swGetString(SWget name) +{ + const char *result = NULL; + + switch (name) + { + case SW_VENDOR: result = "RLSW Header"; break; + case SW_RENDERER: result = "RLSW Software Renderer"; break; + case SW_VERSION: result = "RLSW 1.0"; break; + case SW_EXTENSIONS: result = "None"; break; + default: RLSW.errCode = SW_INVALID_ENUM; break; + } + + return result; +} + +SWerrcode swGetError(void) +{ + SWerrcode ret = RLSW.errCode; + RLSW.errCode = SW_NO_ERROR; + return ret; +} + +void swViewport(int x, int y, int width, int height) +{ + if ((width < 0) || (height < 0)) + { + RLSW.errCode = SW_INVALID_VALUE; + return; + } + + RLSW.vpSize[0] = width; + RLSW.vpSize[1] = height; + + RLSW.vpHalfSize[0] = (int)(width/2.0f + 0.5f); + RLSW.vpHalfSize[1] = (int)(height/2.0f + 0.5f); + + RLSW.vpCenter[0] = x + RLSW.vpHalfSize[0]; + RLSW.vpCenter[1] = y + RLSW.vpHalfSize[1]; + + RLSW.vpMin[0] = sw_clampi(x, 0, RLSW.framebuffer.width - 1); + RLSW.vpMin[1] = sw_clampi(y, 0, RLSW.framebuffer.height - 1); + RLSW.vpMax[0] = sw_clampi(x + width, 0, RLSW.framebuffer.width - 1); + RLSW.vpMax[1] = sw_clampi(y + height, 0, RLSW.framebuffer.height - 1); +} + +void swScissor(int x, int y, int width, int height) +{ + if ((width < 0) || (height < 0)) + { + RLSW.errCode = SW_INVALID_VALUE; + return; + } + + RLSW.scMin[0] = sw_clampi(x, 0, RLSW.framebuffer.width - 1); + RLSW.scMin[1] = sw_clampi(y, 0, RLSW.framebuffer.height - 1); + RLSW.scMax[0] = sw_clampi(x + width, 0, RLSW.framebuffer.width - 1); + RLSW.scMax[1] = sw_clampi(y + height, 0, RLSW.framebuffer.height - 1); + + RLSW.scClipMin[0] = (2.0f*(float)RLSW.scMin[0]/(float)RLSW.vpSize[0]) - 1.0f; + RLSW.scClipMax[0] = (2.0f*(float)RLSW.scMax[0]/(float)RLSW.vpSize[0]) - 1.0f; + RLSW.scClipMax[1] = 1.0f - (2.0f*(float)RLSW.scMin[1]/(float)RLSW.vpSize[1]); + RLSW.scClipMin[1] = 1.0f - (2.0f*(float)RLSW.scMax[1]/(float)RLSW.vpSize[1]); +} + +void swClearColor(float r, float g, float b, float a) +{ + RLSW.clearColor[0] = r; + RLSW.clearColor[1] = g; + RLSW.clearColor[2] = b; + RLSW.clearColor[3] = a; +} + +void swClear(uint32_t bitmask) +{ + int size = RLSW.framebuffer.width*RLSW.framebuffer.height; + + if ((bitmask & (SW_COLOR_BUFFER_BIT | SW_DEPTH_BUFFER_BIT)) == (SW_COLOR_BUFFER_BIT | SW_DEPTH_BUFFER_BIT)) + { + sw_framebuffer_fill(RLSW.framebuffer.color, RLSW.framebuffer.depth,size, RLSW.clearColor, RLSW.clearDepth); + } + else if (bitmask & (SW_COLOR_BUFFER_BIT)) + { + sw_framebuffer_fill_color(RLSW.framebuffer.color, size, RLSW.clearColor); + } + else if (bitmask & SW_DEPTH_BUFFER_BIT) + { + sw_framebuffer_fill_depth(RLSW.framebuffer.depth, size, RLSW.clearDepth); + } +} + +void swBlendFunc(SWfactor sfactor, SWfactor dfactor) +{ + if (!sw_is_blend_src_factor_valid(sfactor) || + !sw_is_blend_dst_factor_valid(dfactor)) + { + RLSW.errCode = SW_INVALID_ENUM; + return; + } + + RLSW.srcFactor = sfactor; + RLSW.dstFactor = dfactor; + + switch (sfactor) + { + case SW_ZERO: RLSW.srcFactorFunc = sw_factor_zero; break; + case SW_ONE: RLSW.srcFactorFunc = sw_factor_one; break; + case SW_SRC_COLOR: RLSW.srcFactorFunc = sw_factor_src_color; break; + case SW_ONE_MINUS_SRC_COLOR: RLSW.srcFactorFunc = sw_factor_one_minus_src_color; break; + case SW_SRC_ALPHA: RLSW.srcFactorFunc = sw_factor_src_alpha; break; + case SW_ONE_MINUS_SRC_ALPHA: RLSW.srcFactorFunc = sw_factor_one_minus_src_alpha; break; + case SW_DST_ALPHA: RLSW.srcFactorFunc = sw_factor_dst_alpha; break; + case SW_ONE_MINUS_DST_ALPHA: RLSW.srcFactorFunc = sw_factor_one_minus_dst_alpha; break; + case SW_DST_COLOR: RLSW.srcFactorFunc = sw_factor_dst_color; break; + case SW_ONE_MINUS_DST_COLOR: RLSW.srcFactorFunc = sw_factor_one_minus_dst_color; break; + case SW_SRC_ALPHA_SATURATE: RLSW.srcFactorFunc = sw_factor_src_alpha_saturate; break; + default: break; + } + + switch (dfactor) + { + case SW_ZERO: RLSW.dstFactorFunc = sw_factor_zero; break; + case SW_ONE: RLSW.dstFactorFunc = sw_factor_one; break; + case SW_SRC_COLOR: RLSW.dstFactorFunc = sw_factor_src_color; break; + case SW_ONE_MINUS_SRC_COLOR: RLSW.dstFactorFunc = sw_factor_one_minus_src_color; break; + case SW_SRC_ALPHA: RLSW.dstFactorFunc = sw_factor_src_alpha; break; + case SW_ONE_MINUS_SRC_ALPHA: RLSW.dstFactorFunc = sw_factor_one_minus_src_alpha; break; + case SW_DST_ALPHA: RLSW.dstFactorFunc = sw_factor_dst_alpha; break; + case SW_ONE_MINUS_DST_ALPHA: RLSW.dstFactorFunc = sw_factor_one_minus_dst_alpha; break; + case SW_DST_COLOR: RLSW.dstFactorFunc = sw_factor_dst_color; break; + case SW_ONE_MINUS_DST_COLOR: RLSW.dstFactorFunc = sw_factor_one_minus_dst_color; break; + case SW_SRC_ALPHA_SATURATE: break; + default: break; + } +} + +void swPolygonMode(SWpoly mode) +{ + if (!sw_is_poly_mode_valid(mode)) + { + RLSW.errCode = SW_INVALID_ENUM; + return; + } + + RLSW.polyMode = mode; +} + +void swCullFace(SWface face) +{ + if (!sw_is_face_valid(face)) + { + RLSW.errCode = SW_INVALID_ENUM; + return; + } + + RLSW.cullFace = face; +} + +void swPointSize(float size) +{ + RLSW.pointRadius = floorf(size*0.5f); +} + +void swLineWidth(float width) +{ + RLSW.lineWidth = roundf(width); +} + +void swMatrixMode(SWmatrix mode) +{ + switch (mode) + { + case SW_PROJECTION: RLSW.currentMatrix = &RLSW.stackProjection[RLSW.stackProjectionCounter - 1]; break; + case SW_MODELVIEW: RLSW.currentMatrix = &RLSW.stackModelview[RLSW.stackModelviewCounter - 1]; break; + case SW_TEXTURE: RLSW.currentMatrix = &RLSW.stackTexture[RLSW.stackTextureCounter - 1]; break; + default: RLSW.errCode = SW_INVALID_ENUM; return; + } + + RLSW.currentMatrixMode = mode; +} + +void swPushMatrix(void) +{ + switch (RLSW.currentMatrixMode) + { + case SW_PROJECTION: + { + if (RLSW.stackProjectionCounter >= SW_MAX_PROJECTION_STACK_SIZE) + { + RLSW.errCode = SW_STACK_OVERFLOW; + return; + } + + int iOld = RLSW.stackProjectionCounter - 1; + int iNew = RLSW.stackProjectionCounter++; + + for (int i = 0; i < 16; i++) + { + RLSW.stackProjection[iNew][i] = RLSW.stackProjection[iOld][i]; + } + + RLSW.currentMatrix = &RLSW.stackProjection[iNew]; + } break; + case SW_MODELVIEW: + { + if (RLSW.stackModelviewCounter >= SW_MAX_MODELVIEW_STACK_SIZE) + { + RLSW.errCode = SW_STACK_OVERFLOW; + return; + } + + int iOld = RLSW.stackModelviewCounter - 1; + int iNew = RLSW.stackModelviewCounter++; + + for (int i = 0; i < 16; i++) + { + RLSW.stackModelview[iNew][i] = RLSW.stackModelview[iOld][i]; + } + + RLSW.currentMatrix = &RLSW.stackModelview[iNew]; + } break; + case SW_TEXTURE: + { + if (RLSW.stackTextureCounter >= SW_MAX_TEXTURE_STACK_SIZE) + { + RLSW.errCode = SW_STACK_OVERFLOW; + return; + } + + int iOld = RLSW.stackTextureCounter - 1; + int iNew = RLSW.stackTextureCounter++; + + for (int i = 0; i < 16; i++) + { + RLSW.stackTexture[iNew][i] = RLSW.stackTexture[iOld][i]; + } + + RLSW.currentMatrix = &RLSW.stackTexture[iNew]; + } break; + default: break; + } +} + +void swPopMatrix(void) +{ + switch (RLSW.currentMatrixMode) + { + case SW_PROJECTION: + { + if (RLSW.stackProjectionCounter <= 0) + { + RLSW.errCode = SW_STACK_UNDERFLOW; + return; + } + + RLSW.currentMatrix = &RLSW.stackProjection[--RLSW.stackProjectionCounter]; + RLSW.isDirtyMVP = true; //< The MVP is considered to have been changed + } break; + case SW_MODELVIEW: + { + if (RLSW.stackModelviewCounter <= 0) + { + RLSW.errCode = SW_STACK_UNDERFLOW; + return; + } + + RLSW.currentMatrix = &RLSW.stackModelview[--RLSW.stackModelviewCounter]; + RLSW.isDirtyMVP = true; //< The MVP is considered to have been changed + } break; + case SW_TEXTURE: + { + if (RLSW.stackTextureCounter <= 0) + { + RLSW.errCode = SW_STACK_UNDERFLOW; + return; + } + + RLSW.currentMatrix = &RLSW.stackTexture[--RLSW.stackTextureCounter]; + } break; + default: break; + } +} + +void swLoadIdentity(void) +{ + sw_matrix_id(*RLSW.currentMatrix); + if (RLSW.currentMatrixMode != SW_TEXTURE) RLSW.isDirtyMVP = true; +} + +void swTranslatef(float x, float y, float z) +{ + sw_matrix_t mat; + sw_matrix_id(mat); + + mat[12] = x; + mat[13] = y; + mat[14] = z; + + sw_matrix_mul(*RLSW.currentMatrix, mat, *RLSW.currentMatrix); + + if (RLSW.currentMatrixMode != SW_TEXTURE) RLSW.isDirtyMVP = true; +} + +void swRotatef(float angle, float x, float y, float z) +{ + angle *= SW_DEG2RAD; + + float lengthSq = x*x + y*y + z*z; + + if (lengthSq != 1.0f && lengthSq != 0.0f) + { + float invLength = 1.0f/sqrtf(lengthSq); + x *= invLength; + y *= invLength; + z *= invLength; + } + + float sinres = sinf(angle); + float cosres = cosf(angle); + float t = 1.0f - cosres; + + sw_matrix_t mat; + + mat[0] = x*x*t + cosres; + mat[1] = y*x*t + z*sinres; + mat[2] = z*x*t - y*sinres; + mat[3] = 0.0f; + + mat[4] = x*y*t - z*sinres; + mat[5] = y*y*t + cosres; + mat[6] = z*y*t + x*sinres; + mat[7] = 0.0f; + + mat[8] = x*z*t + y*sinres; + mat[9] = y*z*t - x*sinres; + mat[10] = z*z*t + cosres; + mat[11] = 0.0f; + + mat[12] = 0.0f; + mat[13] = 0.0f; + mat[14] = 0.0f; + mat[15] = 1.0f; + + sw_matrix_mul(*RLSW.currentMatrix, mat, *RLSW.currentMatrix); + + if (RLSW.currentMatrixMode != SW_TEXTURE) RLSW.isDirtyMVP = true; +} + +void swScalef(float x, float y, float z) +{ + sw_matrix_t mat; + + mat[0] = x, mat[1] = 0, mat[2] = 0, mat[3] = 0; + mat[4] = 0, mat[5] = y, mat[6] = 0, mat[7] = 0; + mat[8] = 0, mat[9] = 0, mat[10] = z, mat[11] = 0; + mat[12] = 0, mat[13] = 0, mat[14] = 0, mat[15] = 1; + + sw_matrix_mul(*RLSW.currentMatrix, mat, *RLSW.currentMatrix); + + if (RLSW.currentMatrixMode != SW_TEXTURE) RLSW.isDirtyMVP = true; +} + +void swMultMatrixf(const float *mat) +{ + sw_matrix_mul(*RLSW.currentMatrix, mat, *RLSW.currentMatrix); + + if (RLSW.currentMatrixMode != SW_TEXTURE) RLSW.isDirtyMVP = true; +} + +void swFrustum(double left, double right, double bottom, double top, double znear, double zfar) +{ + sw_matrix_t mat; + + double rl = right - left; + double tb = top - bottom; + double fn = zfar - znear; + + mat[0] = (float)(znear*2.0)/rl; + mat[1] = 0.0f; + mat[2] = 0.0f; + mat[3] = 0.0f; + + mat[4] = 0.0f; + mat[5] = (float)(znear*2.0)/tb; + mat[6] = 0.0f; + mat[7] = 0.0f; + + mat[8] = (float)(right + left)/rl; + mat[9] = (float)(top + bottom)/tb; + mat[10] = -(float)(zfar + znear)/fn; + mat[11] = -1.0f; + + mat[12] = 0.0f; + mat[13] = 0.0f; + mat[14] = -(zfar*znear*2.0)/fn; + mat[15] = 0.0f; + + sw_matrix_mul(*RLSW.currentMatrix, *RLSW.currentMatrix, mat); + + if (RLSW.currentMatrixMode != SW_TEXTURE) RLSW.isDirtyMVP = true; +} + +void swOrtho(double left, double right, double bottom, double top, double znear, double zfar) +{ + sw_matrix_t mat; + + double rl = right - left; + double tb = top - bottom; + double fn = zfar - znear; + + mat[0] = 2.0f/(float)rl; + mat[1] = 0.0f; + mat[2] = 0.0f; + mat[3] = 0.0f; + + mat[4] = 0.0f; + mat[5] = 2.0f/(float)tb; + mat[6] = 0.0f; + mat[7] = 0.0f; + + mat[8] = 0.0f; + mat[9] = 0.0f; + mat[10] = -2.0f/(float)fn; + mat[11] = 0.0f; + + mat[12] = -(float)(left + right)/rl; + mat[13] = -(float)(top + bottom)/tb; + mat[14] = -(float)(zfar + znear)/fn; + mat[15] = 1.0f; + + sw_matrix_mul(*RLSW.currentMatrix, *RLSW.currentMatrix, mat); + + if (RLSW.currentMatrixMode != SW_TEXTURE) RLSW.isDirtyMVP = true; +} + +void swBegin(SWdraw mode) +{ + // Check if the draw mode is valid + if (!sw_is_draw_mode_valid(mode)) + { + RLSW.errCode = SW_INVALID_ENUM; + return; + } + + // Recalculate the MVP if this is needed + if (RLSW.isDirtyMVP) + { + sw_matrix_mul_rst(RLSW.matMVP, + RLSW.stackModelview[RLSW.stackModelviewCounter - 1], + RLSW.stackProjection[RLSW.stackProjectionCounter - 1]); + + RLSW.isDirtyMVP = false; + } + + // Obtain the number of vertices needed for this primitive + switch (mode) + { + case SW_POINTS: RLSW.reqVertices = 1; break; + case SW_LINES: RLSW.reqVertices = 2; break; + case SW_TRIANGLES: RLSW.reqVertices = 3; break; + case SW_QUADS: RLSW.reqVertices = 4; break; + } + + // Initialize required values + RLSW.vertexCounter = 0; + RLSW.drawMode = mode; +} + +void swEnd(void) +{ + RLSW.drawMode = 0; +} + +void swVertex2i(int x, int y) +{ + const float v[4] = { (float)x, (float)y, 0.0f, 1.0f }; + swVertex4fv(v); +} + +void swVertex2f(float x, float y) +{ + const float v[4] = { x, y, 0.0f, 1.0f }; + swVertex4fv(v); +} + +void swVertex2fv(const float *v) +{ + const float v4[4] = { v[0], v[1], 0.0f, 1.0f }; + swVertex4fv(v4); +} + +void swVertex3i(int x, int y, int z) +{ + const float v[4] = { (float)x, (float)y, (float)z, 1.0f }; + swVertex4fv(v); +} + +void swVertex3f(float x, float y, float z) +{ + const float v[4] = { x, y, z, 1.0f }; + swVertex4fv(v); +} + +void swVertex3fv(const float *v) +{ + const float v4[4] = { v[0], v[1], v[2], 1.0f }; + swVertex4fv(v4); +} + +void swVertex4i(int x, int y, int z, int w) +{ + const float v[4] = { (float)x, (float)y, (float)z, (float)w }; + swVertex4fv(v); +} + +void swVertex4f(float x, float y, float z, float w) +{ + const float v[4] = { x, y, z, w }; + swVertex4fv(v); +} + +void swVertex4fv(const float *v) +{ + // Copy the position in the current vertex + sw_vertex_t *vertex = &RLSW.vertexBuffer[RLSW.vertexCounter++]; + for (int i = 0; i < 4; i++) vertex->position[i] = v[i]; + + // Copy additonal vertex data + for (int i = 0; i < 2; i++) vertex->texcoord[i] = RLSW.current.texcoord[i]; + for (int i = 0; i < 4; i++) vertex->color[i] = RLSW.current.color[i]; + + // Calculation of homogeneous coordinates + const float *m = RLSW.matMVP; + vertex->homogeneous[0] = m[0]*v[0] + m[4]*v[1] + m[8]*v[2] + m[12]*v[3]; + vertex->homogeneous[1] = m[1]*v[0] + m[5]*v[1] + m[9]*v[2] + m[13]*v[3]; + vertex->homogeneous[2] = m[2]*v[0] + m[6]*v[1] + m[10]*v[2] + m[14]*v[3]; + vertex->homogeneous[3] = m[3]*v[0] + m[7]*v[1] + m[11]*v[2] + m[15]*v[3]; + + // Immediate rendering of the primitive if the required number is reached + if (RLSW.vertexCounter == RLSW.reqVertices) + { + switch (RLSW.polyMode) + { + case SW_FILL: sw_poly_fill_render(); break; + case SW_LINE: sw_poly_line_render(); break; + case SW_POINT: sw_poly_point_render(); break; + default: break; + } + + RLSW.vertexCounter = 0; + } +} + +void swColor3ub(uint8_t r, uint8_t g, uint8_t b) +{ + float cv[4]; + cv[0] = (float)r/255; + cv[1] = (float)g/255; + cv[2] = (float)b/255; + cv[3] = 1.0f; + + swColor4fv(cv); +} + +void swColor3ubv(const uint8_t *v) +{ + float cv[4]; + cv[0] = (float)v[0]/255; + cv[1] = (float)v[1]/255; + cv[2] = (float)v[2]/255; + cv[3] = 1.0f; + + swColor4fv(cv); +} + +void swColor3f(float r, float g, float b) +{ + float cv[4]; + cv[0] = r; + cv[1] = g; + cv[2] = b; + cv[3] = 1.0f; + + swColor4fv(cv); +} + +void swColor3fv(const float *v) +{ + float cv[4]; + cv[0] = v[0]; + cv[1] = v[1]; + cv[2] = v[2]; + cv[3] = 1.0f; + + swColor4fv(cv); +} + +void swColor4ub(uint8_t r, uint8_t g, uint8_t b, uint8_t a) +{ + float cv[4]; + cv[0] = (float)r/255; + cv[1] = (float)g/255; + cv[2] = (float)b/255; + cv[3] = (float)a/255; + + swColor4fv(cv); +} + +void swColor4ubv(const uint8_t *v) +{ + float cv[4]; + cv[0] = (float)v[0]/255; + cv[1] = (float)v[1]/255; + cv[2] = (float)v[2]/255; + cv[3] = (float)v[3]/255; + + swColor4fv(cv); +} + +void swColor4f(float r, float g, float b, float a) +{ + float cv[4]; + cv[0] = r; + cv[1] = g; + cv[2] = b; + cv[3] = a; + + swColor4fv(cv); +} + +void swColor4fv(const float *v) +{ + for (int i = 0; i < 4; i++) RLSW.current.color[i] = v[i]; +} + +void swTexCoord2f(float u, float v) +{ + const float *m = RLSW.stackTexture[RLSW.stackTextureCounter - 1]; + + RLSW.current.texcoord[0] = m[0]*u + m[4]*v + m[12]; + RLSW.current.texcoord[1] = m[1]*u + m[5]*v + m[13]; +} + +void swTexCoord2fv(const float *v) +{ + const float *m = RLSW.stackTexture[RLSW.stackTextureCounter - 1]; + + RLSW.current.texcoord[0] = m[0]*v[0] + m[4]*v[1] + m[12]; + RLSW.current.texcoord[1] = m[1]*v[0] + m[5]*v[1] + m[13]; +} + +void swBindArray(SWarray type, void *buffer) +{ + switch (type) + { + case SW_VERTEX_ARRAY: RLSW.array.positions = buffer; break; + case SW_TEXTURE_COORD_ARRAY: RLSW.array.texcoords = buffer; break; + case SW_COLOR_ARRAY: RLSW.array.colors = buffer; break; + default: break; + } +} + +void swDrawArrays(SWdraw mode, int offset, int count) +{ + if (RLSW.array.positions == 0) + { + RLSW.errCode = SW_INVALID_OPERATION; + return; + } + + swBegin(mode); + { + swTexCoord2f(0.0f, 0.0f); + swColor4f(1.0f, 1.0f, 1.0f, 1.0f); + + for (int i = offset; i < count; i++) + { + if (RLSW.array.texcoords) swTexCoord2fv(RLSW.array.texcoords + 2*i); + if (RLSW.array.colors) swColor4ubv(RLSW.array.colors + 4*i); + swVertex3fv(RLSW.array.positions + 3*i); + } + } + swEnd(); +} + +void swGenTextures(int count, uint32_t *textures) +{ + if ((count == 0) || (textures == NULL)) return; + + for (int i = 0; i < count; i++) + { + if (RLSW.loadedTextureCount >= SW_MAX_TEXTURES) + { + RLSW.errCode = SW_STACK_OVERFLOW; // WARNING: Out of memory, not really stack overflow + return; + } + + uint32_t id = 0; + if (RLSW.freeTextureIdCount > 0) id = RLSW.freeTextureIds[--RLSW.freeTextureIdCount]; + else id = RLSW.loadedTextureCount++; + + RLSW.loadedTextures[id] = RLSW.loadedTextures[0]; + textures[i] = id; + } +} + +void swDeleteTextures(int count, uint32_t *textures) +{ + if ((count == 0) || (textures == NULL)) return; + + for (int i = 0; i < count; i++) + { + if (!sw_is_texture_valid(textures[i])) + { + RLSW.errCode = SW_INVALID_VALUE; + continue; + } + + if (RLSW.loadedTextures[textures[i]].copy) + { + SW_FREE(RLSW.loadedTextures[textures[i]].pixels.ptr); + } + + RLSW.loadedTextures[textures[i]].pixels.cptr = NULL; + RLSW.freeTextureIds[RLSW.freeTextureIdCount++] = textures[i]; + } +} + +void swTexImage2D(int width, int height, SWformat format, SWtype type, bool copy, const void *data) +{ + uint32_t id = RLSW.currentTexture; + + if (!sw_is_texture_valid(id)) + { + RLSW.errCode = SW_INVALID_VALUE; + return; + } + + int pixelFormat = sw_get_pixel_format(format, type); + + if (pixelFormat <= SW_PIXELFORMAT_UNKNOWN) + { + RLSW.errCode = SW_INVALID_ENUM; + return; + } + + sw_texture_t *texture = &RLSW.loadedTextures[id]; + + if (copy) + { + int bytes = sw_get_pixel_bytes(pixelFormat); + int size = bytes*width*height; + texture->pixels.ptr = SW_MALLOC(size); + + if (texture->pixels.ptr == NULL) + { + RLSW.errCode = SW_STACK_OVERFLOW; // WARING: Out of memory... + return; + } + + for (int i = 0; i < size; i++) + { + ((uint8_t *)texture->pixels.ptr)[i] = ((uint8_t *)data)[i]; + } + } + else texture->pixels.cptr = data; + + texture->width = width; + texture->height = height; + texture->wMinus1 = width - 1; + texture->hMinus1 = height - 1; + texture->format = pixelFormat; + texture->tx = 1.0f/width; + texture->ty = 1.0f/height; + texture->copy = copy; +} + +void swTexParameteri(int param, int value) +{ + uint32_t id = RLSW.currentTexture; + + if (!sw_is_texture_valid(id)) + { + RLSW.errCode = SW_INVALID_VALUE; + return; + } + + sw_texture_t *texture = &RLSW.loadedTextures[id]; + + switch (param) + { + case SW_TEXTURE_MIN_FILTER: + { + if (!sw_is_texture_filter_valid(value)) + { + RLSW.errCode = SW_INVALID_ENUM; + return; + } + + texture->minFilter = value; + } break; + case SW_TEXTURE_MAG_FILTER: + { + if (!sw_is_texture_filter_valid(value)) + { + RLSW.errCode = SW_INVALID_ENUM; + return; + } + + texture->magFilter = value; + } break; + case SW_TEXTURE_WRAP_S: + { + if (!sw_is_texture_wrap_valid(value)) + { + RLSW.errCode = SW_INVALID_ENUM; + return; + } + + texture->sWrap = value; + } break; + case SW_TEXTURE_WRAP_T: + { + if (!sw_is_texture_wrap_valid(value)) + { + RLSW.errCode = SW_INVALID_ENUM; + return; + } + + texture->tWrap = value; + } break; + default: RLSW.errCode = SW_INVALID_ENUM; break; + } +} + +void swBindTexture(uint32_t id) +{ + if (id >= SW_MAX_TEXTURES) + { + RLSW.errCode = SW_INVALID_VALUE; + return; + } + + if (RLSW.loadedTextures[id].pixels.cptr == NULL) + { + RLSW.errCode = SW_INVALID_OPERATION; + return; + } + + RLSW.currentTexture = id; +} + +#endif // RLSW_IMPLEMENTATION diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index 1fefa4ac4..209b0594c 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -63,18 +63,20 @@ #include "SDL.h" #endif -#if defined(GRAPHICS_API_OPENGL_ES2) - // It seems it does not need to be included to work - //#include "SDL_opengles2.h" -#else - // SDL OpenGL functionality (if required, instead of internal renderer) - #ifdef USING_SDL3_PROJECT - #include "SDL3/SDL_opengl.h" - #elif USING_SDL2_PROJECT - #include "SDL2/SDL_opengl.h" - #else - #include "SDL_opengl.h" - #endif +#if !defined(GRAPHICS_API_OPENGL_11_SOFTWARE) + #if defined(GRAPHICS_API_OPENGL_ES2) + // It seems it does not need to be included to work + //#include "SDL_opengles2.h" + #else + // SDL OpenGL functionality (if required, instead of internal renderer) + #ifdef USING_SDL3_PROJECT + #include "SDL3/SDL_opengl.h" + #elif USING_SDL2_PROJECT + #include "SDL2/SDL_opengl.h" + #else + #include "SDL_opengl.h" + #endif + #endif #endif //---------------------------------------------------------------------------------- @@ -1220,7 +1222,14 @@ void DisableCursor(void) // Swap back buffer with front buffer (screen drawing) void SwapScreenBuffer(void) { +#if defined(GRAPHICS_API_OPENGL_11_SOFTWARE) + // NOTE: We use a preprocessor condition here because `rlCopyFramebuffer` is only declared for software rendering + SDL_Surface *surface = SDL_GetWindowSurface(platform.window); + rlCopyFramebuffer(0, 0, CORE.Window.render.width, CORE.Window.render.height, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, surface->pixels); + SDL_UpdateWindowSurface(platform.window); +#else SDL_GL_SwapWindow(platform.window); +#endif } //---------------------------------------------------------------------------------- @@ -1568,13 +1577,15 @@ void PollInputEvents(void) if (CORE.Input.Keyboard.charPressedQueueCount < MAX_CHAR_PRESSED_QUEUE) { // Add character (codepoint) to the queue - #if defined(USING_VERSION_SDL3) + + #if defined(USING_VERSION_SDL3) size_t textLen = strlen(event.text.text); unsigned int codepoint = (unsigned int)SDL_StepUTF8(&event.text.text, &textLen); - #else + #else int codepointSize = 0; int codepoint = GetCodepointNextSDL(event.text.text, &codepointSize); - #endif + #endif + CORE.Input.Keyboard.charPressedQueue[CORE.Input.Keyboard.charPressedQueueCount] = codepoint; CORE.Input.Keyboard.charPressedQueueCount++; } @@ -1878,7 +1889,6 @@ int InitPlatform(void) //---------------------------------------------------------------------------- unsigned int flags = 0; FLAG_SET(flags, SDL_WINDOW_SHOWN); - FLAG_SET(flags, SDL_WINDOW_OPENGL); FLAG_SET(flags, SDL_WINDOW_INPUT_FOCUS); FLAG_SET(flags, SDL_WINDOW_MOUSE_FOCUS); FLAG_SET(flags, SDL_WINDOW_MOUSE_CAPTURE); // Window has mouse captured @@ -1909,44 +1919,50 @@ int InitPlatform(void) // NOTE: Some OpenGL context attributes must be set before window creation - // Check selection OpenGL version - if (rlGetVersion() == RL_OPENGL_21) + if (rlGetVersion() != RL_OPENGL_11_SOFTWARE) { - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); - } - else if (rlGetVersion() == RL_OPENGL_33) - { - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); - } - else if (rlGetVersion() == RL_OPENGL_43) - { - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); -#if defined(RLGL_ENABLE_OPENGL_DEBUG_CONTEXT) - SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG); // Enable OpenGL Debug Context -#endif - } - else if (rlGetVersion() == RL_OPENGL_ES_20) // Request OpenGL ES 2.0 context - { - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); - } - else if (rlGetVersion() == RL_OPENGL_ES_30) // Request OpenGL ES 3.0 context - { - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); - } + // Add the flag telling the window to use an OpenGL context + flags |= SDL_WINDOW_OPENGL; - if (FLAG_CHECK(CORE.Window.flags, FLAG_MSAA_4X_HINT) > 0) - { - SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1); - SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4); + // Check selection OpenGL version + if (rlGetVersion() == RL_OPENGL_21) + { + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); + } + else if (rlGetVersion() == RL_OPENGL_33) + { + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); + } + else if (rlGetVersion() == RL_OPENGL_43) + { + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); + #if defined(RLGL_ENABLE_OPENGL_DEBUG_CONTEXT) + SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG); // Enable OpenGL Debug Context + #endif + } + else if (rlGetVersion() == RL_OPENGL_ES_20) // Request OpenGL ES 2.0 context + { + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); + } + else if (rlGetVersion() == RL_OPENGL_ES_30) // Request OpenGL ES 3.0 context + { + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); + } + + if (FLAG_CHECK(CORE.Window.flags, FLAG_MSAA_4X_HINT) > 0) + { + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1); + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4); + } } // Init window @@ -1957,10 +1973,12 @@ int InitPlatform(void) #endif // Init OpenGL context - platform.glContext = SDL_GL_CreateContext(platform.window); + if (rlGetVersion() != RL_OPENGL_11_SOFTWARE) + { + platform.glContext = SDL_GL_CreateContext(platform.window); + } - // Check window and glContext have been initialized successfully - if ((platform.window != NULL) && (platform.glContext != NULL)) + if ((platform.window != NULL) && ((rlGetVersion() == RL_OPENGL_11_SOFTWARE) || (platform.glContext != NULL))) { CORE.Window.ready = true; @@ -1981,8 +1999,14 @@ int InitPlatform(void) TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y); - if (FLAG_CHECK(CORE.Window.flags, FLAG_VSYNC_HINT) > 0) SDL_GL_SetSwapInterval(1); - else SDL_GL_SetSwapInterval(0); + if (platform.glContext != NULL) + { + SDL_GL_SetSwapInterval((FLAG_CHECK(CORE.Window.flags, FLAG_VSYNC_HINT) > 0)? 1 : 0); + + // Load OpenGL extensions + // NOTE: GL procedures address loader is required to load extensions + rlLoadExtensions(SDL_GL_GetProcAddress); + } } else { @@ -1990,9 +2014,6 @@ int InitPlatform(void) return -1; } - // Load OpenGL extensions - // NOTE: GL procedures address loader is required to load extensions - rlLoadExtensions(SDL_GL_GetProcAddress); //---------------------------------------------------------------------------- // Initialize input events system @@ -2064,7 +2085,7 @@ int InitPlatform(void) void ClosePlatform(void) { SDL_FreeCursor(platform.cursor); // Free cursor - SDL_GL_DeleteContext(platform.glContext); // Deinitialize OpenGL context + if (platform.glContext != NULL) SDL_GL_DeleteContext(platform.glContext); // Deinitialize OpenGL context SDL_DestroyWindow(platform.window); SDL_Quit(); // Deinitialize SDL internal global state } diff --git a/src/platforms/rcore_drm.c b/src/platforms/rcore_drm.c index a58911d08..b9910d90d 100644 --- a/src/platforms/rcore_drm.c +++ b/src/platforms/rcore_drm.c @@ -65,12 +65,17 @@ // so the enum KEY_F12 from raylib is used #undef KEY_F12 -#include // Generic Buffer Management (native platform for EGL on DRM) #include // Direct Rendering Manager user-level library interface #include // Direct Rendering Manager mode setting (KMS) interface -#include "EGL/egl.h" // Native platform windowing system interface -#include "EGL/eglext.h" // EGL extensions +#if !defined(GRAPHICS_API_OPENGL_11_SOFTWARE) + #include // Generic Buffer Management (native platform for EGL on DRM) + #include "EGL/egl.h" // Native platform windowing system interface + #include "EGL/eglext.h" // EGL extensions +#else + #include // For mmap when copying to the dumb buffer + #include // For the conversion of certain error messages +#endif // NOTE: DRM cache enables triple buffered DRM caching #if defined(SUPPORT_DRM_CACHE) @@ -103,15 +108,19 @@ typedef struct { drmModeConnector *connector; // Direct Rendering Manager (DRM) mode connector drmModeCrtc *crtc; // CRT Controller int modeIndex; // Index of the used mode of connector->modes + uint32_t prevFB; // Previous DRM framebufer (during frame swapping) + +#if !defined(GRAPHICS_API_OPENGL_11_SOFTWARE) struct gbm_device *gbmDevice; // GBM device struct gbm_surface *gbmSurface; // GBM surface struct gbm_bo *prevBO; // Previous GBM buffer object (during frame swapping) - uint32_t prevFB; // Previous GBM framebufer (during frame swapping) - EGLDisplay device; // Native display device (physical screen connection) EGLSurface surface; // Surface to draw on, framebuffers (connected to context) EGLContext context; // Graphic context, mode in which drawing can be done EGLConfig config; // Graphic config +#else + uint32_t prevDumbHandle; // Handle to the previous dumb buffer (during frame swapping) +#endif // Keyboard data int defaultKeyboardMode; // Default keyboard mode @@ -780,6 +789,8 @@ void SwapScreenBuffer() // Swap back buffer with front buffer (screen drawing) void SwapScreenBuffer(void) { +#if !defined(GRAPHICS_API_OPENGL_11_SOFTWARE) + // Hardware rendering buffer swap with EGL eglSwapBuffers(platform.device, platform.surface); if (!platform.gbmSurface || (-1 == platform.fd) || !platform.connector || !platform.crtc) TRACELOG(LOG_ERROR, "DISPLAY: DRM initialization failed to swap"); @@ -805,6 +816,196 @@ void SwapScreenBuffer(void) if (platform.prevBO) gbm_surface_release_buffer(platform.gbmSurface, platform.prevBO); platform.prevBO = bo; +#else + // Software rendering buffer swap + if ((platform.fd == -1) || !platform.connector || (platform.modeIndex < 0)) + { + TRACELOG(LOG_ERROR, "DISPLAY: DRM initialization failed to swap"); + return; + } + + // Get the software rendered color buffer + int bufferWidth = 0, bufferHeight = 0; + void *colorBuffer = swGetColorBuffer(&bufferWidth, &bufferHeight); + if (!colorBuffer) + { + TRACELOG(LOG_ERROR, "DISPLAY: Failed to get software color buffer"); + return; + } + + // Retrieving the dimensions of the display mode used + drmModeModeInfo *mode = &platform.connector->modes[platform.modeIndex]; + uint32_t width = mode->hdisplay; + uint32_t height = mode->vdisplay; + + // Dumb buffers use a fixed format based on bpp +#if SW_COLOR_BUFFER_BITS == 24 + const uint32_t bpp = 32; // 32 bits per pixel (XRGB8888 format) + const uint32_t depth = 24; // Color depth, here only 24 bits, alpha is not used +#else + // REVIEW: Not sure how it will be interpreted (RGB or RGBA?) + const uint32_t bpp = SW_COLOR_BUFFER_BITS; + const uint32_t depth = SW_COLOR_BUFFER_BITS; +#endif + + // Create a dumb buffer for software rendering + struct drm_mode_create_dumb creq = { 0 }; + creq.width = width; + creq.height = height; + creq.bpp = bpp; + + int result = drmIoctl(platform.fd, DRM_IOCTL_MODE_CREATE_DUMB, &creq); + if (result < 0) + { + TRACELOG(LOG_ERROR, "DISPLAY: Failed to create dumb buffer: %s", strerror(errno)); + return; + } + + // Create framebuffer with the correct format + uint32_t fb = 0; + result = drmModeAddFB(platform.fd, width, height, depth, bpp, creq.pitch, creq.handle, &fb); + if (result != 0) + { + TRACELOG(LOG_ERROR, "DISPLAY: drmModeAddFB() failed with result: %d (%s)", result, strerror(errno)); + struct drm_mode_destroy_dumb dreq = { 0 }; + dreq.handle = creq.handle; + drmIoctl(platform.fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dreq); + return; + } + + // Map the dumb buffer to copy our software rendered buffer + struct drm_mode_map_dumb mreq = { 0 }; + mreq.handle = creq.handle; + result = drmIoctl(platform.fd, DRM_IOCTL_MODE_MAP_DUMB, &mreq); + if (result != 0) + { + TRACELOG(LOG_ERROR, "DISPLAY: Failed to map dumb buffer: %s", strerror(errno)); + drmModeRmFB(platform.fd, fb); + struct drm_mode_destroy_dumb dreq = { 0 }; + dreq.handle = creq.handle; + drmIoctl(platform.fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dreq); + return; + } + + // Map the buffer into userspace + void *dumbBuffer = mmap(0, creq.size, PROT_READ | PROT_WRITE, MAP_SHARED, platform.fd, mreq.offset); + if (dumbBuffer == MAP_FAILED) + { + TRACELOG(LOG_ERROR, "DISPLAY: Failed to mmap dumb buffer: %s", strerror(errno)); + drmModeRmFB(platform.fd, fb); + struct drm_mode_destroy_dumb dreq = { 0 }; + dreq.handle = creq.handle; + drmIoctl(platform.fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dreq); + return; + } + + // Copy the software rendered buffer to the dumb buffer with scaling if needed + if (bufferWidth == width && bufferHeight == height) + { + // Direct copy if sizes match + swCopyFramebuffer(0, 0, bufferWidth, bufferHeight, SW_RGBA, SW_UNSIGNED_BYTE, dumbBuffer); + } + else + { + // Scale the software buffer to match the display mode + swBlitFramebuffer(0, 0, width, height, 0, 0, bufferWidth, bufferHeight, SW_RGBA, SW_UNSIGNED_BYTE, dumbBuffer); + } + + // Unmap the buffer + munmap(dumbBuffer, creq.size); + + // Find a CRTC compatible with the connector + uint32_t crtcId = 0; + if (platform.crtc) crtcId = platform.crtc->crtc_id; + else + { + // Find a CRTC that's compatible with this connector + drmModeRes *res = drmModeGetResources(platform.fd); + if (!res) + { + TRACELOG(LOG_ERROR, "DISPLAY: Failed to get DRM resources"); + drmModeRmFB(platform.fd, fb); + struct drm_mode_destroy_dumb dreq = {0}; + dreq.handle = creq.handle; + drmIoctl(platform.fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dreq); + return; + } + + // Check which CRTCs are compatible with this connector + drmModeEncoder *encoder = NULL; + if (platform.connector->encoder_id) encoder = drmModeGetEncoder(platform.fd, platform.connector->encoder_id); + + if (encoder && encoder->crtc_id) + { + crtcId = encoder->crtc_id; + platform.crtc = drmModeGetCrtc(platform.fd, crtcId); + } + else + { + // Find a free CRTC + for (int i = 0; i < res->count_crtcs; i++) + { + drmModeCrtc *crtc = drmModeGetCrtc(platform.fd, res->crtcs[i]); + if (crtc && !crtc->buffer_id) // CRTC is free + { + crtcId = res->crtcs[i]; + if (platform.crtc) drmModeFreeCrtc(platform.crtc); + platform.crtc = crtc; + break; + } + + if (crtc) drmModeFreeCrtc(crtc); + } + } + + if (encoder) drmModeFreeEncoder(encoder); + drmModeFreeResources(res); + + if (!crtcId) + { + TRACELOG(LOG_ERROR, "DISPLAY: No compatible CRTC found"); + drmModeRmFB(platform.fd, fb); + struct drm_mode_destroy_dumb dreq = {0}; + dreq.handle = creq.handle; + drmIoctl(platform.fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dreq); + return; + } + } + + // Set CRTC with better error handling + result = drmModeSetCrtc(platform.fd, crtcId, fb, 0, 0, &platform.connector->connector_id, 1, mode); + if (result != 0) + { + TRACELOG(LOG_ERROR, "DISPLAY: drmModeSetCrtc() failed with result: %d (%s)", result, strerror(errno)); + TRACELOG(LOG_ERROR, "DISPLAY: CRTC ID: %u, FB ID: %u, Connector ID: %u", crtcId, fb, platform.connector->connector_id); + TRACELOG(LOG_ERROR, "DISPLAY: Mode: %dx%d@%d", mode->hdisplay, mode->vdisplay, mode->vrefresh); + + drmModeRmFB(platform.fd, fb); + struct drm_mode_destroy_dumb dreq = {0}; + dreq.handle = creq.handle; + drmIoctl(platform.fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dreq); + return; + } + + // Clean up previous framebuffer + if (platform.prevFB) + { + result = drmModeRmFB(platform.fd, platform.prevFB); + if (result != 0) TRACELOG(LOG_WARNING, "DISPLAY: drmModeRmFB() failed with result: %d", result); + } + + platform.prevFB = fb; + + // Clean up previous dumb buffer + if (platform.prevDumbHandle) + { + struct drm_mode_destroy_dumb dreq = {0}; + dreq.handle = platform.prevDumbHandle; + drmIoctl(platform.fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dreq); + } + + platform.prevDumbHandle = creq.handle; +#endif } #endif // SUPPORT_DRM_CACHE @@ -950,10 +1151,15 @@ int InitPlatform(void) platform.connector = NULL; platform.modeIndex = -1; platform.crtc = NULL; + platform.prevFB = 0; + +#if !defined(GRAPHICS_API_OPENGL_11_SOFTWARE) platform.gbmDevice = NULL; platform.gbmSurface = NULL; platform.prevBO = NULL; - platform.prevFB = 0; +#else + platform.prevDumbHandle = 0; +#endif // Initialize graphic device: display/window and graphic context //---------------------------------------------------------------------------- @@ -965,11 +1171,12 @@ int InitPlatform(void) if (platform.fd != -1) TRACELOG(LOG_INFO, "DISPLAY: Default graphic device DRM opened successfully"); #else TRACELOG(LOG_WARNING, "DISPLAY: No graphic card set, trying platform-gpu-card"); - platform.fd = open("/dev/dri/by-path/platform-gpu-card", O_RDWR); // VideoCore VI (Raspberry Pi 4) + platform.fd = open("/dev/dri/by-path/platform-gpu-card", O_RDWR); // VideoCore VI (Raspberry Pi 4) if (platform.fd != -1) TRACELOG(LOG_INFO, "DISPLAY: platform-gpu-card opened successfully"); if ((platform.fd == -1) || (drmModeGetResources(platform.fd) == NULL)) { + if (platform.fd != -1) close(platform.fd); TRACELOG(LOG_WARNING, "DISPLAY: Failed to open platform-gpu-card, trying card1"); platform.fd = open("/dev/dri/card1", O_RDWR); // Other Embedded if (platform.fd != -1) TRACELOG(LOG_INFO, "DISPLAY: card1 opened successfully"); @@ -977,10 +1184,19 @@ int InitPlatform(void) if ((platform.fd == -1) || (drmModeGetResources(platform.fd) == NULL)) { + if (platform.fd != -1) close(platform.fd); TRACELOG(LOG_WARNING, "DISPLAY: Failed to open graphic card1, trying card0"); platform.fd = open("/dev/dri/card0", O_RDWR); // VideoCore IV (Raspberry Pi 1-3) if (platform.fd != -1) TRACELOG(LOG_INFO, "DISPLAY: card0 opened successfully"); } + + if ((platform.fd == -1) || (drmModeGetResources(platform.fd) == NULL)) + { + if (platform.fd != -1) close(platform.fd); + TRACELOG(LOG_WARNING, "DISPLAY: Failed to open graphic card0, trying card2"); + platform.fd = open("/dev/dri/card2", O_RDWR); + if (platform.fd != -1) TRACELOG(LOG_INFO, "DISPLAY: card2 opened successfully"); + } #endif if (platform.fd == -1) @@ -993,29 +1209,55 @@ int InitPlatform(void) if (!res) { TRACELOG(LOG_WARNING, "DISPLAY: Failed get DRM resources"); + close(platform.fd); return -1; } TRACELOG(LOG_TRACE, "DISPLAY: Connectors found: %i", res->count_connectors); + // Connector detection for (size_t i = 0; i < res->count_connectors; i++) { TRACELOG(LOG_TRACE, "DISPLAY: Connector index %i", i); drmModeConnector *con = drmModeGetConnector(platform.fd, res->connectors[i]); - TRACELOG(LOG_TRACE, "DISPLAY: Connector modes detected: %i", con->count_modes); + if (!con) + { + TRACELOG(LOG_WARNING, "DISPLAY: Failed to get connector %i", i); + continue; + } + + TRACELOG(LOG_TRACE, "DISPLAY: Connector %i modes detected: %i", i, con->count_modes); + TRACELOG(LOG_TRACE, "DISPLAY: Connector %i status: %s", i, + (con->connection == DRM_MODE_CONNECTED) ? "CONNECTED" : + (con->connection == DRM_MODE_DISCONNECTED) ? "DISCONNECTED" : + (con->connection == DRM_MODE_UNKNOWNCONNECTION) ? "UNKNOWN" : "OTHER"); // In certain cases the status of the conneciton is reported as UKNOWN, but it is still connected // This might be a hardware or software limitation like on Raspberry Pi Zero with composite output - if (((con->connection == DRM_MODE_CONNECTED) || (con->connection == DRM_MODE_UNKNOWNCONNECTION)) && (con->encoder_id)) + // WARNING: Accept CONNECTED, UNKNOWN and even those without encoder_id connectors for software mode + if (((con->connection == DRM_MODE_CONNECTED) || (con->connection == DRM_MODE_UNKNOWNCONNECTION)) && (con->count_modes > 0)) { - TRACELOG(LOG_TRACE, "DISPLAY: DRM mode connected"); +#if !defined(GRAPHICS_API_OPENGL_11_SOFTWARE) + // For hardware rendering, we need an encoder_id + if (con->encoder_id) + { + TRACELOG(LOG_TRACE, "DISPLAY: DRM connector %i connected with encoder", i); + platform.connector = con; + break; + } + else TRACELOG(LOG_TRACE, "DISPLAY: DRM connector %i connected but no encoder", i); +#else + // For software rendering, we can accept even without encoder_id + TRACELOG(LOG_TRACE, "DISPLAY: DRM connector %i suitable for software rendering", i); platform.connector = con; break; +#endif } - else + + if (!platform.connector) { - TRACELOG(LOG_TRACE, "DISPLAY: DRM mode NOT connected (deleting)"); + TRACELOG(LOG_TRACE, "DISPLAY: DRM connector %i NOT suitable (deleting)", i); drmModeFreeConnector(con); } } @@ -1024,14 +1266,18 @@ int InitPlatform(void) { TRACELOG(LOG_WARNING, "DISPLAY: No suitable DRM connector found"); drmModeFreeResources(res); + close(platform.fd); return -1; } +#if !defined(GRAPHICS_API_OPENGL_11_SOFTWARE) drmModeEncoder *enc = drmModeGetEncoder(platform.fd, platform.connector->encoder_id); if (!enc) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to get DRM mode encoder"); + drmModeFreeConnector(platform.connector); drmModeFreeResources(res); + close(platform.fd); return -1; } @@ -1040,7 +1286,9 @@ int InitPlatform(void) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to get DRM mode crtc"); drmModeFreeEncoder(enc); + drmModeFreeConnector(platform.connector); drmModeFreeResources(res); + close(platform.fd); return -1; } @@ -1055,7 +1303,9 @@ int InitPlatform(void) { TRACELOG(LOG_WARNING, "DISPLAY: No matching DRM connector mode found"); drmModeFreeEncoder(enc); + drmModeFreeConnector(platform.connector); drmModeFreeResources(res); + close(platform.fd); return -1; } @@ -1064,7 +1314,7 @@ int InitPlatform(void) } const bool allowInterlaced = FLAG_CHECK(CORE.Window.flags. FLAG_INTERLACED_HINT); - const int fps = (CORE.Time.target > 0)? (1.0/CORE.Time.target) : 60; + const int fps = (CORE.Time.target > 0) ? (1.0/CORE.Time.target) : 60; // Try to find an exact matching mode platform.modeIndex = FindExactConnectorMode(platform.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, allowInterlaced); @@ -1083,7 +1333,9 @@ int InitPlatform(void) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to find a suitable DRM connector mode"); drmModeFreeEncoder(enc); + drmModeFreeConnector(platform.connector); drmModeFreeResources(res); + close(platform.fd); return -1; } @@ -1095,16 +1347,42 @@ int InitPlatform(void) FLAG_CHECK(platform.connector->modes[platform.modeIndex].flags, DRM_MODE_FLAG_INTERLACE)? 'i' : 'p', platform.connector->modes[platform.modeIndex].vrefresh); + drmModeFreeEncoder(enc); + enc = NULL; +#else + // For software rendering, the first available mode can be used + if (platform.connector->count_modes > 0) + { + platform.modeIndex = 0; + CORE.Window.display.width = platform.connector->modes[0].hdisplay; + CORE.Window.display.height = platform.connector->modes[0].vdisplay; + + TRACELOG(LOG_INFO, "DISPLAY: Selected DRM connector mode %s (%ux%u%c@%u) for software rendering", + platform.connector->modes[0].name, + platform.connector->modes[0].hdisplay, + platform.connector->modes[0].vdisplay, + (platform.connector->modes[0].flags & DRM_MODE_FLAG_INTERLACE) ? 'i' : 'p', + platform.connector->modes[0].vrefresh); + } + else + { + TRACELOG(LOG_WARNING, "DISPLAY: No modes available for connector"); + drmModeFreeConnector(platform.connector); + drmModeFreeResources(res); + close(platform.fd); + return -1; + } +#endif + // Use the width and height of the surface for render CORE.Window.render.width = CORE.Window.screen.width; CORE.Window.render.height = CORE.Window.screen.height; - drmModeFreeEncoder(enc); - enc = NULL; - drmModeFreeResources(res); res = NULL; +#if !defined(GRAPHICS_API_OPENGL_11_SOFTWARE) + // Hardware rendering initialization with EGL platform.gbmDevice = gbm_create_device(platform.fd); if (!platform.gbmDevice) { @@ -1273,6 +1551,32 @@ int InitPlatform(void) return -1; } + // Load OpenGL extensions + // NOTE: GL procedures address loader is required to load extensions + rlLoadExtensions(eglGetProcAddress); +#else + // At this point we need to manage render size vs screen size + // NOTE: This function use and modify global module variables: + // -> CORE.Window.screen.width/CORE.Window.screen.height + // -> CORE.Window.render.width/CORE.Window.render.height + // -> CORE.Window.screenScale + SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height); + + // Setup window ready state for software rendering + CORE.Window.ready = true; + + CORE.Window.render.width = CORE.Window.screen.width; + CORE.Window.render.height = CORE.Window.screen.height; + CORE.Window.currentFbo.width = CORE.Window.render.width; + CORE.Window.currentFbo.height = CORE.Window.render.height; + + TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully (Software Rendering)"); + TRACELOG(LOG_INFO, " > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height); + TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height); + TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); + TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y); +#endif + if (FLAG_CHECK(CORE.Window.flags, FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow(); // If graphic device is no properly initialized, we end program @@ -1285,12 +1589,8 @@ int InitPlatform(void) FLAG_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED); // true FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED); // false - // Load OpenGL extensions - // NOTE: GL procedures address loader is required to load extensions - rlLoadExtensions(eglGetProcAddress); //---------------------------------------------------------------------------- - - // Initialize timming system + // Initialize timing system //---------------------------------------------------------------------------- // NOTE: timming system must be initialized before the input events system InitTimer(); @@ -1336,6 +1636,7 @@ void ClosePlatform(void) platform.prevFB = 0; } +#if !defined(GRAPHICS_API_OPENGL_11_SOFTWARE) if (platform.prevBO) { gbm_surface_release_buffer(platform.gbmSurface, platform.prevBO); @@ -1353,6 +1654,7 @@ void ClosePlatform(void) gbm_device_destroy(platform.gbmDevice); platform.gbmDevice = NULL; } +#endif if (platform.crtc) { @@ -1374,6 +1676,7 @@ void ClosePlatform(void) platform.fd = -1; } +#if !defined(GRAPHICS_API_OPENGL_11_SOFTWARE) // Close surface, context and display if (platform.device != EGL_NO_DISPLAY) { @@ -1392,6 +1695,7 @@ void ClosePlatform(void) eglTerminate(platform.device); platform.device = EGL_NO_DISPLAY; } +#endif CORE.Window.shouldClose = true; // Added to force threads to exit when the close window is called diff --git a/src/raylib.h b/src/raylib.h index 642f9318c..b7ccaae22 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1258,6 +1258,7 @@ RLAPI Rectangle GetShapesTextureRectangle(void); // Get texture source re RLAPI void DrawPixel(int posX, int posY, Color color); // Draw a pixel using geometry [Can be slow, use with care] RLAPI void DrawPixelV(Vector2 position, Color color); // Draw a pixel using geometry (Vector version) [Can be slow, use with care] RLAPI void DrawLine(int startPosX, int startPosY, int endPosX, int endPosY, Color color); // Draw a line +RLAPI void DrawLineDashed(Vector2 startPos, Vector2 endPos, int dashSize, int whiteSpaceSize, Color color); // Draw a dashed line RLAPI void DrawLineV(Vector2 startPos, Vector2 endPos, Color color); // Draw a line (using gl lines) RLAPI void DrawLineEx(Vector2 startPos, Vector2 endPos, float thick, Color color); // Draw a line (using triangles/quads) RLAPI void DrawLineStrip(const Vector2 *points, int pointCount, Color color); // Draw lines sequence (using gl lines) diff --git a/src/rlgl.h b/src/rlgl.h index 7db05251d..ecd3795a9 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -149,7 +149,8 @@ #endif // Security check in case no GRAPHICS_API_OPENGL_* defined -#if !defined(GRAPHICS_API_OPENGL_11) && \ +#if !defined(GRAPHICS_API_OPENGL_11_SOFTWARE) && \ + !defined(GRAPHICS_API_OPENGL_11) && \ !defined(GRAPHICS_API_OPENGL_21) && \ !defined(GRAPHICS_API_OPENGL_33) && \ !defined(GRAPHICS_API_OPENGL_43) && \ @@ -159,7 +160,7 @@ #endif // Security check in case multiple GRAPHICS_API_OPENGL_* defined -#if defined(GRAPHICS_API_OPENGL_11) +#if defined(GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_11_SOFTWARE) #if defined(GRAPHICS_API_OPENGL_21) #undef GRAPHICS_API_OPENGL_21 #endif @@ -174,6 +175,11 @@ #endif #endif +// Software implementation uses OpenGL 1.1 functionality +#if defined(GRAPHICS_API_OPENGL_11_SOFTWARE) + #define GRAPHICS_API_OPENGL_11 +#endif + // OpenGL 2.1 uses most of OpenGL 3.3 Core functionality // WARNING: Specific parts are checked with #if defines #if defined(GRAPHICS_API_OPENGL_21) @@ -427,7 +433,8 @@ typedef struct rlRenderBatch { // OpenGL version typedef enum { - RL_OPENGL_11 = 1, // OpenGL 1.1 + RL_OPENGL_11_SOFTWARE = 0, // Software rendering + RL_OPENGL_11, // OpenGL 1.1 RL_OPENGL_21, // OpenGL 2.1 (GLSL 120) RL_OPENGL_33, // OpenGL 3.3 (GLSL 330) RL_OPENGL_43, // OpenGL 4.3 (using GLSL 330) @@ -644,10 +651,8 @@ RLAPI void rlEnableVertexBufferElement(unsigned int id); // Enable vertex buffer RLAPI void rlDisableVertexBufferElement(void); // Disable vertex buffer element (VBO element) RLAPI void rlEnableVertexAttribute(unsigned int index); // Enable vertex attribute index RLAPI void rlDisableVertexAttribute(unsigned int index); // Disable vertex attribute index -#if defined(GRAPHICS_API_OPENGL_11) RLAPI void rlEnableStatePointer(int vertexAttribType, void *buffer); // Enable attribute state pointer RLAPI void rlDisableStatePointer(int vertexAttribType); // Disable attribute state pointer -#endif // Textures state RLAPI void rlActiveTextureSlot(int slot); // Select and active a texture slot @@ -686,6 +691,8 @@ RLAPI void rlDisableScissorTest(void); // Disable scissor test RLAPI void rlScissor(int x, int y, int width, int height); // Scissor test RLAPI void rlEnablePointMode(void); // Enable point mode RLAPI void rlDisablePointMode(void); // Disable point mode +RLAPI void rlSetPointSize(float size); // Set the point drawing size +RLAPI float rlGetPointSize(void); // Get the point drawing size RLAPI void rlEnableWireMode(void); // Enable wire mode RLAPI void rlDisableWireMode(void); // Disable wire mode RLAPI void rlSetLineWidth(float width); // Set the line drawing width @@ -768,6 +775,10 @@ RLAPI unsigned int rlLoadFramebuffer(void); // Loa RLAPI void rlFramebufferAttach(unsigned int fboId, unsigned int texId, int attachType, int texType, int mipLevel); // Attach texture/renderbuffer to a framebuffer RLAPI bool rlFramebufferComplete(unsigned int id); // Verify framebuffer is complete RLAPI void rlUnloadFramebuffer(unsigned int id); // Delete framebuffer from GPU +#if defined(GRAPHICS_API_OPENGL_11_SOFTWARE) +RLAPI void rlCopyFramebuffer(int x, int y, int width, int height, int format, void *pixels); // Copy framebuffer pixel data to internal buffer +RLAPI void rlResizeFramebuffer(int width, int height); // Resize internal framebuffer +#endif // Shaders management RLAPI unsigned int rlLoadShaderCode(const char *vsCode, const char *fsCode); // Load shader from code strings @@ -834,24 +845,32 @@ RLAPI void rlLoadDrawQuad(void); // Load and draw a quad #endif #if defined(GRAPHICS_API_OPENGL_11) - #if defined(__APPLE__) - #include // OpenGL 1.1 library for OSX - #include // OpenGL extensions library + #if defined(GRAPHICS_API_OPENGL_11_SOFTWARE) + #define RLSW_IMPLEMENTATION + #define SW_MALLOC(sz) RL_MALLOC(sz) + #define SW_REALLOC(ptr, newSz) RL_REALLOC(ptr, newSz) + #define SW_FREE(ptr) RL_FREE(ptr) + #include "external/rlsw.h" // OpenGL 1.1 software implementation #else - // APIENTRY for OpenGL function pointer declarations is required - #if !defined(APIENTRY) - #if defined(_WIN32) - #define APIENTRY __stdcall - #else - #define APIENTRY + #if defined(__APPLE__) + #include // OpenGL 1.1 library for OSX + #include // OpenGL extensions library + #else + // APIENTRY for OpenGL function pointer declarations is required + #if !defined(APIENTRY) + #if defined(_WIN32) + #define APIENTRY __stdcall + #else + #define APIENTRY + #endif + #endif + // WINGDIAPI definition. Some Windows OpenGL headers need it + #if !defined(WINGDIAPI) && defined(_WIN32) + #define WINGDIAPI __declspec(dllimport) #endif - #endif - // WINGDIAPI definition. Some Windows OpenGL headers need it - #if !defined(WINGDIAPI) && defined(_WIN32) - #define WINGDIAPI __declspec(dllimport) - #endif - #include // OpenGL 1.1 library + #include // OpenGL 1.1 library + #endif #endif #endif @@ -2016,6 +2035,25 @@ float rlGetLineWidth(void) return width; } +// Set the point drawing size +void rlSetPointSize(float size) +{ +#if defined(GRAPHICS_API_OPENGL_11) + glPointSize(size); +#endif +} + +// Get the point drawing size +float rlGetPointSize(void) +{ + float size = 1; +#if defined(GRAPHICS_API_OPENGL_11) + glGetFloatv(GL_POINT_SIZE, &size); +#endif + return size; + +} + // Enable line aliasing void rlEnableSmoothLines(void) { @@ -2318,6 +2356,15 @@ void rlglInit(int width, int height) glShadeModel(GL_SMOOTH); // Smooth shading between vertex (vertex colors interpolation) #endif +#if defined(GRAPHICS_API_OPENGL_11_SOFTWARE) + int result = swInit(width, height); // Initialize software renderer backend + if (result == 0) + { + TRACELOG(RL_LOG_ERROR, "RLSW: Software renderer initialization failed!"); + exit(-1); + } +#endif + #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) // Store screen size into global variables RLGL.State.framebufferWidth = width; @@ -2339,11 +2386,15 @@ void rlglClose(void) #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) rlUnloadRenderBatch(RLGL.defaultBatch); - rlUnloadShaderDefault(); // Unload default shader + rlUnloadShaderDefault(); // Unload default shader glDeleteTextures(1, &RLGL.State.defaultTextureId); // Unload default texture TRACELOG(RL_LOG_INFO, "TEXTURE: [ID %i] Default texture unloaded successfully", RLGL.State.defaultTextureId); #endif + +#if defined(GRAPHICS_API_OPENGL_11_SOFTWARE) + swClose(); // Unload sofware renderer resources +#endif } // Load OpenGL extensions @@ -2653,7 +2704,10 @@ void *rlGetProcAddress(const char *procName) int rlGetVersion(void) { int glVersion = 0; -#if defined(GRAPHICS_API_OPENGL_11) + +#if defined(GRAPHICS_API_OPENGL_11_SOFTWARE) + glVersion = RL_OPENGL_11_SOFTWARE; +#elif defined(GRAPHICS_API_OPENGL_11) glVersion = RL_OPENGL_11; #endif #if defined(GRAPHICS_API_OPENGL_21) @@ -3694,6 +3748,22 @@ void *rlReadTexturePixels(unsigned int id, int width, int height, int format) return pixels; } +#if defined(GRAPHICS_API_OPENGL_11_SOFTWARE) +// Copy framebuffer pixel data to internal buffer +void rlCopyFramebuffer(int x, int y, int width, int height, int format, void* pixels) +{ + unsigned int glInternalFormat, glFormat, glType; + rlGetGlTextureFormats(format, &glInternalFormat, &glFormat, &glType); // Get OpenGL texture format + swCopyFramebuffer(x, y, width, height, glFormat, glType, pixels); +} + +// Resize internal framebuffer +void rlResizeFramebuffer(int width, int height) +{ + swResizeFramebuffer(width, height); +} +#endif + // Read screen pixel data (color buffer) unsigned char *rlReadScreenPixels(int width, int height) { @@ -4003,10 +4073,10 @@ void rlDrawVertexArrayElementsInstanced(int offset, int count, const void *buffe #endif } -#if defined(GRAPHICS_API_OPENGL_11) // Enable vertex state pointer void rlEnableStatePointer(int vertexAttribType, void *buffer) { +#if defined(GRAPHICS_API_OPENGL_11) if (buffer != NULL) glEnableClientState(vertexAttribType); switch (vertexAttribType) { @@ -4017,14 +4087,16 @@ void rlEnableStatePointer(int vertexAttribType, void *buffer) //case GL_INDEX_ARRAY: if (buffer != NULL) glIndexPointer(GL_SHORT, 0, buffer); break; // Indexed colors default: break; } +#endif } // Disable vertex state pointer void rlDisableStatePointer(int vertexAttribType) { +#if defined(GRAPHICS_API_OPENGL_11) glDisableClientState(vertexAttribType); -} #endif +} // Load vertex array object (VAO) unsigned int rlLoadVertexArray(void) @@ -5292,4 +5364,4 @@ static Matrix rlMatrixInvert(Matrix mat) } #endif -#endif // RLGL_IMPLEMENTATION +#endif // RLGL_IMPLEMENTATION \ No newline at end of file diff --git a/src/rmodels.c b/src/rmodels.c index 9aca37a24..9ded580c3 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -1293,10 +1293,19 @@ void UploadMesh(Mesh *mesh, bool dynamic) rlEnableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION); // Enable vertex attributes: texcoords (shader-location = 1) - mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD] = rlLoadVertexBuffer(mesh->texcoords, mesh->vertexCount*2*sizeof(float), dynamic); - rlSetVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD, 2, RL_FLOAT, 0, 0, 0); - rlEnableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD); + if (mesh->texcoords != NULL) + { + mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD] = rlLoadVertexBuffer(mesh->texcoords, mesh->vertexCount*2*sizeof(float), dynamic); + rlSetVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD, 2, RL_FLOAT, 0, 0, 0); + rlEnableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD); + } + else + { + float value[2] = { 0.0f, 0.0f }; + rlSetVertexAttributeDefault(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD, value, SHADER_ATTRIB_VEC2, 2); + rlDisableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD); + } // WARNING: When setting default vertex attribute values, the values for each generic vertex attribute // is part of current state, and it is maintained even if a different program object is used @@ -1420,7 +1429,7 @@ void UpdateMeshBuffer(Mesh mesh, int index, const void *data, int dataSize, int // Draw a 3d mesh with material and transform void DrawMesh(Mesh mesh, Material material, Matrix transform) { -#if defined(GRAPHICS_API_OPENGL_11) +#if defined(GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_11_SOFTWARE) #define GL_VERTEX_ARRAY 0x8074 #define GL_NORMAL_ARRAY 0x8075 #define GL_COLOR_ARRAY 0x8076 @@ -1431,12 +1440,12 @@ void DrawMesh(Mesh mesh, Material material, Matrix transform) if (mesh.animVertices) rlEnableStatePointer(GL_VERTEX_ARRAY, mesh.animVertices); else rlEnableStatePointer(GL_VERTEX_ARRAY, mesh.vertices); - rlEnableStatePointer(GL_TEXTURE_COORD_ARRAY, mesh.texcoords); + if (mesh.texcoords) rlEnableStatePointer(GL_TEXTURE_COORD_ARRAY, mesh.texcoords); if (mesh.animNormals) rlEnableStatePointer(GL_NORMAL_ARRAY, mesh.animNormals); - else rlEnableStatePointer(GL_NORMAL_ARRAY, mesh.normals); + else if (mesh.normals) rlEnableStatePointer(GL_NORMAL_ARRAY, mesh.normals); - rlEnableStatePointer(GL_COLOR_ARRAY, mesh.colors); + if (mesh.colors) rlEnableStatePointer(GL_COLOR_ARRAY, mesh.colors); rlPushMatrix(); rlMultMatrixf(MatrixToFloat(transform)); @@ -3836,6 +3845,7 @@ void DrawModelWiresEx(Model model, Vector3 position, Vector3 rotationAxis, float // Draw a model points // WARNING: OpenGL ES 2.0 does not support point mode drawing +// TODO: gate these properly for non es 2.0 versions only void DrawModelPoints(Model model, Vector3 position, float scale, Color tint) { rlEnablePointMode(); @@ -4420,7 +4430,11 @@ static Model LoadOBJ(const char *fileName) model.meshes[i].vertices = (float *)MemAlloc(sizeof(float)*vertexCount*3); model.meshes[i].normals = (float *)MemAlloc(sizeof(float)*vertexCount*3); model.meshes[i].texcoords = (float *)MemAlloc(sizeof(float)*vertexCount*2); + #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) model.meshes[i].colors = (unsigned char *)MemAlloc(sizeof(unsigned char)*vertexCount*4); + #else + model.meshes[i].colors = NULL; + #endif } MemFree(localMeshVertexCounts); @@ -4474,8 +4488,18 @@ static Model LoadOBJ(const char *fileName) for (int i = 0; i < 3; i++) model.meshes[meshIndex].vertices[localMeshVertexCount*3 + i] = objAttributes.vertices[vertIndex*3 + i]; - for (int i = 0; i < 2; i++) model.meshes[meshIndex].texcoords[localMeshVertexCount*2 + i] = objAttributes.texcoords[texcordIndex*2 + i]; - if (objAttributes.normals != NULL && normalIndex != TINYOBJ_INVALID_INDEX && normalIndex >= 0) + if ((objAttributes.texcoords != NULL) && (texcordIndex != TINYOBJ_INVALID_INDEX) && (texcordIndex >= 0)) + { + for (int i = 0; i < 2; i++) model.meshes[meshIndex].texcoords[localMeshVertexCount*2 + i] = objAttributes.texcoords[texcordIndex*2 + i]; + model.meshes[meshIndex].texcoords[localMeshVertexCount*2 + 1] = 1.0f - model.meshes[meshIndex].texcoords[localMeshVertexCount*2 + 1]; + } + else + { + model.meshes[meshIndex].texcoords[localMeshVertexCount*2 + 0] = 0.0f; + model.meshes[meshIndex].texcoords[localMeshVertexCount*2 + 1] = 0.0f; + } + + if ((objAttributes.normals != NULL) && (normalIndex != TINYOBJ_INVALID_INDEX) && (normalIndex >= 0)) { for (int i = 0; i < 3; i++) model.meshes[meshIndex].normals[localMeshVertexCount*3 + i] = objAttributes.normals[normalIndex*3 + i]; } @@ -4485,11 +4509,9 @@ static Model LoadOBJ(const char *fileName) model.meshes[meshIndex].normals[localMeshVertexCount*3 + 1] = 1.0f; model.meshes[meshIndex].normals[localMeshVertexCount*3 + 2] = 0.0f; } - - model.meshes[meshIndex].texcoords[localMeshVertexCount*2 + 1] = 1.0f - model.meshes[meshIndex].texcoords[localMeshVertexCount*2 + 1]; - + #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) for (int i = 0; i < 4; i++) model.meshes[meshIndex].colors[localMeshVertexCount*4 + i] = 255; - + #endif faceVertIndex++; localMeshVertexCount++; } diff --git a/src/rshapes.c b/src/rshapes.c index 14e3f856a..a62b1c5f8 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -182,6 +182,55 @@ void DrawLine(int startPosX, int startPosY, int endPosX, int endPosY, Color colo rlEnd(); } +void DrawLineDashed(Vector2 startPos, Vector2 endPos, int dashSize, int whiteSpaceSize, Color color) +{ + // Calculate the vector and length of the line + float dx = endPos.x - startPos.x; + float dy = endPos.y - startPos.y; + float lineLength = sqrtf(dx*dx + dy*dy); + + // If the line is too short for dashing or dash size is invalid, draw a solid thick line + if (lineLength < (dashSize + whiteSpaceSize) || dashSize <= 0) + { + DrawLineV(startPos, endPos, color); + return; + } + + // Calculate the normalized direction vector of the line + float invLineLength = 1 / lineLength; + float dirX = dx * invLineLength; + float dirY = dy * invLineLength; + + Vector2 currentPos = startPos; + float distanceTraveled = 0; + + rlBegin(RL_LINES); + rlColor4ub(color.r, color.g, color.b, color.a); + + while (distanceTraveled < lineLength) + { + // Calculate the end of the current dash + float dashEndDist = distanceTraveled + dashSize; + if (dashEndDist > lineLength) + { + dashEndDist = lineLength; + } + + Vector2 dashEndPos = { startPos.x + dashEndDist * dirX, startPos.y + dashEndDist * dirY }; + + // Draw the dash segment + rlVertex2f(currentPos.x, currentPos.y); + rlVertex2f(dashEndPos.x, dashEndPos.y); + + // Update the distance traveled and move the current position for the next dash + distanceTraveled = dashEndDist + whiteSpaceSize; + currentPos.x = startPos.x + distanceTraveled * dirX; + currentPos.y = startPos.y + distanceTraveled * dirY; + } + + rlEnd(); +} + // Draw a line (using gl lines) void DrawLineV(Vector2 startPos, Vector2 endPos, Color color) { diff --git a/src/rtext.c b/src/rtext.c index edfe33c8b..ce9f381c4 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -1452,7 +1452,7 @@ Rectangle GetGlyphAtlasRec(Font font, int codepoint) char **LoadTextLines(const char *text, int *count) { int lineCount = 1; - int textSize = strlen(text); + int textSize = (int)strlen(text); // Text pass to get required line count for (int i = 0; i < textSize; i++) @@ -1679,7 +1679,7 @@ char *GetTextBetween(const char *text, const char *begin, const char *end) if (beginIndex > -1) { - int beginLen = strlen(begin); + int beginLen = (int)strlen(begin); int endIndex = TextFindIndex(text + beginIndex + beginLen, end); if (endIndex > -1) @@ -1758,15 +1758,15 @@ char *TextReplaceBetween(const char *text, const char *begin, const char *end, c if (beginIndex > -1) { - int beginLen = strlen(begin); + int beginLen = (int)strlen(begin); int endIndex = TextFindIndex(text + beginIndex + beginLen, end); if (endIndex > -1) { endIndex += (beginIndex + beginLen); - int textLen = strlen(text); - int replaceLen = (replacement == NULL)? 0 : strlen(replacement); + int textLen = (int)strlen(text); + int replaceLen = (replacement == NULL)? 0 : (int)strlen(replacement); int toreplaceLen = endIndex - beginIndex - beginLen; result = (char *)RL_CALLOC(textLen + replaceLen - toreplaceLen + 1, sizeof(char)); diff --git a/src/rtextures.c b/src/rtextures.c index 6366e4645..a85467d8c 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -811,8 +811,8 @@ Image GenImageColor(int width, int height, Color color) .data = pixels, .width = width, .height = height, - .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, - .mipmaps = 1 + .mipmaps = 1, + .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; return image; @@ -863,8 +863,8 @@ Image GenImageGradientLinear(int width, int height, int direction, Color start, .data = pixels, .width = width, .height = height, - .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, - .mipmaps = 1 + .mipmaps = 1, + .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; return image; @@ -900,8 +900,8 @@ Image GenImageGradientRadial(int width, int height, float density, Color inner, .data = pixels, .width = width, .height = height, - .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, - .mipmaps = 1 + .mipmaps = 1, + .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; return image; @@ -949,8 +949,8 @@ Image GenImageGradientSquare(int width, int height, float density, Color inner, .data = pixels, .width = width, .height = height, - .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, - .mipmaps = 1 + .mipmaps = 1, + .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; return image; @@ -974,8 +974,8 @@ Image GenImageChecked(int width, int height, int checksX, int checksY, Color col .data = pixels, .width = width, .height = height, - .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, - .mipmaps = 1 + .mipmaps = 1, + .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; return image; @@ -997,8 +997,8 @@ Image GenImageWhiteNoise(int width, int height, float factor) .data = pixels, .width = width, .height = height, - .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, - .mipmaps = 1 + .mipmaps = 1, + .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; return image; @@ -1048,8 +1048,8 @@ Image GenImagePerlinNoise(int width, int height, int offsetX, int offsetY, float .data = pixels, .width = width, .height = height, - .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, - .mipmaps = 1 + .mipmaps = 1, + .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; return image; @@ -1113,8 +1113,8 @@ Image GenImageCellular(int width, int height, int tileSize) .data = pixels, .width = width, .height = height, - .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, - .mipmaps = 1 + .mipmaps = 1, + .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; return image; diff --git a/tools/parser/output/raylib_api.json b/tools/parser/output/raylib_api.json index 5c641bac3..7b2ffdc9d 100644 --- a/tools/parser/output/raylib_api.json +++ b/tools/parser/output/raylib_api.json @@ -5508,6 +5508,33 @@ } ] }, + { + "name": "DrawLineDashed", + "description": "Draw a dashed line", + "returnType": "void", + "params": [ + { + "type": "Vector2", + "name": "startPos" + }, + { + "type": "Vector2", + "name": "endPos" + }, + { + "type": "int", + "name": "dashSize" + }, + { + "type": "int", + "name": "whiteSpaceSize" + }, + { + "type": "Color", + "name": "color" + } + ] + }, { "name": "DrawLineV", "description": "Draw a line (using gl lines)", diff --git a/tools/parser/output/raylib_api.lua b/tools/parser/output/raylib_api.lua index ce3671159..62dc9b25d 100644 --- a/tools/parser/output/raylib_api.lua +++ b/tools/parser/output/raylib_api.lua @@ -4758,6 +4758,18 @@ return { {type = "Color", name = "color"} } }, + { + name = "DrawLineDashed", + description = "Draw a dashed line", + returnType = "void", + params = { + {type = "Vector2", name = "startPos"}, + {type = "Vector2", name = "endPos"}, + {type = "int", name = "dashSize"}, + {type = "int", name = "whiteSpaceSize"}, + {type = "Color", name = "color"} + } + }, { name = "DrawLineV", description = "Draw a line (using gl lines)", diff --git a/tools/parser/output/raylib_api.txt b/tools/parser/output/raylib_api.txt index 94568b690..ea00fc74e 100644 --- a/tools/parser/output/raylib_api.txt +++ b/tools/parser/output/raylib_api.txt @@ -993,7 +993,7 @@ Callback 006: AudioCallback() (2 input parameters) Param[1]: bufferData (type: void *) Param[2]: frames (type: unsigned int) -Functions found: 595 +Functions found: 596 Function 001: InitWindow() (3 input parameters) Name: InitWindow @@ -2190,14 +2190,23 @@ Function 221: DrawLine() (5 input parameters) Param[3]: endPosX (type: int) Param[4]: endPosY (type: int) Param[5]: color (type: Color) -Function 222: DrawLineV() (3 input parameters) +Function 222: DrawLineDashed() (5 input parameters) + Name: DrawLineDashed + Return type: void + Description: Draw a dashed line + Param[1]: startPos (type: Vector2) + Param[2]: endPos (type: Vector2) + Param[3]: dashSize (type: int) + Param[4]: whiteSpaceSize (type: int) + Param[5]: color (type: Color) +Function 223: DrawLineV() (3 input parameters) Name: DrawLineV Return type: void Description: Draw a line (using gl lines) Param[1]: startPos (type: Vector2) Param[2]: endPos (type: Vector2) Param[3]: color (type: Color) -Function 223: DrawLineEx() (4 input parameters) +Function 224: DrawLineEx() (4 input parameters) Name: DrawLineEx Return type: void Description: Draw a line (using triangles/quads) @@ -2205,14 +2214,14 @@ Function 223: DrawLineEx() (4 input parameters) Param[2]: endPos (type: Vector2) Param[3]: thick (type: float) Param[4]: color (type: Color) -Function 224: DrawLineStrip() (3 input parameters) +Function 225: DrawLineStrip() (3 input parameters) Name: DrawLineStrip Return type: void Description: Draw lines sequence (using gl lines) Param[1]: points (type: const Vector2 *) Param[2]: pointCount (type: int) Param[3]: color (type: Color) -Function 225: DrawLineBezier() (4 input parameters) +Function 226: DrawLineBezier() (4 input parameters) Name: DrawLineBezier Return type: void Description: Draw line segment cubic-bezier in-out interpolation @@ -2220,7 +2229,7 @@ Function 225: DrawLineBezier() (4 input parameters) Param[2]: endPos (type: Vector2) Param[3]: thick (type: float) Param[4]: color (type: Color) -Function 226: DrawCircle() (4 input parameters) +Function 227: DrawCircle() (4 input parameters) Name: DrawCircle Return type: void Description: Draw a color-filled circle @@ -2228,7 +2237,7 @@ Function 226: DrawCircle() (4 input parameters) Param[2]: centerY (type: int) Param[3]: radius (type: float) Param[4]: color (type: Color) -Function 227: DrawCircleSector() (6 input parameters) +Function 228: DrawCircleSector() (6 input parameters) Name: DrawCircleSector Return type: void Description: Draw a piece of a circle @@ -2238,7 +2247,7 @@ Function 227: DrawCircleSector() (6 input parameters) Param[4]: endAngle (type: float) Param[5]: segments (type: int) Param[6]: color (type: Color) -Function 228: DrawCircleSectorLines() (6 input parameters) +Function 229: DrawCircleSectorLines() (6 input parameters) Name: DrawCircleSectorLines Return type: void Description: Draw circle sector outline @@ -2248,7 +2257,7 @@ Function 228: DrawCircleSectorLines() (6 input parameters) Param[4]: endAngle (type: float) Param[5]: segments (type: int) Param[6]: color (type: Color) -Function 229: DrawCircleGradient() (5 input parameters) +Function 230: DrawCircleGradient() (5 input parameters) Name: DrawCircleGradient Return type: void Description: Draw a gradient-filled circle @@ -2257,14 +2266,14 @@ Function 229: DrawCircleGradient() (5 input parameters) Param[3]: radius (type: float) Param[4]: inner (type: Color) Param[5]: outer (type: Color) -Function 230: DrawCircleV() (3 input parameters) +Function 231: DrawCircleV() (3 input parameters) Name: DrawCircleV Return type: void Description: Draw a color-filled circle (Vector version) Param[1]: center (type: Vector2) Param[2]: radius (type: float) Param[3]: color (type: Color) -Function 231: DrawCircleLines() (4 input parameters) +Function 232: DrawCircleLines() (4 input parameters) Name: DrawCircleLines Return type: void Description: Draw circle outline @@ -2272,14 +2281,14 @@ Function 231: DrawCircleLines() (4 input parameters) Param[2]: centerY (type: int) Param[3]: radius (type: float) Param[4]: color (type: Color) -Function 232: DrawCircleLinesV() (3 input parameters) +Function 233: DrawCircleLinesV() (3 input parameters) Name: DrawCircleLinesV Return type: void Description: Draw circle outline (Vector version) Param[1]: center (type: Vector2) Param[2]: radius (type: float) Param[3]: color (type: Color) -Function 233: DrawEllipse() (5 input parameters) +Function 234: DrawEllipse() (5 input parameters) Name: DrawEllipse Return type: void Description: Draw ellipse @@ -2288,7 +2297,7 @@ Function 233: DrawEllipse() (5 input parameters) Param[3]: radiusH (type: float) Param[4]: radiusV (type: float) Param[5]: color (type: Color) -Function 234: DrawEllipseV() (4 input parameters) +Function 235: DrawEllipseV() (4 input parameters) Name: DrawEllipseV Return type: void Description: Draw ellipse (Vector version) @@ -2296,7 +2305,7 @@ Function 234: DrawEllipseV() (4 input parameters) Param[2]: radiusH (type: float) Param[3]: radiusV (type: float) Param[4]: color (type: Color) -Function 235: DrawEllipseLines() (5 input parameters) +Function 236: DrawEllipseLines() (5 input parameters) Name: DrawEllipseLines Return type: void Description: Draw ellipse outline @@ -2305,7 +2314,7 @@ Function 235: DrawEllipseLines() (5 input parameters) Param[3]: radiusH (type: float) Param[4]: radiusV (type: float) Param[5]: color (type: Color) -Function 236: DrawEllipseLinesV() (4 input parameters) +Function 237: DrawEllipseLinesV() (4 input parameters) Name: DrawEllipseLinesV Return type: void Description: Draw ellipse outline (Vector version) @@ -2313,7 +2322,7 @@ Function 236: DrawEllipseLinesV() (4 input parameters) Param[2]: radiusH (type: float) Param[3]: radiusV (type: float) Param[4]: color (type: Color) -Function 237: DrawRing() (7 input parameters) +Function 238: DrawRing() (7 input parameters) Name: DrawRing Return type: void Description: Draw ring @@ -2324,7 +2333,7 @@ Function 237: DrawRing() (7 input parameters) Param[5]: endAngle (type: float) Param[6]: segments (type: int) Param[7]: color (type: Color) -Function 238: DrawRingLines() (7 input parameters) +Function 239: DrawRingLines() (7 input parameters) Name: DrawRingLines Return type: void Description: Draw ring outline @@ -2335,7 +2344,7 @@ Function 238: DrawRingLines() (7 input parameters) Param[5]: endAngle (type: float) Param[6]: segments (type: int) Param[7]: color (type: Color) -Function 239: DrawRectangle() (5 input parameters) +Function 240: DrawRectangle() (5 input parameters) Name: DrawRectangle Return type: void Description: Draw a color-filled rectangle @@ -2344,20 +2353,20 @@ Function 239: DrawRectangle() (5 input parameters) Param[3]: width (type: int) Param[4]: height (type: int) Param[5]: color (type: Color) -Function 240: DrawRectangleV() (3 input parameters) +Function 241: DrawRectangleV() (3 input parameters) Name: DrawRectangleV Return type: void Description: Draw a color-filled rectangle (Vector version) Param[1]: position (type: Vector2) Param[2]: size (type: Vector2) Param[3]: color (type: Color) -Function 241: DrawRectangleRec() (2 input parameters) +Function 242: DrawRectangleRec() (2 input parameters) Name: DrawRectangleRec Return type: void Description: Draw a color-filled rectangle Param[1]: rec (type: Rectangle) Param[2]: color (type: Color) -Function 242: DrawRectanglePro() (4 input parameters) +Function 243: DrawRectanglePro() (4 input parameters) Name: DrawRectanglePro Return type: void Description: Draw a color-filled rectangle with pro parameters @@ -2365,7 +2374,7 @@ Function 242: DrawRectanglePro() (4 input parameters) Param[2]: origin (type: Vector2) Param[3]: rotation (type: float) Param[4]: color (type: Color) -Function 243: DrawRectangleGradientV() (6 input parameters) +Function 244: DrawRectangleGradientV() (6 input parameters) Name: DrawRectangleGradientV Return type: void Description: Draw a vertical-gradient-filled rectangle @@ -2375,7 +2384,7 @@ Function 243: DrawRectangleGradientV() (6 input parameters) Param[4]: height (type: int) Param[5]: top (type: Color) Param[6]: bottom (type: Color) -Function 244: DrawRectangleGradientH() (6 input parameters) +Function 245: DrawRectangleGradientH() (6 input parameters) Name: DrawRectangleGradientH Return type: void Description: Draw a horizontal-gradient-filled rectangle @@ -2385,7 +2394,7 @@ Function 244: DrawRectangleGradientH() (6 input parameters) Param[4]: height (type: int) Param[5]: left (type: Color) Param[6]: right (type: Color) -Function 245: DrawRectangleGradientEx() (5 input parameters) +Function 246: DrawRectangleGradientEx() (5 input parameters) Name: DrawRectangleGradientEx Return type: void Description: Draw a gradient-filled rectangle with custom vertex colors @@ -2394,7 +2403,7 @@ Function 245: DrawRectangleGradientEx() (5 input parameters) Param[3]: bottomLeft (type: Color) Param[4]: bottomRight (type: Color) Param[5]: topRight (type: Color) -Function 246: DrawRectangleLines() (5 input parameters) +Function 247: DrawRectangleLines() (5 input parameters) Name: DrawRectangleLines Return type: void Description: Draw rectangle outline @@ -2403,14 +2412,14 @@ Function 246: DrawRectangleLines() (5 input parameters) Param[3]: width (type: int) Param[4]: height (type: int) Param[5]: color (type: Color) -Function 247: DrawRectangleLinesEx() (3 input parameters) +Function 248: DrawRectangleLinesEx() (3 input parameters) Name: DrawRectangleLinesEx Return type: void Description: Draw rectangle outline with extended parameters Param[1]: rec (type: Rectangle) Param[2]: lineThick (type: float) Param[3]: color (type: Color) -Function 248: DrawRectangleRounded() (4 input parameters) +Function 249: DrawRectangleRounded() (4 input parameters) Name: DrawRectangleRounded Return type: void Description: Draw rectangle with rounded edges @@ -2418,7 +2427,7 @@ Function 248: DrawRectangleRounded() (4 input parameters) Param[2]: roundness (type: float) Param[3]: segments (type: int) Param[4]: color (type: Color) -Function 249: DrawRectangleRoundedLines() (4 input parameters) +Function 250: DrawRectangleRoundedLines() (4 input parameters) Name: DrawRectangleRoundedLines Return type: void Description: Draw rectangle lines with rounded edges @@ -2426,7 +2435,7 @@ Function 249: DrawRectangleRoundedLines() (4 input parameters) Param[2]: roundness (type: float) Param[3]: segments (type: int) Param[4]: color (type: Color) -Function 250: DrawRectangleRoundedLinesEx() (5 input parameters) +Function 251: DrawRectangleRoundedLinesEx() (5 input parameters) Name: DrawRectangleRoundedLinesEx Return type: void Description: Draw rectangle with rounded edges outline @@ -2435,7 +2444,7 @@ Function 250: DrawRectangleRoundedLinesEx() (5 input parameters) Param[3]: segments (type: int) Param[4]: lineThick (type: float) Param[5]: color (type: Color) -Function 251: DrawTriangle() (4 input parameters) +Function 252: DrawTriangle() (4 input parameters) Name: DrawTriangle Return type: void Description: Draw a color-filled triangle (vertex in counter-clockwise order!) @@ -2443,7 +2452,7 @@ Function 251: DrawTriangle() (4 input parameters) Param[2]: v2 (type: Vector2) Param[3]: v3 (type: Vector2) Param[4]: color (type: Color) -Function 252: DrawTriangleLines() (4 input parameters) +Function 253: DrawTriangleLines() (4 input parameters) Name: DrawTriangleLines Return type: void Description: Draw triangle outline (vertex in counter-clockwise order!) @@ -2451,21 +2460,21 @@ Function 252: DrawTriangleLines() (4 input parameters) Param[2]: v2 (type: Vector2) Param[3]: v3 (type: Vector2) Param[4]: color (type: Color) -Function 253: DrawTriangleFan() (3 input parameters) +Function 254: DrawTriangleFan() (3 input parameters) Name: DrawTriangleFan Return type: void Description: Draw a triangle fan defined by points (first vertex is the center) Param[1]: points (type: const Vector2 *) Param[2]: pointCount (type: int) Param[3]: color (type: Color) -Function 254: DrawTriangleStrip() (3 input parameters) +Function 255: DrawTriangleStrip() (3 input parameters) Name: DrawTriangleStrip Return type: void Description: Draw a triangle strip defined by points Param[1]: points (type: const Vector2 *) Param[2]: pointCount (type: int) Param[3]: color (type: Color) -Function 255: DrawPoly() (5 input parameters) +Function 256: DrawPoly() (5 input parameters) Name: DrawPoly Return type: void Description: Draw a regular polygon (Vector version) @@ -2474,7 +2483,7 @@ Function 255: DrawPoly() (5 input parameters) Param[3]: radius (type: float) Param[4]: rotation (type: float) Param[5]: color (type: Color) -Function 256: DrawPolyLines() (5 input parameters) +Function 257: DrawPolyLines() (5 input parameters) Name: DrawPolyLines Return type: void Description: Draw a polygon outline of n sides @@ -2483,7 +2492,7 @@ Function 256: DrawPolyLines() (5 input parameters) Param[3]: radius (type: float) Param[4]: rotation (type: float) Param[5]: color (type: Color) -Function 257: DrawPolyLinesEx() (6 input parameters) +Function 258: DrawPolyLinesEx() (6 input parameters) Name: DrawPolyLinesEx Return type: void Description: Draw a polygon outline of n sides with extended parameters @@ -2493,7 +2502,7 @@ Function 257: DrawPolyLinesEx() (6 input parameters) Param[4]: rotation (type: float) Param[5]: lineThick (type: float) Param[6]: color (type: Color) -Function 258: DrawSplineLinear() (4 input parameters) +Function 259: DrawSplineLinear() (4 input parameters) Name: DrawSplineLinear Return type: void Description: Draw spline: Linear, minimum 2 points @@ -2501,7 +2510,7 @@ Function 258: DrawSplineLinear() (4 input parameters) Param[2]: pointCount (type: int) Param[3]: thick (type: float) Param[4]: color (type: Color) -Function 259: DrawSplineBasis() (4 input parameters) +Function 260: DrawSplineBasis() (4 input parameters) Name: DrawSplineBasis Return type: void Description: Draw spline: B-Spline, minimum 4 points @@ -2509,7 +2518,7 @@ Function 259: DrawSplineBasis() (4 input parameters) Param[2]: pointCount (type: int) Param[3]: thick (type: float) Param[4]: color (type: Color) -Function 260: DrawSplineCatmullRom() (4 input parameters) +Function 261: DrawSplineCatmullRom() (4 input parameters) Name: DrawSplineCatmullRom Return type: void Description: Draw spline: Catmull-Rom, minimum 4 points @@ -2517,7 +2526,7 @@ Function 260: DrawSplineCatmullRom() (4 input parameters) Param[2]: pointCount (type: int) Param[3]: thick (type: float) Param[4]: color (type: Color) -Function 261: DrawSplineBezierQuadratic() (4 input parameters) +Function 262: DrawSplineBezierQuadratic() (4 input parameters) Name: DrawSplineBezierQuadratic Return type: void Description: Draw spline: Quadratic Bezier, minimum 3 points (1 control point): [p1, c2, p3, c4...] @@ -2525,7 +2534,7 @@ Function 261: DrawSplineBezierQuadratic() (4 input parameters) Param[2]: pointCount (type: int) Param[3]: thick (type: float) Param[4]: color (type: Color) -Function 262: DrawSplineBezierCubic() (4 input parameters) +Function 263: DrawSplineBezierCubic() (4 input parameters) Name: DrawSplineBezierCubic Return type: void Description: Draw spline: Cubic Bezier, minimum 4 points (2 control points): [p1, c2, c3, p4, c5, c6...] @@ -2533,7 +2542,7 @@ Function 262: DrawSplineBezierCubic() (4 input parameters) Param[2]: pointCount (type: int) Param[3]: thick (type: float) Param[4]: color (type: Color) -Function 263: DrawSplineSegmentLinear() (4 input parameters) +Function 264: DrawSplineSegmentLinear() (4 input parameters) Name: DrawSplineSegmentLinear Return type: void Description: Draw spline segment: Linear, 2 points @@ -2541,7 +2550,7 @@ Function 263: DrawSplineSegmentLinear() (4 input parameters) Param[2]: p2 (type: Vector2) Param[3]: thick (type: float) Param[4]: color (type: Color) -Function 264: DrawSplineSegmentBasis() (6 input parameters) +Function 265: DrawSplineSegmentBasis() (6 input parameters) Name: DrawSplineSegmentBasis Return type: void Description: Draw spline segment: B-Spline, 4 points @@ -2551,7 +2560,7 @@ Function 264: DrawSplineSegmentBasis() (6 input parameters) Param[4]: p4 (type: Vector2) Param[5]: thick (type: float) Param[6]: color (type: Color) -Function 265: DrawSplineSegmentCatmullRom() (6 input parameters) +Function 266: DrawSplineSegmentCatmullRom() (6 input parameters) Name: DrawSplineSegmentCatmullRom Return type: void Description: Draw spline segment: Catmull-Rom, 4 points @@ -2561,7 +2570,7 @@ Function 265: DrawSplineSegmentCatmullRom() (6 input parameters) Param[4]: p4 (type: Vector2) Param[5]: thick (type: float) Param[6]: color (type: Color) -Function 266: DrawSplineSegmentBezierQuadratic() (5 input parameters) +Function 267: DrawSplineSegmentBezierQuadratic() (5 input parameters) Name: DrawSplineSegmentBezierQuadratic Return type: void Description: Draw spline segment: Quadratic Bezier, 2 points, 1 control point @@ -2570,7 +2579,7 @@ Function 266: DrawSplineSegmentBezierQuadratic() (5 input parameters) Param[3]: p3 (type: Vector2) Param[4]: thick (type: float) Param[5]: color (type: Color) -Function 267: DrawSplineSegmentBezierCubic() (6 input parameters) +Function 268: DrawSplineSegmentBezierCubic() (6 input parameters) Name: DrawSplineSegmentBezierCubic Return type: void Description: Draw spline segment: Cubic Bezier, 2 points, 2 control points @@ -2580,14 +2589,14 @@ Function 267: DrawSplineSegmentBezierCubic() (6 input parameters) Param[4]: p4 (type: Vector2) Param[5]: thick (type: float) Param[6]: color (type: Color) -Function 268: GetSplinePointLinear() (3 input parameters) +Function 269: GetSplinePointLinear() (3 input parameters) Name: GetSplinePointLinear Return type: Vector2 Description: Get (evaluate) spline point: Linear Param[1]: startPos (type: Vector2) Param[2]: endPos (type: Vector2) Param[3]: t (type: float) -Function 269: GetSplinePointBasis() (5 input parameters) +Function 270: GetSplinePointBasis() (5 input parameters) Name: GetSplinePointBasis Return type: Vector2 Description: Get (evaluate) spline point: B-Spline @@ -2596,7 +2605,7 @@ Function 269: GetSplinePointBasis() (5 input parameters) Param[3]: p3 (type: Vector2) Param[4]: p4 (type: Vector2) Param[5]: t (type: float) -Function 270: GetSplinePointCatmullRom() (5 input parameters) +Function 271: GetSplinePointCatmullRom() (5 input parameters) Name: GetSplinePointCatmullRom Return type: Vector2 Description: Get (evaluate) spline point: Catmull-Rom @@ -2605,7 +2614,7 @@ Function 270: GetSplinePointCatmullRom() (5 input parameters) Param[3]: p3 (type: Vector2) Param[4]: p4 (type: Vector2) Param[5]: t (type: float) -Function 271: GetSplinePointBezierQuad() (4 input parameters) +Function 272: GetSplinePointBezierQuad() (4 input parameters) Name: GetSplinePointBezierQuad Return type: Vector2 Description: Get (evaluate) spline point: Quadratic Bezier @@ -2613,7 +2622,7 @@ Function 271: GetSplinePointBezierQuad() (4 input parameters) Param[2]: c2 (type: Vector2) Param[3]: p3 (type: Vector2) Param[4]: t (type: float) -Function 272: GetSplinePointBezierCubic() (5 input parameters) +Function 273: GetSplinePointBezierCubic() (5 input parameters) Name: GetSplinePointBezierCubic Return type: Vector2 Description: Get (evaluate) spline point: Cubic Bezier @@ -2622,13 +2631,13 @@ Function 272: GetSplinePointBezierCubic() (5 input parameters) Param[3]: c3 (type: Vector2) Param[4]: p4 (type: Vector2) Param[5]: t (type: float) -Function 273: CheckCollisionRecs() (2 input parameters) +Function 274: CheckCollisionRecs() (2 input parameters) Name: CheckCollisionRecs Return type: bool Description: Check collision between two rectangles Param[1]: rec1 (type: Rectangle) Param[2]: rec2 (type: Rectangle) -Function 274: CheckCollisionCircles() (4 input parameters) +Function 275: CheckCollisionCircles() (4 input parameters) Name: CheckCollisionCircles Return type: bool Description: Check collision between two circles @@ -2636,14 +2645,14 @@ Function 274: CheckCollisionCircles() (4 input parameters) Param[2]: radius1 (type: float) Param[3]: center2 (type: Vector2) Param[4]: radius2 (type: float) -Function 275: CheckCollisionCircleRec() (3 input parameters) +Function 276: CheckCollisionCircleRec() (3 input parameters) Name: CheckCollisionCircleRec Return type: bool Description: Check collision between circle and rectangle Param[1]: center (type: Vector2) Param[2]: radius (type: float) Param[3]: rec (type: Rectangle) -Function 276: CheckCollisionCircleLine() (4 input parameters) +Function 277: CheckCollisionCircleLine() (4 input parameters) Name: CheckCollisionCircleLine Return type: bool Description: Check if circle collides with a line created betweeen two points [p1] and [p2] @@ -2651,20 +2660,20 @@ Function 276: CheckCollisionCircleLine() (4 input parameters) Param[2]: radius (type: float) Param[3]: p1 (type: Vector2) Param[4]: p2 (type: Vector2) -Function 277: CheckCollisionPointRec() (2 input parameters) +Function 278: CheckCollisionPointRec() (2 input parameters) Name: CheckCollisionPointRec Return type: bool Description: Check if point is inside rectangle Param[1]: point (type: Vector2) Param[2]: rec (type: Rectangle) -Function 278: CheckCollisionPointCircle() (3 input parameters) +Function 279: CheckCollisionPointCircle() (3 input parameters) Name: CheckCollisionPointCircle Return type: bool Description: Check if point is inside circle Param[1]: point (type: Vector2) Param[2]: center (type: Vector2) Param[3]: radius (type: float) -Function 279: CheckCollisionPointTriangle() (4 input parameters) +Function 280: CheckCollisionPointTriangle() (4 input parameters) Name: CheckCollisionPointTriangle Return type: bool Description: Check if point is inside a triangle @@ -2672,7 +2681,7 @@ Function 279: CheckCollisionPointTriangle() (4 input parameters) Param[2]: p1 (type: Vector2) Param[3]: p2 (type: Vector2) Param[4]: p3 (type: Vector2) -Function 280: CheckCollisionPointLine() (4 input parameters) +Function 281: CheckCollisionPointLine() (4 input parameters) Name: CheckCollisionPointLine Return type: bool Description: Check if point belongs to line created between two points [p1] and [p2] with defined margin in pixels [threshold] @@ -2680,14 +2689,14 @@ Function 280: CheckCollisionPointLine() (4 input parameters) Param[2]: p1 (type: Vector2) Param[3]: p2 (type: Vector2) Param[4]: threshold (type: int) -Function 281: CheckCollisionPointPoly() (3 input parameters) +Function 282: CheckCollisionPointPoly() (3 input parameters) Name: CheckCollisionPointPoly Return type: bool Description: Check if point is within a polygon described by array of vertices Param[1]: point (type: Vector2) Param[2]: points (type: const Vector2 *) Param[3]: pointCount (type: int) -Function 282: CheckCollisionLines() (5 input parameters) +Function 283: CheckCollisionLines() (5 input parameters) Name: CheckCollisionLines Return type: bool Description: Check the collision between two lines defined by two points each, returns collision point by reference @@ -2696,18 +2705,18 @@ Function 282: CheckCollisionLines() (5 input parameters) Param[3]: startPos2 (type: Vector2) Param[4]: endPos2 (type: Vector2) Param[5]: collisionPoint (type: Vector2 *) -Function 283: GetCollisionRec() (2 input parameters) +Function 284: GetCollisionRec() (2 input parameters) Name: GetCollisionRec Return type: Rectangle Description: Get collision rectangle for two rectangles collision Param[1]: rec1 (type: Rectangle) Param[2]: rec2 (type: Rectangle) -Function 284: LoadImage() (1 input parameters) +Function 285: LoadImage() (1 input parameters) Name: LoadImage Return type: Image Description: Load image from file into CPU memory (RAM) Param[1]: fileName (type: const char *) -Function 285: LoadImageRaw() (5 input parameters) +Function 286: LoadImageRaw() (5 input parameters) Name: LoadImageRaw Return type: Image Description: Load image from RAW file data @@ -2716,13 +2725,13 @@ Function 285: LoadImageRaw() (5 input parameters) Param[3]: height (type: int) Param[4]: format (type: int) Param[5]: headerSize (type: int) -Function 286: LoadImageAnim() (2 input parameters) +Function 287: LoadImageAnim() (2 input parameters) Name: LoadImageAnim Return type: Image Description: Load image sequence from file (frames appended to image.data) Param[1]: fileName (type: const char *) Param[2]: frames (type: int *) -Function 287: LoadImageAnimFromMemory() (4 input parameters) +Function 288: LoadImageAnimFromMemory() (4 input parameters) Name: LoadImageAnimFromMemory Return type: Image Description: Load image sequence from memory buffer @@ -2730,60 +2739,60 @@ Function 287: LoadImageAnimFromMemory() (4 input parameters) Param[2]: fileData (type: const unsigned char *) Param[3]: dataSize (type: int) Param[4]: frames (type: int *) -Function 288: LoadImageFromMemory() (3 input parameters) +Function 289: LoadImageFromMemory() (3 input parameters) Name: LoadImageFromMemory Return type: Image Description: Load image from memory buffer, fileType refers to extension: i.e. '.png' Param[1]: fileType (type: const char *) Param[2]: fileData (type: const unsigned char *) Param[3]: dataSize (type: int) -Function 289: LoadImageFromTexture() (1 input parameters) +Function 290: LoadImageFromTexture() (1 input parameters) Name: LoadImageFromTexture Return type: Image Description: Load image from GPU texture data Param[1]: texture (type: Texture2D) -Function 290: LoadImageFromScreen() (0 input parameters) +Function 291: LoadImageFromScreen() (0 input parameters) Name: LoadImageFromScreen Return type: Image Description: Load image from screen buffer and (screenshot) No input parameters -Function 291: IsImageValid() (1 input parameters) +Function 292: IsImageValid() (1 input parameters) Name: IsImageValid Return type: bool Description: Check if an image is valid (data and parameters) Param[1]: image (type: Image) -Function 292: UnloadImage() (1 input parameters) +Function 293: UnloadImage() (1 input parameters) Name: UnloadImage Return type: void Description: Unload image from CPU memory (RAM) Param[1]: image (type: Image) -Function 293: ExportImage() (2 input parameters) +Function 294: ExportImage() (2 input parameters) Name: ExportImage Return type: bool Description: Export image data to file, returns true on success Param[1]: image (type: Image) Param[2]: fileName (type: const char *) -Function 294: ExportImageToMemory() (3 input parameters) +Function 295: ExportImageToMemory() (3 input parameters) Name: ExportImageToMemory Return type: unsigned char * Description: Export image to memory buffer Param[1]: image (type: Image) Param[2]: fileType (type: const char *) Param[3]: fileSize (type: int *) -Function 295: ExportImageAsCode() (2 input parameters) +Function 296: ExportImageAsCode() (2 input parameters) Name: ExportImageAsCode Return type: bool Description: Export image as code file defining an array of bytes, returns true on success Param[1]: image (type: Image) Param[2]: fileName (type: const char *) -Function 296: GenImageColor() (3 input parameters) +Function 297: GenImageColor() (3 input parameters) Name: GenImageColor Return type: Image Description: Generate image: plain color Param[1]: width (type: int) Param[2]: height (type: int) Param[3]: color (type: Color) -Function 297: GenImageGradientLinear() (5 input parameters) +Function 298: GenImageGradientLinear() (5 input parameters) Name: GenImageGradientLinear Return type: Image Description: Generate image: linear gradient, direction in degrees [0..360], 0=Vertical gradient @@ -2792,7 +2801,7 @@ Function 297: GenImageGradientLinear() (5 input parameters) Param[3]: direction (type: int) Param[4]: start (type: Color) Param[5]: end (type: Color) -Function 298: GenImageGradientRadial() (5 input parameters) +Function 299: GenImageGradientRadial() (5 input parameters) Name: GenImageGradientRadial Return type: Image Description: Generate image: radial gradient @@ -2801,7 +2810,7 @@ Function 298: GenImageGradientRadial() (5 input parameters) Param[3]: density (type: float) Param[4]: inner (type: Color) Param[5]: outer (type: Color) -Function 299: GenImageGradientSquare() (5 input parameters) +Function 300: GenImageGradientSquare() (5 input parameters) Name: GenImageGradientSquare Return type: Image Description: Generate image: square gradient @@ -2810,7 +2819,7 @@ Function 299: GenImageGradientSquare() (5 input parameters) Param[3]: density (type: float) Param[4]: inner (type: Color) Param[5]: outer (type: Color) -Function 300: GenImageChecked() (6 input parameters) +Function 301: GenImageChecked() (6 input parameters) Name: GenImageChecked Return type: Image Description: Generate image: checked @@ -2820,14 +2829,14 @@ Function 300: GenImageChecked() (6 input parameters) Param[4]: checksY (type: int) Param[5]: col1 (type: Color) Param[6]: col2 (type: Color) -Function 301: GenImageWhiteNoise() (3 input parameters) +Function 302: GenImageWhiteNoise() (3 input parameters) Name: GenImageWhiteNoise Return type: Image Description: Generate image: white noise Param[1]: width (type: int) Param[2]: height (type: int) Param[3]: factor (type: float) -Function 302: GenImagePerlinNoise() (5 input parameters) +Function 303: GenImagePerlinNoise() (5 input parameters) Name: GenImagePerlinNoise Return type: Image Description: Generate image: perlin noise @@ -2836,45 +2845,45 @@ Function 302: GenImagePerlinNoise() (5 input parameters) Param[3]: offsetX (type: int) Param[4]: offsetY (type: int) Param[5]: scale (type: float) -Function 303: GenImageCellular() (3 input parameters) +Function 304: GenImageCellular() (3 input parameters) Name: GenImageCellular Return type: Image Description: Generate image: cellular algorithm, bigger tileSize means bigger cells Param[1]: width (type: int) Param[2]: height (type: int) Param[3]: tileSize (type: int) -Function 304: GenImageText() (3 input parameters) +Function 305: GenImageText() (3 input parameters) Name: GenImageText Return type: Image Description: Generate image: grayscale image from text data Param[1]: width (type: int) Param[2]: height (type: int) Param[3]: text (type: const char *) -Function 305: ImageCopy() (1 input parameters) +Function 306: ImageCopy() (1 input parameters) Name: ImageCopy Return type: Image Description: Create an image duplicate (useful for transformations) Param[1]: image (type: Image) -Function 306: ImageFromImage() (2 input parameters) +Function 307: ImageFromImage() (2 input parameters) Name: ImageFromImage Return type: Image Description: Create an image from another image piece Param[1]: image (type: Image) Param[2]: rec (type: Rectangle) -Function 307: ImageFromChannel() (2 input parameters) +Function 308: ImageFromChannel() (2 input parameters) Name: ImageFromChannel Return type: Image Description: Create an image from a selected channel of another image (GRAYSCALE) Param[1]: image (type: Image) Param[2]: selectedChannel (type: int) -Function 308: ImageText() (3 input parameters) +Function 309: ImageText() (3 input parameters) Name: ImageText Return type: Image Description: Create an image from text (default font) Param[1]: text (type: const char *) Param[2]: fontSize (type: int) Param[3]: color (type: Color) -Function 309: ImageTextEx() (5 input parameters) +Function 310: ImageTextEx() (5 input parameters) Name: ImageTextEx Return type: Image Description: Create an image from text (custom sprite font) @@ -2883,76 +2892,76 @@ Function 309: ImageTextEx() (5 input parameters) Param[3]: fontSize (type: float) Param[4]: spacing (type: float) Param[5]: tint (type: Color) -Function 310: ImageFormat() (2 input parameters) +Function 311: ImageFormat() (2 input parameters) Name: ImageFormat Return type: void Description: Convert image data to desired format Param[1]: image (type: Image *) Param[2]: newFormat (type: int) -Function 311: ImageToPOT() (2 input parameters) +Function 312: ImageToPOT() (2 input parameters) Name: ImageToPOT Return type: void Description: Convert image to POT (power-of-two) Param[1]: image (type: Image *) Param[2]: fill (type: Color) -Function 312: ImageCrop() (2 input parameters) +Function 313: ImageCrop() (2 input parameters) Name: ImageCrop Return type: void Description: Crop an image to a defined rectangle Param[1]: image (type: Image *) Param[2]: crop (type: Rectangle) -Function 313: ImageAlphaCrop() (2 input parameters) +Function 314: ImageAlphaCrop() (2 input parameters) Name: ImageAlphaCrop Return type: void Description: Crop image depending on alpha value Param[1]: image (type: Image *) Param[2]: threshold (type: float) -Function 314: ImageAlphaClear() (3 input parameters) +Function 315: ImageAlphaClear() (3 input parameters) Name: ImageAlphaClear Return type: void Description: Clear alpha channel to desired color Param[1]: image (type: Image *) Param[2]: color (type: Color) Param[3]: threshold (type: float) -Function 315: ImageAlphaMask() (2 input parameters) +Function 316: ImageAlphaMask() (2 input parameters) Name: ImageAlphaMask Return type: void Description: Apply alpha mask to image Param[1]: image (type: Image *) Param[2]: alphaMask (type: Image) -Function 316: ImageAlphaPremultiply() (1 input parameters) +Function 317: ImageAlphaPremultiply() (1 input parameters) Name: ImageAlphaPremultiply Return type: void Description: Premultiply alpha channel Param[1]: image (type: Image *) -Function 317: ImageBlurGaussian() (2 input parameters) +Function 318: ImageBlurGaussian() (2 input parameters) Name: ImageBlurGaussian Return type: void Description: Apply Gaussian blur using a box blur approximation Param[1]: image (type: Image *) Param[2]: blurSize (type: int) -Function 318: ImageKernelConvolution() (3 input parameters) +Function 319: ImageKernelConvolution() (3 input parameters) Name: ImageKernelConvolution Return type: void Description: Apply custom square convolution kernel to image Param[1]: image (type: Image *) Param[2]: kernel (type: const float *) Param[3]: kernelSize (type: int) -Function 319: ImageResize() (3 input parameters) +Function 320: ImageResize() (3 input parameters) Name: ImageResize Return type: void Description: Resize image (Bicubic scaling algorithm) Param[1]: image (type: Image *) Param[2]: newWidth (type: int) Param[3]: newHeight (type: int) -Function 320: ImageResizeNN() (3 input parameters) +Function 321: ImageResizeNN() (3 input parameters) Name: ImageResizeNN Return type: void Description: Resize image (Nearest-Neighbor scaling algorithm) Param[1]: image (type: Image *) Param[2]: newWidth (type: int) Param[3]: newHeight (type: int) -Function 321: ImageResizeCanvas() (6 input parameters) +Function 322: ImageResizeCanvas() (6 input parameters) Name: ImageResizeCanvas Return type: void Description: Resize canvas and fill with color @@ -2962,12 +2971,12 @@ Function 321: ImageResizeCanvas() (6 input parameters) Param[4]: offsetX (type: int) Param[5]: offsetY (type: int) Param[6]: fill (type: Color) -Function 322: ImageMipmaps() (1 input parameters) +Function 323: ImageMipmaps() (1 input parameters) Name: ImageMipmaps Return type: void Description: Compute all mipmap levels for a provided image Param[1]: image (type: Image *) -Function 323: ImageDither() (5 input parameters) +Function 324: ImageDither() (5 input parameters) Name: ImageDither Return type: void Description: Dither image data to 16bpp or lower (Floyd-Steinberg dithering) @@ -2976,109 +2985,109 @@ Function 323: ImageDither() (5 input parameters) Param[3]: gBpp (type: int) Param[4]: bBpp (type: int) Param[5]: aBpp (type: int) -Function 324: ImageFlipVertical() (1 input parameters) +Function 325: ImageFlipVertical() (1 input parameters) Name: ImageFlipVertical Return type: void Description: Flip image vertically Param[1]: image (type: Image *) -Function 325: ImageFlipHorizontal() (1 input parameters) +Function 326: ImageFlipHorizontal() (1 input parameters) Name: ImageFlipHorizontal Return type: void Description: Flip image horizontally Param[1]: image (type: Image *) -Function 326: ImageRotate() (2 input parameters) +Function 327: ImageRotate() (2 input parameters) Name: ImageRotate Return type: void Description: Rotate image by input angle in degrees (-359 to 359) Param[1]: image (type: Image *) Param[2]: degrees (type: int) -Function 327: ImageRotateCW() (1 input parameters) +Function 328: ImageRotateCW() (1 input parameters) Name: ImageRotateCW Return type: void Description: Rotate image clockwise 90deg Param[1]: image (type: Image *) -Function 328: ImageRotateCCW() (1 input parameters) +Function 329: ImageRotateCCW() (1 input parameters) Name: ImageRotateCCW Return type: void Description: Rotate image counter-clockwise 90deg Param[1]: image (type: Image *) -Function 329: ImageColorTint() (2 input parameters) +Function 330: ImageColorTint() (2 input parameters) Name: ImageColorTint Return type: void Description: Modify image color: tint Param[1]: image (type: Image *) Param[2]: color (type: Color) -Function 330: ImageColorInvert() (1 input parameters) +Function 331: ImageColorInvert() (1 input parameters) Name: ImageColorInvert Return type: void Description: Modify image color: invert Param[1]: image (type: Image *) -Function 331: ImageColorGrayscale() (1 input parameters) +Function 332: ImageColorGrayscale() (1 input parameters) Name: ImageColorGrayscale Return type: void Description: Modify image color: grayscale Param[1]: image (type: Image *) -Function 332: ImageColorContrast() (2 input parameters) +Function 333: ImageColorContrast() (2 input parameters) Name: ImageColorContrast Return type: void Description: Modify image color: contrast (-100 to 100) Param[1]: image (type: Image *) Param[2]: contrast (type: float) -Function 333: ImageColorBrightness() (2 input parameters) +Function 334: ImageColorBrightness() (2 input parameters) Name: ImageColorBrightness Return type: void Description: Modify image color: brightness (-255 to 255) Param[1]: image (type: Image *) Param[2]: brightness (type: int) -Function 334: ImageColorReplace() (3 input parameters) +Function 335: ImageColorReplace() (3 input parameters) Name: ImageColorReplace Return type: void Description: Modify image color: replace color Param[1]: image (type: Image *) Param[2]: color (type: Color) Param[3]: replace (type: Color) -Function 335: LoadImageColors() (1 input parameters) +Function 336: LoadImageColors() (1 input parameters) Name: LoadImageColors Return type: Color * Description: Load color data from image as a Color array (RGBA - 32bit) Param[1]: image (type: Image) -Function 336: LoadImagePalette() (3 input parameters) +Function 337: LoadImagePalette() (3 input parameters) Name: LoadImagePalette Return type: Color * Description: Load colors palette from image as a Color array (RGBA - 32bit) Param[1]: image (type: Image) Param[2]: maxPaletteSize (type: int) Param[3]: colorCount (type: int *) -Function 337: UnloadImageColors() (1 input parameters) +Function 338: UnloadImageColors() (1 input parameters) Name: UnloadImageColors Return type: void Description: Unload color data loaded with LoadImageColors() Param[1]: colors (type: Color *) -Function 338: UnloadImagePalette() (1 input parameters) +Function 339: UnloadImagePalette() (1 input parameters) Name: UnloadImagePalette Return type: void Description: Unload colors palette loaded with LoadImagePalette() Param[1]: colors (type: Color *) -Function 339: GetImageAlphaBorder() (2 input parameters) +Function 340: GetImageAlphaBorder() (2 input parameters) Name: GetImageAlphaBorder Return type: Rectangle Description: Get image alpha border rectangle Param[1]: image (type: Image) Param[2]: threshold (type: float) -Function 340: GetImageColor() (3 input parameters) +Function 341: GetImageColor() (3 input parameters) Name: GetImageColor Return type: Color Description: Get image pixel color at (x, y) position Param[1]: image (type: Image) Param[2]: x (type: int) Param[3]: y (type: int) -Function 341: ImageClearBackground() (2 input parameters) +Function 342: ImageClearBackground() (2 input parameters) Name: ImageClearBackground Return type: void Description: Clear image background with given color Param[1]: dst (type: Image *) Param[2]: color (type: Color) -Function 342: ImageDrawPixel() (4 input parameters) +Function 343: ImageDrawPixel() (4 input parameters) Name: ImageDrawPixel Return type: void Description: Draw pixel within an image @@ -3086,14 +3095,14 @@ Function 342: ImageDrawPixel() (4 input parameters) Param[2]: posX (type: int) Param[3]: posY (type: int) Param[4]: color (type: Color) -Function 343: ImageDrawPixelV() (3 input parameters) +Function 344: ImageDrawPixelV() (3 input parameters) Name: ImageDrawPixelV Return type: void Description: Draw pixel within an image (Vector version) Param[1]: dst (type: Image *) Param[2]: position (type: Vector2) Param[3]: color (type: Color) -Function 344: ImageDrawLine() (6 input parameters) +Function 345: ImageDrawLine() (6 input parameters) Name: ImageDrawLine Return type: void Description: Draw line within an image @@ -3103,7 +3112,7 @@ Function 344: ImageDrawLine() (6 input parameters) Param[4]: endPosX (type: int) Param[5]: endPosY (type: int) Param[6]: color (type: Color) -Function 345: ImageDrawLineV() (4 input parameters) +Function 346: ImageDrawLineV() (4 input parameters) Name: ImageDrawLineV Return type: void Description: Draw line within an image (Vector version) @@ -3111,7 +3120,7 @@ Function 345: ImageDrawLineV() (4 input parameters) Param[2]: start (type: Vector2) Param[3]: end (type: Vector2) Param[4]: color (type: Color) -Function 346: ImageDrawLineEx() (5 input parameters) +Function 347: ImageDrawLineEx() (5 input parameters) Name: ImageDrawLineEx Return type: void Description: Draw a line defining thickness within an image @@ -3120,7 +3129,7 @@ Function 346: ImageDrawLineEx() (5 input parameters) Param[3]: end (type: Vector2) Param[4]: thick (type: int) Param[5]: color (type: Color) -Function 347: ImageDrawCircle() (5 input parameters) +Function 348: ImageDrawCircle() (5 input parameters) Name: ImageDrawCircle Return type: void Description: Draw a filled circle within an image @@ -3129,7 +3138,7 @@ Function 347: ImageDrawCircle() (5 input parameters) Param[3]: centerY (type: int) Param[4]: radius (type: int) Param[5]: color (type: Color) -Function 348: ImageDrawCircleV() (4 input parameters) +Function 349: ImageDrawCircleV() (4 input parameters) Name: ImageDrawCircleV Return type: void Description: Draw a filled circle within an image (Vector version) @@ -3137,7 +3146,7 @@ Function 348: ImageDrawCircleV() (4 input parameters) Param[2]: center (type: Vector2) Param[3]: radius (type: int) Param[4]: color (type: Color) -Function 349: ImageDrawCircleLines() (5 input parameters) +Function 350: ImageDrawCircleLines() (5 input parameters) Name: ImageDrawCircleLines Return type: void Description: Draw circle outline within an image @@ -3146,7 +3155,7 @@ Function 349: ImageDrawCircleLines() (5 input parameters) Param[3]: centerY (type: int) Param[4]: radius (type: int) Param[5]: color (type: Color) -Function 350: ImageDrawCircleLinesV() (4 input parameters) +Function 351: ImageDrawCircleLinesV() (4 input parameters) Name: ImageDrawCircleLinesV Return type: void Description: Draw circle outline within an image (Vector version) @@ -3154,7 +3163,7 @@ Function 350: ImageDrawCircleLinesV() (4 input parameters) Param[2]: center (type: Vector2) Param[3]: radius (type: int) Param[4]: color (type: Color) -Function 351: ImageDrawRectangle() (6 input parameters) +Function 352: ImageDrawRectangle() (6 input parameters) Name: ImageDrawRectangle Return type: void Description: Draw rectangle within an image @@ -3164,7 +3173,7 @@ Function 351: ImageDrawRectangle() (6 input parameters) Param[4]: width (type: int) Param[5]: height (type: int) Param[6]: color (type: Color) -Function 352: ImageDrawRectangleV() (4 input parameters) +Function 353: ImageDrawRectangleV() (4 input parameters) Name: ImageDrawRectangleV Return type: void Description: Draw rectangle within an image (Vector version) @@ -3172,14 +3181,14 @@ Function 352: ImageDrawRectangleV() (4 input parameters) Param[2]: position (type: Vector2) Param[3]: size (type: Vector2) Param[4]: color (type: Color) -Function 353: ImageDrawRectangleRec() (3 input parameters) +Function 354: ImageDrawRectangleRec() (3 input parameters) Name: ImageDrawRectangleRec Return type: void Description: Draw rectangle within an image Param[1]: dst (type: Image *) Param[2]: rec (type: Rectangle) Param[3]: color (type: Color) -Function 354: ImageDrawRectangleLines() (4 input parameters) +Function 355: ImageDrawRectangleLines() (4 input parameters) Name: ImageDrawRectangleLines Return type: void Description: Draw rectangle lines within an image @@ -3187,7 +3196,7 @@ Function 354: ImageDrawRectangleLines() (4 input parameters) Param[2]: rec (type: Rectangle) Param[3]: thick (type: int) Param[4]: color (type: Color) -Function 355: ImageDrawTriangle() (5 input parameters) +Function 356: ImageDrawTriangle() (5 input parameters) Name: ImageDrawTriangle Return type: void Description: Draw triangle within an image @@ -3196,7 +3205,7 @@ Function 355: ImageDrawTriangle() (5 input parameters) Param[3]: v2 (type: Vector2) Param[4]: v3 (type: Vector2) Param[5]: color (type: Color) -Function 356: ImageDrawTriangleEx() (7 input parameters) +Function 357: ImageDrawTriangleEx() (7 input parameters) Name: ImageDrawTriangleEx Return type: void Description: Draw triangle with interpolated colors within an image @@ -3207,7 +3216,7 @@ Function 356: ImageDrawTriangleEx() (7 input parameters) Param[5]: c1 (type: Color) Param[6]: c2 (type: Color) Param[7]: c3 (type: Color) -Function 357: ImageDrawTriangleLines() (5 input parameters) +Function 358: ImageDrawTriangleLines() (5 input parameters) Name: ImageDrawTriangleLines Return type: void Description: Draw triangle outline within an image @@ -3216,7 +3225,7 @@ Function 357: ImageDrawTriangleLines() (5 input parameters) Param[3]: v2 (type: Vector2) Param[4]: v3 (type: Vector2) Param[5]: color (type: Color) -Function 358: ImageDrawTriangleFan() (4 input parameters) +Function 359: ImageDrawTriangleFan() (4 input parameters) Name: ImageDrawTriangleFan Return type: void Description: Draw a triangle fan defined by points within an image (first vertex is the center) @@ -3224,7 +3233,7 @@ Function 358: ImageDrawTriangleFan() (4 input parameters) Param[2]: points (type: const Vector2 *) Param[3]: pointCount (type: int) Param[4]: color (type: Color) -Function 359: ImageDrawTriangleStrip() (4 input parameters) +Function 360: ImageDrawTriangleStrip() (4 input parameters) Name: ImageDrawTriangleStrip Return type: void Description: Draw a triangle strip defined by points within an image @@ -3232,7 +3241,7 @@ Function 359: ImageDrawTriangleStrip() (4 input parameters) Param[2]: points (type: const Vector2 *) Param[3]: pointCount (type: int) Param[4]: color (type: Color) -Function 360: ImageDraw() (5 input parameters) +Function 361: ImageDraw() (5 input parameters) Name: ImageDraw Return type: void Description: Draw a source image within a destination image (tint applied to source) @@ -3241,7 +3250,7 @@ Function 360: ImageDraw() (5 input parameters) Param[3]: srcRec (type: Rectangle) Param[4]: dstRec (type: Rectangle) Param[5]: tint (type: Color) -Function 361: ImageDrawText() (6 input parameters) +Function 362: ImageDrawText() (6 input parameters) Name: ImageDrawText Return type: void Description: Draw text (using default font) within an image (destination) @@ -3251,7 +3260,7 @@ Function 361: ImageDrawText() (6 input parameters) Param[4]: posY (type: int) Param[5]: fontSize (type: int) Param[6]: color (type: Color) -Function 362: ImageDrawTextEx() (7 input parameters) +Function 363: ImageDrawTextEx() (7 input parameters) Name: ImageDrawTextEx Return type: void Description: Draw text (custom sprite font) within an image (destination) @@ -3262,79 +3271,79 @@ Function 362: ImageDrawTextEx() (7 input parameters) Param[5]: fontSize (type: float) Param[6]: spacing (type: float) Param[7]: tint (type: Color) -Function 363: LoadTexture() (1 input parameters) +Function 364: LoadTexture() (1 input parameters) Name: LoadTexture Return type: Texture2D Description: Load texture from file into GPU memory (VRAM) Param[1]: fileName (type: const char *) -Function 364: LoadTextureFromImage() (1 input parameters) +Function 365: LoadTextureFromImage() (1 input parameters) Name: LoadTextureFromImage Return type: Texture2D Description: Load texture from image data Param[1]: image (type: Image) -Function 365: LoadTextureCubemap() (2 input parameters) +Function 366: LoadTextureCubemap() (2 input parameters) Name: LoadTextureCubemap Return type: TextureCubemap Description: Load cubemap from image, multiple image cubemap layouts supported Param[1]: image (type: Image) Param[2]: layout (type: int) -Function 366: LoadRenderTexture() (2 input parameters) +Function 367: LoadRenderTexture() (2 input parameters) Name: LoadRenderTexture Return type: RenderTexture2D Description: Load texture for rendering (framebuffer) Param[1]: width (type: int) Param[2]: height (type: int) -Function 367: IsTextureValid() (1 input parameters) +Function 368: IsTextureValid() (1 input parameters) Name: IsTextureValid Return type: bool Description: Check if a texture is valid (loaded in GPU) Param[1]: texture (type: Texture2D) -Function 368: UnloadTexture() (1 input parameters) +Function 369: UnloadTexture() (1 input parameters) Name: UnloadTexture Return type: void Description: Unload texture from GPU memory (VRAM) Param[1]: texture (type: Texture2D) -Function 369: IsRenderTextureValid() (1 input parameters) +Function 370: IsRenderTextureValid() (1 input parameters) Name: IsRenderTextureValid Return type: bool Description: Check if a render texture is valid (loaded in GPU) Param[1]: target (type: RenderTexture2D) -Function 370: UnloadRenderTexture() (1 input parameters) +Function 371: UnloadRenderTexture() (1 input parameters) Name: UnloadRenderTexture Return type: void Description: Unload render texture from GPU memory (VRAM) Param[1]: target (type: RenderTexture2D) -Function 371: UpdateTexture() (2 input parameters) +Function 372: UpdateTexture() (2 input parameters) Name: UpdateTexture Return type: void Description: Update GPU texture with new data (pixels should be able to fill texture) Param[1]: texture (type: Texture2D) Param[2]: pixels (type: const void *) -Function 372: UpdateTextureRec() (3 input parameters) +Function 373: UpdateTextureRec() (3 input parameters) Name: UpdateTextureRec Return type: void Description: Update GPU texture rectangle with new data (pixels and rec should fit in texture) Param[1]: texture (type: Texture2D) Param[2]: rec (type: Rectangle) Param[3]: pixels (type: const void *) -Function 373: GenTextureMipmaps() (1 input parameters) +Function 374: GenTextureMipmaps() (1 input parameters) Name: GenTextureMipmaps Return type: void Description: Generate GPU mipmaps for a texture Param[1]: texture (type: Texture2D *) -Function 374: SetTextureFilter() (2 input parameters) +Function 375: SetTextureFilter() (2 input parameters) Name: SetTextureFilter Return type: void Description: Set texture scaling filter mode Param[1]: texture (type: Texture2D) Param[2]: filter (type: int) -Function 375: SetTextureWrap() (2 input parameters) +Function 376: SetTextureWrap() (2 input parameters) Name: SetTextureWrap Return type: void Description: Set texture wrapping mode Param[1]: texture (type: Texture2D) Param[2]: wrap (type: int) -Function 376: DrawTexture() (4 input parameters) +Function 377: DrawTexture() (4 input parameters) Name: DrawTexture Return type: void Description: Draw a Texture2D @@ -3342,14 +3351,14 @@ Function 376: DrawTexture() (4 input parameters) Param[2]: posX (type: int) Param[3]: posY (type: int) Param[4]: tint (type: Color) -Function 377: DrawTextureV() (3 input parameters) +Function 378: DrawTextureV() (3 input parameters) Name: DrawTextureV Return type: void Description: Draw a Texture2D with position defined as Vector2 Param[1]: texture (type: Texture2D) Param[2]: position (type: Vector2) Param[3]: tint (type: Color) -Function 378: DrawTextureEx() (5 input parameters) +Function 379: DrawTextureEx() (5 input parameters) Name: DrawTextureEx Return type: void Description: Draw a Texture2D with extended parameters @@ -3358,7 +3367,7 @@ Function 378: DrawTextureEx() (5 input parameters) Param[3]: rotation (type: float) Param[4]: scale (type: float) Param[5]: tint (type: Color) -Function 379: DrawTextureRec() (4 input parameters) +Function 380: DrawTextureRec() (4 input parameters) Name: DrawTextureRec Return type: void Description: Draw a part of a texture defined by a rectangle @@ -3366,7 +3375,7 @@ Function 379: DrawTextureRec() (4 input parameters) Param[2]: source (type: Rectangle) Param[3]: position (type: Vector2) Param[4]: tint (type: Color) -Function 380: DrawTexturePro() (6 input parameters) +Function 381: DrawTexturePro() (6 input parameters) Name: DrawTexturePro Return type: void Description: Draw a part of a texture defined by a rectangle with 'pro' parameters @@ -3376,7 +3385,7 @@ Function 380: DrawTexturePro() (6 input parameters) Param[4]: origin (type: Vector2) Param[5]: rotation (type: float) Param[6]: tint (type: Color) -Function 381: DrawTextureNPatch() (6 input parameters) +Function 382: DrawTextureNPatch() (6 input parameters) Name: DrawTextureNPatch Return type: void Description: Draws a texture (or part of it) that stretches or shrinks nicely @@ -3386,119 +3395,119 @@ Function 381: DrawTextureNPatch() (6 input parameters) Param[4]: origin (type: Vector2) Param[5]: rotation (type: float) Param[6]: tint (type: Color) -Function 382: ColorIsEqual() (2 input parameters) +Function 383: ColorIsEqual() (2 input parameters) Name: ColorIsEqual Return type: bool Description: Check if two colors are equal Param[1]: col1 (type: Color) Param[2]: col2 (type: Color) -Function 383: Fade() (2 input parameters) +Function 384: Fade() (2 input parameters) Name: Fade Return type: Color Description: Get color with alpha applied, alpha goes from 0.0f to 1.0f Param[1]: color (type: Color) Param[2]: alpha (type: float) -Function 384: ColorToInt() (1 input parameters) +Function 385: ColorToInt() (1 input parameters) Name: ColorToInt Return type: int Description: Get hexadecimal value for a Color (0xRRGGBBAA) Param[1]: color (type: Color) -Function 385: ColorNormalize() (1 input parameters) +Function 386: ColorNormalize() (1 input parameters) Name: ColorNormalize Return type: Vector4 Description: Get Color normalized as float [0..1] Param[1]: color (type: Color) -Function 386: ColorFromNormalized() (1 input parameters) +Function 387: ColorFromNormalized() (1 input parameters) Name: ColorFromNormalized Return type: Color Description: Get Color from normalized values [0..1] Param[1]: normalized (type: Vector4) -Function 387: ColorToHSV() (1 input parameters) +Function 388: ColorToHSV() (1 input parameters) Name: ColorToHSV Return type: Vector3 Description: Get HSV values for a Color, hue [0..360], saturation/value [0..1] Param[1]: color (type: Color) -Function 388: ColorFromHSV() (3 input parameters) +Function 389: ColorFromHSV() (3 input parameters) Name: ColorFromHSV Return type: Color Description: Get a Color from HSV values, hue [0..360], saturation/value [0..1] Param[1]: hue (type: float) Param[2]: saturation (type: float) Param[3]: value (type: float) -Function 389: ColorTint() (2 input parameters) +Function 390: ColorTint() (2 input parameters) Name: ColorTint Return type: Color Description: Get color multiplied with another color Param[1]: color (type: Color) Param[2]: tint (type: Color) -Function 390: ColorBrightness() (2 input parameters) +Function 391: ColorBrightness() (2 input parameters) Name: ColorBrightness Return type: Color Description: Get color with brightness correction, brightness factor goes from -1.0f to 1.0f Param[1]: color (type: Color) Param[2]: factor (type: float) -Function 391: ColorContrast() (2 input parameters) +Function 392: ColorContrast() (2 input parameters) Name: ColorContrast Return type: Color Description: Get color with contrast correction, contrast values between -1.0f and 1.0f Param[1]: color (type: Color) Param[2]: contrast (type: float) -Function 392: ColorAlpha() (2 input parameters) +Function 393: ColorAlpha() (2 input parameters) Name: ColorAlpha Return type: Color Description: Get color with alpha applied, alpha goes from 0.0f to 1.0f Param[1]: color (type: Color) Param[2]: alpha (type: float) -Function 393: ColorAlphaBlend() (3 input parameters) +Function 394: ColorAlphaBlend() (3 input parameters) Name: ColorAlphaBlend Return type: Color Description: Get src alpha-blended into dst color with tint Param[1]: dst (type: Color) Param[2]: src (type: Color) Param[3]: tint (type: Color) -Function 394: ColorLerp() (3 input parameters) +Function 395: ColorLerp() (3 input parameters) Name: ColorLerp Return type: Color Description: Get color lerp interpolation between two colors, factor [0.0f..1.0f] Param[1]: color1 (type: Color) Param[2]: color2 (type: Color) Param[3]: factor (type: float) -Function 395: GetColor() (1 input parameters) +Function 396: GetColor() (1 input parameters) Name: GetColor Return type: Color Description: Get Color structure from hexadecimal value Param[1]: hexValue (type: unsigned int) -Function 396: GetPixelColor() (2 input parameters) +Function 397: GetPixelColor() (2 input parameters) Name: GetPixelColor Return type: Color Description: Get Color from a source pixel pointer of certain format Param[1]: srcPtr (type: void *) Param[2]: format (type: int) -Function 397: SetPixelColor() (3 input parameters) +Function 398: SetPixelColor() (3 input parameters) Name: SetPixelColor Return type: void Description: Set color formatted into destination pixel pointer Param[1]: dstPtr (type: void *) Param[2]: color (type: Color) Param[3]: format (type: int) -Function 398: GetPixelDataSize() (3 input parameters) +Function 399: GetPixelDataSize() (3 input parameters) Name: GetPixelDataSize Return type: int Description: Get pixel data size in bytes for certain format Param[1]: width (type: int) Param[2]: height (type: int) Param[3]: format (type: int) -Function 399: GetFontDefault() (0 input parameters) +Function 400: GetFontDefault() (0 input parameters) Name: GetFontDefault Return type: Font Description: Get the default Font No input parameters -Function 400: LoadFont() (1 input parameters) +Function 401: LoadFont() (1 input parameters) Name: LoadFont Return type: Font Description: Load font from file into GPU memory (VRAM) Param[1]: fileName (type: const char *) -Function 401: LoadFontEx() (4 input parameters) +Function 402: LoadFontEx() (4 input parameters) Name: LoadFontEx Return type: Font Description: Load font from file with extended parameters, use NULL for codepoints and 0 for codepointCount to load the default character set, font size is provided in pixels height @@ -3506,14 +3515,14 @@ Function 401: LoadFontEx() (4 input parameters) Param[2]: fontSize (type: int) Param[3]: codepoints (type: const int *) Param[4]: codepointCount (type: int) -Function 402: LoadFontFromImage() (3 input parameters) +Function 403: LoadFontFromImage() (3 input parameters) Name: LoadFontFromImage Return type: Font Description: Load font from Image (XNA style) Param[1]: image (type: Image) Param[2]: key (type: Color) Param[3]: firstChar (type: int) -Function 403: LoadFontFromMemory() (6 input parameters) +Function 404: LoadFontFromMemory() (6 input parameters) Name: LoadFontFromMemory Return type: Font Description: Load font from memory buffer, fileType refers to extension: i.e. '.ttf' @@ -3523,12 +3532,12 @@ Function 403: LoadFontFromMemory() (6 input parameters) Param[4]: fontSize (type: int) Param[5]: codepoints (type: const int *) Param[6]: codepointCount (type: int) -Function 404: IsFontValid() (1 input parameters) +Function 405: IsFontValid() (1 input parameters) Name: IsFontValid Return type: bool Description: Check if a font is valid (font data loaded, WARNING: GPU texture not checked) Param[1]: font (type: Font) -Function 405: LoadFontData() (7 input parameters) +Function 406: LoadFontData() (7 input parameters) Name: LoadFontData Return type: GlyphInfo * Description: Load font data for further use @@ -3539,7 +3548,7 @@ Function 405: LoadFontData() (7 input parameters) Param[5]: codepointCount (type: int) Param[6]: type (type: int) Param[7]: glyphCount (type: int *) -Function 406: GenImageFontAtlas() (6 input parameters) +Function 407: GenImageFontAtlas() (6 input parameters) Name: GenImageFontAtlas Return type: Image Description: Generate image font atlas using chars info @@ -3549,30 +3558,30 @@ Function 406: GenImageFontAtlas() (6 input parameters) Param[4]: fontSize (type: int) Param[5]: padding (type: int) Param[6]: packMethod (type: int) -Function 407: UnloadFontData() (2 input parameters) +Function 408: UnloadFontData() (2 input parameters) Name: UnloadFontData Return type: void Description: Unload font chars info data (RAM) Param[1]: glyphs (type: GlyphInfo *) Param[2]: glyphCount (type: int) -Function 408: UnloadFont() (1 input parameters) +Function 409: UnloadFont() (1 input parameters) Name: UnloadFont Return type: void Description: Unload font from GPU memory (VRAM) Param[1]: font (type: Font) -Function 409: ExportFontAsCode() (2 input parameters) +Function 410: ExportFontAsCode() (2 input parameters) Name: ExportFontAsCode Return type: bool Description: Export font as code file, returns true on success Param[1]: font (type: Font) Param[2]: fileName (type: const char *) -Function 410: DrawFPS() (2 input parameters) +Function 411: DrawFPS() (2 input parameters) Name: DrawFPS Return type: void Description: Draw current FPS Param[1]: posX (type: int) Param[2]: posY (type: int) -Function 411: DrawText() (5 input parameters) +Function 412: DrawText() (5 input parameters) Name: DrawText Return type: void Description: Draw text (using default font) @@ -3581,7 +3590,7 @@ Function 411: DrawText() (5 input parameters) Param[3]: posY (type: int) Param[4]: fontSize (type: int) Param[5]: color (type: Color) -Function 412: DrawTextEx() (6 input parameters) +Function 413: DrawTextEx() (6 input parameters) Name: DrawTextEx Return type: void Description: Draw text using font and additional parameters @@ -3591,7 +3600,7 @@ Function 412: DrawTextEx() (6 input parameters) Param[4]: fontSize (type: float) Param[5]: spacing (type: float) Param[6]: tint (type: Color) -Function 413: DrawTextPro() (8 input parameters) +Function 414: DrawTextPro() (8 input parameters) Name: DrawTextPro Return type: void Description: Draw text using Font and pro parameters (rotation) @@ -3603,7 +3612,7 @@ Function 413: DrawTextPro() (8 input parameters) Param[6]: fontSize (type: float) Param[7]: spacing (type: float) Param[8]: tint (type: Color) -Function 414: DrawTextCodepoint() (5 input parameters) +Function 415: DrawTextCodepoint() (5 input parameters) Name: DrawTextCodepoint Return type: void Description: Draw one character (codepoint) @@ -3612,7 +3621,7 @@ Function 414: DrawTextCodepoint() (5 input parameters) Param[3]: position (type: Vector2) Param[4]: fontSize (type: float) Param[5]: tint (type: Color) -Function 415: DrawTextCodepoints() (7 input parameters) +Function 416: DrawTextCodepoints() (7 input parameters) Name: DrawTextCodepoints Return type: void Description: Draw multiple character (codepoint) @@ -3623,18 +3632,18 @@ Function 415: DrawTextCodepoints() (7 input parameters) Param[5]: fontSize (type: float) Param[6]: spacing (type: float) Param[7]: tint (type: Color) -Function 416: SetTextLineSpacing() (1 input parameters) +Function 417: SetTextLineSpacing() (1 input parameters) Name: SetTextLineSpacing Return type: void Description: Set vertical line spacing when drawing with line-breaks Param[1]: spacing (type: int) -Function 417: MeasureText() (2 input parameters) +Function 418: MeasureText() (2 input parameters) Name: MeasureText Return type: int Description: Measure string width for default font Param[1]: text (type: const char *) Param[2]: fontSize (type: int) -Function 418: MeasureTextEx() (4 input parameters) +Function 419: MeasureTextEx() (4 input parameters) Name: MeasureTextEx Return type: Vector2 Description: Measure string size for Font @@ -3642,137 +3651,137 @@ Function 418: MeasureTextEx() (4 input parameters) Param[2]: text (type: const char *) Param[3]: fontSize (type: float) Param[4]: spacing (type: float) -Function 419: GetGlyphIndex() (2 input parameters) +Function 420: GetGlyphIndex() (2 input parameters) Name: GetGlyphIndex Return type: int Description: Get glyph index position in font for a codepoint (unicode character), fallback to '?' if not found Param[1]: font (type: Font) Param[2]: codepoint (type: int) -Function 420: GetGlyphInfo() (2 input parameters) +Function 421: GetGlyphInfo() (2 input parameters) Name: GetGlyphInfo Return type: GlyphInfo Description: Get glyph font info data for a codepoint (unicode character), fallback to '?' if not found Param[1]: font (type: Font) Param[2]: codepoint (type: int) -Function 421: GetGlyphAtlasRec() (2 input parameters) +Function 422: GetGlyphAtlasRec() (2 input parameters) Name: GetGlyphAtlasRec Return type: Rectangle Description: Get glyph rectangle in font atlas for a codepoint (unicode character), fallback to '?' if not found Param[1]: font (type: Font) Param[2]: codepoint (type: int) -Function 422: LoadUTF8() (2 input parameters) +Function 423: LoadUTF8() (2 input parameters) Name: LoadUTF8 Return type: char * Description: Load UTF-8 text encoded from codepoints array Param[1]: codepoints (type: const int *) Param[2]: length (type: int) -Function 423: UnloadUTF8() (1 input parameters) +Function 424: UnloadUTF8() (1 input parameters) Name: UnloadUTF8 Return type: void Description: Unload UTF-8 text encoded from codepoints array Param[1]: text (type: char *) -Function 424: LoadCodepoints() (2 input parameters) +Function 425: LoadCodepoints() (2 input parameters) Name: LoadCodepoints Return type: int * Description: Load all codepoints from a UTF-8 text string, codepoints count returned by parameter Param[1]: text (type: const char *) Param[2]: count (type: int *) -Function 425: UnloadCodepoints() (1 input parameters) +Function 426: UnloadCodepoints() (1 input parameters) Name: UnloadCodepoints Return type: void Description: Unload codepoints data from memory Param[1]: codepoints (type: int *) -Function 426: GetCodepointCount() (1 input parameters) +Function 427: GetCodepointCount() (1 input parameters) Name: GetCodepointCount Return type: int Description: Get total number of codepoints in a UTF-8 encoded string Param[1]: text (type: const char *) -Function 427: GetCodepoint() (2 input parameters) +Function 428: GetCodepoint() (2 input parameters) Name: GetCodepoint Return type: int Description: Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure Param[1]: text (type: const char *) Param[2]: codepointSize (type: int *) -Function 428: GetCodepointNext() (2 input parameters) +Function 429: GetCodepointNext() (2 input parameters) Name: GetCodepointNext Return type: int Description: Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure Param[1]: text (type: const char *) Param[2]: codepointSize (type: int *) -Function 429: GetCodepointPrevious() (2 input parameters) +Function 430: GetCodepointPrevious() (2 input parameters) Name: GetCodepointPrevious Return type: int Description: Get previous codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure Param[1]: text (type: const char *) Param[2]: codepointSize (type: int *) -Function 430: CodepointToUTF8() (2 input parameters) +Function 431: CodepointToUTF8() (2 input parameters) Name: CodepointToUTF8 Return type: const char * Description: Encode one codepoint into UTF-8 byte array (array length returned as parameter) Param[1]: codepoint (type: int) Param[2]: utf8Size (type: int *) -Function 431: LoadTextLines() (2 input parameters) +Function 432: LoadTextLines() (2 input parameters) Name: LoadTextLines Return type: char ** Description: Load text as separate lines ('\n') Param[1]: text (type: const char *) Param[2]: count (type: int *) -Function 432: UnloadTextLines() (2 input parameters) +Function 433: UnloadTextLines() (2 input parameters) Name: UnloadTextLines Return type: void Description: Unload text lines Param[1]: text (type: char **) Param[2]: lineCount (type: int) -Function 433: TextCopy() (2 input parameters) +Function 434: TextCopy() (2 input parameters) Name: TextCopy Return type: int Description: Copy one string to another, returns bytes copied Param[1]: dst (type: char *) Param[2]: src (type: const char *) -Function 434: TextIsEqual() (2 input parameters) +Function 435: TextIsEqual() (2 input parameters) Name: TextIsEqual Return type: bool Description: Check if two text string are equal Param[1]: text1 (type: const char *) Param[2]: text2 (type: const char *) -Function 435: TextLength() (1 input parameters) +Function 436: TextLength() (1 input parameters) Name: TextLength Return type: unsigned int Description: Get text length, checks for '\0' ending Param[1]: text (type: const char *) -Function 436: TextFormat() (2 input parameters) +Function 437: TextFormat() (2 input parameters) Name: TextFormat Return type: const char * Description: Text formatting with variables (sprintf() style) Param[1]: text (type: const char *) Param[2]: args (type: ...) -Function 437: TextSubtext() (3 input parameters) +Function 438: TextSubtext() (3 input parameters) Name: TextSubtext Return type: const char * Description: Get a piece of a text string Param[1]: text (type: const char *) Param[2]: position (type: int) Param[3]: length (type: int) -Function 438: TextRemoveSpaces() (1 input parameters) +Function 439: TextRemoveSpaces() (1 input parameters) Name: TextRemoveSpaces Return type: const char * Description: Remove text spaces, concat words Param[1]: text (type: const char *) -Function 439: GetTextBetween() (3 input parameters) +Function 440: GetTextBetween() (3 input parameters) Name: GetTextBetween Return type: char * Description: Get text between two strings Param[1]: text (type: const char *) Param[2]: begin (type: const char *) Param[3]: end (type: const char *) -Function 440: TextReplace() (3 input parameters) +Function 441: TextReplace() (3 input parameters) Name: TextReplace Return type: char * Description: Replace text string (WARNING: memory must be freed!) Param[1]: text (type: const char *) Param[2]: search (type: const char *) Param[3]: replacement (type: const char *) -Function 441: TextReplaceBetween() (4 input parameters) +Function 442: TextReplaceBetween() (4 input parameters) Name: TextReplaceBetween Return type: char * Description: Replace text between two specific strings (WARNING: memory must be freed!) @@ -3780,89 +3789,89 @@ Function 441: TextReplaceBetween() (4 input parameters) Param[2]: begin (type: const char *) Param[3]: end (type: const char *) Param[4]: replacement (type: const char *) -Function 442: TextInsert() (3 input parameters) +Function 443: TextInsert() (3 input parameters) Name: TextInsert Return type: char * Description: Insert text in a position (WARNING: memory must be freed!) Param[1]: text (type: const char *) Param[2]: insert (type: const char *) Param[3]: position (type: int) -Function 443: TextJoin() (3 input parameters) +Function 444: TextJoin() (3 input parameters) Name: TextJoin Return type: char * Description: Join text strings with delimiter Param[1]: textList (type: char **) Param[2]: count (type: int) Param[3]: delimiter (type: const char *) -Function 444: TextSplit() (3 input parameters) +Function 445: TextSplit() (3 input parameters) Name: TextSplit Return type: char ** Description: Split text into multiple strings, using MAX_TEXTSPLIT_COUNT static strings Param[1]: text (type: const char *) Param[2]: delimiter (type: char) Param[3]: count (type: int *) -Function 445: TextAppend() (3 input parameters) +Function 446: TextAppend() (3 input parameters) Name: TextAppend Return type: void Description: Append text at specific position and move cursor Param[1]: text (type: char *) Param[2]: append (type: const char *) Param[3]: position (type: int *) -Function 446: TextFindIndex() (2 input parameters) +Function 447: TextFindIndex() (2 input parameters) Name: TextFindIndex Return type: int Description: Find first text occurrence within a string, -1 if not found Param[1]: text (type: const char *) Param[2]: search (type: const char *) -Function 447: TextToUpper() (1 input parameters) +Function 448: TextToUpper() (1 input parameters) Name: TextToUpper Return type: char * Description: Get upper case version of provided string Param[1]: text (type: const char *) -Function 448: TextToLower() (1 input parameters) +Function 449: TextToLower() (1 input parameters) Name: TextToLower Return type: char * Description: Get lower case version of provided string Param[1]: text (type: const char *) -Function 449: TextToPascal() (1 input parameters) +Function 450: TextToPascal() (1 input parameters) Name: TextToPascal Return type: char * Description: Get Pascal case notation version of provided string Param[1]: text (type: const char *) -Function 450: TextToSnake() (1 input parameters) +Function 451: TextToSnake() (1 input parameters) Name: TextToSnake Return type: char * Description: Get Snake case notation version of provided string Param[1]: text (type: const char *) -Function 451: TextToCamel() (1 input parameters) +Function 452: TextToCamel() (1 input parameters) Name: TextToCamel Return type: char * Description: Get Camel case notation version of provided string Param[1]: text (type: const char *) -Function 452: TextToInteger() (1 input parameters) +Function 453: TextToInteger() (1 input parameters) Name: TextToInteger Return type: int Description: Get integer value from text Param[1]: text (type: const char *) -Function 453: TextToFloat() (1 input parameters) +Function 454: TextToFloat() (1 input parameters) Name: TextToFloat Return type: float Description: Get float value from text Param[1]: text (type: const char *) -Function 454: DrawLine3D() (3 input parameters) +Function 455: DrawLine3D() (3 input parameters) Name: DrawLine3D Return type: void Description: Draw a line in 3D world space Param[1]: startPos (type: Vector3) Param[2]: endPos (type: Vector3) Param[3]: color (type: Color) -Function 455: DrawPoint3D() (2 input parameters) +Function 456: DrawPoint3D() (2 input parameters) Name: DrawPoint3D Return type: void Description: Draw a point in 3D space, actually a small line Param[1]: position (type: Vector3) Param[2]: color (type: Color) -Function 456: DrawCircle3D() (5 input parameters) +Function 457: DrawCircle3D() (5 input parameters) Name: DrawCircle3D Return type: void Description: Draw a circle in 3D world space @@ -3871,7 +3880,7 @@ Function 456: DrawCircle3D() (5 input parameters) Param[3]: rotationAxis (type: Vector3) Param[4]: rotationAngle (type: float) Param[5]: color (type: Color) -Function 457: DrawTriangle3D() (4 input parameters) +Function 458: DrawTriangle3D() (4 input parameters) Name: DrawTriangle3D Return type: void Description: Draw a color-filled triangle (vertex in counter-clockwise order!) @@ -3879,14 +3888,14 @@ Function 457: DrawTriangle3D() (4 input parameters) Param[2]: v2 (type: Vector3) Param[3]: v3 (type: Vector3) Param[4]: color (type: Color) -Function 458: DrawTriangleStrip3D() (3 input parameters) +Function 459: DrawTriangleStrip3D() (3 input parameters) Name: DrawTriangleStrip3D Return type: void Description: Draw a triangle strip defined by points Param[1]: points (type: const Vector3 *) Param[2]: pointCount (type: int) Param[3]: color (type: Color) -Function 459: DrawCube() (5 input parameters) +Function 460: DrawCube() (5 input parameters) Name: DrawCube Return type: void Description: Draw cube @@ -3895,14 +3904,14 @@ Function 459: DrawCube() (5 input parameters) Param[3]: height (type: float) Param[4]: length (type: float) Param[5]: color (type: Color) -Function 460: DrawCubeV() (3 input parameters) +Function 461: DrawCubeV() (3 input parameters) Name: DrawCubeV Return type: void Description: Draw cube (Vector version) Param[1]: position (type: Vector3) Param[2]: size (type: Vector3) Param[3]: color (type: Color) -Function 461: DrawCubeWires() (5 input parameters) +Function 462: DrawCubeWires() (5 input parameters) Name: DrawCubeWires Return type: void Description: Draw cube wires @@ -3911,21 +3920,21 @@ Function 461: DrawCubeWires() (5 input parameters) Param[3]: height (type: float) Param[4]: length (type: float) Param[5]: color (type: Color) -Function 462: DrawCubeWiresV() (3 input parameters) +Function 463: DrawCubeWiresV() (3 input parameters) Name: DrawCubeWiresV Return type: void Description: Draw cube wires (Vector version) Param[1]: position (type: Vector3) Param[2]: size (type: Vector3) Param[3]: color (type: Color) -Function 463: DrawSphere() (3 input parameters) +Function 464: DrawSphere() (3 input parameters) Name: DrawSphere Return type: void Description: Draw sphere Param[1]: centerPos (type: Vector3) Param[2]: radius (type: float) Param[3]: color (type: Color) -Function 464: DrawSphereEx() (5 input parameters) +Function 465: DrawSphereEx() (5 input parameters) Name: DrawSphereEx Return type: void Description: Draw sphere with extended parameters @@ -3934,7 +3943,7 @@ Function 464: DrawSphereEx() (5 input parameters) Param[3]: rings (type: int) Param[4]: slices (type: int) Param[5]: color (type: Color) -Function 465: DrawSphereWires() (5 input parameters) +Function 466: DrawSphereWires() (5 input parameters) Name: DrawSphereWires Return type: void Description: Draw sphere wires @@ -3943,7 +3952,7 @@ Function 465: DrawSphereWires() (5 input parameters) Param[3]: rings (type: int) Param[4]: slices (type: int) Param[5]: color (type: Color) -Function 466: DrawCylinder() (6 input parameters) +Function 467: DrawCylinder() (6 input parameters) Name: DrawCylinder Return type: void Description: Draw a cylinder/cone @@ -3953,7 +3962,7 @@ Function 466: DrawCylinder() (6 input parameters) Param[4]: height (type: float) Param[5]: slices (type: int) Param[6]: color (type: Color) -Function 467: DrawCylinderEx() (6 input parameters) +Function 468: DrawCylinderEx() (6 input parameters) Name: DrawCylinderEx Return type: void Description: Draw a cylinder with base at startPos and top at endPos @@ -3963,7 +3972,7 @@ Function 467: DrawCylinderEx() (6 input parameters) Param[4]: endRadius (type: float) Param[5]: sides (type: int) Param[6]: color (type: Color) -Function 468: DrawCylinderWires() (6 input parameters) +Function 469: DrawCylinderWires() (6 input parameters) Name: DrawCylinderWires Return type: void Description: Draw a cylinder/cone wires @@ -3973,7 +3982,7 @@ Function 468: DrawCylinderWires() (6 input parameters) Param[4]: height (type: float) Param[5]: slices (type: int) Param[6]: color (type: Color) -Function 469: DrawCylinderWiresEx() (6 input parameters) +Function 470: DrawCylinderWiresEx() (6 input parameters) Name: DrawCylinderWiresEx Return type: void Description: Draw a cylinder wires with base at startPos and top at endPos @@ -3983,7 +3992,7 @@ Function 469: DrawCylinderWiresEx() (6 input parameters) Param[4]: endRadius (type: float) Param[5]: sides (type: int) Param[6]: color (type: Color) -Function 470: DrawCapsule() (6 input parameters) +Function 471: DrawCapsule() (6 input parameters) Name: DrawCapsule Return type: void Description: Draw a capsule with the center of its sphere caps at startPos and endPos @@ -3993,7 +4002,7 @@ Function 470: DrawCapsule() (6 input parameters) Param[4]: slices (type: int) Param[5]: rings (type: int) Param[6]: color (type: Color) -Function 471: DrawCapsuleWires() (6 input parameters) +Function 472: DrawCapsuleWires() (6 input parameters) Name: DrawCapsuleWires Return type: void Description: Draw capsule wireframe with the center of its sphere caps at startPos and endPos @@ -4003,51 +4012,51 @@ Function 471: DrawCapsuleWires() (6 input parameters) Param[4]: slices (type: int) Param[5]: rings (type: int) Param[6]: color (type: Color) -Function 472: DrawPlane() (3 input parameters) +Function 473: DrawPlane() (3 input parameters) Name: DrawPlane Return type: void Description: Draw a plane XZ Param[1]: centerPos (type: Vector3) Param[2]: size (type: Vector2) Param[3]: color (type: Color) -Function 473: DrawRay() (2 input parameters) +Function 474: DrawRay() (2 input parameters) Name: DrawRay Return type: void Description: Draw a ray line Param[1]: ray (type: Ray) Param[2]: color (type: Color) -Function 474: DrawGrid() (2 input parameters) +Function 475: DrawGrid() (2 input parameters) Name: DrawGrid Return type: void Description: Draw a grid (centered at (0, 0, 0)) Param[1]: slices (type: int) Param[2]: spacing (type: float) -Function 475: LoadModel() (1 input parameters) +Function 476: LoadModel() (1 input parameters) Name: LoadModel Return type: Model Description: Load model from files (meshes and materials) Param[1]: fileName (type: const char *) -Function 476: LoadModelFromMesh() (1 input parameters) +Function 477: LoadModelFromMesh() (1 input parameters) Name: LoadModelFromMesh Return type: Model Description: Load model from generated mesh (default material) Param[1]: mesh (type: Mesh) -Function 477: IsModelValid() (1 input parameters) +Function 478: IsModelValid() (1 input parameters) Name: IsModelValid Return type: bool Description: Check if a model is valid (loaded in GPU, VAO/VBOs) Param[1]: model (type: Model) -Function 478: UnloadModel() (1 input parameters) +Function 479: UnloadModel() (1 input parameters) Name: UnloadModel Return type: void Description: Unload model (including meshes) from memory (RAM and/or VRAM) Param[1]: model (type: Model) -Function 479: GetModelBoundingBox() (1 input parameters) +Function 480: GetModelBoundingBox() (1 input parameters) Name: GetModelBoundingBox Return type: BoundingBox Description: Compute model bounding box limits (considers all meshes) Param[1]: model (type: Model) -Function 480: DrawModel() (4 input parameters) +Function 481: DrawModel() (4 input parameters) Name: DrawModel Return type: void Description: Draw a model (with texture if set) @@ -4055,7 +4064,7 @@ Function 480: DrawModel() (4 input parameters) Param[2]: position (type: Vector3) Param[3]: scale (type: float) Param[4]: tint (type: Color) -Function 481: DrawModelEx() (6 input parameters) +Function 482: DrawModelEx() (6 input parameters) Name: DrawModelEx Return type: void Description: Draw a model with extended parameters @@ -4065,7 +4074,7 @@ Function 481: DrawModelEx() (6 input parameters) Param[4]: rotationAngle (type: float) Param[5]: scale (type: Vector3) Param[6]: tint (type: Color) -Function 482: DrawModelWires() (4 input parameters) +Function 483: DrawModelWires() (4 input parameters) Name: DrawModelWires Return type: void Description: Draw a model wires (with texture if set) @@ -4073,7 +4082,7 @@ Function 482: DrawModelWires() (4 input parameters) Param[2]: position (type: Vector3) Param[3]: scale (type: float) Param[4]: tint (type: Color) -Function 483: DrawModelWiresEx() (6 input parameters) +Function 484: DrawModelWiresEx() (6 input parameters) Name: DrawModelWiresEx Return type: void Description: Draw a model wires (with texture if set) with extended parameters @@ -4083,7 +4092,7 @@ Function 483: DrawModelWiresEx() (6 input parameters) Param[4]: rotationAngle (type: float) Param[5]: scale (type: Vector3) Param[6]: tint (type: Color) -Function 484: DrawModelPoints() (4 input parameters) +Function 485: DrawModelPoints() (4 input parameters) Name: DrawModelPoints Return type: void Description: Draw a model as points @@ -4091,7 +4100,7 @@ Function 484: DrawModelPoints() (4 input parameters) Param[2]: position (type: Vector3) Param[3]: scale (type: float) Param[4]: tint (type: Color) -Function 485: DrawModelPointsEx() (6 input parameters) +Function 486: DrawModelPointsEx() (6 input parameters) Name: DrawModelPointsEx Return type: void Description: Draw a model as points with extended parameters @@ -4101,13 +4110,13 @@ Function 485: DrawModelPointsEx() (6 input parameters) Param[4]: rotationAngle (type: float) Param[5]: scale (type: Vector3) Param[6]: tint (type: Color) -Function 486: DrawBoundingBox() (2 input parameters) +Function 487: DrawBoundingBox() (2 input parameters) Name: DrawBoundingBox Return type: void Description: Draw bounding box (wires) Param[1]: box (type: BoundingBox) Param[2]: color (type: Color) -Function 487: DrawBillboard() (5 input parameters) +Function 488: DrawBillboard() (5 input parameters) Name: DrawBillboard Return type: void Description: Draw a billboard texture @@ -4116,7 +4125,7 @@ Function 487: DrawBillboard() (5 input parameters) Param[3]: position (type: Vector3) Param[4]: scale (type: float) Param[5]: tint (type: Color) -Function 488: DrawBillboardRec() (6 input parameters) +Function 489: DrawBillboardRec() (6 input parameters) Name: DrawBillboardRec Return type: void Description: Draw a billboard texture defined by source @@ -4126,7 +4135,7 @@ Function 488: DrawBillboardRec() (6 input parameters) Param[4]: position (type: Vector3) Param[5]: size (type: Vector2) Param[6]: tint (type: Color) -Function 489: DrawBillboardPro() (9 input parameters) +Function 490: DrawBillboardPro() (9 input parameters) Name: DrawBillboardPro Return type: void Description: Draw a billboard texture defined by source and rotation @@ -4139,13 +4148,13 @@ Function 489: DrawBillboardPro() (9 input parameters) Param[7]: origin (type: Vector2) Param[8]: rotation (type: float) Param[9]: tint (type: Color) -Function 490: UploadMesh() (2 input parameters) +Function 491: UploadMesh() (2 input parameters) Name: UploadMesh Return type: void Description: Upload mesh vertex data in GPU and provide VAO/VBO ids Param[1]: mesh (type: Mesh *) Param[2]: dynamic (type: bool) -Function 491: UpdateMeshBuffer() (5 input parameters) +Function 492: UpdateMeshBuffer() (5 input parameters) Name: UpdateMeshBuffer Return type: void Description: Update mesh vertex data in GPU for a specific buffer index @@ -4154,19 +4163,19 @@ Function 491: UpdateMeshBuffer() (5 input parameters) Param[3]: data (type: const void *) Param[4]: dataSize (type: int) Param[5]: offset (type: int) -Function 492: UnloadMesh() (1 input parameters) +Function 493: UnloadMesh() (1 input parameters) Name: UnloadMesh Return type: void Description: Unload mesh data from CPU and GPU Param[1]: mesh (type: Mesh) -Function 493: DrawMesh() (3 input parameters) +Function 494: DrawMesh() (3 input parameters) Name: DrawMesh Return type: void Description: Draw a 3d mesh with material and transform Param[1]: mesh (type: Mesh) Param[2]: material (type: Material) Param[3]: transform (type: Matrix) -Function 494: DrawMeshInstanced() (4 input parameters) +Function 495: DrawMeshInstanced() (4 input parameters) Name: DrawMeshInstanced Return type: void Description: Draw multiple mesh instances with material and different transforms @@ -4174,35 +4183,35 @@ Function 494: DrawMeshInstanced() (4 input parameters) Param[2]: material (type: Material) Param[3]: transforms (type: const Matrix *) Param[4]: instances (type: int) -Function 495: GetMeshBoundingBox() (1 input parameters) +Function 496: GetMeshBoundingBox() (1 input parameters) Name: GetMeshBoundingBox Return type: BoundingBox Description: Compute mesh bounding box limits Param[1]: mesh (type: Mesh) -Function 496: GenMeshTangents() (1 input parameters) +Function 497: GenMeshTangents() (1 input parameters) Name: GenMeshTangents Return type: void Description: Compute mesh tangents Param[1]: mesh (type: Mesh *) -Function 497: ExportMesh() (2 input parameters) +Function 498: ExportMesh() (2 input parameters) Name: ExportMesh Return type: bool Description: Export mesh data to file, returns true on success Param[1]: mesh (type: Mesh) Param[2]: fileName (type: const char *) -Function 498: ExportMeshAsCode() (2 input parameters) +Function 499: ExportMeshAsCode() (2 input parameters) Name: ExportMeshAsCode Return type: bool Description: Export mesh as code file (.h) defining multiple arrays of vertex attributes Param[1]: mesh (type: Mesh) Param[2]: fileName (type: const char *) -Function 499: GenMeshPoly() (2 input parameters) +Function 500: GenMeshPoly() (2 input parameters) Name: GenMeshPoly Return type: Mesh Description: Generate polygonal mesh Param[1]: sides (type: int) Param[2]: radius (type: float) -Function 500: GenMeshPlane() (4 input parameters) +Function 501: GenMeshPlane() (4 input parameters) Name: GenMeshPlane Return type: Mesh Description: Generate plane mesh (with subdivisions) @@ -4210,42 +4219,42 @@ Function 500: GenMeshPlane() (4 input parameters) Param[2]: length (type: float) Param[3]: resX (type: int) Param[4]: resZ (type: int) -Function 501: GenMeshCube() (3 input parameters) +Function 502: GenMeshCube() (3 input parameters) Name: GenMeshCube Return type: Mesh Description: Generate cuboid mesh Param[1]: width (type: float) Param[2]: height (type: float) Param[3]: length (type: float) -Function 502: GenMeshSphere() (3 input parameters) +Function 503: GenMeshSphere() (3 input parameters) Name: GenMeshSphere Return type: Mesh Description: Generate sphere mesh (standard sphere) Param[1]: radius (type: float) Param[2]: rings (type: int) Param[3]: slices (type: int) -Function 503: GenMeshHemiSphere() (3 input parameters) +Function 504: GenMeshHemiSphere() (3 input parameters) Name: GenMeshHemiSphere Return type: Mesh Description: Generate half-sphere mesh (no bottom cap) Param[1]: radius (type: float) Param[2]: rings (type: int) Param[3]: slices (type: int) -Function 504: GenMeshCylinder() (3 input parameters) +Function 505: GenMeshCylinder() (3 input parameters) Name: GenMeshCylinder Return type: Mesh Description: Generate cylinder mesh Param[1]: radius (type: float) Param[2]: height (type: float) Param[3]: slices (type: int) -Function 505: GenMeshCone() (3 input parameters) +Function 506: GenMeshCone() (3 input parameters) Name: GenMeshCone Return type: Mesh Description: Generate cone/pyramid mesh Param[1]: radius (type: float) Param[2]: height (type: float) Param[3]: slices (type: int) -Function 506: GenMeshTorus() (4 input parameters) +Function 507: GenMeshTorus() (4 input parameters) Name: GenMeshTorus Return type: Mesh Description: Generate torus mesh @@ -4253,7 +4262,7 @@ Function 506: GenMeshTorus() (4 input parameters) Param[2]: size (type: float) Param[3]: radSeg (type: int) Param[4]: sides (type: int) -Function 507: GenMeshKnot() (4 input parameters) +Function 508: GenMeshKnot() (4 input parameters) Name: GenMeshKnot Return type: Mesh Description: Generate trefoil knot mesh @@ -4261,91 +4270,91 @@ Function 507: GenMeshKnot() (4 input parameters) Param[2]: size (type: float) Param[3]: radSeg (type: int) Param[4]: sides (type: int) -Function 508: GenMeshHeightmap() (2 input parameters) +Function 509: GenMeshHeightmap() (2 input parameters) Name: GenMeshHeightmap Return type: Mesh Description: Generate heightmap mesh from image data Param[1]: heightmap (type: Image) Param[2]: size (type: Vector3) -Function 509: GenMeshCubicmap() (2 input parameters) +Function 510: GenMeshCubicmap() (2 input parameters) Name: GenMeshCubicmap Return type: Mesh Description: Generate cubes-based map mesh from image data Param[1]: cubicmap (type: Image) Param[2]: cubeSize (type: Vector3) -Function 510: LoadMaterials() (2 input parameters) +Function 511: LoadMaterials() (2 input parameters) Name: LoadMaterials Return type: Material * Description: Load materials from model file Param[1]: fileName (type: const char *) Param[2]: materialCount (type: int *) -Function 511: LoadMaterialDefault() (0 input parameters) +Function 512: LoadMaterialDefault() (0 input parameters) Name: LoadMaterialDefault Return type: Material Description: Load default material (Supports: DIFFUSE, SPECULAR, NORMAL maps) No input parameters -Function 512: IsMaterialValid() (1 input parameters) +Function 513: IsMaterialValid() (1 input parameters) Name: IsMaterialValid Return type: bool Description: Check if a material is valid (shader assigned, map textures loaded in GPU) Param[1]: material (type: Material) -Function 513: UnloadMaterial() (1 input parameters) +Function 514: UnloadMaterial() (1 input parameters) Name: UnloadMaterial Return type: void Description: Unload material from GPU memory (VRAM) Param[1]: material (type: Material) -Function 514: SetMaterialTexture() (3 input parameters) +Function 515: SetMaterialTexture() (3 input parameters) Name: SetMaterialTexture Return type: void Description: Set texture for a material map type (MATERIAL_MAP_DIFFUSE, MATERIAL_MAP_SPECULAR...) Param[1]: material (type: Material *) Param[2]: mapType (type: int) Param[3]: texture (type: Texture2D) -Function 515: SetModelMeshMaterial() (3 input parameters) +Function 516: SetModelMeshMaterial() (3 input parameters) Name: SetModelMeshMaterial Return type: void Description: Set material for a mesh Param[1]: model (type: Model *) Param[2]: meshId (type: int) Param[3]: materialId (type: int) -Function 516: LoadModelAnimations() (2 input parameters) +Function 517: LoadModelAnimations() (2 input parameters) Name: LoadModelAnimations Return type: ModelAnimation * Description: Load model animations from file Param[1]: fileName (type: const char *) Param[2]: animCount (type: int *) -Function 517: UpdateModelAnimation() (3 input parameters) +Function 518: UpdateModelAnimation() (3 input parameters) Name: UpdateModelAnimation Return type: void Description: Update model animation pose (CPU) Param[1]: model (type: Model) Param[2]: anim (type: ModelAnimation) Param[3]: frame (type: int) -Function 518: UpdateModelAnimationBones() (3 input parameters) +Function 519: UpdateModelAnimationBones() (3 input parameters) Name: UpdateModelAnimationBones Return type: void Description: Update model animation mesh bone matrices (GPU skinning) Param[1]: model (type: Model) Param[2]: anim (type: ModelAnimation) Param[3]: frame (type: int) -Function 519: UnloadModelAnimation() (1 input parameters) +Function 520: UnloadModelAnimation() (1 input parameters) Name: UnloadModelAnimation Return type: void Description: Unload animation data Param[1]: anim (type: ModelAnimation) -Function 520: UnloadModelAnimations() (2 input parameters) +Function 521: UnloadModelAnimations() (2 input parameters) Name: UnloadModelAnimations Return type: void Description: Unload animation array data Param[1]: animations (type: ModelAnimation *) Param[2]: animCount (type: int) -Function 521: IsModelAnimationValid() (2 input parameters) +Function 522: IsModelAnimationValid() (2 input parameters) Name: IsModelAnimationValid Return type: bool Description: Check model animation skeleton match Param[1]: model (type: Model) Param[2]: anim (type: ModelAnimation) -Function 522: CheckCollisionSpheres() (4 input parameters) +Function 523: CheckCollisionSpheres() (4 input parameters) Name: CheckCollisionSpheres Return type: bool Description: Check collision between two spheres @@ -4353,40 +4362,40 @@ Function 522: CheckCollisionSpheres() (4 input parameters) Param[2]: radius1 (type: float) Param[3]: center2 (type: Vector3) Param[4]: radius2 (type: float) -Function 523: CheckCollisionBoxes() (2 input parameters) +Function 524: CheckCollisionBoxes() (2 input parameters) Name: CheckCollisionBoxes Return type: bool Description: Check collision between two bounding boxes Param[1]: box1 (type: BoundingBox) Param[2]: box2 (type: BoundingBox) -Function 524: CheckCollisionBoxSphere() (3 input parameters) +Function 525: CheckCollisionBoxSphere() (3 input parameters) Name: CheckCollisionBoxSphere Return type: bool Description: Check collision between box and sphere Param[1]: box (type: BoundingBox) Param[2]: center (type: Vector3) Param[3]: radius (type: float) -Function 525: GetRayCollisionSphere() (3 input parameters) +Function 526: GetRayCollisionSphere() (3 input parameters) Name: GetRayCollisionSphere Return type: RayCollision Description: Get collision info between ray and sphere Param[1]: ray (type: Ray) Param[2]: center (type: Vector3) Param[3]: radius (type: float) -Function 526: GetRayCollisionBox() (2 input parameters) +Function 527: GetRayCollisionBox() (2 input parameters) Name: GetRayCollisionBox Return type: RayCollision Description: Get collision info between ray and box Param[1]: ray (type: Ray) Param[2]: box (type: BoundingBox) -Function 527: GetRayCollisionMesh() (3 input parameters) +Function 528: GetRayCollisionMesh() (3 input parameters) Name: GetRayCollisionMesh Return type: RayCollision Description: Get collision info between ray and mesh Param[1]: ray (type: Ray) Param[2]: mesh (type: Mesh) Param[3]: transform (type: Matrix) -Function 528: GetRayCollisionTriangle() (4 input parameters) +Function 529: GetRayCollisionTriangle() (4 input parameters) Name: GetRayCollisionTriangle Return type: RayCollision Description: Get collision info between ray and triangle @@ -4394,7 +4403,7 @@ Function 528: GetRayCollisionTriangle() (4 input parameters) Param[2]: p1 (type: Vector3) Param[3]: p2 (type: Vector3) Param[4]: p3 (type: Vector3) -Function 529: GetRayCollisionQuad() (5 input parameters) +Function 530: GetRayCollisionQuad() (5 input parameters) Name: GetRayCollisionQuad Return type: RayCollision Description: Get collision info between ray and quad @@ -4403,158 +4412,158 @@ Function 529: GetRayCollisionQuad() (5 input parameters) Param[3]: p2 (type: Vector3) Param[4]: p3 (type: Vector3) Param[5]: p4 (type: Vector3) -Function 530: InitAudioDevice() (0 input parameters) +Function 531: InitAudioDevice() (0 input parameters) Name: InitAudioDevice Return type: void Description: Initialize audio device and context No input parameters -Function 531: CloseAudioDevice() (0 input parameters) +Function 532: CloseAudioDevice() (0 input parameters) Name: CloseAudioDevice Return type: void Description: Close the audio device and context No input parameters -Function 532: IsAudioDeviceReady() (0 input parameters) +Function 533: IsAudioDeviceReady() (0 input parameters) Name: IsAudioDeviceReady Return type: bool Description: Check if audio device has been initialized successfully No input parameters -Function 533: SetMasterVolume() (1 input parameters) +Function 534: SetMasterVolume() (1 input parameters) Name: SetMasterVolume Return type: void Description: Set master volume (listener) Param[1]: volume (type: float) -Function 534: GetMasterVolume() (0 input parameters) +Function 535: GetMasterVolume() (0 input parameters) Name: GetMasterVolume Return type: float Description: Get master volume (listener) No input parameters -Function 535: LoadWave() (1 input parameters) +Function 536: LoadWave() (1 input parameters) Name: LoadWave Return type: Wave Description: Load wave data from file Param[1]: fileName (type: const char *) -Function 536: LoadWaveFromMemory() (3 input parameters) +Function 537: LoadWaveFromMemory() (3 input parameters) Name: LoadWaveFromMemory Return type: Wave Description: Load wave from memory buffer, fileType refers to extension: i.e. '.wav' Param[1]: fileType (type: const char *) Param[2]: fileData (type: const unsigned char *) Param[3]: dataSize (type: int) -Function 537: IsWaveValid() (1 input parameters) +Function 538: IsWaveValid() (1 input parameters) Name: IsWaveValid Return type: bool Description: Checks if wave data is valid (data loaded and parameters) Param[1]: wave (type: Wave) -Function 538: LoadSound() (1 input parameters) +Function 539: LoadSound() (1 input parameters) Name: LoadSound Return type: Sound Description: Load sound from file Param[1]: fileName (type: const char *) -Function 539: LoadSoundFromWave() (1 input parameters) +Function 540: LoadSoundFromWave() (1 input parameters) Name: LoadSoundFromWave Return type: Sound Description: Load sound from wave data Param[1]: wave (type: Wave) -Function 540: LoadSoundAlias() (1 input parameters) +Function 541: LoadSoundAlias() (1 input parameters) Name: LoadSoundAlias Return type: Sound Description: Create a new sound that shares the same sample data as the source sound, does not own the sound data Param[1]: source (type: Sound) -Function 541: IsSoundValid() (1 input parameters) +Function 542: IsSoundValid() (1 input parameters) Name: IsSoundValid Return type: bool Description: Checks if a sound is valid (data loaded and buffers initialized) Param[1]: sound (type: Sound) -Function 542: UpdateSound() (3 input parameters) +Function 543: UpdateSound() (3 input parameters) Name: UpdateSound Return type: void Description: Update sound buffer with new data (data and frame count should fit in sound) Param[1]: sound (type: Sound) Param[2]: data (type: const void *) Param[3]: sampleCount (type: int) -Function 543: UnloadWave() (1 input parameters) +Function 544: UnloadWave() (1 input parameters) Name: UnloadWave Return type: void Description: Unload wave data Param[1]: wave (type: Wave) -Function 544: UnloadSound() (1 input parameters) +Function 545: UnloadSound() (1 input parameters) Name: UnloadSound Return type: void Description: Unload sound Param[1]: sound (type: Sound) -Function 545: UnloadSoundAlias() (1 input parameters) +Function 546: UnloadSoundAlias() (1 input parameters) Name: UnloadSoundAlias Return type: void Description: Unload a sound alias (does not deallocate sample data) Param[1]: alias (type: Sound) -Function 546: ExportWave() (2 input parameters) +Function 547: ExportWave() (2 input parameters) Name: ExportWave Return type: bool Description: Export wave data to file, returns true on success Param[1]: wave (type: Wave) Param[2]: fileName (type: const char *) -Function 547: ExportWaveAsCode() (2 input parameters) +Function 548: ExportWaveAsCode() (2 input parameters) Name: ExportWaveAsCode Return type: bool Description: Export wave sample data to code (.h), returns true on success Param[1]: wave (type: Wave) Param[2]: fileName (type: const char *) -Function 548: PlaySound() (1 input parameters) +Function 549: PlaySound() (1 input parameters) Name: PlaySound Return type: void Description: Play a sound Param[1]: sound (type: Sound) -Function 549: StopSound() (1 input parameters) +Function 550: StopSound() (1 input parameters) Name: StopSound Return type: void Description: Stop playing a sound Param[1]: sound (type: Sound) -Function 550: PauseSound() (1 input parameters) +Function 551: PauseSound() (1 input parameters) Name: PauseSound Return type: void Description: Pause a sound Param[1]: sound (type: Sound) -Function 551: ResumeSound() (1 input parameters) +Function 552: ResumeSound() (1 input parameters) Name: ResumeSound Return type: void Description: Resume a paused sound Param[1]: sound (type: Sound) -Function 552: IsSoundPlaying() (1 input parameters) +Function 553: IsSoundPlaying() (1 input parameters) Name: IsSoundPlaying Return type: bool Description: Check if a sound is currently playing Param[1]: sound (type: Sound) -Function 553: SetSoundVolume() (2 input parameters) +Function 554: SetSoundVolume() (2 input parameters) Name: SetSoundVolume Return type: void Description: Set volume for a sound (1.0 is max level) Param[1]: sound (type: Sound) Param[2]: volume (type: float) -Function 554: SetSoundPitch() (2 input parameters) +Function 555: SetSoundPitch() (2 input parameters) Name: SetSoundPitch Return type: void Description: Set pitch for a sound (1.0 is base level) Param[1]: sound (type: Sound) Param[2]: pitch (type: float) -Function 555: SetSoundPan() (2 input parameters) +Function 556: SetSoundPan() (2 input parameters) Name: SetSoundPan Return type: void Description: Set pan for a sound (0.5 is center) Param[1]: sound (type: Sound) Param[2]: pan (type: float) -Function 556: WaveCopy() (1 input parameters) +Function 557: WaveCopy() (1 input parameters) Name: WaveCopy Return type: Wave Description: Copy a wave to a new wave Param[1]: wave (type: Wave) -Function 557: WaveCrop() (3 input parameters) +Function 558: WaveCrop() (3 input parameters) Name: WaveCrop Return type: void Description: Crop a wave to defined frames range Param[1]: wave (type: Wave *) Param[2]: initFrame (type: int) Param[3]: finalFrame (type: int) -Function 558: WaveFormat() (4 input parameters) +Function 559: WaveFormat() (4 input parameters) Name: WaveFormat Return type: void Description: Convert wave data to desired format @@ -4562,203 +4571,203 @@ Function 558: WaveFormat() (4 input parameters) Param[2]: sampleRate (type: int) Param[3]: sampleSize (type: int) Param[4]: channels (type: int) -Function 559: LoadWaveSamples() (1 input parameters) +Function 560: LoadWaveSamples() (1 input parameters) Name: LoadWaveSamples Return type: float * Description: Load samples data from wave as a 32bit float data array Param[1]: wave (type: Wave) -Function 560: UnloadWaveSamples() (1 input parameters) +Function 561: UnloadWaveSamples() (1 input parameters) Name: UnloadWaveSamples Return type: void Description: Unload samples data loaded with LoadWaveSamples() Param[1]: samples (type: float *) -Function 561: LoadMusicStream() (1 input parameters) +Function 562: LoadMusicStream() (1 input parameters) Name: LoadMusicStream Return type: Music Description: Load music stream from file Param[1]: fileName (type: const char *) -Function 562: LoadMusicStreamFromMemory() (3 input parameters) +Function 563: LoadMusicStreamFromMemory() (3 input parameters) Name: LoadMusicStreamFromMemory Return type: Music Description: Load music stream from data Param[1]: fileType (type: const char *) Param[2]: data (type: const unsigned char *) Param[3]: dataSize (type: int) -Function 563: IsMusicValid() (1 input parameters) +Function 564: IsMusicValid() (1 input parameters) Name: IsMusicValid Return type: bool Description: Checks if a music stream is valid (context and buffers initialized) Param[1]: music (type: Music) -Function 564: UnloadMusicStream() (1 input parameters) +Function 565: UnloadMusicStream() (1 input parameters) Name: UnloadMusicStream Return type: void Description: Unload music stream Param[1]: music (type: Music) -Function 565: PlayMusicStream() (1 input parameters) +Function 566: PlayMusicStream() (1 input parameters) Name: PlayMusicStream Return type: void Description: Start music playing Param[1]: music (type: Music) -Function 566: IsMusicStreamPlaying() (1 input parameters) +Function 567: IsMusicStreamPlaying() (1 input parameters) Name: IsMusicStreamPlaying Return type: bool Description: Check if music is playing Param[1]: music (type: Music) -Function 567: UpdateMusicStream() (1 input parameters) +Function 568: UpdateMusicStream() (1 input parameters) Name: UpdateMusicStream Return type: void Description: Updates buffers for music streaming Param[1]: music (type: Music) -Function 568: StopMusicStream() (1 input parameters) +Function 569: StopMusicStream() (1 input parameters) Name: StopMusicStream Return type: void Description: Stop music playing Param[1]: music (type: Music) -Function 569: PauseMusicStream() (1 input parameters) +Function 570: PauseMusicStream() (1 input parameters) Name: PauseMusicStream Return type: void Description: Pause music playing Param[1]: music (type: Music) -Function 570: ResumeMusicStream() (1 input parameters) +Function 571: ResumeMusicStream() (1 input parameters) Name: ResumeMusicStream Return type: void Description: Resume playing paused music Param[1]: music (type: Music) -Function 571: SeekMusicStream() (2 input parameters) +Function 572: SeekMusicStream() (2 input parameters) Name: SeekMusicStream Return type: void Description: Seek music to a position (in seconds) Param[1]: music (type: Music) Param[2]: position (type: float) -Function 572: SetMusicVolume() (2 input parameters) +Function 573: SetMusicVolume() (2 input parameters) Name: SetMusicVolume Return type: void Description: Set volume for music (1.0 is max level) Param[1]: music (type: Music) Param[2]: volume (type: float) -Function 573: SetMusicPitch() (2 input parameters) +Function 574: SetMusicPitch() (2 input parameters) Name: SetMusicPitch Return type: void Description: Set pitch for a music (1.0 is base level) Param[1]: music (type: Music) Param[2]: pitch (type: float) -Function 574: SetMusicPan() (2 input parameters) +Function 575: SetMusicPan() (2 input parameters) Name: SetMusicPan Return type: void Description: Set pan for a music (0.5 is center) Param[1]: music (type: Music) Param[2]: pan (type: float) -Function 575: GetMusicTimeLength() (1 input parameters) +Function 576: GetMusicTimeLength() (1 input parameters) Name: GetMusicTimeLength Return type: float Description: Get music time length (in seconds) Param[1]: music (type: Music) -Function 576: GetMusicTimePlayed() (1 input parameters) +Function 577: GetMusicTimePlayed() (1 input parameters) Name: GetMusicTimePlayed Return type: float Description: Get current music time played (in seconds) Param[1]: music (type: Music) -Function 577: LoadAudioStream() (3 input parameters) +Function 578: LoadAudioStream() (3 input parameters) Name: LoadAudioStream Return type: AudioStream Description: Load audio stream (to stream raw audio pcm data) Param[1]: sampleRate (type: unsigned int) Param[2]: sampleSize (type: unsigned int) Param[3]: channels (type: unsigned int) -Function 578: IsAudioStreamValid() (1 input parameters) +Function 579: IsAudioStreamValid() (1 input parameters) Name: IsAudioStreamValid Return type: bool Description: Checks if an audio stream is valid (buffers initialized) Param[1]: stream (type: AudioStream) -Function 579: UnloadAudioStream() (1 input parameters) +Function 580: UnloadAudioStream() (1 input parameters) Name: UnloadAudioStream Return type: void Description: Unload audio stream and free memory Param[1]: stream (type: AudioStream) -Function 580: UpdateAudioStream() (3 input parameters) +Function 581: UpdateAudioStream() (3 input parameters) Name: UpdateAudioStream Return type: void Description: Update audio stream buffers with data Param[1]: stream (type: AudioStream) Param[2]: data (type: const void *) Param[3]: frameCount (type: int) -Function 581: IsAudioStreamProcessed() (1 input parameters) +Function 582: IsAudioStreamProcessed() (1 input parameters) Name: IsAudioStreamProcessed Return type: bool Description: Check if any audio stream buffers requires refill Param[1]: stream (type: AudioStream) -Function 582: PlayAudioStream() (1 input parameters) +Function 583: PlayAudioStream() (1 input parameters) Name: PlayAudioStream Return type: void Description: Play audio stream Param[1]: stream (type: AudioStream) -Function 583: PauseAudioStream() (1 input parameters) +Function 584: PauseAudioStream() (1 input parameters) Name: PauseAudioStream Return type: void Description: Pause audio stream Param[1]: stream (type: AudioStream) -Function 584: ResumeAudioStream() (1 input parameters) +Function 585: ResumeAudioStream() (1 input parameters) Name: ResumeAudioStream Return type: void Description: Resume audio stream Param[1]: stream (type: AudioStream) -Function 585: IsAudioStreamPlaying() (1 input parameters) +Function 586: IsAudioStreamPlaying() (1 input parameters) Name: IsAudioStreamPlaying Return type: bool Description: Check if audio stream is playing Param[1]: stream (type: AudioStream) -Function 586: StopAudioStream() (1 input parameters) +Function 587: StopAudioStream() (1 input parameters) Name: StopAudioStream Return type: void Description: Stop audio stream Param[1]: stream (type: AudioStream) -Function 587: SetAudioStreamVolume() (2 input parameters) +Function 588: SetAudioStreamVolume() (2 input parameters) Name: SetAudioStreamVolume Return type: void Description: Set volume for audio stream (1.0 is max level) Param[1]: stream (type: AudioStream) Param[2]: volume (type: float) -Function 588: SetAudioStreamPitch() (2 input parameters) +Function 589: SetAudioStreamPitch() (2 input parameters) Name: SetAudioStreamPitch Return type: void Description: Set pitch for audio stream (1.0 is base level) Param[1]: stream (type: AudioStream) Param[2]: pitch (type: float) -Function 589: SetAudioStreamPan() (2 input parameters) +Function 590: SetAudioStreamPan() (2 input parameters) Name: SetAudioStreamPan Return type: void Description: Set pan for audio stream (0.5 is centered) Param[1]: stream (type: AudioStream) Param[2]: pan (type: float) -Function 590: SetAudioStreamBufferSizeDefault() (1 input parameters) +Function 591: SetAudioStreamBufferSizeDefault() (1 input parameters) Name: SetAudioStreamBufferSizeDefault Return type: void Description: Default size for new audio streams Param[1]: size (type: int) -Function 591: SetAudioStreamCallback() (2 input parameters) +Function 592: SetAudioStreamCallback() (2 input parameters) Name: SetAudioStreamCallback Return type: void Description: Audio thread callback to request new data Param[1]: stream (type: AudioStream) Param[2]: callback (type: AudioCallback) -Function 592: AttachAudioStreamProcessor() (2 input parameters) +Function 593: AttachAudioStreamProcessor() (2 input parameters) Name: AttachAudioStreamProcessor Return type: void Description: Attach audio stream processor to stream, receives frames x 2 samples as 'float' (stereo) Param[1]: stream (type: AudioStream) Param[2]: processor (type: AudioCallback) -Function 593: DetachAudioStreamProcessor() (2 input parameters) +Function 594: DetachAudioStreamProcessor() (2 input parameters) Name: DetachAudioStreamProcessor Return type: void Description: Detach audio stream processor from stream Param[1]: stream (type: AudioStream) Param[2]: processor (type: AudioCallback) -Function 594: AttachAudioMixedProcessor() (1 input parameters) +Function 595: AttachAudioMixedProcessor() (1 input parameters) Name: AttachAudioMixedProcessor Return type: void Description: Attach audio stream processor to the entire audio pipeline, receives frames x 2 samples as 'float' (stereo) Param[1]: processor (type: AudioCallback) -Function 595: DetachAudioMixedProcessor() (1 input parameters) +Function 596: DetachAudioMixedProcessor() (1 input parameters) Name: DetachAudioMixedProcessor Return type: void Description: Detach audio stream processor from the entire audio pipeline diff --git a/tools/parser/output/raylib_api.xml b/tools/parser/output/raylib_api.xml index 6ea73d8dc..29546a2a8 100644 --- a/tools/parser/output/raylib_api.xml +++ b/tools/parser/output/raylib_api.xml @@ -679,7 +679,7 @@ - + @@ -1359,6 +1359,13 @@ + + + + + + + diff --git a/tools/rexm/Makefile b/tools/rexm/Makefile index f5e7d1fe4..d7d71f64f 100644 --- a/tools/rexm/Makefile +++ b/tools/rexm/Makefile @@ -35,7 +35,7 @@ PROJECT_BUILD_PATH ?= . PROJECT_SOURCE_FILES ?= rexm.c # raylib library variables -RAYLIB_SRC_PATH ?= ..\..\src +RAYLIB_SRC_PATH ?= ../../src RAYLIB_INCLUDE_PATH ?= $(RAYLIB_SRC_PATH) RAYLIB_LIB_PATH ?= $(RAYLIB_SRC_PATH) @@ -236,7 +236,7 @@ ifeq ($(PLATFORM),PLATFORM_WEB) # --source-map-base # allow debugging in browser with source map # --shell-file shell.html # define a custom shell .html and output extension LDFLAGS += -sUSE_GLFW=3 -sTOTAL_MEMORY=$(BUILD_WEB_HEAP_SIZE) -sSTACK_SIZE=$(BUILD_WEB_STACK_SIZE) -sFORCE_FILESYSTEM=1 -sMINIFY_HTML=0 - + # Build using asyncify ifeq ($(BUILD_WEB_ASYNCIFY),TRUE) LDFLAGS += -sASYNCIFY -sASYNCIFY_STACK_SIZE=$(BUILD_WEB_ASYNCIFY_STACK_SIZE) @@ -363,4 +363,3 @@ ifeq ($(PLATFORM),PLATFORM_WEB) del *.o *.html *.js endif @echo Cleaning done - diff --git a/tools/rexm/examples_report.md b/tools/rexm/examples_report.md index bb37b2db7..7c9de0b57 100644 --- a/tools/rexm/examples_report.md +++ b/tools/rexm/examples_report.md @@ -57,6 +57,7 @@ Example elements validated: | core_high_dpi | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | core_render_texture | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | core_undo_redo | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| core_input_actions | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | shapes_basic_shapes | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | shapes_bouncing_ball | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | shapes_colors_palette | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | diff --git a/tools/rexm/rexm.c b/tools/rexm/rexm.c index 7f303538c..3306e613a 100644 --- a/tools/rexm/rexm.c +++ b/tools/rexm/rexm.c @@ -205,12 +205,22 @@ int main(int argc, char *argv[]) exCollectionFilePath = getenv("REXM_EXAMPLES_COLLECTION_FILE_PATH"); exVSProjectSolutionFile = getenv("REXM_EXAMPLES_VS2022_SLN_FILE"); +#if defined(_WIN32) if (!exBasePath) exBasePath = "C:/GitHub/raylib/examples"; if (!exWebPath) exWebPath = "C:/GitHub/raylib.com/examples"; if (!exTemplateFilePath) exTemplateFilePath = "C:/GitHub/raylib/examples/examples_template.c"; if (!exTemplateScreenshot) exTemplateScreenshot = "C:/GitHub/raylib/examples/examples_template.png"; if (!exCollectionFilePath) exCollectionFilePath = "C:/GitHub/raylib/examples/examples_list.txt"; if (!exVSProjectSolutionFile) exVSProjectSolutionFile = "C:/GitHub/raylib/projects/VS2022/raylib.sln"; +#else + // Cross-platform relative fallbacks (run from tools/rexm directory) + if (!exBasePath) exBasePath = "../../examples"; + if (!exWebPath) exWebPath = "../../raylib.com/examples"; + if (!exTemplateFilePath) exTemplateFilePath = "../../examples/examples_template.c"; + if (!exTemplateScreenshot) exTemplateScreenshot = "../../examples/examples_template.png"; + if (!exCollectionFilePath) exCollectionFilePath = "../../examples/examples_list.txt"; + if (!exVSProjectSolutionFile) exVSProjectSolutionFile = "../../projects/VS2022/raylib.sln"; +#endif char inFileName[1024] = { 0 }; // Example input filename (to be added) @@ -1660,54 +1670,67 @@ static int UpdateRequiredFiles(void) // NOTE: Entries format: exampleEntry('⭐️☆☆☆' , 'core' , 'basic_window'), //------------------------------------------------------------------------------------------------ char *jsText = LoadFileText(TextFormat("%s/../common/examples.js", exWebPath)); - char *jsTextUpdated = (char *)RL_CALLOC(REXM_MAX_BUFFER_SIZE, 1); // Updated examples.js copy, 2MB - - int jsListStartIndex = TextFindIndex(jsText, "//EXAMPLE_DATA_LIST_START"); - int jsListEndIndex = TextFindIndex(jsText, "//EXAMPLE_DATA_LIST_END"); - - int jsIndex = 0; - memcpy(jsTextUpdated, jsText, jsListStartIndex); - jsIndex = sprintf(jsTextUpdated + jsListStartIndex, "//EXAMPLE_DATA_LIST_START\n"); - jsIndex += sprintf(jsTextUpdated + jsListStartIndex + jsIndex, " var exampleData = [\n"); - - char starsText[16] = { 0 }; - - // NOTE: We avoid "others" category - for (int i = 0; i < REXM_MAX_EXAMPLE_CATEGORIES - 1; i++) + if (!jsText) { - int exCollectionCount = 0; - rlExampleInfo *exCollection = LoadExamplesData(exCollectionFilePath, exCategories[i], false, &exCollectionCount); - for (int x = 0; x < exCollectionCount; x++) - { - for (int s = 0; s < 4; s++) - { - if (s < exCollection[x].stars) strcpy(starsText + 3*s, "⭐️"); // WARNING: Different than '★', more visual - else strcpy(starsText + 3*s, "☆"); - } - - if ((i == 6) && (x == (exCollectionCount - 1))) - { - // NOTE: Last line to add, special case to consider - jsIndex += sprintf(jsTextUpdated + jsListStartIndex + jsIndex, - TextFormat(" exampleEntry('%s', '%s', '%s')];\n", starsText, exCollection[x].category, exCollection[x].name + strlen(exCollection[x].category) + 1)); - } - else - { - jsIndex += sprintf(jsTextUpdated + jsListStartIndex + jsIndex, - TextFormat(" exampleEntry('%s', '%s', '%s'),\n", starsText, exCollection[x].category, exCollection[x].name + strlen(exCollection[x].category) + 1)); - } - } - - UnloadExamplesData(exCollection); + LOG("INFO: examples.js not found, skipping web examples list update\n"); } + else + { + int jsListStartIndex = TextFindIndex(jsText, "//EXAMPLE_DATA_LIST_START"); + int jsListEndIndex = TextFindIndex(jsText, "//EXAMPLE_DATA_LIST_END"); + if ((jsListStartIndex < 0) || (jsListEndIndex < 0)) + { + LOG("WARNING: examples.js markers not found, skipping update\n"); + UnloadFileText(jsText); + } + else + { + char *jsTextUpdated = (char *)RL_CALLOC(REXM_MAX_BUFFER_SIZE, 1); // Updated examples.js copy, 2MB + int jsIndex = 0; + memcpy(jsTextUpdated, jsText, jsListStartIndex); + jsIndex = sprintf(jsTextUpdated + jsListStartIndex, "//EXAMPLE_DATA_LIST_START\n"); + jsIndex += sprintf(jsTextUpdated + jsListStartIndex + jsIndex, " var exampleData = [\n"); - // Add the remaining part of the original file - memcpy(jsTextUpdated + jsListStartIndex + jsIndex, jsText + jsListEndIndex, strlen(jsText) - jsListEndIndex); + char starsText[16] = { 0 }; - // Save updated file - SaveFileText(TextFormat("%s/../common/examples.js", exWebPath), jsTextUpdated); - UnloadFileText(jsText); - RL_FREE(jsTextUpdated); + // NOTE: We avoid "others" category + for (int i = 0; i < REXM_MAX_EXAMPLE_CATEGORIES - 1; i++) + { + int exCollectionCount = 0; + rlExampleInfo *exCollection = LoadExamplesData(exCollectionFilePath, exCategories[i], false, &exCollectionCount); + for (int x = 0; x < exCollectionCount; x++) + { + for (int s = 0; s < 4; s++) + { + if (s < exCollection[x].stars) strcpy(starsText + 3*s, "⭐️"); // WARNING: Different than '★', more visual + else strcpy(starsText + 3*s, "☆"); + } + + if ((i == 6) && (x == (exCollectionCount - 1))) + { + // NOTE: Last line to add, special case to consider + jsIndex += sprintf(jsTextUpdated + jsListStartIndex + jsIndex, + TextFormat(" exampleEntry('%s', '%s', '%s')];\n", starsText, exCollection[x].category, exCollection[x].name + strlen(exCollection[x].category) + 1)); + } + else + { + jsIndex += sprintf(jsTextUpdated + jsListStartIndex + jsIndex, + TextFormat(" exampleEntry('%s', '%s', '%s'),\n", starsText, exCollection[x].category, exCollection[x].name + strlen(exCollection[x].category) + 1)); + } + } + + UnloadExamplesData(exCollection); + } + + // Add the remaining part of the original file + memcpy(jsTextUpdated + jsListStartIndex + jsIndex, jsText + jsListEndIndex, strlen(jsText) - jsListEndIndex); + + // Save updated file + SaveFileText(TextFormat("%s/../common/examples.js", exWebPath), jsTextUpdated); + UnloadFileText(jsText); + RL_FREE(jsTextUpdated); + } + } //------------------------------------------------------------------------------------------------ return result; @@ -2331,7 +2354,7 @@ static const char *GenerateUUIDv4(void) // Update source code header and comments metadata static void UpdateSourceMetadata(const char *exSrcPath, const rlExampleInfo *info) { - if (FileExists(exSrcPath) && IsFileExtension(exSrcPath, ".c")) + if (FileExists(exSrcPath) && IsFileExtension(exSrcPath, ".c") && (!TextIsEqual(info->category, "others"))) { // WARNING: Cache a copy of exSrcPath to avoid modifications by TextFormat() char exSourcePath[512] = { 0 };