diff --git a/olcPixelGameEngine.h b/olcPixelGameEngine.h
index d9f57d1..5480a15 100644
--- a/olcPixelGameEngine.h
+++ b/olcPixelGameEngine.h
@@ -3,7 +3,7 @@
 	olcPixelGameEngine.h
 
 	+-------------------------------------------------------------+
-	|           OneLoneCoder Pixel Game Engine v2.20              |
+	|           OneLoneCoder Pixel Game Engine v2.21              |
 	|  "What do you need? Pixels... Lots of Pixels..." - javidx9  |
 	+-------------------------------------------------------------+
 
@@ -303,12 +303,19 @@
 	2.20: +DrawRectDecal() - Keeps OneSketchyGuy quiet
 	      +GetScreenSize()
 		  +olc::Sprite::Size() - returns size of sprite in vector format
+	2.21: Emscripten Overhaul - Thanks Moros!
+		  +DrawPolygonDecal() tint overload, can now tint a polygon accounting for vertex colours
+		  +Multiplicative Pixel overload
+		  +v2d_generic clamp()
+		  +v2d_generic lerp()
+		  +GetDroppedFiles() - returns files dropped onto engine window for that frame (MSW only)
+		  +GetDroppedFilesPoint() - returns location of dropped files (MSW only)
+		  +Exposed OpenGL33 Loader interface so the typedefs can be shared with PGEX & user
+		  +Fix OGL33 DecalStructure types - wow, how did that one get missed?? lol
+		  +FillTexturedTriangle() - Software rasterizes a textured, coloured, triangle
+		  +FillTexturedPolygon() - Hijacks DecalStructure for configuration
+		  +olc::vf2d arguments for Sprite::Sample() functions
 		  
-	SIG Updates:
-		The following additions are included in Sig's version of the PGE header (this is a sig version)
-			+GetAnyKey() - Returns when a key is activated
-			+GetAnyKeyPress() - Returns when a key is pressed down (and if a keyboard key, which key it was)
-
     !! Apple Platforms will not see these updates immediately - Sorry, I dont have a mac to test... !!
 	!!   Volunteers willing to help appreciated, though PRs are manually integrated with credit     !!
 */
@@ -387,7 +394,7 @@ int main()
 #include <cstring>
 #pragma endregion
 
-#define PGE_VER 220
+#define PGE_VER 221
 
 // O------------------------------------------------------------------------------O
 // | COMPILER CONFIGURATION ODDITIES                                              |
@@ -433,8 +440,16 @@ int main()
 // | PLATFORM SELECTION CODE, Thanks slavka!                                      |
 // O------------------------------------------------------------------------------O
 
+#if defined(OLC_PGE_HEADLESS)
+	#define OLC_PLATFORM_HEADLESS
+	#define OLC_GFX_HEADLESS
+	#if !defined(OLC_IMAGE_STB) && !defined(OLC_IMAGE_GDI) && !defined(OLC_IMAGE_LIBPNG)
+		#define OLC_IMAGE_HEADLESS
+	#endif
+#endif
+
 // Platform
-#if !defined(OLC_PLATFORM_WINAPI) && !defined(OLC_PLATFORM_X11) && !defined(OLC_PLATFORM_GLUT) && !defined(OLC_PLATFORM_EMSCRIPTEN)
+#if !defined(OLC_PLATFORM_WINAPI) && !defined(OLC_PLATFORM_X11) && !defined(OLC_PLATFORM_GLUT) && !defined(OLC_PLATFORM_EMSCRIPTEN) && !defined(OLC_PLATFORM_HEADLESS)
 	#if !defined(OLC_PLATFORM_CUSTOM_EX)
 		#if defined(_WIN32)
 			#define OLC_PLATFORM_WINAPI
@@ -457,8 +472,10 @@ int main()
 	#define PGE_USE_CUSTOM_START
 #endif
 
+
+
 // Renderer
-#if !defined(OLC_GFX_OPENGL10) && !defined(OLC_GFX_OPENGL33) && !defined(OLC_GFX_DIRECTX10)
+#if !defined(OLC_GFX_OPENGL10) && !defined(OLC_GFX_OPENGL33) && !defined(OLC_GFX_DIRECTX10) && !defined(OLC_GFX_HEADLESS)
 	#if !defined(OLC_GFX_CUSTOM_EX)
 		#if defined(OLC_PLATFORM_EMSCRIPTEN)
 			#define OLC_GFX_OPENGL33
@@ -469,7 +486,7 @@ int main()
 #endif
 
 // Image loader
-#if !defined(OLC_IMAGE_STB) && !defined(OLC_IMAGE_GDI) && !defined(OLC_IMAGE_LIBPNG)
+#if !defined(OLC_IMAGE_STB) && !defined(OLC_IMAGE_GDI) && !defined(OLC_IMAGE_LIBPNG) && !defined(OLC_IMAGE_HEADLESS)
 	#if !defined(OLC_IMAGE_CUSTOM_EX)
 		#if defined(_WIN32)
 			#define	OLC_IMAGE_GDI
@@ -527,6 +544,15 @@ int main()
 	#endif
 #endif
 #endif
+
+#if defined(OLC_PGE_HEADLESS)
+#if defined max
+#undef max
+#endif
+#if defined min
+#undef min
+#endif
+#endif
 #pragma endregion
 
 // O------------------------------------------------------------------------------O
@@ -543,6 +569,7 @@ namespace olc
 	constexpr uint8_t  nDefaultAlpha = 0xFF;
 	constexpr uint32_t nDefaultPixel = (nDefaultAlpha << 24);
 	constexpr uint8_t  nTabSizeInSpaces = 4;
+	constexpr size_t OLC_MAX_VERTS = 128;
 	enum rcode { FAIL = 0, OK = 1, NO_FILE = -1 };
 
 	// O------------------------------------------------------------------------------O
@@ -572,6 +599,8 @@ namespace olc
 		Pixel  operator - (const Pixel& p) const;
 		Pixel& operator +=(const Pixel& p);
 		Pixel& operator -=(const Pixel& p);
+		Pixel  operator * (const Pixel& p) const;
+		Pixel& operator *=(const Pixel& p);
 		Pixel  inv() const;
 	};
 
@@ -653,6 +682,8 @@ namespace olc
 		v2d_generic  min(const v2d_generic& v) const { return v2d_generic(std::min(x, v.x), std::min(y, v.y)); }
 		v2d_generic  cart() { return { std::cos(y) * x, std::sin(y) * x }; }
 		v2d_generic  polar() { return { mag(), std::atan2(y, x) }; }
+		v2d_generic  clamp(const v2d_generic& v1, const v2d_generic& v2) const { return this->max(v1)->min(v2); }
+		v2d_generic	 lerp(const v2d_generic& v1, const double t) { return this->operator*(T(1.0 - t)) + (v1 * T(t)); }
 		T dot(const v2d_generic& rhs) const { return this->x * rhs.x + this->y * rhs.y; }
 		T cross(const v2d_generic& rhs) const { return this->x * rhs.y - this->y * rhs.x; }
 		v2d_generic  operator +  (const v2d_generic& rhs) const { return v2d_generic(this->x + rhs.x, this->y + rhs.y); }
@@ -776,7 +807,9 @@ namespace olc
 		Pixel GetPixel(const olc::vi2d& a) const;
 		bool  SetPixel(const olc::vi2d& a, Pixel p);
 		Pixel Sample(float x, float y) const;
+		Pixel Sample(const olc::vf2d& uv) const;
 		Pixel SampleBL(float u, float v) const;
+		Pixel SampleBL(const olc::vf2d& uv) const;
 		Pixel* GetData();
 		olc::Sprite* Duplicate();
 		olc::Sprite* Duplicate(const olc::vi2d& vPos, const olc::vi2d& vSize);
@@ -938,14 +971,13 @@ namespace olc
 		virtual bool OnUserUpdate(float fElapsedTime);
 		// Called once on application termination, so you can be one clean coder
 		virtual bool OnUserDestroy();
-		virtual void GetAnyKey();
-		virtual void GetAnyKeyPress(olc::Key pressedKey);
 
 		// Called when a text entry is confirmed with "enter" key
 		virtual void OnTextEntryComplete(const std::string& sText);
 		// Called when a console command is executed
 		virtual bool OnConsoleCommand(const std::string& sCommand);
 
