Upstream for PGE updates.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
olcPixelGameEngine/olcPixelGameEngine.h

3086 lines
108 KiB

6 years ago
/*
6 years ago
olcPixelGameEngine.h
6 years ago
+-------------------------------------------------------------+
| OneLoneCoder Pixel Game Engine v2.03 |
| "What do you need? Pixels... Lots of Pixels..." - javidx9 |
6 years ago
+-------------------------------------------------------------+
What is this?
~~~~~~~~~~~~~
olc::PixelGameEngine is a single file, cross platform graphics and userinput
framework used for games, visualisations, algorithm exploration and learning.
It was developed by YouTuber "javidx9" as an assistive tool for many of his
videos. The goal of this project is to provide high speed graphics with
minimal project setup complexity, to encourage new programmers, younger people,
and anyone else that wants to make fun things.
However, olc::PixelGameEngine is not a toy! It is a powerful and fast utility
capable of delivering high resolution, high speed, high quality applications
which behave the same way regardless of the operating system or platform.
This file provides the core utility set of the olc::PixelGameEngine, including
window creation, keyboard/mouse input, main game thread, timing, pixel drawing
routines, image/sprite loading and drawing routines, and a bunch of utility
types to make rapid development of games/visualisations possible.
6 years ago
License (OLC-3)
~~~~~~~~~~~~~~~
Copyright 2018 - 2020 OneLoneCoder.com
6 years ago
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.
6 years ago
Links
~~~~~
YouTube: https://www.youtube.com/javidx9
https://www.youtube.com/javidx9extra
6 years ago
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
Community: https://community.onelonecoder.com
6 years ago
Compiling in Linux
~~~~~~~~~~~~~~~~~~
You will need a modern C++ compiler, so update yours!
6 years ago
To compile use the command:
g++ -o YourProgName YourSource.cpp -lX11 -lGL -lpthread -lpng -lstdc++fs -std=c++17
6 years ago
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
6 years ago
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.
Guide for installing recent GCC for Windows:
https://www.msys2.org/
Guide for configuring code::blocks:
https://solarianprogrammer.com/2019/11/05/install-gcc-windows/
https://solarianprogrammer.com/2019/11/16/install-codeblocks-gcc-windows-build-c-cpp-fortran-programs/
Add these libraries to "Linker Options":
user32 gdi32 opengl32 gdiplus Shlwapi stdc++fs
Set these compiler options: -std=c++17
6 years ago
Ports
~~~~~
olc::PixelGameEngine has been ported and tested with varying degrees of
success to: WinXP, Win7, Win8, Win10, Various Linux, Raspberry Pi,
Chromebook, Playstation Portable (PSP) and Nintendo Switch. If you are
interested in the details of these ports, come and visit the Discord!
6 years ago
Thanks
~~~~~~
I'd like to extend thanks to Eremiell, slavka, gurkanctn, Phantim, IProgramInCPP
JackOJC, KrossX, Huhlig, Dragoneye, Appa, JustinRichardsMusic, SliceNDice
Ralakus, Gorbit99, raoul, joshinils, benedani, Moros1138, SaladinAkara & MagetzUb
for advice, ideas and testing, and I'd like to extend my appreciation to the
135K YouTube followers, 60+ Patreons and 6K Discord server members who give me
the motivation to keep going with all this :D
6 years ago
Significant Contributors: @MaGetzUb, @slavka, @Dragoneye & @Gorbit99
Special thanks to those who bring gifts!
GnarGnarHead.......Domina
Gorbit99...........Bastion, Ori & The Blind Forest, Terraria
Marti Morta........Gris
Danicron...........Terraria
SaladinAkara.......Aseprite
Special thanks to my Patreons too - I wont name you on here, but I've
certainly enjoyed my tea and flapjacks :D
6 years ago
Author
~~~~~~
David Barr, aka javidx9, <EFBFBD>OneLoneCoder 2018, 2019, 2020
2.01: Made renderer and platform static for multifile projects
2.02: Added Decal destructor, optimised Pixel constructor
2.03: Added FreeBSD flags, Added DrawStringDecal()
6 years ago
*/
//////////////////////////////////////////////////////////////////////////////////////////
// O------------------------------------------------------------------------------O
// | Example "Hello World" Program (main.cpp) |
// O------------------------------------------------------------------------------O
/*
#define OLC_PGE_APPLICATION
#include "olcPixelGameEngine.h"
// Override base class with your custom functionality
class Example : public olc::PixelGameEngine
{
public:
Example()
{
// Name you application
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() % 256, rand() % 256, rand()% 256));
return true;
}
};
6 years ago
int main()
{
Example demo;
if (demo.Construct(256, 240, 4, 4))
demo.Start();
return 0;
}
*/
6 years ago
#ifndef OLC_PGE_DEF
#define OLC_PGE_DEF
6 years ago
// O------------------------------------------------------------------------------O
// | STANDARD INCLUDES |
// O------------------------------------------------------------------------------O
6 years ago
#include <cmath>
#include <cstdint>
#include <string>
#include <iostream>
#include <streambuf>
#include <sstream>
6 years ago
#include <chrono>
#include <vector>
#include <list>
#include <thread>
#include <atomic>
#include <condition_variable>
#include <fstream>
#include <map>
#include <functional>
#include <algorithm>
#include <array>
#include <cstring>
6 years ago
// O------------------------------------------------------------------------------O
// | COMPILER CONFIGURATION ODDITIES |
// O------------------------------------------------------------------------------O
#define USE_EXPERIMENTAL_FS
#if defined(_WIN32)
#if _MSC_VER >= 1920 && _MSVC_LANG >= 201703L
#undef USE_EXPERIMENTAL_FS
#endif
#endif
#if defined(__linux__) || defined(__MINGW32__) || defined(__EMSCRIPTEN__) || defined(__FreeBSD__)
#if __cplusplus >= 201703L
#undef USE_EXPERIMENTAL_FS
#endif
#endif
#if defined(USE_EXPERIMENTAL_FS)
// C++14
#define _SILENCE_EXPERIMENTAL_FILESYSTEM_DEPRECATION_WARNING
#include <experimental/filesystem>
namespace _gfs = std::experimental::filesystem::v1;
#else
// C++17
#include <filesystem>
namespace _gfs = std::filesystem;
#endif
#if defined(UNICODE) || defined(_UNICODE)
#define olcT(s) L##s
#else
#define olcT(s) s
#endif
#define UNUSED(x) (void)(x)
#if !defined(OLC_GFX_OPENGL33) && !defined(OLC_GFX_DIRECTX10)
#define OLC_GFX_OPENGL10
#endif
// O------------------------------------------------------------------------------O
// | olcPixelGameEngine INTERFACE DECLARATION |
// O------------------------------------------------------------------------------O
namespace olc
6 years ago
{
class PixelGameEngine;
// Pixel Game Engine Advanced Configuration
constexpr uint8_t nMouseButtons = 5;
constexpr uint8_t nDefaultAlpha = 0xFF;
constexpr uint32_t nDefaultPixel = (nDefaultAlpha << 24);
enum rcode { FAIL = 0, OK = 1, NO_FILE = -1 };
// O------------------------------------------------------------------------------O
// | olc::Pixel - Represents a 32-Bit RGBA colour |
// O------------------------------------------------------------------------------O
6 years ago
struct Pixel
{
union
{
uint32_t n = nDefaultPixel;
struct { uint8_t r; uint8_t g; uint8_t b; uint8_t a; };
6 years ago
};
enum Mode { NORMAL, MASK, ALPHA, CUSTOM };
Pixel();
Pixel(uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha = nDefaultAlpha);
Pixel(uint32_t p);
bool operator==(const Pixel& p) const;
bool operator!=(const Pixel& p) const;
6 years ago
};
Pixel PixelF(float red, float green, float blue, float alpha = 1.0f);
// O------------------------------------------------------------------------------O
// | USEFUL CONSTANTS |
// O------------------------------------------------------------------------------O
6 years ago
static const Pixel
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),
WHITE(255, 255, 255),BLACK(0, 0, 0), BLANK(0, 0, 0, 0);
enum Key
{
NONE,
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, PERIOD
6 years ago
};
// O------------------------------------------------------------------------------O
// | olc::vX2d - A generic 2D vector type |
// O------------------------------------------------------------------------------O
#if !defined(OLC_IGNORE_VEC2D)
template <class T>
struct v2d_generic
{
T x = 0;
T y = 0;
inline v2d_generic() : x(0), y(0) { }
inline v2d_generic(T _x, T _y) : x(_x), y(_y) { }
inline v2d_generic(const v2d_generic& v) : x(v.x), y(v.y){ }
inline T mag() { return std::sqrt(x * x + y * y); }
inline T mag2() { return x * x + y * y; }
inline v2d_generic norm() { T r = 1 / mag(); return v2d_generic(x*r, y*r); }
inline v2d_generic perp() { return v2d_generic(-y, x); }
inline T dot(const v2d_generic& rhs) { return this->x * rhs.x + this->y * rhs.y; }
inline T cross(const v2d_generic& rhs) { return this->x * rhs.y - this->y * rhs.x; }
inline v2d_generic operator + (const v2d_generic& rhs) const { return v2d_generic(this->x + rhs.x, this->y + rhs.y);}
inline v2d_generic operator - (const v2d_generic& rhs) const { return v2d_generic(this->x - rhs.x, this->y - rhs.y);}
inline v2d_generic operator * (const T& rhs) const { return v2d_generic(this->x * rhs, this->y * rhs); }
inline v2d_generic operator * (const v2d_generic& rhs) const { return v2d_generic(this->x * rhs.x, this->y * rhs.y);}
inline v2d_generic operator / (const T& rhs) const { return v2d_generic(this->x / rhs, this->y / rhs); }
inline v2d_generic operator / (const v2d_generic& rhs) const { return v2d_generic(this->x / rhs.x, this->y / rhs.y);}
inline v2d_generic& operator += (const v2d_generic& rhs) { this->x += rhs.x; this->y += rhs.y; return *this; }
inline v2d_generic& operator -= (const v2d_generic& rhs) { this->x -= rhs.x; this->y -= rhs.y; return *this; }
inline v2d_generic& operator *= (const T& rhs) { this->x *= rhs; this->y *= rhs; return *this; }
inline v2d_generic& operator /= (const T& rhs) { this->x /= rhs; this->y /= rhs; return *this; }
inline operator v2d_generic<int32_t>() const { return { static_cast<int32_t>(this->x), static_cast<int32_t>(this->y) }; }
inline operator v2d_generic<float>() const { return { static_cast<float>(this->x), static_cast<float>(this->y) }; }
inline operator v2d_generic<double>() const { return { static_cast<double>(this->x), static_cast<double>(this->y) }; }
};
// Note: joshinils has some good suggestions here, but they are complicated to implement at this moment,
// however they will appear in a future version of PGE
template<class T> inline v2d_generic<T> operator * (const float& lhs, const v2d_generic<T>& rhs)
{ return v2d_generic<T>((T)(lhs * (float)rhs.x), (T)(lhs * (float)rhs.y)); }
template<class T> inline v2d_generic<T> operator * (const double& lhs, const v2d_generic<T>& rhs)
{ return v2d_generic<T>((T)(lhs * (double)rhs.x), (T)(lhs * (double)rhs.y)); }
template<class T> inline v2d_generic<T> operator * (const int& lhs, const v2d_generic<T>& rhs)
{ return v2d_generic<T>((T)(lhs * (int)rhs.x), (T)(lhs * (int)rhs.y)); }
template<class T> inline v2d_generic<T> operator / (const float& lhs, const v2d_generic<T>& rhs)
{ return v2d_generic<T>((T)(lhs / (float)rhs.x), (T)(lhs / (float)rhs.y)); }
template<class T> inline v2d_generic<T> operator / (const double& lhs, const v2d_generic<T>& rhs)
{ return v2d_generic<T>((T)(lhs / (double)rhs.x), (T)(lhs / (double)rhs.y)); }
template<class T> inline v2d_generic<T> operator / (const int& lhs, const v2d_generic<T>& rhs)
{ return v2d_generic<T>((T)(lhs / (int)rhs.x), (T)(lhs / (int)rhs.y)); }
typedef v2d_generic<int32_t> vi2d;
typedef v2d_generic<uint32_t> vu2d;
typedef v2d_generic<float> vf2d;
typedef v2d_generic<double> vd2d;
#endif
6 years ago
// O------------------------------------------------------------------------------O
// | olc::HWButton - Represents the state of a hardware button (mouse/key/joy) |
// O------------------------------------------------------------------------------O
6 years ago
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 true for all frames between pressed and released events
6 years ago
};
// O------------------------------------------------------------------------------O
// | olc::ResourcePack - A virtual scrambled filesystem to pack your assets into |
// O------------------------------------------------------------------------------O
struct ResourceBuffer : public std::streambuf
{
ResourceBuffer(std::ifstream &ifs, uint32_t offset, uint32_t size);
std::vector<char> vMemory;
};
6 years ago
class ResourcePack : public std::streambuf
{
public:
ResourcePack();
~ResourcePack();
bool AddFile(const std::string& sFile);
bool LoadPack(const std::string& sFile, const std::string& sKey);
bool SavePack(const std::string& sFile, const std::string& sKey);
ResourceBuffer GetFileBuffer(const std::string& sFile);
bool Loaded();
private:
struct sResourceFile { uint32_t nSize; uint32_t nOffset; };
std::map<std::string, sResourceFile> mapFiles;
std::ifstream baseFile;
std::vector<char> scramble(const std::vector<char>& data, const std::string& key);
std::string makeposix(const std::string& path);
};
// O------------------------------------------------------------------------------O
// | olc::Sprite - An image represented by a 2D array of olc::Pixel |
// O------------------------------------------------------------------------------O
6 years ago
class Sprite
{
public:
Sprite();
Sprite(const std::string& sImageFile, olc::ResourcePack *pack = nullptr);
6 years ago
Sprite(int32_t w, int32_t h);
~Sprite();
public:
olc::rcode LoadFromFile(const std::string& sImageFile, olc::ResourcePack *pack = nullptr);
olc::rcode LoadFromPGESprFile(const std::string& sImageFile, olc::ResourcePack *pack = nullptr);
olc::rcode SaveToPGESprFile(const std::string& sImageFile);
6 years ago
public:
int32_t width = 0;
int32_t height = 0;
enum Mode { NORMAL, PERIODIC };
enum Flip { NONE = 0, HORIZ = 1, VERT = 2 };
6 years ago
public:
void SetSampleMode(olc::Sprite::Mode mode = olc::Sprite::Mode::NORMAL);
6 years ago
Pixel GetPixel(int32_t x, int32_t y);
bool SetPixel(int32_t x, int32_t y, Pixel p);
Pixel GetPixel(const olc::vi2d& a);
bool SetPixel(const olc::vi2d& a, Pixel p);
6 years ago
Pixel Sample(float x, float y);
6 years ago
Pixel SampleBL(float u, float v);
6 years ago
Pixel* GetData();
Pixel *pColData = nullptr;
Mode modeSample = Mode::NORMAL;
};
6 years ago
// O------------------------------------------------------------------------------O
// | olc::Decal - A GPU resident storage of an olc::Sprite |
// O------------------------------------------------------------------------------O
class Decal
{
public:
Decal(olc::Sprite* spr);
virtual ~Decal();
void Update();
public: // But dont touch
int32_t id = -1;
olc::Sprite* sprite = nullptr;
olc::vf2d vUVScale = { 1.0f, 1.0f };
6 years ago
};
// O------------------------------------------------------------------------------O
// | Auxilliary components internal to engine |
// O------------------------------------------------------------------------------O
6 years ago
struct DecalInstance
6 years ago
{
olc::Decal* decal = nullptr;
olc::vf2d pos[4] = {{ 0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}};
olc::vf2d uv[4] = {{ 0.0f, 0.0f}, {0.0f, 1.0f}, {1.0f, 1.0f}, {1.0f, 0.0f}};
float w[4] = { 1, 1, 1, 1 };
olc::Pixel tint;
6 years ago
};
struct LayerDesc
{
olc::vf2d vOffset = { 0, 0 };
olc::vf2d vScale = { 1, 1 };
bool bShow = false;
bool bUpdate = false;
olc::Sprite* pDrawTarget = nullptr;
uint32_t nResID = 0;
std::vector<DecalInstance> vecDecalInstance;
olc::Pixel tint = olc::WHITE;
std::function<void()> funcHook = nullptr;
};
6 years ago
class Renderer
{
public:
virtual void PrepareDevice() = 0;
virtual olc::rcode CreateDevice(std::vector<void*> params, bool bFullScreen, bool bVSYNC) = 0;
virtual olc::rcode DestroyDevice() = 0;
virtual void DisplayFrame() = 0;
virtual void PrepareDrawing() = 0;
virtual void DrawLayerQuad(const olc::vf2d& offset, const olc::vf2d& scale, const olc::Pixel tint) = 0;
virtual void DrawDecalQuad(const olc::DecalInstance& decal) = 0;
virtual uint32_t CreateTexture(const uint32_t width, const uint32_t height) = 0;
virtual void UpdateTexture(uint32_t id, olc::Sprite* spr) = 0;
virtual uint32_t DeleteTexture(const uint32_t id) = 0;
virtual void ApplyTexture(uint32_t id) = 0;
virtual void UpdateViewport(const olc::vi2d& pos, const olc::vi2d& size) = 0;
virtual void ClearBuffer(olc::Pixel p, bool bDepth) = 0;
static olc::PixelGameEngine* ptrPGE;
};
class Platform
{
public:
virtual olc::rcode ApplicationStartUp() = 0;
virtual olc::rcode ApplicationCleanUp() = 0;
virtual olc::rcode ThreadStartUp() = 0;
virtual olc::rcode ThreadCleanUp() = 0;
virtual olc::rcode CreateGraphics(bool bFullScreen, bool bEnableVSYNC, const olc::vi2d& vViewPos, const olc::vi2d& vViewSize) = 0;
virtual olc::rcode CreateWindowPane(const olc::vi2d& vWindowPos, olc::vi2d& vWindowSize, bool bFullScreen) = 0;
virtual olc::rcode SetWindowTitle(const std::string& s) = 0;
virtual olc::rcode StartSystemEventLoop() = 0;
virtual olc::rcode HandleSystemEvent() = 0;
static olc::PixelGameEngine* ptrPGE;
};
static std::unique_ptr<Renderer> renderer;
static std::unique_ptr<Platform> platform;
static std::map<size_t, uint8_t> mapKeys;
// O------------------------------------------------------------------------------O
// | olc::PixelGameEngine - The main BASE class for your application |
// O------------------------------------------------------------------------------O
6 years ago
class PixelGameEngine
{
public:
PixelGameEngine();
virtual ~PixelGameEngine();
6 years ago
public:
olc::rcode Construct(int32_t screen_w, int32_t screen_h, int32_t pixel_w, int32_t pixel_h,
bool full_screen = false, bool vsync = false);
olc::rcode Start();
6 years ago
public: // User Override Interfaces
6 years ago
// 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 one clean coder
6 years ago
virtual bool OnUserDestroy();
public: // Hardware Interfaces
// Returns true if window is currently in focus
bool IsFocused();
6 years ago
// Get the state of a specific keyboard button
HWButton GetKey(Key k);
// Get the state of a specific mouse button
6 years ago
HWButton GetMouse(uint32_t b);
6 years ago
// Get Mouse X coordinate in "pixel" space
int32_t GetMouseX();
// Get Mouse Y coordinate in "pixel" space
int32_t GetMouseY();
// Get Mouse Wheel Delta
int32_t GetMouseWheel();
6 years ago
public: // Utility
// Returns the width of the screen in "pixels"
const int32_t ScreenWidth();
6 years ago
// Returns the height of the screen in "pixels"
const int32_t ScreenHeight();
6 years ago
// 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
olc::Sprite* GetDrawTarget();
// Resize the primary screen sprite
void SetScreenSize(int w, int h);
6 years ago
// Specify which Sprite should be the target of drawing functions, use nullptr
// to specify the primary screen
void SetDrawTarget(Sprite *target);
// Gets the current Frames Per Second
uint32_t GetFPS();
public: // CONFIGURATION ROUTINES
// Layer targeting functions
void SetDrawTarget(uint8_t layer);
void EnableLayer(uint8_t layer, bool b);
void SetLayerOffset(uint8_t layer, const olc::vf2d& offset);
void SetLayerOffset(uint8_t layer, float x, float y);
void SetLayerScale(uint8_t layer, const olc::vf2d& scale);
void SetLayerScale(uint8_t layer, float x, float y);
void SetLayerTint(uint8_t layer, const olc::Pixel& tint);
void SetLayerCustomRenderFunction(uint8_t layer, std::function<void()> f);
std::vector<LayerDesc>& GetLayers();
uint32_t CreateLayer();
6 years ago
// 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<olc::Pixel(const int x, const int y, const olc::Pixel& pSource, const olc::Pixel& pDest)> 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)
[[deprecated]]
void SetSubPixelOffset(float ox, float oy);
public: // DRAWING ROUTINES
6 years ago
// Draws a single Pixel
virtual bool Draw(int32_t x, int32_t y, Pixel p = olc::WHITE);
bool Draw(const olc::vi2d& pos, Pixel p = olc::WHITE);
6 years ago
// 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, uint32_t pattern = 0xFFFFFFFF);
void DrawLine(const olc::vi2d& pos1, const olc::vi2d& pos2, Pixel p = olc::WHITE, uint32_t pattern = 0xFFFFFFFF);
6 years ago
// Draws a circle located at (x,y) with radius
void DrawCircle(int32_t x, int32_t y, int32_t radius, Pixel p = olc::WHITE, uint8_t mask = 0xFF);
void DrawCircle(const olc::vi2d& pos, int32_t radius, Pixel p = olc::WHITE, uint8_t mask = 0xFF);
6 years ago
// Fills a circle located at (x,y) with radius
void FillCircle(int32_t x, int32_t y, int32_t radius, Pixel p = olc::WHITE);
void FillCircle(const olc::vi2d& pos, int32_t radius, Pixel p = olc::WHITE);
6 years ago
// 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);
void DrawRect(const olc::vi2d& pos, const olc::vi2d& size, Pixel p = olc::WHITE);
6 years ago
// 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);
void FillRect(const olc::vi2d& pos, const olc::vi2d& size, Pixel p = olc::WHITE);
6 years ago
// 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);
void DrawTriangle(const olc::vi2d& pos1, const olc::vi2d& pos2, const olc::vi2d& pos3, Pixel p = olc::WHITE);
6 years ago
// 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);
void FillTriangle(const olc::vi2d& pos1, const olc::vi2d& pos2, const olc::vi2d& pos3, Pixel p = olc::WHITE);
// Draws an entire sprite at well in my defencelocation (x,y)
void DrawSprite(int32_t x, int32_t y, Sprite *sprite, uint32_t scale = 1, uint8_t flip = olc::Sprite::NONE);
void DrawSprite(const olc::vi2d& pos, Sprite *sprite, uint32_t scale = 1, uint8_t flip = olc::Sprite::NONE);
6 years ago
// Draws an area of a sprite at location (x,y), where the
6 years ago
// 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, uint8_t flip = olc::Sprite::NONE);
void DrawPartialSprite(const olc::vi2d& pos, Sprite *sprite, const olc::vi2d& sourcepos, const olc::vi2d& size, uint32_t scale = 1, uint8_t flip = olc::Sprite::NONE);
// Draws a whole decal, with optional scale and tinting
void DrawDecal(const olc::vf2d& pos, olc::Decal *decal, const olc::vf2d& scale = { 1.0f,1.0f }, const olc::Pixel& tint = olc::WHITE);
// Draws a region of a decal, with optional scale and tinting
void DrawPartialDecal(const olc::vf2d& pos, olc::Decal* decal, const olc::vf2d& source_pos, const olc::vf2d& source_size, const olc::vf2d& scale = { 1.0f,1.0f }, const olc::Pixel& tint = olc::WHITE);
void DrawWarpedDecal(olc::Decal* decal, const olc::vf2d(&pos)[4], const olc::Pixel& tint = olc::WHITE);
void DrawWarpedDecal(olc::Decal* decal, const olc::vf2d* pos, const olc::Pixel& tint = olc::WHITE);
void DrawWarpedDecal(olc::Decal* decal, const std::array<olc::vf2d, 4>& pos, const olc::Pixel& tint = olc::WHITE);
void DrawRotatedDecal(const olc::vf2d& pos, olc::Decal* decal, const float fAngle, const olc::vf2d& center = { 0.0f, 0.0f }, const olc::vf2d& scale = { 1.0f,1.0f }, const olc::Pixel& tint = olc::WHITE);
void DrawStringDecal(const olc::vf2d& pos, const std::string& sText, const Pixel col = olc::WHITE, const olc::vf2d& scale = { 1.0f, 1.0f });
// Draws a single line of text
void DrawString(int32_t x, int32_t y, const std::string& sText, Pixel col = olc::WHITE, uint32_t scale = 1);
void DrawString(const olc::vi2d& pos, const std::string& sText, Pixel col = olc::WHITE, uint32_t scale = 1);
// Clears entire draw target to Pixel
void Clear(Pixel p);
// Clears the rendering back buffer
void ClearBuffer(Pixel p, bool bDepth = true);
6 years ago
public: // Branding
std::string sAppName;
private: // Inner mysterious workings
Sprite* pDrawTarget = nullptr;
Pixel::Mode nPixelMode = Pixel::NORMAL;
float fBlendFactor = 1.0f;
olc::vi2d vScreenSize = { 256, 240 };
olc::vf2d vInvScreenSize = { 1.0f / 256.0f, 1.0f / 240.0f };
olc::vi2d vPixelSize = { 4, 4 };
olc::vi2d vMousePos = { 0, 0 };
int32_t nMouseWheelDelta = 0;
olc::vi2d vMousePosCache = { 0, 0 };
int32_t nMouseWheelDeltaCache = 0;
olc::vi2d vWindowSize = { 0, 0 };
olc::vi2d vViewPos = { 0, 0 };
olc::vi2d vViewSize = { 0,0 };
bool bFullScreen = false;
olc::vf2d vPixel = { 1.0f, 1.0f };
bool bHasInputFocus = false;
bool bHasMouseFocus = false;
bool bEnableVSYNC = false;
float fFrameTimer = 1.0f;
int nFrameCount = 0;
Sprite* fontSprite = nullptr;
Decal* fontDecal = nullptr;
Sprite* pDefaultDrawTarget = nullptr;
std::vector<LayerDesc> vLayers;
uint8_t nTargetLayer = 0;
uint32_t nLastFPS = 0;
std::function<olc::Pixel(const int x, const int y, const olc::Pixel&, const olc::Pixel&)> funcPixelMode;
std::chrono::time_point<std::chrono::system_clock> m_tp1, m_tp2;
// State of keyboard
6 years ago
bool pKeyNewState[256]{ 0 };
bool pKeyOldState[256]{ 0 };
HWButton pKeyboardState[256]{ 0 };
// State of mouse
bool pMouseNewState[nMouseButtons]{ 0 };
bool pMouseOldState[nMouseButtons]{ 0 };
HWButton pMouseState[nMouseButtons]{ 0 };
6 years ago
// The main engine thread
6 years ago
void EngineThread();
// At the very end of this file, chooses which
// components to compile
void olc_ConfigureSystem();
6 years ago
// If anything sets this flag to false, the engine
// "should" shut down gracefully
static std::atomic<bool> bAtomActive;
public:
// "Break In" Functions
void olc_UpdateMouse(int32_t x, int32_t y);
void olc_UpdateMouseWheel(int32_t delta);
void olc_UpdateWindowSize(int32_t x, int32_t y);
void olc_UpdateViewport();
void olc_ConstructFontSheet();
void olc_CoreUpdate();
void olc_PrepareEngine();
void olc_UpdateMouseState(int32_t button, bool state);
void olc_UpdateKeyState(int32_t key, bool state);
void olc_UpdateMouseFocus(bool state);
void olc_UpdateKeyFocus(bool state);
void olc_Terminate();
// NOTE: Items Here are to be deprecated, I have left them in for now
// in case you are using them, but they will be removed.
// olc::vf2d vSubPixelOffset = { 0.0f, 0.0f };
6 years ago
};
// O------------------------------------------------------------------------------O
// | PGE EXTENSION BASE CLASS - Permits access to PGE functions from extension |
// O------------------------------------------------------------------------------O
class PGEX
{
friend class olc::PixelGameEngine;
protected:
static PixelGameEngine* pge;
};
6 years ago
}
#endif // OLC_PGE_DEF
6 years ago
/*
Object Oriented Mode
~~~~~~~~~~~~~~~~~~~~
6 years ago
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.
6 years ago
If all else fails, create a file called "olcPixelGameEngine.cpp" with the following
6 years ago
two lines. Then you can just #include "olcPixelGameEngine.h" as normal without worrying
about defining things. Dont forget to include that cpp file as part of your build!
#define OLC_PGE_APPLICATION
#include "olcPixelGameEngine.h"
*/
6 years ago
// O------------------------------------------------------------------------------O
// | START OF OLC_PGE_APPLICATION |
// O------------------------------------------------------------------------------O
#ifdef OLC_PGE_APPLICATION
#undef OLC_PGE_APPLICATION
6 years ago
// O------------------------------------------------------------------------------O
// | olcPixelGameEngine INTERFACE IMPLEMENTATION (CORE) |
// | Note: The core implementation is platform independent |
// O------------------------------------------------------------------------------O
6 years ago
namespace olc
{
// O------------------------------------------------------------------------------O
// | olc::Pixel IMPLEMENTATION |
// O------------------------------------------------------------------------------O
6 years ago
Pixel::Pixel()
{ r = 0; g = 0; b = 0; a = nDefaultAlpha; }
6 years ago
Pixel::Pixel(uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha)
{ n = red | (green << 8) | (blue << 16) | (alpha << 24); } // Thanks jarekpelczar
6 years ago
Pixel::Pixel(uint32_t p)
{ n = p; }
bool Pixel::operator==(const Pixel& p) const
{ return n == p.n; }
bool Pixel::operator!=(const Pixel& p) const
{ return n != p.n; }
Pixel PixelF(float red, float green, float blue, float alpha)
{
return Pixel(uint8_t(red * 255.0f), uint8_t(green * 255.0f), uint8_t(blue * 255.0f), uint8_t(alpha * 255.0f));
}
// O------------------------------------------------------------------------------O
// | olc::Sprite IMPLEMENTATION |
// O------------------------------------------------------------------------------O
6 years ago
Sprite::Sprite()
{ pColData = nullptr; width = 0; height = 0; }
6 years ago
Sprite::Sprite(const std::string& sImageFile, olc::ResourcePack *pack)
{ LoadFromFile(sImageFile, pack); }
6 years ago
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++)
6 years ago
pColData[i] = Pixel();
}
Sprite::~Sprite()
{ if (pColData) delete[] pColData; }
6 years ago
olc::rcode Sprite::LoadFromPGESprFile(const 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, (size_t)width * (size_t)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
{
ResourceBuffer rb = pack->GetFileBuffer(sImageFile);
std::istream is(&rb);
ReadData(is);
return olc::OK;
}
return olc::FAIL;
}
olc::rcode Sprite::SaveToPGESprFile(const 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, (size_t)width * (size_t)height * sizeof(uint32_t));
ofs.close();
return olc::OK;
}
return olc::FAIL;
}
void Sprite::SetSampleMode(olc::Sprite::Mode mode)
{ modeSample = mode; }
Pixel Sprite::GetPixel(const olc::vi2d& a)
{ return GetPixel(a.x, a.y); }
6 years ago
bool Sprite::SetPixel(const olc::vi2d& a, Pixel p)
{ return SetPixel(a.x, a.y, p); }
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)];
}
}
bool Sprite::SetPixel(int32_t x, int32_t y, Pixel p)
{
if (x >= 0 && x < width && y >= 0 && y < height)
{
pColData[y*width + x] = p;
return true;
}
else
return false;
}
Pixel Sprite::Sample(float x, float y)
{
int32_t sx = std::min((int32_t)((x * (float)width)), width - 1);
int32_t sy = std::min((int32_t)((y * (float)height)), height - 1);
return GetPixel(sx, sy);
}
6 years ago
6 years ago
Pixel Sprite::SampleBL(float u, float v)
{
u = u * width - 0.5f;
v = v * height - 0.5f;
int x = (int)floor(u); // cast to int rounds toward zero, not downward
int y = (int)floor(v); // Thanks @joshinils
6 years ago
float u_ratio = u - x;
float v_ratio = v - y;
float u_opposite = 1 - u_ratio;
float v_opposite = 1 - v_ratio;
olc::Pixel p1 = GetPixel(std::max(x, 0), std::max(y, 0));
olc::Pixel p2 = GetPixel(std::min(x + 1, (int)width - 1), std::max(y, 0));
olc::Pixel p3 = GetPixel(std::max(x, 0), std::min(y + 1, (int)height - 1));
olc::Pixel p4 = GetPixel(std::min(x + 1, (int)width - 1), std::min(y + 1, (int)height - 1));
6 years ago
return olc::Pixel(
(uint8_t)((p1.r * u_opposite + p2.r * u_ratio) * v_opposite + (p3.r * u_opposite + p4.r * u_ratio) * v_ratio),
(uint8_t)((p1.g * u_opposite + p2.g * u_ratio) * v_opposite + (p3.g * u_opposite + p4.g * u_ratio) * v_ratio),
(uint8_t)((p1.b * u_opposite + p2.b * u_ratio) * v_opposite + (p3.b * u_opposite + p4.b * u_ratio) * v_ratio));
}
Pixel* Sprite::GetData()
{ return pColData; }
// O------------------------------------------------------------------------------O
// | olc::Decal IMPLEMENTATION |
// O------------------------------------------------------------------------------O
Decal::Decal(olc::Sprite* spr)
{
id = -1;
if (spr == nullptr) return;
sprite = spr;
id = renderer->CreateTexture(sprite->width, sprite->height);
Update();
}
void Decal::Update()
{
if (sprite == nullptr) return;
vUVScale = { 1.0f / float(sprite->width), 1.0f / float(sprite->height) };
renderer->ApplyTexture(id);
renderer->UpdateTexture(id, sprite);
}
Decal::~Decal()
{
if (id != -1)
{
renderer->DeleteTexture(id);
id = -1;
}
}
6 years ago
// O------------------------------------------------------------------------------O
// | olc::ResourcePack IMPLEMENTATION |
// O------------------------------------------------------------------------------O
//=============================================================
// Resource Packs - Allows you to store files in one large
// scrambled file - Thanks MaGetzUb for debugging a null char in std::stringstream bug
ResourceBuffer::ResourceBuffer(std::ifstream& ifs, uint32_t offset, uint32_t size)
{
vMemory.resize(size);
ifs.seekg(offset); ifs.read(vMemory.data(), vMemory.size());
setg(vMemory.data(), vMemory.data(), vMemory.data() + size);
}
ResourcePack::ResourcePack() { }
ResourcePack::~ResourcePack() { baseFile.close(); }
bool ResourcePack::AddFile(const std::string& sFile)
{
const std::string file = makeposix(sFile);
if (_gfs::exists(file))
{
sResourceFile e;
e.nSize = (uint32_t)_gfs::file_size(file);
e.nOffset = 0; // Unknown at this stage
mapFiles[file] = e;
return true;
}
return false;
}
bool ResourcePack::LoadPack(const std::string& sFile, const std::string& sKey)
{
// Open the resource file
baseFile.open(sFile, std::ifstream::binary);
if (!baseFile.is_open()) return false;
// 1) Read Scrambled index
uint32_t nIndexSize = 0;
baseFile.read((char*)&nIndexSize, sizeof(uint32_t));
std::vector<char> buffer(nIndexSize);
for (uint32_t j = 0; j < nIndexSize; j++)
buffer[j] = baseFile.get();
std::vector<char> decoded = scramble(buffer, sKey);
size_t pos = 0;
auto read = [&decoded, &pos](char* dst, size_t size) {
memcpy((void*)dst, (const void*)(decoded.data() + pos), size);
pos += size;
};
auto get = [&read]() -> int {
char c;
read(&c, 1);
return c;
};
// 2) Read Map
uint32_t nMapEntries = 0;
read((char*)&nMapEntries, sizeof(uint32_t));
for (uint32_t i = 0; i < nMapEntries; i++)
{
uint32_t nFilePathSize = 0;
read((char*)&nFilePathSize, sizeof(uint32_t));
std::string sFileName(nFilePathSize, ' ');
for (uint32_t j = 0; j < nFilePathSize; j++)
sFileName[j] = get();
sResourceFile e;
read((char*)&e.nSize, sizeof(uint32_t));
read((char*)&e.nOffset, sizeof(uint32_t));
mapFiles[sFileName] = e;
}
// Don't close base file! we will provide a stream
// pointer when the file is requested
return true;
}
bool ResourcePack::SavePack(const std::string& sFile, const std::string& sKey)
{
// Create/Overwrite the resource file
std::ofstream ofs(sFile, std::ofstream::binary);
if (!ofs.is_open()) return false;
// Iterate through map
uint32_t nIndexSize = 0; // Unknown for now
ofs.write((char*)&nIndexSize, sizeof(uint32_t));
uint32_t nMapSize = uint32_t(mapFiles.size());
ofs.write((char*)&nMapSize, sizeof(uint32_t));
for (auto& e : mapFiles)
{
// Write the path of the file
size_t nPathSize = e.first.size();
ofs.write((char*)&nPathSize, sizeof(uint32_t));
ofs.write(e.first.c_str(), nPathSize);
// Write the file entry properties
ofs.write((char*)&e.second.nSize, sizeof(uint32_t));
ofs.write((char*)&e.second.nOffset, sizeof(uint32_t));
}
// 2) Write the individual Data
std::streampos offset = ofs.tellp();
nIndexSize = (uint32_t)offset;
for (auto& e : mapFiles)
{
// Store beginning of file offset within resource pack file
e.second.nOffset = (uint32_t)offset;
// Load the file to be added
std::vector<uint8_t> vBuffer(e.second.nSize);
std::ifstream i(e.first, std::ifstream::binary);
i.read((char*)vBuffer.data(), e.second.nSize);
i.close();
// Write the loaded file into resource pack file
ofs.write((char*)vBuffer.data(), e.second.nSize);
offset += e.second.nSize;
}
// 3) Scramble Index
std::vector<char> stream;
auto write = [&stream](const char* data, size_t size) {
size_t sizeNow = stream.size();
stream.resize(sizeNow + size);
memcpy(stream.data() + sizeNow, data, size);
};
// Iterate through map
write((char*)&nMapSize, sizeof(uint32_t));
for (auto& e : mapFiles)
{
// Write the path of the file
size_t nPathSize = e.first.size();
write((char*)&nPathSize, sizeof(uint32_t));
write(e.first.c_str(), nPathSize);
// Write the file entry properties
write((char*)&e.second.nSize, sizeof(uint32_t));
write((char*)&e.second.nOffset, sizeof(uint32_t));
}
std::vector<char> sIndexString = scramble(stream, sKey);
uint32_t nIndexStringLen = uint32_t(sIndexString.size());
// 4) Rewrite Map (it has been updated with offsets now)
// at start of file
ofs.seekp(0, std::ios::beg);
ofs.write((char*)&nIndexStringLen, sizeof(uint32_t));
ofs.write(sIndexString.data(), nIndexStringLen);
ofs.close();
return true;
}
ResourceBuffer ResourcePack::GetFileBuffer(const std::string& sFile)
{ return ResourceBuffer(baseFile, mapFiles[sFile].nOffset, mapFiles[sFile].nSize); }
bool ResourcePack::Loaded()
{ return baseFile.is_open(); }
std::vector<char> ResourcePack::scramble(const std::vector<char>& data, const std::string& key)
{
if (key.empty()) return data;
std::vector<char> o;
size_t c = 0;
for (auto s : data) o.push_back(s ^ key[(c++) % key.size()]);
return o;
};
std::string ResourcePack::makeposix(const std::string& path)
{
std::string o;
for (auto s : path) o += std::string(1, s == '\\' ? '/' : s);
return o;
};
// O------------------------------------------------------------------------------O
// | olc::PixelGameEngine IMPLEMENTATION |
// O------------------------------------------------------------------------------O
6 years ago
PixelGameEngine::PixelGameEngine()
{
sAppName = "Undefined";
olc::PGEX::pge = this;
// Bring in relevant Platform & Rendering systems depending
// on compiler parameters
olc_ConfigureSystem();
6 years ago
}
PixelGameEngine::~PixelGameEngine()
{}
olc::rcode PixelGameEngine::Construct(int32_t screen_w, int32_t screen_h, int32_t pixel_w, int32_t pixel_h, bool full_screen, bool vsync)
6 years ago
{
vScreenSize = { screen_w, screen_h };
vInvScreenSize = { 1.0f / float(screen_w), 1.0f / float(screen_h) };
vPixelSize = { pixel_w, pixel_h };
vWindowSize = vScreenSize * vPixelSize;
bFullScreen = full_screen;
bEnableVSYNC = vsync;
vPixel = 2.0f / vScreenSize;
if (vPixelSize.x <= 0 || vPixelSize.y <= 0 || vScreenSize.x <= 0 || vScreenSize.y <= 0)
return olc::FAIL;
6 years ago
return olc::OK;
}
void PixelGameEngine::SetScreenSize(int w, int h)
{
vScreenSize = { w, h };
for (auto& layer : vLayers)
{
delete layer.pDrawTarget; // Erase existing layer sprites
layer.pDrawTarget = new Sprite(vScreenSize.x, vScreenSize.y);
layer.bUpdate = true;
}
SetDrawTarget(nullptr);
renderer->ClearBuffer(olc::BLACK, true);
renderer->DisplayFrame();
renderer->ClearBuffer(olc::BLACK, true);
renderer->UpdateViewport(vViewPos, vViewSize);
}
#if !defined(PGE_USE_CUSTOM_START)
6 years ago
olc::rcode PixelGameEngine::Start()
{
if (platform->ApplicationStartUp() != olc::OK) return olc::FAIL;
6 years ago
// Construct the window
if (platform->CreateWindowPane({ 30,30 }, vWindowSize, bFullScreen) != olc::OK) return olc::FAIL;
olc_UpdateWindowSize(vWindowSize.x, vWindowSize.y);
6 years ago
// Start the thread
bAtomActive = true;
std::thread t = std::thread(&PixelGameEngine::EngineThread, this);
// Some implementations may form an event loop here
platform->StartSystemEventLoop();
6 years ago
// Wait for thread to be exited
t.join();
if (platform->ApplicationCleanUp() != olc::OK) return olc::FAIL;
6 years ago
return olc::OK;
}
#endif
6 years ago
void PixelGameEngine::SetDrawTarget(Sprite *target)
{
if (target)
{
6 years ago
pDrawTarget = target;
}
6 years ago
else
{
nTargetLayer = 0;
pDrawTarget = vLayers[0].pDrawTarget;
}
6 years ago
}
void PixelGameEngine::SetDrawTarget(uint8_t layer)
{
if (layer < vLayers.size())
{
pDrawTarget = vLayers[layer].pDrawTarget;
vLayers[layer].bUpdate = true;
nTargetLayer = layer;
}
}
void PixelGameEngine::EnableLayer(uint8_t layer, bool b)
{ if(layer < vLayers.size()) vLayers[layer].bShow = b; }
void PixelGameEngine::SetLayerOffset(uint8_t layer, const olc::vf2d& offset)
{ SetLayerOffset(layer, offset.x, offset.y); }
void PixelGameEngine::SetLayerOffset(uint8_t layer, float x, float y)
{ if (layer < vLayers.size()) vLayers[layer].vOffset = { x, y }; }
void PixelGameEngine::SetLayerScale(uint8_t layer, const olc::vf2d& scale)
{ SetLayerScale(layer, scale.x, scale.y); }
void PixelGameEngine::SetLayerScale(uint8_t layer, float x, float y)
{ if (layer < vLayers.size()) vLayers[layer].vScale = { x, y }; }
void PixelGameEngine::SetLayerTint(uint8_t layer, const olc::Pixel& tint)
{ if (layer < vLayers.size()) vLayers[layer].tint = tint; }
void PixelGameEngine::SetLayerCustomRenderFunction(uint8_t layer, std::function<void()> f)
{ if (layer < vLayers.size()) vLayers[layer].funcHook = f; }
std::vector<LayerDesc>& PixelGameEngine::GetLayers()
{ return vLayers; }
uint32_t PixelGameEngine::CreateLayer()
6 years ago
{
LayerDesc ld;
ld.pDrawTarget = new olc::Sprite(vScreenSize.x, vScreenSize.y);
ld.nResID = renderer->CreateTexture(vScreenSize.x, vScreenSize.y);
renderer->UpdateTexture(ld.nResID, ld.pDrawTarget);
vLayers.push_back(ld);
return uint32_t(vLayers.size()) - 1;
6 years ago
}
Sprite* PixelGameEngine::GetDrawTarget()
{ return pDrawTarget; }
6 years ago
int32_t PixelGameEngine::GetDrawTargetWidth()
{
if (pDrawTarget)
return pDrawTarget->width;
else
return 0;
}
int32_t PixelGameEngine::GetDrawTargetHeight()
{
if (pDrawTarget)
return pDrawTarget->height;
else
return 0;
}
uint32_t PixelGameEngine::GetFPS()
{ return nLastFPS; }
6 years ago
bool PixelGameEngine::IsFocused()
{ return bHasInputFocus; }
6 years ago
HWButton PixelGameEngine::GetKey(Key k)
{ return pKeyboardState[k]; }
6 years ago
6 years ago
HWButton PixelGameEngine::GetMouse(uint32_t b)
{ return pMouseState[b]; }
6 years ago
int32_t PixelGameEngine::GetMouseX()
{ return vMousePos.x; }
6 years ago
int32_t PixelGameEngine::GetMouseY()
{ return vMousePos.y; }
6 years ago
int32_t PixelGameEngine::GetMouseWheel()
{ return nMouseWheelDelta; }
const int32_t PixelGameEngine::ScreenWidth()
{ return vScreenSize.x; }
6 years ago
const int32_t PixelGameEngine::ScreenHeight()
{ return vScreenSize.y; }
6 years ago
bool PixelGameEngine::Draw(const olc::vi2d& pos, Pixel p)
{ return Draw(pos.x, pos.y, p); }
// This is it, the critical function that plots a pixel
bool PixelGameEngine::Draw(int32_t x, int32_t y, Pixel p)
6 years ago
{
if (!pDrawTarget) return false;
6 years ago
if (nPixelMode == Pixel::NORMAL)
{
return pDrawTarget->SetPixel(x, y, p);
6 years ago
}
if (nPixelMode == Pixel::MASK)
{
6 years ago
if(p.a == 255)
return pDrawTarget->SetPixel(x, y, p);
6 years ago
}
if (nPixelMode == Pixel::ALPHA)
{
Pixel d = pDrawTarget->GetPixel(x, y);
float a = (float)(p.a / 255.0f) * fBlendFactor;
6 years ago
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;
return pDrawTarget->SetPixel(x, y, Pixel((uint8_t)r, (uint8_t)g, (uint8_t)b/*, (uint8_t)(p.a * fBlendFactor)*/));
6 years ago
}
if (nPixelMode == Pixel::CUSTOM)
{
return pDrawTarget->SetPixel(x, y, funcPixelMode(x, y, p, pDrawTarget->GetPixel(x, y)));
}
return false;
6 years ago
}
void PixelGameEngine::SetSubPixelOffset(float ox, float oy)
{
//vSubPixelOffset.x = ox * vPixel.x;
//vSubPixelOffset.y = oy * vPixel.y;
}
void PixelGameEngine::DrawLine(const olc::vi2d& pos1, const olc::vi2d& pos2, Pixel p, uint32_t pattern)
{ DrawLine(pos1.x, pos1.y, pos2.x, pos2.y, p, pattern); }
void PixelGameEngine::DrawLine(int32_t x1, int32_t y1, int32_t x2, int32_t y2, Pixel p, uint32_t pattern)
6 years ago
{
int x, y, dx, dy, dx1, dy1, px, py, xe, ye, i;
dx = x2 - x1; dy = y2 - y1;
auto rol = [&](void){ pattern = (pattern << 1) | (pattern >> 31); return pattern & 1; };
// straight lines idea by gurkanctn
if (dx == 0) // Line is vertical
{
if (y2 < y1) std::swap(y1, y2);
for (y = y1; y <= y2; y++) if (rol()) Draw(x1, y, p);
return;
}
if (dy == 0) // Line is horizontal
{
if (x2 < x1) std::swap(x1, x2);
for (x = x1; x <= x2; x++) if (rol()) Draw(x, y1, p);
return;
}
// Line is Funk-aye
6 years ago
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; }
6 years ago
else
{ x = x2; y = y2; xe = x1; }
6 years ago
if (rol()) Draw(x, y, p);
6 years ago
for (i = 0; x<xe; i++)
{
x = x + 1;
if (px<0)
px = px + 2 * dy1;
else
{
if ((dx<0 && dy<0) || (dx>0 && dy>0)) y = y + 1; else y = y - 1;
px = px + 2 * (dy1 - dx1);
}
if (rol()) Draw(x, y, p);
6 years ago
}
}
else
{
if (dy >= 0)
{ x = x1; y = y1; ye = y2; }
6 years ago
else
{ x = x2; y = y2; ye = y1; }
6 years ago
if (rol()) Draw(x, y, p);
6 years ago
for (i = 0; y<ye; i++)
{
y = y + 1;
if (py <= 0)
py = py + 2 * dx1;
else
{
if ((dx<0 && dy<0) || (dx>0 && dy>0)) x = x + 1; else x = x - 1;
py = py + 2 * (dx1 - dy1);
}
if (rol()) Draw(x, y, p);
6 years ago
}
}
}
void PixelGameEngine::DrawCircle(const olc::vi2d& pos, int32_t radius, Pixel p, uint8_t mask)
{ DrawCircle(pos.x, pos.y, radius, p, mask);}
void PixelGameEngine::DrawCircle(int32_t x, int32_t y, int32_t radius, Pixel p, uint8_t mask)
6 years ago
{
int x0 = 0;
int y0 = radius;
int d = 3 - 2 * radius;
if (!radius) return;
while (y0 >= x0) // only formulate 1/8 of circle
{
if (mask & 0x01) Draw(x + x0, y - y0, p);
if (mask & 0x02) Draw(x + y0, y - x0, p);
if (mask & 0x04) Draw(x + y0, y + x0, p);
if (mask & 0x08) Draw(x + x0, y + y0, p);
if (mask & 0x10) Draw(x - x0, y + y0, p);
if (mask & 0x20) Draw(x - y0, y + x0, p);
if (mask & 0x40) Draw(x - y0, y - x0, p);
if (mask & 0x80) Draw(x - x0, y - y0, p);
6 years ago
if (d < 0) d += 4 * x0++ + 6;
else d += 4 * (x0++ - y0--) + 10;
}
}
void PixelGameEngine::FillCircle(const olc::vi2d& pos, int32_t radius, Pixel p)
{ FillCircle(pos.x, pos.y, radius, p); }
6 years ago
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(const olc::vi2d& pos, const olc::vi2d& size, Pixel p)
{ DrawRect(pos.x, pos.y, size.x, size.y, p); }
6 years ago
void PixelGameEngine::DrawRect(int32_t x, int32_t y, int32_t w, int32_t h, Pixel p)
{
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)
6 years ago
{
int pixels = GetDrawTargetWidth() * GetDrawTargetHeight();
Pixel* m = GetDrawTarget()->GetData();
for (int i = 0; i < pixels; i++) m[i] = p;
}
void PixelGameEngine::ClearBuffer(Pixel p, bool bDepth)
{
renderer->ClearBuffer(p, bDepth);
}
void PixelGameEngine::FillRect(const olc::vi2d& pos, const olc::vi2d& size, Pixel p)
{ FillRect(pos.x, pos.y, size.x, size.y, p); }
void PixelGameEngine::FillRect(int32_t x, int32_t y, int32_t w, int32_t h, Pixel p)
{
6 years ago
int32_t x2 = x + w;
int32_t y2 = y + h;
if (x < 0) x = 0;
if (x >= (int32_t)GetDrawTargetWidth()) x = (int32_t)GetDrawTargetWidth();
6 years ago
if (y < 0) y = 0;
if (y >= (int32_t)GetDrawTargetHeight()) y = (int32_t)GetDrawTargetHeight();
6 years ago
if (x2 < 0) x2 = 0;
if (x2 >= (int32_t)GetDrawTargetWidth()) x2 = (int32_t)GetDrawTargetWidth();
6 years ago
if (y2 < 0) y2 = 0;
if (y2 >= (int32_t)GetDrawTargetHeight()) y2 = (int32_t)GetDrawTargetHeight();
6 years ago
for (int i = x; i < x2; i++)
for (int j = y; j < y2; j++)
Draw(i, j, p);
}
void PixelGameEngine::DrawTriangle(const olc::vi2d& pos1, const olc::vi2d& pos2, const olc::vi2d& pos3, Pixel p)
{ DrawTriangle(pos1.x, pos1.y, pos2.x, pos2.y, pos3.x, pos3.y, p); }
6 years ago
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);
}
void PixelGameEngine::FillTriangle(const olc::vi2d& pos1, const olc::vi2d& pos2, const olc::vi2d& pos3, Pixel p)
{ FillTriangle(pos1.x, pos1.y, pos2.x, pos2.y, pos3.x, pos3.y, p); }
6 years ago
// 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 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) {std::swap(y1, y2); std::swap(x1, x2); }
if (y1>y3) {std::swap(y1, y3); std::swap(x1, x3); }
if (y2>y3) {std::swap(y2, y3); std::swap(x2, x3); }
6 years ago
t1x = t2x = x1; y = y1; // Starting points
dx1 = (int)(x2 - x1);
if (dx1<0) { dx1 = -dx1; signx1 = -1; } else signx1 = 1;
6 years ago
dy1 = (int)(y2 - y1);
dx2 = (int)(x3 - x1);
if (dx2<0) { dx2 = -dx2; signx2 = -1; } else signx2 = 1;
6 years ago
dy2 = (int)(y3 - y1);
if (dy1 > dx1) { std::swap(dx1, dy1); changed1 = true; }
if (dy2 > dx2) { std::swap(dy2, dx2); changed2 = true; }
6 years ago
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<t2x) { minx = t1x; maxx = t2x; }
else { minx = t2x; maxx = t1x; }
// process first line until y value is about to change
while (i<dx1) {
i++;
e1 += dy1;
while (e1 >= 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<t1x) maxx = t1x;
if (maxx<t2x) maxx = t2x;
6 years ago
drawline(minx, maxx, y); // Draw line from min to max points found on the y
// Now increase y
if (!changed1) t1x += signx1;
t1x += t1xp;
if (!changed2) t2x += signx2;
t2x += t2xp;
y += 1;
if (y == y2) break;
}
next:
// Second half
dx1 = (int)(x3 - x2); if (dx1<0) { dx1 = -dx1; signx1 = -1; }
else signx1 = 1;
dy1 = (int)(y3 - y2);
t1x = x2;
if (dy1 > dx1) { // swap values
std::swap(dy1, dx1);
6 years ago
changed1 = true;
}
else changed1 = false;
e1 = (int)(dx1 >> 1);
for (int i = 0; i <= dx1; i++) {
t1xp = 0; t2xp = 0;
if (t1x<t2x) { minx = t1x; maxx = t2x; }
else { minx = t2x; maxx = t1x; }
// process first line until y value is about to change
while (i<dx1) {
e1 += dy1;
while (e1 >= dx1) {
e1 -= dx1;
if (changed1) { t1xp = signx1; break; }//t1x += signx1;
else goto next3;
}
if (changed1) break;
else t1x += signx1;
if (i<dx1) i++;
}
next3:
// process second line until y value is about to change
while (t2x != x3) {
e2 += dy2;
while (e2 >= 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 (maxx<t1x) maxx = t1x;
if (maxx<t2x) maxx = t2x;
6 years ago
drawline(minx, maxx, y);
if (!changed1) t1x += signx1;
t1x += t1xp;
if (!changed2) t2x += signx2;
t2x += t2xp;
y += 1;
if (y>y3) return;
}
}
void PixelGameEngine::DrawSprite(const olc::vi2d& pos, Sprite *sprite, uint32_t scale, uint8_t flip)
{ DrawSprite(pos.x, pos.y, sprite, scale, flip); }
void PixelGameEngine::DrawSprite(int32_t x, int32_t y, Sprite *sprite, uint32_t scale, uint8_t flip)
6 years ago
{
if (sprite == nullptr)
return;
int32_t fxs = 0, fxm = 1, fx = 0;
int32_t fys = 0, fym = 1, fy = 0;
if (flip & olc::Sprite::Flip::HORIZ) { fxs = sprite->width - 1; fxm = -1; }
if (flip & olc::Sprite::Flip::VERT) { fys = sprite->height - 1; fym = -1; }
if (scale > 1)
6 years ago
{
fx = fxs;
for (int32_t i = 0; i < sprite->width; i++, fx += fxm)
{
fy = fys;
for (int32_t j = 0; j < sprite->height; j++, fy += fym)
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(fx, fy));
}
}
else
{
fx = fxs;
for (int32_t i = 0; i < sprite->width; i++, fx += fxm)
{
fy = fys;
for (int32_t j = 0; j < sprite->height; j++, fy += fym)
Draw(x + i, y + j, sprite->GetPixel(fx, fy));
}
6 years ago
}
}
void PixelGameEngine::DrawPartialSprite(const olc::vi2d& pos, Sprite *sprite, const olc::vi2d& sourcepos, const olc::vi2d& size, uint32_t scale, uint8_t flip)
{ DrawPartialSprite(pos.x, pos.y, sprite, sourcepos.x, sourcepos.y, size.x, size.y, scale, flip); }
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, uint8_t flip)
6 years ago
{
if (sprite == nullptr)
return;
int32_t fxs = 0, fxm = 1, fx = 0;
int32_t fys = 0, fym = 1, fy = 0;
if (flip & olc::Sprite::Flip::HORIZ) { fxs = w - 1; fxm = -1; }
if (flip & olc::Sprite::Flip::VERT) { fys = h - 1; fym = -1; }
if (scale > 1)
6 years ago
{
fx = fxs;
for (int32_t i = 0; i < w; i++, fx += fxm)
{
fy = fys;
for (int32_t j = 0; j < h; j++, fy += fym)
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(fx + ox, fy + oy));
}
}
else
{
fx = fxs;
for (int32_t i = 0; i < w; i++, fx += fxm)
{
fy = fys;
for (int32_t j = 0; j < h; j++, fy += fym)
Draw(x + i, y + j, sprite->GetPixel(fx + ox, fy + oy));
}
6 years ago
}
}
void PixelGameEngine::DrawPartialDecal(const olc::vf2d& pos, olc::Decal* decal, const olc::vf2d& source_pos, const olc::vf2d& source_size, const olc::vf2d& scale, const olc::Pixel& tint)
{
olc::vf2d vScreenSpacePos =
{
(pos.x * vInvScreenSize.x) * 2.0f - 1.0f,
((pos.y * vInvScreenSize.y) * 2.0f - 1.0f) * -1.0f
};
olc::vf2d vScreenSpaceDim =
{
vScreenSpacePos.x + (2.0f * source_size.x * vInvScreenSize.x) * scale.x,
vScreenSpacePos.y - (2.0f * source_size.y * vInvScreenSize.y) * scale.y
};
DecalInstance di; di.decal = decal; di.tint = tint;
di.pos[0] = { vScreenSpacePos.x, vScreenSpacePos.y };
di.pos[1] = { vScreenSpacePos.x, vScreenSpaceDim.y };
di.pos[2] = { vScreenSpaceDim.x, vScreenSpaceDim.y };
di.pos[3] = { vScreenSpaceDim.x, vScreenSpacePos.y };
olc::vf2d uvtl = source_pos * decal->vUVScale;
olc::vf2d uvbr = uvtl + (source_size * decal->vUVScale);
di.uv[0] = { uvtl.x, uvtl.y }; di.uv[1] = { uvtl.x, uvbr.y };
di.uv[2] = { uvbr.x, uvbr.y }; di.uv[3] = { uvbr.x, uvtl.y };
vLayers[nTargetLayer].vecDecalInstance.push_back(di);
}
void PixelGameEngine::DrawDecal(const olc::vf2d& pos, olc::Decal *decal, const olc::vf2d& scale, const olc::Pixel& tint)
{
olc::vf2d vScreenSpacePos =
{
(pos.x * vInvScreenSize.x) * 2.0f - 1.0f,
((pos.y * vInvScreenSize.y) * 2.0f - 1.0f) * -1.0f
};
olc::vf2d vScreenSpaceDim =
{
vScreenSpacePos.x + (2.0f * (float(decal->sprite->width) * vInvScreenSize.x)) * scale.x,
vScreenSpacePos.y - (2.0f * (float(decal->sprite->height) * vInvScreenSize.y)) * scale.y
};
DecalInstance di;
di.decal = decal;
di.tint = tint;
di.pos[0] = { vScreenSpacePos.x, vScreenSpacePos.y };
di.pos[1] = { vScreenSpacePos.x, vScreenSpaceDim.y };
di.pos[2] = { vScreenSpaceDim.x, vScreenSpaceDim.y };
di.pos[3] = { vScreenSpaceDim.x, vScreenSpacePos.y };
vLayers[nTargetLayer].vecDecalInstance.push_back(di);
}
void PixelGameEngine::DrawRotatedDecal(const olc::vf2d& pos, olc::Decal* decal, const float fAngle, const olc::vf2d& center, const olc::vf2d& scale, const olc::Pixel& tint)
{
DecalInstance di;
di.decal = decal;
di.tint = tint;
di.pos[0] = (olc::vf2d(0.0f, 0.0f) - center) * scale;
di.pos[1] = (olc::vf2d(0.0f, float(decal->sprite->height)) - center) * scale;
di.pos[2] = (olc::vf2d(float(decal->sprite->width), float(decal->sprite->height)) - center) * scale;
di.pos[3] = (olc::vf2d(float(decal->sprite->width), 0.0f) - center) * scale;
float c = cos(fAngle), s = sin(fAngle);
for (int i = 0; i < 4; i++)
{
di.pos[i] = pos + olc::vf2d(di.pos[i].x * c - di.pos[i].y * s, di.pos[i].x * s + di.pos[i].y * c);
di.pos[i] = di.pos[i] * vInvScreenSize * 2.0f - olc::vf2d(1.0f, 1.0f);
di.pos[i].y *= -1.0f;
}
vLayers[nTargetLayer].vecDecalInstance.push_back(di);
}
void PixelGameEngine::DrawWarpedDecal(olc::Decal* decal, const olc::vf2d* pos, const olc::Pixel& tint)
{
// Thanks Nathan Reed, a brilliant article explaining whats going on here
// http://www.reedbeta.com/blog/quadrilateral-interpolation-part-1/
DecalInstance di;
di.decal = decal;
di.tint = tint;
olc::vf2d center;
float rd = ((pos[2].x - pos[0].x) * (pos[3].y - pos[1].y) - (pos[3].x - pos[1].x) * (pos[2].y - pos[0].y));
if (rd != 0)
{
rd = 1.0f / rd;
float rn = ((pos[3].x - pos[1].x) * (pos[0].y - pos[1].y) - (pos[3].y - pos[1].y) * (pos[0].x - pos[1].x)) * rd;
float sn = ((pos[2].x - pos[0].x) * (pos[0].y - pos[1].y) - (pos[2].y - pos[0].y) * (pos[0].x - pos[1].x)) * rd;
if (!(rn < 0.f || rn > 1.f || sn < 0.f || sn > 1.f)) center = pos[0] + rn * (pos[2] - pos[0]);
float d[4]; for (int i = 0; i < 4; i++) d[i] = (pos[i] - center).mag();
for (int i = 0; i < 4; i++)
{
float q = d[i] == 0.0f ? 1.0f : (d[i] + d[(i + 2) & 3]) / d[(i + 2) & 3];
di.uv[i] *= q; di.w[i] *= q;
di.pos[i] = { (pos[i].x * vInvScreenSize.x) * 2.0f - 1.0f, ((pos[i].y * vInvScreenSize.y) * 2.0f - 1.0f) * -1.0f };
}
vLayers[nTargetLayer].vecDecalInstance.push_back(di);
}
}
void PixelGameEngine::DrawWarpedDecal(olc::Decal* decal, const std::array<olc::vf2d, 4>& pos, const olc::Pixel& tint)
{
DrawWarpedDecal(decal, pos.data(), tint);
}
void PixelGameEngine::DrawWarpedDecal(olc::Decal* decal, const olc::vf2d(&pos)[4], const olc::Pixel& tint)
{
DrawWarpedDecal(decal, &pos[0], tint);
}
void PixelGameEngine::DrawStringDecal(const olc::vf2d& pos, const std::string& sText, const Pixel col, const olc::vf2d& scale)
{
olc::vf2d spos = { 0.0f, 0.0f };
for (auto c : sText)
{
if (c == '\n')
{
spos.x = 0; spos.y += 8.0f * scale.y;
}
else
{
int32_t ox = (c - 32) % 16;
int32_t oy = (c - 32) / 16;
DrawPartialDecal(pos + spos, fontDecal, { float(ox) * 8.0f, float(oy) * 8.0f }, { 8.0f, 8.0f }, scale, col);
spos.x += 8.0f * scale.x;
}
}
}
void PixelGameEngine::DrawString(const olc::vi2d& pos, const std::string& sText, Pixel col, uint32_t scale)
{ DrawString(pos.x, pos.y, sText, col, scale); }
void PixelGameEngine::DrawString(int32_t x, int32_t y, const std::string& sText, Pixel col, uint32_t scale)
{
int32_t sx = 0;
int32_t sy = 0;
Pixel::Mode m = nPixelMode;
// Thanks @tucna, spotted bug with col.ALPHA :P
if(col.a != 255) SetPixelMode(Pixel::ALPHA);
else SetPixelMode(Pixel::MASK);
for (auto c : sText)
{
if (c == '\n')
{
sx = 0; sy += 8 * scale;
6 years ago
}
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;
6 years ago
}
}
6 years ago
SetPixelMode(m);
}
6 years ago
void PixelGameEngine::SetPixelMode(Pixel::Mode m)
{ nPixelMode = m; }
6 years ago
Pixel::Mode PixelGameEngine::GetPixelMode()
{ return nPixelMode; }
void PixelGameEngine::SetPixelMode(std::function<olc::Pixel(const int x, const int y, const olc::Pixel&, const olc::Pixel&)> 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;
}
6 years ago
// 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
6 years ago
bool PixelGameEngine::OnUserCreate()
{ return false; }
6 years ago
bool PixelGameEngine::OnUserUpdate(float fElapsedTime)
{ UNUSED(fElapsedTime); return false; }
6 years ago
bool PixelGameEngine::OnUserDestroy()
{ return true; }
//////////////////////////////////////////////////////////////////
void PixelGameEngine::olc_UpdateViewport()
{
int32_t ww = vScreenSize.x * vPixelSize.x;
int32_t wh = vScreenSize.y * vPixelSize.y;
float wasp = (float)ww / (float)wh;
vViewSize.x = (int32_t)vWindowSize.x;
vViewSize.y = (int32_t)((float)vViewSize.x / wasp);
if (vViewSize.y > vWindowSize.y)
{
vViewSize.y = vWindowSize.y;
vViewSize.x = (int32_t)((float)vViewSize.y * wasp);
}
vViewPos = (vWindowSize - vViewSize) / 2;
}
void PixelGameEngine::olc_UpdateWindowSize(int32_t x, int32_t y)
{
vWindowSize = { x, y };
olc_UpdateViewport();
}
void PixelGameEngine::olc_UpdateMouseWheel(int32_t delta)
{ nMouseWheelDeltaCache += delta; }
void PixelGameEngine::olc_UpdateMouse(int32_t x, int32_t y)
6 years ago
{
// Mouse coords come in screen space
// But leave in pixel space
// Full Screen mode may have a weird viewport we must clamp to
x -= vViewPos.x;
y -= vViewPos.y;
vMousePosCache.x = (int32_t)(((float)x / (float)(vWindowSize.x - (vViewPos.x * 2)) * (float)vScreenSize.x));
vMousePosCache.y = (int32_t)(((float)y / (float)(vWindowSize.y - (vViewPos.y * 2)) * (float)vScreenSize.y));
if (vMousePosCache.x >= (int32_t)vScreenSize.x) vMousePosCache.x = vScreenSize.x - 1;
if (vMousePosCache.y >= (int32_t)vScreenSize.y) vMousePosCache.y = vScreenSize.y - 1;
if (vMousePosCache.x < 0) vMousePosCache.x = 0;
if (vMousePosCache.y < 0) vMousePosCache.y = 0;
}
void PixelGameEngine::olc_UpdateMouseState(int32_t button, bool state)
{ pMouseNewState[button] = state; }
void PixelGameEngine::olc_UpdateKeyState(int32_t key, bool state)
{ pKeyNewState[key] = state; }
void PixelGameEngine::olc_UpdateMouseFocus(bool state)
{ bHasMouseFocus = state; }
void PixelGameEngine::olc_UpdateKeyFocus(bool state)
{ bHasInputFocus = state; }
void PixelGameEngine::olc_Terminate()
{ bAtomActive = false; }
6 years ago
void PixelGameEngine::EngineThread()
{
// Allow platform to do stuff here if needed, since its now in the
// context of this thread
if (platform->ThreadStartUp() == olc::FAIL) return;
6 years ago
// Do engine context specific initialisation
olc_PrepareEngine();
6 years ago
// Create user resources as part of this thread
if (!OnUserCreate()) bAtomActive = false;
6 years ago
while (bAtomActive)
{
// Run as fast as possible
while (bAtomActive) { olc_CoreUpdate(); }
// Allow the user to free resources if they have overrided the destroy function
if (!OnUserDestroy())
6 years ago
{
// User denied destroy for some reason, so continue running
bAtomActive = true;
}
}
6 years ago
platform->ThreadCleanUp();
}
6 years ago
void PixelGameEngine::olc_PrepareEngine()
{
// Start OpenGL, the context is owned by the game thread
if (platform->CreateGraphics(bFullScreen, bEnableVSYNC, vViewPos, vViewSize) == olc::FAIL) return;
6 years ago
// Construct default font sheet
olc_ConstructFontSheet();
// Create Primary Layer "0"
CreateLayer();
vLayers[0].bUpdate = true;
vLayers[0].bShow = true;
SetDrawTarget(nullptr);
6 years ago
m_tp1 = std::chrono::system_clock::now();
m_tp2 = std::chrono::system_clock::now();
}
6 years ago
void PixelGameEngine::olc_CoreUpdate()
{
// Handle Timing
m_tp2 = std::chrono::system_clock::now();
std::chrono::duration<float> elapsedTime = m_tp2 - m_tp1;
m_tp1 = m_tp2;
// Our time per frame coefficient
float fElapsedTime = elapsedTime.count();
6 years ago
// Some platforms will need to check for events
platform->HandleSystemEvent();
// Compare hardware input states from previous frame
auto ScanHardware = [&](HWButton* pKeys, bool* pStateOld, bool* pStateNew, uint32_t nKeyCount)
{
for (uint32_t i = 0; i < nKeyCount; i++)
{
pKeys[i].bPressed = false;
pKeys[i].bReleased = false;
if (pStateNew[i] != pStateOld[i])
{
if (pStateNew[i])
6 years ago
{
pKeys[i].bPressed = !pKeys[i].bHeld;
pKeys[i].bHeld = true;
}
else
{
pKeys[i].bReleased = true;
pKeys[i].bHeld = false;
6 years ago
}
}
pStateOld[i] = pStateNew[i];
}
};
6 years ago
ScanHardware(pKeyboardState, pKeyOldState, pKeyNewState, 256);
ScanHardware(pMouseState, pMouseOldState, pMouseNewState, nMouseButtons);
// Cache mouse coordinates so they remain consistent during frame
vMousePos = vMousePosCache;
nMouseWheelDelta = nMouseWheelDeltaCache;
nMouseWheelDeltaCache = 0;
6 years ago
renderer->ClearBuffer(olc::BLACK, true);
6 years ago
// Handle Frame Update
if (!OnUserUpdate(fElapsedTime))
bAtomActive = false;
// Display Frame
renderer->UpdateViewport(vViewPos, vViewSize);
renderer->ClearBuffer(olc::BLACK, true);
6 years ago
// Layer 0 must always exist
vLayers[0].bUpdate = true;
vLayers[0].bShow = true;
renderer->PrepareDrawing();
for (auto layer = vLayers.rbegin(); layer != vLayers.rend(); ++layer)
{
if (layer->bShow)
{
if (layer->funcHook == nullptr)
{
renderer->ApplyTexture(layer->nResID);
if (layer->bUpdate)
{
renderer->UpdateTexture(layer->nResID, layer->pDrawTarget);
layer->bUpdate = false;
}
renderer->DrawLayerQuad(layer->vOffset, layer->vScale, layer->tint);
// Display Decals in order for this layer
for (auto& decal : layer->vecDecalInstance)
renderer->DrawDecalQuad(decal);
layer->vecDecalInstance.clear();
}
else
{
// Mwa ha ha.... Have Fun!!!
layer->funcHook();
}
6 years ago
}
}
// Present Graphics to screen
renderer->DisplayFrame();
6 years ago
// Update Title Bar
fFrameTimer += fElapsedTime;
nFrameCount++;
if (fFrameTimer >= 1.0f)
{
nLastFPS = nFrameCount;
fFrameTimer -= 1.0f;
std::string sTitle = "OneLoneCoder.com - Pixel Game Engine - " + sAppName + " - FPS: " + std::to_string(nFrameCount);
platform->SetWindowTitle(sTitle);
nFrameCount = 0;
}
}
void PixelGameEngine::olc_ConstructFontSheet()
{
std::string data;
data += "?Q`0001oOch0o01o@F40o0<AGD4090LAGD<090@A7ch0?00O7Q`0600>00000000";
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`<O`000GP800000000";
data += "?P9PL020O`<`N3R0@E4HC7b0@ET<ATB0@@l6C4B0O`H3N7b0?P01L3R000000020";
fontSprite = new olc::Sprite(128, 48);
int px = 0, py = 0;
for (int b = 0; b < 1024; b += 4)
{
uint32_t sym1 = (uint32_t)data[b + 0] - 48;
uint32_t sym2 = (uint32_t)data[b + 1] - 48;
uint32_t sym3 = (uint32_t)data[b + 2] - 48;
uint32_t sym4 = (uint32_t)data[b + 3] - 48;
uint32_t r = sym1 << 18 | sym2 << 12 | sym3 << 6 | sym4;
6 years ago
for (int i = 0; i < 24; i++)
{
int k = r & (1 << i) ? 255 : 0;
6 years ago
fontSprite->SetPixel(px, py, olc::Pixel(k, k, k, k));
if (++py == 48) { px++; py = 0; }
}
}
fontDecal = new olc::Decal(fontSprite);
}
// Need a couple of statics as these are singleton instances
// read from multiple locations
std::atomic<bool> PixelGameEngine::bAtomActive{ false };
olc::PixelGameEngine* olc::PGEX::pge = nullptr;
olc::PixelGameEngine* olc::Platform::ptrPGE = nullptr;
olc::PixelGameEngine* olc::Renderer::ptrPGE = nullptr;
};
// O------------------------------------------------------------------------------O
// | olcPixelGameEngine PLATFORM SPECIFIC IMPLEMENTATIONS |
// O------------------------------------------------------------------------------O
// O------------------------------------------------------------------------------O
// | START RENDERER: OpenGL 1.0 (the original, the best...) |
// O------------------------------------------------------------------------------O
#if defined(OLC_GFX_OPENGL10)
#if defined(_WIN32)
#include <windows.h>
#include <GL/gl.h>
typedef BOOL(WINAPI wglSwapInterval_t) (int interval);
static wglSwapInterval_t* wglSwapInterval = nullptr;
typedef HDC glDeviceContext_t;
typedef HGLRC glRenderContext_t;
6 years ago
#endif
#if defined(__linux__) || defined(__FreeBSD__)
#include <GL/gl.h>
namespace X11
{
#include <GL/glx.h>
#include <X11/X.h>
#include <X11/Xlib.h>
}
#include <png.h>
typedef int(glSwapInterval_t)(X11::Display* dpy, X11::GLXDrawable drawable, int interval);
static glSwapInterval_t* glSwapIntervalEXT;
typedef X11::GLXContext glDeviceContext_t;
typedef X11::GLXContext glRenderContext_t;
#endif
6 years ago
namespace olc
{
class Renderer_OGL10 : public olc::Renderer
{
private:
glDeviceContext_t glDeviceContext = 0;
glRenderContext_t glRenderContext = 0;
#if defined(__linux__) || defined(__FreeBSD__)
X11::Display* olc_Display = nullptr;
X11::Window* olc_Window = nullptr;
X11::XVisualInfo* olc_VisualInfo = nullptr;
#endif
public:
void PrepareDevice() override
{ }
olc::rcode CreateDevice(std::vector<void*> params, bool bFullScreen, bool bVSYNC) override
{
#if defined(_WIN32)
// Create Device Context
glDeviceContext = GetDC((HWND)(params[0]));
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 olc::FAIL;
SetPixelFormat(glDeviceContext, pf, &pfd);
if (!(glRenderContext = wglCreateContext(glDeviceContext))) return olc::FAIL;
wglMakeCurrent(glDeviceContext, glRenderContext);
// Remove Frame cap
wglSwapInterval = (wglSwapInterval_t*)wglGetProcAddress("wglSwapIntervalEXT");
if (wglSwapInterval && !bVSYNC) wglSwapInterval(0);
#endif
#if defined(__linux__) || defined(__FreeBSD__)
using namespace X11;
// Linux has tighter coupling between OpenGL and X11, so we store
// various "platform" handles in the renderer
olc_Display = (X11::Display*)(params[0]);
olc_Window = (X11::Window*)(params[1]);
olc_VisualInfo = (X11::XVisualInfo*)(params[2]);
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 == nullptr && !bVSYNC)
{
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");
}
if (glSwapIntervalEXT != nullptr && !bVSYNC)
glSwapIntervalEXT(olc_Display, *olc_Window, 0);
#endif
glEnable(GL_TEXTURE_2D); // Turn on texturing
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
return olc::rcode::OK;
}
olc::rcode DestroyDevice() override
{
#if defined(_WIN32)
wglDeleteContext(glRenderContext);
#endif
#if defined(__linux__) || defined(__FreeBSD__)
glXMakeCurrent(olc_Display, None, NULL);
glXDestroyContext(olc_Display, glDeviceContext);
#endif
return olc::rcode::FAIL;
}
void DisplayFrame() override
{
#if defined(_WIN32)
SwapBuffers(glDeviceContext);
#endif
6 years ago
#if defined(__linux__) || defined(__FreeBSD__)
X11::glXSwapBuffers(olc_Display, *olc_Window);
#endif
}
6 years ago
void PrepareDrawing() override
{
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
6 years ago
void DrawLayerQuad(const olc::vf2d& offset, const olc::vf2d& scale, const olc::Pixel tint) override
{
glBegin(GL_QUADS);
glColor4ub(tint.r, tint.g, tint.b, tint.a);
glTexCoord2f(0.0f * scale.x + offset.x, 1.0f * scale.y + offset.y);
glVertex3f(-1.0f /*+ vSubPixelOffset.x*/, -1.0f /*+ vSubPixelOffset.y*/, 0.0f);
glTexCoord2f(0.0f * scale.x + offset.x, 0.0f * scale.y + offset.y);
glVertex3f(-1.0f /*+ vSubPixelOffset.x*/, 1.0f /*+ vSubPixelOffset.y*/, 0.0f);
glTexCoord2f(1.0f * scale.x + offset.x, 0.0f * scale.y + offset.y);
glVertex3f(1.0f /*+ vSubPixelOffset.x*/, 1.0f /*+ vSubPixelOffset.y*/, 0.0f);
glTexCoord2f(1.0f * scale.x + offset.x, 1.0f * scale.y + offset.y);
glVertex3f(1.0f /*+ vSubPixelOffset.x*/, -1.0f /*+ vSubPixelOffset.y*/, 0.0f);
glEnd();
}
6 years ago
void DrawDecalQuad(const olc::DecalInstance& decal) override
{
glBindTexture(GL_TEXTURE_2D, decal.decal->id);
glBegin(GL_QUADS);
glColor4ub(decal.tint.r, decal.tint.g, decal.tint.b, decal.tint.a);
glTexCoord4f(decal.uv[0].x, decal.uv[0].y, 0.0f, decal.w[0]); glVertex2f(decal.pos[0].x, decal.pos[0].y);
glTexCoord4f(decal.uv[1].x, decal.uv[1].y, 0.0f, decal.w[1]); glVertex2f(decal.pos[1].x, decal.pos[1].y);
glTexCoord4f(decal.uv[2].x, decal.uv[2].y, 0.0f, decal.w[2]); glVertex2f(decal.pos[2].x, decal.pos[2].y);
glTexCoord4f(decal.uv[3].x, decal.uv[3].y, 0.0f, decal.w[3]); glVertex2f(decal.pos[3].x, decal.pos[3].y);
glEnd();
}
6 years ago
uint32_t CreateTexture(const uint32_t width, const uint32_t height) override
{
uint32_t id = 0;
glGenTextures(1, &id);
glBindTexture(GL_TEXTURE_2D, id);
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_MODULATE);
return id;
}
6 years ago
uint32_t DeleteTexture(const uint32_t id) override
{
glDeleteTextures(1, &id);
return id;
}
void UpdateTexture(uint32_t id, olc::Sprite* spr) override
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, spr->width, spr->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, spr->GetData());
}
6 years ago
void ApplyTexture(uint32_t id) override
{
glBindTexture(GL_TEXTURE_2D, id);
}
6 years ago
void ClearBuffer(olc::Pixel p, bool bDepth) override
{
glClearColor(float(p.r) / 255.0f, float(p.g) / 255.0f, float(p.b) / 255.0f, float(p.a) / 255.0f);
glClear(GL_COLOR_BUFFER_BIT);
if (bDepth) glClear(GL_DEPTH_BUFFER_BIT);
}
void UpdateViewport(const olc::vi2d& pos, const olc::vi2d& size) override
{
glViewport(pos.x, pos.y, size.x, size.y);
}
};
}
#endif
// O------------------------------------------------------------------------------O
// | END RENDERER: OpenGL 1.0 (the original, the best...) |
// O------------------------------------------------------------------------------O
// O------------------------------------------------------------------------------O
// | START PLATFORM: MICROSOFT WINDOWS XP, VISTA, 7, 8, 10 |
// O------------------------------------------------------------------------------O
#if defined(_WIN32)
#if !defined(__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")
#pragma comment(lib, "Shlwapi.lib")
#else
// In Code::Blocks
#if !defined(_WIN32_WINNT)
#ifdef HAVE_MSMF
#define _WIN32_WINNT 0x0600 // Windows Vista
#else
#define _WIN32_WINNT 0x0500 // Windows 2000
#endif
#endif
#endif
// Include WinAPI
#if !defined(NOMINMAX)
#define NOMINMAX
#endif
#define VC_EXTRALEAN
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <gdiplus.h>
#include <Shlwapi.h>
namespace olc
{
// Little utility function to convert from char to wchar in Windows environments
// depending upon how the compiler is configured. This should not be necessary
// on linux platforms
std::wstring ConvertS2W(std::string s)
{
#ifdef __MINGW32__
wchar_t *buffer = new wchar_t[s.length() + 1];
mbstowcs(buffer, s.c_str(), s.length());
buffer[s.length()] = L'\0';
#else
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);
#endif
std::wstring w(buffer);
delete[] buffer;
return w;
6 years ago
}
// Thanks @MaGetzUb for this, which allows sprites to be defined
// at construction, by initialising the GDI subsystem
static class GDIPlusStartup
6 years ago
{
public:
GDIPlusStartup()
6 years ago
{
Gdiplus::GdiplusStartupInput startupInput;
ULONG_PTR token;
Gdiplus::GdiplusStartup(&token, &startupInput, NULL);
6 years ago
};
} gdistartup;
6 years ago
class Platform_Windows : public olc::Platform
{
private:
HWND olc_hWnd = nullptr;
std::wstring wsAppName;
6 years ago
public:
virtual olc::rcode ApplicationStartUp() override { return olc::rcode::OK; }
virtual olc::rcode ApplicationCleanUp() override { return olc::rcode::OK; }
virtual olc::rcode ThreadStartUp() override { return olc::rcode::OK; }
virtual olc::rcode ThreadCleanUp() override
{
renderer->DestroyDevice();
PostMessage(olc_hWnd, WM_DESTROY, 0, 0);
return olc::OK;
}
6 years ago
virtual olc::rcode CreateGraphics(bool bFullScreen, bool bEnableVSYNC, const olc::vi2d& vViewPos, const olc::vi2d& vViewSize) override
6 years ago
{
if (renderer->CreateDevice({ olc_hWnd }, bFullScreen, bEnableVSYNC) == olc::rcode::OK)
{
renderer->UpdateViewport(vViewPos, vViewSize);
return olc::rcode::OK;
}
else
return olc::rcode::FAIL;
}
virtual olc::rcode CreateWindowPane(const olc::vi2d& vWindowPos, olc::vi2d &vWindowSize, bool bFullScreen) override
{
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;
wc.lpszClassName = olcT("OLC_PIXEL_GAME_ENGINE");
RegisterClass(&wc);
// Define window furniture
DWORD dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
DWORD dwStyle = WS_CAPTION | WS_SYSMENU | WS_VISIBLE | WS_THICKFRAME;
// Handle Fullscreen
if (bFullScreen)
{
dwExStyle = 0;
dwStyle = WS_VISIBLE | WS_POPUP;
HMONITOR hmon = MonitorFromWindow(olc_hWnd, MONITOR_DEFAULTTONEAREST);
MONITORINFO mi = { sizeof(mi) };
if (!GetMonitorInfo(hmon, &mi)) return olc::rcode::FAIL;
vWindowSize = { mi.rcMonitor.right, mi.rcMonitor.bottom };
}
// Keep client size as requested
RECT rWndRect = { 0, 0, vWindowSize.x, vWindowSize.y };
AdjustWindowRectEx(&rWndRect, dwStyle, FALSE, dwExStyle);
int width = rWndRect.right - rWndRect.left;
int height = rWndRect.bottom - rWndRect.top;
olc_hWnd = CreateWindowEx(dwExStyle, olcT("OLC_PIXEL_GAME_ENGINE"), olcT(""), dwStyle,
vWindowPos.x, vWindowPos.y, width, height, NULL, NULL, GetModuleHandle(nullptr), this);
// Create Keyboard Mapping
mapKeys[0x00] = Key::NONE;
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::OK;
}
virtual olc::rcode SetWindowTitle(const std::string& s) override
{
#ifdef UNICODE
SetWindowText(olc_hWnd, ConvertS2W(s).c_str());
#else
SetWindowText(olc_hWnd, s.c_str());
#endif
return olc::OK;
}
virtual olc::rcode StartSystemEventLoop() override
{
MSG msg;
while (GetMessage(&msg, NULL, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return olc::OK;
}
virtual olc::rcode HandleSystemEvent() override { return olc::rcode::FAIL; }
// Windows Event Handler - this is statically connected to the windows event system
static LRESULT CALLBACK olc_WindowEvent(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_MOUSEMOVE:
{
// Thanks @ForAbby (Discord)
uint16_t x = lParam & 0xFFFF; uint16_t y = (lParam >> 16) & 0xFFFF;
int16_t ix = *(int16_t*)&x; int16_t iy = *(int16_t*)&y;
ptrPGE->olc_UpdateMouse(ix, iy);
return 0;
}
case WM_SIZE: ptrPGE->olc_UpdateWindowSize(lParam & 0xFFFF, (lParam >> 16) & 0xFFFF); return 0;
case WM_MOUSEWHEEL: ptrPGE->olc_UpdateMouseWheel(GET_WHEEL_DELTA_WPARAM(wParam)); return 0;
case WM_MOUSELEAVE: ptrPGE->olc_UpdateMouseFocus(false); return 0;
case WM_SETFOCUS: ptrPGE->olc_UpdateKeyFocus(true); return 0;
case WM_KILLFOCUS: ptrPGE->olc_UpdateKeyFocus(false); return 0;
case WM_KEYDOWN: ptrPGE->olc_UpdateKeyState(mapKeys[wParam], true); return 0;
case WM_KEYUP: ptrPGE->olc_UpdateKeyState(mapKeys[wParam], false); return 0;
case WM_LBUTTONDOWN:ptrPGE->olc_UpdateMouseState(0, true); return 0;
case WM_LBUTTONUP: ptrPGE->olc_UpdateMouseState(0, false); return 0;
case WM_RBUTTONDOWN:ptrPGE->olc_UpdateMouseState(1, true); return 0;
case WM_RBUTTONUP: ptrPGE->olc_UpdateMouseState(1, false); return 0;
case WM_MBUTTONDOWN:ptrPGE->olc_UpdateMouseState(2, true); return 0;
case WM_MBUTTONUP: ptrPGE->olc_UpdateMouseState(2, false); return 0;
case WM_CLOSE: ptrPGE->olc_Terminate(); return 0;
case WM_DESTROY: PostQuitMessage(0); return 0;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
};
// On Windows load images using GDI+ library
olc::rcode Sprite::LoadFromFile(const std::string& sImageFile, olc::ResourcePack *pack)
{
UNUSED(pack);
Gdiplus::Bitmap *bmp = nullptr;
if (pack != nullptr)
{
// Load sprite from input stream
ResourceBuffer rb = pack->GetFileBuffer(sImageFile);
bmp = Gdiplus::Bitmap::FromStream(SHCreateMemStream((BYTE*)rb.vMemory.data(), UINT(rb.vMemory.size())));
}
else
{
// Load sprite from file
bmp = Gdiplus::Bitmap::FromFile(ConvertS2W(sImageFile).c_str());
}
if (bmp->GetLastStatus() != Gdiplus::Ok) return olc::NO_FILE;
width = bmp->GetWidth();
height = bmp->GetHeight();
pColData = new Pixel[width * height];
for (int y = 0; y < height; y++)
for (int x = 0; x < width; x++)
{
Gdiplus::Color c;
bmp->GetPixel(x, y, &c);
SetPixel(x, y, olc::Pixel(c.GetRed(), c.GetGreen(), c.GetBlue(), c.GetAlpha()));
}
delete bmp;
return olc::OK;
6 years ago
}
}
#endif
// O------------------------------------------------------------------------------O
// | END PLATFORM: MICROSOFT WINDOWS XP, VISTA, 7, 8, 10 |
// O------------------------------------------------------------------------------O
// O------------------------------------------------------------------------------O
// | START PLATFORM: LINUX |
// O------------------------------------------------------------------------------O
#if defined(__linux__) || defined(__FreeBSD__)
namespace olc
{
class Platform_Linux : public olc::Platform
{
private:
X11::Display* olc_Display = nullptr;
X11::Window olc_WindowRoot;
X11::Window olc_Window;
X11::XVisualInfo* olc_VisualInfo;
X11::Colormap olc_ColourMap;
X11::XSetWindowAttributes olc_SetWindowAttribs;
public:
virtual olc::rcode ApplicationStartUp() override
{ return olc::rcode::OK; }
virtual olc::rcode ApplicationCleanUp() override
{ return olc::rcode::OK; }
virtual olc::rcode ThreadStartUp() override
{ return olc::rcode::OK; }
virtual olc::rcode ThreadCleanUp() override
{
renderer->DestroyDevice();
return olc::OK;
}
virtual olc::rcode CreateGraphics(bool bFullScreen, bool bEnableVSYNC, const olc::vi2d& vViewPos, const olc::vi2d& vViewSize) override
{
if (renderer->CreateDevice({ olc_Display, &olc_Window, olc_VisualInfo }, bFullScreen, bEnableVSYNC) == olc::rcode::OK)
{
renderer->UpdateViewport(vViewPos, vViewSize);
return olc::rcode::OK;
}
else
return olc::rcode::FAIL;
}
virtual olc::rcode CreateWindowPane(const olc::vi2d& vWindowPos, olc::vi2d& vWindowSize, bool bFullScreen) override
{
using namespace X11;
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 | StructureNotifyMask;
// Create the window
olc_Window = XCreateWindow(olc_Display, olc_WindowRoot, vWindowPos.x, vWindowPos.y,
vWindowSize.x, vWindowSize.y,
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, "OneLoneCoder.com - Pixel Game Engine");
if (bFullScreen) // Thanks DragonEye, again :D
{
Atom wm_state;
Atom fullscreen;
wm_state = XInternAtom(olc_Display, "_NET_WM_STATE", False);
fullscreen = XInternAtom(olc_Display, "_NET_WM_STATE_FULLSCREEN", False);
XEvent xev{ 0 };
xev.type = ClientMessage;
xev.xclient.window = olc_Window;
xev.xclient.message_type = wm_state;
xev.xclient.format = 32;
xev.xclient.data.l[0] = (bFullScreen ? 1 : 0); // the action (0: off, 1: on, 2: toggle)
xev.xclient.data.l[1] = fullscreen; // first property to alter
xev.xclient.data.l[2] = 0; // second property to alter
xev.xclient.data.l[3] = 0; // source indication
XMapWindow(olc_Display, olc_Window);
XSendEvent(olc_Display, DefaultRootWindow(olc_Display), False,
SubstructureRedirectMask | SubstructureNotifyMask, &xev);
XFlush(olc_Display);
XWindowAttributes gwa;
XGetWindowAttributes(olc_Display, olc_Window, &gwa);
vWindowSize.x = gwa.width;
vWindowSize.y = gwa.height;
}
// Create Keyboard Mapping
mapKeys[0x00] = Key::NONE;
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_period] = Key::PERIOD;
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::OK;
}
virtual olc::rcode SetWindowTitle(const std::string& s) override
{
X11::XStoreName(olc_Display, olc_Window, s.c_str());
return olc::OK;
}
virtual olc::rcode StartSystemEventLoop() override
{ return olc::OK; }
virtual olc::rcode HandleSystemEvent() override
{
using namespace X11;
// 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);
ptrPGE->olc_UpdateWindowSize(gwa.width, gwa.height);
}
else if (xev.type == ConfigureNotify)
{
XConfigureEvent xce = xev.xconfigure;
ptrPGE->olc_UpdateWindowSize(xce.width, xce.height);
}
else if (xev.type == KeyPress)
{
KeySym sym = XLookupKeysym(&xev.xkey, 0);
ptrPGE->olc_UpdateKeyState(mapKeys[sym], true);
XKeyEvent* e = (XKeyEvent*)&xev; // Because DragonEye loves numpads
XLookupString(e, NULL, 0, &sym, NULL);
ptrPGE->olc_UpdateKeyState(mapKeys[sym], true);
}
else if (xev.type == KeyRelease)
{
KeySym sym = XLookupKeysym(&xev.xkey, 0);
ptrPGE->olc_UpdateKeyState(mapKeys[sym], false);
XKeyEvent* e = (XKeyEvent*)&xev;
XLookupString(e, NULL, 0, &sym, NULL);
ptrPGE->olc_UpdateKeyState(mapKeys[sym], false);
}
else if (xev.type == ButtonPress)
{
switch (xev.xbutton.button)
{
case 1: ptrPGE->olc_UpdateMouseState(0, true); break;
case 2: ptrPGE->olc_UpdateMouseState(2, true); break;
case 3: ptrPGE->olc_UpdateMouseState(1, true); break;
case 4: ptrPGE->olc_UpdateMouseWheel(120); break;
case 5: ptrPGE->olc_UpdateMouseWheel(-120); break;
default: break;
}
}
else if (xev.type == ButtonRelease)
{
switch (xev.xbutton.button)
{
case 1: ptrPGE->olc_UpdateMouseState(0, false); break;
case 2: ptrPGE->olc_UpdateMouseState(2, false); break;
case 3: ptrPGE->olc_UpdateMouseState(1, false); break;
default: break;
}
}
else if (xev.type == MotionNotify)
{
ptrPGE->olc_UpdateMouse(xev.xmotion.x, xev.xmotion.y);
}
else if (xev.type == FocusIn)
{
ptrPGE->olc_UpdateKeyFocus(true);
}
else if (xev.type == FocusOut)
{
ptrPGE->olc_UpdateKeyFocus(false);
}
else if (xev.type == ClientMessage)
{
ptrPGE->olc_Terminate();
}
}
return olc::OK;
}
};
void pngReadStream(png_structp pngPtr, png_bytep data, png_size_t length)
{
png_voidp a = png_get_io_ptr(pngPtr);
((std::istream*)a)->read((char*)data, length);
6 years ago
}
olc::rcode Sprite::LoadFromFile(const std::string& sImageFile, olc::ResourcePack* pack)
6 years ago
{
UNUSED(pack);
////////////////////////////////////////////////////////////////////////////
// Use libpng, Thanks to Guillaume Cottenceau
// https://gist.github.com/niw/5963798
// Also reading png from streams
// http://www.piko3d.net/tutorials/libpng-tutorial-loading-png-files-from-streams/
png_structp png;
png_infop info;
auto loadPNG = [&]()
{
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);
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]));
}
}
for (int y = 0; y < height; y++) // Thanks maksym33
free(row_pointers[y]);
free(row_pointers);
png_destroy_read_struct(&png, &info, nullptr);
};
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;
if (pack == nullptr)
6 years ago
{
FILE* f = fopen(sImageFile.c_str(), "rb");
if (!f) return olc::NO_FILE;
png_init_io(png, f);
loadPNG();
fclose(f);
}
else
{
ResourceBuffer rb = pack->GetFileBuffer(sImageFile);
std::istream is(&rb);
png_set_read_fn(png, (png_voidp)&is, pngReadStream);
loadPNG();
6 years ago
}
return olc::OK;
fail_load:
width = 0;
height = 0;
pColData = nullptr;
return olc::FAIL;
6 years ago
}
}
#endif
// O------------------------------------------------------------------------------O
// | END PLATFORM: LINUX |
// O------------------------------------------------------------------------------O
6 years ago
namespace olc
{
void PixelGameEngine::olc_ConfigureSystem()
{
#if defined(_WIN32)
platform = std::make_unique<olc::Platform_Windows>();
6 years ago
#endif
#if defined(__linux__) || defined(__FreeBSD__)
platform = std::make_unique<olc::Platform_Linux>();
#endif
#if defined(OLC_GFX_OPENGL10)
renderer = std::make_unique<olc::Renderer_OGL10>();
#endif
6 years ago
#if defined(OLC_GFX_OPENGL33)
renderer = std::make_unique<olc::Renderer_OGL33>();
#endif
#if defined(OLC_GFX_DIRECTX10)
renderer = std::make_unique<olc::Renderer_DX10>();
#endif
//// Associate components with PGE instance
platform->ptrPGE = this;
renderer->ptrPGE = this;
}
}
#endif // End olc namespace
// O------------------------------------------------------------------------------O
// | END OF OLC_PGE_APPLICATION |
// O------------------------------------------------------------------------------O