42 Commits

Author SHA1 Message Date
Ray
453cc35f27 Update CHANGELOG 2026-10-10 21:35:57 +02:00
Ray
bc77441de0 Update CHANGELOG 2026-10-10 21:35:39 +02:00
89650b5b9e [examples] Update all examples to use explicit NULL pointer checks (#6251) 2026-10-10 21:27:59 +02:00
0b5fab1123 [rcore][win32] Implement SetClipboardText() and GetClipboardText() (#6248)
* [rcore][win32] Implement `SetClipboardText()` and `GetClipboardText()`

* Use explicit NULL pointer checks
2026-10-10 20:24:15 +02:00
Ray
e453e80bed Update CONVENTIONS.md 2026-10-10 20:22:42 +02:00
c5958f5832 [rexm] Validate screenshot size before comparing with template image (#6249) 2026-10-10 20:20:03 +02:00
fd0f4c185b [examples] Avoid unloading stale clipboard textures (#6244)
Co-authored-by: Hugohong258 <282228220+Hugohong258@users.noreply.github.com>
2026-10-10 09:20:02 +02:00
84e4a2a257 [rcore][win32] Stop SetWindowIcon() from leaking the previous icon (#6239)
* [rcore][win32] Stop SetWindowIcon() from leaking the previous icon

* Use WM_SETICON instead of WM_GETICON in ClosePlatform icon cleanup

* Delete icon after window is destroyed
2026-10-09 09:50:28 +02:00
9056ea3299 [examples] core_input_gestures_testbed: fix out-of-bounds log read (#6238)
gestureLogIndex started at GESTURE_LOG_SIZE, and the log drawing loop
starts reading at that index, so gestureLog[GESTURE_LOG_SIZE] (one past
the end) was read every frame until the first gesture was logged.
Start the index at 0 instead. Logging is unchanged: the first logged
gesture still wraps to GESTURE_LOG_SIZE - 1.
2026-10-09 00:57:48 +02:00
806d68a9bc [examples] audio_stream_callback: fix out-of-bounds read in waveform drawing (#6237)
The waveform loop draws a line from sample i to sample i + 1 for every
x in [0, screenWidth). For the last x, the end point index works out to
SAMPLE_RATE, one past the end of buffer[SAMPLE_RATE]. Stop the loop one
pixel earlier so the last segment ends on the final sample.
2026-10-09 00:56:35 +02:00
3825bbf2d4 [raudio] LoadSoundAlias(): check source buffer before use (#6236)
If the source sound failed to load (for example when no audio
playback device could be initialized), its stream.buffer is NULL and
LoadSoundAlias() dereferenced it. Check the buffer first, like
UnloadSoundAlias() and IsSoundValid() already do, so an empty Sound
is returned instead of crashing.

Seen with examples/audio/audio_sound_multi (reported in #5664).
2026-10-09 00:55:47 +02:00
c8b19b297b [rmodels] Fix GetRayCollisionSphere() hitting spheres behind the ray (#6231)
* [rmodels] Fix GetRayCollisionSphere() hitting spheres behind the ray

* Return zeroed struct on failure cases
2026-10-09 00:53:32 +02:00
a043255aa7 [examples] models_animation_blending: avoid crash when animations fail to load (#6234)
If resources/models/gltf/robot.glb can't be found (e.g. the example is
run from another working directory), LoadModelAnimations() returns NULL
with animCount = 0 and the example indexed anims[] in the update loop
and in the timeline drawing, crashing. Skip both when no animations
were loaded, so it just logs the usual load warnings.

Reported in #5664.
2026-10-08 15:33:10 +02:00
8c23ce44a0 [rcore][win32] Clean up window state in CloseWindow() (#6233) 2026-10-08 15:32:37 +02:00
48aaf6e2df [rcore][win32] Allow SetWindowSize() to change only the width or height (#6232) 2026-10-08 15:30:41 +02:00
0ed3570469 [raymath] Fix QuaternionFromVector3ToVector3() returning a zero quaternion for opposite vectors (#6230) 2026-10-08 15:26:36 +02:00
2dee47282a [raymath] Fix MatrixDecompose() reading rows instead of columns (#6225) 2026-10-07 13:13:42 +02:00
607a659363 [rcore][win32] Support MSAA (#6227) 2026-10-07 07:46:34 +02:00
040a4bafa9 [CHANGELOG] Fix typos in 6.2 release notes (#6228) 2026-10-07 07:44:20 +02:00
8571d445e9 [rtextures] Fix ImageDrawImageEx() blending on transparent destinations (#6224) 2026-10-06 20:55:35 +02:00
Ray
4722c9fa58 Minor tweaks 2026-10-06 18:35:06 +02:00
91d50e2938 [rcore][win32] Fix WGL pixel format not being applied (#6219)
* [rcore][win32] Fix WGL pixel format not being applied

* Specify Stencil Bits In Fallback Pixel Format
2026-10-06 08:22:59 +02:00
92f1f3d5d5 [rcore][web] Avoid glfwGetProcAddress() to reduce code size (#6220)
* [rcore][web] Avoid glfwGetProcAddress() to reduce code size

* [build][cmake] Remove unneeded GL_ENABLE_GET_PROC_ADDRESS flag
2026-10-06 08:20:40 +02:00
Ray
f0736f6fb5 GLFW: Boundaries check #6222 2026-10-06 07:49:07 +02:00
Ray
e18de07224 Update rcore_desktop_win32.c 2026-10-05 10:19:16 +02:00
Ray
3c1ab87ae1 Update Makefile 2026-10-05 10:19:08 +02:00
Ray
e8b23bfbe4 Minor tweaks 2026-10-05 09:39:02 +02:00
e5c0709fa2 [Examples] Clarify Camera2d.offset and how it works with High DPI scale mode (#6203)
* rlparser: update raylib_api.* by CI

* Notate that the offset in Camera2d is in pixels space (unscaled by DPI)
Update all the examples that use Camera2d.offset so that they are DPI aware if the user enables DPI scaling.

* rlparser: update raylib_api.* by CI

* remove DPI flag

* remove gltf tests that should not have been part of this PR

* formatting

* formatting

* formatting

* more formatting

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-10-05 09:29:59 +02:00
30fa673ef2 [build][Zig] 0.17 migration (#5951)
* Bump zemscripten to branch suporting latest zig 0.17

* zemscripten changes for build.zig

* general zig 0.17 changes for build.zig

- update calls to `buildDir` and `findProgram` to their new
interfaces/calling sites.
- fix issue where you can't import zon anymore as slice (?)

* Update minimum zig version

* Remove redundant emsdk activations

Examples depend on raylib anyway, and if using emsdk, it will be
activated already.  This activation was causing an error in zig
nightly, so the change here.

* better `getInstallPath` alternative

* Remove example install -> emcc dependency in graph

* Fix emrun path

* Fix file output issue

* Bump zig to latest nightly

* Get emrun step to work

* Remove zemscripten dependency and consolidate with emsdk struct

Bump zig to 0.17.0

Clean up zemscripten struct

Consolidate emscripten-related structs

* Less breaking changes, keep old name, bring back pub fields

* [build][Zig] Fix missing include path and make emsdk a lazy dependency

* [build][Zig] Replace deprecated lazyDependency with the new dependencyLazy

---------

Co-authored-by: Jairo Correa <jairo@omnidoki.com>
2026-10-04 19:08:17 +02:00
5c9e83e642 [rcore][SDL] Accumulate relative mouse motion per frame (#6215)
Co-authored-by: snowyukitty <270071858+snowyukitty@users.noreply.github.com>
2026-10-04 19:01:54 +02:00
791dd6d627 [rtext] Check for empty strings in TextToPascal and TextToCamel (#6213)
* [rtext] Check for empty strings in TextToPascal/TextToCamel

* [rtext] Add parentheses to conditions
2026-10-04 19:00:53 +02:00
a9e3027ebc [rcore][win32] Implement IsKeyPressedRepeat (#6218) 2026-10-04 18:55:27 +02:00
0e8385c890 [examples][textures_image_processing] Fix up arrow wraparound logic (#6217) 2026-10-04 16:54:33 +02:00
98ff2c33fe [build] Avoid _GNU_SOURCE on Linux to keep the library linkable with older glibc (#6214)
On glibc >= 2.38, _GNU_SOURCE implies _ISOC23_SOURCE, which redirects
sscanf(), strtol(), strtoul()... to __isoc23_*() symbols regardless of
-std=c99. Libraries built on a recent distro (like the release builds,
made on Ubuntu 24.04) then fail to link on systems with older glibc:

  undefined reference to `__isoc23_sscanf'
  undefined reference to `__isoc23_strtol'

- Makefile, build.zig: use _DEFAULT_SOURCE instead of _GNU_SOURCE for
  Linux desktop/DRM targets (other targets are unchanged)
- rcore.c: remove the _GNU_SOURCE define, it came after the first libc
  include so it had no effect
- rglfw.c: GLFW needs _GNU_SOURCE (ppoll(), mkostemp(), pipe2()), so
  keep it, but disable glibc's C23 strtol/scanf redirection once
  <features.h> has been processed (__GLIBC_USE_C2X_STRTOL on glibc
  2.38-2.39, __GLIBC_USE_C23_STRTOL on glibc >= 2.40)

Fixes #6205
2026-10-04 13:55:37 +02:00
a2ee68ea46 [rcore] Make FileText*() error handling consistent with Text*() helpers (#6210) 2026-10-03 22:37:01 +02:00
d8ddc8a0a6 [rmodels] UpdateModelAnimation(), compute bone normal matrices once per bone instead of per vertex (#6191)
* [rmodels] `UpdateModelAnimation()`, compute bone normal matrices once per bone instead of per vertex

* [rmodels] use a static buffer for bone normal matrices
2026-10-03 13:42:37 +02:00
8341321d1a [rcore][win32] Add missing break in WndProc switch statement (#6209) 2026-10-03 10:30:43 +02:00
51d713d54d [vox_loader] Prevent memory leak when loading voxel models (#6208) 2026-10-03 10:29:58 +02:00
3ff30034de [examples] Release mesh instancing resources on exit (#6207)
Co-authored-by: Hugohong258 <282228220+Hugohong258@users.noreply.github.com>
2026-10-03 10:26:53 +02:00
012e274ae8 [examples][text_codepoints_loading] Fix out of bounds error in CodepointRemoveDuplicates (#6206) 2026-10-02 18:02:24 +02:00
987a0e54c7 [examples][core_delta_time] Convert GetFrameTime() to milliseconds before printing (#6201) 2026-10-02 00:14:13 +02:00
ab23bd388e [rtext] Fix BDF glyph offsets (#6200)
LoadFontDataBDF() computed offsetX as charBBxoff0 + fontBByoff0, adding the
font bounding box's Y offset to a horizontal position, and offsetY as
fontBBh - (charBBh + charBByoff0 + fontBByoff0 + fontAscent). Every glyph
was drawn shifted, by an amount that depends on the font (4 px left and
up for Spleen 8x16, 5 px left and 3 px down for Helvetica 12), so text was
clipped at the top or left.

A glyph's BBX offsets are relative to the pen position on the baseline,
and the baseline is FONT_ASCENT below the top of the line, so
offsetX = charBBxoff0 and offsetY = fontAscent - (charBBh + charBByoff0).
FONT_ASCENT defaults to the top of the font bounding box when the font
doesn't define it.
2026-10-02 00:13:27 +02:00
47 changed files with 814 additions and 286 deletions

View File

@ -4,20 +4,20 @@ changelog
Current Release: raylib 6.0 (23 April 2026)
-------------------------------------------------------------------------
Release: raylib 6.2 (xx Sep 2026)
Release: raylib 6.2 (xx Oct 2026)
-------------------------------------------------------------------------
KEY CHANGES:
- REDESIGNED: Image API for more alignment with Texture API
- REDESIGNED: File API for more standard return result values
- REVIEWED: Shapes lines drawing functions, added new ones and unify behaviour
Detailed changes (until 12-Sept-2026):
Detailed changes (until 08-Oct-2026):
[rcore] ADDED: `IsPathDirectory()`, for convenience and more readability at user code by @raysan5
[rcore] ADDED: `IsPathAbsolute()`, check absolute paths by @raysan5
[rcore] ADDED: `IsFileHidden()`, check file hidden (OS-dependant) by @raysan5
[rcore] REVIEWED: `File*()` functions for consistent return result value by @raysan5 -WARNING-
[rcore] REVIEWED: Not updating mouse.previousPosition when `SeMousePosition()` #5962 by @raysan5 -WARNING-
[rcore] REVIEWED: Not updating mouse.previousPosition when `SetMousePosition()` #5962 by @raysan5 -WARNING-
[rcore] REVIEWED: Use `rlgl` provided interface instead of `rlsw` direct access by @raysan5
[rcore] REVIEWED: Set mouse position to (0,0) when mouse leaves window area #5945 by @raysan5
[rcore] REVIEWED: Multiple string boundary handling (#5812) by @zhaob1n
@ -26,7 +26,7 @@ Detailed changes (until 12-Sept-2026):
[rcore] REVIEWED: Fix initial macOS window size (#5967) by @loukamb
[rcore] REVIEWED: Fix clipboard image memory leak (#5968) by @LuisR385
[rcore] REVIEWED: `TakeScreenshot()`, support absolute paths by @raysan5
[rcore] REVIEWED: `OpenURL()`, added some safety checks to mitigate malicious url inputs - ROS: `CLN-002 by @raysan5 -WARNING-
[rcore] REVIEWED: `OpenURL()`, added some safety checks to mitigate malicious url inputs - ROS: `CLN-002` by @raysan5 -WARNING-
[rcore] REVIEWED: `LoadAutomationEventList()` - ROS: `CLN-005`, `CLN-006` by @raysan5
[rcore] REVIEWED: `GetWindowHandle()`, to be consistent between backends, fix #5885 by @raysan5
[rcore] REVIEWED: `GetWindowPosition()` #5932 by @raysan5
@ -40,6 +40,8 @@ Detailed changes (until 12-Sept-2026):
[rcore] REVIEWED: `ComputeSHA256()`, wrong hash when message length mod 64 is 56..59 (#6144) by @killerdevildog -WARNING-
[rcore] REVIEWED: `GetPrevDirectoryPath()` behavior to better support side cases (#6073) by @luphi
[rcore] REVIEWED: `GetGamepadAxisCount()`, `GetGamepadName()`, bounds-check gamepad index (#5937) by @szarta
[rcore] REVIEWED: `FileText*()`, make error handling consistent with `Text*()` helpers (#6210) by @Ne0nWinds
[rcore] REVIEWED: `MAX_FILEPATH_LENGTH` to match `MAX_PATH` (#6164) by @randomnumbersandletters
[rcore][GLFW] REVIEWED: `GetClipboardImage()`, fix creating new connection under X11 (#5871) by @steampuker
[rcore][GLFW] REVIEWED: Borderless-windowed mode being always on top (#5868) by @auwsmit
[rcore][GLFW] REVIEWED: Clipboard image memory leak (GLFW, RGFW) (#6103) by @omaribrahimsaad
@ -47,19 +49,35 @@ Detailed changes (until 12-Sept-2026):
[rcore][GLFW] REVIEWED: `Font` symbol conflict (#6094) by @M374LX
[rcore][RGFW] REVIEWED: Add fullscreen behaviour on size 0 window creation (#5859) by @keks137
[rcore][RGFW] REVIEWED: Fix window Y position reading X in RGFW move callback (#6044) by @latent-9
[rcore][GLFW] REVIEWED: Window centering overflow with unsigned screen size (#6119) by @nfvelten
[rcore][GLFW] REVIEWED: Allow non-fullscreen window being able to be larger than a workable area (#6137) by @JamesMillsDev
[rcore][SDL] REVIEWED: Use precise mouse wheel values (#5830) by @Menduist
[rcore][SDL] REVIEWED: Fix SDL3 wrong joystick functions usage (#5948) by @0xFishbyte
[rcore][SDL] REVIEWED: Accumulate relative mouse motion per frame (#6215) by @snowyukitty
[rcore][SDL] REVIEWED: Fix only the first dropped file being registered (#6190) by @SulimanAbdulrazzaq
[rcore][SDL] REVIEWED: `DROP_EVENT_DATA` definition (#6185) by @Yonokid
[rcore][SDL] REVIEWED: Grow dropFilePaths dynamically when file is dragged ontop of window (#6178) by @jestarray
[rcore][SDL] REVIEWED: Refactor dragdrop file onto window handling and grow dropFilepaths dynamically like glfw (#6178) by @jestarray
[rcore][DRM] REVIEWED: Silence warnings when using GRAPHICS_API_OPENGL_SOFTWARE (#5839) by @NighthowlerStudios
[rcore][DRM] REVIEWED: Segfault on some NVIDIA proprietary drivers (#5960) by @girvel
[rcore][DRM] REVIEWED: Raise MAX_DRM_CACHED_BUFFERS to 4 and warn when the cache fills (#6107) by @jtakakura
[rcore][Web] REVIEWED: Fix for stale mouse position (#5981) by @largenumberhere
[rcore][Win32] ADDED: GetClipboardImage() (#6091) by @omaribrahimsaad
[rcore][Web] REVIEWED: Avoid `glfwGetProcAddress()` to reduce code size (#6220) by @vdemcak -WARNING-
[rcore][Win32] ADDED: Support MSAA (#6227) by @Ne0nWinds
[rcore][Win32] ADDED: `IsKeyPressedRepeat()` implementation (#6218) by @Ne0nWinds
[rcore][Win32] ADDED: `GetClipboardImage()` (#6091) by @omaribrahimsaad
[rcore][Win32] REVIEWED: Use `CORE.Input.Keyboard.exitKey` when handling window closing (#5911) by @aceinetx
[rcore][Win32] REVIEWED: Security fixes (#5899) by @M374LX
[rcore][Win32] REVIEWED: Handle conflict with `PlaySoundW()` for wide string builds (#6140) by @JeffM2501
[rcore][Win32] REVIEWED: `SetWindowState()`, prevent infinite loop (#6194) by @Ne0nWinds
[rcore][Win32] REVIEWED: Missing break in WndProc switch statement (#6209) by @Ne0nWinds
[rcore][Win32] REVIEWED: `SetWindowSize()`, allow to change only the width or height (#6232) by @Ne0nWinds
[rcore][Win32] REVIEWED: `CloseWindow()`, clean up window state (#6233) by @Ne0nWinds
[rcore][Win32] REVIEWED: `IsWindowResized()`, return true only when needed (#6186) by @Ne0nWinds
[rcore][Win32] REVIEWED: WGL pixel format not being applied (#6219) by @Ne0nWinds
[rcore][Android] REVIEWED: Restore face-button input on Android gamepads (#5824) by @Tubbles
[rcore][Android] REVIEWED: Relaunch hang after destroy while paused (#6040) by @get-charlie
[rcore][Emscripten] REVIEWED: GetTime(), fix time calculation (#6061) by @andzdroid
[rcore][Emscripten] REVIEWED: `GetTime()`, fix time calculation (#6061) by @andzdroid
[rcore][Emscripten] REVIEWED: Handle emscripten keyboard events (#6067) by @andzdroid
[rcore][Emscripten] REVIEWED: Invert emscripten scroll wheel direction (#6104) by @andzdroid
[rcore][Emscripten] REVIEWED: Update emscripten mouse handling (#6070) by @andzdroid
@ -97,12 +115,16 @@ Detailed changes (until 12-Sept-2026):
[rshapes] REVIEWED: `DrawPolyLinesEx()` line thickness (#6014) by @MatthewRoush
[rshapes] REVIEWED: `DrawRectangleLinesEx()` (#6027) by @MatthewRoush
[rshapes] REVIEWED: `DrawRectangleRoundedLines()` (#5992) by @MatthewRoush
[rshapes] REVIEWED: `DrawRectangleRounded*()`, segment auto-calculation threshold (#6159) by @MatthewRoush
[rtextures] ADDED: Support for .pep image fileformat, useful for pixel-art by @raysan5
[rtextures] ADDED: `LoadRenderTextureEx()` with user defined pixel format (#6114) by @JeffM2501
[rtextures] ADDED: `ImageDrawLineStrip()` by @raysan5
[rtextures] ADDED: `DrawTriangleGradient()` by @raysan5
[rtextures] ADDED: `DrawCircleLinesEx()`, simpler naming than `DrawRing()` by @raysan5
[rtextures] ADDED: `ImageDrawCircleGradient()` by @raysan5
[rtextures] ADDED: Support for .pep image fileformat, useful for pixel-art by @raysan5
[rtextures] ADDED: `ImageDrawImageEx()` by @raysan5
[rtextures] ADDED: `ImageDrawRectanglePro()` by @raysan5
[rtextures] ADDED: `ImageDrawRectangleGradientEx()` by @raysan5
[rtextures] RENAMED: `ImageDrawTriangleEx()` to `ImageDrawTriangleGradient()` by @raysan5 -WARNING-
[rtextures] REDESIGNED: `ImageDraw()`, align with `DrawTexture()` by @raysan5 -WARNING-
[rtextures] REDESIGNED: `ImageDrawRectangleLines()`, consistency with `DrawRectangleLines()` by @raysan5 -WARNING-
@ -118,6 +140,9 @@ Detailed changes (until 12-Sept-2026):
[rtextures] REVIEWED: `ImageColorContrast()` parameter type, consistent with other `Image*()` functions by @raysan5 -WARNING-
[rtextures] REVIEWED: `GetPixelDataSize()`, avoid allocations >2GB - ROS: `CLN-013` by @raysan5
[rtextures] REVIEWED: `GetPixelColor()` const by @raysan5
[rtextures] REVIEWED: `ImageDrawImageEx()`, blending on transparent destinations (#6224) by @Kartikey-cs
[rtextures] REVIEWED: `ImageKernelConvolution()`, sampling across rows at image edges and unclamped alpha (#6192) by @4RH1T3CT0R7
[rtext] REVIEWED: BDF loading writing glyphs through a NULL-based pointer (#6198) by @antonsoo
[rtext] REVIEWED: `LoadFontEx()`, `LoadFontFromMemory()`, additional warnings (#5864) by @CrackedPixel
[rtext] REVIEWED: `TextToSnake()`, not considering spaces properly by @raysan5
[rtext] REVIEWED: `TextSubtext()`, fix returning empty string when position is 0 (#5975) by @thiagomucci
@ -127,10 +152,14 @@ Detailed changes (until 12-Sept-2026):
[rtext] REVIEWED: `LoadFontFromImage()`, memory leak (#6100) by @ApexNadir
[rtext] REVIEWED: `TextInsert()`/`TextInsertAlloc()` (#6021) by @SewerynKaminski
[rtext] REVIEWED: `TextSplit()` reading past its buffer on text of 1024 bytes (#6136) by @MaxFreedomPollard
[rtext] REVIEWED: `TextToPascal()`/`TextToCamel()` truncating text after a separator (#6132) by @MaxFreedomPollard
[rtext] REVIEWED: `TextToPascal()`/`TextToCamel()`, truncating text after a separator (#6132) by @MaxFreedomPollard
[rtext] REVIEWED: `TextToPascal()`/`TextToCamel()`, empty strings checks (#6213) by @Ne0nWinds
[rmodels] REDESIGNED: `DrawCapsule()`, `DrawCapsuleWires()`, parameter order by @raysan5 -WARNING-
[rmodels] REVIEWED: Fix glTF skinning when joints have non-joint parent nodes (#5876) by @cseelhoff
[rmodels] REVIEWED: Change `boneCount` field type to `unsigned int` (#5955) by @lauchimoon
[rmodels] REVIEWED: GLTF animation interpolation (#6007) by @lassade
[rmodels] REVIEWED: glTF pose sampled at the end of an animation returning a pose from the start (#6048) by @LeFede
[rmodels] REVIEWED: `LoadOBJ()`, avoid reading past vertex data on out-of-range face indices (#6199) by @antonsoo
[rmodels] REVIEWED: `LoadGLTF()`, use inverse bind matrices for bind pose (#5832) by @fyl2xp1
[rmodels] REVIEWED: `IsModelValid()` #5780 by @raysan5
[rmodels] REVIEWED: `GenMeshTorus()`, improved comments (#5934) by @EugenioBarbieriViale
@ -138,8 +167,7 @@ Detailed changes (until 12-Sept-2026):
[rmodels] REVIEWED: `DrawSphereWires()`, code optimization (#6000) by @oscujic
[rmodels] REVIEWED: `DrawCapsule()`, `DrawCapsuleWires()`, validate minimum rings (#5909) by @LuisR385
[rmodels] REVIEWED: `UnloadModel()`, fix memory leak (#5907) by @LuisR385
[rmodels] REVIEWED: GLTF animation interpolation (#6007) by @lassade
[rmodels] REVIEWED: glTF pose sampled at the end of an animation returning a pose from the start (#6048) by @LeFede
[rmodels] REVIEWED: `UpdateModelAnimation()`, compute bone normal matrices once per bone instead of per vertex (#6191) by @4RH1T3CT0R7 -WARNING-
[raudio] REVIEWED: `UpdateSound()`, missleading parameter name by @raysan5
[raudio] REVIEWED: `UnloadSoundAlias()`, prevent leak (#5857) by @Peter0x44
[raudio] REVIEWED: Remove extra `drflac_free()` call (#5916) by @CrackedPixel
@ -149,6 +177,9 @@ Detailed changes (until 12-Sept-2026):
[raudio] REVIEWED: `CloseAudioDevice()`, crash when uninit device before mutex (#6039) by @get-charlie
[raymath] ADDED: Operator overload for (float * Vector) (#5815) by @alphahex99
[raymath] REVIEWED: `MatrixCompose()` to follow raymath conventions (#6133) by @carlost0
[raymath] REVIEWED: `MatrixCompose()`, rotating vectors with already overwritten components (#6187) by @ethanstoner
[raymath] REVIEWED: `MatrixDecompose()`, reading rows instead of columns (#6225) by @ewraj
[raymath] REVIEWED: `QuaternionFromVector3ToVector3()`, returning a zero quaternion for opposite vectors (#6230) by @ewraj
[build] REMOVED: Unmaintained project: 4coder by @raysan5
[build] REMOVED: Unmaintained project: VS2019-Android by @raysan5
@ -156,6 +187,7 @@ Detailed changes (until 12-Sept-2026):
[build][Make] REVIEWED: Re-enable `GLFW_LINUX_ENABLE_WAYLAND` flag by default #5816 by @raysan5
[build][Make] REVIEWED: examples/Makefile after testing on OSX (#6077) by @WilliamIsMe1
[build][Make] REVIEWED: Detect the recipe shell for proper clean (#6146) by @Peter0x44
[build][Make] REVIEWED: Avoid _GNU_SOURCE on Linux to keep the library linkable with older glibc (#6214) by @Peter0x44 -WARNING-
[build][VS2022] REVIEWED: x64 build compatibility support (#6052) (#6053) by @yoobezxc
[build][CMake] ADDED: Support for Win32 platform + examples (#5855) by @CrackedPixel
[build][CMake] REVIEWED: Fix config.h parsing into cmake options (#5844) by @Peter0x44
@ -166,16 +198,20 @@ Detailed changes (until 12-Sept-2026):
[build][CMake] REVIEWED: Fix static link dependencies (#5990) by @meta-legend
[build][CMake] REVIEWED: Fix standalone configuration of examples project (#6079) by @zaveshaa
[build][CMake] REVIEWED: Propagate Cocoa backend frameworks to consumers of static raylib on macOS (#6086) by @zaveshaa
[build][CMake] REVIEWED: Fix install export with vendored SDL (#6173) by @RobLoach
[build][Zig] REVIEWED: Support wasm examples building (#5811) by @shadeops
[build][Zig] REVIEWED: Avoid building examples all the time (#5869) by @jn-jairo
[build][Zig] REVIEWED: Avoid building web examples all the time (#6006) by @jn-jairo
[build][Zig] REVIEWED: Update to raygui 5.0 (#6005) by @jn-jairo
[build][Zig] REVIEWED: Migration to latest 0.17 release (#5951) by @miker2049 -WARNING-
[build][CI] ADDED: Clang tidy linting (disabled by default) (#6163) by @jestarray -WARNING-
[build][CI] REVIEWED: GitHub Actions update to latest actions versions by @raysan5
[build][CI] REVIEWED: `build_examples_windows.yml`, use latest actions versions by @raysan5
[build][CI] REVIEWED: `analyze_codeql.yml`, use latest actions versions by @raysan5
[build][CI] REVIEWED: `setup-emsdk`, use latest actions version by @raysan5
[bindings] ADDED: Recomendations for binding authors (#6141) by @JeffM2501
[bindings] ADDED: gcl native raylib module (#6152) by @gnuchanos
[bindings] ADDED: sola-raylib (6.0) (#5790, #5795) by @brettchalupa
[bindings] ADDED: gclang-raylib (6.0) (#5850) by @gnuchanos
[bindings] ADDED: mach-raylib (#6059) by @Angluca
@ -208,26 +244,39 @@ Detailed changes (until 12-Sept-2026):
[bindings] UPDATED: fortran-raylib to raylib 6.0 (#6060) by @interkosmos
[bindings] UPDATED: raylib-py to raylib 6.0 (#6095) by @overdev
[bindings] UPDATED: h-raylib to raylib 6.0 (#6142) by @Anut-py
[bindings] UPDATED: Raylib.nelua (#6183) by @AuzFox
[bindings] UPDATED: mach-raylib to raylib 6.x-dev (#6153) by @Angluca
[examples] ADDED: `core_scissor_shape` (#6032) by @davidbuzatto
[examples] ADDED: `models_mesh_uv_painting` (#6084) by @PanicTitan
[examples] ADDED: `models_procedural_decals` (#6083) by @PanicTitan
[examples] ADDED: `shaders_lights_bloom` (#6081) by @PanicTitan
[examples] ADDED: `shapes_outlines_testbed` (#6072) by @MatthewRoush
[examples] ADDED: `shapes_outlines_thickness` (#6037) by @MatthewRoush
[examples] ADDED: `shapes_polygon_lines` (#6023) by @MatthewRoush
[examples] ADDED: `shapes_drag_puzzle` (#6165) by @gabriel-piangers
[examples] ADDED: `textures_portal_window` (#6082) by @PanicTitan
[examples] REVIEWED: `shaders_shadowmap_rendering` by @raysan5
[examples] REVIEWED: `models_mesh_uv_painting` by @raysan5
[examples] REVIEWED: `shaders_lights_bloom` by @raysan5
[examples] REVIEWED: `textures_portal_window` by @raysan5
[examples] ADDED: `models_mesh_uv_painting` (#6084) by @PanicTitan
[examples] ADDED: `models_procedural_decals` (#6083) by @PanicTitan
[examples] ADDED: `shaders_lights_bloom` (#6081) by @PanicTitan
[examples] REVIEWED: Clarify Camera2d.offset and how it works with High DPI scale mode (#6203) by @JeffM2501
[examples] REVIEWED: Inconsistent version metadata (#6171) by @Ne0nWinds
[examples] REVIEWED: Multiple examples, adding missing `Unload*()` (#6151) by @emecdelam
[examples] REVIEWED: `core_smooth_pixelperfect` (#5803) by @rose-mtz
[examples] REVIEWED: `core_directory_files` (#5823) by @MAXDVVV
[examples] REVIEWED: `core_delta_time`, convert frametime to milliseconds before printing (#6201) by @Ne0nWinds
[examples] REVIEWED: `shapes_ball_physics`, move window for ball shake (#5791) by @Sebbl0508
[examples] REVIEWED: `textures_image_drawing` by @raysan5
[examples] REVIEWED: `textures_portal_window` by @raysan5
[examples] REVIEWED: `textures_image_rotate`, unload source image (#6145) by @Hugohong258
[examples] REVIEWED: `textures_image_processing`, fix up arrow wraparound logic (#6217) by @Ne0nWinds
[examples] REVIEWED: `text_codepoints_loading`, out of bounds error in `CodepointRemoveDuplicates()` (#6206) by @Ne0nWinds
[examples] REVIEWED: `models_basic_voxel` by @raysan5
[examples] REVIEWED: `models_animation_blending` by @raysan5
[examples] REVIEWED: `models_mesh_uv_painting` by @raysan5
[examples] REVIEWED: `models_animation_blending`, avoid crash when animations fail to load (#6234) by @akashdeshmukh-git
[examples] REVIEWED: `shaders_*`, `pbr.fs`, using wrong uniform value (#5965) by @raysan5
[examples] REVIEWED: `shaders_lights_bloom` by @raysan5
[examples] REVIEWED: `shaders_shadowmap_rendering` by @raysan5
[examples] REVIEWED: `shaders_mesh_instancing`, release mesh instancing resources on exit (#6207) by @Hugohong258
[examples] REVIEWED: `shaders_lightmap_rendering`, unload lightmap render texture (#6188) by @Hugohong258
[rlparser] ADDED: S-Expression output type (#5943) by @VisenDev
[rlparser] REVIEWED: Path fixes (#5794) by @gigamonster256
@ -249,11 +298,12 @@ Detailed changes (until 12-Sept-2026):
[external] UPDATED: GLFW to 3.5.1 (#6051) by @M374LX
[external] UPDATED: miniaudio to 0.11.25 (#6050) by @M374LX
[external] UPDATED: `raygui` to latest master (raygui 5.0) by @raysan5
[external] UPDATED: `qoa` with upstream out-of-bounds fixes (#6162) by @thaidn
[external] REVIEWED: `jar_mod_load_file()`, check fread return value (#5840) by @fffaraz
[external] REVIEWED: `tiny_obj_loader` Replace `assert()` by runtime checks - ROS: `CLN-012` by @raysan5
[external] REVIEWED: `fix_win32_compatibility.h`, fix for newer windows SDK versions (#6088) by @JeffM2501
[external] REVIEWED: `vox_loader`, added bounds checks to `Vox_SetVoxel()` and chunk parsing (#6175) by @AvAl4nch
[external] REVIEWED: `vox_loader`, Prevent memory leak when loading voxel models (#6208) by @Ne0nWinds
-------------------------------------------------------------------------
Release: raylib 6.0 (23 April 2026)

View File

@ -68,6 +68,14 @@ void SomeFunction()
{
// TODO: Do something here!
}
```
- When checking pointers, use full verbose check against `NULL`
```c
if (dataPtr != NULL)
{
RL_FREE(dataPtr);
dataPtr = NULL;
}
```
**If proposing new functions, please try to use a clear naming for function-name and functions-parameters, in case of doubt, open an issue for discussion.**

398
build.zig
View File

@ -2,85 +2,257 @@ const std = @import("std");
const builtin = @import("builtin");
pub const emsdk = struct {
const zemscripten = @import("zemscripten");
const emsdk_version = "4.0.19"; // inline constant used only in activate
var emsdk_dep: ?*std.Build.Dependency = null;
pub fn shell(raylib_dep: *std.Build.Dependency) std.Build.LazyPath {
return raylib_dep.path("src/shell.html");
}
pub const FlagsOptions = struct {
optimize: std.builtin.OptimizeMode,
asyncify: bool = true,
// ---------- core types ----------
pub const EmccFlags = std.StringHashMap(void);
pub const EmccSettings = std.StringHashMap([]const u8);
const EmsdkAllocator = enum {
none,
dlmalloc,
emmalloc,
@"emmalloc-debug",
@"emmalloc-memvalidate",
@"emmalloc-verbose",
mimalloc,
};
pub fn emccDefaultFlags(allocator: std.mem.Allocator, options: FlagsOptions) zemscripten.EmccFlags {
var emcc_flags = zemscripten.emccDefaultFlags(allocator, .{
.optimize = options.optimize,
.fsanitize = true,
});
pub const ResourceFile = struct {
src_path: std.Build.LazyPath,
virtual_path: ?[]const u8 = null,
if (options.asyncify)
emcc_flags.put("-sASYNCIFY", {}) catch unreachable;
fn get(self: ResourceFile, b: *std.Build) []const u8 {
return if (self.virtual_path) |vp|
b.fmt("{s}@{s}", .{ self.src_path.getDisplayName(), vp })
else
self.src_path.getDisplayName();
}
};
return emcc_flags;
pub const EmccStepOptions = struct {
optimize: std.builtin.OptimizeMode,
flags: EmccFlags,
settings: EmccSettings,
use_preload_plugins: bool = false,
embed_paths: ?[]const ResourceFile = null,
preload_paths: ?[]const ResourceFile = null,
shell_file_path: ?std.Build.LazyPath = null,
js_library_path: ?std.Build.LazyPath = null,
out_file_name: []const u8,
install_dir: std.Build.InstallDir,
};
// ---------- helpers ----------
pub fn emsdkDep(b: *std.Build) !*std.Build.Dependency {
if (emsdk_dep == null) {
emsdk_dep = try b.dependencyLazy("emsdk", .{});
}
return emsdk_dep.?;
}
pub const SettingsOptions = struct {
pub fn path(b: *std.Build, sub_path: []const u8) !std.Build.LazyPath {
return (try emsdkDep(b)).path(sub_path);
}
pub fn emccPath(b: *std.Build) !std.Build.LazyPath {
return try emsdk.path(b, "upstream/emscripten/emcc.py");
}
pub fn emrunPath(b: *std.Build) !std.Build.LazyPath {
return switch (builtin.target.os.tag) {
.windows => try emsdk.path(b, "upstream/emscripten/emrun.bat"),
else => try emsdk.path(b, "upstream/emscripten/emrun"),
};
}
pub fn emccDefaultFlags(allocator: std.mem.Allocator, options: struct {
optimize: std.builtin.OptimizeMode,
asyncify: bool = true,
fsanitize: bool = true,
}) EmccFlags {
var flags = EmccFlags.init(allocator);
switch (options.optimize) {
.Debug => {
flags.put("-O0", {}) catch @panic("OOM");
flags.put("-gsource-map", {}) catch @panic("OOM");
if (options.fsanitize)
flags.put("-fsanitize=undefined", {}) catch @panic("OOM");
},
.ReleaseSafe => {
flags.put("-O3", {}) catch @panic("OOM");
if (options.fsanitize) {
flags.put("-fsanitize=undefined", {}) catch @panic("OOM");
flags.put("-fsanitize-minimal-runtime", {}) catch @panic("OOM");
}
},
.ReleaseFast => flags.put("-O3", {}) catch @panic("OOM"),
.ReleaseSmall => flags.put("-Oz", {}) catch @panic("OOM"),
}
if (options.asyncify)
flags.put("-sASYNCIFY", {}) catch @panic("OOM");
return flags;
}
pub fn emccDefaultSettings(allocator: std.mem.Allocator, options: struct {
optimize: std.builtin.OptimizeMode,
es3: bool = false,
glfw3: bool = true,
memory_growth: bool = false,
total_memory: u32 = 134217728,
emsdk_allocator: zemscripten.EmsdkAllocator = .emmalloc,
};
pub fn emccDefaultSettings(allocator: std.mem.Allocator, options: SettingsOptions) zemscripten.EmccSettings {
var emcc_settings = zemscripten.emccDefaultSettings(allocator, .{
.optimize = options.optimize,
.emsdk_allocator = options.emsdk_allocator,
});
emsdk_allocator: EmsdkAllocator = .emmalloc,
}) EmccSettings {
var settings = EmccSettings.init(allocator);
switch (options.optimize) {
.Debug, .ReleaseSafe => {
settings.put("SAFE_HEAP", "1") catch @panic("OOM");
settings.put("STACK_OVERFLOW_CHECK", "1") catch @panic("OOM");
settings.put("ASSERTIONS", "1") catch @panic("OOM");
},
else => {},
}
settings.put("MALLOC", @tagName(options.emsdk_allocator)) catch @panic("OOM");
if (options.es3) {
emcc_settings.put("FULL_ES3", "1") catch unreachable;
emcc_settings.put("MIN_WEBGL_VERSION", "2") catch unreachable;
emcc_settings.put("MAX_WEBGL_VERSION", "2") catch unreachable;
}
if (options.glfw3) {
emcc_settings.put("USE_GLFW", "3") catch unreachable;
settings.put("FULL_ES3", "1") catch @panic("OOM");
settings.put("MIN_WEBGL_VERSION", "2") catch @panic("OOM");
settings.put("MAX_WEBGL_VERSION", "2") catch @panic("OOM");
}
if (options.glfw3)
settings.put("USE_GLFW", "3") catch @panic("OOM");
const total_memory = std.fmt.allocPrint(allocator, "{d}", .{options.total_memory}) catch unreachable;
emcc_settings.put("EXPORTED_RUNTIME_METHODS", "['requestFullscreen']") catch unreachable;
emcc_settings.put("TOTAL_MEMORY", total_memory) catch unreachable;
emcc_settings.put("FORCE_FILESYSTEM", "1") catch unreachable;
emcc_settings.put("EXPORTED_RUNTIME_METHODS", "ccall") catch unreachable;
const total_memory = std.fmt.allocPrint(allocator, "{d}", .{options.total_memory}) catch @panic("OOM");
settings.put("EXPORTED_RUNTIME_METHODS", "['requestFullscreen']") catch @panic("OOM");
settings.put("TOTAL_MEMORY", total_memory) catch @panic("OOM");
settings.put("FORCE_FILESYSTEM", "1") catch @panic("OOM");
settings.put("EXPORTED_RUNTIME_METHODS", "ccall") catch @panic("OOM");
if (options.memory_growth)
emcc_settings.put("ALLOW_MEMORY_GROWTH", "1") catch unreachable;
settings.put("ALLOW_MEMORY_GROWTH", "1") catch @panic("OOM");
return emcc_settings;
return settings;
}
pub fn emccStep(b: *std.Build, raylib: *std.Build.Step.Compile, wasm: *std.Build.Step.Compile, options: zemscripten.StepOptions) *std.Build.Step {
const activate_emsdk_step = zemscripten.activateEmsdkStep(b);
// ---------- step builders ----------
pub fn emccStep(b: *std.Build, src_paths: []const std.Build.LazyPath, compile_steps: []const *std.Build.Step.Compile, options: EmccStepOptions) !*std.Build.Step {
var emcc = b.addRunFile(try emccPath(b));
const emsdk_dep = b.dependency("emsdk", .{});
raylib.root_module.addIncludePath(emsdk_dep.path("upstream/emscripten/cache/sysroot/include"));
wasm.root_module.addIncludePath(emsdk_dep.path("upstream/emscripten/cache/sysroot/include"));
// flags
{
var it = options.flags.iterator();
while (it.next()) |kv| emcc.addArg(kv.key_ptr.*);
}
const emcc_step = zemscripten.emccStep(b, wasm, options);
emcc_step.dependOn(activate_emsdk_step);
// settings
{
var it = options.settings.iterator();
while (it.next()) |kv| {
emcc.addArg(b.fmt("-s{s}={s}", .{ kv.key_ptr.*, kv.value_ptr.* }));
}
}
return emcc_step;
// source files & artifact dependencies
for (src_paths) |sp| emcc.addFileArg(sp);
for (compile_steps) |cs| {
emcc.addArtifactArg(cs);
cs.root_module.addIncludePath(try emsdk.path(b, "upstream/emscripten/cache/sysroot/include"));
for (cs.root_module.getGraph().modules) |mod| {
for (mod.link_objects.items) |lo| {
if (lo == .other_step) {
const linked = lo.other_step;
if (linked.kind == .lib) emcc.addArtifactArg(linked);
}
}
}
}
emcc.addArg("-o");
const out_file = emcc.addOutputFileArg(options.out_file_name);
// optional args
if (options.use_preload_plugins) emcc.addArg("--use-preload-plugins");
if (options.embed_paths) |eps| for (eps) |ep| {
emcc.addArg("--embed-file");
emcc.addFileArg(ep.src_path);
};
if (options.preload_paths) |pps| for (pps) |pp| {
emcc.addArg("--preload-file");
emcc.addArg(pp.get(b));
};
if (options.shell_file_path) |sfp| {
emcc.addArg("--shell-file");
emcc.addFileArg(sfp);
}
if (options.js_library_path) |jlp| {
emcc.addArg("--js-library");
emcc.addFileArg(jlp);
}
const install = b.addInstallDirectory(.{
.source_dir = out_file.dirname(),
.install_dir = options.install_dir,
.install_subdir = "",
});
install.step.dependOn(&emcc.step);
return &install.step;
}
pub fn emrunStep(
b: *std.Build,
html_path: []const u8,
extra_args: []const []const u8,
) *std.Build.Step {
return zemscripten.emrunStep(b, html_path, extra_args);
pub fn emrunStep(b: *std.Build, html_path: std.Build.LazyPath, extra_args: []const []const u8) !*std.Build.Step {
var emrun = b.addRunFile(try emrunPath(b));
emrun.addArgs(extra_args);
emrun.addFileArg(html_path);
return &emrun.step;
}
pub fn activateEmsdkStep(b: *std.Build) !*std.Build.Step {
const user = b.step("Activate EMSDK", "Install/Update and prepare emscripten sdk");
const script = switch (builtin.target.os.tag) {
.windows => try emsdk.path(b, "emsdk.bat"),
else => try emsdk.path(b, "emsdk"),
};
var update = b.addRunFile(script);
update.addArg("update");
// chmod / takeown for script
const make_script_exec: ?*std.Build.Step.Run = switch (builtin.target.os.tag) {
.linux, .macos => blk: {
var cmd = b.addSystemCommand(&.{ "chmod", "+x" });
cmd.addFileArg(script);
break :blk cmd;
},
.windows => blk: {
var cmd = b.addSystemCommand(&.{ "takeown", "/f" });
cmd.addFileArg(script);
break :blk cmd;
},
else => null,
};
if (make_script_exec) |mse| update.step.dependOn(&mse.step);
var install = b.addRunFile(script);
install.addArg("install");
install.addArg(emsdk_version);
install.step.dependOn(&update.step);
var activate = b.addRunFile(script);
activate.addArg("activate");
activate.addArg(emsdk_version);
activate.step.dependOn(&install.step);
user.dependOn(&activate.step);
// also ensure emcc/emrun are executable on Unix
if (builtin.target.os.tag == .linux or builtin.target.os.tag == .macos) {
for ([_]std.Build.LazyPath{ try emccPath(b), try emrunPath(b) }) |p| {
var cmd = b.addSystemCommand(&.{ "chmod", "a+x" });
cmd.addFileArg(p);
cmd.step.dependOn(&install.step);
user.dependOn(&cmd.step);
}
}
return user;
}
};
@ -92,7 +264,7 @@ pub fn linkWindows(mod: *std.Build.Module, opengl: bool, comptime shcore: bool)
}
fn findWaylandScanner(b: *std.Build) void {
_ = b.findProgram(&.{"wayland-scanner"}, &.{}) catch {
_ = b.findProgram(.{ .names = &.{"wayland-scanner"} }) orelse {
std.log.err(
\\ `wayland-scanner` may not be installed on the system.
\\ You can switch to X11 in your `build.zig` by changing `Options.linux_display_backend`
@ -126,13 +298,13 @@ pub fn linkBSD(_: *std.Build, mod: *std.Build.Module) void {
mod.linkSystemLibrary("GL", .{});
}
pub fn linkMacOS(b: *std.Build, mod: *std.Build.Module) void {
pub fn linkMacOS(b: *std.Build, mod: *std.Build.Module) !void {
// Include xcode_frameworks for cross compilation
if (b.lazyDependency("xcode_frameworks", .{})) |dep| {
mod.addSystemFrameworkPath(dep.path("Frameworks"));
mod.addSystemIncludePath(dep.path("include"));
mod.addLibraryPath(dep.path("lib"));
}
const dep = try b.dependencyLazy("xcode_frameworks", .{});
mod.addSystemFrameworkPath(dep.path("Frameworks"));
mod.addSystemIncludePath(dep.path("include"));
mod.addLibraryPath(dep.path("lib"));
mod.linkFramework("Foundation", .{});
mod.linkFramework("CoreServices", .{});
@ -155,7 +327,13 @@ fn compileRaylib(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.
.root_module = raylib_mod,
});
raylib_mod.addCMacro("_GNU_SOURCE", "");
// On Linux (glibc) avoid _GNU_SOURCE, on glibc >= 2.38 it redirects sscanf(), strtol()...
// to __isoc23_*() symbols, so the generated library fails to link with older glibc versions
if (target.result.os.tag == .linux and !target.result.abi.isAndroid()) {
raylib_mod.addCMacro("_DEFAULT_SOURCE", "");
} else {
raylib_mod.addCMacro("_GNU_SOURCE", "");
}
raylib_mod.addCMacro("GL_SILENCE_DEPRECATION", "199309L");
var arena: std.heap.ArenaAllocator = .init(b.allocator);
@ -262,7 +440,7 @@ fn compileRaylib(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.
});
_ = raylib_flags_arr.pop();
linkMacOS(b, raylib_mod);
try linkMacOS(b, raylib_mod);
},
.emscripten => {
switch (opengl_version) {
@ -273,7 +451,9 @@ fn compileRaylib(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.
raylib_mod.addCMacro("PLATFORM_WEB", "");
const activate_emsdk_step = emsdk.zemscripten.activateEmsdkStep(b);
raylib_mod.addIncludePath(try emsdk.path(b, "upstream/emscripten/cache/sysroot/include"));
const activate_emsdk_step = try emsdk.activateEmsdkStep(b);
raylib.step.dependOn(activate_emsdk_step);
},
else => @panic("Target is not supported with this platform"),
@ -322,7 +502,7 @@ fn compileRaylib(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.
}
},
.freebsd, .openbsd, .netbsd, .dragonfly => linkBSD(b, raylib_mod),
.macos => linkMacOS(b, raylib_mod),
.macos => try linkMacOS(b, raylib_mod),
.emscripten => {
switch (opengl_version) {
.auto => opengl_version = OpenglVersion.gles_2,
@ -331,7 +511,10 @@ fn compileRaylib(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.
}
raylib_mod.addCMacro("PLATFORM_WEB_RGFW", "");
const activate_emsdk_step = emsdk.zemscripten.activateEmsdkStep(b);
raylib_mod.addIncludePath(try emsdk.path(b, "upstream/emscripten/cache/sysroot/include"));
const activate_emsdk_step = try emsdk.activateEmsdkStep(b);
raylib.step.dependOn(activate_emsdk_step);
},
else => @panic("Target is not supported with this platform"),
@ -477,33 +660,33 @@ fn compileRaylib(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.
return raylib;
}
fn addRaygui(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, raylib: *std.Build.Step.Compile) void {
if (b.lazyDependency("raygui", .{
fn addRaygui(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, raylib: *std.Build.Step.Compile) !void {
const raygui_dep = try b.dependencyLazy("raygui", .{
.target = target,
.optimize = optimize,
.link_libc = true,
})) |raygui_dep| {
var gen_step = b.addWriteFiles();
raylib.step.dependOn(&gen_step.step);
});
const raygui_c_path = gen_step.add("raygui.c", "#define RAYGUI_IMPLEMENTATION\n#include \"raygui.h\"\n");
raylib.root_module.addCSourceFile(.{ .file = raygui_c_path });
raylib.root_module.addIncludePath(raygui_dep.path("src"));
raylib.root_module.addIncludePath(b.path("src"));
var gen_step = b.addWriteFiles();
raylib.step.dependOn(&gen_step.step);
raylib.installHeader(raygui_dep.path("src/raygui.h"), "raygui.h");
const raygui_c_path = gen_step.add("raygui.c", "#define RAYGUI_IMPLEMENTATION\n#include \"raygui.h\"\n");
raylib.root_module.addCSourceFile(.{ .file = raygui_c_path });
raylib.root_module.addIncludePath(raygui_dep.path("src"));
raylib.root_module.addIncludePath(b.path("src"));
const c = b.addTranslateC(.{
.root_source_file = raygui_dep.path("src/raygui.h"),
.target = target,
.optimize = optimize,
.link_libc = true,
});
c.addIncludePath(b.path("src"));
const c_mod = c.createModule();
c_mod.linkLibrary(raylib);
b.modules.put(b.graph.arena, "raygui", c_mod) catch @panic("OOM");
}
raylib.installHeader(raygui_dep.path("src/raygui.h"), "raygui.h");
const c = b.addTranslateC(.{
.root_source_file = raygui_dep.path("src/raygui.h"),
.target = target,
.optimize = optimize,
.link_libc = true,
});
c.addIncludePath(b.path("src"));
const c_mod = c.createModule();
c_mod.linkLibrary(raylib);
b.modules.put(b.graph.arena, "raygui", c_mod) catch @panic("OOM");
}
pub const Options = struct {
@ -624,7 +807,7 @@ pub fn build(b: *std.Build) !void {
translateCMod("rlgl", b, target, optimize, lib);
if (options.raygui) {
addRaygui(b, target, optimize, lib);
try addRaygui(b, target, optimize, lib);
}
const examples = b.step("examples", "build/install all examples");
@ -649,7 +832,7 @@ fn addExamples(
const all = b.step(module, "All " ++ module ++ " examples");
const module_subpath = b.pathJoin(&.{ "examples", module });
var dir = try b.build_root.handle.openDir(b.graph.io, module_subpath, .{ .iterate = true });
var dir = try b.root.openDir(b.graph.io, module_subpath, .{ .iterate = true });
defer dir.close(b.graph.io);
var iter = dir.iterate();
@ -699,7 +882,7 @@ fn addExamples(
exe_mod.addCMacro("PLATFORM_WEB", "");
const wasm = b.addLibrary(.{
.name = filename,
.name = b.fmt("{s}.html", .{filename}),
.root_module = exe_mod,
});
@ -707,24 +890,40 @@ fn addExamples(
const emcc_flags = emsdk.emccDefaultFlags(b.allocator, .{ .optimize = optimize });
const emcc_settings = emsdk.emccDefaultSettings(b.allocator, .{ .optimize = optimize });
const EmccExamplesPreloadMap = std.static_string_map.StaticStringMap([]const emsdk.zemscripten.EmccFilePath);
const EmccExamplesPreloadKV = struct { []const u8, []const emsdk.zemscripten.EmccFilePath };
const emcc_examples_preloads: []const EmccExamplesPreloadKV = @import("examples/example_resources.zon");
const emcc_examples_preloads_map = EmccExamplesPreloadMap.initComptime(emcc_examples_preloads);
const SerialResourceFile = struct { src_path: []const u8, virtual_path: []const u8 };
const EmccExamplesPreloadMap = std.static_string_map.StaticStringMap([]const SerialResourceFile);
const EmccExamplesPreloadSerial = struct { []const u8, []const SerialResourceFile };
const emcc_examples_preloads_serial: []const EmccExamplesPreloadSerial = @import("examples/example_resources.zon");
const emcc_examples_preloads_map = EmccExamplesPreloadMap.initComptime(emcc_examples_preloads_serial);
const preload_paths: ?[]emsdk.ResourceFile = blk: {
if (emcc_examples_preloads_map.get(filename)) |resource_files| {
var rfs = try b.allocator.alloc(emsdk.ResourceFile, resource_files.len);
for (resource_files, 0..) |resource_file, rfidx| {
rfs[rfidx] = .{
.src_path = b.path(resource_file.src_path),
.virtual_path = resource_file.virtual_path,
};
}
break :blk rfs;
} else break :blk null;
};
const emcc_step = emsdk.emccStep(b, raylib, wasm, .{
const emcc_step = try emsdk.emccStep(b, &.{}, &.{ raylib, wasm }, .{
.optimize = optimize,
.flags = emcc_flags,
.settings = emcc_settings,
.preload_paths = emcc_examples_preloads_map.get(filename) orelse &.{},
.preload_paths = preload_paths,
.shell_file_path = b.path("src/shell.html"),
.install_dir = install_dir,
.out_file_name = wasm.name,
});
const html_filename = try std.fmt.allocPrint(b.allocator, "{s}.html", .{wasm.name});
const emrun_step = emsdk.emrunStep(
const emrun_step = try emsdk.emrunStep(
b,
b.getInstallPath(install_dir, html_filename),
b.graph.path(.install_prefix, b.fmt(
"web/{s}/{s}/{s}",
.{ module, filename, wasm.name },
)),
&.{},
);
@ -759,7 +958,7 @@ fn waylandGenerate(
comptime waylandDir: []const u8,
comptime source: bool,
) !void {
const dir = try b.build_root.handle.openDir(b.graph.io, waylandDir, .{ .iterate = true });
const dir = try b.root.openDir(b.graph.io, waylandDir, .{ .iterate = true });
defer dir.close(b.graph.io);
var iter = dir.iterate();
@ -793,4 +992,3 @@ fn waylandGenerate(
}
}
}

View File

@ -1,7 +1,7 @@
.{
.name = .raylib,
.version = "6.0.0",
.minimum_zig_version = "0.16.0",
.minimum_zig_version = "0.17.0",
.fingerprint = 0x13035e5cb8bc1ac2, // Changing this has security and trust implications.
@ -19,10 +19,7 @@
.emsdk = .{
.url = "git+https://github.com/emscripten-core/emsdk#4.0.9",
.hash = "N-V-__8AAJl1DwBezhYo_VE6f53mPVm00R-Fk28NPW7P14EQ",
},
.zemscripten = .{
.url = "git+https://github.com/zig-gamedev/zemscripten#3fa4b778852226c7346bdcc3c1486e875a9a6d02",
.hash = "zemscripten-0.2.0-dev-sRlDqApRAACspTbAZnuNKWIzfWzSYgYkb2nWAXZ-tqqt",
.lazy = true,
},
},

View File

@ -131,7 +131,7 @@ int main(void)
DrawText("Left/right to change wave type", 10, 30, 20, DARKGRAY);
// Draw the last 10 ms of uploaded audio
for (int i = 0; i < screenWidth; i++)
for (int i = 0; i < screenWidth - 1; i++)
{
Vector2 startPos = {(float)i, 250 - 50*buffer[SAMPLE_RATE - SAMPLE_RATE/100 + i*SAMPLE_RATE/100/screenWidth] };
Vector2 endPos = { (float)(i + 1), 250 - 50*buffer[SAMPLE_RATE - SAMPLE_RATE/100 + (i + 1)*SAMPLE_RATE/100/screenWidth] };

View File

@ -54,7 +54,7 @@ int main(void)
Camera2D camera = { 0 };
camera.target = (Vector2){ player.x + 20.0f, player.y + 20.0f };
camera.offset = (Vector2){ screenWidth/2.0f, screenHeight/2.0f };
camera.offset = (Vector2){ GetRenderWidth()/2.0f, GetRenderHeight()/2.0f };
camera.rotation = 0.0f;
camera.zoom = 1.0f;

View File

@ -65,8 +65,8 @@ int main(void)
// Get the world point that is under the mouse
Vector2 mouseWorldPos = GetScreenToWorld2D(GetMousePosition(), camera);
// Set the offset to where the mouse is
camera.offset = GetMousePosition();
// Set the offset to where the mouse is, mouse is scaled to viewport space, but offset is in unscaled pixels
camera.offset = Vector2Multiply(GetMousePosition(), GetWindowScaleDPI());
// Set the target to match, so that the camera maps the world space point
// under the cursor to the screen space point under the cursor at any zoom
@ -86,8 +86,8 @@ int main(void)
// Get the world point that is under the mouse
Vector2 mouseWorldPos = GetScreenToWorld2D(GetMousePosition(), camera);
// Set the offset to where the mouse is
camera.offset = GetMousePosition();
// Set the offset to where the mouse is, mouse is scaled to viewport space, but offset is in unscaled pixels
camera.offset = Vector2Multiply(GetMousePosition(), GetWindowScaleDPI());
// Set the target to match, so that the camera maps the world space point
// under the cursor to the screen space point under the cursor at any zoom

View File

@ -75,7 +75,7 @@ int main(void)
Camera2D camera = { 0 };
camera.target = player.position;
camera.offset = (Vector2){ screenWidth/2.0f, screenHeight/2.0f };
camera.offset = (Vector2){ GetRenderWidth()/2.0f, GetRenderHeight()/2.0f };
camera.rotation = 0.0f;
camera.zoom = 1.0f;

View File

@ -19,6 +19,7 @@
********************************************************************************************/
#include "raylib.h"
#include "raymath.h"
#define PLAYER_SIZE 40
@ -39,13 +40,13 @@ int main(void)
Camera2D camera1 = { 0 };
camera1.target = (Vector2){ player1.x, player1.y };
camera1.offset = (Vector2){ 200.0f, 200.0f };
camera1.offset = Vector2Multiply((Vector2){ 200.0f, 200.0f }, GetWindowScaleDPI());
camera1.rotation = 0.0f;
camera1.zoom = 1.0f;
Camera2D camera2 = { 0 };
camera2.target = (Vector2){ player2.x, player2.y };
camera2.offset = (Vector2){ 200.0f, 200.0f };
camera2.offset = Vector2Multiply((Vector2){ 200.0f, 200.0f }, GetWindowScaleDPI());
camera2.rotation = 0.0f;
camera2.zoom = 1.0f;

View File

@ -69,7 +69,7 @@ int main(void)
// Define camera
Camera2D camera = { 0 };
camera.target = player.position;
camera.offset = (Vector2){ screenWidth/2.0f, screenHeight/2.0f };
camera.offset = (Vector2){ GetRenderWidth()/2.0f, GetRenderHeight()/2.0f };
camera.rotation = 0.0f;
camera.zoom = 1.0f;
@ -117,7 +117,7 @@ int main(void)
player.canJump = false;
camera.target = player.position;
camera.offset = (Vector2){ screenWidth/2.0f, screenHeight/2.0f };
camera.offset = (Vector2){ GetRenderWidth()/2.0f, GetRenderHeight()/2.0f };
camera.rotation = 0.0f;
camera.zoom = 1.0f;
}
@ -169,7 +169,7 @@ int main(void)
player.canJump = false;
camera.target = player.position;
camera.offset = (Vector2){ screenWidth/2.0f, screenHeight/2.0f };
camera.offset = (Vector2){ GetRenderWidth()/2.0f, GetRenderHeight()/2.0f };
camera.rotation = 0.0f;
camera.zoom = 1.0f;
}
@ -205,7 +205,7 @@ int main(void)
// Update camera
//----------------------------------------------------------------------------------
camera.target = player.position;
camera.offset = (Vector2){ screenWidth/2.0f, screenHeight/2.0f };
camera.offset = (Vector2){ GetRenderWidth()/2.0f, GetRenderHeight()/2.0f };
float minX = 1000, minY = 1000, maxX = -1000, maxY = -1000;
// WARNING: On event replay, mouse-wheel internal value is set
@ -267,7 +267,7 @@ int main(void)
player.canJump = false;
camera.target = player.position;
camera.offset = (Vector2){ screenWidth/2.0f, screenHeight/2.0f };
camera.offset = (Vector2){ GetRenderWidth()/2.0f, GetRenderHeight()/2.0f };
camera.rotation = 0.0f;
camera.zoom = 1.0f;
}

View File

@ -92,7 +92,7 @@ int main(void)
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(TextFormat("Frame time: %02.02f ms", GetFrameTime()*1000.0f), 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

View File

@ -50,7 +50,7 @@ int main(void)
// NOTE: The gesture log uses an array (as an inverted circular queue) to store the performed gestures
char gestureLog[GESTURE_LOG_SIZE][12] = { "" };
// NOTE: The index for the inverted circular queue (moving from last to first direction, then looping around)
int gestureLogIndex = GESTURE_LOG_SIZE;
int gestureLogIndex = 0;
int previousGesture = 0;
// Log mode values:

View File

@ -114,7 +114,7 @@ int main(void)
if (IsKeyPressed(KEY_P)) animPause = !animPause;
if (!animPause)
if (!animPause && (animCount > 0)) // Animations could fail to load
{
// Start transition from anim0[] to anim1[]
if (IsKeyPressed(KEY_SPACE) && !animTransition)
@ -256,21 +256,24 @@ int main(void)
GuiSetStyle(DEFAULT, TEXT_SIZE, GuiGetFont().baseSize);
GuiSetStyle(LABEL, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT);
// Draw playing timeline with keyframes for anim0[]
GuiProgressBar((Rectangle){ 60, GetScreenHeight() - 60.0f, GetScreenWidth() - 180.0f, 20 }, "ANIM 0",
TextFormat("FRAME: %.2f / %i", animFrameProgress0, anims[animIndex0].keyframeCount),
&animFrameProgress0, 0.0f, (float)anims[animIndex0].keyframeCount);
for (int i = 0; i < anims[animIndex0].keyframeCount; i++)
DrawRectangle(60 + (int)(((float)(GetScreenWidth() - 180)/(float)anims[animIndex0].keyframeCount)*(float)i),
GetScreenHeight() - 60, 1, 20, BLUE);
if (animCount > 0)
{
// Draw playing timeline with keyframes for anim0[]
GuiProgressBar((Rectangle){ 60, GetScreenHeight() - 60.0f, GetScreenWidth() - 180.0f, 20 }, "ANIM 0",
TextFormat("FRAME: %.2f / %i", animFrameProgress0, anims[animIndex0].keyframeCount),
&animFrameProgress0, 0.0f, (float)anims[animIndex0].keyframeCount);
for (int i = 0; i < anims[animIndex0].keyframeCount; i++)
DrawRectangle(60 + (int)(((float)(GetScreenWidth() - 180)/(float)anims[animIndex0].keyframeCount)*(float)i),
GetScreenHeight() - 60, 1, 20, BLUE);
// Draw playing timeline with keyframes for anim1[]
GuiProgressBar((Rectangle){ 60, GetScreenHeight() - 30.0f, GetScreenWidth() - 180.0f, 20 }, "ANIM 1",
TextFormat("FRAME: %.2f / %i", animFrameProgress1, anims[animIndex1].keyframeCount),
&animFrameProgress1, 0.0f, (float)anims[animIndex1].keyframeCount);
for (int i = 0; i < anims[animIndex1].keyframeCount; i++)
DrawRectangle(60 + (int)(((float)(GetScreenWidth() - 180)/(float)anims[animIndex1].keyframeCount)*(float)i),
GetScreenHeight() - 30, 1, 20, BLUE);
// Draw playing timeline with keyframes for anim1[]
GuiProgressBar((Rectangle){ 60, GetScreenHeight() - 30.0f, GetScreenWidth() - 180.0f, 20 }, "ANIM 1",
TextFormat("FRAME: %.2f / %i", animFrameProgress1, anims[animIndex1].keyframeCount),
&animFrameProgress1, 0.0f, (float)anims[animIndex1].keyframeCount);
for (int i = 0; i < anims[animIndex1].keyframeCount; i++)
DrawRectangle(60 + (int)(((float)(GetScreenWidth() - 180)/(float)anims[animIndex1].keyframeCount)*(float)i),
GetScreenHeight() - 30, 1, 20, BLUE);
}
//---------------------------------------------------------------------------------------------
EndDrawing();

View File

@ -316,7 +316,7 @@ static void AddTriangleToMeshBuilder(MeshBuilder *mb, Vector3 vertices[3])
static void FreeMeshBuilder(MeshBuilder *mb)
{
MemFree(mb->vertices);
if (mb->uvs) MemFree(mb->uvs);
if (mb->uvs != NULL) MemFree(mb->uvs);
*mb = (MeshBuilder){ 0 };
}
@ -328,7 +328,7 @@ static Mesh BuildMesh(MeshBuilder *mb)
outMesh.vertexCount = mb->vertexCount;
outMesh.triangleCount = mb->vertexCount/3;
outMesh.vertices = MemAlloc(outMesh.vertexCount*3*sizeof(float));
if (mb->uvs) outMesh.texcoords = MemAlloc(outMesh.vertexCount*2*sizeof(float));
if (mb->uvs != NULL) outMesh.texcoords = MemAlloc(outMesh.vertexCount*2*sizeof(float));
for (int i = 0; i < mb->vertexCount; i++)
{
@ -336,7 +336,7 @@ static Mesh BuildMesh(MeshBuilder *mb)
outMesh.vertices[3*i+1] = mb->vertices[i].y;
outMesh.vertices[3*i+2] = mb->vertices[i].z;
if (mb->uvs)
if (mb->uvs != NULL)
{
outMesh.texcoords[2*i+0] = mb->uvs[i].x;
outMesh.texcoords[2*i+1] = mb->uvs[i].y;

View File

@ -106,4 +106,3 @@ int main(void)
}

View File

@ -149,7 +149,7 @@ int main(void)
GLFWwindow *window = glfwCreateWindow(screenWidth, screenHeight, "raylib [others] example - rlgl standalone", NULL, NULL);
if (!window)
if (window == NULL)
{
glfwTerminate();
return 2;

View File

@ -139,6 +139,9 @@ int main(void)
// De-Initialization
//--------------------------------------------------------------------------------------
RL_FREE(transforms); // Free transforms
UnloadMaterial(matInstances); // Also unloads the instancing shader
UnloadMaterial(matDefault); // Unload default material
UnloadMesh(cube); // Unload cube mesh
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------

View File

@ -56,7 +56,7 @@ int main(void)
Vector2 mousePos = { 0 };
Vector2 prevMousePos = { 0 };
Vector2 scaleVector = { 1.0f, -1.0f };
Vector2 offset = { (float)screenWidth/2.0f, (float)screenHeight/2.0f };
Vector2 offset = { GetRenderWidth()/2.0f, GetRenderHeight()/2.0f };
Camera2D camera = { 0 };
camera.target = (Vector2){ 0 };

View File

@ -16,12 +16,13 @@
********************************************************************************************/
#include "raylib.h"
#include <math.h>
#include <stdio.h>
#define RAYGUI_IMPLEMENTATION
#include "raygui.h"
#include <math.h>
#include <stdio.h>
#define MAX_PIE_SLICES 10 // Max pie slices
//------------------------------------------------------------------------------------
@ -39,12 +40,8 @@ int main(void)
int sliceCount = 7;
float donutInnerRadius = 25.0f;
float values[MAX_PIE_SLICES] = { 300.0f, 100.0f, 450.0f, 350.0f, 600.0f, 380.0f, 750.0f }; // Initial slice values
char labels[MAX_PIE_SLICES][32] = { 0 };
bool editingLabel[MAX_PIE_SLICES] = { 0 };
for (int i = 0; i < MAX_PIE_SLICES; i++)
snprintf(labels[i], 32, "Slice %02i", i + 1);
bool showValues = true;
bool showPercentages = false;
bool showDonut = false;
@ -59,7 +56,7 @@ int main(void)
// UI Panel top-left anchor
const Vector2 panelPos = {
(float)screenWidth - panelMargin - panelWidth,
(float)screenWidth - panelMargin - panelWidth,
(float)panelMargin
};
@ -72,7 +69,7 @@ int main(void)
// Pie chart geometry
const Rectangle canvas = { 0, 0, panelPos.x, (float)screenHeight };
const Vector2 center = { canvas.width/2.0f, canvas.height/2.0f};
const Vector2 center = { canvas.width/2.0f, canvas.height/2.0f };
const float radius = 205.0f;
// Total value for percentage calculations
@ -93,6 +90,7 @@ int main(void)
// Check for mouse hover over slices
hoveredSlice = -1; // Reset hovered slice
Vector2 mousePos = GetMousePosition();
if (CheckCollisionPointRec(mousePos, canvas)) // Only check if mouse is inside the canvas
{
float dx = mousePos.x - center.x;
@ -176,7 +174,7 @@ int main(void)
if (showDonut) GuiDisable();
GuiSliderBar((Rectangle){ panelPos.x + 80, (float)panelPos.y + 12 + 130, panelRect.width - 100, 30 },
"Inner Radius", NULL, &donutInnerRadius, 5.0f, radius - 10.0f);
"Inner Radius", NULL, & donutInnerRadius, 5.0f, radius - 10.0f);
GuiEnable();
GuiLine((Rectangle){ panelPos.x + 10, (float)panelPos.y + 12 + 170, panelRect.width - 20, 1 }, NULL);
@ -190,8 +188,7 @@ int main(void)
};
int contentHeight = sliceCount*35;
GuiScrollPanel(scrollPanelBounds, NULL,
(Rectangle){ 0, 0, panelRect.width - 25, (float)contentHeight },
GuiScrollPanel(scrollPanelBounds, NULL, (Rectangle){ 0, 0, panelRect.width - 25, (float)contentHeight },
&scrollContentOffset, &view);
const float contentX = view.x + scrollContentOffset.x; // Left of content
@ -208,7 +205,8 @@ int main(void)
DrawRectangle((int)(contentX + 15), rowY + 5, 20, 20, color);
// Label textbox
if (GuiTextBox((Rectangle){ contentX + 45, (float)rowY, 75, 30 }, labels[i], 32, editingLabel[i])) editingLabel[i] = !editingLabel[i];
if (GuiTextBox((Rectangle){ contentX + 45, (float)rowY, 75, 30 }, TextFormat("Slice %02i", i + 1), 32, editingLabel[i]))
editingLabel[i] = !editingLabel[i];
GuiSliderBar((Rectangle){ contentX + 130, (float)rowY, 110, 30 }, NULL, NULL, &values[i], 0.0f, 1000.0f);
}

View File

@ -149,7 +149,7 @@ static int *CodepointRemoveDuplicates(int *codepoints, int codepointCount, int *
{
if (codepointsNoDups[i] == codepointsNoDups[j])
{
for (int k = j; k < codepointsNoDupsCount; k++) codepointsNoDups[k] = codepointsNoDups[k + 1];
for (int k = j; k < codepointsNoDupsCount - 1; k++) codepointsNoDups[k] = codepointsNoDups[k + 1];
codepointsNoDupsCount--;
j--;

View File

@ -50,7 +50,7 @@ int main(void)
if (IsKeyPressed(KEY_R)) // Reset image collection
{
// Unload textures to avoid memory leaks
for (int i = 0; i < MAX_TEXTURE_COLLECTION; i++) UnloadTexture(collection[i].texture);
for (int i = 0; i < currentCollectionIndex; i++) UnloadTexture(collection[i].texture);
currentCollectionIndex = 0;
}
@ -98,7 +98,7 @@ int main(void)
// De-Initialization
//--------------------------------------------------------------------------------------
for (int i = 0; i < MAX_TEXTURE_COLLECTION;i ++)
for (int i = 0; i < currentCollectionIndex; i++)
{
UnloadTexture(collection[i].texture);
}

View File

@ -109,7 +109,7 @@ int main(void)
else if (IsKeyPressed(KEY_UP))
{
currentProcess--;
if (currentProcess < 0) currentProcess = 7;
if (currentProcess < 0) currentProcess = (NUM_PROCESSES - 1);
textureReload = true;
}

View File

@ -16,7 +16,9 @@
********************************************************************************************/
#include "raylib.h"
#include "rlgl.h" // for rlSetBlendFactorsSeparate()
#include "rlgl.h" // Required for: rlSetBlendFactorsSeparate()
#include "raymath.h" // Required for: Vector2Multiply()
//------------------------------------------------------------------------------------
// Program main entry point
@ -45,8 +47,7 @@ int main(void)
// Set magnifying glass zoom
camera.zoom = 2;
// Offset by half the size of the magnifying glass to counteract drawing the texture centered on the mouse position
camera.offset = (Vector2){128, 128};
camera.offset = Vector2Multiply((Vector2){ 128, 128 }, GetWindowScaleDPI());
SetTargetFPS(60);
//--------------------------------------------------------------------------------------

View File

@ -95,7 +95,7 @@ int main(void)
if (!IsMouseButtonDown(MOUSE_LEFT_BUTTON)) curveSelectedPoint = NULL;
// If a point was selected, move it
if (curveSelectedPoint) *curveSelectedPoint = Vector2Add(*curveSelectedPoint, GetMouseDelta());
if (curveSelectedPoint != NULL) *curveSelectedPoint = Vector2Add(*curveSelectedPoint, GetMouseDelta());
// The mouse is down, and nothing was selected, so see if anything was picked
Vector2 mouse = GetMousePosition();

View File

@ -30,7 +30,7 @@ target_link_libraries(${PROJECT_NAME} raylib)
# Web Configurations
if (${PLATFORM} STREQUAL "Web")
set_target_properties(${PROJECT_NAME} PROPERTIES SUFFIX ".html") # Tell Emscripten to build an example.html file.
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s USE_GLFW=3 -s ASSERTIONS=1 -s WASM=1 -s ASYNCIFY -s GL_ENABLE_GET_PROC_ADDRESS=1")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s USE_GLFW=3 -s ASSERTIONS=1 -s WASM=1 -s ASYNCIFY")
endif()
# Checks if OSX and links appropriate frameworks (Only required on MacOS)

View File

@ -356,6 +356,16 @@ ifeq ($(TARGET_PLATFORM),PLATFORM_ANDROID)
AR = $(ANDROID_TOOLCHAIN)/bin/llvm-ar
endif
# NOTE: Native Linux links against glibc, on glibc >= 2.38, _GNU_SOURCE redirects
# sscanf(), strtol()... to __isoc23_*(), so the library fails to link with older glibc
# Web and Android set PLATFORM_OS from the host, but they are not glibc and keep _GNU_SOURCE
DEFINED_SOURCE_MACRO = _GNU_SOURCE
ifeq ($(PLATFORM_OS), LINUX)
ifeq ($(filter $(TARGET_PLATFORM),PLATFORM_WEB PLATFORM_WEB_RGFW PLATFORM_ANDROID),)
DEFINED_SOURCE_MACRO = _DEFAULT_SOURCE
endif
endif
# Define compiler flags: CFLAGS
#------------------------------------------------------------------------------------------------
# -O1 defines optimization level
@ -370,7 +380,7 @@ endif
# -D_GNU_SOURCE access to lots of nonstandard GNU/Linux extension functions
# -Werror=pointer-arith catch unportable code that does direct arithmetic on void pointers
# -fno-strict-aliasing jar_xm.h does shady stuff (breaks strict aliasing)
CFLAGS = -Wall -D_GNU_SOURCE -D$(TARGET_PLATFORM) -D$(GRAPHICS) -Wno-missing-braces -Werror=pointer-arith -fno-strict-aliasing
CFLAGS = -Wall -D$(DEFINED_SOURCE_MACRO) -D$(TARGET_PLATFORM) -D$(GRAPHICS) -Wno-missing-braces -Werror=pointer-arith -fno-strict-aliasing
ifneq ($(RAYLIB_CONFIG_FLAGS), NONE)
CFLAGS += -DEXTERNAL_CONFIG_FLAGS $(RAYLIB_CONFIG_FLAGS)

View File

@ -733,6 +733,7 @@ void Vox_FreeArrays(VoxArray3D* voxarray)
// Free arrays
freeArrayVector3(&voxarray->vertices);
freeArrayVector3(&voxarray->normals);
freeArrayUShort(&voxarray->indices);
freeArrayColor(&voxarray->colors);
}

View File

@ -285,7 +285,6 @@ static int android_close(void *cookie);
// CMake: handled automatically — the PUBLIC flag propagates as INTERFACE_LINK_OPTIONS
// to the consumer's final link via target_link_libraries
// Make: pass -Wl,--wrap=fopen to the linker command producing the final artifact
// build.zig: pass -Wl,--wrap=fopen to the linker command producing the final artifact
// custom: pass -Wl,--wrap=fopen to the linker command producing the final artifact
//
// SHARED library (.so) — wrapping is self-contained:
@ -296,8 +295,6 @@ static int android_close(void *cookie);
// only raylib internals are wrapped, app code requires a separate flag
// Make: handled automatically — src/Makefile sets LDFLAGS += -Wl,--wrap=fopen;
// only raylib internals are wrapped, app code requires a separate flag
// build.zig: NOT supported — std.Build has no dedicated linker wrap helper, the flag
// is not correctly applied at the .so link step
// custom: apply -Wl,--wrap=fopen to the linker command producing the .so
FILE *__real_fopen(const char *fileName, const char *mode); // Real fopen, provided by the linker (--wrap=fopen)
FILE *__wrap_fopen(const char *fileName, const char *mode); // Replacement for fopen()

View File

@ -2093,7 +2093,7 @@ static void WindowDropCallback(GLFWwindow *window, int count, const char **paths
// GLFW3: Keyboard callback, runs on key pressed
static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods)
{
if (key < 0) return; // Security check, macOS fn key generates -1
if ((key < 0) || (key >= MAX_KEYBOARD_KEYS)) return; // Security check, macOS fn key generates -1
// WARNING: GLFW could return GLFW_REPEAT, it needs to be considered as 1
// to work properly with our implementation (IsKeyDown/IsKeyUp checks)

View File

@ -1431,7 +1431,11 @@ void PollInputEvents(void)
CORE.Input.Mouse.currentWheelMove.y = 0;
// Register previous mouse position
if (CORE.Input.Mouse.cursorLocked) CORE.Input.Mouse.currentPosition = (Vector2){ 0.0f, 0.0f };
if (CORE.Input.Mouse.cursorLocked)
{
CORE.Input.Mouse.currentPosition = (Vector2){ 0.0f, 0.0f };
CORE.Input.Mouse.previousPosition = (Vector2){ 0.0f, 0.0f };
}
else CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
// Reset last gamepad button/axis registered state
@ -1727,9 +1731,8 @@ void PollInputEvents(void)
{
if (CORE.Input.Mouse.cursorLocked)
{
CORE.Input.Mouse.currentPosition.x = (float)event.motion.xrel;
CORE.Input.Mouse.currentPosition.y = (float)event.motion.yrel;
CORE.Input.Mouse.previousPosition = (Vector2){ 0.0f, 0.0f };
CORE.Input.Mouse.currentPosition.x += (float)event.motion.xrel;
CORE.Input.Mouse.currentPosition.y += (float)event.motion.yrel;
}
else
{

View File

@ -92,11 +92,13 @@ typedef struct {
HBITMAP hbitmap; // GDI bitmap handler
unsigned int *pixels; // Pointer to pixel data buffer (BGRA format)
unsigned int appScreenWidth;
unsigned int appScreenHeight;
int appScreenWidth;
int appScreenHeight;
unsigned int desiredFlags;
LARGE_INTEGER timerFrequency;
char *clipboardText;
} PlatformData;
// Define WGL function pointer types (no wglext.h needed)
@ -393,7 +395,7 @@ static bool UpdateWindowSize(int mode, HWND hwnd, int width, int height, unsigne
SIZE clientSize = { rect.right, rect.bottom };
// If client size is alread desired size, no need to update
if ((clientSize.cx == desiredSize.cx) || (clientSize.cy == desiredSize.cy)) return false;
if ((clientSize.cx == desiredSize.cx) && (clientSize.cy == desiredSize.cy)) return false;
TRACELOG(LOG_INFO, "WIN32: Restoring client size from [%dx%d] to [%dx%d] (dpi:%lu dpiScaling:%d app:%ix%i)",
clientSize.cx, clientSize.cy, desiredSize.cx, desiredSize.cy, dpi, dpiScaling, width, height);
@ -925,7 +927,8 @@ void SetWindowIcon(Image image)
{
// Set both large and small icons
SendMessage(platform.hwnd, WM_SETICON, ICON_BIG, (LPARAM)hIcon);
SendMessage(platform.hwnd, WM_SETICON, ICON_SMALL, (LPARAM)hIcon);
HICON previousIcon = (HICON)SendMessage(platform.hwnd, WM_SETICON, ICON_SMALL, (LPARAM)hIcon);
if (previousIcon) DestroyIcon(previousIcon);
}
}
@ -1119,14 +1122,101 @@ Vector2 GetWindowScaleDPI(void)
// Set clipboard text content
void SetClipboardText(const char *text)
{
TRACELOG(LOG_WARNING, "SetClipboardText not implemented");
if (OpenClipboard(platform.hwnd))
{
if (text == NULL)
{
EmptyClipboard();
}
else
{
size_t length = strlen(text);
int clipboardTextLength = MultiByteToWideChar(CP_UTF8, 0, text, (int)(length + 1), NULL, 0);
if (clipboardTextLength != 0)
{
HGLOBAL globalHandle = GlobalAlloc(GMEM_MOVEABLE, clipboardTextLength*sizeof(wchar_t));
if (globalHandle)
{
LPVOID clipboardText = GlobalLock(globalHandle);
int charactersCopied = 0;
if (clipboardText != NULL)
{
// Directly copies text to clipboardText and converts from UTF-8 to UTF-16
charactersCopied = MultiByteToWideChar(CP_UTF8, 0, text, (int)(length + 1), clipboardText, clipboardTextLength);
GlobalUnlock(globalHandle);
}
if (charactersCopied != 0)
{
EmptyClipboard();
// We don't need to call GlobalFree when SetClipboardData succeeds; Windows takes control of the memory
HANDLE clipboardData = SetClipboardData(CF_UNICODETEXT, globalHandle);
if (!clipboardData)
{
TRACELOG(LOG_WARNING, "Clipboard: Failed to set clipboard text.");
GlobalFree(globalHandle);
}
}
else
{
GlobalFree(globalHandle);
}
}
}
}
CloseClipboard();
}
else
{
TRACELOG(LOG_WARNING, "Clipboard: Failed to write to clipboard. It may be locked by another program.");
}
}
// Get clipboard text content
const char *GetClipboardText(void)
{
TRACELOG(LOG_WARNING, "GetClipboardText not implemented");
return NULL;
if (platform.clipboardText != NULL)
{
RL_FREE(platform.clipboardText);
platform.clipboardText = NULL;
}
if (OpenClipboard(platform.hwnd))
{
HANDLE clipboardDataHandle = GetClipboardData(CF_UNICODETEXT);
if (clipboardDataHandle)
{
wchar_t *clipboardText = GlobalLock(clipboardDataHandle);
if (clipboardText != NULL)
{
// The clipboard data can't be trusted. It may or may not be null-terminated!
size_t maxLength = GlobalSize(clipboardDataHandle)/sizeof(wchar_t);
int length = (int)wcsnlen(clipboardText, maxLength);
int size = WideCharToMultiByte(CP_UTF8, 0, clipboardText, length, NULL, 0, NULL, NULL);
char *utf8Text = (char *)RL_MALLOC(size + 1);
if ((size > 0) && (WideCharToMultiByte(CP_UTF8, 0, clipboardText, length, utf8Text, size, NULL, NULL) == 0)) size = 0;
utf8Text[size] = '\0';
platform.clipboardText = utf8Text;
GlobalUnlock(clipboardDataHandle);
}
}
CloseClipboard();
}
else
{
TRACELOG(LOG_WARNING, "Clipboard: Failed to read clipboard. It may be locked by another program.");
}
return platform.clipboardText;
}
#if SUPPORT_CLIPBOARD_IMAGE && SUPPORT_MODULE_RTEXTURES
@ -1374,7 +1464,6 @@ void PollInputEvents(void)
//for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.position[i] = (Vector2){ 0, 0 };
memcpy(CORE.Input.Keyboard.previousKeyState, CORE.Input.Keyboard.currentKeyState, sizeof(CORE.Input.Keyboard.previousKeyState));
memset(CORE.Input.Keyboard.keyRepeatInFrame, 0, sizeof(CORE.Input.Keyboard.keyRepeatInFrame));
// Register previous mouse wheel state
CORE.Input.Mouse.previousWheelMove = CORE.Input.Mouse.currentWheelMove;
@ -1402,7 +1491,23 @@ void PollInputEvents(void)
// NOTE: Creating a dummy context first to query required extensions
HGLRC InitOpenGL(HWND hwnd, HDC hdc)
{
// First, create a dummy context to get WGL extensions
// Create an invisible window for initial dummy OpenGL context
WNDCLASSEXW dummyWindowClass = {
.cbSize = sizeof(WNDCLASSEXW),
.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC,
.lpfnWndProc = DefWindowProcW,
.hInstance = GetModuleHandleW(NULL),
.lpszClassName = L"wglDummy"
};
RegisterClassExW(&dummyWindowClass);
HWND dummyWindow = CreateWindowExW(
0, dummyWindowClass.lpszClassName, L"dummyWindow",
0, 0, 0, 1, 1, NULL, NULL, NULL, NULL);
HDC dummyHDC = GetDC(dummyWindow);
// Create a dummy OpenGL context to get WGL extensions
PIXELFORMATDESCRIPTOR pixelFormatDesc = {
.nSize = sizeof(PIXELFORMATDESCRIPTOR),
.nVersion = 1,
@ -1411,18 +1516,19 @@ HGLRC InitOpenGL(HWND hwnd, HDC hdc)
.cColorBits = 32,
.cAlphaBits = 8,
.cDepthBits = 24,
.cStencilBits = 8,
.iLayerType = PFD_MAIN_PLANE
};
int pixelFormat = ChoosePixelFormat(hdc, &pixelFormatDesc);
SetPixelFormat(hdc, pixelFormat, &pixelFormatDesc);
int dummyPixelFormat = ChoosePixelFormat(dummyHDC, &pixelFormatDesc);
SetPixelFormat(dummyHDC, dummyPixelFormat, &pixelFormatDesc);
//int pixelFormat = ChoosePixelFormat(platform.hdc, &pixelFormatDesc);
//if (!pixelFormat) { TRACELOG(LOG_ERROR, "%s failed, error=%lu", "ChoosePixelFormat", GetLastError()); return -1; }
//if (!SetPixelFormat(platform.hdc, pixelFormat, &pixelFormatDesc)) { TRACELOG(LOG_ERROR, "%s failed, error=%lu", "SetPixelFormat", GetLastError()); return -1; }
HGLRC tempContext = wglCreateContext(hdc);
HGLRC tempContext = wglCreateContext(dummyHDC);
//if (!tempContext) { TRACELOG(LOG_ERROR, "%s failed, error=%lu", "wglCreateContext", GetLastError()); return -1; }
BOOL result = wglMakeCurrent(hdc, tempContext);
BOOL result = wglMakeCurrent(dummyHDC, tempContext);
//if (!result) { TRACELOG(LOG_ERROR, "%s failed, error=%lu", "wglMakeCurrent", GetLastError()); return -1; }
// Load WGL extension entry points
@ -1432,8 +1538,11 @@ HGLRC InitOpenGL(HWND hwnd, HDC hdc)
wglGetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARBPROC)wglGetProcAddress("wglGetExtensionsStringARB");
// Setup modern pixel format if extension is available
BOOL setPixelFormatSucceeded = FALSE;
if (wglChoosePixelFormatARB)
{
bool useMSAA = FLAG_IS_SET(CORE.Window.flags, FLAG_MSAA_4X_HINT);
int pixelFormatAttribs[] = {
WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB,
WGL_DRAW_TO_WINDOW_ARB, GL_TRUE,
@ -1441,12 +1550,14 @@ HGLRC InitOpenGL(HWND hwnd, HDC hdc)
WGL_DOUBLE_BUFFER_ARB, GL_TRUE,
WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB,
WGL_COLOR_BITS_ARB, 32,
//WGL_RED_BITS_ARB, 8,
//WGL_GREEN_BITS_ARB, 8,
//WGL_BLUE_BITS_ARB, 8,
//WGL_ALPHA_BITS_ARB, 8,
WGL_RED_BITS_ARB, 8,
WGL_GREEN_BITS_ARB, 8,
WGL_BLUE_BITS_ARB, 8,
WGL_ALPHA_BITS_ARB, 8,
WGL_DEPTH_BITS_ARB, 24,
WGL_STENCIL_BITS_ARB, 8,
WGL_SAMPLES_ARB, (useMSAA)? 4 : 0,
WGL_SAMPLE_BUFFERS_ARB, (int)useMSAA,
0 // Terminator
};
@ -1456,7 +1567,23 @@ HGLRC InitOpenGL(HWND hwnd, HDC hdc)
{
PIXELFORMATDESCRIPTOR newPixelFormatDescriptor = { 0 };
DescribePixelFormat(hdc, format, sizeof(newPixelFormatDescriptor), &newPixelFormatDescriptor);
SetPixelFormat(hdc, format, &newPixelFormatDescriptor);
setPixelFormatSucceeded = SetPixelFormat(hdc, format, &newPixelFormatDescriptor);
}
}
if (!setPixelFormatSucceeded)
{
// If modern pixel format selection failed, fall back to old SetPixelFormat
int pixelFormat = ChoosePixelFormat(hdc, &pixelFormatDesc);
setPixelFormatSucceeded = SetPixelFormat(hdc, pixelFormat, &pixelFormatDesc);
if (setPixelFormatSucceeded)
{
TRACELOG(LOG_WARNING, "WGL: Using legacy pixel format, MSAA not available");
}
else
{
TRACELOG(LOG_ERROR, "WGL: Unable to find a suitable pixel format with this graphics driver");
}
}
@ -1522,10 +1649,18 @@ HGLRC InitOpenGL(HWND hwnd, HDC hdc)
// ERROR_INVALID_PROFILE_ARB (0x2096)
if (realContext == NULL) TRACELOG(LOG_ERROR, "GL: Error creating requested context: %lu", GetLastError());
}
else
{
// If modern context extension isn't available, fall back to classic context creation
realContext = wglCreateContext(hdc);
}
// Cleanup dummy temp context
// Cleanup dummy temp context and window
wglMakeCurrent(NULL, NULL);
wglDeleteContext(tempContext);
ReleaseDC(dummyWindow, dummyHDC);
DestroyWindow(dummyWindow);
UnregisterClassW(dummyWindowClass.lpszClassName, dummyWindowClass.hInstance);
// Activate real context
if (realContext) wglMakeCurrent(hdc, realContext);
@ -1722,12 +1857,43 @@ int InitPlatform(void)
// Close platform
void ClosePlatform(void)
{
// Undo disable cursor if it's locked and unregister raw input devices
EnableCursor();
// Calling DestroyWindow invokes a WindowProc event that sets the FLAG_WINDOW_HIDDEN flag to true
// If InitWindow is called again, the hidden flag will be interpreted as something the user requested
// Save the current flags and restore them after
unsigned int savedFlags = CORE.Window.flags;
if (platform.hwnd)
{
int result = DestroyWindow(platform.hwnd);
if (result == 0) TRACELOG(LOG_WARNING, "WIN32: WINDOW: Failed on window destroy [ERROR: %u]", GetLastError());
// SetWindowIcon uses the same resource for both ICON_BIG and ICON_SMALL
// If this ever changes, this code may have to be updated
HICON icon = (HICON)SendMessage(platform.hwnd, WM_GETICON, ICON_BIG, (LPARAM)0);
BOOL result = DestroyWindow(platform.hwnd);
if (!result) TRACELOG(LOG_WARNING, "WIN32: WINDOW: Failed on window destroy [ERROR: %u]", GetLastError());
HINSTANCE hInstance = GetModuleHandleW(0);
result = UnregisterClassW(CLASS_NAME, hInstance);
if (!result) TRACELOG(LOG_WARNING, "WIN32: WINDOW: Failed to unregister window class [ERROR: %u]", GetLastError());
if (icon != NULL) DestroyIcon(icon);
platform.hwnd = NULL;
}
if (platform.clipboardText != NULL)
{
RL_FREE(platform.clipboardText);
platform.clipboardText = NULL;
}
CORE.Window.flags = savedFlags;
#if SUPPORT_WINMM_HIGHRES_TIMER && !SUPPORT_BUSY_WAIT_LOOP
timeEndPeriod(1); // Restore time period
#endif
}
// Window procedure, message processing callback
@ -1958,8 +2124,8 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lpara
case WM_DPICHANGED:
{
// Get current dpi scale factor
float scalex = HIWORD(wparam)/96.0f;
float scaley = LOWORD(wparam)/96.0f;
//float scalex = HIWORD(wparam)/96.0f;
//float scaley = LOWORD(wparam)/96.0f;
RECT *suggestedRect = (RECT *)lparam;
@ -2000,7 +2166,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lpara
EndPaint(hwnd, &ps);
}
else DefWindowProc(hwnd, msg, wparam, lparam);
}
} break;
case WM_INPUT:
{
//HandleRawInput(lparam);
@ -2074,6 +2240,7 @@ static void HandleKey(WPARAM wparam, LPARAM lparam, char state)
{
CORE.Input.Keyboard.currentKeyState[key] = state;
if (((HIWORD(lparam) & KF_REPEAT) != 0) && (state == 1)) CORE.Input.Keyboard.keyRepeatInFrame[key] = 1;
if ((key == CORE.Input.Keyboard.exitKey) && (state == 1)) CORE.Window.shouldClose = true;
}
else TRACELOG(LOG_WARNING, "INPUT: Unknown (or currently unhandled) virtual keycode %d (0x%x)", wparam, wparam);

View File

@ -111,6 +111,9 @@ static const char cursorLUT[11][12] = {
int InitPlatform(void); // Initialize platform (graphics, inputs and more)
void ClosePlatform(void); // Close platform
// OpenGL extensions loader
static void *WebGLGetProcAddress(const char *procName); // Get WebGL extension procedure address, used by rlLoadExtensions()
// Error callback event
static void ErrorCallback(int error, const char *description); // GLFW3 Error Callback, runs on GLFW3 error
@ -1442,7 +1445,7 @@ int InitPlatform(void)
// Load OpenGL extensions
// NOTE: GL procedures address loader is required to load extensions
rlLoadExtensions(glfwGetProcAddress);
rlLoadExtensions(WebGLGetProcAddress);
//----------------------------------------------------------------------------
// Initialize events callbacks
@ -1499,6 +1502,24 @@ void ClosePlatform(void)
glfwTerminate();
}
// Get WebGL extension procedure address
// WARNING: rlGetProcAddress() uses this loader, so it only returns the functions listed below
static void *WebGLGetProcAddress(const char *procName)
{
void *proc = NULL;
#if defined(GRAPHICS_API_OPENGL_ES2)
if (strcmp(procName, "glGenVertexArraysOES") == 0) proc = (void *)glGenVertexArraysOES;
else if (strcmp(procName, "glBindVertexArrayOES") == 0) proc = (void *)glBindVertexArrayOES;
else if (strcmp(procName, "glDeleteVertexArraysOES") == 0) proc = (void *)glDeleteVertexArraysOES;
else if (strcmp(procName, "glDrawArraysInstancedANGLE") == 0) proc = (void *)glDrawArraysInstancedANGLE;
else if (strcmp(procName, "glDrawElementsInstancedANGLE") == 0) proc = (void *)glDrawElementsInstancedANGLE;
else if (strcmp(procName, "glVertexAttribDivisorANGLE") == 0) proc = (void *)glVertexAttribDivisorANGLE;
#endif
return proc;
}
// GLFW3 callback functions, called on GLFW registered events
//-------------------------------------------------------------------------------------------------------
// GLFW3: Called on errors

View File

@ -209,6 +209,9 @@ static const char cursorLUT[11][12] = {
int InitPlatform(void); // Initialize platform (graphics, inputs and more)
void ClosePlatform(void); // Close platform
// OpenGL extensions loader
static void *WebGLGetProcAddress(const char *procName); // Get WebGL extension procedure address, used by rlLoadExtensions()
// Emscripten window callback events
static EM_BOOL EmscriptenResizeCallback(int eventType, const EmscriptenUiEvent *event, void *userData);
static EM_BOOL EmscriptenFocusCallback(int eventType, const EmscriptenFocusEvent *focusEvent, void *userData);
@ -1387,7 +1390,7 @@ int InitPlatform(void)
// Load OpenGL extensions
// NOTE: GL procedures address loader is required to load extensions
if (platform.glContext != 0) rlLoadExtensions(emscripten_webgl_get_proc_address);
if (platform.glContext != 0) rlLoadExtensions(WebGLGetProcAddress);
//----------------------------------------------------------------------------
// Initialize events callbacks
@ -1450,6 +1453,24 @@ void ClosePlatform(void)
if (platform.glContext != 0) emscripten_webgl_destroy_context(platform.glContext);
}
// Get WebGL extension procedure address
// WARNING: rlGetProcAddress() uses this loader, so it only returns the functions listed below
static void *WebGLGetProcAddress(const char *procName)
{
void *proc = NULL;
#if defined(GRAPHICS_API_OPENGL_ES2)
if (strcmp(procName, "glGenVertexArraysOES") == 0) proc = (void *)glGenVertexArraysOES;
else if (strcmp(procName, "glBindVertexArrayOES") == 0) proc = (void *)glBindVertexArrayOES;
else if (strcmp(procName, "glDeleteVertexArraysOES") == 0) proc = (void *)glDeleteVertexArraysOES;
else if (strcmp(procName, "glDrawArraysInstancedANGLE") == 0) proc = (void *)glDrawArraysInstancedANGLE;
else if (strcmp(procName, "glDrawElementsInstancedANGLE") == 0) proc = (void *)glDrawElementsInstancedANGLE;
else if (strcmp(procName, "glVertexAttribDivisorANGLE") == 0) proc = (void *)glVertexAttribDivisorANGLE;
#endif
return proc;
}
// Emscripten callback functions, called on specific browser events
//-------------------------------------------------------------------------------------------------------
// Emscripten: Called on resize event

View File

@ -988,7 +988,7 @@ Sound LoadSoundAlias(Sound source)
{
Sound sound = { 0 };
if (source.stream.buffer->data != NULL)
if ((source.stream.buffer != NULL) && (source.stream.buffer->data != NULL))
{
AudioBuffer *audioBuffer = LoadAudioBuffer(AUDIO_DEVICE_FORMAT, AUDIO_DEVICE_CHANNELS, AUDIO.System.device.sampleRate, 0, AUDIO_BUFFER_USAGE_STATIC);

View File

@ -336,7 +336,7 @@ typedef Camera3D Camera; // Camera type fallback, defaults to Camera3D
// Camera2D, defines position/orientation in 2d space
typedef struct Camera2D {
Vector2 offset; // Camera offset (screen space offset from window origin)
Vector2 offset; // Camera offset (screen space offset from window origin, unscaled by DPI, real pixel size)
Vector2 target; // Camera target (world space target point that is mapped to screen space offset)
float rotation; // Camera rotation in degrees (pivots around target)
float zoom; // Camera zoom (scaling around target), must not be set to 0, set to 1.0f for no scale

View File

@ -2381,10 +2381,31 @@ RMAPI Quaternion QuaternionFromVector3ToVector3(Vector3 from, Vector3 to)
float cos2Theta = (from.x*to.x + from.y*to.y + from.z*to.z); // Vector3DotProduct(from, to)
Vector3 cross = { from.y*to.z - from.z*to.y, from.z*to.x - from.x*to.z, from.x*to.y - from.y*to.x }; // Vector3CrossProduct(from, to)
float lengths = sqrtf(cross.x*cross.x + cross.y*cross.y + cross.z*cross.z + cos2Theta*cos2Theta); // |from|*|to|
result.x = cross.x;
result.y = cross.y;
result.z = cross.z;
result.w = sqrtf(cross.x*cross.x + cross.y*cross.y + cross.z*cross.z + cos2Theta*cos2Theta) + cos2Theta;
result.w = lengths + cos2Theta;
// Opposite vectors: cross product is zero, rotate 180 degrees around any axis perpendicular to 'from'
if ((lengths > 0.0f) && (result.w <= lengths*EPSILON))
{
if (fabsf(from.x) > fabsf(from.z))
{
result.x = -from.y;
result.y = from.x;
result.z = 0.0f;
}
else
{
result.x = 0.0f;
result.y = -from.z;
result.z = from.y;
}
result.w = 0.0f;
}
// QuaternionNormalize(q);
// NOTE: Normalize to essentially nlerp the original and identity to 0.5
@ -2717,9 +2738,9 @@ RMAPI void MatrixDecompose(Matrix mat, Vector3 *translation, Quaternion *rotatio
translation->z = mat.m14;
// Matrix Columns - Rotation will be extracted into here
Vector3 matColumns[3] = {{ mat.m0, mat.m4, mat.m8 },
{ mat.m1, mat.m5, mat.m9 },
{ mat.m2, mat.m6, mat.m10 }};
Vector3 matColumns[3] = {{ mat.m0, mat.m1, mat.m2 },
{ mat.m4, mat.m5, mat.m6 },
{ mat.m8, mat.m9, mat.m10 }};
// Shear Parameters XY, XZ, and YZ (extract and ignored)
float shear[3] = { 0 };
@ -2783,10 +2804,10 @@ RMAPI void MatrixDecompose(Matrix mat, Vector3 *translation, Quaternion *rotatio
*scale = Vector3Scale(scl, stabilizer);
// Extract Rotation
Matrix rotationMatrix = { matColumns[0].x, matColumns[0].y, matColumns[0].z, 0,
matColumns[1].x, matColumns[1].y, matColumns[1].z, 0,
matColumns[2].x, matColumns[2].y, matColumns[2].z, 0,
0, 0, 0, 1 };
Matrix rotationMatrix = { matColumns[0].x, matColumns[1].x, matColumns[2].x, 0,
matColumns[0].y, matColumns[1].y, matColumns[2].y, 0,
matColumns[0].z, matColumns[1].z, matColumns[2].z, 0,
0, 0, 0, 1 };
*rotation = QuaternionFromMatrix(rotationMatrix);
}

View File

@ -151,10 +151,6 @@
#include "external/rprand.h"
#endif
#if defined(__linux__) && !defined(_GNU_SOURCE)
#define _GNU_SOURCE
#endif
// Platform specific defines to handle GetApplicationDirectory()
#if defined(_WIN32)
#if !defined(MAX_PATH)
@ -2298,9 +2294,12 @@ int FileTextReplace(const char *fileName, const char *search, const char *replac
{
fileText = LoadFileText(fileName);
fileTextUpdated = TextReplaceAlloc(fileText, search, replacement);
bool saved = SaveFileText(fileName, fileTextUpdated);
if (saved) result = 0;
MemFree(fileTextUpdated);
if (fileTextUpdated != NULL)
{
bool saved = SaveFileText(fileName, fileTextUpdated);
if (saved) result = 0;
MemFree(fileTextUpdated);
}
UnloadFileText(fileText);
}
#else
@ -2319,9 +2318,12 @@ int FileTextFindIndex(const char *fileName, const char *search)
if (FileExists(fileName))
{
char *fileText = LoadFileText(fileName);
char *ptr = strstr(fileText, search);
if (ptr != NULL) result = (int)(ptr - fileText);
UnloadFileText(fileText);
if (fileText != NULL)
{
char *ptr = strstr(fileText, search);
if (ptr != NULL) result = (int)(ptr - fileText);
UnloadFileText(fileText);
}
}
return result;

View File

@ -46,7 +46,19 @@
#endif
#if (defined(__linux__) || defined(PLATFORM_WEB)) && !defined(_GNU_SOURCE)
#undef _GNU_SOURCE
#define _GNU_SOURCE // Required for: ppoll if compiled with c99 without gnu ext.
#define _GNU_SOURCE // Required for: ppoll(), mkostemp(), pipe2() if compiled with c99 without gnu ext.
#endif
#if defined(__linux__)
// NOTE: On glibc >= 2.38, _GNU_SOURCE also redirects sscanf(), strtol()... to __isoc23_*() symbols
// (C23 binary prefix parsing), making the library fail to link with older glibc versions,
// GLFW does not need it, so the redirection is disabled once <features.h> has been processed
#include <features.h>
#if defined(__GLIBC__)
#undef __GLIBC_USE_C2X_STRTOL
#define __GLIBC_USE_C2X_STRTOL 0 // glibc 2.38-2.39
#undef __GLIBC_USE_C23_STRTOL
#define __GLIBC_USE_C23_STRTOL 0 // glibc >= 2.40
#endif
#endif
#if defined(_WIN32) || defined(__CYGWIN__)

View File

@ -2486,6 +2486,9 @@ void UpdateModelAnimationEx(Model model, ModelAnimation animA, float frameA, Mod
// NOTE: Required for CPU skinning, uploads animated vertex buffers to GPU
static void UpdateModelAnimationVertexBuffers(Model model)
{
static Matrix boneNormalMatrices[256] = { 0 }; // Normal matrix per bone, bone indices are unsigned char
bool boneNormalsReady = false;
for (int m = 0; m < model.meshCount; m++)
{
Mesh mesh = model.meshes[m];
@ -2502,6 +2505,14 @@ static void UpdateModelAnimationVertexBuffers(Model model)
if ((mesh.boneWeights == NULL) || (mesh.boneIndices == NULL) ||
(mesh.animVertices == NULL) || (mesh.animNormals == NULL)) continue;
// Normal matrix only depends on the bone, compute it once per bone instead of per vertex
if ((mesh.normals != NULL) && !boneNormalsReady)
{
memset(boneNormalMatrices, 0, 256*sizeof(Matrix));
for (unsigned int b = 0; (b < model.skeleton.boneCount) && (b < 256); b++) boneNormalMatrices[b] = MatrixTranspose(MatrixInvert(model.boneMatrices[b]));
boneNormalsReady = true;
}
for (int vCounter = 0; vCounter < vertexValuesCount; vCounter += 3)
{
mesh.animVertices[vCounter] = 0;
@ -2534,7 +2545,7 @@ static void UpdateModelAnimationVertexBuffers(Model model)
if ((mesh.normals != NULL) && (mesh.animNormals != NULL ))
{
animNormal = (Vector3){ mesh.normals[vCounter], mesh.normals[vCounter + 1], mesh.normals[vCounter + 2] };
animNormal = Vector3Transform(animNormal, MatrixTranspose(MatrixInvert(model.boneMatrices[boneIndex])));
animNormal = Vector3Transform(animNormal, boneNormalMatrices[boneIndex]);
mesh.animNormals[vCounter] += animNormal.x*boneWeight;
mesh.animNormals[vCounter + 1] += animNormal.y*boneWeight;
mesh.animNormals[vCounter + 2] += animNormal.z*boneWeight;
@ -4163,31 +4174,45 @@ RayCollision GetRayCollisionSphere(Ray ray, Vector3 center, float radius)
Vector3 raySpherePos = Vector3Subtract(center, ray.position);
float vector = Vector3DotProduct(raySpherePos, ray.direction);
float distance = Vector3Length(raySpherePos);
float d = radius*radius - (distance*distance - vector*vector);
float distanceToCenter = Vector3Length(raySpherePos);
float d = radius*radius - (distanceToCenter*distanceToCenter - vector*vector);
collision.hit = d >= 0.0f;
// Check if ray origin is inside the sphere to calculate the correct collision point
if (distance < radius)
// Check if the ray's line intersects with the sphere
if (d >= 0.0f)
{
collision.distance = vector + sqrtf(d);
Vector3 point = { 0 };
Vector3 normal = { 0 };
float hitDistance = 0.0f;
// Calculate collision point
collision.point = Vector3Add(ray.position, Vector3Scale(ray.direction, collision.distance));
// Check if ray origin is inside the sphere to calculate the correct collision point
if (distanceToCenter < radius)
{
hitDistance = vector + sqrtf(d);
// Calculate collision normal (pointing outwards)
collision.normal = Vector3Negate(Vector3Normalize(Vector3Subtract(collision.point, center)));
}
else
{
collision.distance = vector - sqrtf(d);
// Calculate collision point
point = Vector3Add(ray.position, Vector3Scale(ray.direction, hitDistance));
// Calculate collision point
collision.point = Vector3Add(ray.position, Vector3Scale(ray.direction, collision.distance));
// Calculate collision normal (pointing outwards)
normal = Vector3Negate(Vector3Normalize(Vector3Subtract(point, center)));
}
else
{
hitDistance = vector - sqrtf(d);
// Calculate collision normal (pointing inwards)
collision.normal = Vector3Normalize(Vector3Subtract(collision.point, center));
// Calculate collision point
point = Vector3Add(ray.position, Vector3Scale(ray.direction, hitDistance));
// Calculate collision normal (pointing inwards)
normal = Vector3Normalize(Vector3Subtract(point, center));
}
if (hitDistance >= 0.0f)
{
collision.hit = true;
collision.point = point;
collision.normal = normal;
collision.distance = hitDistance;
}
}
return collision;

View File

@ -2185,7 +2185,7 @@ char *TextToPascal(const char *text)
static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 };
memset(buffer, 0, MAX_TEXT_BUFFER_LENGTH);
if (text != NULL)
if ((text != NULL) && (text[0] != '\0'))
{
// Upper case first character
if ((text[0] >= 'a') && (text[0] <= 'z')) buffer[0] = text[0] - 32;
@ -2269,7 +2269,7 @@ char *TextToCamel(const char *text)
static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 };
memset(buffer, 0, MAX_TEXT_BUFFER_LENGTH);
if (text != NULL)
if ((text != NULL) && (text[0] != '\0'))
{
// Lower case first character
if ((text[0] >= 'A') && (text[0] <= 'Z')) buffer[0] = text[0] + 32;
@ -2943,8 +2943,10 @@ static GlyphInfo *LoadFontDataBDF(const unsigned char *fileData, int dataSize, c
if (outGlyphPtr != NULL)
{
outGlyphPtr->value = charEncoding;
outGlyphPtr->offsetX = charBBxoff0 + fontBByoff0;
outGlyphPtr->offsetY = fontBBh - (charBBh + charBByoff0 + fontBByoff0 + fontAscent);
// BBX offsets place the glyph bitmap relative to the pen position on the baseline,
// raylib offsets are measured from the top of the line, fontAscent above the baseline
outGlyphPtr->offsetX = charBBxoff0;
outGlyphPtr->offsetY = fontAscent - (charBBh + charBByoff0);
outGlyphPtr->advanceX = charDWidthX;
outGlyphPtr->image.data = RL_CALLOC(charBBw*charBBh, 1);
@ -2987,6 +2989,7 @@ static GlyphInfo *LoadFontDataBDF(const unsigned char *fileData, int dataSize, c
if (strstr(buffer, "FONTBOUNDINGBOX") != NULL)
{
readVars = sscanf(buffer, "FONTBOUNDINGBOX %i %i %i %i", &fontBBw, &fontBBh, &fontBBxoff0, &fontBByoff0);
fontAscent = fontBBh + fontBByoff0; // Default if FONT_ASCENT property is not provided
continue;
}

View File

@ -4242,29 +4242,15 @@ void ImageDrawImageEx(Image *dst, Image src, Vector2 position, float rotation, f
Color srcColor = GetImageColor(src, srcX, srcY);
// Apply tint
srcColor.r = (unsigned char)(srcColor.r*tint.r/255.0f);
srcColor.g = (unsigned char)(srcColor.g*tint.g/255.0f);
srcColor.b = (unsigned char)(srcColor.b*tint.b/255.0f);
srcColor.a = (unsigned char)(srcColor.a*tint.a/255.0f);
// Skip fully transparent pixels
if (srcColor.a == 0) continue;
// Read destination pixel for alpha blending
// Read destination pixel and blend source over it, applying tint
// NOTE: ColorAlphaBlend() takes destination alpha into account,
// same as ImageDrawImagePro(), required to draw on transparent images
Color dstColor = GetImageColor(*dst, x, y);
unsigned int alpha = srcColor.a;
unsigned int invA = 255 - alpha;
Color out = {
(unsigned char)((srcColor.r*alpha + dstColor.r*invA)/255), // red
(unsigned char)((srcColor.g*alpha + dstColor.g*invA)/255), // green
(unsigned char)((srcColor.b*alpha + dstColor.b*invA)/255), // blue
(unsigned char)(alpha + (dstColor.a*invA)/255) // alpha
};
ImageDrawPixel(dst, x, y, out);
ImageDrawPixel(dst, x, y, ColorAlphaBlend(dstColor, srcColor, tint));
}
}
}

View File

@ -1161,7 +1161,8 @@ int main(int argc, char *argv[])
// Validate: example screenshot is not the template default one
Image imScreenshot = LoadImage(TextFormat("%s/%s/%s.png", exBasePath, exInfo->category, exInfo->name));
Image imTemplate = LoadImage(TextFormat("%s/examples_template.png", exBasePath));
if (memcmp(imScreenshot.data, imTemplate.data, GetPixelDataSize(imScreenshot.width, imScreenshot.height, imScreenshot.format)) == 0)
bool imSameSize = (imScreenshot.width == imTemplate.width) && (imScreenshot.height == imTemplate.height);
if (imSameSize && (memcmp(imScreenshot.data, imTemplate.data, GetPixelDataSize(imScreenshot.width, imScreenshot.height, imScreenshot.format)) == 0))
exInfo->status |= VALID_INVALID_PNG;
UnloadImage(imTemplate);
UnloadImage(imScreenshot);

View File

@ -763,7 +763,7 @@
{
"type": "Vector2",
"name": "offset",
"description": "Camera offset (screen space offset from window origin)"
"description": "Camera offset (screen space offset from window origin, unscaled by DPI, real pixel size)"
},
{
"type": "Vector2",

View File

@ -763,7 +763,7 @@ return {
{
type = "Vector2",
name = "offset",
description = "Camera offset (screen space offset from window origin)"
description = "Camera offset (screen space offset from window origin, unscaled by DPI, real pixel size)"
},
{
type = "Vector2",

View File

@ -486,7 +486,7 @@
(fields
((type "Vector2")
(name "offset")
(description "Camera offset (screen space offset from window origin)"))
(description "Camera offset (screen space offset from window origin, unscaled by DPI, real pixel size)"))
((type "Vector2")
(name "target")
(description "Camera target (world space target point that is mapped to screen space offset)"))

View File

@ -394,7 +394,7 @@ Struct 13: Camera3D (5 fields)
Struct 14: Camera2D (4 fields)
Name: Camera2D
Description: Camera2D, defines position/orientation in 2d space
Field[1]: Vector2 offset // Camera offset (screen space offset from window origin)
Field[1]: Vector2 offset // Camera offset (screen space offset from window origin, unscaled by DPI, real pixel size)
Field[2]: Vector2 target // Camera target (world space target point that is mapped to screen space offset)
Field[3]: float rotation // Camera rotation in degrees (pivots around target)
Field[4]: float zoom // Camera zoom (scaling around target), must not be set to 0, set to 1.0f for no scale

View File

@ -154,7 +154,7 @@
<Field type="int" name="projection" desc="Camera projection: CAMERA_PERSPECTIVE or CAMERA_ORTHOGRAPHIC" />
</Struct>
<Struct name="Camera2D" fieldCount="4" desc="Camera2D, defines position/orientation in 2d space">
<Field type="Vector2" name="offset" desc="Camera offset (screen space offset from window origin)" />
<Field type="Vector2" name="offset" desc="Camera offset (screen space offset from window origin, unscaled by DPI, real pixel size)" />
<Field type="Vector2" name="target" desc="Camera target (world space target point that is mapped to screen space offset)" />
<Field type="float" name="rotation" desc="Camera rotation in degrees (pivots around target)" />
<Field type="float" name="zoom" desc="Camera zoom (scaling around target), must not be set to 0, set to 1.0f for no scale" />