+
 	public: // Hardware Interfaces
 		// Returns true if window is currently in focus
 		bool IsFocused() const;
@@ -994,6 +1026,9 @@ namespace olc
 		const olc::vi2d& GetScreenPixelSize() const;
 		// Gets "screen" size
 		const olc::vi2d& GetScreenSize() const;
+		// Gets any files dropped this frame
+		const std::vector<std::string>& GetDroppedFiles() const;
+		const olc::vi2d& GetDroppedFilesPoint() const;
 
 	public: // CONFIGURATION ROUTINES
 		// Layer targeting functions
@@ -1047,6 +1082,9 @@ namespace olc
 		// 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);
+		// Fill a textured and coloured triangle
+		void FillTexturedTriangle(const std::vector<olc::vf2d>& vPoints, std::vector<olc::vf2d> vTex, std::vector<olc::Pixel> vColour, olc::Sprite* sprTex);
+		void FillTexturedPolygon(const std::vector<olc::vf2d>& vPoints, const std::vector<olc::vf2d>& vTex, const std::vector<olc::Pixel>& vColour, olc::Sprite* sprTex, olc::DecalStructure structure = olc::DecalStructure::LIST);
 		// Draws an entire sprite at location (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);
@@ -1096,7 +1134,7 @@ namespace olc
 		void DrawPolygonDecal(olc::Decal* decal, const std::vector<olc::vf2d>& pos, const std::vector<olc::vf2d>& uv, const olc::Pixel tint = olc::WHITE);
 		void DrawPolygonDecal(olc::Decal* decal, const std::vector<olc::vf2d>& pos, const std::vector<float>& depth, const std::vector<olc::vf2d>& uv, const olc::Pixel tint = olc::WHITE);
 		void DrawPolygonDecal(olc::Decal* decal, const std::vector<olc::vf2d>& pos, const std::vector<olc::vf2d>& uv, const std::vector<olc::Pixel>& tint);
-
+		void DrawPolygonDecal(olc::Decal* decal, const std::vector<olc::vf2d>& pos, const std::vector<olc::vf2d>& uv, const std::vector<olc::Pixel>& colours, const olc::Pixel tint);
 		// Draws a line in Decal Space
 		void DrawLineDecal(const olc::vf2d& pos1, const olc::vf2d& pos2, Pixel p = olc::WHITE);
 		void DrawRotatedStringDecal(const olc::vf2d& pos, const std::string& sText, const float fAngle, const olc::vf2d& center = { 0.0f, 0.0f }, const olc::Pixel col = olc::WHITE, const olc::vf2d& scale = { 1.0f, 1.0f });
@@ -1199,6 +1237,10 @@ namespace olc
 		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;
 		std::vector<olc::vi2d> vFontSpacing;
+		std::vector<std::string> vDroppedFiles;
+		std::vector<std::string> vDroppedFilesCache;
+		olc::vi2d vDroppedFilesPoint;
+		olc::vi2d vDroppedFilesPointCache;
 
 		// Command Console Specific
 		bool bConsoleShow = false;
@@ -1253,6 +1295,7 @@ namespace olc
 		void olc_UpdateMouseFocus(bool state);
 		void olc_UpdateKeyFocus(bool state);
 		void olc_Terminate();
+		void olc_DropFiles(int32_t x, int32_t y, const std::vector<std::string>& vFiles);
 		void olc_Reanimate();
 		bool olc_IsRunning();
 
@@ -1296,6 +1339,100 @@ namespace olc
 
 #pragma endregion
 
+
+#pragma region opengl33_iface
+// In order to facilitate more advanced graphics features, some PGEX
+// will rely on shaders. Instead of having each PGEX responsible for
+// managing this, for convenience, this interface exists.
+
+#if defined(OLC_GFX_OPENGL33)
+
+	#if defined(OLC_PLATFORM_WINAPI)
+		#include <gl/GL.h>
+		#define CALLSTYLE __stdcall
+	#endif
+
+	#if defined(__linux__) || defined(__FreeBSD__)
+		#include <GL/gl.h>
+	#endif
+
+	#if defined(OLC_PLATFORM_X11)
+		namespace X11
+		{#include <GL/glx.h>}
+		#define CALLSTYLE 
+	#endif
+
+	#if defined(__APPLE__)
+		#define GL_SILENCE_DEPRECATION
+		#include <OpenGL/OpenGL.h>
+		#include <OpenGL/gl.h>
+		#include <OpenGL/glu.h>
+	#endif
+
+	#if defined(OLC_PLATFORM_EMSCRIPTEN)
+		#include <EGL/egl.h>
+		#include <GLES2/gl2.h>
+		#define GL_GLEXT_PROTOTYPES
+		#include <GLES2/gl2ext.h>
+		#include <emscripten/emscripten.h>
+		#define CALLSTYLE
+		#define GL_CLAMP GL_CLAMP_TO_EDGE
+	#endif
+
+namespace olc
+{
+	typedef char GLchar;
+	typedef ptrdiff_t GLsizeiptr;
+
+	typedef GLuint CALLSTYLE locCreateShader_t(GLenum type);
+	typedef GLuint CALLSTYLE locCreateProgram_t(void);
+	typedef void CALLSTYLE locDeleteShader_t(GLuint shader);
+	typedef void CALLSTYLE locCompileShader_t(GLuint shader);
+	typedef void CALLSTYLE locLinkProgram_t(GLuint program);
+	typedef void CALLSTYLE locDeleteProgram_t(GLuint program);
+	typedef void CALLSTYLE locAttachShader_t(GLuint program, GLuint shader);
+	typedef void CALLSTYLE locBindBuffer_t(GLenum target, GLuint buffer);
+	typedef void CALLSTYLE locBufferData_t(GLenum target, GLsizeiptr size, const void* data, GLenum usage);
+	typedef void CALLSTYLE locGenBuffers_t(GLsizei n, GLuint* buffers);
+	typedef void CALLSTYLE locVertexAttribPointer_t(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void* pointer);
+	typedef void CALLSTYLE locEnableVertexAttribArray_t(GLuint index);
+	typedef void CALLSTYLE locUseProgram_t(GLuint program);
+	typedef void CALLSTYLE locBindVertexArray_t(GLuint array);
+	typedef void CALLSTYLE locGenVertexArrays_t(GLsizei n, GLuint* arrays);
+	typedef void CALLSTYLE locGetShaderInfoLog_t(GLuint shader, GLsizei bufSize, GLsizei* length, GLchar* infoLog);
+	typedef GLint CALLSTYLE locGetUniformLocation_t(GLuint program, const GLchar* name);
+	typedef void CALLSTYLE locUniform1f_t(GLint location, GLfloat v0);
+	typedef void CALLSTYLE locUniform1i_t(GLint location, GLint v0);
+	typedef void CALLSTYLE locUniform2fv_t(GLint location, GLsizei count, const GLfloat* value);
+	typedef void CALLSTYLE locActiveTexture_t(GLenum texture);
+	typedef void CALLSTYLE locGenFrameBuffers_t(GLsizei n, GLuint* ids);
+	typedef void CALLSTYLE locBindFrameBuffer_t(GLenum target, GLuint fb);
+	typedef GLenum CALLSTYLE locCheckFrameBufferStatus_t(GLenum target);
+	typedef void CALLSTYLE locDeleteFrameBuffers_t(GLsizei n, const GLuint* fbs);
+	typedef void CALLSTYLE locFrameBufferTexture2D_t(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level);
+	typedef void CALLSTYLE locDrawBuffers_t(GLsizei n, const GLenum* bufs);
+	typedef void CALLSTYLE locBlendFuncSeparate_t(GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha);
+
+#if defined(OLC_PLATFORM_WINAPI)
+	typedef void __stdcall locSwapInterval_t(GLsizei n);
+#endif
+
+#if defined(OLC_PLATFORM_X11)
+	typedef int(locSwapInterval_t)(X11::Display* dpy, X11::GLXDrawable drawable, int interval);
+#endif
+
+#if defined(OLC_PLATFORM_EMSCRIPTEN)
+	typedef void CALLSTYLE locShaderSource_t(GLuint shader, GLsizei count, const GLchar* const* string, const GLint* length);
+	typedef EGLBoolean(locSwapInterval_t)(EGLDisplay display, EGLint interval);
+#else
+	typedef void CALLSTYLE locShaderSource_t(GLuint shader, GLsizei count, const GLchar** string, const GLint* length);
+#endif
+
+} // olc namespace
+#endif // OpenGL33 Definitions
+#pragma endregion
+
+
 #endif // OLC_PGE_DEF
 
 
@@ -1394,6 +1531,24 @@ namespace olc
 		return *this;
 	}
 
