36 Commits

Author SHA1 Message Date
Ray
0fb5c98293 Update models_mesh_uv_painting.c 2026-08-25 19:44:14 +02:00
Ray
e4ccc91d4e REVIEWED: example: textures_portal_window 2026-08-25 19:38:34 +02:00
f3a471fd89 rlparser: update raylib_api.* by CI 2026-08-25 17:26:56 +00:00
820b971eff [rshapes] Add some missing Draw*LinesEx() functions and new example shapes_outlines_testbed (#6072)
* [rshapes] Add `DrawEllipseLinesEx()`

Add DrawEllipseLinesEx() to the API for consistency with other shape drawing functions.

* [rshapes] Add `DrawTriangleLinesEx()`

Add DrawTriangleLinesEx() to the API for consistency with other shape drawing functions.

* [rshapes] Add `DrawCircleSectorLinesEx()`

Add DrawCircleSectorLinesEx() to the API for consistency with other shape drawing functions.

* [rshapes] Add `DrawRingLinesEx()`

Add DrawRingLinesEx() to the API for consistency with other shape drawing functions.

* [examples] Add shapes_outlines_testbed

This example stress tests the previously existing and newly added shape outline functions.
2026-08-25 19:26:42 +02:00
5f58fbe6d7 Added raylib_mojo binding for Mojo language (#6093) 2026-08-25 19:17:03 +02:00
5c5b357b4a [rcore][win32] Implement GetClipboardImage() (#6091) 2026-08-25 19:16:28 +02:00
bcc834fc3e [examples] Add models_mesh_uv_painting (#6084)
* [examples] Add models_mesh_uv_painting

Example showing paintable models textures

* Fixed window size and use the raygui header for the buttons
2026-08-25 19:15:43 +02:00
5b818bb318 [examples] New example models procedural decals (#6083)
* [examples] Add models_procedural_decals

A decal example that works on the web

* Using raygui button
2026-08-25 19:14:58 +02:00
e1efefb7f4 [cmake] Propagate Cocoa backend frameworks to consumers of static raylib on macOS (#6086)
* [cmake] Fix standalone configuration of examples project

Configuring examples/ as a standalone project (cd examples && cmake .)
failed with 'Unknown CMake command add_if_flag_compiles' and a missing
cmake_minimum_required() error on CMake 4.x.

- Add cmake_minimum_required(VERSION 3.22), matching the root project
- Include raylib's AddIfFlagCompiles helper via ../cmake
- Default PLATFORM to Desktop when not inherited from the raylib build

* [cmake] Propagate Cocoa backend frameworks to consumers of static raylib on macOS

Bundled GLFW links '-framework Cocoa/IOKit/QuartzCore' privately, and the
static-install export block removes glfw from the exported dependencies.
As a result the installed CMake package only carried OpenGL.framework,
and linking any consumer of libraylib.a failed with undefined Objective-C
symbols (_objc_retain etc.).

Append the three frameworks to the static install interface when the
bundled GLFW is used, so installed consumers get a complete link line.

* Gate macOS Cocoa framework propagation to the Desktop platform
2026-08-24 16:19:56 +02:00
2c0021ad89 [cmake] Fix standalone configuration of examples project (#6079)
Configuring examples/ as a standalone project (cd examples && cmake .)
failed with 'Unknown CMake command add_if_flag_compiles' and a missing
cmake_minimum_required() error on CMake 4.x.

- Add cmake_minimum_required(VERSION 3.22), matching the root project
- Include raylib's AddIfFlagCompiles helper via ../cmake
- Default PLATFORM to Desktop when not inherited from the raylib build
2026-08-24 13:34:47 +02:00
9bde204f1d [examples] Add textures_portal_window (#6082)
Add a example showing a portal like effect
2026-08-24 13:33:43 +02:00
Ray
47231e37ef REVIEWED: shaders_lights_bloom example 2026-08-23 21:05:34 +02:00
0088b85952 [examples] Add lights_bloom_post (#6081)
Add a bloom light effect example
2026-08-23 20:58:02 +02:00
338458a92b Fix crash when adding a new textures or audio examples (#6089)
rexm could crash and corrupt examples_list.txt when adding a new example in the textures category. This happened because the tool searched for the category name as plain text, and "text" happens to be part of "textures" (and shows up inside names like core_render_texture), so it sometimes found the wrong spot and inserted the new example in the middle of an unrelated line.

This fix makes the search look for the exact category boundary instead of just any matching text, so it always finds the right place. It also fixes a small out-of-bounds issue that could happen specifically with the audio category, since it's the last one in the list.

Tested by creating a new textures example and a new audio example, both now work correctly and the examples list stays intact.
2026-08-23 20:50:26 +02:00
3836f13878 Also include the playsoundapi.h file that is where playsound is now in lean and mean, this ensures that it gets picked up by the replacement macro in more modern SDKs. (#6088) 2026-08-23 15:29:28 +02:00
abe23bf82d [build] Updated examples/Makefile after testing on OSX (#6077)
* Updated examples/Makefile after testing on OSX

I may be wrong, but the best way to install ray lib is through home-brew, and I'd expect that that should be a search path for others.

* Update Makefile

Fixed issues
2026-08-21 21:14:57 +02:00
Ray
6cceb2b873 Update README.md 2026-08-21 21:14:21 +02:00
Ray
86430c156c Update README.md 2026-08-21 21:13:25 +02:00
Ray
3b57140640 Update README.md 2026-08-21 21:11:29 +02:00
Ray
f472c67bb2 Update README.md 2026-08-21 21:08:36 +02:00
Ray
23033d1f18 Update README.md 2026-08-21 21:07:03 +02:00
d7225acd8e Add raymojo binding for Mojo language (#6076) 2026-08-21 20:53:09 +02:00
af3045377a add raystar binding to BINDINGS.md (#6075) 2026-08-20 16:25:23 +02:00
Ray
f9e2215c49 Update README.md 2026-08-19 21:17:17 +02:00
Ray
d75a6589f0 REVIEWED: GetWindowPosition() #5932 2026-08-19 20:25:01 +02:00
47c8e897ce [rcore][GLFW] Fix borderless-windowed mode being always on top (#5868)
* Fix borderless fullscreen always on top

Don't pass a monitor to glfwSetWindowMonitor(). This avoids setting the flag which keeps the window always on top.

* Update for borderless-windowed always on top bug

Only apply the fix for Windows OS, since that seems to be the only
platform with the issue.
2026-08-19 20:02:32 +02:00
1129d42b48 Mostly rewrote GetPrevDirectoryPath() to correct its behavior in a few cases (#6073) 2026-08-19 19:53:56 +02:00
c58072d595 Update emscripten mouse handling (#6070) 2026-08-18 14:33:31 +02:00
b98c4e031d [rcore_web_emscripten] Handle emscripten keyboard events (#6067)
* Handle emscripten keyboard events

* Prevent stuck keys by clearing on blur event
2026-08-17 18:51:06 +02:00
Ray
9506b58633 Update BINDINGS.md 2026-08-17 18:46:33 +02:00
44aa771cc0 add raymod binding for modula 2 and 3 (#6068) 2026-08-17 18:45:49 +02:00
dad422a430 Fix time calculation in GetTime function (#6061) 2026-08-14 00:46:53 +02:00
ae83a50fb3 Update BINDINGS.md (#6060) 2026-08-13 21:20:52 +02:00
4f4b3aef45 Use unmodified GLFW (#6058)
* Use unmodified GLFW

* egl_context.c and osmesa_context.c are common to all platforms

* Define _glfwConnectNull() function
2026-08-13 11:01:10 +02:00
Ray
6862f7b9ca Update BINDINGS.md 2026-08-13 10:56:03 +02:00
ad6b19a37c Add mach-raylib (#6059) 2026-08-13 10:50:22 +02:00
46 changed files with 5010 additions and 581 deletions

View File

@ -8,7 +8,7 @@ Some people ported raylib to other languages in the form of bindings or wrappers
| :--------------------------------------------------------------------------------------- | :--------------: | :------------------------------------------------------------------: | :------------------: |
| [raylib](https://github.com/raysan5/raylib) | **6.0** | [C/C++](https://en.wikipedia.org/wiki/C_(programming_language)) | Zlib |
| [raylib-ada](https://github.com/Fabien-Chouteau/raylib-ada) | **5.5** | [Ada](https://en.wikipedia.org/wiki/Ada_(programming_language)) | MIT |
|[raylib-asm](https://github.com/gAndy50/ASMRay) | **6.0** | Assembly (x86) | Zlib |
| [raylib-asm](https://github.com/gAndy50/ASMRay) | **6.0** | [Assembly (x86)](https://en.wikipedia.org/wiki/X86_assembly_language) | Zlib |
| [raylib-beef](https://github.com/Starpelly/raylib-beef) | **5.5** | [Beef](https://www.beeflang.org) | MIT |
| [raybit](https://github.com/Alex-Velez/raybit) | **5.0** | [Brainfuck](https://en.wikipedia.org/wiki/Brainfuck) | MIT |
| [raylib-c3](https://github.com/c3lang/vendor/tree/main/libraries/raylib6.c3l) | **6** | [C3](https://c3-lang.org) | MIT |
@ -36,10 +36,11 @@ Some people ported raylib to other languages in the form of bindings or wrappers
| [raylib-elfscript](https://github.com/ohmtal/raylib-elfscript) | 6.0 | [ElfScript](https://github.com/ohmtal/ElfScript) | MIT |
| [raylib-elle](https://github.com/acquitelol/elle/blob/rewrite/std/raylib.le) | **5.5** | [Elle](https://github.com/acquitelol/elle) | GPL-3.0 |
| [raylib-factor](https://github.com/factor/factor/blob/master/extra/raylib/raylib.factor) | 5.5 | [Factor](https://factorcode.org) | BSD |
| [raystar](https://github.com/RobertFlexx/raystar) | **6.0** | [F*](https://www.fstar-lang.org/) | EPL-2.0 |
| [raylib4fb](https://github.com/mudhairless/raylib4fb) | **6.0** | [FreeBASIC](https://www.freebasic.net) | Zlib |
| [raylib-freebasic](https://github.com/WIITD/raylib-freebasic) | **5.0** | [FreeBASIC](https://www.freebasic.net) | MIT |
| [raylib.f](https://github.com/cthulhuology/raylib.f) | **5.5** | [Forth](https://forth.com) | Zlib |
| [fortran-raylib](https://github.com/interkosmos/fortran-raylib) | **5.5** | [Fortran](https://fortran-lang.org) | ISC |
| [fortran-raylib](https://github.com/interkosmos/fortran-raylib) | **6.0** | [Fortran](https://fortran-lang.org) | ISC |
| [raylib-go](https://github.com/gen2brain/raylib-go) | **6.0** | [Go](https://golang.org) | Zlib |
| [raylib-guile](https://github.com/petelliott/raylib-guile) | **auto** | [Guile](https://www.gnu.org/software/guile) | Zlib |
| [gforth-raylib](https://github.com/ArnautDaniel/gforth-raylib) | 3.5 | [Gforth](https://gforth.org) | **???** |
@ -59,6 +60,9 @@ Some people ported raylib to other languages in the form of bindings or wrappers
| [raylib-luajit](https://github.com/homma/raylib-luajit) | 5.5 | [Lua](http://www.lua.org) | MIT |
| [raylib-luajit-generated](https://github.com/james2doyle/raylib-luajit-generated) | 5.5 | [Lua](http://www.lua.org) | MIT |
| [raylib-matte](https://github.com/jcorks/raylib-matte) | 4.6-dev | [Matte](https://github.com/jcorks/matte) | **???** |
| [raylib_mojo](https://github.com/willGuimont/raylib_mojo) | **6.0** | [Mojo](https://www.modular.com/mojo) | ZLib |
| [raymojo](https://github.com/RobertFlexx/raymojo) | **6.0** | [Mojo](https://www.modular.com/mojo) | GPLv3 |
| [raymod](https://github.com/RobertFlexx/raymod) | **6.0** | [Modula-2](https://en.wikipedia.org/wiki/Modula-2) / [Modula-3](https://en.wikipedia.org/wiki/Modula-3) | Apache-2.0 |
| [Raylib.nelua](https://github.com/AuzFox/Raylib.nelua) | **5.5** | [nelua](https://nelua.io) | Zlib |
| [raylib-bindings](https://github.com/vaiorabbit/raylib-bindings) | 5.6-dev | [Ruby](https://www.ruby-lang.org/en) | Zlib |
| [naylib](https://github.com/planetis-m/naylib) | **5.6-dev** | [Nim](https://nim-lang.org) | MIT |
@ -67,9 +71,9 @@ Some people ported raylib to other languages in the form of bindings or wrappers
| [raylib_odin_bindings](https://github.com/Deathbat2190/raylib_odin_bindings) | 4.0-dev | [Odin](https://odin-lang.org) | MIT |
| [raylib-ocaml](https://github.com/tjammer/raylib-ocaml) | **6.0** | [OCaml](https://ocaml.org) | MIT |
| [TurboRaylib](https://github.com/turborium/TurboRaylib) | 4.5 | [Object Pascal](https://en.wikipedia.org/wiki/Object_Pascal) | MIT |
| [Rayberon](https://github.com/RobertFlexx/Rayberon) | **6.0** | [Oberon-2](https://en.wikipedia.org/wiki/Oberon-2) / [Component Pascal](https://en.wikipedia.org/wiki/Component_Pascal) | MIT |
|[EuRaylib](https://github.com/gAndy50/EuRaylib6) | **6.0** |[openEuphoria(https://openeuphoria.org/) | Zlib |
| [Ray4Laz](https://github.com/GuvaCode/Ray4Laz) | **6.0** | [Free Pascal](https://en.wikipedia.org/wiki/Free_Pascal)/[Delphi](https://en.wikipedia.org/wiki/Delphi_(software)) | Zlib |
| [Rayberon](https://github.com/RobertFlexx/Rayberon) | **6.0** | [Oberon-2](https://en.wikipedia.org/wiki/Oberon-2) | MIT |
| [EuRaylib](https://github.com/gAndy50/EuRaylib6) | **6.0** | [openEuphoria](https://openeuphoria.org/) | Zlib |
| [Ray4Laz](https://github.com/GuvaCode/Ray4Laz) | **6.0** | [Free Pascal](https://en.wikipedia.org/wiki/Free_Pascal) | Zlib |
| [Raylib.4.0.Pascal](https://github.com/sysrpl/Raylib.4.0.Pascal) | 4.0 | [Free Pascal](https://en.wikipedia.org/wiki/Free_Pascal) | Zlib |
| [pyraylib](https://github.com/Ho011/pyraylib) | 3.7 | [Python](https://www.python.org) | Zlib |
| [raylib-python-cffi](https://github.com/electronstudio/raylib-python-cffi) | **5.5** | [Python](https://www.python.org) | EPL-2.0 |
@ -113,6 +117,7 @@ Some people ported raylib to other languages in the form of bindings or wrappers
| [Rayua](https://github.com/uiua-lang/rayua) | **5.5** | [Uiua](https://www.uiua.org/) | **???** |
| [Target](https://github.com/FinnDemonCat/Target/tree/main/libs/raylib) | **5.5** | [Dart](https://dart.dev/) | Apache-2.0 license |
| [gclang-raylib](https://github.com/gnuchanos/gcLang_Compiler/tree/main/windows_version/raylib_version)| **6.0** | [gclang](https://github.com/gnuchanos/gcLang_Compiler) | AGPL-3.0 |
| [mach-raylib](https://github.com/angluca/mach-raylib) | **6.0-dev** | [Mach](https://machlang.org/) | MIT |
### Utility Wrappers

View File

@ -54,6 +54,17 @@ features
- Bindings to [+70 programming languages](https://github.com/raysan5/raylib/blob/master/BINDINGS.md)!
- **Free and open source**
limitations
-----------
raylib presents some limitation by design for code simplicity, some forks, alternatives and samples are available to overcome most of them but it's up to the users to modify the library for their specific needs.
- Single window with single OpenGL context by default, no multi-window support
- Window resize and move stops the rendering loop on platforms supporting a window
- `RenderTextures` are flipped vertically, as provided by OpenGL, it's up to user to draw then flipped to screen
- Font rasterization has lower quality than alternatives using `Freetype2`, `HarfBuzz` or `Slug`
- Text drawing does not support RTL, ligatures or emojis
basic example
--------------
This is a basic raylib example, it creates a window and draws the text `"Congrats! You created your first window!"` in the middle of the screen. Check this example [running live on web here](https://www.raylib.com/examples/core/loader.html?name=core_basic_window).

View File

@ -1,6 +1,18 @@
# Setup the project and settings
cmake_minimum_required(VERSION 3.22)
project(examples)
# Include raylib's cmake helper functions, so the examples can also be
# configured standalone (`cd examples && cmake .`) and not only as part
# of the raylib project.
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/../cmake")
include(AddIfFlagCompiles)
# Default to the Desktop platform when configured standalone.
if (NOT DEFINED PLATFORM)
set(PLATFORM "Desktop" CACHE STRING "Platform to build examples for")
endif ()
# Directories that contain examples
set(example_dirs
audio

View File

@ -18,7 +18,7 @@
# > PLATFORM_DESKTOP_RGFW (RGFW backend):
# - Windows (Win32, Win64)
# - Linux (X11 desktop mode)
# - macOS/OSX (x64, arm64 (not tested))
# - macOS/OSX (x64, arm64)
# - Others (not tested)
# > PLATFORM_DESKTOP_WIN32 (native Win32):
# - Windows (Win32, Win64)
@ -314,6 +314,9 @@ ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_GLFW)
ifeq ($(PLATFORM_OS),BSD)
LDFLAGS += -Lsrc -L$(RAYLIB_LIB_PATH)
endif
ifeq ($(PLATFORM_OS),OSX)
LDFLAGS += -L/opt/homebrew/lib -L$(RAYLIB_LIB_PATH)
endif
endif
ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_SDL)
ifeq ($(PLATFORM_OS),WINDOWS)

View File

@ -0,0 +1,323 @@
/*******************************************************************************************
*
* raylib [models] example - mesh uv painting
*
* Example demonstrates painting directly onto a mesh's texture: a ray is cast from the
* mouse into the scene, the hit triangle is found, and the hit point is converted into
* UV space using barycentric interpolation so a brush stroke can be drawn on the texture
*
* Example complexity rating: [★★★★] 4/4
*
* Example originally created with raylib 6.0, last time updated with raylib 6.0
*
* Example contributed by PanicTitan (@PanicTitan) and reviewed by Ramon Santamaria (@raysan5)
*
* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
* BSD-like license that allows static linking with closed source software
*
* Copyright (c) 2025 PanicTitan (@PanicTitan)
*
********************************************************************************************/
#include "raylib.h"
#include "raymath.h"
#define RAYGUI_IMPLEMENTATION
#include "raygui.h"
#include <math.h> // Required for: fabsf(), floorf()
#include <stddef.h> // Required for: NULL
//--------------------------------------------------------------------------------------
// Global Definitions
//--------------------------------------------------------------------------------------
#define CANVAS_SIZE 512
#define PALETTE_COUNT 8
//--------------------------------------------------------------------------------------
// Types and Structures Definition
//--------------------------------------------------------------------------------------
typedef enum { TOOL_PAINT = 0, TOOL_PICKER } ToolMode;
typedef enum { SHAPE_SPHERE = 0, SHAPE_CUBE, SHAPE_CYLINDER, SHAPE_TORUS } ShapeType;
//------------------------------------------------------------------------------------
// Module Functions Declaration
//------------------------------------------------------------------------------------
static void ChangeShape(Model *model, BoundingBox *bbox, ShapeType *currentShape, ShapeType newShape, Texture2D canvasTexture);
static bool GetMeshHitUV(Ray ray, Model model, BoundingBox bbox, Vector2 *outUV);
//------------------------------------------------------------------------------------
// Program main entry point
//------------------------------------------------------------------------------------
int main(void)
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [models] example - mesh uv painting");
Camera3D camera = { 0 };
camera.position = (Vector3){ 0.0f, 3.5f, 6.5f };
camera.target = (Vector3){ 0.0f, 0.0f, 0.0f };
camera.up = (Vector3){ 0.0f, 1.0f, 0.0f };
camera.fovy = 45.0f;
camera.projection = CAMERA_PERSPECTIVE;
// CPU-side canvas plus the GPU texture it gets pushed to after every stroke
Image canvasImage = GenImageColor(CANVAS_SIZE, CANVAS_SIZE, RAYWHITE);
Texture2D canvasTexture = LoadTextureFromImage(canvasImage);
SetTextureFilter(canvasTexture, TEXTURE_FILTER_BILINEAR);
Model model = { 0 };
BoundingBox modelBBox = { 0 };
ShapeType currentShape = SHAPE_SPHERE;
ChangeShape(&model, &modelBBox, &currentShape, SHAPE_SPHERE, canvasTexture);
ToolMode currentTool = TOOL_PAINT;
Color activeColor = RED;
int brushRadius = 12;
Vector2 lastHitUV = { 0 };
bool hasLastHit = false;
Color palette[PALETTE_COUNT] = { RED, ORANGE, GOLD, LIME, SKYBLUE, PURPLE, DARKGRAY, WHITE };
Rectangle uiPanelRec = { 10.0f, 10.0f, 230.0f, 490.0f };
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
// Main game loop
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
Vector2 mousePos = GetMousePosition();
bool isMouseOverUI = CheckCollisionPointRec(mousePos, uiPanelRec);
if (IsMouseButtonDown(MOUSE_BUTTON_RIGHT)) UpdateCamera(&camera, CAMERA_THIRD_PERSON);
if (!isMouseOverUI && IsMouseButtonDown(MOUSE_BUTTON_LEFT))
{
Ray ray = GetScreenToWorldRay(mousePos, camera);
Vector2 hitUV = { 0 };
if (GetMeshHitUV(ray, model, modelBBox, &hitUV))
{
int px = (int)(hitUV.x * CANVAS_SIZE);
int py = (int)(hitUV.y * CANVAS_SIZE);
if (currentTool == TOOL_PAINT)
{
// Stroke from the last hit to this one, guarding against jumps across a UV seam
if (hasLastHit && (fabsf(hitUV.x - lastHitUV.x) < 0.25f) && (fabsf(hitUV.y - lastHitUV.y) < 0.25f))
{
int lastPx = (int)(lastHitUV.x * CANVAS_SIZE);
int lastPy = (int)(lastHitUV.y * CANVAS_SIZE);
float dist = Vector2Distance((Vector2){ (float)lastPx, (float)lastPy }, (Vector2){ (float)px, (float)py });
int steps = (int)(dist / 2.0f) + 1;
for (int i = 0; i <= steps; i++)
{
float t = (float)i / (float)steps;
ImageDrawCircle(&canvasImage, (int)Lerp((float)lastPx, (float)px, t),
(int)Lerp((float)lastPy, (float)py, t), brushRadius, activeColor);
}
}
else ImageDrawCircle(&canvasImage, px, py, brushRadius, activeColor);
UpdateTexture(canvasTexture, canvasImage.data);
lastHitUV = hitUV;
hasLastHit = true;
}
else
{
activeColor = GetImageColor(canvasImage, px, py);
currentTool = TOOL_PAINT;
hasLastHit = false;
}
}
else hasLastHit = false;
}
else hasLastHit = false;
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground((Color){ 30, 32, 40, 255 });
BeginMode3D(camera);
DrawModel(model, Vector3Zero(), 1.0f, WHITE);
DrawGrid(10, 1.0f);
EndMode3D();
// Side panel
DrawRectangleRec(uiPanelRec, Fade(BLACK, 0.8f));
DrawRectangleLinesEx(uiPanelRec, 2.0f, DARKGRAY);
DrawText("MESH UV PAINTER", 25, 22, 20, GOLD);
// Tool selection toggles
bool paintActive = (currentTool == TOOL_PAINT);
bool pickerActive = (currentTool == TOOL_PICKER);
if (GuiToggle((Rectangle){ 25, 55, 95, 32 }, "PAINT", &paintActive)) currentTool = TOOL_PAINT;
if (GuiToggle((Rectangle){ 125, 55, 95, 32 }, "PICKER", &pickerActive)) currentTool = TOOL_PICKER;
// Shape selection toggles
DrawText("Mesh Shape:", 25, 100, 10, LIGHTGRAY);
bool sphereActive = (currentShape == SHAPE_SPHERE);
bool cubeActive = (currentShape == SHAPE_CUBE);
bool cylinderActive = (currentShape == SHAPE_CYLINDER);
bool torusActive = (currentShape == SHAPE_TORUS);
if (GuiToggle((Rectangle){ 25, 120, 95, 28 }, "SPHERE", &sphereActive))
ChangeShape(&model, &modelBBox, &currentShape, SHAPE_SPHERE, canvasTexture);
if (GuiToggle((Rectangle){ 125, 120, 95, 28 }, "CUBE", &cubeActive))
ChangeShape(&model, &modelBBox, &currentShape, SHAPE_CUBE, canvasTexture);
if (GuiToggle((Rectangle){ 25, 153, 95, 28 }, "CYLINDER", &cylinderActive))
ChangeShape(&model, &modelBBox, &currentShape, SHAPE_CYLINDER, canvasTexture);
if (GuiToggle((Rectangle){ 125, 153, 95, 28 }, "TORUS", &torusActive))
ChangeShape(&model, &modelBBox, &currentShape, SHAPE_TORUS, canvasTexture);
// Color display
DrawText("Active Color:", 25, 195, 10, LIGHTGRAY);
DrawRectangle(125, 193, 95, 20, activeColor);
DrawRectangleLines(125, 193, 95, 20, WHITE);
// Color swatches
DrawText("Palette Swatches:", 25, 225, 10, LIGHTGRAY);
for (int i = 0; i < PALETTE_COUNT; i++)
{
Rectangle swatchRec = { 25.0f + (i%4)*48, 245.0f + (i/4)*45, 40.0f, 38.0f };
DrawRectangleRec(swatchRec, palette[i]);
DrawRectangleLinesEx(swatchRec, 1.0f, WHITE);
if (CheckCollisionPointRec(mousePos, swatchRec) && IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) activeColor = palette[i];
}
// Brush size buttons
DrawText(TextFormat("Brush Size: %dpx", brushRadius), 25, 345, 10, LIGHTGRAY);
if (GuiButton((Rectangle){ 25, 365, 95, 30 }, "SIZE -") && (brushRadius > 2)) brushRadius -= 2;
if (GuiButton((Rectangle){ 125, 365, 95, 30 }, "SIZE +") && (brushRadius < 64)) brushRadius += 2;
// Canvas action buttons
if (GuiButton((Rectangle){ 25, 410, 95, 30 }, "CLEAR"))
{
ImageClearBackground(&canvasImage, RAYWHITE);
UpdateTexture(canvasTexture, canvasImage.data);
}
if (GuiButton((Rectangle){ 125, 410, 95, 30 }, "FILL"))
{
ImageClearBackground(&canvasImage, activeColor);
UpdateTexture(canvasTexture, canvasImage.data);
}
DrawText("Left click: paint / pick color | Right drag: orbit camera", 260, screenHeight - 25, 10, RAYWHITE);
DrawFPS(screenWidth - 90, 15);
EndDrawing();
//----------------------------------------------------------------------------------
}
// De-Initialization
//--------------------------------------------------------------------------------------
UnloadModel(model);
UnloadImage(canvasImage);
UnloadTexture(canvasTexture);
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
}
//----------------------------------------------------------------------------------
// Module Functions Definition
//----------------------------------------------------------------------------------
// Unloads the current model and builds a new primitive shape in its place, sharing the
// same canvas texture, and recomputes the transformed bounding box used for ray testing
static void ChangeShape(Model *model, BoundingBox *bbox, ShapeType *currentShape, ShapeType newShape, Texture2D canvasTexture)
{
UnloadModel(*model);
Mesh mesh = { 0 };
switch (newShape)
{
case SHAPE_SPHERE: mesh = GenMeshSphere(1.5f, 32, 32); break;
case SHAPE_CUBE: mesh = GenMeshCube(2.2f, 2.2f, 2.2f); break;
case SHAPE_CYLINDER: mesh = GenMeshCylinder(1.2f, 2.5f, 24); break;
case SHAPE_TORUS: mesh = GenMeshTorus(0.6f, 1.6f, 24, 36); break;
default: mesh = GenMeshSphere(1.5f, 32, 32); break;
}
*model = LoadModelFromMesh(mesh);
// GenMeshCylinder builds upward from y = 0; shift it down to center on the origin
if (newShape == SHAPE_CYLINDER) model->transform = MatrixTranslate(0.0f, -1.25f, 0.0f);
model->materials[0].maps[MATERIAL_MAP_DIFFUSE].texture = canvasTexture;
BoundingBox box = GetMeshBoundingBox(model->meshes[0]);
box.min = Vector3Transform(box.min, model->transform);
box.max = Vector3Transform(box.max, model->transform);
*bbox = box;
*currentShape = newShape;
}
// Raycasts against the model's mesh triangles and returns the UV coordinate of the
// closest hit, interpolated from the hit triangle's vertices with barycentric weights
static bool GetMeshHitUV(Ray ray, Model model, BoundingBox bbox, Vector2 *outUV)
{
if (!GetRayCollisionBox(ray, bbox).hit) return false; // Fast reject
Mesh mesh = model.meshes[0];
float closestDistance = 1e9f;
bool found = false;
Vector2 hitUV = { 0 };
for (int tri = 0; tri < mesh.triangleCount; tri++)
{
int i0, i1, i2;
if (mesh.indices != NULL)
{
i0 = mesh.indices[3*tri + 0];
i1 = mesh.indices[3*tri + 1];
i2 = mesh.indices[3*tri + 2];
}
else { i0 = 3*tri; i1 = 3*tri + 1; i2 = 3*tri + 2; }
Vector3 a = Vector3Transform((Vector3){ mesh.vertices[3*i0], mesh.vertices[3*i0 + 1], mesh.vertices[3*i0 + 2] }, model.transform);
Vector3 b = Vector3Transform((Vector3){ mesh.vertices[3*i1], mesh.vertices[3*i1 + 1], mesh.vertices[3*i1 + 2] }, model.transform);
Vector3 c = Vector3Transform((Vector3){ mesh.vertices[3*i2], mesh.vertices[3*i2 + 1], mesh.vertices[3*i2 + 2] }, model.transform);
RayCollision hit = GetRayCollisionTriangle(ray, a, b, c);
if (hit.hit && (hit.distance < closestDistance))
{
closestDistance = hit.distance;
found = true;
Vector2 uvA = { mesh.texcoords[2*i0], mesh.texcoords[2*i0 + 1] };
Vector2 uvB = { mesh.texcoords[2*i1], mesh.texcoords[2*i1 + 1] };
Vector2 uvC = { mesh.texcoords[2*i2], mesh.texcoords[2*i2 + 1] };
Vector3 w = Vector3Barycenter(hit.point, a, b, c);
hitUV.x = w.x*uvA.x + w.y*uvB.x + w.z*uvC.x;
hitUV.y = w.x*uvA.y + w.y*uvB.y + w.z*uvC.y;
// Wrap into [0, 1] in case of minor floating point drift at UV seams
hitUV.x -= floorf(hitUV.x);
hitUV.y -= floorf(hitUV.y);
}
}
if (found && (outUV != NULL)) *outUV = hitUV;
return found;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

View File

@ -0,0 +1,504 @@
/*******************************************************************************************
*
* raylib [models] example - procedural decals
*
* Example demonstrates projecting decals onto a 3D mesh surface by transforming the
* mesh's own triangles into the decal's local space and clipping them against a box,
* using only procedurally generated geometry and textures (no external resource files)
*
* NOTE: This is the same clip-space decal projection idea used by models_decals.c
* (in turn based on three.js' DecalGeometry), applied here to a generated mesh and
* generated textures instead of a loaded model, so the example is fully self-contained
*
* Example complexity rating: [★★★★] 4/4
*
* Example originally created with raylib 6.0, last time updated with raylib 6.0
*
* Example contributed by PanicTitan (@PanicTitan) and reviewed by Ramon Santamaria (@raysan5)
*
* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
* BSD-like license that allows static linking with closed source software
*
* Copyright (c) 2025 PanicTitan (@PanicTitan)
*
********************************************************************************************/
#include "raylib.h"
#define RAYGUI_IMPLEMENTATION
#include "raygui.h"
#include "raymath.h"
#include <stddef.h> // Required for: NULL
#include <string.h> // Required for: memcpy()
#include <math.h> // Required for: fabsf(), fminf()
//--------------------------------------------------------------------------------------
// Global Definitions
//--------------------------------------------------------------------------------------
#define MAX_DECALS 256
//--------------------------------------------------------------------------------------
// Types and Structures Definition
//--------------------------------------------------------------------------------------
// Growable triangle-soup buffer used while building a clipped decal mesh
typedef struct MeshBuilder {
int vertexCount;
int vertexCapacity;
Vector3 *vertices;
Vector2 *uvs;
} MeshBuilder;
//------------------------------------------------------------------------------------
// Module Functions Declaration
//------------------------------------------------------------------------------------
static void AddTriangleToMeshBuilder(MeshBuilder *mb, Vector3 v0, Vector3 v1, Vector3 v2);
static void FreeMeshBuilder(MeshBuilder *mb);
static Mesh BuildMesh(MeshBuilder *mb);
static Vector3 ClipSegment(Vector3 v0, Vector3 v1, Vector3 p, float s);
static Mesh GenMeshDecal(Model model, Matrix projection, float decalSize, float decalOffset);
//------------------------------------------------------------------------------------
// Program main entry point
//------------------------------------------------------------------------------------
int main(void)
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
SetConfigFlags(FLAG_MSAA_4X_HINT);
InitWindow(screenWidth, screenHeight, "raylib [models] example - procedural decals");
Camera3D camera = { 0 };
camera.position = (Vector3){ 0.0f, 2.5f, 5.0f };
camera.target = (Vector3){ 0.0f, 0.0f, 0.0f };
camera.up = (Vector3){ 0.0f, 1.0f, 0.0f };
camera.fovy = 45.0f;
camera.projection = CAMERA_PERSPECTIVE;
// Target model: a procedurally generated knot with a checkerboard skin
Model model = LoadModelFromMesh(GenMeshTorus(0.8f, 1.8f, 32, 64));
Image checkerImage = GenImageChecked(512, 512, 32, 32, LIGHTGRAY, GRAY);
Texture2D modelTexture = LoadTextureFromImage(checkerImage);
UnloadImage(checkerImage);
SetTextureFilter(modelTexture, TEXTURE_FILTER_BILINEAR);
model.materials[0].maps[MATERIAL_MAP_DIFFUSE].texture = modelTexture;
BoundingBox modelBBox = GetMeshBoundingBox(model.meshes[0]);
camera.target = Vector3Lerp(modelBBox.min, modelBBox.max, 0.5f);
float modelSize = fminf(fminf(fabsf(modelBBox.max.x - modelBBox.min.x),
fabsf(modelBBox.max.y - modelBBox.min.y)), fabsf(modelBBox.max.z - modelBBox.min.z));
float decalSize = modelSize*0.35f;
float decalOffset = 0.01f;
// Translucent cube previewing where the next decal will land
Model placementCube = LoadModelFromMesh(GenMeshCube(decalSize, decalSize, decalSize));
placementCube.materials[0].maps[0].color = LIME;
// Decal texture: a procedurally drawn target/bullseye, no image file needed
Image decalImage = GenImageColor(128, 128, BLANK);
ImageDrawCircle(&decalImage, 64, 64, 60, RED);
ImageDrawCircle(&decalImage, 64, 64, 45, WHITE);
ImageDrawCircle(&decalImage, 64, 64, 30, RED);
ImageDrawCircle(&decalImage, 64, 64, 15, YELLOW);
Texture2D decalTexture = LoadTextureFromImage(decalImage);
UnloadImage(decalImage);
SetTextureFilter(decalTexture, TEXTURE_FILTER_BILINEAR);
Material decalMaterial = LoadMaterialDefault();
decalMaterial.maps[MATERIAL_MAP_DIFFUSE].texture = decalTexture;
decalMaterial.maps[MATERIAL_MAP_DIFFUSE].color = RAYWHITE;
bool showModel = true;
Model decals[MAX_DECALS] = { 0 };
int decalCount = 0;
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
// Main game loop
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
if (IsMouseButtonDown(MOUSE_BUTTON_RIGHT)) UpdateCamera(&camera, CAMERA_THIRD_PERSON);
// Cast a ray from the mouse and keep the closest point it hits on the model
Ray ray = GetScreenToWorldRay(GetMousePosition(), camera);
RayCollision collision = { 0 };
if (GetRayCollisionBox(ray, modelBBox).hit && (decalCount < MAX_DECALS))
{
for (int m = 0; m < model.meshCount; m++)
{
RayCollision meshHit = GetRayCollisionMesh(ray, model.meshes[m], model.transform);
if (meshHit.hit && (!collision.hit || (meshHit.distance < collision.distance))) collision = meshHit;
}
}
// Project a new decal at the hit point, facing along the surface normal
if (collision.hit && IsMouseButtonPressed(MOUSE_BUTTON_LEFT) && (decalCount < MAX_DECALS))
{
Vector3 lookTarget = Vector3Add(collision.point, collision.normal);
Matrix splat = MatrixLookAt(collision.point, lookTarget, (Vector3){ 0.0f, 1.0f, 0.0f });
splat = MatrixMultiply(splat, MatrixRotateZ(DEG2RAD*(float)GetRandomValue(-180, 180)));
Mesh decalMesh = GenMeshDecal(model, splat, decalSize, decalOffset);
if (decalMesh.vertexCount > 0)
{
decals[decalCount] = LoadModelFromMesh(decalMesh);
decals[decalCount].materials[0].maps[0] = decalMaterial.maps[0];
decalCount++;
}
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(RAYWHITE);
BeginMode3D(camera);
if (showModel) DrawModel(model, Vector3Zero(), 1.0f, WHITE);
for (int i = 0; i < decalCount; i++) DrawModel(decals[i], Vector3Zero(), 1.0f, WHITE);
// Preview cube at the surface point currently under the mouse
if (collision.hit)
{
Vector3 lookTarget = Vector3Add(collision.point, collision.normal);
Matrix splat = MatrixLookAt(collision.point, lookTarget, (Vector3){ 0.0f, 1.0f, 0.0f });
placementCube.transform = MatrixInvert(splat);
DrawModel(placementCube, Vector3Zero(), 1.0f, Fade(WHITE, 0.5f));
}
DrawGrid(10, 1.0f);
EndMode3D();
// Vertex/triangle counts: base model vs total once decal geometry is added
int baseVertices = 0, baseTriangles = 0;
for (int i = 0; i < model.meshCount; i++)
{
baseVertices += model.meshes[i].vertexCount;
baseTriangles += model.meshes[i].triangleCount;
}
int totalVertices = baseVertices, totalTriangles = baseTriangles;
for (int i = 0; i < decalCount; i++)
{
totalVertices += decals[i].meshes[0].vertexCount;
totalTriangles += decals[i].meshes[0].triangleCount;
}
int statX = screenWidth - 280;
DrawText("Vertices", statX + 90, 10, 10, LIME);
DrawText("Triangles", statX + 180, 10, 10, LIME);
DrawText("Base Model", statX, 25, 10, LIME);
DrawText(TextFormat("%d", baseVertices), statX + 90, 25, 10, LIME);
DrawText(TextFormat("%d", baseTriangles), statX + 180, 25, 10, LIME);
DrawText("TOTAL", statX, 40, 10, LIME);
DrawText(TextFormat("%d", totalVertices), statX + 90, 40, 10, LIME);
DrawText(TextFormat("%d", totalTriangles), statX + 180, 40, 10, LIME);
if (GuiButton((Rectangle){ 10, screenHeight - 80.0f, 100, 40 }, showModel ? "Hide Model" : "Show Model"))
showModel = !showModel;
if (GuiButton((Rectangle){ 120, screenHeight - 80.0f, 100, 40 }, "Clear Decals"))
{
for (int i = 0; i < decalCount; i++) UnloadModel(decals[i]);
decalCount = 0;
}
DrawText("Left click: place decal | Right drag: orbit camera", 10, screenHeight - 25, 10, DARKGRAY);
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
}
// De-Initialization
//--------------------------------------------------------------------------------------
for (int i = 0; i < decalCount; i++) UnloadModel(decals[i]);
UnloadModel(placementCube);
UnloadTexture(decalTexture);
UnloadTexture(modelTexture);
UnloadModel(model);
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
}
//----------------------------------------------------------------------------------
// Module Functions Definition
//----------------------------------------------------------------------------------
// Appends a triangle to a growable mesh builder buffer, growing it in fixed-size chunks
static void AddTriangleToMeshBuilder(MeshBuilder *mb, Vector3 v0, Vector3 v1, Vector3 v2)
{
if (mb->vertexCapacity <= (mb->vertexCount + 3))
{
int newVertexCapacity = (1 + (mb->vertexCapacity/256))*256;
Vector3 *newVertices = (Vector3 *)MemAlloc(newVertexCapacity*sizeof(Vector3));
if (mb->vertexCapacity > 0)
{
memcpy(newVertices, mb->vertices, mb->vertexCount*sizeof(Vector3));
MemFree(mb->vertices);
}
mb->vertices = newVertices;
mb->vertexCapacity = newVertexCapacity;
}
int index = mb->vertexCount;
mb->vertexCount += 3;
mb->vertices[index + 0] = v0;
mb->vertices[index + 1] = v1;
mb->vertices[index + 2] = v2;
}
// Frees a mesh builder's backing buffers
static void FreeMeshBuilder(MeshBuilder *mb)
{
if (mb->vertices != NULL) MemFree(mb->vertices);
if (mb->uvs != NULL) MemFree(mb->uvs);
*mb = (MeshBuilder){ 0 };
}
// Converts a mesh builder's triangle soup into a standard uploaded raylib Mesh
static Mesh BuildMesh(MeshBuilder *mb)
{
Mesh outMesh = { 0 };
outMesh.vertexCount = mb->vertexCount;
outMesh.triangleCount = mb->vertexCount/3;
outMesh.vertices = (float *)MemAlloc(outMesh.vertexCount*3*sizeof(float));
if (mb->uvs != NULL) outMesh.texcoords = (float *)MemAlloc(outMesh.vertexCount*2*sizeof(float));
for (int i = 0; i < mb->vertexCount; i++)
{
outMesh.vertices[3*i + 0] = mb->vertices[i].x;
outMesh.vertices[3*i + 1] = mb->vertices[i].y;
outMesh.vertices[3*i + 2] = mb->vertices[i].z;
if (mb->uvs != NULL)
{
outMesh.texcoords[2*i + 0] = mb->uvs[i].x;
outMesh.texcoords[2*i + 1] = mb->uvs[i].y;
}
}
UploadMesh(&outMesh, false);
return outMesh;
}
// Clips the segment [v0, v1] against the plane p.x = s, returning the intersection point
static Vector3 ClipSegment(Vector3 v0, Vector3 v1, Vector3 p, float s)
{
float d0 = Vector3DotProduct(v0, p) - s;
float d1 = Vector3DotProduct(v1, p) - s;
float t = d0/(d0 - d1);
return Vector3Lerp(v0, v1, t);
}
// Builds a decal mesh: the model's triangles are transformed into the decal's local space
// (so the decal sits at the origin, facing +Z) and clipped against a decalSize-sided box,
// following the same clip-space projection idea used by engines' decal systems (and by
// three.js' DecalGeometry, which the technique is commonly traced back to). What's left
// after clipping becomes the decal's own small mesh, UV-mapped from its local coordinates
static Mesh GenMeshDecal(Model model, Matrix projection, float decalSize, float decalOffset)
{
Matrix invProj = MatrixInvert(projection);
MeshBuilder meshBuilders[2] = { 0 };
int mbIndex = 0;
// Gather triangles that land anywhere near the decal box; this is a loose, cheap
// pre-filter meant to skip most of the model, the precise clip happens below
for (int meshIndex = 0; meshIndex < model.meshCount; meshIndex++)
{
Mesh mesh = model.meshes[meshIndex];
for (int tri = 0; tri < mesh.triangleCount; tri++)
{
Vector3 vertices[3] = { 0 };
if (mesh.indices == NULL)
{
for (int v = 0; v < 3; v++)
{
vertices[v] = (Vector3){
mesh.vertices[3*3*tri + 3*v + 0],
mesh.vertices[3*3*tri + 3*v + 1],
mesh.vertices[3*3*tri + 3*v + 2]
};
}
}
else
{
for (int v = 0; v < 3; v++)
{
int idx = mesh.indices[3*tri + v];
vertices[v] = (Vector3){
mesh.vertices[3*idx + 0],
mesh.vertices[3*idx + 1],
mesh.vertices[3*idx + 2]
};
}
}
int insideCount = 0;
for (int i = 0; i < 3; i++)
{
Vector3 v = Vector3Transform(vertices[i], projection);
if ((fabsf(v.x) < decalSize) || (fabsf(v.y) <= decalSize) || (fabsf(v.z) <= decalSize)) insideCount++;
vertices[i] = v;
}
if (insideCount > 0) AddTriangleToMeshBuilder(&meshBuilders[mbIndex], vertices[0], vertices[1], vertices[2]);
}
}
// Clip the surviving triangles against each of the decal box's 6 faces in turn
Vector3 planes[6] = {
{ 1, 0, 0 }, { -1, 0, 0 },
{ 0, 1, 0 }, { 0, -1, 0 },
{ 0, 0, 1 }, { 0, 0, -1 }
};
for (int face = 0; face < 6; face++)
{
mbIndex = 1 - mbIndex;
MeshBuilder *inMesh = &meshBuilders[1 - mbIndex];
MeshBuilder *outMesh = &meshBuilders[mbIndex];
outMesh->vertexCount = 0;
float s = 0.5f*decalSize;
for (int i = 0; i < inMesh->vertexCount; i += 3)
{
Vector3 nV1, nV2, nV3, nV4;
float d1 = Vector3DotProduct(inMesh->vertices[i + 0], planes[face]) - s;
float d2 = Vector3DotProduct(inMesh->vertices[i + 1], planes[face]) - s;
float d3 = Vector3DotProduct(inMesh->vertices[i + 2], planes[face]) - s;
int v1Out = (d1 > 0);
int v2Out = (d2 > 0);
int v3Out = (d3 > 0);
int total = v1Out + v2Out + v3Out;
switch (total)
{
case 0:
// Whole triangle is inside this face, keep it as-is
AddTriangleToMeshBuilder(outMesh, inMesh->vertices[i], inMesh->vertices[i + 1], inMesh->vertices[i + 2]);
break;
case 1:
// One corner is outside; clip it off, turning the triangle into a quad
if (v1Out)
{
nV1 = inMesh->vertices[i + 1];
nV2 = inMesh->vertices[i + 2];
nV3 = ClipSegment(inMesh->vertices[i], nV1, planes[face], s);
nV4 = ClipSegment(inMesh->vertices[i], nV2, planes[face], s);
}
if (v2Out)
{
nV1 = inMesh->vertices[i];
nV2 = inMesh->vertices[i + 2];
nV3 = ClipSegment(inMesh->vertices[i + 1], nV1, planes[face], s);
nV4 = ClipSegment(inMesh->vertices[i + 1], nV2, planes[face], s);
AddTriangleToMeshBuilder(outMesh, nV3, nV2, nV1);
AddTriangleToMeshBuilder(outMesh, nV2, nV3, nV4);
break;
}
if (v3Out)
{
nV1 = inMesh->vertices[i];
nV2 = inMesh->vertices[i + 1];
nV3 = ClipSegment(inMesh->vertices[i + 2], nV1, planes[face], s);
nV4 = ClipSegment(inMesh->vertices[i + 2], nV2, planes[face], s);
}
AddTriangleToMeshBuilder(outMesh, nV1, nV2, nV3);
AddTriangleToMeshBuilder(outMesh, nV4, nV3, nV2);
break;
case 2:
// Two corners are outside; only the small corner near the surviving vertex remains
if (!v1Out)
{
nV1 = inMesh->vertices[i];
nV2 = ClipSegment(nV1, inMesh->vertices[i + 1], planes[face], s);
nV3 = ClipSegment(nV1, inMesh->vertices[i + 2], planes[face], s);
AddTriangleToMeshBuilder(outMesh, nV1, nV2, nV3);
}
if (!v2Out)
{
nV1 = inMesh->vertices[i + 1];
nV2 = ClipSegment(nV1, inMesh->vertices[i + 2], planes[face], s);
nV3 = ClipSegment(nV1, inMesh->vertices[i], planes[face], s);
AddTriangleToMeshBuilder(outMesh, nV1, nV2, nV3);
}
if (!v3Out)
{
nV1 = inMesh->vertices[i + 2];
nV2 = ClipSegment(nV1, inMesh->vertices[i], planes[face], s);
nV3 = ClipSegment(nV1, inMesh->vertices[i + 1], planes[face], s);
AddTriangleToMeshBuilder(outMesh, nV1, nV2, nV3);
}
break;
default:
// All 3 corners are outside this face, the triangle is fully clipped away
break;
}
}
}
MeshBuilder *finalMesh = &meshBuilders[mbIndex];
Mesh decalMesh = { 0 };
if (finalMesh->vertexCount > 0)
{
finalMesh->uvs = (Vector2 *)MemAlloc(sizeof(Vector2)*finalMesh->vertexCount);
for (int i = 0; i < finalMesh->vertexCount; i++)
{
// Clipped coordinates run roughly (-decalSize/2 .. decalSize/2); remap to (0..1)
finalMesh->uvs[i].x = (finalMesh->vertices[i].x/decalSize + 0.5f);
finalMesh->uvs[i].y = (finalMesh->vertices[i].y/decalSize + 0.5f);
// Nudge slightly along the normal so the decal doesn't z-fight with the surface
finalMesh->vertices[i].z -= decalOffset;
finalMesh->vertices[i] = Vector3Transform(finalMesh->vertices[i], invProj);
}
decalMesh = BuildMesh(finalMesh);
}
FreeMeshBuilder(&meshBuilders[0]);
FreeMeshBuilder(&meshBuilders[1]);
return decalMesh;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

View File

@ -0,0 +1,44 @@
#version 100
precision mediump float;
// Input values from vertex shader
varying vec3 fragPosition;
varying vec3 fragNormal;
varying vec2 fragTexCoord;
// Input uniform values
uniform sampler2D texture0;
uniform vec3 viewPos;
uniform vec3 lightPositions[8];
uniform vec3 lightColors[8];
void main()
{
vec4 texColor = texture2D(texture0, fragTexCoord);
vec3 norm = normalize(fragNormal);
vec3 viewDir = normalize(viewPos - fragPosition);
vec3 ambient = 0.05*texColor.rgb;
vec3 totalDiffuse = vec3(0.0);
vec3 totalSpecular = vec3(0.0);
for (int i = 0; i < 8; i++)
{
vec3 lightDir = normalize(lightPositions[i] - fragPosition);
float dist = length(lightPositions[i] - fragPosition);
float attenuation = 1.0/(1.0 + 0.8*dist + 0.4*dist*dist);
// Diffuse shading
float diff = max(dot(norm, lightDir), 0.0);
totalDiffuse += diff*lightColors[i]*attenuation*1.2;
// Blinn-Phong specular
vec3 halfwayDir = normalize(lightDir + viewDir);
float spec = pow(max(dot(norm, halfwayDir), 0.0), 32.0);
totalSpecular += spec*lightColors[i]*attenuation*0.8;
}
vec3 result = ambient + (totalDiffuse*texColor.rgb) + totalSpecular;
gl_FragColor = vec4(result, texColor.a);
}

View File

@ -0,0 +1,23 @@
#version 100
// Input vertex attributes
attribute vec3 vertexPosition;
attribute vec2 vertexTexCoord;
attribute vec3 vertexNormal;
// Output values to fragment shader
varying vec3 fragPosition;
varying vec3 fragNormal;
varying vec2 fragTexCoord;
// Input uniform values
uniform mat4 mvp;
uniform mat4 matModel;
void main()
{
fragPosition = vec3(matModel*vec4(vertexPosition, 1.0));
fragNormal = normalize(vec3(matModel*vec4(vertexNormal, 0.0)));
fragTexCoord = vertexTexCoord;
gl_Position = mvp*vec4(vertexPosition, 1.0);
}

View File

@ -0,0 +1,33 @@
#version 100
precision mediump float;
varying vec2 fragTexCoord;
uniform sampler2D texture0;
void main()
{
vec4 color = texture2D(texture0, fragTexCoord);
// High-pass threshold blur: only pixels brighter than 0.75 in any channel contribute
vec4 bloom = vec4(0.0);
vec2 texel = vec2(1.0/1280.0, 1.0/720.0)*2.5;
for (int x = -2; x <= 2; x++)
{
for (int y = -2; y <= 2; y++)
{
vec4 smp = texture2D(texture0, fragTexCoord + vec2(float(x), float(y))*texel);
float brightness = max(smp.r, max(smp.g, smp.b));
if (brightness > 0.75) bloom += smp;
}
}
bloom /= 25.0;
// Combine original with the bloom glow, then Reinhard tone-map back to [0, 1]
vec3 hdr = color.rgb + bloom.rgb*1.2;
vec3 ldr = hdr/(hdr + vec3(1.0));
gl_FragColor = vec4(ldr, color.a);
}

View File

@ -0,0 +1,44 @@
#version 330
// Input values from vertex shader
in vec3 fragPosition;
in vec3 fragNormal;
in vec2 fragTexCoord;
out vec4 finalColor;
// Input uniform values
uniform sampler2D texture0;
uniform vec3 viewPos;
uniform vec3 lightPositions[8];
uniform vec3 lightColors[8];
void main()
{
vec4 texColor = texture(texture0, fragTexCoord);
vec3 norm = normalize(fragNormal);
vec3 viewDir = normalize(viewPos - fragPosition);
vec3 ambient = 0.05*texColor.rgb;
vec3 totalDiffuse = vec3(0.0);
vec3 totalSpecular = vec3(0.0);
for (int i = 0; i < 8; i++)
{
vec3 lightDir = normalize(lightPositions[i] - fragPosition);
float dist = length(lightPositions[i] - fragPosition);
float attenuation = 1.0/(1.0 + 0.8*dist + 0.4*dist*dist);
// Diffuse shading
float diff = max(dot(norm, lightDir), 0.0);
totalDiffuse += diff*lightColors[i]*attenuation*1.2;
// Blinn-Phong specular
vec3 halfwayDir = normalize(lightDir + viewDir);
float spec = pow(max(dot(norm, halfwayDir), 0.0), 32.0);
totalSpecular += spec*lightColors[i]*attenuation*0.8;
}
vec3 result = ambient + (totalDiffuse*texColor.rgb) + totalSpecular;
finalColor = vec4(result, texColor.a);
}

View File

@ -0,0 +1,23 @@
#version 330
// Input vertex attributes
in vec3 vertexPosition;
in vec2 vertexTexCoord;
in vec3 vertexNormal;
// Output values to fragment shader
out vec3 fragPosition;
out vec3 fragNormal;
out vec2 fragTexCoord;
// Input uniform values
uniform mat4 mvp;
uniform mat4 matModel;
void main()
{
fragPosition = vec3(matModel*vec4(vertexPosition, 1.0));
fragNormal = normalize(vec3(matModel*vec4(vertexNormal, 0.0)));
fragTexCoord = vertexTexCoord;
gl_Position = mvp*vec4(vertexPosition, 1.0);
}

View File

@ -0,0 +1,32 @@
#version 330
in vec2 fragTexCoord;
out vec4 finalColor;
uniform sampler2D texture0;
void main()
{
vec4 color = texture(texture0, fragTexCoord);
// High-pass threshold blur: only pixels brighter than 0.75 in any channel contribute
vec4 bloom = vec4(0.0);
vec2 texel = vec2(1.0/1280.0, 1.0/720.0)*2.5;
for (int x = -2; x <= 2; x++)
{
for (int y = -2; y <= 2; y++)
{
vec4 smp = texture(texture0, fragTexCoord + vec2(float(x), float(y))*texel);
float brightness = max(smp.r, max(smp.g, smp.b));
if (brightness > 0.75) bloom += smp;
}
}
bloom /= 25.0;
// Combine original with the bloom glow, then Reinhard tone-map back to [0, 1]
vec3 hdr = color.rgb + bloom.rgb*1.2;
vec3 ldr = hdr/(hdr + vec3(1.0));
finalColor = vec4(ldr, color.a);
}

View File

@ -0,0 +1,185 @@
/*******************************************************************************************
*
* raylib [shaders] example - forward multi-lighting with bloom
*
* Example demonstrates forward multi-point lighting (8 point lights, attenuation and
* Blinn-Phong specular) combined with a threshold-based bloom pass and Reinhard tone
* mapping, applied as a full-screen post-process pass
*
* Example complexity rating: [★★★☆] 3/4
*
* Example uses resources/shaders/glsl100 and resources/shaders/glsl330, shared with the
* rest of the shaders examples
*
* Example originally created with raylib 6.0, last time updated with raylib 6.0
*
* Example contributed by PanicTitan (@PanicTitan) and reviewed by Ramon Santamaria (@raysan5)
*
* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
* BSD-like license that allows static linking with closed source software
*
* Copyright (c) 2025 PanicTitan (@PanicTitan)
*
********************************************************************************************/
#include "raylib.h"
#include "raymath.h"
#include "rlgl.h"
#include <math.h> // Required for: sinf(), cosf()
#if defined(PLATFORM_DESKTOP)
#define GLSL_VERSION 330
#else // PLATFORM_ANDROID, PLATFORM_WEB
#define GLSL_VERSION 100
#endif
//--------------------------------------------------------------------------------------
// Global Definitions
//--------------------------------------------------------------------------------------
#define MAX_LIGHTS 8
//------------------------------------------------------------------------------------
// Program main entry point
//------------------------------------------------------------------------------------
int main(void)
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
SetConfigFlags(FLAG_MSAA_4X_HINT | FLAG_VSYNC_HINT);
InitWindow(screenWidth, screenHeight, "raylib [shaders] example - forward multi-lighting bloom");
Camera3D camera = { 0 };
camera.position = (Vector3){ 0.0f, 5.0f, 9.0f };
camera.target = (Vector3){ 0.0f, 0.5f, 0.0f };
camera.up = (Vector3){ 0.0f, 1.0f, 0.0f };
camera.fovy = 45.0f;
camera.projection = CAMERA_PERSPECTIVE;
RenderTexture2D target = LoadRenderTexture(screenWidth, screenHeight);
// Forward multi-light shader and bloom post-process shader, both loaded from the
// resources folder shared with the rest of the shaders examples. If the GLSL version
// guessed from the PLATFORM_DESKTOP build flag turns out to be wrong for whatever
// raylib was built against, linking fails and silently falls back to the default
// shader - so this retries once with the other version, and either way the result
// is checked explicitly and reported on screen rather than staying silently wrong
Shader lightShader = LoadShader(TextFormat("resources/shaders/glsl%i/lights_bloom.vs", GLSL_VERSION),
TextFormat("resources/shaders/glsl%i/lights_bloom.fs", GLSL_VERSION));
Shader bloomShader = LoadShader(0, TextFormat("resources/shaders/glsl%i/lights_bloom_post.fs", GLSL_VERSION));
// Load models from generated cube mesh and plane
// NOTE: Meshes are automatically unloaded on UnloadModel()
Model cube = LoadModelFromMesh(GenMeshCube(2.0f, 2.0f, 2.0f));
Model floor = LoadModelFromMesh(GenMeshPlane(14.0f, 14.0f, 1, 1));
cube.materials[0].shader = lightShader;
floor.materials[0].shader = lightShader;
int lightPosLoc = GetShaderLocation(lightShader, "lightPositions");
int lightColLoc = GetShaderLocation(lightShader, "lightColors");
int viewPosLoc = GetShaderLocation(lightShader, "viewPos");
Vector3 lightPositions[MAX_LIGHTS] = { 0 };
Vector3 lightColors[MAX_LIGHTS] = {
{ 1.0f, 0.2f, 0.2f }, // Red
{ 0.2f, 1.0f, 0.3f }, // Green
{ 0.2f, 0.5f, 1.0f }, // Blue
{ 1.0f, 0.8f, 0.1f }, // Yellow
{ 1.0f, 0.1f, 0.8f }, // Magenta
{ 0.1f, 1.0f, 1.0f }, // Cyan
{ 1.0f, 0.4f, 0.1f }, // Orange
{ 0.7f, 0.2f, 1.0f }, // Purple
};
SetShaderValueV(lightShader, lightColLoc, lightColors, SHADER_UNIFORM_VEC3, MAX_LIGHTS);
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
// Main game loop
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(&camera, CAMERA_ORBITAL);
float time = (float)GetTime();
// Orbital path for each point light, its own radius/height offset by index
for (int i = 0; i < MAX_LIGHTS; i++)
{
float angle = (i/(float)MAX_LIGHTS)*2.0f*PI + time*0.6f;
float radius = 4.2f + sinf(time*1.2f + i)*0.4f;
float height = 1.0f + sinf(time*1.8f + i)*0.6f;
lightPositions[i] = (Vector3){ sinf(angle)*radius, height, cosf(angle)*radius };
}
SetShaderValueV(lightShader, lightPosLoc, lightPositions, SHADER_UNIFORM_VEC3, MAX_LIGHTS);
SetShaderValue(lightShader, viewPosLoc, &camera.position, SHADER_UNIFORM_VEC3);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
// Render the lit scene to an offscreen texture
BeginTextureMode(target);
ClearBackground((Color){ 12, 12, 18, 255 });
BeginMode3D(camera);
DrawModel(cube, (Vector3){ 0.0f, 1.0f, 0.0f }, 1.0f, GRAY);
DrawModel(floor, (Vector3){ 0.0f, 0.0f, 0.0f }, 1.0f, DARKGRAY);
DrawGrid(10, 1.0f);
// Glowing bulbs mark each light's position
for (int i = 0; i < MAX_LIGHTS; i++)
{
Color bulbColor = (Color){
(unsigned char)(lightColors[i].x*255),
(unsigned char)(lightColors[i].y*255),
(unsigned char)(lightColors[i].z*255),
255 };
DrawSphere(lightPositions[i], 0.15f, bulbColor);
}
EndMode3D();
EndTextureMode();
// Present the offscreen texture through the bloom + tone mapping shader
BeginDrawing();
ClearBackground(BLACK);
Rectangle srcRec = { 0, 0, (float)target.texture.width, (float)-target.texture.height };
BeginShaderMode(bloomShader);
// NOTE: Render texture must be y-flipped due to default OpenGL coordinates (left-bottom)
DrawTextureRec(target.texture, srcRec, (Vector2){ 0, 0 }, WHITE);
EndShaderMode();
DrawText("BALANCED MULTI-LIGHT + REINHARD TONE MAPPED BLOOM", 20, 20, 20, GREEN);
EndDrawing();
//----------------------------------------------------------------------------------
}
// De-Initialization
//--------------------------------------------------------------------------------------
UnloadModel(cube);
UnloadModel(floor);
UnloadShader(lightShader);
UnloadShader(bloomShader);
UnloadRenderTexture(target);
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 203 KiB

View File

@ -0,0 +1,407 @@
/*******************************************************************************************
*
* raylib [shapes] example - outlines testbed
*
* Example complexity rating: [★★★☆] 3/4
*
* Example originally created with raylib 6.1, last time updated with raylib 6.1
*
* Example contributed by Matthew Roush (@MatthewRoush) and reviewed by Ramon Santamaria (@raysan5)
*
* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
* BSD-like license that allows static linking with closed source software
*
* Copyright (c) 2026 Matthew Roush (@MatthewRoush)
*
********************************************************************************************/
#include "raylib.h"
#include "raymath.h"
#define RAYGUI_IMPLEMENTATION
#include "raygui.h" // Required for GUI controls
#include <stdbool.h>
#define COLOR_FILLED DARKBLUE
#define COLOR_OUTLINE YELLOW
#define SHAPE_SPACING 14.0f
#define SHAPE_SIZE 46.0f
#define BOX_SPACING 10.0f
#define MOUSE_CAMERA_ZOOM_SPEED 0.3f
#define KEYBOARD_CAMERA_MOVE_SPEED 10.0f
#define KEYBOARD_CAMERA_ZOOM_SPEED 0.1f
#define CAMERA_ZOOM_MIN 0.1f
#define CAMERA_ZOOM_MAX 1000.0f
// The shapes are ordered according to their order in this enum, left to right
enum {
ORDER_RECTANGLE = 0,
ORDER_RECTANGLE_ROUNDED,
ORDER_CIRCLE,
ORDER_ELLIPSE,
ORDER_CIRCLE_SECTOR,
ORDER_RING,
ORDER_TRIANGLE,
ORDER_POLYGON,
COUNT_SHAPES
};
// The line style groups are ordered according to their order in this enum, top to bottom
enum {
ORDER_LINES = 0,
ORDER_LINES_EX_WORLD,
ORDER_LINES_EX_SCREEN,
};
#define BOX_WIDTH (SHAPE_SIZE*COUNT_SHAPES + SHAPE_SPACING*(COUNT_SHAPES - 1) + BOX_SPACING*2.0f)
#define BOX_HEIGHT (SHAPE_SIZE + BOX_SPACING*2.0f)
//------------------------------------------------------------------------------------
// Program main entry point
//------------------------------------------------------------------------------------
int main(void)
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [shapes] example - outlines testbed");
Camera2D camera = { .zoom = 1 };
// User configurable options while running the program.
float lineOpacity = 130;
float lineThickness = 4.0f;
float rectangleRoundness = 0.4f;
float rectangleSegments = 9;
float ellipseRadiusY = 0.5f;
float circleStartAngle = 20.0f;
float circleEndAngle = 270.0f;
float circleSegments = 36;
float ringInnerRadiusScale = 0.3f;
float polygonSides = 6;
bool disableMouseControl = false;
const Rectangle optionsBackground = { 510, 0, 290, 450 };
const Vector2 zoomPoint = {((float)screenWidth - optionsBackground.width)/2.0f, (float)screenHeight/2.0f};
GuiSetStyle(LABEL, TEXT_COLOR_NORMAL, ColorToInt(WHITE));
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
// Main game loop
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
const Vector2 mousePosScreen = GetMousePosition();
const Vector2 mouseWheelVec = GetMouseWheelMoveV();
if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT) && CheckCollisionPointRec(mousePosScreen, optionsBackground)) disableMouseControl = true;
if (IsMouseButtonReleased(MOUSE_BUTTON_LEFT)) disableMouseControl = false;
if (!disableMouseControl) {
if (mouseWheelVec.y != 0.0f) {
const Vector2 prevWorldZoomPoint = GetScreenToWorld2D(zoomPoint, camera);
camera.zoom *= exp2f(MOUSE_CAMERA_ZOOM_SPEED*mouseWheelVec.y); // Constant zoom rate
camera.zoom = Clamp(camera.zoom, CAMERA_ZOOM_MIN, CAMERA_ZOOM_MAX);
const Vector2 worldZoomPoint = GetScreenToWorld2D(zoomPoint, camera);
camera.target = Vector2Add(camera.target, Vector2Subtract(prevWorldZoomPoint, worldZoomPoint));
}
if (IsMouseButtonDown(MOUSE_BUTTON_LEFT)) {
const Vector2 mouseDelta = GetMouseDelta();
camera.target.x -= mouseDelta.x/camera.zoom;
camera.target.y -= mouseDelta.y/camera.zoom;
const int mouseMaxX = (int)optionsBackground.x;
const int mouseMaxY = screenHeight;
int newX = (int)mousePosScreen.x;
int newY = (int)mousePosScreen.y;
// In C, the '%' operator computes the remainder, we want the modulus
newX = (newX%mouseMaxX + mouseMaxX)%mouseMaxX;
newY = (newY%mouseMaxY + mouseMaxY)%mouseMaxY;
if ((newX != (int)mousePosScreen.x) || (newY != (int)mousePosScreen.y)) SetMousePosition(newX, newY);
}
}
if (IsKeyDown(KEY_A)) camera.target.x -= KEYBOARD_CAMERA_MOVE_SPEED/camera.zoom;
if (IsKeyDown(KEY_D)) camera.target.x += KEYBOARD_CAMERA_MOVE_SPEED/camera.zoom;
if (IsKeyDown(KEY_W)) camera.target.y -= KEYBOARD_CAMERA_MOVE_SPEED/camera.zoom;
if (IsKeyDown(KEY_S)) camera.target.y += KEYBOARD_CAMERA_MOVE_SPEED/camera.zoom;
if (IsKeyDown(KEY_UP))
{
const Vector2 prevWorldZoomPoint = GetScreenToWorld2D(zoomPoint, camera);
camera.zoom *= exp2f(KEYBOARD_CAMERA_ZOOM_SPEED);
camera.zoom = Clamp(camera.zoom, CAMERA_ZOOM_MIN, CAMERA_ZOOM_MAX);
const Vector2 worldZoomPoint = GetScreenToWorld2D(zoomPoint, camera);
camera.target = Vector2Add(camera.target, Vector2Subtract(prevWorldZoomPoint, worldZoomPoint));
}
if (IsKeyDown(KEY_DOWN))
{
const Vector2 prevWorldZoomPoint = GetScreenToWorld2D(zoomPoint, camera);
camera.zoom *= exp2f(-KEYBOARD_CAMERA_ZOOM_SPEED);
camera.zoom = Clamp(camera.zoom, CAMERA_ZOOM_MIN, CAMERA_ZOOM_MAX);
const Vector2 worldZoomPoint = GetScreenToWorld2D(zoomPoint, camera);
camera.target = Vector2Add(camera.target, Vector2Subtract(prevWorldZoomPoint, worldZoomPoint));
}
if (IsKeyPressed(KEY_Z))
{
const Vector2 zoomPoint = {((float)screenWidth - optionsBackground.width)/2.0f, (float)screenHeight/2.0f};
const Vector2 prevWorldZoomPoint = GetScreenToWorld2D(zoomPoint, camera);
camera.zoom = 1.0f;
const Vector2 worldZoomPoint = GetScreenToWorld2D(zoomPoint, camera);
camera.target = Vector2Add(camera.target, Vector2Subtract(prevWorldZoomPoint, worldZoomPoint));
}
if (IsKeyPressed(KEY_C)) camera.target = (Vector2){ 0 };
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground((Color){ 50, 50, 55, 255 });
Color colorOutline = COLOR_OUTLINE;
colorOutline.a = (unsigned char)lineOpacity;
BeginMode2D(camera);
const float shapeOffset = BOX_SPACING*2.0f;
const float shapePaddingX = SHAPE_SIZE + SHAPE_SPACING;
const float shapePaddingY = SHAPE_SIZE + SHAPE_SPACING + BOX_SPACING*2.0f;
const float radiusX = SHAPE_SIZE/2.0f;
const float radiusY = radiusX*ellipseRadiusY;
const float ringOuterRadius = SHAPE_SIZE/2.0f;
const float ringInnerRadius = ringOuterRadius*ringInnerRadiusScale;
const Vector2 triangleVertex0Offset = { 0.0f, SHAPE_SIZE*0.8f };
const Vector2 triangleVertex1Offset = { SHAPE_SIZE*0.6f, SHAPE_SIZE };
const Vector2 triangleVertex2Offset = { SHAPE_SIZE, 0.0f };
// ----------------------------------------
// Draw*Lines()
float posY = shapeOffset + shapePaddingY*ORDER_LINES;
// Rectangle
float posX = shapeOffset + shapePaddingX*ORDER_RECTANGLE;
DrawRectangleRec((Rectangle){ posX, posY, SHAPE_SIZE, SHAPE_SIZE }, COLOR_FILLED);
DrawRectangleLines(posX, posY, SHAPE_SIZE, SHAPE_SIZE, colorOutline);
// Rectangle Rounded
posX = shapeOffset + shapePaddingX*ORDER_RECTANGLE_ROUNDED;
DrawRectangleRounded((Rectangle){ posX, posY, SHAPE_SIZE, SHAPE_SIZE }, rectangleRoundness, (int)rectangleSegments, COLOR_FILLED);
DrawRectangleRoundedLines((Rectangle){ posX, posY, SHAPE_SIZE, SHAPE_SIZE }, rectangleRoundness, (int)rectangleSegments, colorOutline);
// Circle
posX = shapeOffset + shapePaddingX*ORDER_CIRCLE;
DrawCircleV((Vector2){ posX + SHAPE_SIZE/2.0f, posY + SHAPE_SIZE/2.0f }, radiusX, COLOR_FILLED);
DrawCircleLinesV((Vector2){ posX + SHAPE_SIZE/2.0f, posY + SHAPE_SIZE/2.0f }, radiusX, colorOutline);
// Ellipse
posX = shapeOffset + shapePaddingX*ORDER_ELLIPSE;
DrawEllipseV((Vector2){ posX + SHAPE_SIZE/2.0f, posY + SHAPE_SIZE/2.0f }, radiusX, radiusY, COLOR_FILLED);
DrawEllipseLinesV((Vector2){ posX + SHAPE_SIZE/2.0f, posY + SHAPE_SIZE/2.0f }, radiusX, radiusY, colorOutline);
// Circle Sector
posX = shapeOffset + shapePaddingX*ORDER_CIRCLE_SECTOR;
DrawCircleSector((Vector2){ posX + SHAPE_SIZE/2.0f, posY + SHAPE_SIZE/2.0f }, radiusX, circleStartAngle, circleEndAngle, (int)circleSegments, COLOR_FILLED);
DrawCircleSectorLines((Vector2){ posX + SHAPE_SIZE/2.0f, posY + SHAPE_SIZE/2.0f }, radiusX, circleStartAngle, circleEndAngle, (int)circleSegments, colorOutline);
// Ring
posX = shapeOffset + shapePaddingX*ORDER_RING;
DrawRing((Vector2){ posX + SHAPE_SIZE/2.0f, posY + SHAPE_SIZE/2.0f }, ringInnerRadius, ringOuterRadius, circleStartAngle, circleEndAngle, (int)circleSegments, COLOR_FILLED);
DrawRingLines((Vector2){ posX + SHAPE_SIZE/2.0f, posY + SHAPE_SIZE/2.0f }, ringInnerRadius, ringOuterRadius, circleStartAngle, circleEndAngle, (int)circleSegments, colorOutline);
// Triangle
posX = shapeOffset + shapePaddingX*ORDER_TRIANGLE;
DrawTriangle((Vector2){ posX + triangleVertex0Offset.x, posY + triangleVertex0Offset.y },
(Vector2){ posX + triangleVertex1Offset.x, posY + triangleVertex1Offset.y },
(Vector2){ posX + triangleVertex2Offset.x, posY + triangleVertex2Offset.y },
COLOR_FILLED);
DrawTriangleLines((Vector2){ posX + triangleVertex0Offset.x, posY + triangleVertex0Offset.y },
(Vector2){ posX + triangleVertex1Offset.x, posY + triangleVertex1Offset.y },
(Vector2){ posX + triangleVertex2Offset.x, posY + triangleVertex2Offset.y },
colorOutline);
// Polygon
posX = shapeOffset + shapePaddingX*ORDER_POLYGON;
DrawPoly((Vector2){ posX + SHAPE_SIZE/2.0f, posY + SHAPE_SIZE/2.0f }, (int)polygonSides, radiusX, 0.0f, COLOR_FILLED);
DrawPolyLines((Vector2){ posX + SHAPE_SIZE/2.0f, posY + SHAPE_SIZE/2.0f }, (int)polygonSides, radiusX, 0.0f, colorOutline);
// Group the shapes
GuiGroupBox((Rectangle){ shapeOffset - BOX_SPACING, posY - BOX_SPACING, BOX_WIDTH, BOX_HEIGHT }, "Draw*Lines()");
// ----------------------------------------
// ----------------------------------------
// Draw*LinesEx() with world space pixel thickness
posY = shapeOffset + shapePaddingY*ORDER_LINES_EX_WORLD;
// Rectangle
posX = shapeOffset + shapePaddingX*ORDER_RECTANGLE;
DrawRectangleRec((Rectangle){ posX, posY, SHAPE_SIZE, SHAPE_SIZE }, COLOR_FILLED);
DrawRectangleLinesEx((Rectangle){ posX, posY, SHAPE_SIZE, SHAPE_SIZE }, lineThickness, colorOutline);
// Rectangle Rounded
posX = shapeOffset + shapePaddingX*ORDER_RECTANGLE_ROUNDED;
DrawRectangleRounded((Rectangle){ posX, posY, SHAPE_SIZE, SHAPE_SIZE }, rectangleRoundness, (int)rectangleSegments, COLOR_FILLED);
DrawRectangleRoundedLinesEx((Rectangle){ posX, posY, SHAPE_SIZE, SHAPE_SIZE }, rectangleRoundness, (int)rectangleSegments, lineThickness, colorOutline);
// Circle
posX = shapeOffset + shapePaddingX*ORDER_CIRCLE;
DrawCircleV((Vector2){ posX + SHAPE_SIZE/2.0f, posY + SHAPE_SIZE/2.0f }, radiusX, COLOR_FILLED);
DrawCircleLinesEx((Vector2){ posX + SHAPE_SIZE/2.0f, posY + SHAPE_SIZE/2.0f }, radiusX, lineThickness, colorOutline);
// Ellipse
posX = shapeOffset + shapePaddingX*ORDER_ELLIPSE;
DrawEllipseV((Vector2){ posX + SHAPE_SIZE/2.0f, posY + SHAPE_SIZE/2.0f }, radiusX, radiusY, COLOR_FILLED);
DrawEllipseLinesEx((Vector2){ posX + SHAPE_SIZE/2.0f, posY + SHAPE_SIZE/2.0f }, radiusX, radiusY, lineThickness, colorOutline);
// Circle Sector
posX = shapeOffset + shapePaddingX*ORDER_CIRCLE_SECTOR;
DrawCircleSector((Vector2){ posX + SHAPE_SIZE/2.0f, posY + SHAPE_SIZE/2.0f }, radiusX, circleStartAngle, circleEndAngle, (int)circleSegments, COLOR_FILLED);
DrawCircleSectorLinesEx((Vector2){ posX + SHAPE_SIZE/2.0f, posY + SHAPE_SIZE/2.0f }, radiusX, circleStartAngle, circleEndAngle, (int)circleSegments, lineThickness, colorOutline);
// Ring
posX = shapeOffset + shapePaddingX*ORDER_RING;
DrawRing((Vector2){ posX + SHAPE_SIZE/2.0f, posY + SHAPE_SIZE/2.0f }, ringInnerRadius, ringOuterRadius, circleStartAngle, circleEndAngle, (int)circleSegments, COLOR_FILLED);
DrawRingLinesEx((Vector2){ posX + SHAPE_SIZE/2.0f, posY + SHAPE_SIZE/2.0f }, ringInnerRadius, ringOuterRadius, circleStartAngle, circleEndAngle, (int)circleSegments, lineThickness, colorOutline);
// Triangle
posX = shapeOffset + shapePaddingX*ORDER_TRIANGLE;
DrawTriangle((Vector2){ posX + triangleVertex0Offset.x, posY + triangleVertex0Offset.y },
(Vector2){ posX + triangleVertex1Offset.x, posY + triangleVertex1Offset.y },
(Vector2){ posX + triangleVertex2Offset.x, posY + triangleVertex2Offset.y },
COLOR_FILLED);
DrawTriangleLinesEx((Vector2){ posX + triangleVertex0Offset.x, posY + triangleVertex0Offset.y },
(Vector2){ posX + triangleVertex1Offset.x, posY + triangleVertex1Offset.y },
(Vector2){ posX + triangleVertex2Offset.x, posY + triangleVertex2Offset.y },
lineThickness, colorOutline);
// Polygon
posX = shapeOffset + shapePaddingX*ORDER_POLYGON;
DrawPoly((Vector2){ posX + SHAPE_SIZE/2.0f, posY + SHAPE_SIZE/2.0f }, (int)polygonSides, radiusX, 0.0f, COLOR_FILLED);
DrawPolyLinesEx((Vector2){ posX + SHAPE_SIZE/2.0f, posY + SHAPE_SIZE/2.0f }, (int)polygonSides, radiusX, 0.0f, lineThickness, colorOutline);
// Group the shapes
GuiGroupBox((Rectangle){ shapeOffset - BOX_SPACING, posY - BOX_SPACING, BOX_WIDTH, BOX_HEIGHT }, "Draw*LinesEx() with *world space* pixel thickness");
// ----------------------------------------
// ----------------------------------------
// Draw*LinesEx() with screen space pixel thickness
posY = shapeOffset + shapePaddingY*ORDER_LINES_EX_SCREEN;
float constantThickness = lineThickness/camera.zoom;
// Rectangle
posX = shapeOffset + shapePaddingX*ORDER_RECTANGLE;
DrawRectangleRec((Rectangle){ posX, posY, SHAPE_SIZE, SHAPE_SIZE }, COLOR_FILLED);
DrawRectangleLinesEx((Rectangle){ posX, posY, SHAPE_SIZE, SHAPE_SIZE }, constantThickness, colorOutline);
// Rectangle Rounded
posX = shapeOffset + shapePaddingX*ORDER_RECTANGLE_ROUNDED;
DrawRectangleRounded((Rectangle){ posX, posY, SHAPE_SIZE, SHAPE_SIZE }, rectangleRoundness, (int)rectangleSegments, COLOR_FILLED);
DrawRectangleRoundedLinesEx((Rectangle){ posX, posY, SHAPE_SIZE, SHAPE_SIZE }, rectangleRoundness, (int)rectangleSegments, constantThickness, colorOutline);
// Circle
posX = shapeOffset + shapePaddingX*ORDER_CIRCLE;
DrawCircleV((Vector2){ posX + SHAPE_SIZE/2.0f, posY + SHAPE_SIZE/2.0f }, radiusX, COLOR_FILLED);
DrawCircleLinesEx((Vector2){ posX + SHAPE_SIZE/2.0f, posY + SHAPE_SIZE/2.0f }, radiusX, constantThickness, colorOutline);
// Ellipse
posX = shapeOffset + shapePaddingX*ORDER_ELLIPSE;
DrawEllipseV((Vector2){ posX + SHAPE_SIZE/2.0f, posY + SHAPE_SIZE/2.0f }, radiusX, radiusY, COLOR_FILLED);
DrawEllipseLinesEx((Vector2){ posX + SHAPE_SIZE/2.0f, posY + SHAPE_SIZE/2.0f }, radiusX, radiusY, constantThickness, colorOutline);
// Circle Sector
posX = shapeOffset + shapePaddingX*ORDER_CIRCLE_SECTOR;
DrawCircleSector((Vector2){ posX + SHAPE_SIZE/2.0f, posY + SHAPE_SIZE/2.0f }, radiusX, circleStartAngle, circleEndAngle, (int)circleSegments, COLOR_FILLED);
DrawCircleSectorLinesEx((Vector2){ posX + SHAPE_SIZE/2.0f, posY + SHAPE_SIZE/2.0f }, radiusX, circleStartAngle, circleEndAngle, (int)circleSegments, constantThickness, colorOutline);
// Ring
posX = shapeOffset + shapePaddingX*ORDER_RING;
DrawRing((Vector2){ posX + SHAPE_SIZE/2.0f, posY + SHAPE_SIZE/2.0f }, ringInnerRadius, ringOuterRadius, circleStartAngle, circleEndAngle, (int)circleSegments, COLOR_FILLED);
DrawRingLinesEx((Vector2){ posX + SHAPE_SIZE/2.0f, posY + SHAPE_SIZE/2.0f }, ringInnerRadius, ringOuterRadius, circleStartAngle, circleEndAngle, (int)circleSegments, constantThickness, colorOutline);
// Triangle
posX = shapeOffset + shapePaddingX*ORDER_TRIANGLE;
DrawTriangle((Vector2){ posX + triangleVertex0Offset.x, posY + triangleVertex0Offset.y },
(Vector2){ posX + triangleVertex1Offset.x, posY + triangleVertex1Offset.y },
(Vector2){ posX + triangleVertex2Offset.x, posY + triangleVertex2Offset.y },
COLOR_FILLED);
DrawTriangleLinesEx((Vector2){ posX + triangleVertex0Offset.x, posY + triangleVertex0Offset.y },
(Vector2){ posX + triangleVertex1Offset.x, posY + triangleVertex1Offset.y },
(Vector2){ posX + triangleVertex2Offset.x, posY + triangleVertex2Offset.y },
constantThickness, colorOutline);
// Polygon
posX = shapeOffset + shapePaddingX*ORDER_POLYGON;
DrawPoly((Vector2){ posX + SHAPE_SIZE/2.0f, posY + SHAPE_SIZE/2.0f }, (int)polygonSides, radiusX, 0.0f, COLOR_FILLED);
DrawPolyLinesEx((Vector2){ posX + SHAPE_SIZE/2.0f, posY + SHAPE_SIZE/2.0f }, (int)polygonSides, radiusX, 0.0f, constantThickness, colorOutline);
// Group the shapes
GuiGroupBox((Rectangle){ shapeOffset - BOX_SPACING, posY - BOX_SPACING, BOX_WIDTH, BOX_HEIGHT }, "Draw*LinesEx() with *screen space* pixel thickness");
// ----------------------------------------
EndMode2D();
DrawRectangleRec(optionsBackground, Fade(DARKGRAY, 0.75f));
DrawRectangleRec((Rectangle){ optionsBackground.x, 360, optionsBackground.width, 90 }, Fade(BLACK, 0.4f));
DrawLineEx((Vector2){ optionsBackground.x, 360 }, (Vector2){ optionsBackground.x + optionsBackground.width, 360 }, 1.0f, BLACK);
DrawText("Move with the mouse or WASD keys", (int)optionsBackground.x + 10, 370, 10, ORANGE);
DrawText("Zoom with the mouse or UP and DOWN keys", (int)optionsBackground.x + 10, 390, 10, ORANGE);
DrawText("Press C to reset position", (int)optionsBackground.x + 10, 410, 10, ORANGE);
DrawText("Press Z to reset zoom", (int)optionsBackground.x + 10, 430, 10, ORANGE);
DrawLineEx((Vector2){ optionsBackground.x, optionsBackground.y }, (Vector2){ optionsBackground.x, optionsBackground.y + optionsBackground.height }, 1.0f, BLACK);
GuiSliderBar((Rectangle){ 605, 10, 150, 25 }, "Line Opacity" , TextFormat("%d" , (int)lineOpacity) , &lineOpacity , 0 , 255);
GuiSliderBar((Rectangle){ 605, 45, 150, 25 }, "Thickness" , TextFormat("%.2f", lineThickness) , &lineThickness , -20.0f , 40.0f);
GuiSliderBar((Rectangle){ 605, 80, 150, 25 }, "Rect Roundness" , TextFormat("%.2f", rectangleRoundness) , &rectangleRoundness , -1.0f , 2.0f);
GuiSliderBar((Rectangle){ 605, 115, 150, 25 }, "Rect Segments" , TextFormat("%d" , (int)rectangleSegments), &rectangleSegments , -1 , 100);
GuiSliderBar((Rectangle){ 605, 150, 150, 25 }, "Ellipse Radius Y", TextFormat("%.2f", ellipseRadiusY) , &ellipseRadiusY , 0.0f , 1.25f);
GuiSliderBar((Rectangle){ 605, 185, 150, 25 }, "Start Angle" , TextFormat("%.2f", circleStartAngle) , &circleStartAngle , -360.0f, 360.0f);
GuiSliderBar((Rectangle){ 605, 220, 150, 25 }, "End Angle" , TextFormat("%.2f", circleEndAngle) , &circleEndAngle , -360.0f, 360.0f);
GuiSliderBar((Rectangle){ 605, 255, 150, 25 }, "Circle Segments" , TextFormat("%d" , (int)circleSegments) , &circleSegments , -1 , 100);
GuiSliderBar((Rectangle){ 605, 290, 150, 25 }, "Ring Radius" , TextFormat("%.2f", ringInnerRadiusScale) , &ringInnerRadiusScale, -1.0f , 2.0f);
GuiSliderBar((Rectangle){ 605, 325, 150, 25 }, "Poly Sides" , TextFormat("%d" , (int)polygonSides) , &polygonSides , -1 , 100);
EndDrawing();
//----------------------------------------------------------------------------------
}
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View File

@ -0,0 +1,242 @@
/*******************************************************************************************
*
* raylib [textures] example - portal window
*
* Example demonstrates rendering a second scene to a texture and projecting it onto a
* quad to create the illusion of a portal window looking into another place. The second
* scene is rendered with an off-axis ("oblique frustum") projection matched to the
* viewer's actual position relative to the window, so the illusion holds up correctly
* as the player moves and looks around, rather than only looking right from one spot
*
* Example complexity rating: [★★★★] 4/4
*
* Example originally created with raylib 6.0, last time updated with raylib 6.0
*
* Example contributed by PanicTitan (@PanicTitan) and reviewed by Ramon Santamaria (@raysan5)
*
* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
* BSD-like license that allows static linking with closed source software
*
* Copyright (c) 2025 PanicTitan (@PanicTitan)
*
********************************************************************************************/
#include "raylib.h"
#include "raymath.h"
#include "rlgl.h"
//------------------------------------------------------------------------------------
// Module Functions Declaration
//------------------------------------------------------------------------------------
// Begin portal mode 3D view
static void BeginPortalMode3D(Vector3 eye, Vector3 bottomLeft, Vector3 bottomRight, Vector3 topLeft, float nearPlane, float farPlane);
// End portal 3D mode and returns to default 2D orthographic mode
static void EndPortalMode3D(void);
//------------------------------------------------------------------------------------
// Program main entry point
//------------------------------------------------------------------------------------
int main(void)
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [textures] example - portal window");
// Camera to navigate the "real world" (Dimension A)
Camera3D camera = { 0 };
camera.position = (Vector3){ 0.0f, 2.5f, 7.0f };
camera.target = (Vector3){ 0.0f, 1.8f, 0.0f };
camera.up = (Vector3){ 0.0f, 1.0f, 0.0f };
camera.fovy = 45.0f;
camera.projection = CAMERA_PERSPECTIVE;
// The archway opening, in Dimension A world space (used both to draw the frame and
// as the window rectangle the oblique projection is built from)
Vector3 archBottomLeft = { -1.5f, 0.0f, 0.0f };
Vector3 archBottomRight = { 1.5f, 0.0f, 0.0f };
Vector3 archTopLeft = { -1.5f, 4.0f, 0.0f };
// The archway sits at portalA and looks out onto portalB, far away in world space.
// Every frame, Dimension B gets rendered to a texture using the same relative eye
// and window position, shifted by the offset between the two portals
Vector3 portalA = { 0.0f, 0.0f, 0.0f };
Vector3 portalB = { 0.0f, 0.0f, -60.0f };
RenderTexture2D portalView = LoadRenderTexture(480, 640);
DisableCursor(); // Lock cursor for first-person free camera controls
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
// Main game loop
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(&camera, CAMERA_FREE);
float time = (float)GetTime();
// Eye and window corners, shifted into Dimension B so they match the player's
// actual position and viewing angle relative to the archway
Vector3 offset = Vector3Subtract(portalB, portalA);
Vector3 eyeInB = Vector3Add(camera.position, offset);
Vector3 blInB = Vector3Add(archBottomLeft, offset);
Vector3 brInB = Vector3Add(archBottomRight, offset);
Vector3 tlInB = Vector3Add(archTopLeft, offset);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
// Render Dimension B into an offscreen texture, using an oblique projection so
// its perspective lines up with the archway exactly as the real camera sees it
BeginTextureMode(portalView);
ClearBackground((Color){ 10, 5, 20, 255 });
BeginPortalMode3D(eyeInB, blInB, brInB, tlInB, 0.05f, 100.0f);
DrawGrid(30, 0.8f);
// Floating pulsing core sphere
Vector3 corePos = Vector3Add(portalB, (Vector3){ 0.0f, 2.0f + sinf(time * 2.5f) * 0.4f, -4.0f });
DrawSphere(corePos, 1.2f, PURPLE);
DrawSphereWires(corePos, 1.25f, 16, 16, MAGENTA);
// Orbiting cubes
for (int i = 0; i < 4; i++)
{
float angle = time * 1.5f + i * (PI / 2.0f);
Vector3 pos = Vector3Add(portalB, (Vector3){
sinf(angle) * 2.5f,
2.0f + cosf(time * 3.0f + i) * 0.5f,
-4.0f + cosf(angle) * 2.5f });
DrawCube(pos, 0.5f, 0.5f, 0.5f, LIME);
DrawCubeWires(pos, 0.52f, 0.52f, 0.52f, DARKGREEN);
}
EndPortalMode3D();
EndTextureMode();
BeginDrawing();
ClearBackground(RAYWHITE);
BeginMode3D(camera);
DrawGrid(20, 1.0f);
// Side pillars
DrawCube((Vector3){ -3.5f, 2.0f, 0.0f }, 0.8f, 4.0f, 0.8f, DARKGRAY);
DrawCubeWires((Vector3){ -3.5f, 2.0f, 0.0f }, 0.8f, 4.0f, 0.8f, ORANGE);
DrawCube((Vector3){ 3.5f, 2.0f, 0.0f }, 0.8f, 4.0f, 0.8f, DARKGRAY);
DrawCubeWires((Vector3){ 3.5f, 2.0f, 0.0f }, 0.8f, 4.0f, 0.8f, ORANGE);
// Golden archway frame and solid base
DrawCubeWires((Vector3){ 0.0f, 2.0f, 0.0f }, 3.2f, 4.2f, 0.2f, GOLD);
DrawCube((Vector3){ 0.0f, 0.05f, 0.0f }, 3.4f, 0.1f, 0.6f, MAROON);
// Solid backing wall, only ever seen if looking at the archway from behind
DrawCube((Vector3){ 0.0f, 2.0f, -0.05f }, 3.1f, 4.1f, 0.05f, MAROON);
// The portal opening itself: a plain quad textured with the Dimension B
// render, filling the archway exactly, so nothing "leaks" outside its shape
rlSetTexture(portalView.texture.id);
rlBegin(RL_QUADS);
rlColor4ub(255, 255, 255, 255);
rlNormal3f(0.0f, 0.0f, 1.0f);
rlTexCoord2f(0.0f, 0.0f); rlVertex3f(archBottomLeft.x, archBottomLeft.y, archBottomLeft.z);
rlTexCoord2f(1.0f, 0.0f); rlVertex3f(archBottomRight.x, archBottomRight.y, archBottomRight.z);
rlTexCoord2f(1.0f, 1.0f); rlVertex3f(archBottomRight.x, 4.0f, archBottomRight.z);
rlTexCoord2f(0.0f, 1.0f); rlVertex3f(archTopLeft.x, archTopLeft.y, archTopLeft.z);
rlEnd();
rlSetTexture(0);
EndMode3D();
// HUD overlay
DrawRectangle(15, 15, 520, 100, Fade(BLACK, 0.75f));
DrawRectangleLines(15, 15, 520, 100, GOLD);
DrawText("PORTAL WINDOW", 28, 25, 20, GOLD);
DrawText("Look through the golden arch into Dimension B", 28, 52, 20, RAYWHITE);
DrawText("Controls: Mouse to look | WASD to move", 28, 80, 20, GRAY);
EndDrawing();
//----------------------------------------------------------------------------------
}
// De-Initialization
//--------------------------------------------------------------------------------------
UnloadRenderTexture(portalView);
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
}
//----------------------------------------------------------------------------------
// Module Functions Definition
//----------------------------------------------------------------------------------
// Starts a 3D mode using an off-axis ("oblique frustum") projection, built directly from
// an eye point and 3 corners of a rectangular window, instead of a fovy centered straight
// ahead of the camera. This is the standard technique for rendering a scene as seen through
// a window that isn't necessarily faced head-on (also used for multi-monitor and VR
// rendering) - the key difference from a normal Camera3D is that the frustum is allowed
// to be asymmetric, so perspective lines through the window line up correctly from any
// eye position, instead of behaving like a flat image pasted onto the window
static void BeginPortalMode3D(Vector3 eye, Vector3 bottomLeft, Vector3 bottomRight, Vector3 topLeft, float nearPlane, float farPlane)
{
Vector3 right = Vector3Normalize(Vector3Subtract(bottomRight, bottomLeft));
Vector3 up = Vector3Normalize(Vector3Subtract(topLeft, bottomLeft));
Vector3 normal = Vector3Normalize(Vector3CrossProduct(right, up));
// Vectors from the eye to 3 corners of the window, used to project the window onto
// the near plane and read off how far it extends left/right/bottom/top of the eye
Vector3 toBL = Vector3Subtract(bottomLeft, eye);
Vector3 toBR = Vector3Subtract(bottomRight, eye);
Vector3 toTL = Vector3Subtract(topLeft, eye);
float dist = -Vector3DotProduct(toBL, normal);
if (dist < 0.01f) dist = 0.01f; // Keep the eye from crossing the window plane
float scale = nearPlane/dist;
rlDrawRenderBatchActive();
rlMatrixMode(RL_PROJECTION);
rlPushMatrix();
rlSetMatrixProjection(MatrixFrustum(
Vector3DotProduct(right, toBL)*scale, Vector3DotProduct(right, toBR)*scale,
Vector3DotProduct(up, toBL)*scale, Vector3DotProduct(up, toTL)*scale,
nearPlane, farPlane));
// View orientation is fixed to the window's own plane (looking straight through it
// along its normal), NOT aimed at any target - that's what the asymmetric frustum
// above is for, and is what lets the eye move off to one side without distorting
rlMatrixMode(RL_MODELVIEW);
rlLoadIdentity();
rlMultMatrixf(MatrixToFloat(MatrixLookAt(eye, Vector3Subtract(eye, normal), up)));
rlEnableDepthTest();
}
// End portal 3D mode and returns to default 2D orthographic mode
// NOTE: Similar implementation to EndMode3D()
static void EndPortalMode3D(void)
{
rlDrawRenderBatchActive(); // Update and draw internal render batch
rlMatrixMode(RL_PROJECTION); // Switch to projection matrix
rlPopMatrix(); // Restore previous matrix (projection) from matrix stack
rlMatrixMode(RL_MODELVIEW); // Switch back to modelview matrix
rlLoadIdentity(); // Reset current matrix (modelview)
rlDisableDepthTest(); // Disable DEPTH_TEST for 2D
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View File

@ -114,6 +114,20 @@ if (NOT BUILD_SHARED_LIBS)
if (NOT glfw3_FOUND)
list(REMOVE_ITEM raylib_install_private_libs glfw)
# Bundled GLFW links its Cocoa backend frameworks privately, so they
# must be propagated to consumers of the static library, whose final
# link line would otherwise miss them. Only the Desktop platform uses
# bundled GLFW; other platforms (e.g. Memory) have no Cocoa/GLFW symbols.
if (APPLE AND PLATFORM STREQUAL "Desktop")
find_library(COCOA_FRAMEWORK Cocoa)
find_library(IOKIT_FRAMEWORK IOKit)
find_library(QUARTZCORE_FRAMEWORK QuartzCore)
list(APPEND raylib_install_private_libs
${COCOA_FRAMEWORK}
${IOKIT_FRAMEWORK}
${QUARTZCORE_FRAMEWORK})
endif ()
endif()
foreach(lib IN LISTS raylib_install_private_libs)

View File

@ -46,6 +46,7 @@
// include windows
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <playsoundapi.h>
// remove all our redefinitions so that raylib can define them properly
#undef CloseWindow

View File

@ -1,18 +1,198 @@
# GLFW (modified for raylib)
# GLFW
This directory contains a modification of GLFW, whose official website and
upstream repository are https://glfw.org and https://github.com/glfw/glfw,
respectively.
[![Build status](https://github.com/glfw/glfw/actions/workflows/build.yml/badge.svg)](https://github.com/glfw/glfw/actions)
[![Build status](https://ci.appveyor.com/api/projects/status/0kf0ct9831i5l6sp/branch/master?svg=true)](https://ci.appveyor.com/project/elmindreda/glfw)
In this modification, some static functions sharing the same name in different
platforms have been renamed so all of GLFW's source files can be combined into
one (as done by ``rglfw.c``). Also, the Null platform, which is not used by
raylib, has been disabled. The renamed functions are:
## Introduction
```
createKeyTables()
translateKey()
acquireMonitor()
releaseMonitor()
```
GLFW is an Open Source, multi-platform library for OpenGL, OpenGL ES and Vulkan
application development. It provides a simple, platform-independent API for
creating windows, contexts and surfaces, reading input, handling events, etc.
GLFW is written primarily in C99, with parts of macOS support being written in
Objective-C.
GLFW supports Windows, macOS and Linux, and also works on many other Unix-like
systems. On Linux both Wayland and X11 are supported.
GLFW is licensed under the [zlib/libpng
license](https://www.glfw.org/license.html).
You can [download](https://www.glfw.org/download.html) the latest stable release
as source or Windows and macOS binaries. There are [release
tags](https://github.com/glfw/glfw/releases) with source and binary archives
attached for every version since 3.0.
The [documentation](https://www.glfw.org/docs/latest/) is available online and is
also included in source and binary archives, except those generated
automatically by Github. The documentation contains guides, a tutorial and the
API reference. The [release
notes](https://www.glfw.org/docs/latest/news.html) list the new features,
caveats and deprecations in the latest release. The [version
history](https://www.glfw.org/changelog.html) lists every user-visible change
for every release.
GLFW exists because of the contributions of [many people](CONTRIBUTORS.md)
around the world, whether by reporting bugs, providing community support, adding
features, reviewing or testing code, debugging, proofreading docs, suggesting
features or fixing bugs.
## System requirements
GLFW supports Windows 7 and later and macOS 10.11 and later. On GNOME Wayland,
window decorations will be very basic unless the
[libdecor](https://gitlab.freedesktop.org/libdecor/libdecor) package is
installed. Linux and other Unix-like systems running X11 are supported even
without a desktop environment or modern extensions, although some features
require a clipboard manager or a modern window manager.
See the [compatibility guide](https://www.glfw.org/docs/latest/compat.html)
for more detailed information.
## Compiling GLFW
GLFW supports compilation with Visual C++ (2013 and later), GCC and Clang. Both
Clang-CL and MinGW-w64 are supported. Other C99 compilers will likely also
work, but this is not regularly tested.
There are [pre-compiled binaries](https://www.glfw.org/download.html)
available for Windows and macOS.
GLFW itself needs only CMake and the headers and libraries for your operating
system and window system. No other SDKs are required.
See the [compilation guide](https://www.glfw.org/docs/latest/compile.html) for
more information about compiling GLFW and the exact dependencies required for
each window system.
The examples and test programs depend on a number of tiny libraries. These are
bundled in the `deps/` directory. The repository has no submodules.
- [getopt\_port](https://github.com/kimgr/getopt_port/) for examples
with command-line options
- [TinyCThread](https://github.com/tinycthread/tinycthread) for threaded
examples
- [glad2](https://github.com/Dav1dde/glad) for loading OpenGL and Vulkan
functions
- [linmath.h](https://github.com/datenwolf/linmath.h) for linear algebra in
examples
- [Nuklear](https://github.com/Immediate-Mode-UI/Nuklear) for test and example UI
- [stb\_image\_write](https://github.com/nothings/stb) for writing images to disk
The documentation is generated with [Doxygen](https://doxygen.org/) when the
library is built, provided CMake could find a sufficiently new version of it
during configuration.
## Using GLFW
See the [HTML documentation](https://www.glfw.org/docs/latest/) for a tutorial,
guides and the API reference.
## Contributing to GLFW
See the [contribution
guide](https://github.com/glfw/glfw/blob/master/docs/CONTRIBUTING.md) for
more information.
The `master` branch is the stable integration branch and _should_ always compile
and run on all supported platforms. Details of a newly added feature,
including the public API, may change until it has been included in a release.
The `latest` branch is equivalent to the [highest numbered](https://semver.org/)
release, although it may not always point to the same commit as the tag for that
release.
The `ci` branch is used to trigger continuous integration jobs for code under
testing and should never be relied on for any purpose.
## Reporting bugs
Bugs are reported to our [issue tracker](https://github.com/glfw/glfw/issues).
Please check the [contribution
guide](https://github.com/glfw/glfw/blob/master/docs/CONTRIBUTING.md) for
information on what to include when reporting a bug.
## Changelog since 3.5
None.
## Changelog since 3.4
- Added `GLFW_UNLIMITED_MOUSE_BUTTONS` input mode that allows mouse buttons beyond
the limit of the mouse button tokens to be reported (#2423)
- Added `glfwGetEGLConfig` function to query the `EGLConfig` of a window (#2045)
- Added `glfwGetGLXFBConfig` function to query the `GLXFBConfig` of a window (#1925)
- Updated minimum CMake version to 3.16 (#2541)
- Removed support for building with original MinGW (#2540)
- [Win32] Removed support for Windows XP and Vista (#2505)
- [Win32] Bugfix: Media keys were reported with a scancode of 256 (#1768,#2417,#2625)
- [Cocoa] Added `QuartzCore` framework as link-time dependency
- [Cocoa] Removed support for OS X 10.10 Yosemite and earlier (#2506)
- [Cocoa] Bugfix: Cmd+Period, Ctrl+Tab and Ctrl+Esc key events were not emitted
(#1362,#2278)
- [Wayland] Bugfix: The fractional scaling related objects were not destroyed
- [Wayland] Bugfix: `glfwInit` would segfault on compositor with no seat (#2517)
- [Wayland] Bugfix: A drag entering a non-GLFW surface could cause a segfault
- [Wayland] Bugfix: Ignore key repeat events when no window has keyboard focus (#2727)
- [Wayland] Bugfix: Reset key repeat timer when window destroyed (#2741,#2727)
- [Wayland] Bugfix: Memory would leak if reading a data offer failed midway
- [Wayland] Bugfix: Retrieved cursor position would be incorrect when hovering over
fallback decorations
- [Wayland] Bugfix: Fallback decorations would report scroll events
- [Wayland] Bugfix: Keyboard repeat events halted when any key is released (#2568)
- [Wayland] Bugfix: Fallback decorations would show menu at wrong position
- [Wayland] Bugfix: The cursor was not updated when clicking through from
a modal to a fallback decoration
- [Wayland] Bugfix: The cursor position was not updated when clicking through
from a modal to the content area
- [Wayland] Bugfix: free modules at end of terminate function to resolve
potential segmentation fault (#2744)
- [Wayland] Bugfix: Confining or disabling the cursor could segfault on
compositors without `pointer-constraints-unstable-v1`
- [Wayland] Bugfix: Key repeat did not function on very old compositors
- [Wayland] Bugfix: The `libwayland-client` library was not unloaded at termination
- [Wayland] Bugfix: Scroll events were sent twice on some versions of GNOME (#2494)
- [Wayland] Bugfix: Two-dimensional scroll input was emitted as separate axes
- [Wayland] Bugfix: Mouse wheel scroll distance was incorrect on some compositors
- [Wayland] Bugfix: `glfwSwapBuffers` would halt with nonzero swap interval when window
was suspended (#1350,#2582,#2640,#2719,#2723,#2800,#2827)
- [Wayland] Bugfix: `glfwPostEmptyEvent` would leak a callback proxy (#2836)
- [Wayland] Bugfix: `glfwHideWindow` did not always send its request immediately
- [Wayland] Bugfix: Some event types were not always processed by `glfwPollEvents` or
`glfwWait*Events` (#2793,#2795)
- [Wayland] Bugfix: Scaled framebuffer size was sometimes incorect (#2713,#2810)
- [X11] Bugfix: Running without a WM could trigger an assert (#2593,#2601,#2631)
- [X11] Bugfix: Occasional crash when an idle display awakes (#2766)
- [X11] Bugfix: Prevent BadWindow when creating small windows with a content scale
less than 1 (#2754)
- [X11] Bugfix: Clamp width and height to >= 1 to prevent BadValue error and app exit
- [X11] Bugfix: Floating windows silently became non-floating when hidden (#2276)
- [X11] Bugfix: The `libXext` library was not unloaded at termination
- [Linux] Bugfix: The header for `ioctl` was only implicitly included (#2778)
- [Null] Added Vulkan 'window' surface creation via `VK_EXT_headless_surface`
- [Null] Added EGL context creation on Mesa via `EGL_MESA_platform_surfaceless`
- [EGL] Allowed native access on Wayland with `GLFW_CONTEXT_CREATION_API` set to
`GLFW_NATIVE_CONTEXT_API` (#2518)
## Contact
On [glfw.org](https://www.glfw.org/) you can find the latest version of GLFW, as
well as news, documentation and other information about the project.
If you have questions related to the use of GLFW, we have a
[forum](https://discourse.glfw.org/).
If you have a bug to report, a patch to submit or a feature you'd like to
request, please file it in the
[issue tracker](https://github.com/glfw/glfw/issues) on GitHub.
Finally, if you're interested in helping out with the development of GLFW or
porting it to your favorite platform, join us on the forum or GitHub.

View File

@ -1,8 +1,7 @@
//========================================================================
// GLFW 3.5 Cocoa (modified for raylib) - www.glfw.org; www.raylib.com
// GLFW 3.5 Cocoa - www.glfw.org
//------------------------------------------------------------------------
// Copyright (c) 2009-2019 Camilla Löwy <elmindreda@glfw.org>
// Copyright (c) 2024-2026 M374LX <wilsalx@gmail.com>
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
@ -176,7 +175,7 @@ static void createMenuBar(void)
// Create key code translation tables
//
static void createKeyTablesCocoa(void)
static void createKeyTables(void)
{
memset(_glfw.ns.keycodes, -1, sizeof(_glfw.ns.keycodes));
memset(_glfw.ns.scancodes, -1, sizeof(_glfw.ns.scancodes));
@ -584,7 +583,7 @@ int _glfwInitCocoa(void)
name:NSTextInputContextKeyboardSelectionDidChangeNotification
object:nil];
createKeyTablesCocoa();
createKeyTables();
_glfw.ns.eventSource = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
if (!_glfw.ns.eventSource)

View File

@ -1,8 +1,7 @@
//========================================================================
// GLFW 3.5 Cocoa (modified for raylib) - www.glfw.org; www.raylib.com
// GLFW 3.5 Cocoa - www.glfw.org
//------------------------------------------------------------------------
// Copyright (c) 2009-2019 Camilla Löwy <elmindreda@glfw.org>
// Copyright (c) 2024-2026 M374LX <wilsalx@gmail.com>
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
@ -115,7 +114,7 @@ static void updateCursorMode(_GLFWwindow* window)
// Make the specified window and its video mode active on its monitor
//
static void acquireMonitorCocoa(_GLFWwindow* window)
static void acquireMonitor(_GLFWwindow* window)
{
_glfwSetVideoModeCocoa(window->monitor, &window->videoMode);
const CGRect bounds = CGDisplayBounds(window->monitor->ns.displayID);
@ -131,7 +130,7 @@ static void acquireMonitorCocoa(_GLFWwindow* window)
// Remove the window and restore the original video mode
//
static void releaseMonitorCocoa(_GLFWwindow* window)
static void releaseMonitor(_GLFWwindow* window)
{
if (window->monitor->window != window)
return;
@ -162,7 +161,7 @@ static int translateFlags(NSUInteger flags)
// Translates a macOS keycode to a GLFW keycode
//
static int translateKeyCocoa(unsigned int key)
static int translateKey(unsigned int key)
{
if (key >= sizeof(_glfw.ns.keycodes) / sizeof(_glfw.ns.keycodes[0]))
return GLFW_KEY_UNKNOWN;
@ -281,7 +280,7 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
- (void)windowDidMiniaturize:(NSNotification *)notification
{
if (window->monitor)
releaseMonitorCocoa(window);
releaseMonitor(window);
_glfwInputWindowIconify(window, GLFW_TRUE);
}
@ -289,7 +288,7 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
- (void)windowDidDeminiaturize:(NSNotification *)notification
{
if (window->monitor)
acquireMonitorCocoa(window);
acquireMonitor(window);
_glfwInputWindowIconify(window, GLFW_FALSE);
}
@ -563,7 +562,7 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
- (void)keyDown:(NSEvent *)event
{
const int key = translateKeyCocoa([event keyCode]);
const int key = translateKey([event keyCode]);
const int mods = translateFlags([event modifierFlags]);
_glfwInputKey(window, key, [event keyCode], GLFW_PRESS, mods);
@ -576,7 +575,7 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
int action;
const unsigned int modifierFlags =
[event modifierFlags] & NSEventModifierFlagDeviceIndependentFlagsMask;
const int key = translateKeyCocoa([event keyCode]);
const int key = translateKey([event keyCode]);
const int mods = translateFlags(modifierFlags);
const NSUInteger keyFlag = translateKeyToModifierFlag(key);
@ -595,7 +594,7 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
- (void)keyUp:(NSEvent *)event
{
const int key = translateKeyCocoa([event keyCode]);
const int key = translateKey([event keyCode]);
const int mods = translateFlags([event modifierFlags]);
_glfwInputKey(window, key, [event keyCode], GLFW_RELEASE, mods);
}
@ -604,7 +603,7 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
{
// HACK: Some key combinations are consumed before reaching keyDown:
// so we claim those events and emit them here
const int key = translateKeyCocoa([event keyCode]);
const int key = translateKey([event keyCode]);
const int mods = translateFlags([event modifierFlags]);
if (mods & GLFW_MOD_CONTROL)
@ -996,7 +995,7 @@ GLFWbool _glfwCreateWindowCocoa(_GLFWwindow* window,
{
_glfwShowWindowCocoa(window);
_glfwFocusWindowCocoa(window);
acquireMonitorCocoa(window);
acquireMonitor(window);
if (wndconfig->centerCursor)
_glfwCenterCursorInContentArea(window);
@ -1026,7 +1025,7 @@ void _glfwDestroyWindowCocoa(_GLFWwindow* window)
[window->ns.object orderOut:nil];
if (window->monitor)
releaseMonitorCocoa(window);
releaseMonitor(window);
if (window->context.destroy)
window->context.destroy(window);
@ -1113,7 +1112,7 @@ void _glfwSetWindowSizeCocoa(_GLFWwindow* window, int width, int height)
if (window->monitor)
{
if (window->monitor->window == window)
acquireMonitorCocoa(window);
acquireMonitor(window);
}
else
{
@ -1282,7 +1281,7 @@ void _glfwSetWindowMonitorCocoa(_GLFWwindow* window,
if (monitor)
{
if (monitor->window == window)
acquireMonitorCocoa(window);
acquireMonitor(window);
}
else
{
@ -1300,7 +1299,7 @@ void _glfwSetWindowMonitorCocoa(_GLFWwindow* window,
}
if (window->monitor)
releaseMonitorCocoa(window);
releaseMonitor(window);
_glfwInputWindowMonitor(window, monitor);
@ -1338,7 +1337,7 @@ void _glfwSetWindowMonitorCocoa(_GLFWwindow* window,
[window->ns.object setLevel:NSMainMenuWindowLevel + 1];
[window->ns.object setHasShadow:NO];
acquireMonitorCocoa(window);
acquireMonitor(window);
}
else
{

View File

@ -1,9 +1,8 @@
//========================================================================
// GLFW 3.5 (modified for raylib) - www.glfw.org; www.raylib.com
// GLFW 3.5 - www.glfw.org
//------------------------------------------------------------------------
// Copyright (c) 2016 Google Inc.
// Copyright (c) 2016-2019 Camilla Löwy <elmindreda@glfw.org>
// Copyright (c) 2024-2026 M374LX <wilsalx@gmail.com>
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
@ -61,12 +60,12 @@ static void fitToMonitor(_GLFWwindow* window)
window->null.height = mode.height;
}
static void acquireMonitorNull(_GLFWwindow* window)
static void acquireMonitor(_GLFWwindow* window)
{
_glfwInputMonitorWindow(window->monitor, window);
}
static void releaseMonitorNull(_GLFWwindow* window)
static void releaseMonitor(_GLFWwindow* window)
{
if (window->monitor->window != window)
return;
@ -149,7 +148,7 @@ GLFWbool _glfwCreateWindowNull(_GLFWwindow* window,
{
_glfwShowWindowNull(window);
_glfwFocusWindowNull(window);
acquireMonitorNull(window);
acquireMonitor(window);
if (wndconfig->centerCursor)
_glfwCenterCursorInContentArea(window);
@ -170,7 +169,7 @@ GLFWbool _glfwCreateWindowNull(_GLFWwindow* window,
void _glfwDestroyWindowNull(_GLFWwindow* window)
{
if (window->monitor)
releaseMonitorNull(window);
releaseMonitor(window);
if (_glfw.null.focusedWindow == window)
_glfw.null.focusedWindow = NULL;
@ -205,14 +204,14 @@ void _glfwSetWindowMonitorNull(_GLFWwindow* window,
}
if (window->monitor)
releaseMonitorNull(window);
releaseMonitor(window);
_glfwInputWindowMonitor(window, monitor);
if (window->monitor)
{
window->null.visible = GLFW_TRUE;
acquireMonitorNull(window);
acquireMonitor(window);
fitToMonitor(window);
}
else
@ -342,7 +341,7 @@ void _glfwIconifyWindowNull(_GLFWwindow* window)
_glfwInputWindowIconify(window, GLFW_TRUE);
if (window->monitor)
releaseMonitorNull(window);
releaseMonitor(window);
}
}
@ -354,7 +353,7 @@ void _glfwRestoreWindowNull(_GLFWwindow* window)
_glfwInputWindowIconify(window, GLFW_FALSE);
if (window->monitor)
acquireMonitorNull(window);
acquireMonitor(window);
}
else if (window->null.maximized)
{

View File

@ -74,9 +74,6 @@ GLFWbool _glfwSelectPlatform(int desiredID, _GLFWplatform* platform)
return GLFW_FALSE;
}
//Modification for raylib: this Null platform check is removed because it is
//not used by raylib
#if 0
// Only allow the Null platform if specifically requested
if (desiredID == GLFW_PLATFORM_NULL)
return _glfwConnectNull(desiredID, platform);
@ -85,7 +82,6 @@ GLFWbool _glfwSelectPlatform(int desiredID, _GLFWplatform* platform)
_glfwInputError(GLFW_PLATFORM_UNAVAILABLE, "This binary only supports the Null platform");
return GLFW_FALSE;
}
#endif
#if defined(_GLFW_WAYLAND) && defined(_GLFW_X11)
if (desiredID == GLFW_ANY_PLATFORM)

View File

@ -1,9 +1,8 @@
//========================================================================
// GLFW 3.5 Win32 (modified for raylib) - www.glfw.org; www.raylib.com
// GLFW 3.5 Win32 - www.glfw.org
//------------------------------------------------------------------------
// Copyright (c) 2002-2006 Marcus Geelnard
// Copyright (c) 2006-2019 Camilla Löwy <elmindreda@glfw.org>
// Copyright (c) 2024-2026 M374LX <wilsalx@gmail.com>
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
@ -169,7 +168,7 @@ static GLFWbool loadLibraries(void)
// Create key code translation tables
//
static void createKeyTablesWin32(void)
static void createKeyTables(void)
{
int scancode;
@ -660,7 +659,7 @@ int _glfwInitWin32(void)
if (!loadLibraries())
return GLFW_FALSE;
createKeyTablesWin32();
createKeyTables();
_glfwUpdateKeyNamesWin32();
if (_glfwIsWindows10Version1703OrGreaterWin32())

View File

@ -1,9 +1,8 @@
//========================================================================
// GLFW 3.5 Win32 (modified for raylib) - www.glfw.org; www.raylib.com
// GLFW 3.5 Win32 - www.glfw.org
//------------------------------------------------------------------------
// Copyright (c) 2002-2006 Marcus Geelnard
// Copyright (c) 2006-2019 Camilla Löwy <elmindreda@glfw.org>
// Copyright (c) 2024-2026 M374LX <wilsalx@gmail.com>
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
@ -438,7 +437,7 @@ static void fitToMonitor(_GLFWwindow* window)
// Make the specified window and its video mode active on its monitor
//
static void acquireMonitorWin32(_GLFWwindow* window)
static void acquireMonitor(_GLFWwindow* window)
{
if (!_glfw.win32.acquiredMonitorCount)
{
@ -459,7 +458,7 @@ static void acquireMonitorWin32(_GLFWwindow* window)
// Remove the window and restore the original video mode
//
static void releaseMonitorWin32(_GLFWwindow* window)
static void releaseMonitor(_GLFWwindow* window)
{
if (window->monitor->window != window)
return;
@ -469,7 +468,7 @@ static void releaseMonitorWin32(_GLFWwindow* window)
{
SetThreadExecutionState(ES_CONTINUOUS);
// HACK: Restore mouse trail length saved in acquireMonitorWin32
// HACK: Restore mouse trail length saved in acquireMonitor
SystemParametersInfoW(SPI_SETMOUSETRAILS, _glfw.win32.mouseTrailSize, 0, 0);
}
@ -1049,10 +1048,10 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM l
if (window->monitor && window->win32.iconified != iconified)
{
if (iconified)
releaseMonitorWin32(window);
releaseMonitor(window);
else
{
acquireMonitorWin32(window);
acquireMonitor(window);
fitToMonitor(window);
}
}
@ -1527,7 +1526,7 @@ GLFWbool _glfwCreateWindowWin32(_GLFWwindow* window,
{
_glfwShowWindowWin32(window);
_glfwFocusWindowWin32(window);
acquireMonitorWin32(window);
acquireMonitor(window);
fitToMonitor(window);
if (wndconfig->centerCursor)
@ -1549,7 +1548,7 @@ GLFWbool _glfwCreateWindowWin32(_GLFWwindow* window,
void _glfwDestroyWindowWin32(_GLFWwindow* window)
{
if (window->monitor)
releaseMonitorWin32(window);
releaseMonitor(window);
if (window->context.destroy)
window->context.destroy(window);
@ -1670,7 +1669,7 @@ void _glfwSetWindowSizeWin32(_GLFWwindow* window, int width, int height)
{
if (window->monitor->window == window)
{
acquireMonitorWin32(window);
acquireMonitor(window);
fitToMonitor(window);
}
}
@ -1842,7 +1841,7 @@ void _glfwSetWindowMonitorWin32(_GLFWwindow* window,
{
if (monitor->window == window)
{
acquireMonitorWin32(window);
acquireMonitor(window);
fitToMonitor(window);
}
}
@ -1872,7 +1871,7 @@ void _glfwSetWindowMonitorWin32(_GLFWwindow* window,
}
if (window->monitor)
releaseMonitorWin32(window);
releaseMonitor(window);
_glfwInputWindowMonitor(window, monitor);
@ -1890,7 +1889,7 @@ void _glfwSetWindowMonitorWin32(_GLFWwindow* window,
flags |= SWP_FRAMECHANGED;
}
acquireMonitorWin32(window);
acquireMonitor(window);
GetMonitorInfoW(window->monitor->win32.handle, &mi);
SetWindowPos(window->win32.handle, HWND_TOPMOST,

View File

@ -1,8 +1,7 @@
//========================================================================
// GLFW 3.5 Wayland (modified for raylib) - www.glfw.org; www.raylib.com
// GLFW 3.5 Wayland - www.glfw.org
//------------------------------------------------------------------------
// Copyright (c) 2014 Jonas Ådahl <jadahl@gmail.com>
// Copyright (c) 2024-2026 M374LX <wilsalx@gmail.com>
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
@ -255,7 +254,7 @@ static const struct wl_callback_listener libdecorReadyListener =
// Create key code translation tables
//
static void createKeyTablesWayland(void)
static void createKeyTables(void)
{
memset(_glfw.wl.keycodes, -1, sizeof(_glfw.wl.keycodes));
memset(_glfw.wl.scancodes, -1, sizeof(_glfw.wl.scancodes));
@ -847,7 +846,7 @@ int _glfwInitWayland(void)
_glfw.wl.registry = wl_display_get_registry(_glfw.wl.display);
wl_registry_add_listener(_glfw.wl.registry, &registryListener, NULL);
createKeyTablesWayland();
createKeyTables();
_glfw.wl.keyRepeatTimerfd =
timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK);

View File

@ -1,8 +1,7 @@
//========================================================================
// GLFW 3.5 Wayland (modified for raylib) - www.glfw.org; www.raylib.com
// GLFW 3.5 Wayland - www.glfw.org
//------------------------------------------------------------------------
// Copyright (c) 2014 Jonas Ådahl <jadahl@gmail.com>
// Copyright (c) 2024-2026 M374LX <wilsalx@gmail.com>
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
@ -663,7 +662,7 @@ static void setIdleInhibitor(_GLFWwindow* window, GLFWbool enable)
// Make the specified window and its video mode active on its monitor
//
static void acquireMonitorWayland(_GLFWwindow* window)
static void acquireMonitor(_GLFWwindow* window)
{
if (window->wl.libdecor.frame)
{
@ -684,7 +683,7 @@ static void acquireMonitorWayland(_GLFWwindow* window)
// Remove the window and restore the original video mode
//
static void releaseMonitorWayland(_GLFWwindow* window)
static void releaseMonitor(_GLFWwindow* window)
{
if (window->wl.libdecor.frame)
libdecor_frame_unset_fullscreen(window->wl.libdecor.frame);
@ -1329,7 +1328,7 @@ static GLFWbool flushDisplay(void)
return GLFW_TRUE;
}
static int translateKeyWayland(uint32_t scancode)
static int translateKey(uint32_t scancode)
{
if (scancode < sizeof(_glfw.wl.keycodes) / sizeof(_glfw.wl.keycodes[0]))
return _glfw.wl.keycodes[scancode];
@ -1453,7 +1452,7 @@ static void handleEvents(double* timeout)
for (uint64_t i = 0; i < repeats; i++)
{
_glfwInputKey(_glfw.wl.keyboardFocus,
translateKeyWayland(_glfw.wl.keyRepeatScancode),
translateKey(_glfw.wl.keyRepeatScancode),
_glfw.wl.keyRepeatScancode,
GLFW_PRESS,
_glfw.wl.xkb.modifiers);
@ -1963,7 +1962,7 @@ static void keyboardHandleKey(void* userData,
if (!window)
return;
const int key = translateKeyWayland(scancode);
const int key = translateKey(scancode);
const int action =
state == WL_KEYBOARD_KEY_STATE_PRESSED ? GLFW_PRESS : GLFW_RELEASE;
@ -2810,12 +2809,12 @@ void _glfwSetWindowMonitorWayland(_GLFWwindow* window,
}
if (window->monitor)
releaseMonitorWayland(window);
releaseMonitor(window);
_glfwInputWindowMonitor(window, monitor);
if (window->monitor)
acquireMonitorWayland(window);
acquireMonitor(window);
else
_glfwSetWindowSizeWayland(window, width, height);
}

View File

@ -1,9 +1,8 @@
//========================================================================
// GLFW 3.5 X11 (modified for raylib) - www.glfw.org; www.raylib.com
// GLFW 3.5 X11 - www.glfw.org
//------------------------------------------------------------------------
// Copyright (c) 2002-2006 Marcus Geelnard
// Copyright (c) 2006-2019 Camilla Löwy <elmindreda@glfw.org>
// Copyright (c) 2024-2026 M374LX <wilsalx@gmail.com>
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
@ -211,7 +210,7 @@ static int translateKeySyms(const KeySym* keysyms, int width)
// Create key code translation tables
//
static void createKeyTablesX11(void)
static void createKeyTables(void)
{
int scancodeMin, scancodeMax;
@ -910,7 +909,7 @@ static GLFWbool initExtensions(void)
// Update the key code LUT
// FIXME: We should listen to XkbMapNotify events to track changes to
// the keyboard mapping.
createKeyTablesX11();
createKeyTables();
// String format atoms
_glfw.x11.NULL_ = XInternAtom(_glfw.x11.display, "NULL", False);

View File

@ -1,9 +1,8 @@
//========================================================================
// GLFW 3.5 X11 (modified for raylib) - www.glfw.org; www.raylib.com
// GLFW 3.5 X11 - www.glfw.org
//------------------------------------------------------------------------
// Copyright (c) 2002-2006 Marcus Geelnard
// Copyright (c) 2006-2019 Camilla Löwy <elmindreda@glfw.org>
// Copyright (c) 2024-2026 M374LX
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
@ -236,9 +235,9 @@ static int translateState(int state)
// Translates an X11 key code to a GLFW key token
//
static int translateKeyX11(int scancode)
static int translateKey(int scancode)
{
// Use the pre-filled LUT (see createKeyTablesX11() in x11_init.c)
// Use the pre-filled LUT (see createKeyTables() in x11_init.c)
if (scancode < 0 || scancode > 255)
return GLFW_KEY_UNKNOWN;
@ -1086,7 +1085,7 @@ static const char* getSelectionString(Atom selection)
// Make the specified window and its video mode active on its monitor
//
static void acquireMonitorX11(_GLFWwindow* window)
static void acquireMonitor(_GLFWwindow* window)
{
if (_glfw.x11.saver.count == 0)
{
@ -1125,7 +1124,7 @@ static void acquireMonitorX11(_GLFWwindow* window)
// Remove the window and restore the original video mode
//
static void releaseMonitorX11(_GLFWwindow* window)
static void releaseMonitor(_GLFWwindow* window)
{
if (window->monitor->window != window)
return;
@ -1247,7 +1246,7 @@ static void processEvent(XEvent *event)
case KeyPress:
{
const int key = translateKeyX11(keycode);
const int key = translateKey(keycode);
const int mods = translateState(event->xkey.state);
const int plain = !(mods & (GLFW_MOD_CONTROL | GLFW_MOD_ALT));
@ -1319,7 +1318,7 @@ static void processEvent(XEvent *event)
case KeyRelease:
{
const int key = translateKeyX11(keycode);
const int key = translateKey(keycode);
const int mods = translateState(event->xkey.state);
if (!_glfw.x11.xkb.detectable)
@ -1811,9 +1810,9 @@ static void processEvent(XEvent *event)
if (window->monitor)
{
if (iconified)
releaseMonitorX11(window);
releaseMonitor(window);
else
acquireMonitorX11(window);
acquireMonitor(window);
}
window->x11.iconified = iconified;
@ -2030,7 +2029,7 @@ GLFWbool _glfwCreateWindowX11(_GLFWwindow* window,
{
_glfwShowWindowX11(window);
updateWindowMode(window);
acquireMonitorX11(window);
acquireMonitor(window);
if (wndconfig->centerCursor)
_glfwCenterCursorInContentArea(window);
@ -2055,7 +2054,7 @@ void _glfwDestroyWindowX11(_GLFWwindow* window)
enableCursor(window);
if (window->monitor)
releaseMonitorX11(window);
releaseMonitor(window);
if (window->x11.ic)
{
@ -2215,7 +2214,7 @@ void _glfwSetWindowSizeX11(_GLFWwindow* window, int width, int height)
if (window->monitor)
{
if (window->monitor->window == window)
acquireMonitorX11(window);
acquireMonitor(window);
}
else
{
@ -2518,7 +2517,7 @@ void _glfwSetWindowMonitorX11(_GLFWwindow* window,
if (monitor)
{
if (monitor->window == window)
acquireMonitorX11(window);
acquireMonitor(window);
}
else
{
@ -2537,7 +2536,7 @@ void _glfwSetWindowMonitorX11(_GLFWwindow* window,
{
_glfwSetWindowDecoratedX11(window, window->decorated);
_glfwSetWindowFloatingX11(window, window->floating);
releaseMonitorX11(window);
releaseMonitor(window);
}
_glfwInputWindowMonitor(window, monitor);
@ -2552,7 +2551,7 @@ void _glfwSetWindowMonitorX11(_GLFWwindow* window,
}
updateWindowMode(window);
acquireMonitorX11(window);
acquireMonitor(window);
}
else
{

View File

@ -287,8 +287,14 @@ void ToggleBorderlessWindowed(void)
CORE.Window.screen.height = mode->height;
// Set screen position and size
#if defined(_WIN32)
// NOTE: To prevent the fullscreen window from always staying on top, don't pass a monitor at this point.
glfwSetWindowMonitor(platform.handle, NULL, CORE.Window.position.x, CORE.Window.position.y,
CORE.Window.screen.width, CORE.Window.screen.height, mode->refreshRate);
#else
glfwSetWindowMonitor(platform.handle, monitors[monitor], CORE.Window.position.x, CORE.Window.position.y,
CORE.Window.screen.width, CORE.Window.screen.height, mode->refreshRate);
#endif
// Refocus window
glfwFocusWindow(platform.handle);
@ -1024,6 +1030,8 @@ const char *GetMonitorName(int monitor)
// Get window position XY on monitor
Vector2 GetWindowPosition(void)
{
glfwGetWindowPos(platform.handle, &CORE.Window.position.x, &CORE.Window.position.y);
return (Vector2){ (float)CORE.Window.position.x, (float)CORE.Window.position.y };
}

View File

@ -1129,12 +1129,37 @@ const char *GetClipboardText(void)
return NULL;
}
#if SUPPORT_CLIPBOARD_IMAGE && SUPPORT_MODULE_RTEXTURES
#define WIN32_CLIPBOARD_IMPLEMENTATION
#define WINUSER_ALREADY_INCLUDED
#define WINBASE_ALREADY_INCLUDED
#define WINGDI_ALREADY_INCLUDED
#include "../external/win32_clipboard.h"
#endif
// Get clipboard image
Image GetClipboardImage(void)
{
Image image = { 0 };
TRACELOG(LOG_WARNING, "GetClipboardText not implemented");
#if SUPPORT_CLIPBOARD_IMAGE && SUPPORT_MODULE_RTEXTURES
unsigned int dataSize = 0;
void *fileData = NULL;
int width = 0;
int height = 0;
fileData = (void *)Win32GetClipboardImageData(&width, &height, &dataSize);
if (fileData == NULL) TRACELOG(LOG_WARNING, "Clipboard image: Couldn't get clipboard data.");
else
{
image = LoadImageFromMemory(".bmp", (const unsigned char*)fileData, (int)dataSize);
RL_FREE(fileData);
}
#else
TRACELOG(LOG_WARNING, "Clipboard image: SUPPORT_CLIPBOARD_IMAGE requires SUPPORT_MODULE_RTEXTURES to work properly");
#endif // SUPPORT_CLIPBOARD_IMAGE
return image;
}

View File

@ -42,6 +42,7 @@
**********************************************************************************************/
#include <emscripten/emscripten.h> // Emscripten functionality for C
#include <emscripten/dom_pk_codes.h> // Emscripten physical keyboard codes
#include <emscripten/html5.h> // Emscripten HTML5 library
#include <sys/time.h> // Required for: timespec, nanosleep(), select() - POSIX
@ -54,6 +55,9 @@
#define _POSIX_C_SOURCE 199309L // Required for: CLOCK_MONOTONIC if compiled with c99 without gnu ext.
#endif
#define DOM_PK_CODE_MAPPED_NUM 128
#define DOM_PK_CODE_EXTENDED_OFFSET 0xE000
//----------------------------------------------------------------------------------
// Types and Structures Definition
//----------------------------------------------------------------------------------
@ -70,9 +74,121 @@ extern CoreData CORE; // Global CORE state context
static PlatformData platform = { 0 }; // Platform specific data
//----------------------------------------------------------------------------------
// Global Variables Definition
//----------------------------------------------------------------------------------
static const KeyboardKey mapDomCodeToKey[DOM_PK_CODE_MAPPED_NUM] = {
[DOM_PK_ESCAPE] = KEY_ESCAPE,
[DOM_PK_0] = KEY_ZERO,
[DOM_PK_1] = KEY_ONE,
[DOM_PK_2] = KEY_TWO,
[DOM_PK_3] = KEY_THREE,
[DOM_PK_4] = KEY_FOUR,
[DOM_PK_5] = KEY_FIVE,
[DOM_PK_6] = KEY_SIX,
[DOM_PK_7] = KEY_SEVEN,
[DOM_PK_8] = KEY_EIGHT,
[DOM_PK_9] = KEY_NINE,
[DOM_PK_MINUS] = KEY_MINUS,
[DOM_PK_EQUAL] = KEY_EQUAL,
[DOM_PK_BACKSPACE] = KEY_BACKSPACE,
[DOM_PK_TAB] = KEY_TAB,
[DOM_PK_Q] = KEY_Q,
[DOM_PK_W] = KEY_W,
[DOM_PK_E] = KEY_E,
[DOM_PK_R] = KEY_R,
[DOM_PK_T] = KEY_T,
[DOM_PK_Y] = KEY_Y,
[DOM_PK_U] = KEY_U,
[DOM_PK_I] = KEY_I,
[DOM_PK_O] = KEY_O,
[DOM_PK_P] = KEY_P,
[DOM_PK_BRACKET_LEFT] = KEY_LEFT_BRACKET,
[DOM_PK_BRACKET_RIGHT] = KEY_RIGHT_BRACKET,
[DOM_PK_ENTER] = KEY_ENTER,
[DOM_PK_CONTROL_LEFT] = KEY_LEFT_CONTROL,
[DOM_PK_A] = KEY_A,
[DOM_PK_S] = KEY_S,
[DOM_PK_D] = KEY_D,
[DOM_PK_F] = KEY_F,
[DOM_PK_G] = KEY_G,
[DOM_PK_H] = KEY_H,
[DOM_PK_J] = KEY_J,
[DOM_PK_K] = KEY_K,
[DOM_PK_L] = KEY_L,
[DOM_PK_SEMICOLON] = KEY_SEMICOLON,
[DOM_PK_QUOTE] = KEY_APOSTROPHE,
[DOM_PK_BACKQUOTE] = KEY_GRAVE,
[DOM_PK_SHIFT_LEFT] = KEY_LEFT_SHIFT,
[DOM_PK_BACKSLASH] = KEY_BACKSLASH,
[DOM_PK_Z] = KEY_Z,
[DOM_PK_X] = KEY_X,
[DOM_PK_C] = KEY_C,
[DOM_PK_V] = KEY_V,
[DOM_PK_B] = KEY_B,
[DOM_PK_N] = KEY_N,
[DOM_PK_M] = KEY_M,
[DOM_PK_COMMA] = KEY_COMMA,
[DOM_PK_PERIOD] = KEY_PERIOD,
[DOM_PK_SLASH] = KEY_SLASH,
[DOM_PK_SHIFT_RIGHT] = KEY_RIGHT_SHIFT,
[DOM_PK_NUMPAD_MULTIPLY] = KEY_KP_MULTIPLY,
[DOM_PK_ALT_LEFT] = KEY_LEFT_ALT,
[DOM_PK_SPACE] = KEY_SPACE,
[DOM_PK_CAPS_LOCK] = KEY_CAPS_LOCK,
[DOM_PK_F1] = KEY_F1,
[DOM_PK_F2] = KEY_F2,
[DOM_PK_F3] = KEY_F3,
[DOM_PK_F4] = KEY_F4,
[DOM_PK_F5] = KEY_F5,
[DOM_PK_F6] = KEY_F6,
[DOM_PK_F7] = KEY_F7,
[DOM_PK_F8] = KEY_F8,
[DOM_PK_F9] = KEY_F9,
[DOM_PK_F10] = KEY_F10,
[DOM_PK_PAUSE] = KEY_PAUSE,
[DOM_PK_SCROLL_LOCK] = KEY_SCROLL_LOCK,
[DOM_PK_NUMPAD_7] = KEY_KP_7,
[DOM_PK_NUMPAD_8] = KEY_KP_8,
[DOM_PK_NUMPAD_9] = KEY_KP_9,
[DOM_PK_NUMPAD_SUBTRACT] = KEY_KP_SUBTRACT,
[DOM_PK_NUMPAD_4] = KEY_KP_4,
[DOM_PK_NUMPAD_5] = KEY_KP_5,
[DOM_PK_NUMPAD_6] = KEY_KP_6,
[DOM_PK_NUMPAD_ADD] = KEY_KP_ADD,
[DOM_PK_NUMPAD_1] = KEY_KP_1,
[DOM_PK_NUMPAD_2] = KEY_KP_2,
[DOM_PK_NUMPAD_3] = KEY_KP_3,
[DOM_PK_NUMPAD_0] = KEY_KP_0,
[DOM_PK_NUMPAD_DECIMAL] = KEY_KP_DECIMAL,
[DOM_PK_PRINT_SCREEN] = KEY_PRINT_SCREEN,
[DOM_PK_F11] = KEY_F11,
[DOM_PK_F12] = KEY_F12,
[DOM_PK_NUMPAD_EQUAL] = KEY_KP_EQUAL,
[DOM_PK_NUMPAD_COMMA] = KEY_KP_DECIMAL
};
// Emscripten places keys with extended scan codes (right-side modifiers, navigation and some keypad keys) in the 0xE0xx range
static const KeyboardKey mapDomCodeToKeyExtended[DOM_PK_CODE_MAPPED_NUM] = {
[DOM_PK_NUMPAD_ENTER - DOM_PK_CODE_EXTENDED_OFFSET] = KEY_KP_ENTER,
[DOM_PK_CONTROL_RIGHT - DOM_PK_CODE_EXTENDED_OFFSET] = KEY_RIGHT_CONTROL,
[DOM_PK_AUDIO_VOLUME_DOWN - DOM_PK_CODE_EXTENDED_OFFSET] = KEY_VOLUME_DOWN,
[DOM_PK_AUDIO_VOLUME_UP - DOM_PK_CODE_EXTENDED_OFFSET] = KEY_VOLUME_UP,
[DOM_PK_NUMPAD_DIVIDE - DOM_PK_CODE_EXTENDED_OFFSET] = KEY_KP_DIVIDE,
[DOM_PK_ALT_RIGHT - DOM_PK_CODE_EXTENDED_OFFSET] = KEY_RIGHT_ALT,
[DOM_PK_NUM_LOCK - DOM_PK_CODE_EXTENDED_OFFSET] = KEY_NUM_LOCK,
[DOM_PK_HOME - DOM_PK_CODE_EXTENDED_OFFSET] = KEY_HOME,
[DOM_PK_ARROW_UP - DOM_PK_CODE_EXTENDED_OFFSET] = KEY_UP,
[DOM_PK_PAGE_UP - DOM_PK_CODE_EXTENDED_OFFSET] = KEY_PAGE_UP,
[DOM_PK_ARROW_LEFT - DOM_PK_CODE_EXTENDED_OFFSET] = KEY_LEFT,
[DOM_PK_ARROW_RIGHT - DOM_PK_CODE_EXTENDED_OFFSET] = KEY_RIGHT,
[DOM_PK_END - DOM_PK_CODE_EXTENDED_OFFSET] = KEY_END,
[DOM_PK_ARROW_DOWN - DOM_PK_CODE_EXTENDED_OFFSET] = KEY_DOWN,
[DOM_PK_PAGE_DOWN - DOM_PK_CODE_EXTENDED_OFFSET] = KEY_PAGE_DOWN,
[DOM_PK_INSERT - DOM_PK_CODE_EXTENDED_OFFSET] = KEY_INSERT,
[DOM_PK_DELETE - DOM_PK_CODE_EXTENDED_OFFSET] = KEY_DELETE,
[DOM_PK_META_LEFT - DOM_PK_CODE_EXTENDED_OFFSET] = KEY_LEFT_SUPER,
[DOM_PK_META_RIGHT - DOM_PK_CODE_EXTENDED_OFFSET] = KEY_RIGHT_SUPER,
[DOM_PK_CONTEXT_MENU - DOM_PK_CODE_EXTENDED_OFFSET] = KEY_KB_MENU
};
static const char cursorLUT[11][12] = {
"default", // 0 MOUSE_CURSOR_DEFAULT
"default", // 1 MOUSE_CURSOR_ARROW
@ -110,6 +226,8 @@ static EM_BOOL EmscriptenPointerlockCallback(int eventType, const EmscriptenPoin
static EM_BOOL EmscriptenTouchCallback(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData);
static EM_BOOL EmscriptenGamepadCallback(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData);
static KeyboardKey ConvertKeyboardEventToKey(const EmscriptenKeyboardEvent *keyboardEvent);
// JS: Set the canvas id provided by the module configuration
EM_JS(void, SetCanvasIdJs, (char *out, int outSize), {
var canvasId = "#" + Module.canvas.id;
@ -962,7 +1080,7 @@ void SwapScreenBuffer(void)
// Get elapsed time measure in seconds since InitTimer()
double GetTime(void)
{
double time = emscripten_get_now()*1000.0;
double time = emscripten_get_now()/1000.0;
return time;
}
@ -1391,7 +1509,11 @@ static EM_BOOL EmscriptenFocusCallback(int eventType, const EmscriptenFocusEvent
switch (eventType)
{
case EMSCRIPTEN_EVENT_BLUR: FLAG_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED); break;
case EMSCRIPTEN_EVENT_BLUR:
{
FLAG_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED);
memset(CORE.Input.Keyboard.currentKeyState, 0, sizeof(CORE.Input.Keyboard.currentKeyState));
} break;
case EMSCRIPTEN_EVENT_FOCUS: FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED); break;
default: consumed = 0; break;
}
@ -1454,52 +1576,75 @@ static void WindowDropCallback(GLFWwindow *window, int count, const char **paths
}
*/
// Emscripten keyboard event to raylib key mapping
static KeyboardKey ConvertKeyboardEventToKey(const EmscriptenKeyboardEvent *keyboardEvent)
{
DOM_PK_CODE_TYPE code = emscripten_compute_dom_pk_code(keyboardEvent->code);
KeyboardKey key = KEY_NULL;
if ((code > DOM_PK_UNKNOWN) && (code < DOM_PK_CODE_MAPPED_NUM)) key = mapDomCodeToKey[code];
else if ((code >= DOM_PK_CODE_EXTENDED_OFFSET) && (code < (DOM_PK_CODE_EXTENDED_OFFSET + DOM_PK_CODE_MAPPED_NUM)))
{
key = mapDomCodeToKeyExtended[code - DOM_PK_CODE_EXTENDED_OFFSET];
}
return key;
}
// Emscripten: Called on key events
// TODO: keyCodes should be mapped to raylib/GLFW3 Key values
static EM_BOOL EmscriptenKeyboardCallback(int eventType, const EmscriptenKeyboardEvent *keyboardEvent, void *userData)
{
KeyboardKey key = ConvertKeyboardEventToKey(keyboardEvent);
EM_BOOL preventDefault = 1;
switch (eventType)
{
case EMSCRIPTEN_EVENT_KEYPRESS:
{
if (keyboardEvent->repeat) CORE.Input.Keyboard.keyRepeatInFrame[keyboardEvent->keyCode] = 1;
} break;
case EMSCRIPTEN_EVENT_KEYDOWN:
{
CORE.Input.Keyboard.currentKeyState[keyboardEvent->keyCode] = 1;
if (key != KEY_NULL)
{
// Check if there is space available in the key queue
if ((!keyboardEvent->repeat) && (CORE.Input.Keyboard.currentKeyState[key] == 0) &&
(CORE.Input.Keyboard.keyPressedQueueCount < MAX_KEY_PRESSED_QUEUE))
{
CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = key;
CORE.Input.Keyboard.keyPressedQueueCount++;
}
CORE.Input.Keyboard.currentKeyState[key] = 1;
if (keyboardEvent->repeat) CORE.Input.Keyboard.keyRepeatInFrame[key] = 1;
// Check the exit key to set close window
if ((key == CORE.Input.Keyboard.exitKey) && !keyboardEvent->repeat) CORE.Window.shouldClose = true;
}
// Allow printable keydown events so the browser can emit the keypress event used to collect characters
preventDefault = ((key == KEY_BACKSPACE) || (key == KEY_TAB) ||
(key == KEY_INSERT) || (key == KEY_DELETE) ||
(key == KEY_HOME) || (key == KEY_END) ||
(key == KEY_PAGE_UP) || (key == KEY_PAGE_DOWN) ||
(key == KEY_LEFT) || (key == KEY_RIGHT) ||
(key == KEY_UP) || (key == KEY_DOWN) ||
((key >= KEY_F1) && (key <= KEY_F12)) || keyboardEvent->ctrlKey);
} break;
case EMSCRIPTEN_EVENT_KEYUP:
{
CORE.Input.Keyboard.currentKeyState[keyboardEvent->keyCode] = 0;
if (key != KEY_NULL) CORE.Input.Keyboard.currentKeyState[key] = 0;
} break;
case EMSCRIPTEN_EVENT_KEYPRESS:
{
// Check if there is space available in the queue for characters to be added
if ((keyboardEvent->charCode > 0) && (CORE.Input.Keyboard.charPressedQueueCount < MAX_CHAR_PRESSED_QUEUE))
{
CORE.Input.Keyboard.charPressedQueue[CORE.Input.Keyboard.charPressedQueueCount] = keyboardEvent->charCode;
CORE.Input.Keyboard.charPressedQueueCount++;
}
} break;
default: break;
}
// TODO: Add char codes
//unsigned int charCode
// Check if there is space available in the queue for characters to be added
/*
if (CORE.Input.Keyboard.charPressedQueueCount < MAX_CHAR_PRESSED_QUEUE)
{
// Add character to the queue
CORE.Input.Keyboard.charPressedQueue[CORE.Input.Keyboard.charPressedQueueCount] = keyboardEvent->charCode;
CORE.Input.Keyboard.charPressedQueueCount++;
}
*/
/*
// Check if there is space available in the key queue
if ((CORE.Input.Keyboard.keyPressedQueueCount < MAX_KEY_PRESSED_QUEUE) && (eventType == EMSCRIPTEN_EVENT_KEYPRESS))
{
// Add character to the queue
CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = keyboardEvent->keyCode;
CORE.Input.Keyboard.keyPressedQueueCount++;
}
// Check the exit key to set close window
//if ((keyboardEvent->keyCode == CORE.Input.Keyboard.exitKey) && (eventType == EMSCRIPTEN_EVENT_KEYPRESS)) CORE.Window.shouldClose = true;
*/
return 1; // The event was consumed by the callback handler
return preventDefault;
}
// Emscripten: Called on mouse input events
@ -1563,7 +1708,8 @@ static EM_BOOL EmscriptenMouseCallback(int eventType, const EmscriptenMouseEvent
if (GetMouseX() != 0 || GetMouseY() != 0) ProcessGestureEvent(gestureEvent);
#endif
return 1; // The event was consumed by the callback handler
// Don't prevent default to allow focusing on canvas
return 0;
}
// Emscripten: Called on mouse move events
@ -1577,32 +1723,18 @@ static EM_BOOL EmscriptenMouseMoveCallback(int eventType, const EmscriptenMouseE
else
{
// Get mouse position in canvas CSS pixels
float mouseCssX = (float)mouseEvent->canvasX;
float mouseCssY = (float)mouseEvent->canvasY;
float mouseCssX = (float)mouseEvent->targetX;
float mouseCssY = (float)mouseEvent->targetY;
// Get canvas sizes
double cssWidth = 0.0;
double cssHeight = 0.0;
emscripten_get_element_css_size(platform.canvasId, &cssWidth, &cssHeight);
int fbWidth = 0;
int fbHeight = 0;
emscripten_get_canvas_element_size(platform.canvasId, &fbWidth, &fbHeight);
// Convert CSS to framebuffer coordinates
float scaleX = (float)fbWidth/(float)cssWidth;
float scaleY = (float)fbHeight/(float)cssHeight;
int mouseX = (int)(mouseCssX*scaleX);
int mouseY = (int)(mouseCssY*scaleY);
CORE.Input.Mouse.currentPosition.x = mouseX;//(float)mouseEvent->canvasX;
CORE.Input.Mouse.currentPosition.y = mouseY;//(float)mouseEvent->canvasY;
// Shorter alternative:
//double dpr = emscripten_get_device_pixel_ratio();
//int mouseX = (int)(e->canvasX*dpr);
//int mouseY = (int)(e->canvasY*dpr);
float normalizedX = mouseCssX/(float)cssWidth;
float normalizedY = mouseCssY/(float)cssHeight;
CORE.Input.Mouse.currentPosition.x = normalizedX*(float)CORE.Window.screen.width;
CORE.Input.Mouse.currentPosition.y = normalizedY*(float)CORE.Window.screen.height;
CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition;
}

View File

@ -1284,6 +1284,7 @@ RLAPI void DrawLineDashed(Vector2 startPos, Vector2 endPos, int dashSize, int sp
RLAPI void DrawTriangle(Vector2 v1, Vector2 v2, Vector2 v3, Color color); // Draw a color-filled triangle, counter-clockwise vertex order
RLAPI void DrawTriangleGradient(Vector2 v1, Vector2 v2, Vector2 v3, Color c1, Color c2, Color c3); // Draw triangle with interpolated colors, counter-clockwise vertex/color order
RLAPI void DrawTriangleLines(Vector2 v1, Vector2 v2, Vector2 v3, Color color); // Draw triangle outline, counter-clockwise vertex order
RLAPI void DrawTriangleLinesEx(Vector2 v1, Vector2 v2, Vector2 v3, float thick, Color color); // Draw triangle outline with line thickness, counter-clockwise vertex order
RLAPI void DrawTriangleFan(const Vector2 *points, int pointCount, Color color); // Draw a triangle fan defined by points (first vertex is the center)
RLAPI void DrawTriangleStrip(const Vector2 *points, int pointCount, Color color); // Draw a triangle strip defined by points
RLAPI void DrawRectangle(int posX, int posY, int width, int height, Color color); // Draw a color-filled rectangle
@ -1306,6 +1307,7 @@ RLAPI void DrawCircleV(Vector2 center, float radius, Color color);
RLAPI void DrawCircleGradient(Vector2 center, float radius, Color inner, Color outer); // Draw a gradient-filled circle
RLAPI void DrawCircleSector(Vector2 center, float radius, float startAngle, float endAngle, int segments, Color color); // Draw a piece of a circle
RLAPI void DrawCircleSectorLines(Vector2 center, float radius, float startAngle, float endAngle, int segments, Color color); // Draw circle sector outline
RLAPI void DrawCircleSectorLinesEx(Vector2 center, float radius, float startAngle, float endAngle, int segments, float thick, Color color); // Draw circle sector outline with thickness
RLAPI void DrawCircleLines(int centerX, int centerY, float radius, Color color); // Draw circle outline
RLAPI void DrawCircleLinesV(Vector2 center, float radius, Color color); // Draw circle outline (Vector version)
RLAPI void DrawCircleLinesEx(Vector2 center, float radius, float thick, Color color); // Draw circle outline with line thickness
@ -1313,8 +1315,10 @@ RLAPI void DrawEllipse(int centerX, int centerY, float radiusH, float radiusV, C
RLAPI void DrawEllipseV(Vector2 center, float radiusH, float radiusV, Color color); // Draw ellipse (Vector version)
RLAPI void DrawEllipseLines(int centerX, int centerY, float radiusH, float radiusV, Color color); // Draw ellipse outline
RLAPI void DrawEllipseLinesV(Vector2 center, float radiusH, float radiusV, Color color); // Draw ellipse outline (Vector version)
RLAPI void DrawEllipseLinesEx(Vector2 center, float radiusH, float radiusV, float thick, Color color); // Draw ellipse outline with line thickness
RLAPI void DrawRing(Vector2 center, float innerRadius, float outerRadius, float startAngle, float endAngle, int segments, Color color); // Draw ring
RLAPI void DrawRingLines(Vector2 center, float innerRadius, float outerRadius, float startAngle, float endAngle, int segments, Color color); // Draw ring outline
RLAPI void DrawRingLinesEx(Vector2 center, float innerRadius, float outerRadius, float startAngle, float endAngle, int segments, float thick, Color color); // Draw ring outline with line thickness
// Splines drawing functions
RLAPI void DrawSplineLinear(const Vector2 *points, int pointCount, float thick, Color color); // Draw spline: Linear, minimum 2 points

View File

@ -2583,20 +2583,28 @@ const char *GetPrevDirectoryPath(const char *dirPath)
{
static char prevDirPath[MAX_FILEPATH_LENGTH] = { 0 };
memset(prevDirPath, 0, MAX_FILEPATH_LENGTH);
int dirPathLength = (int)strlen(dirPath);
if (dirPathLength <= 3) snprintf(prevDirPath, MAX_FILEPATH_LENGTH, "%s", dirPath);
for (int i = (dirPathLength - 1); (i >= 0) && (dirPathLength > 3); i--)
const int lastIndex = (int)strlen(dirPath) - 1;
bool isFile = IsPathFile(dirPath);
for (int i = lastIndex; i >= 0; i--)
{
// If the character is a path separator.
if ((dirPath[i] == '\\') || (dirPath[i] == '/'))
{
// Check for root: "C:\" or "/"
if (((i == 2) && (dirPath[1] ==':')) || (i == 0)) i++;
// If this character is a leading '/' (e.g. the '/' in "/usr") or
// part of a drive root (e.g. "C:\"), include it with the result.
if ((i == 0) || ((i == 2) && (dirPath[1] == ':'))) i += 1;
// If this character is a trailing path separator (e.g. the last
// '/' in "/usr/bin/" or the last '\' in "C:\raylib\"), continue.
else if (i == lastIndex) continue;
if (!isFile)
{
memcpy(prevDirPath, dirPath, i);
break;
}
else isFile = false;
}
}
return prevDirPath;

View File

@ -77,6 +77,15 @@
#include "external/glfw/src/window.c"
#include "external/glfw/src/input.c"
#include "external/glfw/src/vulkan.c"
#include "external/glfw/src/egl_context.c"
#include "external/glfw/src/osmesa_context.c"
//We need to define this function because GLFW calls it even though raylib does
//not use GLFW's Null platform
GLFWbool _glfwConnectNull(int platformID, _GLFWplatform* platform)
{
return GLFW_FALSE;
}
#if defined(_WIN32) || defined(__CYGWIN__)
#include "external/glfw/src/win32_init.c"
@ -87,9 +96,6 @@
#include "external/glfw/src/win32_time.c"
#include "external/glfw/src/win32_thread.c"
#include "external/glfw/src/wgl_context.c"
#include "external/glfw/src/egl_context.c"
#include "external/glfw/src/osmesa_context.c"
#endif
#if defined(__linux__)
@ -100,19 +106,42 @@
#include "external/glfw/src/linux_joystick.c"
#include "external/glfw/src/xkb_unicode.c"
#include "external/glfw/src/egl_context.c"
#include "external/glfw/src/osmesa_context.c"
#if defined(_GLFW_WAYLAND)
//These functions need to be temporarily renamed because including
//source files for both Wayland and X11 results in pairs of functions
//with the same name and thus a build error
#define createKeyTables createKeyTablesWayland
#define translateKey translateKeyWayland
#define acquireMonitor acquireMonitorWayland
#define releaseMonitor releaseMonitorWayland
#include "external/glfw/src/wl_init.c"
#include "external/glfw/src/wl_monitor.c"
#include "external/glfw/src/wl_window.c"
#undef createKeyTables
#undef translateKey
#undef acquireMonitor
#undef releaseMonitor
#endif
#if defined(_GLFW_X11)
//These functions need to be temporarily renamed because including
//source files for both Wayland and X11 results in pairs of functions
//with the same name and thus a build error
#define createKeyTables createKeyTablesX11
#define translateKey translateKeyX11
#define acquireMonitor acquireMonitorX11
#define releaseMonitor releaseMonitorX11
#include "external/glfw/src/x11_init.c"
#include "external/glfw/src/x11_monitor.c"
#include "external/glfw/src/x11_window.c"
#include "external/glfw/src/glx_context.c"
#undef createKeyTables
#undef translateKey
#undef acquireMonitor
#undef releaseMonitor
#endif
#endif
@ -128,9 +157,6 @@
#include "external/glfw/src/x11_monitor.c"
#include "external/glfw/src/x11_window.c"
#include "external/glfw/src/glx_context.c"
#include "external/glfw/src/egl_context.c"
#include "external/glfw/src/osmesa_context.c"
#endif
#if defined(__APPLE__)
@ -142,7 +168,4 @@
#include "external/glfw/src/cocoa_window.m"
#include "external/glfw/src/macos_time.c"
#include "external/glfw/src/nsgl_context.m"
#include "external/glfw/src/egl_context.c"
#include "external/glfw/src/osmesa_context.c"
#endif

File diff suppressed because it is too large Load Diff

View File

@ -588,7 +588,7 @@ int main(int argc, char *argv[])
else if (TextIsEqual(exCategory, "text")) nextCategoryIndex = 4;
else if (TextIsEqual(exCategory, "models")) nextCategoryIndex = 5;
else if (TextIsEqual(exCategory, "shaders")) nextCategoryIndex = 6;
else if (TextIsEqual(exCategory, "audio")) nextCategoryIndex = 7;
else if (TextIsEqual(exCategory, "audio")) nextCategoryIndex = -1; // EOF, "audio" is the last category, avoid out-of-bounds exCategories[7] access
// Get required example info from example file header (if provided)
@ -621,7 +621,13 @@ int main(int argc, char *argv[])
else
{
// Add example to collection, at the end of the category list
int categoryIndex = TextFindIndex(exCollectionList, exCategories[nextCategoryIndex]);
// NOTE: Search is anchored to "\n<category>;" (not just "<category>") to avoid
// false-positive matches when one category name is a text-prefix of another
// (e.g. "text" is a prefix of "textures", so a bare search for "text" matches
// inside "core_render_texture" or at the start of the "textures" block instead
// of the actual "text" category boundary, corrupting the collection list)
int categoryIndex = TextFindIndex(exCollectionList, TextFormat("\n%s;", exCategories[nextCategoryIndex])) + 1;
if (categoryIndex == 0) categoryIndex = (int)strlen(exCollectionList); // Category not found, fallback to EOF
memcpy(exCollectionListUpdated, exCollectionList, categoryIndex);
int textWritenSize = sprintf(exCollectionListUpdated + categoryIndex, TextFormat("%s;%s;%s;%s;%s;%i;%i;\"%s\";@%s\n",
exInfo->category, exInfo->name, starsText, exInfo->verCreated, exInfo->verUpdated, exInfo->yearCreated, exInfo->yearReviewed, exInfo->author, exInfo->authorGitHub));
@ -1030,8 +1036,16 @@ int main(int argc, char *argv[])
// Find position to add new example on list, just before the following category
// Category order: core, shapes, textures, text, models, shaders, audio
// NOTE: Search is anchored to "\n<category>;" (not just "\n<category>") to avoid
// false-positive matches when one category name is a text-prefix of another
// (e.g. "text" is a prefix of "textures", so a bare "\ntext" search matches the
// START of the "textures" block instead of the actual "text" category boundary).
int exListNextCatIndex = -1;
if (nextCatIndex != -1) exListNextCatIndex = TextFindIndex(exList, TextFormat("\n%s", exCategories[nextCatIndex])) + 1;
if (nextCatIndex != -1)
{
exListNextCatIndex = TextFindIndex(exList, TextFormat("\n%s;", exCategories[nextCatIndex])) + 1;
if (exListNextCatIndex == 0) exListNextCatIndex = exListLen; // Category not found, fallback to EOF
}
else exListNextCatIndex = exListLen; // EOF
strncpy(exListUpdated, exList, exListNextCatIndex);

View File

@ -5768,6 +5768,33 @@
}
]
},
{
"name": "DrawTriangleLinesEx",
"description": "Draw triangle outline with line thickness, counter-clockwise vertex order",
"returnType": "void",
"params": [
{
"type": "Vector2",
"name": "v1"
},
{
"type": "Vector2",
"name": "v2"
},
{
"type": "Vector2",
"name": "v3"
},
{
"type": "float",
"name": "thick"
},
{
"type": "Color",
"name": "color"
}
]
},
{
"name": "DrawTriangleFan",
"description": "Draw a triangle fan defined by points (first vertex is the center)",
@ -6310,6 +6337,41 @@
}
]
},
{
"name": "DrawCircleSectorLinesEx",
"description": "Draw circle sector outline with thickness",
"returnType": "void",
"params": [
{
"type": "Vector2",
"name": "center"
},
{
"type": "float",
"name": "radius"
},
{
"type": "float",
"name": "startAngle"
},
{
"type": "float",
"name": "endAngle"
},
{
"type": "int",
"name": "segments"
},
{
"type": "float",
"name": "thick"
},
{
"type": "Color",
"name": "color"
}
]
},
{
"name": "DrawCircleLines",
"description": "Draw circle outline",
@ -6475,6 +6537,33 @@
}
]
},
{
"name": "DrawEllipseLinesEx",
"description": "Draw ellipse outline with line thickness",
"returnType": "void",
"params": [
{
"type": "Vector2",
"name": "center"
},
{
"type": "float",
"name": "radiusH"
},
{
"type": "float",
"name": "radiusV"
},
{
"type": "float",
"name": "thick"
},
{
"type": "Color",
"name": "color"
}
]
},
{
"name": "DrawRing",
"description": "Draw ring",
@ -6545,6 +6634,45 @@
}
]
},
{
"name": "DrawRingLinesEx",
"description": "Draw ring outline with line thickness",
"returnType": "void",
"params": [
{
"type": "Vector2",
"name": "center"
},
{
"type": "float",
"name": "innerRadius"
},
{
"type": "float",
"name": "outerRadius"
},
{
"type": "float",
"name": "startAngle"
},
{
"type": "float",
"name": "endAngle"
},
{
"type": "int",
"name": "segments"
},
{
"type": "float",
"name": "thick"
},
{
"type": "Color",
"name": "color"
}
]
},
{
"name": "DrawSplineLinear",
"description": "Draw spline: Linear, minimum 2 points",

View File

@ -4895,6 +4895,18 @@ return {
{type = "Color", name = "color"}
}
},
{
name = "DrawTriangleLinesEx",
description = "Draw triangle outline with line thickness, counter-clockwise vertex order",
returnType = "void",
params = {
{type = "Vector2", name = "v1"},
{type = "Vector2", name = "v2"},
{type = "Vector2", name = "v3"},
{type = "float", name = "thick"},
{type = "Color", name = "color"}
}
},
{
name = "DrawTriangleFan",
description = "Draw a triangle fan defined by points (first vertex is the center)",
@ -5146,6 +5158,20 @@ return {
{type = "Color", name = "color"}
}
},
{
name = "DrawCircleSectorLinesEx",
description = "Draw circle sector outline with thickness",
returnType = "void",
params = {
{type = "Vector2", name = "center"},
{type = "float", name = "radius"},
{type = "float", name = "startAngle"},
{type = "float", name = "endAngle"},
{type = "int", name = "segments"},
{type = "float", name = "thick"},
{type = "Color", name = "color"}
}
},
{
name = "DrawCircleLines",
description = "Draw circle outline",
@ -5224,6 +5250,18 @@ return {
{type = "Color", name = "color"}
}
},
{
name = "DrawEllipseLinesEx",
description = "Draw ellipse outline with line thickness",
returnType = "void",
params = {
{type = "Vector2", name = "center"},
{type = "float", name = "radiusH"},
{type = "float", name = "radiusV"},
{type = "float", name = "thick"},
{type = "Color", name = "color"}
}
},
{
name = "DrawRing",
description = "Draw ring",
@ -5252,6 +5290,21 @@ return {
{type = "Color", name = "color"}
}
},
{
name = "DrawRingLinesEx",
description = "Draw ring outline with line thickness",
returnType = "void",
params = {
{type = "Vector2", name = "center"},
{type = "float", name = "innerRadius"},
{type = "float", name = "outerRadius"},
{type = "float", name = "startAngle"},
{type = "float", name = "endAngle"},
{type = "int", name = "segments"},
{type = "float", name = "thick"},
{type = "Color", name = "color"}
}
},
{
name = "DrawSplineLinear",
description = "Draw spline: Linear, minimum 2 points",

View File

@ -3594,6 +3594,16 @@
((type "Vector2") (name "v3"))
((type "Color") (name "color"))))
((name "DrawTriangleLinesEx")
(description "Draw triangle outline with line thickness, counter-clockwise vertex order")
(return-type "void")
(params
((type "Vector2") (name "v1"))
((type "Vector2") (name "v2"))
((type "Vector2") (name "v3"))
((type "float") (name "thick"))
((type "Color") (name "color"))))
((name "DrawTriangleFan")
(description "Draw a triangle fan defined by points (first vertex is the center)")
(return-type "void")
@ -3801,6 +3811,18 @@
((type "int") (name "segments"))
((type "Color") (name "color"))))
((name "DrawCircleSectorLinesEx")
(description "Draw circle sector outline with thickness")
(return-type "void")
(params
((type "Vector2") (name "center"))
((type "float") (name "radius"))
((type "float") (name "startAngle"))
((type "float") (name "endAngle"))
((type "int") (name "segments"))
((type "float") (name "thick"))
((type "Color") (name "color"))))
((name "DrawCircleLines")
(description "Draw circle outline")
(return-type "void")
@ -3865,6 +3887,16 @@
((type "float") (name "radiusV"))
((type "Color") (name "color"))))
((name "DrawEllipseLinesEx")
(description "Draw ellipse outline with line thickness")
(return-type "void")
(params
((type "Vector2") (name "center"))
((type "float") (name "radiusH"))
((type "float") (name "radiusV"))
((type "float") (name "thick"))
((type "Color") (name "color"))))
((name "DrawRing")
(description "Draw ring")
(return-type "void")
@ -3889,6 +3921,19 @@
((type "int") (name "segments"))
((type "Color") (name "color"))))
((name "DrawRingLinesEx")
(description "Draw ring outline with line thickness")
(return-type "void")
(params
((type "Vector2") (name "center"))
((type "float") (name "innerRadius"))
((type "float") (name "outerRadius"))
((type "float") (name "startAngle"))
((type "float") (name "endAngle"))
((type "int") (name "segments"))
((type "float") (name "thick"))
((type "Color") (name "color"))))
((name "DrawSplineLinear")
(description "Draw spline: Linear, minimum 2 points")
(return-type "void")

File diff suppressed because it is too large Load Diff

View File

@ -681,7 +681,7 @@
<Param type="unsigned int" name="frames" desc="" />
</Callback>
</Callbacks>
<Functions count="613">
<Functions count="617">
<Function name="InitWindow" retType="void" paramCount="3" desc="Initialize window and OpenGL context">
<Param type="int" name="width" desc="" />
<Param type="int" name="height" desc="" />
@ -1428,6 +1428,13 @@
<Param type="Vector2" name="v3" desc="" />
<Param type="Color" name="color" desc="" />
</Function>
<Function name="DrawTriangleLinesEx" retType="void" paramCount="5" desc="Draw triangle outline with line thickness, counter-clockwise vertex order">
<Param type="Vector2" name="v1" desc="" />
<Param type="Vector2" name="v2" desc="" />
<Param type="Vector2" name="v3" desc="" />
<Param type="float" name="thick" desc="" />
<Param type="Color" name="color" desc="" />
</Function>
<Function name="DrawTriangleFan" retType="void" paramCount="3" desc="Draw a triangle fan defined by points (first vertex is the center)">
<Param type="const Vector2 *" name="points" desc="" />
<Param type="int" name="pointCount" desc="" />
@ -1569,6 +1576,15 @@
<Param type="int" name="segments" desc="" />
<Param type="Color" name="color" desc="" />
</Function>
<Function name="DrawCircleSectorLinesEx" retType="void" paramCount="7" desc="Draw circle sector outline with thickness">
<Param type="Vector2" name="center" desc="" />
<Param type="float" name="radius" desc="" />
<Param type="float" name="startAngle" desc="" />
<Param type="float" name="endAngle" desc="" />
<Param type="int" name="segments" desc="" />
<Param type="float" name="thick" desc="" />
<Param type="Color" name="color" desc="" />
</Function>
<Function name="DrawCircleLines" retType="void" paramCount="4" desc="Draw circle outline">
<Param type="int" name="centerX" desc="" />
<Param type="int" name="centerY" desc="" />
@ -1612,6 +1628,13 @@
<Param type="float" name="radiusV" desc="" />
<Param type="Color" name="color" desc="" />
</Function>
<Function name="DrawEllipseLinesEx" retType="void" paramCount="5" desc="Draw ellipse outline with line thickness">
<Param type="Vector2" name="center" desc="" />
<Param type="float" name="radiusH" desc="" />
<Param type="float" name="radiusV" desc="" />
<Param type="float" name="thick" desc="" />
<Param type="Color" name="color" desc="" />
</Function>
<Function name="DrawRing" retType="void" paramCount="7" desc="Draw ring">
<Param type="Vector2" name="center" desc="" />
<Param type="float" name="innerRadius" desc="" />
@ -1630,6 +1653,16 @@
<Param type="int" name="segments" desc="" />
<Param type="Color" name="color" desc="" />
</Function>
<Function name="DrawRingLinesEx" retType="void" paramCount="8" desc="Draw ring outline with line thickness">
<Param type="Vector2" name="center" desc="" />
<Param type="float" name="innerRadius" desc="" />
<Param type="float" name="outerRadius" desc="" />
<Param type="float" name="startAngle" desc="" />
<Param type="float" name="endAngle" desc="" />
<Param type="int" name="segments" desc="" />
<Param type="float" name="thick" desc="" />
<Param type="Color" name="color" desc="" />
</Function>
<Function name="DrawSplineLinear" retType="void" paramCount="4" desc="Draw spline: Linear, minimum 2 points">
<Param type="const Vector2 *" name="points" desc="" />
<Param type="int" name="pointCount" desc="" />