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. 0
      Extensions/olcPGEX_Graphics2D.h
  2. 0
      Extensions/olcPGEX_Graphics3D.h
  3. 0
      Extensions/olcPGEX_Sound.h
  4. 0
      Videos/CarCrimeCity/Part1/City_Roads1_mip0.png
  5. 0
      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. 0
      Videos/CarCrimeCity/Part1/olcPGEX_Graphics3D.h
  10. 0
      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. 0
      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. 0
      Videos/CarCrimeCity/Part2/cAutomata.cpp
  46. 0
      Videos/CarCrimeCity/Part2/cAutomata.h
  47. 0
      Videos/CarCrimeCity/Part2/cCarCrimeCity.cpp
  48. 0
      Videos/CarCrimeCity/Part2/cCarCrimeCity.h
  49. 0
      Videos/CarCrimeCity/Part2/cCell.cpp
  50. 0
      Videos/CarCrimeCity/Part2/cCell.h
  51. 0
      Videos/CarCrimeCity/Part2/cCell_Building.cpp
  52. 0
      Videos/CarCrimeCity/Part2/cCell_Building.h
  53. 0
      Videos/CarCrimeCity/Part2/cCell_Plane.cpp
  54. 0
      Videos/CarCrimeCity/Part2/cCell_Plane.h
  55. 0
      Videos/CarCrimeCity/Part2/cCell_Road.cpp
  56. 0
      Videos/CarCrimeCity/Part2/cCell_Road.h
  57. 0
      Videos/CarCrimeCity/Part2/cCell_Water.cpp
  58. 0
      Videos/CarCrimeCity/Part2/cCell_Water.h
  59. 0
      Videos/CarCrimeCity/Part2/cCityMap.cpp
  60. 0
      Videos/CarCrimeCity/Part2/cCityMap.h
  61. 0
      Videos/CarCrimeCity/Part2/cGameSettings.cpp
  62. 0
      Videos/CarCrimeCity/Part2/cGameSettings.h
  63. 0
      Videos/CarCrimeCity/Part2/lua53.dll
  64. 0
      Videos/CarCrimeCity/Part2/main.cpp
  65. 0
      Videos/CarCrimeCity/Part2/olcPGEX_Graphics3D.h
  66. 0
      Videos/CarCrimeCity/Part2/olcPixelGameEngine.cpp
  67. 0
      Videos/CarCrimeCity/Part2/olcPixelGameEngine.h
  68. 0
      Videos/DemoBinaries/OLC_8BitsImProc.zip
  69. 0
      Videos/OneLoneCoder_PGE_8BitsImProc.cpp
  70. 0
      Videos/OneLoneCoder_PGE_Balls2.cpp
  71. 0
      Videos/OneLoneCoder_PGE_ExtensionTestGFX2D.cpp
  72. 0
      Videos/OneLoneCoder_PGE_PathFinding_WaveProp.cpp
  73. 0
      Videos/OneLoneCoder_PGE_PolygonCollisions1.cpp
  74. 0
      Videos/OneLoneCoder_PGE_Polymorphism.cpp
  75. 0
      Videos/OneLoneCoder_PGE_RobotArm1.cpp
  76. 0
      Videos/OneLoneCoder_PGE_ShadowCasting2D.cpp
  77. 0
      Videos/OneLoneCoder_PGE_SoundTest.cpp
  78. 0
      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. 0
      Videos/SavingSedit/OneLoneCoder_PGE_SavingSedit.cpp
  84. 0
      Videos/SavingSedit/Zix_PGE_Controller.h
  85. 0
      Videos/SavingSedit/licence.txt
  86. 0
      Videos/SavingSedit/olcPGEX_Graphics2D.h
  87. 0
      Videos/SavingSedit/olcPGEX_Sound.h
  88. 0
      Videos/SavingSedit/olcPGEX_TileMaps_new.h
  89. 0
      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. 139
      olcPixelGameEngine.h

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

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

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

Loading…
Cancel
Save