#pragma once #include "olcPGEX_TTF.h" #include "olcUTIL_Geometry2D.h" #include #include #include #include #include #include #include // Declarations namespace olc { class ViewPort : public olc::PGEX { public: ViewPort(); ViewPort(std::vector vertices, vf2d offset = {0, 0}); geom2d::rectrect{}; 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 DrawRectDecal(const olc::vf2d& pos, const olc::vf2d& size, const olc::Pixel col=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, const float *ws, 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 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; // Draws a multiline string as a decal, with tinting and scaling void DrawStringDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col = olc::WHITE, const olc::vf2d& scale = { 1.0f, 1.0f },const float width=std::numeric_limits::max(),const bool disableDynamicScaling=false); void DrawStringDecal(Font&font, const olc::vf2d& pos, const std::u32string& sText, const Pixel col = olc::WHITE, const olc::vf2d& scale = { 1.0f, 1.0f }); void DrawStringPropDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col = olc::WHITE, const olc::vf2d& scale = { 1.0f, 1.0f }, const float width=std::numeric_limits::max(),const bool disableDynamicScaling=false); void DrawShadowStringDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col = olc::WHITE, const Pixel shadowCol = olc::BLACK, const olc::vf2d& scale = { 1.0f, 1.0f },const float width=std::numeric_limits::max(),const float shadowSizeFactor=1,const bool disableDynamicScaling=false); void DrawShadowStringPropDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col = olc::WHITE, const Pixel shadowCol = olc::BLACK, const olc::vf2d& scale = { 1.0f, 1.0f },const float width=std::numeric_limits::max(),const float shadowSizeFactor=1,const bool disableDynamicScaling=false); void DrawShadowStringDecal(Font&font, const olc::vf2d& pos, const std::u32string& sText, const Pixel col = olc::WHITE, const Pixel shadowCol = olc::BLACK, const olc::vf2d& scale = { 1.0f, 1.0f },const float shadowSizeFactor=1); void DrawDropShadowStringDecal(Font&font, const olc::vf2d& pos, const std::u32string& sText, const Pixel col = olc::WHITE, const Pixel shadowCol = olc::BLACK, const olc::vf2d& scale = { 1.0f, 1.0f }); private: void drawClippedDecal(Decal *decal, const vf2d *points, const vf2d *uvs, const Pixel *col, const float *ws, uint32_t elements = 0) const; 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) { olc::ViewPort newPort={{ topLeft, {topLeft.x, topLeft.y + size.y}, topLeft + size, {topLeft.x + size.x, topLeft.y}, }, offset}; newPort.rect={topLeft,size}; return newPort; } 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, const float *ws, uint32_t elements) const { drawClippedDecal(decal, pos, uv, col, ws, 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; } drawClippedDecal(decal, pos, uvs.data(), cols.data(), w.data(), 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; } drawClippedDecal(decal, pos, uvs.data(), cols.data(), ws.data(), 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::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, }; std::vectorw{1,1,1,1}; drawClippedDecal(nullptr, points.data(), uvs.data(), colors.data(), w.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; } std::vectorw{1,1,1,1}; drawClippedDecal(decal, pos.data(), uv.data(), colors.data(), w.data(), pos.size()); } void olc::ViewPort::DrawPolygonDecal(Decal *decal, const std::vector &pos, const std::vector &, const std::vector &uv, const Pixel tint) const { DrawPolygonDecal(decal, pos, uv, tint); } void olc::ViewPort::DrawPolygonDecal(Decal *decal, const std::vector &pos, const std::vector &uv, const std::vector &tint) const { std::vectorw{1,1,1,1}; drawClippedDecal(decal, pos.data(), uv.data(), tint.data(), w.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]; auto clipB = clipVertices[(i + 1) % clipVertices.size()]; 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; } } pge->DrawLineDecal(posA + offset, posB + offset, p); } void olc::ViewPort::drawClippedDecal(Decal *decal, const vf2d *points, const vf2d *uvs, const Pixel *col, const float *ws, uint32_t elements) const { std::vector outputList{points, points + elements}; std::vector outputUvs{uvs, uvs + elements}; std::vector outputWs{ws, ws + elements}; std::vector outputCols{col, col + elements}; vf2d min={std::numeric_limits::max(),std::numeric_limits::max()},max; bool pointsOutside=false; if(rect!=geom2d::rect{}){ for(vf2d&points:outputList){ if(!geom2d::contains(rect,points)){ pointsOutside=true; break; } } }else{pointsOutside=true;} if(!pointsOutside)goto render; for (auto i = 0u; i < clipVertices.size(); i++) { auto clipA = clipVertices[i]; auto clipB = clipVertices[(i + 1) % clipVertices.size()]; auto inputList{outputList}; auto inputUvs{outputUvs}; auto inputWs{outputWs}; auto inputCols{outputCols}; outputList.clear(); outputUvs.clear(); outputWs.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 Wa = inputWs[i]; auto Wb = inputWs[(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 intersectionW = Wa + (Wb - Wa) * 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); outputWs.push_back(intersectionW); outputCols.push_back(intersectionCol); } outputList.push_back(polygonB); outputUvs.push_back(uvB); outputWs.push_back(Wb); outputCols.push_back(colB); } else if (aDirection <= 0) { outputList.push_back(intersectionPoint); outputUvs.push_back(intersectionUv); outputWs.push_back(intersectionW); outputCols.push_back(intersectionCol); } } } if (outputList.size() == 0) { return; } render: for (auto &point : outputList) { point += offset; } pge->DrawExplicitDecal(decal, outputList.data(), outputUvs.data(), outputCols.data(), outputWs.data(), outputList.size()); } 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); } void olc::ViewPort::DrawStringDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col, const olc::vf2d& scale, const float width,const bool disableDynamicScaling){ if(sText.length()==0)return; std::string key{"DSD_"+std::string(pge->stripCol(sText))}; key+=std::to_string(width); if(!disableDynamicScaling){ key+=scale.str(); } if(!pge->garbageCollector.count(key)||pge->garbageCollector[key].originalStr!=sText){ //If the text key already exists, don't have to recreate the decal, just update the expire time. vi2d imageSize=pge->GetWrappedTextSize(sText,width,scale); if(imageSize.x<1||imageSize.y<1)return; Decal*newDecal=nullptr; if(!pge->garbageCollector.count(key)){ newDecal=new Decal(new Sprite(imageSize.x/scale.x,imageSize.y/scale.x)); pge->garbageCollector[key].decal=newDecal; }else{ newDecal=pge->garbageCollector[key].decal; } pge->garbageCollector[key].originalStr=sText; pge->SetDrawTarget(newDecal->sprite); pge->Clear(BLANK); pge->DrawString({0,0},sText,WHITE,1U,width/scale.x); pge->SetDrawTarget(nullptr); newDecal->Update(); } pge->garbageCollector[key].expireTime=pge->GetRuntime()+120.0f; DrawDecal(pos,pge->garbageCollector[key].decal,scale,col); } void olc::ViewPort::DrawStringDecal(Font&font, const olc::vf2d& pos, const std::u32string& sText, const Pixel col, const olc::vf2d& scale){ if(sText.length()==0)return; std::u32string Ukey=U"DSD_"+font.GetFontName()+U"_"+pge->stripCol(sText); std::string key=std::string(Ukey.begin(),Ukey.end()); if(!pge->garbageCollector.count(key)||pge->garbageCollector[key].originalStr!=std::string(sText.begin(),sText.end())){ //If the text key already exists, don't have to recreate the decal, just update the expire time. if(pge->garbageCollector.count(key)){ delete pge->garbageCollector[key].decal; } pge->garbageCollector[key].decal=font.RenderStringToDecal(sText,WHITE); pge->garbageCollector[key].originalStr=std::string(sText.begin(),sText.end()); } pge->garbageCollector[key].expireTime=pge->GetRuntime()+120.0f; DrawDecal(pos,pge->garbageCollector[key].decal,scale/4,col); } void olc::ViewPort::DrawStringPropDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col, const olc::vf2d& scale,const float width,const bool disableDynamicScaling){ if(sText.length()==0)return; std::string key{"DSPD"+std::string(pge->stripCol(sText))}; key+=std::to_string(width); if(!disableDynamicScaling){ key+=scale.str(); } if(!pge->garbageCollector.count(key)||pge->garbageCollector[key].originalStr!=sText){ //If the text key already exists, don't have to recreate the decal, just update the expire time. vi2d imageSize=pge->GetWrappedTextSizeProp(sText,width,scale); if(imageSize.x<1||imageSize.y<1)return; Decal*newDecal=nullptr; if(!pge->garbageCollector.count(key)){ newDecal=new Decal(new Sprite(imageSize.x/scale.x,imageSize.y/scale.x)); pge->garbageCollector[key].decal=newDecal; }else{ newDecal=pge->garbageCollector[key].decal; } pge->garbageCollector[key].originalStr=sText; pge->SetDrawTarget(newDecal->sprite); pge->Clear(BLANK); pge->DrawStringProp({0,0},sText,WHITE,1U,width/scale.x); pge->SetDrawTarget(nullptr); newDecal->Update(); } pge->garbageCollector[key].expireTime=pge->GetRuntime()+120.0f; DrawDecal(pos,pge->garbageCollector[key].decal,scale,col); } void olc::ViewPort::DrawShadowStringDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col, const Pixel shadowCol, const olc::vf2d& scale,const float width,const float shadowSizeFactor,const bool disableDynamicScaling){ if(sText.length()==0)return; std::string key{"DSSD_"+std::string(pge->stripCol(sText))}; key+=std::to_string(width); if(!disableDynamicScaling){ key+=scale.str(); } if(!pge->garbageCollector.count(key)||pge->garbageCollector[key].originalStr!=sText){ //If the text key already exists, don't have to recreate the decal, just update the expire time. vi2d imageSize=pge->GetWrappedTextSize(sText,width,scale); if(imageSize.x<1||imageSize.y<1)return; Decal*newDecal=nullptr; if(!pge->garbageCollector.count(key)){ newDecal=new Decal(new Sprite(imageSize.x/scale.x,imageSize.x/scale.x)); pge->garbageCollector[key].decal=newDecal; }else{ newDecal=pge->garbageCollector[key].decal; } pge->garbageCollector[key].originalStr=sText; pge->SetDrawTarget(newDecal->sprite); pge->Clear(BLANK); pge->DrawString({0,0},sText,WHITE,1U,width/scale.x); newDecal->Update(); vf2d adjustedShadowSizeFactor=vf2d{shadowSizeFactor,shadowSizeFactor}*4/scale; Decal*newShadowDecal=nullptr; if(!pge->garbageCollector.count(key+"_SHADOW")){ newShadowDecal=new Decal(new Sprite((imageSize.x/scale.x*4)+adjustedShadowSizeFactor.x*2,(imageSize.x/scale.x*4)+adjustedShadowSizeFactor.y*2)); pge->garbageCollector[key+"_SHADOW"].decal=newShadowDecal; }else{ newShadowDecal=pge->garbageCollector[key+"_SHADOW"].decal; } pge->SetDrawTarget(newShadowDecal->sprite); pge->Clear(BLANK); for(float y=-adjustedShadowSizeFactor.y;y<=adjustedShadowSizeFactor.y+0.1;y+=adjustedShadowSizeFactor.y/2){ for(float x=-adjustedShadowSizeFactor.x;x<=adjustedShadowSizeFactor.x+0.1;x+=adjustedShadowSizeFactor.x/2){ if(x!=0||y!=0){ pge->DrawString(vf2d{x,y}+adjustedShadowSizeFactor, sText, WHITE,4U,width/scale.x*4); } } } pge->SetDrawTarget(nullptr); newShadowDecal->Update(); } pge->garbageCollector[key].expireTime=pge->GetRuntime()+120.0f; pge->garbageCollector[key+"_SHADOW"].expireTime=pge->GetRuntime()+120.0f; DrawDecal(pos-vf2d{shadowSizeFactor,shadowSizeFactor},pge->garbageCollector[key+"_SHADOW"].decal,scale/4,shadowCol); DrawDecal(pos,pge->garbageCollector[key].decal,scale,col); } void olc::ViewPort::DrawShadowStringPropDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col, const Pixel shadowCol, const olc::vf2d& scale,const float width,const float shadowSizeFactor,const bool disableDynamicScaling){ if(sText.length()==0)return; std::string key{"DSSPD"+std::string(pge->stripCol(sText))}; key+=std::to_string(width); if(!disableDynamicScaling){ key+=scale.str(); } if(!pge->garbageCollector.count(key)||pge->garbageCollector[key].originalStr!=sText){ //If the text key already exists, don't have to recreate the decal, just update the expire time. vi2d imageSize=pge->GetWrappedTextSizeProp(sText,width,scale); if(imageSize.x<1||imageSize.y<1)return; Decal*newDecal=nullptr; if(!pge->garbageCollector.count(key)){ newDecal=new Decal(new Sprite(imageSize.x/scale.x,imageSize.x/scale.x)); pge->garbageCollector[key].decal=newDecal; }else{ newDecal=pge->garbageCollector[key].decal; } pge->garbageCollector[key].originalStr=sText; pge->SetDrawTarget(newDecal->sprite); pge->Clear(BLANK); pge->DrawStringProp({0,0},sText,WHITE,1U,width/scale.x); newDecal->Update(); vf2d adjustedShadowSizeFactor=vf2d{shadowSizeFactor,shadowSizeFactor}*4/scale; Decal*newShadowDecal=nullptr; if(!pge->garbageCollector.count(key+"_SHADOW")){ newShadowDecal=new Decal(new Sprite((imageSize.x/scale.x*4)+adjustedShadowSizeFactor.x*2,(imageSize.x/scale.x*4)+adjustedShadowSizeFactor.y*2)); pge->garbageCollector[key+"_SHADOW"].decal=newShadowDecal; }else{ newShadowDecal=pge->garbageCollector[key+"_SHADOW"].decal; } pge->SetDrawTarget(newShadowDecal->sprite); pge->Clear(BLANK); for(float y=-adjustedShadowSizeFactor.y;y<=adjustedShadowSizeFactor.y+0.1;y+=adjustedShadowSizeFactor.y/2){ for(float x=-adjustedShadowSizeFactor.x;x<=adjustedShadowSizeFactor.x+0.1;x+=adjustedShadowSizeFactor.x/2){ if(x!=0||y!=0){ pge->DrawStringProp(vf2d{x,y}+adjustedShadowSizeFactor, sText, WHITE,4U,width/scale.x*4); } } } pge->SetDrawTarget(nullptr); newShadowDecal->Update(); } pge->garbageCollector[key].expireTime=pge->GetRuntime()+120.0f; pge->garbageCollector[key+"_SHADOW"].expireTime=pge->GetRuntime()+120.0f; DrawDecal(pos-vf2d{shadowSizeFactor,shadowSizeFactor},pge->garbageCollector[key+"_SHADOW"].decal,scale/4,shadowCol); DrawDecal(pos,pge->garbageCollector[key].decal,scale,col); } void olc::ViewPort::DrawShadowStringDecal(Font&font, const olc::vf2d& pos, const std::u32string& sText, const Pixel col, const Pixel shadowCol, const olc::vf2d& scale,const float shadowSizeFactor){ if(sText.length()==0)return; std::u32string Ukey=U"DSSD_"+font.GetFontName()+U"_"+pge->stripCol(sText); std::string key=std::string(Ukey.begin(),Ukey.end()); if(!pge->garbageCollector.count(key)||pge->garbageCollector[key].originalStr!=std::string(sText.begin(),sText.end())){ //If the text key already exists, don't have to recreate the decal, just update the expire time. if(pge->garbageCollector.count(key)){ delete pge->garbageCollector[key].decal; } pge->garbageCollector[key].decal=font.RenderStringToDecal(sText,WHITE); pge->garbageCollector[key].originalStr=std::string(sText.begin(),sText.end()); } pge->garbageCollector[key].expireTime=pge->GetRuntime()+120.0f; std::erase_if(pge->garbageCollector,[&](auto&key){ if(key.second.expireTimeGetRuntime()){ delete key.second.decal; return true; } return false; }); 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){ DrawDecal(pos+vf2d{x,y},pge->garbageCollector[key].decal,scale/4,shadowCol); } } } DrawDecal(pos,pge->garbageCollector[key].decal,scale/4,col); } void olc::ViewPort::DrawDropShadowStringDecal(Font&font, const olc::vf2d& pos, const std::u32string& sText, const Pixel col, const Pixel shadowCol, const olc::vf2d& scale){ if(sText.length()==0)return; std::u32string Ukey=U"DDSSD_"+font.GetFontName()+U"_"+pge->stripCol(sText); std::string key=std::string(Ukey.begin(),Ukey.end()); if(!pge->garbageCollector.count(key)||pge->garbageCollector[key].originalStr!=std::string(sText.begin(),sText.end())){ //If the text key already exists, don't have to recreate the decal, just update the expire time. if(pge->garbageCollector.count(key)){ delete pge->garbageCollector[key].decal; } pge->garbageCollector[key].decal=font.RenderStringToDecal(sText,WHITE); pge->garbageCollector[key].originalStr=std::string(sText.begin(),sText.end()); } pge->garbageCollector[key].expireTime=pge->GetRuntime()+120.0f; DrawDecal(pos+vf2d{0,0.5f},pge->garbageCollector[key].decal,scale/4,shadowCol); DrawDecal(pos+vf2d{0.5f,0},pge->garbageCollector[key].decal,scale/4,shadowCol); DrawDecal(pos+vf2d{0.5f,0.5f},pge->garbageCollector[key].decal,scale/4,shadowCol); DrawDecal(pos,pge->garbageCollector[key].decal,scale/4,col); } void olc::ViewPort::DrawRectDecal(const olc::vf2d& pos, const olc::vf2d& size, const olc::Pixel col)const{ FillRectDecal(pos,{size.x+1,1},col); FillRectDecal(pos+vf2d{0,size.y-1+1},{size.x+1,1},col); FillRectDecal(pos+vf2d{0,1},{1,size.y-1*2+1},col); FillRectDecal(pos+vf2d{size.x-1+1,1},{1,size.y-1*2+1},col); } #endif