+	Pixel Pixel::operator * (const Pixel& p) const
+	{
+		uint8_t nR = uint8_t(std::min(255.0f, std::max(0.0f, float(r) * float(p.r) / 255.0f)));
+		uint8_t nG = uint8_t(std::min(255.0f, std::max(0.0f, float(g) * float(p.g) / 255.0f)));
+		uint8_t nB = uint8_t(std::min(255.0f, std::max(0.0f, float(b) * float(p.b) / 255.0f)));
+		uint8_t nA = uint8_t(std::min(255.0f, std::max(0.0f, float(a) * float(p.a) / 255.0f)));
+		return Pixel(nR, nG, nB, nA);
+	}
+
+	Pixel& Pixel::operator *=(const Pixel& p)
+	{
+		this->r = uint8_t(std::min(255.0f, std::max(0.0f, float(r) * float(p.r) / 255.0f)));
+		this->g = uint8_t(std::min(255.0f, std::max(0.0f, float(g) * float(p.g) / 255.0f)));
+		this->b = uint8_t(std::min(255.0f, std::max(0.0f, float(b) * float(p.b) / 255.0f)));
+		this->a = uint8_t(std::min(255.0f, std::max(0.0f, float(a) * float(p.a) / 255.0f)));
+		return *this;
+	}
+
 	Pixel Pixel::inv() const
 	{
 		uint8_t nR = uint8_t(std::min(255, std::max(0, 255 - int(r))));
@@ -1472,6 +1627,11 @@ namespace olc
 		return GetPixel(sx, sy);
 	}
 
+	Pixel Sprite::Sample(const olc::vf2d& uv) const
+	{
+		return Sample(uv.x, uv.y);
+	}
+
 	Pixel Sprite::SampleBL(float u, float v) const
 	{
 		u = u * width - 0.5f;
@@ -1494,6 +1654,11 @@ namespace olc
 			(uint8_t)((p1.b * u_opposite + p2.b * u_ratio) * v_opposite + (p3.b * u_opposite + p4.b * u_ratio) * v_ratio));
 	}
 
+	Pixel Sprite::SampleBL(const olc::vf2d& uv) const
+	{
+		return SampleBL(uv.x, uv.y);
+	}
+
 	Pixel* Sprite::GetData()
 	{ return pColData.data(); }
 
@@ -2411,6 +2576,168 @@ namespace olc
 		}
 	}
 
+	void PixelGameEngine::FillTexturedTriangle(const std::vector<olc::vf2d>& vPoints, std::vector<olc::vf2d> vTex, std::vector<olc::Pixel> vColour, olc::Sprite* sprTex)
+	{
+		olc::vi2d p1 = vPoints[0];
+		olc::vi2d p2 = vPoints[1];
+		olc::vi2d p3 = vPoints[2];
+
+		if (p2.y < p1.y){std::swap(p1.y, p2.y); std::swap(p1.x, p2.x); std::swap(vTex[0].x, vTex[1].x); std::swap(vTex[0].y, vTex[1].y); std::swap(vColour[0], vColour[1]);}
+		if (p3.y < p1.y){std::swap(p1.y, p3.y); std::swap(p1.x, p3.x); std::swap(vTex[0].x, vTex[2].x); std::swap(vTex[0].y, vTex[2].y); std::swap(vColour[0], vColour[2]);}
+		if (p3.y < p2.y){std::swap(p2.y, p3.y); std::swap(p2.x, p3.x); std::swap(vTex[1].x, vTex[2].x); std::swap(vTex[1].y, vTex[2].y); std::swap(vColour[1], vColour[2]);}
+
+		olc::vi2d dPos1 = p2 - p1;
+		olc::vf2d dTex1 = vTex[1] - vTex[0];
+		int dcr1 = vColour[1].r - vColour[0].r;
+		int dcg1 = vColour[1].g - vColour[0].g;
+		int dcb1 = vColour[1].b - vColour[0].b;
+		int dca1 = vColour[1].a - vColour[0].a;
+
+		olc::vi2d dPos2 = p3 - p1;
+		olc::vf2d dTex2 = vTex[2] - vTex[0];
+		int dcr2 = vColour[2].r - vColour[0].r;
+		int dcg2 = vColour[2].g - vColour[0].g;
+		int dcb2 = vColour[2].b - vColour[0].b;
+		int dca2 = vColour[2].a - vColour[0].a;
+
+		float dax_step = 0, dbx_step = 0, dcr1_step = 0, dcr2_step = 0,	dcg1_step = 0, dcg2_step = 0, dcb1_step = 0, dcb2_step = 0,	dca1_step = 0, dca2_step = 0;
+		olc::vf2d vTex1Step, vTex2Step;
+
+		if (dPos1.y)
+		{
+			dax_step = dPos1.x / (float)abs(dPos1.y);
+			vTex1Step = dTex1 / (float)abs(dPos1.y);
+			dcr1_step = dcr1 / (float)abs(dPos1.y);
+			dcg1_step = dcg1 / (float)abs(dPos1.y);
+			dcb1_step = dcb1 / (float)abs(dPos1.y);
+			dca1_step = dca1 / (float)abs(dPos1.y);
+		}
+
+		if (dPos2.y)
+		{
+			dbx_step = dPos2.x / (float)abs(dPos2.y);
+			vTex2Step = dTex2 / (float)abs(dPos2.y);
+			dcr2_step = dcr2 / (float)abs(dPos2.y);
+			dcg2_step = dcg2 / (float)abs(dPos2.y);
+			dcb2_step = dcb2 / (float)abs(dPos2.y);
+			dca2_step = dca2 / (float)abs(dPos2.y);
+		}
+
+		olc::vi2d vStart;
+		olc::vi2d vEnd;
+		int vStartIdx;
+
+		for (int pass = 0; pass < 2; pass++)
+		{
+			if (pass == 0)
+			{
+				vStart = p1; vEnd = p2;	vStartIdx = 0;
+			}
+			else
+			{
+				dPos1 = p3 - p2;
+				dTex1 = vTex[2] - vTex[1];
+				dcr1 = vColour[2].r - vColour[1].r;
+				dcg1 = vColour[2].g - vColour[1].g;
+				dcb1 = vColour[2].b - vColour[1].b;
+				dca1 = vColour[2].a - vColour[1].a;
+				dcr1_step = 0; dcg1_step = 0; dcb1_step = 0; dca1_step = 0;
+
+				if (dPos2.y) dbx_step = dPos2.x / (float)abs(dPos2.y);
+				if (dPos1.y)
+				{
+					dax_step = dPos1.x / (float)abs(dPos1.y);
+					vTex1Step = dTex1 / (float)abs(dPos1.y);
+					dcr1_step = dcr1 / (float)abs(dPos1.y);
+					dcg1_step = dcg1 / (float)abs(dPos1.y);
+					dcb1_step = dcb1 / (float)abs(dPos1.y);
+					dca1_step = dca1 / (float)abs(dPos1.y);
+				}
+
+				vStart = p2; vEnd = p3; vStartIdx = 1;
+			}
+
+			if (dPos1.y)
+			{
+				for (int i = vStart.y; i <= vEnd.y; i++)
+				{
+					int ax = int(vStart.x + (float)(i - vStart.y) * dax_step);
+					int bx = int(p1.x + (float)(i - p1.y) * dbx_step);
+
+					olc::vf2d tex_s(vTex[vStartIdx].x + (float)(i - vStart.y) * vTex1Step.x, vTex[vStartIdx].y + (float)(i - vStart.y) * vTex1Step.y);
+					olc::vf2d tex_e(vTex[0].x + (float)(i - p1.y) * vTex2Step.x, vTex[0].y + (float)(i - p1.y) * vTex2Step.y);
+
+					olc::Pixel col_s(vColour[vStartIdx].r + uint8_t((float)(i - vStart.y) * dcr1_step), vColour[vStartIdx].g + uint8_t((float)(i - vStart.y) * dcg1_step),
+						vColour[vStartIdx].b + uint8_t((float)(i - vStart.y) * dcb1_step), vColour[vStartIdx].a + uint8_t((float)(i - vStart.y) * dca1_step));
+
+					olc::Pixel col_e(vColour[0].r + uint8_t((float)(i - p1.y) * dcr2_step), vColour[0].g + uint8_t((float)(i - p1.y) * dcg2_step),
+						vColour[0].b + uint8_t((float)(i - p1.y) * dcb2_step), vColour[0].a + uint8_t((float)(i - p1.y) * dca2_step));
+
+					if (ax > bx) { std::swap(ax, bx); std::swap(tex_s, tex_e); std::swap(col_s, col_e); }
+
+					float tstep = 1.0f / ((float)(bx - ax));
+					float t = 0.0f;
+
+					for (int j = ax; j < bx; j++)
+					{
+						olc::Pixel pixel = PixelLerp(col_s, col_e, t);
+						if (sprTex != nullptr) pixel *= sprTex->Sample(tex_s.lerp(tex_e, t));
+						Draw(j, i, pixel);
+						t += tstep;
+					}
+				}
+			}
+		}			
+	}
+
+	void PixelGameEngine::FillTexturedPolygon(const std::vector<olc::vf2d>& vPoints, const std::vector<olc::vf2d>& vTex, const std::vector<olc::Pixel>& vColour, olc::Sprite* sprTex, olc::DecalStructure structure)
+	{
+		if (structure == olc::DecalStructure::LINE)
+		{
+			return; // Meaningless, so do nothing
+		}
+
+		if (vPoints.size() < 3 || vTex.size() < 3 || vColour.size() < 3)
+			return;
+
+		if (structure == olc::DecalStructure::LIST)
+		{			
+			for (int tri = 0; tri < vPoints.size() / 3; tri++)
+			{
+				std::vector<olc::vf2d> vP = { vPoints[tri * 3 + 0], vPoints[tri * 3 + 1], vPoints[tri * 3 + 2] };
+				std::vector<olc::vf2d> vT = { vTex[tri * 3 + 0], vTex[tri * 3 + 1], vTex[tri * 3 + 2] };
+				std::vector<olc::Pixel> vC = { vColour[tri * 3 + 0], vColour[tri * 3 + 1], vColour[tri * 3 + 2] };
+				FillTexturedTriangle(vP, vT, vC, sprTex);
+			}
+			return;
+		}
+
+		if (structure == olc::DecalStructure::STRIP)
+		{
+			for (int tri = 2; tri < vPoints.size(); tri++)
+			{
+				std::vector<olc::vf2d> vP = { vPoints[tri - 2], vPoints[tri-1], vPoints[tri] };
+				std::vector<olc::vf2d> vT = { vTex[tri - 2], vTex[tri - 1], vTex[tri] };
+				std::vector<olc::Pixel> vC = { vColour[tri - 2], vColour[tri - 1], vColour[tri] };
+				FillTexturedTriangle(vP, vT, vC, sprTex);
+			}
+			return;
+		}
+
+		if (structure == olc::DecalStructure::FAN)
+		{
+			for (int tri = 2; tri < vPoints.size(); tri++)
+			{
+				std::vector<olc::vf2d> vP = { vPoints[0], vPoints[tri - 1], vPoints[tri] };
+				std::vector<olc::vf2d> vT = { vTex[0], vTex[tri - 1], vTex[tri] };
+				std::vector<olc::Pixel> vC = { vColour[0], vColour[tri - 1], vColour[tri] };
+				FillTexturedTriangle(vP, vT, vC, sprTex);
+			}
+			return;
+		}
+	}
+
+
 	void PixelGameEngine::DrawSprite(const olc::vi2d& pos, Sprite* sprite, uint32_t scale, uint8_t flip)
 	{ DrawSprite(pos.x, pos.y, sprite, scale, flip); }
 
