From 6ee7e293d5fe565db3296b726dd2e70854fc0c9e Mon Sep 17 00:00:00 2001 From: sigonasr2 Date: Wed, 21 Aug 2024 00:34:46 -0500 Subject: [PATCH] Add Viewport PGEX back in for radar stuff. --- assets/radar.png | Bin 0 -> 5482 bytes assets/radar.xcf | Bin 0 -> 13653 bytes src/HamsterGame.cpp | 12 +- src/HamsterGame.h | 2 + src/olcPGEX_Viewport.h | 722 +++++++++++++++++++++++++++++++++++++ src/olcPixelGameEngine.cpp | 4 +- src/util.cpp | 3 + src/util.h | 1 + 8 files changed, 742 insertions(+), 2 deletions(-) create mode 100644 assets/radar.png create mode 100644 assets/radar.xcf create mode 100644 src/olcPGEX_Viewport.h diff --git a/assets/radar.png b/assets/radar.png new file mode 100644 index 0000000000000000000000000000000000000000..3c70953bb9ad7ec6579577f7b6b48a06e9afb1d3 GIT binary patch literal 5482 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 literal 0 HcmV?d00001 diff --git a/assets/radar.xcf b/assets/radar.xcf new file mode 100644 index 0000000000000000000000000000000000000000..94d43ed795426f3a22f1c0e95842943c671856ec GIT binary patch literal 13653 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 + for(int i=360;i>=0;i-=4){ + float angle=util::degToRad(float(i))-geom2d::pi/2; + if(i==360){radarCircle.push_back(vf2d{cos(angle),sin(angle)}*42+43);} + radarCircle.push_back(vf2d{cos(angle),sin(angle)}*43+vf2d{43,44}); + } + radar=ViewPort{radarCircle,{5.f,8.f}}; return true; } @@ -57,6 +64,7 @@ void HamsterGame::LoadGraphics(){ _LoadImage("fuelbar_outline.png"); _LoadImage("speedometer.png"); _LoadImage("speedometer_overlay.png"); + _LoadImage("radar.png"); UpdateMatrixTexture(); } @@ -190,7 +198,7 @@ void HamsterGame::DrawGame(){ for(int y:std::ranges::iota_view(0,4)){ for(int x:std::ranges::iota_view(0,2)){ const int powerupInd{y*2+x}; - const float drawX{x*32.f+20.f}; + const float drawX{x*32.f+16.f}; const float drawY{y*32.f+12.f+96.f}; const Powerup::PowerupType powerupType{Powerup::PowerupType(powerupInd)}; const geom2d::rectpowerupSubimageRect{Powerup::GetPowerupSubimageRect(powerupType)}; @@ -246,6 +254,8 @@ void HamsterGame::DrawGame(){ } } DrawStringDecal(SCREEN_FRAME.pos+SCREEN_FRAME.size-speedometerStrSize-vf2d{4.f,4.f},speedometerStr,speedometerCol); + radar.FillRectDecal({},{128,128},GREEN); + DrawDecal({2.f,4.f},GetGFX("radar.png").Decal()); } const Terrain::TerrainType HamsterGame::GetTerrainTypeAtPos(const vf2d pos)const{ diff --git a/src/HamsterGame.h b/src/HamsterGame.h index 87d2440..f1f49f1 100644 --- a/src/HamsterGame.h +++ b/src/HamsterGame.h @@ -48,6 +48,7 @@ All rights reserved. #include "SpecialRenderable.h" #include "olcPGEX_Graphics3D.h" #include "AnimationState.h" +#include "olcPGEX_Viewport.h" struct Letter{ vf2d pos; @@ -112,4 +113,5 @@ private: vf2d cloudSpd{}; vf2d cloudOffset{}; float speedometerDisplayAmt{0.f}; + ViewPort radar; }; \ No newline at end of file diff --git a/src/olcPGEX_Viewport.h b/src/olcPGEX_Viewport.h new file mode 100644 index 0000000..b179eca --- /dev/null +++ b/src/olcPGEX_Viewport.h @@ -0,0 +1,722 @@ +#pragma once + +#include "olcPixelGameEngine.h" + +#include +#include +#include +#include +#include +#include + +// Declarations +namespace olc { + class ViewPort : public olc::PGEX { + public: + ViewPort(); + //Define a set of vertices to construct this viewport with. Winding order is counter-clockwise. + ViewPort(std::vector vertices, vf2d offset = {0, 0}); + virtual ~ViewPort(); + void addPoint(vf2d point); + void clear(); + void drawEdges(); + void setOffset(vf2d offset); + + static ViewPort rectViewPort(vf2d topLeft, + vf2d size, + olc::vf2d offset = {0, 0}); + + void DrawDecal(const olc::vf2d &pos, + olc::Decal *decal, + const olc::vf2d &scale = {1.0f, 1.0f}, + const olc::Pixel &tint = olc::WHITE) const; + void DrawPartialDecal(const olc::vf2d &pos, + olc::Decal *decal, + const olc::vf2d &source_pos, + const olc::vf2d &source_size, + const olc::vf2d &scale = {1.0f, 1.0f}, + const olc::Pixel &tint = olc::WHITE) const; + void DrawPartialDecal(const vf2d &pos, + const vf2d &size, + Decal *decal, + const vf2d source_pos, + const vf2d &source_size, + const Pixel &tint = olc::WHITE) const; + void DrawExplicitDecal(olc::Decal *decal, + const olc::vf2d *pos, + const olc::vf2d *uv, + const olc::Pixel *col, + uint32_t elements = 4) const; + void DrawWarpedDecal(Decal *decal, + const vf2d (&pos)[4], + const Pixel &tint = WHITE) const; + void DrawWarpedDecal(Decal *decal, + const vf2d *pos, + const Pixel &tint = WHITE) const; + void DrawWarpedDecal(Decal *decal, + const std::array &pos, + const Pixel &tint = WHITE) const; + void DrawPartialWarpedDecal(Decal *decal, + const vf2d (&pos)[4], + const vf2d &source_pos, + const vf2d &source_size, + const Pixel &tint = WHITE) const; + void DrawPartialWarpedDecal(Decal *decal, + const vf2d *pos, + const vf2d &source_pos, + const vf2d &source_size, + const Pixel &tint = WHITE) const; + void DrawPartialWarpedDecal(Decal *decal, + const std::array &pos, + const vf2d &source_pos, + const vf2d &source_size, + const Pixel &tint = WHITE) const; + void DrawRotatedDecal(const vf2d &pos, + Decal *decal, + const float fAngle, + const vf2d ¢er = {0.0f, 0.0f}, + const vf2d &scale = {1.0f, 1.0f}, + const Pixel &tint = WHITE) const; + void DrawPartialRotatedDecal(const vf2d &pos, + Decal *decal, + const float fAngle, + const vf2d ¢er, + const vf2d &source_pos, + const vf2d &source_size, + const vf2d &scale = {1.0f, 1.0f}, + const Pixel &tint = WHITE) const; + void DrawRectDecal(const vf2d &pos, + const vf2d &size, + const Pixel col = WHITE) const; + void FillRectDecal(const vf2d &pos, + const vf2d &size, + const Pixel col = WHITE) const; + void GradientFillRectDecal(const vf2d &pos, + const vf2d &size, + const Pixel colTL, + const Pixel colBL, + const Pixel colBR, + const Pixel colTR) const; + void DrawPolygonDecal(Decal *decal, + const std::vector &pos, + const std::vector &uv, + const Pixel tint = WHITE) const; + void DrawPolygonDecal(Decal *decal, + const std::vector &pos, + const std::vector &depth, + const std::vector &uv, + const Pixel tint = WHITE) const; + void DrawPolygonDecal(Decal *decal, + const std::vector &pos, + const std::vector &uv, + const std::vector &tint) const; + void DrawLineDecal(const vf2d &pos1, + const vf2d &pos2, + Pixel p = WHITE) const; + + private: + void drawClippedDecal(Decal *decal, + const vf2d *points, + const vf2d *uvs, + const Pixel *col, + uint32_t elements = 0) const; + void drawClippedPolygonDecal(Decal *decal, + const vf2d *points, + const vf2d *uvs, + const float *depth, + const Pixel tint, + uint32_t elements = 0) const; + + static bool ccw(vf2d A,vf2d B,vf2d C); + static bool intersect(vf2d A,vf2d B,vf2d C,vf2d D); + static float lineSegmentIntersect(vf2d lineA, + vf2d lineB, + vf2d segmentA, + vf2d segmentB); + static float directionFromLine(vf2d lineA, vf2d lineB, vf2d point); + + std::vector clipVertices; + olc::vf2d offset; + }; +} // namespace olc + +// Definitions + +#ifdef OLC_PGEX_VIEWPORT +#undef OLC_PGEX_VIEWPORT + +olc::ViewPort::ViewPort() { +} +olc::ViewPort::~ViewPort() { +} + +olc::ViewPort::ViewPort(std::vector vertices, olc::vf2d offset) + : clipVertices{vertices}, + offset{offset} { +} + +void olc::ViewPort::addPoint(vf2d point) { + clipVertices.push_back(point); +} + +void olc::ViewPort::clear() { + clipVertices.clear(); +} + +void olc::ViewPort::drawEdges() { + for (auto i = 0u; i < clipVertices.size(); i++) { + auto current = clipVertices[i] + offset; + auto next = clipVertices[(i + 1) % clipVertices.size()] + offset; + + pge->DrawLineDecal(current, next, olc::RED); + } +} + +void olc::ViewPort::setOffset(vf2d offset) { + this->offset = offset; +} + +olc::ViewPort + olc::ViewPort::rectViewPort(vf2d topLeft, vf2d size, olc::vf2d offset) { + return {{ + topLeft, + {topLeft.x, topLeft.y + size.y}, + topLeft + size, + {topLeft.x + size.x, topLeft.y}, + }, + offset}; +} + +void olc::ViewPort::DrawDecal(const olc::vf2d &pos, + olc::Decal *decal, + const olc::vf2d &scale, + const olc::Pixel &tint) const { + std::vector points{ + pos, + {pos.x, pos.y + decal->sprite->height * scale.y}, + {pos.x + decal->sprite->width * scale.x, + pos.y + decal->sprite->height * scale.y}, + {pos.x + decal->sprite->width * scale.x, pos.y}, + }; + DrawWarpedDecal(decal, points.data(), tint); +} + +void olc::ViewPort::DrawPartialDecal(const olc::vf2d &pos, + olc::Decal *decal, + const olc::vf2d &source_pos, + const olc::vf2d &source_size, + const olc::vf2d &scale, + const olc::Pixel &tint) const { + DrawPartialDecal(pos, source_size * scale, decal, source_pos, source_size, tint); +} + +void olc::ViewPort::DrawPartialDecal(const vf2d &pos, + const vf2d &size, + Decal *decal, + const vf2d source_pos, + const vf2d &source_size, + const Pixel &tint) const { + std::vector points{ + pos, + {pos.x, pos.y + size.y}, + pos + size, + {pos.x + size.x, pos.y}, + }; + DrawPartialWarpedDecal(decal, points.data(), source_pos, source_size, tint); +} + +void olc::ViewPort::DrawExplicitDecal(olc::Decal *decal, + const olc::vf2d *pos, + const olc::vf2d *uv, + const olc::Pixel *col, + uint32_t elements) const { + drawClippedDecal(decal, pos, uv, col, elements); +} + +void olc::ViewPort::DrawWarpedDecal(Decal *decal, + const vf2d (&pos)[4], + const Pixel &tint) const { + DrawWarpedDecal(decal, (const vf2d *)pos, tint); +} +void olc::ViewPort::DrawWarpedDecal(Decal *decal, + const vf2d *pos, + const Pixel &tint) const { + std::vector w{ 1, 1, 1, 1 }; + std::vector newPos; + newPos.resize(4); + std::vector uvs{ + {0, 0}, + {0, 1}, + {1, 1}, + {1, 0}, + }; + std::vector cols{ + tint, + tint, + tint, + tint, + }; + + olc::vf2d vInvScreenSize={ 1.0f / pge->GetScreenSize().x, 1.0f / pge->GetScreenSize().y }; + + olc::vf2d center; + float rd = ((pos[2].x - pos[0].x) * (pos[3].y - pos[1].y) - (pos[3].x - pos[1].x) * (pos[2].y - pos[0].y)); + if (rd != 0) + { + rd = 1.0f / rd; + float rn = ((pos[3].x - pos[1].x) * (pos[0].y - pos[1].y) - (pos[3].y - pos[1].y) * (pos[0].x - pos[1].x)) * rd; + float sn = ((pos[2].x - pos[0].x) * (pos[0].y - pos[1].y) - (pos[2].y - pos[0].y) * (pos[0].x - pos[1].x)) * rd; + if (!(rn < 0.f || rn > 1.f || sn < 0.f || sn > 1.f)) center = pos[0] + rn * (pos[2] - pos[0]); + float d[4]; for (int i = 0; i < 4; i++) d[i] = (pos[i] - center).mag(); + for (int i = 0; i < 4; i++) + { + float q = d[i] == 0.0f ? 1.0f : (d[i] + d[(i + 2) & 3]) / d[(i + 2) & 3]; + uvs[i] *= q; w[i] *= q; + } + + drawClippedPolygonDecal(decal, pos, uvs.data(), w.data(), tint, 4); + } +} +void olc::ViewPort::DrawWarpedDecal(Decal *decal, + const std::array &pos, + const Pixel &tint) const { + DrawWarpedDecal(decal, pos.data(), tint); +} + +void olc::ViewPort::DrawPartialWarpedDecal(Decal *decal, + const vf2d (&pos)[4], + const vf2d &source_pos, + const vf2d &source_size, + const Pixel &tint) const { + DrawPartialWarpedDecal(decal, + (const vf2d *)pos, + source_pos, + source_size, + tint); +} + +void olc::ViewPort::DrawPartialWarpedDecal(Decal *decal, + const vf2d *pos, + const vf2d &source_pos, + const vf2d &source_size, + const Pixel &tint) const { + olc::vf2d sourceUvPos = + source_pos + / olc::vf2d{static_cast(decal->sprite->width), + static_cast(decal->sprite->height)}; + olc::vf2d sourceUvSize = + source_size + / olc::vf2d{static_cast(decal->sprite->width), + static_cast(decal->sprite->height)}; + std::vector uvs{ + sourceUvPos, + {sourceUvPos.x, sourceUvPos.y + sourceUvSize.y}, + sourceUvPos + sourceUvSize, + {sourceUvPos.x + sourceUvSize.x, sourceUvPos.y}, + }; + std::vector cols{ + tint, + tint, + tint, + tint, + }; + + std::vectorws{1,1,1,1}; + + olc::vf2d center; + float rd = ((pos[2].x - pos[0].x) * (pos[3].y - pos[1].y) - (pos[3].x - pos[1].x) * (pos[2].y - pos[0].y)); + if (rd != 0) + { + rd = 1.0f / rd; + float rn = ((pos[3].x - pos[1].x) * (pos[0].y - pos[1].y) - (pos[3].y - pos[1].y) * (pos[0].x - pos[1].x)) * rd; + float sn = ((pos[2].x - pos[0].x) * (pos[0].y - pos[1].y) - (pos[2].y - pos[0].y) * (pos[0].x - pos[1].x)) * rd; + if (!(rn < 0.f || rn > 1.f || sn < 0.f || sn > 1.f)) center = pos[0] + rn * (pos[2] - pos[0]); + float d[4]; for (int i = 0; i < 4; i++) d[i] = (pos[i] - center).mag(); + for (int i = 0; i < 4; i++) + { + float q = d[i] == 0.0f ? 1.0f : (d[i] + d[(i + 2) & 3]) / d[(i + 2) & 3]; + uvs[i] *= q; ws[i] *= q; + } + + drawClippedPolygonDecal(decal, pos, uvs.data(), ws.data(), tint, 4); + } +} + +void olc::ViewPort::DrawPartialWarpedDecal(Decal *decal, + const std::array &pos, + const vf2d &source_pos, + const vf2d &source_size, + const Pixel &tint) const { + DrawPartialWarpedDecal(decal, pos.data(), source_pos, source_size, tint); +} + +void olc::ViewPort::DrawRotatedDecal(const vf2d &pos, + Decal *decal, + const float fAngle, + const vf2d ¢er, + const vf2d &scale, + const Pixel &tint) const { + auto sin = std::sin(fAngle); + auto cos = std::cos(fAngle); + + std::vector points{ + -center * scale, + olc::vf2d{-center.x, decal->sprite->height - center.y} * scale, + olc::vf2d{decal->sprite->width - center.x, + decal->sprite->height - center.y} + * scale, + olc::vf2d{decal->sprite->width - center.x, -center.y} * scale, + }; + + for (auto i = 0u; i < points.size(); i++) { + points[i] = pos + + olc::vf2d{points[i].x * cos - points[i].y * sin, + points[i].x * sin + points[i].y * cos}; + } + + DrawWarpedDecal(decal, points.data(), tint); +} + +void olc::ViewPort::DrawPartialRotatedDecal(const vf2d &pos, + Decal *decal, + const float fAngle, + const vf2d ¢er, + const vf2d &source_pos, + const vf2d &source_size, + const vf2d &scale, + const Pixel &tint) const { + auto sin = std::sin(fAngle); + auto cos = std::cos(fAngle); + + std::vector points{ + -center * scale, + olc::vf2d{-center.x, source_size.y - center.y} * scale, + (source_size - center) * scale, + olc::vf2d{source_size.x - center.x, -center.y} * scale, + }; + + for (auto i = 0u; i < points.size(); i++) { + points[i] = pos + + olc::vf2d{points[i].x * cos - points[i].y * sin, + points[i].x * sin + points[i].y * cos}; + } + + DrawPartialWarpedDecal(decal, points.data(), source_pos, source_size, tint); +} + +void olc::ViewPort::DrawRectDecal(const vf2d &pos, + const vf2d &size, + const Pixel col) const { + std::vector points{ + pos, + {pos.x, pos.y + size.y}, + pos + size, + {pos.x + size.x, pos.y}, + }; + + // Ideally we use the wireframe mode just like the PGE, + // however we can't save the current decal mode which + // can impact some applications so instead we draw 4 + // lines. + + DrawLineDecal(points[0],points[1],col); + DrawLineDecal(points[1],points[2],col); + DrawLineDecal(points[2],points[3],col); + DrawLineDecal(points[3],points[0],col); +} + +void olc::ViewPort::FillRectDecal(const vf2d &pos, + const vf2d &size, + const Pixel col) const { + std::vector points{ + pos, + {pos.x, pos.y + size.y}, + pos + size, + {pos.x + size.x, pos.y}, + }; + std::vector uvs{ + {0, 0}, + {0, 1}, + {1, 1}, + {1, 0}, + }; + + DrawPolygonDecal(nullptr, points, uvs, col); +} + +void olc::ViewPort::GradientFillRectDecal(const vf2d &pos, + const vf2d &size, + const Pixel colTL, + const Pixel colBL, + const Pixel colBR, + const Pixel colTR) const { + std::vector points{ + pos, + {pos.x, pos.y + size.y}, + pos + size, + {pos.x + size.x, pos.y}, + }; + + std::vector uvs{ + {0, 0}, + {0, 1}, + {1, 1}, + {1, 0}, + }; + + std::vector colors{ + colTL, + colBL, + colBR, + colTR, + }; + + drawClippedDecal(nullptr, points.data(), uvs.data(), colors.data(), points.size()); +} + +void olc::ViewPort::DrawPolygonDecal(Decal *decal, + const std::vector &pos, + const std::vector &uv, + const Pixel tint) const { + std::vector colors; + colors.resize(pos.size()); + for (auto i = 0u; i < colors.size(); i++) { + colors[i] = tint; + } + + drawClippedDecal(decal, pos.data(), uv.data(), colors.data(), pos.size()); +} + +void olc::ViewPort::DrawPolygonDecal(Decal *decal, + const std::vector &pos, + const std::vector &depth, + const std::vector &uv, + const Pixel tint) const { + drawClippedPolygonDecal(decal, pos.data(), uv.data(), depth.data(), tint, pos.size()); +} + +void olc::ViewPort::DrawPolygonDecal(Decal *decal, + const std::vector &pos, + const std::vector &uv, + const std::vector &tint) const { + drawClippedDecal(decal, pos.data(), uv.data(), tint.data(), pos.size()); +} + +void olc::ViewPort::DrawLineDecal(const vf2d &pos1, + const vf2d &pos2, + Pixel p) const { + vf2d posA = pos1; + vf2d posB = pos2; + + for (auto i = 0u; i < clipVertices.size(); i++) { + auto clipA = clipVertices[i] - offset; + auto clipB = clipVertices[(i + 1) % clipVertices.size()] - offset; + + auto intersection = lineSegmentIntersect(clipA, clipB, posA, posB); + if (intersection < 0 || intersection > 1) { + continue; + } + + auto clipDirection = directionFromLine(clipA, clipB, posA); + auto intersectionPoint = posA + (posB - posA) * intersection; + + if (clipDirection >= 0) { + posA = intersectionPoint; + } else { + posB = intersectionPoint; + } + } + + + // Inside check. Draw a ray to the edge of the screen and count the times + // it intersects. When odd, we are inside a shape, when even we are outside + // of it. + + vf2d leftEdgeA = {0.f,posA.y}; + vf2d leftEdgeB = {0.f,posB.y}; + + int leftEdgeIntersectionsA = 0; + int leftEdgeIntersectionsB = 0; + for (auto i = 0u; i < clipVertices.size(); i++) { + auto clipA = clipVertices[i] - offset; + auto clipB = clipVertices[(i + 1) % clipVertices.size()] - offset; + auto leftEdgeIntersectA = intersect(clipA, clipB, leftEdgeA, posA); + auto leftEdgeIntersectB = intersect(clipA, clipB, leftEdgeB, posB); + + if (leftEdgeIntersectA) { + leftEdgeIntersectionsA++; + } + if (leftEdgeIntersectB) { + leftEdgeIntersectionsB++; + } + } + + // If we found an intersection, we are drawing this line. + // + // Otherwise, if either count is odd, one point is at + // least inside the shape, so render it. + if (leftEdgeIntersectionsA % 2 == 1 || leftEdgeIntersectionsB % 2 == 1) { + pge->DrawLineDecal(posA, posB, p); + } +} + +void olc::ViewPort::drawClippedDecal(Decal *decal, + const vf2d *points, + const vf2d *uvs, + const Pixel *col, + uint32_t elements) const { + std::vector outputList{points, points + elements}; + std::vector outputUvs{uvs, uvs + elements}; + std::vector outputCols{col, col + elements}; + + for (auto i = 0u; i < clipVertices.size(); i++) { + auto clipA = clipVertices[i]; + auto clipB = clipVertices[(i + 1) % clipVertices.size()]; + + auto inputList{outputList}; + auto inputUvs{outputUvs}; + auto inputCols{outputCols}; + outputList.clear(); + outputUvs.clear(); + outputCols.clear(); + + for (auto i = 0u; i < inputList.size(); i++) { + auto polygonA = inputList[i]; + auto polygonB = inputList[(i + 1) % inputList.size()]; + auto uvA = inputUvs[i]; + auto uvB = inputUvs[(i + 1) % inputList.size()]; + auto colA = inputCols[i]; + auto colB = inputCols[(i + 1) % inputList.size()]; + + auto intersection = + lineSegmentIntersect(clipA, clipB, polygonA, polygonB); + auto intersectionPoint = + polygonA + (polygonB - polygonA) * intersection; + auto intersectionUv = uvA + (uvB - uvA) * intersection; + auto intersectionCol = PixelLerp(colA, colB, intersection); + + float aDirection = directionFromLine(clipA, clipB, polygonA); + float bDirection = directionFromLine(clipA, clipB, polygonB); + + if (bDirection <= 0) { + if (aDirection > 0) { + outputList.push_back(intersectionPoint); + outputUvs.push_back(intersectionUv); + outputCols.push_back(intersectionCol); + } + outputList.push_back(polygonB); + outputUvs.push_back(uvB); + outputCols.push_back(colB); + } else if (aDirection <= 0) { + outputList.push_back(intersectionPoint); + outputUvs.push_back(intersectionUv); + outputCols.push_back(intersectionCol); + } + } + } + + if (outputList.size() == 0) { + return; + } + + for (auto &point : outputList) { + point += offset; + } + + pge->DrawExplicitDecal(decal, + outputList.data(), + outputUvs.data(), + outputCols.data(), + outputList.size()); +} +void olc::ViewPort::drawClippedPolygonDecal(Decal *decal, + const vf2d *points, + const vf2d *uvs, + const float *depth, + const Pixel tint, + uint32_t elements) const { + std::vector outputList{points, points + elements}; + std::vector outputUvs{uvs, uvs + elements}; + std::vector outputDepths{depth, depth + elements}; + + for (auto i = 0u; i < clipVertices.size(); i++) { + auto clipA = clipVertices[i]; + auto clipB = clipVertices[(i + 1) % clipVertices.size()]; + + auto inputList{outputList}; + auto inputUvs{outputUvs}; + auto inputWs{outputDepths}; + outputList.clear(); + outputUvs.clear(); + outputDepths.clear(); + + for (auto i = 0u; i < inputList.size(); i++) { + auto polygonA = inputList[i]; + auto polygonB = inputList[(i + 1) % inputList.size()]; + auto uvA = inputUvs[i]; + auto uvB = inputUvs[(i + 1) % inputList.size()]; + auto Wa = inputWs[i]; + auto Wb = inputWs[(i + 1) % inputList.size()]; + + auto intersection = + lineSegmentIntersect(clipA, clipB, polygonA, polygonB); + auto intersectionPoint = + polygonA + (polygonB - polygonA) * intersection; + auto intersectionUv = uvA + (uvB - uvA) * intersection; + auto intersectionDepth = Wa + (Wb - Wa) * intersection; + + float aDirection = directionFromLine(clipA, clipB, polygonA); + float bDirection = directionFromLine(clipA, clipB, polygonB); + + if (bDirection <= 0) { + if (aDirection > 0) { + outputList.push_back(intersectionPoint); + outputUvs.push_back(intersectionUv); + outputDepths.push_back(intersectionDepth); + } + outputList.push_back(polygonB); + outputUvs.push_back(uvB); + outputDepths.push_back(Wb); + } else if (aDirection <= 0) { + outputList.push_back(intersectionPoint); + outputUvs.push_back(intersectionUv); + outputDepths.push_back(intersectionDepth); + } + } + } + + for (auto &point : outputList) { + point += offset; + } + + pge->DrawPolygonDecal(decal, + outputList, + outputDepths, + outputUvs, + tint); +} + +bool olc::ViewPort::ccw(vf2d A,vf2d B,vf2d C) { + return (C.y-A.y) * (B.x-A.x) > (B.y-A.y) * (C.x-A.x); +} + +bool olc::ViewPort::intersect(vf2d A,vf2d B,vf2d C,vf2d D) { + return ccw(A,C,D) != ccw(B,C,D) and ccw(A,B,C) != ccw(A,B,D); +} + +float olc::ViewPort::lineSegmentIntersect(vf2d lineA, + vf2d lineB, + vf2d segmentA, + vf2d segmentB) { + return ((lineA.x - segmentA.x) * (lineA.y - lineB.y) + - (lineA.y - segmentA.y) * (lineA.x - lineB.x)) + / ((lineA.x - lineB.x) * (segmentA.y - segmentB.y) + - (lineA.y - lineB.y) * (segmentA.x - segmentB.x)); +} + +float olc::ViewPort::directionFromLine(vf2d lineA, vf2d lineB, vf2d point) { + return (lineB.x - lineA.x) * (point.y - lineA.y) + - (point.x - lineA.x) * (lineB.y - lineA.y); +} + +#endif \ No newline at end of file diff --git a/src/olcPixelGameEngine.cpp b/src/olcPixelGameEngine.cpp index 1edd76a..074c5d1 100644 --- a/src/olcPixelGameEngine.cpp +++ b/src/olcPixelGameEngine.cpp @@ -8,4 +8,6 @@ #define TSX_PARSER_SETUP #include "TSXParser.h" #define OLC_PGEX_GRAPHICS3D -#include "olcPGEX_Graphics3D.h" \ No newline at end of file +#include "olcPGEX_Graphics3D.h" +#define OLC_PGEX_VIEWPORT +#include "olcPGEX_Viewport.h" \ No newline at end of file diff --git a/src/util.cpp b/src/util.cpp index eced7c2..8457b08 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -50,4 +50,7 @@ void olc::util::turn_towards_direction(float&angle,float target,float rate) float olc::util::lerp(float n1,float n2,double t){ return float(n1*(1-t)+n2*t); +} +float olc::util::degToRad(float deg){ + return deg*(geom2d::pi/180); } \ No newline at end of file diff --git a/src/util.h b/src/util.h index 08c2079..c24bc8c 100644 --- a/src/util.h +++ b/src/util.h @@ -48,4 +48,5 @@ namespace olc::util{ float angle_difference(float angle_1, float angle_2); void turn_towards_direction(float&angle,float target,float rate); float lerp(float n1,float n2,double t); + float degToRad(float deg); }; \ No newline at end of file