diff --git a/OneLoneCoder_Splines1.cpp b/OneLoneCoder_Splines1.cpp new file mode 100644 index 0000000..35f1204 --- /dev/null +++ b/OneLoneCoder_Splines1.cpp @@ -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 +#include +using namespace std; + +#include "olcConsoleGameEngine.h" + +struct sPoint2D +{ + float x; + float y; +}; + +struct sSpline +{ + vector 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; +} \ No newline at end of file diff --git a/olcConsoleGameEngine.h b/olcConsoleGameEngine.h index 54e9ced..a80fdc7 100644 --- a/olcConsoleGameEngine.h +++ b/olcConsoleGameEngine.h @@ -117,7 +117,9 @@ enum COLOUR enum PIXEL_TYPE { PIXEL_SOLID = 0x2588, + PIXEL_THREEQUARTERS = 0x2593 PIXEL_HALF = 0x2592, + PIXEL_QUARTER = 0x2591, };