The open source repository for the action RPG game in development by Sig Productions titled 'Adventures in Lestoria'! https://forums.lestoria.net
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
AdventuresInLestoria/Adventures in Lestoria/olcPGEX_ViewPort.h

1125 lines
44 KiB

#pragma once
#include "olcPGEX_TTF.h"
#include "olcUTIL_Geometry2D.h"
#include <algorithm>
#include <array>
#include <cmath>
#include <cstdint>
#include <iostream>
#include <vector>
#include <limits>
// Declarations
namespace olc {
class ViewPort : public olc::PGEX {
public:
ViewPort();
ViewPort(std::vector<vf2d> vertices, vf2d offset = {0, 0});
geom2d::rect<float>rect{};
virtual ~ViewPort();
void addPoint(vf2d point);
void clear();
void drawEdges();
void setOffset(vf2d offset);
const vf2d&GetOffset();
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<vf2d, 4> &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<vf2d, 4> &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 &center = {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 &center,
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<vf2d> &pos,
const std::vector<vf2d> &uv,
const Pixel tint = WHITE) const;
void DrawPolygonDecal(Decal *decal,
const std::vector<vf2d> &pos,
const std::vector<float> &depth,
const std::vector<vf2d> &uv,
const Pixel tint = WHITE) const;
void DrawPolygonDecal(Decal *decal,
const std::vector<vf2d> &pos,
const std::vector<vf2d> &uv,
const std::vector<Pixel> &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<float>::max());
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<float>::max());
void DrawShadowStringDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col=WHITE, const Pixel shadowCol=BLACK, const olc::vf2d& scale={1.f,1.f});
void DrawShadowStringDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col, const Pixel shadowCol, const olc::vf2d& scale, const olc::vf2d& shadowScale,const float width=std::numeric_limits<float>::max());
void DrawShadowStringPropDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col=WHITE, const Pixel shadowCol=BLACK, const olc::vf2d& scale={1.f,1.f});
void DrawShadowStringPropDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col, const Pixel shadowCol, const olc::vf2d& scale, const olc::vf2d& shadowScale,const float width=std::numeric_limits<float>::max());
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<vf2d> 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<vf2d> 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;
}
const vf2d&olc::ViewPort::GetOffset() {
return 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<olc::vf2d> 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<vf2d> 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<float> w{ 1, 1, 1, 1 };
std::vector<olc::vf2d> newPos;
newPos.resize(4);
std::vector<vf2d> uvs{
{0, 0},
{0, 1},
{1, 1},
{1, 0},
};
std::vector<Pixel> 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<vf2d, 4> &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<float>(decal->sprite->width),
static_cast<float>(decal->sprite->height)};
olc::vf2d sourceUvSize =
source_size
/ olc::vf2d{static_cast<float>(decal->sprite->width),
static_cast<float>(decal->sprite->height)};
std::vector<vf2d> uvs{
sourceUvPos,
{sourceUvPos.x, sourceUvPos.y + sourceUvSize.y},
sourceUvPos + sourceUvSize,
{sourceUvPos.x + sourceUvSize.x, sourceUvPos.y},
};
std::vector<Pixel> cols{
tint,
tint,
tint,
tint,
};
std::vector<float>ws{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<vf2d, 4> &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 &center,
const vf2d &scale,
const Pixel &tint) const {
auto sin = std::sin(fAngle);
auto cos = std::cos(fAngle);
std::vector<vf2d> 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 &center,
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<vf2d> 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<vf2d> points{
pos,
{pos.x, pos.y + size.y},
pos + size,
{pos.x + size.x, pos.y},
};
std::vector<vf2d> 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<vf2d> points{
pos,
{pos.x, pos.y + size.y},
pos + size,
{pos.x + size.x, pos.y},
};
std::vector<vf2d> uvs{
{0, 0},
{0, 1},
{1, 1},
{1, 0},
};
std::vector<Pixel> colors{
colTL,
colBL,
colBR,
colTR,
};
std::vector<float>w{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<vf2d> &pos,
const std::vector<vf2d> &uv,
const Pixel tint) const {
std::vector<Pixel> colors;
colors.resize(pos.size());
for (auto i = 0u; i < colors.size(); i++) {
colors[i] = tint;
}
std::vector<float>w{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<vf2d> &pos,
const std::vector<float> &,
const std::vector<vf2d> &uv,
const Pixel tint) const {
DrawPolygonDecal(decal, pos, uv, tint);
}
void olc::ViewPort::DrawPolygonDecal(Decal *decal,
const std::vector<vf2d> &pos,
const std::vector<vf2d> &uv,
const std::vector<Pixel> &tint) const {
std::vector<float>w{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<vf2d> outputList{points, points + elements};
std::vector<vf2d> outputUvs{uvs, uvs + elements};
std::vector<float> outputWs{ws, ws + elements};
std::vector<Pixel> outputCols{col, col + elements};
vf2d min={std::numeric_limits<float>::max(),std::numeric_limits<float>::max()},max;
bool pointsOutside=false;
if(rect!=geom2d::rect<float>{}){
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){
Pixel textCol=col;
static std::vector<PixelGameEngine::StringDecalData>letters;
letters.clear();
bool wrappingOccurred=false;
olc::vf2d planningMarker = { 0.0f, 0.0f };
olc::vf2d drawingMarker = { 0.0f, 0.0f };
const auto hexToNumber=[](char c){
if(c<='9')return c-'0';
return (c-'A')+10;
};
for (int skip=0,index=-1;auto c : sText)
{
index++;
if(skip){
skip--;
continue;
}
if(c==' '||c=='\t'||c=='\n'){
for(PixelGameEngine::StringDecalData&letter:letters){
DrawPartialDecal(pos + drawingMarker, pge->fontRenderable.Decal(), letter.sourcePos, letter.sourceSize, scale,letter.col);
drawingMarker.x += 8.0f * scale.x;
}
letters.clear();
wrappingOccurred=false;
}
if(wrappingOccurred){
if(c!=' '&&c!='\t'&&c!='\n'){
wrappingOccurred=false;
}else{
continue;
}
}
if(c=='\r')continue; //Ignore carriage returns. Stupid Linux.
if (c == '\n')
{
planningMarker.x = 0; planningMarker.y += 8.0f * scale.y;
for(PixelGameEngine::StringDecalData&letter:letters){
DrawPartialDecal(pos + drawingMarker, pge->fontRenderable.Decal(), letter.sourcePos, letter.sourceSize, scale, letter.col);
drawingMarker.x += 8.0f * scale.x;
}
letters.clear();
drawingMarker.x = 0; drawingMarker.y += 8.0f * scale.y;
}
else if (c == ' ')
{
drawingMarker.x += 8.0f * scale.x;
planningMarker.x += 8.0f * scale.x;
}
else if (c == '\t')
{
drawingMarker.x += 8.0f * float(nTabSizeInSpaces) * scale.x;
planningMarker.x += 8.0f * float(nTabSizeInSpaces) * scale.x;
}
else if (c>=-128&&c<-105)
{
textCol={PixelGameEngine::charToColor[c].r,PixelGameEngine::charToColor[c].g,PixelGameEngine::charToColor[c].b,col.a};
}
else if (c==PixelGameEngine::Reset[0])
{
textCol=col;
}
else if (c=='#')
{
skip=6;
textCol=BLACK;
for(int i=1;i<7;i++){
if(i<3){
textCol.r*=16;
textCol.r+=hexToNumber(sText[index+i]);
}else
if(i<5){
textCol.g*=16;
textCol.g+=hexToNumber(sText[index+i]);
}else{
textCol.b*=16;
textCol.b+=hexToNumber(sText[index+i]);
}
}
if(textCol==WHITE)textCol=col;
}
else
{
int32_t ox = (c - 32) % 16;
int32_t oy = (c - 32) / 16;
planningMarker.x += 8.0f * scale.x;
if(planningMarker.x>width){
if(drawingMarker.x==0){ //The text has overflowed a full line, so we have to dump the line.
for(PixelGameEngine::StringDecalData&letter:letters){
DrawPartialDecal(pos + drawingMarker, pge->fontRenderable.Decal(), letter.sourcePos, letter.sourceSize, scale,letter.col);
drawingMarker.x += 8.0f * scale.x;
}
letters.clear();
}else{
drawingMarker.x-=pge->vFontSpacing[' '-32].y*scale.x; //Don't include the space in the sizing when wrapping.
}
planningMarker.x = 0; planningMarker.y += 8.0f * scale.y;
drawingMarker=planningMarker;
wrappingOccurred=true;
for(PixelGameEngine::StringDecalData&letter:letters){
planningMarker.x += 8.0f * scale.x;
}
}
letters.emplace_back(c,vf2d{ float(ox) * 8.0f, float(oy) * 8.0f }, vf2d{ 8.0f, 8.0f },textCol);
}
}
for(PixelGameEngine::StringDecalData&letter:letters){
DrawPartialDecal(pos + drawingMarker, pge->fontRenderable.Decal(), letter.sourcePos, letter.sourceSize, scale,letter.col);
drawingMarker.x += 8.0f * scale.x;
}
}
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 originalKey{pge->stripCol(sText)};
std::u32string renderStr{pge->stripLeadingCol(sText)};
std::u32string Ukey=U"DSD_"+font.GetFontName()+U"_"+originalKey;
std::string key=std::string(Ukey.begin(),Ukey.end());
const bool RerenderRequired=pge->garbageCollector.count(key)&&pge->garbageCollector[key].originalStr!=std::string(renderStr.begin(),renderStr.end());
if(!pge->garbageCollector.count(key)||RerenderRequired){ //If the text key already exists, don't have to recreate the decal, just update the expire time.
delete pge->garbageCollector[key].decal;
pge->garbageCollector[key].decal=font.RenderStringToDecal(renderStr,WHITE);
pge->garbageCollector[key].originalStr=std::string(originalKey.begin(),originalKey.end());
}
pge->garbageCollector[key].expireTime=pge->GetRunTime()+120.0f;
DrawDecal(pos,pge->garbageCollector[key].decal,scale/4,pge->GetFinalRenderColor(col,sText));
}
void olc::ViewPort::DrawStringPropDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col, const olc::vf2d& scale,const float width){
Pixel textCol=col;
static std::vector<PixelGameEngine::StringDecalData>letters;
letters.clear();
bool wrappingOccurred=false;
olc::vf2d planningMarker = { 0.0f, 0.0f };
olc::vf2d drawingMarker = { 0.0f, 0.0f };
const auto hexToNumber=[](char c){
if(c<='9')return c-'0';
return (c-'A')+10;
};
for (int skip=0,index=-1;auto c : sText)
{
index++;
if(skip){
skip--;
continue;
}
if(c==' '||c=='\t'||c=='\n'){
for(PixelGameEngine::StringDecalData&letter:letters){
DrawPartialDecal(pos + drawingMarker, pge->fontRenderable.Decal(), letter.sourcePos, letter.sourceSize, scale, letter.col);
drawingMarker.x += float(pge->vFontSpacing[letter.c - 32].y) * scale.x;
}
letters.clear();
wrappingOccurred=false;
}
if(wrappingOccurred){
if(c!=' '&&c!='\t'&&c!='\n'){
wrappingOccurred=false;
}else{
continue;
}
}
if(c=='\r')continue; //Ignore carriage returns. Stupid Linux.
if (c == '\n')
{
planningMarker.x = 0; planningMarker.y += 8.0f * scale.y;
for(PixelGameEngine::StringDecalData&letter:letters){
DrawPartialDecal(pos + drawingMarker, pge->fontRenderable.Decal(), letter.sourcePos, letter.sourceSize, scale, letter.col);
drawingMarker.x += float(pge->vFontSpacing[letter.c - 32].y) * scale.x;
}
letters.clear();
drawingMarker.x = 0; drawingMarker.y += 8.0f * scale.y;
}
else if (c == ' ')
{
drawingMarker.x += float(pge->vFontSpacing[' ' - 32].y) * scale.x;
planningMarker.x += float(pge->vFontSpacing[' ' - 32].y) * scale.x;
}
else if (c == '\t')
{
drawingMarker.x += 8.0f * float(nTabSizeInSpaces) * scale.x;
planningMarker.x += 8.0f * float(nTabSizeInSpaces) * scale.x;
}
else if (c>=-128&&c<-105)
{
textCol={PixelGameEngine::charToColor[c].r,PixelGameEngine::charToColor[c].g,PixelGameEngine::charToColor[c].b,col.a};
}
else if (c==PixelGameEngine::Reset[0])
{
textCol=col;
}
else if (c=='#')
{
skip=6;
textCol=BLACK;
for(int i=1;i<7;i++){
if(i<3){
textCol.r*=16;
textCol.r+=hexToNumber(sText[index+i]);
}else
if(i<5){
textCol.g*=16;
textCol.g+=hexToNumber(sText[index+i]);
}else{
textCol.b*=16;
textCol.b+=hexToNumber(sText[index+i]);
}
}
if(textCol==WHITE)textCol=col;
}
else
{
int32_t ox = (c - 32) % 16;
int32_t oy = (c - 32) / 16;
planningMarker.x += float(pge->vFontSpacing[c - 32].y) * scale.x;
if(planningMarker.x>width){
if(drawingMarker.x==0){ //The text has overflowed a full line, so we have to dump the line.
for(PixelGameEngine::StringDecalData&letter:letters){
DrawPartialDecal(pos + drawingMarker, pge->fontRenderable.Decal(), letter.sourcePos, letter.sourceSize, scale,letter.col);
drawingMarker.x += float(pge->vFontSpacing[letter.c - 32].y) * scale.x;
}
letters.clear();
}else{
drawingMarker.x-=float(pge->vFontSpacing[' '-32].y)*scale.x; //Don't include the space in the sizing when wrapping.
}
planningMarker.x = 0; planningMarker.y += 8.0f * scale.y;
drawingMarker=planningMarker;
wrappingOccurred=true;
for(PixelGameEngine::StringDecalData&letter:letters){
planningMarker.x += float(pge->vFontSpacing[letter.c - 32].y) * scale.x;
}
}
letters.emplace_back(c,vf2d{ float(ox) * 8.0f + float(pge->vFontSpacing[c - 32].x), float(oy) * 8.0f }, vf2d{ float(pge->vFontSpacing[c - 32].y), 8.0f },textCol);
}
}
for(PixelGameEngine::StringDecalData&letter:letters){
DrawPartialDecal(pos + drawingMarker, pge->fontRenderable.Decal(), letter.sourcePos, letter.sourceSize, scale, letter.col);
drawingMarker.x += float(pge->vFontSpacing[letter.c - 32].y) * scale.x;
}
}
void olc::ViewPort::DrawShadowStringDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col, const Pixel shadowCol, const olc::vf2d& scale){
DrawShadowStringDecal(pos,sText,col,shadowCol,scale,scale);
}
void olc::ViewPort::DrawShadowStringDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col, const Pixel shadowCol, const olc::vf2d& scale,const olc::vf2d& shadowScale,const float width){
static std::vector<PixelGameEngine::StringDecalData>letters;
letters.clear();
bool wrappingOccurred=false;
olc::vf2d planningMarker = { 0.0f, 0.0f };
olc::vf2d drawingMarker = { 0.0f, 0.0f };
const auto hexToNumber=[](char c){
if(c<='9')return c-'0';
return (c-'A')+10;
};
for (int skip=0,index=-1;auto c : sText)
{
index++;
if(skip){
skip--;
continue;
}
if(c==' '||c=='\t'||c=='\n'){
for(PixelGameEngine::StringDecalData&letter:letters){
DrawPartialRotatedDecal(pos + drawingMarker+vf2d{5.f,5.f}*scale-shadowScale, pge->fontRenderableShadow.Decal(),0.f,{5.f,5.f}, letter.sourcePos, letter.sourceSize,shadowScale,shadowCol);
drawingMarker.x += 8.0f * scale.x;
}
letters.clear();
wrappingOccurred=false;
}
if(wrappingOccurred){
if(c!=' '&&c!='\t'&&c!='\n'){
wrappingOccurred=false;
}else{
continue;
}
}
if(c=='\r')continue; //Ignore carriage returns. Stupid Linux.
if (c == '\n')
{
planningMarker.x = 0; planningMarker.y += 8.0f * scale.y;
for(PixelGameEngine::StringDecalData&letter:letters){
DrawPartialRotatedDecal(pos + drawingMarker+vf2d{5.f,5.f}*scale-shadowScale, pge->fontRenderableShadow.Decal(),0.f,{5.f,5.f}, letter.sourcePos, letter.sourceSize,shadowScale,shadowCol);
drawingMarker.x += 8.0f * scale.x;
}
letters.clear();
drawingMarker.x = 0; drawingMarker.y += 8.0f * scale.y;
}
else if (c == ' ')
{
drawingMarker.x += 8.0f * scale.x;
planningMarker.x += 8.0f * scale.x;
}
else if (c == '\t')
{
drawingMarker.x += 8.0f * float(nTabSizeInSpaces) * scale.x;
planningMarker.x += 8.0f * float(nTabSizeInSpaces) * scale.x;
}
else if (c>=-128&&c<-105){}
else if (c==PixelGameEngine::Reset[0]){}
else if (c=='#')
{
skip=6;
}
else
{
int32_t ox = (c - 32) % 16;
int32_t oy = (c - 32) / 16;
planningMarker.x += 8.0f * scale.x;
if(planningMarker.x>width){
if(drawingMarker.x==0){ //The text has overflowed a full line, so we have to dump the line.
for(PixelGameEngine::StringDecalData&letter:letters){
DrawPartialRotatedDecal(pos + drawingMarker+vf2d{5.f,5.f}*scale-shadowScale, pge->fontRenderableShadow.Decal(),0.f,{5.f,5.f}, letter.sourcePos, letter.sourceSize,shadowScale,shadowCol);
drawingMarker.x += 8.0f * scale.x;
}
letters.clear();
}else{
drawingMarker.x-=pge->vFontSpacing[' '-32].y*scale.x; //Don't include the space in the sizing when wrapping.
}
planningMarker.x = 0; planningMarker.y += 8.0f * scale.y;
drawingMarker=planningMarker;
wrappingOccurred=true;
for(PixelGameEngine::StringDecalData&letter:letters){
planningMarker.x += 8.0f * scale.x;
}
}
letters.emplace_back(c,vf2d{ float(ox) * 10.0f, float(oy) * 10.0f }, vf2d{ 10.0f, 10.0f },shadowCol);
}
}
for(PixelGameEngine::StringDecalData&letter:letters){
DrawPartialRotatedDecal(pos + drawingMarker+vf2d{5.f,5.f}*scale-shadowScale, pge->fontRenderableShadow.Decal(),0.f,{5.f,5.f}, letter.sourcePos, letter.sourceSize,shadowScale,shadowCol);
drawingMarker.x += 8.0f * scale.x;
}
DrawStringDecal(pos,sText,col,scale,width);
}
void olc::ViewPort::DrawShadowStringPropDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col, const Pixel shadowCol, const olc::vf2d& scale){
DrawShadowStringPropDecal(pos,sText,col,shadowCol,scale,scale);
}
void olc::ViewPort::DrawShadowStringPropDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col, const Pixel shadowCol, const olc::vf2d& scale,const olc::vf2d& shadowScale,const float width){
static std::vector<PixelGameEngine::StringDecalData>letters;
letters.clear();
bool wrappingOccurred=false;
olc::vf2d planningMarker = { 0.0f, 0.0f };
olc::vf2d drawingMarker = { 0.0f, 0.0f };
const auto hexToNumber=[](char c){
if(c<='9')return c-'0';
return (c-'A')+10;
};
for (int skip=0,index=-1;auto c : sText)
{
index++;
if(skip){
skip--;
continue;
}
if(c==' '||c=='\t'||c=='\n'){
for(PixelGameEngine::StringDecalData&letter:letters){
DrawPartialRotatedDecal(pos + drawingMarker+vf2d{5.f,5.f}*scale-shadowScale, pge->fontRenderableShadow.Decal(),0.f,{5.f,5.f}, letter.sourcePos, letter.sourceSize,shadowScale,shadowCol);
drawingMarker.x += float(pge->vFontSpacing[letter.c - 32].y) * scale.x;
}
letters.clear();
wrappingOccurred=false;
}
if(wrappingOccurred){
if(c!=' '&&c!='\t'&&c!='\n'){
wrappingOccurred=false;
}else{
continue;
}
}
if(c=='\r')continue; //Ignore carriage returns. Stupid Linux.
if (c == '\n')
{
planningMarker.x = 0; planningMarker.y += 8.0f * scale.y;
for(PixelGameEngine::StringDecalData&letter:letters){
DrawPartialRotatedDecal(pos + drawingMarker+vf2d{5.f,5.f}*scale-shadowScale, pge->fontRenderableShadow.Decal(),0.f,{5.f,5.f}, letter.sourcePos, letter.sourceSize,shadowScale,shadowCol);
drawingMarker.x += float(pge->vFontSpacing[letter.c - 32].y) * scale.x;
}
letters.clear();
drawingMarker.x = 0; drawingMarker.y += 8.0f * scale.y;
}
else if (c == ' ')
{
drawingMarker.x += float(pge->vFontSpacing[' ' - 32].y) * scale.x;
planningMarker.x += float(pge->vFontSpacing[' ' - 32].y) * scale.x;
}
else if (c == '\t')
{
drawingMarker.x += 8.0f * float(nTabSizeInSpaces) * scale.x;
planningMarker.x += 8.0f * float(nTabSizeInSpaces) * scale.x;
}
else if (c>=-128&&c<-105){}
else if (c==PixelGameEngine::Reset[0]){}
else if (c=='#')
{
skip=6;
}
else
{
int32_t ox = (c - 32) % 16;
int32_t oy = (c - 32) / 16;
planningMarker.x += float(pge->vFontSpacing[c - 32].y) * scale.x;
if(planningMarker.x>width){
if(drawingMarker.x==0){ //The text has overflowed a full line, so we have to dump the line.
for(PixelGameEngine::StringDecalData&letter:letters){
DrawPartialRotatedDecal(pos + drawingMarker+vf2d{5.f,5.f}*scale-shadowScale, pge->fontRenderableShadow.Decal(),0.f,{5.f,5.f}, letter.sourcePos, letter.sourceSize,shadowScale,shadowCol);
drawingMarker.x += float(pge->vFontSpacing[letter.c - 32].y) * scale.x;
}
letters.clear();
}else{
drawingMarker.x-=pge->vFontSpacing[' '-32].y*scale.x; //Don't include the space in the sizing when wrapping.
}
planningMarker.x = 0; planningMarker.y += 8.0f * scale.y;
drawingMarker=planningMarker;
wrappingOccurred=true;
for(PixelGameEngine::StringDecalData&letter:letters){
planningMarker.x += float(pge->vFontSpacing[letter.c - 32].y) * scale.x;
}
}
letters.emplace_back(c,vf2d{ float(ox) * 10.0f + float(pge->vFontSpacing[c - 32].x), float(oy) * 10.0f }, vf2d{ float(pge->vFontSpacing[c - 32].y)+2, 10.0f },shadowCol);
}
}
for(PixelGameEngine::StringDecalData&letter:letters){
DrawPartialRotatedDecal(pos + drawingMarker+vf2d{5.f,5.f}*scale-shadowScale, pge->fontRenderableShadow.Decal(),0.f,{5.f,5.f}, letter.sourcePos, letter.sourceSize,shadowScale,shadowCol);
drawingMarker.x += float(pge->vFontSpacing[letter.c - 32].y) * scale.x;
}
DrawStringPropDecal(pos,sText,col,scale,width);
}
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 originalKey{pge->stripCol(sText)};
std::u32string renderStr{pge->stripLeadingCol(sText)};
std::u32string Ukey=U"DSSD_"+font.GetFontName()+U"_"+originalKey;
std::string key=std::string(Ukey.begin(),Ukey.end());
const bool RerenderRequired=pge->garbageCollector.count(key)&&pge->garbageCollector[key].originalStr!=std::string(renderStr.begin(),renderStr.end());
if(!pge->garbageCollector.count(key)||RerenderRequired){ //If the text key already exists, don't have to recreate the decal, just update the expire time.
delete pge->garbageCollector[key].decal;
pge->garbageCollector[key].decal=font.RenderStringToDecal(renderStr,WHITE);
pge->garbageCollector[key].originalStr=std::string(renderStr.begin(),renderStr.end());
}
pge->garbageCollector[key].expireTime=pge->GetRunTime()+120.0f;
std::erase_if(pge->garbageCollector,[&](auto&key){
if(key.second.expireTime<pge->GetRunTime()){
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,pge->GetFinalRenderColor(col,sText));
}
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 originalKey{pge->stripCol(sText)};
std::u32string renderStr{pge->stripLeadingCol(sText)};
std::u32string Ukey=U"DDSSD_"+font.GetFontName()+U"_"+originalKey;
std::string key=std::string(Ukey.begin(),Ukey.end());
const bool RerenderRequired=pge->garbageCollector.count(key)&&pge->garbageCollector[key].originalStr!=std::string(renderStr.begin(),renderStr.end());
if(!pge->garbageCollector.count(key)||RerenderRequired){ //If the text key already exists, don't have to recreate the decal, just update the expire time.
delete pge->garbageCollector[key].decal;
pge->garbageCollector[key].decal=font.RenderStringToDecal(renderStr,WHITE);
pge->garbageCollector[key].originalStr=std::string(renderStr.begin(),renderStr.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,pge->GetFinalRenderColor(col,sText));
}
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