From e3d0283fcb0ce3bfe2558c115045a1a66d36a40f Mon Sep 17 00:00:00 2001 From: sigonasr2 Date: Wed, 11 Oct 2023 19:50:12 -0500 Subject: [PATCH] Prepare framework with Draw and DrawDecal split. --- Crawler/Crawler.vcxproj | 1 + Crawler/Crawler.vcxproj.filters | 3 + Crawler/InventoryWindow.cpp | 8 +- Crawler/Menu.cpp | 78 ++++++-- Crawler/Menu.h | 14 +- Crawler/MenuComponent.cpp | 8 +- Crawler/MenuComponent.h | 1 + Crawler/MenuIconButton.h | 2 +- Crawler/MenuItemButton.h | 7 +- Crawler/MenuLabel.h | 8 +- Crawler/Version.h | 2 +- Crawler/olcPGEX_Graphics2D.h | 313 ++++++++++++++++++++++++++++++++ Crawler/olcPixelGameEngine.h | 50 ++++- Crawler/pixelGameEngine.cpp | 4 +- Crawler/utils.cpp | 25 +++ Crawler/utils.h | 1 + 16 files changed, 479 insertions(+), 46 deletions(-) create mode 100644 Crawler/olcPGEX_Graphics2D.h diff --git a/Crawler/Crawler.vcxproj b/Crawler/Crawler.vcxproj index 42db6863..4edb87fc 100644 --- a/Crawler/Crawler.vcxproj +++ b/Crawler/Crawler.vcxproj @@ -283,6 +283,7 @@ + diff --git a/Crawler/Crawler.vcxproj.filters b/Crawler/Crawler.vcxproj.filters index 70b8f184..7f5bcf5f 100644 --- a/Crawler/Crawler.vcxproj.filters +++ b/Crawler/Crawler.vcxproj.filters @@ -183,6 +183,9 @@ Header Files\Interface + + Header Files + diff --git a/Crawler/InventoryWindow.cpp b/Crawler/InventoryWindow.cpp index 7572bbc1..3637f5c7 100644 --- a/Crawler/InventoryWindow.cpp +++ b/Crawler/InventoryWindow.cpp @@ -19,7 +19,9 @@ Menu*Menu::InitializeInventoryWindow(){ constexpr int buttonSize=24; constexpr int totalSpacing=buttonSize+itemSpacing; - Menu*inventoryWindow=new Menu(CENTERED,{totalSpacing*invWidth-itemSpacing,totalSpacing*(invHeight+1)-itemSpacing}); + vf2d windowSize={totalSpacing*invWidth-itemSpacing+1,totalSpacing*(invHeight+1)-itemSpacing+24}; + + Menu*inventoryWindow=new Menu(CENTERED,windowSize); MenuFunc useItemFunc=[](MenuFuncData data){ MenuItemButton*button=(MenuItemButton*)data.component; @@ -34,9 +36,9 @@ Menu*Menu::InitializeInventoryWindow(){ } } - MenuLabel*itemNameLabel=new MenuLabel{INVENTORY,vf2d{0,invHeight*totalSpacing-4},"",false,true}; + MenuLabel*itemNameLabel=new MenuLabel{INVENTORY,geom2d::rect(vf2d{2,invHeight*totalSpacing-4},windowSize),"",false,true}; inventoryWindow->AddComponent("itemName",itemNameLabel); - MenuLabel*itemDescriptionLabel=new MenuLabel{INVENTORY,vf2d{2+inventoryWindow->size.x/2,invHeight*totalSpacing+4+itemSpacing},"",true,true}; + MenuLabel*itemDescriptionLabel=new MenuLabel{INVENTORY,geom2d::rect(vf2d{2,invHeight*totalSpacing+itemSpacing},{windowSize.x-4,windowSize.y-108}),"",true,true}; inventoryWindow->AddComponent("itemDescription",itemDescriptionLabel); return inventoryWindow; diff --git a/Crawler/Menu.cpp b/Crawler/Menu.cpp index 5bc0aa46..1399bcaf 100644 --- a/Crawler/Menu.cpp +++ b/Crawler/Menu.cpp @@ -17,7 +17,8 @@ Menu::Menu(){} Menu::Menu(vf2d pos,vf2d size) :pos(pos==CENTERED?WINDOW_SIZE/2-size/2:vi2d{pos}),size(size){ - + r.Create(size.x,size.y); + overlay.Create(WINDOW_SIZE.x,WINDOW_SIZE.y); } void Menu::InitializeMenus(){ @@ -139,30 +140,65 @@ void Menu::Update(Crawler*game){ }; void Menu::Draw(Crawler*game){ - if(GetCurrentTheme().IsScaled()){ - DrawScaledWindow(game,pos); + DrawScaledWindowBackground(game,pos); }else{ - DrawTiledWindow(game,pos); + DrawTiledWindowBackground(game,pos); } + game->SetDrawTarget(r.Sprite()); + Pixel::Mode prevMode=game->GetPixelMode(); + game->SetPixelMode(Pixel::MASK); + game->Clear(BLANK); + for(auto&key:buttons){ + for(auto&button:key.second){ + button->Draw(game,{0,0},this==Menu::stack.back()); + } + } + for(auto&component:displayComponents){ + component->Draw(game,{0,0},this==Menu::stack.back()); + } + game->SetPixelMode(prevMode); + game->SetDrawTarget(nullptr); + r.Decal()->Update(); + game->DrawDecal(pos,r.Decal()); for(auto&key:buttons){ for(auto&button:key.second){ - button->Draw(game,pos,this==Menu::stack.back()); + button->DrawDecal(game,{0,0},this==Menu::stack.back()); } } for(auto&component:displayComponents){ - component->Draw(game,pos,this==Menu::stack.back()); + component->DrawDecal(game,{0,0},this==Menu::stack.back()); + } + + if(GetCurrentTheme().IsScaled()){ + DrawScaledWindowBorder(game,pos); + }else{ + DrawTiledWindowBorder(game,pos); } if(draggingComponent!=nullptr){ + game->SetDrawTarget(overlay.Sprite()); + Pixel::Mode prevMode=game->GetPixelMode(); + game->SetPixelMode(Pixel::MASK); + game->Clear(BLANK); vf2d offsetPos=draggingComponent->rect.pos; if(!MOUSE_NAVIGATION){ MenuComponent*selectedComponent=buttons[selection.y][selection.x]; - draggingComponent->Draw(game,pos+-offsetPos+selectedComponent->rect.pos+vi2d{1,-4},this==Menu::stack.back()); + draggingComponent->Draw(game,pos-offsetPos+selectedComponent->rect.pos+vi2d{1,-4},this==Menu::stack.back()); }else{ draggingComponent->Draw(game,-offsetPos+game->GetMousePos(),this==Menu::stack.back()); } + game->SetPixelMode(prevMode); + game->SetDrawTarget(nullptr); + overlay.Decal()->Update(); + game->DrawDecal({0,0},overlay.Decal()); + if(!MOUSE_NAVIGATION){ + MenuComponent*selectedComponent=buttons[selection.y][selection.x]; + draggingComponent->DrawDecal(game,-offsetPos+selectedComponent->rect.pos+vi2d{1,-4},this==Menu::stack.back()); + }else{ + draggingComponent->DrawDecal(game,-pos-offsetPos+game->GetMousePos(),this==Menu::stack.back()); + } } }; @@ -299,7 +335,7 @@ void Menu::KeyboardButtonNavigation(Crawler*game,vf2d menuPos){ } } -void Menu::DrawScaledWindow(Crawler*game,vf2d menuPos){ +void Menu::DrawScaledWindowBorder(Crawler*game,vf2d menuPos){ vf2d patchSize={"Interface.9PatchSize"_f[0],"Interface.9PatchSize"_f[1]}; //Upper-Left @@ -318,16 +354,9 @@ void Menu::DrawScaledWindow(Crawler*game,vf2d menuPos){ game->DrawPartialDecal(menuPos+vf2d{size.x,0},vf2d{patchSize.x,size.y},GetPatchPart(2,1).Decal(),{patchSize.x*2,patchSize.y*1},patchSize,GetRenderColor()); //Bottom game->DrawPartialDecal(menuPos+vf2d{0,size.y},vf2d{size.x,patchSize.y},GetPatchPart(1,2).Decal(),{patchSize.x*1,patchSize.y*2},patchSize,GetRenderColor()); - //Center - if(GetCurrentTheme().HasBackground()){ - Decal*back=GetCurrentTheme().GetBackground(); - game->DrawPartialDecal(menuPos,size,back,{0,0},back->sprite->Size(),GetRenderColor()); - }else{ - game->DrawPartialDecal(menuPos,size,GetPatchPart(1,1).Decal(),{patchSize.x*1,patchSize.y*1},patchSize,GetRenderColor()); - } } -void Menu::DrawTiledWindow(Crawler*game,vf2d menuPos){ +void Menu::DrawTiledWindowBorder(Crawler*game,vf2d menuPos){ vf2d patchSize={"Interface.9PatchSize"_f[0],"Interface.9PatchSize"_f[1]}; //Upper-Left @@ -346,6 +375,23 @@ void Menu::DrawTiledWindow(Crawler*game,vf2d menuPos){ game->DrawPartialDecal(menuPos+vf2d{size.x,0},vf2d{patchSize.x,size.y},GetPatchPart(2,1).Decal(),{0,0},vf2d{patchSize.x,size.y},GetRenderColor()); //Bottom game->DrawPartialDecal(menuPos+vf2d{0,size.y},vf2d{size.x,patchSize.y},GetPatchPart(1,2).Decal(),{0,0},vf2d{size.x,patchSize.y},GetRenderColor()); +} + +void Menu::DrawScaledWindowBackground(Crawler*game,vf2d menuPos){ + vf2d patchSize={"Interface.9PatchSize"_f[0],"Interface.9PatchSize"_f[1]}; + + //Center + if(GetCurrentTheme().HasBackground()){ + Decal*back=GetCurrentTheme().GetBackground(); + game->DrawPartialDecal(menuPos,size,back,{0,0},back->sprite->Size(),GetRenderColor()); + }else{ + game->DrawPartialDecal(menuPos,size,GetPatchPart(1,1).Decal(),{patchSize.x*1,patchSize.y*1},patchSize,GetRenderColor()); + } +} + +void Menu::DrawTiledWindowBackground(Crawler*game,vf2d menuPos){ + vf2d patchSize={"Interface.9PatchSize"_f[0],"Interface.9PatchSize"_f[1]}; + //Center if(GetCurrentTheme().HasBackground()){ Decal*back=GetCurrentTheme().GetBackground(); diff --git a/Crawler/Menu.h b/Crawler/Menu.h index 9b73782e..60480180 100644 --- a/Crawler/Menu.h +++ b/Crawler/Menu.h @@ -24,18 +24,15 @@ class Menu:IAttributable{ friend class Crawler; friend class Player; static bool MOUSE_NAVIGATION; - static std::mapmenus; float buttonHoldTime=0; std::map>buttons; //Buttons are stored in rows followed by their column order. std::map>newButtonArrangement; //The new setup that the buttons will be at if a drag operation completes. std::vectordisplayComponents; //Components that are only for displaying purposes. vi2d selection={-1,-1}; - vf2d pos; //Specify the upper-left corner of the window. Using CENTERED will always put this where the upper-left corner would center the window. - vf2d size; //Size in tiles (24x24), every menu will be tile-based MenuComponent*draggingComponent=nullptr; - Renderable r; + Renderable r,overlay; public: Menu(); Menu(vf2d pos,vf2d size); @@ -49,6 +46,9 @@ public: static safeunorderedmapthemes; static const vf2d CENTERED; safemapcomponents; //A friendly way to interrogate any component we are interested in. + static std::mapmenus; + vf2d pos; //Specify the upper-left corner of the window. Using CENTERED will always put this where the upper-left corner would center the window. + vf2d size; //Size in tiles (24x24), every menu will be tile-based private: void HoverMenuSelect(Crawler*game); @@ -63,8 +63,10 @@ private: static Theme&GetCurrentTheme(); void KeyboardButtonNavigation(Crawler*game,vf2d menuPos); - void DrawScaledWindow(Crawler*game,vf2d menuPos); - void DrawTiledWindow(Crawler*game,vf2d menuPos); + void DrawScaledWindowBackground(Crawler*game,vf2d menuPos); + void DrawTiledWindowBackground(Crawler*game,vf2d menuPos); + void DrawScaledWindowBorder(Crawler*game,vf2d menuPos); + void DrawTiledWindowBorder(Crawler*game,vf2d menuPos); Pixel GetRenderColor(); }; diff --git a/Crawler/MenuComponent.cpp b/Crawler/MenuComponent.cpp index af5f5925..aafd12a9 100644 --- a/Crawler/MenuComponent.cpp +++ b/Crawler/MenuComponent.cpp @@ -16,13 +16,15 @@ void MenuComponent::Update(Crawler*game){ } void MenuComponent::Draw(Crawler*game,vf2d parentPos,bool focused){ - game->FillRectDecal(rect.pos+parentPos,rect.size,PixelLerp(Menu::themes[Menu::themeSelection].GetButtonCol(),Menu::themes[Menu::themeSelection].GetHighlightCol(),hoverEffect/"ThemeGlobal.HighlightTime"_F)*(focused?1:"ThemeGlobal.MenuUnfocusedColorMult"_F)); + game->FillRect(rect.pos+parentPos,rect.size,PixelLerp(Menu::themes[Menu::themeSelection].GetButtonCol(),Menu::themes[Menu::themeSelection].GetHighlightCol(),hoverEffect/"ThemeGlobal.HighlightTime"_F)*(focused?1:"ThemeGlobal.MenuUnfocusedColorMult"_F)); if(border){ - game->DrawRectDecal(rect.pos+parentPos,rect.size,focused?GREY:GREY*"ThemeGlobal.MenuUnfocusedColorMult"_F); + game->DrawRect(rect.pos+parentPos,rect.size,focused?GREY:GREY*"ThemeGlobal.MenuUnfocusedColorMult"_F); } - game->DrawStringPropDecal(rect.pos+parentPos+rect.size/2-game->GetTextSizeProp(label)/2,label,focused?WHITE:WHITE*"ThemeGlobal.MenuUnfocusedColorMult"_F); + game->DrawStringProp(rect.pos+parentPos+rect.size/2-game->GetTextSizeProp(label)/2,label,focused?WHITE:WHITE*"ThemeGlobal.MenuUnfocusedColorMult"_F); } +void MenuComponent::DrawDecal(Crawler*game,vf2d parentPos,bool focused){} + MenuComponent*MenuComponent::PickUpDraggableItem(){ return nullptr; } diff --git a/Crawler/MenuComponent.h b/Crawler/MenuComponent.h index f2677c84..418caed6 100644 --- a/Crawler/MenuComponent.h +++ b/Crawler/MenuComponent.h @@ -21,6 +21,7 @@ public: MenuComponent(MenuType parent,geom2d::rectrect,std::string label,MenuType menuDest,MenuFunc onClick,bool selectable=true); virtual void Update(Crawler*game); virtual void Draw(Crawler*game,vf2d parentPos,bool focused); + virtual void DrawDecal(Crawler*game,vf2d parentPos,bool focused); //We picked up a draggable component, we should make a copy and return it here. If a nullptr is returned here, the pickup is not allowed. //WARNING!!! This allocates a brand new component when successful!!! Be prepared to clear it! virtual MenuComponent*PickUpDraggableItem(); diff --git a/Crawler/MenuIconButton.h b/Crawler/MenuIconButton.h index 9b9f639f..d6ab4082 100644 --- a/Crawler/MenuIconButton.h +++ b/Crawler/MenuIconButton.h @@ -18,7 +18,7 @@ protected: virtual inline void Draw(Crawler*game,vf2d parentPos,bool focused)override{ MenuComponent::Draw(game,parentPos,focused); if(icon!=nullptr){ - game->DrawRotatedDecal(parentPos+rect.middle(),icon,0,icon->sprite->Size()/2,{1,1},focused?WHITE:WHITE*"ThemeGlobal.MenuUnfocusedColorMult"_F); + game->DrawSprite(parentPos+rect.middle()-icon->sprite->Size()/2,icon->sprite,1,Sprite::Flip::NONE,[&](Pixel&in){return focused?in:in*"ThemeGlobal.MenuUnfocusedColorMult"_F;}); } } }; \ No newline at end of file diff --git a/Crawler/MenuItemButton.h b/Crawler/MenuItemButton.h index ff6e934d..2c64e5a4 100644 --- a/Crawler/MenuItemButton.h +++ b/Crawler/MenuItemButton.h @@ -4,6 +4,7 @@ #include "Crawler.h" #include "Item.h" #include "safemap.h" +#include "olcPGEX_Graphics2D.h" INCLUDE_game INCLUDE_ITEM_DATA @@ -57,12 +58,12 @@ protected: } } } - virtual inline void Draw(Crawler*game,vf2d parentPos,bool focused)override{ - MenuIconButton::Draw(game,parentPos,focused); + virtual inline void DrawDecal(Crawler*game,vf2d parentPos,bool focused)override{ + MenuIconButton::DrawDecal(game,parentPos,focused); if(valid){ std::string quantityText="x"+std::to_string(Inventory::GetItemCount(invRef.at(inventoryIndex))); vf2d textSize=vf2d(game->GetTextSizeProp(quantityText))*0.5; - game->DrawShadowStringDecal(parentPos+rect.pos+rect.size-textSize,quantityText,WHITE,BLACK,{0.5,0.5},0.5); + game->DrawShadowStringDecal(Menu::menus[parentMenu]->pos+parentPos+rect.pos+rect.size-textSize,quantityText,WHITE,BLACK,{0.5,0.5},0.5); } } virtual inline MenuComponent*PickUpDraggableItem()override{ diff --git a/Crawler/MenuLabel.h b/Crawler/MenuLabel.h index 64b1bc18..d2251b00 100644 --- a/Crawler/MenuLabel.h +++ b/Crawler/MenuLabel.h @@ -2,6 +2,7 @@ #include "MenuComponent.h" #include "DEFINES.h" #include "Crawler.h" +#include "utils.h" INCLUDE_game @@ -21,14 +22,15 @@ protected: MenuComponent::Update(game); } virtual void inline Draw(Crawler*game,vf2d parentPos,bool focused)override{ - vf2d drawPos=rect.pos+parentPos+rect.size/2-game->GetTextSizeProp(label)/2; //Assume centered. + std::string wrappedText=util::WrapText(game,label,rect.size.x,true,{1,1}); + vf2d drawPos=parentPos+rect.middle()-game->GetTextSizeProp(wrappedText)/2; //Assume centered. if(!centered){ drawPos=rect.pos+parentPos; } if(shadow){ - game->DrawShadowStringPropDecal(drawPos,label,focused?WHITE:WHITE*"ThemeGlobal.MenuUnfocusedColorMult"_F); + game->DrawShadowStringProp(drawPos,wrappedText,focused?WHITE:WHITE*"ThemeGlobal.MenuUnfocusedColorMult"_F); }else{ - game->DrawStringPropDecal(drawPos,label,focused?WHITE:WHITE*"ThemeGlobal.MenuUnfocusedColorMult"_F); + game->DrawStringProp(drawPos,wrappedText,focused?WHITE:WHITE*"ThemeGlobal.MenuUnfocusedColorMult"_F); } } }; \ No newline at end of file diff --git a/Crawler/Version.h b/Crawler/Version.h index 6a1a75ba..2498cb57 100644 --- a/Crawler/Version.h +++ b/Crawler/Version.h @@ -2,7 +2,7 @@ #define VERSION_MAJOR 0 #define VERSION_MINOR 2 #define VERSION_PATCH 0 -#define VERSION_BUILD 1899 +#define VERSION_BUILD 1954 #define stringify(a) stringify_(a) #define stringify_(a) #a diff --git a/Crawler/olcPGEX_Graphics2D.h b/Crawler/olcPGEX_Graphics2D.h new file mode 100644 index 00000000..45d473ef --- /dev/null +++ b/Crawler/olcPGEX_Graphics2D.h @@ -0,0 +1,313 @@ +/* + olcPGEX_Graphics2D.h + + +-------------------------------------------------------------+ + | OneLoneCoder Pixel Game Engine Extension | + | Advanced 2D Rendering - v0.5 | + +-------------------------------------------------------------+ + + What is this? + ~~~~~~~~~~~~~ + This is an extension to the olcPixelGameEngine, which provides + advanced olc::Sprite manipulation and drawing routines. To use + it, simply include this header file. + + License (OLC-3) + ~~~~~~~~~~~~~~~ + + Copyright 2018 - 2019 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 +*/ + +/* + Matrices stored as [Column][Row] (i.e. x, y) + + |C0R0 C1R0 C2R0| | x | | x'| + |C0R1 C1R1 C2R1| * | y | = | y'| + |C0R2 C1R2 C2R2| |1.0| | - | +*/ + + + +#ifndef OLC_PGEX_GFX2D +#define OLC_PGEX_GFX2D + +#include +#undef min +#undef max + +namespace olc +{ + // Container class for Advanced 2D Drawing functions + class GFX2D : public olc::PGEX + { + // A representation of an affine transform, used to rotate, scale, offset & shear space + public: + class Transform2D + { + public: + Transform2D(); + + public: + // Set this transformation to unity + void Reset(); + // Append a rotation of fTheta radians to this transform + void Rotate(float fTheta); + // Append a translation (ox, oy) to this transform + void Translate(float ox, float oy); + // Append a scaling operation (sx, sy) to this transform + void Scale(float sx, float sy); + // Append a shear operation (sx, sy) to this transform + void Shear(float sx, float sy); + + void Perspective(float ox, float oy); + // Calculate the Forward Transformation of the coordinate (in_x, in_y) -> (out_x, out_y) + void Forward(float in_x, float in_y, float &out_x, float &out_y); + // Calculate the Inverse Transformation of the coordinate (in_x, in_y) -> (out_x, out_y) + void Backward(float in_x, float in_y, float &out_x, float &out_y); + // Regenerate the Inverse Transformation + void Invert(); + + private: + void Multiply(); + float matrix[4][3][3]; + int nTargetMatrix; + int nSourceMatrix; + bool bDirty; + }; + + public: + // Draws a sprite with the transform applied + static void DrawSprite(olc::Sprite *sprite, olc::GFX2D::Transform2D &transform); + }; +} + + +#ifdef OLC_PGEX_GRAPHICS2D +#undef OLC_PGEX_GRAPHICS2D + +namespace olc +{ + void GFX2D::DrawSprite(olc::Sprite *sprite, olc::GFX2D::Transform2D &transform) + { + if (sprite == nullptr) + return; + + // Work out bounding rectangle of sprite + float ex, ey; + float sx, sy; + float px, py; + + transform.Forward(0.0f, 0.0f, sx, sy); + px = sx; py = sy; + sx = std::min(sx, px); sy = std::min(sy, py); + ex = std::max(ex, px); ey = std::max(ey, py); + + transform.Forward((float)sprite->width, (float)sprite->height, px, py); + sx = std::min(sx, px); sy = std::min(sy, py); + ex = std::max(ex, px); ey = std::max(ey, py); + + transform.Forward(0.0f, (float)sprite->height, px, py); + sx = std::min(sx, px); sy = std::min(sy, py); + ex = std::max(ex, px); ey = std::max(ey, py); + + transform.Forward((float)sprite->width, 0.0f, px, py); + sx = std::min(sx, px); sy = std::min(sy, py); + ex = std::max(ex, px); ey = std::max(ey, py); + + // Perform inversion of transform if required + transform.Invert(); + + if (ex < sx) + std::swap(ex, sx); + if (ey < sy) + std::swap(ey, sy); + + // Iterate through render space, and sample Sprite from suitable texel location + for (float i = sx; i < ex; i++) + { + for (float j = sy; j < ey; j++) + { + float ox, oy; + transform.Backward(i, j, ox, oy); + pge->Draw((int32_t)i, (int32_t)j, sprite->GetPixel((int32_t)(ox+0.5f), (int32_t)(oy+0.5f))); + } + } + } + + olc::GFX2D::Transform2D::Transform2D() + { + Reset(); + } + + void olc::GFX2D::Transform2D::Reset() + { + nTargetMatrix = 0; + nSourceMatrix = 1; + bDirty = true; + + // Columns Then Rows + + // Matrices 0 & 1 are used as swaps in Transform accumulation + matrix[0][0][0] = 1.0f; matrix[0][1][0] = 0.0f; matrix[0][2][0] = 0.0f; + matrix[0][0][1] = 0.0f; matrix[0][1][1] = 1.0f; matrix[0][2][1] = 0.0f; + matrix[0][0][2] = 0.0f; matrix[0][1][2] = 0.0f; matrix[0][2][2] = 1.0f; + + matrix[1][0][0] = 1.0f; matrix[1][1][0] = 0.0f; matrix[1][2][0] = 0.0f; + matrix[1][0][1] = 0.0f; matrix[1][1][1] = 1.0f; matrix[1][2][1] = 0.0f; + matrix[1][0][2] = 0.0f; matrix[1][1][2] = 0.0f; matrix[1][2][2] = 1.0f; + + // Matrix 2 is a cache matrix to hold the immediate transform operation + // Matrix 3 is a cache matrix to hold the inverted transform + } + + void olc::GFX2D::Transform2D::Multiply() + { + for (int c = 0; c < 3; c++) + { + for (int r = 0; r < 3; r++) + { + matrix[nTargetMatrix][c][r] = matrix[2][0][r] * matrix[nSourceMatrix][c][0] + + matrix[2][1][r] * matrix[nSourceMatrix][c][1] + + matrix[2][2][r] * matrix[nSourceMatrix][c][2]; + } + } + + std::swap(nTargetMatrix, nSourceMatrix); + bDirty = true; // Any transform multiply dirties the inversion + } + + void olc::GFX2D::Transform2D::Rotate(float fTheta) + { + // Construct Rotation Matrix + matrix[2][0][0] = cosf(fTheta); matrix[2][1][0] = sinf(fTheta); matrix[2][2][0] = 0.0f; + matrix[2][0][1] = -sinf(fTheta); matrix[2][1][1] = cosf(fTheta); matrix[2][2][1] = 0.0f; + matrix[2][0][2] = 0.0f; matrix[2][1][2] = 0.0f; matrix[2][2][2] = 1.0f; + Multiply(); + } + + void olc::GFX2D::Transform2D::Scale(float sx, float sy) + { + // Construct Scale Matrix + matrix[2][0][0] = sx; matrix[2][1][0] = 0.0f; matrix[2][2][0] = 0.0f; + matrix[2][0][1] = 0.0f; matrix[2][1][1] = sy; matrix[2][2][1] = 0.0f; + matrix[2][0][2] = 0.0f; matrix[2][1][2] = 0.0f; matrix[2][2][2] = 1.0f; + Multiply(); + } + + void olc::GFX2D::Transform2D::Shear(float sx, float sy) + { + // Construct Shear Matrix + matrix[2][0][0] = 1.0f; matrix[2][1][0] = sx; matrix[2][2][0] = 0.0f; + matrix[2][0][1] = sy; matrix[2][1][1] = 1.0f; matrix[2][2][1] = 0.0f; + matrix[2][0][2] = 0.0f; matrix[2][1][2] = 0.0f; matrix[2][2][2] = 1.0f; + Multiply(); + } + + void olc::GFX2D::Transform2D::Translate(float ox, float oy) + { + // Construct Translate Matrix + matrix[2][0][0] = 1.0f; matrix[2][1][0] = 0.0f; matrix[2][2][0] = ox; + matrix[2][0][1] = 0.0f; matrix[2][1][1] = 1.0f; matrix[2][2][1] = oy; + matrix[2][0][2] = 0.0f; matrix[2][1][2] = 0.0f; matrix[2][2][2] = 1.0f; + Multiply(); + } + + void olc::GFX2D::Transform2D::Perspective(float ox, float oy) + { + // Construct Translate Matrix + matrix[2][0][0] = 1.0f; matrix[2][1][0] = 0.0f; matrix[2][2][0] = 0.0f; + matrix[2][0][1] = 0.0f; matrix[2][1][1] = 1.0f; matrix[2][2][1] = 0.0f; + matrix[2][0][2] = ox; matrix[2][1][2] = oy; matrix[2][2][2] = 1.0f; + Multiply(); + } + + void olc::GFX2D::Transform2D::Forward(float in_x, float in_y, float &out_x, float &out_y) + { + out_x = in_x * matrix[nSourceMatrix][0][0] + in_y * matrix[nSourceMatrix][1][0] + matrix[nSourceMatrix][2][0]; + out_y = in_x * matrix[nSourceMatrix][0][1] + in_y * matrix[nSourceMatrix][1][1] + matrix[nSourceMatrix][2][1]; + float out_z = in_x * matrix[nSourceMatrix][0][2] + in_y * matrix[nSourceMatrix][1][2] + matrix[nSourceMatrix][2][2]; + if (out_z != 0) + { + out_x /= out_z; + out_y /= out_z; + } + } + + void olc::GFX2D::Transform2D::Backward(float in_x, float in_y, float &out_x, float &out_y) + { + out_x = in_x * matrix[3][0][0] + in_y * matrix[3][1][0] + matrix[3][2][0]; + out_y = in_x * matrix[3][0][1] + in_y * matrix[3][1][1] + matrix[3][2][1]; + float out_z = in_x * matrix[3][0][2] + in_y * matrix[3][1][2] + matrix[3][2][2]; + if (out_z != 0) + { + out_x /= out_z; + out_y /= out_z; + } + } + + void olc::GFX2D::Transform2D::Invert() + { + if (bDirty) // Obviously costly so only do if needed + { + float det = matrix[nSourceMatrix][0][0] * (matrix[nSourceMatrix][1][1] * matrix[nSourceMatrix][2][2] - matrix[nSourceMatrix][1][2] * matrix[nSourceMatrix][2][1]) - + matrix[nSourceMatrix][1][0] * (matrix[nSourceMatrix][0][1] * matrix[nSourceMatrix][2][2] - matrix[nSourceMatrix][2][1] * matrix[nSourceMatrix][0][2]) + + matrix[nSourceMatrix][2][0] * (matrix[nSourceMatrix][0][1] * matrix[nSourceMatrix][1][2] - matrix[nSourceMatrix][1][1] * matrix[nSourceMatrix][0][2]); + + float idet = 1.0f / det; + matrix[3][0][0] = (matrix[nSourceMatrix][1][1] * matrix[nSourceMatrix][2][2] - matrix[nSourceMatrix][1][2] * matrix[nSourceMatrix][2][1]) * idet; + matrix[3][1][0] = (matrix[nSourceMatrix][2][0] * matrix[nSourceMatrix][1][2] - matrix[nSourceMatrix][1][0] * matrix[nSourceMatrix][2][2]) * idet; + matrix[3][2][0] = (matrix[nSourceMatrix][1][0] * matrix[nSourceMatrix][2][1] - matrix[nSourceMatrix][2][0] * matrix[nSourceMatrix][1][1]) * idet; + matrix[3][0][1] = (matrix[nSourceMatrix][2][1] * matrix[nSourceMatrix][0][2] - matrix[nSourceMatrix][0][1] * matrix[nSourceMatrix][2][2]) * idet; + matrix[3][1][1] = (matrix[nSourceMatrix][0][0] * matrix[nSourceMatrix][2][2] - matrix[nSourceMatrix][2][0] * matrix[nSourceMatrix][0][2]) * idet; + matrix[3][2][1] = (matrix[nSourceMatrix][0][1] * matrix[nSourceMatrix][2][0] - matrix[nSourceMatrix][0][0] * matrix[nSourceMatrix][2][1]) * idet; + matrix[3][0][2] = (matrix[nSourceMatrix][0][1] * matrix[nSourceMatrix][1][2] - matrix[nSourceMatrix][0][2] * matrix[nSourceMatrix][1][1]) * idet; + matrix[3][1][2] = (matrix[nSourceMatrix][0][2] * matrix[nSourceMatrix][1][0] - matrix[nSourceMatrix][0][0] * matrix[nSourceMatrix][1][2]) * idet; + matrix[3][2][2] = (matrix[nSourceMatrix][0][0] * matrix[nSourceMatrix][1][1] - matrix[nSourceMatrix][0][1] * matrix[nSourceMatrix][1][0]) * idet; + bDirty = false; + } + } +} + +#endif +#endif \ No newline at end of file diff --git a/Crawler/olcPixelGameEngine.h b/Crawler/olcPixelGameEngine.h index b472774e..5825f88d 100644 --- a/Crawler/olcPixelGameEngine.h +++ b/Crawler/olcPixelGameEngine.h @@ -805,6 +805,7 @@ namespace olc public: void SetSampleMode(olc::Sprite::Mode mode = olc::Sprite::Mode::NORMAL); + void Resize(int32_t w, int32_t h); Pixel GetPixel(int32_t x, int32_t y) const; bool SetPixel(int32_t x, int32_t y, Pixel p); Pixel GetPixel(const olc::vi2d& a) const; @@ -1093,20 +1094,21 @@ namespace olc void FillTexturedTriangle(const std::vector& vPoints, std::vector vTex, std::vector vColour, olc::Sprite* sprTex); void FillTexturedPolygon(const std::vector& vPoints, const std::vector& vTex, const std::vector& vColour, olc::Sprite* sprTex, olc::DecalStructure structure = olc::DecalStructure::LIST); // Draws an entire sprite at location (x,y) - void DrawSprite(int32_t x, int32_t y, Sprite* sprite, uint32_t scale = 1, uint8_t flip = olc::Sprite::NONE); - void DrawSprite(const olc::vi2d& pos, Sprite* sprite, uint32_t scale = 1, uint8_t flip = olc::Sprite::NONE); + void DrawSprite(int32_t x, int32_t y, Sprite* sprite, uint32_t scale = 1, uint8_t flip = olc::Sprite::NONE, std::functioncolorFunc=[](Pixel&in){return in;}); + void DrawSprite(const olc::vi2d& pos, Sprite* sprite, uint32_t scale = 1, uint8_t flip = olc::Sprite::NONE, std::functioncolorFunc=[](Pixel&in){return in;}); // Draws an area of a sprite at location (x,y), where the // selected area is (ox,oy) to (ox+w,oy+h) void DrawPartialSprite(int32_t x, int32_t y, Sprite* sprite, int32_t ox, int32_t oy, int32_t w, int32_t h, uint32_t scale = 1, uint8_t flip = olc::Sprite::NONE); void DrawPartialSprite(const olc::vi2d& pos, Sprite* sprite, const olc::vi2d& sourcepos, const olc::vi2d& size, uint32_t scale = 1, uint8_t flip = olc::Sprite::NONE); // Draws a single line of text - traditional monospaced - void DrawString(int32_t x, int32_t y, const std::string& sText, Pixel col = olc::WHITE, uint32_t scale = 1); - void DrawString(const olc::vi2d& pos, const std::string& sText, Pixel col = olc::WHITE, uint32_t scale = 1); - olc::vi2d GetTextSize(const std::string& s); - // Draws a single line of text - non-monospaced - void DrawStringProp(int32_t x, int32_t y, const std::string& sText, Pixel col = olc::WHITE, uint32_t scale = 1); void DrawStringProp(const olc::vi2d& pos, const std::string& sText, Pixel col = olc::WHITE, uint32_t scale = 1); + void DrawShadowString(const olc::vi2d& pos, const std::string& sText, Pixel col = olc::WHITE, const Pixel shadowCol = olc::BLACK, const olc::vf2d& scale = { 1.0f, 1.0f },const float shadowSizeFactor=1); + void DrawStringProp(int32_t x, int32_t y, const std::string& sText, Pixel col = olc::WHITE, uint32_t scale = 1); + void DrawShadowStringProp(const olc::vi2d& pos, const std::string& sText, Pixel col = olc::WHITE, const Pixel shadowCol = olc::BLACK, const olc::vf2d& scale = { 1.0f, 1.0f },const float shadowSizeFactor=1); + olc::vi2d GetTextSize(const std::string& s); olc::vi2d GetTextSizeProp(const std::string& s); + void DrawString(const olc::vi2d& pos, const std::string& sText, Pixel col = olc::WHITE, uint32_t scale = 1); + void DrawString(int32_t x, int32_t y, const std::string& sText, Pixel col = olc::WHITE, uint32_t scale = 1); // Decal Quad functions void SetDecalMode(const olc::DecalMode& mode); @@ -1597,6 +1599,13 @@ namespace olc Sprite::~Sprite() { pColData.clear(); } + void Sprite::Resize(int32_t w, int32_t h) + { + width = w; height = h; + pColData.resize(width * height); + pColData.resize(width * height, nDefaultPixel); + } + void Sprite::SetSampleMode(olc::Sprite::Mode mode) { modeSample = mode; } @@ -2759,10 +2768,10 @@ namespace olc } - void PixelGameEngine::DrawSprite(const olc::vi2d& pos, Sprite* sprite, uint32_t scale, uint8_t flip) + void PixelGameEngine::DrawSprite(const olc::vi2d& pos, Sprite* sprite, uint32_t scale, uint8_t flip, std::functioncolorFunc) { DrawSprite(pos.x, pos.y, sprite, scale, flip); } - void PixelGameEngine::DrawSprite(int32_t x, int32_t y, Sprite* sprite, uint32_t scale, uint8_t flip) + void PixelGameEngine::DrawSprite(int32_t x, int32_t y, Sprite* sprite, uint32_t scale, uint8_t flip, std::functioncolorFunc) { if (sprite == nullptr) return; @@ -3327,6 +3336,29 @@ namespace olc } DrawStringPropDecal(pos, sText, col,scale); } + + void PixelGameEngine::DrawShadowString(const olc::vi2d& pos, const std::string& sText, Pixel col, const Pixel shadowCol, const olc::vf2d& scale,const float shadowSizeFactor){ + for(float y=-shadowSizeFactor;y<=shadowSizeFactor+0.1;y+=shadowSizeFactor/2){ + for(float x=-shadowSizeFactor;x<=shadowSizeFactor+0.1;x+=shadowSizeFactor/2){ + if(x!=0||y!=0){ + DrawString(pos.x+x,pos.y+y, sText, shadowCol,int(scale.x)); + } + } + } + DrawString(pos.x,pos.y, sText, col,int(scale.x)); + } + + void PixelGameEngine::DrawShadowStringProp(const olc::vi2d& pos, const std::string& sText, Pixel col, const Pixel shadowCol, const olc::vf2d& scale,const float shadowSizeFactor){ + for(float y=-shadowSizeFactor;y<=shadowSizeFactor+0.1;y+=shadowSizeFactor/2){ + for(float x=-shadowSizeFactor;x<=shadowSizeFactor+0.1;x+=shadowSizeFactor/2){ + if(x!=0||y!=0){ + DrawStringProp(pos.x+x,pos.y+y, sText, shadowCol,int(scale.x)); + } + } + } + DrawStringProp(pos.x,pos.y, sText, col,int(scale.x)); + } + // Thanks Oso-Grande/Sopadeoso For these awesom and stupidly clever Text Rotation routines... duh XD void PixelGameEngine::DrawRotatedStringDecal(const olc::vf2d& pos, const std::string& sText, const float fAngle, const olc::vf2d& center, const Pixel col, const olc::vf2d& scale) { diff --git a/Crawler/pixelGameEngine.cpp b/Crawler/pixelGameEngine.cpp index 0ff56113..26a00e8f 100644 --- a/Crawler/pixelGameEngine.cpp +++ b/Crawler/pixelGameEngine.cpp @@ -6,4 +6,6 @@ #define TMX_PARSER_SETUP #include "TMXParser.h" #define TSX_PARSER_SETUP -#include "TSXParser.h" \ No newline at end of file +#include "TSXParser.h" +#define OLC_PGEX_GRAPHICS2D +#include "olcPGEX_Graphics2D.h" \ No newline at end of file diff --git a/Crawler/utils.cpp b/Crawler/utils.cpp index 31887da6..89c58877 100644 --- a/Crawler/utils.cpp +++ b/Crawler/utils.cpp @@ -1,4 +1,5 @@ #include "utils.h" +#include "olcPixelGameEngine.h" float util::random(float range){ return float(rand())/RAND_MAX*range; @@ -42,4 +43,28 @@ std::string util::timerStr(float time){ timeStr+=std::to_string(seconds%60); return timeStr; +} + +std::string util::WrapText(PixelGameEngine*pge,std::string str,int width,bool proportional,vd2d scale){ + std::string newStr; + while (true) { + std::string word; + if (str.find(" ")==std::string::npos) { + word=str; + } else { + word = str.substr(0,str.find(" ")); + } + vi2d newSize = vd2d(proportional?pge->GetTextSizeProp(newStr+(newStr.size()>0?" ":"")+word):pge->GetTextSize(newStr+(newStr.size()>0?" ":"")+word))*scale; + if (newSize.x>width) { + newStr+="\n"+word; + } else { + newStr+=(newStr.size()>0?" ":"")+word; + } + if (str.find(" ")==std::string::npos) { + break; + } else { + str.erase(0,str.find(" ")+1); + } + } + return newStr; } \ No newline at end of file diff --git a/Crawler/utils.h b/Crawler/utils.h index 24e583db..bd1399ac 100644 --- a/Crawler/utils.h +++ b/Crawler/utils.h @@ -12,4 +12,5 @@ namespace util{ float radToDeg(float rad); float lerp(float n1,float n2,double t); std::string timerStr(float time); + std::string WrapText(PixelGameEngine*pge,std::string str,int width,bool proportional,vd2d scale); } \ No newline at end of file