From bcc834fc3e8a430bce8b15500d6419b2003acfb4 Mon Sep 17 00:00:00 2001 From: PanicTitan <66186142+PanicTitan@users.noreply.github.com> Date: Tue, 25 Aug 2026 14:15:43 -0300 Subject: [PATCH] [examples] Add models_mesh_uv_painting (#6084) * [examples] Add models_mesh_uv_painting Example showing paintable models textures * Fixed window size and use the raygui header for the buttons --- examples/models/models_mesh_uv_painting.c | 320 ++++++++++++++++++++ examples/models/models_mesh_uv_painting.png | Bin 0 -> 32089 bytes 2 files changed, 320 insertions(+) create mode 100644 examples/models/models_mesh_uv_painting.c create mode 100644 examples/models/models_mesh_uv_painting.png diff --git a/examples/models/models_mesh_uv_painting.c b/examples/models/models_mesh_uv_painting.c new file mode 100644 index 000000000..17b3199d6 --- /dev/null +++ b/examples/models/models_mesh_uv_painting.c @@ -0,0 +1,320 @@ +/******************************************************************************************* +* +* 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" +#define RAYGUI_IMPLEMENTATION +#include "raygui.h" +#include "raymath.h" +#include // Required for: fabsf(), floorf() +#include // 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, 18, 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, 12, 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, 12, LIGHTGRAY); + DrawRectangle(125, 193, 95, 20, activeColor); + DrawRectangleLines(125, 193, 95, 20, WHITE); + + // Color swatches + DrawText("Palette Swatches:", 25, 225, 12, 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, 12, 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, 12, 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; +} diff --git a/examples/models/models_mesh_uv_painting.png b/examples/models/models_mesh_uv_painting.png new file mode 100644 index 0000000000000000000000000000000000000000..0294239d1b969dd76320622087f4890974b39991 GIT binary patch literal 32089 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