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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 HDWM4fS-30l literal 0 HcmV?d00001