PixeGameEngine v1.20

+ Foundations for more system implementations
+ More compiler friendly on Linux
+ Tidied up public repo
pull/113/head
javidx9 5 years ago
parent 1cf1544316
commit 396a40eb5c
  1. 624
      Extensions/olcPGEX_Graphics2D.h
  2. 2346
      Extensions/olcPGEX_Graphics3D.h
  3. 1782
      Extensions/olcPGEX_Sound.h
  4. 0
      Videos/CarCrimeCity/Part1/City_Roads1_mip0.png
  5. 1338
      Videos/CarCrimeCity/Part1/OneLoneCoder_CarCrimeCity1.cpp
  6. 0
      Videos/CarCrimeCity/Part1/car_top.png
  7. 0
      Videos/CarCrimeCity/Part1/car_top1.png
  8. 0
      Videos/CarCrimeCity/Part1/example1.city
  9. 2346
      Videos/CarCrimeCity/Part1/olcPGEX_Graphics3D.h
  10. 4132
      Videos/CarCrimeCity/Part1/olcPixelGameEngine.h
  11. 0
      Videos/CarCrimeCity/Part2/Lua533/include/lauxlib.h
  12. 0
      Videos/CarCrimeCity/Part2/Lua533/include/lua.h
  13. 0
      Videos/CarCrimeCity/Part2/Lua533/include/lua.hpp
  14. 0
      Videos/CarCrimeCity/Part2/Lua533/include/luaconf.h
  15. 0
      Videos/CarCrimeCity/Part2/Lua533/include/lualib.h
  16. 0
      Videos/CarCrimeCity/Part2/Lua533/liblua53.a
  17. 0
      Videos/CarCrimeCity/Part2/Lua533/lua53.dll
  18. 0
      Videos/CarCrimeCity/Part2/assets/buildings/udxs_building1.obj
  19. 0
      Videos/CarCrimeCity/Part2/assets/buildings/udxs_building1.png
  20. 0
      Videos/CarCrimeCity/Part2/assets/buildings/unit_building.blend
  21. 0
      Videos/CarCrimeCity/Part2/assets/buildings/unit_building.blend1
  22. 0
      Videos/CarCrimeCity/Part2/assets/buildings/unit_building.obj
  23. 0
      Videos/CarCrimeCity/Part2/assets/cities/example1.city
  24. 100
      Videos/CarCrimeCity/Part2/assets/config.lua
  25. 0
      Videos/CarCrimeCity/Part2/assets/system/car_top.png
  26. 0
      Videos/CarCrimeCity/Part2/assets/system/car_top3.png
  27. 0
      Videos/CarCrimeCity/Part2/assets/system/ccctitle1.png
  28. 0
      Videos/CarCrimeCity/Part2/assets/system/clouds1.png
  29. 0
      Videos/CarCrimeCity/Part2/assets/system/clouds2.png
  30. 0
      Videos/CarCrimeCity/Part2/assets/system/grass1.png
  31. 0
      Videos/CarCrimeCity/Part2/assets/system/roads1.png
  32. 0
      Videos/CarCrimeCity/Part2/assets/system/roads2.png
  33. 0
      Videos/CarCrimeCity/Part2/assets/system/roads3.png
  34. 0
      Videos/CarCrimeCity/Part2/assets/system/roads4.png
  35. 0
      Videos/CarCrimeCity/Part2/assets/system/skidsmoke1.png
  36. 0
      Videos/CarCrimeCity/Part2/assets/system/water1.png
  37. 0
      Videos/CarCrimeCity/Part2/assets/system/waterside1.png
  38. 0
      Videos/CarCrimeCity/Part2/assets/vehicles/CarCrime_SUV.obj
  39. 0
      Videos/CarCrimeCity/Part2/assets/vehicles/CarCrime_Sedan.obj
  40. 0
      Videos/CarCrimeCity/Part2/assets/vehicles/CarCrime_Truck_Cab.obj
  41. 0
      Videos/CarCrimeCity/Part2/assets/vehicles/CarCrime_Truck_Trailer.obj
  42. 0
      Videos/CarCrimeCity/Part2/assets/vehicles/CarCrime_Ute.obj
  43. 0
      Videos/CarCrimeCity/Part2/assets/vehicles/CarCrime_Wagon.obj
  44. 0
      Videos/CarCrimeCity/Part2/assets/vehicles/CarTex_256.png
  45. 412
      Videos/CarCrimeCity/Part2/cAutomata.cpp
  46. 214
      Videos/CarCrimeCity/Part2/cAutomata.h
  47. 1418
      Videos/CarCrimeCity/Part2/cCarCrimeCity.cpp
  48. 578
      Videos/CarCrimeCity/Part2/cCarCrimeCity.h
  49. 242
      Videos/CarCrimeCity/Part2/cCell.cpp
  50. 120
      Videos/CarCrimeCity/Part2/cCell.h
  51. 106
      Videos/CarCrimeCity/Part2/cCell_Building.cpp
  52. 50
      Videos/CarCrimeCity/Part2/cCell_Building.h
  53. 98
      Videos/CarCrimeCity/Part2/cCell_Plane.cpp
  54. 68
      Videos/CarCrimeCity/Part2/cCell_Plane.h
  55. 1624
      Videos/CarCrimeCity/Part2/cCell_Road.cpp
  56. 108
      Videos/CarCrimeCity/Part2/cCell_Road.h
  57. 180
      Videos/CarCrimeCity/Part2/cCell_Water.cpp
  58. 50
      Videos/CarCrimeCity/Part2/cCell_Water.h
  59. 402
      Videos/CarCrimeCity/Part2/cCityMap.cpp
  60. 126
      Videos/CarCrimeCity/Part2/cCityMap.h
  61. 310
      Videos/CarCrimeCity/Part2/cGameSettings.cpp
  62. 130
      Videos/CarCrimeCity/Part2/cGameSettings.h
  63. 0
      Videos/CarCrimeCity/Part2/lua53.dll
  64. 732
      Videos/CarCrimeCity/Part2/main.cpp
  65. 3448
      Videos/CarCrimeCity/Part2/olcPGEX_Graphics3D.h
  66. 10
      Videos/CarCrimeCity/Part2/olcPixelGameEngine.cpp
  67. 4634
      Videos/CarCrimeCity/Part2/olcPixelGameEngine.h
  68. 0
      Videos/DemoBinaries/OLC_8BitsImProc.zip
  69. 1008
      Videos/OneLoneCoder_PGE_8BitsImProc.cpp
  70. 1000
      Videos/OneLoneCoder_PGE_Balls2.cpp
  71. 376
      Videos/OneLoneCoder_PGE_ExtensionTestGFX2D.cpp
  72. 850
      Videos/OneLoneCoder_PGE_PathFinding_WaveProp.cpp
  73. 866
      Videos/OneLoneCoder_PGE_PolygonCollisions1.cpp
  74. 1072
      Videos/OneLoneCoder_PGE_Polymorphism.cpp
  75. 542
      Videos/OneLoneCoder_PGE_RobotArm1.cpp
  76. 0
      Videos/OneLoneCoder_PGE_ShadowCasting2D.cpp
  77. 542
      Videos/OneLoneCoder_PGE_SoundTest.cpp
  78. 512
      Videos/OneLoneCoder_PGE_SpriteTransforms.cpp
  79. 0
      Videos/OneLoneCoder_PGE_olcEngine3D.cpp
  80. 0
      Videos/SampleA.wav
  81. 0
      Videos/SampleB.wav
  82. 0
      Videos/SampleC.wav
  83. 3018
      Videos/SavingSedit/OneLoneCoder_PGE_SavingSedit.cpp
  84. 446
      Videos/SavingSedit/Zix_PGE_Controller.h
  85. 84
      Videos/SavingSedit/licence.txt
  86. 624
      Videos/SavingSedit/olcPGEX_Graphics2D.h
  87. 1810
      Videos/SavingSedit/olcPGEX_Sound.h
  88. 762
      Videos/SavingSedit/olcPGEX_TileMaps_new.h
  89. 4634
      Videos/SavingSedit/olcPixelGameEngine.h
  90. 0
      Videos/car_top1.png
  91. 0
      Videos/light_cast.png
  92. 0
      Videos/logo_long.png
  93. 0
      Videos/mountains.obj
  94. 313
      Videos/olcPGEX_Graphics2D.h
  95. 1174
      Videos/olcPGEX_Graphics3D.h
  96. 892
      Videos/olcPGEX_Sound.h
  97. 2353
      Videos/olcPixelGameEngine.h
  98. 0
      Videos/zombie.png
  99. 149
      olcPixelGameEngine.h

@ -1,313 +1,313 @@
/* /*
olcPGEX_Graphics2D.h olcPGEX_Graphics2D.h
+-------------------------------------------------------------+ +-------------------------------------------------------------+
| OneLoneCoder Pixel Game Engine Extension | | OneLoneCoder Pixel Game Engine Extension |
| Advanced 2D Rendering - v0.4 | | Advanced 2D Rendering - v0.4 |
+-------------------------------------------------------------+ +-------------------------------------------------------------+
What is this? What is this?
~~~~~~~~~~~~~ ~~~~~~~~~~~~~
This is an extension to the olcPixelGameEngine, which provides This is an extension to the olcPixelGameEngine, which provides
advanced olc::Sprite manipulation and drawing routines. To use advanced olc::Sprite manipulation and drawing routines. To use
it, simply include this header file. it, simply include this header file.
License (OLC-3) License (OLC-3)
~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~
Copyright 2018 - 2019 OneLoneCoder.com Copyright 2018 - 2019 OneLoneCoder.com
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions modification, are permitted provided that the following conditions
are met: are met:
1. Redistributions or derivations of source code must retain the above 1. Redistributions or derivations of source code must retain the above
copyright notice, this list of conditions and the following disclaimer. copyright notice, this list of conditions and the following disclaimer.
2. Redistributions or derivative works in binary form must reproduce 2. Redistributions or derivative works in binary form must reproduce
the above copyright notice. This list of conditions and the following the above copyright notice. This list of conditions and the following
disclaimer must be reproduced in the documentation and/or other disclaimer must be reproduced in the documentation and/or other
materials provided with the distribution. materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its 3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived contributors may be used to endorse or promote products derived
from this software without specific prior written permission. from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Links Links
~~~~~ ~~~~~
YouTube: https://www.youtube.com/javidx9 YouTube: https://www.youtube.com/javidx9
Discord: https://discord.gg/WhwHUMV Discord: https://discord.gg/WhwHUMV
Twitter: https://www.twitter.com/javidx9 Twitter: https://www.twitter.com/javidx9
Twitch: https://www.twitch.tv/javidx9 Twitch: https://www.twitch.tv/javidx9
GitHub: https://www.github.com/onelonecoder GitHub: https://www.github.com/onelonecoder
Homepage: https://www.onelonecoder.com Homepage: https://www.onelonecoder.com
Author Author
~~~~~~ ~~~~~~
David Barr, aka javidx9, ©OneLoneCoder 2019 David Barr, aka javidx9, ©OneLoneCoder 2019
*/ */
/* /*
Matrices stored as [Column][Row] (i.e. x, y) Matrices stored as [Column][Row] (i.e. x, y)
|C0R0 C1R0 C2R0| | x | | x'| |C0R0 C1R0 C2R0| | x | | x'|
|C0R1 C1R1 C2R1| * | y | = | y'| |C0R1 C1R1 C2R1| * | y | = | y'|
|C0R2 C1R2 C2R2| |1.0| | - | |C0R2 C1R2 C2R2| |1.0| | - |
*/ */
#ifndef OLC_PGEX_GFX2D #ifndef OLC_PGEX_GFX2D
#define OLC_PGEX_GFX2D #define OLC_PGEX_GFX2D
#include <algorithm> #include <algorithm>
#undef min #undef min
#undef max #undef max
namespace olc namespace olc
{ {
// Container class for Advanced 2D Drawing functions // Container class for Advanced 2D Drawing functions
class GFX2D : public olc::PGEX class GFX2D : public olc::PGEX
{ {
// A representation of an affine transform, used to rotate, scale, offset & shear space // A representation of an affine transform, used to rotate, scale, offset & shear space
public: public:
class Transform2D class Transform2D
{ {
public: public:
inline Transform2D(); inline Transform2D();
public: public:
// Set this transformation to unity // Set this transformation to unity
inline void Reset(); inline void Reset();
// Append a rotation of fTheta radians to this transform // Append a rotation of fTheta radians to this transform
inline void Rotate(float fTheta); inline void Rotate(float fTheta);
// Append a translation (ox, oy) to this transform // Append a translation (ox, oy) to this transform
inline void Translate(float ox, float oy); inline void Translate(float ox, float oy);
// Append a scaling operation (sx, sy) to this transform // Append a scaling operation (sx, sy) to this transform
inline void Scale(float sx, float sy); inline void Scale(float sx, float sy);
// Append a shear operation (sx, sy) to this transform // Append a shear operation (sx, sy) to this transform
inline void Shear(float sx, float sy); inline void Shear(float sx, float sy);
inline void Perspective(float ox, float oy); inline void Perspective(float ox, float oy);
// Calculate the Forward Transformation of the coordinate (in_x, in_y) -> (out_x, out_y) // Calculate the Forward Transformation of the coordinate (in_x, in_y) -> (out_x, out_y)
inline void Forward(float in_x, float in_y, float &out_x, float &out_y); inline void Forward(float in_x, float in_y, float &out_x, float &out_y);
// Calculate the Inverse Transformation of the coordinate (in_x, in_y) -> (out_x, out_y) // Calculate the Inverse Transformation of the coordinate (in_x, in_y) -> (out_x, out_y)
inline void Backward(float in_x, float in_y, float &out_x, float &out_y); inline void Backward(float in_x, float in_y, float &out_x, float &out_y);
// Regenerate the Inverse Transformation // Regenerate the Inverse Transformation
inline void Invert(); inline void Invert();
private: private:
inline void Multiply(); inline void Multiply();
float matrix[4][3][3]; float matrix[4][3][3];
int nTargetMatrix; int nTargetMatrix;
int nSourceMatrix; int nSourceMatrix;
bool bDirty; bool bDirty;
}; };
public: public:
// Draws a sprite with the transform applied // Draws a sprite with the transform applied
inline static void DrawSprite(olc::Sprite *sprite, olc::GFX2D::Transform2D &transform); inline static void DrawSprite(olc::Sprite *sprite, olc::GFX2D::Transform2D &transform);
}; };
} }
#ifdef OLC_PGE_GRAPHICS2D #ifdef OLC_PGE_GRAPHICS2D
#undef OLC_PGE_GRAPHICS2D #undef OLC_PGE_GRAPHICS2D
namespace olc namespace olc
{ {
void GFX2D::DrawSprite(olc::Sprite *sprite, olc::GFX2D::Transform2D &transform) void GFX2D::DrawSprite(olc::Sprite *sprite, olc::GFX2D::Transform2D &transform)
{ {
if (sprite == nullptr) if (sprite == nullptr)
return; return;
// Work out bounding rectangle of sprite // Work out bounding rectangle of sprite
float ex, ey; float ex, ey;
float sx, sy; float sx, sy;
float px, py; float px, py;
transform.Forward(0.0f, 0.0f, sx, sy); transform.Forward(0.0f, 0.0f, sx, sy);
px = sx; py = sy; px = sx; py = sy;
sx = std::min(sx, px); sy = std::min(sy, py); sx = std::min(sx, px); sy = std::min(sy, py);
ex = std::max(ex, px); ey = std::max(ey, py); ex = std::max(ex, px); ey = std::max(ey, py);
transform.Forward((float)sprite->width, (float)sprite->height, px, py); transform.Forward((float)sprite->width, (float)sprite->height, px, py);
sx = std::min(sx, px); sy = std::min(sy, py); sx = std::min(sx, px); sy = std::min(sy, py);
ex = std::max(ex, px); ey = std::max(ey, py); ex = std::max(ex, px); ey = std::max(ey, py);
transform.Forward(0.0f, (float)sprite->height, px, py); transform.Forward(0.0f, (float)sprite->height, px, py);
sx = std::min(sx, px); sy = std::min(sy, py); sx = std::min(sx, px); sy = std::min(sy, py);
ex = std::max(ex, px); ey = std::max(ey, py); ex = std::max(ex, px); ey = std::max(ey, py);
transform.Forward((float)sprite->width, 0.0f, px, py); transform.Forward((float)sprite->width, 0.0f, px, py);
sx = std::min(sx, px); sy = std::min(sy, py); sx = std::min(sx, px); sy = std::min(sy, py);
ex = std::max(ex, px); ey = std::max(ey, py); ex = std::max(ex, px); ey = std::max(ey, py);
// Perform inversion of transform if required // Perform inversion of transform if required
transform.Invert(); transform.Invert();
if (ex < sx) if (ex < sx)
std::swap(ex, sx); std::swap(ex, sx);
if (ey < sy) if (ey < sy)
std::swap(ey, sy); std::swap(ey, sy);
// Iterate through render space, and sample Sprite from suitable texel location // Iterate through render space, and sample Sprite from suitable texel location
for (float i = sx; i < ex; i++) for (float i = sx; i < ex; i++)
{ {
for (float j = sy; j < ey; j++) for (float j = sy; j < ey; j++)
{ {
float ox, oy; float ox, oy;
transform.Backward(i, j, ox, oy); transform.Backward(i, j, ox, oy);
pge->Draw((int32_t)i, (int32_t)j, sprite->GetPixel((int32_t)(ox+0.5f), (int32_t)(oy+0.5f))); pge->Draw((int32_t)i, (int32_t)j, sprite->GetPixel((int32_t)(ox+0.5f), (int32_t)(oy+0.5f)));
} }
} }
} }
olc::GFX2D::Transform2D::Transform2D() olc::GFX2D::Transform2D::Transform2D()
{ {
Reset(); Reset();
} }
void olc::GFX2D::Transform2D::Reset() void olc::GFX2D::Transform2D::Reset()
{ {
nTargetMatrix = 0; nTargetMatrix = 0;
nSourceMatrix = 1; nSourceMatrix = 1;
bDirty = true; bDirty = true;
// Columns Then Rows // Columns Then Rows
// Matrices 0 & 1 are used as swaps in Transform accumulation // Matrices 0 & 1 are used as swaps in Transform accumulation
matrix[0][0][0] = 1.0f; matrix[0][1][0] = 0.0f; matrix[0][2][0] = 0.0f; matrix[0][0][0] = 1.0f; matrix[0][1][0] = 0.0f; matrix[0][2][0] = 0.0f;
matrix[0][0][1] = 0.0f; matrix[0][1][1] = 1.0f; matrix[0][2][1] = 0.0f; matrix[0][0][1] = 0.0f; matrix[0][1][1] = 1.0f; matrix[0][2][1] = 0.0f;
matrix[0][0][2] = 0.0f; matrix[0][1][2] = 0.0f; matrix[0][2][2] = 1.0f; matrix[0][0][2] = 0.0f; matrix[0][1][2] = 0.0f; matrix[0][2][2] = 1.0f;
matrix[1][0][0] = 1.0f; matrix[1][1][0] = 0.0f; matrix[1][2][0] = 0.0f; matrix[1][0][0] = 1.0f; matrix[1][1][0] = 0.0f; matrix[1][2][0] = 0.0f;
matrix[1][0][1] = 0.0f; matrix[1][1][1] = 1.0f; matrix[1][2][1] = 0.0f; matrix[1][0][1] = 0.0f; matrix[1][1][1] = 1.0f; matrix[1][2][1] = 0.0f;
matrix[1][0][2] = 0.0f; matrix[1][1][2] = 0.0f; matrix[1][2][2] = 1.0f; matrix[1][0][2] = 0.0f; matrix[1][1][2] = 0.0f; matrix[1][2][2] = 1.0f;
// Matrix 2 is a cache matrix to hold the immediate transform operation // Matrix 2 is a cache matrix to hold the immediate transform operation
// Matrix 3 is a cache matrix to hold the inverted transform // Matrix 3 is a cache matrix to hold the inverted transform
} }
void olc::GFX2D::Transform2D::Multiply() void olc::GFX2D::Transform2D::Multiply()
{ {
for (int c = 0; c < 3; c++) for (int c = 0; c < 3; c++)
{ {
for (int r = 0; r < 3; r++) for (int r = 0; r < 3; r++)
{ {
matrix[nTargetMatrix][c][r] = matrix[2][0][r] * matrix[nSourceMatrix][c][0] + matrix[nTargetMatrix][c][r] = matrix[2][0][r] * matrix[nSourceMatrix][c][0] +
matrix[2][1][r] * matrix[nSourceMatrix][c][1] + matrix[2][1][r] * matrix[nSourceMatrix][c][1] +
matrix[2][2][r] * matrix[nSourceMatrix][c][2]; matrix[2][2][r] * matrix[nSourceMatrix][c][2];
} }
} }
std::swap(nTargetMatrix, nSourceMatrix); std::swap(nTargetMatrix, nSourceMatrix);
bDirty = true; // Any transform multiply dirties the inversion bDirty = true; // Any transform multiply dirties the inversion
} }
void olc::GFX2D::Transform2D::Rotate(float fTheta) void olc::GFX2D::Transform2D::Rotate(float fTheta)
{ {
// Construct Rotation Matrix // Construct Rotation Matrix
matrix[2][0][0] = cosf(fTheta); matrix[2][1][0] = sinf(fTheta); matrix[2][2][0] = 0.0f; matrix[2][0][0] = cosf(fTheta); matrix[2][1][0] = sinf(fTheta); matrix[2][2][0] = 0.0f;
matrix[2][0][1] = -sinf(fTheta); matrix[2][1][1] = cosf(fTheta); matrix[2][2][1] = 0.0f; matrix[2][0][1] = -sinf(fTheta); matrix[2][1][1] = cosf(fTheta); matrix[2][2][1] = 0.0f;
matrix[2][0][2] = 0.0f; matrix[2][1][2] = 0.0f; matrix[2][2][2] = 1.0f; matrix[2][0][2] = 0.0f; matrix[2][1][2] = 0.0f; matrix[2][2][2] = 1.0f;
Multiply(); Multiply();
} }
void olc::GFX2D::Transform2D::Scale(float sx, float sy) void olc::GFX2D::Transform2D::Scale(float sx, float sy)
{ {
// Construct Scale Matrix // Construct Scale Matrix
matrix[2][0][0] = sx; matrix[2][1][0] = 0.0f; matrix[2][2][0] = 0.0f; matrix[2][0][0] = sx; matrix[2][1][0] = 0.0f; matrix[2][2][0] = 0.0f;
matrix[2][0][1] = 0.0f; matrix[2][1][1] = sy; matrix[2][2][1] = 0.0f; matrix[2][0][1] = 0.0f; matrix[2][1][1] = sy; matrix[2][2][1] = 0.0f;
matrix[2][0][2] = 0.0f; matrix[2][1][2] = 0.0f; matrix[2][2][2] = 1.0f; matrix[2][0][2] = 0.0f; matrix[2][1][2] = 0.0f; matrix[2][2][2] = 1.0f;
Multiply(); Multiply();
} }
void olc::GFX2D::Transform2D::Shear(float sx, float sy) void olc::GFX2D::Transform2D::Shear(float sx, float sy)
{ {
// Construct Shear Matrix // Construct Shear Matrix
matrix[2][0][0] = 1.0f; matrix[2][1][0] = sx; matrix[2][2][0] = 0.0f; matrix[2][0][0] = 1.0f; matrix[2][1][0] = sx; matrix[2][2][0] = 0.0f;
matrix[2][0][1] = sy; matrix[2][1][1] = 1.0f; matrix[2][2][1] = 0.0f; matrix[2][0][1] = sy; matrix[2][1][1] = 1.0f; matrix[2][2][1] = 0.0f;
matrix[2][0][2] = 0.0f; matrix[2][1][2] = 0.0f; matrix[2][2][2] = 1.0f; matrix[2][0][2] = 0.0f; matrix[2][1][2] = 0.0f; matrix[2][2][2] = 1.0f;
Multiply(); Multiply();
} }
void olc::GFX2D::Transform2D::Translate(float ox, float oy) void olc::GFX2D::Transform2D::Translate(float ox, float oy)
{ {
// Construct Translate Matrix // Construct Translate Matrix
matrix[2][0][0] = 1.0f; matrix[2][1][0] = 0.0f; matrix[2][2][0] = ox; matrix[2][0][0] = 1.0f; matrix[2][1][0] = 0.0f; matrix[2][2][0] = ox;
matrix[2][0][1] = 0.0f; matrix[2][1][1] = 1.0f; matrix[2][2][1] = oy; matrix[2][0][1] = 0.0f; matrix[2][1][1] = 1.0f; matrix[2][2][1] = oy;
matrix[2][0][2] = 0.0f; matrix[2][1][2] = 0.0f; matrix[2][2][2] = 1.0f; matrix[2][0][2] = 0.0f; matrix[2][1][2] = 0.0f; matrix[2][2][2] = 1.0f;
Multiply(); Multiply();
} }
void olc::GFX2D::Transform2D::Perspective(float ox, float oy) void olc::GFX2D::Transform2D::Perspective(float ox, float oy)
{ {
// Construct Translate Matrix // Construct Translate Matrix
matrix[2][0][0] = 1.0f; matrix[2][1][0] = 0.0f; matrix[2][2][0] = 0.0f; matrix[2][0][0] = 1.0f; matrix[2][1][0] = 0.0f; matrix[2][2][0] = 0.0f;
matrix[2][0][1] = 0.0f; matrix[2][1][1] = 1.0f; matrix[2][2][1] = 0.0f; matrix[2][0][1] = 0.0f; matrix[2][1][1] = 1.0f; matrix[2][2][1] = 0.0f;
matrix[2][0][2] = ox; matrix[2][1][2] = oy; matrix[2][2][2] = 1.0f; matrix[2][0][2] = ox; matrix[2][1][2] = oy; matrix[2][2][2] = 1.0f;
Multiply(); Multiply();
} }
void olc::GFX2D::Transform2D::Forward(float in_x, float in_y, float &out_x, float &out_y) void olc::GFX2D::Transform2D::Forward(float in_x, float in_y, float &out_x, float &out_y)
{ {
out_x = in_x * matrix[nSourceMatrix][0][0] + in_y * matrix[nSourceMatrix][1][0] + matrix[nSourceMatrix][2][0]; out_x = in_x * matrix[nSourceMatrix][0][0] + in_y * matrix[nSourceMatrix][1][0] + matrix[nSourceMatrix][2][0];
out_y = in_x * matrix[nSourceMatrix][0][1] + in_y * matrix[nSourceMatrix][1][1] + matrix[nSourceMatrix][2][1]; out_y = in_x * matrix[nSourceMatrix][0][1] + in_y * matrix[nSourceMatrix][1][1] + matrix[nSourceMatrix][2][1];
float out_z = in_x * matrix[nSourceMatrix][0][2] + in_y * matrix[nSourceMatrix][1][2] + matrix[nSourceMatrix][2][2]; float out_z = in_x * matrix[nSourceMatrix][0][2] + in_y * matrix[nSourceMatrix][1][2] + matrix[nSourceMatrix][2][2];
if (out_z != 0) if (out_z != 0)
{ {
out_x /= out_z; out_x /= out_z;
out_y /= out_z; out_y /= out_z;
} }
} }
void olc::GFX2D::Transform2D::Backward(float in_x, float in_y, float &out_x, float &out_y) void olc::GFX2D::Transform2D::Backward(float in_x, float in_y, float &out_x, float &out_y)
{ {
out_x = in_x * matrix[3][0][0] + in_y * matrix[3][1][0] + matrix[3][2][0]; out_x = in_x * matrix[3][0][0] + in_y * matrix[3][1][0] + matrix[3][2][0];
out_y = in_x * matrix[3][0][1] + in_y * matrix[3][1][1] + matrix[3][2][1]; out_y = in_x * matrix[3][0][1] + in_y * matrix[3][1][1] + matrix[3][2][1];
float out_z = in_x * matrix[3][0][2] + in_y * matrix[3][1][2] + matrix[3][2][2]; float out_z = in_x * matrix[3][0][2] + in_y * matrix[3][1][2] + matrix[3][2][2];
if (out_z != 0) if (out_z != 0)
{ {
out_x /= out_z; out_x /= out_z;
out_y /= out_z; out_y /= out_z;
} }
} }
void olc::GFX2D::Transform2D::Invert() void olc::GFX2D::Transform2D::Invert()
{ {
if (bDirty) // Obviously costly so only do if needed if (bDirty) // Obviously costly so only do if needed
{ {
float det = matrix[nSourceMatrix][0][0] * (matrix[nSourceMatrix][1][1] * matrix[nSourceMatrix][2][2] - matrix[nSourceMatrix][1][2] * matrix[nSourceMatrix][2][1]) - float det = matrix[nSourceMatrix][0][0] * (matrix[nSourceMatrix][1][1] * matrix[nSourceMatrix][2][2] - matrix[nSourceMatrix][1][2] * matrix[nSourceMatrix][2][1]) -
matrix[nSourceMatrix][1][0] * (matrix[nSourceMatrix][0][1] * matrix[nSourceMatrix][2][2] - matrix[nSourceMatrix][2][1] * matrix[nSourceMatrix][0][2]) + matrix[nSourceMatrix][1][0] * (matrix[nSourceMatrix][0][1] * matrix[nSourceMatrix][2][2] - matrix[nSourceMatrix][2][1] * matrix[nSourceMatrix][0][2]) +
matrix[nSourceMatrix][2][0] * (matrix[nSourceMatrix][0][1] * matrix[nSourceMatrix][1][2] - matrix[nSourceMatrix][1][1] * matrix[nSourceMatrix][0][2]); matrix[nSourceMatrix][2][0] * (matrix[nSourceMatrix][0][1] * matrix[nSourceMatrix][1][2] - matrix[nSourceMatrix][1][1] * matrix[nSourceMatrix][0][2]);
float idet = 1.0f / det; float idet = 1.0f / det;
matrix[3][0][0] = (matrix[nSourceMatrix][1][1] * matrix[nSourceMatrix][2][2] - matrix[nSourceMatrix][1][2] * matrix[nSourceMatrix][2][1]) * idet; matrix[3][0][0] = (matrix[nSourceMatrix][1][1] * matrix[nSourceMatrix][2][2] - matrix[nSourceMatrix][1][2] * matrix[nSourceMatrix][2][1]) * idet;
matrix[3][1][0] = (matrix[nSourceMatrix][2][0] * matrix[nSourceMatrix][1][2] - matrix[nSourceMatrix][1][0] * matrix[nSourceMatrix][2][2]) * idet; matrix[3][1][0] = (matrix[nSourceMatrix][2][0] * matrix[nSourceMatrix][1][2] - matrix[nSourceMatrix][1][0] * matrix[nSourceMatrix][2][2]) * idet;
matrix[3][2][0] = (matrix[nSourceMatrix][1][0] * matrix[nSourceMatrix][2][1] - matrix[nSourceMatrix][2][0] * matrix[nSourceMatrix][1][1]) * idet; matrix[3][2][0] = (matrix[nSourceMatrix][1][0] * matrix[nSourceMatrix][2][1] - matrix[nSourceMatrix][2][0] * matrix[nSourceMatrix][1][1]) * idet;
matrix[3][0][1] = (matrix[nSourceMatrix][2][1] * matrix[nSourceMatrix][0][2] - matrix[nSourceMatrix][0][1] * matrix[nSourceMatrix][2][2]) * idet; matrix[3][0][1] = (matrix[nSourceMatrix][2][1] * matrix[nSourceMatrix][0][2] - matrix[nSourceMatrix][0][1] * matrix[nSourceMatrix][2][2]) * idet;
matrix[3][1][1] = (matrix[nSourceMatrix][0][0] * matrix[nSourceMatrix][2][2] - matrix[nSourceMatrix][2][0] * matrix[nSourceMatrix][0][2]) * idet; matrix[3][1][1] = (matrix[nSourceMatrix][0][0] * matrix[nSourceMatrix][2][2] - matrix[nSourceMatrix][2][0] * matrix[nSourceMatrix][0][2]) * idet;
matrix[3][2][1] = (matrix[nSourceMatrix][0][1] * matrix[nSourceMatrix][2][0] - matrix[nSourceMatrix][0][0] * matrix[nSourceMatrix][2][1]) * idet; matrix[3][2][1] = (matrix[nSourceMatrix][0][1] * matrix[nSourceMatrix][2][0] - matrix[nSourceMatrix][0][0] * matrix[nSourceMatrix][2][1]) * idet;
matrix[3][0][2] = (matrix[nSourceMatrix][0][1] * matrix[nSourceMatrix][1][2] - matrix[nSourceMatrix][0][2] * matrix[nSourceMatrix][1][1]) * idet; matrix[3][0][2] = (matrix[nSourceMatrix][0][1] * matrix[nSourceMatrix][1][2] - matrix[nSourceMatrix][0][2] * matrix[nSourceMatrix][1][1]) * idet;
matrix[3][1][2] = (matrix[nSourceMatrix][0][2] * matrix[nSourceMatrix][1][0] - matrix[nSourceMatrix][0][0] * matrix[nSourceMatrix][1][2]) * idet; matrix[3][1][2] = (matrix[nSourceMatrix][0][2] * matrix[nSourceMatrix][1][0] - matrix[nSourceMatrix][0][0] * matrix[nSourceMatrix][1][2]) * idet;
matrix[3][2][2] = (matrix[nSourceMatrix][0][0] * matrix[nSourceMatrix][1][1] - matrix[nSourceMatrix][0][1] * matrix[nSourceMatrix][1][0]) * idet; matrix[3][2][2] = (matrix[nSourceMatrix][0][0] * matrix[nSourceMatrix][1][1] - matrix[nSourceMatrix][0][1] * matrix[nSourceMatrix][1][0]) * idet;
bDirty = false; bDirty = false;
} }
} }
} }
#endif #endif
#endif #endif

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 41 KiB

Before

Width:  |  Height:  |  Size: 181 KiB

After

Width:  |  Height:  |  Size: 181 KiB

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

@ -1,50 +1,50 @@
-- Size of pixel -- Size of pixel
PixelWidth = 2 PixelWidth = 2
PixelHeight = 2 PixelHeight = 2
-- Size of display window in pixels -- Size of display window in pixels
ScreenWidth = 768 ScreenWidth = 768
ScreenHeight = 480 ScreenHeight = 480
--ScreenWidth = 384 --ScreenWidth = 384
--ScreenHeight = 240 --ScreenHeight = 240
FullScreen = false FullScreen = false
-- Default city parameters -- Default city parameters
DefaultMapWidth = 64 DefaultMapWidth = 64
DefaultMapHeight = 32 DefaultMapHeight = 32
--DefaultCityFile = "assets/cities/example1.city" --DefaultCityFile = "assets/cities/example1.city"
-- Textures used by various game systems -- Textures used by various game systems
Textures = {} Textures = {}
Textures[1] = {"Grass", "assets/system/grass1.png"} Textures[1] = {"Grass", "assets/system/grass1.png"}
Textures[2] = {"AllRoads", "assets/system/roads4.png"} Textures[2] = {"AllRoads", "assets/system/roads4.png"}
Textures[3] = {"Water", "assets/system/water1.png"} Textures[3] = {"Water", "assets/system/water1.png"}
Textures[4] = {"Clouds", "assets/system/clouds2.png"} Textures[4] = {"Clouds", "assets/system/clouds2.png"}
Textures[5] = {"WaterSide", "assets/system/waterside1.png"} Textures[5] = {"WaterSide", "assets/system/waterside1.png"}
Textures[6] = {"Smoke", "assets/system/skidsmoke1.png"} Textures[6] = {"Smoke", "assets/system/skidsmoke1.png"}
-- Buildings -- Buildings
Buildings = {} Buildings = {}
Buildings[1] = {"javidx9", "UnitBuilding_1", "assets/buildings/unit_building.obj", "", Buildings[1] = {"javidx9", "UnitBuilding_1", "assets/buildings/unit_building.obj", "",
0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0} 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0}
Buildings[2] = {"UDXS", "Apartments_1", "assets/buildings/udxs_building1.obj", "assets/buildings/udxs_building1.png", Buildings[2] = {"UDXS", "Apartments_1", "assets/buildings/udxs_building1.obj", "assets/buildings/udxs_building1.png",
0.0, 0.0, 0.0, 1.0, 1.0, 0.5, 1.0, 1.0, 0.0} 0.0, 0.0, 0.0, 1.0, 1.0, 0.5, 1.0, 1.0, 0.0}
Vehicles = {} Vehicles = {}
Vehicles[1] = {"JustinRM", "Sedan", "assets/vehicles/CarCrime_Sedan.obj", "assets/vehicles/CarTex_256.png", Vehicles[1] = {"JustinRM", "Sedan", "assets/vehicles/CarCrime_Sedan.obj", "assets/vehicles/CarTex_256.png",
0.0, 0.0, 1.5708, 0.05, 0.05, 0.05, 0.0, 0.0, 0.0} 0.0, 0.0, 1.5708, 0.05, 0.05, 0.05, 0.0, 0.0, 0.0}
Vehicles[2] = {"JustinRM", "SUV", "assets/vehicles/CarCrime_SUV.obj", "assets/vehicles/CarTex_256.png", Vehicles[2] = {"JustinRM", "SUV", "assets/vehicles/CarCrime_SUV.obj", "assets/vehicles/CarTex_256.png",
0.0, 0.0, 0.0, 0.05, 0.05, 0.05, 0.0, 0.0, 0.0} 0.0, 0.0, 0.0, 0.05, 0.05, 0.05, 0.0, 0.0, 0.0}
Vehicles[3] = {"JustinRM", "TruckCab", "assets/vehicles/CarCrime_Truck_Cab.obj", "assets/vehicles/CarTex_256.png", Vehicles[3] = {"JustinRM", "TruckCab", "assets/vehicles/CarCrime_Truck_Cab.obj", "assets/vehicles/CarTex_256.png",
0.0, 0.0, 0.0, 0.05, 0.05, 0.05, 0.0, 0.0, 0.0} 0.0, 0.0, 0.0, 0.05, 0.05, 0.05, 0.0, 0.0, 0.0}
Vehicles[4] = {"JustinRM", "TruckTrailer", "assets/vehicles/CarCrime_Truck_Trailer.obj", "assets/vehicles/CarTex_256.png", Vehicles[4] = {"JustinRM", "TruckTrailer", "assets/vehicles/CarCrime_Truck_Trailer.obj", "assets/vehicles/CarTex_256.png",
0.0, 0.0, 0.0, 0.05, 0.05, 0.05, 0.0, 0.0, 0.0} 0.0, 0.0, 0.0, 0.05, 0.05, 0.05, 0.0, 0.0, 0.0}
Vehicles[5] = {"JustinRM", "UTE", "assets/vehicles/CarCrime_Ute.obj", "assets/vehicles/CarTex_256.png", Vehicles[5] = {"JustinRM", "UTE", "assets/vehicles/CarCrime_Ute.obj", "assets/vehicles/CarTex_256.png",
0.0, 0.0, 0.0, 0.05, 0.05, 0.05, 0.0, 0.0, 0.0} 0.0, 0.0, 0.0, 0.05, 0.05, 0.05, 0.0, 0.0, 0.0}
Vehicles[6] = {"JustinRM", "Wagon", "assets/vehicles/CarCrime_Wahon.obj", "assets/vehicles/CarTex_256.png", Vehicles[6] = {"JustinRM", "Wagon", "assets/vehicles/CarCrime_Wahon.obj", "assets/vehicles/CarTex_256.png",
0.0, 0.0, 0.0, 0.05, 0.05, 0.05, 0.0, 0.0, 0.0} 0.0, 0.0, 0.0, 0.05, 0.05, 0.05, 0.0, 0.0, 0.0}

Before

Width:  |  Height:  |  Size: 181 KiB

After

Width:  |  Height:  |  Size: 181 KiB

Before

Width:  |  Height:  |  Size: 284 KiB

After

Width:  |  Height:  |  Size: 284 KiB

Before

Width:  |  Height:  |  Size: 181 KiB

After

Width:  |  Height:  |  Size: 181 KiB

Before

Width:  |  Height:  |  Size: 482 KiB

After

Width:  |  Height:  |  Size: 482 KiB

Before

Width:  |  Height:  |  Size: 383 KiB

After

Width:  |  Height:  |  Size: 383 KiB

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 54 KiB

Before

Width:  |  Height:  |  Size: 691 KiB

After

Width:  |  Height:  |  Size: 691 KiB

Before

Width:  |  Height:  |  Size: 732 KiB

After

Width:  |  Height:  |  Size: 732 KiB

Before

Width:  |  Height:  |  Size: 616 KiB

After

Width:  |  Height:  |  Size: 616 KiB

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

@ -1,206 +1,206 @@
#include "cAutomata.h" #include "cAutomata.h"
cAuto_Node::cAuto_Node() cAuto_Node::cAuto_Node()
{ {
pos = { 0,0 }; pos = { 0,0 };
} }
cAuto_Node::cAuto_Node(const olc::vf2d &worldpos) cAuto_Node::cAuto_Node(const olc::vf2d &worldpos)
{ {
pos = worldpos; pos = worldpos;
} }
olc::vf2d cAuto_Track::GetPostion(float t, cAuto_Node *pStart) olc::vf2d cAuto_Track::GetPostion(float t, cAuto_Node *pStart)
{ {
// pStart indicates the node the automata first encounted this track // pStart indicates the node the automata first encounted this track
if (node[0] == pStart) if (node[0] == pStart)
{ {
return node[0]->pos + (node[1]->pos - node[0]->pos) * (t / fTrackLength); return node[0]->pos + (node[1]->pos - node[0]->pos) * (t / fTrackLength);
} }
else else
{ {
return node[1]->pos + (node[0]->pos - node[1]->pos) * (t / fTrackLength); return node[1]->pos + (node[0]->pos - node[1]->pos) * (t / fTrackLength);
} }
} }
cAuto_Body::cAuto_Body() cAuto_Body::cAuto_Body()
{ {
} }
cAuto_Body::~cAuto_Body() cAuto_Body::~cAuto_Body()
{ {
} }
void cAuto_Body::UpdateAuto(float fElapsedTime) void cAuto_Body::UpdateAuto(float fElapsedTime)
{ {
// Work out which node is the target destination // Work out which node is the target destination
cAuto_Node *pExitNode = pCurrentTrack->node[0]; cAuto_Node *pExitNode = pCurrentTrack->node[0];
if (pExitNode == pTrackOriginNode) if (pExitNode == pTrackOriginNode)
pExitNode = pCurrentTrack->node[1]; pExitNode = pCurrentTrack->node[1];
bool bAutomataCanMove = true; bool bAutomataCanMove = true;
float fDistanceToAutoInFront = 1.0f; float fDistanceToAutoInFront = 1.0f;
// First check if the vehicle overlaps with the one in front of it // First check if the vehicle overlaps with the one in front of it
// Get an iterator for this automata // Get an iterator for this automata
auto itThisAutomata = std::find(pCurrentTrack->listAutos.begin(), pCurrentTrack->listAutos.end(), this); auto itThisAutomata = std::find(pCurrentTrack->listAutos.begin(), pCurrentTrack->listAutos.end(), this);
// If this automata is at the front of this track segment // If this automata is at the front of this track segment
if (*itThisAutomata == pCurrentTrack->listAutos.front()) if (*itThisAutomata == pCurrentTrack->listAutos.front())
{ {
// Then check all the following track segments. Take the position of // Then check all the following track segments. Take the position of
// each vehicle at the back of the track segments auto list // each vehicle at the back of the track segments auto list
for (auto &track : pExitNode->listTracks) for (auto &track : pExitNode->listTracks)
{ {
if (track != pCurrentTrack && !track->listAutos.empty()) if (track != pCurrentTrack && !track->listAutos.empty())
{ {
// Get Auto at back // Get Auto at back
float fDistanceFromTrackStartToAutoRear = track->listAutos.back()->fAutoPos - track->listAutos.back()->fAutoLength; float fDistanceFromTrackStartToAutoRear = track->listAutos.back()->fAutoPos - track->listAutos.back()->fAutoLength;
if ((*itThisAutomata)->fAutoPos < (pCurrentTrack->fTrackLength + fDistanceFromTrackStartToAutoRear - fAutoLength)) if ((*itThisAutomata)->fAutoPos < (pCurrentTrack->fTrackLength + fDistanceFromTrackStartToAutoRear - fAutoLength))
{ {
// Move Automata along track, as there is space // Move Automata along track, as there is space
//bAutomataCanMove = true; //bAutomataCanMove = true;
fDistanceToAutoInFront = (pCurrentTrack->fTrackLength + fDistanceFromTrackStartToAutoRear - 0.1f) - (*itThisAutomata)->fAutoPos; fDistanceToAutoInFront = (pCurrentTrack->fTrackLength + fDistanceFromTrackStartToAutoRear - 0.1f) - (*itThisAutomata)->fAutoPos;
} }
else else
{ {
// No space, so do not move automata // No space, so do not move automata
bAutomataCanMove = false; bAutomataCanMove = false;
} }
} }
else else
{ {
// Track in front was empty, node is clear to pass through so // Track in front was empty, node is clear to pass through so
//bAutomataCanMove = true; //bAutomataCanMove = true;
} }
} }
} }
else else
{ {
// Get the automata in front // Get the automata in front
auto itAutomataInFront = itThisAutomata; auto itAutomataInFront = itThisAutomata;
itAutomataInFront--; itAutomataInFront--;
// If the distance between the front of the automata in front and the fornt of this automata // If the distance between the front of the automata in front and the fornt of this automata
// is greater than the length of the automata in front, then there is space for this automata // is greater than the length of the automata in front, then there is space for this automata
// to enter // to enter
if (fabs((*itAutomataInFront)->fAutoPos - (*itThisAutomata)->fAutoPos) > ((*itAutomataInFront)->fAutoLength + 0.1f)) if (fabs((*itAutomataInFront)->fAutoPos - (*itThisAutomata)->fAutoPos) > ((*itAutomataInFront)->fAutoLength + 0.1f))
{ {
// Move Automata along track // Move Automata along track
//bAutomataCanMove = true; //bAutomataCanMove = true;
fDistanceToAutoInFront = ((*itAutomataInFront)->fAutoPos - (*itAutomataInFront)->fAutoLength - 0.1f) - (*itThisAutomata)->fAutoPos; fDistanceToAutoInFront = ((*itAutomataInFront)->fAutoPos - (*itAutomataInFront)->fAutoLength - 0.1f) - (*itThisAutomata)->fAutoPos;
} }
else else
{ {
// No space, so do not move automata // No space, so do not move automata
bAutomataCanMove = false; bAutomataCanMove = false;
} }
} }
if (bAutomataCanMove) if (bAutomataCanMove)
{ {
if (fDistanceToAutoInFront > pCurrentTrack->fTrackLength) fDistanceToAutoInFront = pCurrentTrack->fTrackLength; if (fDistanceToAutoInFront > pCurrentTrack->fTrackLength) fDistanceToAutoInFront = pCurrentTrack->fTrackLength;
fAutoPos += fElapsedTime * std::max(fDistanceToAutoInFront, 1.0f) * (fAutoLength < 0.1f ? 0.3f : 0.5f); fAutoPos += fElapsedTime * std::max(fDistanceToAutoInFront, 1.0f) * (fAutoLength < 0.1f ? 0.3f : 0.5f);
} }
if (fAutoPos >= pCurrentTrack->fTrackLength) if (fAutoPos >= pCurrentTrack->fTrackLength)
{ {
// Automata has reached end of current track // Automata has reached end of current track
// Check if it can transition beyond node // Check if it can transition beyond node
if (!pExitNode->bBlock) if (!pExitNode->bBlock)
{ {
// It can, so reset position along track back to start // It can, so reset position along track back to start
fAutoPos -= pCurrentTrack->fTrackLength; fAutoPos -= pCurrentTrack->fTrackLength;
// Choose a track from the node not equal to this one, that has an unblocked exit node // Choose a track from the node not equal to this one, that has an unblocked exit node
// For now choose at random // For now choose at random
cAuto_Track *pNewTrack = nullptr; cAuto_Track *pNewTrack = nullptr;
if (pExitNode->listTracks.size() == 2) if (pExitNode->listTracks.size() == 2)
{ {
// Automata is travelling along straight joined sections, one of the // Automata is travelling along straight joined sections, one of the
// tracks is the track its just come in on, the other is the exit, so // tracks is the track its just come in on, the other is the exit, so
// choose the exit. // choose the exit.
auto it = pExitNode->listTracks.begin(); auto it = pExitNode->listTracks.begin();
pNewTrack = (*it); pNewTrack = (*it);
if (pCurrentTrack == pNewTrack) if (pCurrentTrack == pNewTrack)
{ {
++it; ++it;
pNewTrack = (*it); pNewTrack = (*it);
} }
} }
else else
{ {
// Automata has reached a junction with several exits // Automata has reached a junction with several exits
while (pNewTrack == nullptr) while (pNewTrack == nullptr)
{ {
int i = rand() % pExitNode->listTracks.size(); int i = rand() % pExitNode->listTracks.size();
int j = 0; int j = 0;
for (auto it = pExitNode->listTracks.begin(); it != pExitNode->listTracks.end(); ++it) for (auto it = pExitNode->listTracks.begin(); it != pExitNode->listTracks.end(); ++it)
{ {
cAuto_Track* track = (*it); cAuto_Track* track = (*it);
// Work out which node is the target destination // Work out which node is the target destination
cAuto_Node *pNewExitNode = track->node[0]; cAuto_Node *pNewExitNode = track->node[0];
if (pNewExitNode == pExitNode) if (pNewExitNode == pExitNode)
pNewExitNode = track->node[1]; pNewExitNode = track->node[1];
if (j == i && track != pCurrentTrack && !pNewExitNode->bBlock /*((*it)->cell != pCurrentTrack->cell)*/) if (j == i && track != pCurrentTrack && !pNewExitNode->bBlock /*((*it)->cell != pCurrentTrack->cell)*/)
{ {
pNewTrack = track; pNewTrack = track;
break; break;
} }
j++; j++;
} }
} }
} }
// Change to new track, the origin node of the next // Change to new track, the origin node of the next
// track is the same as the exit node to the current track // track is the same as the exit node to the current track
pTrackOriginNode = pExitNode; pTrackOriginNode = pExitNode;
// Remove the automata from the front of the queue // Remove the automata from the front of the queue
// on the current track // on the current track
pCurrentTrack->listAutos.pop_front(); pCurrentTrack->listAutos.pop_front();
// Switch the automatas track link to the new track // Switch the automatas track link to the new track
pCurrentTrack = pNewTrack; pCurrentTrack = pNewTrack;
// Push the automata onto the back of the new track queue // Push the automata onto the back of the new track queue
pCurrentTrack->listAutos.push_back(this); pCurrentTrack->listAutos.push_back(this);
} }
else else
{ {
// It cant pass the node, so clamp automata at this location // It cant pass the node, so clamp automata at this location
fAutoPos = pCurrentTrack->fTrackLength; fAutoPos = pCurrentTrack->fTrackLength;
} }
} }
else else
{ {
// Automata is travelling // Automata is travelling
vAutoPos = pCurrentTrack->GetPostion(fAutoPos, pTrackOriginNode); vAutoPos = pCurrentTrack->GetPostion(fAutoPos, pTrackOriginNode);
} }
} }

@ -1,107 +1,107 @@
/* /*
Top Down City Based Car Crime Game - Part #2 Top Down City Based Car Crime Game - Part #2
"Colin, I hope you're shooting 600+ wherever you are buddy. RIP." - javidx9 "Colin, I hope you're shooting 600+ wherever you are buddy. RIP." - javidx9
License (OLC-3) License (OLC-3)
~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~
Copyright 2018-2019 OneLoneCoder.com Copyright 2018-2019 OneLoneCoder.com
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions modification, are permitted provided that the following conditions
are met: are met:
1. Redistributions or derivations of source code must retain the above 1. Redistributions or derivations of source code must retain the above
copyright notice, this list of conditions and the following disclaimer. copyright notice, this list of conditions and the following disclaimer.
2. Redistributions or derivative works in binary form must reproduce 2. Redistributions or derivative works in binary form must reproduce
the above copyright notice. This list of conditions and the following the above copyright notice. This list of conditions and the following
disclaimer must be reproduced in the documentation and/or other disclaimer must be reproduced in the documentation and/or other
materials provided with the distribution. materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its 3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived contributors may be used to endorse or promote products derived
from this software without specific prior written permission. from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Instructions: Instructions:
~~~~~~~~~~~~~ ~~~~~~~~~~~~~
Scroll with middle mouse wheel, TAB toggle edit mode, R to place road Scroll with middle mouse wheel, TAB toggle edit mode, R to place road
P to place pavement, Q to place building, Arrow keys to drive car P to place pavement, Q to place building, Arrow keys to drive car
Relevant Video: https://youtu.be/fIV6P1W-wuo Relevant Video: https://youtu.be/fIV6P1W-wuo
Links Links
~~~~~ ~~~~~
YouTube: https://www.youtube.com/javidx9 YouTube: https://www.youtube.com/javidx9
https://www.youtube.com/javidx9extra https://www.youtube.com/javidx9extra
Discord: https://discord.gg/WhwHUMV Discord: https://discord.gg/WhwHUMV
Twitter: https://www.twitter.com/javidx9 Twitter: https://www.twitter.com/javidx9
Twitch: https://www.twitch.tv/javidx9 Twitch: https://www.twitch.tv/javidx9
GitHub: https://www.github.com/onelonecoder GitHub: https://www.github.com/onelonecoder
Patreon: https://www.patreon.com/javidx9 Patreon: https://www.patreon.com/javidx9
Homepage: https://www.onelonecoder.com Homepage: https://www.onelonecoder.com
Author Author
~~~~~~ ~~~~~~
David Barr, aka javidx9, ©OneLoneCoder 2019 David Barr, aka javidx9, ©OneLoneCoder 2019
*/ */
#pragma once #pragma once
#include "olcPixelGameEngine.h" #include "olcPixelGameEngine.h"
class cAuto_Track; class cAuto_Track;
class cAuto_Node; class cAuto_Node;
class cAuto_Body; class cAuto_Body;
class cCell; class cCell;
class cAuto_Node class cAuto_Node
{ {
public: public:
cAuto_Node(); cAuto_Node();
cAuto_Node(const olc::vf2d &worldpos); cAuto_Node(const olc::vf2d &worldpos);
olc::vf2d pos; olc::vf2d pos;
bool bBlock = false; bool bBlock = false;
std::list<cAuto_Track*> listTracks; std::list<cAuto_Track*> listTracks;
}; };
class cAuto_Track class cAuto_Track
{ {
public: public:
cAuto_Node* node[2]; // Two end nodes cAuto_Node* node[2]; // Two end nodes
cCell* cell; // Pointer to host cell cCell* cell; // Pointer to host cell
olc::vf2d GetPostion(float t, cAuto_Node *pstart); olc::vf2d GetPostion(float t, cAuto_Node *pstart);
std::list<cAuto_Body*> listAutos; std::list<cAuto_Body*> listAutos;
float fTrackLength = 1.0f; float fTrackLength = 1.0f;
}; };
class cAuto_Body class cAuto_Body
{ {
public: public:
cAuto_Body(); cAuto_Body();
~cAuto_Body(); ~cAuto_Body();
public: public:
void UpdateAuto(float fElapsedTime); void UpdateAuto(float fElapsedTime);
public: public:
olc::vf2d vAutoPos = { 0.0f, 0.0f }; olc::vf2d vAutoPos = { 0.0f, 0.0f };
float fAutoPos = 0.0f; // Location of automata along track float fAutoPos = 0.0f; // Location of automata along track
float fAutoLength = 0.0f; // Physical length of automata float fAutoLength = 0.0f; // Physical length of automata
cAuto_Track *pCurrentTrack = nullptr; cAuto_Track *pCurrentTrack = nullptr;
cAuto_Node *pTrackOriginNode = nullptr; cAuto_Node *pTrackOriginNode = nullptr;
}; };

@ -1,289 +1,289 @@
/* /*
Top Down City Based Car Crime Game - Part #2 Top Down City Based Car Crime Game - Part #2
"Colin, I hope you're shooting 600+ wherever you are buddy. RIP." - javidx9 "Colin, I hope you're shooting 600+ wherever you are buddy. RIP." - javidx9
License (OLC-3) License (OLC-3)
~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~
Copyright 2018-2019 OneLoneCoder.com Copyright 2018-2019 OneLoneCoder.com
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions modification, are permitted provided that the following conditions
are met: are met:
1. Redistributions or derivations of source code must retain the above 1. Redistributions or derivations of source code must retain the above
copyright notice, this list of conditions and the following disclaimer. copyright notice, this list of conditions and the following disclaimer.
2. Redistributions or derivative works in binary form must reproduce 2. Redistributions or derivative works in binary form must reproduce
the above copyright notice. This list of conditions and the following the above copyright notice. This list of conditions and the following
disclaimer must be reproduced in the documentation and/or other disclaimer must be reproduced in the documentation and/or other
materials provided with the distribution. materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its 3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived contributors may be used to endorse or promote products derived
from this software without specific prior written permission. from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Instructions: Instructions:
~~~~~~~~~~~~~ ~~~~~~~~~~~~~
Scroll with middle mouse wheel, TAB toggle edit mode, R to place road Scroll with middle mouse wheel, TAB toggle edit mode, R to place road
P to place pavement, Q to place building, Arrow keys to drive car P to place pavement, Q to place building, Arrow keys to drive car
Relevant Video: https://youtu.be/fIV6P1W-wuo Relevant Video: https://youtu.be/fIV6P1W-wuo
Links Links
~~~~~ ~~~~~
YouTube: https://www.youtube.com/javidx9 YouTube: https://www.youtube.com/javidx9
https://www.youtube.com/javidx9extra https://www.youtube.com/javidx9extra
Discord: https://discord.gg/WhwHUMV Discord: https://discord.gg/WhwHUMV
Twitter: https://www.twitter.com/javidx9 Twitter: https://www.twitter.com/javidx9
Twitch: https://www.twitch.tv/javidx9 Twitch: https://www.twitch.tv/javidx9
GitHub: https://www.github.com/onelonecoder GitHub: https://www.github.com/onelonecoder
Patreon: https://www.patreon.com/javidx9 Patreon: https://www.patreon.com/javidx9
Homepage: https://www.onelonecoder.com Homepage: https://www.onelonecoder.com
Author Author
~~~~~~ ~~~~~~
David Barr, aka javidx9, ©OneLoneCoder 2019 David Barr, aka javidx9, ©OneLoneCoder 2019
*/ */
#pragma once #pragma once
#include "olcPixelGameEngine.h" #include "olcPixelGameEngine.h"
#include "olcPGEX_Graphics3D.h" #include "olcPGEX_Graphics3D.h"
#include "cGameSettings.h" #include "cGameSettings.h"
#include "cCityMap.h" #include "cCityMap.h"
#include <vector> #include <vector>
#include <unordered_set> #include <unordered_set>
struct sSmokeDecal struct sSmokeDecal
{ {
float fLifetime = 0.1f; float fLifetime = 0.1f;
olc::GFX3D::vec3d pos; olc::GFX3D::vec3d pos;
}; };
class cCarCrimeCity : public olc::PixelGameEngine class cCarCrimeCity : public olc::PixelGameEngine
{ {
public: public:
cCarCrimeCity(); cCarCrimeCity();
~cCarCrimeCity(); ~cCarCrimeCity();
private: private:
bool OnUserCreate() override; bool OnUserCreate() override;
bool OnUserUpdate(float fElapsedTime) override; bool OnUserUpdate(float fElapsedTime) override;
bool OnUserDestroy() override; bool OnUserDestroy() override;
private: private:
class cGameObjectQuad class cGameObjectQuad
{ {
public: public:
cGameObjectQuad(float w, float h) cGameObjectQuad(float w, float h)
{ {
fWidth = w; fWidth = w;
fHeight = h; fHeight = h;
fAngle = 0.0f; fAngle = 0.0f;
// Construct Model Quad Geometry // Construct Model Quad Geometry
vecPointsModel = { {-fWidth / 2.0f, -fHeight / 2.0f, -0.01f, 1.0f}, vecPointsModel = { {-fWidth / 2.0f, -fHeight / 2.0f, -0.01f, 1.0f},
{-fWidth / 2.0f, +fHeight / 2.0f, -0.01f, 1.0f}, {-fWidth / 2.0f, +fHeight / 2.0f, -0.01f, 1.0f},
{+fWidth / 2.0f, +fHeight / 2.0f, -0.01f, 1.0f}, {+fWidth / 2.0f, +fHeight / 2.0f, -0.01f, 1.0f},
{+fWidth / 2.0f, -fHeight / 2.0f, -0.01f, 1.0f} }; {+fWidth / 2.0f, -fHeight / 2.0f, -0.01f, 1.0f} };
vecPointsWorld.resize(vecPointsModel.size()); vecPointsWorld.resize(vecPointsModel.size());
TransformModelToWorld(); TransformModelToWorld();
} }
void TransformModelToWorld() void TransformModelToWorld()
{ {
for (size_t i = 0; i < vecPointsModel.size(); ++i) for (size_t i = 0; i < vecPointsModel.size(); ++i)
{ {
vecPointsWorld[i] = { vecPointsWorld[i] = {
(vecPointsModel[i].x * cosf(fAngle)) - (vecPointsModel[i].y * sinf(fAngle)) + pos.x, (vecPointsModel[i].x * cosf(fAngle)) - (vecPointsModel[i].y * sinf(fAngle)) + pos.x,
(vecPointsModel[i].x * sinf(fAngle)) + (vecPointsModel[i].y * cosf(fAngle)) + pos.y, (vecPointsModel[i].x * sinf(fAngle)) + (vecPointsModel[i].y * cosf(fAngle)) + pos.y,
vecPointsModel[i].z, vecPointsModel[i].z,
vecPointsModel[i].w vecPointsModel[i].w
}; };
} }
} }
std::vector<olc::GFX3D::triangle> GetTriangles() std::vector<olc::GFX3D::triangle> GetTriangles()
{ {
// Return triangles based upon this quad // Return triangles based upon this quad
return return
{ {
{vecPointsWorld[0], vecPointsWorld[1], vecPointsWorld[2], 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, olc::RED}, {vecPointsWorld[0], vecPointsWorld[1], vecPointsWorld[2], 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, olc::RED},
{vecPointsWorld[0], vecPointsWorld[2], vecPointsWorld[3], 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, olc::RED}, {vecPointsWorld[0], vecPointsWorld[2], vecPointsWorld[3], 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, olc::RED},
}; };
} }
// Use rectangle edge intersections. // Use rectangle edge intersections.
bool StaticCollisionWith(cGameObjectQuad &r2, bool bResolveStatic = false) bool StaticCollisionWith(cGameObjectQuad &r2, bool bResolveStatic = false)
{ {
struct vec2d { float x; float y; }; struct vec2d { float x; float y; };
bool bCollision = false; bool bCollision = false;
// Check diagonals of R1 against edges of R2 // Check diagonals of R1 against edges of R2
for (size_t p = 0; p < vecPointsWorld.size(); p++) for (size_t p = 0; p < vecPointsWorld.size(); p++)
{ {
vec2d line_r1s = { pos.x, pos.y }; vec2d line_r1s = { pos.x, pos.y };
vec2d line_r1e = { vecPointsWorld[p].x, vecPointsWorld[p].y }; vec2d line_r1e = { vecPointsWorld[p].x, vecPointsWorld[p].y };
vec2d displacement = { 0,0 }; vec2d displacement = { 0,0 };
for (size_t q = 0; q < r2.vecPointsWorld.size(); q++) for (size_t q = 0; q < r2.vecPointsWorld.size(); q++)
{ {
vec2d line_r2s = { r2.vecPointsWorld[q].x, r2.vecPointsWorld[q].y }; vec2d line_r2s = { r2.vecPointsWorld[q].x, r2.vecPointsWorld[q].y };
vec2d line_r2e = { r2.vecPointsWorld[(q + 1) % r2.vecPointsWorld.size()].x, r2.vecPointsWorld[(q + 1) % r2.vecPointsWorld.size()].y }; vec2d line_r2e = { r2.vecPointsWorld[(q + 1) % r2.vecPointsWorld.size()].x, r2.vecPointsWorld[(q + 1) % r2.vecPointsWorld.size()].y };
// Standard "off the shelf" line segment intersection // Standard "off the shelf" line segment intersection
float h = (line_r2e.x - line_r2s.x) * (line_r1s.y - line_r1e.y) - (line_r1s.x - line_r1e.x) * (line_r2e.y - line_r2s.y); float h = (line_r2e.x - line_r2s.x) * (line_r1s.y - line_r1e.y) - (line_r1s.x - line_r1e.x) * (line_r2e.y - line_r2s.y);
float t1 = ((line_r2s.y - line_r2e.y) * (line_r1s.x - line_r2s.x) + (line_r2e.x - line_r2s.x) * (line_r1s.y - line_r2s.y)) / h; float t1 = ((line_r2s.y - line_r2e.y) * (line_r1s.x - line_r2s.x) + (line_r2e.x - line_r2s.x) * (line_r1s.y - line_r2s.y)) / h;
float t2 = ((line_r1s.y - line_r1e.y) * (line_r1s.x - line_r2s.x) + (line_r1e.x - line_r1s.x) * (line_r1s.y - line_r2s.y)) / h; float t2 = ((line_r1s.y - line_r1e.y) * (line_r1s.x - line_r2s.x) + (line_r1e.x - line_r1s.x) * (line_r1s.y - line_r2s.y)) / h;
if (t1 >= 0.0f && t1 <= 1.0f && t2 >= 0.0f && t2 <= 1.0f) if (t1 >= 0.0f && t1 <= 1.0f && t2 >= 0.0f && t2 <= 1.0f)
{ {
if (bResolveStatic) if (bResolveStatic)
{ {
displacement.x += (1.0f - t1) * (line_r1e.x - line_r1s.x); displacement.x += (1.0f - t1) * (line_r1e.x - line_r1s.x);
displacement.y += (1.0f - t1) * (line_r1e.y - line_r1s.y); displacement.y += (1.0f - t1) * (line_r1e.y - line_r1s.y);
bCollision = true; bCollision = true;
} }
else else
return true; return true;
} }
} }
pos.x -= displacement.x; pos.x -= displacement.x;
pos.y -= displacement.y; pos.y -= displacement.y;
} }
// Check diagonals of R2 against edges of R1 // Check diagonals of R2 against edges of R1
for (size_t p = 0; p < r2.vecPointsWorld.size(); p++) for (size_t p = 0; p < r2.vecPointsWorld.size(); p++)
{ {
vec2d line_r1s = { r2.pos.x, r2.pos.y }; vec2d line_r1s = { r2.pos.x, r2.pos.y };
vec2d line_r1e = { r2.vecPointsWorld[p].x, r2.vecPointsWorld[p].y }; vec2d line_r1e = { r2.vecPointsWorld[p].x, r2.vecPointsWorld[p].y };
vec2d displacement = { 0,0 }; vec2d displacement = { 0,0 };
for (size_t q = 0; q < vecPointsWorld.size(); q++) for (size_t q = 0; q < vecPointsWorld.size(); q++)
{ {
vec2d line_r2s = { vecPointsWorld[q].x, vecPointsWorld[q].y }; vec2d line_r2s = { vecPointsWorld[q].x, vecPointsWorld[q].y };
vec2d line_r2e = { vecPointsWorld[(q + 1) % vecPointsWorld.size()].x, vecPointsWorld[(q + 1) % vecPointsWorld.size()].y }; vec2d line_r2e = { vecPointsWorld[(q + 1) % vecPointsWorld.size()].x, vecPointsWorld[(q + 1) % vecPointsWorld.size()].y };
// Standard "off the shelf" line segment intersection // Standard "off the shelf" line segment intersection
float h = (line_r2e.x - line_r2s.x) * (line_r1s.y - line_r1e.y) - (line_r1s.x - line_r1e.x) * (line_r2e.y - line_r2s.y); float h = (line_r2e.x - line_r2s.x) * (line_r1s.y - line_r1e.y) - (line_r1s.x - line_r1e.x) * (line_r2e.y - line_r2s.y);
float t1 = ((line_r2s.y - line_r2e.y) * (line_r1s.x - line_r2s.x) + (line_r2e.x - line_r2s.x) * (line_r1s.y - line_r2s.y)) / h; float t1 = ((line_r2s.y - line_r2e.y) * (line_r1s.x - line_r2s.x) + (line_r2e.x - line_r2s.x) * (line_r1s.y - line_r2s.y)) / h;
float t2 = ((line_r1s.y - line_r1e.y) * (line_r1s.x - line_r2s.x) + (line_r1e.x - line_r1s.x) * (line_r1s.y - line_r2s.y)) / h; float t2 = ((line_r1s.y - line_r1e.y) * (line_r1s.x - line_r2s.x) + (line_r1e.x - line_r1s.x) * (line_r1s.y - line_r2s.y)) / h;
if (t1 >= 0.0f && t1 <= 1.0f && t2 >= 0.0f && t2 <= 1.0f) if (t1 >= 0.0f && t1 <= 1.0f && t2 >= 0.0f && t2 <= 1.0f)
{ {
if (bResolveStatic) if (bResolveStatic)
{ {
displacement.x += (1.0f - t1) * (line_r1e.x - line_r1s.x); displacement.x += (1.0f - t1) * (line_r1e.x - line_r1s.x);
displacement.y += (1.0f - t1) * (line_r1e.y - line_r1s.y); displacement.y += (1.0f - t1) * (line_r1e.y - line_r1s.y);
bCollision = true; bCollision = true;
} }
else else
return true; return true;
} }
} }
pos.x += displacement.x; pos.x += displacement.x;
pos.y += displacement.y; pos.y += displacement.y;
} }
return bCollision; return bCollision;
} }
std::vector<olc::GFX3D::triangle> meshTris; std::vector<olc::GFX3D::triangle> meshTris;
std::vector<olc::GFX3D::vec3d> vecPointsModel; std::vector<olc::GFX3D::vec3d> vecPointsModel;
std::vector<olc::GFX3D::vec3d> vecPointsWorld; std::vector<olc::GFX3D::vec3d> vecPointsWorld;
olc::GFX3D::vec3d pos; olc::GFX3D::vec3d pos;
float fWidth; float fWidth;
float fHeight; float fHeight;
float fOriginX; float fOriginX;
float fOriginY; float fOriginY;
float fAngle; float fAngle;
}; };
bool LoadAssets(); bool LoadAssets();
std::map<std::string, olc::Sprite*> mapAssetTextures; std::map<std::string, olc::Sprite*> mapAssetTextures;
std::map<std::string, olc::GFX3D::mesh*> mapAssetMeshes; std::map<std::string, olc::GFX3D::mesh*> mapAssetMeshes;
std::map<std::string, olc::GFX3D::mat4x4> mapAssetTransform; std::map<std::string, olc::GFX3D::mat4x4> mapAssetTransform;
// Camera variables // Camera variables
olc::GFX3D::vec3d vCamera = { 0.0f, 0.0f, -3.0f }; olc::GFX3D::vec3d vCamera = { 0.0f, 0.0f, -3.0f };
olc::GFX3D::vec3d vUp = { 0.0f, 1.0f, 0.0f }; olc::GFX3D::vec3d vUp = { 0.0f, 1.0f, 0.0f };
olc::GFX3D::vec3d vEye = { 0.0f, 0.0f, -3.0f }; olc::GFX3D::vec3d vEye = { 0.0f, 0.0f, -3.0f };
olc::GFX3D::vec3d vLookDir = { 0.0f, 0.0f, 1.0f }; olc::GFX3D::vec3d vLookDir = { 0.0f, 0.0f, 1.0f };
// Ray Casting Parameters // Ray Casting Parameters
olc::vf2d viewWorldTopLeft; olc::vf2d viewWorldTopLeft;
olc::vf2d viewWorldBottomRight; olc::vf2d viewWorldBottomRight;
// Cloud movement variables // Cloud movement variables
float fCloudOffsetX = 0.0f; float fCloudOffsetX = 0.0f;
float fCloudOffsetY = 0.0f; float fCloudOffsetY = 0.0f;
// Mouse Control // Mouse Control
olc::vf2d vOffset = { 0.0f, 0.0f }; olc::vf2d vOffset = { 0.0f, 0.0f };
olc::vf2d vStartPan = { 0.0f, 0.0f }; olc::vf2d vStartPan = { 0.0f, 0.0f };
olc::vf2d vMouseOnGround = { 0.0f, 0.0f }; olc::vf2d vMouseOnGround = { 0.0f, 0.0f };
float fScale = 1.0f; float fScale = 1.0f;
olc::vf2d GetMouseOnGround(const olc::vf2d &vMouseScreen); olc::vf2d GetMouseOnGround(const olc::vf2d &vMouseScreen);
//cVehicle car; //cVehicle car;
olc::vf2d carvel; olc::vf2d carvel;
olc::vf2d carpos; olc::vf2d carpos;
float fSpeed = 0.0f; float fSpeed = 0.0f;
float fAngle = 0.0f; float fAngle = 0.0f;
std::list<cAuto_Body*> listAutomata; // Holds all automata, note its a pointer because we use polymorphism std::list<cAuto_Body*> listAutomata; // Holds all automata, note its a pointer because we use polymorphism
void SpawnPedestrian(int x, int y); void SpawnPedestrian(int x, int y);
void SpawnVehicle(int x, int y); void SpawnVehicle(int x, int y);
//cGameObjectQuad *goCar = nullptr; //cGameObjectQuad *goCar = nullptr;
//cGameObjectQuad *goObstacle = nullptr; //cGameObjectQuad *goObstacle = nullptr;
//std::vector<cGameObjectQuad> vecObstacles; //std::vector<cGameObjectQuad> vecObstacles;
cCityMap *pCity = nullptr; cCityMap *pCity = nullptr;
float fGlobalTime = 0.0f; float fGlobalTime = 0.0f;
// Editing Utilities // Editing Utilities
bool bEditMode = true; bool bEditMode = true;
int nMouseX = 0; int nMouseX = 0;
int nMouseY = 0; int nMouseY = 0;
struct sCellLoc { int x, y; }; struct sCellLoc { int x, y; };
std::unordered_set<int> setSelectedCells; std::unordered_set<int> setSelectedCells;
//std::list<sSmokeDecal> listDecalSmoke; //std::list<sSmokeDecal> listDecalSmoke;
//int nTrafficState = 0; //int nTrafficState = 0;
void DoEditMode(float fElapsedTime); void DoEditMode(float fElapsedTime);
}; };

@ -1,121 +1,121 @@
#include "cCell.h" #include "cCell.h"
#include "cCityMap.h" #include "cCityMap.h"
#include "olcPixelGameEngine.h" #include "olcPixelGameEngine.h"
#include <map> #include <map>
cCell::cCell() cCell::cCell()
{ {
} }
cCell::~cCell() cCell::~cCell()
{ {
// Cells own a list of automata navigation tracks // Cells own a list of automata navigation tracks
// but this will be destroyed when the cell is deleted // but this will be destroyed when the cell is deleted
} }
cCell::cCell(cCityMap* map, int x, int y) cCell::cCell(cCityMap* map, int x, int y)
{ {
pMap = map; pMap = map;
nWorldX = x; nWorldX = x;
nWorldY = y; nWorldY = y;
nCellType = CELL_BLANK; nCellType = CELL_BLANK;
// Connect internal nodes // Connect internal nodes
for (int i = 0; i < 49; i++) for (int i = 0; i < 49; i++)
pNaviNodes[i] = pMap->GetAutoNodeBase(x, y) + i; pNaviNodes[i] = pMap->GetAutoNodeBase(x, y) + i;
// Link cell into maps node pool // Link cell into maps node pool
if (y > 0) if (y > 0)
{ {
for (int i = 0; i < 7; i++) for (int i = 0; i < 7; i++)
pNaviNodes[i] = pMap->GetAutoNodeBase(x, y - 1) + 42 + i; pNaviNodes[i] = pMap->GetAutoNodeBase(x, y - 1) + 42 + i;
} }
else else
{ {
for (int i = 0; i < 7; i++) for (int i = 0; i < 7; i++)
pNaviNodes[i] = nullptr; pNaviNodes[i] = nullptr;
} }
if (x > 0) if (x > 0)
{ {
// Link West side // Link West side
for (int i = 0; i < 7; i++) for (int i = 0; i < 7; i++)
pNaviNodes[i * 7] = pMap->GetAutoNodeBase(x - 1, y) + 6 + i * 7; pNaviNodes[i * 7] = pMap->GetAutoNodeBase(x - 1, y) + 6 + i * 7;
} }
else else
{ {
for (int i = 0; i < 7; i++) for (int i = 0; i < 7; i++)
pNaviNodes[i * 7] = nullptr; pNaviNodes[i * 7] = nullptr;
} }
// South Side // South Side
if (y < pMap->GetHeight() - 1) if (y < pMap->GetHeight() - 1)
{ {
} }
else else
{ {
for (int i = 0; i < 7; i++) for (int i = 0; i < 7; i++)
pNaviNodes[42 + i] = nullptr; pNaviNodes[42 + i] = nullptr;
} }
// East Side // East Side
if (x < pMap->GetWidth() - 1) if (x < pMap->GetWidth() - 1)
{ {
} }
else else
{ {
for (int i = 0; i < 7; i++) for (int i = 0; i < 7; i++)
pNaviNodes[6 + i * 7] = nullptr; pNaviNodes[6 + i * 7] = nullptr;
} }
// Unused Nodes // Unused Nodes
pNaviNodes[9] = nullptr; pNaviNodes[9] = nullptr;
pNaviNodes[11] = nullptr; pNaviNodes[11] = nullptr;
pNaviNodes[15] = nullptr; pNaviNodes[15] = nullptr;
pNaviNodes[19] = nullptr; pNaviNodes[19] = nullptr;
pNaviNodes[29] = nullptr; pNaviNodes[29] = nullptr;
pNaviNodes[33] = nullptr; pNaviNodes[33] = nullptr;
pNaviNodes[37] = nullptr; pNaviNodes[37] = nullptr;
pNaviNodes[39] = nullptr; pNaviNodes[39] = nullptr;
pNaviNodes[0] = nullptr; pNaviNodes[0] = nullptr;
pNaviNodes[6] = nullptr; pNaviNodes[6] = nullptr;
pNaviNodes[42] = nullptr; pNaviNodes[42] = nullptr;
pNaviNodes[48] = nullptr; pNaviNodes[48] = nullptr;
} }
bool cCell::LinkAssets(std::map<std::string, olc::Sprite*> &mapTextures, std::map<std::string, olc::GFX3D::mesh*> &mapMesh, std::map<std::string, olc::GFX3D::mat4x4> &mapTransforms) bool cCell::LinkAssets(std::map<std::string, olc::Sprite*> &mapTextures, std::map<std::string, olc::GFX3D::mesh*> &mapMesh, std::map<std::string, olc::GFX3D::mat4x4> &mapTransforms)
{ {
return false; return false;
} }
bool cCell::Update(float fElapsedTime) bool cCell::Update(float fElapsedTime)
{ {
return false; return false;
} }
bool cCell::DrawBase(olc::PixelGameEngine *pge, olc::GFX3D::PipeLine &pipe) bool cCell::DrawBase(olc::PixelGameEngine *pge, olc::GFX3D::PipeLine &pipe)
{ {
return false; return false;
} }
bool cCell::DrawAlpha(olc::PixelGameEngine *pge, olc::GFX3D::PipeLine &pipe) bool cCell::DrawAlpha(olc::PixelGameEngine *pge, olc::GFX3D::PipeLine &pipe)
{ {
return false; return false;
} }
bool cCell::DrawDebug(olc::PixelGameEngine *pge, olc::GFX3D::PipeLine &pipe) bool cCell::DrawDebug(olc::PixelGameEngine *pge, olc::GFX3D::PipeLine &pipe)
{ {
return false; return false;
} }
void cCell::CalculateAdjacency() void cCell::CalculateAdjacency()
{ {
} }

@ -1,60 +1,60 @@
#pragma once #pragma once
#include <map> #include <map>
#include "olcPixelGameEngine.h" #include "olcPixelGameEngine.h"
#include "olcPGEX_Graphics3D.h" #include "olcPGEX_Graphics3D.h"
#include "cAutomata.h" #include "cAutomata.h"
class cCityMap; class cCityMap;
enum CellType enum CellType
{ {
CELL_BLANK, CELL_BLANK,
CELL_GRASS, CELL_GRASS,
CELL_CONCRETE, CELL_CONCRETE,
CELL_WATER, CELL_WATER,
CELL_BUILDING, CELL_BUILDING,
CELL_ROAD, CELL_ROAD,
CELL_PAVEMENT, CELL_PAVEMENT,
}; };
class cCell class cCell
{ {
public: public:
cCell(); cCell();
cCell(cCityMap* map, int x, int y); cCell(cCityMap* map, int x, int y);
~cCell(); ~cCell();
protected: protected:
cCityMap* pMap = nullptr; cCityMap* pMap = nullptr;
public: public:
int nWorldX = 0; int nWorldX = 0;
int nWorldY = 0; int nWorldY = 0;
bool bSolid = false; bool bSolid = false;
CellType nCellType = CELL_BLANK; CellType nCellType = CELL_BLANK;
// This cell may actuall be occupied by a multi-cell body // This cell may actuall be occupied by a multi-cell body
// so this pointer points to the host cell that contains // so this pointer points to the host cell that contains
// that body // that body
cCell* pHostCell = nullptr; cCell* pHostCell = nullptr;
// Each cell links to 20 automata transport nodes, 5 on each side // Each cell links to 20 automata transport nodes, 5 on each side
cAuto_Node* pNaviNodes[49]; cAuto_Node* pNaviNodes[49];
// Each cell can have a number of automata transport tracks, it owns them // Each cell can have a number of automata transport tracks, it owns them
// These connect nodes together as determined by the cell // These connect nodes together as determined by the cell
std::list<cAuto_Track> listTracks; std::list<cAuto_Track> listTracks;
public: public:
virtual void CalculateAdjacency(); virtual void CalculateAdjacency();
virtual bool LinkAssets(std::map<std::string, olc::Sprite*> &mapTextures, std::map<std::string, olc::GFX3D::mesh*> &mapMesh, std::map<std::string, olc::GFX3D::mat4x4> &mapTransforms); virtual bool LinkAssets(std::map<std::string, olc::Sprite*> &mapTextures, std::map<std::string, olc::GFX3D::mesh*> &mapMesh, std::map<std::string, olc::GFX3D::mat4x4> &mapTransforms);
virtual bool Update(float fElapsedTime); virtual bool Update(float fElapsedTime);
virtual bool DrawBase(olc::PixelGameEngine *pge, olc::GFX3D::PipeLine &pipe); virtual bool DrawBase(olc::PixelGameEngine *pge, olc::GFX3D::PipeLine &pipe);
virtual bool DrawAlpha(olc::PixelGameEngine *pge, olc::GFX3D::PipeLine &pipe); virtual bool DrawAlpha(olc::PixelGameEngine *pge, olc::GFX3D::PipeLine &pipe);
virtual bool DrawDebug(olc::PixelGameEngine *pge, olc::GFX3D::PipeLine &pipe); virtual bool DrawDebug(olc::PixelGameEngine *pge, olc::GFX3D::PipeLine &pipe);
}; };

@ -1,53 +1,53 @@
#include "cCell_Building.h" #include "cCell_Building.h"
cCell_Building::cCell_Building(const std::string &name, cCityMap* map, int x, int y) : cCell(map, x, y) cCell_Building::cCell_Building(const std::string &name, cCityMap* map, int x, int y) : cCell(map, x, y)
{ {
sName = name; sName = name;
} }
cCell_Building::~cCell_Building() cCell_Building::~cCell_Building()
{ {
} }
void cCell_Building::CalculateAdjacency() void cCell_Building::CalculateAdjacency()
{ {
} }
bool cCell_Building::LinkAssets(std::map<std::string, olc::Sprite*>& mapTextures, std::map<std::string, olc::GFX3D::mesh*>& mapMesh, std::map<std::string, olc::GFX3D::mat4x4> &mapTransforms) bool cCell_Building::LinkAssets(std::map<std::string, olc::Sprite*>& mapTextures, std::map<std::string, olc::GFX3D::mesh*>& mapMesh, std::map<std::string, olc::GFX3D::mat4x4> &mapTransforms)
{ {
texture = mapTextures[sName]; texture = mapTextures[sName];
mesh = mapMesh[sName]; mesh = mapMesh[sName];
transform = mapTransforms[sName]; transform = mapTransforms[sName];
return false; return false;
} }
bool cCell_Building::Update(float fElapsedTime) bool cCell_Building::Update(float fElapsedTime)
{ {
return false; return false;
} }
bool cCell_Building::DrawBase(olc::PixelGameEngine * pge, olc::GFX3D::PipeLine & pipe) bool cCell_Building::DrawBase(olc::PixelGameEngine * pge, olc::GFX3D::PipeLine & pipe)
{ {
olc::GFX3D::mat4x4 matTranslate = olc::GFX3D::Math::Mat_MakeTranslation((float)nWorldX, (float)nWorldY, 0.0f); olc::GFX3D::mat4x4 matTranslate = olc::GFX3D::Math::Mat_MakeTranslation((float)nWorldX, (float)nWorldY, 0.0f);
olc::GFX3D::mat4x4 matWorld = olc::GFX3D::Math::Mat_MultiplyMatrix(transform, matTranslate); olc::GFX3D::mat4x4 matWorld = olc::GFX3D::Math::Mat_MultiplyMatrix(transform, matTranslate);
pipe.SetTransform(matWorld); pipe.SetTransform(matWorld);
if (texture != nullptr) if (texture != nullptr)
{ {
pipe.SetTexture(texture); pipe.SetTexture(texture);
pipe.Render(mesh->tris,olc::GFX3D::RENDER_CULL_CW | olc::GFX3D::RENDER_DEPTH | olc::GFX3D::RENDER_TEXTURED | olc::GFX3D::RENDER_LIGHTS); pipe.Render(mesh->tris,olc::GFX3D::RENDER_CULL_CW | olc::GFX3D::RENDER_DEPTH | olc::GFX3D::RENDER_TEXTURED | olc::GFX3D::RENDER_LIGHTS);
} }
else else
{ {
pipe.Render(mesh->tris, olc::GFX3D::RENDER_CULL_CW | olc::GFX3D::RENDER_DEPTH | olc::GFX3D::RENDER_FLAT | olc::GFX3D::RENDER_LIGHTS); pipe.Render(mesh->tris, olc::GFX3D::RENDER_CULL_CW | olc::GFX3D::RENDER_DEPTH | olc::GFX3D::RENDER_FLAT | olc::GFX3D::RENDER_LIGHTS);
} }
return false; return false;
} }
bool cCell_Building::DrawAlpha(olc::PixelGameEngine * pge, olc::GFX3D::PipeLine & pipe) bool cCell_Building::DrawAlpha(olc::PixelGameEngine * pge, olc::GFX3D::PipeLine & pipe)
{ {
return false; return false;
} }

@ -1,25 +1,25 @@
#pragma once #pragma once
#include "cCell.h" #include "cCell.h"
#include "olcPGEX_Graphics3D.h" #include "olcPGEX_Graphics3D.h"
class cCell_Building : public cCell class cCell_Building : public cCell
{ {
public: public:
cCell_Building(const std::string &name, cCityMap* map, int x, int y); cCell_Building(const std::string &name, cCityMap* map, int x, int y);
~cCell_Building(); ~cCell_Building();
private: private:
std::string sName; std::string sName;
olc::Sprite* texture = nullptr; olc::Sprite* texture = nullptr;
olc::GFX3D::mesh* mesh = nullptr; olc::GFX3D::mesh* mesh = nullptr;
olc::GFX3D::mat4x4 transform; olc::GFX3D::mat4x4 transform;
public: public:
virtual void CalculateAdjacency(); virtual void CalculateAdjacency();
virtual bool LinkAssets(std::map<std::string, olc::Sprite*> &mapTextures, std::map<std::string, olc::GFX3D::mesh*> &mapMesh, std::map<std::string, olc::GFX3D::mat4x4> &mapTransforms); virtual bool LinkAssets(std::map<std::string, olc::Sprite*> &mapTextures, std::map<std::string, olc::GFX3D::mesh*> &mapMesh, std::map<std::string, olc::GFX3D::mat4x4> &mapTransforms);
virtual bool Update(float fElapsedTime); virtual bool Update(float fElapsedTime);
virtual bool DrawBase(olc::PixelGameEngine *pge, olc::GFX3D::PipeLine &pipe); virtual bool DrawBase(olc::PixelGameEngine *pge, olc::GFX3D::PipeLine &pipe);
virtual bool DrawAlpha(olc::PixelGameEngine *pge, olc::GFX3D::PipeLine &pipe); virtual bool DrawAlpha(olc::PixelGameEngine *pge, olc::GFX3D::PipeLine &pipe);
}; };

@ -1,49 +1,49 @@
#include "cCell_Plane.h" #include "cCell_Plane.h"
cCell_Plane::cCell_Plane(cCityMap* map, int x, int y, CELL_PLANE type) : cCell(map, x, y) cCell_Plane::cCell_Plane(cCityMap* map, int x, int y, CELL_PLANE type) : cCell(map, x, y)
{ {
bSolid = false; bSolid = false;
nType = type; nType = type;
if (nType == PLANE_GRASS) nCellType = CELL_GRASS; if (nType == PLANE_GRASS) nCellType = CELL_GRASS;
if (nType == PLANE_ASPHALT) nCellType = CELL_PAVEMENT; if (nType == PLANE_ASPHALT) nCellType = CELL_PAVEMENT;
} }
cCell_Plane::~cCell_Plane() cCell_Plane::~cCell_Plane()
{ {
} }
bool cCell_Plane::LinkAssets(std::map<std::string, olc::Sprite*> &mapTextures, std::map<std::string, olc::GFX3D::mesh*> &mapMesh, std::map<std::string, olc::GFX3D::mat4x4> &mapTransforms) bool cCell_Plane::LinkAssets(std::map<std::string, olc::Sprite*> &mapTextures, std::map<std::string, olc::GFX3D::mesh*> &mapMesh, std::map<std::string, olc::GFX3D::mat4x4> &mapTransforms)
{ {
sprGrass = mapTextures["Grass"]; sprGrass = mapTextures["Grass"];
sprPavement = mapTextures["Pavement"]; sprPavement = mapTextures["Pavement"];
meshUnitQuad = mapMesh["UnitQuad"]; meshUnitQuad = mapMesh["UnitQuad"];
return true; return true;
} }
bool cCell_Plane::Update(float fElapsedTime) bool cCell_Plane::Update(float fElapsedTime)
{ {
return false; return false;
} }
bool cCell_Plane::DrawBase(olc::PixelGameEngine *pge, olc::GFX3D::PipeLine &pipe) bool cCell_Plane::DrawBase(olc::PixelGameEngine *pge, olc::GFX3D::PipeLine &pipe)
{ {
olc::GFX3D::mat4x4 matWorld; olc::GFX3D::mat4x4 matWorld;
matWorld = olc::GFX3D::Math::Mat_MakeTranslation((float)nWorldX, (float)nWorldY, 0.0f); matWorld = olc::GFX3D::Math::Mat_MakeTranslation((float)nWorldX, (float)nWorldY, 0.0f);
pipe.SetTransform(matWorld); pipe.SetTransform(matWorld);
if(nType == PLANE_GRASS) if(nType == PLANE_GRASS)
pipe.SetTexture(sprGrass); pipe.SetTexture(sprGrass);
else else
pipe.SetTexture(sprPavement); pipe.SetTexture(sprPavement);
pipe.Render(meshUnitQuad->tris, olc::GFX3D::RENDER_CULL_CW | olc::GFX3D::RENDER_DEPTH | olc::GFX3D::RENDER_TEXTURED); pipe.Render(meshUnitQuad->tris, olc::GFX3D::RENDER_CULL_CW | olc::GFX3D::RENDER_DEPTH | olc::GFX3D::RENDER_TEXTURED);
return false; return false;
} }
bool cCell_Plane::DrawAlpha(olc::PixelGameEngine *pge, olc::GFX3D::PipeLine &pipe) bool cCell_Plane::DrawAlpha(olc::PixelGameEngine *pge, olc::GFX3D::PipeLine &pipe)
{ {
return false; return false;
} }

@ -1,34 +1,34 @@
#pragma once #pragma once
#include "cCell.h" #include "cCell.h"
#include "olcPixelGameEngine.h" #include "olcPixelGameEngine.h"
#include "olcPGEX_Graphics3D.h" #include "olcPGEX_Graphics3D.h"
#include <map> #include <map>
enum CELL_PLANE enum CELL_PLANE
{ {
PLANE_GRASS, PLANE_GRASS,
PLANE_ASPHALT PLANE_ASPHALT
}; };
class cCell_Plane : public cCell class cCell_Plane : public cCell
{ {
public: public:
cCell_Plane(cCityMap* map, int x, int y, CELL_PLANE type); cCell_Plane(cCityMap* map, int x, int y, CELL_PLANE type);
~cCell_Plane(); ~cCell_Plane();
protected: protected:
CELL_PLANE nType = PLANE_GRASS; CELL_PLANE nType = PLANE_GRASS;
private: private:
olc::GFX3D::mesh* meshUnitQuad = nullptr; olc::GFX3D::mesh* meshUnitQuad = nullptr;
olc::Sprite* sprGrass = nullptr; olc::Sprite* sprGrass = nullptr;
olc::Sprite* sprPavement = nullptr; olc::Sprite* sprPavement = nullptr;
public: public:
virtual bool LinkAssets(std::map<std::string, olc::Sprite*> &mapTextures, std::map<std::string, olc::GFX3D::mesh*> &mapMesh, std::map<std::string, olc::GFX3D::mat4x4> &mapTransforms); virtual bool LinkAssets(std::map<std::string, olc::Sprite*> &mapTextures, std::map<std::string, olc::GFX3D::mesh*> &mapMesh, std::map<std::string, olc::GFX3D::mat4x4> &mapTransforms);
virtual bool Update(float fElapsedTime); virtual bool Update(float fElapsedTime);
virtual bool DrawBase(olc::PixelGameEngine *pge, olc::GFX3D::PipeLine &pipe); virtual bool DrawBase(olc::PixelGameEngine *pge, olc::GFX3D::PipeLine &pipe);
virtual bool DrawAlpha(olc::PixelGameEngine *pge, olc::GFX3D::PipeLine &pipe); virtual bool DrawAlpha(olc::PixelGameEngine *pge, olc::GFX3D::PipeLine &pipe);
}; };

@ -1,54 +1,54 @@
#pragma once #pragma once
#include "cCell.h" #include "cCell.h"
enum RoadType enum RoadType
{ {
ROAD_H, ROAD_H,
ROAD_V, ROAD_V,
ROAD_C1, ROAD_C1,
ROAD_C2, ROAD_C2,
ROAD_C3, ROAD_C3,
ROAD_C4, ROAD_C4,
ROAD_T1, ROAD_T1,
ROAD_T2, ROAD_T2,
ROAD_T3, ROAD_T3,
ROAD_T4, ROAD_T4,
ROAD_X, ROAD_X,
}; };
class cCell_Road : public cCell class cCell_Road : public cCell
{ {
public: public:
cCell_Road(cCityMap* map, int x, int y); cCell_Road(cCityMap* map, int x, int y);
~cCell_Road(); ~cCell_Road();
private: private:
struct StopPattern struct StopPattern
{ {
bool bStop[49]; bool bStop[49];
}; };
private: private:
bool bNeighboursAreRoads[4]; bool bNeighboursAreRoads[4];
olc::GFX3D::mesh *meshUnitQuad = nullptr; olc::GFX3D::mesh *meshUnitQuad = nullptr;
olc::Sprite* sprRoadTex[11]; olc::Sprite* sprRoadTex[11];
std::vector<StopPattern> vStopPattern; std::vector<StopPattern> vStopPattern;
int nCurrentStopPattern = 0; int nCurrentStopPattern = 0;
float fStopPatternTimer = 0.0f; float fStopPatternTimer = 0.0f;
public: public:
RoadType nRoadType = ROAD_X; RoadType nRoadType = ROAD_X;
cAuto_Track* pSafeCarTrack = nullptr; cAuto_Track* pSafeCarTrack = nullptr;
cAuto_Track* pSafePedestrianTrack = nullptr; cAuto_Track* pSafePedestrianTrack = nullptr;
cAuto_Track* pSafeChaseTrack = nullptr; cAuto_Track* pSafeChaseTrack = nullptr;
virtual void CalculateAdjacency(); virtual void CalculateAdjacency();
virtual bool LinkAssets(std::map<std::string, olc::Sprite*> &mapTextures, std::map<std::string, olc::GFX3D::mesh*> &mapMesh, std::map<std::string, olc::GFX3D::mat4x4> &mapTransforms); virtual bool LinkAssets(std::map<std::string, olc::Sprite*> &mapTextures, std::map<std::string, olc::GFX3D::mesh*> &mapMesh, std::map<std::string, olc::GFX3D::mat4x4> &mapTransforms);
virtual bool Update(float fElapsedTime); virtual bool Update(float fElapsedTime);
virtual bool DrawBase(olc::PixelGameEngine *pge, olc::GFX3D::PipeLine &pipe); virtual bool DrawBase(olc::PixelGameEngine *pge, olc::GFX3D::PipeLine &pipe);
virtual bool DrawAlpha(olc::PixelGameEngine *pge, olc::GFX3D::PipeLine &pipe); virtual bool DrawAlpha(olc::PixelGameEngine *pge, olc::GFX3D::PipeLine &pipe);
virtual bool DrawDebug(olc::PixelGameEngine *pge, olc::GFX3D::PipeLine &pipe); virtual bool DrawDebug(olc::PixelGameEngine *pge, olc::GFX3D::PipeLine &pipe);
}; };

@ -1,91 +1,91 @@
#include "cCell_Water.h" #include "cCell_Water.h"
#include "cCityMap.h" #include "cCityMap.h"
cCell_Water::cCell_Water(cCityMap* map, int x, int y) : cCell(map, x, y) cCell_Water::cCell_Water(cCityMap* map, int x, int y) : cCell(map, x, y)
{ {
nCellType = CELL_WATER; nCellType = CELL_WATER;
bNeighboursAreWater[0] = false; bNeighboursAreWater[0] = false;
bNeighboursAreWater[1] = false; bNeighboursAreWater[1] = false;
bNeighboursAreWater[2] = false; bNeighboursAreWater[2] = false;
bNeighboursAreWater[3] = false; bNeighboursAreWater[3] = false;
} }
cCell_Water::~cCell_Water() cCell_Water::~cCell_Water()
{ {
} }
bool cCell_Water::LinkAssets(std::map<std::string, olc::Sprite*>& mapTextures, std::map<std::string, olc::GFX3D::mesh*>& mapMesh, std::map<std::string, olc::GFX3D::mat4x4> &mapTransforms) bool cCell_Water::LinkAssets(std::map<std::string, olc::Sprite*>& mapTextures, std::map<std::string, olc::GFX3D::mesh*>& mapMesh, std::map<std::string, olc::GFX3D::mat4x4> &mapTransforms)
{ {
meshUnitQuad = mapMesh["UnitQuad"]; meshUnitQuad = mapMesh["UnitQuad"];
meshWalls = mapMesh["WallsOut"]; meshWalls = mapMesh["WallsOut"];
sprWater = mapTextures["Water"]; sprWater = mapTextures["Water"];
sprSides = mapTextures["WaterSide"]; sprSides = mapTextures["WaterSide"];
sprClouds = mapTextures["Clouds"]; sprClouds = mapTextures["Clouds"];
return false; return false;
} }
bool cCell_Water::Update(float fElapsedTime) bool cCell_Water::Update(float fElapsedTime)
{ {
return false; return false;
} }
bool cCell_Water::DrawBase(olc::PixelGameEngine * pge, olc::GFX3D::PipeLine & pipe) bool cCell_Water::DrawBase(olc::PixelGameEngine * pge, olc::GFX3D::PipeLine & pipe)
{ {
olc::GFX3D::mat4x4 matWorld; olc::GFX3D::mat4x4 matWorld;
matWorld = olc::GFX3D::Math::Mat_MakeTranslation((float)nWorldX, (float)nWorldY, 0.0f); matWorld = olc::GFX3D::Math::Mat_MakeTranslation((float)nWorldX, (float)nWorldY, 0.0f);
pipe.SetTransform(matWorld); pipe.SetTransform(matWorld);
pipe.SetTexture(sprSides); pipe.SetTexture(sprSides);
if (!bNeighboursAreWater[1]) pipe.Render(meshWalls->tris, olc::GFX3D::RENDER_LIGHTS | olc::GFX3D::RENDER_CULL_CCW | olc::GFX3D::RENDER_TEXTURED | olc::GFX3D::RENDER_DEPTH, 0, 2); if (!bNeighboursAreWater[1]) pipe.Render(meshWalls->tris, olc::GFX3D::RENDER_LIGHTS | olc::GFX3D::RENDER_CULL_CCW | olc::GFX3D::RENDER_TEXTURED | olc::GFX3D::RENDER_DEPTH, 0, 2);
if (!bNeighboursAreWater[3]) pipe.Render(meshWalls->tris, olc::GFX3D::RENDER_LIGHTS | olc::GFX3D::RENDER_CULL_CCW | olc::GFX3D::RENDER_TEXTURED | olc::GFX3D::RENDER_DEPTH, 2, 2); if (!bNeighboursAreWater[3]) pipe.Render(meshWalls->tris, olc::GFX3D::RENDER_LIGHTS | olc::GFX3D::RENDER_CULL_CCW | olc::GFX3D::RENDER_TEXTURED | olc::GFX3D::RENDER_DEPTH, 2, 2);
if (!bNeighboursAreWater[2]) pipe.Render(meshWalls->tris, olc::GFX3D::RENDER_LIGHTS | olc::GFX3D::RENDER_CULL_CCW | olc::GFX3D::RENDER_TEXTURED | olc::GFX3D::RENDER_DEPTH, 4, 2); if (!bNeighboursAreWater[2]) pipe.Render(meshWalls->tris, olc::GFX3D::RENDER_LIGHTS | olc::GFX3D::RENDER_CULL_CCW | olc::GFX3D::RENDER_TEXTURED | olc::GFX3D::RENDER_DEPTH, 4, 2);
if (!bNeighboursAreWater[0]) pipe.Render(meshWalls->tris, olc::GFX3D::RENDER_LIGHTS | olc::GFX3D::RENDER_CULL_CCW | olc::GFX3D::RENDER_TEXTURED | olc::GFX3D::RENDER_DEPTH, 6, 2); if (!bNeighboursAreWater[0]) pipe.Render(meshWalls->tris, olc::GFX3D::RENDER_LIGHTS | olc::GFX3D::RENDER_CULL_CCW | olc::GFX3D::RENDER_TEXTURED | olc::GFX3D::RENDER_DEPTH, 6, 2);
return false; return false;
} }
bool cCell_Water::DrawAlpha(olc::PixelGameEngine * pge, olc::GFX3D::PipeLine & pipe) bool cCell_Water::DrawAlpha(olc::PixelGameEngine * pge, olc::GFX3D::PipeLine & pipe)
{ {
auto renderWater = [&](const int x, const int y, const olc::Pixel& pSource, const olc::Pixel& pDest) auto renderWater = [&](const int x, const int y, const olc::Pixel& pSource, const olc::Pixel& pDest)
{ {
float a = (float)(pSource.a / 255.0f) * 0.6f; float a = (float)(pSource.a / 255.0f) * 0.6f;
float c = 1.0f - a; float c = 1.0f - a;
float r = a * (float)pSource.r + c * (float)pDest.r; float r = a * (float)pSource.r + c * (float)pDest.r;
float g = a * (float)pSource.g + c * (float)pDest.g; float g = a * (float)pSource.g + c * (float)pDest.g;
float b = a * (float)pSource.b + c * (float)pDest.b; float b = a * (float)pSource.b + c * (float)pDest.b;
a = 0.4f; a = 0.4f;
c = 1.0f - a; c = 1.0f - a;
olc::Pixel sky = sprClouds->GetPixel(x, y); olc::Pixel sky = sprClouds->GetPixel(x, y);
float sr = a * (float)sky.r + c * r; float sr = a * (float)sky.r + c * r;
float sg = a * (float)sky.g + c * g; float sg = a * (float)sky.g + c * g;
float sb = a * (float)sky.b + c * b; float sb = a * (float)sky.b + c * b;
return olc::Pixel((uint8_t)sr, (uint8_t)sg, (uint8_t)sb); return olc::Pixel((uint8_t)sr, (uint8_t)sg, (uint8_t)sb);
}; };
pge->SetPixelMode(renderWater); pge->SetPixelMode(renderWater);
olc::GFX3D::mat4x4 matWorld = olc::GFX3D::Math::Mat_MakeTranslation((float)nWorldX, (float)nWorldY, 0.07f); olc::GFX3D::mat4x4 matWorld = olc::GFX3D::Math::Mat_MakeTranslation((float)nWorldX, (float)nWorldY, 0.07f);
pipe.SetTransform(matWorld); pipe.SetTransform(matWorld);
pipe.SetTexture(sprWater); pipe.SetTexture(sprWater);
pipe.Render(meshUnitQuad->tris, olc::GFX3D::RENDER_CULL_CW | olc::GFX3D::RENDER_DEPTH | olc::GFX3D::RENDER_TEXTURED); pipe.Render(meshUnitQuad->tris, olc::GFX3D::RENDER_CULL_CW | olc::GFX3D::RENDER_DEPTH | olc::GFX3D::RENDER_TEXTURED);
pge->SetPixelMode(olc::Pixel::NORMAL); pge->SetPixelMode(olc::Pixel::NORMAL);
return false; return false;
} }
void cCell_Water::CalculateAdjacency() void cCell_Water::CalculateAdjacency()
{ {
auto r = [&](int i, int j) auto r = [&](int i, int j)
{ {
if (pMap->Cell(nWorldX + i, nWorldY + j) != nullptr) if (pMap->Cell(nWorldX + i, nWorldY + j) != nullptr)
return pMap->Cell(nWorldX + i, nWorldY + j)->nCellType == CELL_WATER; return pMap->Cell(nWorldX + i, nWorldY + j)->nCellType == CELL_WATER;
else else
return false; return false;
}; };
bNeighboursAreWater[0] = r(0, -1); bNeighboursAreWater[0] = r(0, -1);
bNeighboursAreWater[1] = r(+1, 0); bNeighboursAreWater[1] = r(+1, 0);
bNeighboursAreWater[2] = r(0, +1); bNeighboursAreWater[2] = r(0, +1);
bNeighboursAreWater[3] = r(-1, 0); bNeighboursAreWater[3] = r(-1, 0);
} }

@ -1,25 +1,25 @@
#pragma once #pragma once
#include "cCell.h" #include "cCell.h"
class cCell_Water : public cCell class cCell_Water : public cCell
{ {
public: public:
cCell_Water(cCityMap* map, int x, int y); cCell_Water(cCityMap* map, int x, int y);
~cCell_Water(); ~cCell_Water();
private: private:
olc::GFX3D::mesh* meshUnitQuad = nullptr; olc::GFX3D::mesh* meshUnitQuad = nullptr;
olc::GFX3D::mesh* meshWalls = nullptr; olc::GFX3D::mesh* meshWalls = nullptr;
olc::Sprite* sprWater = nullptr; olc::Sprite* sprWater = nullptr;
olc::Sprite* sprSides = nullptr; olc::Sprite* sprSides = nullptr;
olc::Sprite* sprClouds = nullptr; olc::Sprite* sprClouds = nullptr;
bool bNeighboursAreWater[4]; bool bNeighboursAreWater[4];
public: public:
virtual void CalculateAdjacency(); virtual void CalculateAdjacency();
virtual bool LinkAssets(std::map<std::string, olc::Sprite*> &mapTextures, std::map<std::string, olc::GFX3D::mesh*> &mapMesh, std::map<std::string, olc::GFX3D::mat4x4> &mapTransforms); virtual bool LinkAssets(std::map<std::string, olc::Sprite*> &mapTextures, std::map<std::string, olc::GFX3D::mesh*> &mapMesh, std::map<std::string, olc::GFX3D::mat4x4> &mapTransforms);
virtual bool Update(float fElapsedTime); virtual bool Update(float fElapsedTime);
virtual bool DrawBase(olc::PixelGameEngine *pge, olc::GFX3D::PipeLine &pipe); virtual bool DrawBase(olc::PixelGameEngine *pge, olc::GFX3D::PipeLine &pipe);
virtual bool DrawAlpha(olc::PixelGameEngine *pge, olc::GFX3D::PipeLine &pipe); virtual bool DrawAlpha(olc::PixelGameEngine *pge, olc::GFX3D::PipeLine &pipe);
}; };

@ -1,202 +1,202 @@
#include "cCityMap.h" #include "cCityMap.h"
#include <fstream> #include <fstream>
cCityMap::cCityMap(int w, int h, std::map<std::string, olc::Sprite*> &mapTextures, std::map<std::string, olc::GFX3D::mesh*> &mapMesh, std::map<std::string, olc::GFX3D::mat4x4> &mapTransforms) cCityMap::cCityMap(int w, int h, std::map<std::string, olc::Sprite*> &mapTextures, std::map<std::string, olc::GFX3D::mesh*> &mapMesh, std::map<std::string, olc::GFX3D::mat4x4> &mapTransforms)
{ {
CreateCity(w, h, mapTextures, mapMesh, mapTransforms); CreateCity(w, h, mapTextures, mapMesh, mapTransforms);
} }
cCityMap::~cCityMap() cCityMap::~cCityMap()
{ {
ReleaseCity(); ReleaseCity();
} }
int cCityMap::GetWidth() int cCityMap::GetWidth()
{ {
return nWidth; return nWidth;
} }
int cCityMap::GetHeight() int cCityMap::GetHeight()
{ {
return nHeight; return nHeight;
} }
cCell* cCityMap::Cell(int x, int y) cCell* cCityMap::Cell(int x, int y)
{ {
if (x >= 0 && x < nWidth && y >= 0 && y < nHeight) if (x >= 0 && x < nWidth && y >= 0 && y < nHeight)
return pCells[y*nWidth + x]; return pCells[y*nWidth + x];
else else
return nullptr; return nullptr;
} }
cCell* cCityMap::Replace(int x, int y, cCell* cell) cCell* cCityMap::Replace(int x, int y, cCell* cell)
{ {
if (cell == nullptr) if (cell == nullptr)
return nullptr; return nullptr;
if (pCells[y * nWidth + x] != nullptr) if (pCells[y * nWidth + x] != nullptr)
delete pCells[y * nWidth + x]; delete pCells[y * nWidth + x];
pCells[y * nWidth + x] = cell; pCells[y * nWidth + x] = cell;
return cell; return cell;
} }
void cCityMap::CreateCity(int w, int h, std::map<std::string, olc::Sprite*> &mapTextures, std::map<std::string, olc::GFX3D::mesh*> &mapMesh, std::map<std::string, olc::GFX3D::mat4x4> &mapTransforms) void cCityMap::CreateCity(int w, int h, std::map<std::string, olc::Sprite*> &mapTextures, std::map<std::string, olc::GFX3D::mesh*> &mapMesh, std::map<std::string, olc::GFX3D::mat4x4> &mapTransforms)
{ {
ReleaseCity(); ReleaseCity();
nWidth = w; nWidth = w;
nHeight = h; nHeight = h;
pCells = new cCell*[nHeight * nWidth]; pCells = new cCell*[nHeight * nWidth];
// Create Navigation Node Pool, assumes 5 nodes on east and south // Create Navigation Node Pool, assumes 5 nodes on east and south
// side of each cell. The City owns these nodes, and cells in the // side of each cell. The City owns these nodes, and cells in the
// city borrow them and link to them as required // city borrow them and link to them as required
pNodes = new cAuto_Node[nHeight * nWidth * 49]; pNodes = new cAuto_Node[nHeight * nWidth * 49];
// The cell has 49 nodes, though some are simply unused. This is less memory // The cell has 49 nodes, though some are simply unused. This is less memory
// efficient certainly, but makes code more intuitive and easier to write // efficient certainly, but makes code more intuitive and easier to write
for (int x = 0; x < nWidth; x++) for (int x = 0; x < nWidth; x++)
{ {
for (int y = 0; y < nHeight; y++) for (int y = 0; y < nHeight; y++)
{ {
// Nodes sit between cells, therefore each create nodes along // Nodes sit between cells, therefore each create nodes along
// the east and southern sides of the cell. This assumes that // the east and southern sides of the cell. This assumes that
// navigation along the top and left boundaries of the map // navigation along the top and left boundaries of the map
// will not occur. And it shouldnt, as its water // will not occur. And it shouldnt, as its water
int idx = (y * nWidth + x) * 49; int idx = (y * nWidth + x) * 49;
for (int dx = 0; dx < 7; dx++) for (int dx = 0; dx < 7; dx++)
{ {
float off_x = 0.0f; float off_x = 0.0f;
switch (dx) switch (dx)
{ {
case 0: off_x = 0.000f; break; case 0: off_x = 0.000f; break;
case 1: off_x = 0.083f; break; case 1: off_x = 0.083f; break;
case 2: off_x = 0.333f; break; case 2: off_x = 0.333f; break;
case 3: off_x = 0.500f; break; case 3: off_x = 0.500f; break;
case 4: off_x = 0.667f; break; case 4: off_x = 0.667f; break;
case 5: off_x = 0.917f; break; case 5: off_x = 0.917f; break;
case 6: off_x = 1.000f; break; case 6: off_x = 1.000f; break;
} }
for (int dy = 0; dy < 7; dy++) for (int dy = 0; dy < 7; dy++)
{ {
float off_y = 0.0f; float off_y = 0.0f;
switch (dy) switch (dy)
{ {
case 0: off_y = 0.000f; break; case 0: off_y = 0.000f; break;
case 1: off_y = 0.083f; break; case 1: off_y = 0.083f; break;
case 2: off_y = 0.333f; break; case 2: off_y = 0.333f; break;
case 3: off_y = 0.500f; break; case 3: off_y = 0.500f; break;
case 4: off_y = 0.667f; break; case 4: off_y = 0.667f; break;
case 5: off_y = 0.917f; break; case 5: off_y = 0.917f; break;
case 6: off_y = 1.000f; break; case 6: off_y = 1.000f; break;
} }
pNodes[idx + dy * 7 + dx].pos = { (float)x + off_x, (float)y + off_y }; pNodes[idx + dy * 7 + dx].pos = { (float)x + off_x, (float)y + off_y };
pNodes[idx + dy * 7 + dx].bBlock = false; pNodes[idx + dy * 7 + dx].bBlock = false;
} }
} }
} }
} }
// Now create default Cell // Now create default Cell
for (int x = 0; x < nWidth; x++) for (int x = 0; x < nWidth; x++)
{ {
for (int y = 0; y < nHeight; y++) for (int y = 0; y < nHeight; y++)
{ {
// Default city, everything is grass // Default city, everything is grass
pCells[y * nWidth + x] = new cCell_Plane(this, x, y, PLANE_GRASS); pCells[y * nWidth + x] = new cCell_Plane(this, x, y, PLANE_GRASS);
// Give the cell the opportunity to locally reference the resources it needs // Give the cell the opportunity to locally reference the resources it needs
pCells[y * nWidth + x]->LinkAssets(mapTextures, mapMesh, mapTransforms); pCells[y * nWidth + x]->LinkAssets(mapTextures, mapMesh, mapTransforms);
} }
} }
} }
cAuto_Node* cCityMap::GetAutoNodeBase(int x, int y) cAuto_Node* cCityMap::GetAutoNodeBase(int x, int y)
{ {
return pNodes + (y * nWidth + x) * 49; return pNodes + (y * nWidth + x) * 49;
} }
void cCityMap::RemoveAllTracks() void cCityMap::RemoveAllTracks()
{ {
for (int i = 0; i < nWidth * nHeight * 49; i++) for (int i = 0; i < nWidth * nHeight * 49; i++)
{ {
pNodes[i].listTracks.clear(); pNodes[i].listTracks.clear();
} }
} }
void cCityMap::ReleaseCity() void cCityMap::ReleaseCity()
{ {
for (int x = 0; x < nWidth; x++) for (int x = 0; x < nWidth; x++)
{ {
for (int y = 0; y < nHeight; y++) for (int y = 0; y < nHeight; y++)
{ {
// Erase any tracks attached to nodes // Erase any tracks attached to nodes
for(int i=0; i<49; i++) for(int i=0; i<49; i++)
Cell(x, y)->pNaviNodes[i]->listTracks.clear(); Cell(x, y)->pNaviNodes[i]->listTracks.clear();
// Release individual cell objects // Release individual cell objects
delete pCells[y * nWidth + x]; delete pCells[y * nWidth + x];
} }
} }
// Release array of cell pointers // Release array of cell pointers
if (pCells != nullptr) delete pCells; if (pCells != nullptr) delete pCells;
// Release array of automata navigation nodes // Release array of automata navigation nodes
if (pNodes != nullptr) delete pNodes; if (pNodes != nullptr) delete pNodes;
nWidth = 0; nWidth = 0;
nHeight = 0; nHeight = 0;
} }
bool cCityMap::SaveCity(std::string sFilename) bool cCityMap::SaveCity(std::string sFilename)
{ {
/*std::ofstream file(sFilename, std::ios::out | std::ios::binary); /*std::ofstream file(sFilename, std::ios::out | std::ios::binary);
if (!file.is_open()) return false; if (!file.is_open()) return false;
file.write((char*)&m_nWidth, sizeof(int)); file.write((char*)&m_nWidth, sizeof(int));
file.write((char*)&m_nHeight, sizeof(int)); file.write((char*)&m_nHeight, sizeof(int));
for (int x = 0; x < m_nWidth; x++) for (int x = 0; x < m_nWidth; x++)
{ {
for (int y = 0; y < m_nHeight; y++) for (int y = 0; y < m_nHeight; y++)
{ {
file.write((char*)Cell(x, y), sizeof(cCityCell)); file.write((char*)Cell(x, y), sizeof(cCityCell));
} }
}*/ }*/
return true; return true;
} }
bool cCityMap::LoadCity(std::string sFilename) bool cCityMap::LoadCity(std::string sFilename)
{ {
/*std::ifstream file(sFilename, std::ios::in | std::ios::binary); /*std::ifstream file(sFilename, std::ios::in | std::ios::binary);
if (!file.is_open()) return false; if (!file.is_open()) return false;
int w, h; int w, h;
file.read((char*)&w, sizeof(int)); file.read((char*)&w, sizeof(int));
file.read((char*)&h, sizeof(int)); file.read((char*)&h, sizeof(int));
CreateCity(w, h); CreateCity(w, h);
for (int x = 0; x < m_nWidth; x++) for (int x = 0; x < m_nWidth; x++)
{ {
for (int y = 0; y < m_nHeight; y++) for (int y = 0; y < m_nHeight; y++)
{ {
file.read((char*)Cell(x, y), sizeof(cCityCell)); file.read((char*)Cell(x, y), sizeof(cCityCell));
} }
}*/ }*/
return true; return true;
} }

@ -1,63 +1,63 @@
#pragma once #pragma once
#include <string> #include <string>
#include <map> #include <map>
#include "olcPixelGameEngine.h" #include "olcPixelGameEngine.h"
#include "olcPGEX_Graphics3D.h" #include "olcPGEX_Graphics3D.h"
#include "cCell.h" #include "cCell.h"
#include "cCell_Plane.h" #include "cCell_Plane.h"
#include "cCell_Water.h" #include "cCell_Water.h"
#include "cCell_Road.h" #include "cCell_Road.h"
#include "cCell_Building.h" #include "cCell_Building.h"
/* /*
This class holds the definition of a map. The map data is actually This class holds the definition of a map. The map data is actually
stored within this clap, as well as accessors to access the individual stored within this clap, as well as accessors to access the individual
map cells map cells
*/ */
class cCityMap class cCityMap
{ {
public: public:
// Construct a "blank" city w units wide by h units high // Construct a "blank" city w units wide by h units high
cCityMap(int w, int h, std::map<std::string, olc::Sprite*> &mapTextures, std::map<std::string, olc::GFX3D::mesh*> &mapMesh, std::map<std::string, olc::GFX3D::mat4x4> &mapTransforms); cCityMap(int w, int h, std::map<std::string, olc::Sprite*> &mapTextures, std::map<std::string, olc::GFX3D::mesh*> &mapMesh, std::map<std::string, olc::GFX3D::mat4x4> &mapTransforms);
// Cleans up city, like Batman // Cleans up city, like Batman
~cCityMap(); ~cCityMap();
public: public:
// Save the current city to a file, this will overwrite an existing // Save the current city to a file, this will overwrite an existing
// city file without warning. Returns true if successful // city file without warning. Returns true if successful
bool SaveCity(std::string sFilename); bool SaveCity(std::string sFilename);
// Load a city from file and replace current city with it, retuns // Load a city from file and replace current city with it, retuns
// true if successful // true if successful
bool LoadCity(std::string sFilename); bool LoadCity(std::string sFilename);
public: public:
// Return width of city in cells // Return width of city in cells
int GetWidth(); int GetWidth();
// Return height of city in cells // Return height of city in cells
int GetHeight(); int GetHeight();
// Return a specific cell reference if inside city limits, or nullptr // Return a specific cell reference if inside city limits, or nullptr
cCell* Cell(int x, int y); cCell* Cell(int x, int y);
// Replace a specific cell // Replace a specific cell
cCell* Replace(int x, int y, cCell* cell); cCell* Replace(int x, int y, cCell* cell);
cAuto_Node* GetAutoNodeBase(int x, int y); cAuto_Node* GetAutoNodeBase(int x, int y);
void RemoveAllTracks(); void RemoveAllTracks();
private: private:
int nWidth = 0; int nWidth = 0;
int nHeight = 0; int nHeight = 0;
cCell **pCells = nullptr; cCell **pCells = nullptr;
cAuto_Node *pNodes = nullptr; cAuto_Node *pNodes = nullptr;
private: private:
// Creates a "default" city of specified size // Creates a "default" city of specified size
void CreateCity(int w, int h, std::map<std::string, olc::Sprite*> &mapTextures, std::map<std::string, olc::GFX3D::mesh*> &mapMesh, std::map<std::string, olc::GFX3D::mat4x4> &mapTransforms); void CreateCity(int w, int h, std::map<std::string, olc::Sprite*> &mapTextures, std::map<std::string, olc::GFX3D::mesh*> &mapMesh, std::map<std::string, olc::GFX3D::mat4x4> &mapTransforms);
// Destroy city // Destroy city
void ReleaseCity(); void ReleaseCity();
}; };

@ -1,156 +1,156 @@
#include "cGameSettings.h" #include "cGameSettings.h"
cGameSettings::cGameSettings() cGameSettings::cGameSettings()
{ {
} }
cGameSettings::~cGameSettings() cGameSettings::~cGameSettings()
{ {
} }
bool cGameSettings::LoadConfigFile(std::string sFile) bool cGameSettings::LoadConfigFile(std::string sFile)
{ {
lua_State *L = luaL_newstate(); lua_State *L = luaL_newstate();
luaL_openlibs(L); luaL_openlibs(L);
// Load game settings file // Load game settings file
int r = luaL_loadfile(L, sFile.c_str()); int r = luaL_loadfile(L, sFile.c_str());
if (r != LUA_OK) if (r != LUA_OK)
{ {
std::string errormsg = lua_tostring(L, -1); std::string errormsg = lua_tostring(L, -1);
std::cout << errormsg << std::endl; std::cout << errormsg << std::endl;
return false; return false;
} }
// Execute it // Execute it
int i = lua_pcall(L, 0, LUA_MULTRET, 0); int i = lua_pcall(L, 0, LUA_MULTRET, 0);
if (i != LUA_OK) if (i != LUA_OK)
{ {
std::string errormsg = lua_tostring(L, -1); std::string errormsg = lua_tostring(L, -1);
std::cout << errormsg << std::endl; std::cout << errormsg << std::endl;
return false; return false;
} }
lua_getglobal(L, "PixelWidth"); lua_getglobal(L, "PixelWidth");
if (lua_isinteger(L, -1)) cGameSettings::nPixelWidth = (int)lua_tointeger(L, -1); if (lua_isinteger(L, -1)) cGameSettings::nPixelWidth = (int)lua_tointeger(L, -1);
lua_getglobal(L, "PixelHeight"); lua_getglobal(L, "PixelHeight");
if (lua_isinteger(L, -1)) cGameSettings::nPixelHeight = (int)lua_tointeger(L, -1); if (lua_isinteger(L, -1)) cGameSettings::nPixelHeight = (int)lua_tointeger(L, -1);
lua_getglobal(L, "ScreenWidth"); lua_getglobal(L, "ScreenWidth");
if (lua_isinteger(L, -1)) cGameSettings::nScreenWidth = (int)lua_tointeger(L, -1); if (lua_isinteger(L, -1)) cGameSettings::nScreenWidth = (int)lua_tointeger(L, -1);
lua_getglobal(L, "ScreenHeight"); lua_getglobal(L, "ScreenHeight");
if (lua_isinteger(L, -1)) cGameSettings::nScreenHeight = (int)lua_tointeger(L, -1); if (lua_isinteger(L, -1)) cGameSettings::nScreenHeight = (int)lua_tointeger(L, -1);
lua_getglobal(L, "DefaultMapWidth"); lua_getglobal(L, "DefaultMapWidth");
if (lua_isinteger(L, -1)) cGameSettings::nDefaultMapWidth = (int)lua_tointeger(L, -1); if (lua_isinteger(L, -1)) cGameSettings::nDefaultMapWidth = (int)lua_tointeger(L, -1);
lua_getglobal(L, "DefaultMapHeight"); lua_getglobal(L, "DefaultMapHeight");
if (lua_isinteger(L, -1)) cGameSettings::nDefaultMapHeight = (int)lua_tointeger(L, -1); if (lua_isinteger(L, -1)) cGameSettings::nDefaultMapHeight = (int)lua_tointeger(L, -1);
lua_getglobal(L, "DefaultCityFile"); lua_getglobal(L, "DefaultCityFile");
if (lua_isstring(L, -1)) cGameSettings::sDefaultCityFile = lua_tostring(L, -1); if (lua_isstring(L, -1)) cGameSettings::sDefaultCityFile = lua_tostring(L, -1);
lua_getglobal(L, "FullScreen"); lua_getglobal(L, "FullScreen");
if (lua_isboolean(L, -1)) cGameSettings::bFullScreen = lua_toboolean(L, -1); if (lua_isboolean(L, -1)) cGameSettings::bFullScreen = lua_toboolean(L, -1);
//// Load System Texture files //// Load System Texture files
// Load Texture Assets // Load Texture Assets
lua_getglobal(L, "Textures"); // -1 Table "Teams" lua_getglobal(L, "Textures"); // -1 Table "Teams"
if (lua_istable(L, -1)) if (lua_istable(L, -1))
{ {
lua_pushnil(L); // -2 Key Nil : -1 Table "Teams" lua_pushnil(L); // -2 Key Nil : -1 Table "Teams"
while (lua_next(L, -2) != 0) // -1 Table : -2 Key "TeamName" : -3 Table "Teams" while (lua_next(L, -2) != 0) // -1 Table : -2 Key "TeamName" : -3 Table "Teams"
{ {
sAssetTexture texture; sAssetTexture texture;
int stage = 0; int stage = 0;
if (lua_istable(L, -1)) if (lua_istable(L, -1))
{ {
lua_gettable(L, -1); // -1 Table : -2 Table Value : -3 Key "TeamName" : -4 Table "Teams" lua_gettable(L, -1); // -1 Table : -2 Table Value : -3 Key "TeamName" : -4 Table "Teams"
lua_pushnil(L); // -1 Key Nil : -2 Table : -3 Table Value : -4 Key "TeamName" : -5 Table "Teams" lua_pushnil(L); // -1 Key Nil : -2 Table : -3 Table Value : -4 Key "TeamName" : -5 Table "Teams"
while (lua_next(L, -2) != 0) // -1 Value "BotFile" : -2 Key Nil : -3 Table : -4 Table Value : -5 Key "TeamName" : -6 Table "Teams" while (lua_next(L, -2) != 0) // -1 Value "BotFile" : -2 Key Nil : -3 Table : -4 Table Value : -5 Key "TeamName" : -6 Table "Teams"
{ {
if (stage == 0) texture.sName = lua_tostring(L, -1); if (stage == 0) texture.sName = lua_tostring(L, -1);
if (stage == 1) texture.sFile = lua_tostring(L, -1); if (stage == 1) texture.sFile = lua_tostring(L, -1);
lua_pop(L, 1); // -1 Key Nil : -2 Table : -3 Table Value : -4 Key "TeamName" : -5 Table "Teams" lua_pop(L, 1); // -1 Key Nil : -2 Table : -3 Table Value : -4 Key "TeamName" : -5 Table "Teams"
stage++; stage++;
} }
} }
lua_pop(L, 1); // -1 Table : -2 Table Value : -3 Key "TeamName" : -4 Table "Teams" lua_pop(L, 1); // -1 Table : -2 Table Value : -3 Key "TeamName" : -4 Table "Teams"
vecAssetTextures.push_back(texture); vecAssetTextures.push_back(texture);
} }
} }
auto GroupLoadAssets = [L](const std::string &group, std::vector<sAssetModel> &vec) auto GroupLoadAssets = [L](const std::string &group, std::vector<sAssetModel> &vec)
{ {
lua_getglobal(L, group.c_str()); lua_getglobal(L, group.c_str());
if (lua_istable(L, -1)) if (lua_istable(L, -1))
{ {
lua_pushnil(L); lua_pushnil(L);
while (lua_next(L, -2) != 0) while (lua_next(L, -2) != 0)
{ {
sAssetModel model; sAssetModel model;
int stage = 0; int stage = 0;
if (lua_istable(L, -1)) if (lua_istable(L, -1))
{ {
lua_gettable(L, -1); lua_gettable(L, -1);
lua_pushnil(L); lua_pushnil(L);
while (lua_next(L, -2) != 0) while (lua_next(L, -2) != 0)
{ {
if (stage == 0) model.sCreator = lua_tostring(L, -1); if (stage == 0) model.sCreator = lua_tostring(L, -1);
if (stage == 1) model.sDescription = lua_tostring(L, -1); if (stage == 1) model.sDescription = lua_tostring(L, -1);
if (stage == 2) model.sModelOBJ = lua_tostring(L, -1); if (stage == 2) model.sModelOBJ = lua_tostring(L, -1);
if (stage == 3) model.sModelPNG = lua_tostring(L, -1); if (stage == 3) model.sModelPNG = lua_tostring(L, -1);
if (stage == 4) model.fRotate[0] = (float)lua_tonumber(L, -1); if (stage == 4) model.fRotate[0] = (float)lua_tonumber(L, -1);
if (stage == 5) model.fRotate[1] = (float)lua_tonumber(L, -1); if (stage == 5) model.fRotate[1] = (float)lua_tonumber(L, -1);
if (stage == 6) model.fRotate[2] = (float)lua_tonumber(L, -1); if (stage == 6) model.fRotate[2] = (float)lua_tonumber(L, -1);
if (stage == 7) model.fScale[0] = (float)lua_tonumber(L, -1); if (stage == 7) model.fScale[0] = (float)lua_tonumber(L, -1);
if (stage == 8) model.fScale[1] = (float)lua_tonumber(L, -1); if (stage == 8) model.fScale[1] = (float)lua_tonumber(L, -1);
if (stage == 9) model.fScale[2] = (float)lua_tonumber(L, -1); if (stage == 9) model.fScale[2] = (float)lua_tonumber(L, -1);
if (stage == 10) model.fTranslate[0] = (float)lua_tonumber(L, -1); if (stage == 10) model.fTranslate[0] = (float)lua_tonumber(L, -1);
if (stage == 11) model.fTranslate[1] = (float)lua_tonumber(L, -1); if (stage == 11) model.fTranslate[1] = (float)lua_tonumber(L, -1);
if (stage == 12) model.fTranslate[2] = (float)lua_tonumber(L, -1); if (stage == 12) model.fTranslate[2] = (float)lua_tonumber(L, -1);
lua_pop(L, 1); lua_pop(L, 1);
stage++; stage++;
} }
} }
lua_pop(L, 1); lua_pop(L, 1);
vec.push_back(model); vec.push_back(model);
} }
} }
}; };
// Load Building Assets // Load Building Assets
GroupLoadAssets("Buildings", vecAssetBuildings); GroupLoadAssets("Buildings", vecAssetBuildings);
// Load Vehicle Assets // Load Vehicle Assets
GroupLoadAssets("Vehicles", vecAssetVehicles); GroupLoadAssets("Vehicles", vecAssetVehicles);
lua_close(L); lua_close(L);
return true; return true;
} }
int cGameSettings::nScreenWidth = 768; int cGameSettings::nScreenWidth = 768;
int cGameSettings::nScreenHeight = 480; int cGameSettings::nScreenHeight = 480;
int cGameSettings::nPixelWidth = 2; int cGameSettings::nPixelWidth = 2;
int cGameSettings::nPixelHeight = 2; int cGameSettings::nPixelHeight = 2;
bool cGameSettings::bFullScreen = false; bool cGameSettings::bFullScreen = false;
int cGameSettings::nDefaultMapWidth = 64; int cGameSettings::nDefaultMapWidth = 64;
int cGameSettings::nDefaultMapHeight = 32; int cGameSettings::nDefaultMapHeight = 32;
std::string cGameSettings::sDefaultCityFile = ""; std::string cGameSettings::sDefaultCityFile = "";
std::vector<sAssetTexture> cGameSettings::vecAssetTextures; std::vector<sAssetTexture> cGameSettings::vecAssetTextures;
std::vector<sAssetModel> cGameSettings::vecAssetBuildings; std::vector<sAssetModel> cGameSettings::vecAssetBuildings;
std::vector<sAssetModel> cGameSettings::vecAssetVehicles; std::vector<sAssetModel> cGameSettings::vecAssetVehicles;

@ -1,65 +1,65 @@
#pragma once #pragma once
#include <iostream> #include <iostream>
#include <string> #include <string>
#include <vector> #include <vector>
extern "C" extern "C"
{ {
#include "lua533/include/lua.h" #include "lua533/include/lua.h"
#include "lua533/include/lauxlib.h" #include "lua533/include/lauxlib.h"
#include "lua533/include/lualib.h" #include "lua533/include/lualib.h"
} }
#ifdef _WIN32 #ifdef _WIN32
#pragma comment(lib, "lua533/liblua53.a") #pragma comment(lib, "lua533/liblua53.a")
#endif #endif
/* /*
This is a singleton that stores all the games configuration settings. This is a singleton that stores all the games configuration settings.
These settings are loaded on game start up and are to be considered These settings are loaded on game start up and are to be considered
read-only. read-only.
*/ */
struct sAssetModel struct sAssetModel
{ {
std::string sCreator; std::string sCreator;
std::string sDescription; std::string sDescription;
std::string sModelOBJ; std::string sModelOBJ;
std::string sModelPNG; std::string sModelPNG;
float fRotate[3]; float fRotate[3];
float fScale[3]; float fScale[3];
float fTranslate[3]; float fTranslate[3];
}; };
struct sAssetTexture struct sAssetTexture
{ {
std::string sName; std::string sName;
std::string sFile; std::string sFile;
}; };
class cGameSettings class cGameSettings
{ {
public: public:
cGameSettings(); cGameSettings();
~cGameSettings(); ~cGameSettings();
public: public:
bool LoadConfigFile(std::string sFile); bool LoadConfigFile(std::string sFile);
public: public:
static int nScreenWidth; static int nScreenWidth;
static int nScreenHeight; static int nScreenHeight;
static int nPixelWidth; static int nPixelWidth;
static int nPixelHeight; static int nPixelHeight;
static bool bFullScreen; static bool bFullScreen;
static int nDefaultMapWidth; static int nDefaultMapWidth;
static int nDefaultMapHeight; static int nDefaultMapHeight;
static std::string sDefaultCityFile; static std::string sDefaultCityFile;
static std::vector<sAssetTexture> vecAssetTextures; static std::vector<sAssetTexture> vecAssetTextures;
static std::vector<sAssetModel> vecAssetBuildings; static std::vector<sAssetModel> vecAssetBuildings;
static std::vector<sAssetModel> vecAssetVehicles; static std::vector<sAssetModel> vecAssetVehicles;
}; };

@ -1,367 +1,367 @@
/* /*
Top Down City Based Car Crime Game - Part #2 Top Down City Based Car Crime Game - Part #2
"Colin, I hope you're shooting 600+ wherever you are buddy. RIP." - javidx9 "Colin, I hope you're shooting 600+ wherever you are buddy. RIP." - javidx9
License (OLC-3) License (OLC-3)
~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~
Copyright 2018-2019 OneLoneCoder.com Copyright 2018-2019 OneLoneCoder.com
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions modification, are permitted provided that the following conditions
are met: are met:
1. Redistributions or derivations of source code must retain the above 1. Redistributions or derivations of source code must retain the above
copyright notice, this list of conditions and the following disclaimer. copyright notice, this list of conditions and the following disclaimer.
2. Redistributions or derivative works in binary form must reproduce 2. Redistributions or derivative works in binary form must reproduce
the above copyright notice. This list of conditions and the following the above copyright notice. This list of conditions and the following
disclaimer must be reproduced in the documentation and/or other disclaimer must be reproduced in the documentation and/or other
materials provided with the distribution. materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its 3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived contributors may be used to endorse or promote products derived
from this software without specific prior written permission. from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Instructions: Instructions:
~~~~~~~~~~~~~ ~~~~~~~~~~~~~
Scroll with middle mouse wheel, TAB toggle edit mode, R to place road Scroll with middle mouse wheel, TAB toggle edit mode, R to place road
P to place pavement, Q to place building, Arrow keys to drive car P to place pavement, Q to place building, Arrow keys to drive car
Relevant Video: https://youtu.be/fIV6P1W-wuo Relevant Video: https://youtu.be/fIV6P1W-wuo
Links Links
~~~~~ ~~~~~
YouTube: https://www.youtube.com/javidx9 YouTube: https://www.youtube.com/javidx9
https://www.youtube.com/javidx9extra https://www.youtube.com/javidx9extra
Discord: https://discord.gg/WhwHUMV Discord: https://discord.gg/WhwHUMV
Twitter: https://www.twitter.com/javidx9 Twitter: https://www.twitter.com/javidx9
Twitch: https://www.twitch.tv/javidx9 Twitch: https://www.twitch.tv/javidx9
GitHub: https://www.github.com/onelonecoder GitHub: https://www.github.com/onelonecoder
Patreon: https://www.patreon.com/javidx9 Patreon: https://www.patreon.com/javidx9
Homepage: https://www.onelonecoder.com Homepage: https://www.onelonecoder.com
Author Author
~~~~~~ ~~~~~~
David Barr, aka javidx9, ©OneLoneCoder 2019 David Barr, aka javidx9, ©OneLoneCoder 2019
*/ */
#include "cGameSettings.h" #include "cGameSettings.h"
#include "cCarCrimeCity.h" #include "cCarCrimeCity.h"
int main() int main()
{ {
// Load the settings singleton // Load the settings singleton
cGameSettings config; cGameSettings config;
if (!config.LoadConfigFile("assets/config.lua")) if (!config.LoadConfigFile("assets/config.lua"))
{ {
std::cout << "Failed to load '/assets/config.lua'" << std::endl; std::cout << "Failed to load '/assets/config.lua'" << std::endl;
std::cout << " -> Using default configuration" << std::endl; std::cout << " -> Using default configuration" << std::endl;
} }
// Start the PixelGameEngine // Start the PixelGameEngine
cCarCrimeCity game; cCarCrimeCity game;
if (game.Construct(config.nScreenWidth, config.nScreenHeight, config.nPixelWidth, config.nPixelHeight, config.bFullScreen)) if (game.Construct(config.nScreenWidth, config.nScreenHeight, config.nPixelWidth, config.nPixelHeight, config.bFullScreen))
game.Start(); game.Start();
// Exit! // Exit!
return 0; return 0;
} }
//#define OLC_PGE_APPLICATION //#define OLC_PGE_APPLICATION
//#include "olcPixelGameEngine.h" //#include "olcPixelGameEngine.h"
// //
//#define OLC_PGEX_GRAPHICS3D //#define OLC_PGEX_GRAPHICS3D
//#include "olcPGEX_Graphics3D.h" //#include "olcPGEX_Graphics3D.h"
// //
// //
// //
//enum CELLTYPE //enum CELLTYPE
//{ //{
// CELL_BLANK = 0, // CELL_BLANK = 0,
// CELL_GRASS = 1, // CELL_GRASS = 1,
// CELL_CONCRETE = 2, // CELL_CONCRETE = 2,
// CELL_WATER = 3, // CELL_WATER = 3,
// CELL_BUILDING = 4, // CELL_BUILDING = 4,
// CELL_ROAD_H = 5, // CELL_ROAD_H = 5,
// CELL_ROAD_V = 6, // CELL_ROAD_V = 6,
// CELL_ROAD_C1 = 7, // CELL_ROAD_C1 = 7,
// CELL_ROAD_C2 = 8, // CELL_ROAD_C2 = 8,
// CELL_ROAD_C3 = 9, // CELL_ROAD_C3 = 9,
// CELL_ROAD_C4 = 10, // CELL_ROAD_C4 = 10,
// CELL_ROAD_T1 = 11, // CELL_ROAD_T1 = 11,
// CELL_ROAD_T2 = 12, // CELL_ROAD_T2 = 12,
// CELL_ROAD_T3 = 13, // CELL_ROAD_T3 = 13,
// CELL_ROAD_T4 = 14, // CELL_ROAD_T4 = 14,
// CELL_ROAD_X = 15, // CELL_ROAD_X = 15,
//}; //};
// //
//struct cCityCell //struct cCityCell
//{ //{
// int nType = 5;// CELL_GRASS; // int nType = 5;// CELL_GRASS;
//}; //};
// //
//class cCityMap //class cCityMap
//{ //{
//public: //public:
// // Construct a "blank" city w units wide by h units high // // Construct a "blank" city w units wide by h units high
// cCityMap(int w, int h); // cCityMap(int w, int h);
// //
// // Cleans up city, like Batman // // Cleans up city, like Batman
// ~cCityMap(); // ~cCityMap();
// //
// //
//public: //public:
// // Return width of city in cells // // Return width of city in cells
// int GetWidth(); // int GetWidth();
// // Return height of city in cells // // Return height of city in cells
// int GetHeight(); // int GetHeight();
// // Return a specific cell reference if inside city limits, or nullptr // // Return a specific cell reference if inside city limits, or nullptr
// cCityCell* Cell(int x, int y); // cCityCell* Cell(int x, int y);
// //
//private: //private:
// int m_nWidth = 0; // int m_nWidth = 0;
// int m_nHeight = 0; // int m_nHeight = 0;
// cCityCell *m_pCells = nullptr; // cCityCell *m_pCells = nullptr;
// //
//private: //private:
// // Creates a "default" city of specified size // // Creates a "default" city of specified size
// void CreateCity(int w, int h); // void CreateCity(int w, int h);
// // Destroy city // // Destroy city
// void ReleaseCity(); // void ReleaseCity();
//}; //};
// //
//cCityMap::cCityMap(int w, int h) //cCityMap::cCityMap(int w, int h)
//{ //{
// CreateCity(w, h); // CreateCity(w, h);
//} //}
// //
//cCityMap::~cCityMap() //cCityMap::~cCityMap()
//{ //{
// //ReleaseCity(); // //ReleaseCity();
//} //}
// //
//int cCityMap::GetWidth() //int cCityMap::GetWidth()
//{ //{
// return m_nWidth; // return m_nWidth;
//} //}
// //
//int cCityMap::GetHeight() //int cCityMap::GetHeight()
//{ //{
// return m_nHeight; // return m_nHeight;
//} //}
// //
//cCityCell* cCityMap::Cell(int x, int y) //cCityCell* cCityMap::Cell(int x, int y)
//{ //{
// if (x >= 0 && x < m_nWidth && y >= 0 && y < m_nHeight) // if (x >= 0 && x < m_nWidth && y >= 0 && y < m_nHeight)
// return &m_pCells[y*m_nWidth + x]; // return &m_pCells[y*m_nWidth + x];
// else // else
// return nullptr; // return nullptr;
//} //}
// //
//void cCityMap::CreateCity(int w, int h) //void cCityMap::CreateCity(int w, int h)
//{ //{
// //ReleaseCity(); // //ReleaseCity();
// m_nWidth = w; // m_nWidth = w;
// m_nHeight = h; // m_nHeight = h;
// m_pCells = new cCityCell[m_nHeight * m_nWidth]; // m_pCells = new cCityCell[m_nHeight * m_nWidth];
// //
// for (int x = 0; x < m_nWidth; x++) // for (int x = 0; x < m_nWidth; x++)
// { // {
// for (int y = 0; y < m_nHeight; y++) // for (int y = 0; y < m_nHeight; y++)
// { // {
// //m_pCells[y*m_nWidth + x] = new cCityCell(); // //m_pCells[y*m_nWidth + x] = new cCityCell();
// //Cell(x, y)->bRoad = false; // //Cell(x, y)->bRoad = false;
// //Cell(x, y)->nHeight = 0; // //Cell(x, y)->nHeight = 0;
// //Cell(x, y)->nWorldX = x; // //Cell(x, y)->nWorldX = x;
// //Cell(x, y)->nWorldY = y; // //Cell(x, y)->nWorldY = y;
// Cell(x, y)->nType = CELL_GRASS; // Cell(x, y)->nType = CELL_GRASS;
// //Cell(x, y)->bBuilding = false; // //Cell(x, y)->bBuilding = false;
// } // }
// } // }
//} //}
// //
//void cCityMap::ReleaseCity() //void cCityMap::ReleaseCity()
//{ //{
// if (m_pCells != nullptr) delete m_pCells; // if (m_pCells != nullptr) delete m_pCells;
// m_nWidth = 0; // m_nWidth = 0;
// m_nHeight = 0; // m_nHeight = 0;
//} //}
// //
// //
//class cCarCrimeCity : public olc::PixelGameEngine //class cCarCrimeCity : public olc::PixelGameEngine
//{ //{
//public: //public:
// cCarCrimeCity() // cCarCrimeCity()
// { // {
// sAppName = "Car Crime City"; // sAppName = "Car Crime City";
// } // }
// //
// ~cCarCrimeCity() // ~cCarCrimeCity()
// { // {
// } // }
// //
// bool OnUserCreate() // bool OnUserCreate()
// { // {
// // Initialise PGEX 3D // // Initialise PGEX 3D
// olc::GFX3D::ConfigureDisplay(); // olc::GFX3D::ConfigureDisplay();
// //
// // Create Default city // // Create Default city
// pCity = new cCityMap(64, 32);// cGameSettings::nDefaultMapWidth, cGameSettings::nDefaultMapHeight); // pCity = new cCityMap(64, 32);// cGameSettings::nDefaultMapWidth, cGameSettings::nDefaultMapHeight);
// //
// //
// // A simple flat unit quad // // A simple flat unit quad
// meshQuad.tris = // meshQuad.tris =
// { // {
// { 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, olc::RED }, // { 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, olc::RED },
// { 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, olc::RED}, // { 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, olc::RED},
// }; // };
// //
// //
// sprOld = new olc::Sprite("assets/system/grass1.png"); // sprOld = new olc::Sprite("assets/system/grass1.png");
// //
// //
// //
// SetDrawTarget(nullptr); // SetDrawTarget(nullptr);
// return true; // return true;
// } // }
// //
// //
// bool OnUserUpdate(float fElapsedTime) // bool OnUserUpdate(float fElapsedTime)
// { // {
// // User Input // // User Input
// if (GetKey(olc::Key::W).bHeld) vCamera.y -= 2.0f * fElapsedTime; // if (GetKey(olc::Key::W).bHeld) vCamera.y -= 2.0f * fElapsedTime;
// if (GetKey(olc::Key::S).bHeld) vCamera.y += 2.0f * fElapsedTime; // if (GetKey(olc::Key::S).bHeld) vCamera.y += 2.0f * fElapsedTime;
// if (GetKey(olc::Key::A).bHeld) vCamera.x -= 2.0f * fElapsedTime; // if (GetKey(olc::Key::A).bHeld) vCamera.x -= 2.0f * fElapsedTime;
// if (GetKey(olc::Key::D).bHeld) vCamera.x += 2.0f * fElapsedTime; // if (GetKey(olc::Key::D).bHeld) vCamera.x += 2.0f * fElapsedTime;
// if (GetKey(olc::Key::Z).bHeld) vCamera.z += 10.0f * fElapsedTime; // if (GetKey(olc::Key::Z).bHeld) vCamera.z += 10.0f * fElapsedTime;
// if (GetKey(olc::Key::X).bHeld) vCamera.z -= 10.0f * fElapsedTime; // if (GetKey(olc::Key::X).bHeld) vCamera.z -= 10.0f * fElapsedTime;
// //
// //
// vEye = vCamera; // vEye = vCamera;
// //
// // Perform Ray casting to calculate visible world extents and mouse position // // Perform Ray casting to calculate visible world extents and mouse position
// olc::GFX3D::vec3d vLookTarget = olc::GFX3D::Math::Vec_Add(vEye, vLookDir); // olc::GFX3D::vec3d vLookTarget = olc::GFX3D::Math::Vec_Add(vEye, vLookDir);
// olc::GFX3D::mat4x4 matProj = olc::GFX3D::Math::Mat_MakeProjection(90.0f, (float)ScreenHeight() / (float)ScreenWidth(), 0.5f, 1000.0f); // olc::GFX3D::mat4x4 matProj = olc::GFX3D::Math::Mat_MakeProjection(90.0f, (float)ScreenHeight() / (float)ScreenWidth(), 0.5f, 1000.0f);
// olc::GFX3D::mat4x4 matView = olc::GFX3D::Math::Mat_PointAt(vEye, vLookTarget, vUp); // olc::GFX3D::mat4x4 matView = olc::GFX3D::Math::Mat_PointAt(vEye, vLookTarget, vUp);
// //
// //
// //
// // Render Scene // // Render Scene
// Clear(olc::BLUE); // Clear(olc::BLUE);
// olc::GFX3D::ClearDepth(); // olc::GFX3D::ClearDepth();
// //
// // Create rendering pipeline // // Create rendering pipeline
// olc::GFX3D::PipeLine pipe; // olc::GFX3D::PipeLine pipe;
// pipe.SetProjection(90.0f, (float)ScreenHeight() / (float)ScreenWidth(), 0.5f, 1000.0f, 0.0f, 0.0f, (float)ScreenWidth(), (float)ScreenHeight()); // pipe.SetProjection(90.0f, (float)ScreenHeight() / (float)ScreenWidth(), 0.5f, 1000.0f, 0.0f, 0.0f, (float)ScreenWidth(), (float)ScreenHeight());
// pipe.SetCamera(vEye, vLookTarget, vUp); // pipe.SetCamera(vEye, vLookTarget, vUp);
// //
// //
// //
// int nStartX = 0; // int nStartX = 0;
// int nEndX = pCity->GetWidth(); // int nEndX = pCity->GetWidth();
// int nStartY = 0; // int nStartY = 0;
// int nEndY = pCity->GetHeight(); // int nEndY = pCity->GetHeight();
// //
// // Render Ground, Roads, Walls & Buildings // // Render Ground, Roads, Walls & Buildings
// for (int x = nStartX; x < nEndX; x++) // for (int x = nStartX; x < nEndX; x++)
// { // {
// if (x == 15) // if (x == 15)
// int k = 7; // int k = 7;
// //
// for (int y = nStartY; y < nEndY; y++) // for (int y = nStartY; y < nEndY; y++)
// { // {
// //
// //
// switch (pCity->Cell(x, y)->nType) // switch (pCity->Cell(x, y)->nType)
// { // {
// case CELL_GRASS: // case CELL_GRASS:
// { // {
// olc::GFX3D::mat4x4 matWorld; // olc::GFX3D::mat4x4 matWorld;
// matWorld = olc::GFX3D::Math::Mat_MakeTranslation((float)x, (float)y, 0.0f); // matWorld = olc::GFX3D::Math::Mat_MakeTranslation((float)x, (float)y, 0.0f);
// pipe.SetTransform(matWorld); // pipe.SetTransform(matWorld);
// pipe.SetTexture(sprOld); // pipe.SetTexture(sprOld);
// //pipe.SetTexture(vecSpriteSystem[0]); // //pipe.SetTexture(vecSpriteSystem[0]);
// //pipe.Render(vecMeshSystem[0].tris); // //pipe.Render(vecMeshSystem[0].tris);
// pipe.Render(meshQuad.tris); // pipe.Render(meshQuad.tris);
// //pipe.Render(vecMeshSystem[0].tris, olc::GFX3D::RENDER_FLAT); // //pipe.Render(vecMeshSystem[0].tris, olc::GFX3D::RENDER_FLAT);
// break; // break;
// } // }
// //
// //
// default: // default:
// { // {
// olc::GFX3D::mat4x4 matWorld; // olc::GFX3D::mat4x4 matWorld;
// matWorld = olc::GFX3D::Math::Mat_MakeTranslation((float)x, (float)y, 0.0f); // matWorld = olc::GFX3D::Math::Mat_MakeTranslation((float)x, (float)y, 0.0f);
// pipe.SetTransform(matWorld); // pipe.SetTransform(matWorld);
// pipe.Render(meshQuad.tris, olc::GFX3D::RENDER_WIRE); // pipe.Render(meshQuad.tris, olc::GFX3D::RENDER_WIRE);
// break; // break;
// } // }
// } // }
// //
// //
// //
// //
// } // }
// } // }
// //
// //
// //
// return true; // return true;
// } // }
// //
// bool OnUserDestroy() // bool OnUserDestroy()
// { // {
// return true; // return true;
// } // }
// //
// //
//private: //private:
// olc::GFX3D::vec3d vCamera = { 0.0f, 0.0f, -10.0f }; // olc::GFX3D::vec3d vCamera = { 0.0f, 0.0f, -10.0f };
// olc::GFX3D::vec3d vUp = { 0.0f, 1.0f, 0.0f }; // olc::GFX3D::vec3d vUp = { 0.0f, 1.0f, 0.0f };
// olc::GFX3D::vec3d vEye = { 0.0f, 0.0f, -10.0f }; // olc::GFX3D::vec3d vEye = { 0.0f, 0.0f, -10.0f };
// olc::GFX3D::vec3d vLookDir = { 0.0f, 0.0f, 1.0f }; // olc::GFX3D::vec3d vLookDir = { 0.0f, 0.0f, 1.0f };
// //
// //
// //
// olc::Sprite *sprOld = nullptr; // olc::Sprite *sprOld = nullptr;
// olc::GFX3D::mesh meshQuad; // olc::GFX3D::mesh meshQuad;
// //
// cCityMap *pCity = nullptr; // cCityMap *pCity = nullptr;
// //
// //
// //
//}; //};
// //
//int main() //int main()
//{ //{
// // Load the settings singleton // // Load the settings singleton
// /*cGameSettings config; // /*cGameSettings config;
// if (!config.LoadConfigFile("assets/config.lua")) // if (!config.LoadConfigFile("assets/config.lua"))
// { // {
// std::cout << "Failed to load '/assets/config.lua'" << std::endl; // std::cout << "Failed to load '/assets/config.lua'" << std::endl;
// std::cout << " -> Using default configuration" << std::endl; // std::cout << " -> Using default configuration" << std::endl;
// }*/ // }*/
// //
// // Start the PixelGameEngine // // Start the PixelGameEngine
// cCarCrimeCity game; // cCarCrimeCity game;
// if (game.Construct(256, 240, 4, 4))// config.nScreenWidth, config.nScreenHeight, config.nPixelWidth, config.nPixelHeight)) // if (game.Construct(256, 240, 4, 4))// config.nScreenWidth, config.nScreenHeight, config.nPixelWidth, config.nPixelHeight))
// game.Start(); // game.Start();
// //
// // Exit! // // Exit!
// return 0; // return 0;
//} //}

@ -1,5 +1,5 @@
#define OLC_PGE_APPLICATION #define OLC_PGE_APPLICATION
#include "olcPixelGameEngine.h" #include "olcPixelGameEngine.h"
#define OLC_PGEX_GRAPHICS3D #define OLC_PGEX_GRAPHICS3D
#include "olcPGEX_Graphics3D.h" #include "olcPGEX_Graphics3D.h"

@ -1,189 +1,189 @@
/* /*
OneLoneCoder_PGE_ExtensionTestGFX2D.cpp OneLoneCoder_PGE_ExtensionTestGFX2D.cpp
License (OLC-3) License (OLC-3)
~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~
Copyright 2018 OneLoneCoder.com Copyright 2018 OneLoneCoder.com
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions modification, are permitted provided that the following conditions
are met: are met:
1. Redistributions or derivations of source code must retain the above 1. Redistributions or derivations of source code must retain the above
copyright notice, this list of conditions and the following disclaimer. copyright notice, this list of conditions and the following disclaimer.
2. Redistributions or derivative works in binary form must reproduce 2. Redistributions or derivative works in binary form must reproduce
the above copyright notice. This list of conditions and the following the above copyright notice. This list of conditions and the following
disclaimer must be reproduced in the documentation and/or other disclaimer must be reproduced in the documentation and/or other
materials provided with the distribution. materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its 3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived contributors may be used to endorse or promote products derived
from this software without specific prior written permission. from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Links Links
~~~~~ ~~~~~
YouTube: https://www.youtube.com/javidx9 YouTube: https://www.youtube.com/javidx9
Discord: https://discord.gg/WhwHUMV Discord: https://discord.gg/WhwHUMV
Twitter: https://www.twitter.com/javidx9 Twitter: https://www.twitter.com/javidx9
Twitch: https://www.twitch.tv/javidx9 Twitch: https://www.twitch.tv/javidx9
GitHub: https://www.github.com/onelonecoder GitHub: https://www.github.com/onelonecoder
Homepage: https://www.onelonecoder.com Homepage: https://www.onelonecoder.com
Author Author
~~~~~~ ~~~~~~
David Barr, aka javidx9, ©OneLoneCoder 2018 David Barr, aka javidx9, ©OneLoneCoder 2018
*/ */
// Include the olcPixelGameEngine // Include the olcPixelGameEngine
#define OLC_PGE_APPLICATION #define OLC_PGE_APPLICATION
#include "olcPixelGameEngine.h" #include "olcPixelGameEngine.h"
// To use an extension, just include it // To use an extension, just include it
#define OLC_PGE_GRAPHICS2D #define OLC_PGE_GRAPHICS2D
#include "olcPGEX_Graphics2D.h" #include "olcPGEX_Graphics2D.h"
class TestExtension : public olc::PixelGameEngine class TestExtension : public olc::PixelGameEngine
{ {
public: public:
TestExtension() TestExtension()
{ {
sAppName = "Testing Graphics2D"; sAppName = "Testing Graphics2D";
} }
public: public:
bool OnUserCreate() override bool OnUserCreate() override
{ {
for (int i = 0; i < 16; i++) for (int i = 0; i < 16; i++)
listEvents.push_back(""); listEvents.push_back("");
spr = new olc::Sprite("new_piskel.png"); spr = new olc::Sprite("new_piskel.png");
return true; return true;
} }
std::list<std::string> listEvents; std::list<std::string> listEvents;
float fTotalTime = 0.0f; float fTotalTime = 0.0f;
olc::Sprite *spr; olc::Sprite *spr;
bool OnUserUpdate(float fElapsedTime) override bool OnUserUpdate(float fElapsedTime) override
{ {
// Clear Screen // Clear Screen
SetPixelMode(olc::Pixel::NORMAL); SetPixelMode(olc::Pixel::NORMAL);
Clear(olc::BLUE); Clear(olc::BLUE);
// Draw Primitives // Draw Primitives
DrawCircle(32, 32, 30); // Circle DrawCircle(32, 32, 30); // Circle
DrawCircle(96, 32, 30); // Circle DrawCircle(96, 32, 30); // Circle
float mx = (float)GetMouseX(); float mx = (float)GetMouseX();
float my = (float)GetMouseY(); float my = (float)GetMouseY();
float px1 = mx - 32, px2 = mx - 96; float px1 = mx - 32, px2 = mx - 96;
float py1 = my - 32, py2 = my - 32; float py1 = my - 32, py2 = my - 32;
float pr1 = 1.0f / sqrtf(px1*px1 + py1*py1); float pr1 = 1.0f / sqrtf(px1*px1 + py1*py1);
float pr2 = 1.0f / sqrtf(px2*px2 + py2*py2); float pr2 = 1.0f / sqrtf(px2*px2 + py2*py2);
px1 = 22.0f * (px1 * pr1) + 32.0f; px1 = 22.0f * (px1 * pr1) + 32.0f;
py1 = 22.0f * (py1 * pr1) + 32.0f; py1 = 22.0f * (py1 * pr1) + 32.0f;
px2 = 22.0f * (px2 * pr2) + 96.0f; px2 = 22.0f * (px2 * pr2) + 96.0f;
py2 = 22.0f * (py2 * pr2) + 32.0f; py2 = 22.0f * (py2 * pr2) + 32.0f;
FillCircle((int32_t)px1, (int32_t)py1, 8, olc::CYAN); FillCircle((int32_t)px1, (int32_t)py1, 8, olc::CYAN);
FillCircle((int32_t)px2, (int32_t)py2, 8, olc::CYAN); FillCircle((int32_t)px2, (int32_t)py2, 8, olc::CYAN);
DrawLine(10, 70, 54, 70); // Lines DrawLine(10, 70, 54, 70); // Lines
DrawLine(54, 70, 70, 54); DrawLine(54, 70, 70, 54);
DrawRect(10, 80, 54, 30); DrawRect(10, 80, 54, 30);
FillRect(10, 80, 54, 30); FillRect(10, 80, 54, 30);
// Multiline Text // Multiline Text
std::string mpos = "Your Mouse Position is:\nX=" + std::to_string(mx) + "\nY=" + std::to_string(my); std::string mpos = "Your Mouse Position is:\nX=" + std::to_string(mx) + "\nY=" + std::to_string(my);
DrawString(10, 130, mpos); DrawString(10, 130, mpos);
auto AddEvent = [&](std::string s) auto AddEvent = [&](std::string s)
{ {
listEvents.push_back(s); listEvents.push_back(s);
listEvents.pop_front(); listEvents.pop_front();
}; };
if (GetMouse(0).bPressed) AddEvent("Mouse Button 0 Down"); if (GetMouse(0).bPressed) AddEvent("Mouse Button 0 Down");
if (GetMouse(0).bReleased) AddEvent("Mouse Button 0 Up"); if (GetMouse(0).bReleased) AddEvent("Mouse Button 0 Up");
if (GetMouse(1).bPressed) AddEvent("Mouse Button 1 Down"); if (GetMouse(1).bPressed) AddEvent("Mouse Button 1 Down");
if (GetMouse(1).bReleased) AddEvent("Mouse Button 1 Up"); if (GetMouse(1).bReleased) AddEvent("Mouse Button 1 Up");
if (GetMouse(2).bPressed) AddEvent("Mouse Button 2 Down"); if (GetMouse(2).bPressed) AddEvent("Mouse Button 2 Down");
if (GetMouse(2).bReleased) AddEvent("Mouse Button 2 Up"); if (GetMouse(2).bReleased) AddEvent("Mouse Button 2 Up");
// Draw Event Log // Draw Event Log
int nLog = 0; int nLog = 0;
for (auto &s : listEvents) for (auto &s : listEvents)
{ {
DrawString(200, nLog * 8 + 20, s, olc::Pixel(nLog * 16, nLog * 16, nLog * 16)); DrawString(200, nLog * 8 + 20, s, olc::Pixel(nLog * 16, nLog * 16, nLog * 16));
nLog++; nLog++;
} }
std::string notes = "CDEFGAB"; std::string notes = "CDEFGAB";
// Test Text scaling and colours // Test Text scaling and colours
DrawString(0, 360, "Text Scale = 1", olc::WHITE, 1); DrawString(0, 360, "Text Scale = 1", olc::WHITE, 1);
DrawString(0, 368, "Text Scale = 2", olc::BLUE, 2); DrawString(0, 368, "Text Scale = 2", olc::BLUE, 2);
DrawString(0, 384, "Text Scale = 3", olc::RED, 3); DrawString(0, 384, "Text Scale = 3", olc::RED, 3);
DrawString(0, 408, "Text Scale = 4", olc::YELLOW, 4); DrawString(0, 408, "Text Scale = 4", olc::YELLOW, 4);
DrawString(0, 440, "Text Scale = 5", olc::GREEN, 5); DrawString(0, 440, "Text Scale = 5", olc::GREEN, 5);
fTotalTime += fElapsedTime; fTotalTime += fElapsedTime;
float fAngle = fTotalTime; float fAngle = fTotalTime;
// Draw Sprite using extension, first create a transformation stack // Draw Sprite using extension, first create a transformation stack
olc::GFX2D::Transform2D t1; olc::GFX2D::Transform2D t1;
// Traslate sprite so center of image is at 0,0 // Traslate sprite so center of image is at 0,0
t1.Translate(-250, -35); t1.Translate(-250, -35);
// Scale the sprite // Scale the sprite
t1.Scale(1 * sinf(fAngle) + 1, 1 * sinf(fAngle) + 1); t1.Scale(1 * sinf(fAngle) + 1, 1 * sinf(fAngle) + 1);
// Rotate it // Rotate it
t1.Rotate(fAngle*2.0f); t1.Rotate(fAngle*2.0f);
// Translate to 0,100 // Translate to 0,100
t1.Translate(0, 100); t1.Translate(0, 100);
// Rotate different speed // Rotate different speed
t1.Rotate(fAngle / 3); t1.Rotate(fAngle / 3);
// Translate to centre of screen // Translate to centre of screen
t1.Translate(320, 240); t1.Translate(320, 240);
SetPixelMode(olc::Pixel::ALPHA); SetPixelMode(olc::Pixel::ALPHA);
// Use extension to draw sprite with transform applied // Use extension to draw sprite with transform applied
olc::GFX2D::DrawSprite(spr, t1); olc::GFX2D::DrawSprite(spr, t1);
DrawSprite((int32_t)mx, (int32_t)my, spr, 4); DrawSprite((int32_t)mx, (int32_t)my, spr, 4);
return true; return true;
} }
}; };
int main() int main()
{ {
TestExtension demo; TestExtension demo;
if (demo.Construct(640, 480, 2, 2)) if (demo.Construct(640, 480, 2, 2))
demo.Start(); demo.Start();
return 0; return 0;
} }

@ -1,426 +1,426 @@
/* /*
OneLoneCoder.com - Path Finding #2 - Wave Propagation & Potential Fields OneLoneCoder.com - Path Finding #2 - Wave Propagation & Potential Fields
"...never get lost again, so long as you know where you are" - @Javidx9 "...never get lost again, so long as you know where you are" - @Javidx9
Background Background
~~~~~~~~~~ ~~~~~~~~~~
A nice follow up alternative to the A* Algorithm. Wave propagation is less A nice follow up alternative to the A* Algorithm. Wave propagation is less
applicable to multiple objects with multiple destinations, but fantatsic applicable to multiple objects with multiple destinations, but fantatsic
for multiple objects all reaching the same destination. for multiple objects all reaching the same destination.
WARNING! This code is NOT OPTIMAL!! It is however very robust. There WARNING! This code is NOT OPTIMAL!! It is however very robust. There
are many ways to optimise this further. are many ways to optimise this further.
License (OLC-3) License (OLC-3)
~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~
Copyright 2018 OneLoneCoder.com Copyright 2018 OneLoneCoder.com
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions modification, are permitted provided that the following conditions
are met: are met:
1. Redistributions or derivations of source code must retain the above 1. Redistributions or derivations of source code must retain the above
copyright notice, this list of conditions and the following disclaimer. copyright notice, this list of conditions and the following disclaimer.
2. Redistributions or derivative works in binary form must reproduce 2. Redistributions or derivative works in binary form must reproduce
the above copyright notice. This list of conditions and the following the above copyright notice. This list of conditions and the following
disclaimer must be reproduced in the documentation and/or other disclaimer must be reproduced in the documentation and/or other
materials provided with the distribution. materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its 3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived contributors may be used to endorse or promote products derived
from this software without specific prior written permission. from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Links Links
~~~~~ ~~~~~
YouTube: https://www.youtube.com/javidx9 YouTube: https://www.youtube.com/javidx9
Discord: https://discord.gg/WhwHUMV Discord: https://discord.gg/WhwHUMV
Twitter: https://www.twitter.com/javidx9 Twitter: https://www.twitter.com/javidx9
Twitch: https://www.twitch.tv/javidx9 Twitch: https://www.twitch.tv/javidx9
GitHub: https://www.github.com/onelonecoder GitHub: https://www.github.com/onelonecoder
Patreon: https://www.patreon/javidx9 Patreon: https://www.patreon/javidx9
Homepage: https://www.onelonecoder.com Homepage: https://www.onelonecoder.com
Relevant Videos Relevant Videos
~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~
Part #1 https://youtu.be/icZj67PTFhc Part #1 https://youtu.be/icZj67PTFhc
Part #2 https://youtu.be/0ihciMKlcP8 Part #2 https://youtu.be/0ihciMKlcP8
Author Author
~~~~~~ ~~~~~~
David Barr, aka javidx9, ©OneLoneCoder 2018 David Barr, aka javidx9, ©OneLoneCoder 2018
*/ */
#define OLC_PGE_APPLICATION #define OLC_PGE_APPLICATION
#include "olcPixelGameEngine.h" #include "olcPixelGameEngine.h"
#include <vector> #include <vector>
#include <list> #include <list>
#include <algorithm> #include <algorithm>
#include <utility> #include <utility>
// Override base class with your custom functionality // Override base class with your custom functionality
class PathFinding_FlowFields : public olc::PixelGameEngine class PathFinding_FlowFields : public olc::PixelGameEngine
{ {
public: public:
PathFinding_FlowFields() PathFinding_FlowFields()
{ {
sAppName = "PathFinding - Flow Fields"; sAppName = "PathFinding - Flow Fields";
} }
private: private:
int nMapWidth; int nMapWidth;
int nMapHeight; int nMapHeight;
int nCellSize; int nCellSize;
int nBorderWidth; int nBorderWidth;
bool *bObstacleMap; bool *bObstacleMap;
int *nFlowFieldZ; int *nFlowFieldZ;
float *fFlowFieldY; float *fFlowFieldY;
float *fFlowFieldX; float *fFlowFieldX;
int nStartX; int nStartX;
int nStartY; int nStartY;
int nEndX; int nEndX;
int nEndY; int nEndY;
int nWave = 1; int nWave = 1;
public: public:
bool OnUserCreate() override bool OnUserCreate() override
{ {
nBorderWidth = 4; nBorderWidth = 4;
nCellSize = 32; nCellSize = 32;
nMapWidth = ScreenWidth() / nCellSize; nMapWidth = ScreenWidth() / nCellSize;
nMapHeight = ScreenHeight() / nCellSize; nMapHeight = ScreenHeight() / nCellSize;
bObstacleMap = new bool[nMapWidth * nMapHeight]{ false }; bObstacleMap = new bool[nMapWidth * nMapHeight]{ false };
nFlowFieldZ = new int[nMapWidth * nMapHeight]{ 0 }; nFlowFieldZ = new int[nMapWidth * nMapHeight]{ 0 };
fFlowFieldX = new float[nMapWidth * nMapHeight]{ 0 }; fFlowFieldX = new float[nMapWidth * nMapHeight]{ 0 };
fFlowFieldY = new float[nMapWidth * nMapHeight]{ 0 }; fFlowFieldY = new float[nMapWidth * nMapHeight]{ 0 };
nStartX = 3; nStartX = 3;
nStartY = 7; nStartY = 7;
nEndX = 12; nEndX = 12;
nEndY = 7; nEndY = 7;
return true; return true;
} }
bool OnUserUpdate(float fElapsedTime) override bool OnUserUpdate(float fElapsedTime) override
{ {
// Little convenience lambda 2D -> 1D // Little convenience lambda 2D -> 1D
auto p = [&](int x, int y) { return y * nMapWidth + x; }; auto p = [&](int x, int y) { return y * nMapWidth + x; };
// User Input // User Input
int nSelectedCellX = GetMouseX() / nCellSize; int nSelectedCellX = GetMouseX() / nCellSize;
int nSelectedCellY = GetMouseY() / nCellSize; int nSelectedCellY = GetMouseY() / nCellSize;
if (GetMouse(0).bReleased) if (GetMouse(0).bReleased)
{ {
// Toggle Obstacle if mouse left clicked // Toggle Obstacle if mouse left clicked
bObstacleMap[p(nSelectedCellX, nSelectedCellY)] = bObstacleMap[p(nSelectedCellX, nSelectedCellY)] =
!bObstacleMap[p(nSelectedCellX, nSelectedCellY)]; !bObstacleMap[p(nSelectedCellX, nSelectedCellY)];
} }
if (GetMouse(1).bReleased) if (GetMouse(1).bReleased)
{ {
nStartX = nSelectedCellX; nStartX = nSelectedCellX;
nStartY = nSelectedCellY; nStartY = nSelectedCellY;
} }
if (GetKey(olc::Key::Q).bReleased) if (GetKey(olc::Key::Q).bReleased)
{ {
nWave++; nWave++;
} }
if (GetKey(olc::Key::A).bReleased) if (GetKey(olc::Key::A).bReleased)
{ {
nWave--; nWave--;
if (nWave == 0) if (nWave == 0)
nWave = 1; nWave = 1;
} }
// 1) Prepare flow field, add a boundary, and add obstacles // 1) Prepare flow field, add a boundary, and add obstacles
// by setting the flow Field Height (Z) to -1 // by setting the flow Field Height (Z) to -1
for (int x = 0; x < nMapWidth; x++) for (int x = 0; x < nMapWidth; x++)
{ {
for (int y = 0; y < nMapHeight; y++) for (int y = 0; y < nMapHeight; y++)
{ {
// Set border or obstacles // Set border or obstacles
if (x == 0 || y == 0 || x == (nMapWidth - 1) || y == (nMapHeight - 1) || bObstacleMap[p(x, y)]) if (x == 0 || y == 0 || x == (nMapWidth - 1) || y == (nMapHeight - 1) || bObstacleMap[p(x, y)])
{ {
nFlowFieldZ[p(x, y)] = -1; nFlowFieldZ[p(x, y)] = -1;
} }
else else
{ {
nFlowFieldZ[p(x, y)] = 0; nFlowFieldZ[p(x, y)] = 0;
} }
} }
} }
// 2) Propagate a wave (i.e. flood-fill) from target location. Here I use // 2) Propagate a wave (i.e. flood-fill) from target location. Here I use
// a tuple, of {x, y, distance} - though you could use a struct or // a tuple, of {x, y, distance} - though you could use a struct or
// similar. // similar.
std::list<std::tuple<int, int, int>> nodes; std::list<std::tuple<int, int, int>> nodes;
// Add the first discovered node - the target location, with a distance of 1 // Add the first discovered node - the target location, with a distance of 1
nodes.push_back({ nEndX, nEndY, 1 }); nodes.push_back({ nEndX, nEndY, 1 });
while (!nodes.empty()) while (!nodes.empty())
{ {
// Each iteration through the discovered nodes may create newly discovered // Each iteration through the discovered nodes may create newly discovered
// nodes, so I maintain a second list. It's important not to contaminate // nodes, so I maintain a second list. It's important not to contaminate
// the list being iterated through. // the list being iterated through.
std::list<std::tuple<int, int, int>> new_nodes; std::list<std::tuple<int, int, int>> new_nodes;
// Now iterate through each discovered node. If it has neighbouring nodes // Now iterate through each discovered node. If it has neighbouring nodes
// that are empty space and undiscovered, add those locations to the // that are empty space and undiscovered, add those locations to the
// new nodes list // new nodes list
for (auto &n : nodes) for (auto &n : nodes)
{ {
int x = std::get<0>(n); // Map X-Coordinate int x = std::get<0>(n); // Map X-Coordinate
int y = std::get<1>(n); // Map Y-Coordinate int y = std::get<1>(n); // Map Y-Coordinate
int d = std::get<2>(n); // Distance From Target Location int d = std::get<2>(n); // Distance From Target Location
// Set distance count for this node. NOte that when we add nodes we add 1 // Set distance count for this node. NOte that when we add nodes we add 1
// to this distance. This emulates propagating a wave across the map, where // to this distance. This emulates propagating a wave across the map, where
// the front of that wave increments each iteration. In this way, we can // the front of that wave increments each iteration. In this way, we can
// propagate distance information 'away from target location' // propagate distance information 'away from target location'
nFlowFieldZ[p(x, y)] = d; nFlowFieldZ[p(x, y)] = d;
// Add neigbour nodes if unmarked, i.e their "height" is 0. Any discovered // Add neigbour nodes if unmarked, i.e their "height" is 0. Any discovered
// node or obstacle will be non-zero // node or obstacle will be non-zero
// Check East // Check East
if ((x + 1) < nMapWidth && nFlowFieldZ[p(x + 1, y)] == 0) if ((x + 1) < nMapWidth && nFlowFieldZ[p(x + 1, y)] == 0)
new_nodes.push_back({ x + 1, y, d + 1 }); new_nodes.push_back({ x + 1, y, d + 1 });
// Check West // Check West
if ((x - 1) >= 0 && nFlowFieldZ[p(x - 1, y)] == 0) if ((x - 1) >= 0 && nFlowFieldZ[p(x - 1, y)] == 0)
new_nodes.push_back({ x - 1, y, d + 1 }); new_nodes.push_back({ x - 1, y, d + 1 });
// Check South // Check South
if ((y + 1) < nMapHeight && nFlowFieldZ[p(x, y + 1)] == 0) if ((y + 1) < nMapHeight && nFlowFieldZ[p(x, y + 1)] == 0)
new_nodes.push_back({ x, y + 1, d + 1 }); new_nodes.push_back({ x, y + 1, d + 1 });
// Check North // Check North
if ((y - 1) >= 0 && nFlowFieldZ[p(x, y - 1)] == 0) if ((y - 1) >= 0 && nFlowFieldZ[p(x, y - 1)] == 0)
new_nodes.push_back({ x, y - 1, d + 1 }); new_nodes.push_back({ x, y - 1, d + 1 });
} }
// We will now have potentially multiple nodes for a single location. This means our // We will now have potentially multiple nodes for a single location. This means our
// algorithm will never complete! So we must remove duplicates form our new node list. // algorithm will never complete! So we must remove duplicates form our new node list.
// Im doing this with some clever code - but it is not performant(!) - it is merely // Im doing this with some clever code - but it is not performant(!) - it is merely
// convenient. I'd suggest doing away with overhead structures like linked lists and sorts // convenient. I'd suggest doing away with overhead structures like linked lists and sorts
// if you are aiming for fastest path finding. // if you are aiming for fastest path finding.
// Sort the nodes - This will stack up nodes that are similar: A, B, B, B, B, C, D, D, E, F, F // Sort the nodes - This will stack up nodes that are similar: A, B, B, B, B, C, D, D, E, F, F
new_nodes.sort([&](const std::tuple<int, int, int> &n1, const std::tuple<int, int, int> &n2) new_nodes.sort([&](const std::tuple<int, int, int> &n1, const std::tuple<int, int, int> &n2)
{ {
// In this instance I dont care how the values are sorted, so long as nodes that // In this instance I dont care how the values are sorted, so long as nodes that
// represent the same location are adjacent in the list. I can use the p() lambda // represent the same location are adjacent in the list. I can use the p() lambda
// to generate a unique 1D value for a 2D coordinate, so I'll sort by that. // to generate a unique 1D value for a 2D coordinate, so I'll sort by that.
return p(std::get<0>(n1), std::get<1>(n1)) < p(std::get<0>(n2), std::get<1>(n2)); return p(std::get<0>(n1), std::get<1>(n1)) < p(std::get<0>(n2), std::get<1>(n2));
}); });
// Use "unique" function to remove adjacent duplicates : A, B, -, -, -, C, D, -, E, F - // Use "unique" function to remove adjacent duplicates : A, B, -, -, -, C, D, -, E, F -
// and also erase them : A, B, C, D, E, F // and also erase them : A, B, C, D, E, F
new_nodes.unique([&](const std::tuple<int, int, int> &n1, const std::tuple<int, int, int> &n2) new_nodes.unique([&](const std::tuple<int, int, int> &n1, const std::tuple<int, int, int> &n2)
{ {
return p(std::get<0>(n1), std::get<1>(n1)) == p(std::get<0>(n2), std::get<1>(n2)); return p(std::get<0>(n1), std::get<1>(n1)) == p(std::get<0>(n2), std::get<1>(n2));
}); });
// We've now processed all the discoverd nodes, so clear the list, and add the newly // We've now processed all the discoverd nodes, so clear the list, and add the newly
// discovered nodes for processing on the next iteration // discovered nodes for processing on the next iteration
nodes.clear(); nodes.clear();
nodes.insert(nodes.begin(), new_nodes.begin(), new_nodes.end()); nodes.insert(nodes.begin(), new_nodes.begin(), new_nodes.end());
// When there are no more newly discovered nodes, we have "flood filled" the entire // When there are no more newly discovered nodes, we have "flood filled" the entire
// map. The propagation phase of the algorithm is complete // map. The propagation phase of the algorithm is complete
} }
// 3) Create Path. Starting a start location, create a path of nodes until you reach target // 3) Create Path. Starting a start location, create a path of nodes until you reach target
// location. At each node find the neighbour with the lowest "distance" score. // location. At each node find the neighbour with the lowest "distance" score.
std::list<std::pair<int, int>> path; std::list<std::pair<int, int>> path;
path.push_back({ nStartX, nStartY }); path.push_back({ nStartX, nStartY });
int nLocX = nStartX; int nLocX = nStartX;
int nLocY = nStartY; int nLocY = nStartY;
bool bNoPath = false; bool bNoPath = false;
while (!(nLocX == nEndX && nLocY == nEndY) && !bNoPath) while (!(nLocX == nEndX && nLocY == nEndY) && !bNoPath)
{ {
std::list<std::tuple<int, int, int>> listNeighbours; std::list<std::tuple<int, int, int>> listNeighbours;
// 4-Way Connectivity // 4-Way Connectivity
if ((nLocY - 1) >= 0 && nFlowFieldZ[p(nLocX, nLocY - 1)] > 0) if ((nLocY - 1) >= 0 && nFlowFieldZ[p(nLocX, nLocY - 1)] > 0)
listNeighbours.push_back({ nLocX, nLocY - 1, nFlowFieldZ[p(nLocX, nLocY - 1)] }); listNeighbours.push_back({ nLocX, nLocY - 1, nFlowFieldZ[p(nLocX, nLocY - 1)] });
if ((nLocX + 1) < nMapWidth && nFlowFieldZ[p(nLocX + 1, nLocY)] > 0) if ((nLocX + 1) < nMapWidth && nFlowFieldZ[p(nLocX + 1, nLocY)] > 0)
listNeighbours.push_back({ nLocX + 1, nLocY, nFlowFieldZ[p(nLocX + 1, nLocY)] }); listNeighbours.push_back({ nLocX + 1, nLocY, nFlowFieldZ[p(nLocX + 1, nLocY)] });
if ((nLocY + 1) < nMapHeight && nFlowFieldZ[p(nLocX, nLocY + 1)] > 0) if ((nLocY + 1) < nMapHeight && nFlowFieldZ[p(nLocX, nLocY + 1)] > 0)
listNeighbours.push_back({ nLocX, nLocY + 1, nFlowFieldZ[p(nLocX, nLocY + 1)] }); listNeighbours.push_back({ nLocX, nLocY + 1, nFlowFieldZ[p(nLocX, nLocY + 1)] });
if ((nLocX - 1) >= 0 && nFlowFieldZ[p(nLocX - 1, nLocY)] > 0) if ((nLocX - 1) >= 0 && nFlowFieldZ[p(nLocX - 1, nLocY)] > 0)
listNeighbours.push_back({ nLocX - 1, nLocY, nFlowFieldZ[p(nLocX - 1, nLocY)] }); listNeighbours.push_back({ nLocX - 1, nLocY, nFlowFieldZ[p(nLocX - 1, nLocY)] });
// 8-Way Connectivity // 8-Way Connectivity
if ((nLocY - 1) >= 0 && (nLocX - 1) >= 0 && nFlowFieldZ[p(nLocX - 1, nLocY - 1)] > 0) if ((nLocY - 1) >= 0 && (nLocX - 1) >= 0 && nFlowFieldZ[p(nLocX - 1, nLocY - 1)] > 0)
listNeighbours.push_back({ nLocX - 1, nLocY - 1, nFlowFieldZ[p(nLocX - 1, nLocY - 1)] }); listNeighbours.push_back({ nLocX - 1, nLocY - 1, nFlowFieldZ[p(nLocX - 1, nLocY - 1)] });
if ((nLocY - 1) >= 0 && (nLocX + 1) < nMapWidth && nFlowFieldZ[p(nLocX + 1, nLocY - 1)] > 0) if ((nLocY - 1) >= 0 && (nLocX + 1) < nMapWidth && nFlowFieldZ[p(nLocX + 1, nLocY - 1)] > 0)
listNeighbours.push_back({ nLocX + 1, nLocY - 1, nFlowFieldZ[p(nLocX + 1, nLocY - 1)] }); listNeighbours.push_back({ nLocX + 1, nLocY - 1, nFlowFieldZ[p(nLocX + 1, nLocY - 1)] });
if ((nLocY + 1) < nMapHeight && (nLocX - 1) >= 0 && nFlowFieldZ[p(nLocX - 1, nLocY + 1)] > 0) if ((nLocY + 1) < nMapHeight && (nLocX - 1) >= 0 && nFlowFieldZ[p(nLocX - 1, nLocY + 1)] > 0)
listNeighbours.push_back({ nLocX - 1, nLocY + 1, nFlowFieldZ[p(nLocX - 1, nLocY + 1)] }); listNeighbours.push_back({ nLocX - 1, nLocY + 1, nFlowFieldZ[p(nLocX - 1, nLocY + 1)] });
if ((nLocY + 1) < nMapHeight && (nLocX + 1) < nMapWidth && nFlowFieldZ[p(nLocX + 1, nLocY + 1)] > 0) if ((nLocY + 1) < nMapHeight && (nLocX + 1) < nMapWidth && nFlowFieldZ[p(nLocX + 1, nLocY + 1)] > 0)
listNeighbours.push_back({ nLocX + 1, nLocY + 1, nFlowFieldZ[p(nLocX + 1, nLocY + 1)] }); listNeighbours.push_back({ nLocX + 1, nLocY + 1, nFlowFieldZ[p(nLocX + 1, nLocY + 1)] });
// Sprt neigbours based on height, so lowest neighbour is at front // Sprt neigbours based on height, so lowest neighbour is at front
// of list // of list
listNeighbours.sort([&](const std::tuple<int, int, int> &n1, const std::tuple<int, int, int> &n2) listNeighbours.sort([&](const std::tuple<int, int, int> &n1, const std::tuple<int, int, int> &n2)
{ {
return std::get<2>(n1) < std::get<2>(n2); // Compare distances return std::get<2>(n1) < std::get<2>(n2); // Compare distances
}); });
if (listNeighbours.empty()) // Neighbour is invalid or no possible path if (listNeighbours.empty()) // Neighbour is invalid or no possible path
bNoPath = true; bNoPath = true;
else else
{ {
nLocX = std::get<0>(listNeighbours.front()); nLocX = std::get<0>(listNeighbours.front());
nLocY = std::get<1>(listNeighbours.front()); nLocY = std::get<1>(listNeighbours.front());
path.push_back({ nLocX, nLocY }); path.push_back({ nLocX, nLocY });
} }
} }
// 4) Create Flow "Field" // 4) Create Flow "Field"
for (int x = 1; x < nMapWidth - 1; x++) for (int x = 1; x < nMapWidth - 1; x++)
{ {
for (int y = 1; y < nMapHeight - 1; y++) for (int y = 1; y < nMapHeight - 1; y++)
{ {
float vx = 0.0f; float vx = 0.0f;
float vy = 0.0f; float vy = 0.0f;
vy -= (float)((nFlowFieldZ[p(x, y + 1)] <= 0 ? nFlowFieldZ[p(x, y)] : nFlowFieldZ[p(x, y + 1)]) - nFlowFieldZ[p(x, y)]); vy -= (float)((nFlowFieldZ[p(x, y + 1)] <= 0 ? nFlowFieldZ[p(x, y)] : nFlowFieldZ[p(x, y + 1)]) - nFlowFieldZ[p(x, y)]);
vx -= (float)((nFlowFieldZ[p(x + 1, y)] <= 0 ? nFlowFieldZ[p(x, y)] : nFlowFieldZ[p(x + 1, y)]) - nFlowFieldZ[p(x, y)]); vx -= (float)((nFlowFieldZ[p(x + 1, y)] <= 0 ? nFlowFieldZ[p(x, y)] : nFlowFieldZ[p(x + 1, y)]) - nFlowFieldZ[p(x, y)]);
vy += (float)((nFlowFieldZ[p(x, y - 1)] <= 0 ? nFlowFieldZ[p(x, y)] : nFlowFieldZ[p(x, y - 1)]) - nFlowFieldZ[p(x, y)]); vy += (float)((nFlowFieldZ[p(x, y - 1)] <= 0 ? nFlowFieldZ[p(x, y)] : nFlowFieldZ[p(x, y - 1)]) - nFlowFieldZ[p(x, y)]);
vx += (float)((nFlowFieldZ[p(x - 1, y)] <= 0 ? nFlowFieldZ[p(x, y)] : nFlowFieldZ[p(x - 1, y)]) - nFlowFieldZ[p(x, y)]); vx += (float)((nFlowFieldZ[p(x - 1, y)] <= 0 ? nFlowFieldZ[p(x, y)] : nFlowFieldZ[p(x - 1, y)]) - nFlowFieldZ[p(x, y)]);
float r = 1.0f / sqrtf(vx*vx + vy * vy); float r = 1.0f / sqrtf(vx*vx + vy * vy);
fFlowFieldX[p(x, y)] = vx * r; fFlowFieldX[p(x, y)] = vx * r;
fFlowFieldY[p(x, y)] = vy * r; fFlowFieldY[p(x, y)] = vy * r;
} }
} }
// Draw Map // Draw Map
Clear(olc::BLACK); Clear(olc::BLACK);
for (int x = 0; x < nMapWidth; x++) for (int x = 0; x < nMapWidth; x++)
{ {
for (int y = 0; y < nMapHeight; y++) for (int y = 0; y < nMapHeight; y++)
{ {
olc::Pixel colour = olc::BLUE; olc::Pixel colour = olc::BLUE;
if (bObstacleMap[p(x, y)]) if (bObstacleMap[p(x, y)])
colour = olc::GREY; colour = olc::GREY;
if (nWave == nFlowFieldZ[p(x, y)]) if (nWave == nFlowFieldZ[p(x, y)])
colour = olc::DARK_CYAN; colour = olc::DARK_CYAN;
if (x == nStartX && y == nStartY) if (x == nStartX && y == nStartY)
colour = olc::GREEN; colour = olc::GREEN;
if (x == nEndX && y == nEndY) if (x == nEndX && y == nEndY)
colour = olc::RED; colour = olc::RED;
// Draw Base // Draw Base
FillRect(x * nCellSize, y * nCellSize, nCellSize - nBorderWidth, nCellSize - nBorderWidth, colour); FillRect(x * nCellSize, y * nCellSize, nCellSize - nBorderWidth, nCellSize - nBorderWidth, colour);
// Draw "potential" or "distance" or "height" :D // Draw "potential" or "distance" or "height" :D
//DrawString(x * nCellSize, y * nCellSize, std::to_string(nFlowFieldZ[p(x, y)]), olc::WHITE); //DrawString(x * nCellSize, y * nCellSize, std::to_string(nFlowFieldZ[p(x, y)]), olc::WHITE);
if (nFlowFieldZ[p(x, y)] > 0) if (nFlowFieldZ[p(x, y)] > 0)
{ {
float ax[4], ay[4]; float ax[4], ay[4];
float fAngle = atan2f(fFlowFieldY[p(x, y)], fFlowFieldX[p(x, y)]); float fAngle = atan2f(fFlowFieldY[p(x, y)], fFlowFieldX[p(x, y)]);
float fRadius = (float)(nCellSize - nBorderWidth) / 2.0f; float fRadius = (float)(nCellSize - nBorderWidth) / 2.0f;
int fOffsetX = x * nCellSize + ((nCellSize - nBorderWidth) / 2); int fOffsetX = x * nCellSize + ((nCellSize - nBorderWidth) / 2);
int fOffsetY = y * nCellSize + ((nCellSize - nBorderWidth) / 2); int fOffsetY = y * nCellSize + ((nCellSize - nBorderWidth) / 2);
ax[0] = cosf(fAngle) * fRadius + fOffsetX; ax[0] = cosf(fAngle) * fRadius + fOffsetX;
ay[0] = sinf(fAngle) * fRadius + fOffsetY; ay[0] = sinf(fAngle) * fRadius + fOffsetY;
ax[1] = cosf(fAngle) * -fRadius + fOffsetX; ax[1] = cosf(fAngle) * -fRadius + fOffsetX;
ay[1] = sinf(fAngle) * -fRadius + fOffsetY; ay[1] = sinf(fAngle) * -fRadius + fOffsetY;
ax[2] = cosf(fAngle + 0.1f) * fRadius * 0.7f + fOffsetX; ax[2] = cosf(fAngle + 0.1f) * fRadius * 0.7f + fOffsetX;
ay[2] = sinf(fAngle + 0.1f) * fRadius * 0.7f + fOffsetY; ay[2] = sinf(fAngle + 0.1f) * fRadius * 0.7f + fOffsetY;
ax[3] = cosf(fAngle - 0.1f) * fRadius * 0.7f + fOffsetX; ax[3] = cosf(fAngle - 0.1f) * fRadius * 0.7f + fOffsetX;
ay[3] = sinf(fAngle - 0.1f) * fRadius * 0.7f + fOffsetY; ay[3] = sinf(fAngle - 0.1f) * fRadius * 0.7f + fOffsetY;
DrawLine(ax[0], ay[0], ax[1], ay[1], olc::CYAN); DrawLine(ax[0], ay[0], ax[1], ay[1], olc::CYAN);
DrawLine(ax[0], ay[0], ax[2], ay[2], olc::CYAN); DrawLine(ax[0], ay[0], ax[2], ay[2], olc::CYAN);
DrawLine(ax[0], ay[0], ax[3], ay[3], olc::CYAN); DrawLine(ax[0], ay[0], ax[3], ay[3], olc::CYAN);
} }
} }
} }
bool bFirstPoint = true; bool bFirstPoint = true;
int ox, oy; int ox, oy;
for (auto &a : path) for (auto &a : path)
{ {
if (bFirstPoint) if (bFirstPoint)
{ {
ox = a.first; ox = a.first;
oy = a.second; oy = a.second;
bFirstPoint = false; bFirstPoint = false;
} }
else else
{ {
DrawLine( DrawLine(
ox * nCellSize + ((nCellSize - nBorderWidth) / 2), ox * nCellSize + ((nCellSize - nBorderWidth) / 2),
oy * nCellSize + ((nCellSize - nBorderWidth) / 2), oy * nCellSize + ((nCellSize - nBorderWidth) / 2),
a.first * nCellSize + ((nCellSize - nBorderWidth) / 2), a.first * nCellSize + ((nCellSize - nBorderWidth) / 2),
a.second * nCellSize + ((nCellSize - nBorderWidth) / 2), olc::YELLOW); a.second * nCellSize + ((nCellSize - nBorderWidth) / 2), olc::YELLOW);
ox = a.first; ox = a.first;
oy = a.second; oy = a.second;
FillCircle(ox * nCellSize + ((nCellSize - nBorderWidth) / 2), oy * nCellSize + ((nCellSize - nBorderWidth) / 2), 10, olc::YELLOW); FillCircle(ox * nCellSize + ((nCellSize - nBorderWidth) / 2), oy * nCellSize + ((nCellSize - nBorderWidth) / 2), 10, olc::YELLOW);
} }
} }
return true; return true;
} }
}; };
int main() int main()
{ {
PathFinding_FlowFields demo; PathFinding_FlowFields demo;
if (demo.Construct(512, 480, 2, 2)) if (demo.Construct(512, 480, 2, 2))
demo.Start(); demo.Start();
return 0; return 0;
} }

@ -1,434 +1,434 @@
/* /*
Convex Polygon Collision Detection Convex Polygon Collision Detection
"Don't you dare try concave ones..." - javidx9 "Don't you dare try concave ones..." - javidx9
License (OLC-3) License (OLC-3)
~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~
Copyright 2018-2019 OneLoneCoder.com Copyright 2018-2019 OneLoneCoder.com
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions modification, are permitted provided that the following conditions
are met: are met:
1. Redistributions or derivations of source code must retain the above 1. Redistributions or derivations of source code must retain the above
copyright notice, this list of conditions and the following disclaimer. copyright notice, this list of conditions and the following disclaimer.
2. Redistributions or derivative works in binary form must reproduce 2. Redistributions or derivative works in binary form must reproduce
the above copyright notice. This list of conditions and the following the above copyright notice. This list of conditions and the following
disclaimer must be reproduced in the documentation and/or other disclaimer must be reproduced in the documentation and/or other
materials provided with the distribution. materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its 3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived contributors may be used to endorse or promote products derived
from this software without specific prior written permission. from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Instructions: Instructions:
~~~~~~~~~~~~~ ~~~~~~~~~~~~~
Use arrow keys to control pentagon Use arrow keys to control pentagon
Use WASD to control triangle Use WASD to control triangle
F1..F4 selects algorithm F1..F4 selects algorithm
Relevant Video: https://youtu.be/7Ik2vowGcU0 Relevant Video: https://youtu.be/7Ik2vowGcU0
Links Links
~~~~~ ~~~~~
YouTube: https://www.youtube.com/javidx9 YouTube: https://www.youtube.com/javidx9
https://www.youtube.com/javidx9extra https://www.youtube.com/javidx9extra
Discord: https://discord.gg/WhwHUMV Discord: https://discord.gg/WhwHUMV
Twitter: https://www.twitter.com/javidx9 Twitter: https://www.twitter.com/javidx9
Twitch: https://www.twitch.tv/javidx9 Twitch: https://www.twitch.tv/javidx9
GitHub: https://www.github.com/onelonecoder GitHub: https://www.github.com/onelonecoder
Patreon: https://www.patreon.com/javidx9 Patreon: https://www.patreon.com/javidx9
Homepage: https://www.onelonecoder.com Homepage: https://www.onelonecoder.com
Author Author
~~~~~~ ~~~~~~
David Barr, aka javidx9, ©OneLoneCoder 2019 David Barr, aka javidx9, ©OneLoneCoder 2019
*/ */
#define OLC_PGE_APPLICATION #define OLC_PGE_APPLICATION
#include "olcPixelGameEngine.h" #include "olcPixelGameEngine.h"
#include <vector> #include <vector>
#include <algorithm> #include <algorithm>
// Override base class with your custom functionality // Override base class with your custom functionality
class PolygonCollisions : public olc::PixelGameEngine class PolygonCollisions : public olc::PixelGameEngine
{ {
public: public:
PolygonCollisions() PolygonCollisions()
{ {
sAppName = "Polygon Collisions"; sAppName = "Polygon Collisions";
} }
struct vec2d struct vec2d
{ {
float x; float x;
float y; float y;
}; };
struct polygon struct polygon
{ {
std::vector<vec2d> p; // Transformed Points std::vector<vec2d> p; // Transformed Points
vec2d pos; // Position of shape vec2d pos; // Position of shape
float angle; // Direction of shape float angle; // Direction of shape
std::vector<vec2d> o; // "Model" of shape std::vector<vec2d> o; // "Model" of shape
bool overlap = false; // Flag to indicate if overlap has occurred bool overlap = false; // Flag to indicate if overlap has occurred
}; };
std::vector<polygon> vecShapes; std::vector<polygon> vecShapes;
int nMode = 0; int nMode = 0;
public: public:
bool OnUserCreate() override bool OnUserCreate() override
{ {
// Create Pentagon // Create Pentagon
polygon s1; polygon s1;
float fTheta = 3.14159f * 2.0f / 5.0f; float fTheta = 3.14159f * 2.0f / 5.0f;
s1.pos = { 100, 100 }; s1.pos = { 100, 100 };
s1.angle = 0.0f; s1.angle = 0.0f;
for (int i = 0; i < 5; i++) for (int i = 0; i < 5; i++)
{ {
s1.o.push_back({ 30.0f * cosf(fTheta * i), 30.0f * sinf(fTheta * i) }); s1.o.push_back({ 30.0f * cosf(fTheta * i), 30.0f * sinf(fTheta * i) });
s1.p.push_back({ 30.0f * cosf(fTheta * i), 30.0f * sinf(fTheta * i) }); s1.p.push_back({ 30.0f * cosf(fTheta * i), 30.0f * sinf(fTheta * i) });
} }
// Create Triangle // Create Triangle
polygon s2; polygon s2;
fTheta = 3.14159f * 2.0f / 3.0f; fTheta = 3.14159f * 2.0f / 3.0f;
s2.pos = { 200, 150 }; s2.pos = { 200, 150 };
s2.angle = 0.0f; s2.angle = 0.0f;
for (int i = 0; i < 3; i++) for (int i = 0; i < 3; i++)
{ {
s2.o.push_back({ 20.0f * cosf(fTheta * i), 20.0f * sinf(fTheta * i) }); s2.o.push_back({ 20.0f * cosf(fTheta * i), 20.0f * sinf(fTheta * i) });
s2.p.push_back({ 20.0f * cosf(fTheta * i), 20.0f * sinf(fTheta * i) }); s2.p.push_back({ 20.0f * cosf(fTheta * i), 20.0f * sinf(fTheta * i) });
} }
// Create Quad // Create Quad
polygon s3; polygon s3;
s3.pos = { 50, 200 }; s3.pos = { 50, 200 };
s3.angle = 0.0f; s3.angle = 0.0f;
s3.o.push_back({ -30, -30 }); s3.o.push_back({ -30, -30 });
s3.o.push_back({ -30, +30 }); s3.o.push_back({ -30, +30 });
s3.o.push_back({ +30, +30 }); s3.o.push_back({ +30, +30 });
s3.o.push_back({ +30, -30 }); s3.o.push_back({ +30, -30 });
s3.p.resize(4); s3.p.resize(4);
vecShapes.push_back(s1); vecShapes.push_back(s1);
vecShapes.push_back(s2); vecShapes.push_back(s2);
vecShapes.push_back(s3); vecShapes.push_back(s3);
return true; return true;
} }
bool ShapeOverlap_SAT(polygon &r1, polygon &r2) bool ShapeOverlap_SAT(polygon &r1, polygon &r2)
{ {
polygon *poly1 = &r1; polygon *poly1 = &r1;
polygon *poly2 = &r2; polygon *poly2 = &r2;
for (int shape = 0; shape < 2; shape++) for (int shape = 0; shape < 2; shape++)
{ {
if (shape == 1) if (shape == 1)
{ {
poly1 = &r2; poly1 = &r2;
poly2 = &r1; poly2 = &r1;
} }
for (int a = 0; a < poly1->p.size(); a++) for (int a = 0; a < poly1->p.size(); a++)
{ {
int b = (a + 1) % poly1->p.size(); int b = (a + 1) % poly1->p.size();
vec2d axisProj = { -(poly1->p[b].y - poly1->p[a].y), poly1->p[b].x - poly1->p[a].x }; vec2d axisProj = { -(poly1->p[b].y - poly1->p[a].y), poly1->p[b].x - poly1->p[a].x };
float d = sqrtf(axisProj.x * axisProj.x + axisProj.y * axisProj.y); float d = sqrtf(axisProj.x * axisProj.x + axisProj.y * axisProj.y);
axisProj = { axisProj.x / d, axisProj.y / d }; axisProj = { axisProj.x / d, axisProj.y / d };
// Work out min and max 1D points for r1 // Work out min and max 1D points for r1
float min_r1 = INFINITY, max_r1 = -INFINITY; float min_r1 = INFINITY, max_r1 = -INFINITY;
for (int p = 0; p < poly1->p.size(); p++) for (int p = 0; p < poly1->p.size(); p++)
{ {
float q = (poly1->p[p].x * axisProj.x + poly1->p[p].y * axisProj.y); float q = (poly1->p[p].x * axisProj.x + poly1->p[p].y * axisProj.y);
min_r1 = std::min(min_r1, q); min_r1 = std::min(min_r1, q);
max_r1 = std::max(max_r1, q); max_r1 = std::max(max_r1, q);
} }
// Work out min and max 1D points for r2 // Work out min and max 1D points for r2
float min_r2 = INFINITY, max_r2 = -INFINITY; float min_r2 = INFINITY, max_r2 = -INFINITY;
for (int p = 0; p < poly2->p.size(); p++) for (int p = 0; p < poly2->p.size(); p++)
{ {
float q = (poly2->p[p].x * axisProj.x + poly2->p[p].y * axisProj.y); float q = (poly2->p[p].x * axisProj.x + poly2->p[p].y * axisProj.y);
min_r2 = std::min(min_r2, q); min_r2 = std::min(min_r2, q);
max_r2 = std::max(max_r2, q); max_r2 = std::max(max_r2, q);
} }
if (!(max_r2 >= min_r1 && max_r1 >= min_r2)) if (!(max_r2 >= min_r1 && max_r1 >= min_r2))
return false; return false;
} }
} }
return true; return true;
} }
bool ShapeOverlap_SAT_STATIC(polygon &r1, polygon &r2) bool ShapeOverlap_SAT_STATIC(polygon &r1, polygon &r2)
{ {
polygon *poly1 = &r1; polygon *poly1 = &r1;
polygon *poly2 = &r2; polygon *poly2 = &r2;
float overlap = INFINITY; float overlap = INFINITY;
for (int shape = 0; shape < 2; shape++) for (int shape = 0; shape < 2; shape++)
{ {
if (shape == 1) if (shape == 1)
{ {
poly1 = &r2; poly1 = &r2;
poly2 = &r1; poly2 = &r1;
} }
for (int a = 0; a < poly1->p.size(); a++) for (int a = 0; a < poly1->p.size(); a++)
{ {
int b = (a + 1) % poly1->p.size(); int b = (a + 1) % poly1->p.size();
vec2d axisProj = { -(poly1->p[b].y - poly1->p[a].y), poly1->p[b].x - poly1->p[a].x }; vec2d axisProj = { -(poly1->p[b].y - poly1->p[a].y), poly1->p[b].x - poly1->p[a].x };
// Optional normalisation of projection axis enhances stability slightly // Optional normalisation of projection axis enhances stability slightly
//float d = sqrtf(axisProj.x * axisProj.x + axisProj.y * axisProj.y); //float d = sqrtf(axisProj.x * axisProj.x + axisProj.y * axisProj.y);
//axisProj = { axisProj.x / d, axisProj.y / d }; //axisProj = { axisProj.x / d, axisProj.y / d };
// Work out min and max 1D points for r1 // Work out min and max 1D points for r1
float min_r1 = INFINITY, max_r1 = -INFINITY; float min_r1 = INFINITY, max_r1 = -INFINITY;
for (int p = 0; p < poly1->p.size(); p++) for (int p = 0; p < poly1->p.size(); p++)
{ {
float q = (poly1->p[p].x * axisProj.x + poly1->p[p].y * axisProj.y); float q = (poly1->p[p].x * axisProj.x + poly1->p[p].y * axisProj.y);
min_r1 = std::min(min_r1, q); min_r1 = std::min(min_r1, q);
max_r1 = std::max(max_r1, q); max_r1 = std::max(max_r1, q);
} }
// Work out min and max 1D points for r2 // Work out min and max 1D points for r2
float min_r2 = INFINITY, max_r2 = -INFINITY; float min_r2 = INFINITY, max_r2 = -INFINITY;
for (int p = 0; p < poly2->p.size(); p++) for (int p = 0; p < poly2->p.size(); p++)
{ {
float q = (poly2->p[p].x * axisProj.x + poly2->p[p].y * axisProj.y); float q = (poly2->p[p].x * axisProj.x + poly2->p[p].y * axisProj.y);
min_r2 = std::min(min_r2, q); min_r2 = std::min(min_r2, q);
max_r2 = std::max(max_r2, q); max_r2 = std::max(max_r2, q);
} }
// Calculate actual overlap along projected axis, and store the minimum // Calculate actual overlap along projected axis, and store the minimum
overlap = std::min(std::min(max_r1, max_r2) - std::max(min_r1, min_r2), overlap); overlap = std::min(std::min(max_r1, max_r2) - std::max(min_r1, min_r2), overlap);
if (!(max_r2 >= min_r1 && max_r1 >= min_r2)) if (!(max_r2 >= min_r1 && max_r1 >= min_r2))
return false; return false;
} }
} }
// If we got here, the objects have collided, we will displace r1 // If we got here, the objects have collided, we will displace r1
// by overlap along the vector between the two object centers // by overlap along the vector between the two object centers
vec2d d = { r2.pos.x - r1.pos.x, r2.pos.y - r1.pos.y }; vec2d d = { r2.pos.x - r1.pos.x, r2.pos.y - r1.pos.y };
float s = sqrtf(d.x*d.x + d.y*d.y); float s = sqrtf(d.x*d.x + d.y*d.y);
r1.pos.x -= overlap * d.x / s; r1.pos.x -= overlap * d.x / s;
r1.pos.y -= overlap * d.y / s; r1.pos.y -= overlap * d.y / s;
return false; return false;
} }
// Use edge/diagonal intersections. // Use edge/diagonal intersections.
bool ShapeOverlap_DIAGS(polygon &r1, polygon &r2) bool ShapeOverlap_DIAGS(polygon &r1, polygon &r2)
{ {
polygon *poly1 = &r1; polygon *poly1 = &r1;
polygon *poly2 = &r2; polygon *poly2 = &r2;
for (int shape = 0; shape < 2; shape++) for (int shape = 0; shape < 2; shape++)
{ {
if (shape == 1) if (shape == 1)
{ {
poly1 = &r2; poly1 = &r2;
poly2 = &r1; poly2 = &r1;
} }
// Check diagonals of polygon... // Check diagonals of polygon...
for (int p = 0; p < poly1->p.size(); p++) for (int p = 0; p < poly1->p.size(); p++)
{ {
vec2d line_r1s = poly1->pos; vec2d line_r1s = poly1->pos;
vec2d line_r1e = poly1->p[p]; vec2d line_r1e = poly1->p[p];
// ...against edges of the other // ...against edges of the other
for (int q = 0; q < poly2->p.size(); q++) for (int q = 0; q < poly2->p.size(); q++)
{ {
vec2d line_r2s = poly2->p[q]; vec2d line_r2s = poly2->p[q];
vec2d line_r2e = poly2->p[(q + 1) % poly2->p.size()]; vec2d line_r2e = poly2->p[(q + 1) % poly2->p.size()];
// Standard "off the shelf" line segment intersection // Standard "off the shelf" line segment intersection
float h = (line_r2e.x - line_r2s.x) * (line_r1s.y - line_r1e.y) - (line_r1s.x - line_r1e.x) * (line_r2e.y - line_r2s.y); float h = (line_r2e.x - line_r2s.x) * (line_r1s.y - line_r1e.y) - (line_r1s.x - line_r1e.x) * (line_r2e.y - line_r2s.y);
float t1 = ((line_r2s.y - line_r2e.y) * (line_r1s.x - line_r2s.x) + (line_r2e.x - line_r2s.x) * (line_r1s.y - line_r2s.y)) / h; float t1 = ((line_r2s.y - line_r2e.y) * (line_r1s.x - line_r2s.x) + (line_r2e.x - line_r2s.x) * (line_r1s.y - line_r2s.y)) / h;
float t2 = ((line_r1s.y - line_r1e.y) * (line_r1s.x - line_r2s.x) + (line_r1e.x - line_r1s.x) * (line_r1s.y - line_r2s.y)) / h; float t2 = ((line_r1s.y - line_r1e.y) * (line_r1s.x - line_r2s.x) + (line_r1e.x - line_r1s.x) * (line_r1s.y - line_r2s.y)) / h;
if (t1 >= 0.0f && t1 < 1.0f && t2 >= 0.0f && t2 < 1.0f) if (t1 >= 0.0f && t1 < 1.0f && t2 >= 0.0f && t2 < 1.0f)
{ {
return true; return true;
} }
} }
} }
} }
return false; return false;
} }
// Use edge/diagonal intersections. // Use edge/diagonal intersections.
bool ShapeOverlap_DIAGS_STATIC(polygon &r1, polygon &r2) bool ShapeOverlap_DIAGS_STATIC(polygon &r1, polygon &r2)
{ {
polygon *poly1 = &r1; polygon *poly1 = &r1;
polygon *poly2 = &r2; polygon *poly2 = &r2;
for (int shape = 0; shape < 2; shape++) for (int shape = 0; shape < 2; shape++)
{ {
if (shape == 1) if (shape == 1)
{ {
poly1 = &r2; poly1 = &r2;
poly2 = &r1; poly2 = &r1;
} }
// Check diagonals of this polygon... // Check diagonals of this polygon...
for (int p = 0; p < poly1->p.size(); p++) for (int p = 0; p < poly1->p.size(); p++)
{ {
vec2d line_r1s = poly1->pos; vec2d line_r1s = poly1->pos;
vec2d line_r1e = poly1->p[p]; vec2d line_r1e = poly1->p[p];
vec2d displacement = { 0,0 }; vec2d displacement = { 0,0 };
// ...against edges of this polygon // ...against edges of this polygon
for (int q = 0; q < poly2->p.size(); q++) for (int q = 0; q < poly2->p.size(); q++)
{ {
vec2d line_r2s = poly2->p[q]; vec2d line_r2s = poly2->p[q];
vec2d line_r2e = poly2->p[(q + 1) % poly2->p.size()]; vec2d line_r2e = poly2->p[(q + 1) % poly2->p.size()];
// Standard "off the shelf" line segment intersection // Standard "off the shelf" line segment intersection
float h = (line_r2e.x - line_r2s.x) * (line_r1s.y - line_r1e.y) - (line_r1s.x - line_r1e.x) * (line_r2e.y - line_r2s.y); float h = (line_r2e.x - line_r2s.x) * (line_r1s.y - line_r1e.y) - (line_r1s.x - line_r1e.x) * (line_r2e.y - line_r2s.y);
float t1 = ((line_r2s.y - line_r2e.y) * (line_r1s.x - line_r2s.x) + (line_r2e.x - line_r2s.x) * (line_r1s.y - line_r2s.y)) / h; float t1 = ((line_r2s.y - line_r2e.y) * (line_r1s.x - line_r2s.x) + (line_r2e.x - line_r2s.x) * (line_r1s.y - line_r2s.y)) / h;
float t2 = ((line_r1s.y - line_r1e.y) * (line_r1s.x - line_r2s.x) + (line_r1e.x - line_r1s.x) * (line_r1s.y - line_r2s.y)) / h; float t2 = ((line_r1s.y - line_r1e.y) * (line_r1s.x - line_r2s.x) + (line_r1e.x - line_r1s.x) * (line_r1s.y - line_r2s.y)) / h;
if (t1 >= 0.0f && t1 < 1.0f && t2 >= 0.0f && t2 < 1.0f) if (t1 >= 0.0f && t1 < 1.0f && t2 >= 0.0f && t2 < 1.0f)
{ {
displacement.x += (1.0f - t1) * (line_r1e.x - line_r1s.x); displacement.x += (1.0f - t1) * (line_r1e.x - line_r1s.x);
displacement.y += (1.0f - t1) * (line_r1e.y - line_r1s.y); displacement.y += (1.0f - t1) * (line_r1e.y - line_r1s.y);
} }
} }
r1.pos.x += displacement.x * (shape == 0 ? -1 : +1); r1.pos.x += displacement.x * (shape == 0 ? -1 : +1);
r1.pos.y += displacement.y * (shape == 0 ? -1 : +1); r1.pos.y += displacement.y * (shape == 0 ? -1 : +1);
} }
} }
// Cant overlap if static collision is resolved // Cant overlap if static collision is resolved
return false; return false;
} }
bool OnUserUpdate(float fElapsedTime) override bool OnUserUpdate(float fElapsedTime) override
{ {
if (GetKey(olc::Key::F1).bReleased) nMode = 0; if (GetKey(olc::Key::F1).bReleased) nMode = 0;
if (GetKey(olc::Key::F2).bReleased) nMode = 1; if (GetKey(olc::Key::F2).bReleased) nMode = 1;
if (GetKey(olc::Key::F3).bReleased) nMode = 2; if (GetKey(olc::Key::F3).bReleased) nMode = 2;
if (GetKey(olc::Key::F4).bReleased) nMode = 3; if (GetKey(olc::Key::F4).bReleased) nMode = 3;
// Shape 1 // Shape 1
if (GetKey(olc::Key::LEFT).bHeld) vecShapes[0].angle -= 2.0f * fElapsedTime; if (GetKey(olc::Key::LEFT).bHeld) vecShapes[0].angle -= 2.0f * fElapsedTime;
if (GetKey(olc::Key::RIGHT).bHeld) vecShapes[0].angle += 2.0f * fElapsedTime; if (GetKey(olc::Key::RIGHT).bHeld) vecShapes[0].angle += 2.0f * fElapsedTime;
if (GetKey(olc::Key::UP).bHeld) if (GetKey(olc::Key::UP).bHeld)
{ {
vecShapes[0].pos.x += cosf(vecShapes[0].angle) * 60.0f * fElapsedTime; vecShapes[0].pos.x += cosf(vecShapes[0].angle) * 60.0f * fElapsedTime;
vecShapes[0].pos.y += sinf(vecShapes[0].angle) * 60.0f * fElapsedTime; vecShapes[0].pos.y += sinf(vecShapes[0].angle) * 60.0f * fElapsedTime;
} }
if (GetKey(olc::Key::DOWN).bHeld) if (GetKey(olc::Key::DOWN).bHeld)
{ {
vecShapes[0].pos.x -= cosf(vecShapes[0].angle) * 60.0f * fElapsedTime; vecShapes[0].pos.x -= cosf(vecShapes[0].angle) * 60.0f * fElapsedTime;
vecShapes[0].pos.y -= sinf(vecShapes[0].angle) * 60.0f * fElapsedTime; vecShapes[0].pos.y -= sinf(vecShapes[0].angle) * 60.0f * fElapsedTime;
} }
// Shape 2 // Shape 2
if (GetKey(olc::Key::A).bHeld) vecShapes[1].angle -= 2.0f * fElapsedTime; if (GetKey(olc::Key::A).bHeld) vecShapes[1].angle -= 2.0f * fElapsedTime;
if (GetKey(olc::Key::D).bHeld) vecShapes[1].angle += 2.0f * fElapsedTime; if (GetKey(olc::Key::D).bHeld) vecShapes[1].angle += 2.0f * fElapsedTime;
if (GetKey(olc::Key::W).bHeld) if (GetKey(olc::Key::W).bHeld)
{ {
vecShapes[1].pos.x += cosf(vecShapes[1].angle) * 60.0f * fElapsedTime; vecShapes[1].pos.x += cosf(vecShapes[1].angle) * 60.0f * fElapsedTime;
vecShapes[1].pos.y += sinf(vecShapes[1].angle) * 60.0f * fElapsedTime; vecShapes[1].pos.y += sinf(vecShapes[1].angle) * 60.0f * fElapsedTime;
} }
if (GetKey(olc::Key::S).bHeld) if (GetKey(olc::Key::S).bHeld)
{ {
vecShapes[1].pos.x -= cosf(vecShapes[1].angle) * 60.0f * fElapsedTime; vecShapes[1].pos.x -= cosf(vecShapes[1].angle) * 60.0f * fElapsedTime;
vecShapes[1].pos.y -= sinf(vecShapes[1].angle) * 60.0f * fElapsedTime; vecShapes[1].pos.y -= sinf(vecShapes[1].angle) * 60.0f * fElapsedTime;
} }
// Update Shapes and reset flags // Update Shapes and reset flags
for (auto &r : vecShapes) for (auto &r : vecShapes)
{ {
for (int i = 0; i < r.o.size(); i++) for (int i = 0; i < r.o.size(); i++)
r.p[i] = r.p[i] =
{ // 2D Rotation Transform + 2D Translation { // 2D Rotation Transform + 2D Translation
(r.o[i].x * cosf(r.angle)) - (r.o[i].y * sinf(r.angle)) + r.pos.x, (r.o[i].x * cosf(r.angle)) - (r.o[i].y * sinf(r.angle)) + r.pos.x,
(r.o[i].x * sinf(r.angle)) + (r.o[i].y * cosf(r.angle)) + r.pos.y, (r.o[i].x * sinf(r.angle)) + (r.o[i].y * cosf(r.angle)) + r.pos.y,
}; };
r.overlap = false; r.overlap = false;
} }
// Check for overlap // Check for overlap
for (int m = 0; m < vecShapes.size(); m++) for (int m = 0; m < vecShapes.size(); m++)
for (int n = m + 1; n < vecShapes.size(); n++) for (int n = m + 1; n < vecShapes.size(); n++)
{ {
switch (nMode) switch (nMode)
{ {
case 0: vecShapes[m].overlap |= ShapeOverlap_SAT(vecShapes[m], vecShapes[n]); break; case 0: vecShapes[m].overlap |= ShapeOverlap_SAT(vecShapes[m], vecShapes[n]); break;
case 1: vecShapes[m].overlap |= ShapeOverlap_SAT_STATIC(vecShapes[m], vecShapes[n]); break; case 1: vecShapes[m].overlap |= ShapeOverlap_SAT_STATIC(vecShapes[m], vecShapes[n]); break;
case 2: vecShapes[m].overlap |= ShapeOverlap_DIAGS(vecShapes[m], vecShapes[n]); break; case 2: vecShapes[m].overlap |= ShapeOverlap_DIAGS(vecShapes[m], vecShapes[n]); break;
case 3: vecShapes[m].overlap |= ShapeOverlap_DIAGS_STATIC(vecShapes[m], vecShapes[n]); break; case 3: vecShapes[m].overlap |= ShapeOverlap_DIAGS_STATIC(vecShapes[m], vecShapes[n]); break;
} }
} }
// === Render Display === // === Render Display ===
Clear(olc::BLUE); Clear(olc::BLUE);
// Draw Shapes // Draw Shapes
for (auto &r : vecShapes) for (auto &r : vecShapes)
{ {
// Draw Boundary // Draw Boundary
for (int i = 0; i < r.p.size(); i++) for (int i = 0; i < r.p.size(); i++)
DrawLine(r.p[i].x, r.p[i].y, r.p[(i + 1) % r.p.size()].x, r.p[(i + 1) % r.p.size()].y, (r.overlap ? olc::RED : olc::WHITE)); DrawLine(r.p[i].x, r.p[i].y, r.p[(i + 1) % r.p.size()].x, r.p[(i + 1) % r.p.size()].y, (r.overlap ? olc::RED : olc::WHITE));
// Draw Direction // Draw Direction
DrawLine(r.p[0].x, r.p[0].y, r.pos.x, r.pos.y, (r.overlap ? olc::RED : olc::WHITE)); DrawLine(r.p[0].x, r.p[0].y, r.pos.x, r.pos.y, (r.overlap ? olc::RED : olc::WHITE));
} }
// Draw HUD // Draw HUD
DrawString(8, 10, "F1: SAT", (nMode == 0 ? olc::RED : olc::YELLOW)); DrawString(8, 10, "F1: SAT", (nMode == 0 ? olc::RED : olc::YELLOW));
DrawString(8, 20, "F2: SAT/STATIC", (nMode == 1 ? olc::RED : olc::YELLOW)); DrawString(8, 20, "F2: SAT/STATIC", (nMode == 1 ? olc::RED : olc::YELLOW));
DrawString(8, 30, "F3: DIAG", (nMode == 2 ? olc::RED : olc::YELLOW)); DrawString(8, 30, "F3: DIAG", (nMode == 2 ? olc::RED : olc::YELLOW));
DrawString(8, 40, "F4: DIAG/STATIC", (nMode == 3 ? olc::RED : olc::YELLOW)); DrawString(8, 40, "F4: DIAG/STATIC", (nMode == 3 ? olc::RED : olc::YELLOW));
return true; return true;
} }
}; };
int main() int main()
{ {
PolygonCollisions demo; PolygonCollisions demo;
if (demo.Construct(256, 240, 4, 4)) if (demo.Construct(256, 240, 4, 4))
demo.Start(); demo.Start();
return 0; return 0;
} }

@ -1,272 +1,272 @@
/* /*
Programming a robotic arm Programming a robotic arm
"I told you, put down the screwdriver..." - javidx9 "I told you, put down the screwdriver..." - javidx9
License (OLC-3) License (OLC-3)
~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~
Copyright 2018-2019 OneLoneCoder.com Copyright 2018-2019 OneLoneCoder.com
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions modification, are permitted provided that the following conditions
are met: are met:
1. Redistributions or derivations of source code must retain the above 1. Redistributions or derivations of source code must retain the above
copyright notice, this list of conditions and the following disclaimer. copyright notice, this list of conditions and the following disclaimer.
2. Redistributions or derivative works in binary form must reproduce 2. Redistributions or derivative works in binary form must reproduce
the above copyright notice. This list of conditions and the following the above copyright notice. This list of conditions and the following
disclaimer must be reproduced in the documentation and/or other disclaimer must be reproduced in the documentation and/or other
materials provided with the distribution. materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its 3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived contributors may be used to endorse or promote products derived
from this software without specific prior written permission. from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Instructions: Instructions:
~~~~~~~~~~~~~ ~~~~~~~~~~~~~
Without a robot arm and an mbed there is not much you can do! Without a robot arm and an mbed there is not much you can do!
Also requires a 3rd Party PGEX UI by ZleapingBear: Also requires a 3rd Party PGEX UI by ZleapingBear:
https://youtu.be/bfiSjC__MCI https://youtu.be/bfiSjC__MCI
Relevant Video: https://youtu.be/ekdQ-aAB36Y Relevant Video: https://youtu.be/ekdQ-aAB36Y
Links Links
~~~~~ ~~~~~
YouTube: https://www.youtube.com/javidx9 YouTube: https://www.youtube.com/javidx9
https://www.youtube.com/javidx9extra https://www.youtube.com/javidx9extra
Discord: https://discord.gg/WhwHUMV Discord: https://discord.gg/WhwHUMV
Twitter: https://www.twitter.com/javidx9 Twitter: https://www.twitter.com/javidx9
Twitch: https://www.twitch.tv/javidx9 Twitch: https://www.twitch.tv/javidx9
GitHub: https://www.github.com/onelonecoder GitHub: https://www.github.com/onelonecoder
Patreon: https://www.patreon.com/javidx9 Patreon: https://www.patreon.com/javidx9
Homepage: https://www.onelonecoder.com Homepage: https://www.onelonecoder.com
Author Author
~~~~~~ ~~~~~~
David Barr, aka javidx9, ©OneLoneCoder 2019 David Barr, aka javidx9, ©OneLoneCoder 2019
*/ */
#define OLC_PGE_APPLICATION #define OLC_PGE_APPLICATION
#include "olcPixelGameEngine.h" #include "olcPixelGameEngine.h"
#include "olcPGEX_UI.h" #include "olcPGEX_UI.h"
class RobotArm1 : public olc::PixelGameEngine class RobotArm1 : public olc::PixelGameEngine
{ {
public: public:
RobotArm1() RobotArm1()
{ {
sAppName = "Robot Arm 1"; sAppName = "Robot Arm 1";
} }
olc::UI_CONTAINER gui; olc::UI_CONTAINER gui;
float fJointAngle[6]; float fJointAngle[6];
float fAccumulatedTime = 0.0f; float fAccumulatedTime = 0.0f;
HANDLE hCom = nullptr; HANDLE hCom = nullptr;
public: public:
bool OnUserCreate() override bool OnUserCreate() override
{ {
gui.addSlider(10, 20, 180); gui.addSlider(10, 20, 180);
gui.addSlider(10, 60, 180); gui.addSlider(10, 60, 180);
gui.addSlider(10, 100, 180); gui.addSlider(10, 100, 180);
gui.addSlider(10, 140, 180); gui.addSlider(10, 140, 180);
gui.addSlider(10, 180, 180); gui.addSlider(10, 180, 180);
gui.addSlider(10, 220, 180); gui.addSlider(10, 220, 180);
gui.setValue(0, 50); gui.setValue(0, 50);
gui.setValue(1, 50); gui.setValue(1, 50);
gui.setValue(2, 50); gui.setValue(2, 50);
gui.setValue(3, 50); gui.setValue(3, 50);
gui.setValue(4, 50); gui.setValue(4, 50);
gui.setValue(5, 50); gui.setValue(5, 50);
// Open COM Port // Open COM Port
hCom = CreateFile("COM3", GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); hCom = CreateFile("COM3", GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
if (hCom == nullptr) return false; if (hCom == nullptr) return false;
// Configure Protocol: 9600bps, 8N1 // Configure Protocol: 9600bps, 8N1
DCB dcb = { 0 }; DCB dcb = { 0 };
GetCommState(hCom, &dcb); GetCommState(hCom, &dcb);
dcb.BaudRate = CBR_9600; dcb.BaudRate = CBR_9600;
dcb.ByteSize = 8; dcb.ByteSize = 8;
dcb.StopBits = ONESTOPBIT; dcb.StopBits = ONESTOPBIT;
dcb.Parity = NOPARITY; dcb.Parity = NOPARITY;
SetCommState(hCom, &dcb); SetCommState(hCom, &dcb);
return true; return true;
} }
bool OnUserDestroy() override bool OnUserDestroy() override
{ {
if (hCom != nullptr) CloseHandle(hCom); if (hCom != nullptr) CloseHandle(hCom);
return true; return true;
} }
bool OnUserUpdate(float fElapsedTime) override bool OnUserUpdate(float fElapsedTime) override
{ {
gui.Update(fElapsedTime); gui.Update(fElapsedTime);
Clear(olc::GREEN); Clear(olc::GREEN);
gui.drawUIObjects(); gui.drawUIObjects();
for (int i = 0; i < 6; i++) for (int i = 0; i < 6; i++)
fJointAngle[i] = (gui.getSliderFloat(i) / 100.0f) * 180.0f - 90.0f; fJointAngle[i] = (gui.getSliderFloat(i) / 100.0f) * 180.0f - 90.0f;
unsigned char command[12]; unsigned char command[12];
for (int i = 0; i < 6; i++) for (int i = 0; i < 6; i++)
{ {
command[i * 2 + 0] = i; command[i * 2 + 0] = i;
command[i * 2 + 1] = (int)(128 + fJointAngle[i]); command[i * 2 + 1] = (int)(128 + fJointAngle[i]);
} }
fAccumulatedTime += fElapsedTime; fAccumulatedTime += fElapsedTime;
if (fAccumulatedTime > 0.05f) if (fAccumulatedTime > 0.05f)
{ {
fAccumulatedTime -= 0.05f; fAccumulatedTime -= 0.05f;
DWORD bw = 0; DWORD bw = 0;
WriteFile(hCom, command, 12, &bw, 0); WriteFile(hCom, command, 12, &bw, 0);
} }
return true; return true;
} }
}; };
int main() int main()
{ {
RobotArm1 demo; RobotArm1 demo;
if (demo.Construct(400, 400, 2, 2)) if (demo.Construct(400, 400, 2, 2))
demo.Start(); demo.Start();
return 0; return 0;
} }
// Below here is the source code compiled on MBED LPC1768, using the BufferedSerial Library // Below here is the source code compiled on MBED LPC1768, using the BufferedSerial Library
/* /*
#include "mbed.h" #include "mbed.h"
#include "BufferedSerial.h" #include "BufferedSerial.h"
PwmOut pin26(p26); PwmOut pin26(p26);
PwmOut pin25(p25); PwmOut pin25(p25);
PwmOut pin24(p24); PwmOut pin24(p24);
PwmOut pin23(p23); PwmOut pin23(p23);
PwmOut pin22(p22); PwmOut pin22(p22);
PwmOut pin21(p21); PwmOut pin21(p21);
BufferedSerial uart(p9, p10); BufferedSerial uart(p9, p10);
class Joint class Joint
{ {
private: private:
static const float fDutyMin = 0.03f; // -90 static const float fDutyMin = 0.03f; // -90
static const float fDutyMax = 0.11f; // +90 static const float fDutyMax = 0.11f; // +90
static const float fDutyRange = fDutyMax - fDutyMin; static const float fDutyRange = fDutyMax - fDutyMin;
float fTarget; float fTarget;
float fPosition; float fPosition;
float fJointMax; float fJointMax;
float fJointMin; float fJointMin;
public: public:
Joint(float fMin = -90.0f, float fMax = 90.0f, float fDefaultPos = 0.0f) Joint(float fMin = -90.0f, float fMax = 90.0f, float fDefaultPos = 0.0f)
{ {
fJointMin = fMin; fJointMin = fMin;
fJointMax = fMax; fJointMax = fMax;
fPosition = 0.0f; fPosition = 0.0f;
SetTarget(fDefaultPos); SetTarget(fDefaultPos);
} }
void SetTarget(float fAngle) void SetTarget(float fAngle)
{ {
fTarget = fAngle; fTarget = fAngle;
if(fTarget < fJointMin) fTarget = fJointMin; if(fTarget < fJointMin) fTarget = fJointMin;
if(fTarget > fJointMax) fTarget = fJointMax; if(fTarget > fJointMax) fTarget = fJointMax;
} }
void UpdatePosition() void UpdatePosition()
{ {
fPosition = fTarget; fPosition = fTarget;
} }
float GetTarget() float GetTarget()
{ {
return fTarget; return fTarget;
} }
float GetDutyCycle() float GetDutyCycle()
{ {
float fDutyCycle = fPosition / (fJointMax - fJointMin); float fDutyCycle = fPosition / (fJointMax - fJointMin);
fDutyCycle = (fDutyCycle * fDutyRange) + fDutyMin + (fDutyRange * 0.5f); fDutyCycle = (fDutyCycle * fDutyRange) + fDutyMin + (fDutyRange * 0.5f);
return fDutyCycle; return fDutyCycle;
} }
}; };
int main() int main()
{ {
// Servos (MG996R) operate on 20ms period, so set // Servos (MG996R) operate on 20ms period, so set
// PWM period for each pin // PWM period for each pin
pin26.period(0.02f); pin26.period(0.02f);
pin25.period(0.02f); pin25.period(0.02f);
pin24.period(0.02f); pin24.period(0.02f);
pin23.period(0.02f); pin23.period(0.02f);
pin22.period(0.02f); pin22.period(0.02f);
pin21.period(0.02f); pin21.period(0.02f);
Joint joint[6]; Joint joint[6];
joint[0].SetTarget(0.0f); joint[0].SetTarget(0.0f);
joint[1].SetTarget(0.0f); joint[1].SetTarget(0.0f);
joint[2].SetTarget(0.0f); joint[2].SetTarget(0.0f);
joint[3].SetTarget(-25.0f); joint[3].SetTarget(-25.0f);
joint[4].SetTarget(-20.0f); joint[4].SetTarget(-20.0f);
joint[5].SetTarget(-15.0f); joint[5].SetTarget(-15.0f);
int nTargetJoint = 0; int nTargetJoint = 0;
while(1) while(1)
{ {
// Read from UART // Read from UART
if(uart.readable()) if(uart.readable())
{ {
unsigned char c = (unsigned char)uart.getc(); unsigned char c = (unsigned char)uart.getc();
if(c < 10) if(c < 10)
nTargetJoint = c; nTargetJoint = c;
else else
joint[nTargetJoint].SetTarget((float)c - 128); joint[nTargetJoint].SetTarget((float)c - 128);
} }
// Write Duty Cycles // Write Duty Cycles
// Update each joints position // Update each joints position
for(int i=0; i<6; i++) for(int i=0; i<6; i++)
joint[i].UpdatePosition(); joint[i].UpdatePosition();
// Set PWM values for each joint // Set PWM values for each joint
pin26.write(joint[0].GetDutyCycle()); pin26.write(joint[0].GetDutyCycle());
pin25.write(joint[1].GetDutyCycle()); pin25.write(joint[1].GetDutyCycle());
pin24.write(joint[2].GetDutyCycle()); pin24.write(joint[2].GetDutyCycle());
pin23.write(joint[3].GetDutyCycle()); pin23.write(joint[3].GetDutyCycle());
pin22.write(joint[4].GetDutyCycle()); pin22.write(joint[4].GetDutyCycle());
pin21.write(joint[5].GetDutyCycle()); pin21.write(joint[5].GetDutyCycle());
} }
} }
*/ */

@ -1,272 +1,272 @@
/* /*
Simple example code for olcPGEX_Sound.h - Mind your speakers! Simple example code for olcPGEX_Sound.h - Mind your speakers!
You will need SampleA.wav, SampleB.wav and SampleC.wav for this demo. You will need SampleA.wav, SampleB.wav and SampleC.wav for this demo.
License (OLC-3) License (OLC-3)
~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~
Copyright 2018 OneLoneCoder.com Copyright 2018 OneLoneCoder.com
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions modification, are permitted provided that the following conditions
are met: are met:
1. Redistributions or derivations of source code must retain the above 1. Redistributions or derivations of source code must retain the above
copyright notice, this list of conditions and the following disclaimer. copyright notice, this list of conditions and the following disclaimer.
2. Redistributions or derivative works in binary form must reproduce 2. Redistributions or derivative works in binary form must reproduce
the above copyright notice. This list of conditions and the following the above copyright notice. This list of conditions and the following
disclaimer must be reproduced in the documentation and/or other disclaimer must be reproduced in the documentation and/or other
materials provided with the distribution. materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its 3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived contributors may be used to endorse or promote products derived
from this software without specific prior written permission. from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Links Links
~~~~~ ~~~~~
YouTube: https://www.youtube.com/javidx9 YouTube: https://www.youtube.com/javidx9
Discord: https://discord.gg/WhwHUMV Discord: https://discord.gg/WhwHUMV
Twitter: https://www.twitter.com/javidx9 Twitter: https://www.twitter.com/javidx9
Twitch: https://www.twitch.tv/javidx9 Twitch: https://www.twitch.tv/javidx9
GitHub: https://www.github.com/onelonecoder GitHub: https://www.github.com/onelonecoder
Homepage: https://www.onelonecoder.com Homepage: https://www.onelonecoder.com
Patreon: https://www.patreon.com/javidx9 Patreon: https://www.patreon.com/javidx9
Author Author
~~~~~~ ~~~~~~
David Barr, aka javidx9, ©OneLoneCoder 2018 David Barr, aka javidx9, ©OneLoneCoder 2018
*/ */
#define OLC_PGE_APPLICATION #define OLC_PGE_APPLICATION
#include "olcPixelGameEngine.h" #include "olcPixelGameEngine.h"
#define OLC_PGEX_SOUND #define OLC_PGEX_SOUND
#include "olcPGEX_Sound.h" #include "olcPGEX_Sound.h"
#include <list> #include <list>
class SoundTest : public olc::PixelGameEngine class SoundTest : public olc::PixelGameEngine
{ {
public: public:
SoundTest() SoundTest()
{ {
sAppName = "Sound Test"; sAppName = "Sound Test";
} }
private: private:
int sndSampleA; int sndSampleA;
int sndSampleB; int sndSampleB;
int sndSampleC; int sndSampleC;
bool bToggle = false; bool bToggle = false;
static bool bSynthPlaying; static bool bSynthPlaying;
static float fSynthFrequency; static float fSynthFrequency;
static float fFilterVolume; static float fFilterVolume;
const olc::Key keys[12] = { olc::Key::Z, olc::Key::S, olc::Key::X, olc::Key::D, olc::Key::C, const olc::Key keys[12] = { olc::Key::Z, olc::Key::S, olc::Key::X, olc::Key::D, olc::Key::C,
olc::Key::V, olc::Key::G, olc::Key::B, olc::Key::H, olc::Key::N, olc::Key::J, olc::Key::M}; olc::Key::V, olc::Key::G, olc::Key::B, olc::Key::H, olc::Key::N, olc::Key::J, olc::Key::M};
static float fPreviousSamples[128]; static float fPreviousSamples[128];
static int nSamplePos; static int nSamplePos;
private: private:
// This is an optional function that allows the user to generate or synthesize sounds // This is an optional function that allows the user to generate or synthesize sounds
// in a custom way, it is fed into the output mixer bu the extension // in a custom way, it is fed into the output mixer bu the extension
static float MyCustomSynthFunction(int nChannel, float fGlobalTime, float fTimeStep) static float MyCustomSynthFunction(int nChannel, float fGlobalTime, float fTimeStep)
{ {
// Just generate a sine wave of the appropriate frequency // Just generate a sine wave of the appropriate frequency
if (bSynthPlaying) if (bSynthPlaying)
return sin(fSynthFrequency * 2.0f * 3.14159f * fGlobalTime); return sin(fSynthFrequency * 2.0f * 3.14159f * fGlobalTime);
else else
return 0.0f; return 0.0f;
} }
// This is an optional function that allows the user to filter the output from // This is an optional function that allows the user to filter the output from
// the internal mixer of the extension. Here you could add effects or just // the internal mixer of the extension. Here you could add effects or just
// control volume. I also like to use it to extract information about // control volume. I also like to use it to extract information about
// the currently playing output waveform // the currently playing output waveform
static float MyCustomFilterFunction(int nChannel, float fGlobalTime, float fSample) static float MyCustomFilterFunction(int nChannel, float fGlobalTime, float fSample)
{ {
// Fundamentally just control volume // Fundamentally just control volume
float fOutput = fSample * fFilterVolume; float fOutput = fSample * fFilterVolume;
// But also add sample to list of previous samples for visualisation // But also add sample to list of previous samples for visualisation
fPreviousSamples[nSamplePos] = fOutput; fPreviousSamples[nSamplePos] = fOutput;
nSamplePos++; nSamplePos++;
nSamplePos %= 128; nSamplePos %= 128;
return fOutput; return fOutput;
} }
bool OnUserCreate() bool OnUserCreate()
{ {
olc::SOUND::InitialiseAudio(44100, 1, 8, 512); olc::SOUND::InitialiseAudio(44100, 1, 8, 512);
sndSampleA = olc::SOUND::LoadAudioSample("SampleA.wav"); sndSampleA = olc::SOUND::LoadAudioSample("SampleA.wav");
sndSampleB = olc::SOUND::LoadAudioSample("SampleB.wav"); sndSampleB = olc::SOUND::LoadAudioSample("SampleB.wav");
sndSampleC = olc::SOUND::LoadAudioSample("SampleC.wav"); sndSampleC = olc::SOUND::LoadAudioSample("SampleC.wav");
// Give the sound engine a hook to a custom generation function // Give the sound engine a hook to a custom generation function
olc::SOUND::SetUserSynthFunction(MyCustomSynthFunction); olc::SOUND::SetUserSynthFunction(MyCustomSynthFunction);
// Give the sound engine a hook to a custom filtering function // Give the sound engine a hook to a custom filtering function
olc::SOUND::SetUserFilterFunction(MyCustomFilterFunction); olc::SOUND::SetUserFilterFunction(MyCustomFilterFunction);
return true; return true;
} }
bool OnUserUpdate(float fElapsedTime) bool OnUserUpdate(float fElapsedTime)
{ {
//olc::SOUND::PlaySample(sndTest); //olc::SOUND::PlaySample(sndTest);
auto PointInRect = [&](int x, int y, int rx, int ry, int rw, int rh) auto PointInRect = [&](int x, int y, int rx, int ry, int rw, int rh)
{ {
return x >= rx && x < (rx + rw) && y >= ry && y < (ry + rh); return x >= rx && x < (rx + rw) && y >= ry && y < (ry + rh);
}; };
int nMouseX = GetMouseX(); int nMouseX = GetMouseX();
int nMouseY = GetMouseY(); int nMouseY = GetMouseY();
if(GetMouse(0).bPressed && PointInRect(nMouseX, nMouseY, 16, 16, 128, 24)) if(GetMouse(0).bPressed && PointInRect(nMouseX, nMouseY, 16, 16, 128, 24))
olc::SOUND::PlaySample(sndSampleA); // Plays the sample once olc::SOUND::PlaySample(sndSampleA); // Plays the sample once
if (GetMouse(0).bPressed && PointInRect(nMouseX, nMouseY, 16, 48, 128, 24)) if (GetMouse(0).bPressed && PointInRect(nMouseX, nMouseY, 16, 48, 128, 24))
olc::SOUND::PlaySample(sndSampleB); olc::SOUND::PlaySample(sndSampleB);
if (GetMouse(0).bPressed && PointInRect(nMouseX, nMouseY, 16, 80, 128, 24)) if (GetMouse(0).bPressed && PointInRect(nMouseX, nMouseY, 16, 80, 128, 24))
{ {
bToggle = !bToggle; bToggle = !bToggle;
if (bToggle) if (bToggle)
{ {
olc::SOUND::PlaySample(sndSampleC, true); // Plays the sample in looping mode olc::SOUND::PlaySample(sndSampleC, true); // Plays the sample in looping mode
} }
else else
{ {
olc::SOUND::StopSample(sndSampleC); olc::SOUND::StopSample(sndSampleC);
} }
} }
if (GetMouse(0).bHeld && PointInRect(nMouseX, nMouseY, 160, 16, 90, 24)) if (GetMouse(0).bHeld && PointInRect(nMouseX, nMouseY, 160, 16, 90, 24))
fFilterVolume += 2.0f * fElapsedTime; fFilterVolume += 2.0f * fElapsedTime;
if (GetMouse(0).bHeld && PointInRect(nMouseX, nMouseY, 160, 48, 90, 24)) if (GetMouse(0).bHeld && PointInRect(nMouseX, nMouseY, 160, 48, 90, 24))
fFilterVolume -= 2.0f * fElapsedTime; fFilterVolume -= 2.0f * fElapsedTime;
if (fFilterVolume < 0.0f) fFilterVolume = 0.0f; if (fFilterVolume < 0.0f) fFilterVolume = 0.0f;
if (fFilterVolume > 1.0f) fFilterVolume = 1.0f; if (fFilterVolume > 1.0f) fFilterVolume = 1.0f;
// Detect keyboard - very simple synthesizer // Detect keyboard - very simple synthesizer
if (IsFocused()) if (IsFocused())
{ {
bool bKeyIsPressed = false; bool bKeyIsPressed = false;
float fFrequency = 0.0f; float fFrequency = 0.0f;
for (int i = 0; i < 12; i++) for (int i = 0; i < 12; i++)
{ {
if (GetKey(keys[i]).bHeld) if (GetKey(keys[i]).bHeld)
{ {
bKeyIsPressed = true; bKeyIsPressed = true;
float fOctaveBaseFrequency = 220.0f; float fOctaveBaseFrequency = 220.0f;
float f12thRootOf2 = pow(2.0f, 1.0f / 12.0f); float f12thRootOf2 = pow(2.0f, 1.0f / 12.0f);
fFrequency = fOctaveBaseFrequency * powf(f12thRootOf2, (float)i); fFrequency = fOctaveBaseFrequency * powf(f12thRootOf2, (float)i);
} }
} }
fSynthFrequency = fFrequency; fSynthFrequency = fFrequency;
bSynthPlaying = bKeyIsPressed; bSynthPlaying = bKeyIsPressed;
} }
// Draw Buttons // Draw Buttons
Clear(olc::BLUE); Clear(olc::BLUE);
DrawRect(16, 16, 128, 24); DrawRect(16, 16, 128, 24);
DrawString(20, 20, "Play Sample A"); DrawString(20, 20, "Play Sample A");
DrawRect(16, 48, 128, 24); DrawRect(16, 48, 128, 24);
DrawString(20, 52, "Play Sample B"); DrawString(20, 52, "Play Sample B");
DrawRect(16, 80, 128, 24); DrawRect(16, 80, 128, 24);
DrawString(20, 84, (bToggle ? "Stop Sample C" : "Loop Sample C")); DrawString(20, 84, (bToggle ? "Stop Sample C" : "Loop Sample C"));
DrawRect(160, 16, 90, 24); DrawRect(160, 16, 90, 24);
DrawString(164, 20, "Volume +"); DrawString(164, 20, "Volume +");
DrawRect(160, 48, 90, 24); DrawRect(160, 48, 90, 24);
DrawString(164, 52, "Volume -"); DrawString(164, 52, "Volume -");
DrawString(164, 80, "Volume: " + std::to_string((int)(fFilterVolume * 10.0f))); DrawString(164, 80, "Volume: " + std::to_string((int)(fFilterVolume * 10.0f)));
// Draw Keyboard // Draw Keyboard
// White Keys // White Keys
for (int i = 0; i < 7; i++) for (int i = 0; i < 7; i++)
{ {
FillRect(i * 16 + 8, 160, 16, 64); FillRect(i * 16 + 8, 160, 16, 64);
DrawRect(i * 16 + 8, 160, 16, 64, olc::BLACK); DrawRect(i * 16 + 8, 160, 16, 64, olc::BLACK);
DrawString(i * 16 + 12, 212, std::string(1, "ZXCVBNM"[i]), olc::BLACK); DrawString(i * 16 + 12, 212, std::string(1, "ZXCVBNM"[i]), olc::BLACK);
} }
// Black Keys // Black Keys
for (int i = 0; i < 6; i++) for (int i = 0; i < 6; i++)
{ {
if (i != 2) if (i != 2)
{ {
FillRect(i * 16 + 18, 160, 12, 32, olc::BLACK); FillRect(i * 16 + 18, 160, 12, 32, olc::BLACK);
DrawString(i * 16 + 20, 180, std::string(1, "SDFGHJ"[i]), olc::WHITE); DrawString(i * 16 + 20, 180, std::string(1, "SDFGHJ"[i]), olc::WHITE);
} }
} }
// Draw visualisation // Draw visualisation
int nStartPos = (nSamplePos + 127) % 128; int nStartPos = (nSamplePos + 127) % 128;
for (int i = 127; i >= 0; i--) for (int i = 127; i >= 0; i--)
{ {
float fSample = fPreviousSamples[(nSamplePos + i) % 128]; float fSample = fPreviousSamples[(nSamplePos + i) % 128];
DrawLine(124 + i, 210, 124 + i, 210 + (int)(fSample * 20.0f), olc::RED); DrawLine(124 + i, 210, 124 + i, 210 + (int)(fSample * 20.0f), olc::RED);
} }
return true; return true;
} }
// Note we must shut down the sound system too!! // Note we must shut down the sound system too!!
bool OnUserDestroy() bool OnUserDestroy()
{ {
olc::SOUND::DestroyAudio(); olc::SOUND::DestroyAudio();
return true; return true;
} }
}; };
bool SoundTest::bSynthPlaying = false; bool SoundTest::bSynthPlaying = false;
float SoundTest::fSynthFrequency = 0.0f; float SoundTest::fSynthFrequency = 0.0f;
float SoundTest::fFilterVolume = 1.0f; float SoundTest::fFilterVolume = 1.0f;
int SoundTest::nSamplePos = 0; int SoundTest::nSamplePos = 0;
float SoundTest::fPreviousSamples[128]; float SoundTest::fPreviousSamples[128];
int main() int main()
{ {
SoundTest demo; SoundTest demo;
if(demo.Construct(256, 240, 4, 4)) if(demo.Construct(256, 240, 4, 4))
demo.Start(); demo.Start();
return 0; return 0;
} }

@ -1,257 +1,257 @@
/* /*
OneLoneCoder.com - 2D Sprite Affine Transformations OneLoneCoder.com - 2D Sprite Affine Transformations
"No more 90 degree movements" - @Javidx9 "No more 90 degree movements" - @Javidx9
Background Background
~~~~~~~~~~ ~~~~~~~~~~
The sophistication of 2D engines is enhanced when the programmer is The sophistication of 2D engines is enhanced when the programmer is
able to rotate and scale sprites in a convenient manner. This program able to rotate and scale sprites in a convenient manner. This program
shows the basics of how affine transformations accomplish this. shows the basics of how affine transformations accomplish this.
License (OLC-3) License (OLC-3)
~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~
Copyright 2018 OneLoneCoder.com Copyright 2018 OneLoneCoder.com
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions modification, are permitted provided that the following conditions
are met: are met:
1. Redistributions or derivations of source code must retain the above 1. Redistributions or derivations of source code must retain the above
copyright notice, this list of conditions and the following disclaimer. copyright notice, this list of conditions and the following disclaimer.
2. Redistributions or derivative works in binary form must reproduce 2. Redistributions or derivative works in binary form must reproduce
the above copyright notice. This list of conditions and the following the above copyright notice. This list of conditions and the following
disclaimer must be reproduced in the documentation and/or other disclaimer must be reproduced in the documentation and/or other
materials provided with the distribution. materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its 3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived contributors may be used to endorse or promote products derived
from this software without specific prior written permission. from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Links Links
~~~~~ ~~~~~
YouTube: https://www.youtube.com/javidx9 YouTube: https://www.youtube.com/javidx9
Discord: https://discord.gg/WhwHUMV Discord: https://discord.gg/WhwHUMV
Twitter: https://www.twitter.com/javidx9 Twitter: https://www.twitter.com/javidx9
Twitch: https://www.twitch.tv/javidx9 Twitch: https://www.twitch.tv/javidx9
GitHub: https://www.github.com/onelonecoder GitHub: https://www.github.com/onelonecoder
Patreon: https://www.patreon.com/javidx9 Patreon: https://www.patreon.com/javidx9
Homepage: https://www.onelonecoder.com Homepage: https://www.onelonecoder.com
Relevant Videos Relevant Videos
~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~
https://youtu.be/zxwLN2blwbQ https://youtu.be/zxwLN2blwbQ
Author Author
~~~~~~ ~~~~~~
David Barr, aka javidx9, ©OneLoneCoder 2018 David Barr, aka javidx9, ©OneLoneCoder 2018
*/ */
#define OLC_PGE_APPLICATION #define OLC_PGE_APPLICATION
#include "olcPixelGameEngine.h" #include "olcPixelGameEngine.h"
#include <algorithm> #include <algorithm>
#undef min #undef min
#undef max #undef max
class SpriteTransforms : public olc::PixelGameEngine class SpriteTransforms : public olc::PixelGameEngine
{ {
public: public:
SpriteTransforms() SpriteTransforms()
{ {
sAppName = "Sprite Transforms"; sAppName = "Sprite Transforms";
} }
private: private:
olc::Sprite *sprCar; olc::Sprite *sprCar;
struct matrix3x3 struct matrix3x3
{ {
float m[3][3]; float m[3][3];
}; };
void Identity(matrix3x3 &mat) void Identity(matrix3x3 &mat)
{ {
mat.m[0][0] = 1.0f; mat.m[1][0] = 0.0f; mat.m[2][0] = 0.0f; mat.m[0][0] = 1.0f; mat.m[1][0] = 0.0f; mat.m[2][0] = 0.0f;
mat.m[0][1] = 0.0f; mat.m[1][1] = 1.0f; mat.m[2][1] = 0.0f; mat.m[0][1] = 0.0f; mat.m[1][1] = 1.0f; mat.m[2][1] = 0.0f;
mat.m[0][2] = 0.0f; mat.m[1][2] = 0.0f; mat.m[2][2] = 1.0f; mat.m[0][2] = 0.0f; mat.m[1][2] = 0.0f; mat.m[2][2] = 1.0f;
} }
void Translate(matrix3x3 &mat, float ox, float oy) void Translate(matrix3x3 &mat, float ox, float oy)
{ {
mat.m[0][0] = 1.0f; mat.m[1][0] = 0.0f; mat.m[2][0] = ox; mat.m[0][0] = 1.0f; mat.m[1][0] = 0.0f; mat.m[2][0] = ox;
mat.m[0][1] = 0.0f; mat.m[1][1] = 1.0f; mat.m[2][1] = oy; mat.m[0][1] = 0.0f; mat.m[1][1] = 1.0f; mat.m[2][1] = oy;
mat.m[0][2] = 0.0f; mat.m[1][2] = 0.0f; mat.m[2][2] = 1.0f; mat.m[0][2] = 0.0f; mat.m[1][2] = 0.0f; mat.m[2][2] = 1.0f;
} }
void Rotate(matrix3x3 &mat, float fTheta) void Rotate(matrix3x3 &mat, float fTheta)
{ {
mat.m[0][0] = cosf(fTheta); mat.m[1][0] = sinf(fTheta); mat.m[2][0] = 0.0f; mat.m[0][0] = cosf(fTheta); mat.m[1][0] = sinf(fTheta); mat.m[2][0] = 0.0f;
mat.m[0][1] = -sinf(fTheta); mat.m[1][1] = cosf(fTheta); mat.m[2][1] = 0.0f; mat.m[0][1] = -sinf(fTheta); mat.m[1][1] = cosf(fTheta); mat.m[2][1] = 0.0f;
mat.m[0][2] = 0.0f; mat.m[1][2] = 0.0f; mat.m[2][2] = 1.0f; mat.m[0][2] = 0.0f; mat.m[1][2] = 0.0f; mat.m[2][2] = 1.0f;
} }
void Scale(matrix3x3 &mat, float sx, float sy) void Scale(matrix3x3 &mat, float sx, float sy)
{ {
mat.m[0][0] = sx; mat.m[1][0] = 0.0f; mat.m[2][0] = 0.0f; mat.m[0][0] = sx; mat.m[1][0] = 0.0f; mat.m[2][0] = 0.0f;
mat.m[0][1] = 0.0f; mat.m[1][1] = sy; mat.m[2][1] = 0.0f; mat.m[0][1] = 0.0f; mat.m[1][1] = sy; mat.m[2][1] = 0.0f;
mat.m[0][2] = 0.0f; mat.m[1][2] = 0.0f; mat.m[2][2] = 1.0f; mat.m[0][2] = 0.0f; mat.m[1][2] = 0.0f; mat.m[2][2] = 1.0f;
} }
void Shear(matrix3x3 &mat, float sx, float sy) void Shear(matrix3x3 &mat, float sx, float sy)
{ {
mat.m[0][0] = 1.0f; mat.m[1][0] = sx; mat.m[2][0] = 0.0f; mat.m[0][0] = 1.0f; mat.m[1][0] = sx; mat.m[2][0] = 0.0f;
mat.m[0][1] = sy; mat.m[1][1] = 1.0f; mat.m[2][1] = 0.0f; mat.m[0][1] = sy; mat.m[1][1] = 1.0f; mat.m[2][1] = 0.0f;
mat.m[0][2] = 0.0f; mat.m[1][2] = 0.0f; mat.m[2][2] = 1.0f; mat.m[0][2] = 0.0f; mat.m[1][2] = 0.0f; mat.m[2][2] = 1.0f;
} }
void MatrixMultiply(matrix3x3 &matResult, matrix3x3 &matA, matrix3x3 &matB) void MatrixMultiply(matrix3x3 &matResult, matrix3x3 &matA, matrix3x3 &matB)
{ {
for (int c = 0; c < 3; c++) for (int c = 0; c < 3; c++)
{ {
for (int r = 0; r < 3; r++) for (int r = 0; r < 3; r++)
{ {
matResult.m[c][r] = matA.m[0][r] * matB.m[c][0] + matResult.m[c][r] = matA.m[0][r] * matB.m[c][0] +
matA.m[1][r] * matB.m[c][1] + matA.m[1][r] * matB.m[c][1] +
matA.m[2][r] * matB.m[c][2]; matA.m[2][r] * matB.m[c][2];
} }
} }
} }
void Forward(matrix3x3 &mat, float in_x, float in_y, float &out_x, float &out_y) void Forward(matrix3x3 &mat, float in_x, float in_y, float &out_x, float &out_y)
{ {
out_x = in_x * mat.m[0][0] + in_y * mat.m[1][0] + mat.m[2][0]; out_x = in_x * mat.m[0][0] + in_y * mat.m[1][0] + mat.m[2][0];
out_y = in_x * mat.m[0][1] + in_y * mat.m[1][1] + mat.m[2][1]; out_y = in_x * mat.m[0][1] + in_y * mat.m[1][1] + mat.m[2][1];
} }
void Invert(matrix3x3 &matIn, matrix3x3 &matOut) void Invert(matrix3x3 &matIn, matrix3x3 &matOut)
{ {
float det = matIn.m[0][0] * (matIn.m[1][1] * matIn.m[2][2] - matIn.m[1][2] * matIn.m[2][1]) - float det = matIn.m[0][0] * (matIn.m[1][1] * matIn.m[2][2] - matIn.m[1][2] * matIn.m[2][1]) -
matIn.m[1][0] * (matIn.m[0][1] * matIn.m[2][2] - matIn.m[2][1] * matIn.m[0][2]) + matIn.m[1][0] * (matIn.m[0][1] * matIn.m[2][2] - matIn.m[2][1] * matIn.m[0][2]) +
matIn.m[2][0] * (matIn.m[0][1] * matIn.m[1][2] - matIn.m[1][1] * matIn.m[0][2]); matIn.m[2][0] * (matIn.m[0][1] * matIn.m[1][2] - matIn.m[1][1] * matIn.m[0][2]);
float idet = 1.0f / det; float idet = 1.0f / det;
matOut.m[0][0] = (matIn.m[1][1] * matIn.m[2][2] - matIn.m[1][2] * matIn.m[2][1]) * idet; matOut.m[0][0] = (matIn.m[1][1] * matIn.m[2][2] - matIn.m[1][2] * matIn.m[2][1]) * idet;
matOut.m[1][0] = (matIn.m[2][0] * matIn.m[1][2] - matIn.m[1][0] * matIn.m[2][2]) * idet; matOut.m[1][0] = (matIn.m[2][0] * matIn.m[1][2] - matIn.m[1][0] * matIn.m[2][2]) * idet;
matOut.m[2][0] = (matIn.m[1][0] * matIn.m[2][1] - matIn.m[2][0] * matIn.m[1][1]) * idet; matOut.m[2][0] = (matIn.m[1][0] * matIn.m[2][1] - matIn.m[2][0] * matIn.m[1][1]) * idet;
matOut.m[0][1] = (matIn.m[2][1] * matIn.m[0][2] - matIn.m[0][1] * matIn.m[2][2]) * idet; matOut.m[0][1] = (matIn.m[2][1] * matIn.m[0][2] - matIn.m[0][1] * matIn.m[2][2]) * idet;
matOut.m[1][1] = (matIn.m[0][0] * matIn.m[2][2] - matIn.m[2][0] * matIn.m[0][2]) * idet; matOut.m[1][1] = (matIn.m[0][0] * matIn.m[2][2] - matIn.m[2][0] * matIn.m[0][2]) * idet;
matOut.m[2][1] = (matIn.m[0][1] * matIn.m[2][0] - matIn.m[0][0] * matIn.m[2][1]) * idet; matOut.m[2][1] = (matIn.m[0][1] * matIn.m[2][0] - matIn.m[0][0] * matIn.m[2][1]) * idet;
matOut.m[0][2] = (matIn.m[0][1] * matIn.m[1][2] - matIn.m[0][2] * matIn.m[1][1]) * idet; matOut.m[0][2] = (matIn.m[0][1] * matIn.m[1][2] - matIn.m[0][2] * matIn.m[1][1]) * idet;
matOut.m[1][2] = (matIn.m[0][2] * matIn.m[1][0] - matIn.m[0][0] * matIn.m[1][2]) * idet; matOut.m[1][2] = (matIn.m[0][2] * matIn.m[1][0] - matIn.m[0][0] * matIn.m[1][2]) * idet;
matOut.m[2][2] = (matIn.m[0][0] * matIn.m[1][1] - matIn.m[0][1] * matIn.m[1][0]) * idet; matOut.m[2][2] = (matIn.m[0][0] * matIn.m[1][1] - matIn.m[0][1] * matIn.m[1][0]) * idet;
} }
float fRotate = 0.0f; float fRotate = 0.0f;
public: public:
bool OnUserCreate() override bool OnUserCreate() override
{ {
sprCar = new olc::Sprite("car_top1.png"); sprCar = new olc::Sprite("car_top1.png");
return true; return true;
} }
bool OnUserUpdate(float fElapsedTime) override bool OnUserUpdate(float fElapsedTime) override
{ {
if (GetKey(olc::Key::Z).bHeld) fRotate -= 2.0f * fElapsedTime; if (GetKey(olc::Key::Z).bHeld) fRotate -= 2.0f * fElapsedTime;
if (GetKey(olc::Key::X).bHeld) fRotate += 2.0f * fElapsedTime; if (GetKey(olc::Key::X).bHeld) fRotate += 2.0f * fElapsedTime;
Clear(olc::DARK_CYAN); Clear(olc::DARK_CYAN);
SetPixelMode(olc::Pixel::ALPHA); SetPixelMode(olc::Pixel::ALPHA);
//DrawSprite(0, 0, sprCar, 3); //DrawSprite(0, 0, sprCar, 3);
matrix3x3 matFinal, matA, matB, matC, matFinalInv; matrix3x3 matFinal, matA, matB, matC, matFinalInv;
Translate(matA, -100, -50); Translate(matA, -100, -50);
Rotate(matB, fRotate); Rotate(matB, fRotate);
MatrixMultiply(matC, matB, matA); MatrixMultiply(matC, matB, matA);
Translate(matA, (float)ScreenWidth()/2, (float)ScreenHeight()/2); Translate(matA, (float)ScreenWidth()/2, (float)ScreenHeight()/2);
MatrixMultiply(matFinal, matA, matC); MatrixMultiply(matFinal, matA, matC);
Invert(matFinal, matFinalInv); Invert(matFinal, matFinalInv);
// Draws the dumb way, but leaves gaps // Draws the dumb way, but leaves gaps
/*for (int x = 0; x < sprCar->width; x++) /*for (int x = 0; x < sprCar->width; x++)
{ {
for (int y = 0; y < sprCar->height; y++) for (int y = 0; y < sprCar->height; y++)
{ {
olc::Pixel p = sprCar->GetPixel(x, y); olc::Pixel p = sprCar->GetPixel(x, y);
float nx, ny; float nx, ny;
Forward(matFinal, (float)x, (float)y, nx, ny); Forward(matFinal, (float)x, (float)y, nx, ny);
Draw(nx, ny, p); Draw(nx, ny, p);
} }
}*/ }*/
// Work out bounding box of sprite post-transformation // Work out bounding box of sprite post-transformation
// by passing through sprite corner locations into // by passing through sprite corner locations into
// transformation matrix // transformation matrix
float ex, ey; float ex, ey;
float sx, sy; float sx, sy;
float px, py; float px, py;
Forward(matFinal, 0.0f, 0.0f, px, py); Forward(matFinal, 0.0f, 0.0f, px, py);
sx = px; sy = py; sx = px; sy = py;
ex = px; ey = py; ex = px; ey = py;
Forward(matFinal, (float)sprCar->width, (float)sprCar->height, px, py); Forward(matFinal, (float)sprCar->width, (float)sprCar->height, px, py);
sx = std::min(sx, px); sy = std::min(sy, py); sx = std::min(sx, px); sy = std::min(sy, py);
ex = std::max(ex, px); ey = std::max(ey, py); ex = std::max(ex, px); ey = std::max(ey, py);
Forward(matFinal, 0.0f, (float)sprCar->height, px, py); Forward(matFinal, 0.0f, (float)sprCar->height, px, py);
sx = std::min(sx, px); sy = std::min(sy, py); sx = std::min(sx, px); sy = std::min(sy, py);
ex = std::max(ex, px); ey = std::max(ey, py); ex = std::max(ex, px); ey = std::max(ey, py);
Forward(matFinal, (float)sprCar->width, 0.0f, px, py); Forward(matFinal, (float)sprCar->width, 0.0f, px, py);
sx = std::min(sx, px); sy = std::min(sy, py); sx = std::min(sx, px); sy = std::min(sy, py);
ex = std::max(ex, px); ey = std::max(ey, py); ex = std::max(ex, px); ey = std::max(ey, py);
// Use transformed corner locations in screen space to establish // Use transformed corner locations in screen space to establish
// region of pixels to fill, using inverse transform to sample // region of pixels to fill, using inverse transform to sample
// sprite at suitable locations. // sprite at suitable locations.
for (int x = sx; x < ex; x++) for (int x = sx; x < ex; x++)
{ {
for (int y = sy; y < ey; y++) for (int y = sy; y < ey; y++)
{ {
float nx, ny; float nx, ny;
Forward(matFinalInv, (float)x, (float)y, nx, ny); Forward(matFinalInv, (float)x, (float)y, nx, ny);
olc::Pixel p = sprCar->GetPixel((int32_t)(nx + 0.5f), (int32_t)(ny + 0.5f)); olc::Pixel p = sprCar->GetPixel((int32_t)(nx + 0.5f), (int32_t)(ny + 0.5f));
Draw(x, y, p); Draw(x, y, p);
} }
} }
SetPixelMode(olc::Pixel::NORMAL); SetPixelMode(olc::Pixel::NORMAL);
return true; return true;
} }
}; };
int main() int main()
{ {
SpriteTransforms demo; SpriteTransforms demo;
if (demo.Construct(256, 240, 4, 4)) if (demo.Construct(256, 240, 4, 4))
demo.Start(); demo.Start();
return 0; return 0;
} }

@ -1,224 +1,224 @@
#pragma once #pragma once
#ifdef WIN32 #ifdef WIN32
#include <windows.h> #include <windows.h>
#include <xinput.h> #include <xinput.h>
typedef DWORD(WINAPI XInputGetState_t)(DWORD dwUserIndex, XINPUT_STATE* pState); typedef DWORD(WINAPI XInputGetState_t)(DWORD dwUserIndex, XINPUT_STATE* pState);
static XInputGetState_t* XInputStateGet; static XInputGetState_t* XInputStateGet;
typedef DWORD(WINAPI XInputSetState_t)(DWORD dwUserIndex, XINPUT_VIBRATION* pVibration); typedef DWORD(WINAPI XInputSetState_t)(DWORD dwUserIndex, XINPUT_VIBRATION* pVibration);
static XInputSetState_t* XInputStateSet; static XInputSetState_t* XInputStateSet;
#endif #endif
#define C_BUTTON_COUNT 14 #define C_BUTTON_COUNT 14
enum CButton enum CButton
{ {
UP, UP,
DOWN, DOWN,
LEFT, LEFT,
RIGHT, RIGHT,
START, START,
BACK, BACK,
A, A,
B, B,
X, X,
Y, Y,
LEFT_SHOULDER, LEFT_SHOULDER,
RIGHT_SHOULDER, RIGHT_SHOULDER,
LEFT_THUMB, LEFT_THUMB,
RIGHT_THUMB RIGHT_THUMB
}; };
struct CBState struct CBState
{ {
bool bPressed = false; bool bPressed = false;
bool bReleased = false; bool bReleased = false;
bool bHeld = false; bool bHeld = false;
}; };
class ControllerManager class ControllerManager
{ {
private: private:
bool buttonState[C_BUTTON_COUNT]; bool buttonState[C_BUTTON_COUNT];
bool lastButtonState[C_BUTTON_COUNT]; bool lastButtonState[C_BUTTON_COUNT];
// Trigger values are in the range of 0 to 1, where 0 is fully // Trigger values are in the range of 0 to 1, where 0 is fully
// released and 1 is fully pressed. // released and 1 is fully pressed.
float triggerLeft = 0; float triggerLeft = 0;
float triggerRight = 0; float triggerRight = 0;
// Stick values are in the range of -1 to 1. For X values, -1 is // Stick values are in the range of -1 to 1. For X values, -1 is
// all the way to the left while +1 is all the way to the right. // all the way to the left while +1 is all the way to the right.
float leftStickX = 0; float leftStickX = 0;
float leftStickY = 0; float leftStickY = 0;
float rightStickX = 0; float rightStickX = 0;
float rightStickY = 0; float rightStickY = 0;
// Whether or not the controller is plugged in. // Whether or not the controller is plugged in.
bool pluggedIn = true; bool pluggedIn = true;
bool vibrating = false; bool vibrating = false;
float vibrateTime = 0; float vibrateTime = 0;
float vibrateCounter = 0; float vibrateCounter = 0;
public: public:
bool Initialize(); bool Initialize();
void Update(float dt); void Update(float dt);
void Vibrate(short amt, int timeMs); void Vibrate(short amt, int timeMs);
CBState GetButton(CButton button); CBState GetButton(CButton button);
float GetLeftTrigger() { return triggerLeft; } float GetLeftTrigger() { return triggerLeft; }
float GetRightTrigger() { return triggerRight; } float GetRightTrigger() { return triggerRight; }
float GetLeftStickX() { return leftStickX; } float GetLeftStickX() { return leftStickX; }
float GetLeftStickY() { return leftStickY; } float GetLeftStickY() { return leftStickY; }
float GetRightStickX() { return rightStickX; } float GetRightStickX() { return rightStickX; }
float GetRightStickY() { return rightStickY; } float GetRightStickY() { return rightStickY; }
bool IsVibrating() { return vibrating; } bool IsVibrating() { return vibrating; }
bool IsPluggedIn() { return pluggedIn; } bool IsPluggedIn() { return pluggedIn; }
private: private:
float NormalizeStickValue(short value); float NormalizeStickValue(short value);
}; };
bool ControllerManager::Initialize() bool ControllerManager::Initialize()
{ {
#ifdef WIN32 #ifdef WIN32
// TODO: Should we check for version 9.1.0 if we fail to find 1.4? // TODO: Should we check for version 9.1.0 if we fail to find 1.4?
HMODULE lib = LoadLibraryA("xinput1_4.dll"); HMODULE lib = LoadLibraryA("xinput1_4.dll");
if (!lib) return false; if (!lib) return false;
XInputStateGet = (XInputGetState_t*)GetProcAddress(lib, "XInputGetState"); XInputStateGet = (XInputGetState_t*)GetProcAddress(lib, "XInputGetState");
XInputStateSet = (XInputSetState_t*)GetProcAddress(lib, "XInputSetState"); XInputStateSet = (XInputSetState_t*)GetProcAddress(lib, "XInputSetState");
#endif #endif
return true; return true;
} }
float ControllerManager::NormalizeStickValue(short value) float ControllerManager::NormalizeStickValue(short value)
{ {
// The value we are given is in the range -32768 to 32767 with some deadzone around zero. // The value we are given is in the range -32768 to 32767 with some deadzone around zero.
// We will assume all values in this dead zone to be a reading of zero (the stick is not moved). // We will assume all values in this dead zone to be a reading of zero (the stick is not moved).
if (value > -7000 && value < 7000) return 0; if (value > -7000 && value < 7000) return 0;
// Otherwise, we are going to normalize the value. // Otherwise, we are going to normalize the value.
return ((value + 32768.0f) / (32768.0f + 32767.0f) * 2) - 1; return ((value + 32768.0f) / (32768.0f + 32767.0f) * 2) - 1;
} }
void ControllerManager::Vibrate(short amt, int timeMs) void ControllerManager::Vibrate(short amt, int timeMs)
{ {
// If we are already vibrating, just ignore this, unless they say zero, in which case we will let them stop it. // If we are already vibrating, just ignore this, unless they say zero, in which case we will let them stop it.
if (vibrating && amt != 0) return; if (vibrating && amt != 0) return;
// Only start the timer if we are actually vibrating. // Only start the timer if we are actually vibrating.
if (amt != 0) if (amt != 0)
{ {
vibrateTime = timeMs / 1000.0f; vibrateTime = timeMs / 1000.0f;
vibrating = true; vibrating = true;
} }
#ifdef WIN32 #ifdef WIN32
XINPUT_VIBRATION info = XINPUT_VIBRATION info =
{ {
amt, amt,
amt amt
}; };
XInputStateSet(0, &info); XInputStateSet(0, &info);
#endif #endif
} }
CBState ControllerManager::GetButton(CButton button) CBState ControllerManager::GetButton(CButton button)
{ {
return return
{ {
!lastButtonState[button] && buttonState[button], !lastButtonState[button] && buttonState[button],
lastButtonState[button] && !buttonState[button], lastButtonState[button] && !buttonState[button],
lastButtonState[button] && buttonState[button] lastButtonState[button] && buttonState[button]
}; };
} }
void ControllerManager::Update(float dt) void ControllerManager::Update(float dt)
{ {
#ifdef WIN32 #ifdef WIN32
if (vibrating) if (vibrating)
{ {
vibrateCounter += dt; vibrateCounter += dt;
if (vibrateCounter >= vibrateTime) if (vibrateCounter >= vibrateTime)
{ {
XINPUT_VIBRATION info = XINPUT_VIBRATION info =
{ {
0, 0 0, 0
}; };
XInputStateSet(0, &info); XInputStateSet(0, &info);
vibrating = false; vibrating = false;
vibrateCounter = 0; vibrateCounter = 0;
vibrateTime = 0; vibrateTime = 0;
} }
} }
for (int i = 0; i < C_BUTTON_COUNT; i++) for (int i = 0; i < C_BUTTON_COUNT; i++)
{ {
lastButtonState[i] = buttonState[i]; lastButtonState[i] = buttonState[i];
} }
XINPUT_STATE state; XINPUT_STATE state;
// Try and get the first controller. For now we will only support a single one. // Try and get the first controller. For now we will only support a single one.
DWORD res = XInputStateGet(0, &state); DWORD res = XInputStateGet(0, &state);
// If the controller is plugged in, handle input. // If the controller is plugged in, handle input.
if (res == ERROR_SUCCESS) if (res == ERROR_SUCCESS)
{ {
XINPUT_GAMEPAD* pad = &state.Gamepad; XINPUT_GAMEPAD* pad = &state.Gamepad;
buttonState[UP] = (pad->wButtons & XINPUT_GAMEPAD_DPAD_UP); buttonState[UP] = (pad->wButtons & XINPUT_GAMEPAD_DPAD_UP);
buttonState[DOWN] = (pad->wButtons & XINPUT_GAMEPAD_DPAD_DOWN); buttonState[DOWN] = (pad->wButtons & XINPUT_GAMEPAD_DPAD_DOWN);
buttonState[LEFT] = (pad->wButtons & XINPUT_GAMEPAD_DPAD_LEFT); buttonState[LEFT] = (pad->wButtons & XINPUT_GAMEPAD_DPAD_LEFT);
buttonState[RIGHT] = (pad->wButtons & XINPUT_GAMEPAD_DPAD_RIGHT); buttonState[RIGHT] = (pad->wButtons & XINPUT_GAMEPAD_DPAD_RIGHT);
buttonState[START] = (pad->wButtons & XINPUT_GAMEPAD_START); buttonState[START] = (pad->wButtons & XINPUT_GAMEPAD_START);
buttonState[BACK] = (pad->wButtons & XINPUT_GAMEPAD_BACK); buttonState[BACK] = (pad->wButtons & XINPUT_GAMEPAD_BACK);
buttonState[LEFT_SHOULDER] = (pad->wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER); buttonState[LEFT_SHOULDER] = (pad->wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER);
buttonState[RIGHT_SHOULDER] = (pad->wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER); buttonState[RIGHT_SHOULDER] = (pad->wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER);
buttonState[LEFT_THUMB] = (pad->wButtons & XINPUT_GAMEPAD_LEFT_THUMB); buttonState[LEFT_THUMB] = (pad->wButtons & XINPUT_GAMEPAD_LEFT_THUMB);
buttonState[RIGHT_THUMB] = (pad->wButtons & XINPUT_GAMEPAD_RIGHT_THUMB); buttonState[RIGHT_THUMB] = (pad->wButtons & XINPUT_GAMEPAD_RIGHT_THUMB);
buttonState[A] = (pad->wButtons & XINPUT_GAMEPAD_A); buttonState[A] = (pad->wButtons & XINPUT_GAMEPAD_A);
buttonState[B] = (pad->wButtons & XINPUT_GAMEPAD_B); buttonState[B] = (pad->wButtons & XINPUT_GAMEPAD_B);
buttonState[X] = (pad->wButtons & XINPUT_GAMEPAD_X); buttonState[X] = (pad->wButtons & XINPUT_GAMEPAD_X);
buttonState[Y] = (pad->wButtons & XINPUT_GAMEPAD_Y); buttonState[Y] = (pad->wButtons & XINPUT_GAMEPAD_Y);
triggerLeft = pad->bLeftTrigger / 255.0f; triggerLeft = pad->bLeftTrigger / 255.0f;
triggerRight = pad->bRightTrigger / 255.0f; triggerRight = pad->bRightTrigger / 255.0f;
leftStickX = NormalizeStickValue(pad->sThumbLX); leftStickX = NormalizeStickValue(pad->sThumbLX);
leftStickY = NormalizeStickValue(pad->sThumbLY); leftStickY = NormalizeStickValue(pad->sThumbLY);
rightStickX = NormalizeStickValue(pad->sThumbRX); rightStickX = NormalizeStickValue(pad->sThumbRX);
rightStickY = NormalizeStickValue(pad->sThumbRY); rightStickY = NormalizeStickValue(pad->sThumbRY);
if (!pluggedIn) if (!pluggedIn)
{ {
pluggedIn = true; pluggedIn = true;
// Send callback. // Send callback.
// printf("Plugged in.\n"); // printf("Plugged in.\n");
} }
} }
else else
{ {
if (pluggedIn) if (pluggedIn)
{ {
pluggedIn = false; pluggedIn = false;
// Send callback. // Send callback.
// printf("Unplugged.\n"); // printf("Unplugged.\n");
} }
} }
#else #else
for (int i = 0; i < C_BUTTON_COUNT; i++) for (int i = 0; i < C_BUTTON_COUNT; i++)
{ {
lastButtonState[i] = buttonState[i] = false; lastButtonState[i] = buttonState[i] = false;
} }
#endif #endif
} }

@ -1,42 +1,42 @@
License (OLC-3) License (OLC-3)
~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~
Copyright 2018 - 2019 OneLoneCoder.com Copyright 2018 - 2019 OneLoneCoder.com
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions modification, are permitted provided that the following conditions
are met: are met:
1. Redistributions or derivations of source code must retain the above 1. Redistributions or derivations of source code must retain the above
copyright notice, this list of conditions and the following disclaimer. copyright notice, this list of conditions and the following disclaimer.
2. Redistributions or derivative works in binary form must reproduce 2. Redistributions or derivative works in binary form must reproduce
the above copyright notice. This list of conditions and the following the above copyright notice. This list of conditions and the following
disclaimer must be reproduced in the documentation and/or other disclaimer must be reproduced in the documentation and/or other
materials provided with the distribution. materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its 3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived contributors may be used to endorse or promote products derived
from this software without specific prior written permission. from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Links Links
~~~~~ ~~~~~
YouTube: https://www.youtube.com/javidx9 YouTube: https://www.youtube.com/javidx9
Discord: https://discord.gg/WhwHUMV Discord: https://discord.gg/WhwHUMV
Twitter: https://www.twitter.com/javidx9 Twitter: https://www.twitter.com/javidx9
Twitch: https://www.twitch.tv/javidx9 Twitch: https://www.twitch.tv/javidx9
GitHub: https://www.github.com/onelonecoder GitHub: https://www.github.com/onelonecoder
Homepage: https://www.onelonecoder.com Homepage: https://www.onelonecoder.com
Patreon: https://www.patreon.com/javidx9 Patreon: https://www.patreon.com/javidx9

@ -1,313 +1,313 @@
/* /*
olcPGEX_Graphics2D.h olcPGEX_Graphics2D.h
+-------------------------------------------------------------+ +-------------------------------------------------------------+
| OneLoneCoder Pixel Game Engine Extension | | OneLoneCoder Pixel Game Engine Extension |
| Advanced 2D Rendering - v0.4 | | Advanced 2D Rendering - v0.4 |
+-------------------------------------------------------------+ +-------------------------------------------------------------+
What is this? What is this?
~~~~~~~~~~~~~ ~~~~~~~~~~~~~
This is an extension to the olcPixelGameEngine, which provides This is an extension to the olcPixelGameEngine, which provides
advanced olc::Sprite manipulation and drawing routines. To use advanced olc::Sprite manipulation and drawing routines. To use
it, simply include this header file. it, simply include this header file.
License (OLC-3) License (OLC-3)
~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~
Copyright 2018 - 2019 OneLoneCoder.com Copyright 2018 - 2019 OneLoneCoder.com
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions modification, are permitted provided that the following conditions
are met: are met:
1. Redistributions or derivations of source code must retain the above 1. Redistributions or derivations of source code must retain the above
copyright notice, this list of conditions and the following disclaimer. copyright notice, this list of conditions and the following disclaimer.
2. Redistributions or derivative works in binary form must reproduce 2. Redistributions or derivative works in binary form must reproduce
the above copyright notice. This list of conditions and the following the above copyright notice. This list of conditions and the following
disclaimer must be reproduced in the documentation and/or other disclaimer must be reproduced in the documentation and/or other
materials provided with the distribution. materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its 3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived contributors may be used to endorse or promote products derived
from this software without specific prior written permission. from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Links Links
~~~~~ ~~~~~
YouTube: https://www.youtube.com/javidx9 YouTube: https://www.youtube.com/javidx9
Discord: https://discord.gg/WhwHUMV Discord: https://discord.gg/WhwHUMV
Twitter: https://www.twitter.com/javidx9 Twitter: https://www.twitter.com/javidx9
Twitch: https://www.twitch.tv/javidx9 Twitch: https://www.twitch.tv/javidx9
GitHub: https://www.github.com/onelonecoder GitHub: https://www.github.com/onelonecoder
Homepage: https://www.onelonecoder.com Homepage: https://www.onelonecoder.com
Author Author
~~~~~~ ~~~~~~
David Barr, aka javidx9, ©OneLoneCoder 2019 David Barr, aka javidx9, ©OneLoneCoder 2019
*/ */
/* /*
Matrices stored as [Column][Row] (i.e. x, y) Matrices stored as [Column][Row] (i.e. x, y)
|C0R0 C1R0 C2R0| | x | | x'| |C0R0 C1R0 C2R0| | x | | x'|
|C0R1 C1R1 C2R1| * | y | = | y'| |C0R1 C1R1 C2R1| * | y | = | y'|
|C0R2 C1R2 C2R2| |1.0| | - | |C0R2 C1R2 C2R2| |1.0| | - |
*/ */
#ifndef OLC_PGEX_GFX2D #ifndef OLC_PGEX_GFX2D
#define OLC_PGEX_GFX2D #define OLC_PGEX_GFX2D
#include <algorithm> #include <algorithm>
#undef min #undef min
#undef max #undef max
namespace olc namespace olc
{ {
// Container class for Advanced 2D Drawing functions // Container class for Advanced 2D Drawing functions
class GFX2D : public olc::PGEX class GFX2D : public olc::PGEX
{ {
// A representation of an affine transform, used to rotate, scale, offset & shear space // A representation of an affine transform, used to rotate, scale, offset & shear space
public: public:
class Transform2D class Transform2D
{ {
public: public:
Transform2D(); Transform2D();
public: public:
// Set this transformation to unity // Set this transformation to unity
void Reset(); void Reset();
// Append a rotation of fTheta radians to this transform // Append a rotation of fTheta radians to this transform
void Rotate(float fTheta); void Rotate(float fTheta);
// Append a translation (ox, oy) to this transform // Append a translation (ox, oy) to this transform
void Translate(float ox, float oy); void Translate(float ox, float oy);
// Append a scaling operation (sx, sy) to this transform // Append a scaling operation (sx, sy) to this transform
void Scale(float sx, float sy); void Scale(float sx, float sy);
// Append a shear operation (sx, sy) to this transform // Append a shear operation (sx, sy) to this transform
void Shear(float sx, float sy); void Shear(float sx, float sy);
void Perspective(float ox, float oy); void Perspective(float ox, float oy);
// Calculate the Forward Transformation of the coordinate (in_x, in_y) -> (out_x, out_y) // Calculate the Forward Transformation of the coordinate (in_x, in_y) -> (out_x, out_y)
void Forward(float in_x, float in_y, float &out_x, float &out_y); void Forward(float in_x, float in_y, float &out_x, float &out_y);
// Calculate the Inverse Transformation of the coordinate (in_x, in_y) -> (out_x, out_y) // Calculate the Inverse Transformation of the coordinate (in_x, in_y) -> (out_x, out_y)
void Backward(float in_x, float in_y, float &out_x, float &out_y); void Backward(float in_x, float in_y, float &out_x, float &out_y);
// Regenerate the Inverse Transformation // Regenerate the Inverse Transformation
void Invert(); void Invert();
private: private:
void Multiply(); void Multiply();
float matrix[4][3][3]; float matrix[4][3][3];
int nTargetMatrix; int nTargetMatrix;
int nSourceMatrix; int nSourceMatrix;
bool bDirty; bool bDirty;
}; };
public: public:
// Draws a sprite with the transform applied // Draws a sprite with the transform applied
static void DrawSprite(olc::Sprite *sprite, olc::GFX2D::Transform2D &transform); static void DrawSprite(olc::Sprite *sprite, olc::GFX2D::Transform2D &transform);
}; };
} }
#ifdef OLC_PGE_GRAPHICS2D #ifdef OLC_PGE_GRAPHICS2D
#undef OLC_PGE_GRAPHICS2D #undef OLC_PGE_GRAPHICS2D
namespace olc namespace olc
{ {
void GFX2D::DrawSprite(olc::Sprite *sprite, olc::GFX2D::Transform2D &transform) void GFX2D::DrawSprite(olc::Sprite *sprite, olc::GFX2D::Transform2D &transform)
{ {
if (sprite == nullptr) if (sprite == nullptr)
return; return;
// Work out bounding rectangle of sprite // Work out bounding rectangle of sprite
float ex, ey; float ex, ey;
float sx, sy; float sx, sy;
float px, py; float px, py;
transform.Forward(0.0f, 0.0f, sx, sy); transform.Forward(0.0f, 0.0f, sx, sy);
px = sx; py = sy; px = sx; py = sy;
sx = std::min(sx, px); sy = std::min(sy, py); sx = std::min(sx, px); sy = std::min(sy, py);
ex = std::max(ex, px); ey = std::max(ey, py); ex = std::max(ex, px); ey = std::max(ey, py);
transform.Forward((float)sprite->width, (float)sprite->height, px, py); transform.Forward((float)sprite->width, (float)sprite->height, px, py);
sx = std::min(sx, px); sy = std::min(sy, py); sx = std::min(sx, px); sy = std::min(sy, py);
ex = std::max(ex, px); ey = std::max(ey, py); ex = std::max(ex, px); ey = std::max(ey, py);
transform.Forward(0.0f, (float)sprite->height, px, py); transform.Forward(0.0f, (float)sprite->height, px, py);
sx = std::min(sx, px); sy = std::min(sy, py); sx = std::min(sx, px); sy = std::min(sy, py);
ex = std::max(ex, px); ey = std::max(ey, py); ex = std::max(ex, px); ey = std::max(ey, py);
transform.Forward((float)sprite->width, 0.0f, px, py); transform.Forward((float)sprite->width, 0.0f, px, py);
sx = std::min(sx, px); sy = std::min(sy, py); sx = std::min(sx, px); sy = std::min(sy, py);
ex = std::max(ex, px); ey = std::max(ey, py); ex = std::max(ex, px); ey = std::max(ey, py);
// Perform inversion of transform if required // Perform inversion of transform if required
transform.Invert(); transform.Invert();
if (ex < sx) if (ex < sx)
std::swap(ex, sx); std::swap(ex, sx);
if (ey < sy) if (ey < sy)
std::swap(ey, sy); std::swap(ey, sy);
// Iterate through render space, and sample Sprite from suitable texel location // Iterate through render space, and sample Sprite from suitable texel location
for (float i = sx; i < ex; i++) for (float i = sx; i < ex; i++)
{ {
for (float j = sy; j < ey; j++) for (float j = sy; j < ey; j++)
{ {
float ox, oy; float ox, oy;
transform.Backward(i, j, ox, oy); transform.Backward(i, j, ox, oy);
pge->Draw((int32_t)i, (int32_t)j, sprite->GetPixel((int32_t)(ox+0.5f), (int32_t)(oy+0.5f))); pge->Draw((int32_t)i, (int32_t)j, sprite->GetPixel((int32_t)(ox+0.5f), (int32_t)(oy+0.5f)));
} }
} }
} }
olc::GFX2D::Transform2D::Transform2D() olc::GFX2D::Transform2D::Transform2D()
{ {
Reset(); Reset();
} }
void olc::GFX2D::Transform2D::Reset() void olc::GFX2D::Transform2D::Reset()
{ {
nTargetMatrix = 0; nTargetMatrix = 0;
nSourceMatrix = 1; nSourceMatrix = 1;
bDirty = true; bDirty = true;
// Columns Then Rows // Columns Then Rows
// Matrices 0 & 1 are used as swaps in Transform accumulation // Matrices 0 & 1 are used as swaps in Transform accumulation
matrix[0][0][0] = 1.0f; matrix[0][1][0] = 0.0f; matrix[0][2][0] = 0.0f; matrix[0][0][0] = 1.0f; matrix[0][1][0] = 0.0f; matrix[0][2][0] = 0.0f;
matrix[0][0][1] = 0.0f; matrix[0][1][1] = 1.0f; matrix[0][2][1] = 0.0f; matrix[0][0][1] = 0.0f; matrix[0][1][1] = 1.0f; matrix[0][2][1] = 0.0f;
matrix[0][0][2] = 0.0f; matrix[0][1][2] = 0.0f; matrix[0][2][2] = 1.0f; matrix[0][0][2] = 0.0f; matrix[0][1][2] = 0.0f; matrix[0][2][2] = 1.0f;
matrix[1][0][0] = 1.0f; matrix[1][1][0] = 0.0f; matrix[1][2][0] = 0.0f; matrix[1][0][0] = 1.0f; matrix[1][1][0] = 0.0f; matrix[1][2][0] = 0.0f;
matrix[1][0][1] = 0.0f; matrix[1][1][1] = 1.0f; matrix[1][2][1] = 0.0f; matrix[1][0][1] = 0.0f; matrix[1][1][1] = 1.0f; matrix[1][2][1] = 0.0f;
matrix[1][0][2] = 0.0f; matrix[1][1][2] = 0.0f; matrix[1][2][2] = 1.0f; matrix[1][0][2] = 0.0f; matrix[1][1][2] = 0.0f; matrix[1][2][2] = 1.0f;
// Matrix 2 is a cache matrix to hold the immediate transform operation // Matrix 2 is a cache matrix to hold the immediate transform operation
// Matrix 3 is a cache matrix to hold the inverted transform // Matrix 3 is a cache matrix to hold the inverted transform
} }
void olc::GFX2D::Transform2D::Multiply() void olc::GFX2D::Transform2D::Multiply()
{ {
for (int c = 0; c < 3; c++) for (int c = 0; c < 3; c++)
{ {
for (int r = 0; r < 3; r++) for (int r = 0; r < 3; r++)
{ {
matrix[nTargetMatrix][c][r] = matrix[2][0][r] * matrix[nSourceMatrix][c][0] + matrix[nTargetMatrix][c][r] = matrix[2][0][r] * matrix[nSourceMatrix][c][0] +
matrix[2][1][r] * matrix[nSourceMatrix][c][1] + matrix[2][1][r] * matrix[nSourceMatrix][c][1] +
matrix[2][2][r] * matrix[nSourceMatrix][c][2]; matrix[2][2][r] * matrix[nSourceMatrix][c][2];
} }
} }
std::swap(nTargetMatrix, nSourceMatrix); std::swap(nTargetMatrix, nSourceMatrix);
bDirty = true; // Any transform multiply dirties the inversion bDirty = true; // Any transform multiply dirties the inversion
} }
void olc::GFX2D::Transform2D::Rotate(float fTheta) void olc::GFX2D::Transform2D::Rotate(float fTheta)
{ {
// Construct Rotation Matrix // Construct Rotation Matrix
matrix[2][0][0] = cosf(fTheta); matrix[2][1][0] = sinf(fTheta); matrix[2][2][0] = 0.0f; matrix[2][0][0] = cosf(fTheta); matrix[2][1][0] = sinf(fTheta); matrix[2][2][0] = 0.0f;
matrix[2][0][1] = -sinf(fTheta); matrix[2][1][1] = cosf(fTheta); matrix[2][2][1] = 0.0f; matrix[2][0][1] = -sinf(fTheta); matrix[2][1][1] = cosf(fTheta); matrix[2][2][1] = 0.0f;
matrix[2][0][2] = 0.0f; matrix[2][1][2] = 0.0f; matrix[2][2][2] = 1.0f; matrix[2][0][2] = 0.0f; matrix[2][1][2] = 0.0f; matrix[2][2][2] = 1.0f;
Multiply(); Multiply();
} }
void olc::GFX2D::Transform2D::Scale(float sx, float sy) void olc::GFX2D::Transform2D::Scale(float sx, float sy)
{ {
// Construct Scale Matrix // Construct Scale Matrix
matrix[2][0][0] = sx; matrix[2][1][0] = 0.0f; matrix[2][2][0] = 0.0f; matrix[2][0][0] = sx; matrix[2][1][0] = 0.0f; matrix[2][2][0] = 0.0f;
matrix[2][0][1] = 0.0f; matrix[2][1][1] = sy; matrix[2][2][1] = 0.0f; matrix[2][0][1] = 0.0f; matrix[2][1][1] = sy; matrix[2][2][1] = 0.0f;
matrix[2][0][2] = 0.0f; matrix[2][1][2] = 0.0f; matrix[2][2][2] = 1.0f; matrix[2][0][2] = 0.0f; matrix[2][1][2] = 0.0f; matrix[2][2][2] = 1.0f;
Multiply(); Multiply();
} }
void olc::GFX2D::Transform2D::Shear(float sx, float sy) void olc::GFX2D::Transform2D::Shear(float sx, float sy)
{ {
// Construct Shear Matrix // Construct Shear Matrix
matrix[2][0][0] = 1.0f; matrix[2][1][0] = sx; matrix[2][2][0] = 0.0f; matrix[2][0][0] = 1.0f; matrix[2][1][0] = sx; matrix[2][2][0] = 0.0f;
matrix[2][0][1] = sy; matrix[2][1][1] = 1.0f; matrix[2][2][1] = 0.0f; matrix[2][0][1] = sy; matrix[2][1][1] = 1.0f; matrix[2][2][1] = 0.0f;
matrix[2][0][2] = 0.0f; matrix[2][1][2] = 0.0f; matrix[2][2][2] = 1.0f; matrix[2][0][2] = 0.0f; matrix[2][1][2] = 0.0f; matrix[2][2][2] = 1.0f;
Multiply(); Multiply();
} }
void olc::GFX2D::Transform2D::Translate(float ox, float oy) void olc::GFX2D::Transform2D::Translate(float ox, float oy)
{ {
// Construct Translate Matrix // Construct Translate Matrix
matrix[2][0][0] = 1.0f; matrix[2][1][0] = 0.0f; matrix[2][2][0] = ox; matrix[2][0][0] = 1.0f; matrix[2][1][0] = 0.0f; matrix[2][2][0] = ox;
matrix[2][0][1] = 0.0f; matrix[2][1][1] = 1.0f; matrix[2][2][1] = oy; matrix[2][0][1] = 0.0f; matrix[2][1][1] = 1.0f; matrix[2][2][1] = oy;
matrix[2][0][2] = 0.0f; matrix[2][1][2] = 0.0f; matrix[2][2][2] = 1.0f; matrix[2][0][2] = 0.0f; matrix[2][1][2] = 0.0f; matrix[2][2][2] = 1.0f;
Multiply(); Multiply();
} }
void olc::GFX2D::Transform2D::Perspective(float ox, float oy) void olc::GFX2D::Transform2D::Perspective(float ox, float oy)
{ {
// Construct Translate Matrix // Construct Translate Matrix
matrix[2][0][0] = 1.0f; matrix[2][1][0] = 0.0f; matrix[2][2][0] = 0.0f; matrix[2][0][0] = 1.0f; matrix[2][1][0] = 0.0f; matrix[2][2][0] = 0.0f;
matrix[2][0][1] = 0.0f; matrix[2][1][1] = 1.0f; matrix[2][2][1] = 0.0f; matrix[2][0][1] = 0.0f; matrix[2][1][1] = 1.0f; matrix[2][2][1] = 0.0f;
matrix[2][0][2] = ox; matrix[2][1][2] = oy; matrix[2][2][2] = 1.0f; matrix[2][0][2] = ox; matrix[2][1][2] = oy; matrix[2][2][2] = 1.0f;
Multiply(); Multiply();
} }
void olc::GFX2D::Transform2D::Forward(float in_x, float in_y, float &out_x, float &out_y) void olc::GFX2D::Transform2D::Forward(float in_x, float in_y, float &out_x, float &out_y)
{ {
out_x = in_x * matrix[nSourceMatrix][0][0] + in_y * matrix[nSourceMatrix][1][0] + matrix[nSourceMatrix][2][0]; out_x = in_x * matrix[nSourceMatrix][0][0] + in_y * matrix[nSourceMatrix][1][0] + matrix[nSourceMatrix][2][0];
out_y = in_x * matrix[nSourceMatrix][0][1] + in_y * matrix[nSourceMatrix][1][1] + matrix[nSourceMatrix][2][1]; out_y = in_x * matrix[nSourceMatrix][0][1] + in_y * matrix[nSourceMatrix][1][1] + matrix[nSourceMatrix][2][1];
float out_z = in_x * matrix[nSourceMatrix][0][2] + in_y * matrix[nSourceMatrix][1][2] + matrix[nSourceMatrix][2][2]; float out_z = in_x * matrix[nSourceMatrix][0][2] + in_y * matrix[nSourceMatrix][1][2] + matrix[nSourceMatrix][2][2];
if (out_z != 0) if (out_z != 0)
{ {
out_x /= out_z; out_x /= out_z;
out_y /= out_z; out_y /= out_z;
} }
} }
void olc::GFX2D::Transform2D::Backward(float in_x, float in_y, float &out_x, float &out_y) void olc::GFX2D::Transform2D::Backward(float in_x, float in_y, float &out_x, float &out_y)
{ {
out_x = in_x * matrix[3][0][0] + in_y * matrix[3][1][0] + matrix[3][2][0]; out_x = in_x * matrix[3][0][0] + in_y * matrix[3][1][0] + matrix[3][2][0];
out_y = in_x * matrix[3][0][1] + in_y * matrix[3][1][1] + matrix[3][2][1]; out_y = in_x * matrix[3][0][1] + in_y * matrix[3][1][1] + matrix[3][2][1];
float out_z = in_x * matrix[3][0][2] + in_y * matrix[3][1][2] + matrix[3][2][2]; float out_z = in_x * matrix[3][0][2] + in_y * matrix[3][1][2] + matrix[3][2][2];
if (out_z != 0) if (out_z != 0)
{ {
out_x /= out_z; out_x /= out_z;
out_y /= out_z; out_y /= out_z;
} }
} }
void olc::GFX2D::Transform2D::Invert() void olc::GFX2D::Transform2D::Invert()
{ {
if (bDirty) // Obviously costly so only do if needed if (bDirty) // Obviously costly so only do if needed
{ {
float det = matrix[nSourceMatrix][0][0] * (matrix[nSourceMatrix][1][1] * matrix[nSourceMatrix][2][2] - matrix[nSourceMatrix][1][2] * matrix[nSourceMatrix][2][1]) - float det = matrix[nSourceMatrix][0][0] * (matrix[nSourceMatrix][1][1] * matrix[nSourceMatrix][2][2] - matrix[nSourceMatrix][1][2] * matrix[nSourceMatrix][2][1]) -
matrix[nSourceMatrix][1][0] * (matrix[nSourceMatrix][0][1] * matrix[nSourceMatrix][2][2] - matrix[nSourceMatrix][2][1] * matrix[nSourceMatrix][0][2]) + matrix[nSourceMatrix][1][0] * (matrix[nSourceMatrix][0][1] * matrix[nSourceMatrix][2][2] - matrix[nSourceMatrix][2][1] * matrix[nSourceMatrix][0][2]) +
matrix[nSourceMatrix][2][0] * (matrix[nSourceMatrix][0][1] * matrix[nSourceMatrix][1][2] - matrix[nSourceMatrix][1][1] * matrix[nSourceMatrix][0][2]); matrix[nSourceMatrix][2][0] * (matrix[nSourceMatrix][0][1] * matrix[nSourceMatrix][1][2] - matrix[nSourceMatrix][1][1] * matrix[nSourceMatrix][0][2]);
float idet = 1.0f / det; float idet = 1.0f / det;
matrix[3][0][0] = (matrix[nSourceMatrix][1][1] * matrix[nSourceMatrix][2][2] - matrix[nSourceMatrix][1][2] * matrix[nSourceMatrix][2][1]) * idet; matrix[3][0][0] = (matrix[nSourceMatrix][1][1] * matrix[nSourceMatrix][2][2] - matrix[nSourceMatrix][1][2] * matrix[nSourceMatrix][2][1]) * idet;
matrix[3][1][0] = (matrix[nSourceMatrix][2][0] * matrix[nSourceMatrix][1][2] - matrix[nSourceMatrix][1][0] * matrix[nSourceMatrix][2][2]) * idet; matrix[3][1][0] = (matrix[nSourceMatrix][2][0] * matrix[nSourceMatrix][1][2] - matrix[nSourceMatrix][1][0] * matrix[nSourceMatrix][2][2]) * idet;
matrix[3][2][0] = (matrix[nSourceMatrix][1][0] * matrix[nSourceMatrix][2][1] - matrix[nSourceMatrix][2][0] * matrix[nSourceMatrix][1][1]) * idet; matrix[3][2][0] = (matrix[nSourceMatrix][1][0] * matrix[nSourceMatrix][2][1] - matrix[nSourceMatrix][2][0] * matrix[nSourceMatrix][1][1]) * idet;
matrix[3][0][1] = (matrix[nSourceMatrix][2][1] * matrix[nSourceMatrix][0][2] - matrix[nSourceMatrix][0][1] * matrix[nSourceMatrix][2][2]) * idet; matrix[3][0][1] = (matrix[nSourceMatrix][2][1] * matrix[nSourceMatrix][0][2] - matrix[nSourceMatrix][0][1] * matrix[nSourceMatrix][2][2]) * idet;
matrix[3][1][1] = (matrix[nSourceMatrix][0][0] * matrix[nSourceMatrix][2][2] - matrix[nSourceMatrix][2][0] * matrix[nSourceMatrix][0][2]) * idet; matrix[3][1][1] = (matrix[nSourceMatrix][0][0] * matrix[nSourceMatrix][2][2] - matrix[nSourceMatrix][2][0] * matrix[nSourceMatrix][0][2]) * idet;
matrix[3][2][1] = (matrix[nSourceMatrix][0][1] * matrix[nSourceMatrix][2][0] - matrix[nSourceMatrix][0][0] * matrix[nSourceMatrix][2][1]) * idet; matrix[3][2][1] = (matrix[nSourceMatrix][0][1] * matrix[nSourceMatrix][2][0] - matrix[nSourceMatrix][0][0] * matrix[nSourceMatrix][2][1]) * idet;
matrix[3][0][2] = (matrix[nSourceMatrix][0][1] * matrix[nSourceMatrix][1][2] - matrix[nSourceMatrix][0][2] * matrix[nSourceMatrix][1][1]) * idet; matrix[3][0][2] = (matrix[nSourceMatrix][0][1] * matrix[nSourceMatrix][1][2] - matrix[nSourceMatrix][0][2] * matrix[nSourceMatrix][1][1]) * idet;
matrix[3][1][2] = (matrix[nSourceMatrix][0][2] * matrix[nSourceMatrix][1][0] - matrix[nSourceMatrix][0][0] * matrix[nSourceMatrix][1][2]) * idet; matrix[3][1][2] = (matrix[nSourceMatrix][0][2] * matrix[nSourceMatrix][1][0] - matrix[nSourceMatrix][0][0] * matrix[nSourceMatrix][1][2]) * idet;
matrix[3][2][2] = (matrix[nSourceMatrix][0][0] * matrix[nSourceMatrix][1][1] - matrix[nSourceMatrix][0][1] * matrix[nSourceMatrix][1][0]) * idet; matrix[3][2][2] = (matrix[nSourceMatrix][0][0] * matrix[nSourceMatrix][1][1] - matrix[nSourceMatrix][0][1] * matrix[nSourceMatrix][1][0]) * idet;
bDirty = false; bDirty = false;
} }
} }
} }
#endif #endif
#endif #endif

@ -1,381 +1,381 @@
#pragma once #pragma once
#include "olcPixelGameEngine.h" #include "olcPixelGameEngine.h"
#include <algorithm> #include <algorithm>
#undef min #undef min
#undef max #undef max
namespace olc namespace olc
{ {
class TILE : public olc::PGEX class TILE : public olc::PGEX
{ {
public: public:
struct Edge struct Edge
{ {
float sx, sy; float sx, sy;
float ex, ey; float ex, ey;
}; };
public: public:
class Atlas class Atlas
{ {
public: public:
Atlas(); Atlas();
void Create(olc::Sprite *tileSheet); void Create(olc::Sprite *tileSheet);
olc::rcode LoadFromFile(std::string filename); olc::rcode LoadFromFile(std::string filename);
olc::rcode SaveToFile(std::string filename); olc::rcode SaveToFile(std::string filename);
public: public:
olc::Sprite *sprTileSheet; olc::Sprite *sprTileSheet;
std::vector<std::tuple<int32_t, int32_t, int32_t, int32_t>> location; std::vector<std::tuple<int32_t, int32_t, int32_t, int32_t>> location;
}; };
public: public:
template <class T> template <class T>
class Layer class Layer
{ {
public: public:
Layer(); Layer();
void Create(int32_t w, int32_t h, int32_t tw, int32_t th); void Create(int32_t w, int32_t h, int32_t tw, int32_t th);
olc::rcode LoadFromFile(std::string filename); olc::rcode LoadFromFile(std::string filename);
olc::rcode SaveToFile(std::string filename); olc::rcode SaveToFile(std::string filename);
T* GetTile(int32_t x, int32_t y); T* GetTile(int32_t x, int32_t y);
public: public:
int32_t nLayerWidth; int32_t nLayerWidth;
int32_t nLayerHeight; int32_t nLayerHeight;
int32_t nTileWidth; int32_t nTileWidth;
int32_t nTileHeight; int32_t nTileHeight;
private: private:
T *pTiles; T *pTiles;
}; };
class BasicTile class BasicTile
{ {
public: public:
BasicTile(); BasicTile();
public: public:
int32_t id; int32_t id;
bool exist; bool exist;
int edge_id[4]; int edge_id[4];
bool edge_exist[4]; bool edge_exist[4];
}; };
public: public:
template<typename T> template<typename T>
static void DrawLayer(olc::TILE::Layer<T> &layer, olc::TILE::Atlas &atlas, float cam_x, float cam_y, int tiles_x, int tiles_y, int nScale = 1); static void DrawLayer(olc::TILE::Layer<T> &layer, olc::TILE::Atlas &atlas, float cam_x, float cam_y, int tiles_x, int tiles_y, int nScale = 1);
template<typename T> template<typename T>
static olc::Pixel GetLayerPixel(olc::TILE::Layer<T> &layer, olc::TILE::Atlas &atlas, float x, float y); static olc::Pixel GetLayerPixel(olc::TILE::Layer<T> &layer, olc::TILE::Atlas &atlas, float x, float y);
template<typename T> template<typename T>
static std::vector<olc::TILE::Edge> ExtractEdgesFromLayer(olc::TILE::Layer<T> &layer, int sx, int sy, int width, int height); static std::vector<olc::TILE::Edge> ExtractEdgesFromLayer(olc::TILE::Layer<T> &layer, int sx, int sy, int width, int height);
}; };
} }
namespace olc namespace olc
{ {
TILE::BasicTile::BasicTile() TILE::BasicTile::BasicTile()
{ {
exist = false; exist = false;
id = 0; id = 0;
for (int i = 0; i < 4; i++) for (int i = 0; i < 4; i++)
{ {
edge_exist[i] = false; edge_exist[i] = false;
edge_id[i] = 0; edge_id[i] = 0;
} }
} }
template <class T> template <class T>
TILE::Layer<T>::Layer() TILE::Layer<T>::Layer()
{ {
} }
template <class T> template <class T>
void TILE::Layer<T>::Create(int32_t w, int32_t h, int32_t tw, int32_t th) void TILE::Layer<T>::Create(int32_t w, int32_t h, int32_t tw, int32_t th)
{ {
nLayerWidth = w; nLayerWidth = w;
nLayerHeight = h; nLayerHeight = h;
nTileWidth = tw; nTileWidth = tw;
nTileHeight = th; nTileHeight = th;
pTiles = new T[nLayerWidth * nLayerHeight]; pTiles = new T[nLayerWidth * nLayerHeight];
for (int i = 0; i < nLayerWidth*nLayerHeight; i++) for (int i = 0; i < nLayerWidth*nLayerHeight; i++)
{ {
pTiles[i].id = 0; pTiles[i].id = 0;
} }
} }
template <class T> template <class T>
olc::rcode TILE::Layer<T>::LoadFromFile(std::string filename) olc::rcode TILE::Layer<T>::LoadFromFile(std::string filename)
{ {
return olc::FAIL; return olc::FAIL;
} }
template <class T> template <class T>
olc::rcode TILE::Layer<T>::SaveToFile(std::string filename) olc::rcode TILE::Layer<T>::SaveToFile(std::string filename)
{ {
return olc::FAIL; return olc::FAIL;
} }
template <class T> template <class T>
T* TILE::Layer<T>::GetTile(int32_t x, int32_t y) T* TILE::Layer<T>::GetTile(int32_t x, int32_t y)
{ {
if (x < 0 || x >= nLayerWidth || y < 0 || y >= nLayerHeight) if (x < 0 || x >= nLayerWidth || y < 0 || y >= nLayerHeight)
return nullptr; return nullptr;
else else
return &pTiles[y*nLayerWidth + x]; return &pTiles[y*nLayerWidth + x];
} }
template<typename T> template<typename T>
void TILE::DrawLayer(olc::TILE::Layer<T> &layer, olc::TILE::Atlas &atlas, float cam_x, float cam_y, int32_t tiles_x, int32_t tiles_y, int nScale) void TILE::DrawLayer(olc::TILE::Layer<T> &layer, olc::TILE::Atlas &atlas, float cam_x, float cam_y, int32_t tiles_x, int32_t tiles_y, int nScale)
{ {
float fOffsetX = cam_x - (int)cam_x; float fOffsetX = cam_x - (int)cam_x;
float fOffsetY = cam_y - (int)cam_y; float fOffsetY = cam_y - (int)cam_y;
for (int32_t x = 0; x < tiles_x; x++) for (int32_t x = 0; x < tiles_x; x++)
{ {
for (int32_t y = 0; y < tiles_y; y++) for (int32_t y = 0; y < tiles_y; y++)
{ {
olc::TILE::BasicTile *t = layer.GetTile(x + (int)cam_x, y + (int)cam_y); olc::TILE::BasicTile *t = layer.GetTile(x + (int)cam_x, y + (int)cam_y);
if (t != nullptr && t->exist) if (t != nullptr && t->exist)
{ {
float fx = (int)(((float)x - fOffsetX) * (float)(layer.nTileWidth)); float fx = (int)(((float)x - fOffsetX) * (float)(layer.nTileWidth));
float fy = (int)(((float)y - fOffsetY) * (float)(layer.nTileHeight)); float fy = (int)(((float)y - fOffsetY) * (float)(layer.nTileHeight));
pge->DrawPartialSprite( pge->DrawPartialSprite(
fx + 0.5f - (fx < 0.0f), fx + 0.5f - (fx < 0.0f),
fy + 0.5f - (fy < 0.0f), fy + 0.5f - (fy < 0.0f),
atlas.sprTileSheet, atlas.sprTileSheet,
std::get<0>(atlas.location[t->id]), std::get<0>(atlas.location[t->id]),
std::get<1>(atlas.location[t->id]), std::get<1>(atlas.location[t->id]),
std::get<2>(atlas.location[t->id]), std::get<2>(atlas.location[t->id]),
std::get<3>(atlas.location[t->id]), std::get<3>(atlas.location[t->id]),
nScale); nScale);
} }
} }
} }
} }
template<typename T> template<typename T>
olc::Pixel TILE::GetLayerPixel(olc::TILE::Layer<T> &layer, olc::TILE::Atlas &atlas, float x, float y) olc::Pixel TILE::GetLayerPixel(olc::TILE::Layer<T> &layer, olc::TILE::Atlas &atlas, float x, float y)
{ {
olc::TILE::BasicTile *t = layer.GetTile((int32_t)x, (int32_t)y); olc::TILE::BasicTile *t = layer.GetTile((int32_t)x, (int32_t)y);
if (t != nullptr) if (t != nullptr)
{ {
float fOffsetX = x - (int)x; float fOffsetX = x - (int)x;
float fOffsetY = y - (int)y; float fOffsetY = y - (int)y;
return atlas.sprTileSheet->GetPixel(std::get<0>(atlas.location[t->id]) + fOffsetX * std::get<2>(atlas.location[t->id]), return atlas.sprTileSheet->GetPixel(std::get<0>(atlas.location[t->id]) + fOffsetX * std::get<2>(atlas.location[t->id]),
std::get<1>(atlas.location[t->id]) + fOffsetX * std::get<3>(atlas.location[t->id])); std::get<1>(atlas.location[t->id]) + fOffsetX * std::get<3>(atlas.location[t->id]));
} }
else else
return olc::BLANK; return olc::BLANK;
} }
template<typename T> template<typename T>
std::vector<olc::TILE::Edge> TILE::ExtractEdgesFromLayer(olc::TILE::Layer<T> &layer, int sx, int sy, int width, int height) std::vector<olc::TILE::Edge> TILE::ExtractEdgesFromLayer(olc::TILE::Layer<T> &layer, int sx, int sy, int width, int height)
{ {
enum enum
{ {
NORTH = 0, NORTH = 0,
EAST = 1, EAST = 1,
SOUTH = 2, SOUTH = 2,
WEST = 3 WEST = 3
}; };
std::vector<olc::TILE::Edge> vecEdges; std::vector<olc::TILE::Edge> vecEdges;
for (int x = -1; x < width + 1; x++) for (int x = -1; x < width + 1; x++)
for (int y = -1; y < height + 1; y++) for (int y = -1; y < height + 1; y++)
for (int j = 0; j < 4; j++) for (int j = 0; j < 4; j++)
{ {
if ((x + sx) >= 0 && (y + sy) >= 0 && (x + sx) < (layer.nLayerWidth - 1) && (y + sy) < (layer.nLayerHeight - 1)) if ((x + sx) >= 0 && (y + sy) >= 0 && (x + sx) < (layer.nLayerWidth - 1) && (y + sy) < (layer.nLayerHeight - 1))
{ {
layer.GetTile(x + sx, y + sy)->edge_exist[j] = false; layer.GetTile(x + sx, y + sy)->edge_exist[j] = false;
layer.GetTile(x + sx, y + sy)->edge_id[j] = 0; layer.GetTile(x + sx, y + sy)->edge_id[j] = 0;
} }
} }
// Add boundary edges // Add boundary edges
vecEdges.push_back({ (float)(sx)* layer.nTileWidth, (float)(sy)*layer.nTileHeight, (float)(sx + width)*layer.nTileWidth, (float)(sy)*layer.nTileHeight }); vecEdges.push_back({ (float)(sx)* layer.nTileWidth, (float)(sy)*layer.nTileHeight, (float)(sx + width)*layer.nTileWidth, (float)(sy)*layer.nTileHeight });
vecEdges.push_back({ (float)(sx + width)* layer.nTileWidth, (float)(sy)*layer.nTileHeight, (float)(sx + width)*layer.nTileWidth, (float)(sy + height)*layer.nTileHeight }); vecEdges.push_back({ (float)(sx + width)* layer.nTileWidth, (float)(sy)*layer.nTileHeight, (float)(sx + width)*layer.nTileWidth, (float)(sy + height)*layer.nTileHeight });
vecEdges.push_back({ (float)(sx + width)* layer.nTileWidth, (float)(sy + height)*layer.nTileHeight, (float)(sx)*layer.nTileWidth, (float)(sy + height)*layer.nTileHeight }); vecEdges.push_back({ (float)(sx + width)* layer.nTileWidth, (float)(sy + height)*layer.nTileHeight, (float)(sx)*layer.nTileWidth, (float)(sy + height)*layer.nTileHeight });
vecEdges.push_back({ (float)(sx)* layer.nTileWidth, (float)(sy + height)*layer.nTileHeight, (float)(sx)*layer.nTileWidth, (float)(sy)*layer.nTileHeight }); vecEdges.push_back({ (float)(sx)* layer.nTileWidth, (float)(sy + height)*layer.nTileHeight, (float)(sx)*layer.nTileWidth, (float)(sy)*layer.nTileHeight });
// Iterate through region from top left to bottom right // Iterate through region from top left to bottom right
for (int x = 0; x < width; x++) for (int x = 0; x < width; x++)
for (int y = 0; y < height; y++) for (int y = 0; y < height; y++)
{ {
T* i = layer.GetTile(x + sx, y + sy); //This T* i = layer.GetTile(x + sx, y + sy); //This
T* n = layer.GetTile(x + sx, y + sy - 1); T* n = layer.GetTile(x + sx, y + sy - 1);
T* s = layer.GetTile(x + sx, y + sy + 1); T* s = layer.GetTile(x + sx, y + sy + 1);
T* w = layer.GetTile(x + sx - 1, y + sy); T* w = layer.GetTile(x + sx - 1, y + sy);
T* e = layer.GetTile(x + sx + 1, y + sy); T* e = layer.GetTile(x + sx + 1, y + sy);
// If this cell exists, check if it needs edges // If this cell exists, check if it needs edges
if (i->exist) if (i->exist)
{ {
// If this cell has no western neighbour, it needs a western edge // If this cell has no western neighbour, it needs a western edge
if (w && !w->exist) if (w && !w->exist)
{ {
// It can either extend it from its northern neighbour if they have // It can either extend it from its northern neighbour if they have
// one, or It can start a new one. // one, or It can start a new one.
if (n && n->edge_exist[WEST]) if (n && n->edge_exist[WEST])
{ {
// Northern neighbour has a western edge, so grow it downwards // Northern neighbour has a western edge, so grow it downwards
vecEdges[n->edge_id[WEST]].ey += layer.nTileHeight; vecEdges[n->edge_id[WEST]].ey += layer.nTileHeight;
i->edge_id[WEST] = n->edge_id[WEST]; i->edge_id[WEST] = n->edge_id[WEST];
i->edge_exist[WEST] = true; i->edge_exist[WEST] = true;
} }
else else
{ {
// Northern neighbour does not have one, so create one // Northern neighbour does not have one, so create one
olc::TILE::Edge edge; olc::TILE::Edge edge;
edge.sx = (sx + x) * layer.nTileWidth; edge.sy = (sy + y) * layer.nTileHeight; edge.sx = (sx + x) * layer.nTileWidth; edge.sy = (sy + y) * layer.nTileHeight;
edge.ex = edge.sx; edge.ey = edge.sy + layer.nTileHeight; edge.ex = edge.sx; edge.ey = edge.sy + layer.nTileHeight;
// Add edge to Polygon Pool // Add edge to Polygon Pool
int edge_id = vecEdges.size(); int edge_id = vecEdges.size();
vecEdges.push_back(edge); vecEdges.push_back(edge);
// Update tile information with edge information // Update tile information with edge information
i->edge_id[WEST] = edge_id; i->edge_id[WEST] = edge_id;
i->edge_exist[WEST] = true; i->edge_exist[WEST] = true;
} }
} }
// If this cell dont have an eastern neignbour, It needs a eastern edge // If this cell dont have an eastern neignbour, It needs a eastern edge
if (e && !e->exist) if (e && !e->exist)
{ {
// It can either extend it from its northern neighbour if they have // It can either extend it from its northern neighbour if they have
// one, or It can start a new one. // one, or It can start a new one.
if (n && n->edge_exist[EAST]) if (n && n->edge_exist[EAST])
{ {
// Northern neighbour has one, so grow it downwards // Northern neighbour has one, so grow it downwards
vecEdges[n->edge_id[EAST]].ey += layer.nTileHeight; vecEdges[n->edge_id[EAST]].ey += layer.nTileHeight;
i->edge_id[EAST] = n->edge_id[EAST]; i->edge_id[EAST] = n->edge_id[EAST];
i->edge_exist[EAST] = true; i->edge_exist[EAST] = true;
} }
else else
{ {
// Northern neighbour does not have one, so create one // Northern neighbour does not have one, so create one
olc::TILE::Edge edge; olc::TILE::Edge edge;
edge.sx = (sx + x + 1) * layer.nTileWidth; edge.sy = (sy + y) * layer.nTileHeight; edge.sx = (sx + x + 1) * layer.nTileWidth; edge.sy = (sy + y) * layer.nTileHeight;
edge.ex = edge.sx; edge.ey = edge.sy + layer.nTileHeight; edge.ex = edge.sx; edge.ey = edge.sy + layer.nTileHeight;
// Add edge to Polygon Pool // Add edge to Polygon Pool
int edge_id = vecEdges.size(); int edge_id = vecEdges.size();
vecEdges.push_back(edge); vecEdges.push_back(edge);
// Update tile information with edge information // Update tile information with edge information
i->edge_id[EAST] = edge_id; i->edge_id[EAST] = edge_id;
i->edge_exist[EAST] = true; i->edge_exist[EAST] = true;
} }
} }
// If this cell doesnt have a northern neignbour, It needs a northern edge // If this cell doesnt have a northern neignbour, It needs a northern edge
if (n && !n->exist) if (n && !n->exist)
{ {
// It can either extend it from its western neighbour if they have // It can either extend it from its western neighbour if they have
// one, or It can start a new one. // one, or It can start a new one.
if (w && w->edge_exist[NORTH]) if (w && w->edge_exist[NORTH])
{ {
// Western neighbour has one, so grow it eastwards // Western neighbour has one, so grow it eastwards
vecEdges[w->edge_id[NORTH]].ex += layer.nTileWidth; vecEdges[w->edge_id[NORTH]].ex += layer.nTileWidth;
i->edge_id[NORTH] = w->edge_id[NORTH]; i->edge_id[NORTH] = w->edge_id[NORTH];
i->edge_exist[NORTH] = true; i->edge_exist[NORTH] = true;
} }
else else
{ {
// Western neighbour does not have one, so create one // Western neighbour does not have one, so create one
olc::TILE::Edge edge; olc::TILE::Edge edge;
edge.sx = (sx + x) * layer.nTileWidth; edge.sy = (sy + y) * layer.nTileHeight; edge.sx = (sx + x) * layer.nTileWidth; edge.sy = (sy + y) * layer.nTileHeight;
edge.ex = edge.sx + layer.nTileWidth; edge.ey = edge.sy; edge.ex = edge.sx + layer.nTileWidth; edge.ey = edge.sy;
// Add edge to Polygon Pool // Add edge to Polygon Pool
int edge_id = vecEdges.size(); int edge_id = vecEdges.size();
vecEdges.push_back(edge); vecEdges.push_back(edge);
// Update tile information with edge information // Update tile information with edge information
i->edge_id[NORTH] = edge_id; i->edge_id[NORTH] = edge_id;
i->edge_exist[NORTH] = true; i->edge_exist[NORTH] = true;
} }
} }
// If this cell doesnt have a southern neignbour, It needs a southern edge // If this cell doesnt have a southern neignbour, It needs a southern edge
if (s && !s->exist) if (s && !s->exist)
{ {
// It can either extend it from its western neighbour if they have // It can either extend it from its western neighbour if they have
// one, or It can start a new one. // one, or It can start a new one.
if (w && w->edge_exist[SOUTH]) if (w && w->edge_exist[SOUTH])
{ {
// Western neighbour has one, so grow it eastwards // Western neighbour has one, so grow it eastwards
vecEdges[w->edge_id[SOUTH]].ex += layer.nTileWidth; vecEdges[w->edge_id[SOUTH]].ex += layer.nTileWidth;
i->edge_id[SOUTH] = w->edge_id[SOUTH]; i->edge_id[SOUTH] = w->edge_id[SOUTH];
i->edge_exist[SOUTH] = true; i->edge_exist[SOUTH] = true;
} }
else else
{ {
// Western neighbour does not have one, so I need to create one // Western neighbour does not have one, so I need to create one
olc::TILE::Edge edge; olc::TILE::Edge edge;
edge.sx = (sx + x) * layer.nTileWidth; edge.sy = (sy + y + 1) * layer.nTileHeight; edge.sx = (sx + x) * layer.nTileWidth; edge.sy = (sy + y + 1) * layer.nTileHeight;
edge.ex = edge.sx + layer.nTileWidth; edge.ey = edge.sy; edge.ex = edge.sx + layer.nTileWidth; edge.ey = edge.sy;
// Add edge to Polygon Pool // Add edge to Polygon Pool
int edge_id = vecEdges.size(); int edge_id = vecEdges.size();
vecEdges.push_back(edge); vecEdges.push_back(edge);
// Update tile information with edge information // Update tile information with edge information
i->edge_id[SOUTH] = edge_id; i->edge_id[SOUTH] = edge_id;
i->edge_exist[SOUTH] = true; i->edge_exist[SOUTH] = true;
} }
} }
} }
} }
return vecEdges; return vecEdges;
} }
TILE::Atlas::Atlas() TILE::Atlas::Atlas()
{ {
} }
void TILE::Atlas::Create(olc::Sprite *tileSheet) void TILE::Atlas::Create(olc::Sprite *tileSheet)
{ {
sprTileSheet = tileSheet; sprTileSheet = tileSheet;
location.clear(); location.clear();
} }
olc::rcode TILE::Atlas::LoadFromFile(std::string filename) olc::rcode TILE::Atlas::LoadFromFile(std::string filename)
{ {
return olc::FAIL; return olc::FAIL;
} }
olc::rcode TILE::Atlas::SaveToFile(std::string filename) olc::rcode TILE::Atlas::SaveToFile(std::string filename)
{ {
return olc::FAIL; return olc::FAIL;
} }
} }

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Before

Width:  |  Height:  |  Size: 130 KiB

After

Width:  |  Height:  |  Size: 130 KiB

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

@ -0,0 +1,313 @@
/*
olcPGEX_Graphics2D.h
+-------------------------------------------------------------+
| OneLoneCoder Pixel Game Engine Extension |
| Advanced 2D Rendering - v0.4 |
+-------------------------------------------------------------+
What is this?
~~~~~~~~~~~~~
This is an extension to the olcPixelGameEngine, which provides
advanced olc::Sprite manipulation and drawing routines. To use
it, simply include this header file.
License (OLC-3)
~~~~~~~~~~~~~~~
Copyright 2018 - 2019 OneLoneCoder.com
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions or derivations of source code must retain the above
copyright notice, this list of conditions and the following disclaimer.
2. Redistributions or derivative works in binary form must reproduce
the above copyright notice. This list of conditions and the following
disclaimer must be reproduced in the documentation and/or other
materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Links
~~~~~
YouTube: https://www.youtube.com/javidx9
Discord: https://discord.gg/WhwHUMV
Twitter: https://www.twitter.com/javidx9
Twitch: https://www.twitch.tv/javidx9
GitHub: https://www.github.com/onelonecoder
Homepage: https://www.onelonecoder.com
Author
~~~~~~
David Barr, aka javidx9, ©OneLoneCoder 2019
*/
/*
Matrices stored as [Column][Row] (i.e. x, y)
|C0R0 C1R0 C2R0| | x | | x'|
|C0R1 C1R1 C2R1| * | y | = | y'|
|C0R2 C1R2 C2R2| |1.0| | - |
*/
#ifndef OLC_PGEX_GFX2D
#define OLC_PGEX_GFX2D
#include <algorithm>
#undef min
#undef max
namespace olc
{
// Container class for Advanced 2D Drawing functions
class GFX2D : public olc::PGEX
{
// A representation of an affine transform, used to rotate, scale, offset & shear space
public:
class Transform2D
{
public:
inline Transform2D();
public:
// Set this transformation to unity
inline void Reset();
// Append a rotation of fTheta radians to this transform
inline void Rotate(float fTheta);
// Append a translation (ox, oy) to this transform
inline void Translate(float ox, float oy);
// Append a scaling operation (sx, sy) to this transform
inline void Scale(float sx, float sy);
// Append a shear operation (sx, sy) to this transform
inline void Shear(float sx, float sy);
inline void Perspective(float ox, float oy);
// Calculate the Forward Transformation of the coordinate (in_x, in_y) -> (out_x, out_y)
inline void Forward(float in_x, float in_y, float &out_x, float &out_y);
// Calculate the Inverse Transformation of the coordinate (in_x, in_y) -> (out_x, out_y)
inline void Backward(float in_x, float in_y, float &out_x, float &out_y);
// Regenerate the Inverse Transformation
inline void Invert();
private:
inline void Multiply();
float matrix[4][3][3];
int nTargetMatrix;
int nSourceMatrix;
bool bDirty;
};
public:
// Draws a sprite with the transform applied
inline static void DrawSprite(olc::Sprite *sprite, olc::GFX2D::Transform2D &transform);
};
}
#ifdef OLC_PGE_GRAPHICS2D
#undef OLC_PGE_GRAPHICS2D
namespace olc
{
void GFX2D::DrawSprite(olc::Sprite *sprite, olc::GFX2D::Transform2D &transform)
{
if (sprite == nullptr)
return;
// Work out bounding rectangle of sprite
float ex, ey;
float sx, sy;
float px, py;
transform.Forward(0.0f, 0.0f, sx, sy);
px = sx; py = sy;
sx = std::min(sx, px); sy = std::min(sy, py);
ex = std::max(ex, px); ey = std::max(ey, py);
transform.Forward((float)sprite->width, (float)sprite->height, px, py);
sx = std::min(sx, px); sy = std::min(sy, py);
ex = std::max(ex, px); ey = std::max(ey, py);
transform.Forward(0.0f, (float)sprite->height, px, py);
sx = std::min(sx, px); sy = std::min(sy, py);
ex = std::max(ex, px); ey = std::max(ey, py);
transform.Forward((float)sprite->width, 0.0f, px, py);
sx = std::min(sx, px); sy = std::min(sy, py);
ex = std::max(ex, px); ey = std::max(ey, py);
// Perform inversion of transform if required
transform.Invert();
if (ex < sx)
std::swap(ex, sx);
if (ey < sy)
std::swap(ey, sy);
// Iterate through render space, and sample Sprite from suitable texel location
for (float i = sx; i < ex; i++)
{
for (float j = sy; j < ey; j++)
{
float ox, oy;
transform.Backward(i, j, ox, oy);
pge->Draw((int32_t)i, (int32_t)j, sprite->GetPixel((int32_t)(ox+0.5f), (int32_t)(oy+0.5f)));
}
}
}
olc::GFX2D::Transform2D::Transform2D()
{
Reset();
}
void olc::GFX2D::Transform2D::Reset()
{
nTargetMatrix = 0;
nSourceMatrix = 1;
bDirty = true;
// Columns Then Rows
// Matrices 0 & 1 are used as swaps in Transform accumulation
matrix[0][0][0] = 1.0f; matrix[0][1][0] = 0.0f; matrix[0][2][0] = 0.0f;
matrix[0][0][1] = 0.0f; matrix[0][1][1] = 1.0f; matrix[0][2][1] = 0.0f;
matrix[0][0][2] = 0.0f; matrix[0][1][2] = 0.0f; matrix[0][2][2] = 1.0f;
matrix[1][0][0] = 1.0f; matrix[1][1][0] = 0.0f; matrix[1][2][0] = 0.0f;
matrix[1][0][1] = 0.0f; matrix[1][1][1] = 1.0f; matrix[1][2][1] = 0.0f;
matrix[1][0][2] = 0.0f; matrix[1][1][2] = 0.0f; matrix[1][2][2] = 1.0f;
// Matrix 2 is a cache matrix to hold the immediate transform operation
// Matrix 3 is a cache matrix to hold the inverted transform
}
void olc::GFX2D::Transform2D::Multiply()
{
for (int c = 0; c < 3; c++)
{
for (int r = 0; r < 3; r++)
{
matrix[nTargetMatrix][c][r] = matrix[2][0][r] * matrix[nSourceMatrix][c][0] +
matrix[2][1][r] * matrix[nSourceMatrix][c][1] +
matrix[2][2][r] * matrix[nSourceMatrix][c][2];
}
}
std::swap(nTargetMatrix, nSourceMatrix);
bDirty = true; // Any transform multiply dirties the inversion
}
void olc::GFX2D::Transform2D::Rotate(float fTheta)
{
// Construct Rotation Matrix
matrix[2][0][0] = cosf(fTheta); matrix[2][1][0] = sinf(fTheta); matrix[2][2][0] = 0.0f;
matrix[2][0][1] = -sinf(fTheta); matrix[2][1][1] = cosf(fTheta); matrix[2][2][1] = 0.0f;
matrix[2][0][2] = 0.0f; matrix[2][1][2] = 0.0f; matrix[2][2][2] = 1.0f;
Multiply();
}
void olc::GFX2D::Transform2D::Scale(float sx, float sy)
{
// Construct Scale Matrix
matrix[2][0][0] = sx; matrix[2][1][0] = 0.0f; matrix[2][2][0] = 0.0f;
matrix[2][0][1] = 0.0f; matrix[2][1][1] = sy; matrix[2][2][1] = 0.0f;
matrix[2][0][2] = 0.0f; matrix[2][1][2] = 0.0f; matrix[2][2][2] = 1.0f;
Multiply();
}
void olc::GFX2D::Transform2D::Shear(float sx, float sy)
{
// Construct Shear Matrix
matrix[2][0][0] = 1.0f; matrix[2][1][0] = sx; matrix[2][2][0] = 0.0f;
matrix[2][0][1] = sy; matrix[2][1][1] = 1.0f; matrix[2][2][1] = 0.0f;
matrix[2][0][2] = 0.0f; matrix[2][1][2] = 0.0f; matrix[2][2][2] = 1.0f;
Multiply();
}
void olc::GFX2D::Transform2D::Translate(float ox, float oy)
{
// Construct Translate Matrix
matrix[2][0][0] = 1.0f; matrix[2][1][0] = 0.0f; matrix[2][2][0] = ox;
matrix[2][0][1] = 0.0f; matrix[2][1][1] = 1.0f; matrix[2][2][1] = oy;
matrix[2][0][2] = 0.0f; matrix[2][1][2] = 0.0f; matrix[2][2][2] = 1.0f;
Multiply();
}
void olc::GFX2D::Transform2D::Perspective(float ox, float oy)
{
// Construct Translate Matrix
matrix[2][0][0] = 1.0f; matrix[2][1][0] = 0.0f; matrix[2][2][0] = 0.0f;
matrix[2][0][1] = 0.0f; matrix[2][1][1] = 1.0f; matrix[2][2][1] = 0.0f;
matrix[2][0][2] = ox; matrix[2][1][2] = oy; matrix[2][2][2] = 1.0f;
Multiply();
}
void olc::GFX2D::Transform2D::Forward(float in_x, float in_y, float &out_x, float &out_y)
{
out_x = in_x * matrix[nSourceMatrix][0][0] + in_y * matrix[nSourceMatrix][1][0] + matrix[nSourceMatrix][2][0];
out_y = in_x * matrix[nSourceMatrix][0][1] + in_y * matrix[nSourceMatrix][1][1] + matrix[nSourceMatrix][2][1];
float out_z = in_x * matrix[nSourceMatrix][0][2] + in_y * matrix[nSourceMatrix][1][2] + matrix[nSourceMatrix][2][2];
if (out_z != 0)
{
out_x /= out_z;
out_y /= out_z;
}
}
void olc::GFX2D::Transform2D::Backward(float in_x, float in_y, float &out_x, float &out_y)
{
out_x = in_x * matrix[3][0][0] + in_y * matrix[3][1][0] + matrix[3][2][0];
out_y = in_x * matrix[3][0][1] + in_y * matrix[3][1][1] + matrix[3][2][1];
float out_z = in_x * matrix[3][0][2] + in_y * matrix[3][1][2] + matrix[3][2][2];
if (out_z != 0)
{
out_x /= out_z;
out_y /= out_z;
}
}
void olc::GFX2D::Transform2D::Invert()
{
if (bDirty) // Obviously costly so only do if needed
{
float det = matrix[nSourceMatrix][0][0] * (matrix[nSourceMatrix][1][1] * matrix[nSourceMatrix][2][2] - matrix[nSourceMatrix][1][2] * matrix[nSourceMatrix][2][1]) -
matrix[nSourceMatrix][1][0] * (matrix[nSourceMatrix][0][1] * matrix[nSourceMatrix][2][2] - matrix[nSourceMatrix][2][1] * matrix[nSourceMatrix][0][2]) +
matrix[nSourceMatrix][2][0] * (matrix[nSourceMatrix][0][1] * matrix[nSourceMatrix][1][2] - matrix[nSourceMatrix][1][1] * matrix[nSourceMatrix][0][2]);
float idet = 1.0f / det;
matrix[3][0][0] = (matrix[nSourceMatrix][1][1] * matrix[nSourceMatrix][2][2] - matrix[nSourceMatrix][1][2] * matrix[nSourceMatrix][2][1]) * idet;
matrix[3][1][0] = (matrix[nSourceMatrix][2][0] * matrix[nSourceMatrix][1][2] - matrix[nSourceMatrix][1][0] * matrix[nSourceMatrix][2][2]) * idet;
matrix[3][2][0] = (matrix[nSourceMatrix][1][0] * matrix[nSourceMatrix][2][1] - matrix[nSourceMatrix][2][0] * matrix[nSourceMatrix][1][1]) * idet;
matrix[3][0][1] = (matrix[nSourceMatrix][2][1] * matrix[nSourceMatrix][0][2] - matrix[nSourceMatrix][0][1] * matrix[nSourceMatrix][2][2]) * idet;
matrix[3][1][1] = (matrix[nSourceMatrix][0][0] * matrix[nSourceMatrix][2][2] - matrix[nSourceMatrix][2][0] * matrix[nSourceMatrix][0][2]) * idet;
matrix[3][2][1] = (matrix[nSourceMatrix][0][1] * matrix[nSourceMatrix][2][0] - matrix[nSourceMatrix][0][0] * matrix[nSourceMatrix][2][1]) * idet;
matrix[3][0][2] = (matrix[nSourceMatrix][0][1] * matrix[nSourceMatrix][1][2] - matrix[nSourceMatrix][0][2] * matrix[nSourceMatrix][1][1]) * idet;
matrix[3][1][2] = (matrix[nSourceMatrix][0][2] * matrix[nSourceMatrix][1][0] - matrix[nSourceMatrix][0][0] * matrix[nSourceMatrix][1][2]) * idet;
matrix[3][2][2] = (matrix[nSourceMatrix][0][0] * matrix[nSourceMatrix][1][1] - matrix[nSourceMatrix][0][1] * matrix[nSourceMatrix][1][0]) * idet;
bDirty = false;
}
}
}
#endif
#endif

File diff suppressed because it is too large Load Diff

@ -0,0 +1,892 @@
/*
olcPGEX_Sound.h
+-------------------------------------------------------------+
| OneLoneCoder Pixel Game Engine Extension |
| Sound - v0.3 |
+-------------------------------------------------------------+
What is this?
~~~~~~~~~~~~~
This is an extension to the olcPixelGameEngine, which provides
sound generation and wave playing routines.
Special Thanks:
~~~~~~~~~~~~~~~
Slavka - For entire non-windows system back end!
Gorbit99 - Testing, Bug Fixes
Cyberdroid - Testing, Bug Fixes
Dragoneye - Testing
Puol - Testing
License (OLC-3)
~~~~~~~~~~~~~~~
Copyright 2018 - 2019 OneLoneCoder.com
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions or derivations of source code must retain the above
copyright notice, this list of conditions and the following disclaimer.
2. Redistributions or derivative works in binary form must reproduce
the above copyright notice. This list of conditions and the following
disclaimer must be reproduced in the documentation and/or other
materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Links
~~~~~
YouTube: https://www.youtube.com/javidx9
Discord: https://discord.gg/WhwHUMV
Twitter: https://www.twitter.com/javidx9
Twitch: https://www.twitch.tv/javidx9
GitHub: https://www.github.com/onelonecoder
Homepage: https://www.onelonecoder.com
Patreon: https://www.patreon.com/javidx9
Author
~~~~~~
David Barr, aka javidx9, ©OneLoneCoder 2019
*/
#ifndef OLC_PGEX_SOUND_H
#define OLC_PGEX_SOUND_H
#include <istream>
#include <cstring>
#include <climits>
#include <algorithm>
#undef min
#undef max
// Choose a default sound backend
#if !defined(USE_ALSA) && !defined(USE_OPENAL) && !defined(USE_WINDOWS)
#ifdef __linux__
#define USE_ALSA
#endif
#ifdef __EMSCRIPTEN__
#define USE_OPENAL
#endif
#ifdef _WIN32
#define USE_WINDOWS
#endif
#endif
#ifdef USE_ALSA
#define ALSA_PCM_NEW_HW_PARAMS_API
#include <alsa/asoundlib.h>
#endif
#ifdef USE_OPENAL
#include <AL/al.h>
#include <AL/alc.h>
#include <queue>
#endif
#pragma pack(push, 1)
typedef struct {
uint16_t wFormatTag;
uint16_t nChannels;
uint32_t nSamplesPerSec;
uint32_t nAvgBytesPerSec;
uint16_t nBlockAlign;
uint16_t wBitsPerSample;
uint16_t cbSize;
} OLC_WAVEFORMATEX;
#pragma pack(pop)
namespace olc
{
// Container class for Advanced 2D Drawing functions
class SOUND : public olc::PGEX
{
// A representation of an affine transform, used to rotate, scale, offset & shear space
public:
class AudioSample
{
public:
AudioSample();
AudioSample(std::string sWavFile, olc::ResourcePack *pack = nullptr);
olc::rcode LoadFromFile(std::string sWavFile, olc::ResourcePack *pack = nullptr);
public:
OLC_WAVEFORMATEX wavHeader;
float *fSample = nullptr;
long nSamples = 0;
int nChannels = 0;
bool bSampleValid = false;
};
struct sCurrentlyPlayingSample
{
int nAudioSampleID = 0;
long nSamplePosition = 0;
bool bFinished = false;
bool bLoop = false;
bool bFlagForStop = false;
};
static std::list<sCurrentlyPlayingSample> listActiveSamples;
public:
static bool InitialiseAudio(unsigned int nSampleRate = 44100, unsigned int nChannels = 1, unsigned int nBlocks = 8, unsigned int nBlockSamples = 512);
static bool DestroyAudio();
static void SetUserSynthFunction(std::function<float(int, float, float)> func);
static void SetUserFilterFunction(std::function<float(int, float, float)> func);
public:
static int LoadAudioSample(std::string sWavFile, olc::ResourcePack *pack = nullptr);
static void PlaySample(int id, bool bLoop = false);
static void StopSample(int id);
static void StopAll();
static float GetMixerOutput(int nChannel, float fGlobalTime, float fTimeStep);
private:
#ifdef USE_WINDOWS // Windows specific sound management
static void CALLBACK waveOutProc(HWAVEOUT hWaveOut, UINT uMsg, DWORD dwParam1, DWORD dwParam2);
static unsigned int m_nSampleRate;
static unsigned int m_nChannels;
static unsigned int m_nBlockCount;
static unsigned int m_nBlockSamples;
static unsigned int m_nBlockCurrent;
static short* m_pBlockMemory;
static WAVEHDR *m_pWaveHeaders;
static HWAVEOUT m_hwDevice;
static std::atomic<unsigned int> m_nBlockFree;
static std::condition_variable m_cvBlockNotZero;
static std::mutex m_muxBlockNotZero;
#endif
#ifdef USE_ALSA
static snd_pcm_t *m_pPCM;
static unsigned int m_nSampleRate;
static unsigned int m_nChannels;
static unsigned int m_nBlockSamples;
static short* m_pBlockMemory;
#endif
#ifdef USE_OPENAL
static std::queue<ALuint> m_qAvailableBuffers;
static ALuint *m_pBuffers;
static ALuint m_nSource;
static ALCdevice *m_pDevice;
static ALCcontext *m_pContext;
static unsigned int m_nSampleRate;
static unsigned int m_nChannels;
static unsigned int m_nBlockCount;
static unsigned int m_nBlockSamples;
static short* m_pBlockMemory;
#endif
static void AudioThread();
static std::thread m_AudioThread;
static std::atomic<bool> m_bAudioThreadActive;
static std::atomic<float> m_fGlobalTime;
static std::function<float(int, float, float)> funcUserSynth;
static std::function<float(int, float, float)> funcUserFilter;
};
}
// Implementation, platform-independent
#ifdef OLC_PGEX_SOUND
#undef OLC_PGEX_SOUND
namespace olc
{
SOUND::AudioSample::AudioSample()
{ }
SOUND::AudioSample::AudioSample(std::string sWavFile, olc::ResourcePack *pack)
{
LoadFromFile(sWavFile, pack);
}
olc::rcode SOUND::AudioSample::LoadFromFile(std::string sWavFile, olc::ResourcePack *pack)
{
auto ReadWave = [&](std::istream &is)
{
char dump[4];
is.read(dump, sizeof(char) * 4); // Read "RIFF"
if (strncmp(dump, "RIFF", 4) != 0) return olc::FAIL;
is.read(dump, sizeof(char) * 4); // Not Interested
is.read(dump, sizeof(char) * 4); // Read "WAVE"
if (strncmp(dump, "WAVE", 4) != 0) return olc::FAIL;
// Read Wave description chunk
is.read(dump, sizeof(char) * 4); // Read "fmt "
unsigned int nHeaderSize = 0;
is.read((char*)&nHeaderSize, sizeof(unsigned int)); // Not Interested
is.read((char*)&wavHeader, nHeaderSize);// sizeof(WAVEFORMATEX)); // Read Wave Format Structure chunk
// Note the -2, because the structure has 2 bytes to indicate its own size
// which are not in the wav file
// Just check if wave format is compatible with olcPGE
if (wavHeader.wBitsPerSample != 16 || wavHeader.nSamplesPerSec != 44100)
return olc::FAIL;
// Search for audio data chunk
uint32_t nChunksize = 0;
is.read(dump, sizeof(char) * 4); // Read chunk header
is.read((char*)&nChunksize, sizeof(uint32_t)); // Read chunk size
while (strncmp(dump, "data", 4) != 0)
{
// Not audio data, so just skip it
//std::fseek(f, nChunksize, SEEK_CUR);
is.seekg(nChunksize, std::istream::cur);
is.read(dump, sizeof(char) * 4);
is.read((char*)&nChunksize, sizeof(uint32_t));
}
// Finally got to data, so read it all in and convert to float samples
nSamples = nChunksize / (wavHeader.nChannels * (wavHeader.wBitsPerSample >> 3));
nChannels = wavHeader.nChannels;
// Create floating point buffer to hold audio sample
fSample = new float[nSamples * nChannels];
float *pSample = fSample;
// Read in audio data and normalise
for (long i = 0; i < nSamples; i++)
{
for (int c = 0; c < nChannels; c++)
{
short s = 0;
if (!is.eof())
{
is.read((char*)&s, sizeof(short));
*pSample = (float)s / (float)(SHRT_MAX);
pSample++;
}
}
}
// All done, flag sound as valid
bSampleValid = true;
return olc::OK;
};
if (pack != nullptr)
{
olc::ResourcePack::sEntry entry = pack->GetStreamBuffer(sWavFile);
std::istream is(&entry);
return ReadWave(is);
}
else
{
// Read from file
std::ifstream ifs(sWavFile, std::ifstream::binary);
if (ifs.is_open())
{
return ReadWave(ifs);
}
else
return olc::FAIL;
}
}
// This vector holds all loaded sound samples in memory
std::vector<olc::SOUND::AudioSample> vecAudioSamples;
// This structure represents a sound that is currently playing. It only
// holds the sound ID and where this instance of it is up to for its
// current playback
void SOUND::SetUserSynthFunction(std::function<float(int, float, float)> func)
{
funcUserSynth = func;
}
void SOUND::SetUserFilterFunction(std::function<float(int, float, float)> func)
{
funcUserFilter = func;
}
// Load a 16-bit WAVE file @ 44100Hz ONLY into memory. A sample ID
// number is returned if successful, otherwise -1
int SOUND::LoadAudioSample(std::string sWavFile, olc::ResourcePack *pack)
{
olc::SOUND::AudioSample a(sWavFile, pack);
if (a.bSampleValid)
{
vecAudioSamples.push_back(a);
return (unsigned int)vecAudioSamples.size();
}
else
return -1;
}
// Add sample 'id' to the mixers sounds to play list
void SOUND::PlaySample(int id, bool bLoop)
{
olc::SOUND::sCurrentlyPlayingSample a;
a.nAudioSampleID = id;
a.nSamplePosition = 0;
a.bFinished = false;
a.bFlagForStop = false;
a.bLoop = bLoop;
SOUND::listActiveSamples.push_back(a);
}
void SOUND::StopSample(int id)
{
// Find first occurence of sample id
auto s = std::find_if(listActiveSamples.begin(), listActiveSamples.end(), [&](const olc::SOUND::sCurrentlyPlayingSample &s) { return s.nAudioSampleID == id; });
if (s != listActiveSamples.end())
s->bFlagForStop = true;
}
void SOUND::StopAll()
{
for (auto &s : listActiveSamples)
{
s.bFlagForStop = true;
}
}
float SOUND::GetMixerOutput(int nChannel, float fGlobalTime, float fTimeStep)
{
// Accumulate sample for this channel
float fMixerSample = 0.0f;
for (auto &s : listActiveSamples)
{
if (m_bAudioThreadActive)
{
if (s.bFlagForStop)
{
s.bLoop = false;
s.bFinished = true;
}
else
{
// Calculate sample position
s.nSamplePosition += roundf((float)vecAudioSamples[s.nAudioSampleID - 1].wavHeader.nSamplesPerSec * fTimeStep);
// If sample position is valid add to the mix
if (s.nSamplePosition < vecAudioSamples[s.nAudioSampleID - 1].nSamples)
fMixerSample += vecAudioSamples[s.nAudioSampleID - 1].fSample[(s.nSamplePosition * vecAudioSamples[s.nAudioSampleID - 1].nChannels) + nChannel];
else
{
if (s.bLoop)
{
s.nSamplePosition = 0;
}
else
s.bFinished = true; // Else sound has completed
}
}
}
else
return 0.0f;
}
// If sounds have completed then remove them
listActiveSamples.remove_if([](const sCurrentlyPlayingSample &s) {return s.bFinished; });
// The users application might be generating sound, so grab that if it exists
if (funcUserSynth != nullptr)
fMixerSample += funcUserSynth(nChannel, fGlobalTime, fTimeStep);
// Return the sample via an optional user override to filter the sound
if (funcUserFilter != nullptr)
return funcUserFilter(nChannel, fGlobalTime, fMixerSample);
else
return fMixerSample;
}
std::thread SOUND::m_AudioThread;
std::atomic<bool> SOUND::m_bAudioThreadActive{ false };
std::atomic<float> SOUND::m_fGlobalTime{ 0.0f };
std::list<SOUND::sCurrentlyPlayingSample> SOUND::listActiveSamples;
std::function<float(int, float, float)> SOUND::funcUserSynth = nullptr;
std::function<float(int, float, float)> SOUND::funcUserFilter = nullptr;
}
// Implementation, Windows-specific
#ifdef USE_WINDOWS
#pragma comment(lib, "winmm.lib")
namespace olc
{
bool SOUND::InitialiseAudio(unsigned int nSampleRate, unsigned int nChannels, unsigned int nBlocks, unsigned int nBlockSamples)
{
// Initialise Sound Engine
m_bAudioThreadActive = false;
m_nSampleRate = nSampleRate;
m_nChannels = nChannels;
m_nBlockCount = nBlocks;
m_nBlockSamples = nBlockSamples;
m_nBlockFree = m_nBlockCount;
m_nBlockCurrent = 0;
m_pBlockMemory = nullptr;
m_pWaveHeaders = nullptr;
// Device is available
WAVEFORMATEX waveFormat;
waveFormat.wFormatTag = WAVE_FORMAT_PCM;
waveFormat.nSamplesPerSec = m_nSampleRate;
waveFormat.wBitsPerSample = sizeof(short) * 8;
waveFormat.nChannels = m_nChannels;
waveFormat.nBlockAlign = (waveFormat.wBitsPerSample / 8) * waveFormat.nChannels;
waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;
waveFormat.cbSize = 0;
listActiveSamples.clear();
// Open Device if valid
if (waveOutOpen(&m_hwDevice, WAVE_MAPPER, &waveFormat, (DWORD_PTR)SOUND::waveOutProc, (DWORD_PTR)0, CALLBACK_FUNCTION) != S_OK)
return DestroyAudio();
// Allocate Wave|Block Memory
m_pBlockMemory = new short[m_nBlockCount * m_nBlockSamples];
if (m_pBlockMemory == nullptr)
return DestroyAudio();
ZeroMemory(m_pBlockMemory, sizeof(short) * m_nBlockCount * m_nBlockSamples);
m_pWaveHeaders = new WAVEHDR[m_nBlockCount];
if (m_pWaveHeaders == nullptr)
return DestroyAudio();
ZeroMemory(m_pWaveHeaders, sizeof(WAVEHDR) * m_nBlockCount);
// Link headers to block memory
for (unsigned int n = 0; n < m_nBlockCount; n++)
{
m_pWaveHeaders[n].dwBufferLength = m_nBlockSamples * sizeof(short);
m_pWaveHeaders[n].lpData = (LPSTR)(m_pBlockMemory + (n * m_nBlockSamples));
}
m_bAudioThreadActive = true;
m_AudioThread = std::thread(&SOUND::AudioThread);
// Start the ball rolling with the sound delivery thread
std::unique_lock<std::mutex> lm(m_muxBlockNotZero);
m_cvBlockNotZero.notify_one();
return true;
}
// Stop and clean up audio system
bool SOUND::DestroyAudio()
{
m_bAudioThreadActive = false;
m_AudioThread.join();
return false;
}
// Handler for soundcard request for more data
void CALLBACK SOUND::waveOutProc(HWAVEOUT hWaveOut, UINT uMsg, DWORD dwParam1, DWORD dwParam2)
{
if (uMsg != WOM_DONE) return;
m_nBlockFree++;
std::unique_lock<std::mutex> lm(m_muxBlockNotZero);
m_cvBlockNotZero.notify_one();
}
// Audio thread. This loop responds to requests from the soundcard to fill 'blocks'
// with audio data. If no requests are available it goes dormant until the sound
// card is ready for more data. The block is fille by the "user" in some manner
// and then issued to the soundcard.
void SOUND::AudioThread()
{
m_fGlobalTime = 0.0f;
static float fTimeStep = 1.0f / (float)m_nSampleRate;
// Goofy hack to get maximum integer for a type at run-time
short nMaxSample = (short)pow(2, (sizeof(short) * 8) - 1) - 1;
float fMaxSample = (float)nMaxSample;
short nPreviousSample = 0;
while (m_bAudioThreadActive)
{
// Wait for block to become available
if (m_nBlockFree == 0)
{
std::unique_lock<std::mutex> lm(m_muxBlockNotZero);
while (m_nBlockFree == 0) // sometimes, Windows signals incorrectly
m_cvBlockNotZero.wait(lm);
}
// Block is here, so use it
m_nBlockFree--;
// Prepare block for processing
if (m_pWaveHeaders[m_nBlockCurrent].dwFlags & WHDR_PREPARED)
waveOutUnprepareHeader(m_hwDevice, &m_pWaveHeaders[m_nBlockCurrent], sizeof(WAVEHDR));
short nNewSample = 0;
int nCurrentBlock = m_nBlockCurrent * m_nBlockSamples;
auto clip = [](float fSample, float fMax)
{
if (fSample >= 0.0)
return fmin(fSample, fMax);
else
return fmax(fSample, -fMax);
};
for (unsigned int n = 0; n < m_nBlockSamples; n += m_nChannels)
{
// User Process
for (unsigned int c = 0; c < m_nChannels; c++)
{
nNewSample = (short)(clip(GetMixerOutput(c, m_fGlobalTime, fTimeStep), 1.0) * fMaxSample);
m_pBlockMemory[nCurrentBlock + n + c] = nNewSample;
nPreviousSample = nNewSample;
}
m_fGlobalTime = m_fGlobalTime + fTimeStep;
}
// Send block to sound device
waveOutPrepareHeader(m_hwDevice, &m_pWaveHeaders[m_nBlockCurrent], sizeof(WAVEHDR));
waveOutWrite(m_hwDevice, &m_pWaveHeaders[m_nBlockCurrent], sizeof(WAVEHDR));
m_nBlockCurrent++;
m_nBlockCurrent %= m_nBlockCount;
}
}
unsigned int SOUND::m_nSampleRate = 0;
unsigned int SOUND::m_nChannels = 0;
unsigned int SOUND::m_nBlockCount = 0;
unsigned int SOUND::m_nBlockSamples = 0;
unsigned int SOUND::m_nBlockCurrent = 0;
short* SOUND::m_pBlockMemory = nullptr;
WAVEHDR *SOUND::m_pWaveHeaders = nullptr;
HWAVEOUT SOUND::m_hwDevice;
std::atomic<unsigned int> SOUND::m_nBlockFree = 0;
std::condition_variable SOUND::m_cvBlockNotZero;
std::mutex SOUND::m_muxBlockNotZero;
}
#elif defined(USE_ALSA)
namespace olc
{
bool SOUND::InitialiseAudio(unsigned int nSampleRate, unsigned int nChannels, unsigned int nBlocks, unsigned int nBlockSamples)
{
// Initialise Sound Engine
m_bAudioThreadActive = false;
m_nSampleRate = nSampleRate;
m_nChannels = nChannels;
m_nBlockSamples = nBlockSamples;
m_pBlockMemory = nullptr;
// Open PCM stream
int rc = snd_pcm_open(&m_pPCM, "default", SND_PCM_STREAM_PLAYBACK, 0);
if (rc < 0)
return DestroyAudio();
// Prepare the parameter structure and set default parameters
snd_pcm_hw_params_t *params;
snd_pcm_hw_params_alloca(&params);
snd_pcm_hw_params_any(m_pPCM, params);
// Set other parameters
snd_pcm_hw_params_set_format(m_pPCM, params, SND_PCM_FORMAT_S16_LE);
snd_pcm_hw_params_set_rate(m_pPCM, params, m_nSampleRate, 0);
snd_pcm_hw_params_set_channels(m_pPCM, params, m_nChannels);
snd_pcm_hw_params_set_period_size(m_pPCM, params, m_nBlockSamples, 0);
snd_pcm_hw_params_set_periods(m_pPCM, params, nBlocks, 0);
// Save these parameters
rc = snd_pcm_hw_params(m_pPCM, params);
if (rc < 0)
return DestroyAudio();
listActiveSamples.clear();
// Allocate Wave|Block Memory
m_pBlockMemory = new short[m_nBlockSamples];
if (m_pBlockMemory == nullptr)
return DestroyAudio();
std::fill(m_pBlockMemory, m_pBlockMemory + m_nBlockSamples, 0);
// Unsure if really needed, helped prevent underrun on my setup
snd_pcm_start(m_pPCM);
for (unsigned int i = 0; i < nBlocks; i++)
rc = snd_pcm_writei(m_pPCM, m_pBlockMemory, 512);
snd_pcm_start(m_pPCM);
m_bAudioThreadActive = true;
m_AudioThread = std::thread(&SOUND::AudioThread);
return true;
}
// Stop and clean up audio system
bool SOUND::DestroyAudio()
{
m_bAudioThreadActive = false;
m_AudioThread.join();
snd_pcm_drain(m_pPCM);
snd_pcm_close(m_pPCM);
return false;
}
// Audio thread. This loop responds to requests from the soundcard to fill 'blocks'
// with audio data. If no requests are available it goes dormant until the sound
// card is ready for more data. The block is fille by the "user" in some manner
// and then issued to the soundcard.
void SOUND::AudioThread()
{
m_fGlobalTime = 0.0f;
static float fTimeStep = 1.0f / (float)m_nSampleRate;
// Goofy hack to get maximum integer for a type at run-time
short nMaxSample = (short)pow(2, (sizeof(short) * 8) - 1) - 1;
float fMaxSample = (float)nMaxSample;
short nPreviousSample = 0;
while (m_bAudioThreadActive)
{
short nNewSample = 0;
auto clip = [](float fSample, float fMax)
{
if (fSample >= 0.0)
return fmin(fSample, fMax);
else
return fmax(fSample, -fMax);
};
for (unsigned int n = 0; n < m_nBlockSamples; n += m_nChannels)
{
// User Process
for (unsigned int c = 0; c < m_nChannels; c++)
{
nNewSample = (short)(clip(GetMixerOutput(c, m_fGlobalTime, fTimeStep), 1.0) * fMaxSample);
m_pBlockMemory[n + c] = nNewSample;
nPreviousSample = nNewSample;
}
m_fGlobalTime = m_fGlobalTime + fTimeStep;
}
// Send block to sound device
snd_pcm_uframes_t nLeft = m_nBlockSamples;
short *pBlockPos = m_pBlockMemory;
while (nLeft > 0)
{
int rc = snd_pcm_writei(m_pPCM, pBlockPos, nLeft);
if (rc > 0)
{
pBlockPos += rc * m_nChannels;
nLeft -= rc;
}
if (rc == -EAGAIN) continue;
if (rc == -EPIPE) // an underrun occured, prepare the device for more data
snd_pcm_prepare(m_pPCM);
}
}
}
snd_pcm_t* SOUND::m_pPCM = nullptr;
unsigned int SOUND::m_nSampleRate = 0;
unsigned int SOUND::m_nChannels = 0;
unsigned int SOUND::m_nBlockSamples = 0;
short* SOUND::m_pBlockMemory = nullptr;
}
#elif defined(USE_OPENAL)
namespace olc
{
bool SOUND::InitialiseAudio(unsigned int nSampleRate, unsigned int nChannels, unsigned int nBlocks, unsigned int nBlockSamples)
{
// Initialise Sound Engine
m_bAudioThreadActive = false;
m_nSampleRate = nSampleRate;
m_nChannels = nChannels;
m_nBlockCount = nBlocks;
m_nBlockSamples = nBlockSamples;
m_pBlockMemory = nullptr;
// Open the device and create the context
m_pDevice = alcOpenDevice(NULL);
if (m_pDevice)
{
m_pContext = alcCreateContext(m_pDevice, NULL);
alcMakeContextCurrent(m_pContext);
}
else
return DestroyAudio();
// Allocate memory for sound data
alGetError();
m_pBuffers = new ALuint[m_nBlockCount];
alGenBuffers(m_nBlockCount, m_pBuffers);
alGenSources(1, &m_nSource);
for (unsigned int i = 0; i < m_nBlockCount; i++)
m_qAvailableBuffers.push(m_pBuffers[i]);
listActiveSamples.clear();
// Allocate Wave|Block Memory
m_pBlockMemory = new short[m_nBlockSamples];
if (m_pBlockMemory == nullptr)
return DestroyAudio();
std::fill(m_pBlockMemory, m_pBlockMemory + m_nBlockSamples, 0);
m_bAudioThreadActive = true;
m_AudioThread = std::thread(&SOUND::AudioThread);
return true;
}
// Stop and clean up audio system
bool SOUND::DestroyAudio()
{
m_bAudioThreadActive = false;
m_AudioThread.join();
alDeleteBuffers(m_nBlockCount, m_pBuffers);
delete[] m_pBuffers;
alDeleteSources(1, &m_nSource);
alcMakeContextCurrent(NULL);
alcDestroyContext(m_pContext);
alcCloseDevice(m_pDevice);
return false;
}
// Audio thread. This loop responds to requests from the soundcard to fill 'blocks'
// with audio data. If no requests are available it goes dormant until the sound
// card is ready for more data. The block is fille by the "user" in some manner
// and then issued to the soundcard.
void SOUND::AudioThread()
{
m_fGlobalTime = 0.0f;
static float fTimeStep = 1.0f / (float)m_nSampleRate;
// Goofy hack to get maximum integer for a type at run-time
short nMaxSample = (short)pow(2, (sizeof(short) * 8) - 1) - 1;
float fMaxSample = (float)nMaxSample;
short nPreviousSample = 0;
std::vector<ALuint> vProcessed;
while (m_bAudioThreadActive)
{
ALint nState, nProcessed;
alGetSourcei(m_nSource, AL_SOURCE_STATE, &nState);
alGetSourcei(m_nSource, AL_BUFFERS_PROCESSED, &nProcessed);
// Add processed buffers to our queue
vProcessed.resize(nProcessed);
alSourceUnqueueBuffers(m_nSource, nProcessed, vProcessed.data());
for (ALint nBuf : vProcessed) m_qAvailableBuffers.push(nBuf);
// Wait until there is a free buffer (ewww)
if (m_qAvailableBuffers.empty()) continue;
short nNewSample = 0;
auto clip = [](float fSample, float fMax)
{
if (fSample >= 0.0)
return fmin(fSample, fMax);
else
return fmax(fSample, -fMax);
};
for (unsigned int n = 0; n < m_nBlockSamples; n += m_nChannels)
{
// User Process
for (unsigned int c = 0; c < m_nChannels; c++)
{
nNewSample = (short)(clip(GetMixerOutput(c, m_fGlobalTime, fTimeStep), 1.0) * fMaxSample);
m_pBlockMemory[n + c] = nNewSample;
nPreviousSample = nNewSample;
}
m_fGlobalTime = m_fGlobalTime + fTimeStep;
}
// Fill OpenAL data buffer
alBufferData(
m_qAvailableBuffers.front(),
m_nChannels == 1 ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16,
m_pBlockMemory,
2 * m_nBlockSamples,
m_nSampleRate
);
// Add it to the OpenAL queue
alSourceQueueBuffers(m_nSource, 1, &m_qAvailableBuffers.front());
// Remove it from ours
m_qAvailableBuffers.pop();
// If it's not playing for some reason, change that
if (nState != AL_PLAYING)
alSourcePlay(m_nSource);
}
}
std::queue<ALuint> SOUND::m_qAvailableBuffers;
ALuint *SOUND::m_pBuffers = nullptr;
ALuint SOUND::m_nSource = 0;
ALCdevice *SOUND::m_pDevice = nullptr;
ALCcontext *SOUND::m_pContext = nullptr;
unsigned int SOUND::m_nSampleRate = 0;
unsigned int SOUND::m_nChannels = 0;
unsigned int SOUND::m_nBlockCount = 0;
unsigned int SOUND::m_nBlockSamples = 0;
short* SOUND::m_pBlockMemory = nullptr;
}
#else // Some other platform
namespace olc
{
bool SOUND::InitialiseAudio(unsigned int nSampleRate, unsigned int nChannels, unsigned int nBlocks, unsigned int nBlockSamples)
{
return true;
}
// Stop and clean up audio system
bool SOUND::DestroyAudio()
{
return false;
}
// Audio thread. This loop responds to requests from the soundcard to fill 'blocks'
// with audio data. If no requests are available it goes dormant until the sound
// card is ready for more data. The block is fille by the "user" in some manner
// and then issued to the soundcard.
void SOUND::AudioThread()
{ }
}
#endif
#endif
#endif // OLC_PGEX_SOUND

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 971 B

After

Width:  |  Height:  |  Size: 971 B

@ -2,7 +2,7 @@
olcPixelGameEngine.h olcPixelGameEngine.h
+-------------------------------------------------------------+ +-------------------------------------------------------------+
| OneLoneCoder Pixel Game Engine v1.19 | | OneLoneCoder Pixel Game Engine v1.20 |
| "Like the command prompt console one, but not..." - javidx9 | | "Like the command prompt console one, but not..." - javidx9 |
+-------------------------------------------------------------+ +-------------------------------------------------------------+
@ -119,10 +119,12 @@
I'd like to extend thanks to Eremiell, slavka, gurkanctn, Phantim, I'd like to extend thanks to Eremiell, slavka, gurkanctn, Phantim,
JackOJC, KrossX, Huhlig, Dragoneye, Appa, JustinRichardsMusic, SliceNDice JackOJC, KrossX, Huhlig, Dragoneye, Appa, JustinRichardsMusic, SliceNDice
Ralakus, Gorbit99, raoul, joshinils, benedani & MagetzUb for advice, ideas and Ralakus, Gorbit99, raoul, joshinils, benedani & MagetzUb for advice, ideas and
testing, and I'd like to extend my appreciation to the 96K YouTube followers, testing, and I'd like to extend my appreciation to the 101K YouTube followers,
47 Patreons and 4.5K Discord server members who give me the motivation to keep 52 Patreons and 4.6K Discord server members who give me the motivation to keep
going with all this :D going with all this :D
Significant Contributors: @MaGetzUb, @slavka, @Dragoneye & @Gorbit99
Special thanks to those who bring gifts! Special thanks to those who bring gifts!
GnarGnarHead.......Domina GnarGnarHead.......Domina
Gorbit99...........Bastion, Ori & The Blind Forest Gorbit99...........Bastion, Ori & The Blind Forest
@ -176,7 +178,7 @@
#ifndef OLC_PGE_DEF #ifndef OLC_PGE_DEF
#define OLC_PGE_DEF #define OLC_PGE_DEF
#ifdef _WIN32 #if defined(_WIN32) // WINDOWS specific includes ==============================================
// Link to libraries // Link to libraries
#ifndef __MINGW32__ #ifndef __MINGW32__
#pragma comment(lib, "user32.lib") // Visual Studio Only #pragma comment(lib, "user32.lib") // Visual Studio Only
@ -202,7 +204,9 @@
#include <GL/gl.h> #include <GL/gl.h>
typedef BOOL(WINAPI wglSwapInterval_t) (int interval); typedef BOOL(WINAPI wglSwapInterval_t) (int interval);
static wglSwapInterval_t *wglSwapInterval; static wglSwapInterval_t *wglSwapInterval;
#else #endif
#ifdef __linux__ // LINUX specific includes ==============================================
#include <GL/gl.h> #include <GL/gl.h>
#include <GL/glx.h> #include <GL/glx.h>
#include <X11/X.h> #include <X11/X.h>
@ -232,6 +236,7 @@
#undef min #undef min
#undef max #undef max
#define UNUSED(x) (void)(x)
namespace olc // All OneLoneCoder stuff will now exist in the "olc" namespace namespace olc // All OneLoneCoder stuff will now exist in the "olc" namespace
{ {
@ -301,6 +306,8 @@ namespace olc // All OneLoneCoder stuff will now exist in the "olc" namespace
inline v2d_generic& operator *= (const T& rhs) { this->x *= rhs; this->y *= rhs; return *this; } inline v2d_generic& operator *= (const T& rhs) { this->x *= rhs; this->y *= rhs; return *this; }
inline v2d_generic& operator /= (const T& rhs) { this->x /= rhs; this->y /= rhs; return *this; } inline v2d_generic& operator /= (const T& rhs) { this->x /= rhs; this->y /= rhs; return *this; }
inline T& operator [] (std::size_t i) { return *((T*)this + i); /* <-- D'oh :( */ } inline T& operator [] (std::size_t i) { return *((T*)this + i); /* <-- D'oh :( */ }
inline operator v2d_generic<int>() const { return { static_cast<int32_t>(this->x), static_cast<int32_t>(this->y) }; }
inline operator v2d_generic<float>() const { return { static_cast<float>(this->x), static_cast<float>(this->y) }; }
}; };
template<class T> inline v2d_generic<T> operator * (const float& lhs, const v2d_generic<T>& rhs) { return v2d_generic<T>(lhs * rhs.x, lhs * rhs.y); } template<class T> inline v2d_generic<T> operator * (const float& lhs, const v2d_generic<T>& rhs) { return v2d_generic<T>(lhs * rhs.x, lhs * rhs.y); }
@ -332,7 +339,7 @@ namespace olc // All OneLoneCoder stuff will now exist in the "olc" namespace
ResourcePack(); ResourcePack();
~ResourcePack(); ~ResourcePack();
struct sEntry : public std::streambuf { struct sEntry : public std::streambuf {
uint32_t nID, nFileOffset, nFileSize; uint8_t* data; void _config() { this->setg((char*)data, (char*)data, (char*)(data + nFileSize)); } uint32_t nID = 0, nFileOffset = 0, nFileSize = 0; uint8_t* data = nullptr; void _config() { this->setg((char*)data, (char*)data, (char*)(data + nFileSize)); }
}; };
public: public:
@ -545,10 +552,12 @@ namespace olc // All OneLoneCoder stuff will now exist in the "olc" namespace
bool pMouseOldState[5]{ 0 }; bool pMouseOldState[5]{ 0 };
HWButton pMouseState[5]; HWButton pMouseState[5];
#ifdef _WIN32 #if defined(_WIN32)
HDC glDeviceContext = nullptr; HDC glDeviceContext = nullptr;
HGLRC glRenderContext = nullptr; HGLRC glRenderContext = nullptr;
#else #endif
#if defined(__linux__)
GLXContext glDeviceContext = nullptr; GLXContext glDeviceContext = nullptr;
GLXContext glRenderContext = nullptr; GLXContext glRenderContext = nullptr;
#endif #endif
@ -569,13 +578,15 @@ namespace olc // All OneLoneCoder stuff will now exist in the "olc" namespace
void olc_ConstructFontSheet(); void olc_ConstructFontSheet();
#ifdef _WIN32 #if defined(_WIN32)
// Windows specific window handling // Windows specific window handling
HWND olc_hWnd = nullptr; HWND olc_hWnd = nullptr;
HWND olc_WindowCreate(); HWND olc_WindowCreate();
std::wstring wsAppName; std::wstring wsAppName;
static LRESULT CALLBACK olc_WindowEvent(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); static LRESULT CALLBACK olc_WindowEvent(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
#else #endif
#if defined(__linux__)
// Non-Windows specific window handling // Non-Windows specific window handling
Display* olc_Display = nullptr; Display* olc_Display = nullptr;
Window olc_WindowRoot; Window olc_WindowRoot;
@ -654,19 +665,23 @@ namespace olc
//========================================================== //==========================================================
#if defined(_WIN32)
std::wstring ConvertS2W(std::string s) std::wstring ConvertS2W(std::string s)
{ {
#ifdef _WIN32 #ifdef __MINGW32__
wchar_t *buffer = new wchar_t[s.length() + 1];
mbstowcs(buffer, s.c_str(), s.length());
buffer[s.length()] = L'\0';
#else
int count = MultiByteToWideChar(CP_UTF8, 0, s.c_str(), -1, NULL, 0); int count = MultiByteToWideChar(CP_UTF8, 0, s.c_str(), -1, NULL, 0);
wchar_t* buffer = new wchar_t[count]; wchar_t* buffer = new wchar_t[count];
MultiByteToWideChar(CP_UTF8, 0, s.c_str(), -1, buffer, count); MultiByteToWideChar(CP_UTF8, 0, s.c_str(), -1, buffer, count);
#endif
std::wstring w(buffer); std::wstring w(buffer);
delete[] buffer; delete[] buffer;
return w; return w;
#else
return L"SVN FTW!";
#endif
} }
#endif
Sprite::Sprite() Sprite::Sprite()
{ {
@ -756,18 +771,10 @@ namespace olc
olc::rcode Sprite::LoadFromFile(std::string sImageFile, olc::ResourcePack *pack) olc::rcode Sprite::LoadFromFile(std::string sImageFile, olc::ResourcePack *pack)
{ {
#ifdef _WIN32 UNUSED(pack);
#if defined(_WIN32)
// Use GDI+ // Use GDI+
std::wstring wsImageFile; std::wstring wsImageFile = ConvertS2W(sImageFile);
#ifdef __MINGW32__
wchar_t *buffer = new wchar_t[sImageFile.length() + 1];
mbstowcs(buffer, sImageFile.c_str(), sImageFile.length());
buffer[sImageFile.length()] = L'\0';
wsImageFile = buffer;
delete [] buffer;
#else
wsImageFile = ConvertS2W(sImageFile);
#endif
Gdiplus::Bitmap *bmp = Gdiplus::Bitmap::FromFile(wsImageFile.c_str()); Gdiplus::Bitmap *bmp = Gdiplus::Bitmap::FromFile(wsImageFile.c_str());
if (bmp == nullptr) if (bmp == nullptr)
return olc::NO_FILE; return olc::NO_FILE;
@ -785,7 +792,9 @@ namespace olc
} }
delete bmp; delete bmp;
return olc::OK; return olc::OK;
#else #endif
#if defined(__linux__)
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
// Use libpng, Thanks to Guillaume Cottenceau // Use libpng, Thanks to Guillaume Cottenceau
// https://gist.github.com/niw/5963798 // https://gist.github.com/niw/5963798
@ -1094,12 +1103,8 @@ namespace olc
if (nPixelWidth == 0 || nPixelHeight == 0 || nScreenWidth == 0 || nScreenHeight == 0) if (nPixelWidth == 0 || nPixelHeight == 0 || nScreenWidth == 0 || nScreenHeight == 0)
return olc::FAIL; return olc::FAIL;
#ifdef _WIN32 #if defined(_WIN32) && defined(UNICODE) && !defined(__MINGW32__)
#ifdef UNICODE
#ifndef __MINGW32__
wsAppName = ConvertS2W(sAppName); wsAppName = ConvertS2W(sAppName);
#endif
#endif
#endif #endif
// Load the default font sheet // Load the default font sheet
olc_ConstructFontSheet(); olc_ConstructFontSheet();
@ -1119,11 +1124,15 @@ namespace olc
pDefaultDrawTarget = new Sprite(nScreenWidth, nScreenHeight); pDefaultDrawTarget = new Sprite(nScreenWidth, nScreenHeight);
SetDrawTarget(nullptr); SetDrawTarget(nullptr);
glClear(GL_COLOR_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT);
#ifdef _WIN32
#if defined(_WIN32)
SwapBuffers(glDeviceContext); SwapBuffers(glDeviceContext);
#else #endif
#if defined(__linux__)
glXSwapBuffers(olc_Display, olc_Window); glXSwapBuffers(olc_Display, olc_Window);
#endif #endif
glClear(GL_COLOR_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT);
olc_UpdateViewport(); olc_UpdateViewport();
} }
@ -1134,21 +1143,11 @@ namespace olc
if (!olc_WindowCreate()) if (!olc_WindowCreate())
return olc::FAIL; return olc::FAIL;
// Load libraries required for PNG file interaction
//#ifdef _WIN32
// // Windows use GDI+
// Gdiplus::GdiplusStartupInput startupInput;
// ULONG_PTR token;
// Gdiplus::GdiplusStartup(&token, &startupInput, NULL);
//#else
// // Linux use libpng
//
//#endif
// Start the thread // Start the thread
bAtomActive = true; bAtomActive = true;
std::thread t = std::thread(&PixelGameEngine::EngineThread, this); std::thread t = std::thread(&PixelGameEngine::EngineThread, this);
#ifdef _WIN32 #if defined(_WIN32)
// Handle Windows Message Loop // Handle Windows Message Loop
MSG msg; MSG msg;
while (GetMessage(&msg, NULL, 0, 0) > 0) while (GetMessage(&msg, NULL, 0, 0) > 0)
@ -1521,8 +1520,10 @@ namespace olc
else t2x += signx2; else t2x += signx2;
} }
next2: next2:
if (minx>t1x) minx = t1x; if (minx>t2x) minx = t2x; if (minx>t1x) minx = t1x;
if (maxx<t1x) maxx = t1x; if (maxx<t2x) maxx = t2x; if (minx>t2x) minx = t2x;
if (maxx<t1x) maxx = t1x;
if (maxx<t2x) maxx = t2x;
drawline(minx, maxx, y); // Draw line from min to max points found on the y drawline(minx, maxx, y); // Draw line from min to max points found on the y
// Now increase y // Now increase y
if (!changed1) t1x += signx1; if (!changed1) t1x += signx1;
@ -1578,8 +1579,10 @@ namespace olc
} }
next4: next4:
if (minx>t1x) minx = t1x; if (minx>t2x) minx = t2x; if (minx>t1x) minx = t1x;
if (maxx<t1x) maxx = t1x; if (maxx<t2x) maxx = t2x; if (minx>t2x) minx = t2x;
if (maxx<t1x) maxx = t1x;
if (maxx<t2x) maxx = t2x;
drawline(minx, maxx, y); drawline(minx, maxx, y);
if (!changed1) t1x += signx1; if (!changed1) t1x += signx1;
t1x += t1xp; t1x += t1xp;
@ -1701,7 +1704,7 @@ namespace olc
bool PixelGameEngine::OnUserCreate() bool PixelGameEngine::OnUserCreate()
{ return false; } { return false; }
bool PixelGameEngine::OnUserUpdate(float fElapsedTime) bool PixelGameEngine::OnUserUpdate(float fElapsedTime)
{ return false; } { UNUSED(fElapsedTime); return false; }
bool PixelGameEngine::OnUserDestroy() bool PixelGameEngine::OnUserDestroy()
{ return true; } { return true; }
////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////
@ -1743,12 +1746,9 @@ namespace olc
// Mouse coords come in screen space // Mouse coords come in screen space
// But leave in pixel space // But leave in pixel space
//if (bFullScreen) // Full Screen mode may have a weird viewport we must clamp to
{ x -= nViewX;
// Full Screen mode may have a weird viewport we must clamp to y -= nViewY;
x -= nViewX;
y -= nViewY;
}
nMousePosXcache = (int32_t)(((float)x / (float)(nWindowWidth - (nViewX * 2)) * (float)nScreenWidth)); nMousePosXcache = (int32_t)(((float)x / (float)(nWindowWidth - (nViewX * 2)) * (float)nScreenWidth));
nMousePosYcache = (int32_t)(((float)y / (float)(nWindowHeight - (nViewY * 2)) * (float)nScreenHeight)); nMousePosYcache = (int32_t)(((float)y / (float)(nWindowHeight - (nViewY * 2)) * (float)nScreenHeight));
@ -1800,7 +1800,7 @@ namespace olc
// Our time per frame coefficient // Our time per frame coefficient
float fElapsedTime = elapsedTime.count(); float fElapsedTime = elapsedTime.count();
#ifndef _WIN32 #if defined(__linux__)
// Handle Xlib Message Loop - we do this in the // Handle Xlib Message Loop - we do this in the
// same thread that OpenGL was created so we dont // same thread that OpenGL was created so we dont
// need to worry too much about multithreading with X11 // need to worry too much about multithreading with X11
@ -1958,9 +1958,11 @@ namespace olc
glEnd(); glEnd();
// Present Graphics to screen // Present Graphics to screen
#ifdef _WIN32 #if defined(_WIN32)
SwapBuffers(glDeviceContext); SwapBuffers(glDeviceContext);
#else #endif
#if defined(__linux__)
glXSwapBuffers(olc_Display, olc_Window); glXSwapBuffers(olc_Display, olc_Window);
#endif #endif
@ -1972,13 +1974,15 @@ namespace olc
fFrameTimer -= 1.0f; fFrameTimer -= 1.0f;
std::string sTitle = "OneLoneCoder.com - Pixel Game Engine - " + sAppName + " - FPS: " + std::to_string(nFrameCount); std::string sTitle = "OneLoneCoder.com - Pixel Game Engine - " + sAppName + " - FPS: " + std::to_string(nFrameCount);
#ifdef _WIN32 #if defined(_WIN32)
#ifdef UNICODE #ifdef UNICODE
SetWindowText(olc_hWnd, ConvertS2W(sTitle).c_str()); SetWindowText(olc_hWnd, ConvertS2W(sTitle).c_str());
#else #else
SetWindowText(olc_hWnd, sTitle.c_str()); SetWindowText(olc_hWnd, sTitle.c_str());
#endif #endif
#else #endif
#if defined (__linux__)
XStoreName(olc_Display, olc_Window, sTitle.c_str()); XStoreName(olc_Display, olc_Window, sTitle.c_str());
#endif #endif
nFrameCount = 0; nFrameCount = 0;
@ -1997,10 +2001,12 @@ namespace olc
} }
} }
#ifdef _WIN32 #if defined(_WIN32)
wglDeleteContext(glRenderContext); wglDeleteContext(glRenderContext);
PostMessage(olc_hWnd, WM_DESTROY, 0, 0); PostMessage(olc_hWnd, WM_DESTROY, 0, 0);
#else #endif
#if defined (__linux__)
glXMakeCurrent(olc_Display, None, NULL); glXMakeCurrent(olc_Display, None, NULL);
glXDestroyContext(olc_Display, glDeviceContext); glXDestroyContext(olc_Display, glDeviceContext);
XDestroyWindow(olc_Display, olc_Window); XDestroyWindow(olc_Display, olc_Window);
@ -2009,7 +2015,7 @@ namespace olc
} }
#ifdef _WIN32 #if defined (_WIN32)
// Thanks @MaGetzUb for this, which allows sprites to be defined // Thanks @MaGetzUb for this, which allows sprites to be defined
// at construction, by initialising the GDI subsystem // at construction, by initialising the GDI subsystem
static class GDIPlusStartup static class GDIPlusStartup
@ -2064,7 +2070,7 @@ namespace olc
} }
} }
#ifdef _WIN32 #if defined(_WIN32)
HWND PixelGameEngine::olc_WindowCreate() HWND PixelGameEngine::olc_WindowCreate()
{ {
WNDCLASS wc; WNDCLASS wc;
@ -2228,7 +2234,9 @@ namespace olc
} }
return DefWindowProc(hWnd, uMsg, wParam, lParam); return DefWindowProc(hWnd, uMsg, wParam, lParam);
} }
#else #endif
#if defined(__linux__)
// Do the Linux stuff! // Do the Linux stuff!
Display* PixelGameEngine::olc_WindowCreate() Display* PixelGameEngine::olc_WindowCreate()
{ {
@ -2325,15 +2333,16 @@ namespace olc
glSwapIntervalEXT = nullptr; glSwapIntervalEXT = nullptr;
glSwapIntervalEXT = (glSwapInterval_t*)glXGetProcAddress((unsigned char*)"glXSwapIntervalEXT"); glSwapIntervalEXT = (glSwapInterval_t*)glXGetProcAddress((unsigned char*)"glXSwapIntervalEXT");
if (glSwapIntervalEXT && !bEnableVSYNC)
glSwapIntervalEXT(olc_Display, olc_Window, 0); if (glSwapIntervalEXT == nullptr && !bEnableVSYNC)
else
{ {
printf("NOTE: Could not disable VSYNC, glXSwapIntervalEXT() was not found!\n"); printf("NOTE: Could not disable VSYNC, glXSwapIntervalEXT() was not found!\n");
printf(" Don't worry though, things will still work, it's just the\n"); printf(" Don't worry though, things will still work, it's just the\n");
printf(" frame rate will be capped to your monitors refresh rate - javidx9\n"); printf(" frame rate will be capped to your monitors refresh rate - javidx9\n");
} }
if (glSwapIntervalEXT != nullptr && !bEnableVSYNC)
glSwapIntervalEXT(olc_Display, olc_Window, 0);
return true; return true;
} }

Loading…
Cancel
Save