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; |
||||
} |