parent
0a38b0b740
commit
20e551029e
@ -0,0 +1,219 @@ |
|||||||
|
/*
|
||||||
|
OneLoneCoder.com - Splines Part 1 |
||||||
|
"Bendy Wavy Curly" - @Javidx9 |
||||||
|
|
||||||
|
Disclaimer |
||||||
|
~~~~~~~~~~ |
||||||
|
I don't care what you use this for. It's intended to be educational, and perhaps |
||||||
|
to the oddly minded - a little bit of fun. Please hack this, change it and use it |
||||||
|
in any way you see fit. BUT, you acknowledge that I am not responsible for anything |
||||||
|
bad that happens as a result of your actions. However, if good stuff happens, I |
||||||
|
would appreciate a shout out, or at least give the blog some publicity for me. |
||||||
|
Cheers! |
||||||
|
|
||||||
|
Background |
||||||
|
~~~~~~~~~~ |
||||||
|
Curvy things are always better. Splines are a nice way to approximate |
||||||
|
curves and loops for games. This video is the first of two parts |
||||||
|
demonstrating how Catmull-Rom splines can be implemented. |
||||||
|
|
||||||
|
Use Z + X to select a point and move it with the arrow keys |
||||||
|
Use A + S to move the agent around the spline loop |
||||||
|
|
||||||
|
Author |
||||||
|
~~~~~~ |
||||||
|
Twitter: @javidx9 |
||||||
|
Blog: www.onelonecoder.com |
||||||
|
|
||||||
|
Video: |
||||||
|
~~~~~~ |
||||||
|
https://youtu.be/9_aJGUTePYo
|
||||||
|
|
||||||
|
Last Updated: 06/08/2017 |
||||||
|
*/ |
||||||
|
#include <iostream> |
||||||
|
#include <string> |
||||||
|
using namespace std; |
||||||
|
|
||||||
|
#include "olcConsoleGameEngine.h" |
||||||
|
|
||||||
|
struct sPoint2D |
||||||
|
{ |
||||||
|
float x; |
||||||
|
float y; |
||||||
|
}; |
||||||
|
|
||||||
|
struct sSpline |
||||||
|
{ |
||||||
|
vector<sPoint2D> points; |
||||||
|
|
||||||
|
sPoint2D GetSplinePoint(float t, bool bLooped = false) |
||||||
|
{ |
||||||
|
int p0, p1, p2, p3; |
||||||
|
if (!bLooped) |
||||||
|
{ |
||||||
|
p1 = (int)t + 1; |
||||||
|
p2 = p1 + 1; |
||||||
|
p3 = p2 + 1; |
||||||
|
p0 = p1 - 1; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
p1 = (int)t; |
||||||
|
p2 = (p1 + 1) % points.size(); |
||||||
|
p3 = (p2 + 1) % points.size(); |
||||||
|
p0 = p1 >= 1 ? p1 - 1 : points.size() - 1; |
||||||
|
} |
||||||
|
|
||||||
|
t = t - (int)t; |
||||||
|
|
||||||
|
float tt = t * t; |
||||||
|
float ttt = tt * t; |
||||||
|
|
||||||
|
float q1 = -ttt + 2.0f*tt - t; |
||||||
|
float q2 = 3.0f*ttt - 5.0f*tt + 2.0f; |
||||||
|
float q3 = -3.0f*ttt + 4.0f*tt + t; |
||||||
|
float q4 = ttt - tt; |
||||||
|
|
||||||
|
float tx = 0.5f * (points[p0].x * q1 + points[p1].x * q2 + points[p2].x * q3 + points[p3].x * q4); |
||||||
|
float ty = 0.5f * (points[p0].y * q1 + points[p1].y * q2 + points[p2].y * q3 + points[p3].y * q4); |
||||||
|
|
||||||
|
return{ tx, ty }; |
||||||
|
} |
||||||
|
|
||||||
|
sPoint2D GetSplineGradient(float t, bool bLooped = false) |
||||||
|
{ |
||||||
|
int p0, p1, p2, p3; |
||||||
|
if (!bLooped) |
||||||
|
{ |
||||||
|
p1 = (int)t + 1; |
||||||
|
p2 = p1 + 1; |
||||||
|
p3 = p2 + 1; |
||||||
|
p0 = p1 - 1; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
p1 = (int)t; |
||||||
|
p2 = (p1 + 1) % points.size(); |
||||||
|
p3 = (p2 + 1) % points.size(); |
||||||
|
p0 = p1 >= 1 ? p1 - 1 : points.size() - 1; |
||||||
|
} |
||||||
|
|
||||||
|
t = t - (int)t; |
||||||
|
|
||||||
|
float tt = t * t; |
||||||
|
float ttt = tt * t; |
||||||
|
|
||||||
|
float q1 = -3.0f * tt + 4.0f*t - 1; |
||||||
|
float q2 = 9.0f*tt - 10.0f*t; |
||||||
|
float q3 = -9.0f*tt + 8.0f*t + 1.0f; |
||||||
|
float q4 = 3.0f*tt - 2.0f*t; |
||||||
|
|
||||||
|
float tx = 0.5f * (points[p0].x * q1 + points[p1].x * q2 + points[p2].x * q3 + points[p3].x * q4); |
||||||
|
float ty = 0.5f * (points[p0].y * q1 + points[p1].y * q2 + points[p2].y * q3 + points[p3].y * q4); |
||||||
|
|
||||||
|
return{ tx, ty }; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
class OneLoneCoder_Splines : public olcConsoleGameEngine |
||||||
|
{ |
||||||
|
public: |
||||||
|
OneLoneCoder_Splines() |
||||||
|
{ |
||||||
|
m_sAppName = L"Splines"; |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
sSpline path; |
||||||
|
int nSelectedPoint = 0; |
||||||
|
float fMarker = 0.0f; |
||||||
|
|
||||||
|
protected: |
||||||
|
// Called by olcConsoleGameEngine
|
||||||
|
virtual bool OnUserCreate() |
||||||
|
{ |
||||||
|
//path.points = { { 10, 41 },{ 40, 41 },{ 70, 41 },{ 100, 41 } };
|
||||||
|
path.points = { { 10, 41 },{ 20, 41 },{ 30, 41 },{ 40, 41 },{ 50, 41 },{ 60, 41 },{ 70, 41 },{ 80, 41 },{ 90, 41 },{ 100, 41 } }; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
// Called by olcConsoleGameEngine
|
||||||
|
virtual bool OnUserUpdate(float fElapsedTime) |
||||||
|
{ |
||||||
|
// Clear Screen
|
||||||
|
Fill(0, 0, ScreenWidth(), ScreenHeight(), L' '); |
||||||
|
|
||||||
|
// Handle input
|
||||||
|
if (m_keys[L'X'].bReleased) |
||||||
|
{ |
||||||
|
nSelectedPoint++; |
||||||
|
if (nSelectedPoint >= path.points.size()) |
||||||
|
nSelectedPoint = 0; |
||||||
|
} |
||||||
|
|
||||||
|
if (m_keys[L'Z'].bReleased) |
||||||
|
{ |
||||||
|
nSelectedPoint--; |
||||||
|
if (nSelectedPoint < 0) |
||||||
|
nSelectedPoint = path.points.size() - 1; |
||||||
|
} |
||||||
|
|
||||||
|
if (m_keys[VK_LEFT].bHeld) |
||||||
|
path.points[nSelectedPoint].x -= 30.0f * fElapsedTime; |
||||||
|
|
||||||
|
if (m_keys[VK_RIGHT].bHeld) |
||||||
|
path.points[nSelectedPoint].x += 30.0f * fElapsedTime; |
||||||
|
|
||||||
|
if (m_keys[VK_UP].bHeld) |
||||||
|
path.points[nSelectedPoint].y -= 30.0f * fElapsedTime; |
||||||
|
|
||||||
|
if (m_keys[VK_DOWN].bHeld) |
||||||
|
path.points[nSelectedPoint].y += 30.0f * fElapsedTime; |
||||||
|
|
||||||
|
if (m_keys[L'A'].bHeld) |
||||||
|
fMarker -= 5.0f * fElapsedTime; |
||||||
|
|
||||||
|
if (m_keys[L'S'].bHeld) |
||||||
|
fMarker += 5.0f * fElapsedTime; |
||||||
|
|
||||||
|
if (fMarker >= (float)path.points.size()) |
||||||
|
fMarker -= (float)path.points.size(); |
||||||
|
|
||||||
|
if (fMarker < 0.0f) |
||||||
|
fMarker += (float)path.points.size(); |
||||||
|
|
||||||
|
// Draw Spline
|
||||||
|
for (float t = 0; t < (float)path.points.size(); t += 0.005f) |
||||||
|
{ |
||||||
|
sPoint2D pos = path.GetSplinePoint(t, true); |
||||||
|
Draw(pos.x, pos.y); |
||||||
|
} |
||||||
|
|
||||||
|
// Draw Control Points
|
||||||
|
for (int i = 0; i < path.points.size(); i++) |
||||||
|
{ |
||||||
|
Fill(path.points[i].x - 1, path.points[i].y - 1, path.points[i].x + 2, path.points[i].y + 2, PIXEL_SOLID, FG_RED); |
||||||
|
DrawString(path.points[i].x, path.points[i].y, to_wstring(i)); |
||||||
|
} |
||||||
|
|
||||||
|
// Highlight control point
|
||||||
|
Fill(path.points[nSelectedPoint].x - 1, path.points[nSelectedPoint].y - 1, path.points[nSelectedPoint].x + 2, path.points[nSelectedPoint].y + 2, PIXEL_SOLID, FG_YELLOW); |
||||||
|
DrawString(path.points[nSelectedPoint].x, path.points[nSelectedPoint].y, to_wstring(nSelectedPoint)); |
||||||
|
|
||||||
|
// Draw agent to demonstrate gradient
|
||||||
|
sPoint2D p1 = path.GetSplinePoint(fMarker, true); |
||||||
|
sPoint2D g1 = path.GetSplineGradient(fMarker, true); |
||||||
|
float r = atan2(-g1.y, g1.x); |
||||||
|
DrawLine(5.0f * sin(r) + p1.x, 5.0f * cos(r) + p1.y, -5.0f * sin(r) + p1.x, -5.0f * cos(r) + p1.y, PIXEL_SOLID, FG_BLUE); |
||||||
|
return true; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
int main() |
||||||
|
{ |
||||||
|
OneLoneCoder_Splines demo; |
||||||
|
demo.ConstructConsole(160, 80, 10, 10); |
||||||
|
demo.Start(); |
||||||
|
return 0; |
||||||
|
} |
Loading…
Reference in new issue