diff --git a/dev/msvc/olcPGE3.vcxproj b/dev/msvc/olcPGE3.vcxproj index 2f4484c7..75d91f2a 100644 --- a/dev/msvc/olcPGE3.vcxproj +++ b/dev/msvc/olcPGE3.vcxproj @@ -187,7 +187,13 @@ + + + + + + @@ -200,9 +206,13 @@ + + + + true @@ -210,19 +220,22 @@ true true - + + true + true + false false - false - false + true + true false false - false - false + true + true diff --git a/dev/msvc/olcPGE3.vcxproj.filters b/dev/msvc/olcPGE3.vcxproj.filters index 21f7cc2b..08ebcaab 100644 --- a/dev/msvc/olcPGE3.vcxproj.filters +++ b/dev/msvc/olcPGE3.vcxproj.filters @@ -81,6 +81,24 @@ Header Files + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + @@ -116,5 +134,17 @@ Source Files + + Source Files + + + Source Files + + + Header Files + + + Source Files + \ No newline at end of file diff --git a/dev/msvc/olcPGE3_BuildSH/olcPixelGameEngine3.h b/dev/msvc/olcPGE3_BuildSH/olcPixelGameEngine3.h index 6ac0a1a4..6b5e2868 100644 --- a/dev/msvc/olcPGE3_BuildSH/olcPixelGameEngine3.h +++ b/dev/msvc/olcPGE3_BuildSH/olcPixelGameEngine3.h @@ -21,6 +21,9 @@ #include #include #include +#include +#include +#include // Version number (accessible as value for incremental comparisons) #define PGE_VER 300 @@ -32,7 +35,7 @@ #define PGE_PIXEL_LAYOUT_ABGR 2 #if !defined(PGE_PIXEL_LAYOUT) - #define PGE_PIXEL_LAYOUT PGE_PIXEL_LAYOUT_ABGR + #define PGE_PIXEL_LAYOUT PGE_PIXEL_LAYOUT_RGBA #endif @@ -91,6 +94,21 @@ #define OLC_GPU OLC_GPU_OPENGL33 #endif + + +#define OLC_IMAGELOADER_NONE 1 +#define OLC_IMAGELOADER_WINGDI 2 + +#if !defined(OLC_IMAGELOADER) + #define OLC_IMAGELOADER OLC_IMAGELOADER_WINGDI +#endif + + +#define OLC_MOUSE_BUTTONS 3 + +#define OLC_GPU_MAX_VERTICES 8192 +#define OLC_GPU_ERRORCHECK 0 + #define LICENCE_DEFAULT "OneLoneCoder.com - Pixel Game Engine 3 - " @@ -893,6 +911,18 @@ namespace olc return out; } + + // Transform a vector of v_2d by this matrix + template + inline constexpr auto transform(const std::vector>& v) + { + std::vector> o(v.size()); + std::transform(v.begin(), v.end(), o.begin(), [this](const olc::v_2d& i) {return (*this) * i; }); + return o; + } + + + // Return this matrix as a std::string, of the form "[c1r1, c2r1, c3r1]\n[c1r2, c2r2, c3r2]\n[c1r3, c2r3, c3r3]" inline std::string str() const { @@ -1088,6 +1118,13 @@ namespace olc update(); } + template + inline constexpr auto operator * (const olc::t_2d& rhs) const + { + auto& me = *this; + return me.forward_matrix() * rhs.forward_matrix(); + } + protected: // Constructs resultant matrices when transformation changes inline constexpr void update() @@ -1142,129 +1179,72 @@ namespace olc const olc::vi2d& Size() const; // Returns read/write pointer to start of 1D stream of pixel data olc::Pixel* Data(); + // [UNSAFE] Returns pixel at location + olc::Pixel& Pixel(const olc::vf2d& pos); // Returns how this image was configured upon creation const ImageConfig& GetConfig() const; // Return GPU Resource ID int32_t GetGPUID() const; // Set GPU Resource ID (0 to eliminate) void SetGPUID(const int32_t id); + // Get underlying vector of pixels + std::vector& GetPixels(); - // True if data mirror has in-front RAM image - bool HasWetCPU() const; - // True if data mirror has in-front VRAM image - bool HasWetGPU() const; + bool BoundToGPU() const; + bool BoundToCPU() const; - void PrimeCPU(); - void PrimeGPU(); + public: // Make friendly private later + void BindGPU(); + void BindCPU(); protected: ImageConfig config; olc::vi2d dimensions; std::vector pixels; int32_t gpuResourceID = 0; - bool gpuWet = false; - bool cpuWet = true; + bool onGPU = false; + bool onCPU = true; }; } #define PGE_IMAGE_DECLARED 1 #endif -#if !defined(PGE_WINDOW_DECLARED) - -#if OLC_HOST == OLC_HOST_WINDOWS - #define FRIENDLY_HOST Host_Windows_WinAPI -#endif - +#if !defined(PGE_IMAGELOADER_IFACE_DECLARED) namespace olc { - namespace host - { - class FRIENDLY_HOST; - class Host; - } - - namespace gpu - { - class Renderer; - } - - class Window + namespace imload { - friend class olc::host::FRIENDLY_HOST; - - public: - Window(); - virtual ~Window(); - - void ConnecToHost(olc::host::Host* host); - - public: - // Return true if window is to continue - virtual bool OnUserCreate(); - // Return true if window is to continue - virtual bool OnUserUpdate(float fElapsedTime); - // Return true if window is to close - virtual bool OnUserDestroy(); - - private: - // Set Mouse Device State - bool olc_OnMouseButton(const uint8_t nButton, const bool bPressed); - bool olc_OnMouseMove(const olc::vi2d& vMousePos); - bool olc_OnMouseWheel(const int32_t nScroll); - bool olc_OnMouseFocus(const bool bHasFocus); - - // Set Window State - bool olc_OnWindowPosition(const olc::vi2d& vWindowPos); - bool olc_OnWindowSize(const olc::vi2d& vWindowSize); - - // Set Keyboard State - - - - public: - bool olc_ShouldRemove() const; - bool olc_WindowUpdate(const float fElapsedTime, olc::gpu::Renderer* const gpu); - - public: - size_t GetUID() const; - - const olc::vi2d& GetSize() const; - bool SetSize(const olc::vi2d& vSize); - - const olc::vi2d& GetPosition() const; - bool SetPosition(const olc::vi2d& vPosition); - - const std::string& GetTitle() const; - bool SetTitle(const std::string& sTitle); - + class ImageLoader + { + public: + ImageLoader() = default; + virtual ~ImageLoader() = default; - protected: - virtual bool olc_PrimaryWindowInit(); + // Create an image resource based on an image file asset on disk + virtual bool CreateImageFromFile(olc::Image& image, const std::string& sFileName) = 0; + + // Create an image resource based on an image file asset in memory + virtual bool CreateImageFromMemory(olc::Image& image, const uint8_t* data, const size_t bytes) = 0; - protected: - bool bShouldRemove = false; + // Create an image resource based on an image file asset in memory + virtual bool CreateImageFromMemory(olc::Image& image, const std::vector& data) = 0; + + // Store an image as a file asset on disk + virtual bool WriteImageToFile(const olc::Image& image, const std::string& sFileName) = 0; + + // Store an image as a file asset in memory + virtual bool WriteImageToMemoryFile(olc::Image& image, const std::vector& data) = 0; - protected: - size_t nUniqueID = -1; - olc::vi2d vWindowPos; - olc::vi2d vWindowSize; - std::string sFrameTitle; - - private: - olc::vi2d volatile_vMousePos; - - olc::host::Host* pHost = nullptr; - //olc::gpu::Renderer* pRenderer = nullptr; - }; + }; + } } -#define PGE_WINDOW_DECLARED 1 +#define PGE_IMAGELOADER_IFACE_DECLARED 1 #endif #if !defined(PGE_GPUTASK_DECLARED) namespace olc { - namespace pgeguts - { + // This is the default "packet" of work that is sent to // a GPU for drawing. Various drawing operations throughout // PGE create GPUTasks which are stored and dispatched when @@ -1273,10 +1253,19 @@ namespace olc // upon PGE structures struct GPUTask { + enum class Task : uint8_t + { + DrawPolygon, + } task = Task::DrawPolygon; + struct Vertex { - float p[6]; // x, y, z, w, u, v + float p[4]; // x, y, z, w olc::Pixel c; // 32-bit colour + float t0[2]; + float t1[2]; + float t2[2]; + float t3[2]; }; // Simple vertex buffer @@ -1299,6 +1288,8 @@ namespace olc // Overall biasing colour (great for blends) olc::Pixel tint = olc::Colour::WHITE; + olc::Image* pImage = nullptr; + // Define super structure to be drawn enum class Structure : uint8_t { @@ -1361,7 +1352,7 @@ namespace olc // return task; //} - } + } #define PGE_GPUTASK_DECLARED 1 #endif @@ -1386,6 +1377,33 @@ namespace olc FailedToSwitchRenderContext, }; + class Shader + { + public: + virtual ~Shader(); + + public: + void SetPixelShaderSource(const std::string& src); + void SetVertexShaderSource(const std::string& src); + void SetGeometryShaderSource(const std::string& src); + + virtual std::string Compile() = 0; + virtual int32_t CreateUniform(const std::string& name) = 0; + + int32_t GetUniform(const std::string& name) const; + int32_t GetShaderID() const; + + protected: + std::string srcPixelShader; + std::string srcVertexShader; + std::string srcGeometryShader; + int32_t nPixelShaderID; + int32_t nVertexShaderID; + int32_t nGeometryShaderID; + int32_t nCompiledShaderID; + std::unordered_map mapUniforms; + }; + class Renderer { public: @@ -1397,6 +1415,8 @@ namespace olc inline RendererError GetLastError() const { return lastError; } inline const RendererConfig& GetConfig() const { return config; } + + public: // Device Stuff // Constructs a GPU Device interface virtual bool CreateDevice(std::vector params, const RendererConfig& cfg) = 0; @@ -1419,8 +1439,11 @@ namespace olc public: // Shader Construction Stuff + virtual bool ApplyShader(const Shader& shader) = 0; + virtual bool ApplyDefaultShader() = 0; + public: // GPU Task Processing Stuff - virtual bool DoGPUTask(const olc::pgeguts::GPUTask& task) = 0; + virtual bool DoGPUTask(const olc::GPUTask& task) = 0; public: // Swap Chain Stuff // Clears the viewport to a specific colour and depth @@ -1441,109 +1464,381 @@ namespace olc } #define PGE_RENDERER_IFACE_DECLARED 1 #endif - -#if !defined(PGE_HOST_IFACE_DECLARED) + +#if !defined(PGE_DRAW2D_DECLARED) namespace olc { - namespace pgeguts + namespace gpu { - inline static size_t uuid = 0; - - inline constexpr size_t CreateUID() - { - return uuid++; - } + class Renderer; } + - namespace host + class Draw2D { + public: + Draw2D(); + + // Associate this drawing toolbox with a renderer + void SetGPU(olc::gpu::Renderer* const renderer); + + public: + // Sets the drawing target of this drawing toolbox + void SetTarget(olc::Image& image); + + // Clears transform stack and sets identity transform + void ClearTransform(); + + void SetTransform(const olc::tf2d& trans); + + olc::tf2d& GetTransform(); + void ProcessGPUTasks(); + + protected: + // Checks residency of image resource, and brings it to cpu RAM for r/w + void PrepareTargetForSW(); + // Checks residency of image resource, and brings it to gpu VRAM for r/w + void PrepareTargetForHW(); + + // Checks residency of image resource, and brings it to cpu RAM for r/w + void PrepareImageForSW(olc::Image& image); + // Checks residency of image resource, and brings it to gpu VRAM for r/w + void PrepareImageForHW(olc::Image& image); + + olc::Image* pTarget = nullptr; + olc::gpu::Renderer* pRenderer = nullptr; + olc::tf2d transformTarget; + olc::tf2d transformAffine; + olc::mf3d transformCombined; + + std::vector vecGPUTasks; + + public: // Affine Transformation (these affect all subsequent draw calls for this target) + void AffineReset(); + void AffineScale(const olc::vf2d& vScale); + void AffineOffset(const olc::vf2d& vOffset); + void AffineRotate(const float& fTheta, const olc::vf2d& vPoint = { 0,0 }); + + public: + // Plot a single pixel + void Pixel(const olc::vf2d& pos, const olc::Pixel col = olc::Colour::WHITE); + // Read a pixel from an image (guarantees fresh) + olc::Pixel GetPixel(olc::Image& image, const olc::vf2d& pos); + + public: + // Draws a single pixel wide line + GPUTask Line(const olc::vf2d& p1, const olc::vf2d& p2, const olc::Pixel col = olc::Colour::WHITE); + // Draws a single pixel wide line with a gradient + GPUTask Line(const olc::vf2d& p1, const olc::vf2d& p2, const olc::Pixel c1, const olc::Pixel c2); + + // Draws a rectangle outline + GPUTask Rect(const olc::vf2d& pos, const olc::vf2d& size, const olc::Pixel col = olc::Colour::WHITE); + // Draws a filled, single colour rectangle + GPUTask FillRect(const olc::vf2d& pos, const olc::vf2d& size, const olc::Pixel col = olc::Colour::WHITE); + // Draws an image + GPUTask Image(olc::Image& image, const olc::vf2d& pos, const olc::vf2d& size); + - struct HostConfig - { - - }; - enum class HostError + + public: // GPU Task Creator Functions (not normally called by user) + GPUTask TaskDrawPolygon( + GPUTask::Structure structure, + const std::vector& vPoints, + const std::vector& vColours, + const olc::Pixel tint = olc::Colour::WHITE); + + GPUTask TaskDrawPolygon( + GPUTask::Structure structure, + const std::vector& vPoints, + const olc::Pixel colour, + const olc::Pixel tint = olc::Colour::WHITE); + + GPUTask TaskFillPolygon( + GPUTask::Structure structure, + const std::vector& vPoints, + const std::vector& vColours, + const olc::Pixel tint = olc::Colour::WHITE); + + GPUTask TaskFillPolygon( + GPUTask::Structure structure, + const std::vector& vPoints, + const olc::Pixel colour, + const olc::Pixel tint = olc::Colour::WHITE); + + GPUTask TaskTexturedPolygon( + GPUTask::Structure structure, + const std::vector& vPoints, + const std::vector& vColours, + const std::vector& vTexCoords, + olc::Image* const image, + const olc::Pixel tint = olc::Colour::WHITE); + + }; +} +#define PGE_DRAW2D_DECLARED +#endif + +#if !defined(PGE_HARDWAREINPUT_DECLARED) +namespace olc +{ + namespace hw + { + struct Button { - None, - + bool bPressed = false; + bool bReleased = false; + bool bHeld = false; }; + } +} +#define PGE_HARDWAREINPUT_DECLARED 1 +#endif - class Host - { - public: - Host() = default; - virtual ~Host() {}; +#if !defined(PGE_HW_MOUSE_DECLARED) +namespace olc +{ + // Forward declare for friendship + class Window; - public: - // Check/Get last error - HostError GetLastError() const { return lastError; } - public: - virtual bool StartSystemEventLoop() = 0; - virtual bool AddWindowFrame(olc::Window* pWindow, const olc::vi2d& vWindowPos, const olc::vi2d& vWindowSize, const bool bFullScreen) = 0; - - virtual bool UpdateWindowFrameTitle(olc::Window* pWindow) = 0; + namespace hw + { + class Mouse + { + friend class olc::Window; - virtual std::vector GetHostWindowDescriptor(olc::Window* pWindow) = 0; - - - virtual bool ConnectHostResourceToRenderer() = 0; + public: + Mouse() = default; - // Wait for entire host desktop refresh (for smooooth vsync) - virtual bool SyncWithDesktopComposite() = 0; + public: + const Button& GetButton(const int nButton) const; + const olc::vf2d& GetPosition() const; protected: - HostError lastError = HostError::None; + std::array buttons{}; + std::array buttons_new{}; + std::array buttons_old{}; + olc::vf2d position; + olc::vf2d position_in; + + private: + void SetPosition(const olc::vf2d& pos); + void SetButton(const int nButton, bool state); + void UpdateState(); }; } } -#define PGE_HOST_IFACE_DECLARED 1 +#define PGE_HW_MOUSE_DECLARED 1 +#endif + +#if !defined(PGE_WINDOW_DECLARED) + +#if OLC_HOST == OLC_HOST_WINDOWS + #define FRIENDLY_HOST Host_Windows_WinAPI #endif -#if !defined(PGE_CORE_DECLARED) namespace olc { - // A grouping of all settable PGE properties - struct PGEConfig + namespace host { - // Size of "screen" in PGE pixels - olc::vi2d vScreenSize = { 256, 240 }; - // Size of a PGE pixel - olc::vi2d vPixelSize = { 4, 4 }; - // Top left location of shown main window - olc::vi2d vWindowOffset = { 30,30 }; - // Start in full-screen mode - bool bFullScreen = false; - // Allow full screen as an option with ALT-ENTER - bool bFullScreenable = true; - // Allow teh window to be resized by user - bool bResizeable = true; - // Synchronise rendering with monitor - bool bVSync = false; - // Behave like a host window, resizing the screen in response to window resize - bool bRealWindow = false; - // Ensure aspect ratio of "screen" is mainatined regardless of window size - bool bRetainAspectRatio = true; - // Force "screen" pixels to be integer in size - bool bForceIntegerPixelSize = false; - // Allow main PGE window to spawn child windows - bool bAllowChildWindows = true; - }; + class FRIENDLY_HOST; + class Host; + } - class PixelGameEngine : protected Window + namespace gpu { - public: - PixelGameEngine(); - virtual ~PixelGameEngine(); + class Renderer; + } - public: - bool Construct(const olc::vi2d& vScreenSize, const olc::vi2d& vPixelSize, bool bFullScreen = false); - bool Construct(const PGEConfig& cfg = PGEConfig{}); + namespace hw + { + class Mouse; + } - public: + class Window + { + friend class olc::host::FRIENDLY_HOST; + + + public: + Window(); + virtual ~Window(); + + void ConnecToHost(olc::host::Host* host); + + public: + // Return true if window is to continue + virtual bool OnUserCreate(); + // Return true if window is to continue + virtual bool OnUserUpdate(float fElapsedTime); + // Return true if window is to close + virtual bool OnUserDestroy(); + + private: + // Set Mouse Device State + bool olc_OnMouseButton(const uint8_t nButton, const bool bPressed); + bool olc_OnMouseMove(const olc::vi2d& vMousePos); + bool olc_OnMouseWheel(const int32_t nScroll); + bool olc_OnMouseFocus(const bool bHasFocus); + + // Set Window State + bool olc_OnWindowPosition(const olc::vi2d& vWindowPos); + bool olc_OnWindowSize(const olc::vi2d& vWindowSize); + + // Set Keyboard State + + + + public: + bool olc_ShouldRemove() const; + bool olc_WindowUpdate(const float fElapsedTime, olc::gpu::Renderer* const gpu); + + public: + size_t GetUID() const; + + const olc::vi2d& GetSize() const; + bool SetSize(const olc::vi2d& vSize); + + const olc::vi2d& GetPosition() const; + bool SetPosition(const olc::vi2d& vPosition); + + const std::string& GetTitle() const; + bool SetTitle(const std::string& sTitle); + + + protected: + virtual bool olc_PrimaryWindowInit(); + + protected: + bool bShouldRemove = false; + + protected: + size_t nUniqueID = -1; + olc::vi2d vWindowPos; + olc::vi2d vWindowSize; + std::string sFrameTitle; + + private: + olc::vi2d volatile_vMousePos; + + olc::host::Host* pHost = nullptr; + //olc::gpu::Renderer* pRenderer = nullptr; + + protected: + olc::Draw2D draw; + olc::Image imgPrimary; + + olc::hw::Mouse mouse; + }; +} +#define PGE_WINDOW_DECLARED 1 +#endif + +#if !defined(PGE_HOST_IFACE_DECLARED) +namespace olc +{ + namespace pgeguts + { + inline static size_t uuid = 0; + + inline constexpr size_t CreateUID() + { + return uuid++; + } + } + + namespace host + { + + + + struct HostConfig + { + + }; + + enum class HostError + { + None, + + }; + + class Host + { + public: + Host() = default; + virtual ~Host() {}; + + public: + // Check/Get last error + HostError GetLastError() const { return lastError; } + + public: + virtual bool StartSystemEventLoop() = 0; + virtual bool AddWindowFrame(olc::Window* pWindow, const olc::vi2d& vWindowPos, const olc::vi2d& vWindowSize, const bool bFullScreen) = 0; + + virtual bool UpdateWindowFrameTitle(olc::Window* pWindow) = 0; + + virtual std::vector GetHostWindowDescriptor(olc::Window* pWindow) = 0; + + + virtual bool ConnectHostResourceToRenderer() = 0; + + // Wait for entire host desktop refresh (for smooooth vsync) + virtual bool SyncWithDesktopComposite() = 0; + + protected: + HostError lastError = HostError::None; + }; + } +} +#define PGE_HOST_IFACE_DECLARED 1 +#endif + +#if !defined(PGE_CORE_DECLARED) +namespace olc +{ + // A grouping of all settable PGE properties + struct PGEConfig + { + // Size of "screen" in PGE pixels + olc::vi2d vScreenSize = { 256, 240 }; + // Size of a PGE pixel + olc::vi2d vPixelSize = { 4, 4 }; + // Top left location of shown main window + olc::vi2d vWindowOffset = { 30,30 }; + // Start in full-screen mode + bool bFullScreen = false; + // Allow full screen as an option with ALT-ENTER + bool bFullScreenable = true; + // Allow teh window to be resized by user + bool bResizeable = true; + // Synchronise rendering with monitor + bool bVSync = false; + // Behave like a host window, resizing the screen in response to window resize + bool bRealWindow = false; + // Ensure aspect ratio of "screen" is mainatined regardless of window size + bool bRetainAspectRatio = true; + // Force "screen" pixels to be integer in size + bool bForceIntegerPixelSize = false; + // Allow main PGE window to spawn child windows + bool bAllowChildWindows = true; + }; + + class PixelGameEngine : protected Window + { + public: + PixelGameEngine(); + virtual ~PixelGameEngine(); + + public: + bool Construct(const olc::vi2d& vScreenSize, const olc::vi2d& vPixelSize, bool bFullScreen = false); + bool Construct(const PGEConfig& cfg = PGEConfig{}); + + public: bool Start(); public: // The "Overridables" @@ -1565,7 +1860,7 @@ namespace olc // Store an image as a file asset on disk bool WriteImageToFile(const olc::Image& image, const std::string& sFileName); // Store an image as a file asset in memory - bool WriteImageToMemory(const olc::Image& image, std::vector bytes, const std::string& sFileName); + //bool WriteImageToMemory(const olc::Image& image, std::vector bytes, const std::string& sFileName); // Destroy an image void DestroyImage(olc::Image& image); @@ -1592,6 +1887,7 @@ namespace olc std::unique_ptr gpu; std::unique_ptr host; + std::unique_ptr imageloader; }; } #define PGE_CORE_DECLARED 1 @@ -1603,6 +1899,7 @@ namespace olc + #if OLC_HOST == OLC_HOST_WINDOWS #if defined(UNICODE) || defined(_UNICODE) #define olcT(s) L##s @@ -1740,6 +2037,7 @@ namespace olc typedef GLuint CALLSTYLE glCreateShader_t(GLenum type); typedef GLuint CALLSTYLE glCreateProgram_t(void); + typedef void CALLSTYLE glShaderSource_t(GLuint shader, GLsizei count, const GLchar** string, const GLint* length); typedef void CALLSTYLE glDeleteShader_t(GLuint shader); typedef void CALLSTYLE glCompileShader_t(GLuint shader); typedef void CALLSTYLE glLinkProgram_t(GLuint program); @@ -1761,11 +2059,11 @@ namespace olc typedef void CALLSTYLE glUniform4fv_t(GLint location, GLsizei count, const GLfloat* value); typedef void CALLSTYLE glUniformMatrix4fv_t(GLint location, GLsizei count, GLboolean trasnpose, const GLfloat* value); typedef void CALLSTYLE glActiveTexture_t(GLenum texture); - typedef void CALLSTYLE glGenFrameBuffers_t(GLsizei n, GLuint* ids); - typedef void CALLSTYLE glBindFrameBuffer_t(GLenum target, GLuint fb); - typedef GLenum CALLSTYLE glCheckFrameBufferStatus_t(GLenum target); - typedef void CALLSTYLE glDeleteFrameBuffers_t(GLsizei n, const GLuint* fbs); - typedef void CALLSTYLE glFrameBufferTexture2D_t(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); + typedef void CALLSTYLE glGenFramebuffers_t(GLsizei n, GLuint* ids); + typedef void CALLSTYLE glBindFramebuffer_t(GLenum target, GLuint fb); + typedef GLenum CALLSTYLE glCheckFramebufferStatus_t(GLenum target); + typedef void CALLSTYLE glDeleteFramebuffers_t(GLsizei n, const GLuint* fbs); + typedef void CALLSTYLE glFramebufferTexture2D_t(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); typedef void CALLSTYLE glDrawBuffers_t(GLsizei n, const GLenum* bufs); typedef void CALLSTYLE glBlendFuncSeparate_t(GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); @@ -1780,6 +2078,7 @@ namespace olc { public: static gl& Get(); + inline bool HasLoaded() const { return bLoaded; } private: gl() = default; @@ -1788,37 +2087,75 @@ namespace olc private: bool LoadAll(); + protected: + glCreateShader_t* _glCreateShader = nullptr; + glCreateProgram_t* _glCreateProgram = nullptr; + glShaderSource_t* _glShaderSource = nullptr; + glDeleteShader_t* _glDeleteShader = nullptr; + glCompileShader_t* _glCompileShader = nullptr; + glLinkProgram_t* _glLinkProgram = nullptr; + glDeleteProgram_t* _glDeleteProgram = nullptr; + glAttachShader_t* _glAttachShader = nullptr; + glBindBuffer_t* _glBindBuffer = nullptr; + glBufferData_t* _glBufferData = nullptr; + glGenBuffers_t* _glGenBuffers = nullptr; + glVertexAttribPointer_t* _glVertexAttribPointer = nullptr; + glEnableVertexAttribArray_t* _glEnableVertexAttribArray = nullptr; + glUseProgram_t* _glUseProgram = nullptr; + glBindVertexArray_t* _glBindVertexArray = nullptr; + glGenVertexArrays_t* _glGenVertexArrays = nullptr; + glGetShaderInfoLog_t* _glGetShaderInfoLog = nullptr; + glGetUniformLocation_t* _glGetUniformLocation = nullptr; + glUniform1f_t* _glUniform1f = nullptr; + glUniform1i_t* _glUniform1i = nullptr; + glUniform2fv_t* _glUniform2fv = nullptr; + glUniform4fv_t* _glUniform4fv = nullptr; + glUniformMatrix4fv_t* _glUniformMatrix4fv = nullptr; + glActiveTexture_t* _glActiveTexture = nullptr; + glGenFramebuffers_t* _glGenFramebuffers = nullptr; + glBindFramebuffer_t* _glBindFramebuffer = nullptr; + glCheckFramebufferStatus_t* _glCheckFramebufferStatus = nullptr; + glDeleteFramebuffers_t* _glDeleteFramebuffers = nullptr; + glFramebufferTexture2D_t* _glFramebufferTexture2D = nullptr; + glDrawBuffers_t* _glDrawBuffers = nullptr; + glBlendFuncSeparate_t* _glBlendFuncSeparate = nullptr; + public: - glCreateShader_t* glCreateShader = nullptr; - glCreateProgram_t* glCreateProgram = nullptr; - glDeleteShader_t* glDeleteShader = nullptr; - glCompileShader_t* glCompileShader = nullptr; - glLinkProgram_t* glLinkProgram = nullptr; - glDeleteProgram_t* glDeleteProgram = nullptr; - glAttachShader_t* glAttachShader = nullptr; - glBindBuffer_t* glBindBuffer = nullptr; - glBufferData_t* glBufferData = nullptr; - glGenBuffers_t* glGenBuffers = nullptr; - glVertexAttribPointer_t* glVertexAttribPointer = nullptr; - glEnableVertexAttribArray_t* glEnableVertexAttribArray = nullptr; - glUseProgram_t* glUseProgram = nullptr; - glBindVertexArray_t* glBindVertexArray = nullptr; - glGenVertexArrays_t* glGenVertexArrays = nullptr; - glGetShaderInfoLog_t* glGetShaderInfoLog = nullptr; - glGetUniformLocation_t* glGetUniformLocation = nullptr; - glUniform1f_t* glUniform1f = nullptr; - glUniform1i_t* glUniform1i = nullptr; - glUniform2fv_t* glUniform2fv = nullptr; - glUniform4fv_t* glUniform4fv = nullptr; - glUniformMatrix4fv_t* glUniformMatrix4fv = nullptr; - glActiveTexture_t* glActiveTexture = nullptr; - glGenFrameBuffers_t* glGenFrameBuffers = nullptr; - glBindFrameBuffer_t* glBindFrameBuffer = nullptr; - glCheckFrameBufferStatus_t* glCheckFrameBufferStatus = nullptr; - glDeleteFrameBuffers_t* glDeleteFrameBuffers = nullptr; - glFrameBufferTexture2D_t* glFrameBufferTexture2D = nullptr; - glDrawBuffers_t* glDrawBuffers = nullptr; - glBlendFuncSeparate_t* glBlendFuncSeparate = nullptr; + // Proxies allow switchable, clutter-free error checking + + // OpenGL3.3 Proxies + + GLuint glCreateShader(GLenum type); + GLuint glCreateProgram(void); + void glShaderSource(GLuint shader, GLsizei count, const GLchar** string, const GLint* length); + void glDeleteShader(GLuint shader); + void glCompileShader(GLuint shader); + void glLinkProgram(GLuint program); + void glDeleteProgram(GLuint program); + void glAttachShader(GLuint program, GLuint shader); + void glBindBuffer(GLenum target, GLuint buffer); + void glBufferData(GLenum target, GLsizeiptr size, const void* data, GLenum usage); + void glGenBuffers(GLsizei n, GLuint* buffers); + void glVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void* pointer); + void glEnableVertexAttribArray(GLuint index); + void glUseProgram(GLuint program); + void glBindVertexArray(GLuint array); + void glGenVertexArrays(GLsizei n, GLuint* arrays); + void glGetShaderInfoLog(GLuint shader, GLsizei bufSize, GLsizei* length, GLchar* infoLog); + GLint glGetUniformLocation(GLuint program, const GLchar* name); + void glUniform1f(GLint location, GLfloat v0); + void glUniform1i(GLint location, GLint v0); + void glUniform2fv(GLint location, GLsizei count, const GLfloat* value); + void glUniform4fv(GLint location, GLsizei count, const GLfloat* value); + void glUniformMatrix4fv(GLint location, GLsizei count, GLboolean trasnpose, const GLfloat* value); + void glActiveTexture(GLenum texture); + void glGenFramebuffers(GLsizei n, GLuint* ids); + void glBindFramebuffer(GLenum target, GLuint fb); + GLenum glCheckFramebufferStatus(GLenum target); + void glDeleteFramebuffers(GLsizei n, const GLuint* fbs); + void glFramebufferTexture2D(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); + void glDrawBuffers(GLsizei n, const GLenum* bufs); + void glBlendFuncSeparate(GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); // OpenGL1.2 Proxies (just keeps things tidy imo) void glGenTextures(GLsizei n, GLuint* textures); @@ -1830,6 +2167,18 @@ namespace olc void glClear(GLbitfield mask); void glViewport(GLint x, GLint y, GLsizei width, GLsizei height); void glClearColor(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); + void glReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void* data); + void glCullFace(GLenum mode); + void glEnable(GLenum cap); + void glDisable(GLenum cap); + void glDrawArrays(GLenum mode, GLint first, GLsizei count); + void glBlendFunc(GLenum sfactor, GLenum dfactor); + void glDepthFunc(GLenum func); + void glGetTexImage(GLenum target, GLint level, GLenum format, GLenum type, void* pixels); + + + private: + bool CheckError(const std::source_location loc = std::source_location::current()); }; } @@ -1854,6 +2203,13 @@ namespace olc { namespace gpu { + class Shader_GLSL33 : public olc::gpu::Shader + { + public: + std::string Compile() override; + int32_t CreateUniform(const std::string& name) override; + }; + class Renderer_OGL33 : public olc::gpu::Renderer { public: // Device Stuff @@ -1862,6 +2218,7 @@ namespace olc // Destroys a GPU device interface bool DestroyDevice() override; + public: // Texture Resource Stuff // Allocates a new texture resource in VRAM, returns handle uint32_t CreateTexture(const olc::vi2d& vSize, const olc::ImageConfig& cfg = olc::ImageConfig()) override; @@ -1876,9 +2233,12 @@ namespace olc // Makes active the given texture resource (for subsequent rendering operations) bool AssignTextureTarget(const uint32_t slot, const uint32_t texid) override; + public: // Shader Construction Stuff + bool ApplyShader(const Shader& shader) override; + bool ApplyDefaultShader() override; public: // GPU Task Stuff - virtual bool DoGPUTask(const olc::pgeguts::GPUTask& task) override; + virtual bool DoGPUTask(const olc::GPUTask& task) override; public: // Swap Chain Stuff // Clears the viewport to a specific colour and depth @@ -1895,6 +2255,12 @@ namespace olc olc::apis::opengl::glDeviceContext_t glDeviceContext = 0; olc::apis::opengl::glRenderContext_t glRenderContext = 0; + Shader_GLSL33 shaderDefault; + uint32_t nDefaultVB = 0; + uint32_t nDefaultVA = 0; + uint32_t nDefaultFBO = 0; + olc::Image imgBlank; + }; } } @@ -1902,155 +2268,79 @@ namespace olc #endif #endif +#if OLC_IMAGELOADER == OLC_IMAGELOADER_WINGDI +#if defined(UNICODE) || defined(_UNICODE) + #define olcT(s) L##s +#else + #define olcT(s) s +#endif + +#define _WINSOCKAPI_ +#if !defined(VC_EXTRALEAN) + #define VC_EXTRALEAN +#endif +#if !defined(NOMINMAX) + #define NOMINMAX +#endif +// 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 +// Embrace MSVC superiority +#pragma comment(lib, "gdiplus.lib") +#pragma comment(lib, "Shlwapi.lib") +#include +#include +#include +#include +#undef _WINSOCKAPI_ -#if defined(OLC_PGE_APPLICATION) && !defined(PGE_WINDOW_IMPLEMENTED) +#if !defined(PGE_IMAGELOADER_WINGDI_DECLARED) namespace olc { - Window::Window() + namespace imload { - nUniqueID = pgeguts::CreateUID(); - } + class ImageLoader_WinGDI : public ImageLoader + { + public: + + // Create an image resource based on an image file asset on disk + bool CreateImageFromFile(olc::Image& image, const std::string& sFileName) override; - Window::~Window() - { - } + // Create an image resource based on an image file asset in memory + bool CreateImageFromMemory(olc::Image& image, const uint8_t* data, const size_t bytes) override; - void Window::ConnecToHost(olc::host::Host* host) - { - pHost = host; - sFrameTitle = "OneLoneCoder.com - Pixel Game Engine 3"; - pHost->UpdateWindowFrameTitle(this); - } + // Create an image resource based on an image file asset in memory + bool CreateImageFromMemory(olc::Image& image, const std::vector& data) override; - bool Window::OnUserCreate() - { - return false; - } + // Store an image as a file asset on disk + bool WriteImageToFile(const olc::Image& image, const std::string& sFileName) override; - bool Window::OnUserUpdate(float fElapsedTime) - { - return false; - } + // Store an image as a file asset in memory + bool WriteImageToMemoryFile(olc::Image& image, const std::vector& data) override; - bool Window::OnUserDestroy() - { - return false; + }; } +} - bool Window::olc_OnMouseButton(const uint8_t nButton, const bool bPressed) - { - return false; - } +#define PGE_IMAGELOADER_WINGDI_DECLARED 1 +#endif +#endif - bool Window::olc_OnMouseMove(const olc::vi2d& vMousePos) - { - volatile_vMousePos = vMousePos; - return true; - } - bool Window::olc_OnMouseWheel(const int32_t nScroll) - { - return false; - } - bool Window::olc_OnMouseFocus(const bool bHasFocus) - { - return false; - } - - bool Window::olc_OnWindowPosition(const olc::vi2d& vWindowPos) - { - return false; - } - - bool Window::olc_OnWindowSize(const olc::vi2d& vWindowSize) - { - return false; - } - - bool Window::olc_ShouldRemove() const - { - return bShouldRemove; - } - - bool Window::olc_WindowUpdate(const float fElapsedTime, olc::gpu::Renderer* const gpu) - { - // Environmental changes - - gpu->SetViewport({ 0,0 }, GetSize()); - gpu->ClearViewport(olc::Colour::TANGERINE, false, false); - - // User Update - if (!OnUserUpdate(fElapsedTime)) - { - // User has requested termination of window by returning false - if (OnUserDestroy()) - { - // User has confirmed window destruction by returning true - bShouldRemove = true; - } - } - - - gpu->DisplayDraw(); - - return true; - } - - size_t Window::GetUID() const - { - return nUniqueID; - } - - const olc::vi2d& Window::GetSize() const - { - return vWindowSize; - } - - const olc::vi2d& Window::GetPosition() const - { - return vWindowPos; - } - - bool Window::SetSize(const olc::vi2d& vSize) - { - // Request host to change geometry of window object - - vWindowSize = vSize; - return true; - } - - bool Window::SetPosition(const olc::vi2d& vPosition) - { - // Request host to change geometry of window object - vWindowPos = vPosition; - return true; - } - const std::string& Window::GetTitle() const - { - return sFrameTitle; - } - bool Window::SetTitle(const std::string& sTitle) - { - sFrameTitle = sTitle; - pHost->UpdateWindowFrameTitle(this); - return false; - } - bool Window::olc_PrimaryWindowInit() - { - return false; - } -}; -#define PGE_WINDOW_IMPLEMENTED 1 -#endif #if defined(OLC_PGE_APPLICATION) && !defined(PGE_HOST_IMPLEMENTED) #if OLC_HOST == OLC_HOST_WINDOWS @@ -2121,8 +2411,9 @@ namespace olc::host { // The user created olc::Window object is the SSoT for what a window // should look like, so get that sort of thing from there - olc::vi2d vWinPos = pWindow->GetPosition(); - olc::vi2d vWinSize = pWindow->GetSize(); + olc::vi2d vWinPos = vWindowPos; + olc::vi2d vWinSize = vWindowSize; + // Define WindowClass WNDCLASS wc = { 0 }; @@ -2162,6 +2453,7 @@ namespace olc::host AdjustWindowRectEx(&rWndRect, dwStyle, FALSE, dwExStyle); int width = rWndRect.right - rWndRect.left; int height = rWndRect.bottom - rWndRect.top; + pWindow->SetSize({width, height}); // Create the actual OS window, return a handle HWND hWnd = CreateWindowEx(dwExStyle, olcT("OLC_PIXEL_GAME_ENGINE3"), olcT(""), dwStyle, @@ -2240,7 +2532,12 @@ namespace olc::host // case WM_MOVE: vWinPos = olc::vi2d(lParam & 0xFFFF, (lParam >> 16) & 0xFFFF); ptrPGE->olc_UpdateWindowPos(lParam & 0xFFFF, (lParam >> 16) & 0xFFFF); return 0; - // case WM_SIZE: vWinSize = olc::vi2d(lParam & 0xFFFF, (lParam >> 16) & 0xFFFF); ptrPGE->olc_UpdateWindowSize(lParam & 0xFFFF, (lParam >> 16) & 0xFFFF); return 0; + case WM_SIZE: + { + window->olc_OnWindowSize(olc::vi2d(lParam & 0xFFFF, (lParam >> 16) & 0xFFFF)); + return 0; + } + break; // 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; @@ -2249,12 +2546,36 @@ namespace olc::host // case WM_KEYUP: ptrPGE->olc_UpdateKeyState(int32_t(wParam), false); return 0; // case WM_SYSKEYDOWN: ptrPGE->olc_UpdateKeyState(int32_t(wParam), true); return 0; // case WM_SYSKEYUP: ptrPGE->olc_UpdateKeyState(int32_t(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_LBUTTONDOWN: + { + window->olc_OnMouseButton(0, true); + return 0; + } + case WM_LBUTTONUP: + { + window->olc_OnMouseButton(0, false); + return 0; + } + case WM_RBUTTONDOWN: + { + window->olc_OnMouseButton(1, true); + return 0; + } + case WM_RBUTTONUP: + { + window->olc_OnMouseButton(1, false); + return 0; + } + case WM_MBUTTONDOWN: + { + window->olc_OnMouseButton(2, true); + return 0; + } + case WM_MBUTTONUP: + { + window->olc_OnMouseButton(2, false); + return 0; + } // case WM_DROPFILES: // { // // This is all eww... @@ -2309,6 +2630,38 @@ namespace olc::host #if defined(OLC_PGE_APPLICATION) && !defined(PGE_GPU_IMPLEMENTED) #if OLC_GPU == OLC_GPU_OPENGL33 +namespace olc +{ + gpu::Shader::~Shader() + { + } + + void gpu::Shader::SetPixelShaderSource(const std::string& src) + { + srcPixelShader = src; + } + + void gpu::Shader::SetVertexShaderSource(const std::string& src) + { + srcVertexShader = src; + } + + void gpu::Shader::SetGeometryShaderSource(const std::string& src) + { + srcGeometryShader = src; + } + + int32_t gpu::Shader::GetUniform(const std::string& name) const + { + return mapUniforms.at(name); + } + + int32_t gpu::Shader::GetShaderID() const + { + return nCompiledShaderID; + } +} + namespace olc::apis::opengl { bool gl::bLoaded = false; @@ -2324,239 +2677,786 @@ namespace olc::apis::opengl { // Load all the OpenGL API entry points bLoaded = true; - bLoaded &= (glCreateShader = OGL_LOAD(glCreateShader)) != nullptr; - bLoaded &= (glCreateProgram = OGL_LOAD(glCreateProgram)) != nullptr; - bLoaded &= (glDeleteShader = OGL_LOAD(glDeleteShader)) != nullptr; - bLoaded &= (glCompileShader = OGL_LOAD(glCompileShader)) != nullptr; - bLoaded &= (glLinkProgram = OGL_LOAD(glLinkProgram)) != nullptr; - bLoaded &= (glDeleteProgram = OGL_LOAD(glDeleteProgram)) != nullptr; - bLoaded &= (glAttachShader = OGL_LOAD(glAttachShader)) != nullptr; - bLoaded &= (glBindBuffer = OGL_LOAD(glBindBuffer)) != nullptr; - bLoaded &= (glBufferData = OGL_LOAD(glBufferData)) != nullptr; - bLoaded &= (glGenBuffers = OGL_LOAD(glGenBuffers)) != nullptr; - bLoaded &= (glVertexAttribPointer = OGL_LOAD(glVertexAttribPointer)) != nullptr; - bLoaded &= (glEnableVertexAttribArray = OGL_LOAD(glEnableVertexAttribArray)) != nullptr; - bLoaded &= (glUseProgram = OGL_LOAD(glUseProgram)) != nullptr; - bLoaded &= (glBindVertexArray = OGL_LOAD(glBindVertexArray)) != nullptr; - bLoaded &= (glGenVertexArrays = OGL_LOAD(glGenVertexArrays)) != nullptr; - bLoaded &= (glGetShaderInfoLog = OGL_LOAD(glGetShaderInfoLog)) != nullptr; - bLoaded &= (glGetUniformLocation = OGL_LOAD(glGetUniformLocation)) != nullptr; - bLoaded &= (glUniform1f = OGL_LOAD(glUniform1f)) != nullptr; - bLoaded &= (glUniform1i = OGL_LOAD(glUniform1i)) != nullptr; - bLoaded &= (glUniform2fv = OGL_LOAD(glUniform2fv)) != nullptr; - bLoaded &= (glUniform4fv = OGL_LOAD(glUniform4fv)) != nullptr; - bLoaded &= (glUniformMatrix4fv = OGL_LOAD(glUniformMatrix4fv)) != nullptr; - bLoaded &= (glActiveTexture = OGL_LOAD(glActiveTexture)) != nullptr; - bLoaded &= (glGenFrameBuffers = OGL_LOAD(glGenFrameBuffers)) != nullptr; - bLoaded &= (glBindFrameBuffer = OGL_LOAD(glBindFrameBuffer)) != nullptr; - bLoaded &= (glCheckFrameBufferStatus = OGL_LOAD(glCheckFrameBufferStatus)) != nullptr; - bLoaded &= (glDeleteFrameBuffers = OGL_LOAD(glDeleteFrameBuffers)) != nullptr; - bLoaded &= (glFrameBufferTexture2D = OGL_LOAD(glFrameBufferTexture2D)) != nullptr; - bLoaded &= (glDrawBuffers = OGL_LOAD(glDrawBuffers)) != nullptr; - bLoaded &= (glBlendFuncSeparate = OGL_LOAD(glBlendFuncSeparate)) != nullptr; - - if (!bLoaded) - return false; // API load has failed - + bLoaded &= (_glCreateShader = OGL_LOAD(glCreateShader)) != nullptr; + bLoaded &= (_glCreateProgram = OGL_LOAD(glCreateProgram)) != nullptr; + bLoaded &= (_glShaderSource = OGL_LOAD(glShaderSource)) != nullptr; + bLoaded &= (_glDeleteShader = OGL_LOAD(glDeleteShader)) != nullptr; + bLoaded &= (_glCompileShader = OGL_LOAD(glCompileShader)) != nullptr; + bLoaded &= (_glLinkProgram = OGL_LOAD(glLinkProgram)) != nullptr; + bLoaded &= (_glDeleteProgram = OGL_LOAD(glDeleteProgram)) != nullptr; + bLoaded &= (_glAttachShader = OGL_LOAD(glAttachShader)) != nullptr; + bLoaded &= (_glBindBuffer = OGL_LOAD(glBindBuffer)) != nullptr; + bLoaded &= (_glBufferData = OGL_LOAD(glBufferData)) != nullptr; + bLoaded &= (_glGenBuffers = OGL_LOAD(glGenBuffers)) != nullptr; + bLoaded &= (_glVertexAttribPointer = OGL_LOAD(glVertexAttribPointer)) != nullptr; + bLoaded &= (_glEnableVertexAttribArray = OGL_LOAD(glEnableVertexAttribArray)) != nullptr; + bLoaded &= (_glUseProgram = OGL_LOAD(glUseProgram)) != nullptr; + bLoaded &= (_glBindVertexArray = OGL_LOAD(glBindVertexArray)) != nullptr; + bLoaded &= (_glGenVertexArrays = OGL_LOAD(glGenVertexArrays)) != nullptr; + bLoaded &= (_glGetShaderInfoLog = OGL_LOAD(glGetShaderInfoLog)) != nullptr; + bLoaded &= (_glGetUniformLocation = OGL_LOAD(glGetUniformLocation)) != nullptr; + bLoaded &= (_glUniform1f = OGL_LOAD(glUniform1f)) != nullptr; + bLoaded &= (_glUniform1i = OGL_LOAD(glUniform1i)) != nullptr; + bLoaded &= (_glUniform2fv = OGL_LOAD(glUniform2fv)) != nullptr; + bLoaded &= (_glUniform4fv = OGL_LOAD(glUniform4fv)) != nullptr; + bLoaded &= (_glUniformMatrix4fv = OGL_LOAD(glUniformMatrix4fv)) != nullptr; + bLoaded &= (_glActiveTexture = OGL_LOAD(glActiveTexture)) != nullptr; + bLoaded &= (_glGenFramebuffers = OGL_LOAD(glGenFramebuffers)) != nullptr; + bLoaded &= (_glBindFramebuffer = OGL_LOAD(glBindFramebuffer)) != nullptr; + bLoaded &= (_glCheckFramebufferStatus = OGL_LOAD(glCheckFramebufferStatus)) != nullptr; + bLoaded &= (_glDeleteFramebuffers = OGL_LOAD(glDeleteFramebuffers)) != nullptr; + bLoaded &= (_glFramebufferTexture2D = OGL_LOAD(glFramebufferTexture2D)) != nullptr; + bLoaded &= (_glDrawBuffers = OGL_LOAD(glDrawBuffers)) != nullptr; + bLoaded &= (_glBlendFuncSeparate = OGL_LOAD(glBlendFuncSeparate)) != nullptr; + + return bLoaded; + } + bool gl::CheckError(const std::source_location loc) + { +#if OLC_GPU_ERRORCHECK == 1 + GLenum err; + bool bWasError = false; + std::stringstream sLocation; + sLocation + << loc.file_name() << '(' + << loc.line() << ':' + << loc.column() << ") `" + << loc.function_name() << "`: "; + while ((err = ::glGetError()) != GL_NO_ERROR) + { + switch (err) + { + case GL_INVALID_ENUM: + sLocation << "OGL33 Error: GL_INVALID_ENUM\n"; break; + case GL_INVALID_VALUE: + sLocation << "OGL33 Error: GL_INVALID_VALUE\n"; break; + case GL_INVALID_OPERATION: + sLocation << "OGL33 Error: GL_INVALID_OPERATION\n"; break; + case 0x0506: //GL_INVALID_FRAMEBUFFER_OPERATION: + sLocation << "OGL33 Error: GL_INVALID_FRAMEBUFFER_OPERATION\n"; break; + case GL_OUT_OF_MEMORY: + sLocation << "OGL33 Error: GL_OUT_OF_MEMORY\n"; break; + case GL_STACK_UNDERFLOW: + sLocation << "OGL33 Error: GL_STACK_UNDERFLOW\n"; break; + case GL_STACK_OVERFLOW: + sLocation << "OGL33 Error: GL_STACK_OVERFLOW\n"; break; + } + bWasError = true; - return bLoaded; + std::cout << sLocation.str() << "\n"; + sLocation.clear(); + } + return bWasError; +#else + return false; +#endif } void gl::glGenTextures(GLsizei n, GLuint* textures) { ::glGenTextures(n, textures); + CheckError(); } void gl::glBindTexture(GLenum target, GLuint texture) { ::glBindTexture(target, texture); + CheckError(); } void gl::glTexParameteri(GLenum target, GLenum pname, GLint param) { ::glTexParameteri(target, pname, param); + CheckError(); } void gl::glTexEnvf(GLenum target, GLenum pname, GLfloat param) { ::glTexEnvf(target, pname, param); + CheckError(); } void gl::glDeleteTextures(GLsizei n, const GLuint* textures) { ::glDeleteTextures(n, textures); + CheckError(); } void gl::glTexImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid* pixels) { ::glTexImage2D(target, level, internalformat, width, height, border, format, type, pixels); + CheckError(); } void gl::glClear(GLbitfield mask) { ::glClear(mask); + CheckError(); } void gl::glViewport(GLint x, GLint y, GLsizei width, GLsizei height) { ::glViewport(x, y, width, height); + CheckError(); } void gl::glClearColor(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) { ::glClearColor(red, green, blue, alpha); + CheckError(); } -} -namespace olc::gpu -{ - bool Renderer_OGL33::CreateDevice(std::vector params, const RendererConfig& cfg) - { - config = cfg; - - - // Create OpenGL Device Context -#if OLC_HOST == OLC_HOST_WINDOWS - // "wgl*" all live in WinGDI - 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))) - { - lastError = RendererError::InvalidDCPixelFormat; - return false; - } - - if (!SetPixelFormat(glDeviceContext, pf, &pfd)) - { - lastError = RendererError::FailedToSetDCPixelFormat; - return false; - } - - if (!(glRenderContext = wglCreateContext(glDeviceContext))) - { - lastError = RendererError::FailedToCreateRenderContext; - return false; - } - - if (!wglMakeCurrent(glDeviceContext, glRenderContext)) - { - lastError = RendererError::FailedToSwitchRenderContext; - return false; - } - - //// Set Vertical Sync - //glSwapInterval = OGL_LOAD(glSwapInterval); - //if (locSwapInterval && !bVSYNC) locSwapInterval(0); - //bSync = bVSYNC; -#endif - - // Can't load OpenGL API until context is loaded - auto& gl = olc::apis::opengl::gl::Get(); + void gl::glReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void* data) + { + ::glReadPixels(x, y, width, height, format, type, data); + CheckError(); + } + void gl::glCullFace(GLenum mode) + { + ::glCullFace(mode); + CheckError(); + } - lastError = RendererError::None; - return true; + void gl::glEnable(GLenum cap) + { + ::glEnable(cap); + CheckError(); } - bool Renderer_OGL33::DestroyDevice() + void gl::glDisable(GLenum cap) { - auto& gl = olc::apis::opengl::gl::Get(); + ::glDisable(cap); + CheckError(); + } -#if OLC_HOST == OLC_HOST_WINDOWS - wglDeleteContext(glRenderContext); -#endif - return false; + void gl::glDrawArrays(GLenum mode, GLint first, GLsizei count) + { + ::glDrawArrays(mode, first, count); + CheckError(); } - uint32_t Renderer_OGL33::CreateTexture(const olc::vi2d& vSize, const olc::ImageConfig& cfg) + void gl::glBlendFunc(GLenum sfactor, GLenum dfactor) { - auto& gl = olc::apis::opengl::gl::Get(); + ::glBlendFunc(sfactor, dfactor); + CheckError(); + } - // Curiously OpenGL doesnt actually care about the size of the texture - // as part of its creation. This matters later when we Write to texture - // resources on GPU - - uint32_t id = 0; - gl.glGenTextures(1, &id); - gl.glBindTexture(GL_TEXTURE_2D, id); + void gl::glDepthFunc(GLenum func) + { + ::glDepthFunc(func); + CheckError(); + } - if (cfg.Filtered) - { - gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - } - else - { - gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - } + void gl::glGetTexImage(GLenum target, GLint level, GLenum format, GLenum type, void* pixels) + { + ::glGetTexImage(target, level, format, type, pixels); + CheckError(); + } - if (cfg.Clamp) - { - gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); - gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); - } - else - { - gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - } + GLuint gl::glCreateShader(GLenum type) + { + return _glCreateShader(type); + } -#if OLC_HOST != OLC_HOST_EMSCRIPTEN - gl.glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); -#endif - return id; + GLuint gl::glCreateProgram(void) + { + return _glCreateProgram(); } - bool Renderer_OGL33::WriteTexture(const uint32_t texid, olc::Image& image) + void gl::glShaderSource(GLuint shader, GLsizei count, const GLchar** string, const GLint* length) { - auto& gl = olc::apis::opengl::gl::Get(); - gl.glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.Size().x, image.Size().y, 0, GL_RGBA, GL_UNSIGNED_BYTE, image.Data()); - return false; + _glShaderSource(shader, count, string, length); + CheckError(); } - bool Renderer_OGL33::ReadTexture(const uint32_t texid, olc::Image& image) + void gl::glDeleteShader(GLuint shader) { - auto& gl = olc::apis::opengl::gl::Get(); - return false; + _glDeleteShader(shader); + CheckError(); } - bool Renderer_OGL33::DeleteTexture(const uint32_t texid) + void gl::glCompileShader(GLuint shader) { - auto& gl = olc::apis::opengl::gl::Get(); - gl.glDeleteTextures(1, &texid); - return true; + _glCompileShader(shader); + CheckError(); } - bool Renderer_OGL33::AssignTextureSource(const uint32_t slot, const uint32_t texid) + void gl::glLinkProgram(GLuint program) { - auto& gl = olc::apis::opengl::gl::Get(); - return false; + _glLinkProgram(program); + CheckError(); } - bool Renderer_OGL33::AssignTextureTarget(const uint32_t slot, const uint32_t texid) + void gl::glDeleteProgram(GLuint program) { - auto& gl = olc::apis::opengl::gl::Get(); - return false; + _glDeleteProgram(program); + CheckError(); } - bool Renderer_OGL33::DoGPUTask(const olc::pgeguts::GPUTask& task) + void gl::glAttachShader(GLuint program, GLuint shader) { - auto& gl = olc::apis::opengl::gl::Get(); - return false; + _glAttachShader(program, shader); + CheckError(); } - bool Renderer_OGL33::ClearViewport(const olc::Pixel col, bool bDepth, bool bStencil) + void gl::glBindBuffer(GLenum target, GLuint buffer) + { + _glBindBuffer(target, buffer); + CheckError(); + } + + void gl::glBufferData(GLenum target, GLsizeiptr size, const void* data, GLenum usage) + { + _glBufferData(target, size, data, usage); + CheckError(); + } + + void gl::glGenBuffers(GLsizei n, GLuint* buffers) + { + _glGenBuffers(n, buffers); + CheckError(); + } + + void gl::glVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void* pointer) + { + _glVertexAttribPointer(index, size, type, normalized, stride, pointer); + CheckError(); + } + + void gl::glEnableVertexAttribArray(GLuint index) + { + _glEnableVertexAttribArray(index); + CheckError(); + } + + void gl::glUseProgram(GLuint program) + { + _glUseProgram(program); + CheckError(); + } + + void gl::glBindVertexArray(GLuint array) + { + _glBindVertexArray(array); + CheckError(); + } + + void gl::glGenVertexArrays(GLsizei n, GLuint* arrays) + { + _glGenVertexArrays(n, arrays); + CheckError(); + } + + void gl::glGetShaderInfoLog(GLuint shader, GLsizei bufSize, GLsizei* length, GLchar* infoLog) + { + _glGetShaderInfoLog(shader, bufSize, length, infoLog); + CheckError(); + } + + GLint gl::glGetUniformLocation(GLuint program, const GLchar* name) + { + return _glGetUniformLocation(program, name); + } + + void gl::glUniform1f(GLint location, GLfloat v0) + { + _glUniform1f(location, v0); + CheckError(); + } + + void gl::glUniform1i(GLint location, GLint v0) + { + _glUniform1i(location, v0); + CheckError(); + } + + void gl::glUniform2fv(GLint location, GLsizei count, const GLfloat* value) + { + _glUniform2fv(location, count, value); + CheckError(); + } + + void gl::glUniform4fv(GLint location, GLsizei count, const GLfloat* value) + { + _glUniform4fv(location, count, value); + CheckError(); + } + + void gl::glUniformMatrix4fv(GLint location, GLsizei count, GLboolean trasnpose, const GLfloat* value) + { + _glUniformMatrix4fv(location, count, trasnpose, value); + CheckError(); + } + + void gl::glActiveTexture(GLenum texture) + { + _glActiveTexture(texture); + CheckError(); + } + + void gl::glGenFramebuffers(GLsizei n, GLuint* ids) + { + _glGenFramebuffers(n, ids); + CheckError(); + } + + void gl::glBindFramebuffer(GLenum target, GLuint fb) + { + _glBindFramebuffer(target, fb); + CheckError(); + } + + GLenum gl::glCheckFramebufferStatus(GLenum target) + { + return _glCheckFramebufferStatus(target); + } + + void gl::glDeleteFramebuffers(GLsizei n, const GLuint* fbs) + { + _glDeleteFramebuffers(n, fbs); + CheckError(); + } + + void gl::glFramebufferTexture2D(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) + { + _glFramebufferTexture2D(target, attachment, textarget, texture, level); + CheckError(); + } + + void gl::glDrawBuffers(GLsizei n, const GLenum* bufs) + { + _glDrawBuffers(n, bufs); + CheckError(); + } + + void gl::glBlendFuncSeparate(GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha) + { + _glBlendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha); + CheckError(); + } +} +namespace olc::gpu +{ + std::string Shader_GLSL33::Compile() + { + auto& gl = olc::apis::opengl::gl::Get(); + + nCompiledShaderID = gl.glCreateProgram(); + + // Fragment Shader + if (!srcPixelShader.empty()) + { + nPixelShaderID = gl.glCreateShader(0x8B30); + const char* s = srcPixelShader.c_str(); + gl.glShaderSource(nPixelShaderID, 1, &s, nullptr); + gl.glCompileShader(nPixelShaderID); + // TODO: Error Check + gl.glAttachShader(nCompiledShaderID, nPixelShaderID); + } + + // Vertex Shader + if (!srcVertexShader.empty()) + { + nVertexShaderID = gl.glCreateShader(0x8B31); + const char* s = srcVertexShader.c_str(); + gl.glShaderSource(nVertexShaderID, 1, &s, nullptr); + gl.glCompileShader(nVertexShaderID); + // TODO: Error Check + gl.glAttachShader(nCompiledShaderID, nVertexShaderID); + } + + + // Geometry Shader + if (!srcGeometryShader.empty()) + { + nGeometryShaderID = gl.glCreateShader(0x8DD9); + const char* s = srcGeometryShader.c_str(); + gl.glShaderSource(nGeometryShaderID, 1, &s, nullptr); + gl.glCompileShader(nGeometryShaderID); + // TODO: Error Check + gl.glAttachShader(nCompiledShaderID, nGeometryShaderID); + } + + gl.glLinkProgram(nCompiledShaderID); + + return "OK"; + } + + int32_t Shader_GLSL33::CreateUniform(const std::string& name) + { + auto& gl = olc::apis::opengl::gl::Get(); + const char* s = name.c_str(); + mapUniforms.insert({ name, gl.glGetUniformLocation(nCompiledShaderID, s) }); + return GetUniform(name); + } + + + + bool Renderer_OGL33::CreateDevice(std::vector params, const RendererConfig& cfg) + { + config = cfg; + + + // Create OpenGL Device Context +#if OLC_HOST == OLC_HOST_WINDOWS + // "wgl*" all live in WinGDI + 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))) + { + lastError = RendererError::InvalidDCPixelFormat; + return false; + } + + if (!SetPixelFormat(glDeviceContext, pf, &pfd)) + { + lastError = RendererError::FailedToSetDCPixelFormat; + return false; + } + + if (!(glRenderContext = wglCreateContext(glDeviceContext))) + { + lastError = RendererError::FailedToCreateRenderContext; + return false; + } + + if (!wglMakeCurrent(glDeviceContext, glRenderContext)) + { + lastError = RendererError::FailedToSwitchRenderContext; + return false; + } + + //// Set Vertical Sync + //glSwapInterval = OGL_LOAD(glSwapInterval); + //if (locSwapInterval && !bVSYNC) locSwapInterval(0); + //bSync = bVSYNC; + +#endif + + // Can't load OpenGL API until context is loaded + auto& gl = olc::apis::opengl::gl::Get(); + if (!gl.HasLoaded()) + { + std::cout << "Error: Could not Load OpenGL!\n"; + lastError = RendererError::None; + return false; + } + + + // Create "Default" Shader + shaderDefault.SetPixelShaderSource( + "#version 330 core\n" + "layout(location = 0) out vec4 pixel;\n" + "in vec2 oTex;\n" + "in vec4 oCol;\n" + "uniform sampler2D sprTex;\n" + "void main(){pixel = texture(sprTex, oTex) * oCol;}" + ); + + shaderDefault.SetVertexShaderSource( + "#version 330 core\n" + "layout(location = 0) in vec4 aPos;\n" + "layout(location = 1) in vec4 aCol;\n" + "layout(location = 2) in vec2 aTex;\n" + "uniform mat4 mvp;\n" + "uniform int is3d;\n" + "uniform vec4 tint;\n" + "out vec2 oTex;\n" + "out vec4 oCol;\n" + "void main(){ if(is3d!=0) {gl_Position = mvp * vec4(aPos.x, aPos.y, aPos.z, 1.0); oTex = aTex;} else {float p = 1.0 / aPos.z; gl_Position = p * vec4(aPos.x, aPos.y, 0.0, 1.0); oTex = p * aTex;} oCol = aCol * tint;}" + ); + + shaderDefault.Compile(); + shaderDefault.CreateUniform("mvp"); + shaderDefault.CreateUniform("is3d"); + shaderDefault.CreateUniform("tint"); + + // Create "Default" Vertex Buffer / Vertex Attributes. This buffer is reused + // for all drawing operations. It's possible future versions may allow the + // creation of additional named buffers for repeated drawing operations with + // minimal overhead. + gl.glGenBuffers(1, &nDefaultVB); + gl.glGenVertexArrays(1, &nDefaultVA); + gl.glBindVertexArray(nDefaultVA); + gl.glBindBuffer(0x8892, nDefaultVB); + + // A big one is allocated to reduce shuffles in GPU memory + GPUTask::Vertex verts[OLC_GPU_MAX_VERTICES]; + gl.glBufferData(0x8892, sizeof(GPUTask::Vertex) * OLC_GPU_MAX_VERTICES, verts, 0x88E0); + + // Float Index 0 = x, 1 = y, 2 = z, 3 = w + gl.glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(GPUTask::Vertex), (void*)(0 * sizeof(float))); + gl.glEnableVertexAttribArray(0); + // Float Index 4 = (RGBA 8-bit x4) + gl.glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(GPUTask::Vertex), (void*)(4 * sizeof(float))); + gl.glEnableVertexAttribArray(1); + // Float Index 5 = u0, 6 = v0 + gl.glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(GPUTask::Vertex), (void*)(5 * sizeof(float))); + gl.glEnableVertexAttribArray(2); + // Float Index 7 = u1, 8 = v1 + gl.glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, sizeof(GPUTask::Vertex), (void*)(7 * sizeof(float))); + gl.glEnableVertexAttribArray(3); + // Float Index 9 = u2, 10 = v2 + gl.glVertexAttribPointer(4, 2, GL_FLOAT, GL_FALSE, sizeof(GPUTask::Vertex), (void*)(9 * sizeof(float))); + gl.glEnableVertexAttribArray(4); + // Float Index 11 = u3, 12 = v4 + gl.glVertexAttribPointer(5, 2, GL_FLOAT, GL_FALSE, sizeof(GPUTask::Vertex), (void*)(11 * sizeof(float))); + gl.glEnableVertexAttribArray(5); + + // Buffers are configured, unbind for now + gl.glBindBuffer(0x8892, 0); + gl.glBindVertexArray(0); + + + + + + // Create a null-texture so sampler doesnt fail. We don't have some of the core's helper + // functions here, so we construct it manually + imgBlank.Create({ 1,1 }); + imgBlank.SetGPUID(CreateTexture(imgBlank.Size())); + imgBlank.BindCPU(); + imgBlank.Pixel({ 0,0 }) = olc::Colour::WHITE; + imgBlank.BindGPU(); + WriteTexture(imgBlank.GetGPUID(), imgBlank); + + // Create a Frame Buffer Object for off-screen rendering things + gl.glGenFramebuffers(1, (GLuint*)&nDefaultFBO); + gl.glBindFramebuffer(36160U, nDefaultFBO); // GL_FRAMEBUFFER + // Attach 4 colour buffers + std::array attachments = { {36064U, 36065U, 36066U, 36067U} }; + gl.glDrawBuffers(4, attachments.data()); + // Unlink them from any existing image textures + //gl.glFramebufferTexture2D(36160U, attachments[0], GL_TEXTURE_2D, 0, 0); + //gl.glFramebufferTexture2D(36160U, attachments[1], GL_TEXTURE_2D, 0, 0); + //gl.glFramebufferTexture2D(36160U, attachments[2], GL_TEXTURE_2D, 0, 0); + //gl.glFramebufferTexture2D(36160U, attachments[3], GL_TEXTURE_2D, 0, 0); + // Unbind the FBO + gl.glBindFramebuffer(36160U, 0); + + glEnable(GL_TEXTURE_2D); // Turn on texturing + glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); + + lastError = RendererError::None; + return true; + } + + bool Renderer_OGL33::DestroyDevice() + { + auto& gl = olc::apis::opengl::gl::Get(); + +#if OLC_HOST == OLC_HOST_WINDOWS + wglDeleteContext(glRenderContext); +#endif + return false; + } + + uint32_t Renderer_OGL33::CreateTexture(const olc::vi2d& vSize, const olc::ImageConfig& cfg) + { + auto& gl = olc::apis::opengl::gl::Get(); + + // Curiously OpenGL doesnt actually care about the size of the texture + // as part of its creation. This matters later when we Write to texture + // resources on GPU + + uint32_t id = 0; + gl.glGenTextures(1, &id); + gl.glBindTexture(GL_TEXTURE_2D, id); + + if (cfg.Filtered) + { + gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + } + else + { + gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + } + + if (cfg.Clamp) + { + gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + } + else + { + gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + } + +#if OLC_HOST != OLC_HOST_EMSCRIPTEN + gl.glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); +#endif + + return id; + } + + bool Renderer_OGL33::WriteTexture(const uint32_t texid, olc::Image& image) + { + auto& gl = olc::apis::opengl::gl::Get(); + gl.glBindTexture(GL_TEXTURE_2D, image.GetGPUID()); + gl.glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.Size().x, image.Size().y, 0, GL_RGBA, GL_UNSIGNED_BYTE, image.Data()); + return true; + } + + bool Renderer_OGL33::ReadTexture(const uint32_t texid, olc::Image& image) + { + auto& gl = olc::apis::opengl::gl::Get(); + gl.glBindTexture(GL_TEXTURE_2D, image.GetGPUID()); + //gl.glReadPixels(0, 0, image.Size().x, image.Size().y, GL_RGBA, GL_UNSIGNED_BYTE, image.Data()); + gl.glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, image.Data()); + return true; + } + + bool Renderer_OGL33::DeleteTexture(const uint32_t texid) + { + auto& gl = olc::apis::opengl::gl::Get(); + gl.glDeleteTextures(1, &texid); + return true; + } + + bool Renderer_OGL33::AssignTextureSource(const uint32_t slot, const uint32_t texid) + { + auto& gl = olc::apis::opengl::gl::Get(); + + gl.glActiveTexture(0x84C0 + slot); // GL_TEXTURE0 + gl.glBindTexture(GL_TEXTURE_2D, texid); + return true; + } + + bool Renderer_OGL33::AssignTextureTarget(const uint32_t slot, const uint32_t texid) + { + auto& gl = olc::apis::opengl::gl::Get(); + + if (texid == 0) + { + gl.glBindFramebuffer(36160U, 0); + return true; + } + + // Bind FBO + gl.glBindFramebuffer(36160U, nDefaultFBO); + // Allocate target buffers + std::array attachments = + { { 36064U, 36065U, 36066U, 36067U, 36068U, 36069U, 36070U, 36071U } }; + gl.glDrawBuffers(1, attachments.data()); + // Bind buffers to textures + gl.glFramebufferTexture2D(36160U, 36064U + slot, GL_TEXTURE_2D, texid, 0); + return true; + } + + bool Renderer_OGL33::ApplyShader(const Shader& shader) + { + auto& gl = olc::apis::opengl::gl::Get(); + gl.glUseProgram(shader.GetShaderID()); + return true; + } + + bool Renderer_OGL33::ApplyDefaultShader() + { + auto& gl = olc::apis::opengl::gl::Get(); + gl.glUseProgram(shaderDefault.GetShaderID()); + return true; + } + + bool Renderer_OGL33::DoGPUTask(const olc::GPUTask& task) + { + auto& gl = olc::apis::opengl::gl::Get(); + + switch (task.task) + { + case GPUTask::Task::DrawPolygon: + { + gl.glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + if (task.pImage == nullptr) + AssignTextureSource(0, imgBlank.GetGPUID()); + else + AssignTextureSource(0, task.pImage->GetGPUID()); + + // Bind generic vertex buffer + gl.glBindVertexArray(nDefaultVA); + gl.glBindBuffer(0x8892, nDefaultVB); + + // Copy data from CPU to GPU + gl.glBufferData(0x8892, sizeof(GPUTask::Vertex) * task.vertexBuffer.size(), task.vertexBuffer.data(), 0x88E0); + + // Shader: Configure Rendering Mode + gl.glUniform1i(shaderDefault.GetUniform("is3d"), 0); + + // Shader: Apply MVP Matrix + gl.glUniformMatrix4fv(shaderDefault.GetUniform("mvp"), 1, false, task.mvpMatrix.data()); + + // Shader: Apply Global Tint + float f[4] = { + float(task.tint.r) / 255.0f, + float(task.tint.g) / 255.0f, + float(task.tint.b) / 255.0f, + float(task.tint.a) / 255.0f + }; + gl.glUniform4fv(shaderDefault.GetUniform("tint"), 1, f); + + // Apply Culling modes + if (task.cullmode == GPUTask::CullMode::None) + { + gl.glCullFace(GL_FRONT); + gl.glDisable(GL_CULL_FACE); + } + else if (task.cullmode == GPUTask::CullMode::ClockWise) + { + gl.glCullFace(GL_FRONT); + gl.glEnable(GL_CULL_FACE); + } + else if (task.cullmode == GPUTask::CullMode::CounterClockWise) + { + gl.glCullFace(GL_BACK); + gl.glEnable(GL_CULL_FACE); + } + + // Apply Depth Testing (if required) + if (task.bDepth) + gl.glEnable(GL_DEPTH_TEST); + + // Draw the thing! + if (task.bWireframe) + { + gl.glDrawArrays(GL_LINE_LOOP, 0, (GLsizei)task.vertexBuffer.size()); + } + else + { + if (task.structure == GPUTask::Structure::Fan) + gl.glDrawArrays(GL_TRIANGLE_FAN, 0, (GLsizei)task.vertexBuffer.size()); + else if (task.structure == GPUTask::Structure::Strip) + gl.glDrawArrays(GL_TRIANGLE_STRIP, 0, (GLsizei)task.vertexBuffer.size()); + else if (task.structure == GPUTask::Structure::List) + gl.glDrawArrays(GL_TRIANGLES, 0, (GLsizei)task.vertexBuffer.size()); + else if (task.structure == GPUTask::Structure::Line) + gl.glDrawArrays(GL_LINES, 0, (GLsizei)task.vertexBuffer.size()); + } + + if (task.bDepth) + gl.glDisable(GL_DEPTH_TEST); + + } + break; + } + + return true; + } + + bool Renderer_OGL33::ClearViewport(const olc::Pixel col, bool bDepth, bool bStencil) { auto& gl = olc::apis::opengl::gl::Get(); gl.glClearColor(float(col.r) / 255.0f, float(col.g) / 255.0f, float(col.b) / 255.0f, float(col.a) / 255.0f); @@ -2568,29 +3468,311 @@ namespace olc::gpu { auto& gl = olc::apis::opengl::gl::Get(); gl.glViewport(int(pos.x), int(pos.y), int(size.x), int(size.y)); + return true; + } + + bool Renderer_OGL33::DisplayPrepare() + { + auto& gl = olc::apis::opengl::gl::Get(); + + //gl.glUseProgram(shaderDefault.GetShaderID()); + gl.glEnable(GL_BLEND); + gl.glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + gl.glDepthFunc(GL_LESS); + //gl.glBindTexture(GL_TEXTURE_2D, imgBlank.GetGPUID()); + + return false; + } + + bool Renderer_OGL33::DisplayDraw(bool bVerticalSyncNow) + { + auto& gl = olc::apis::opengl::gl::Get(); + + +#if OLC_HOST == OLC_HOST_WINDOWS + SwapBuffers(glDeviceContext); +#endif + return false; } - bool Renderer_OGL33::DisplayPrepare() - { - auto& gl = olc::apis::opengl::gl::Get(); - return false; - } - bool Renderer_OGL33::DisplayDraw(bool bVerticalSyncNow) - { - auto& gl = olc::apis::opengl::gl::Get(); +} +#endif +#define PGE_GPU_IMPLEMENTED 1 +#endif + +#if defined(OLC_PGE_APPLICATION) && !defined(PGE_DRAW2D_IMPLEMENTED) +using namespace olc; + +Draw2D::Draw2D() +{ +} + +void Draw2D::SetGPU(olc::gpu::Renderer* const renderer) +{ + pRenderer = renderer; +} + +void Draw2D::SetTarget(olc::Image& image) +{ + ProcessGPUTasks(); + + // Store the target image + pTarget = ℑ + + // Create a transform to denormalise the image and present it + // in pixel space + transformTarget.scale(olc::vf2d(2.0f, 2.0f) / olc::vf2d(pTarget->Size())); + transformTarget.translate( + olc::vf2d( + -1.0f + (1.0f / float(pTarget->Size().x)), + -1.0f + (1.0f / float(pTarget->Size().y)) + )); + + AffineReset(); + + pRenderer->AssignTextureTarget(0, pTarget->GetGPUID()); + pRenderer->SetViewport({ 0,0 }, pTarget->Size()); +} + +void Draw2D::ClearTransform() +{ + transformTarget = olc::tf2d(); + transformAffine = olc::tf2d(); + transformCombined = olc::mf3d(); +} + +void Draw2D::SetTransform(const olc::tf2d& trans) +{ + transformTarget = trans; +} + +tf2d& olc::Draw2D::GetTransform() +{ + return transformAffine; +} + +void olc::Draw2D::ProcessGPUTasks() +{ + for (const auto& task : vecGPUTasks) + pRenderer->DoGPUTask(task); + + vecGPUTasks.clear(); +} + +void Draw2D::PrepareTargetForSW() +{ + if (pTarget->BoundToGPU()) + { + // Process GPU queue bound for the target + ProcessGPUTasks(); + + // Image resource is primed for GPU operations, bring it to CPU + pRenderer->ReadTexture(pTarget->GetGPUID(), *pTarget); + + // Image is now CPU bound + pTarget->BindCPU(); + } +} + +void Draw2D::PrepareTargetForHW() +{ + if (pTarget->BoundToCPU()) + { + // Image resource is primed for CPU operations, send it to GPU + pRenderer->WriteTexture(pTarget->GetGPUID(), *pTarget); + + // Image is now GPU bound + pTarget->BindGPU(); + } +} + +void Draw2D::PrepareImageForSW(olc::Image& image) +{ + if (image.BoundToGPU()) + { + // Process GPU queue bound for the target + ProcessGPUTasks(); + + // Image resource is primed for GPU operations, bring it to CPU + pRenderer->ReadTexture(image.GetGPUID(), image); + + // Image is now CPU bound + image.BindCPU(); + } +} + +void Draw2D::PrepareImageForHW(olc::Image& image) +{ + if (image.BoundToCPU()) + { + // Image resource is primed for CPU operations, send it to GPU + pRenderer->WriteTexture(image.GetGPUID(), image); + + // Image is now GPU bound + image.BindGPU(); + } +} + +void olc::Draw2D::AffineReset() +{ + transformAffine = olc::tf2d(); + transformCombined = transformAffine * transformTarget; +} + +void olc::Draw2D::AffineScale(const olc::vf2d& vScale) +{ + transformAffine.scale(vScale); + transformCombined = transformAffine * transformTarget; +} + +void olc::Draw2D::AffineOffset(const olc::vf2d& vOffset) +{ + transformAffine.translate(vOffset); + transformCombined = transformAffine * transformTarget; +} + +void olc::Draw2D::AffineRotate(const float& fTheta, const olc::vf2d& vPoint) +{ + transformAffine.rotate(fTheta, vPoint); + transformCombined = transformAffine * transformTarget; +} + +void Draw2D::Pixel(const olc::vf2d& pos, const olc::Pixel col) +{ + // Check if in bounds + olc::vf2d tpos = transformTarget.forward(pos); + if (tpos.x >= 0 && tpos.y >= 0 && tpos.x < pTarget->Size().x && tpos.y < pTarget->Size().y) + { + PrepareTargetForSW(); + pTarget->Pixel(tpos) = col; + } + + // otherwise do nothing +} + +olc::Pixel olc::Draw2D::GetPixel(olc::Image& image, const olc::vf2d& pos) +{ + PrepareImageForSW(image); + return image.Pixel(pos); +} + +GPUTask olc::Draw2D::TaskDrawPolygon(GPUTask::Structure structure, const std::vector& vPoints, const std::vector& vColours, const olc::Pixel tint) +{ + GPUTask task; + task.structure = structure; + task.bWireframe = true; + for (size_t i = 0; i < vPoints.size(); i++) + task.vertexBuffer.push_back({ vPoints[i].x, vPoints[i].y, 1.0f, 1.0f, vColours[i], 0, 0, 0,0, 0, 0, 0, 0}); + //task.mvpMatrix = transformCombined.m + task.tint = tint; + return task; +} + +GPUTask olc::Draw2D::TaskDrawPolygon(GPUTask::Structure structure, const std::vector& vPoints, const olc::Pixel colour, const olc::Pixel tint) +{ + GPUTask task; + task.structure = structure; + task.bWireframe = true; + for (const auto& v : vPoints) + task.vertexBuffer.push_back({ v.x, v.y, 1.0f, 1.0f, colour, 0, 0, 0,0, 0, 0, 0, 0 }); + task.tint = tint; + return task; +} + +GPUTask olc::Draw2D::TaskFillPolygon(GPUTask::Structure structure, const std::vector& vPoints, const std::vector& vColours, const olc::Pixel tint) +{ + return GPUTask(); +} + +GPUTask olc::Draw2D::TaskFillPolygon(GPUTask::Structure structure, const std::vector& vPoints, const olc::Pixel colour, const olc::Pixel tint) +{ + GPUTask task; + task.structure = structure; + for (const auto& v : vPoints) + task.vertexBuffer.push_back({ v.x, v.y, 1.0f, 1.0f, colour, 0, 0, 0,0, 0, 0, 0, 0 }); + task.tint = tint; + return task; +} + +GPUTask olc::Draw2D::TaskTexturedPolygon(GPUTask::Structure structure, const std::vector& vPoints, const std::vector& vColours, const std::vector& vTexCoords, olc::Image* const image, const olc::Pixel tint) +{ + GPUTask task; + for (size_t i = 0; i({ p1, p2 }), + col, + olc::Colour::WHITE + )); +} + +GPUTask Draw2D::Line(const olc::vf2d& p1, const olc::vf2d& p2, const olc::Pixel c1, const olc::Pixel c2) +{ + PrepareTargetForHW(); + return vecGPUTasks.emplace_back( + TaskDrawPolygon( + GPUTask::Structure::Line, + transformCombined.transform({ p1, p2 }), + { c1, c2 }, + olc::Colour::WHITE + )); +} + +GPUTask olc::Draw2D::Rect(const olc::vf2d& pos, const olc::vf2d& size, const olc::Pixel col) +{ + PrepareTargetForHW(); + return vecGPUTasks.emplace_back( + TaskDrawPolygon( + GPUTask::Structure::Fan, + transformCombined.transform({ { pos.x, pos.y }, { pos.x + size.x, pos.y }, { pos.x + size.x, pos.y + size.y }, { pos.x, pos.y + size.y } }), + col, + olc::Colour::WHITE + )); +} -#if OLC_HOST == OLC_HOST_WINDOWS - SwapBuffers(glDeviceContext); -#endif +GPUTask olc::Draw2D::FillRect(const olc::vf2d& pos, const olc::vf2d& size, const olc::Pixel col) +{ + PrepareTargetForHW(); + return vecGPUTasks.emplace_back( + TaskFillPolygon( + GPUTask::Structure::Fan, + transformCombined.transform({ { pos.x, pos.y }, { pos.x + size.x, pos.y }, { pos.x + size.x, pos.y + size.y }, { pos.x, pos.y + size.y } }), + col, + olc::Colour::WHITE + )); +} - return false; - } +GPUTask olc::Draw2D::Image(olc::Image& image, const olc::vf2d& pos, const olc::vf2d& size) +{ + // Ensure source image is up to date in VRAM + PrepareImageForHW(image); + + PrepareTargetForHW(); + return vecGPUTasks.emplace_back( + TaskTexturedPolygon( + GPUTask::Structure::Fan, + transformCombined.transform({ { pos.x, pos.y }, { pos.x + size.x, pos.y }, { pos.x + size.x, pos.y + size.y }, { pos.x, pos.y + size.y } }), + { olc::Colour::WHITE, olc::Colour::WHITE, olc::Colour::WHITE, olc::Colour::WHITE }, + { {0,0}, {1,0}, {1,1}, {0,1} }, + &image + )); } -#endif -#define PGE_GPU_IMPLEMENTED 1 + + +#define PGE_DRAW2D_IMPLEMENTED 1 #endif #if defined(OLC_PGE_APPLICATION) && !defined(PGE_CORE_IMPLEMENTED) @@ -2628,7 +3810,7 @@ namespace olc // Link this olc::Window to a host resource if (host) - host->AddWindowFrame(this, { 30,30 }, { 250, 240 }, false); + host->AddWindowFrame(this, { 30,30 }, config.vPixelSize * config.vScreenSize, false); coreActive = true; coreThread = std::thread(&PixelGameEngine::EngineThread, this); @@ -2678,7 +3860,24 @@ namespace olc } bool PixelGameEngine::CreateImageFromFile(olc::Image& image, const std::string& sFileName, const ImageConfig& cfg) - { + { + if (imageloader->CreateImageFromFile(image, sFileName)) + { + // Image has loaded ok, and populated into pixel vector + // + // Create GPU Image + auto id = gpu->CreateTexture(image.Size(), cfg); + if (id == 0) + { + image.Create({ 0,0 }); + return false; + } + + // Associate CPU object with GPU Resource + image.SetGPUID(id); + return true; + } + return false; } @@ -2692,10 +3891,10 @@ namespace olc return false; } - bool PixelGameEngine::WriteImageToMemory(const olc::Image& image, std::vector bytes, const std::string& sFileName) - { - return false; - } + //bool PixelGameEngine::WriteImageToMemory(const olc::Image& image, std::vector bytes, const std::string& sFileName) + //{ + // return false; + //} void PixelGameEngine::DestroyImage(olc::Image& image) { @@ -2721,6 +3920,9 @@ namespace olc using namespace std::chrono_literals; timeFrame2 = std::chrono::steady_clock::now(); timeFrame1 = std::chrono::steady_clock::now(); + + // Initialise ImageLoader Interface + imageloader = std::make_unique(); // Initialise GPU Interface - This thread is the context @@ -2738,6 +3940,10 @@ namespace olc return; } + + CreateImage(imgPrimary, config.vScreenSize); + gpu->WriteTexture(imgPrimary.GetGPUID(), imgPrimary); + if (!OnUserCreate()) { // Creation process signalled abort @@ -2793,7 +3999,7 @@ namespace olc // Wait for vertical sync if required. // Note: Child windows will never vsync as waiting for each buffer swap with vsync - // divides up teh frame rate budget across the windows. + // divides up the frame rate budget across the windows. if (gpu->GetConfig().VerticalSync) { host->SyncWithDesktopComposite(); @@ -2853,6 +4059,11 @@ namespace olc return pixels.data(); } + olc::Pixel& Image::Pixel(const olc::vf2d& pos) + { + return pixels[int(pos.y) * dimensions.x + int(pos.x)]; + } + const ImageConfig& Image::GetConfig() const { return config; @@ -2868,8 +4079,349 @@ namespace olc gpuResourceID = id; } + std::vector& Image::GetPixels() + { + return pixels; + } + + bool Image::BoundToGPU() const + { + return onGPU; + } + + bool Image::BoundToCPU() const + { + return onCPU; + } + + void Image::BindGPU() + { + onGPU = true; + onCPU = false; + } + + void Image::BindCPU() + { + onCPU = true; + onGPU = false; + } + }; #define PGE_IMAGE_IMPLEMENTED 1 #endif +#if defined(OLC_PGE_APPLICATION) && !defined(PGE_HW_MOUSE_IMPLEMENTED) +namespace olc::hw +{ + const Button& Mouse::GetButton(const int nButton) const + { + return buttons.at(nButton); + } + + const olc::vf2d& Mouse::GetPosition() const + { + return position; + } + + void Mouse::SetPosition(const olc::vf2d& pos) + { + position_in = pos; + } + + void Mouse::SetButton(const int nButton, bool state) + { + buttons_new[nButton] = state; + } + + void Mouse::UpdateState() + { + for (size_t i=0; iUpdateWindowFrameTitle(this); + } + + bool Window::OnUserCreate() + { + return false; + } + + bool Window::OnUserUpdate(float fElapsedTime) + { + return false; + } + + bool Window::OnUserDestroy() + { + return false; + } + + bool Window::olc_OnMouseButton(const uint8_t nButton, const bool bPressed) + { + mouse.SetButton(nButton, bPressed); + return false; + } + + bool Window::olc_OnMouseMove(const olc::vi2d& vMousePos) + { + mouse.SetPosition(olc::vf2d(vMousePos) / olc::vf2d(GetSize()) * imgPrimary.Size()); + return true; + } + + bool Window::olc_OnMouseWheel(const int32_t nScroll) + { + return false; + } + + bool Window::olc_OnMouseFocus(const bool bHasFocus) + { + return false; + } + + bool Window::olc_OnWindowPosition(const olc::vi2d& vWindowPos) + { + return false; + } + + bool Window::olc_OnWindowSize(const olc::vi2d& vWindowSize) + { + return SetSize(vWindowSize); + } + + bool Window::olc_ShouldRemove() const + { + return bShouldRemove; + } + + bool Window::olc_WindowUpdate(const float fElapsedTime, olc::gpu::Renderer* const gpu) + { + // Environmental changes + + // Input Changes + mouse.UpdateState(); + + + draw.SetGPU(gpu); + draw.SetTarget(imgPrimary); + + + gpu->DisplayPrepare(); + + + gpu->AssignTextureTarget(0, imgPrimary.GetGPUID()); + gpu->SetViewport({ 0,0 }, imgPrimary.Size()); + gpu->ClearViewport(olc::Colour::BLACK, true, true); + + gpu->ApplyDefaultShader(); + + // User Update + if (!OnUserUpdate(fElapsedTime)) + { + // User has requested termination of window by returning false + if (OnUserDestroy()) + { + // User has confirmed window destruction by returning true + bShouldRemove = true; + } + } + + + // Finialise any outstanding tasks + draw.ProcessGPUTasks(); + + // Take the window's completed "screen" and draw it as a textured quad to the backbuffer + gpu->AssignTextureTarget(0, 0); + gpu->SetViewport({ 0,0 }, GetSize()); + gpu->ClearViewport(olc::Colour::BLANK, true, true); + draw.ClearTransform(); + draw.Image(imgPrimary, { -1.0,1.0 }, { 2.0f,-2.0f }); + draw.ProcessGPUTasks(); + + // Update Window's primary surface + gpu->DisplayDraw(); + + return true; + } + + size_t Window::GetUID() const + { + return nUniqueID; + } + + const olc::vi2d& Window::GetSize() const + { + return vWindowSize; + } + + const olc::vi2d& Window::GetPosition() const + { + return vWindowPos; + } + + bool Window::SetSize(const olc::vi2d& vSize) + { + // Request host to change geometry of window object + + vWindowSize = vSize; + return true; + } + + bool Window::SetPosition(const olc::vi2d& vPosition) + { + // Request host to change geometry of window object + + vWindowPos = vPosition; + return true; + } + + const std::string& Window::GetTitle() const + { + return sFrameTitle; + } + + bool Window::SetTitle(const std::string& sTitle) + { + sFrameTitle = sTitle; + pHost->UpdateWindowFrameTitle(this); + return false; + } + + bool Window::olc_PrimaryWindowInit() + { + return false; + } +}; +#define PGE_WINDOW_IMPLEMENTED 1 +#endif + +#if defined(OLC_PGE_APPLICATION) && !defined(PGE_IMAGELOADER_IMPLEMENTED) +#if OLC_IMAGELOADER == OLC_IMAGELOADER_WINGDI +namespace olc::imload +{ + namespace internals + { + 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; + } + + static class GDIPlusStartup + { + public: + GDIPlusStartup() + { + Gdiplus::GdiplusStartupInput startupInput; + GdiplusStartup(&token, &startupInput, NULL); + } + + ULONG_PTR token; + + ~GDIPlusStartup() + { + Gdiplus::GdiplusShutdown(token); + } + + } gdistartup; + } + + bool ImageLoader_WinGDI::CreateImageFromFile(olc::Image& image, const std::string& sFileName) + { + // Check file exists + if (!std::filesystem::exists(sFileName)) + return false; // File doesn't exist + + // Load file into windows "bitmap". 1992 calling... + Gdiplus::Bitmap* bmp = nullptr; + bmp = Gdiplus::Bitmap::FromFile(internals::ConvertS2W(sFileName).c_str()); + if (bmp->GetLastStatus() != Gdiplus::Ok) + return false; // File wasn't valid + + // Need to swizzle each pixel... + image.Create(olc::vi2d(bmp->GetWidth(), bmp->GetHeight())); + for (int y = 0; y < image.Size().y; y++) + for (int x = 0; x < image.Size().x; x++) + { + Gdiplus::Color c; + bmp->GetPixel(x, y, &c); + image.Pixel(olc::vi2d(x, y)) = olc::Pixel(c.GetRed(), c.GetGreen(), c.GetBlue(), c.GetAlpha()); + } + + // All done + delete bmp; + return true; + } + + bool ImageLoader_WinGDI::CreateImageFromMemory(olc::Image& image, const uint8_t* data, const size_t bytes) + { + return false; + } + + bool ImageLoader_WinGDI::CreateImageFromMemory(olc::Image& image, const std::vector& data) + { + return false; + } + + bool ImageLoader_WinGDI::WriteImageToFile(const olc::Image& image, const std::string& sFileName) + { + return false; + } + + bool ImageLoader_WinGDI::WriteImageToMemoryFile(olc::Image& image, const std::vector& data) + { + return false; + } +} +#endif +#define PGE_IMAGELOADER_IMPLEMENTED 1 +#endif diff --git a/dev/msvc/olcPGE3_BuildSH/test_sh.cpp b/dev/msvc/olcPGE3_BuildSH/test_sh.cpp index f8c3480b..2b7e2170 100644 --- a/dev/msvc/olcPGE3_BuildSH/test_sh.cpp +++ b/dev/msvc/olcPGE3_BuildSH/test_sh.cpp @@ -9,14 +9,58 @@ class Example : public olc::PixelGameEngine } + olc::Image imTest; + + olc::Image sprite1; + + float fAngle = 0.0f; + public: bool OnUserCreate() override { + CreateImage(imTest, { 64,64 }); + CreateImageFromFile(sprite1, "E:/assets/Graphics/Tiles_SideOn/HiQ/FactoryPack/png/256/objects/static/Computer (1).png"); + return true; } bool OnUserUpdate(float fElapsedTime) override { + fAngle += fElapsedTime; + + + + + draw.SetTarget(imTest); + draw.FillRect({ 0,0 }, imTest.Size(), olc::Colour::BLUE); + + draw.AffineRotate(fAngle, { 32.0f, 32.0f }); + + draw.Rect({ 5,5 }, { 10,10 }, olc::Colour::YELLOW); + draw.Line({ 0,0 }, { 20, 20 }); + + + draw.SetTarget(imgPrimary); + draw.AffineReset(); + + draw.FillRect({ 0,0 }, imgPrimary.Size(), olc::Colour::VERY_DARK_MAGENTA); + + draw.Line({ 0,0 }, imgPrimary.Size() - 1, olc::Colour::RED); + draw.Line(olc::vf2d(imgPrimary.Size().x - 1, 0), olc::vf2d(0, imgPrimary.Size().y - 1), olc::Colour::GREEN); + + if (mouse.GetButton(0).bHeld) + draw.Line({ 0,0 }, mouse.GetPosition(), olc::Colour::TANGERINE); + + draw.AffineRotate(fAngle * 0.2f, imgPrimary.Size() * 0.5f); + draw.Rect({ 0,0 }, imgPrimary.Size()); + draw.Image(imTest, { 10, 10 }, { 64, 64 }); + draw.Image(imTest, { 100, 10 }, { 64, 64 }); + draw.Image(imTest, { 10, 100 }, { 64, 64 }); + draw.Image(sprite1, { 100, 100 }, { 64, 64 }); + + olc::Pixel p = draw.GetPixel(imTest, { 8,5 }); + + draw.FillRect({ 200,200 }, { 10,10 }, p); return true; } }; diff --git a/dev/src/api_opengl.cpp b/dev/src/api_opengl.cpp index c1b0b222..6d254174 100644 --- a/dev/src/api_opengl.cpp +++ b/dev/src/api_opengl.cpp @@ -17,88 +17,364 @@ namespace olc::apis::opengl { // Load all the OpenGL API entry points bLoaded = true; - bLoaded &= (glCreateShader = OGL_LOAD(glCreateShader)) != nullptr; - bLoaded &= (glCreateProgram = OGL_LOAD(glCreateProgram)) != nullptr; - bLoaded &= (glDeleteShader = OGL_LOAD(glDeleteShader)) != nullptr; - bLoaded &= (glCompileShader = OGL_LOAD(glCompileShader)) != nullptr; - bLoaded &= (glLinkProgram = OGL_LOAD(glLinkProgram)) != nullptr; - bLoaded &= (glDeleteProgram = OGL_LOAD(glDeleteProgram)) != nullptr; - bLoaded &= (glAttachShader = OGL_LOAD(glAttachShader)) != nullptr; - bLoaded &= (glBindBuffer = OGL_LOAD(glBindBuffer)) != nullptr; - bLoaded &= (glBufferData = OGL_LOAD(glBufferData)) != nullptr; - bLoaded &= (glGenBuffers = OGL_LOAD(glGenBuffers)) != nullptr; - bLoaded &= (glVertexAttribPointer = OGL_LOAD(glVertexAttribPointer)) != nullptr; - bLoaded &= (glEnableVertexAttribArray = OGL_LOAD(glEnableVertexAttribArray)) != nullptr; - bLoaded &= (glUseProgram = OGL_LOAD(glUseProgram)) != nullptr; - bLoaded &= (glBindVertexArray = OGL_LOAD(glBindVertexArray)) != nullptr; - bLoaded &= (glGenVertexArrays = OGL_LOAD(glGenVertexArrays)) != nullptr; - bLoaded &= (glGetShaderInfoLog = OGL_LOAD(glGetShaderInfoLog)) != nullptr; - bLoaded &= (glGetUniformLocation = OGL_LOAD(glGetUniformLocation)) != nullptr; - bLoaded &= (glUniform1f = OGL_LOAD(glUniform1f)) != nullptr; - bLoaded &= (glUniform1i = OGL_LOAD(glUniform1i)) != nullptr; - bLoaded &= (glUniform2fv = OGL_LOAD(glUniform2fv)) != nullptr; - bLoaded &= (glUniform4fv = OGL_LOAD(glUniform4fv)) != nullptr; - bLoaded &= (glUniformMatrix4fv = OGL_LOAD(glUniformMatrix4fv)) != nullptr; - bLoaded &= (glActiveTexture = OGL_LOAD(glActiveTexture)) != nullptr; - bLoaded &= (glGenFrameBuffers = OGL_LOAD(glGenFrameBuffers)) != nullptr; - bLoaded &= (glBindFrameBuffer = OGL_LOAD(glBindFrameBuffer)) != nullptr; - bLoaded &= (glCheckFrameBufferStatus = OGL_LOAD(glCheckFrameBufferStatus)) != nullptr; - bLoaded &= (glDeleteFrameBuffers = OGL_LOAD(glDeleteFrameBuffers)) != nullptr; - bLoaded &= (glFrameBufferTexture2D = OGL_LOAD(glFrameBufferTexture2D)) != nullptr; - bLoaded &= (glDrawBuffers = OGL_LOAD(glDrawBuffers)) != nullptr; - bLoaded &= (glBlendFuncSeparate = OGL_LOAD(glBlendFuncSeparate)) != nullptr; - - if (!bLoaded) - return false; // API load has failed - + bLoaded &= (_glCreateShader = OGL_LOAD(glCreateShader)) != nullptr; + bLoaded &= (_glCreateProgram = OGL_LOAD(glCreateProgram)) != nullptr; + bLoaded &= (_glShaderSource = OGL_LOAD(glShaderSource)) != nullptr; + bLoaded &= (_glDeleteShader = OGL_LOAD(glDeleteShader)) != nullptr; + bLoaded &= (_glCompileShader = OGL_LOAD(glCompileShader)) != nullptr; + bLoaded &= (_glLinkProgram = OGL_LOAD(glLinkProgram)) != nullptr; + bLoaded &= (_glDeleteProgram = OGL_LOAD(glDeleteProgram)) != nullptr; + bLoaded &= (_glAttachShader = OGL_LOAD(glAttachShader)) != nullptr; + bLoaded &= (_glBindBuffer = OGL_LOAD(glBindBuffer)) != nullptr; + bLoaded &= (_glBufferData = OGL_LOAD(glBufferData)) != nullptr; + bLoaded &= (_glGenBuffers = OGL_LOAD(glGenBuffers)) != nullptr; + bLoaded &= (_glVertexAttribPointer = OGL_LOAD(glVertexAttribPointer)) != nullptr; + bLoaded &= (_glEnableVertexAttribArray = OGL_LOAD(glEnableVertexAttribArray)) != nullptr; + bLoaded &= (_glUseProgram = OGL_LOAD(glUseProgram)) != nullptr; + bLoaded &= (_glBindVertexArray = OGL_LOAD(glBindVertexArray)) != nullptr; + bLoaded &= (_glGenVertexArrays = OGL_LOAD(glGenVertexArrays)) != nullptr; + bLoaded &= (_glGetShaderInfoLog = OGL_LOAD(glGetShaderInfoLog)) != nullptr; + bLoaded &= (_glGetUniformLocation = OGL_LOAD(glGetUniformLocation)) != nullptr; + bLoaded &= (_glUniform1f = OGL_LOAD(glUniform1f)) != nullptr; + bLoaded &= (_glUniform1i = OGL_LOAD(glUniform1i)) != nullptr; + bLoaded &= (_glUniform2fv = OGL_LOAD(glUniform2fv)) != nullptr; + bLoaded &= (_glUniform4fv = OGL_LOAD(glUniform4fv)) != nullptr; + bLoaded &= (_glUniformMatrix4fv = OGL_LOAD(glUniformMatrix4fv)) != nullptr; + bLoaded &= (_glActiveTexture = OGL_LOAD(glActiveTexture)) != nullptr; + bLoaded &= (_glGenFramebuffers = OGL_LOAD(glGenFramebuffers)) != nullptr; + bLoaded &= (_glBindFramebuffer = OGL_LOAD(glBindFramebuffer)) != nullptr; + bLoaded &= (_glCheckFramebufferStatus = OGL_LOAD(glCheckFramebufferStatus)) != nullptr; + bLoaded &= (_glDeleteFramebuffers = OGL_LOAD(glDeleteFramebuffers)) != nullptr; + bLoaded &= (_glFramebufferTexture2D = OGL_LOAD(glFramebufferTexture2D)) != nullptr; + bLoaded &= (_glDrawBuffers = OGL_LOAD(glDrawBuffers)) != nullptr; + bLoaded &= (_glBlendFuncSeparate = OGL_LOAD(glBlendFuncSeparate)) != nullptr; + + return bLoaded; + } + bool gl::CheckError(const std::source_location loc) + { +#if OLC_GPU_ERRORCHECK == 1 + GLenum err; + bool bWasError = false; + std::stringstream sLocation; + sLocation + << loc.file_name() << '(' + << loc.line() << ':' + << loc.column() << ") `" + << loc.function_name() << "`: "; + while ((err = ::glGetError()) != GL_NO_ERROR) + { + switch (err) + { + case GL_INVALID_ENUM: + sLocation << "OGL33 Error: GL_INVALID_ENUM\n"; break; + case GL_INVALID_VALUE: + sLocation << "OGL33 Error: GL_INVALID_VALUE\n"; break; + case GL_INVALID_OPERATION: + sLocation << "OGL33 Error: GL_INVALID_OPERATION\n"; break; + case 0x0506: //GL_INVALID_FRAMEBUFFER_OPERATION: + sLocation << "OGL33 Error: GL_INVALID_FRAMEBUFFER_OPERATION\n"; break; + case GL_OUT_OF_MEMORY: + sLocation << "OGL33 Error: GL_OUT_OF_MEMORY\n"; break; + case GL_STACK_UNDERFLOW: + sLocation << "OGL33 Error: GL_STACK_UNDERFLOW\n"; break; + case GL_STACK_OVERFLOW: + sLocation << "OGL33 Error: GL_STACK_OVERFLOW\n"; break; + } + bWasError = true; - return bLoaded; + std::cout << sLocation.str() << "\n"; + sLocation.clear(); + } + return bWasError; +#else + return false; +#endif } void gl::glGenTextures(GLsizei n, GLuint* textures) { ::glGenTextures(n, textures); + CheckError(); } void gl::glBindTexture(GLenum target, GLuint texture) { ::glBindTexture(target, texture); + CheckError(); } void gl::glTexParameteri(GLenum target, GLenum pname, GLint param) { ::glTexParameteri(target, pname, param); + CheckError(); } void gl::glTexEnvf(GLenum target, GLenum pname, GLfloat param) { ::glTexEnvf(target, pname, param); + CheckError(); } void gl::glDeleteTextures(GLsizei n, const GLuint* textures) { ::glDeleteTextures(n, textures); + CheckError(); } void gl::glTexImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid* pixels) { ::glTexImage2D(target, level, internalformat, width, height, border, format, type, pixels); + CheckError(); } void gl::glClear(GLbitfield mask) { ::glClear(mask); + CheckError(); } void gl::glViewport(GLint x, GLint y, GLsizei width, GLsizei height) { ::glViewport(x, y, width, height); + CheckError(); } void gl::glClearColor(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) { ::glClearColor(red, green, blue, alpha); + CheckError(); + } + + void gl::glReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void* data) + { + ::glReadPixels(x, y, width, height, format, type, data); + CheckError(); + } + + void gl::glCullFace(GLenum mode) + { + ::glCullFace(mode); + CheckError(); + } + + void gl::glEnable(GLenum cap) + { + ::glEnable(cap); + CheckError(); + } + + void gl::glDisable(GLenum cap) + { + ::glDisable(cap); + CheckError(); + } + + void gl::glDrawArrays(GLenum mode, GLint first, GLsizei count) + { + ::glDrawArrays(mode, first, count); + CheckError(); + } + + void gl::glBlendFunc(GLenum sfactor, GLenum dfactor) + { + ::glBlendFunc(sfactor, dfactor); + CheckError(); + } + + void gl::glDepthFunc(GLenum func) + { + ::glDepthFunc(func); + CheckError(); + } + + void gl::glGetTexImage(GLenum target, GLint level, GLenum format, GLenum type, void* pixels) + { + ::glGetTexImage(target, level, format, type, pixels); + CheckError(); + } + + GLuint gl::glCreateShader(GLenum type) + { + return _glCreateShader(type); + } + + GLuint gl::glCreateProgram(void) + { + return _glCreateProgram(); + } + + void gl::glShaderSource(GLuint shader, GLsizei count, const GLchar** string, const GLint* length) + { + _glShaderSource(shader, count, string, length); + CheckError(); + } + + void gl::glDeleteShader(GLuint shader) + { + _glDeleteShader(shader); + CheckError(); + } + + void gl::glCompileShader(GLuint shader) + { + _glCompileShader(shader); + CheckError(); + } + + void gl::glLinkProgram(GLuint program) + { + _glLinkProgram(program); + CheckError(); + } + + void gl::glDeleteProgram(GLuint program) + { + _glDeleteProgram(program); + CheckError(); + } + + void gl::glAttachShader(GLuint program, GLuint shader) + { + _glAttachShader(program, shader); + CheckError(); + } + + void gl::glBindBuffer(GLenum target, GLuint buffer) + { + _glBindBuffer(target, buffer); + CheckError(); + } + + void gl::glBufferData(GLenum target, GLsizeiptr size, const void* data, GLenum usage) + { + _glBufferData(target, size, data, usage); + CheckError(); + } + + void gl::glGenBuffers(GLsizei n, GLuint* buffers) + { + _glGenBuffers(n, buffers); + CheckError(); + } + + void gl::glVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void* pointer) + { + _glVertexAttribPointer(index, size, type, normalized, stride, pointer); + CheckError(); + } + + void gl::glEnableVertexAttribArray(GLuint index) + { + _glEnableVertexAttribArray(index); + CheckError(); + } + + void gl::glUseProgram(GLuint program) + { + _glUseProgram(program); + CheckError(); + } + + void gl::glBindVertexArray(GLuint array) + { + _glBindVertexArray(array); + CheckError(); + } + + void gl::glGenVertexArrays(GLsizei n, GLuint* arrays) + { + _glGenVertexArrays(n, arrays); + CheckError(); + } + + void gl::glGetShaderInfoLog(GLuint shader, GLsizei bufSize, GLsizei* length, GLchar* infoLog) + { + _glGetShaderInfoLog(shader, bufSize, length, infoLog); + CheckError(); + } + + GLint gl::glGetUniformLocation(GLuint program, const GLchar* name) + { + return _glGetUniformLocation(program, name); + } + + void gl::glUniform1f(GLint location, GLfloat v0) + { + _glUniform1f(location, v0); + CheckError(); + } + + void gl::glUniform1i(GLint location, GLint v0) + { + _glUniform1i(location, v0); + CheckError(); + } + + void gl::glUniform2fv(GLint location, GLsizei count, const GLfloat* value) + { + _glUniform2fv(location, count, value); + CheckError(); + } + + void gl::glUniform4fv(GLint location, GLsizei count, const GLfloat* value) + { + _glUniform4fv(location, count, value); + CheckError(); + } + + void gl::glUniformMatrix4fv(GLint location, GLsizei count, GLboolean trasnpose, const GLfloat* value) + { + _glUniformMatrix4fv(location, count, trasnpose, value); + CheckError(); + } + + void gl::glActiveTexture(GLenum texture) + { + _glActiveTexture(texture); + CheckError(); + } + + void gl::glGenFramebuffers(GLsizei n, GLuint* ids) + { + _glGenFramebuffers(n, ids); + CheckError(); + } + + void gl::glBindFramebuffer(GLenum target, GLuint fb) + { + _glBindFramebuffer(target, fb); + CheckError(); + } + + GLenum gl::glCheckFramebufferStatus(GLenum target) + { + return _glCheckFramebufferStatus(target); + } + + void gl::glDeleteFramebuffers(GLsizei n, const GLuint* fbs) + { + _glDeleteFramebuffers(n, fbs); + CheckError(); + } + + void gl::glFramebufferTexture2D(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) + { + _glFramebufferTexture2D(target, attachment, textarget, texture, level); + CheckError(); + } + + void gl::glDrawBuffers(GLsizei n, const GLenum* bufs) + { + _glDrawBuffers(n, bufs); + CheckError(); + } + + void gl::glBlendFuncSeparate(GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha) + { + _glBlendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha); + CheckError(); } } //! END IMPLEMENTATION \ No newline at end of file diff --git a/dev/src/api_opengl.h b/dev/src/api_opengl.h index d7bd033f..13b36ea8 100644 --- a/dev/src/api_opengl.h +++ b/dev/src/api_opengl.h @@ -3,8 +3,15 @@ //! START CUSTOMHEADER #include "config.h" + //! END CUSTOMHEADER +//! START STDHEADER GLOBAL +#include +#include +#include +//! END STDHEADER + //! START OPENGL_CONFIG @@ -68,6 +75,7 @@ namespace olc typedef GLuint CALLSTYLE glCreateShader_t(GLenum type); typedef GLuint CALLSTYLE glCreateProgram_t(void); + typedef void CALLSTYLE glShaderSource_t(GLuint shader, GLsizei count, const GLchar** string, const GLint* length); typedef void CALLSTYLE glDeleteShader_t(GLuint shader); typedef void CALLSTYLE glCompileShader_t(GLuint shader); typedef void CALLSTYLE glLinkProgram_t(GLuint program); @@ -89,11 +97,11 @@ namespace olc typedef void CALLSTYLE glUniform4fv_t(GLint location, GLsizei count, const GLfloat* value); typedef void CALLSTYLE glUniformMatrix4fv_t(GLint location, GLsizei count, GLboolean trasnpose, const GLfloat* value); typedef void CALLSTYLE glActiveTexture_t(GLenum texture); - typedef void CALLSTYLE glGenFrameBuffers_t(GLsizei n, GLuint* ids); - typedef void CALLSTYLE glBindFrameBuffer_t(GLenum target, GLuint fb); - typedef GLenum CALLSTYLE glCheckFrameBufferStatus_t(GLenum target); - typedef void CALLSTYLE glDeleteFrameBuffers_t(GLsizei n, const GLuint* fbs); - typedef void CALLSTYLE glFrameBufferTexture2D_t(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); + typedef void CALLSTYLE glGenFramebuffers_t(GLsizei n, GLuint* ids); + typedef void CALLSTYLE glBindFramebuffer_t(GLenum target, GLuint fb); + typedef GLenum CALLSTYLE glCheckFramebufferStatus_t(GLenum target); + typedef void CALLSTYLE glDeleteFramebuffers_t(GLsizei n, const GLuint* fbs); + typedef void CALLSTYLE glFramebufferTexture2D_t(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); typedef void CALLSTYLE glDrawBuffers_t(GLsizei n, const GLenum* bufs); typedef void CALLSTYLE glBlendFuncSeparate_t(GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); @@ -108,6 +116,7 @@ namespace olc { public: static gl& Get(); + inline bool HasLoaded() const { return bLoaded; } private: gl() = default; @@ -116,37 +125,75 @@ namespace olc private: bool LoadAll(); + protected: + glCreateShader_t* _glCreateShader = nullptr; + glCreateProgram_t* _glCreateProgram = nullptr; + glShaderSource_t* _glShaderSource = nullptr; + glDeleteShader_t* _glDeleteShader = nullptr; + glCompileShader_t* _glCompileShader = nullptr; + glLinkProgram_t* _glLinkProgram = nullptr; + glDeleteProgram_t* _glDeleteProgram = nullptr; + glAttachShader_t* _glAttachShader = nullptr; + glBindBuffer_t* _glBindBuffer = nullptr; + glBufferData_t* _glBufferData = nullptr; + glGenBuffers_t* _glGenBuffers = nullptr; + glVertexAttribPointer_t* _glVertexAttribPointer = nullptr; + glEnableVertexAttribArray_t* _glEnableVertexAttribArray = nullptr; + glUseProgram_t* _glUseProgram = nullptr; + glBindVertexArray_t* _glBindVertexArray = nullptr; + glGenVertexArrays_t* _glGenVertexArrays = nullptr; + glGetShaderInfoLog_t* _glGetShaderInfoLog = nullptr; + glGetUniformLocation_t* _glGetUniformLocation = nullptr; + glUniform1f_t* _glUniform1f = nullptr; + glUniform1i_t* _glUniform1i = nullptr; + glUniform2fv_t* _glUniform2fv = nullptr; + glUniform4fv_t* _glUniform4fv = nullptr; + glUniformMatrix4fv_t* _glUniformMatrix4fv = nullptr; + glActiveTexture_t* _glActiveTexture = nullptr; + glGenFramebuffers_t* _glGenFramebuffers = nullptr; + glBindFramebuffer_t* _glBindFramebuffer = nullptr; + glCheckFramebufferStatus_t* _glCheckFramebufferStatus = nullptr; + glDeleteFramebuffers_t* _glDeleteFramebuffers = nullptr; + glFramebufferTexture2D_t* _glFramebufferTexture2D = nullptr; + glDrawBuffers_t* _glDrawBuffers = nullptr; + glBlendFuncSeparate_t* _glBlendFuncSeparate = nullptr; + public: - glCreateShader_t* glCreateShader = nullptr; - glCreateProgram_t* glCreateProgram = nullptr; - glDeleteShader_t* glDeleteShader = nullptr; - glCompileShader_t* glCompileShader = nullptr; - glLinkProgram_t* glLinkProgram = nullptr; - glDeleteProgram_t* glDeleteProgram = nullptr; - glAttachShader_t* glAttachShader = nullptr; - glBindBuffer_t* glBindBuffer = nullptr; - glBufferData_t* glBufferData = nullptr; - glGenBuffers_t* glGenBuffers = nullptr; - glVertexAttribPointer_t* glVertexAttribPointer = nullptr; - glEnableVertexAttribArray_t* glEnableVertexAttribArray = nullptr; - glUseProgram_t* glUseProgram = nullptr; - glBindVertexArray_t* glBindVertexArray = nullptr; - glGenVertexArrays_t* glGenVertexArrays = nullptr; - glGetShaderInfoLog_t* glGetShaderInfoLog = nullptr; - glGetUniformLocation_t* glGetUniformLocation = nullptr; - glUniform1f_t* glUniform1f = nullptr; - glUniform1i_t* glUniform1i = nullptr; - glUniform2fv_t* glUniform2fv = nullptr; - glUniform4fv_t* glUniform4fv = nullptr; - glUniformMatrix4fv_t* glUniformMatrix4fv = nullptr; - glActiveTexture_t* glActiveTexture = nullptr; - glGenFrameBuffers_t* glGenFrameBuffers = nullptr; - glBindFrameBuffer_t* glBindFrameBuffer = nullptr; - glCheckFrameBufferStatus_t* glCheckFrameBufferStatus = nullptr; - glDeleteFrameBuffers_t* glDeleteFrameBuffers = nullptr; - glFrameBufferTexture2D_t* glFrameBufferTexture2D = nullptr; - glDrawBuffers_t* glDrawBuffers = nullptr; - glBlendFuncSeparate_t* glBlendFuncSeparate = nullptr; + // Proxies allow switchable, clutter-free error checking + + // OpenGL3.3 Proxies + + GLuint glCreateShader(GLenum type); + GLuint glCreateProgram(void); + void glShaderSource(GLuint shader, GLsizei count, const GLchar** string, const GLint* length); + void glDeleteShader(GLuint shader); + void glCompileShader(GLuint shader); + void glLinkProgram(GLuint program); + void glDeleteProgram(GLuint program); + void glAttachShader(GLuint program, GLuint shader); + void glBindBuffer(GLenum target, GLuint buffer); + void glBufferData(GLenum target, GLsizeiptr size, const void* data, GLenum usage); + void glGenBuffers(GLsizei n, GLuint* buffers); + void glVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void* pointer); + void glEnableVertexAttribArray(GLuint index); + void glUseProgram(GLuint program); + void glBindVertexArray(GLuint array); + void glGenVertexArrays(GLsizei n, GLuint* arrays); + void glGetShaderInfoLog(GLuint shader, GLsizei bufSize, GLsizei* length, GLchar* infoLog); + GLint glGetUniformLocation(GLuint program, const GLchar* name); + void glUniform1f(GLint location, GLfloat v0); + void glUniform1i(GLint location, GLint v0); + void glUniform2fv(GLint location, GLsizei count, const GLfloat* value); + void glUniform4fv(GLint location, GLsizei count, const GLfloat* value); + void glUniformMatrix4fv(GLint location, GLsizei count, GLboolean trasnpose, const GLfloat* value); + void glActiveTexture(GLenum texture); + void glGenFramebuffers(GLsizei n, GLuint* ids); + void glBindFramebuffer(GLenum target, GLuint fb); + GLenum glCheckFramebufferStatus(GLenum target); + void glDeleteFramebuffers(GLsizei n, const GLuint* fbs); + void glFramebufferTexture2D(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); + void glDrawBuffers(GLsizei n, const GLenum* bufs); + void glBlendFuncSeparate(GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); // OpenGL1.2 Proxies (just keeps things tidy imo) void glGenTextures(GLsizei n, GLuint* textures); @@ -158,6 +205,18 @@ namespace olc void glClear(GLbitfield mask); void glViewport(GLint x, GLint y, GLsizei width, GLsizei height); void glClearColor(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); + void glReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void* data); + void glCullFace(GLenum mode); + void glEnable(GLenum cap); + void glDisable(GLenum cap); + void glDrawArrays(GLenum mode, GLint first, GLsizei count); + void glBlendFunc(GLenum sfactor, GLenum dfactor); + void glDepthFunc(GLenum func); + void glGetTexImage(GLenum target, GLint level, GLenum format, GLenum type, void* pixels); + + + private: + bool CheckError(const std::source_location loc = std::source_location::current()); }; } diff --git a/dev/src/config.h b/dev/src/config.h index bac80e0f..93ec4638 100644 --- a/dev/src/config.h +++ b/dev/src/config.h @@ -11,7 +11,7 @@ #define PGE_PIXEL_LAYOUT_ABGR 2 #if !defined(PGE_PIXEL_LAYOUT) - #define PGE_PIXEL_LAYOUT PGE_PIXEL_LAYOUT_ABGR + #define PGE_PIXEL_LAYOUT PGE_PIXEL_LAYOUT_RGBA #endif @@ -70,6 +70,21 @@ #define OLC_GPU OLC_GPU_OPENGL33 #endif + + +#define OLC_IMAGELOADER_NONE 1 +#define OLC_IMAGELOADER_WINGDI 2 + +#if !defined(OLC_IMAGELOADER) + #define OLC_IMAGELOADER OLC_IMAGELOADER_WINGDI +#endif + + +#define OLC_MOUSE_BUTTONS 3 + +#define OLC_GPU_MAX_VERTICES 8192 +#define OLC_GPU_ERRORCHECK 0 + #define LICENCE_DEFAULT "OneLoneCoder.com - Pixel Game Engine 3 - " //! END CONFIGURATION \ No newline at end of file diff --git a/dev/src/core.cpp b/dev/src/core.cpp index 22175bb4..33781124 100644 --- a/dev/src/core.cpp +++ b/dev/src/core.cpp @@ -2,6 +2,7 @@ #include "gpu_opengl33.h" #include "host_win_winapi.h" +#include "imload_wingdi.h" //! START IMPLEMENTATION namespace olc @@ -26,8 +27,7 @@ namespace olc bool PixelGameEngine::Construct(const PGEConfig& cfg) { config = cfg; - - return false; + return true; } bool PixelGameEngine::Start() @@ -38,7 +38,7 @@ namespace olc // Link this olc::Window to a host resource if (host) - host->AddWindowFrame(this, { 30,30 }, { 250, 240 }, false); + host->AddWindowFrame(this, { 30,30 }, config.vPixelSize * config.vScreenSize, false); coreActive = true; coreThread = std::thread(&PixelGameEngine::EngineThread, this); @@ -88,7 +88,24 @@ namespace olc } bool PixelGameEngine::CreateImageFromFile(olc::Image& image, const std::string& sFileName, const ImageConfig& cfg) - { + { + if (imageloader->CreateImageFromFile(image, sFileName)) + { + // Image has loaded ok, and populated into pixel vector + // + // Create GPU Image + auto id = gpu->CreateTexture(image.Size(), cfg); + if (id == 0) + { + image.Create({ 0,0 }); + return false; + } + + // Associate CPU object with GPU Resource + image.SetGPUID(id); + return true; + } + return false; } @@ -102,10 +119,10 @@ namespace olc return false; } - bool PixelGameEngine::WriteImageToMemory(const olc::Image& image, std::vector bytes, const std::string& sFileName) - { - return false; - } + //bool PixelGameEngine::WriteImageToMemory(const olc::Image& image, std::vector bytes, const std::string& sFileName) + //{ + // return false; + //} void PixelGameEngine::DestroyImage(olc::Image& image) { @@ -131,10 +148,15 @@ namespace olc using namespace std::chrono_literals; timeFrame2 = std::chrono::steady_clock::now(); timeFrame1 = std::chrono::steady_clock::now(); + + // Initialise ImageLoader Interface + imageloader = std::make_unique(); // Initialise GPU Interface - This thread is the context olc::gpu::RendererConfig cfgRenderer; + cfgRenderer.VerticalSync = config.bVSync; + gpu = std::make_unique(); // The GPU device can be based upon the primary window configuration. This @@ -148,13 +170,22 @@ namespace olc return; } + + CreateImage(imgPrimary, config.vScreenSize); + + + draw.SetGPU(gpu.get()); + gpu->ApplyDefaultShader(); + draw.SetTarget(imgPrimary); + if (!OnUserCreate()) { // Creation process signalled abort return; } - + draw.ProcessGPUTasks(); + draw.SetTarget(imgPrimary); // Initialise Input Devices @@ -203,7 +234,7 @@ namespace olc // Wait for vertical sync if required. // Note: Child windows will never vsync as waiting for each buffer swap with vsync - // divides up teh frame rate budget across the windows. + // divides up the frame rate budget across the windows. if (gpu->GetConfig().VerticalSync) { host->SyncWithDesktopComposite(); diff --git a/dev/src/core.h b/dev/src/core.h index d75429b7..2bdc9b14 100644 --- a/dev/src/core.h +++ b/dev/src/core.h @@ -19,6 +19,7 @@ #include "window.h" #include "gpu_iface.h" #include "host_iface.h" +#include "imload_iface.h" //! END CUSTOMHEADER GLOBAL //! START DECLARATION @@ -84,7 +85,7 @@ namespace olc // Store an image as a file asset on disk bool WriteImageToFile(const olc::Image& image, const std::string& sFileName); // Store an image as a file asset in memory - bool WriteImageToMemory(const olc::Image& image, std::vector bytes, const std::string& sFileName); + //bool WriteImageToMemory(const olc::Image& image, std::vector bytes, const std::string& sFileName); // Destroy an image void DestroyImage(olc::Image& image); @@ -111,6 +112,7 @@ namespace olc std::unique_ptr gpu; std::unique_ptr host; + std::unique_ptr imageloader; }; } #define PGE_CORE_DECLARED 1 diff --git a/dev/src/draw2d.cpp b/dev/src/draw2d.cpp new file mode 100644 index 00000000..d639ab34 --- /dev/null +++ b/dev/src/draw2d.cpp @@ -0,0 +1,438 @@ +#include "draw2d.h" + +#include "gpu_iface.h" + +//! START IMPLEMENTATION +using namespace olc; + +Draw2D::Draw2D() +{ +} + +void Draw2D::SetGPU(olc::gpu::Renderer* const renderer) +{ + pRenderer = renderer; +} + +void Draw2D::SetTarget(olc::Image& image) +{ + // Perform any outstanding tasks for current target + ProcessGPUTasks(); + + // Endure new target exists in GPU up to date + PrepareImageForHW(image); + + // Store the target image + pTarget = ℑ + + // Create a transform to denormalise the image and present it + // in pixel space + transformTarget.scale(olc::vf2d(2.0f, 2.0f) / olc::vf2d(pTarget->Size())); + transformTarget.translate( + olc::vf2d( + -1.0f,// +(1.0f / float(pTarget->Size().x)), + -1.0f// + (1.0f / float(pTarget->Size().y)) + )); + + WorldReset(); + + pRenderer->AssignTextureTarget(0, pTarget->GetGPUID()); + pRenderer->SetViewport({ 0,0 }, pTarget->Size()); +} + +void Draw2D::ClearTransform() +{ + transformTarget = olc::tf2d(); + transformAffine = olc::tf2d(); + transformCombined = olc::mf3d(); +} + +void Draw2D::SetTransform(const olc::tf2d& trans) +{ + transformTarget = trans; +} + +tf2d& olc::Draw2D::GetTransform() +{ + return transformAffine; +} + +void olc::Draw2D::ProcessGPUTasks() +{ + for (const auto& task : vecGPUTasks) + pRenderer->DoGPUTask(task); + + vecGPUTasks.clear(); +} + +void Draw2D::PrepareTargetForSW() +{ + if (pTarget->BoundToGPU()) + { + // Process GPU queue bound for the target + ProcessGPUTasks(); + + // Image resource is primed for GPU operations, bring it to CPU + pRenderer->ReadTexture(pTarget->GetGPUID(), *pTarget); + + // Image is now CPU bound + pTarget->BindCPU(); + } +} + +void Draw2D::PrepareTargetForHW() +{ + if (pTarget->BoundToCPU()) + { + // Image resource is primed for CPU operations, send it to GPU + pRenderer->WriteTexture(pTarget->GetGPUID(), *pTarget); + + // Image is now GPU bound + pTarget->BindGPU(); + } +} + +void Draw2D::PrepareImageForSW(olc::Image& image) +{ + if (image.BoundToGPU()) + { + // Process GPU queue bound for the target + ProcessGPUTasks(); + + // Image resource is primed for GPU operations, bring it to CPU + pRenderer->ReadTexture(image.GetGPUID(), image); + + // Image is now CPU bound + image.BindCPU(); + } +} + +void Draw2D::PrepareImageForHW(olc::Image& image) +{ + if (image.BoundToCPU()) + { + // Image resource is primed for CPU operations, send it to GPU + pRenderer->WriteTexture(image.GetGPUID(), image); + + // Image is now GPU bound + image.BindGPU(); + } +} + +void olc::Draw2D::WorldReset() +{ + transformAffine = olc::tf2d(); + transformCombined = transformAffine * transformTarget; +} + +void olc::Draw2D::WorldScale(const olc::vf2d& vScale) +{ + transformAffine.scale(vScale); + transformCombined = transformAffine * transformTarget; +} + +void olc::Draw2D::WorldOffset(const olc::vf2d& vOffset) +{ + transformAffine.translate(vOffset); + transformCombined = transformAffine * transformTarget; +} + +void olc::Draw2D::WorldRotate(const float& fTheta, const olc::vf2d& vPoint) +{ + transformAffine.rotate(fTheta, vPoint); + transformCombined = transformAffine * transformTarget; +} + +olc::vf2d olc::Draw2D::WorldToScreen(const olc::vf2d& v) const +{ + return transformAffine.forward(v); +} + +olc::vf2d olc::Draw2D::ScreenToWorld(const olc::vf2d& v) const +{ + return transformAffine.inverse(v); +} + +void Draw2D::Pixel(const olc::vf2d& pos, const olc::Pixel col) +{ + // Check if in bounds + olc::vf2d tpos = transformAffine.forward(pos); + if (tpos.x >= 0 && tpos.y >= 0 && tpos.x < pTarget->Size().x && tpos.y < pTarget->Size().y) + { + PrepareTargetForSW(); + pTarget->Pixel(tpos) = col; + } + + // otherwise do nothing +} + +olc::Pixel olc::Draw2D::GetPixel(olc::Image& image, const olc::vf2d& pos) +{ + PrepareImageForSW(image); + return image.Pixel(pos); +} + +void olc::Draw2D::Clear(const olc::Pixel& col) +{ + //PrepareImageForHW(*pTarget); + pRenderer->ClearViewport(col, true, true); +} + +GPUTask olc::Draw2D::TaskDrawPolygon(GPUTask::Structure structure, const std::vector& vPoints, const std::vector& vColours, const olc::Pixel tint) +{ + GPUTask task; + task.structure = structure; + task.bWireframe = true; + for (size_t i = 0; i < vPoints.size(); i++) + task.vertexBuffer.push_back({ vPoints[i].x, vPoints[i].y, 1.0f, 1.0f, vColours[i], 0, 0, 0,0, 0, 0, 0, 0}); + //task.mvpMatrix = transformCombined.m + task.tint = tint; + return task; +} + +GPUTask olc::Draw2D::TaskDrawPolygon(GPUTask::Structure structure, const std::vector& vPoints, const olc::Pixel colour, const olc::Pixel tint) +{ + GPUTask task; + task.structure = structure; + task.bWireframe = true; + for (const auto& v : vPoints) + task.vertexBuffer.push_back({ v.x+0.0f, v.y+0.0f, 1.0f, 1.0f, colour, 0, 0, 0,0, 0, 0, 0, 0 }); + task.tint = tint; + return task; +} + +GPUTask olc::Draw2D::TaskFillPolygon(GPUTask::Structure structure, const std::vector& vPoints, const std::vector& vColours, const olc::Pixel tint) +{ + return GPUTask(); +} + +GPUTask olc::Draw2D::TaskFillPolygon(GPUTask::Structure structure, const std::vector& vPoints, const olc::Pixel colour, const olc::Pixel tint) +{ + GPUTask task; + task.structure = structure; + for (const auto& v : vPoints) + task.vertexBuffer.push_back({ v.x, v.y, 1.0f, 1.0f, colour, 0, 0, 0,0, 0, 0, 0, 0 }); + task.tint = tint; + return task; +} + +GPUTask olc::Draw2D::TaskTexturedPolygon(GPUTask::Structure structure, const std::vector& vPoints, const std::vector& vColours, const std::vector& vTexCoords, olc::Image* const image, const olc::Pixel tint) +{ + GPUTask task; + for (size_t i = 0; i& vPoints, const std::vector& vZWs, const std::vector& vColours, const std::vector& vTexCoords, olc::Image* const image, const olc::Pixel tint) +{ + GPUTask task; + for (size_t i = 0; i < vPoints.size(); i++) + task.vertexBuffer.push_back({ vPoints[i].x, vPoints[i].y, vZWs[i].x, vZWs[i].y, vColours[i], vTexCoords[i].x, vTexCoords[i].y, 0, 0, 0, 0, 0, 0}); + task.pImage = image; + task.tint = tint; + return task; +} + +GPUTask Draw2D::Line(const olc::vf2d& p1, const olc::vf2d& p2, const olc::Pixel col) +{ + PrepareTargetForHW(); + return vecGPUTasks.emplace_back( + TaskDrawPolygon( + GPUTask::Structure::Line, + //transformCombined.transform({ p1, p2 }), + {p1, p2}, + col, + olc::Colour::WHITE + )); +} + +GPUTask Draw2D::Line(const olc::vf2d& p1, const olc::vf2d& p2, const olc::Pixel c1, const olc::Pixel c2) +{ + PrepareTargetForHW(); + return vecGPUTasks.emplace_back( + TaskDrawPolygon( + GPUTask::Structure::Line, + transformCombined.transform({ p1, p2 }), + { c1, c2 }, + olc::Colour::WHITE + )); +} + +GPUTask olc::Draw2D::Rect(const olc::vf2d& pos, const olc::vf2d& size, const olc::Pixel col) +{ + // Right a big, hearty, F&^% you to OpenGL's Diamond Exit Strategy. It makes line drawing + // with OpenGL a smidge unreliable + + // Check if its axis aligned + if (std::abs(transformAffine.rotate()) < 0.0001f) + { + // SW Raster the line + } + + PrepareTargetForHW(); + return vecGPUTasks.emplace_back( + TaskDrawPolygon( + GPUTask::Structure::Fan, + transformAffine.forward({ + olc::vf2d( pos.x + 0.0f, pos.y + 0.0f ), + olc::vf2d( pos.x + size.x +0.0f, pos.y + 0.0f ), + + //olc::vf2d( pos.x + size.x + 0.0f, pos.y + 0.0f), + olc::vf2d( pos.x + size.x + 0.0f, pos.y + size.y + 0.0f ), + + //olc::vf2d( pos.x + size.x + 0.0f, pos.y + size.y + 0.0f), + olc::vf2d( pos.x + 0.0f, pos.y + size.y + 0.0f ), + + //olc::vf2d( pos.x + 0.0f, pos.y + size.y + 0.0f), + //olc::vf2d( pos.x + 0.0f, pos.y + 0.0f), + }), + + // transformCombined.transform({ { pos.x, pos.y }, { pos.x + size.x, pos.y }, { pos.x + size.x, pos.y + size.y }, { pos.x, pos.y + size.y } }), + col, + olc::Colour::WHITE + )); + +} + + +GPUTask olc::Draw2D::FilledRect(const olc::vf2d& pos, const olc::vf2d& size, const olc::Pixel col) +{ + PrepareTargetForHW(); + return vecGPUTasks.emplace_back( + TaskFillPolygon( + GPUTask::Structure::Fan, + //transformCombined.transform({ { pos.x, pos.y }, { pos.x + size.x, pos.y }, { pos.x + size.x, pos.y + size.y }, { pos.x, pos.y + size.y } }), + { { pos.x, pos.y }, { pos.x + size.x, pos.y }, { pos.x + size.x, pos.y + size.y }, { pos.x, pos.y + size.y } }, + + col, + olc::Colour::WHITE + )); +} + +GPUTask olc::Draw2D::Image(olc::ImageRegion image, const olc::vf2d& pos, const olc::vf2d& scale) +{ + // Ensure source image is up to date in VRAM + PrepareImageForHW(image.image); + + PrepareTargetForHW(); + + olc::vf2d size = image.regionsize * scale; + + return vecGPUTasks.emplace_back( + TaskTexturedPolygon( + GPUTask::Structure::Fan, + //transformCombined.transform({ { pos.x, pos.y }, { pos.x + size.x, pos.y }, { pos.x + size.x, pos.y + size.y }, { pos.x, pos.y + size.y } }), + { { pos.x, pos.y }, { pos.x + size.x, pos.y }, { pos.x + size.x, pos.y + size.y }, { pos.x, pos.y + size.y } }, + { olc::Colour::WHITE, olc::Colour::WHITE, olc::Colour::WHITE, olc::Colour::WHITE }, + // Tex coords are clockwise + { image.coords[0], image.coords[1], image.coords[2], image.coords[3]}, + &image.image + )); +} + +GPUTask olc::Draw2D::ImageRotated(olc::ImageRegion image, const olc::vf2d& pos, const float theta, const olc::vf2d& center, const olc::vf2d& scale) +{ + // Ensure source image is up to date in VRAM + PrepareImageForHW(image.image); + PrepareTargetForHW(); + + olc::vf2d size = image.regionsize * scale; + + std::vector vPoints(4); + vPoints[0] = (olc::vf2d(0.0f, 0.0f) - center) * scale; + vPoints[1] = (olc::vf2d(size.x, 0.0f) - center) * scale; + vPoints[2] = (size - center) * scale; + vPoints[3] = (olc::vf2d(0.0f, size.y) - center) * scale; + + float c = cos(theta), s = sin(theta); + for (int i = 0; i < 4; i++) + vPoints[i] = pos + olc::vf2d(vPoints[i].x * c - vPoints[i].y * s, vPoints[i].x * s + vPoints[i].y * c); + + return vecGPUTasks.emplace_back( + TaskTexturedPolygon( + GPUTask::Structure::Fan, + //transformCombined.transform(vPoints), + vPoints, + { olc::Colour::WHITE, olc::Colour::WHITE, olc::Colour::WHITE, olc::Colour::WHITE }, + { image.coords[0], image.coords[1], image.coords[2], image.coords[3] }, + &image.image + )); +} + +GPUTask olc::Draw2D::ImageQuad(olc::ImageRegion image, const olc::vf2d& vTL, const olc::vf2d& vTR, const olc::vf2d& vBR, const olc::vf2d& vBL) +{ + // Ensure source image is up to date in VRAM + PrepareImageForHW(image.image); + PrepareTargetForHW(); + + + float rd = ((vBR.x - vTL.x) * (vTR.y - vBL.y) - (vTR.x - vBL.x) * (vBR.y - vTL.y)); + if (rd != 0) + { + rd = 1.0f / rd; + float rn = ((vTR.x - vBL.x) * (vTL.y - vBL.y) - (vTR.y - vBL.y) * (vTL.x - vBL.x)) * rd; + float sn = ((vBR.x - vTL.x) * (vTL.y - vBL.y) - (vBR.y - vTL.y) * (vTL.x - vBL.x)) * rd; + + olc::vf2d center; + if (!(rn < 0.f || rn > 1.f || sn < 0.f || sn > 1.f)) + center = vTL + rn * (vBR - vTL); + + std::array d = { { + (vTL - center).mag(), + (vTR - center).mag(), + (vBR - center).mag(), + (vBL - center).mag(), + } }; + + std::array q = { { + d[0] == 0.0f ? 1.0f : (d[0] + d[2]) / d[2], + d[1] == 0.0f ? 1.0f : (d[1] + d[3]) / d[3], + d[2] == 0.0f ? 1.0f : (d[2] + d[0]) / d[0], + d[3] == 0.0f ? 1.0f : (d[3] + d[1]) / d[1], + } }; + + return vecGPUTasks.emplace_back( + TaskTexturedPolygon( + GPUTask::Structure::Fan, + //transformCombined.transform({ vTL, vTR, vBR, vBL }), + { vTL, vTR, vBR, vBL }, + { {q[0], 1.0f}, {q[1], 1.0f}, {q[2], 1.0f}, {q[3], 1.0f} }, + { olc::Colour::WHITE, olc::Colour::WHITE, olc::Colour::WHITE, olc::Colour::WHITE }, + { image.coords[0] * q[0], image.coords[1] * q[1], image.coords[2] * q[2], image.coords[3] * q[3] }, + &image.image + )); + } + + return GPUTask(); +} + +GPUTask olc::Draw2D::ImageQuad(olc::ImageRegion image, const std::vector& vecPoints) +{ + return ImageQuad(image, vecPoints[0], vecPoints[1], vecPoints[2], vecPoints[3]); +} + + +GPUTask olc::Draw2D::ImageRect(olc::ImageRegion image, const olc::vf2d& pos, const olc::vf2d& size) +{ + // Ensure source image is up to date in VRAM + PrepareImageForHW(image.image); + + PrepareTargetForHW(); + + return vecGPUTasks.emplace_back( + TaskTexturedPolygon( + GPUTask::Structure::Fan, + //transformCombined.transform({ { pos.x, pos.y }, { pos.x + size.x, pos.y }, { pos.x + size.x, pos.y + size.y }, { pos.x, pos.y + size.y } }), + { { pos.x, pos.y }, { pos.x + size.x, pos.y }, { pos.x + size.x, pos.y + size.y }, { pos.x, pos.y + size.y } }, + { olc::Colour::WHITE, olc::Colour::WHITE, olc::Colour::WHITE, olc::Colour::WHITE }, + // Tex coords are clockwise + { image.coords[3], image.coords[2], image.coords[1], image.coords[0] }, + &image.image + )); +} + +//! END IMPLEMENTATION \ No newline at end of file diff --git a/dev/src/draw2d.h b/dev/src/draw2d.h index 666e06eb..b549381c 100644 --- a/dev/src/draw2d.h +++ b/dev/src/draw2d.h @@ -12,45 +12,217 @@ //! START CUSTOMHEADER #include "config.h" #include "vector2d.h" +#include "transform2d.h" #include "pixel.h" +#include "image.h" +#include "gputask.h" //! END CUSTOMHEADER //! START DECLARATION #if !defined(PGE_DRAW2D_DECLARED) namespace olc { + namespace gpu + { + class Renderer; + } + + class Draw2D { public: Draw2D(); + // Associate this drawing toolbox with a renderer + void SetGPU(olc::gpu::Renderer* const renderer); + + public: + // Sets the drawing target of this drawing toolbox + void SetTarget(olc::Image& image); + + // Clears transform stack and sets identity transform + void ClearTransform(); + + void SetTransform(const olc::tf2d& trans); + + olc::tf2d& GetTransform(); + + void ProcessGPUTasks(); + + + + public: // Affine Transformation (these affect all subsequent draw calls for this target) + void WorldReset(); + void WorldScale(const olc::vf2d& vScale); + void WorldOffset(const olc::vf2d& vOffset); + void WorldRotate(const float& fTheta, const olc::vf2d& vPoint = { 0,0 }); + olc::vf2d WorldToScreen(const olc::vf2d& v) const; + olc::vf2d ScreenToWorld(const olc::vf2d& v) const; + public: // Plot a single pixel - template - void Draw(const olc::v_2d& pos, const olc::Pixel col = olc::Colour::WHITE) - { - /* - if(target image not in ram) - copy_gpu_to_cpu(target image) + void Pixel( + const olc::vf2d& pos, + const olc::Pixel col = olc::Colour::WHITE); + + // Read a pixel from an image (guarantees fresh) + olc::Pixel GetPixel( + olc::Image& image, + const olc::vf2d& pos); + + // Clear + void Clear(const olc::Pixel& col); + + public: + // Draws a single pixel wide line + GPUTask Line( + const olc::vf2d& p1, + const olc::vf2d& p2, + const olc::Pixel col = olc::Colour::WHITE); + + // Draws a single pixel wide line with a gradient + GPUTask Line( + const olc::vf2d& p1, + const olc::vf2d& p2, + const olc::Pixel c1, + const olc::Pixel c2); + + // Draws a rectangle outline + GPUTask Rect( + const olc::vf2d& pos, + const olc::vf2d& size, + const olc::Pixel col = olc::Colour::WHITE); + + // Draws a filled, single colour rectangle + GPUTask FilledRect( + const olc::vf2d& pos, + const olc::vf2d& size, + const olc::Pixel col = olc::Colour::WHITE); + + //GPUTask ShadedRect(const olc::vf2d& pos, const olc::vf2d& size, const olc::Pixel colTL, const olc::Pixel colTR, const olc::Pixel colBL, const olc::Pixel colBR); + //TexturedRect + + + //Circle + //FilledCircle + //ShadedCircle + //TexturedCircle + + //Ellipse + //FilledEllipse + //ShadedEllipse + //TexturedEllipse + + //RoundRect + //FilledRoundRect + //ShadedRoundRect + //TexturedRoundRect + + //Triangle + //FilledTriangle + //ShadedTriangle + //TexturedTriangle + + //Polygon + //FilledPolygon + //ShadedPolygon + //TexturedPolygon + + + // Draws a scaled image at specified location + GPUTask Image( + olc::ImageRegion image, + const olc::vf2d& pos, + const olc::vf2d& scale = { 1.0f, 1.0f }); + + // Draws an image rotated around a point at specified location + GPUTask ImageRotated( + olc::ImageRegion image, + const olc::vf2d& pos, + const float theta, + const olc::vf2d& center = { 0.0f, 0.0f }, + const olc::vf2d& scale = { 1.0f, 1.0f }); + + // Draws an image warped correctly to linearly fill a quadrilateral (formerly DrawWarped...) + GPUTask ImageQuad( + olc::ImageRegion image, + const olc::vf2d& vTL, + const olc::vf2d& vTR, + const olc::vf2d& vBR, + const olc::vf2d& vBL); + + GPUTask ImageQuad( + olc::ImageRegion image, + const std::vector& vecPoints); + + + // Draws an image scaled to a specified rectangular area + GPUTask ImageRect(olc::ImageRegion image, const olc::vf2d& pos, const olc::vf2d& size); + + + + + public: // GPU Task Creator Functions (not normally called by user) + GPUTask TaskDrawPolygon( + GPUTask::Structure structure, + const std::vector& vPoints, + const std::vector& vColours, + const olc::Pixel tint = olc::Colour::WHITE); + + GPUTask TaskDrawPolygon( + GPUTask::Structure structure, + const std::vector& vPoints, + const olc::Pixel colour, + const olc::Pixel tint = olc::Colour::WHITE); + + GPUTask TaskFillPolygon( + GPUTask::Structure structure, + const std::vector& vPoints, + const std::vector& vColours, + const olc::Pixel tint = olc::Colour::WHITE); + GPUTask TaskFillPolygon( + GPUTask::Structure structure, + const std::vector& vPoints, + const olc::Pixel colour, + const olc::Pixel tint = olc::Colour::WHITE); - - */ - } + GPUTask TaskTexturedPolygon( + GPUTask::Structure structure, + const std::vector& vPoints, + const std::vector& vColours, + const std::vector& vTexCoords, + olc::Image* const image, + const olc::Pixel tint = olc::Colour::WHITE); + GPUTask TaskTexturedPolygon( + GPUTask::Structure structure, + const std::vector& vPoints, + const std::vector& vZWs, + const std::vector& vColours, + const std::vector& vTexCoords, + olc::Image* const image, + const olc::Pixel tint = olc::Colour::WHITE); - // Draws a single pixel wide line - template - void DrawLine(const olc::v_2d& p1, const olc::v_2d& p2, const olc::Pixel col = olc::Colour::WHITE) - { - /* - if(target image not in ram) - copy_gpu_to_cpu(target image) + protected: + // Checks residency of image resource, and brings it to cpu RAM for r/w + void PrepareTargetForSW(); + // Checks residency of image resource, and brings it to gpu VRAM for r/w + void PrepareTargetForHW(); + // Checks residency of image resource, and brings it to cpu RAM for r/w + void PrepareImageForSW(olc::Image& image); + // Checks residency of image resource, and brings it to gpu VRAM for r/w + void PrepareImageForHW(olc::Image& image); + olc::Image* pTarget = nullptr; + olc::gpu::Renderer* pRenderer = nullptr; + olc::tf2d transformTarget; + olc::tf2d transformAffine; + olc::mf3d transformCombined; - */ - } + std::vector vecGPUTasks; + }; } #define PGE_DRAW2D_DECLARED diff --git a/dev/src/gpu_iface.cpp b/dev/src/gpu_iface.cpp new file mode 100644 index 00000000..eceba7aa --- /dev/null +++ b/dev/src/gpu_iface.cpp @@ -0,0 +1,37 @@ +#include "gpu_iface.h" + + +//! START IMPLEMENTATION +namespace olc +{ + gpu::Shader::~Shader() + { + } + + void gpu::Shader::SetPixelShaderSource(const std::string& src) + { + srcPixelShader = src; + } + + void gpu::Shader::SetVertexShaderSource(const std::string& src) + { + srcVertexShader = src; + } + + void gpu::Shader::SetGeometryShaderSource(const std::string& src) + { + srcGeometryShader = src; + } + + int32_t gpu::Shader::GetUniform(const std::string& name) const + { + return mapUniforms.at(name); + } + + int32_t gpu::Shader::GetShaderID() const + { + return nCompiledShaderID; + } +} + +//! END IMPLEMENTATION \ No newline at end of file diff --git a/dev/src/gpu_iface.h b/dev/src/gpu_iface.h index 74a60f46..edf28e45 100644 --- a/dev/src/gpu_iface.h +++ b/dev/src/gpu_iface.h @@ -34,6 +34,33 @@ namespace olc FailedToSwitchRenderContext, }; + class Shader + { + public: + virtual ~Shader(); + + public: + void SetPixelShaderSource(const std::string& src); + void SetVertexShaderSource(const std::string& src); + void SetGeometryShaderSource(const std::string& src); + + virtual std::string Compile() = 0; + virtual int32_t CreateUniform(const std::string& name) = 0; + + int32_t GetUniform(const std::string& name) const; + int32_t GetShaderID() const; + + protected: + std::string srcPixelShader; + std::string srcVertexShader; + std::string srcGeometryShader; + int32_t nPixelShaderID; + int32_t nVertexShaderID; + int32_t nGeometryShaderID; + int32_t nCompiledShaderID; + std::unordered_map mapUniforms; + }; + class Renderer { public: @@ -45,6 +72,8 @@ namespace olc inline RendererError GetLastError() const { return lastError; } inline const RendererConfig& GetConfig() const { return config; } + + public: // Device Stuff // Constructs a GPU Device interface virtual bool CreateDevice(std::vector params, const RendererConfig& cfg) = 0; @@ -67,8 +96,11 @@ namespace olc public: // Shader Construction Stuff + virtual bool ApplyShader(const Shader& shader) = 0; + virtual bool ApplyDefaultShader() = 0; + public: // GPU Task Processing Stuff - virtual bool DoGPUTask(const olc::pgeguts::GPUTask& task) = 0; + virtual bool DoGPUTask(const olc::GPUTask& task) = 0; public: // Swap Chain Stuff // Clears the viewport to a specific colour and depth diff --git a/dev/src/gpu_opengl33.cpp b/dev/src/gpu_opengl33.cpp index d9ffd94b..52f20bd1 100644 --- a/dev/src/gpu_opengl33.cpp +++ b/dev/src/gpu_opengl33.cpp @@ -3,6 +3,61 @@ //! START IMPLEMENTATION namespace olc::gpu { + std::string Shader_GLSL33::Compile() + { + auto& gl = olc::apis::opengl::gl::Get(); + + nCompiledShaderID = gl.glCreateProgram(); + + // Fragment Shader + if (!srcPixelShader.empty()) + { + nPixelShaderID = gl.glCreateShader(0x8B30); + const char* s = srcPixelShader.c_str(); + gl.glShaderSource(nPixelShaderID, 1, &s, nullptr); + gl.glCompileShader(nPixelShaderID); + // TODO: Error Check + gl.glAttachShader(nCompiledShaderID, nPixelShaderID); + } + + // Vertex Shader + if (!srcVertexShader.empty()) + { + nVertexShaderID = gl.glCreateShader(0x8B31); + const char* s = srcVertexShader.c_str(); + gl.glShaderSource(nVertexShaderID, 1, &s, nullptr); + gl.glCompileShader(nVertexShaderID); + // TODO: Error Check + gl.glAttachShader(nCompiledShaderID, nVertexShaderID); + } + + + // Geometry Shader + if (!srcGeometryShader.empty()) + { + nGeometryShaderID = gl.glCreateShader(0x8DD9); + const char* s = srcGeometryShader.c_str(); + gl.glShaderSource(nGeometryShaderID, 1, &s, nullptr); + gl.glCompileShader(nGeometryShaderID); + // TODO: Error Check + gl.glAttachShader(nCompiledShaderID, nGeometryShaderID); + } + + gl.glLinkProgram(nCompiledShaderID); + + return "OK"; + } + + int32_t Shader_GLSL33::CreateUniform(const std::string& name) + { + auto& gl = olc::apis::opengl::gl::Get(); + const char* s = name.c_str(); + mapUniforms.insert({ name, gl.glGetUniformLocation(nCompiledShaderID, s) }); + return GetUniform(name); + } + + + bool Renderer_OGL33::CreateDevice(std::vector params, const RendererConfig& cfg) { config = cfg; @@ -55,7 +110,186 @@ namespace olc::gpu // Can't load OpenGL API until context is loaded auto& gl = olc::apis::opengl::gl::Get(); + if (!gl.HasLoaded()) + { + std::cout << "Error: Could not Load OpenGL!\n"; + lastError = RendererError::None; + return false; + } + + + // Create "Default" Shader + shaderDefault.SetPixelShaderSource(R"( + #version 330 core + layout(location = 0) out vec4 pixel; + in vec2 oTex; + in vec4 oCol; + uniform sampler2D sprTex; + + void main() + { + pixel = texture(sprTex, oTex) * oCol; + } + )"); + + shaderDefault.SetVertexShaderSource(R"( + #version 330 core + layout(location = 0) in vec4 aPos; + layout(location = 1) in vec4 aCol; + layout(location = 2) in vec2 aTex; + uniform mat4 mvp; + uniform int drawtype; + uniform vec4 tint; + uniform vec2 target; + uniform vec2 invtarget; + out vec2 oTex; + out vec4 oCol; + + void main() + { + if(drawtype == 2) // 3D + { + gl_Position = mvp * vec4(aPos.x, aPos.y, aPos.z, 1.0); + oTex = aTex; + } + + else if(drawtype == 1) // 2D Line + { + float p = 1.0 / aPos.z; + gl_Position = p * vec4(vec2(2.0 * ((floor(aPos.xy) + 0.5) * invtarget) - 1.0), 0.0, 1.0); + oTex = aTex; + } + + else if(drawtype == 0) // 2D Polygon + { + float p = 1.0 / aPos.z; + gl_Position = p * vec4(vec2(2.0 * ((floor(aPos.xy)) * invtarget) - 1.0), 0.0, 1.0); + oTex = p * vec2(aTex.x, aTex.y); + } + + else // Balanced default + { + gl_Position = aPos; + oTex = aTex; + } + + oCol = aCol * tint; + } + )"); + + //shaderDefault.SetVertexShaderSource( + // "#version 330 core\n" + // "layout(location = 0) in vec4 aPos;\n" + // "layout(location = 1) in vec4 aCol;\n" + // "layout(location = 2) in vec2 aTex;\n" + // "uniform mat4 mvp;\n" + // "uniform int drawtype;\n" + // "uniform vec4 tint;\n" + // "uniform vec2 target;\n" + // "uniform vec2 invtarget;\n" + // "out vec2 oTex;\n" + // "out vec4 oCol;\n" + // "void main()\n" + // "{\n " + // " if(drawtype == 2)\n // 3D\n " + // " {\n " + // " gl_Position = mvp * vec4(aPos.x, aPos.y, aPos.z, 1.0);\n " + // " oTex = aTex;\n " + // " }\n " + // "\n" + // " else if(drawtype == 1)\n // Line\n " + // " {\n " + // " float p = 1.0 / aPos.z;\n " + // " gl_Position = p * vec4(2.0 * ((floor(aPos.x) + 0.5) * invtarget.x) - 1.0,2.0 * ((floor(aPos.y)+0.5) * invtarget.y) - 1.0, 0.0, 1.0);\n " + // " oTex = p * vec2(aTex.x, aTex.y);\n " + // " }\n " + // "" + // " else if(drawtype == 0)\n // Quad\n " + // " {\n " + // " float p = 1.0 / aPos.z;\n " + // " gl_Position = p * vec4(2.0 * ((floor(aPos.x)) * invtarget.x) - 1.0,2.0 * ((floor(aPos.y)) * invtarget.y) - 1.0, 0.0, 1.0);\n " + // " oTex = p * vec2(aTex.x, aTex.y);\n " + // " } else {gl_Position = aPos;}\n " + // " \n " + // " \n " + // " oCol = aCol * tint;\n " + // "}\n" + // //"void main(){ if(is3d!=0) {gl_Position = mvp * vec4(aPos.x, aPos.y, aPos.z, 1.0); oTex = aTex;} else {float p = 1.0 / aPos.z; gl_Position = mvp * (p * vec4(aPos.x, aPos.y, 0.0, 1.0)); oTex = p * aTex;} oCol = aCol * tint;}" + + //); + + shaderDefault.Compile(); + shaderDefault.CreateUniform("mvp"); + shaderDefault.CreateUniform("drawtype"); + shaderDefault.CreateUniform("tint"); + shaderDefault.CreateUniform("target"); + shaderDefault.CreateUniform("invtarget"); + + // Create "Default" Vertex Buffer / Vertex Attributes. This buffer is reused + // for all drawing operations. It's possible future versions may allow the + // creation of additional named buffers for repeated drawing operations with + // minimal overhead. + gl.glGenBuffers(1, &nDefaultVB); + gl.glGenVertexArrays(1, &nDefaultVA); + gl.glBindVertexArray(nDefaultVA); + gl.glBindBuffer(0x8892, nDefaultVB); + + // A big one is allocated to reduce shuffles in GPU memory + GPUTask::Vertex verts[OLC_GPU_MAX_VERTICES]; + gl.glBufferData(0x8892, sizeof(GPUTask::Vertex) * OLC_GPU_MAX_VERTICES, verts, 0x88E0); + + // Float Index 0 = x, 1 = y, 2 = z, 3 = w + gl.glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(GPUTask::Vertex), (void*)(0 * sizeof(float))); + gl.glEnableVertexAttribArray(0); + // Float Index 4 = (RGBA 8-bit x4) + gl.glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(GPUTask::Vertex), (void*)(4 * sizeof(float))); + gl.glEnableVertexAttribArray(1); + // Float Index 5 = u0, 6 = v0 + gl.glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(GPUTask::Vertex), (void*)(5 * sizeof(float))); + gl.glEnableVertexAttribArray(2); + // Float Index 7 = u1, 8 = v1 + gl.glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, sizeof(GPUTask::Vertex), (void*)(7 * sizeof(float))); + gl.glEnableVertexAttribArray(3); + // Float Index 9 = u2, 10 = v2 + gl.glVertexAttribPointer(4, 2, GL_FLOAT, GL_FALSE, sizeof(GPUTask::Vertex), (void*)(9 * sizeof(float))); + gl.glEnableVertexAttribArray(4); + // Float Index 11 = u3, 12 = v4 + gl.glVertexAttribPointer(5, 2, GL_FLOAT, GL_FALSE, sizeof(GPUTask::Vertex), (void*)(11 * sizeof(float))); + gl.glEnableVertexAttribArray(5); + + // Buffers are configured, unbind for now + gl.glBindBuffer(0x8892, 0); + gl.glBindVertexArray(0); + + + + + // Create a null-texture so sampler doesnt fail. We don't have some of the core's helper + // functions here, so we construct it manually + imgBlank.Create({ 1,1 }); + imgBlank.SetGPUID(CreateTexture(imgBlank.Size())); + imgBlank.BindCPU(); + imgBlank.Pixel({ 0,0 }) = olc::Colour::WHITE; + imgBlank.BindGPU(); + WriteTexture(imgBlank.GetGPUID(), imgBlank); + + // Create a Frame Buffer Object for off-screen rendering things + gl.glGenFramebuffers(1, (GLuint*)&nDefaultFBO); + gl.glBindFramebuffer(36160U, nDefaultFBO); // GL_FRAMEBUFFER + // Attach 4 colour buffers + std::array attachments = { {36064U, 36065U, 36066U, 36067U} }; + gl.glDrawBuffers(4, attachments.data()); + // Unlink them from any existing image textures + //gl.glFramebufferTexture2D(36160U, attachments[0], GL_TEXTURE_2D, 0, 0); + //gl.glFramebufferTexture2D(36160U, attachments[1], GL_TEXTURE_2D, 0, 0); + //gl.glFramebufferTexture2D(36160U, attachments[2], GL_TEXTURE_2D, 0, 0); + //gl.glFramebufferTexture2D(36160U, attachments[3], GL_TEXTURE_2D, 0, 0); + // Unbind the FBO + gl.glBindFramebuffer(36160U, 0); + + glEnable(GL_TEXTURE_2D); // Turn on texturing + glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); lastError = RendererError::None; return true; @@ -108,20 +342,25 @@ namespace olc::gpu #if OLC_HOST != OLC_HOST_EMSCRIPTEN gl.glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); #endif + return id; } bool Renderer_OGL33::WriteTexture(const uint32_t texid, olc::Image& image) { auto& gl = olc::apis::opengl::gl::Get(); + gl.glBindTexture(GL_TEXTURE_2D, image.GetGPUID()); gl.glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.Size().x, image.Size().y, 0, GL_RGBA, GL_UNSIGNED_BYTE, image.Data()); - return false; + return true; } bool Renderer_OGL33::ReadTexture(const uint32_t texid, olc::Image& image) { auto& gl = olc::apis::opengl::gl::Get(); - return false; + gl.glBindTexture(GL_TEXTURE_2D, image.GetGPUID()); + //gl.glReadPixels(0, 0, image.Size().x, image.Size().y, GL_RGBA, GL_UNSIGNED_BYTE, image.Data()); + gl.glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, image.Data()); + return true; } bool Renderer_OGL33::DeleteTexture(const uint32_t texid) @@ -134,19 +373,147 @@ namespace olc::gpu bool Renderer_OGL33::AssignTextureSource(const uint32_t slot, const uint32_t texid) { auto& gl = olc::apis::opengl::gl::Get(); - return false; + + gl.glActiveTexture(0x84C0 + slot); // GL_TEXTURE0 + gl.glBindTexture(GL_TEXTURE_2D, texid); + return true; } bool Renderer_OGL33::AssignTextureTarget(const uint32_t slot, const uint32_t texid) { auto& gl = olc::apis::opengl::gl::Get(); - return false; + + if (texid == 0) + { + gl.glBindFramebuffer(36160U, 0); + return true; + } + + // Bind FBO + gl.glBindFramebuffer(36160U, nDefaultFBO); + // Allocate target buffers + std::array attachments = + { { 36064U, 36065U, 36066U, 36067U, 36068U, 36069U, 36070U, 36071U } }; + gl.glDrawBuffers(1, attachments.data()); + // Bind buffers to textures + gl.glFramebufferTexture2D(36160U, 36064U + slot, GL_TEXTURE_2D, texid, 0); + + + return true; } - bool Renderer_OGL33::DoGPUTask(const olc::pgeguts::GPUTask& task) + bool Renderer_OGL33::ApplyShader(const Shader& shader) { auto& gl = olc::apis::opengl::gl::Get(); - return false; + gl.glUseProgram(shader.GetShaderID()); + return true; + } + + bool Renderer_OGL33::ApplyDefaultShader() + { + auto& gl = olc::apis::opengl::gl::Get(); + gl.glUseProgram(shaderDefault.GetShaderID()); + + + return true; + } + + bool Renderer_OGL33::DoGPUTask(const olc::GPUTask& task) + { + auto& gl = olc::apis::opengl::gl::Get(); + + switch (task.task) + { + case GPUTask::Task::DrawPolygon: + { + + gl.glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + //gl.glBlendFunc(GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA); + + if (task.pImage == nullptr) + AssignTextureSource(0, imgBlank.GetGPUID()); + else + AssignTextureSource(0, task.pImage->GetGPUID()); + + // Bind generic vertex buffer + gl.glBindVertexArray(nDefaultVA); + gl.glBindBuffer(0x8892, nDefaultVB); + + // Copy data from CPU to GPU + gl.glBufferData(0x8892, sizeof(GPUTask::Vertex) * task.vertexBuffer.size(), task.vertexBuffer.data(), 0x88E0); + + + + // Shader: Apply MVP Matrix + //gl.glUniformMatrix4fv(shaderDefault.GetUniform("mvp"), 1, true, task.mvpMatrix.data()); + + // Shader: Apply Global Tint + float f[4] = { + float(task.tint.r) / 255.0f, + float(task.tint.g) / 255.0f, + float(task.tint.b) / 255.0f, + float(task.tint.a) / 255.0f + }; + gl.glUniform4fv(shaderDefault.GetUniform("tint"), 1, f); + + f[0] = 64.0f; + f[1] = 64.0f; + gl.glUniform2fv(shaderDefault.GetUniform("target"), 1, vTargetSize.a().data()); + gl.glUniform2fv(shaderDefault.GetUniform("invtarget"), 1, ((1.0f / vTargetSize)).a().data()); + + // Apply Culling modes + //if (task.cullmode == GPUTask::CullMode::None) + //{ + // gl.glCullFace(GL_FRONT); + // gl.glDisable(GL_CULL_FACE); + //} + //else if (task.cullmode == GPUTask::CullMode::ClockWise) + //{ + // gl.glCullFace(GL_FRONT); + // gl.glEnable(GL_CULL_FACE); + //} + //else if (task.cullmode == GPUTask::CullMode::CounterClockWise) + //{ + // gl.glCullFace(GL_BACK); + // gl.glEnable(GL_CULL_FACE); + //} + + //// Apply Depth Testing (if required) + //if (task.bDepth) + // gl.glEnable(GL_DEPTH_TEST); + + // Draw the thing! + if (task.bWireframe) + { + // Shader: Configure Rendering Mode + gl.glUniform1i(shaderDefault.GetUniform("drawtype"), 1); + gl.glDrawArrays(GL_LINE_LOOP, 0, (GLsizei)task.vertexBuffer.size()); + } + else + { + // Shader: Configure Rendering Mode + gl.glUniform1i(shaderDefault.GetUniform("drawtype"), 0); + + if (task.structure == GPUTask::Structure::Fan) + gl.glDrawArrays(GL_TRIANGLE_FAN, 0, (GLsizei)task.vertexBuffer.size()); + else if (task.structure == GPUTask::Structure::Strip) + gl.glDrawArrays(GL_TRIANGLE_STRIP, 0, (GLsizei)task.vertexBuffer.size()); + else if (task.structure == GPUTask::Structure::List) + gl.glDrawArrays(GL_TRIANGLES, 0, (GLsizei)task.vertexBuffer.size()); + else if (task.structure == GPUTask::Structure::Line) + gl.glDrawArrays(GL_LINES, 0, (GLsizei)task.vertexBuffer.size()); + else if (task.structure == GPUTask::Structure::Point) + gl.glDrawArrays(GL_POINTS, 0, (GLsizei)task.vertexBuffer.size()); + } + + if (task.bDepth) + gl.glDisable(GL_DEPTH_TEST); + + } + break; + } + + return true; } bool Renderer_OGL33::ClearViewport(const olc::Pixel col, bool bDepth, bool bStencil) @@ -161,12 +528,20 @@ namespace olc::gpu { auto& gl = olc::apis::opengl::gl::Get(); gl.glViewport(int(pos.x), int(pos.y), int(size.x), int(size.y)); - return false; + vTargetSize = size; + return true; } bool Renderer_OGL33::DisplayPrepare() { auto& gl = olc::apis::opengl::gl::Get(); + + //gl.glUseProgram(shaderDefault.GetShaderID()); + gl.glEnable(GL_BLEND); + gl.glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + gl.glDepthFunc(GL_LESS); + //gl.glBindTexture(GL_TEXTURE_2D, imgBlank.GetGPUID()); + return false; } @@ -181,5 +556,7 @@ namespace olc::gpu return false; } + + } //! END IMPLEMENTATION \ No newline at end of file diff --git a/dev/src/gpu_opengl33.h b/dev/src/gpu_opengl33.h index 49d57bcb..175ab14e 100644 --- a/dev/src/gpu_opengl33.h +++ b/dev/src/gpu_opengl33.h @@ -9,6 +9,13 @@ namespace olc { namespace gpu { + class Shader_GLSL33 : public olc::gpu::Shader + { + public: + std::string Compile() override; + int32_t CreateUniform(const std::string& name) override; + }; + class Renderer_OGL33 : public olc::gpu::Renderer { public: // Device Stuff @@ -17,6 +24,7 @@ namespace olc // Destroys a GPU device interface bool DestroyDevice() override; + public: // Texture Resource Stuff // Allocates a new texture resource in VRAM, returns handle uint32_t CreateTexture(const olc::vi2d& vSize, const olc::ImageConfig& cfg = olc::ImageConfig()) override; @@ -31,9 +39,12 @@ namespace olc // Makes active the given texture resource (for subsequent rendering operations) bool AssignTextureTarget(const uint32_t slot, const uint32_t texid) override; + public: // Shader Construction Stuff + bool ApplyShader(const Shader& shader) override; + bool ApplyDefaultShader() override; public: // GPU Task Stuff - virtual bool DoGPUTask(const olc::pgeguts::GPUTask& task) override; + virtual bool DoGPUTask(const olc::GPUTask& task) override; public: // Swap Chain Stuff // Clears the viewport to a specific colour and depth @@ -50,6 +61,13 @@ namespace olc olc::apis::opengl::glDeviceContext_t glDeviceContext = 0; olc::apis::opengl::glRenderContext_t glRenderContext = 0; + Shader_GLSL33 shaderDefault; + uint32_t nDefaultVB = 0; + uint32_t nDefaultVA = 0; + uint32_t nDefaultFBO = 0; + olc::Image imgBlank; + olc::vf2d vTargetSize; + }; } } diff --git a/dev/src/gputask.h b/dev/src/gputask.h index 2f30ab7b..9c86491b 100644 --- a/dev/src/gputask.h +++ b/dev/src/gputask.h @@ -10,6 +10,7 @@ //! START CUSTOMHEADER #include "config.h" #include "pixel.h" +#include "image.h" #include "transform2d.h" //! END CUSTOMHEADER @@ -17,8 +18,7 @@ #if !defined(PGE_GPUTASK_DECLARED) namespace olc { - namespace pgeguts - { + // This is the default "packet" of work that is sent to // a GPU for drawing. Various drawing operations throughout // PGE create GPUTasks which are stored and dispatched when @@ -27,10 +27,20 @@ namespace olc // upon PGE structures struct GPUTask { + enum class Task : uint8_t + { + DrawPolygon, + NullTask + } task = Task::DrawPolygon; + struct Vertex { - float p[6]; // x, y, z, w, u, v + float p[4]; // x, y, z, w olc::Pixel c; // 32-bit colour + float t0[2]; + float t1[2]; + float t2[2]; + float t3[2]; }; // Simple vertex buffer @@ -53,6 +63,8 @@ namespace olc // Overall biasing colour (great for blends) olc::Pixel tint = olc::Colour::WHITE; + olc::Image* pImage = nullptr; + // Define super structure to be drawn enum class Structure : uint8_t { @@ -115,7 +127,7 @@ namespace olc // return task; //} - } + } #define PGE_GPUTASK_DECLARED 1 #endif diff --git a/dev/src/host_win_winapi.cpp b/dev/src/host_win_winapi.cpp index dad6a113..faa444af 100644 --- a/dev/src/host_win_winapi.cpp +++ b/dev/src/host_win_winapi.cpp @@ -68,8 +68,9 @@ namespace olc::host { // The user created olc::Window object is the SSoT for what a window // should look like, so get that sort of thing from there - olc::vi2d vWinPos = pWindow->GetPosition(); - olc::vi2d vWinSize = pWindow->GetSize(); + olc::vi2d vWinPos = vWindowPos; + olc::vi2d vWinSize = vWindowSize; + // Define WindowClass WNDCLASS wc = { 0 }; @@ -109,11 +110,14 @@ namespace olc::host AdjustWindowRectEx(&rWndRect, dwStyle, FALSE, dwExStyle); int width = rWndRect.right - rWndRect.left; int height = rWndRect.bottom - rWndRect.top; + pWindow->SetSize(vWindowSize); // Create the actual OS window, return a handle HWND hWnd = CreateWindowEx(dwExStyle, olcT("OLC_PIXEL_GAME_ENGINE3"), olcT(""), dwStyle, vTopLeft.x, vTopLeft.y, width, height, NULL, NULL, GetModuleHandle(nullptr), this); + SetWindowPos(hWnd, NULL, vWinPos.x, vWinPos.y, width, height, SWP_SHOWWINDOW); + // Now... awkwardly, the above has already fired off some window messages // and they arent necessarily in a consistent order. Whereas one might // assume WM_CREATE would be the first, there are some others on more @@ -187,7 +191,12 @@ namespace olc::host // case WM_MOVE: vWinPos = olc::vi2d(lParam & 0xFFFF, (lParam >> 16) & 0xFFFF); ptrPGE->olc_UpdateWindowPos(lParam & 0xFFFF, (lParam >> 16) & 0xFFFF); return 0; - // case WM_SIZE: vWinSize = olc::vi2d(lParam & 0xFFFF, (lParam >> 16) & 0xFFFF); ptrPGE->olc_UpdateWindowSize(lParam & 0xFFFF, (lParam >> 16) & 0xFFFF); return 0; + case WM_SIZE: + { + window->olc_OnWindowSize(olc::vi2d(lParam & 0xFFFF, (lParam >> 16) & 0xFFFF)); + return 0; + } + break; // 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; @@ -196,12 +205,36 @@ namespace olc::host // case WM_KEYUP: ptrPGE->olc_UpdateKeyState(int32_t(wParam), false); return 0; // case WM_SYSKEYDOWN: ptrPGE->olc_UpdateKeyState(int32_t(wParam), true); return 0; // case WM_SYSKEYUP: ptrPGE->olc_UpdateKeyState(int32_t(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_LBUTTONDOWN: + { + window->olc_OnMouseButton(0, true); + return 0; + } + case WM_LBUTTONUP: + { + window->olc_OnMouseButton(0, false); + return 0; + } + case WM_RBUTTONDOWN: + { + window->olc_OnMouseButton(1, true); + return 0; + } + case WM_RBUTTONUP: + { + window->olc_OnMouseButton(1, false); + return 0; + } + case WM_MBUTTONDOWN: + { + window->olc_OnMouseButton(2, true); + return 0; + } + case WM_MBUTTONUP: + { + window->olc_OnMouseButton(2, false); + return 0; + } // case WM_DROPFILES: // { // // This is all eww... diff --git a/dev/src/hw_input.h b/dev/src/hw_input.h new file mode 100644 index 00000000..e65f5e32 --- /dev/null +++ b/dev/src/hw_input.h @@ -0,0 +1,26 @@ +#pragma once + +//! START STDHEADER + +//! END STDHEADER + +//! START CUSTOMHEADER + +//! END CUSTOMHEADER + +//! START DECLARATION +#if !defined(PGE_HARDWAREINPUT_DECLARED) +namespace olc +{ + namespace hw + { + struct Button + { + bool bPressed = false; + bool bReleased = false; + bool bHeld = false; + }; + } +} +#define PGE_HARDWAREINPUT_DECLARED 1 +#endif diff --git a/dev/src/hw_keyboard.h b/dev/src/hw_keyboard.h new file mode 100644 index 00000000..6f70f09b --- /dev/null +++ b/dev/src/hw_keyboard.h @@ -0,0 +1 @@ +#pragma once diff --git a/dev/src/hw_mouse.cpp b/dev/src/hw_mouse.cpp new file mode 100644 index 00000000..bf2af5c6 --- /dev/null +++ b/dev/src/hw_mouse.cpp @@ -0,0 +1,51 @@ +#include "hw_mouse.h" + +//! START IMPLEMENTATION +namespace olc::hw +{ + const Button& Mouse::GetButton(const int nButton) const + { + return buttons.at(nButton); + } + + const olc::vf2d& Mouse::GetPosition() const + { + return position; + } + + void Mouse::SetPosition(const olc::vf2d& pos) + { + position_in = pos; + } + + void Mouse::SetButton(const int nButton, bool state) + { + buttons_new[nButton] = state; + } + + void Mouse::UpdateState() + { + for (size_t i=0; i +//! END STDHEADER + +//! START CUSTOMHEADER +#include "config.h" +#include "hw_input.h" +#include "vector2d.h" +//! END CUSTOMHEADER + +//! START DECLARATION +#if !defined(PGE_HW_MOUSE_DECLARED) +namespace olc +{ + // Forward declare for friendship + class Window; + + + namespace hw + { + class Mouse + { + friend class olc::Window; + + public: + Mouse() = default; + + public: + const Button& GetButton(const int nButton) const; + const olc::vf2d& GetPosition() const; + + protected: + std::array buttons{}; + std::array buttons_new{}; + std::array buttons_old{}; + olc::vf2d position; + olc::vf2d position_in; + + private: + void SetPosition(const olc::vf2d& pos); + void SetButton(const int nButton, bool state); + void UpdateState(); + }; + } +} +#define PGE_HW_MOUSE_DECLARED 1 +#endif diff --git a/dev/src/hw_touch.h b/dev/src/hw_touch.h new file mode 100644 index 00000000..6f70f09b --- /dev/null +++ b/dev/src/hw_touch.h @@ -0,0 +1 @@ +#pragma once diff --git a/dev/src/image.cpp b/dev/src/image.cpp index 7da56313..f84d5766 100644 --- a/dev/src/image.cpp +++ b/dev/src/image.cpp @@ -15,7 +15,7 @@ namespace olc { dimensions = size; config = cfg; - pixels.resize(dimensions.area(), olc::Pixel(255, 165, 0)); + pixels.resize(dimensions.area(), olc::Colour::TANGERINE); return true; } @@ -29,6 +29,11 @@ namespace olc return pixels.data(); } + olc::Pixel& Image::Pixel(const olc::vf2d& pos) + { + return pixels[int(pos.y) * dimensions.x + int(pos.x)]; + } + const ImageConfig& Image::GetConfig() const { return config; @@ -44,5 +49,43 @@ namespace olc gpuResourceID = id; } + std::vector& Image::GetPixels() + { + return pixels; + } + + bool Image::BoundToGPU() const + { + return onGPU; + } + + bool Image::BoundToCPU() const + { + return onCPU; + } + + olc::ImageRegion Image::region(const olc::vf2d pos, const olc::vf2d& size) + { + return region(pos, { pos.x + size.x, pos.y }, { pos.x, pos.y + size.y }, pos + size); + } + + olc::ImageRegion Image::region(const olc::vf2d& vTL, const olc::vf2d& vTR, const olc::vf2d& vBL, const olc::vf2d& vBR) + { + auto i = 1.0f / this->Size(); + return olc::ImageRegion(*this, vTL * i, vTR * i, vBL * i, vBR * i ); + } + + void Image::BindGPU() + { + onGPU = true; + onCPU = false; + } + + void Image::BindCPU() + { + onCPU = true; + onGPU = false; + } + }; //! END IMPLEMENTATION \ No newline at end of file diff --git a/dev/src/image.h b/dev/src/image.h index 3bba1b93..d8b9300e 100644 --- a/dev/src/image.h +++ b/dev/src/image.h @@ -27,6 +27,8 @@ namespace olc bool InVRAM = true; }; + struct ImageRegion; + class Image { public: @@ -42,29 +44,59 @@ namespace olc const olc::vi2d& Size() const; // Returns read/write pointer to start of 1D stream of pixel data olc::Pixel* Data(); + // [UNSAFE] Returns pixel at location + olc::Pixel& Pixel(const olc::vf2d& pos); // Returns how this image was configured upon creation const ImageConfig& GetConfig() const; // Return GPU Resource ID int32_t GetGPUID() const; // Set GPU Resource ID (0 to eliminate) void SetGPUID(const int32_t id); + // Get underlying vector of pixels + std::vector& GetPixels(); - // True if data mirror has in-front RAM image - bool HasWetCPU() const; - // True if data mirror has in-front VRAM image - bool HasWetGPU() const; + bool BoundToGPU() const; + bool BoundToCPU() const; + + olc::ImageRegion region(const olc::vf2d pos, const olc::vf2d& size); + olc::ImageRegion region(const olc::vf2d& vTL, const olc::vf2d& vTR, const olc::vf2d& vBL, const olc::vf2d& vBR); - void PrimeCPU(); - void PrimeGPU(); + + public: // Make friendly private later + void BindGPU(); + void BindCPU(); protected: ImageConfig config; olc::vi2d dimensions; std::vector pixels; int32_t gpuResourceID = 0; - bool gpuWet = false; - bool cpuWet = true; + bool onGPU = false; + bool onCPU = true; + }; + + struct ImageRegion + { + ImageRegion(olc::Image& i, const olc::vf2d& vTL = { 0,0 }, const olc::vf2d& vTR = { 1,0 }, const olc::vf2d& vBL = { 0,1 }, const olc::vf2d& vBR = { 1,1 }) + : image(i) + { + coords = { vTL, vTR, vBR, vBL }; + regionsize = (vBR - vTL) * image.Size(); + } + + olc::Image& image; + olc::vf2d regionsize; + union + { + std::array coords; + olc::vf2d tl; + olc::vf2d tr; + olc::vf2d br; + olc::vf2d bl; + }; }; + + } #define PGE_IMAGE_DECLARED 1 #endif diff --git a/dev/src/imload_iface.h b/dev/src/imload_iface.h new file mode 100644 index 00000000..8e22b45d --- /dev/null +++ b/dev/src/imload_iface.h @@ -0,0 +1,45 @@ +#pragma once + +//! START STDHEADER GLOBAL +#include +#include +//! END STDHEADER + +//! START CUSTOMHEADER +#include "config.h" +#include "image.h" +//! END CUSTOMHEADER + +//! START DECLARATION +#if !defined(PGE_IMAGELOADER_IFACE_DECLARED) +namespace olc +{ + namespace imload + { + class ImageLoader + { + public: + ImageLoader() = default; + virtual ~ImageLoader() = default; + + // Create an image resource based on an image file asset on disk + virtual bool CreateImageFromFile(olc::Image& image, const std::string& sFileName) = 0; + + // Create an image resource based on an image file asset in memory + virtual bool CreateImageFromMemory(olc::Image& image, const uint8_t* data, const size_t bytes) = 0; + + // Create an image resource based on an image file asset in memory + virtual bool CreateImageFromMemory(olc::Image& image, const std::vector& data) = 0; + + // Store an image as a file asset on disk + virtual bool WriteImageToFile(const olc::Image& image, const std::string& sFileName) = 0; + + // Store an image as a file asset in memory + virtual bool WriteImageToMemoryFile(olc::Image& image, const std::vector& data) = 0; + + }; + } +} +#define PGE_IMAGELOADER_IFACE_DECLARED 1 +#endif +//! END DECLARATION \ No newline at end of file diff --git a/dev/src/imload_wingdi.cpp b/dev/src/imload_wingdi.cpp new file mode 100644 index 00000000..5f2d5856 --- /dev/null +++ b/dev/src/imload_wingdi.cpp @@ -0,0 +1,90 @@ +#include "imload_wingdi.h" + +//! START IMPLEMENTATION +namespace olc::imload +{ + namespace internals + { + 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; + } + + static class GDIPlusStartup + { + public: + GDIPlusStartup() + { + Gdiplus::GdiplusStartupInput startupInput; + GdiplusStartup(&token, &startupInput, NULL); + } + + ULONG_PTR token; + + ~GDIPlusStartup() + { + Gdiplus::GdiplusShutdown(token); + } + + } gdistartup; + } + + bool ImageLoader_WinGDI::CreateImageFromFile(olc::Image& image, const std::string& sFileName) + { + // Check file exists + if (!std::filesystem::exists(sFileName)) + return false; // File doesn't exist + + // Load file into windows "bitmap". 1992 calling... + Gdiplus::Bitmap* bmp = nullptr; + bmp = Gdiplus::Bitmap::FromFile(internals::ConvertS2W(sFileName).c_str()); + if (bmp->GetLastStatus() != Gdiplus::Ok) + return false; // File wasn't valid + + // Need to swizzle each pixel... + image.Create(olc::vi2d(bmp->GetWidth(), bmp->GetHeight())); + for (int y = 0; y < image.Size().y; y++) + for (int x = 0; x < image.Size().x; x++) + { + Gdiplus::Color c; + bmp->GetPixel(x, y, &c); + image.Pixel(olc::vi2d(x, y)) = olc::Pixel(c.GetRed(), c.GetGreen(), c.GetBlue(), c.GetAlpha()); + } + + // All done + delete bmp; + return true; + } + + bool ImageLoader_WinGDI::CreateImageFromMemory(olc::Image& image, const uint8_t* data, const size_t bytes) + { + return false; + } + + bool ImageLoader_WinGDI::CreateImageFromMemory(olc::Image& image, const std::vector& data) + { + return false; + } + + bool ImageLoader_WinGDI::WriteImageToFile(const olc::Image& image, const std::string& sFileName) + { + return false; + } + + bool ImageLoader_WinGDI::WriteImageToMemoryFile(olc::Image& image, const std::vector& data) + { + return false; + } +} +//! END IMPLEMENTATION diff --git a/dev/src/imload_wingdi.h b/dev/src/imload_wingdi.h new file mode 100644 index 00000000..14dae709 --- /dev/null +++ b/dev/src/imload_wingdi.h @@ -0,0 +1,79 @@ +#pragma once + +//! START CUSTOMHEADER +#include "imload_iface.h" +//! END CUSTOMHEADER + +//! START STDHEADER GLOBAL +#include +//! END STDHEADER + +//! START WINAPI_CONFIG +#if defined(UNICODE) || defined(_UNICODE) + #define olcT(s) L##s +#else + #define olcT(s) s +#endif + +#define _WINSOCKAPI_ + +#if !defined(VC_EXTRALEAN) + #define VC_EXTRALEAN +#endif + +#if !defined(NOMINMAX) + #define NOMINMAX +#endif + +// 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 + +// Embrace MSVC superiority +#pragma comment(lib, "gdiplus.lib") +#pragma comment(lib, "Shlwapi.lib") +#include +#include +#include +#include +#undef _WINSOCKAPI_ +//! END WINAPI_CONFIG + +//! START DECLARATION +#if !defined(PGE_IMAGELOADER_WINGDI_DECLARED) +namespace olc +{ + namespace imload + { + class ImageLoader_WinGDI : public ImageLoader + { + public: + + // Create an image resource based on an image file asset on disk + bool CreateImageFromFile(olc::Image& image, const std::string& sFileName) override; + + // Create an image resource based on an image file asset in memory + bool CreateImageFromMemory(olc::Image& image, const uint8_t* data, const size_t bytes) override; + + // Create an image resource based on an image file asset in memory + bool CreateImageFromMemory(olc::Image& image, const std::vector& data) override; + + // Store an image as a file asset on disk + bool WriteImageToFile(const olc::Image& image, const std::string& sFileName) override; + + // Store an image as a file asset in memory + bool WriteImageToMemoryFile(olc::Image& image, const std::vector& data) override; + + }; + } +} + +#define PGE_IMAGELOADER_WINGDI_DECLARED 1 +#endif +//! END DECLARATION + diff --git a/dev/src/matrix3d.h b/dev/src/matrix3d.h index 9e75f1c9..1d90418b 100644 --- a/dev/src/matrix3d.h +++ b/dev/src/matrix3d.h @@ -81,6 +81,17 @@ namespace olc me(2, 2) = 1; } + inline constexpr std::array m4x4() + { + auto& me = (*this); + return { + me(0,0), me(1,0), me(2,0), 0, + me(0,1), me(1,1), me(2,1), 0, + me(0,2), me(1,2), me(2,2), 0, + 0, 0, 0, 1 + }; + } + // Create translation matrix via components template inline constexpr void translate(const Q x, const Q y) @@ -194,6 +205,18 @@ namespace olc return out; } + + // Transform a vector of v_2d by this matrix + template + inline constexpr auto transform(const std::vector>& v) + { + std::vector> o(v.size()); + std::transform(v.begin(), v.end(), o.begin(), [this](const olc::v_2d& i) {return (*this) * i; }); + return o; + } + + + // Return this matrix as a std::string, of the form "[c1r1, c2r1, c3r1]\n[c1r2, c2r2, c3r2]\n[c1r3, c2r3, c3r3]" inline std::string str() const { diff --git a/dev/src/olcpge3.h b/dev/src/olcpge3.h index 90d21e0d..aaf83f12 100644 --- a/dev/src/olcpge3.h +++ b/dev/src/olcpge3.h @@ -7,7 +7,11 @@ #include "transform2d.h" #include "window.h" #include "core.h" + #include "image.h" +#include "imload_iface.h" +#include "imload_wingdi.h" + #include "gputask.h" #include "gpu_iface.h" #include "gpu_opengl33.h" @@ -15,3 +19,4 @@ + diff --git a/dev/src/sh_template.h b/dev/src/sh_template.h index f334ebcd..79e3c5cb 100644 --- a/dev/src/sh_template.h +++ b/dev/src/sh_template.h @@ -21,11 +21,19 @@ //! GRAB image.h DECLARATION -//! GRAB window.h DECLARATION +//! GRAB imload_iface.h DECLARATION //! GRAB gputask.h DECLARATION //! GRAB gpu_iface.h DECLARATION + +//! GRAB draw2d.h DECLARATION + +//! GRAB hw_input.h DECLARATION + +//! GRAB hw_mouse.h DECLARATION + +//! GRAB window.h DECLARATION //! GRAB host_iface.h DECLARATION @@ -37,6 +45,7 @@ + #if OLC_HOST == OLC_HOST_WINDOWS //! GRAB host_win_winapi.h WINAPI_CONFIG @@ -51,15 +60,18 @@ //! GRAB gpu_opengl33.h DECLARATION #endif +#if OLC_IMAGELOADER == OLC_IMAGELOADER_WINGDI +//! GRAB imload_wingdi.h WINAPI_CONFIG + +//! GRAB imload_wingdi.h DECLARATION +#endif + + -#if defined(OLC_PGE_APPLICATION) && !defined(PGE_WINDOW_IMPLEMENTED) -//! GRAB window.cpp IMPLEMENTATION -#define PGE_WINDOW_IMPLEMENTED 1 -#endif #if defined(OLC_PGE_APPLICATION) && !defined(PGE_HOST_IMPLEMENTED) #if OLC_HOST == OLC_HOST_WINDOWS @@ -70,12 +82,18 @@ #if defined(OLC_PGE_APPLICATION) && !defined(PGE_GPU_IMPLEMENTED) #if OLC_GPU == OLC_GPU_OPENGL33 +//! GRAB gpu_iface.cpp IMPLEMENTATION //! GRAB api_opengl.cpp IMPLEMENTATION //! GRAB gpu_opengl33.cpp IMPLEMENTATION #endif #define PGE_GPU_IMPLEMENTED 1 #endif +#if defined(OLC_PGE_APPLICATION) && !defined(PGE_DRAW2D_IMPLEMENTED) +//! GRAB draw2d.cpp IMPLEMENTATION +#define PGE_DRAW2D_IMPLEMENTED 1 +#endif + #if defined(OLC_PGE_APPLICATION) && !defined(PGE_CORE_IMPLEMENTED) //! GRAB core.cpp IMPLEMENTATION #define PGE_CORE_IMPLEMENTED 1 @@ -86,4 +104,20 @@ #define PGE_IMAGE_IMPLEMENTED 1 #endif +#if defined(OLC_PGE_APPLICATION) && !defined(PGE_HW_MOUSE_IMPLEMENTED) +//! GRAB hw_mouse.cpp IMPLEMENTATION +#define PGE_HW_MOUSE_IMPLEMENTED 1 +#endif + +#if defined(OLC_PGE_APPLICATION) && !defined(PGE_WINDOW_IMPLEMENTED) +//! GRAB window.cpp IMPLEMENTATION +#define PGE_WINDOW_IMPLEMENTED 1 +#endif + +#if defined(OLC_PGE_APPLICATION) && !defined(PGE_IMAGELOADER_IMPLEMENTED) +#if OLC_IMAGELOADER == OLC_IMAGELOADER_WINGDI +//! GRAB imload_wingdi.cpp IMPLEMENTATION +#endif +#define PGE_IMAGELOADER_IMPLEMENTED 1 +#endif diff --git a/dev/src/transform2d.h b/dev/src/transform2d.h index 90e4a82e..0fcd5777 100644 --- a/dev/src/transform2d.h +++ b/dev/src/transform2d.h @@ -50,14 +50,14 @@ namespace olc // Transform a vector by this transform template - inline constexpr auto forward(const olc::v_2d& v) + inline constexpr auto forward(const olc::v_2d& v) const { return m_mForward * v; } // Transform a vector of v_2d by this transform template - inline constexpr auto forward(const std::vector>& v) + inline constexpr auto forward(const std::vector>& v) const { std::vector> o(v.size()); std::transform(v.begin(), v.end(), o.begin(), [this](const olc::v_2d& i) {return m_mForward * i; }); @@ -66,14 +66,14 @@ namespace olc // Transform a vector by the inverse of this transform template - inline constexpr auto inverse(const olc::v_2d& v) + inline constexpr auto inverse(const olc::v_2d& v) const { return m_mInverse * v; } // Transform a vector by this transform template - inline constexpr auto inverse(const std::vector>& v) + inline constexpr auto inverse(const std::vector>& v) const { std::vector> o(v.size()); std::transform(v.begin(), v.end(), o.begin(), [this](const olc::v_2d& i) {return m_mInverse * i; }); @@ -185,6 +185,13 @@ namespace olc update(); } + template + inline constexpr auto operator * (const olc::t_2d& rhs) const + { + auto& me = *this; + return me.forward_matrix() * rhs.forward_matrix(); + } + protected: // Constructs resultant matrices when transformation changes inline constexpr void update() diff --git a/dev/src/window.cpp b/dev/src/window.cpp index d1e3e1a0..35d17abd 100644 --- a/dev/src/window.cpp +++ b/dev/src/window.cpp @@ -38,12 +38,13 @@ namespace olc bool Window::olc_OnMouseButton(const uint8_t nButton, const bool bPressed) { + mouse.SetButton(nButton, bPressed); return false; } bool Window::olc_OnMouseMove(const olc::vi2d& vMousePos) - { - volatile_vMousePos = vMousePos; + { + mouse.SetPosition(olc::vf2d(vMousePos) / olc::vf2d(GetSize()) * imgPrimary.Size()); return true; } @@ -64,7 +65,7 @@ namespace olc bool Window::olc_OnWindowSize(const olc::vi2d& vWindowSize) { - return false; + return SetSize(vWindowSize); } bool Window::olc_ShouldRemove() const @@ -76,8 +77,22 @@ namespace olc { // Environmental changes - gpu->SetViewport({ 0,0 }, GetSize()); - gpu->ClearViewport(olc::Colour::TANGERINE, false, false); + // Input Changes + mouse.UpdateState(); + + + draw.SetGPU(gpu); + draw.SetTarget(imgPrimary); + + + gpu->DisplayPrepare(); + + + gpu->AssignTextureTarget(0, imgPrimary.GetGPUID()); + gpu->SetViewport({ 0,0 }, imgPrimary.Size()); + //gpu->ClearViewport(olc::Colour::BLACK, true, true); + + gpu->ApplyDefaultShader(); // User Update if (!OnUserUpdate(fElapsedTime)) @@ -90,7 +105,20 @@ namespace olc } } - + + // Finialise any outstanding tasks + draw.ProcessGPUTasks(); + + // Take the window's completed "screen" and draw it as a textured quad to the backbuffer + gpu->AssignTextureTarget(0, 0); + gpu->SetViewport({ 0,0 }, GetSize()); + gpu->ClearViewport(olc::Colour::MAGENTA, true, true); + draw.ClearTransform(); + //draw.ImageRect(imgPrimary, { -1.0,1.0 }, { 2.0f,-2.0f }); + draw.ImageRect(imgPrimary, { 0.0,0.0 }, GetSize()); + draw.ProcessGPUTasks(); + + // Update Window's primary surface gpu->DisplayDraw(); return true; diff --git a/dev/src/window.h b/dev/src/window.h index 6139c8c3..d8c88600 100644 --- a/dev/src/window.h +++ b/dev/src/window.h @@ -15,6 +15,8 @@ #include "pixel.h" #include "vector2d.h" #include "gpu_iface.h" +#include "draw2d.h" +#include "hw_mouse.h" //! END CUSTOMHEADER //! START DECLARATION @@ -37,9 +39,15 @@ namespace olc class Renderer; } + namespace hw + { + class Mouse; + } + class Window { friend class olc::host::FRIENDLY_HOST; + public: Window(); @@ -104,6 +112,12 @@ namespace olc olc::host::Host* pHost = nullptr; //olc::gpu::Renderer* pRenderer = nullptr; + + protected: + olc::Draw2D draw; + olc::Image imgPrimary; + + olc::hw::Mouse mouse; }; } #define PGE_WINDOW_DECLARED 1 diff --git a/dev/tests/olc.png b/dev/tests/olc.png new file mode 100644 index 00000000..fb4a6a39 Binary files /dev/null and b/dev/tests/olc.png differ diff --git a/dev/tests/test_mh.cpp b/dev/tests/test_mh.cpp index c4d64faa..eaaccc16 100644 --- a/dev/tests/test_mh.cpp +++ b/dev/tests/test_mh.cpp @@ -1,6 +1,8 @@ #define OLC_PGE_APPLICATION #include "olcpge3.h" + + class Example : public olc::PixelGameEngine { public: @@ -11,15 +13,279 @@ class Example : public olc::PixelGameEngine olc::Image imTest; + olc::Image imLogo; + olc::Image imTemp; + + olc::Image imSampleTest; + + void CreateSampleTestImage(olc::Image& image, const olc::vi2d& s) + { + CreateImage(image, s); + draw.SetTarget(image); + + draw.Clear(olc::Colour::WHITE); + + std::vector vColours = { + olc::Colour::RED, + olc::Colour::YELLOW, + olc::Colour::GREEN, + olc::Colour::CYAN, + olc::Colour::BLUE, + olc::Colour::MAGENTA, + }; + + for (int i = 0; i < s.x/2; i++) + { + draw.Rect(olc::vi2d{ i,i }, s - olc::vi2d{ (i * 2)+1 ,(i * 2)+1 }, vColours[i % vColours.size()]); + } + + draw.Pixel({ 0.0,1 }, olc::Colour::BLACK); + + } + + float fAngle = 0.0f; + + + struct logo + { + olc::vf2d pos; + olc::vf2d vel; + float ang; + float angvel; + }; + std::vector vecLogos; + + std::vector vecVerts; + + + int nSelectedVert = -1; + public: bool OnUserCreate() override { - CreateImage(imTest, { 128,128 }); + //CreateImage(imTest, { 64,64 }); + CreateImageFromFile(imLogo, "../tests/olc.png"); + + CreateSampleTestImage(imSampleTest, { 33, 33 }); + + CreateImage(imTemp, { 64, 64 }); + + size_t x = 1; + vecLogos.resize(x); + for (auto& a : vecLogos) + { + a.pos = olc::vf2d(rand() % imgPrimary.Size().x, rand() % imgPrimary.Size().y); + //a.vel = olc::vf2d(rand() % 100 - 50, rand() % 100 - 50); + a.angvel = 1.1f; + } + + vecVerts = { + { 200.0f, 200.0f}, + { 400.0f, 200.0f}, + { 400.0f, 400.0f}, + { 200.0f, 400.0f} + }; + return true; } bool OnUserUpdate(float fElapsedTime) override { + //draw.SetTarget(imTemp); + //draw.Clear(olc::Colour::BLANK); + //draw.FilledRect({ 20,5 }, { 6, 54 }, olc::Colour::GREEN); + //draw.FilledRect({ 0,0 }, imTemp.Size(), olc::Colour::BLANK); + + + //draw.SetTarget(imgPrimary); + //draw.Clear(olc::Colour::BLUE); + + + //draw.WorldReset(); + //draw.FilledRect({ 0,0 }, imgPrimary.Size() * olc::vf2d(0.5f, 1.0f), olc::Pixel(0, 0, 0, 1)); + + + draw.Clear(olc::Colour::VERY_DARK_BLUE); + + if (mouse.GetButton(1).bHeld) + { + fAngle += 0.2f * fElapsedTime; + draw.WorldRotate(fAngle, imgPrimary.Size() / 2.0f); + } + //for (int x = 0; x < imgPrimary.Size().x; x++) + // for (int y = 0; y < imgPrimary.Size().y; y++) + // draw.Pixel(olc::vf2d( x, y ), olc::Pixel(rand() % 255, rand() % 255, rand() % 255)); + + //// + //return true; + + + //std::cout << mouse.GetPosition() << "\n"; + auto vMouse = draw.ScreenToWorld(mouse.GetPosition()); + + if (mouse.GetButton(0).bPressed) + { + float dist = 100000.0f; + int idx = -1; + for (int i = 0; i < 4; i++) + { + float d = (vecVerts[i] - vMouse).mag(); + if (d < 8 && d < dist) + { + dist = d; + idx = i; + } + } + nSelectedVert = idx; + } + + + + if (nSelectedVert != -1 && mouse.GetButton(0).bHeld) + vecVerts[nSelectedVert] = vMouse; + + if (mouse.GetButton(0).bReleased) + nSelectedVert = -1; + + draw.ImageQuad(imLogo.region({ 0,0 }, { 10,10 }), vecVerts); + + draw.Line(vecVerts[0], vecVerts[1], olc::Colour::MAGENTA); + draw.Line(vecVerts[1], vecVerts[2], olc::Colour::MAGENTA); + draw.Line(vecVerts[2], vecVerts[3], olc::Colour::MAGENTA); + draw.Line(vecVerts[3], vecVerts[0], olc::Colour::MAGENTA); + + //draw.Image(imTemp, vMouse, { 4,4 }); + + draw.Image(imSampleTest, vMouse); + + + //draw.Pixel(vMouse, olc::Colour::GREEN); + //draw.Line(vMouse, vMouse + 1, olc::Colour::GREEN); + draw.FilledRect({ 5,5 }, { 10,10 }, olc::Colour::YELLOW); + draw.Rect({ 8,8 }, { 20,20 }, olc::Colour::RED); + + if(mouse.GetButton(0).bHeld) + draw.Line({ 1.0f, 1.0f }, { 25.5f, 25.5f }); + + //draw.Rect(vMouse, { 100,100 }); + + /*for (auto& a : vecLogos) + { + a.pos += a.vel * fElapsedTime; + a.ang += a.angvel * fElapsedTime; + + if (a.pos.x >= imgPrimary.Size().x - 46) + { + a.pos.x = imgPrimary.Size().x - 46; + a.vel.x *= -1.0f; + } + + if (a.pos.y >= imgPrimary.Size().y - 28) + { + a.pos.y = imgPrimary.Size().y - 28; + a.vel.y *= -1.0f; + } + + if (a.pos.x < 0) + { + a.pos.x = 0; + a.vel.x *= -1.0f; + } + + if (a.pos.y < 0) + { + a.pos.y = 0; + a.vel.y *= -1.0f; + } + + draw.ImageRotated(imLogo, a.pos, a.ang, { 10,10 }, { 2.0f, 4.0f }); + }*/ + + + + /*draw.SetTarget(imTest); + draw.FilledRect({ 0,0 }, imTest.Size(), olc::Colour::BLUE); + + draw.AffineRotate(fAngle, { 32.0f, 32.0f }); + + draw.Rect({ 5,5 }, { 10,10 }, olc::Colour::YELLOW); + draw.Line({ 0,0 }, { 20, 20 }); + + + draw.SetTarget(imgPrimary); + draw.AffineReset(); + + draw.FilledRect({ 0,0 }, imgPrimary.Size(), olc::Colour::VERY_DARK_MAGENTA); + + draw.Line({ 0,0 }, imgPrimary.Size() - 1, olc::Colour::RED); + draw.Line(olc::vf2d( imgPrimary.Size().x - 1, 0 ), olc::vf2d( 0, imgPrimary.Size().y - 1 ), olc::Colour::GREEN); + + if(mouse.GetButton(0).bHeld) + draw.Line({ 0,0 }, mouse.GetPosition(), olc::Colour::TANGERINE); + + draw.AffineRotate(fAngle * 0.2f, imgPrimary.Size() * 0.5f); + draw.Rect({ 0,0 }, imgPrimary.Size()); + draw.Image(imTest, { 10, 10 }, { 64, 64 }); + draw.Image(imTest, { 100, 10 }, { 64, 64 }); + draw.Image(imTest, { 10, 100 }, { 64, 64 }); + + draw.Image(sprite1, { 100, 100 }, { 64, 64 }); + draw.Image(sprite1.region({ 20.0f, 20.0f }, { 32.0f, 32.0f }), { 200, 100 }, { 32, 32 }); + draw.Image(sprite1.region({ 20.0f, 20.0f }, { 32.0f, 32.0f }, { 100.0f, 200.0f }, { 150.0f, 180.0f }), { 100, 200 }, { 32, 32 }); + olc::Pixel p = draw.GetPixel(imTest, { 8,5 }); + + draw.FilledRect({ 200,200 }, { 10,10 }, p);*/ + + ////draw.AffineRotate(fAngle, { 128,120 }); + ////draw.AffineOffset({ 10,10 }); + //draw.AffineScale(olc::vf2d(1,1) * (std::sin(fAngle) + 1.1)); + + //draw.Rect({ 0,0 }, GetSize() - olc::vf2d{1, 1}, olc::Colour::CYAN); + //draw.FillRect({ 100.0f, 30.0f }, { 20.0f, 50.0f }, olc::Colour::TANGERINE); + + + + //for (float i = 0; i < 20; i++) + //{ + // draw.AffineRotate(fAngle + (i/50.0f), {128,120}); + // if (fmod(i,2) == 1) + // { + // draw.Line({ 0 + i * 4 ,0 + i * 4 }, { 255 - i * 4, 0 + i * 4 }, olc::Colour::RED, olc::Colour::GREEN); + // draw.Line({ 255 - i * 4,0 + i * 4 }, { 255 - i * 4, 239 - i * 4 }, olc::Colour::GREEN, olc::Colour::CYAN); + // draw.Line({ 255 - i * 4,239 - i * 4 }, { 0 + i * 4, 239 - i * 4 }, olc::Colour::CYAN, olc::Colour::WHITE); + // draw.Line({ 0 + i * 4,239 - i * 4 }, { 0 + i * 4, 0 + i * 4 }, olc::Colour::WHITE, olc::Colour::RED); + // } + // else + // { + // draw.Line({ 0 + i * 4 ,0 + i * 4 }, { 255 - i * 4, 0 + i * 4 }, olc::Colour::RED); + // draw.Line({ 255 - i * 4,0 + i * 4 }, { 255 - i * 4, 239 - i * 4 }, olc::Colour::GREEN); + // draw.Line({ 255 - i * 4,239 - i * 4 }, { 0 + i * 4, 239 - i * 4 }, olc::Colour::CYAN); + // draw.Line({ 0 + i * 4,239 - i * 4 }, { 0 + i * 4, 0 + i * 4 }, olc::Colour::WHITE); + // } + //} + + ///*for (int x = 0; x < 100; x++) + //{ + // for (int y = 0; y < 100; y++) + // { + // if (x % 2 == 0) + // { + // draw.Pixel(olc::vf2d(x, y) + olc::vf2d(100, 100), olc::Colour::YELLOW); + // } + // } + //}*/ + + ////draw.Pixel({ -0.5f, -0.25f }, olc::Colour::YELLOW); + + //draw.FillRect({ 100.0f, 30.0f }, { 20.0f, 50.0f }, olc::Colour::TANGERINE); + + ////draw.Pixel({ 0.3f, 0.3f }, olc::Colour::BLACK); + ////draw.Pixel({ 0.32f, 0.3f }, olc::Colour::BLACK); + ////draw.Pixel({ 0.34f, 0.3f }, olc::Colour::BLACK); + + + + return true; } }; @@ -27,7 +293,15 @@ class Example : public olc::PixelGameEngine int main() { Example demo; - if (demo.Construct({ 256, 240 }, { 4, 4 })) + //if (demo.Construct({ 256, 240 }, { 4, 4 })) + + olc::PGEConfig cfg; + cfg.vPixelSize = { 4,4 }; + cfg.vScreenSize = { 256, 240 }; + //cfg.bVSync = true; + + //if (demo.Construct({ 1280, 960 }, { 1, 1 }, cfg)) + if(demo.Construct(cfg)) demo.Start(); return 0;