From ea91e8d47676b28fa10ea05ab35d0bb8ac48593d Mon Sep 17 00:00:00 2001 From: Ben Date: Fri, 1 Feb 2019 16:29:47 +0000 Subject: [PATCH] Simple pendulum --- C++/Mandelbrot explorer/main.cpp | 8 +- C++/Mandelbrot explorer/output.o | Bin 0 -> 167584 bytes C++/Simple pendulum/.vscode/settings.json | 6 + C++/Simple pendulum/main.cpp | 101 + C++/Simple pendulum/maths.h | 223 +++ C++/Simple pendulum/olcPixelGameEngine.h | 2069 +++++++++++++++++++++ C++/Simple pendulum/output.o | Bin 0 -> 165928 bytes C++/Simple pendulum/rect.cpp | 164 ++ C++/Simple pendulum/rect.h | 73 + 9 files changed, 2639 insertions(+), 5 deletions(-) create mode 100755 C++/Mandelbrot explorer/output.o create mode 100644 C++/Simple pendulum/.vscode/settings.json create mode 100644 C++/Simple pendulum/main.cpp create mode 100644 C++/Simple pendulum/maths.h create mode 100644 C++/Simple pendulum/olcPixelGameEngine.h create mode 100755 C++/Simple pendulum/output.o create mode 100644 C++/Simple pendulum/rect.cpp create mode 100644 C++/Simple pendulum/rect.h diff --git a/C++/Mandelbrot explorer/main.cpp b/C++/Mandelbrot explorer/main.cpp index 2c15cc6..aab5a09 100644 --- a/C++/Mandelbrot explorer/main.cpp +++ b/C++/Mandelbrot explorer/main.cpp @@ -33,10 +33,8 @@ public: Zre = tX; iteration++; } - long double ratio1 = (long double)iteration - std::log2(std::log2(Zre*Zre + Zim*Zim)) / m_maxIterations; - long double ratio2 = (long double)iteration - std::log(std::log(Zre*Zre * Zim*Zim)) / m_maxIterations; - long double ratio3 = (long double)iteration - std::log10(std::log10(Zre*Zre - Zim*Zim)) / m_maxIterations; - olc::Pixel colour = olc::Pixel(ratio1 + 255.0f, ratio2 + 255.0f, ratio3 + 255.0f); + long double ratio = (long double)iteration - std::log2(std::log2(Zre*Zre + Zim*Zim)) / m_maxIterations; + olc::Pixel colour = olc::Pixel(ratio + 255.0f, ratio + 255.0f, ratio + 255.0f); DrawRect(pX, pY, 1, 1, colour); } } @@ -61,7 +59,7 @@ private: long double m_x = 1.0f; long double m_y = 1.0f; long double m_scale = 0.7f; - int m_maxIterations = 15; + int m_maxIterations = 100; std::map m_palete; }; diff --git a/C++/Mandelbrot explorer/output.o b/C++/Mandelbrot explorer/output.o new file mode 100755 index 0000000000000000000000000000000000000000..54deadfe54a8c9c0d47f3e66fc5b73efeb90ca89 GIT binary patch literal 167584 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 Z%a}Lo)%P66 +#include +#include + +#include "rect.h" + +#define OLC_PGE_APPLICATION +#include "olcPixelGameEngine.h" + +#define PI 3.14159265359 + +class SimplePendulum : public olc::PixelGameEngine { +public: + SimplePendulum() { + sAppName = "Simple Pendulum"; + } + + bool OnUserCreate() override { + return true; + } + + bool OnUserUpdate(float fElapsedTime) override { + /* START CALCULATONS */ + if (!m_isStopped) { + m_aAccel = ((-1 * m_gravity / m_pLength) * sin(m_theta) * m_pMass) * fElapsedTime; + + m_aVelocity += m_aAccel *= m_pDamping; + m_theta += m_aVelocity; + } + + m_x = m_pLength * sin(m_theta) + m_pivotX; + m_y = m_pLength * cos(m_theta) + m_pivotY; + /* END CALCULATIONS */ + draw(); + + std::stringstream angularVel; + angularVel << "Angular velocity: " << m_aVelocity; + DrawString(0, 0, angularVel.str(), olc::BLACK); + std::stringstream angularAccel; + angularAccel << "Angular acceleration: " << m_aAccel; + DrawString(0, 10, angularAccel.str(), olc::BLACK); + std::stringstream coords; + coords << "Pendulum bob pos X:" << m_x << " Y: " << m_y; + DrawString(0, 20, coords.str(), olc::BLACK); + + Rect p; + p.SetRect(m_x - 40, m_y - 40, 80, 80); + if (p.Contains(Vec2(GetMouseX(), GetMouseY()))) { + DrawRect(p.x, p.y, p.w, p.h, olc::DARK_MAGENTA); + if (GetMouse(0).bHeld) { + m_isStopped = true; + m_theta = atan2(GetMouseX() - m_pivotX, GetMouseY() - m_pivotY); + m_aAccel = 0.0f; + m_aVelocity = 0.0f; + } else { + m_isStopped = false; + } + } else { + DrawRect(p.x, p.y, p.w, p.h, olc::RED); + m_isStopped = false; + } + + return true; + } + + void draw() { + Clear(olc::WHITE); + FillCircle(m_pivotX, m_pivotY, 8, olc::RED); + DrawLine(m_pivotX, m_pivotY, m_x, m_y, olc::BLACK); + if (!m_isStopped) { + FillCircle(m_x, m_y, 20, olc::BLACK); + } else { + FillCircle(m_x, m_y, 20, olc::DARK_GREY); + } + } +private: + bool m_isStopped = false; + float m_theta = PI / 4; + + float m_pivotX = 500; + float m_pivotY = 20; + float m_x = 500; + float m_y = 220; + + float m_pLength = 300; + + float m_aVelocity = 0.0f; + float m_aAccel = 0.0f; + + float m_gravity = 9.81; + float m_pMass = 5; + + float m_pDamping = 0.9; +}; + +int main(int argc, char** argv) { + SimplePendulum app; + if (app.Construct(1000, 600, 1, 1)) + app.Start(); + return 0; +} diff --git a/C++/Simple pendulum/maths.h b/C++/Simple pendulum/maths.h new file mode 100644 index 0000000..20aa1fb --- /dev/null +++ b/C++/Simple pendulum/maths.h @@ -0,0 +1,223 @@ +#ifndef MATH_H_ +#define MATH_H_ + +const float DEG2RAD = 0.01745329251994329576923690768f; +const float RAD2DEG = 57.2957795130823208767981548141f; + +inline float ToRadian(const float Degree) { + return (Degree * DEG2RAD); +} + +inline float ToDegree(const float Radian) { + return (Radian * RAD2DEG); +} + +template +struct Vec4 { + T x, y, z, w; + template + Vec4(P x, P y, P z, P w) : x(x), y(y), z(z), w(w) {} + template + Vec4(P all) : x(all), y(all), z(all), w(all) {} + Vec4() : x(0), y(0), z(0), w(0) {} + inline Vec4& dot(const Vec4& v) { + return (x * v.x + y * v.y + z * v.z + w * v.w); + } + inline const Vec4& operator+() { + return *this; + } + inline Vec4& operator-() { + return Vec4(-x, -y, -z, -w); + } + inline Vec4& operator+(const Vec4& v) { + return new Vec4(x + v.x, y + v.y, z + v.z, w + v.w); + } + inline Vec4& operator-(const Vec4& v) { + return new Vec4(x - v.x, y - v.y, z - v.z, w - v.w); + } + inline Vec4& operator*(const Vec4& v) { + return new Vec4(x * v.x, y * v.y, z * v.z, w * v.w); + } + inline Vec4& operator/(const Vec4& v) { + return new Vec4(x / v.x, y / v.y, z / v.z, w / v.w); + } + inline Vec4& operator+=(const Vec4& v) { + x+=v.x; y+=v.y; z+=v.z; w+=v.w; + return *this; + } + inline Vec4& operator-=(const Vec4& v) { + x-=v.x; y-=v.y; z-=v.z; w-=v.w; + return *this; + } + inline Vec4& operator*=(const Vec4& v) { + x*=v.x; y*=v.y; z*=v.z; w*=v.w; + return *this; + } + inline Vec4& operator/=(const Vec4& v) { + x/=v.x; y/=v.y; z/=v.z; w/=v.w; + return *this; + } + template + inline Vec4& operator+=(P s) { + x+=s; y+=s; z+=s; w+=s; + return *this; + } + template + inline Vec4& operator-=(P s) { + x-=s; y-=s; z-=s; w-=s; + return *this; + } + template + inline Vec4& operator*=(P s) { + x*=s; y*=s; z*=s; w*=s; + return *this; + } + template + inline Vec4& operator/=(P s) { + x/=s; y/=s; z/=s; w/=s; + return *this; + } +}; + +template +struct Vec3 { + T x, y, z; + template + Vec3(P x, P y, P z) : x(x), y(y), z(z) {} + template + Vec3(P all) : x(all), y(all), z(all) {} + Vec3() : x(0), y(0), z(0) {} + inline Vec3& cross(const Vec3& v) { + return new Vec3( + (y * v.z - z * v.y), + (x * v.z - z * v.x), + (x * v.y - y * v.x) + ); + } + inline Vec3& dot(const Vec3& v) { + return (x * v.x + y * v.y + z * v.z); + } + inline const Vec3& operator+() { + return *this; + } + inline Vec3& operator-() { + return Vec3(-x, -y, -z); + } + inline Vec3& operator+(const Vec3& v) { + return new Vec3(x + v.x, y + v.y, z + v.z); + } + inline Vec3& operator-(const Vec3& v) { + return new Vec3(x - v.x, y - v.y, z - v.z); + } + inline Vec3& operator*(const Vec3& v) { + return new Vec3(x * v.x, y * v.y, z * v.z); + } + inline Vec3& operator/(const Vec3& v) { + return new Vec3(x / v.x, y / v.y, z / v.z); + } + inline Vec3& operator+=(const Vec3& v) { + x+=v.x; y+=v.y; z+=v.z; + return *this; + } + inline Vec3& operator-=(const Vec3& v) { + x-=v.x; y-=v.y; z-=v.z; + return *this; + } + inline Vec3& operator*=(const Vec3& v) { + x*=v.x; y*=v.y; z*=v.z; + return *this; + } + inline Vec3& operator/=(const Vec3& v) { + x/=v.x; y/=v.y; z/=v.z; + return *this; + } + template + inline Vec3& operator+=(P s) { + x+=s; y+=s; z+=s; + return *this; + } + template + inline Vec3& operator-=(P s) { + x-=s; y-=s; z-=s; + return *this; + } + template + inline Vec3& operator*=(P s) { + x*=s; y*=s; z*=s; + return *this; + } + template + inline Vec3& operator/=(P s) { + x/=s; y/=s; z/=s; + return *this; + } +}; + +template +struct Vec2 { + T x, y; + template + Vec2(P x, P y) : x(x), y(y) {} + template + Vec2(P all) : x(all), y(all) {} + Vec2() : x(0), y(0) {} + inline const Vec2& operator+() { + return *this; + } + inline Vec2& dot(const Vec3& v) { + return (x * v.x + y * v.y); + } + inline Vec2& operator-() { + return Vec3(-x, -y); + } + inline Vec2& operator+(const Vec2& v) { + return new Vec2(x + v.x, y + v.y); + } + inline Vec2& operator-(const Vec2& v) { + return new Vec2(x - v.x, y - v.y); + } + inline Vec2& operator*(const Vec2& v) { + return new Vec2(x * v.x, y * v.y); + } + inline Vec2& operator/(const Vec2& v) { + return new Vec2(x / v.x, y / v.y); + } + inline Vec2& operator+=(const Vec2& v) { + x+=v.x; y+=v.y; + return *this; + } + inline Vec2& operator-=(const Vec2& v) { + x-=v.x; y-=v.y; + return *this; + } + inline Vec2& operator*=(const Vec2& v) { + x*=v.x; y*=v.y; + return *this; + } + inline Vec2& operator/=(const Vec2& v) { + x/=v.x; y/=v.y; + return *this; + } + template + inline Vec2& operator+=(P s) { + x+=s; y+=s; + return *this; + } + template + inline Vec2& operator-=(P s) { + x-=s; y-=s; + return *this; + } + template + inline Vec2& operator*=(P s) { + x*=s; y*=s; + return *this; + } + template + inline Vec2& operator/=(P s) { + x/=s; y/=s; + return *this; + } +}; + +#endif diff --git a/C++/Simple pendulum/olcPixelGameEngine.h b/C++/Simple pendulum/olcPixelGameEngine.h new file mode 100644 index 0000000..4458a61 --- /dev/null +++ b/C++/Simple pendulum/olcPixelGameEngine.h @@ -0,0 +1,2069 @@ +/* + olcPixelGameEngine.h + + +-------------------------------------------------------------+ + | OneLoneCoder Pixel Game Engine v1.12 | + | "Like the command prompt console one, but not..." - javidx9 | + +-------------------------------------------------------------+ + + What is this? + ~~~~~~~~~~~~~ + The olcConsoleGameEngine has been a surprsing and wonderful + success for me, and I'm delighted how people have reacted so + positively towards it, so thanks for that. + + However, there are limitations that I simply cannot avoid. + Firstly, I need to maintain several different versions of + it to accommodate users on Windows7, 8, 10, Linux, Mac, + Visual Studio & Code::Blocks. Secondly, this year I've been + pushing the console to the limits of its graphical capabilities + and the effect is becoming underwhelming. The engine itself + is not slow at all, but the process that Windows uses to + draw the command prompt to the screen is, and worse still, + it's dynamic based upon the variation of character colours + and glyphs. Sadly I have no control over this, and recent + videos that are extremely graphical (for a command prompt :P ) + have been dipping to unacceptable framerates. As the channel + has been popular with aspiring game developers, I'm concerned + that the visual appeal of the command prompt is perhaps + limited to us oldies, and I dont want to alienate younger + learners. Finally, I'd like to demonstrate many more + algorithms and image processing that exist in the graphical + domain, for which the console is insufficient. + + For this reason, I have created olcPixelGameEngine! The look + and feel to the programmer is almost identical, so all of my + existing code from the videos is easily portable, and the + programmer uses this file in exactly the same way. But I've + decided that rather than just build a command prompt emulator, + that I would at least harness some modern(ish) portable + technologies. + + As a result, the olcPixelGameEngine supports 32-bit colour, is + written in a cross-platform style, uses modern(ish) C++ + conventions and most importantly, renders much much faster. I + will use this version when my applications are predominantly + graphics based, but use the console version when they are + predominantly text based - Don't worry, loads more command + prompt silliness to come yet, but evolution is important!! + + License (OLC-3) + ~~~~~~~~~~~~~~~ + + Copyright 2018 OneLoneCoder.com + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions or derivations of source code must retain the above + copyright notice, this list of conditions and the following disclaimer. + + 2. Redistributions or derivative works in binary form must reproduce + the above copyright notice. This list of conditions and the following + disclaimer must be reproduced in the documentation and/or other + materials provided with the distribution. + + 3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + Links + ~~~~~ + YouTube: https://www.youtube.com/javidx9 + Discord: https://discord.gg/WhwHUMV + Twitter: https://www.twitter.com/javidx9 + Twitch: https://www.twitch.tv/javidx9 + GitHub: https://www.github.com/onelonecoder + Homepage: https://www.onelonecoder.com + Patreon: https://www.patreon.com/javidx9 + + Relevant Videos + ~~~~~~~~~~~~~~~ + https://youtu.be/kRH6oJLFYxY Introducing olcPixelGameEngine + + Compiling in Linux + ~~~~~~~~~~~~~~~~~~ + You will need a modern C++ compiler, so update yours! + To compile use the command: + + g++ -o YourProgName YourSource.cpp -lX11 -lGL -lpthread -lpng + + On some Linux configurations, the frame rate is locked to the refresh + rate of the monitor. This engine tries to unlock it but may not be + able to, in which case try launching your program like this: + + vblank_mode=0 ./YourProgName + + + Compiling in Code::Blocks on Windows + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Well I wont judge you, but make sure your Code::Blocks installation + is really up to date - you may even consider updating your C++ toolchain + to use MinGW32-W64, so google this. You will also need to enable C++14 + in your build options, and add to your linker the following libraries: + user32 gdi32 opengl32 gdiplus + + Thanks + ~~~~~~ + I'd like to extend thanks to Eremiell, slavka, gurkanctn, Phantim, + JackOJC, KrossX, Huhlig, Dragoneye, Appa, JustinRichardsMusic, SliceNDice + Ralakus, Gorbit99, raoul & MagetzUb for advice, ideas and testing, and I'd like + to extend my appreciation to the 23K YouTube followers and 1.5K Discord server + members who give me the motivation to keep going with all this :D + + Special thanks to those who bring gifts! + GnarGnarHead.......Domina + Gorbit99...........Bastion + + Special thanks to my Patreons too - I wont name you on here, but I've + certainly enjoyed my tea and flapjacks :D + + Author + ~~~~~~ + David Barr, aka javidx9, ©OneLoneCoder 2018, 2019 +*/ + +////////////////////////////////////////////////////////////////////////////////////////// + +/* Example Usage (main.cpp) + #define OLC_PGE_APPLICATION + #include "olcPixelGameEngine.h" + // Override base class with your custom functionality + class Example : public olc::PixelGameEngine + { + public: + Example() + { + sAppName = "Example"; + } + public: + bool OnUserCreate() override + { + // Called once at the start, so create things here + return true; + } + bool OnUserUpdate(float fElapsedTime) override + { + // called once per frame, draws random coloured pixels + for (int x = 0; x < ScreenWidth(); x++) + for (int y = 0; y < ScreenHeight(); y++) + Draw(x, y, olc::Pixel(rand() % 255, rand() % 255, rand()% 255)); + return true; + } + }; + int main() + { + Example demo; + if (demo.Construct(256, 240, 4, 4)) + demo.Start(); + return 0; + } +*/ + +#ifndef OLC_PGE_DEF +#define OLC_PGE_DEF + +#ifdef _WIN32 + // Link to libraries +#ifndef __MINGW32__ + #pragma comment(lib, "user32.lib") // Visual Studio Only + #pragma comment(lib, "gdi32.lib") // For other Windows Compilers please add + #pragma comment(lib, "opengl32.lib") // these libs to your linker input + #pragma comment(lib, "gdiplus.lib") +#else + // In Code::Blocks, Select C++14 in your build options, and add the + // following libs to your linker: user32 gdi32 opengl32 gdiplus +#endif + // Include WinAPI + #include + #include + + // OpenGL Extension + #include + typedef BOOL(WINAPI wglSwapInterval_t) (int interval); + static wglSwapInterval_t *wglSwapInterval; +#else + #include + #include + #include + #include + #include + typedef int(glSwapInterval_t) (Display *dpy, GLXDrawable drawable, int interval); + static glSwapInterval_t *glSwapIntervalEXT; +#endif + + +// Standard includes +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#undef min +#undef max + +namespace olc // All OneLoneCoder stuff will now exist in the "olc" namespace +{ + struct Pixel + { + union + { + uint32_t n = 0xFF000000; + struct + { + uint8_t r; uint8_t g; uint8_t b; uint8_t a; + }; + }; + + Pixel(); + Pixel(uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha = 255); + Pixel(uint32_t p); + enum Mode { NORMAL, MASK, ALPHA, CUSTOM }; + }; + + // Some constants for symbolic naming of Pixels + static const Pixel + WHITE(255, 255, 255), + GREY(192, 192, 192), DARK_GREY(128, 128, 128), VERY_DARK_GREY(64, 64, 64), + RED(255, 0, 0), DARK_RED(128, 0, 0), VERY_DARK_RED(64, 0, 0), + YELLOW(255, 255, 0), DARK_YELLOW(128, 128, 0), VERY_DARK_YELLOW(64, 64, 0), + GREEN(0, 255, 0), DARK_GREEN(0, 128, 0), VERY_DARK_GREEN(0, 64, 0), + CYAN(0, 255, 255), DARK_CYAN(0, 128, 128), VERY_DARK_CYAN(0, 64, 64), + BLUE(0, 0, 255), DARK_BLUE(0, 0, 128), VERY_DARK_BLUE(0, 0, 64), + MAGENTA(255, 0, 255), DARK_MAGENTA(128, 0, 128), VERY_DARK_MAGENTA(64, 0, 64), + BLACK(0, 0, 0), + BLANK(0, 0, 0, 0); + + enum rcode + { + FAIL = 0, + OK = 1, + NO_FILE = -1, + }; + + //============================================================= + + struct HWButton + { + bool bPressed = false; // Set once during the frame the event occurs + bool bReleased = false; // Set once during the frame the event occurs + bool bHeld = false; // Set tru for all frames between pressed and released events + }; + + //============================================================= + + class ResourcePack + { + public: + ResourcePack(); + // ~ResourcePack(); + struct sEntry : public std::streambuf { + uint32_t nID, nFileOffset, nFileSize; uint8_t* data; void _config() { this->setg((char*)data, (char*)data, (char*)(data + nFileSize)); } + }; + + public: + olc::rcode AddToPack(std::string sFile); + + public: + olc::rcode SavePack(std::string sFile); + olc::rcode LoadPack(std::string sFile); + olc::rcode ClearPack(); + + public: + olc::ResourcePack::sEntry GetStreamBuffer(std::string sFile); + + private: + + std::map mapFiles; + }; + + //============================================================= + + // A bitmap-like structure that stores a 2D array of Pixels + class Sprite + { + public: + Sprite(); + Sprite(std::string sImageFile); + Sprite(std::string sImageFile, olc::ResourcePack *pack); + Sprite(int32_t w, int32_t h); + ~Sprite(); + + public: + olc::rcode LoadFromFile(std::string sImageFile, olc::ResourcePack *pack = nullptr); + olc::rcode LoadFromPGESprFile(std::string sImageFile, olc::ResourcePack *pack = nullptr); + olc::rcode SaveToPGESprFile(std::string sImageFile); + + public: + int32_t width = 0; + int32_t height = 0; + enum Mode { NORMAL, PERIODIC }; + + public: + void SetSampleMode(olc::Sprite::Mode mode = olc::Sprite::Mode::NORMAL); + Pixel GetPixel(int32_t x, int32_t y); + void SetPixel(int32_t x, int32_t y, Pixel p); + Pixel Sample(float x, float y); + Pixel* GetData(); + + private: + Pixel *pColData = nullptr; + Mode modeSample = Mode::NORMAL; + +#ifdef OLC_DBG_OVERDRAW + public: + static int nOverdrawCount; +#endif + + }; + + //============================================================= + + enum Key + { + A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, + K0, K1, K2, K3, K4, K5, K6, K7, K8, K9, + F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, + UP, DOWN, LEFT, RIGHT, + SPACE, TAB, SHIFT, CTRL, INS, DEL, HOME, END, PGUP, PGDN, + BACK, ESCAPE, RETURN, ENTER, PAUSE, SCROLL, + NP0, NP1, NP2, NP3, NP4, NP5, NP6, NP7, NP8, NP9, + NP_MUL, NP_DIV, NP_ADD, NP_SUB, NP_DECIMAL, + }; + + + //============================================================= + + class PixelGameEngine + { + public: + PixelGameEngine(); + + public: + olc::rcode Construct(uint32_t screen_w, uint32_t screen_h, uint32_t pixel_w, uint32_t pixel_h); + olc::rcode Start(); + + public: // Override Interfaces + // Called once on application startup, use to load your resources + virtual bool OnUserCreate(); + // Called every frame, and provides you with a time per frame value + virtual bool OnUserUpdate(float fElapsedTime); + // Called once on application termination, so you can be a clean coder + virtual bool OnUserDestroy(); + + public: // Hardware Interfaces + // Returns true if window is currently in focus + bool IsFocused(); + // Get the state of a specific keyboard button + HWButton GetKey(Key k); + // Get the state of a specific mouse button + HWButton GetMouse(uint32_t b); + // Get Mouse X coordinate in "pixel" space + int32_t GetMouseX(); + // Get Mouse Y coordinate in "pixel" space + int32_t GetMouseY(); + + public: // Utility + // Returns the width of the screen in "pixels" + int32_t ScreenWidth(); + // Returns the height of the screen in "pixels" + int32_t ScreenHeight(); + // Returns the width of the currently selected drawing target in "pixels" + int32_t GetDrawTargetWidth(); + // Returns the height of the currently selected drawing target in "pixels" + int32_t GetDrawTargetHeight(); + // Returns the currently active draw target + Sprite* GetDrawTarget(); + + public: // Draw Routines + // Specify which Sprite should be the target of drawing functions, use nullptr + // to specify the primary screen + void SetDrawTarget(Sprite *target); + // Change the pixel mode for different optimisations + // olc::Pixel::NORMAL = No transparency + // olc::Pixel::MASK = Transparent if alpha is < 255 + // olc::Pixel::ALPHA = Full transparency + void SetPixelMode(Pixel::Mode m); + Pixel::Mode GetPixelMode(); + // Use a custom blend function + void SetPixelMode(std::function pixelMode); + // Change the blend factor form between 0.0f to 1.0f; + void SetPixelBlend(float fBlend); + // Offset texels by sub-pixel amount (advanced, do not use) + void SetSubPixelOffset(float ox, float oy); + + // Draws a single Pixel + virtual void Draw(int32_t x, int32_t y, Pixel p = olc::WHITE); + // Draws a line from (x1,y1) to (x2,y2) + void DrawLine(int32_t x1, int32_t y1, int32_t x2, int32_t y2, Pixel p = olc::WHITE); + // Draws a circle located at (x,y) with radius + void DrawCircle(int32_t x, int32_t y, int32_t radius, Pixel p = olc::WHITE); + // Fills a circle located at (x,y) with radius + void FillCircle(int32_t x, int32_t y, int32_t radius, Pixel p = olc::WHITE); + // Draws a rectangle at (x,y) to (x+w,y+h) + void DrawRect(int32_t x, int32_t y, int32_t w, int32_t h, Pixel p = olc::WHITE); + // Fills a rectangle at (x,y) to (x+w,y+h) + void FillRect(int32_t x, int32_t y, int32_t w, int32_t h, Pixel p = olc::WHITE); + // Draws a triangle between points (x1,y1), (x2,y2) and (x3,y3) + void DrawTriangle(int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t x3, int32_t y3, Pixel p = olc::WHITE); + // Flat fills a triangle between points (x1,y1), (x2,y2) and (x3,y3) + void FillTriangle(int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t x3, int32_t y3, Pixel p = olc::WHITE); + // Draws an entire sprite at location (x,y) + void DrawSprite(int32_t x, int32_t y, Sprite *sprite, uint32_t scale = 1); + // Draws an area of a sprite at location (x,y), where the + // selected area is (ox,oy) to (ox+w,oy+h) + void DrawPartialSprite(int32_t x, int32_t y, Sprite *sprite, int32_t ox, int32_t oy, int32_t w, int32_t h, uint32_t scale = 1); + // Draws a single line of text + void DrawString(int32_t x, int32_t y, std::string sText, Pixel col = olc::WHITE, uint32_t scale = 1); + // Clears entire draw target to Pixel + void Clear(Pixel p); + + public: // Branding + std::string sAppName; + + private: // Inner mysterious workings + Sprite *pDefaultDrawTarget = nullptr; + Sprite *pDrawTarget = nullptr; + Pixel::Mode nPixelMode = Pixel::NORMAL; + float fBlendFactor = 1.0f; + uint32_t nScreenWidth = 256; + uint32_t nScreenHeight = 240; + uint32_t nPixelWidth = 4; + uint32_t nPixelHeight = 4; + int32_t nMousePosX = 0; + int32_t nMousePosY = 0; + float fPixelX = 1.0f; + float fPixelY = 1.0f; + float fSubPixelOffsetX = 0.0f; + float fSubPixelOffsetY = 0.0f; + bool bHasInputFocus = false; + bool bHasMouseFocus = false; + float fFrameTimer = 1.0f; + int nFrameCount = 0; + Sprite *fontSprite = nullptr; + std::function funcPixelMode; + + static std::map mapKeys; + bool pKeyNewState[256]{ 0 }; + bool pKeyOldState[256]{ 0 }; + HWButton pKeyboardState[256]; + + bool pMouseNewState[5]{ 0 }; + bool pMouseOldState[5]{ 0 }; + HWButton pMouseState[5]; + +#ifdef _WIN32 + HDC glDeviceContext = nullptr; + HGLRC glRenderContext = nullptr; +#else + GLXContext glDeviceContext = nullptr; + GLXContext glRenderContext = nullptr; +#endif + GLuint glBuffer; + + void EngineThread(); + + // If anything sets this flag to false, the engine + // "should" shut down gracefully + static std::atomic bAtomActive; + + // Common initialisation functions + void olc_UpdateMouse(int32_t x, int32_t y); + bool olc_OpenGLCreate(); + void olc_ConstructFontSheet(); + +#ifdef _WIN32 + // Windows specific window handling + HWND olc_hWnd = nullptr; + HWND olc_WindowCreate(); + std::wstring wsAppName; + static LRESULT CALLBACK olc_WindowEvent(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); +#else + // Non-Windows specific window handling + Display* olc_Display = nullptr; + Window olc_WindowRoot; + Window olc_Window; + XVisualInfo* olc_VisualInfo; + Colormap olc_ColourMap; + XSetWindowAttributes olc_SetWindowAttribs; + Display* olc_WindowCreate(); +#endif + + }; + + + class PGEX + { + friend class olc::PixelGameEngine; + protected: + static PixelGameEngine* pge; + }; + + //============================================================= +} + +#endif // OLC_PGE_DEF + + + + +/* + Object Oriented Mode + ~~~~~~~~~~~~~~~~~~~~ + + If the olcPixelGameEngine.h is called from several sources it can cause + multiple definitions of objects. To prevent this, ONLY ONE of the pathways + to including this file must have OLC_PGE_APPLICATION defined before it. This prevents + the definitions being duplicated. + + Consider the following project structure: + + Class1.h - Includes olcPixelGameEngine.h, overrides olc::PixelGameEngine + Class1.cpp - #define OLC_PGE_APPLICATION #include "Class1.h" + Class2.h - Includes Class1.h, which includes olcPixelGameEngine.h + Class2.cpp - #define OLC_PGE_APPLICATION #include "Class2.h" + main.cpp - Includes Class1.h and Class2.h + + If all of this is a bit too confusing, you can split this file in two! + Everything below this comment block can go into olcPixelGameEngineOOP.cpp + and everything above it can go into olcPixelGameEngineOOP.h + +*/ + +#ifdef OLC_PGE_APPLICATION +#undef OLC_PGE_APPLICATION + +namespace olc +{ + Pixel::Pixel() + { + r = 0; g = 0; b = 0; a = 255; + } + + Pixel::Pixel(uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha) + { + r = red; g = green; b = blue; a = alpha; + } + + Pixel::Pixel(uint32_t p) + { + n = p; + } + + //========================================================== + +// std::wstring ConvertS2W(std::string s) +// { +// #ifdef _WIN32 +// int count = MultiByteToWideChar(CP_UTF8, 0, s.c_str(), -1, NULL, 0); +// wchar_t* buffer = new wchar_t[count]; +// MultiByteToWideChar(CP_UTF8, 0, s.c_str(), -1, buffer, count); +// std::wstring w(buffer); +// delete[] buffer; +// return w; +// #endif +//#ifdef __MINGW32__ +// wchar_t *buffer = new wchar_t[sImageFile.length() + 1]; +// mbstowcs(buffer, sImageFile.c_str(), sImageFile.length()); +// buffer[sImageFile.length()] = L'\0'; +// wsImageFile = buffer; +// delete[] buffer; +//#else + // } + + Sprite::Sprite() + { + pColData = nullptr; + width = 0; + height = 0; + } + + Sprite::Sprite(std::string sImageFile) + { + LoadFromFile(sImageFile); + } + + Sprite::Sprite(std::string sImageFile, olc::ResourcePack *pack) + { + LoadFromPGESprFile(sImageFile, pack); + } + + Sprite::Sprite(int32_t w, int32_t h) + { + if(pColData) delete[] pColData; + width = w; height = h; + pColData = new Pixel[width * height]; + for (int32_t i = 0; i < width*height; i++) + pColData[i] = Pixel(); + } + + Sprite::~Sprite() + { + if (pColData) delete pColData; + } + + olc::rcode Sprite::LoadFromPGESprFile(std::string sImageFile, olc::ResourcePack *pack) + { + if (pColData) delete[] pColData; + + auto ReadData = [&](std::istream &is) + { + is.read((char*)&width, sizeof(int32_t)); + is.read((char*)&height, sizeof(int32_t)); + pColData = new Pixel[width * height]; + is.read((char*)pColData, width * height * sizeof(uint32_t)); + }; + + // These are essentially Memory Surfaces represented by olc::Sprite + // which load very fast, but are completely uncompressed + if (pack == nullptr) + { + std::ifstream ifs; + ifs.open(sImageFile, std::ifstream::binary); + if (ifs.is_open()) + { + ReadData(ifs); + return olc::OK; + } + else + return olc::FAIL; + } + else + { + auto streamBuffer = pack->GetStreamBuffer(sImageFile); + std::istream is(&streamBuffer); + ReadData(is); + } + + + return olc::FAIL; + } + + olc::rcode Sprite::SaveToPGESprFile(std::string sImageFile) + { + if (pColData == nullptr) return olc::FAIL; + + std::ofstream ofs; + ofs.open(sImageFile, std::ifstream::binary); + if (ofs.is_open()) + { + ofs.write((char*)&width, sizeof(int32_t)); + ofs.write((char*)&height, sizeof(int32_t)); + ofs.write((char*)pColData, width*height*sizeof(uint32_t)); + ofs.close(); + return olc::OK; + } + + return olc::FAIL; + } + + olc::rcode Sprite::LoadFromFile(std::string sImageFile, olc::ResourcePack *pack) + { +#ifdef _WIN32 + // Use GDI+ + std::wstring wsImageFile; +#ifdef __MINGW32__ + wchar_t *buffer = new wchar_t[sImageFile.length() + 1]; + mbstowcs(buffer, sImageFile.c_str(), sImageFile.length()); + buffer[sImageFile.length()] = L'\0'; + wsImageFile = buffer; + delete [] buffer; +#else + // wsImageFile = ConvertS2W(sImageFile); +#endif + Gdiplus::Bitmap *bmp = Gdiplus::Bitmap::FromFile(wsImageFile.c_str()); + if (bmp == nullptr) + return olc::NO_FILE; + + width = bmp->GetWidth(); + height = bmp->GetHeight(); + pColData = new Pixel[width * height]; + + for(int x=0; xGetPixel(x, y, &c); + SetPixel(x, y, Pixel(c.GetRed(), c.GetGreen(), c.GetBlue(), c.GetAlpha())); + } + delete bmp; + return olc::OK; +#else + //////////////////////////////////////////////////////////////////////////// + // Use libpng, Thanks to Guillaume Cottenceau + // https://gist.github.com/niw/5963798 + png_structp png; + png_infop info; + + FILE *f = fopen(sImageFile.c_str(), "rb"); + if (!f) return olc::NO_FILE; + + png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png) goto fail_load; + + info = png_create_info_struct(png); + if (!info) goto fail_load; + + if (setjmp(png_jmpbuf(png))) goto fail_load; + + png_init_io(png, f); + png_read_info(png, info); + + png_byte color_type; + png_byte bit_depth; + png_bytep *row_pointers; + width = png_get_image_width(png, info); + height = png_get_image_height(png, info); + color_type = png_get_color_type(png, info); + bit_depth = png_get_bit_depth(png, info); + +#ifdef _DEBUG + std::cout << "Loading PNG: " << sImageFile << "\n"; + std::cout << "W:" << width << " H:" << height << " D:" << (int)bit_depth << "\n"; +#endif + + if (bit_depth == 16) png_set_strip_16(png); + if (color_type == PNG_COLOR_TYPE_PALETTE) png_set_palette_to_rgb(png); + if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) png_set_expand_gray_1_2_4_to_8(png); + if (png_get_valid(png, info, PNG_INFO_tRNS)) png_set_tRNS_to_alpha(png); + if (color_type == PNG_COLOR_TYPE_RGB || + color_type == PNG_COLOR_TYPE_GRAY || + color_type == PNG_COLOR_TYPE_PALETTE) + png_set_filler(png, 0xFF, PNG_FILLER_AFTER); + if (color_type == PNG_COLOR_TYPE_GRAY || + color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + png_set_gray_to_rgb(png); + + png_read_update_info(png, info); + row_pointers = (png_bytep*)malloc(sizeof(png_bytep) * height); + for (int y = 0; y < height; y++) { + row_pointers[y] = (png_byte*)malloc(png_get_rowbytes(png, info)); + } + png_read_image(png, row_pointers); + //////////////////////////////////////////////////////////////////////////// + + // Create sprite array + pColData = new Pixel[width * height]; + + // Iterate through image rows, converting into sprite format + for (int y = 0; y < height; y++) + { + png_bytep row = row_pointers[y]; + for (int x = 0; x < width; x++) + { + png_bytep px = &(row[x * 4]); + SetPixel(x, y, Pixel(px[0], px[1], px[2], px[3])); + } + } + + fclose(f); + return olc::OK; + + fail_load: + width = 0; + height = 0; + fclose(f); + pColData = nullptr; + return olc::FAIL; +#endif + } + + void Sprite::SetSampleMode(olc::Sprite::Mode mode) + { + modeSample = mode; + } + + + Pixel Sprite::GetPixel(int32_t x, int32_t y) + { + if (modeSample == olc::Sprite::Mode::NORMAL) + { + if (x >= 0 && x < width && y >= 0 && y < height) + return pColData[y*width + x]; + else + return Pixel(0, 0, 0, 0); + } + else + { + return pColData[abs(y%height)*width + abs(x%width)]; + } + } + + void Sprite::SetPixel(int32_t x, int32_t y, Pixel p) + { + +#ifdef OLC_DBG_OVERDRAW + nOverdrawCount++; +#endif + + if (x >= 0 && x < width && y >= 0 && y < height) + pColData[y*width + x] = p; + } + + Pixel Sprite::Sample(float x, float y) + { + int32_t sx = (int32_t)(x * (float)width); + int32_t sy = (int32_t)(y * (float)height); + return GetPixel(sx, sy); + } + + Pixel* Sprite::GetData() { return pColData; } + + //========================================================== + + ResourcePack::ResourcePack() + { + + } + + // ResourcePack::~ResourcePack() + // { + // ClearPack(); + // } + + olc::rcode ResourcePack::AddToPack(std::string sFile) + { + std::ifstream ifs(sFile, std::ifstream::binary); + if (!ifs.is_open()) return olc::FAIL; + + // Get File Size + std::streampos p = 0; + p = ifs.tellg(); + ifs.seekg(0, std::ios::end); + p = ifs.tellg() - p; + ifs.seekg(0, std::ios::beg); + + // Create entry + sEntry e; + e.data = nullptr; + e.nFileSize = (uint32_t)p; + + // Read file into memory + e.data = new uint8_t[(uint32_t)e.nFileSize]; + ifs.read((char*)e.data, e.nFileSize); + ifs.close(); + + // Add To Map + mapFiles[sFile] = e; + return olc::OK; + } + + olc::rcode ResourcePack::SavePack(std::string sFile) + { + std::ofstream ofs(sFile, std::ofstream::binary); + if (!ofs.is_open()) return olc::FAIL; + + // 1) Write Map + size_t nMapSize = mapFiles.size(); + ofs.write((char*)&nMapSize, sizeof(size_t)); + for (auto &e : mapFiles) + { + size_t nPathSize = e.first.size(); + ofs.write((char*)&nPathSize, sizeof(size_t)); + ofs.write(e.first.c_str(), nPathSize); + ofs.write((char*)&e.second.nID, sizeof(uint32_t)); + ofs.write((char*)&e.second.nFileSize, sizeof(uint32_t)); + ofs.write((char*)&e.second.nFileOffset, sizeof(uint32_t)); + } + + // 2) Write Data + std::streampos offset = ofs.tellp(); + for (auto &e : mapFiles) + { + e.second.nFileOffset = (uint32_t)offset; + ofs.write((char*)e.second.data, e.second.nFileSize); + offset += e.second.nFileSize; + } + + // 3) Rewrite Map (it has been updated with offsets now) + ofs.seekp(std::ios::beg); + ofs.write((char*)&nMapSize, sizeof(size_t)); + for (auto &e : mapFiles) + { + size_t nPathSize = e.first.size(); + ofs.write((char*)&nPathSize, sizeof(size_t)); + ofs.write(e.first.c_str(), nPathSize); + ofs.write((char*)&e.second.nID, sizeof(uint32_t)); + ofs.write((char*)&e.second.nFileSize, sizeof(uint32_t)); + ofs.write((char*)&e.second.nFileOffset, sizeof(uint32_t)); + } + ofs.close(); + + return olc::OK; + } + + olc::rcode ResourcePack::LoadPack(std::string sFile) + { + std::ifstream ifs(sFile, std::ifstream::binary); + if (!ifs.is_open()) return olc::FAIL; + + // 1) Read Map + size_t nMapEntries; + ifs.read((char*)&nMapEntries, sizeof(size_t)); + for (size_t i = 0; i < nMapEntries; i++) + { + size_t nFilePathSize = 0; + ifs.read((char*)&nFilePathSize, sizeof(size_t)); + + std::string sFileName(nFilePathSize, ' '); + for (size_t j = 0; j < nFilePathSize; j++) + sFileName[j] = ifs.get(); + + sEntry e; + e.data = nullptr; + ifs.read((char*)&e.nID, sizeof(uint32_t)); + ifs.read((char*)&e.nFileSize, sizeof(uint32_t)); + ifs.read((char*)&e.nFileOffset, sizeof(uint32_t)); + mapFiles[sFileName] = e; + } + + // 2) Read Data + for (auto &e : mapFiles) + { + e.second.data = new uint8_t[(uint32_t)e.second.nFileSize]; + ifs.seekg(e.second.nFileOffset); + ifs.read((char*)e.second.data, e.second.nFileSize); + e.second._config(); + } + + ifs.close(); + return olc::OK; + } + + olc::ResourcePack::sEntry ResourcePack::GetStreamBuffer(std::string sFile) + { + return mapFiles[sFile]; + } + + olc::rcode ResourcePack::ClearPack() + { + for (auto &e : mapFiles) + { + if (e.second.data != nullptr) + delete[] e.second.data; + } + + mapFiles.clear(); + return olc::OK; + } + + //========================================================== + + PixelGameEngine::PixelGameEngine() + { + sAppName = "Undefined"; + olc::PGEX::pge = this; + } + + olc::rcode PixelGameEngine::Construct(uint32_t screen_w, uint32_t screen_h, uint32_t pixel_w, uint32_t pixel_h) + { + nScreenWidth = screen_w; + nScreenHeight = screen_h; + nPixelWidth = pixel_w; + nPixelHeight = pixel_h; + + fPixelX = 2.0f / (float)(nScreenWidth); + fPixelY = 2.0f / (float)(nScreenHeight); + + if (nPixelWidth == 0 || nPixelHeight == 0 || nScreenWidth == 0 || nScreenHeight == 0) + return olc::FAIL; + +#ifdef _WIN32 +#ifdef UNICODE +#ifndef __MINGW32__ + wsAppName = ConvertS2W(sAppName); +#endif +#endif +#endif + // Load the default font sheet + olc_ConstructFontSheet(); + + // Create a sprite that represents the primary drawing target + pDefaultDrawTarget = new Sprite(nScreenWidth, nScreenHeight); + SetDrawTarget(nullptr); + return olc::OK; + } + + olc::rcode PixelGameEngine::Start() + { + // Construct the window + if (!olc_WindowCreate()) + return olc::FAIL; + + // Load libraries required for PNG file interaction +#ifdef _WIN32 + // Windows use GDI+ + Gdiplus::GdiplusStartupInput startupInput; + ULONG_PTR token; + Gdiplus::GdiplusStartup(&token, &startupInput, NULL); +#else + // Linux use libpng + +#endif + // Start the thread + bAtomActive = true; + std::thread t = std::thread(&PixelGameEngine::EngineThread, this); + +#ifdef _WIN32 + // Handle Windows Message Loop + MSG msg; + while (GetMessage(&msg, NULL, 0, 0) > 0) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } +#endif + + // Wait for thread to be exited + t.join(); + return olc::OK; + } + + void PixelGameEngine::SetDrawTarget(Sprite *target) + { + if (target) + pDrawTarget = target; + else + pDrawTarget = pDefaultDrawTarget; + } + + Sprite* PixelGameEngine::GetDrawTarget() + { + return pDrawTarget; + } + + int32_t PixelGameEngine::GetDrawTargetWidth() + { + if (pDrawTarget) + return pDrawTarget->width; + else + return 0; + } + + int32_t PixelGameEngine::GetDrawTargetHeight() + { + if (pDrawTarget) + return pDrawTarget->height; + else + return 0; + } + + bool PixelGameEngine::IsFocused() + { + return bHasInputFocus; + } + + HWButton PixelGameEngine::GetKey(Key k) + { + return pKeyboardState[k]; + } + + HWButton PixelGameEngine::GetMouse(uint32_t b) + { + return pMouseState[b]; + } + + int32_t PixelGameEngine::GetMouseX() + { + return nMousePosX; + } + + int32_t PixelGameEngine::GetMouseY() + { + return nMousePosY; + } + + int32_t PixelGameEngine::ScreenWidth() + { + return nScreenWidth; + } + + int32_t PixelGameEngine::ScreenHeight() + { + return nScreenHeight; + } + + void PixelGameEngine::Draw(int32_t x, int32_t y, Pixel p) + { + if (!pDrawTarget) return; + + + if (nPixelMode == Pixel::NORMAL) + { + pDrawTarget->SetPixel(x, y, p); + return; + } + + if (nPixelMode == Pixel::MASK) + { + if(p.a == 255) + pDrawTarget->SetPixel(x, y, p); + return; + } + + if (nPixelMode == Pixel::ALPHA) + { + Pixel d = pDrawTarget->GetPixel(x, y); + float a = (float)(p.a / 255.0f) * fBlendFactor; + float c = 1.0f - a; + float r = a * (float)p.r + c * (float)d.r; + float g = a * (float)p.g + c * (float)d.g; + float b = a * (float)p.b + c * (float)d.b; + pDrawTarget->SetPixel(x, y, Pixel((uint8_t)r, (uint8_t)g, (uint8_t)b)); + return; + } + + if (nPixelMode == Pixel::CUSTOM) + { + pDrawTarget->SetPixel(x, y, funcPixelMode(x, y, p, pDrawTarget->GetPixel(x, y))); + return; + } + } + + void PixelGameEngine::SetSubPixelOffset(float ox, float oy) + { + fSubPixelOffsetX = ox * fPixelX; + fSubPixelOffsetY = oy * fPixelY; + } + + void PixelGameEngine::DrawLine(int32_t x1, int32_t y1, int32_t x2, int32_t y2, Pixel p) + { + int x, y, dx, dy, dx1, dy1, px, py, xe, ye, i; + dx = x2 - x1; dy = y2 - y1; + + // straight lines idea by gurkanctn + if (dx == 0) // Line is vertical + { + if (y2 < y1) std::swap(y1, y2); + for (y = y1; y <= y2; y++) + Draw(x1, y, p); + return; + } + + if (dy == 0) // Line is horizontal + { + if (x2 < x1) std::swap(x1, x2); + for (x = x1; x <= x2; x++) + Draw(x, y1, p); + return; + } + + // Line is Funk-aye + dx1 = abs(dx); dy1 = abs(dy); + px = 2 * dy1 - dx1; py = 2 * dx1 - dy1; + if (dy1 <= dx1) + { + if (dx >= 0) + { + x = x1; y = y1; xe = x2; + } + else + { + x = x2; y = y2; xe = x1; + } + + Draw(x, y, p); + + for (i = 0; x0 && dy>0)) y = y + 1; else y = y - 1; + px = px + 2 * (dy1 - dx1); + } + Draw(x, y, p); + } + } + else + { + if (dy >= 0) + { + x = x1; y = y1; ye = y2; + } + else + { + x = x2; y = y2; ye = y1; + } + + Draw(x, y, p); + + for (i = 0; y0 && dy>0)) x = x + 1; else x = x - 1; + py = py + 2 * (dx1 - dy1); + } + Draw(x, y, p); + } + } + } + + void PixelGameEngine::DrawCircle(int32_t x, int32_t y, int32_t radius, Pixel p) + { + int x0 = 0; + int y0 = radius; + int d = 3 - 2 * radius; + if (!radius) return; + + while (y0 >= x0) // only formulate 1/8 of circle + { + Draw(x - x0, y - y0, p);//upper left left + Draw(x - y0, y - x0, p);//upper upper left + Draw(x + y0, y - x0, p);//upper upper right + Draw(x + x0, y - y0, p);//upper right right + Draw(x - x0, y + y0, p);//lower left left + Draw(x - y0, y + x0, p);//lower lower left + Draw(x + y0, y + x0, p);//lower lower right + Draw(x + x0, y + y0, p);//lower right right + if (d < 0) d += 4 * x0++ + 6; + else d += 4 * (x0++ - y0--) + 10; + } + } + + void PixelGameEngine::FillCircle(int32_t x, int32_t y, int32_t radius, Pixel p) + { + // Taken from wikipedia + int x0 = 0; + int y0 = radius; + int d = 3 - 2 * radius; + if (!radius) return; + + auto drawline = [&](int sx, int ex, int ny) + { + for (int i = sx; i <= ex; i++) + Draw(i, ny, p); + }; + + while (y0 >= x0) + { + // Modified to draw scan-lines instead of edges + drawline(x - x0, x + x0, y - y0); + drawline(x - y0, x + y0, y - x0); + drawline(x - x0, x + x0, y + y0); + drawline(x - y0, x + y0, y + x0); + if (d < 0) d += 4 * x0++ + 6; + else d += 4 * (x0++ - y0--) + 10; + } + } + + void PixelGameEngine::DrawRect(int32_t x, int32_t y, int32_t w, int32_t h, Pixel p) + { + w--; h--; + DrawLine(x, y, x+w, y, p); + DrawLine(x+w, y, x+w, y+h, p); + DrawLine(x+w, y+h, x, y+h, p); + DrawLine(x, y+h, x, y, p); + } + + void PixelGameEngine::Clear(Pixel p) + { + int pixels = GetDrawTargetWidth() * GetDrawTargetHeight(); + Pixel* m = GetDrawTarget()->GetData(); + for (int i = 0; i < pixels; i++) + m[i] = p; +#ifdef OLC_DBG_OVERDRAW + olc::Sprite::nOverdrawCount += pixels; +#endif + } + + void PixelGameEngine::FillRect(int32_t x, int32_t y, int32_t w, int32_t h, Pixel p) + { + int32_t x2 = x + w; + int32_t y2 = y + h; + + if (x < 0) x = 0; + if (x >= (int32_t)nScreenWidth) x = (int32_t)nScreenWidth; + if (y < 0) y = 0; + if (y >= (int32_t)nScreenHeight) y = (int32_t)nScreenHeight; + + if (x2 < 0) x2 = 0; + if (x2 >= (int32_t)nScreenWidth) x2 = (int32_t)nScreenWidth; + if (y2 < 0) y2 = 0; + if (y2 >= (int32_t)nScreenHeight) y2 = (int32_t)nScreenHeight; + + for (int i = x; i < x2; i++) + for (int j = y; j < y2; j++) + Draw(i, j, p); + } + + void PixelGameEngine::DrawTriangle(int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t x3, int32_t y3, Pixel p) + { + DrawLine(x1, y1, x2, y2, p); + DrawLine(x2, y2, x3, y3, p); + DrawLine(x3, y3, x1, y1, p); + } + + // https://www.avrfreaks.net/sites/default/files/triangles.c + void PixelGameEngine::FillTriangle(int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t x3, int32_t y3, Pixel p) + { + auto SWAP = [](int &x, int &y) { int t = x; x = y; y = t; }; + auto drawline = [&](int sx, int ex, int ny) { for (int i = sx; i <= ex; i++) Draw(i, ny, p); }; + + int t1x, t2x, y, minx, maxx, t1xp, t2xp; + bool changed1 = false; + bool changed2 = false; + int signx1, signx2, dx1, dy1, dx2, dy2; + int e1, e2; + // Sort vertices + if (y1>y2) { SWAP(y1, y2); SWAP(x1, x2); } + if (y1>y3) { SWAP(y1, y3); SWAP(x1, x3); } + if (y2>y3) { SWAP(y2, y3); SWAP(x2, x3); } + + t1x = t2x = x1; y = y1; // Starting points + dx1 = (int)(x2 - x1); if (dx1<0) { dx1 = -dx1; signx1 = -1; } + else signx1 = 1; + dy1 = (int)(y2 - y1); + + dx2 = (int)(x3 - x1); if (dx2<0) { dx2 = -dx2; signx2 = -1; } + else signx2 = 1; + dy2 = (int)(y3 - y1); + + if (dy1 > dx1) { // swap values + SWAP(dx1, dy1); + changed1 = true; + } + if (dy2 > dx2) { // swap values + SWAP(dy2, dx2); + changed2 = true; + } + + e2 = (int)(dx2 >> 1); + // Flat top, just process the second half + if (y1 == y2) goto next; + e1 = (int)(dx1 >> 1); + + for (int i = 0; i < dx1;) { + t1xp = 0; t2xp = 0; + if (t1x= dx1) { + e1 -= dx1; + if (changed1) t1xp = signx1;//t1x += signx1; + else goto next1; + } + if (changed1) break; + else t1x += signx1; + } + // Move line + next1: + // process second line until y value is about to change + while (1) { + e2 += dy2; + while (e2 >= dx2) { + e2 -= dx2; + if (changed2) t2xp = signx2;//t2x += signx2; + else goto next2; + } + if (changed2) break; + else t2x += signx2; + } + next2: + if (minx>t1x) minx = t1x; if (minx>t2x) minx = t2x; + if (maxx dx1) { // swap values + SWAP(dy1, dx1); + changed1 = true; + } + else changed1 = false; + + e1 = (int)(dx1 >> 1); + + for (int i = 0; i <= dx1; i++) { + t1xp = 0; t2xp = 0; + if (t1x= dx1) { + e1 -= dx1; + if (changed1) { t1xp = signx1; break; }//t1x += signx1; + else goto next3; + } + if (changed1) break; + else t1x += signx1; + if (i= dx2) { + e2 -= dx2; + if (changed2) t2xp = signx2; + else goto next4; + } + if (changed2) break; + else t2x += signx2; + } + next4: + + if (minx>t1x) minx = t1x; if (minx>t2x) minx = t2x; + if (maxxy3) return; + } + } + + void PixelGameEngine::DrawSprite(int32_t x, int32_t y, Sprite *sprite, uint32_t scale) + { + if (sprite == nullptr) + return; + + if (scale > 1) + { + for (int32_t i = 0; i < sprite->width; i++) + for (int32_t j = 0; j < sprite->height; j++) + for (uint32_t is = 0; is < scale; is++) + for (uint32_t js = 0; js < scale; js++) + Draw(x + (i*scale) + is, y + (j*scale) + js, sprite->GetPixel(i, j)); + } + else + { + for (int32_t i = 0; i < sprite->width; i++) + for (int32_t j = 0; j < sprite->height; j++) + Draw(x + i, y + j, sprite->GetPixel(i, j)); + } + } + + void PixelGameEngine::DrawPartialSprite(int32_t x, int32_t y, Sprite *sprite, int32_t ox, int32_t oy, int32_t w, int32_t h, uint32_t scale) + { + if (sprite == nullptr) + return; + + if (scale > 1) + { + for (int32_t i = 0; i < w; i++) + for (int32_t j = 0; j < h; j++) + for (uint32_t is = 0; is < scale; is++) + for (uint32_t js = 0; js < scale; js++) + Draw(x + (i*scale) + is, y + (j*scale) + js, sprite->GetPixel(i + ox, j + oy)); + } + else + { + for (int32_t i = 0; i < w; i++) + for (int32_t j = 0; j < h; j++) + Draw(x + i, y + j, sprite->GetPixel(i + ox, j + oy)); + } + } + + void PixelGameEngine::DrawString(int32_t x, int32_t y, std::string sText, Pixel col, uint32_t scale) + { + int32_t sx = 0; + int32_t sy = 0; + Pixel::Mode m = nPixelMode; + if(col.ALPHA != 255) SetPixelMode(Pixel::ALPHA); + else SetPixelMode(Pixel::MASK); + for (auto c : sText) + { + if (c == '\n') + { + sx = 0; sy += 8 * scale; + } + else + { + int32_t ox = (c - 32) % 16; + int32_t oy = (c - 32) / 16; + + if (scale > 1) + { + for (uint32_t i = 0; i < 8; i++) + for (uint32_t j = 0; j < 8; j++) + if (fontSprite->GetPixel(i + ox * 8, j + oy * 8).r > 0) + for (uint32_t is = 0; is < scale; is++) + for (uint32_t js = 0; js < scale; js++) + Draw(x + sx + (i*scale) + is, y + sy + (j*scale) + js, col); + } + else + { + for (uint32_t i = 0; i < 8; i++) + for (uint32_t j = 0; j < 8; j++) + if (fontSprite->GetPixel(i + ox * 8, j + oy * 8).r > 0) + Draw(x + sx + i, y + sy + j, col); + } + sx += 8 * scale; + } + } + SetPixelMode(m); + } + + void PixelGameEngine::SetPixelMode(Pixel::Mode m) + { + nPixelMode = m; + } + + Pixel::Mode PixelGameEngine::GetPixelMode() + { + return nPixelMode; + } + + void PixelGameEngine::SetPixelMode(std::function pixelMode) + { + funcPixelMode = pixelMode; + nPixelMode = Pixel::Mode::CUSTOM; + } + + void PixelGameEngine::SetPixelBlend(float fBlend) + { + fBlendFactor = fBlend; + if (fBlendFactor < 0.0f) fBlendFactor = 0.0f; + if (fBlendFactor > 1.0f) fBlendFactor = 1.0f; + } + + // User must override these functions as required. I have not made + // them abstract because I do need a default behaviour to occur if + // they are not overwritten + bool PixelGameEngine::OnUserCreate() + { return false; } + bool PixelGameEngine::OnUserUpdate(float fElapsedTime) + { return false; } + bool PixelGameEngine::OnUserDestroy() + { return true; } + ////////////////////////////////////////////////////////////////// + + void PixelGameEngine::olc_UpdateMouse(int32_t x, int32_t y) + { + // Mouse coords come in screen space + // But leave in pixel space + nMousePosX = x / (int32_t)nPixelWidth; + nMousePosY = y / (int32_t)nPixelHeight; + + if (nMousePosX >= (int32_t)nScreenWidth) + nMousePosX = nScreenWidth - 1; + if (nMousePosY >= (int32_t)nScreenHeight) + nMousePosY = nScreenHeight - 1; + + if (nMousePosX < 0) + nMousePosX = 0; + if (nMousePosY < 0) + nMousePosY = 0; + } + + void PixelGameEngine::EngineThread() + { + // Start OpenGL, the context is owned by the game thread + olc_OpenGLCreate(); + + // Create Screen Texture - disable filtering + glEnable(GL_TEXTURE_2D); + glGenTextures(1, &glBuffer); + glBindTexture(GL_TEXTURE_2D, glBuffer); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, nScreenWidth, nScreenHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, pDefaultDrawTarget->GetData()); + + + // Create user resources as part of this thread + if (!OnUserCreate()) + bAtomActive = false; + + auto tp1 = std::chrono::system_clock::now(); + auto tp2 = std::chrono::system_clock::now(); + + while (bAtomActive) + { + // Run as fast as possible + while (bAtomActive) + { + // Handle Timing + tp2 = std::chrono::system_clock::now(); + std::chrono::duration elapsedTime = tp2 - tp1; + tp1 = tp2; + + // Our time per frame coefficient + float fElapsedTime = elapsedTime.count(); + +#ifndef _WIN32 + // Handle Xlib Message Loop - we do this in the + // same thread that OpenGL was created so we dont + // need to worry too much about multithreading with X11 + XEvent xev; + while (XPending(olc_Display)) + { + XNextEvent(olc_Display, &xev); + if (xev.type == Expose) + { + XWindowAttributes gwa; + XGetWindowAttributes(olc_Display, olc_Window, &gwa); + glViewport(0, 0, gwa.width, gwa.height); + } + else if (xev.type == KeyPress) + { + KeySym sym = XLookupKeysym(&xev.xkey, 0); + pKeyNewState[mapKeys[sym]] = true; + } + else if (xev.type == KeyRelease) + { + KeySym sym = XLookupKeysym(&xev.xkey, 0); + pKeyNewState[mapKeys[sym]] = false; + } + else if (xev.type == ButtonPress) + { + pMouseNewState[xev.xbutton.button-1] = true; + } + else if (xev.type == ButtonRelease) + { + pMouseNewState[xev.xbutton.button-1] = false; + } + else if (xev.type == MotionNotify) + { + olc_UpdateMouse(xev.xmotion.x, xev.xmotion.y); + } + else if (xev.type == FocusIn) + { + bHasInputFocus = true; + } + else if (xev.type == FocusOut) + { + bHasInputFocus = false; + } + else if (xev.type == ClientMessage) + { + bAtomActive = false; + } + } +#endif + + // Handle User Input - Keyboard + for (int i = 0; i < 256; i++) + { + pKeyboardState[i].bPressed = false; + pKeyboardState[i].bReleased = false; + + if (pKeyNewState[i] != pKeyOldState[i]) + { + if (pKeyNewState[i]) + { + pKeyboardState[i].bPressed = !pKeyboardState[i].bHeld; + pKeyboardState[i].bHeld = true; + } + else + { + pKeyboardState[i].bReleased = true; + pKeyboardState[i].bHeld = false; + } + } + + pKeyOldState[i] = pKeyNewState[i]; + } + + // Handle User Input - Mouse + for (int i = 0; i < 5; i++) + { + pMouseState[i].bPressed = false; + pMouseState[i].bReleased = false; + + if (pMouseNewState[i] != pMouseOldState[i]) + { + if (pMouseNewState[i]) + { + pMouseState[i].bPressed = !pMouseState[i].bHeld; + pMouseState[i].bHeld = true; + } + else + { + pMouseState[i].bReleased = true; + pMouseState[i].bHeld = false; + } + } + + pMouseOldState[i] = pMouseNewState[i]; + } + +#ifdef OLC_DBG_OVERDRAW + olc::Sprite::nOverdrawCount = 0; +#endif + + // Handle Frame Update + if (!OnUserUpdate(fElapsedTime)) + bAtomActive = false; + + // Display Graphics + + // TODO: This is a bit slow (especially in debug, but 100x faster in release mode???) + // Copy pixel array into texture + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, nScreenWidth, nScreenHeight, GL_RGBA, GL_UNSIGNED_BYTE, pDefaultDrawTarget->GetData()); + + // Display texture on screen + glBegin(GL_QUADS); + glTexCoord2f(0.0, 1.0); glVertex3f(-1.0f + (fSubPixelOffsetX), -1.0f + (fSubPixelOffsetY), 0.0f); + glTexCoord2f(0.0, 0.0); glVertex3f(-1.0f + (fSubPixelOffsetX), 1.0f + (fSubPixelOffsetY), 0.0f); + glTexCoord2f(1.0, 0.0); glVertex3f( 1.0f + (fSubPixelOffsetX), 1.0f + (fSubPixelOffsetY), 0.0f); + glTexCoord2f(1.0, 1.0); glVertex3f( 1.0f + (fSubPixelOffsetX), -1.0f + (fSubPixelOffsetY), 0.0f); + glEnd(); + + // Present Graphics to screen +#ifdef _WIN32 + SwapBuffers(glDeviceContext); +#else + glXSwapBuffers(olc_Display, olc_Window); +#endif + + // Update Title Bar + fFrameTimer += fElapsedTime; + nFrameCount++; + if (fFrameTimer >= 1.0f) + { + fFrameTimer -= 1.0f; + + std::string sTitle = sAppName + " - FPS: " + std::to_string(nFrameCount); +#ifdef _WIN32 +#ifdef UNICODE + SetWindowText(olc_hWnd, ConvertS2W(sTitle).c_str()); +#else + SetWindowText(olc_hWnd, sTitle.c_str()); +#endif +#else + XStoreName(olc_Display, olc_Window, sTitle.c_str()); +#endif + nFrameCount = 0; + } + } + + // Allow the user to free resources if they have overrided the destroy function + if (OnUserDestroy()) + { + // User has permitted destroy, so exit and clean up + } + else + { + // User denied destroy for some reason, so continue running + bAtomActive = true; + } + } + +#ifdef _WIN32 + wglDeleteContext(glRenderContext); + PostMessage(olc_hWnd, WM_DESTROY, 0, 0); +#else + glXMakeCurrent(olc_Display, None, NULL); + glXDestroyContext(olc_Display, glDeviceContext); + XDestroyWindow(olc_Display, olc_Window); + XCloseDisplay(olc_Display); +#endif + + } + + + void PixelGameEngine::olc_ConstructFontSheet() + { + std::string data; + data += "?Q`0001oOch0o01o@F40o000000000"; + data += "O000000nOT0063Qo4d8>?7a14Gno94AA4gno94AaOT0>o3`oO400o7QN00000400"; + data += "Of80001oOg<7O7moBGT7O7lABET024@aBEd714AiOdl717a_=TH013Q>00000000"; + data += "720D000V?V5oB3Q_HdUoE7a9@DdDE4A9@DmoE4A;Hg]oM4Aj8S4D84@`00000000"; + data += "OaPT1000Oa`^13P1@AI[?g`1@A=[OdAoHgljA4Ao?WlBA7l1710007l100000000"; + data += "ObM6000oOfMV?3QoBDD`O7a0BDDH@5A0BDD<@5A0BGeVO5ao@CQR?5Po00000000"; + data += "Oc``000?Ogij70PO2D]??0Ph2DUM@7i`2DTg@7lh2GUj?0TO0C1870T?00000000"; + data += "70<4001o?P<7?1QoHg43O;`h@GT0@:@LB@d0>:@hN@L0@?aoN@<0O7ao0000?000"; + data += "OcH0001SOglLA7mg24TnK7ln24US>0PL24U140PnOgl0>7QgOcH0K71S0000A000"; + data += "00H00000@Dm1S007@DUSg00?OdTnH7YhOfTL<7Yh@Cl0700?@Ah0300700000000"; + data += "<008001QL00ZA41a@6HnI<1i@FHLM81M@@0LG81?O`0nC?Y7?`0ZA7Y300080000"; + data += "O`082000Oh0827mo6>Hn?Wmo?6HnMb11MP08@C11H`08@FP0@@0004@000000000"; + data += "00P00001Oab00003OcKP0006@6=PMgl<@440MglH@000000`@000001P00000000"; + data += "Ob@8@@00Ob@8@Ga13R@8Mga172@8?PAo3R@827QoOb@820@0O`0007`0000007P0"; + data += "O`000P08Od400g`<3V=P0G`673IP0`@3>1`00P@6O`P00g`SetPixel(px, py, olc::Pixel(k, k, k, k)); + if (++py == 48) { px++; py = 0; } + } + } + } + +#ifdef _WIN32 + HWND PixelGameEngine::olc_WindowCreate() + { + WNDCLASS wc; + wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; + wc.hInstance = GetModuleHandle(nullptr); + wc.lpfnWndProc = olc_WindowEvent; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.lpszMenuName = nullptr; + wc.hbrBackground = nullptr; +#ifdef UNICODE + wc.lpszClassName = L"OLC_PIXEL_GAME_ENGINE"; +#else + wc.lpszClassName = "OLC_PIXEL_GAME_ENGINE"; +#endif + + RegisterClass(&wc); + + // Define window furniture + DWORD dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; + DWORD dwStyle = WS_CAPTION | WS_SYSMENU | WS_VISIBLE; + RECT rWndRect = { 0, 0, (LONG)nScreenWidth * (LONG)nPixelWidth, (LONG)nScreenHeight * (LONG)nPixelHeight }; + + // Keep client size as requested + AdjustWindowRectEx(&rWndRect, dwStyle, FALSE, dwExStyle); + + int width = rWndRect.right - rWndRect.left; + int height = rWndRect.bottom - rWndRect.top; + +#ifdef UNICODE + olc_hWnd = CreateWindowEx(dwExStyle, L"OLC_PIXEL_GAME_ENGINE", L"", dwStyle, + 30, 30, width, height, NULL, NULL, GetModuleHandle(nullptr), this); +#else + olc_hWnd = CreateWindowEx(dwExStyle, "OLC_PIXEL_GAME_ENGINE", "", dwStyle, + 30, 30, width, height, NULL, NULL, GetModuleHandle(nullptr), this); +#endif + + // Create Keyboard Mapping + mapKeys[0x41] = Key::A; mapKeys[0x42] = Key::B; mapKeys[0x43] = Key::C; mapKeys[0x44] = Key::D; mapKeys[0x45] = Key::E; + mapKeys[0x46] = Key::F; mapKeys[0x47] = Key::G; mapKeys[0x48] = Key::H; mapKeys[0x49] = Key::I; mapKeys[0x4A] = Key::J; + mapKeys[0x4B] = Key::K; mapKeys[0x4C] = Key::L; mapKeys[0x4D] = Key::M; mapKeys[0x4E] = Key::N; mapKeys[0x4F] = Key::O; + mapKeys[0x50] = Key::P; mapKeys[0x51] = Key::Q; mapKeys[0x52] = Key::R; mapKeys[0x53] = Key::S; mapKeys[0x54] = Key::T; + mapKeys[0x55] = Key::U; mapKeys[0x56] = Key::V; mapKeys[0x57] = Key::W; mapKeys[0x58] = Key::X; mapKeys[0x59] = Key::Y; + mapKeys[0x5A] = Key::Z; + + mapKeys[VK_F1] = Key::F1; mapKeys[VK_F2] = Key::F2; mapKeys[VK_F3] = Key::F3; mapKeys[VK_F4] = Key::F4; + mapKeys[VK_F5] = Key::F5; mapKeys[VK_F6] = Key::F6; mapKeys[VK_F7] = Key::F7; mapKeys[VK_F8] = Key::F8; + mapKeys[VK_F9] = Key::F9; mapKeys[VK_F10] = Key::F10; mapKeys[VK_F11] = Key::F11; mapKeys[VK_F12] = Key::F12; + + mapKeys[VK_DOWN] = Key::DOWN; mapKeys[VK_LEFT] = Key::LEFT; mapKeys[VK_RIGHT] = Key::RIGHT; mapKeys[VK_UP] = Key::UP; + mapKeys[VK_RETURN] = Key::ENTER; //mapKeys[VK_RETURN] = Key::RETURN; + + mapKeys[VK_BACK] = Key::BACK; mapKeys[VK_ESCAPE] = Key::ESCAPE; mapKeys[VK_RETURN] = Key::ENTER; mapKeys[VK_PAUSE] = Key::PAUSE; + mapKeys[VK_SCROLL] = Key::SCROLL; mapKeys[VK_TAB] = Key::TAB; mapKeys[VK_DELETE] = Key::DEL; mapKeys[VK_HOME] = Key::HOME; + mapKeys[VK_END] = Key::END; mapKeys[VK_PRIOR] = Key::PGUP; mapKeys[VK_NEXT] = Key::PGDN; mapKeys[VK_INSERT] = Key::INS; + mapKeys[VK_SHIFT] = Key::SHIFT; mapKeys[VK_CONTROL] = Key::CTRL; + mapKeys[VK_SPACE] = Key::SPACE; + + mapKeys[0x30] = Key::K0; mapKeys[0x31] = Key::K1; mapKeys[0x32] = Key::K2; mapKeys[0x33] = Key::K3; mapKeys[0x34] = Key::K4; + mapKeys[0x35] = Key::K5; mapKeys[0x36] = Key::K6; mapKeys[0x37] = Key::K7; mapKeys[0x38] = Key::K8; mapKeys[0x39] = Key::K9; + + mapKeys[VK_NUMPAD0] = Key::NP0; mapKeys[VK_NUMPAD1] = Key::NP1; mapKeys[VK_NUMPAD2] = Key::NP2; mapKeys[VK_NUMPAD3] = Key::NP3; mapKeys[VK_NUMPAD4] = Key::NP4; + mapKeys[VK_NUMPAD5] = Key::NP5; mapKeys[VK_NUMPAD6] = Key::NP6; mapKeys[VK_NUMPAD7] = Key::NP7; mapKeys[VK_NUMPAD8] = Key::NP8; mapKeys[VK_NUMPAD9] = Key::NP9; + mapKeys[VK_MULTIPLY] = Key::NP_MUL; mapKeys[VK_ADD] = Key::NP_ADD; mapKeys[VK_DIVIDE] = Key::NP_DIV; mapKeys[VK_SUBTRACT] = Key::NP_SUB; mapKeys[VK_DECIMAL] = Key::NP_DECIMAL; + + return olc_hWnd; + } + + bool PixelGameEngine::olc_OpenGLCreate() + { + // Create Device Context + glDeviceContext = GetDC(olc_hWnd); + PIXELFORMATDESCRIPTOR pfd = + { + sizeof(PIXELFORMATDESCRIPTOR), 1, + PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, + PFD_TYPE_RGBA, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + PFD_MAIN_PLANE, 0, 0, 0, 0 + }; + + int pf = 0; + if (!(pf = ChoosePixelFormat(glDeviceContext, &pfd))) return false; + SetPixelFormat(glDeviceContext, pf, &pfd); + + if (!(glRenderContext = wglCreateContext(glDeviceContext))) return false; + wglMakeCurrent(glDeviceContext, glRenderContext); + + // Remove Frame cap + wglSwapInterval = (wglSwapInterval_t*)wglGetProcAddress("wglSwapIntervalEXT"); + wglSwapInterval(0); + return true; + } + + // Windows Event Handler + LRESULT CALLBACK PixelGameEngine::olc_WindowEvent(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) + { + static PixelGameEngine *sge; + switch (uMsg) + { + case WM_CREATE: sge = (PixelGameEngine*)((LPCREATESTRUCT)lParam)->lpCreateParams; return 0; + case WM_MOUSEMOVE: + { + uint16_t x = lParam & 0xFFFF; // Thanks @ForAbby (Discord) + uint16_t y = (lParam >> 16) & 0xFFFF; + int16_t ix = *(int16_t*)&x; + int16_t iy = *(int16_t*)&y; + sge->olc_UpdateMouse(ix, iy); + return 0; + } + case WM_MOUSELEAVE: sge->bHasMouseFocus = false; + case WM_SETFOCUS: sge->bHasInputFocus = true; return 0; + case WM_KILLFOCUS: sge->bHasInputFocus = false; return 0; + case WM_KEYDOWN: sge->pKeyNewState[mapKeys[wParam]] = true; return 0; + case WM_KEYUP: sge->pKeyNewState[mapKeys[wParam]] = false; return 0; + case WM_LBUTTONDOWN:sge->pMouseNewState[0] = true; return 0; + case WM_LBUTTONUP: sge->pMouseNewState[0] = false; return 0; + case WM_RBUTTONDOWN:sge->pMouseNewState[1] = true; return 0; + case WM_RBUTTONUP: sge->pMouseNewState[1] = false; return 0; + case WM_MBUTTONDOWN:sge->pMouseNewState[2] = true; return 0; + case WM_MBUTTONUP: sge->pMouseNewState[2] = false; return 0; + case WM_CLOSE: bAtomActive = false; return 0; + case WM_DESTROY: PostQuitMessage(0); return 0; + } + return DefWindowProc(hWnd, uMsg, wParam, lParam); + } +#else + // Do the Linux stuff! + Display* PixelGameEngine::olc_WindowCreate() + { + XInitThreads(); + + // Grab the deafult display and window + olc_Display = XOpenDisplay(NULL); + olc_WindowRoot = DefaultRootWindow(olc_Display); + + // Based on the display capabilities, configure the appearance of the window + GLint olc_GLAttribs[] = { GLX_RGBA, GLX_DEPTH_SIZE, 24, GLX_DOUBLEBUFFER, None }; + olc_VisualInfo = glXChooseVisual(olc_Display, 0, olc_GLAttribs); + olc_ColourMap = XCreateColormap(olc_Display, olc_WindowRoot, olc_VisualInfo->visual, AllocNone); + olc_SetWindowAttribs.colormap = olc_ColourMap; + + // Register which events we are interested in receiving + olc_SetWindowAttribs.event_mask = ExposureMask | KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | FocusChangeMask; + + // Create the window + olc_Window = XCreateWindow(olc_Display, olc_WindowRoot, 30, 30, nScreenWidth * nPixelWidth, nScreenHeight * nPixelHeight, 0, olc_VisualInfo->depth, InputOutput, olc_VisualInfo->visual, CWColormap | CWEventMask, &olc_SetWindowAttribs); + + Atom wmDelete = XInternAtom(olc_Display, "WM_DELETE_WINDOW", true); + XSetWMProtocols(olc_Display, olc_Window, &wmDelete, 1); + + XMapWindow(olc_Display, olc_Window); + XStoreName(olc_Display, olc_Window, ""); + + // Create Keyboard Mapping + mapKeys[0x61] = Key::A; mapKeys[0x62] = Key::B; mapKeys[0x63] = Key::C; mapKeys[0x64] = Key::D; mapKeys[0x65] = Key::E; + mapKeys[0x66] = Key::F; mapKeys[0x67] = Key::G; mapKeys[0x68] = Key::H; mapKeys[0x69] = Key::I; mapKeys[0x6A] = Key::J; + mapKeys[0x6B] = Key::K; mapKeys[0x6C] = Key::L; mapKeys[0x6D] = Key::M; mapKeys[0x6E] = Key::N; mapKeys[0x6F] = Key::O; + mapKeys[0x70] = Key::P; mapKeys[0x71] = Key::Q; mapKeys[0x72] = Key::R; mapKeys[0x73] = Key::S; mapKeys[0x74] = Key::T; + mapKeys[0x75] = Key::U; mapKeys[0x76] = Key::V; mapKeys[0x77] = Key::W; mapKeys[0x78] = Key::X; mapKeys[0x79] = Key::Y; + mapKeys[0x7A] = Key::Z; + + mapKeys[XK_F1] = Key::F1; mapKeys[XK_F2] = Key::F2; mapKeys[XK_F3] = Key::F3; mapKeys[XK_F4] = Key::F4; + mapKeys[XK_F5] = Key::F5; mapKeys[XK_F6] = Key::F6; mapKeys[XK_F7] = Key::F7; mapKeys[XK_F8] = Key::F8; + mapKeys[XK_F9] = Key::F9; mapKeys[XK_F10] = Key::F10; mapKeys[XK_F11] = Key::F11; mapKeys[XK_F12] = Key::F12; + + mapKeys[XK_Down] = Key::DOWN; mapKeys[XK_Left] = Key::LEFT; mapKeys[XK_Right] = Key::RIGHT; mapKeys[XK_Up] = Key::UP; + mapKeys[XK_KP_Enter] = Key::ENTER; mapKeys[XK_Return] = Key::ENTER; + + mapKeys[XK_BackSpace] = Key::BACK; mapKeys[XK_Escape] = Key::ESCAPE; mapKeys[XK_Linefeed] = Key::ENTER; mapKeys[XK_Pause] = Key::PAUSE; + mapKeys[XK_Scroll_Lock] = Key::SCROLL; mapKeys[XK_Tab] = Key::TAB; mapKeys[XK_Delete] = Key::DEL; mapKeys[XK_Home] = Key::HOME; + mapKeys[XK_End] = Key::END; mapKeys[XK_Page_Up] = Key::PGUP; mapKeys[XK_Page_Down] = Key::PGDN; mapKeys[XK_Insert] = Key::INS; + mapKeys[XK_Shift_L] = Key::SHIFT; mapKeys[XK_Shift_R] = Key::SHIFT; mapKeys[XK_Control_L] = Key::CTRL; mapKeys[XK_Control_R] = Key::CTRL; + mapKeys[XK_space] = Key::SPACE; + + mapKeys[XK_0] = Key::K0; mapKeys[XK_1] = Key::K1; mapKeys[XK_2] = Key::K2; mapKeys[XK_3] = Key::K3; mapKeys[XK_4] = Key::K4; + mapKeys[XK_5] = Key::K5; mapKeys[XK_6] = Key::K6; mapKeys[XK_7] = Key::K7; mapKeys[XK_8] = Key::K8; mapKeys[XK_9] = Key::K9; + + mapKeys[XK_KP_0] = Key::NP0; mapKeys[XK_KP_1] = Key::NP1; mapKeys[XK_KP_2] = Key::NP2; mapKeys[XK_KP_3] = Key::NP3; mapKeys[XK_KP_4] = Key::NP4; + mapKeys[XK_KP_5] = Key::NP5; mapKeys[XK_KP_6] = Key::NP6; mapKeys[XK_KP_7] = Key::NP7; mapKeys[XK_KP_8] = Key::NP8; mapKeys[XK_KP_9] = Key::NP9; + mapKeys[XK_KP_Multiply] = Key::NP_MUL; mapKeys[XK_KP_Add] = Key::NP_ADD; mapKeys[XK_KP_Divide] = Key::NP_DIV; mapKeys[XK_KP_Subtract] = Key::NP_SUB; mapKeys[XK_KP_Decimal] = Key::NP_DECIMAL; + + return olc_Display; + } + + bool PixelGameEngine::olc_OpenGLCreate() + { + glDeviceContext = glXCreateContext(olc_Display, olc_VisualInfo, nullptr, GL_TRUE); + glXMakeCurrent(olc_Display, olc_Window, glDeviceContext); + + XWindowAttributes gwa; + XGetWindowAttributes(olc_Display, olc_Window, &gwa); + glViewport(0, 0, gwa.width, gwa.height); + + glSwapIntervalEXT = nullptr; + glSwapIntervalEXT = (glSwapInterval_t*)glXGetProcAddress((unsigned char*)"glXSwapIntervalEXT"); + if (glSwapIntervalEXT) + glSwapIntervalEXT(olc_Display, olc_Window, 0); + else + { + printf("NOTE: Could not disable VSYNC, glXSwapIntervalEXT() was not found!\n"); + printf(" Don't worry though, things will still work, it's just the\n"); + printf(" frame rate will be capped to your monitors refresh rate - javidx9\n"); + } + + return true; + } + +#endif + + // Need a couple of statics as these are singleton instances + // read from multiple locations + std::atomic PixelGameEngine::bAtomActive{ false }; + std::map PixelGameEngine::mapKeys; + olc::PixelGameEngine* olc::PGEX::pge = nullptr; +#ifdef OLC_DBG_OVERDRAW + int olc::Sprite::nOverdrawCount = 0; +#endif + //============================================================= +} + +#endif + diff --git a/C++/Simple pendulum/output.o b/C++/Simple pendulum/output.o new file mode 100755 index 0000000000000000000000000000000000000000..d37ecf5aa46d4edad8d0a56da4ed501350538c23 GIT binary patch literal 165928 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 literal 0 HcmV?d00001 diff --git a/C++/Simple pendulum/rect.cpp b/C++/Simple pendulum/rect.cpp new file mode 100644 index 0000000..117a101 --- /dev/null +++ b/C++/Simple pendulum/rect.cpp @@ -0,0 +1,164 @@ +#include "rect.h" + +Rect::Rect() { + Clear(); +} + +Rect::Rect(int x, int y, int w, int h) { + SetRect(x, y, w, h); +} + +void Rect::Clear() { + SetRect(0, 0, 0, 0); +} + +std::string Rect::ToString() { + std::string res = "("; + res += std::to_string(x); + res += ", "; + res += std::to_string(y); + res += ", "; + res += std::to_string(w); + res += ", "; + res += std::to_string(h); + res += ")"; + return res; +} + +bool Rect::Intersects(Rect* rect) { + int leftA = x; + int rightA = x + w; + int topA = y; + int bottomA = y + h; + + int leftB = rect->x; + int rightB = rect->x + rect->w; + int topB = rect->y; + int bottomB = rect->y + rect->h; + + if (bottomA <= topB) return false; + if (topA >= bottomB) return false; + if (rightA <= leftB) return false; + if (leftA >= rightB) return false; + + return true; +} + +// bool Rect::Intersects(int x, int y, int w, int h) { +// return Intersects(&CreateRect(x, y, w, h)); +// } + +bool Rect::Contains(Rect* rect) { + return (rect->x >= x && rect->Right() <= (x + w) && rect->y >= y && rect->Bottom() <= (y + h)); +} + +bool Rect::Contains(Vec2* point) { + return (point->x >= x && point->x <= (x + w) && point->y >= y && point->y <= (y + h)); +} + + +bool Rect::Contains(Vec2 point) { + return (point.x >= x && point.x <= (x + w) && point.y >= y && point.y <= (y + h)); +} + +bool Rect::Contains(int x, int y, int w, int h) { + Rect tempRect(x, y, w, h); + return Contains(&tempRect); +} + +Vec2* Rect::Position() { + Vec2* res = new Vec2(x, y); + return res; +} + +Vec2* Rect::Center() { + Vec2* res = new Vec2(x + (w / 2), y + (h / 2)); + return res; +} + +int Rect::CenterX() { + return (x + (w / 2)); +} + +int Rect::CenterY() { + return (y + (h / 2)); +} + +int Rect::Left() { + return x; +} + +int Rect::Right() { + return (x + w); +} + +int Rect::Top() { + return y; +} + +int Rect::Bottom() { + return y + h; +} + +int Rect::Perimiter() { + return (w + w + h + h); +} + +int Rect::Area() { + return (w + h); +} + +int Rect::GetX() { + return x; +} + +int Rect::GetY() { + return y; +} + +int Rect::GetW() { + return w; +} + +int Rect::GetH() { + return h; +} + +void Rect::SetRect(int x, int y, int w, int h) { + this->x = x; + this->y = y; + this->w = w; + this->h = h; +} + +void Rect::SetSize(Vec2* size) { + this->x = size->x; + this->y = size->y; +} + +void Rect::SetPos(Vec2* pos) { + this->w = pos->x; + this->h = pos->y; +} + +void Rect::SetPos(int x, int y) { + this->w = x; + this->h = y; +} + +void Rect::Translate(Vec2* offset) { + this->x += offset->x; + this->y += offset->y; +} + +void Rect::TranslateX(int x) { + this->x += x; +} + +void Rect::TranslateY(int y) { + this->y += y; +} + +Rect::~Rect() { + +} diff --git a/C++/Simple pendulum/rect.h b/C++/Simple pendulum/rect.h new file mode 100644 index 0000000..ba8c015 --- /dev/null +++ b/C++/Simple pendulum/rect.h @@ -0,0 +1,73 @@ +#ifndef RECT_H_ +#define RECT_H_ + +#include + +#include "maths.h" + +class Rect { +public: + Rect(); + Rect(int x, int y, int w, int h); + void Clear(); + + static Rect CreateRect(int x, int y, int w, int h) { + Rect tempRect(x, y, w, h); + return tempRect; + } + + Rect operator+(Rect* rect) { + return Rect(this->x + rect->x, this->y + this->x, w, h); + } + Rect operator-(Rect* rect) { + return Rect(this->x - rect->x, this->y - this->x, w, h); + } + bool operator==(const Rect* rect) { + return (x == rect->x && y == rect->y && w == rect->w && h == rect->h); + } + bool operator!=(const Rect* rect) { + return !(x == rect->x && y == rect->y && w == rect->w && h == rect->h); + } + + std::string ToString(); + + bool Intersects(Rect* rect); + // bool Intersects(int x, int y, int w, int h); + + bool Contains(Rect* rect); + bool Contains(Vec2* point); + bool Contains(Vec2 point); + bool Contains(int x, int y, int w, int h); + + Vec2* Position(); + Vec2* Center(); + int CenterX(); + int CenterY(); + + int Left(); + int Right(); + int Top(); + int Bottom(); + int Perimiter(); + int Area(); + + int GetX(); + int GetY(); + int GetW(); + int GetH(); + + void SetRect(int x, int y, int w, int h); + void SetSize(Vec2* size); + void SetPos(Vec2* pos); + void SetPos(int x, int y); + void Translate(Vec2* offset); + void TranslateX(int x); + void TranslateY(int y); + + int x, y, w, h; + + virtual ~Rect(); +private: +}; + +#endif