Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

235 rader
9.6 KiB

2 år sedan
  1. /*******************************************************************************************
  2. *
  3. * raylib [textures] example - Draw a texture along a segmented curve
  4. *
  5. * Example complexity rating: [] 3/4
  6. *
  7. * Example originally created with raylib 4.5, last time updated with raylib 4.5
  8. *
  9. * Example contributed by Jeffery Myers (@JeffM2501) and reviewed by Ramon Santamaria (@raysan5)
  10. *
  11. * Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
  12. * BSD-like license that allows static linking with closed source software
  13. *
  14. * Copyright (c) 2022-2025 Jeffery Myers (@JeffM2501) and Ramon Santamaria (@raysan5)
  15. *
  16. ********************************************************************************************/
  17. #include "raylib.h"
  18. #include "raymath.h"
  19. #include "rlgl.h"
  20. #include <math.h> // Required for: powf()
  21. #include <stdlib.h> // Required for: NULL
  22. //----------------------------------------------------------------------------------
  23. // Global Variables Definition
  24. //----------------------------------------------------------------------------------
  25. static Texture texRoad = { 0 };
  26. static bool showCurve = false;
  27. static float curveWidth = 50;
  28. static int curveSegments = 24;
  29. static Vector2 curveStartPosition = { 0 };
  30. static Vector2 curveStartPositionTangent = { 0 };
  31. static Vector2 curveEndPosition = { 0 };
  32. static Vector2 curveEndPositionTangent = { 0 };
  33. static Vector2 *curveSelectedPoint = NULL;
  34. //----------------------------------------------------------------------------------
  35. // Module Functions Declaration
  36. //----------------------------------------------------------------------------------
  37. static void DrawTexturedCurve(void);
  38. //------------------------------------------------------------------------------------
  39. // Program main entry point
  40. //------------------------------------------------------------------------------------
  41. int main()
  42. {
  43. // Initialization
  44. //--------------------------------------------------------------------------------------
  45. const int screenWidth = 800;
  46. const int screenHeight = 450;
  47. SetConfigFlags(FLAG_VSYNC_HINT | FLAG_MSAA_4X_HINT);
  48. InitWindow(screenWidth, screenHeight, "raylib [textures] examples - textured curve");
  49. // Load the road texture
  50. texRoad = LoadTexture("resources/road.png");
  51. SetTextureFilter(texRoad, TEXTURE_FILTER_BILINEAR);
  52. // Setup the curve
  53. curveStartPosition = (Vector2){ 80, 100 };
  54. curveStartPositionTangent = (Vector2){ 100, 300 };
  55. curveEndPosition = (Vector2){ 700, 350 };
  56. curveEndPositionTangent = (Vector2){ 600, 100 };
  57. SetTargetFPS(60); // Set our game to run at 60 frames-per-second
  58. //--------------------------------------------------------------------------------------
  59. // Main game loop
  60. while (!WindowShouldClose()) // Detect window close button or ESC key
  61. {
  62. // Update
  63. //----------------------------------------------------------------------------------
  64. // Curve config options
  65. if (IsKeyPressed(KEY_SPACE)) showCurve = !showCurve;
  66. if (IsKeyPressed(KEY_EQUAL)) curveWidth += 2;
  67. if (IsKeyPressed(KEY_MINUS)) curveWidth -= 2;
  68. if (curveWidth < 2) curveWidth = 2;
  69. // Update segments
  70. if (IsKeyPressed(KEY_LEFT)) curveSegments -= 2;
  71. if (IsKeyPressed(KEY_RIGHT)) curveSegments += 2;
  72. if (curveSegments < 2) curveSegments = 2;
  73. // Update curve logic
  74. // If the mouse is not down, we are not editing the curve so clear the selection
  75. if (!IsMouseButtonDown(MOUSE_LEFT_BUTTON)) curveSelectedPoint = NULL;
  76. // If a point was selected, move it
  77. if (curveSelectedPoint) *curveSelectedPoint = Vector2Add(*curveSelectedPoint, GetMouseDelta());
  78. // The mouse is down, and nothing was selected, so see if anything was picked
  79. Vector2 mouse = GetMousePosition();
  80. if (CheckCollisionPointCircle(mouse, curveStartPosition, 6)) curveSelectedPoint = &curveStartPosition;
  81. else if (CheckCollisionPointCircle(mouse, curveStartPositionTangent, 6)) curveSelectedPoint = &curveStartPositionTangent;
  82. else if (CheckCollisionPointCircle(mouse, curveEndPosition, 6)) curveSelectedPoint = &curveEndPosition;
  83. else if (CheckCollisionPointCircle(mouse, curveEndPositionTangent, 6)) curveSelectedPoint = &curveEndPositionTangent;
  84. //----------------------------------------------------------------------------------
  85. // Draw
  86. //----------------------------------------------------------------------------------
  87. BeginDrawing();
  88. ClearBackground(RAYWHITE);
  89. DrawTexturedCurve(); // Draw a textured Spline Cubic Bezier
  90. // Draw spline for reference
  91. if (showCurve) DrawSplineSegmentBezierCubic(curveStartPosition, curveEndPosition, curveStartPositionTangent, curveEndPositionTangent, 2, BLUE);
  92. // Draw the various control points and highlight where the mouse is
  93. DrawLineV(curveStartPosition, curveStartPositionTangent, SKYBLUE);
  94. DrawLineV(curveStartPositionTangent, curveEndPositionTangent, Fade(LIGHTGRAY, 0.4f));
  95. DrawLineV(curveEndPosition, curveEndPositionTangent, PURPLE);
  96. if (CheckCollisionPointCircle(mouse, curveStartPosition, 6)) DrawCircleV(curveStartPosition, 7, YELLOW);
  97. DrawCircleV(curveStartPosition, 5, RED);
  98. if (CheckCollisionPointCircle(mouse, curveStartPositionTangent, 6)) DrawCircleV(curveStartPositionTangent, 7, YELLOW);
  99. DrawCircleV(curveStartPositionTangent, 5, MAROON);
  100. if (CheckCollisionPointCircle(mouse, curveEndPosition, 6)) DrawCircleV(curveEndPosition, 7, YELLOW);
  101. DrawCircleV(curveEndPosition, 5, GREEN);
  102. if (CheckCollisionPointCircle(mouse, curveEndPositionTangent, 6)) DrawCircleV(curveEndPositionTangent, 7, YELLOW);
  103. DrawCircleV(curveEndPositionTangent, 5, DARKGREEN);
  104. // Draw usage info
  105. DrawText("Drag points to move curve, press SPACE to show/hide base curve", 10, 10, 10, DARKGRAY);
  106. DrawText(TextFormat("Curve width: %2.0f (Use + and - to adjust)", curveWidth), 10, 30, 10, DARKGRAY);
  107. DrawText(TextFormat("Curve segments: %d (Use LEFT and RIGHT to adjust)", curveSegments), 10, 50, 10, DARKGRAY);
  108. EndDrawing();
  109. //----------------------------------------------------------------------------------
  110. }
  111. // De-Initialization
  112. //--------------------------------------------------------------------------------------
  113. UnloadTexture(texRoad);
  114. CloseWindow(); // Close window and OpenGL context
  115. //--------------------------------------------------------------------------------------
  116. return 0;
  117. }
  118. //----------------------------------------------------------------------------------
  119. // Module Functions Definition
  120. //----------------------------------------------------------------------------------
  121. // Draw textured curve using Spline Cubic Bezier
  122. static void DrawTexturedCurve(void)
  123. {
  124. const float step = 1.0f/curveSegments;
  125. Vector2 previous = curveStartPosition;
  126. Vector2 previousTangent = { 0 };
  127. float previousV = 0;
  128. // We can't compute a tangent for the first point, so we need to reuse the tangent from the first segment
  129. bool tangentSet = false;
  130. Vector2 current = { 0 };
  131. float t = 0.0f;
  132. for (int i = 1; i <= curveSegments; i++)
  133. {
  134. t = step*(float)i;
  135. float a = powf(1.0f - t, 3);
  136. float b = 3.0f*powf(1.0f - t, 2)*t;
  137. float c = 3.0f*(1.0f - t)*powf(t, 2);
  138. float d = powf(t, 3);
  139. // Compute the endpoint for this segment
  140. current.y = a*curveStartPosition.y + b*curveStartPositionTangent.y + c*curveEndPositionTangent.y + d*curveEndPosition.y;
  141. current.x = a*curveStartPosition.x + b*curveStartPositionTangent.x + c*curveEndPositionTangent.x + d*curveEndPosition.x;
  142. // Vector from previous to current
  143. Vector2 delta = { current.x - previous.x, current.y - previous.y };
  144. // The right hand normal to the delta vector
  145. Vector2 normal = Vector2Normalize((Vector2){ -delta.y, delta.x });
  146. // The v texture coordinate of the segment (add up the length of all the segments so far)
  147. float v = previousV + Vector2Length(delta);
  148. // Make sure the start point has a normal
  149. if (!tangentSet)
  150. {
  151. previousTangent = normal;
  152. tangentSet = true;
  153. }
  154. // Extend out the normals from the previous and current points to get the quad for this segment
  155. Vector2 prevPosNormal = Vector2Add(previous, Vector2Scale(previousTangent, curveWidth));
  156. Vector2 prevNegNormal = Vector2Add(previous, Vector2Scale(previousTangent, -curveWidth));
  157. Vector2 currentPosNormal = Vector2Add(current, Vector2Scale(normal, curveWidth));
  158. Vector2 currentNegNormal = Vector2Add(current, Vector2Scale(normal, -curveWidth));
  159. // Draw the segment as a quad
  160. rlSetTexture(texRoad.id);
  161. rlBegin(RL_QUADS);
  162. rlColor4ub(255,255,255,255);
  163. rlNormal3f(0.0f, 0.0f, 1.0f);
  164. rlTexCoord2f(0, previousV);
  165. rlVertex2f(prevNegNormal.x, prevNegNormal.y);
  166. rlTexCoord2f(1, previousV);
  167. rlVertex2f(prevPosNormal.x, prevPosNormal.y);
  168. rlTexCoord2f(1, v);
  169. rlVertex2f(currentPosNormal.x, currentPosNormal.y);
  170. rlTexCoord2f(0, v);
  171. rlVertex2f(currentNegNormal.x, currentNegNormal.y);
  172. rlEnd();
  173. // The current step is the start of the next step
  174. previous = current;
  175. previousTangent = normal;
  176. previousV = v;
  177. }
  178. }