REVIEWED: Example: textures_textured_curve

This commit is contained in:
Ray 2022-12-04 11:01:59 +01:00
parent 57dd345dc3
commit 89698844a1
3 changed files with 153 additions and 135 deletions

View file

Before

Width:  |  Height:  |  Size: 1,006 B

After

Width:  |  Height:  |  Size: 1,006 B

Before After
Before After

View file

@ -2,145 +2,219 @@
* *
* raylib [textures] example - Draw a texture along a segmented curve * raylib [textures] example - Draw a texture along a segmented curve
* *
* Example originally created with raylib 4.5 * Example originally created with raylib 4.5-dev
* *
* Example contributed by Jeffery Myers and reviewed by Ramon Santamaria (@raysan5) * Example contributed by Jeffery Myers and reviewed by Ramon Santamaria (@raysan5)
* *
* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, * Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
* BSD-like license that allows static linking with closed source software * BSD-like license that allows static linking with closed source software
* *
* Copyright (c) 2019-2022 Jeffery Myers and Ramon Santamaria (@raysan5) * Copyright (c) 2022 Jeffery Myers and Ramon Santamaria (@raysan5)
* *
********************************************************************************************/ ********************************************************************************************/
#include "raylib.h" #include "raylib.h"
#include "raymath.h" #include "raymath.h"
#include "rlgl.h" #include "rlgl.h"
Texture RoadTexture = { 0 }; #include <math.h> // Required for: powf()
#include <stdlib.h> // Required for: NULL
bool ShowCurve = false; //----------------------------------------------------------------------------------
// Global Variables Definition
//----------------------------------------------------------------------------------
static Texture texRoad = { 0 };
float Width = 50; static bool showCurve = false;
int Segments = 24;
Vector2 SP = { 0 }; static float curveWidth = 50;
Vector2 SPTangent = { 0 }; static int curveSegments = 24;
Vector2 EP = { 0 }; static Vector2 curveStartPosition = { 0 };
Vector2 EPTangent = { 0 }; static Vector2 curveStartPositionTangent = { 0 };
Vector2* Selected = NULL; static Vector2 curveEndPosition = { 0 };
static Vector2 curveEndPositionTangent = { 0 };
void DrawCurve() 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()
{ {
if (ShowCurve) // Initialization
DrawLineBezierCubic(SP, EP, SPTangent, EPTangent, 2, BLUE); //--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
// draw the various control points and highlight where the mouse is SetConfigFlags(FLAG_VSYNC_HINT | FLAG_MSAA_4X_HINT);
DrawLineV(SP, SPTangent, SKYBLUE); InitWindow(screenWidth, screenHeight, "raylib [textures] examples - textured curve");
DrawLineV(EP, EPTangent, PURPLE);
Vector2 mouse = GetMousePosition();
if (CheckCollisionPointCircle(mouse, SP, 6)) // Load the road texture
DrawCircleV(SP, 7, YELLOW); texRoad = LoadTexture("resources/road.png");
DrawCircleV(SP, 5, RED); SetTextureFilter(texRoad, TEXTURE_FILTER_BILINEAR);
if (CheckCollisionPointCircle(mouse, SPTangent, 6)) // Setup the curve
DrawCircleV(SPTangent, 7, YELLOW); curveStartPosition = (Vector2){ 80, 100 };
DrawCircleV(SPTangent, 5, MAROON); curveStartPositionTangent = (Vector2){ 100, 300 };
if (CheckCollisionPointCircle(mouse, EP, 6)) curveEndPosition = (Vector2){ 700, 350 };
DrawCircleV(EP, 7, YELLOW); curveEndPositionTangent = (Vector2){ 600, 100 };
DrawCircleV(EP, 5, GREEN);
if (CheckCollisionPointCircle(mouse, EPTangent, 6)) SetTargetFPS(60); // Set our game to run at 60 frames-per-second
DrawCircleV(EPTangent, 7, YELLOW); //--------------------------------------------------------------------------------------
DrawCircleV(EPTangent, 5, DARKGREEN);
// 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;
} }
void EditCurve() //----------------------------------------------------------------------------------
// Module Functions Definition
//----------------------------------------------------------------------------------
static void DrawCurve(void)
{ {
// if the mouse is not down, we are not editing the curve so clear the selection 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)) if (!IsMouseButtonDown(MOUSE_LEFT_BUTTON))
{ {
Selected = NULL; curveSelectedPoint = NULL;
return; return;
} }
// if a point was selected, move it // If a point was selected, move it
if (Selected) if (curveSelectedPoint)
{ {
*Selected = Vector2Add(*Selected, GetMouseDelta()); *curveSelectedPoint = Vector2Add(*curveSelectedPoint, GetMouseDelta());
return; return;
} }
// the mouse is down, and nothing was selected, so see if anything was picked // The mouse is down, and nothing was selected, so see if anything was picked
Vector2 mouse = GetMousePosition(); Vector2 mouse = GetMousePosition();
if (CheckCollisionPointCircle(mouse, SP, 6)) if (CheckCollisionPointCircle(mouse, curveStartPosition, 6)) curveSelectedPoint = &curveStartPosition;
Selected = &SP; else if (CheckCollisionPointCircle(mouse, curveStartPositionTangent, 6)) curveSelectedPoint = &curveStartPositionTangent;
else if (CheckCollisionPointCircle(mouse, SPTangent, 6)) else if (CheckCollisionPointCircle(mouse, curveEndPosition, 6)) curveSelectedPoint = &curveEndPosition;
Selected = &SPTangent; else if (CheckCollisionPointCircle(mouse, curveEndPositionTangent, 6)) curveSelectedPoint = &curveEndPositionTangent;
else if (CheckCollisionPointCircle(mouse, EP, 6))
Selected = &EP;
else if (CheckCollisionPointCircle(mouse, EPTangent, 6))
Selected = &EPTangent;
} }
void DrawTexturedCurve() static void DrawTexturedCurve(void)
{ {
const float step = 1.0f / Segments; const float step = 1.0f/curveSegments;
Vector2 previous = SP; Vector2 previous = curveStartPosition;
Vector2 previousTangent = { 0 }; Vector2 previousTangent = { 0 };
float previousV = 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 // We can't compute a tangent for the first point, so we need to reuse the tangent from the first segment
bool tangentSet = false; bool tangentSet = false;
Vector2 current = { 0 }; Vector2 current = { 0 };
float t = 0.0f; float t = 0.0f;
for (int i = 1; i <= Segments; i++) for (int i = 1; i <= curveSegments; i++)
{ {
// segment the curve // Segment the curve
t = step * i; t = step*i;
float a = powf(1 - t, 3); float a = powf(1 - t, 3);
float b = 3 * powf(1 - t, 2) * t; float b = 3*powf(1 - t, 2)*t;
float c = 3 * (1 - t) * powf(t, 2); float c = 3*(1 - t)*powf(t, 2);
float d = powf(t, 3); float d = powf(t, 3);
// compute the endpoint for this segment // Compute the endpoint for this segment
current.y = a * SP.y + b * SPTangent.y + c * EPTangent.y + d * EP.y; current.y = a*curveStartPosition.y + b*curveStartPositionTangent.y + c*curveEndPositionTangent.y + d*curveEndPosition.y;
current.x = a * SP.x + b * SPTangent.x + c * EPTangent.x + d * EP.x; current.x = a*curveStartPosition.x + b*curveStartPositionTangent.x + c*curveEndPositionTangent.x + d*curveEndPosition.x;
// vector from previous to current // Vector from previous to current
Vector2 delta = { current.x - previous.x, current.y - previous.y }; Vector2 delta = { current.x - previous.x, current.y - previous.y };
// the right hand normal to the delta vector // The right hand normal to the delta vector
Vector2 normal = Vector2Normalize((Vector2){ -delta.y, delta.x }); 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) // The v texture coordinate of the segment (add up the length of all the segments so far)
float v = previousV + Vector2Length(delta); float v = previousV + Vector2Length(delta);
// make sure the start point has a normal // Make sure the start point has a normal
if (!tangentSet) if (!tangentSet)
{ {
previousTangent = normal; previousTangent = normal;
tangentSet = true; tangentSet = true;
} }
// extend out the normals from the previous and current points to get the quad for this segment // Extend out the normals from the previous and current points to get the quad for this segment
Vector2 prevPosNormal = Vector2Add(previous, Vector2Scale(previousTangent, Width)); Vector2 prevPosNormal = Vector2Add(previous, Vector2Scale(previousTangent, curveWidth));
Vector2 prevNegNormal = Vector2Add(previous, Vector2Scale(previousTangent, -Width)); Vector2 prevNegNormal = Vector2Add(previous, Vector2Scale(previousTangent, -curveWidth));
Vector2 currentPosNormal = Vector2Add(current, Vector2Scale(normal, Width)); Vector2 currentPosNormal = Vector2Add(current, Vector2Scale(normal, curveWidth));
Vector2 currentNegNormal = Vector2Add(current, Vector2Scale(normal, -Width)); Vector2 currentNegNormal = Vector2Add(current, Vector2Scale(normal, -curveWidth));
// draw the segment as a quad // Draw the segment as a quad
rlSetTexture(RoadTexture.id); rlSetTexture(texRoad.id);
rlBegin(RL_QUADS); rlBegin(RL_QUADS);
rlColor4ub(255,255,255,255); rlColor4ub(255,255,255,255);
@ -160,82 +234,26 @@ void DrawTexturedCurve()
rlEnd(); rlEnd();
// the current step is the start of the next step // The current step is the start of the next step
previous = current; previous = current;
previousTangent = normal; previousTangent = normal;
previousV = v; previousV = v;
} }
} }
void UpdateOptions() static void UpdateOptions(void)
{ {
if (IsKeyPressed(KEY_SPACE)) if (IsKeyPressed(KEY_SPACE)) showCurve = !showCurve;
ShowCurve = !ShowCurve;
// width // Update with
if (IsKeyPressed(KEY_EQUAL)) if (IsKeyPressed(KEY_EQUAL)) curveWidth += 2;
Width += 2; if (IsKeyPressed(KEY_MINUS)) curveWidth -= 2;
if (IsKeyPressed(KEY_MINUS)) if (curveWidth < 2) curveWidth = 2;
Width -= 2;
if (Width < 2) // Update segments
Width = 2; if (IsKeyPressed(KEY_LEFT)) curveSegments -= 2;
if (IsKeyPressed(KEY_RIGHT)) curveSegments += 2;
// segments if (curveSegments < 2) curveSegments = 2;
if (IsKeyPressed(KEY_LEFT_BRACKET))
Segments -= 2;
if (IsKeyPressed(KEY_RIGHT_BRACKET))
Segments += 2;
if (Segments < 2)
Segments = 2;
} }
int main ()
{
// set up the window
SetConfigFlags(FLAG_VSYNC_HINT);
InitWindow(1280, 800, "raylib [textures] examples - textured curve");
SetTargetFPS(144);
// load the road texture
RoadTexture = LoadTexture("resources/roadTexture_01.png");
// setup the curve
SP = (Vector2){ 80, 400 };
SPTangent = (Vector2){ 600, 100 };
EP = (Vector2){ 1200, 400 };
EPTangent = (Vector2){ 600, 700 };
// game loop
while (!WindowShouldClose())
{
EditCurve();
UpdateOptions();
BeginDrawing();
ClearBackground(BLACK);
DrawTexturedCurve();
DrawCurve();
DrawText("Drag points to move curve, press space to show/hide base curve", 10, 0, 20, WHITE);
DrawText(TextFormat("Width %2.0f + and - to adjust", Width), 10, 20, 20, WHITE);
DrawText(TextFormat("Segments %d [ and ] to adjust", Segments), 10, 40, 20, WHITE);
DrawFPS(10, 60);
EndDrawing();
}
// cleanup
UnloadTexture(RoadTexture);
CloseWindow();
return 0;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Before After
Before After