Repo re-org #2
@ -1,9 +0,0 @@
|
||||
# User Repositories & Applications
|
||||
|
||||
Here are links to the repos of people that use olcPixelGameEngine! If you want your stuff added, send me the link and a very brief description of what it is.
|
||||
|
||||
# Games
|
||||
* CodeJam2020 - Logic Puzzle game
|
||||
* https://github.com/Jack-Punter/olcCodeJam2020_TheGreatMachine
|
||||
* CodeJam2020 - A very simple dungeon rogue-like with a twist
|
||||
* https://github.com/benkyd/OLCCodeJam2020-The-Great-Machine
|
||||
|
Before Width: | Height: | Size: 41 KiB |
@ -1,670 +0,0 @@
|
||||
/*
|
||||
BIG PROJECT - Top Down City Based Car Crime Game Part #1
|
||||
"Probably gonna regret starting this one..." - javidx9
|
||||
|
||||
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.
|
||||
|
||||
Instructions:
|
||||
~~~~~~~~~~~~~
|
||||
This is the source that accompanies part 1 of the video series which
|
||||
can be viewed via the link below. Here you can create and edit a city
|
||||
from a top down perspective ad navigate it with the car.
|
||||
|
||||
Using the mouse left click you can select cells. Using right click clears
|
||||
all the selected cells.
|
||||
|
||||
"E" - Lowers building height
|
||||
"T" - Raises building height
|
||||
"R" - Places road
|
||||
"Z, X" - Zoom
|
||||
"Up, Left, Right" - Control car
|
||||
"F5" - Save current city
|
||||
"F8" - Load existing city
|
||||
|
||||
A default city is provided for you - "example1.city", please ensure
|
||||
you have this file also.
|
||||
|
||||
Relevant Video: https://youtu.be/mD6b_hP17WI
|
||||
|
||||
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
|
||||
Patreon: https://www.patreon.com/javidx9
|
||||
Homepage: https://www.onelonecoder.com
|
||||
|
||||
Author
|
||||
~~~~~~
|
||||
David Barr, aka javidx9, ©OneLoneCoder 2018
|
||||
*/
|
||||
|
||||
#define OLC_PGE_APPLICATION
|
||||
#include "olcPixelGameEngine.h"
|
||||
#include "olcPGEX_Graphics3D.h"
|
||||
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <fstream>
|
||||
|
||||
// Override base class with your custom functionality
|
||||
class CarCrimeCity : public olc::PixelGameEngine
|
||||
{
|
||||
public:
|
||||
CarCrimeCity()
|
||||
{
|
||||
sAppName = "Car Crime City";
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
// Define the cell
|
||||
struct sCell
|
||||
{
|
||||
int nHeight = 0;
|
||||
int nWorldX = 0;
|
||||
int nWorldY = 0;
|
||||
bool bRoad = false;
|
||||
bool bBuilding = true;
|
||||
};
|
||||
|
||||
// Map variables
|
||||
int nMapWidth;
|
||||
int nMapHeight;
|
||||
sCell *pMap;
|
||||
|
||||
olc::Sprite *sprAll;
|
||||
olc::Sprite *sprGround;
|
||||
olc::Sprite *sprRoof;
|
||||
olc::Sprite *sprFrontage;
|
||||
olc::Sprite *sprWindows;
|
||||
olc::Sprite *sprRoad[12];
|
||||
olc::Sprite *sprCar;
|
||||
|
||||
float fCameraX = 0.0f;
|
||||
float fCameraY = 0.0f;
|
||||
float fCameraZ = -10.0f;
|
||||
|
||||
olc::GFX3D::mesh meshCube;
|
||||
olc::GFX3D::mesh meshFlat;
|
||||
olc::GFX3D::mesh meshWallsOut;
|
||||
|
||||
float fCarAngle = 0.0f;
|
||||
float fCarSpeed = 2.0f;
|
||||
olc::GFX3D::vec3d vecCarVel = { 0,0,0 };
|
||||
olc::GFX3D::vec3d vecCarPos = { 0,0,0 };
|
||||
|
||||
|
||||
int nMouseWorldX = 0;
|
||||
int nMouseWorldY = 0;
|
||||
int nOldMouseWorldX = 0;
|
||||
int nOldMouseWorldY = 0;
|
||||
|
||||
bool bMouseDown = false;
|
||||
std::unordered_set<sCell*> setSelectedCells;
|
||||
|
||||
olc::GFX3D::PipeLine pipeRender;
|
||||
olc::GFX3D::mat4x4 matProj;
|
||||
olc::GFX3D::vec3d vUp = { 0,1,0 };
|
||||
olc::GFX3D::vec3d vEye = { 0,0,-10 };
|
||||
olc::GFX3D::vec3d vLookDir = { 0,0,1 };
|
||||
|
||||
olc::GFX3D::vec3d viewWorldTopLeft, viewWorldBottomRight;
|
||||
|
||||
|
||||
void SaveCity(std::string sFilename)
|
||||
{
|
||||
std::ofstream file(sFilename, std::ios::out | std::ios::binary);
|
||||
file.write((char*)&nMapWidth, sizeof(int));
|
||||
file.write((char*)&nMapHeight, sizeof(int));
|
||||
for (int x = 0; x < nMapWidth; x++)
|
||||
{
|
||||
for (int y = 0; y < nMapHeight; y++)
|
||||
{
|
||||
file.write((char*)&pMap[y*nMapWidth + x], sizeof(sCell));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LoadCity(std::string sFilename)
|
||||
{
|
||||
std::ifstream file(sFilename, std::ios::in | std::ios::binary);
|
||||
file.read((char*)&nMapWidth, sizeof(int));
|
||||
file.read((char*)&nMapHeight, sizeof(int));
|
||||
delete[] pMap;
|
||||
pMap = new sCell[nMapWidth * nMapHeight];
|
||||
for (int x = 0; x < nMapWidth; x++)
|
||||
{
|
||||
for (int y = 0; y < nMapHeight; y++)
|
||||
{
|
||||
file.read((char*)&pMap[y*nMapWidth + x], sizeof(sCell));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
bool OnUserCreate() override
|
||||
{
|
||||
// Load Sprite Sheet
|
||||
sprAll = new olc::Sprite("City_Roads1_mip0.png");
|
||||
|
||||
// Here we break up the sprite sheet into individual textures. This is more
|
||||
// out of convenience than anything else, as it keeps the texture coordinates
|
||||
// easy to manipulate
|
||||
|
||||
// Building Lowest Floor
|
||||
sprFrontage = new olc::Sprite(32, 96);
|
||||
SetDrawTarget(sprFrontage);
|
||||
DrawPartialSprite(0, 0, sprAll, 288, 64, 32, 96);
|
||||
|
||||
// Building Windows
|
||||
sprWindows = new olc::Sprite(32, 96);
|
||||
SetDrawTarget(sprWindows);
|
||||
DrawPartialSprite(0, 0, sprAll, 320, 64, 32, 96);
|
||||
|
||||
// Plain Grass Field
|
||||
sprGround = new olc::Sprite(96, 96);
|
||||
SetDrawTarget(sprGround);
|
||||
DrawPartialSprite(0, 0, sprAll, 192, 0, 96, 96);
|
||||
|
||||
// Building Roof
|
||||
sprRoof = new olc::Sprite(96, 96);
|
||||
SetDrawTarget(sprRoof);
|
||||
DrawPartialSprite(0, 0, sprAll, 352, 64, 96, 96);
|
||||
|
||||
// There are 12 Road Textures, aranged in a 3x4 grid
|
||||
for (int r = 0; r < 12; r++)
|
||||
{
|
||||
sprRoad[r] = new olc::Sprite(96, 96);
|
||||
SetDrawTarget(sprRoad[r]);
|
||||
DrawPartialSprite(0, 0, sprAll, (r%3)*96, (r/3)*96, 96, 96);
|
||||
}
|
||||
|
||||
// Don't foregt to set the draw target back to being the main screen (been there... wasted 1.5 hours :| )
|
||||
SetDrawTarget(nullptr);
|
||||
|
||||
// The Yellow Car
|
||||
sprCar = new olc::Sprite("car_top.png");
|
||||
|
||||
|
||||
|
||||
// Define the city map, a 64x32 array of Cells. Initialise cells
|
||||
// to be just grass fields
|
||||
nMapWidth = 64;
|
||||
nMapHeight = 32;
|
||||
pMap = new sCell[nMapWidth * nMapHeight];
|
||||
for (int x = 0; x < nMapWidth; x++)
|
||||
{
|
||||
for (int y = 0; y < nMapHeight; y++)
|
||||
{
|
||||
pMap[y*nMapWidth + x].bRoad = false;
|
||||
pMap[y*nMapWidth + x].nHeight = 0;
|
||||
pMap[y*nMapWidth + x].nWorldX = x;
|
||||
pMap[y*nMapWidth + x].nWorldY = y;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Now we'll hand construct some meshes. These are DELIBERATELY simple and not optimised (see a later video)
|
||||
// Here the geometry is unit in size (1x1x1)
|
||||
|
||||
// A Full cube - Always useful for debugging
|
||||
meshCube.tris =
|
||||
{
|
||||
// SOUTH
|
||||
{ 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, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, },
|
||||
{ 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, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, },
|
||||
|
||||
// EAST
|
||||
{ 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, },
|
||||
{ 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, },
|
||||
|
||||
// NORTH
|
||||
{ 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.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, 0.0f, },
|
||||
{ 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, },
|
||||
|
||||
// WEST
|
||||
{ 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, },
|
||||
{ 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, },
|
||||
|
||||
// TOP
|
||||
{ 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, },
|
||||
{ 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, },
|
||||
|
||||
// BOTTOM
|
||||
{ 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, },
|
||||
{ 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, },
|
||||
|
||||
};
|
||||
|
||||
// A Flat quad
|
||||
meshFlat.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, },
|
||||
{ 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, },
|
||||
};
|
||||
|
||||
// The four outer walls of a cell
|
||||
meshWallsOut.tris =
|
||||
{
|
||||
// EAST
|
||||
{ 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.2f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, },
|
||||
{ 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.2f, 1.0f, 1.0f, 0.0f, 0.2f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, },
|
||||
|
||||
// WEST
|
||||
{ 0.0f, 0.0f, 0.2f, 1.0f, 0.0f, 1.0f, 0.2f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, },
|
||||
{ 0.0f, 0.0f, 0.2f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, },
|
||||
|
||||
// TOP
|
||||
{ 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.2f, 1.0f, 1.0f, 1.0f, 0.2f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, },
|
||||
{ 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.2f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, },
|
||||
|
||||
// BOTTOM
|
||||
{ 1.0f, 0.0f, 0.2f, 1.0f, 0.0f, 0.0f, 0.2f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, },
|
||||
{ 1.0f, 0.0f, 0.2f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, },
|
||||
};
|
||||
|
||||
|
||||
// Initialise the 3D Graphics PGE Extension. This is required
|
||||
// to setup internal buffers to the same size as the main output
|
||||
olc::GFX3D::ConfigureDisplay();
|
||||
|
||||
// Configure the rendering pipeline with projection and viewport properties
|
||||
pipeRender.SetProjection(90.0f, (float)ScreenHeight() / (float)ScreenWidth(), 0.1f, 1000.0f, 0.0f, 0.0f, ScreenWidth(), ScreenHeight());
|
||||
|
||||
// Also make a projection matrix, we might need this later
|
||||
matProj = olc::GFX3D::Math::Mat_MakeProjection(90.0f, (float)ScreenHeight() / (float)ScreenWidth(), 0.1f, 1000.0f);
|
||||
|
||||
LoadCity("example1.city");
|
||||
|
||||
// Ok, lets go!
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OnUserUpdate(float fElapsedTime) override
|
||||
{
|
||||
// Directly manipulate camera
|
||||
//if (GetKey(olc::Key::W).bHeld) fCameraY -= 2.0f * fElapsedTime;
|
||||
//if (GetKey(olc::Key::S).bHeld) fCameraY += 2.0f * fElapsedTime;
|
||||
//if (GetKey(olc::Key::A).bHeld) fCameraX -= 2.0f * fElapsedTime;
|
||||
//if (GetKey(olc::Key::D).bHeld) fCameraX += 2.0f * fElapsedTime;
|
||||
if (GetKey(olc::Key::Z).bHeld) fCameraZ += 5.0f * fElapsedTime;
|
||||
if (GetKey(olc::Key::X).bHeld) fCameraZ -= 5.0f * fElapsedTime;
|
||||
|
||||
if (GetKey(olc::Key::F5).bReleased) SaveCity("example1.city");
|
||||
if (GetKey(olc::Key::F8).bReleased) LoadCity("example1.city");
|
||||
|
||||
// === Handle User Input for Editing ==
|
||||
|
||||
// If there are no selected cells, then only edit the cell under the current mouse cursor
|
||||
// otherwise iterate through the set of sleected cells and apply to all of them
|
||||
|
||||
// Check that cell exists in valid 2D map space
|
||||
if (nMouseWorldX >= 0 && nMouseWorldX < nMapWidth && nMouseWorldY >= 0 && nMouseWorldY < nMapHeight)
|
||||
{
|
||||
// Press "R" to toggle Road flag for selected cell(s)
|
||||
if (GetKey(olc::Key::R).bPressed)
|
||||
{
|
||||
if (!setSelectedCells.empty())
|
||||
{
|
||||
for (auto &cell : setSelectedCells)
|
||||
{
|
||||
cell->bRoad = !cell->bRoad;
|
||||
}
|
||||
}
|
||||
else
|
||||
pMap[nMouseWorldY*nMapWidth + nMouseWorldX].bRoad = !pMap[nMouseWorldY*nMapWidth + nMouseWorldX].bRoad;
|
||||
}
|
||||
|
||||
// Press "T" to increase height for selected cell(s)
|
||||
if (GetKey(olc::Key::T).bPressed)
|
||||
{
|
||||
if (!setSelectedCells.empty())
|
||||
{
|
||||
for (auto &cell : setSelectedCells)
|
||||
{
|
||||
cell->nHeight++;
|
||||
}
|
||||
}
|
||||
else
|
||||
pMap[nMouseWorldY*nMapWidth + nMouseWorldX].nHeight++;
|
||||
}
|
||||
|
||||
// Press "E" to decrease height for selected cell(s)
|
||||
if (GetKey(olc::Key::E).bPressed)
|
||||
{
|
||||
if (!setSelectedCells.empty())
|
||||
{
|
||||
for (auto &cell : setSelectedCells)
|
||||
{
|
||||
cell->nHeight--;
|
||||
}
|
||||
}
|
||||
else
|
||||
pMap[nMouseWorldY*nMapWidth + nMouseWorldX].nHeight--;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// === Car User Input ===
|
||||
|
||||
if (GetKey(olc::Key::LEFT).bHeld) fCarAngle -= 4.0f * fElapsedTime;
|
||||
if (GetKey(olc::Key::RIGHT).bHeld) fCarAngle += 4.0f * fElapsedTime;
|
||||
|
||||
olc::GFX3D::vec3d a = { 1, 0, 0 };
|
||||
olc::GFX3D::mat4x4 m = olc::GFX3D::Math::Mat_MakeRotationZ(fCarAngle);
|
||||
vecCarVel = olc::GFX3D::Math::Mat_MultiplyVector(m, a);
|
||||
|
||||
if (GetKey(olc::Key::UP).bHeld)
|
||||
{
|
||||
vecCarPos.x += vecCarVel.x * fCarSpeed * fElapsedTime;
|
||||
vecCarPos.y += vecCarVel.y * fCarSpeed * fElapsedTime;
|
||||
}
|
||||
|
||||
// === Position Camera ===
|
||||
|
||||
// Our camera currently follows the car, and the car stays in the middle of
|
||||
// the screen. We need to know where the camera is before we can work with
|
||||
// on screen interactions
|
||||
fCameraY = vecCarPos.y;
|
||||
fCameraX = vecCarPos.x;
|
||||
vEye = { fCameraX,fCameraY,fCameraZ };
|
||||
olc::GFX3D::vec3d vLookTarget = olc::GFX3D::Math::Vec_Add(vEye, vLookDir);
|
||||
|
||||
// Setup the camera properties for the pipeline - aka "view" transform
|
||||
pipeRender.SetCamera(vEye, vLookTarget, vUp);
|
||||
|
||||
|
||||
// === Calculate Mouse Position on Ground Plane ===
|
||||
|
||||
// Here we take the screen coordinate of the mouse, transform it into normalised space (-1 --> +1)
|
||||
// for both axes. Instead of inverting and multiplying by the projection matrix, we only need the
|
||||
// aspect ratio parameters, with which we'll scale the mouse coordinate. This new point is then
|
||||
// multiplied by the inverse of the look at matrix (camera view matrix) aka a point at matrix, to
|
||||
// transform the new point into world space.
|
||||
//
|
||||
// Now, the thing is, a 2D point is no good on its own, our world has depth. If we treat the 2D
|
||||
// point as a ray cast from (0, 0)->(mx, my), we can see where this ray intersects with a plane
|
||||
// at a specific depth.
|
||||
|
||||
// Create a point at matrix, if you recall, this is the inverse of the look at matrix
|
||||
// used by the camera
|
||||
olc::GFX3D::mat4x4 matView = olc::GFX3D::Math::Mat_PointAt(vEye, vLookTarget, vUp);
|
||||
|
||||
// Assume the origin of the mouse ray is the middle of the screen...
|
||||
olc::GFX3D::vec3d vecMouseOrigin = { 0.0f, 0.0f, 0.0f };
|
||||
|
||||
// ...and that a ray is cast to the mouse location from the origin. Here we translate
|
||||
// the mouse coordinates into viewport coordinates
|
||||
olc::GFX3D::vec3d vecMouseDir = {
|
||||
2.0f * ((GetMouseX() / (float)ScreenWidth()) - 0.5f) / matProj.m[0][0],
|
||||
2.0f * ((GetMouseY() / (float)ScreenHeight()) - 0.5f) / matProj.m[1][1],
|
||||
1.0f,
|
||||
0.0f };
|
||||
|
||||
// Now transform the origin point and ray direction by the inverse of the camera
|
||||
vecMouseOrigin = olc::GFX3D::Math::Mat_MultiplyVector(matView, vecMouseOrigin);
|
||||
vecMouseDir = olc::GFX3D::Math::Mat_MultiplyVector(matView, vecMouseDir);
|
||||
|
||||
// Extend the mouse ray to a large length
|
||||
vecMouseDir = olc::GFX3D::Math::Vec_Mul(vecMouseDir, 1000.0f);
|
||||
|
||||
// Offset the mouse ray by the mouse origin
|
||||
vecMouseDir = olc::GFX3D::Math::Vec_Add(vecMouseOrigin, vecMouseDir);
|
||||
|
||||
// All of our intersections for mouse checks occur in the ground plane (z=0), so
|
||||
// define a plane at that location
|
||||
olc::GFX3D::vec3d plane_p = { 0.0f, 0.0f, 0.0f };
|
||||
olc::GFX3D::vec3d plane_n = { 0.0f, 0.0f, 1.0f };
|
||||
|
||||
// Calculate Mouse Location in plane, by doing a line/plane intersection test
|
||||
float t = 0.0f;
|
||||
olc::GFX3D::vec3d mouse3d = olc::GFX3D::Math::Vec_IntersectPlane(plane_p, plane_n, vecMouseOrigin, vecMouseDir, t);
|
||||
|
||||
|
||||
|
||||
// === Now we have the mouse in 3D! Handle mouse user input ===
|
||||
|
||||
// Left click & left click drag selects cells by adding them to the set of selected cells
|
||||
// Here I make sure only to do this if the cell under the mouse has changed from the
|
||||
// previous frame, but the set will also reject duplicate cells being added
|
||||
if (GetMouse(0).bHeld && ((nMouseWorldX != nOldMouseWorldX) || (nMouseWorldY != nOldMouseWorldY)))
|
||||
setSelectedCells.emplace(&pMap[nMouseWorldY * nMapWidth + nMouseWorldX]);
|
||||
|
||||
// Single clicking cells also adds them
|
||||
if (GetMouse(0).bPressed)
|
||||
setSelectedCells.emplace(&pMap[nMouseWorldY * nMapWidth + nMouseWorldX]);
|
||||
|
||||
// If the user right clicks, the set of selected cells is emptied
|
||||
if (GetMouse(1).bReleased)
|
||||
setSelectedCells.clear();
|
||||
|
||||
// Cache the current mouse position to use during comparison in next frame
|
||||
nOldMouseWorldX = nMouseWorldX;
|
||||
nOldMouseWorldY = nMouseWorldY;
|
||||
|
||||
nMouseWorldX = (int)mouse3d.x;
|
||||
nMouseWorldY = (int)mouse3d.y;
|
||||
|
||||
|
||||
|
||||
|
||||
// === Rendering ===
|
||||
|
||||
// Right, now we're gonna render the scene!
|
||||
|
||||
// First Clear the screen and the depth buffer
|
||||
Clear(olc::BLUE);
|
||||
olc::GFX3D::ClearDepth();
|
||||
|
||||
// === Calculate Visible World ===
|
||||
|
||||
// Since we now have the transforms to convert screen space into ground plane space, we
|
||||
// can calculate the visible extents of the world, regardless of zoom level! The method is
|
||||
// exactly the same for the mouse, but we use fixed screen coordinates that represent the
|
||||
// top left, and bottom right of the screen
|
||||
|
||||
// Work out Top Left Ground Cell
|
||||
vecMouseDir = { -1.0f / matProj.m[0][0],-1.0f / matProj.m[1][1], 1.0f, 0.0f };
|
||||
vecMouseDir = olc::GFX3D::Math::Mat_MultiplyVector(matView, vecMouseDir);
|
||||
vecMouseDir = olc::GFX3D::Math::Vec_Mul(vecMouseDir, 1000.0f);
|
||||
vecMouseDir = olc::GFX3D::Math::Vec_Add(vecMouseOrigin, vecMouseDir);
|
||||
viewWorldTopLeft = olc::GFX3D::Math::Vec_IntersectPlane(plane_p, plane_n, vecMouseOrigin, vecMouseDir, t);
|
||||
|
||||
// Work out Bottom Right Ground Cell
|
||||
vecMouseDir = { 1.0f / matProj.m[0][0], 1.0f / matProj.m[1][1], 1.0f, 0.0f };
|
||||
vecMouseDir = olc::GFX3D::Math::Mat_MultiplyVector(matView, vecMouseDir);
|
||||
vecMouseDir = olc::GFX3D::Math::Vec_Mul(vecMouseDir, 1000.0f);
|
||||
vecMouseDir = olc::GFX3D::Math::Vec_Add(vecMouseOrigin, vecMouseDir);
|
||||
viewWorldBottomRight = olc::GFX3D::Math::Vec_IntersectPlane(plane_p, plane_n, vecMouseOrigin, vecMouseDir, t);
|
||||
|
||||
// Calculate visible tiles
|
||||
//int nStartX = 0;
|
||||
//int nEndX = nMapWidth;
|
||||
//int nStartY = 0;
|
||||
//int nEndY = nMapHeight;
|
||||
|
||||
int nStartX = std::max(0, (int)viewWorldTopLeft.x - 1);
|
||||
int nEndX = std::min(nMapWidth, (int)viewWorldBottomRight.x + 1);
|
||||
int nStartY = std::max(0, (int)viewWorldTopLeft.y - 1);
|
||||
int nEndY = std::min(nMapHeight, (int)viewWorldBottomRight.y + 1);
|
||||
|
||||
|
||||
// Iterate through all the cells we wish to draw. Each cell is 1x1 and elevates in the Z -Axis
|
||||
for (int x = nStartX; x < nEndX; x++)
|
||||
{
|
||||
for (int y = nStartY; y < nEndY; y++)
|
||||
{
|
||||
if (pMap[y*nMapWidth + x].bRoad)
|
||||
{
|
||||
// Cell is a road, look at neighbouring cells. If they are roads also,
|
||||
// then choose the appropriate texture that joins up correctly
|
||||
|
||||
int road = 0;
|
||||
auto r = [&](int i, int j)
|
||||
{
|
||||
return pMap[(y + j) * nMapWidth + (x + i)].bRoad;
|
||||
};
|
||||
|
||||
if (r(0, -1) && r(0, +1) && !r(-1, 0) && !r(+1, 0)) road = 0;
|
||||
if (!r(0, -1) && !r(0, +1) && r(-1, 0) && r(+1, 0)) road = 1;
|
||||
|
||||
if (!r(0, -1) && r(0, +1) && !r(-1, 0) && r(+1, 0)) road = 3;
|
||||
if (!r(0, -1) && r(0, +1) && r(-1, 0) && r(+1, 0)) road = 4;
|
||||
if (!r(0, -1) && r(0, +1) && r(-1, 0) && !r(+1, 0)) road = 5;
|
||||
|
||||
if (r(0, -1) && r(0, +1) && !r(-1, 0) && r(+1, 0)) road = 6;
|
||||
if (r(0, -1) && r(0, +1) && r(-1, 0) && r(+1, 0)) road = 7;
|
||||
if (r(0, -1) && r(0, +1) && r(-1, 0) && !r(+1, 0)) road = 8;
|
||||
|
||||
if (r(0, -1) && !r(0, +1) && !r(-1, 0) && r(+1, 0)) road = 9;
|
||||
if (r(0, -1) && !r(0, +1) && r(-1, 0) && r(+1, 0)) road = 10;
|
||||
if (r(0, -1) && !r(0, +1) && r(-1, 0) && !r(+1, 0)) road = 11;
|
||||
|
||||
// Create a translation transform to position the cell in the world
|
||||
olc::GFX3D::mat4x4 matWorld = olc::GFX3D::Math::Mat_MakeTranslation(x, y, 0.0f);
|
||||
pipeRender.SetTransform(matWorld);
|
||||
|
||||
// Set the appropriate texture to use
|
||||
pipeRender.SetTexture(sprRoad[road]);
|
||||
|
||||
// Draw a flat quad
|
||||
pipeRender.Render(meshFlat.tris);
|
||||
|
||||
}
|
||||
else // Not Road
|
||||
{
|
||||
// If the cell is not considered road, then draw it appropriately
|
||||
|
||||
if (pMap[y*nMapWidth + x].nHeight < 0)
|
||||
{
|
||||
// Cell is blank - for now ;-P
|
||||
}
|
||||
|
||||
if (pMap[y*nMapWidth + x].nHeight == 0)
|
||||
{
|
||||
// Cell is ground, draw a flat grass quad at height 0
|
||||
olc::GFX3D::mat4x4 matWorld = olc::GFX3D::Math::Mat_MakeTranslation(x, y, 0.0f);
|
||||
pipeRender.SetTransform(matWorld);
|
||||
pipeRender.SetTexture(sprGround);
|
||||
pipeRender.Render(meshFlat.tris);
|
||||
}
|
||||
|
||||
if (pMap[y*nMapWidth + x].nHeight > 0)
|
||||
{
|
||||
// Cell is Building, for now, we'll draw each storey as a seperate mesh
|
||||
int h, t;
|
||||
t = pMap[y*nMapWidth + x].nHeight;
|
||||
|
||||
for (h = 0; h < t; h++)
|
||||
{
|
||||
// Create a transform that positions the storey according to its height
|
||||
olc::GFX3D::mat4x4 matWorld = olc::GFX3D::Math::Mat_MakeTranslation(x, y, -(h + 1) * 0.2f);
|
||||
pipeRender.SetTransform(matWorld);
|
||||
|
||||
// Choose a texture, if its ground level, use the "street level front", otherwise use windows
|
||||
pipeRender.SetTexture(h == 0 ? sprFrontage : sprWindows);
|
||||
pipeRender.Render(meshWallsOut.tris);
|
||||
}
|
||||
|
||||
// Top the building off with a roof
|
||||
olc::GFX3D::mat4x4 matWorld = olc::GFX3D::Math::Mat_MakeTranslation(x, y, -(h) * 0.2f);
|
||||
pipeRender.SetTransform(matWorld);
|
||||
pipeRender.SetTexture(sprRoof);
|
||||
pipeRender.Render(meshFlat.tris);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draw Selected Cells, iterate through the set of cells, and draw a wireframe quad at ground level
|
||||
// to indicate it is in the selection set
|
||||
for (auto &cell : setSelectedCells)
|
||||
{
|
||||
// Draw CursorCube
|
||||
olc::GFX3D::mat4x4 matWorld = olc::GFX3D::Math::Mat_MakeTranslation(cell->nWorldX, cell->nWorldY, 0.0f);
|
||||
pipeRender.SetTransform(matWorld);
|
||||
pipeRender.SetTexture(sprRoof);
|
||||
pipeRender.Render(meshFlat.tris, olc::GFX3D::RENDER_WIRE);
|
||||
}
|
||||
|
||||
// Draw Car, a few transforms required for this
|
||||
|
||||
// 1) Offset the car to the middle of the quad
|
||||
olc::GFX3D::mat4x4 matCarOffset = olc::GFX3D::Math::Mat_MakeTranslation(-0.5f, -0.5f, -0.0f);
|
||||
// 2) The quad is currently unit square, scale it to be more rectangular and smaller than the cells
|
||||
olc::GFX3D::mat4x4 matCarScale = olc::GFX3D::Math::Mat_MakeScale(0.4f, 0.2f, 1.0f);
|
||||
// 3) Combine into matrix
|
||||
olc::GFX3D::mat4x4 matCar = olc::GFX3D::Math::Mat_MultiplyMatrix(matCarOffset, matCarScale);
|
||||
// 4) Rotate the car around its offset origin, according to its angle
|
||||
olc::GFX3D::mat4x4 matCarRot = olc::GFX3D::Math::Mat_MakeRotationZ(fCarAngle);
|
||||
matCar = olc::GFX3D::Math::Mat_MultiplyMatrix(matCar, matCarRot);
|
||||
// 5) Translate the car into its position in the world. Give it a little elevation so its baove the ground
|
||||
olc::GFX3D::mat4x4 matCarTrans = olc::GFX3D::Math::Mat_MakeTranslation(vecCarPos.x, vecCarPos.y, -0.01f);
|
||||
matCar = olc::GFX3D::Math::Mat_MultiplyMatrix(matCar, matCarTrans);
|
||||
|
||||
// Set the car texture to the pipeline
|
||||
pipeRender.SetTexture(sprCar);
|
||||
// Apply "world" transform to pipeline
|
||||
pipeRender.SetTransform(matCar);
|
||||
|
||||
// The car has transparency, so enable it
|
||||
SetPixelMode(olc::Pixel::ALPHA);
|
||||
// Render the quad
|
||||
pipeRender.Render(meshFlat.tris);
|
||||
// Set transparency back to none to optimise drawing other pixels
|
||||
SetPixelMode(olc::Pixel::NORMAL);
|
||||
|
||||
|
||||
// Draw the current camera position for debug information
|
||||
//DrawString(10, 30, "CX: " + std::to_string(fCameraX) + " CY: " + std::to_string(fCameraY) + " CZ: " + std::to_string(fCameraZ));
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
CarCrimeCity demo;
|
||||
if (demo.Construct(768, 480, 2, 2))
|
||||
demo.Start();
|
||||
return 0;
|
||||
}
|
||||
|
Before Width: | Height: | Size: 181 KiB |
|
Before Width: | Height: | Size: 16 KiB |
@ -1,256 +0,0 @@
|
||||
/*
|
||||
** $Id: lauxlib.h,v 1.129 2015/11/23 11:29:43 roberto Exp $
|
||||
** Auxiliary functions for building Lua libraries
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
|
||||
#ifndef lauxlib_h
|
||||
#define lauxlib_h
|
||||
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "lua.h"
|
||||
|
||||
|
||||
|
||||
/* extra error code for 'luaL_load' */
|
||||
#define LUA_ERRFILE (LUA_ERRERR+1)
|
||||
|
||||
|
||||
typedef struct luaL_Reg {
|
||||
const char *name;
|
||||
lua_CFunction func;
|
||||
} luaL_Reg;
|
||||
|
||||
|
||||
#define LUAL_NUMSIZES (sizeof(lua_Integer)*16 + sizeof(lua_Number))
|
||||
|
||||
LUALIB_API void (luaL_checkversion_) (lua_State *L, lua_Number ver, size_t sz);
|
||||
#define luaL_checkversion(L) \
|
||||
luaL_checkversion_(L, LUA_VERSION_NUM, LUAL_NUMSIZES)
|
||||
|
||||
LUALIB_API int (luaL_getmetafield) (lua_State *L, int obj, const char *e);
|
||||
LUALIB_API int (luaL_callmeta) (lua_State *L, int obj, const char *e);
|
||||
LUALIB_API const char *(luaL_tolstring) (lua_State *L, int idx, size_t *len);
|
||||
LUALIB_API int (luaL_argerror) (lua_State *L, int arg, const char *extramsg);
|
||||
LUALIB_API const char *(luaL_checklstring) (lua_State *L, int arg,
|
||||
size_t *l);
|
||||
LUALIB_API const char *(luaL_optlstring) (lua_State *L, int arg,
|
||||
const char *def, size_t *l);
|
||||
LUALIB_API lua_Number (luaL_checknumber) (lua_State *L, int arg);
|
||||
LUALIB_API lua_Number (luaL_optnumber) (lua_State *L, int arg, lua_Number def);
|
||||
|
||||
LUALIB_API lua_Integer (luaL_checkinteger) (lua_State *L, int arg);
|
||||
LUALIB_API lua_Integer (luaL_optinteger) (lua_State *L, int arg,
|
||||
lua_Integer def);
|
||||
|
||||
LUALIB_API void (luaL_checkstack) (lua_State *L, int sz, const char *msg);
|
||||
LUALIB_API void (luaL_checktype) (lua_State *L, int arg, int t);
|
||||
LUALIB_API void (luaL_checkany) (lua_State *L, int arg);
|
||||
|
||||
LUALIB_API int (luaL_newmetatable) (lua_State *L, const char *tname);
|
||||
LUALIB_API void (luaL_setmetatable) (lua_State *L, const char *tname);
|
||||
LUALIB_API void *(luaL_testudata) (lua_State *L, int ud, const char *tname);
|
||||
LUALIB_API void *(luaL_checkudata) (lua_State *L, int ud, const char *tname);
|
||||
|
||||
LUALIB_API void (luaL_where) (lua_State *L, int lvl);
|
||||
LUALIB_API int (luaL_error) (lua_State *L, const char *fmt, ...);
|
||||
|
||||
LUALIB_API int (luaL_checkoption) (lua_State *L, int arg, const char *def,
|
||||
const char *const lst[]);
|
||||
|
||||
LUALIB_API int (luaL_fileresult) (lua_State *L, int stat, const char *fname);
|
||||
LUALIB_API int (luaL_execresult) (lua_State *L, int stat);
|
||||
|
||||
/* predefined references */
|
||||
#define LUA_NOREF (-2)
|
||||
#define LUA_REFNIL (-1)
|
||||
|
||||
LUALIB_API int (luaL_ref) (lua_State *L, int t);
|
||||
LUALIB_API void (luaL_unref) (lua_State *L, int t, int ref);
|
||||
|
||||
LUALIB_API int (luaL_loadfilex) (lua_State *L, const char *filename,
|
||||
const char *mode);
|
||||
|
||||
#define luaL_loadfile(L,f) luaL_loadfilex(L,f,NULL)
|
||||
|
||||
LUALIB_API int (luaL_loadbufferx) (lua_State *L, const char *buff, size_t sz,
|
||||
const char *name, const char *mode);
|
||||
LUALIB_API int (luaL_loadstring) (lua_State *L, const char *s);
|
||||
|
||||
LUALIB_API lua_State *(luaL_newstate) (void);
|
||||
|
||||
LUALIB_API lua_Integer (luaL_len) (lua_State *L, int idx);
|
||||
|
||||
LUALIB_API const char *(luaL_gsub) (lua_State *L, const char *s, const char *p,
|
||||
const char *r);
|
||||
|
||||
LUALIB_API void (luaL_setfuncs) (lua_State *L, const luaL_Reg *l, int nup);
|
||||
|
||||
LUALIB_API int (luaL_getsubtable) (lua_State *L, int idx, const char *fname);
|
||||
|
||||
LUALIB_API void (luaL_traceback) (lua_State *L, lua_State *L1,
|
||||
const char *msg, int level);
|
||||
|
||||
LUALIB_API void (luaL_requiref) (lua_State *L, const char *modname,
|
||||
lua_CFunction openf, int glb);
|
||||
|
||||
/*
|
||||
** ===============================================================
|
||||
** some useful macros
|
||||
** ===============================================================
|
||||
*/
|
||||
|
||||
|
||||
#define luaL_newlibtable(L,l) \
|
||||
lua_createtable(L, 0, sizeof(l)/sizeof((l)[0]) - 1)
|
||||
|
||||
#define luaL_newlib(L,l) \
|
||||
(luaL_checkversion(L), luaL_newlibtable(L,l), luaL_setfuncs(L,l,0))
|
||||
|
||||
#define luaL_argcheck(L, cond,arg,extramsg) \
|
||||
((void)((cond) || luaL_argerror(L, (arg), (extramsg))))
|
||||
#define luaL_checkstring(L,n) (luaL_checklstring(L, (n), NULL))
|
||||
#define luaL_optstring(L,n,d) (luaL_optlstring(L, (n), (d), NULL))
|
||||
|
||||
#define luaL_typename(L,i) lua_typename(L, lua_type(L,(i)))
|
||||
|
||||
#define luaL_dofile(L, fn) \
|
||||
(luaL_loadfile(L, fn) || lua_pcall(L, 0, LUA_MULTRET, 0))
|
||||
|
||||
#define luaL_dostring(L, s) \
|
||||
(luaL_loadstring(L, s) || lua_pcall(L, 0, LUA_MULTRET, 0))
|
||||
|
||||
#define luaL_getmetatable(L,n) (lua_getfield(L, LUA_REGISTRYINDEX, (n)))
|
||||
|
||||
#define luaL_opt(L,f,n,d) (lua_isnoneornil(L,(n)) ? (d) : f(L,(n)))
|
||||
|
||||
#define luaL_loadbuffer(L,s,sz,n) luaL_loadbufferx(L,s,sz,n,NULL)
|
||||
|
||||
|
||||
/*
|
||||
** {======================================================
|
||||
** Generic Buffer manipulation
|
||||
** =======================================================
|
||||
*/
|
||||
|
||||
typedef struct luaL_Buffer {
|
||||
char *b; /* buffer address */
|
||||
size_t size; /* buffer size */
|
||||
size_t n; /* number of characters in buffer */
|
||||
lua_State *L;
|
||||
char initb[LUAL_BUFFERSIZE]; /* initial buffer */
|
||||
} luaL_Buffer;
|
||||
|
||||
|
||||
#define luaL_addchar(B,c) \
|
||||
((void)((B)->n < (B)->size || luaL_prepbuffsize((B), 1)), \
|
||||
((B)->b[(B)->n++] = (c)))
|
||||
|
||||
#define luaL_addsize(B,s) ((B)->n += (s))
|
||||
|
||||
LUALIB_API void (luaL_buffinit) (lua_State *L, luaL_Buffer *B);
|
||||
LUALIB_API char *(luaL_prepbuffsize) (luaL_Buffer *B, size_t sz);
|
||||
LUALIB_API void (luaL_addlstring) (luaL_Buffer *B, const char *s, size_t l);
|
||||
LUALIB_API void (luaL_addstring) (luaL_Buffer *B, const char *s);
|
||||
LUALIB_API void (luaL_addvalue) (luaL_Buffer *B);
|
||||
LUALIB_API void (luaL_pushresult) (luaL_Buffer *B);
|
||||
LUALIB_API void (luaL_pushresultsize) (luaL_Buffer *B, size_t sz);
|
||||
LUALIB_API char *(luaL_buffinitsize) (lua_State *L, luaL_Buffer *B, size_t sz);
|
||||
|
||||
#define luaL_prepbuffer(B) luaL_prepbuffsize(B, LUAL_BUFFERSIZE)
|
||||
|
||||
/* }====================================================== */
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** {======================================================
|
||||
** File handles for IO library
|
||||
** =======================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
** A file handle is a userdata with metatable 'LUA_FILEHANDLE' and
|
||||
** initial structure 'luaL_Stream' (it may contain other fields
|
||||
** after that initial structure).
|
||||
*/
|
||||
|
||||
#define LUA_FILEHANDLE "FILE*"
|
||||
|
||||
|
||||
typedef struct luaL_Stream {
|
||||
FILE *f; /* stream (NULL for incompletely created streams) */
|
||||
lua_CFunction closef; /* to close stream (NULL for closed streams) */
|
||||
} luaL_Stream;
|
||||
|
||||
/* }====================================================== */
|
||||
|
||||
|
||||
|
||||
/* compatibility with old module system */
|
||||
#if defined(LUA_COMPAT_MODULE)
|
||||
|
||||
LUALIB_API void (luaL_pushmodule) (lua_State *L, const char *modname,
|
||||
int sizehint);
|
||||
LUALIB_API void (luaL_openlib) (lua_State *L, const char *libname,
|
||||
const luaL_Reg *l, int nup);
|
||||
|
||||
#define luaL_register(L,n,l) (luaL_openlib(L,(n),(l),0))
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** {==================================================================
|
||||
** "Abstraction Layer" for basic report of messages and errors
|
||||
** ===================================================================
|
||||
*/
|
||||
|
||||
/* print a string */
|
||||
#if !defined(lua_writestring)
|
||||
#define lua_writestring(s,l) fwrite((s), sizeof(char), (l), stdout)
|
||||
#endif
|
||||
|
||||
/* print a newline and flush the output */
|
||||
#if !defined(lua_writeline)
|
||||
#define lua_writeline() (lua_writestring("\n", 1), fflush(stdout))
|
||||
#endif
|
||||
|
||||
/* print an error message */
|
||||
#if !defined(lua_writestringerror)
|
||||
#define lua_writestringerror(s,p) \
|
||||
(fprintf(stderr, (s), (p)), fflush(stderr))
|
||||
#endif
|
||||
|
||||
/* }================================================================== */
|
||||
|
||||
|
||||
/*
|
||||
** {============================================================
|
||||
** Compatibility with deprecated conversions
|
||||
** =============================================================
|
||||
*/
|
||||
#if defined(LUA_COMPAT_APIINTCASTS)
|
||||
|
||||
#define luaL_checkunsigned(L,a) ((lua_Unsigned)luaL_checkinteger(L,a))
|
||||
#define luaL_optunsigned(L,a,d) \
|
||||
((lua_Unsigned)luaL_optinteger(L,a,(lua_Integer)(d)))
|
||||
|
||||
#define luaL_checkint(L,n) ((int)luaL_checkinteger(L, (n)))
|
||||
#define luaL_optint(L,n,d) ((int)luaL_optinteger(L, (n), (d)))
|
||||
|
||||
#define luaL_checklong(L,n) ((long)luaL_checkinteger(L, (n)))
|
||||
#define luaL_optlong(L,n,d) ((long)luaL_optinteger(L, (n), (d)))
|
||||
|
||||
#endif
|
||||
/* }============================================================ */
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@ -1,486 +0,0 @@
|
||||
/*
|
||||
** $Id: lua.h,v 1.331 2016/05/30 15:53:28 roberto Exp $
|
||||
** Lua - A Scripting Language
|
||||
** Lua.org, PUC-Rio, Brazil (http://www.lua.org)
|
||||
** See Copyright Notice at the end of this file
|
||||
*/
|
||||
|
||||
|
||||
#ifndef lua_h
|
||||
#define lua_h
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
|
||||
|
||||
#include "luaconf.h"
|
||||
|
||||
|
||||
#define LUA_VERSION_MAJOR "5"
|
||||
#define LUA_VERSION_MINOR "3"
|
||||
#define LUA_VERSION_NUM 503
|
||||
#define LUA_VERSION_RELEASE "3"
|
||||
|
||||
#define LUA_VERSION "Lua " LUA_VERSION_MAJOR "." LUA_VERSION_MINOR
|
||||
#define LUA_RELEASE LUA_VERSION "." LUA_VERSION_RELEASE
|
||||
#define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2016 Lua.org, PUC-Rio"
|
||||
#define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo, W. Celes"
|
||||
|
||||
|
||||
/* mark for precompiled code ('<esc>Lua') */
|
||||
#define LUA_SIGNATURE "\x1bLua"
|
||||
|
||||
/* option for multiple returns in 'lua_pcall' and 'lua_call' */
|
||||
#define LUA_MULTRET (-1)
|
||||
|
||||
|
||||
/*
|
||||
** Pseudo-indices
|
||||
** (-LUAI_MAXSTACK is the minimum valid index; we keep some free empty
|
||||
** space after that to help overflow detection)
|
||||
*/
|
||||
#define LUA_REGISTRYINDEX (-LUAI_MAXSTACK - 1000)
|
||||
#define lua_upvalueindex(i) (LUA_REGISTRYINDEX - (i))
|
||||
|
||||
|
||||
/* thread status */
|
||||
#define LUA_OK 0
|
||||
#define LUA_YIELD 1
|
||||
#define LUA_ERRRUN 2
|
||||
#define LUA_ERRSYNTAX 3
|
||||
#define LUA_ERRMEM 4
|
||||
#define LUA_ERRGCMM 5
|
||||
#define LUA_ERRERR 6
|
||||
|
||||
|
||||
typedef struct lua_State lua_State;
|
||||
|
||||
|
||||
/*
|
||||
** basic types
|
||||
*/
|
||||
#define LUA_TNONE (-1)
|
||||
|
||||
#define LUA_TNIL 0
|
||||
#define LUA_TBOOLEAN 1
|
||||
#define LUA_TLIGHTUSERDATA 2
|
||||
#define LUA_TNUMBER 3
|
||||
#define LUA_TSTRING 4
|
||||
#define LUA_TTABLE 5
|
||||
#define LUA_TFUNCTION 6
|
||||
#define LUA_TUSERDATA 7
|
||||
#define LUA_TTHREAD 8
|
||||
|
||||
#define LUA_NUMTAGS 9
|
||||
|
||||
|
||||
|
||||
/* minimum Lua stack available to a C function */
|
||||
#define LUA_MINSTACK 20
|
||||
|
||||
|
||||
/* predefined values in the registry */
|
||||
#define LUA_RIDX_MAINTHREAD 1
|
||||
#define LUA_RIDX_GLOBALS 2
|
||||
#define LUA_RIDX_LAST LUA_RIDX_GLOBALS
|
||||
|
||||
|
||||
/* type of numbers in Lua */
|
||||
typedef LUA_NUMBER lua_Number;
|
||||
|
||||
|
||||
/* type for integer functions */
|
||||
typedef LUA_INTEGER lua_Integer;
|
||||
|
||||
/* unsigned integer type */
|
||||
typedef LUA_UNSIGNED lua_Unsigned;
|
||||
|
||||
/* type for continuation-function contexts */
|
||||
typedef LUA_KCONTEXT lua_KContext;
|
||||
|
||||
|
||||
/*
|
||||
** Type for C functions registered with Lua
|
||||
*/
|
||||
typedef int (*lua_CFunction) (lua_State *L);
|
||||
|
||||
/*
|
||||
** Type for continuation functions
|
||||
*/
|
||||
typedef int (*lua_KFunction) (lua_State *L, int status, lua_KContext ctx);
|
||||
|
||||
|
||||
/*
|
||||
** Type for functions that read/write blocks when loading/dumping Lua chunks
|
||||
*/
|
||||
typedef const char * (*lua_Reader) (lua_State *L, void *ud, size_t *sz);
|
||||
|
||||
typedef int (*lua_Writer) (lua_State *L, const void *p, size_t sz, void *ud);
|
||||
|
||||
|
||||
/*
|
||||
** Type for memory-allocation functions
|
||||
*/
|
||||
typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize);
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** generic extra include file
|
||||
*/
|
||||
#if defined(LUA_USER_H)
|
||||
#include LUA_USER_H
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** RCS ident string
|
||||
*/
|
||||
extern const char lua_ident[];
|
||||
|
||||
|
||||
/*
|
||||
** state manipulation
|
||||
*/
|
||||
LUA_API lua_State *(lua_newstate) (lua_Alloc f, void *ud);
|
||||
LUA_API void (lua_close) (lua_State *L);
|
||||
LUA_API lua_State *(lua_newthread) (lua_State *L);
|
||||
|
||||
LUA_API lua_CFunction (lua_atpanic) (lua_State *L, lua_CFunction panicf);
|
||||
|
||||
|
||||
LUA_API const lua_Number *(lua_version) (lua_State *L);
|
||||
|
||||
|
||||
/*
|
||||
** basic stack manipulation
|
||||
*/
|
||||
LUA_API int (lua_absindex) (lua_State *L, int idx);
|
||||
LUA_API int (lua_gettop) (lua_State *L);
|
||||
LUA_API void (lua_settop) (lua_State *L, int idx);
|
||||
LUA_API void (lua_pushvalue) (lua_State *L, int idx);
|
||||
LUA_API void (lua_rotate) (lua_State *L, int idx, int n);
|
||||
LUA_API void (lua_copy) (lua_State *L, int fromidx, int toidx);
|
||||
LUA_API int (lua_checkstack) (lua_State *L, int n);
|
||||
|
||||
LUA_API void (lua_xmove) (lua_State *from, lua_State *to, int n);
|
||||
|
||||
|
||||
/*
|
||||
** access functions (stack -> C)
|
||||
*/
|
||||
|
||||
LUA_API int (lua_isnumber) (lua_State *L, int idx);
|
||||
LUA_API int (lua_isstring) (lua_State *L, int idx);
|
||||
LUA_API int (lua_iscfunction) (lua_State *L, int idx);
|
||||
LUA_API int (lua_isinteger) (lua_State *L, int idx);
|
||||
LUA_API int (lua_isuserdata) (lua_State *L, int idx);
|
||||
LUA_API int (lua_type) (lua_State *L, int idx);
|
||||
LUA_API const char *(lua_typename) (lua_State *L, int tp);
|
||||
|
||||
LUA_API lua_Number (lua_tonumberx) (lua_State *L, int idx, int *isnum);
|
||||
LUA_API lua_Integer (lua_tointegerx) (lua_State *L, int idx, int *isnum);
|
||||
LUA_API int (lua_toboolean) (lua_State *L, int idx);
|
||||
LUA_API const char *(lua_tolstring) (lua_State *L, int idx, size_t *len);
|
||||
LUA_API size_t (lua_rawlen) (lua_State *L, int idx);
|
||||
LUA_API lua_CFunction (lua_tocfunction) (lua_State *L, int idx);
|
||||
LUA_API void *(lua_touserdata) (lua_State *L, int idx);
|
||||
LUA_API lua_State *(lua_tothread) (lua_State *L, int idx);
|
||||
LUA_API const void *(lua_topointer) (lua_State *L, int idx);
|
||||
|
||||
|
||||
/*
|
||||
** Comparison and arithmetic functions
|
||||
*/
|
||||
|
||||
#define LUA_OPADD 0 /* ORDER TM, ORDER OP */
|
||||
#define LUA_OPSUB 1
|
||||
#define LUA_OPMUL 2
|
||||
#define LUA_OPMOD 3
|
||||
#define LUA_OPPOW 4
|
||||
#define LUA_OPDIV 5
|
||||
#define LUA_OPIDIV 6
|
||||
#define LUA_OPBAND 7
|
||||
#define LUA_OPBOR 8
|
||||
#define LUA_OPBXOR 9
|
||||
#define LUA_OPSHL 10
|
||||
#define LUA_OPSHR 11
|
||||
#define LUA_OPUNM 12
|
||||
#define LUA_OPBNOT 13
|
||||
|
||||
LUA_API void (lua_arith) (lua_State *L, int op);
|
||||
|
||||
#define LUA_OPEQ 0
|
||||
#define LUA_OPLT 1
|
||||
#define LUA_OPLE 2
|
||||
|
||||
LUA_API int (lua_rawequal) (lua_State *L, int idx1, int idx2);
|
||||
LUA_API int (lua_compare) (lua_State *L, int idx1, int idx2, int op);
|
||||
|
||||
|
||||
/*
|
||||
** push functions (C -> stack)
|
||||
*/
|
||||
LUA_API void (lua_pushnil) (lua_State *L);
|
||||
LUA_API void (lua_pushnumber) (lua_State *L, lua_Number n);
|
||||
LUA_API void (lua_pushinteger) (lua_State *L, lua_Integer n);
|
||||
LUA_API const char *(lua_pushlstring) (lua_State *L, const char *s, size_t len);
|
||||
LUA_API const char *(lua_pushstring) (lua_State *L, const char *s);
|
||||
LUA_API const char *(lua_pushvfstring) (lua_State *L, const char *fmt,
|
||||
va_list argp);
|
||||
LUA_API const char *(lua_pushfstring) (lua_State *L, const char *fmt, ...);
|
||||
LUA_API void (lua_pushcclosure) (lua_State *L, lua_CFunction fn, int n);
|
||||
LUA_API void (lua_pushboolean) (lua_State *L, int b);
|
||||
LUA_API void (lua_pushlightuserdata) (lua_State *L, void *p);
|
||||
LUA_API int (lua_pushthread) (lua_State *L);
|
||||
|
||||
|
||||
/*
|
||||
** get functions (Lua -> stack)
|
||||
*/
|
||||
LUA_API int (lua_getglobal) (lua_State *L, const char *name);
|
||||
LUA_API int (lua_gettable) (lua_State *L, int idx);
|
||||
LUA_API int (lua_getfield) (lua_State *L, int idx, const char *k);
|
||||
LUA_API int (lua_geti) (lua_State *L, int idx, lua_Integer n);
|
||||
LUA_API int (lua_rawget) (lua_State *L, int idx);
|
||||
LUA_API int (lua_rawgeti) (lua_State *L, int idx, lua_Integer n);
|
||||
LUA_API int (lua_rawgetp) (lua_State *L, int idx, const void *p);
|
||||
|
||||
LUA_API void (lua_createtable) (lua_State *L, int narr, int nrec);
|
||||
LUA_API void *(lua_newuserdata) (lua_State *L, size_t sz);
|
||||
LUA_API int (lua_getmetatable) (lua_State *L, int objindex);
|
||||
LUA_API int (lua_getuservalue) (lua_State *L, int idx);
|
||||
|
||||
|
||||
/*
|
||||
** set functions (stack -> Lua)
|
||||
*/
|
||||
LUA_API void (lua_setglobal) (lua_State *L, const char *name);
|
||||
LUA_API void (lua_settable) (lua_State *L, int idx);
|
||||
LUA_API void (lua_setfield) (lua_State *L, int idx, const char *k);
|
||||
LUA_API void (lua_seti) (lua_State *L, int idx, lua_Integer n);
|
||||
LUA_API void (lua_rawset) (lua_State *L, int idx);
|
||||
LUA_API void (lua_rawseti) (lua_State *L, int idx, lua_Integer n);
|
||||
LUA_API void (lua_rawsetp) (lua_State *L, int idx, const void *p);
|
||||
LUA_API int (lua_setmetatable) (lua_State *L, int objindex);
|
||||
LUA_API void (lua_setuservalue) (lua_State *L, int idx);
|
||||
|
||||
|
||||
/*
|
||||
** 'load' and 'call' functions (load and run Lua code)
|
||||
*/
|
||||
LUA_API void (lua_callk) (lua_State *L, int nargs, int nresults,
|
||||
lua_KContext ctx, lua_KFunction k);
|
||||
#define lua_call(L,n,r) lua_callk(L, (n), (r), 0, NULL)
|
||||
|
||||
LUA_API int (lua_pcallk) (lua_State *L, int nargs, int nresults, int errfunc,
|
||||
lua_KContext ctx, lua_KFunction k);
|
||||
#define lua_pcall(L,n,r,f) lua_pcallk(L, (n), (r), (f), 0, NULL)
|
||||
|
||||
LUA_API int (lua_load) (lua_State *L, lua_Reader reader, void *dt,
|
||||
const char *chunkname, const char *mode);
|
||||
|
||||
LUA_API int (lua_dump) (lua_State *L, lua_Writer writer, void *data, int strip);
|
||||
|
||||
|
||||
/*
|
||||
** coroutine functions
|
||||
*/
|
||||
LUA_API int (lua_yieldk) (lua_State *L, int nresults, lua_KContext ctx,
|
||||
lua_KFunction k);
|
||||
LUA_API int (lua_resume) (lua_State *L, lua_State *from, int narg);
|
||||
LUA_API int (lua_status) (lua_State *L);
|
||||
LUA_API int (lua_isyieldable) (lua_State *L);
|
||||
|
||||
#define lua_yield(L,n) lua_yieldk(L, (n), 0, NULL)
|
||||
|
||||
|
||||
/*
|
||||
** garbage-collection function and options
|
||||
*/
|
||||
|
||||
#define LUA_GCSTOP 0
|
||||
#define LUA_GCRESTART 1
|
||||
#define LUA_GCCOLLECT 2
|
||||
#define LUA_GCCOUNT 3
|
||||
#define LUA_GCCOUNTB 4
|
||||
#define LUA_GCSTEP 5
|
||||
#define LUA_GCSETPAUSE 6
|
||||
#define LUA_GCSETSTEPMUL 7
|
||||
#define LUA_GCISRUNNING 9
|
||||
|
||||
LUA_API int (lua_gc) (lua_State *L, int what, int data);
|
||||
|
||||
|
||||
/*
|
||||
** miscellaneous functions
|
||||
*/
|
||||
|
||||
LUA_API int (lua_error) (lua_State *L);
|
||||
|
||||
LUA_API int (lua_next) (lua_State *L, int idx);
|
||||
|
||||
LUA_API void (lua_concat) (lua_State *L, int n);
|
||||
LUA_API void (lua_len) (lua_State *L, int idx);
|
||||
|
||||
LUA_API size_t (lua_stringtonumber) (lua_State *L, const char *s);
|
||||
|
||||
LUA_API lua_Alloc (lua_getallocf) (lua_State *L, void **ud);
|
||||
LUA_API void (lua_setallocf) (lua_State *L, lua_Alloc f, void *ud);
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** {==============================================================
|
||||
** some useful macros
|
||||
** ===============================================================
|
||||
*/
|
||||
|
||||
#define lua_getextraspace(L) ((void *)((char *)(L) - LUA_EXTRASPACE))
|
||||
|
||||
#define lua_tonumber(L,i) lua_tonumberx(L,(i),NULL)
|
||||
#define lua_tointeger(L,i) lua_tointegerx(L,(i),NULL)
|
||||
|
||||
#define lua_pop(L,n) lua_settop(L, -(n)-1)
|
||||
|
||||
#define lua_newtable(L) lua_createtable(L, 0, 0)
|
||||
|
||||
#define lua_register(L,n,f) (lua_pushcfunction(L, (f)), lua_setglobal(L, (n)))
|
||||
|
||||
#define lua_pushcfunction(L,f) lua_pushcclosure(L, (f), 0)
|
||||
|
||||
#define lua_isfunction(L,n) (lua_type(L, (n)) == LUA_TFUNCTION)
|
||||
#define lua_istable(L,n) (lua_type(L, (n)) == LUA_TTABLE)
|
||||
#define lua_islightuserdata(L,n) (lua_type(L, (n)) == LUA_TLIGHTUSERDATA)
|
||||
#define lua_isnil(L,n) (lua_type(L, (n)) == LUA_TNIL)
|
||||
#define lua_isboolean(L,n) (lua_type(L, (n)) == LUA_TBOOLEAN)
|
||||
#define lua_isthread(L,n) (lua_type(L, (n)) == LUA_TTHREAD)
|
||||
#define lua_isnone(L,n) (lua_type(L, (n)) == LUA_TNONE)
|
||||
#define lua_isnoneornil(L, n) (lua_type(L, (n)) <= 0)
|
||||
|
||||
#define lua_pushliteral(L, s) lua_pushstring(L, "" s)
|
||||
|
||||
#define lua_pushglobaltable(L) \
|
||||
((void)lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS))
|
||||
|
||||
#define lua_tostring(L,i) lua_tolstring(L, (i), NULL)
|
||||
|
||||
|
||||
#define lua_insert(L,idx) lua_rotate(L, (idx), 1)
|
||||
|
||||
#define lua_remove(L,idx) (lua_rotate(L, (idx), -1), lua_pop(L, 1))
|
||||
|
||||
#define lua_replace(L,idx) (lua_copy(L, -1, (idx)), lua_pop(L, 1))
|
||||
|
||||
/* }============================================================== */
|
||||
|
||||
|
||||
/*
|
||||
** {==============================================================
|
||||
** compatibility macros for unsigned conversions
|
||||
** ===============================================================
|
||||
*/
|
||||
#if defined(LUA_COMPAT_APIINTCASTS)
|
||||
|
||||
#define lua_pushunsigned(L,n) lua_pushinteger(L, (lua_Integer)(n))
|
||||
#define lua_tounsignedx(L,i,is) ((lua_Unsigned)lua_tointegerx(L,i,is))
|
||||
#define lua_tounsigned(L,i) lua_tounsignedx(L,(i),NULL)
|
||||
|
||||
#endif
|
||||
/* }============================================================== */
|
||||
|
||||
/*
|
||||
** {======================================================================
|
||||
** Debug API
|
||||
** =======================================================================
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
** Event codes
|
||||
*/
|
||||
#define LUA_HOOKCALL 0
|
||||
#define LUA_HOOKRET 1
|
||||
#define LUA_HOOKLINE 2
|
||||
#define LUA_HOOKCOUNT 3
|
||||
#define LUA_HOOKTAILCALL 4
|
||||
|
||||
|
||||
/*
|
||||
** Event masks
|
||||
*/
|
||||
#define LUA_MASKCALL (1 << LUA_HOOKCALL)
|
||||
#define LUA_MASKRET (1 << LUA_HOOKRET)
|
||||
#define LUA_MASKLINE (1 << LUA_HOOKLINE)
|
||||
#define LUA_MASKCOUNT (1 << LUA_HOOKCOUNT)
|
||||
|
||||
typedef struct lua_Debug lua_Debug; /* activation record */
|
||||
|
||||
|
||||
/* Functions to be called by the debugger in specific events */
|
||||
typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar);
|
||||
|
||||
|
||||
LUA_API int (lua_getstack) (lua_State *L, int level, lua_Debug *ar);
|
||||
LUA_API int (lua_getinfo) (lua_State *L, const char *what, lua_Debug *ar);
|
||||
LUA_API const char *(lua_getlocal) (lua_State *L, const lua_Debug *ar, int n);
|
||||
LUA_API const char *(lua_setlocal) (lua_State *L, const lua_Debug *ar, int n);
|
||||
LUA_API const char *(lua_getupvalue) (lua_State *L, int funcindex, int n);
|
||||
LUA_API const char *(lua_setupvalue) (lua_State *L, int funcindex, int n);
|
||||
|
||||
LUA_API void *(lua_upvalueid) (lua_State *L, int fidx, int n);
|
||||
LUA_API void (lua_upvaluejoin) (lua_State *L, int fidx1, int n1,
|
||||
int fidx2, int n2);
|
||||
|
||||
LUA_API void (lua_sethook) (lua_State *L, lua_Hook func, int mask, int count);
|
||||
LUA_API lua_Hook (lua_gethook) (lua_State *L);
|
||||
LUA_API int (lua_gethookmask) (lua_State *L);
|
||||
LUA_API int (lua_gethookcount) (lua_State *L);
|
||||
|
||||
|
||||
struct lua_Debug {
|
||||
int event;
|
||||
const char *name; /* (n) */
|
||||
const char *namewhat; /* (n) 'global', 'local', 'field', 'method' */
|
||||
const char *what; /* (S) 'Lua', 'C', 'main', 'tail' */
|
||||
const char *source; /* (S) */
|
||||
int currentline; /* (l) */
|
||||
int linedefined; /* (S) */
|
||||
int lastlinedefined; /* (S) */
|
||||
unsigned char nups; /* (u) number of upvalues */
|
||||
unsigned char nparams;/* (u) number of parameters */
|
||||
char isvararg; /* (u) */
|
||||
char istailcall; /* (t) */
|
||||
char short_src[LUA_IDSIZE]; /* (S) */
|
||||
/* private part */
|
||||
struct CallInfo *i_ci; /* active function */
|
||||
};
|
||||
|
||||
/* }====================================================================== */
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Copyright (C) 1994-2016 Lua.org, PUC-Rio.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
******************************************************************************/
|
||||
|
||||
|
||||
#endif
|
||||
@ -1,9 +0,0 @@
|
||||
// lua.hpp
|
||||
// Lua header files for C++
|
||||
// <<extern "C">> not supplied automatically because Lua also compiles as C++
|
||||
|
||||
extern "C" {
|
||||
#include "lua.h"
|
||||
#include "lualib.h"
|
||||
#include "lauxlib.h"
|
||||
}
|
||||
@ -1,769 +0,0 @@
|
||||
/*
|
||||
** $Id: luaconf.h,v 1.255 2016/05/01 20:06:09 roberto Exp $
|
||||
** Configuration file for Lua
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
|
||||
#ifndef luaconf_h
|
||||
#define luaconf_h
|
||||
|
||||
#include <limits.h>
|
||||
#include <stddef.h>
|
||||
|
||||
|
||||
/*
|
||||
** ===================================================================
|
||||
** Search for "@@" to find all configurable definitions.
|
||||
** ===================================================================
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
** {====================================================================
|
||||
** System Configuration: macros to adapt (if needed) Lua to some
|
||||
** particular platform, for instance compiling it with 32-bit numbers or
|
||||
** restricting it to C89.
|
||||
** =====================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ LUA_32BITS enables Lua with 32-bit integers and 32-bit floats. You
|
||||
** can also define LUA_32BITS in the make file, but changing here you
|
||||
** ensure that all software connected to Lua will be compiled with the
|
||||
** same configuration.
|
||||
*/
|
||||
/* #define LUA_32BITS */
|
||||
|
||||
|
||||
/*
|
||||
@@ LUA_USE_C89 controls the use of non-ISO-C89 features.
|
||||
** Define it if you want Lua to avoid the use of a few C99 features
|
||||
** or Windows-specific features on Windows.
|
||||
*/
|
||||
/* #define LUA_USE_C89 */
|
||||
|
||||
|
||||
/*
|
||||
** By default, Lua on Windows use (some) specific Windows features
|
||||
*/
|
||||
#if !defined(LUA_USE_C89) && defined(_WIN32) && !defined(_WIN32_WCE)
|
||||
#define LUA_USE_WINDOWS /* enable goodies for regular Windows */
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(LUA_USE_WINDOWS)
|
||||
#define LUA_DL_DLL /* enable support for DLL */
|
||||
#define LUA_USE_C89 /* broadly, Windows is C89 */
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(LUA_USE_LINUX)
|
||||
#define LUA_USE_POSIX
|
||||
#define LUA_USE_DLOPEN /* needs an extra library: -ldl */
|
||||
#define LUA_USE_READLINE /* needs some extra libraries */
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(LUA_USE_MACOSX)
|
||||
#define LUA_USE_POSIX
|
||||
#define LUA_USE_DLOPEN /* MacOS does not need -ldl */
|
||||
#define LUA_USE_READLINE /* needs an extra library: -lreadline */
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
@@ LUA_C89_NUMBERS ensures that Lua uses the largest types available for
|
||||
** C89 ('long' and 'double'); Windows always has '__int64', so it does
|
||||
** not need to use this case.
|
||||
*/
|
||||
#if defined(LUA_USE_C89) && !defined(LUA_USE_WINDOWS)
|
||||
#define LUA_C89_NUMBERS
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/*
|
||||
@@ LUAI_BITSINT defines the (minimum) number of bits in an 'int'.
|
||||
*/
|
||||
/* avoid undefined shifts */
|
||||
#if ((INT_MAX >> 15) >> 15) >= 1
|
||||
#define LUAI_BITSINT 32
|
||||
#else
|
||||
/* 'int' always must have at least 16 bits */
|
||||
#define LUAI_BITSINT 16
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
@@ LUA_INT_TYPE defines the type for Lua integers.
|
||||
@@ LUA_FLOAT_TYPE defines the type for Lua floats.
|
||||
** Lua should work fine with any mix of these options (if supported
|
||||
** by your C compiler). The usual configurations are 64-bit integers
|
||||
** and 'double' (the default), 32-bit integers and 'float' (for
|
||||
** restricted platforms), and 'long'/'double' (for C compilers not
|
||||
** compliant with C99, which may not have support for 'long long').
|
||||
*/
|
||||
|
||||
/* predefined options for LUA_INT_TYPE */
|
||||
#define LUA_INT_INT 1
|
||||
#define LUA_INT_LONG 2
|
||||
#define LUA_INT_LONGLONG 3
|
||||
|
||||
/* predefined options for LUA_FLOAT_TYPE */
|
||||
#define LUA_FLOAT_FLOAT 1
|
||||
#define LUA_FLOAT_DOUBLE 2
|
||||
#define LUA_FLOAT_LONGDOUBLE 3
|
||||
|
||||
#if defined(LUA_32BITS) /* { */
|
||||
/*
|
||||
** 32-bit integers and 'float'
|
||||
*/
|
||||
#if LUAI_BITSINT >= 32 /* use 'int' if big enough */
|
||||
#define LUA_INT_TYPE LUA_INT_INT
|
||||
#else /* otherwise use 'long' */
|
||||
#define LUA_INT_TYPE LUA_INT_LONG
|
||||
#endif
|
||||
#define LUA_FLOAT_TYPE LUA_FLOAT_FLOAT
|
||||
|
||||
#elif defined(LUA_C89_NUMBERS) /* }{ */
|
||||
/*
|
||||
** largest types available for C89 ('long' and 'double')
|
||||
*/
|
||||
#define LUA_INT_TYPE LUA_INT_LONG
|
||||
#define LUA_FLOAT_TYPE LUA_FLOAT_DOUBLE
|
||||
|
||||
#endif /* } */
|
||||
|
||||
|
||||
/*
|
||||
** default configuration for 64-bit Lua ('long long' and 'double')
|
||||
*/
|
||||
#if !defined(LUA_INT_TYPE)
|
||||
#define LUA_INT_TYPE LUA_INT_LONGLONG
|
||||
#endif
|
||||
|
||||
#if !defined(LUA_FLOAT_TYPE)
|
||||
#define LUA_FLOAT_TYPE LUA_FLOAT_DOUBLE
|
||||
#endif
|
||||
|
||||
/* }================================================================== */
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** {==================================================================
|
||||
** Configuration for Paths.
|
||||
** ===================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ LUA_PATH_DEFAULT is the default path that Lua uses to look for
|
||||
** Lua libraries.
|
||||
@@ LUA_CPATH_DEFAULT is the default path that Lua uses to look for
|
||||
** C libraries.
|
||||
** CHANGE them if your machine has a non-conventional directory
|
||||
** hierarchy or if you want to install your libraries in
|
||||
** non-conventional directories.
|
||||
*/
|
||||
#define LUA_VDIR LUA_VERSION_MAJOR "." LUA_VERSION_MINOR
|
||||
#if defined(_WIN32) /* { */
|
||||
/*
|
||||
** In Windows, any exclamation mark ('!') in the path is replaced by the
|
||||
** path of the directory of the executable file of the current process.
|
||||
*/
|
||||
#define LUA_LDIR "!\\lua\\"
|
||||
#define LUA_CDIR "!\\"
|
||||
#define LUA_SHRDIR "!\\..\\share\\lua\\" LUA_VDIR "\\"
|
||||
#define LUA_PATH_DEFAULT \
|
||||
LUA_LDIR"?.lua;" LUA_LDIR"?\\init.lua;" \
|
||||
LUA_CDIR"?.lua;" LUA_CDIR"?\\init.lua;" \
|
||||
LUA_SHRDIR"?.lua;" LUA_SHRDIR"?\\init.lua;" \
|
||||
".\\?.lua;" ".\\?\\init.lua"
|
||||
#define LUA_CPATH_DEFAULT \
|
||||
LUA_CDIR"?.dll;" \
|
||||
LUA_CDIR"..\\lib\\lua\\" LUA_VDIR "\\?.dll;" \
|
||||
LUA_CDIR"loadall.dll;" ".\\?.dll;" \
|
||||
LUA_CDIR"?53.dll;" ".\\?53.dll"
|
||||
|
||||
#else /* }{ */
|
||||
|
||||
#define LUA_ROOT "/usr/local/"
|
||||
#define LUA_LDIR LUA_ROOT "share/lua/" LUA_VDIR "/"
|
||||
#define LUA_CDIR LUA_ROOT "lib/lua/" LUA_VDIR "/"
|
||||
#define LUA_PATH_DEFAULT \
|
||||
LUA_LDIR"?.lua;" LUA_LDIR"?/init.lua;" \
|
||||
LUA_CDIR"?.lua;" LUA_CDIR"?/init.lua;" \
|
||||
"./?.lua;" "./?/init.lua"
|
||||
#define LUA_CPATH_DEFAULT \
|
||||
LUA_CDIR"?.so;" LUA_CDIR"loadall.so;" "./?.so;" \
|
||||
LUA_CDIR"lib?53.so;" "./lib?53.so"
|
||||
#endif /* } */
|
||||
|
||||
|
||||
/*
|
||||
@@ LUA_DIRSEP is the directory separator (for submodules).
|
||||
** CHANGE it if your machine does not use "/" as the directory separator
|
||||
** and is not Windows. (On Windows Lua automatically uses "\".)
|
||||
*/
|
||||
#if defined(_WIN32)
|
||||
#define LUA_DIRSEP "\\"
|
||||
#else
|
||||
#define LUA_DIRSEP "/"
|
||||
#endif
|
||||
|
||||
/* }================================================================== */
|
||||
|
||||
|
||||
/*
|
||||
** {==================================================================
|
||||
** Marks for exported symbols in the C code
|
||||
** ===================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ LUA_API is a mark for all core API functions.
|
||||
@@ LUALIB_API is a mark for all auxiliary library functions.
|
||||
@@ LUAMOD_API is a mark for all standard library opening functions.
|
||||
** CHANGE them if you need to define those functions in some special way.
|
||||
** For instance, if you want to create one Windows DLL with the core and
|
||||
** the libraries, you may want to use the following definition (define
|
||||
** LUA_BUILD_AS_DLL to get it).
|
||||
*/
|
||||
#if defined(LUA_BUILD_AS_DLL) /* { */
|
||||
|
||||
#if defined(LUA_CORE) || defined(LUA_LIB) /* { */
|
||||
#define LUA_API __declspec(dllexport)
|
||||
#else /* }{ */
|
||||
#define LUA_API __declspec(dllimport)
|
||||
#endif /* } */
|
||||
|
||||
#else /* }{ */
|
||||
|
||||
#define LUA_API extern
|
||||
|
||||
#endif /* } */
|
||||
|
||||
|
||||
/* more often than not the libs go together with the core */
|
||||
#define LUALIB_API LUA_API
|
||||
#define LUAMOD_API LUALIB_API
|
||||
|
||||
|
||||
/*
|
||||
@@ LUAI_FUNC is a mark for all extern functions that are not to be
|
||||
** exported to outside modules.
|
||||
@@ LUAI_DDEF and LUAI_DDEC are marks for all extern (const) variables
|
||||
** that are not to be exported to outside modules (LUAI_DDEF for
|
||||
** definitions and LUAI_DDEC for declarations).
|
||||
** CHANGE them if you need to mark them in some special way. Elf/gcc
|
||||
** (versions 3.2 and later) mark them as "hidden" to optimize access
|
||||
** when Lua is compiled as a shared library. Not all elf targets support
|
||||
** this attribute. Unfortunately, gcc does not offer a way to check
|
||||
** whether the target offers that support, and those without support
|
||||
** give a warning about it. To avoid these warnings, change to the
|
||||
** default definition.
|
||||
*/
|
||||
#if defined(__GNUC__) && ((__GNUC__*100 + __GNUC_MINOR__) >= 302) && \
|
||||
defined(__ELF__) /* { */
|
||||
#define LUAI_FUNC __attribute__((visibility("hidden"))) extern
|
||||
#else /* }{ */
|
||||
#define LUAI_FUNC extern
|
||||
#endif /* } */
|
||||
|
||||
#define LUAI_DDEC LUAI_FUNC
|
||||
#define LUAI_DDEF /* empty */
|
||||
|
||||
/* }================================================================== */
|
||||
|
||||
|
||||
/*
|
||||
** {==================================================================
|
||||
** Compatibility with previous versions
|
||||
** ===================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ LUA_COMPAT_5_2 controls other macros for compatibility with Lua 5.2.
|
||||
@@ LUA_COMPAT_5_1 controls other macros for compatibility with Lua 5.1.
|
||||
** You can define it to get all options, or change specific options
|
||||
** to fit your specific needs.
|
||||
*/
|
||||
#if defined(LUA_COMPAT_5_2) /* { */
|
||||
|
||||
/*
|
||||
@@ LUA_COMPAT_MATHLIB controls the presence of several deprecated
|
||||
** functions in the mathematical library.
|
||||
*/
|
||||
#define LUA_COMPAT_MATHLIB
|
||||
|
||||
/*
|
||||
@@ LUA_COMPAT_BITLIB controls the presence of library 'bit32'.
|
||||
*/
|
||||
#define LUA_COMPAT_BITLIB
|
||||
|
||||
/*
|
||||
@@ LUA_COMPAT_IPAIRS controls the effectiveness of the __ipairs metamethod.
|
||||
*/
|
||||
#define LUA_COMPAT_IPAIRS
|
||||
|
||||
/*
|
||||
@@ LUA_COMPAT_APIINTCASTS controls the presence of macros for
|
||||
** manipulating other integer types (lua_pushunsigned, lua_tounsigned,
|
||||
** luaL_checkint, luaL_checklong, etc.)
|
||||
*/
|
||||
#define LUA_COMPAT_APIINTCASTS
|
||||
|
||||
#endif /* } */
|
||||
|
||||
|
||||
#if defined(LUA_COMPAT_5_1) /* { */
|
||||
|
||||
/* Incompatibilities from 5.2 -> 5.3 */
|
||||
#define LUA_COMPAT_MATHLIB
|
||||
#define LUA_COMPAT_APIINTCASTS
|
||||
|
||||
/*
|
||||
@@ LUA_COMPAT_UNPACK controls the presence of global 'unpack'.
|
||||
** You can replace it with 'table.unpack'.
|
||||
*/
|
||||
#define LUA_COMPAT_UNPACK
|
||||
|
||||
/*
|
||||
@@ LUA_COMPAT_LOADERS controls the presence of table 'package.loaders'.
|
||||
** You can replace it with 'package.searchers'.
|
||||
*/
|
||||
#define LUA_COMPAT_LOADERS
|
||||
|
||||
/*
|
||||
@@ macro 'lua_cpcall' emulates deprecated function lua_cpcall.
|
||||
** You can call your C function directly (with light C functions).
|
||||
*/
|
||||
#define lua_cpcall(L,f,u) \
|
||||
(lua_pushcfunction(L, (f)), \
|
||||
lua_pushlightuserdata(L,(u)), \
|
||||
lua_pcall(L,1,0,0))
|
||||
|
||||
|
||||
/*
|
||||
@@ LUA_COMPAT_LOG10 defines the function 'log10' in the math library.
|
||||
** You can rewrite 'log10(x)' as 'log(x, 10)'.
|
||||
*/
|
||||
#define LUA_COMPAT_LOG10
|
||||
|
||||
/*
|
||||
@@ LUA_COMPAT_LOADSTRING defines the function 'loadstring' in the base
|
||||
** library. You can rewrite 'loadstring(s)' as 'load(s)'.
|
||||
*/
|
||||
#define LUA_COMPAT_LOADSTRING
|
||||
|
||||
/*
|
||||
@@ LUA_COMPAT_MAXN defines the function 'maxn' in the table library.
|
||||
*/
|
||||
#define LUA_COMPAT_MAXN
|
||||
|
||||
/*
|
||||
@@ The following macros supply trivial compatibility for some
|
||||
** changes in the API. The macros themselves document how to
|
||||
** change your code to avoid using them.
|
||||
*/
|
||||
#define lua_strlen(L,i) lua_rawlen(L, (i))
|
||||
|
||||
#define lua_objlen(L,i) lua_rawlen(L, (i))
|
||||
|
||||
#define lua_equal(L,idx1,idx2) lua_compare(L,(idx1),(idx2),LUA_OPEQ)
|
||||
#define lua_lessthan(L,idx1,idx2) lua_compare(L,(idx1),(idx2),LUA_OPLT)
|
||||
|
||||
/*
|
||||
@@ LUA_COMPAT_MODULE controls compatibility with previous
|
||||
** module functions 'module' (Lua) and 'luaL_register' (C).
|
||||
*/
|
||||
#define LUA_COMPAT_MODULE
|
||||
|
||||
#endif /* } */
|
||||
|
||||
|
||||
/*
|
||||
@@ LUA_COMPAT_FLOATSTRING makes Lua format integral floats without a
|
||||
@@ a float mark ('.0').
|
||||
** This macro is not on by default even in compatibility mode,
|
||||
** because this is not really an incompatibility.
|
||||
*/
|
||||
/* #define LUA_COMPAT_FLOATSTRING */
|
||||
|
||||
/* }================================================================== */
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** {==================================================================
|
||||
** Configuration for Numbers.
|
||||
** Change these definitions if no predefined LUA_FLOAT_* / LUA_INT_*
|
||||
** satisfy your needs.
|
||||
** ===================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ LUA_NUMBER is the floating-point type used by Lua.
|
||||
@@ LUAI_UACNUMBER is the result of an 'usual argument conversion'
|
||||
@@ over a floating number.
|
||||
@@ l_mathlim(x) corrects limit name 'x' to the proper float type
|
||||
** by prefixing it with one of FLT/DBL/LDBL.
|
||||
@@ LUA_NUMBER_FRMLEN is the length modifier for writing floats.
|
||||
@@ LUA_NUMBER_FMT is the format for writing floats.
|
||||
@@ lua_number2str converts a float to a string.
|
||||
@@ l_mathop allows the addition of an 'l' or 'f' to all math operations.
|
||||
@@ l_floor takes the floor of a float.
|
||||
@@ lua_str2number converts a decimal numeric string to a number.
|
||||
*/
|
||||
|
||||
|
||||
/* The following definitions are good for most cases here */
|
||||
|
||||
#define l_floor(x) (l_mathop(floor)(x))
|
||||
|
||||
#define lua_number2str(s,sz,n) l_sprintf((s), sz, LUA_NUMBER_FMT, (n))
|
||||
|
||||
/*
|
||||
@@ lua_numbertointeger converts a float number to an integer, or
|
||||
** returns 0 if float is not within the range of a lua_Integer.
|
||||
** (The range comparisons are tricky because of rounding. The tests
|
||||
** here assume a two-complement representation, where MININTEGER always
|
||||
** has an exact representation as a float; MAXINTEGER may not have one,
|
||||
** and therefore its conversion to float may have an ill-defined value.)
|
||||
*/
|
||||
#define lua_numbertointeger(n,p) \
|
||||
((n) >= (LUA_NUMBER)(LUA_MININTEGER) && \
|
||||
(n) < -(LUA_NUMBER)(LUA_MININTEGER) && \
|
||||
(*(p) = (LUA_INTEGER)(n), 1))
|
||||
|
||||
|
||||
/* now the variable definitions */
|
||||
|
||||
#if LUA_FLOAT_TYPE == LUA_FLOAT_FLOAT /* { single float */
|
||||
|
||||
#define LUA_NUMBER float
|
||||
|
||||
#define l_mathlim(n) (FLT_##n)
|
||||
|
||||
#define LUAI_UACNUMBER double
|
||||
|
||||
#define LUA_NUMBER_FRMLEN ""
|
||||
#define LUA_NUMBER_FMT "%.7g"
|
||||
|
||||
#define l_mathop(op) op##f
|
||||
|
||||
#define lua_str2number(s,p) strtof((s), (p))
|
||||
|
||||
|
||||
#elif LUA_FLOAT_TYPE == LUA_FLOAT_LONGDOUBLE /* }{ long double */
|
||||
|
||||
#define LUA_NUMBER long double
|
||||
|
||||
#define l_mathlim(n) (LDBL_##n)
|
||||
|
||||
#define LUAI_UACNUMBER long double
|
||||
|
||||
#define LUA_NUMBER_FRMLEN "L"
|
||||
#define LUA_NUMBER_FMT "%.19Lg"
|
||||
|
||||
#define l_mathop(op) op##l
|
||||
|
||||
#define lua_str2number(s,p) strtold((s), (p))
|
||||
|
||||
#elif LUA_FLOAT_TYPE == LUA_FLOAT_DOUBLE /* }{ double */
|
||||
|
||||
#define LUA_NUMBER double
|
||||
|
||||
#define l_mathlim(n) (DBL_##n)
|
||||
|
||||
#define LUAI_UACNUMBER double
|
||||
|
||||
#define LUA_NUMBER_FRMLEN ""
|
||||
#define LUA_NUMBER_FMT "%.14g"
|
||||
|
||||
#define l_mathop(op) op
|
||||
|
||||
#define lua_str2number(s,p) strtod((s), (p))
|
||||
|
||||
#else /* }{ */
|
||||
|
||||
#error "numeric float type not defined"
|
||||
|
||||
#endif /* } */
|
||||
|
||||
|
||||
|
||||
/*
|
||||
@@ LUA_INTEGER is the integer type used by Lua.
|
||||
**
|
||||
@@ LUA_UNSIGNED is the unsigned version of LUA_INTEGER.
|
||||
**
|
||||
@@ LUAI_UACINT is the result of an 'usual argument conversion'
|
||||
@@ over a lUA_INTEGER.
|
||||
@@ LUA_INTEGER_FRMLEN is the length modifier for reading/writing integers.
|
||||
@@ LUA_INTEGER_FMT is the format for writing integers.
|
||||
@@ LUA_MAXINTEGER is the maximum value for a LUA_INTEGER.
|
||||
@@ LUA_MININTEGER is the minimum value for a LUA_INTEGER.
|
||||
@@ lua_integer2str converts an integer to a string.
|
||||
*/
|
||||
|
||||
|
||||
/* The following definitions are good for most cases here */
|
||||
|
||||
#define LUA_INTEGER_FMT "%" LUA_INTEGER_FRMLEN "d"
|
||||
#define lua_integer2str(s,sz,n) l_sprintf((s), sz, LUA_INTEGER_FMT, (n))
|
||||
|
||||
#define LUAI_UACINT LUA_INTEGER
|
||||
|
||||
/*
|
||||
** use LUAI_UACINT here to avoid problems with promotions (which
|
||||
** can turn a comparison between unsigneds into a signed comparison)
|
||||
*/
|
||||
#define LUA_UNSIGNED unsigned LUAI_UACINT
|
||||
|
||||
|
||||
/* now the variable definitions */
|
||||
|
||||
#if LUA_INT_TYPE == LUA_INT_INT /* { int */
|
||||
|
||||
#define LUA_INTEGER int
|
||||
#define LUA_INTEGER_FRMLEN ""
|
||||
|
||||
#define LUA_MAXINTEGER INT_MAX
|
||||
#define LUA_MININTEGER INT_MIN
|
||||
|
||||
#elif LUA_INT_TYPE == LUA_INT_LONG /* }{ long */
|
||||
|
||||
#define LUA_INTEGER long
|
||||
#define LUA_INTEGER_FRMLEN "l"
|
||||
|
||||
#define LUA_MAXINTEGER LONG_MAX
|
||||
#define LUA_MININTEGER LONG_MIN
|
||||
|
||||
#elif LUA_INT_TYPE == LUA_INT_LONGLONG /* }{ long long */
|
||||
|
||||
/* use presence of macro LLONG_MAX as proxy for C99 compliance */
|
||||
#if defined(LLONG_MAX) /* { */
|
||||
/* use ISO C99 stuff */
|
||||
|
||||
#define LUA_INTEGER long long
|
||||
#define LUA_INTEGER_FRMLEN "ll"
|
||||
|
||||
#define LUA_MAXINTEGER LLONG_MAX
|
||||
#define LUA_MININTEGER LLONG_MIN
|
||||
|
||||
#elif defined(LUA_USE_WINDOWS) /* }{ */
|
||||
/* in Windows, can use specific Windows types */
|
||||
|
||||
#define LUA_INTEGER __int64
|
||||
#define LUA_INTEGER_FRMLEN "I64"
|
||||
|
||||
#define LUA_MAXINTEGER _I64_MAX
|
||||
#define LUA_MININTEGER _I64_MIN
|
||||
|
||||
#else /* }{ */
|
||||
|
||||
#error "Compiler does not support 'long long'. Use option '-DLUA_32BITS' \
|
||||
or '-DLUA_C89_NUMBERS' (see file 'luaconf.h' for details)"
|
||||
|
||||
#endif /* } */
|
||||
|
||||
#else /* }{ */
|
||||
|
||||
#error "numeric integer type not defined"
|
||||
|
||||
#endif /* } */
|
||||
|
||||
/* }================================================================== */
|
||||
|
||||
|
||||
/*
|
||||
** {==================================================================
|
||||
** Dependencies with C99 and other C details
|
||||
** ===================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ l_sprintf is equivalent to 'snprintf' or 'sprintf' in C89.
|
||||
** (All uses in Lua have only one format item.)
|
||||
*/
|
||||
#if !defined(LUA_USE_C89)
|
||||
#define l_sprintf(s,sz,f,i) snprintf(s,sz,f,i)
|
||||
#else
|
||||
#define l_sprintf(s,sz,f,i) ((void)(sz), sprintf(s,f,i))
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
@@ lua_strx2number converts an hexadecimal numeric string to a number.
|
||||
** In C99, 'strtod' does that conversion. Otherwise, you can
|
||||
** leave 'lua_strx2number' undefined and Lua will provide its own
|
||||
** implementation.
|
||||
*/
|
||||
#if !defined(LUA_USE_C89)
|
||||
#define lua_strx2number(s,p) lua_str2number(s,p)
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
@@ lua_number2strx converts a float to an hexadecimal numeric string.
|
||||
** In C99, 'sprintf' (with format specifiers '%a'/'%A') does that.
|
||||
** Otherwise, you can leave 'lua_number2strx' undefined and Lua will
|
||||
** provide its own implementation.
|
||||
*/
|
||||
#if !defined(LUA_USE_C89)
|
||||
#define lua_number2strx(L,b,sz,f,n) ((void)L, l_sprintf(b,sz,f,n))
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** 'strtof' and 'opf' variants for math functions are not valid in
|
||||
** C89. Otherwise, the macro 'HUGE_VALF' is a good proxy for testing the
|
||||
** availability of these variants. ('math.h' is already included in
|
||||
** all files that use these macros.)
|
||||
*/
|
||||
#if defined(LUA_USE_C89) || (defined(HUGE_VAL) && !defined(HUGE_VALF))
|
||||
#undef l_mathop /* variants not available */
|
||||
#undef lua_str2number
|
||||
#define l_mathop(op) (lua_Number)op /* no variant */
|
||||
#define lua_str2number(s,p) ((lua_Number)strtod((s), (p)))
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
@@ LUA_KCONTEXT is the type of the context ('ctx') for continuation
|
||||
** functions. It must be a numerical type; Lua will use 'intptr_t' if
|
||||
** available, otherwise it will use 'ptrdiff_t' (the nearest thing to
|
||||
** 'intptr_t' in C89)
|
||||
*/
|
||||
#define LUA_KCONTEXT ptrdiff_t
|
||||
|
||||
#if !defined(LUA_USE_C89) && defined(__STDC_VERSION__) && \
|
||||
__STDC_VERSION__ >= 199901L
|
||||
#include <stdint.h>
|
||||
#if defined(INTPTR_MAX) /* even in C99 this type is optional */
|
||||
#undef LUA_KCONTEXT
|
||||
#define LUA_KCONTEXT intptr_t
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
@@ lua_getlocaledecpoint gets the locale "radix character" (decimal point).
|
||||
** Change that if you do not want to use C locales. (Code using this
|
||||
** macro must include header 'locale.h'.)
|
||||
*/
|
||||
#if !defined(lua_getlocaledecpoint)
|
||||
#define lua_getlocaledecpoint() (localeconv()->decimal_point[0])
|
||||
#endif
|
||||
|
||||
/* }================================================================== */
|
||||
|
||||
|
||||
/*
|
||||
** {==================================================================
|
||||
** Language Variations
|
||||
** =====================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ LUA_NOCVTN2S/LUA_NOCVTS2N control how Lua performs some
|
||||
** coercions. Define LUA_NOCVTN2S to turn off automatic coercion from
|
||||
** numbers to strings. Define LUA_NOCVTS2N to turn off automatic
|
||||
** coercion from strings to numbers.
|
||||
*/
|
||||
/* #define LUA_NOCVTN2S */
|
||||
/* #define LUA_NOCVTS2N */
|
||||
|
||||
|
||||
/*
|
||||
@@ LUA_USE_APICHECK turns on several consistency checks on the C API.
|
||||
** Define it as a help when debugging C code.
|
||||
*/
|
||||
#if defined(LUA_USE_APICHECK)
|
||||
#include <assert.h>
|
||||
#define luai_apicheck(l,e) assert(e)
|
||||
#endif
|
||||
|
||||
/* }================================================================== */
|
||||
|
||||
|
||||
/*
|
||||
** {==================================================================
|
||||
** Macros that affect the API and must be stable (that is, must be the
|
||||
** same when you compile Lua and when you compile code that links to
|
||||
** Lua). You probably do not want/need to change them.
|
||||
** =====================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ LUAI_MAXSTACK limits the size of the Lua stack.
|
||||
** CHANGE it if you need a different limit. This limit is arbitrary;
|
||||
** its only purpose is to stop Lua from consuming unlimited stack
|
||||
** space (and to reserve some numbers for pseudo-indices).
|
||||
*/
|
||||
#if LUAI_BITSINT >= 32
|
||||
#define LUAI_MAXSTACK 1000000
|
||||
#else
|
||||
#define LUAI_MAXSTACK 15000
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
@@ LUA_EXTRASPACE defines the size of a raw memory area associated with
|
||||
** a Lua state with very fast access.
|
||||
** CHANGE it if you need a different size.
|
||||
*/
|
||||
#define LUA_EXTRASPACE (sizeof(void *))
|
||||
|
||||
|
||||
/*
|
||||
@@ LUA_IDSIZE gives the maximum size for the description of the source
|
||||
@@ of a function in debug information.
|
||||
** CHANGE it if you want a different size.
|
||||
*/
|
||||
#define LUA_IDSIZE 60
|
||||
|
||||
|
||||
/*
|
||||
@@ LUAL_BUFFERSIZE is the buffer size used by the lauxlib buffer system.
|
||||
** CHANGE it if it uses too much C-stack space. (For long double,
|
||||
** 'string.format("%.99f", 1e4932)' needs ~5030 bytes, so a
|
||||
** smaller buffer would force a memory allocation for each call to
|
||||
** 'string.format'.)
|
||||
*/
|
||||
#if defined(LUA_FLOAT_LONGDOUBLE)
|
||||
#define LUAL_BUFFERSIZE 8192
|
||||
#else
|
||||
#define LUAL_BUFFERSIZE ((int)(0x80 * sizeof(void*) * sizeof(lua_Integer)))
|
||||
#endif
|
||||
|
||||
/* }================================================================== */
|
||||
|
||||
|
||||
/*
|
||||
@@ LUA_QL describes how error messages quote program elements.
|
||||
** Lua does not use these macros anymore; they are here for
|
||||
** compatibility only.
|
||||
*/
|
||||
#define LUA_QL(x) "'" x "'"
|
||||
#define LUA_QS LUA_QL("%s")
|
||||
|
||||
|
||||
|
||||
|
||||
/* =================================================================== */
|
||||
|
||||
/*
|
||||
** Local configuration. You can use this space to add your redefinitions
|
||||
** without modifying the main part of the file.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@ -1,58 +0,0 @@
|
||||
/*
|
||||
** $Id: lualib.h,v 1.44 2014/02/06 17:32:33 roberto Exp $
|
||||
** Lua standard libraries
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
|
||||
#ifndef lualib_h
|
||||
#define lualib_h
|
||||
|
||||
#include "lua.h"
|
||||
|
||||
|
||||
|
||||
LUAMOD_API int (luaopen_base) (lua_State *L);
|
||||
|
||||
#define LUA_COLIBNAME "coroutine"
|
||||
LUAMOD_API int (luaopen_coroutine) (lua_State *L);
|
||||
|
||||
#define LUA_TABLIBNAME "table"
|
||||
LUAMOD_API int (luaopen_table) (lua_State *L);
|
||||
|
||||
#define LUA_IOLIBNAME "io"
|
||||
LUAMOD_API int (luaopen_io) (lua_State *L);
|
||||
|
||||
#define LUA_OSLIBNAME "os"
|
||||
LUAMOD_API int (luaopen_os) (lua_State *L);
|
||||
|
||||
#define LUA_STRLIBNAME "string"
|
||||
LUAMOD_API int (luaopen_string) (lua_State *L);
|
||||
|
||||
#define LUA_UTF8LIBNAME "utf8"
|
||||
LUAMOD_API int (luaopen_utf8) (lua_State *L);
|
||||
|
||||
#define LUA_BITLIBNAME "bit32"
|
||||
LUAMOD_API int (luaopen_bit32) (lua_State *L);
|
||||
|
||||
#define LUA_MATHLIBNAME "math"
|
||||
LUAMOD_API int (luaopen_math) (lua_State *L);
|
||||
|
||||
#define LUA_DBLIBNAME "debug"
|
||||
LUAMOD_API int (luaopen_debug) (lua_State *L);
|
||||
|
||||
#define LUA_LOADLIBNAME "package"
|
||||
LUAMOD_API int (luaopen_package) (lua_State *L);
|
||||
|
||||
|
||||
/* open all previous libraries */
|
||||
LUALIB_API void (luaL_openlibs) (lua_State *L);
|
||||
|
||||
|
||||
|
||||
#if !defined(lua_assert)
|
||||
#define lua_assert(x) ((void)0)
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
@ -1,716 +0,0 @@
|
||||
# Blender v2.79 (sub 0) OBJ File: 'olc_building0.blend'
|
||||
# www.blender.org
|
||||
mtllib olc_building0.mtl
|
||||
o Plane
|
||||
v 0.000000 0.000000 0.000000
|
||||
v 0.000000 -1.000000 0.000000
|
||||
v -1.000000 0.000000 0.000000
|
||||
v -1.000000 -1.000000 0.000000
|
||||
v -0.025000 -0.412671 -0.052133
|
||||
v -0.025000 -0.587329 -0.052133
|
||||
v -0.975000 -0.025000 -0.050000
|
||||
v -0.975000 -0.975000 -0.050000
|
||||
v -1.000000 0.000000 -0.050000
|
||||
v 0.000000 0.000000 -0.050000
|
||||
v 0.000000 -1.000000 -0.050000
|
||||
v -1.000000 -1.000000 -0.050000
|
||||
v -0.975000 -0.025000 -0.517611
|
||||
v -0.975000 -0.975000 -0.517611
|
||||
v -1.000000 0.000000 -0.527316
|
||||
v 0.000000 0.000000 -0.527316
|
||||
v 0.000000 -1.000000 -0.527316
|
||||
v -1.000000 -1.000000 -0.527316
|
||||
v -0.975000 -0.025000 -0.550000
|
||||
v -0.025000 -0.025000 -0.550000
|
||||
v -0.025000 -0.975000 -0.550000
|
||||
v -0.975000 -0.975000 -0.550000
|
||||
v -0.025000 -0.601364 -0.402078
|
||||
v -0.025000 -0.601364 -0.044706
|
||||
v -0.025000 -0.398636 -0.044706
|
||||
v -0.025000 -0.398636 -0.402078
|
||||
v -0.374620 -0.412671 -0.045097
|
||||
v -0.374620 -0.587329 -0.047815
|
||||
v -0.374620 -0.587329 -0.388044
|
||||
v -0.374620 -0.412671 -0.388044
|
||||
v -0.005099 -0.601364 -0.402078
|
||||
v -0.005099 -0.601364 -0.044706
|
||||
v -0.005099 -0.398636 -0.044706
|
||||
v -0.005099 -0.398636 -0.402078
|
||||
v -0.005099 -0.587329 -0.388044
|
||||
v -0.005099 -0.587329 -0.047815
|
||||
v -0.005099 -0.412671 -0.045097
|
||||
v -0.005099 -0.412671 -0.388044
|
||||
v -0.017305 -0.419472 -0.044438
|
||||
v -0.017305 -0.594130 -0.044438
|
||||
v -0.025000 -0.975000 -0.517611
|
||||
v -0.025000 -0.975000 -0.050000
|
||||
v -0.025000 -0.601364 -0.402078
|
||||
v -0.025000 -0.601364 -0.044706
|
||||
v -0.025000 -0.025000 -0.050000
|
||||
v -0.025000 -0.025000 -0.517611
|
||||
v -0.025000 -0.398636 -0.044706
|
||||
v -0.025000 -0.398636 -0.402078
|
||||
v -0.925000 -0.075000 -1.550000
|
||||
v -0.075000 -0.075000 -1.550000
|
||||
v -0.075000 -0.925000 -1.550000
|
||||
v -0.925000 -0.925000 -1.550000
|
||||
v -0.975000 -0.025000 -1.550000
|
||||
v -0.025000 -0.025000 -1.550000
|
||||
v -0.025000 -0.975000 -1.550000
|
||||
v -0.975000 -0.975000 -1.550000
|
||||
v -0.975000 -0.025000 -1.600000
|
||||
v -0.025000 -0.025000 -1.600000
|
||||
v -0.025000 -0.975000 -1.600000
|
||||
v -0.975000 -0.975000 -1.600000
|
||||
v -0.925000 -0.075000 -1.600000
|
||||
v -0.075000 -0.075000 -1.600000
|
||||
v -0.075000 -0.925000 -1.600000
|
||||
v -0.925000 -0.925000 -1.600000
|
||||
v 0.160449 -0.127295 -1.107365
|
||||
v 0.160449 -0.127295 -1.287365
|
||||
v -0.026426 -0.127295 -1.107365
|
||||
v -0.026426 -0.127295 -1.287365
|
||||
v 0.160449 -0.427295 -1.107365
|
||||
v 0.160449 -0.427295 -1.287365
|
||||
v -0.026426 -0.427295 -1.107365
|
||||
v -0.026426 -0.427295 -1.287365
|
||||
v -0.026426 -0.127295 -1.287365
|
||||
v -0.026426 -0.127295 -1.107365
|
||||
v 0.160449 -0.127295 -1.287365
|
||||
v 0.160449 -0.127295 -1.107365
|
||||
v 0.160449 -0.427295 -1.287365
|
||||
v 0.160449 -0.427295 -1.107365
|
||||
v 0.160449 -0.127295 -1.287365
|
||||
v 0.160449 -0.127295 -1.107365
|
||||
v -0.026426 -0.427295 -1.287365
|
||||
v -0.026426 -0.427295 -1.107365
|
||||
v 0.160449 -0.427295 -1.287365
|
||||
v 0.160449 -0.427295 -1.107365
|
||||
v -0.157753 -0.144986 -1.539514
|
||||
v -0.157753 -0.144986 -1.637282
|
||||
v -0.240677 -0.144986 -1.539514
|
||||
v -0.240677 -0.144986 -1.637282
|
||||
v -0.157753 -0.328413 -1.539514
|
||||
v -0.157753 -0.328413 -1.637282
|
||||
v -0.240677 -0.328413 -1.539514
|
||||
v -0.240677 -0.328413 -1.637282
|
||||
v -0.480538 -0.445573 -1.455033
|
||||
v -0.480538 -0.445573 -1.817243
|
||||
v -0.732419 -0.445573 -1.455033
|
||||
v -0.732419 -0.445573 -1.817243
|
||||
v -0.480538 -0.823959 -1.455033
|
||||
v -0.480538 -0.823959 -1.685969
|
||||
v -0.732419 -0.823959 -1.455033
|
||||
v -0.732419 -0.823959 -1.685969
|
||||
v -0.157753 -0.144986 -1.539514
|
||||
v -0.157753 -0.144986 -1.637282
|
||||
v -0.240677 -0.144986 -1.539514
|
||||
v -0.240677 -0.144986 -1.637282
|
||||
v -0.157753 -0.328413 -1.539514
|
||||
v -0.157753 -0.328413 -1.637282
|
||||
v -0.240677 -0.328413 -1.539514
|
||||
v -0.240677 -0.328413 -1.637282
|
||||
v -0.026426 -0.585532 -1.287365
|
||||
v -0.026426 -0.585532 -1.107365
|
||||
v 0.160449 -0.585532 -1.287365
|
||||
v 0.160449 -0.585532 -1.107365
|
||||
v 0.160449 -0.885532 -1.287365
|
||||
v 0.160449 -0.885532 -1.107365
|
||||
v 0.160449 -0.585532 -1.287365
|
||||
v 0.160449 -0.585532 -1.107365
|
||||
v -0.026426 -0.885532 -1.287365
|
||||
v -0.026426 -0.885532 -1.107365
|
||||
v 0.160449 -0.885532 -1.287365
|
||||
v 0.160449 -0.885532 -1.107365
|
||||
v 0.160449 -0.586497 -0.622366
|
||||
v 0.160449 -0.586497 -0.802366
|
||||
v -0.026426 -0.586497 -0.622366
|
||||
v -0.026426 -0.586497 -0.802366
|
||||
v 0.160449 -0.886497 -0.802366
|
||||
v 0.160449 -0.886497 -0.622366
|
||||
v 0.160449 -0.586497 -0.802366
|
||||
v 0.160449 -0.586497 -0.622366
|
||||
v -0.026426 -0.886497 -0.802366
|
||||
v -0.026426 -0.886497 -0.622366
|
||||
v 0.160449 -0.886497 -0.802366
|
||||
v 0.160449 -0.886497 -0.622366
|
||||
v 0.160449 -0.126028 -0.622366
|
||||
v 0.160449 -0.126028 -0.802366
|
||||
v -0.026426 -0.126028 -0.622366
|
||||
v -0.026426 -0.126028 -0.802366
|
||||
v 0.160449 -0.426028 -0.802366
|
||||
v 0.160449 -0.426028 -0.622366
|
||||
v 0.160449 -0.126028 -0.802366
|
||||
v 0.160449 -0.126028 -0.622366
|
||||
v -0.026426 -0.426028 -0.802366
|
||||
v -0.026426 -0.426028 -0.622366
|
||||
v 0.160449 -0.426028 -0.802366
|
||||
v 0.160449 -0.426028 -0.622366
|
||||
v -0.026426 -0.585532 -1.287365
|
||||
v -0.026426 -0.585532 -1.107365
|
||||
v 0.160449 -0.585532 -1.287365
|
||||
v 0.160449 -0.585532 -1.107365
|
||||
v 0.160449 -0.885532 -1.287365
|
||||
v 0.160449 -0.885532 -1.107365
|
||||
v 0.160449 -0.585532 -1.287365
|
||||
v 0.160449 -0.585532 -1.107365
|
||||
v -0.026426 -0.885532 -1.287365
|
||||
v -0.026426 -0.885532 -1.107365
|
||||
v 0.160449 -0.885532 -1.287365
|
||||
v 0.160449 -0.885532 -1.107365
|
||||
v 0.160449 -0.586497 -0.622366
|
||||
v 0.160449 -0.586497 -0.802366
|
||||
v -0.026426 -0.586497 -0.622366
|
||||
v -0.026426 -0.586497 -0.802366
|
||||
v 0.160449 -0.886497 -0.802366
|
||||
v 0.160449 -0.886497 -0.622366
|
||||
v 0.160449 -0.586497 -0.802366
|
||||
v 0.160449 -0.586497 -0.622366
|
||||
v -0.026426 -0.886497 -0.802366
|
||||
v -0.026426 -0.886497 -0.622366
|
||||
v 0.160449 -0.886497 -0.802366
|
||||
v 0.160449 -0.886497 -0.622366
|
||||
v 0.160449 -0.126028 -0.622366
|
||||
v 0.160449 -0.126028 -0.802366
|
||||
v -0.026426 -0.126028 -0.622366
|
||||
v -0.026426 -0.126028 -0.802366
|
||||
v 0.160449 -0.426028 -0.802366
|
||||
v 0.160449 -0.426028 -0.622366
|
||||
v 0.160449 -0.126028 -0.802366
|
||||
v 0.160449 -0.126028 -0.622366
|
||||
v -0.026426 -0.426028 -0.802366
|
||||
v -0.026426 -0.426028 -0.622366
|
||||
v 0.160449 -0.426028 -0.802366
|
||||
v 0.160449 -0.426028 -0.622366
|
||||
v 0.160449 -0.584850 -1.107365
|
||||
v -0.026426 -0.584850 -1.107365
|
||||
v 0.160449 -0.884850 -1.107365
|
||||
v -0.026426 -0.884850 -1.107365
|
||||
v 0.160449 -0.127295 -0.622373
|
||||
v -0.026426 -0.127295 -0.622373
|
||||
v 0.160449 -0.427295 -0.622373
|
||||
v -0.026426 -0.427295 -0.622373
|
||||
v 0.160449 -0.584850 -0.622373
|
||||
v -0.026426 -0.584850 -0.622373
|
||||
v 0.160449 -0.884850 -0.622373
|
||||
v -0.026426 -0.884850 -0.622373
|
||||
vt 0.014847 0.403582
|
||||
vt 0.153703 0.694145
|
||||
vt 0.014847 0.694145
|
||||
vt 0.034115 0.694145
|
||||
vt 0.048963 1.000000
|
||||
vt 0.034115 1.000000
|
||||
vt 0.047359 0.372997
|
||||
vt 0.032511 0.067142
|
||||
vt 0.047358 0.067142
|
||||
vt 0.048963 0.694145
|
||||
vt 0.063810 1.000000
|
||||
vt 0.048963 1.000000
|
||||
vt 0.019268 0.694145
|
||||
vt 0.034115 1.000000
|
||||
vt 0.019268 1.000000
|
||||
vt 0.710478 0.701791
|
||||
vt 1.000000 0.694145
|
||||
vt 0.992576 0.701791
|
||||
vt 1.000000 1.000000
|
||||
vt 0.992576 0.992353
|
||||
vt 0.703054 1.000000
|
||||
vt 0.710478 0.992354
|
||||
vt 0.703054 0.694145
|
||||
vt 1.000000 0.051849
|
||||
vt 0.717901 0.357704
|
||||
vt 0.717901 0.051849
|
||||
vt 0.047359 0.082435
|
||||
vt 0.186214 0.372997
|
||||
vt 0.047359 0.372997
|
||||
vt 0.186214 0.082435
|
||||
vt 0.325069 0.372997
|
||||
vt 0.186214 0.372997
|
||||
vt 1.000000 1.000000
|
||||
vt 1.000000 1.000000
|
||||
vt 1.000000 1.000000
|
||||
vt 0.703054 0.694145
|
||||
vt 0.413541 0.701796
|
||||
vt 0.406108 0.694145
|
||||
vt 0.703054 1.000000
|
||||
vt 0.695640 0.701796
|
||||
vt 0.406108 1.000000
|
||||
vt 0.695640 0.992358
|
||||
vt 0.413541 0.992358
|
||||
vt 0.004421 0.885721
|
||||
vt 0.002849 0.709438
|
||||
vt 0.004421 0.823716
|
||||
vt 0.118100 0.779467
|
||||
vt 0.115894 0.721754
|
||||
vt 0.118100 0.726047
|
||||
vt 1.000000 1.000000
|
||||
vt 1.000000 1.000000
|
||||
vt 0.119844 0.783760
|
||||
vt 0.124009 0.890693
|
||||
vt 0.124009 0.890693
|
||||
vt 0.509125 0.003716
|
||||
vt 0.515034 0.113020
|
||||
vt 0.509125 0.113020
|
||||
vt 0.616064 0.113020
|
||||
vt 0.717901 0.000000
|
||||
vt 0.717901 0.113020
|
||||
vt 1.000000 1.000000
|
||||
vt 1.000000 1.000000
|
||||
vt 0.119842 0.995708
|
||||
vt 0.124009 0.890696
|
||||
vt 0.124009 1.000000
|
||||
vt 0.067977 0.995708
|
||||
vt 0.063810 0.890696
|
||||
vt 0.067977 0.890815
|
||||
vt 0.063810 1.000000
|
||||
vt 0.515034 0.113020
|
||||
vt 0.616064 0.000000
|
||||
vt 0.616064 0.113020
|
||||
vt 0.118100 0.721754
|
||||
vt 0.124009 0.783760
|
||||
vt 0.118100 0.783760
|
||||
vt 0.509125 0.113020
|
||||
vt 0.503215 0.003716
|
||||
vt 0.509125 0.003716
|
||||
vt 0.115815 0.777387
|
||||
vt 0.115815 0.723967
|
||||
vt 0.431190 0.196713
|
||||
vt 0.465497 0.082435
|
||||
vt 0.465497 0.372997
|
||||
vt 0.465497 0.113020
|
||||
vt 0.717901 0.372997
|
||||
vt 0.465497 0.372997
|
||||
vt 0.717901 0.372997
|
||||
vt 0.435802 0.678852
|
||||
vt 0.435802 0.372997
|
||||
vt 1.000000 0.372997
|
||||
vt 0.717901 0.678852
|
||||
vt 0.717901 0.372997
|
||||
vt 0.435802 0.372997
|
||||
vt 0.153703 0.678852
|
||||
vt 0.153703 0.372997
|
||||
vt 0.717901 0.678852
|
||||
vt 0.435802 0.694145
|
||||
vt 0.017664 0.372997
|
||||
vt 0.002817 0.113020
|
||||
vt 0.017664 0.113020
|
||||
vt 1.000000 0.357704
|
||||
vt 0.717901 0.372997
|
||||
vt 0.032511 0.372997
|
||||
vt 0.017664 0.372997
|
||||
vt 0.138856 0.724730
|
||||
vt 0.406108 0.709437
|
||||
vt 0.391260 0.724730
|
||||
vt 0.406108 1.000000
|
||||
vt 0.391260 0.984707
|
||||
vt 0.124009 1.000000
|
||||
vt 0.138856 0.984707
|
||||
vt 0.124009 0.709438
|
||||
vt 0.014847 0.694145
|
||||
vt 0.000000 0.434168
|
||||
vt 0.014847 0.434168
|
||||
vt 1.000000 0.678852
|
||||
vt 0.717901 0.694145
|
||||
vt 0.435802 0.678852
|
||||
vt 0.153703 0.694145
|
||||
vt 0.004421 0.740023
|
||||
vt 0.019268 1.000000
|
||||
vt 0.004421 1.000000
|
||||
vt 0.316450 0.067652
|
||||
vt 0.252222 0.005787
|
||||
vt 0.316449 0.005787
|
||||
vt 0.483784 0.067652
|
||||
vt 0.419557 0.005787
|
||||
vt 0.483784 0.005787
|
||||
vt 0.419557 0.067652
|
||||
vt 0.248111 0.067096
|
||||
vt 0.165675 0.015745
|
||||
vt 0.248111 0.015745
|
||||
vt 0.252222 0.005787
|
||||
vt 0.316450 0.067652
|
||||
vt 0.316449 0.005787
|
||||
vt 0.316449 0.005787
|
||||
vt 0.419557 0.067652
|
||||
vt 0.419557 0.005787
|
||||
vt 0.419557 0.005787
|
||||
vt 0.483784 0.067652
|
||||
vt 0.483784 0.005787
|
||||
vt 0.000000 0.000000
|
||||
vt 0.000000 0.000000
|
||||
vt 0.000000 0.000000
|
||||
vt 0.000000 0.000000
|
||||
vt 0.000000 0.000000
|
||||
vt 0.000000 0.000000
|
||||
vt 0.000000 0.000000
|
||||
vt 0.000000 0.000000
|
||||
vt 0.525322 0.836366
|
||||
vt 0.586878 0.919862
|
||||
vt 0.525322 0.919862
|
||||
vt 0.552285 0.749140
|
||||
vt 0.608723 0.836366
|
||||
vt 0.552285 0.836366
|
||||
vt 0.586878 0.894429
|
||||
vt 0.643316 0.836366
|
||||
vt 0.643316 0.894429
|
||||
vt 0.520203 0.749140
|
||||
vt 0.463765 0.836366
|
||||
vt 0.463765 0.749140
|
||||
vt 0.463765 0.836366
|
||||
vt 0.525322 0.928691
|
||||
vt 0.463765 0.928691
|
||||
vt 0.892836 0.808021
|
||||
vt 0.844939 0.752099
|
||||
vt 0.892836 0.752099
|
||||
vt 0.884262 0.808021
|
||||
vt 0.827791 0.912939
|
||||
vt 0.827791 0.808021
|
||||
vt 0.940733 0.808021
|
||||
vt 0.892836 0.752099
|
||||
vt 0.940733 0.752099
|
||||
vt 0.884262 0.912939
|
||||
vt 0.940733 0.808021
|
||||
vt 0.940733 0.912939
|
||||
vt 0.779894 0.808021
|
||||
vt 0.827791 0.912939
|
||||
vt 0.779894 0.912939
|
||||
vt 0.252222 0.005787
|
||||
vt 0.316450 0.067652
|
||||
vt 0.316449 0.005787
|
||||
vt 0.316449 0.005787
|
||||
vt 0.419557 0.067652
|
||||
vt 0.419557 0.005787
|
||||
vt 0.419557 0.005787
|
||||
vt 0.483784 0.067652
|
||||
vt 0.483784 0.005787
|
||||
vt 0.252222 0.005787
|
||||
vt 0.316450 0.067652
|
||||
vt 0.316449 0.005787
|
||||
vt 0.316449 0.005787
|
||||
vt 0.419557 0.067652
|
||||
vt 0.419557 0.005787
|
||||
vt 0.419557 0.005787
|
||||
vt 0.483784 0.067652
|
||||
vt 0.483784 0.005787
|
||||
vt 0.252222 0.005787
|
||||
vt 0.316450 0.067652
|
||||
vt 0.316449 0.005787
|
||||
vt 0.316449 0.005787
|
||||
vt 0.419557 0.067652
|
||||
vt 0.419557 0.005787
|
||||
vt 0.419557 0.005787
|
||||
vt 0.483784 0.067652
|
||||
vt 0.483784 0.005787
|
||||
vt 0.316450 0.067652
|
||||
vt 0.252222 0.005787
|
||||
vt 0.316449 0.005787
|
||||
vt 0.419557 0.067652
|
||||
vt 0.316449 0.005787
|
||||
vt 0.419557 0.005787
|
||||
vt 0.483784 0.067652
|
||||
vt 0.419557 0.005787
|
||||
vt 0.483784 0.005787
|
||||
vt 0.316450 0.067652
|
||||
vt 0.252222 0.005787
|
||||
vt 0.316449 0.005787
|
||||
vt 0.419557 0.067652
|
||||
vt 0.316449 0.005787
|
||||
vt 0.419557 0.005787
|
||||
vt 0.483784 0.067652
|
||||
vt 0.419557 0.005787
|
||||
vt 0.483784 0.005787
|
||||
vt 0.316450 0.067652
|
||||
vt 0.252222 0.005787
|
||||
vt 0.316449 0.005787
|
||||
vt 0.419557 0.067652
|
||||
vt 0.316449 0.005787
|
||||
vt 0.419557 0.005787
|
||||
vt 0.483784 0.067652
|
||||
vt 0.419557 0.005787
|
||||
vt 0.483784 0.005787
|
||||
vt 0.248111 0.067096
|
||||
vt 0.165675 0.015745
|
||||
vt 0.248111 0.015745
|
||||
vt 0.248111 0.067096
|
||||
vt 0.165675 0.015745
|
||||
vt 0.248111 0.015745
|
||||
vt 0.248111 0.067096
|
||||
vt 0.165675 0.015745
|
||||
vt 0.248111 0.015745
|
||||
vt 0.153703 0.403582
|
||||
vt 0.048963 0.694145
|
||||
vt 0.032511 0.372997
|
||||
vt 0.063810 0.694145
|
||||
vt 0.034115 0.694145
|
||||
vt 0.186214 0.082435
|
||||
vt 0.325069 0.082435
|
||||
vt 0.002849 1.000000
|
||||
vt 0.115894 0.783760
|
||||
vt 1.000000 1.000000
|
||||
vt 0.119840 0.890696
|
||||
vt 0.067976 0.890695
|
||||
vt 0.067979 0.783760
|
||||
vt 0.063810 0.890691
|
||||
vt 0.063810 0.890691
|
||||
vt 0.515034 0.003716
|
||||
vt 0.616064 0.000000
|
||||
vt 0.119842 0.891647
|
||||
vt 0.515034 0.000000
|
||||
vt 0.124009 0.721754
|
||||
vt 0.503215 0.113020
|
||||
vt 0.326641 0.372997
|
||||
vt 0.431190 0.258718
|
||||
vt 0.325069 0.258718
|
||||
vt 0.325069 0.196713
|
||||
vt 0.326641 0.082435
|
||||
vt 0.717901 0.113020
|
||||
vt 0.717901 0.694145
|
||||
vt 0.002817 0.372997
|
||||
vt 1.000000 0.372997
|
||||
vt 0.032511 0.113020
|
||||
vt 0.000000 0.694145
|
||||
vt 1.000000 0.694145
|
||||
vt 0.435802 0.694145
|
||||
vt 0.019268 0.740023
|
||||
vt 0.252222 0.067652
|
||||
vt 0.165675 0.067096
|
||||
vt 0.252222 0.067652
|
||||
vt 0.316450 0.067652
|
||||
vt 0.419557 0.067652
|
||||
vt 0.586878 0.836366
|
||||
vt 0.640804 0.749140
|
||||
vt 0.586878 0.836366
|
||||
vt 0.552285 0.836366
|
||||
vt 0.844939 0.808021
|
||||
vt 0.884262 0.912939
|
||||
vt 0.892836 0.808021
|
||||
vt 0.884262 0.808021
|
||||
vt 0.827791 0.808021
|
||||
vt 0.252222 0.067652
|
||||
vt 0.316450 0.067652
|
||||
vt 0.419557 0.067652
|
||||
vt 0.252222 0.067652
|
||||
vt 0.316450 0.067652
|
||||
vt 0.419557 0.067652
|
||||
vt 0.252222 0.067652
|
||||
vt 0.316450 0.067652
|
||||
vt 0.419557 0.067652
|
||||
vt 0.252222 0.067652
|
||||
vt 0.316450 0.067652
|
||||
vt 0.419557 0.067652
|
||||
vt 0.252222 0.067652
|
||||
vt 0.316450 0.067652
|
||||
vt 0.419557 0.067652
|
||||
vt 0.252222 0.067652
|
||||
vt 0.316450 0.067652
|
||||
vt 0.419557 0.067652
|
||||
vt 0.165675 0.067096
|
||||
vt 0.165675 0.067096
|
||||
vt 0.165675 0.067096
|
||||
vn 0.0000 -1.0000 0.0000
|
||||
vn -1.0000 0.0000 0.0000
|
||||
vn 1.0000 0.0000 0.0000
|
||||
vn 0.0000 1.0000 0.0000
|
||||
vn 0.0000 0.0000 -1.0000
|
||||
vn 0.0000 0.0000 1.0000
|
||||
vn 0.0000 0.6720 -0.7406
|
||||
vn 0.6720 0.0000 -0.7406
|
||||
vn 0.0000 -0.6720 -0.7406
|
||||
vn -0.6720 0.0000 -0.7406
|
||||
vn 0.7071 0.0000 -0.7071
|
||||
vn 0.0000 -0.3278 -0.9448
|
||||
vn -0.0178 0.4677 -0.8837
|
||||
vn -0.0123 0.0000 -0.9999
|
||||
vn -0.0201 0.0156 -0.9997
|
||||
vn -0.0109 -0.4677 -0.8838
|
||||
usemtl Material.001
|
||||
s off
|
||||
f 8/1/1 41/2/1 42/3/1
|
||||
f 3/4/2 12/5/2 4/6/2
|
||||
f 2/7/3 10/8/3 1/9/3
|
||||
f 4/10/1 11/11/1 2/12/1
|
||||
f 1/13/4 9/14/4 3/15/4
|
||||
f 7/16/5 10/17/5 45/18/5
|
||||
f 45/18/5 11/19/5 42/20/5
|
||||
f 42/20/5 12/21/5 8/22/5
|
||||
f 8/22/5 9/23/5 7/16/5
|
||||
f 22/24/1 55/25/1 21/26/1
|
||||
f 45/27/4 13/28/4 7/29/4
|
||||
f 7/30/2 14/31/2 8/32/2
|
||||
f 43/33/6 26/34/6 23/35/6
|
||||
f 16/36/7 19/37/7 15/38/7
|
||||
f 17/39/8 20/40/8 16/36/8
|
||||
f 18/41/9 21/42/9 17/39/9
|
||||
f 15/38/10 22/43/10 18/41/10
|
||||
f 47/44/2 42/45/2 44/46/2
|
||||
f 5/47/3 44/48/3 6/49/3
|
||||
f 48/50/6 25/51/6 26/34/6
|
||||
f 27/52/6 25/53/6 47/54/6
|
||||
f 25/55/4 34/56/4 26/57/4
|
||||
f 30/58/1 37/59/1 27/60/1
|
||||
f 44/61/6 23/35/6 24/62/6
|
||||
f 35/63/3 32/64/3 31/65/3
|
||||
f 38/66/3 33/67/3 37/68/3
|
||||
f 35/63/3 34/69/3 38/66/3
|
||||
f 28/70/4 35/71/4 29/72/4
|
||||
f 26/73/5 31/74/5 23/75/5
|
||||
f 23/76/1 32/77/1 24/78/1
|
||||
f 6/49/11 39/79/11 40/80/11
|
||||
f 43/81/3 41/82/3 46/83/3
|
||||
f 51/84/5 49/85/5 50/86/5
|
||||
f 20/87/4 53/88/4 19/89/4
|
||||
f 19/90/2 56/91/2 22/92/2
|
||||
f 21/93/3 54/94/3 20/95/3
|
||||
f 54/96/4 57/97/4 53/88/4
|
||||
f 52/98/3 61/99/3 49/100/3
|
||||
f 56/101/1 59/102/1 55/25/1
|
||||
f 49/100/1 62/103/1 50/104/1
|
||||
f 61/105/5 58/106/5 62/107/5
|
||||
f 62/107/5 59/108/5 63/109/5
|
||||
f 63/109/5 60/110/5 64/111/5
|
||||
f 64/111/5 57/112/5 61/105/5
|
||||
f 50/113/2 63/114/2 51/115/2
|
||||
f 53/116/2 60/117/2 56/91/2
|
||||
f 55/118/3 58/119/3 54/94/3
|
||||
f 51/120/4 64/121/4 52/122/4
|
||||
f 66/123/4 67/124/4 65/125/4
|
||||
f 72/126/1 69/127/1 71/128/1
|
||||
f 70/129/3 65/125/3 69/127/3
|
||||
f 65/130/5 71/131/5 67/132/5
|
||||
f 74/133/1 75/134/1 76/135/1
|
||||
f 80/136/2 77/137/2 78/138/2
|
||||
f 84/139/4 81/140/4 82/141/4
|
||||
f 86/142/4 87/143/4 85/144/4
|
||||
f 88/145/2 91/146/2 87/143/2
|
||||
f 92/147/1 89/148/1 91/146/1
|
||||
f 90/149/3 85/144/3 89/148/3
|
||||
f 88/145/5 90/149/5 92/147/5
|
||||
f 94/150/4 95/151/4 93/152/4
|
||||
f 95/153/2 100/154/2 99/155/2
|
||||
f 100/156/1 97/157/1 99/158/1
|
||||
f 98/159/3 93/160/3 97/161/3
|
||||
f 96/162/12 98/163/12 100/164/12
|
||||
f 102/165/4 103/166/4 101/167/4
|
||||
f 104/168/2 107/169/2 103/170/2
|
||||
f 108/171/1 105/172/1 107/173/1
|
||||
f 106/174/3 101/175/3 105/176/3
|
||||
f 104/177/5 106/178/5 108/179/5
|
||||
f 110/180/1 111/181/1 112/182/1
|
||||
f 116/183/2 113/184/2 114/185/2
|
||||
f 120/186/4 117/187/4 118/188/4
|
||||
f 123/189/1 122/190/1 121/191/1
|
||||
f 128/192/2 125/193/2 126/194/2
|
||||
f 132/195/4 129/196/4 130/197/4
|
||||
f 135/198/1 134/199/1 133/200/1
|
||||
f 140/201/2 137/202/2 138/203/2
|
||||
f 144/204/4 141/205/4 142/206/4
|
||||
f 147/207/4 146/208/4 148/209/4
|
||||
f 149/210/3 152/211/3 150/212/3
|
||||
f 153/213/1 156/214/1 154/215/1
|
||||
f 158/216/4 159/217/4 157/218/4
|
||||
f 161/219/3 164/220/3 162/221/3
|
||||
f 165/222/1 168/223/1 166/224/1
|
||||
f 170/225/4 171/226/4 169/227/4
|
||||
f 173/228/3 176/229/3 174/230/3
|
||||
f 177/231/1 180/232/1 178/233/1
|
||||
f 181/234/5 184/235/5 182/236/5
|
||||
f 185/237/5 188/238/5 186/239/5
|
||||
f 189/240/5 192/241/5 190/242/5
|
||||
f 8/1/1 14/243/1 41/2/1
|
||||
f 3/4/2 9/244/2 12/5/2
|
||||
f 2/7/3 11/245/3 10/8/3
|
||||
f 4/10/1 12/246/1 11/11/1
|
||||
f 1/13/4 10/247/4 9/14/4
|
||||
f 7/16/5 9/23/5 10/17/5
|
||||
f 45/18/5 10/17/5 11/19/5
|
||||
f 42/20/5 11/19/5 12/21/5
|
||||
f 8/22/5 12/21/5 9/23/5
|
||||
f 22/24/1 56/101/1 55/25/1
|
||||
f 45/27/4 46/248/4 13/28/4
|
||||
f 7/30/2 13/249/2 14/31/2
|
||||
f 43/33/6 48/50/6 26/34/6
|
||||
f 16/36/7 20/40/7 19/37/7
|
||||
f 17/39/8 21/42/8 20/40/8
|
||||
f 18/41/9 22/43/9 21/42/9
|
||||
f 15/38/10 19/37/10 22/43/10
|
||||
f 47/44/2 45/250/2 42/45/2
|
||||
f 5/47/3 47/251/3 44/48/3
|
||||
f 48/50/6 47/252/6 25/51/6
|
||||
f 47/54/13 5/253/13 27/52/13
|
||||
f 5/253/14 6/254/14 28/255/14
|
||||
f 44/256/6 24/257/6 28/255/6
|
||||
f 5/253/15 28/255/15 27/52/15
|
||||
f 6/254/16 44/256/16 28/255/16
|
||||
f 25/55/4 33/258/4 34/56/4
|
||||
f 30/58/1 38/259/1 37/59/1
|
||||
f 44/61/6 43/33/6 23/35/6
|
||||
f 35/63/3 36/260/3 32/64/3
|
||||
f 38/66/3 34/69/3 33/67/3
|
||||
f 35/63/3 31/65/3 34/69/3
|
||||
f 28/70/4 36/261/4 35/71/4
|
||||
f 26/73/5 34/262/5 31/74/5
|
||||
f 23/76/1 31/263/1 32/77/1
|
||||
f 6/49/11 5/47/11 39/79/11
|
||||
f 46/83/3 45/264/3 48/265/3
|
||||
f 45/264/3 47/266/3 48/265/3
|
||||
f 44/267/3 42/268/3 43/81/3
|
||||
f 42/268/3 41/82/3 43/81/3
|
||||
f 46/83/3 48/265/3 43/81/3
|
||||
f 51/84/5 52/269/5 49/85/5
|
||||
f 20/87/4 54/96/4 53/88/4
|
||||
f 19/90/2 53/116/2 56/91/2
|
||||
f 21/93/3 55/118/3 54/94/3
|
||||
f 54/96/4 58/270/4 57/97/4
|
||||
f 52/98/3 64/271/3 61/99/3
|
||||
f 56/101/1 60/272/1 59/102/1
|
||||
f 49/100/1 61/273/1 62/103/1
|
||||
f 61/105/5 57/112/5 58/106/5
|
||||
f 62/107/5 58/106/5 59/108/5
|
||||
f 63/109/5 59/108/5 60/110/5
|
||||
f 64/111/5 60/110/5 57/112/5
|
||||
f 50/113/2 62/274/2 63/114/2
|
||||
f 53/116/2 57/275/2 60/117/2
|
||||
f 55/118/3 59/276/3 58/119/3
|
||||
f 51/120/4 63/277/4 64/121/4
|
||||
f 66/123/4 68/278/4 67/124/4
|
||||
f 72/126/1 70/129/1 69/127/1
|
||||
f 70/129/3 66/123/3 65/125/3
|
||||
f 65/130/5 69/279/5 71/131/5
|
||||
f 74/133/1 73/280/1 75/134/1
|
||||
f 80/136/2 79/281/2 77/137/2
|
||||
f 84/139/4 83/282/4 81/140/4
|
||||
f 86/142/4 88/145/4 87/143/4
|
||||
f 88/145/2 92/147/2 91/146/2
|
||||
f 92/147/1 90/149/1 89/148/1
|
||||
f 90/149/3 86/142/3 85/144/3
|
||||
f 88/145/5 86/142/5 90/149/5
|
||||
f 94/150/4 96/283/4 95/151/4
|
||||
f 95/153/2 96/284/2 100/154/2
|
||||
f 100/156/1 98/285/1 97/157/1
|
||||
f 98/159/3 94/286/3 93/160/3
|
||||
f 96/162/12 94/150/12 98/163/12
|
||||
f 102/165/4 104/287/4 103/166/4
|
||||
f 104/168/2 108/288/2 107/169/2
|
||||
f 108/171/1 106/289/1 105/172/1
|
||||
f 106/174/3 102/290/3 101/175/3
|
||||
f 104/177/5 102/291/5 106/178/5
|
||||
f 110/180/1 109/292/1 111/181/1
|
||||
f 116/183/2 115/293/2 113/184/2
|
||||
f 120/186/4 119/294/4 117/187/4
|
||||
f 123/189/1 124/295/1 122/190/1
|
||||
f 128/192/2 127/296/2 125/193/2
|
||||
f 132/195/4 131/297/4 129/196/4
|
||||
f 135/198/1 136/298/1 134/199/1
|
||||
f 140/201/2 139/299/2 137/202/2
|
||||
f 144/204/4 143/300/4 141/205/4
|
||||
f 147/207/4 145/301/4 146/208/4
|
||||
f 149/210/3 151/302/3 152/211/3
|
||||
f 153/213/1 155/303/1 156/214/1
|
||||
f 158/216/4 160/304/4 159/217/4
|
||||
f 161/219/3 163/305/3 164/220/3
|
||||
f 165/222/1 167/306/1 168/223/1
|
||||
f 170/225/4 172/307/4 171/226/4
|
||||
f 173/228/3 175/308/3 176/229/3
|
||||
f 177/231/1 179/309/1 180/232/1
|
||||
f 181/234/5 183/310/5 184/235/5
|
||||
f 185/237/5 187/311/5 188/238/5
|
||||
f 189/240/5 191/312/5 192/241/5
|
||||
|
Before Width: | Height: | Size: 494 KiB |
@ -1,26 +0,0 @@
|
||||
# Blender v2.79 (sub 0) OBJ File: 'unit_building.blend'
|
||||
# www.blender.org
|
||||
v 1.0000 1.000 -0.000
|
||||
v 1.0000 1.0 -0.50
|
||||
v 0.0000 1.00 -0.000
|
||||
v 0.0000 1.000 -0.5
|
||||
v 1.0 0.00 0.000
|
||||
v 1.0 0.00000 -0.5
|
||||
v -0.000081 0.004528 0.004407
|
||||
v -0.000081 0.000101 -0.495573
|
||||
vn 0.0002 1.0000 -0.0089
|
||||
vn -1.0000 0.0002 -0.0000
|
||||
vn -0.0002 -1.0000 0.0089
|
||||
vn 1.0000 -0.0002 0.0000
|
||||
vn -0.0000 -0.0089 -1.0000
|
||||
s off
|
||||
f 2//1 3//1 1//1
|
||||
f 4//2 7//2 3//2
|
||||
f 8//3 5//3 7//3
|
||||
f 6//4 1//4 5//4
|
||||
f 4//5 6//5 8//5
|
||||
f 2//1 4//1 3//1
|
||||
f 4//2 8//2 7//2
|
||||
f 8//3 6//3 5//3
|
||||
f 6//4 2//4 1//4
|
||||
f 4//5 2//5 6//5
|
||||
@ -1,50 +0,0 @@
|
||||
|
||||
|
||||
-- Size of pixel
|
||||
PixelWidth = 2
|
||||
PixelHeight = 2
|
||||
|
||||
-- Size of display window in pixels
|
||||
ScreenWidth = 768
|
||||
ScreenHeight = 480
|
||||
--ScreenWidth = 384
|
||||
--ScreenHeight = 240
|
||||
|
||||
FullScreen = false
|
||||
|
||||
-- Default city parameters
|
||||
DefaultMapWidth = 64
|
||||
DefaultMapHeight = 32
|
||||
--DefaultCityFile = "assets/cities/example1.city"
|
||||
|
||||
|
||||
-- Textures used by various game systems
|
||||
Textures = {}
|
||||
Textures[1] = {"Grass", "assets/system/grass1.png"}
|
||||
Textures[2] = {"AllRoads", "assets/system/roads4.png"}
|
||||
Textures[3] = {"Water", "assets/system/water1.png"}
|
||||
Textures[4] = {"Clouds", "assets/system/clouds2.png"}
|
||||
Textures[5] = {"WaterSide", "assets/system/waterside1.png"}
|
||||
Textures[6] = {"Smoke", "assets/system/skidsmoke1.png"}
|
||||
|
||||
-- Buildings
|
||||
Buildings = {}
|
||||
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}
|
||||
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}
|
||||
|
||||
Vehicles = {}
|
||||
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}
|
||||
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}
|
||||
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}
|
||||
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}
|
||||
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}
|
||||
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}
|
||||
|
||||
|
Before Width: | Height: | Size: 181 KiB |
|
Before Width: | Height: | Size: 284 KiB |
|
Before Width: | Height: | Size: 181 KiB |
|
Before Width: | Height: | Size: 482 KiB |
|
Before Width: | Height: | Size: 383 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 691 KiB |
|
Before Width: | Height: | Size: 732 KiB |
|
Before Width: | Height: | Size: 616 KiB |
|
Before Width: | Height: | Size: 99 KiB |
|
Before Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 3.6 KiB |
@ -1,121 +0,0 @@
|
||||
# Blender v2.69 (sub 0) OBJ File: 'Car002.blend'
|
||||
# www.blender.org
|
||||
v 0.600000 -0.400000 -1.700000
|
||||
v 0.600000 1.700000 -1.700000
|
||||
v -0.600000 -0.400000 -1.700000
|
||||
v -0.600000 1.700000 -1.700000
|
||||
v -0.800000 2.000000 -1.100000
|
||||
v 0.800000 2.000000 -1.100000
|
||||
v -0.800000 2.000000 -1.100000
|
||||
v 0.800000 2.000000 -1.100000
|
||||
v -0.800000 -1.000000 -0.950000
|
||||
v 0.800000 -1.000000 -0.950000
|
||||
v -0.800000 -2.000000 -0.900000
|
||||
v 0.800000 -2.000000 -0.900000
|
||||
v -0.800000 2.000000 -0.400000
|
||||
v 0.800000 2.000000 -0.400000
|
||||
v -0.800000 -2.000000 -0.400000
|
||||
v 0.800000 -2.000000 -0.400000
|
||||
v 0.800000 -2.000000 -0.400000
|
||||
v 0.800000 2.000000 -0.400000
|
||||
v 0.800000 -2.000000 -0.900000
|
||||
v 0.800000 2.000000 -1.100000
|
||||
v -0.800000 -2.000000 -0.900000
|
||||
v -0.800000 2.000000 -1.100000
|
||||
v -0.800000 -2.000000 -0.400000
|
||||
v -0.800000 2.000000 -0.400000
|
||||
v 0.780000 1.200000 -0.400000
|
||||
v 0.780000 1.500000 -0.400000
|
||||
v 0.780000 1.400000 0.000000
|
||||
v 0.780000 1.000000 0.000000
|
||||
v 0.780000 0.900000 -0.400000
|
||||
v 0.780000 -1.100000 -0.400000
|
||||
v 0.780000 -0.800000 -0.400000
|
||||
v 0.780000 -0.900000 0.000000
|
||||
v 0.780000 -1.300000 0.000000
|
||||
v 0.780000 -1.400000 -0.400000
|
||||
v -0.780000 -1.100000 -0.400000
|
||||
v -0.780000 -1.400000 -0.400000
|
||||
v -0.780000 -1.300000 0.000000
|
||||
v -0.780000 -0.900000 0.000000
|
||||
v -0.780000 -0.800000 -0.400000
|
||||
v -0.780000 1.200000 -0.400000
|
||||
v -0.780000 0.900000 -0.400000
|
||||
v -0.780000 1.000000 0.000000
|
||||
v -0.780000 1.400000 0.000000
|
||||
v -0.780000 1.500000 -0.400000
|
||||
vt 0.564792 0.689981
|
||||
vt 0.564792 0.953949
|
||||
vt 0.338554 0.953949
|
||||
vt 0.338554 0.689981
|
||||
vt 0.334296 0.692438
|
||||
vt 0.334296 0.954445
|
||||
vt 0.203294 0.986197
|
||||
vt 0.203294 0.656186
|
||||
vt 0.695616 0.984433
|
||||
vt 0.572654 0.959219
|
||||
vt 0.695616 0.657719
|
||||
vt 0.572795 0.683358
|
||||
vt 0.941945 0.999442
|
||||
vt 0.694306 0.999442
|
||||
vt 0.941945 0.647349
|
||||
vt 0.694306 0.647349
|
||||
vt 0.070499 0.643570
|
||||
vt 0.070499 0.998147
|
||||
vt 0.003817 0.998147
|
||||
vt 0.003817 0.643570
|
||||
vt 0.997239 0.998410
|
||||
vt 0.940308 0.998410
|
||||
vt 0.997239 0.647262
|
||||
vt 0.940308 0.647262
|
||||
vt 0.331416 0.690927
|
||||
vt 0.238667 0.577426
|
||||
vt 0.563274 0.690927
|
||||
vt 0.735343 0.574985
|
||||
vt 0.970266 0.522520
|
||||
vt 0.962942 0.572543
|
||||
vt 0.072079 0.572543
|
||||
vt 0.079403 0.522520
|
||||
vt 0.744362 0.446044
|
||||
vt 0.780351 0.385565
|
||||
vt 0.817830 0.446044
|
||||
vt 0.854309 0.385565
|
||||
vt 0.891299 0.446044
|
||||
vt 0.743883 0.446331
|
||||
vt 0.779872 0.385852
|
||||
vt 0.817351 0.446331
|
||||
vt 0.853830 0.385852
|
||||
vt 0.890820 0.446331
|
||||
s off
|
||||
f 1/1 3/2 4/3
|
||||
f 2/4 1/1 4/3
|
||||
f 2/5 4/6 5/7
|
||||
f 5/7 6/8 2/5
|
||||
f 9/9 3/10 10/11
|
||||
f 3/10 1/12 10/11
|
||||
f 11/13 9/14 12/15
|
||||
f 10/16 12/15 9/14
|
||||
f 8/17 7/18 13/19
|
||||
f 13/19 14/20 8/17
|
||||
f 15/21 11/22 16/23
|
||||
f 12/24 16/23 11/22
|
||||
f 2/25 6/26 1/27
|
||||
f 10/28 1/27 6/26
|
||||
f 4/25 3/27 5/26
|
||||
f 9/28 5/26 3/27
|
||||
f 17/29 19/30 20/31
|
||||
f 18/32 17/29 20/31
|
||||
f 21/30 23/29 24/32
|
||||
f 22/31 21/30 24/32
|
||||
f 26/33 27/34 25/35
|
||||
f 27/34 28/36 25/35
|
||||
f 28/36 29/37 25/35
|
||||
f 31/38 32/39 30/40
|
||||
f 32/39 33/41 30/40
|
||||
f 33/41 34/42 30/40
|
||||
f 36/42 37/41 35/40
|
||||
f 37/41 38/39 35/40
|
||||
f 38/39 39/38 35/40
|
||||
f 41/37 42/36 40/35
|
||||
f 42/36 43/34 40/35
|
||||
f 43/34 44/33 40/35
|
||||
@ -1,127 +0,0 @@
|
||||
# Blender v2.69 (sub 0) OBJ File: 'Car002.blend'
|
||||
# www.blender.org
|
||||
v 0.600000 -0.200000 -1.100000
|
||||
v 0.600000 1.000000 -1.100000
|
||||
v -0.600000 -0.200000 -1.100000
|
||||
v -0.600000 1.000000 -1.100000
|
||||
v -0.800000 1.400000 -0.700000
|
||||
v 0.800000 1.400000 -0.700000
|
||||
v -0.800000 2.000000 -0.700000
|
||||
v 0.800000 2.000000 -0.700000
|
||||
v -0.800000 -1.000000 -0.700000
|
||||
v 0.800000 -1.000000 -0.700000
|
||||
v -0.800000 -2.000000 -0.700000
|
||||
v 0.800000 -2.000000 -0.700000
|
||||
v -0.800000 2.000000 -0.300000
|
||||
v 0.800000 2.000000 -0.300000
|
||||
v -0.800000 -2.000000 -0.300000
|
||||
v 0.800000 -2.000000 -0.300000
|
||||
v 0.800000 -2.000000 -0.300000
|
||||
v 0.800000 2.000000 -0.300000
|
||||
v 0.800000 -2.000000 -0.700000
|
||||
v 0.800000 2.000000 -0.700000
|
||||
v -0.800000 -2.000000 -0.700000
|
||||
v -0.800000 2.000000 -0.700000
|
||||
v -0.800000 -2.000000 -0.300000
|
||||
v -0.800000 2.000000 -0.300000
|
||||
v 0.780000 1.200000 -0.300000
|
||||
v 0.780000 1.500000 -0.300000
|
||||
v 0.780000 1.400000 0.000000
|
||||
v 0.780000 1.000000 0.000000
|
||||
v 0.780000 0.900000 -0.300000
|
||||
v 0.780000 -1.100000 -0.300000
|
||||
v 0.780000 -0.800000 -0.300000
|
||||
v 0.780000 -0.900000 0.000000
|
||||
v 0.780000 -1.300000 0.000000
|
||||
v 0.780000 -1.400000 -0.300000
|
||||
v -0.780000 -1.100000 -0.300000
|
||||
v -0.780000 -1.400000 -0.300000
|
||||
v -0.780000 -1.300000 0.000000
|
||||
v -0.780000 -0.900000 0.000000
|
||||
v -0.780000 -0.800000 -0.300000
|
||||
v -0.780000 1.200000 -0.300000
|
||||
v -0.780000 0.900000 -0.300000
|
||||
v -0.780000 1.000000 0.000000
|
||||
v -0.780000 1.400000 0.000000
|
||||
v -0.780000 1.500000 -0.300000
|
||||
vt 0.564792 0.689981
|
||||
vt 0.564792 0.953949
|
||||
vt 0.338554 0.953949
|
||||
vt 0.338554 0.689981
|
||||
vt 0.334296 0.692438
|
||||
vt 0.334296 0.954445
|
||||
vt 0.203294 0.986197
|
||||
vt 0.203294 0.656186
|
||||
vt 0.203326 0.657931
|
||||
vt 0.203326 0.986286
|
||||
vt 0.080997 0.986286
|
||||
vt 0.080997 0.657931
|
||||
vt 0.695616 0.984433
|
||||
vt 0.572654 0.959219
|
||||
vt 0.695616 0.657719
|
||||
vt 0.572795 0.683358
|
||||
vt 0.941945 0.999442
|
||||
vt 0.694306 0.999442
|
||||
vt 0.941945 0.647349
|
||||
vt 0.694306 0.647349
|
||||
vt 0.070499 0.643570
|
||||
vt 0.070499 0.998147
|
||||
vt 0.003817 0.998147
|
||||
vt 0.003817 0.643570
|
||||
vt 0.997239 0.998410
|
||||
vt 0.940308 0.998410
|
||||
vt 0.997239 0.647262
|
||||
vt 0.940308 0.647262
|
||||
vt 0.331416 0.690927
|
||||
vt 0.238667 0.577426
|
||||
vt 0.563274 0.690927
|
||||
vt 0.735343 0.574985
|
||||
vt 0.970266 0.522520
|
||||
vt 0.962942 0.572543
|
||||
vt 0.072079 0.572543
|
||||
vt 0.079403 0.522520
|
||||
vt 0.744362 0.446044
|
||||
vt 0.780351 0.385565
|
||||
vt 0.817830 0.446044
|
||||
vt 0.854309 0.385565
|
||||
vt 0.891299 0.446044
|
||||
vt 0.743883 0.446331
|
||||
vt 0.779872 0.385852
|
||||
vt 0.817351 0.446331
|
||||
vt 0.853830 0.385852
|
||||
vt 0.890820 0.446331
|
||||
s off
|
||||
f 1/1 3/2 4/3
|
||||
f 2/4 1/1 4/3
|
||||
f 2/5 4/6 5/7
|
||||
f 5/7 6/8 2/5
|
||||
f 6/9 5/10 7/11
|
||||
f 7/11 8/12 6/9
|
||||
f 9/13 3/14 10/15
|
||||
f 3/14 1/16 10/15
|
||||
f 11/17 9/18 12/19
|
||||
f 10/20 12/19 9/18
|
||||
f 8/21 7/22 13/23
|
||||
f 13/23 14/24 8/21
|
||||
f 15/25 11/26 16/27
|
||||
f 12/28 16/27 11/26
|
||||
f 2/29 6/30 1/31
|
||||
f 10/32 1/31 6/30
|
||||
f 4/29 3/31 5/30
|
||||
f 9/32 5/30 3/31
|
||||
f 17/33 19/34 20/35
|
||||
f 18/36 17/33 20/35
|
||||
f 21/34 23/33 24/36
|
||||
f 22/35 21/34 24/36
|
||||
f 26/37 27/38 25/39
|
||||
f 27/38 28/40 25/39
|
||||
f 28/40 29/41 25/39
|
||||
f 31/42 32/43 30/44
|
||||
f 32/43 33/45 30/44
|
||||
f 33/45 34/46 30/44
|
||||
f 36/46 37/45 35/44
|
||||
f 37/45 38/43 35/44
|
||||
f 38/43 39/42 35/44
|
||||
f 41/41 42/40 40/39
|
||||
f 42/40 43/38 40/39
|
||||
f 43/38 44/37 40/39
|
||||
@ -1,137 +0,0 @@
|
||||
# Blender v2.69 (sub 0) OBJ File: 'Car002.blend'
|
||||
# www.blender.org
|
||||
v 0.600000 -1.900000 -1.500000
|
||||
v 0.600000 -1.000000 -1.500000
|
||||
v -0.600000 -1.900000 -1.500000
|
||||
v -0.600000 -1.000000 -1.500000
|
||||
v -0.800000 -0.900000 -0.700000
|
||||
v 0.800000 -0.900000 -0.700000
|
||||
v -0.800000 0.600000 -0.700000
|
||||
v 0.800000 0.600000 -0.700000
|
||||
v -0.800000 -2.400000 -0.700000
|
||||
v 0.800000 -2.400000 -0.700000
|
||||
v -0.800000 -2.400000 -0.700000
|
||||
v 0.800000 -2.400000 -0.700000
|
||||
v -0.800000 0.600000 -0.300000
|
||||
v 0.800000 0.600000 -0.300000
|
||||
v -0.800000 -2.400000 -0.300000
|
||||
v 0.800000 -2.400000 -0.300000
|
||||
v 0.800000 -2.400000 -0.300000
|
||||
v 0.800000 0.600000 -0.300000
|
||||
v 0.800000 -2.400000 -0.700000
|
||||
v 0.800000 0.600000 -0.700000
|
||||
v -0.800000 -2.400000 -0.700000
|
||||
v -0.800000 0.600000 -0.700000
|
||||
v -0.800000 -2.400000 -0.300000
|
||||
v -0.800000 0.600000 -0.300000
|
||||
v 0.780000 0.100000 -0.300000
|
||||
v 0.780000 0.400000 -0.300000
|
||||
v 0.780000 0.300000 0.000000
|
||||
v 0.780000 -0.100000 0.000000
|
||||
v 0.780000 -0.200000 -0.300000
|
||||
v 0.780000 -1.900000 -0.300000
|
||||
v 0.780000 -1.600000 -0.300000
|
||||
v 0.780000 -1.700000 0.000000
|
||||
v 0.780000 -2.100000 0.000000
|
||||
v 0.780000 -2.200000 -0.300000
|
||||
v -0.780000 -1.900000 -0.300000
|
||||
v -0.780000 -2.200000 -0.300000
|
||||
v -0.780000 -2.100000 0.000000
|
||||
v -0.780000 -1.700000 0.000000
|
||||
v -0.780000 -1.600000 -0.300000
|
||||
v -0.780000 0.100000 -0.300000
|
||||
v -0.780000 -0.200000 -0.300000
|
||||
v -0.780000 -0.100000 0.000000
|
||||
v -0.780000 0.300000 0.000000
|
||||
v -0.780000 0.400000 -0.300000
|
||||
v 0.780000 -0.600000 -0.300000
|
||||
v 0.780000 -0.300000 -0.300000
|
||||
v 0.780000 -0.400000 0.000000
|
||||
v 0.780000 -0.800000 0.000000
|
||||
v 0.780000 -0.900000 -0.300000
|
||||
v -0.780000 -0.600000 -0.300000
|
||||
v -0.780000 -0.900000 -0.300000
|
||||
v -0.780000 -0.800000 0.000000
|
||||
v -0.780000 -0.400000 0.000000
|
||||
v -0.780000 -0.300000 -0.300000
|
||||
vt 0.564792 0.689981
|
||||
vt 0.564792 0.953949
|
||||
vt 0.338554 0.953949
|
||||
vt 0.338554 0.689981
|
||||
vt 0.334296 0.692438
|
||||
vt 0.334296 0.954445
|
||||
vt 0.203294 0.986197
|
||||
vt 0.203294 0.656186
|
||||
vt 0.695616 0.984433
|
||||
vt 0.572654 0.959219
|
||||
vt 0.695616 0.657719
|
||||
vt 0.572795 0.683358
|
||||
vt 0.070499 0.643570
|
||||
vt 0.070499 0.998147
|
||||
vt 0.003817 0.998147
|
||||
vt 0.003817 0.643570
|
||||
vt 0.997239 0.998410
|
||||
vt 0.940308 0.998410
|
||||
vt 0.997239 0.647262
|
||||
vt 0.940308 0.647262
|
||||
vt 0.471796 0.690927
|
||||
vt 0.487691 0.573764
|
||||
vt 0.563274 0.690927
|
||||
vt 0.735343 0.574985
|
||||
vt 0.970266 0.522520
|
||||
vt 0.962942 0.572543
|
||||
vt 0.072079 0.572543
|
||||
vt 0.079403 0.522520
|
||||
vt 0.744362 0.446044
|
||||
vt 0.780351 0.385565
|
||||
vt 0.817830 0.446044
|
||||
vt 0.854309 0.385565
|
||||
vt 0.891299 0.446044
|
||||
vt 0.743883 0.446331
|
||||
vt 0.779872 0.385852
|
||||
vt 0.817351 0.446331
|
||||
vt 0.853830 0.385852
|
||||
vt 0.890820 0.446331
|
||||
vt 0.122341 0.504785
|
||||
vt 0.003867 0.504785
|
||||
vt 0.122341 0.378413
|
||||
vt 0.003867 0.378413
|
||||
s off
|
||||
f 1/1 3/2 4/3
|
||||
f 2/4 1/1 4/3
|
||||
f 2/5 4/6 5/7
|
||||
f 5/7 6/8 2/5
|
||||
f 9/9 3/10 10/11
|
||||
f 3/10 1/12 10/11
|
||||
f 8/13 7/14 13/15
|
||||
f 13/15 14/16 8/13
|
||||
f 15/17 11/18 16/19
|
||||
f 12/20 16/19 11/18
|
||||
f 2/21 6/22 1/23
|
||||
f 10/24 1/23 6/22
|
||||
f 4/21 3/23 5/22
|
||||
f 9/24 5/22 3/23
|
||||
f 17/25 19/26 20/27
|
||||
f 18/28 17/25 20/27
|
||||
f 21/26 23/25 24/28
|
||||
f 22/27 21/26 24/28
|
||||
f 26/29 27/30 25/31
|
||||
f 27/30 28/32 25/31
|
||||
f 28/32 29/33 25/31
|
||||
f 31/34 32/35 30/36
|
||||
f 32/35 33/37 30/36
|
||||
f 33/37 34/38 30/36
|
||||
f 36/38 37/37 35/36
|
||||
f 37/37 38/35 35/36
|
||||
f 38/35 39/34 35/36
|
||||
f 41/33 42/32 40/31
|
||||
f 42/32 43/30 40/31
|
||||
f 43/30 44/29 40/31
|
||||
f 5/39 7/40 6/41
|
||||
f 7/40 8/42 6/41
|
||||
f 46/29 47/30 45/31
|
||||
f 47/30 48/32 45/31
|
||||
f 48/32 49/33 45/31
|
||||
f 51/33 52/32 50/31
|
||||
f 52/32 53/30 50/31
|
||||
f 53/30 54/29 50/31
|
||||
@ -1,106 +0,0 @@
|
||||
# Blender v2.69 (sub 0) OBJ File: 'Car002.blend'
|
||||
# www.blender.org
|
||||
v -0.800000 2.900000 -0.700000
|
||||
v 0.800000 2.900000 -0.700000
|
||||
v -0.800000 1.200000 -0.700000
|
||||
v 0.800000 1.200000 -0.700000
|
||||
v -0.800000 2.900000 -0.300000
|
||||
v 0.800000 2.900000 -0.300000
|
||||
v -0.800000 1.200000 -0.300000
|
||||
v 0.800000 1.200000 -0.300000
|
||||
v 0.800000 1.200000 -0.300000
|
||||
v 0.800000 2.900000 -0.300000
|
||||
v 0.800000 1.200000 -0.700000
|
||||
v 0.800000 2.900000 -0.700000
|
||||
v -0.800000 1.200000 -0.700000
|
||||
v -0.800000 2.900000 -0.700000
|
||||
v -0.800000 1.200000 -0.300000
|
||||
v -0.800000 2.900000 -0.300000
|
||||
v 0.780000 2.400000 -0.300000
|
||||
v 0.780000 2.700000 -0.300000
|
||||
v 0.780000 2.600000 0.000000
|
||||
v 0.780000 2.200000 0.000000
|
||||
v 0.780000 2.100000 -0.300000
|
||||
v -0.780000 2.400000 -0.300000
|
||||
v -0.780000 2.100000 -0.300000
|
||||
v -0.780000 2.200000 0.000000
|
||||
v -0.780000 2.600000 0.000000
|
||||
v -0.780000 2.700000 -0.300000
|
||||
v 0.780000 1.700000 -0.300000
|
||||
v 0.780000 2.000000 -0.300000
|
||||
v 0.780000 1.900000 0.000000
|
||||
v 0.780000 1.500000 0.000000
|
||||
v 0.780000 1.400000 -0.300000
|
||||
v -0.780000 1.700000 -0.300000
|
||||
v -0.780000 1.400000 -0.300000
|
||||
v -0.780000 1.500000 0.000000
|
||||
v -0.780000 1.900000 0.000000
|
||||
v -0.780000 2.000000 -0.300000
|
||||
v 0.800000 -0.600000 -2.000000
|
||||
v 0.800000 2.900000 -2.000000
|
||||
v -0.800000 -0.600000 -2.000000
|
||||
v -0.800000 2.900000 -2.000000
|
||||
v 0.800000 -0.600000 -0.800000
|
||||
v 0.800000 2.900000 -0.800000
|
||||
v -0.800000 -0.600000 -0.800000
|
||||
v -0.800000 2.900000 -0.800000
|
||||
vt 0.070499 0.643570
|
||||
vt 0.070499 0.998147
|
||||
vt 0.003817 0.998147
|
||||
vt 0.003817 0.643570
|
||||
vt 0.997239 0.998410
|
||||
vt 0.940308 0.998410
|
||||
vt 0.997239 0.647262
|
||||
vt 0.940308 0.647262
|
||||
vt 0.970266 0.522520
|
||||
vt 0.962942 0.572543
|
||||
vt 0.072079 0.572543
|
||||
vt 0.079403 0.522520
|
||||
vt 0.744362 0.446044
|
||||
vt 0.780351 0.385565
|
||||
vt 0.817830 0.446044
|
||||
vt 0.854309 0.385565
|
||||
vt 0.891299 0.446044
|
||||
vt 0.880027 0.325855
|
||||
vt 0.591008 0.325855
|
||||
vt 0.880027 0.000323
|
||||
vt 0.589446 0.324644
|
||||
vt 0.001453 0.324644
|
||||
vt 0.589446 0.001209
|
||||
vt 0.584620 0.002788
|
||||
vt 0.584620 0.324497
|
||||
vt 0.001010 0.324497
|
||||
vt 0.001010 0.002788
|
||||
vt 0.591008 0.000323
|
||||
vt 0.001453 0.001209
|
||||
s off
|
||||
f 2/1 1/2 5/3
|
||||
f 5/3 6/4 2/1
|
||||
f 7/5 3/6 8/7
|
||||
f 4/8 8/7 3/6
|
||||
f 9/9 11/10 12/11
|
||||
f 10/12 9/9 12/11
|
||||
f 13/10 15/9 16/12
|
||||
f 14/11 13/10 16/12
|
||||
f 18/13 19/14 17/15
|
||||
f 19/14 20/16 17/15
|
||||
f 20/16 21/17 17/15
|
||||
f 23/17 24/16 22/15
|
||||
f 24/16 25/14 22/15
|
||||
f 25/14 26/13 22/15
|
||||
f 39/18 37/19 43/20
|
||||
f 37/21 38/22 41/23
|
||||
f 28/13 29/14 27/15
|
||||
f 29/14 30/16 27/15
|
||||
f 30/16 31/17 27/15
|
||||
f 33/17 34/16 32/15
|
||||
f 34/16 35/14 32/15
|
||||
f 35/14 36/13 32/15
|
||||
f 37/24 39/25 40/26
|
||||
f 38/27 37/24 40/26
|
||||
f 37/19 41/28 43/20
|
||||
f 40/18 44/20 42/28
|
||||
f 39/21 43/23 44/29
|
||||
f 38/22 42/29 41/23
|
||||
f 38/19 40/18 42/28
|
||||
f 40/22 39/21 44/29
|
||||
@ -1,127 +0,0 @@
|
||||
# Blender v2.69 (sub 0) OBJ File: 'Car002.blend'
|
||||
# www.blender.org
|
||||
v 0.600000 -0.200000 -1.100000
|
||||
v 0.600000 0.200000 -1.100000
|
||||
v -0.600000 -0.200000 -1.100000
|
||||
v -0.600000 0.200000 -1.100000
|
||||
v -0.800000 0.300000 -0.700000
|
||||
v 0.800000 0.300000 -0.700000
|
||||
v -0.800000 2.000000 -0.700000
|
||||
v 0.800000 2.000000 -0.700000
|
||||
v -0.800000 -1.000000 -0.700000
|
||||
v 0.800000 -1.000000 -0.700000
|
||||
v -0.800000 -2.000000 -0.700000
|
||||
v 0.800000 -2.000000 -0.700000
|
||||
v -0.800000 2.000000 -0.300000
|
||||
v 0.800000 2.000000 -0.300000
|
||||
v -0.800000 -2.000000 -0.300000
|
||||
v 0.800000 -2.000000 -0.300000
|
||||
v 0.800000 -2.000000 -0.300000
|
||||
v 0.800000 2.000000 -0.300000
|
||||
v 0.800000 -2.000000 -0.700000
|
||||
v 0.800000 2.000000 -0.700000
|
||||
v -0.800000 -2.000000 -0.700000
|
||||
v -0.800000 2.000000 -0.700000
|
||||
v -0.800000 -2.000000 -0.300000
|
||||
v -0.800000 2.000000 -0.300000
|
||||
v 0.780000 1.200000 -0.300000
|
||||
v 0.780000 1.500000 -0.300000
|
||||
v 0.780000 1.400000 0.000000
|
||||
v 0.780000 1.000000 0.000000
|
||||
v 0.780000 0.900000 -0.300000
|
||||
v 0.780000 -1.100000 -0.300000
|
||||
v 0.780000 -0.800000 -0.300000
|
||||
v 0.780000 -0.900000 0.000000
|
||||
v 0.780000 -1.300000 0.000000
|
||||
v 0.780000 -1.400000 -0.300000
|
||||
v -0.780000 -1.100000 -0.300000
|
||||
v -0.780000 -1.400000 -0.300000
|
||||
v -0.780000 -1.300000 0.000000
|
||||
v -0.780000 -0.900000 0.000000
|
||||
v -0.780000 -0.800000 -0.300000
|
||||
v -0.780000 1.200000 -0.300000
|
||||
v -0.780000 0.900000 -0.300000
|
||||
v -0.780000 1.000000 0.000000
|
||||
v -0.780000 1.400000 0.000000
|
||||
v -0.780000 1.500000 -0.300000
|
||||
vt 0.564792 0.689981
|
||||
vt 0.564792 0.953949
|
||||
vt 0.338554 0.953949
|
||||
vt 0.338554 0.689981
|
||||
vt 0.334296 0.692438
|
||||
vt 0.334296 0.954445
|
||||
vt 0.203294 0.986197
|
||||
vt 0.203294 0.656186
|
||||
vt 0.695616 0.984433
|
||||
vt 0.572654 0.959219
|
||||
vt 0.695616 0.657719
|
||||
vt 0.572795 0.683358
|
||||
vt 0.941945 0.999442
|
||||
vt 0.694306 0.999442
|
||||
vt 0.941945 0.647349
|
||||
vt 0.694306 0.647349
|
||||
vt 0.070499 0.643570
|
||||
vt 0.070499 0.998147
|
||||
vt 0.003817 0.998147
|
||||
vt 0.003817 0.643570
|
||||
vt 0.997239 0.998410
|
||||
vt 0.940308 0.998410
|
||||
vt 0.997239 0.647262
|
||||
vt 0.940308 0.647262
|
||||
vt 0.471796 0.690927
|
||||
vt 0.487691 0.573764
|
||||
vt 0.563274 0.690927
|
||||
vt 0.735343 0.574985
|
||||
vt 0.970266 0.522520
|
||||
vt 0.962942 0.572543
|
||||
vt 0.072079 0.572543
|
||||
vt 0.079403 0.522520
|
||||
vt 0.744362 0.446044
|
||||
vt 0.780351 0.385565
|
||||
vt 0.817830 0.446044
|
||||
vt 0.854309 0.385565
|
||||
vt 0.891299 0.446044
|
||||
vt 0.743883 0.446331
|
||||
vt 0.779872 0.385852
|
||||
vt 0.817351 0.446331
|
||||
vt 0.853830 0.385852
|
||||
vt 0.890820 0.446331
|
||||
vt 0.122341 0.504785
|
||||
vt 0.003867 0.504785
|
||||
vt 0.122341 0.378413
|
||||
vt 0.003867 0.378413
|
||||
s off
|
||||
f 1/1 3/2 4/3
|
||||
f 2/4 1/1 4/3
|
||||
f 2/5 4/6 5/7
|
||||
f 5/7 6/8 2/5
|
||||
f 9/9 3/10 10/11
|
||||
f 3/10 1/12 10/11
|
||||
f 11/13 9/14 12/15
|
||||
f 10/16 12/15 9/14
|
||||
f 8/17 7/18 13/19
|
||||
f 13/19 14/20 8/17
|
||||
f 15/21 11/22 16/23
|
||||
f 12/24 16/23 11/22
|
||||
f 2/25 6/26 1/27
|
||||
f 10/28 1/27 6/26
|
||||
f 4/25 3/27 5/26
|
||||
f 9/28 5/26 3/27
|
||||
f 17/29 19/30 20/31
|
||||
f 18/32 17/29 20/31
|
||||
f 21/30 23/29 24/32
|
||||
f 22/31 21/30 24/32
|
||||
f 26/33 27/34 25/35
|
||||
f 27/34 28/36 25/35
|
||||
f 28/36 29/37 25/35
|
||||
f 31/38 32/39 30/40
|
||||
f 32/39 33/41 30/40
|
||||
f 33/41 34/42 30/40
|
||||
f 36/42 37/41 35/40
|
||||
f 37/41 38/39 35/40
|
||||
f 38/39 39/38 35/40
|
||||
f 41/37 42/36 40/35
|
||||
f 42/36 43/34 40/35
|
||||
f 43/34 44/33 40/35
|
||||
f 5/43 7/44 6/45
|
||||
f 7/44 8/46 6/45
|
||||
@ -1,121 +0,0 @@
|
||||
# Blender v2.69 (sub 0) OBJ File: 'Car002.blend'
|
||||
# www.blender.org
|
||||
v 0.600000 -0.200000 -1.100000
|
||||
v 0.600000 1.600000 -1.100000
|
||||
v -0.600000 -0.200000 -1.100000
|
||||
v -0.600000 1.600000 -1.100000
|
||||
v -0.800000 2.000000 -0.700000
|
||||
v 0.800000 2.000000 -0.700000
|
||||
v -0.800000 2.000000 -0.700000
|
||||
v 0.800000 2.000000 -0.700000
|
||||
v -0.800000 -1.000000 -0.700000
|
||||
v 0.800000 -1.000000 -0.700000
|
||||
v -0.800000 -2.000000 -0.700000
|
||||
v 0.800000 -2.000000 -0.700000
|
||||
v -0.800000 2.000000 -0.300000
|
||||
v 0.800000 2.000000 -0.300000
|
||||
v -0.800000 -2.000000 -0.300000
|
||||
v 0.800000 -2.000000 -0.300000
|
||||
v 0.800000 -2.000000 -0.300000
|
||||
v 0.800000 2.000000 -0.300000
|
||||
v 0.800000 -2.000000 -0.700000
|
||||
v 0.800000 2.000000 -0.700000
|
||||
v -0.800000 -2.000000 -0.700000
|
||||
v -0.800000 2.000000 -0.700000
|
||||
v -0.800000 -2.000000 -0.300000
|
||||
v -0.800000 2.000000 -0.300000
|
||||
v 0.780000 1.200000 -0.300000
|
||||
v 0.780000 1.500000 -0.300000
|
||||
v 0.780000 1.400000 0.000000
|
||||
v 0.780000 1.000000 0.000000
|
||||
v 0.780000 0.900000 -0.300000
|
||||
v 0.780000 -1.100000 -0.300000
|
||||
v 0.780000 -0.800000 -0.300000
|
||||
v 0.780000 -0.900000 0.000000
|
||||
v 0.780000 -1.300000 0.000000
|
||||
v 0.780000 -1.400000 -0.300000
|
||||
v -0.780000 -1.100000 -0.300000
|
||||
v -0.780000 -1.400000 -0.300000
|
||||
v -0.780000 -1.300000 0.000000
|
||||
v -0.780000 -0.900000 0.000000
|
||||
v -0.780000 -0.800000 -0.300000
|
||||
v -0.780000 1.200000 -0.300000
|
||||
v -0.780000 0.900000 -0.300000
|
||||
v -0.780000 1.000000 0.000000
|
||||
v -0.780000 1.400000 0.000000
|
||||
v -0.780000 1.500000 -0.300000
|
||||
vt 0.564792 0.689981
|
||||
vt 0.564792 0.953949
|
||||
vt 0.338554 0.953949
|
||||
vt 0.338554 0.689981
|
||||
vt 0.334296 0.692438
|
||||
vt 0.334296 0.954445
|
||||
vt 0.203294 0.986197
|
||||
vt 0.203294 0.656186
|
||||
vt 0.695616 0.984433
|
||||
vt 0.572654 0.959219
|
||||
vt 0.695616 0.657719
|
||||
vt 0.572795 0.683358
|
||||
vt 0.941945 0.999442
|
||||
vt 0.694306 0.999442
|
||||
vt 0.941945 0.647349
|
||||
vt 0.694306 0.647349
|
||||
vt 0.070499 0.643570
|
||||
vt 0.070499 0.998147
|
||||
vt 0.003817 0.998147
|
||||
vt 0.003817 0.643570
|
||||
vt 0.997239 0.998410
|
||||
vt 0.940308 0.998410
|
||||
vt 0.997239 0.647262
|
||||
vt 0.940308 0.647262
|
||||
vt 0.331416 0.690927
|
||||
vt 0.238667 0.577426
|
||||
vt 0.563274 0.690927
|
||||
vt 0.735343 0.574985
|
||||
vt 0.970266 0.522520
|
||||
vt 0.962942 0.572543
|
||||
vt 0.072079 0.572543
|
||||
vt 0.079403 0.522520
|
||||
vt 0.744362 0.446044
|
||||
vt 0.780351 0.385565
|
||||
vt 0.817830 0.446044
|
||||
vt 0.854309 0.385565
|
||||
vt 0.891299 0.446044
|
||||
vt 0.743883 0.446331
|
||||
vt 0.779872 0.385852
|
||||
vt 0.817351 0.446331
|
||||
vt 0.853830 0.385852
|
||||
vt 0.890820 0.446331
|
||||
s off
|
||||
f 1/1 3/2 4/3
|
||||
f 2/4 1/1 4/3
|
||||
f 2/5 4/6 5/7
|
||||
f 5/7 6/8 2/5
|
||||
f 9/9 3/10 10/11
|
||||
f 3/10 1/12 10/11
|
||||
f 11/13 9/14 12/15
|
||||
f 10/16 12/15 9/14
|
||||
f 8/17 7/18 13/19
|
||||
f 13/19 14/20 8/17
|
||||
f 15/21 11/22 16/23
|
||||
f 12/24 16/23 11/22
|
||||
f 2/25 6/26 1/27
|
||||
f 10/28 1/27 6/26
|
||||
f 4/25 3/27 5/26
|
||||
f 9/28 5/26 3/27
|
||||
f 17/29 19/30 20/31
|
||||
f 18/32 17/29 20/31
|
||||
f 21/30 23/29 24/32
|
||||
f 22/31 21/30 24/32
|
||||
f 26/33 27/34 25/35
|
||||
f 27/34 28/36 25/35
|
||||
f 28/36 29/37 25/35
|
||||
f 31/38 32/39 30/40
|
||||
f 32/39 33/41 30/40
|
||||
f 33/41 34/42 30/40
|
||||
f 36/42 37/41 35/40
|
||||
f 37/41 38/39 35/40
|
||||
f 38/39 39/38 35/40
|
||||
f 41/37 42/36 40/35
|
||||
f 42/36 43/34 40/35
|
||||
f 43/34 44/33 40/35
|
||||
|
Before Width: | Height: | Size: 51 KiB |
@ -1,206 +0,0 @@
|
||||
#include "cAutomata.h"
|
||||
|
||||
|
||||
cAuto_Node::cAuto_Node()
|
||||
{
|
||||
pos = { 0,0 };
|
||||
}
|
||||
|
||||
cAuto_Node::cAuto_Node(const olc::vf2d &worldpos)
|
||||
{
|
||||
pos = worldpos;
|
||||
}
|
||||
|
||||
olc::vf2d cAuto_Track::GetPostion(float t, cAuto_Node *pStart)
|
||||
{
|
||||
// pStart indicates the node the automata first encounted this track
|
||||
if (node[0] == pStart)
|
||||
{
|
||||
return node[0]->pos + (node[1]->pos - node[0]->pos) * (t / fTrackLength);
|
||||
}
|
||||
else
|
||||
{
|
||||
return node[1]->pos + (node[0]->pos - node[1]->pos) * (t / fTrackLength);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
cAuto_Body::cAuto_Body()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
cAuto_Body::~cAuto_Body()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void cAuto_Body::UpdateAuto(float fElapsedTime)
|
||||
{
|
||||
// Work out which node is the target destination
|
||||
cAuto_Node *pExitNode = pCurrentTrack->node[0];
|
||||
if (pExitNode == pTrackOriginNode)
|
||||
pExitNode = pCurrentTrack->node[1];
|
||||
|
||||
bool bAutomataCanMove = true;
|
||||
|
||||
float fDistanceToAutoInFront = 1.0f;
|
||||
|
||||
// First check if the vehicle overlaps with the one in front of it
|
||||
|
||||
// Get an iterator for this automata
|
||||
auto itThisAutomata = std::find(pCurrentTrack->listAutos.begin(), pCurrentTrack->listAutos.end(), this);
|
||||
|
||||
// If this automata is at the front of this track segment
|
||||
if (*itThisAutomata == pCurrentTrack->listAutos.front())
|
||||
{
|
||||
// Then check all the following track segments. Take the position of
|
||||
// each vehicle at the back of the track segments auto list
|
||||
for (auto &track : pExitNode->listTracks)
|
||||
{
|
||||
if (track != pCurrentTrack && !track->listAutos.empty())
|
||||
{
|
||||
// Get Auto at back
|
||||
float fDistanceFromTrackStartToAutoRear = track->listAutos.back()->fAutoPos - track->listAutos.back()->fAutoLength;
|
||||
|
||||
if ((*itThisAutomata)->fAutoPos < (pCurrentTrack->fTrackLength + fDistanceFromTrackStartToAutoRear - fAutoLength))
|
||||
{
|
||||
// Move Automata along track, as there is space
|
||||
//bAutomataCanMove = true;
|
||||
fDistanceToAutoInFront = (pCurrentTrack->fTrackLength + fDistanceFromTrackStartToAutoRear - 0.1f) - (*itThisAutomata)->fAutoPos;
|
||||
}
|
||||
else
|
||||
{
|
||||
// No space, so do not move automata
|
||||
bAutomataCanMove = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Track in front was empty, node is clear to pass through so
|
||||
//bAutomataCanMove = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// Get the automata in front
|
||||
auto itAutomataInFront = itThisAutomata;
|
||||
itAutomataInFront--;
|
||||
|
||||
// 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
|
||||
// to enter
|
||||
if (fabs((*itAutomataInFront)->fAutoPos - (*itThisAutomata)->fAutoPos) > ((*itAutomataInFront)->fAutoLength + 0.1f))
|
||||
{
|
||||
// Move Automata along track
|
||||
//bAutomataCanMove = true;
|
||||
fDistanceToAutoInFront = ((*itAutomataInFront)->fAutoPos - (*itAutomataInFront)->fAutoLength - 0.1f) - (*itThisAutomata)->fAutoPos;
|
||||
}
|
||||
else
|
||||
{
|
||||
// No space, so do not move automata
|
||||
bAutomataCanMove = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (bAutomataCanMove)
|
||||
{
|
||||
if (fDistanceToAutoInFront > pCurrentTrack->fTrackLength) fDistanceToAutoInFront = pCurrentTrack->fTrackLength;
|
||||
fAutoPos += fElapsedTime * std::max(fDistanceToAutoInFront, 1.0f) * (fAutoLength < 0.1f ? 0.3f : 0.5f);
|
||||
}
|
||||
|
||||
|
||||
if (fAutoPos >= pCurrentTrack->fTrackLength)
|
||||
{
|
||||
// Automata has reached end of current track
|
||||
|
||||
// Check if it can transition beyond node
|
||||
if (!pExitNode->bBlock)
|
||||
{
|
||||
// It can, so reset position along track back to start
|
||||
fAutoPos -= pCurrentTrack->fTrackLength;
|
||||
|
||||
// Choose a track from the node not equal to this one, that has an unblocked exit node
|
||||
|
||||
// For now choose at random
|
||||
cAuto_Track *pNewTrack = nullptr;
|
||||
|
||||
if (pExitNode->listTracks.size() == 2)
|
||||
{
|
||||
// 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
|
||||
// choose the exit.
|
||||
auto it = pExitNode->listTracks.begin();
|
||||
pNewTrack = (*it);
|
||||
if (pCurrentTrack == pNewTrack)
|
||||
{
|
||||
++it;
|
||||
pNewTrack = (*it);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Automata has reached a junction with several exits
|
||||
while (pNewTrack == nullptr)
|
||||
{
|
||||
int i = rand() % pExitNode->listTracks.size();
|
||||
int j = 0;
|
||||
for (auto it = pExitNode->listTracks.begin(); it != pExitNode->listTracks.end(); ++it)
|
||||
{
|
||||
cAuto_Track* track = (*it);
|
||||
|
||||
// Work out which node is the target destination
|
||||
cAuto_Node *pNewExitNode = track->node[0];
|
||||
if (pNewExitNode == pExitNode)
|
||||
pNewExitNode = track->node[1];
|
||||
|
||||
if (j == i && track != pCurrentTrack && !pNewExitNode->bBlock /*((*it)->cell != pCurrentTrack->cell)*/)
|
||||
{
|
||||
pNewTrack = track;
|
||||
break;
|
||||
}
|
||||
|
||||
j++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Change to new track, the origin node of the next
|
||||
// track is the same as the exit node to the current track
|
||||
pTrackOriginNode = pExitNode;
|
||||
|
||||
// Remove the automata from the front of the queue
|
||||
// on the current track
|
||||
pCurrentTrack->listAutos.pop_front();
|
||||
|
||||
// Switch the automatas track link to the new track
|
||||
pCurrentTrack = pNewTrack;
|
||||
|
||||
// Push the automata onto the back of the new track queue
|
||||
pCurrentTrack->listAutos.push_back(this);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// It cant pass the node, so clamp automata at this location
|
||||
fAutoPos = pCurrentTrack->fTrackLength;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// Automata is travelling
|
||||
vAutoPos = pCurrentTrack->GetPostion(fAutoPos, pTrackOriginNode);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -1,107 +0,0 @@
|
||||
/*
|
||||
Top Down City Based Car Crime Game - Part #2
|
||||
"Colin, I hope you're shooting 600+ wherever you are buddy. RIP." - javidx9
|
||||
|
||||
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.
|
||||
|
||||
Instructions:
|
||||
~~~~~~~~~~~~~
|
||||
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
|
||||
|
||||
Relevant Video: https://youtu.be/fIV6P1W-wuo
|
||||
|
||||
Links
|
||||
~~~~~
|
||||
YouTube: https://www.youtube.com/javidx9
|
||||
https://www.youtube.com/javidx9extra
|
||||
Discord: https://discord.gg/WhwHUMV
|
||||
Twitter: https://www.twitter.com/javidx9
|
||||
Twitch: https://www.twitch.tv/javidx9
|
||||
GitHub: https://www.github.com/onelonecoder
|
||||
Patreon: https://www.patreon.com/javidx9
|
||||
Homepage: https://www.onelonecoder.com
|
||||
|
||||
Author
|
||||
~~~~~~
|
||||
David Barr, aka javidx9, ©OneLoneCoder 2019
|
||||
*/
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "olcPixelGameEngine.h"
|
||||
|
||||
class cAuto_Track;
|
||||
class cAuto_Node;
|
||||
class cAuto_Body;
|
||||
class cCell;
|
||||
|
||||
class cAuto_Node
|
||||
{
|
||||
public:
|
||||
cAuto_Node();
|
||||
cAuto_Node(const olc::vf2d &worldpos);
|
||||
olc::vf2d pos;
|
||||
bool bBlock = false;
|
||||
std::list<cAuto_Track*> listTracks;
|
||||
};
|
||||
|
||||
class cAuto_Track
|
||||
{
|
||||
public:
|
||||
cAuto_Node* node[2]; // Two end nodes
|
||||
cCell* cell; // Pointer to host cell
|
||||
olc::vf2d GetPostion(float t, cAuto_Node *pstart);
|
||||
std::list<cAuto_Body*> listAutos;
|
||||
float fTrackLength = 1.0f;
|
||||
};
|
||||
|
||||
class cAuto_Body
|
||||
{
|
||||
public:
|
||||
cAuto_Body();
|
||||
~cAuto_Body();
|
||||
|
||||
public:
|
||||
void UpdateAuto(float fElapsedTime);
|
||||
|
||||
public:
|
||||
olc::vf2d vAutoPos = { 0.0f, 0.0f };
|
||||
float fAutoPos = 0.0f; // Location of automata along track
|
||||
float fAutoLength = 0.0f; // Physical length of automata
|
||||
cAuto_Track *pCurrentTrack = nullptr;
|
||||
cAuto_Node *pTrackOriginNode = nullptr;
|
||||
|
||||
};
|
||||
@ -1,709 +0,0 @@
|
||||
#include "cCarCrimeCity.h"
|
||||
|
||||
cCarCrimeCity::cCarCrimeCity()
|
||||
{
|
||||
sAppName = "Car Crime City";
|
||||
}
|
||||
|
||||
cCarCrimeCity::~cCarCrimeCity()
|
||||
{
|
||||
}
|
||||
|
||||
bool cCarCrimeCity::OnUserCreate()
|
||||
{
|
||||
// Initialise PGEX 3D
|
||||
olc::GFX3D::ConfigureDisplay();
|
||||
|
||||
// Load fixed system assets, i.e. those need to simply do anything
|
||||
if (!LoadAssets()) return false;
|
||||
|
||||
// Create Default city
|
||||
pCity = new cCityMap(cGameSettings::nDefaultMapWidth, cGameSettings::nDefaultMapHeight, mapAssetTextures, mapAssetMeshes, mapAssetTransform);
|
||||
|
||||
// If a city map file has been specified, then load it
|
||||
if (!cGameSettings::sDefaultCityFile.empty())
|
||||
{
|
||||
if (!pCity->LoadCity(cGameSettings::sDefaultCityFile))
|
||||
{
|
||||
std::cout << "Failed to load '" << cGameSettings::sDefaultCityFile << "'" << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cCarCrimeCity::LoadAssets()
|
||||
{
|
||||
// Game Settings should have loaded all the relevant file information
|
||||
// to start loading asset information. Game assets will be stored in
|
||||
// a map structure. Maps can have slightly longer access times, so each
|
||||
// in game object will have facility to extract required resources once
|
||||
// when it is created, meaning no map search during normal use
|
||||
|
||||
// System Meshes
|
||||
// A simple flat unit quad
|
||||
olc::GFX3D::mesh* meshQuad = new olc::GFX3D::mesh();
|
||||
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::WHITE, olc::WHITE, olc::WHITE },
|
||||
{ 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::WHITE, olc::WHITE, olc::WHITE },
|
||||
};
|
||||
mapAssetMeshes["UnitQuad"] = meshQuad;
|
||||
|
||||
//// The four outer walls of a cell
|
||||
olc::GFX3D::mesh* meshWallsOut = new olc::GFX3D::mesh();
|
||||
meshWallsOut->tris =
|
||||
{
|
||||
// EAST
|
||||
{ 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.2f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, olc::WHITE, olc::WHITE, olc::WHITE },
|
||||
{ 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.2f, 1.0f, 1.0f, 0.0f, 0.2f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, olc::WHITE, olc::WHITE, olc::WHITE },
|
||||
|
||||
// WEST
|
||||
{ 0.0f, 0.0f, 0.2f, 1.0f, 0.0f, 1.0f, 0.2f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, olc::WHITE, olc::WHITE, olc::WHITE },
|
||||
{ 0.0f, 0.0f, 0.2f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, olc::WHITE, olc::WHITE, olc::WHITE },
|
||||
|
||||
// TOP
|
||||
{ 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.2f, 1.0f, 1.0f, 1.0f, 0.2f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, olc::WHITE, olc::WHITE, olc::WHITE },
|
||||
{ 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.2f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, olc::WHITE, olc::WHITE, olc::WHITE },
|
||||
|
||||
// BOTTOM
|
||||
{ 1.0f, 0.0f, 0.2f, 1.0f, 0.0f, 0.0f, 0.2f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, olc::WHITE, olc::WHITE, olc::WHITE },
|
||||
{ 1.0f, 0.0f, 0.2f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, olc::WHITE, olc::WHITE, olc::WHITE },
|
||||
};
|
||||
mapAssetMeshes["WallsOut"] = meshWallsOut;
|
||||
|
||||
|
||||
// System Textures
|
||||
for (auto &asset : cGameSettings::vecAssetTextures)
|
||||
{
|
||||
olc::Sprite *sprAsset = new olc::Sprite();
|
||||
if (sprAsset->LoadFromFile(asset.sFile))
|
||||
{
|
||||
mapAssetTextures[asset.sName] = sprAsset;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Failed to load " << asset.sName << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Break up roads sprite into individual sprites. Why? Its easier to maintain
|
||||
// the roads sprite as a single image, but easier to use if they are all individual.
|
||||
// Breaking it up manually in the image editing software is time consuming so just
|
||||
// do it here
|
||||
int nRoadTexSize = 256; // In pixels in base texture
|
||||
int nRoadTexOffset = 64; // There exists a 64 pixel offset from top left of source image
|
||||
for (int r = 0; r < 12; r++)
|
||||
{
|
||||
olc::Sprite* road = new olc::Sprite(nRoadTexSize, nRoadTexSize);
|
||||
SetDrawTarget(road);
|
||||
DrawPartialSprite(0, 0, mapAssetTextures["AllRoads"], ((r % 3) * nRoadTexSize) + nRoadTexOffset, ((r / 3) * nRoadTexSize) + nRoadTexOffset, nRoadTexSize, nRoadTexSize);
|
||||
switch (r)
|
||||
{
|
||||
case 0: mapAssetTextures["Road_V"] = road; break;
|
||||
case 1: mapAssetTextures["Road_H"] = road; break;
|
||||
case 2: mapAssetTextures["Pavement"] = road; break;
|
||||
case 3: mapAssetTextures["Road_C1"] = road; break;
|
||||
case 4: mapAssetTextures["Road_T1"] = road; break;
|
||||
case 5: mapAssetTextures["Road_C2"] = road; break;
|
||||
case 6: mapAssetTextures["Road_T2"] = road; break;
|
||||
case 7: mapAssetTextures["Road_X"] = road; break;
|
||||
case 8: mapAssetTextures["Road_T3"] = road; break;
|
||||
case 9: mapAssetTextures["Road_C3"] = road; break;
|
||||
case 10: mapAssetTextures["Road_T4"] = road; break;
|
||||
case 11: mapAssetTextures["Road_C4"] = road; break;
|
||||
}
|
||||
}
|
||||
SetDrawTarget(nullptr);
|
||||
|
||||
|
||||
// Load Buildings
|
||||
for (auto &asset : cGameSettings::vecAssetBuildings)
|
||||
{
|
||||
mapAssetMeshes[asset.sDescription] = new olc::GFX3D::mesh();
|
||||
mapAssetMeshes[asset.sDescription]->LoadOBJFile(asset.sModelOBJ);
|
||||
mapAssetTextures[asset.sDescription] = new olc::Sprite(asset.sModelPNG);
|
||||
|
||||
olc::GFX3D::mat4x4 matScale = olc::GFX3D::Math::Mat_MakeScale(asset.fScale[0], asset.fScale[1], asset.fScale[2]);
|
||||
olc::GFX3D::mat4x4 matTranslate = olc::GFX3D::Math::Mat_MakeTranslation(asset.fTranslate[0], asset.fTranslate[1], asset.fTranslate[2]);
|
||||
olc::GFX3D::mat4x4 matRotateX = olc::GFX3D::Math::Mat_MakeRotationX(asset.fRotate[0]);
|
||||
olc::GFX3D::mat4x4 matRotateY = olc::GFX3D::Math::Mat_MakeRotationY(asset.fRotate[1]);
|
||||
olc::GFX3D::mat4x4 matRotateZ = olc::GFX3D::Math::Mat_MakeRotationZ(asset.fRotate[2]);
|
||||
olc::GFX3D::mat4x4 matTransform = olc::GFX3D::Math::Mat_MultiplyMatrix(matTranslate, matScale);
|
||||
matTransform = olc::GFX3D::Math::Mat_MultiplyMatrix(matTransform, matRotateX);
|
||||
matTransform = olc::GFX3D::Math::Mat_MultiplyMatrix(matTransform, matRotateY);
|
||||
matTransform = olc::GFX3D::Math::Mat_MultiplyMatrix(matTransform, matRotateZ);
|
||||
mapAssetTransform[asset.sDescription] = matTransform;
|
||||
}
|
||||
|
||||
// Load Vehicles
|
||||
for (auto &asset : cGameSettings::vecAssetVehicles)
|
||||
{
|
||||
mapAssetMeshes[asset.sDescription] = new olc::GFX3D::mesh();
|
||||
mapAssetMeshes[asset.sDescription]->LoadOBJFile(asset.sModelOBJ);
|
||||
mapAssetTextures[asset.sDescription] = new olc::Sprite(asset.sModelPNG);
|
||||
|
||||
olc::GFX3D::mat4x4 matScale = olc::GFX3D::Math::Mat_MakeScale(asset.fScale[0], asset.fScale[1], asset.fScale[2]);
|
||||
olc::GFX3D::mat4x4 matTranslate = olc::GFX3D::Math::Mat_MakeTranslation(asset.fTranslate[0], asset.fTranslate[1], asset.fTranslate[2]);
|
||||
olc::GFX3D::mat4x4 matRotateX = olc::GFX3D::Math::Mat_MakeRotationX(asset.fRotate[0]);
|
||||
olc::GFX3D::mat4x4 matRotateY = olc::GFX3D::Math::Mat_MakeRotationY(asset.fRotate[1]);
|
||||
olc::GFX3D::mat4x4 matRotateZ = olc::GFX3D::Math::Mat_MakeRotationZ(asset.fRotate[2]);
|
||||
olc::GFX3D::mat4x4 matTransform = olc::GFX3D::Math::Mat_MultiplyMatrix(matTranslate, matScale);
|
||||
matTransform = olc::GFX3D::Math::Mat_MultiplyMatrix(matTransform, matRotateX);
|
||||
matTransform = olc::GFX3D::Math::Mat_MultiplyMatrix(matTransform, matRotateY);
|
||||
matTransform = olc::GFX3D::Math::Mat_MultiplyMatrix(matTransform, matRotateZ);
|
||||
mapAssetTransform[asset.sDescription] = matTransform;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void cCarCrimeCity::SpawnPedestrian(int x, int y)
|
||||
{
|
||||
cCell* cell = pCity->Cell(x, y);
|
||||
|
||||
cAuto_Track *t = ((cCell_Road*)cell)->pSafePedestrianTrack;
|
||||
if (t == nullptr) return;
|
||||
|
||||
cAuto_Body *a = new cAuto_Body();
|
||||
a->fAutoLength = 0.05f;
|
||||
a->pCurrentTrack = t;
|
||||
a->pCurrentTrack->listAutos.push_back(a);
|
||||
a->pTrackOriginNode = t->node[0];
|
||||
a->UpdateAuto(0.0f);
|
||||
listAutomata.push_back(a);
|
||||
}
|
||||
|
||||
void cCarCrimeCity::SpawnVehicle(int x, int y)
|
||||
{
|
||||
cCell* cell = pCity->Cell(x, y);
|
||||
|
||||
cAuto_Track *t = ((cCell_Road*)cell)->pSafeCarTrack;
|
||||
if (t == nullptr) return;
|
||||
|
||||
cAuto_Body *a = new cAuto_Body();
|
||||
a->fAutoLength = 0.2f;
|
||||
a->pCurrentTrack = t;
|
||||
a->pCurrentTrack->listAutos.push_back(a);
|
||||
a->pTrackOriginNode = t->node[0];
|
||||
a->UpdateAuto(0.0f);
|
||||
listAutomata.push_back(a);
|
||||
}
|
||||
|
||||
void cCarCrimeCity::DoEditMode(float fElapsedTime)
|
||||
{
|
||||
// Get cell under mouse cursor
|
||||
cCell* mcell = pCity->Cell(nMouseX, nMouseY);
|
||||
bool bTempCellAdded = false;
|
||||
|
||||
// Left click and drag adds cells
|
||||
if (mcell != nullptr && GetMouse(0).bHeld)
|
||||
setSelectedCells.emplace(nMouseY * pCity->GetWidth() + nMouseX);
|
||||
|
||||
// Right click clears selection
|
||||
if (GetMouse(1).bReleased)
|
||||
setSelectedCells.clear();
|
||||
|
||||
if (setSelectedCells.empty())
|
||||
{
|
||||
// If nothing can be edited validly then just exit
|
||||
if (mcell == nullptr)
|
||||
return;
|
||||
|
||||
// else set is empty, so temporarily add current cell to it
|
||||
setSelectedCells.emplace(nMouseY * pCity->GetWidth() + nMouseX);
|
||||
bTempCellAdded = true;
|
||||
}
|
||||
|
||||
// If the map changes, we will need to update
|
||||
// the automata, and adjacency
|
||||
bool bMapChanged = false;
|
||||
|
||||
// Press "G" to apply grass
|
||||
if (GetKey(olc::Key::G).bPressed)
|
||||
{
|
||||
for (auto &c : setSelectedCells)
|
||||
{
|
||||
int x = c % pCity->GetWidth();
|
||||
int y = c / pCity->GetWidth();
|
||||
cCell* cell = pCity->Replace(x, y, new cCell_Plane(pCity, x, y, PLANE_GRASS));
|
||||
cell->LinkAssets(mapAssetTextures, mapAssetMeshes, mapAssetTransform);
|
||||
}
|
||||
|
||||
bMapChanged = true;
|
||||
}
|
||||
|
||||
// Press "P" to apply Pavement
|
||||
if (GetKey(olc::Key::P).bPressed)
|
||||
{
|
||||
for (auto &c : setSelectedCells)
|
||||
{
|
||||
int x = c % pCity->GetWidth();
|
||||
int y = c / pCity->GetWidth();
|
||||
cCell* cell = pCity->Replace(x, y, new cCell_Plane(pCity, x, y, PLANE_ASPHALT));
|
||||
cell->LinkAssets(mapAssetTextures, mapAssetMeshes, mapAssetTransform);
|
||||
}
|
||||
|
||||
bMapChanged = true;
|
||||
}
|
||||
|
||||
// Press "W" to apply Water
|
||||
if (GetKey(olc::Key::W).bPressed)
|
||||
{
|
||||
for (auto &c : setSelectedCells)
|
||||
{
|
||||
int x = c % pCity->GetWidth();
|
||||
int y = c / pCity->GetWidth();
|
||||
cCell* cell = pCity->Replace(x, y, new cCell_Water(pCity, x, y));
|
||||
cell->LinkAssets(mapAssetTextures, mapAssetMeshes, mapAssetTransform);
|
||||
}
|
||||
|
||||
bMapChanged = true;
|
||||
}
|
||||
|
||||
// Press "R" to apply Roads
|
||||
if (GetKey(olc::Key::Q).bPressed)
|
||||
{
|
||||
for (auto &c : setSelectedCells)
|
||||
{
|
||||
int x = c % pCity->GetWidth();
|
||||
int y = c / pCity->GetWidth();
|
||||
cCell* cell = pCity->Replace(x, y, new cCell_Building("Apartments_1", pCity, x, y));
|
||||
cell->LinkAssets(mapAssetTextures, mapAssetMeshes, mapAssetTransform);
|
||||
}
|
||||
|
||||
bMapChanged = true;
|
||||
}
|
||||
|
||||
|
||||
// Press "R" to apply Roads
|
||||
if (GetKey(olc::Key::R).bPressed)
|
||||
{
|
||||
for (auto &c : setSelectedCells)
|
||||
{
|
||||
int x = c % pCity->GetWidth();
|
||||
int y = c / pCity->GetWidth();
|
||||
cCell* cell = pCity->Replace(x, y, new cCell_Road(pCity, x, y));
|
||||
cell->LinkAssets(mapAssetTextures, mapAssetMeshes, mapAssetTransform);
|
||||
}
|
||||
|
||||
bMapChanged = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (GetKey(olc::Key::C).bPressed)
|
||||
{
|
||||
for (auto &c : setSelectedCells)
|
||||
{
|
||||
int x = c % pCity->GetWidth();
|
||||
int y = c / pCity->GetWidth();
|
||||
SpawnVehicle(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (GetKey(olc::Key::V).bPressed)
|
||||
{
|
||||
for (auto &c : setSelectedCells)
|
||||
{
|
||||
int x = c % pCity->GetWidth();
|
||||
int y = c / pCity->GetWidth();
|
||||
SpawnPedestrian(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
if (bMapChanged)
|
||||
{
|
||||
// The navigation nodes may have tracks attached to them, so get rid of them
|
||||
// all. Below we will reconstruct all tracks because city has changed
|
||||
pCity->RemoveAllTracks();
|
||||
|
||||
for (auto &a : listAutomata) delete a;
|
||||
listAutomata.clear();
|
||||
|
||||
for (int x = 0; x < pCity->GetWidth(); x++)
|
||||
{
|
||||
for (int y = 0; y < pCity->GetHeight(); y++)
|
||||
{
|
||||
cCell *c = pCity->Cell(x, y);
|
||||
|
||||
// Update adjacency information, i.e. those cells whose
|
||||
// state changes based on neighbouring cells
|
||||
c->CalculateAdjacency();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// To facilitate "edit under cursor" we added a temporary cell
|
||||
// which needs to be removed now
|
||||
if (bTempCellAdded)
|
||||
setSelectedCells.clear();
|
||||
}
|
||||
|
||||
olc::vf2d cCarCrimeCity::GetMouseOnGround(const olc::vf2d &vMouseScreen)
|
||||
{
|
||||
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.1f, 1000.0f);
|
||||
olc::GFX3D::mat4x4 matView = olc::GFX3D::Math::Mat_PointAt(vEye, vLookTarget, vUp);
|
||||
olc::GFX3D::vec3d vecMouseDir = {
|
||||
2.0f * ((vMouseScreen.x / (float)ScreenWidth()) - 0.5f) / matProj.m[0][0],
|
||||
2.0f * ((vMouseScreen.y / (float)ScreenHeight()) - 0.5f) / matProj.m[1][1],
|
||||
1.0f,
|
||||
0.0f };
|
||||
|
||||
olc::GFX3D::vec3d vecMouseOrigin = { 0.0f, 0.0f, 0.0f };
|
||||
vecMouseOrigin = olc::GFX3D::Math::Mat_MultiplyVector(matView, vecMouseOrigin);
|
||||
vecMouseDir = olc::GFX3D::Math::Mat_MultiplyVector(matView, vecMouseDir);
|
||||
vecMouseDir = olc::GFX3D::Math::Vec_Mul(vecMouseDir, 1000.0f);
|
||||
vecMouseDir = olc::GFX3D::Math::Vec_Add(vecMouseOrigin, vecMouseDir);
|
||||
|
||||
// Perform line/plane intersection to determine mouse position in world space
|
||||
olc::GFX3D::vec3d plane_p = { 0.0f, 0.0f, 0.0f };
|
||||
olc::GFX3D::vec3d plane_n = { 0.0f, 0.0f, 1.0f };
|
||||
float t = 0.0f;
|
||||
olc::GFX3D::vec3d mouse3d = olc::GFX3D::Math::Vec_IntersectPlane(plane_p, plane_n, vecMouseOrigin, vecMouseDir, t);
|
||||
return { mouse3d.x, mouse3d.y };
|
||||
}
|
||||
|
||||
bool cCarCrimeCity::OnUserUpdate(float fElapsedTime)
|
||||
{
|
||||
fGlobalTime += fElapsedTime;
|
||||
|
||||
if (GetKey(olc::Key::TAB).bReleased) bEditMode = !bEditMode;
|
||||
|
||||
if (bEditMode) // Use mouse to pan and zoom, and place objects
|
||||
{
|
||||
vEye = vCamera;
|
||||
olc::vf2d vMouseScreen = { (float)GetMouseX(), (float)GetMouseY() };
|
||||
olc::vf2d vMouseOnGroundBeforeZoom = GetMouseOnGround(vMouseScreen);
|
||||
|
||||
vOffset = { 0,0 };
|
||||
|
||||
if (IsFocused())
|
||||
{
|
||||
if (GetMouse(2).bPressed) { vStartPan = vMouseOnGroundBeforeZoom; }
|
||||
if (GetMouse(2).bHeld) { vOffset = (vStartPan - vMouseOnGroundBeforeZoom); };
|
||||
|
||||
if (GetMouseWheel() > 0)
|
||||
{
|
||||
vCamera.z *= 0.5f;
|
||||
}
|
||||
|
||||
if (GetMouseWheel() < 0)
|
||||
{
|
||||
vCamera.z *= 1.5f;
|
||||
}
|
||||
}
|
||||
|
||||
vEye = vCamera;
|
||||
olc::vf2d vMouseOnGroundAfterZoom = GetMouseOnGround(vMouseScreen);
|
||||
vOffset += (vMouseOnGroundBeforeZoom - vMouseOnGroundAfterZoom);
|
||||
vCamera.x += vOffset.x;
|
||||
vCamera.y += vOffset.y;
|
||||
vEye = vCamera;
|
||||
|
||||
// Get Integer versions of mouse coords in world space
|
||||
nMouseX = (int)vMouseOnGroundAfterZoom.x;
|
||||
nMouseY = (int)vMouseOnGroundAfterZoom.y;
|
||||
|
||||
DoEditMode(fElapsedTime);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Not in edit mode, so camera follows player
|
||||
if (GetKey(olc::Key::LEFT).bHeld) fAngle += -2.5f * fElapsedTime;
|
||||
if (GetKey(olc::Key::RIGHT).bHeld) fAngle += 2.5f * fElapsedTime;
|
||||
if (GetKey(olc::Key::UP).bHeld)
|
||||
{
|
||||
carvel = { cos(fAngle), sin(fAngle) };
|
||||
carpos += carvel * 2.0f * fElapsedTime;
|
||||
}
|
||||
|
||||
vCamera.x = carpos.x;
|
||||
vCamera.y = carpos.y;
|
||||
vEye = vCamera;
|
||||
}
|
||||
|
||||
/*fAngle = 0.0f;
|
||||
if (GetKey(olc::Key::LEFT).bHeld) fAngle = -0.8f;
|
||||
if (GetKey(olc::Key::RIGHT).bHeld) fAngle = 0.8f;*/
|
||||
|
||||
|
||||
//car.UpdateDrive(fElapsedTime, 1.0f, GetKey(olc::Key::UP).bHeld, GetKey(olc::Key::SPACE).bHeld, GetKey(olc::Key::DOWN).bHeld, fAngle);
|
||||
|
||||
|
||||
//if (car.bSkidding && fmod(fGlobalTime, 0.05f) < 0.01f)
|
||||
//{
|
||||
// listDecalSmoke.push_front({ 0.1f, {car.vPosRear.x, car.vPosRear.y, -0.03f} });
|
||||
//}
|
||||
|
||||
|
||||
//// Update Decals
|
||||
//for (auto &d : listDecalSmoke)
|
||||
//{
|
||||
// d.fLifetime += fElapsedTime;
|
||||
//}
|
||||
|
||||
//listDecalSmoke.remove_if([](const sSmokeDecal &d) {return d.fLifetime > 2.0f; });
|
||||
|
||||
//if (!bEditMode)
|
||||
//{
|
||||
// vCamera.x = car.GetOrigin().x;
|
||||
// vCamera.y = car.GetOrigin().y;
|
||||
//}
|
||||
|
||||
|
||||
//float fTargetHeight = -1.0f;
|
||||
//int nCarX = vCamera.x;
|
||||
//int nCarY = vCamera.y;
|
||||
|
||||
std::vector<cGameObjectQuad> vecNeighbours;
|
||||
|
||||
//// Check surrounding cells height
|
||||
//for (int x = nCarX - 1; x < nCarX + 2; x++)
|
||||
// for (int y = nCarY - 1; y < nCarY + 2; y++)
|
||||
// {
|
||||
// if (pCity->Cell(x,y) && pCity->Cell(x, y)->bBuilding)
|
||||
// {
|
||||
// cGameObjectQuad ob(1.0f, 1.0f);
|
||||
// ob.pos = { (float)x + 0.5f, (float)y + 0.5f, 0.0f, 1.0f };
|
||||
// ob.TransformModelToWorld();
|
||||
// vecNeighbours.push_back(ob);
|
||||
// fTargetHeight = -2.0f;
|
||||
// }
|
||||
// }
|
||||
|
||||
//goCar->pos.x = car.GetOrigin().x;
|
||||
//goCar->pos.y = car.GetOrigin().y;
|
||||
//goCar->fAngle = car.GetRotation();
|
||||
//goCar->TransformModelToWorld();
|
||||
|
||||
//for (auto &ob : vecNeighbours)
|
||||
//{
|
||||
// if (goCar->StaticCollisionWith(ob, true))
|
||||
// {
|
||||
// goCar->TransformModelToWorld();
|
||||
// car.vPosRear.x += goCar->pos.x - car.GetOrigin().x;
|
||||
// car.vPosRear.y += goCar->pos.y - car.GetOrigin().y;
|
||||
// car.vPosFront.x += goCar->pos.x - car.GetOrigin().x;
|
||||
// car.vPosFront.y += goCar->pos.y - car.GetOrigin().y;
|
||||
// car.fSpeed = 0.0f;
|
||||
// }
|
||||
//}
|
||||
|
||||
//if(!bEditMode)
|
||||
// vCamera.z += (fTargetHeight - vCamera.z) * 10.0f * fElapsedTime;
|
||||
|
||||
|
||||
//car.UpdateTow(fElapsedTime, { mouse3d.x, mouse3d.y });
|
||||
|
||||
|
||||
|
||||
//for (int v = 1; v<vecTraffic.size(); v++)
|
||||
//{
|
||||
// //vecTraffic[v].UpdateTow(fElapsedTime * 10.0f, vecTraffic[v - 1].vPosRear);
|
||||
//}
|
||||
|
||||
// Calculate Visible ground plane dimensions
|
||||
viewWorldTopLeft = GetMouseOnGround(olc::vf2d( 0.0f, 0.0f ));
|
||||
viewWorldBottomRight = GetMouseOnGround(olc::vf2d((float)ScreenWidth(), (float)ScreenHeight()));
|
||||
|
||||
// Calculate visible world extents
|
||||
int nStartX = std::max(0, (int)viewWorldTopLeft.x - 1);
|
||||
int nEndX = std::min(pCity->GetWidth(), (int)viewWorldBottomRight.x + 1);
|
||||
int nStartY = std::max(0, (int)viewWorldTopLeft.y - 1);
|
||||
int nEndY = std::min(pCity->GetHeight(), (int)viewWorldBottomRight.y + 1);
|
||||
|
||||
// Only update automata for cells near player
|
||||
int nAutomStartX = std::max(0, (int)viewWorldTopLeft.x - 3);
|
||||
int nAutomEndX = std::min(pCity->GetWidth(), (int)viewWorldBottomRight.x + 3);
|
||||
int nAutomStartY = std::max(0, (int)viewWorldTopLeft.y - 3);
|
||||
int nAutomEndY = std::min(pCity->GetHeight(), (int)viewWorldBottomRight.y + 3);
|
||||
|
||||
int nLocalStartX = std::max(0, (int)vCamera.x - 3);
|
||||
int nLocalEndX = std::min(pCity->GetWidth(), (int)vCamera.x + 3);
|
||||
int nLocalStartY = std::max(0, (int)vCamera.y - 3);
|
||||
int nLocalEndY = std::min(pCity->GetHeight(), (int)vCamera.y + 3);
|
||||
|
||||
|
||||
// Update Cells
|
||||
for (int x = nStartX; x < nEndX; x++)
|
||||
{
|
||||
for (int y = nStartY; y < nEndY; y++)
|
||||
{
|
||||
pCity->Cell(x, y)->Update(fElapsedTime);
|
||||
}
|
||||
}
|
||||
|
||||
// Update Automata
|
||||
for (auto &a : listAutomata)
|
||||
{
|
||||
a->UpdateAuto(fElapsedTime);
|
||||
|
||||
// If automata is too far from camera, remove it
|
||||
if ((a->vAutoPos - olc::vf2d(vCamera.x, vCamera.y)).mag() > 5.0f)
|
||||
{
|
||||
// Despawn automata
|
||||
|
||||
// 1) Disconnect it from track
|
||||
a->pCurrentTrack->listAutos.remove(a);
|
||||
|
||||
// 2) Erase it from memory
|
||||
delete a; a = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove dead automata, their pointer has been set to nullptr in the list
|
||||
listAutomata.remove(nullptr);
|
||||
|
||||
// Maintain a certain level of automata in vicinty of player
|
||||
if (listAutomata.size() < 20)
|
||||
{
|
||||
bool bSpawnOK = false;
|
||||
int nSpawnAttempt = 20;
|
||||
while (!bSpawnOK && nSpawnAttempt > 0)
|
||||
{
|
||||
// Find random cell on edge of vicinty, which is out of view of the player
|
||||
float fRandomAngle = ((float)rand() / (float)RAND_MAX) * 2.0f * 3.14159f;
|
||||
int nRandomCellX = vCamera.x + cos(fRandomAngle) * 3.0f;
|
||||
int nRandomCellY = vCamera.y + sin(fRandomAngle) * 3.0f;
|
||||
|
||||
nSpawnAttempt--;
|
||||
|
||||
if (pCity->Cell(nRandomCellX, nRandomCellY) && pCity->Cell(nRandomCellX, nRandomCellY)->nCellType == CELL_ROAD)
|
||||
{
|
||||
bSpawnOK = true;
|
||||
|
||||
// Add random automata
|
||||
if (rand() % 100 < 50)
|
||||
{
|
||||
// Spawn Pedestrian
|
||||
SpawnPedestrian(nRandomCellX, nRandomCellY);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Spawn Vehicle
|
||||
SpawnVehicle(nRandomCellX, nRandomCellY);
|
||||
// TODO: Get % chance of vehicle spawn from lua script
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Render Scene
|
||||
Clear(olc::BLUE);
|
||||
olc::GFX3D::ClearDepth();
|
||||
|
||||
// Create rendering pipeline
|
||||
olc::GFX3D::PipeLine pipe;
|
||||
pipe.SetProjection(90.0f, (float)ScreenHeight() / (float)ScreenWidth(), 0.1f, 1000.0f, 0.0f, 0.0f, (float)ScreenWidth(), (float)ScreenHeight());
|
||||
olc::GFX3D::vec3d vLookTarget = olc::GFX3D::Math::Vec_Add(vEye, vLookDir);
|
||||
pipe.SetCamera(vEye, vLookTarget, vUp);
|
||||
|
||||
|
||||
// Add global illumination vector (sunlight)
|
||||
olc::GFX3D::vec3d lightdir = { 1.0f, 1.0f, -1.0f };
|
||||
pipe.SetLightSource(0, olc::GFX3D::LIGHT_AMBIENT, olc::Pixel(100,100,100), { 0,0,0 }, lightdir);
|
||||
pipe.SetLightSource(1, olc::GFX3D::LIGHT_DIRECTIONAL, olc::WHITE, { 0,0,0 }, lightdir);
|
||||
|
||||
|
||||
// RENDER CELL CONTENTS
|
||||
|
||||
// Render Base Objects (those without alpha components)
|
||||
for (int x = nStartX; x < nEndX; x++)
|
||||
{
|
||||
//omp_set_dynamic(0);
|
||||
//omp_set_num_threads(4);
|
||||
//#pragma omp parallel for
|
||||
for (int y = nStartY; y < nEndY; y++)
|
||||
{
|
||||
pCity->Cell(x, y)->DrawBase(this, pipe);
|
||||
}
|
||||
//#pragma omp barrier
|
||||
}
|
||||
|
||||
// Render Upper Objects (those with alpha components)
|
||||
for (int x = nStartX; x < nEndX; x++)
|
||||
{
|
||||
for (int y = nStartY; y < nEndY; y++)
|
||||
{
|
||||
pCity->Cell(x, y)->DrawAlpha(this, pipe);
|
||||
}
|
||||
}
|
||||
|
||||
if (bEditMode)
|
||||
{
|
||||
// Render additional per cell debug information
|
||||
for (int x = nStartX; x < nEndX; x++)
|
||||
{
|
||||
for (int y = nStartY; y < nEndY; y++)
|
||||
{
|
||||
pCity->Cell(x, y)->DrawDebug(this, pipe);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bEditMode)
|
||||
{
|
||||
// Draw Selections
|
||||
for (auto &c : setSelectedCells)
|
||||
{
|
||||
int x = c % pCity->GetWidth();
|
||||
int y = c / pCity->GetWidth();
|
||||
olc::GFX3D::mat4x4 matWorld = olc::GFX3D::Math::Mat_MakeTranslation((float)x, (float)y, 0.01f);
|
||||
pipe.SetTransform(matWorld);
|
||||
pipe.Render(mapAssetMeshes["UnitQuad"]->tris, olc::GFX3D::RENDER_WIRE);
|
||||
}
|
||||
}
|
||||
|
||||
// RENDER AUTOMATA
|
||||
|
||||
std::string test[] = { "Sedan", "SUV", "TruckCab", "TruckTrailer", "UTE", "Wagon" };
|
||||
int i = 0;
|
||||
for (auto &a : listAutomata)
|
||||
{
|
||||
olc::GFX3D::vec3d v = { a->vAutoPos.x, a->vAutoPos.y, 0.0f };
|
||||
|
||||
/*olc::GFX3D::mat4x4 matWorld = olc::GFX3D::Math::Mat_MakeTranslation(a->vAutoPos.x, a->vAutoPos.y, 0.01f);
|
||||
matWorld = olc::GFX3D::Math::Mat_MultiplyMatrix(mapAssetTransform[test[i]], matWorld);
|
||||
pipe.SetTransform(matWorld);
|
||||
pipe.SetTexture(mapAssetTextures[test[i]]);
|
||||
pipe.Render(mapAssetMeshes[test[i]]->tris, olc::GFX3D::RENDER_CULL_CW | olc::GFX3D::RENDER_DEPTH | olc::GFX3D::RENDER_TEXTURED | olc::GFX3D::RENDER_LIGHTS);
|
||||
i++;
|
||||
i = i % 6;*/
|
||||
|
||||
pipe.RenderCircleXZ(v, a->fAutoLength < 0.1f ? 0.05f : 0.07f, a->fAutoLength < 0.1f ? olc::MAGENTA : olc::YELLOW);
|
||||
}
|
||||
|
||||
|
||||
// Draw Player Vehicle
|
||||
{
|
||||
olc::GFX3D::mat4x4 matRotateZ = olc::GFX3D::Math::Mat_MakeRotationZ(fAngle);
|
||||
olc::GFX3D::mat4x4 matTranslate = olc::GFX3D::Math::Mat_MakeTranslation(carpos.x, carpos.y, 0.01f);
|
||||
olc::GFX3D::mat4x4 matWorld = olc::GFX3D::Math::Mat_MultiplyMatrix(mapAssetTransform["Sedan"], matRotateZ);
|
||||
matWorld = olc::GFX3D::Math::Mat_MultiplyMatrix(matWorld, matTranslate);
|
||||
pipe.SetTransform(matWorld);
|
||||
pipe.SetTexture(mapAssetTextures["Sedan"]);
|
||||
pipe.Render(mapAssetMeshes[test[i]]->tris, olc::GFX3D::RENDER_CULL_CW | olc::GFX3D::RENDER_DEPTH | olc::GFX3D::RENDER_TEXTURED | olc::GFX3D::RENDER_LIGHTS);
|
||||
}
|
||||
|
||||
DrawString(10, 10, "Automata: " + std::to_string(listAutomata.size()), olc::WHITE);
|
||||
|
||||
|
||||
if (GetKey(olc::Key::ESCAPE).bPressed)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cCarCrimeCity::OnUserDestroy()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@ -1,289 +0,0 @@
|
||||
/*
|
||||
Top Down City Based Car Crime Game - Part #2
|
||||
"Colin, I hope you're shooting 600+ wherever you are buddy. RIP." - javidx9
|
||||
|
||||
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.
|
||||
|
||||
Instructions:
|
||||
~~~~~~~~~~~~~
|
||||
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
|
||||
|
||||
Relevant Video: https://youtu.be/fIV6P1W-wuo
|
||||
|
||||
Links
|
||||
~~~~~
|
||||
YouTube: https://www.youtube.com/javidx9
|
||||
https://www.youtube.com/javidx9extra
|
||||
Discord: https://discord.gg/WhwHUMV
|
||||
Twitter: https://www.twitter.com/javidx9
|
||||
Twitch: https://www.twitch.tv/javidx9
|
||||
GitHub: https://www.github.com/onelonecoder
|
||||
Patreon: https://www.patreon.com/javidx9
|
||||
Homepage: https://www.onelonecoder.com
|
||||
|
||||
Author
|
||||
~~~~~~
|
||||
David Barr, aka javidx9, ©OneLoneCoder 2019
|
||||
*/
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "olcPixelGameEngine.h"
|
||||
#include "olcPGEX_Graphics3D.h"
|
||||
|
||||
#include "cGameSettings.h"
|
||||
#include "cCityMap.h"
|
||||
|
||||
#include <vector>
|
||||
#include <unordered_set>
|
||||
|
||||
struct sSmokeDecal
|
||||
{
|
||||
float fLifetime = 0.1f;
|
||||
olc::GFX3D::vec3d pos;
|
||||
};
|
||||
|
||||
class cCarCrimeCity : public olc::PixelGameEngine
|
||||
{
|
||||
public:
|
||||
cCarCrimeCity();
|
||||
~cCarCrimeCity();
|
||||
|
||||
private:
|
||||
bool OnUserCreate() override;
|
||||
bool OnUserUpdate(float fElapsedTime) override;
|
||||
bool OnUserDestroy() override;
|
||||
|
||||
private:
|
||||
|
||||
class cGameObjectQuad
|
||||
{
|
||||
public:
|
||||
cGameObjectQuad(float w, float h)
|
||||
{
|
||||
fWidth = w;
|
||||
fHeight = h;
|
||||
fAngle = 0.0f;
|
||||
|
||||
// Construct Model Quad Geometry
|
||||
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} };
|
||||
|
||||
vecPointsWorld.resize(vecPointsModel.size());
|
||||
TransformModelToWorld();
|
||||
}
|
||||
|
||||
void TransformModelToWorld()
|
||||
{
|
||||
for (size_t i = 0; i < vecPointsModel.size(); ++i)
|
||||
{
|
||||
vecPointsWorld[i] = {
|
||||
(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].z,
|
||||
vecPointsModel[i].w
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<olc::GFX3D::triangle> GetTriangles()
|
||||
{
|
||||
// Return triangles based upon this quad
|
||||
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[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.
|
||||
bool StaticCollisionWith(cGameObjectQuad &r2, bool bResolveStatic = false)
|
||||
{
|
||||
struct vec2d { float x; float y; };
|
||||
|
||||
bool bCollision = false;
|
||||
|
||||
// Check diagonals of R1 against edges of R2
|
||||
for (size_t p = 0; p < vecPointsWorld.size(); p++)
|
||||
{
|
||||
vec2d line_r1s = { pos.x, pos.y };
|
||||
vec2d line_r1e = { vecPointsWorld[p].x, vecPointsWorld[p].y };
|
||||
|
||||
vec2d displacement = { 0,0 };
|
||||
|
||||
for (size_t q = 0; q < r2.vecPointsWorld.size(); q++)
|
||||
{
|
||||
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 };
|
||||
|
||||
// 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 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;
|
||||
|
||||
if (t1 >= 0.0f && t1 <= 1.0f && t2 >= 0.0f && t2 <= 1.0f)
|
||||
{
|
||||
if (bResolveStatic)
|
||||
{
|
||||
displacement.x += (1.0f - t1) * (line_r1e.x - line_r1s.x);
|
||||
displacement.y += (1.0f - t1) * (line_r1e.y - line_r1s.y);
|
||||
bCollision = true;
|
||||
}
|
||||
else
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
pos.x -= displacement.x;
|
||||
pos.y -= displacement.y;
|
||||
}
|
||||
|
||||
// Check diagonals of R2 against edges of R1
|
||||
for (size_t p = 0; p < r2.vecPointsWorld.size(); p++)
|
||||
{
|
||||
vec2d line_r1s = { r2.pos.x, r2.pos.y };
|
||||
vec2d line_r1e = { r2.vecPointsWorld[p].x, r2.vecPointsWorld[p].y };
|
||||
|
||||
vec2d displacement = { 0,0 };
|
||||
|
||||
for (size_t q = 0; q < vecPointsWorld.size(); q++)
|
||||
{
|
||||
vec2d line_r2s = { vecPointsWorld[q].x, vecPointsWorld[q].y };
|
||||
vec2d line_r2e = { vecPointsWorld[(q + 1) % vecPointsWorld.size()].x, vecPointsWorld[(q + 1) % vecPointsWorld.size()].y };
|
||||
|
||||
// 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 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;
|
||||
|
||||
if (t1 >= 0.0f && t1 <= 1.0f && t2 >= 0.0f && t2 <= 1.0f)
|
||||
{
|
||||
if (bResolveStatic)
|
||||
{
|
||||
displacement.x += (1.0f - t1) * (line_r1e.x - line_r1s.x);
|
||||
displacement.y += (1.0f - t1) * (line_r1e.y - line_r1s.y);
|
||||
bCollision = true;
|
||||
}
|
||||
else
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pos.x += displacement.x;
|
||||
pos.y += displacement.y;
|
||||
}
|
||||
|
||||
return bCollision;
|
||||
}
|
||||
|
||||
std::vector<olc::GFX3D::triangle> meshTris;
|
||||
std::vector<olc::GFX3D::vec3d> vecPointsModel;
|
||||
std::vector<olc::GFX3D::vec3d> vecPointsWorld;
|
||||
olc::GFX3D::vec3d pos;
|
||||
|
||||
float fWidth;
|
||||
float fHeight;
|
||||
float fOriginX;
|
||||
float fOriginY;
|
||||
float fAngle;
|
||||
};
|
||||
|
||||
bool LoadAssets();
|
||||
|
||||
std::map<std::string, olc::Sprite*> mapAssetTextures;
|
||||
std::map<std::string, olc::GFX3D::mesh*> mapAssetMeshes;
|
||||
std::map<std::string, olc::GFX3D::mat4x4> mapAssetTransform;
|
||||
|
||||
// Camera variables
|
||||
olc::GFX3D::vec3d vCamera = { 0.0f, 0.0f, -3.0f };
|
||||
olc::GFX3D::vec3d vUp = { 0.0f, 1.0f, 0.0f };
|
||||
olc::GFX3D::vec3d vEye = { 0.0f, 0.0f, -3.0f };
|
||||
olc::GFX3D::vec3d vLookDir = { 0.0f, 0.0f, 1.0f };
|
||||
|
||||
// Ray Casting Parameters
|
||||
olc::vf2d viewWorldTopLeft;
|
||||
olc::vf2d viewWorldBottomRight;
|
||||
|
||||
// Cloud movement variables
|
||||
float fCloudOffsetX = 0.0f;
|
||||
float fCloudOffsetY = 0.0f;
|
||||
|
||||
// Mouse Control
|
||||
olc::vf2d vOffset = { 0.0f, 0.0f };
|
||||
olc::vf2d vStartPan = { 0.0f, 0.0f };
|
||||
olc::vf2d vMouseOnGround = { 0.0f, 0.0f };
|
||||
float fScale = 1.0f;
|
||||
|
||||
olc::vf2d GetMouseOnGround(const olc::vf2d &vMouseScreen);
|
||||
|
||||
//cVehicle car;
|
||||
olc::vf2d carvel;
|
||||
olc::vf2d carpos;
|
||||
float fSpeed = 0.0f;
|
||||
float fAngle = 0.0f;
|
||||
|
||||
std::list<cAuto_Body*> listAutomata; // Holds all automata, note its a pointer because we use polymorphism
|
||||
|
||||
void SpawnPedestrian(int x, int y);
|
||||
void SpawnVehicle(int x, int y);
|
||||
|
||||
//cGameObjectQuad *goCar = nullptr;
|
||||
//cGameObjectQuad *goObstacle = nullptr;
|
||||
|
||||
//std::vector<cGameObjectQuad> vecObstacles;
|
||||
|
||||
cCityMap *pCity = nullptr;
|
||||
|
||||
float fGlobalTime = 0.0f;
|
||||
|
||||
// Editing Utilities
|
||||
bool bEditMode = true;
|
||||
int nMouseX = 0;
|
||||
int nMouseY = 0;
|
||||
|
||||
struct sCellLoc { int x, y; };
|
||||
std::unordered_set<int> setSelectedCells;
|
||||
|
||||
//std::list<sSmokeDecal> listDecalSmoke;
|
||||
|
||||
//int nTrafficState = 0;
|
||||
|
||||
void DoEditMode(float fElapsedTime);
|
||||
};
|
||||
|
||||
@ -1,121 +0,0 @@
|
||||
#include "cCell.h"
|
||||
|
||||
#include "cCityMap.h"
|
||||
#include "olcPixelGameEngine.h"
|
||||
#include <map>
|
||||
|
||||
cCell::cCell()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
cCell::~cCell()
|
||||
{
|
||||
// Cells own a list of automata navigation tracks
|
||||
// but this will be destroyed when the cell is deleted
|
||||
}
|
||||
|
||||
cCell::cCell(cCityMap* map, int x, int y)
|
||||
{
|
||||
pMap = map;
|
||||
nWorldX = x;
|
||||
nWorldY = y;
|
||||
nCellType = CELL_BLANK;
|
||||
|
||||
// Connect internal nodes
|
||||
for (int i = 0; i < 49; i++)
|
||||
pNaviNodes[i] = pMap->GetAutoNodeBase(x, y) + i;
|
||||
|
||||
// Link cell into maps node pool
|
||||
if (y > 0)
|
||||
{
|
||||
for (int i = 0; i < 7; i++)
|
||||
pNaviNodes[i] = pMap->GetAutoNodeBase(x, y - 1) + 42 + i;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < 7; i++)
|
||||
pNaviNodes[i] = nullptr;
|
||||
}
|
||||
|
||||
if (x > 0)
|
||||
{
|
||||
// Link West side
|
||||
for (int i = 0; i < 7; i++)
|
||||
pNaviNodes[i * 7] = pMap->GetAutoNodeBase(x - 1, y) + 6 + i * 7;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < 7; i++)
|
||||
pNaviNodes[i * 7] = nullptr;
|
||||
}
|
||||
|
||||
// South Side
|
||||
if (y < pMap->GetHeight() - 1)
|
||||
{
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < 7; i++)
|
||||
pNaviNodes[42 + i] = nullptr;
|
||||
}
|
||||
|
||||
// East Side
|
||||
if (x < pMap->GetWidth() - 1)
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < 7; i++)
|
||||
pNaviNodes[6 + i * 7] = nullptr;
|
||||
}
|
||||
|
||||
// Unused Nodes
|
||||
pNaviNodes[9] = nullptr;
|
||||
pNaviNodes[11] = nullptr;
|
||||
pNaviNodes[15] = nullptr;
|
||||
pNaviNodes[19] = nullptr;
|
||||
pNaviNodes[29] = nullptr;
|
||||
pNaviNodes[33] = nullptr;
|
||||
pNaviNodes[37] = nullptr;
|
||||
pNaviNodes[39] = nullptr;
|
||||
pNaviNodes[0] = nullptr;
|
||||
pNaviNodes[6] = nullptr;
|
||||
pNaviNodes[42] = 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)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool cCell::Update(float fElapsedTime)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool cCell::DrawBase(olc::PixelGameEngine *pge, olc::GFX3D::PipeLine &pipe)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool cCell::DrawAlpha(olc::PixelGameEngine *pge, olc::GFX3D::PipeLine &pipe)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool cCell::DrawDebug(olc::PixelGameEngine *pge, olc::GFX3D::PipeLine &pipe)
|
||||
{
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void cCell::CalculateAdjacency()
|
||||
{
|
||||
|
||||
}
|
||||
@ -1,60 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "olcPixelGameEngine.h"
|
||||
#include "olcPGEX_Graphics3D.h"
|
||||
|
||||
#include "cAutomata.h"
|
||||
|
||||
|
||||
class cCityMap;
|
||||
|
||||
enum CellType
|
||||
{
|
||||
CELL_BLANK,
|
||||
CELL_GRASS,
|
||||
CELL_CONCRETE,
|
||||
CELL_WATER,
|
||||
CELL_BUILDING,
|
||||
CELL_ROAD,
|
||||
CELL_PAVEMENT,
|
||||
};
|
||||
|
||||
class cCell
|
||||
{
|
||||
public:
|
||||
cCell();
|
||||
cCell(cCityMap* map, int x, int y);
|
||||
~cCell();
|
||||
|
||||
protected:
|
||||
cCityMap* pMap = nullptr;
|
||||
|
||||
public:
|
||||
int nWorldX = 0;
|
||||
int nWorldY = 0;
|
||||
bool bSolid = false;
|
||||
CellType nCellType = CELL_BLANK;
|
||||
|
||||
// This cell may actuall be occupied by a multi-cell body
|
||||
// so this pointer points to the host cell that contains
|
||||
// that body
|
||||
cCell* pHostCell = nullptr;
|
||||
|
||||
// Each cell links to 20 automata transport nodes, 5 on each side
|
||||
cAuto_Node* pNaviNodes[49];
|
||||
|
||||
// Each cell can have a number of automata transport tracks, it owns them
|
||||
// These connect nodes together as determined by the cell
|
||||
std::list<cAuto_Track> listTracks;
|
||||
|
||||
public:
|
||||
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 Update(float fElapsedTime);
|
||||
virtual bool DrawBase(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);
|
||||
};
|
||||
|
||||
@ -1,53 +0,0 @@
|
||||
#include "cCell_Building.h"
|
||||
|
||||
|
||||
|
||||
cCell_Building::cCell_Building(const std::string &name, cCityMap* map, int x, int y) : cCell(map, x, y)
|
||||
{
|
||||
sName = name;
|
||||
}
|
||||
|
||||
|
||||
cCell_Building::~cCell_Building()
|
||||
{
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
texture = mapTextures[sName];
|
||||
mesh = mapMesh[sName];
|
||||
transform = mapTransforms[sName];
|
||||
return false;
|
||||
}
|
||||
|
||||
bool cCell_Building::Update(float fElapsedTime)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
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 matWorld = olc::GFX3D::Math::Mat_MultiplyMatrix(transform, matTranslate);
|
||||
pipe.SetTransform(matWorld);
|
||||
if (texture != nullptr)
|
||||
{
|
||||
pipe.SetTexture(texture);
|
||||
pipe.Render(mesh->tris,olc::GFX3D::RENDER_CULL_CW | olc::GFX3D::RENDER_DEPTH | olc::GFX3D::RENDER_TEXTURED | olc::GFX3D::RENDER_LIGHTS);
|
||||
}
|
||||
else
|
||||
{
|
||||
pipe.Render(mesh->tris, olc::GFX3D::RENDER_CULL_CW | olc::GFX3D::RENDER_DEPTH | olc::GFX3D::RENDER_FLAT | olc::GFX3D::RENDER_LIGHTS);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool cCell_Building::DrawAlpha(olc::PixelGameEngine * pge, olc::GFX3D::PipeLine & pipe)
|
||||
{
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -1,25 +0,0 @@
|
||||
#pragma once
|
||||
#include "cCell.h"
|
||||
#include "olcPGEX_Graphics3D.h"
|
||||
|
||||
|
||||
class cCell_Building : public cCell
|
||||
{
|
||||
public:
|
||||
cCell_Building(const std::string &name, cCityMap* map, int x, int y);
|
||||
~cCell_Building();
|
||||
|
||||
private:
|
||||
std::string sName;
|
||||
olc::Sprite* texture = nullptr;
|
||||
olc::GFX3D::mesh* mesh = nullptr;
|
||||
olc::GFX3D::mat4x4 transform;
|
||||
|
||||
public:
|
||||
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 Update(float fElapsedTime);
|
||||
virtual bool DrawBase(olc::PixelGameEngine *pge, olc::GFX3D::PipeLine &pipe);
|
||||
virtual bool DrawAlpha(olc::PixelGameEngine *pge, olc::GFX3D::PipeLine &pipe);
|
||||
};
|
||||
|
||||
@ -1,49 +0,0 @@
|
||||
#include "cCell_Plane.h"
|
||||
|
||||
|
||||
|
||||
cCell_Plane::cCell_Plane(cCityMap* map, int x, int y, CELL_PLANE type) : cCell(map, x, y)
|
||||
{
|
||||
bSolid = false;
|
||||
nType = type;
|
||||
if (nType == PLANE_GRASS) nCellType = CELL_GRASS;
|
||||
if (nType == PLANE_ASPHALT) nCellType = CELL_PAVEMENT;
|
||||
}
|
||||
|
||||
|
||||
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)
|
||||
{
|
||||
sprGrass = mapTextures["Grass"];
|
||||
sprPavement = mapTextures["Pavement"];
|
||||
meshUnitQuad = mapMesh["UnitQuad"];
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cCell_Plane::Update(float fElapsedTime)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool cCell_Plane::DrawBase(olc::PixelGameEngine *pge, olc::GFX3D::PipeLine &pipe)
|
||||
{
|
||||
olc::GFX3D::mat4x4 matWorld;
|
||||
matWorld = olc::GFX3D::Math::Mat_MakeTranslation((float)nWorldX, (float)nWorldY, 0.0f);
|
||||
pipe.SetTransform(matWorld);
|
||||
|
||||
if(nType == PLANE_GRASS)
|
||||
pipe.SetTexture(sprGrass);
|
||||
else
|
||||
pipe.SetTexture(sprPavement);
|
||||
|
||||
pipe.Render(meshUnitQuad->tris, olc::GFX3D::RENDER_CULL_CW | olc::GFX3D::RENDER_DEPTH | olc::GFX3D::RENDER_TEXTURED);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool cCell_Plane::DrawAlpha(olc::PixelGameEngine *pge, olc::GFX3D::PipeLine &pipe)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -1,34 +0,0 @@
|
||||
#pragma once
|
||||
#include "cCell.h"
|
||||
#include "olcPixelGameEngine.h"
|
||||
#include "olcPGEX_Graphics3D.h"
|
||||
#include <map>
|
||||
|
||||
|
||||
enum CELL_PLANE
|
||||
{
|
||||
PLANE_GRASS,
|
||||
PLANE_ASPHALT
|
||||
};
|
||||
|
||||
class cCell_Plane : public cCell
|
||||
{
|
||||
public:
|
||||
cCell_Plane(cCityMap* map, int x, int y, CELL_PLANE type);
|
||||
~cCell_Plane();
|
||||
|
||||
protected:
|
||||
CELL_PLANE nType = PLANE_GRASS;
|
||||
|
||||
private:
|
||||
olc::GFX3D::mesh* meshUnitQuad = nullptr;
|
||||
olc::Sprite* sprGrass = nullptr;
|
||||
olc::Sprite* sprPavement = nullptr;
|
||||
|
||||
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 Update(float fElapsedTime);
|
||||
virtual bool DrawBase(olc::PixelGameEngine *pge, olc::GFX3D::PipeLine &pipe);
|
||||
virtual bool DrawAlpha(olc::PixelGameEngine *pge, olc::GFX3D::PipeLine &pipe);
|
||||
};
|
||||
|
||||
@ -1,812 +0,0 @@
|
||||
#include "cCell_Road.h"
|
||||
#include "cCityMap.h"
|
||||
|
||||
|
||||
cCell_Road::cCell_Road(cCityMap* map, int x, int y) : cCell(map, x, y)
|
||||
{
|
||||
bSolid = false;
|
||||
nCellType = CELL_ROAD;
|
||||
}
|
||||
|
||||
cCell_Road::~cCell_Road()
|
||||
{
|
||||
}
|
||||
|
||||
void cCell_Road::CalculateAdjacency()
|
||||
{
|
||||
|
||||
// Calculate suitable road junction type
|
||||
auto r = [&](int i, int j)
|
||||
{
|
||||
return (pMap->Cell(nWorldX + i, nWorldY + j) != nullptr && pMap->Cell(nWorldX + i, nWorldY + j)->nCellType == CELL_ROAD);
|
||||
};
|
||||
|
||||
if (r(0, -1) && r(0, +1) && !r(-1, 0) && !r(+1, 0)) nRoadType = ROAD_V;
|
||||
if (!r(0, -1) && !r(0, +1) && r(-1, 0) && r(+1, 0)) nRoadType =ROAD_H;
|
||||
if (!r(0, -1) && r(0, +1) && !r(-1, 0) && r(+1, 0)) nRoadType = ROAD_C1;
|
||||
if (!r(0, -1) && r(0, +1) && r(-1, 0) && r(+1, 0)) nRoadType =ROAD_T1;
|
||||
if (!r(0, -1) && r(0, +1) && r(-1, 0) && !r(+1, 0)) nRoadType = ROAD_C2;
|
||||
if (r(0, -1) && r(0, +1) && !r(-1, 0) && r(+1, 0)) nRoadType = ROAD_T2;
|
||||
if (r(0, -1) && r(0, +1) && r(-1, 0) && r(+1, 0)) nRoadType = ROAD_X;
|
||||
if (r(0, -1) && r(0, +1) && r(-1, 0) && !r(+1, 0)) nRoadType = ROAD_T3;
|
||||
if (r(0, -1) && !r(0, +1) && !r(-1, 0) && r(+1, 0)) nRoadType = ROAD_C3;
|
||||
if (r(0, -1) && !r(0, +1) && r(-1, 0) && r(+1, 0)) nRoadType = ROAD_T4;
|
||||
if (r(0, -1) && !r(0, +1) && r(-1, 0) && !r(+1, 0)) nRoadType = ROAD_C4;
|
||||
|
||||
// Add navigation tracks based on type
|
||||
|
||||
auto AddTrack = [&](int n1, int n2) -> cAuto_Track*
|
||||
{
|
||||
if (pNaviNodes[n1] == nullptr || pNaviNodes[n2] == nullptr)
|
||||
{
|
||||
// Can't add track
|
||||
return nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Nodes exist so add track
|
||||
cAuto_Track t;
|
||||
t.node[0] = pNaviNodes[n1];
|
||||
t.node[1] = pNaviNodes[n2];
|
||||
t.cell = this;
|
||||
t.fTrackLength = (pNaviNodes[n1]->pos - pNaviNodes[n2]->pos).mag();
|
||||
listTracks.push_back(t);
|
||||
|
||||
// Add pointers to track to start and end nodes
|
||||
pNaviNodes[n1]->listTracks.push_back(&listTracks.back());
|
||||
pNaviNodes[n2]->listTracks.push_back(&listTracks.back());
|
||||
|
||||
return &listTracks.back();
|
||||
}
|
||||
};
|
||||
|
||||
// Ensure list of tracks for this cell is clear
|
||||
listTracks.clear();
|
||||
|
||||
// Add tracks depending on junction type
|
||||
pSafePedestrianTrack = nullptr;
|
||||
pSafeCarTrack = nullptr;
|
||||
pSafeChaseTrack = nullptr;
|
||||
|
||||
// Add Pedestrian Tracks
|
||||
switch (nRoadType)
|
||||
{
|
||||
case ROAD_H: pSafePedestrianTrack = AddTrack(7, 13); AddTrack(41, 35); break;
|
||||
case ROAD_V: pSafePedestrianTrack = AddTrack(1, 43); AddTrack(5, 47); break;
|
||||
|
||||
case ROAD_C1: pSafePedestrianTrack = AddTrack(43, 8); AddTrack(8, 13); AddTrack(47, 40); AddTrack(40, 41); break;
|
||||
case ROAD_C2: AddTrack(7, 12); AddTrack(12, 47); pSafePedestrianTrack = AddTrack(35, 36); AddTrack(36, 43); break;
|
||||
case ROAD_C3: AddTrack(1, 36); pSafePedestrianTrack = AddTrack(36, 41); AddTrack(5, 12); AddTrack(12, 13); break;
|
||||
case ROAD_C4: AddTrack(35, 40); AddTrack(40, 5); pSafePedestrianTrack = AddTrack(7, 8); AddTrack(8, 1); break;
|
||||
|
||||
case ROAD_T1: pSafePedestrianTrack = AddTrack(7, 8); AddTrack(8, 12); AddTrack(12, 13); AddTrack(35, 36); AddTrack(36, 38); AddTrack(38, 40); AddTrack(40, 41); AddTrack(8, 22); AddTrack(22, 36); AddTrack(36, 43); AddTrack(12, 26); AddTrack(26, 40); AddTrack(40, 47); break;
|
||||
case ROAD_T2: pSafePedestrianTrack = AddTrack(1, 8); AddTrack(8, 36); AddTrack(36, 43); AddTrack(5, 12); AddTrack(12, 26); AddTrack(26, 40); AddTrack(40, 47); AddTrack(8, 10); AddTrack(10, 12); AddTrack(12, 13); AddTrack(36, 38), AddTrack(38, 40); AddTrack(40, 41); break;
|
||||
case ROAD_T3: pSafePedestrianTrack = AddTrack(5, 12); AddTrack(12, 40); AddTrack(40, 47); AddTrack(1, 8); AddTrack(8, 22); AddTrack(22, 36); AddTrack(36, 43); AddTrack(12, 10); AddTrack(10, 8); AddTrack(8, 7); AddTrack(40, 38); AddTrack(38, 36); AddTrack(36, 35); break;
|
||||
case ROAD_T4: pSafePedestrianTrack = AddTrack(35, 36); AddTrack(36, 40); AddTrack(40, 41); AddTrack(7, 8); AddTrack(8, 10); AddTrack(10, 12); AddTrack(12, 13); AddTrack(36, 22); AddTrack(22, 8); AddTrack(8, 1); AddTrack(40, 26); AddTrack(26, 12); AddTrack(12, 5); break;
|
||||
|
||||
case ROAD_X: AddTrack(35, 36); AddTrack(36, 38); AddTrack(38, 40); AddTrack(40, 41); AddTrack(7, 8); AddTrack(8, 10); AddTrack(10, 12); AddTrack(12, 13); AddTrack(36, 22); AddTrack(22, 8); AddTrack(8, 1); AddTrack(40, 26); AddTrack(26, 12); AddTrack(12, 5); pSafePedestrianTrack = AddTrack(36, 43); AddTrack(40, 47); break;
|
||||
}
|
||||
|
||||
|
||||
// Add Chase Tracks
|
||||
switch (nRoadType)
|
||||
{
|
||||
case ROAD_H: AddTrack(21, 27); break;
|
||||
case ROAD_V: AddTrack(3, 45); break;
|
||||
|
||||
case ROAD_C1: AddTrack(45, 24); AddTrack(24, 27); break;
|
||||
case ROAD_C2: AddTrack(21, 24); AddTrack(24, 45); break;
|
||||
case ROAD_C3: AddTrack(3, 24); AddTrack(24, 27); break;
|
||||
case ROAD_C4: AddTrack(21, 24); AddTrack(24, 3); break;
|
||||
|
||||
case ROAD_T1: AddTrack(21, 24); AddTrack(24, 27); AddTrack(24, 45); break;
|
||||
case ROAD_T2: AddTrack(3, 24); AddTrack(24, 45); AddTrack(24, 27); break;
|
||||
case ROAD_T3: AddTrack(3, 24); AddTrack(24, 45); AddTrack(24, 21); break;
|
||||
case ROAD_T4: AddTrack(21, 24); AddTrack(24, 27); AddTrack(24, 3); break;
|
||||
|
||||
case ROAD_X: AddTrack(3, 24); AddTrack(27, 24); AddTrack(45, 24); AddTrack(21, 24); break;
|
||||
}
|
||||
|
||||
|
||||
//// Road traffic tracks
|
||||
switch (nRoadType)
|
||||
{
|
||||
case ROAD_H: pSafeCarTrack = AddTrack(14, 20); AddTrack(28, 34); break;
|
||||
case ROAD_V: AddTrack(2, 44); pSafeCarTrack = AddTrack(4, 46); break;
|
||||
|
||||
case ROAD_C1: pSafeCarTrack = AddTrack(44, 16); AddTrack(16, 20); AddTrack(46, 32); AddTrack(32, 34); break;
|
||||
case ROAD_C2: pSafeCarTrack = AddTrack(14, 18); AddTrack(18, 46); AddTrack(28, 30); AddTrack(30, 44); break;
|
||||
case ROAD_C3: AddTrack(2, 30); AddTrack(30, 34); pSafeCarTrack = AddTrack(4, 18); AddTrack(18, 20); break;
|
||||
case ROAD_C4: AddTrack(2, 16); AddTrack(16, 14); pSafeCarTrack = AddTrack(4, 32); AddTrack(32, 28); break;
|
||||
|
||||
|
||||
case ROAD_T1: AddTrack(14, 16); AddTrack(16, 18); AddTrack(18, 20); AddTrack(28, 30); AddTrack(30, 32); AddTrack(32, 34);
|
||||
AddTrack(16, 30); AddTrack(30, 44); AddTrack(18, 32); AddTrack(32, 46); break;
|
||||
|
||||
case ROAD_T4: AddTrack(14, 16); AddTrack(16, 18); AddTrack(18, 20); AddTrack(28, 30); AddTrack(30, 32); AddTrack(32, 34);
|
||||
AddTrack(16, 30); AddTrack(16, 2); AddTrack(18, 32); AddTrack(18, 4); break;
|
||||
|
||||
case ROAD_T2: AddTrack(2, 16); AddTrack(16, 30); AddTrack(30, 44); AddTrack(4, 18); AddTrack(18, 32); AddTrack(32, 46);
|
||||
AddTrack(16, 18); AddTrack(18, 20); AddTrack(30, 32); AddTrack(32, 34); break;
|
||||
|
||||
case ROAD_T3: AddTrack(2, 16); AddTrack(16, 30); AddTrack(30, 44); AddTrack(4, 18); AddTrack(18, 32); AddTrack(32, 46);
|
||||
AddTrack(14, 16); AddTrack(16, 18); AddTrack(28, 30); AddTrack(30, 32); break;
|
||||
|
||||
case ROAD_X:
|
||||
AddTrack(2, 16); AddTrack(16, 30); AddTrack(30, 44); AddTrack(4, 18); AddTrack(18, 32); AddTrack(32, 46);
|
||||
AddTrack(14, 16); AddTrack(16, 18); AddTrack(18, 20); AddTrack(28, 30); AddTrack(30, 32); AddTrack(32, 34); break;
|
||||
}
|
||||
|
||||
|
||||
// Stop Patterns, here we go, loads of data... :(
|
||||
|
||||
// .PO.OP.
|
||||
// PP.P.PP
|
||||
// O.O.O.O
|
||||
// .P...P.
|
||||
// O.O.O.O
|
||||
// PP.P.PP
|
||||
// .PO.OP.
|
||||
|
||||
// .PO.OP.
|
||||
// PP.P.PP
|
||||
// O.X.X.O
|
||||
// .P...P.
|
||||
// O.X.X.O
|
||||
// PP.P.PP
|
||||
// .PO.OP.
|
||||
|
||||
// .PO.OP.
|
||||
// PP.X.PP
|
||||
// O.X.X.O
|
||||
// .X...X.
|
||||
// O.X.X.O
|
||||
// PP.X.PP
|
||||
// .PO.OP.
|
||||
|
||||
auto stopmap = [&](const std::string &s)
|
||||
{
|
||||
StopPattern p;
|
||||
for (size_t i = 0; i < s.size(); i++)
|
||||
p.bStop[i] = (s[i] == 'X');
|
||||
return p;
|
||||
};
|
||||
|
||||
switch (nRoadType)
|
||||
{
|
||||
case ROAD_H:
|
||||
case ROAD_V:
|
||||
case ROAD_C1:
|
||||
case ROAD_C2:
|
||||
case ROAD_C3:
|
||||
case ROAD_C4:
|
||||
// Allow all
|
||||
/*vStopPattern.push_back(
|
||||
stopmap(
|
||||
".PO.OP."
|
||||
"PP.P.PP"
|
||||
"O.O.O.O"
|
||||
".P...P."
|
||||
"O.O.O.O"
|
||||
"PP.P.PP"
|
||||
".PO.OP."));*/
|
||||
break;
|
||||
|
||||
case ROAD_X:
|
||||
// Allow Pedestrians
|
||||
vStopPattern.push_back(
|
||||
stopmap(
|
||||
".PX.XP."
|
||||
"PP.P.PP"
|
||||
"X.X.X.X"
|
||||
".P...P."
|
||||
"X.X.X.X"
|
||||
"PP.P.PP"
|
||||
".PX.XP."));
|
||||
// Drain Pedestrians
|
||||
vStopPattern.push_back(
|
||||
stopmap(
|
||||
".PX.XP."
|
||||
"PP.X.PP"
|
||||
"X.X.X.X"
|
||||
".X...X."
|
||||
"X.X.X.X"
|
||||
"PP.X.PP"
|
||||
".PX.XP."));
|
||||
// Allow West Traffic
|
||||
vStopPattern.push_back(
|
||||
stopmap(
|
||||
".PO.XP."
|
||||
"PP.X.PP"
|
||||
"O.O.O.O"
|
||||
".X...X."
|
||||
"X.X.O.X"
|
||||
"PP.X.PP"
|
||||
".PX.OP."));
|
||||
// Drain West Traffic
|
||||
vStopPattern.push_back(
|
||||
stopmap(
|
||||
".PO.XP."
|
||||
"PP.X.PP"
|
||||
"X.O.O.O"
|
||||
".X...X."
|
||||
"X.X.O.X"
|
||||
"PP.X.PP"
|
||||
".PX.OP."));
|
||||
// Allow North Traffic
|
||||
vStopPattern.push_back(
|
||||
stopmap(
|
||||
".PX.OP."
|
||||
"PP.X.PP"
|
||||
"X.X.O.O"
|
||||
".X...X."
|
||||
"O.O.O.X"
|
||||
"PP.X.PP"
|
||||
".PX.OP."));
|
||||
// Drain North Traffic
|
||||
vStopPattern.push_back(
|
||||
stopmap(
|
||||
".PX.XP."
|
||||
"PP.X.PP"
|
||||
"X.X.O.O"
|
||||
".X...X."
|
||||
"O.O.O.X"
|
||||
"PP.X.PP"
|
||||
".PX.OP."));
|
||||
// Allow Pedestrians
|
||||
vStopPattern.push_back(
|
||||
stopmap(
|
||||
".PX.XP."
|
||||
"PP.P.PP"
|
||||
"X.X.X.X"
|
||||
".P...P."
|
||||
"X.X.X.X"
|
||||
"PP.P.PP"
|
||||
".PX.XP."));
|
||||
// Drain Pedestrians
|
||||
vStopPattern.push_back(
|
||||
stopmap(
|
||||
".PX.XP."
|
||||
"PP.X.PP"
|
||||
"X.X.X.X"
|
||||
".X...X."
|
||||
"X.X.X.X"
|
||||
"PP.X.PP"
|
||||
".PX.XP."));
|
||||
// Allow EAST Traffic
|
||||
vStopPattern.push_back(
|
||||
stopmap(
|
||||
".PO.XP."
|
||||
"PP.X.PP"
|
||||
"X.O.X.X"
|
||||
".X...X."
|
||||
"O.O.O.O"
|
||||
"PP.X.PP"
|
||||
".PX.OP."));
|
||||
// Drain East Traffic
|
||||
vStopPattern.push_back(
|
||||
stopmap(
|
||||
".PO.XP."
|
||||
"PP.X.PP"
|
||||
"X.O.X.X"
|
||||
".X...X."
|
||||
"O.O.O.X"
|
||||
"PP.X.PP"
|
||||
".PX.OP."));
|
||||
// Allow SOUTH Traffic
|
||||
vStopPattern.push_back(
|
||||
stopmap(
|
||||
".PO.XP."
|
||||
"PP.X.PP"
|
||||
"X.O.O.O"
|
||||
".X...X."
|
||||
"O.O.X.X"
|
||||
"PP.X.PP"
|
||||
".PO.XP."));
|
||||
// Drain SOUTH Traffic
|
||||
vStopPattern.push_back(
|
||||
stopmap(
|
||||
".PO.XP."
|
||||
"PP.X.PP"
|
||||
"X.O.O.O"
|
||||
".X...X."
|
||||
"O.O.X.X"
|
||||
"PP.X.PP"
|
||||
".PX.XP."));
|
||||
|
||||
break;
|
||||
|
||||
case ROAD_T1:
|
||||
// Allow Pedestrians
|
||||
vStopPattern.push_back(
|
||||
stopmap(
|
||||
".PX.XP."
|
||||
"PP.P.PP"
|
||||
"X.X.X.X"
|
||||
".P...P."
|
||||
"X.X.X.X"
|
||||
"PP.P.PP"
|
||||
".PX.XP."));
|
||||
// Drain Pedestrians
|
||||
vStopPattern.push_back(
|
||||
stopmap(
|
||||
".PX.XP."
|
||||
"PP.P.PP"
|
||||
"X.X.X.X"
|
||||
".X...X."
|
||||
"X.X.X.X"
|
||||
"PP.X.PP"
|
||||
".PX.XP."));
|
||||
// Allow West Traffic
|
||||
vStopPattern.push_back(
|
||||
stopmap(
|
||||
".PX.XP."
|
||||
"PP.P.PP"
|
||||
"O.O.O.O"
|
||||
".X...X."
|
||||
"X.X.O.X"
|
||||
"PP.X.PP"
|
||||
".PX.OP."));
|
||||
// Drain West Traffic
|
||||
vStopPattern.push_back(
|
||||
stopmap(
|
||||
".PX.XP."
|
||||
"PP.P.PP"
|
||||
"X.O.O.O"
|
||||
".X...X."
|
||||
"X.X.O.X"
|
||||
"PP.X.PP"
|
||||
".PX.OP."));
|
||||
// Allow Pedestrians
|
||||
vStopPattern.push_back(
|
||||
stopmap(
|
||||
".PX.XP."
|
||||
"PP.P.PP"
|
||||
"X.X.X.X"
|
||||
".P...P."
|
||||
"X.X.X.X"
|
||||
"PP.P.PP"
|
||||
".PX.XP."));
|
||||
// Drain Pedestrians
|
||||
vStopPattern.push_back(
|
||||
stopmap(
|
||||
".PX.XP."
|
||||
"PP.X.PP"
|
||||
"X.X.X.X"
|
||||
".X...X."
|
||||
"X.X.X.X"
|
||||
"PP.X.PP"
|
||||
".PX.XP."));
|
||||
// Allow EAST Traffic
|
||||
vStopPattern.push_back(
|
||||
stopmap(
|
||||
".PX.XP."
|
||||
"PP.P.PP"
|
||||
"X.X.X.X"
|
||||
".X...X."
|
||||
"O.O.O.O"
|
||||
"PP.X.PP"
|
||||
".PX.OP."));
|
||||
// Drain East Traffic
|
||||
vStopPattern.push_back(
|
||||
stopmap(
|
||||
".PX.XP."
|
||||
"PP.P.PP"
|
||||
"X.X.X.X"
|
||||
".X...X."
|
||||
"O.O.O.X"
|
||||
"PP.X.PP"
|
||||
".PX.OP."));
|
||||
// Allow SOUTH Traffic
|
||||
vStopPattern.push_back(
|
||||
stopmap(
|
||||
".PX.XP."
|
||||
"PP.P.PP"
|
||||
"X.O.O.O"
|
||||
".X...X."
|
||||
"O.O.X.X"
|
||||
"PP.X.PP"
|
||||
".PO.XP."));
|
||||
// Drain SOUTH Traffic
|
||||
vStopPattern.push_back(
|
||||
stopmap(
|
||||
".PX.XP."
|
||||
"PP.P.PP"
|
||||
"X.O.O.O"
|
||||
".X...X."
|
||||
"O.O.X.X"
|
||||
"PP.X.PP"
|
||||
".PX.XP."));
|
||||
break;
|
||||
|
||||
case ROAD_T2:
|
||||
// Allow Pedestrians
|
||||
vStopPattern.push_back(
|
||||
stopmap(
|
||||
".PX.XP."
|
||||
"PP.P.PP"
|
||||
"X.X.X.X"
|
||||
".P...P."
|
||||
"X.X.X.X"
|
||||
"PP.P.PP"
|
||||
".PX.XP."));
|
||||
// Drain Pedestrians
|
||||
vStopPattern.push_back(
|
||||
stopmap(
|
||||
".PX.XP."
|
||||
"PP.X.PP"
|
||||
"X.X.X.X"
|
||||
".P...X."
|
||||
"X.X.X.X"
|
||||
"PP.X.PP"
|
||||
".PX.XP."));
|
||||
// Allow North Traffic
|
||||
vStopPattern.push_back(
|
||||
stopmap(
|
||||
".PX.OP."
|
||||
"PP.X.PP"
|
||||
"X.X.O.O"
|
||||
".P...X."
|
||||
"X.X.O.X"
|
||||
"PP.X.PP"
|
||||
".PX.OP."));
|
||||
// Drain North Traffic
|
||||
vStopPattern.push_back(
|
||||
stopmap(
|
||||
".PX.XP."
|
||||
"PP.X.PP"
|
||||
"X.X.O.O"
|
||||
".P...X."
|
||||
"X.X.O.X"
|
||||
"PP.X.PP"
|
||||
".PX.OP."));
|
||||
// Allow Pedestrians
|
||||
vStopPattern.push_back(
|
||||
stopmap(
|
||||
".PX.XP."
|
||||
"PP.P.PP"
|
||||
"X.X.X.X"
|
||||
".P...P."
|
||||
"X.X.X.X"
|
||||
"PP.P.PP"
|
||||
".PX.XP."));
|
||||
// Drain Pedestrians
|
||||
vStopPattern.push_back(
|
||||
stopmap(
|
||||
".PX.XP."
|
||||
"PP.X.PP"
|
||||
"X.X.X.X"
|
||||
".X...X."
|
||||
"X.X.X.X"
|
||||
"PP.X.PP"
|
||||
".PX.XP."));
|
||||
// Allow EAST Traffic
|
||||
vStopPattern.push_back(
|
||||
stopmap(
|
||||
".PO.XP."
|
||||
"PP.X.PP"
|
||||
"X.O.X.X"
|
||||
".P...X."
|
||||
"X.O.O.O"
|
||||
"PP.X.PP"
|
||||
".PX.OP."));
|
||||
// Drain East Traffic
|
||||
vStopPattern.push_back(
|
||||
stopmap(
|
||||
".PO.XP."
|
||||
"PP.X.PP"
|
||||
"X.O.X.X"
|
||||
".P...X."
|
||||
"X.O.O.X"
|
||||
"PP.X.PP"
|
||||
".PX.OP."));
|
||||
// Allow SOUTH Traffic
|
||||
vStopPattern.push_back(
|
||||
stopmap(
|
||||
".PO.XP."
|
||||
"PP.X.PP"
|
||||
"X.O.O.O"
|
||||
".P...X."
|
||||
"X.O.X.X"
|
||||
"PP.X.PP"
|
||||
".PO.XP."));
|
||||
// Drain SOUTH Traffic
|
||||
vStopPattern.push_back(
|
||||
stopmap(
|
||||
".PO.XP."
|
||||
"PP.X.PP"
|
||||
"X.O.O.O"
|
||||
".P...X."
|
||||
"X.O.X.X"
|
||||
"PP.X.PP"
|
||||
".PX.XP."));
|
||||
break;
|
||||
case ROAD_T3:
|
||||
// Allow Pedestrians
|
||||
vStopPattern.push_back(
|
||||
stopmap(
|
||||
".PX.XP."
|
||||
"PP.P.PP"
|
||||
"X.X.X.X"
|
||||
".P...P."
|
||||
"X.X.X.X"
|
||||
"PP.P.PP"
|
||||
".PX.XP."));
|
||||
// Drain Pedestrians
|
||||
vStopPattern.push_back(
|
||||
stopmap(
|
||||
".PX.XP."
|
||||
"PP.X.PP"
|
||||
"X.X.X.X"
|
||||
".X...P."
|
||||
"X.X.X.X"
|
||||
"PP.X.PP"
|
||||
".PX.XP."));
|
||||
// Allow West Traffic
|
||||
vStopPattern.push_back(
|
||||
stopmap(
|
||||
".PO.XP."
|
||||
"PP.X.PP"
|
||||
"O.O.O.X"
|
||||
".X...P."
|
||||
"X.X.O.X"
|
||||
"PP.X.PP"
|
||||
".PX.OP."));
|
||||
// Drain West Traffic
|
||||
vStopPattern.push_back(
|
||||
stopmap(
|
||||
".PO.XP."
|
||||
"PP.X.PP"
|
||||
"X.O.O.X"
|
||||
".X...P."
|
||||
"X.X.O.X"
|
||||
"PP.X.PP"
|
||||
".PX.OP."));
|
||||
// Allow Pedestrians
|
||||
vStopPattern.push_back(
|
||||
stopmap(
|
||||
".PX.XP."
|
||||
"PP.P.PP"
|
||||
"X.X.X.X"
|
||||
".P...P."
|
||||
"X.X.X.X"
|
||||
"PP.P.PP"
|
||||
".PX.XP."));
|
||||
// Drain Pedestrians
|
||||
vStopPattern.push_back(
|
||||
stopmap(
|
||||
".PX.XP."
|
||||
"PP.X.PP"
|
||||
"X.X.X.X"
|
||||
".X...X."
|
||||
"X.X.X.X"
|
||||
"PP.X.PP"
|
||||
".PX.XP."));
|
||||
// Allow North Traffic
|
||||
vStopPattern.push_back(
|
||||
stopmap(
|
||||
".PX.OP."
|
||||
"PP.X.PP"
|
||||
"X.X.O.X"
|
||||
".X...P."
|
||||
"O.O.O.X"
|
||||
"PP.X.PP"
|
||||
".PX.OP."));
|
||||
// Drain North Traffic
|
||||
vStopPattern.push_back(
|
||||
stopmap(
|
||||
".PX.XP."
|
||||
"PP.X.PP"
|
||||
"X.X.O.X"
|
||||
".X...P."
|
||||
"O.O.O.X"
|
||||
"PP.X.PP"
|
||||
".PX.OP."));
|
||||
|
||||
// Allow SOUTH Traffic
|
||||
vStopPattern.push_back(
|
||||
stopmap(
|
||||
".PO.XP."
|
||||
"PP.X.PP"
|
||||
"X.O.X.X"
|
||||
".X...P."
|
||||
"O.O.X.X"
|
||||
"PP.X.PP"
|
||||
".PO.XP."));
|
||||
// Drain SOUTH Traffic
|
||||
vStopPattern.push_back(
|
||||
stopmap(
|
||||
".PO.XP."
|
||||
"PP.X.PP"
|
||||
"X.O.X.X"
|
||||
".X...P."
|
||||
"O.O.X.X"
|
||||
"PP.X.PP"
|
||||
".PX.XP."));
|
||||
break;
|
||||
|
||||
case ROAD_T4:
|
||||
// Allow Pedestrians
|
||||
vStopPattern.push_back(
|
||||
stopmap(
|
||||
".PX.XP."
|
||||
"PP.P.PP"
|
||||
"X.X.X.X"
|
||||
".P...P."
|
||||
"X.X.X.X"
|
||||
"PP.P.PP"
|
||||
".PX.XP."));
|
||||
// Drain Pedestrians
|
||||
vStopPattern.push_back(
|
||||
stopmap(
|
||||
".PX.XP."
|
||||
"PP.X.PP"
|
||||
"X.X.X.X"
|
||||
".X...X."
|
||||
"X.X.X.X"
|
||||
"PP.P.PP"
|
||||
".PX.XP."));
|
||||
// Allow West Traffic
|
||||
vStopPattern.push_back(
|
||||
stopmap(
|
||||
".PO.XP."
|
||||
"PP.X.PP"
|
||||
"O.O.O.O"
|
||||
".X...X."
|
||||
"X.X.X.X"
|
||||
"PP.P.PP"
|
||||
".PX.XP."));
|
||||
// Drain West Traffic
|
||||
vStopPattern.push_back(
|
||||
stopmap(
|
||||
".PO.XP."
|
||||
"PP.X.PP"
|
||||
"X.O.O.O"
|
||||
".X...X."
|
||||
"X.X.X.X"
|
||||
"PP.P.PP"
|
||||
".PX.XP."));
|
||||
// Allow North Traffic
|
||||
vStopPattern.push_back(
|
||||
stopmap(
|
||||
".PX.OP."
|
||||
"PP.X.PP"
|
||||
"X.X.O.O"
|
||||
".X...X."
|
||||
"O.O.O.X"
|
||||
"PP.P.PP"
|
||||
".PX.XP."));
|
||||
// Drain North Traffic
|
||||
vStopPattern.push_back(
|
||||
stopmap(
|
||||
".PX.XP."
|
||||
"PP.X.PP"
|
||||
"X.X.O.O"
|
||||
".X...X."
|
||||
"O.O.O.X"
|
||||
"PP.P.PP"
|
||||
".PX.XP."));
|
||||
// Allow Pedestrians
|
||||
vStopPattern.push_back(
|
||||
stopmap(
|
||||
".PX.XP."
|
||||
"PP.P.PP"
|
||||
"X.X.X.X"
|
||||
".P...P."
|
||||
"X.X.X.X"
|
||||
"PP.P.PP"
|
||||
".PX.XP."));
|
||||
// Drain Pedestrians
|
||||
vStopPattern.push_back(
|
||||
stopmap(
|
||||
".PX.XP."
|
||||
"PP.X.PP"
|
||||
"X.X.X.X"
|
||||
".X...X."
|
||||
"X.X.X.X"
|
||||
"PP.X.PP"
|
||||
".PX.XP."));
|
||||
// Allow EAST Traffic
|
||||
vStopPattern.push_back(
|
||||
stopmap(
|
||||
".PO.XP."
|
||||
"PP.X.PP"
|
||||
"X.O.X.X"
|
||||
".X...X."
|
||||
"O.O.O.O"
|
||||
"PP.P.PP"
|
||||
".PX.XP."));
|
||||
// Drain East Traffic
|
||||
vStopPattern.push_back(
|
||||
stopmap(
|
||||
".PO.XP."
|
||||
"PP.X.PP"
|
||||
"X.O.X.X"
|
||||
".X...X."
|
||||
"O.O.O.X"
|
||||
"PP.P.PP"
|
||||
".PX.XP."));
|
||||
break;
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool cCell_Road::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"];
|
||||
sprRoadTex[ROAD_V] = mapTextures["Road_V"];
|
||||
sprRoadTex[ROAD_H] = mapTextures["Road_H"];
|
||||
sprRoadTex[ROAD_C1] = mapTextures["Road_C1"];
|
||||
sprRoadTex[ROAD_T1] = mapTextures["Road_T1"];
|
||||
sprRoadTex[ROAD_C2] = mapTextures["Road_C2"];
|
||||
sprRoadTex[ROAD_T2] = mapTextures["Road_T2"];
|
||||
sprRoadTex[ROAD_X] = mapTextures["Road_X"];
|
||||
sprRoadTex[ROAD_T3] = mapTextures["Road_T3"];
|
||||
sprRoadTex[ROAD_C3] = mapTextures["Road_C3"];
|
||||
sprRoadTex[ROAD_T4] = mapTextures["Road_T4"];
|
||||
sprRoadTex[ROAD_C4] = mapTextures["Road_C4"];
|
||||
return false;
|
||||
}
|
||||
|
||||
bool cCell_Road::Update(float fElapsedTime)
|
||||
{
|
||||
if (vStopPattern.empty())
|
||||
return false;
|
||||
|
||||
fStopPatternTimer += fElapsedTime;
|
||||
if (fStopPatternTimer >= 5.0f)
|
||||
{
|
||||
fStopPatternTimer -= 5.0f;
|
||||
nCurrentStopPattern++;
|
||||
nCurrentStopPattern %= vStopPattern.size();
|
||||
for (int i = 0; i < 49; i++)
|
||||
if(pNaviNodes[i] != nullptr)
|
||||
pNaviNodes[i]->bBlock = vStopPattern[nCurrentStopPattern].bStop[i];
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool cCell_Road::DrawBase(olc::PixelGameEngine* pge, olc::GFX3D::PipeLine & pipe)
|
||||
{
|
||||
olc::GFX3D::mat4x4 matWorld;
|
||||
matWorld = olc::GFX3D::Math::Mat_MakeTranslation((float)nWorldX, (float)nWorldY, 0.0f);
|
||||
pipe.SetTransform(matWorld);
|
||||
pipe.SetTexture(sprRoadTex[nRoadType]);
|
||||
pipe.Render(meshUnitQuad->tris, olc::GFX3D::RENDER_CULL_CW | olc::GFX3D::RENDER_DEPTH | olc::GFX3D::RENDER_TEXTURED);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool cCell_Road::DrawAlpha(olc::PixelGameEngine* pge, olc::GFX3D::PipeLine & pipe)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool cCell_Road::DrawDebug(olc::PixelGameEngine* pge, olc::GFX3D::PipeLine & pipe)
|
||||
{
|
||||
|
||||
|
||||
|
||||
// Draw Automata navigation tracks
|
||||
for (auto &track : listTracks)
|
||||
{
|
||||
olc::GFX3D::vec3d p1 = { track.node[0]->pos.x, track.node[0]->pos.y, 0.0f };
|
||||
olc::GFX3D::vec3d p2 = { track.node[1]->pos.x, track.node[1]->pos.y, 0.0f };
|
||||
pipe.RenderLine(p1, p2, olc::CYAN);
|
||||
}
|
||||
|
||||
|
||||
for (int i = 0; i < 49; i++)
|
||||
{
|
||||
if (pNaviNodes[i] != nullptr)
|
||||
{
|
||||
olc::GFX3D::vec3d p1 = { pNaviNodes[i]->pos.x, pNaviNodes[i]->pos.y, 0.01f };
|
||||
pipe.RenderCircleXZ(p1, 0.03f, pNaviNodes[i]->bBlock ? olc::RED : olc::GREEN);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -1,54 +0,0 @@
|
||||
#pragma once
|
||||
#include "cCell.h"
|
||||
|
||||
enum RoadType
|
||||
{
|
||||
ROAD_H,
|
||||
ROAD_V,
|
||||
ROAD_C1,
|
||||
ROAD_C2,
|
||||
ROAD_C3,
|
||||
ROAD_C4,
|
||||
ROAD_T1,
|
||||
ROAD_T2,
|
||||
ROAD_T3,
|
||||
ROAD_T4,
|
||||
ROAD_X,
|
||||
};
|
||||
|
||||
|
||||
class cCell_Road : public cCell
|
||||
{
|
||||
public:
|
||||
cCell_Road(cCityMap* map, int x, int y);
|
||||
~cCell_Road();
|
||||
|
||||
private:
|
||||
struct StopPattern
|
||||
{
|
||||
bool bStop[49];
|
||||
};
|
||||
|
||||
private:
|
||||
bool bNeighboursAreRoads[4];
|
||||
|
||||
olc::GFX3D::mesh *meshUnitQuad = nullptr;
|
||||
olc::Sprite* sprRoadTex[11];
|
||||
|
||||
std::vector<StopPattern> vStopPattern;
|
||||
int nCurrentStopPattern = 0;
|
||||
float fStopPatternTimer = 0.0f;
|
||||
public:
|
||||
RoadType nRoadType = ROAD_X;
|
||||
cAuto_Track* pSafeCarTrack = nullptr;
|
||||
cAuto_Track* pSafePedestrianTrack = nullptr;
|
||||
cAuto_Track* pSafeChaseTrack = nullptr;
|
||||
|
||||
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 Update(float fElapsedTime);
|
||||
virtual bool DrawBase(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);
|
||||
};
|
||||
|
||||
@ -1,91 +0,0 @@
|
||||
#include "cCell_Water.h"
|
||||
#include "cCityMap.h"
|
||||
|
||||
|
||||
cCell_Water::cCell_Water(cCityMap* map, int x, int y) : cCell(map, x, y)
|
||||
{
|
||||
nCellType = CELL_WATER;
|
||||
bNeighboursAreWater[0] = false;
|
||||
bNeighboursAreWater[1] = false;
|
||||
bNeighboursAreWater[2] = false;
|
||||
bNeighboursAreWater[3] = false;
|
||||
}
|
||||
|
||||
|
||||
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)
|
||||
{
|
||||
meshUnitQuad = mapMesh["UnitQuad"];
|
||||
meshWalls = mapMesh["WallsOut"];
|
||||
sprWater = mapTextures["Water"];
|
||||
sprSides = mapTextures["WaterSide"];
|
||||
sprClouds = mapTextures["Clouds"];
|
||||
return false;
|
||||
}
|
||||
|
||||
bool cCell_Water::Update(float fElapsedTime)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool cCell_Water::DrawBase(olc::PixelGameEngine * pge, olc::GFX3D::PipeLine & pipe)
|
||||
{
|
||||
olc::GFX3D::mat4x4 matWorld;
|
||||
matWorld = olc::GFX3D::Math::Mat_MakeTranslation((float)nWorldX, (float)nWorldY, 0.0f);
|
||||
pipe.SetTransform(matWorld);
|
||||
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[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[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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
float a = (float)(pSource.a / 255.0f) * 0.6f;
|
||||
float c = 1.0f - a;
|
||||
float r = a * (float)pSource.r + c * (float)pDest.r;
|
||||
float g = a * (float)pSource.g + c * (float)pDest.g;
|
||||
float b = a * (float)pSource.b + c * (float)pDest.b;
|
||||
|
||||
a = 0.4f;
|
||||
c = 1.0f - a;
|
||||
olc::Pixel sky = sprClouds->GetPixel(x, y);
|
||||
float sr = a * (float)sky.r + c * r;
|
||||
float sg = a * (float)sky.g + c * g;
|
||||
float sb = a * (float)sky.b + c * b;
|
||||
|
||||
return olc::Pixel((uint8_t)sr, (uint8_t)sg, (uint8_t)sb);
|
||||
};
|
||||
|
||||
pge->SetPixelMode(renderWater);
|
||||
olc::GFX3D::mat4x4 matWorld = olc::GFX3D::Math::Mat_MakeTranslation((float)nWorldX, (float)nWorldY, 0.07f);
|
||||
pipe.SetTransform(matWorld);
|
||||
pipe.SetTexture(sprWater);
|
||||
pipe.Render(meshUnitQuad->tris, olc::GFX3D::RENDER_CULL_CW | olc::GFX3D::RENDER_DEPTH | olc::GFX3D::RENDER_TEXTURED);
|
||||
pge->SetPixelMode(olc::Pixel::NORMAL);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void cCell_Water::CalculateAdjacency()
|
||||
{
|
||||
auto r = [&](int i, int j)
|
||||
{
|
||||
if (pMap->Cell(nWorldX + i, nWorldY + j) != nullptr)
|
||||
return pMap->Cell(nWorldX + i, nWorldY + j)->nCellType == CELL_WATER;
|
||||
else
|
||||
return false;
|
||||
};
|
||||
|
||||
bNeighboursAreWater[0] = r(0, -1);
|
||||
bNeighboursAreWater[1] = r(+1, 0);
|
||||
bNeighboursAreWater[2] = r(0, +1);
|
||||
bNeighboursAreWater[3] = r(-1, 0);
|
||||
}
|
||||
@ -1,25 +0,0 @@
|
||||
#pragma once
|
||||
#include "cCell.h"
|
||||
class cCell_Water : public cCell
|
||||
{
|
||||
public:
|
||||
cCell_Water(cCityMap* map, int x, int y);
|
||||
~cCell_Water();
|
||||
|
||||
private:
|
||||
olc::GFX3D::mesh* meshUnitQuad = nullptr;
|
||||
olc::GFX3D::mesh* meshWalls = nullptr;
|
||||
olc::Sprite* sprWater = nullptr;
|
||||
olc::Sprite* sprSides = nullptr;
|
||||
olc::Sprite* sprClouds = nullptr;
|
||||
|
||||
bool bNeighboursAreWater[4];
|
||||
|
||||
public:
|
||||
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 Update(float fElapsedTime);
|
||||
virtual bool DrawBase(olc::PixelGameEngine *pge, olc::GFX3D::PipeLine &pipe);
|
||||
virtual bool DrawAlpha(olc::PixelGameEngine *pge, olc::GFX3D::PipeLine &pipe);
|
||||
};
|
||||
|
||||
@ -1,202 +0,0 @@
|
||||
#include "cCityMap.h"
|
||||
|
||||
#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)
|
||||
{
|
||||
CreateCity(w, h, mapTextures, mapMesh, mapTransforms);
|
||||
}
|
||||
|
||||
cCityMap::~cCityMap()
|
||||
{
|
||||
ReleaseCity();
|
||||
}
|
||||
|
||||
int cCityMap::GetWidth()
|
||||
{
|
||||
return nWidth;
|
||||
}
|
||||
|
||||
int cCityMap::GetHeight()
|
||||
{
|
||||
return nHeight;
|
||||
}
|
||||
|
||||
cCell* cCityMap::Cell(int x, int y)
|
||||
{
|
||||
if (x >= 0 && x < nWidth && y >= 0 && y < nHeight)
|
||||
return pCells[y*nWidth + x];
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
cCell* cCityMap::Replace(int x, int y, cCell* cell)
|
||||
{
|
||||
if (cell == nullptr)
|
||||
return nullptr;
|
||||
|
||||
if (pCells[y * nWidth + x] != nullptr)
|
||||
delete pCells[y * nWidth + x];
|
||||
|
||||
pCells[y * nWidth + x] = 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)
|
||||
{
|
||||
ReleaseCity();
|
||||
nWidth = w;
|
||||
nHeight = h;
|
||||
pCells = new cCell*[nHeight * nWidth];
|
||||
|
||||
// Create Navigation Node Pool, assumes 5 nodes on east and south
|
||||
// side of each cell. The City owns these nodes, and cells in the
|
||||
// city borrow them and link to them as required
|
||||
pNodes = new cAuto_Node[nHeight * nWidth * 49];
|
||||
|
||||
// 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
|
||||
|
||||
for (int x = 0; x < nWidth; x++)
|
||||
{
|
||||
for (int y = 0; y < nHeight; y++)
|
||||
{
|
||||
// Nodes sit between cells, therefore each create nodes along
|
||||
// the east and southern sides of the cell. This assumes that
|
||||
// navigation along the top and left boundaries of the map
|
||||
// will not occur. And it shouldnt, as its water
|
||||
|
||||
int idx = (y * nWidth + x) * 49;
|
||||
|
||||
for (int dx = 0; dx < 7; dx++)
|
||||
{
|
||||
float off_x = 0.0f;
|
||||
switch (dx)
|
||||
{
|
||||
case 0: off_x = 0.000f; break;
|
||||
case 1: off_x = 0.083f; break;
|
||||
case 2: off_x = 0.333f; break;
|
||||
case 3: off_x = 0.500f; break;
|
||||
case 4: off_x = 0.667f; break;
|
||||
case 5: off_x = 0.917f; break;
|
||||
case 6: off_x = 1.000f; break;
|
||||
}
|
||||
|
||||
|
||||
for (int dy = 0; dy < 7; dy++)
|
||||
{
|
||||
float off_y = 0.0f;
|
||||
switch (dy)
|
||||
{
|
||||
case 0: off_y = 0.000f; break;
|
||||
case 1: off_y = 0.083f; break;
|
||||
case 2: off_y = 0.333f; break;
|
||||
case 3: off_y = 0.500f; break;
|
||||
case 4: off_y = 0.667f; break;
|
||||
case 5: off_y = 0.917f; 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].bBlock = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Now create default Cell
|
||||
for (int x = 0; x < nWidth; x++)
|
||||
{
|
||||
for (int y = 0; y < nHeight; y++)
|
||||
{
|
||||
// Default city, everything is 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
|
||||
pCells[y * nWidth + x]->LinkAssets(mapTextures, mapMesh, mapTransforms);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
cAuto_Node* cCityMap::GetAutoNodeBase(int x, int y)
|
||||
{
|
||||
return pNodes + (y * nWidth + x) * 49;
|
||||
}
|
||||
|
||||
void cCityMap::RemoveAllTracks()
|
||||
{
|
||||
for (int i = 0; i < nWidth * nHeight * 49; i++)
|
||||
{
|
||||
pNodes[i].listTracks.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void cCityMap::ReleaseCity()
|
||||
{
|
||||
for (int x = 0; x < nWidth; x++)
|
||||
{
|
||||
for (int y = 0; y < nHeight; y++)
|
||||
{
|
||||
// Erase any tracks attached to nodes
|
||||
for(int i=0; i<49; i++)
|
||||
Cell(x, y)->pNaviNodes[i]->listTracks.clear();
|
||||
|
||||
// Release individual cell objects
|
||||
delete pCells[y * nWidth + x];
|
||||
}
|
||||
}
|
||||
|
||||
// Release array of cell pointers
|
||||
if (pCells != nullptr) delete pCells;
|
||||
|
||||
// Release array of automata navigation nodes
|
||||
if (pNodes != nullptr) delete pNodes;
|
||||
|
||||
nWidth = 0;
|
||||
nHeight = 0;
|
||||
}
|
||||
|
||||
|
||||
bool cCityMap::SaveCity(std::string sFilename)
|
||||
{
|
||||
/*std::ofstream file(sFilename, std::ios::out | std::ios::binary);
|
||||
if (!file.is_open()) return false;
|
||||
|
||||
file.write((char*)&m_nWidth, sizeof(int));
|
||||
file.write((char*)&m_nHeight, sizeof(int));
|
||||
|
||||
for (int x = 0; x < m_nWidth; x++)
|
||||
{
|
||||
for (int y = 0; y < m_nHeight; y++)
|
||||
{
|
||||
file.write((char*)Cell(x, y), sizeof(cCityCell));
|
||||
}
|
||||
}*/
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cCityMap::LoadCity(std::string sFilename)
|
||||
{
|
||||
/*std::ifstream file(sFilename, std::ios::in | std::ios::binary);
|
||||
if (!file.is_open()) return false;
|
||||
|
||||
int w, h;
|
||||
file.read((char*)&w, sizeof(int));
|
||||
file.read((char*)&h, sizeof(int));
|
||||
CreateCity(w, h);
|
||||
|
||||
for (int x = 0; x < m_nWidth; x++)
|
||||
{
|
||||
for (int y = 0; y < m_nHeight; y++)
|
||||
{
|
||||
file.read((char*)Cell(x, y), sizeof(cCityCell));
|
||||
}
|
||||
}*/
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -1,63 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
#include "olcPixelGameEngine.h"
|
||||
#include "olcPGEX_Graphics3D.h"
|
||||
#include "cCell.h"
|
||||
#include "cCell_Plane.h"
|
||||
#include "cCell_Water.h"
|
||||
#include "cCell_Road.h"
|
||||
#include "cCell_Building.h"
|
||||
|
||||
/*
|
||||
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
|
||||
map cells
|
||||
*/
|
||||
class cCityMap
|
||||
{
|
||||
public:
|
||||
// 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);
|
||||
|
||||
// Cleans up city, like Batman
|
||||
~cCityMap();
|
||||
|
||||
public:
|
||||
// Save the current city to a file, this will overwrite an existing
|
||||
// city file without warning. Returns true if successful
|
||||
bool SaveCity(std::string sFilename);
|
||||
|
||||
// Load a city from file and replace current city with it, retuns
|
||||
// true if successful
|
||||
bool LoadCity(std::string sFilename);
|
||||
|
||||
public:
|
||||
// Return width of city in cells
|
||||
int GetWidth();
|
||||
// Return height of city in cells
|
||||
int GetHeight();
|
||||
// Return a specific cell reference if inside city limits, or nullptr
|
||||
cCell* Cell(int x, int y);
|
||||
// Replace a specific cell
|
||||
cCell* Replace(int x, int y, cCell* cell);
|
||||
|
||||
cAuto_Node* GetAutoNodeBase(int x, int y);
|
||||
|
||||
void RemoveAllTracks();
|
||||
|
||||
private:
|
||||
int nWidth = 0;
|
||||
int nHeight = 0;
|
||||
cCell **pCells = nullptr;
|
||||
cAuto_Node *pNodes = nullptr;
|
||||
|
||||
private:
|
||||
// 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);
|
||||
// Destroy city
|
||||
void ReleaseCity();
|
||||
};
|
||||
|
||||
@ -1,156 +0,0 @@
|
||||
#include "cGameSettings.h"
|
||||
|
||||
|
||||
|
||||
cGameSettings::cGameSettings()
|
||||
{
|
||||
}
|
||||
|
||||
cGameSettings::~cGameSettings()
|
||||
{
|
||||
}
|
||||
|
||||
bool cGameSettings::LoadConfigFile(std::string sFile)
|
||||
{
|
||||
lua_State *L = luaL_newstate();
|
||||
luaL_openlibs(L);
|
||||
|
||||
// Load game settings file
|
||||
int r = luaL_loadfile(L, sFile.c_str());
|
||||
if (r != LUA_OK)
|
||||
{
|
||||
std::string errormsg = lua_tostring(L, -1);
|
||||
std::cout << errormsg << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Execute it
|
||||
int i = lua_pcall(L, 0, LUA_MULTRET, 0);
|
||||
if (i != LUA_OK)
|
||||
{
|
||||
std::string errormsg = lua_tostring(L, -1);
|
||||
std::cout << errormsg << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
lua_getglobal(L, "PixelWidth");
|
||||
if (lua_isinteger(L, -1)) cGameSettings::nPixelWidth = (int)lua_tointeger(L, -1);
|
||||
|
||||
lua_getglobal(L, "PixelHeight");
|
||||
if (lua_isinteger(L, -1)) cGameSettings::nPixelHeight = (int)lua_tointeger(L, -1);
|
||||
|
||||
lua_getglobal(L, "ScreenWidth");
|
||||
if (lua_isinteger(L, -1)) cGameSettings::nScreenWidth = (int)lua_tointeger(L, -1);
|
||||
|
||||
lua_getglobal(L, "ScreenHeight");
|
||||
if (lua_isinteger(L, -1)) cGameSettings::nScreenHeight = (int)lua_tointeger(L, -1);
|
||||
|
||||
lua_getglobal(L, "DefaultMapWidth");
|
||||
if (lua_isinteger(L, -1)) cGameSettings::nDefaultMapWidth = (int)lua_tointeger(L, -1);
|
||||
|
||||
lua_getglobal(L, "DefaultMapHeight");
|
||||
if (lua_isinteger(L, -1)) cGameSettings::nDefaultMapHeight = (int)lua_tointeger(L, -1);
|
||||
|
||||
lua_getglobal(L, "DefaultCityFile");
|
||||
if (lua_isstring(L, -1)) cGameSettings::sDefaultCityFile = lua_tostring(L, -1);
|
||||
|
||||
lua_getglobal(L, "FullScreen");
|
||||
if (lua_isboolean(L, -1)) cGameSettings::bFullScreen = lua_toboolean(L, -1);
|
||||
|
||||
|
||||
//// Load System Texture files
|
||||
|
||||
// Load Texture Assets
|
||||
lua_getglobal(L, "Textures"); // -1 Table "Teams"
|
||||
if (lua_istable(L, -1))
|
||||
{
|
||||
lua_pushnil(L); // -2 Key Nil : -1 Table "Teams"
|
||||
|
||||
while (lua_next(L, -2) != 0) // -1 Table : -2 Key "TeamName" : -3 Table "Teams"
|
||||
{
|
||||
sAssetTexture texture;
|
||||
int stage = 0;
|
||||
if (lua_istable(L, -1))
|
||||
{
|
||||
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"
|
||||
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 == 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"
|
||||
stage++;
|
||||
}
|
||||
}
|
||||
lua_pop(L, 1); // -1 Table : -2 Table Value : -3 Key "TeamName" : -4 Table "Teams"
|
||||
vecAssetTextures.push_back(texture);
|
||||
}
|
||||
}
|
||||
|
||||
auto GroupLoadAssets = [L](const std::string &group, std::vector<sAssetModel> &vec)
|
||||
{
|
||||
lua_getglobal(L, group.c_str());
|
||||
if (lua_istable(L, -1))
|
||||
{
|
||||
lua_pushnil(L);
|
||||
while (lua_next(L, -2) != 0)
|
||||
{
|
||||
sAssetModel model;
|
||||
int stage = 0;
|
||||
if (lua_istable(L, -1))
|
||||
{
|
||||
lua_gettable(L, -1);
|
||||
lua_pushnil(L);
|
||||
while (lua_next(L, -2) != 0)
|
||||
{
|
||||
if (stage == 0) model.sCreator = lua_tostring(L, -1);
|
||||
if (stage == 1) model.sDescription = lua_tostring(L, -1);
|
||||
if (stage == 2) model.sModelOBJ = 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 == 5) model.fRotate[1] = (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 == 8) model.fScale[1] = (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 == 11) model.fTranslate[1] = (float)lua_tonumber(L, -1);
|
||||
if (stage == 12) model.fTranslate[2] = (float)lua_tonumber(L, -1);
|
||||
lua_pop(L, 1);
|
||||
stage++;
|
||||
}
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
vec.push_back(model);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Load Building Assets
|
||||
GroupLoadAssets("Buildings", vecAssetBuildings);
|
||||
|
||||
// Load Vehicle Assets
|
||||
GroupLoadAssets("Vehicles", vecAssetVehicles);
|
||||
|
||||
|
||||
lua_close(L);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int cGameSettings::nScreenWidth = 768;
|
||||
int cGameSettings::nScreenHeight = 480;
|
||||
int cGameSettings::nPixelWidth = 2;
|
||||
int cGameSettings::nPixelHeight = 2;
|
||||
bool cGameSettings::bFullScreen = false;
|
||||
|
||||
int cGameSettings::nDefaultMapWidth = 64;
|
||||
int cGameSettings::nDefaultMapHeight = 32;
|
||||
std::string cGameSettings::sDefaultCityFile = "";
|
||||
|
||||
std::vector<sAssetTexture> cGameSettings::vecAssetTextures;
|
||||
std::vector<sAssetModel> cGameSettings::vecAssetBuildings;
|
||||
std::vector<sAssetModel> cGameSettings::vecAssetVehicles;
|
||||
@ -1,65 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include "lua533/include/lua.h"
|
||||
#include "lua533/include/lauxlib.h"
|
||||
#include "lua533/include/lualib.h"
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma comment(lib, "lua533/liblua53.a")
|
||||
#endif
|
||||
|
||||
/*
|
||||
This is a singleton that stores all the games configuration settings.
|
||||
These settings are loaded on game start up and are to be considered
|
||||
read-only.
|
||||
*/
|
||||
|
||||
struct sAssetModel
|
||||
{
|
||||
std::string sCreator;
|
||||
std::string sDescription;
|
||||
std::string sModelOBJ;
|
||||
std::string sModelPNG;
|
||||
float fRotate[3];
|
||||
float fScale[3];
|
||||
float fTranslate[3];
|
||||
};
|
||||
|
||||
struct sAssetTexture
|
||||
{
|
||||
std::string sName;
|
||||
std::string sFile;
|
||||
};
|
||||
|
||||
class cGameSettings
|
||||
{
|
||||
public:
|
||||
cGameSettings();
|
||||
~cGameSettings();
|
||||
|
||||
public:
|
||||
bool LoadConfigFile(std::string sFile);
|
||||
|
||||
public:
|
||||
static int nScreenWidth;
|
||||
static int nScreenHeight;
|
||||
static int nPixelWidth;
|
||||
static int nPixelHeight;
|
||||
static bool bFullScreen;
|
||||
|
||||
static int nDefaultMapWidth;
|
||||
static int nDefaultMapHeight;
|
||||
static std::string sDefaultCityFile;
|
||||
|
||||
static std::vector<sAssetTexture> vecAssetTextures;
|
||||
static std::vector<sAssetModel> vecAssetBuildings;
|
||||
static std::vector<sAssetModel> vecAssetVehicles;
|
||||
};
|
||||
|
||||
@ -1,367 +0,0 @@
|
||||
/*
|
||||
Top Down City Based Car Crime Game - Part #2
|
||||
"Colin, I hope you're shooting 600+ wherever you are buddy. RIP." - javidx9
|
||||
|
||||
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.
|
||||
|
||||
Instructions:
|
||||
~~~~~~~~~~~~~
|
||||
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
|
||||
|
||||
Relevant Video: https://youtu.be/fIV6P1W-wuo
|
||||
|
||||
Links
|
||||
~~~~~
|
||||
YouTube: https://www.youtube.com/javidx9
|
||||
https://www.youtube.com/javidx9extra
|
||||
Discord: https://discord.gg/WhwHUMV
|
||||
Twitter: https://www.twitter.com/javidx9
|
||||
Twitch: https://www.twitch.tv/javidx9
|
||||
GitHub: https://www.github.com/onelonecoder
|
||||
Patreon: https://www.patreon.com/javidx9
|
||||
Homepage: https://www.onelonecoder.com
|
||||
|
||||
Author
|
||||
~~~~~~
|
||||
David Barr, aka javidx9, ©OneLoneCoder 2019
|
||||
*/
|
||||
|
||||
|
||||
#include "cGameSettings.h"
|
||||
#include "cCarCrimeCity.h"
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
// Load the settings singleton
|
||||
cGameSettings config;
|
||||
if (!config.LoadConfigFile("assets/config.lua"))
|
||||
{
|
||||
std::cout << "Failed to load '/assets/config.lua'" << std::endl;
|
||||
std::cout << " -> Using default configuration" << std::endl;
|
||||
}
|
||||
|
||||
// Start the PixelGameEngine
|
||||
cCarCrimeCity game;
|
||||
if (game.Construct(config.nScreenWidth, config.nScreenHeight, config.nPixelWidth, config.nPixelHeight, config.bFullScreen))
|
||||
game.Start();
|
||||
|
||||
// Exit!
|
||||
return 0;
|
||||
}
|
||||
|
||||
//#define OLC_PGE_APPLICATION
|
||||
//#include "olcPixelGameEngine.h"
|
||||
//
|
||||
//#define OLC_PGEX_GRAPHICS3D
|
||||
//#include "olcPGEX_Graphics3D.h"
|
||||
//
|
||||
//
|
||||
//
|
||||
//enum CELLTYPE
|
||||
//{
|
||||
// CELL_BLANK = 0,
|
||||
// CELL_GRASS = 1,
|
||||
// CELL_CONCRETE = 2,
|
||||
// CELL_WATER = 3,
|
||||
// CELL_BUILDING = 4,
|
||||
// CELL_ROAD_H = 5,
|
||||
// CELL_ROAD_V = 6,
|
||||
// CELL_ROAD_C1 = 7,
|
||||
// CELL_ROAD_C2 = 8,
|
||||
// CELL_ROAD_C3 = 9,
|
||||
// CELL_ROAD_C4 = 10,
|
||||
// CELL_ROAD_T1 = 11,
|
||||
// CELL_ROAD_T2 = 12,
|
||||
// CELL_ROAD_T3 = 13,
|
||||
// CELL_ROAD_T4 = 14,
|
||||
// CELL_ROAD_X = 15,
|
||||
//};
|
||||
//
|
||||
//struct cCityCell
|
||||
//{
|
||||
// int nType = 5;// CELL_GRASS;
|
||||
//};
|
||||
//
|
||||
//class cCityMap
|
||||
//{
|
||||
//public:
|
||||
// // Construct a "blank" city w units wide by h units high
|
||||
// cCityMap(int w, int h);
|
||||
//
|
||||
// // Cleans up city, like Batman
|
||||
// ~cCityMap();
|
||||
//
|
||||
//
|
||||
//public:
|
||||
// // Return width of city in cells
|
||||
// int GetWidth();
|
||||
// // Return height of city in cells
|
||||
// int GetHeight();
|
||||
// // Return a specific cell reference if inside city limits, or nullptr
|
||||
// cCityCell* Cell(int x, int y);
|
||||
//
|
||||
//private:
|
||||
// int m_nWidth = 0;
|
||||
// int m_nHeight = 0;
|
||||
// cCityCell *m_pCells = nullptr;
|
||||
//
|
||||
//private:
|
||||
// // Creates a "default" city of specified size
|
||||
// void CreateCity(int w, int h);
|
||||
// // Destroy city
|
||||
// void ReleaseCity();
|
||||
//};
|
||||
//
|
||||
//cCityMap::cCityMap(int w, int h)
|
||||
//{
|
||||
// CreateCity(w, h);
|
||||
//}
|
||||
//
|
||||
//cCityMap::~cCityMap()
|
||||
//{
|
||||
// //ReleaseCity();
|
||||
//}
|
||||
//
|
||||
//int cCityMap::GetWidth()
|
||||
//{
|
||||
// return m_nWidth;
|
||||
//}
|
||||
//
|
||||
//int cCityMap::GetHeight()
|
||||
//{
|
||||
// return m_nHeight;
|
||||
//}
|
||||
//
|
||||
//cCityCell* cCityMap::Cell(int x, int y)
|
||||
//{
|
||||
// if (x >= 0 && x < m_nWidth && y >= 0 && y < m_nHeight)
|
||||
// return &m_pCells[y*m_nWidth + x];
|
||||
// else
|
||||
// return nullptr;
|
||||
//}
|
||||
//
|
||||
//void cCityMap::CreateCity(int w, int h)
|
||||
//{
|
||||
// //ReleaseCity();
|
||||
// m_nWidth = w;
|
||||
// m_nHeight = h;
|
||||
// m_pCells = new cCityCell[m_nHeight * m_nWidth];
|
||||
//
|
||||
// for (int x = 0; x < m_nWidth; x++)
|
||||
// {
|
||||
// for (int y = 0; y < m_nHeight; y++)
|
||||
// {
|
||||
// //m_pCells[y*m_nWidth + x] = new cCityCell();
|
||||
// //Cell(x, y)->bRoad = false;
|
||||
// //Cell(x, y)->nHeight = 0;
|
||||
// //Cell(x, y)->nWorldX = x;
|
||||
// //Cell(x, y)->nWorldY = y;
|
||||
// Cell(x, y)->nType = CELL_GRASS;
|
||||
// //Cell(x, y)->bBuilding = false;
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//void cCityMap::ReleaseCity()
|
||||
//{
|
||||
// if (m_pCells != nullptr) delete m_pCells;
|
||||
// m_nWidth = 0;
|
||||
// m_nHeight = 0;
|
||||
//}
|
||||
//
|
||||
//
|
||||
//class cCarCrimeCity : public olc::PixelGameEngine
|
||||
//{
|
||||
//public:
|
||||
// cCarCrimeCity()
|
||||
// {
|
||||
// sAppName = "Car Crime City";
|
||||
// }
|
||||
//
|
||||
// ~cCarCrimeCity()
|
||||
// {
|
||||
// }
|
||||
//
|
||||
// bool OnUserCreate()
|
||||
// {
|
||||
// // Initialise PGEX 3D
|
||||
// olc::GFX3D::ConfigureDisplay();
|
||||
//
|
||||
// // Create Default city
|
||||
// pCity = new cCityMap(64, 32);// cGameSettings::nDefaultMapWidth, cGameSettings::nDefaultMapHeight);
|
||||
//
|
||||
//
|
||||
// // A simple flat unit quad
|
||||
// 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, 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");
|
||||
//
|
||||
//
|
||||
//
|
||||
// SetDrawTarget(nullptr);
|
||||
// return true;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// bool OnUserUpdate(float fElapsedTime)
|
||||
// {
|
||||
// // User Input
|
||||
// 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::A).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::X).bHeld) vCamera.z -= 10.0f * fElapsedTime;
|
||||
//
|
||||
//
|
||||
// vEye = vCamera;
|
||||
//
|
||||
// // Perform Ray casting to calculate visible world extents and mouse position
|
||||
// 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 matView = olc::GFX3D::Math::Mat_PointAt(vEye, vLookTarget, vUp);
|
||||
//
|
||||
//
|
||||
//
|
||||
// // Render Scene
|
||||
// Clear(olc::BLUE);
|
||||
// olc::GFX3D::ClearDepth();
|
||||
//
|
||||
// // Create rendering pipeline
|
||||
// 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.SetCamera(vEye, vLookTarget, vUp);
|
||||
//
|
||||
//
|
||||
//
|
||||
// int nStartX = 0;
|
||||
// int nEndX = pCity->GetWidth();
|
||||
// int nStartY = 0;
|
||||
// int nEndY = pCity->GetHeight();
|
||||
//
|
||||
// // Render Ground, Roads, Walls & Buildings
|
||||
// for (int x = nStartX; x < nEndX; x++)
|
||||
// {
|
||||
// if (x == 15)
|
||||
// int k = 7;
|
||||
//
|
||||
// for (int y = nStartY; y < nEndY; y++)
|
||||
// {
|
||||
//
|
||||
//
|
||||
// switch (pCity->Cell(x, y)->nType)
|
||||
// {
|
||||
// case CELL_GRASS:
|
||||
// {
|
||||
// olc::GFX3D::mat4x4 matWorld;
|
||||
// matWorld = olc::GFX3D::Math::Mat_MakeTranslation((float)x, (float)y, 0.0f);
|
||||
// pipe.SetTransform(matWorld);
|
||||
// pipe.SetTexture(sprOld);
|
||||
// //pipe.SetTexture(vecSpriteSystem[0]);
|
||||
// //pipe.Render(vecMeshSystem[0].tris);
|
||||
// pipe.Render(meshQuad.tris);
|
||||
// //pipe.Render(vecMeshSystem[0].tris, olc::GFX3D::RENDER_FLAT);
|
||||
// break;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// default:
|
||||
// {
|
||||
// olc::GFX3D::mat4x4 matWorld;
|
||||
// matWorld = olc::GFX3D::Math::Mat_MakeTranslation((float)x, (float)y, 0.0f);
|
||||
// pipe.SetTransform(matWorld);
|
||||
// pipe.Render(meshQuad.tris, olc::GFX3D::RENDER_WIRE);
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//
|
||||
//
|
||||
// return true;
|
||||
// }
|
||||
//
|
||||
// bool OnUserDestroy()
|
||||
// {
|
||||
// return true;
|
||||
// }
|
||||
//
|
||||
//
|
||||
//private:
|
||||
// olc::GFX3D::vec3d vCamera = { 0.0f, 0.0f, -10.0f };
|
||||
// olc::GFX3D::vec3d vUp = { 0.0f, 1.0f, 0.0f };
|
||||
// olc::GFX3D::vec3d vEye = { 0.0f, 0.0f, -10.0f };
|
||||
// olc::GFX3D::vec3d vLookDir = { 0.0f, 0.0f, 1.0f };
|
||||
//
|
||||
//
|
||||
//
|
||||
// olc::Sprite *sprOld = nullptr;
|
||||
// olc::GFX3D::mesh meshQuad;
|
||||
//
|
||||
// cCityMap *pCity = nullptr;
|
||||
//
|
||||
//
|
||||
//
|
||||
//};
|
||||
//
|
||||
//int main()
|
||||
//{
|
||||
// // Load the settings singleton
|
||||
// /*cGameSettings config;
|
||||
// if (!config.LoadConfigFile("assets/config.lua"))
|
||||
// {
|
||||
// std::cout << "Failed to load '/assets/config.lua'" << std::endl;
|
||||
// std::cout << " -> Using default configuration" << std::endl;
|
||||
// }*/
|
||||
//
|
||||
// // Start the PixelGameEngine
|
||||
// cCarCrimeCity game;
|
||||
// if (game.Construct(256, 240, 4, 4))// config.nScreenWidth, config.nScreenHeight, config.nPixelWidth, config.nPixelHeight))
|
||||
// game.Start();
|
||||
//
|
||||
// // Exit!
|
||||
// return 0;
|
||||
//}
|
||||
@ -1,5 +0,0 @@
|
||||
#define OLC_PGE_APPLICATION
|
||||
#include "olcPixelGameEngine.h"
|
||||
|
||||
#define OLC_PGEX_GRAPHICS3D
|
||||
#include "olcPGEX_Graphics3D.h"
|
||||
@ -1,166 +0,0 @@
|
||||
/*
|
||||
MMO Client/Server Framework using ASIO
|
||||
"Happy Birthday Mrs Javidx9!" - javidx9
|
||||
|
||||
Videos:
|
||||
Part #1: https://youtu.be/2hNdkYInj4g
|
||||
Part #2: https://youtu.be/UbjxGvrDrbw
|
||||
|
||||
License (OLC-3)
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Copyright 2018 - 2020 OneLoneCoder.com
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions or derivations of source code must retain the above
|
||||
copyright notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions or derivative works in binary form must reproduce
|
||||
the above copyright notice. This list of conditions and the following
|
||||
disclaimer must be reproduced in the documentation and/or other
|
||||
materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Links
|
||||
~~~~~
|
||||
YouTube: https://www.youtube.com/javidx9
|
||||
Discord: https://discord.gg/WhwHUMV
|
||||
Twitter: https://www.twitter.com/javidx9
|
||||
Twitch: https://www.twitch.tv/javidx9
|
||||
GitHub: https://www.github.com/onelonecoder
|
||||
Homepage: https://www.onelonecoder.com
|
||||
|
||||
Author
|
||||
~~~~~~
|
||||
David Barr, aka javidx9, ©OneLoneCoder 2019, 2020
|
||||
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <olc_net.h>
|
||||
|
||||
enum class CustomMsgTypes : uint32_t
|
||||
{
|
||||
ServerAccept,
|
||||
ServerDeny,
|
||||
ServerPing,
|
||||
MessageAll,
|
||||
ServerMessage,
|
||||
};
|
||||
|
||||
|
||||
|
||||
class CustomClient : public olc::net::client_interface<CustomMsgTypes>
|
||||
{
|
||||
public:
|
||||
void PingServer()
|
||||
{
|
||||
olc::net::message<CustomMsgTypes> msg;
|
||||
msg.header.id = CustomMsgTypes::ServerPing;
|
||||
|
||||
// Caution with this...
|
||||
std::chrono::system_clock::time_point timeNow = std::chrono::system_clock::now();
|
||||
|
||||
msg << timeNow;
|
||||
Send(msg);
|
||||
}
|
||||
|
||||
void MessageAll()
|
||||
{
|
||||
olc::net::message<CustomMsgTypes> msg;
|
||||
msg.header.id = CustomMsgTypes::MessageAll;
|
||||
Send(msg);
|
||||
}
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
CustomClient c;
|
||||
c.Connect("127.0.0.1", 60000);
|
||||
|
||||
bool key[3] = { false, false, false };
|
||||
bool old_key[3] = { false, false, false };
|
||||
|
||||
bool bQuit = false;
|
||||
while (!bQuit)
|
||||
{
|
||||
if (GetForegroundWindow() == GetConsoleWindow())
|
||||
{
|
||||
key[0] = GetAsyncKeyState('1') & 0x8000;
|
||||
key[1] = GetAsyncKeyState('2') & 0x8000;
|
||||
key[2] = GetAsyncKeyState('3') & 0x8000;
|
||||
}
|
||||
|
||||
if (key[0] && !old_key[0]) c.PingServer();
|
||||
if (key[1] && !old_key[1]) c.MessageAll();
|
||||
if (key[2] && !old_key[2]) bQuit = true;
|
||||
|
||||
for (int i = 0; i < 3; i++) old_key[i] = key[i];
|
||||
|
||||
if (c.IsConnected())
|
||||
{
|
||||
if (!c.Incoming().empty())
|
||||
{
|
||||
|
||||
|
||||
auto msg = c.Incoming().pop_front().msg;
|
||||
|
||||
switch (msg.header.id)
|
||||
{
|
||||
case CustomMsgTypes::ServerAccept:
|
||||
{
|
||||
// Server has responded to a ping request
|
||||
std::cout << "Server Accepted Connection\n";
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case CustomMsgTypes::ServerPing:
|
||||
{
|
||||
// Server has responded to a ping request
|
||||
std::chrono::system_clock::time_point timeNow = std::chrono::system_clock::now();
|
||||
std::chrono::system_clock::time_point timeThen;
|
||||
msg >> timeThen;
|
||||
std::cout << "Ping: " << std::chrono::duration<double>(timeNow - timeThen).count() << "\n";
|
||||
}
|
||||
break;
|
||||
|
||||
case CustomMsgTypes::ServerMessage:
|
||||
{
|
||||
// Server has responded to a ping request
|
||||
uint32_t clientID;
|
||||
msg >> clientID;
|
||||
std::cout << "Hello from [" << clientID << "]\n";
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Server Down\n";
|
||||
bQuit = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1,137 +0,0 @@
|
||||
/*
|
||||
MMO Client/Server Framework using ASIO
|
||||
"Happy Birthday Mrs Javidx9!" - javidx9
|
||||
|
||||
Videos:
|
||||
Part #1: https://youtu.be/2hNdkYInj4g
|
||||
Part #2: https://youtu.be/UbjxGvrDrbw
|
||||
|
||||
License (OLC-3)
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Copyright 2018 - 2020 OneLoneCoder.com
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions or derivations of source code must retain the above
|
||||
copyright notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions or derivative works in binary form must reproduce
|
||||
the above copyright notice. This list of conditions and the following
|
||||
disclaimer must be reproduced in the documentation and/or other
|
||||
materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Links
|
||||
~~~~~
|
||||
YouTube: https://www.youtube.com/javidx9
|
||||
Discord: https://discord.gg/WhwHUMV
|
||||
Twitter: https://www.twitter.com/javidx9
|
||||
Twitch: https://www.twitch.tv/javidx9
|
||||
GitHub: https://www.github.com/onelonecoder
|
||||
Homepage: https://www.onelonecoder.com
|
||||
|
||||
Author
|
||||
~~~~~~
|
||||
David Barr, aka javidx9, ©OneLoneCoder 2019, 2020
|
||||
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <olc_net.h>
|
||||
|
||||
enum class CustomMsgTypes : uint32_t
|
||||
{
|
||||
ServerAccept,
|
||||
ServerDeny,
|
||||
ServerPing,
|
||||
MessageAll,
|
||||
ServerMessage,
|
||||
};
|
||||
|
||||
|
||||
|
||||
class CustomServer : public olc::net::server_interface<CustomMsgTypes>
|
||||
{
|
||||
public:
|
||||
CustomServer(uint16_t nPort) : olc::net::server_interface<CustomMsgTypes>(nPort)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual bool OnClientConnect(std::shared_ptr<olc::net::connection<CustomMsgTypes>> client)
|
||||
{
|
||||
olc::net::message<CustomMsgTypes> msg;
|
||||
msg.header.id = CustomMsgTypes::ServerAccept;
|
||||
client->Send(msg);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Called when a client appears to have disconnected
|
||||
virtual void OnClientDisconnect(std::shared_ptr<olc::net::connection<CustomMsgTypes>> client)
|
||||
{
|
||||
std::cout << "Removing client [" << client->GetID() << "]\n";
|
||||
}
|
||||
|
||||
// Called when a message arrives
|
||||
virtual void OnMessage(std::shared_ptr<olc::net::connection<CustomMsgTypes>> client, olc::net::message<CustomMsgTypes>& msg)
|
||||
{
|
||||
switch (msg.header.id)
|
||||
{
|
||||
case CustomMsgTypes::ServerPing:
|
||||
{
|
||||
std::cout << "[" << client->GetID() << "]: Server Ping\n";
|
||||
|
||||
// Simply bounce message back to client
|
||||
client->Send(msg);
|
||||
}
|
||||
break;
|
||||
|
||||
case CustomMsgTypes::MessageAll:
|
||||
{
|
||||
std::cout << "[" << client->GetID() << "]: Message All\n";
|
||||
|
||||
// Construct a new message and send it to all clients
|
||||
olc::net::message<CustomMsgTypes> msg;
|
||||
msg.header.id = CustomMsgTypes::ServerMessage;
|
||||
msg << client->GetID();
|
||||
MessageAllClients(msg, client);
|
||||
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
CustomServer server(60000);
|
||||
server.Start();
|
||||
|
||||
while (1)
|
||||
{
|
||||
server.Update(-1, true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1,160 +0,0 @@
|
||||
/*
|
||||
MMO Client/Server Framework using ASIO
|
||||
"Happy Birthday Mrs Javidx9!" - javidx9
|
||||
|
||||
Videos:
|
||||
Part #1: https://youtu.be/2hNdkYInj4g
|
||||
Part #2: https://youtu.be/UbjxGvrDrbw
|
||||
|
||||
License (OLC-3)
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Copyright 2018 - 2020 OneLoneCoder.com
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions or derivations of source code must retain the above
|
||||
copyright notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions or derivative works in binary form must reproduce
|
||||
the above copyright notice. This list of conditions and the following
|
||||
disclaimer must be reproduced in the documentation and/or other
|
||||
materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Links
|
||||
~~~~~
|
||||
YouTube: https://www.youtube.com/javidx9
|
||||
Discord: https://discord.gg/WhwHUMV
|
||||
Twitter: https://www.twitter.com/javidx9
|
||||
Twitch: https://www.twitch.tv/javidx9
|
||||
GitHub: https://www.github.com/onelonecoder
|
||||
Homepage: https://www.onelonecoder.com
|
||||
|
||||
Author
|
||||
~~~~~~
|
||||
David Barr, aka javidx9, ©OneLoneCoder 2019, 2020
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "net_common.h"
|
||||
|
||||
namespace olc
|
||||
{
|
||||
namespace net
|
||||
{
|
||||
template <typename T>
|
||||
class client_interface
|
||||
{
|
||||
public:
|
||||
client_interface()
|
||||
{}
|
||||
|
||||
virtual ~client_interface()
|
||||
{
|
||||
// If the client is destroyed, always try and disconnect from server
|
||||
Disconnect();
|
||||
}
|
||||
|
||||
public:
|
||||
// Connect to server with hostname/ip-address and port
|
||||
bool Connect(const std::string& host, const uint16_t port)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Resolve hostname/ip-address into tangiable physical address
|
||||
asio::ip::tcp::resolver resolver(m_context);
|
||||
asio::ip::tcp::resolver::results_type endpoints = resolver.resolve(host, std::to_string(port));
|
||||
|
||||
// Create connection
|
||||
m_connection = std::make_unique<connection<T>>(connection<T>::owner::client, m_context, asio::ip::tcp::socket(m_context), m_qMessagesIn);
|
||||
|
||||
// Tell the connection object to connect to server
|
||||
m_connection->ConnectToServer(endpoints);
|
||||
|
||||
// Start Context Thread
|
||||
thrContext = std::thread([this]() { m_context.run(); });
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "Client Exception: " << e.what() << "\n";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Disconnect from server
|
||||
void Disconnect()
|
||||
{
|
||||
// If connection exists, and it's connected then...
|
||||
if(IsConnected())
|
||||
{
|
||||
// ...disconnect from server gracefully
|
||||
m_connection->Disconnect();
|
||||
}
|
||||
|
||||
// Either way, we're also done with the asio context...
|
||||
m_context.stop();
|
||||
// ...and its thread
|
||||
if (thrContext.joinable())
|
||||
thrContext.join();
|
||||
|
||||
// Destroy the connection object
|
||||
m_connection.release();
|
||||
}
|
||||
|
||||
// Check if client is actually connected to a server
|
||||
bool IsConnected()
|
||||
{
|
||||
if (m_connection)
|
||||
return m_connection->IsConnected();
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
public:
|
||||
// Send message to server
|
||||
void Send(const message<T>& msg)
|
||||
{
|
||||
if (IsConnected())
|
||||
m_connection->Send(msg);
|
||||
}
|
||||
|
||||
// Retrieve queue of messages from server
|
||||
tsqueue<owned_message<T>>& Incoming()
|
||||
{
|
||||
return m_qMessagesIn;
|
||||
}
|
||||
|
||||
protected:
|
||||
// asio context handles the data transfer...
|
||||
asio::io_context m_context;
|
||||
// ...but needs a thread of its own to execute its work commands
|
||||
std::thread thrContext;
|
||||
// The client has a single instance of a "connection" object, which handles data transfer
|
||||
std::unique_ptr<connection<T>> m_connection;
|
||||
|
||||
private:
|
||||
// This is the thread safe queue of incoming messages from server
|
||||
tsqueue<owned_message<T>> m_qMessagesIn;
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -1,78 +0,0 @@
|
||||
/*
|
||||
MMO Client/Server Framework using ASIO
|
||||
"Happy Birthday Mrs Javidx9!" - javidx9
|
||||
|
||||
Videos:
|
||||
Part #1: https://youtu.be/2hNdkYInj4g
|
||||
Part #2: https://youtu.be/UbjxGvrDrbw
|
||||
|
||||
License (OLC-3)
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Copyright 2018 - 2020 OneLoneCoder.com
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions or derivations of source code must retain the above
|
||||
copyright notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions or derivative works in binary form must reproduce
|
||||
the above copyright notice. This list of conditions and the following
|
||||
disclaimer must be reproduced in the documentation and/or other
|
||||
materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Links
|
||||
~~~~~
|
||||
YouTube: https://www.youtube.com/javidx9
|
||||
Discord: https://discord.gg/WhwHUMV
|
||||
Twitter: https://www.twitter.com/javidx9
|
||||
Twitch: https://www.twitch.tv/javidx9
|
||||
GitHub: https://www.github.com/onelonecoder
|
||||
Homepage: https://www.onelonecoder.com
|
||||
|
||||
Author
|
||||
~~~~~~
|
||||
David Barr, aka javidx9, ©OneLoneCoder 2019, 2020
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <deque>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
|
||||
#ifdef _WIN32
|
||||
#define _WIN32_WINNT 0x0A00
|
||||
#endif
|
||||
|
||||
#define ASIO_STANDALONE
|
||||
#include <asio.hpp>
|
||||
#include <asio/ts/buffer.hpp>
|
||||
#include <asio/ts/internet.hpp>
|
||||
|
||||
@ -1,353 +0,0 @@
|
||||
/*
|
||||
MMO Client/Server Framework using ASIO
|
||||
"Happy Birthday Mrs Javidx9!" - javidx9
|
||||
|
||||
Videos:
|
||||
Part #1: https://youtu.be/2hNdkYInj4g
|
||||
Part #2: https://youtu.be/UbjxGvrDrbw
|
||||
|
||||
License (OLC-3)
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Copyright 2018 - 2020 OneLoneCoder.com
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions or derivations of source code must retain the above
|
||||
copyright notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions or derivative works in binary form must reproduce
|
||||
the above copyright notice. This list of conditions and the following
|
||||
disclaimer must be reproduced in the documentation and/or other
|
||||
materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Links
|
||||
~~~~~
|
||||
YouTube: https://www.youtube.com/javidx9
|
||||
Discord: https://discord.gg/WhwHUMV
|
||||
Twitter: https://www.twitter.com/javidx9
|
||||
Twitch: https://www.twitch.tv/javidx9
|
||||
GitHub: https://www.github.com/onelonecoder
|
||||
Homepage: https://www.onelonecoder.com
|
||||
|
||||
Author
|
||||
~~~~~~
|
||||
David Barr, aka javidx9, ©OneLoneCoder 2019, 2020
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "net_common.h"
|
||||
#include "net_tsqueue.h"
|
||||
#include "net_message.h"
|
||||
|
||||
|
||||
namespace olc
|
||||
{
|
||||
namespace net
|
||||
{
|
||||
template<typename T>
|
||||
class connection : public std::enable_shared_from_this<connection<T>>
|
||||
{
|
||||
public:
|
||||
// A connection is "owned" by either a server or a client, and its
|
||||
// behaviour is slightly different bewteen the two.
|
||||
enum class owner
|
||||
{
|
||||
server,
|
||||
client
|
||||
};
|
||||
|
||||
public:
|
||||
// Constructor: Specify Owner, connect to context, transfer the socket
|
||||
// Provide reference to incoming message queue
|
||||
connection(owner parent, asio::io_context& asioContext, asio::ip::tcp::socket socket, tsqueue<owned_message<T>>& qIn)
|
||||
: m_asioContext(asioContext), m_socket(std::move(socket)), m_qMessagesIn(qIn)
|
||||
{
|
||||
m_nOwnerType = parent;
|
||||
}
|
||||
|
||||
virtual ~connection()
|
||||
{}
|
||||
|
||||
// This ID is used system wide - its how clients will understand other clients
|
||||
// exist across the whole system.
|
||||
uint32_t GetID() const
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
public:
|
||||
void ConnectToClient(uint32_t uid = 0)
|
||||
{
|
||||
if (m_nOwnerType == owner::server)
|
||||
{
|
||||
if (m_socket.is_open())
|
||||
{
|
||||
id = uid;
|
||||
ReadHeader();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectToServer(const asio::ip::tcp::resolver::results_type& endpoints)
|
||||
{
|
||||
// Only clients can connect to servers
|
||||
if (m_nOwnerType == owner::client)
|
||||
{
|
||||
// Request asio attempts to connect to an endpoint
|
||||
asio::async_connect(m_socket, endpoints,
|
||||
[this](std::error_code ec, asio::ip::tcp::endpoint endpoint)
|
||||
{
|
||||
if (!ec)
|
||||
{
|
||||
ReadHeader();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Disconnect()
|
||||
{
|
||||
if (IsConnected())
|
||||
asio::post(m_asioContext, [this]() { m_socket.close(); });
|
||||
}
|
||||
|
||||
bool IsConnected() const
|
||||
{
|
||||
return m_socket.is_open();
|
||||
}
|
||||
|
||||
// Prime the connection to wait for incoming messages
|
||||
void StartListening()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public:
|
||||
// ASYNC - Send a message, connections are one-to-one so no need to specifiy
|
||||
// the target, for a client, the target is the server and vice versa
|
||||
void Send(const message<T>& msg)
|
||||
{
|
||||
asio::post(m_asioContext,
|
||||
[this, msg]()
|
||||
{
|
||||
// If the queue has a message in it, then we must
|
||||
// assume that it is in the process of asynchronously being written.
|
||||
// Either way add the message to the queue to be output. If no messages
|
||||
// were available to be written, then start the process of writing the
|
||||
// message at the front of the queue.
|
||||
bool bWritingMessage = !m_qMessagesOut.empty();
|
||||
m_qMessagesOut.push_back(msg);
|
||||
if (!bWritingMessage)
|
||||
{
|
||||
WriteHeader();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
private:
|
||||
// ASYNC - Prime context to write a message header
|
||||
void WriteHeader()
|
||||
{
|
||||
// If this function is called, we know the outgoing message queue must have
|
||||
// at least one message to send. So allocate a transmission buffer to hold
|
||||
// the message, and issue the work - asio, send these bytes
|
||||
asio::async_write(m_socket, asio::buffer(&m_qMessagesOut.front().header, sizeof(message_header<T>)),
|
||||
[this](std::error_code ec, std::size_t length)
|
||||
{
|
||||
// asio has now sent the bytes - if there was a problem
|
||||
// an error would be available...
|
||||
if (!ec)
|
||||
{
|
||||
// ... no error, so check if the message header just sent also
|
||||
// has a message body...
|
||||
if (m_qMessagesOut.front().body.size() > 0)
|
||||
{
|
||||
// ...it does, so issue the task to write the body bytes
|
||||
WriteBody();
|
||||
}
|
||||
else
|
||||
{
|
||||
// ...it didnt, so we are done with this message. Remove it from
|
||||
// the outgoing message queue
|
||||
m_qMessagesOut.pop_front();
|
||||
|
||||
// If the queue is not empty, there are more messages to send, so
|
||||
// make this happen by issuing the task to send the next header.
|
||||
if (!m_qMessagesOut.empty())
|
||||
{
|
||||
WriteHeader();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// ...asio failed to write the message, we could analyse why but
|
||||
// for now simply assume the connection has died by closing the
|
||||
// socket. When a future attempt to write to this client fails due
|
||||
// to the closed socket, it will be tidied up.
|
||||
std::cout << "[" << id << "] Write Header Fail.\n";
|
||||
m_socket.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ASYNC - Prime context to write a message body
|
||||
void WriteBody()
|
||||
{
|
||||
// If this function is called, a header has just been sent, and that header
|
||||
// indicated a body existed for this message. Fill a transmission buffer
|
||||
// with the body data, and send it!
|
||||
asio::async_write(m_socket, asio::buffer(m_qMessagesOut.front().body.data(), m_qMessagesOut.front().body.size()),
|
||||
[this](std::error_code ec, std::size_t length)
|
||||
{
|
||||
if (!ec)
|
||||
{
|
||||
// Sending was successful, so we are done with the message
|
||||
// and remove it from the queue
|
||||
m_qMessagesOut.pop_front();
|
||||
|
||||
// If the queue still has messages in it, then issue the task to
|
||||
// send the next messages' header.
|
||||
if (!m_qMessagesOut.empty())
|
||||
{
|
||||
WriteHeader();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Sending failed, see WriteHeader() equivalent for description :P
|
||||
std::cout << "[" << id << "] Write Body Fail.\n";
|
||||
m_socket.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ASYNC - Prime context ready to read a message header
|
||||
void ReadHeader()
|
||||
{
|
||||
// If this function is called, we are expecting asio to wait until it receives
|
||||
// enough bytes to form a header of a message. We know the headers are a fixed
|
||||
// size, so allocate a transmission buffer large enough to store it. In fact,
|
||||
// we will construct the message in a "temporary" message object as it's
|
||||
// convenient to work with.
|
||||
asio::async_read(m_socket, asio::buffer(&m_msgTemporaryIn.header, sizeof(message_header<T>)),
|
||||
[this](std::error_code ec, std::size_t length)
|
||||
{
|
||||
if (!ec)
|
||||
{
|
||||
// A complete message header has been read, check if this message
|
||||
// has a body to follow...
|
||||
if (m_msgTemporaryIn.header.size > 0)
|
||||
{
|
||||
// ...it does, so allocate enough space in the messages' body
|
||||
// vector, and issue asio with the task to read the body.
|
||||
m_msgTemporaryIn.body.resize(m_msgTemporaryIn.header.size);
|
||||
ReadBody();
|
||||
}
|
||||
else
|
||||
{
|
||||
// it doesn't, so add this bodyless message to the connections
|
||||
// incoming message queue
|
||||
AddToIncomingMessageQueue();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Reading form the client went wrong, most likely a disconnect
|
||||
// has occurred. Close the socket and let the system tidy it up later.
|
||||
std::cout << "[" << id << "] Read Header Fail.\n";
|
||||
m_socket.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ASYNC - Prime context ready to read a message body
|
||||
void ReadBody()
|
||||
{
|
||||
// If this function is called, a header has already been read, and that header
|
||||
// request we read a body, The space for that body has already been allocated
|
||||
// in the temporary message object, so just wait for the bytes to arrive...
|
||||
asio::async_read(m_socket, asio::buffer(m_msgTemporaryIn.body.data(), m_msgTemporaryIn.body.size()),
|
||||
[this](std::error_code ec, std::size_t length)
|
||||
{
|
||||
if (!ec)
|
||||
{
|
||||
// ...and they have! The message is now complete, so add
|
||||
// the whole message to incoming queue
|
||||
AddToIncomingMessageQueue();
|
||||
}
|
||||
else
|
||||
{
|
||||
// As above!
|
||||
std::cout << "[" << id << "] Read Body Fail.\n";
|
||||
m_socket.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Once a full message is received, add it to the incoming queue
|
||||
void AddToIncomingMessageQueue()
|
||||
{
|
||||
// Shove it in queue, converting it to an "owned message", by initialising
|
||||
// with the a shared pointer from this connection object
|
||||
if(m_nOwnerType == owner::server)
|
||||
m_qMessagesIn.push_back({ this->shared_from_this(), m_msgTemporaryIn });
|
||||
else
|
||||
m_qMessagesIn.push_back({ nullptr, m_msgTemporaryIn });
|
||||
|
||||
// We must now prime the asio context to receive the next message. It
|
||||
// wil just sit and wait for bytes to arrive, and the message construction
|
||||
// process repeats itself. Clever huh?
|
||||
ReadHeader();
|
||||
}
|
||||
|
||||
protected:
|
||||
// Each connection has a unique socket to a remote
|
||||
asio::ip::tcp::socket m_socket;
|
||||
|
||||
// This context is shared with the whole asio instance
|
||||
asio::io_context& m_asioContext;
|
||||
|
||||
// This queue holds all messages to be sent to the remote side
|
||||
// of this connection
|
||||
tsqueue<message<T>> m_qMessagesOut;
|
||||
|
||||
// This references the incoming queue of the parent object
|
||||
tsqueue<owned_message<T>>& m_qMessagesIn;
|
||||
|
||||
// Incoming messages are constructed asynchronously, so we will
|
||||
// store the part assembled message here, until it is ready
|
||||
message<T> m_msgTemporaryIn;
|
||||
|
||||
// The "owner" decides how some of the connection behaves
|
||||
owner m_nOwnerType = owner::server;
|
||||
|
||||
uint32_t id = 0;
|
||||
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -1,177 +0,0 @@
|
||||
/*
|
||||
MMO Client/Server Framework using ASIO
|
||||
"Happy Birthday Mrs Javidx9!" - javidx9
|
||||
|
||||
Videos:
|
||||
Part #1: https://youtu.be/2hNdkYInj4g
|
||||
Part #2: https://youtu.be/UbjxGvrDrbw
|
||||
|
||||
License (OLC-3)
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Copyright 2018 - 2020 OneLoneCoder.com
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions or derivations of source code must retain the above
|
||||
copyright notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions or derivative works in binary form must reproduce
|
||||
the above copyright notice. This list of conditions and the following
|
||||
disclaimer must be reproduced in the documentation and/or other
|
||||
materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Links
|
||||
~~~~~
|
||||
YouTube: https://www.youtube.com/javidx9
|
||||
Discord: https://discord.gg/WhwHUMV
|
||||
Twitter: https://www.twitter.com/javidx9
|
||||
Twitch: https://www.twitch.tv/javidx9
|
||||
GitHub: https://www.github.com/onelonecoder
|
||||
Homepage: https://www.onelonecoder.com
|
||||
|
||||
Author
|
||||
~~~~~~
|
||||
David Barr, aka javidx9, ©OneLoneCoder 2019, 2020
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "net_common.h"
|
||||
|
||||
namespace olc
|
||||
{
|
||||
namespace net
|
||||
{
|
||||
///[OLC_HEADERIFYIER] START "MESSAGE"
|
||||
|
||||
// Message Header is sent at start of all messages. The template allows us
|
||||
// to use "enum class" to ensure that the messages are valid at compile time
|
||||
template <typename T>
|
||||
struct message_header
|
||||
{
|
||||
T id{};
|
||||
uint32_t size = 0;
|
||||
};
|
||||
|
||||
// Message Body contains a header and a std::vector, containing raw bytes
|
||||
// of infomation. This way the message can be variable length, but the size
|
||||
// in the header must be updated.
|
||||
template <typename T>
|
||||
struct message
|
||||
{
|
||||
// Header & Body vector
|
||||
message_header<T> header{};
|
||||
std::vector<uint8_t> body;
|
||||
|
||||
// returns size of entire message packet in bytes
|
||||
size_t size() const
|
||||
{
|
||||
return body.size();
|
||||
}
|
||||
|
||||
// Override for std::cout compatibility - produces friendly description of message
|
||||
friend std::ostream& operator << (std::ostream& os, const message<T>& msg)
|
||||
{
|
||||
os << "ID:" << int(msg.header.id) << " Size:" << msg.header.size;
|
||||
return os;
|
||||
}
|
||||
|
||||
// Convenience Operator overloads - These allow us to add and remove stuff from
|
||||
// the body vector as if it were a stack, so First in, Last Out. These are a
|
||||
// template in itself, because we dont know what data type the user is pushing or
|
||||
// popping, so lets allow them all. NOTE: It assumes the data type is fundamentally
|
||||
// Plain Old Data (POD). TLDR: Serialise & Deserialise into/from a vector
|
||||
|
||||
// Pushes any POD-like data into the message buffer
|
||||
template<typename DataType>
|
||||
friend message<T>& operator << (message<T>& msg, const DataType& data)
|
||||
{
|
||||
// Check that the type of the data being pushed is trivially copyable
|
||||
static_assert(std::is_standard_layout<DataType>::value, "Data is too complex to be pushed into vector");
|
||||
|
||||
// Cache current size of vector, as this will be the point we insert the data
|
||||
size_t i = msg.body.size();
|
||||
|
||||
// Resize the vector by the size of the data being pushed
|
||||
msg.body.resize(msg.body.size() + sizeof(DataType));
|
||||
|
||||
// Physically copy the data into the newly allocated vector space
|
||||
std::memcpy(msg.body.data() + i, &data, sizeof(DataType));
|
||||
|
||||
// Recalculate the message size
|
||||
msg.header.size = msg.size();
|
||||
|
||||
// Return the target message so it can be "chained"
|
||||
return msg;
|
||||
}
|
||||
|
||||
// Pulls any POD-like data form the message buffer
|
||||
template<typename DataType>
|
||||
friend message<T>& operator >> (message<T>& msg, DataType& data)
|
||||
{
|
||||
// Check that the type of the data being pushed is trivially copyable
|
||||
static_assert(std::is_standard_layout<DataType>::value, "Data is too complex to be pulled from vector");
|
||||
|
||||
// Cache the location towards the end of the vector where the pulled data starts
|
||||
size_t i = msg.body.size() - sizeof(DataType);
|
||||
|
||||
// Physically copy the data from the vector into the user variable
|
||||
std::memcpy(&data, msg.body.data() + i, sizeof(DataType));
|
||||
|
||||
// Shrink the vector to remove read bytes, and reset end position
|
||||
msg.body.resize(i);
|
||||
|
||||
// Recalculate the message size
|
||||
msg.header.size = msg.size();
|
||||
|
||||
// Return the target message so it can be "chained"
|
||||
return msg;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// An "owned" message is identical to a regular message, but it is associated with
|
||||
// a connection. On a server, the owner would be the client that sent the message,
|
||||
// on a client the owner would be the server.
|
||||
|
||||
// Forward declare the connection
|
||||
template <typename T>
|
||||
class connection;
|
||||
|
||||
template <typename T>
|
||||
struct owned_message
|
||||
{
|
||||
std::shared_ptr<connection<T>> remote = nullptr;
|
||||
message<T> msg;
|
||||
|
||||
// Again, a friendly string maker
|
||||
friend std::ostream& operator<<(std::ostream& os, const owned_message<T>& msg)
|
||||
{
|
||||
os << msg.msg;
|
||||
return os;
|
||||
}
|
||||
};
|
||||
|
||||
///[OLC_HEADERIFYIER] END "MESSAGE"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,298 +0,0 @@
|
||||
/*
|
||||
MMO Client/Server Framework using ASIO
|
||||
"Happy Birthday Mrs Javidx9!" - javidx9
|
||||
|
||||
Videos:
|
||||
Part #1: https://youtu.be/2hNdkYInj4g
|
||||
Part #2: https://youtu.be/UbjxGvrDrbw
|
||||
|
||||
License (OLC-3)
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Copyright 2018 - 2020 OneLoneCoder.com
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions or derivations of source code must retain the above
|
||||
copyright notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions or derivative works in binary form must reproduce
|
||||
the above copyright notice. This list of conditions and the following
|
||||
disclaimer must be reproduced in the documentation and/or other
|
||||
materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Links
|
||||
~~~~~
|
||||
YouTube: https://www.youtube.com/javidx9
|
||||
Discord: https://discord.gg/WhwHUMV
|
||||
Twitter: https://www.twitter.com/javidx9
|
||||
Twitch: https://www.twitch.tv/javidx9
|
||||
GitHub: https://www.github.com/onelonecoder
|
||||
Homepage: https://www.onelonecoder.com
|
||||
|
||||
Author
|
||||
~~~~~~
|
||||
David Barr, aka javidx9, ©OneLoneCoder 2019, 2020
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "net_common.h"
|
||||
#include "net_tsqueue.h"
|
||||
#include "net_message.h"
|
||||
#include "net_connection.h"
|
||||
|
||||
namespace olc
|
||||
{
|
||||
namespace net
|
||||
{
|
||||
template<typename T>
|
||||
class server_interface
|
||||
{
|
||||
public:
|
||||
// Create a server, ready to listen on specified port
|
||||
server_interface(uint16_t port)
|
||||
: m_asioAcceptor(m_asioContext, asio::ip::tcp::endpoint(asio::ip::tcp::v4(), port))
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
virtual ~server_interface()
|
||||
{
|
||||
// May as well try and tidy up
|
||||
Stop();
|
||||
}
|
||||
|
||||
// Starts the server!
|
||||
bool Start()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Issue a task to the asio context - This is important
|
||||
// as it will prime the context with "work", and stop it
|
||||
// from exiting immediately. Since this is a server, we
|
||||
// want it primed ready to handle clients trying to
|
||||
// connect.
|
||||
WaitForClientConnection();
|
||||
|
||||
// Launch the asio context in its own thread
|
||||
m_threadContext = std::thread([this]() { m_asioContext.run(); });
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
// Something prohibited the server from listening
|
||||
std::cerr << "[SERVER] Exception: " << e.what() << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
std::cout << "[SERVER] Started!\n";
|
||||
return true;
|
||||
}
|
||||
|
||||
// Stops the server!
|
||||
void Stop()
|
||||
{
|
||||
// Request the context to close
|
||||
m_asioContext.stop();
|
||||
|
||||
// Tidy up the context thread
|
||||
if (m_threadContext.joinable()) m_threadContext.join();
|
||||
|
||||
// Inform someone, anybody, if they care...
|
||||
std::cout << "[SERVER] Stopped!\n";
|
||||
}
|
||||
|
||||
// ASYNC - Instruct asio to wait for connection
|
||||
void WaitForClientConnection()
|
||||
{
|
||||
// Prime context with an instruction to wait until a socket connects. This
|
||||
// is the purpose of an "acceptor" object. It will provide a unique socket
|
||||
// for each incoming connection attempt
|
||||
m_asioAcceptor.async_accept(
|
||||
[this](std::error_code ec, asio::ip::tcp::socket socket)
|
||||
{
|
||||
// Triggered by incoming connection request
|
||||
if (!ec)
|
||||
{
|
||||
// Display some useful(?) information
|
||||
std::cout << "[SERVER] New Connection: " << socket.remote_endpoint() << "\n";
|
||||
|
||||
// Create a new connection to handle this client
|
||||
std::shared_ptr<connection<T>> newconn =
|
||||
std::make_shared<connection<T>>(connection<T>::owner::server,
|
||||
m_asioContext, std::move(socket), m_qMessagesIn);
|
||||
|
||||
|
||||
|
||||
// Give the user server a chance to deny connection
|
||||
if (OnClientConnect(newconn))
|
||||
{
|
||||
// Connection allowed, so add to container of new connections
|
||||
m_deqConnections.push_back(std::move(newconn));
|
||||
|
||||
// And very important! Issue a task to the connection's
|
||||
// asio context to sit and wait for bytes to arrive!
|
||||
m_deqConnections.back()->ConnectToClient(nIDCounter++);
|
||||
|
||||
std::cout << "[" << m_deqConnections.back()->GetID() << "] Connection Approved\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "[-----] Connection Denied\n";
|
||||
|
||||
// Connection will go out of scope with no pending tasks, so will
|
||||
// get destroyed automagically due to the wonder of smart pointers
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Error has occurred during acceptance
|
||||
std::cout << "[SERVER] New Connection Error: " << ec.message() << "\n";
|
||||
}
|
||||
|
||||
// Prime the asio context with more work - again simply wait for
|
||||
// another connection...
|
||||
WaitForClientConnection();
|
||||
});
|
||||
}
|
||||
|
||||
// Send a message to a specific client
|
||||
void MessageClient(std::shared_ptr<connection<T>> client, const message<T>& msg)
|
||||
{
|
||||
// Check client is legitimate...
|
||||
if (client && client->IsConnected())
|
||||
{
|
||||
// ...and post the message via the connection
|
||||
client->Send(msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we cant communicate with client then we may as
|
||||
// well remove the client - let the server know, it may
|
||||
// be tracking it somehow
|
||||
OnClientDisconnect(client);
|
||||
|
||||
// Off you go now, bye bye!
|
||||
client.reset();
|
||||
|
||||
// Then physically remove it from the container
|
||||
m_deqConnections.erase(
|
||||
std::remove(m_deqConnections.begin(), m_deqConnections.end(), client), m_deqConnections.end());
|
||||
}
|
||||
}
|
||||
|
||||
// Send message to all clients
|
||||
void MessageAllClients(const message<T>& msg, std::shared_ptr<connection<T>> pIgnoreClient = nullptr)
|
||||
{
|
||||
bool bInvalidClientExists = false;
|
||||
|
||||
// Iterate through all clients in container
|
||||
for (auto& client : m_deqConnections)
|
||||
{
|
||||
// Check client is connected...
|
||||
if (client && client->IsConnected())
|
||||
{
|
||||
// ..it is!
|
||||
if(client != pIgnoreClient)
|
||||
client->Send(msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
// The client couldnt be contacted, so assume it has
|
||||
// disconnected.
|
||||
OnClientDisconnect(client);
|
||||
client.reset();
|
||||
|
||||
// Set this flag to then remove dead clients from container
|
||||
bInvalidClientExists = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove dead clients, all in one go - this way, we dont invalidate the
|
||||
// container as we iterated through it.
|
||||
if (bInvalidClientExists)
|
||||
m_deqConnections.erase(
|
||||
std::remove(m_deqConnections.begin(), m_deqConnections.end(), nullptr), m_deqConnections.end());
|
||||
}
|
||||
|
||||
// Force server to respond to incoming messages
|
||||
void Update(size_t nMaxMessages = -1, bool bWait = false)
|
||||
{
|
||||
if (bWait) m_qMessagesIn.wait();
|
||||
|
||||
// Process as many messages as you can up to the value
|
||||
// specified
|
||||
size_t nMessageCount = 0;
|
||||
while (nMessageCount < nMaxMessages && !m_qMessagesIn.empty())
|
||||
{
|
||||
// Grab the front message
|
||||
auto msg = m_qMessagesIn.pop_front();
|
||||
|
||||
// Pass to message handler
|
||||
OnMessage(msg.remote, msg.msg);
|
||||
|
||||
nMessageCount++;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
// This server class should override thse functions to implement
|
||||
// customised functionality
|
||||
|
||||
// Called when a client connects, you can veto the connection by returning false
|
||||
virtual bool OnClientConnect(std::shared_ptr<connection<T>> client)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Called when a client appears to have disconnected
|
||||
virtual void OnClientDisconnect(std::shared_ptr<connection<T>> client)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// Called when a message arrives
|
||||
virtual void OnMessage(std::shared_ptr<connection<T>> client, message<T>& msg)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
protected:
|
||||
// Thread Safe Queue for incoming message packets
|
||||
tsqueue<owned_message<T>> m_qMessagesIn;
|
||||
|
||||
// Container of active validated connections
|
||||
std::deque<std::shared_ptr<connection<T>>> m_deqConnections;
|
||||
|
||||
// Order of declaration is important - it is also the order of initialisation
|
||||
asio::io_context m_asioContext;
|
||||
std::thread m_threadContext;
|
||||
|
||||
// These things need an asio context
|
||||
asio::ip::tcp::acceptor m_asioAcceptor; // Handles new incoming connection attempts...
|
||||
|
||||
// Clients will be identified in the "wider system" via an ID
|
||||
uint32_t nIDCounter = 10000;
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -1,163 +0,0 @@
|
||||
/*
|
||||
MMO Client/Server Framework using ASIO
|
||||
"Happy Birthday Mrs Javidx9!" - javidx9
|
||||
|
||||
Videos:
|
||||
Part #1: https://youtu.be/2hNdkYInj4g
|
||||
Part #2: https://youtu.be/UbjxGvrDrbw
|
||||
|
||||
License (OLC-3)
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Copyright 2018 - 2020 OneLoneCoder.com
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions or derivations of source code must retain the above
|
||||
copyright notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions or derivative works in binary form must reproduce
|
||||
the above copyright notice. This list of conditions and the following
|
||||
disclaimer must be reproduced in the documentation and/or other
|
||||
materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Links
|
||||
~~~~~
|
||||
YouTube: https://www.youtube.com/javidx9
|
||||
Discord: https://discord.gg/WhwHUMV
|
||||
Twitter: https://www.twitter.com/javidx9
|
||||
Twitch: https://www.twitch.tv/javidx9
|
||||
GitHub: https://www.github.com/onelonecoder
|
||||
Homepage: https://www.onelonecoder.com
|
||||
|
||||
Author
|
||||
~~~~~~
|
||||
David Barr, aka javidx9, ©OneLoneCoder 2019, 2020
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "net_common.h"
|
||||
|
||||
namespace olc
|
||||
{
|
||||
namespace net
|
||||
{
|
||||
template<typename T>
|
||||
class tsqueue
|
||||
{
|
||||
public:
|
||||
tsqueue() = default;
|
||||
tsqueue(const tsqueue<T>&) = delete;
|
||||
virtual ~tsqueue() { clear(); }
|
||||
|
||||
public:
|
||||
// Returns and maintains item at front of Queue
|
||||
const T& front()
|
||||
{
|
||||
std::scoped_lock lock(muxQueue);
|
||||
return deqQueue.front();
|
||||
}
|
||||
|
||||
// Returns and maintains item at back of Queue
|
||||
const T& back()
|
||||
{
|
||||
std::scoped_lock lock(muxQueue);
|
||||
return deqQueue.back();
|
||||
}
|
||||
|
||||
// Removes and returns item from front of Queue
|
||||
T pop_front()
|
||||
{
|
||||
std::scoped_lock lock(muxQueue);
|
||||
auto t = std::move(deqQueue.front());
|
||||
deqQueue.pop_front();
|
||||
return t;
|
||||
}
|
||||
|
||||
// Removes and returns item from back of Queue
|
||||
T pop_back()
|
||||
{
|
||||
std::scoped_lock lock(muxQueue);
|
||||
auto t = std::move(deqQueue.back());
|
||||
deqQueue.pop_back();
|
||||
return t;
|
||||
}
|
||||
|
||||
// Adds an item to back of Queue
|
||||
void push_back(const T& item)
|
||||
{
|
||||
std::scoped_lock lock(muxQueue);
|
||||
deqQueue.emplace_back(std::move(item));
|
||||
|
||||
std::unique_lock<std::mutex> ul(muxBlocking);
|
||||
cvBlocking.notify_one();
|
||||
}
|
||||
|
||||
// Adds an item to front of Queue
|
||||
void push_front(const T& item)
|
||||
{
|
||||
std::scoped_lock lock(muxQueue);
|
||||
deqQueue.emplace_front(std::move(item));
|
||||
|
||||
std::unique_lock<std::mutex> ul(muxBlocking);
|
||||
cvBlocking.notify_one();
|
||||
}
|
||||
|
||||
// Returns true if Queue has no items
|
||||
bool empty()
|
||||
{
|
||||
std::scoped_lock lock(muxQueue);
|
||||
return deqQueue.empty();
|
||||
}
|
||||
|
||||
// Returns number of items in Queue
|
||||
size_t count()
|
||||
{
|
||||
std::scoped_lock lock(muxQueue);
|
||||
return deqQueue.size();
|
||||
}
|
||||
|
||||
// Clears Queue
|
||||
void clear()
|
||||
{
|
||||
std::scoped_lock lock(muxQueue);
|
||||
deqQueue.clear();
|
||||
}
|
||||
|
||||
void wait()
|
||||
{
|
||||
while (empty())
|
||||
{
|
||||
std::unique_lock<std::mutex> ul(muxBlocking);
|
||||
cvBlocking.wait(ul);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
std::mutex muxQueue;
|
||||
std::deque<T> deqQueue;
|
||||
std::condition_variable cvBlocking;
|
||||
std::mutex muxBlocking;
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -1,64 +0,0 @@
|
||||
/*
|
||||
MMO Client/Server Framework using ASIO
|
||||
"Happy Birthday Mrs Javidx9!" - javidx9
|
||||
|
||||
Videos:
|
||||
Part #1: https://youtu.be/2hNdkYInj4g
|
||||
Part #2: https://youtu.be/UbjxGvrDrbw
|
||||
|
||||
License (OLC-3)
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Copyright 2018 - 2020 OneLoneCoder.com
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions or derivations of source code must retain the above
|
||||
copyright notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions or derivative works in binary form must reproduce
|
||||
the above copyright notice. This list of conditions and the following
|
||||
disclaimer must be reproduced in the documentation and/or other
|
||||
materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Links
|
||||
~~~~~
|
||||
YouTube: https://www.youtube.com/javidx9
|
||||
Discord: https://discord.gg/WhwHUMV
|
||||
Twitter: https://www.twitter.com/javidx9
|
||||
Twitch: https://www.twitch.tv/javidx9
|
||||
GitHub: https://www.github.com/onelonecoder
|
||||
Homepage: https://www.onelonecoder.com
|
||||
|
||||
Author
|
||||
~~~~~~
|
||||
David Barr, aka javidx9, ©OneLoneCoder 2019, 2020
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "net_common.h"
|
||||
#include "net_tsqueue.h"
|
||||
#include "net_message.h"
|
||||
#include "net_client.h"
|
||||
#include "net_server.h"
|
||||
#include "net_connection.h"
|
||||
@ -1,3 +0,0 @@
|
||||
Networking Code
|
||||
|
||||
This version is incomplete! Its fine for experimenting, but it will consume whole processor cores, and it has a memory leak. These will be identified and resolved in Part#3 when we look at making it robust.
|
||||
@ -1,278 +0,0 @@
|
||||
|
||||
#include "../MMO_Server/MMO_Common.h"
|
||||
|
||||
#define OLC_PGEX_TRANSFORMEDVIEW
|
||||
#include "olcPGEX_TransformedView.h"
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
class MMOGame : public olc::PixelGameEngine, olc::net::client_interface<GameMsg>
|
||||
{
|
||||
public:
|
||||
MMOGame()
|
||||
{
|
||||
sAppName = "MMO Client";
|
||||
}
|
||||
|
||||
private:
|
||||
olc::TileTransformedView tv;
|
||||
|
||||
std::string sWorldMap =
|
||||
"################################"
|
||||
"#..............................#"
|
||||
"#..............................#"
|
||||
"#..............................#"
|
||||
"#..............................#"
|
||||
"#..............................#"
|
||||
"#..............................#"
|
||||
"#..............................#"
|
||||
"#..............................#"
|
||||
"#..............................#"
|
||||
"#..............................#"
|
||||
"#..............................#"
|
||||
"#..........####...####.........#"
|
||||
"#..........#.........#.........#"
|
||||
"#..........#.........#.........#"
|
||||
"#..........#.........#.........#"
|
||||
"#..........##############......#"
|
||||
"#..............................#"
|
||||
"#..................#.#.#.#.....#"
|
||||
"#..............................#"
|
||||
"#..................#.#.#.#.....#"
|
||||
"#..............................#"
|
||||
"#..............................#"
|
||||
"#..............................#"
|
||||
"#..............................#"
|
||||
"#..............................#"
|
||||
"#..............................#"
|
||||
"#..............................#"
|
||||
"#..............................#"
|
||||
"#..............................#"
|
||||
"#..............................#"
|
||||
"################################";
|
||||
|
||||
olc::vi2d vWorldSize = { 32, 32 };
|
||||
|
||||
private:
|
||||
std::unordered_map<uint32_t, sPlayerDescription> mapObjects;
|
||||
uint32_t nPlayerID = 0;
|
||||
sPlayerDescription descPlayer;
|
||||
|
||||
bool bWaitingForConnection = true;
|
||||
|
||||
public:
|
||||
bool OnUserCreate() override
|
||||
{
|
||||
tv = olc::TileTransformedView({ ScreenWidth(), ScreenHeight() }, { 8, 8 });
|
||||
|
||||
//mapObjects[0].nUniqueID = 0;
|
||||
//mapObjects[0].vPos = { 3.0f, 3.0f };
|
||||
|
||||
if (Connect("127.0.0.1", 60000))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool OnUserUpdate(float fElapsedTime) override
|
||||
{
|
||||
// Check for incoming network messages
|
||||
if (IsConnected())
|
||||
{
|
||||
while (!Incoming().empty())
|
||||
{
|
||||
auto msg = Incoming().pop_front().msg;
|
||||
|
||||
switch (msg.header.id)
|
||||
{
|
||||
case(GameMsg::Client_Accepted):
|
||||
{
|
||||
std::cout << "Server accepted client - you're in!\n";
|
||||
olc::net::message<GameMsg> msg;
|
||||
msg.header.id = GameMsg::Client_RegisterWithServer;
|
||||
descPlayer.vPos = { 3.0f, 3.0f };
|
||||
msg << descPlayer;
|
||||
Send(msg);
|
||||
break;
|
||||
}
|
||||
|
||||
case(GameMsg::Client_AssignID):
|
||||
{
|
||||
// Server is assigning us OUR id
|
||||
msg >> nPlayerID;
|
||||
std::cout << "Assigned Client ID = " << nPlayerID << "\n";
|
||||
break;
|
||||
}
|
||||
|
||||
case(GameMsg::Game_AddPlayer):
|
||||
{
|
||||
sPlayerDescription desc;
|
||||
msg >> desc;
|
||||
mapObjects.insert_or_assign(desc.nUniqueID, desc);
|
||||
|
||||
if (desc.nUniqueID == nPlayerID)
|
||||
{
|
||||
// Now we exist in game world
|
||||
bWaitingForConnection = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case(GameMsg::Game_RemovePlayer):
|
||||
{
|
||||
uint32_t nRemovalID = 0;
|
||||
msg >> nRemovalID;
|
||||
mapObjects.erase(nRemovalID);
|
||||
break;
|
||||
}
|
||||
|
||||
case(GameMsg::Game_UpdatePlayer):
|
||||
{
|
||||
sPlayerDescription desc;
|
||||
msg >> desc;
|
||||
mapObjects.insert_or_assign(desc.nUniqueID, desc);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bWaitingForConnection)
|
||||
{
|
||||
Clear(olc::DARK_BLUE);
|
||||
DrawString({ 10,10 }, "Waiting To Connect...", olc::WHITE);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Control of Player Object
|
||||
mapObjects[nPlayerID].vVel = { 0.0f, 0.0f };
|
||||
if (GetKey(olc::Key::W).bHeld) mapObjects[nPlayerID].vVel += { 0.0f, -1.0f };
|
||||
if (GetKey(olc::Key::S).bHeld) mapObjects[nPlayerID].vVel += { 0.0f, +1.0f };
|
||||
if (GetKey(olc::Key::A).bHeld) mapObjects[nPlayerID].vVel += { -1.0f, 0.0f };
|
||||
if (GetKey(olc::Key::D).bHeld) mapObjects[nPlayerID].vVel += { +1.0f, 0.0f };
|
||||
|
||||
if (mapObjects[nPlayerID].vVel.mag2() > 0)
|
||||
mapObjects[nPlayerID].vVel = mapObjects[nPlayerID].vVel.norm() * 4.0f;
|
||||
|
||||
// Update objects locally
|
||||
for (auto& object : mapObjects)
|
||||
{
|
||||
// Where will object be worst case?
|
||||
olc::vf2d vPotentialPosition = object.second.vPos + object.second.vVel * fElapsedTime;
|
||||
|
||||
// Extract region of world cells that could have collision this frame
|
||||
olc::vi2d vCurrentCell = object.second.vPos.floor();
|
||||
olc::vi2d vTargetCell = vPotentialPosition;
|
||||
olc::vi2d vAreaTL = (vCurrentCell.min(vTargetCell) - olc::vi2d(1, 1)).max({ 0,0 });
|
||||
olc::vi2d vAreaBR = (vCurrentCell.max(vTargetCell) + olc::vi2d(1, 1)).min(vWorldSize);
|
||||
|
||||
// Iterate through each cell in test area
|
||||
olc::vi2d vCell;
|
||||
for (vCell.y = vAreaTL.y; vCell.y <= vAreaBR.y; vCell.y++)
|
||||
{
|
||||
for (vCell.x = vAreaTL.x; vCell.x <= vAreaBR.x; vCell.x++)
|
||||
{
|
||||
// Check if the cell is actually solid...
|
||||
// olc::vf2d vCellMiddle = vCell.floor();
|
||||
if (sWorldMap[vCell.y * vWorldSize.x + vCell.x] == '#')
|
||||
{
|
||||
// ...it is! So work out nearest point to future player position, around perimeter
|
||||
// of cell rectangle. We can test the distance to this point to see if we have
|
||||
// collided.
|
||||
|
||||
olc::vf2d vNearestPoint;
|
||||
// Inspired by this (very clever btw)
|
||||
// https://stackoverflow.com/questions/45370692/circle-rectangle-collision-response
|
||||
vNearestPoint.x = std::max(float(vCell.x), std::min(vPotentialPosition.x, float(vCell.x + 1)));
|
||||
vNearestPoint.y = std::max(float(vCell.y), std::min(vPotentialPosition.y, float(vCell.y + 1)));
|
||||
|
||||
// But modified to work :P
|
||||
olc::vf2d vRayToNearest = vNearestPoint - vPotentialPosition;
|
||||
float fOverlap = object.second.fRadius - vRayToNearest.mag();
|
||||
if (std::isnan(fOverlap)) fOverlap = 0;// Thanks Dandistine!
|
||||
|
||||
// If overlap is positive, then a collision has occurred, so we displace backwards by the
|
||||
// overlap amount. The potential position is then tested against other tiles in the area
|
||||
// therefore "statically" resolving the collision
|
||||
if (fOverlap > 0)
|
||||
{
|
||||
// Statically resolve the collision
|
||||
vPotentialPosition = vPotentialPosition - vRayToNearest.norm() * fOverlap;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set the objects new position to the allowed potential position
|
||||
object.second.vPos = vPotentialPosition;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Handle Pan & Zoom
|
||||
if (GetMouse(2).bPressed) tv.StartPan(GetMousePos());
|
||||
if (GetMouse(2).bHeld) tv.UpdatePan(GetMousePos());
|
||||
if (GetMouse(2).bReleased) tv.EndPan(GetMousePos());
|
||||
if (GetMouseWheel() > 0) tv.ZoomAtScreenPos(1.5f, GetMousePos());
|
||||
if (GetMouseWheel() < 0) tv.ZoomAtScreenPos(0.75f, GetMousePos());
|
||||
|
||||
// Clear World
|
||||
Clear(olc::BLACK);
|
||||
|
||||
// Draw World
|
||||
olc::vi2d vTL = tv.GetTopLeftTile().max({ 0,0 });
|
||||
olc::vi2d vBR = tv.GetBottomRightTile().min(vWorldSize);
|
||||
olc::vi2d vTile;
|
||||
for (vTile.y = vTL.y; vTile.y < vBR.y; vTile.y++)
|
||||
for (vTile.x = vTL.x; vTile.x < vBR.x; vTile.x++)
|
||||
{
|
||||
if (sWorldMap[vTile.y * vWorldSize.x + vTile.x] == '#')
|
||||
{
|
||||
tv.DrawRect(vTile, { 1.0f, 1.0f });
|
||||
tv.DrawRect(olc::vf2d(vTile) + olc::vf2d(0.1f, 0.1f), { 0.8f, 0.8f });
|
||||
}
|
||||
}
|
||||
|
||||
// Draw World Objects
|
||||
for (auto& object : mapObjects)
|
||||
{
|
||||
// Draw Boundary
|
||||
tv.DrawCircle(object.second.vPos, object.second.fRadius);
|
||||
|
||||
// Draw Velocity
|
||||
if (object.second.vVel.mag2() > 0)
|
||||
tv.DrawLine(object.second.vPos, object.second.vPos + object.second.vVel.norm() * object.second.fRadius, olc::MAGENTA);
|
||||
|
||||
// Draw Name
|
||||
olc::vi2d vNameSize = GetTextSizeProp("ID: " + std::to_string(object.first));
|
||||
tv.DrawStringPropDecal(object.second.vPos - olc::vf2d{ vNameSize.x * 0.5f * 0.25f * 0.125f, -object.second.fRadius * 1.25f }, "ID: " + std::to_string(object.first), olc::BLUE, { 0.25f, 0.25f });
|
||||
}
|
||||
|
||||
// Send player description
|
||||
olc::net::message<GameMsg> msg;
|
||||
msg.header.id = GameMsg::Game_UpdatePlayer;
|
||||
msg << mapObjects[nPlayerID];
|
||||
Send(msg);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
MMOGame demo;
|
||||
if (demo.Construct(480, 480, 1, 1))
|
||||
demo.Start();
|
||||
return 0;
|
||||
}
|
||||
@ -1,39 +0,0 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
|
||||
#define OLC_PGE_APPLICATION
|
||||
#include "olcPixelGameEngine.h"
|
||||
|
||||
#define OLC_PGEX_NETWORK
|
||||
#include "olcPGEX_Network.h"
|
||||
|
||||
enum class GameMsg : uint32_t
|
||||
{
|
||||
Server_GetStatus,
|
||||
Server_GetPing,
|
||||
|
||||
Client_Accepted,
|
||||
Client_AssignID,
|
||||
Client_RegisterWithServer,
|
||||
Client_UnregisterWithServer,
|
||||
|
||||
Game_AddPlayer,
|
||||
Game_RemovePlayer,
|
||||
Game_UpdatePlayer,
|
||||
};
|
||||
|
||||
struct sPlayerDescription
|
||||
{
|
||||
uint32_t nUniqueID = 0;
|
||||
uint32_t nAvatarID = 0;
|
||||
|
||||
uint32_t nHealth = 100;
|
||||
uint32_t nAmmo = 20;
|
||||
uint32_t nKills = 0;
|
||||
uint32_t nDeaths = 0;
|
||||
|
||||
float fRadius = 0.5f;
|
||||
|
||||
olc::vf2d vPos;
|
||||
olc::vf2d vVel;
|
||||
};
|
||||
@ -1,128 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "MMO_Common.h"
|
||||
|
||||
class GameServer : public olc::net::server_interface<GameMsg>
|
||||
{
|
||||
public:
|
||||
GameServer(uint16_t nPort) : olc::net::server_interface<GameMsg>(nPort)
|
||||
{
|
||||
}
|
||||
|
||||
std::unordered_map<uint32_t, sPlayerDescription> m_mapPlayerRoster;
|
||||
std::vector<uint32_t> m_vGarbageIDs;
|
||||
|
||||
protected:
|
||||
bool OnClientConnect(std::shared_ptr<olc::net::connection<GameMsg>> client) override
|
||||
{
|
||||
// For now we will allow all
|
||||
return true;
|
||||
}
|
||||
|
||||
void OnClientValidated(std::shared_ptr<olc::net::connection<GameMsg>> client) override
|
||||
{
|
||||
// Client passed validation check, so send them a message informing
|
||||
// them they can continue to communicate
|
||||
olc::net::message<GameMsg> msg;
|
||||
msg.header.id = GameMsg::Client_Accepted;
|
||||
client->Send(msg);
|
||||
}
|
||||
|
||||
void OnClientDisconnect(std::shared_ptr<olc::net::connection<GameMsg>> client) override
|
||||
{
|
||||
if (client)
|
||||
{
|
||||
if (m_mapPlayerRoster.find(client->GetID()) == m_mapPlayerRoster.end())
|
||||
{
|
||||
// client never added to roster, so just let it disappear
|
||||
}
|
||||
else
|
||||
{
|
||||
auto& pd = m_mapPlayerRoster[client->GetID()];
|
||||
std::cout << "[UNGRACEFUL REMOVAL]:" + std::to_string(pd.nUniqueID) + "\n";
|
||||
m_mapPlayerRoster.erase(client->GetID());
|
||||
m_vGarbageIDs.push_back(client->GetID());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void OnMessage(std::shared_ptr<olc::net::connection<GameMsg>> client, olc::net::message<GameMsg>& msg) override
|
||||
{
|
||||
if (!m_vGarbageIDs.empty())
|
||||
{
|
||||
for (auto pid : m_vGarbageIDs)
|
||||
{
|
||||
olc::net::message<GameMsg> m;
|
||||
m.header.id = GameMsg::Game_RemovePlayer;
|
||||
m << pid;
|
||||
std::cout << "Removing " << pid << "\n";
|
||||
MessageAllClients(m);
|
||||
}
|
||||
m_vGarbageIDs.clear();
|
||||
}
|
||||
|
||||
|
||||
|
||||
switch (msg.header.id)
|
||||
{
|
||||
case GameMsg::Client_RegisterWithServer:
|
||||
{
|
||||
sPlayerDescription desc;
|
||||
msg >> desc;
|
||||
desc.nUniqueID = client->GetID();
|
||||
m_mapPlayerRoster.insert_or_assign(desc.nUniqueID, desc);
|
||||
|
||||
olc::net::message<GameMsg> msgSendID;
|
||||
msgSendID.header.id = GameMsg::Client_AssignID;
|
||||
msgSendID << desc.nUniqueID;
|
||||
MessageClient(client, msgSendID);
|
||||
|
||||
olc::net::message<GameMsg> msgAddPlayer;
|
||||
msgAddPlayer.header.id = GameMsg::Game_AddPlayer;
|
||||
msgAddPlayer << desc;
|
||||
MessageAllClients(msgAddPlayer);
|
||||
|
||||
for (const auto& player : m_mapPlayerRoster)
|
||||
{
|
||||
olc::net::message<GameMsg> msgAddOtherPlayers;
|
||||
msgAddOtherPlayers.header.id = GameMsg::Game_AddPlayer;
|
||||
msgAddOtherPlayers << player.second;
|
||||
MessageClient(client, msgAddOtherPlayers);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case GameMsg::Client_UnregisterWithServer:
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
case GameMsg::Game_UpdatePlayer:
|
||||
{
|
||||
// Simply bounce update to everyone except incoming client
|
||||
MessageAllClients(msg, client);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
GameServer server(60000);
|
||||
server.Start();
|
||||
|
||||
while (1)
|
||||
{
|
||||
server.Update(-1, true);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -1,658 +0,0 @@
|
||||
/*
|
||||
olcPGEX_TransformedView.h
|
||||
|
||||
+-------------------------------------------------------------+
|
||||
| OneLoneCoder Pixel Game Engine Extension |
|
||||
| Transformed View v1.00 |
|
||||
+-------------------------------------------------------------+
|
||||
|
||||
NOTE: UNDER ACTIVE DEVELOPMENT - THERE ARE BUGS/GLITCHES
|
||||
|
||||
What is this?
|
||||
~~~~~~~~~~~~~
|
||||
This extension provides drawing routines that are compatible with
|
||||
changeable world and screen spaces. For example you can pan and
|
||||
zoom, and all PGE drawing routines will automatically adopt the current
|
||||
world scales and offsets.
|
||||
|
||||
License (OLC-3)
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Copyright 2018 - 2021 OneLoneCoder.com
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions or derivations of source code must retain the above
|
||||
copyright notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions or derivative works in binary form must reproduce
|
||||
the above copyright notice. This list of conditions and the following
|
||||
disclaimer must be reproduced in the documentation and/or other
|
||||
materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Links
|
||||
~~~~~
|
||||
YouTube: https://www.youtube.com/javidx9
|
||||
Discord: https://discord.gg/WhwHUMV
|
||||
Twitter: https://www.twitter.com/javidx9
|
||||
Twitch: https://www.twitch.tv/javidx9
|
||||
GitHub: https://www.github.com/onelonecoder
|
||||
Homepage: https://www.onelonecoder.com
|
||||
|
||||
Author
|
||||
~~~~~~
|
||||
David Barr, aka javidx9, ©OneLoneCoder 2019, 2020, 2021
|
||||
|
||||
Revisions:
|
||||
1.00: Initial Release
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#ifndef OLC_PGEX_TRANSFORMEDVIEW_H
|
||||
#define OLC_PGEX_TRANSFORMEDVIEW_H
|
||||
|
||||
#include "olcPixelGameEngine.h"
|
||||
|
||||
|
||||
|
||||
namespace olc
|
||||
{
|
||||
class TransformedView : public olc::PGEX
|
||||
{
|
||||
public:
|
||||
TransformedView() = default;
|
||||
virtual void Initialise(const olc::vi2d& vViewArea, const olc::vf2d& vPixelScale = { 1.0f, 1.0f });
|
||||
|
||||
public:
|
||||
void SetWorldOffset(const olc::vf2d& vOffset);
|
||||
void MoveWorldOffset(const olc::vf2d& vDeltaOffset);
|
||||
void SetWorldScale(const olc::vf2d& vScale);
|
||||
void SetViewArea(const olc::vi2d& vViewArea);
|
||||
olc::vf2d GetWorldTL() const;
|
||||
olc::vf2d GetWorldBR() const;
|
||||
olc::vf2d GetWorldVisibleArea() const;
|
||||
void ZoomAtScreenPos(const float fDeltaZoom, const olc::vi2d& vPos);
|
||||
void SetZoom(const float fZoom, const olc::vi2d& vPos);
|
||||
void StartPan(const olc::vi2d& vPos);
|
||||
void UpdatePan(const olc::vi2d& vPos);
|
||||
void EndPan(const olc::vi2d& vPos);
|
||||
const olc::vf2d& GetWorldOffset() const;
|
||||
const olc::vf2d& GetWorldScale() const;
|
||||
virtual olc::vi2d WorldToScreen(const olc::vf2d& vWorldPos) const;
|
||||
virtual olc::vf2d ScreenToWorld(const olc::vi2d& vScreenPos) const;
|
||||
virtual olc::vf2d ScaleToWorld(const olc::vi2d& vScreenSize) const;
|
||||
virtual olc::vi2d ScaleToScreen(const olc::vf2d& vWorldSize) const;
|
||||
virtual bool IsPointVisible(const olc::vf2d& vPos) const;
|
||||
virtual bool IsRectVisible(const olc::vf2d& vPos, const olc::vf2d& vSize) const;
|
||||
|
||||
protected:
|
||||
olc::vf2d m_vWorldOffset = { 0.0f, 0.0f };
|
||||
olc::vf2d m_vWorldScale = { 1.0f, 1.0f };
|
||||
olc::vf2d m_vRecipPixel = { 1.0f, 1.0f };
|
||||
olc::vf2d m_vPixelScale = { 1.0f, 1.0f };
|
||||
bool m_bPanning = false;
|
||||
olc::vf2d m_vStartPan = { 0.0f, 0.0f };
|
||||
olc::vi2d m_vViewArea;
|
||||
|
||||
public: // Hopefully, these should look familiar!
|
||||
// Plots a single point
|
||||
virtual bool Draw(float x, float y, olc::Pixel p = olc::WHITE);
|
||||
bool Draw(const olc::vf2d& pos, olc::Pixel p = olc::WHITE);
|
||||
// Draws a line from (x1,y1) to (x2,y2)
|
||||
void DrawLine(float x1, float y1, float x2, float y2, olc::Pixel p = olc::WHITE, uint32_t pattern = 0xFFFFFFFF);
|
||||
void DrawLine(const olc::vf2d& pos1, const olc::vf2d& pos2, olc::Pixel p = olc::WHITE, uint32_t pattern = 0xFFFFFFFF);
|
||||
// Draws a circle located at (x,y) with radius
|
||||
void DrawCircle(float x, float y, float radius, olc::Pixel p = olc::WHITE, uint8_t mask = 0xFF);
|
||||
void DrawCircle(const olc::vf2d& pos, float radius, olc::Pixel p = olc::WHITE, uint8_t mask = 0xFF);
|
||||
// Fills a circle located at (x,y) with radius
|
||||
void FillCircle(float x, float y, float radius, olc::Pixel p = olc::WHITE);
|
||||
void FillCircle(const olc::vf2d& pos, float radius, olc::Pixel p = olc::WHITE);
|
||||
// Draws a rectangle at (x,y) to (x+w,y+h)
|
||||
void DrawRect(float x, float y, float w, float h, olc::Pixel p = olc::WHITE);
|
||||
void DrawRect(const olc::vf2d& pos, const olc::vf2d& size, olc::Pixel p = olc::WHITE);
|
||||
// Fills a rectangle at (x,y) to (x+w,y+h)
|
||||
void FillRect(float x, float y, float w, float h, olc::Pixel p = olc::WHITE);
|
||||
void FillRect(const olc::vf2d& pos, const olc::vf2d& size, olc::Pixel p = olc::WHITE);
|
||||
// Draws a triangle between points (x1,y1), (x2,y2) and (x3,y3)
|
||||
void DrawTriangle(float x1, float y1, float x2, float y2, float x3, float y3, olc::Pixel p = olc::WHITE);
|
||||
void DrawTriangle(const olc::vf2d& pos1, const olc::vf2d& pos2, const olc::vf2d& pos3, olc::Pixel p = olc::WHITE);
|
||||
// Flat fills a triangle between points (x1,y1), (x2,y2) and (x3,y3)
|
||||
void FillTriangle(float x1, float y1, float x2, float y2, float x3, float y3, olc::Pixel p = olc::WHITE);
|
||||
void FillTriangle(const olc::vf2d& pos1, const olc::vf2d& pos2, const olc::vf2d& pos3, olc::Pixel p = olc::WHITE);
|
||||
// Draws an entire sprite at location (x,y)
|
||||
void DrawSprite(float x, float y, olc::Sprite* sprite, float scalex = 1, float scaley = 1, uint8_t flip = olc::Sprite::NONE);
|
||||
void DrawSprite(const olc::vf2d& pos, olc::Sprite* sprite, const olc::vf2d& scale = { 1.0f, 1.0f }, uint8_t flip = olc::Sprite::NONE);
|
||||
// Draws an area of a sprite at location (x,y), where the
|
||||
// selected area is (ox,oy) to (ox+w,oy+h)
|
||||
void DrawPartialSprite(float x, float y, Sprite* sprite, int32_t ox, int32_t oy, int32_t w, int32_t h, float scalex = 1, float scaley = 1, uint8_t flip = olc::Sprite::NONE);
|
||||
void DrawPartialSprite(const olc::vf2d& pos, Sprite* sprite, const olc::vi2d& sourcepos, const olc::vi2d& size, const olc::vf2d& scale = { 1.0f, 1.0f }, uint8_t flip = olc::Sprite::NONE);
|
||||
void DrawString(float x, float y, const std::string& sText, Pixel col, const olc::vf2d& scale);
|
||||
void DrawString(const olc::vf2d& pos, const std::string& sText, const Pixel col, const olc::vf2d& scale);
|
||||
|
||||
|
||||
// Draws a whole decal, with optional scale and tinting
|
||||
void DrawDecal(const olc::vf2d& pos, olc::Decal* decal, const olc::vf2d& scale = { 1.0f,1.0f }, const olc::Pixel& tint = olc::WHITE);
|
||||
// Draws a region of a decal, with optional scale and tinting
|
||||
void DrawPartialDecal(const olc::vf2d& pos, olc::Decal* decal, const olc::vf2d& source_pos, const olc::vf2d& source_size, const olc::vf2d& scale = { 1.0f,1.0f }, const olc::Pixel& tint = olc::WHITE);
|
||||
void DrawPartialDecal(const olc::vf2d& pos, const olc::vf2d& size, olc::Decal* decal, const olc::vf2d& source_pos, const olc::vf2d& source_size, const olc::Pixel& tint = olc::WHITE);
|
||||
// Draws fully user controlled 4 vertices, pos(pixels), uv(pixels), colours
|
||||
void DrawExplicitDecal(olc::Decal* decal, const olc::vf2d* pos, const olc::vf2d* uv, const olc::Pixel* col, uint32_t elements = 4);
|
||||
//// Draws a decal with 4 arbitrary points, warping the texture to look "correct"
|
||||
void DrawWarpedDecal(olc::Decal* decal, const olc::vf2d(&pos)[4], const olc::Pixel& tint = olc::WHITE);
|
||||
void DrawWarpedDecal(olc::Decal* decal, const olc::vf2d* pos, const olc::Pixel& tint = olc::WHITE);
|
||||
void DrawWarpedDecal(olc::Decal* decal, const std::array<olc::vf2d, 4>& pos, const olc::Pixel& tint = olc::WHITE);
|
||||
//// As above, but you can specify a region of a decal source sprite
|
||||
void DrawPartialWarpedDecal(olc::Decal* decal, const olc::vf2d(&pos)[4], const olc::vf2d& source_pos, const olc::vf2d& source_size, const olc::Pixel& tint = olc::WHITE);
|
||||
void DrawPartialWarpedDecal(olc::Decal* decal, const olc::vf2d* pos, const olc::vf2d& source_pos, const olc::vf2d& source_size, const olc::Pixel& tint = olc::WHITE);
|
||||
void DrawPartialWarpedDecal(olc::Decal* decal, const std::array<olc::vf2d, 4>& pos, const olc::vf2d& source_pos, const olc::vf2d& source_size, const olc::Pixel& tint = olc::WHITE);
|
||||
//// Draws a decal rotated to specified angle, wit point of rotation offset
|
||||
void DrawRotatedDecal(const olc::vf2d& pos, olc::Decal* decal, const float fAngle, const olc::vf2d& center = { 0.0f, 0.0f }, const olc::vf2d& scale = { 1.0f,1.0f }, const olc::Pixel& tint = olc::WHITE);
|
||||
void DrawPartialRotatedDecal(const olc::vf2d& pos, olc::Decal* decal, const float fAngle, const olc::vf2d& center, const olc::vf2d& source_pos, const olc::vf2d& source_size, const olc::vf2d& scale = { 1.0f, 1.0f }, const olc::Pixel& tint = olc::WHITE);
|
||||
// Draws a multiline string as a decal, with tiniting and scaling
|
||||
void DrawStringDecal(const olc::vf2d& pos, const std::string& sText, const olc::Pixel col = olc::WHITE, const olc::vf2d& scale = { 1.0f, 1.0f });
|
||||
void DrawStringPropDecal(const olc::vf2d& pos, const std::string& sText, const olc::Pixel col = olc::WHITE, const olc::vf2d& scale = { 1.0f, 1.0f });
|
||||
// Draws a single shaded filled rectangle as a decal
|
||||
void FillRectDecal(const olc::vf2d& pos, const olc::vf2d& size, const olc::Pixel col = olc::WHITE);
|
||||
// Draws a corner shaded rectangle as a decal
|
||||
void GradientFillRectDecal(const olc::vf2d& pos, const olc::vf2d& size, const olc::Pixel colTL, const olc::Pixel colBL, const olc::Pixel colBR, const olc::Pixel colTR);
|
||||
// Draws an arbitrary convex textured polygon using GPU
|
||||
void DrawPolygonDecal(olc::Decal* decal, const std::vector<olc::vf2d>& pos, const std::vector<olc::vf2d>& uv, const olc::Pixel tint = olc::WHITE);
|
||||
|
||||
};
|
||||
|
||||
class TileTransformedView : public TransformedView
|
||||
{
|
||||
public:
|
||||
TileTransformedView() = default;
|
||||
TileTransformedView(const olc::vi2d& vViewArea, const olc::vi2d& vTileSize);
|
||||
|
||||
public:
|
||||
void SetRangeX(const bool bRanged, const int32_t nMin = 0, const int32_t nMax = 0);
|
||||
void SetRangeY(const bool bRanged, const int32_t nMin = 0, const int32_t nMax = 0);
|
||||
olc::vi2d GetTopLeftTile() const;
|
||||
olc::vi2d GetBottomRightTile() const;
|
||||
olc::vi2d GetVisibleTiles() const;
|
||||
olc::vi2d GetTileUnderScreenPos(const olc::vi2d& vPos) const;
|
||||
const olc::vi2d GetTileOffset() const;
|
||||
|
||||
private:
|
||||
bool m_bRangedX = false;
|
||||
int32_t m_nMinRangeX = 0;
|
||||
int32_t m_nMaxRangeX = 0;
|
||||
bool m_bRangedY = false;
|
||||
int32_t m_nMinRangeY = 0;
|
||||
int32_t m_nMaxRangeY = 0;
|
||||
};
|
||||
}
|
||||
|
||||
#ifdef OLC_PGEX_TRANSFORMEDVIEW
|
||||
#undef OLC_PGEX_TRANSFORMEDVIEW
|
||||
|
||||
namespace olc
|
||||
{
|
||||
|
||||
void TransformedView::Initialise(const olc::vi2d& vViewArea, const olc::vf2d& vPixelScale)
|
||||
{
|
||||
SetViewArea(vViewArea);
|
||||
SetWorldScale(vPixelScale);
|
||||
m_vPixelScale = vPixelScale;
|
||||
m_vRecipPixel = 1.0f / m_vPixelScale;
|
||||
}
|
||||
|
||||
void TransformedView::SetWorldOffset(const olc::vf2d& vOffset)
|
||||
{
|
||||
m_vWorldOffset = vOffset;
|
||||
}
|
||||
|
||||
void TransformedView::MoveWorldOffset(const olc::vf2d& vDeltaOffset)
|
||||
{
|
||||
m_vWorldOffset += vDeltaOffset;
|
||||
}
|
||||
|
||||
void TransformedView::SetWorldScale(const olc::vf2d& vScale)
|
||||
{
|
||||
m_vWorldScale = vScale;
|
||||
}
|
||||
|
||||
void TransformedView::SetViewArea(const olc::vi2d& vViewArea)
|
||||
{
|
||||
m_vViewArea = vViewArea;
|
||||
}
|
||||
|
||||
olc::vf2d TransformedView::GetWorldTL() const
|
||||
{
|
||||
return ScreenToWorld({ 0,0 });
|
||||
}
|
||||
|
||||
olc::vf2d TransformedView::GetWorldBR() const
|
||||
{
|
||||
return TransformedView::ScreenToWorld(m_vViewArea);
|
||||
}
|
||||
|
||||
olc::vf2d TransformedView::GetWorldVisibleArea() const
|
||||
{
|
||||
return GetWorldBR() - GetWorldTL();
|
||||
}
|
||||
|
||||
void TransformedView::ZoomAtScreenPos(const float fDeltaZoom, const olc::vi2d& vPos)
|
||||
{
|
||||
olc::vf2d vOffsetBeforeZoom = ScreenToWorld(vPos);
|
||||
m_vWorldScale *= fDeltaZoom;
|
||||
olc::vf2d vOffsetAfterZoom = ScreenToWorld(vPos);
|
||||
m_vWorldOffset += vOffsetBeforeZoom - vOffsetAfterZoom;
|
||||
}
|
||||
|
||||
void TransformedView::SetZoom(const float fZoom, const olc::vi2d& vPos)
|
||||
{
|
||||
olc::vf2d vOffsetBeforeZoom = ScreenToWorld(vPos);
|
||||
m_vWorldScale = { fZoom, fZoom };
|
||||
olc::vf2d vOffsetAfterZoom = ScreenToWorld(vPos);
|
||||
m_vWorldOffset += vOffsetBeforeZoom - vOffsetAfterZoom;
|
||||
}
|
||||
|
||||
void TransformedView::StartPan(const olc::vi2d& vPos)
|
||||
{
|
||||
m_bPanning = true;
|
||||
m_vStartPan = olc::vf2d(vPos);
|
||||
}
|
||||
|
||||
void TransformedView::UpdatePan(const olc::vi2d& vPos)
|
||||
{
|
||||
if (m_bPanning)
|
||||
{
|
||||
m_vWorldOffset -= (olc::vf2d(vPos) - m_vStartPan) / m_vWorldScale;
|
||||
m_vStartPan = olc::vf2d(vPos);
|
||||
}
|
||||
}
|
||||
|
||||
void TransformedView::EndPan(const olc::vi2d& vPos)
|
||||
{
|
||||
UpdatePan(vPos);
|
||||
m_bPanning = false;
|
||||
}
|
||||
|
||||
const olc::vf2d& TransformedView::GetWorldOffset() const
|
||||
{
|
||||
return m_vWorldOffset;
|
||||
}
|
||||
|
||||
const olc::vf2d& TransformedView::GetWorldScale() const
|
||||
{
|
||||
return m_vWorldScale;
|
||||
}
|
||||
|
||||
olc::vi2d TransformedView::WorldToScreen(const olc::vf2d& vWorldPos) const
|
||||
{
|
||||
olc::vf2d vFloat = ((vWorldPos - m_vWorldOffset) * m_vWorldScale);
|
||||
vFloat = { std::floor(vFloat.x), std::floor(vFloat.y) };
|
||||
return vFloat;
|
||||
}
|
||||
|
||||
olc::vf2d TransformedView::ScreenToWorld(const olc::vi2d& vScreenPos) const
|
||||
{
|
||||
return (olc::vf2d(vScreenPos) / m_vWorldScale) + m_vWorldOffset;
|
||||
}
|
||||
|
||||
olc::vf2d TransformedView::ScaleToWorld(const olc::vi2d& vScreenSize) const
|
||||
{
|
||||
return (olc::vf2d(vScreenSize) / m_vWorldScale);
|
||||
}
|
||||
|
||||
olc::vi2d TransformedView::ScaleToScreen(const olc::vf2d& vWorldSize) const
|
||||
{
|
||||
olc::vf2d vFloat = vWorldSize * m_vWorldScale;
|
||||
return vFloat.floor();
|
||||
}
|
||||
|
||||
bool TransformedView::IsPointVisible(const olc::vf2d & vPos) const
|
||||
{
|
||||
olc::vi2d vScreen = WorldToScreen(vPos);
|
||||
return vScreen.x >= 0 && vScreen.x < m_vViewArea.x&& vScreen.y >= 0 && vScreen.y < m_vViewArea.y;
|
||||
}
|
||||
|
||||
bool TransformedView::IsRectVisible(const olc::vf2d& vPos, const olc::vf2d& vSize) const
|
||||
{
|
||||
olc::vi2d vScreenPos = WorldToScreen(vPos);
|
||||
olc::vi2d vScreenSize = vSize * m_vWorldScale;
|
||||
return (vScreenPos.x < 0 + m_vViewArea.x && vScreenPos.x + vScreenSize.x > 0 && vScreenPos.y < m_vViewArea.y&& vScreenPos.y + vScreenSize.y > 0);
|
||||
}
|
||||
|
||||
bool TransformedView::Draw(float x, float y, olc::Pixel p)
|
||||
{
|
||||
return Draw({ x, y }, p);
|
||||
}
|
||||
|
||||
bool TransformedView::Draw(const olc::vf2d & pos, olc::Pixel p)
|
||||
{
|
||||
return pge->Draw(WorldToScreen(pos), p);
|
||||
}
|
||||
|
||||
void TransformedView::DrawLine(float x1, float y1, float x2, float y2, olc::Pixel p, uint32_t pattern)
|
||||
{
|
||||
DrawLine({ x1, y2 }, { x2, y2 }, p, pattern);
|
||||
}
|
||||
|
||||
void TransformedView::DrawLine(const olc::vf2d & pos1, const olc::vf2d & pos2, olc::Pixel p, uint32_t pattern)
|
||||
{
|
||||
pge->DrawLine(WorldToScreen(pos1), WorldToScreen(pos2), p, pattern);
|
||||
}
|
||||
|
||||
void TransformedView::DrawCircle(float x, float y, float radius, olc::Pixel p, uint8_t mask)
|
||||
{
|
||||
DrawCircle({ x,y }, radius, p, mask);
|
||||
}
|
||||
|
||||
void TransformedView::DrawCircle(const olc::vf2d & pos, float radius, olc::Pixel p, uint8_t mask)
|
||||
{
|
||||
pge->DrawCircle(WorldToScreen(pos), int32_t(radius * m_vWorldScale.x), p, mask);
|
||||
}
|
||||
|
||||
void TransformedView::FillCircle(float x, float y, float radius, olc::Pixel p)
|
||||
{
|
||||
FillCircle({ x,y }, radius, p);
|
||||
}
|
||||
|
||||
void TransformedView::FillCircle(const olc::vf2d & pos, float radius, olc::Pixel p)
|
||||
{
|
||||
pge->FillCircle(WorldToScreen(pos), int32_t(radius * m_vWorldScale.x), p);
|
||||
}
|
||||
|
||||
void TransformedView::DrawRect(float x, float y, float w, float h, olc::Pixel p)
|
||||
{
|
||||
DrawRect({ x, y }, { w, h }, p);
|
||||
}
|
||||
|
||||
void TransformedView::DrawRect(const olc::vf2d & pos, const olc::vf2d & size, olc::Pixel p)
|
||||
{
|
||||
pge->DrawRect(WorldToScreen(pos), ((size * m_vWorldScale) + olc::vf2d(0.5f, 0.5f)).floor(), p);
|
||||
}
|
||||
|
||||
void TransformedView::FillRect(float x, float y, float w, float h, olc::Pixel p)
|
||||
{
|
||||
FillRect({ x, y }, { w, h }, p);
|
||||
}
|
||||
|
||||
void TransformedView::FillRect(const olc::vf2d & pos, const olc::vf2d & size, olc::Pixel p)
|
||||
{
|
||||
pge->FillRect(WorldToScreen(pos), size * m_vWorldScale, p);
|
||||
}
|
||||
|
||||
void TransformedView::DrawTriangle(float x1, float y1, float x2, float y2, float x3, float y3, olc::Pixel p)
|
||||
{
|
||||
DrawTriangle({ x1, y1 }, { x2, y2 }, { x3, y3 }, p);
|
||||
}
|
||||
|
||||
void TransformedView::DrawTriangle(const olc::vf2d & pos1, const olc::vf2d & pos2, const olc::vf2d & pos3, olc::Pixel p)
|
||||
{
|
||||
pge->DrawTriangle(WorldToScreen(pos1), WorldToScreen(pos2), WorldToScreen(pos3), p);
|
||||
}
|
||||
|
||||
void TransformedView::FillTriangle(float x1, float y1, float x2, float y2, float x3, float y3, olc::Pixel p)
|
||||
{
|
||||
FillTriangle({ x1, y1 }, { x2, y2 }, { x3, y3 }, p);
|
||||
}
|
||||
|
||||
void TransformedView::FillTriangle(const olc::vf2d & pos1, const olc::vf2d & pos2, const olc::vf2d & pos3, olc::Pixel p)
|
||||
{
|
||||
pge->FillTriangle(WorldToScreen(pos1), WorldToScreen(pos2), WorldToScreen(pos3), p);
|
||||
}
|
||||
|
||||
void TransformedView::DrawSprite(float x, float y, olc::Sprite* sprite, float scalex, float scaley, uint8_t flip)
|
||||
{
|
||||
DrawSprite({ x, y }, sprite, { scalex, scaley }, flip);
|
||||
}
|
||||
|
||||
void TransformedView::DrawSprite(const olc::vf2d & pos, olc::Sprite * sprite, const olc::vf2d & scale, uint8_t flip)
|
||||
{
|
||||
olc::vf2d vSpriteSize = olc::vf2d(float(sprite->width), float(sprite->height));
|
||||
if (IsRectVisible(pos, vSpriteSize * scale))
|
||||
{
|
||||
olc::vf2d vSpriteScaledSize = vSpriteSize * m_vRecipPixel * m_vWorldScale * scale;
|
||||
olc::vi2d vPixel, vStart = WorldToScreen(pos), vEnd = vSpriteScaledSize + vStart;
|
||||
olc::vf2d vPixelStep = 1.0f / vSpriteScaledSize;
|
||||
for (vPixel.y = vStart.y; vPixel.y < vEnd.y; vPixel.y++)
|
||||
{
|
||||
for (vPixel.x = vStart.x; vPixel.x < vEnd.x; vPixel.x++)
|
||||
{
|
||||
olc::vf2d vSample = olc::vf2d(vPixel - vStart) * vPixelStep;
|
||||
pge->Draw(vPixel, sprite->Sample(vSample.x, vSample.y));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void TransformedView::DrawPartialSprite(float x, float y, Sprite* sprite, int32_t ox, int32_t oy, int32_t w, int32_t h, float scalex, float scaley, uint8_t flip)
|
||||
{
|
||||
DrawPartialSprite({ x,y }, sprite, { ox,oy }, { w, h }, { scalex, scaley }, flip);
|
||||
}
|
||||
|
||||
void TransformedView::DrawPartialSprite(const olc::vf2d& pos, Sprite* sprite, const olc::vi2d& sourcepos, const olc::vi2d& size, const olc::vf2d& scale, uint8_t flip)
|
||||
{
|
||||
olc::vf2d vSpriteSize = size;
|
||||
if (IsRectVisible(pos, size * scale))
|
||||
{
|
||||
olc::vf2d vSpriteScaledSize = olc::vf2d(size) * m_vRecipPixel * m_vWorldScale * scale;
|
||||
olc::vf2d vSpritePixelStep = 1.0f / olc::vf2d(float(sprite->width), float(sprite->height));
|
||||
olc::vi2d vPixel, vStart = WorldToScreen(pos), vEnd = vSpriteScaledSize + vStart;
|
||||
olc::vf2d vScreenPixelStep = 1.0f / vSpriteScaledSize;
|
||||
|
||||
for (vPixel.y = vStart.y; vPixel.y < vEnd.y; vPixel.y++)
|
||||
{
|
||||
for (vPixel.x = vStart.x; vPixel.x < vEnd.x; vPixel.x++)
|
||||
{
|
||||
olc::vf2d vSample = ((olc::vf2d(vPixel - vStart) * vScreenPixelStep) * size * vSpritePixelStep) + olc::vf2d(sourcepos) * vSpritePixelStep;
|
||||
pge->Draw(vPixel, sprite->Sample(vSample.x, vSample.y));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TransformedView::DrawString(float x, float y, const std::string& sText, Pixel col, const olc::vf2d& scale)
|
||||
{
|
||||
DrawString({ x, y }, sText, col, scale);
|
||||
}
|
||||
|
||||
void TransformedView::DrawString(const olc::vf2d& pos, const std::string& sText, const Pixel col, const olc::vf2d& scale)
|
||||
{
|
||||
olc::vf2d vOffset = { 0.0f, 0.0f };
|
||||
Pixel::Mode m = pge->GetPixelMode();
|
||||
|
||||
auto StringPlot = [&col](const int x, const int y, const olc::Pixel& pSource, const olc::Pixel& pDest)
|
||||
{
|
||||
return pSource.r > 1 ? col : pDest;
|
||||
};
|
||||
|
||||
pge->SetPixelMode(StringPlot);
|
||||
|
||||
for (auto c : sText)
|
||||
{
|
||||
if (c == '\n')
|
||||
{
|
||||
vOffset.x = 0.0f; vOffset.y += 8.0f * m_vRecipPixel.y * scale.y;
|
||||
}
|
||||
else
|
||||
{
|
||||
int32_t ox = ((c - 32) % 16) * 8;
|
||||
int32_t oy = ((c - 32) / 16) * 8;
|
||||
DrawPartialSprite(pos + vOffset, pge->GetFontSprite(), { ox, oy }, { 8, 8 }, scale);
|
||||
vOffset.x += 8.0f * m_vRecipPixel.x * scale.x;
|
||||
}
|
||||
}
|
||||
pge->SetPixelMode(m);
|
||||
}
|
||||
|
||||
|
||||
void TransformedView::DrawDecal(const olc::vf2d & pos, olc::Decal * decal, const olc::vf2d & scale, const olc::Pixel & tint)
|
||||
{
|
||||
pge->DrawDecal(WorldToScreen(pos), decal, scale * m_vWorldScale * m_vRecipPixel, tint);
|
||||
}
|
||||
|
||||
void TransformedView::DrawPartialDecal(const olc::vf2d & pos, olc::Decal * decal, const olc::vf2d & source_pos, const olc::vf2d & source_size, const olc::vf2d & scale, const olc::Pixel & tint)
|
||||
{
|
||||
pge->DrawPartialDecal(WorldToScreen(pos), decal, source_pos, source_size, scale * m_vWorldScale * m_vRecipPixel, tint);
|
||||
}
|
||||
|
||||
void TransformedView::DrawPartialDecal(const olc::vf2d & pos, const olc::vf2d & size, olc::Decal * decal, const olc::vf2d & source_pos, const olc::vf2d & source_size, const olc::Pixel & tint)
|
||||
{
|
||||
pge->DrawPartialDecal(WorldToScreen(pos), size * m_vWorldScale * m_vRecipPixel, decal, source_pos, source_size, tint);
|
||||
}
|
||||
|
||||
void TransformedView::DrawExplicitDecal(olc::Decal* decal, const olc::vf2d* pos, const olc::vf2d* uv, const olc::Pixel* col, uint32_t elements)
|
||||
{
|
||||
std::vector<olc::vf2d> vTransformed(elements);
|
||||
for (uint32_t n = 0; n < elements; n++)
|
||||
vTransformed[n] = WorldToScreen(pos[n]);
|
||||
pge->DrawExplicitDecal(decal, vTransformed.data(), uv, col, elements);
|
||||
}
|
||||
|
||||
void TransformedView::DrawWarpedDecal(olc::Decal* decal, const olc::vf2d* pos, const olc::Pixel& tint)
|
||||
{
|
||||
std::array<olc::vf2d, 4> vTransformed =
|
||||
{ {
|
||||
WorldToScreen(pos[0]), WorldToScreen(pos[1]),
|
||||
WorldToScreen(pos[2]), WorldToScreen(pos[3]),
|
||||
} };
|
||||
|
||||
pge->DrawWarpedDecal(decal, vTransformed, tint);
|
||||
}
|
||||
|
||||
void TransformedView::DrawWarpedDecal(olc::Decal* decal, const olc::vf2d(&pos)[4], const olc::Pixel& tint)
|
||||
{
|
||||
DrawWarpedDecal(decal, &pos[0], tint);
|
||||
}
|
||||
|
||||
void TransformedView::DrawWarpedDecal(olc::Decal* decal, const std::array<olc::vf2d, 4>& pos, const olc::Pixel& tint)
|
||||
{
|
||||
DrawWarpedDecal(decal, pos.data(), tint);
|
||||
}
|
||||
|
||||
void TransformedView::DrawPartialWarpedDecal(olc::Decal* decal, const olc::vf2d(&pos)[4], const olc::vf2d& source_pos, const olc::vf2d& source_size, const olc::Pixel& tint)
|
||||
{
|
||||
DrawPartialWarpedDecal(decal, &pos[0], source_pos, source_size, tint);
|
||||
}
|
||||
|
||||
void TransformedView::DrawPartialWarpedDecal(olc::Decal* decal, const olc::vf2d* pos, const olc::vf2d& source_pos, const olc::vf2d& source_size, const olc::Pixel& tint)
|
||||
{
|
||||
std::array<olc::vf2d, 4> vTransformed =
|
||||
{ {
|
||||
WorldToScreen(pos[0]), WorldToScreen(pos[1]),
|
||||
WorldToScreen(pos[2]), WorldToScreen(pos[3]),
|
||||
} };
|
||||
|
||||
pge->DrawPartialWarpedDecal(decal, vTransformed, source_pos, source_size, tint);
|
||||
}
|
||||
|
||||
void TransformedView::DrawPartialWarpedDecal(olc::Decal* decal, const std::array<olc::vf2d, 4>& pos, const olc::vf2d& source_pos, const olc::vf2d& source_size, const olc::Pixel& tint)
|
||||
{
|
||||
DrawPartialWarpedDecal(decal, pos.data(), source_pos, source_size, tint);
|
||||
}
|
||||
|
||||
void TransformedView::DrawRotatedDecal(const olc::vf2d & pos, olc::Decal * decal, const float fAngle, const olc::vf2d & center, const olc::vf2d & scale, const olc::Pixel & tint)
|
||||
{
|
||||
pge->DrawRotatedDecal(WorldToScreen(pos), decal, fAngle, center, scale * m_vWorldScale * m_vRecipPixel, tint);
|
||||
}
|
||||
|
||||
void TransformedView::DrawPartialRotatedDecal(const olc::vf2d & pos, olc::Decal * decal, const float fAngle, const olc::vf2d & center, const olc::vf2d & source_pos, const olc::vf2d & source_size, const olc::vf2d & scale, const olc::Pixel & tint)
|
||||
{
|
||||
pge->DrawPartialRotatedDecal(WorldToScreen(pos), decal, fAngle, center, source_pos, source_size, scale * m_vWorldScale * m_vRecipPixel, tint);
|
||||
}
|
||||
|
||||
void TransformedView::DrawStringDecal(const olc::vf2d & pos, const std::string & sText, const olc::Pixel col, const olc::vf2d & scale)
|
||||
{
|
||||
pge->DrawStringDecal(WorldToScreen(pos), sText, col, scale * m_vWorldScale * m_vRecipPixel);
|
||||
}
|
||||
|
||||
void TransformedView::DrawStringPropDecal(const olc::vf2d & pos, const std::string & sText, const olc::Pixel col, const olc::vf2d & scale )
|
||||
{
|
||||
pge->DrawStringPropDecal(WorldToScreen(pos), sText, col, scale * m_vWorldScale * m_vRecipPixel);
|
||||
}
|
||||
|
||||
void TransformedView::FillRectDecal(const olc::vf2d & pos, const olc::vf2d & size, const olc::Pixel col)
|
||||
{
|
||||
pge->FillRectDecal(WorldToScreen(pos), (size * m_vWorldScale).ceil(), col);
|
||||
}
|
||||
|
||||
void TransformedView::GradientFillRectDecal(const olc::vf2d & pos, const olc::vf2d & size, const olc::Pixel colTL, const olc::Pixel colBL, const olc::Pixel colBR, const olc::Pixel colTR)
|
||||
{
|
||||
pge->GradientFillRectDecal(WorldToScreen(pos), size * m_vWorldScale, colTL, colBL, colBR, colTR);
|
||||
}
|
||||
|
||||
void TransformedView::DrawPolygonDecal(olc::Decal* decal, const std::vector<olc::vf2d>& pos, const std::vector<olc::vf2d>& uv, const olc::Pixel tint)
|
||||
{
|
||||
std::vector<olc::vf2d> vTransformed(pos.size());
|
||||
for (uint32_t n = 0; n < pos.size(); n++)
|
||||
vTransformed[n] = WorldToScreen(pos[n]);
|
||||
pge->DrawPolygonDecal(decal, vTransformed, uv, tint);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
TileTransformedView::TileTransformedView(const olc::vi2d& vViewArea, const olc::vi2d& vTileSize)
|
||||
{
|
||||
Initialise(vViewArea, vTileSize);
|
||||
}
|
||||
|
||||
void TileTransformedView::SetRangeX(const bool bRanged, const int32_t nMin, const int32_t nMax)
|
||||
{
|
||||
m_bRangedX = bRanged;
|
||||
m_nMinRangeX = nMin;
|
||||
m_nMaxRangeX = nMax;
|
||||
}
|
||||
|
||||
void TileTransformedView::SetRangeY(const bool bRanged, const int32_t nMin, const int32_t nMax)
|
||||
{
|
||||
m_bRangedY = bRanged;
|
||||
m_nMinRangeY = nMin;
|
||||
m_nMaxRangeY = nMax;
|
||||
}
|
||||
|
||||
olc::vi2d TileTransformedView::GetTopLeftTile() const
|
||||
{
|
||||
return ScreenToWorld({ 0,0 }).floor();
|
||||
}
|
||||
|
||||
olc::vi2d TileTransformedView::GetBottomRightTile() const
|
||||
{
|
||||
return ScreenToWorld(m_vViewArea).ceil();
|
||||
}
|
||||
|
||||
olc::vi2d TileTransformedView::GetVisibleTiles() const
|
||||
{
|
||||
return GetBottomRightTile() - GetTopLeftTile();
|
||||
}
|
||||
|
||||
olc::vi2d TileTransformedView::GetTileUnderScreenPos(const olc::vi2d& vPos) const
|
||||
{
|
||||
return ScreenToWorld(vPos).floor();
|
||||
}
|
||||
|
||||
const olc::vi2d TileTransformedView::GetTileOffset() const
|
||||
{
|
||||
return { int32_t((m_vWorldOffset.x - std::floor(m_vWorldOffset.x)) * m_vWorldScale.x),
|
||||
int32_t((m_vWorldOffset.y - std::floor(m_vWorldOffset.y)) * m_vWorldScale.y) };
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
@ -1,504 +0,0 @@
|
||||
/*
|
||||
8-Bits Of Image Processing You Should Know
|
||||
"Colin, at least you'll always get 700s now..." - javidx9
|
||||
|
||||
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.
|
||||
|
||||
Instructions:
|
||||
~~~~~~~~~~~~~
|
||||
Choose algorithm 1-8, instructions on screen
|
||||
|
||||
|
||||
Relevant Video: https://youtu.be/mRM5Js3VLCk
|
||||
|
||||
Links
|
||||
~~~~~
|
||||
YouTube: https://www.youtube.com/javidx9
|
||||
https://www.youtube.com/javidx9extra
|
||||
Discord: https://discord.gg/WhwHUMV
|
||||
Twitter: https://www.twitter.com/javidx9
|
||||
Twitch: https://www.twitch.tv/javidx9
|
||||
GitHub: https://www.github.com/onelonecoder
|
||||
Patreon: https://www.patreon.com/javidx9
|
||||
Homepage: https://www.onelonecoder.com
|
||||
|
||||
Author
|
||||
~~~~~~
|
||||
David Barr, aka javidx9, ©OneLoneCoder 2019
|
||||
*/
|
||||
|
||||
|
||||
#define OLC_PGE_APPLICATION
|
||||
#include "olcPixelGameEngine.h"
|
||||
|
||||
#include "escapi.h"
|
||||
|
||||
int nFrameWidth = 320;
|
||||
int nFrameHeight = 240;
|
||||
|
||||
struct frame
|
||||
{
|
||||
float *pixels = nullptr;
|
||||
|
||||
frame()
|
||||
{
|
||||
pixels = new float[nFrameWidth * nFrameHeight];
|
||||
}
|
||||
|
||||
~frame()
|
||||
{
|
||||
delete[] pixels;
|
||||
}
|
||||
|
||||
|
||||
float get(int x, int y)
|
||||
{
|
||||
if (x >= 0 && x < nFrameWidth && y >= 0 && y < nFrameHeight)
|
||||
{
|
||||
return pixels[y*nFrameWidth + x];
|
||||
}
|
||||
else
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
void set(int x, int y, float p)
|
||||
{
|
||||
if (x >= 0 && x < nFrameWidth && y >= 0 && y < nFrameHeight)
|
||||
{
|
||||
pixels[y*nFrameWidth + x] = p;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void operator=(const frame &f)
|
||||
{
|
||||
memcpy(this->pixels, f.pixels, nFrameWidth * nFrameHeight * sizeof(float));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
class WIP_ImageProcessing : public olc::PixelGameEngine
|
||||
{
|
||||
public:
|
||||
WIP_ImageProcessing()
|
||||
{
|
||||
sAppName = "WIP_ImageProcessing";
|
||||
}
|
||||
|
||||
union RGBint
|
||||
{
|
||||
int rgb;
|
||||
unsigned char c[4];
|
||||
};
|
||||
|
||||
int nCameras = 0;
|
||||
SimpleCapParams capture;
|
||||
|
||||
public:
|
||||
bool OnUserCreate() override
|
||||
{
|
||||
// Initialise webcam to screen dimensions
|
||||
nCameras = setupESCAPI();
|
||||
if (nCameras == 0) return false;
|
||||
capture.mWidth = nFrameWidth;
|
||||
capture.mHeight = nFrameHeight;
|
||||
capture.mTargetBuf = new int[nFrameWidth * nFrameHeight];
|
||||
if (initCapture(0, &capture) == 0) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void DrawFrame(frame &f, int x, int y)
|
||||
{
|
||||
for (int i = 0; i < nFrameWidth; i++)
|
||||
for (int j = 0; j < nFrameHeight; j++)
|
||||
{
|
||||
int c = (int)std::min(std::max(0.0f, f.pixels[j*nFrameWidth + i] * 255.0f), 255.0f);
|
||||
Draw(x + i, y + j, olc::Pixel(c, c, c));
|
||||
}
|
||||
}
|
||||
|
||||
enum ALGORITHM
|
||||
{
|
||||
THRESHOLD, MOTION, LOWPASS, CONVOLUTION,
|
||||
SOBEL, MORPHO, MEDIAN, ADAPTIVE,
|
||||
};
|
||||
|
||||
enum MORPHOP
|
||||
{
|
||||
DILATION,
|
||||
EROSION,
|
||||
EDGE
|
||||
};
|
||||
|
||||
frame input, output, prev_input, activity, threshold;
|
||||
|
||||
// Algorithm Currently Running
|
||||
ALGORITHM algo = THRESHOLD;
|
||||
MORPHOP morph = DILATION;
|
||||
int nMorphCount = 1;
|
||||
|
||||
float fThresholdValue = 0.5f;
|
||||
float fLowPassRC = 0.1f;
|
||||
float fAdaptiveBias = 1.1f;
|
||||
|
||||
float *pConvoKernel = kernel_blur;
|
||||
|
||||
float kernel_blur[9] =
|
||||
{
|
||||
0.0f, 0.125, 0.0f,
|
||||
0.125f, 0.5f, 0.125f,
|
||||
0.0f, 0.125f, 0.0f,
|
||||
};
|
||||
|
||||
float kernel_sharpen[9] =
|
||||
{
|
||||
0.0f, -1.0f, 0.0f,
|
||||
-1.0f, 5.0f, -1.0f,
|
||||
0.0f, -1.0f, 0.0f,
|
||||
};
|
||||
|
||||
float kernel_sobel_v[9] =
|
||||
{
|
||||
-1.0f, 0.0f, +1.0f,
|
||||
-2.0f, 0.0f, +2.0f,
|
||||
-1.0f, 0.0f, +1.0f,
|
||||
};
|
||||
|
||||
float kernel_sobel_h[9] =
|
||||
{
|
||||
-1.0f, -2.0f, -1.0f,
|
||||
0.0f, 0.0f, 0.0f,
|
||||
+1.0f, +2.0f, +1.0f,
|
||||
};
|
||||
|
||||
bool OnUserUpdate(float fElapsedTime) override
|
||||
{
|
||||
// CAPTURING WEBCAM IMAGE
|
||||
prev_input = input;
|
||||
doCapture(0); while (isCaptureDone(0) == 0) {}
|
||||
for (int y = 0; y < capture.mHeight; y++)
|
||||
for (int x = 0; x < capture.mWidth; x++)
|
||||
{
|
||||
RGBint col;
|
||||
int id = y * capture.mWidth + x;
|
||||
col.rgb = capture.mTargetBuf[id];
|
||||
input.pixels[y*nFrameWidth + x] = (float)col.c[1] / 255.0f;
|
||||
}
|
||||
|
||||
if (GetKey(olc::Key::K1).bReleased) algo = THRESHOLD;
|
||||
if (GetKey(olc::Key::K2).bReleased) algo = MOTION;
|
||||
if (GetKey(olc::Key::K3).bReleased) algo = LOWPASS;
|
||||
if (GetKey(olc::Key::K4).bReleased) algo = CONVOLUTION;
|
||||
if (GetKey(olc::Key::K5).bReleased) algo = SOBEL;
|
||||
if (GetKey(olc::Key::K6).bReleased) algo = MORPHO;
|
||||
if (GetKey(olc::Key::K7).bReleased) algo = MEDIAN;
|
||||
if (GetKey(olc::Key::K8).bReleased) algo = ADAPTIVE;
|
||||
|
||||
|
||||
switch (algo)
|
||||
{
|
||||
case THRESHOLD:
|
||||
|
||||
// Respond to user input
|
||||
if (GetKey(olc::Key::Z).bHeld) fThresholdValue -= 0.1f * fElapsedTime;
|
||||
if (GetKey(olc::Key::X).bHeld) fThresholdValue += 0.1f * fElapsedTime;
|
||||
if (fThresholdValue > 1.0f) fThresholdValue = 1.0f;
|
||||
if (fThresholdValue < 0.0f) fThresholdValue = 0.0f;
|
||||
|
||||
// Perform threshold per pixel
|
||||
for (int i = 0; i < nFrameWidth; i++)
|
||||
for (int j = 0; j < nFrameHeight; j++)
|
||||
output.set(i, j, input.get(i, j) >= fThresholdValue ? 1.0f : 0.0f);
|
||||
break;
|
||||
|
||||
case MOTION:
|
||||
|
||||
// Returns the absolute difference between successive frames per pixel
|
||||
for (int i = 0; i < nFrameWidth; i++)
|
||||
for (int j = 0; j < nFrameHeight; j++)
|
||||
output.set(i, j, fabs(input.get(i, j) - prev_input.get(i, j)));
|
||||
break;
|
||||
|
||||
|
||||
case LOWPASS:
|
||||
|
||||
// Respond to user input
|
||||
if (GetKey(olc::Key::Z).bHeld) fLowPassRC -= 0.1f * fElapsedTime;
|
||||
if (GetKey(olc::Key::X).bHeld) fLowPassRC += 0.1f * fElapsedTime;
|
||||
if (fLowPassRC > 1.0f) fLowPassRC = 1.0f;
|
||||
if (fLowPassRC < 0.0f) fLowPassRC = 0.0f;
|
||||
|
||||
// Pass each pixel through a temporal RC filter
|
||||
for (int i = 0; i < nFrameWidth; i++)
|
||||
for (int j = 0; j < nFrameHeight; j++)
|
||||
{
|
||||
float dPixel = input.get(i, j) - output.get(i, j);
|
||||
dPixel *= fLowPassRC;
|
||||
output.set(i, j, dPixel + output.get(i, j));
|
||||
}
|
||||
break;
|
||||
|
||||
case CONVOLUTION:
|
||||
// Respond to user input
|
||||
if (GetKey(olc::Key::Z).bHeld) pConvoKernel = kernel_blur;
|
||||
if (GetKey(olc::Key::X).bHeld) pConvoKernel = kernel_sharpen;
|
||||
|
||||
for (int i = 0; i < nFrameWidth; i++)
|
||||
for (int j = 0; j < nFrameHeight; j++)
|
||||
{
|
||||
float fSum = 0.0f;
|
||||
for (int n = -1; n < +2; n++)
|
||||
for (int m = -1; m < +2; m++)
|
||||
fSum += input.get(i + n, j + m) * pConvoKernel[(m + 1) * 3 + (n + 1)];
|
||||
|
||||
output.set(i, j, fSum);
|
||||
}
|
||||
break;
|
||||
|
||||
case SOBEL:
|
||||
for (int i = 0; i < nFrameWidth; i++)
|
||||
for (int j = 0; j < nFrameHeight; j++)
|
||||
{
|
||||
float fKernelSumH = 0.0f;
|
||||
float fKernelSumV = 0.0f;
|
||||
|
||||
for (int n = -1; n < +2; n++)
|
||||
for (int m = -1; m < +2; m++)
|
||||
{
|
||||
fKernelSumH += input.get(i + n, j + m) * kernel_sobel_h[(m + 1) * 3 + (n + 1)];
|
||||
fKernelSumV += input.get(i + n, j + m) * kernel_sobel_v[(m + 1) * 3 + (n + 1)];
|
||||
}
|
||||
|
||||
output.set(i, j, fabs((fKernelSumH + fKernelSumV) / 2.0f));
|
||||
}
|
||||
break;
|
||||
|
||||
case MORPHO:
|
||||
|
||||
// Respond to user input
|
||||
if (GetKey(olc::Key::Z).bHeld) morph = DILATION;
|
||||
if (GetKey(olc::Key::X).bHeld) morph = EROSION;
|
||||
if (GetKey(olc::Key::C).bHeld) morph = EDGE;
|
||||
|
||||
if (GetKey(olc::Key::A).bReleased) nMorphCount--;
|
||||
if (GetKey(olc::Key::S).bReleased) nMorphCount++;
|
||||
if (nMorphCount > 10.0f) nMorphCount = 10.0f;
|
||||
if (nMorphCount < 1.0f) nMorphCount = 1.0f;
|
||||
|
||||
// Threshold First to binarise image
|
||||
for (int i = 0; i < nFrameWidth; i++)
|
||||
for (int j = 0; j < nFrameHeight; j++)
|
||||
{
|
||||
activity.set(i, j, input.get(i, j) > fThresholdValue ? 1.0f : 0.0f);
|
||||
}
|
||||
|
||||
threshold = activity;
|
||||
|
||||
switch (morph)
|
||||
{
|
||||
case DILATION:
|
||||
for (int n = 0; n < nMorphCount; n++)
|
||||
{
|
||||
output = activity;
|
||||
|
||||
for (int i = 0; i < nFrameWidth; i++)
|
||||
for (int j = 0; j < nFrameHeight; j++)
|
||||
{
|
||||
if (activity.get(i, j) == 1.0f)
|
||||
{
|
||||
output.set(i, j, 1.0f);
|
||||
output.set(i - 1, j, 1.0f);
|
||||
output.set(i + 1, j, 1.0f);
|
||||
output.set(i, j - 1, 1.0f);
|
||||
output.set(i, j + 1, 1.0f);
|
||||
output.set(i - 1, j - 1, 1.0f);
|
||||
output.set(i + 1, j + 1, 1.0f);
|
||||
output.set(i + 1, j - 1, 1.0f);
|
||||
output.set(i - 1, j + 1, 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
activity = output;
|
||||
}
|
||||
break;
|
||||
|
||||
case EROSION:
|
||||
for (int n = 0; n < nMorphCount; n++)
|
||||
{
|
||||
output = activity;
|
||||
for (int i = 0; i < nFrameWidth; i++)
|
||||
for (int j = 0; j < nFrameHeight; j++)
|
||||
{
|
||||
|
||||
float sum = activity.get(i - 1, j) + activity.get(i + 1, j) + activity.get(i, j - 1) + activity.get(i, j + 1) +
|
||||
activity.get(i - 1, j - 1) + activity.get(i + 1, j + 1) + activity.get(i + 1, j - 1) + activity.get(i - 1, j + 1);
|
||||
|
||||
if (activity.get(i, j) == 1.0f && sum < 8.0f)
|
||||
{
|
||||
output.set(i, j, 0.0f);
|
||||
}
|
||||
}
|
||||
activity = output;
|
||||
}
|
||||
break;
|
||||
|
||||
case EDGE:
|
||||
output = activity;
|
||||
for (int i = 0; i < nFrameWidth; i++)
|
||||
for (int j = 0; j < nFrameHeight; j++)
|
||||
{
|
||||
|
||||
float sum = activity.get(i - 1, j) + activity.get(i + 1, j) + activity.get(i, j - 1) + activity.get(i, j + 1) +
|
||||
activity.get(i - 1, j - 1) + activity.get(i + 1, j + 1) + activity.get(i + 1, j - 1) + activity.get(i - 1, j + 1);
|
||||
|
||||
if (activity.get(i, j) == 1.0f && sum == 8.0f)
|
||||
{
|
||||
output.set(i, j, 0.0f);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
case MEDIAN:
|
||||
for (int i = 0; i < nFrameWidth; i++)
|
||||
for (int j = 0; j < nFrameHeight; j++)
|
||||
{
|
||||
std::vector<float> v;
|
||||
|
||||
for (int n = -2; n < +3; n++)
|
||||
for (int m = -2; m < +3; m++)
|
||||
v.push_back(input.get(i + n, j + m));
|
||||
|
||||
std::sort(v.begin(), v.end(), std::greater<float>());
|
||||
output.set(i, j, v[12]);
|
||||
}
|
||||
break;
|
||||
|
||||
case ADAPTIVE:
|
||||
// Respond to user input
|
||||
if (GetKey(olc::Key::Z).bHeld) fAdaptiveBias -= 0.1f * fElapsedTime;
|
||||
if (GetKey(olc::Key::X).bHeld) fAdaptiveBias += 0.1f * fElapsedTime;
|
||||
if (fAdaptiveBias > 1.5f) fAdaptiveBias = 1.5f;
|
||||
if (fAdaptiveBias < 0.5f) fAdaptiveBias = 0.5f;
|
||||
|
||||
|
||||
for (int i = 0; i < nFrameWidth; i++)
|
||||
for (int j = 0; j < nFrameHeight; j++)
|
||||
{
|
||||
float fRegionSum = 0.0f;
|
||||
|
||||
for (int n = -2; n < +3; n++)
|
||||
for (int m = -2; m < +3; m++)
|
||||
fRegionSum += input.get(i + n, j + m);
|
||||
|
||||
fRegionSum /= 25.0f;
|
||||
output.set(i, j, input.get(i, j) > (fRegionSum * fAdaptiveBias) ? 1.0f : 0.0f);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// DRAW STUFF ONLY HERE
|
||||
Clear(olc::DARK_BLUE);
|
||||
DrawFrame(algo == MORPHO ? threshold : input, 10, 10);
|
||||
DrawFrame(output, 340, 10);
|
||||
|
||||
DrawString(150, 255, "INPUT");
|
||||
DrawString(480, 255, "OUTPUT");
|
||||
|
||||
DrawString(10, 275, "1) Threshold");
|
||||
DrawString(10, 285, "2) Absolute Motion");
|
||||
DrawString(10, 295, "3) Low-Pass Temporal Filtering");
|
||||
DrawString(10, 305, "4) Convolution (Blurring/Sharpening)");
|
||||
DrawString(10, 315, "5) Sobel Edge Detection");
|
||||
DrawString(10, 325, "6) Binary Morphological Operations (Erosion/Dilation)");
|
||||
DrawString(10, 335, "7) Median Filter");
|
||||
DrawString(10, 345, "8) Adaptive Threshold");
|
||||
|
||||
|
||||
switch (algo)
|
||||
{
|
||||
case THRESHOLD:
|
||||
DrawString(10, 375, "Change threshold value with Z and X keys");
|
||||
DrawString(10, 385, "Current value = " + std::to_string(fThresholdValue));
|
||||
break;
|
||||
|
||||
case LOWPASS:
|
||||
DrawString(10, 375, "Change RC constant value with Z and X keys");
|
||||
DrawString(10, 385, "Current value = " + std::to_string(fLowPassRC));
|
||||
break;
|
||||
|
||||
case CONVOLUTION:
|
||||
DrawString(10, 375, "Change convolution kernel with Z and X keys");
|
||||
DrawString(10, 385, "Current kernel = " + std::string((pConvoKernel == kernel_blur) ? "Blur" : "Sharpen"));
|
||||
break;
|
||||
|
||||
case MORPHO:
|
||||
DrawString(10, 375, "Change operation with Z and X and C keys");
|
||||
if (morph == DILATION) DrawString(10, 385, "Current operation = DILATION");
|
||||
if (morph == EROSION) DrawString(10, 385, "Current operation = EROSION");
|
||||
if (morph == EDGE) DrawString(10, 385, "Current operation = EDGE");
|
||||
DrawString(10, 395, "Change Iterations with A and S keys");
|
||||
DrawString(10, 405, "Current iteration count = " + std::to_string(nMorphCount));
|
||||
|
||||
|
||||
break;
|
||||
|
||||
case ADAPTIVE:
|
||||
DrawString(10, 375, "Change adaptive threshold bias with Z and X keys");
|
||||
DrawString(10, 385, "Current value = " + std::to_string(fAdaptiveBias));
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (GetKey(olc::Key::ESCAPE).bPressed) return false;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
WIP_ImageProcessing demo;
|
||||
if (demo.Construct(670, 460, 2, 2))
|
||||
demo.Start();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@ -1,500 +0,0 @@
|
||||
/*
|
||||
OneLoneCoder.com - Programming Balls! #2 Circle Vs Edge Collisions
|
||||
"...totally overkill for pong..." - @Javidx9
|
||||
|
||||
|
||||
Background
|
||||
~~~~~~~~~~
|
||||
Collision detection engines can get quite complicated. This program shows the interactions
|
||||
between circular objects of different sizes and masses. Use Left mouse button to select
|
||||
and drag a ball to examin static collisions, and use Right mouse button to apply velocity
|
||||
to the balls as if using a pool/snooker/billiards cue.
|
||||
|
||||
License (OLC-3)
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Copyright 2018 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
|
||||
|
||||
Relevant Videos
|
||||
~~~~~~~~~~~~~~~
|
||||
Part #1 https://youtu.be/LPzyNOHY3A4
|
||||
Part #2 https://youtu.be/ebq7L2Wtbl4
|
||||
|
||||
Author
|
||||
~~~~~~
|
||||
David Barr, aka javidx9, ©OneLoneCoder 2018
|
||||
*/
|
||||
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
using namespace std;
|
||||
|
||||
#define OLC_PGE_APPLICATION
|
||||
#include "olcPixelGameEngine.h"
|
||||
|
||||
struct sBall
|
||||
{
|
||||
float px, py;
|
||||
float vx, vy;
|
||||
float ax, ay;
|
||||
float ox, oy;
|
||||
float radius;
|
||||
float mass;
|
||||
float friction;
|
||||
int score;
|
||||
int id;
|
||||
float fSimTimeRemaining;
|
||||
olc::Pixel col;
|
||||
};
|
||||
|
||||
struct sLineSegment
|
||||
{
|
||||
float sx, sy;
|
||||
float ex, ey;
|
||||
float radius;
|
||||
};
|
||||
|
||||
|
||||
|
||||
class CirclePhysics : public olc::PixelGameEngine
|
||||
{
|
||||
public:
|
||||
CirclePhysics()
|
||||
{
|
||||
sAppName = "Circles V Edges";
|
||||
}
|
||||
|
||||
private:
|
||||
vector<sBall> vecBalls;
|
||||
vector<sLineSegment> vecLines;
|
||||
vector<pair<float, float>> modelCircle;
|
||||
sBall* pSelectedBall = nullptr;
|
||||
olc::Sprite *spriteBalls = nullptr;
|
||||
sLineSegment* pSelectedLine = nullptr;
|
||||
bool bSelectedLineStart = false;
|
||||
|
||||
void AddBall(float x, float y, float r = 5.0f, int s = 0)
|
||||
{
|
||||
sBall b;
|
||||
b.px = x; b.py = y;
|
||||
b.vx = 0; b.vy = 0;
|
||||
b.ax = 0; b.ay = 0;
|
||||
b.ox = 0; b.oy = 0;
|
||||
b.radius = r;
|
||||
b.mass = r * 10.0f;
|
||||
b.friction = 0.0f;
|
||||
b.score = s;
|
||||
b.fSimTimeRemaining = 0.0f;
|
||||
b.id = vecBalls.size();
|
||||
b.col = olc::Pixel(rand() % 200 + 55, rand() % 200 + 55, rand() % 200 + 55);
|
||||
vecBalls.emplace_back(b);
|
||||
}
|
||||
|
||||
olc::Sprite spr;
|
||||
|
||||
public:
|
||||
bool OnUserCreate()
|
||||
{
|
||||
|
||||
float fBallRadius = 4.0f;
|
||||
for (int i = 0; i <100; i++)
|
||||
AddBall(((float)rand()/(float)RAND_MAX) * ScreenWidth(), ((float)rand() / (float)RAND_MAX) * ScreenHeight(), fBallRadius);
|
||||
|
||||
AddBall(28.0f, 33.0, fBallRadius * 3);
|
||||
AddBall(28.0f, 35.0, fBallRadius * 2);
|
||||
|
||||
float fLineRadius = 4.0f;
|
||||
vecLines.push_back({ 12.0f, 4.0f, 64.0f, 4.0f, fLineRadius });
|
||||
vecLines.push_back({ 76.0f, 4.0f, 132.0f, 4.0f, fLineRadius });
|
||||
vecLines.push_back({ 12.0f, 68.0f, 64.0f, 68.0f, fLineRadius });
|
||||
vecLines.push_back({ 76.0f, 68.0f, 132.0f, 68.0f, fLineRadius });
|
||||
vecLines.push_back({ 4.0f, 12.0f, 4.0f, 60.0f, fLineRadius });
|
||||
vecLines.push_back({ 140.0f, 12.0f, 140.0f, 60.0f, fLineRadius });
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OnUserUpdate(float fElapsedTime)
|
||||
{
|
||||
|
||||
auto DoCirclesOverlap = [](float x1, float y1, float r1, float x2, float y2, float r2)
|
||||
{
|
||||
return fabs((x1 - x2)*(x1 - x2) + (y1 - y2)*(y1 - y2)) <= ((r1 + r2) * (r1 + r2));
|
||||
};
|
||||
|
||||
auto IsPointInCircle = [](float x1, float y1, float r1, float px, float py)
|
||||
{
|
||||
return fabs((x1 - px)*(x1 - px) + (y1 - py)*(y1 - py)) < (r1 * r1);
|
||||
};
|
||||
|
||||
if (GetMouse(0).bPressed)
|
||||
{
|
||||
// Check for selected ball
|
||||
pSelectedBall = nullptr;
|
||||
for (auto &ball : vecBalls)
|
||||
{
|
||||
if (IsPointInCircle(ball.px, ball.py, ball.radius, GetMouseX(), GetMouseY()))
|
||||
{
|
||||
pSelectedBall = &ball;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for selected line segment end
|
||||
pSelectedLine = nullptr;
|
||||
for (auto &line : vecLines)
|
||||
{
|
||||
if (IsPointInCircle(line.sx, line.sy, line.radius, GetMouseX(), GetMouseY()))
|
||||
{
|
||||
pSelectedLine = &line;
|
||||
bSelectedLineStart = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (IsPointInCircle(line.ex, line.ey, line.radius, GetMouseX(), GetMouseY()))
|
||||
{
|
||||
pSelectedLine = &line;
|
||||
bSelectedLineStart = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (GetMouse(0).bHeld)
|
||||
{
|
||||
if (pSelectedLine != nullptr)
|
||||
{
|
||||
if (bSelectedLineStart)
|
||||
{
|
||||
pSelectedLine->sx = GetMouseX();
|
||||
pSelectedLine->sy = GetMouseY();
|
||||
}
|
||||
else
|
||||
{
|
||||
pSelectedLine->ex = GetMouseX();
|
||||
pSelectedLine->ey = GetMouseY();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (GetMouse(0).bReleased)
|
||||
{
|
||||
if (pSelectedBall != nullptr)
|
||||
{
|
||||
// Apply velocity
|
||||
pSelectedBall->vx = 5.0f * ((pSelectedBall->px) - GetMouseX());
|
||||
pSelectedBall->vy = 5.0f * ((pSelectedBall->py) - GetMouseY());
|
||||
}
|
||||
|
||||
pSelectedBall = nullptr;
|
||||
pSelectedLine = nullptr;
|
||||
}
|
||||
|
||||
if (GetMouse(1).bHeld)
|
||||
{
|
||||
for (auto &ball : vecBalls)
|
||||
{
|
||||
ball.vx += (GetMouseX() - ball.px) * 0.01f;
|
||||
ball.vy += (GetMouseY() - ball.py) * 0.01f;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
vector<pair<sBall*, sBall*>> vecCollidingPairs;
|
||||
vector<sBall*> vecFakeBalls;
|
||||
|
||||
// Threshold indicating stability of object
|
||||
float fStable = 0.005f;
|
||||
|
||||
// Multiple simulation updates with small time steps permit more accurate physics
|
||||
// and realistic results at the expense of CPU time of course
|
||||
int nSimulationUpdates = 4;
|
||||
|
||||
// Multiple collision trees require more steps to resolve. Normally we would
|
||||
// continue simulation until the object has no simulation time left for this
|
||||
// epoch, however this is risky as the system may never find stability, so we
|
||||
// can clamp it here
|
||||
int nMaxSimulationSteps = 15;
|
||||
|
||||
// Break up the frame elapsed time into smaller deltas for each simulation update
|
||||
float fSimElapsedTime = fElapsedTime / (float)nSimulationUpdates;
|
||||
|
||||
// Main simulation loop
|
||||
for (int i = 0; i < nSimulationUpdates; i++)
|
||||
{
|
||||
// Set all balls time to maximum for this epoch
|
||||
for (auto &ball : vecBalls)
|
||||
ball.fSimTimeRemaining = fSimElapsedTime;
|
||||
|
||||
// Erode simulation time on a per objec tbasis, depending upon what happens
|
||||
// to it during its journey through this epoch
|
||||
for (int j = 0; j < nMaxSimulationSteps; j++)
|
||||
{
|
||||
// Update Ball Positions
|
||||
for (auto &ball : vecBalls)
|
||||
{
|
||||
if (ball.fSimTimeRemaining > 0.0f)
|
||||
{
|
||||
ball.ox = ball.px; // Store original position this epoch
|
||||
ball.oy = ball.py;
|
||||
|
||||
ball.ax = -ball.vx * 0.8f; // Apply drag and gravity
|
||||
ball.ay = -ball.vy * 0.8f + 100.0f;
|
||||
|
||||
ball.vx += ball.ax * ball.fSimTimeRemaining; // Update Velocity
|
||||
ball.vy += ball.ay * ball.fSimTimeRemaining;
|
||||
|
||||
ball.px += ball.vx * ball.fSimTimeRemaining; // Update position
|
||||
ball.py += ball.vy * ball.fSimTimeRemaining;
|
||||
|
||||
// Crudely wrap balls to screen - note this cause issues when collisions occur on screen boundaries
|
||||
if (ball.px < 0) ball.px += (float)ScreenWidth();
|
||||
if (ball.px >= ScreenWidth()) ball.px -= (float)ScreenWidth();
|
||||
if (ball.py < 0) ball.py += (float)ScreenHeight();
|
||||
if (ball.py >= ScreenHeight()) ball.py -= (float)ScreenHeight();
|
||||
|
||||
// Stop ball when velocity is neglible
|
||||
if (fabs(ball.vx*ball.vx + ball.vy*ball.vy) < fStable)
|
||||
{
|
||||
ball.vx = 0;
|
||||
ball.vy = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Work out static collisions with walls and displace balls so no overlaps
|
||||
for (auto &ball : vecBalls)
|
||||
{
|
||||
float fDeltaTime = ball.fSimTimeRemaining;
|
||||
|
||||
// Against Edges
|
||||
for (auto &edge : vecLines)
|
||||
{
|
||||
// Check that line formed by velocity vector, intersects with line segment
|
||||
float fLineX1 = edge.ex - edge.sx;
|
||||
float fLineY1 = edge.ey - edge.sy;
|
||||
|
||||
float fLineX2 = ball.px - edge.sx;
|
||||
float fLineY2 = ball.py - edge.sy;
|
||||
|
||||
float fEdgeLength = fLineX1 * fLineX1 + fLineY1 * fLineY1;
|
||||
|
||||
// This is nifty - It uses the DP of the line segment vs the line to the object, to work out
|
||||
// how much of the segment is in the "shadow" of the object vector. The min and max clamp
|
||||
// this to lie between 0 and the line segment length, which is then normalised. We can
|
||||
// use this to calculate the closest point on the line segment
|
||||
float t = std::max(0.0f, std::min(fEdgeLength, (fLineX1 * fLineX2 + fLineY1 * fLineY2))) / fEdgeLength;
|
||||
|
||||
// Which we do here
|
||||
float fClosestPointX = edge.sx + t * fLineX1;
|
||||
float fClosestPointY = edge.sy + t * fLineY1;
|
||||
|
||||
// And once we know the closest point, we can check if the ball has collided with the segment in the
|
||||
// same way we check if two balls have collided
|
||||
float fDistance = sqrtf((ball.px - fClosestPointX)*(ball.px - fClosestPointX) + (ball.py - fClosestPointY)*(ball.py - fClosestPointY));
|
||||
|
||||
if (fDistance <= (ball.radius + edge.radius))
|
||||
{
|
||||
// Collision has occurred - treat collision point as a ball that cannot move. To make this
|
||||
// compatible with the dynamic resolution code below, we add a fake ball with an infinite mass
|
||||
// so it behaves like a solid object when the momentum calculations are performed
|
||||
sBall *fakeball = new sBall();
|
||||
fakeball->radius = edge.radius;
|
||||
fakeball->mass = ball.mass * 0.8f;
|
||||
fakeball->px = fClosestPointX;
|
||||
fakeball->py = fClosestPointY;
|
||||
fakeball->vx = -ball.vx; // We will use these later to allow the lines to impart energy into ball
|
||||
fakeball->vy = -ball.vy; // if the lines are moving, i.e. like pinball flippers
|
||||
|
||||
// Store Fake Ball
|
||||
vecFakeBalls.push_back(fakeball);
|
||||
|
||||
// Add collision to vector of collisions for dynamic resolution
|
||||
vecCollidingPairs.push_back({ &ball, fakeball });
|
||||
|
||||
// Calculate displacement required
|
||||
float fOverlap = 1.0f * (fDistance - ball.radius - fakeball->radius);
|
||||
|
||||
// Displace Current Ball away from collision
|
||||
ball.px -= fOverlap * (ball.px - fakeball->px) / fDistance;
|
||||
ball.py -= fOverlap * (ball.py - fakeball->py) / fDistance;
|
||||
}
|
||||
}
|
||||
|
||||
// Against other balls
|
||||
for (auto &target : vecBalls)
|
||||
{
|
||||
if (ball.id != target.id) // Do not check against self
|
||||
{
|
||||
if (DoCirclesOverlap(ball.px, ball.py, ball.radius, target.px, target.py, target.radius))
|
||||
{
|
||||
// Collision has occured
|
||||
vecCollidingPairs.push_back({ &ball, &target });
|
||||
|
||||
// Distance between ball centers
|
||||
float fDistance = sqrtf((ball.px - target.px)*(ball.px - target.px) + (ball.py - target.py)*(ball.py - target.py));
|
||||
|
||||
// Calculate displacement required
|
||||
float fOverlap = 0.5f * (fDistance - ball.radius - target.radius);
|
||||
|
||||
// Displace Current Ball away from collision
|
||||
ball.px -= fOverlap * (ball.px - target.px) / fDistance;
|
||||
ball.py -= fOverlap * (ball.py - target.py) / fDistance;
|
||||
|
||||
// Displace Target Ball away from collision - Note, this should affect the timing of the target ball
|
||||
// and it does, but this is absorbed by the target ball calculating its own time delta later on
|
||||
target.px += fOverlap * (ball.px - target.px) / fDistance;
|
||||
target.py += fOverlap * (ball.py - target.py) / fDistance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Time displacement - we knew the velocity of the ball, so we can estimate the distance it should have covered
|
||||
// however due to collisions it could not do the full distance, so we look at the actual distance to the collision
|
||||
// point and calculate how much time that journey would have taken using the speed of the object. Therefore
|
||||
// we can now work out how much time remains in that timestep.
|
||||
float fIntendedSpeed = sqrtf(ball.vx * ball.vx + ball.vy * ball.vy);
|
||||
float fIntendedDistance = fIntendedSpeed * ball.fSimTimeRemaining;
|
||||
float fActualDistance = sqrtf((ball.px - ball.ox)*(ball.px - ball.ox) + (ball.py - ball.oy)*(ball.py - ball.oy));
|
||||
float fActualTime = fActualDistance / fIntendedSpeed;
|
||||
|
||||
// After static resolution, there may be some time still left for this epoch, so allow simulation to continue
|
||||
ball.fSimTimeRemaining = ball.fSimTimeRemaining - fActualTime;
|
||||
}
|
||||
|
||||
// Now work out dynamic collisions
|
||||
float fEfficiency = 1.00f;
|
||||
for (auto c : vecCollidingPairs)
|
||||
{
|
||||
sBall *b1 = c.first, *b2 = c.second;
|
||||
|
||||
// Distance between balls
|
||||
float fDistance = sqrtf((b1->px - b2->px)*(b1->px - b2->px) + (b1->py - b2->py)*(b1->py - b2->py));
|
||||
|
||||
// Normal
|
||||
float nx = (b2->px - b1->px) / fDistance;
|
||||
float ny = (b2->py - b1->py) / fDistance;
|
||||
|
||||
// Tangent
|
||||
float tx = -ny;
|
||||
float ty = nx;
|
||||
|
||||
// Dot Product Tangent
|
||||
float dpTan1 = b1->vx * tx + b1->vy * ty;
|
||||
float dpTan2 = b2->vx * tx + b2->vy * ty;
|
||||
|
||||
// Dot Product Normal
|
||||
float dpNorm1 = b1->vx * nx + b1->vy * ny;
|
||||
float dpNorm2 = b2->vx * nx + b2->vy * ny;
|
||||
|
||||
// Conservation of momentum in 1D
|
||||
float m1 = fEfficiency * (dpNorm1 * (b1->mass - b2->mass) + 2.0f * b2->mass * dpNorm2) / (b1->mass + b2->mass);
|
||||
float m2 = fEfficiency * (dpNorm2 * (b2->mass - b1->mass) + 2.0f * b1->mass * dpNorm1) / (b1->mass + b2->mass);
|
||||
|
||||
// Update ball velocities
|
||||
b1->vx = tx * dpTan1 + nx * m1;
|
||||
b1->vy = ty * dpTan1 + ny * m1;
|
||||
b2->vx = tx * dpTan2 + nx * m2;
|
||||
b2->vy = ty * dpTan2 + ny * m2;
|
||||
}
|
||||
|
||||
// Remove collisions
|
||||
vecCollidingPairs.clear();
|
||||
|
||||
// Remove fake balls
|
||||
for (auto &b : vecFakeBalls) delete b;
|
||||
vecFakeBalls.clear();
|
||||
}
|
||||
}
|
||||
|
||||
// Clear Screen
|
||||
FillRect(0, 0, ScreenWidth(), ScreenHeight(), olc::Pixel(0, 0, 0));
|
||||
|
||||
// Draw Lines
|
||||
for (auto line : vecLines)
|
||||
{
|
||||
FillCircle(line.sx, line.sy, line.radius, olc::Pixel(255,255,255));
|
||||
FillCircle(line.ex, line.ey, line.radius, olc::Pixel(128, 128, 128));
|
||||
|
||||
float nx = -(line.ey - line.sy);
|
||||
float ny = (line.ex - line.sx);
|
||||
float d = sqrt(nx*nx + ny * ny);
|
||||
nx /= d;
|
||||
ny /= d;
|
||||
|
||||
DrawLine((line.sx + nx * line.radius), (line.sy + ny * line.radius), (line.ex + nx * line.radius), (line.ey + ny * line.radius), olc::Pixel(255, 255, 255));
|
||||
DrawLine((line.sx - nx * line.radius), (line.sy - ny * line.radius), (line.ex - nx * line.radius), (line.ey - ny * line.radius), olc::Pixel(255, 255, 255));
|
||||
}
|
||||
|
||||
// Draw Balls
|
||||
for (auto ball : vecBalls)
|
||||
{
|
||||
FillCircle(ball.px, ball.py, ball.radius, ball.col);
|
||||
|
||||
}
|
||||
|
||||
// Draw Cue
|
||||
if (pSelectedBall != nullptr)
|
||||
DrawLine(pSelectedBall->px, pSelectedBall->py, GetMouseX(), GetMouseY(), olc::Pixel(0, 0, 255));
|
||||
|
||||
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
|
||||
CirclePhysics game;
|
||||
if (game.Construct(320, 240, 4, 4))
|
||||
game.Start();
|
||||
else
|
||||
wcout << L"Could not construct console" << endl;
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
@ -1,245 +0,0 @@
|
||||
/*
|
||||
Easy Circle Vs Rectangle Collision Resolution
|
||||
"Everything's just damp, the walls, the roof, everything!" - javidx9
|
||||
|
||||
Video: https://youtu.be/D2a5fHX-Qrs
|
||||
|
||||
License (OLC-3)
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Copyright 2018 - 2021 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
|
||||
https://www.youtube.com/javidx9extra
|
||||
Discord: https://discord.gg/WhwHUMV
|
||||
Twitter: https://www.twitter.com/javidx9
|
||||
Twitch: https://www.twitch.tv/javidx9
|
||||
GitHub: https://www.github.com/onelonecoder
|
||||
Homepage: https://www.onelonecoder.com
|
||||
|
||||
Author
|
||||
~~~~~~
|
||||
David Barr, aka javidx9, ©OneLoneCoder 2019, 2020, 2021
|
||||
*/
|
||||
|
||||
#define OLC_PGE_APPLICATION
|
||||
#include "olcPixelGameEngine.h"
|
||||
|
||||
#define OLC_PGEX_TRANSFORMEDVIEW
|
||||
#include "olcPGEX_TransformedView.h"
|
||||
|
||||
|
||||
class CircleVsRect : public olc::PixelGameEngine
|
||||
{
|
||||
public:
|
||||
CircleVsRect()
|
||||
{
|
||||
sAppName = "Circle Vs Rectangle";
|
||||
}
|
||||
|
||||
private:
|
||||
olc::TileTransformedView tv;
|
||||
|
||||
struct sWorldObject
|
||||
{
|
||||
olc::vf2d vPos;
|
||||
olc::vf2d vVel;
|
||||
float fRadius = 0.5f;
|
||||
};
|
||||
|
||||
sWorldObject object;
|
||||
|
||||
std::string sWorldMap =
|
||||
"################################"
|
||||
"#..............................#"
|
||||
"#.......#####.#.....#####......#"
|
||||
"#.......#...#.#.....#..........#"
|
||||
"#.......#...#.#.....#..........#"
|
||||
"#.......#####.#####.#####......#"
|
||||
"#..............................#"
|
||||
"#.....#####.#####.#####..##....#"
|
||||
"#.........#.#...#.....#.#.#....#"
|
||||
"#.....#####.#...#.#####...#....#"
|
||||
"#.....#.....#...#.#.......#....#"
|
||||
"#.....#####.#####.#####.#####..#"
|
||||
"#..............................#"
|
||||
"#..............................#"
|
||||
"#..#.#..........#....#.........#"
|
||||
"#..#.#..........#....#.........#"
|
||||
"#..#.#.......#####.#######.....#"
|
||||
"#..#.#..........#....#.........#"
|
||||
"#..#.#.............###.#.#.....#"
|
||||
"#..#.##########................#"
|
||||
"#..#..........#....#.#.#.#.....#"
|
||||
"#..#.####.###.#................#"
|
||||
"#..#.#......#.#................#"
|
||||
"#..#.#.####.#.#....###..###....#"
|
||||
"#..#.#......#.#....#......#....#"
|
||||
"#..#.########.#....#......#....#"
|
||||
"#..#..........#....#......#....#"
|
||||
"#..############....#......#....#"
|
||||
"#..................########....#"
|
||||
"#..............................#"
|
||||
"#..............................#"
|
||||
"################################";
|
||||
|
||||
olc::vi2d vWorldSize = { 32, 32 };
|
||||
|
||||
bool bFollowObject = false;
|
||||
|
||||
public:
|
||||
bool OnUserCreate() override
|
||||
{
|
||||
// Create "Tiled World", where each tile is 32x32 screen pixels. Coordinates
|
||||
// for drawing will exist in unit-tile space from now on...
|
||||
tv = olc::TileTransformedView({ ScreenWidth(), ScreenHeight() }, { 32, 32 });
|
||||
object.vPos = { 3.0f, 3.0f };
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OnUserUpdate(float fElapsedTime) override
|
||||
{
|
||||
// Control of Player Object
|
||||
object.vVel = { 0.0f, 0.0f };
|
||||
if (GetKey(olc::Key::W).bHeld) object.vVel += { 0.0f, -1.0f };
|
||||
if (GetKey(olc::Key::S).bHeld) object.vVel += { 0.0f, +1.0f };
|
||||
if (GetKey(olc::Key::A).bHeld) object.vVel += { -1.0f, 0.0f };
|
||||
if (GetKey(olc::Key::D).bHeld) object.vVel += { +1.0f, 0.0f };
|
||||
|
||||
if (object.vVel.mag2() > 0)
|
||||
object.vVel = object.vVel.norm() * (GetKey(olc::Key::SHIFT).bHeld ? 5.0f : 2.0f);
|
||||
|
||||
if (GetKey(olc::Key::SPACE).bReleased) bFollowObject = !bFollowObject;
|
||||
|
||||
|
||||
// Where will object be worst case?
|
||||
olc::vf2d vPotentialPosition = object.vPos + object.vVel * fElapsedTime;
|
||||
|
||||
// Extract region of world cells that could have collision this frame
|
||||
olc::vi2d vCurrentCell = object.vPos.floor();
|
||||
olc::vi2d vTargetCell = vPotentialPosition;
|
||||
olc::vi2d vAreaTL = (vCurrentCell.min(vTargetCell) - olc::vi2d(1, 1)).max({ 0,0 });
|
||||
olc::vi2d vAreaBR = (vCurrentCell.max(vTargetCell) + olc::vi2d(1, 1)).min(vWorldSize);
|
||||
|
||||
olc::vf2d vRayToNearest;
|
||||
|
||||
// Iterate through each cell in test area
|
||||
olc::vi2d vCell;
|
||||
for (vCell.y = vAreaTL.y; vCell.y <= vAreaBR.y; vCell.y++)
|
||||
{
|
||||
for (vCell.x = vAreaTL.x; vCell.x <= vAreaBR.x; vCell.x++)
|
||||
{
|
||||
// Check if the cell is actually solid...
|
||||
if (sWorldMap[vCell.y * vWorldSize.x + vCell.x] == '#')
|
||||
{
|
||||
// ...it is! So work out nearest point to future player position, around perimeter
|
||||
// of cell rectangle. We can test the distance to this point to see if we have
|
||||
// collided.
|
||||
|
||||
olc::vf2d vNearestPoint;
|
||||
vNearestPoint.x = std::max(float(vCell.x), std::min(vPotentialPosition.x, float(vCell.x + 1)));
|
||||
vNearestPoint.y = std::max(float(vCell.y), std::min(vPotentialPosition.y, float(vCell.y + 1)));
|
||||
|
||||
olc::vf2d vRayToNearest = vNearestPoint - vPotentialPosition;
|
||||
float fOverlap = object.fRadius - vRayToNearest.mag();
|
||||
if (std::isnan(fOverlap)) fOverlap = 0;
|
||||
|
||||
// If overlap is positive, then a collision has occurred, so we displace backwards by the
|
||||
// overlap amount. The potential position is then tested against other tiles in the area
|
||||
// therefore "statically" resolving the collision
|
||||
if (fOverlap > 0)
|
||||
{
|
||||
// Statically resolve the collision
|
||||
vPotentialPosition = vPotentialPosition - vRayToNearest.norm() * fOverlap;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set the objects new position to the allowed potential position
|
||||
object.vPos = vPotentialPosition;
|
||||
|
||||
|
||||
// Clear World
|
||||
Clear(olc::VERY_DARK_BLUE);
|
||||
|
||||
if (bFollowObject)
|
||||
{
|
||||
tv.SetWorldOffset(object.vPos - tv.ScaleToWorld(olc::vf2d(ScreenWidth()/2.0f, ScreenHeight()/2.0f)));
|
||||
DrawString({ 10,10 }, "Following Object");
|
||||
}
|
||||
|
||||
// Handle Pan & Zoom
|
||||
if (GetMouse(2).bPressed) tv.StartPan(GetMousePos());
|
||||
if (GetMouse(2).bHeld) tv.UpdatePan(GetMousePos());
|
||||
if (GetMouse(2).bReleased) tv.EndPan(GetMousePos());
|
||||
if (GetMouseWheel() > 0) tv.ZoomAtScreenPos(2.0f, GetMousePos());
|
||||
if (GetMouseWheel() < 0) tv.ZoomAtScreenPos(0.5f, GetMousePos());
|
||||
|
||||
// Draw World
|
||||
olc::vi2d vTL = tv.GetTopLeftTile().max({ 0,0 });
|
||||
olc::vi2d vBR = tv.GetBottomRightTile().min(vWorldSize);
|
||||
olc::vi2d vTile;
|
||||
for (vTile.y = vTL.y; vTile.y < vBR.y; vTile.y++)
|
||||
for (vTile.x = vTL.x; vTile.x < vBR.x; vTile.x++)
|
||||
{
|
||||
if (sWorldMap[vTile.y * vWorldSize.x + vTile.x] == '#')
|
||||
{
|
||||
tv.DrawRect(vTile, { 1.0f, 1.0f }, olc::WHITE);
|
||||
tv.DrawLine(vTile, vTile + olc::vf2d(1.0f, 1.0f), olc::WHITE);
|
||||
tv.DrawLine(vTile + olc::vf2d(0.0f, 1.0f), vTile + olc::vf2d(1.0f, 0.0f), olc::WHITE);
|
||||
}
|
||||
}
|
||||
|
||||
tv.FillRectDecal(vAreaTL, vAreaBR - vAreaTL + olc::vi2d(1,1), olc::Pixel(0,255,255,32));
|
||||
|
||||
// Draw Boundary
|
||||
tv.DrawCircle(object.vPos, object.fRadius, olc::WHITE);
|
||||
|
||||
// Draw Velocity
|
||||
if (object.vVel.mag2() > 0)
|
||||
{
|
||||
tv.DrawLine(object.vPos, object.vPos + object.vVel.norm() * object.fRadius, olc::MAGENTA);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
CircleVsRect demo;
|
||||
if (demo.Construct(640, 480, 2, 2))
|
||||
demo.Start();
|
||||
return 0;
|
||||
}
|
||||
@ -1,265 +0,0 @@
|
||||
/*
|
||||
Blithering About Dithering (Floyd-Steinberg)
|
||||
"2022 lets go!" - javidx9
|
||||
|
||||
License (OLC-3)
|
||||
~~~~~~~~~~~~~~~
|
||||
Copyright 2018 - 2021 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.
|
||||
|
||||
Video:
|
||||
~~~~~~
|
||||
https://youtu.be/lseR6ZguBNY
|
||||
|
||||
Use Q and W keys to view quantised image and dithered image respectively
|
||||
Use Left mouse button to pan, and mouse wheel to zoom to cursor
|
||||
|
||||
Links
|
||||
~~~~~
|
||||
YouTube: https://www.youtube.com/javidx9
|
||||
https://www.youtube.com/javidx9extra
|
||||
Discord: https://discord.gg/WhwHUMV
|
||||
Twitter: https://www.twitter.com/javidx9
|
||||
Twitch: https://www.twitch.tv/javidx9
|
||||
GitHub: https://www.github.com/onelonecoder
|
||||
Homepage: https://www.onelonecoder.com
|
||||
|
||||
Author
|
||||
~~~~~~
|
||||
David Barr, aka javidx9, ©OneLoneCoder 2019, 2020, 2021, 2022
|
||||
*/
|
||||
|
||||
|
||||
// Using olc::PixelGameEngine for input and visualisation
|
||||
#define OLC_PGE_APPLICATION
|
||||
#include "olcPixelGameEngine.h"
|
||||
|
||||
// Using a transformed view to handle pan and zoom
|
||||
#define OLC_PGEX_TRANSFORMEDVIEW
|
||||
#include "olcPGEX_TransformedView.h"
|
||||
|
||||
//#include <algorithm>
|
||||
|
||||
// Override base class with your custom functionality
|
||||
class Dithering : public olc::PixelGameEngine
|
||||
{
|
||||
public:
|
||||
Dithering()
|
||||
{
|
||||
sAppName = "Floyd Steinberg Dithering";
|
||||
}
|
||||
|
||||
olc::TransformedView tv;
|
||||
std::unique_ptr<olc::Sprite> m_pImage;
|
||||
std::unique_ptr<olc::Sprite> m_pQuantised;
|
||||
std::unique_ptr<olc::Sprite> m_pDithered;
|
||||
|
||||
public:
|
||||
// Called once at start of application
|
||||
bool OnUserCreate() override
|
||||
{
|
||||
// Prepare Pan & Zoom
|
||||
tv.Initialise({ ScreenWidth(), ScreenHeight() });
|
||||
|
||||
// Load Test Image
|
||||
m_pImage = std::make_unique<olc::Sprite>("./assets/Flower_640x480.png");
|
||||
|
||||
// Create two more images with the same dimensions
|
||||
m_pQuantised = std::make_unique<olc::Sprite>(m_pImage->width, m_pImage->height);
|
||||
m_pDithered = std::make_unique<olc::Sprite>(m_pImage->width, m_pImage->height);
|
||||
|
||||
// These lambda functions output a new olc::Pixel based on
|
||||
// the pixel it is given
|
||||
auto Convert_RGB_To_Greyscale = [](const olc::Pixel in)
|
||||
{
|
||||
uint8_t greyscale = uint8_t(0.2162f * float(in.r) + 0.7152f * float(in.g) + 0.0722f * float(in.b));
|
||||
return olc::Pixel(greyscale, greyscale, greyscale);
|
||||
};
|
||||
|
||||
|
||||
// Quantising functions
|
||||
auto Quantise_Greyscale_1Bit = [](const olc::Pixel in)
|
||||
{
|
||||
return in.r < 128 ? olc::BLACK : olc::WHITE;
|
||||
};
|
||||
|
||||
auto Quantise_Greyscale_NBit = [](const olc::Pixel in)
|
||||
{
|
||||
constexpr int nBits = 2;
|
||||
constexpr float fLevels = (1 << nBits) - 1;
|
||||
uint8_t c = uint8_t(std::clamp(std::round(float(in.r) / 255.0f * fLevels) / fLevels * 255.0f, 0.0f, 255.0f));
|
||||
return olc::Pixel(c, c, c);
|
||||
};
|
||||
|
||||
auto Quantise_RGB_NBit = [](const olc::Pixel in)
|
||||
{
|
||||
constexpr int nBits = 2;
|
||||
constexpr float fLevels = (1 << nBits) - 1;
|
||||
uint8_t cr = uint8_t(std::clamp(std::round(float(in.r) / 255.0f * fLevels) / fLevels * 255.0f, 0.0f, 255.0f));
|
||||
uint8_t cb = uint8_t(std::clamp(std::round(float(in.g) / 255.0f * fLevels) / fLevels * 255.0f, 0.0f, 255.0f));
|
||||
uint8_t cg = uint8_t(std::clamp(std::round(float(in.b) / 255.0f * fLevels) / fLevels * 255.0f, 0.0f, 255.0f));
|
||||
return olc::Pixel(cr, cb, cg);
|
||||
};
|
||||
|
||||
auto Quantise_RGB_CustomPalette = [](const olc::Pixel in)
|
||||
{
|
||||
std::array<olc::Pixel, 5> nShades = { olc::BLACK, olc::WHITE, olc::YELLOW, olc::MAGENTA, olc::CYAN };
|
||||
|
||||
float fClosest = INFINITY;
|
||||
olc::Pixel pClosest;
|
||||
|
||||
for (const auto& c : nShades)
|
||||
{
|
||||
float fDistance = float(
|
||||
std::sqrt(
|
||||
std::pow(float(c.r) - float(in.r), 2) +
|
||||
std::pow(float(c.g) - float(in.g), 2) +
|
||||
std::pow(float(c.b) - float(in.b), 2)));
|
||||
|
||||
if (fDistance < fClosest)
|
||||
{
|
||||
fClosest = fDistance;
|
||||
pClosest = c;
|
||||
}
|
||||
}
|
||||
|
||||
return pClosest;
|
||||
};
|
||||
|
||||
|
||||
// We don't need greyscale for the final demonstration, which uses
|
||||
// RGB, but I've left this here as reference
|
||||
//std::transform(
|
||||
// m_pImage->pColData.begin(),
|
||||
// m_pImage->pColData.end(),
|
||||
// m_pImage->pColData.begin(), Convert_RGB_To_Greyscale);
|
||||
|
||||
|
||||
// Quantise The Image
|
||||
std::transform(
|
||||
m_pImage->pColData.begin(),
|
||||
m_pImage->pColData.end(),
|
||||
m_pQuantised->pColData.begin(), Quantise_RGB_NBit);
|
||||
|
||||
// Perform Dither
|
||||
Dither_FloydSteinberg(m_pImage.get(), m_pDithered.get(), Quantise_RGB_NBit);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Dither_FloydSteinberg(const olc::Sprite* pSource, olc::Sprite* pDest,
|
||||
std::function<olc::Pixel(const olc::Pixel)> funcQuantise)
|
||||
{
|
||||
// The destination image is primed with the source image as the pixel
|
||||
// values become altered as the algorithm executes
|
||||
std::copy(pSource->pColData.begin(), pSource->pColData.end(), pDest->pColData.begin());
|
||||
|
||||
// Iterate through each pixel from top left to bottom right, compare the pixel
|
||||
// with that on the "allowed" list, and distribute that error to neighbours
|
||||
// not yet computed
|
||||
olc::vi2d vPixel;
|
||||
for (vPixel.y = 0; vPixel.y < pSource->height; vPixel.y++)
|
||||
{
|
||||
for (vPixel.x = 0; vPixel.x < pSource->width; vPixel.x++)
|
||||
{
|
||||
// Grap and get nearest pixel equivalent from our allowed
|
||||
// palette
|
||||
olc::Pixel op = pDest->GetPixel(vPixel);
|
||||
olc::Pixel qp = funcQuantise(op);
|
||||
|
||||
// olc::Pixels are "inconveniently" clamped to sensible ranges using an unsigned type...
|
||||
// ...which means they cant be negative. This hampers us a tad here,
|
||||
// so will resort to manual alteration using a signed type
|
||||
int32_t error[3] =
|
||||
{
|
||||
op.r - qp.r,
|
||||
op.g - qp.g,
|
||||
op.b - qp.b
|
||||
};
|
||||
|
||||
// Set destination pixel with nearest match from quantisation function
|
||||
pDest->SetPixel(vPixel, qp);
|
||||
|
||||
// Distribute Error - Using a little utility lambda to keep the messy code
|
||||
// all in one place. It's important to allow pixels to temporarily become
|
||||
// negative in order to distribute the error to the neighbours in both
|
||||
// directions... value directions that is, not spatial!
|
||||
auto UpdatePixel = [&vPixel, &pDest, &error](const olc::vi2d& vOffset, const float fErrorBias)
|
||||
{
|
||||
olc::Pixel p = pDest->GetPixel(vPixel + vOffset);
|
||||
int32_t k[3] = { p.r, p.g, p.b };
|
||||
k[0] += int32_t(float(error[0]) * fErrorBias);
|
||||
k[1] += int32_t(float(error[1]) * fErrorBias);
|
||||
k[2] += int32_t(float(error[2]) * fErrorBias);
|
||||
pDest->SetPixel(vPixel + vOffset, olc::Pixel(std::clamp(k[0], 0, 255), std::clamp(k[1], 0, 255), std::clamp(k[2], 0, 255)));
|
||||
};
|
||||
|
||||
UpdatePixel({ +1, 0 }, 7.0f / 16.0f);
|
||||
UpdatePixel({ -1, +1 }, 3.0f / 16.0f);
|
||||
UpdatePixel({ 0, +1 }, 5.0f / 16.0f);
|
||||
UpdatePixel({ +1, +1 }, 1.0f / 16.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Called every frame
|
||||
bool OnUserUpdate(float fElapsedTime) override
|
||||
{
|
||||
// Handle Pan & Zoom using defaults middle mouse button
|
||||
tv.HandlePanAndZoom(0);
|
||||
|
||||
// Erase previous frame
|
||||
Clear(olc::BLACK);
|
||||
|
||||
// Draw Source Image
|
||||
if (GetKey(olc::Key::Q).bHeld)
|
||||
{
|
||||
tv.DrawSprite({ 0,0 }, m_pQuantised.get());
|
||||
}
|
||||
else if (GetKey(olc::Key::W).bHeld)
|
||||
{
|
||||
tv.DrawSprite({ 0,0 }, m_pDithered.get());
|
||||
}
|
||||
else
|
||||
{
|
||||
tv.DrawSprite({ 0,0 }, m_pImage.get());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
Dithering demo;
|
||||
if (demo.Construct(1280, 720, 1, 1))
|
||||
demo.Start();
|
||||
return 0;
|
||||
}
|
||||
@ -1,431 +0,0 @@
|
||||
/*
|
||||
Dungeon Warping via Orthographic Projections
|
||||
"For my Mother-In-Law, you will be missed..." - javidx9
|
||||
|
||||
License (OLC-3)
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Copyright 2018-2020 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.
|
||||
|
||||
Relevant Video: https://youtu.be/Ql5VZGkL23o
|
||||
|
||||
Links
|
||||
~~~~~
|
||||
YouTube: https://www.youtube.com/javidx9
|
||||
https://www.youtube.com/javidx9extra
|
||||
Discord: https://discord.gg/WhwHUMV
|
||||
Twitter: https://www.twitter.com/javidx9
|
||||
Twitch: https://www.twitch.tv/javidx9
|
||||
GitHub: https://www.github.com/onelonecoder
|
||||
Patreon: https://www.patreon.com/javidx9
|
||||
Homepage: https://www.onelonecoder.com
|
||||
|
||||
Community Blog: https://community.onelonecoder.com
|
||||
|
||||
Author
|
||||
~~~~~~
|
||||
David Barr, aka javidx9, ©OneLoneCoder 2018, 2019, 2020
|
||||
*/
|
||||
|
||||
#define OLC_PGE_APPLICATION
|
||||
#include "olcPixelGameEngine.h"
|
||||
|
||||
/*
|
||||
|
||||
NOTE! This program requires a tile spritesheet NOT
|
||||
provided in this github. You only need a few tiles,
|
||||
see video for details.
|
||||
|
||||
*/
|
||||
|
||||
class olcDungeon : public olc::PixelGameEngine
|
||||
{
|
||||
public:
|
||||
olcDungeon()
|
||||
{
|
||||
sAppName = "Dungeon Explorer";
|
||||
}
|
||||
|
||||
struct Renderable
|
||||
{
|
||||
Renderable() {}
|
||||
|
||||
void Load(const std::string& sFile)
|
||||
{
|
||||
sprite = new olc::Sprite(sFile);
|
||||
decal = new olc::Decal(sprite);
|
||||
}
|
||||
|
||||
~Renderable()
|
||||
{
|
||||
delete decal;
|
||||
delete sprite;
|
||||
}
|
||||
|
||||
olc::Sprite* sprite = nullptr;
|
||||
olc::Decal* decal = nullptr;
|
||||
};
|
||||
|
||||
struct vec3d
|
||||
{
|
||||
float x, y, z;
|
||||
};
|
||||
|
||||
struct sQuad
|
||||
{
|
||||
vec3d points[4];
|
||||
olc::vf2d tile;
|
||||
};
|
||||
|
||||
struct sCell
|
||||
{
|
||||
bool wall = false;
|
||||
olc::vi2d id[6]{ };
|
||||
};
|
||||
|
||||
class World
|
||||
{
|
||||
public:
|
||||
World()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Create(int w, int h)
|
||||
{
|
||||
size = { w, h };
|
||||
vCells.resize(w * h);
|
||||
}
|
||||
|
||||
sCell& GetCell(const olc::vi2d& v)
|
||||
{
|
||||
if (v.x >= 0 && v.x < size.x && v.y >= 0 && v.y < size.y)
|
||||
return vCells[v.y * size.x + v.x];
|
||||
else
|
||||
return NullCell;
|
||||
}
|
||||
|
||||
public:
|
||||
olc::vi2d size;
|
||||
|
||||
private:
|
||||
std::vector<sCell> vCells;
|
||||
sCell NullCell;
|
||||
};
|
||||
|
||||
World world;
|
||||
Renderable rendSelect;
|
||||
Renderable rendAllWalls;
|
||||
|
||||
olc::vf2d vCameraPos = { 0.0f, 0.0f };
|
||||
float fCameraAngle = 0.0f;
|
||||
float fCameraAngleTarget = fCameraAngle;
|
||||
float fCameraPitch = 5.5f;
|
||||
float fCameraZoom = 16.0f;
|
||||
|
||||
bool bVisible[6];
|
||||
|
||||
olc::vi2d vCursor = { 0, 0 };
|
||||
olc::vi2d vTileCursor = { 0,0 };
|
||||
olc::vi2d vTileSize = { 32, 32 };
|
||||
|
||||
enum Face
|
||||
{
|
||||
Floor = 0,
|
||||
North = 1,
|
||||
East = 2,
|
||||
South = 3,
|
||||
West = 4,
|
||||
Top = 5
|
||||
};
|
||||
|
||||
public:
|
||||
bool OnUserCreate() override
|
||||
{
|
||||
rendSelect.Load("./gfx/dng_select.png");
|
||||
rendAllWalls.Load("./gfx/oldDungeon.png");
|
||||
|
||||
world.Create(64, 64);
|
||||
|
||||
for (int y=0; y<world.size.y; y++)
|
||||
for(int x=0; x<world.size.x; x++)
|
||||
{
|
||||
world.GetCell({ x, y }).wall = false;
|
||||
world.GetCell({ x, y }).id[Face::Floor] = olc::vi2d{ 3, 0 } * vTileSize;
|
||||
world.GetCell({ x, y }).id[Face::Top] = olc::vi2d{ 1, 0 } * vTileSize;
|
||||
world.GetCell({ x, y }).id[Face::North] = olc::vi2d{ 0, 6 } * vTileSize;
|
||||
world.GetCell({ x, y }).id[Face::South] = olc::vi2d{ 0, 6 } * vTileSize;
|
||||
world.GetCell({ x, y }).id[Face::West] = olc::vi2d{ 0, 6 } * vTileSize;
|
||||
world.GetCell({ x, y }).id[Face::East] = olc::vi2d{ 0, 6 } * vTileSize;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::array<vec3d, 8> CreateCube(const olc::vi2d& vCell, const float fAngle, const float fPitch, const float fScale, const vec3d& vCamera)
|
||||
{
|
||||
// Unit Cube
|
||||
std::array<vec3d, 8> unitCube, rotCube, worldCube, projCube;
|
||||
unitCube[0] = { 0.0f, 0.0f, 0.0f };
|
||||
unitCube[1] = { fScale, 0.0f, 0.0f };
|
||||
unitCube[2] = { fScale, -fScale, 0.0f };
|
||||
unitCube[3] = { 0.0f, -fScale, 0.0f };
|
||||
unitCube[4] = { 0.0f, 0.0f, fScale };
|
||||
unitCube[5] = { fScale, 0.0f, fScale };
|
||||
unitCube[6] = { fScale, -fScale, fScale };
|
||||
unitCube[7] = { 0.0f, -fScale, fScale };
|
||||
|
||||
// Translate Cube in X-Z Plane
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
unitCube[i].x += (vCell.x * fScale - vCamera.x);
|
||||
unitCube[i].y += -vCamera.y;
|
||||
unitCube[i].z += (vCell.y * fScale - vCamera.z);
|
||||
}
|
||||
|
||||
// Rotate Cube in Y-Axis around origin
|
||||
float s = sin(fAngle);
|
||||
float c = cos(fAngle);
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
rotCube[i].x = unitCube[i].x * c + unitCube[i].z * s;
|
||||
rotCube[i].y = unitCube[i].y;
|
||||
rotCube[i].z = unitCube[i].x * -s + unitCube[i].z * c;
|
||||
}
|
||||
|
||||
// Rotate Cube in X-Axis around origin (tilt slighly overhead)
|
||||
s = sin(fPitch);
|
||||
c = cos(fPitch);
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
worldCube[i].x = rotCube[i].x;
|
||||
worldCube[i].y = rotCube[i].y * c - rotCube[i].z * s;
|
||||
worldCube[i].z = rotCube[i].y * s + rotCube[i].z * c;
|
||||
}
|
||||
|
||||
// Project Cube Orthographically - Unit Cube Viewport
|
||||
//float fLeft = -ScreenWidth() * 0.5f;
|
||||
//float fRight = ScreenWidth() * 0.5f;
|
||||
//float fTop = ScreenHeight() * 0.5f;
|
||||
//float fBottom = -ScreenHeight() * 0.5f;
|
||||
//float fNear = 0.1f;
|
||||
//float fFar = 100.0f;*/
|
||||
//for (int i = 0; i < 8; i++)
|
||||
//{
|
||||
// projCube[i].x = (2.0f / (fRight - fLeft)) * worldCube[i].x - ((fRight + fLeft) / (fRight - fLeft));
|
||||
// projCube[i].y = (2.0f / (fTop - fBottom)) * worldCube[i].y - ((fTop + fBottom) / (fTop - fBottom));
|
||||
// projCube[i].z = (2.0f / (fFar - fNear)) * worldCube[i].z - ((fFar + fNear) / (fFar - fNear));
|
||||
// projCube[i].x *= -fRight;
|
||||
// projCube[i].y *= -fTop;
|
||||
// projCube[i].x += fRight;
|
||||
// projCube[i].y += fTop;
|
||||
//}
|
||||
|
||||
// Project Cube Orthographically - Full Screen Centered
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
projCube[i].x = worldCube[i].x + ScreenWidth() * 0.5f;
|
||||
projCube[i].y = worldCube[i].y + ScreenHeight() * 0.5f;
|
||||
projCube[i].z = worldCube[i].z;
|
||||
}
|
||||
|
||||
return projCube;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void CalculateVisibleFaces(std::array<vec3d, 8>& cube)
|
||||
{
|
||||
auto CheckNormal = [&](int v1, int v2, int v3)
|
||||
{
|
||||
olc::vf2d a = { cube[v1].x, cube[v1].y };
|
||||
olc::vf2d b = { cube[v2].x, cube[v2].y };
|
||||
olc::vf2d c = { cube[v3].x, cube[v3].y };
|
||||
return (b - a).cross(c - a) > 0;
|
||||
};
|
||||
|
||||
bVisible[Face::Floor] = CheckNormal(4, 0, 1);
|
||||
bVisible[Face::South] = CheckNormal(3, 0, 1);
|
||||
bVisible[Face::North] = CheckNormal(6, 5, 4);
|
||||
bVisible[Face::East] = CheckNormal(7, 4, 0);
|
||||
bVisible[Face::West] = CheckNormal(2, 1, 5);
|
||||
bVisible[Face::Top] = CheckNormal(7, 3, 2);
|
||||
}
|
||||
|
||||
void GetFaceQuads(const olc::vi2d& vCell, const float fAngle, const float fPitch, const float fScale, const vec3d& vCamera, std::vector<sQuad> &render)
|
||||
{
|
||||
std::array<vec3d, 8> projCube = CreateCube(vCell, fAngle, fPitch, fScale, vCamera);
|
||||
|
||||
auto& cell = world.GetCell(vCell);
|
||||
|
||||
auto MakeFace = [&](int v1, int v2, int v3, int v4, Face f)
|
||||
{
|
||||
render.push_back({ projCube[v1], projCube[v2], projCube[v3], projCube[v4], cell.id[f] });
|
||||
};
|
||||
|
||||
if (!cell.wall)
|
||||
{
|
||||
if(bVisible[Face::Floor]) MakeFace(4, 0, 1, 5, Face::Floor);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (bVisible[Face::South]) MakeFace(3, 0, 1, 2, Face::South);
|
||||
if (bVisible[Face::North]) MakeFace(6, 5, 4, 7, Face::North);
|
||||
if (bVisible[Face::East]) MakeFace(7, 4, 0, 3, Face::East);
|
||||
if (bVisible[Face::West]) MakeFace(2, 1, 5, 6, Face::West);
|
||||
if (bVisible[Face::Top]) MakeFace(7, 3, 2, 6, Face::Top);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool OnUserUpdate(float fElapsedTime) override
|
||||
{
|
||||
// Grab mouse for convenience
|
||||
olc::vi2d vMouse = { GetMouseX(), GetMouseY() };
|
||||
|
||||
// Edit mode - Selection from tile sprite sheet
|
||||
if (GetKey(olc::Key::TAB).bHeld)
|
||||
{
|
||||
DrawSprite({ 0, 0 }, rendAllWalls.sprite);
|
||||
DrawRect(vTileCursor * vTileSize, vTileSize);
|
||||
if (GetMouse(0).bPressed) vTileCursor = vMouse / vTileSize;
|
||||
return true;
|
||||
}
|
||||
|
||||
// WS keys to tilt camera
|
||||
if (GetKey(olc::Key::W).bHeld) fCameraPitch += 1.0f * fElapsedTime;
|
||||
if (GetKey(olc::Key::S).bHeld) fCameraPitch -= 1.0f * fElapsedTime;
|
||||
|
||||
// DA Keys to manually rotate camera
|
||||
if (GetKey(olc::Key::D).bHeld) fCameraAngleTarget += 1.0f * fElapsedTime;
|
||||
if (GetKey(olc::Key::A).bHeld) fCameraAngleTarget -= 1.0f * fElapsedTime;
|
||||
|
||||
// QZ Keys to zoom in or out
|
||||
if (GetKey(olc::Key::Q).bHeld) fCameraZoom += 5.0f * fElapsedTime;
|
||||
if (GetKey(olc::Key::Z).bHeld) fCameraZoom -= 5.0f * fElapsedTime;
|
||||
|
||||
// Numpad keys used to rotate camera to fixed angles
|
||||
if (GetKey(olc::Key::NP2).bPressed) fCameraAngleTarget = 3.14159f * 0.0f;
|
||||
if (GetKey(olc::Key::NP1).bPressed) fCameraAngleTarget = 3.14159f * 0.25f;
|
||||
if (GetKey(olc::Key::NP4).bPressed) fCameraAngleTarget = 3.14159f * 0.5f;
|
||||
if (GetKey(olc::Key::NP7).bPressed) fCameraAngleTarget = 3.14159f * 0.75f;
|
||||
if (GetKey(olc::Key::NP8).bPressed) fCameraAngleTarget = 3.14159f * 1.0f;
|
||||
if (GetKey(olc::Key::NP9).bPressed) fCameraAngleTarget = 3.14159f * 1.25f;
|
||||
if (GetKey(olc::Key::NP6).bPressed) fCameraAngleTarget = 3.14159f * 1.5f;
|
||||
if (GetKey(olc::Key::NP3).bPressed) fCameraAngleTarget = 3.14159f * 1.75f;
|
||||
|
||||
// Numeric keys apply selected tile to specific face
|
||||
if (GetKey(olc::Key::K1).bPressed) world.GetCell(vCursor).id[Face::North] = vTileCursor * vTileSize;
|
||||
if (GetKey(olc::Key::K2).bPressed) world.GetCell(vCursor).id[Face::East] = vTileCursor * vTileSize;
|
||||
if (GetKey(olc::Key::K3).bPressed) world.GetCell(vCursor).id[Face::South] = vTileCursor * vTileSize;
|
||||
if (GetKey(olc::Key::K4).bPressed) world.GetCell(vCursor).id[Face::West] = vTileCursor * vTileSize;
|
||||
if (GetKey(olc::Key::K5).bPressed) world.GetCell(vCursor).id[Face::Floor] = vTileCursor * vTileSize;
|
||||
if (GetKey(olc::Key::K6).bPressed) world.GetCell(vCursor).id[Face::Top] = vTileCursor * vTileSize;
|
||||
|
||||
// Smooth camera
|
||||
fCameraAngle += (fCameraAngleTarget - fCameraAngle) * 10.0f * fElapsedTime;
|
||||
|
||||
// Arrow keys to move the selection cursor around map (boundary checked)
|
||||
if (GetKey(olc::Key::LEFT).bPressed) vCursor.x--;
|
||||
if (GetKey(olc::Key::RIGHT).bPressed) vCursor.x++;
|
||||
if (GetKey(olc::Key::UP).bPressed) vCursor.y--;
|
||||
if (GetKey(olc::Key::DOWN).bPressed) vCursor.y++;
|
||||
if (vCursor.x < 0) vCursor.x = 0;
|
||||
if (vCursor.y < 0) vCursor.y = 0;
|
||||
if (vCursor.x >= world.size.x) vCursor.x = world.size.x - 1;
|
||||
if (vCursor.y >= world.size.y) vCursor.y = world.size.y - 1;
|
||||
|
||||
// Place block with space
|
||||
if (GetKey(olc::Key::SPACE).bPressed)
|
||||
{
|
||||
world.GetCell(vCursor).wall = !world.GetCell(vCursor).wall;
|
||||
}
|
||||
|
||||
// Position camera in world
|
||||
vCameraPos = { vCursor.x + 0.5f, vCursor.y + 0.5f };
|
||||
vCameraPos *= fCameraZoom;
|
||||
|
||||
// Rendering
|
||||
|
||||
// 1) Create dummy cube to extract visible face information
|
||||
// Cull faces that cannot be seen
|
||||
std::array<vec3d, 8> cullCube = CreateCube({ 0, 0 }, fCameraAngle, fCameraPitch, fCameraZoom, { vCameraPos.x, 0.0f, vCameraPos.y });
|
||||
CalculateVisibleFaces(cullCube);
|
||||
|
||||
// 2) Get all visible sides of all visible "tile cubes"
|
||||
std::vector<sQuad> vQuads;
|
||||
for(int y = 0; y<world.size.y; y++)
|
||||
for(int x=0; x<world.size.x; x++)
|
||||
GetFaceQuads({ x, y }, fCameraAngle, fCameraPitch, fCameraZoom, { vCameraPos.x, 0.0f, vCameraPos.y }, vQuads);
|
||||
|
||||
// 3) Sort in order of depth, from farthest away to closest
|
||||
std::sort(vQuads.begin(), vQuads.end(), [](const sQuad& q1, const sQuad& q2)
|
||||
{
|
||||
float z1 = (q1.points[0].z + q1.points[1].z + q1.points[2].z + q1.points[3].z) * 0.25f;
|
||||
float z2 = (q2.points[0].z + q2.points[1].z + q2.points[2].z + q2.points[3].z) * 0.25f;
|
||||
return z1 < z2;
|
||||
});
|
||||
|
||||
// 4) Iterate through all "tile cubes" and draw their visible faces
|
||||
Clear(olc::BLACK);
|
||||
for (auto& q : vQuads)
|
||||
DrawPartialWarpedDecal
|
||||
(
|
||||
rendAllWalls.decal,
|
||||
{ {q.points[0].x, q.points[0].y}, {q.points[1].x, q.points[1].y}, {q.points[2].x, q.points[2].y}, {q.points[3].x, q.points[3].y} },
|
||||
q.tile,
|
||||
vTileSize
|
||||
);
|
||||
|
||||
// 5) Draw current tile selection
|
||||
DrawPartialDecal({ 10,10 }, rendAllWalls.decal, vTileCursor * vTileSize, vTileSize);
|
||||
|
||||
// 6) Draw selection "tile cube"
|
||||
vQuads.clear();
|
||||
GetFaceQuads(vCursor, fCameraAngle, fCameraPitch, fCameraZoom, { vCameraPos.x, 0.0f, vCameraPos.y }, vQuads);
|
||||
for (auto& q : vQuads)
|
||||
DrawWarpedDecal(rendSelect.decal, { {q.points[0].x, q.points[0].y}, {q.points[1].x, q.points[1].y}, {q.points[2].x, q.points[2].y}, {q.points[3].x, q.points[3].y} });
|
||||
|
||||
// 7) Draw some debug info
|
||||
DrawStringDecal({ 0,0 }, "Cursor: " + std::to_string(vCursor.x) + ", " + std::to_string(vCursor.y), olc::YELLOW, { 0.5f, 0.5f });
|
||||
DrawStringDecal({ 0,8 }, "Angle: " + std::to_string(fCameraAngle) + ", " + std::to_string(fCameraPitch), olc::YELLOW, { 0.5f, 0.5f });
|
||||
|
||||
// Graceful exit if user is in full screen mode
|
||||
return !GetKey(olc::Key::ESCAPE).bPressed;
|
||||
}
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
olcDungeon demo;
|
||||
if (demo.Construct(640, 480, 2, 2, false))
|
||||
demo.Start();
|
||||
return 0;
|
||||
}
|
||||
@ -1,189 +0,0 @@
|
||||
/*
|
||||
OneLoneCoder_PGE_ExtensionTestGFX2D.cpp
|
||||
|
||||
|
||||
License (OLC-3)
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Copyright 2018 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 2018
|
||||
*/
|
||||
|
||||
// Include the olcPixelGameEngine
|
||||
#define OLC_PGE_APPLICATION
|
||||
#include "olcPixelGameEngine.h"
|
||||
|
||||
// To use an extension, just include it (thanks MaxCE, for pointing out old definition format)
|
||||
#define OLC_PGEX_GRAPHICS2D
|
||||
#include "olcPGEX_Graphics2D.h"
|
||||
|
||||
class TestExtension : public olc::PixelGameEngine
|
||||
{
|
||||
public:
|
||||
TestExtension()
|
||||
{
|
||||
sAppName = "Testing Graphics2D";
|
||||
}
|
||||
|
||||
public:
|
||||
bool OnUserCreate() override
|
||||
{
|
||||
for (int i = 0; i < 16; i++)
|
||||
listEvents.push_back("");
|
||||
|
||||
spr = new olc::Sprite("new_piskel.png");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::list<std::string> listEvents;
|
||||
float fTotalTime = 0.0f;
|
||||
olc::Sprite *spr;
|
||||
|
||||
bool OnUserUpdate(float fElapsedTime) override
|
||||
{
|
||||
// Clear Screen
|
||||
SetPixelMode(olc::Pixel::NORMAL);
|
||||
Clear(olc::BLUE);
|
||||
|
||||
// Draw Primitives
|
||||
DrawCircle(32, 32, 30); // Circle
|
||||
DrawCircle(96, 32, 30); // Circle
|
||||
|
||||
|
||||
float mx = (float)GetMouseX();
|
||||
float my = (float)GetMouseY();
|
||||
|
||||
float px1 = mx - 32, px2 = mx - 96;
|
||||
float py1 = my - 32, py2 = my - 32;
|
||||
float pr1 = 1.0f / sqrtf(px1*px1 + py1*py1);
|
||||
float pr2 = 1.0f / sqrtf(px2*px2 + py2*py2);
|
||||
px1 = 22.0f * (px1 * pr1) + 32.0f;
|
||||
py1 = 22.0f * (py1 * pr1) + 32.0f;
|
||||
px2 = 22.0f * (px2 * pr2) + 96.0f;
|
||||
py2 = 22.0f * (py2 * pr2) + 32.0f;
|
||||
FillCircle((int32_t)px1, (int32_t)py1, 8, olc::CYAN);
|
||||
FillCircle((int32_t)px2, (int32_t)py2, 8, olc::CYAN);
|
||||
|
||||
DrawLine(10, 70, 54, 70); // Lines
|
||||
DrawLine(54, 70, 70, 54);
|
||||
|
||||
DrawRect(10, 80, 54, 30);
|
||||
FillRect(10, 80, 54, 30);
|
||||
|
||||
// Multiline Text
|
||||
std::string mpos = "Your Mouse Position is:\nX=" + std::to_string(mx) + "\nY=" + std::to_string(my);
|
||||
DrawString(10, 130, mpos);
|
||||
|
||||
auto AddEvent = [&](std::string s)
|
||||
{
|
||||
listEvents.push_back(s);
|
||||
listEvents.pop_front();
|
||||
};
|
||||
|
||||
if (GetMouse(0).bPressed) AddEvent("Mouse Button 0 Down");
|
||||
if (GetMouse(0).bReleased) AddEvent("Mouse Button 0 Up");
|
||||
if (GetMouse(1).bPressed) AddEvent("Mouse Button 1 Down");
|
||||
if (GetMouse(1).bReleased) AddEvent("Mouse Button 1 Up");
|
||||
if (GetMouse(2).bPressed) AddEvent("Mouse Button 2 Down");
|
||||
if (GetMouse(2).bReleased) AddEvent("Mouse Button 2 Up");
|
||||
|
||||
|
||||
// Draw Event Log
|
||||
int nLog = 0;
|
||||
for (auto &s : listEvents)
|
||||
{
|
||||
DrawString(200, nLog * 8 + 20, s, olc::Pixel(nLog * 16, nLog * 16, nLog * 16));
|
||||
nLog++;
|
||||
}
|
||||
|
||||
std::string notes = "CDEFGAB";
|
||||
|
||||
|
||||
// Test Text scaling and colours
|
||||
DrawString(0, 360, "Text Scale = 1", olc::WHITE, 1);
|
||||
DrawString(0, 368, "Text Scale = 2", olc::BLUE, 2);
|
||||
DrawString(0, 384, "Text Scale = 3", olc::RED, 3);
|
||||
DrawString(0, 408, "Text Scale = 4", olc::YELLOW, 4);
|
||||
DrawString(0, 440, "Text Scale = 5", olc::GREEN, 5);
|
||||
|
||||
fTotalTime += fElapsedTime;
|
||||
|
||||
float fAngle = fTotalTime;
|
||||
|
||||
// Draw Sprite using extension, first create a transformation stack
|
||||
olc::GFX2D::Transform2D t1;
|
||||
|
||||
// Traslate sprite so center of image is at 0,0
|
||||
t1.Translate(-250, -35);
|
||||
// Scale the sprite
|
||||
t1.Scale(1 * sinf(fAngle) + 1, 1 * sinf(fAngle) + 1);
|
||||
// Rotate it
|
||||
t1.Rotate(fAngle*2.0f);
|
||||
// Translate to 0,100
|
||||
t1.Translate(0, 100);
|
||||
// Rotate different speed
|
||||
t1.Rotate(fAngle / 3);
|
||||
// Translate to centre of screen
|
||||
t1.Translate(320, 240);
|
||||
|
||||
SetPixelMode(olc::Pixel::ALPHA);
|
||||
|
||||
// Use extension to draw sprite with transform applied
|
||||
olc::GFX2D::DrawSprite(spr, t1);
|
||||
|
||||
DrawSprite((int32_t)mx, (int32_t)my, spr, 4);
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
TestExtension demo;
|
||||
if (demo.Construct(640, 480, 2, 2))
|
||||
demo.Start();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1,443 +0,0 @@
|
||||
/*
|
||||
Programming Practice: Five Dice
|
||||
"OK, it's getting a bit serious now..." - javidx9
|
||||
|
||||
Video: https://youtu.be/D2a5fHX-Qrs
|
||||
|
||||
License (OLC-3)
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Copyright 2018 - 2021 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.
|
||||
|
||||
Video:
|
||||
~~~~~~
|
||||
https://youtu.be/DDV_2cWT94U
|
||||
|
||||
Links
|
||||
~~~~~
|
||||
YouTube: https://www.youtube.com/javidx9
|
||||
https://www.youtube.com/javidx9extra
|
||||
Discord: https://discord.gg/WhwHUMV
|
||||
Twitter: https://www.twitter.com/javidx9
|
||||
Twitch: https://www.twitch.tv/javidx9
|
||||
GitHub: https://www.github.com/onelonecoder
|
||||
Homepage: https://www.onelonecoder.com
|
||||
|
||||
Author
|
||||
~~~~~~
|
||||
David Barr, aka javidx9, ©OneLoneCoder 2019, 2020, 2021
|
||||
*/
|
||||
|
||||
#define OLC_PGE_APPLICATION
|
||||
#include "olcPixelGameEngine.h"
|
||||
|
||||
#include <set>
|
||||
#include <numeric>
|
||||
|
||||
class FiveDice : public olc::PixelGameEngine
|
||||
{
|
||||
public:
|
||||
FiveDice()
|
||||
{
|
||||
sAppName = "Five Dice";
|
||||
}
|
||||
|
||||
std::vector<uint8_t> vRolled;
|
||||
|
||||
public:
|
||||
void DrawDie(const olc::vi2d& vPos,
|
||||
const uint8_t nFace,
|
||||
const olc::vi2d& vSize = { 64, 64 },
|
||||
const olc::Pixel colFace = olc::DARK_RED,
|
||||
const olc::Pixel colSpot = olc::WHITE)
|
||||
{
|
||||
// Draw Background
|
||||
FillRect(vPos, vSize, colFace);
|
||||
|
||||
int32_t nColL = int32_t(double(vSize.x) * 0.25);
|
||||
int32_t nColM = int32_t(double(vSize.x) * 0.5);
|
||||
int32_t nColR = int32_t(double(vSize.x) * 0.75);
|
||||
int32_t nRowT = int32_t(double(vSize.y) * 0.25);
|
||||
int32_t nRowM = int32_t(double(vSize.y) * 0.5);
|
||||
int32_t nRowB = int32_t(double(vSize.y) * 0.75);
|
||||
int32_t nRad = int32_t(double(nColL) * 0.4);
|
||||
|
||||
/*
|
||||
switch (nFace)
|
||||
{
|
||||
case 1:
|
||||
FillCircle(vPos + olc::vi2d(nColM, nRowM), nRad, colSpot);
|
||||
break;
|
||||
case 2:
|
||||
FillCircle(vPos + olc::vi2d(nColL, nRowT), nRad, colSpot);
|
||||
FillCircle(vPos + olc::vi2d(nColR, nRowB), nRad, colSpot);
|
||||
break;
|
||||
case 3:
|
||||
FillCircle(vPos + olc::vi2d(nColL, nRowT), nRad, colSpot);
|
||||
FillCircle(vPos + olc::vi2d(nColM, nRowM), nRad, colSpot);
|
||||
FillCircle(vPos + olc::vi2d(nColR, nRowB), nRad, colSpot);
|
||||
break;
|
||||
case 4:
|
||||
FillCircle(vPos + olc::vi2d(nColL, nRowT), nRad, colSpot);
|
||||
FillCircle(vPos + olc::vi2d(nColR, nRowB), nRad, colSpot);
|
||||
FillCircle(vPos + olc::vi2d(nColL, nRowB), nRad, colSpot);
|
||||
FillCircle(vPos + olc::vi2d(nColR, nRowT), nRad, colSpot);
|
||||
break;
|
||||
case 5:
|
||||
FillCircle(vPos + olc::vi2d(nColL, nRowT), nRad, colSpot);
|
||||
FillCircle(vPos + olc::vi2d(nColR, nRowB), nRad, colSpot);
|
||||
FillCircle(vPos + olc::vi2d(nColL, nRowB), nRad, colSpot);
|
||||
FillCircle(vPos + olc::vi2d(nColR, nRowT), nRad, colSpot);
|
||||
FillCircle(vPos + olc::vi2d(nColM, nRowM), nRad, colSpot);
|
||||
break;
|
||||
case 6:
|
||||
FillCircle(vPos + olc::vi2d(nColL, nRowT), nRad, colSpot);
|
||||
FillCircle(vPos + olc::vi2d(nColR, nRowB), nRad, colSpot);
|
||||
FillCircle(vPos + olc::vi2d(nColL, nRowB), nRad, colSpot);
|
||||
FillCircle(vPos + olc::vi2d(nColR, nRowT), nRad, colSpot);
|
||||
FillCircle(vPos + olc::vi2d(nColL, nRowM), nRad, colSpot);
|
||||
FillCircle(vPos + olc::vi2d(nColR, nRowM), nRad, colSpot);
|
||||
break;
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
if((std::set<uint8_t>{2, 3, 4, 5, 6}).count(nFace) > 0)
|
||||
FillCircle(vPos + olc::vi2d(nColL, nRowT), nRad, colSpot);
|
||||
if ((std::set<uint8_t>{6}).count(nFace) > 0)
|
||||
FillCircle(vPos + olc::vi2d(nColL, nRowM), nRad, colSpot);
|
||||
if ((std::set<uint8_t>{4, 5, 6}).count(nFace) > 0)
|
||||
FillCircle(vPos + olc::vi2d(nColL, nRowB), nRad, colSpot);
|
||||
|
||||
if ((std::set<uint8_t>{1, 3, 5}).count(nFace) > 0)
|
||||
FillCircle(vPos + olc::vi2d(nColM, nRowM), nRad, colSpot);
|
||||
|
||||
if ((std::set<uint8_t>{4, 5, 6}).count(nFace) > 0)
|
||||
FillCircle(vPos + olc::vi2d(nColR, nRowT), nRad, colSpot);
|
||||
if ((std::set<uint8_t>{6}).count(nFace) > 0)
|
||||
FillCircle(vPos + olc::vi2d(nColR, nRowM), nRad, colSpot);
|
||||
if ((std::set<uint8_t>{2, 3, 4, 5, 6}).count(nFace) > 0)
|
||||
FillCircle(vPos + olc::vi2d(nColR, nRowB), nRad, colSpot);
|
||||
*/
|
||||
|
||||
|
||||
if (nFace & 1)
|
||||
{
|
||||
FillCircle(vPos + olc::vi2d(nColM, nRowM), nRad, colSpot);
|
||||
}
|
||||
|
||||
if (nFace > 1)
|
||||
{
|
||||
FillCircle(vPos + olc::vi2d(nColR, nRowB), nRad, colSpot);
|
||||
FillCircle(vPos + olc::vi2d(nColL, nRowT), nRad, colSpot);
|
||||
}
|
||||
|
||||
if (nFace > 3)
|
||||
{
|
||||
FillCircle(vPos + olc::vi2d(nColR, nRowT), nRad, colSpot);
|
||||
FillCircle(vPos + olc::vi2d(nColL, nRowB), nRad, colSpot);
|
||||
}
|
||||
|
||||
if (nFace == 6)
|
||||
{
|
||||
FillCircle(vPos + olc::vi2d(nColL, nRowM), nRad, colSpot);
|
||||
FillCircle(vPos + olc::vi2d(nColR, nRowM), nRad, colSpot);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
bool OnUserCreate() override
|
||||
{
|
||||
vRolled = { 1, 6, 3, 3, 5 };
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OnUserUpdate(float fElapsedTime) override
|
||||
{
|
||||
|
||||
//if (GetKey(olc::Key::SPACE).bReleased)
|
||||
//{
|
||||
// /*
|
||||
// vRolled =
|
||||
// {
|
||||
// uint8_t(rand() % 6 + 1),
|
||||
// uint8_t(rand() % 6 + 1),
|
||||
// uint8_t(rand() % 6 + 1),
|
||||
// uint8_t(rand() % 6 + 1),
|
||||
// uint8_t(rand() % 6 + 1)
|
||||
// };
|
||||
// */
|
||||
|
||||
// /*
|
||||
// std::transform(
|
||||
// vRolled.begin(),
|
||||
// vRolled.end(),
|
||||
// vRolled.begin(),
|
||||
// [](uint8_t n) { return rand() % 6 + 1; }
|
||||
// );
|
||||
// */
|
||||
|
||||
// std::generate(
|
||||
// vRolled.begin(),
|
||||
// vRolled.end(),
|
||||
// []() { return rand() % 6 + 1; }
|
||||
// );
|
||||
|
||||
// std::sort(vRolled.begin(), vRolled.end());
|
||||
//}
|
||||
|
||||
|
||||
/*
|
||||
int nScore_AllDice = std::accumulate(vRolled.begin(), vRolled.end(), 0);
|
||||
|
||||
int nScore_CountOnes = std::count(vRolled.begin(), vRolled.end(), 1) * 1;
|
||||
int nScore_CountTwos = std::count(vRolled.begin(), vRolled.end(), 2) * 2;
|
||||
int nScore_CountThrees = std::count(vRolled.begin(), vRolled.end(), 3) * 3;
|
||||
int nScore_CountFours = std::count(vRolled.begin(), vRolled.end(), 4) * 4;
|
||||
int nScore_CountFives = std::count(vRolled.begin(), vRolled.end(), 5) * 5;
|
||||
int nScore_CountSixes = std::count(vRolled.begin(), vRolled.end(), 6) * 6;
|
||||
|
||||
int nScore_ThreeOfAKind = 0;
|
||||
int nScore_FourOfAKind = 0;
|
||||
int nScore_FiveOfAKind = 0;
|
||||
int nScore_SmallStraight = 0;
|
||||
int nScore_LargeStraight = 0;
|
||||
int nScore_FullHouse = 0;
|
||||
*/
|
||||
|
||||
/*
|
||||
auto PatternMatch = [&](const std::vector<uint8_t> vDice, const std::string& sPattern) -> bool
|
||||
{
|
||||
// nnnnn - Yahtzee
|
||||
// nnnn?, ?nnnn - four of a kind
|
||||
// nnn??, ?nnn?, ??nnn - three of a kind
|
||||
// 1234?, ?2345, 2345?, ?3456 - Small Straight
|
||||
// 12345, 23456 - Large Straight
|
||||
// nnn?? & ???nn, nn??? & ??nnn - Full House
|
||||
|
||||
bool bMatch = true;
|
||||
uint8_t n = 0;
|
||||
|
||||
for (size_t idx = 0; idx < 5; idx++)
|
||||
{
|
||||
if (sPattern[idx] == 'n')
|
||||
{
|
||||
if (n == 0)
|
||||
{
|
||||
n = vDice[idx];
|
||||
}
|
||||
else
|
||||
{
|
||||
bMatch &= (vDice[idx] == n);
|
||||
}
|
||||
}
|
||||
else if (sPattern[idx] == '?')
|
||||
{
|
||||
bMatch &= true;
|
||||
}
|
||||
else // is Face Value
|
||||
{
|
||||
bMatch &= ((sPattern[idx] - '0') == vDice[idx]);
|
||||
}
|
||||
|
||||
}
|
||||
return bMatch;
|
||||
};
|
||||
|
||||
|
||||
// nnnnn - Yahtzee
|
||||
if (PatternMatch(vRolled, "nnnnn"))
|
||||
{
|
||||
nScore_FiveOfAKind = 50;
|
||||
}
|
||||
|
||||
// nnnn?, ?nnnn - four of a kind
|
||||
if (PatternMatch(vRolled, "nnnn?") || PatternMatch(vRolled, "?nnnn"))
|
||||
{
|
||||
nScore_FourOfAKind = 4 * vRolled[2];
|
||||
}
|
||||
|
||||
// nnn??, ?nnn?, ??nnn - three of a kind
|
||||
if (PatternMatch(vRolled, "nnn??") || PatternMatch(vRolled, "?nnn?") || PatternMatch(vRolled, "??nnn"))
|
||||
{
|
||||
nScore_ThreeOfAKind = 3 * vRolled[2];
|
||||
}
|
||||
|
||||
// 1234?, ?2345, 2345?, ?3456 - Small Straight
|
||||
if (PatternMatch(vRolled, "1234?") || PatternMatch(vRolled, "2345?") || PatternMatch(vRolled, "?3456") || PatternMatch(vRolled, "?2345"))
|
||||
{
|
||||
nScore_SmallStraight = 30;
|
||||
}
|
||||
|
||||
// 12345, 23456 - Large Straight
|
||||
if (PatternMatch(vRolled, "12345") || PatternMatch(vRolled, "23456"))
|
||||
{
|
||||
nScore_LargeStraight = 40;
|
||||
}
|
||||
|
||||
// nnn?? & ???nn, nn??? & ??nnn - Full House
|
||||
if ((PatternMatch(vRolled, "nnn??") && PatternMatch(vRolled, "???nn")) || (PatternMatch(vRolled, "??nnn") && PatternMatch(vRolled, "nn???")))
|
||||
{
|
||||
nScore_FullHouse = 25;
|
||||
}
|
||||
|
||||
std::vector<std::pair<std::string, int>> vScores =
|
||||
{
|
||||
{"Total Ones : ", std::count(vRolled.begin(), vRolled.end(), 1) * 1},
|
||||
{"Total Twos : ", std::count(vRolled.begin(), vRolled.end(), 2) * 2},
|
||||
{"Total Threes : ", std::count(vRolled.begin(), vRolled.end(), 3) * 3},
|
||||
{"Total Fours : ", std::count(vRolled.begin(), vRolled.end(), 4) * 4},
|
||||
{"Total Fives : ", std::count(vRolled.begin(), vRolled.end(), 5) * 5},
|
||||
{"Total Sixes : ", std::count(vRolled.begin(), vRolled.end(), 6) * 6},
|
||||
{"Chance : ", std::accumulate(vRolled.begin(), vRolled.end(), 0)},
|
||||
|
||||
{"Three Of A Kind : ", (PatternMatch(vRolled, "nnn??") || PatternMatch(vRolled, "?nnn?") || PatternMatch(vRolled, "??nnn")) ? (std::count(vRolled.begin(), vRolled.end(), vRolled[2]) * vRolled[2]) : 0},
|
||||
{"Four Of A Kind : ", (PatternMatch(vRolled, "nnnn?") || PatternMatch(vRolled, "?nnnn")) ? (std::count(vRolled.begin(), vRolled.end(), vRolled[2]) * vRolled[2]) : 0},
|
||||
{"Full House : ", ((PatternMatch(vRolled, "nnn??") && PatternMatch(vRolled, "???nn")) || (PatternMatch(vRolled, "??nnn") && PatternMatch(vRolled, "nn???"))) ? 25 : 0},
|
||||
{"Small Straight : ", (PatternMatch(vRolled, "1234?") || PatternMatch(vRolled, "2345?") || PatternMatch(vRolled, "?3456") || PatternMatch(vRolled, "?2345")) ? 30 : 0},
|
||||
{"Large Straight : ", (PatternMatch(vRolled, "12345") || PatternMatch(vRolled, "23456")) ? 40 : 0},
|
||||
{"Five Of A Kind : ", (PatternMatch(vRolled, "nnnnn")) ? 50 : 0},
|
||||
};
|
||||
|
||||
*/
|
||||
|
||||
auto Match = [&vRolled = vRolled](const std::vector<std::string>& vPatterns) -> bool
|
||||
{
|
||||
// nnnnn - Yahtzee
|
||||
// nnnn?, ?nnnn - four of a kind
|
||||
// nnn??, ?nnn?, ??nnn - three of a kind
|
||||
// 1234?, ?2345, 2345?, ?3456 - Small Straight
|
||||
// 12345, 23456 - Large Straight
|
||||
// nnn?? & ???nn, nn??? & ??nnn - Full House
|
||||
|
||||
bool bResult = false;
|
||||
|
||||
for (const auto& sPattern : vPatterns)
|
||||
{
|
||||
|
||||
bool bMatch = true;
|
||||
uint8_t n = 0;
|
||||
|
||||
for (size_t idx = 0; idx < 5; idx++)
|
||||
{
|
||||
if (sPattern[idx] == 'n')
|
||||
{
|
||||
if (n == 0)
|
||||
{
|
||||
n = vRolled[idx];
|
||||
}
|
||||
else
|
||||
{
|
||||
bMatch &= (vRolled[idx] == n);
|
||||
}
|
||||
}
|
||||
else if (sPattern[idx] == '?')
|
||||
{
|
||||
bMatch &= true;
|
||||
}
|
||||
else // is Face Value
|
||||
{
|
||||
bMatch &= ((sPattern[idx] - '0') == vRolled[idx]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bResult |= bMatch;
|
||||
}
|
||||
return bResult;
|
||||
};
|
||||
|
||||
/*DrawDie({ 10, 10 }, vRolled[0]);
|
||||
DrawDie({ 80, 10 }, vRolled[1]);
|
||||
DrawDie({ 150, 10 }, vRolled[2]);
|
||||
DrawDie({ 220, 10 }, vRolled[3]);
|
||||
DrawDie({ 290, 10 }, vRolled[4]);
|
||||
|
||||
int nOffsetY = 100;
|
||||
DrawString(10, nOffsetY += 10, "Total Ones : " + std::to_string(nScore_CountOnes));
|
||||
DrawString(10, nOffsetY += 10, "Total Twos : " + std::to_string(nScore_CountTwos));
|
||||
DrawString(10, nOffsetY += 10, "Total Threes : " + std::to_string(nScore_CountThrees));
|
||||
DrawString(10, nOffsetY += 10, "Total Fours : " + std::to_string(nScore_CountFours));
|
||||
DrawString(10, nOffsetY += 10, "Total Fives : " + std::to_string(nScore_CountFives));
|
||||
DrawString(10, nOffsetY += 10, "Total Sixes : " + std::to_string(nScore_CountSixes));
|
||||
DrawString(10, nOffsetY += 10, "Three Of A Kind : " + std::to_string(nScore_ThreeOfAKind));
|
||||
DrawString(10, nOffsetY += 10, "Four Of A Kind : " + std::to_string(nScore_FourOfAKind));
|
||||
DrawString(10, nOffsetY += 10, "Full House : " + std::to_string(nScore_FullHouse));
|
||||
DrawString(10, nOffsetY += 10, "Small Straight : " + std::to_string(nScore_SmallStraight));
|
||||
DrawString(10, nOffsetY += 10, "Large Straight : " + std::to_string(nScore_LargeStraight));
|
||||
DrawString(10, nOffsetY += 10, "Five Of A Kind : " + std::to_string(nScore_FiveOfAKind));
|
||||
DrawString(10, nOffsetY += 10, "Chance : " + std::to_string(nScore_AllDice));*/
|
||||
|
||||
if (GetKey(olc::Key::SPACE).bReleased)
|
||||
{
|
||||
std::generate(vRolled.begin(), vRolled.end(), []() { return rand() % 6 + 1; });
|
||||
std::sort(vRolled.begin(), vRolled.end());
|
||||
}
|
||||
|
||||
std::vector<std::pair<std::string, int>> vScores =
|
||||
{
|
||||
{"Total Ones : ", std::count(vRolled.begin(), vRolled.end(), 1) * 1},
|
||||
{"Total Twos : ", std::count(vRolled.begin(), vRolled.end(), 2) * 2},
|
||||
{"Total Threes : ", std::count(vRolled.begin(), vRolled.end(), 3) * 3},
|
||||
{"Total Fours : ", std::count(vRolled.begin(), vRolled.end(), 4) * 4},
|
||||
{"Total Fives : ", std::count(vRolled.begin(), vRolled.end(), 5) * 5},
|
||||
{"Total Sixes : ", std::count(vRolled.begin(), vRolled.end(), 6) * 6},
|
||||
{"Chance : ", std::accumulate(vRolled.begin(), vRolled.end(), 0)},
|
||||
|
||||
{"Three Of A Kind : ", (Match({"nnn??", "?nnn?", "??nnn"})) ? (3 * vRolled[2]) : 0},
|
||||
{"Four Of A Kind : ", (Match({"nnnn?", "?nnnn"})) ? (4 * vRolled[2]) : 0},
|
||||
{"Full House : ", ((Match({"nnn??"}) && Match({"???nn"})) || (Match({"??nnn"}) && Match({"nn???"}))) ? 25 : 0},
|
||||
{"Small Straight : ", (Match({"1234?", "2345?", "?3456", "?2345"})) ? 30 : 0},
|
||||
{"Large Straight : ", (Match({"12345", "23456"})) ? 40 : 0},
|
||||
{"Five Of A Kind : ", (Match({"nnnnn"})) ? 50 : 0},
|
||||
};
|
||||
|
||||
|
||||
Clear(olc::DARK_GREEN);
|
||||
olc::vi2d vOffset = { -60, 90 };
|
||||
for (int i = 0; i < 5; i++) DrawDie({ vOffset.x += 70, 10 }, vRolled[i]);
|
||||
for (const auto& score : vScores) DrawString(10, vOffset.y += 10, score.first + std::to_string(score.second));
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
FiveDice demo;
|
||||
if (demo.Construct(640, 480, 2, 2))
|
||||
demo.Start();
|
||||
return 0;
|
||||
}
|
||||
@ -1,214 +0,0 @@
|
||||
/*
|
||||
Coding Quickie: Isometric Tiles
|
||||
"Owww... My insides hurt :(" - javidx9
|
||||
|
||||
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.
|
||||
|
||||
Relevant Video: https://youtu.be/ukkbNKTgf5U
|
||||
|
||||
Links
|
||||
~~~~~
|
||||
YouTube: https://www.youtube.com/javidx9
|
||||
https://www.youtube.com/javidx9extra
|
||||
Discord: https://discord.gg/WhwHUMV
|
||||
Twitter: https://www.twitter.com/javidx9
|
||||
Twitch: https://www.twitch.tv/javidx9
|
||||
GitHub: https://www.github.com/onelonecoder
|
||||
Patreon: https://www.patreon.com/javidx9
|
||||
Homepage: https://www.onelonecoder.com
|
||||
|
||||
Author
|
||||
~~~~~~
|
||||
David Barr, aka javidx9, ©OneLoneCoder 2019
|
||||
*/
|
||||
|
||||
#define OLC_PGE_APPLICATION
|
||||
#include "olcPixelGameEngine.h"
|
||||
|
||||
// Override base class with your custom functionality
|
||||
class IsometricDemo : public olc::PixelGameEngine
|
||||
{
|
||||
public:
|
||||
IsometricDemo()
|
||||
{
|
||||
sAppName = "Coding Quickie: Isometric Tiles";
|
||||
}
|
||||
|
||||
private:
|
||||
// Number of tiles in world
|
||||
olc::vi2d vWorldSize = { 14, 10 };
|
||||
|
||||
// Size of single tile graphic
|
||||
olc::vi2d vTileSize = { 40, 20 };
|
||||
|
||||
// Where to place tile (0,0) on screen (in tile size steps)
|
||||
olc::vi2d vOrigin = { 5, 1 };
|
||||
|
||||
// Sprite that holds all imagery
|
||||
olc::Sprite *sprIsom = nullptr;
|
||||
|
||||
// Pointer to create 2D world array
|
||||
int *pWorld = nullptr;
|
||||
|
||||
public:
|
||||
bool OnUserCreate() override
|
||||
{
|
||||
// Load sprites used in demonstration
|
||||
sprIsom = new olc::Sprite("isometric_demo.png");
|
||||
|
||||
// Create empty world
|
||||
pWorld = new int[vWorldSize.x * vWorldSize.y]{ 0 };
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OnUserUpdate(float fElapsedTime) override
|
||||
{
|
||||
Clear(olc::WHITE);
|
||||
|
||||
// Get Mouse in world
|
||||
olc::vi2d vMouse = { GetMouseX(), GetMouseY() };
|
||||
|
||||
// Work out active cell
|
||||
olc::vi2d vCell = { vMouse.x / vTileSize.x, vMouse.y / vTileSize.y };
|
||||
|
||||
// Work out mouse offset into cell
|
||||
olc::vi2d vOffset = { vMouse.x % vTileSize.x, vMouse.y % vTileSize.y };
|
||||
|
||||
// Sample into cell offset colour
|
||||
olc::Pixel col = sprIsom->GetPixel(3 * vTileSize.x + vOffset.x, vOffset.y);
|
||||
|
||||
// Work out selected cell by transforming screen cell
|
||||
olc::vi2d vSelected =
|
||||
{
|
||||
(vCell.y - vOrigin.y) + (vCell.x - vOrigin.x),
|
||||
(vCell.y - vOrigin.y) - (vCell.x - vOrigin.x)
|
||||
};
|
||||
|
||||
// "Bodge" selected cell by sampling corners
|
||||
if (col == olc::RED) vSelected += {-1, +0};
|
||||
if (col == olc::BLUE) vSelected += {+0, -1};
|
||||
if (col == olc::GREEN) vSelected += {+0, +1};
|
||||
if (col == olc::YELLOW) vSelected += {+1, +0};
|
||||
|
||||
// Handle mouse click to toggle if a tile is visible or not
|
||||
if (GetMouse(0).bPressed)
|
||||
{
|
||||
// Guard array boundary
|
||||
if (vSelected.x >= 0 && vSelected.x < vWorldSize.x && vSelected.y >= 0 && vSelected.y < vWorldSize.y)
|
||||
++pWorld[vSelected.y * vWorldSize.x + vSelected.x] %= 6;
|
||||
}
|
||||
|
||||
// Labmda function to convert "world" coordinate into screen space
|
||||
auto ToScreen = [&](int x, int y)
|
||||
{
|
||||
return olc::vi2d
|
||||
{
|
||||
(vOrigin.x * vTileSize.x) + (x - y) * (vTileSize.x / 2),
|
||||
(vOrigin.y * vTileSize.y) + (x + y) * (vTileSize.y / 2)
|
||||
};
|
||||
};
|
||||
|
||||
// Draw World - has binary transparancy so enable masking
|
||||
SetPixelMode(olc::Pixel::MASK);
|
||||
|
||||
// (0,0) is at top, defined by vOrigin, so draw from top to bottom
|
||||
// to ensure tiles closest to camera are drawn last
|
||||
for (int y = 0; y < vWorldSize.y; y++)
|
||||
{
|
||||
for (int x = 0; x < vWorldSize.x; x++)
|
||||
{
|
||||
// Convert cell coordinate to world space
|
||||
olc::vi2d vWorld = ToScreen(x, y);
|
||||
|
||||
switch(pWorld[y*vWorldSize.x + x])
|
||||
{
|
||||
case 0:
|
||||
// Invisble Tile
|
||||
DrawPartialSprite(vWorld.x, vWorld.y, sprIsom, 1 * vTileSize.x, 0, vTileSize.x, vTileSize.y);
|
||||
break;
|
||||
case 1:
|
||||
// Visible Tile
|
||||
DrawPartialSprite(vWorld.x, vWorld.y, sprIsom, 2 * vTileSize.x, 0, vTileSize.x, vTileSize.y);
|
||||
break;
|
||||
case 2:
|
||||
// Tree
|
||||
DrawPartialSprite(vWorld.x, vWorld.y - vTileSize.y, sprIsom, 0 * vTileSize.x, 1 * vTileSize.y, vTileSize.x, vTileSize.y * 2);
|
||||
break;
|
||||
case 3:
|
||||
// Spooky Tree
|
||||
DrawPartialSprite(vWorld.x, vWorld.y - vTileSize.y, sprIsom, 1 * vTileSize.x, 1 * vTileSize.y, vTileSize.x, vTileSize.y * 2);
|
||||
break;
|
||||
case 4:
|
||||
// Beach
|
||||
DrawPartialSprite(vWorld.x, vWorld.y - vTileSize.y, sprIsom, 2 * vTileSize.x, 1 * vTileSize.y, vTileSize.x, vTileSize.y * 2);
|
||||
break;
|
||||
case 5:
|
||||
// Water
|
||||
DrawPartialSprite(vWorld.x, vWorld.y - vTileSize.y, sprIsom, 3 * vTileSize.x, 1 * vTileSize.y, vTileSize.x, vTileSize.y * 2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draw Selected Cell - Has varying alpha components
|
||||
SetPixelMode(olc::Pixel::ALPHA);
|
||||
|
||||
// Convert selected cell coordinate to world space
|
||||
olc::vi2d vSelectedWorld = ToScreen(vSelected.x, vSelected.y);
|
||||
|
||||
// Draw "highlight" tile
|
||||
DrawPartialSprite(vSelectedWorld.x, vSelectedWorld.y, sprIsom, 0 * vTileSize.x, 0, vTileSize.x, vTileSize.y);
|
||||
|
||||
// Go back to normal drawing with no expected transparency
|
||||
SetPixelMode(olc::Pixel::NORMAL);
|
||||
|
||||
// Draw Hovered Cell Boundary
|
||||
//DrawRect(vCell.x * vTileSize.x, vCell.y * vTileSize.y, vTileSize.x, vTileSize.y, olc::RED);
|
||||
|
||||
// Draw Debug Info
|
||||
DrawString(4, 4, "Mouse : " + std::to_string(vMouse.x) + ", " + std::to_string(vMouse.y), olc::BLACK);
|
||||
DrawString(4, 14, "Cell : " + std::to_string(vCell.x) + ", " + std::to_string(vCell.y), olc::BLACK);
|
||||
DrawString(4, 24, "Selected: " + std::to_string(vSelected.x) + ", " + std::to_string(vSelected.y), olc::BLACK);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
IsometricDemo demo;
|
||||
if (demo.Construct(512, 480, 2, 2))
|
||||
demo.Start();
|
||||
return 0;
|
||||
}
|
||||
@ -1,626 +0,0 @@
|
||||
/*
|
||||
Programming MIDI: Parsing, Displaying (& Playing) MIDI Files
|
||||
"Better get these done before im virused..." - javidx9
|
||||
|
||||
License (OLC-3)
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Copyright 2018-2020 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.
|
||||
|
||||
Relevant Video: https://youtu.be/040BKtnDdg0
|
||||
|
||||
Links
|
||||
~~~~~
|
||||
YouTube: https://www.youtube.com/javidx9
|
||||
https://www.youtube.com/javidx9extra
|
||||
Discord: https://discord.gg/WhwHUMV
|
||||
Twitter: https://www.twitter.com/javidx9
|
||||
Twitch: https://www.twitch.tv/javidx9
|
||||
GitHub: https://www.github.com/onelonecoder
|
||||
Patreon: https://www.patreon.com/javidx9
|
||||
Homepage: https://www.onelonecoder.com
|
||||
|
||||
Community: https://community.onelonecoder.com
|
||||
|
||||
Author
|
||||
~~~~~~
|
||||
David Barr, aka javidx9, ©OneLoneCoder 2018, 2019, 2020
|
||||
*/
|
||||
|
||||
|
||||
#define OLC_PGE_APPLICATION
|
||||
#include "olcPixelGameEngine.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <array>
|
||||
|
||||
//#pragma comment(lib, "winmm.lib")
|
||||
|
||||
|
||||
struct MidiEvent
|
||||
{
|
||||
enum class Type
|
||||
{
|
||||
NoteOff,
|
||||
NoteOn,
|
||||
Other
|
||||
} event;
|
||||
|
||||
uint8_t nKey = 0;
|
||||
uint8_t nVelocity = 0;
|
||||
uint32_t nDeltaTick = 0;
|
||||
};
|
||||
|
||||
|
||||
struct MidiNote
|
||||
{
|
||||
uint8_t nKey = 0;
|
||||
uint8_t nVelocity = 0;
|
||||
uint32_t nStartTime = 0;
|
||||
uint32_t nDuration = 0;
|
||||
};
|
||||
|
||||
struct MidiTrack
|
||||
{
|
||||
std::string sName;
|
||||
std::string sInstrument;
|
||||
std::vector<MidiEvent> vecEvents;
|
||||
std::vector<MidiNote> vecNotes;
|
||||
uint8_t nMaxNote = 64;
|
||||
uint8_t nMinNote = 64;
|
||||
};
|
||||
|
||||
|
||||
class MidiFile
|
||||
{
|
||||
public:
|
||||
enum EventName : uint8_t
|
||||
{
|
||||
VoiceNoteOff = 0x80,
|
||||
VoiceNoteOn = 0x90,
|
||||
VoiceAftertouch = 0xA0,
|
||||
VoiceControlChange = 0xB0,
|
||||
VoiceProgramChange = 0xC0,
|
||||
VoiceChannelPressure = 0xD0,
|
||||
VoicePitchBend = 0xE0,
|
||||
SystemExclusive = 0xF0,
|
||||
};
|
||||
|
||||
enum MetaEventName : uint8_t
|
||||
{
|
||||
MetaSequence = 0x00,
|
||||
MetaText = 0x01,
|
||||
MetaCopyright = 0x02,
|
||||
MetaTrackName = 0x03,
|
||||
MetaInstrumentName = 0x04,
|
||||
MetaLyrics = 0x05,
|
||||
MetaMarker = 0x06,
|
||||
MetaCuePoint = 0x07,
|
||||
MetaChannelPrefix = 0x20,
|
||||
MetaEndOfTrack = 0x2F,
|
||||
MetaSetTempo = 0x51,
|
||||
MetaSMPTEOffset = 0x54,
|
||||
MetaTimeSignature = 0x58,
|
||||
MetaKeySignature = 0x59,
|
||||
MetaSequencerSpecific = 0x7F,
|
||||
};
|
||||
|
||||
public:
|
||||
MidiFile()
|
||||
{
|
||||
}
|
||||
|
||||
MidiFile(const std::string& sFileName)
|
||||
{
|
||||
ParseFile(sFileName);
|
||||
}
|
||||
|
||||
void Clear()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool ParseFile(const std::string& sFileName)
|
||||
{
|
||||
// Open the MIDI File as a stream
|
||||
std::ifstream ifs;
|
||||
ifs.open(sFileName, std::fstream::in | std::ios::binary);
|
||||
if (!ifs.is_open())
|
||||
return false;
|
||||
|
||||
|
||||
// Helper Utilities ====================
|
||||
|
||||
// Swaps byte order of 32-bit integer
|
||||
auto Swap32 = [](uint32_t n)
|
||||
{
|
||||
return (((n >> 24) & 0xff) | ((n << 8) & 0xff0000) | ((n >> 8) & 0xff00) | ((n << 24) & 0xff000000));
|
||||
};
|
||||
|
||||
// Swaps byte order of 16-bit integer
|
||||
auto Swap16 = [](uint16_t n)
|
||||
{
|
||||
return ((n >> 8) | (n << 8));
|
||||
};
|
||||
|
||||
// Reads nLength bytes form file stream, and constructs a text string
|
||||
auto ReadString = [&ifs](uint32_t nLength)
|
||||
{
|
||||
std::string s;
|
||||
for (uint32_t i = 0; i < nLength; i++) s += ifs.get();
|
||||
return s;
|
||||
};
|
||||
|
||||
// Reads a compressed MIDI value. This can be up to 32 bits long. Essentially if the first byte, first
|
||||
// bit is set to 1, that indicates that the next byte is required to construct the full word. Only
|
||||
// the bottom 7 bits of each byte are used to construct the final word value. Each successive byte
|
||||
// that has MSB set, indicates a further byte needs to be read.
|
||||
auto ReadValue = [&ifs]()
|
||||
{
|
||||
uint32_t nValue = 0;
|
||||
uint8_t nByte = 0;
|
||||
|
||||
// Read byte
|
||||
nValue = ifs.get();
|
||||
|
||||
// Check MSB, if set, more bytes need reading
|
||||
if (nValue & 0x80)
|
||||
{
|
||||
// Extract bottom 7 bits of read byte
|
||||
nValue &= 0x7F;
|
||||
do
|
||||
{
|
||||
// Read next byte
|
||||
nByte = ifs.get();
|
||||
|
||||
// Construct value by setting bottom 7 bits, then shifting 7 bits
|
||||
nValue = (nValue << 7) | (nByte & 0x7F);
|
||||
}
|
||||
while (nByte & 0x80); // Loop whilst read byte MSB is 1
|
||||
}
|
||||
|
||||
// Return final construction (always 32-bit unsigned integer internally)
|
||||
return nValue;
|
||||
};
|
||||
|
||||
uint32_t n32 = 0;
|
||||
uint16_t n16 = 0;
|
||||
|
||||
// Read MIDI Header (Fixed Size)
|
||||
ifs.read((char*)&n32, sizeof(uint32_t));
|
||||
uint32_t nFileID = Swap32(n32);
|
||||
ifs.read((char*)&n32, sizeof(uint32_t));
|
||||
uint32_t nHeaderLength = Swap32(n32);
|
||||
ifs.read((char*)&n16, sizeof(uint16_t));
|
||||
uint16_t nFormat = Swap16(n16);
|
||||
ifs.read((char*)&n16, sizeof(uint16_t));
|
||||
uint16_t nTrackChunks = Swap16(n16);
|
||||
ifs.read((char*)&n16, sizeof(uint16_t));
|
||||
uint16_t nDivision = Swap16(n16);
|
||||
|
||||
for (uint16_t nChunk = 0; nChunk < nTrackChunks; nChunk++)
|
||||
{
|
||||
std::cout << "===== NEW TRACK" << std::endl;
|
||||
// Read Track Header
|
||||
ifs.read((char*)&n32, sizeof(uint32_t));
|
||||
uint32_t nTrackID = Swap32(n32);
|
||||
ifs.read((char*)&n32, sizeof(uint32_t));
|
||||
uint32_t nTrackLength = Swap32(n32);
|
||||
|
||||
bool bEndOfTrack = false;
|
||||
|
||||
vecTracks.push_back(MidiTrack());
|
||||
|
||||
uint32_t nWallTime = 0;
|
||||
|
||||
uint8_t nPreviousStatus = 0;
|
||||
|
||||
while (!ifs.eof() && !bEndOfTrack)
|
||||
{
|
||||
// Fundamentally all MIDI Events contain a timecode, and a status byte*
|
||||
uint32_t nStatusTimeDelta = 0;
|
||||
uint8_t nStatus = 0;
|
||||
|
||||
|
||||
// Read Timecode from MIDI stream. This could be variable in length
|
||||
// and is the delta in "ticks" from the previous event. Of course this value
|
||||
// could be 0 if two events happen simultaneously.
|
||||
nStatusTimeDelta = ReadValue();
|
||||
|
||||
// Read first byte of message, this could be the status byte, or it could not...
|
||||
nStatus = ifs.get();
|
||||
|
||||
// All MIDI Status events have the MSB set. The data within a standard MIDI event
|
||||
// does not. A crude yet utilised form of compression is to omit sending status
|
||||
// bytes if the following sequence of events all refer to the same MIDI Status.
|
||||
// This is called MIDI Running Status, and is essential to succesful decoding of
|
||||
// MIDI streams and files.
|
||||
//
|
||||
// If the MSB of the read byte was not set, and on the whole we were expecting a
|
||||
// status byte, then Running Status is in effect, so we refer to the previous
|
||||
// confirmed status byte.
|
||||
if (nStatus < 0x80)
|
||||
{
|
||||
// MIDI Running Status is happening, so refer to previous valid MIDI Status byte
|
||||
nStatus = nPreviousStatus;
|
||||
|
||||
// We had to read the byte to assess if MIDI Running Status is in effect. But!
|
||||
// that read removed the byte form the stream, and that will desync all of the
|
||||
// following code because normally we would have read a status byte, but instead
|
||||
// we have read the data contained within a MIDI message. The simple solution is
|
||||
// to put the byte back :P
|
||||
ifs.seekg(-1, std::ios_base::cur);
|
||||
}
|
||||
|
||||
|
||||
|
||||
if ((nStatus & 0xF0) == EventName::VoiceNoteOff)
|
||||
{
|
||||
nPreviousStatus = nStatus;
|
||||
uint8_t nChannel = nStatus & 0x0F;
|
||||
uint8_t nNoteID = ifs.get();
|
||||
uint8_t nNoteVelocity = ifs.get();
|
||||
vecTracks[nChunk].vecEvents.push_back({ MidiEvent::Type::NoteOff, nNoteID, nNoteVelocity, nStatusTimeDelta });
|
||||
}
|
||||
|
||||
else if ((nStatus & 0xF0) == EventName::VoiceNoteOn)
|
||||
{
|
||||
nPreviousStatus = nStatus;
|
||||
uint8_t nChannel = nStatus & 0x0F;
|
||||
uint8_t nNoteID = ifs.get();
|
||||
uint8_t nNoteVelocity = ifs.get();
|
||||
if(nNoteVelocity == 0)
|
||||
vecTracks[nChunk].vecEvents.push_back({ MidiEvent::Type::NoteOff, nNoteID, nNoteVelocity, nStatusTimeDelta });
|
||||
else
|
||||
vecTracks[nChunk].vecEvents.push_back({ MidiEvent::Type::NoteOn, nNoteID, nNoteVelocity, nStatusTimeDelta });
|
||||
}
|
||||
|
||||
else if ((nStatus & 0xF0) == EventName::VoiceAftertouch)
|
||||
{
|
||||
nPreviousStatus = nStatus;
|
||||
uint8_t nChannel = nStatus & 0x0F;
|
||||
uint8_t nNoteID = ifs.get();
|
||||
uint8_t nNoteVelocity = ifs.get();
|
||||
vecTracks[nChunk].vecEvents.push_back({ MidiEvent::Type::Other });
|
||||
}
|
||||
|
||||
else if ((nStatus & 0xF0) == EventName::VoiceControlChange)
|
||||
{
|
||||
nPreviousStatus = nStatus;
|
||||
uint8_t nChannel = nStatus & 0x0F;
|
||||
uint8_t nControlID = ifs.get();
|
||||
uint8_t nControlValue = ifs.get();
|
||||
vecTracks[nChunk].vecEvents.push_back({ MidiEvent::Type::Other });
|
||||
}
|
||||
|
||||
else if ((nStatus & 0xF0) == EventName::VoiceProgramChange)
|
||||
{
|
||||
nPreviousStatus = nStatus;
|
||||
uint8_t nChannel = nStatus & 0x0F;
|
||||
uint8_t nProgramID = ifs.get();
|
||||
vecTracks[nChunk].vecEvents.push_back({ MidiEvent::Type::Other });
|
||||
}
|
||||
|
||||
else if ((nStatus & 0xF0) == EventName::VoiceChannelPressure)
|
||||
{
|
||||
nPreviousStatus = nStatus;
|
||||
uint8_t nChannel = nStatus & 0x0F;
|
||||
uint8_t nChannelPressure = ifs.get();
|
||||
vecTracks[nChunk].vecEvents.push_back({ MidiEvent::Type::Other });
|
||||
}
|
||||
|
||||
else if ((nStatus & 0xF0) == EventName::VoicePitchBend)
|
||||
{
|
||||
nPreviousStatus = nStatus;
|
||||
uint8_t nChannel = nStatus & 0x0F;
|
||||
uint8_t nLS7B = ifs.get();
|
||||
uint8_t nMS7B = ifs.get();
|
||||
vecTracks[nChunk].vecEvents.push_back({ MidiEvent::Type::Other });
|
||||
|
||||
}
|
||||
|
||||
else if ((nStatus & 0xF0) == EventName::SystemExclusive)
|
||||
{
|
||||
nPreviousStatus = 0;
|
||||
|
||||
if (nStatus == 0xFF)
|
||||
{
|
||||
// Meta Message
|
||||
uint8_t nType = ifs.get();
|
||||
uint8_t nLength = ReadValue();
|
||||
|
||||
switch (nType)
|
||||
{
|
||||
case MetaSequence:
|
||||
std::cout << "Sequence Number: " << ifs.get() << ifs.get() << std::endl;
|
||||
break;
|
||||
case MetaText:
|
||||
std::cout << "Text: " << ReadString(nLength) << std::endl;
|
||||
break;
|
||||
case MetaCopyright:
|
||||
std::cout << "Copyright: " << ReadString(nLength) << std::endl;
|
||||
break;
|
||||
case MetaTrackName:
|
||||
vecTracks[nChunk].sName = ReadString(nLength);
|
||||
std::cout << "Track Name: " << vecTracks[nChunk].sName << std::endl;
|
||||
break;
|
||||
case MetaInstrumentName:
|
||||
vecTracks[nChunk].sInstrument = ReadString(nLength);
|
||||
std::cout << "Instrument Name: " << vecTracks[nChunk].sInstrument << std::endl;
|
||||
break;
|
||||
case MetaLyrics:
|
||||
std::cout << "Lyrics: " << ReadString(nLength) << std::endl;
|
||||
break;
|
||||
case MetaMarker:
|
||||
std::cout << "Marker: " << ReadString(nLength) << std::endl;
|
||||
break;
|
||||
case MetaCuePoint:
|
||||
std::cout << "Cue: " << ReadString(nLength) << std::endl;
|
||||
break;
|
||||
case MetaChannelPrefix:
|
||||
std::cout << "Prefix: " << ifs.get() << std::endl;
|
||||
break;
|
||||
case MetaEndOfTrack:
|
||||
bEndOfTrack = true;
|
||||
break;
|
||||
case MetaSetTempo:
|
||||
// Tempo is in microseconds per quarter note
|
||||
if (m_nTempo == 0)
|
||||
{
|
||||
(m_nTempo |= (ifs.get() << 16));
|
||||
(m_nTempo |= (ifs.get() << 8));
|
||||
(m_nTempo |= (ifs.get() << 0));
|
||||
m_nBPM = (60000000 / m_nTempo);
|
||||
std::cout << "Tempo: " << m_nTempo << " (" << m_nBPM << "bpm)" << std::endl;
|
||||
}
|
||||
break;
|
||||
case MetaSMPTEOffset:
|
||||
std::cout << "SMPTE: H:" << ifs.get() << " M:" << ifs.get() << " S:" << ifs.get() << " FR:" << ifs.get() << " FF:" << ifs.get() << std::endl;
|
||||
break;
|
||||
case MetaTimeSignature:
|
||||
std::cout << "Time Signature: " << ifs.get() << "/" << (2 << ifs.get()) << std::endl;
|
||||
std::cout << "ClocksPerTick: " << ifs.get() << std::endl;
|
||||
|
||||
// A MIDI "Beat" is 24 ticks, so specify how many 32nd notes constitute a beat
|
||||
std::cout << "32per24Clocks: " << ifs.get() << std::endl;
|
||||
break;
|
||||
case MetaKeySignature:
|
||||
std::cout << "Key Signature: " << ifs.get() << std::endl;
|
||||
std::cout << "Minor Key: " << ifs.get() << std::endl;
|
||||
break;
|
||||
case MetaSequencerSpecific:
|
||||
std::cout << "Sequencer Specific: " << ReadString(nLength) << std::endl;
|
||||
break;
|
||||
default:
|
||||
std::cout << "Unrecognised MetaEvent: " << nType << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
if (nStatus == 0xF0)
|
||||
{
|
||||
// System Exclusive Message Begin
|
||||
std::cout << "System Exclusive Begin: " << ReadString(ReadValue()) << std::endl;
|
||||
}
|
||||
|
||||
if (nStatus == 0xF7)
|
||||
{
|
||||
// System Exclusive Message Begin
|
||||
std::cout << "System Exclusive End: " << ReadString(ReadValue()) << std::endl;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Unrecognised Status Byte: " << nStatus << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Convert Time Events to Notes
|
||||
for (auto& track : vecTracks)
|
||||
{
|
||||
uint32_t nWallTime = 0;
|
||||
|
||||
std::list<MidiNote> listNotesBeingProcessed;
|
||||
|
||||
for (auto& event : track.vecEvents)
|
||||
{
|
||||
nWallTime += event.nDeltaTick;
|
||||
|
||||
if (event.event == MidiEvent::Type::NoteOn)
|
||||
{
|
||||
// New Note
|
||||
listNotesBeingProcessed.push_back({ event.nKey, event.nVelocity, nWallTime, 0 });
|
||||
}
|
||||
|
||||
if (event.event == MidiEvent::Type::NoteOff)
|
||||
{
|
||||
auto note = std::find_if(listNotesBeingProcessed.begin(), listNotesBeingProcessed.end(), [&](const MidiNote& n) { return n.nKey == event.nKey; });
|
||||
if (note != listNotesBeingProcessed.end())
|
||||
{
|
||||
note->nDuration = nWallTime - note->nStartTime;
|
||||
track.vecNotes.push_back(*note);
|
||||
track.nMinNote = std::min(track.nMinNote, note->nKey);
|
||||
track.nMaxNote = std::max(track.nMaxNote, note->nKey);
|
||||
listNotesBeingProcessed.erase(note);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public:
|
||||
std::vector<MidiTrack> vecTracks;
|
||||
uint32_t m_nTempo = 0;
|
||||
uint32_t m_nBPM = 0;
|
||||
|
||||
};
|
||||
|
||||
|
||||
class olcMIDIViewer : public olc::PixelGameEngine
|
||||
{
|
||||
public:
|
||||
olcMIDIViewer()
|
||||
{
|
||||
sAppName = "MIDI File Viewer";
|
||||
}
|
||||
|
||||
|
||||
MidiFile midi;
|
||||
|
||||
//HMIDIOUT hInstrument;
|
||||
size_t nCurrentNote[16]{ 0 };
|
||||
|
||||
double dSongTime = 0.0;
|
||||
double dRunTime = 0.0;
|
||||
uint32_t nMidiClock = 0;
|
||||
|
||||
|
||||
public:
|
||||
bool OnUserCreate() override
|
||||
{
|
||||
|
||||
midi.ParseFile("ff7_battle.mid");
|
||||
|
||||
/*
|
||||
int nMidiDevices = midiOutGetNumDevs();
|
||||
if (nMidiDevices > 0)
|
||||
{
|
||||
if (midiOutOpen(&hInstrument, 2, NULL, 0, NULL) == MMSYSERR_NOERROR)
|
||||
{
|
||||
std::cout << "Opened midi" << std::endl;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
float nTrackOffset = 1000;
|
||||
|
||||
bool OnUserUpdate(float fElapsedTime) override
|
||||
{
|
||||
Clear(olc::BLACK);
|
||||
uint32_t nTimePerColumn = 50;
|
||||
uint32_t nNoteHeight = 2;
|
||||
uint32_t nOffsetY = 0;
|
||||
|
||||
if (GetKey(olc::Key::LEFT).bHeld) nTrackOffset -= 10000.0f * fElapsedTime;
|
||||
if (GetKey(olc::Key::RIGHT).bHeld) nTrackOffset += 10000.0f * fElapsedTime;
|
||||
|
||||
|
||||
for (auto& track : midi.vecTracks)
|
||||
{
|
||||
if (!track.vecNotes.empty())
|
||||
{
|
||||
uint32_t nNoteRange = track.nMaxNote - track.nMinNote;
|
||||
|
||||
FillRect(0, nOffsetY, ScreenWidth(), (nNoteRange + 1) * nNoteHeight, olc::DARK_GREY);
|
||||
DrawString(1, nOffsetY + 1, track.sName);
|
||||
|
||||
for (auto& note : track.vecNotes)
|
||||
{
|
||||
FillRect((note.nStartTime - nTrackOffset) / nTimePerColumn, (nNoteRange - (note.nKey - track.nMinNote)) * nNoteHeight + nOffsetY, note.nDuration / nTimePerColumn, nNoteHeight, olc::WHITE);
|
||||
}
|
||||
|
||||
nOffsetY += (nNoteRange + 1) * nNoteHeight + 4;
|
||||
}
|
||||
}
|
||||
|
||||
// BELOW - ABSOLUTELY HORRIBLE BODGE TO PLAY SOUND
|
||||
// DO NOT USE THIS CODE...
|
||||
|
||||
/*
|
||||
dRunTime += fElapsedTime;
|
||||
uint32_t nTempo = 4;
|
||||
int nTrack = 1;
|
||||
while (dRunTime >= 1.0 / double(midi.m_nBPM * 8))
|
||||
{
|
||||
dRunTime -= 1.0 / double(midi.m_nBPM * 8);
|
||||
|
||||
// Single MIDI Clock
|
||||
nMidiClock++;
|
||||
|
||||
int i = 0;
|
||||
int nTrack = 1;
|
||||
//for (nTrack = 1; nTrack < 3; nTrack++)
|
||||
{
|
||||
if (nCurrentNote[nTrack] < midi.vecTracks[nTrack].vecEvents.size())
|
||||
{
|
||||
if (midi.vecTracks[nTrack].vecEvents[nCurrentNote[nTrack]].nDeltaTick == 0)
|
||||
{
|
||||
uint32_t nStatus = 0;
|
||||
uint32_t nNote = midi.vecTracks[nTrack].vecEvents[nCurrentNote[nTrack]].nKey;
|
||||
uint32_t nVelocity = midi.vecTracks[nTrack].vecEvents[nCurrentNote[nTrack]].nVelocity;
|
||||
|
||||
if (midi.vecTracks[nTrack].vecEvents[nCurrentNote[nTrack]].event == MidiEvent::Type::NoteOn)
|
||||
nStatus = 0x90;
|
||||
else
|
||||
nStatus = 0x80;
|
||||
|
||||
midiOutShortMsg(hInstrument, (nVelocity << 16) | (nNote << 8) | nStatus);
|
||||
nCurrentNote[nTrack]++;
|
||||
}
|
||||
else
|
||||
midi.vecTracks[nTrack].vecEvents[nCurrentNote[nTrack]].nDeltaTick--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (GetKey(olc::Key::SPACE).bPressed)
|
||||
{
|
||||
midiOutShortMsg(hInstrument, 0x00403C90);
|
||||
}
|
||||
|
||||
if (GetKey(olc::Key::SPACE).bReleased)
|
||||
{
|
||||
midiOutShortMsg(hInstrument, 0x00003C80);
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
olcMIDIViewer demo;
|
||||
if (demo.Construct(1280, 960, 1, 1))
|
||||
demo.Start();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1,592 +0,0 @@
|
||||
/*
|
||||
Brute Force Processing a Mandelbrot Renderer
|
||||
"Dammit Moros & Saladin, you guys keep making tools, I'll have nothing left to video..." - javidx9
|
||||
|
||||
License (OLC-3)
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Copyright 2018-2020 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.
|
||||
|
||||
Relevant Video: https://youtu.be/PBvLs88hvJ8
|
||||
|
||||
Links
|
||||
~~~~~
|
||||
YouTube: https://www.youtube.com/javidx9
|
||||
https://www.youtube.com/javidx9extra
|
||||
Discord: https://discord.gg/WhwHUMV
|
||||
Twitter: https://www.twitter.com/javidx9
|
||||
Twitch: https://www.twitch.tv/javidx9
|
||||
GitHub: https://www.github.com/onelonecoder
|
||||
Patreon: https://www.patreon.com/javidx9
|
||||
Homepage: https://www.onelonecoder.com
|
||||
|
||||
Community Blog: https://community.onelonecoder.com
|
||||
|
||||
Author
|
||||
~~~~~~
|
||||
David Barr, aka javidx9, ©OneLoneCoder 2018, 2019, 2020
|
||||
*/
|
||||
|
||||
#define OLC_PGE_APPLICATION
|
||||
#include "olcPixelGameEngine.h"
|
||||
|
||||
#include <condition_variable>
|
||||
#include <atomic>
|
||||
#include <complex>
|
||||
#include <cstdlib>
|
||||
#include <immintrin.h>
|
||||
|
||||
constexpr int nMaxThreads = 32;
|
||||
|
||||
class olcFractalExplorer : public olc::PixelGameEngine
|
||||
{
|
||||
public:
|
||||
olcFractalExplorer()
|
||||
{
|
||||
sAppName = "Brute Force Processing";
|
||||
}
|
||||
|
||||
int* pFractal = nullptr;
|
||||
int nMode = 4;
|
||||
int nIterations = 128;
|
||||
|
||||
public:
|
||||
bool OnUserCreate() override
|
||||
{
|
||||
//pFractal = new int[ScreenWidth() * ScreenHeight()]{ 0 };
|
||||
|
||||
// Using Vector extensions, align memory (not as necessary as it used to be)
|
||||
// MS Specific - see std::aligned_alloc for others
|
||||
pFractal = (int*)_aligned_malloc(size_t(ScreenWidth()) * size_t(ScreenHeight()) * sizeof(int), 64);
|
||||
|
||||
InitialiseThreadPool();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OnUserDestroy() override
|
||||
{
|
||||
// Stop Worker threads
|
||||
for (int i = 0; i < nMaxThreads; i++)
|
||||
{
|
||||
workers[i].alive = false; // Allow thread exit
|
||||
workers[i].cvStart.notify_one(); // Fake starting gun
|
||||
}
|
||||
|
||||
// Clean up worker threads
|
||||
for (int i = 0; i < nMaxThreads; i++)
|
||||
workers[i].thread.join();
|
||||
|
||||
// Clean up memory
|
||||
_aligned_free(pFractal);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Method 1) - Super simple, no effort at optimising
|
||||
void CreateFractalBasic(const olc::vi2d& pix_tl, const olc::vi2d& pix_br, const olc::vd2d& frac_tl, const olc::vd2d& frac_br, const int iterations)
|
||||
{
|
||||
double x_scale = (frac_br.x - frac_tl.x) / (double(pix_br.x) - double(pix_tl.x));
|
||||
double y_scale = (frac_br.y - frac_tl.y) / (double(pix_br.y) - double(pix_tl.y));
|
||||
|
||||
for (int y = pix_tl.y; y < pix_br.y; y++)
|
||||
{
|
||||
for (int x = pix_tl.x; x < pix_br.x; x++)
|
||||
{
|
||||
std::complex<double> c(x * x_scale + frac_tl.x, y * y_scale + frac_tl.y);
|
||||
std::complex<double> z(0, 0);
|
||||
|
||||
int n = 0;
|
||||
while (abs(z) < 2.0 && n < iterations)
|
||||
{
|
||||
z = (z * z) + c;
|
||||
n++;
|
||||
}
|
||||
|
||||
pFractal[y * ScreenWidth() + x] = n;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Method 2) - Attempt to pre-calculate as much as possible, and reduce
|
||||
// repeated multiplications
|
||||
void CreateFractalPreCalculate(const olc::vi2d& pix_tl, const olc::vi2d& pix_br, const olc::vd2d& frac_tl, const olc::vd2d& frac_br, const int iterations)
|
||||
{
|
||||
double x_scale = (frac_br.x - frac_tl.x) / (double(pix_br.x) - double(pix_tl.x));
|
||||
double y_scale = (frac_br.y - frac_tl.y) / (double(pix_br.y) - double(pix_tl.y));
|
||||
|
||||
double x_pos = frac_tl.x;
|
||||
double y_pos = frac_tl.y;
|
||||
|
||||
int y_offset = 0;
|
||||
int row_size = pix_br.x - pix_tl.x;
|
||||
|
||||
int x, y, n;
|
||||
std::complex<double> c, z;
|
||||
|
||||
for (y = pix_tl.y; y < pix_br.y; y++)
|
||||
{
|
||||
x_pos = frac_tl.x;
|
||||
for (x = pix_tl.x; x < pix_br.x; x++)
|
||||
{
|
||||
c = { x_pos, y_pos };
|
||||
z = { 0,0 };
|
||||
|
||||
n = 0;
|
||||
while (abs(z) < 2.0 && n < iterations)
|
||||
{
|
||||
z = (z * z) + c;
|
||||
n++;
|
||||
}
|
||||
|
||||
pFractal[y_offset + x] = n;
|
||||
x_pos += x_scale;
|
||||
}
|
||||
|
||||
y_pos += y_scale;
|
||||
y_offset += row_size;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Method 3) - Replace std::complex with just hard coded mathematics
|
||||
void CreateFractalNoComplex(const olc::vi2d& pix_tl, const olc::vi2d& pix_br, const olc::vd2d& frac_tl, const olc::vd2d& frac_br, const int iterations)
|
||||
{
|
||||
double x_scale = (frac_br.x - frac_tl.x) / (double(pix_br.x) - double(pix_tl.x));
|
||||
double y_scale = (frac_br.y - frac_tl.y) / (double(pix_br.y) - double(pix_tl.y));
|
||||
|
||||
double x_pos = frac_tl.x;
|
||||
double y_pos = frac_tl.y;
|
||||
|
||||
int y_offset = 0;
|
||||
int row_size = ScreenWidth();
|
||||
|
||||
int x, y, n;
|
||||
|
||||
double cr = 0;
|
||||
double ci = 0;
|
||||
double zr = 0;
|
||||
double zi = 0;
|
||||
double re = 0;
|
||||
double im = 0;
|
||||
|
||||
for (y = pix_tl.y; y < pix_br.y; y++)
|
||||
{
|
||||
x_pos = frac_tl.x;
|
||||
ci = y_pos;
|
||||
for (x = pix_tl.x; x < pix_br.x; x++)
|
||||
{
|
||||
cr = x_pos;
|
||||
zr = 0;
|
||||
zi = 0;
|
||||
|
||||
n = 0;
|
||||
while ((zr * zr + zi * zi) < 4.0 && n < iterations)
|
||||
{
|
||||
re = zr * zr - zi * zi + cr;
|
||||
im = zr * zi * 2.0 + ci;
|
||||
zr = re;
|
||||
zi = im;
|
||||
n++;
|
||||
}
|
||||
|
||||
pFractal[y_offset + x] = n;
|
||||
x_pos += x_scale;
|
||||
}
|
||||
|
||||
y_pos += y_scale;
|
||||
y_offset += row_size;
|
||||
}
|
||||
}
|
||||
|
||||
// Method 4) - Use AVX2 Vector co-processor to handle 4 fractal locations at once
|
||||
void CreateFractalIntrinsics(const olc::vi2d& pix_tl, const olc::vi2d& pix_br, const olc::vd2d& frac_tl, const olc::vd2d& frac_br, const int iterations)
|
||||
{
|
||||
double x_scale = (frac_br.x - frac_tl.x) / (double(pix_br.x) - double(pix_tl.x));
|
||||
double y_scale = (frac_br.y - frac_tl.y) / (double(pix_br.y) - double(pix_tl.y));
|
||||
|
||||
double y_pos = frac_tl.y;
|
||||
|
||||
int y_offset = 0;
|
||||
int row_size = ScreenWidth();
|
||||
|
||||
int x, y;
|
||||
|
||||
__m256d _a, _b, _two, _four, _mask1;
|
||||
__m256d _zr, _zi, _zr2, _zi2, _cr, _ci;
|
||||
__m256d _x_pos_offsets, _x_pos, _x_scale, _x_jump;
|
||||
__m256i _one, _c, _n, _iterations, _mask2;
|
||||
|
||||
_one = _mm256_set1_epi64x(1);
|
||||
_two = _mm256_set1_pd(2.0);
|
||||
_four = _mm256_set1_pd(4.0);
|
||||
_iterations = _mm256_set1_epi64x(iterations);
|
||||
|
||||
_x_scale = _mm256_set1_pd(x_scale);
|
||||
_x_jump = _mm256_set1_pd(x_scale * 4);
|
||||
_x_pos_offsets = _mm256_set_pd(0, 1, 2, 3);
|
||||
_x_pos_offsets = _mm256_mul_pd(_x_pos_offsets, _x_scale);
|
||||
|
||||
|
||||
for (y = pix_tl.y; y < pix_br.y; y++)
|
||||
{
|
||||
// Reset x_position
|
||||
_a = _mm256_set1_pd(frac_tl.x);
|
||||
_x_pos = _mm256_add_pd(_a, _x_pos_offsets);
|
||||
|
||||
_ci = _mm256_set1_pd(y_pos);
|
||||
|
||||
for (x = pix_tl.x; x < pix_br.x; x += 4)
|
||||
{
|
||||
_cr = _x_pos;
|
||||
_zr = _mm256_setzero_pd();
|
||||
_zi = _mm256_setzero_pd();
|
||||
_n = _mm256_setzero_si256();
|
||||
|
||||
|
||||
repeat:
|
||||
_zr2 = _mm256_mul_pd(_zr, _zr);
|
||||
_zi2 = _mm256_mul_pd(_zi, _zi);
|
||||
_a = _mm256_sub_pd(_zr2, _zi2);
|
||||
_a = _mm256_add_pd(_a, _cr);
|
||||
_b = _mm256_mul_pd(_zr, _zi);
|
||||
_b = _mm256_fmadd_pd(_b, _two, _ci);
|
||||
_zr = _a;
|
||||
_zi = _b;
|
||||
_a = _mm256_add_pd(_zr2, _zi2);
|
||||
_mask1 = _mm256_cmp_pd(_a, _four, _CMP_LT_OQ);
|
||||
_mask2 = _mm256_cmpgt_epi64(_iterations, _n);
|
||||
_mask2 = _mm256_and_si256(_mask2, _mm256_castpd_si256(_mask1));
|
||||
_c = _mm256_and_si256(_one, _mask2); // Zero out ones where n < iterations
|
||||
_n = _mm256_add_epi64(_n, _c); // n++ Increase all n
|
||||
if (_mm256_movemask_pd(_mm256_castsi256_pd(_mask2)) > 0)
|
||||
goto repeat;
|
||||
|
||||
pFractal[y_offset + x + 0] = int(_n.m256i_i64[3]);
|
||||
pFractal[y_offset + x + 1] = int(_n.m256i_i64[2]);
|
||||
pFractal[y_offset + x + 2] = int(_n.m256i_i64[1]);
|
||||
pFractal[y_offset + x + 3] = int(_n.m256i_i64[0]);
|
||||
_x_pos = _mm256_add_pd(_x_pos, _x_jump);
|
||||
}
|
||||
|
||||
y_pos += y_scale;
|
||||
y_offset += row_size;
|
||||
}
|
||||
}
|
||||
|
||||
// Method 5) - Spawn threads that use AVX method above
|
||||
void CreateFractalThreads(const olc::vi2d& pix_tl, const olc::vi2d& pix_br, const olc::vd2d& frac_tl, const olc::vd2d& frac_br, const int iterations)
|
||||
{
|
||||
int nSectionWidth = (pix_br.x - pix_tl.x) / nMaxThreads;
|
||||
double dFractalWidth = (frac_br.x - frac_tl.x) / double(nMaxThreads);
|
||||
|
||||
std::thread t[nMaxThreads];
|
||||
|
||||
for (size_t i = 0; i < nMaxThreads; i++)
|
||||
t[i] = std::thread(&olcFractalExplorer::CreateFractalIntrinsics, this,
|
||||
olc::vi2d(pix_tl.x + nSectionWidth * (i), pix_tl.y),
|
||||
olc::vi2d(pix_tl.x + nSectionWidth * (i + 1), pix_br.y),
|
||||
olc::vd2d(frac_tl.x + dFractalWidth * double(i), frac_tl.y),
|
||||
olc::vd2d(frac_tl.x + dFractalWidth * double(i + 1), frac_br.y),
|
||||
iterations);
|
||||
|
||||
for (size_t i = 0; i < nMaxThreads; i++)
|
||||
t[i].join();
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Method 6) - Threadpool, keep threads alive and reuse them, reducing setup overhead
|
||||
struct WorkerThread
|
||||
{
|
||||
olc::vi2d pix_tl = { 0,0 };
|
||||
olc::vi2d pix_br = { 0,0 };
|
||||
olc::vd2d frac_tl = { 0,0 };
|
||||
olc::vd2d frac_br = { 0,0 };
|
||||
int iterations = 0;
|
||||
std::condition_variable cvStart;
|
||||
bool alive = true;
|
||||
std::mutex mux;
|
||||
int screen_width = 0;
|
||||
int* fractal = nullptr;
|
||||
|
||||
std::thread thread;
|
||||
|
||||
void Start(const olc::vi2d& ptl, const olc::vi2d& pbr, const olc::vd2d& ftl, const olc::vd2d& fbr, const int it)
|
||||
{
|
||||
pix_tl = ptl;
|
||||
pix_br = pbr;
|
||||
frac_tl = ftl;
|
||||
frac_br = fbr;
|
||||
iterations = it;
|
||||
std::unique_lock<std::mutex> lm(mux);
|
||||
cvStart.notify_one();
|
||||
}
|
||||
|
||||
void CreateFractal()
|
||||
{
|
||||
while (alive)
|
||||
{
|
||||
std::unique_lock<std::mutex> lm(mux);
|
||||
cvStart.wait(lm);
|
||||
|
||||
double x_scale = (frac_br.x - frac_tl.x) / (double(pix_br.x) - double(pix_tl.x));
|
||||
double y_scale = (frac_br.y - frac_tl.y) / (double(pix_br.y) - double(pix_tl.y));
|
||||
|
||||
double y_pos = frac_tl.y;
|
||||
|
||||
int y_offset = 0;
|
||||
int row_size = screen_width;
|
||||
|
||||
int x, y;
|
||||
|
||||
__m256d _a, _b, _two, _four, _mask1;
|
||||
__m256d _zr, _zi, _zr2, _zi2, _cr, _ci;
|
||||
__m256d _x_pos_offsets, _x_pos, _x_scale, _x_jump;
|
||||
__m256i _one, _c, _n, _iterations, _mask2;
|
||||
|
||||
_one = _mm256_set1_epi64x(1);
|
||||
_two = _mm256_set1_pd(2.0);
|
||||
_four = _mm256_set1_pd(4.0);
|
||||
_iterations = _mm256_set1_epi64x(iterations);
|
||||
|
||||
_x_scale = _mm256_set1_pd(x_scale);
|
||||
_x_jump = _mm256_set1_pd(x_scale * 4);
|
||||
_x_pos_offsets = _mm256_set_pd(0, 1, 2, 3);
|
||||
_x_pos_offsets = _mm256_mul_pd(_x_pos_offsets, _x_scale);
|
||||
|
||||
|
||||
for (y = pix_tl.y; y < pix_br.y; y++)
|
||||
{
|
||||
// Reset x_position
|
||||
_a = _mm256_set1_pd(frac_tl.x);
|
||||
_x_pos = _mm256_add_pd(_a, _x_pos_offsets);
|
||||
|
||||
_ci = _mm256_set1_pd(y_pos);
|
||||
|
||||
for (x = pix_tl.x; x < pix_br.x; x += 4)
|
||||
{
|
||||
_cr = _x_pos;
|
||||
_zr = _mm256_setzero_pd();
|
||||
_zi = _mm256_setzero_pd();
|
||||
_n = _mm256_setzero_si256();
|
||||
|
||||
repeat:
|
||||
_zr2 = _mm256_mul_pd(_zr, _zr);
|
||||
_zi2 = _mm256_mul_pd(_zi, _zi);
|
||||
_a = _mm256_sub_pd(_zr2, _zi2);
|
||||
_a = _mm256_add_pd(_a, _cr);
|
||||
_b = _mm256_mul_pd(_zr, _zi);
|
||||
_b = _mm256_fmadd_pd(_b, _two, _ci);
|
||||
_zr = _a;
|
||||
_zi = _b;
|
||||
_a = _mm256_add_pd(_zr2, _zi2);
|
||||
_mask1 = _mm256_cmp_pd(_a, _four, _CMP_LT_OQ);
|
||||
_mask2 = _mm256_cmpgt_epi64(_iterations, _n);
|
||||
_mask2 = _mm256_and_si256(_mask2, _mm256_castpd_si256(_mask1));
|
||||
_c = _mm256_and_si256(_one, _mask2); // Zero out ones where n < iterations
|
||||
_n = _mm256_add_epi64(_n, _c); // n++ Increase all n
|
||||
if (_mm256_movemask_pd(_mm256_castsi256_pd(_mask2)) > 0)
|
||||
goto repeat;
|
||||
|
||||
fractal[y_offset + x + 0] = int(_n.m256i_i64[3]);
|
||||
fractal[y_offset + x + 1] = int(_n.m256i_i64[2]);
|
||||
fractal[y_offset + x + 2] = int(_n.m256i_i64[1]);
|
||||
fractal[y_offset + x + 3] = int(_n.m256i_i64[0]);
|
||||
_x_pos = _mm256_add_pd(_x_pos, _x_jump);
|
||||
}
|
||||
|
||||
y_pos += y_scale;
|
||||
y_offset += row_size;
|
||||
}
|
||||
nWorkerComplete++;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
WorkerThread workers[nMaxThreads];
|
||||
static std::atomic<int> nWorkerComplete;
|
||||
|
||||
void InitialiseThreadPool()
|
||||
{
|
||||
for (int i = 0; i < nMaxThreads; i++)
|
||||
{
|
||||
workers[i].alive = true;
|
||||
workers[i].fractal = pFractal;
|
||||
workers[i].screen_width = ScreenWidth();
|
||||
workers[i].thread = std::thread(&WorkerThread::CreateFractal, &workers[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void CreateFractalThreadPool(const olc::vi2d& pix_tl, const olc::vi2d& pix_br, const olc::vd2d& frac_tl, const olc::vd2d& frac_br, const int iterations)
|
||||
{
|
||||
int nSectionWidth = (pix_br.x - pix_tl.x) / nMaxThreads;
|
||||
double dFractalWidth = (frac_br.x - frac_tl.x) / double(nMaxThreads);
|
||||
nWorkerComplete = 0;
|
||||
|
||||
for (size_t i = 0; i < nMaxThreads; i++)
|
||||
workers[i].Start(
|
||||
olc::vi2d(pix_tl.x + nSectionWidth * i, pix_tl.y),
|
||||
olc::vi2d(pix_tl.x + nSectionWidth * (i + 1), pix_br.y),
|
||||
olc::vd2d(frac_tl.x + dFractalWidth * double(i), frac_tl.y),
|
||||
olc::vd2d(frac_tl.x + dFractalWidth * double(i + 1), frac_br.y),
|
||||
iterations);
|
||||
|
||||
|
||||
while (nWorkerComplete < nMaxThreads) // Wait for all workers to complete
|
||||
{ }
|
||||
}
|
||||
|
||||
|
||||
bool OnUserUpdate(float fElapsedTime) override
|
||||
{
|
||||
|
||||
// Get mouse location this frame
|
||||
olc::vd2d vMouse = { (double)GetMouseX(), (double)GetMouseY() };
|
||||
|
||||
// Handle Pan & Zoom
|
||||
if (GetMouse(2).bPressed)
|
||||
{
|
||||
vStartPan = vMouse;
|
||||
}
|
||||
|
||||
if (GetMouse(2).bHeld)
|
||||
{
|
||||
vOffset -= (vMouse - vStartPan) / vScale;
|
||||
vStartPan = vMouse;
|
||||
}
|
||||
|
||||
olc::vd2d vMouseBeforeZoom;
|
||||
ScreenToWorld(vMouse, vMouseBeforeZoom);
|
||||
|
||||
if (GetKey(olc::Key::Q).bHeld || GetMouseWheel() > 0) vScale *= 1.1;
|
||||
if (GetKey(olc::Key::A).bHeld || GetMouseWheel() < 0) vScale *= 0.9;
|
||||
|
||||
olc::vd2d vMouseAfterZoom;
|
||||
ScreenToWorld(vMouse, vMouseAfterZoom);
|
||||
vOffset += (vMouseBeforeZoom - vMouseAfterZoom);
|
||||
|
||||
olc::vi2d pix_tl = { 0,0 };
|
||||
olc::vi2d pix_br = { ScreenWidth(), ScreenHeight() };
|
||||
olc::vd2d frac_tl = { -2.0, -1.0 };
|
||||
olc::vd2d frac_br = { 1.0, 1.0 };
|
||||
|
||||
ScreenToWorld(pix_tl, frac_tl);
|
||||
ScreenToWorld(pix_br, frac_br);
|
||||
|
||||
// Handle User Input
|
||||
if (GetKey(olc::K1).bPressed) nMode = 0;
|
||||
if (GetKey(olc::K2).bPressed) nMode = 1;
|
||||
if (GetKey(olc::K3).bPressed) nMode = 2;
|
||||
if (GetKey(olc::K4).bPressed) nMode = 3;
|
||||
if (GetKey(olc::K5).bPressed) nMode = 4;
|
||||
if (GetKey(olc::K6).bPressed) nMode = 5;
|
||||
if (GetKey(olc::UP).bPressed) nIterations += 64;
|
||||
if (GetKey(olc::DOWN).bPressed) nIterations -= 64;
|
||||
if (nIterations < 64) nIterations = 64;
|
||||
|
||||
|
||||
// START TIMING
|
||||
auto tp1 = std::chrono::high_resolution_clock::now();
|
||||
|
||||
// Do the computation
|
||||
switch (nMode)
|
||||
{
|
||||
case 0: CreateFractalBasic(pix_tl, pix_br, frac_tl, frac_br, nIterations); break;
|
||||
case 1: CreateFractalPreCalculate(pix_tl, pix_br, frac_tl, frac_br, nIterations); break;
|
||||
case 2: CreateFractalNoComplex(pix_tl, pix_br, frac_tl, frac_br, nIterations); break;
|
||||
case 3: CreateFractalIntrinsics(pix_tl, pix_br, frac_tl, frac_br, nIterations); break;
|
||||
case 4: CreateFractalThreads(pix_tl, pix_br, frac_tl, frac_br, nIterations); break;
|
||||
case 5: CreateFractalThreadPool(pix_tl, pix_br, frac_tl, frac_br, nIterations); break;
|
||||
}
|
||||
|
||||
// STOP TIMING
|
||||
auto tp2 = std::chrono::high_resolution_clock::now();
|
||||
std::chrono::duration<double> elapsedTime = tp2 - tp1;
|
||||
|
||||
|
||||
// Render result to screen
|
||||
for (int y = 0; y < ScreenHeight(); y++)
|
||||
{
|
||||
for (int x = 0; x < ScreenWidth(); x++)
|
||||
{
|
||||
int i = pFractal[y * ScreenWidth() + x];
|
||||
float n = (float)i;
|
||||
float a = 0.1f;
|
||||
// Thank you @Eriksonn - Wonderful Magic Fractal Oddball Man
|
||||
Draw(x, y, olc::PixelF(0.5f * sin(a * n) + 0.5f, 0.5f * sin(a * n + 2.094f) + 0.5f, 0.5f * sin(a * n + 4.188f) + 0.5f));
|
||||
}
|
||||
}
|
||||
|
||||
// Render UI
|
||||
switch (nMode)
|
||||
{
|
||||
case 0: DrawString(0, 0, "1) Naive Method", olc::WHITE, 3); break;
|
||||
case 1: DrawString(0, 0, "2) Precalculate Method", olc::WHITE, 3); break;
|
||||
case 2: DrawString(0, 0, "3) Hand-code Maths Method", olc::WHITE, 3); break;
|
||||
case 3: DrawString(0, 0, "4) Vector Extensions (AVX2) Method", olc::WHITE, 3); break;
|
||||
case 4: DrawString(0, 0, "5) Threads Method", olc::WHITE, 3); break;
|
||||
case 5: DrawString(0, 0, "6) ThreadPool Method", olc::WHITE, 3); break;
|
||||
}
|
||||
|
||||
DrawString(0, 30, "Time Taken: " + std::to_string(elapsedTime.count()) + "s", olc::WHITE, 3);
|
||||
DrawString(0, 60, "Iterations: " + std::to_string(nIterations), olc::WHITE, 3);
|
||||
return !(GetKey(olc::Key::ESCAPE).bPressed);
|
||||
}
|
||||
|
||||
// Pan & Zoom variables
|
||||
olc::vd2d vOffset = { 0.0, 0.0 };
|
||||
olc::vd2d vStartPan = { 0.0, 0.0 };
|
||||
olc::vd2d vScale = { 1280.0 / 2.0, 720.0 };
|
||||
|
||||
|
||||
// Convert coordinates from World Space --> Screen Space
|
||||
void WorldToScreen(const olc::vd2d& v, olc::vi2d &n)
|
||||
{
|
||||
n.x = (int)((v.x - vOffset.x) * vScale.x);
|
||||
n.y = (int)((v.y - vOffset.y) * vScale.y);
|
||||
}
|
||||
|
||||
// Convert coordinates from Screen Space --> World Space
|
||||
void ScreenToWorld(const olc::vi2d& n, olc::vd2d& v)
|
||||
{
|
||||
v.x = (double)(n.x) / vScale.x + vOffset.x;
|
||||
v.y = (double)(n.y) / vScale.y + vOffset.y;
|
||||
}
|
||||
};
|
||||
|
||||
std::atomic<int> olcFractalExplorer::nWorkerComplete = 0;
|
||||
|
||||
int main()
|
||||
{
|
||||
olcFractalExplorer demo;
|
||||
if (demo.Construct(1280, 720, 1, 1, false, false))
|
||||
demo.Start();
|
||||
return 0;
|
||||
}
|
||||
@ -1,426 +0,0 @@
|
||||
/*
|
||||
OneLoneCoder.com - Path Finding #2 - Wave Propagation & Potential Fields
|
||||
"...never get lost again, so long as you know where you are" - @Javidx9
|
||||
|
||||
|
||||
Background
|
||||
~~~~~~~~~~
|
||||
A nice follow up alternative to the A* Algorithm. Wave propagation is less
|
||||
applicable to multiple objects with multiple destinations, but fantatsic
|
||||
for multiple objects all reaching the same destination.
|
||||
|
||||
WARNING! This code is NOT OPTIMAL!! It is however very robust. There
|
||||
are many ways to optimise this further.
|
||||
|
||||
License (OLC-3)
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Copyright 2018 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
|
||||
Patreon: https://www.patreon/javidx9
|
||||
Homepage: https://www.onelonecoder.com
|
||||
|
||||
Relevant Videos
|
||||
~~~~~~~~~~~~~~~
|
||||
Part #1 https://youtu.be/icZj67PTFhc
|
||||
Part #2 https://youtu.be/0ihciMKlcP8
|
||||
|
||||
Author
|
||||
~~~~~~
|
||||
David Barr, aka javidx9, ©OneLoneCoder 2018
|
||||
*/
|
||||
#define OLC_PGE_APPLICATION
|
||||
#include "olcPixelGameEngine.h"
|
||||
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
|
||||
|
||||
// Override base class with your custom functionality
|
||||
class PathFinding_FlowFields : public olc::PixelGameEngine
|
||||
{
|
||||
public:
|
||||
PathFinding_FlowFields()
|
||||
{
|
||||
sAppName = "PathFinding - Flow Fields";
|
||||
}
|
||||
|
||||
private:
|
||||
int nMapWidth;
|
||||
int nMapHeight;
|
||||
int nCellSize;
|
||||
int nBorderWidth;
|
||||
|
||||
bool *bObstacleMap;
|
||||
|
||||
int *nFlowFieldZ;
|
||||
float *fFlowFieldY;
|
||||
float *fFlowFieldX;
|
||||
|
||||
int nStartX;
|
||||
int nStartY;
|
||||
int nEndX;
|
||||
int nEndY;
|
||||
|
||||
int nWave = 1;
|
||||
|
||||
public:
|
||||
bool OnUserCreate() override
|
||||
{
|
||||
nBorderWidth = 4;
|
||||
nCellSize = 32;
|
||||
nMapWidth = ScreenWidth() / nCellSize;
|
||||
nMapHeight = ScreenHeight() / nCellSize;
|
||||
bObstacleMap = new bool[nMapWidth * nMapHeight]{ false };
|
||||
nFlowFieldZ = new int[nMapWidth * nMapHeight]{ 0 };
|
||||
fFlowFieldX = new float[nMapWidth * nMapHeight]{ 0 };
|
||||
fFlowFieldY = new float[nMapWidth * nMapHeight]{ 0 };
|
||||
|
||||
nStartX = 3;
|
||||
nStartY = 7;
|
||||
nEndX = 12;
|
||||
nEndY = 7;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OnUserUpdate(float fElapsedTime) override
|
||||
{
|
||||
// Little convenience lambda 2D -> 1D
|
||||
auto p = [&](int x, int y) { return y * nMapWidth + x; };
|
||||
|
||||
// User Input
|
||||
int nSelectedCellX = GetMouseX() / nCellSize;
|
||||
int nSelectedCellY = GetMouseY() / nCellSize;
|
||||
|
||||
if (GetMouse(0).bReleased)
|
||||
{
|
||||
// Toggle Obstacle if mouse left clicked
|
||||
bObstacleMap[p(nSelectedCellX, nSelectedCellY)] =
|
||||
!bObstacleMap[p(nSelectedCellX, nSelectedCellY)];
|
||||
}
|
||||
|
||||
if (GetMouse(1).bReleased)
|
||||
{
|
||||
nStartX = nSelectedCellX;
|
||||
nStartY = nSelectedCellY;
|
||||
}
|
||||
|
||||
if (GetKey(olc::Key::Q).bReleased)
|
||||
{
|
||||
nWave++;
|
||||
}
|
||||
|
||||
if (GetKey(olc::Key::A).bReleased)
|
||||
{
|
||||
nWave--;
|
||||
if (nWave == 0)
|
||||
nWave = 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 1) Prepare flow field, add a boundary, and add obstacles
|
||||
// by setting the flow Field Height (Z) to -1
|
||||
for (int x = 0; x < nMapWidth; x++)
|
||||
{
|
||||
for (int y = 0; y < nMapHeight; y++)
|
||||
{
|
||||
// Set border or obstacles
|
||||
if (x == 0 || y == 0 || x == (nMapWidth - 1) || y == (nMapHeight - 1) || bObstacleMap[p(x, y)])
|
||||
{
|
||||
nFlowFieldZ[p(x, y)] = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
nFlowFieldZ[p(x, y)] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
// similar.
|
||||
std::list<std::tuple<int, int, int>> nodes;
|
||||
|
||||
// Add the first discovered node - the target location, with a distance of 1
|
||||
nodes.push_back({ nEndX, nEndY, 1 });
|
||||
|
||||
while (!nodes.empty())
|
||||
{
|
||||
// Each iteration through the discovered nodes may create newly discovered
|
||||
// nodes, so I maintain a second list. It's important not to contaminate
|
||||
// the list being iterated through.
|
||||
std::list<std::tuple<int, int, int>> new_nodes;
|
||||
|
||||
// Now iterate through each discovered node. If it has neighbouring nodes
|
||||
// that are empty space and undiscovered, add those locations to the
|
||||
// new nodes list
|
||||
for (auto &n : nodes)
|
||||
{
|
||||
int x = std::get<0>(n); // Map X-Coordinate
|
||||
int y = std::get<1>(n); // Map Y-Coordinate
|
||||
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
|
||||
// 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
|
||||
// propagate distance information 'away from target location'
|
||||
nFlowFieldZ[p(x, y)] = d;
|
||||
|
||||
// Add neigbour nodes if unmarked, i.e their "height" is 0. Any discovered
|
||||
// node or obstacle will be non-zero
|
||||
|
||||
// Check East
|
||||
if ((x + 1) < nMapWidth && nFlowFieldZ[p(x + 1, y)] == 0)
|
||||
new_nodes.push_back({ x + 1, y, d + 1 });
|
||||
|
||||
// Check West
|
||||
if ((x - 1) >= 0 && nFlowFieldZ[p(x - 1, y)] == 0)
|
||||
new_nodes.push_back({ x - 1, y, d + 1 });
|
||||
|
||||
// Check South
|
||||
if ((y + 1) < nMapHeight && nFlowFieldZ[p(x, y + 1)] == 0)
|
||||
new_nodes.push_back({ x, y + 1, d + 1 });
|
||||
|
||||
// Check North
|
||||
if ((y - 1) >= 0 && nFlowFieldZ[p(x, y - 1)] == 0)
|
||||
new_nodes.push_back({ x, y - 1, d + 1 });
|
||||
}
|
||||
|
||||
// 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.
|
||||
// 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
|
||||
// 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
|
||||
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
|
||||
// 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.
|
||||
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 -
|
||||
// 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)
|
||||
{
|
||||
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
|
||||
// discovered nodes for processing on the next iteration
|
||||
nodes.clear();
|
||||
nodes.insert(nodes.begin(), new_nodes.begin(), new_nodes.end());
|
||||
|
||||
// When there are no more newly discovered nodes, we have "flood filled" the entire
|
||||
// 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
|
||||
// location. At each node find the neighbour with the lowest "distance" score.
|
||||
std::list<std::pair<int, int>> path;
|
||||
path.push_back({ nStartX, nStartY });
|
||||
int nLocX = nStartX;
|
||||
int nLocY = nStartY;
|
||||
bool bNoPath = false;
|
||||
|
||||
while (!(nLocX == nEndX && nLocY == nEndY) && !bNoPath)
|
||||
{
|
||||
std::list<std::tuple<int, int, int>> listNeighbours;
|
||||
|
||||
// 4-Way Connectivity
|
||||
if ((nLocY - 1) >= 0 && nFlowFieldZ[p(nLocX, nLocY - 1)] > 0)
|
||||
listNeighbours.push_back({ nLocX, nLocY - 1, nFlowFieldZ[p(nLocX, nLocY - 1)] });
|
||||
|
||||
if ((nLocX + 1) < nMapWidth && nFlowFieldZ[p(nLocX + 1, nLocY)] > 0)
|
||||
listNeighbours.push_back({ nLocX + 1, nLocY, nFlowFieldZ[p(nLocX + 1, nLocY)] });
|
||||
|
||||
if ((nLocY + 1) < nMapHeight && nFlowFieldZ[p(nLocX, nLocY + 1)] > 0)
|
||||
listNeighbours.push_back({ nLocX, nLocY + 1, nFlowFieldZ[p(nLocX, nLocY + 1)] });
|
||||
|
||||
if ((nLocX - 1) >= 0 && nFlowFieldZ[p(nLocX - 1, nLocY)] > 0)
|
||||
listNeighbours.push_back({ nLocX - 1, nLocY, nFlowFieldZ[p(nLocX - 1, nLocY)] });
|
||||
|
||||
// 8-Way Connectivity
|
||||
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)] });
|
||||
|
||||
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)] });
|
||||
|
||||
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)] });
|
||||
|
||||
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)] });
|
||||
|
||||
// Sprt neigbours based on height, so lowest neighbour is at front
|
||||
// of list
|
||||
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
|
||||
});
|
||||
|
||||
if (listNeighbours.empty()) // Neighbour is invalid or no possible path
|
||||
bNoPath = true;
|
||||
else
|
||||
{
|
||||
nLocX = std::get<0>(listNeighbours.front());
|
||||
nLocY = std::get<1>(listNeighbours.front());
|
||||
path.push_back({ nLocX, nLocY });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 4) Create Flow "Field"
|
||||
for (int x = 1; x < nMapWidth - 1; x++)
|
||||
{
|
||||
for (int y = 1; y < nMapHeight - 1; y++)
|
||||
{
|
||||
float vx = 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)]);
|
||||
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)]);
|
||||
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);
|
||||
fFlowFieldX[p(x, y)] = vx * r;
|
||||
fFlowFieldY[p(x, y)] = vy * r;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Draw Map
|
||||
Clear(olc::BLACK);
|
||||
|
||||
for (int x = 0; x < nMapWidth; x++)
|
||||
{
|
||||
for (int y = 0; y < nMapHeight; y++)
|
||||
{
|
||||
olc::Pixel colour = olc::BLUE;
|
||||
|
||||
if (bObstacleMap[p(x, y)])
|
||||
colour = olc::GREY;
|
||||
|
||||
if (nWave == nFlowFieldZ[p(x, y)])
|
||||
colour = olc::DARK_CYAN;
|
||||
|
||||
if (x == nStartX && y == nStartY)
|
||||
colour = olc::GREEN;
|
||||
|
||||
if (x == nEndX && y == nEndY)
|
||||
colour = olc::RED;
|
||||
|
||||
// Draw Base
|
||||
FillRect(x * nCellSize, y * nCellSize, nCellSize - nBorderWidth, nCellSize - nBorderWidth, colour);
|
||||
|
||||
// Draw "potential" or "distance" or "height" :D
|
||||
//DrawString(x * nCellSize, y * nCellSize, std::to_string(nFlowFieldZ[p(x, y)]), olc::WHITE);
|
||||
|
||||
if (nFlowFieldZ[p(x, y)] > 0)
|
||||
{
|
||||
float ax[4], ay[4];
|
||||
float fAngle = atan2f(fFlowFieldY[p(x, y)], fFlowFieldX[p(x, y)]);
|
||||
float fRadius = (float)(nCellSize - nBorderWidth) / 2.0f;
|
||||
int fOffsetX = x * nCellSize + ((nCellSize - nBorderWidth) / 2);
|
||||
int fOffsetY = y * nCellSize + ((nCellSize - nBorderWidth) / 2);
|
||||
ax[0] = cosf(fAngle) * fRadius + fOffsetX;
|
||||
ay[0] = sinf(fAngle) * fRadius + fOffsetY;
|
||||
ax[1] = cosf(fAngle) * -fRadius + fOffsetX;
|
||||
ay[1] = sinf(fAngle) * -fRadius + fOffsetY;
|
||||
ax[2] = cosf(fAngle + 0.1f) * fRadius * 0.7f + fOffsetX;
|
||||
ay[2] = sinf(fAngle + 0.1f) * fRadius * 0.7f + fOffsetY;
|
||||
ax[3] = cosf(fAngle - 0.1f) * fRadius * 0.7f + fOffsetX;
|
||||
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[2], ay[2], olc::CYAN);
|
||||
DrawLine(ax[0], ay[0], ax[3], ay[3], olc::CYAN);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool bFirstPoint = true;
|
||||
int ox, oy;
|
||||
for (auto &a : path)
|
||||
{
|
||||
if (bFirstPoint)
|
||||
{
|
||||
ox = a.first;
|
||||
oy = a.second;
|
||||
bFirstPoint = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
DrawLine(
|
||||
ox * nCellSize + ((nCellSize - nBorderWidth) / 2),
|
||||
oy * nCellSize + ((nCellSize - nBorderWidth) / 2),
|
||||
a.first * nCellSize + ((nCellSize - nBorderWidth) / 2),
|
||||
a.second * nCellSize + ((nCellSize - nBorderWidth) / 2), olc::YELLOW);
|
||||
|
||||
ox = a.first;
|
||||
oy = a.second;
|
||||
|
||||
FillCircle(ox * nCellSize + ((nCellSize - nBorderWidth) / 2), oy * nCellSize + ((nCellSize - nBorderWidth) / 2), 10, olc::YELLOW);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
PathFinding_FlowFields demo;
|
||||
if (demo.Construct(512, 480, 2, 2))
|
||||
demo.Start();
|
||||
return 0;
|
||||
}
|
||||
@ -1,434 +0,0 @@
|
||||
/*
|
||||
Convex Polygon Collision Detection
|
||||
"Don't you dare try concave ones..." - javidx9
|
||||
|
||||
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.
|
||||
|
||||
Instructions:
|
||||
~~~~~~~~~~~~~
|
||||
Use arrow keys to control pentagon
|
||||
Use WASD to control triangle
|
||||
F1..F4 selects algorithm
|
||||
|
||||
Relevant Video: https://youtu.be/7Ik2vowGcU0
|
||||
|
||||
Links
|
||||
~~~~~
|
||||
YouTube: https://www.youtube.com/javidx9
|
||||
https://www.youtube.com/javidx9extra
|
||||
Discord: https://discord.gg/WhwHUMV
|
||||
Twitter: https://www.twitter.com/javidx9
|
||||
Twitch: https://www.twitch.tv/javidx9
|
||||
GitHub: https://www.github.com/onelonecoder
|
||||
Patreon: https://www.patreon.com/javidx9
|
||||
Homepage: https://www.onelonecoder.com
|
||||
|
||||
Author
|
||||
~~~~~~
|
||||
David Barr, aka javidx9, ©OneLoneCoder 2019
|
||||
*/
|
||||
|
||||
#define OLC_PGE_APPLICATION
|
||||
#include "olcPixelGameEngine.h"
|
||||
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
// Override base class with your custom functionality
|
||||
class PolygonCollisions : public olc::PixelGameEngine
|
||||
{
|
||||
public:
|
||||
PolygonCollisions()
|
||||
{
|
||||
sAppName = "Polygon Collisions";
|
||||
}
|
||||
|
||||
struct vec2d
|
||||
{
|
||||
float x;
|
||||
float y;
|
||||
};
|
||||
|
||||
struct polygon
|
||||
{
|
||||
std::vector<vec2d> p; // Transformed Points
|
||||
vec2d pos; // Position of shape
|
||||
float angle; // Direction of shape
|
||||
std::vector<vec2d> o; // "Model" of shape
|
||||
bool overlap = false; // Flag to indicate if overlap has occurred
|
||||
};
|
||||
|
||||
std::vector<polygon> vecShapes;
|
||||
|
||||
int nMode = 0;
|
||||
|
||||
public:
|
||||
bool OnUserCreate() override
|
||||
{
|
||||
// Create Pentagon
|
||||
polygon s1;
|
||||
float fTheta = 3.14159f * 2.0f / 5.0f;
|
||||
s1.pos = { 100, 100 };
|
||||
s1.angle = 0.0f;
|
||||
for (int i = 0; i < 5; 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) });
|
||||
}
|
||||
|
||||
// Create Triangle
|
||||
polygon s2;
|
||||
fTheta = 3.14159f * 2.0f / 3.0f;
|
||||
s2.pos = { 200, 150 };
|
||||
s2.angle = 0.0f;
|
||||
for (int i = 0; i < 3; 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) });
|
||||
}
|
||||
|
||||
// Create Quad
|
||||
polygon s3;
|
||||
s3.pos = { 50, 200 };
|
||||
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.p.resize(4);
|
||||
|
||||
|
||||
vecShapes.push_back(s1);
|
||||
vecShapes.push_back(s2);
|
||||
vecShapes.push_back(s3);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool ShapeOverlap_SAT(polygon &r1, polygon &r2)
|
||||
{
|
||||
polygon *poly1 = &r1;
|
||||
polygon *poly2 = &r2;
|
||||
|
||||
for (int shape = 0; shape < 2; shape++)
|
||||
{
|
||||
if (shape == 1)
|
||||
{
|
||||
poly1 = &r2;
|
||||
poly2 = &r1;
|
||||
}
|
||||
|
||||
for (int a = 0; a < poly1->p.size(); a++)
|
||||
{
|
||||
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 };
|
||||
float d = sqrtf(axisProj.x * axisProj.x + axisProj.y * axisProj.y);
|
||||
axisProj = { axisProj.x / d, axisProj.y / d };
|
||||
|
||||
// Work out min and max 1D points for r1
|
||||
float min_r1 = INFINITY, max_r1 = -INFINITY;
|
||||
for (int p = 0; p < poly1->p.size(); p++)
|
||||
{
|
||||
float q = (poly1->p[p].x * axisProj.x + poly1->p[p].y * axisProj.y);
|
||||
min_r1 = std::min(min_r1, q);
|
||||
max_r1 = std::max(max_r1, q);
|
||||
}
|
||||
|
||||
// Work out min and max 1D points for r2
|
||||
float min_r2 = INFINITY, max_r2 = -INFINITY;
|
||||
for (int p = 0; p < poly2->p.size(); p++)
|
||||
{
|
||||
float q = (poly2->p[p].x * axisProj.x + poly2->p[p].y * axisProj.y);
|
||||
min_r2 = std::min(min_r2, q);
|
||||
max_r2 = std::max(max_r2, q);
|
||||
}
|
||||
|
||||
if (!(max_r2 >= min_r1 && max_r1 >= min_r2))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ShapeOverlap_SAT_STATIC(polygon &r1, polygon &r2)
|
||||
{
|
||||
polygon *poly1 = &r1;
|
||||
polygon *poly2 = &r2;
|
||||
|
||||
float overlap = INFINITY;
|
||||
|
||||
for (int shape = 0; shape < 2; shape++)
|
||||
{
|
||||
if (shape == 1)
|
||||
{
|
||||
poly1 = &r2;
|
||||
poly2 = &r1;
|
||||
}
|
||||
|
||||
for (int a = 0; a < poly1->p.size(); a++)
|
||||
{
|
||||
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 };
|
||||
|
||||
// Optional normalisation of projection axis enhances stability slightly
|
||||
//float d = sqrtf(axisProj.x * axisProj.x + axisProj.y * axisProj.y);
|
||||
//axisProj = { axisProj.x / d, axisProj.y / d };
|
||||
|
||||
// Work out min and max 1D points for r1
|
||||
float min_r1 = INFINITY, max_r1 = -INFINITY;
|
||||
for (int p = 0; p < poly1->p.size(); p++)
|
||||
{
|
||||
float q = (poly1->p[p].x * axisProj.x + poly1->p[p].y * axisProj.y);
|
||||
min_r1 = std::min(min_r1, q);
|
||||
max_r1 = std::max(max_r1, q);
|
||||
}
|
||||
|
||||
// Work out min and max 1D points for r2
|
||||
float min_r2 = INFINITY, max_r2 = -INFINITY;
|
||||
for (int p = 0; p < poly2->p.size(); p++)
|
||||
{
|
||||
float q = (poly2->p[p].x * axisProj.x + poly2->p[p].y * axisProj.y);
|
||||
min_r2 = std::min(min_r2, q);
|
||||
max_r2 = std::max(max_r2, q);
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
if (!(max_r2 >= min_r1 && max_r1 >= min_r2))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// If we got here, the objects have collided, we will displace r1
|
||||
// by overlap along the vector between the two object centers
|
||||
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);
|
||||
r1.pos.x -= overlap * d.x / s;
|
||||
r1.pos.y -= overlap * d.y / s;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Use edge/diagonal intersections.
|
||||
bool ShapeOverlap_DIAGS(polygon &r1, polygon &r2)
|
||||
{
|
||||
polygon *poly1 = &r1;
|
||||
polygon *poly2 = &r2;
|
||||
|
||||
for (int shape = 0; shape < 2; shape++)
|
||||
{
|
||||
if (shape == 1)
|
||||
{
|
||||
poly1 = &r2;
|
||||
poly2 = &r1;
|
||||
}
|
||||
|
||||
// Check diagonals of polygon...
|
||||
for (int p = 0; p < poly1->p.size(); p++)
|
||||
{
|
||||
vec2d line_r1s = poly1->pos;
|
||||
vec2d line_r1e = poly1->p[p];
|
||||
|
||||
// ...against edges of the other
|
||||
for (int q = 0; q < poly2->p.size(); q++)
|
||||
{
|
||||
vec2d line_r2s = poly2->p[q];
|
||||
vec2d line_r2e = poly2->p[(q + 1) % poly2->p.size()];
|
||||
|
||||
// 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 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;
|
||||
|
||||
if (t1 >= 0.0f && t1 < 1.0f && t2 >= 0.0f && t2 < 1.0f)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Use edge/diagonal intersections.
|
||||
bool ShapeOverlap_DIAGS_STATIC(polygon &r1, polygon &r2)
|
||||
{
|
||||
polygon *poly1 = &r1;
|
||||
polygon *poly2 = &r2;
|
||||
|
||||
for (int shape = 0; shape < 2; shape++)
|
||||
{
|
||||
if (shape == 1)
|
||||
{
|
||||
poly1 = &r2;
|
||||
poly2 = &r1;
|
||||
}
|
||||
|
||||
// Check diagonals of this polygon...
|
||||
for (int p = 0; p < poly1->p.size(); p++)
|
||||
{
|
||||
vec2d line_r1s = poly1->pos;
|
||||
vec2d line_r1e = poly1->p[p];
|
||||
|
||||
vec2d displacement = { 0,0 };
|
||||
|
||||
// ...against edges of this polygon
|
||||
for (int q = 0; q < poly2->p.size(); q++)
|
||||
{
|
||||
vec2d line_r2s = poly2->p[q];
|
||||
vec2d line_r2e = poly2->p[(q + 1) % poly2->p.size()];
|
||||
|
||||
// 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 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;
|
||||
|
||||
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.y += (1.0f - t1) * (line_r1e.y - line_r1s.y);
|
||||
}
|
||||
}
|
||||
|
||||
r1.pos.x += displacement.x * (shape == 0 ? -1 : +1);
|
||||
r1.pos.y += displacement.y * (shape == 0 ? -1 : +1);
|
||||
}
|
||||
}
|
||||
|
||||
// Cant overlap if static collision is resolved
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool OnUserUpdate(float fElapsedTime) override
|
||||
{
|
||||
if (GetKey(olc::Key::F1).bReleased) nMode = 0;
|
||||
if (GetKey(olc::Key::F2).bReleased) nMode = 1;
|
||||
if (GetKey(olc::Key::F3).bReleased) nMode = 2;
|
||||
if (GetKey(olc::Key::F4).bReleased) nMode = 3;
|
||||
|
||||
// Shape 1
|
||||
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::UP).bHeld)
|
||||
{
|
||||
vecShapes[0].pos.x += cosf(vecShapes[0].angle) * 60.0f * fElapsedTime;
|
||||
vecShapes[0].pos.y += sinf(vecShapes[0].angle) * 60.0f * fElapsedTime;
|
||||
}
|
||||
|
||||
if (GetKey(olc::Key::DOWN).bHeld)
|
||||
{
|
||||
vecShapes[0].pos.x -= cosf(vecShapes[0].angle) * 60.0f * fElapsedTime;
|
||||
vecShapes[0].pos.y -= sinf(vecShapes[0].angle) * 60.0f * fElapsedTime;
|
||||
}
|
||||
|
||||
// Shape 2
|
||||
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::W).bHeld)
|
||||
{
|
||||
vecShapes[1].pos.x += cosf(vecShapes[1].angle) * 60.0f * fElapsedTime;
|
||||
vecShapes[1].pos.y += sinf(vecShapes[1].angle) * 60.0f * fElapsedTime;
|
||||
}
|
||||
|
||||
if (GetKey(olc::Key::S).bHeld)
|
||||
{
|
||||
vecShapes[1].pos.x -= cosf(vecShapes[1].angle) * 60.0f * fElapsedTime;
|
||||
vecShapes[1].pos.y -= sinf(vecShapes[1].angle) * 60.0f * fElapsedTime;
|
||||
}
|
||||
|
||||
// Update Shapes and reset flags
|
||||
for (auto &r : vecShapes)
|
||||
{
|
||||
for (int i = 0; i < r.o.size(); i++)
|
||||
r.p[i] =
|
||||
{ // 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 * sinf(r.angle)) + (r.o[i].y * cosf(r.angle)) + r.pos.y,
|
||||
};
|
||||
|
||||
r.overlap = false;
|
||||
}
|
||||
|
||||
// Check for overlap
|
||||
for (int m = 0; m < vecShapes.size(); m++)
|
||||
for (int n = m + 1; n < vecShapes.size(); n++)
|
||||
{
|
||||
switch (nMode)
|
||||
{
|
||||
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 2: vecShapes[m].overlap |= ShapeOverlap_DIAGS(vecShapes[m], vecShapes[n]); break;
|
||||
case 3: vecShapes[m].overlap |= ShapeOverlap_DIAGS_STATIC(vecShapes[m], vecShapes[n]); break;
|
||||
}
|
||||
}
|
||||
|
||||
// === Render Display ===
|
||||
Clear(olc::BLUE);
|
||||
|
||||
// Draw Shapes
|
||||
for (auto &r : vecShapes)
|
||||
{
|
||||
// Draw Boundary
|
||||
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));
|
||||
|
||||
// Draw Direction
|
||||
DrawLine(r.p[0].x, r.p[0].y, r.pos.x, r.pos.y, (r.overlap ? olc::RED : olc::WHITE));
|
||||
}
|
||||
|
||||
// Draw HUD
|
||||
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, 30, "F3: DIAG", (nMode == 2 ? olc::RED : olc::YELLOW));
|
||||
DrawString(8, 40, "F4: DIAG/STATIC", (nMode == 3 ? olc::RED : olc::YELLOW));
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
PolygonCollisions demo;
|
||||
if (demo.Construct(256, 240, 4, 4))
|
||||
demo.Start();
|
||||
return 0;
|
||||
}
|
||||
@ -1,542 +0,0 @@
|
||||
/*
|
||||
OLC::CAD - A practical example of Polymorphism
|
||||
"Damn Gorbette, you made us giggle..." - javidx9
|
||||
|
||||
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.
|
||||
|
||||
Instructions:
|
||||
~~~~~~~~~~~~~
|
||||
Press & Hold middle mouse mutton to PAN
|
||||
Use Scroll wheel (or Q & A) to zoom in & out
|
||||
Press L to start drawing a line
|
||||
Press C to start drawing a circle
|
||||
Press B to start drawing a box
|
||||
Press S to start drawing a curve
|
||||
Press M to move node under cursor
|
||||
|
||||
Relevant Video: https://youtu.be/kxKKHKSMGIg
|
||||
|
||||
Links
|
||||
~~~~~
|
||||
YouTube: https://www.youtube.com/javidx9
|
||||
https://www.youtube.com/javidx9extra
|
||||
Discord: https://discord.gg/WhwHUMV
|
||||
Twitter: https://www.twitter.com/javidx9
|
||||
Twitch: https://www.twitch.tv/javidx9
|
||||
GitHub: https://www.github.com/onelonecoder
|
||||
Patreon: https://www.patreon.com/javidx9
|
||||
Homepage: https://www.onelonecoder.com
|
||||
|
||||
Author
|
||||
~~~~~~
|
||||
David Barr, aka javidx9, ©OneLoneCoder 2019
|
||||
*/
|
||||
|
||||
#define OLC_PGE_APPLICATION
|
||||
#include "olcPixelGameEngine.h"
|
||||
|
||||
// Forward declare shape, since we use it in sNode
|
||||
struct sShape;
|
||||
|
||||
// Define a node
|
||||
struct sNode
|
||||
{
|
||||
sShape *parent;
|
||||
olc::vf2d pos;
|
||||
};
|
||||
|
||||
// Our BASE class, defines the interface for all shapes
|
||||
struct sShape
|
||||
{
|
||||
// Shapes are defined by the placment of nodes
|
||||
std::vector<sNode> vecNodes;
|
||||
uint32_t nMaxNodes = 0;
|
||||
|
||||
// The colour of the shape
|
||||
olc::Pixel col = olc::GREEN;
|
||||
|
||||
// All shapes share word to screen transformation
|
||||
// coefficients, so share them staically
|
||||
static float fWorldScale;
|
||||
static olc::vf2d vWorldOffset;
|
||||
|
||||
// Convert coordinates from World Space --> Screen Space
|
||||
void WorldToScreen(const olc::vf2d &v, int &nScreenX, int &nScreenY)
|
||||
{
|
||||
nScreenX = (int)((v.x - vWorldOffset.x) * fWorldScale);
|
||||
nScreenY = (int)((v.y - vWorldOffset.y) * fWorldScale);
|
||||
}
|
||||
|
||||
// This is a PURE function, which makes this class abstract. A sub-class
|
||||
// of this class must provide an implementation of this function by
|
||||
// overriding it
|
||||
virtual void DrawYourself(olc::PixelGameEngine *pge) = 0;
|
||||
|
||||
// Shapes are defined by nodes, the shape is responsible
|
||||
// for issuing nodes that get placed by the user. The shape may
|
||||
// change depending on how many nodes have been placed. Once the
|
||||
// maximum number of nodes for a shape have been placed, it returns
|
||||
// nullptr
|
||||
sNode* GetNextNode(const olc::vf2d &p)
|
||||
{
|
||||
if (vecNodes.size() == nMaxNodes)
|
||||
return nullptr; // Shape is complete so no new nodes to be issued
|
||||
|
||||
// else create new node and add to shapes node vector
|
||||
sNode n;
|
||||
n.parent = this;
|
||||
n.pos = p;
|
||||
vecNodes.push_back(n);
|
||||
|
||||
// Beware! - This normally is bad! But see sub classes
|
||||
return &vecNodes[vecNodes.size() - 1];
|
||||
}
|
||||
|
||||
// Test to see if supplied coordinate exists at same location
|
||||
// as any of the nodes for this shape. Return a pointer to that
|
||||
// node if it does
|
||||
sNode* HitNode(olc::vf2d &p)
|
||||
{
|
||||
for (auto &n : vecNodes)
|
||||
{
|
||||
if ((p - n.pos).mag() < 0.01f)
|
||||
return &n;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Draw all of the nodes that define this shape so far
|
||||
void DrawNodes(olc::PixelGameEngine *pge)
|
||||
{
|
||||
for (auto &n : vecNodes)
|
||||
{
|
||||
int sx, sy;
|
||||
WorldToScreen(n.pos, sx, sy);
|
||||
pge->FillCircle(sx, sy, 2, olc::RED);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// We must provide an implementation of our static variables
|
||||
float sShape::fWorldScale = 1.0f;
|
||||
olc::vf2d sShape::vWorldOffset = { 0,0 };
|
||||
|
||||
|
||||
|
||||
// LINE sub class, inherits from sShape
|
||||
struct sLine : public sShape
|
||||
{
|
||||
sLine()
|
||||
{
|
||||
nMaxNodes = 2;
|
||||
vecNodes.reserve(nMaxNodes); // We're gonna be getting pointers to vector elements
|
||||
// though we have defined already how much capacity our vector will have. This makes
|
||||
// it safe to do this as we know the vector will not be maniupulated as we add nodes
|
||||
// to it. Is this bad practice? Possibly, but as with all thing programming, if you
|
||||
// know what you are doing, it's ok :D
|
||||
}
|
||||
|
||||
// Implements custom DrawYourself Function, meaning the shape
|
||||
// is no longer abstract
|
||||
void DrawYourself(olc::PixelGameEngine *pge) override
|
||||
{
|
||||
int sx, sy, ex, ey;
|
||||
WorldToScreen(vecNodes[0].pos, sx, sy);
|
||||
WorldToScreen(vecNodes[1].pos, ex, ey);
|
||||
pge->DrawLine(sx, sy, ex, ey, col);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// BOX
|
||||
struct sBox : public sShape
|
||||
{
|
||||
sBox()
|
||||
{
|
||||
nMaxNodes = 2;
|
||||
vecNodes.reserve(nMaxNodes);
|
||||
}
|
||||
|
||||
void DrawYourself(olc::PixelGameEngine *pge) override
|
||||
{
|
||||
int sx, sy, ex, ey;
|
||||
WorldToScreen(vecNodes[0].pos, sx, sy);
|
||||
WorldToScreen(vecNodes[1].pos, ex, ey);
|
||||
pge->DrawRect(sx, sy, ex - sx, ey - sy, col);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// CIRCLE
|
||||
struct sCircle : public sShape
|
||||
{
|
||||
sCircle()
|
||||
{
|
||||
nMaxNodes = 2;
|
||||
vecNodes.reserve(nMaxNodes);
|
||||
}
|
||||
|
||||
void DrawYourself(olc::PixelGameEngine *pge) override
|
||||
{
|
||||
float fRadius = (vecNodes[0].pos - vecNodes[1].pos).mag();
|
||||
int sx, sy, ex, ey;
|
||||
WorldToScreen(vecNodes[0].pos, sx, sy);
|
||||
WorldToScreen(vecNodes[1].pos, ex, ey);
|
||||
pge->DrawLine(sx, sy, ex, ey, col, 0xFF00FF00);
|
||||
|
||||
// Note the radius is also scaled so it is drawn appropriately
|
||||
pge->DrawCircle(sx, sy, (int32_t)(fRadius * fWorldScale), col);
|
||||
}
|
||||
};
|
||||
|
||||
// BEZIER SPLINE - requires 3 nodes to be defined fully
|
||||
struct sCurve : public sShape
|
||||
{
|
||||
sCurve()
|
||||
{
|
||||
nMaxNodes = 3;
|
||||
vecNodes.reserve(nMaxNodes);
|
||||
}
|
||||
|
||||
void DrawYourself(olc::PixelGameEngine *pge) override
|
||||
{
|
||||
int sx, sy, ex, ey;
|
||||
|
||||
if (vecNodes.size() < 3)
|
||||
{
|
||||
// Can only draw line from first to second
|
||||
WorldToScreen(vecNodes[0].pos, sx, sy);
|
||||
WorldToScreen(vecNodes[1].pos, ex, ey);
|
||||
pge->DrawLine(sx, sy, ex, ey, col, 0xFF00FF00);
|
||||
}
|
||||
|
||||
if (vecNodes.size() == 3)
|
||||
{
|
||||
// Can draw line from first to second
|
||||
WorldToScreen(vecNodes[0].pos, sx, sy);
|
||||
WorldToScreen(vecNodes[1].pos, ex, ey);
|
||||
pge->DrawLine(sx, sy, ex, ey, col, 0xFF00FF00);
|
||||
|
||||
// Can draw second structural line
|
||||
WorldToScreen(vecNodes[1].pos, sx, sy);
|
||||
WorldToScreen(vecNodes[2].pos, ex, ey);
|
||||
pge->DrawLine(sx, sy, ex, ey, col, 0xFF00FF00);
|
||||
|
||||
// And bezier curve
|
||||
olc::vf2d op = vecNodes[0].pos;
|
||||
olc::vf2d np = op;
|
||||
for (float t = 0; t < 1.0f; t += 0.01f)
|
||||
{
|
||||
np = (1 - t)*(1 - t)*vecNodes[0].pos + 2 * (1 - t)*t*vecNodes[1].pos + t * t * vecNodes[2].pos;
|
||||
WorldToScreen(op, sx, sy);
|
||||
WorldToScreen(np, ex, ey);
|
||||
pge->DrawLine(sx, sy, ex, ey, col);
|
||||
op = np;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
// APPLICATION STARTS HERE
|
||||
|
||||
class Polymorphism : public olc::PixelGameEngine
|
||||
{
|
||||
public:
|
||||
Polymorphism()
|
||||
{
|
||||
sAppName = "Polymorphism";
|
||||
}
|
||||
|
||||
private:
|
||||
// Pan & Zoom variables
|
||||
olc::vf2d vOffset = { 0.0f, 0.0f };
|
||||
olc::vf2d vStartPan = { 0.0f, 0.0f };
|
||||
float fScale = 10.0f;
|
||||
float fGrid = 1.0f;
|
||||
|
||||
// Convert coordinates from World Space --> Screen Space
|
||||
void WorldToScreen(const olc::vf2d &v, int &nScreenX, int &nScreenY)
|
||||
{
|
||||
nScreenX = (int)((v.x - vOffset.x) * fScale);
|
||||
nScreenY = (int)((v.y - vOffset.y) * fScale);
|
||||
}
|
||||
|
||||
// Convert coordinates from Screen Space --> World Space
|
||||
void ScreenToWorld(int nScreenX, int nScreenY, olc::vf2d &v)
|
||||
{
|
||||
v.x = (float)(nScreenX) / fScale + vOffset.x;
|
||||
v.y = (float)(nScreenY) / fScale + vOffset.y;
|
||||
}
|
||||
|
||||
|
||||
// A pointer to a shape that is currently being defined
|
||||
// by the placment of nodes
|
||||
sShape* tempShape = nullptr;
|
||||
|
||||
// A list of pointers to all shapes which have been drawn
|
||||
// so far
|
||||
std::list<sShape*> listShapes;
|
||||
|
||||
// A pointer to a node that is currently selected. Selected
|
||||
// nodes follow the mouse cursor
|
||||
sNode *selectedNode = nullptr;
|
||||
|
||||
// "Snapped" mouse location
|
||||
olc::vf2d vCursor = { 0, 0 };
|
||||
|
||||
// NOTE! No direct instances of lines, circles, boxes or curves,
|
||||
// the application is only aware of the existence of shapes!
|
||||
// THIS IS THE POWER OF POLYMORPHISM!
|
||||
|
||||
public:
|
||||
bool OnUserCreate() override
|
||||
{
|
||||
// Configure world space (0,0) to be middle of screen space
|
||||
vOffset = { (float)(-ScreenWidth() / 2) / fScale, (float)(-ScreenHeight() / 2) / fScale };
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OnUserUpdate(float fElapsedTime) override
|
||||
{
|
||||
// Get mouse location this frame
|
||||
olc::vf2d vMouse = { (float)GetMouseX(), (float)GetMouseY() };
|
||||
|
||||
|
||||
// Handle Pan & Zoom
|
||||
if (GetMouse(2).bPressed)
|
||||
{
|
||||
vStartPan = vMouse;
|
||||
}
|
||||
|
||||
if (GetMouse(2).bHeld)
|
||||
{
|
||||
vOffset -= (vMouse - vStartPan) / fScale;
|
||||
vStartPan = vMouse;
|
||||
}
|
||||
|
||||
olc::vf2d vMouseBeforeZoom;
|
||||
ScreenToWorld((int)vMouse.x, (int)vMouse.y, vMouseBeforeZoom);
|
||||
|
||||
if (GetKey(olc::Key::Q).bHeld || GetMouseWheel() > 0)
|
||||
{
|
||||
fScale *= 1.1f;
|
||||
}
|
||||
|
||||
if (GetKey(olc::Key::A).bHeld || GetMouseWheel() < 0)
|
||||
{
|
||||
fScale *= 0.9f;
|
||||
}
|
||||
|
||||
olc::vf2d vMouseAfterZoom;
|
||||
ScreenToWorld((int)vMouse.x, (int)vMouse.y, vMouseAfterZoom);
|
||||
vOffset += (vMouseBeforeZoom - vMouseAfterZoom);
|
||||
|
||||
|
||||
// Snap mouse cursor to nearest grid interval
|
||||
vCursor.x = floorf((vMouseAfterZoom.x + 0.5f) * fGrid);
|
||||
vCursor.y = floorf((vMouseAfterZoom.y + 0.5f) * fGrid);
|
||||
|
||||
|
||||
if (GetKey(olc::Key::L).bPressed)
|
||||
{
|
||||
tempShape = new sLine();
|
||||
|
||||
// Place first node at location of keypress
|
||||
selectedNode = tempShape->GetNextNode(vCursor);
|
||||
|
||||
// Get Second node
|
||||
selectedNode = tempShape->GetNextNode(vCursor);
|
||||
}
|
||||
|
||||
|
||||
if (GetKey(olc::Key::B).bPressed)
|
||||
{
|
||||
tempShape = new sBox();
|
||||
|
||||
// Place first node at location of keypress
|
||||
selectedNode = tempShape->GetNextNode(vCursor);
|
||||
|
||||
// Get Second node
|
||||
selectedNode = tempShape->GetNextNode(vCursor);
|
||||
}
|
||||
|
||||
if (GetKey(olc::Key::C).bPressed)
|
||||
{
|
||||
// Create new shape as a temporary
|
||||
tempShape = new sCircle();
|
||||
|
||||
// Place first node at location of keypress
|
||||
selectedNode = tempShape->GetNextNode(vCursor);
|
||||
|
||||
// Get Second node
|
||||
selectedNode = tempShape->GetNextNode(vCursor);
|
||||
}
|
||||
|
||||
if (GetKey(olc::Key::S).bPressed)
|
||||
{
|
||||
// Create new shape as a temporary
|
||||
tempShape = new sCurve();
|
||||
|
||||
// Place first node at location of keypress
|
||||
selectedNode = tempShape->GetNextNode(vCursor);
|
||||
|
||||
// Get Second node
|
||||
selectedNode = tempShape->GetNextNode(vCursor);
|
||||
}
|
||||
|
||||
// Search for any node that exists under the cursor, if one
|
||||
// is found then select it
|
||||
if (GetKey(olc::Key::M).bPressed)
|
||||
{
|
||||
selectedNode = nullptr;
|
||||
for (auto &shape : listShapes)
|
||||
{
|
||||
selectedNode = shape->HitNode(vCursor);
|
||||
if (selectedNode != nullptr)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// If a node is selected, make it follow the mouse cursor
|
||||
// by updating its position
|
||||
if (selectedNode != nullptr)
|
||||
{
|
||||
selectedNode->pos = vCursor;
|
||||
}
|
||||
|
||||
|
||||
// As the user left clicks to place nodes, the shape can grow
|
||||
// until it requires no more nodes, at which point it is completed
|
||||
// and added to the list of completed shapes.
|
||||
if (GetMouse(0).bReleased)
|
||||
{
|
||||
if (tempShape != nullptr)
|
||||
{
|
||||
selectedNode = tempShape->GetNextNode(vCursor);
|
||||
if (selectedNode == nullptr)
|
||||
{
|
||||
tempShape->col = olc::WHITE;
|
||||
listShapes.push_back(tempShape);
|
||||
tempShape = nullptr; // Thanks @howlevergreen /Disord
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
selectedNode = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Clear Screen
|
||||
Clear(olc::VERY_DARK_BLUE);
|
||||
|
||||
int sx, sy;
|
||||
int ex, ey;
|
||||
|
||||
// Get visible world
|
||||
olc::vf2d vWorldTopLeft, vWorldBottomRight;
|
||||
ScreenToWorld(0, 0, vWorldTopLeft);
|
||||
ScreenToWorld(ScreenWidth(), ScreenHeight(), vWorldBottomRight);
|
||||
|
||||
// Get values just beyond screen boundaries
|
||||
vWorldTopLeft.x = floor(vWorldTopLeft.x);
|
||||
vWorldTopLeft.y = floor(vWorldTopLeft.y);
|
||||
vWorldBottomRight.x = ceil(vWorldBottomRight.x);
|
||||
vWorldBottomRight.y = ceil(vWorldBottomRight.y);
|
||||
|
||||
// Draw Grid dots
|
||||
for (float x = vWorldTopLeft.x; x < vWorldBottomRight.x; x += fGrid)
|
||||
{
|
||||
for (float y = vWorldTopLeft.y; y < vWorldBottomRight.y; y += fGrid)
|
||||
{
|
||||
WorldToScreen({ x, y }, sx, sy);
|
||||
Draw(sx, sy, olc::BLUE);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw World Axis
|
||||
WorldToScreen({ 0,vWorldTopLeft.y }, sx, sy);
|
||||
WorldToScreen({ 0,vWorldBottomRight.y }, ex, ey);
|
||||
DrawLine(sx, sy, ex, ey, olc::GREY, 0xF0F0F0F0);
|
||||
WorldToScreen({ vWorldTopLeft.x,0 }, sx, sy);
|
||||
WorldToScreen({ vWorldBottomRight.x,0 }, ex, ey);
|
||||
DrawLine(sx, sy, ex, ey, olc::GREY, 0xF0F0F0F0);
|
||||
|
||||
// Update shape translation coefficients
|
||||
sShape::fWorldScale = fScale;
|
||||
sShape::vWorldOffset = vOffset;
|
||||
|
||||
// Draw All Existing Shapes
|
||||
for (auto &shape : listShapes)
|
||||
{
|
||||
shape->DrawYourself(this);
|
||||
shape->DrawNodes(this);
|
||||
}
|
||||
|
||||
// Draw shape currently being defined
|
||||
if (tempShape != nullptr)
|
||||
{
|
||||
tempShape->DrawYourself(this);
|
||||
tempShape->DrawNodes(this);
|
||||
}
|
||||
|
||||
// Draw "Snapped" Cursor
|
||||
WorldToScreen(vCursor, sx, sy);
|
||||
DrawCircle(sx, sy, 3, olc::YELLOW);
|
||||
|
||||
// Draw Cursor Position
|
||||
DrawString(10, 10, "X=" + std::to_string(vCursor.x) + ", Y=" + std::to_string(vCursor.x), olc::YELLOW, 2);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
Polymorphism demo;
|
||||
if (demo.Construct(800, 480, 1, 1, false))
|
||||
demo.Start();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@ -1,375 +0,0 @@
|
||||
/*
|
||||
Procedural Generation: Programming The Universe
|
||||
"Here we go again! Year 4 begins now..." - javidx9
|
||||
|
||||
License (OLC-3)
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Copyright 2018-2020 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.
|
||||
|
||||
Relevant Video: https://youtu.be/ZZY9YE7rZJw
|
||||
|
||||
Links
|
||||
~~~~~
|
||||
YouTube: https://www.youtube.com/javidx9
|
||||
https://www.youtube.com/javidx9extra
|
||||
Discord: https://discord.gg/WhwHUMV
|
||||
Twitter: https://www.twitter.com/javidx9
|
||||
Twitch: https://www.twitch.tv/javidx9
|
||||
GitHub: https://www.github.com/onelonecoder
|
||||
Patreon: https://www.patreon.com/javidx9
|
||||
Homepage: https://www.onelonecoder.com
|
||||
|
||||
Author
|
||||
~~~~~~
|
||||
David Barr, aka javidx9, ©OneLoneCoder 2018, 2019, 2020
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#define OLC_PGE_APPLICATION
|
||||
#include "olcPixelGameEngine.h"
|
||||
|
||||
#include <random>
|
||||
|
||||
constexpr uint32_t g_starColours[8] =
|
||||
{
|
||||
0xFFFFFFFF, 0xFFD9FFFF, 0xFFA3FFFF, 0xFFFFC8C8,
|
||||
0xFFFFCB9D, 0xFF9F9FFF, 0xFF415EFF, 0xFF28199D
|
||||
};
|
||||
|
||||
|
||||
struct sPlanet
|
||||
{
|
||||
double distance = 0.0;
|
||||
double diameter = 0.0;
|
||||
double foliage = 0.0;
|
||||
double minerals = 0.0;
|
||||
double water = 0.0;
|
||||
double gases = 0.0;
|
||||
double temperature = 0.0;
|
||||
double population = 0.0;
|
||||
bool ring = false;
|
||||
std::vector<double> vMoons;
|
||||
};
|
||||
|
||||
class cStarSystem
|
||||
{
|
||||
public:
|
||||
cStarSystem(uint32_t x, uint32_t y, bool bGenerateFullSystem = false)
|
||||
{
|
||||
// Set seed based on location of star system
|
||||
nProcGen = (x & 0xFFFF) << 16 | (y & 0xFFFF);
|
||||
|
||||
// Not all locations contain a system
|
||||
starExists = (rndInt(0, 20) == 1);
|
||||
if (!starExists) return;
|
||||
|
||||
// Generate Star
|
||||
starDiameter = rndDouble(10.0, 40.0);
|
||||
starColour.n = g_starColours[rndInt(0, 8)];
|
||||
|
||||
// When viewing the galaxy map, we only care about the star
|
||||
// so abort early
|
||||
if (!bGenerateFullSystem) return;
|
||||
|
||||
// If we are viewing the system map, we need to generate the
|
||||
// full system
|
||||
|
||||
// Generate Planets
|
||||
double dDistanceFromStar = rndDouble(60.0, 200.0);
|
||||
int nPlanets = rndInt(0, 10);
|
||||
for (int i = 0; i < nPlanets; i++)
|
||||
{
|
||||
sPlanet p;
|
||||
p.distance = dDistanceFromStar;
|
||||
dDistanceFromStar += rndDouble(20.0, 200.0);
|
||||
p.diameter = rndDouble(4.0, 20.0);
|
||||
|
||||
// Could make temeprature a function of distance from star
|
||||
p.temperature = rndDouble(-200.0, 300.0);
|
||||
|
||||
// Composition of planet
|
||||
p.foliage = rndDouble(0.0, 1.0);
|
||||
p.minerals = rndDouble(0.0, 1.0);
|
||||
p.gases = rndDouble(0.0, 1.0);
|
||||
p.water = rndDouble(0.0, 1.0);
|
||||
|
||||
// Normalise to 100%
|
||||
double dSum = 1.0 / (p.foliage + p.minerals + p.gases + p.water);
|
||||
p.foliage *= dSum;
|
||||
p.minerals *= dSum;
|
||||
p.gases *= dSum;
|
||||
p.water *= dSum;
|
||||
|
||||
// Population could be a function of other habitat encouraging
|
||||
// properties, such as temperature and water
|
||||
p.population = std::max(rndInt(-5000000, 20000000), 0);
|
||||
|
||||
// 10% of planets have a ring
|
||||
p.ring = rndInt(0, 10) == 1;
|
||||
|
||||
// Satellites (Moons)
|
||||
int nMoons = std::max(rndInt(-5, 5), 0);
|
||||
for (int n = 0; n < nMoons; n++)
|
||||
{
|
||||
// A moon is just a diameter for now, but it could be
|
||||
// whatever you want!
|
||||
p.vMoons.push_back(rndDouble(1.0, 5.0));
|
||||
}
|
||||
|
||||
// Add planet to vector
|
||||
vPlanets.push_back(p);
|
||||
}
|
||||
}
|
||||
|
||||
~cStarSystem()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public:
|
||||
std::vector<sPlanet> vPlanets;
|
||||
|
||||
public:
|
||||
bool starExists = false;
|
||||
double starDiameter = 0.0f;
|
||||
olc::Pixel starColour = olc::WHITE;
|
||||
|
||||
private:
|
||||
uint32_t nProcGen = 0;
|
||||
|
||||
double rndDouble(double min, double max)
|
||||
{
|
||||
return ((double)rnd() / (double)(0x7FFFFFFF)) * (max - min) + min;
|
||||
}
|
||||
|
||||
int rndInt(int min, int max)
|
||||
{
|
||||
return (rnd() % (max - min)) + min;
|
||||
}
|
||||
|
||||
// Modified from this for 64-bit systems:
|
||||
// https://lemire.me/blog/2019/03/19/the-fastest-conventional-random-number-generator-that-can-pass-big-crush/
|
||||
// Now I found the link again - Also, check out his blog, it's a fantastic resource!
|
||||
uint32_t rnd()
|
||||
{
|
||||
nProcGen += 0xe120fc15;
|
||||
uint64_t tmp;
|
||||
tmp = (uint64_t)nProcGen * 0x4a39b70d;
|
||||
uint32_t m1 = (tmp >> 32) ^ tmp;
|
||||
tmp = (uint64_t)m1 * 0x12fad5c9;
|
||||
uint32_t m2 = (tmp >> 32) ^ tmp;
|
||||
return m2;
|
||||
}
|
||||
};
|
||||
|
||||
class olcGalaxy : public olc::PixelGameEngine
|
||||
{
|
||||
public:
|
||||
olcGalaxy()
|
||||
{
|
||||
sAppName = "olcGalaxy";
|
||||
}
|
||||
|
||||
public:
|
||||
bool OnUserCreate() override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
olc::vf2d vGalaxyOffset = { 0,0 };
|
||||
bool bStarSelected = false;
|
||||
uint32_t nSelectedStarSeed1 = 0;
|
||||
uint32_t nSelectedStarSeed2 = 0;
|
||||
|
||||
|
||||
/*uint32_t nLehmer = 0;
|
||||
uint32_t Lehmer32()
|
||||
{
|
||||
nLehmer += 0xe120fc15;
|
||||
uint64_t tmp;
|
||||
tmp = (uint64_t)nLehmer * 0x4a39b70d;
|
||||
uint32_t m1 = (tmp >> 32) ^ tmp;
|
||||
tmp = (uint64_t)m1 * 0x12fad5c9;
|
||||
uint32_t m2 = (tmp >> 32) ^ tmp;
|
||||
return m2;
|
||||
}*/
|
||||
|
||||
bool OnUserUpdate(float fElapsedTime) override
|
||||
{
|
||||
if (fElapsedTime <= 0.0001f) return true;
|
||||
Clear(olc::BLACK);
|
||||
|
||||
//if (GetKey(olc::SPACE).bReleased)
|
||||
//{
|
||||
|
||||
// //srand(1000);
|
||||
|
||||
// std::random_device rd;
|
||||
// std::mt19937 mt(1000);
|
||||
// std::uniform_int_distribution<int> dist(0, 256);
|
||||
|
||||
// auto tp1 = std::chrono::system_clock::now();
|
||||
// // Ranomness Tests
|
||||
// for (int x = 0; x < ScreenWidth(); x++)
|
||||
// {
|
||||
// for (int y = 0; y < ScreenHeight(); y++)
|
||||
// {
|
||||
// bool bIsStar = false;
|
||||
// int nSeed = y << 16 | x;
|
||||
//
|
||||
// // Standard C++ rand()
|
||||
// //srand(nSeed);
|
||||
// //bIsStar = rand() % 256 < 32;
|
||||
|
||||
// // std::random
|
||||
// //mt.seed(nSeed);
|
||||
// //bIsStar = dist(mt) < 32;
|
||||
|
||||
// // Lehmer32
|
||||
// nLehmer = nSeed;
|
||||
// bIsStar = Lehmer32() % 256 < 32;
|
||||
|
||||
// Draw(x, y, bIsStar ? olc::WHITE : olc::BLACK);
|
||||
// }
|
||||
// }
|
||||
// auto tp2 = std::chrono::system_clock::now();
|
||||
// std::chrono::duration<float> elapsedTime = tp2 - tp1;
|
||||
// DrawString(3, 3, "Time: " + std::to_string(elapsedTime.count()), olc::RED, 2);
|
||||
//}
|
||||
|
||||
|
||||
//return true;
|
||||
|
||||
|
||||
if (GetKey(olc::W).bHeld) vGalaxyOffset.y -= 50.0f * fElapsedTime;
|
||||
if (GetKey(olc::S).bHeld) vGalaxyOffset.y += 50.0f * fElapsedTime;
|
||||
if (GetKey(olc::A).bHeld) vGalaxyOffset.x -= 50.0f * fElapsedTime;
|
||||
if (GetKey(olc::D).bHeld) vGalaxyOffset.x += 50.0f * fElapsedTime;
|
||||
|
||||
int nSectorsX = ScreenWidth() / 16;
|
||||
int nSectorsY = ScreenHeight() / 16;
|
||||
|
||||
olc::vi2d mouse = { GetMouseX() / 16, GetMouseY() / 16 };
|
||||
olc::vi2d galaxy_mouse = mouse + vGalaxyOffset;
|
||||
olc::vi2d screen_sector = { 0,0 };
|
||||
|
||||
for (screen_sector.x = 0; screen_sector.x < nSectorsX; screen_sector.x++)
|
||||
for (screen_sector.y = 0; screen_sector.y < nSectorsY; screen_sector.y++)
|
||||
{
|
||||
uint32_t seed1 = (uint32_t)vGalaxyOffset.x + (uint32_t)screen_sector.x;
|
||||
uint32_t seed2 = (uint32_t)vGalaxyOffset.y + (uint32_t)screen_sector.y;
|
||||
|
||||
cStarSystem star(seed1, seed2);
|
||||
if (star.starExists)
|
||||
{
|
||||
FillCircle(screen_sector.x * 16 + 8, screen_sector.y * 16 + 8,
|
||||
(int)star.starDiameter / 8, star.starColour);
|
||||
|
||||
// For convenience highlight hovered star
|
||||
if (mouse.x == screen_sector.x && mouse.y == screen_sector.y)
|
||||
{
|
||||
DrawCircle(screen_sector.x * 16 + 8, screen_sector.y * 16 + 8, 12, olc::YELLOW);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle Mouse Click
|
||||
if (GetMouse(0).bPressed)
|
||||
{
|
||||
uint32_t seed1 = (uint32_t)vGalaxyOffset.x + (uint32_t)mouse.x;
|
||||
uint32_t seed2 = (uint32_t)vGalaxyOffset.y + (uint32_t)mouse.y;
|
||||
|
||||
cStarSystem star(seed1, seed2);
|
||||
if (star.starExists)
|
||||
{
|
||||
bStarSelected = true;
|
||||
nSelectedStarSeed1 = seed1;
|
||||
nSelectedStarSeed2 = seed2;
|
||||
}
|
||||
else
|
||||
bStarSelected = false;
|
||||
}
|
||||
|
||||
// Draw Details of selected star system
|
||||
if (bStarSelected)
|
||||
{
|
||||
// Generate full star system
|
||||
cStarSystem star(nSelectedStarSeed1, nSelectedStarSeed2, true);
|
||||
|
||||
// Draw Window
|
||||
FillRect(8, 240, 496, 232, olc::DARK_BLUE);
|
||||
DrawRect(8, 240, 496, 232, olc::WHITE);
|
||||
|
||||
// Draw Star
|
||||
olc::vi2d vBody = { 14, 356 };
|
||||
|
||||
vBody.x += star.starDiameter * 1.375;
|
||||
FillCircle(vBody, (int)(star.starDiameter * 1.375), star.starColour);
|
||||
vBody.x += (star.starDiameter * 1.375) + 8;
|
||||
|
||||
|
||||
|
||||
// Draw Planets
|
||||
for (auto& planet : star.vPlanets)
|
||||
{
|
||||
if (vBody.x + planet.diameter >= 496) break;
|
||||
|
||||
vBody.x += planet.diameter;
|
||||
FillCircle(vBody, (int)(planet.diameter * 1.0), olc::RED);
|
||||
|
||||
olc::vi2d vMoon = vBody;
|
||||
vMoon.y += planet.diameter + 10;
|
||||
|
||||
// Draw Moons
|
||||
for (auto& moon : planet.vMoons)
|
||||
{
|
||||
vMoon.y += moon;
|
||||
FillCircle(vMoon, (int)(moon * 1.0), olc::GREY);
|
||||
vMoon.y += moon + 10;
|
||||
}
|
||||
|
||||
vBody.x += planet.diameter + 8;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
olcGalaxy demo;
|
||||
if (demo.Construct(512, 480, 2, 2, false, false))
|
||||
demo.Start();
|
||||
return 0;
|
||||
}
|
||||
@ -1,493 +0,0 @@
|
||||
/*
|
||||
Quirky Quad Trees Part #1 - Static Quad Tree Implementation
|
||||
"War... huh... What is it good for? Absolutely nothin..." - javidx9
|
||||
|
||||
License (OLC-3)
|
||||
~~~~~~~~~~~~~~~
|
||||
Copyright 2018 - 2022 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.
|
||||
|
||||
Video:
|
||||
~~~~~~
|
||||
https://youtu.be/ASAowY6yJII
|
||||
|
||||
Pan & Zoom with middle mouse, TAB to switch between methods
|
||||
|
||||
Links
|
||||
~~~~~
|
||||
YouTube: https://www.youtube.com/javidx9
|
||||
https://www.youtube.com/javidx9extra
|
||||
Discord: https://discord.gg/WhwHUMV
|
||||
Twitter: https://www.twitter.com/javidx9
|
||||
Twitch: https://www.twitch.tv/javidx9
|
||||
GitHub: https://www.github.com/onelonecoder
|
||||
Homepage: https://www.onelonecoder.com
|
||||
|
||||
Author
|
||||
~~~~~~
|
||||
David Barr, aka javidx9, ©OneLoneCoder 2019, 2020, 2021, 2022
|
||||
*/
|
||||
|
||||
|
||||
#define OLC_PGE_APPLICATION
|
||||
#include "olcPixelGameEngine.h"
|
||||
|
||||
#define OLC_PGEX_TRANSFORMEDVIEW
|
||||
#include "olcPGEX_TransformedView.h"
|
||||
|
||||
namespace olc
|
||||
{
|
||||
struct rect
|
||||
{
|
||||
olc::vf2d pos;
|
||||
olc::vf2d size;
|
||||
|
||||
rect(const olc::vf2d& p = { 0.0f, 0.0f }, const olc::vf2d& s = { 1.0f, 1.0f }) : pos(p), size(s)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
constexpr bool contains(const olc::vf2d& p) const
|
||||
{
|
||||
return !(p.x < pos.x || p.y < pos.y || p.x >= (pos.x + size.x) || p.y >= (pos.y + size.y));
|
||||
}
|
||||
|
||||
constexpr bool contains(const olc::rect& r) const
|
||||
{
|
||||
return (r.pos.x >= pos.x) && (r.pos.x + r.size.x < pos.x + size.x) &&
|
||||
(r.pos.y >= pos.y) && (r.pos.y + r.size.y < pos.y + size.y);
|
||||
}
|
||||
|
||||
constexpr bool overlaps(const olc::rect& r) const
|
||||
{
|
||||
return (pos.x < r.pos.x + r.size.x && pos.x + size.x >= r.pos.x && pos.y < r.pos.y + r.size.y && pos.y + size.y >= r.pos.y);
|
||||
}
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
|
||||
// Constrain depth of Quad Tree. Since its floating point, it could in principle sub-divide for
|
||||
// a very long time, consuming far more time and memory than is sensible
|
||||
constexpr size_t MAX_DEPTH = 8;
|
||||
|
||||
|
||||
template <typename OBJECT_TYPE>
|
||||
class StaticQuadTree
|
||||
{
|
||||
public:
|
||||
StaticQuadTree(const olc::rect& size = { {0.0f, 0.0f}, {100.0f, 100.0f} }, const size_t nDepth = 0)
|
||||
{
|
||||
m_depth = nDepth;
|
||||
resize(size);
|
||||
}
|
||||
|
||||
// Force area change on Tree, invalidates this and all child layers
|
||||
void resize(const olc::rect& rArea)
|
||||
{
|
||||
// Erase this layer
|
||||
clear();
|
||||
|
||||
// Recalculate area of children
|
||||
m_rect = rArea;
|
||||
olc::vf2d vChildSize = m_rect.size / 2.0f;
|
||||
|
||||
// Cache child areas local to this layer
|
||||
m_rChild =
|
||||
{
|
||||
// Top Left
|
||||
olc::rect(m_rect.pos, vChildSize),
|
||||
// Top Right
|
||||
olc::rect({m_rect.pos.x + vChildSize.x, m_rect.pos.y}, vChildSize),
|
||||
// Bottom Left
|
||||
olc::rect({m_rect.pos.x, m_rect.pos.y + vChildSize.y}, vChildSize),
|
||||
// Bottom Right
|
||||
olc::rect(m_rect.pos + vChildSize, vChildSize)
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
// Clears the contents of this layer, and all child layers
|
||||
void clear()
|
||||
{
|
||||
// Erase any items stored in this layer
|
||||
m_pItems.clear();
|
||||
|
||||
// Iterate through children, erase them too
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
if (m_pChild[i])
|
||||
m_pChild[i]->clear();
|
||||
m_pChild[i].reset();
|
||||
}
|
||||
}
|
||||
|
||||
// Returns a count of how many items are stored in this layer, and all children of this layer
|
||||
size_t size() const
|
||||
{
|
||||
size_t nCount = m_pItems.size();
|
||||
for (int i = 0; i < 4; i++)
|
||||
if (m_pChild[i]) nCount += m_pChild[i]->size();
|
||||
return nCount;
|
||||
}
|
||||
|
||||
// Inserts an object into this layer (or appropriate child layer), given the area the item occupies
|
||||
void insert(const OBJECT_TYPE& item, const olc::rect& itemsize)
|
||||
{
|
||||
// Check each child
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
// If the child can wholly contain the item being inserted
|
||||
if (m_rChild[i].contains(itemsize))
|
||||
{
|
||||
// Have we reached depth limit?
|
||||
if (m_depth + 1 < MAX_DEPTH)
|
||||
{
|
||||
// No, so does child exist?
|
||||
if (!m_pChild[i])
|
||||
{
|
||||
// No, so create it
|
||||
m_pChild[i] = std::make_shared<StaticQuadTree<OBJECT_TYPE>>(m_rChild[i], m_depth + 1);
|
||||
}
|
||||
|
||||
// Yes, so add item to it
|
||||
m_pChild[i]->insert(item, itemsize);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// It didnt fit, so item must belong to this quad
|
||||
m_pItems.push_back({ itemsize, item });
|
||||
}
|
||||
|
||||
// Returns a list of objects in the given search area
|
||||
std::list<OBJECT_TYPE> search(const olc::rect& rArea) const
|
||||
{
|
||||
std::list<OBJECT_TYPE> listItems;
|
||||
search(rArea, listItems);
|
||||
return listItems;
|
||||
}
|
||||
|
||||
// Returns the objects in the given search area, by adding to supplied list
|
||||
void search(const olc::rect& rArea, std::list<OBJECT_TYPE>& listItems) const
|
||||
{
|
||||
// First, check for items belonging to this area, add them to the list
|
||||
// if there is overlap
|
||||
for (const auto& p : m_pItems)
|
||||
{
|
||||
if (rArea.overlaps(p.first))
|
||||
listItems.push_back(p.second);
|
||||
}
|
||||
|
||||
// Second, recurse through children and see if they can
|
||||
// add to the list
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
if (m_pChild[i])
|
||||
{
|
||||
// If child is entirely contained within area, recursively
|
||||
// add all of its children, no need to check boundaries
|
||||
if (rArea.contains(m_rChild[i]))
|
||||
m_pChild[i]->items(listItems);
|
||||
|
||||
// If child overlaps with search area then checks need
|
||||
// to be made
|
||||
else if (m_rChild[i].overlaps(rArea))
|
||||
m_pChild[i]->search(rArea, listItems);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void items(std::list<OBJECT_TYPE>& listItems) const
|
||||
{
|
||||
// No questions asked, just return child items
|
||||
for (const auto& p : m_pItems)
|
||||
listItems.push_back(p.second);
|
||||
|
||||
// Now add children of this layer's items
|
||||
for (int i = 0; i < 4; i++)
|
||||
if (m_pChild[i]) m_pChild[i]->items(listItems);
|
||||
}
|
||||
|
||||
|
||||
std::list<OBJECT_TYPE> items() const
|
||||
{
|
||||
// No questions asked, just return child items
|
||||
std::list<OBJECT_TYPE> listItems;
|
||||
items(listItems);
|
||||
return listItems;
|
||||
}
|
||||
|
||||
// Returns area of this layer
|
||||
const olc::rect& area()
|
||||
{
|
||||
return m_rect;
|
||||
}
|
||||
|
||||
|
||||
protected:
|
||||
// Depth of this StaticQuadTree layer
|
||||
size_t m_depth = 0;
|
||||
|
||||
// Area of this StaticQuadTree
|
||||
olc::rect m_rect;
|
||||
|
||||
// 4 child areas of this StaticQuadTree
|
||||
std::array<olc::rect, 4> m_rChild{};
|
||||
|
||||
// 4 potential children of this StaticQuadTree
|
||||
std::array<std::shared_ptr<StaticQuadTree<OBJECT_TYPE>>, 4> m_pChild{};
|
||||
|
||||
// Items which belong to this StaticQuadTree
|
||||
std::vector<std::pair<olc::rect, OBJECT_TYPE>> m_pItems;
|
||||
};
|
||||
|
||||
|
||||
template <typename OBJECT_TYPE>
|
||||
class StaticQuadTreeContainer
|
||||
{
|
||||
// Using a std::list as we dont want pointers to be invalidated to objects stored in the
|
||||
// tree should the contents of the tree change
|
||||
using QuadTreeContainer = std::list<OBJECT_TYPE>;
|
||||
|
||||
protected:
|
||||
// The actual container
|
||||
QuadTreeContainer m_allItems;
|
||||
|
||||
// Use our StaticQuadTree to store "pointers" instead of objects - this reduces
|
||||
// overheads when moving or copying objects
|
||||
StaticQuadTree<typename QuadTreeContainer::iterator> root;
|
||||
|
||||
public:
|
||||
StaticQuadTreeContainer(const olc::rect& size = { {0.0f, 0.0f}, { 100.0f, 100.0f } }, const size_t nDepth = 0) : root(size, nDepth)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// Sets the spatial coverage area of the quadtree
|
||||
// Invalidates tree
|
||||
void resize(const olc::rect& rArea)
|
||||
{
|
||||
root.resize(rArea);
|
||||
}
|
||||
|
||||
// Returns number of items within tree
|
||||
size_t size() const
|
||||
{
|
||||
return m_allItems.size();
|
||||
}
|
||||
|
||||
// Returns true if tree is empty
|
||||
bool empty() const
|
||||
{
|
||||
return m_allItems.empty();
|
||||
}
|
||||
|
||||
// Removes all items from tree
|
||||
void clear()
|
||||
{
|
||||
root.clear();
|
||||
m_allItems.clear();
|
||||
}
|
||||
|
||||
|
||||
// Convenience functions for ranged for loop
|
||||
typename QuadTreeContainer::iterator begin()
|
||||
{
|
||||
return m_allItems.begin();
|
||||
}
|
||||
|
||||
typename QuadTreeContainer::iterator end()
|
||||
{
|
||||
return m_allItems.end();
|
||||
}
|
||||
|
||||
typename QuadTreeContainer::const_iterator cbegin()
|
||||
{
|
||||
return m_allItems.cbegin();
|
||||
}
|
||||
|
||||
typename QuadTreeContainer::const_iterator cend()
|
||||
{
|
||||
return m_allItems.cend();
|
||||
}
|
||||
|
||||
|
||||
// Insert item into tree in specified area
|
||||
void insert(const OBJECT_TYPE& item, const olc::rect& itemsize)
|
||||
{
|
||||
// Item is stored in container
|
||||
m_allItems.push_back(item);
|
||||
|
||||
// Pointer/Area of item is stored in quad tree
|
||||
root.insert(std::prev(m_allItems.end()), itemsize);
|
||||
}
|
||||
|
||||
// Returns a std::list of pointers to items within the search area
|
||||
std::list<typename QuadTreeContainer::iterator> search(const olc::rect& rArea) const
|
||||
{
|
||||
std::list<typename QuadTreeContainer::iterator> listItemPointers;
|
||||
root.search(rArea, listItemPointers);
|
||||
return listItemPointers;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
// The Example!
|
||||
class Example_StaticQuadTree : public olc::PixelGameEngine
|
||||
{
|
||||
public:
|
||||
Example_StaticQuadTree()
|
||||
{
|
||||
sAppName = "Static QuadTree";
|
||||
}
|
||||
|
||||
protected:
|
||||
olc::TransformedView tv;
|
||||
|
||||
// An example object of something in 2D space
|
||||
struct SomeObjectWithArea
|
||||
{
|
||||
olc::vf2d vPos;
|
||||
olc::vf2d vVel;
|
||||
olc::vf2d vSize;
|
||||
olc::Pixel colour;
|
||||
};
|
||||
|
||||
// A regular list of the objects
|
||||
std::list<SomeObjectWithArea> vecObjects;
|
||||
|
||||
// An equivalent quad tree of the objects
|
||||
StaticQuadTreeContainer<SomeObjectWithArea> treeObjects;
|
||||
|
||||
// The "length" of one side of the "world" the objects reside in
|
||||
float fArea = 100000.0f;
|
||||
|
||||
bool bUseQuadTree = true;
|
||||
|
||||
public:
|
||||
bool OnUserCreate() override
|
||||
{
|
||||
// Transform View - enables Pan & Zoom
|
||||
tv.Initialise({ ScreenWidth(), ScreenHeight() });
|
||||
|
||||
// Create the tree, and size it to the world
|
||||
treeObjects.resize(olc::rect({ 0.0f, 0.0f }, { fArea, fArea }));
|
||||
|
||||
|
||||
// Dirty random float generator
|
||||
auto rand_float = [](const float a, const float b)
|
||||
{
|
||||
return float(rand()) / float(RAND_MAX) * (b - a) + a;
|
||||
};
|
||||
|
||||
|
||||
// Create 1,000,000 objects, push into both containers (so 2,000,000 I suppose :P )
|
||||
for (int i = 0; i < 1000000; i++)
|
||||
{
|
||||
SomeObjectWithArea ob;
|
||||
ob.vPos = { rand_float(0.0f, fArea), rand_float(0.0f, fArea) };
|
||||
ob.vSize = { rand_float(0.1f, 100.0f), rand_float(0.1f, 100.0f) };
|
||||
ob.colour = olc::Pixel(rand() % 256, rand() % 256, rand() % 256);
|
||||
|
||||
treeObjects.insert(ob, olc::rect(ob.vPos, ob.vSize));
|
||||
vecObjects.push_back(ob);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OnUserUpdate(float fElapsedTime) override
|
||||
{
|
||||
// Tab switches between modes
|
||||
if (GetKey(olc::Key::TAB).bPressed)
|
||||
bUseQuadTree = !bUseQuadTree;
|
||||
|
||||
tv.HandlePanAndZoom();
|
||||
|
||||
// Get rectangle that equates to screen in world space
|
||||
olc::rect rScreen = { tv.GetWorldTL(), tv.GetWorldBR() - tv.GetWorldTL() };
|
||||
size_t nObjectCount = 0;
|
||||
|
||||
if (bUseQuadTree)
|
||||
{
|
||||
// QUAD TREE MODE
|
||||
auto tpStart = std::chrono::system_clock::now();
|
||||
|
||||
// Use search function to return list of pointers to objects in that area
|
||||
for (const auto& object : treeObjects.search(rScreen))
|
||||
{
|
||||
tv.FillRectDecal(object->vPos, object->vSize, object->colour);
|
||||
nObjectCount++;
|
||||
}
|
||||
std::chrono::duration<float> duration = std::chrono::system_clock::now() - tpStart;
|
||||
|
||||
|
||||
std::string sOutput = "Quadtree " + std::to_string(nObjectCount) + "/" + std::to_string(vecObjects.size()) + " in " + std::to_string(duration.count());
|
||||
DrawStringDecal({ 4, 4 }, sOutput, olc::BLACK, { 4.0f, 8.0f });
|
||||
DrawStringDecal({ 2, 2 }, sOutput, olc::WHITE, { 4.0f, 8.0f });
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// LINEAR SEARCH MODE
|
||||
auto tpStart = std::chrono::system_clock::now();
|
||||
|
||||
// Blindly check all objects to see if they overlap with screen
|
||||
for (const auto& object : vecObjects)
|
||||
{
|
||||
if (rScreen.overlaps({ object.vPos, object.vSize }))
|
||||
{
|
||||
tv.FillRectDecal(object.vPos, object.vSize, object.colour);
|
||||
nObjectCount++;
|
||||
}
|
||||
}
|
||||
std::chrono::duration<float> duration = std::chrono::system_clock::now() - tpStart;
|
||||
|
||||
std::string sOutput = "Linear " + std::to_string(nObjectCount) + "/" + std::to_string(vecObjects.size()) + " in " + std::to_string(duration.count());
|
||||
DrawStringDecal({ 4, 4 }, sOutput, olc::BLACK, { 4.0f, 8.0f });
|
||||
DrawStringDecal({ 2, 2 }, sOutput, olc::WHITE, { 4.0f, 8.0f });
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
Example_StaticQuadTree demo;
|
||||
if (demo.Construct(1280, 960, 1, 1, false, false))
|
||||
demo.Start();
|
||||
return 0;
|
||||
}
|
||||
@ -1,215 +0,0 @@
|
||||
/*
|
||||
Fast Ray Casting Using DDA
|
||||
"Itchy Eyes... Not blinking enough..." - javidx9
|
||||
|
||||
Video: https://youtu.be/NbSee-XM7WA
|
||||
|
||||
License (OLC-3)
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Copyright 2018 - 2021 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
|
||||
https://www.youtube.com/javidx9extra
|
||||
Discord: https://discord.gg/WhwHUMV
|
||||
Twitter: https://www.twitter.com/javidx9
|
||||
Twitch: https://www.twitch.tv/javidx9
|
||||
GitHub: https://www.github.com/onelonecoder
|
||||
Homepage: https://www.onelonecoder.com
|
||||
|
||||
Author
|
||||
~~~~~~
|
||||
David Barr, aka javidx9, ©OneLoneCoder 2019, 2020, 2021
|
||||
*/
|
||||
|
||||
#define OLC_PGE_APPLICATION
|
||||
#include "olcPixelGameEngine.h"
|
||||
|
||||
class Example : public olc::PixelGameEngine
|
||||
{
|
||||
public:
|
||||
Example()
|
||||
{
|
||||
sAppName = "RayCast With DDA Algorithm";
|
||||
}
|
||||
|
||||
olc::vf2d vPlayer = { 0,0 };
|
||||
olc::vi2d vMapSize = { 32, 30 };
|
||||
olc::vi2d vCellSize = { 16, 16 };
|
||||
std::vector<int> vecMap;
|
||||
|
||||
|
||||
public:
|
||||
bool OnUserCreate() override
|
||||
{
|
||||
// Construct Map
|
||||
vecMap.resize(vMapSize.x * vMapSize.y);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OnUserUpdate(float fElapsedTime) override
|
||||
{
|
||||
olc::vf2d vMouse = { float(GetMouseX()), float(GetMouseY()) };
|
||||
olc::vf2d vMouseCell = vMouse / vCellSize;
|
||||
olc::vi2d vCell = vMouseCell; // implicit cast to integer, rounds down
|
||||
|
||||
// Paint with right mouse button "solid" tiles
|
||||
if (GetMouse(1).bHeld) vecMap[vCell.y * vMapSize.x + vCell.x] = 1;
|
||||
|
||||
// Move "player" position
|
||||
if (GetKey(olc::Key::W).bHeld) vPlayer.y -= 25.0f * fElapsedTime;
|
||||
if (GetKey(olc::Key::S).bHeld) vPlayer.y += 25.0f * fElapsedTime;
|
||||
if (GetKey(olc::Key::A).bHeld) vPlayer.x -= 25.0f * fElapsedTime;
|
||||
if (GetKey(olc::Key::D).bHeld) vPlayer.x += 25.0f * fElapsedTime;
|
||||
|
||||
// DDA Algorithm ==============================================
|
||||
// https://lodev.org/cgtutor/raycasting.html
|
||||
|
||||
|
||||
// Form ray cast from player into scene
|
||||
olc::vf2d vRayStart = vPlayer;
|
||||
olc::vf2d vRayDir = (vMouseCell - vPlayer).norm();
|
||||
|
||||
// Lodev.org also explains this additional optimistaion (but it's beyond scope of video)
|
||||
// olc::vf2d vRayUnitStepSize = { abs(1.0f / vRayDir.x), abs(1.0f / vRayDir.y) };
|
||||
|
||||
olc::vf2d vRayUnitStepSize = { sqrt(1 + (vRayDir.y / vRayDir.x) * (vRayDir.y / vRayDir.x)), sqrt(1 + (vRayDir.x / vRayDir.y) * (vRayDir.x / vRayDir.y)) };
|
||||
olc::vi2d vMapCheck = vRayStart;
|
||||
olc::vf2d vRayLength1D;
|
||||
olc::vi2d vStep;
|
||||
|
||||
// Establish Starting Conditions
|
||||
if (vRayDir.x < 0)
|
||||
{
|
||||
vStep.x = -1;
|
||||
vRayLength1D.x = (vRayStart.x - float(vMapCheck.x)) * vRayUnitStepSize.x;
|
||||
}
|
||||
else
|
||||
{
|
||||
vStep.x = 1;
|
||||
vRayLength1D.x = (float(vMapCheck.x + 1) - vRayStart.x) * vRayUnitStepSize.x;
|
||||
}
|
||||
|
||||
if (vRayDir.y < 0)
|
||||
{
|
||||
vStep.y = -1;
|
||||
vRayLength1D.y = (vRayStart.y - float(vMapCheck.y)) * vRayUnitStepSize.y;
|
||||
}
|
||||
else
|
||||
{
|
||||
vStep.y = 1;
|
||||
vRayLength1D.y = (float(vMapCheck.y + 1) - vRayStart.y) * vRayUnitStepSize.y;
|
||||
}
|
||||
|
||||
// Perform "Walk" until collision or range check
|
||||
bool bTileFound = false;
|
||||
float fMaxDistance = 100.0f;
|
||||
float fDistance = 0.0f;
|
||||
while (!bTileFound && fDistance < fMaxDistance)
|
||||
{
|
||||
// Walk along shortest path
|
||||
if (vRayLength1D.x < vRayLength1D.y)
|
||||
{
|
||||
vMapCheck.x += vStep.x;
|
||||
fDistance = vRayLength1D.x;
|
||||
vRayLength1D.x += vRayUnitStepSize.x;
|
||||
}
|
||||
else
|
||||
{
|
||||
vMapCheck.y += vStep.y;
|
||||
fDistance = vRayLength1D.y;
|
||||
vRayLength1D.y += vRayUnitStepSize.y;
|
||||
}
|
||||
|
||||
// Test tile at new test point
|
||||
if (vMapCheck.x >= 0 && vMapCheck.x < vMapSize.x && vMapCheck.y >= 0 && vMapCheck.y < vMapSize.y)
|
||||
{
|
||||
if (vecMap[vMapCheck.y * vMapSize.x + vMapCheck.x] == 1)
|
||||
{
|
||||
bTileFound = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate intersection location
|
||||
olc::vf2d vIntersection;
|
||||
if (bTileFound)
|
||||
{
|
||||
vIntersection = vRayStart + vRayDir * fDistance;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Clear(olc::BLACK);
|
||||
|
||||
// Draw Map
|
||||
for (int y = 0; y < vMapSize.y; y++)
|
||||
{
|
||||
for (int x = 0; x < vMapSize.x; x++)
|
||||
{
|
||||
int cell = vecMap[y * vMapSize.x + x];
|
||||
if (cell == 1)
|
||||
FillRect(olc::vi2d(x, y) * vCellSize, vCellSize, olc::BLUE);
|
||||
|
||||
// Draw Cell border
|
||||
DrawRect(olc::vi2d(x, y) * vCellSize, vCellSize, olc::DARK_GREY);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw ray between player and mouse if left mouse button held
|
||||
if (GetMouse(0).bHeld)
|
||||
{
|
||||
DrawLine(vPlayer * vCellSize, vMouse, olc::WHITE, 0xF0F0F0F0);
|
||||
|
||||
if (bTileFound)
|
||||
{
|
||||
DrawCircle(vIntersection * vCellSize, 4.0f, olc::YELLOW);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw Player
|
||||
FillCircle(vPlayer * vCellSize, 4.0f, olc::RED);
|
||||
|
||||
// Draw Mouse
|
||||
FillCircle(vMouse, 4.0f, olc::GREEN);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
Example demo;
|
||||
if (demo.Construct(512, 480, 2, 2))
|
||||
demo.Start();
|
||||
return 0;
|
||||
}
|
||||
@ -1,255 +0,0 @@
|
||||
/*
|
||||
Simple example of RayCastWorld Pixel Game Engine Extension
|
||||
"My Kneeeeeeeees!!!!" - javidx9
|
||||
|
||||
License (OLC-3)
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Copyright 2018-2020 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.
|
||||
|
||||
Relevant Video: https://youtu.be/Vij_obgv9h4
|
||||
|
||||
Links
|
||||
~~~~~
|
||||
YouTube: https://www.youtube.com/javidx9
|
||||
https://www.youtube.com/javidx9extra
|
||||
Discord: https://discord.gg/WhwHUMV
|
||||
Twitter: https://www.twitter.com/javidx9
|
||||
Twitch: https://www.twitch.tv/javidx9
|
||||
GitHub: https://www.github.com/onelonecoder
|
||||
Patreon: https://www.patreon.com/javidx9
|
||||
Homepage: https://www.onelonecoder.com
|
||||
|
||||
Community Blog: https://community.onelonecoder.com
|
||||
|
||||
Author
|
||||
~~~~~~
|
||||
David Barr, aka javidx9, ©OneLoneCoder 2018, 2019, 2020
|
||||
*/
|
||||
|
||||
#define OLC_PGE_APPLICATION
|
||||
#include "olcPixelGameEngine.h"
|
||||
|
||||
#define OLC_PGEX_RAYCASTWORLD
|
||||
#include "olcPGEX_RayCastWorld.h"
|
||||
|
||||
// ADAPTOR CLASS - Override the RayCastWorld Engine and fill in the blanks!
|
||||
class ExampleGame : public olc::rcw::Engine
|
||||
{
|
||||
public:
|
||||
ExampleGame(const int screen_w, const int screen_h, const float fov)
|
||||
: olc::rcw::Engine(screen_w, screen_h, fov)
|
||||
{
|
||||
sMap =
|
||||
"################################################################"
|
||||
"#.........#....................##..............................#"
|
||||
"#.........#....................................................#"
|
||||
"#.........#....................................................#"
|
||||
"#.........#....................................................#"
|
||||
"#.........#############........................................#"
|
||||
"#...............#..............................................#"
|
||||
"#...............#..............................................#"
|
||||
"#...............#..............................................#"
|
||||
"#.....#..#..#...#..............................................#"
|
||||
"#...............#..............................................#"
|
||||
"#...............#..............................................#"
|
||||
"#.....#..#..#..................................................#"
|
||||
"#..............................................................#"
|
||||
"#..............................................................#"
|
||||
"#..............................................................#"
|
||||
"#..............................................................#"
|
||||
"#.....................######..#................................#"
|
||||
"#.....................#.......#................................#"
|
||||
"#....................##.###.###.........................#......#"
|
||||
"#....................##.....#........................##........#"
|
||||
"#....................##.#####........................##.#......#"
|
||||
"#....................#.#.......................................#"
|
||||
"#....................#..#...............................#......#"
|
||||
"#..............................................................#"
|
||||
"#..............................................................#"
|
||||
"#..............................................................#"
|
||||
"#..............................................................#"
|
||||
"#..............................##..............................#"
|
||||
"#..............................##..............................#"
|
||||
"#..............................##..............................#"
|
||||
"################################################################";
|
||||
|
||||
vWorldSize = { 64, 32 };
|
||||
}
|
||||
|
||||
protected:
|
||||
// User implementation to retrieve appropriate graphics for scenery
|
||||
olc::Pixel SelectSceneryPixel(const int tile_x, const int tile_y, const olc::rcw::Engine::CellSide side, const float sample_x, const float sample_y, const float distance) override
|
||||
{
|
||||
olc::Pixel p;
|
||||
|
||||
// Choose appropriate colour
|
||||
switch (side)
|
||||
{
|
||||
case olc::rcw::Engine::CellSide::Top: // Location is "Sky"
|
||||
p = olc::CYAN;
|
||||
break;
|
||||
|
||||
case olc::rcw::Engine::CellSide::Bottom: // Location is "Ground"
|
||||
p = olc::DARK_GREEN;
|
||||
break;
|
||||
|
||||
default: // Location is "Wall"
|
||||
p = olc::WHITE;
|
||||
if (sample_x < 0.05f || sample_x > 0.95f || sample_y < 0.05f || sample_y > 0.95f)
|
||||
p = olc::BLACK;
|
||||
break;
|
||||
}
|
||||
|
||||
// Apply directional lighting, by first creating a shadow scalar...
|
||||
float fShadow = 1.0f;
|
||||
switch (side)
|
||||
{
|
||||
case olc::rcw::Engine::CellSide::South: fShadow = 0.3f; break;
|
||||
case olc::rcw::Engine::CellSide::East: fShadow = 0.3f; break;
|
||||
}
|
||||
|
||||
// ...also shade by distance...
|
||||
float fDistance = 1.0f - std::min(distance / 32.0f, 1.0f);
|
||||
|
||||
// ...and applying it to sampled pixel
|
||||
p.r = uint8_t(float(p.r) * fDistance);
|
||||
p.g = uint8_t(float(p.g) * fDistance);
|
||||
p.b = uint8_t(float(p.b) * fDistance);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
// User implementation to retrieve if a particular tile is solid
|
||||
bool IsLocationSolid(const float tile_x, const float tile_y) override
|
||||
{
|
||||
if (int(tile_x) >= 0 && int(tile_x) < vWorldSize.x && int(tile_y) >= 0 && int(tile_y) < vWorldSize.y)
|
||||
return sMap[int(tile_y) * vWorldSize.x + int(tile_x)] == '#';
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// NOTE! Objects are not used in this demonstration ===================
|
||||
|
||||
// User implementation to retrieve dimensions of an in game object
|
||||
float GetObjectWidth(const uint32_t id) override
|
||||
{ return 1; }
|
||||
|
||||
float GetObjectHeight(const uint32_t id) override
|
||||
{ return 1; }
|
||||
|
||||
// User implementation to retrieve appropriate graphics for objects
|
||||
olc::Pixel SelectObjectPixel(const uint32_t id, const float sample_x, const float sample_y, const float distance, const float angle) override
|
||||
{ return olc::BLACK; }
|
||||
|
||||
private:
|
||||
std::string sMap;
|
||||
olc::vi2d vWorldSize;
|
||||
};
|
||||
|
||||
|
||||
|
||||
class RayCastWorldDemo_SIMPLE : public olc::PixelGameEngine
|
||||
{
|
||||
public:
|
||||
RayCastWorldDemo_SIMPLE()
|
||||
{
|
||||
sAppName = "RayCastWorld - SIMPLE";
|
||||
}
|
||||
|
||||
public:
|
||||
bool OnUserCreate() override
|
||||
{
|
||||
// Create game object
|
||||
pGame.reset(new ExampleGame(ScreenWidth(), ScreenHeight(), 3.14159f / 3.333f));
|
||||
|
||||
// Add an object "player"
|
||||
std::shared_ptr<olc::rcw::Object> player = std::make_shared<olc::rcw::Object>();
|
||||
player->pos = { 2.1f, 2.1f };
|
||||
player->bVisible = false;
|
||||
|
||||
// Insert into game world
|
||||
pGame->mapObjects.insert_or_assign(0, player);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OnUserUpdate(float fElapsedTime) override
|
||||
{
|
||||
// Handle User Input =================================================
|
||||
auto& player = pGame->mapObjects[0];
|
||||
|
||||
if (GetKey(olc::Key::A).bHeld) // Turn Left
|
||||
player->Turn(-fPlayerMoveSpeed * 0.1f * fElapsedTime);
|
||||
|
||||
if (GetKey(olc::Key::D).bHeld) // Turn Right
|
||||
player->Turn(+fPlayerMoveSpeed * 0.1f * fElapsedTime);
|
||||
|
||||
// Reset speed and velocity so player doesnt move if keys are not pressed
|
||||
player->Stop();
|
||||
|
||||
// Walk Forward
|
||||
if (GetKey(olc::Key::W).bHeld) player->Walk(+fPlayerMoveSpeed);
|
||||
// Walk Backwards
|
||||
if (GetKey(olc::Key::S).bHeld) player->Walk(-fPlayerMoveSpeed);
|
||||
// Strafe Right
|
||||
if (GetKey(olc::Key::E).bHeld) player->Strafe(+fPlayerMoveSpeed);
|
||||
// Strafe Left
|
||||
if (GetKey(olc::Key::Q).bHeld) player->Strafe(-fPlayerMoveSpeed);
|
||||
|
||||
// Update & Render World ==================================================
|
||||
|
||||
// Update ray cast world engine - this will move the player object
|
||||
pGame->Update(fElapsedTime);
|
||||
|
||||
// Link the camera to the play position
|
||||
pGame->SetCamera(player->pos, player->fHeading);
|
||||
|
||||
// Render the scene!
|
||||
pGame->Render();
|
||||
|
||||
// Draw the players position, cos why not?
|
||||
DrawString({ 10,10 }, player->pos.str(), olc::BLACK);
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
float fPlayerMoveSpeed = 16.0f;
|
||||
std::unique_ptr<ExampleGame> pGame = nullptr;
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
RayCastWorldDemo_SIMPLE demo;
|
||||
if (demo.Construct(320, 240, 4, 4))
|
||||
demo.Start();
|
||||
return 0;
|
||||
}
|
||||