From 2ee840a7033381f48e23dbb303b69d694b7633e8 Mon Sep 17 00:00:00 2001 From: Javidx9 <25419386+OneLoneCoder@users.noreply.github.com> Date: Thu, 25 Sep 2025 22:58:00 +0100 Subject: [PATCH 01/17] Fixed SH + Demo --- .../olcPGE3_BuildSH/olcPixelGameEngine3.h | 625 +++++++++++++++--- dev/msvc/olcPGE3_BuildSH/test_sh.cpp | 20 +- dev/src/draw2d.cpp | 2 +- 3 files changed, 531 insertions(+), 116 deletions(-) diff --git a/dev/msvc/olcPGE3_BuildSH/olcPixelGameEngine3.h b/dev/msvc/olcPGE3_BuildSH/olcPixelGameEngine3.h index 6b5e2868..81592e4c 100644 --- a/dev/msvc/olcPGE3_BuildSH/olcPixelGameEngine3.h +++ b/dev/msvc/olcPGE3_BuildSH/olcPixelGameEngine3.h @@ -798,6 +798,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) @@ -983,14 +994,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; }); @@ -999,14 +1010,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; }); @@ -1164,6 +1175,8 @@ namespace olc bool InVRAM = true; }; + struct ImageRegion; + class Image { public: @@ -1193,6 +1206,10 @@ namespace olc 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); + + public: // Make friendly private later void BindGPU(); void BindCPU(); @@ -1205,6 +1222,29 @@ namespace olc 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 @@ -1256,6 +1296,7 @@ namespace olc enum class Task : uint8_t { DrawPolygon, + NullTask } task = Task::DrawPolygon; struct Vertex @@ -1495,50 +1536,115 @@ namespace olc 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 }); + 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 - void Pixel(const olc::vf2d& pos, const olc::Pixel col = olc::Colour::WHITE); + 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); + 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); + 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); + 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); + 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); + 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 an image - GPUTask Image(olc::Image& image, const olc::vf2d& pos, const olc::vf2d& size); + // 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); @@ -1575,6 +1681,34 @@ namespace olc 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); + + 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; }; } @@ -2260,6 +2394,7 @@ namespace olc uint32_t nDefaultVA = 0; uint32_t nDefaultFBO = 0; olc::Image imgBlank; + olc::vf2d vTargetSize; }; } @@ -2453,12 +2588,14 @@ namespace olc::host AdjustWindowRectEx(&rWndRect, dwStyle, FALSE, dwExStyle); int width = rWndRect.right - rWndRect.left; int height = rWndRect.bottom - rWndRect.top; - pWindow->SetSize({width, height}); + 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 @@ -3155,32 +3292,111 @@ namespace olc::gpu // 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.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("is3d"); + 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 @@ -3354,6 +3570,8 @@ namespace olc::gpu gl.glDrawBuffers(1, attachments.data()); // Bind buffers to textures gl.glFramebufferTexture2D(36160U, 36064U + slot, GL_TEXTURE_2D, texid, 0); + + return true; } @@ -3368,6 +3586,8 @@ namespace olc::gpu { auto& gl = olc::apis::opengl::gl::Get(); gl.glUseProgram(shaderDefault.GetShaderID()); + + return true; } @@ -3379,7 +3599,9 @@ namespace olc::gpu { 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()); @@ -3393,11 +3615,10 @@ namespace olc::gpu // 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()); + //gl.glUniformMatrix4fv(shaderDefault.GetUniform("mvp"), 1, true, task.mvpMatrix.data()); // Shader: Apply Global Tint float f[4] = { @@ -3408,34 +3629,44 @@ namespace olc::gpu }; 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); - } + 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 Depth Testing (if required) - if (task.bDepth) - gl.glEnable(GL_DEPTH_TEST); + // 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) @@ -3444,6 +3675,8 @@ namespace olc::gpu 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) @@ -3468,6 +3701,7 @@ namespace olc::gpu { auto& gl = olc::apis::opengl::gl::Get(); gl.glViewport(int(pos.x), int(pos.y), int(size.x), int(size.y)); + vTargetSize = size; return true; } @@ -3516,8 +3750,12 @@ void Draw2D::SetGPU(olc::gpu::Renderer* const 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 = ℑ @@ -3526,11 +3764,11 @@ void Draw2D::SetTarget(olc::Image& image) 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)) + -1.0f,// +(1.0f / float(pTarget->Size().x)), + -1.0f// + (1.0f / float(pTarget->Size().y)) )); - AffineReset(); + WorldReset(); pRenderer->AssignTextureTarget(0, pTarget->GetGPUID()); pRenderer->SetViewport({ 0,0 }, pTarget->Size()); @@ -3615,34 +3853,44 @@ void Draw2D::PrepareImageForHW(olc::Image& image) } } -void olc::Draw2D::AffineReset() +void olc::Draw2D::WorldReset() { transformAffine = olc::tf2d(); transformCombined = transformAffine * transformTarget; } -void olc::Draw2D::AffineScale(const olc::vf2d& vScale) +void olc::Draw2D::WorldScale(const olc::vf2d& vScale) { transformAffine.scale(vScale); transformCombined = transformAffine * transformTarget; } -void olc::Draw2D::AffineOffset(const olc::vf2d& vOffset) +void olc::Draw2D::WorldOffset(const olc::vf2d& vOffset) { transformAffine.translate(vOffset); transformCombined = transformAffine * transformTarget; } -void olc::Draw2D::AffineRotate(const float& fTheta, const olc::vf2d& vPoint) +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 = transformTarget.forward(pos); + olc::vf2d tpos = transformAffine.forward(pos); if (tpos.x >= 0 && tpos.y >= 0 && tpos.x < pTarget->Size().x && tpos.y < pTarget->Size().y) { PrepareTargetForSW(); @@ -3658,6 +3906,12 @@ olc::Pixel olc::Draw2D::GetPixel(olc::Image& image, const olc::vf2d& pos) 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; @@ -3676,7 +3930,7 @@ GPUTask olc::Draw2D::TaskDrawPolygon(GPUTask::Structure structure, const std::ve 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.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; } @@ -3703,6 +3957,17 @@ GPUTask olc::Draw2D::TaskTexturedPolygon(GPUTask::Structure structure, const std task.vertexBuffer.push_back({ vPoints[i].x, vPoints[i].y, 1.0f, 1.0f, vColours[i], vTexCoords[i].x, vTexCoords[i].y, 0, 0, 0, 0, 0, 0}); task.pImage = image; task.tint = tint; + //task.mvpMatrix = transformCombined.m4x4(); + return task; +} + +GPUTask olc::Draw2D::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) +{ + 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; } @@ -3712,7 +3977,8 @@ GPUTask Draw2D::Line(const olc::vf2d& p1, const olc::vf2d& p2, const olc::Pixel return vecGPUTasks.emplace_back( TaskDrawPolygon( GPUTask::Structure::Line, - transformCombined.transform({ p1, p2 }), + //transformCombined.transform({ p1, p2 }), + {p1, p2}, col, olc::Colour::WHITE )); @@ -3732,45 +3998,176 @@ GPUTask Draw2D::Line(const olc::vf2d& p1, const olc::vf2d& p2, const olc::Pixel 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, - 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 } }), + 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::FillRect(const olc::vf2d& pos, const olc::vf2d& size, const olc::Pixel col) +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 } }), + //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::Image& image, const olc::vf2d& pos, const olc::vf2d& size) +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); + 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 } }), + transformAffine.forward({ { 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({ { pos.x, pos.y }, { pos.x + size.x, pos.y }, { pos.x + size.x, pos.y + size.y }, { pos.x, pos.y + size.y } }), + //transformCombined.transform(vPoints), + vPoints, { olc::Colour::WHITE, olc::Colour::WHITE, olc::Colour::WHITE, olc::Colour::WHITE }, - { {0,0}, {1,0}, {1,1}, {0,1} }, - &image + { 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 + )); +} #define PGE_DRAW2D_IMPLEMENTED 1 #endif @@ -3798,8 +4195,7 @@ namespace olc bool PixelGameEngine::Construct(const PGEConfig& cfg) { config = cfg; - - return false; + return true; } bool PixelGameEngine::Start() @@ -3927,6 +4323,8 @@ namespace olc // 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 @@ -3942,7 +4340,11 @@ namespace olc CreateImage(imgPrimary, config.vScreenSize); - gpu->WriteTexture(imgPrimary.GetGPUID(), imgPrimary); + + + draw.SetGPU(gpu.get()); + gpu->ApplyDefaultShader(); + draw.SetTarget(imgPrimary); if (!OnUserCreate()) { @@ -3950,7 +4352,8 @@ namespace olc return; } - + draw.ProcessGPUTasks(); + draw.SetTarget(imgPrimary); // Initialise Input Devices @@ -4045,7 +4448,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; } @@ -4094,6 +4497,17 @@ namespace olc 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; @@ -4249,7 +4663,7 @@ namespace olc gpu->AssignTextureTarget(0, imgPrimary.GetGPUID()); gpu->SetViewport({ 0,0 }, imgPrimary.Size()); - gpu->ClearViewport(olc::Colour::BLACK, true, true); + //gpu->ClearViewport(olc::Colour::BLACK, true, true); gpu->ApplyDefaultShader(); @@ -4271,9 +4685,10 @@ namespace olc // 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); + gpu->ClearViewport(olc::Colour::MAGENTA, true, true); draw.ClearTransform(); - draw.Image(imgPrimary, { -1.0,1.0 }, { 2.0f,-2.0f }); + //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 diff --git a/dev/msvc/olcPGE3_BuildSH/test_sh.cpp b/dev/msvc/olcPGE3_BuildSH/test_sh.cpp index 2b7e2170..152090e0 100644 --- a/dev/msvc/olcPGE3_BuildSH/test_sh.cpp +++ b/dev/msvc/olcPGE3_BuildSH/test_sh.cpp @@ -32,18 +32,18 @@ class Example : public olc::PixelGameEngine draw.SetTarget(imTest); - draw.FillRect({ 0,0 }, imTest.Size(), olc::Colour::BLUE); + draw.FilledRect({ 0,0 }, imTest.Size(), olc::Colour::BLUE); - draw.AffineRotate(fAngle, { 32.0f, 32.0f }); + draw.WorldRotate(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.WorldReset(); - draw.FillRect({ 0,0 }, imgPrimary.Size(), olc::Colour::VERY_DARK_MAGENTA); + 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); @@ -51,16 +51,16 @@ class Example : public olc::PixelGameEngine if (mouse.GetButton(0).bHeld) draw.Line({ 0,0 }, mouse.GetPosition(), olc::Colour::TANGERINE); - draw.AffineRotate(fAngle * 0.2f, imgPrimary.Size() * 0.5f); + draw.WorldRotate(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(imTest, { 10, 10 }); + draw.Image(imTest, { 100, 10 }); + draw.Image(imTest, { 10, 100 }); + draw.Image(sprite1, { 100, 100 }, {0.1f, 0.1f}); olc::Pixel p = draw.GetPixel(imTest, { 8,5 }); - draw.FillRect({ 200,200 }, { 10,10 }, p); + draw.FilledRect({ 200,200 }, { 10,10 }, p); return true; } }; diff --git a/dev/src/draw2d.cpp b/dev/src/draw2d.cpp index d639ab34..81ae7dcd 100644 --- a/dev/src/draw2d.cpp +++ b/dev/src/draw2d.cpp @@ -326,7 +326,7 @@ GPUTask olc::Draw2D::Image(olc::ImageRegion image, const olc::vf2d& pos, const o 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 } }, + transformAffine.forward({ { 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]}, From 4760209c2633d8eb37f265abdd2e3a8388892fd0 Mon Sep 17 00:00:00 2001 From: Javidx9 <25419386+OneLoneCoder@users.noreply.github.com> Date: Sun, 23 Nov 2025 12:08:38 +0000 Subject: [PATCH 02/17] more gitignore bs --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index a93da9a7..dd66d0d7 100644 --- a/.gitignore +++ b/.gitignore @@ -36,3 +36,4 @@ /dev/tests/build /dev/msvc/olcPGE3_BuildSH/x64/Debug /dev/msvc/olcPGE3/x64/Release +/dev/msvc/x64/Debug/olcPGE3.tlog From 1e67837c6a3a45fe0a6ed0e95b4eadd65935a175 Mon Sep 17 00:00:00 2001 From: Javidx9 <25419386+OneLoneCoder@users.noreply.github.com> Date: Sun, 23 Nov 2025 12:12:20 +0000 Subject: [PATCH 03/17] [IMPORTANT!!!] This is a javid system stability commit ADDS Scaffold for multiple windows BUT breaks event parallelism BUT breaks OpenGL Context handling DO NOT TREAT AS GOSPEL --- dev/msvc/olcPGE3.vcxproj | 9 +- .../olcPGE3_BuildSH/olcPGE3_BuildSH.vcxproj | 8 +- .../olcPGE3_BuildSH/olcPixelGameEngine3.h | 783 ++++++++++-------- dev/msvc/olcPGE3_BuildSH/test_sh.cpp | 14 +- dev/src/api_opengl.cpp | 2 +- dev/src/config.h | 3 + dev/src/core.cpp | 277 +++++-- dev/src/core.h | 71 +- dev/src/draw2d.cpp | 116 ++- dev/src/draw2d.h | 46 +- dev/src/host_iface.h | 2 +- dev/src/host_win_winapi.cpp | 48 +- dev/src/host_win_winapi.h | 1 + dev/src/hw_mouse.cpp | 12 + dev/src/hw_mouse.h | 8 +- dev/src/image.h | 19 +- dev/src/imload_wingdi.h | 14 +- dev/src/pixel.h | 8 +- dev/src/sh_template.h | 16 +- dev/src/window.cpp | 97 +-- dev/src/window.h | 59 +- dev/tests/test_mh.cpp | 100 ++- utilities/olcUTIL3_GameMode.h | 49 ++ 23 files changed, 1053 insertions(+), 709 deletions(-) create mode 100644 utilities/olcUTIL3_GameMode.h diff --git a/dev/msvc/olcPGE3.vcxproj b/dev/msvc/olcPGE3.vcxproj index 75d91f2a..50fe935d 100644 --- a/dev/msvc/olcPGE3.vcxproj +++ b/dev/msvc/olcPGE3.vcxproj @@ -29,26 +29,26 @@ Application true - v143 + v145 Unicode Application false - v143 + v145 true Unicode Application true - v143 + v145 Unicode Application false - v143 + v145 true Unicode @@ -137,6 +137,7 @@ _DEBUG;_CONSOLE;%(PreprocessorDefinitions) true stdcpp20 + TurnOffAllWarnings Console diff --git a/dev/msvc/olcPGE3_BuildSH/olcPGE3_BuildSH.vcxproj b/dev/msvc/olcPGE3_BuildSH/olcPGE3_BuildSH.vcxproj index e93c0b77..3c66e2a5 100644 --- a/dev/msvc/olcPGE3_BuildSH/olcPGE3_BuildSH.vcxproj +++ b/dev/msvc/olcPGE3_BuildSH/olcPGE3_BuildSH.vcxproj @@ -35,26 +35,26 @@ Application true - v143 + v145 Unicode Application false - v143 + v145 true Unicode Application true - v143 + v145 Unicode Application false - v143 + v145 true Unicode diff --git a/dev/msvc/olcPGE3_BuildSH/olcPixelGameEngine3.h b/dev/msvc/olcPGE3_BuildSH/olcPixelGameEngine3.h index 81592e4c..d6e9bb73 100644 --- a/dev/msvc/olcPGE3_BuildSH/olcPixelGameEngine3.h +++ b/dev/msvc/olcPGE3_BuildSH/olcPixelGameEngine3.h @@ -111,6 +111,9 @@ #define LICENCE_DEFAULT "OneLoneCoder.com - Pixel Game Engine 3 - " +// De-Noise in MSVC (C++20) +#pragma warning(disable:4820) // Disable Padding Warnings + #if !defined(PGE_PIXEL_DECLARED) namespace olc @@ -168,7 +171,13 @@ namespace olc // Construction via individual channels inline constexpr Pixel(const uint8_t red, const uint8_t green, const uint8_t blue, const uint8_t alpha = 0xFF) - : a(alpha), b(blue), g(green), r(red) +#if PGE_PIXEL_LAYOUT == PGE_PIXEL_LAYOUT_RGBA + : r(red), g(green), b(blue), a(alpha) +#endif +#if PGE_PIXEL_LAYOUT == PGE_PIXEL_LAYOUT_ABGR + : a(alpha), b(blue), g(green), r(red) +#endif + { } // Construction via numeric assignment (e.g. #00DDBBFF) @@ -1225,13 +1234,6 @@ namespace olc 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 @@ -1242,6 +1244,18 @@ namespace olc olc::vf2d br; olc::vf2d bl; }; + + 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(); + } + + ImageRegion& operator=(ImageRegion& o) + { + + } }; @@ -1522,19 +1536,13 @@ namespace olc // Associate this drawing toolbox with a renderer void SetGPU(olc::gpu::Renderer* const renderer); + void ProcessGPUTasks(); 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(); @@ -1543,6 +1551,8 @@ namespace olc void WorldScale(const olc::vf2d& vScale); void WorldOffset(const olc::vf2d& vOffset); void WorldRotate(const float& fTheta, const olc::vf2d& vPoint = { 0,0 }); + void SetWorldTransform(const olc::tf2d& trans); + olc::tf2d& GetWorldTransform(); olc::vf2d WorldToScreen(const olc::vf2d& v) const; olc::vf2d ScreenToWorld(const olc::vf2d& v) const; @@ -1557,36 +1567,45 @@ namespace olc olc::Image& image, const olc::vf2d& pos); - // Clear + // Clear entire draw target to specific colour void Clear(const olc::Pixel& col); public: // Draws a single pixel wide line - GPUTask Line( + const 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 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 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 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); + // Draws a filled, multiple colour rectangle, with linear colour interpolation + const GPUTask& FilledRect( + 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 @@ -1617,34 +1636,35 @@ namespace olc // Draws a scaled image at specified location - GPUTask Image( + const 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( + const 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( + // Draws an image warped correctly to linearly fill a quadrilateral (formerly DrawWarped...) + const GPUTask& ImageQuad( olc::ImageRegion image, const olc::vf2d& vTL, const olc::vf2d& vTR, const olc::vf2d& vBR, const olc::vf2d& vBL); - GPUTask ImageQuad( + // Draws an image warped correctly to linearly fill a quadrilateral (formerly DrawWarped...) + const 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); + const GPUTask& ImageRect(olc::ImageRegion image, const olc::vf2d& pos, const olc::vf2d& size); @@ -1704,9 +1724,7 @@ namespace olc olc::Image* pTarget = nullptr; olc::gpu::Renderer* pRenderer = nullptr; - olc::tf2d transformTarget; olc::tf2d transformAffine; - olc::mf3d transformCombined; std::vector vecGPUTasks; @@ -1735,7 +1753,8 @@ namespace olc namespace olc { // Forward declare for friendship - class Window; + class Window; + class PGEWindow; namespace hw @@ -1743,6 +1762,7 @@ namespace olc class Mouse { friend class olc::Window; + friend class olc::PGEWindow; public: Mouse() = default; @@ -1750,6 +1770,7 @@ namespace olc public: const Button& GetButton(const int nButton) const; const olc::vf2d& GetPosition() const; + int32_t GetWheel() const; protected: std::array buttons{}; @@ -1757,10 +1778,13 @@ namespace olc std::array buttons_old{}; olc::vf2d position; olc::vf2d position_in; + int32_t wheel_in; + int32_t wheel; private: void SetPosition(const olc::vf2d& pos); void SetButton(const int nButton, bool state); + void SetWheel(const int32_t w); void UpdateState(); }; } @@ -1787,6 +1811,11 @@ namespace olc class Renderer; } + namespace imload + { + class ImageLoader; + } + namespace hw { class Mouse; @@ -1800,27 +1829,22 @@ namespace olc public: Window(); virtual ~Window(); + + void LinkToHost(olc::host::Host* host); - 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: + private: // These are called externally from the host // 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); + virtual bool olc_OnMouseButton(const uint8_t nButton, const bool bPressed); + virtual bool olc_OnMouseMove(const olc::vi2d& vMousePos); + virtual bool olc_OnMouseWheel(const int32_t nScroll); + virtual bool olc_OnMouseFocus(const bool bHasFocus); // Set Window State - bool olc_OnWindowPosition(const olc::vi2d& vWindowPos); - bool olc_OnWindowSize(const olc::vi2d& vWindowSize); + virtual bool olc_OnWindowPosition(const olc::vi2d& vWindowPos); + virtual bool olc_OnWindowSize(const olc::vi2d& vWindowSize); + virtual bool olc_OnWindowClose(); // Set Keyboard State @@ -1828,25 +1852,22 @@ namespace olc 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); + const olc::vi2d& GetWindowSize() const; + bool SetWindowSize(const olc::vi2d& vSize); + const olc::vi2d& GetWindowPosition() const; + bool SetWindowPosition(const olc::vi2d& vPosition); - protected: - virtual bool olc_PrimaryWindowInit(); + const std::string& GetWindowTitle() const; + bool SetWindowTitle(const std::string& sTitle); protected: + bool bRequestToClose = false; bool bShouldRemove = false; protected: @@ -1856,16 +1877,11 @@ namespace olc 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 @@ -1962,28 +1978,20 @@ namespace olc bool bAllowChildWindows = true; }; - class PixelGameEngine : protected Window + class PGEWindow : public 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{}); - + bool Create(const olc::vi2d& vScreenSize, const olc::vi2d& vPixelSize); + public: - bool Start(); - - public: // The "Overridables" - // Called once when the engine is ready and the window is created - bool OnUserCreate() override; - // Called every frame while the engine is running - bool OnUserUpdate(float fElapsedTime) override; - // Called when something requests the engine shut down - bool OnUserDestroy() override; - - + // 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(); + + public: // olc::Image Handling // Create an image resource bool CreateImage(olc::Image& image, const olc::vi2d& size, const ImageConfig& cfg = olc::ImageConfig()); @@ -1998,14 +2006,51 @@ namespace olc // Destroy an image void DestroyImage(olc::Image& image); + public: + void LinkToRenderer(olc::gpu::Renderer* gpu); + void LinkToImageLoader(olc::imload::ImageLoader* imload); + + + olc::Image& GetDefaultImage(); + + protected: - bool olc_PrimaryWindowInit() override; + bool olc_OnMouseMove(const olc::vi2d& vMousePos) override; + + public: + virtual bool olc_WindowUpdate(const float fElapsedTime); + + protected: + olc::Draw2D draw; + + + private: + olc::Image imgPrimary; + olc::gpu::Renderer* pRenderer = nullptr; + olc::imload::ImageLoader* pImageLoader = nullptr; + }; + + class PixelGameEngine : protected PGEWindow + { + 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: // Child Windows + bool AddChildWindow(std::shared_ptr window); private: void EngineThread(); private: - std::deque> deqChildWindows; + std::deque> deqChildWindows; // Frame Timing & Overall Clocking std::chrono::steady_clock::time_point timeFrame1; @@ -2421,13 +2466,13 @@ namespace olc #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 +//#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") @@ -2477,7 +2522,7 @@ namespace olc -#if defined(OLC_PGE_APPLICATION) && !defined(PGE_HOST_IMPLEMENTED) +#if defined(OLC_PGE3_APPLICATION) && !defined(PGE_HOST_IMPLEMENTED) #if OLC_HOST == OLC_HOST_WINDOWS namespace olc::host { @@ -2489,13 +2534,13 @@ namespace olc::host bool Host_Windows_WinAPI::StartSystemEventLoop() { MSG msg; - while (GetMessage(&msg, NULL, 0, 0) > 0) + while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) > 0) { TranslateMessage(&msg); DispatchMessage(&msg); } - return true; + return true; } // Static linkage to lpfnWndProc - the hWnd is tagged with meta-info to get @@ -2551,7 +2596,8 @@ namespace olc::host // Define WindowClass - WNDCLASS wc = { 0 }; + WNDCLASSEX wc = { 0 }; + wc.cbSize = sizeof(WNDCLASSEX); wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; @@ -2562,7 +2608,7 @@ namespace olc::host wc.lpszMenuName = nullptr; wc.hbrBackground = nullptr; wc.lpszClassName = olcT("OLC_PIXEL_GAME_ENGINE3"); - RegisterClass(&wc); + RegisterClassEx(&wc); // Define window furniture DWORD dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; @@ -2588,13 +2634,20 @@ namespace olc::host AdjustWindowRectEx(&rWndRect, dwStyle, FALSE, dwExStyle); int width = rWndRect.right - rWndRect.left; int height = rWndRect.bottom - rWndRect.top; - pWindow->SetSize(vWindowSize); + pWindow->SetWindowSize(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); + LONG_PTR lp = GetWindowLongPtr(hWnd, GWL_STYLE); + SetWindowLongPtr(hWnd, GWL_STYLE, lp | (WS_CAPTION | WS_SYSMENU | WS_POPUPWINDOW | WS_THICKFRAME)); + lp = GetWindowLongPtr(hWnd, GWL_EXSTYLE); + SetWindowLongPtr(hWnd, GWL_EXSTYLE, lp | (WS_EX_WINDOWEDGE)); + + //SetWindowPos(hWnd, NULL, vWinPos.x, vWinPos.y, width, height, SWP_SHOWWINDOW); + //ShowWindow(hWnd, 1); + //UpdateWindow(hWnd); // Now... awkwardly, the above has already fired off some window messages // and they arent necessarily in a consistent order. Whereas one might @@ -2608,7 +2661,7 @@ namespace olc::host mapUID2HWND.insert_or_assign(pWindow->GetUID(), hWnd); mapHWND2PTR.insert_or_assign(hWnd, pWindow); - pWindow->ConnecToHost(this); + //DragAcceptFiles(olc_hWnd, true); @@ -2619,9 +2672,9 @@ namespace olc::host bool Host_Windows_WinAPI::UpdateWindowFrameTitle(olc::Window* pWindow) { #ifdef UNICODE - SetWindowText(mapUID2HWND.at(pWindow->GetUID()), ConvertS2W(pWindow->GetTitle()).c_str()); + SetWindowText(mapUID2HWND.at(pWindow->GetUID()), ConvertS2W(pWindow->GetWindowTitle()).c_str()); #else - SetWindowText(mapUID2HWND.at(pWindow->GetUID()), sTitle.c_str()); + SetWindowText(mapUID2HWND.at(pWindow->GetUID()), pWindow->GetWindowTitle().c_str()); #endif return true; } @@ -2644,7 +2697,7 @@ namespace olc::host LRESULT Host_Windows_WinAPI::OnWindowEvent(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { if (!mapHWND2PTR.contains(hWnd)) - return false; + return DefWindowProc(hWnd, uMsg, wParam, lParam);; // Get target olc::Window const auto& window = mapHWND2PTR.at(hWnd); @@ -2675,7 +2728,13 @@ namespace olc::host return 0; } break; - // case WM_MOUSEWHEEL: ptrPGE->olc_UpdateMouseWheel(GET_WHEEL_DELTA_WPARAM(wParam)); return 0; + + case WM_MOUSEWHEEL: + { + window->olc_OnMouseWheel(GET_WHEEL_DELTA_WPARAM(wParam)); + return 0; + } + break; // case WM_MOUSELEAVE: ptrPGE->olc_UpdateMouseFocus(false); return 0; // case WM_SETFOCUS: ptrPGE->olc_UpdateKeyFocus(true); return 0; // case WM_KILLFOCUS: ptrPGE->olc_UpdateKeyFocus(false); return 0; @@ -2751,7 +2810,11 @@ namespace olc::host // break; // // - // case WM_CLOSE: ptrPGE->olc_Terminate(); return 0; + case WM_CLOSE: + { + window->olc_OnWindowClose(); + return 0; + } case WM_DESTROY: PostQuitMessage(0); DestroyWindow(hWnd); @@ -2765,7 +2828,7 @@ namespace olc::host #define PGE_HOST_IMPLEMENTED 1 #endif -#if defined(OLC_PGE_APPLICATION) && !defined(PGE_GPU_IMPLEMENTED) +#if defined(OLC_PGE3_APPLICATION) && !defined(PGE_GPU_IMPLEMENTED) #if OLC_GPU == OLC_GPU_OPENGL33 namespace olc { @@ -2849,7 +2912,7 @@ namespace olc::apis::opengl return bLoaded; } - bool gl::CheckError(const std::source_location loc) + bool gl::CheckError([[maybe_unused]] const std::source_location loc) { #if OLC_GPU_ERRORCHECK == 1 GLenum err; @@ -3736,7 +3799,7 @@ namespace olc::gpu #define PGE_GPU_IMPLEMENTED 1 #endif -#if defined(OLC_PGE_APPLICATION) && !defined(PGE_DRAW2D_IMPLEMENTED) +#if defined(OLC_PGE3_APPLICATION) && !defined(PGE_DRAW2D_IMPLEMENTED) using namespace olc; Draw2D::Draw2D() @@ -3759,38 +3822,14 @@ void Draw2D::SetTarget(olc::Image& 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)) - )); - + // Reset Affine transform to unity WorldReset(); + // Configure default render target 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) @@ -3856,25 +3895,31 @@ void Draw2D::PrepareImageForHW(olc::Image& image) 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; +} + +void olc::Draw2D::SetWorldTransform(const olc::tf2d& trans) +{ + transformAffine = trans; +} + +olc::tf2d& olc::Draw2D::GetWorldTransform() +{ + return transformAffine; } olc::vf2d olc::Draw2D::WorldToScreen(const olc::vf2d& v) const @@ -3937,7 +3982,12 @@ GPUTask olc::Draw2D::TaskDrawPolygon(GPUTask::Structure structure, const std::ve GPUTask olc::Draw2D::TaskFillPolygon(GPUTask::Structure structure, const std::vector& vPoints, const std::vector& vColours, const olc::Pixel tint) { - return GPUTask(); + GPUTask task; + task.structure = structure; + 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.tint = tint; + return task; } GPUTask olc::Draw2D::TaskFillPolygon(GPUTask::Structure structure, const std::vector& vPoints, const olc::Pixel colour, const olc::Pixel tint) @@ -3971,32 +4021,31 @@ GPUTask olc::Draw2D::TaskTexturedPolygon(GPUTask::Structure structure, const std return task; } -GPUTask Draw2D::Line(const olc::vf2d& p1, const olc::vf2d& p2, const olc::Pixel col) +const 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}, + transformAffine.forward({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) +const 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 }), + transformAffine.forward({ p1, p2 }), { c1, c2 }, olc::Colour::WHITE )); } -GPUTask olc::Draw2D::Rect(const olc::vf2d& pos, const olc::vf2d& size, const olc::Pixel col) +const 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 @@ -4014,18 +4063,9 @@ GPUTask olc::Draw2D::Rect(const olc::vf2d& pos, const olc::vf2d& size, const olc 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 )); @@ -4033,21 +4073,32 @@ GPUTask olc::Draw2D::Rect(const olc::vf2d& pos, const olc::vf2d& size, const olc } -GPUTask olc::Draw2D::FilledRect(const olc::vf2d& pos, const olc::vf2d& size, const olc::Pixel col) +const 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 } }, + transformAffine.forward({ { 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) +const GPUTask& olc::Draw2D::FilledRect(const olc::vf2d& pos, const olc::vf2d& size, const olc::Pixel colTL, const olc::Pixel colTR, const olc::Pixel colBL, const olc::Pixel colBR) +{ + PrepareTargetForHW(); + return vecGPUTasks.emplace_back( + TaskFillPolygon( + GPUTask::Structure::Fan, + transformAffine.forward({ { pos.x, pos.y }, { pos.x + size.x, pos.y }, { pos.x + size.x, pos.y + size.y }, { pos.x, pos.y + size.y } }), + { colTL, colTR, colBR, colBL }, + olc::Colour::WHITE + )); +} + +const 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); @@ -4059,7 +4110,6 @@ GPUTask olc::Draw2D::Image(olc::ImageRegion image, const olc::vf2d& pos, const o 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 } }), transformAffine.forward({ { 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 @@ -4068,7 +4118,7 @@ GPUTask olc::Draw2D::Image(olc::ImageRegion image, const olc::vf2d& pos, const o )); } -GPUTask olc::Draw2D::ImageRotated(olc::ImageRegion image, const olc::vf2d& pos, const float theta, const olc::vf2d& center, const olc::vf2d& scale) +const 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); @@ -4089,15 +4139,14 @@ GPUTask olc::Draw2D::ImageRotated(olc::ImageRegion image, const olc::vf2d& pos, return vecGPUTasks.emplace_back( TaskTexturedPolygon( GPUTask::Structure::Fan, - //transformCombined.transform(vPoints), - vPoints, + transformAffine.forward(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) +const 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); @@ -4132,8 +4181,7 @@ GPUTask olc::Draw2D::ImageQuad(olc::ImageRegion image, const olc::vf2d& vTL, con return vecGPUTasks.emplace_back( TaskTexturedPolygon( GPUTask::Structure::Fan, - //transformCombined.transform({ vTL, vTR, vBR, vBL }), - { vTL, vTR, vBR, vBL }, + transformAffine.forward({ 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] }, @@ -4141,16 +4189,24 @@ GPUTask olc::Draw2D::ImageQuad(olc::ImageRegion image, const olc::vf2d& vTL, con )); } - return GPUTask(); + // Default is just return a textured quad + return vecGPUTasks.emplace_back( + TaskTexturedPolygon( + GPUTask::Structure::Fan, + transformAffine.forward({ vTL, vTR, vBR, vBL }), + { 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 std::vector& vecPoints) +const 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) +const 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); @@ -4160,8 +4216,7 @@ GPUTask olc::Draw2D::ImageRect(olc::ImageRegion image, const olc::vf2d& pos, con 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 } }, + transformAffine.forward({ { 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] }, @@ -4172,78 +4227,85 @@ GPUTask olc::Draw2D::ImageRect(olc::ImageRegion image, const olc::vf2d& pos, con #define PGE_DRAW2D_IMPLEMENTED 1 #endif -#if defined(OLC_PGE_APPLICATION) && !defined(PGE_CORE_IMPLEMENTED) +#if defined(OLC_PGE3_APPLICATION) && !defined(PGE_CORE_IMPLEMENTED) namespace olc { - PixelGameEngine::PixelGameEngine() : Window() + bool PGEWindow::Create(const olc::vi2d& vScreenSize, const olc::vi2d& vPixelSize) { + CreateImage(GetDefaultImage(), vScreenSize); + SetWindowSize(vScreenSize * vPixelSize); + return true; } - PixelGameEngine::~PixelGameEngine() + bool PGEWindow::OnUserCreate() { + return true; } - bool PixelGameEngine::Construct(const olc::vi2d& vScreenSize, const olc::vi2d& vPixelSize, bool bFullScreen) + bool PGEWindow::OnUserUpdate(float fElapsedTime) { - config.vScreenSize = vScreenSize; - config.vPixelSize = vPixelSize; - config.bFullScreen = bFullScreen; - return true; } - bool PixelGameEngine::Construct(const PGEConfig& cfg) - { - config = cfg; + bool PGEWindow::OnUserDestroy() + { return true; } - bool PixelGameEngine::Start() + bool PGEWindow::olc_WindowUpdate(const float fElapsedTime) { - // Initialise Host Interface - host = std::make_unique(); - + // Input Changes + mouse.UpdateState(); - // Link this olc::Window to a host resource - if (host) - host->AddWindowFrame(this, { 30,30 }, config.vPixelSize * config.vScreenSize, false); - - coreActive = true; - coreThread = std::thread(&PixelGameEngine::EngineThread, this); - // This will block depending upon the system - host->StartSystemEventLoop(); + draw.SetGPU(pRenderer); + draw.SetTarget(GetDefaultImage()); - // Gracefully terminate the main engine thread - coreActive = false; - coreThread.join(); - return true; - } + pRenderer->ApplyDefaultShader(); - bool PixelGameEngine::OnUserCreate() - { - return false; - } - bool PixelGameEngine::OnUserUpdate(float fElapsedTime) - { - return false; - } - bool PixelGameEngine::OnUserDestroy() - { - return false; + // User Update + if (!OnUserUpdate(fElapsedTime) || bRequestToClose) + { + // User has requested termination of window by returning false + if (OnUserDestroy()) + { + // User has confirmed window destruction by returning true + bShouldRemove = true; + } + else + bRequestToClose = false; // User vetoed closure + } + + + // Finialise any outstanding tasks + draw.ProcessGPUTasks(); + + // Take the window's completed "screen" and draw it as a textured quad to the backbuffer + pRenderer->AssignTextureTarget(0, 0); + pRenderer->SetViewport({ 0,0 }, GetWindowSize()); + pRenderer->ClearViewport(olc::Colour::MAGENTA, true, true); + + draw.WorldReset(); + draw.ImageRect(imgPrimary, { 0.0,0.0 }, GetWindowSize()); + draw.ProcessGPUTasks(); + + // Update Window's primary surface + pRenderer->DisplayDraw(); + + return true; } - bool PixelGameEngine::CreateImage(olc::Image& image, const olc::vi2d& size, const ImageConfig& cfg) + bool PGEWindow::CreateImage(olc::Image& image, const olc::vi2d& size, const ImageConfig& cfg) { // Create CPU Image if (!image.Create(size, cfg)) return false; // Create GPU Image - auto id = gpu->CreateTexture(image.Size(), cfg); + auto id = pRenderer->CreateTexture(image.Size(), cfg); if (id == 0) { image.Create({ 0,0 }); @@ -4255,14 +4317,14 @@ namespace olc return true; } - bool PixelGameEngine::CreateImageFromFile(olc::Image& image, const std::string& sFileName, const ImageConfig& cfg) - { - if (imageloader->CreateImageFromFile(image, sFileName)) + bool PGEWindow::CreateImageFromFile(olc::Image& image, const std::string& sFileName, const ImageConfig& cfg) + { + if (pImageLoader->CreateImageFromFile(image, sFileName)) { // Image has loaded ok, and populated into pixel vector // // Create GPU Image - auto id = gpu->CreateTexture(image.Size(), cfg); + auto id = pRenderer->CreateTexture(image.Size(), cfg); if (id == 0) { image.Create({ 0,0 }); @@ -4277,27 +4339,22 @@ namespace olc return false; } - bool PixelGameEngine::CreateImageFromMemory(olc::Image& image, const uint8_t* data, const size_t bytes, const ImageConfig& cfg) + bool PGEWindow::CreateImageFromMemory(olc::Image& image, const uint8_t* data, const size_t bytes, const ImageConfig& cfg) { return false; } - bool PixelGameEngine::WriteImageToFile(const olc::Image& image, const std::string& sFileName) + bool PGEWindow::WriteImageToFile(const olc::Image& image, 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) + void PGEWindow::DestroyImage(olc::Image& image) { // If image has gpu resource, remove it if (image.GetGPUID() != 0) { - gpu->DeleteTexture(image.GetGPUID()); + pRenderer->DeleteTexture(image.GetGPUID()); image.SetGPUID(0); } @@ -4305,18 +4362,104 @@ namespace olc image.Create({ 0,0 }); } - bool PixelGameEngine::olc_PrimaryWindowInit() + void PGEWindow::LinkToRenderer(olc::gpu::Renderer* gpu) { - // Called by window to initialise core systems. Sub windows should ignore this - return false; + pRenderer = gpu; + } + + void PGEWindow::LinkToImageLoader(olc::imload::ImageLoader* imload) + { + pImageLoader = imload; + } + + olc::Image& PGEWindow::GetDefaultImage() + { + return imgPrimary; + } + + bool PGEWindow::olc_OnMouseMove(const olc::vi2d& vMousePos) + { + mouse.SetPosition(olc::vf2d(vMousePos) / olc::vf2d(GetWindowSize()) * GetDefaultImage().Size()); + return true; + } + + + + + + + + + PixelGameEngine::PixelGameEngine() : PGEWindow() + { + } + + PixelGameEngine::~PixelGameEngine() + { + } + + bool PixelGameEngine::Construct(const olc::vi2d& vScreenSize, const olc::vi2d& vPixelSize, bool bFullScreen) + { + config.vScreenSize = vScreenSize; + config.vPixelSize = vPixelSize; + config.bFullScreen = bFullScreen; + + return true; + } + + bool PixelGameEngine::Construct(const PGEConfig& cfg) + { + config = cfg; + return true; + } + + bool PixelGameEngine::Start() + { + // Initialise Host Interface + host = std::make_unique(); + + + + + + coreActive = true; + coreThread = std::thread(&PixelGameEngine::EngineThread, this); + + + + // Gracefully terminate the main engine thread + //coreActive = false; + coreThread.join(); + + return true; + } + + bool PixelGameEngine::AddChildWindow(std::shared_ptr window) + { + // Link this olc::Window to a host resource + if (host) + { + host->AddWindowFrame(window.get(), {30,30}, config.vPixelSize * config.vScreenSize, false); + window->LinkToHost(host.get()); + window->LinkToRenderer(gpu.get()); + window->LinkToImageLoader(imageloader.get()); + window->Create({ 64, 64 }, { 8,8 }); + deqChildWindows.push_back(window); + } + + return true; } + void PixelGameEngine::EngineThread() { using namespace std::chrono_literals; timeFrame2 = std::chrono::steady_clock::now(); timeFrame1 = std::chrono::steady_clock::now(); + // Create Primary Window + host->AddWindowFrame(this, { 30,30 }, config.vPixelSize * config.vScreenSize, false); + // Initialise ImageLoader Interface imageloader = std::make_unique(); @@ -4327,6 +4470,11 @@ namespace olc gpu = std::make_unique(); + // Link this windows devices + LinkToHost(host.get()); + LinkToRenderer(gpu.get()); + LinkToImageLoader(imageloader.get()); + // The GPU device can be based upon the primary window configuration. This // gives us completed gpu and host objects to pass to other windows as and // when required @@ -4339,12 +4487,12 @@ namespace olc } - CreateImage(imgPrimary, config.vScreenSize); + CreateImage(GetDefaultImage(), config.vScreenSize); draw.SetGPU(gpu.get()); gpu->ApplyDefaultShader(); - draw.SetTarget(imgPrimary); + draw.SetTarget(GetDefaultImage()); if (!OnUserCreate()) { @@ -4353,7 +4501,7 @@ namespace olc } draw.ProcessGPUTasks(); - draw.SetTarget(imgPrimary); + draw.SetTarget(GetDefaultImage()); // Initialise Input Devices @@ -4362,6 +4510,9 @@ namespace olc while (coreActive) { + // This will block depending upon the system + host->StartSystemEventLoop(); + // Frame Delta Timing - "ElapsedTime" since last core update // ~~~~~~~~~~~~~~~~~~ // All timing is synchronous to the primary window, i.e child @@ -4389,42 +4540,45 @@ namespace olc { durationFrameCount -= 1s; std::string sTitle = "OneLoneCoder.com - Pixel Game Engine 3 - Test - FPS: " + std::to_string(frameCount); - SetTitle(sTitle); + SetWindowTitle(sTitle); frameCount = 0; } - - // Update Child Windows (if any) - for (auto& winChild : deqChildWindows) - winChild->olc_WindowUpdate(fDT, gpu.get()); - - // Update Primary Window - olc_WindowUpdate(fDT, gpu.get()); - - // Wait for vertical sync if required. - // Note: Child windows will never vsync as waiting for each buffer swap with vsync - // divides up the frame rate budget across the windows. - if (gpu->GetConfig().VerticalSync) - { - host->SyncWithDesktopComposite(); - } - - // Remove child windows that have requested closure - if (!deqChildWindows.empty()) - { - deqChildWindows.erase(std::remove_if(deqChildWindows.begin(), deqChildWindows.end(), - [](const std::shared_ptr& w) - { - return w->olc_ShouldRemove(); - } - )); - } + // Primary Window if (olc_ShouldRemove()) { - // Application is to be terminated + // Application is to be terminated as primary window has closed coreActive = false; } + else + { + // Update Child Windows (if any) + for (auto& winChild : deqChildWindows) + winChild->olc_WindowUpdate(fDT); + + // Remove child windows that have requested closure + if (!deqChildWindows.empty()) + { + deqChildWindows.erase(std::remove_if(deqChildWindows.begin(), deqChildWindows.end(), + [](const std::shared_ptr& w) + { + return w->olc_ShouldRemove(); + } + ), deqChildWindows.end()); + } + + // Update Primary Window + olc_WindowUpdate(fDT); + + // Wait for vertical sync if required. + // Note: Child windows will never vsync as waiting for each buffer swap with vsync + // divides up the frame rate budget across the windows. + if (gpu->GetConfig().VerticalSync) + { + host->SyncWithDesktopComposite(); + } + } } @@ -4433,7 +4587,7 @@ namespace olc #define PGE_CORE_IMPLEMENTED 1 #endif -#if defined(OLC_PGE_APPLICATION) && !defined(PGE_IMAGE_IMPLEMENTED) +#if defined(OLC_PGE3_APPLICATION) && !defined(PGE_IMAGE_IMPLEMENTED) namespace olc { /*Image::Image(const olc::vi2d& size, const ImageConfig& cfg) @@ -4524,7 +4678,7 @@ namespace olc #define PGE_IMAGE_IMPLEMENTED 1 #endif -#if defined(OLC_PGE_APPLICATION) && !defined(PGE_HW_MOUSE_IMPLEMENTED) +#if defined(OLC_PGE3_APPLICATION) && !defined(PGE_HW_MOUSE_IMPLEMENTED) namespace olc::hw { const Button& Mouse::GetButton(const int nButton) const @@ -4537,6 +4691,11 @@ namespace olc::hw return position; } + int32_t Mouse::GetWheel() const + { + return wheel; + } + void Mouse::SetPosition(const olc::vf2d& pos) { position_in = pos; @@ -4547,6 +4706,11 @@ namespace olc::hw buttons_new[nButton] = state; } + void Mouse::SetWheel(const int32_t w) + { + wheel_in = w; + } + 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) { @@ -4617,13 +4769,14 @@ namespace olc bool Window::olc_OnMouseMove(const olc::vi2d& vMousePos) { - mouse.SetPosition(olc::vf2d(vMousePos) / olc::vf2d(GetSize()) * imgPrimary.Size()); + mouse.SetPosition(olc::vf2d(vMousePos) / olc::vf2d(GetWindowSize())); return true; } bool Window::olc_OnMouseWheel(const int32_t nScroll) { - return false; + mouse.SetWheel(nScroll); + return true; } bool Window::olc_OnMouseFocus(const bool bHasFocus) @@ -4638,81 +4791,37 @@ namespace olc bool Window::olc_OnWindowSize(const olc::vi2d& vWindowSize) { - return SetSize(vWindowSize); + return SetWindowSize(vWindowSize); } - bool Window::olc_ShouldRemove() const + bool Window::olc_OnWindowClose() { - return bShouldRemove; + bRequestToClose = true; + return true; } - bool Window::olc_WindowUpdate(const float fElapsedTime, olc::gpu::Renderer* const gpu) + bool Window::olc_ShouldRemove() const { - // 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::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; + return bShouldRemove; } + size_t Window::GetUID() const { return nUniqueID; } - const olc::vi2d& Window::GetSize() const + const olc::vi2d& Window::GetWindowSize() const { return vWindowSize; } - const olc::vi2d& Window::GetPosition() const + const olc::vi2d& Window::GetWindowPosition() const { return vWindowPos; } - bool Window::SetSize(const olc::vi2d& vSize) + bool Window::SetWindowSize(const olc::vi2d& vSize) { // Request host to change geometry of window object @@ -4720,7 +4829,7 @@ namespace olc return true; } - bool Window::SetPosition(const olc::vi2d& vPosition) + bool Window::SetWindowPosition(const olc::vi2d& vPosition) { // Request host to change geometry of window object @@ -4728,27 +4837,23 @@ namespace olc return true; } - const std::string& Window::GetTitle() const + const std::string& Window::GetWindowTitle() const { return sFrameTitle; } - bool Window::SetTitle(const std::string& sTitle) + bool Window::SetWindowTitle(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 defined(OLC_PGE3_APPLICATION) && !defined(PGE_IMAGELOADER_IMPLEMENTED) #if OLC_IMAGELOADER == OLC_IMAGELOADER_WINGDI namespace olc::imload { diff --git a/dev/msvc/olcPGE3_BuildSH/test_sh.cpp b/dev/msvc/olcPGE3_BuildSH/test_sh.cpp index 152090e0..5c24cbb9 100644 --- a/dev/msvc/olcPGE3_BuildSH/test_sh.cpp +++ b/dev/msvc/olcPGE3_BuildSH/test_sh.cpp @@ -1,4 +1,4 @@ -#define OLC_PGE_APPLICATION +#define OLC_PGE3_APPLICATION #include "olcPixelGameEngine3.h" class Example : public olc::PixelGameEngine @@ -40,19 +40,19 @@ class Example : public olc::PixelGameEngine draw.Line({ 0,0 }, { 20, 20 }); - draw.SetTarget(imgPrimary); + draw.SetTarget(GetDefaultImage()); draw.WorldReset(); - draw.FilledRect({ 0,0 }, imgPrimary.Size(), olc::Colour::VERY_DARK_MAGENTA); + draw.FilledRect({ 0,0 }, GetDefaultImage().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); + draw.Line({ 0,0 }, GetDefaultImage().Size() - 1, olc::Colour::RED); + draw.Line(olc::vf2d(GetDefaultImage().Size().x - 1, 0), olc::vf2d(0, GetDefaultImage().Size().y - 1), olc::Colour::GREEN); if (mouse.GetButton(0).bHeld) draw.Line({ 0,0 }, mouse.GetPosition(), olc::Colour::TANGERINE); - draw.WorldRotate(fAngle * 0.2f, imgPrimary.Size() * 0.5f); - draw.Rect({ 0,0 }, imgPrimary.Size()); + draw.WorldRotate(fAngle * 0.2f, GetDefaultImage().Size() * 0.5f); + draw.Rect({ 0,0 }, GetDefaultImage().Size()); draw.Image(imTest, { 10, 10 }); draw.Image(imTest, { 100, 10 }); draw.Image(imTest, { 10, 100 }); diff --git a/dev/src/api_opengl.cpp b/dev/src/api_opengl.cpp index 6d254174..51dc1381 100644 --- a/dev/src/api_opengl.cpp +++ b/dev/src/api_opengl.cpp @@ -52,7 +52,7 @@ namespace olc::apis::opengl return bLoaded; } - bool gl::CheckError(const std::source_location loc) + bool gl::CheckError([[maybe_unused]] const std::source_location loc) { #if OLC_GPU_ERRORCHECK == 1 GLenum err; diff --git a/dev/src/config.h b/dev/src/config.h index 93ec4638..030a2d84 100644 --- a/dev/src/config.h +++ b/dev/src/config.h @@ -87,4 +87,7 @@ #define LICENCE_DEFAULT "OneLoneCoder.com - Pixel Game Engine 3 - " +// De-Noise in MSVC (C++20) +#pragma warning(disable:4820) // Disable Padding Warnings + //! END CONFIGURATION \ No newline at end of file diff --git a/dev/src/core.cpp b/dev/src/core.cpp index 33781124..74b64314 100644 --- a/dev/src/core.cpp +++ b/dev/src/core.cpp @@ -7,75 +7,82 @@ //! START IMPLEMENTATION namespace olc { - PixelGameEngine::PixelGameEngine() : Window() + bool PGEWindow::Create(const olc::vi2d& vScreenSize, const olc::vi2d& vPixelSize) { + CreateImage(GetDefaultImage(), vScreenSize); + SetWindowSize(vScreenSize * vPixelSize); + return true; } - PixelGameEngine::~PixelGameEngine() + bool PGEWindow::OnUserCreate() { + return true; } - bool PixelGameEngine::Construct(const olc::vi2d& vScreenSize, const olc::vi2d& vPixelSize, bool bFullScreen) + bool PGEWindow::OnUserUpdate(float fElapsedTime) { - config.vScreenSize = vScreenSize; - config.vPixelSize = vPixelSize; - config.bFullScreen = bFullScreen; - return true; } - bool PixelGameEngine::Construct(const PGEConfig& cfg) - { - config = cfg; + bool PGEWindow::OnUserDestroy() + { return true; } - bool PixelGameEngine::Start() + bool PGEWindow::olc_WindowUpdate(const float fElapsedTime) { - // Initialise Host Interface - host = std::make_unique(); - + // Input Changes + mouse.UpdateState(); - // Link this olc::Window to a host resource - if (host) - host->AddWindowFrame(this, { 30,30 }, config.vPixelSize * config.vScreenSize, false); - - coreActive = true; - coreThread = std::thread(&PixelGameEngine::EngineThread, this); - // This will block depending upon the system - host->StartSystemEventLoop(); + draw.SetGPU(pRenderer); + draw.SetTarget(GetDefaultImage()); - // Gracefully terminate the main engine thread - coreActive = false; - coreThread.join(); - return true; - } + pRenderer->ApplyDefaultShader(); - bool PixelGameEngine::OnUserCreate() - { - return false; - } - bool PixelGameEngine::OnUserUpdate(float fElapsedTime) - { - return false; - } - bool PixelGameEngine::OnUserDestroy() - { - return false; + // User Update + if (!OnUserUpdate(fElapsedTime) || bRequestToClose) + { + // User has requested termination of window by returning false + if (OnUserDestroy()) + { + // User has confirmed window destruction by returning true + bShouldRemove = true; + } + else + bRequestToClose = false; // User vetoed closure + } + + + // Finialise any outstanding tasks + draw.ProcessGPUTasks(); + + // Take the window's completed "screen" and draw it as a textured quad to the backbuffer + pRenderer->AssignTextureTarget(0, 0); + pRenderer->SetViewport({ 0,0 }, GetWindowSize()); + pRenderer->ClearViewport(olc::Colour::MAGENTA, true, true); + + draw.WorldReset(); + draw.ImageRect(imgPrimary, { 0.0,0.0 }, GetWindowSize()); + draw.ProcessGPUTasks(); + + // Update Window's primary surface + pRenderer->DisplayDraw(); + + return true; } - bool PixelGameEngine::CreateImage(olc::Image& image, const olc::vi2d& size, const ImageConfig& cfg) + bool PGEWindow::CreateImage(olc::Image& image, const olc::vi2d& size, const ImageConfig& cfg) { // Create CPU Image if (!image.Create(size, cfg)) return false; // Create GPU Image - auto id = gpu->CreateTexture(image.Size(), cfg); + auto id = pRenderer->CreateTexture(image.Size(), cfg); if (id == 0) { image.Create({ 0,0 }); @@ -87,14 +94,14 @@ namespace olc return true; } - bool PixelGameEngine::CreateImageFromFile(olc::Image& image, const std::string& sFileName, const ImageConfig& cfg) - { - if (imageloader->CreateImageFromFile(image, sFileName)) + bool PGEWindow::CreateImageFromFile(olc::Image& image, const std::string& sFileName, const ImageConfig& cfg) + { + if (pImageLoader->CreateImageFromFile(image, sFileName)) { // Image has loaded ok, and populated into pixel vector // // Create GPU Image - auto id = gpu->CreateTexture(image.Size(), cfg); + auto id = pRenderer->CreateTexture(image.Size(), cfg); if (id == 0) { image.Create({ 0,0 }); @@ -109,27 +116,22 @@ namespace olc return false; } - bool PixelGameEngine::CreateImageFromMemory(olc::Image& image, const uint8_t* data, const size_t bytes, const ImageConfig& cfg) + bool PGEWindow::CreateImageFromMemory(olc::Image& image, const uint8_t* data, const size_t bytes, const ImageConfig& cfg) { return false; } - bool PixelGameEngine::WriteImageToFile(const olc::Image& image, const std::string& sFileName) + bool PGEWindow::WriteImageToFile(const olc::Image& image, 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) + void PGEWindow::DestroyImage(olc::Image& image) { // If image has gpu resource, remove it if (image.GetGPUID() != 0) { - gpu->DeleteTexture(image.GetGPUID()); + pRenderer->DeleteTexture(image.GetGPUID()); image.SetGPUID(0); } @@ -137,18 +139,104 @@ namespace olc image.Create({ 0,0 }); } - bool PixelGameEngine::olc_PrimaryWindowInit() + void PGEWindow::LinkToRenderer(olc::gpu::Renderer* gpu) { - // Called by window to initialise core systems. Sub windows should ignore this - return false; + pRenderer = gpu; } + void PGEWindow::LinkToImageLoader(olc::imload::ImageLoader* imload) + { + pImageLoader = imload; + } + + olc::Image& PGEWindow::GetDefaultImage() + { + return imgPrimary; + } + + bool PGEWindow::olc_OnMouseMove(const olc::vi2d& vMousePos) + { + mouse.SetPosition(olc::vf2d(vMousePos) / olc::vf2d(GetWindowSize()) * GetDefaultImage().Size()); + return true; + } + + + + + + + + + PixelGameEngine::PixelGameEngine() : PGEWindow() + { + } + + PixelGameEngine::~PixelGameEngine() + { + } + + bool PixelGameEngine::Construct(const olc::vi2d& vScreenSize, const olc::vi2d& vPixelSize, bool bFullScreen) + { + config.vScreenSize = vScreenSize; + config.vPixelSize = vPixelSize; + config.bFullScreen = bFullScreen; + + return true; + } + + bool PixelGameEngine::Construct(const PGEConfig& cfg) + { + config = cfg; + return true; + } + + bool PixelGameEngine::Start() + { + // Initialise Host Interface + host = std::make_unique(); + + + + + + coreActive = true; + coreThread = std::thread(&PixelGameEngine::EngineThread, this); + + + + // Gracefully terminate the main engine thread + //coreActive = false; + coreThread.join(); + + return true; + } + + bool PixelGameEngine::AddChildWindow(std::shared_ptr window) + { + // Link this olc::Window to a host resource + if (host) + { + host->AddWindowFrame(window.get(), {30,30}, config.vPixelSize * config.vScreenSize, false); + window->LinkToHost(host.get()); + window->LinkToRenderer(gpu.get()); + window->LinkToImageLoader(imageloader.get()); + window->Create({ 64, 64 }, { 8,8 }); + deqChildWindows.push_back(window); + } + + return true; + } + + void PixelGameEngine::EngineThread() { using namespace std::chrono_literals; timeFrame2 = std::chrono::steady_clock::now(); timeFrame1 = std::chrono::steady_clock::now(); + // Create Primary Window + host->AddWindowFrame(this, { 30,30 }, config.vPixelSize * config.vScreenSize, false); + // Initialise ImageLoader Interface imageloader = std::make_unique(); @@ -159,6 +247,11 @@ namespace olc gpu = std::make_unique(); + // Link this windows devices + LinkToHost(host.get()); + LinkToRenderer(gpu.get()); + LinkToImageLoader(imageloader.get()); + // The GPU device can be based upon the primary window configuration. This // gives us completed gpu and host objects to pass to other windows as and // when required @@ -171,12 +264,12 @@ namespace olc } - CreateImage(imgPrimary, config.vScreenSize); + CreateImage(GetDefaultImage(), config.vScreenSize); draw.SetGPU(gpu.get()); gpu->ApplyDefaultShader(); - draw.SetTarget(imgPrimary); + draw.SetTarget(GetDefaultImage()); if (!OnUserCreate()) { @@ -185,7 +278,7 @@ namespace olc } draw.ProcessGPUTasks(); - draw.SetTarget(imgPrimary); + draw.SetTarget(GetDefaultImage()); // Initialise Input Devices @@ -194,6 +287,9 @@ namespace olc while (coreActive) { + // This will block depending upon the system + host->StartSystemEventLoop(); + // Frame Delta Timing - "ElapsedTime" since last core update // ~~~~~~~~~~~~~~~~~~ // All timing is synchronous to the primary window, i.e child @@ -221,42 +317,49 @@ namespace olc { durationFrameCount -= 1s; std::string sTitle = "OneLoneCoder.com - Pixel Game Engine 3 - Test - FPS: " + std::to_string(frameCount); - SetTitle(sTitle); + SetWindowTitle(sTitle); frameCount = 0; } - - // Update Child Windows (if any) - for (auto& winChild : deqChildWindows) - winChild->olc_WindowUpdate(fDT, gpu.get()); - - // Update Primary Window - olc_WindowUpdate(fDT, gpu.get()); - - // Wait for vertical sync if required. - // Note: Child windows will never vsync as waiting for each buffer swap with vsync - // divides up the frame rate budget across the windows. - if (gpu->GetConfig().VerticalSync) - { - host->SyncWithDesktopComposite(); - } - - // Remove child windows that have requested closure - if (!deqChildWindows.empty()) - { - deqChildWindows.erase(std::remove_if(deqChildWindows.begin(), deqChildWindows.end(), - [](const std::shared_ptr& w) - { - return w->olc_ShouldRemove(); - } - )); - } + // Primary Window if (olc_ShouldRemove()) { - // Application is to be terminated + // Application is to be terminated as primary window has closed coreActive = false; } + else + { + // Update Child Windows (if any) + for (auto& winChild : deqChildWindows) + winChild->olc_WindowUpdate(fDT); + + // Remove child windows that have requested closure + if (!deqChildWindows.empty()) + { + deqChildWindows.erase(std::remove_if(deqChildWindows.begin(), deqChildWindows.end(), + [this](const std::shared_ptr& w) + { + if (w->olc_ShouldRemove()) + { + host->CloseWindowFrame(w.get()); + } + return w->olc_ShouldRemove(); + } + ), deqChildWindows.end()); + } + + // Update Primary Window + olc_WindowUpdate(fDT); + + // Wait for vertical sync if required. + // Note: Child windows will never vsync as waiting for each buffer swap with vsync + // 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 2bdc9b14..b832fe3f 100644 --- a/dev/src/core.h +++ b/dev/src/core.h @@ -53,28 +53,20 @@ namespace olc bool bAllowChildWindows = true; }; - class PixelGameEngine : protected Window + class PGEWindow : public Window { public: - PixelGameEngine(); - virtual ~PixelGameEngine(); - + bool Create(const olc::vi2d& vScreenSize, const olc::vi2d& vPixelSize); + 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" - // Called once when the engine is ready and the window is created - bool OnUserCreate() override; - // Called every frame while the engine is running - bool OnUserUpdate(float fElapsedTime) override; - // Called when something requests the engine shut down - bool OnUserDestroy() override; - - + // 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(); + + public: // olc::Image Handling // Create an image resource bool CreateImage(olc::Image& image, const olc::vi2d& size, const ImageConfig& cfg = olc::ImageConfig()); @@ -89,14 +81,51 @@ namespace olc // Destroy an image void DestroyImage(olc::Image& image); + public: + void LinkToRenderer(olc::gpu::Renderer* gpu); + void LinkToImageLoader(olc::imload::ImageLoader* imload); + + + olc::Image& GetDefaultImage(); + + + protected: + bool olc_OnMouseMove(const olc::vi2d& vMousePos) override; + + public: + virtual bool olc_WindowUpdate(const float fElapsedTime); + protected: - bool olc_PrimaryWindowInit() override; + olc::Draw2D draw; + + + private: + olc::Image imgPrimary; + olc::gpu::Renderer* pRenderer = nullptr; + olc::imload::ImageLoader* pImageLoader = nullptr; + }; + + class PixelGameEngine : protected PGEWindow + { + 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: // Child Windows + bool AddChildWindow(std::shared_ptr window); private: void EngineThread(); private: - std::deque> deqChildWindows; + std::deque> deqChildWindows; // Frame Timing & Overall Clocking std::chrono::steady_clock::time_point timeFrame1; diff --git a/dev/src/draw2d.cpp b/dev/src/draw2d.cpp index 81ae7dcd..d9cbe7b9 100644 --- a/dev/src/draw2d.cpp +++ b/dev/src/draw2d.cpp @@ -25,38 +25,14 @@ void Draw2D::SetTarget(olc::Image& 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)) - )); - + // Reset Affine transform to unity WorldReset(); + // Configure default render target 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) @@ -122,25 +98,31 @@ void Draw2D::PrepareImageForHW(olc::Image& image) 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; +} + +void olc::Draw2D::SetWorldTransform(const olc::tf2d& trans) +{ + transformAffine = trans; +} + +olc::tf2d& olc::Draw2D::GetWorldTransform() +{ + return transformAffine; } olc::vf2d olc::Draw2D::WorldToScreen(const olc::vf2d& v) const @@ -203,7 +185,12 @@ GPUTask olc::Draw2D::TaskDrawPolygon(GPUTask::Structure structure, const std::ve GPUTask olc::Draw2D::TaskFillPolygon(GPUTask::Structure structure, const std::vector& vPoints, const std::vector& vColours, const olc::Pixel tint) { - return GPUTask(); + GPUTask task; + task.structure = structure; + 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.tint = tint; + return task; } GPUTask olc::Draw2D::TaskFillPolygon(GPUTask::Structure structure, const std::vector& vPoints, const olc::Pixel colour, const olc::Pixel tint) @@ -237,32 +224,31 @@ GPUTask olc::Draw2D::TaskTexturedPolygon(GPUTask::Structure structure, const std return task; } -GPUTask Draw2D::Line(const olc::vf2d& p1, const olc::vf2d& p2, const olc::Pixel col) +const 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}, + transformAffine.forward({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) +const 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 }), + transformAffine.forward({ p1, p2 }), { c1, c2 }, olc::Colour::WHITE )); } -GPUTask olc::Draw2D::Rect(const olc::vf2d& pos, const olc::vf2d& size, const olc::Pixel col) +const 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 @@ -280,18 +266,9 @@ GPUTask olc::Draw2D::Rect(const olc::vf2d& pos, const olc::vf2d& size, const olc 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 )); @@ -299,21 +276,32 @@ GPUTask olc::Draw2D::Rect(const olc::vf2d& pos, const olc::vf2d& size, const olc } -GPUTask olc::Draw2D::FilledRect(const olc::vf2d& pos, const olc::vf2d& size, const olc::Pixel col) +const 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 } }, + transformAffine.forward({ { 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) +const GPUTask& olc::Draw2D::FilledRect(const olc::vf2d& pos, const olc::vf2d& size, const olc::Pixel colTL, const olc::Pixel colTR, const olc::Pixel colBL, const olc::Pixel colBR) +{ + PrepareTargetForHW(); + return vecGPUTasks.emplace_back( + TaskFillPolygon( + GPUTask::Structure::Fan, + transformAffine.forward({ { pos.x, pos.y }, { pos.x + size.x, pos.y }, { pos.x + size.x, pos.y + size.y }, { pos.x, pos.y + size.y } }), + { colTL, colTR, colBR, colBL }, + olc::Colour::WHITE + )); +} + +const 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); @@ -325,7 +313,6 @@ GPUTask olc::Draw2D::Image(olc::ImageRegion image, const olc::vf2d& pos, const o 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 } }), transformAffine.forward({ { 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 @@ -334,7 +321,7 @@ GPUTask olc::Draw2D::Image(olc::ImageRegion image, const olc::vf2d& pos, const o )); } -GPUTask olc::Draw2D::ImageRotated(olc::ImageRegion image, const olc::vf2d& pos, const float theta, const olc::vf2d& center, const olc::vf2d& scale) +const 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); @@ -355,15 +342,14 @@ GPUTask olc::Draw2D::ImageRotated(olc::ImageRegion image, const olc::vf2d& pos, return vecGPUTasks.emplace_back( TaskTexturedPolygon( GPUTask::Structure::Fan, - //transformCombined.transform(vPoints), - vPoints, + transformAffine.forward(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) +const 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); @@ -398,8 +384,7 @@ GPUTask olc::Draw2D::ImageQuad(olc::ImageRegion image, const olc::vf2d& vTL, con return vecGPUTasks.emplace_back( TaskTexturedPolygon( GPUTask::Structure::Fan, - //transformCombined.transform({ vTL, vTR, vBR, vBL }), - { vTL, vTR, vBR, vBL }, + transformAffine.forward({ 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] }, @@ -407,16 +392,24 @@ GPUTask olc::Draw2D::ImageQuad(olc::ImageRegion image, const olc::vf2d& vTL, con )); } - return GPUTask(); + // Default is just return a textured quad + return vecGPUTasks.emplace_back( + TaskTexturedPolygon( + GPUTask::Structure::Fan, + transformAffine.forward({ vTL, vTR, vBR, vBL }), + { 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 std::vector& vecPoints) +const 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) +const 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); @@ -426,8 +419,7 @@ GPUTask olc::Draw2D::ImageRect(olc::ImageRegion image, const olc::vf2d& pos, con 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 } }, + transformAffine.forward({ { 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] }, diff --git a/dev/src/draw2d.h b/dev/src/draw2d.h index b549381c..ec7e8735 100644 --- a/dev/src/draw2d.h +++ b/dev/src/draw2d.h @@ -35,19 +35,13 @@ namespace olc // Associate this drawing toolbox with a renderer void SetGPU(olc::gpu::Renderer* const renderer); + void ProcessGPUTasks(); 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(); @@ -56,6 +50,8 @@ namespace olc void WorldScale(const olc::vf2d& vScale); void WorldOffset(const olc::vf2d& vOffset); void WorldRotate(const float& fTheta, const olc::vf2d& vPoint = { 0,0 }); + void SetWorldTransform(const olc::tf2d& trans); + olc::tf2d& GetWorldTransform(); olc::vf2d WorldToScreen(const olc::vf2d& v) const; olc::vf2d ScreenToWorld(const olc::vf2d& v) const; @@ -70,36 +66,45 @@ namespace olc olc::Image& image, const olc::vf2d& pos); - // Clear + // Clear entire draw target to specific colour void Clear(const olc::Pixel& col); public: // Draws a single pixel wide line - GPUTask Line( + const 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 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 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 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); + // Draws a filled, multiple colour rectangle, with linear colour interpolation + const GPUTask& FilledRect( + 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 @@ -130,34 +135,35 @@ namespace olc // Draws a scaled image at specified location - GPUTask Image( + const 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( + const 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( + // Draws an image warped correctly to linearly fill a quadrilateral (formerly DrawWarped...) + const GPUTask& ImageQuad( olc::ImageRegion image, const olc::vf2d& vTL, const olc::vf2d& vTR, const olc::vf2d& vBR, const olc::vf2d& vBL); - GPUTask ImageQuad( + // Draws an image warped correctly to linearly fill a quadrilateral (formerly DrawWarped...) + const 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); + const GPUTask& ImageRect(olc::ImageRegion image, const olc::vf2d& pos, const olc::vf2d& size); @@ -217,9 +223,7 @@ namespace olc olc::Image* pTarget = nullptr; olc::gpu::Renderer* pRenderer = nullptr; - olc::tf2d transformTarget; olc::tf2d transformAffine; - olc::mf3d transformCombined; std::vector vecGPUTasks; diff --git a/dev/src/host_iface.h b/dev/src/host_iface.h index 52db6956..f7c354a8 100644 --- a/dev/src/host_iface.h +++ b/dev/src/host_iface.h @@ -56,7 +56,7 @@ namespace olc 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 CloseWindowFrame(olc::Window* pWindow) = 0; virtual bool UpdateWindowFrameTitle(olc::Window* pWindow) = 0; virtual std::vector GetHostWindowDescriptor(olc::Window* pWindow) = 0; diff --git a/dev/src/host_win_winapi.cpp b/dev/src/host_win_winapi.cpp index faa444af..3a07c32e 100644 --- a/dev/src/host_win_winapi.cpp +++ b/dev/src/host_win_winapi.cpp @@ -11,13 +11,13 @@ namespace olc::host bool Host_Windows_WinAPI::StartSystemEventLoop() { MSG msg; - while (GetMessage(&msg, NULL, 0, 0) > 0) + while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) > 0) { TranslateMessage(&msg); DispatchMessage(&msg); } - return true; + return true; } // Static linkage to lpfnWndProc - the hWnd is tagged with meta-info to get @@ -73,7 +73,8 @@ namespace olc::host // Define WindowClass - WNDCLASS wc = { 0 }; + WNDCLASSEX wc = { 0 }; + wc.cbSize = sizeof(WNDCLASSEX); wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; @@ -84,7 +85,7 @@ namespace olc::host wc.lpszMenuName = nullptr; wc.hbrBackground = nullptr; wc.lpszClassName = olcT("OLC_PIXEL_GAME_ENGINE3"); - RegisterClass(&wc); + RegisterClassEx(&wc); // Define window furniture DWORD dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; @@ -110,13 +111,20 @@ namespace olc::host AdjustWindowRectEx(&rWndRect, dwStyle, FALSE, dwExStyle); int width = rWndRect.right - rWndRect.left; int height = rWndRect.bottom - rWndRect.top; - pWindow->SetSize(vWindowSize); + pWindow->SetWindowSize(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); + LONG_PTR lp = GetWindowLongPtr(hWnd, GWL_STYLE); + SetWindowLongPtr(hWnd, GWL_STYLE, lp | (WS_CAPTION | WS_SYSMENU | WS_POPUPWINDOW | WS_THICKFRAME)); + lp = GetWindowLongPtr(hWnd, GWL_EXSTYLE); + SetWindowLongPtr(hWnd, GWL_EXSTYLE, lp | (WS_EX_WINDOWEDGE)); + + //SetWindowPos(hWnd, NULL, vWinPos.x, vWinPos.y, width, height, SWP_SHOWWINDOW); + //ShowWindow(hWnd, 1); + //UpdateWindow(hWnd); // Now... awkwardly, the above has already fired off some window messages // and they arent necessarily in a consistent order. Whereas one might @@ -130,7 +138,7 @@ namespace olc::host mapUID2HWND.insert_or_assign(pWindow->GetUID(), hWnd); mapHWND2PTR.insert_or_assign(hWnd, pWindow); - pWindow->ConnecToHost(this); + //DragAcceptFiles(olc_hWnd, true); @@ -138,12 +146,18 @@ namespace olc::host return true; } + bool Host_Windows_WinAPI::CloseWindowFrame(olc::Window* pWindow) + { + DestroyWindow((HWND)GetHostWindowDescriptor(pWindow).front()); + return false; + } + bool Host_Windows_WinAPI::UpdateWindowFrameTitle(olc::Window* pWindow) { #ifdef UNICODE - SetWindowText(mapUID2HWND.at(pWindow->GetUID()), ConvertS2W(pWindow->GetTitle()).c_str()); + SetWindowText(mapUID2HWND.at(pWindow->GetUID()), ConvertS2W(pWindow->GetWindowTitle()).c_str()); #else - SetWindowText(mapUID2HWND.at(pWindow->GetUID()), sTitle.c_str()); + SetWindowText(mapUID2HWND.at(pWindow->GetUID()), pWindow->GetWindowTitle().c_str()); #endif return true; } @@ -166,7 +180,7 @@ namespace olc::host LRESULT Host_Windows_WinAPI::OnWindowEvent(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { if (!mapHWND2PTR.contains(hWnd)) - return false; + return DefWindowProc(hWnd, uMsg, wParam, lParam);; // Get target olc::Window const auto& window = mapHWND2PTR.at(hWnd); @@ -197,7 +211,13 @@ namespace olc::host return 0; } break; - // case WM_MOUSEWHEEL: ptrPGE->olc_UpdateMouseWheel(GET_WHEEL_DELTA_WPARAM(wParam)); return 0; + + case WM_MOUSEWHEEL: + { + window->olc_OnMouseWheel(GET_WHEEL_DELTA_WPARAM(wParam)); + return 0; + } + break; // case WM_MOUSELEAVE: ptrPGE->olc_UpdateMouseFocus(false); return 0; // case WM_SETFOCUS: ptrPGE->olc_UpdateKeyFocus(true); return 0; // case WM_KILLFOCUS: ptrPGE->olc_UpdateKeyFocus(false); return 0; @@ -273,7 +293,11 @@ namespace olc::host // break; // // - // case WM_CLOSE: ptrPGE->olc_Terminate(); return 0; + case WM_CLOSE: + { + window->olc_OnWindowClose(); + return 0; + } case WM_DESTROY: PostQuitMessage(0); DestroyWindow(hWnd); diff --git a/dev/src/host_win_winapi.h b/dev/src/host_win_winapi.h index eb6e94af..9c16044f 100644 --- a/dev/src/host_win_winapi.h +++ b/dev/src/host_win_winapi.h @@ -66,6 +66,7 @@ namespace olc public: bool StartSystemEventLoop(); bool AddWindowFrame(olc::Window* pWindow, const olc::vi2d& vWindowPos, const olc::vi2d& vWindowSize, const bool bFullScreen); + bool CloseWindowFrame(olc::Window* pWindow); bool UpdateWindowFrameTitle(olc::Window* pWindow); std::vector GetHostWindowDescriptor(olc::Window* pWindow); diff --git a/dev/src/hw_mouse.cpp b/dev/src/hw_mouse.cpp index bf2af5c6..ea65e2b1 100644 --- a/dev/src/hw_mouse.cpp +++ b/dev/src/hw_mouse.cpp @@ -13,6 +13,11 @@ namespace olc::hw return position; } + int32_t Mouse::GetWheel() const + { + return wheel; + } + void Mouse::SetPosition(const olc::vf2d& pos) { position_in = pos; @@ -23,6 +28,11 @@ namespace olc::hw buttons_new[nButton] = state; } + void Mouse::SetWheel(const int32_t w) + { + wheel_in = w; + } + void Mouse::UpdateState() { for (size_t i=0; i buttons{}; @@ -37,10 +40,13 @@ namespace olc std::array buttons_old{}; olc::vf2d position; olc::vf2d position_in; + int32_t wheel_in; + int32_t wheel; private: void SetPosition(const olc::vf2d& pos); void SetButton(const int nButton, bool state); + void SetWheel(const int32_t w); void UpdateState(); }; } diff --git a/dev/src/image.h b/dev/src/image.h index d8b9300e..8b84321e 100644 --- a/dev/src/image.h +++ b/dev/src/image.h @@ -77,13 +77,6 @@ namespace olc 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 @@ -94,6 +87,18 @@ namespace olc olc::vf2d br; olc::vf2d bl; }; + + 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(); + } + + ImageRegion& operator=(ImageRegion& o) + { + + } }; diff --git a/dev/src/imload_wingdi.h b/dev/src/imload_wingdi.h index 14dae709..a13da637 100644 --- a/dev/src/imload_wingdi.h +++ b/dev/src/imload_wingdi.h @@ -26,13 +26,13 @@ #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 +//#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") diff --git a/dev/src/pixel.h b/dev/src/pixel.h index 4f92f10f..d8eeea93 100644 --- a/dev/src/pixel.h +++ b/dev/src/pixel.h @@ -72,7 +72,13 @@ namespace olc // Construction via individual channels inline constexpr Pixel(const uint8_t red, const uint8_t green, const uint8_t blue, const uint8_t alpha = 0xFF) - : a(alpha), b(blue), g(green), r(red) +#if PGE_PIXEL_LAYOUT == PGE_PIXEL_LAYOUT_RGBA + : r(red), g(green), b(blue), a(alpha) +#endif +#if PGE_PIXEL_LAYOUT == PGE_PIXEL_LAYOUT_ABGR + : a(alpha), b(blue), g(green), r(red) +#endif + { } // Construction via numeric assignment (e.g. #00DDBBFF) diff --git a/dev/src/sh_template.h b/dev/src/sh_template.h index 79e3c5cb..955a3096 100644 --- a/dev/src/sh_template.h +++ b/dev/src/sh_template.h @@ -73,14 +73,14 @@ -#if defined(OLC_PGE_APPLICATION) && !defined(PGE_HOST_IMPLEMENTED) +#if defined(OLC_PGE3_APPLICATION) && !defined(PGE_HOST_IMPLEMENTED) #if OLC_HOST == OLC_HOST_WINDOWS //! GRAB host_win_winapi.cpp IMPLEMENTATION #endif #define PGE_HOST_IMPLEMENTED 1 #endif -#if defined(OLC_PGE_APPLICATION) && !defined(PGE_GPU_IMPLEMENTED) +#if defined(OLC_PGE3_APPLICATION) && !defined(PGE_GPU_IMPLEMENTED) #if OLC_GPU == OLC_GPU_OPENGL33 //! GRAB gpu_iface.cpp IMPLEMENTATION //! GRAB api_opengl.cpp IMPLEMENTATION @@ -89,32 +89,32 @@ #define PGE_GPU_IMPLEMENTED 1 #endif -#if defined(OLC_PGE_APPLICATION) && !defined(PGE_DRAW2D_IMPLEMENTED) +#if defined(OLC_PGE3_APPLICATION) && !defined(PGE_DRAW2D_IMPLEMENTED) //! GRAB draw2d.cpp IMPLEMENTATION #define PGE_DRAW2D_IMPLEMENTED 1 #endif -#if defined(OLC_PGE_APPLICATION) && !defined(PGE_CORE_IMPLEMENTED) +#if defined(OLC_PGE3_APPLICATION) && !defined(PGE_CORE_IMPLEMENTED) //! GRAB core.cpp IMPLEMENTATION #define PGE_CORE_IMPLEMENTED 1 #endif -#if defined(OLC_PGE_APPLICATION) && !defined(PGE_IMAGE_IMPLEMENTED) +#if defined(OLC_PGE3_APPLICATION) && !defined(PGE_IMAGE_IMPLEMENTED) //! GRAB image.cpp IMPLEMENTATION #define PGE_IMAGE_IMPLEMENTED 1 #endif -#if defined(OLC_PGE_APPLICATION) && !defined(PGE_HW_MOUSE_IMPLEMENTED) +#if defined(OLC_PGE3_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) +#if defined(OLC_PGE3_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 defined(OLC_PGE3_APPLICATION) && !defined(PGE_IMAGELOADER_IMPLEMENTED) #if OLC_IMAGELOADER == OLC_IMAGELOADER_WINGDI //! GRAB imload_wingdi.cpp IMPLEMENTATION #endif diff --git a/dev/src/window.cpp b/dev/src/window.cpp index 35d17abd..f3e6772f 100644 --- a/dev/src/window.cpp +++ b/dev/src/window.cpp @@ -1,6 +1,8 @@ #include "window.h" #include "host_iface.h" +#include "gpu_iface.h" +#include "imload_iface.h" //! START IMPLEMENTATION namespace olc @@ -14,27 +16,13 @@ namespace olc { } - void Window::ConnecToHost(olc::host::Host* host) + void Window::LinkToHost(olc::host::Host* host) { pHost = host; sFrameTitle = "OneLoneCoder.com - Pixel Game Engine 3"; pHost->UpdateWindowFrameTitle(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) { @@ -44,13 +32,14 @@ namespace olc bool Window::olc_OnMouseMove(const olc::vi2d& vMousePos) { - mouse.SetPosition(olc::vf2d(vMousePos) / olc::vf2d(GetSize()) * imgPrimary.Size()); + mouse.SetPosition(olc::vf2d(vMousePos) / olc::vf2d(GetWindowSize())); return true; } bool Window::olc_OnMouseWheel(const int32_t nScroll) { - return false; + mouse.SetWheel(nScroll); + return true; } bool Window::olc_OnMouseFocus(const bool bHasFocus) @@ -65,81 +54,37 @@ namespace olc bool Window::olc_OnWindowSize(const olc::vi2d& vWindowSize) { - return SetSize(vWindowSize); + return SetWindowSize(vWindowSize); } - bool Window::olc_ShouldRemove() const + bool Window::olc_OnWindowClose() { - return bShouldRemove; + bRequestToClose = true; + return true; } - bool Window::olc_WindowUpdate(const float fElapsedTime, olc::gpu::Renderer* const gpu) + bool Window::olc_ShouldRemove() const { - // 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::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; + return bShouldRemove; } + size_t Window::GetUID() const { return nUniqueID; } - const olc::vi2d& Window::GetSize() const + const olc::vi2d& Window::GetWindowSize() const { return vWindowSize; } - const olc::vi2d& Window::GetPosition() const + const olc::vi2d& Window::GetWindowPosition() const { return vWindowPos; } - bool Window::SetSize(const olc::vi2d& vSize) + bool Window::SetWindowSize(const olc::vi2d& vSize) { // Request host to change geometry of window object @@ -147,7 +92,7 @@ namespace olc return true; } - bool Window::SetPosition(const olc::vi2d& vPosition) + bool Window::SetWindowPosition(const olc::vi2d& vPosition) { // Request host to change geometry of window object @@ -155,21 +100,17 @@ namespace olc return true; } - const std::string& Window::GetTitle() const + const std::string& Window::GetWindowTitle() const { return sFrameTitle; } - bool Window::SetTitle(const std::string& sTitle) + bool Window::SetWindowTitle(const std::string& sTitle) { sFrameTitle = sTitle; pHost->UpdateWindowFrameTitle(this); return false; } - bool Window::olc_PrimaryWindowInit() - { - return false; - } }; //! END IMPLEMENTATION \ No newline at end of file diff --git a/dev/src/window.h b/dev/src/window.h index d8c88600..36b31c9b 100644 --- a/dev/src/window.h +++ b/dev/src/window.h @@ -14,7 +14,6 @@ #include "config.h" #include "pixel.h" #include "vector2d.h" -#include "gpu_iface.h" #include "draw2d.h" #include "hw_mouse.h" //! END CUSTOMHEADER @@ -39,6 +38,11 @@ namespace olc class Renderer; } + namespace imload + { + class ImageLoader; + } + namespace hw { class Mouse; @@ -52,27 +56,22 @@ namespace olc public: Window(); virtual ~Window(); + + void LinkToHost(olc::host::Host* host); - 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: + private: // These are called externally from the host // 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); + virtual bool olc_OnMouseButton(const uint8_t nButton, const bool bPressed); + virtual bool olc_OnMouseMove(const olc::vi2d& vMousePos); + virtual bool olc_OnMouseWheel(const int32_t nScroll); + virtual bool olc_OnMouseFocus(const bool bHasFocus); // Set Window State - bool olc_OnWindowPosition(const olc::vi2d& vWindowPos); - bool olc_OnWindowSize(const olc::vi2d& vWindowSize); + virtual bool olc_OnWindowPosition(const olc::vi2d& vWindowPos); + virtual bool olc_OnWindowSize(const olc::vi2d& vWindowSize); + virtual bool olc_OnWindowClose(); // Set Keyboard State @@ -80,25 +79,22 @@ namespace olc 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 olc::vi2d& GetWindowSize() const; + bool SetWindowSize(const olc::vi2d& vSize); - const std::string& GetTitle() const; - bool SetTitle(const std::string& sTitle); + const olc::vi2d& GetWindowPosition() const; + bool SetWindowPosition(const olc::vi2d& vPosition); + const std::string& GetWindowTitle() const; + bool SetWindowTitle(const std::string& sTitle); protected: - virtual bool olc_PrimaryWindowInit(); - - protected: + bool bRequestToClose = false; bool bShouldRemove = false; protected: @@ -108,16 +104,11 @@ namespace olc 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 diff --git a/dev/tests/test_mh.cpp b/dev/tests/test_mh.cpp index eaaccc16..8e5f24fa 100644 --- a/dev/tests/test_mh.cpp +++ b/dev/tests/test_mh.cpp @@ -1,7 +1,26 @@ -#define OLC_PGE_APPLICATION +#define OLC_PGE3_APPLICATION #include "olcpge3.h" +class SecondWindow : public olc::PGEWindow +{ +public: + + // Return true if window is to continue + bool OnUserCreate() override + { + + return false; + } + + // Return true if window is to continue + bool OnUserUpdate(float fElapsedTime) override + { + + return true; + } +}; + class Example : public olc::PixelGameEngine { @@ -18,6 +37,9 @@ class Example : public olc::PixelGameEngine olc::Image imSampleTest; + olc::Image imLowRes; + olc::Image imHighResSprite; + void CreateSampleTestImage(olc::Image& image, const olc::vi2d& s) { CreateImage(image, s); @@ -50,23 +72,32 @@ class Example : public olc::PixelGameEngine { olc::vf2d pos; olc::vf2d vel; - float ang; - float angvel; + float ang = 0; + float angvel = 0; }; std::vector vecLogos; std::vector vecVerts; + float fScale = 1.0f; + int nSelectedVert = -1; + std::shared_ptr win2; + public: bool OnUserCreate() override { + + CreateImage(imLowRes, { 256, 240 }); + CreateImageFromFile(imHighResSprite, "e:/voxel.png"); + //CreateImage(imTest, { 64,64 }); CreateImageFromFile(imLogo, "../tests/olc.png"); - CreateSampleTestImage(imSampleTest, { 33, 33 }); + + CreateSampleTestImage(imSampleTest, { 32, 32 }); CreateImage(imTemp, { 64, 64 }); @@ -74,18 +105,22 @@ class Example : public olc::PixelGameEngine vecLogos.resize(x); for (auto& a : vecLogos) { - a.pos = olc::vf2d(rand() % imgPrimary.Size().x, rand() % imgPrimary.Size().y); + a.pos = olc::vf2d(rand() % GetDefaultImage().Size().x, rand() % GetDefaultImage().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} + { 100.0f, 100.0f}, + { 200.0f, 100.0f}, + { 200.0f, 200.0f}, + { 100.0f, 200.0f} }; + win2 = std::make_shared(); + AddChildWindow(win2); + + return true; } @@ -105,13 +140,34 @@ class Example : public olc::PixelGameEngine //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); + + draw.SetTarget(GetDefaultImage()); + draw.WorldReset(); + draw.Clear(olc::Colour::TANGERINE); + //draw.Image(imHighResSprite, mouse.GetPosition()); + + + //draw.SetTarget(imLowRes); + + //draw.Clear(olc::Colour::BLANK); if (mouse.GetButton(1).bHeld) { fAngle += 0.2f * fElapsedTime; - draw.WorldRotate(fAngle, imgPrimary.Size() / 2.0f); + draw.WorldRotate(fAngle, imLowRes.Size() / 2.0f); + } + + if (mouse.GetWheel() > 0) + { + fScale *= 1.1f; } + + if (mouse.GetWheel() < 0) + { + fScale *= 0.9f; + } + + draw.WorldScale({ fScale, fScale }); //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)); @@ -120,8 +176,10 @@ class Example : public olc::PixelGameEngine //return true; + + //std::cout << mouse.GetPosition() << "\n"; - auto vMouse = draw.ScreenToWorld(mouse.GetPosition()); + auto vMouse = draw.ScreenToWorld(mouse.GetPosition() ); if (mouse.GetButton(0).bPressed) { @@ -161,12 +219,26 @@ class Example : public olc::PixelGameEngine //draw.Pixel(vMouse, olc::Colour::GREEN); //draw.Line(vMouse, vMouse + 1, olc::Colour::GREEN); + + draw.FilledRect({ 5, 100 }, { 100,100 }, olc::Colour::TANGERINE, olc::Colour::DARK_CYAN, olc::Colour::RED, 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.SetTarget(imgPrimary); + //draw.WorldReset(); + //draw.Image(imLowRes, { 0,0 }, { 4, 4 }); + //draw.Rect(vMouse, { 100,100 }); /*for (auto& a : vecLogos) @@ -296,8 +368,8 @@ int main() //if (demo.Construct({ 256, 240 }, { 4, 4 })) olc::PGEConfig cfg; - cfg.vPixelSize = { 4,4 }; - cfg.vScreenSize = { 256, 240 }; + cfg.vPixelSize = { 1,1 }; + cfg.vScreenSize = { 1024, 960 }; //cfg.bVSync = true; //if (demo.Construct({ 1280, 960 }, { 1, 1 }, cfg)) diff --git a/utilities/olcUTIL3_GameMode.h b/utilities/olcUTIL3_GameMode.h new file mode 100644 index 00000000..ac024266 --- /dev/null +++ b/utilities/olcUTIL3_GameMode.h @@ -0,0 +1,49 @@ +#pragma once + +#if defined(OLC_PGE3_MULTIFILE) + +#else + #include +#endif + +namespace olc::util3::gsm +{ + template + class Mode + { + public: + + Mode() = default; + + + Mode(std::shared_ptr pGlobalData) : m_pGlobalData(pGlobalData) + { + + } + + public: + virtual bool OnCreate(olc::PixelGameEngine* pge) + { + return false; + } + + virtual GState OnUpdate(olc::PixelGameEngine* pge, float fElapsedTime) + { + return GState{}; + } + + virtual bool OnEnterMode(olc::PixelGameEngine* pge) + { + return false; + } + + virtual bool OnExitMode(olc::PixelGameEngine* pge) + { + return false; + } + + protected: + std::shared_ptr m_pGlobalData; + }; + +} \ No newline at end of file From b260c38442e8e7e1c2ab74e6df6e40253f72eff2 Mon Sep 17 00:00:00 2001 From: Javidx9 <25419386+OneLoneCoder@users.noreply.github.com> Date: Sun, 23 Nov 2025 16:58:27 +0000 Subject: [PATCH 04/17] more gitignore bs --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index dd66d0d7..d2acef1b 100644 --- a/.gitignore +++ b/.gitignore @@ -37,3 +37,4 @@ /dev/msvc/olcPGE3_BuildSH/x64/Debug /dev/msvc/olcPGE3/x64/Release /dev/msvc/x64/Debug/olcPGE3.tlog +/dev/msvc/.vs/CopilotSnapshots/48318E23BE80F04A9ED976A29E9EEE0D From bbff5539a0bb3269b797a7e8bc2148467cd70b3b Mon Sep 17 00:00:00 2001 From: Javidx9 <25419386+OneLoneCoder@users.noreply.github.com> Date: Sun, 23 Nov 2025 16:59:27 +0000 Subject: [PATCH 05/17] Working multi window rendering. Needs a simultaneous event scheduler so action doesnt halt when events are happening a la pge2 --- .../olcPGE3_BuildSH/olcPixelGameEngine3.h | 189 ++++++++++++------ dev/src/config.h | 2 +- dev/src/core.cpp | 44 ++-- dev/src/core.h | 2 +- dev/src/gpu_iface.h | 8 +- dev/src/gpu_opengl33.cpp | 80 +++++--- dev/src/gpu_opengl33.h | 10 +- dev/src/host_iface.h | 2 +- dev/src/host_win_winapi.cpp | 22 +- dev/src/host_win_winapi.h | 2 +- dev/src/window.h | 2 +- dev/tests/test_mh.cpp | 18 +- 12 files changed, 262 insertions(+), 119 deletions(-) diff --git a/dev/msvc/olcPGE3_BuildSH/olcPixelGameEngine3.h b/dev/msvc/olcPGE3_BuildSH/olcPixelGameEngine3.h index d6e9bb73..ab450a17 100644 --- a/dev/msvc/olcPGE3_BuildSH/olcPixelGameEngine3.h +++ b/dev/msvc/olcPGE3_BuildSH/olcPixelGameEngine3.h @@ -107,7 +107,7 @@ #define OLC_MOUSE_BUTTONS 3 #define OLC_GPU_MAX_VERTICES 8192 -#define OLC_GPU_ERRORCHECK 0 +#define OLC_GPU_ERRORCHECK 1 #define LICENCE_DEFAULT "OneLoneCoder.com - Pixel Game Engine 3 - " @@ -1474,9 +1474,13 @@ namespace olc public: // Device Stuff // Constructs a GPU Device interface - virtual bool CreateDevice(std::vector params, const RendererConfig& cfg) = 0; + virtual bool CreateDevice(std::vector os_win_id, const RendererConfig& cfg) = 0; // Destroys a GPU device interface virtual bool DestroyDevice() = 0; + // If applicable, retarget the rendering context + virtual bool RetargetDevice(std::vector os_win_id) = 0; + // Prepare an OS rendering target + virtual bool PrepareWindowTarget(std::vector os_win_id) = 0; public: // Texture Resource Stuff // Allocates a new texture resource in VRAM, returns handle @@ -1508,7 +1512,7 @@ namespace olc // Configures defaults prior to drawing virtual bool DisplayPrepare() = 0; // Displays the final output - virtual bool DisplayDraw(bool bVerticalSyncNow = false) = 0; + virtual bool DisplayDraw(std::vector os_win_id, bool bVerticalSyncNow = false) = 0; protected: @@ -1876,7 +1880,7 @@ namespace olc olc::vi2d vWindowSize; std::string sFrameTitle; - private: + protected: olc::host::Host* pHost = nullptr; protected: @@ -1927,9 +1931,9 @@ namespace olc HostError GetLastError() const { return lastError; } public: - virtual bool StartSystemEventLoop() = 0; + virtual bool StartSystemEventLoop(bool bBlockIfPossible = false) = 0; virtual bool AddWindowFrame(olc::Window* pWindow, const olc::vi2d& vWindowPos, const olc::vi2d& vWindowSize, const bool bFullScreen) = 0; - + virtual bool CloseWindowFrame(olc::Window* pWindow) = 0; virtual bool UpdateWindowFrameTitle(olc::Window* pWindow) = 0; virtual std::vector GetHostWindowDescriptor(olc::Window* pWindow) = 0; @@ -2044,7 +2048,7 @@ namespace olc bool Start(); public: // Child Windows - bool AddChildWindow(std::shared_ptr window); + bool AddChildWindow(std::shared_ptr window, const olc::vi2d& vScreenSize, const olc::vi2d& vPixelSize); private: void EngineThread(); @@ -2131,8 +2135,9 @@ namespace olc public: - bool StartSystemEventLoop(); + bool StartSystemEventLoop(bool bBlockIfPossible = false); bool AddWindowFrame(olc::Window* pWindow, const olc::vi2d& vWindowPos, const olc::vi2d& vWindowSize, const bool bFullScreen); + bool CloseWindowFrame(olc::Window* pWindow); bool UpdateWindowFrameTitle(olc::Window* pWindow); std::vector GetHostWindowDescriptor(olc::Window* pWindow); @@ -2393,9 +2398,13 @@ namespace olc { public: // Device Stuff // Constructs a GPU Device interface - bool CreateDevice(std::vector params, const RendererConfig& cfg) override; + bool CreateDevice(std::vector os_win_id, const RendererConfig& cfg) override; // Destroys a GPU device interface bool DestroyDevice() override; + // If applicable, relocate the rendering context + bool RetargetDevice(std::vector os_win_id) override; + // Prepare an OS rendering target + bool PrepareWindowTarget(std::vector os_win_id) override; public: // Texture Resource Stuff @@ -2427,11 +2436,11 @@ namespace olc // Configures defaults prior to drawing virtual bool DisplayPrepare() override; // Displays the final output - virtual bool DisplayDraw(bool bVerticalSyncNow) override; + virtual bool DisplayDraw(std::vector os_win_id, bool bVerticalSyncNow) override; protected: // These may need some thinking about re multiple window - olc::apis::opengl::glDeviceContext_t glDeviceContext = 0; + //olc::apis::opengl::glDeviceContext_t glDeviceContext = 0; olc::apis::opengl::glRenderContext_t glRenderContext = 0; Shader_GLSL33 shaderDefault; @@ -2531,13 +2540,25 @@ namespace olc::host // Windows app needs an event loop somewhere. This is blocking of course. This loop handles // all windows created for this host. - bool Host_Windows_WinAPI::StartSystemEventLoop() + bool Host_Windows_WinAPI::StartSystemEventLoop(bool bBlockIfPossible) { - MSG msg; - while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) > 0) + if (bBlockIfPossible) + { + MSG msg; + while (GetMessage(&msg, NULL, 0, 0) > 0) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + else { - TranslateMessage(&msg); - DispatchMessage(&msg); + MSG msg; + while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) > 0) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } } return true; @@ -2669,6 +2690,12 @@ namespace olc::host return true; } + bool Host_Windows_WinAPI::CloseWindowFrame(olc::Window* pWindow) + { + DestroyWindow((HWND)GetHostWindowDescriptor(pWindow).front()); + return false; + } + bool Host_Windows_WinAPI::UpdateWindowFrameTitle(olc::Window* pWindow) { #ifdef UNICODE @@ -3294,37 +3321,22 @@ namespace olc::gpu - bool Renderer_OGL33::CreateDevice(std::vector params, const RendererConfig& cfg) + bool Renderer_OGL33::CreateDevice(std::vector os_win_id, 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))) + // Create OpenGL Device Context + if (!PrepareWindowTarget(os_win_id)) { - lastError = RendererError::InvalidDCPixelFormat; return false; } - if (!SetPixelFormat(glDeviceContext, pf, &pfd)) - { - lastError = RendererError::FailedToSetDCPixelFormat; - return false; - } + auto glDeviceContext = GetDC((HWND)(os_win_id[0])); + // Create OpenGL Render Context if (!(glRenderContext = wglCreateContext(glDeviceContext))) { lastError = RendererError::FailedToCreateRenderContext; @@ -3541,6 +3553,52 @@ namespace olc::gpu return false; } + bool Renderer_OGL33::RetargetDevice(std::vector os_win_id) + { +#if OLC_HOST == OLC_HOST_WINDOWS + auto glDeviceContext = GetDC((HWND)(os_win_id[0])); + + if (!wglMakeCurrent(glDeviceContext, glRenderContext)) + { + lastError = RendererError::FailedToSwitchRenderContext; + auto err = ::GetLastError(); + return false; + } +#endif + + return true; + } + + bool Renderer_OGL33::PrepareWindowTarget(std::vector os_win_id) + { +#if OLC_HOST == OLC_HOST_WINDOWS + auto dc = GetDC((HWND)(os_win_id[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(dc, &pfd))) + { + lastError = RendererError::InvalidDCPixelFormat; + return false; + } + + if (!SetPixelFormat(dc, pf, &pfd)) + { + lastError = RendererError::FailedToSetDCPixelFormat; + return false; + } +#endif + + return true; + } + uint32_t Renderer_OGL33::CreateTexture(const olc::vi2d& vSize, const olc::ImageConfig& cfg) { auto& gl = olc::apis::opengl::gl::Get(); @@ -3781,16 +3839,17 @@ namespace olc::gpu return false; } - bool Renderer_OGL33::DisplayDraw(bool bVerticalSyncNow) + bool Renderer_OGL33::DisplayDraw(std::vector os_win_id, bool bVerticalSyncNow) { auto& gl = olc::apis::opengl::gl::Get(); #if OLC_HOST == OLC_HOST_WINDOWS - SwapBuffers(glDeviceContext); + auto glDeviceContext = GetDC((HWND)(os_win_id[0])); + SwapBuffers(glDeviceContext); #endif - return false; + return true; } @@ -4232,6 +4291,8 @@ namespace olc { bool PGEWindow::Create(const olc::vi2d& vScreenSize, const olc::vi2d& vPixelSize) { + //pRenderer->RetargetDevice(pHost->GetHostWindowDescriptor(this)); + pRenderer->PrepareWindowTarget(pHost->GetHostWindowDescriptor(this)); CreateImage(GetDefaultImage(), vScreenSize); SetWindowSize(vScreenSize * vPixelSize); return true; @@ -4256,12 +4317,10 @@ namespace olc { // Input Changes mouse.UpdateState(); - - + draw.SetGPU(pRenderer); draw.SetTarget(GetDefaultImage()); - - + pRenderer->RetargetDevice(pHost->GetHostWindowDescriptor(this)); pRenderer->ApplyDefaultShader(); @@ -4293,7 +4352,8 @@ namespace olc draw.ProcessGPUTasks(); // Update Window's primary surface - pRenderer->DisplayDraw(); + //pRenderer->RetargetDevice(pHost->GetHostWindowDescriptor(this)); + pRenderer->DisplayDraw(pHost->GetHostWindowDescriptor(this)); return true; } @@ -4388,8 +4448,6 @@ namespace olc - - PixelGameEngine::PixelGameEngine() : PGEWindow() { } @@ -4418,32 +4476,31 @@ namespace olc // Initialise Host Interface host = std::make_unique(); - - - coreActive = true; - coreThread = std::thread(&PixelGameEngine::EngineThread, this); + //coreThread = std::thread(&PixelGameEngine::EngineThread, this); - + EngineThread(); // Gracefully terminate the main engine thread //coreActive = false; - coreThread.join(); + //coreThread.join(); return true; } - bool PixelGameEngine::AddChildWindow(std::shared_ptr window) + bool PixelGameEngine::AddChildWindow(std::shared_ptr window, const olc::vi2d& vScreenSize, const olc::vi2d& vPixelSize) { // Link this olc::Window to a host resource if (host) { - host->AddWindowFrame(window.get(), {30,30}, config.vPixelSize * config.vScreenSize, false); + host->AddWindowFrame(window.get(), { 30,30 }, vScreenSize * vPixelSize, false); window->LinkToHost(host.get()); window->LinkToRenderer(gpu.get()); window->LinkToImageLoader(imageloader.get()); - window->Create({ 64, 64 }, { 8,8 }); + + //gpu->RetargetDevice(host->GetHostWindowDescriptor(window.get())); + window->Create(vScreenSize, vPixelSize); deqChildWindows.push_back(window); } @@ -4451,12 +4508,16 @@ namespace olc } + void PixelGameEngine::EngineThread() { using namespace std::chrono_literals; timeFrame2 = std::chrono::steady_clock::now(); timeFrame1 = std::chrono::steady_clock::now(); + // Start the host system event loop in its own thread + //auto t1 = std::thread([this]() { host->StartSystemEventLoop(true); }); + // Create Primary Window host->AddWindowFrame(this, { 30,30 }, config.vPixelSize * config.vScreenSize, false); @@ -4487,6 +4548,8 @@ namespace olc } + + CreateImage(GetDefaultImage(), config.vScreenSize); @@ -4499,6 +4562,8 @@ namespace olc // Creation process signalled abort return; } + + draw.ProcessGPUTasks(); draw.SetTarget(GetDefaultImage()); @@ -4508,9 +4573,10 @@ namespace olc durationFrameCount = 0s; + + while (coreActive) { - // This will block depending upon the system host->StartSystemEventLoop(); // Frame Delta Timing - "ElapsedTime" since last core update @@ -4543,8 +4609,9 @@ namespace olc SetWindowTitle(sTitle); frameCount = 0; } - + + // Primary Window if (olc_ShouldRemove()) { @@ -4561,8 +4628,12 @@ namespace olc if (!deqChildWindows.empty()) { deqChildWindows.erase(std::remove_if(deqChildWindows.begin(), deqChildWindows.end(), - [](const std::shared_ptr& w) + [this](const std::shared_ptr& w) { + if (w->olc_ShouldRemove()) + { + host->CloseWindowFrame(w.get()); + } return w->olc_ShouldRemove(); } ), deqChildWindows.end()); diff --git a/dev/src/config.h b/dev/src/config.h index 030a2d84..52625e6b 100644 --- a/dev/src/config.h +++ b/dev/src/config.h @@ -83,7 +83,7 @@ #define OLC_MOUSE_BUTTONS 3 #define OLC_GPU_MAX_VERTICES 8192 -#define OLC_GPU_ERRORCHECK 0 +#define OLC_GPU_ERRORCHECK 1 #define LICENCE_DEFAULT "OneLoneCoder.com - Pixel Game Engine 3 - " diff --git a/dev/src/core.cpp b/dev/src/core.cpp index 74b64314..0bf97df5 100644 --- a/dev/src/core.cpp +++ b/dev/src/core.cpp @@ -9,6 +9,8 @@ namespace olc { bool PGEWindow::Create(const olc::vi2d& vScreenSize, const olc::vi2d& vPixelSize) { + //pRenderer->RetargetDevice(pHost->GetHostWindowDescriptor(this)); + pRenderer->PrepareWindowTarget(pHost->GetHostWindowDescriptor(this)); CreateImage(GetDefaultImage(), vScreenSize); SetWindowSize(vScreenSize * vPixelSize); return true; @@ -33,12 +35,10 @@ namespace olc { // Input Changes mouse.UpdateState(); - - + draw.SetGPU(pRenderer); draw.SetTarget(GetDefaultImage()); - - + pRenderer->RetargetDevice(pHost->GetHostWindowDescriptor(this)); pRenderer->ApplyDefaultShader(); @@ -70,7 +70,8 @@ namespace olc draw.ProcessGPUTasks(); // Update Window's primary surface - pRenderer->DisplayDraw(); + //pRenderer->RetargetDevice(pHost->GetHostWindowDescriptor(this)); + pRenderer->DisplayDraw(pHost->GetHostWindowDescriptor(this)); return true; } @@ -165,8 +166,6 @@ namespace olc - - PixelGameEngine::PixelGameEngine() : PGEWindow() { } @@ -195,32 +194,31 @@ namespace olc // Initialise Host Interface host = std::make_unique(); - - - coreActive = true; - coreThread = std::thread(&PixelGameEngine::EngineThread, this); + //coreThread = std::thread(&PixelGameEngine::EngineThread, this); - + EngineThread(); // Gracefully terminate the main engine thread //coreActive = false; - coreThread.join(); + //coreThread.join(); return true; } - bool PixelGameEngine::AddChildWindow(std::shared_ptr window) + bool PixelGameEngine::AddChildWindow(std::shared_ptr window, const olc::vi2d& vScreenSize, const olc::vi2d& vPixelSize) { // Link this olc::Window to a host resource if (host) { - host->AddWindowFrame(window.get(), {30,30}, config.vPixelSize * config.vScreenSize, false); + host->AddWindowFrame(window.get(), { 30,30 }, vScreenSize * vPixelSize, false); window->LinkToHost(host.get()); window->LinkToRenderer(gpu.get()); window->LinkToImageLoader(imageloader.get()); - window->Create({ 64, 64 }, { 8,8 }); + + //gpu->RetargetDevice(host->GetHostWindowDescriptor(window.get())); + window->Create(vScreenSize, vPixelSize); deqChildWindows.push_back(window); } @@ -228,12 +226,16 @@ namespace olc } + void PixelGameEngine::EngineThread() { using namespace std::chrono_literals; timeFrame2 = std::chrono::steady_clock::now(); timeFrame1 = std::chrono::steady_clock::now(); + // Start the host system event loop in its own thread + //auto t1 = std::thread([this]() { host->StartSystemEventLoop(true); }); + // Create Primary Window host->AddWindowFrame(this, { 30,30 }, config.vPixelSize * config.vScreenSize, false); @@ -264,6 +266,8 @@ namespace olc } + + CreateImage(GetDefaultImage(), config.vScreenSize); @@ -276,6 +280,8 @@ namespace olc // Creation process signalled abort return; } + + draw.ProcessGPUTasks(); draw.SetTarget(GetDefaultImage()); @@ -285,9 +291,10 @@ namespace olc durationFrameCount = 0s; + + while (coreActive) { - // This will block depending upon the system host->StartSystemEventLoop(); // Frame Delta Timing - "ElapsedTime" since last core update @@ -320,8 +327,9 @@ namespace olc SetWindowTitle(sTitle); frameCount = 0; } - + + // Primary Window if (olc_ShouldRemove()) { diff --git a/dev/src/core.h b/dev/src/core.h index b832fe3f..00b0c9df 100644 --- a/dev/src/core.h +++ b/dev/src/core.h @@ -119,7 +119,7 @@ namespace olc bool Start(); public: // Child Windows - bool AddChildWindow(std::shared_ptr window); + bool AddChildWindow(std::shared_ptr window, const olc::vi2d& vScreenSize, const olc::vi2d& vPixelSize); private: void EngineThread(); diff --git a/dev/src/gpu_iface.h b/dev/src/gpu_iface.h index edf28e45..13f49f23 100644 --- a/dev/src/gpu_iface.h +++ b/dev/src/gpu_iface.h @@ -76,9 +76,13 @@ namespace olc public: // Device Stuff // Constructs a GPU Device interface - virtual bool CreateDevice(std::vector params, const RendererConfig& cfg) = 0; + virtual bool CreateDevice(std::vector os_win_id, const RendererConfig& cfg) = 0; // Destroys a GPU device interface virtual bool DestroyDevice() = 0; + // If applicable, retarget the rendering context + virtual bool RetargetDevice(std::vector os_win_id) = 0; + // Prepare an OS rendering target + virtual bool PrepareWindowTarget(std::vector os_win_id) = 0; public: // Texture Resource Stuff // Allocates a new texture resource in VRAM, returns handle @@ -110,7 +114,7 @@ namespace olc // Configures defaults prior to drawing virtual bool DisplayPrepare() = 0; // Displays the final output - virtual bool DisplayDraw(bool bVerticalSyncNow = false) = 0; + virtual bool DisplayDraw(std::vector os_win_id, bool bVerticalSyncNow = false) = 0; protected: diff --git a/dev/src/gpu_opengl33.cpp b/dev/src/gpu_opengl33.cpp index 52f20bd1..7aa0e339 100644 --- a/dev/src/gpu_opengl33.cpp +++ b/dev/src/gpu_opengl33.cpp @@ -58,37 +58,22 @@ namespace olc::gpu - bool Renderer_OGL33::CreateDevice(std::vector params, const RendererConfig& cfg) + bool Renderer_OGL33::CreateDevice(std::vector os_win_id, 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))) + // Create OpenGL Device Context + if (!PrepareWindowTarget(os_win_id)) { - lastError = RendererError::InvalidDCPixelFormat; return false; } - if (!SetPixelFormat(glDeviceContext, pf, &pfd)) - { - lastError = RendererError::FailedToSetDCPixelFormat; - return false; - } + auto glDeviceContext = GetDC((HWND)(os_win_id[0])); + // Create OpenGL Render Context if (!(glRenderContext = wglCreateContext(glDeviceContext))) { lastError = RendererError::FailedToCreateRenderContext; @@ -305,6 +290,52 @@ namespace olc::gpu return false; } + bool Renderer_OGL33::RetargetDevice(std::vector os_win_id) + { +#if OLC_HOST == OLC_HOST_WINDOWS + auto glDeviceContext = GetDC((HWND)(os_win_id[0])); + + if (!wglMakeCurrent(glDeviceContext, glRenderContext)) + { + lastError = RendererError::FailedToSwitchRenderContext; + auto err = ::GetLastError(); + return false; + } +#endif + + return true; + } + + bool Renderer_OGL33::PrepareWindowTarget(std::vector os_win_id) + { +#if OLC_HOST == OLC_HOST_WINDOWS + auto dc = GetDC((HWND)(os_win_id[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(dc, &pfd))) + { + lastError = RendererError::InvalidDCPixelFormat; + return false; + } + + if (!SetPixelFormat(dc, pf, &pfd)) + { + lastError = RendererError::FailedToSetDCPixelFormat; + return false; + } +#endif + + return true; + } + uint32_t Renderer_OGL33::CreateTexture(const olc::vi2d& vSize, const olc::ImageConfig& cfg) { auto& gl = olc::apis::opengl::gl::Get(); @@ -545,16 +576,17 @@ namespace olc::gpu return false; } - bool Renderer_OGL33::DisplayDraw(bool bVerticalSyncNow) + bool Renderer_OGL33::DisplayDraw(std::vector os_win_id, bool bVerticalSyncNow) { auto& gl = olc::apis::opengl::gl::Get(); #if OLC_HOST == OLC_HOST_WINDOWS - SwapBuffers(glDeviceContext); + auto glDeviceContext = GetDC((HWND)(os_win_id[0])); + SwapBuffers(glDeviceContext); #endif - return false; + return true; } diff --git a/dev/src/gpu_opengl33.h b/dev/src/gpu_opengl33.h index 175ab14e..26e9396d 100644 --- a/dev/src/gpu_opengl33.h +++ b/dev/src/gpu_opengl33.h @@ -20,9 +20,13 @@ namespace olc { public: // Device Stuff // Constructs a GPU Device interface - bool CreateDevice(std::vector params, const RendererConfig& cfg) override; + bool CreateDevice(std::vector os_win_id, const RendererConfig& cfg) override; // Destroys a GPU device interface bool DestroyDevice() override; + // If applicable, relocate the rendering context + bool RetargetDevice(std::vector os_win_id) override; + // Prepare an OS rendering target + bool PrepareWindowTarget(std::vector os_win_id) override; public: // Texture Resource Stuff @@ -54,11 +58,11 @@ namespace olc // Configures defaults prior to drawing virtual bool DisplayPrepare() override; // Displays the final output - virtual bool DisplayDraw(bool bVerticalSyncNow) override; + virtual bool DisplayDraw(std::vector os_win_id, bool bVerticalSyncNow) override; protected: // These may need some thinking about re multiple window - olc::apis::opengl::glDeviceContext_t glDeviceContext = 0; + //olc::apis::opengl::glDeviceContext_t glDeviceContext = 0; olc::apis::opengl::glRenderContext_t glRenderContext = 0; Shader_GLSL33 shaderDefault; diff --git a/dev/src/host_iface.h b/dev/src/host_iface.h index f7c354a8..7d01094e 100644 --- a/dev/src/host_iface.h +++ b/dev/src/host_iface.h @@ -54,7 +54,7 @@ namespace olc HostError GetLastError() const { return lastError; } public: - virtual bool StartSystemEventLoop() = 0; + virtual bool StartSystemEventLoop(bool bBlockIfPossible = false) = 0; virtual bool AddWindowFrame(olc::Window* pWindow, const olc::vi2d& vWindowPos, const olc::vi2d& vWindowSize, const bool bFullScreen) = 0; virtual bool CloseWindowFrame(olc::Window* pWindow) = 0; virtual bool UpdateWindowFrameTitle(olc::Window* pWindow) = 0; diff --git a/dev/src/host_win_winapi.cpp b/dev/src/host_win_winapi.cpp index 3a07c32e..d64e2518 100644 --- a/dev/src/host_win_winapi.cpp +++ b/dev/src/host_win_winapi.cpp @@ -8,13 +8,25 @@ namespace olc::host // Windows app needs an event loop somewhere. This is blocking of course. This loop handles // all windows created for this host. - bool Host_Windows_WinAPI::StartSystemEventLoop() + bool Host_Windows_WinAPI::StartSystemEventLoop(bool bBlockIfPossible) { - MSG msg; - while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) > 0) + if (bBlockIfPossible) { - TranslateMessage(&msg); - DispatchMessage(&msg); + MSG msg; + while (GetMessage(&msg, NULL, 0, 0) > 0) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + else + { + MSG msg; + while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) > 0) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } } return true; diff --git a/dev/src/host_win_winapi.h b/dev/src/host_win_winapi.h index 9c16044f..0da6a964 100644 --- a/dev/src/host_win_winapi.h +++ b/dev/src/host_win_winapi.h @@ -64,7 +64,7 @@ namespace olc public: - bool StartSystemEventLoop(); + bool StartSystemEventLoop(bool bBlockIfPossible = false); bool AddWindowFrame(olc::Window* pWindow, const olc::vi2d& vWindowPos, const olc::vi2d& vWindowSize, const bool bFullScreen); bool CloseWindowFrame(olc::Window* pWindow); bool UpdateWindowFrameTitle(olc::Window* pWindow); diff --git a/dev/src/window.h b/dev/src/window.h index 36b31c9b..11498286 100644 --- a/dev/src/window.h +++ b/dev/src/window.h @@ -103,7 +103,7 @@ namespace olc olc::vi2d vWindowSize; std::string sFrameTitle; - private: + protected: olc::host::Host* pHost = nullptr; protected: diff --git a/dev/tests/test_mh.cpp b/dev/tests/test_mh.cpp index 8e5f24fa..c7f8847c 100644 --- a/dev/tests/test_mh.cpp +++ b/dev/tests/test_mh.cpp @@ -16,9 +16,16 @@ class SecondWindow : public olc::PGEWindow // Return true if window is to continue bool OnUserUpdate(float fElapsedTime) override { + fTotalTime += fElapsedTime; + draw.Clear(olc::Colour::RED); + draw.WorldRotate(fTotalTime, GetDefaultImage().Size() / 2); + draw.FilledRect({ 10,10 }, { 20,20 }, olc::Colour::BLUE); return true; - } + } + + + float fTotalTime = 0; }; @@ -117,8 +124,7 @@ class Example : public olc::PixelGameEngine { 100.0f, 200.0f} }; - win2 = std::make_shared(); - AddChildWindow(win2); + return true; @@ -157,6 +163,12 @@ class Example : public olc::PixelGameEngine draw.WorldRotate(fAngle, imLowRes.Size() / 2.0f); } + if (mouse.GetButton(2).bPressed) + { + auto w = std::make_shared(); + AddChildWindow(w, { 100,100 }, { 2, 2 }); + } + if (mouse.GetWheel() > 0) { fScale *= 1.1f; From a05b9741f91c37ea562371fe02f4f1e6d17afeb6 Mon Sep 17 00:00:00 2001 From: Javidx9 <25419386+OneLoneCoder@users.noreply.github.com> Date: Mon, 24 Nov 2025 23:32:50 +0000 Subject: [PATCH 06/17] Tidied up winapi events, added multiwin flag for developing efficacy, fixed a DC resourcing issue --- dev/src/config.h | 8 ++++++ dev/src/core.cpp | 50 +++++++++++++++++++++++++------------ dev/src/gpu_opengl33.cpp | 9 ++++--- dev/src/host_win_winapi.cpp | 26 +++++++++---------- dev/tests/test_mh.cpp | 2 +- 5 files changed, 62 insertions(+), 33 deletions(-) diff --git a/dev/src/config.h b/dev/src/config.h index 52625e6b..38540c70 100644 --- a/dev/src/config.h +++ b/dev/src/config.h @@ -80,6 +80,14 @@ #endif +#define OLC_MULTIWINDOW_NO 1 +#define OLC_MULTIWINDOW_YES 2 + +#if !defined(OLC_MULTIWINDOW) + #define OLC_MULTIWINDOW OLC_MULTIWINDOW_NO +#endif + + #define OLC_MOUSE_BUTTONS 3 #define OLC_GPU_MAX_VERTICES 8192 diff --git a/dev/src/core.cpp b/dev/src/core.cpp index 0bf97df5..a5fa7d0f 100644 --- a/dev/src/core.cpp +++ b/dev/src/core.cpp @@ -38,7 +38,9 @@ namespace olc draw.SetGPU(pRenderer); draw.SetTarget(GetDefaultImage()); +#if OLC_MULTIWINDOW == OLC_MULTIWINDOW_YES pRenderer->RetargetDevice(pHost->GetHostWindowDescriptor(this)); +#endif pRenderer->ApplyDefaultShader(); @@ -194,21 +196,29 @@ namespace olc // Initialise Host Interface host = std::make_unique(); - +#if OLC_MULTIWINDOW == OLC_MULTIWINDOW_NO + // Create OS window on this thread + host->AddWindowFrame(this, { 30,30 }, config.vPixelSize * config.vScreenSize, false); + // Create EngineThread - no more windows will be created now. We needed one window + // at least to initialise teh rendering subsystem... sigh. coreActive = true; - //coreThread = std::thread(&PixelGameEngine::EngineThread, this); - - EngineThread(); - - // Gracefully terminate the main engine thread - //coreActive = false; - //coreThread.join(); - + coreThread = std::thread(&PixelGameEngine::EngineThread, this); + // Handle window events on this thread (and block) + host->StartSystemEventLoop(true); + // Window has closed its event handler, so shut down gracefully + coreActive = false; + // Wait for engine thread to terminate + coreThread.join(); +#else + +#endif + return true; } bool PixelGameEngine::AddChildWindow(std::shared_ptr window, const olc::vi2d& vScreenSize, const olc::vi2d& vPixelSize) { +#if OLC_MULTIWINDOW == OLC_MULTIWINDOW_YES // Link this olc::Window to a host resource if (host) { @@ -221,8 +231,11 @@ namespace olc window->Create(vScreenSize, vPixelSize); deqChildWindows.push_back(window); } - return true; +#else + // Can't create new windows + return false; +#endif } @@ -233,12 +246,12 @@ namespace olc timeFrame2 = std::chrono::steady_clock::now(); timeFrame1 = std::chrono::steady_clock::now(); - // Start the host system event loop in its own thread - //auto t1 = std::thread([this]() { host->StartSystemEventLoop(true); }); - - // Create Primary Window +#if OLC_MULTIWINDOW == OLC_MULTIWINDOW_YES + // Create Primary Window on EngineThread, event loop also exists for all windows + // on this thread, and all windows will be created on this thread host->AddWindowFrame(this, { 30,30 }, config.vPixelSize * config.vScreenSize, false); - +#endif + // Initialise ImageLoader Interface imageloader = std::make_unique(); @@ -295,7 +308,10 @@ namespace olc while (coreActive) { - host->StartSystemEventLoop(); +#if OLC_MULTIWINDOW == OLC_MULTIWINDOW_YES + // Multiwindow system uses one event loop (non blocking) for all windows + host->StartSystemEventLoop(false); +#endif // Frame Delta Timing - "ElapsedTime" since last core update // ~~~~~~~~~~~~~~~~~~ @@ -338,6 +354,7 @@ namespace olc } else { +#if OLC_MULTIWINDOW == OLC_MULTIWINDOW_YES // Update Child Windows (if any) for (auto& winChild : deqChildWindows) winChild->olc_WindowUpdate(fDT); @@ -356,6 +373,7 @@ namespace olc } ), deqChildWindows.end()); } +#endif // Update Primary Window olc_WindowUpdate(fDT); diff --git a/dev/src/gpu_opengl33.cpp b/dev/src/gpu_opengl33.cpp index 7aa0e339..290cd7ce 100644 --- a/dev/src/gpu_opengl33.cpp +++ b/dev/src/gpu_opengl33.cpp @@ -301,6 +301,7 @@ namespace olc::gpu auto err = ::GetLastError(); return false; } + ReleaseDC((HWND)(os_win_id[0]), glDeviceContext); #endif return true; @@ -309,7 +310,7 @@ namespace olc::gpu bool Renderer_OGL33::PrepareWindowTarget(std::vector os_win_id) { #if OLC_HOST == OLC_HOST_WINDOWS - auto dc = GetDC((HWND)(os_win_id[0])); + auto glDeviceContext = GetDC((HWND)(os_win_id[0])); PIXELFORMATDESCRIPTOR pfd = { @@ -320,17 +321,18 @@ namespace olc::gpu }; int pf = 0; - if (!(pf = ChoosePixelFormat(dc, &pfd))) + if (!(pf = ChoosePixelFormat(glDeviceContext, &pfd))) { lastError = RendererError::InvalidDCPixelFormat; return false; } - if (!SetPixelFormat(dc, pf, &pfd)) + if (!SetPixelFormat(glDeviceContext, pf, &pfd)) { lastError = RendererError::FailedToSetDCPixelFormat; return false; } + ReleaseDC((HWND)(os_win_id[0]), glDeviceContext); #endif return true; @@ -584,6 +586,7 @@ namespace olc::gpu #if OLC_HOST == OLC_HOST_WINDOWS auto glDeviceContext = GetDC((HWND)(os_win_id[0])); SwapBuffers(glDeviceContext); + ReleaseDC((HWND)(os_win_id[0]), glDeviceContext); #endif return true; diff --git a/dev/src/host_win_winapi.cpp b/dev/src/host_win_winapi.cpp index d64e2518..476a4a97 100644 --- a/dev/src/host_win_winapi.cpp +++ b/dev/src/host_win_winapi.cpp @@ -211,25 +211,22 @@ namespace olc::host int16_t iy = *(int16_t*)&y; // Tell window new mouse location window->olc_OnMouseMove(olc::vi2d{ ix, iy }); - return 0; + break; } - break; // case WM_MOVE: vWinPos = olc::vi2d(lParam & 0xFFFF, (lParam >> 16) & 0xFFFF); ptrPGE->olc_UpdateWindowPos(lParam & 0xFFFF, (lParam >> 16) & 0xFFFF); return 0; case WM_SIZE: { window->olc_OnWindowSize(olc::vi2d(lParam & 0xFFFF, (lParam >> 16) & 0xFFFF)); - return 0; + break; } - break; case WM_MOUSEWHEEL: { window->olc_OnMouseWheel(GET_WHEEL_DELTA_WPARAM(wParam)); - return 0; + break; } - break; // case WM_MOUSELEAVE: ptrPGE->olc_UpdateMouseFocus(false); return 0; // case WM_SETFOCUS: ptrPGE->olc_UpdateKeyFocus(true); return 0; // case WM_KILLFOCUS: ptrPGE->olc_UpdateKeyFocus(false); return 0; @@ -240,32 +237,32 @@ namespace olc::host case WM_LBUTTONDOWN: { window->olc_OnMouseButton(0, true); - return 0; + break; } case WM_LBUTTONUP: { window->olc_OnMouseButton(0, false); - return 0; + break; } case WM_RBUTTONDOWN: { window->olc_OnMouseButton(1, true); - return 0; + break; } case WM_RBUTTONUP: { window->olc_OnMouseButton(1, false); - return 0; + break; } case WM_MBUTTONDOWN: { window->olc_OnMouseButton(2, true); - return 0; + break; } case WM_MBUTTONUP: { window->olc_OnMouseButton(2, false); - return 0; + break; } // case WM_DROPFILES: // { @@ -308,13 +305,16 @@ namespace olc::host case WM_CLOSE: { window->olc_OnWindowClose(); - return 0; + break; + //return DefWindowProc(hWnd, uMsg, wParam, lParam); } case WM_DESTROY: PostQuitMessage(0); DestroyWindow(hWnd); } + + return DefWindowProc(hWnd, uMsg, wParam, lParam); } diff --git a/dev/tests/test_mh.cpp b/dev/tests/test_mh.cpp index c7f8847c..48c52bb7 100644 --- a/dev/tests/test_mh.cpp +++ b/dev/tests/test_mh.cpp @@ -166,7 +166,7 @@ class Example : public olc::PixelGameEngine if (mouse.GetButton(2).bPressed) { auto w = std::make_shared(); - AddChildWindow(w, { 100,100 }, { 2, 2 }); + //AddChildWindow(w, { 100,100 }, { 2, 2 }); } if (mouse.GetWheel() > 0) From 2e87c611827261fb22652aae8d4aaae7eac5146b Mon Sep 17 00:00:00 2001 From: Javidx9 <25419386+OneLoneCoder@users.noreply.github.com> Date: Wed, 26 Nov 2025 22:51:59 +0000 Subject: [PATCH 07/17] rough cut hack job for font support. totally wip right now, buggy too! --- dev/msvc/olcPGE3.vcxproj | 2 + dev/msvc/olcPGE3.vcxproj.filters | 6 ++ dev/src/core.cpp | 4 ++ dev/src/core.h | 1 + dev/src/draw2d.cpp | 35 +++++++++++ dev/src/draw2d.h | 6 ++ dev/src/font.cpp | 103 +++++++++++++++++++++++++++++++ dev/src/font.h | 59 ++++++++++++++++++ dev/src/image.cpp | 4 +- dev/src/image.h | 2 +- dev/tests/test_mh.cpp | 4 ++ 11 files changed, 223 insertions(+), 3 deletions(-) create mode 100644 dev/src/font.cpp create mode 100644 dev/src/font.h diff --git a/dev/msvc/olcPGE3.vcxproj b/dev/msvc/olcPGE3.vcxproj index 50fe935d..e829cf45 100644 --- a/dev/msvc/olcPGE3.vcxproj +++ b/dev/msvc/olcPGE3.vcxproj @@ -180,6 +180,7 @@ + @@ -208,6 +209,7 @@ + diff --git a/dev/msvc/olcPGE3.vcxproj.filters b/dev/msvc/olcPGE3.vcxproj.filters index 08ebcaab..59ae8f74 100644 --- a/dev/msvc/olcPGE3.vcxproj.filters +++ b/dev/msvc/olcPGE3.vcxproj.filters @@ -99,6 +99,9 @@ Header Files + + Header Files + @@ -146,5 +149,8 @@ Source Files + + Source Files + \ No newline at end of file diff --git a/dev/src/core.cpp b/dev/src/core.cpp index a5fa7d0f..f4ac40f2 100644 --- a/dev/src/core.cpp +++ b/dev/src/core.cpp @@ -284,6 +284,10 @@ namespace olc CreateImage(GetDefaultImage(), config.vScreenSize); + // Initialise Font System + olc::pgeguts::CreateClassicFont(this); + + draw.SetGPU(gpu.get()); gpu->ApplyDefaultShader(); draw.SetTarget(GetDefaultImage()); diff --git a/dev/src/core.h b/dev/src/core.h index 00b0c9df..92855e08 100644 --- a/dev/src/core.h +++ b/dev/src/core.h @@ -20,6 +20,7 @@ #include "gpu_iface.h" #include "host_iface.h" #include "imload_iface.h" +#include "font.h" //! END CUSTOMHEADER GLOBAL //! START DECLARATION diff --git a/dev/src/draw2d.cpp b/dev/src/draw2d.cpp index d9cbe7b9..eb22838f 100644 --- a/dev/src/draw2d.cpp +++ b/dev/src/draw2d.cpp @@ -301,6 +301,41 @@ const GPUTask& olc::Draw2D::FilledRect(const olc::vf2d& pos, const olc::vf2d& si )); } +const GPUTask& olc::Draw2D::String(const olc::vf2d& pos, const std::string& text, olc::Font& font) +{ + PrepareTargetForHW(); + + olc::vf2d spos = { 0.0f, 0.0f }; + //olc::vf2d size = image.regionsize * scale; + + for (auto c : text) + { + if (c == '\n') + { + spos.x = 0; spos.y += 8.0f * 1.0f; + } + else if (c == '\t') + { + spos.x += 8.0f * float(4) * 1.0f; + } + else + { + int32_t ox = (c - 32) % 16; + int32_t oy = (c - 32) / 16; + Image(font.glyphs[c].imgGlyph, pos + spos); + + + + + + + spos.x += 8.0f * 1.0f; + } + } + + return vecGPUTasks.emplace_back(); +} + const GPUTask& olc::Draw2D::Image(olc::ImageRegion image, const olc::vf2d& pos, const olc::vf2d& scale) { // Ensure source image is up to date in VRAM diff --git a/dev/src/draw2d.h b/dev/src/draw2d.h index ec7e8735..ae7010ce 100644 --- a/dev/src/draw2d.h +++ b/dev/src/draw2d.h @@ -16,6 +16,7 @@ #include "pixel.h" #include "image.h" #include "gputask.h" +#include "font.h" //! END CUSTOMHEADER //! START DECLARATION @@ -133,6 +134,11 @@ namespace olc //ShadedPolygon //TexturedPolygon + const GPUTask& String( + const olc::vf2d& pos, + const std::string& text, + olc::Font& font = olc::fontClassicPGE); + // Draws a scaled image at specified location const GPUTask& Image( diff --git a/dev/src/font.cpp b/dev/src/font.cpp new file mode 100644 index 00000000..8116a8bd --- /dev/null +++ b/dev/src/font.cpp @@ -0,0 +1,103 @@ +#include "font.h" + +#include "core.h" + +//! START IMPLEMENTATION +namespace olc +{ + + + /*void Font::CreateFontSheet(const olc::Image& imgFont, const std::array& glyphs) + { + imgFontSheet = imgFont; + allGlyphs = glyphs; + }*/ + + /*bool Font::LoadFontSheet(imload::ImageLoader* imload, const std::string& sImageFile, const std::array& glyphs) + { + if (imload->CreateImageFromFile(imgFontSheet, sImageFile)) + { + allGlyphs = glyphs; + return true; + } + + return false; + }*/ + + /*olc::FontGlyph Font::GetGlyph(const char c) + { + return allGlyphs[uint8_t(c)]; + }*/ + + namespace pgeguts + { + void CreateClassicFont(olc::PGEWindow* pge) + { + std::string data = + "?Q`0001oOch0o01o@F40o000000000" + "O000000nOT0063Qo4d8>?7a14Gno94AA4gno94AaOT0>o3`oO400o7QN00000400" + "Of80001oOg<7O7moBGT7O7lABET024@aBEd714AiOdl717a_=TH013Q>00000000" + "720D000V?V5oB3Q_HdUoE7a9@DdDE4A9@DmoE4A;Hg]oM4Aj8S4D84@`00000000" + "OaPT1000Oa`^13P1@AI[?g`1@A=[OdAoHgljA4Ao?WlBA7l1710007l100000000" + "ObM6000oOfMV?3QoBDD`O7a0BDDH@5A0BDD<@5A0BGeVO5ao@CQR?5Po00000000" + "Oc``000?Ogij70PO2D]??0Ph2DUM@7i`2DTg@7lh2GUj?0TO0C1870T?00000000" + "70<4001o?P<7?1QoHg43O;`h@GT0@:@LB@d0>:@hN@L0@?aoN@<0O7ao0000?000" + "OcH0001SOglLA7mg24TnK7ln24US>0PL24U140PnOgl0>7QgOcH0K71S0000A000" + "00H00000@Dm1S007@DUSg00?OdTnH7YhOfTL<7Yh@Cl0700?@Ah0300700000000" + "<008001QL00ZA41a@6HnI<1i@FHLM81M@@0LG81?O`0nC?Y7?`0ZA7Y300080000" + "O`082000Oh0827mo6>Hn?Wmo?6HnMb11MP08@C11H`08@FP0@@0004@000000000" + "00P00001Oab00003OcKP0006@6=PMgl<@440MglH@000000`@000001P00000000" + "Ob@8@@00Ob@8@Ga13R@8Mga172@8?PAo3R@827QoOb@820@0O`0007`0000007P0" + "O`000P08Od400g`<3V=P0G`673IP0`@3>1`00P@6O`P00g`CreateImage(fontClassicPGE.imgFontSheet, { 128, 48 }); + + fontClassicPGE.imgFontSheet.BindCPU(); + + int px = 0, py = 0; + for (size_t b = 0; b < 1024; b += 4) + { + uint32_t sym1 = (uint32_t)data[b + 0] - 48; + uint32_t sym2 = (uint32_t)data[b + 1] - 48; + uint32_t sym3 = (uint32_t)data[b + 2] - 48; + uint32_t sym4 = (uint32_t)data[b + 3] - 48; + uint32_t r = sym1 << 18 | sym2 << 12 | sym3 << 6 | sym4; + + for (int i = 0; i < 24; i++) + { + int k = r & (1 << i) ? 255 : 0; + fontClassicPGE.imgFontSheet.Pixel({ px, py }) = olc::Pixel(k, k, k, k); + if (++py == 48) { px++; py = 0; } + } + } + + // Monospace font, 8x8 pixels per character + for (int c = 0; c < 256; c++) + { + if (c >= 32 && c < 128) + { + FontGlyph glyph{ + fontClassicPGE.imgFontSheet.region( + { float(((c - 32) % 16) * 8), float(((c - 32) / 16) * 8) }, + { 8.0f, 8.0f } + ), + { 8.0f, 8.0f } + }; + fontClassicPGE.glyphs.push_back(glyph); + } + else + fontClassicPGE.glyphs.push_back(FontGlyph{ fontClassicPGE.imgFontSheet.region({0,0}, {8,8}) , {8.0f, 8.0f} }); + + } + + + //fontClassicPGE.imgFontSheet = img; + + } + } +} +//! END IMPLEMENTATION diff --git a/dev/src/font.h b/dev/src/font.h new file mode 100644 index 00000000..6962ca3e --- /dev/null +++ b/dev/src/font.h @@ -0,0 +1,59 @@ +#pragma once + +//! START STDHEADER GLOBAL +#include +#include +#include +#include +#include +#include +//! END STDHEADER + + +//! START CUSTOMHEADER GLOBAL +#include "config.h" +#include "image.h" +#include "imload_iface.h" +//! END CUSTOMHEADER + + +//! START DECLARATION +#if !defined(PGE_FONT_DECLARED) +namespace olc +{ + class PGEWindow; + + struct FontGlyph + { + olc::ImageRegion imgGlyph; + olc::vf2d vKerning; + }; + + class Font + { + public: + Font() = default; + ~Font() = default; + + //void CreateFontSheet(const olc::Image& imgFont, const std::array& glyphs); + //bool LoadFontSheet(imload::ImageLoader* imload, const std::string& sImageFile, const std::array& glyphs); + //FontGlyph GetGlyph(const char c); + + public: + olc::Image imgFontSheet; + std::vector glyphs; + olc::vf2d vCharSize; + }; + + inline olc::Font fontClassicPGE; + + namespace pgeguts + { + void CreateClassicFont(olc::PGEWindow* pge); + } + +} + +#define PGE_FONT_DECLARED 1 +#endif +//! END DECLARATION \ No newline at end of file diff --git a/dev/src/image.cpp b/dev/src/image.cpp index f84d5766..9880e8db 100644 --- a/dev/src/image.cpp +++ b/dev/src/image.cpp @@ -29,7 +29,7 @@ namespace olc return pixels.data(); } - olc::Pixel& Image::Pixel(const olc::vf2d& pos) + olc::Pixel& Image::Pixel(const olc::vi2d& pos) { return pixels[int(pos.y) * dimensions.x + int(pos.x)]; } @@ -69,7 +69,7 @@ namespace olc 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) + 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 ); diff --git a/dev/src/image.h b/dev/src/image.h index 8b84321e..eef0c149 100644 --- a/dev/src/image.h +++ b/dev/src/image.h @@ -45,7 +45,7 @@ namespace olc // 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); + olc::Pixel& Pixel(const olc::vi2d& pos); // Returns how this image was configured upon creation const ImageConfig& GetConfig() const; // Return GPU Resource ID diff --git a/dev/tests/test_mh.cpp b/dev/tests/test_mh.cpp index 48c52bb7..079f298b 100644 --- a/dev/tests/test_mh.cpp +++ b/dev/tests/test_mh.cpp @@ -228,6 +228,10 @@ class Example : public olc::PixelGameEngine draw.Image(imSampleTest, vMouse); + draw.Image(olc::fontClassicPGE.imgFontSheet, vMouse); + + draw.String({ 10.0f, 10.0f }, "Hello World!"); + //draw.Pixel(vMouse, olc::Colour::GREEN); //draw.Line(vMouse, vMouse + 1, olc::Colour::GREEN); From 6c4e55b0c41cbbb80c72a1e3098fb00c0c5597cf Mon Sep 17 00:00:00 2001 From: Javidx9 <25419386+OneLoneCoder@users.noreply.github.com> Date: Sat, 29 Nov 2025 23:38:03 +0000 Subject: [PATCH 08/17] gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index d2acef1b..7dcb3309 100644 --- a/.gitignore +++ b/.gitignore @@ -38,3 +38,4 @@ /dev/msvc/olcPGE3/x64/Release /dev/msvc/x64/Debug/olcPGE3.tlog /dev/msvc/.vs/CopilotSnapshots/48318E23BE80F04A9ED976A29E9EEE0D +/dev/msvc/olcPGE3_BuildSH/x64/Release From 46e03b1519886ae0ead984c39ad3895d92cc2565 Mon Sep 17 00:00:00 2001 From: Javidx9 <25419386+OneLoneCoder@users.noreply.github.com> Date: Sat, 29 Nov 2025 23:38:33 +0000 Subject: [PATCH 09/17] tidied up fonts, a bit slow in debug, so will look to group the renderings --- .../olcPGE3_BuildSH/olcPixelGameEngine3.h | 501 +++++++++++++++--- dev/src/api_opengl.cpp | 6 + dev/src/api_opengl.h | 2 +- dev/src/draw2d.cpp | 95 +++- dev/src/draw2d.h | 43 +- dev/src/font.cpp | 71 ++- dev/src/font.h | 25 +- dev/src/gpu_opengl33.cpp | 5 +- dev/src/sh_template.h | 90 +++- dev/tests/test_mh.cpp | 15 +- 10 files changed, 708 insertions(+), 145 deletions(-) diff --git a/dev/msvc/olcPGE3_BuildSH/olcPixelGameEngine3.h b/dev/msvc/olcPGE3_BuildSH/olcPixelGameEngine3.h index ab450a17..1eb9b2fe 100644 --- a/dev/msvc/olcPGE3_BuildSH/olcPixelGameEngine3.h +++ b/dev/msvc/olcPGE3_BuildSH/olcPixelGameEngine3.h @@ -1,12 +1,87 @@ #pragma once -// olc::PixelGameEngine 3.0 -// OneLoneCoder 2025 - // DO NOT EDIT THIS FILE! // This file is automatically generated. Changes to source should be made // through relevant component source file. +/* + olcPixelGameEngine3.h + + +-------------------------------------------------------------+ + | OneLoneCoder Pixel Game Engine v3.00 | + | "What do you need? Pixels... Lots of Pixels..." - javidx9 | + +-------------------------------------------------------------+ + + What is this? + ~~~~~~~~~~~~~ + olc::PixelGameEngine is a single file, cross platform graphics and userinput + framework used for games, visualisations, algorithm exploration and learning. + It was developed by YouTuber "javidx9" as an assistive tool for many of his + videos. The goal of this project is to provide high speed graphics with + minimal project setup complexity, to encourage new programmers, younger people, + and anyone else that wants to make fun things. + + However, olc::PixelGameEngine is not a toy! It is a powerful and fast utility + capable of delivering high resolution, high speed, high quality applications + which behave the same way regardless of the operating system or platform. + + This file provides the core utility set of the olc::PixelGameEngine, including + window creation, keyboard/mouse input, main game thread, timing, pixel drawing + routines, image/sprite loading and drawing routines, and a bunch of utility + types to make rapid development of games/visualisations possible. + + + License (OLC-3) + ~~~~~~~~~~~~~~~ + + Copyright 2018 - 2025 OneLoneCoder.com + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions or derivations of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions or derivative works in binary form must reproduce the above + copyright notice. This list of conditions and the following disclaimer must be + reproduced in the documentation and/or other materials provided with the distribution. + + 3. Neither the name of the copyright holder nor the names of its contributors may + be used to endorse or promote products derived from this software without specific + prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. + + TLDR: + You can do whatever you want with this code. Your only obligation is to credit in a + visible way to your end users in your running program that you used this code or a + derivative of it. If you don't edit this file, you are automatically in compliance with + OLC-3, as the message "OneLoneCoder.com" is present in the output window title bar. If + you compile to full screen, or remove the title bar, then you must display somewhere + something like "Powered by olcPixelGameEngine v3.00 - OneLoneCoder.com". That's it. + + Links + ~~~~~ + YouTube: https://www.youtube.com/javidx9 + https://www.youtube.com/javidx9extra + Discord: https://discord.gg/WhwHUMV + X: https://www.x.com/javidx9 + Twitch: https://www.twitch.tv/javidx9 + GitHub: https://www.github.com/onelonecoder + Homepage: https://www.onelonecoder.com + Patreon: https://www.patreon.com/javidx9 +*/ + + #include #include #include @@ -104,6 +179,14 @@ #endif +#define OLC_MULTIWINDOW_NO 1 +#define OLC_MULTIWINDOW_YES 2 + +#if !defined(OLC_MULTIWINDOW) + #define OLC_MULTIWINDOW OLC_MULTIWINDOW_NO +#endif + + #define OLC_MOUSE_BUTTONS 3 #define OLC_GPU_MAX_VERTICES 8192 @@ -1202,7 +1285,7 @@ namespace olc // 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); + olc::Pixel& Pixel(const olc::vi2d& pos); // Returns how this image was configured upon creation const ImageConfig& GetConfig() const; // Return GPU Resource ID @@ -1263,6 +1346,56 @@ namespace olc #define PGE_IMAGE_DECLARED 1 #endif +#if !defined(PGE_FONT_DECLARED) +namespace olc +{ + class PGEWindow; + + struct FontGlyph + { + // Region of an olc::Image that contains the glyph + // in bitmap format. Typically WHITE on TRANSPARENT... + // ...though have fun with crazy alternatives :P + olc::ImageRegion imgGlyph; + // Leading spacing in pixels before glyph + float spacing; + // Size of the glyph in pixels + olc::vf2d vPropSize; + // Size of the glyph in monospace format + olc::vf2d vMonoSize; + }; + + class Font + { + public: + Font() = default; + ~Font() = default; + + public: + // Image that contains all glyphs + olc::Image imgFont; + // All glyphs in the font + std::vector glyphs; + // Line height in pixels + float fLineHeight = 10.0f; + // Tab width in pixels + float fTabWidth = 32.0f; + }; + + // Predefined classic PGE font + inline olc::Font fontClassicPGE; + + namespace pgeguts + { + // Create the classic PGE font + void CreateClassicFont(olc::PGEWindow* pge); + } + +} + +#define PGE_FONT_DECLARED 1 +#endif + #if !defined(PGE_IMAGELOADER_IFACE_DECLARED) namespace olc { @@ -1560,7 +1693,7 @@ namespace olc olc::vf2d WorldToScreen(const olc::vf2d& v) const; olc::vf2d ScreenToWorld(const olc::vf2d& v) const; - public: + public: // Primitive Drawing Functions // Plot a single pixel void Pixel( const olc::vf2d& pos, @@ -1574,7 +1707,7 @@ namespace olc // Clear entire draw target to specific colour void Clear(const olc::Pixel& col); - public: + public: // Shape Drawing Functions // Draws a single pixel wide line const GPUTask& Line( const olc::vf2d& p1, @@ -1638,12 +1771,36 @@ namespace olc //ShadedPolygon //TexturedPolygon - + public: // Text Drawing Functions + // Draws a string at specified location in monospace font + const GPUTask& String( + const olc::vf2d& pos, + const std::string& text, + const olc::Pixel col = olc::Colour::WHITE, + const olc::vf2d& scale = { 1.0f, 1.0f }, + olc::Font& font = olc::fontClassicPGE); + + // Draws a string at specified location in proportional font + const GPUTask& StringProp( + const olc::vf2d& pos, + const std::string& text, + const olc::Pixel col = olc::Colour::WHITE, + const olc::vf2d& scale = { 1.0f, 1.0f }, + olc::Font& font = olc::fontClassicPGE); + + olc::vf2d GetTextSize( + const std::string& text, + const bool bProportional = false, + const olc::vf2d& scale = { 1.0f, 1.0f }, + olc::Font& font = olc::fontClassicPGE); + + public: // Image Drawing Functions // Draws a scaled image at specified location const GPUTask& Image( olc::ImageRegion image, const olc::vf2d& pos, - const olc::vf2d& scale = { 1.0f, 1.0f }); + const olc::vf2d& scale = { 1.0f, 1.0f }, + const olc::Pixel tint = olc::Colour::WHITE); // Draws an image rotated around a point at specified location const GPUTask& ImageRotated( @@ -1651,7 +1808,8 @@ namespace olc const olc::vf2d& pos, const float theta, const olc::vf2d& center = { 0.0f, 0.0f }, - const olc::vf2d& scale = { 1.0f, 1.0f }); + const olc::vf2d& scale = { 1.0f, 1.0f }, + const olc::Pixel tint = olc::Colour::WHITE); // Draws an image warped correctly to linearly fill a quadrilateral (formerly DrawWarped...) const GPUTask& ImageQuad( @@ -1659,16 +1817,21 @@ namespace olc const olc::vf2d& vTL, const olc::vf2d& vTR, const olc::vf2d& vBR, - const olc::vf2d& vBL); + const olc::vf2d& vBL, + const olc::Pixel tint = olc::Colour::WHITE); // Draws an image warped correctly to linearly fill a quadrilateral (formerly DrawWarped...) const GPUTask& ImageQuad( olc::ImageRegion image, - const std::vector& vecPoints); - + const std::vector& vecPoints, + const olc::Pixel tint = olc::Colour::WHITE); // Draws an image scaled to a specified rectangular area - const GPUTask& ImageRect(olc::ImageRegion image, const olc::vf2d& pos, const olc::vf2d& size); + const GPUTask& ImageRect( + olc::ImageRegion image, + const olc::vf2d& pos, + const olc::vf2d& size, + const olc::Pixel tint = olc::Colour::WHITE); @@ -2083,6 +2246,8 @@ namespace olc + + #if OLC_HOST == OLC_HOST_WINDOWS #if defined(UNICODE) || defined(_UNICODE) #define olcT(s) L##s @@ -2359,7 +2524,7 @@ namespace olc void glBlendFunc(GLenum sfactor, GLenum dfactor); void glDepthFunc(GLenum func); void glGetTexImage(GLenum target, GLint level, GLenum format, GLenum type, void* pixels); - + void glHint(GLenum target, GLenum mode); private: bool CheckError(const std::source_location loc = std::source_location::current()); @@ -2743,25 +2908,22 @@ namespace olc::host int16_t iy = *(int16_t*)&y; // Tell window new mouse location window->olc_OnMouseMove(olc::vi2d{ ix, iy }); - return 0; + break; } - break; // case WM_MOVE: vWinPos = olc::vi2d(lParam & 0xFFFF, (lParam >> 16) & 0xFFFF); ptrPGE->olc_UpdateWindowPos(lParam & 0xFFFF, (lParam >> 16) & 0xFFFF); return 0; case WM_SIZE: { window->olc_OnWindowSize(olc::vi2d(lParam & 0xFFFF, (lParam >> 16) & 0xFFFF)); - return 0; + break; } - break; case WM_MOUSEWHEEL: { window->olc_OnMouseWheel(GET_WHEEL_DELTA_WPARAM(wParam)); - return 0; + break; } - break; // case WM_MOUSELEAVE: ptrPGE->olc_UpdateMouseFocus(false); return 0; // case WM_SETFOCUS: ptrPGE->olc_UpdateKeyFocus(true); return 0; // case WM_KILLFOCUS: ptrPGE->olc_UpdateKeyFocus(false); return 0; @@ -2772,32 +2934,32 @@ namespace olc::host case WM_LBUTTONDOWN: { window->olc_OnMouseButton(0, true); - return 0; + break; } case WM_LBUTTONUP: { window->olc_OnMouseButton(0, false); - return 0; + break; } case WM_RBUTTONDOWN: { window->olc_OnMouseButton(1, true); - return 0; + break; } case WM_RBUTTONUP: { window->olc_OnMouseButton(1, false); - return 0; + break; } case WM_MBUTTONDOWN: { window->olc_OnMouseButton(2, true); - return 0; + break; } case WM_MBUTTONUP: { window->olc_OnMouseButton(2, false); - return 0; + break; } // case WM_DROPFILES: // { @@ -2840,13 +3002,16 @@ namespace olc::host case WM_CLOSE: { window->olc_OnWindowClose(); - return 0; + break; + //return DefWindowProc(hWnd, uMsg, wParam, lParam); } case WM_DESTROY: PostQuitMessage(0); DestroyWindow(hWnd); } + + return DefWindowProc(hWnd, uMsg, wParam, lParam); } @@ -3082,6 +3247,12 @@ namespace olc::apis::opengl CheckError(); } + void gl::glHint(GLenum target, GLenum mode) + { + ::glHint(target, mode); + CheckError(); + } + GLuint gl::glCreateShader(GLenum type) { return _glCreateShader(type); @@ -3536,8 +3707,9 @@ namespace olc::gpu // Unbind the FBO gl.glBindFramebuffer(36160U, 0); - glEnable(GL_TEXTURE_2D); // Turn on texturing - glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); + gl.glEnable(GL_TEXTURE_2D); // Turn on texturing + gl.glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); + gl.glEnable(GL_BLEND); lastError = RendererError::None; return true; @@ -3564,6 +3736,7 @@ namespace olc::gpu auto err = ::GetLastError(); return false; } + ReleaseDC((HWND)(os_win_id[0]), glDeviceContext); #endif return true; @@ -3572,7 +3745,7 @@ namespace olc::gpu bool Renderer_OGL33::PrepareWindowTarget(std::vector os_win_id) { #if OLC_HOST == OLC_HOST_WINDOWS - auto dc = GetDC((HWND)(os_win_id[0])); + auto glDeviceContext = GetDC((HWND)(os_win_id[0])); PIXELFORMATDESCRIPTOR pfd = { @@ -3583,17 +3756,18 @@ namespace olc::gpu }; int pf = 0; - if (!(pf = ChoosePixelFormat(dc, &pfd))) + if (!(pf = ChoosePixelFormat(glDeviceContext, &pfd))) { lastError = RendererError::InvalidDCPixelFormat; return false; } - if (!SetPixelFormat(dc, pf, &pfd)) + if (!SetPixelFormat(glDeviceContext, pf, &pfd)) { lastError = RendererError::FailedToSetDCPixelFormat; return false; } + ReleaseDC((HWND)(os_win_id[0]), glDeviceContext); #endif return true; @@ -3847,6 +4021,7 @@ namespace olc::gpu #if OLC_HOST == OLC_HOST_WINDOWS auto glDeviceContext = GetDC((HWND)(os_win_id[0])); SwapBuffers(glDeviceContext); + ReleaseDC((HWND)(os_win_id[0]), glDeviceContext); #endif return true; @@ -4157,7 +4332,97 @@ const GPUTask& olc::Draw2D::FilledRect(const olc::vf2d& pos, const olc::vf2d& si )); } -const GPUTask& olc::Draw2D::Image(olc::ImageRegion image, const olc::vf2d& pos, const olc::vf2d& scale) +const GPUTask& olc::Draw2D::String(const olc::vf2d& pos, const std::string& text, const olc::Pixel col, const olc::vf2d& scale, olc::Font& font) +{ + PrepareTargetForHW(); + + olc::vf2d spos = { 0.0f, 0.0f }; + + for (auto c : text) + { + const auto& glyph = font.glyphs[c]; + + if (c == '\n') + { + spos.x = 0; + spos.y += font.fLineHeight * scale.y; + } + else if (c == '\t') + { + spos.x += font.fTabWidth * scale.x; + } + else + { + Draw2D::Image(font.glyphs[c].imgGlyph, pos + spos, scale, col); + spos.x += glyph.vMonoSize.x * scale.x; + } + } + + return vecGPUTasks.emplace_back(); +} + +const GPUTask& olc::Draw2D::StringProp(const olc::vf2d& pos, const std::string& text, const olc::Pixel col, const olc::vf2d& scale, olc::Font& font) +{ + PrepareTargetForHW(); + + olc::vf2d spos = { 0.0f, 0.0f }; + + for (auto c : text) + { + const auto& glyph = font.glyphs[c]; + + if (c == '\n') + { + spos.x = 0; + spos.y += font.fLineHeight * scale.y; + } + else if (c == '\t') + { + spos.x += font.fTabWidth * scale.x; + } + else + { + Draw2D::Image(font.glyphs[c].imgGlyph, pos + spos, scale, col); + spos.x += glyph.vPropSize.x * scale.x; + } + } + + return vecGPUTasks.emplace_back(); +} + +olc::vf2d olc::Draw2D::GetTextSize(const std::string& text, const bool bProportional, const olc::vf2d& scale, olc::Font& font) +{ + olc::vf2d size = { 0, font.fLineHeight * scale.y }; + olc::vf2d pos = { 0, font.fLineHeight * scale.y }; + + for (auto c : text) + { + const auto& glyph = font.glyphs[c]; + + if (c == '\n') + { + pos.y += font.fLineHeight * scale.y; + pos.x = 0; + } + else if (c == '\t') + { + pos.x += font.fTabWidth * scale.x; + } + else + { + if(bProportional) + pos.x += glyph.vPropSize.x * scale.x; + else + pos.x += glyph.vMonoSize.x * scale.x; + } + + size = size.max(pos); + } + + return size; +} + +const GPUTask& olc::Draw2D::Image(olc::ImageRegion image, const olc::vf2d& pos, const olc::vf2d& scale, const olc::Pixel tint) { // Ensure source image is up to date in VRAM PrepareImageForHW(image.image); @@ -4170,14 +4435,14 @@ const GPUTask& olc::Draw2D::Image(olc::ImageRegion image, const olc::vf2d& pos, TaskTexturedPolygon( GPUTask::Structure::Fan, transformAffine.forward({ { 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 }, + { tint, tint, tint, tint }, // Tex coords are clockwise { image.coords[0], image.coords[1], image.coords[2], image.coords[3]}, &image.image )); } -const GPUTask& olc::Draw2D::ImageRotated(olc::ImageRegion image, const olc::vf2d& pos, const float theta, const olc::vf2d& center, const olc::vf2d& scale) +const GPUTask& olc::Draw2D::ImageRotated(olc::ImageRegion image, const olc::vf2d& pos, const float theta, const olc::vf2d& center, const olc::vf2d& scale, const olc::Pixel tint) { // Ensure source image is up to date in VRAM PrepareImageForHW(image.image); @@ -4199,13 +4464,13 @@ const GPUTask& olc::Draw2D::ImageRotated(olc::ImageRegion image, const olc::vf2d TaskTexturedPolygon( GPUTask::Structure::Fan, transformAffine.forward(vPoints), - { olc::Colour::WHITE, olc::Colour::WHITE, olc::Colour::WHITE, olc::Colour::WHITE }, + { tint, tint, tint, tint}, { image.coords[0], image.coords[1], image.coords[2], image.coords[3] }, &image.image )); } -const GPUTask& olc::Draw2D::ImageQuad(olc::ImageRegion image, const olc::vf2d& vTL, const olc::vf2d& vTR, const olc::vf2d& vBR, const olc::vf2d& vBL) +const GPUTask& olc::Draw2D::ImageQuad(olc::ImageRegion image, const olc::vf2d& vTL, const olc::vf2d& vTR, const olc::vf2d& vBR, const olc::vf2d& vBL, const olc::Pixel tint) { // Ensure source image is up to date in VRAM PrepareImageForHW(image.image); @@ -4242,7 +4507,7 @@ const GPUTask& olc::Draw2D::ImageQuad(olc::ImageRegion image, const olc::vf2d& v GPUTask::Structure::Fan, transformAffine.forward({ 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 }, + { tint, tint, tint, tint}, { image.coords[0] * q[0], image.coords[1] * q[1], image.coords[2] * q[2], image.coords[3] * q[3] }, &image.image )); @@ -4253,19 +4518,19 @@ const GPUTask& olc::Draw2D::ImageQuad(olc::ImageRegion image, const olc::vf2d& v TaskTexturedPolygon( GPUTask::Structure::Fan, transformAffine.forward({ vTL, vTR, vBR, vBL }), - { olc::Colour::WHITE, olc::Colour::WHITE, olc::Colour::WHITE, olc::Colour::WHITE }, + { tint, tint, tint, tint }, { image.coords[0], image.coords[1], image.coords[2], image.coords[3] }, &image.image )); } -const GPUTask& olc::Draw2D::ImageQuad(olc::ImageRegion image, const std::vector& vecPoints) +const GPUTask& olc::Draw2D::ImageQuad(olc::ImageRegion image, const std::vector& vecPoints, const olc::Pixel tint) { - return ImageQuad(image, vecPoints[0], vecPoints[1], vecPoints[2], vecPoints[3]); + return ImageQuad(image, vecPoints[0], vecPoints[1], vecPoints[2], vecPoints[3], tint); } -const GPUTask& olc::Draw2D::ImageRect(olc::ImageRegion image, const olc::vf2d& pos, const olc::vf2d& size) +const GPUTask& olc::Draw2D::ImageRect(olc::ImageRegion image, const olc::vf2d& pos, const olc::vf2d& size, const olc::Pixel tint) { // Ensure source image is up to date in VRAM PrepareImageForHW(image.image); @@ -4276,7 +4541,7 @@ const GPUTask& olc::Draw2D::ImageRect(olc::ImageRegion image, const olc::vf2d& p TaskTexturedPolygon( GPUTask::Structure::Fan, transformAffine.forward({ { 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 }, + { tint, tint, tint, tint }, // Tex coords are clockwise { image.coords[3], image.coords[2], image.coords[1], image.coords[0] }, &image.image @@ -4320,7 +4585,9 @@ namespace olc draw.SetGPU(pRenderer); draw.SetTarget(GetDefaultImage()); +#if OLC_MULTIWINDOW == OLC_MULTIWINDOW_YES pRenderer->RetargetDevice(pHost->GetHostWindowDescriptor(this)); +#endif pRenderer->ApplyDefaultShader(); @@ -4476,21 +4743,29 @@ namespace olc // Initialise Host Interface host = std::make_unique(); - +#if OLC_MULTIWINDOW == OLC_MULTIWINDOW_NO + // Create OS window on this thread + host->AddWindowFrame(this, { 30,30 }, config.vPixelSize * config.vScreenSize, false); + // Create EngineThread - no more windows will be created now. We needed one window + // at least to initialise teh rendering subsystem... sigh. coreActive = true; - //coreThread = std::thread(&PixelGameEngine::EngineThread, this); - - EngineThread(); - - // Gracefully terminate the main engine thread - //coreActive = false; - //coreThread.join(); - + coreThread = std::thread(&PixelGameEngine::EngineThread, this); + // Handle window events on this thread (and block) + host->StartSystemEventLoop(true); + // Window has closed its event handler, so shut down gracefully + coreActive = false; + // Wait for engine thread to terminate + coreThread.join(); +#else + +#endif + return true; } bool PixelGameEngine::AddChildWindow(std::shared_ptr window, const olc::vi2d& vScreenSize, const olc::vi2d& vPixelSize) { +#if OLC_MULTIWINDOW == OLC_MULTIWINDOW_YES // Link this olc::Window to a host resource if (host) { @@ -4503,8 +4778,11 @@ namespace olc window->Create(vScreenSize, vPixelSize); deqChildWindows.push_back(window); } - return true; +#else + // Can't create new windows + return false; +#endif } @@ -4515,12 +4793,12 @@ namespace olc timeFrame2 = std::chrono::steady_clock::now(); timeFrame1 = std::chrono::steady_clock::now(); - // Start the host system event loop in its own thread - //auto t1 = std::thread([this]() { host->StartSystemEventLoop(true); }); - - // Create Primary Window +#if OLC_MULTIWINDOW == OLC_MULTIWINDOW_YES + // Create Primary Window on EngineThread, event loop also exists for all windows + // on this thread, and all windows will be created on this thread host->AddWindowFrame(this, { 30,30 }, config.vPixelSize * config.vScreenSize, false); - +#endif + // Initialise ImageLoader Interface imageloader = std::make_unique(); @@ -4553,6 +4831,10 @@ namespace olc CreateImage(GetDefaultImage(), config.vScreenSize); + // Initialise Font System + olc::pgeguts::CreateClassicFont(this); + + draw.SetGPU(gpu.get()); gpu->ApplyDefaultShader(); draw.SetTarget(GetDefaultImage()); @@ -4577,7 +4859,10 @@ namespace olc while (coreActive) { - host->StartSystemEventLoop(); +#if OLC_MULTIWINDOW == OLC_MULTIWINDOW_YES + // Multiwindow system uses one event loop (non blocking) for all windows + host->StartSystemEventLoop(false); +#endif // Frame Delta Timing - "ElapsedTime" since last core update // ~~~~~~~~~~~~~~~~~~ @@ -4620,6 +4905,7 @@ namespace olc } else { +#if OLC_MULTIWINDOW == OLC_MULTIWINDOW_YES // Update Child Windows (if any) for (auto& winChild : deqChildWindows) winChild->olc_WindowUpdate(fDT); @@ -4638,6 +4924,7 @@ namespace olc } ), deqChildWindows.end()); } +#endif // Update Primary Window olc_WindowUpdate(fDT); @@ -4687,7 +4974,7 @@ namespace olc return pixels.data(); } - olc::Pixel& Image::Pixel(const olc::vf2d& pos) + olc::Pixel& Image::Pixel(const olc::vi2d& pos) { return pixels[int(pos.y) * dimensions.x + int(pos.x)]; } @@ -4727,7 +5014,7 @@ namespace olc 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) + 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 ); @@ -4749,6 +5036,98 @@ namespace olc #define PGE_IMAGE_IMPLEMENTED 1 #endif +#if defined(OLC_PGE3_APPLICATION) && !defined(PGE_FONT_IMPLEMENTED) +namespace olc +{ + namespace pgeguts + { + void CreateClassicFont(olc::PGEWindow* pge) + { + std::string data = + "?Q`0001oOch0o01o@F40o000000000" + "O000000nOT0063Qo4d8>?7a14Gno94AA4gno94AaOT0>o3`oO400o7QN00000400" + "Of80001oOg<7O7moBGT7O7lABET024@aBEd714AiOdl717a_=TH013Q>00000000" + "720D000V?V5oB3Q_HdUoE7a9@DdDE4A9@DmoE4A;Hg]oM4Aj8S4D84@`00000000" + "OaPT1000Oa`^13P1@AI[?g`1@A=[OdAoHgljA4Ao?WlBA7l1710007l100000000" + "ObM6000oOfMV?3QoBDD`O7a0BDDH@5A0BDD<@5A0BGeVO5ao@CQR?5Po00000000" + "Oc``000?Ogij70PO2D]??0Ph2DUM@7i`2DTg@7lh2GUj?0TO0C1870T?00000000" + "70<4001o?P<7?1QoHg43O;`h@GT0@:@LB@d0>:@hN@L0@?aoN@<0O7ao0000?000" + "OcH0001SOglLA7mg24TnK7ln24US>0PL24U140PnOgl0>7QgOcH0K71S0000A000" + "00H00000@Dm1S007@DUSg00?OdTnH7YhOfTL<7Yh@Cl0700?@Ah0300700000000" + "<008001QL00ZA41a@6HnI<1i@FHLM81M@@0LG81?O`0nC?Y7?`0ZA7Y300080000" + "O`082000Oh0827mo6>Hn?Wmo?6HnMb11MP08@C11H`08@FP0@@0004@000000000" + "00P00001Oab00003OcKP0006@6=PMgl<@440MglH@000000`@000001P00000000" + "Ob@8@@00Ob@8@Ga13R@8Mga172@8?PAo3R@827QoOb@820@0O`0007`0000007P0" + "O`000P08Od400g`<3V=P0G`673IP0`@3>1`00P@6O`P00g` vSpacing = { + 0x03,0x25,0x16,0x08,0x07,0x08,0x08,0x04,0x15,0x15,0x08,0x07,0x15,0x07,0x24,0x08, + 0x08,0x17,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x24,0x15,0x06,0x07,0x16,0x17, + 0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x17,0x08,0x08,0x17,0x08,0x08,0x08, + 0x08,0x08,0x08,0x08,0x17,0x08,0x08,0x08,0x08,0x17,0x08,0x15,0x08,0x15,0x08,0x08, + 0x24,0x18,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x33,0x17,0x17,0x33,0x18,0x17,0x17, + 0x17,0x17,0x17,0x17,0x07,0x17,0x17,0x18,0x18,0x17,0x17,0x07,0x33,0x07,0x08,0x00 }; + + + pge->CreateImage(fontClassicPGE.imgFont, { 128, 48 }); + + fontClassicPGE.imgFont.BindCPU(); + + int px = 0, py = 0; + for (size_t b = 0; b < 1024; b += 4) + { + uint32_t sym1 = (uint32_t)data[b + 0] - 48; + uint32_t sym2 = (uint32_t)data[b + 1] - 48; + uint32_t sym3 = (uint32_t)data[b + 2] - 48; + uint32_t sym4 = (uint32_t)data[b + 3] - 48; + uint32_t r = sym1 << 18 | sym2 << 12 | sym3 << 6 | sym4; + + for (int i = 0; i < 24; i++) + { + int k = r & (1 << i) ? 255 : 0; + fontClassicPGE.imgFont.Pixel({ px, py }) = olc::Pixel(k, k, k, k); + if (++py == 48) { px++; py = 0; } + } + } + + for (int c = 0; c < 256; c++) + { + if (c >= 32 && c < 128) + { + int i = c - 32; + + float fSpace = float(vSpacing[i] >> 4); + float fWidth = float(vSpacing[i] & 15); + + olc::vf2d vPos = + { float((i % 16) * 8) + fSpace, float((i / 16) * 8) }; + + olc::vf2d vSize = + { fWidth, 8.0f }; + + FontGlyph glyph{ + fontClassicPGE.imgFont.region(vPos, vSize), + fSpace, + vSize, + { 8.0f, 8.0f }, + }; + fontClassicPGE.glyphs.push_back(glyph); + } + else + fontClassicPGE.glyphs.push_back(FontGlyph{ fontClassicPGE.imgFont.region({0,0}, {8,8}) , 8.0f, {8.0f, 8.0f} }); + } + + fontClassicPGE.fLineHeight = 10.0f; + fontClassicPGE.fTabWidth = 32.0f; + } + } +} +#define PGE_FONT_IMPLEMENTED 1 +#endif + #if defined(OLC_PGE3_APPLICATION) && !defined(PGE_HW_MOUSE_IMPLEMENTED) namespace olc::hw { diff --git a/dev/src/api_opengl.cpp b/dev/src/api_opengl.cpp index 51dc1381..a168a720 100644 --- a/dev/src/api_opengl.cpp +++ b/dev/src/api_opengl.cpp @@ -195,6 +195,12 @@ namespace olc::apis::opengl CheckError(); } + void gl::glHint(GLenum target, GLenum mode) + { + ::glHint(target, mode); + CheckError(); + } + GLuint gl::glCreateShader(GLenum type) { return _glCreateShader(type); diff --git a/dev/src/api_opengl.h b/dev/src/api_opengl.h index 13b36ea8..738637a9 100644 --- a/dev/src/api_opengl.h +++ b/dev/src/api_opengl.h @@ -213,7 +213,7 @@ namespace olc void glBlendFunc(GLenum sfactor, GLenum dfactor); void glDepthFunc(GLenum func); void glGetTexImage(GLenum target, GLint level, GLenum format, GLenum type, void* pixels); - + void glHint(GLenum target, GLenum mode); private: bool CheckError(const std::source_location loc = std::source_location::current()); diff --git a/dev/src/draw2d.cpp b/dev/src/draw2d.cpp index eb22838f..b235be0b 100644 --- a/dev/src/draw2d.cpp +++ b/dev/src/draw2d.cpp @@ -301,42 +301,97 @@ const GPUTask& olc::Draw2D::FilledRect(const olc::vf2d& pos, const olc::vf2d& si )); } -const GPUTask& olc::Draw2D::String(const olc::vf2d& pos, const std::string& text, olc::Font& font) +const GPUTask& olc::Draw2D::String(const olc::vf2d& pos, const std::string& text, const olc::Pixel col, const olc::vf2d& scale, olc::Font& font) { PrepareTargetForHW(); olc::vf2d spos = { 0.0f, 0.0f }; - //olc::vf2d size = image.regionsize * scale; for (auto c : text) { + const auto& glyph = font.glyphs[c]; + if (c == '\n') { - spos.x = 0; spos.y += 8.0f * 1.0f; + spos.x = 0; + spos.y += font.fLineHeight * scale.y; } else if (c == '\t') { - spos.x += 8.0f * float(4) * 1.0f; + spos.x += font.fTabWidth * scale.x; } else { - int32_t ox = (c - 32) % 16; - int32_t oy = (c - 32) / 16; - Image(font.glyphs[c].imgGlyph, pos + spos); - + Draw2D::Image(font.glyphs[c].imgGlyph, pos + spos, scale, col); + spos.x += glyph.vMonoSize.x * scale.x; + } + } + return vecGPUTasks.emplace_back(); +} +const GPUTask& olc::Draw2D::StringProp(const olc::vf2d& pos, const std::string& text, const olc::Pixel col, const olc::vf2d& scale, olc::Font& font) +{ + PrepareTargetForHW(); + olc::vf2d spos = { 0.0f, 0.0f }; + for (auto c : text) + { + const auto& glyph = font.glyphs[c]; - spos.x += 8.0f * 1.0f; + if (c == '\n') + { + spos.x = 0; + spos.y += font.fLineHeight * scale.y; + } + else if (c == '\t') + { + spos.x += font.fTabWidth * scale.x; + } + else + { + Draw2D::Image(font.glyphs[c].imgGlyph, pos + spos, scale, col); + spos.x += glyph.vPropSize.x * scale.x; } } return vecGPUTasks.emplace_back(); } -const GPUTask& olc::Draw2D::Image(olc::ImageRegion image, const olc::vf2d& pos, const olc::vf2d& scale) +olc::vf2d olc::Draw2D::GetTextSize(const std::string& text, const bool bProportional, const olc::vf2d& scale, olc::Font& font) +{ + olc::vf2d size = { 0, font.fLineHeight * scale.y }; + olc::vf2d pos = { 0, font.fLineHeight * scale.y }; + + for (auto c : text) + { + const auto& glyph = font.glyphs[c]; + + if (c == '\n') + { + pos.y += font.fLineHeight * scale.y; + pos.x = 0; + } + else if (c == '\t') + { + pos.x += font.fTabWidth * scale.x; + } + else + { + if(bProportional) + pos.x += glyph.vPropSize.x * scale.x; + else + pos.x += glyph.vMonoSize.x * scale.x; + } + + size = size.max(pos); + } + + return size; +} + +const GPUTask& olc::Draw2D::Image(olc::ImageRegion image, const olc::vf2d& pos, const olc::vf2d& scale, const olc::Pixel tint) { // Ensure source image is up to date in VRAM PrepareImageForHW(image.image); @@ -349,14 +404,14 @@ const GPUTask& olc::Draw2D::Image(olc::ImageRegion image, const olc::vf2d& pos, TaskTexturedPolygon( GPUTask::Structure::Fan, transformAffine.forward({ { 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 }, + { tint, tint, tint, tint }, // Tex coords are clockwise { image.coords[0], image.coords[1], image.coords[2], image.coords[3]}, &image.image )); } -const GPUTask& olc::Draw2D::ImageRotated(olc::ImageRegion image, const olc::vf2d& pos, const float theta, const olc::vf2d& center, const olc::vf2d& scale) +const GPUTask& olc::Draw2D::ImageRotated(olc::ImageRegion image, const olc::vf2d& pos, const float theta, const olc::vf2d& center, const olc::vf2d& scale, const olc::Pixel tint) { // Ensure source image is up to date in VRAM PrepareImageForHW(image.image); @@ -378,13 +433,13 @@ const GPUTask& olc::Draw2D::ImageRotated(olc::ImageRegion image, const olc::vf2d TaskTexturedPolygon( GPUTask::Structure::Fan, transformAffine.forward(vPoints), - { olc::Colour::WHITE, olc::Colour::WHITE, olc::Colour::WHITE, olc::Colour::WHITE }, + { tint, tint, tint, tint}, { image.coords[0], image.coords[1], image.coords[2], image.coords[3] }, &image.image )); } -const GPUTask& olc::Draw2D::ImageQuad(olc::ImageRegion image, const olc::vf2d& vTL, const olc::vf2d& vTR, const olc::vf2d& vBR, const olc::vf2d& vBL) +const GPUTask& olc::Draw2D::ImageQuad(olc::ImageRegion image, const olc::vf2d& vTL, const olc::vf2d& vTR, const olc::vf2d& vBR, const olc::vf2d& vBL, const olc::Pixel tint) { // Ensure source image is up to date in VRAM PrepareImageForHW(image.image); @@ -421,7 +476,7 @@ const GPUTask& olc::Draw2D::ImageQuad(olc::ImageRegion image, const olc::vf2d& v GPUTask::Structure::Fan, transformAffine.forward({ 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 }, + { tint, tint, tint, tint}, { image.coords[0] * q[0], image.coords[1] * q[1], image.coords[2] * q[2], image.coords[3] * q[3] }, &image.image )); @@ -432,19 +487,19 @@ const GPUTask& olc::Draw2D::ImageQuad(olc::ImageRegion image, const olc::vf2d& v TaskTexturedPolygon( GPUTask::Structure::Fan, transformAffine.forward({ vTL, vTR, vBR, vBL }), - { olc::Colour::WHITE, olc::Colour::WHITE, olc::Colour::WHITE, olc::Colour::WHITE }, + { tint, tint, tint, tint }, { image.coords[0], image.coords[1], image.coords[2], image.coords[3] }, &image.image )); } -const GPUTask& olc::Draw2D::ImageQuad(olc::ImageRegion image, const std::vector& vecPoints) +const GPUTask& olc::Draw2D::ImageQuad(olc::ImageRegion image, const std::vector& vecPoints, const olc::Pixel tint) { - return ImageQuad(image, vecPoints[0], vecPoints[1], vecPoints[2], vecPoints[3]); + return ImageQuad(image, vecPoints[0], vecPoints[1], vecPoints[2], vecPoints[3], tint); } -const GPUTask& olc::Draw2D::ImageRect(olc::ImageRegion image, const olc::vf2d& pos, const olc::vf2d& size) +const GPUTask& olc::Draw2D::ImageRect(olc::ImageRegion image, const olc::vf2d& pos, const olc::vf2d& size, const olc::Pixel tint) { // Ensure source image is up to date in VRAM PrepareImageForHW(image.image); @@ -455,7 +510,7 @@ const GPUTask& olc::Draw2D::ImageRect(olc::ImageRegion image, const olc::vf2d& p TaskTexturedPolygon( GPUTask::Structure::Fan, transformAffine.forward({ { 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 }, + { tint, tint, tint, tint }, // Tex coords are clockwise { image.coords[3], image.coords[2], image.coords[1], image.coords[0] }, &image.image diff --git a/dev/src/draw2d.h b/dev/src/draw2d.h index ae7010ce..29a3d89a 100644 --- a/dev/src/draw2d.h +++ b/dev/src/draw2d.h @@ -56,7 +56,7 @@ namespace olc olc::vf2d WorldToScreen(const olc::vf2d& v) const; olc::vf2d ScreenToWorld(const olc::vf2d& v) const; - public: + public: // Primitive Drawing Functions // Plot a single pixel void Pixel( const olc::vf2d& pos, @@ -70,7 +70,7 @@ namespace olc // Clear entire draw target to specific colour void Clear(const olc::Pixel& col); - public: + public: // Shape Drawing Functions // Draws a single pixel wide line const GPUTask& Line( const olc::vf2d& p1, @@ -134,17 +134,36 @@ namespace olc //ShadedPolygon //TexturedPolygon + public: // Text Drawing Functions + // Draws a string at specified location in monospace font const GPUTask& String( const olc::vf2d& pos, const std::string& text, + const olc::Pixel col = olc::Colour::WHITE, + const olc::vf2d& scale = { 1.0f, 1.0f }, olc::Font& font = olc::fontClassicPGE); - + // Draws a string at specified location in proportional font + const GPUTask& StringProp( + const olc::vf2d& pos, + const std::string& text, + const olc::Pixel col = olc::Colour::WHITE, + const olc::vf2d& scale = { 1.0f, 1.0f }, + olc::Font& font = olc::fontClassicPGE); + + olc::vf2d GetTextSize( + const std::string& text, + const bool bProportional = false, + const olc::vf2d& scale = { 1.0f, 1.0f }, + olc::Font& font = olc::fontClassicPGE); + + public: // Image Drawing Functions // Draws a scaled image at specified location const GPUTask& Image( olc::ImageRegion image, const olc::vf2d& pos, - const olc::vf2d& scale = { 1.0f, 1.0f }); + const olc::vf2d& scale = { 1.0f, 1.0f }, + const olc::Pixel tint = olc::Colour::WHITE); // Draws an image rotated around a point at specified location const GPUTask& ImageRotated( @@ -152,7 +171,8 @@ namespace olc const olc::vf2d& pos, const float theta, const olc::vf2d& center = { 0.0f, 0.0f }, - const olc::vf2d& scale = { 1.0f, 1.0f }); + const olc::vf2d& scale = { 1.0f, 1.0f }, + const olc::Pixel tint = olc::Colour::WHITE); // Draws an image warped correctly to linearly fill a quadrilateral (formerly DrawWarped...) const GPUTask& ImageQuad( @@ -160,16 +180,21 @@ namespace olc const olc::vf2d& vTL, const olc::vf2d& vTR, const olc::vf2d& vBR, - const olc::vf2d& vBL); + const olc::vf2d& vBL, + const olc::Pixel tint = olc::Colour::WHITE); // Draws an image warped correctly to linearly fill a quadrilateral (formerly DrawWarped...) const GPUTask& ImageQuad( olc::ImageRegion image, - const std::vector& vecPoints); - + const std::vector& vecPoints, + const olc::Pixel tint = olc::Colour::WHITE); // Draws an image scaled to a specified rectangular area - const GPUTask& ImageRect(olc::ImageRegion image, const olc::vf2d& pos, const olc::vf2d& size); + const GPUTask& ImageRect( + olc::ImageRegion image, + const olc::vf2d& pos, + const olc::vf2d& size, + const olc::Pixel tint = olc::Colour::WHITE); diff --git a/dev/src/font.cpp b/dev/src/font.cpp index 8116a8bd..38074825 100644 --- a/dev/src/font.cpp +++ b/dev/src/font.cpp @@ -5,30 +5,6 @@ //! START IMPLEMENTATION namespace olc { - - - /*void Font::CreateFontSheet(const olc::Image& imgFont, const std::array& glyphs) - { - imgFontSheet = imgFont; - allGlyphs = glyphs; - }*/ - - /*bool Font::LoadFontSheet(imload::ImageLoader* imload, const std::string& sImageFile, const std::array& glyphs) - { - if (imload->CreateImageFromFile(imgFontSheet, sImageFile)) - { - allGlyphs = glyphs; - return true; - } - - return false; - }*/ - - /*olc::FontGlyph Font::GetGlyph(const char c) - { - return allGlyphs[uint8_t(c)]; - }*/ - namespace pgeguts { void CreateClassicFont(olc::PGEWindow* pge) @@ -51,12 +27,20 @@ namespace olc "O`000P08Od400g`<3V=P0G`673IP0`@3>1`00P@6O`P00g`CreateImage(fontClassicPGE.imgFontSheet, { 128, 48 }); + // Pixels Space, then Pixels Width + constexpr std::array vSpacing = { + 0x03,0x25,0x16,0x08,0x07,0x08,0x08,0x04,0x15,0x15,0x08,0x07,0x15,0x07,0x24,0x08, + 0x08,0x17,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x24,0x15,0x06,0x07,0x16,0x17, + 0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x17,0x08,0x08,0x17,0x08,0x08,0x08, + 0x08,0x08,0x08,0x08,0x17,0x08,0x08,0x08,0x08,0x17,0x08,0x15,0x08,0x15,0x08,0x08, + 0x24,0x18,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x33,0x17,0x17,0x33,0x18,0x17,0x17, + 0x17,0x17,0x17,0x17,0x07,0x17,0x17,0x18,0x18,0x17,0x17,0x07,0x33,0x07,0x08,0x00 }; + + + pge->CreateImage(fontClassicPGE.imgFont, { 128, 48 }); - fontClassicPGE.imgFontSheet.BindCPU(); + fontClassicPGE.imgFont.BindCPU(); int px = 0, py = 0; for (size_t b = 0; b < 1024; b += 4) @@ -70,33 +54,40 @@ namespace olc for (int i = 0; i < 24; i++) { int k = r & (1 << i) ? 255 : 0; - fontClassicPGE.imgFontSheet.Pixel({ px, py }) = olc::Pixel(k, k, k, k); + fontClassicPGE.imgFont.Pixel({ px, py }) = olc::Pixel(k, k, k, k); if (++py == 48) { px++; py = 0; } } } - // Monospace font, 8x8 pixels per character for (int c = 0; c < 256; c++) { if (c >= 32 && c < 128) { + int i = c - 32; + + float fSpace = float(vSpacing[i] >> 4); + float fWidth = float(vSpacing[i] & 15); + + olc::vf2d vPos = + { float((i % 16) * 8) + fSpace, float((i / 16) * 8) }; + + olc::vf2d vSize = + { fWidth, 8.0f }; + FontGlyph glyph{ - fontClassicPGE.imgFontSheet.region( - { float(((c - 32) % 16) * 8), float(((c - 32) / 16) * 8) }, - { 8.0f, 8.0f } - ), - { 8.0f, 8.0f } + fontClassicPGE.imgFont.region(vPos, vSize), + fSpace, + vSize, + { 8.0f, 8.0f }, }; fontClassicPGE.glyphs.push_back(glyph); } else - fontClassicPGE.glyphs.push_back(FontGlyph{ fontClassicPGE.imgFontSheet.region({0,0}, {8,8}) , {8.0f, 8.0f} }); - + fontClassicPGE.glyphs.push_back(FontGlyph{ fontClassicPGE.imgFont.region({0,0}, {8,8}) , 8.0f, {8.0f, 8.0f} }); } - - //fontClassicPGE.imgFontSheet = img; - + fontClassicPGE.fLineHeight = 10.0f; + fontClassicPGE.fTabWidth = 32.0f; } } } diff --git a/dev/src/font.h b/dev/src/font.h index 6962ca3e..5d16622c 100644 --- a/dev/src/font.h +++ b/dev/src/font.h @@ -25,8 +25,16 @@ namespace olc struct FontGlyph { + // Region of an olc::Image that contains the glyph + // in bitmap format. Typically WHITE on TRANSPARENT... + // ...though have fun with crazy alternatives :P olc::ImageRegion imgGlyph; - olc::vf2d vKerning; + // Leading spacing in pixels before glyph + float spacing; + // Size of the glyph in pixels + olc::vf2d vPropSize; + // Size of the glyph in monospace format + olc::vf2d vMonoSize; }; class Font @@ -35,20 +43,23 @@ namespace olc Font() = default; ~Font() = default; - //void CreateFontSheet(const olc::Image& imgFont, const std::array& glyphs); - //bool LoadFontSheet(imload::ImageLoader* imload, const std::string& sImageFile, const std::array& glyphs); - //FontGlyph GetGlyph(const char c); - public: - olc::Image imgFontSheet; + // Image that contains all glyphs + olc::Image imgFont; + // All glyphs in the font std::vector glyphs; - olc::vf2d vCharSize; + // Line height in pixels + float fLineHeight = 10.0f; + // Tab width in pixels + float fTabWidth = 32.0f; }; + // Predefined classic PGE font inline olc::Font fontClassicPGE; namespace pgeguts { + // Create the classic PGE font void CreateClassicFont(olc::PGEWindow* pge); } diff --git a/dev/src/gpu_opengl33.cpp b/dev/src/gpu_opengl33.cpp index 290cd7ce..2786a4b0 100644 --- a/dev/src/gpu_opengl33.cpp +++ b/dev/src/gpu_opengl33.cpp @@ -273,8 +273,9 @@ namespace olc::gpu // Unbind the FBO gl.glBindFramebuffer(36160U, 0); - glEnable(GL_TEXTURE_2D); // Turn on texturing - glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); + gl.glEnable(GL_TEXTURE_2D); // Turn on texturing + gl.glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); + gl.glEnable(GL_BLEND); lastError = RendererError::None; return true; diff --git a/dev/src/sh_template.h b/dev/src/sh_template.h index 955a3096..2455bcd4 100644 --- a/dev/src/sh_template.h +++ b/dev/src/sh_template.h @@ -1,12 +1,87 @@ #pragma once -// olc::PixelGameEngine 3.0 -// OneLoneCoder 2025 - // DO NOT EDIT THIS FILE! // This file is automatically generated. Changes to source should be made // through relevant component source file. +/* + olcPixelGameEngine3.h + + +-------------------------------------------------------------+ + | OneLoneCoder Pixel Game Engine v3.00 | + | "What do you need? Pixels... Lots of Pixels..." - javidx9 | + +-------------------------------------------------------------+ + + What is this? + ~~~~~~~~~~~~~ + olc::PixelGameEngine is a single file, cross platform graphics and userinput + framework used for games, visualisations, algorithm exploration and learning. + It was developed by YouTuber "javidx9" as an assistive tool for many of his + videos. The goal of this project is to provide high speed graphics with + minimal project setup complexity, to encourage new programmers, younger people, + and anyone else that wants to make fun things. + + However, olc::PixelGameEngine is not a toy! It is a powerful and fast utility + capable of delivering high resolution, high speed, high quality applications + which behave the same way regardless of the operating system or platform. + + This file provides the core utility set of the olc::PixelGameEngine, including + window creation, keyboard/mouse input, main game thread, timing, pixel drawing + routines, image/sprite loading and drawing routines, and a bunch of utility + types to make rapid development of games/visualisations possible. + + + License (OLC-3) + ~~~~~~~~~~~~~~~ + + Copyright 2018 - 2025 OneLoneCoder.com + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions or derivations of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions or derivative works in binary form must reproduce the above + copyright notice. This list of conditions and the following disclaimer must be + reproduced in the documentation and/or other materials provided with the distribution. + + 3. Neither the name of the copyright holder nor the names of its contributors may + be used to endorse or promote products derived from this software without specific + prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. + + TLDR: + You can do whatever you want with this code. Your only obligation is to credit in a + visible way to your end users in your running program that you used this code or a + derivative of it. If you don't edit this file, you are automatically in compliance with + OLC-3, as the message "OneLoneCoder.com" is present in the output window title bar. If + you compile to full screen, or remove the title bar, then you must display somewhere + something like "Powered by olcPixelGameEngine v3.00 - OneLoneCoder.com". That's it. + + Links + ~~~~~ + YouTube: https://www.youtube.com/javidx9 + https://www.youtube.com/javidx9extra + Discord: https://discord.gg/WhwHUMV + X: https://www.x.com/javidx9 + Twitch: https://www.twitch.tv/javidx9 + GitHub: https://www.github.com/onelonecoder + Homepage: https://www.onelonecoder.com + Patreon: https://www.patreon.com/javidx9 +*/ + + //! GRAB GLOBAL STDHEADER //! GRAB config.h CONFIGURATION @@ -21,6 +96,8 @@ //! GRAB image.h DECLARATION +//! GRAB font.h DECLARATION + //! GRAB imload_iface.h DECLARATION //! GRAB gputask.h DECLARATION @@ -46,6 +123,8 @@ + + #if OLC_HOST == OLC_HOST_WINDOWS //! GRAB host_win_winapi.h WINAPI_CONFIG @@ -104,6 +183,11 @@ #define PGE_IMAGE_IMPLEMENTED 1 #endif +#if defined(OLC_PGE3_APPLICATION) && !defined(PGE_FONT_IMPLEMENTED) +//! GRAB font.cpp IMPLEMENTATION +#define PGE_FONT_IMPLEMENTED 1 +#endif + #if defined(OLC_PGE3_APPLICATION) && !defined(PGE_HW_MOUSE_IMPLEMENTED) //! GRAB hw_mouse.cpp IMPLEMENTATION #define PGE_HW_MOUSE_IMPLEMENTED 1 diff --git a/dev/tests/test_mh.cpp b/dev/tests/test_mh.cpp index 079f298b..f619696a 100644 --- a/dev/tests/test_mh.cpp +++ b/dev/tests/test_mh.cpp @@ -228,11 +228,22 @@ class Example : public olc::PixelGameEngine draw.Image(imSampleTest, vMouse); - draw.Image(olc::fontClassicPGE.imgFontSheet, vMouse); + draw.Image(olc::fontClassicPGE.imgFont, vMouse); - draw.String({ 10.0f, 10.0f }, "Hello World!"); + std::string sTest = "Hello World!\nThe quick brown fox JUMPS over the LaZy dog..."; + olc::vf2d vSizeMono = draw.GetTextSize(sTest, false, { 1, 2 }); + olc::vf2d vSizeProp = draw.GetTextSize(sTest, true, { 1, 2 }); + draw.String({ 10.0f, 10.0f }, "Hello World!\nThe quick brown fox JUMPS over the LaZy dog...", olc::Colour::WHITE, { 1, 2 }); + draw.Rect({ 10.0f, 10.0f }, vSizeMono, olc::Colour::GREEN); + + draw.StringProp({ 10.0f, 100.0f }, "Hello World!\nThe quick brown fox JUMPS over the LaZy dog...", olc::Colour::BLUE, { 1, 2 }); + draw.Rect({ 10.0f, 100.0f }, vSizeProp, olc::Colour::GREEN); + + draw.StringProp({ 10.0f, 200.0f }, "Hello World!\nThe quick brown fox JUMPS over the LaZy dog...", olc::PixelF(0.5f, 0.0f, 0.0f, 0.25f), {1, 2}); + draw.Rect({ 10.0f, 200.0f }, vSizeProp, olc::Colour::GREEN); + //draw.Pixel(vMouse, olc::Colour::GREEN); //draw.Line(vMouse, vMouse + 1, olc::Colour::GREEN); From e1865e4a7ea97db60b962cade58b9f1d658ad0d7 Mon Sep 17 00:00:00 2001 From: Javidx9 <25419386+OneLoneCoder@users.noreply.github.com> Date: Sun, 7 Dec 2025 12:55:34 +0000 Subject: [PATCH 10/17] gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 7dcb3309..ab59e71c 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,4 @@ /dev/msvc/x64/Debug/olcPGE3.tlog /dev/msvc/.vs/CopilotSnapshots/48318E23BE80F04A9ED976A29E9EEE0D /dev/msvc/olcPGE3_BuildSH/x64/Release +/dev/msvc/.vs/CopilotSnapshots/7B23092673724F4A930E627F3C0D78FA From b7864ad80673c3b6a21f7c381a0d1891c193d7f4 Mon Sep 17 00:00:00 2001 From: Javidx9 <25419386+OneLoneCoder@users.noreply.github.com> Date: Sun, 7 Dec 2025 12:56:39 +0000 Subject: [PATCH 11/17] Fonts working, performance is not great, needs to be batch issued to GPU, but otherwise happy. "Fixed" a texture sampling bug, no doubt imapcts all sorts of "working" things... --- .../olcPGE3_BuildSH/olcPixelGameEngine3.h | 83 +++++++++++++++++-- dev/src/config.h | 2 +- dev/src/core.cpp | 10 +++ dev/src/core.h | 5 +- dev/src/draw2d.cpp | 2 +- dev/src/gpu_opengl33.cpp | 48 +++++++++-- dev/src/gpu_opengl33.h | 3 + dev/src/image.cpp | 10 ++- dev/src/image.h | 2 + dev/src/transform2d.h | 14 ++++ dev/src/vector2d.h | 6 ++ 11 files changed, 166 insertions(+), 19 deletions(-) diff --git a/dev/msvc/olcPGE3_BuildSH/olcPixelGameEngine3.h b/dev/msvc/olcPGE3_BuildSH/olcPixelGameEngine3.h index 1eb9b2fe..93593bf0 100644 --- a/dev/msvc/olcPGE3_BuildSH/olcPixelGameEngine3.h +++ b/dev/msvc/olcPGE3_BuildSH/olcPixelGameEngine3.h @@ -187,7 +187,7 @@ #endif -#define OLC_MOUSE_BUTTONS 3 +#define OLC_MOUSE_BUTTONS 5 #define OLC_GPU_MAX_VERTICES 8192 #define OLC_GPU_ERRORCHECK 1 @@ -1294,6 +1294,8 @@ namespace olc void SetGPUID(const int32_t id); // Get underlying vector of pixels std::vector& GetPixels(); + + void Resize(const olc::vi2d& size); bool BoundToGPU() const; bool BoundToCPU() const; @@ -2178,7 +2180,10 @@ namespace olc void LinkToImageLoader(olc::imload::ImageLoader* imload); + public: olc::Image& GetDefaultImage(); + olc::Draw2D& GetDraw(); + olc::hw::Mouse& GetMouse(); protected: @@ -2197,7 +2202,7 @@ namespace olc olc::imload::ImageLoader* pImageLoader = nullptr; }; - class PixelGameEngine : protected PGEWindow + class PixelGameEngine : public PGEWindow { public: PixelGameEngine(); @@ -2615,6 +2620,9 @@ namespace olc olc::Image imgBlank; olc::vf2d vTargetSize; + uint32_t nCurrentTextureTarget = 0; + uint32_t nCurrentTextureSource = 0; + }; } } @@ -3842,8 +3850,21 @@ namespace olc::gpu { auto& gl = olc::apis::opengl::gl::Get(); + // If the requested source texture is currently attached as the render target, + // unbind the framebuffer to avoid sampling from a texture that's being written to. + if (texid == nCurrentTextureTarget && texid != 0) + { + std::cout << "Warning ATS: Requested source is currently attached as target (" << texid << ") - unbinding FBO\n"; + gl.glBindFramebuffer(36160U, 0); + nCurrentTextureTarget = 0; + } + + if (nCurrentTextureSource == texid) + return true; + gl.glActiveTexture(0x84C0 + slot); // GL_TEXTURE0 gl.glBindTexture(GL_TEXTURE_2D, texid); + nCurrentTextureSource = texid; return true; } @@ -3851,21 +3872,40 @@ namespace olc::gpu { auto& gl = olc::apis::opengl::gl::Get(); + + // If the requested target texture is currently bound as a source, unbind it + // from all texture units to ensure we do not sample from a texture that's + // attached to the FBO (undefined behavior). + if (texid != 0 && texid == nCurrentTextureSource) + { + std::cout << "Warning ATT: Requested target is currently bound as source (" << texid << ") - unbinding texture units\n"; + // Unbind from a reasonable number of texture units (0..7) used by this renderer + for (int i = 0; i < 8; ++i) + { + gl.glActiveTexture(0x84C0 + i); + gl.glBindTexture(GL_TEXTURE_2D, 0); + } + nCurrentTextureSource = 0; + } + if (texid == 0) { + // Unbind the FBO (bind default framebuffer) gl.glBindFramebuffer(36160U, 0); return true; - } - + } + // Bind FBO gl.glBindFramebuffer(36160U, nDefaultFBO); - // Allocate target buffers + // Allocate target buffers - pick the single attachment corresponding to 'slot' 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); + GLenum draw = attachments[slot]; + gl.glDrawBuffers(1, &draw); + // Bind buffers to texture + gl.glFramebufferTexture2D(36160U, 36064U + slot, GL_TEXTURE_2D, texid, 0); + nCurrentTextureTarget = texid; return true; } @@ -4431,10 +4471,17 @@ const GPUTask& olc::Draw2D::Image(olc::ImageRegion image, const olc::vf2d& pos, olc::vf2d size = image.regionsize * scale; + auto quantisedPositions = transformAffine.forward({ { pos.x, pos.y }, { pos.x + size.x, pos.y }, { pos.x + size.x, pos.y + size.y }, { pos.x, pos.y + size.y } }); + std::transform(quantisedPositions.begin(), quantisedPositions.end(), quantisedPositions.begin(), + [](const olc::vf2d& v) { + return olc::vf2d(std::round(v.x), std::round(v.y)); + }); + return vecGPUTasks.emplace_back( TaskTexturedPolygon( GPUTask::Structure::Fan, - transformAffine.forward({ { pos.x, pos.y }, { pos.x + size.x, pos.y }, { pos.x + size.x, pos.y + size.y }, { pos.x, pos.y + size.y } }), + //transformAffine.forward({ { pos.x, pos.y }, { pos.x + size.x, pos.y }, { pos.x + size.x, pos.y + size.y }, { pos.x, pos.y + size.y } }), + quantisedPositions, { tint, tint, tint, tint }, // Tex coords are clockwise { image.coords[0], image.coords[1], image.coords[2], image.coords[3]}, @@ -4704,6 +4751,16 @@ namespace olc return imgPrimary; } + olc::Draw2D& PGEWindow::GetDraw() + { + return draw; + } + + olc::hw::Mouse& PGEWindow::GetMouse() + { + return mouse; + } + bool PGEWindow::olc_OnMouseMove(const olc::vi2d& vMousePos) { mouse.SetPosition(olc::vf2d(vMousePos) / olc::vf2d(GetWindowSize()) * GetDefaultImage().Size()); @@ -4999,6 +5056,13 @@ namespace olc return pixels; } + void Image::Resize(const olc::vi2d& size) + { + dimensions = size; + pixels.resize(dimensions.area(), olc::Colour::TANGERINE); + BindCPU(); + } + bool Image::BoundToGPU() const { return onGPU; @@ -5018,6 +5082,7 @@ namespace olc { auto i = 1.0f / this->Size(); return olc::ImageRegion(*this, vTL * i, vTR * i, vBL * i, vBR * i ); + //return olc::ImageRegion(*this, (vTL + olc::vf2d{0.5f, 0.5f}) * i, (vTR + olc::vf2d{ -0.5f, 0.5f })* i, (vBL + olc::vf2d{ 0.5f, -0.5f })* i, (vBR + olc::vf2d{ -0.5f, -0.5f })* i); } void Image::BindGPU() diff --git a/dev/src/config.h b/dev/src/config.h index 38540c70..7cefade6 100644 --- a/dev/src/config.h +++ b/dev/src/config.h @@ -88,7 +88,7 @@ #endif -#define OLC_MOUSE_BUTTONS 3 +#define OLC_MOUSE_BUTTONS 5 #define OLC_GPU_MAX_VERTICES 8192 #define OLC_GPU_ERRORCHECK 1 diff --git a/dev/src/core.cpp b/dev/src/core.cpp index f4ac40f2..f879abdd 100644 --- a/dev/src/core.cpp +++ b/dev/src/core.cpp @@ -157,6 +157,16 @@ namespace olc return imgPrimary; } + olc::Draw2D& PGEWindow::GetDraw() + { + return draw; + } + + olc::hw::Mouse& PGEWindow::GetMouse() + { + return mouse; + } + bool PGEWindow::olc_OnMouseMove(const olc::vi2d& vMousePos) { mouse.SetPosition(olc::vf2d(vMousePos) / olc::vf2d(GetWindowSize()) * GetDefaultImage().Size()); diff --git a/dev/src/core.h b/dev/src/core.h index 92855e08..4a40b81b 100644 --- a/dev/src/core.h +++ b/dev/src/core.h @@ -87,7 +87,10 @@ namespace olc void LinkToImageLoader(olc::imload::ImageLoader* imload); + public: olc::Image& GetDefaultImage(); + olc::Draw2D& GetDraw(); + olc::hw::Mouse& GetMouse(); protected: @@ -106,7 +109,7 @@ namespace olc olc::imload::ImageLoader* pImageLoader = nullptr; }; - class PixelGameEngine : protected PGEWindow + class PixelGameEngine : public PGEWindow { public: PixelGameEngine(); diff --git a/dev/src/draw2d.cpp b/dev/src/draw2d.cpp index b235be0b..7dba71ab 100644 --- a/dev/src/draw2d.cpp +++ b/dev/src/draw2d.cpp @@ -403,7 +403,7 @@ const GPUTask& olc::Draw2D::Image(olc::ImageRegion image, const olc::vf2d& pos, return vecGPUTasks.emplace_back( TaskTexturedPolygon( GPUTask::Structure::Fan, - transformAffine.forward({ { pos.x, pos.y }, { pos.x + size.x, pos.y }, { pos.x + size.x, pos.y + size.y }, { pos.x, pos.y + size.y } }), + transformAffine.forwardRound({ { pos.x, pos.y }, { pos.x + size.x, pos.y }, { pos.x + size.x, pos.y + size.y }, { pos.x, pos.y + size.y } }), { tint, tint, tint, tint }, // Tex coords are clockwise { image.coords[0], image.coords[1], image.coords[2], image.coords[3]}, diff --git a/dev/src/gpu_opengl33.cpp b/dev/src/gpu_opengl33.cpp index 2786a4b0..ca143ff1 100644 --- a/dev/src/gpu_opengl33.cpp +++ b/dev/src/gpu_opengl33.cpp @@ -408,8 +408,23 @@ namespace olc::gpu { auto& gl = olc::apis::opengl::gl::Get(); + // If the requested source texture is currently attached as the render target, + // unbind the framebuffer to avoid sampling from a texture that's being written to. + if (texid == nCurrentTextureTarget && texid != 0) + { +#if defined(OLC_GPU_DEBUG) + std::cout << "Warning ATS: Requested source is currently attached as target (" << texid << ") - unbinding FBO\n"; +#endif + gl.glBindFramebuffer(36160U, 0); + nCurrentTextureTarget = 0; + } + + if (nCurrentTextureSource == texid) + return true; + gl.glActiveTexture(0x84C0 + slot); // GL_TEXTURE0 gl.glBindTexture(GL_TEXTURE_2D, texid); + nCurrentTextureSource = texid; return true; } @@ -417,21 +432,42 @@ namespace olc::gpu { auto& gl = olc::apis::opengl::gl::Get(); + + // If the requested target texture is currently bound as a source, unbind it + // from all texture units to ensure we do not sample from a texture that's + // attached to the FBO (undefined behavior). + if (texid != 0 && texid == nCurrentTextureSource) + { +#if defined(OLC_GPU_DEBUG) + std::cout << "Warning ATT: Requested target is currently bound as source (" << texid << ") - unbinding texture units\n"; +#endif + // Unbind from a reasonable number of texture units (0..7) used by this renderer + for (int i = 0; i < 8; ++i) + { + gl.glActiveTexture(0x84C0 + i); + gl.glBindTexture(GL_TEXTURE_2D, 0); + } + nCurrentTextureSource = 0; + } + if (texid == 0) { + // Unbind the FBO (bind default framebuffer) gl.glBindFramebuffer(36160U, 0); return true; - } - + } + // Bind FBO gl.glBindFramebuffer(36160U, nDefaultFBO); - // Allocate target buffers + // Allocate target buffers - pick the single attachment corresponding to 'slot' 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); + GLenum draw = attachments[slot]; + gl.glDrawBuffers(1, &draw); + // Bind buffers to texture + gl.glFramebufferTexture2D(36160U, 36064U + slot, GL_TEXTURE_2D, texid, 0); + nCurrentTextureTarget = texid; return true; } diff --git a/dev/src/gpu_opengl33.h b/dev/src/gpu_opengl33.h index 26e9396d..cd29f201 100644 --- a/dev/src/gpu_opengl33.h +++ b/dev/src/gpu_opengl33.h @@ -72,6 +72,9 @@ namespace olc olc::Image imgBlank; olc::vf2d vTargetSize; + uint32_t nCurrentTextureTarget = 0; + uint32_t nCurrentTextureSource = 0; + }; } } diff --git a/dev/src/image.cpp b/dev/src/image.cpp index 9880e8db..a1102038 100644 --- a/dev/src/image.cpp +++ b/dev/src/image.cpp @@ -54,6 +54,13 @@ namespace olc return pixels; } + void Image::Resize(const olc::vi2d& size) + { + dimensions = size; + pixels.resize(dimensions.area(), olc::Colour::TANGERINE); + BindCPU(); + } + bool Image::BoundToGPU() const { return onGPU; @@ -72,7 +79,8 @@ namespace olc 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 ); + //return olc::ImageRegion(*this, vTL * i, vTR * i, vBL * i, vBR * i ); + return olc::ImageRegion(*this, (vTL + olc::vf2d{0.005f, 0.005f}) * i, (vTR + olc::vf2d{ -0.005f, 0.005f })* i, (vBL + olc::vf2d{ 0.005f, -0.005f })* i, (vBR + olc::vf2d{ -0.005f, -0.005f })* i); } void Image::BindGPU() diff --git a/dev/src/image.h b/dev/src/image.h index eef0c149..8d0f93e7 100644 --- a/dev/src/image.h +++ b/dev/src/image.h @@ -54,6 +54,8 @@ namespace olc void SetGPUID(const int32_t id); // Get underlying vector of pixels std::vector& GetPixels(); + + void Resize(const olc::vi2d& size); bool BoundToGPU() const; bool BoundToCPU() const; diff --git a/dev/src/transform2d.h b/dev/src/transform2d.h index 0fcd5777..3f881d11 100644 --- a/dev/src/transform2d.h +++ b/dev/src/transform2d.h @@ -55,6 +55,12 @@ namespace olc return m_mForward * v; } + template + inline constexpr auto forwardRound(const olc::v_2d& v) const + { + return (m_mForward * v).round(); + } + // Transform a vector of v_2d by this transform template inline constexpr auto forward(const std::vector>& v) const @@ -64,6 +70,14 @@ namespace olc return o; } + template + inline constexpr auto forwardRound(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).round(); }); + return o; + } + // Transform a vector by the inverse of this transform template inline constexpr auto inverse(const olc::v_2d& v) const diff --git a/dev/src/vector2d.h b/dev/src/vector2d.h index 01f671c1..3ca61440 100644 --- a/dev/src/vector2d.h +++ b/dev/src/vector2d.h @@ -95,6 +95,12 @@ namespace olc return v_2d(std::floor(x), std::floor(y)); } + // Rounds both components accurately + inline constexpr v_2d round() const + { + return v_2d(std::round(x), std::round(y)); + } + // Rounds both components up inline constexpr v_2d ceil() const { From 784b0d9c593c89b84de254876b29ec424538951e Mon Sep 17 00:00:00 2001 From: Javidx9 <25419386+OneLoneCoder@users.noreply.github.com> Date: Sun, 14 Dec 2025 19:11:42 +0000 Subject: [PATCH 12/17] working on interesting blending bug --- .../olcPGE3_BuildSH/olcPixelGameEngine3.h | 48 ++++++++++++++---- dev/src/config.h | 2 +- dev/src/gpu_opengl33.cpp | 13 ++++- dev/tests/blend.png | Bin 0 -> 3493 bytes dev/tests/test_mh.cpp | 17 +++++-- 5 files changed, 63 insertions(+), 17 deletions(-) create mode 100644 dev/tests/blend.png diff --git a/dev/msvc/olcPGE3_BuildSH/olcPixelGameEngine3.h b/dev/msvc/olcPGE3_BuildSH/olcPixelGameEngine3.h index 93593bf0..e626b0ec 100644 --- a/dev/msvc/olcPGE3_BuildSH/olcPixelGameEngine3.h +++ b/dev/msvc/olcPGE3_BuildSH/olcPixelGameEngine3.h @@ -190,7 +190,7 @@ #define OLC_MOUSE_BUTTONS 5 #define OLC_GPU_MAX_VERTICES 8192 -#define OLC_GPU_ERRORCHECK 1 +#define OLC_GPU_ERRORCHECK 0 #define LICENCE_DEFAULT "OneLoneCoder.com - Pixel Game Engine 3 - " @@ -582,6 +582,12 @@ namespace olc return v_2d(std::floor(x), std::floor(y)); } + // Rounds both components accurately + inline constexpr v_2d round() const + { + return v_2d(std::round(x), std::round(y)); + } + // Rounds both components up inline constexpr v_2d ceil() const { @@ -1091,6 +1097,12 @@ namespace olc return m_mForward * v; } + template + inline constexpr auto forwardRound(const olc::v_2d& v) const + { + return (m_mForward * v).round(); + } + // Transform a vector of v_2d by this transform template inline constexpr auto forward(const std::vector>& v) const @@ -1100,6 +1112,14 @@ namespace olc return o; } + template + inline constexpr auto forwardRound(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).round(); }); + return o; + } + // Transform a vector by the inverse of this transform template inline constexpr auto inverse(const olc::v_2d& v) const @@ -3854,7 +3874,9 @@ namespace olc::gpu // unbind the framebuffer to avoid sampling from a texture that's being written to. if (texid == nCurrentTextureTarget && texid != 0) { +#if defined(OLC_GPU_ERRORCHECK) && OLC_GPU_ERRORCHECK == 1 std::cout << "Warning ATS: Requested source is currently attached as target (" << texid << ") - unbinding FBO\n"; +#endif gl.glBindFramebuffer(36160U, 0); nCurrentTextureTarget = 0; } @@ -3878,7 +3900,9 @@ namespace olc::gpu // attached to the FBO (undefined behavior). if (texid != 0 && texid == nCurrentTextureSource) { +#if defined(OLC_GPU_ERRORCHECK) && OLC_GPU_ERRORCHECK == 1 std::cout << "Warning ATT: Requested target is currently bound as source (" << texid << ") - unbinding texture units\n"; +#endif // Unbind from a reasonable number of texture units (0..7) used by this renderer for (int i = 0; i < 8; ++i) { @@ -3897,6 +3921,9 @@ namespace olc::gpu // Bind FBO gl.glBindFramebuffer(36160U, nDefaultFBO); + + //gl.glEnable(GL_BLEND); + //gl.glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // Allocate target buffers - pick the single attachment corresponding to 'slot' std::array attachments = { { 36064U, 36065U, 36066U, 36067U, 36068U, 36069U, 36070U, 36071U } }; @@ -3905,6 +3932,9 @@ namespace olc::gpu // Bind buffers to texture gl.glFramebufferTexture2D(36160U, 36064U + slot, GL_TEXTURE_2D, texid, 0); + //glReadBuffer(36064U + slot); // GL_COLOR_ATTACHMENT0 + slot + + nCurrentTextureTarget = texid; return true; @@ -3990,6 +4020,9 @@ namespace olc::gpu //if (task.bDepth) // gl.glEnable(GL_DEPTH_TEST); + gl.glEnable(GL_BLEND); + //gl.glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + gl.glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); // Draw the thing! if (task.bWireframe) { @@ -4471,17 +4504,10 @@ const GPUTask& olc::Draw2D::Image(olc::ImageRegion image, const olc::vf2d& pos, olc::vf2d size = image.regionsize * scale; - auto quantisedPositions = transformAffine.forward({ { pos.x, pos.y }, { pos.x + size.x, pos.y }, { pos.x + size.x, pos.y + size.y }, { pos.x, pos.y + size.y } }); - std::transform(quantisedPositions.begin(), quantisedPositions.end(), quantisedPositions.begin(), - [](const olc::vf2d& v) { - return olc::vf2d(std::round(v.x), std::round(v.y)); - }); - return vecGPUTasks.emplace_back( TaskTexturedPolygon( GPUTask::Structure::Fan, - //transformAffine.forward({ { pos.x, pos.y }, { pos.x + size.x, pos.y }, { pos.x + size.x, pos.y + size.y }, { pos.x, pos.y + size.y } }), - quantisedPositions, + transformAffine.forwardRound({ { pos.x, pos.y }, { pos.x + size.x, pos.y }, { pos.x + size.x, pos.y + size.y }, { pos.x, pos.y + size.y } }), { tint, tint, tint, tint }, // Tex coords are clockwise { image.coords[0], image.coords[1], image.coords[2], image.coords[3]}, @@ -5081,8 +5107,8 @@ namespace olc 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 ); - //return olc::ImageRegion(*this, (vTL + olc::vf2d{0.5f, 0.5f}) * i, (vTR + olc::vf2d{ -0.5f, 0.5f })* i, (vBL + olc::vf2d{ 0.5f, -0.5f })* i, (vBR + olc::vf2d{ -0.5f, -0.5f })* i); + //return olc::ImageRegion(*this, vTL * i, vTR * i, vBL * i, vBR * i ); + return olc::ImageRegion(*this, (vTL + olc::vf2d{0.005f, 0.005f}) * i, (vTR + olc::vf2d{ -0.005f, 0.005f })* i, (vBL + olc::vf2d{ 0.005f, -0.005f })* i, (vBR + olc::vf2d{ -0.005f, -0.005f })* i); } void Image::BindGPU() diff --git a/dev/src/config.h b/dev/src/config.h index 7cefade6..2522c90e 100644 --- a/dev/src/config.h +++ b/dev/src/config.h @@ -91,7 +91,7 @@ #define OLC_MOUSE_BUTTONS 5 #define OLC_GPU_MAX_VERTICES 8192 -#define OLC_GPU_ERRORCHECK 1 +#define OLC_GPU_ERRORCHECK 0 #define LICENCE_DEFAULT "OneLoneCoder.com - Pixel Game Engine 3 - " diff --git a/dev/src/gpu_opengl33.cpp b/dev/src/gpu_opengl33.cpp index ca143ff1..08da787f 100644 --- a/dev/src/gpu_opengl33.cpp +++ b/dev/src/gpu_opengl33.cpp @@ -412,7 +412,7 @@ namespace olc::gpu // unbind the framebuffer to avoid sampling from a texture that's being written to. if (texid == nCurrentTextureTarget && texid != 0) { -#if defined(OLC_GPU_DEBUG) +#if defined(OLC_GPU_ERRORCHECK) && OLC_GPU_ERRORCHECK == 1 std::cout << "Warning ATS: Requested source is currently attached as target (" << texid << ") - unbinding FBO\n"; #endif gl.glBindFramebuffer(36160U, 0); @@ -438,7 +438,7 @@ namespace olc::gpu // attached to the FBO (undefined behavior). if (texid != 0 && texid == nCurrentTextureSource) { -#if defined(OLC_GPU_DEBUG) +#if defined(OLC_GPU_ERRORCHECK) && OLC_GPU_ERRORCHECK == 1 std::cout << "Warning ATT: Requested target is currently bound as source (" << texid << ") - unbinding texture units\n"; #endif // Unbind from a reasonable number of texture units (0..7) used by this renderer @@ -459,6 +459,9 @@ namespace olc::gpu // Bind FBO gl.glBindFramebuffer(36160U, nDefaultFBO); + + //gl.glEnable(GL_BLEND); + //gl.glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // Allocate target buffers - pick the single attachment corresponding to 'slot' std::array attachments = { { 36064U, 36065U, 36066U, 36067U, 36068U, 36069U, 36070U, 36071U } }; @@ -467,6 +470,9 @@ namespace olc::gpu // Bind buffers to texture gl.glFramebufferTexture2D(36160U, 36064U + slot, GL_TEXTURE_2D, texid, 0); + //glReadBuffer(36064U + slot); // GL_COLOR_ATTACHMENT0 + slot + + nCurrentTextureTarget = texid; return true; @@ -552,6 +558,9 @@ namespace olc::gpu //if (task.bDepth) // gl.glEnable(GL_DEPTH_TEST); + gl.glEnable(GL_BLEND); + //gl.glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + gl.glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); // Draw the thing! if (task.bWireframe) { diff --git a/dev/tests/blend.png b/dev/tests/blend.png new file mode 100644 index 0000000000000000000000000000000000000000..912ff4319e5f5e7ae210426b5b32df4974da0837 GIT binary patch literal 3493 zcmcgvdpwkR7k|dBTx;V+7d6dBD(03E!?+E}D;Xpc7GvhY7>s5HW0HoVwTX1GnuxBv ztwho)tPz)`<_A+WPub8Xb;^41Qw0O8Dp@<7?Zsi93G9qo0x1vL$3w0 z3zJ3*;a~(CQWgP$!J7RE-T!y! zoOchXr63pcCS?`HJB3q%K`%Ou6S8P+5!ekz1TR5kl?jWIN!v4m4%HGn7sA{o@*+k+ z?7*Ua=V<>DNY*TH%Q*?*chT7#CM$OC(R>GafCuJR^UE;$TK-*wQI|PJnP!Ju8G!{;hbrBp3$3p!+UxI}#F(Dvi?oXAR z7YXW9R47>^{}z@dYDwUhbA;b@KD2NC88lI#(S%x_Y(+QnNd4*;+P zI;4dq8UO&pjHrxiXFE47k#(rs8IVLI9yyAmXW6#4Q;-lRHU($!>*4}@`W6V8&WS%bDWe7+#D8bj0dvS$V$_k4s*Lj(iUYmL@ zic2A6oL6n^+*Z0QXQl0w*}1@MpjHjLHC}gxl@iS{Cyr@;IMef?XWchikI5Sy1eOtQ z?N@kqk}I#2SFctHnw=fu!n&R^@;0@ zr(MV5Q(}y!fsNaO>q9(5Jm^PAIda#2HUOw<%5E4S$kqh_rEi_=ZTE7s2XcJUx^}K~ zK~dwMC)eAduK{@{&@~lkT<$ zFUlwTM!H}I#;4O~6q`hWuv65(x}NwS{K>k19%GDFp+_RIqqI$)nCAtG zCDmu!M{XT9)0Q53i%~d~LWP<3W$T3w-F0a!(TJMLJu7Z%xYklBt(izrEYBf?O1%nf zdm8FD!U~l0UdRc62($w4i?OhY%I0;R*)Pcau7II-N>{+i1ZDVXVU|2lHd;D4*cf}r zY*5iPYl{p&p5C>hRd~~_V%E6+iExjpK>{H;VB}eM${-=&m9jv*%1Ag`NRqF-f527f zXTz4$6L;L|5a4=y&W=yAn*IDr`>bh#5LnR>V~-;)t1}B~UW(7DhLH{?nvD;lZ^ubA zvWDo4VBzMziVqV(WPX1@@#@yAQ*T@Ay@C9sPJu0N&C|EDcVzU~YKjcV{KgPZCOgpQVnbf7a1|A%OH;p; znVx`uE<0XX7Mf*v3vf+T&Q2S!XuZJyqH;{(05@Rh+^Yj?%)X}L@05-*hHxd%0>9T0 z1i+dvcUNa}&FdLo&zAZn5P-%Sqj7hY0N=G=64mv|-^Z9%jD|y`KHumzHNMhUtm4h& zKufi-K7q$X>c4CDJ#WMqq*Okv%dEEj+(;OEJF8Z5onX_Xo;5LuR>;+-hL;iQQ+`y# zZTUHje@hSDta*C}JyFrjwXm1rcC4md(lCw>o3g1w3Ag0k;oV_L&7PjwGxB9n%`zjQ z{!@pMQE|glNH6l%h&hU>)w3(1NC03^EWZT;|*`ZyU9N#RZ6@Qiq_*tEpRnN zVXR4kO-|7RpL@KDeL8Vtkd`YBKQj}jU#c6F56!Vj$1%#arPz<@H;l+OeISJw1CDhAqXjQknz-?mWi=c}5P7x^Fivb@e&|FNIV zv8>hg4=&0iEtT6N88B}i$x3VVXzlay?thsm{!g|@i2XkQlpR50>8CB_aSGcVzvI7a z30LyZRR_{^4!*fe!M_-pz7!@Hg=&H0y z(NM8x@?qXAOjGTKdp;F26lawB5TR=0t@c%VGW|qC&FrSZ4P*QQXdo!pyAH7Y#GaQ7mHxh32#JN6ijcGHY9(T>X4ry488tI$H4X>0jf^TZx+8?6suB~IU8vtoP15H z=s)rHZmPvhitY^sIT3D#Tk!#!5tEdk>ZPx*Q>5HB*SNRGcO1H~h8qa$ths`esB%(t zgO~Bdv#)Kv@U!U~@4gcVe3{EeMm`}QTR3KS!c_L}%E@7meUV*2;;)zkp#uN_ literal 0 HcmV?d00001 diff --git a/dev/tests/test_mh.cpp b/dev/tests/test_mh.cpp index f619696a..13149770 100644 --- a/dev/tests/test_mh.cpp +++ b/dev/tests/test_mh.cpp @@ -93,6 +93,9 @@ class Example : public olc::PixelGameEngine std::shared_ptr win2; + olc::Image imBlend; + olc::Image imTempBuffer; + public: bool OnUserCreate() override { @@ -102,7 +105,9 @@ class Example : public olc::PixelGameEngine //CreateImage(imTest, { 64,64 }); CreateImageFromFile(imLogo, "../tests/olc.png"); + CreateImageFromFile(imBlend, "../tests/blend.png"); + CreateImage(imTempBuffer, { 128, 128 }); CreateSampleTestImage(imSampleTest, { 32, 32 }); @@ -146,6 +151,10 @@ class Example : public olc::PixelGameEngine //draw.FilledRect({ 0,0 }, imgPrimary.Size() * olc::vf2d(0.5f, 1.0f), olc::Pixel(0, 0, 0, 1)); + draw.SetTarget(imTempBuffer); + draw.Clear(olc::Colour::WHITE); + draw.Image(imBlend, { 0,0 }); + draw.SetTarget(GetDefaultImage()); draw.WorldReset(); @@ -226,9 +235,11 @@ class Example : public olc::PixelGameEngine //draw.Image(imTemp, vMouse, { 4,4 }); - draw.Image(imSampleTest, vMouse); + //draw.Image(imSampleTest, vMouse); - draw.Image(olc::fontClassicPGE.imgFont, vMouse); + //draw.Image(olc::fontClassicPGE.imgFont, vMouse); + + std::string sTest = "Hello World!\nThe quick brown fox JUMPS over the LaZy dog..."; olc::vf2d vSizeMono = draw.GetTextSize(sTest, false, { 1, 2 }); @@ -260,7 +271,7 @@ class Example : public olc::PixelGameEngine - + draw.Image(imTempBuffer, vMouse); //draw.SetTarget(imgPrimary); //draw.WorldReset(); From 89e7045c441e42fd9832e1355b40250a4a7cbbae Mon Sep 17 00:00:00 2001 From: Javidx9 <25419386+OneLoneCoder@users.noreply.github.com> Date: Tue, 16 Dec 2025 22:56:51 +0000 Subject: [PATCH 13/17] blending bug fixed, now experimenting with polygon lines to get cohesion between lines and fills --- .../olcPGE3_BuildSH/olcPixelGameEngine3.h | 101 ++++++++++++++++-- dev/src/draw2d.cpp | 92 ++++++++++++++-- dev/src/gpu_opengl33.cpp | 9 +- 3 files changed, 180 insertions(+), 22 deletions(-) diff --git a/dev/msvc/olcPGE3_BuildSH/olcPixelGameEngine3.h b/dev/msvc/olcPGE3_BuildSH/olcPixelGameEngine3.h index e626b0ec..d392411f 100644 --- a/dev/msvc/olcPGE3_BuildSH/olcPixelGameEngine3.h +++ b/dev/msvc/olcPGE3_BuildSH/olcPixelGameEngine3.h @@ -3610,7 +3610,7 @@ namespace olc::gpu 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); + gl_Position = p * vec4(vec2(2.0 * ((floor(aPos.xy) ) * invtarget) - 1.0), 0.0, 1.0); oTex = p * vec2(aTex.x, aTex.y); } @@ -3839,6 +3839,7 @@ namespace olc::gpu gl.glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); #endif + std::cout << "Created Texture ID: " << id << " Size: " << vSize.x << "x" << vSize.y << "\n"; return id; } @@ -3881,8 +3882,8 @@ namespace olc::gpu nCurrentTextureTarget = 0; } - if (nCurrentTextureSource == texid) - return true; + //if (nCurrentTextureSource == texid) + // return true; gl.glActiveTexture(0x84C0 + slot); // GL_TEXTURE0 gl.glBindTexture(GL_TEXTURE_2D, texid); @@ -3909,6 +3910,8 @@ namespace olc::gpu gl.glActiveTexture(0x84C0 + i); gl.glBindTexture(GL_TEXTURE_2D, 0); } + // Reset to texture unit 0 + gl.glActiveTexture(0x84C0); nCurrentTextureSource = 0; } @@ -4194,6 +4197,12 @@ void Draw2D::PrepareImageForHW(olc::Image& image) // Image resource is primed for CPU operations, send it to GPU pRenderer->WriteTexture(image.GetGPUID(), image); + if (&image == pTarget) + { + // If this image is also the current target, ensure renderer is updated + SetTarget(image); + } + // Image is now GPU bound image.BindGPU(); } @@ -4268,7 +4277,7 @@ GPUTask olc::Draw2D::TaskDrawPolygon(GPUTask::Structure structure, const std::ve { GPUTask task; task.structure = structure; - task.bWireframe = true; + task.bWireframe = false; 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 @@ -4280,7 +4289,7 @@ GPUTask olc::Draw2D::TaskDrawPolygon(GPUTask::Structure structure, const std::ve { GPUTask task; task.structure = structure; - task.bWireframe = true; + task.bWireframe = false; 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; @@ -4331,25 +4340,72 @@ GPUTask olc::Draw2D::TaskTexturedPolygon(GPUTask::Structure structure, const std const GPUTask& Draw2D::Line(const olc::vf2d& p1, const olc::vf2d& p2, const olc::Pixel col) { PrepareTargetForHW(); - return vecGPUTasks.emplace_back( + /*return vecGPUTasks.emplace_back( TaskDrawPolygon( GPUTask::Structure::Line, transformAffine.forward({p1, p2}), col, olc::Colour::WHITE - )); + ));*/ + + // get image target pixel size in screen space + //olc::vf2d tsize = transformAffine.inverse({ 2.75f, 2.75f }) - transformAffine.inverse({ 0.0f, 0.0f }); + + olc::vf2d tsize = { 0.5f, 0.5f }; // 40.0f / olc::vf2d(pTarget->Size()); + + auto tP1 = transformAffine.forward(p1); + auto tP2 = transformAffine.forward(p2); + + olc::vf2d vSlope = (tP2 - tP1).norm().perp() * tsize; + + + return vecGPUTasks.emplace_back( + TaskDrawPolygon( + GPUTask::Structure::Fan, + /*transformAffine.forward({ + p1 - vSlope, + p1 + vSlope, + p2 + vSlope, + p2 - vSlope, + })*/ + { tP1 - vSlope, tP1 + vSlope, tP2 + vSlope, tP2 - vSlope}, + { col, col, col, col }, + olc::Colour::WHITE + )); } const GPUTask& Draw2D::Line(const olc::vf2d& p1, const olc::vf2d& p2, const olc::Pixel c1, const olc::Pixel c2) { PrepareTargetForHW(); - return vecGPUTasks.emplace_back( + /*return vecGPUTasks.emplace_back( TaskDrawPolygon( GPUTask::Structure::Line, transformAffine.forward({ p1, p2 }), { c1, c2 }, olc::Colour::WHITE - )); + ));*/ + + // get image target pixel size in screen space + //olc::vf2d tsize = transformAffine.inverse({ 1.0f, 1.0f }) - transformAffine.inverse({ 0.0f, 0.0f }); + + olc::vf2d tsize = 1.0f / olc::vf2d(pTarget->Size()); + olc::vf2d vSlope = (p2 - p1).norm().perp(); + + // Draw a thin polygon rectangle to represent line + return vecGPUTasks.emplace_back( + TaskDrawPolygon( + GPUTask::Structure::Fan, + transformAffine.forward({ + olc::vf2d( p1.x - tsize.x * vSlope.x, p1.y - tsize.y * vSlope.y), + olc::vf2d( p2.x + tsize.x * vSlope.x, p1.y - tsize.y * vSlope.y), + olc::vf2d( p2.x + tsize.x * vSlope.x, p2.y + tsize.y * vSlope.y), + olc::vf2d( p1.x - tsize.x * vSlope.x, p2.y + tsize.y * vSlope.y), + }), + { c1, c2, c2, c1 }, + olc::Colour::WHITE + )); + + } const GPUTask& olc::Draw2D::Rect(const olc::vf2d& pos, const olc::vf2d& size, const olc::Pixel col) @@ -4364,7 +4420,7 @@ const GPUTask& olc::Draw2D::Rect(const olc::vf2d& pos, const olc::vf2d& size, co } PrepareTargetForHW(); - return vecGPUTasks.emplace_back( + /*return vecGPUTasks.emplace_back( TaskDrawPolygon( GPUTask::Structure::Fan, transformAffine.forward({ @@ -4375,7 +4431,30 @@ const GPUTask& olc::Draw2D::Rect(const olc::vf2d& pos, const olc::vf2d& size, co }), col, olc::Colour::WHITE - )); + ));*/ + + + // Draw a strip of triangles to represent the outer boundary of a rectangle + /*return vecGPUTasks.emplace_back( + TaskDrawPolygon( + GPUTask::Structure::Fan, + transformAffine.forward({ + olc::vf2d( pos.x - 0.5f, pos.y - 0.5f ), + olc::vf2d( pos.x + size.x + 0.5f, pos.y - 0.5f ), + olc::vf2d( pos.x + size.x + 0.5f, pos.y + 0.5f ), + olc::vf2d( pos.x + size.x + 0.5f, pos.y + size.y + 0.5f ), + olc::vf2d( pos.x - 0.5f, pos.y + size.y + 0.5f ), + olc::vf2d( pos.x - 0.5f, pos.y - 0.5f ), + }), + { col, col, col, col, col, col }, + olc::Colour::WHITE + ));*/ + + Line({ pos.x, pos.y }, { pos.x + size.x, pos.y }, col); + Line({ pos.x + size.x, pos.y }, { pos.x + size.x, pos.y + size.y }, col); + Line({ pos.x + size.x, pos.y + size.y }, { pos.x, pos.y + size.y }, col); + return Line({ pos.x, pos.y + size.y }, { pos.x, pos.y }, col); + } diff --git a/dev/src/draw2d.cpp b/dev/src/draw2d.cpp index 7dba71ab..cb0b15c4 100644 --- a/dev/src/draw2d.cpp +++ b/dev/src/draw2d.cpp @@ -90,6 +90,12 @@ void Draw2D::PrepareImageForHW(olc::Image& image) // Image resource is primed for CPU operations, send it to GPU pRenderer->WriteTexture(image.GetGPUID(), image); + if (&image == pTarget) + { + // If this image is also the current target, ensure renderer is updated + SetTarget(image); + } + // Image is now GPU bound image.BindGPU(); } @@ -164,7 +170,7 @@ GPUTask olc::Draw2D::TaskDrawPolygon(GPUTask::Structure structure, const std::ve { GPUTask task; task.structure = structure; - task.bWireframe = true; + task.bWireframe = false; 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 @@ -176,7 +182,7 @@ GPUTask olc::Draw2D::TaskDrawPolygon(GPUTask::Structure structure, const std::ve { GPUTask task; task.structure = structure; - task.bWireframe = true; + task.bWireframe = false; 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; @@ -227,25 +233,72 @@ GPUTask olc::Draw2D::TaskTexturedPolygon(GPUTask::Structure structure, const std const GPUTask& Draw2D::Line(const olc::vf2d& p1, const olc::vf2d& p2, const olc::Pixel col) { PrepareTargetForHW(); - return vecGPUTasks.emplace_back( + /*return vecGPUTasks.emplace_back( TaskDrawPolygon( GPUTask::Structure::Line, transformAffine.forward({p1, p2}), col, olc::Colour::WHITE - )); + ));*/ + + // get image target pixel size in screen space + //olc::vf2d tsize = transformAffine.inverse({ 2.75f, 2.75f }) - transformAffine.inverse({ 0.0f, 0.0f }); + + olc::vf2d tsize = { 0.5f, 0.5f }; // 40.0f / olc::vf2d(pTarget->Size()); + + auto tP1 = transformAffine.forward(p1); + auto tP2 = transformAffine.forward(p2); + + olc::vf2d vSlope = (tP2 - tP1).norm().perp() * tsize; + + + return vecGPUTasks.emplace_back( + TaskDrawPolygon( + GPUTask::Structure::Fan, + /*transformAffine.forward({ + p1 - vSlope, + p1 + vSlope, + p2 + vSlope, + p2 - vSlope, + })*/ + { tP1 - vSlope, tP1 + vSlope, tP2 + vSlope, tP2 - vSlope}, + { col, col, col, col }, + olc::Colour::WHITE + )); } const GPUTask& Draw2D::Line(const olc::vf2d& p1, const olc::vf2d& p2, const olc::Pixel c1, const olc::Pixel c2) { PrepareTargetForHW(); - return vecGPUTasks.emplace_back( + /*return vecGPUTasks.emplace_back( TaskDrawPolygon( GPUTask::Structure::Line, transformAffine.forward({ p1, p2 }), { c1, c2 }, olc::Colour::WHITE - )); + ));*/ + + // get image target pixel size in screen space + //olc::vf2d tsize = transformAffine.inverse({ 1.0f, 1.0f }) - transformAffine.inverse({ 0.0f, 0.0f }); + + olc::vf2d tsize = 1.0f / olc::vf2d(pTarget->Size()); + olc::vf2d vSlope = (p2 - p1).norm().perp(); + + // Draw a thin polygon rectangle to represent line + return vecGPUTasks.emplace_back( + TaskDrawPolygon( + GPUTask::Structure::Fan, + transformAffine.forward({ + olc::vf2d( p1.x - tsize.x * vSlope.x, p1.y - tsize.y * vSlope.y), + olc::vf2d( p2.x + tsize.x * vSlope.x, p1.y - tsize.y * vSlope.y), + olc::vf2d( p2.x + tsize.x * vSlope.x, p2.y + tsize.y * vSlope.y), + olc::vf2d( p1.x - tsize.x * vSlope.x, p2.y + tsize.y * vSlope.y), + }), + { c1, c2, c2, c1 }, + olc::Colour::WHITE + )); + + } const GPUTask& olc::Draw2D::Rect(const olc::vf2d& pos, const olc::vf2d& size, const olc::Pixel col) @@ -260,7 +313,7 @@ const GPUTask& olc::Draw2D::Rect(const olc::vf2d& pos, const olc::vf2d& size, co } PrepareTargetForHW(); - return vecGPUTasks.emplace_back( + /*return vecGPUTasks.emplace_back( TaskDrawPolygon( GPUTask::Structure::Fan, transformAffine.forward({ @@ -271,7 +324,30 @@ const GPUTask& olc::Draw2D::Rect(const olc::vf2d& pos, const olc::vf2d& size, co }), col, olc::Colour::WHITE - )); + ));*/ + + + // Draw a strip of triangles to represent the outer boundary of a rectangle + /*return vecGPUTasks.emplace_back( + TaskDrawPolygon( + GPUTask::Structure::Fan, + transformAffine.forward({ + olc::vf2d( pos.x - 0.5f, pos.y - 0.5f ), + olc::vf2d( pos.x + size.x + 0.5f, pos.y - 0.5f ), + olc::vf2d( pos.x + size.x + 0.5f, pos.y + 0.5f ), + olc::vf2d( pos.x + size.x + 0.5f, pos.y + size.y + 0.5f ), + olc::vf2d( pos.x - 0.5f, pos.y + size.y + 0.5f ), + olc::vf2d( pos.x - 0.5f, pos.y - 0.5f ), + }), + { col, col, col, col, col, col }, + olc::Colour::WHITE + ));*/ + + Line({ pos.x, pos.y }, { pos.x + size.x, pos.y }, col); + Line({ pos.x + size.x, pos.y }, { pos.x + size.x, pos.y + size.y }, col); + Line({ pos.x + size.x, pos.y + size.y }, { pos.x, pos.y + size.y }, col); + return Line({ pos.x, pos.y + size.y }, { pos.x, pos.y }, col); + } diff --git a/dev/src/gpu_opengl33.cpp b/dev/src/gpu_opengl33.cpp index 08da787f..9ba8e12d 100644 --- a/dev/src/gpu_opengl33.cpp +++ b/dev/src/gpu_opengl33.cpp @@ -148,7 +148,7 @@ namespace olc::gpu 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); + gl_Position = p * vec4(vec2(2.0 * ((floor(aPos.xy) ) * invtarget) - 1.0), 0.0, 1.0); oTex = p * vec2(aTex.x, aTex.y); } @@ -377,6 +377,7 @@ namespace olc::gpu gl.glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); #endif + std::cout << "Created Texture ID: " << id << " Size: " << vSize.x << "x" << vSize.y << "\n"; return id; } @@ -419,8 +420,8 @@ namespace olc::gpu nCurrentTextureTarget = 0; } - if (nCurrentTextureSource == texid) - return true; + //if (nCurrentTextureSource == texid) + // return true; gl.glActiveTexture(0x84C0 + slot); // GL_TEXTURE0 gl.glBindTexture(GL_TEXTURE_2D, texid); @@ -447,6 +448,8 @@ namespace olc::gpu gl.glActiveTexture(0x84C0 + i); gl.glBindTexture(GL_TEXTURE_2D, 0); } + // Reset to texture unit 0 + gl.glActiveTexture(0x84C0); nCurrentTextureSource = 0; } From 426ec49d34203ff444ee9978d64dd629f9d23b1a Mon Sep 17 00:00:00 2001 From: Javidx9 <25419386+OneLoneCoder@users.noreply.github.com> Date: Sun, 21 Dec 2025 12:13:50 +0000 Subject: [PATCH 14/17] more gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index ab59e71c..9625364e 100644 --- a/.gitignore +++ b/.gitignore @@ -40,3 +40,4 @@ /dev/msvc/.vs/CopilotSnapshots/48318E23BE80F04A9ED976A29E9EEE0D /dev/msvc/olcPGE3_BuildSH/x64/Release /dev/msvc/.vs/CopilotSnapshots/7B23092673724F4A930E627F3C0D78FA +/dev/msvc/.vs/CopilotSnapshots/ECC89C71CE5C0C4DBE0F5A2549246AC7 From adbfa156f54a539237767dd15a91d9855ad63cd3 Mon Sep 17 00:00:00 2001 From: Javidx9 <25419386+OneLoneCoder@users.noreply.github.com> Date: Sun, 21 Dec 2025 12:22:14 +0000 Subject: [PATCH 15/17] Beginning to add PGE2 sw rasteriser --- dev/msvc/olcPGE3.vcxproj | 1 + dev/msvc/olcPGE3.vcxproj.filters | 3 + dev/src/draw2d.cpp | 198 ++++++++++++++++++++++++++----- dev/src/draw2d.h | 19 +++ dev/src/draw2d_sw.cpp | 23 ++++ dev/src/gpu_opengl33.cpp | 2 +- dev/tests/test_mh.cpp | 6 +- 7 files changed, 216 insertions(+), 36 deletions(-) create mode 100644 dev/src/draw2d_sw.cpp diff --git a/dev/msvc/olcPGE3.vcxproj b/dev/msvc/olcPGE3.vcxproj index e829cf45..13cc0792 100644 --- a/dev/msvc/olcPGE3.vcxproj +++ b/dev/msvc/olcPGE3.vcxproj @@ -209,6 +209,7 @@ + diff --git a/dev/msvc/olcPGE3.vcxproj.filters b/dev/msvc/olcPGE3.vcxproj.filters index 59ae8f74..396282f8 100644 --- a/dev/msvc/olcPGE3.vcxproj.filters +++ b/dev/msvc/olcPGE3.vcxproj.filters @@ -152,5 +152,8 @@ Source Files + + Source Files + \ No newline at end of file diff --git a/dev/src/draw2d.cpp b/dev/src/draw2d.cpp index cb0b15c4..76dce473 100644 --- a/dev/src/draw2d.cpp +++ b/dev/src/draw2d.cpp @@ -230,41 +230,175 @@ GPUTask olc::Draw2D::TaskTexturedPolygon(GPUTask::Structure structure, const std return task; } -const GPUTask& Draw2D::Line(const olc::vf2d& p1, const olc::vf2d& p2, const olc::Pixel col) -{ - PrepareTargetForHW(); - /*return vecGPUTasks.emplace_back( - TaskDrawPolygon( - GPUTask::Structure::Line, - transformAffine.forward({p1, p2}), - col, - olc::Colour::WHITE - ));*/ - - // get image target pixel size in screen space - //olc::vf2d tsize = transformAffine.inverse({ 2.75f, 2.75f }) - transformAffine.inverse({ 0.0f, 0.0f }); - olc::vf2d tsize = { 0.5f, 0.5f }; // 40.0f / olc::vf2d(pTarget->Size()); - - auto tP1 = transformAffine.forward(p1); - auto tP2 = transformAffine.forward(p2); - - olc::vf2d vSlope = (tP2 - tP1).norm().perp() * tsize; +const GPUTask& Draw2D::Line(const olc::vf2d& p1, const olc::vf2d& p2, const olc::Pixel col) +{ + // Lines are a pain. Software Rasterization is easy, but GPU line drawing is a mess + PrepareTargetForSW(); - return vecGPUTasks.emplace_back( - TaskDrawPolygon( - GPUTask::Structure::Fan, - /*transformAffine.forward({ - p1 - vSlope, - p1 + vSlope, - p2 + vSlope, - p2 - vSlope, - })*/ - { tP1 - vSlope, tP1 + vSlope, tP2 + vSlope, tP2 - vSlope}, - { col, col, col, col }, - olc::Colour::WHITE - )); + auto ClipLineToDrawTarget = [&](olc::vi2d& in_p1, olc::vi2d& in_p2) + { + olc::vi2d vDrawTargetSize{ (int32_t)pTarget->Size().x, (int32_t)pTarget->Size().y}; + + // https://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm + static constexpr int SEG_I = 0b0000, SEG_L = 0b0001, SEG_R = 0b0010, SEG_B = 0b0100, SEG_T = 0b1000; + auto Segment = [&vDrawTargetSize = vDrawTargetSize](const olc::vi2d& v) + { + int i = SEG_I; + if (v.x < 0) i |= SEG_L; else if (v.x > vDrawTargetSize.x) i |= SEG_R; + if (v.y < 0) i |= SEG_B; else if (v.y > vDrawTargetSize.y) i |= SEG_T; + return i; + }; + + int s1 = Segment(in_p1), s2 = Segment(in_p2); + + while (true) + { + if (!(s1 | s2)) return true; + else if (s1 & s2) return false; + else + { + int s3 = s2 > s1 ? s2 : s1; + olc::vi2d n; + if (s3 & SEG_T) { n.x = in_p1.x + (in_p2.x - in_p1.x) * (vDrawTargetSize.y - in_p1.y) / (in_p2.y - in_p1.y); n.y = vDrawTargetSize.y; } + else if (s3 & SEG_B) { n.x = in_p1.x + (in_p2.x - in_p1.x) * (0 - in_p1.y) / (in_p2.y - in_p1.y); n.y = 0; } + else if (s3 & SEG_R) { n.x = vDrawTargetSize.x; n.y = in_p1.y + (in_p2.y - in_p1.y) * (vDrawTargetSize.x - in_p1.x) / (in_p2.x - in_p1.x); } + else if (s3 & SEG_L) { n.x = 0; n.y = in_p1.y + (in_p2.y - in_p1.y) * (0 - in_p1.x) / (in_p2.x - in_p1.x); } + if (s3 == s1) { in_p1 = n; s1 = Segment(in_p1); } + else { in_p2 = n; s2 = Segment(in_p2); } + } + } + return true; + }; + + + auto DrawLine = [&](int32_t x1, int32_t y1, int32_t x2, int32_t y2, olc::Pixel p, uint32_t pattern) + { + int x, y, dx, dy, dx1, dy1, px, py, xe, ye, i; + dx = x2 - x1; dy = y2 - y1; + + auto rol = [&](void) { pattern = (pattern << 1) | (pattern >> 31); return pattern & 1; }; + + olc::vi2d p1(x1, y1), p2(x2, y2); + if (!ClipLineToDrawTarget(p1, p2)) + return; + x1 = p1.x; y1 = p1.y; + x2 = p2.x; y2 = p2.y; + + // straight lines idea by gurkanctn + if (dx == 0) // Line is vertical + { + if (y2 < y1) std::swap(y1, y2); + for (y = y1; y <= y2; y++) if (rol()) Pixel(olc::vf2d(x1, y), p); + return; + } + + if (dy == 0) // Line is horizontal + { + if (x2 < x1) std::swap(x1, x2); + for (x = x1; x <= x2; x++) if (rol()) Pixel(olc::vf2d(x, y1), p); + return; + } + + // Line is Funk-aye + dx1 = abs(dx); dy1 = abs(dy); + px = 2 * dy1 - dx1; py = 2 * dx1 - dy1; + if (dy1 <= dx1) + { + if (dx >= 0) + { + x = x1; y = y1; xe = x2; + } + else + { + x = x2; y = y2; xe = x1; + } + + if (rol()) Pixel(olc::vf2d(x, y), p); + + for (i = 0; x < xe; i++) + { + x = x + 1; + if (px < 0) + px = px + 2 * dy1; + else + { + if ((dx < 0 && dy < 0) || (dx > 0 && dy > 0)) y = y + 1; else y = y - 1; + px = px + 2 * (dy1 - dx1); + } + if (rol()) Pixel(olc::vf2d(x, y), p); + } + } + else + { + if (dy >= 0) + { + x = x1; y = y1; ye = y2; + } + else + { + x = x2; y = y2; ye = y1; + } + + if (rol()) Pixel(olc::vf2d(x, y), p); + + for (i = 0; y < ye; i++) + { + y = y + 1; + if (py <= 0) + py = py + 2 * dx1; + else + { + if ((dx < 0 && dy < 0) || (dx > 0 && dy > 0)) x = x + 1; else x = x - 1; + py = py + 2 * (dx1 - dy1); + } + if (rol()) Pixel(olc::vf2d(x, y), p); + } + } + }; + + + DrawLine(p1.x, p1.y, p2.x, p2.y, col, 0xFFFFFFFF); + + return vecGPUTasks.emplace_back(); // return a dummy task + + //PrepareTargetForHW(); + ///*return vecGPUTasks.emplace_back( + // TaskDrawPolygon( + // GPUTask::Structure::Line, + // transformAffine.forward({p1, p2}), + // col, + // olc::Colour::WHITE + // ));*/ + + // // get image target pixel size in screen space + ////olc::vf2d tsize = transformAffine.inverse({ 2.75f, 2.75f }) - transformAffine.inverse({ 0.0f, 0.0f }); + + + + //// Transform line to pixel space, and select pixel centers + //auto tP1 = transformAffine.forward(p1).floor() + 0.5f; + //auto tP2 = transformAffine.forward(p2).floor() + 0.5f; + + //// Diamond exit strategy workaround - hardcode line width to 1 pixel in target space + //olc::vf2d tsize = { 0.5f, 0.5f }; + //olc::vf2d vSlope = (tP2 - tP1).norm().perp() * tsize; + + + //return vecGPUTasks.emplace_back( + // TaskDrawPolygon( + // GPUTask::Structure::Fan, + // /*transformAffine.forward({ + // p1 - vSlope, + // p1 + vSlope, + // p2 + vSlope, + // p2 - vSlope, + // })*/ + // { tP1 - vSlope, tP1 + vSlope, tP2 + vSlope, tP2 - vSlope}, + // { col, col, col, col }, + // olc::Colour::WHITE + // )); } const GPUTask& Draw2D::Line(const olc::vf2d& p1, const olc::vf2d& p2, const olc::Pixel c1, const olc::Pixel c2) diff --git a/dev/src/draw2d.h b/dev/src/draw2d.h index 29a3d89a..f40cc84b 100644 --- a/dev/src/draw2d.h +++ b/dev/src/draw2d.h @@ -241,6 +241,25 @@ namespace olc olc::Image* const image, const olc::Pixel tint = olc::Colour::WHITE); + + public: // Precision drawing functions via software rasteriser + // Draws a single pixel wide line of fixed colour + void swLine( + const olc::vf2d& p1, + const olc::vf2d& p2, + const olc::Pixel col = olc::Colour::WHITE); + + // Draws a single pixel wide line with a gradient + void swLine( + const olc::vf2d& p1, + const olc::vf2d& p2, + const olc::Pixel c1, + const olc::Pixel c2); + + + + + protected: // Checks residency of image resource, and brings it to cpu RAM for r/w void PrepareTargetForSW(); diff --git a/dev/src/draw2d_sw.cpp b/dev/src/draw2d_sw.cpp new file mode 100644 index 00000000..21237f2b --- /dev/null +++ b/dev/src/draw2d_sw.cpp @@ -0,0 +1,23 @@ +#include "draw2d.h" + +#include "gpu_iface.h" + + + +//! START IMPLEMENTATION + +// This is all essentially the olc::PixelGameEngine 2 rasteriser code +using namespace olc; + + +void Draw2D::swLine(const olc::vf2d& p1, const olc::vf2d& p2, const olc::Pixel col) +{ +} + +void Draw2D::swLine(const olc::vf2d& p1, const olc::vf2d& p2, const olc::Pixel c1, const olc::Pixel c2) +{ +} + + + +//! END IMPLEMENTATION \ No newline at end of file diff --git a/dev/src/gpu_opengl33.cpp b/dev/src/gpu_opengl33.cpp index 9ba8e12d..60fbdf8a 100644 --- a/dev/src/gpu_opengl33.cpp +++ b/dev/src/gpu_opengl33.cpp @@ -148,7 +148,7 @@ namespace olc::gpu 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); + gl_Position = p * vec4(vec2(2.0 * ((floor(aPos.xy)) * invtarget) - 1.0), 0.0, 1.0); oTex = p * vec2(aTex.x, aTex.y); } diff --git a/dev/tests/test_mh.cpp b/dev/tests/test_mh.cpp index 13149770..c25387c1 100644 --- a/dev/tests/test_mh.cpp +++ b/dev/tests/test_mh.cpp @@ -235,7 +235,7 @@ class Example : public olc::PixelGameEngine //draw.Image(imTemp, vMouse, { 4,4 }); - //draw.Image(imSampleTest, vMouse); + //draw.Image(olc::fontClassicPGE.imgFont, vMouse); @@ -271,8 +271,8 @@ class Example : public olc::PixelGameEngine - draw.Image(imTempBuffer, vMouse); - + //draw.Image(imTempBuffer, vMouse); + draw.Image(imSampleTest, vMouse); //draw.SetTarget(imgPrimary); //draw.WorldReset(); //draw.Image(imLowRes, { 0,0 }, { 4, 4 }); From be5a8881acd9486c3e3f5e4093a4bec0e3e5e51f Mon Sep 17 00:00:00 2001 From: Javidx9 <25419386+OneLoneCoder@users.noreply.github.com> Date: Sun, 21 Dec 2025 14:39:58 +0000 Subject: [PATCH 16/17] Settling on patterns, though on wrong branch :P --- .../olcPGE3_BuildSH/olcPixelGameEngine3.h | 140 ++++------ dev/src/draw2d.cpp | 256 +++--------------- dev/src/draw2d.h | 36 +++ dev/src/draw2d_sw.cpp | 213 +++++++++++++++ dev/src/vector2d.h | 6 + dev/tests/test_mh.cpp | 8 +- 6 files changed, 361 insertions(+), 298 deletions(-) diff --git a/dev/msvc/olcPGE3_BuildSH/olcPixelGameEngine3.h b/dev/msvc/olcPGE3_BuildSH/olcPixelGameEngine3.h index d392411f..c35cfbe7 100644 --- a/dev/msvc/olcPGE3_BuildSH/olcPixelGameEngine3.h +++ b/dev/msvc/olcPGE3_BuildSH/olcPixelGameEngine3.h @@ -606,6 +606,12 @@ namespace olc return v_2d(std::min(x, v.x), std::min(y, v.y)); } + // Returns 'element-wise' abs of this vector + inline constexpr v_2d abs() const + { + return v_2d(std::abs(x), std::abs(y)); + } + // Calculates scalar dot product between this and another vector inline constexpr auto dot(const v_2d& rhs) const { @@ -1900,6 +1906,37 @@ namespace olc olc::Image* const image, const olc::Pixel tint = olc::Colour::WHITE); + + public: // Precision drawing functions via software rasteriser + // Draws a single pixel wide line of fixed colour + void swLine( + const olc::vf2d& p1, + const olc::vf2d& p2, + const olc::Pixel col = olc::Colour::WHITE); + + // Draws a single pixel wide line with a gradient + void swLine( + const olc::vf2d& p1, + const olc::vf2d& p2, + const olc::Pixel c1, + const olc::Pixel c2); + + + + // Software rasteriser helper functions + + // Clips a line to a rectangular region, returns true if line is visible + bool swClipLine( + olc::vf2d& v0, + olc::vf2d& v1, + const olc::vf2d& vMin, + const olc::vf2d& vMax); + + + + + + protected: // Checks residency of image resource, and brings it to cpu RAM for r/w void PrepareTargetForSW(); @@ -3610,7 +3647,7 @@ namespace olc::gpu 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); + gl_Position = p * vec4(vec2(2.0 * ((floor(aPos.xy)) * invtarget) - 1.0), 0.0, 1.0); oTex = p * vec2(aTex.x, aTex.y); } @@ -4200,7 +4237,17 @@ void Draw2D::PrepareImageForHW(olc::Image& image) if (&image == pTarget) { // If this image is also the current target, ensure renderer is updated - SetTarget(image); + //SetTarget(image); + + // Store the target image + pTarget = ℑ + + // Reset Affine transform to unity + WorldReset(); + + // Configure default render target + pRenderer->AssignTextureTarget(0, pTarget->GetGPUID()); + pRenderer->SetViewport({ 0,0 }, pTarget->Size()); } // Image is now GPU bound @@ -4277,7 +4324,7 @@ GPUTask olc::Draw2D::TaskDrawPolygon(GPUTask::Structure structure, const std::ve { GPUTask task; task.structure = structure; - task.bWireframe = false; + 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 @@ -4289,7 +4336,7 @@ GPUTask olc::Draw2D::TaskDrawPolygon(GPUTask::Structure structure, const std::ve { GPUTask task; task.structure = structure; - task.bWireframe = false; + 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; @@ -4337,75 +4384,30 @@ GPUTask olc::Draw2D::TaskTexturedPolygon(GPUTask::Structure structure, const std return task; } + + const GPUTask& Draw2D::Line(const olc::vf2d& p1, const olc::vf2d& p2, const olc::Pixel col) { PrepareTargetForHW(); - /*return vecGPUTasks.emplace_back( + return vecGPUTasks.emplace_back( TaskDrawPolygon( GPUTask::Structure::Line, transformAffine.forward({p1, p2}), col, olc::Colour::WHITE - ));*/ - - // get image target pixel size in screen space - //olc::vf2d tsize = transformAffine.inverse({ 2.75f, 2.75f }) - transformAffine.inverse({ 0.0f, 0.0f }); - - olc::vf2d tsize = { 0.5f, 0.5f }; // 40.0f / olc::vf2d(pTarget->Size()); - - auto tP1 = transformAffine.forward(p1); - auto tP2 = transformAffine.forward(p2); - - olc::vf2d vSlope = (tP2 - tP1).norm().perp() * tsize; - - - return vecGPUTasks.emplace_back( - TaskDrawPolygon( - GPUTask::Structure::Fan, - /*transformAffine.forward({ - p1 - vSlope, - p1 + vSlope, - p2 + vSlope, - p2 - vSlope, - })*/ - { tP1 - vSlope, tP1 + vSlope, tP2 + vSlope, tP2 - vSlope}, - { col, col, col, col }, - olc::Colour::WHITE - )); + )); } const GPUTask& Draw2D::Line(const olc::vf2d& p1, const olc::vf2d& p2, const olc::Pixel c1, const olc::Pixel c2) { PrepareTargetForHW(); - /*return vecGPUTasks.emplace_back( + return vecGPUTasks.emplace_back( TaskDrawPolygon( GPUTask::Structure::Line, transformAffine.forward({ p1, p2 }), { c1, c2 }, olc::Colour::WHITE - ));*/ - - // get image target pixel size in screen space - //olc::vf2d tsize = transformAffine.inverse({ 1.0f, 1.0f }) - transformAffine.inverse({ 0.0f, 0.0f }); - - olc::vf2d tsize = 1.0f / olc::vf2d(pTarget->Size()); - olc::vf2d vSlope = (p2 - p1).norm().perp(); - - // Draw a thin polygon rectangle to represent line - return vecGPUTasks.emplace_back( - TaskDrawPolygon( - GPUTask::Structure::Fan, - transformAffine.forward({ - olc::vf2d( p1.x - tsize.x * vSlope.x, p1.y - tsize.y * vSlope.y), - olc::vf2d( p2.x + tsize.x * vSlope.x, p1.y - tsize.y * vSlope.y), - olc::vf2d( p2.x + tsize.x * vSlope.x, p2.y + tsize.y * vSlope.y), - olc::vf2d( p1.x - tsize.x * vSlope.x, p2.y + tsize.y * vSlope.y), - }), - { c1, c2, c2, c1 }, - olc::Colour::WHITE - )); - - + )); } const GPUTask& olc::Draw2D::Rect(const olc::vf2d& pos, const olc::vf2d& size, const olc::Pixel col) @@ -4420,7 +4422,7 @@ const GPUTask& olc::Draw2D::Rect(const olc::vf2d& pos, const olc::vf2d& size, co } PrepareTargetForHW(); - /*return vecGPUTasks.emplace_back( + return vecGPUTasks.emplace_back( TaskDrawPolygon( GPUTask::Structure::Fan, transformAffine.forward({ @@ -4431,31 +4433,7 @@ const GPUTask& olc::Draw2D::Rect(const olc::vf2d& pos, const olc::vf2d& size, co }), col, olc::Colour::WHITE - ));*/ - - - // Draw a strip of triangles to represent the outer boundary of a rectangle - /*return vecGPUTasks.emplace_back( - TaskDrawPolygon( - GPUTask::Structure::Fan, - transformAffine.forward({ - olc::vf2d( pos.x - 0.5f, pos.y - 0.5f ), - olc::vf2d( pos.x + size.x + 0.5f, pos.y - 0.5f ), - olc::vf2d( pos.x + size.x + 0.5f, pos.y + 0.5f ), - olc::vf2d( pos.x + size.x + 0.5f, pos.y + size.y + 0.5f ), - olc::vf2d( pos.x - 0.5f, pos.y + size.y + 0.5f ), - olc::vf2d( pos.x - 0.5f, pos.y - 0.5f ), - }), - { col, col, col, col, col, col }, - olc::Colour::WHITE - ));*/ - - Line({ pos.x, pos.y }, { pos.x + size.x, pos.y }, col); - Line({ pos.x + size.x, pos.y }, { pos.x + size.x, pos.y + size.y }, col); - Line({ pos.x + size.x, pos.y + size.y }, { pos.x, pos.y + size.y }, col); - return Line({ pos.x, pos.y + size.y }, { pos.x, pos.y }, col); - - + )); } diff --git a/dev/src/draw2d.cpp b/dev/src/draw2d.cpp index 76dce473..286bceea 100644 --- a/dev/src/draw2d.cpp +++ b/dev/src/draw2d.cpp @@ -93,7 +93,17 @@ void Draw2D::PrepareImageForHW(olc::Image& image) if (&image == pTarget) { // If this image is also the current target, ensure renderer is updated - SetTarget(image); + //SetTarget(image); + + // Store the target image + pTarget = ℑ + + // Reset Affine transform to unity + WorldReset(); + + // Configure default render target + pRenderer->AssignTextureTarget(0, pTarget->GetGPUID()); + pRenderer->SetViewport({ 0,0 }, pTarget->Size()); } // Image is now GPU bound @@ -170,7 +180,7 @@ GPUTask olc::Draw2D::TaskDrawPolygon(GPUTask::Structure structure, const std::ve { GPUTask task; task.structure = structure; - task.bWireframe = false; + 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 @@ -182,7 +192,7 @@ GPUTask olc::Draw2D::TaskDrawPolygon(GPUTask::Structure structure, const std::ve { GPUTask task; task.structure = structure; - task.bWireframe = false; + 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; @@ -234,205 +244,26 @@ GPUTask olc::Draw2D::TaskTexturedPolygon(GPUTask::Structure structure, const std const GPUTask& Draw2D::Line(const olc::vf2d& p1, const olc::vf2d& p2, const olc::Pixel col) { - // Lines are a pain. Software Rasterization is easy, but GPU line drawing is a mess - PrepareTargetForSW(); - - auto ClipLineToDrawTarget = [&](olc::vi2d& in_p1, olc::vi2d& in_p2) - { - olc::vi2d vDrawTargetSize{ (int32_t)pTarget->Size().x, (int32_t)pTarget->Size().y}; - - // https://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm - static constexpr int SEG_I = 0b0000, SEG_L = 0b0001, SEG_R = 0b0010, SEG_B = 0b0100, SEG_T = 0b1000; - auto Segment = [&vDrawTargetSize = vDrawTargetSize](const olc::vi2d& v) - { - int i = SEG_I; - if (v.x < 0) i |= SEG_L; else if (v.x > vDrawTargetSize.x) i |= SEG_R; - if (v.y < 0) i |= SEG_B; else if (v.y > vDrawTargetSize.y) i |= SEG_T; - return i; - }; - - int s1 = Segment(in_p1), s2 = Segment(in_p2); - - while (true) - { - if (!(s1 | s2)) return true; - else if (s1 & s2) return false; - else - { - int s3 = s2 > s1 ? s2 : s1; - olc::vi2d n; - if (s3 & SEG_T) { n.x = in_p1.x + (in_p2.x - in_p1.x) * (vDrawTargetSize.y - in_p1.y) / (in_p2.y - in_p1.y); n.y = vDrawTargetSize.y; } - else if (s3 & SEG_B) { n.x = in_p1.x + (in_p2.x - in_p1.x) * (0 - in_p1.y) / (in_p2.y - in_p1.y); n.y = 0; } - else if (s3 & SEG_R) { n.x = vDrawTargetSize.x; n.y = in_p1.y + (in_p2.y - in_p1.y) * (vDrawTargetSize.x - in_p1.x) / (in_p2.x - in_p1.x); } - else if (s3 & SEG_L) { n.x = 0; n.y = in_p1.y + (in_p2.y - in_p1.y) * (0 - in_p1.x) / (in_p2.x - in_p1.x); } - if (s3 == s1) { in_p1 = n; s1 = Segment(in_p1); } - else { in_p2 = n; s2 = Segment(in_p2); } - } - } - return true; - }; - - - auto DrawLine = [&](int32_t x1, int32_t y1, int32_t x2, int32_t y2, olc::Pixel p, uint32_t pattern) - { - int x, y, dx, dy, dx1, dy1, px, py, xe, ye, i; - dx = x2 - x1; dy = y2 - y1; - - auto rol = [&](void) { pattern = (pattern << 1) | (pattern >> 31); return pattern & 1; }; - - olc::vi2d p1(x1, y1), p2(x2, y2); - if (!ClipLineToDrawTarget(p1, p2)) - return; - x1 = p1.x; y1 = p1.y; - x2 = p2.x; y2 = p2.y; - - // straight lines idea by gurkanctn - if (dx == 0) // Line is vertical - { - if (y2 < y1) std::swap(y1, y2); - for (y = y1; y <= y2; y++) if (rol()) Pixel(olc::vf2d(x1, y), p); - return; - } - - if (dy == 0) // Line is horizontal - { - if (x2 < x1) std::swap(x1, x2); - for (x = x1; x <= x2; x++) if (rol()) Pixel(olc::vf2d(x, y1), p); - return; - } - - // Line is Funk-aye - dx1 = abs(dx); dy1 = abs(dy); - px = 2 * dy1 - dx1; py = 2 * dx1 - dy1; - if (dy1 <= dx1) - { - if (dx >= 0) - { - x = x1; y = y1; xe = x2; - } - else - { - x = x2; y = y2; xe = x1; - } - - if (rol()) Pixel(olc::vf2d(x, y), p); - - for (i = 0; x < xe; i++) - { - x = x + 1; - if (px < 0) - px = px + 2 * dy1; - else - { - if ((dx < 0 && dy < 0) || (dx > 0 && dy > 0)) y = y + 1; else y = y - 1; - px = px + 2 * (dy1 - dx1); - } - if (rol()) Pixel(olc::vf2d(x, y), p); - } - } - else - { - if (dy >= 0) - { - x = x1; y = y1; ye = y2; - } - else - { - x = x2; y = y2; ye = y1; - } - - if (rol()) Pixel(olc::vf2d(x, y), p); - - for (i = 0; y < ye; i++) - { - y = y + 1; - if (py <= 0) - py = py + 2 * dx1; - else - { - if ((dx < 0 && dy < 0) || (dx > 0 && dy > 0)) x = x + 1; else x = x - 1; - py = py + 2 * (dx1 - dy1); - } - if (rol()) Pixel(olc::vf2d(x, y), p); - } - } - }; - - - DrawLine(p1.x, p1.y, p2.x, p2.y, col, 0xFFFFFFFF); - - return vecGPUTasks.emplace_back(); // return a dummy task - - //PrepareTargetForHW(); - ///*return vecGPUTasks.emplace_back( - // TaskDrawPolygon( - // GPUTask::Structure::Line, - // transformAffine.forward({p1, p2}), - // col, - // olc::Colour::WHITE - // ));*/ - - // // get image target pixel size in screen space - ////olc::vf2d tsize = transformAffine.inverse({ 2.75f, 2.75f }) - transformAffine.inverse({ 0.0f, 0.0f }); - - - - //// Transform line to pixel space, and select pixel centers - //auto tP1 = transformAffine.forward(p1).floor() + 0.5f; - //auto tP2 = transformAffine.forward(p2).floor() + 0.5f; - - //// Diamond exit strategy workaround - hardcode line width to 1 pixel in target space - //olc::vf2d tsize = { 0.5f, 0.5f }; - //olc::vf2d vSlope = (tP2 - tP1).norm().perp() * tsize; - - - //return vecGPUTasks.emplace_back( - // TaskDrawPolygon( - // GPUTask::Structure::Fan, - // /*transformAffine.forward({ - // p1 - vSlope, - // p1 + vSlope, - // p2 + vSlope, - // p2 - vSlope, - // })*/ - // { tP1 - vSlope, tP1 + vSlope, tP2 + vSlope, tP2 - vSlope}, - // { col, col, col, col }, - // olc::Colour::WHITE - // )); + PrepareTargetForHW(); + return vecGPUTasks.emplace_back( + TaskDrawPolygon( + GPUTask::Structure::Line, + transformAffine.forward({p1, p2}), + col, + olc::Colour::WHITE + )); } const GPUTask& Draw2D::Line(const olc::vf2d& p1, const olc::vf2d& p2, const olc::Pixel c1, const olc::Pixel c2) { PrepareTargetForHW(); - /*return vecGPUTasks.emplace_back( + return vecGPUTasks.emplace_back( TaskDrawPolygon( GPUTask::Structure::Line, transformAffine.forward({ p1, p2 }), { c1, c2 }, olc::Colour::WHITE - ));*/ - - // get image target pixel size in screen space - //olc::vf2d tsize = transformAffine.inverse({ 1.0f, 1.0f }) - transformAffine.inverse({ 0.0f, 0.0f }); - - olc::vf2d tsize = 1.0f / olc::vf2d(pTarget->Size()); - olc::vf2d vSlope = (p2 - p1).norm().perp(); - - // Draw a thin polygon rectangle to represent line - return vecGPUTasks.emplace_back( - TaskDrawPolygon( - GPUTask::Structure::Fan, - transformAffine.forward({ - olc::vf2d( p1.x - tsize.x * vSlope.x, p1.y - tsize.y * vSlope.y), - olc::vf2d( p2.x + tsize.x * vSlope.x, p1.y - tsize.y * vSlope.y), - olc::vf2d( p2.x + tsize.x * vSlope.x, p2.y + tsize.y * vSlope.y), - olc::vf2d( p1.x - tsize.x * vSlope.x, p2.y + tsize.y * vSlope.y), - }), - { c1, c2, c2, c1 }, - olc::Colour::WHITE - )); - - + )); } const GPUTask& olc::Draw2D::Rect(const olc::vf2d& pos, const olc::vf2d& size, const olc::Pixel col) @@ -447,7 +278,7 @@ const GPUTask& olc::Draw2D::Rect(const olc::vf2d& pos, const olc::vf2d& size, co } PrepareTargetForHW(); - /*return vecGPUTasks.emplace_back( + return vecGPUTasks.emplace_back( TaskDrawPolygon( GPUTask::Structure::Fan, transformAffine.forward({ @@ -458,31 +289,24 @@ const GPUTask& olc::Draw2D::Rect(const olc::vf2d& pos, const olc::vf2d& size, co }), col, olc::Colour::WHITE - ));*/ - + )); +} - // Draw a strip of triangles to represent the outer boundary of a rectangle - /*return vecGPUTasks.emplace_back( - TaskDrawPolygon( - GPUTask::Structure::Fan, - transformAffine.forward({ - olc::vf2d( pos.x - 0.5f, pos.y - 0.5f ), - olc::vf2d( pos.x + size.x + 0.5f, pos.y - 0.5f ), - olc::vf2d( pos.x + size.x + 0.5f, pos.y + 0.5f ), - olc::vf2d( pos.x + size.x + 0.5f, pos.y + size.y + 0.5f ), - olc::vf2d( pos.x - 0.5f, pos.y + size.y + 0.5f ), - olc::vf2d( pos.x - 0.5f, pos.y - 0.5f ), - }), - { col, col, col, col, col, col }, +const GPUTask& olc::Draw2D::Rect(const olc::vf2d& pos, const olc::vf2d& size, const olc::Pixel colTL, const olc::Pixel colTR, const olc::Pixel colBL, const olc::Pixel colBR) +{ + 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 + size.y + 0.0f), + olc::vf2d(pos.x + 0.0f, pos.y + size.y + 0.0f), + }), + { colTL, colTR, colBR, colBL }, olc::Colour::WHITE - ));*/ - - Line({ pos.x, pos.y }, { pos.x + size.x, pos.y }, col); - Line({ pos.x + size.x, pos.y }, { pos.x + size.x, pos.y + size.y }, col); - Line({ pos.x + size.x, pos.y + size.y }, { pos.x, pos.y + size.y }, col); - return Line({ pos.x, pos.y + size.y }, { pos.x, pos.y }, col); - - + )); } diff --git a/dev/src/draw2d.h b/dev/src/draw2d.h index f40cc84b..a2a70a00 100644 --- a/dev/src/draw2d.h +++ b/dev/src/draw2d.h @@ -90,6 +90,15 @@ namespace olc const olc::vf2d& size, const olc::Pixel col = olc::Colour::WHITE); + // Draws a multiple colour rectangle, with linear colour interpolation + const GPUTask& Rect( + const olc::vf2d& pos, + const olc::vf2d& size, + const olc::Pixel colTL, + const olc::Pixel colTR, + const olc::Pixel colBL, + const olc::Pixel colBR); + // Draws a filled, single colour rectangle const GPUTask& FilledRect( const olc::vf2d& pos, @@ -255,6 +264,33 @@ namespace olc const olc::vf2d& p2, const olc::Pixel c1, const olc::Pixel c2); + + // Draws a rectangle outline + void swRect( + const olc::vf2d& pos, + const olc::vf2d& size, + const olc::Pixel col = olc::Colour::WHITE); + + // Draws a multiple colour rectangle, with linear colour interpolation + void swRect( + const olc::vf2d& pos, + const olc::vf2d& size, + const olc::Pixel colTL, + const olc::Pixel colTR, + const olc::Pixel colBL, + const olc::Pixel colBR); + + + + // Software rasteriser helper functions + + // Clips a line to a rectangular region, returns true if line is visible + bool swClipLine( + olc::vf2d& v0, + olc::vf2d& v1, + const olc::vf2d& vMin, + const olc::vf2d& vMax); + diff --git a/dev/src/draw2d_sw.cpp b/dev/src/draw2d_sw.cpp index 21237f2b..7a342598 100644 --- a/dev/src/draw2d_sw.cpp +++ b/dev/src/draw2d_sw.cpp @@ -12,10 +12,223 @@ using namespace olc; void Draw2D::swLine(const olc::vf2d& p1, const olc::vf2d& p2, const olc::Pixel col) { + swLine(p1, p2, col, col); } void Draw2D::swLine(const olc::vf2d& p1, const olc::vf2d& p2, const olc::Pixel c1, const olc::Pixel c2) { + PrepareTargetForSW(); + + // Lambda to draw a pixel gated by a pattern bit + uint32_t pattern = 0xFFFFFFFF; + auto rol = [&](void) + { + pattern = (pattern << 1) | (pattern >> 31); + return pattern & 1; + }; + + // Lambda to draw a pixel at integer location + auto Plot = [&](int32_t x, int32_t y, const olc::Pixel& p) + { + if (x >= 0 && x < pTarget->Size().x && y >= 0 && y < pTarget->Size().y) + pTarget->Pixel({ x, y }) = p; + }; + + // Clip line to draw target + olc::vf2d clipped_p1 = transformAffine.forward(p1); + olc::vf2d clipped_p2 = transformAffine.forward(p2); + + // If line is completely outside bounds, exit + if (!swClipLine(clipped_p1, clipped_p2, { 0,0 }, pTarget->Size())) + return; + + // Move to integer space + olc::vi2d ip1 = clipped_p1.floor(); + olc::vi2d ip2 = clipped_p2.floor(); + olc::vi2d pixel; + + // Gradients + olc::vi2d diff1 = ip2 - ip1; + + // Colour interpolation variables + float fColourT = 0.0f; + float fColourStep = 1.0f / float(std::max(std::abs(diff1.x), std::abs(diff1.y))); + + // Quick draw straight lines + if (diff1.x == 0) // Line is vertical + { + if (ip2.y < ip1.y) + { + std::swap(ip1.y, ip2.y); + fColourStep *= -1.0f; + fColourT = 1.0f; + } + + for (pixel.y = ip1.y; pixel.y <= ip2.y; pixel.y++) + { + if (rol()) + Plot(ip1.x, pixel.y, olc::PixelLerp(c1, c2, fColourT)); + + fColourT += fColourStep; + } + + // Early exit + return; + } + + if (diff1.y == 0) // Line is horizontal + { + if (ip2.x < ip1.x) + { + std::swap(ip1.x, ip2.x); + fColourStep *= -1.0f; + fColourT = 1.0f; + } + + + for (pixel.x = ip1.x; pixel.x <= ip2.x; pixel.x++) + { + if (rol()) + Plot(pixel.x, ip1.y, olc::PixelLerp(c1, c2, fColourT)); + + fColourT += fColourStep; + } + + // Early exit + return; + } + + // Line is sloped + olc::vi2d diff2 = diff1.abs(); + + // Apply Bresenham algorithm + olc::vi2d p = { diff2.y - diff2.x * 2, diff2.x - diff2.y * 2 }; + olc::vi2d end = { 0,0 }; + + if (diff2.y <= diff2.x) // Propoagate in x-direction + { + if (diff1.x >= 0) + { + pixel = ip1; + end = ip2; + } + else + { + pixel = ip2; + end = ip1; + fColourStep *= -1.0f; + fColourT = 1.0f; + } + + if (rol()) + Plot(pixel.x, pixel.y, olc::PixelLerp(c1, c2, fColourT)); + + for (int i = 0; pixel.x < end.x; i++) + { + pixel.x = pixel.x + 1; + fColourT += fColourStep; + + if (p.x < 0) + p.x = p.x + 2 * diff2.y; + else + { + if ((diff1.x < 0 && diff1.y < 0) || (diff1.x > 0 && diff1.y > 0)) + pixel.y = pixel.y + 1; + else + pixel.y = pixel.y - 1; + + p.x = p.x + 2 * (diff2.y - diff2.x); + } + + if (rol()) + Plot(pixel.x, pixel.y, olc::PixelLerp(c1, c2, fColourT)); + } + } + else + { + if (diff1.y >= 0) // Propogate in y-direction + { + pixel = ip1; + end = ip2; + } + else + { + pixel = ip2; + end = ip1; + fColourStep *= -1.0f; + fColourT = 1.0f; + } + + if (rol()) + Plot(pixel.x, pixel.y, olc::PixelLerp(c1, c2, fColourT)); + + for (int i = 0; pixel.y < end.y; i++) + { + pixel.y = pixel.y + 1; + fColourT += fColourStep; + + if (p.y <= 0) + p.y = p.y + 2 * diff2.x; + else + { + if ((diff1.x < 0 && diff1.y < 0) || (diff1.x > 0 && diff1.y > 0)) + pixel.x = pixel.x + 1; + else + pixel.x = pixel.x - 1; + + p.y = p.y + 2 * (diff2.x - diff2.y); + } + + if (rol()) + Plot(pixel.x, pixel.y, olc::PixelLerp(c1, c2, fColourT)); + } + } +} + +void olc::Draw2D::swRect(const olc::vf2d& pos, const olc::vf2d& size, const olc::Pixel col) +{ + swRect(pos, size, col, col, col, col); +} + +void olc::Draw2D::swRect(const olc::vf2d& pos, const olc::vf2d& size, const olc::Pixel colTL, const olc::Pixel colTR, const olc::Pixel colBL, const olc::Pixel colBR) +{ + swLine(pos, { pos.x + size.x, pos.y }, colTL, colTR); + swLine({ pos.x + size.x, pos.y }, { pos.x + size.x, pos.y + size.y }, colTR, colBR); + swLine({ pos.x + size.x, pos.y + size.y }, { pos.x, pos.y + size.y }, colBR, colBL); + swLine({ pos.x, pos.y + size.y }, pos, colBL, colTL); +} + +bool olc::Draw2D::swClipLine(olc::vf2d& p1, olc::vf2d& p2, const olc::vf2d& vMin, const olc::vf2d& vMax) +{ + // https://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm + static constexpr int SEG_I = 0b0000, SEG_L = 0b0001, SEG_R = 0b0010, SEG_B = 0b0100, SEG_T = 0b1000; + auto Segment = [&vMin = vMin, &vMax = vMax](const olc::vi2d& v) + { + int i = SEG_I; + if (v.x < vMin.x) i |= SEG_L; else if (v.x > vMax.x) i |= SEG_R; + if (v.y < vMin.y) i |= SEG_B; else if (v.y > vMax.y) i |= SEG_T; + return i; + }; + + int s1 = Segment(p1), s2 = Segment(p2); + + while (true) + { + if (!(s1 | s2)) return true; + else if (s1 & s2) return false; + else + { + int s3 = s2 > s1 ? s2 : s1; + olc::vf2d n; + if (s3 & SEG_T) { n.x = p1.x + (p2.x - p1.x) * (vMax.y - p1.y) / (p2.y - p1.y); n.y = vMax.y; } + else if (s3 & SEG_B) { n.x = p1.x + (p2.x - p1.x) * (vMin.y - p1.y) / (p2.y - p1.y); n.y = vMin.y; } + else if (s3 & SEG_R) { n.x = vMax.x; n.y = p1.y + (p2.y - p1.y) * (vMax.x - p1.x) / (p2.x - p1.x); } + else if (s3 & SEG_L) { n.x = vMin.x; n.y = p1.y + (p2.y - p1.y) * (vMin.x - p1.x) / (p2.x - p1.x); } + if (s3 == s1) { p1 = n; s1 = Segment(p1); } + else { p2 = n; s2 = Segment(p2); } + } + } + return true; } diff --git a/dev/src/vector2d.h b/dev/src/vector2d.h index 3ca61440..ac8be2db 100644 --- a/dev/src/vector2d.h +++ b/dev/src/vector2d.h @@ -119,6 +119,12 @@ namespace olc return v_2d(std::min(x, v.x), std::min(y, v.y)); } + // Returns 'element-wise' abs of this vector + inline constexpr v_2d abs() const + { + return v_2d(std::abs(x), std::abs(y)); + } + // Calculates scalar dot product between this and another vector inline constexpr auto dot(const v_2d& rhs) const { diff --git a/dev/tests/test_mh.cpp b/dev/tests/test_mh.cpp index c25387c1..25ed9dee 100644 --- a/dev/tests/test_mh.cpp +++ b/dev/tests/test_mh.cpp @@ -264,7 +264,7 @@ class Example : public olc::PixelGameEngine - draw.Rect({ 8,8 }, { 20,20 }, olc::Colour::RED); + // draw.Rect({ 8,8 }, { 20,20 }, olc::Colour::RED); if(mouse.GetButton(0).bHeld) draw.Line({ 1.0f, 1.0f }, { 25.5f, 25.5f }); @@ -276,6 +276,12 @@ class Example : public olc::PixelGameEngine //draw.SetTarget(imgPrimary); //draw.WorldReset(); //draw.Image(imLowRes, { 0,0 }, { 4, 4 }); + + + draw.swLine({ 100,100 }, vMouse, olc::Colour::BLACK, olc::Colour::YELLOW); + + draw.Rect({ 8,8, }, { 20,20 }, olc::Colour::RED, olc::Colour::GREEN, olc::Colour::BLUE, olc::Colour::WHITE); + draw.swRect({ 7,7, }, { 22,22 }, olc::Colour::RED, olc::Colour::GREEN, olc::Colour::BLUE, olc::Colour::WHITE); //draw.Rect(vMouse, { 100,100 }); From 426bc4bead4fc86b24d2c588af754942a3ab56e3 Mon Sep 17 00:00:00 2001 From: Javidx9 <25419386+OneLoneCoder@users.noreply.github.com> Date: Sun, 21 Dec 2025 16:04:10 +0000 Subject: [PATCH 17/17] gitignore update --- .gitignore | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/.gitignore b/.gitignore index 9625364e..c61a328d 100644 --- a/.gitignore +++ b/.gitignore @@ -31,13 +31,9 @@ *.out *.app /.vs -/dev/msvc/.vs/olcPGE3 -/dev/msvc/olcPGE3/x64/Debug +/dev/msvc/.vs +/dev/msvc/x64 /dev/tests/build -/dev/msvc/olcPGE3_BuildSH/x64/Debug -/dev/msvc/olcPGE3/x64/Release -/dev/msvc/x64/Debug/olcPGE3.tlog -/dev/msvc/.vs/CopilotSnapshots/48318E23BE80F04A9ED976A29E9EEE0D -/dev/msvc/olcPGE3_BuildSH/x64/Release -/dev/msvc/.vs/CopilotSnapshots/7B23092673724F4A930E627F3C0D78FA -/dev/msvc/.vs/CopilotSnapshots/ECC89C71CE5C0C4DBE0F5A2549246AC7 +/dev/msvc/olcPGE3/x64 +/dev/msvc/olcPGE3_BuildSH/x64 +