mirror of
https://github.com/raylib-cs/raylib-cs
synced 2025-04-05 11:19:39 -04:00
285 lines
9.2 KiB
C#
285 lines
9.2 KiB
C#
using System;
|
|
using System.Numerics;
|
|
using static Raylib_cs.Raylib;
|
|
|
|
namespace Examples.Textures;
|
|
|
|
public unsafe class TexturedCurve
|
|
{
|
|
public class CurvePoint
|
|
{
|
|
public Vector2 value;
|
|
|
|
public float X => value.X;
|
|
public float Y => value.Y;
|
|
|
|
public static implicit operator CurvePoint(Vector2 v) => new CurvePoint { value = v };
|
|
public static implicit operator Vector2(CurvePoint v) => v.value;
|
|
}
|
|
|
|
static Texture2D texRoad;
|
|
static bool showCurve = false;
|
|
static float curveWidth = 50;
|
|
static int curveSegments = 24;
|
|
static CurvePoint curveStartPosition;
|
|
static CurvePoint curveStartPositionTangent;
|
|
static CurvePoint curveEndPosition;
|
|
static CurvePoint curveEndPositionTangent;
|
|
static CurvePoint curveSelectedPoint;
|
|
|
|
public static int Main()
|
|
{
|
|
// Initialization
|
|
//--------------------------------------------------------------------------------------
|
|
const int screenWidth = 800;
|
|
const int screenHeight = 450;
|
|
|
|
SetConfigFlags(ConfigFlags.FLAG_VSYNC_HINT | ConfigFlags.FLAG_MSAA_4X_HINT);
|
|
InitWindow(screenWidth, screenHeight, "raylib [textures] examples - textured curve");
|
|
|
|
// Load the road texture
|
|
texRoad = LoadTexture("resources/road.png");
|
|
SetTextureFilter(texRoad, TextureFilter.TEXTURE_FILTER_BILINEAR);
|
|
|
|
// Setup the curve
|
|
curveStartPosition = new Vector2(80, 100);
|
|
curveStartPositionTangent = new Vector2(100, 300);
|
|
|
|
curveEndPosition = new Vector2(700, 350);
|
|
curveEndPositionTangent = new Vector2(600, 100);
|
|
|
|
SetTargetFPS(60);
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
// Main game loop
|
|
while (!WindowShouldClose())
|
|
{
|
|
// Update
|
|
//----------------------------------------------------------------------------------
|
|
UpdateCurve();
|
|
UpdateOptions();
|
|
//----------------------------------------------------------------------------------
|
|
|
|
// Draw
|
|
//----------------------------------------------------------------------------------
|
|
BeginDrawing();
|
|
ClearBackground(Color.RAYWHITE);
|
|
|
|
DrawTexturedCurve();
|
|
DrawCurve();
|
|
|
|
DrawText("Drag points to move curve, press SPACE to show/hide base curve", 10, 10, 10, Color.DARKGRAY);
|
|
DrawText($"Curve width: {curveWidth} (Use + and - to adjust)", 10, 30, 10, Color.DARKGRAY);
|
|
DrawText($"Curve segments: {curveSegments} (Use LEFT and RIGHT to adjust)", 10, 50, 10, Color.DARKGRAY);
|
|
|
|
EndDrawing();
|
|
//----------------------------------------------------------------------------------
|
|
}
|
|
|
|
// De-Initialization
|
|
//--------------------------------------------------------------------------------------
|
|
UnloadTexture(texRoad);
|
|
|
|
CloseWindow();
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void DrawCurve()
|
|
{
|
|
if (showCurve)
|
|
{
|
|
DrawLineBezierCubic(
|
|
curveStartPosition,
|
|
curveEndPosition,
|
|
curveStartPositionTangent,
|
|
curveEndPositionTangent,
|
|
2,
|
|
Color.BLUE
|
|
);
|
|
}
|
|
|
|
// Draw the various control points and highlight where the mouse is
|
|
DrawLineV(curveStartPosition, curveStartPositionTangent, Color.SKYBLUE);
|
|
DrawLineV(curveEndPosition, curveEndPositionTangent, Color.PURPLE);
|
|
Vector2 mouse = GetMousePosition();
|
|
|
|
if (CheckCollisionPointCircle(mouse, curveStartPosition, 6))
|
|
{
|
|
DrawCircleV(curveStartPosition, 7, Color.YELLOW);
|
|
}
|
|
DrawCircleV(curveStartPosition, 5, Color.RED);
|
|
|
|
if (CheckCollisionPointCircle(mouse, curveStartPositionTangent, 6))
|
|
{
|
|
DrawCircleV(curveStartPositionTangent, 7, Color.YELLOW);
|
|
}
|
|
DrawCircleV(curveStartPositionTangent, 5, Color.MAROON);
|
|
|
|
if (CheckCollisionPointCircle(mouse, curveEndPosition, 6))
|
|
{
|
|
DrawCircleV(curveEndPosition, 7, Color.YELLOW);
|
|
}
|
|
DrawCircleV(curveEndPosition, 5, Color.GREEN);
|
|
|
|
if (CheckCollisionPointCircle(mouse, curveEndPositionTangent, 6))
|
|
{
|
|
DrawCircleV(curveEndPositionTangent, 7, Color.YELLOW);
|
|
}
|
|
DrawCircleV(curveEndPositionTangent, 5, Color.DARKGREEN);
|
|
}
|
|
|
|
static void UpdateCurve()
|
|
{
|
|
// If the mouse is not down, we are not editing the curve so clear the selection
|
|
if (!IsMouseButtonDown(MouseButton.MOUSE_LEFT_BUTTON))
|
|
{
|
|
return;
|
|
}
|
|
|
|
// If a point was selected, move it
|
|
if (curveSelectedPoint != null)
|
|
{
|
|
curveSelectedPoint.value += GetMouseDelta();
|
|
}
|
|
|
|
// The mouse is down, and nothing was selected, so see if anything was picked
|
|
Vector2 mouse = GetMousePosition();
|
|
|
|
if (CheckCollisionPointCircle(mouse, curveStartPosition, 6))
|
|
{
|
|
curveSelectedPoint = curveStartPosition;
|
|
}
|
|
else if (CheckCollisionPointCircle(mouse, curveStartPositionTangent, 6))
|
|
{
|
|
curveSelectedPoint = curveStartPositionTangent;
|
|
}
|
|
else if (CheckCollisionPointCircle(mouse, curveEndPosition, 6))
|
|
{
|
|
curveSelectedPoint = curveEndPosition;
|
|
}
|
|
else if (CheckCollisionPointCircle(mouse, curveEndPositionTangent, 6))
|
|
{
|
|
curveSelectedPoint = curveEndPositionTangent;
|
|
}
|
|
}
|
|
|
|
static void DrawTexturedCurve()
|
|
{
|
|
float step = 1.0f / curveSegments;
|
|
|
|
Vector2 previous = curveStartPosition;
|
|
Vector2 previousTangent = Vector2.Zero;
|
|
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 = Vector2.Zero;
|
|
float t = 0.0f;
|
|
|
|
for (int i = 1; i <= curveSegments; i++)
|
|
{
|
|
// Segment the curve
|
|
t = step * i;
|
|
float a = MathF.Pow(1 - t, 3);
|
|
float b = 3 * MathF.Pow(1 - t, 2) * t;
|
|
float c = 3 * (1 - t) * MathF.Pow(t, 2);
|
|
float d = MathF.Pow(t, 3);
|
|
|
|
// Compute the endpoint for this segment
|
|
current.Y = a * curveStartPosition.Y + b * curveStartPositionTangent.Y;
|
|
current.Y += c * curveEndPositionTangent.Y + d * curveEndPosition.Y;
|
|
current.X = a * curveStartPosition.X + b * curveStartPositionTangent.X;
|
|
current.X += c * curveEndPositionTangent.X + d * curveEndPosition.X;
|
|
|
|
// Vector from previous to current
|
|
Vector2 delta = new(current.X - previous.X, current.Y - previous.Y);
|
|
|
|
// The right hand normal to the delta vector
|
|
Vector2 normal = Vector2.Normalize(new 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 + delta.Length();
|
|
|
|
// 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 = previous + (previousTangent * curveWidth);
|
|
Vector2 prevNegNormal = previous + (previousTangent * -curveWidth);
|
|
|
|
Vector2 currentPosNormal = current + (normal * curveWidth);
|
|
Vector2 currentNegNormal = current + (normal * -curveWidth);
|
|
|
|
// Draw the segment as a quad
|
|
Rlgl.SetTexture(texRoad.Id);
|
|
Rlgl.Begin(DrawMode.QUADS);
|
|
|
|
Rlgl.Color4ub(255, 255, 255, 255);
|
|
Rlgl.Normal3f(0.0f, 0.0f, 1.0f);
|
|
|
|
Rlgl.TexCoord2f(0, previousV);
|
|
Rlgl.Vertex2f(prevNegNormal.X, prevNegNormal.Y);
|
|
|
|
Rlgl.TexCoord2f(1, previousV);
|
|
Rlgl.Vertex2f(prevPosNormal.X, prevPosNormal.Y);
|
|
|
|
Rlgl.TexCoord2f(1, v);
|
|
Rlgl.Vertex2f(currentPosNormal.X, currentPosNormal.Y);
|
|
|
|
Rlgl.TexCoord2f(0, v);
|
|
Rlgl.Vertex2f(currentNegNormal.X, currentNegNormal.Y);
|
|
|
|
Rlgl.End();
|
|
|
|
// The current step is the start of the neXt step
|
|
previous = current;
|
|
previousTangent = normal;
|
|
previousV = v;
|
|
}
|
|
}
|
|
|
|
static void UpdateOptions()
|
|
{
|
|
if (IsKeyPressed(KeyboardKey.KEY_SPACE))
|
|
{
|
|
showCurve = !showCurve;
|
|
}
|
|
|
|
// Update with
|
|
if (IsKeyPressed(KeyboardKey.KEY_EQUAL))
|
|
{
|
|
curveWidth += 2;
|
|
}
|
|
if (IsKeyPressed(KeyboardKey.KEY_MINUS))
|
|
{
|
|
curveWidth -= 2;
|
|
}
|
|
|
|
if (curveWidth < 2)
|
|
{
|
|
curveWidth = 2;
|
|
}
|
|
|
|
// Update segments
|
|
if (IsKeyPressed(KeyboardKey.KEY_LEFT))
|
|
{
|
|
curveSegments -= 2;
|
|
}
|
|
if (IsKeyPressed(KeyboardKey.KEY_RIGHT))
|
|
{
|
|
curveSegments += 2;
|
|
}
|
|
if (curveSegments < 2)
|
|
{
|
|
curveSegments = 2;
|
|
}
|
|
}
|
|
}
|