|  | /******************************************************************************************* | 
						
						
							|  | * | 
						
						
							|  | *   raylib [textures] example - textured curve | 
						
						
							|  | * | 
						
						
							|  | *   Example complexity rating: [★★★☆] 3/4 | 
						
						
							|  | * | 
						
						
							|  | *   Example originally created with raylib 4.5, last time updated with raylib 4.5 | 
						
						
							|  | * | 
						
						
							|  | *   Example contributed by Jeffery Myers (@JeffM2501) 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) 2022-2025 Jeffery Myers (@JeffM2501) and Ramon Santamaria (@raysan5) | 
						
						
							|  | * | 
						
						
							|  | ********************************************************************************************/ | 
						
						
							|  | 
 | 
						
						
							|  | #include "raylib.h" | 
						
						
							|  |  | 
						
						
							|  | #include "raymath.h" | 
						
						
							|  | #include "rlgl.h" | 
						
						
							|  |  | 
						
						
							|  | #include <math.h>           // Required for: powf() | 
						
						
							|  | #include <stdlib.h>         // Required for: NULL | 
						
						
							|  |  | 
						
						
							|  | //---------------------------------------------------------------------------------- | 
						
						
							|  | // Global Variables Definition | 
						
						
							|  | //---------------------------------------------------------------------------------- | 
						
						
							|  | static Texture texRoad = { 0 }; | 
						
						
							|  | 
 | 
						
						
							|  | static bool showCurve = false; | 
						
						
							|  | 
 | 
						
						
							|  | static float curveWidth = 50; | 
						
						
							|  | static int curveSegments = 24; | 
						
						
							|  | 
 | 
						
						
							|  | static Vector2 curveStartPosition = { 0 }; | 
						
						
							|  | static Vector2 curveStartPositionTangent = { 0 }; | 
						
						
							|  | 
 | 
						
						
							|  | static Vector2 curveEndPosition = { 0 }; | 
						
						
							|  | static Vector2 curveEndPositionTangent = { 0 }; | 
						
						
							|  | 
 | 
						
						
							|  | static Vector2 *curveSelectedPoint = NULL; | 
						
						
							|  | 
 | 
						
						
							|  | //---------------------------------------------------------------------------------- | 
						
						
							|  | // Module Functions Declaration | 
						
						
							|  | //---------------------------------------------------------------------------------- | 
						
						
							|  | static void DrawTexturedCurve(void); | 
						
						
							|  | 
 | 
						
						
							|  | //------------------------------------------------------------------------------------ | 
						
						
							|  | // Program main entry point | 
						
						
							|  | //------------------------------------------------------------------------------------ | 
						
						
							|  | int main() | 
						
						
							|  | { | 
						
						
							|  |     // Initialization | 
						
						
							|  |     //-------------------------------------------------------------------------------------- | 
						
						
							|  |     const int screenWidth = 800; | 
						
						
							|  |     const int screenHeight = 450; | 
						
						
							|  | 
 | 
						
						
							|  |     SetConfigFlags(FLAG_VSYNC_HINT | FLAG_MSAA_4X_HINT); | 
						
						
							|  |     InitWindow(screenWidth, screenHeight, "raylib [textures] example - textured curve"); | 
						
						
							|  | 
 | 
						
						
							|  |     // Load the road texture | 
						
						
							|  |     texRoad = LoadTexture("resources/road.png"); | 
						
						
							|  |     SetTextureFilter(texRoad, TEXTURE_FILTER_BILINEAR); | 
						
						
							|  | 
 | 
						
						
							|  |     // Setup the curve | 
						
						
							|  |     curveStartPosition = (Vector2){ 80, 100 }; | 
						
						
							|  |     curveStartPositionTangent = (Vector2){ 100, 300 }; | 
						
						
							|  | 
 | 
						
						
							|  |     curveEndPosition = (Vector2){ 700, 350 }; | 
						
						
							|  |     curveEndPositionTangent = (Vector2){ 600, 100 }; | 
						
						
							|  | 
 | 
						
						
							|  |     SetTargetFPS(60);               // Set our game to run at 60 frames-per-second | 
						
						
							|  |     //-------------------------------------------------------------------------------------- | 
						
						
							|  |  | 
						
						
							|  |     // Main game loop | 
						
						
							|  |     while (!WindowShouldClose())    // Detect window close button or ESC key | 
						
						
							|  |     { | 
						
						
							|  |         // Update | 
						
						
							|  |         //---------------------------------------------------------------------------------- | 
						
						
							|  |         // Curve config options | 
						
						
							|  |         if (IsKeyPressed(KEY_SPACE)) showCurve = !showCurve; | 
						
						
							|  |         if (IsKeyPressed(KEY_EQUAL)) curveWidth += 2; | 
						
						
							|  |         if (IsKeyPressed(KEY_MINUS)) curveWidth -= 2; | 
						
						
							|  |         if (curveWidth < 2) curveWidth = 2; | 
						
						
							|  | 
 | 
						
						
							|  |         // Update segments | 
						
						
							|  |         if (IsKeyPressed(KEY_LEFT)) curveSegments -= 2; | 
						
						
							|  |         if (IsKeyPressed(KEY_RIGHT)) curveSegments += 2; | 
						
						
							|  | 
 | 
						
						
							|  |         if (curveSegments < 2) curveSegments = 2; | 
						
						
							|  | 
 | 
						
						
							|  |         // Update curve logic | 
						
						
							|  |         // If the mouse is not down, we are not editing the curve so clear the selection | 
						
						
							|  |         if (!IsMouseButtonDown(MOUSE_LEFT_BUTTON))  curveSelectedPoint = NULL; | 
						
						
							|  | 
 | 
						
						
							|  |         // If a point was selected, move it | 
						
						
							|  |         if (curveSelectedPoint) *curveSelectedPoint = Vector2Add(*curveSelectedPoint, GetMouseDelta()); | 
						
						
							|  | 
 | 
						
						
							|  |         // The mouse is down, and nothing was selected, so see if anything was picked | 
						
						
							|  |         Vector2 mouse = GetMousePosition(); | 
						
						
							|  |         if (CheckCollisionPointCircle(mouse, curveStartPosition, 6)) curveSelectedPoint = &curveStartPosition; | 
						
						
							|  |         else if (CheckCollisionPointCircle(mouse, curveStartPositionTangent, 6)) curveSelectedPoint = &curveStartPositionTangent; | 
						
						
							|  |         else if (CheckCollisionPointCircle(mouse, curveEndPosition, 6)) curveSelectedPoint = &curveEndPosition; | 
						
						
							|  |         else if (CheckCollisionPointCircle(mouse, curveEndPositionTangent, 6)) curveSelectedPoint = &curveEndPositionTangent; | 
						
						
							|  |         //---------------------------------------------------------------------------------- | 
						
						
							|  |  | 
						
						
							|  |         // Draw | 
						
						
							|  |         //---------------------------------------------------------------------------------- | 
						
						
							|  |         BeginDrawing(); | 
						
						
							|  | 
 | 
						
						
							|  |             ClearBackground(RAYWHITE); | 
						
						
							|  | 
 | 
						
						
							|  |             DrawTexturedCurve();    // Draw a textured Spline Cubic Bezier | 
						
						
							|  |  | 
						
						
							|  |             // Draw spline for reference | 
						
						
							|  |             if (showCurve) DrawSplineSegmentBezierCubic(curveStartPosition, curveEndPosition, curveStartPositionTangent, curveEndPositionTangent, 2, BLUE); | 
						
						
							|  | 
 | 
						
						
							|  |             // Draw the various control points and highlight where the mouse is | 
						
						
							|  |             DrawLineV(curveStartPosition, curveStartPositionTangent, SKYBLUE); | 
						
						
							|  |             DrawLineV(curveStartPositionTangent, curveEndPositionTangent, Fade(LIGHTGRAY, 0.4f)); | 
						
						
							|  |             DrawLineV(curveEndPosition, curveEndPositionTangent, PURPLE); | 
						
						
							|  | 
 | 
						
						
							|  |             if (CheckCollisionPointCircle(mouse, curveStartPosition, 6)) DrawCircleV(curveStartPosition, 7, YELLOW); | 
						
						
							|  |             DrawCircleV(curveStartPosition, 5, RED); | 
						
						
							|  | 
 | 
						
						
							|  |             if (CheckCollisionPointCircle(mouse, curveStartPositionTangent, 6)) DrawCircleV(curveStartPositionTangent, 7, YELLOW); | 
						
						
							|  |             DrawCircleV(curveStartPositionTangent, 5, MAROON); | 
						
						
							|  | 
 | 
						
						
							|  |             if (CheckCollisionPointCircle(mouse, curveEndPosition, 6)) DrawCircleV(curveEndPosition, 7, YELLOW); | 
						
						
							|  |             DrawCircleV(curveEndPosition, 5, GREEN); | 
						
						
							|  | 
 | 
						
						
							|  |             if (CheckCollisionPointCircle(mouse, curveEndPositionTangent, 6)) DrawCircleV(curveEndPositionTangent, 7, YELLOW); | 
						
						
							|  |             DrawCircleV(curveEndPositionTangent, 5, DARKGREEN); | 
						
						
							|  | 
 | 
						
						
							|  |             // Draw usage info | 
						
						
							|  |             DrawText("Drag points to move curve, press SPACE to show/hide base curve", 10, 10, 10, DARKGRAY); | 
						
						
							|  |             DrawText(TextFormat("Curve width: %2.0f (Use + and - to adjust)", curveWidth), 10, 30, 10, DARKGRAY); | 
						
						
							|  |             DrawText(TextFormat("Curve segments: %d (Use LEFT and RIGHT to adjust)", curveSegments), 10, 50, 10, DARKGRAY); | 
						
						
							|  | 
 | 
						
						
							|  |         EndDrawing(); | 
						
						
							|  |         //---------------------------------------------------------------------------------- | 
						
						
							|  |     } | 
						
						
							|  | 
 | 
						
						
							|  |     // De-Initialization | 
						
						
							|  |     //-------------------------------------------------------------------------------------- | 
						
						
							|  |     UnloadTexture(texRoad); | 
						
						
							|  | 
 | 
						
						
							|  |     CloseWindow();              // Close window and OpenGL context | 
						
						
							|  |     //-------------------------------------------------------------------------------------- | 
						
						
							|  |  | 
						
						
							|  |     return 0; | 
						
						
							|  | } | 
						
						
							|  | 
 | 
						
						
							|  | //---------------------------------------------------------------------------------- | 
						
						
							|  | // Module Functions Definition | 
						
						
							|  | //---------------------------------------------------------------------------------- | 
						
						
							|  | // Draw textured curve using Spline Cubic Bezier | 
						
						
							|  | static void DrawTexturedCurve(void) | 
						
						
							|  | { | 
						
						
							|  |     const float step = 1.0f/curveSegments; | 
						
						
							|  | 
 | 
						
						
							|  |     Vector2 previous = curveStartPosition; | 
						
						
							|  |     Vector2 previousTangent = { 0 }; | 
						
						
							|  |     float previousV = 0; | 
						
						
							|  | 
 | 
						
						
							|  |     // We can't compute a tangent for the first point, so we need to reuse the tangent from the first segment | 
						
						
							|  |     bool tangentSet = false; | 
						
						
							|  | 
 | 
						
						
							|  |     Vector2 current = { 0 }; | 
						
						
							|  |     float t = 0.0f; | 
						
						
							|  | 
 | 
						
						
							|  |     for (int i = 1; i <= curveSegments; i++) | 
						
						
							|  |     { | 
						
						
							|  |         t = step*(float)i; | 
						
						
							|  | 
 | 
						
						
							|  |         float a = powf(1.0f - t, 3); | 
						
						
							|  |         float b = 3.0f*powf(1.0f - t, 2)*t; | 
						
						
							|  |         float c = 3.0f*(1.0f - t)*powf(t, 2); | 
						
						
							|  |         float d = powf(t, 3); | 
						
						
							|  | 
 | 
						
						
							|  |         // Compute the endpoint for this segment | 
						
						
							|  |         current.y = a*curveStartPosition.y + b*curveStartPositionTangent.y + c*curveEndPositionTangent.y + d*curveEndPosition.y; | 
						
						
							|  |         current.x = a*curveStartPosition.x + b*curveStartPositionTangent.x + c*curveEndPositionTangent.x + d*curveEndPosition.x; | 
						
						
							|  | 
 | 
						
						
							|  |         // Vector from previous to current | 
						
						
							|  |         Vector2 delta = { current.x - previous.x, current.y - previous.y }; | 
						
						
							|  | 
 | 
						
						
							|  |         // The right hand normal to the delta vector | 
						
						
							|  |         Vector2 normal = Vector2Normalize((Vector2){ -delta.y, delta.x }); | 
						
						
							|  | 
 | 
						
						
							|  |         // The v texture coordinate of the segment (add up the length of all the segments so far) | 
						
						
							|  |         float v = previousV + Vector2Length(delta); | 
						
						
							|  | 
 | 
						
						
							|  |         // Make sure the start point has a normal | 
						
						
							|  |         if (!tangentSet) | 
						
						
							|  |         { | 
						
						
							|  |             previousTangent = normal; | 
						
						
							|  |             tangentSet = true; | 
						
						
							|  |         } | 
						
						
							|  | 
 | 
						
						
							|  |         // Extend out the normals from the previous and current points to get the quad for this segment | 
						
						
							|  |         Vector2 prevPosNormal = Vector2Add(previous, Vector2Scale(previousTangent, curveWidth)); | 
						
						
							|  |         Vector2 prevNegNormal = Vector2Add(previous, Vector2Scale(previousTangent, -curveWidth)); | 
						
						
							|  | 
 | 
						
						
							|  |         Vector2 currentPosNormal = Vector2Add(current, Vector2Scale(normal, curveWidth)); | 
						
						
							|  |         Vector2 currentNegNormal = Vector2Add(current, Vector2Scale(normal, -curveWidth)); | 
						
						
							|  | 
 | 
						
						
							|  |         // Draw the segment as a quad | 
						
						
							|  |         rlSetTexture(texRoad.id); | 
						
						
							|  |         rlBegin(RL_QUADS); | 
						
						
							|  |             rlColor4ub(255,255,255,255); | 
						
						
							|  |             rlNormal3f(0.0f, 0.0f, 1.0f); | 
						
						
							|  | 
 | 
						
						
							|  |             rlTexCoord2f(0, previousV); | 
						
						
							|  |             rlVertex2f(prevNegNormal.x, prevNegNormal.y); | 
						
						
							|  | 
 | 
						
						
							|  |             rlTexCoord2f(1, previousV); | 
						
						
							|  |             rlVertex2f(prevPosNormal.x, prevPosNormal.y); | 
						
						
							|  | 
 | 
						
						
							|  |             rlTexCoord2f(1, v); | 
						
						
							|  |             rlVertex2f(currentPosNormal.x, currentPosNormal.y); | 
						
						
							|  | 
 | 
						
						
							|  |             rlTexCoord2f(0, v); | 
						
						
							|  |             rlVertex2f(currentNegNormal.x, currentNegNormal.y); | 
						
						
							|  |         rlEnd(); | 
						
						
							|  | 
 | 
						
						
							|  |         // The current step is the start of the next step | 
						
						
							|  |         previous = current; | 
						
						
							|  |         previousTangent = normal; | 
						
						
							|  |         previousV = v; | 
						
						
							|  |     } | 
						
						
							|  | } | 
						
						
							|  | 
 |