mirror of
https://github.com/raysan5/raylib.git
synced 2026-08-25 13:48:27 -04:00
Compare commits
28 Commits
b98c4e031d
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 0fb5c98293 | |||
| e4ccc91d4e | |||
| f3a471fd89 | |||
| 820b971eff | |||
| 5f58fbe6d7 | |||
| 5c5b357b4a | |||
| bcc834fc3e | |||
| 5b818bb318 | |||
| e1efefb7f4 | |||
| 2c0021ad89 | |||
| 9bde204f1d | |||
| 47231e37ef | |||
| 0088b85952 | |||
| 338458a92b | |||
| 3836f13878 | |||
| abe23bf82d | |||
| 6cceb2b873 | |||
| 86430c156c | |||
| 3b57140640 | |||
| f472c67bb2 | |||
| 23033d1f18 | |||
| d7225acd8e | |||
| af3045377a | |||
| f9e2215c49 | |||
| d75a6589f0 | |||
| 47c8e897ce | |||
| 1129d42b48 | |||
| c58072d595 |
@ -36,6 +36,7 @@ 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-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-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 |
|
| [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 |
|
| [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-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 |
|
| [raylib.f](https://github.com/cthulhuology/raylib.f) | **5.5** | [Forth](https://forth.com) | Zlib |
|
||||||
@ -59,6 +60,8 @@ 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](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-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-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 |
|
| [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.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 |
|
| [raylib-bindings](https://github.com/vaiorabbit/raylib-bindings) | 5.6-dev | [Ruby](https://www.ruby-lang.org/en) | Zlib |
|
||||||
@ -69,7 +72,7 @@ Some people ported raylib to other languages in the form of bindings or wrappers
|
|||||||
| [raylib-ocaml](https://github.com/tjammer/raylib-ocaml) | **6.0** | [OCaml](https://ocaml.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 |
|
| [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) | MIT |
|
| [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 |
|
| [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 |
|
| [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 |
|
| [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 |
|
| [pyraylib](https://github.com/Ho011/pyraylib) | 3.7 | [Python](https://www.python.org) | Zlib |
|
||||||
@ -95,7 +98,7 @@ Some people ported raylib to other languages in the form of bindings or wrappers
|
|||||||
| [raylib-v](https://github.com/vlang/raylib) | 5.5 | [V](https://vlang.io) | MIT/Unlicense |
|
| [raylib-v](https://github.com/vlang/raylib) | 5.5 | [V](https://vlang.io) | MIT/Unlicense |
|
||||||
| [raylib.v](https://github.com/irishgreencitrus/raylib.v) | 4.2 | [V](https://vlang.io) | Zlib |
|
| [raylib.v](https://github.com/irishgreencitrus/raylib.v) | 4.2 | [V](https://vlang.io) | Zlib |
|
||||||
| [raylib-vapi](https://github.com/lxmcf/raylib-vapi) | **6.0** | [Vala](https://vala.dev) | Zlib |
|
| [raylib-vapi](https://github.com/lxmcf/raylib-vapi) | **6.0** | [Vala](https://vala.dev) | Zlib |
|
||||||
| [raylib-wave](https://github.com/wavefnd/raylib-wave) | **auto** |[Wave](http://wave-lang.dev) | Zlib |
|
| [raylib-wave](https://github.com/wavefnd/raylib-wave) | **auto** | [Wave](http://wave-lang.dev) | Zlib |
|
||||||
| [raylib-wren](https://github.com/TSnake41/raylib-wren) | 4.5 | [Wren](http://wren.io) | ISC |
|
| [raylib-wren](https://github.com/TSnake41/raylib-wren) | 4.5 | [Wren](http://wren.io) | ISC |
|
||||||
| [raylib-zig](https://github.com/raylib-zig/raylib-zig) | **6.0** | [Zig](https://ziglang.org) | MIT |
|
| [raylib-zig](https://github.com/raylib-zig/raylib-zig) | **6.0** | [Zig](https://ziglang.org) | MIT |
|
||||||
| [raylib.zig](https://github.com/ryupold/raylib.zig) | **5.1-dev** | [Zig](https://ziglang.org) | MIT |
|
| [raylib.zig](https://github.com/ryupold/raylib.zig) | **5.1-dev** | [Zig](https://ziglang.org) | MIT |
|
||||||
|
|||||||
11
README.md
11
README.md
@ -54,6 +54,17 @@ features
|
|||||||
- Bindings to [+70 programming languages](https://github.com/raysan5/raylib/blob/master/BINDINGS.md)!
|
- Bindings to [+70 programming languages](https://github.com/raysan5/raylib/blob/master/BINDINGS.md)!
|
||||||
- **Free and open source**
|
- **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
|
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).
|
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).
|
||||||
|
|||||||
@ -1,6 +1,18 @@
|
|||||||
# Setup the project and settings
|
# Setup the project and settings
|
||||||
|
cmake_minimum_required(VERSION 3.22)
|
||||||
project(examples)
|
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
|
# Directories that contain examples
|
||||||
set(example_dirs
|
set(example_dirs
|
||||||
audio
|
audio
|
||||||
|
|||||||
@ -18,7 +18,7 @@
|
|||||||
# > PLATFORM_DESKTOP_RGFW (RGFW backend):
|
# > PLATFORM_DESKTOP_RGFW (RGFW backend):
|
||||||
# - Windows (Win32, Win64)
|
# - Windows (Win32, Win64)
|
||||||
# - Linux (X11 desktop mode)
|
# - Linux (X11 desktop mode)
|
||||||
# - macOS/OSX (x64, arm64 (not tested))
|
# - macOS/OSX (x64, arm64)
|
||||||
# - Others (not tested)
|
# - Others (not tested)
|
||||||
# > PLATFORM_DESKTOP_WIN32 (native Win32):
|
# > PLATFORM_DESKTOP_WIN32 (native Win32):
|
||||||
# - Windows (Win32, Win64)
|
# - Windows (Win32, Win64)
|
||||||
@ -314,6 +314,9 @@ ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_GLFW)
|
|||||||
ifeq ($(PLATFORM_OS),BSD)
|
ifeq ($(PLATFORM_OS),BSD)
|
||||||
LDFLAGS += -Lsrc -L$(RAYLIB_LIB_PATH)
|
LDFLAGS += -Lsrc -L$(RAYLIB_LIB_PATH)
|
||||||
endif
|
endif
|
||||||
|
ifeq ($(PLATFORM_OS),OSX)
|
||||||
|
LDFLAGS += -L/opt/homebrew/lib -L$(RAYLIB_LIB_PATH)
|
||||||
|
endif
|
||||||
endif
|
endif
|
||||||
ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_SDL)
|
ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_SDL)
|
||||||
ifeq ($(PLATFORM_OS),WINDOWS)
|
ifeq ($(PLATFORM_OS),WINDOWS)
|
||||||
|
|||||||
323
examples/models/models_mesh_uv_painting.c
Normal file
323
examples/models/models_mesh_uv_painting.c
Normal 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, ¤tShape, 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, ¤tShape, SHAPE_SPHERE, canvasTexture);
|
||||||
|
if (GuiToggle((Rectangle){ 125, 120, 95, 28 }, "CUBE", &cubeActive))
|
||||||
|
ChangeShape(&model, &modelBBox, ¤tShape, SHAPE_CUBE, canvasTexture);
|
||||||
|
if (GuiToggle((Rectangle){ 25, 153, 95, 28 }, "CYLINDER", &cylinderActive))
|
||||||
|
ChangeShape(&model, &modelBBox, ¤tShape, SHAPE_CYLINDER, canvasTexture);
|
||||||
|
if (GuiToggle((Rectangle){ 125, 153, 95, 28 }, "TORUS", &torusActive))
|
||||||
|
ChangeShape(&model, &modelBBox, ¤tShape, 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;
|
||||||
|
}
|
||||||
BIN
examples/models/models_mesh_uv_painting.png
Normal file
BIN
examples/models/models_mesh_uv_painting.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 31 KiB |
504
examples/models/models_procedural_decals.c
Normal file
504
examples/models/models_procedural_decals.c
Normal 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;
|
||||||
|
}
|
||||||
BIN
examples/models/models_procedural_decals.png
Normal file
BIN
examples/models/models_procedural_decals.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 48 KiB |
44
examples/shaders/resources/shaders/glsl100/lights_bloom.fs
Normal file
44
examples/shaders/resources/shaders/glsl100/lights_bloom.fs
Normal 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);
|
||||||
|
}
|
||||||
23
examples/shaders/resources/shaders/glsl100/lights_bloom.vs
Normal file
23
examples/shaders/resources/shaders/glsl100/lights_bloom.vs
Normal 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);
|
||||||
|
}
|
||||||
@ -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);
|
||||||
|
}
|
||||||
44
examples/shaders/resources/shaders/glsl330/lights_bloom.fs
Normal file
44
examples/shaders/resources/shaders/glsl330/lights_bloom.fs
Normal 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);
|
||||||
|
}
|
||||||
23
examples/shaders/resources/shaders/glsl330/lights_bloom.vs
Normal file
23
examples/shaders/resources/shaders/glsl330/lights_bloom.vs
Normal 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);
|
||||||
|
}
|
||||||
@ -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);
|
||||||
|
}
|
||||||
185
examples/shaders/shaders_lights_bloom.c
Normal file
185
examples/shaders/shaders_lights_bloom.c
Normal 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;
|
||||||
|
}
|
||||||
BIN
examples/shaders/shaders_lights_bloom.png
Normal file
BIN
examples/shaders/shaders_lights_bloom.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 203 KiB |
407
examples/shapes/shapes_outlines_testbed.c
Normal file
407
examples/shapes/shapes_outlines_testbed.c
Normal 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;
|
||||||
|
}
|
||||||
BIN
examples/shapes/shapes_outlines_testbed.png
Normal file
BIN
examples/shapes/shapes_outlines_testbed.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 24 KiB |
242
examples/textures/textures_portal_window.c
Normal file
242
examples/textures/textures_portal_window.c
Normal 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
|
||||||
|
}
|
||||||
BIN
examples/textures/textures_portal_window.png
Normal file
BIN
examples/textures/textures_portal_window.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 26 KiB |
@ -114,6 +114,20 @@ if (NOT BUILD_SHARED_LIBS)
|
|||||||
|
|
||||||
if (NOT glfw3_FOUND)
|
if (NOT glfw3_FOUND)
|
||||||
list(REMOVE_ITEM raylib_install_private_libs glfw)
|
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()
|
endif()
|
||||||
|
|
||||||
foreach(lib IN LISTS raylib_install_private_libs)
|
foreach(lib IN LISTS raylib_install_private_libs)
|
||||||
|
|||||||
1
src/external/fix_win32_compatibility.h
vendored
1
src/external/fix_win32_compatibility.h
vendored
@ -46,6 +46,7 @@
|
|||||||
// include windows
|
// include windows
|
||||||
#define WIN32_LEAN_AND_MEAN
|
#define WIN32_LEAN_AND_MEAN
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
#include <playsoundapi.h>
|
||||||
|
|
||||||
// remove all our redefinitions so that raylib can define them properly
|
// remove all our redefinitions so that raylib can define them properly
|
||||||
#undef CloseWindow
|
#undef CloseWindow
|
||||||
|
|||||||
@ -287,8 +287,14 @@ void ToggleBorderlessWindowed(void)
|
|||||||
CORE.Window.screen.height = mode->height;
|
CORE.Window.screen.height = mode->height;
|
||||||
|
|
||||||
// Set screen position and size
|
// 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,
|
glfwSetWindowMonitor(platform.handle, monitors[monitor], CORE.Window.position.x, CORE.Window.position.y,
|
||||||
CORE.Window.screen.width, CORE.Window.screen.height, mode->refreshRate);
|
CORE.Window.screen.width, CORE.Window.screen.height, mode->refreshRate);
|
||||||
|
#endif
|
||||||
|
|
||||||
// Refocus window
|
// Refocus window
|
||||||
glfwFocusWindow(platform.handle);
|
glfwFocusWindow(platform.handle);
|
||||||
@ -1024,6 +1030,8 @@ const char *GetMonitorName(int monitor)
|
|||||||
// Get window position XY on monitor
|
// Get window position XY on monitor
|
||||||
Vector2 GetWindowPosition(void)
|
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 };
|
return (Vector2){ (float)CORE.Window.position.x, (float)CORE.Window.position.y };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1129,12 +1129,37 @@ const char *GetClipboardText(void)
|
|||||||
return NULL;
|
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
|
// Get clipboard image
|
||||||
Image GetClipboardImage(void)
|
Image GetClipboardImage(void)
|
||||||
{
|
{
|
||||||
Image image = { 0 };
|
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;
|
return image;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1723,32 +1723,18 @@ static EM_BOOL EmscriptenMouseMoveCallback(int eventType, const EmscriptenMouseE
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Get mouse position in canvas CSS pixels
|
// Get mouse position in canvas CSS pixels
|
||||||
float mouseCssX = (float)mouseEvent->canvasX;
|
float mouseCssX = (float)mouseEvent->targetX;
|
||||||
float mouseCssY = (float)mouseEvent->canvasY;
|
float mouseCssY = (float)mouseEvent->targetY;
|
||||||
|
|
||||||
// Get canvas sizes
|
// Get canvas sizes
|
||||||
double cssWidth = 0.0;
|
double cssWidth = 0.0;
|
||||||
double cssHeight = 0.0;
|
double cssHeight = 0.0;
|
||||||
emscripten_get_element_css_size(platform.canvasId, &cssWidth, &cssHeight);
|
emscripten_get_element_css_size(platform.canvasId, &cssWidth, &cssHeight);
|
||||||
|
|
||||||
int fbWidth = 0;
|
float normalizedX = mouseCssX/(float)cssWidth;
|
||||||
int fbHeight = 0;
|
float normalizedY = mouseCssY/(float)cssHeight;
|
||||||
emscripten_get_canvas_element_size(platform.canvasId, &fbWidth, &fbHeight);
|
CORE.Input.Mouse.currentPosition.x = normalizedX*(float)CORE.Window.screen.width;
|
||||||
|
CORE.Input.Mouse.currentPosition.y = normalizedY*(float)CORE.Window.screen.height;
|
||||||
// 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);
|
|
||||||
|
|
||||||
CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition;
|
CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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 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 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 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 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 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
|
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 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 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 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 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 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
|
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 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 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 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 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 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
|
// Splines drawing functions
|
||||||
RLAPI void DrawSplineLinear(const Vector2 *points, int pointCount, float thick, Color color); // Draw spline: Linear, minimum 2 points
|
RLAPI void DrawSplineLinear(const Vector2 *points, int pointCount, float thick, Color color); // Draw spline: Linear, minimum 2 points
|
||||||
|
|||||||
20
src/rcore.c
20
src/rcore.c
@ -2583,20 +2583,28 @@ const char *GetPrevDirectoryPath(const char *dirPath)
|
|||||||
{
|
{
|
||||||
static char prevDirPath[MAX_FILEPATH_LENGTH] = { 0 };
|
static char prevDirPath[MAX_FILEPATH_LENGTH] = { 0 };
|
||||||
memset(prevDirPath, 0, MAX_FILEPATH_LENGTH);
|
memset(prevDirPath, 0, MAX_FILEPATH_LENGTH);
|
||||||
int dirPathLength = (int)strlen(dirPath);
|
|
||||||
|
|
||||||
if (dirPathLength <= 3) snprintf(prevDirPath, MAX_FILEPATH_LENGTH, "%s", dirPath);
|
const int lastIndex = (int)strlen(dirPath) - 1;
|
||||||
|
bool isFile = IsPathFile(dirPath);
|
||||||
for (int i = (dirPathLength - 1); (i >= 0) && (dirPathLength > 3); i--)
|
for (int i = lastIndex; i >= 0; i--)
|
||||||
{
|
{
|
||||||
|
// If the character is a path separator.
|
||||||
if ((dirPath[i] == '\\') || (dirPath[i] == '/'))
|
if ((dirPath[i] == '\\') || (dirPath[i] == '/'))
|
||||||
{
|
{
|
||||||
// Check for root: "C:\" or "/"
|
// If this character is a leading '/' (e.g. the '/' in "/usr") or
|
||||||
if (((i == 2) && (dirPath[1] ==':')) || (i == 0)) i++;
|
// 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);
|
memcpy(prevDirPath, dirPath, i);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
else isFile = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return prevDirPath;
|
return prevDirPath;
|
||||||
|
|||||||
1842
src/rshapes.c
1842
src/rshapes.c
File diff suppressed because it is too large
Load Diff
@ -588,7 +588,7 @@ int main(int argc, char *argv[])
|
|||||||
else if (TextIsEqual(exCategory, "text")) nextCategoryIndex = 4;
|
else if (TextIsEqual(exCategory, "text")) nextCategoryIndex = 4;
|
||||||
else if (TextIsEqual(exCategory, "models")) nextCategoryIndex = 5;
|
else if (TextIsEqual(exCategory, "models")) nextCategoryIndex = 5;
|
||||||
else if (TextIsEqual(exCategory, "shaders")) nextCategoryIndex = 6;
|
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)
|
// Get required example info from example file header (if provided)
|
||||||
|
|
||||||
@ -621,7 +621,13 @@ int main(int argc, char *argv[])
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Add example to collection, at the end of the category list
|
// 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);
|
memcpy(exCollectionListUpdated, exCollectionList, categoryIndex);
|
||||||
int textWritenSize = sprintf(exCollectionListUpdated + categoryIndex, TextFormat("%s;%s;%s;%s;%s;%i;%i;\"%s\";@%s\n",
|
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));
|
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
|
// Find position to add new example on list, just before the following category
|
||||||
// Category order: core, shapes, textures, text, models, shaders, audio
|
// 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;
|
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
|
else exListNextCatIndex = exListLen; // EOF
|
||||||
|
|
||||||
strncpy(exListUpdated, exList, exListNextCatIndex);
|
strncpy(exListUpdated, exList, exListNextCatIndex);
|
||||||
|
|||||||
@ -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",
|
"name": "DrawTriangleFan",
|
||||||
"description": "Draw a triangle fan defined by points (first vertex is the center)",
|
"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",
|
"name": "DrawCircleLines",
|
||||||
"description": "Draw circle outline",
|
"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",
|
"name": "DrawRing",
|
||||||
"description": "Draw ring",
|
"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",
|
"name": "DrawSplineLinear",
|
||||||
"description": "Draw spline: Linear, minimum 2 points",
|
"description": "Draw spline: Linear, minimum 2 points",
|
||||||
|
|||||||
@ -4895,6 +4895,18 @@ return {
|
|||||||
{type = "Color", name = "color"}
|
{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",
|
name = "DrawTriangleFan",
|
||||||
description = "Draw a triangle fan defined by points (first vertex is the center)",
|
description = "Draw a triangle fan defined by points (first vertex is the center)",
|
||||||
@ -5146,6 +5158,20 @@ return {
|
|||||||
{type = "Color", name = "color"}
|
{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",
|
name = "DrawCircleLines",
|
||||||
description = "Draw circle outline",
|
description = "Draw circle outline",
|
||||||
@ -5224,6 +5250,18 @@ return {
|
|||||||
{type = "Color", name = "color"}
|
{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",
|
name = "DrawRing",
|
||||||
description = "Draw ring",
|
description = "Draw ring",
|
||||||
@ -5252,6 +5290,21 @@ return {
|
|||||||
{type = "Color", name = "color"}
|
{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",
|
name = "DrawSplineLinear",
|
||||||
description = "Draw spline: Linear, minimum 2 points",
|
description = "Draw spline: Linear, minimum 2 points",
|
||||||
|
|||||||
@ -3594,6 +3594,16 @@
|
|||||||
((type "Vector2") (name "v3"))
|
((type "Vector2") (name "v3"))
|
||||||
((type "Color") (name "color"))))
|
((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")
|
((name "DrawTriangleFan")
|
||||||
(description "Draw a triangle fan defined by points (first vertex is the center)")
|
(description "Draw a triangle fan defined by points (first vertex is the center)")
|
||||||
(return-type "void")
|
(return-type "void")
|
||||||
@ -3801,6 +3811,18 @@
|
|||||||
((type "int") (name "segments"))
|
((type "int") (name "segments"))
|
||||||
((type "Color") (name "color"))))
|
((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")
|
((name "DrawCircleLines")
|
||||||
(description "Draw circle outline")
|
(description "Draw circle outline")
|
||||||
(return-type "void")
|
(return-type "void")
|
||||||
@ -3865,6 +3887,16 @@
|
|||||||
((type "float") (name "radiusV"))
|
((type "float") (name "radiusV"))
|
||||||
((type "Color") (name "color"))))
|
((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")
|
((name "DrawRing")
|
||||||
(description "Draw ring")
|
(description "Draw ring")
|
||||||
(return-type "void")
|
(return-type "void")
|
||||||
@ -3889,6 +3921,19 @@
|
|||||||
((type "int") (name "segments"))
|
((type "int") (name "segments"))
|
||||||
((type "Color") (name "color"))))
|
((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")
|
((name "DrawSplineLinear")
|
||||||
(description "Draw spline: Linear, minimum 2 points")
|
(description "Draw spline: Linear, minimum 2 points")
|
||||||
(return-type "void")
|
(return-type "void")
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -681,7 +681,7 @@
|
|||||||
<Param type="unsigned int" name="frames" desc="" />
|
<Param type="unsigned int" name="frames" desc="" />
|
||||||
</Callback>
|
</Callback>
|
||||||
</Callbacks>
|
</Callbacks>
|
||||||
<Functions count="613">
|
<Functions count="617">
|
||||||
<Function name="InitWindow" retType="void" paramCount="3" desc="Initialize window and OpenGL context">
|
<Function name="InitWindow" retType="void" paramCount="3" desc="Initialize window and OpenGL context">
|
||||||
<Param type="int" name="width" desc="" />
|
<Param type="int" name="width" desc="" />
|
||||||
<Param type="int" name="height" desc="" />
|
<Param type="int" name="height" desc="" />
|
||||||
@ -1428,6 +1428,13 @@
|
|||||||
<Param type="Vector2" name="v3" desc="" />
|
<Param type="Vector2" name="v3" desc="" />
|
||||||
<Param type="Color" name="color" desc="" />
|
<Param type="Color" name="color" desc="" />
|
||||||
</Function>
|
</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)">
|
<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="const Vector2 *" name="points" desc="" />
|
||||||
<Param type="int" name="pointCount" desc="" />
|
<Param type="int" name="pointCount" desc="" />
|
||||||
@ -1569,6 +1576,15 @@
|
|||||||
<Param type="int" name="segments" desc="" />
|
<Param type="int" name="segments" desc="" />
|
||||||
<Param type="Color" name="color" desc="" />
|
<Param type="Color" name="color" desc="" />
|
||||||
</Function>
|
</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">
|
<Function name="DrawCircleLines" retType="void" paramCount="4" desc="Draw circle outline">
|
||||||
<Param type="int" name="centerX" desc="" />
|
<Param type="int" name="centerX" desc="" />
|
||||||
<Param type="int" name="centerY" desc="" />
|
<Param type="int" name="centerY" desc="" />
|
||||||
@ -1612,6 +1628,13 @@
|
|||||||
<Param type="float" name="radiusV" desc="" />
|
<Param type="float" name="radiusV" desc="" />
|
||||||
<Param type="Color" name="color" desc="" />
|
<Param type="Color" name="color" desc="" />
|
||||||
</Function>
|
</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">
|
<Function name="DrawRing" retType="void" paramCount="7" desc="Draw ring">
|
||||||
<Param type="Vector2" name="center" desc="" />
|
<Param type="Vector2" name="center" desc="" />
|
||||||
<Param type="float" name="innerRadius" desc="" />
|
<Param type="float" name="innerRadius" desc="" />
|
||||||
@ -1630,6 +1653,16 @@
|
|||||||
<Param type="int" name="segments" desc="" />
|
<Param type="int" name="segments" desc="" />
|
||||||
<Param type="Color" name="color" desc="" />
|
<Param type="Color" name="color" desc="" />
|
||||||
</Function>
|
</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">
|
<Function name="DrawSplineLinear" retType="void" paramCount="4" desc="Draw spline: Linear, minimum 2 points">
|
||||||
<Param type="const Vector2 *" name="points" desc="" />
|
<Param type="const Vector2 *" name="points" desc="" />
|
||||||
<Param type="int" name="pointCount" desc="" />
|
<Param type="int" name="pointCount" desc="" />
|
||||||
|
|||||||
Reference in New Issue
Block a user