Remove dependency on olcPGEX_ViewPort. Add olcPGEX_TransformedView. Update border to be in front of the rest of the game drawing.
parent
1a6980b3d4
commit
2b20570eb3
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 11 KiB |
Binary file not shown.
@ -0,0 +1,770 @@ |
|||||||
|
/*
|
||||||
|
olcPGEX_TransformedView.h |
||||||
|
|
||||||
|
+-------------------------------------------------------------+ |
||||||
|
| OneLoneCoder Pixel Game Engine Extension | |
||||||
|
| Transformed View v1.09 | |
||||||
|
+-------------------------------------------------------------+ |
||||||
|
|
||||||
|
NOTE: UNDER ACTIVE DEVELOPMENT - THERE ARE BUGS/GLITCHES |
||||||
|
|
||||||
|
What is this? |
||||||
|
~~~~~~~~~~~~~ |
||||||
|
This extension provides drawing routines that are compatible with |
||||||
|
changeable world and screen spaces. For example you can pan and |
||||||
|
zoom, and all PGE drawing routines will automatically adopt the current |
||||||
|
world scales and offsets. |
||||||
|
|
||||||
|
License (OLC-3) |
||||||
|
~~~~~~~~~~~~~~~ |
||||||
|
|
||||||
|
Copyright 2018 - 2024 OneLoneCoder.com |
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without |
||||||
|
modification, are permitted provided that the following conditions |
||||||
|
are met: |
||||||
|
|
||||||
|
1. Redistributions or derivations of source code must retain the above |
||||||
|
copyright notice, this list of conditions and the following disclaimer. |
||||||
|
|
||||||
|
2. Redistributions or derivative works in binary form must reproduce |
||||||
|
the above copyright notice. This list of conditions and the following |
||||||
|
disclaimer must be reproduced in the documentation and/or other |
||||||
|
materials provided with the distribution. |
||||||
|
|
||||||
|
3. Neither the name of the copyright holder nor the names of its |
||||||
|
contributors may be used to endorse or promote products derived |
||||||
|
from this software without specific prior written permission. |
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||||
|
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
|
||||||
|
Links |
||||||
|
~~~~~ |
||||||
|
YouTube: https://www.youtube.com/javidx9
|
||||||
|
Discord: https://discord.gg/WhwHUMV
|
||||||
|
Twitter: https://www.twitter.com/javidx9
|
||||||
|
Twitch: https://www.twitch.tv/javidx9
|
||||||
|
GitHub: https://www.github.com/onelonecoder
|
||||||
|
Homepage: https://www.onelonecoder.com
|
||||||
|
|
||||||
|
Author |
||||||
|
~~~~~~ |
||||||
|
David Barr, aka javidx9, ©OneLoneCoder 2019, 2020, 2021, 2022, 2023, 2024 |
||||||
|
|
||||||
|
Revisions: |
||||||
|
1.00: Initial Release |
||||||
|
1.01: Fix for rounding error when scaling to screen |
||||||
|
1.02: Added DrawLineDecal for convenience |
||||||
|
1.03: Removed std::floor from WorldToScreen() |
||||||
|
Added HandlePanAndZoom(...) convenience function |
||||||
|
Removed unused "range" facility in TileTransformView |
||||||
|
1.04: Added DrawPolygonDecal() for arbitrary polygons |
||||||
|
1.05: Clipped DrawSprite() to visible area, massive performance increase |
||||||
|
1.06: Fixed error in DrawLine() - Thanks CraisyDaisyRecords (& Fern)! |
||||||
|
1.07: +DrawRectDecal() |
||||||
|
+GetPGE() |
||||||
|
1.08: +DrawPolygonDecal() with tint overload, akin to PGE |
||||||
|
1.09: +SetScaleExtents() - Sets range that world scale can exist within |
||||||
|
+EnableScaleClamp() - Applies a range that scaling is clamped to |
||||||
|
These are both useful for having zoom clamped between a min and max |
||||||
|
without weird panning artefacts occuring |
||||||
|
*/ |
||||||
|
|
||||||
|
#pragma once |
||||||
|
#ifndef OLC_PGEX_TRANSFORMEDVIEW_H |
||||||
|
#define OLC_PGEX_TRANSFORMEDVIEW_H |
||||||
|
|
||||||
|
#include "olcPixelGameEngine.h" |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
namespace olc |
||||||
|
{ |
||||||
|
class TransformedView : public olc::PGEX |
||||||
|
{ |
||||||
|
public: |
||||||
|
TransformedView() = default; |
||||||
|
virtual void Initialise(const olc::vi2d& vViewArea, const olc::vf2d& vPixelScale = { 1.0f, 1.0f }); |
||||||
|
|
||||||
|
olc::PixelGameEngine* GetPGE(); |
||||||
|
|
||||||
|
public: |
||||||
|
void SetWorldOffset(const olc::vf2d& vOffset); |
||||||
|
void MoveWorldOffset(const olc::vf2d& vDeltaOffset); |
||||||
|
void SetWorldScale(const olc::vf2d& vScale); |
||||||
|
void SetViewArea(const olc::vi2d& vViewArea); |
||||||
|
olc::vf2d GetWorldTL() const; |
||||||
|
olc::vf2d GetWorldBR() const; |
||||||
|
olc::vf2d GetWorldVisibleArea() const; |
||||||
|
void ZoomAtScreenPos(const float fDeltaZoom, const olc::vi2d& vPos); |
||||||
|
void SetZoom(const float fZoom, const olc::vf2d& vPos); |
||||||
|
void StartPan(const olc::vi2d& vPos); |
||||||
|
void UpdatePan(const olc::vi2d& vPos); |
||||||
|
void EndPan(const olc::vi2d& vPos); |
||||||
|
const olc::vf2d& GetWorldOffset() const; |
||||||
|
const olc::vf2d& GetWorldScale() const; |
||||||
|
virtual olc::vf2d WorldToScreen(const olc::vf2d& vWorldPos) const; |
||||||
|
virtual olc::vf2d ScreenToWorld(const olc::vf2d& vScreenPos) const; |
||||||
|
virtual olc::vf2d ScaleToWorld(const olc::vf2d& vScreenSize) const; |
||||||
|
virtual olc::vf2d ScaleToScreen(const olc::vf2d& vWorldSize) const; |
||||||
|
virtual bool IsPointVisible(const olc::vf2d& vPos) const; |
||||||
|
virtual bool IsRectVisible(const olc::vf2d& vPos, const olc::vf2d& vSize) const; |
||||||
|
virtual void HandlePanAndZoom(const int nMouseButton = 2, const float fZoomRate = 0.1f, const bool bPan = true, const bool bZoom = true); |
||||||
|
void SetScaleExtents(const olc::vf2d& vScaleMin, const olc::vf2d& vScaleMax); |
||||||
|
void EnableScaleClamp(const bool bEnable); |
||||||
|
|
||||||
|
protected: |
||||||
|
olc::vf2d m_vWorldOffset = { 0.0f, 0.0f }; |
||||||
|
olc::vf2d m_vWorldScale = { 1.0f, 1.0f }; |
||||||
|
olc::vf2d m_vRecipPixel = { 1.0f, 1.0f }; |
||||||
|
olc::vf2d m_vPixelScale = { 1.0f, 1.0f }; |
||||||
|
bool m_bPanning = false; |
||||||
|
olc::vf2d m_vStartPan = { 0.0f, 0.0f }; |
||||||
|
olc::vi2d m_vViewArea; |
||||||
|
bool m_bZoomClamp = false; |
||||||
|
olc::vf2d m_vMaxScale = { 0.0f, 0.0f }; |
||||||
|
olc::vf2d m_vMinScale = { 0.0f, 0.0f }; |
||||||
|
|
||||||
|
public: // Hopefully, these should look familiar!
|
||||||
|
// Plots a single point
|
||||||
|
virtual bool Draw(float x, float y, olc::Pixel p = olc::WHITE); |
||||||
|
bool Draw(const olc::vf2d& pos, olc::Pixel p = olc::WHITE);
|
||||||
|
// Draws a line from (x1,y1) to (x2,y2)
|
||||||
|
void DrawLine(float x1, float y1, float x2, float y2, olc::Pixel p = olc::WHITE, uint32_t pattern = 0xFFFFFFFF); |
||||||
|
void DrawLine(const olc::vf2d& pos1, const olc::vf2d& pos2, olc::Pixel p = olc::WHITE, uint32_t pattern = 0xFFFFFFFF); |
||||||
|
// Draws a circle located at (x,y) with radius
|
||||||
|
void DrawCircle(float x, float y, float radius, olc::Pixel p = olc::WHITE, uint8_t mask = 0xFF); |
||||||
|
void DrawCircle(const olc::vf2d& pos, float radius, olc::Pixel p = olc::WHITE, uint8_t mask = 0xFF); |
||||||
|
// Fills a circle located at (x,y) with radius
|
||||||
|
void FillCircle(float x, float y, float radius, olc::Pixel p = olc::WHITE); |
||||||
|
void FillCircle(const olc::vf2d& pos, float radius, olc::Pixel p = olc::WHITE); |
||||||
|
// Draws a rectangle at (x,y) to (x+w,y+h)
|
||||||
|
void DrawRect(float x, float y, float w, float h, olc::Pixel p = olc::WHITE); |
||||||
|
void DrawRect(const olc::vf2d& pos, const olc::vf2d& size, olc::Pixel p = olc::WHITE); |
||||||
|
// Fills a rectangle at (x,y) to (x+w,y+h)
|
||||||
|
void FillRect(float x, float y, float w, float h, olc::Pixel p = olc::WHITE); |
||||||
|
void FillRect(const olc::vf2d& pos, const olc::vf2d& size, olc::Pixel p = olc::WHITE); |
||||||
|
// Draws a triangle between points (x1,y1), (x2,y2) and (x3,y3)
|
||||||
|
void DrawTriangle(float x1, float y1, float x2, float y2, float x3, float y3, olc::Pixel p = olc::WHITE); |
||||||
|
void DrawTriangle(const olc::vf2d& pos1, const olc::vf2d& pos2, const olc::vf2d& pos3, olc::Pixel p = olc::WHITE); |
||||||
|
// Flat fills a triangle between points (x1,y1), (x2,y2) and (x3,y3)
|
||||||
|
void FillTriangle(float x1, float y1, float x2, float y2, float x3, float y3, olc::Pixel p = olc::WHITE); |
||||||
|
void FillTriangle(const olc::vf2d& pos1, const olc::vf2d& pos2, const olc::vf2d& pos3, olc::Pixel p = olc::WHITE); |
||||||
|
// Draws an entire sprite at location (x,y)
|
||||||
|
void DrawSprite(float x, float y, olc::Sprite* sprite, float scalex = 1, float scaley = 1, uint8_t flip = olc::Sprite::NONE); |
||||||
|
void DrawSprite(const olc::vf2d& pos, olc::Sprite* sprite, const olc::vf2d& scale = { 1.0f, 1.0f }, uint8_t flip = olc::Sprite::NONE); |
||||||
|
// Draws an area of a sprite at location (x,y), where the
|
||||||
|
// selected area is (ox,oy) to (ox+w,oy+h)
|
||||||
|
void DrawPartialSprite(float x, float y, Sprite* sprite, int32_t ox, int32_t oy, int32_t w, int32_t h, float scalex = 1, float scaley = 1, uint8_t flip = olc::Sprite::NONE); |
||||||
|
void DrawPartialSprite(const olc::vf2d& pos, Sprite* sprite, const olc::vi2d& sourcepos, const olc::vi2d& size, const olc::vf2d& scale = { 1.0f, 1.0f }, uint8_t flip = olc::Sprite::NONE); |
||||||
|
void DrawString(float x, float y, const std::string& sText, Pixel col, const olc::vf2d& scale); |
||||||
|
void DrawString(const olc::vf2d& pos, const std::string& sText, const Pixel col, const olc::vf2d& scale); |
||||||
|
|
||||||
|
|
||||||
|
// Draws a whole decal, with optional scale and tinting
|
||||||
|
void DrawDecal(const olc::vf2d& pos, olc::Decal* decal, const olc::vf2d& scale = { 1.0f,1.0f }, const olc::Pixel& tint = olc::WHITE); |
||||||
|
// Draws a region of a decal, with optional scale and tinting
|
||||||
|
void DrawPartialDecal(const olc::vf2d& pos, olc::Decal* decal, const olc::vf2d& source_pos, const olc::vf2d& source_size, const olc::vf2d& scale = { 1.0f,1.0f }, const olc::Pixel& tint = olc::WHITE); |
||||||
|
void DrawPartialDecal(const olc::vf2d& pos, const olc::vf2d& size, olc::Decal* decal, const olc::vf2d& source_pos, const olc::vf2d& source_size, const olc::Pixel& tint = olc::WHITE); |
||||||
|
// Draws fully user controlled 4 vertices, pos(pixels), uv(pixels), colours
|
||||||
|
void DrawExplicitDecal(olc::Decal* decal, const olc::vf2d* pos, const olc::vf2d* uv, const olc::Pixel* col, uint32_t elements = 4); |
||||||
|
//// Draws a decal with 4 arbitrary points, warping the texture to look "correct"
|
||||||
|
void DrawWarpedDecal(olc::Decal* decal, const olc::vf2d(&pos)[4], const olc::Pixel& tint = olc::WHITE); |
||||||
|
void DrawWarpedDecal(olc::Decal* decal, const olc::vf2d* pos, const olc::Pixel& tint = olc::WHITE); |
||||||
|
void DrawWarpedDecal(olc::Decal* decal, const std::array<olc::vf2d, 4>& pos, const olc::Pixel& tint = olc::WHITE); |
||||||
|
//// As above, but you can specify a region of a decal source sprite
|
||||||
|
void DrawPartialWarpedDecal(olc::Decal* decal, const olc::vf2d(&pos)[4], const olc::vf2d& source_pos, const olc::vf2d& source_size, const olc::Pixel& tint = olc::WHITE); |
||||||
|
void DrawPartialWarpedDecal(olc::Decal* decal, const olc::vf2d* pos, const olc::vf2d& source_pos, const olc::vf2d& source_size, const olc::Pixel& tint = olc::WHITE); |
||||||
|
void DrawPartialWarpedDecal(olc::Decal* decal, const std::array<olc::vf2d, 4>& pos, const olc::vf2d& source_pos, const olc::vf2d& source_size, const olc::Pixel& tint = olc::WHITE); |
||||||
|
//// Draws a decal rotated to specified angle, wit point of rotation offset
|
||||||
|
void DrawRotatedDecal(const olc::vf2d& pos, olc::Decal* decal, const float fAngle, const olc::vf2d& center = { 0.0f, 0.0f }, const olc::vf2d& scale = { 1.0f,1.0f }, const olc::Pixel& tint = olc::WHITE); |
||||||
|
void DrawPartialRotatedDecal(const olc::vf2d& pos, olc::Decal* decal, const float fAngle, const olc::vf2d& center, const olc::vf2d& source_pos, const olc::vf2d& source_size, const olc::vf2d& scale = { 1.0f, 1.0f }, const olc::Pixel& tint = olc::WHITE); |
||||||
|
// Draws a multiline string as a decal, with tiniting and scaling
|
||||||
|
void DrawStringDecal(const olc::vf2d& pos, const std::string& sText, const olc::Pixel col = olc::WHITE, const olc::vf2d& scale = { 1.0f, 1.0f }); |
||||||
|
void DrawStringPropDecal(const olc::vf2d& pos, const std::string& sText, const olc::Pixel col = olc::WHITE, const olc::vf2d& scale = { 1.0f, 1.0f }); |
||||||
|
// Draws a single shaded filled rectangle as a decal
|
||||||
|
void FillRectDecal(const olc::vf2d& pos, const olc::vf2d& size, const olc::Pixel col = olc::WHITE); |
||||||
|
void DrawRectDecal(const olc::vf2d& pos, const olc::vf2d& size, const olc::Pixel col = olc::WHITE); |
||||||
|
|
||||||
|
// Draws a corner shaded rectangle as a decal
|
||||||
|
void GradientFillRectDecal(const olc::vf2d& pos, const olc::vf2d& size, const olc::Pixel colTL, const olc::Pixel colBL, const olc::Pixel colBR, const olc::Pixel colTR); |
||||||
|
// Draws an arbitrary convex textured polygon using GPU
|
||||||
|
void DrawPolygonDecal(olc::Decal* decal, const std::vector<olc::vf2d>& pos, const std::vector<olc::vf2d>& uv, const olc::Pixel tint = olc::WHITE); |
||||||
|
void DrawLineDecal(const olc::vf2d& pos1, const olc::vf2d& pos2, Pixel p = olc::WHITE); |
||||||
|
void DrawPolygonDecal(olc::Decal* decal, const std::vector<olc::vf2d>&pos, const std::vector<olc::vf2d>&uv, const std::vector<olc::Pixel> &tint); |
||||||
|
void DrawPolygonDecal(olc::Decal* decal, const std::vector<olc::vf2d>& pos, const std::vector<olc::vf2d>& uv, const std::vector<olc::Pixel>& colours, const olc::Pixel tint); |
||||||
|
|
||||||
|
|
||||||
|
#if defined(OLC_PGEX_SHADER) |
||||||
|
// Shader Specific
|
||||||
|
void DrawDecal(olc::Shade& shader, const olc::vf2d & pos, olc::Decal * decal, const olc::vf2d & scale = { 1.0f,1.0f }, const olc::Pixel & tint = olc::WHITE); |
||||||
|
void DrawPartialDecal(olc::Shade& shader, const olc::vf2d& pos, olc::Decal* decal, const olc::vf2d& source_pos, const olc::vf2d& source_size, const olc::vf2d& scale = { 1.0f,1.0f }, const olc::Pixel& tint = olc::WHITE); |
||||||
|
void DrawPartialDecal(olc::Shade& shader, const olc::vf2d& pos, const olc::vf2d& size, olc::Decal* decal, const olc::vf2d& source_pos, const olc::vf2d& source_size, const olc::Pixel& tint = olc::WHITE); |
||||||
|
#endif |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
class TileTransformedView : public TransformedView |
||||||
|
{ |
||||||
|
public: |
||||||
|
TileTransformedView() = default;
|
||||||
|
TileTransformedView(const olc::vi2d& vViewArea, const olc::vi2d& vTileSize); |
||||||
|
|
||||||
|
public: |
||||||
|
olc::vi2d GetTopLeftTile() const; |
||||||
|
olc::vi2d GetBottomRightTile() const; |
||||||
|
olc::vi2d GetVisibleTiles() const; |
||||||
|
olc::vi2d GetTileUnderScreenPos(const olc::vi2d& vPos) const; |
||||||
|
const olc::vi2d GetTileOffset() const; |
||||||
|
|
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
#ifdef OLC_PGEX_TRANSFORMEDVIEW |
||||||
|
#undef OLC_PGEX_TRANSFORMEDVIEW |
||||||
|
|
||||||
|
namespace olc |
||||||
|
{ |
||||||
|
olc::PixelGameEngine* TransformedView::GetPGE() |
||||||
|
{ |
||||||
|
return pge; |
||||||
|
} |
||||||
|
|
||||||
|
void TransformedView::Initialise(const olc::vi2d& vViewArea, const olc::vf2d& vPixelScale) |
||||||
|
{ |
||||||
|
SetViewArea(vViewArea); |
||||||
|
SetWorldScale(vPixelScale); |
||||||
|
m_vPixelScale = vPixelScale; |
||||||
|
m_vRecipPixel = 1.0f / m_vPixelScale; |
||||||
|
} |
||||||
|
|
||||||
|
void TransformedView::SetWorldOffset(const olc::vf2d& vOffset) |
||||||
|
{ |
||||||
|
m_vWorldOffset = vOffset; |
||||||
|
} |
||||||
|
|
||||||
|
void TransformedView::MoveWorldOffset(const olc::vf2d& vDeltaOffset) |
||||||
|
{ |
||||||
|
m_vWorldOffset += vDeltaOffset; |
||||||
|
} |
||||||
|
|
||||||
|
void TransformedView::SetWorldScale(const olc::vf2d& vScale) |
||||||
|
{ |
||||||
|
m_vWorldScale = vScale; |
||||||
|
if (m_bZoomClamp) m_vWorldScale = m_vWorldScale.clamp(m_vMinScale, m_vMaxScale); |
||||||
|
} |
||||||
|
|
||||||
|
void TransformedView::SetViewArea(const olc::vi2d& vViewArea) |
||||||
|
{ |
||||||
|
m_vViewArea = vViewArea; |
||||||
|
} |
||||||
|
|
||||||
|
olc::vf2d TransformedView::GetWorldTL() const |
||||||
|
{ |
||||||
|
return TransformedView::ScreenToWorld({ 0,0 }); |
||||||
|
} |
||||||
|
|
||||||
|
olc::vf2d TransformedView::GetWorldBR() const |
||||||
|
{ |
||||||
|
return TransformedView::ScreenToWorld(m_vViewArea); |
||||||
|
} |
||||||
|
|
||||||
|
olc::vf2d TransformedView::GetWorldVisibleArea() const |
||||||
|
{ |
||||||
|
return GetWorldBR() - GetWorldTL(); |
||||||
|
} |
||||||
|
|
||||||
|
void TransformedView::SetScaleExtents(const olc::vf2d& vScaleMin, const olc::vf2d& vScaleMax) |
||||||
|
{ |
||||||
|
m_vMaxScale = vScaleMax; |
||||||
|
m_vMinScale = vScaleMin; |
||||||
|
} |
||||||
|
|
||||||
|
void TransformedView::EnableScaleClamp(const bool bEnable) |
||||||
|
{ |
||||||
|
m_bZoomClamp = bEnable; |
||||||
|
} |
||||||
|
|
||||||
|
void TransformedView::ZoomAtScreenPos(const float fDeltaZoom, const olc::vi2d& vPos) |
||||||
|
{ |
||||||
|
olc::vf2d vOffsetBeforeZoom = ScreenToWorld(vPos); |
||||||
|
m_vWorldScale *= fDeltaZoom; |
||||||
|
if (m_bZoomClamp) m_vWorldScale = m_vWorldScale.clamp(m_vMinScale, m_vMaxScale); |
||||||
|
olc::vf2d vOffsetAfterZoom = ScreenToWorld(vPos); |
||||||
|
m_vWorldOffset += vOffsetBeforeZoom - vOffsetAfterZoom; |
||||||
|
} |
||||||
|
|
||||||
|
void TransformedView::SetZoom(const float fZoom, const olc::vf2d& vPos) |
||||||
|
{ |
||||||
|
olc::vf2d vOffsetBeforeZoom = ScreenToWorld(vPos); |
||||||
|
m_vWorldScale = { fZoom, fZoom }; |
||||||
|
if (m_bZoomClamp) m_vWorldScale = m_vWorldScale.clamp(m_vMinScale, m_vMaxScale); |
||||||
|
olc::vf2d vOffsetAfterZoom = ScreenToWorld(vPos); |
||||||
|
m_vWorldOffset += vOffsetBeforeZoom - vOffsetAfterZoom; |
||||||
|
} |
||||||
|
|
||||||
|
void TransformedView::StartPan(const olc::vi2d& vPos) |
||||||
|
{ |
||||||
|
m_bPanning = true; |
||||||
|
m_vStartPan = olc::vf2d(vPos); |
||||||
|
} |
||||||
|
|
||||||
|
void TransformedView::UpdatePan(const olc::vi2d& vPos) |
||||||
|
{ |
||||||
|
if (m_bPanning) |
||||||
|
{ |
||||||
|
m_vWorldOffset -= (olc::vf2d(vPos) - m_vStartPan) / m_vWorldScale; |
||||||
|
m_vStartPan = olc::vf2d(vPos); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void TransformedView::EndPan(const olc::vi2d& vPos) |
||||||
|
{ |
||||||
|
UpdatePan(vPos); |
||||||
|
m_bPanning = false; |
||||||
|
} |
||||||
|
|
||||||
|
const olc::vf2d& TransformedView::GetWorldOffset() const |
||||||
|
{ |
||||||
|
return m_vWorldOffset; |
||||||
|
} |
||||||
|
|
||||||
|
const olc::vf2d& TransformedView::GetWorldScale() const |
||||||
|
{ |
||||||
|
return m_vWorldScale; |
||||||
|
} |
||||||
|
|
||||||
|
olc::vf2d TransformedView::WorldToScreen(const olc::vf2d& vWorldPos) const |
||||||
|
{ |
||||||
|
olc::vf2d vFloat = ((vWorldPos - m_vWorldOffset) * m_vWorldScale); |
||||||
|
//vFloat = { std::floor(vFloat.x + 0.5f), std::floor(vFloat.y + 0.5f) };
|
||||||
|
return vFloat; |
||||||
|
} |
||||||
|
|
||||||
|
olc::vf2d TransformedView::ScreenToWorld(const olc::vf2d& vScreenPos) const |
||||||
|
{ |
||||||
|
return (olc::vf2d(vScreenPos) / m_vWorldScale) + m_vWorldOffset; |
||||||
|
} |
||||||
|
|
||||||
|
olc::vf2d TransformedView::ScaleToWorld(const olc::vf2d& vScreenSize) const |
||||||
|
{ |
||||||
|
return (olc::vf2d(vScreenSize) / m_vWorldScale); |
||||||
|
} |
||||||
|
|
||||||
|
olc::vf2d TransformedView::ScaleToScreen(const olc::vf2d& vWorldSize) const |
||||||
|
{ |
||||||
|
//olc::vf2d vFloat = (vWorldSize * m_vWorldScale) + olc::vf2d(0.5f, 0.5f);
|
||||||
|
//return vFloat.floor();
|
||||||
|
return (vWorldSize * m_vWorldScale); |
||||||
|
} |
||||||
|
|
||||||
|
bool TransformedView::IsPointVisible(const olc::vf2d & vPos) const |
||||||
|
{ |
||||||
|
olc::vi2d vScreen = WorldToScreen(vPos); |
||||||
|
return vScreen.x >= 0 && vScreen.x < m_vViewArea.x&& vScreen.y >= 0 && vScreen.y < m_vViewArea.y; |
||||||
|
} |
||||||
|
|
||||||
|
bool TransformedView::IsRectVisible(const olc::vf2d& vPos, const olc::vf2d& vSize) const |
||||||
|
{ |
||||||
|
olc::vi2d vScreenPos = WorldToScreen(vPos); |
||||||
|
olc::vi2d vScreenSize = vSize * m_vWorldScale; |
||||||
|
return (vScreenPos.x < 0 + m_vViewArea.x && vScreenPos.x + vScreenSize.x > 0 && vScreenPos.y < m_vViewArea.y&& vScreenPos.y + vScreenSize.y > 0); |
||||||
|
} |
||||||
|
|
||||||
|
void TransformedView::HandlePanAndZoom(const int nMouseButton, const float fZoomRate, const bool bPan, const bool bZoom) |
||||||
|
{ |
||||||
|
const auto& vMousePos = pge->GetMousePos(); |
||||||
|
if (bPan) |
||||||
|
{ |
||||||
|
if (pge->GetMouse(nMouseButton).bPressed) StartPan(vMousePos); |
||||||
|
if (pge->GetMouse(nMouseButton).bHeld) UpdatePan(vMousePos); |
||||||
|
if (pge->GetMouse(nMouseButton).bReleased) EndPan(vMousePos); |
||||||
|
} |
||||||
|
|
||||||
|
if (bZoom) |
||||||
|
{ |
||||||
|
if (pge->GetMouseWheel() > 0) ZoomAtScreenPos(1.0f + fZoomRate, vMousePos); |
||||||
|
if (pge->GetMouseWheel() < 0) ZoomAtScreenPos(1.0f - fZoomRate, vMousePos); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
bool TransformedView::Draw(float x, float y, olc::Pixel p) |
||||||
|
{ |
||||||
|
return Draw({ x, y }, p); |
||||||
|
} |
||||||
|
|
||||||
|
bool TransformedView::Draw(const olc::vf2d & pos, olc::Pixel p) |
||||||
|
{ |
||||||
|
return pge->Draw(WorldToScreen(pos), p); |
||||||
|
} |
||||||
|
|
||||||
|
void TransformedView::DrawLine(float x1, float y1, float x2, float y2, olc::Pixel p, uint32_t pattern) |
||||||
|
{ |
||||||
|
DrawLine({ x1, y1 }, { x2, y2 }, p, pattern); |
||||||
|
} |
||||||
|
|
||||||
|
void TransformedView::DrawLine(const olc::vf2d & pos1, const olc::vf2d & pos2, olc::Pixel p, uint32_t pattern) |
||||||
|
{ |
||||||
|
pge->DrawLine(WorldToScreen(pos1), WorldToScreen(pos2), p, pattern); |
||||||
|
} |
||||||
|
|
||||||
|
void TransformedView::DrawCircle(float x, float y, float radius, olc::Pixel p, uint8_t mask) |
||||||
|
{ |
||||||
|
DrawCircle({ x,y }, radius, p, mask); |
||||||
|
} |
||||||
|
|
||||||
|
void TransformedView::DrawCircle(const olc::vf2d & pos, float radius, olc::Pixel p, uint8_t mask) |
||||||
|
{ |
||||||
|
pge->DrawCircle(WorldToScreen(pos), int32_t(radius * m_vWorldScale.x), p, mask); |
||||||
|
} |
||||||
|
|
||||||
|
void TransformedView::FillCircle(float x, float y, float radius, olc::Pixel p) |
||||||
|
{ |
||||||
|
FillCircle({ x,y }, radius, p); |
||||||
|
} |
||||||
|
|
||||||
|
void TransformedView::FillCircle(const olc::vf2d & pos, float radius, olc::Pixel p) |
||||||
|
{ |
||||||
|
pge->FillCircle(WorldToScreen(pos), int32_t(radius * m_vWorldScale.x), p); |
||||||
|
} |
||||||
|
|
||||||
|
void TransformedView::DrawRect(float x, float y, float w, float h, olc::Pixel p) |
||||||
|
{ |
||||||
|
DrawRect({ x, y }, { w, h }, p); |
||||||
|
} |
||||||
|
|
||||||
|
void TransformedView::DrawRect(const olc::vf2d & pos, const olc::vf2d & size, olc::Pixel p) |
||||||
|
{ |
||||||
|
pge->DrawRect(WorldToScreen(pos), ((size * m_vWorldScale) + olc::vf2d(0.5f, 0.5f)).floor(), p); |
||||||
|
} |
||||||
|
|
||||||
|
void TransformedView::FillRect(float x, float y, float w, float h, olc::Pixel p) |
||||||
|
{ |
||||||
|
FillRect({ x, y }, { w, h }, p); |
||||||
|
} |
||||||
|
|
||||||
|
void TransformedView::FillRect(const olc::vf2d & pos, const olc::vf2d & size, olc::Pixel p) |
||||||
|
{ |
||||||
|
pge->FillRect(WorldToScreen(pos), size * m_vWorldScale, p); |
||||||
|
} |
||||||
|
|
||||||
|
void TransformedView::DrawTriangle(float x1, float y1, float x2, float y2, float x3, float y3, olc::Pixel p) |
||||||
|
{ |
||||||
|
DrawTriangle({ x1, y1 }, { x2, y2 }, { x3, y3 }, p); |
||||||
|
} |
||||||
|
|
||||||
|
void TransformedView::DrawTriangle(const olc::vf2d & pos1, const olc::vf2d & pos2, const olc::vf2d & pos3, olc::Pixel p) |
||||||
|
{ |
||||||
|
pge->DrawTriangle(WorldToScreen(pos1), WorldToScreen(pos2), WorldToScreen(pos3), p); |
||||||
|
} |
||||||
|
|
||||||
|
void TransformedView::FillTriangle(float x1, float y1, float x2, float y2, float x3, float y3, olc::Pixel p) |
||||||
|
{ |
||||||
|
FillTriangle({ x1, y1 }, { x2, y2 }, { x3, y3 }, p); |
||||||
|
} |
||||||
|
|
||||||
|
void TransformedView::FillTriangle(const olc::vf2d & pos1, const olc::vf2d & pos2, const olc::vf2d & pos3, olc::Pixel p) |
||||||
|
{ |
||||||
|
pge->FillTriangle(WorldToScreen(pos1), WorldToScreen(pos2), WorldToScreen(pos3), p); |
||||||
|
} |
||||||
|
|
||||||
|
void TransformedView::DrawSprite(float x, float y, olc::Sprite* sprite, float scalex, float scaley, uint8_t flip) |
||||||
|
{ |
||||||
|
DrawSprite({ x, y }, sprite, { scalex, scaley }, flip); |
||||||
|
} |
||||||
|
|
||||||
|
void TransformedView::DrawSprite(const olc::vf2d & pos, olc::Sprite * sprite, const olc::vf2d & scale, uint8_t flip) |
||||||
|
{ |
||||||
|
olc::vf2d vSpriteSize = olc::vf2d(float(sprite->width), float(sprite->height)); |
||||||
|
if (IsRectVisible(pos, vSpriteSize * scale)) |
||||||
|
{ |
||||||
|
olc::vf2d vSpriteScaledSize = vSpriteSize * m_vRecipPixel * m_vWorldScale * scale; |
||||||
|
olc::vi2d vPixel; |
||||||
|
olc::vi2d vSpritePixelStart = WorldToScreen(pos); |
||||||
|
olc::vi2d vSpritePixelEnd = WorldToScreen((vSpriteSize * scale) + pos); |
||||||
|
|
||||||
|
olc::vi2d vScreenPixelStart = (vSpritePixelStart).max({0,0}); |
||||||
|
olc::vi2d vScreenPixelEnd = (vSpritePixelEnd).min({ pge->ScreenWidth(),pge->ScreenHeight() }); |
||||||
|
|
||||||
|
olc::vf2d vPixelStep = 1.0f / vSpriteScaledSize; |
||||||
|
|
||||||
|
for (vPixel.y = vScreenPixelStart.y; vPixel.y < vScreenPixelEnd.y; vPixel.y++) |
||||||
|
{ |
||||||
|
for (vPixel.x = vScreenPixelStart.x; vPixel.x < vScreenPixelEnd.x; vPixel.x++) |
||||||
|
{ |
||||||
|
olc::vf2d vSample = olc::vf2d(vPixel - vSpritePixelStart) * vPixelStep; |
||||||
|
pge->Draw(vPixel, sprite->Sample(vSample.x, vSample.y)); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
void TransformedView::DrawPartialSprite(float x, float y, Sprite* sprite, int32_t ox, int32_t oy, int32_t w, int32_t h, float scalex, float scaley, uint8_t flip) |
||||||
|
{ |
||||||
|
DrawPartialSprite({ x,y }, sprite, { ox,oy }, { w, h }, { scalex, scaley }, flip); |
||||||
|
} |
||||||
|
|
||||||
|
void TransformedView::DrawPartialSprite(const olc::vf2d& pos, Sprite* sprite, const olc::vi2d& sourcepos, const olc::vi2d& size, const olc::vf2d& scale, uint8_t flip) |
||||||
|
{ |
||||||
|
olc::vf2d vSpriteSize = size; |
||||||
|
if (IsRectVisible(pos, size * scale)) |
||||||
|
{ |
||||||
|
olc::vf2d vSpriteScaledSize = olc::vf2d(size) * m_vRecipPixel * m_vWorldScale * scale; |
||||||
|
olc::vf2d vSpritePixelStep = 1.0f / olc::vf2d(float(sprite->width), float(sprite->height)); |
||||||
|
olc::vi2d vPixel, vStart = WorldToScreen(pos), vEnd = vSpriteScaledSize + vStart; |
||||||
|
olc::vf2d vScreenPixelStep = 1.0f / vSpriteScaledSize; |
||||||
|
|
||||||
|
for (vPixel.y = vStart.y; vPixel.y < vEnd.y; vPixel.y++) |
||||||
|
{ |
||||||
|
for (vPixel.x = vStart.x; vPixel.x < vEnd.x; vPixel.x++) |
||||||
|
{ |
||||||
|
olc::vf2d vSample = ((olc::vf2d(vPixel - vStart) * vScreenPixelStep) * size * vSpritePixelStep) + olc::vf2d(sourcepos) * vSpritePixelStep; |
||||||
|
pge->Draw(vPixel, sprite->Sample(vSample.x, vSample.y)); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void TransformedView::DrawString(float x, float y, const std::string& sText, Pixel col, const olc::vf2d& scale) |
||||||
|
{ |
||||||
|
DrawString({ x, y }, sText, col, scale); |
||||||
|
} |
||||||
|
|
||||||
|
void TransformedView::DrawString(const olc::vf2d& pos, const std::string& sText, const Pixel col, const olc::vf2d& scale) |
||||||
|
{ |
||||||
|
olc::vf2d vOffset = { 0.0f, 0.0f }; |
||||||
|
Pixel::Mode m = pge->GetPixelMode(); |
||||||
|
|
||||||
|
auto StringPlot = [&col](const int x, const int y, const olc::Pixel& pSource, const olc::Pixel& pDest) |
||||||
|
{
|
||||||
|
return pSource.r > 1 ? col : pDest; |
||||||
|
}; |
||||||
|
|
||||||
|
pge->SetPixelMode(StringPlot); |
||||||
|
|
||||||
|
for (auto c : sText) |
||||||
|
{ |
||||||
|
if (c == '\n') |
||||||
|
{ |
||||||
|
vOffset.x = 0.0f; vOffset.y += 8.0f * m_vRecipPixel.y * scale.y; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
int32_t ox = ((c - 32) % 16) * 8; |
||||||
|
int32_t oy = ((c - 32) / 16) * 8; |
||||||
|
DrawPartialSprite(pos + vOffset, pge->GetFontSprite(), { ox, oy }, { 8, 8 }, scale); |
||||||
|
vOffset.x += 8.0f * m_vRecipPixel.x * scale.x; |
||||||
|
} |
||||||
|
} |
||||||
|
pge->SetPixelMode(m); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
void TransformedView::DrawDecal(const olc::vf2d & pos, olc::Decal * decal, const olc::vf2d & scale, const olc::Pixel & tint) |
||||||
|
{ |
||||||
|
pge->DrawDecal(WorldToScreen(pos), decal, scale * m_vWorldScale * m_vRecipPixel, tint); |
||||||
|
} |
||||||
|
|
||||||
|
void TransformedView::DrawPartialDecal(const olc::vf2d & pos, olc::Decal * decal, const olc::vf2d & source_pos, const olc::vf2d & source_size, const olc::vf2d & scale, const olc::Pixel & tint) |
||||||
|
{ |
||||||
|
pge->DrawPartialDecal(WorldToScreen(pos), decal, source_pos, source_size, scale * m_vWorldScale * m_vRecipPixel, tint); |
||||||
|
} |
||||||
|
|
||||||
|
void TransformedView::DrawPartialDecal(const olc::vf2d & pos, const olc::vf2d & size, olc::Decal * decal, const olc::vf2d & source_pos, const olc::vf2d & source_size, const olc::Pixel & tint) |
||||||
|
{ |
||||||
|
pge->DrawPartialDecal(WorldToScreen(pos), size * m_vWorldScale * m_vRecipPixel, decal, source_pos, source_size, tint); |
||||||
|
} |
||||||
|
|
||||||
|
void TransformedView::DrawExplicitDecal(olc::Decal* decal, const olc::vf2d* pos, const olc::vf2d* uv, const olc::Pixel* col, uint32_t elements) |
||||||
|
{ |
||||||
|
std::vector<olc::vf2d> vTransformed(elements); |
||||||
|
for (uint32_t n = 0; n < elements; n++) |
||||||
|
vTransformed[n] = WorldToScreen(pos[n]);
|
||||||
|
pge->DrawExplicitDecal(decal, vTransformed.data(), uv, col, elements); |
||||||
|
} |
||||||
|
|
||||||
|
void TransformedView::DrawWarpedDecal(olc::Decal* decal, const olc::vf2d* pos, const olc::Pixel& tint) |
||||||
|
{ |
||||||
|
std::array<olc::vf2d, 4> vTransformed =
|
||||||
|
{ { |
||||||
|
WorldToScreen(pos[0]), WorldToScreen(pos[1]), |
||||||
|
WorldToScreen(pos[2]), WorldToScreen(pos[3]), |
||||||
|
} }; |
||||||
|
|
||||||
|
pge->DrawWarpedDecal(decal, vTransformed, tint); |
||||||
|
} |
||||||
|
|
||||||
|
void TransformedView::DrawWarpedDecal(olc::Decal* decal, const olc::vf2d(&pos)[4], const olc::Pixel& tint) |
||||||
|
{ |
||||||
|
DrawWarpedDecal(decal, &pos[0], tint); |
||||||
|
} |
||||||
|
|
||||||
|
void TransformedView::DrawWarpedDecal(olc::Decal* decal, const std::array<olc::vf2d, 4>& pos, const olc::Pixel& tint) |
||||||
|
{ |
||||||
|
DrawWarpedDecal(decal, pos.data(), tint); |
||||||
|
} |
||||||
|
|
||||||
|
void TransformedView::DrawPartialWarpedDecal(olc::Decal* decal, const olc::vf2d(&pos)[4], const olc::vf2d& source_pos, const olc::vf2d& source_size, const olc::Pixel& tint) |
||||||
|
{ |
||||||
|
DrawPartialWarpedDecal(decal, &pos[0], source_pos, source_size, tint); |
||||||
|
} |
||||||
|
|
||||||
|
void TransformedView::DrawPartialWarpedDecal(olc::Decal* decal, const olc::vf2d* pos, const olc::vf2d& source_pos, const olc::vf2d& source_size, const olc::Pixel& tint) |
||||||
|
{ |
||||||
|
std::array<olc::vf2d, 4> vTransformed = |
||||||
|
{ { |
||||||
|
WorldToScreen(pos[0]), WorldToScreen(pos[1]), |
||||||
|
WorldToScreen(pos[2]), WorldToScreen(pos[3]), |
||||||
|
} }; |
||||||
|
|
||||||
|
pge->DrawPartialWarpedDecal(decal, vTransformed, source_pos, source_size, tint); |
||||||
|
} |
||||||
|
|
||||||
|
void TransformedView::DrawPartialWarpedDecal(olc::Decal* decal, const std::array<olc::vf2d, 4>& pos, const olc::vf2d& source_pos, const olc::vf2d& source_size, const olc::Pixel& tint) |
||||||
|
{ |
||||||
|
DrawPartialWarpedDecal(decal, pos.data(), source_pos, source_size, tint); |
||||||
|
} |
||||||
|
|
||||||
|
void TransformedView::DrawRotatedDecal(const olc::vf2d & pos, olc::Decal * decal, const float fAngle, const olc::vf2d & center, const olc::vf2d & scale, const olc::Pixel & tint) |
||||||
|
{ |
||||||
|
pge->DrawRotatedDecal(WorldToScreen(pos), decal, fAngle, center, scale * m_vWorldScale * m_vRecipPixel, tint); |
||||||
|
} |
||||||
|
|
||||||
|
void TransformedView::DrawPartialRotatedDecal(const olc::vf2d & pos, olc::Decal * decal, const float fAngle, const olc::vf2d & center, const olc::vf2d & source_pos, const olc::vf2d & source_size, const olc::vf2d & scale, const olc::Pixel & tint) |
||||||
|
{ |
||||||
|
pge->DrawPartialRotatedDecal(WorldToScreen(pos), decal, fAngle, center, source_pos, source_size, scale * m_vWorldScale * m_vRecipPixel, tint); |
||||||
|
} |
||||||
|
|
||||||
|
void TransformedView::DrawStringDecal(const olc::vf2d & pos, const std::string & sText, const olc::Pixel col, const olc::vf2d & scale) |
||||||
|
{ |
||||||
|
pge->DrawStringDecal(WorldToScreen(pos), sText, col, scale * m_vWorldScale * m_vRecipPixel); |
||||||
|
} |
||||||
|
|
||||||
|
void TransformedView::DrawStringPropDecal(const olc::vf2d & pos, const std::string & sText, const olc::Pixel col, const olc::vf2d & scale ) |
||||||
|
{ |
||||||
|
pge->DrawStringPropDecal(WorldToScreen(pos), sText, col, scale * m_vWorldScale * m_vRecipPixel); |
||||||
|
} |
||||||
|
|
||||||
|
void TransformedView::FillRectDecal(const olc::vf2d & pos, const olc::vf2d & size, const olc::Pixel col) |
||||||
|
{ |
||||||
|
pge->FillRectDecal(WorldToScreen(pos), (size * m_vWorldScale).ceil(), col); |
||||||
|
} |
||||||
|
|
||||||
|
void TransformedView::DrawRectDecal(const olc::vf2d& pos, const olc::vf2d& size, const olc::Pixel col) |
||||||
|
{ |
||||||
|
pge->DrawRectDecal(WorldToScreen(pos), (size * m_vWorldScale).ceil(), col); |
||||||
|
} |
||||||
|
|
||||||
|
void TransformedView::DrawLineDecal(const olc::vf2d& pos1, const olc::vf2d& pos2, Pixel p) |
||||||
|
{ |
||||||
|
pge->DrawLineDecal(WorldToScreen(pos1), WorldToScreen(pos2), p); |
||||||
|
} |
||||||
|
|
||||||
|
void TransformedView::GradientFillRectDecal(const olc::vf2d & pos, const olc::vf2d & size, const olc::Pixel colTL, const olc::Pixel colBL, const olc::Pixel colBR, const olc::Pixel colTR) |
||||||
|
{ |
||||||
|
pge->GradientFillRectDecal(WorldToScreen(pos), size * m_vWorldScale, colTL, colBL, colBR, colTR); |
||||||
|
} |
||||||
|
|
||||||
|
void TransformedView::DrawPolygonDecal(olc::Decal* decal, const std::vector<olc::vf2d>& pos, const std::vector<olc::vf2d>& uv, const olc::Pixel tint) |
||||||
|
{ |
||||||
|
std::vector<olc::vf2d> vTransformed(pos.size()); |
||||||
|
for (uint32_t n = 0; n < pos.size(); n++) |
||||||
|
vTransformed[n] = WorldToScreen(pos[n]); |
||||||
|
pge->DrawPolygonDecal(decal, vTransformed, uv, tint); |
||||||
|
} |
||||||
|
|
||||||
|
void TransformedView::DrawPolygonDecal(olc::Decal* decal, const std::vector<olc::vf2d>& pos, const std::vector<olc::vf2d>& uv, const std::vector<olc::Pixel> &tint) |
||||||
|
{ |
||||||
|
std::vector<olc::vf2d> vTransformed(pos.size()); |
||||||
|
for (uint32_t n = 0; n < pos.size(); n++) |
||||||
|
vTransformed[n] = WorldToScreen(pos[n]); |
||||||
|
pge->DrawPolygonDecal(decal, vTransformed, uv, tint); |
||||||
|
} |
||||||
|
|
||||||
|
void TransformedView::DrawPolygonDecal(olc::Decal* decal, const std::vector<olc::vf2d>& pos, const std::vector<olc::vf2d>& uv, const std::vector<olc::Pixel>& colours, const olc::Pixel tint) |
||||||
|
{ |
||||||
|
std::vector<olc::vf2d> vTransformed(pos.size()); |
||||||
|
for (uint32_t n = 0; n < pos.size(); n++) |
||||||
|
vTransformed[n] = WorldToScreen(pos[n]); |
||||||
|
pge->DrawPolygonDecal(decal, vTransformed, uv, colours, tint); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#if defined (OLC_PGEX_SHADER) |
||||||
|
|
||||||
|
void TransformedView::DrawDecal(olc::Shade &shade, const olc::vf2d& pos, olc::Decal* decal, const olc::vf2d& scale, const olc::Pixel& tint) |
||||||
|
{ |
||||||
|
shade.DrawDecal(WorldToScreen(pos), decal, scale * m_vWorldScale * m_vRecipPixel, tint); |
||||||
|
} |
||||||
|
|
||||||
|
void TransformedView::DrawPartialDecal(olc::Shade& shade, const olc::vf2d& pos, olc::Decal* decal, const olc::vf2d& source_pos, const olc::vf2d& source_size, const olc::vf2d& scale, const olc::Pixel& tint) |
||||||
|
{ |
||||||
|
shade.DrawPartialDecal(WorldToScreen(pos), decal, source_pos, source_size, scale * m_vWorldScale * m_vRecipPixel, tint); |
||||||
|
} |
||||||
|
|
||||||
|
void TransformedView::DrawPartialDecal(olc::Shade& shade, const olc::vf2d& pos, const olc::vf2d& size, olc::Decal* decal, const olc::vf2d& source_pos, const olc::vf2d& source_size, const olc::Pixel& tint) |
||||||
|
{ |
||||||
|
shade.DrawPartialDecal(WorldToScreen(pos), size * m_vWorldScale * m_vRecipPixel, decal, source_pos, source_size, tint); |
||||||
|
} |
||||||
|
|
||||||
|
#endif |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////
|
||||||
|
/////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
TileTransformedView::TileTransformedView(const olc::vi2d& vViewArea, const olc::vi2d& vTileSize)
|
||||||
|
{
|
||||||
|
Initialise(vViewArea, vTileSize); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
olc::vi2d TileTransformedView::GetTopLeftTile() const |
||||||
|
{ |
||||||
|
return ScreenToWorld({ 0,0 }).floor();
|
||||||
|
} |
||||||
|
|
||||||
|
olc::vi2d TileTransformedView::GetBottomRightTile() const |
||||||
|
{ |
||||||
|
return ScreenToWorld(m_vViewArea).ceil(); |
||||||
|
} |
||||||
|
|
||||||
|
olc::vi2d TileTransformedView::GetVisibleTiles() const |
||||||
|
{ |
||||||
|
return GetBottomRightTile() - GetTopLeftTile(); |
||||||
|
} |
||||||
|
|
||||||
|
olc::vi2d TileTransformedView::GetTileUnderScreenPos(const olc::vi2d& vPos) const |
||||||
|
{ |
||||||
|
return ScreenToWorld(vPos).floor();
|
||||||
|
} |
||||||
|
|
||||||
|
const olc::vi2d TileTransformedView::GetTileOffset() const |
||||||
|
{ |
||||||
|
return { int32_t((m_vWorldOffset.x - std::floor(m_vWorldOffset.x)) * m_vWorldScale.x), |
||||||
|
int32_t((m_vWorldOffset.y - std::floor(m_vWorldOffset.y)) * m_vWorldScale.y) }; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#endif |
||||||
|
#endif |
@ -1,722 +0,0 @@ |
|||||||
#pragma once |
|
||||||
|
|
||||||
#include "olcPixelGameEngine.h" |
|
||||||
|
|
||||||
#include <algorithm> |
|
||||||
#include <array> |
|
||||||
#include <cmath> |
|
||||||
#include <cstdint> |
|
||||||
#include <iostream> |
|
||||||
#include <vector> |
|
||||||
|
|
||||||
// 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<vf2d> 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<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 ¢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<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; |
|
||||||
|
|
||||||
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<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; |
|
||||||
} |
|
||||||
|
|
||||||
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<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, |
|
||||||
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<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; |
|
||||||
} |
|
||||||
|
|
||||||
drawClippedPolygonDecal(decal, pos, uvs.data(), w.data(), tint, 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; |
|
||||||
} |
|
||||||
|
|
||||||
drawClippedPolygonDecal(decal, pos, uvs.data(), ws.data(), tint, 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 ¢er, |
|
||||||
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 ¢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<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::DrawRectDecal(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}, |
|
||||||
}; |
|
||||||
|
|
||||||
// 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<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, |
|
||||||
}; |
|
||||||
|
|
||||||
drawClippedDecal(nullptr, points.data(), uvs.data(), colors.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; |
|
||||||
} |
|
||||||
|
|
||||||
drawClippedDecal(decal, pos.data(), uv.data(), colors.data(), pos.size()); |
|
||||||
} |
|
||||||
|
|
||||||
void olc::ViewPort::DrawPolygonDecal(Decal *decal, |
|
||||||
const std::vector<vf2d> &pos, |
|
||||||
const std::vector<float> &depth, |
|
||||||
const std::vector<vf2d> &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<vf2d> &pos, |
|
||||||
const std::vector<vf2d> &uv, |
|
||||||
const std::vector<Pixel> &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<vf2d> outputList{points, points + elements}; |
|
||||||
std::vector<vf2d> outputUvs{uvs, uvs + elements}; |
|
||||||
std::vector<Pixel> outputCols{col, col + elements}; |
|
||||||
|
|
||||||
for (auto i = 0u; i < clipVertices.size(); i++) { |
|
||||||
auto clipA = clipVertices[i] - offset; |
|
||||||
auto clipB = clipVertices[(i + 1) % clipVertices.size()] - offset; |
|
||||||
|
|
||||||
auto inputList{outputList}; |
|
||||||
auto inputUvs{outputUvs}; |
|
||||||
auto inputCols{outputCols}; |
|
||||||
outputList.clear(); |
|
||||||
outputUvs.clear(); |
|
||||||
outputCols.clear(); |
|
||||||
|
|
||||||
for (auto i = 0u; i < inputList.size(); i++) { |
|
||||||
auto polygonA = inputList[i]; |
|
||||||
auto polygonB = inputList[(i + 1) % inputList.size()]; |
|
||||||
auto uvA = inputUvs[i]; |
|
||||||
auto uvB = inputUvs[(i + 1) % inputList.size()]; |
|
||||||
auto colA = inputCols[i]; |
|
||||||
auto colB = inputCols[(i + 1) % inputList.size()]; |
|
||||||
|
|
||||||
auto intersection = |
|
||||||
lineSegmentIntersect(clipA, clipB, polygonA, polygonB); |
|
||||||
auto intersectionPoint = |
|
||||||
polygonA + (polygonB - polygonA) * intersection; |
|
||||||
auto intersectionUv = uvA + (uvB - uvA) * intersection; |
|
||||||
auto intersectionCol = PixelLerp(colA, colB, intersection); |
|
||||||
|
|
||||||
float aDirection = directionFromLine(clipA, clipB, polygonA); |
|
||||||
float bDirection = directionFromLine(clipA, clipB, polygonB); |
|
||||||
|
|
||||||
if (bDirection <= 0) { |
|
||||||
if (aDirection > 0) { |
|
||||||
outputList.push_back(intersectionPoint); |
|
||||||
outputUvs.push_back(intersectionUv); |
|
||||||
outputCols.push_back(intersectionCol); |
|
||||||
} |
|
||||||
outputList.push_back(polygonB); |
|
||||||
outputUvs.push_back(uvB); |
|
||||||
outputCols.push_back(colB); |
|
||||||
} else if (aDirection <= 0) { |
|
||||||
outputList.push_back(intersectionPoint); |
|
||||||
outputUvs.push_back(intersectionUv); |
|
||||||
outputCols.push_back(intersectionCol); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
if (outputList.size() == 0) { |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
for (auto &point : outputList) { |
|
||||||
point += offset; |
|
||||||
} |
|
||||||
|
|
||||||
pge->DrawExplicitDecal(decal, |
|
||||||
outputList.data(), |
|
||||||
outputUvs.data(), |
|
||||||
outputCols.data(), |
|
||||||
outputList.size()); |
|
||||||
} |
|
||||||
void olc::ViewPort::drawClippedPolygonDecal(Decal *decal, |
|
||||||
const vf2d *points, |
|
||||||
const vf2d *uvs, |
|
||||||
const float *depth, |
|
||||||
const Pixel tint, |
|
||||||
uint32_t elements) const { |
|
||||||
std::vector<vf2d> outputList{points, points + elements}; |
|
||||||
std::vector<vf2d> outputUvs{uvs, uvs + elements}; |
|
||||||
std::vector<float> outputDepths{depth, depth + elements}; |
|
||||||
|
|
||||||
for (auto i = 0u; i < clipVertices.size(); i++) { |
|
||||||
auto clipA = clipVertices[i] - offset; |
|
||||||
auto clipB = clipVertices[(i + 1) % clipVertices.size()] - offset; |
|
||||||
|
|
||||||
auto inputList{outputList}; |
|
||||||
auto inputUvs{outputUvs}; |
|
||||||
auto inputWs{outputDepths}; |
|
||||||
outputList.clear(); |
|
||||||
outputUvs.clear(); |
|
||||||
outputDepths.clear(); |
|
||||||
|
|
||||||
for (auto i = 0u; i < inputList.size(); i++) { |
|
||||||
auto polygonA = inputList[i]; |
|
||||||
auto polygonB = inputList[(i + 1) % inputList.size()]; |
|
||||||
auto uvA = inputUvs[i]; |
|
||||||
auto uvB = inputUvs[(i + 1) % inputList.size()]; |
|
||||||
auto Wa = inputWs[i]; |
|
||||||
auto Wb = inputWs[(i + 1) % inputList.size()]; |
|
||||||
|
|
||||||
auto intersection = |
|
||||||
lineSegmentIntersect(clipA, clipB, polygonA, polygonB); |
|
||||||
auto intersectionPoint = |
|
||||||
polygonA + (polygonB - polygonA) * intersection; |
|
||||||
auto intersectionUv = uvA + (uvB - uvA) * intersection; |
|
||||||
auto intersectionDepth = Wa + (Wb - Wa) * intersection; |
|
||||||
|
|
||||||
float aDirection = directionFromLine(clipA, clipB, polygonA); |
|
||||||
float bDirection = directionFromLine(clipA, clipB, polygonB); |
|
||||||
|
|
||||||
if (bDirection <= 0) { |
|
||||||
if (aDirection > 0) { |
|
||||||
outputList.push_back(intersectionPoint); |
|
||||||
outputUvs.push_back(intersectionUv); |
|
||||||
outputDepths.push_back(intersectionDepth); |
|
||||||
} |
|
||||||
outputList.push_back(polygonB); |
|
||||||
outputUvs.push_back(uvB); |
|
||||||
outputDepths.push_back(Wb); |
|
||||||
} else if (aDirection <= 0) { |
|
||||||
outputList.push_back(intersectionPoint); |
|
||||||
outputUvs.push_back(intersectionUv); |
|
||||||
outputDepths.push_back(intersectionDepth); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
for (auto &point : outputList) { |
|
||||||
point += offset; |
|
||||||
} |
|
||||||
|
|
||||||
pge->DrawPolygonDecal(decal, |
|
||||||
outputList, |
|
||||||
outputDepths, |
|
||||||
outputUvs, |
|
||||||
tint); |
|
||||||
} |
|
||||||
|
|
||||||
bool olc::ViewPort::ccw(vf2d A,vf2d B,vf2d C) { |
|
||||||
return (C.y-A.y) * (B.x-A.x) > (B.y-A.y) * (C.x-A.x); |
|
||||||
} |
|
||||||
|
|
||||||
bool olc::ViewPort::intersect(vf2d A,vf2d B,vf2d C,vf2d D) { |
|
||||||
return ccw(A,C,D) != ccw(B,C,D) and ccw(A,B,C) != ccw(A,B,D); |
|
||||||
} |
|
||||||
|
|
||||||
float olc::ViewPort::lineSegmentIntersect(vf2d lineA, |
|
||||||
vf2d lineB, |
|
||||||
vf2d segmentA, |
|
||||||
vf2d segmentB) { |
|
||||||
return ((lineA.x - segmentA.x) * (lineA.y - lineB.y) |
|
||||||
- (lineA.y - segmentA.y) * (lineA.x - lineB.x)) |
|
||||||
/ ((lineA.x - lineB.x) * (segmentA.y - segmentB.y) |
|
||||||
- (lineA.y - lineB.y) * (segmentA.x - segmentB.x)); |
|
||||||
} |
|
||||||
|
|
||||||
float olc::ViewPort::directionFromLine(vf2d lineA, vf2d lineB, vf2d point) { |
|
||||||
return (lineB.x - lineA.x) * (point.y - lineA.y) |
|
||||||
- (point.x - lineA.x) * (lineB.y - lineA.y); |
|
||||||
} |
|
||||||
|
|
||||||
#endif |
|
@ -1,5 +1,5 @@ |
|||||||
#include "olcUTIL_Geometry2D.h" |
#include "olcUTIL_Geometry2D.h" |
||||||
#define OLC_PGE_APPLICATION |
#define OLC_PGE_APPLICATION |
||||||
#include "olcPixelGameEngine.h" |
#include "olcPixelGameEngine.h" |
||||||
#define OLC_PGEX_VIEWPORT |
#define OLC_PGEX_TRANSFORMEDVIEW |
||||||
#include "olcPGEX_ViewPort.h" |
#include "olcPGEX_TransformedView.h" |
Loading…
Reference in new issue