@@ -2642,6 +2969,15 @@ namespace olc
 		vLayers[nTargetLayer].vecDecalInstance.push_back(di);
 	}
 
+	void PixelGameEngine::DrawPolygonDecal(olc::Decal* decal, const std::vector<olc::vf2d>& pos, const std::vector<olc::vf2d>& uv, const std::vector<olc::Pixel>& colours, const olc::Pixel tint)
+	{
+		std::vector<olc::Pixel> newColours(colours.size(), olc::WHITE);
+		std::transform(colours.begin(), colours.end(), newColours.begin(),
+			[&tint](const olc::Pixel pin) {	return pin * tint; });
+		DrawPolygonDecal(decal, pos, uv, newColours);
+	}
+
+
 	void PixelGameEngine::DrawPolygonDecal(olc::Decal* decal, const std::vector<olc::vf2d>& pos, const std::vector<float>& depth, const std::vector<olc::vf2d>& uv, const olc::Pixel tint)
 	{
 		DecalInstance di;
@@ -3217,6 +3553,12 @@ namespace olc
 	}
 
 
+	const std::vector<std::string>& PixelGameEngine::GetDroppedFiles() const
+	{ return vDroppedFiles;	}
+
+	const olc::vi2d& PixelGameEngine::GetDroppedFilesPoint() const
+	{ return vDroppedFilesPoint; }
+
 
 	void PixelGameEngine::TextEntryEnable(const bool bEnable, const std::string& sText)
 	{
@@ -3332,13 +3674,9 @@ namespace olc
 	bool PixelGameEngine::OnUserDestroy()
 	{ return true; }
 
-	void PixelGameEngine::GetAnyKey(){};
-	void PixelGameEngine::GetAnyKeyPress(olc::Key pressedKey){};
-
 	void PixelGameEngine::OnTextEntryComplete(const std::string& sText) { UNUSED(sText); }
 	bool PixelGameEngine::OnConsoleCommand(const std::string& sCommand) { UNUSED(sCommand); return false; }
 	
-
 	// Externalised API
 	void PixelGameEngine::olc_UpdateViewport()
 	{
@@ -3404,6 +3742,19 @@ namespace olc
 	void PixelGameEngine::olc_UpdateKeyFocus(bool state)
 	{ bHasInputFocus = state; }
 
+	void PixelGameEngine::olc_DropFiles(int32_t x, int32_t y, const std::vector<std::string>& vFiles)
+	{ 
+		x -= vViewPos.x;
+		y -= vViewPos.y;
+		vDroppedFilesPointCache.x = (int32_t)(((float)x / (float)(vWindowSize.x - (vViewPos.x * 2)) * (float)vScreenSize.x));
+		vDroppedFilesPointCache.y = (int32_t)(((float)y / (float)(vWindowSize.y - (vViewPos.y * 2)) * (float)vScreenSize.y));
+		if (vDroppedFilesPointCache.x >= (int32_t)vScreenSize.x)	vDroppedFilesPointCache.x = vScreenSize.x - 1;
+		if (vDroppedFilesPointCache.y >= (int32_t)vScreenSize.y)	vDroppedFilesPointCache.y = vScreenSize.y - 1;
+		if (vDroppedFilesPointCache.x < 0) vDroppedFilesPointCache.x = 0;
+		if (vDroppedFilesPointCache.y < 0) vDroppedFilesPointCache.y = 0;
+		vDroppedFilesCache = vFiles; 
+	}
+
 	void PixelGameEngine::olc_Reanimate()
 	{ bAtomActive = true; }
 
@@ -3480,10 +3831,8 @@ namespace olc
 		platform->HandleSystemEvent();
 
 		// Compare hardware input states from previous frame
-		auto ScanHardware = [&](HWButton* pKeys, bool* pStateOld, bool* pStateNew, uint32_t nKeyCount,bool keyboard=true)
+		auto ScanHardware = [&](HWButton* pKeys, bool* pStateOld, bool* pStateNew, uint32_t nKeyCount)
 		{
-			bool pressed=false;
-			int key;
 			for (uint32_t i = 0; i < nKeyCount; i++)
 			{
 				pKeys[i].bPressed = false;
@@ -3492,8 +3841,6 @@ namespace olc
 				{
 					if (pStateNew[i])
 					{
-						pressed=true;
-						key=i;
 						pKeys[i].bPressed = !pKeys[i].bHeld;
 						pKeys[i].bHeld = true;
 					}
@@ -3502,35 +3849,35 @@ namespace olc
 						pKeys[i].bReleased = true;
 						pKeys[i].bHeld = false;
 					}
-					GetAnyKey();
 				}
 				pStateOld[i] = pStateNew[i];
 			}
-			if (pressed) {
-				if (keyboard) {GetAnyKeyPress((olc::Key)key);}
-				else {GetAnyKeyPress(olc::Key::NONE);}
-			}
 		};
 
 		ScanHardware(pKeyboardState, pKeyOldState, pKeyNewState, 256);
-		ScanHardware(pMouseState, pMouseOldState, pMouseNewState, nMouseButtons, false);
+		ScanHardware(pMouseState, pMouseOldState, pMouseNewState, nMouseButtons);
 
 		// Cache mouse coordinates so they remain consistent during frame
 		vMousePos = vMousePosCache;
 		nMouseWheelDelta = nMouseWheelDeltaCache;
 		nMouseWheelDeltaCache = 0;
 
+		vDroppedFiles = vDroppedFilesCache;
+		vDroppedFilesPoint = vDroppedFilesPointCache;
+		vDroppedFilesCache.clear();
+
 		if (bTextEntryEnable)
 		{
 			UpdateTextEntry();
 		}
 
 		// Handle Frame Update
-		bool bExtensionBlockFrame = false;
+		bool bExtensionBlockFrame = false;		
 		for (auto& ext : vExtensions) bExtensionBlockFrame |= ext->OnBeforeUserUpdate(fElapsedTime);
 		if (!bExtensionBlockFrame)
 		{
 			if (!OnUserUpdate(fElapsedTime)) bAtomActive = false;
+			
 		}
 		for (auto& ext : vExtensions) ext->OnAfterUserUpdate(fElapsedTime);
 
@@ -3540,6 +3887,8 @@ namespace olc
 			UpdateConsole();
 		}
 
