From 30f09d95f665b88ced201a705ef562b7f7ca8cdb Mon Sep 17 00:00:00 2001 From: Ben Date: Thu, 31 Jan 2019 17:30:19 +0000 Subject: [PATCH] Mandelbrot explorer --- C++/Mandelbrot explorer/main.cpp | 70 + C++/Mandelbrot explorer/olcPixelGameEngine.h | 2069 ++++++++++++++++++ C++/Mandelbrot explorer/output.o | Bin 0 -> 169272 bytes C++/Pixel-Engine/Rays/.gitignore | 4 + C++/Pixel-Engine/Rays/.vscode/settings.json | 48 + C++/Pixel-Engine/Rays/main.cpp | 19 +- C++/Pixel-Engine/Rays/olcPixelGameEngine.h | 37 +- C++/Pixel-Engine/Rays/output.o | Bin 162824 -> 166992 bytes OpenGL/cube/.vscode/settings.json | 5 + OpenGL/cube/shaders/simple.frag | 10 - OpenGL/cube/shaders/simple.vert | 12 - 11 files changed, 2221 insertions(+), 53 deletions(-) create mode 100644 C++/Mandelbrot explorer/main.cpp create mode 100644 C++/Mandelbrot explorer/olcPixelGameEngine.h create mode 100755 C++/Mandelbrot explorer/output.o create mode 100644 C++/Pixel-Engine/Rays/.gitignore create mode 100644 C++/Pixel-Engine/Rays/.vscode/settings.json create mode 100644 OpenGL/cube/.vscode/settings.json delete mode 100644 OpenGL/cube/shaders/simple.frag delete mode 100644 OpenGL/cube/shaders/simple.vert diff --git a/C++/Mandelbrot explorer/main.cpp b/C++/Mandelbrot explorer/main.cpp new file mode 100644 index 0000000..1f504f6 --- /dev/null +++ b/C++/Mandelbrot explorer/main.cpp @@ -0,0 +1,70 @@ +#include +#include + +#define OLC_PGE_APPLICATION +#include "olcPixelGameEngine.h" + +class Mandelbrot : public olc::PixelGameEngine { +public: + Mandelbrot() { + sAppName = "Mandelbrot set"; + } + + bool OnUserCreate() override { + return true; + } + + bool OnUserUpdate(float fElapsedTime) override { + #pragma omp parallel for schedule(dynamic) + for (int pX = 0; pX < 1000; pX++) { + float mX = ((float)pX - 1000.0f / 2.0f) / 100 * m_scale - 1 + m_x; + #pragma omp parallel for schedule(dynamic) + for (int pY = 0; pY < 600; pY++) { + float mY = (600.0f / 2.0f - (float)pY) / 100 * m_scale - 1 + m_y; + + float x = 0.0f; float y = 0.0f; + int iteration = 0; + while ((x * x) + (y * y) <= 2 * 2 && iteration < m_maxIterations) { + float tX = (x * x) - (y * y) + mX; + y = (2 * x * y) + mY; + x = tX; + iteration++; + } + olc::Pixel colour = olc::Pixel(iteration * 2, iteration * 2, iteration * 2); + DrawRect(pX, pY, 1, 1, colour); + } + } + // m_scale -= 0.01f; + + if (GetKey(olc::Key::W).bHeld) + m_y *= 1.01f; + if (GetKey(olc::Key::S).bHeld) + m_y /= 1.01f; + if (GetKey(olc::Key::D).bHeld) + m_x *= 1.01f; + if (GetKey(olc::Key::A).bHeld) + m_x /= 1.01f; + if (GetKey(olc::Key::DOWN).bHeld) + m_scale *= 1.01f; + if (GetKey(olc::Key::UP).bHeld) + m_scale /= 1.01f; + + std::cout << "Render time: " << fElapsedTime << std::endl; + return true; + } +private: + float m_x = 1.0f; + float m_y = 1.0f; + float m_scale = 0.7f; + int m_maxIterations = 100; + std::map m_palete; +}; + +int main(int argc, char** argv) { + Mandelbrot app; + + app.Construct(1000, 600, 1, 1); + app.Start(); + + return 0; +} diff --git a/C++/Mandelbrot explorer/olcPixelGameEngine.h b/C++/Mandelbrot explorer/olcPixelGameEngine.h new file mode 100644 index 0000000..75645d9 --- /dev/null +++ b/C++/Mandelbrot explorer/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++/Mandelbrot explorer/output.o b/C++/Mandelbrot explorer/output.o new file mode 100755 index 0000000000000000000000000000000000000000..ea4a77ed6735e8fd2069ceb635edb439be1eaf70 GIT binary patch literal 169272 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 -Rect rect; bool isHoverd = false; class RaysApp : public olc::PixelGameEngine { public: - RaysApp() { + RaysApp() + : rect() { sAppName = "Rays"; } bool OnUserCreate() override { rect.SetRect(100, 100, 200, 100); - return true; } bool OnUserUpdate(float fElapsedTime) override { Clear(olc::BLACK); - if (!isHoverd) { - DrawRect(rect.x, rect.y, rect.w, rect.h, olc::RED); - } else { + if (rect.Contains(new Vec2(GetMouseX(), GetMouseY()))) { FillRect(rect.x, rect.y, rect.w, rect.h, olc::RED); + } else { + DrawRect(rect.x, rect.y, rect.w, rect.h, olc::RED); } std::cout << "MouseX: " << GetMouseX() << " MouseY: " << GetMouseY() << std::endl; - - if (rect.Contains(new Vec2(GetMouseX(), GetMouseY()))) { - isHoverd = true; - } else { - isHoverd = false; - } - return true; } + Rect rect; }; int main(int argc, char** argv) { diff --git a/C++/Pixel-Engine/Rays/olcPixelGameEngine.h b/C++/Pixel-Engine/Rays/olcPixelGameEngine.h index 1dfc208..58c5f76 100644 --- a/C++/Pixel-Engine/Rays/olcPixelGameEngine.h +++ b/C++/Pixel-Engine/Rays/olcPixelGameEngine.h @@ -569,24 +569,24 @@ namespace olc //========================================================== -// 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 -// } + 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() { @@ -1273,6 +1273,7 @@ namespace olc 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); diff --git a/C++/Pixel-Engine/Rays/output.o b/C++/Pixel-Engine/Rays/output.o index a7a78d705a9d05c483ca5839fc5b8246cc0dfbe7..a6b6ca853c3be8106a2ca0c8d440ffa2915fdfd1 100755 GIT binary patch delta 43158 zcmaKV33yFc_x?Fo5-}zsB9n-Sh#+DPMH5A@v7x9MLs2y~G)OhvAT*Ervi>p{s@{dX)iTQULO1{Ho&C)Q^mb2o?VlUTLlxB*L zQekwCi*@kDcqsu&u2VG^{*P+f`6x;QlfN<6%FI_LJ(ncHNH0|IPZ zmpj*0_@We|ocJI2NIu$$Pho$n6TdE5_ASndU-+ktGm3eeQE?s{;++cgejp1ZIPv*h zL6Q?6{gL!fapLDPpY|kgQ&ztv3*9MeuNa0Bo`NW+0uPzbb>ho>5`S7~T?vEzQJ_sPa~n91|a9;@6q@m%#J>ujD)NHp5Wr zWH9)hCf=B5s;Ah*cQRE_@qh4k6TgN6$jheW^AynSCD5RO2TlA=6JKcJo16Gz6CY;c zOH6!g6JIKLj6YSp(qt$z6?ogk+fDpB6JKHCH<i*HzC-+nCf+wiCNFM7ij<@=*u+~*{^2IR2J@o+D3d{)4%irN;;C}uiZSstdd6in z@zf~eiZk(iP!Oh3DZKv488_dmE8bK*6P?acw4E-5M(lxnfRtA-frT9O?-ukZ)V~vO?-%nSC)vBwJ9|Bp(ca7 zGzh+hiT5<|Els?yi4QmN!6v?yi4QmN5mn<4hbWVwjj2GiiEnG-V@!OciMN{gb|yZ~ z#J4win*UKILkGhEj5qNeP5d+y-`T_`nD{OxKFP#)HSsCnMgGwgM4Jq0rUKnee2$6l zZsOOO_@_*Ku8Hqy;&)c_Hl>%zkY8<3o;L9ZO?-@rFEsJ7CcfCj_crk*CjJ@0i~cK0 zsmajCRG`en_cif$6W`CoSD1LKiLW&A{Y|`LTPk;Mnt}l)gS&|zXyQFh{2&wWYvKo+ z_+S(NoQV%--bNM2nG8{;0z*xFw22>P;$uww^CsSE;)k2~I1@jzdjG>JFd1Gj6^J+S zqfGoX6VDGp!kS>>N1ObUO#GNC9`m1KGK{S<;9g+j$C>yX6F=U>uQTxzOnk11k2mo< ziKqOdDVS(7u*{0Dwt&|;BMk)n|MzX|Eh`iHSq~1KG?+1G4bKc_>4wVFxO;= zG8K5u#7CR>L=zul;^&!otBId);^PdS`oF+r7-bm1C7Jkm6aTu2pJw70nfL?~zu3el znRu$3#~-V}WJoa;NHg(EOni=sUuxplnfPTUKG(#jR`WJxr^&Fq+Mwi{_!TDppovd2 z@r5Qn-NYB0_zV+YB6#$l=04M8C^Z$xGVx_5KHJ3GP5fIXzQV-knD|N)zp85d$$=jX zs6neu1>8-%X5u|f{8|(5YvOeiA8g{+GcW27HyJjX3PhRs%_ctD#BVY2F(!ViiMN{g zToWIcs>)*o^>~}fFv?V5yNQoC@p%Ut^uAM1i?g__j&rxQQx)s-!bFRs#JcQ+`|+wW zj?PzMa?;@RgSe_vC?WDVt9xwC_q}L)nPUx#ir>CLv7@=p7PESGQ zQ9@rzdR@|kg}xBFZCX7dJOweA9OyBqToL+o(mhD8cnqS&B+}`rr@Tz)qe-Wyo$?Z) z4FZ5%ihmxKv^g}ixS`d*V zi2bC8k)9&-U8J`pJwfQ(Ne?GIUg#T0Z$)~X&{vZlL3)hPGe~budX&(YlHP{&V4>R< z6490jPeIHjot{d{6`@Zjy&dTlk37&n4Z~qB{}vG*X@;96FHRh4d7mN08o?^aP=YkWLRBA`=aen*RiL<}avQ#i~eeF*7_ z(5I9B9O)Gg$$t{*aio_CeKhHG<}WW1`Y_UmkzOeD0i-`qdcM$mlTK&*@?4uBx)U*i zh#aAJAblk1DMF7R{RPq!gdRdVo$1Tth3-%Ki=@X1-HUWO!5kce62;3@REq|?#4ToL+o(qARLqEh4l=?SEl z34Ju_bVM#M5&AIF=aOD1^Z}&5MtZ)`dy}3>$H?+rL3AgAj=<$PLhnHOe9}{d9zps7 z(i4OpLOPvv%j1RaPx|Ym#|hnw^o69y2)#DxZ;&1(bPMUps2?LBh=;8~EF#@g==Vrp zOu8cU8>9>WioZk-ke)*JGNE4}eF^C$LO)0PQql{Bewy@Uq~{C$80ofDB60y~|Gj_ux8tqpSFOvg zRm2S+lDdZ{tjudS!n&+y7`zl~`mc#~ zt(iT~MW~9sEk@Bk4L?r0s%^C0FMic<1nk!IMU~d{c@;U<%)XW5j%!Jy^VBU`lQGSk zJbd7A-1N7mAGc=rEwQE-*~>v#(@RMluZ0b^`zh%vS1~nhQ>1q!_Zd4WDsQHW^tiGpB9v`o+(7qhsT-~pgk8k2R`97So zmzUv=N{wx#`Aq1f=4t~c1lC`6m$=fK7@8t`KW*NGLGJHk_NDzAy2d{>xKai;JU;!?;rI+gt=x3|j3&WUxIV@KRW%VqSQWAo z1$Y$yd7ym}-`?|31=ZO(e=ki~k^O_!6C>PGpx6g$Q4<5z@!H^tLEi0xFze}O$>Bc6 zh47YUo7lAXOd9RXzU!=+BQQ1Jli@|`#FNwx)RBG+WLmnXlj#GkY+}I33FuZ@-{VfU zR=0^gma^!W=R715YN7i7a5z#H9j{T%Y)4@*$~-T4)?85Kd0HDfsj<&~SeF-iyj_@D zNZBZZe61~>)UwSybcG5gy5Xlwb--~nNKf`4L;H4;H*)3rq*m%i&GV(E>KHBRr2xyF z-?hOnMTKm>^}oroo(9UuH7{-JOS{$e+Mvle25ZTaBh@(VgUN?f+kvT5qEw65z}5Tx z>&>r?Ilw}oZn|k-Onu5TMoiA~LQaTgTJ4uZJyUUawPsh5yEXgmdDSoCGQeSFmUej9IzQq`sS~%AZHFLR@VrIS_BSMR& ze1yYvQY1Pe7|n>vhGX?O3`IaBe0!PjhEp9l-K+twvB|Y1)h+ju zTiQ3%Vm-Ux5p6TN@s$?*N>lY)E#{Tb)^5~nZfAO5yEPO4(5JnkmCMsa^Ck7M_7+Oz zYkObuY4DpcW56?M(2MNfY2UvR+Bo5+h?OKdOW7`L8?*+~8>_vwR?~yj=9(2hU9^{{ z2ZjE6Lo`ZMfe^Q_WtTs~lL|UwG<3iA{`64qaj;m^mnX=!h%Iumc4d0gmg8YS+Al&0 zVMLVm!WR>ry*x)WqsV?qYdNF!7+EghM`Rxu+Yesu^JYRFV<<;b7>svHT% znbDd#I3F2w5XEH+#f}lAeK?~@>)djRFXubGZ!xMWrh(~?qb1rNFWWAtIPK3FO~VJk zkXq=G`U_GF@q`2Xmk9s9g(zQ0|rvMy?G%?wgM z*Y@D&7VYbqK`+@T0jh(pK--;%whh{AWSs0wkv$C@QmiH24czGmC012+MfPdbN10c% zv>~&4d+s8_7>pu&rj|P^QthFAGb^^5~PFN zUOt4Krf#R4I#Cq8ye5j#9wW0%%wJFK(yJl3bE*k`s;3r^5Yp*u8V1^mmK6rm;9QOo ztums7B1(Bzk$qLmV1PC!q2ZXRWny+liaC)3d;AxiB3jEylKn2Sd*IY%Oq|ixH?F|X zX)+rl98cy&?Q%j?!4RETF9KR=&OPkTZ7oTZ%Mt(nmq#lXm1eCcR*t? zS|wFvZ>y!uX*+l`T4*pu@VL3n80FTOv4eDPKXsgrO3z!D-SWkG3EO$2{93pji;akG zr*@1Kva!hCKzleR(y}5?iT_um^{L4I4F*6)boKU9m79??W+Jpbh3r>)!Ov7xyGvDrlczOKTx1E_uB}as zv0U7yl_n0Zvmi`SzMywE_MfzTZEhf_fDWGy|=ZT=FA<Q7BCmm7yYnxtw#p2gTtGVzC)u9zFd_jHhK+`wYxVkT!bxU0~%dTn%QbN^itu!S{ z9jnz^(n)Qp^;*)}@^_^+V@Z=%4{(YQBO?0Xi35Vi-s9ILnejBRSPwly=W=d(o=x9>p&5MxpFrO@+{TlfcF=GG@2mXe-L>*nfkD4DQWV!2vIE!N`J`E~8-RM3wL zl20?(?4$mru=`>ycb%W@cRXnS;W4VJ(o^UXHRe0Pd%AuNw7Wb}UUsAahX3h^l{(_O z-YMoh#I#2pt{Mz$W{#ca5Xq_qZfu~wuMJrLY|};*u-#FQrN3e@O=B~Ynp2sq<*yHW zDFE-Njp@j?x?6F`!IoQuw-bDv*ck*7w@F$a*&}kS**NsbXQK*Xfj`o>5oQWyb4acw zWLX00bgkQlRzV3LS2eaMCIRiK3JRA4TFQpbZol8az{F^uZ3tC6YGoV3a3H9;F>K%%@Wpw_gRo?tW3ofAozNyJ(d`&gV<4JECDRFiY-9U}`)y5Xu%8kuz&mV$I zcHdk)9J=FG4fVrQ_KB#TO#SmomDgORZd6n6G4(?=b&;tP)l>s9wng@R)l|Rvc=}rc_h@c#}l2hE-F~G4*scmC95bNj*=!{FFJrYPZ`=xm8om z@n(wR|A8TtcRk^se*91rX7xx4O>O#Rd*zYUn|rFAwO*T>s|~bCo10sHKcv06IYd3B z?b@8}{VrxTbMOheVf4L)jRPenX#=-(9h-ju^VqI``iUcSMP>q+F1ROR789wFMN+X8F;jx)Yp3DPcZ3vm02 zbi2>!Y!Y*uP&n7AMSYjPj97h_%Btp&EmhduU`w4wwp3{wK{oo#=cG~-wiU0CZH2UT zBikVDErf4EVw7;J1-FbDV4Qx7Dwb+iDqN71{EHtv+mP63Dhj+UAihP1rnOTRVqrYo+Z)vdz?9 zeaB~XPZHyVTLZXlc#YgPNVjHW>m+Pmux&~t+a_sqC7Z9XHH2-;JhE+(w(={mJ<={C z{MRIw1yCm&!EO5ja@#K5_L1#`u=&82mqfNaX?vS&JB6(=Y&%{j+YV`aoord!b9p{v z#*vsH-2C9S>j-T+yQEtz*@g>SC~UjGAlq(fYeBYXVQT@~o2w?ooxBH0p!tqp94i^z6Z+WL}hl(4mh?MN}%j!0W8vh~omB78j( zgN0i=xE&i#ZpWnC-OI4K2wQvDP7Ed632FO|Y<55DSqIooK2J96DRdtBlx$}-j~zav zH2`%|_kE~m&0u>JO}0nU_9fX$g)IcO$KA;GSlZqv8+QEnpI76x*WT}| zj@CYXzpdrghnoHUCLMp@fjlU3z4wzME=+PfKIF3R*hx1X*UO--nXaV-_4xW;&&U3k!hS@bu;IQ%AH30B_Of`c zSRSu^x+`!*?5XNk%N{1Dji>RzN^LZ8-_{Y&to%CO2=^ISzG>%Bqn4c5sYQI?-!-v@ zqAXcdN$*!ZmcI-IP2Yb3%^mRpHK)<-QldI<1%-Z2J5N!H?C#nd9|X81kzcH~3E~(M zgSF2;2&mbMM8}8Pg&gPy!sn1fHz$V}a-bh@c#Rx&077)c&~VT{a; zNTCq;Mp)fx^pfK%fW?461!iwL=Oyq$t|x4{N(!CEIJ-^bZ5v zz9ey&mI-kuiS4!B9|qLSB++xX)_zaJ`W$aIQFlJj?tJL)Hk9PEn#Z01w@xH}s6|0+ zOk$QcbWgy`4}X9-1G5`@kcxjwKMv6kqklh>!vHb;JzJ5yjvjBa8-4g4L1!OO=)(xS zH?g12b2xJ3TO&Ln;~85;9Q$~D!C{=%?$AS>_SU`t^}Oc5&lLKpB^5N%zSvh2uN9!x z@!Bd9wBcz*(VqUzSL?aoOKq#g?SHA(`dndIcuG6Gf1z4%pzDEJs>MW{{z-2(&}*^m&Kc+_ z(Z^7~{B(c{Re4VsV7w0G@B)O_RA%dTheNx2-Pb1EV-HTofPGqwABRXfLO;mu zPwNw}>S9O6j))yjFYG+=!Vb^j4RB+_yVJ}@6&Pvx#^%;n=mfYy*}0?zcfUx;78v_H26WzvuLNx#=89sB)d`!2D4T%r#%zwImDEJpHeD{qxMn`OZ81-85 zs6D%9le5UYB+RCKpC~%j`Y7I|;Vg35?hq-_`T7JojB!u;HLCLaVFEd(Q9Fxo{U-H=cDo@vIX&E_OUW@5qs_#PXPTf<}QpQ==@Yte*E#m@{c8vRe<_{J5ry zk8b|>q;K%UjTi*pr#p(rl_L9faGVlPX`>E?S{~@y;)AWCZmt7QAAO44!3m|vJ_-v! zzMABGdUkzP*~8io2U~YZ5d^(nEv5o z=t=YWFPBJV?i&ZQCd_!N!#{mU-d=pw@>@4b+z_~Mp%wLIFNRzyK2cFuATU7 zuf_4UHsMBuw)OA?d?f8SJgL?n;?r8!kYn2P&&R5VwW7~ks(IR-&pXvVh0ibXzS`9u zcA)K%S*q%++7d>|TR#jrbO1P>w1GKW+^|WVC z4fROE=OBMrJt7L}#m?P92i`k1%+lrcIcsJr?Xo3=nf-+Jae=;ml|o8PSw-eeb>@*_+4$( zcO^AXTyQv+Rj8WhZ;iDh-#0!m?)ytM@lv_RrN?f;T~X`vTFm8W+WmzhQzI^uPgeUzyDxLui7Rc^|G$g8@=((_d$mcRD; zy+#KfT-$9?>uc})Hl^+KSNt=-kO2PWD7R`EIe_#3S) z+VUo8EC8a`)Et~MmXY_(~P`bEt{4iDfyyA09@dPdTt}k9KzkPRB#2&l}t1g^`Tcx@1 zc38Ra%UYXzlT_~mZ{1s^s&lnw_ggkez@6xcwf^b2s&R?brrf{L==k!gB*9o#(rmF{->b4ybEhHft7DR-%Bt%{Hh!CG0 z8qF!P|M!KeI%ZQHY{BtfIne20XN&r}HvLg7Hk93u)>|wilC@_Y9qPS4+~GK7e4R>5 zad`X0y`d;0tl4cx0iPe#>Q|g7-0cmuU5=3lf_s9U`y~ur4Mq6*v}&wi|dBqkG=l zh8|&wJOZ&F7}!WrP5^rVZvn>weI_^@$-r>nCSV5eFz^QOBJdIL5wLzd%H!!Y7}x{Y z0XP;obrQZS1l$G81&*HLa1;W&zwB_7*&wWjpfpyLl4%Y{FmOM%z8GNCbcchUw4-OB z<-o(hJm78M31GjO4#zcMBsOUmUq#t~ZJS;m_+YCY1T4c|Jrh_7%mPL&K+l0|foFg_ zfVNu@4ne5vrzod^ZGcOY9F7X$g=G$hufL*L-b90dNvj-=c;Gj<;pPP3H<~nzL{I#3 zxDNhlR1C~V&V&O?fL(zV!1+K=JlmuJqk!)M-W&(EuU62d@8!`02F2F$;i3H$G;K#r$VDKhX2GkvwJlG8fP6H+amjc%SZJQtzKsW+C54-`q4|IPQ z>mGY&6tEL;5O6r~Wndz32XGB=EMBb~2L1`W2CVTOMi4iV=YXNWEMOntZs26#5nw9N z4ews^nqvJ2LpT8Zra3(Nr7b*sh2NnRcfaifbf%kz0 zK<{Sw4R&BBVAuy}I4~NR2pkVw155!HG_xV8Ae;wr26!KM59l3&-`mIYd?#S|hgiqJ z?!aW=WZ)*?O5kDOZs0{=Igmb*{tFlkblZbf1MCfq2TlW~06zxi0qgH|IEroL0KpEN z0dx<=ItR7^hUKH@z+_+o@ISyD;1ys#umV^Dd1!Envte$o5u>lK2^>47eRQ960qbdJaqht^;lW9t0KuOM$+hWBosZ z(EoGn-4XaDVPFs7T;N#XI$$#J1~3=sc?2y7MgXq?`v6^9V}}EV0ySVC;5Oi7V4W|} zbD$qE4>$vO0{A-c8t{w_f=e6RY=EJ_=tA@yI0;Cu7qk+SLvy0Jl z;2vNu@EY(0@B#1|FzXa5ZiguVh6B$6t-##VSZ2Ut;2PisU;)tmD^v{Z1H2DBbOyN= zh4p_1LUa^b_BE0X*aMgZOaiV0t_B_i<^fBA`+=3fLZEMZR1Ay;UIdN;J_IHK+nq&u z-~ix3;ACJaa6YgSSoa%Dc?YcjFbF+5py$A`z(im&a4B#Ta5wNU@I3G$a7PJx-Vqf8 z!+~W$EASqWeuXLcTTB^n?KvbXa3}B#@I3Gq&;hL53HO-)V42$>w1F@PI1`uv+zwm= zEC3b&uLI8m?*s9^UGYDUDeH`z7O)d=E^s*T5-u3~ciwvK`nR=-v%G z8ZaFA2G9z88%V$0l@CkTfEzk;F z1Dp#i0Hy%X12+Nh1NQ^HpF;8hI{_~MhXZc|6M?=LQ63luEC9v;&jW`5?*q>P1A8dS z&%hqQTQ&$|A+-7#JqLCNZUPPm9tO?^UIeBA9|5-m1AAiq1A71~fn$Lomrx!!1h@$} z9e5F#0el3^0|xd|lsmu};6K24psnd;+?6220&{_5fQ7(UfMvjCK>EKjRsn;79{^*3 zCxI!z3Scg8q4wz)e>%RWUg6{DP?h;3aQEs=r$@?oJ^{D-o z(1&+&I9&CkzG_1~&`+)FW=Gi$o%KjR_r9?E6#-J4TBe0Ku!v12Fy%P3Eu)p|(eR!4K*A*+JhfIcO$o_BY^}|$u zJ;h&jvG}l(ONtjOg{0JHrHquFPl+;a^hLwGr}VRwER~Q?uh3T_$-;cyuvk_&94|f5 zhVh~ep~Rt$GoRA?1)%TYkVp5_XOkQUd4H_FmgIcI$X%fyq2w%q{IHKq)C$P{ef7u0 zd)CD1V1*tONcE#^N?*NKAj$@#Ejyppr;==ie9x+{gdDaL{tNs6-}zDu`^x@uZ0wLz z`s-y_HL28g&8_1Ui(kk$G8wA-hNFN;pc_!r5 zL-d6tXF(nor!Q^_c{k*YIQmctRFSojbkatMlRtkA7Vtn#MjQv$HDq+96#Noh=xw;MW zs@vdO7poEWerCHRRyYKMi-tX9slzedY{!^b;)RPop_%IHHWp6Pmg*V()c{wBqnA>K z`slgMR2R3&7_f}-`mSb3^(@GTUeV8yd<%|;X6g@{VG`=r!$d96{X-zPfqZz0T)u-K zA7ptZsfgZm@g}$KrZN5*98v8J0uS?HOPtv zt*!QJ13wLc7NA0xr}Tx*(fKR{znUuc3tJ-WKc~vI>r&GchdMS&#tB9p=TWBda);wI zyu%6r8>HyJja1$Guk5@2!f-zba zY@j$WDlRkh*`cbBTQYonSI`|7cW%hxEA%0BM?VZXVujouEz8DtSmWn4T3@>T5Hw1?+HzP&=;{0kwYnAqFPAm3j>NfTtL5a{m~ z+yEaoAUy6Jk?yFr_L~mJN7URm!;C)GbE9$kKz5Vnj{Xadb#LnP!;qjkkZYysTTtFD zAM#&s>PJW}f!sVzzeI8cCPBBsIQj@+VYx zhz5oZElDuDoYW)3(TEtFE(+51*}XB&5Vu1lIt6k*4pY>ZT*$j|ehJ5TuETkT>}B9m z)AidVE8e)5!k4Bu81i9putI3a$I|7r$3s4W*yNJ}`6LcqB%B^Jp&p=e8~$&GiY9s-K&CBLvEjm4!q8I70-|6C!XADwIkj{og02GaQZ}^FYW$ zhY}1f4y`YS9p`*xfW9RH3A-C*u4Kp=J_Gq1mTy5mk|Afft`DAIGN`padQ@xdXKg@~ zW$4{oBZK20*Ur?(Lk>%X+$+=R){XR(`Uwg&cLnbdNhNNP|4^fB-s3b|_Lp3SPU z%*E1|=8wwI7rD|h^`MTbPwimnmC(;+>TYdNcVJ_5Bun1VdO&_EOLk-|y?b6Ilv=1E?p!3oIUbDOV0J^>$Nz$4C} z)2Gp(PN>`^plNlZOy}ruKg^N{?BhjwMBkh%MH!JtKk4Hm(U1iA6lcr)&VhU)TR#$s z{Laso`AzSrH)YHGu7G?MzSIFve{?Kcu8}Cn;Kh1~gZwr7BtZUxQ)pO$;AgiZlMMJiu8Z1{G zzg6X8`Cck+Nid*qFvz4?2OaI%5rw2V47v77{WQt+3O#zYeme@mDBJq2*6X(i9}4+8 zWHFJD%UGTa`NC?kk42p5Gd58gBs-FjRyAC91d*Pip$^r`g8r&Ms5jk=%dNjLUVm8zHkP8u>TuNAOhrzlJ*{+HG zsGm1-^B&s!Z!=5-(g}8aW&K~bD6$WS{eo;#SR&+N_)^kWtwQr|B4WucssM7OC?_ZU zJoM|DobXD>z1GMH4-Cf9be){=9*`HVlM@~f*?&DHtdEZ0(sOZ30nur_z9&`V#(4#y04SyI}BhAlKfYZ|Q=;FMyo9LFNK|Ix&9( zod$dw(h@q1xQh|@9M$!vkG{3L+Qcn51ds6>_hm=cFfa^7gD}~tC1zQ|_dWPYwuG+Nn zLSbJBduOvfp}J{(V1G+g?>ZS`hHObH#MDi4l=C3Z-y}xawHUHpxU{fCyo$j8S5~^W zz{_tLI2>YyXs(+T;wAK+b}jl3y6l_8GJ7k$u+z!HU6Hc^0ltR9~ zMfBRE5Zx=?!uk1G1O4ePYNLp2An$|hg4Vih#Cgw1Zc~EBv_ukbbJ?Ce!J5Lae87DLLrZbEVg0Dqhy?ZlOgxp`hVwL zD(ur?|G$zW5B6!?rW24yq7FLpU4uN7>vO@=#z2-sA@^hdK9Dk9CCPNNmJ{7Vz z_aP5*5Xw>?iXl(sICjYKTjfsa9wFP<$DE+1wG$5e%KuLvtgvTol}AYWFWOdb6=NBe z200hyYMbNoJ(kw>PS`WH(mO(bw_?aCXb+9A9Wvr!cbDtmy|vJ9(>us;$T?f(&A|%! zn5frBWN#GuRE+v^5!>Z?eQ^x>yaql!a^;*CK#s`e4GMB-u6$g%54kn--feJeWVsXM z;9PplkegT_+BGPb<0sbDZ86xyQs7j`734zR!-WeW??eUE=rYLba&^%Gr7g}i@S&Ha z!H{!u_DUkEvFP;M+=jO@*D}=n3eaaxGih2WFBeBsM;UUiz zAqAbBD<3kzPS4d>cE&CSaWX`-C2|_tI1HUzMuXS6cJ;!dbHwY_yHzpDppW9rWrw_8 zwxJGvvX+MOJrzaY8iAt$eZ=-|uFOvQm<^4k)uZS1!P-Z!g_2K71}i0`$bKLpPy`(j z+mv0zmdW8Bg~L1Z;gGjLrlhe#-i1IIr!e|&{Pv7+INqv(&KMg+jf@)93rf5+*jHd; zjRe~%b@5DC3^^rN|GqDh2fvA;yvJi)w>>_z!C=z1*#@%C@*v0<1L}?2OvoDhXF*=V z{BFo^Fnb^Tzxe0Ga=8;)fYmx4M*D+W4t?C zFkfTbs{4ue8a2Yj3oy~w0+h+x?r09S}h^W>O6f}AX9eCU8X;cVu6K%SN-H>I&AK3Vd+`r}z+be?{i za1hE9_R7<1(@QhBL_wq@aUky6ad~oC_JE8cVu_B0JUOrG4B?gxJtdDmGQisw$Y`FB z4?|8xH5BV2sP=a2gB#k;yibM=RAbcop5Ct5*Kl5*c-5w+e6nl2`hsd% zBuaha8?V0T(v1IqT;mV!;1BGG5XRrr(L6s{Jz3qN+Kkvn-Gnt1PdvQJ0#EV{6ywiz zJ;{6F?(uK_4VAPp|HgYO{CC4_<~XRhkrMsJzZLLNV*ZuKQQi2fVx~wn6yK`9CS#%z zA48a8j*nv%T{RScWf~8eWaQ5p_$pX#sy^&x^)0{UvYb)6A3RjiUj5YX#h`s z^fWcn7Q90GxpTBD!rN2H=V3LXJQ92W-cm}&;Ojt8F%1Rm@4|69Fn`b~&a=#aDtQ}c z3?Ap$aLB2^ROUZ(;$LU}uoIst_yDC1H`Hj*M&_fQ;4!Vc*wE<-172w{AMM1KGT+0A zzs`J&6aQc4`#AAlxG~^=Ar4TiPKM@W06)ly@6LRj6F-vq;ZFQi=0`d4$;^)hFVfl= zBApHKP6c)_KiP>t#QZcT{yg(Do%mmvPpIbQvige+iPZ*?Mh$R2ppi&&;+r#{?8HCC ze2No4iuqJ0ep-#DvVt@xLlO#D@aOna^ldZL#(rM1naT^C+|d{@GOy9Ug?Zx1Ws*0{ zB{0*PNaDYCW{mGe=98UxJBJ(afs7-LMG)zuU8?3sz5h%#sy+V+Go+Kaj9S((?_A5e z#`>82!RbwVfxsacSi<-EWo}9PhL#%|w+rGW^fxwupJ&4UHg54tVu{HY>dH z1n-5Wd+Y0GsqOsk?UyC+ib`A$VGe;gM!y2HCF(ofYqlDxPS;~+V{1y#=K$K@&zH{h zC`s3^HD#eid!_CzL1~P=i|U%Be>)qKHQ+nR<4ILqD`1v&sjnjVZoU1h@G5#Bt@YV! zRxKH1jXsgghsz|7`-QkB;J{5m#_FqJR%hz(BbHyML}{JNbJDDi^vcuk!mIz$FQwV2 z<2akUd?k6jYZI48U0LkJeW~M!F0R*^Ptb=ZpiQ}FBt3=A|Am?QeOg}vvpP&)pMW;4 z*eA`#P2Rn`N!8Kk0Y13-empzJ*tf?1uczdsi(@#H0$%0aA177CKA-<_-a`a0br@54$XxlnKR z8WJyr8z|lhAbEj0~7OU?gvlpKL@nTzC zr8p9hd6s?!X8$NoTjT6B0x!7fPkZ_%>t2a!zYb^i%3^q|5Z5-?skj?YDBeeii@tsD ziBkej0~v{Gq+bY6pi!+26;Qb{{o6z}%J_Q;#_j(ktkk9OsWRG&9Q87niqPB7Q=j$w z`iSI>k;q1L3Yw*_num#Atsk9-Tnl?3y^K^m1cAIPmnDzoAg)KuhjIS%+$kU7`G))l z=*{M1YAf_^^D(u-w`3_i?2Bs>riq$7QeQG(ZQY*41*4@2sUfar*ycs^j!He0`DpN7 zIKeiw#j!^|dnB>PI_Bef^o=F^1M@%eKn!C4QHvBM_+QRwuLa9Sy1ykId3cr0;L)-O zWjIfxF`_qNXka?t8oT>LeaQk0tzR?Qqhn5+-Al~-^4@9W;sWNqzLfRjO^mp_^w=Z} z?ehZKzmA1V~FEDdbwTy}1T^x-ERAI@<-1_)sIKk%ByVK#M&^wtJ7XvxNZw27a9I|h_xp5>(^tKYF@8gU?R1z7 zyYQy88rA&)p1K_ISn|gHRs%0QiC;KI@*5;5W0@~mDT!$2-vdw0uJ~9IPci?AB$3N}1Uei-i^jDdB(Z!3uOSY;mpHw8urGbVg!t$J zNf=}3jm1m+*)qwmW&a!C<%0BTExUawUYZ`)khlOmm0WX1>c$@ODf7$zlDv_5o^7Q6 z1*chji}`hYw=w*G29M$PQF<+ufu50|)WthI(X`IXAyE_Zu1QlL_Bc)+;NRONiFcSU zHcHw&BV|dq)6#^D5!V3lRC51DsfTlC7cp8p~}#EOF0L9K|NW1CM}SAYUW)%`(y;*- z7@%)kj16e5gd+lMb+G@@P`af{uNk~s#ek>Qr#UUKnaoe)+{G?t+D5mxpbV)RV-Ak~q$M40zPr zTe)^!GDgJc$-*k8o;r9N>f#+!bRf%>uVKaw`;xvQ^&S&h`2 zEkn*EW1}E1=LgfZ%vT?-JSUr%uf}21coVy4VGtQ zf!W+3qX*A3fASCM7{UJUfR`D$mp$5iCQT37qdo4@)CA{qKtA)%&))uhrN8qrDuVg$ zJlEJp#Py@(ZAzyjl334%`u*gX4CBdb#(WFruP=~f2CET`#~^z+PELr8$(nzGA*9mp;vWV~iKocIlQR zkfh@JjCtqhpZ_vnc2)Wr`)%jJqKC+)zoY?KDz3g{5L@^WNR)5WoOZ^qWKm_!R9V6p zv|qtf)6VlIl`GB4t3xE8z*CB2fw)#PKZv*1w#@f^PWXE%U5-n}7|~<;-87s@Ju#A0 z&*MUAHWu1!@RV^Ewn%DY?>qn?-DU&_uw$j3Y-t2 zgTT||8-IXy68q27CuU+>Sqw8BSdyF$EO;#iv${oisaGXD5Mb*)Qs%$$v0XTa+ytKb z*oOOP>}?d2Qw2H61F9?1#_UcN(y*tL>@yaZ)E7qD|Ljn5+wy&?lSf6`P2p62c9 zXc<7~IAPOydIonR2_B~(OH%}UJP)2mCi#IR1~Pw}d6&DAFjmN0GdNq*+-dd7F}(FPW#< zaDWR8FOY;WB$t`@y)SvALDmFWf%u`Q3h|KJ;yz@jja?Ct>VAGF)@6 z<}7&HlZ`dSUp-NNmBl)~CR?||F8#f@*dpdXKPU-oY~nhjUqNlIWiU6OyG#E@iL&1F z3#3B>cI*ybB=j9gRCSyABc~*9W&dD(+gfxZY@RH(lSd_#&0U!v#di>6N|Qw~+Xgm2 z{kiminv4C-e3xUAh-7}ieA(*JIg)sRbLutl)aqca##o+S=pFGM+$g5NMqaafoj_`rTD0_f;U*3`*3}8-ou6TAr%M03eD5=c|6ArK zJ)k%<@M}|Cf!}gj;0*V`*cqlU@BDJjdxiA>hSS{`xe3f4J1he?;rOL`gH6}~?g?n$ z>`mFt6WEXFG8W5F@YK!2KTE!?1m%D}2c`S}!Di>A8k#1=R4|G%=dEI@Lt67 z5)qrshC~saAI9L%3K9^SAd%!i)Jd=AFMh6}yh7#P4lLG{g^nmqm;z3DLK0$0@PfdTBO( zGr?%*dgc>(AdCU}T@O0 z17l>xHEENGV^a#aKoA>tNrO_!6J&g%TW7P(y!@%su_gPr08hi>{N2=H%*X#J9gPK~ zi8x$X(FRy*H>tHnhQ7^r7Gq8#iKheH-~sI6{QiA3c&dE&4H>XC$6e06^K0J|M#Lt+ zLZWvG#_wYIa!GY7*J*12iDK`#ElqvcV;b|$=kc1k(m&;xbgaYvQ<-;u^1lS0HjVp_ zrK6GXe@lP-|BE-J!I=7u+hheR$|T=Kg7W-!$qzd!iEhlV2Tv75|04-w>UXoh$}`h| z{p-9V;{@*~!iL}3VnY-gRvwVVbmre>B#DvCzp1z1fph!u9kS!SDkV?9J4V+feIm@A|A3h$cI_9E zwn|V!-;;dnPb9H`4+xvVi!^M1ls%lk9GtLImUMmxxLd!A3f1ERY?s)p3(r#k*IVm- zS$Z-Lm%%S#{vfYJV;KJfp5`@Uv z(#|`3*lrp1F+Us_J4Y(>&fjeQ1ia`*lQZnG^qy>>QPSl@_OOkDL@jpycKbWz0X~>N z)90yI4zPdi0vXYmgWs6{>w)A$cti&6k@4-{J=p?X@-}7lUTN^*0v~XJ5^l0L^S$9m z4RubISu+&y2$kq0&`pcY&~b2iNQUJ~`0k7vH#Wl0!4KF<7@zn=8> z*k_Dw5G2}C%Y5YM8LKi0Jay%x`%jiEVm^!Wz7Drw2{$>F4x9&*)VO;DN@FLG_mXBmf z=bLi?^T}MYHJ40g-u-h)7%QCqXDG@#=T95!ek^(8mks?mj-TN1LVGt}Xh%Y!<~whW zDa<>6S#O)(em73#N7(HAy<0!*dcopm8xDyAD__cLy}0({%rE<062|K4aFG2u^*q@B zDe!W8>CYZRc_NI6|LAwoB)`U=$^b*I%aUt3z$U%fhuD37XS4HrtNMpzvHE;l>dwU` zgQrnF^0y@BGM}O^`A}^)<*jD&a!JhjXDP1Znt6~eWDo6XK|+>SAS3C*O-UjlxW=`M=lw`&AsO344D zS-yHNNOx4Ku63iHd3E;uDRUE_@9;ujecy4l($4gt2U2g4WNEx=kDET|4ND_6QJ?gN ekv7{WmOKYqwY?4_RTVK0VPA^#1^qCYy-> delta 43406 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