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
+
0 commit comments