+		
+
 		// Display Frame
 		renderer->UpdateViewport(vViewPos, vViewSize);
 		renderer->ClearBuffer(olc::BLACK, true);
@@ -3698,10 +4047,118 @@ namespace olc
 };
 #pragma endregion 
 
+
+#pragma region platform_headless
+namespace olc
+{
+#if defined(OLC_GFX_HEADLESS)
+	class Renderer_Headless : public olc::Renderer
+	{
+	public:
+		virtual void       PrepareDevice() {};
+		virtual olc::rcode CreateDevice(std::vector<void*> params, bool bFullScreen, bool bVSYNC) { return olc::rcode::OK;		}
+		virtual olc::rcode DestroyDevice() { return olc::rcode::OK; }
+		virtual void       DisplayFrame() {}
+		virtual void       PrepareDrawing() {}
+		virtual void	   SetDecalMode(const olc::DecalMode& mode) {}
+		virtual void       DrawLayerQuad(const olc::vf2d& offset, const olc::vf2d& scale, const olc::Pixel tint) {}
+		virtual void       DrawDecal(const olc::DecalInstance& decal) {}
+		virtual uint32_t   CreateTexture(const uint32_t width, const uint32_t height, const bool filtered = false, const bool clamp = true) {return 1;};
+		virtual void       UpdateTexture(uint32_t id, olc::Sprite* spr) {}
+		virtual void       ReadTexture(uint32_t id, olc::Sprite* spr) {}
+		virtual uint32_t   DeleteTexture(const uint32_t id) {return 1;}
+		virtual void       ApplyTexture(uint32_t id) {}
+		virtual void       UpdateViewport(const olc::vi2d& pos, const olc::vi2d& size) {}
+		virtual void       ClearBuffer(olc::Pixel p, bool bDepth) {}
+	};
+#endif
+#if defined(OLC_PLATFORM_HEADLESS)
+	class Platform_Headless : public olc::Platform
+	{
+	public:
+		virtual olc::rcode ApplicationStartUp() { return olc::rcode::OK; }
+		virtual olc::rcode ApplicationCleanUp() { return olc::rcode::OK; }
+		virtual olc::rcode ThreadStartUp() { return olc::rcode::OK; }
+		virtual olc::rcode ThreadCleanUp() { return olc::rcode::OK; }
+		virtual olc::rcode CreateGraphics(bool bFullScreen, bool bEnableVSYNC, const olc::vi2d& vViewPos, const olc::vi2d& vViewSize) { return olc::rcode::OK; }
+		virtual olc::rcode CreateWindowPane(const olc::vi2d& vWindowPos, olc::vi2d& vWindowSize, bool bFullScreen) { return olc::rcode::OK; }
+		virtual olc::rcode SetWindowTitle(const std::string& s) { return olc::rcode::OK; }
+		virtual olc::rcode StartSystemEventLoop() { return olc::rcode::OK; }
+		virtual olc::rcode HandleSystemEvent() { return olc::rcode::OK; }
+	};
+#endif
+}
+#pragma endregion
+
 // O------------------------------------------------------------------------------O
 // | olcPixelGameEngine Renderers - the draw-y bits                               |
 // O------------------------------------------------------------------------------O
 
+#pragma region image_stb
+// O------------------------------------------------------------------------------O
+// | START IMAGE LOADER: stb_image.h, all systems, very fast                      |
+// O------------------------------------------------------------------------------O
+// Thanks to Sean Barrett - https://github.com/nothings/stb/blob/master/stb_image.h
+// MIT License - Copyright(c) 2017 Sean Barrett
+
+// Note you need to download the above file into your project folder, and
+// #define OLC_IMAGE_STB 
+// #define OLC_PGE_APPLICATION
+// #include "olcPixelGameEngine.h"
+
+#if defined(OLC_IMAGE_STB)
+#define STB_IMAGE_IMPLEMENTATION
+#include "stb_image.h"
+namespace olc
+{
+	class ImageLoader_STB : public olc::ImageLoader
+	{
+	public:
+		ImageLoader_STB() : ImageLoader()
+		{}
+
+		olc::rcode LoadImageResource(olc::Sprite* spr, const std::string& sImageFile, olc::ResourcePack* pack) override
+		{
+			UNUSED(pack);
+			// clear out existing sprite
+			spr->pColData.clear();
+			// Open file
+			stbi_uc* bytes = nullptr;
+			int w = 0, h = 0, cmp = 0;
+			if (pack != nullptr)
+			{
+				ResourceBuffer rb = pack->GetFileBuffer(sImageFile);
+				bytes = stbi_load_from_memory((unsigned char*)rb.vMemory.data(), rb.vMemory.size(), &w, &h, &cmp, 4);
+			}
+			else
+			{
+				// Check file exists
+				if (!_gfs::exists(sImageFile)) return olc::rcode::NO_FILE;
+				bytes = stbi_load(sImageFile.c_str(), &w, &h, &cmp, 4);
+			}
+
+			if (!bytes) return olc::rcode::FAIL;
+			spr->width = w; spr->height = h;
+			spr->pColData.resize(spr->width * spr->height);
+			std::memcpy(spr->pColData.data(), bytes, spr->width * spr->height * 4);
+			delete[] bytes;
+			return olc::rcode::OK;
+		}
+
+		olc::rcode SaveImageResource(olc::Sprite* spr, const std::string& sImageFile) override
+		{
+			return olc::rcode::OK;
+		}
+	};
+}
+#endif
+// O------------------------------------------------------------------------------O
+// | START IMAGE LOADER: stb_image.h                                              |
+// O------------------------------------------------------------------------------O
+#pragma endregion
+
+
+
 #if !defined(OLC_PGE_HEADLESS)
 
 #pragma region renderer_ogl10
@@ -4103,79 +4560,91 @@ namespace olc
 
 #if defined(OLC_PLATFORM_WINAPI)
 	#include <dwmapi.h>
-	#include <gl/GL.h>
+	//#include <gl/GL.h>
 	#if !defined(__MINGW32__)
 		#pragma comment(lib, "Dwmapi.lib")
 	#endif		
-	typedef void __stdcall locSwapInterval_t(GLsizei n);
+	//typedef void __stdcall locSwapInterval_t(GLsizei n);
 	typedef HDC glDeviceContext_t;
 	typedef HGLRC glRenderContext_t;
-	#define CALLSTYLE __stdcall
+	//#define CALLSTYLE __stdcall
 	#define OGL_LOAD(t, n) (t*)wglGetProcAddress(#n)
 #endif
