Add a textured curve example (#2821)
This commit is contained in:
parent
5b5dff3f9e
commit
57dd345dc3
6 changed files with 648 additions and 0 deletions
241
examples/textures/textures_textured_curve.c
Normal file
241
examples/textures/textures_textured_curve.c
Normal file
|
@ -0,0 +1,241 @@
|
|||
/*******************************************************************************************
|
||||
*
|
||||
* raylib [textures] example - Draw a texture along a segmented curve
|
||||
*
|
||||
* Example originally created 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) 2019-2022 Jeffery Myers and Ramon Santamaria (@raysan5)
|
||||
*
|
||||
********************************************************************************************/
|
||||
|
||||
|
||||
#include "raylib.h"
|
||||
#include "raymath.h"
|
||||
#include "rlgl.h"
|
||||
|
||||
Texture RoadTexture = { 0 };
|
||||
|
||||
bool ShowCurve = false;
|
||||
|
||||
float Width = 50;
|
||||
int Segments = 24;
|
||||
|
||||
Vector2 SP = { 0 };
|
||||
Vector2 SPTangent = { 0 };
|
||||
|
||||
Vector2 EP = { 0 };
|
||||
Vector2 EPTangent = { 0 };
|
||||
|
||||
Vector2* Selected = NULL;
|
||||
|
||||
void DrawCurve()
|
||||
{
|
||||
if (ShowCurve)
|
||||
DrawLineBezierCubic(SP, EP, SPTangent, EPTangent, 2, BLUE);
|
||||
|
||||
// draw the various control points and highlight where the mouse is
|
||||
DrawLineV(SP, SPTangent, SKYBLUE);
|
||||
DrawLineV(EP, EPTangent, PURPLE);
|
||||
Vector2 mouse = GetMousePosition();
|
||||
|
||||
if (CheckCollisionPointCircle(mouse, SP, 6))
|
||||
DrawCircleV(SP, 7, YELLOW);
|
||||
DrawCircleV(SP, 5, RED);
|
||||
|
||||
if (CheckCollisionPointCircle(mouse, SPTangent, 6))
|
||||
DrawCircleV(SPTangent, 7, YELLOW);
|
||||
DrawCircleV(SPTangent, 5, MAROON);
|
||||
|
||||
if (CheckCollisionPointCircle(mouse, EP, 6))
|
||||
DrawCircleV(EP, 7, YELLOW);
|
||||
DrawCircleV(EP, 5, GREEN);
|
||||
|
||||
if (CheckCollisionPointCircle(mouse, EPTangent, 6))
|
||||
DrawCircleV(EPTangent, 7, YELLOW);
|
||||
DrawCircleV(EPTangent, 5, DARKGREEN);
|
||||
}
|
||||
|
||||
void EditCurve()
|
||||
{
|
||||
// if the mouse is not down, we are not editing the curve so clear the selection
|
||||
if (!IsMouseButtonDown(MOUSE_LEFT_BUTTON))
|
||||
{
|
||||
Selected = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
// if a point was selected, move it
|
||||
if (Selected)
|
||||
{
|
||||
*Selected = Vector2Add(*Selected, GetMouseDelta());
|
||||
return;
|
||||
}
|
||||
|
||||
// the mouse is down, and nothing was selected, so see if anything was picked
|
||||
Vector2 mouse = GetMousePosition();
|
||||
|
||||
if (CheckCollisionPointCircle(mouse, SP, 6))
|
||||
Selected = &SP;
|
||||
else if (CheckCollisionPointCircle(mouse, SPTangent, 6))
|
||||
Selected = &SPTangent;
|
||||
else if (CheckCollisionPointCircle(mouse, EP, 6))
|
||||
Selected = &EP;
|
||||
else if (CheckCollisionPointCircle(mouse, EPTangent, 6))
|
||||
Selected = &EPTangent;
|
||||
}
|
||||
|
||||
void DrawTexturedCurve()
|
||||
{
|
||||
const float step = 1.0f / Segments;
|
||||
|
||||
Vector2 previous = SP;
|
||||
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 <= Segments; 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 * SP.y + b * SPTangent.y + c * EPTangent.y + d * EP.y;
|
||||
current.x = a * SP.x + b * SPTangent.x + c * EPTangent.x + d * EP.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, Width));
|
||||
Vector2 prevNegNormal = Vector2Add(previous, Vector2Scale(previousTangent, -Width));
|
||||
|
||||
Vector2 currentPosNormal = Vector2Add(current, Vector2Scale(normal, Width));
|
||||
Vector2 currentNegNormal = Vector2Add(current, Vector2Scale(normal, -Width));
|
||||
|
||||
// draw the segment as a quad
|
||||
rlSetTexture(RoadTexture.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;
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateOptions()
|
||||
{
|
||||
if (IsKeyPressed(KEY_SPACE))
|
||||
ShowCurve = !ShowCurve;
|
||||
|
||||
// width
|
||||
if (IsKeyPressed(KEY_EQUAL))
|
||||
Width += 2;
|
||||
|
||||
if (IsKeyPressed(KEY_MINUS))
|
||||
Width -= 2;
|
||||
|
||||
if (Width < 2)
|
||||
Width = 2;
|
||||
|
||||
// segments
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue