mirror of
https://github.com/raysan5/raylib.git
synced 2026-10-11 12:11:55 -04:00
Compare commits
35 Commits
d8ddc8a0a6
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 453cc35f27 | |||
| bc77441de0 | |||
| 89650b5b9e | |||
| 0b5fab1123 | |||
| e453e80bed | |||
| c5958f5832 | |||
| fd0f4c185b | |||
| 84e4a2a257 | |||
| 9056ea3299 | |||
| 806d68a9bc | |||
| 3825bbf2d4 | |||
| c8b19b297b | |||
| a043255aa7 | |||
| 8c23ce44a0 | |||
| 48aaf6e2df | |||
| 0ed3570469 | |||
| 2dee47282a | |||
| 607a659363 | |||
| 040a4bafa9 | |||
| 8571d445e9 | |||
| 4722c9fa58 | |||
| 91d50e2938 | |||
| 92f1f3d5d5 | |||
| f0736f6fb5 | |||
| e18de07224 | |||
| 3c1ab87ae1 | |||
| e8b23bfbe4 | |||
| e5c0709fa2 | |||
| 30fa673ef2 | |||
| 5c9e83e642 | |||
| 791dd6d627 | |||
| a9e3027ebc | |||
| 0e8385c890 | |||
| 98ff2c33fe | |||
| a2ee68ea46 |
88
CHANGELOG
88
CHANGELOG
@ -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)
|
||||
|
||||
@ -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
398
build.zig
@ -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(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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,
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
@ -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] };
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -106,4 +106,3 @@ int main(void)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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 };
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
//--------------------------------------------------------------------------------------
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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)
|
||||
|
||||
12
src/Makefile
12
src/Makefile
@ -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)
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
{
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
22
src/rcore.c
22
src/rcore.c
@ -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;
|
||||
|
||||
14
src/rglfw.c
14
src/rglfw.c
@ -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__)
|
||||
|
||||
@ -4174,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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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)"))
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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" />
|
||||
|
||||
Reference in New Issue
Block a user