-
-#if defined(__linux__) || defined(__FreeBSD__)
-	#include <GL/gl.h>
-#endif
+//
+//#if defined(__linux__) || defined(__FreeBSD__)
+//	#include <GL/gl.h>
+//#endif
 
 #if defined(OLC_PLATFORM_X11)
-	namespace X11
+	/*namespace X11
 	{
 		#include <GL/glx.h>
 	}
-	typedef int(locSwapInterval_t)(X11::Display* dpy, X11::GLXDrawable drawable, int interval);
+	typedef int(locSwapInterval_t)(X11::Display* dpy, X11::GLXDrawable drawable, int interval);*/
 	typedef X11::GLXContext glDeviceContext_t;
 	typedef X11::GLXContext glRenderContext_t;
-	#define CALLSTYLE 
+	//#define CALLSTYLE 
 	#define OGL_LOAD(t, n) (t*)glXGetProcAddress((unsigned char*)#n);
 #endif
 
-#if defined(__APPLE__)
-	#define GL_SILENCE_DEPRECATION
-	#include <OpenGL/OpenGL.h>
-	#include <OpenGL/gl.h>
-	#include <OpenGL/glu.h>
-#endif
-
-#if defined(OLC_PLATFORM_EMSCRIPTEN)
-	#include <EGL/egl.h>
-	#include <GLES2/gl2.h>
-	#define GL_GLEXT_PROTOTYPES
-	#include <GLES2/gl2ext.h>
-	#include <emscripten/emscripten.h>
-	#define CALLSTYLE
-	typedef EGLBoolean(locSwapInterval_t)(EGLDisplay display, EGLint interval);
-	#define GL_CLAMP GL_CLAMP_TO_EDGE
-	#define OGL_LOAD(t, n) n;
-#endif
+//#if defined(__APPLE__)
+//	#define GL_SILENCE_DEPRECATION
+//	#include <OpenGL/OpenGL.h>
+//	#include <OpenGL/gl.h>
+//	#include <OpenGL/glu.h>
+//#endif
+
+//#if defined(OLC_PLATFORM_EMSCRIPTEN)
+//	#include <EGL/egl.h>
+//	#include <GLES2/gl2.h>
+//	#define GL_GLEXT_PROTOTYPES
+//	#include <GLES2/gl2ext.h>
+//	#include <emscripten/emscripten.h>
+//	#define CALLSTYLE
+//	typedef EGLBoolean(locSwapInterval_t)(EGLDisplay display, EGLint interval);
+//	#define GL_CLAMP GL_CLAMP_TO_EDGE
+//	#define OGL_LOAD(t, n) n;
+//#endif
 
 namespace olc
 {
-	typedef char GLchar;
-	typedef ptrdiff_t GLsizeiptr;
-	typedef GLuint CALLSTYLE locCreateShader_t(GLenum type);
-	typedef GLuint CALLSTYLE locCreateProgram_t(void);
-	typedef void CALLSTYLE locDeleteShader_t(GLuint shader);
-#if defined(OLC_PLATFORM_EMSCRIPTEN)
-	typedef void CALLSTYLE locShaderSource_t(GLuint shader, GLsizei count, const GLchar* const* string, const GLint* length);
-#else
-	typedef void CALLSTYLE locShaderSource_t(GLuint shader, GLsizei count, const GLchar** string, const GLint* length);
-#endif
-	typedef void CALLSTYLE locCompileShader_t(GLuint shader);
-	typedef void CALLSTYLE locLinkProgram_t(GLuint program);
-	typedef void CALLSTYLE locDeleteProgram_t(GLuint program);
-	typedef void CALLSTYLE locAttachShader_t(GLuint program, GLuint shader);
-	typedef void CALLSTYLE locBindBuffer_t(GLenum target, GLuint buffer);
-	typedef void CALLSTYLE locBufferData_t(GLenum target, GLsizeiptr size, const void* data, GLenum usage);
-	typedef void CALLSTYLE locGenBuffers_t(GLsizei n, GLuint* buffers);
-	typedef void CALLSTYLE locVertexAttribPointer_t(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void* pointer);
-	typedef void CALLSTYLE locEnableVertexAttribArray_t(GLuint index);
-	typedef void CALLSTYLE locUseProgram_t(GLuint program);
-	typedef void CALLSTYLE locBindVertexArray_t(GLuint array);
-	typedef void CALLSTYLE locGenVertexArrays_t(GLsizei n, GLuint* arrays);
-	typedef void CALLSTYLE locGetShaderInfoLog_t(GLuint shader, GLsizei bufSize, GLsizei* length, GLchar* infoLog);
+//	typedef char GLchar;
+//	typedef ptrdiff_t GLsizeiptr;
+//	typedef GLuint CALLSTYLE locCreateShader_t(GLenum type);
+//	typedef GLuint CALLSTYLE locCreateProgram_t(void);
+//	typedef void CALLSTYLE locDeleteShader_t(GLuint shader);
+//#if defined(OLC_PLATFORM_EMSCRIPTEN)
+//	typedef void CALLSTYLE locShaderSource_t(GLuint shader, GLsizei count, const GLchar* const* string, const GLint* length);
+//#else
+//	typedef void CALLSTYLE locShaderSource_t(GLuint shader, GLsizei count, const GLchar** string, const GLint* length);
+//#endif
+//	typedef void CALLSTYLE locCompileShader_t(GLuint shader);
+//	typedef void CALLSTYLE locLinkProgram_t(GLuint program);
+//	typedef void CALLSTYLE locDeleteProgram_t(GLuint program);
+//	typedef void CALLSTYLE locAttachShader_t(GLuint program, GLuint shader);
+//	typedef void CALLSTYLE locBindBuffer_t(GLenum target, GLuint buffer);
+//	typedef void CALLSTYLE locBufferData_t(GLenum target, GLsizeiptr size, const void* data, GLenum usage);
+//	typedef void CALLSTYLE locGenBuffers_t(GLsizei n, GLuint* buffers);
+//	typedef void CALLSTYLE locVertexAttribPointer_t(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void* pointer);
+//	typedef void CALLSTYLE locEnableVertexAttribArray_t(GLuint index);
+//	typedef void CALLSTYLE locUseProgram_t(GLuint program);
+//	typedef void CALLSTYLE locBindVertexArray_t(GLuint array);
+//	typedef void CALLSTYLE locGenVertexArrays_t(GLsizei n, GLuint* arrays);
+//	typedef void CALLSTYLE locGetShaderInfoLog_t(GLuint shader, GLsizei bufSize, GLsizei* length, GLchar* infoLog);
+//	typedef GLint CALLSTYLE locGetUniformLocation_t(GLuint program, const GLchar* name);
+//	typedef void CALLSTYLE locUniform1f_t(GLint location, GLfloat v0);
+//	typedef void CALLSTYLE locUniform1i_t(GLint location, GLint v0);
+//	typedef void CALLSTYLE locUniform2fv_t(GLint location, GLsizei count, const GLfloat* value);
+//	typedef void CALLSTYLE locActiveTexture_t(GLenum texture);
+//	typedef void CALLSTYLE locGenFrameBuffers_t(GLsizei n, GLuint* ids);
+//	typedef void CALLSTYLE locBindFrameBuffer_t(GLenum target, GLuint fb);
+//	typedef GLenum CALLSTYLE locCheckFrameBufferStatus_t(GLenum target);
+//	typedef void CALLSTYLE locDeleteFrameBuffers_t(GLsizei n, const GLuint* fbs);
+//	typedef void CALLSTYLE locFrameBufferTexture2D_t(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level);
+//	typedef void CALLSTYLE locDrawBuffers_t(GLsizei n, const GLenum* bufs);
+//	typedef void CALLSTYLE locBlendFuncSeparate_t(GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha);
 
-	constexpr size_t OLC_MAX_VERTS = 128;
+	
 
 	class Renderer_OGL33 : public olc::Renderer
 	{
@@ -4280,7 +4749,7 @@ namespace olc
 			wglMakeCurrent(glDeviceContext, glRenderContext);
 
 			// Set Vertical Sync
-			locSwapInterval = OGL_LOAD(locSwapInterval_t, "wglSwapIntervalEXT");
+			locSwapInterval = OGL_LOAD(locSwapInterval_t, wglSwapIntervalEXT);
 			if (locSwapInterval && !bVSYNC) locSwapInterval(0);
 			bSync = bVSYNC;
 #endif
@@ -4300,7 +4769,7 @@ namespace olc
 			XGetWindowAttributes(olc_Display, *olc_Window, &gwa);
 			glViewport(0, 0, gwa.width, gwa.height);
 
-			locSwapInterval = OGL_LOAD(locSwapInterval_t, "glXSwapIntervalEXT");
+			locSwapInterval = OGL_LOAD(locSwapInterval_t, glXSwapIntervalEXT);
 
 			if (locSwapInterval == nullptr && !bVSYNC)
 			{
@@ -4537,7 +5006,14 @@ namespace olc
 			if (nDecalMode == DecalMode::WIREFRAME)
 				glDrawArrays(GL_LINE_LOOP, 0, decal.points);
 			else
-				glDrawArrays(GL_TRIANGLE_FAN, 0, decal.points);
+			{
+				if (decal.structure == olc::DecalStructure::FAN)
+					glDrawArrays(GL_TRIANGLE_FAN, 0, decal.points);
+				else if (decal.structure == olc::DecalStructure::STRIP)
+					glDrawArrays(GL_TRIANGLE_STRIP, 0, decal.points);
+				else if (decal.structure == olc::DecalStructure::LIST)
+					glDrawArrays(GL_TRIANGLES, 0, decal.points);
+			}
 		}
 
 		uint32_t CreateTexture(const uint32_t width, const uint32_t height, const bool filtered, const bool clamp) override
@@ -4865,68 +5341,6 @@ namespace olc
 // O------------------------------------------------------------------------------O
 #pragma endregion
 
-#pragma region image_stb
-// O------------------------------------------------------------------------------O
-// | START IMAGE LOADER: stb_image.h, all systems, very fast                      |
-// O------------------------------------------------------------------------------O
-// Thanks to Sean Barrett - https://github.com/nothings/stb/blob/master/stb_image.h
-// MIT License - Copyright(c) 2017 Sean Barrett
-
-// Note you need to download the above file into your project folder, and
-// #define OLC_IMAGE_STB 
-// #define OLC_PGE_APPLICATION
-// #include "olcPixelGameEngine.h"
-
-#if defined(OLC_IMAGE_STB)
-#define STB_IMAGE_IMPLEMENTATION
-#include "stb_image.h"
-namespace olc
-{
-	class ImageLoader_STB : public olc::ImageLoader
-	{
-	public:
-		ImageLoader_STB() : ImageLoader()
-		{}
-
-		olc::rcode LoadImageResource(olc::Sprite* spr, const std::string& sImageFile, olc::ResourcePack* pack) override
-		{
-			UNUSED(pack);
-			// clear out existing sprite
-			spr->pColData.clear();
-			// Open file
-			stbi_uc* bytes = nullptr;
-			int w = 0, h = 0, cmp = 0;
-			if (pack != nullptr)
-			{
-				ResourceBuffer rb = pack->GetFileBuffer(sImageFile);
-				bytes = stbi_load_from_memory((unsigned char*)rb.vMemory.data(), rb.vMemory.size(), &w, &h, &cmp, 4);
-			}
-			else
-			{
-				// Check file exists
-				if (!_gfs::exists(sImageFile)) return olc::rcode::NO_FILE;
-				bytes = stbi_load(sImageFile.c_str(), &w, &h, &cmp, 4);
-			}
-
-			if (!bytes) return olc::rcode::FAIL;
-			spr->width = w; spr->height = h;
-			spr->pColData.resize(spr->width * spr->height);
-			std::memcpy(spr->pColData.data(), bytes, spr->width * spr->height * 4);
-			delete[] bytes;
-			return olc::rcode::OK;
-		}
-
-		olc::rcode SaveImageResource(olc::Sprite* spr, const std::string& sImageFile) override
-		{
-			return olc::rcode::OK;
-		}
-	};
-}
-#endif
-// O------------------------------------------------------------------------------O
-// | START IMAGE LOADER: stb_image.h                                              |
-// O------------------------------------------------------------------------------O
-#pragma endregion
 
 // O------------------------------------------------------------------------------O
 // | olcPixelGameEngine Platforms                                                 |
@@ -4968,6 +5382,8 @@ namespace olc
 			return w;
 		}
 
+
+
 	public:
 		virtual olc::rcode ApplicationStartUp() override { return olc::rcode::OK; }
 		virtual olc::rcode ApplicationCleanUp() override { return olc::rcode::OK; }
@@ -5034,6 +5450,8 @@ namespace olc
 			olc_hWnd = CreateWindowEx(dwExStyle, olcT("OLC_PIXEL_GAME_ENGINE"), olcT(""), dwStyle,
 				vTopLeft.x, vTopLeft.y, width, height, NULL, NULL, GetModuleHandle(nullptr), this);
 
+			DragAcceptFiles(olc_hWnd, true);
+
 			// 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;
@@ -5131,6 +5549,44 @@ namespace olc
 			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_DROPFILES:
+			{
+				// This is all eww...
+				HDROP drop = (HDROP)wParam;
+				
+				uint32_t nFiles = DragQueryFile(drop, 0xFFFFFFFF, nullptr, 0);
+				std::vector<std::string> vFiles;
+				for (uint32_t i = 0; i < nFiles; i++)
+				{
+					TCHAR dfbuffer[256]{};
+					uint32_t len = DragQueryFile(drop, i, nullptr, 0);
+					DragQueryFile(drop, i, dfbuffer, 256);
+#ifdef UNICODE
+	#ifdef __MINGW32__
+					char* buffer = new char[len + 1];
+					wcstombs(buffer, dfbuffer, len);
+					buffer[len] = '\0';
+	#else
+					int count = WideCharToMultiByte(CP_UTF8, 0, dfbuffer, -1, NULL, 0, NULL, NULL);
+					char* buffer = new char[count];
+					WideCharToMultiByte(CP_UTF8, 0, dfbuffer, -1, buffer, count, NULL, NULL);
+	#endif				
+					vFiles.push_back(std::string(buffer));
+					delete[] buffer;
+#else
+					vFiles.push_back(std::string(dbuffer));
+#endif
+				}
+
+				// Even more eww...
+				POINT p; DragQueryPoint(drop, &p);
+				ptrPGE->olc_DropFiles(p.x, p.y, vFiles);
+				DragFinish(drop);
+				return 0;
+			} 
+			break;
+
+
 			case WM_CLOSE:		ptrPGE->olc_Terminate();                                                return 0;
 			case WM_DESTROY:	PostQuitMessage(0); DestroyWindow(hWnd);								return 0;
 			}
