diff --git a/assets/border.png b/assets/border.png index b4cc9ff..aa0e2a9 100644 Binary files a/assets/border.png and b/assets/border.png differ diff --git a/assets/border.xcf b/assets/border.xcf index f8f26a4..932b4f2 100644 Binary files a/assets/border.xcf and b/assets/border.xcf differ diff --git a/hamster.vcxproj b/hamster.vcxproj index b0d8b16..ae1c84f 100644 --- a/hamster.vcxproj +++ b/hamster.vcxproj @@ -350,7 +350,10 @@ if %errorlevel% neq 0 goto :VCEnd - + + + + diff --git a/hamster.vcxproj.filters b/hamster.vcxproj.filters index f5759d1..4c5c28a 100644 --- a/hamster.vcxproj.filters +++ b/hamster.vcxproj.filters @@ -49,9 +49,6 @@ Header Files - - Header Files - Header Files @@ -70,5 +67,8 @@ Header Files + + Header Files + \ No newline at end of file diff --git a/src/Hamster.cpp b/src/Hamster.cpp index 26300c2..5c0661e 100644 --- a/src/Hamster.cpp +++ b/src/Hamster.cpp @@ -68,10 +68,10 @@ void Hamster::LoadHamsters(const vf2d startingLoc){ } } -void Hamster::DrawHamsters(const ViewPort&view){ +void Hamster::DrawHamsters(TransformedView&tv){ for(Hamster&h:HAMSTER_LIST){ const Animate2D::Frame&img{h.GetCurrentAnimation()}; - view.DrawPartialRotatedDecal(h.pos,img.GetSourceImage()->Decal(),h.rot,img.GetSourceRect().size/2,img.GetSourceRect().pos,img.GetSourceRect().size); + tv.DrawPartialRotatedDecal(h.pos,img.GetSourceImage()->Decal(),h.rot,img.GetSourceRect().size/2,img.GetSourceRect().pos,img.GetSourceRect().size); } } diff --git a/src/Hamster.h b/src/Hamster.h index 4be0a0f..0746748 100644 --- a/src/Hamster.h +++ b/src/Hamster.h @@ -40,7 +40,6 @@ All rights reserved. #include #include "olcUTIL_Geometry2D.h" #include "olcUTIL_Animate2D.h" -#include "olcPGEX_ViewPort.h" class Hamster{ enum PlayerControlled{ @@ -66,6 +65,6 @@ public: Hamster(const vf2d spawnPos,const std::string_view img,const PlayerControlled playerControlled=NPC); static void UpdateHamsters(const float fElapsedTime); static void LoadHamsters(const vf2d startingLoc); - static void DrawHamsters(const ViewPort&view); + static void DrawHamsters(TransformedView&tv); const Animate2D::Frame&GetCurrentAnimation()const; }; \ No newline at end of file diff --git a/src/HamsterGame.cpp b/src/HamsterGame.cpp index 8bc05e0..266f1c7 100644 --- a/src/HamsterGame.cpp +++ b/src/HamsterGame.cpp @@ -13,6 +13,8 @@ HamsterGame::HamsterGame() } bool HamsterGame::OnUserCreate(){ + tv.Initialise({320,288},{1,1}); + tv.SetWorldOffset(-SCREEN_FRAME.pos); LoadGraphics(); LoadAnimations(); LoadLevel(); //THIS IS TEMPORARY. @@ -59,9 +61,9 @@ void HamsterGame::UpdateGame(const float fElapsedTime){ } void HamsterGame::DrawGame(){ + tv.FillRectDecal({10,10},{500.f,150.f},WHITE); + Hamster::DrawHamsters(tv); DrawDecal({},GetGFX("border.png").Decal()); - gameWindow.FillRectDecal({},{500.f,150.f},WHITE); - Hamster::DrawHamsters(gameWindow); } bool HamsterGame::OnUserUpdate(float fElapsedTime){ diff --git a/src/HamsterGame.h b/src/HamsterGame.h index 354d8aa..624b4a6 100644 --- a/src/HamsterGame.h +++ b/src/HamsterGame.h @@ -39,7 +39,7 @@ All rights reserved. #include #include "olcUTIL_Geometry2D.h" #include "olcUTIL_Animate2D.h" -#include "olcPGEX_ViewPort.h" +#include "olcPGEX_TransformedView.h" #include "olcUTIL_Camera2D.h" class HamsterGame : public olc::PixelGameEngine @@ -52,7 +52,7 @@ public: HamsterGame(); static geom2d::rectSCREEN_FRAME; - const ViewPort gameWindow{{SCREEN_FRAME.pos,SCREEN_FRAME.pos+vf2d{0.f,SCREEN_FRAME.size.y},SCREEN_FRAME.pos+SCREEN_FRAME.size,SCREEN_FRAME.pos+vf2d{SCREEN_FRAME.size.x,0.f}},{96,0}}; + TransformedView tv{}; public: bool OnUserCreate()override final; bool OnUserUpdate(float fElapsedTime)override final; diff --git a/src/olcPGEX_TransformedView.h b/src/olcPGEX_TransformedView.h new file mode 100644 index 0000000..7bef5f5 --- /dev/null +++ b/src/olcPGEX_TransformedView.h @@ -0,0 +1,770 @@ +/* + olcPGEX_TransformedView.h + + +-------------------------------------------------------------+ + | OneLoneCoder Pixel Game Engine Extension | + | Transformed View v1.09 | + +-------------------------------------------------------------+ + + NOTE: UNDER ACTIVE DEVELOPMENT - THERE ARE BUGS/GLITCHES + + What is this? + ~~~~~~~~~~~~~ + This extension provides drawing routines that are compatible with + changeable world and screen spaces. For example you can pan and + zoom, and all PGE drawing routines will automatically adopt the current + world scales and offsets. + + License (OLC-3) + ~~~~~~~~~~~~~~~ + + Copyright 2018 - 2024 OneLoneCoder.com + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions or derivations of source code must retain the above + copyright notice, this list of conditions and the following disclaimer. + + 2. Redistributions or derivative works in binary form must reproduce + the above copyright notice. This list of conditions and the following + disclaimer must be reproduced in the documentation and/or other + materials provided with the distribution. + + 3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + Links + ~~~~~ + YouTube: https://www.youtube.com/javidx9 + Discord: https://discord.gg/WhwHUMV + Twitter: https://www.twitter.com/javidx9 + Twitch: https://www.twitch.tv/javidx9 + GitHub: https://www.github.com/onelonecoder + Homepage: https://www.onelonecoder.com + + Author + ~~~~~~ + David Barr, aka javidx9, ŠOneLoneCoder 2019, 2020, 2021, 2022, 2023, 2024 + + Revisions: + 1.00: Initial Release + 1.01: Fix for rounding error when scaling to screen + 1.02: Added DrawLineDecal for convenience + 1.03: Removed std::floor from WorldToScreen() + Added HandlePanAndZoom(...) convenience function + Removed unused "range" facility in TileTransformView + 1.04: Added DrawPolygonDecal() for arbitrary polygons + 1.05: Clipped DrawSprite() to visible area, massive performance increase + 1.06: Fixed error in DrawLine() - Thanks CraisyDaisyRecords (& Fern)! + 1.07: +DrawRectDecal() + +GetPGE() + 1.08: +DrawPolygonDecal() with tint overload, akin to PGE + 1.09: +SetScaleExtents() - Sets range that world scale can exist within + +EnableScaleClamp() - Applies a range that scaling is clamped to + These are both useful for having zoom clamped between a min and max + without weird panning artefacts occuring +*/ + +#pragma once +#ifndef OLC_PGEX_TRANSFORMEDVIEW_H +#define OLC_PGEX_TRANSFORMEDVIEW_H + +#include "olcPixelGameEngine.h" + + + +namespace olc +{ + class TransformedView : public olc::PGEX + { + public: + TransformedView() = default; + virtual void Initialise(const olc::vi2d& vViewArea, const olc::vf2d& vPixelScale = { 1.0f, 1.0f }); + + olc::PixelGameEngine* GetPGE(); + + public: + void SetWorldOffset(const olc::vf2d& vOffset); + void MoveWorldOffset(const olc::vf2d& vDeltaOffset); + void SetWorldScale(const olc::vf2d& vScale); + void SetViewArea(const olc::vi2d& vViewArea); + olc::vf2d GetWorldTL() const; + olc::vf2d GetWorldBR() const; + olc::vf2d GetWorldVisibleArea() const; + void ZoomAtScreenPos(const float fDeltaZoom, const olc::vi2d& vPos); + void SetZoom(const float fZoom, const olc::vf2d& vPos); + void StartPan(const olc::vi2d& vPos); + void UpdatePan(const olc::vi2d& vPos); + void EndPan(const olc::vi2d& vPos); + const olc::vf2d& GetWorldOffset() const; + const olc::vf2d& GetWorldScale() const; + virtual olc::vf2d WorldToScreen(const olc::vf2d& vWorldPos) const; + virtual olc::vf2d ScreenToWorld(const olc::vf2d& vScreenPos) const; + virtual olc::vf2d ScaleToWorld(const olc::vf2d& vScreenSize) const; + virtual olc::vf2d ScaleToScreen(const olc::vf2d& vWorldSize) const; + virtual bool IsPointVisible(const olc::vf2d& vPos) const; + virtual bool IsRectVisible(const olc::vf2d& vPos, const olc::vf2d& vSize) const; + virtual void HandlePanAndZoom(const int nMouseButton = 2, const float fZoomRate = 0.1f, const bool bPan = true, const bool bZoom = true); + void SetScaleExtents(const olc::vf2d& vScaleMin, const olc::vf2d& vScaleMax); + void EnableScaleClamp(const bool bEnable); + + protected: + olc::vf2d m_vWorldOffset = { 0.0f, 0.0f }; + olc::vf2d m_vWorldScale = { 1.0f, 1.0f }; + olc::vf2d m_vRecipPixel = { 1.0f, 1.0f }; + olc::vf2d m_vPixelScale = { 1.0f, 1.0f }; + bool m_bPanning = false; + olc::vf2d m_vStartPan = { 0.0f, 0.0f }; + olc::vi2d m_vViewArea; + bool m_bZoomClamp = false; + olc::vf2d m_vMaxScale = { 0.0f, 0.0f }; + olc::vf2d m_vMinScale = { 0.0f, 0.0f }; + + public: // Hopefully, these should look familiar! + // Plots a single point + virtual bool Draw(float x, float y, olc::Pixel p = olc::WHITE); + bool Draw(const olc::vf2d& pos, olc::Pixel p = olc::WHITE); + // Draws a line from (x1,y1) to (x2,y2) + void DrawLine(float x1, float y1, float x2, float y2, olc::Pixel p = olc::WHITE, uint32_t pattern = 0xFFFFFFFF); + void DrawLine(const olc::vf2d& pos1, const olc::vf2d& pos2, olc::Pixel p = olc::WHITE, uint32_t pattern = 0xFFFFFFFF); + // Draws a circle located at (x,y) with radius + void DrawCircle(float x, float y, float radius, olc::Pixel p = olc::WHITE, uint8_t mask = 0xFF); + void DrawCircle(const olc::vf2d& pos, float radius, olc::Pixel p = olc::WHITE, uint8_t mask = 0xFF); + // Fills a circle located at (x,y) with radius + void FillCircle(float x, float y, float radius, olc::Pixel p = olc::WHITE); + void FillCircle(const olc::vf2d& pos, float radius, olc::Pixel p = olc::WHITE); + // Draws a rectangle at (x,y) to (x+w,y+h) + void DrawRect(float x, float y, float w, float h, olc::Pixel p = olc::WHITE); + void DrawRect(const olc::vf2d& pos, const olc::vf2d& size, olc::Pixel p = olc::WHITE); + // Fills a rectangle at (x,y) to (x+w,y+h) + void FillRect(float x, float y, float w, float h, olc::Pixel p = olc::WHITE); + void FillRect(const olc::vf2d& pos, const olc::vf2d& size, olc::Pixel p = olc::WHITE); + // Draws a triangle between points (x1,y1), (x2,y2) and (x3,y3) + void DrawTriangle(float x1, float y1, float x2, float y2, float x3, float y3, olc::Pixel p = olc::WHITE); + void DrawTriangle(const olc::vf2d& pos1, const olc::vf2d& pos2, const olc::vf2d& pos3, olc::Pixel p = olc::WHITE); + // Flat fills a triangle between points (x1,y1), (x2,y2) and (x3,y3) + void FillTriangle(float x1, float y1, float x2, float y2, float x3, float y3, olc::Pixel p = olc::WHITE); + void FillTriangle(const olc::vf2d& pos1, const olc::vf2d& pos2, const olc::vf2d& pos3, olc::Pixel p = olc::WHITE); + // Draws an entire sprite at location (x,y) + void DrawSprite(float x, float y, olc::Sprite* sprite, float scalex = 1, float scaley = 1, uint8_t flip = olc::Sprite::NONE); + void DrawSprite(const olc::vf2d& pos, olc::Sprite* sprite, const olc::vf2d& scale = { 1.0f, 1.0f }, uint8_t flip = olc::Sprite::NONE); + // Draws an area of a sprite at location (x,y), where the + // selected area is (ox,oy) to (ox+w,oy+h) + void DrawPartialSprite(float x, float y, Sprite* sprite, int32_t ox, int32_t oy, int32_t w, int32_t h, float scalex = 1, float scaley = 1, uint8_t flip = olc::Sprite::NONE); + void DrawPartialSprite(const olc::vf2d& pos, Sprite* sprite, const olc::vi2d& sourcepos, const olc::vi2d& size, const olc::vf2d& scale = { 1.0f, 1.0f }, uint8_t flip = olc::Sprite::NONE); + void DrawString(float x, float y, const std::string& sText, Pixel col, const olc::vf2d& scale); + void DrawString(const olc::vf2d& pos, const std::string& sText, const Pixel col, const olc::vf2d& scale); + + + // Draws a whole decal, with optional scale and tinting + void DrawDecal(const olc::vf2d& pos, olc::Decal* decal, const olc::vf2d& scale = { 1.0f,1.0f }, const olc::Pixel& tint = olc::WHITE); + // Draws a region of a decal, with optional scale and tinting + void DrawPartialDecal(const olc::vf2d& pos, olc::Decal* decal, const olc::vf2d& source_pos, const olc::vf2d& source_size, const olc::vf2d& scale = { 1.0f,1.0f }, const olc::Pixel& tint = olc::WHITE); + void DrawPartialDecal(const olc::vf2d& pos, const olc::vf2d& size, olc::Decal* decal, const olc::vf2d& source_pos, const olc::vf2d& source_size, const olc::Pixel& tint = olc::WHITE); + // Draws fully user controlled 4 vertices, pos(pixels), uv(pixels), colours + void DrawExplicitDecal(olc::Decal* decal, const olc::vf2d* pos, const olc::vf2d* uv, const olc::Pixel* col, uint32_t elements = 4); + //// Draws a decal with 4 arbitrary points, warping the texture to look "correct" + void DrawWarpedDecal(olc::Decal* decal, const olc::vf2d(&pos)[4], const olc::Pixel& tint = olc::WHITE); + void DrawWarpedDecal(olc::Decal* decal, const olc::vf2d* pos, const olc::Pixel& tint = olc::WHITE); + void DrawWarpedDecal(olc::Decal* decal, const std::array& pos, const olc::Pixel& tint = olc::WHITE); + //// As above, but you can specify a region of a decal source sprite + void DrawPartialWarpedDecal(olc::Decal* decal, const olc::vf2d(&pos)[4], const olc::vf2d& source_pos, const olc::vf2d& source_size, const olc::Pixel& tint = olc::WHITE); + void DrawPartialWarpedDecal(olc::Decal* decal, const olc::vf2d* pos, const olc::vf2d& source_pos, const olc::vf2d& source_size, const olc::Pixel& tint = olc::WHITE); + void DrawPartialWarpedDecal(olc::Decal* decal, const std::array& pos, const olc::vf2d& source_pos, const olc::vf2d& source_size, const olc::Pixel& tint = olc::WHITE); + //// Draws a decal rotated to specified angle, wit point of rotation offset + void DrawRotatedDecal(const olc::vf2d& pos, olc::Decal* decal, const float fAngle, const olc::vf2d& center = { 0.0f, 0.0f }, const olc::vf2d& scale = { 1.0f,1.0f }, const olc::Pixel& tint = olc::WHITE); + void DrawPartialRotatedDecal(const olc::vf2d& pos, olc::Decal* decal, const float fAngle, const olc::vf2d& center, const olc::vf2d& source_pos, const olc::vf2d& source_size, const olc::vf2d& scale = { 1.0f, 1.0f }, const olc::Pixel& tint = olc::WHITE); + // Draws a multiline string as a decal, with tiniting and scaling + void DrawStringDecal(const olc::vf2d& pos, const std::string& sText, const olc::Pixel col = olc::WHITE, const olc::vf2d& scale = { 1.0f, 1.0f }); + void DrawStringPropDecal(const olc::vf2d& pos, const std::string& sText, const olc::Pixel col = olc::WHITE, const olc::vf2d& scale = { 1.0f, 1.0f }); + // Draws a single shaded filled rectangle as a decal + void FillRectDecal(const olc::vf2d& pos, const olc::vf2d& size, const olc::Pixel col = olc::WHITE); + void DrawRectDecal(const olc::vf2d& pos, const olc::vf2d& size, const olc::Pixel col = olc::WHITE); + + // Draws a corner shaded rectangle as a decal + void GradientFillRectDecal(const olc::vf2d& pos, const olc::vf2d& size, const olc::Pixel colTL, const olc::Pixel colBL, const olc::Pixel colBR, const olc::Pixel colTR); + // Draws an arbitrary convex textured polygon using GPU + void DrawPolygonDecal(olc::Decal* decal, const std::vector& pos, const std::vector& uv, const olc::Pixel tint = olc::WHITE); + void DrawLineDecal(const olc::vf2d& pos1, const olc::vf2d& pos2, Pixel p = olc::WHITE); + void DrawPolygonDecal(olc::Decal* decal, const std::vector&pos, const std::vector&uv, const std::vector &tint); + void DrawPolygonDecal(olc::Decal* decal, const std::vector& pos, const std::vector& uv, const std::vector& colours, const olc::Pixel tint); + + +#if defined(OLC_PGEX_SHADER) + // Shader Specific + void DrawDecal(olc::Shade& shader, const olc::vf2d & pos, olc::Decal * decal, const olc::vf2d & scale = { 1.0f,1.0f }, const olc::Pixel & tint = olc::WHITE); + void DrawPartialDecal(olc::Shade& shader, const olc::vf2d& pos, olc::Decal* decal, const olc::vf2d& source_pos, const olc::vf2d& source_size, const olc::vf2d& scale = { 1.0f,1.0f }, const olc::Pixel& tint = olc::WHITE); + void DrawPartialDecal(olc::Shade& shader, const olc::vf2d& pos, const olc::vf2d& size, olc::Decal* decal, const olc::vf2d& source_pos, const olc::vf2d& source_size, const olc::Pixel& tint = olc::WHITE); +#endif + + + + }; + + class TileTransformedView : public TransformedView + { + public: + TileTransformedView() = default; + TileTransformedView(const olc::vi2d& vViewArea, const olc::vi2d& vTileSize); + + public: + olc::vi2d GetTopLeftTile() const; + olc::vi2d GetBottomRightTile() const; + olc::vi2d GetVisibleTiles() const; + olc::vi2d GetTileUnderScreenPos(const olc::vi2d& vPos) const; + const olc::vi2d GetTileOffset() const; + + }; +} + +#ifdef OLC_PGEX_TRANSFORMEDVIEW +#undef OLC_PGEX_TRANSFORMEDVIEW + +namespace olc +{ + olc::PixelGameEngine* TransformedView::GetPGE() + { + return pge; + } + + void TransformedView::Initialise(const olc::vi2d& vViewArea, const olc::vf2d& vPixelScale) + { + SetViewArea(vViewArea); + SetWorldScale(vPixelScale); + m_vPixelScale = vPixelScale; + m_vRecipPixel = 1.0f / m_vPixelScale; + } + + void TransformedView::SetWorldOffset(const olc::vf2d& vOffset) + { + m_vWorldOffset = vOffset; + } + + void TransformedView::MoveWorldOffset(const olc::vf2d& vDeltaOffset) + { + m_vWorldOffset += vDeltaOffset; + } + + void TransformedView::SetWorldScale(const olc::vf2d& vScale) + { + m_vWorldScale = vScale; + if (m_bZoomClamp) m_vWorldScale = m_vWorldScale.clamp(m_vMinScale, m_vMaxScale); + } + + void TransformedView::SetViewArea(const olc::vi2d& vViewArea) + { + m_vViewArea = vViewArea; + } + + olc::vf2d TransformedView::GetWorldTL() const + { + return TransformedView::ScreenToWorld({ 0,0 }); + } + + olc::vf2d TransformedView::GetWorldBR() const + { + return TransformedView::ScreenToWorld(m_vViewArea); + } + + olc::vf2d TransformedView::GetWorldVisibleArea() const + { + return GetWorldBR() - GetWorldTL(); + } + + void TransformedView::SetScaleExtents(const olc::vf2d& vScaleMin, const olc::vf2d& vScaleMax) + { + m_vMaxScale = vScaleMax; + m_vMinScale = vScaleMin; + } + + void TransformedView::EnableScaleClamp(const bool bEnable) + { + m_bZoomClamp = bEnable; + } + + void TransformedView::ZoomAtScreenPos(const float fDeltaZoom, const olc::vi2d& vPos) + { + olc::vf2d vOffsetBeforeZoom = ScreenToWorld(vPos); + m_vWorldScale *= fDeltaZoom; + if (m_bZoomClamp) m_vWorldScale = m_vWorldScale.clamp(m_vMinScale, m_vMaxScale); + olc::vf2d vOffsetAfterZoom = ScreenToWorld(vPos); + m_vWorldOffset += vOffsetBeforeZoom - vOffsetAfterZoom; + } + + void TransformedView::SetZoom(const float fZoom, const olc::vf2d& vPos) + { + olc::vf2d vOffsetBeforeZoom = ScreenToWorld(vPos); + m_vWorldScale = { fZoom, fZoom }; + if (m_bZoomClamp) m_vWorldScale = m_vWorldScale.clamp(m_vMinScale, m_vMaxScale); + olc::vf2d vOffsetAfterZoom = ScreenToWorld(vPos); + m_vWorldOffset += vOffsetBeforeZoom - vOffsetAfterZoom; + } + + void TransformedView::StartPan(const olc::vi2d& vPos) + { + m_bPanning = true; + m_vStartPan = olc::vf2d(vPos); + } + + void TransformedView::UpdatePan(const olc::vi2d& vPos) + { + if (m_bPanning) + { + m_vWorldOffset -= (olc::vf2d(vPos) - m_vStartPan) / m_vWorldScale; + m_vStartPan = olc::vf2d(vPos); + } + } + + void TransformedView::EndPan(const olc::vi2d& vPos) + { + UpdatePan(vPos); + m_bPanning = false; + } + + const olc::vf2d& TransformedView::GetWorldOffset() const + { + return m_vWorldOffset; + } + + const olc::vf2d& TransformedView::GetWorldScale() const + { + return m_vWorldScale; + } + + olc::vf2d TransformedView::WorldToScreen(const olc::vf2d& vWorldPos) const + { + olc::vf2d vFloat = ((vWorldPos - m_vWorldOffset) * m_vWorldScale); + //vFloat = { std::floor(vFloat.x + 0.5f), std::floor(vFloat.y + 0.5f) }; + return vFloat; + } + + olc::vf2d TransformedView::ScreenToWorld(const olc::vf2d& vScreenPos) const + { + return (olc::vf2d(vScreenPos) / m_vWorldScale) + m_vWorldOffset; + } + + olc::vf2d TransformedView::ScaleToWorld(const olc::vf2d& vScreenSize) const + { + return (olc::vf2d(vScreenSize) / m_vWorldScale); + } + + olc::vf2d TransformedView::ScaleToScreen(const olc::vf2d& vWorldSize) const + { + //olc::vf2d vFloat = (vWorldSize * m_vWorldScale) + olc::vf2d(0.5f, 0.5f); + //return vFloat.floor(); + return (vWorldSize * m_vWorldScale); + } + + bool TransformedView::IsPointVisible(const olc::vf2d & vPos) const + { + olc::vi2d vScreen = WorldToScreen(vPos); + return vScreen.x >= 0 && vScreen.x < m_vViewArea.x&& vScreen.y >= 0 && vScreen.y < m_vViewArea.y; + } + + bool TransformedView::IsRectVisible(const olc::vf2d& vPos, const olc::vf2d& vSize) const + { + olc::vi2d vScreenPos = WorldToScreen(vPos); + olc::vi2d vScreenSize = vSize * m_vWorldScale; + return (vScreenPos.x < 0 + m_vViewArea.x && vScreenPos.x + vScreenSize.x > 0 && vScreenPos.y < m_vViewArea.y&& vScreenPos.y + vScreenSize.y > 0); + } + + void TransformedView::HandlePanAndZoom(const int nMouseButton, const float fZoomRate, const bool bPan, const bool bZoom) + { + const auto& vMousePos = pge->GetMousePos(); + if (bPan) + { + if (pge->GetMouse(nMouseButton).bPressed) StartPan(vMousePos); + if (pge->GetMouse(nMouseButton).bHeld) UpdatePan(vMousePos); + if (pge->GetMouse(nMouseButton).bReleased) EndPan(vMousePos); + } + + if (bZoom) + { + if (pge->GetMouseWheel() > 0) ZoomAtScreenPos(1.0f + fZoomRate, vMousePos); + if (pge->GetMouseWheel() < 0) ZoomAtScreenPos(1.0f - fZoomRate, vMousePos); + } + } + + bool TransformedView::Draw(float x, float y, olc::Pixel p) + { + return Draw({ x, y }, p); + } + + bool TransformedView::Draw(const olc::vf2d & pos, olc::Pixel p) + { + return pge->Draw(WorldToScreen(pos), p); + } + + void TransformedView::DrawLine(float x1, float y1, float x2, float y2, olc::Pixel p, uint32_t pattern) + { + DrawLine({ x1, y1 }, { x2, y2 }, p, pattern); + } + + void TransformedView::DrawLine(const olc::vf2d & pos1, const olc::vf2d & pos2, olc::Pixel p, uint32_t pattern) + { + pge->DrawLine(WorldToScreen(pos1), WorldToScreen(pos2), p, pattern); + } + + void TransformedView::DrawCircle(float x, float y, float radius, olc::Pixel p, uint8_t mask) + { + DrawCircle({ x,y }, radius, p, mask); + } + + void TransformedView::DrawCircle(const olc::vf2d & pos, float radius, olc::Pixel p, uint8_t mask) + { + pge->DrawCircle(WorldToScreen(pos), int32_t(radius * m_vWorldScale.x), p, mask); + } + + void TransformedView::FillCircle(float x, float y, float radius, olc::Pixel p) + { + FillCircle({ x,y }, radius, p); + } + + void TransformedView::FillCircle(const olc::vf2d & pos, float radius, olc::Pixel p) + { + pge->FillCircle(WorldToScreen(pos), int32_t(radius * m_vWorldScale.x), p); + } + + void TransformedView::DrawRect(float x, float y, float w, float h, olc::Pixel p) + { + DrawRect({ x, y }, { w, h }, p); + } + + void TransformedView::DrawRect(const olc::vf2d & pos, const olc::vf2d & size, olc::Pixel p) + { + pge->DrawRect(WorldToScreen(pos), ((size * m_vWorldScale) + olc::vf2d(0.5f, 0.5f)).floor(), p); + } + + void TransformedView::FillRect(float x, float y, float w, float h, olc::Pixel p) + { + FillRect({ x, y }, { w, h }, p); + } + + void TransformedView::FillRect(const olc::vf2d & pos, const olc::vf2d & size, olc::Pixel p) + { + pge->FillRect(WorldToScreen(pos), size * m_vWorldScale, p); + } + + void TransformedView::DrawTriangle(float x1, float y1, float x2, float y2, float x3, float y3, olc::Pixel p) + { + DrawTriangle({ x1, y1 }, { x2, y2 }, { x3, y3 }, p); + } + + void TransformedView::DrawTriangle(const olc::vf2d & pos1, const olc::vf2d & pos2, const olc::vf2d & pos3, olc::Pixel p) + { + pge->DrawTriangle(WorldToScreen(pos1), WorldToScreen(pos2), WorldToScreen(pos3), p); + } + + void TransformedView::FillTriangle(float x1, float y1, float x2, float y2, float x3, float y3, olc::Pixel p) + { + FillTriangle({ x1, y1 }, { x2, y2 }, { x3, y3 }, p); + } + + void TransformedView::FillTriangle(const olc::vf2d & pos1, const olc::vf2d & pos2, const olc::vf2d & pos3, olc::Pixel p) + { + pge->FillTriangle(WorldToScreen(pos1), WorldToScreen(pos2), WorldToScreen(pos3), p); + } + + void TransformedView::DrawSprite(float x, float y, olc::Sprite* sprite, float scalex, float scaley, uint8_t flip) + { + DrawSprite({ x, y }, sprite, { scalex, scaley }, flip); + } + + void TransformedView::DrawSprite(const olc::vf2d & pos, olc::Sprite * sprite, const olc::vf2d & scale, uint8_t flip) + { + olc::vf2d vSpriteSize = olc::vf2d(float(sprite->width), float(sprite->height)); + if (IsRectVisible(pos, vSpriteSize * scale)) + { + olc::vf2d vSpriteScaledSize = vSpriteSize * m_vRecipPixel * m_vWorldScale * scale; + olc::vi2d vPixel; + olc::vi2d vSpritePixelStart = WorldToScreen(pos); + olc::vi2d vSpritePixelEnd = WorldToScreen((vSpriteSize * scale) + pos); + + olc::vi2d vScreenPixelStart = (vSpritePixelStart).max({0,0}); + olc::vi2d vScreenPixelEnd = (vSpritePixelEnd).min({ pge->ScreenWidth(),pge->ScreenHeight() }); + + olc::vf2d vPixelStep = 1.0f / vSpriteScaledSize; + + for (vPixel.y = vScreenPixelStart.y; vPixel.y < vScreenPixelEnd.y; vPixel.y++) + { + for (vPixel.x = vScreenPixelStart.x; vPixel.x < vScreenPixelEnd.x; vPixel.x++) + { + olc::vf2d vSample = olc::vf2d(vPixel - vSpritePixelStart) * vPixelStep; + pge->Draw(vPixel, sprite->Sample(vSample.x, vSample.y)); + } + } + } + } + + + void TransformedView::DrawPartialSprite(float x, float y, Sprite* sprite, int32_t ox, int32_t oy, int32_t w, int32_t h, float scalex, float scaley, uint8_t flip) + { + DrawPartialSprite({ x,y }, sprite, { ox,oy }, { w, h }, { scalex, scaley }, flip); + } + + void TransformedView::DrawPartialSprite(const olc::vf2d& pos, Sprite* sprite, const olc::vi2d& sourcepos, const olc::vi2d& size, const olc::vf2d& scale, uint8_t flip) + { + olc::vf2d vSpriteSize = size; + if (IsRectVisible(pos, size * scale)) + { + olc::vf2d vSpriteScaledSize = olc::vf2d(size) * m_vRecipPixel * m_vWorldScale * scale; + olc::vf2d vSpritePixelStep = 1.0f / olc::vf2d(float(sprite->width), float(sprite->height)); + olc::vi2d vPixel, vStart = WorldToScreen(pos), vEnd = vSpriteScaledSize + vStart; + olc::vf2d vScreenPixelStep = 1.0f / vSpriteScaledSize; + + for (vPixel.y = vStart.y; vPixel.y < vEnd.y; vPixel.y++) + { + for (vPixel.x = vStart.x; vPixel.x < vEnd.x; vPixel.x++) + { + olc::vf2d vSample = ((olc::vf2d(vPixel - vStart) * vScreenPixelStep) * size * vSpritePixelStep) + olc::vf2d(sourcepos) * vSpritePixelStep; + pge->Draw(vPixel, sprite->Sample(vSample.x, vSample.y)); + } + } + } + } + + void TransformedView::DrawString(float x, float y, const std::string& sText, Pixel col, const olc::vf2d& scale) + { + DrawString({ x, y }, sText, col, scale); + } + + void TransformedView::DrawString(const olc::vf2d& pos, const std::string& sText, const Pixel col, const olc::vf2d& scale) + { + olc::vf2d vOffset = { 0.0f, 0.0f }; + Pixel::Mode m = pge->GetPixelMode(); + + auto StringPlot = [&col](const int x, const int y, const olc::Pixel& pSource, const olc::Pixel& pDest) + { + return pSource.r > 1 ? col : pDest; + }; + + pge->SetPixelMode(StringPlot); + + for (auto c : sText) + { + if (c == '\n') + { + vOffset.x = 0.0f; vOffset.y += 8.0f * m_vRecipPixel.y * scale.y; + } + else + { + int32_t ox = ((c - 32) % 16) * 8; + int32_t oy = ((c - 32) / 16) * 8; + DrawPartialSprite(pos + vOffset, pge->GetFontSprite(), { ox, oy }, { 8, 8 }, scale); + vOffset.x += 8.0f * m_vRecipPixel.x * scale.x; + } + } + pge->SetPixelMode(m); + } + + + void TransformedView::DrawDecal(const olc::vf2d & pos, olc::Decal * decal, const olc::vf2d & scale, const olc::Pixel & tint) + { + pge->DrawDecal(WorldToScreen(pos), decal, scale * m_vWorldScale * m_vRecipPixel, tint); + } + + void TransformedView::DrawPartialDecal(const olc::vf2d & pos, olc::Decal * decal, const olc::vf2d & source_pos, const olc::vf2d & source_size, const olc::vf2d & scale, const olc::Pixel & tint) + { + pge->DrawPartialDecal(WorldToScreen(pos), decal, source_pos, source_size, scale * m_vWorldScale * m_vRecipPixel, tint); + } + + void TransformedView::DrawPartialDecal(const olc::vf2d & pos, const olc::vf2d & size, olc::Decal * decal, const olc::vf2d & source_pos, const olc::vf2d & source_size, const olc::Pixel & tint) + { + pge->DrawPartialDecal(WorldToScreen(pos), size * m_vWorldScale * m_vRecipPixel, decal, source_pos, source_size, tint); + } + + void TransformedView::DrawExplicitDecal(olc::Decal* decal, const olc::vf2d* pos, const olc::vf2d* uv, const olc::Pixel* col, uint32_t elements) + { + std::vector vTransformed(elements); + for (uint32_t n = 0; n < elements; n++) + vTransformed[n] = WorldToScreen(pos[n]); + pge->DrawExplicitDecal(decal, vTransformed.data(), uv, col, elements); + } + + void TransformedView::DrawWarpedDecal(olc::Decal* decal, const olc::vf2d* pos, const olc::Pixel& tint) + { + std::array vTransformed = + { { + WorldToScreen(pos[0]), WorldToScreen(pos[1]), + WorldToScreen(pos[2]), WorldToScreen(pos[3]), + } }; + + pge->DrawWarpedDecal(decal, vTransformed, tint); + } + + void TransformedView::DrawWarpedDecal(olc::Decal* decal, const olc::vf2d(&pos)[4], const olc::Pixel& tint) + { + DrawWarpedDecal(decal, &pos[0], tint); + } + + void TransformedView::DrawWarpedDecal(olc::Decal* decal, const std::array& pos, const olc::Pixel& tint) + { + DrawWarpedDecal(decal, pos.data(), tint); + } + + void TransformedView::DrawPartialWarpedDecal(olc::Decal* decal, const olc::vf2d(&pos)[4], const olc::vf2d& source_pos, const olc::vf2d& source_size, const olc::Pixel& tint) + { + DrawPartialWarpedDecal(decal, &pos[0], source_pos, source_size, tint); + } + + void TransformedView::DrawPartialWarpedDecal(olc::Decal* decal, const olc::vf2d* pos, const olc::vf2d& source_pos, const olc::vf2d& source_size, const olc::Pixel& tint) + { + std::array vTransformed = + { { + WorldToScreen(pos[0]), WorldToScreen(pos[1]), + WorldToScreen(pos[2]), WorldToScreen(pos[3]), + } }; + + pge->DrawPartialWarpedDecal(decal, vTransformed, source_pos, source_size, tint); + } + + void TransformedView::DrawPartialWarpedDecal(olc::Decal* decal, const std::array& pos, const olc::vf2d& source_pos, const olc::vf2d& source_size, const olc::Pixel& tint) + { + DrawPartialWarpedDecal(decal, pos.data(), source_pos, source_size, tint); + } + + void TransformedView::DrawRotatedDecal(const olc::vf2d & pos, olc::Decal * decal, const float fAngle, const olc::vf2d & center, const olc::vf2d & scale, const olc::Pixel & tint) + { + pge->DrawRotatedDecal(WorldToScreen(pos), decal, fAngle, center, scale * m_vWorldScale * m_vRecipPixel, tint); + } + + void TransformedView::DrawPartialRotatedDecal(const olc::vf2d & pos, olc::Decal * decal, const float fAngle, const olc::vf2d & center, const olc::vf2d & source_pos, const olc::vf2d & source_size, const olc::vf2d & scale, const olc::Pixel & tint) + { + pge->DrawPartialRotatedDecal(WorldToScreen(pos), decal, fAngle, center, source_pos, source_size, scale * m_vWorldScale * m_vRecipPixel, tint); + } + + void TransformedView::DrawStringDecal(const olc::vf2d & pos, const std::string & sText, const olc::Pixel col, const olc::vf2d & scale) + { + pge->DrawStringDecal(WorldToScreen(pos), sText, col, scale * m_vWorldScale * m_vRecipPixel); + } + + void TransformedView::DrawStringPropDecal(const olc::vf2d & pos, const std::string & sText, const olc::Pixel col, const olc::vf2d & scale ) + { + pge->DrawStringPropDecal(WorldToScreen(pos), sText, col, scale * m_vWorldScale * m_vRecipPixel); + } + + void TransformedView::FillRectDecal(const olc::vf2d & pos, const olc::vf2d & size, const olc::Pixel col) + { + pge->FillRectDecal(WorldToScreen(pos), (size * m_vWorldScale).ceil(), col); + } + + void TransformedView::DrawRectDecal(const olc::vf2d& pos, const olc::vf2d& size, const olc::Pixel col) + { + pge->DrawRectDecal(WorldToScreen(pos), (size * m_vWorldScale).ceil(), col); + } + + void TransformedView::DrawLineDecal(const olc::vf2d& pos1, const olc::vf2d& pos2, Pixel p) + { + pge->DrawLineDecal(WorldToScreen(pos1), WorldToScreen(pos2), p); + } + + void TransformedView::GradientFillRectDecal(const olc::vf2d & pos, const olc::vf2d & size, const olc::Pixel colTL, const olc::Pixel colBL, const olc::Pixel colBR, const olc::Pixel colTR) + { + pge->GradientFillRectDecal(WorldToScreen(pos), size * m_vWorldScale, colTL, colBL, colBR, colTR); + } + + void TransformedView::DrawPolygonDecal(olc::Decal* decal, const std::vector& pos, const std::vector& uv, const olc::Pixel tint) + { + std::vector vTransformed(pos.size()); + for (uint32_t n = 0; n < pos.size(); n++) + vTransformed[n] = WorldToScreen(pos[n]); + pge->DrawPolygonDecal(decal, vTransformed, uv, tint); + } + + void TransformedView::DrawPolygonDecal(olc::Decal* decal, const std::vector& pos, const std::vector& uv, const std::vector &tint) + { + std::vector vTransformed(pos.size()); + for (uint32_t n = 0; n < pos.size(); n++) + vTransformed[n] = WorldToScreen(pos[n]); + pge->DrawPolygonDecal(decal, vTransformed, uv, tint); + } + + void TransformedView::DrawPolygonDecal(olc::Decal* decal, const std::vector& pos, const std::vector& uv, const std::vector& colours, const olc::Pixel tint) + { + std::vector vTransformed(pos.size()); + for (uint32_t n = 0; n < pos.size(); n++) + vTransformed[n] = WorldToScreen(pos[n]); + pge->DrawPolygonDecal(decal, vTransformed, uv, colours, tint); + } + + + +#if defined (OLC_PGEX_SHADER) + + void TransformedView::DrawDecal(olc::Shade &shade, const olc::vf2d& pos, olc::Decal* decal, const olc::vf2d& scale, const olc::Pixel& tint) + { + shade.DrawDecal(WorldToScreen(pos), decal, scale * m_vWorldScale * m_vRecipPixel, tint); + } + + void TransformedView::DrawPartialDecal(olc::Shade& shade, const olc::vf2d& pos, olc::Decal* decal, const olc::vf2d& source_pos, const olc::vf2d& source_size, const olc::vf2d& scale, const olc::Pixel& tint) + { + shade.DrawPartialDecal(WorldToScreen(pos), decal, source_pos, source_size, scale * m_vWorldScale * m_vRecipPixel, tint); + } + + void TransformedView::DrawPartialDecal(olc::Shade& shade, const olc::vf2d& pos, const olc::vf2d& size, olc::Decal* decal, const olc::vf2d& source_pos, const olc::vf2d& source_size, const olc::Pixel& tint) + { + shade.DrawPartialDecal(WorldToScreen(pos), size * m_vWorldScale * m_vRecipPixel, decal, source_pos, source_size, tint); + } + +#endif + + + + + + + + ///////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////// + + + TileTransformedView::TileTransformedView(const olc::vi2d& vViewArea, const olc::vi2d& vTileSize) + { + Initialise(vViewArea, vTileSize); + } + + + olc::vi2d TileTransformedView::GetTopLeftTile() const + { + return ScreenToWorld({ 0,0 }).floor(); + } + + olc::vi2d TileTransformedView::GetBottomRightTile() const + { + return ScreenToWorld(m_vViewArea).ceil(); + } + + olc::vi2d TileTransformedView::GetVisibleTiles() const + { + return GetBottomRightTile() - GetTopLeftTile(); + } + + olc::vi2d TileTransformedView::GetTileUnderScreenPos(const olc::vi2d& vPos) const + { + return ScreenToWorld(vPos).floor(); + } + + const olc::vi2d TileTransformedView::GetTileOffset() const + { + return { int32_t((m_vWorldOffset.x - std::floor(m_vWorldOffset.x)) * m_vWorldScale.x), + int32_t((m_vWorldOffset.y - std::floor(m_vWorldOffset.y)) * m_vWorldScale.y) }; + } +} + +#endif +#endif diff --git a/src/olcPGEX_ViewPort.h b/src/olcPGEX_ViewPort.h deleted file mode 100644 index 307b25e..0000000 --- a/src/olcPGEX_ViewPort.h +++ /dev/null @@ -1,722 +0,0 @@ -#pragma once - -#include "olcPixelGameEngine.h" - -#include -#include -#include -#include -#include -#include - -// Declarations -namespace olc { - class ViewPort : public olc::PGEX { - public: - ViewPort(); - //Define a set of vertices to construct this viewport with. Winding order is counter-clockwise. - ViewPort(std::vector vertices, vf2d offset = {0, 0}); - virtual ~ViewPort(); - void addPoint(vf2d point); - void clear(); - void drawEdges(); - void setOffset(vf2d offset); - - static ViewPort rectViewPort(vf2d topLeft, - vf2d size, - olc::vf2d offset = {0, 0}); - - void DrawDecal(const olc::vf2d &pos, - olc::Decal *decal, - const olc::vf2d &scale = {1.0f, 1.0f}, - const olc::Pixel &tint = olc::WHITE) const; - void DrawPartialDecal(const olc::vf2d &pos, - olc::Decal *decal, - const olc::vf2d &source_pos, - const olc::vf2d &source_size, - const olc::vf2d &scale = {1.0f, 1.0f}, - const olc::Pixel &tint = olc::WHITE) const; - void DrawPartialDecal(const vf2d &pos, - const vf2d &size, - Decal *decal, - const vf2d source_pos, - const vf2d &source_size, - const Pixel &tint = olc::WHITE) const; - void DrawExplicitDecal(olc::Decal *decal, - const olc::vf2d *pos, - const olc::vf2d *uv, - const olc::Pixel *col, - uint32_t elements = 4) const; - void DrawWarpedDecal(Decal *decal, - const vf2d (&pos)[4], - const Pixel &tint = WHITE) const; - void DrawWarpedDecal(Decal *decal, - const vf2d *pos, - const Pixel &tint = WHITE) const; - void DrawWarpedDecal(Decal *decal, - const std::array &pos, - const Pixel &tint = WHITE) const; - void DrawPartialWarpedDecal(Decal *decal, - const vf2d (&pos)[4], - const vf2d &source_pos, - const vf2d &source_size, - const Pixel &tint = WHITE) const; - void DrawPartialWarpedDecal(Decal *decal, - const vf2d *pos, - const vf2d &source_pos, - const vf2d &source_size, - const Pixel &tint = WHITE) const; - void DrawPartialWarpedDecal(Decal *decal, - const std::array &pos, - const vf2d &source_pos, - const vf2d &source_size, - const Pixel &tint = WHITE) const; - void DrawRotatedDecal(const vf2d &pos, - Decal *decal, - const float fAngle, - const vf2d ¢er = {0.0f, 0.0f}, - const vf2d &scale = {1.0f, 1.0f}, - const Pixel &tint = WHITE) const; - void DrawPartialRotatedDecal(const vf2d &pos, - Decal *decal, - const float fAngle, - const vf2d ¢er, - const vf2d &source_pos, - const vf2d &source_size, - const vf2d &scale = {1.0f, 1.0f}, - const Pixel &tint = WHITE) const; - void DrawRectDecal(const vf2d &pos, - const vf2d &size, - const Pixel col = WHITE) const; - void FillRectDecal(const vf2d &pos, - const vf2d &size, - const Pixel col = WHITE) const; - void GradientFillRectDecal(const vf2d &pos, - const vf2d &size, - const Pixel colTL, - const Pixel colBL, - const Pixel colBR, - const Pixel colTR) const; - void DrawPolygonDecal(Decal *decal, - const std::vector &pos, - const std::vector &uv, - const Pixel tint = WHITE) const; - void DrawPolygonDecal(Decal *decal, - const std::vector &pos, - const std::vector &depth, - const std::vector &uv, - const Pixel tint = WHITE) const; - void DrawPolygonDecal(Decal *decal, - const std::vector &pos, - const std::vector &uv, - const std::vector &tint) const; - void DrawLineDecal(const vf2d &pos1, - const vf2d &pos2, - Pixel p = WHITE) const; - - private: - void drawClippedDecal(Decal *decal, - const vf2d *points, - const vf2d *uvs, - const Pixel *col, - uint32_t elements = 0) const; - void drawClippedPolygonDecal(Decal *decal, - const vf2d *points, - const vf2d *uvs, - const float *depth, - const Pixel tint, - uint32_t elements = 0) const; - - static bool ccw(vf2d A,vf2d B,vf2d C); - static bool intersect(vf2d A,vf2d B,vf2d C,vf2d D); - static float lineSegmentIntersect(vf2d lineA, - vf2d lineB, - vf2d segmentA, - vf2d segmentB); - static float directionFromLine(vf2d lineA, vf2d lineB, vf2d point); - - std::vector clipVertices; - olc::vf2d offset; - }; -} // namespace olc - -// Definitions - -#ifdef OLC_PGEX_VIEWPORT -#undef OLC_PGEX_VIEWPORT - -olc::ViewPort::ViewPort() { -} -olc::ViewPort::~ViewPort() { -} - -olc::ViewPort::ViewPort(std::vector vertices, olc::vf2d offset) - : clipVertices{vertices}, - offset{offset} { -} - -void olc::ViewPort::addPoint(vf2d point) { - clipVertices.push_back(point); -} - -void olc::ViewPort::clear() { - clipVertices.clear(); -} - -void olc::ViewPort::drawEdges() { - for (auto i = 0u; i < clipVertices.size(); i++) { - auto current = clipVertices[i] + offset; - auto next = clipVertices[(i + 1) % clipVertices.size()] + offset; - - pge->DrawLineDecal(current, next, olc::RED); - } -} - -void olc::ViewPort::setOffset(vf2d offset) { - this->offset = offset; -} - -olc::ViewPort - olc::ViewPort::rectViewPort(vf2d topLeft, vf2d size, olc::vf2d offset) { - return {{ - topLeft, - {topLeft.x, topLeft.y + size.y}, - topLeft + size, - {topLeft.x + size.x, topLeft.y}, - }, - offset}; -} - -void olc::ViewPort::DrawDecal(const olc::vf2d &pos, - olc::Decal *decal, - const olc::vf2d &scale, - const olc::Pixel &tint) const { - std::vector points{ - pos, - {pos.x, pos.y + decal->sprite->height * scale.y}, - {pos.x + decal->sprite->width * scale.x, - pos.y + decal->sprite->height * scale.y}, - {pos.x + decal->sprite->width * scale.x, pos.y}, - }; - DrawWarpedDecal(decal, points.data(), tint); -} - -void olc::ViewPort::DrawPartialDecal(const olc::vf2d &pos, - olc::Decal *decal, - const olc::vf2d &source_pos, - const olc::vf2d &source_size, - const olc::vf2d &scale, - const olc::Pixel &tint) const { - DrawPartialDecal(pos, source_size * scale, decal, source_pos, source_size, tint); -} - -void olc::ViewPort::DrawPartialDecal(const vf2d &pos, - const vf2d &size, - Decal *decal, - const vf2d source_pos, - const vf2d &source_size, - const Pixel &tint) const { - std::vector points{ - pos, - {pos.x, pos.y + size.y}, - pos + size, - {pos.x + size.x, pos.y}, - }; - DrawPartialWarpedDecal(decal, points.data(), source_pos, source_size, tint); -} - -void olc::ViewPort::DrawExplicitDecal(olc::Decal *decal, - const olc::vf2d *pos, - const olc::vf2d *uv, - const olc::Pixel *col, - uint32_t elements) const { - drawClippedDecal(decal, pos, uv, col, elements); -} - -void olc::ViewPort::DrawWarpedDecal(Decal *decal, - const vf2d (&pos)[4], - const Pixel &tint) const { - DrawWarpedDecal(decal, (const vf2d *)pos, tint); -} -void olc::ViewPort::DrawWarpedDecal(Decal *decal, - const vf2d *pos, - const Pixel &tint) const { - std::vector w{ 1, 1, 1, 1 }; - std::vector newPos; - newPos.resize(4); - std::vector uvs{ - {0, 0}, - {0, 1}, - {1, 1}, - {1, 0}, - }; - std::vector cols{ - tint, - tint, - tint, - tint, - }; - - olc::vf2d vInvScreenSize={ 1.0f / pge->GetScreenSize().x, 1.0f / pge->GetScreenSize().y }; - - olc::vf2d center; - float rd = ((pos[2].x - pos[0].x) * (pos[3].y - pos[1].y) - (pos[3].x - pos[1].x) * (pos[2].y - pos[0].y)); - if (rd != 0) - { - rd = 1.0f / rd; - float rn = ((pos[3].x - pos[1].x) * (pos[0].y - pos[1].y) - (pos[3].y - pos[1].y) * (pos[0].x - pos[1].x)) * rd; - float sn = ((pos[2].x - pos[0].x) * (pos[0].y - pos[1].y) - (pos[2].y - pos[0].y) * (pos[0].x - pos[1].x)) * rd; - if (!(rn < 0.f || rn > 1.f || sn < 0.f || sn > 1.f)) center = pos[0] + rn * (pos[2] - pos[0]); - float d[4]; for (int i = 0; i < 4; i++) d[i] = (pos[i] - center).mag(); - for (int i = 0; i < 4; i++) - { - float q = d[i] == 0.0f ? 1.0f : (d[i] + d[(i + 2) & 3]) / d[(i + 2) & 3]; - uvs[i] *= q; w[i] *= q; - } - - drawClippedPolygonDecal(decal, pos, uvs.data(), w.data(), tint, 4); - } -} -void olc::ViewPort::DrawWarpedDecal(Decal *decal, - const std::array &pos, - const Pixel &tint) const { - DrawWarpedDecal(decal, pos.data(), tint); -} - -void olc::ViewPort::DrawPartialWarpedDecal(Decal *decal, - const vf2d (&pos)[4], - const vf2d &source_pos, - const vf2d &source_size, - const Pixel &tint) const { - DrawPartialWarpedDecal(decal, - (const vf2d *)pos, - source_pos, - source_size, - tint); -} - -void olc::ViewPort::DrawPartialWarpedDecal(Decal *decal, - const vf2d *pos, - const vf2d &source_pos, - const vf2d &source_size, - const Pixel &tint) const { - olc::vf2d sourceUvPos = - source_pos - / olc::vf2d{static_cast(decal->sprite->width), - static_cast(decal->sprite->height)}; - olc::vf2d sourceUvSize = - source_size - / olc::vf2d{static_cast(decal->sprite->width), - static_cast(decal->sprite->height)}; - std::vector uvs{ - sourceUvPos, - {sourceUvPos.x, sourceUvPos.y + sourceUvSize.y}, - sourceUvPos + sourceUvSize, - {sourceUvPos.x + sourceUvSize.x, sourceUvPos.y}, - }; - std::vector cols{ - tint, - tint, - tint, - tint, - }; - - std::vectorws{1,1,1,1}; - - olc::vf2d center; - float rd = ((pos[2].x - pos[0].x) * (pos[3].y - pos[1].y) - (pos[3].x - pos[1].x) * (pos[2].y - pos[0].y)); - if (rd != 0) - { - rd = 1.0f / rd; - float rn = ((pos[3].x - pos[1].x) * (pos[0].y - pos[1].y) - (pos[3].y - pos[1].y) * (pos[0].x - pos[1].x)) * rd; - float sn = ((pos[2].x - pos[0].x) * (pos[0].y - pos[1].y) - (pos[2].y - pos[0].y) * (pos[0].x - pos[1].x)) * rd; - if (!(rn < 0.f || rn > 1.f || sn < 0.f || sn > 1.f)) center = pos[0] + rn * (pos[2] - pos[0]); - float d[4]; for (int i = 0; i < 4; i++) d[i] = (pos[i] - center).mag(); - for (int i = 0; i < 4; i++) - { - float q = d[i] == 0.0f ? 1.0f : (d[i] + d[(i + 2) & 3]) / d[(i + 2) & 3]; - uvs[i] *= q; ws[i] *= q; - } - - drawClippedPolygonDecal(decal, pos, uvs.data(), ws.data(), tint, 4); - } -} - -void olc::ViewPort::DrawPartialWarpedDecal(Decal *decal, - const std::array &pos, - const vf2d &source_pos, - const vf2d &source_size, - const Pixel &tint) const { - DrawPartialWarpedDecal(decal, pos.data(), source_pos, source_size, tint); -} - -void olc::ViewPort::DrawRotatedDecal(const vf2d &pos, - Decal *decal, - const float fAngle, - const vf2d ¢er, - const vf2d &scale, - const Pixel &tint) const { - auto sin = std::sin(fAngle); - auto cos = std::cos(fAngle); - - std::vector points{ - -center * scale, - olc::vf2d{-center.x, decal->sprite->height - center.y} * scale, - olc::vf2d{decal->sprite->width - center.x, - decal->sprite->height - center.y} - * scale, - olc::vf2d{decal->sprite->width - center.x, -center.y} * scale, - }; - - for (auto i = 0u; i < points.size(); i++) { - points[i] = pos - + olc::vf2d{points[i].x * cos - points[i].y * sin, - points[i].x * sin + points[i].y * cos}; - } - - DrawWarpedDecal(decal, points.data(), tint); -} - -void olc::ViewPort::DrawPartialRotatedDecal(const vf2d &pos, - Decal *decal, - const float fAngle, - const vf2d ¢er, - const vf2d &source_pos, - const vf2d &source_size, - const vf2d &scale, - const Pixel &tint) const { - auto sin = std::sin(fAngle); - auto cos = std::cos(fAngle); - - std::vector points{ - -center * scale, - olc::vf2d{-center.x, source_size.y - center.y} * scale, - (source_size - center) * scale, - olc::vf2d{source_size.x - center.x, -center.y} * scale, - }; - - for (auto i = 0u; i < points.size(); i++) { - points[i] = pos - + olc::vf2d{points[i].x * cos - points[i].y * sin, - points[i].x * sin + points[i].y * cos}; - } - - DrawPartialWarpedDecal(decal, points.data(), source_pos, source_size, tint); -} - -void olc::ViewPort::DrawRectDecal(const vf2d &pos, - const vf2d &size, - const Pixel col) const { - std::vector points{ - pos, - {pos.x, pos.y + size.y}, - pos + size, - {pos.x + size.x, pos.y}, - }; - - // Ideally we use the wireframe mode just like the PGE, - // however we can't save the current decal mode which - // can impact some applications so instead we draw 4 - // lines. - - DrawLineDecal(points[0],points[1],col); - DrawLineDecal(points[1],points[2],col); - DrawLineDecal(points[2],points[3],col); - DrawLineDecal(points[3],points[0],col); -} - -void olc::ViewPort::FillRectDecal(const vf2d &pos, - const vf2d &size, - const Pixel col) const { - std::vector points{ - pos, - {pos.x, pos.y + size.y}, - pos + size, - {pos.x + size.x, pos.y}, - }; - std::vector uvs{ - {0, 0}, - {0, 1}, - {1, 1}, - {1, 0}, - }; - - DrawPolygonDecal(nullptr, points, uvs, col); -} - -void olc::ViewPort::GradientFillRectDecal(const vf2d &pos, - const vf2d &size, - const Pixel colTL, - const Pixel colBL, - const Pixel colBR, - const Pixel colTR) const { - std::vector points{ - pos, - {pos.x, pos.y + size.y}, - pos + size, - {pos.x + size.x, pos.y}, - }; - - std::vector uvs{ - {0, 0}, - {0, 1}, - {1, 1}, - {1, 0}, - }; - - std::vector colors{ - colTL, - colBL, - colBR, - colTR, - }; - - drawClippedDecal(nullptr, points.data(), uvs.data(), colors.data(), points.size()); -} - -void olc::ViewPort::DrawPolygonDecal(Decal *decal, - const std::vector &pos, - const std::vector &uv, - const Pixel tint) const { - std::vector colors; - colors.resize(pos.size()); - for (auto i = 0u; i < colors.size(); i++) { - colors[i] = tint; - } - - drawClippedDecal(decal, pos.data(), uv.data(), colors.data(), pos.size()); -} - -void olc::ViewPort::DrawPolygonDecal(Decal *decal, - const std::vector &pos, - const std::vector &depth, - const std::vector &uv, - const Pixel tint) const { - drawClippedPolygonDecal(decal, pos.data(), uv.data(), depth.data(), tint, pos.size()); -} - -void olc::ViewPort::DrawPolygonDecal(Decal *decal, - const std::vector &pos, - const std::vector &uv, - const std::vector &tint) const { - drawClippedDecal(decal, pos.data(), uv.data(), tint.data(), pos.size()); -} - -void olc::ViewPort::DrawLineDecal(const vf2d &pos1, - const vf2d &pos2, - Pixel p) const { - vf2d posA = pos1; - vf2d posB = pos2; - - for (auto i = 0u; i < clipVertices.size(); i++) { - auto clipA = clipVertices[i] - offset; - auto clipB = clipVertices[(i + 1) % clipVertices.size()] - offset; - - auto intersection = lineSegmentIntersect(clipA, clipB, posA, posB); - if (intersection < 0 || intersection > 1) { - continue; - } - - auto clipDirection = directionFromLine(clipA, clipB, posA); - auto intersectionPoint = posA + (posB - posA) * intersection; - - if (clipDirection >= 0) { - posA = intersectionPoint; - } else { - posB = intersectionPoint; - } - } - - - // Inside check. Draw a ray to the edge of the screen and count the times - // it intersects. When odd, we are inside a shape, when even we are outside - // of it. - - vf2d leftEdgeA = {0.f,posA.y}; - vf2d leftEdgeB = {0.f,posB.y}; - - int leftEdgeIntersectionsA = 0; - int leftEdgeIntersectionsB = 0; - for (auto i = 0u; i < clipVertices.size(); i++) { - auto clipA = clipVertices[i] - offset; - auto clipB = clipVertices[(i + 1) % clipVertices.size()] - offset; - auto leftEdgeIntersectA = intersect(clipA, clipB, leftEdgeA, posA); - auto leftEdgeIntersectB = intersect(clipA, clipB, leftEdgeB, posB); - - if (leftEdgeIntersectA) { - leftEdgeIntersectionsA++; - } - if (leftEdgeIntersectB) { - leftEdgeIntersectionsB++; - } - } - - // If we found an intersection, we are drawing this line. - // - // Otherwise, if either count is odd, one point is at - // least inside the shape, so render it. - if (leftEdgeIntersectionsA % 2 == 1 || leftEdgeIntersectionsB % 2 == 1) { - pge->DrawLineDecal(posA, posB, p); - } -} - -void olc::ViewPort::drawClippedDecal(Decal *decal, - const vf2d *points, - const vf2d *uvs, - const Pixel *col, - uint32_t elements) const { - std::vector outputList{points, points + elements}; - std::vector outputUvs{uvs, uvs + elements}; - std::vector outputCols{col, col + elements}; - - for (auto i = 0u; i < clipVertices.size(); i++) { - auto clipA = clipVertices[i] - offset; - auto clipB = clipVertices[(i + 1) % clipVertices.size()] - offset; - - auto inputList{outputList}; - auto inputUvs{outputUvs}; - auto inputCols{outputCols}; - outputList.clear(); - outputUvs.clear(); - outputCols.clear(); - - for (auto i = 0u; i < inputList.size(); i++) { - auto polygonA = inputList[i]; - auto polygonB = inputList[(i + 1) % inputList.size()]; - auto uvA = inputUvs[i]; - auto uvB = inputUvs[(i + 1) % inputList.size()]; - auto colA = inputCols[i]; - auto colB = inputCols[(i + 1) % inputList.size()]; - - auto intersection = - lineSegmentIntersect(clipA, clipB, polygonA, polygonB); - auto intersectionPoint = - polygonA + (polygonB - polygonA) * intersection; - auto intersectionUv = uvA + (uvB - uvA) * intersection; - auto intersectionCol = PixelLerp(colA, colB, intersection); - - float aDirection = directionFromLine(clipA, clipB, polygonA); - float bDirection = directionFromLine(clipA, clipB, polygonB); - - if (bDirection <= 0) { - if (aDirection > 0) { - outputList.push_back(intersectionPoint); - outputUvs.push_back(intersectionUv); - outputCols.push_back(intersectionCol); - } - outputList.push_back(polygonB); - outputUvs.push_back(uvB); - outputCols.push_back(colB); - } else if (aDirection <= 0) { - outputList.push_back(intersectionPoint); - outputUvs.push_back(intersectionUv); - outputCols.push_back(intersectionCol); - } - } - } - - if (outputList.size() == 0) { - return; - } - - for (auto &point : outputList) { - point += offset; - } - - pge->DrawExplicitDecal(decal, - outputList.data(), - outputUvs.data(), - outputCols.data(), - outputList.size()); -} -void olc::ViewPort::drawClippedPolygonDecal(Decal *decal, - const vf2d *points, - const vf2d *uvs, - const float *depth, - const Pixel tint, - uint32_t elements) const { - std::vector outputList{points, points + elements}; - std::vector outputUvs{uvs, uvs + elements}; - std::vector outputDepths{depth, depth + elements}; - - for (auto i = 0u; i < clipVertices.size(); i++) { - auto clipA = clipVertices[i] - offset; - auto clipB = clipVertices[(i + 1) % clipVertices.size()] - offset; - - auto inputList{outputList}; - auto inputUvs{outputUvs}; - auto inputWs{outputDepths}; - outputList.clear(); - outputUvs.clear(); - outputDepths.clear(); - - for (auto i = 0u; i < inputList.size(); i++) { - auto polygonA = inputList[i]; - auto polygonB = inputList[(i + 1) % inputList.size()]; - auto uvA = inputUvs[i]; - auto uvB = inputUvs[(i + 1) % inputList.size()]; - auto Wa = inputWs[i]; - auto Wb = inputWs[(i + 1) % inputList.size()]; - - auto intersection = - lineSegmentIntersect(clipA, clipB, polygonA, polygonB); - auto intersectionPoint = - polygonA + (polygonB - polygonA) * intersection; - auto intersectionUv = uvA + (uvB - uvA) * intersection; - auto intersectionDepth = Wa + (Wb - Wa) * intersection; - - float aDirection = directionFromLine(clipA, clipB, polygonA); - float bDirection = directionFromLine(clipA, clipB, polygonB); - - if (bDirection <= 0) { - if (aDirection > 0) { - outputList.push_back(intersectionPoint); - outputUvs.push_back(intersectionUv); - outputDepths.push_back(intersectionDepth); - } - outputList.push_back(polygonB); - outputUvs.push_back(uvB); - outputDepths.push_back(Wb); - } else if (aDirection <= 0) { - outputList.push_back(intersectionPoint); - outputUvs.push_back(intersectionUv); - outputDepths.push_back(intersectionDepth); - } - } - } - - for (auto &point : outputList) { - point += offset; - } - - pge->DrawPolygonDecal(decal, - outputList, - outputDepths, - outputUvs, - tint); -} - -bool olc::ViewPort::ccw(vf2d A,vf2d B,vf2d C) { - return (C.y-A.y) * (B.x-A.x) > (B.y-A.y) * (C.x-A.x); -} - -bool olc::ViewPort::intersect(vf2d A,vf2d B,vf2d C,vf2d D) { - return ccw(A,C,D) != ccw(B,C,D) and ccw(A,B,C) != ccw(A,B,D); -} - -float olc::ViewPort::lineSegmentIntersect(vf2d lineA, - vf2d lineB, - vf2d segmentA, - vf2d segmentB) { - return ((lineA.x - segmentA.x) * (lineA.y - lineB.y) - - (lineA.y - segmentA.y) * (lineA.x - lineB.x)) - / ((lineA.x - lineB.x) * (segmentA.y - segmentB.y) - - (lineA.y - lineB.y) * (segmentA.x - segmentB.x)); -} - -float olc::ViewPort::directionFromLine(vf2d lineA, vf2d lineB, vf2d point) { - return (lineB.x - lineA.x) * (point.y - lineA.y) - - (point.x - lineA.x) * (lineB.y - lineA.y); -} - -#endif \ No newline at end of file diff --git a/src/olcPixelGameEngine.cpp b/src/olcPixelGameEngine.cpp index fd14497..b756e9c 100644 --- a/src/olcPixelGameEngine.cpp +++ b/src/olcPixelGameEngine.cpp @@ -1,5 +1,5 @@ #include "olcUTIL_Geometry2D.h" #define OLC_PGE_APPLICATION #include "olcPixelGameEngine.h" -#define OLC_PGEX_VIEWPORT -#include "olcPGEX_ViewPort.h" +#define OLC_PGEX_TRANSFORMEDVIEW +#include "olcPGEX_TransformedView.h" \ No newline at end of file