mirror of
https://github.com/raysan5/raylib.git
synced 2026-08-25 13:48:27 -04:00
Compare commits
11 Commits
47231e37ef
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 0fb5c98293 | |||
| e4ccc91d4e | |||
| f3a471fd89 | |||
| 820b971eff | |||
| 5f58fbe6d7 | |||
| 5c5b357b4a | |||
| bcc834fc3e | |||
| 5b818bb318 | |||
| e1efefb7f4 | |||
| 2c0021ad89 | |||
| 9bde204f1d |
13
BINDINGS.md
13
BINDINGS.md
@ -36,7 +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 |
|
| [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 |
|
||||||
@ -60,7 +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) | **???** |
|
||||||
| [raymojo](https://github.com/RobertFlexx/raymojo) | **6.0** | [Mojo](https://www.modular.com/mojo) | GPLv3 |
|
| [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 |
|
||||||
@ -71,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 |
|
||||||
@ -97,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 |
|
||||||
@ -113,8 +114,8 @@ Some people ported raylib to other languages in the form of bindings or wrappers
|
|||||||
| [raylib-apl](https://github.com/Brian-ED/raylib-apl) | **5.0** | [Dyalog APL](https://www.dyalog.com/) | MIT |
|
| [raylib-apl](https://github.com/Brian-ED/raylib-apl) | **5.0** | [Dyalog APL](https://www.dyalog.com/) | MIT |
|
||||||
| [raylib-jai](https://github.com/ahmedqarmout2/raylib-jai) | **6.0** | [Jai](https://github.com/BSVino/JaiPrimer/blob/master/JaiPrimer.md) | MIT |
|
| [raylib-jai](https://github.com/ahmedqarmout2/raylib-jai) | **6.0** | [Jai](https://github.com/BSVino/JaiPrimer/blob/master/JaiPrimer.md) | MIT |
|
||||||
| [fnl-raylib](https://github.com/0riginaln0/fnl-raylib) | **5.5** | [Fennel](https://fennel-lang.org/) | MIT |
|
| [fnl-raylib](https://github.com/0riginaln0/fnl-raylib) | **5.5** | [Fennel](https://fennel-lang.org/) | MIT |
|
||||||
| [Rayua](https://github.com/uiua-lang/rayua) | **5.5** | [Uiua](https://www.uiua.org/) | **???** |
|
| [Rayua](https://github.com/uiua-lang/rayua) | **5.5** | [Uiua](https://www.uiua.org/) | **???** |
|
||||||
| [Target](https://github.com/FinnDemonCat/Target/tree/main/libs/raylib) | **5.5** | [Dart](https://dart.dev/) | Apache-2.0 license |
|
| [Target](https://github.com/FinnDemonCat/Target/tree/main/libs/raylib) | **5.5** | [Dart](https://dart.dev/) | Apache-2.0 license |
|
||||||
| [gclang-raylib](https://github.com/gnuchanos/gcLang_Compiler/tree/main/windows_version/raylib_version)| **6.0** | [gclang](https://github.com/gnuchanos/gcLang_Compiler) | AGPL-3.0 |
|
| [gclang-raylib](https://github.com/gnuchanos/gcLang_Compiler/tree/main/windows_version/raylib_version)| **6.0** | [gclang](https://github.com/gnuchanos/gcLang_Compiler) | AGPL-3.0 |
|
||||||
| [mach-raylib](https://github.com/angluca/mach-raylib) | **6.0-dev** | [Mach](https://machlang.org/) | MIT |
|
| [mach-raylib](https://github.com/angluca/mach-raylib) | **6.0-dev** | [Mach](https://machlang.org/) | MIT |
|
||||||
|
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
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 |
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)
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
1842
src/rshapes.c
1842
src/rshapes.c
File diff suppressed because it is too large
Load Diff
@ -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