@@ -5700,6 +6156,9 @@ namespace olc {
 #pragma endregion 
 
 
+
+
+
 #pragma region platform_emscripten
 // O------------------------------------------------------------------------------O
 // | START PLATFORM: Emscripten - Totally Game Changing...                        |
@@ -5836,118 +6295,100 @@ namespace olc
 			//	the giant web baby.
 			
 
-			// Fullscreen and Resize Observers
 			EM_ASM({
 
-			// cache for reuse
-			Module._olc_EmscriptenShellCss = "width: 100%; height: 70vh; margin-left: auto; margin-right: auto;";
+			// olc_ApsectRatio
+			// 
+			// Used by olc_ResizeHandler to calculate the viewport from the
+			// dimensions of the canvas container's element.
+			Module.olc_AspectRatio = $0 / $1;
+
+			// HACK ALERT!
+			// 
+			// Here we assume any html shell that uses 3 or more instance of the class "emscripten"
+			// is using one of the default or minimal emscripten page layouts
+			Module.olc_AssumeDefaultShells = (document.querySelectorAll('.emscripten').length >= 3) ? true : false;
+
+			// olc_ResizeHandler
+			// 
+			// Used by olc_Init, and is called when a resize observer and fullscreenchange event is triggered.
+			var olc_ResizeHandler = function()
+			{
+				// are we in fullscreen mode?
+				let isFullscreen = (document.fullscreenElement != null);
+
+				// get the width of the containing element
+				let width  = (isFullscreen || !Module.olc_AssumeDefaultShells) ? window.innerWidth  : Module.canvas.parentNode.clientWidth;
+				let height = (isFullscreen || !Module.olc_AssumeDefaultShells) ? window.innerHeight : Module.canvas.parentNode.clientHeight;
 
-			// width / height = aspect ratio
-			Module._olc_WindowAspectRatio = $0 / $1;
-			Module.canvas.parentNode.addEventListener("resize", function(e) {
-				
-				if (e.defaultPrevented) { e.stopPropagation(); 	return;	}
-				var viewWidth = e.detail.width;
-				var viewHeight = e.detail.width / Module._olc_WindowAspectRatio;
-				if (viewHeight > e.detail.height)
+				// calculate the expected viewport size
+				let viewWidth  = width;
+				let viewHeight = width / Module.olc_AspectRatio;
+
+				// if we're taller than the containing element, recalculate based on height
+				if(viewHeight > height)
 				{
-					viewHeight = e.detail.height;
-					viewWidth = e.detail.height * Module._olc_WindowAspectRatio;
-				}
-					
-				if (Module.canvas.parentNode.className == 'emscripten_border')
-					Module.canvas.parentNode.style.cssText = Module._olc_EmscriptenShellCss + " width: " + viewWidth.toString() + "px; height: " + viewHeight.toString() + "px;";
-
-				Module.canvas.setAttribute("width", viewWidth);
-				Module.canvas.setAttribute("height", viewHeight);
-
-				if (document.fullscreenElement != null)
-				{						
-					var top = (e.detail.height - viewHeight) / 2;
-					var left = (e.detail.width - viewWidth) / 2;
-					Module.canvas.style.position = "fixed";
-					Module.canvas.style.top = top.toString() + "px";
-					Module.canvas.style.left = left.toString() + "px";
-					Module.canvas.style.width = "";
-					Module.canvas.style.height = "";
+					viewWidth  = height * Module.olc_AspectRatio;
+					viewHeight = height;
 				}
+    
+				// ensure resulting viewport is in integer space
+				viewWidth  = parseInt(viewWidth);
+				viewHeight = parseInt(viewHeight);
 
-				// trigger PGE update
-				Module._olc_PGE_UpdateWindowSize(viewWidth, viewHeight);
-				// this is really only needed when enter/exiting fullscreen
-				Module.canvas.focus();
-				// prevent this event from ever affecting the document beyond this element
-				e.stopPropagation();
-			});
-
-			// helper function to prevent repeating the same code everywhere
-			Module._olc_ResizeCanvas = function()
-			{
-				// yes, we still have to wait, sigh..
 				setTimeout(function()
 				{
-					// if default template, stretch width as well
-					if (Module.canvas.parentNode.className == 'emscripten_border')
-						Module.canvas.parentNode.style.cssText = Module._olc_EmscriptenShellCss;
-
-					// override it's styling so we can get it's stretched size
-					Module.canvas.style.cssText = "width: 100%; height: 100%; outline: none;";
-
-					// setup custom resize event
-					var resizeEvent = new CustomEvent('resize', 
-					{
-						detail: {
-							width: Module.canvas.clientWidth,
-							height : Module.canvas.clientHeight
-						},
-						bubbles : true,
-						cancelable : true
-					});
-
-					// trigger custom resize event on canvas element
-					Module.canvas.dispatchEvent(resizeEvent);
-				}, 50);
+					// if default shells, apply default styles
+					if(Module.olc_AssumeDefaultShells)
+						Module.canvas.parentNode.setAttribute('style', 'width: 100%; height: 70vh; margin-left: auto; margin-right: auto;');
+				
+					// apply viewport dimensions to teh canvas
+					Module.canvas.setAttribute('width', viewWidth);
+					Module.canvas.setAttribute('height', viewHeight);
+					Module.canvas.setAttribute('style', `width: ${viewWidth}px; height: ${viewHeight}px;`);
+					
+					// update the PGE window size
+					Module._olc_PGE_UpdateWindowSize(viewWidth, viewHeight);
+					
+					// force focus on our PGE canvas
+					Module.canvas.focus();
+				}, 200);
 			};
 
-
-			// Disable Refresh Gesture on mobile
-			document.body.style.cssText += " overscroll-behavior-y: contain;";
-
-			if (Module.canvas.parentNode.className == 'emscripten_border')
+			
+			// olc_Init
+			// 
+			// set up resize observer and fullscreenchange event handler
+			var olc_Init = function()
 			{
-				// force body to have no margin in emscripten's minimal shell
-				document.body.style.margin = "0";
-				Module.canvas.parentNode.style.cssText = Module._olc_EmscriptenShellCss;
-			}
-
-			Module._olc_ResizeCanvas();
+				if(Module.olc_AspectRatio === undefined)
+				{
+					setTimeout(function() { Module.olc_Init(); }, 50);
+					return;
+				}
+					
+				let resizeObserver = new ResizeObserver(function(entries)
+				{
+					Module.olc_ResizeHandler();
+				}).observe(Module.canvas.parentNode);
 
-			// observe and react to resizing of the container element
-			var resizeObserver = new ResizeObserver(function(entries) {Module._olc_ResizeCanvas();}).observe(Module.canvas.parentNode);
+				let mutationObserver = new MutationObserver(function(mutationsList, observer)
+				{
+					setTimeout(function() { Module.olc_ResizeHandler(); },  200);
+				}).observe(Module.canvas.parentNode, { attributes: false, childList: true, subtree: false });
 
-			// observe and react to changes that occur when entering/exiting fullscreen
-			var mutationObserver = new MutationObserver(function(mutationsList, observer)
-			{
-				// a change has occurred, let's check them out!
-				for (var i = 0; i < mutationsList.length; i++)
+				window.addEventListener('fullscreenchange', function(e)
 				{
-					// cycle through all of the newly added elements
-					for (var j = 0; j < mutationsList[i].addedNodes.length; j++)
-					{
-						// if this element is a our canvas, trigger resize
-						if (mutationsList[i].addedNodes[j].id == 'canvas')
-							Module._olc_ResizeCanvas();
-					}
-				}
-			}).observe(Module.canvas.parentNode, 
-			{
-				attributes: false,
-				childList : true,
-				subtree : false
-			});
+					setTimeout(function() { Module.olc_ResizeHandler();},  200);
+				});
+			};
+			
+			// set up hooks
+			Module.olc_ResizeHandler = (Module.olc_ResizeHandler != undefined) ? Module.olc_ResizeHandler : olc_ResizeHandler;
+			Module.olc_Init          = (Module.olc_Init          != undefined) ? Module.olc_Init : olc_Init;
 
