From 2b20570eb37402770c1a653fc6173f6d6294a922 Mon Sep 17 00:00:00 2001 From: sigonasr2 Date: Fri, 16 Aug 2024 20:02:33 -0500 Subject: [PATCH] Remove dependency on olcPGEX_ViewPort. Add olcPGEX_TransformedView. Update border to be in front of the rest of the game drawing. --- assets/border.png | Bin 12220 -> 10875 bytes assets/border.xcf | Bin 115351 -> 130334 bytes hamster.vcxproj | 5 +- hamster.vcxproj.filters | 6 +- src/Hamster.cpp | 4 +- src/Hamster.h | 3 +- src/HamsterGame.cpp | 6 +- src/HamsterGame.h | 4 +- src/olcPGEX_TransformedView.h | 770 ++++++++++++++++++++++++++++++++++ src/olcPGEX_ViewPort.h | 722 ------------------------------- src/olcPixelGameEngine.cpp | 4 +- 11 files changed, 788 insertions(+), 736 deletions(-) create mode 100644 src/olcPGEX_TransformedView.h delete mode 100644 src/olcPGEX_ViewPort.h diff --git a/assets/border.png b/assets/border.png index b4cc9ff86d1cd0d91dbb3de91cfd2b80bb63e053..aa0e2a98ed1aa2348dd3892c9d23bc8baba69aef 100644 GIT binary patch delta 10418 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 DPs}(A delta 11693 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 diff --git a/assets/border.xcf b/assets/border.xcf index f8f26a48146ae276ed4694be2503266e7a4f321a..932b4f26735e768addb87069687c5db3d815ea1c 100644 GIT binary patch delta 18288 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 delta 3493 zcmai1dx(@(6uiGK0bf9S-+6{Mg}F z7VV$my^x}PqK{rCm^mfbxlyp|Pr>e(3M|*16Gd;6;Fw;)3C?iRyCP58=kN=`xf=v; z-sJEthwnT5L~zmTf=ibOE?+FT;#WCetvD@;N7f2HeW~E{X@{K-$2*)M_`-dHFRm8c zx21rNU?_|4;j-}gJ5E_iH<;ORLQb@CF2 z^BgX8c$dS61e=}}4BF-k)9!lO{A^!%-Gz>C#lExYFT`4wpE5z~K`PH#mI7qAD-ATW2cQmu_gC(W8v114Te_rA^7v zRVr#Gsf$ce`6i)iOt11xPDM-?*4sdXLg9iV&EeXybx|Mtr-t7yDFrF`)20UYKB!(b znOdyHz(=q@1)nuoC$Hf6w|B>CX<|$eG2w6BJ(c{z?Xht6 zRoC3Y;2Ukg8V4H$=Oa*UlEXS}52wx`iGrlu3oTt1Fdx zi~Gj~9dKc=M%1Wl=RdH^?#-zSk^BBl&$F80=R)K;)Em=^^L*H~&QWo5S{by4=B+A* z7A37o9_1FEZMK=zsf3M=T~A>W;_y@erNY9{+*>oY$5N6c%8`Lp7Bup2NDq`7RITX& zUIa4mM2{>ndz?{cSWSvas59W3!{>(X3kueV!(>bcv`sjWNp#xbk%PNwjr9@gG-}V| zM?>NMdyn9UL#JX2gLA^E2m^?6v zspX(>D3YofCG#){8pP?9p-5nQqsWYQ=Xe9Y9n;T}Q-411p)qIm!qdKh+Yt}!7*WtD z9Y^ZF_#~W)jX`e0^J<^Zx4|i!$)@K2l}}?mikA5ZszwC{{qs4;QB~)32DS7MFzw>d zdA5eEZ!P2)D2go|(%Ks2T!Y$lV7bonQPYn4B`nX26G6LJFTO?*GfKvf_bUpqafD4l z8a@(AKkR&TZg6(~a#&@`J;%%alY(*B=^!NIAVs!kN1xx^4c56+oa>EEu?EnQB!7nv{lJ6f*Z@TnS|c7z*zkyT=%K?^s5S{k(fggxm)lj-qy^N*3dl zhY!yg6U5ATYaTHdV|xl)N{%K$<}b?th#l>KT+h@{1!m4r3Rp`(<_s|m%5@-2u2g~X za0q$PSVnnS$n&SrGm9eDKz%O3ew)O!LauPuMQOy`1`_lrDl zmEim*9hM!+C!U^Pb@E=p8}&Hnd_eLA*TU58W31KQ?9! eR#wxSwbs=|f9j-HBl}mX>a3>_%Z?MeBk~u4W2deF 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