mirror of
				https://github.com/raysan5/raylib.git
				synced 2025-11-04 01:34:19 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			260 lines
		
	
	
		
			9.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			260 lines
		
	
	
		
			9.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*******************************************************************************************
 | 
						|
*
 | 
						|
*   raylib [textures] example - Draw a texture along a segmented curve
 | 
						|
*
 | 
						|
*   Example originally created with raylib 4.5, last time updated with raylib 4.5
 | 
						|
*
 | 
						|
*   Example contributed by Jeffery Myers 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-2023 Jeffery Myers 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 UpdateOptions(void);
 | 
						|
static void UpdateCurve(void);
 | 
						|
static void DrawCurve(void);
 | 
						|
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] examples - 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
 | 
						|
        //----------------------------------------------------------------------------------
 | 
						|
        UpdateCurve();
 | 
						|
        UpdateOptions();
 | 
						|
 | 
						|
        //----------------------------------------------------------------------------------
 | 
						|
 | 
						|
        // Draw
 | 
						|
        //----------------------------------------------------------------------------------
 | 
						|
        BeginDrawing();
 | 
						|
 | 
						|
            ClearBackground(RAYWHITE);
 | 
						|
 | 
						|
            DrawTexturedCurve();
 | 
						|
            DrawCurve();
 | 
						|
        
 | 
						|
            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
 | 
						|
//----------------------------------------------------------------------------------
 | 
						|
static void DrawCurve(void)
 | 
						|
{
 | 
						|
    if (showCurve) DrawLineBezierCubic(curveStartPosition, curveEndPosition, curveStartPositionTangent, curveEndPositionTangent, 2, BLUE);
 | 
						|
 | 
						|
    // Draw the various control points and highlight where the mouse is
 | 
						|
    DrawLineV(curveStartPosition, curveStartPositionTangent, SKYBLUE);
 | 
						|
    DrawLineV(curveEndPosition, curveEndPositionTangent, PURPLE);
 | 
						|
    Vector2 mouse = GetMousePosition();
 | 
						|
 | 
						|
    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);
 | 
						|
}
 | 
						|
 | 
						|
static void UpdateCurve(void)
 | 
						|
{
 | 
						|
    // If the mouse is not down, we are not editing the curve so clear the selection
 | 
						|
    if (!IsMouseButtonDown(MOUSE_LEFT_BUTTON))
 | 
						|
    {
 | 
						|
        curveSelectedPoint = NULL;
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    // If a point was selected, move it
 | 
						|
    if (curveSelectedPoint)
 | 
						|
    {
 | 
						|
        *curveSelectedPoint = Vector2Add(*curveSelectedPoint, GetMouseDelta());
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    // 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;
 | 
						|
}
 | 
						|
 | 
						|
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++)
 | 
						|
    {
 | 
						|
        // Segment the curve
 | 
						|
        t = step*i;
 | 
						|
        float a = powf(1 - t, 3);
 | 
						|
        float b = 3*powf(1 - t, 2)*t;
 | 
						|
        float c = 3*(1 - 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;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static void UpdateOptions(void)
 | 
						|
{
 | 
						|
    if (IsKeyPressed(KEY_SPACE)) showCurve = !showCurve;
 | 
						|
 | 
						|
    // Update with
 | 
						|
    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;
 | 
						|
}
 |