Skip to content

Commit 57dd345

Browse files
authored
Add a textured curve example (#2821)
1 parent 5b5dff3 commit 57dd345

File tree

6 files changed

+648
-0
lines changed

6 files changed

+648
-0
lines changed

examples/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,7 @@ TEXTURES = \
460460
textures/textures_sprite_anim \
461461
textures/textures_sprite_button \
462462
textures/textures_sprite_explosion \
463+
textures/textures_textured_curve \
463464
textures/textures_bunnymark \
464465
textures/textures_blend_modes \
465466
textures/textures_draw_tiled \
1006 Bytes
Loading
Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
/*******************************************************************************************
2+
*
3+
* raylib [textures] example - Draw a texture along a segmented curve
4+
*
5+
* Example originally created with raylib 4.5
6+
*
7+
* Example contributed by Jeffery Myers and reviewed by Ramon Santamaria (@raysan5)
8+
*
9+
* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
10+
* BSD-like license that allows static linking with closed source software
11+
*
12+
* Copyright (c) 2019-2022 Jeffery Myers and Ramon Santamaria (@raysan5)
13+
*
14+
********************************************************************************************/
15+
16+
17+
#include "raylib.h"
18+
#include "raymath.h"
19+
#include "rlgl.h"
20+
21+
Texture RoadTexture = { 0 };
22+
23+
bool ShowCurve = false;
24+
25+
float Width = 50;
26+
int Segments = 24;
27+
28+
Vector2 SP = { 0 };
29+
Vector2 SPTangent = { 0 };
30+
31+
Vector2 EP = { 0 };
32+
Vector2 EPTangent = { 0 };
33+
34+
Vector2* Selected = NULL;
35+
36+
void DrawCurve()
37+
{
38+
if (ShowCurve)
39+
DrawLineBezierCubic(SP, EP, SPTangent, EPTangent, 2, BLUE);
40+
41+
// draw the various control points and highlight where the mouse is
42+
DrawLineV(SP, SPTangent, SKYBLUE);
43+
DrawLineV(EP, EPTangent, PURPLE);
44+
Vector2 mouse = GetMousePosition();
45+
46+
if (CheckCollisionPointCircle(mouse, SP, 6))
47+
DrawCircleV(SP, 7, YELLOW);
48+
DrawCircleV(SP, 5, RED);
49+
50+
if (CheckCollisionPointCircle(mouse, SPTangent, 6))
51+
DrawCircleV(SPTangent, 7, YELLOW);
52+
DrawCircleV(SPTangent, 5, MAROON);
53+
54+
if (CheckCollisionPointCircle(mouse, EP, 6))
55+
DrawCircleV(EP, 7, YELLOW);
56+
DrawCircleV(EP, 5, GREEN);
57+
58+
if (CheckCollisionPointCircle(mouse, EPTangent, 6))
59+
DrawCircleV(EPTangent, 7, YELLOW);
60+
DrawCircleV(EPTangent, 5, DARKGREEN);
61+
}
62+
63+
void EditCurve()
64+
{
65+
// if the mouse is not down, we are not editing the curve so clear the selection
66+
if (!IsMouseButtonDown(MOUSE_LEFT_BUTTON))
67+
{
68+
Selected = NULL;
69+
return;
70+
}
71+
72+
// if a point was selected, move it
73+
if (Selected)
74+
{
75+
*Selected = Vector2Add(*Selected, GetMouseDelta());
76+
return;
77+
}
78+
79+
// the mouse is down, and nothing was selected, so see if anything was picked
80+
Vector2 mouse = GetMousePosition();
81+
82+
if (CheckCollisionPointCircle(mouse, SP, 6))
83+
Selected = &SP;
84+
else if (CheckCollisionPointCircle(mouse, SPTangent, 6))
85+
Selected = &SPTangent;
86+
else if (CheckCollisionPointCircle(mouse, EP, 6))
87+
Selected = &EP;
88+
else if (CheckCollisionPointCircle(mouse, EPTangent, 6))
89+
Selected = &EPTangent;
90+
}
91+
92+
void DrawTexturedCurve()
93+
{
94+
const float step = 1.0f / Segments;
95+
96+
Vector2 previous = SP;
97+
Vector2 previousTangent = { 0 };
98+
float previousV = 0;
99+
100+
// we can't compute a tangent for the first point, so we need to reuse the tangent from the first segment
101+
bool tangentSet = false;
102+
103+
Vector2 current = { 0 };
104+
float t = 0.0f;
105+
106+
for (int i = 1; i <= Segments; i++)
107+
{
108+
// segment the curve
109+
t = step * i;
110+
float a = powf(1 - t, 3);
111+
float b = 3 * powf(1 - t, 2) * t;
112+
float c = 3 * (1 - t) * powf(t, 2);
113+
float d = powf(t, 3);
114+
115+
// compute the endpoint for this segment
116+
current.y = a * SP.y + b * SPTangent.y + c * EPTangent.y + d * EP.y;
117+
current.x = a * SP.x + b * SPTangent.x + c * EPTangent.x + d * EP.x;
118+
119+
// vector from previous to current
120+
Vector2 delta = { current.x - previous.x, current.y - previous.y };
121+
122+
// the right hand normal to the delta vector
123+
Vector2 normal = Vector2Normalize((Vector2){ -delta.y, delta.x });
124+
125+
// the v texture coordinate of the segment (add up the length of all the segments so far)
126+
float v = previousV + Vector2Length(delta);
127+
128+
// make sure the start point has a normal
129+
if (!tangentSet)
130+
{
131+
previousTangent = normal;
132+
tangentSet = true;
133+
}
134+
135+
// extend out the normals from the previous and current points to get the quad for this segment
136+
Vector2 prevPosNormal = Vector2Add(previous, Vector2Scale(previousTangent, Width));
137+
Vector2 prevNegNormal = Vector2Add(previous, Vector2Scale(previousTangent, -Width));
138+
139+
Vector2 currentPosNormal = Vector2Add(current, Vector2Scale(normal, Width));
140+
Vector2 currentNegNormal = Vector2Add(current, Vector2Scale(normal, -Width));
141+
142+
// draw the segment as a quad
143+
rlSetTexture(RoadTexture.id);
144+
rlBegin(RL_QUADS);
145+
146+
rlColor4ub(255,255,255,255);
147+
rlNormal3f(0.0f, 0.0f, 1.0f);
148+
149+
rlTexCoord2f(0, previousV);
150+
rlVertex2f(prevNegNormal.x, prevNegNormal.y);
151+
152+
rlTexCoord2f(1, previousV);
153+
rlVertex2f(prevPosNormal.x, prevPosNormal.y);
154+
155+
rlTexCoord2f(1, v);
156+
rlVertex2f(currentPosNormal.x, currentPosNormal.y);
157+
158+
rlTexCoord2f(0, v);
159+
rlVertex2f(currentNegNormal.x, currentNegNormal.y);
160+
161+
rlEnd();
162+
163+
// the current step is the start of the next step
164+
previous = current;
165+
previousTangent = normal;
166+
previousV = v;
167+
}
168+
}
169+
170+
void UpdateOptions()
171+
{
172+
if (IsKeyPressed(KEY_SPACE))
173+
ShowCurve = !ShowCurve;
174+
175+
// width
176+
if (IsKeyPressed(KEY_EQUAL))
177+
Width += 2;
178+
179+
if (IsKeyPressed(KEY_MINUS))
180+
Width -= 2;
181+
182+
if (Width < 2)
183+
Width = 2;
184+
185+
// segments
186+
187+
if (IsKeyPressed(KEY_LEFT_BRACKET))
188+
Segments -= 2;
189+
190+
if (IsKeyPressed(KEY_RIGHT_BRACKET))
191+
Segments += 2;
192+
193+
if (Segments < 2)
194+
Segments = 2;
195+
}
196+
197+
int main ()
198+
{
199+
// set up the window
200+
SetConfigFlags(FLAG_VSYNC_HINT);
201+
InitWindow(1280, 800, "raylib [textures] examples - textured curve");
202+
SetTargetFPS(144);
203+
204+
// load the road texture
205+
RoadTexture = LoadTexture("resources/roadTexture_01.png");
206+
207+
// setup the curve
208+
SP = (Vector2){ 80, 400 };
209+
SPTangent = (Vector2){ 600, 100 };
210+
211+
EP = (Vector2){ 1200, 400 };
212+
EPTangent = (Vector2){ 600, 700 };
213+
214+
// game loop
215+
while (!WindowShouldClose())
216+
{
217+
EditCurve();
218+
UpdateOptions();
219+
220+
BeginDrawing();
221+
222+
ClearBackground(BLACK);
223+
224+
DrawTexturedCurve();
225+
DrawCurve();
226+
227+
DrawText("Drag points to move curve, press space to show/hide base curve", 10, 0, 20, WHITE);
228+
DrawText(TextFormat("Width %2.0f + and - to adjust", Width), 10, 20, 20, WHITE);
229+
DrawText(TextFormat("Segments %d [ and ] to adjust", Segments), 10, 40, 20, WHITE);
230+
DrawFPS(10, 60);
231+
232+
EndDrawing();
233+
}
234+
235+
// cleanup
236+
UnloadTexture(RoadTexture);
237+
CloseWindow();
238+
return 0;
239+
}
240+
241+
20.5 KB
Loading

0 commit comments

Comments
 (0)