-			// add resize listener on window
-			window.addEventListener("resize", function(e) { Module._olc_ResizeCanvas(); });
+			// run everything!
+			Module.olc_Init();
 
 			}, vWindowSize.x, vWindowSize.y); // Fullscreen and Resize Observers
 #pragma warning restore format
@@ -6157,7 +6598,9 @@ namespace olc
 	void PixelGameEngine::olc_ConfigureSystem()
 	{
 
-#if !defined(OLC_PGE_HEADLESS)
+//#if !defined(OLC_PGE_HEADLESS)
+
+		olc::Sprite::loader = nullptr;
 
 #if defined(OLC_IMAGE_GDI)
 		olc::Sprite::loader = std::make_unique<olc::ImageLoader_GDIPlus>();
@@ -6176,7 +6619,9 @@ namespace olc
 #endif
 
 
-
+#if defined(OLC_PLATFORM_HEADLESS)
+		platform = std::make_unique<olc::Platform_Headless>();
+#endif
 
 #if defined(OLC_PLATFORM_WINAPI)
 		platform = std::make_unique<olc::Platform_Windows>();
@@ -6198,7 +6643,9 @@ namespace olc
 		platform = std::make_unique<OLC_PLATFORM_CUSTOM_EX>();
 #endif
 
-
+#if defined(OLC_GFX_HEADLESS)
+		renderer = std::make_unique<olc::Renderer_Headless>();
+#endif
 
 #if defined(OLC_GFX_OPENGL10)
 		renderer = std::make_unique<olc::Renderer_OGL10>();
@@ -6227,11 +6674,11 @@ namespace olc
 		// Associate components with PGE instance
 		platform->ptrPGE = this;
 		renderer->ptrPGE = this;
-#else
-		olc::Sprite::loader = nullptr;
-		platform = nullptr;
-		renderer = nullptr;
-#endif
+//#else
+//		olc::Sprite::loader = nullptr;
+//		platform = nullptr;
+//		renderer = nullptr;
+//#endif
 	}
 }