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) |
| ⭐⭐☆☆ | 5.0 | 5.5 | [Jonathan Marler](https://github.com/marler8997) |
| [core_render_texture](core/core_render_texture.c) |
| ⭐☆☆☆ | 5.6-dev | 5.6-dev | [Ramon Santamaria](https://github.com/raysan5) |
| [core_undo_redo](core/core_undo_redo.c) |
| ⭐⭐⭐☆ | 5.5 | 5.6 | [Ramon Santamaria](https://github.com/raysan5) |
+| [core_input_actions](core/core_input_actions.c) |
| ⭐⭐☆☆ | 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) |
| ⭐☆☆☆ | 1.0 | 4.2 | [Ramon Santamaria](https://github.com/raysan5) |
| [shapes_bouncing_ball](shapes/shapes_bouncing_ball.c) |
| ⭐☆☆☆ | 2.5 | 2.5 | [Ramon Santamaria](https://github.com/raysan5) |
+| [shapes_bullet_hell](shapes/shapes_bullet_hell.c) |
| ⭐☆☆☆ | 5.6 | 5.6 | [Zero](https://github.com/zerohorsepower) |
| [shapes_colors_palette](shapes/shapes_colors_palette.c) |
| ⭐⭐☆☆ | 1.0 | 2.5 | [Ramon Santamaria](https://github.com/raysan5) |
| [shapes_logo_raylib](shapes/shapes_logo_raylib.c) |
| ⭐☆☆☆ | 1.0 | 1.0 | [Ramon Santamaria](https://github.com/raysan5) |
| [shapes_logo_raylib_anim](shapes/shapes_logo_raylib_anim.c) |
| ⭐⭐☆☆ | 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) |
| ⭐⭐⭐☆ | 5.0 | 5.0 | [Ramon Santamaria](https://github.com/raysan5) |
| [shapes_digital_clock](shapes/shapes_digital_clock.c) |
| ⭐⭐⭐⭐️ | 5.5 | 5.6 | [Hamza RAHAL](https://github.com/hmz-rhl) |
| [shapes_double_pendulum](shapes/shapes_double_pendulum.c) |
| ⭐⭐☆☆ | 5.5 | 5.5 | [JoeCheong](https://github.com/Joecheong2006) |
+| [shapes_dashed_line](shapes/shapes_dashed_line.c) |
| ⭐☆☆☆ | 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) |
| ⭐⭐⭐☆ | 4.2 | 4.2 | [Ramon Santamaria](https://github.com/raysan5) |
| [text_inline_styling](text/text_inline_styling.c) |
| ⭐⭐⭐☆ | 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) |
| ⭐⭐⭐☆ | 4.5 | 4.5 | [Daniel Holden](https://github.com/orangeduck) |
| [models_bone_socket](models/models_bone_socket.c) |
| ⭐⭐⭐⭐️ | 4.5 | 4.5 | [iP](https://github.com/ipzaur) |
| [models_tesseract_view](models/models_tesseract_view.c) |
| ⭐⭐☆☆ | 5.6-dev | 5.6-dev | [Timothy van der Valk](https://github.com/arceryz) |
+| [models_basic_voxel](models/models_basic_voxel.c) |
| ⭐⭐☆☆ | 5.5 | 5.5 | [Tim Little](https://github.com/timlittle) |
+| [models_geometry_textures_cube](models/models_geometry_textures_cube.c) |
| ⭐☆☆☆ | 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 };