From dde8c8e07e1936a2879fd9b68517340697296375 Mon Sep 17 00:00:00 2001 From: areynaldo <49327220+areynaldo@users.noreply.github.com> Date: Fri, 17 Apr 2026 18:04:08 +0200 Subject: [PATCH 1/3] [build-windows.bat] Update build-windows script (#5769) * Update build-windows script * Use vswhere in build-windows script --- projects/scripts/build-windows.bat | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/projects/scripts/build-windows.bat b/projects/scripts/build-windows.bat index 2c085f8d6..311da23f7 100644 --- a/projects/scripts/build-windows.bat +++ b/projects/scripts/build-windows.bat @@ -26,13 +26,13 @@ REM Checks if cl is available and skips to the argument loop if it is REM (Prevents calling vcvarsall every time you run this script) WHERE cl >nul 2>nul IF %ERRORLEVEL% == 0 goto READ_ARGS + REM Activate the msvc build environment if cl isn't available yet -IF EXIST "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" ( - set VC_INIT="C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" -) ELSE IF EXIST "C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvarsall.bat" ( - set VC_INIT="C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvarsall.bat" -) ELSE IF EXIST "C:\Program Files (x86)\Microsoft Visual C++ Build Tools\vcbuildtools.bat" ( - set VC_INIT="C:\Program Files (x86)\Microsoft Visual C++ Build Tools\vcbuildtools.bat" +for /f "tokens=*" %%i in ( + '"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" -latest -property installationPath 2^>nul' +) do set VS_PATH=%%i +IF defined VS_PATH ( + set VC_INIT="%VS_PATH%\VC\Auxiliary\Build\vcvarsall.bat" ) ELSE ( REM Initialize your vc environment here if the defaults don't work REM set VC_INIT="C:\your\path\here\vcvarsall.bat" @@ -167,7 +167,7 @@ IF NOT EXIST !TEMP_DIR!\ ( cd !TEMP_DIR! REM raylib source folder set "RAYLIB_DEFINES=/D_DEFAULT_SOURCE /DPLATFORM_DESKTOP /DGRAPHICS_API_OPENGL_33" - set RAYLIB_C_FILES="!RAYLIB_SRC!\rcore.c" "!RAYLIB_SRC!\rshapes.c" "!RAYLIB_SRC!\rtextures.c" "!RAYLIB_SRC!\rtext.c" "!RAYLIB_SRC!\rmodels.c" "!RAYLIB_SRC!\utils.c" "!RAYLIB_SRC!\raudio.c" "!RAYLIB_SRC!\rglfw.c" + set RAYLIB_C_FILES="!RAYLIB_SRC!\rcore.c" "!RAYLIB_SRC!\rshapes.c" "!RAYLIB_SRC!\rtextures.c" "!RAYLIB_SRC!\rtext.c" "!RAYLIB_SRC!\rmodels.c" "!RAYLIB_SRC!\raudio.c" "!RAYLIB_SRC!\rglfw.c" set RAYLIB_INCLUDE_FLAGS=/I"!RAYLIB_SRC!" /I"!RAYLIB_SRC!\external\glfw\include" IF DEFINED REALLY_QUIET ( From f0a043bb75c54974cba1642df0d24333c4315d6f Mon Sep 17 00:00:00 2001 From: Thomas Anderson <5776225+CrackedPixel@users.noreply.github.com> Date: Fri, 17 Apr 2026 11:09:57 -0500 Subject: [PATCH 2/3] [Platform/RGFW] add support for software rendering (#5773) * add support for software rendering * null check on freeing * add back line * rename framebuffer to surface * add guard on free * update makefile to prevent software on web * update for mac --- src/Makefile | 11 +- src/platforms/rcore_desktop_rgfw.c | 185 +++++++++++++++++++++++++++-- 2 files changed, 186 insertions(+), 10 deletions(-) diff --git a/src/Makefile b/src/Makefile index 997165b4a..13f132707 100644 --- a/src/Makefile +++ b/src/Makefile @@ -275,11 +275,20 @@ ifeq ($(TARGET_PLATFORM),PLATFORM_DRM) GRAPHICS ?= GRAPHICS_API_OPENGL_ES2 #GRAPHICS = GRAPHICS_API_OPENGL_SOFTWARE # Uncomment to use software rendering endif -ifeq ($(TARGET_PLATFORM),$(filter $(TARGET_PLATFORM),PLATFORM_WEB PLATFORM_WEB_RGFW)) +ifeq ($(TARGET_PLATFORM),PLATFORM_WEB) # On HTML5 OpenGL ES 2.0 is used, emscripten translates it to WebGL 1.0 GRAPHICS ?= GRAPHICS_API_OPENGL_ES2 #GRAPHICS = GRAPHICS_API_OPENGL_ES3 endif +ifeq ($(TARGET_PLATFORM),PLATFORM_WEB_RGFW) + # On HTML5 OpenGL ES 2.0 is used, emscripten translates it to WebGL 1.0 + GRAPHICS ?= GRAPHICS_API_OPENGL_ES2 + #GRAPHICS = GRAPHICS_API_OPENGL_ES3 + + ifeq ($(GRAPHICS),GRAPHICS_API_OPENGL_SOFTWARE) + $(error WEB_RGFW: Software rendering not supported!) + endif +endif ifeq ($(TARGET_PLATFORM),PLATFORM_ANDROID) # By default use OpenGL ES 2.0 on Android GRAPHICS ?= GRAPHICS_API_OPENGL_ES2 diff --git a/src/platforms/rcore_desktop_rgfw.c b/src/platforms/rcore_desktop_rgfw.c index a1ba74f52..1a4831bf1 100755 --- a/src/platforms/rcore_desktop_rgfw.c +++ b/src/platforms/rcore_desktop_rgfw.c @@ -177,6 +177,13 @@ typedef struct { RGFW_window *window; // Native display device (physical screen connection) RGFW_monitor *monitor; mg_gamepads minigamepad; + + #if defined(GRAPHICS_API_OPENGL_SOFTWARE) + RGFW_surface *surface; + u8 *surfacePixels; + i32 surfaceWidth; + i32 surfaceHeight; + #endif } PlatformData; //---------------------------------------------------------------------------------- @@ -1121,7 +1128,35 @@ void DisableCursor(void) // Swap back buffer with front buffer (screen drawing) void SwapScreenBuffer(void) { - RGFW_window_swapBuffers_OpenGL(platform.window); + #if defined(GRAPHICS_API_OPENGL_SOFTWARE) + { + if (platform.surface) + { + // copy rlsw pixel data to the surface framebuffer + swReadPixels(0, 0, platform.surfaceWidth, platform.surfaceHeight, SW_RGBA, SW_UNSIGNED_BYTE, platform.surfacePixels); + + // Mac wants a different pixel order. I cant seem to get this to work any other way + #if defined(__APPLE__) + unsigned char temp = 0; + unsigned char *p = NULL; + for (int i = 0; i < (platform.surfaceWidth * platform.surfaceHeight); i += 1) + { + p = platform.surfacePixels + (i * 4); + temp = p[0]; + p[0] = p[2]; + p[2] = temp; + } + #endif + + // blit surface to the window + RGFW_window_blitSurface(platform.window, platform.surface); + } + } + #else + { + RGFW_window_swapBuffers_OpenGL(platform.window); + } + #endif } //---------------------------------------------------------------------------------- @@ -1315,6 +1350,9 @@ void PollInputEvents(void) // Window events are also polled (Minimized, maximized, close...) case RGFW_windowResized: { + // set flag that the window was resized + CORE.Window.resizedLastFrame = true; + #if defined(__APPLE__) if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI)) { @@ -1363,7 +1401,42 @@ void PollInputEvents(void) CORE.Window.currentFbo.width = CORE.Window.screen.width; CORE.Window.currentFbo.height = CORE.Window.screen.height; #endif - CORE.Window.resizedLastFrame = true; + + #if defined(GRAPHICS_API_OPENGL_SOFTWARE) + #if defined(__APPLE__) + RGFW_monitor* currentMonitor = RGFW_window_getMonitor(platform.window); + CORE.Window.screenScale = MatrixScale(currentMonitor->pixelRatio, currentMonitor->pixelRatio, 1.0f); + SetupViewport(platform.window->w * currentMonitor->pixelRatio, platform.window->h * currentMonitor->pixelRatio); + + CORE.Window.render.width = CORE.Window.screen.width * currentMonitor->pixelRatio; + CORE.Window.render.height = CORE.Window.screen.height * currentMonitor->pixelRatio; + CORE.Window.currentFbo.width = CORE.Window.render.width; + CORE.Window.currentFbo.height = CORE.Window.render.height; + #endif + platform.surfaceWidth = CORE.Window.currentFbo.width; + platform.surfaceHeight = CORE.Window.currentFbo.height; + + // in software mode we dont have the viewport so we need to reverse the highdpi changes + if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI)) + { + Vector2 scaleDpi = GetWindowScaleDPI(); + platform.surfaceWidth *= scaleDpi.x; + platform.surfaceHeight *= scaleDpi.y; + } + + if (platform.surfacePixels != NULL) + { + RL_FREE(platform.surfacePixels); + platform.surfacePixels = RL_MALLOC(platform.surfaceWidth * platform.surfaceHeight * 4); + } + + if (platform.surface != NULL) + { + RGFW_surface_free(platform.surface); + platform.surface = RGFW_window_createSurface(platform.window, platform.surfacePixels, platform.surfaceWidth, platform.surfaceHeight, RGFW_formatBGRA8); + swResize(platform.surfaceWidth, platform.surfaceHeight); + } + #endif } break; case RGFW_windowMaximized: { @@ -1641,6 +1714,12 @@ int InitPlatform(void) hints->major = 4; hints->minor = 3; } + else if (rlGetVersion() == RL_OPENGL_SOFTWARE) + { + hints->major = 1; + hints->minor = 1; + hints->renderer = RGFW_glSoftware; + } if (FLAG_IS_SET(CORE.Window.flags, FLAG_MSAA_4X_HINT)) hints->samples = 4; @@ -1720,6 +1799,39 @@ int InitPlatform(void) #endif } + #if defined(GRAPHICS_API_OPENGL_SOFTWARE) + // apple always scales for retina + #if defined(__APPLE__) + RGFW_monitor* currentMonitor = RGFW_window_getMonitor(platform.window); + CORE.Window.screenScale = MatrixScale(currentMonitor->pixelRatio, currentMonitor->pixelRatio, 1.0f); + + CORE.Window.render.width = CORE.Window.screen.width * currentMonitor->pixelRatio; + CORE.Window.render.height = CORE.Window.screen.height * currentMonitor->pixelRatio; + CORE.Window.currentFbo.width = CORE.Window.render.width; + CORE.Window.currentFbo.height = CORE.Window.render.height; + #endif + + platform.surfaceWidth = CORE.Window.currentFbo.width; + platform.surfaceHeight = CORE.Window.currentFbo.height; + + platform.surfacePixels = RL_MALLOC(platform.surfaceWidth * platform.surfaceHeight * 4); + if (platform.surfacePixels == NULL) + { + TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize software pixel buffer"); + return -1; + } + + platform.surface = RGFW_window_createSurface(platform.window, platform.surfacePixels, platform.surfaceWidth, platform.surfaceHeight, RGFW_formatBGRA8); + + if (platform.surface == NULL) + { + RL_FREE(platform.surfacePixels); + + TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize software surface"); + return -1; + } + #endif + TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully %s", FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI)? "(HighDPI)" : ""); TRACELOG(LOG_INFO, " > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height); @@ -1750,20 +1862,63 @@ int InitPlatform(void) //---------------------------------------------------------------------------- #if defined(RGFW_WAYLAND) - if (RGFW_usingWayland()) TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (RGFW - Wayland): Initialized successfully"); - else TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (RGFW - X11 (fallback)): Initialized successfully"); + if (rlGetVersion() == RL_OPENGL_SOFTWARE) + { + if (RGFW_usingWayland()) TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (RGFW - Wayland, Software): Initialized successfully"); + else TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (RGFW - X11, Software (fallback)): Initialized successfully"); + } + else + { + if (RGFW_usingWayland()) TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (RGFW - Wayland): Initialized successfully"); + else TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (RGFW - X11 (fallback)): Initialized successfully"); + } #elif defined(RGFW_X11) #if defined(__APPLE__) - TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (RGFW - X11 (MacOS)): Initialized successfully"); + if (rlGetVersion() == RL_OPENGL_SOFTWARE) + { + TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (RGFW - X11, Software, (MacOS)): Initialized successfully"); + } + else + { + TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (RGFW - X11, (MacOS)): Initialized successfully"); + } #else - TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (RGFW - X11): Initialized successfully"); + if (rlGetVersion() == RL_OPENGL_SOFTWARE) + { + TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (RGFW - X11, Software): Initialized successfully"); + } + else + { + TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (RGFW - X11): Initialized successfully"); + } #endif #elif defined (RGFW_WINDOWS) - TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (RGFW - Win32): Initialized successfully"); + if (rlGetVersion() == RL_OPENGL_SOFTWARE) + { + TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (RGFW - Win32, Software): Initialized successfully"); + } + else + { + TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (RGFW - Win32): Initialized successfully"); + } #elif defined(RGFW_WASM) - TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (RGFW - WASMs): Initialized successfully"); + if (rlGetVersion() == RL_OPENGL_SOFTWARE) + { + TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (RGFW - WASMs, Software): Initialized successfully"); + } + else + { + TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (RGFW - WASMs): Initialized successfully"); + } #elif defined(RGFW_MACOS) - TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (RGFW - MacOS): Initialized successfully"); + if (rlGetVersion() == RL_OPENGL_SOFTWARE) + { + TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (RGFW - MacOS, Software): Initialized successfully"); + } + else + { + TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (RGFW - MacOS): Initialized successfully"); + } #endif mg_gamepads_init(&platform.minigamepad); @@ -1776,6 +1931,18 @@ void ClosePlatform(void) { mg_gamepads_free(&platform.minigamepad); RGFW_window_close(platform.window); + + #if defined(GRAPHICS_API_OPENGL_SOFTWARE) + if (platform.surfacePixels != NULL) + { + RL_FREE(platform.surfacePixels); + } + + if (platform.surface != NULL) + { + RGFW_surface_free(platform.surface); + } + #endif } // Keycode mapping From 9a3283f698881963c3f8e589353ddcb4599ab5d7 Mon Sep 17 00:00:00 2001 From: Ziya Date: Sat, 18 Apr 2026 20:40:07 +0400 Subject: [PATCH 3/3] [examples] `shapes_collision_ellipses` (#5722) * Added shapes_collision_ellipses example * Address review comments from raysan * Address review comments * update collision ellipses screenshot * Restore shapes_following_eyes.c to upstream * Rename shapes_collision_ellipses to shapes_ellipse_collision and fix header --- examples/shapes/shapes_ellipse_collision.c | 133 +++++++++++++++++++ examples/shapes/shapes_ellipse_collision.png | Bin 0 -> 16967 bytes 2 files changed, 133 insertions(+) create mode 100644 examples/shapes/shapes_ellipse_collision.c create mode 100644 examples/shapes/shapes_ellipse_collision.png diff --git a/examples/shapes/shapes_ellipse_collision.c b/examples/shapes/shapes_ellipse_collision.c new file mode 100644 index 000000000..4a6d7e911 --- /dev/null +++ b/examples/shapes/shapes_ellipse_collision.c @@ -0,0 +1,133 @@ +/******************************************************************************************* +* +* raylib [shapes] example - ellipse collision +* +* Example complexity rating: [★★☆☆] 2/4 +* +* Example originally created with raylib 5.5, last time updated with raylib 5.5 +* +* Example contributed by Ziya (@Monjaris) +* +* 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 Ziya (@Monjaris) +* +********************************************************************************************/ + +#include "raylib.h" +#include + +// Check if point is inside ellipse +static bool CheckCollisionPointEllipse(Vector2 point, Vector2 center, float rx, float ry) +{ + float dx = (point.x - center.x)/rx; + float dy = (point.y - center.y)/ry; + return (dx*dx + dy*dy) <= 1.0f; +} + +// Check if two ellipses collide +// Uses radial boundary distance in the direction between centers — scales correctly with radii +static bool CheckCollisionEllipses(Vector2 c1, float rx1, float ry1, Vector2 c2, float rx2, float ry2) +{ + float dx = c2.x - c1.x; + float dy = c2.y - c1.y; + float dist = sqrtf(dx*dx + dy*dy); + + // Ellipses are on top of each other + if (dist == 0.0f) return true; + + float theta = atan2f(dy, dx); + float cosT = cosf(theta); + float sinT = sinf(theta); + + // Radial distance from center to ellipse boundary in direction theta + // r(theta) = (rx * ry) / sqrt((ry*cos)^2 + (rx*sin)^2) + float r1 = (rx1*ry1)/sqrtf((ry1*cosT)*(ry1*cosT) + (rx1*sinT)*(rx1*sinT)); + float r2 = (rx2*ry2)/sqrtf((ry2*cosT)*(ry2*cosT) + (rx2*sinT)*(rx2*sinT)); + + return dist <= (r1 + r2); +} + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [shapes] example - collision ellipses"); + SetTargetFPS(60); + + Vector2 ellipseACenter = { (float)screenWidth/4, (float)screenHeight/2 }; + float ellipseARx = 120.0f; + float ellipseARy = 70.0f; + + Vector2 ellipseBCenter = { (float)screenWidth*3/4, (float)screenHeight/2 }; + float ellipseBRx = 90.0f; + float ellipseBRy = 140.0f; + + // 0 = controlling A, 1 = controlling B + int controlled = 0; + + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + if (IsKeyPressed(KEY_A)) controlled = 0; + if (IsKeyPressed(KEY_B)) controlled = 1; + + if (controlled == 0) ellipseACenter = GetMousePosition(); + else ellipseBCenter = GetMousePosition(); + + bool ellipsesCollide = CheckCollisionEllipses( + ellipseACenter, ellipseARx, ellipseARy, + ellipseBCenter, ellipseBRx, ellipseBRy + ); + + bool mouseInA = CheckCollisionPointEllipse(GetMousePosition(), ellipseACenter, ellipseARx, ellipseARy); + bool mouseInB = CheckCollisionPointEllipse(GetMousePosition(), ellipseBCenter, ellipseBRx, ellipseBRy); + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + DrawEllipse((int)ellipseACenter.x, (int)ellipseACenter.y, ellipseARx, ellipseARy, ellipsesCollide ? RED : BLUE); + + DrawEllipse((int)ellipseBCenter.x, (int)ellipseBCenter.y, ellipseBRx, ellipseBRy, ellipsesCollide ? RED : GREEN); + + DrawEllipseLines((int)ellipseACenter.x, (int)ellipseACenter.y, ellipseARx, ellipseARy, WHITE); + + DrawEllipseLines((int)ellipseBCenter.x, (int)ellipseBCenter.y, ellipseBRx, ellipseBRy, WHITE); + + DrawCircleV(ellipseACenter, 4, WHITE); + DrawCircleV(ellipseBCenter, 4, WHITE); + + if (ellipsesCollide) DrawText("ELLIPSES COLLIDE", screenWidth/2 - 120, 40, 28, RED); + else DrawText("NO COLLISION", screenWidth/2 - 80, 40, 28, DARKGRAY); + + DrawText(controlled == 0 ? "Controlling: A" : "Controlling: B", 20, screenHeight - 40, 20, YELLOW); + + if (mouseInA && controlled != 0) DrawText("Mouse inside ellipse A", 20, screenHeight - 70, 20, BLUE); + if (mouseInB && controlled != 1) DrawText("Mouse inside ellipse B", 20, screenHeight - 70, 20, GREEN); + + DrawText("Press [A] or [B] to switch control", 20, 20, 20, GRAY); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + CloseWindow(); + + return 0; +} diff --git a/examples/shapes/shapes_ellipse_collision.png b/examples/shapes/shapes_ellipse_collision.png new file mode 100644 index 0000000000000000000000000000000000000000..30c66c164a3ad2ef0e4f17d7bd2d9b6a4ec638e0 GIT binary patch literal 16967 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 yO?^;