diff --git a/CarCrimeCity/Part1/City_Roads1_mip0.png b/CarCrimeCity/Part1/City_Roads1_mip0.png deleted file mode 100644 index 46cec50..0000000 Binary files a/CarCrimeCity/Part1/City_Roads1_mip0.png and /dev/null differ diff --git a/CarCrimeCity/Part1/OneLoneCoder_CarCrimeCity1.cpp b/CarCrimeCity/Part1/OneLoneCoder_CarCrimeCity1.cpp deleted file mode 100644 index 8a4c7a7..0000000 --- a/CarCrimeCity/Part1/OneLoneCoder_CarCrimeCity1.cpp +++ /dev/null @@ -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 -#include -#include -#include -#include -#include -#include - -// 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 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; -} \ No newline at end of file diff --git a/CarCrimeCity/Part1/car_top.png b/CarCrimeCity/Part1/car_top.png deleted file mode 100644 index ad89ae4..0000000 Binary files a/CarCrimeCity/Part1/car_top.png and /dev/null differ diff --git a/CarCrimeCity/Part1/car_top1.png b/CarCrimeCity/Part1/car_top1.png deleted file mode 100644 index 15ceb1d..0000000 Binary files a/CarCrimeCity/Part1/car_top1.png and /dev/null differ diff --git a/CarCrimeCity/Part1/example1.city b/CarCrimeCity/Part1/example1.city deleted file mode 100644 index 47dfb0b..0000000 Binary files a/CarCrimeCity/Part1/example1.city and /dev/null differ diff --git a/CarCrimeCity/Part1/olcPGEX_Graphics3D.h b/CarCrimeCity/Part1/olcPGEX_Graphics3D.h deleted file mode 100644 index 954e776..0000000 --- a/CarCrimeCity/Part1/olcPGEX_Graphics3D.h +++ /dev/null @@ -1,1174 +0,0 @@ -/* - olcPGEX_Graphics3D.h - - +-------------------------------------------------------------+ - | OneLoneCoder Pixel Game Engine Extension | - | 3D Rendering - v0.1 | - +-------------------------------------------------------------+ - - What is this? - ~~~~~~~~~~~~~ - This is an extension to the olcPixelGameEngine, which provides - support for software rendering 3D graphics. - - NOTE!!! This file is under development and may change! - - License (OLC-3) - ~~~~~~~~~~~~~~~ - - Copyright 2018-2019 OneLoneCoder.com - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - - 1. Redistributions or derivations of source code must retain the above - copyright notice, this list of conditions and the following disclaimer. - - 2. Redistributions or derivative works in binary form must reproduce - the above copyright notice. This list of conditions and the following - disclaimer must be reproduced in the documentation and/or other - materials provided with the distribution. - - 3. Neither the name of the copyright holder nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - Links - ~~~~~ - YouTube: https://www.youtube.com/javidx9 - Discord: https://discord.gg/WhwHUMV - Twitter: https://www.twitter.com/javidx9 - Twitch: https://www.twitch.tv/javidx9 - GitHub: https://www.github.com/onelonecoder - Patreon: https://www.patreon.com/javidx9 - Homepage: https://www.onelonecoder.com - - Author - ~~~~~~ - David Barr, aka javidx9, ŠOneLoneCoder 2018 -*/ - - -#ifndef OLC_PGEX_GFX3D -#define OLC_PGEX_GFX3D - -#include -#include -#include -#undef min -#undef max - -namespace olc -{ - // Container class for Advanced 2D Drawing functions - class GFX3D : public olc::PGEX - { - - public: - - struct vec2d - { - float x = 0; - float y = 0; - float z = 0; - }; - - struct vec3d - { - float x = 0; - float y = 0; - float z = 0; - float w = 1; // Need a 4th term to perform sensible matrix vector multiplication - }; - - struct triangle - { - vec3d p[3]; - vec2d t[3]; - olc::Pixel col; - }; - - struct mat4x4 - { - float m[4][4] = { 0 }; - }; - - struct mesh - { - std::vector tris; - }; - - class Math - { - public: - inline Math(); - public: - inline static vec3d Mat_MultiplyVector(mat4x4 &m, vec3d &i); - inline static mat4x4 Mat_MultiplyMatrix(mat4x4 &m1, mat4x4 &m2); - inline static mat4x4 Mat_MakeIdentity(); - inline static mat4x4 Mat_MakeRotationX(float fAngleRad); - inline static mat4x4 Mat_MakeRotationY(float fAngleRad); - inline static mat4x4 Mat_MakeRotationZ(float fAngleRad); - inline static mat4x4 Mat_MakeScale(float x, float y, float z); - inline static mat4x4 Mat_MakeTranslation(float x, float y, float z); - inline static mat4x4 Mat_MakeProjection(float fFovDegrees, float fAspectRatio, float fNear, float fFar); - inline static mat4x4 Mat_PointAt(vec3d &pos, vec3d &target, vec3d &up); - inline static mat4x4 Mat_QuickInverse(mat4x4 &m); // Only for Rotation/Translation Matrices - inline static mat4x4 Mat_Inverse(olc::GFX3D::mat4x4 &m); - - inline static vec3d Vec_Add(vec3d &v1, vec3d &v2); - inline static vec3d Vec_Sub(vec3d &v1, vec3d &v2); - inline static vec3d Vec_Mul(vec3d &v1, float k); - inline static vec3d Vec_Div(vec3d &v1, float k); - inline static float Vec_DotProduct(vec3d &v1, vec3d &v2); - inline static float Vec_Length(vec3d &v); - inline static vec3d Vec_Normalise(vec3d &v); - inline static vec3d Vec_CrossProduct(vec3d &v1, vec3d &v2); - inline static vec3d Vec_IntersectPlane(vec3d &plane_p, vec3d &plane_n, vec3d &lineStart, vec3d &lineEnd, float &t); - - inline static int Triangle_ClipAgainstPlane(vec3d plane_p, vec3d plane_n, triangle &in_tri, triangle &out_tri1, triangle &out_tri2); - }; - - enum RENDERFLAGS - { - RENDER_WIRE = 0x01, - RENDER_FLAT = 0x02, - RENDER_TEXTURED = 0x04, - RENDER_CULL_CW = 0x08, - RENDER_CULL_CCW = 0x10, - RENDER_DEPTH = 0x20, - }; - - - class PipeLine - { - public: - PipeLine(); - - public: - void SetProjection(float fFovDegrees, float fAspectRatio, float fNear, float fFar, float fLeft, float fTop, float fWidth, float fHeight); - void SetCamera(olc::GFX3D::vec3d &pos, olc::GFX3D::vec3d &lookat, olc::GFX3D::vec3d &up); - void SetTransform(olc::GFX3D::mat4x4 &transform); - void SetTexture(olc::Sprite *texture); - void SetLightSource(olc::GFX3D::vec3d &pos, olc::GFX3D::vec3d &dir, olc::Pixel &col); - uint32_t Render(std::vector &triangles, uint32_t flags = RENDER_CULL_CW | RENDER_TEXTURED | RENDER_DEPTH); - - private: - olc::GFX3D::mat4x4 matProj; - olc::GFX3D::mat4x4 matView; - olc::GFX3D::mat4x4 matWorld; - olc::Sprite *sprTexture; - float fViewX; - float fViewY; - float fViewW; - float fViewH; - }; - - - - public: - //static const int RF_TEXTURE = 0x00000001; - //static const int RF_ = 0x00000002; - - inline static void ConfigureDisplay(); - inline static void ClearDepth(); - inline static void AddTriangleToScene(olc::GFX3D::triangle &tri); - inline static void RenderScene(); - - inline static void DrawTriangleFlat(olc::GFX3D::triangle &tri); - inline static void DrawTriangleWire(olc::GFX3D::triangle &tri, olc::Pixel col = olc::WHITE); - inline static void DrawTriangleTex(olc::GFX3D::triangle &tri, olc::Sprite* spr); - inline static void TexturedTriangle(int x1, int y1, float u1, float v1, float w1, - int x2, int y2, float u2, float v2, float w2, - int x3, int y3, float u3, float v3, float w3, olc::Sprite* spr); - - // Draws a sprite with the transform applied - //inline static void DrawSprite(olc::Sprite *sprite, olc::GFX2D::Transform2D &transform); - - private: - static float* m_DepthBuffer; - }; -} - - - - -namespace olc -{ - olc::GFX3D::Math::Math() - { - - } - - olc::GFX3D::vec3d olc::GFX3D::Math::Mat_MultiplyVector(olc::GFX3D::mat4x4 &m, olc::GFX3D::vec3d &i) - { - vec3d v; - v.x = i.x * m.m[0][0] + i.y * m.m[1][0] + i.z * m.m[2][0] + i.w * m.m[3][0]; - v.y = i.x * m.m[0][1] + i.y * m.m[1][1] + i.z * m.m[2][1] + i.w * m.m[3][1]; - v.z = i.x * m.m[0][2] + i.y * m.m[1][2] + i.z * m.m[2][2] + i.w * m.m[3][2]; - v.w = i.x * m.m[0][3] + i.y * m.m[1][3] + i.z * m.m[2][3] + i.w * m.m[3][3]; - return v; - } - - olc::GFX3D::mat4x4 olc::GFX3D::Math::Mat_MakeIdentity() - { - olc::GFX3D::mat4x4 matrix; - matrix.m[0][0] = 1.0f; - matrix.m[1][1] = 1.0f; - matrix.m[2][2] = 1.0f; - matrix.m[3][3] = 1.0f; - return matrix; - } - - olc::GFX3D::mat4x4 olc::GFX3D::Math::Mat_MakeRotationX(float fAngleRad) - { - olc::GFX3D::mat4x4 matrix; - matrix.m[0][0] = 1.0f; - matrix.m[1][1] = cosf(fAngleRad); - matrix.m[1][2] = sinf(fAngleRad); - matrix.m[2][1] = -sinf(fAngleRad); - matrix.m[2][2] = cosf(fAngleRad); - matrix.m[3][3] = 1.0f; - return matrix; - } - - olc::GFX3D::mat4x4 olc::GFX3D::Math::Mat_MakeRotationY(float fAngleRad) - { - olc::GFX3D::mat4x4 matrix; - matrix.m[0][0] = cosf(fAngleRad); - matrix.m[0][2] = sinf(fAngleRad); - matrix.m[2][0] = -sinf(fAngleRad); - matrix.m[1][1] = 1.0f; - matrix.m[2][2] = cosf(fAngleRad); - matrix.m[3][3] = 1.0f; - return matrix; - } - - olc::GFX3D::mat4x4 olc::GFX3D::Math::Mat_MakeRotationZ(float fAngleRad) - { - olc::GFX3D::mat4x4 matrix; - matrix.m[0][0] = cosf(fAngleRad); - matrix.m[0][1] = sinf(fAngleRad); - matrix.m[1][0] = -sinf(fAngleRad); - matrix.m[1][1] = cosf(fAngleRad); - matrix.m[2][2] = 1.0f; - matrix.m[3][3] = 1.0f; - return matrix; - } - - olc::GFX3D::mat4x4 olc::GFX3D::Math::Mat_MakeScale(float x, float y, float z) - { - olc::GFX3D::mat4x4 matrix; - matrix.m[0][0] = x; - matrix.m[1][1] = y; - matrix.m[2][2] = z; - matrix.m[3][3] = 1.0f; - return matrix; - } - - olc::GFX3D::mat4x4 olc::GFX3D::Math::Mat_MakeTranslation(float x, float y, float z) - { - olc::GFX3D::mat4x4 matrix; - matrix.m[0][0] = 1.0f; - matrix.m[1][1] = 1.0f; - matrix.m[2][2] = 1.0f; - matrix.m[3][3] = 1.0f; - matrix.m[3][0] = x; - matrix.m[3][1] = y; - matrix.m[3][2] = z; - return matrix; - } - - olc::GFX3D::mat4x4 olc::GFX3D::Math::Mat_MakeProjection(float fFovDegrees, float fAspectRatio, float fNear, float fFar) - { - float fFovRad = 1.0f / tanf(fFovDegrees * 0.5f / 180.0f * 3.14159f); - olc::GFX3D::mat4x4 matrix; - matrix.m[0][0] = fAspectRatio * fFovRad; - matrix.m[1][1] = fFovRad; - matrix.m[2][2] = fFar / (fFar - fNear); - matrix.m[3][2] = (-fFar * fNear) / (fFar - fNear); - matrix.m[2][3] = 1.0f; - matrix.m[3][3] = 0.0f; - return matrix; - } - - olc::GFX3D::mat4x4 olc::GFX3D::Math::Mat_MultiplyMatrix(olc::GFX3D::mat4x4 &m1, olc::GFX3D::mat4x4 &m2) - { - olc::GFX3D::mat4x4 matrix; - for (int c = 0; c < 4; c++) - for (int r = 0; r < 4; r++) - matrix.m[r][c] = m1.m[r][0] * m2.m[0][c] + m1.m[r][1] * m2.m[1][c] + m1.m[r][2] * m2.m[2][c] + m1.m[r][3] * m2.m[3][c]; - return matrix; - } - - olc::GFX3D::mat4x4 olc::GFX3D::Math::Mat_PointAt(olc::GFX3D::vec3d &pos, olc::GFX3D::vec3d &target, olc::GFX3D::vec3d &up) - { - // Calculate new forward direction - olc::GFX3D::vec3d newForward = Vec_Sub(target, pos); - newForward = Vec_Normalise(newForward); - - // Calculate new Up direction - olc::GFX3D::vec3d a = Vec_Mul(newForward, Vec_DotProduct(up, newForward)); - olc::GFX3D::vec3d newUp = Vec_Sub(up, a); - newUp = Vec_Normalise(newUp); - - // New Right direction is easy, its just cross product - olc::GFX3D::vec3d newRight = Vec_CrossProduct(newUp, newForward); - - // Construct Dimensioning and Translation Matrix - olc::GFX3D::mat4x4 matrix; - matrix.m[0][0] = newRight.x; matrix.m[0][1] = newRight.y; matrix.m[0][2] = newRight.z; matrix.m[0][3] = 0.0f; - matrix.m[1][0] = newUp.x; matrix.m[1][1] = newUp.y; matrix.m[1][2] = newUp.z; matrix.m[1][3] = 0.0f; - matrix.m[2][0] = newForward.x; matrix.m[2][1] = newForward.y; matrix.m[2][2] = newForward.z; matrix.m[2][3] = 0.0f; - matrix.m[3][0] = pos.x; matrix.m[3][1] = pos.y; matrix.m[3][2] = pos.z; matrix.m[3][3] = 1.0f; - return matrix; - - } - - olc::GFX3D::mat4x4 olc::GFX3D::Math::Mat_QuickInverse(olc::GFX3D::mat4x4 &m) // Only for Rotation/Translation Matrices - { - olc::GFX3D::mat4x4 matrix; - matrix.m[0][0] = m.m[0][0]; matrix.m[0][1] = m.m[1][0]; matrix.m[0][2] = m.m[2][0]; matrix.m[0][3] = 0.0f; - matrix.m[1][0] = m.m[0][1]; matrix.m[1][1] = m.m[1][1]; matrix.m[1][2] = m.m[2][1]; matrix.m[1][3] = 0.0f; - matrix.m[2][0] = m.m[0][2]; matrix.m[2][1] = m.m[1][2]; matrix.m[2][2] = m.m[2][2]; matrix.m[2][3] = 0.0f; - matrix.m[3][0] = -(m.m[3][0] * matrix.m[0][0] + m.m[3][1] * matrix.m[1][0] + m.m[3][2] * matrix.m[2][0]); - matrix.m[3][1] = -(m.m[3][0] * matrix.m[0][1] + m.m[3][1] * matrix.m[1][1] + m.m[3][2] * matrix.m[2][1]); - matrix.m[3][2] = -(m.m[3][0] * matrix.m[0][2] + m.m[3][1] * matrix.m[1][2] + m.m[3][2] * matrix.m[2][2]); - matrix.m[3][3] = 1.0f; - return matrix; - } - - olc::GFX3D::mat4x4 olc::GFX3D::Math::Mat_Inverse(olc::GFX3D::mat4x4 &m) - { - double det; - - - mat4x4 matInv; - - matInv.m[0][0] = m.m[1][1] * m.m[2][2] * m.m[3][3] - m.m[1][1] * m.m[2][3] * m.m[3][2] - m.m[2][1] * m.m[1][2] * m.m[3][3] + m.m[2][1] * m.m[1][3] * m.m[3][2] + m.m[3][1] * m.m[1][2] * m.m[2][3] - m.m[3][1] * m.m[1][3] * m.m[2][2]; - matInv.m[1][0] = -m.m[1][0] * m.m[2][2] * m.m[3][3] + m.m[1][0] * m.m[2][3] * m.m[3][2] + m.m[2][0] * m.m[1][2] * m.m[3][3] - m.m[2][0] * m.m[1][3] * m.m[3][2] - m.m[3][0] * m.m[1][2] * m.m[2][3] + m.m[3][0] * m.m[1][3] * m.m[2][2]; - matInv.m[2][0] = m.m[1][0] * m.m[2][1] * m.m[3][3] - m.m[1][0] * m.m[2][3] * m.m[3][1] - m.m[2][0] * m.m[1][1] * m.m[3][3] + m.m[2][0] * m.m[1][3] * m.m[3][1] + m.m[3][0] * m.m[1][1] * m.m[2][3] - m.m[3][0] * m.m[1][3] * m.m[2][1]; - matInv.m[3][0] = -m.m[1][0] * m.m[2][1] * m.m[3][2] + m.m[1][0] * m.m[2][2] * m.m[3][1] + m.m[2][0] * m.m[1][1] * m.m[3][2] - m.m[2][0] * m.m[1][2] * m.m[3][1] - m.m[3][0] * m.m[1][1] * m.m[2][2] + m.m[3][0] * m.m[1][2] * m.m[2][1]; - matInv.m[0][1] = -m.m[0][1] * m.m[2][2] * m.m[3][3] + m.m[0][1] * m.m[2][3] * m.m[3][2] + m.m[2][1] * m.m[0][2] * m.m[3][3] - m.m[2][1] * m.m[0][3] * m.m[3][2] - m.m[3][1] * m.m[0][2] * m.m[2][3] + m.m[3][1] * m.m[0][3] * m.m[2][2]; - matInv.m[1][1] = m.m[0][0] * m.m[2][2] * m.m[3][3] - m.m[0][0] * m.m[2][3] * m.m[3][2] - m.m[2][0] * m.m[0][2] * m.m[3][3] + m.m[2][0] * m.m[0][3] * m.m[3][2] + m.m[3][0] * m.m[0][2] * m.m[2][3] - m.m[3][0] * m.m[0][3] * m.m[2][2]; - matInv.m[2][1] = -m.m[0][0] * m.m[2][1] * m.m[3][3] + m.m[0][0] * m.m[2][3] * m.m[3][1] + m.m[2][0] * m.m[0][1] * m.m[3][3] - m.m[2][0] * m.m[0][3] * m.m[3][1] - m.m[3][0] * m.m[0][1] * m.m[2][3] + m.m[3][0] * m.m[0][3] * m.m[2][1]; - matInv.m[3][1] = m.m[0][0] * m.m[2][1] * m.m[3][2] - m.m[0][0] * m.m[2][2] * m.m[3][1] - m.m[2][0] * m.m[0][1] * m.m[3][2] + m.m[2][0] * m.m[0][2] * m.m[3][1] + m.m[3][0] * m.m[0][1] * m.m[2][2] - m.m[3][0] * m.m[0][2] * m.m[2][1]; - matInv.m[0][2] = m.m[0][1] * m.m[1][2] * m.m[3][3] - m.m[0][1] * m.m[1][3] * m.m[3][2] - m.m[1][1] * m.m[0][2] * m.m[3][3] + m.m[1][1] * m.m[0][3] * m.m[3][2] + m.m[3][1] * m.m[0][2] * m.m[1][3] - m.m[3][1] * m.m[0][3] * m.m[1][2]; - matInv.m[1][2] = -m.m[0][0] * m.m[1][2] * m.m[3][3] + m.m[0][0] * m.m[1][3] * m.m[3][2] + m.m[1][0] * m.m[0][2] * m.m[3][3] - m.m[1][0] * m.m[0][3] * m.m[3][2] - m.m[3][0] * m.m[0][2] * m.m[1][3] + m.m[3][0] * m.m[0][3] * m.m[1][2]; - matInv.m[2][2] = m.m[0][0] * m.m[1][1] * m.m[3][3] - m.m[0][0] * m.m[1][3] * m.m[3][1] - m.m[1][0] * m.m[0][1] * m.m[3][3] + m.m[1][0] * m.m[0][3] * m.m[3][1] + m.m[3][0] * m.m[0][1] * m.m[1][3] - m.m[3][0] * m.m[0][3] * m.m[1][1]; - matInv.m[3][2] = -m.m[0][0] * m.m[1][1] * m.m[3][2] + m.m[0][0] * m.m[1][2] * m.m[3][1] + m.m[1][0] * m.m[0][1] * m.m[3][2] - m.m[1][0] * m.m[0][2] * m.m[3][1] - m.m[3][0] * m.m[0][1] * m.m[1][2] + m.m[3][0] * m.m[0][2] * m.m[1][1]; - matInv.m[0][3] = -m.m[0][1] * m.m[1][2] * m.m[2][3] + m.m[0][1] * m.m[1][3] * m.m[2][2] + m.m[1][1] * m.m[0][2] * m.m[2][3] - m.m[1][1] * m.m[0][3] * m.m[2][2] - m.m[2][1] * m.m[0][2] * m.m[1][3] + m.m[2][1] * m.m[0][3] * m.m[1][2]; - matInv.m[1][3] = m.m[0][0] * m.m[1][2] * m.m[2][3] - m.m[0][0] * m.m[1][3] * m.m[2][2] - m.m[1][0] * m.m[0][2] * m.m[2][3] + m.m[1][0] * m.m[0][3] * m.m[2][2] + m.m[2][0] * m.m[0][2] * m.m[1][3] - m.m[2][0] * m.m[0][3] * m.m[1][2]; - matInv.m[2][3] = -m.m[0][0] * m.m[1][1] * m.m[2][3] + m.m[0][0] * m.m[1][3] * m.m[2][1] + m.m[1][0] * m.m[0][1] * m.m[2][3] - m.m[1][0] * m.m[0][3] * m.m[2][1] - m.m[2][0] * m.m[0][1] * m.m[1][3] + m.m[2][0] * m.m[0][3] * m.m[1][1]; - matInv.m[3][3] = m.m[0][0] * m.m[1][1] * m.m[2][2] - m.m[0][0] * m.m[1][2] * m.m[2][1] - m.m[1][0] * m.m[0][1] * m.m[2][2] + m.m[1][0] * m.m[0][2] * m.m[2][1] + m.m[2][0] * m.m[0][1] * m.m[1][2] - m.m[2][0] * m.m[0][2] * m.m[1][1]; - - det = m.m[0][0] * matInv.m[0][0] + m.m[0][1] * matInv.m[1][0] + m.m[0][2] * matInv.m[2][0] + m.m[0][3] * matInv.m[3][0]; - // if (det == 0) return false; - - det = 1.0 / det; - - for (int i = 0; i < 4; i++) - for (int j = 0; j < 4; j++) - matInv.m[i][j] *= (float)det; - - return matInv; - } - - olc::GFX3D::vec3d olc::GFX3D::Math::Vec_Add(olc::GFX3D::vec3d &v1, olc::GFX3D::vec3d &v2) - { - return { v1.x + v2.x, v1.y + v2.y, v1.z + v2.z }; - } - - olc::GFX3D::vec3d olc::GFX3D::Math::Vec_Sub(olc::GFX3D::vec3d &v1, olc::GFX3D::vec3d &v2) - { - return { v1.x - v2.x, v1.y - v2.y, v1.z - v2.z }; - } - - olc::GFX3D::vec3d olc::GFX3D::Math::Vec_Mul(olc::GFX3D::vec3d &v1, float k) - { - return { v1.x * k, v1.y * k, v1.z * k }; - } - - olc::GFX3D::vec3d olc::GFX3D::Math::Vec_Div(olc::GFX3D::vec3d &v1, float k) - { - return { v1.x / k, v1.y / k, v1.z / k }; - } - - float olc::GFX3D::Math::Vec_DotProduct(olc::GFX3D::vec3d &v1, olc::GFX3D::vec3d &v2) - { - return v1.x*v2.x + v1.y*v2.y + v1.z * v2.z; - } - - float olc::GFX3D::Math::Vec_Length(olc::GFX3D::vec3d &v) - { - return sqrtf(Vec_DotProduct(v, v)); - } - - olc::GFX3D::vec3d olc::GFX3D::Math::Vec_Normalise(olc::GFX3D::vec3d &v) - { - float l = Vec_Length(v); - return { v.x / l, v.y / l, v.z / l }; - } - - olc::GFX3D::vec3d olc::GFX3D::Math::Vec_CrossProduct(olc::GFX3D::vec3d &v1, olc::GFX3D::vec3d &v2) - { - vec3d v; - v.x = v1.y * v2.z - v1.z * v2.y; - v.y = v1.z * v2.x - v1.x * v2.z; - v.z = v1.x * v2.y - v1.y * v2.x; - return v; - } - - olc::GFX3D::vec3d olc::GFX3D::Math::Vec_IntersectPlane(olc::GFX3D::vec3d &plane_p, olc::GFX3D::vec3d &plane_n, olc::GFX3D::vec3d &lineStart, olc::GFX3D::vec3d &lineEnd, float &t) - { - plane_n = Vec_Normalise(plane_n); - float plane_d = -Vec_DotProduct(plane_n, plane_p); - float ad = Vec_DotProduct(lineStart, plane_n); - float bd = Vec_DotProduct(lineEnd, plane_n); - t = (-plane_d - ad) / (bd - ad); - olc::GFX3D::vec3d lineStartToEnd = Vec_Sub(lineEnd, lineStart); - olc::GFX3D::vec3d lineToIntersect = Vec_Mul(lineStartToEnd, t); - return Vec_Add(lineStart, lineToIntersect); - } - - - int olc::GFX3D::Math::Triangle_ClipAgainstPlane(vec3d plane_p, vec3d plane_n, triangle &in_tri, triangle &out_tri1, triangle &out_tri2) - { - // Make sure plane normal is indeed normal - plane_n = Math::Vec_Normalise(plane_n); - - out_tri1.t[0] = in_tri.t[0]; - out_tri2.t[0] = in_tri.t[0]; - out_tri1.t[1] = in_tri.t[1]; - out_tri2.t[1] = in_tri.t[1]; - out_tri1.t[2] = in_tri.t[2]; - out_tri2.t[2] = in_tri.t[2]; - - // Return signed shortest distance from point to plane, plane normal must be normalised - auto dist = [&](vec3d &p) - { - vec3d n = Math::Vec_Normalise(p); - return (plane_n.x * p.x + plane_n.y * p.y + plane_n.z * p.z - Math::Vec_DotProduct(plane_n, plane_p)); - }; - - // Create two temporary storage arrays to classify points either side of plane - // If distance sign is positive, point lies on "inside" of plane - vec3d* inside_points[3]; int nInsidePointCount = 0; - vec3d* outside_points[3]; int nOutsidePointCount = 0; - vec2d* inside_tex[3]; int nInsideTexCount = 0; - vec2d* outside_tex[3]; int nOutsideTexCount = 0; - - - // Get signed distance of each point in triangle to plane - float d0 = dist(in_tri.p[0]); - float d1 = dist(in_tri.p[1]); - float d2 = dist(in_tri.p[2]); - - if (d0 >= 0) { inside_points[nInsidePointCount++] = &in_tri.p[0]; inside_tex[nInsideTexCount++] = &in_tri.t[0]; } - else { - outside_points[nOutsidePointCount++] = &in_tri.p[0]; outside_tex[nOutsideTexCount++] = &in_tri.t[0]; - } - if (d1 >= 0) { - inside_points[nInsidePointCount++] = &in_tri.p[1]; inside_tex[nInsideTexCount++] = &in_tri.t[1]; - } - else { - outside_points[nOutsidePointCount++] = &in_tri.p[1]; outside_tex[nOutsideTexCount++] = &in_tri.t[1]; - } - if (d2 >= 0) { - inside_points[nInsidePointCount++] = &in_tri.p[2]; inside_tex[nInsideTexCount++] = &in_tri.t[2]; - } - else { - outside_points[nOutsidePointCount++] = &in_tri.p[2]; outside_tex[nOutsideTexCount++] = &in_tri.t[2]; - } - - // Now classify triangle points, and break the input triangle into - // smaller output triangles if required. There are four possible - // outcomes... - - if (nInsidePointCount == 0) - { - // All points lie on the outside of plane, so clip whole triangle - // It ceases to exist - - return 0; // No returned triangles are valid - } - - if (nInsidePointCount == 3) - { - // All points lie on the inside of plane, so do nothing - // and allow the triangle to simply pass through - out_tri1 = in_tri; - - return 1; // Just the one returned original triangle is valid - } - - if (nInsidePointCount == 1 && nOutsidePointCount == 2) - { - // Triangle should be clipped. As two points lie outside - // the plane, the triangle simply becomes a smaller triangle - - // Copy appearance info to new triangle - out_tri1.col = olc::MAGENTA;// in_tri.col; - - // The inside point is valid, so keep that... - out_tri1.p[0] = *inside_points[0]; - out_tri1.t[0] = *inside_tex[0]; - - // but the two new points are at the locations where the - // original sides of the triangle (lines) intersect with the plane - float t; - out_tri1.p[1] = Math::Vec_IntersectPlane(plane_p, plane_n, *inside_points[0], *outside_points[0], t); - out_tri1.t[1].x = t * (outside_tex[0]->x - inside_tex[0]->x) + inside_tex[0]->x; - out_tri1.t[1].y = t * (outside_tex[0]->y - inside_tex[0]->y) + inside_tex[0]->y; - out_tri1.t[1].z = t * (outside_tex[0]->z - inside_tex[0]->z) + inside_tex[0]->z; - - out_tri1.p[2] = Math::Vec_IntersectPlane(plane_p, plane_n, *inside_points[0], *outside_points[1], t); - out_tri1.t[2].x = t * (outside_tex[1]->x - inside_tex[0]->x) + inside_tex[0]->x; - out_tri1.t[2].y = t * (outside_tex[1]->y - inside_tex[0]->y) + inside_tex[0]->y; - out_tri1.t[2].z = t * (outside_tex[1]->z - inside_tex[0]->z) + inside_tex[0]->z; - - return 1; // Return the newly formed single triangle - } - - if (nInsidePointCount == 2 && nOutsidePointCount == 1) - { - // Triangle should be clipped. As two points lie inside the plane, - // the clipped triangle becomes a "quad". Fortunately, we can - // represent a quad with two new triangles - - // Copy appearance info to new triangles - out_tri1.col = olc::GREEN;// in_tri.col; - out_tri2.col = olc::RED;// in_tri.col; - - // The first triangle consists of the two inside points and a new - // point determined by the location where one side of the triangle - // intersects with the plane - out_tri1.p[0] = *inside_points[0]; - out_tri1.t[0] = *inside_tex[0]; - - out_tri1.p[1] = *inside_points[1]; - out_tri1.t[1] = *inside_tex[1]; - - float t; - out_tri1.p[2] = Math::Vec_IntersectPlane(plane_p, plane_n, *inside_points[0], *outside_points[0], t); - out_tri1.t[2].x = t * (outside_tex[0]->x - inside_tex[0]->x) + inside_tex[0]->x; - out_tri1.t[2].y = t * (outside_tex[0]->y - inside_tex[0]->y) + inside_tex[0]->y; - out_tri1.t[2].z = t * (outside_tex[0]->z - inside_tex[0]->z) + inside_tex[0]->z; - - // The second triangle is composed of one of he inside points, a - // new point determined by the intersection of the other side of the - // triangle and the plane, and the newly created point above - out_tri2.p[1] = *inside_points[1]; - out_tri2.t[1] = *inside_tex[1]; - out_tri2.p[0] = out_tri1.p[2]; - out_tri2.t[0] = out_tri1.t[2]; - out_tri2.p[2] = Math::Vec_IntersectPlane(plane_p, plane_n, *inside_points[1], *outside_points[0], t); - out_tri2.t[2].x = t * (outside_tex[0]->x - inside_tex[1]->x) + inside_tex[1]->x; - out_tri2.t[2].y = t * (outside_tex[0]->y - inside_tex[1]->y) + inside_tex[1]->y; - out_tri2.t[2].z = t * (outside_tex[0]->z - inside_tex[1]->z) + inside_tex[1]->z; - return 2; // Return two newly formed triangles which form a quad - } - - return 0; - } - - void GFX3D::DrawTriangleFlat(olc::GFX3D::triangle &tri) - { - pge->FillTriangle(tri.p[0].x, tri.p[0].y, tri.p[1].x, tri.p[1].y, tri.p[2].x, tri.p[2].y, tri.col); - } - - void GFX3D::DrawTriangleWire(olc::GFX3D::triangle &tri, olc::Pixel col) - { - pge->DrawTriangle(tri.p[0].x, tri.p[0].y, tri.p[1].x, tri.p[1].y, tri.p[2].x, tri.p[2].y, col); - } - - void GFX3D::TexturedTriangle(int x1, int y1, float u1, float v1, float w1, - int x2, int y2, float u2, float v2, float w2, - int x3, int y3, float u3, float v3, float w3, olc::Sprite* spr) - - { - if (y2 < y1) - { - std::swap(y1, y2); - std::swap(x1, x2); - std::swap(u1, u2); - std::swap(v1, v2); - std::swap(w1, w2); - } - - if (y3 < y1) - { - std::swap(y1, y3); - std::swap(x1, x3); - std::swap(u1, u3); - std::swap(v1, v3); - std::swap(w1, w3); - } - - if (y3 < y2) - { - std::swap(y2, y3); - std::swap(x2, x3); - std::swap(u2, u3); - std::swap(v2, v3); - std::swap(w2, w3); - } - - int dy1 = y2 - y1; - int dx1 = x2 - x1; - float dv1 = v2 - v1; - float du1 = u2 - u1; - float dw1 = w2 - w1; - - int dy2 = y3 - y1; - int dx2 = x3 - x1; - float dv2 = v3 - v1; - float du2 = u3 - u1; - float dw2 = w3 - w1; - - float tex_u, tex_v, tex_w; - - float dax_step = 0, dbx_step = 0, - du1_step = 0, dv1_step = 0, - du2_step = 0, dv2_step = 0, - dw1_step = 0, dw2_step = 0; - - if (dy1) dax_step = dx1 / (float)abs(dy1); - if (dy2) dbx_step = dx2 / (float)abs(dy2); - - if (dy1) du1_step = du1 / (float)abs(dy1); - if (dy1) dv1_step = dv1 / (float)abs(dy1); - if (dy1) dw1_step = dw1 / (float)abs(dy1); - - if (dy2) du2_step = du2 / (float)abs(dy2); - if (dy2) dv2_step = dv2 / (float)abs(dy2); - if (dy2) dw2_step = dw2 / (float)abs(dy2); - - if (dy1) - { - for (int i = y1; i <= y2; i++) - { - int ax = x1 + (float)(i - y1) * dax_step; - int bx = x1 + (float)(i - y1) * dbx_step; - - float tex_su = u1 + (float)(i - y1) * du1_step; - float tex_sv = v1 + (float)(i - y1) * dv1_step; - float tex_sw = w1 + (float)(i - y1) * dw1_step; - - float tex_eu = u1 + (float)(i - y1) * du2_step; - float tex_ev = v1 + (float)(i - y1) * dv2_step; - float tex_ew = w1 + (float)(i - y1) * dw2_step; - - if (ax > bx) - { - std::swap(ax, bx); - std::swap(tex_su, tex_eu); - std::swap(tex_sv, tex_ev); - std::swap(tex_sw, tex_ew); - } - - tex_u = tex_su; - tex_v = tex_sv; - tex_w = tex_sw; - - float tstep = 1.0f / ((float)(bx - ax)); - float t = 0.0f; - - for (int j = ax; j < bx; j++) - { - tex_u = (1.0f - t) * tex_su + t * tex_eu; - tex_v = (1.0f - t) * tex_sv + t * tex_ev; - tex_w = (1.0f - t) * tex_sw + t * tex_ew; - if (tex_w > m_DepthBuffer[i*pge->ScreenWidth() + j]) - { - pge->Draw(j, i, spr->Sample(tex_u / tex_w, tex_v / tex_w)); - m_DepthBuffer[i*pge->ScreenWidth() + j] = tex_w; - } - t += tstep; - } - - } - } - - dy1 = y3 - y2; - dx1 = x3 - x2; - dv1 = v3 - v2; - du1 = u3 - u2; - dw1 = w3 - w2; - - if (dy1) dax_step = dx1 / (float)abs(dy1); - if (dy2) dbx_step = dx2 / (float)abs(dy2); - - du1_step = 0, dv1_step = 0; - if (dy1) du1_step = du1 / (float)abs(dy1); - if (dy1) dv1_step = dv1 / (float)abs(dy1); - if (dy1) dw1_step = dw1 / (float)abs(dy1); - - if (dy1) - { - for (int i = y2; i <= y3; i++) - { - int ax = x2 + (float)(i - y2) * dax_step; - int bx = x1 + (float)(i - y1) * dbx_step; - - float tex_su = u2 + (float)(i - y2) * du1_step; - float tex_sv = v2 + (float)(i - y2) * dv1_step; - float tex_sw = w2 + (float)(i - y2) * dw1_step; - - float tex_eu = u1 + (float)(i - y1) * du2_step; - float tex_ev = v1 + (float)(i - y1) * dv2_step; - float tex_ew = w1 + (float)(i - y1) * dw2_step; - - if (ax > bx) - { - std::swap(ax, bx); - std::swap(tex_su, tex_eu); - std::swap(tex_sv, tex_ev); - std::swap(tex_sw, tex_ew); - } - - tex_u = tex_su; - tex_v = tex_sv; - tex_w = tex_sw; - - float tstep = 1.0f / ((float)(bx - ax)); - float t = 0.0f; - - for (int j = ax; j < bx; j++) - { - tex_u = (1.0f - t) * tex_su + t * tex_eu; - tex_v = (1.0f - t) * tex_sv + t * tex_ev; - tex_w = (1.0f - t) * tex_sw + t * tex_ew; - - if (tex_w > m_DepthBuffer[i*pge->ScreenWidth() + j]) - { - pge->Draw(j, i, spr->Sample(tex_u / tex_w, tex_v / tex_w)); - m_DepthBuffer[i*pge->ScreenWidth() + j] = tex_w; - } - t += tstep; - } - } - } - } - - - void GFX3D::DrawTriangleTex(olc::GFX3D::triangle &tri, olc::Sprite* spr) - { - if (tri.p[1].y < tri.p[0].y) - { - std::swap(tri.p[0].y, tri.p[1].y); - std::swap(tri.p[0].x, tri.p[1].x); - std::swap(tri.t[0].x, tri.t[1].x); - std::swap(tri.t[0].y, tri.t[1].y); - std::swap(tri.t[0].z, tri.t[1].z); - } - - if (tri.p[2].y < tri.p[0].y) - { - std::swap(tri.p[0].y, tri.p[2].y); - std::swap(tri.p[0].x, tri.p[2].x); - std::swap(tri.t[0].x, tri.t[2].x); - std::swap(tri.t[0].y, tri.t[2].y); - std::swap(tri.t[0].z, tri.t[2].z); - } - - if (tri.p[2].y < tri.p[1].y) - { - std::swap(tri.p[1].y, tri.p[2].y); - std::swap(tri.p[1].x, tri.p[2].x); - std::swap(tri.t[1].x, tri.t[2].x); - std::swap(tri.t[1].y, tri.t[2].y); - std::swap(tri.t[1].z, tri.t[2].z); - } - - int dy1 = tri.p[1].y - tri.p[0].y; - int dx1 = tri.p[1].x - tri.p[0].x; - float dv1 = tri.t[1].y - tri.t[0].y; - float du1 = tri.t[1].x - tri.t[0].x; - float dz1 = tri.t[1].z - tri.t[0].z; - - int dy2 = tri.p[2].y - tri.p[0].y; - int dx2 = tri.p[2].x - tri.p[0].x; - float dv2 = tri.t[2].y - tri.t[0].y; - float du2 = tri.t[2].x - tri.t[0].x; - float dz2 = tri.t[2].z - tri.t[0].z; - - float tex_x, tex_y, tex_z; - - float du1_step = 0, dv1_step = 0, du2_step = 0, dv2_step = 0, dz1_step = 0, dz2_step = 0; - float dax_step = 0, dbx_step = 0; - - if (dy1) dax_step = dx1 / (float)abs(dy1); - if (dy2) dbx_step = dx2 / (float)abs(dy2); - - if (dy1) du1_step = du1 / (float)abs(dy1); - if (dy1) dv1_step = dv1 / (float)abs(dy1); - if (dy1) dz1_step = dz1 / (float)abs(dy1); - - if (dy2) du2_step = du2 / (float)abs(dy2); - if (dy2) dv2_step = dv2 / (float)abs(dy2); - if (dy2) dz2_step = dz2 / (float)abs(dy2); - - - - if (dy1) - { - for (int i = tri.p[0].y; i <= tri.p[1].y; i++) - { - int ax = tri.p[0].x + (i - tri.p[0].y) * dax_step; - int bx = tri.p[0].x + (i - tri.p[0].y) * dbx_step; - - // Start and end points in texture space - float tex_su = tri.t[0].x + (float)(i - tri.p[0].y) * du1_step; - float tex_sv = tri.t[0].y + (float)(i - tri.p[0].y) * dv1_step; - float tex_sz = tri.t[0].z + (float)(i - tri.p[0].y) * dz1_step; - - float tex_eu = tri.t[0].x + (float)(i - tri.p[0].y) * du2_step; - float tex_ev = tri.t[0].y + (float)(i - tri.p[0].y) * dv2_step; - float tex_ez = tri.t[0].z + (float)(i - tri.p[0].y) * dz2_step; - - if (ax > bx) - { - std::swap(ax, bx); - std::swap(tex_su, tex_eu); - std::swap(tex_sv, tex_ev); - std::swap(tex_sz, tex_ez); - } - - tex_x = tex_su; - tex_y = tex_sv; - tex_z = tex_sz; - - - float tstep = 1.0f / ((float)(bx - ax)); - float t = 0; - - for (int j = ax; j < bx; j++) - { - tex_x = (1.0f - t) * tex_su + t * tex_eu; - tex_y = (1.0f - t) * tex_sv + t * tex_ev; - tex_z = (1.0f - t) * tex_sz + t * tex_ez; - - if (tex_z > m_DepthBuffer[i*pge->ScreenWidth() + j]) - { - pge->Draw(j, i, spr->Sample(tex_x / tex_z, tex_y / tex_z)); - m_DepthBuffer[i*pge->ScreenWidth() + j] = tex_z; - } - t += tstep; - } - - - } - } - - dy1 = tri.p[2].y - tri.p[1].y; - dx1 = tri.p[2].x - tri.p[1].x; - dv1 = tri.t[2].y - tri.t[1].y; - du1 = tri.t[2].x - tri.t[1].x; - dz1 = tri.t[2].z - tri.t[1].z; - - if (dy1) dax_step = dx1 / (float)abs(dy1); - if (dy2) dbx_step = dx2 / (float)abs(dy2); - - - du1_step = 0, dv1_step = 0;// , dz1_step = 0;// , du2_step = 0, dv2_step = 0; - if (dy1) du1_step = du1 / (float)abs(dy1); - if (dy1) dv1_step = dv1 / (float)abs(dy1); - if (dy1) dz1_step = dz1 / (float)abs(dy1); - - if (dy1) - { - for (int i = tri.p[1].y; i <= tri.p[2].y; i++) - { - int ax = tri.p[1].x + (i - tri.p[1].y) * dax_step; - int bx = tri.p[0].x + (i - tri.p[0].y) * dbx_step; - - // Start and end points in texture space - float tex_su = tri.t[1].x + (float)(i - tri.p[1].y) * du1_step; - float tex_sv = tri.t[1].y + (float)(i - tri.p[1].y) * dv1_step; - float tex_sz = tri.t[1].z + (float)(i - tri.p[1].y) * dz1_step; - - float tex_eu = tri.t[0].x + (float)(i - tri.p[0].y) * du2_step; - float tex_ev = tri.t[0].y + (float)(i - tri.p[0].y) * dv2_step; - float tex_ez = tri.t[0].z + (float)(i - tri.p[0].y) * dz2_step; - - if (ax > bx) - { - std::swap(ax, bx); - std::swap(tex_su, tex_eu); - std::swap(tex_sv, tex_ev); - std::swap(tex_sz, tex_ez); - } - - tex_x = tex_su; - tex_y = tex_sv; - tex_z = tex_sz; - - - float tstep = 1.0f / ((float)(bx - ax)); - float t = 0; - - for (int j = ax; j < bx; j++) - { - tex_x = (1.0f - t) * tex_su + t * tex_eu; - tex_y = (1.0f - t) * tex_sv + t * tex_ev; - tex_z = (1.0f - t) * tex_sz + t * tex_ez; - - if (tex_z > m_DepthBuffer[i*pge->ScreenWidth() + j]) - { - pge->Draw(j, i, spr->Sample(tex_x / tex_z, tex_y / tex_z)); - m_DepthBuffer[i*pge->ScreenWidth() + j] = tex_z; - } - - t += tstep; - } - } - } - - } - - float* GFX3D::m_DepthBuffer = nullptr; - - void GFX3D::ConfigureDisplay() - { - m_DepthBuffer = new float[pge->ScreenWidth() * pge->ScreenHeight()]{ 0 }; - } - - - void GFX3D::ClearDepth() - { - memset(m_DepthBuffer, 0, pge->ScreenWidth() * pge->ScreenHeight() * sizeof(float)); - } - - - - - GFX3D::PipeLine::PipeLine() - { - - } - - void GFX3D::PipeLine::SetProjection(float fFovDegrees, float fAspectRatio, float fNear, float fFar, float fLeft, float fTop, float fWidth, float fHeight) - { - matProj = GFX3D::Math::Mat_MakeProjection(fFovDegrees, fAspectRatio, fNear, fFar); - fViewX = fLeft; - fViewY = fTop; - fViewW = fWidth; - fViewH = fHeight; - } - - void GFX3D::PipeLine::SetCamera(olc::GFX3D::vec3d &pos, olc::GFX3D::vec3d &lookat, olc::GFX3D::vec3d &up) - { - matView = GFX3D::Math::Mat_PointAt(pos, lookat, up); - matView = GFX3D::Math::Mat_QuickInverse(matView); - } - - void GFX3D::PipeLine::SetTransform(olc::GFX3D::mat4x4 &transform) - { - matWorld = transform; - } - - void GFX3D::PipeLine::SetTexture(olc::Sprite *texture) - { - sprTexture = texture; - } - - void GFX3D::PipeLine::SetLightSource(olc::GFX3D::vec3d &pos, olc::GFX3D::vec3d &dir, olc::Pixel &col) - { - - } - - uint32_t GFX3D::PipeLine::Render(std::vector &triangles, uint32_t flags) - { - // Calculate Transformation Matrix - mat4x4 matWorldView = Math::Mat_MultiplyMatrix(matWorld, matView); - //matWorldViewProj = Math::Mat_MultiplyMatrix(matWorldView, matProj); - - // Store triangles for rastering later - std::vector vecTrianglesToRaster; - - int nTriangleDrawnCount = 0; - - // Process Triangles - for (auto &tri : triangles) - { - GFX3D::triangle triTransformed; - - // Just copy through texture coordinates - triTransformed.t[0] = { tri.t[0].x, tri.t[0].y, tri.t[0].z }; - triTransformed.t[1] = { tri.t[1].x, tri.t[1].y, tri.t[1].z }; - triTransformed.t[2] = { tri.t[2].x, tri.t[2].y, tri.t[2].z }; // Think! - - // Transform Triangle from object into projected space - triTransformed.p[0] = GFX3D::Math::Mat_MultiplyVector(matWorldView, tri.p[0]); - triTransformed.p[1] = GFX3D::Math::Mat_MultiplyVector(matWorldView, tri.p[1]); - triTransformed.p[2] = GFX3D::Math::Mat_MultiplyVector(matWorldView, tri.p[2]); - - // Calculate Triangle Normal in WorldView Space - GFX3D::vec3d normal, line1, line2; - line1 = GFX3D::Math::Vec_Sub(triTransformed.p[1], triTransformed.p[0]); - line2 = GFX3D::Math::Vec_Sub(triTransformed.p[2], triTransformed.p[0]); - normal = GFX3D::Math::Vec_CrossProduct(line1, line2); - normal = GFX3D::Math::Vec_Normalise(normal); - - // Cull triangles that face away from viewer - if (flags & RENDER_CULL_CW && GFX3D::Math::Vec_DotProduct(normal, triTransformed.p[0]) > 0.0f) continue; - if (flags & RENDER_CULL_CCW && GFX3D::Math::Vec_DotProduct(normal, triTransformed.p[0]) < 0.0f) continue; - - // If Lighting, calculate shading - triTransformed.col = olc::WHITE; - - // Clip triangle against near plane - int nClippedTriangles = 0; - triangle clipped[2]; - nClippedTriangles = GFX3D::Math::Triangle_ClipAgainstPlane({ 0.0f, 0.0f, 0.1f }, { 0.0f, 0.0f, 1.0f }, triTransformed, clipped[0], clipped[1]); - - // This may yield two new triangles - for (int n = 0; n < nClippedTriangles; n++) - { - triangle triProjected = clipped[n]; - - // Project new triangle - triProjected.p[0] = GFX3D::Math::Mat_MultiplyVector(matProj, clipped[n].p[0]); - triProjected.p[1] = GFX3D::Math::Mat_MultiplyVector(matProj, clipped[n].p[1]); - triProjected.p[2] = GFX3D::Math::Mat_MultiplyVector(matProj, clipped[n].p[2]); - - // Apply Projection to Verts - triProjected.p[0].x = triProjected.p[0].x / triProjected.p[0].w; - triProjected.p[1].x = triProjected.p[1].x / triProjected.p[1].w; - triProjected.p[2].x = triProjected.p[2].x / triProjected.p[2].w; - - triProjected.p[0].y = triProjected.p[0].y / triProjected.p[0].w; - triProjected.p[1].y = triProjected.p[1].y / triProjected.p[1].w; - triProjected.p[2].y = triProjected.p[2].y / triProjected.p[2].w; - - triProjected.p[0].z = triProjected.p[0].z / triProjected.p[0].w; - triProjected.p[1].z = triProjected.p[1].z / triProjected.p[1].w; - triProjected.p[2].z = triProjected.p[2].z / triProjected.p[2].w; - - // Apply Projection to Tex coords - triProjected.t[0].x = triProjected.t[0].x / triProjected.p[0].w; - triProjected.t[1].x = triProjected.t[1].x / triProjected.p[1].w; - triProjected.t[2].x = triProjected.t[2].x / triProjected.p[2].w; - - triProjected.t[0].y = triProjected.t[0].y / triProjected.p[0].w; - triProjected.t[1].y = triProjected.t[1].y / triProjected.p[1].w; - triProjected.t[2].y = triProjected.t[2].y / triProjected.p[2].w; - - triProjected.t[0].z = 1.0f / triProjected.p[0].w; - triProjected.t[1].z = 1.0f / triProjected.p[1].w; - triProjected.t[2].z = 1.0f / triProjected.p[2].w; - - // Clip against viewport in screen space - // Clip triangles against all four screen edges, this could yield - // a bunch of triangles, so create a queue that we traverse to - // ensure we only test new triangles generated against planes - triangle sclipped[2]; - std::list listTriangles; - - - // Add initial triangle - listTriangles.push_back(triProjected); - int nNewTriangles = 1; - - for (int p = 0; p < 4; p++) - { - int nTrisToAdd = 0; - while (nNewTriangles > 0) - { - // Take triangle from front of queue - triangle test = listTriangles.front(); - listTriangles.pop_front(); - nNewTriangles--; - - // Clip it against a plane. We only need to test each - // subsequent plane, against subsequent new triangles - // as all triangles after a plane clip are guaranteed - // to lie on the inside of the plane. I like how this - // comment is almost completely and utterly justified - switch (p) - { - case 0: nTrisToAdd = GFX3D::Math::Triangle_ClipAgainstPlane({ 0.0f, -1.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }, test, sclipped[0], sclipped[1]); break; - case 1: nTrisToAdd = GFX3D::Math::Triangle_ClipAgainstPlane({ 0.0f, +1.0f, 0.0f }, { 0.0f, -1.0f, 0.0f }, test, sclipped[0], sclipped[1]); break; - case 2: nTrisToAdd = GFX3D::Math::Triangle_ClipAgainstPlane({ -1.0f, 0.0f, 0.0f }, { 1.0f, 0.0f, 0.0f }, test, sclipped[0], sclipped[1]); break; - case 3: nTrisToAdd = GFX3D::Math::Triangle_ClipAgainstPlane({ +1.0f, 0.0f, 0.0f }, { -1.0f, 0.0f, 0.0f }, test, sclipped[0], sclipped[1]); break; - } - - - // Clipping may yield a variable number of triangles, so - // add these new ones to the back of the queue for subsequent - // clipping against next planes - for (int w = 0; w < nTrisToAdd; w++) - listTriangles.push_back(sclipped[w]); - } - nNewTriangles = listTriangles.size(); - } - - for (auto &triRaster : listTriangles) - { - // Scale to viewport - /*triRaster.p[0].x *= -1.0f; - triRaster.p[1].x *= -1.0f; - triRaster.p[2].x *= -1.0f; - triRaster.p[0].y *= -1.0f; - triRaster.p[1].y *= -1.0f; - triRaster.p[2].y *= -1.0f;*/ - vec3d vOffsetView = { 1,1,0 }; - triRaster.p[0] = Math::Vec_Add(triRaster.p[0], vOffsetView); - triRaster.p[1] = Math::Vec_Add(triRaster.p[1], vOffsetView); - triRaster.p[2] = Math::Vec_Add(triRaster.p[2], vOffsetView); - triRaster.p[0].x *= 0.5f * fViewW; - triRaster.p[0].y *= 0.5f * fViewH; - triRaster.p[1].x *= 0.5f * fViewW; - triRaster.p[1].y *= 0.5f * fViewH; - triRaster.p[2].x *= 0.5f * fViewW; - triRaster.p[2].y *= 0.5f * fViewH; - vOffsetView = { fViewX,fViewY,0 }; - triRaster.p[0] = Math::Vec_Add(triRaster.p[0], vOffsetView); - triRaster.p[1] = Math::Vec_Add(triRaster.p[1], vOffsetView); - triRaster.p[2] = Math::Vec_Add(triRaster.p[2], vOffsetView); - - // For now, just draw triangle - - if (flags & RENDER_TEXTURED) - { - TexturedTriangle( - triRaster.p[0].x, triRaster.p[0].y, triRaster.t[0].x, triRaster.t[0].y, triRaster.t[0].z, - triRaster.p[1].x, triRaster.p[1].y, triRaster.t[1].x, triRaster.t[1].y, triRaster.t[1].z, - triRaster.p[2].x, triRaster.p[2].y, triRaster.t[2].x, triRaster.t[2].y, triRaster.t[2].z, - sprTexture); - } - - if (flags & RENDER_WIRE) - { - DrawTriangleWire(triRaster, olc::RED); - } - - if (flags & RENDER_FLAT) - { - DrawTriangleFlat(triRaster); - } - - nTriangleDrawnCount++; - } - } - } - - return nTriangleDrawnCount; - } -} - -#endif \ No newline at end of file diff --git a/CarCrimeCity/Part1/olcPixelGameEngine.h b/CarCrimeCity/Part1/olcPixelGameEngine.h deleted file mode 100644 index 37c3ae8..0000000 --- a/CarCrimeCity/Part1/olcPixelGameEngine.h +++ /dev/null @@ -1,2067 +0,0 @@ -/* - olcPixelGameEngine.h - - +-------------------------------------------------------------+ - | OneLoneCoder Pixel Game Engine v1.12 | - | "Like the command prompt console one, but not..." - javidx9 | - +-------------------------------------------------------------+ - - What is this? - ~~~~~~~~~~~~~ - The olcConsoleGameEngine has been a surprsing and wonderful - success for me, and I'm delighted how people have reacted so - positively towards it, so thanks for that. - - However, there are limitations that I simply cannot avoid. - Firstly, I need to maintain several different versions of - it to accommodate users on Windows7, 8, 10, Linux, Mac, - Visual Studio & Code::Blocks. Secondly, this year I've been - pushing the console to the limits of its graphical capabilities - and the effect is becoming underwhelming. The engine itself - is not slow at all, but the process that Windows uses to - draw the command prompt to the screen is, and worse still, - it's dynamic based upon the variation of character colours - and glyphs. Sadly I have no control over this, and recent - videos that are extremely graphical (for a command prompt :P ) - have been dipping to unacceptable framerates. As the channel - has been popular with aspiring game developers, I'm concerned - that the visual appeal of the command prompt is perhaps - limited to us oldies, and I dont want to alienate younger - learners. Finally, I'd like to demonstrate many more - algorithms and image processing that exist in the graphical - domain, for which the console is insufficient. - - For this reason, I have created olcPixelGameEngine! The look - and feel to the programmer is almost identical, so all of my - existing code from the videos is easily portable, and the - programmer uses this file in exactly the same way. But I've - decided that rather than just build a command prompt emulator, - that I would at least harness some modern(ish) portable - technologies. - - As a result, the olcPixelGameEngine supports 32-bit colour, is - written in a cross-platform style, uses modern(ish) C++ - conventions and most importantly, renders much much faster. I - will use this version when my applications are predominantly - graphics based, but use the console version when they are - predominantly text based - Don't worry, loads more command - prompt silliness to come yet, but evolution is important!! - - 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 - Patreon: https://www.patreon.com/javidx9 - - Relevant Videos - ~~~~~~~~~~~~~~~ - https://youtu.be/kRH6oJLFYxY Introducing olcPixelGameEngine - - Compiling in Linux - ~~~~~~~~~~~~~~~~~~ - You will need a modern C++ compiler, so update yours! - To compile use the command: - - g++ -o YourProgName YourSource.cpp -lX11 -lGL -lpthread -lpng - - On some Linux configurations, the frame rate is locked to the refresh - rate of the monitor. This engine tries to unlock it but may not be - able to, in which case try launching your program like this: - - vblank_mode=0 ./YourProgName - - - Compiling in Code::Blocks on Windows - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Well I wont judge you, but make sure your Code::Blocks installation - is really up to date - you may even consider updating your C++ toolchain - to use MinGW32-W64, so google this. You will also need to enable C++14 - in your build options, and add to your linker the following libraries: - user32 gdi32 opengl32 gdiplus - - Thanks - ~~~~~~ - I'd like to extend thanks to Eremiell, slavka, gurkanctn, Phantim, - JackOJC, KrossX, Huhlig, Dragoneye, Appa, JustinRichardsMusic, SliceNDice - Ralakus, Gorbit99, raoul & MagetzUb for advice, ideas and testing, and I'd like - to extend my appreciation to the 23K YouTube followers and 1.5K Discord server - members who give me the motivation to keep going with all this :D - - Special thanks to those who bring gifts! - GnarGnarHead.......Domina - Gorbit99...........Bastion - - Special thanks to my Patreons too - I wont name you on here, but I've - certainly enjoyed my tea and flapjacks :D - - Author - ~~~~~~ - David Barr, aka javidx9, ŠOneLoneCoder 2018, 2019 -*/ - -////////////////////////////////////////////////////////////////////////////////////////// - -/* Example Usage (main.cpp) - #define OLC_PGE_APPLICATION - #include "olcPixelGameEngine.h" - // Override base class with your custom functionality - class Example : public olc::PixelGameEngine - { - public: - Example() - { - sAppName = "Example"; - } - public: - bool OnUserCreate() override - { - // Called once at the start, so create things here - return true; - } - bool OnUserUpdate(float fElapsedTime) override - { - // called once per frame, draws random coloured pixels - for (int x = 0; x < ScreenWidth(); x++) - for (int y = 0; y < ScreenHeight(); y++) - Draw(x, y, olc::Pixel(rand() % 255, rand() % 255, rand()% 255)); - return true; - } - }; - int main() - { - Example demo; - if (demo.Construct(256, 240, 4, 4)) - demo.Start(); - return 0; - } -*/ - -#ifndef OLC_PGE_DEF -#define OLC_PGE_DEF - -#ifdef _WIN32 - // Link to libraries -#ifndef __MINGW32__ - #pragma comment(lib, "user32.lib") // Visual Studio Only - #pragma comment(lib, "gdi32.lib") // For other Windows Compilers please add - #pragma comment(lib, "opengl32.lib") // these libs to your linker input - #pragma comment(lib, "gdiplus.lib") -#else - // In Code::Blocks, Select C++14 in your build options, and add the - // following libs to your linker: user32 gdi32 opengl32 gdiplus -#endif - // Include WinAPI - #include - #include - - // OpenGL Extension - #include - typedef BOOL(WINAPI wglSwapInterval_t) (int interval); - static wglSwapInterval_t *wglSwapInterval; -#else - #include - #include - #include - #include - #include - typedef int(glSwapInterval_t) (Display *dpy, GLXDrawable drawable, int interval); - static glSwapInterval_t *glSwapIntervalEXT; -#endif - - -// Standard includes -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#undef min -#undef max - -namespace olc // All OneLoneCoder stuff will now exist in the "olc" namespace -{ - struct Pixel - { - union - { - uint32_t n = 0xFF000000; - struct - { - uint8_t r; uint8_t g; uint8_t b; uint8_t a; - }; - }; - - Pixel(); - Pixel(uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha = 255); - Pixel(uint32_t p); - enum Mode { NORMAL, MASK, ALPHA, CUSTOM }; - }; - - // Some constants for symbolic naming of Pixels - static const Pixel - WHITE(255, 255, 255), - GREY(192, 192, 192), DARK_GREY(128, 128, 128), VERY_DARK_GREY(64, 64, 64), - RED(255, 0, 0), DARK_RED(128, 0, 0), VERY_DARK_RED(64, 0, 0), - YELLOW(255, 255, 0), DARK_YELLOW(128, 128, 0), VERY_DARK_YELLOW(64, 64, 0), - GREEN(0, 255, 0), DARK_GREEN(0, 128, 0), VERY_DARK_GREEN(0, 64, 0), - CYAN(0, 255, 255), DARK_CYAN(0, 128, 128), VERY_DARK_CYAN(0, 64, 64), - BLUE(0, 0, 255), DARK_BLUE(0, 0, 128), VERY_DARK_BLUE(0, 0, 64), - MAGENTA(255, 0, 255), DARK_MAGENTA(128, 0, 128), VERY_DARK_MAGENTA(64, 0, 64), - BLACK(0, 0, 0), - BLANK(0, 0, 0, 0); - - enum rcode - { - FAIL = 0, - OK = 1, - NO_FILE = -1, - }; - - //============================================================= - - struct HWButton - { - bool bPressed = false; // Set once during the frame the event occurs - bool bReleased = false; // Set once during the frame the event occurs - bool bHeld = false; // Set tru for all frames between pressed and released events - }; - - //============================================================= - - class ResourcePack - { - public: - ResourcePack(); - ~ResourcePack(); - struct sEntry : public std::streambuf { - uint32_t nID, nFileOffset, nFileSize; uint8_t* data; void _config() { this->setg((char*)data, (char*)data, (char*)(data + nFileSize)); } - }; - - public: - olc::rcode AddToPack(std::string sFile); - - public: - olc::rcode SavePack(std::string sFile); - olc::rcode LoadPack(std::string sFile); - olc::rcode ClearPack(); - - public: - olc::ResourcePack::sEntry GetStreamBuffer(std::string sFile); - - private: - - std::map mapFiles; - }; - - //============================================================= - - // A bitmap-like structure that stores a 2D array of Pixels - class Sprite - { - public: - Sprite(); - Sprite(std::string sImageFile); - Sprite(std::string sImageFile, olc::ResourcePack *pack); - Sprite(int32_t w, int32_t h); - ~Sprite(); - - public: - olc::rcode LoadFromFile(std::string sImageFile, olc::ResourcePack *pack = nullptr); - olc::rcode LoadFromPGESprFile(std::string sImageFile, olc::ResourcePack *pack = nullptr); - olc::rcode SaveToPGESprFile(std::string sImageFile); - - public: - int32_t width = 0; - int32_t height = 0; - enum Mode { NORMAL, PERIODIC }; - - public: - void SetSampleMode(olc::Sprite::Mode mode = olc::Sprite::Mode::NORMAL); - Pixel GetPixel(int32_t x, int32_t y); - void SetPixel(int32_t x, int32_t y, Pixel p); - Pixel Sample(float x, float y); - Pixel* GetData(); - - private: - Pixel *pColData = nullptr; - Mode modeSample = Mode::NORMAL; - -#ifdef OLC_DBG_OVERDRAW - public: - static int nOverdrawCount; -#endif - - }; - - //============================================================= - - enum Key - { - A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, - K0, K1, K2, K3, K4, K5, K6, K7, K8, K9, - F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, - UP, DOWN, LEFT, RIGHT, - SPACE, TAB, SHIFT, CTRL, INS, DEL, HOME, END, PGUP, PGDN, - BACK, ESCAPE, RETURN, ENTER, PAUSE, SCROLL, - NP0, NP1, NP2, NP3, NP4, NP5, NP6, NP7, NP8, NP9, - NP_MUL, NP_DIV, NP_ADD, NP_SUB, NP_DECIMAL, - }; - - - //============================================================= - - class PixelGameEngine - { - public: - PixelGameEngine(); - - public: - olc::rcode Construct(uint32_t screen_w, uint32_t screen_h, uint32_t pixel_w, uint32_t pixel_h); - olc::rcode Start(); - - public: // Override Interfaces - // Called once on application startup, use to load your resources - virtual bool OnUserCreate(); - // Called every frame, and provides you with a time per frame value - virtual bool OnUserUpdate(float fElapsedTime); - // Called once on application termination, so you can be a clean coder - virtual bool OnUserDestroy(); - - public: // Hardware Interfaces - // Returns true if window is currently in focus - bool IsFocused(); - // Get the state of a specific keyboard button - HWButton GetKey(Key k); - // Get the state of a specific mouse button - HWButton GetMouse(uint32_t b); - // Get Mouse X coordinate in "pixel" space - int32_t GetMouseX(); - // Get Mouse Y coordinate in "pixel" space - int32_t GetMouseY(); - - public: // Utility - // Returns the width of the screen in "pixels" - int32_t ScreenWidth(); - // Returns the height of the screen in "pixels" - int32_t ScreenHeight(); - // Returns the width of the currently selected drawing target in "pixels" - int32_t GetDrawTargetWidth(); - // Returns the height of the currently selected drawing target in "pixels" - int32_t GetDrawTargetHeight(); - // Returns the currently active draw target - Sprite* GetDrawTarget(); - - public: // Draw Routines - // Specify which Sprite should be the target of drawing functions, use nullptr - // to specify the primary screen - void SetDrawTarget(Sprite *target); - // Change the pixel mode for different optimisations - // olc::Pixel::NORMAL = No transparency - // olc::Pixel::MASK = Transparent if alpha is < 255 - // olc::Pixel::ALPHA = Full transparency - void SetPixelMode(Pixel::Mode m); - Pixel::Mode GetPixelMode(); - // Use a custom blend function - void SetPixelMode(std::function pixelMode); - // Change the blend factor form between 0.0f to 1.0f; - void SetPixelBlend(float fBlend); - // Offset texels by sub-pixel amount (advanced, do not use) - void SetSubPixelOffset(float ox, float oy); - - // Draws a single Pixel - virtual void Draw(int32_t x, int32_t y, Pixel p = olc::WHITE); - // Draws a line from (x1,y1) to (x2,y2) - void DrawLine(int32_t x1, int32_t y1, int32_t x2, int32_t y2, Pixel p = olc::WHITE); - // Draws a circle located at (x,y) with radius - void DrawCircle(int32_t x, int32_t y, int32_t radius, Pixel p = olc::WHITE); - // Fills a circle located at (x,y) with radius - void FillCircle(int32_t x, int32_t y, int32_t radius, Pixel p = olc::WHITE); - // Draws a rectangle at (x,y) to (x+w,y+h) - void DrawRect(int32_t x, int32_t y, int32_t w, int32_t h, Pixel p = olc::WHITE); - // Fills a rectangle at (x,y) to (x+w,y+h) - void FillRect(int32_t x, int32_t y, int32_t w, int32_t h, Pixel p = olc::WHITE); - // Draws a triangle between points (x1,y1), (x2,y2) and (x3,y3) - void DrawTriangle(int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t x3, int32_t y3, Pixel p = olc::WHITE); - // Flat fills a triangle between points (x1,y1), (x2,y2) and (x3,y3) - void FillTriangle(int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t x3, int32_t y3, Pixel p = olc::WHITE); - // Draws an entire sprite at location (x,y) - void DrawSprite(int32_t x, int32_t y, Sprite *sprite, uint32_t scale = 1); - // Draws an area of a sprite at location (x,y), where the - // selected area is (ox,oy) to (ox+w,oy+h) - void DrawPartialSprite(int32_t x, int32_t y, Sprite *sprite, int32_t ox, int32_t oy, int32_t w, int32_t h, uint32_t scale = 1); - // Draws a single line of text - void DrawString(int32_t x, int32_t y, std::string sText, Pixel col = olc::WHITE, uint32_t scale = 1); - // Clears entire draw target to Pixel - void Clear(Pixel p); - - public: // Branding - std::string sAppName; - - private: // Inner mysterious workings - Sprite *pDefaultDrawTarget = nullptr; - Sprite *pDrawTarget = nullptr; - Pixel::Mode nPixelMode = Pixel::NORMAL; - float fBlendFactor = 1.0f; - uint32_t nScreenWidth = 256; - uint32_t nScreenHeight = 240; - uint32_t nPixelWidth = 4; - uint32_t nPixelHeight = 4; - int32_t nMousePosX = 0; - int32_t nMousePosY = 0; - float fPixelX = 1.0f; - float fPixelY = 1.0f; - float fSubPixelOffsetX = 0.0f; - float fSubPixelOffsetY = 0.0f; - bool bHasInputFocus = false; - bool bHasMouseFocus = false; - float fFrameTimer = 1.0f; - int nFrameCount = 0; - Sprite *fontSprite = nullptr; - std::function funcPixelMode; - - static std::map mapKeys; - bool pKeyNewState[256]{ 0 }; - bool pKeyOldState[256]{ 0 }; - HWButton pKeyboardState[256]; - - bool pMouseNewState[5]{ 0 }; - bool pMouseOldState[5]{ 0 }; - HWButton pMouseState[5]; - -#ifdef _WIN32 - HDC glDeviceContext = nullptr; - HGLRC glRenderContext = nullptr; -#else - GLXContext glDeviceContext = nullptr; - GLXContext glRenderContext = nullptr; -#endif - GLuint glBuffer; - - void EngineThread(); - - // If anything sets this flag to false, the engine - // "should" shut down gracefully - static std::atomic bAtomActive; - - // Common initialisation functions - void olc_UpdateMouse(int32_t x, int32_t y); - bool olc_OpenGLCreate(); - void olc_ConstructFontSheet(); - -#ifdef _WIN32 - // Windows specific window handling - HWND olc_hWnd = nullptr; - HWND olc_WindowCreate(); - std::wstring wsAppName; - static LRESULT CALLBACK olc_WindowEvent(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); -#else - // Non-Windows specific window handling - Display* olc_Display = nullptr; - Window olc_WindowRoot; - Window olc_Window; - XVisualInfo* olc_VisualInfo; - Colormap olc_ColourMap; - XSetWindowAttributes olc_SetWindowAttribs; - Display* olc_WindowCreate(); -#endif - - }; - - - class PGEX - { - friend class olc::PixelGameEngine; - protected: - static PixelGameEngine* pge; - }; - - //============================================================= -} - -#endif // OLC_PGE_DEF - - - - -/* - Object Oriented Mode - ~~~~~~~~~~~~~~~~~~~~ - - If the olcPixelGameEngine.h is called from several sources it can cause - multiple definitions of objects. To prevent this, ONLY ONE of the pathways - to including this file must have OLC_PGE_APPLICATION defined before it. This prevents - the definitions being duplicated. - - Consider the following project structure: - - Class1.h - Includes olcPixelGameEngine.h, overrides olc::PixelGameEngine - Class1.cpp - #define OLC_PGE_APPLICATION #include "Class1.h" - Class2.h - Includes Class1.h, which includes olcPixelGameEngine.h - Class2.cpp - #define OLC_PGE_APPLICATION #include "Class2.h" - main.cpp - Includes Class1.h and Class2.h - - If all of this is a bit too confusing, you can split this file in two! - Everything below this comment block can go into olcPixelGameEngineOOP.cpp - and everything above it can go into olcPixelGameEngineOOP.h - -*/ - -#ifdef OLC_PGE_APPLICATION -#undef OLC_PGE_APPLICATION - -namespace olc -{ - Pixel::Pixel() - { - r = 0; g = 0; b = 0; a = 255; - } - - Pixel::Pixel(uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha) - { - r = red; g = green; b = blue; a = alpha; - } - - Pixel::Pixel(uint32_t p) - { - n = p; - } - - //========================================================== - - std::wstring ConvertS2W(std::string s) - { -#ifdef _WIN32 - int count = MultiByteToWideChar(CP_UTF8, 0, s.c_str(), -1, NULL, 0); - wchar_t* buffer = new wchar_t[count]; - MultiByteToWideChar(CP_UTF8, 0, s.c_str(), -1, buffer, count); - std::wstring w(buffer); - delete[] buffer; - return w; -#endif -//#ifdef __MINGW32__ -// wchar_t *buffer = new wchar_t[sImageFile.length() + 1]; -// mbstowcs(buffer, sImageFile.c_str(), sImageFile.length()); -// buffer[sImageFile.length()] = L'\0'; -// wsImageFile = buffer; -// delete[] buffer; -//#else - } - - Sprite::Sprite() - { - pColData = nullptr; - width = 0; - height = 0; - } - - Sprite::Sprite(std::string sImageFile) - { - LoadFromFile(sImageFile); - } - - Sprite::Sprite(std::string sImageFile, olc::ResourcePack *pack) - { - LoadFromPGESprFile(sImageFile, pack); - } - - Sprite::Sprite(int32_t w, int32_t h) - { - if(pColData) delete[] pColData; - width = w; height = h; - pColData = new Pixel[width * height]; - for (int32_t i = 0; i < width*height; i++) - pColData[i] = Pixel(); - } - - Sprite::~Sprite() - { - if (pColData) delete pColData; - } - - olc::rcode Sprite::LoadFromPGESprFile(std::string sImageFile, olc::ResourcePack *pack) - { - if (pColData) delete[] pColData; - - auto ReadData = [&](std::istream &is) - { - is.read((char*)&width, sizeof(int32_t)); - is.read((char*)&height, sizeof(int32_t)); - pColData = new Pixel[width * height]; - is.read((char*)pColData, width * height * sizeof(uint32_t)); - }; - - // These are essentially Memory Surfaces represented by olc::Sprite - // which load very fast, but are completely uncompressed - if (pack == nullptr) - { - std::ifstream ifs; - ifs.open(sImageFile, std::ifstream::binary); - if (ifs.is_open()) - { - ReadData(ifs); - return olc::OK; - } - else - return olc::FAIL; - } - else - { - auto streamBuffer = pack->GetStreamBuffer(sImageFile); - std::istream is(&streamBuffer); - ReadData(is); - } - - - return olc::FAIL; - } - - olc::rcode Sprite::SaveToPGESprFile(std::string sImageFile) - { - if (pColData == nullptr) return olc::FAIL; - - std::ofstream ofs; - ofs.open(sImageFile, std::ifstream::binary); - if (ofs.is_open()) - { - ofs.write((char*)&width, sizeof(int32_t)); - ofs.write((char*)&height, sizeof(int32_t)); - ofs.write((char*)pColData, width*height*sizeof(uint32_t)); - ofs.close(); - return olc::OK; - } - - return olc::FAIL; - } - - olc::rcode Sprite::LoadFromFile(std::string sImageFile, olc::ResourcePack *pack) - { -#ifdef _WIN32 - // Use GDI+ - std::wstring wsImageFile; -#ifdef __MINGW32__ - wchar_t *buffer = new wchar_t[sImageFile.length() + 1]; - mbstowcs(buffer, sImageFile.c_str(), sImageFile.length()); - buffer[sImageFile.length()] = L'\0'; - wsImageFile = buffer; - delete [] buffer; -#else - wsImageFile = ConvertS2W(sImageFile); -#endif - Gdiplus::Bitmap *bmp = Gdiplus::Bitmap::FromFile(wsImageFile.c_str()); - if (bmp == nullptr) - return olc::NO_FILE; - - width = bmp->GetWidth(); - height = bmp->GetHeight(); - pColData = new Pixel[width * height]; - - for(int x=0; xGetPixel(x, y, &c); - SetPixel(x, y, Pixel(c.GetRed(), c.GetGreen(), c.GetBlue(), c.GetAlpha())); - } - delete bmp; - return olc::OK; -#else - //////////////////////////////////////////////////////////////////////////// - // Use libpng, Thanks to Guillaume Cottenceau - // https://gist.github.com/niw/5963798 - png_structp png; - png_infop info; - - FILE *f = fopen(sImageFile.c_str(), "rb"); - if (!f) return olc::NO_FILE; - - png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); - if (!png) goto fail_load; - - info = png_create_info_struct(png); - if (!info) goto fail_load; - - if (setjmp(png_jmpbuf(png))) goto fail_load; - - png_init_io(png, f); - png_read_info(png, info); - - png_byte color_type; - png_byte bit_depth; - png_bytep *row_pointers; - width = png_get_image_width(png, info); - height = png_get_image_height(png, info); - color_type = png_get_color_type(png, info); - bit_depth = png_get_bit_depth(png, info); - -#ifdef _DEBUG - std::cout << "Loading PNG: " << sImageFile << "\n"; - std::cout << "W:" << width << " H:" << height << " D:" << (int)bit_depth << "\n"; -#endif - - if (bit_depth == 16) png_set_strip_16(png); - if (color_type == PNG_COLOR_TYPE_PALETTE) png_set_palette_to_rgb(png); - if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) png_set_expand_gray_1_2_4_to_8(png); - if (png_get_valid(png, info, PNG_INFO_tRNS)) png_set_tRNS_to_alpha(png); - if (color_type == PNG_COLOR_TYPE_RGB || - color_type == PNG_COLOR_TYPE_GRAY || - color_type == PNG_COLOR_TYPE_PALETTE) - png_set_filler(png, 0xFF, PNG_FILLER_AFTER); - if (color_type == PNG_COLOR_TYPE_GRAY || - color_type == PNG_COLOR_TYPE_GRAY_ALPHA) - png_set_gray_to_rgb(png); - - png_read_update_info(png, info); - row_pointers = (png_bytep*)malloc(sizeof(png_bytep) * height); - for (int y = 0; y < height; y++) { - row_pointers[y] = (png_byte*)malloc(png_get_rowbytes(png, info)); - } - png_read_image(png, row_pointers); - //////////////////////////////////////////////////////////////////////////// - - // Create sprite array - pColData = new Pixel[width * height]; - - // Iterate through image rows, converting into sprite format - for (int y = 0; y < height; y++) - { - png_bytep row = row_pointers[y]; - for (int x = 0; x < width; x++) - { - png_bytep px = &(row[x * 4]); - SetPixel(x, y, Pixel(px[0], px[1], px[2], px[3])); - } - } - - fclose(f); - return olc::OK; - - fail_load: - width = 0; - height = 0; - fclose(f); - pColData = nullptr; - return olc::FAIL; -#endif - } - - void Sprite::SetSampleMode(olc::Sprite::Mode mode) - { - modeSample = mode; - } - - - Pixel Sprite::GetPixel(int32_t x, int32_t y) - { - if (modeSample == olc::Sprite::Mode::NORMAL) - { - if (x >= 0 && x < width && y >= 0 && y < height) - return pColData[y*width + x]; - else - return Pixel(0, 0, 0, 0); - } - else - { - return pColData[abs(y%height)*width + abs(x%width)]; - } - } - - void Sprite::SetPixel(int32_t x, int32_t y, Pixel p) - { - -#ifdef OLC_DBG_OVERDRAW - nOverdrawCount++; -#endif - - if (x >= 0 && x < width && y >= 0 && y < height) - pColData[y*width + x] = p; - } - - Pixel Sprite::Sample(float x, float y) - { - int32_t sx = (int32_t)((x * (float)width) - 0.5f); - int32_t sy = (int32_t)((y * (float)height) - 0.5f); - return GetPixel(sx, sy); - } - - Pixel* Sprite::GetData() { return pColData; } - - //========================================================== - - ResourcePack::ResourcePack() - { - - } - - ResourcePack::~ResourcePack() - { - ClearPack(); - } - - olc::rcode ResourcePack::AddToPack(std::string sFile) - { - std::ifstream ifs(sFile, std::ifstream::binary); - if (!ifs.is_open()) return olc::FAIL; - - // Get File Size - std::streampos p = 0; - p = ifs.tellg(); - ifs.seekg(0, std::ios::end); - p = ifs.tellg() - p; - ifs.seekg(0, std::ios::beg); - - // Create entry - sEntry e; - e.data = nullptr; - e.nFileSize = (uint32_t)p; - - // Read file into memory - e.data = new uint8_t[(uint32_t)e.nFileSize]; - ifs.read((char*)e.data, e.nFileSize); - ifs.close(); - - // Add To Map - mapFiles[sFile] = e; - return olc::OK; - } - - olc::rcode ResourcePack::SavePack(std::string sFile) - { - std::ofstream ofs(sFile, std::ofstream::binary); - if (!ofs.is_open()) return olc::FAIL; - - // 1) Write Map - size_t nMapSize = mapFiles.size(); - ofs.write((char*)&nMapSize, sizeof(size_t)); - for (auto &e : mapFiles) - { - size_t nPathSize = e.first.size(); - ofs.write((char*)&nPathSize, sizeof(size_t)); - ofs.write(e.first.c_str(), nPathSize); - ofs.write((char*)&e.second.nID, sizeof(uint32_t)); - ofs.write((char*)&e.second.nFileSize, sizeof(uint32_t)); - ofs.write((char*)&e.second.nFileOffset, sizeof(uint32_t)); - } - - // 2) Write Data - std::streampos offset = ofs.tellp(); - for (auto &e : mapFiles) - { - e.second.nFileOffset = (uint32_t)offset; - ofs.write((char*)e.second.data, e.second.nFileSize); - offset += e.second.nFileSize; - } - - // 3) Rewrite Map (it has been updated with offsets now) - ofs.seekp(std::ios::beg); - ofs.write((char*)&nMapSize, sizeof(size_t)); - for (auto &e : mapFiles) - { - size_t nPathSize = e.first.size(); - ofs.write((char*)&nPathSize, sizeof(size_t)); - ofs.write(e.first.c_str(), nPathSize); - ofs.write((char*)&e.second.nID, sizeof(uint32_t)); - ofs.write((char*)&e.second.nFileSize, sizeof(uint32_t)); - ofs.write((char*)&e.second.nFileOffset, sizeof(uint32_t)); - } - ofs.close(); - - return olc::OK; - } - - olc::rcode ResourcePack::LoadPack(std::string sFile) - { - std::ifstream ifs(sFile, std::ifstream::binary); - if (!ifs.is_open()) return olc::FAIL; - - // 1) Read Map - size_t nMapEntries; - ifs.read((char*)&nMapEntries, sizeof(size_t)); - for (size_t i = 0; i < nMapEntries; i++) - { - size_t nFilePathSize = 0; - ifs.read((char*)&nFilePathSize, sizeof(size_t)); - - std::string sFileName(nFilePathSize, ' '); - for (size_t j = 0; j < nFilePathSize; j++) - sFileName[j] = ifs.get(); - - sEntry e; - e.data = nullptr; - ifs.read((char*)&e.nID, sizeof(uint32_t)); - ifs.read((char*)&e.nFileSize, sizeof(uint32_t)); - ifs.read((char*)&e.nFileOffset, sizeof(uint32_t)); - mapFiles[sFileName] = e; - } - - // 2) Read Data - for (auto &e : mapFiles) - { - e.second.data = new uint8_t[(uint32_t)e.second.nFileSize]; - ifs.seekg(e.second.nFileOffset); - ifs.read((char*)e.second.data, e.second.nFileSize); - e.second._config(); - } - - ifs.close(); - return olc::OK; - } - - olc::ResourcePack::sEntry ResourcePack::GetStreamBuffer(std::string sFile) - { - return mapFiles[sFile]; - } - - olc::rcode ResourcePack::ClearPack() - { - for (auto &e : mapFiles) - { - if (e.second.data != nullptr) - delete[] e.second.data; - } - - mapFiles.clear(); - return olc::OK; - } - - //========================================================== - - PixelGameEngine::PixelGameEngine() - { - sAppName = "Undefined"; - olc::PGEX::pge = this; - } - - olc::rcode PixelGameEngine::Construct(uint32_t screen_w, uint32_t screen_h, uint32_t pixel_w, uint32_t pixel_h) - { - nScreenWidth = screen_w; - nScreenHeight = screen_h; - nPixelWidth = pixel_w; - nPixelHeight = pixel_h; - - fPixelX = 2.0f / (float)(nScreenWidth); - fPixelY = 2.0f / (float)(nScreenHeight); - - if (nPixelWidth == 0 || nPixelHeight == 0 || nScreenWidth == 0 || nScreenHeight == 0) - return olc::FAIL; - -#ifdef _WIN32 -#ifdef UNICODE -#ifndef __MINGW32__ - wsAppName = ConvertS2W(sAppName); -#endif -#endif -#endif - // Load the default font sheet - olc_ConstructFontSheet(); - - // Create a sprite that represents the primary drawing target - pDefaultDrawTarget = new Sprite(nScreenWidth, nScreenHeight); - SetDrawTarget(nullptr); - return olc::OK; - } - - olc::rcode PixelGameEngine::Start() - { - // Construct the window - if (!olc_WindowCreate()) - return olc::FAIL; - - // Load libraries required for PNG file interaction -#ifdef _WIN32 - // Windows use GDI+ - Gdiplus::GdiplusStartupInput startupInput; - ULONG_PTR token; - Gdiplus::GdiplusStartup(&token, &startupInput, NULL); -#else - // Linux use libpng - -#endif - // Start the thread - bAtomActive = true; - std::thread t = std::thread(&PixelGameEngine::EngineThread, this); - -#ifdef _WIN32 - // Handle Windows Message Loop - MSG msg; - while (GetMessage(&msg, NULL, 0, 0) > 0) - { - TranslateMessage(&msg); - DispatchMessage(&msg); - } -#endif - - // Wait for thread to be exited - t.join(); - return olc::OK; - } - - void PixelGameEngine::SetDrawTarget(Sprite *target) - { - if (target) - pDrawTarget = target; - else - pDrawTarget = pDefaultDrawTarget; - } - - Sprite* PixelGameEngine::GetDrawTarget() - { - return pDrawTarget; - } - - int32_t PixelGameEngine::GetDrawTargetWidth() - { - if (pDrawTarget) - return pDrawTarget->width; - else - return 0; - } - - int32_t PixelGameEngine::GetDrawTargetHeight() - { - if (pDrawTarget) - return pDrawTarget->height; - else - return 0; - } - - bool PixelGameEngine::IsFocused() - { - return bHasInputFocus; - } - - HWButton PixelGameEngine::GetKey(Key k) - { - return pKeyboardState[k]; - } - - HWButton PixelGameEngine::GetMouse(uint32_t b) - { - return pMouseState[b]; - } - - int32_t PixelGameEngine::GetMouseX() - { - return nMousePosX; - } - - int32_t PixelGameEngine::GetMouseY() - { - return nMousePosY; - } - - int32_t PixelGameEngine::ScreenWidth() - { - return nScreenWidth; - } - - int32_t PixelGameEngine::ScreenHeight() - { - return nScreenHeight; - } - - void PixelGameEngine::Draw(int32_t x, int32_t y, Pixel p) - { - if (!pDrawTarget) return; - - - if (nPixelMode == Pixel::NORMAL) - { - pDrawTarget->SetPixel(x, y, p); - return; - } - - if (nPixelMode == Pixel::MASK) - { - if(p.a == 255) - pDrawTarget->SetPixel(x, y, p); - return; - } - - if (nPixelMode == Pixel::ALPHA) - { - Pixel d = pDrawTarget->GetPixel(x, y); - float a = (float)(p.a / 255.0f) * fBlendFactor; - float c = 1.0f - a; - float r = a * (float)p.r + c * (float)d.r; - float g = a * (float)p.g + c * (float)d.g; - float b = a * (float)p.b + c * (float)d.b; - pDrawTarget->SetPixel(x, y, Pixel((uint8_t)r, (uint8_t)g, (uint8_t)b)); - return; - } - - if (nPixelMode == Pixel::CUSTOM) - { - pDrawTarget->SetPixel(x, y, funcPixelMode(x, y, p, pDrawTarget->GetPixel(x, y))); - return; - } - } - - void PixelGameEngine::SetSubPixelOffset(float ox, float oy) - { - fSubPixelOffsetX = ox * fPixelX; - fSubPixelOffsetY = oy * fPixelY; - } - - void PixelGameEngine::DrawLine(int32_t x1, int32_t y1, int32_t x2, int32_t y2, Pixel p) - { - int x, y, dx, dy, dx1, dy1, px, py, xe, ye, i; - dx = x2 - x1; dy = y2 - y1; - - // straight lines idea by gurkanctn - if (dx == 0) // Line is vertical - { - if (y2 < y1) std::swap(y1, y2); - for (y = y1; y <= y2; y++) - Draw(x1, y, p); - return; - } - - if (dy == 0) // Line is horizontal - { - if (x2 < x1) std::swap(x1, x2); - for (x = x1; x <= x2; x++) - Draw(x, y1, p); - return; - } - - // Line is Funk-aye - dx1 = abs(dx); dy1 = abs(dy); - px = 2 * dy1 - dx1; py = 2 * dx1 - dy1; - if (dy1 <= dx1) - { - if (dx >= 0) - { - x = x1; y = y1; xe = x2; - } - else - { - x = x2; y = y2; xe = x1; - } - - Draw(x, y, p); - - for (i = 0; x0 && dy>0)) y = y + 1; else y = y - 1; - px = px + 2 * (dy1 - dx1); - } - Draw(x, y, p); - } - } - else - { - if (dy >= 0) - { - x = x1; y = y1; ye = y2; - } - else - { - x = x2; y = y2; ye = y1; - } - - Draw(x, y, p); - - for (i = 0; y0 && dy>0)) x = x + 1; else x = x - 1; - py = py + 2 * (dx1 - dy1); - } - Draw(x, y, p); - } - } - } - - void PixelGameEngine::DrawCircle(int32_t x, int32_t y, int32_t radius, Pixel p) - { - int x0 = 0; - int y0 = radius; - int d = 3 - 2 * radius; - if (!radius) return; - - while (y0 >= x0) // only formulate 1/8 of circle - { - Draw(x - x0, y - y0, p);//upper left left - Draw(x - y0, y - x0, p);//upper upper left - Draw(x + y0, y - x0, p);//upper upper right - Draw(x + x0, y - y0, p);//upper right right - Draw(x - x0, y + y0, p);//lower left left - Draw(x - y0, y + x0, p);//lower lower left - Draw(x + y0, y + x0, p);//lower lower right - Draw(x + x0, y + y0, p);//lower right right - if (d < 0) d += 4 * x0++ + 6; - else d += 4 * (x0++ - y0--) + 10; - } - } - - void PixelGameEngine::FillCircle(int32_t x, int32_t y, int32_t radius, Pixel p) - { - // Taken from wikipedia - int x0 = 0; - int y0 = radius; - int d = 3 - 2 * radius; - if (!radius) return; - - auto drawline = [&](int sx, int ex, int ny) - { - for (int i = sx; i <= ex; i++) - Draw(i, ny, p); - }; - - while (y0 >= x0) - { - // Modified to draw scan-lines instead of edges - drawline(x - x0, x + x0, y - y0); - drawline(x - y0, x + y0, y - x0); - drawline(x - x0, x + x0, y + y0); - drawline(x - y0, x + y0, y + x0); - if (d < 0) d += 4 * x0++ + 6; - else d += 4 * (x0++ - y0--) + 10; - } - } - - void PixelGameEngine::DrawRect(int32_t x, int32_t y, int32_t w, int32_t h, Pixel p) - { - DrawLine(x, y, x+w, y, p); - DrawLine(x+w, y, x+w, y+h, p); - DrawLine(x+w, y+h, x, y+h, p); - DrawLine(x, y+h, x, y, p); - } - - void PixelGameEngine::Clear(Pixel p) - { - int pixels = GetDrawTargetWidth() * GetDrawTargetHeight(); - Pixel* m = GetDrawTarget()->GetData(); - for (int i = 0; i < pixels; i++) - m[i] = p; -#ifdef OLC_DBG_OVERDRAW - olc::Sprite::nOverdrawCount += pixels; -#endif - } - - void PixelGameEngine::FillRect(int32_t x, int32_t y, int32_t w, int32_t h, Pixel p) - { - int32_t x2 = x + w; - int32_t y2 = y + h; - - if (x < 0) x = 0; - if (x >= (int32_t)nScreenWidth) x = (int32_t)nScreenWidth; - if (y < 0) y = 0; - if (y >= (int32_t)nScreenHeight) y = (int32_t)nScreenHeight; - - if (x2 < 0) x2 = 0; - if (x2 >= (int32_t)nScreenWidth) x2 = (int32_t)nScreenWidth; - if (y2 < 0) y2 = 0; - if (y2 >= (int32_t)nScreenHeight) y2 = (int32_t)nScreenHeight; - - for (int i = x; i < x2; i++) - for (int j = y; j < y2; j++) - Draw(i, j, p); - } - - void PixelGameEngine::DrawTriangle(int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t x3, int32_t y3, Pixel p) - { - DrawLine(x1, y1, x2, y2, p); - DrawLine(x2, y2, x3, y3, p); - DrawLine(x3, y3, x1, y1, p); - } - - // https://www.avrfreaks.net/sites/default/files/triangles.c - void PixelGameEngine::FillTriangle(int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t x3, int32_t y3, Pixel p) - { - auto SWAP = [](int &x, int &y) { int t = x; x = y; y = t; }; - auto drawline = [&](int sx, int ex, int ny) { for (int i = sx; i <= ex; i++) Draw(i, ny, p); }; - - int t1x, t2x, y, minx, maxx, t1xp, t2xp; - bool changed1 = false; - bool changed2 = false; - int signx1, signx2, dx1, dy1, dx2, dy2; - int e1, e2; - // Sort vertices - if (y1>y2) { SWAP(y1, y2); SWAP(x1, x2); } - if (y1>y3) { SWAP(y1, y3); SWAP(x1, x3); } - if (y2>y3) { SWAP(y2, y3); SWAP(x2, x3); } - - t1x = t2x = x1; y = y1; // Starting points - dx1 = (int)(x2 - x1); if (dx1<0) { dx1 = -dx1; signx1 = -1; } - else signx1 = 1; - dy1 = (int)(y2 - y1); - - dx2 = (int)(x3 - x1); if (dx2<0) { dx2 = -dx2; signx2 = -1; } - else signx2 = 1; - dy2 = (int)(y3 - y1); - - if (dy1 > dx1) { // swap values - SWAP(dx1, dy1); - changed1 = true; - } - if (dy2 > dx2) { // swap values - SWAP(dy2, dx2); - changed2 = true; - } - - e2 = (int)(dx2 >> 1); - // Flat top, just process the second half - if (y1 == y2) goto next; - e1 = (int)(dx1 >> 1); - - for (int i = 0; i < dx1;) { - t1xp = 0; t2xp = 0; - if (t1x= dx1) { - e1 -= dx1; - if (changed1) t1xp = signx1;//t1x += signx1; - else goto next1; - } - if (changed1) break; - else t1x += signx1; - } - // Move line - next1: - // process second line until y value is about to change - while (1) { - e2 += dy2; - while (e2 >= dx2) { - e2 -= dx2; - if (changed2) t2xp = signx2;//t2x += signx2; - else goto next2; - } - if (changed2) break; - else t2x += signx2; - } - next2: - if (minx>t1x) minx = t1x; if (minx>t2x) minx = t2x; - if (maxx dx1) { // swap values - SWAP(dy1, dx1); - changed1 = true; - } - else changed1 = false; - - e1 = (int)(dx1 >> 1); - - for (int i = 0; i <= dx1; i++) { - t1xp = 0; t2xp = 0; - if (t1x= dx1) { - e1 -= dx1; - if (changed1) { t1xp = signx1; break; }//t1x += signx1; - else goto next3; - } - if (changed1) break; - else t1x += signx1; - if (i= dx2) { - e2 -= dx2; - if (changed2) t2xp = signx2; - else goto next4; - } - if (changed2) break; - else t2x += signx2; - } - next4: - - if (minx>t1x) minx = t1x; if (minx>t2x) minx = t2x; - if (maxxy3) return; - } - } - - void PixelGameEngine::DrawSprite(int32_t x, int32_t y, Sprite *sprite, uint32_t scale) - { - if (sprite == nullptr) - return; - - if (scale > 1) - { - for (int32_t i = 0; i < sprite->width; i++) - for (int32_t j = 0; j < sprite->height; j++) - for (uint32_t is = 0; is < scale; is++) - for (uint32_t js = 0; js < scale; js++) - Draw(x + (i*scale) + is, y + (j*scale) + js, sprite->GetPixel(i, j)); - } - else - { - for (int32_t i = 0; i < sprite->width; i++) - for (int32_t j = 0; j < sprite->height; j++) - Draw(x + i, y + j, sprite->GetPixel(i, j)); - } - } - - void PixelGameEngine::DrawPartialSprite(int32_t x, int32_t y, Sprite *sprite, int32_t ox, int32_t oy, int32_t w, int32_t h, uint32_t scale) - { - if (sprite == nullptr) - return; - - if (scale > 1) - { - for (int32_t i = 0; i < w; i++) - for (int32_t j = 0; j < h; j++) - for (uint32_t is = 0; is < scale; is++) - for (uint32_t js = 0; js < scale; js++) - Draw(x + (i*scale) + is, y + (j*scale) + js, sprite->GetPixel(i + ox, j + oy)); - } - else - { - for (int32_t i = 0; i < w; i++) - for (int32_t j = 0; j < h; j++) - Draw(x + i, y + j, sprite->GetPixel(i + ox, j + oy)); - } - } - - void PixelGameEngine::DrawString(int32_t x, int32_t y, std::string sText, Pixel col, uint32_t scale) - { - int32_t sx = 0; - int32_t sy = 0; - Pixel::Mode m = nPixelMode; - if(col.ALPHA != 255) SetPixelMode(Pixel::ALPHA); - else SetPixelMode(Pixel::MASK); - for (auto c : sText) - { - if (c == '\n') - { - sx = 0; sy += 8 * scale; - } - else - { - int32_t ox = (c - 32) % 16; - int32_t oy = (c - 32) / 16; - - if (scale > 1) - { - for (uint32_t i = 0; i < 8; i++) - for (uint32_t j = 0; j < 8; j++) - if (fontSprite->GetPixel(i + ox * 8, j + oy * 8).r > 0) - for (uint32_t is = 0; is < scale; is++) - for (uint32_t js = 0; js < scale; js++) - Draw(x + sx + (i*scale) + is, y + sy + (j*scale) + js, col); - } - else - { - for (uint32_t i = 0; i < 8; i++) - for (uint32_t j = 0; j < 8; j++) - if (fontSprite->GetPixel(i + ox * 8, j + oy * 8).r > 0) - Draw(x + sx + i, y + sy + j, col); - } - sx += 8 * scale; - } - } - SetPixelMode(m); - } - - void PixelGameEngine::SetPixelMode(Pixel::Mode m) - { - nPixelMode = m; - } - - Pixel::Mode PixelGameEngine::GetPixelMode() - { - return nPixelMode; - } - - void PixelGameEngine::SetPixelMode(std::function pixelMode) - { - funcPixelMode = pixelMode; - nPixelMode = Pixel::Mode::CUSTOM; - } - - void PixelGameEngine::SetPixelBlend(float fBlend) - { - fBlendFactor = fBlend; - if (fBlendFactor < 0.0f) fBlendFactor = 0.0f; - if (fBlendFactor > 1.0f) fBlendFactor = 1.0f; - } - - // User must override these functions as required. I have not made - // them abstract because I do need a default behaviour to occur if - // they are not overwritten - bool PixelGameEngine::OnUserCreate() - { return false; } - bool PixelGameEngine::OnUserUpdate(float fElapsedTime) - { return false; } - bool PixelGameEngine::OnUserDestroy() - { return true; } - ////////////////////////////////////////////////////////////////// - - void PixelGameEngine::olc_UpdateMouse(int32_t x, int32_t y) - { - // Mouse coords come in screen space - // But leave in pixel space - nMousePosX = x / (int32_t)nPixelWidth; - nMousePosY = y / (int32_t)nPixelHeight; - - if (nMousePosX >= (int32_t)nScreenWidth) - nMousePosX = nScreenWidth - 1; - if (nMousePosY >= (int32_t)nScreenHeight) - nMousePosY = nScreenHeight - 1; - - if (nMousePosX < 0) - nMousePosX = 0; - if (nMousePosY < 0) - nMousePosY = 0; - } - - void PixelGameEngine::EngineThread() - { - // Start OpenGL, the context is owned by the game thread - olc_OpenGLCreate(); - - // Create Screen Texture - disable filtering - glEnable(GL_TEXTURE_2D); - glGenTextures(1, &glBuffer); - glBindTexture(GL_TEXTURE_2D, glBuffer); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL); - - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, nScreenWidth, nScreenHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, pDefaultDrawTarget->GetData()); - - - // Create user resources as part of this thread - if (!OnUserCreate()) - bAtomActive = false; - - auto tp1 = std::chrono::system_clock::now(); - auto tp2 = std::chrono::system_clock::now(); - - while (bAtomActive) - { - // Run as fast as possible - while (bAtomActive) - { - // Handle Timing - tp2 = std::chrono::system_clock::now(); - std::chrono::duration elapsedTime = tp2 - tp1; - tp1 = tp2; - - // Our time per frame coefficient - float fElapsedTime = elapsedTime.count(); - -#ifndef _WIN32 - // Handle Xlib Message Loop - we do this in the - // same thread that OpenGL was created so we dont - // need to worry too much about multithreading with X11 - XEvent xev; - while (XPending(olc_Display)) - { - XNextEvent(olc_Display, &xev); - if (xev.type == Expose) - { - XWindowAttributes gwa; - XGetWindowAttributes(olc_Display, olc_Window, &gwa); - glViewport(0, 0, gwa.width, gwa.height); - } - else if (xev.type == KeyPress) - { - KeySym sym = XLookupKeysym(&xev.xkey, 0); - pKeyNewState[mapKeys[sym]] = true; - } - else if (xev.type == KeyRelease) - { - KeySym sym = XLookupKeysym(&xev.xkey, 0); - pKeyNewState[mapKeys[sym]] = false; - } - else if (xev.type == ButtonPress) - { - pMouseNewState[xev.xbutton.button-1] = true; - } - else if (xev.type == ButtonRelease) - { - pMouseNewState[xev.xbutton.button-1] = false; - } - else if (xev.type == MotionNotify) - { - olc_UpdateMouse(xev.xmotion.x, xev.xmotion.y); - } - else if (xev.type == FocusIn) - { - bHasInputFocus = true; - } - else if (xev.type == FocusOut) - { - bHasInputFocus = false; - } - else if (xev.type == ClientMessage) - { - bAtomActive = false; - } - } -#endif - - // Handle User Input - Keyboard - for (int i = 0; i < 256; i++) - { - pKeyboardState[i].bPressed = false; - pKeyboardState[i].bReleased = false; - - if (pKeyNewState[i] != pKeyOldState[i]) - { - if (pKeyNewState[i]) - { - pKeyboardState[i].bPressed = !pKeyboardState[i].bHeld; - pKeyboardState[i].bHeld = true; - } - else - { - pKeyboardState[i].bReleased = true; - pKeyboardState[i].bHeld = false; - } - } - - pKeyOldState[i] = pKeyNewState[i]; - } - - // Handle User Input - Mouse - for (int i = 0; i < 5; i++) - { - pMouseState[i].bPressed = false; - pMouseState[i].bReleased = false; - - if (pMouseNewState[i] != pMouseOldState[i]) - { - if (pMouseNewState[i]) - { - pMouseState[i].bPressed = !pMouseState[i].bHeld; - pMouseState[i].bHeld = true; - } - else - { - pMouseState[i].bReleased = true; - pMouseState[i].bHeld = false; - } - } - - pMouseOldState[i] = pMouseNewState[i]; - } - -#ifdef OLC_DBG_OVERDRAW - olc::Sprite::nOverdrawCount = 0; -#endif - - // Handle Frame Update - if (!OnUserUpdate(fElapsedTime)) - bAtomActive = false; - - // Display Graphics - - // TODO: This is a bit slow (especially in debug, but 100x faster in release mode???) - // Copy pixel array into texture - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, nScreenWidth, nScreenHeight, GL_RGBA, GL_UNSIGNED_BYTE, pDefaultDrawTarget->GetData()); - - // Display texture on screen - glBegin(GL_QUADS); - glTexCoord2f(0.0, 1.0); glVertex3f(-1.0f + (fSubPixelOffsetX), -1.0f + (fSubPixelOffsetY), 0.0f); - glTexCoord2f(0.0, 0.0); glVertex3f(-1.0f + (fSubPixelOffsetX), 1.0f + (fSubPixelOffsetY), 0.0f); - glTexCoord2f(1.0, 0.0); glVertex3f( 1.0f + (fSubPixelOffsetX), 1.0f + (fSubPixelOffsetY), 0.0f); - glTexCoord2f(1.0, 1.0); glVertex3f( 1.0f + (fSubPixelOffsetX), -1.0f + (fSubPixelOffsetY), 0.0f); - glEnd(); - - // Present Graphics to screen -#ifdef _WIN32 - SwapBuffers(glDeviceContext); -#else - glXSwapBuffers(olc_Display, olc_Window); -#endif - - // Update Title Bar - fFrameTimer += fElapsedTime; - nFrameCount++; - if (fFrameTimer >= 1.0f) - { - fFrameTimer -= 1.0f; - - std::string sTitle = "OneLoneCoder.com - Pixel Game Engine - " + sAppName + " - FPS: " + std::to_string(nFrameCount); -#ifdef _WIN32 -#ifdef UNICODE - SetWindowText(olc_hWnd, ConvertS2W(sTitle).c_str()); -#else - SetWindowText(olc_hWnd, sTitle.c_str()); -#endif -#else - XStoreName(olc_Display, olc_Window, sTitle.c_str()); -#endif - nFrameCount = 0; - } - } - - // Allow the user to free resources if they have overrided the destroy function - if (OnUserDestroy()) - { - // User has permitted destroy, so exit and clean up - } - else - { - // User denied destroy for some reason, so continue running - bAtomActive = true; - } - } - -#ifdef _WIN32 - wglDeleteContext(glRenderContext); - PostMessage(olc_hWnd, WM_DESTROY, 0, 0); -#else - glXMakeCurrent(olc_Display, None, NULL); - glXDestroyContext(olc_Display, glDeviceContext); - XDestroyWindow(olc_Display, olc_Window); - XCloseDisplay(olc_Display); -#endif - - } - - - void PixelGameEngine::olc_ConstructFontSheet() - { - std::string data; - data += "?Q`0001oOch0o01o@F40o000000000"; - data += "O000000nOT0063Qo4d8>?7a14Gno94AA4gno94AaOT0>o3`oO400o7QN00000400"; - data += "Of80001oOg<7O7moBGT7O7lABET024@aBEd714AiOdl717a_=TH013Q>00000000"; - data += "720D000V?V5oB3Q_HdUoE7a9@DdDE4A9@DmoE4A;Hg]oM4Aj8S4D84@`00000000"; - data += "OaPT1000Oa`^13P1@AI[?g`1@A=[OdAoHgljA4Ao?WlBA7l1710007l100000000"; - data += "ObM6000oOfMV?3QoBDD`O7a0BDDH@5A0BDD<@5A0BGeVO5ao@CQR?5Po00000000"; - data += "Oc``000?Ogij70PO2D]??0Ph2DUM@7i`2DTg@7lh2GUj?0TO0C1870T?00000000"; - data += "70<4001o?P<7?1QoHg43O;`h@GT0@:@LB@d0>:@hN@L0@?aoN@<0O7ao0000?000"; - data += "OcH0001SOglLA7mg24TnK7ln24US>0PL24U140PnOgl0>7QgOcH0K71S0000A000"; - data += "00H00000@Dm1S007@DUSg00?OdTnH7YhOfTL<7Yh@Cl0700?@Ah0300700000000"; - data += "<008001QL00ZA41a@6HnI<1i@FHLM81M@@0LG81?O`0nC?Y7?`0ZA7Y300080000"; - data += "O`082000Oh0827mo6>Hn?Wmo?6HnMb11MP08@C11H`08@FP0@@0004@000000000"; - data += "00P00001Oab00003OcKP0006@6=PMgl<@440MglH@000000`@000001P00000000"; - data += "Ob@8@@00Ob@8@Ga13R@8Mga172@8?PAo3R@827QoOb@820@0O`0007`0000007P0"; - data += "O`000P08Od400g`<3V=P0G`673IP0`@3>1`00P@6O`P00g`SetPixel(px, py, olc::Pixel(k, k, k, k)); - if (++py == 48) { px++; py = 0; } - } - } - } - -#ifdef _WIN32 - HWND PixelGameEngine::olc_WindowCreate() - { - WNDCLASS wc; - wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); - wc.hCursor = LoadCursor(NULL, IDC_ARROW); - wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; - wc.hInstance = GetModuleHandle(nullptr); - wc.lpfnWndProc = olc_WindowEvent; - wc.cbClsExtra = 0; - wc.cbWndExtra = 0; - wc.lpszMenuName = nullptr; - wc.hbrBackground = nullptr; -#ifdef UNICODE - wc.lpszClassName = L"OLC_PIXEL_GAME_ENGINE"; -#else - wc.lpszClassName = "OLC_PIXEL_GAME_ENGINE"; -#endif - - RegisterClass(&wc); - - // Define window furniture - DWORD dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; - DWORD dwStyle = WS_CAPTION | WS_SYSMENU | WS_VISIBLE; - RECT rWndRect = { 0, 0, (LONG)nScreenWidth * (LONG)nPixelWidth, (LONG)nScreenHeight * (LONG)nPixelHeight }; - - // Keep client size as requested - AdjustWindowRectEx(&rWndRect, dwStyle, FALSE, dwExStyle); - - int width = rWndRect.right - rWndRect.left; - int height = rWndRect.bottom - rWndRect.top; - -#ifdef UNICODE - olc_hWnd = CreateWindowEx(dwExStyle, L"OLC_PIXEL_GAME_ENGINE", L"", dwStyle, - 30, 30, width, height, NULL, NULL, GetModuleHandle(nullptr), this); -#else - olc_hWnd = CreateWindowEx(dwExStyle, "OLC_PIXEL_GAME_ENGINE", "", dwStyle, - 30, 30, width, height, NULL, NULL, GetModuleHandle(nullptr), this); -#endif - - // Create Keyboard Mapping - mapKeys[0x41] = Key::A; mapKeys[0x42] = Key::B; mapKeys[0x43] = Key::C; mapKeys[0x44] = Key::D; mapKeys[0x45] = Key::E; - mapKeys[0x46] = Key::F; mapKeys[0x47] = Key::G; mapKeys[0x48] = Key::H; mapKeys[0x49] = Key::I; mapKeys[0x4A] = Key::J; - mapKeys[0x4B] = Key::K; mapKeys[0x4C] = Key::L; mapKeys[0x4D] = Key::M; mapKeys[0x4E] = Key::N; mapKeys[0x4F] = Key::O; - mapKeys[0x50] = Key::P; mapKeys[0x51] = Key::Q; mapKeys[0x52] = Key::R; mapKeys[0x53] = Key::S; mapKeys[0x54] = Key::T; - mapKeys[0x55] = Key::U; mapKeys[0x56] = Key::V; mapKeys[0x57] = Key::W; mapKeys[0x58] = Key::X; mapKeys[0x59] = Key::Y; - mapKeys[0x5A] = Key::Z; - - mapKeys[VK_F1] = Key::F1; mapKeys[VK_F2] = Key::F2; mapKeys[VK_F3] = Key::F3; mapKeys[VK_F4] = Key::F4; - mapKeys[VK_F5] = Key::F5; mapKeys[VK_F6] = Key::F6; mapKeys[VK_F7] = Key::F7; mapKeys[VK_F8] = Key::F8; - mapKeys[VK_F9] = Key::F9; mapKeys[VK_F10] = Key::F10; mapKeys[VK_F11] = Key::F11; mapKeys[VK_F12] = Key::F12; - - mapKeys[VK_DOWN] = Key::DOWN; mapKeys[VK_LEFT] = Key::LEFT; mapKeys[VK_RIGHT] = Key::RIGHT; mapKeys[VK_UP] = Key::UP; - mapKeys[VK_RETURN] = Key::ENTER; //mapKeys[VK_RETURN] = Key::RETURN; - - mapKeys[VK_BACK] = Key::BACK; mapKeys[VK_ESCAPE] = Key::ESCAPE; mapKeys[VK_RETURN] = Key::ENTER; mapKeys[VK_PAUSE] = Key::PAUSE; - mapKeys[VK_SCROLL] = Key::SCROLL; mapKeys[VK_TAB] = Key::TAB; mapKeys[VK_DELETE] = Key::DEL; mapKeys[VK_HOME] = Key::HOME; - mapKeys[VK_END] = Key::END; mapKeys[VK_PRIOR] = Key::PGUP; mapKeys[VK_NEXT] = Key::PGDN; mapKeys[VK_INSERT] = Key::INS; - mapKeys[VK_SHIFT] = Key::SHIFT; mapKeys[VK_CONTROL] = Key::CTRL; - mapKeys[VK_SPACE] = Key::SPACE; - - mapKeys[0x30] = Key::K0; mapKeys[0x31] = Key::K1; mapKeys[0x32] = Key::K2; mapKeys[0x33] = Key::K3; mapKeys[0x34] = Key::K4; - mapKeys[0x35] = Key::K5; mapKeys[0x36] = Key::K6; mapKeys[0x37] = Key::K7; mapKeys[0x38] = Key::K8; mapKeys[0x39] = Key::K9; - - mapKeys[VK_NUMPAD0] = Key::NP0; mapKeys[VK_NUMPAD1] = Key::NP1; mapKeys[VK_NUMPAD2] = Key::NP2; mapKeys[VK_NUMPAD3] = Key::NP3; mapKeys[VK_NUMPAD4] = Key::NP4; - mapKeys[VK_NUMPAD5] = Key::NP5; mapKeys[VK_NUMPAD6] = Key::NP6; mapKeys[VK_NUMPAD7] = Key::NP7; mapKeys[VK_NUMPAD8] = Key::NP8; mapKeys[VK_NUMPAD9] = Key::NP9; - mapKeys[VK_MULTIPLY] = Key::NP_MUL; mapKeys[VK_ADD] = Key::NP_ADD; mapKeys[VK_DIVIDE] = Key::NP_DIV; mapKeys[VK_SUBTRACT] = Key::NP_SUB; mapKeys[VK_DECIMAL] = Key::NP_DECIMAL; - - return olc_hWnd; - } - - bool PixelGameEngine::olc_OpenGLCreate() - { - // Create Device Context - glDeviceContext = GetDC(olc_hWnd); - PIXELFORMATDESCRIPTOR pfd = - { - sizeof(PIXELFORMATDESCRIPTOR), 1, - PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, - PFD_TYPE_RGBA, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - PFD_MAIN_PLANE, 0, 0, 0, 0 - }; - - int pf = 0; - if (!(pf = ChoosePixelFormat(glDeviceContext, &pfd))) return false; - SetPixelFormat(glDeviceContext, pf, &pfd); - - if (!(glRenderContext = wglCreateContext(glDeviceContext))) return false; - wglMakeCurrent(glDeviceContext, glRenderContext); - - // Remove Frame cap - wglSwapInterval = (wglSwapInterval_t*)wglGetProcAddress("wglSwapIntervalEXT"); - wglSwapInterval(0); - return true; - } - - // Windows Event Handler - LRESULT CALLBACK PixelGameEngine::olc_WindowEvent(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) - { - static PixelGameEngine *sge; - switch (uMsg) - { - case WM_CREATE: sge = (PixelGameEngine*)((LPCREATESTRUCT)lParam)->lpCreateParams; return 0; - case WM_MOUSEMOVE: - { - uint16_t x = lParam & 0xFFFF; // Thanks @ForAbby (Discord) - uint16_t y = (lParam >> 16) & 0xFFFF; - int16_t ix = *(int16_t*)&x; - int16_t iy = *(int16_t*)&y; - sge->olc_UpdateMouse(ix, iy); - return 0; - } - case WM_MOUSELEAVE: sge->bHasMouseFocus = false; - case WM_SETFOCUS: sge->bHasInputFocus = true; return 0; - case WM_KILLFOCUS: sge->bHasInputFocus = false; return 0; - case WM_KEYDOWN: sge->pKeyNewState[mapKeys[wParam]] = true; return 0; - case WM_KEYUP: sge->pKeyNewState[mapKeys[wParam]] = false; return 0; - case WM_LBUTTONDOWN:sge->pMouseNewState[0] = true; return 0; - case WM_LBUTTONUP: sge->pMouseNewState[0] = false; return 0; - case WM_RBUTTONDOWN:sge->pMouseNewState[1] = true; return 0; - case WM_RBUTTONUP: sge->pMouseNewState[1] = false; return 0; - case WM_MBUTTONDOWN:sge->pMouseNewState[2] = true; return 0; - case WM_MBUTTONUP: sge->pMouseNewState[2] = false; return 0; - case WM_CLOSE: bAtomActive = false; return 0; - case WM_DESTROY: PostQuitMessage(0); return 0; - } - return DefWindowProc(hWnd, uMsg, wParam, lParam); - } -#else - // Do the Linux stuff! - Display* PixelGameEngine::olc_WindowCreate() - { - XInitThreads(); - - // Grab the deafult display and window - olc_Display = XOpenDisplay(NULL); - olc_WindowRoot = DefaultRootWindow(olc_Display); - - // Based on the display capabilities, configure the appearance of the window - GLint olc_GLAttribs[] = { GLX_RGBA, GLX_DEPTH_SIZE, 24, GLX_DOUBLEBUFFER, None }; - olc_VisualInfo = glXChooseVisual(olc_Display, 0, olc_GLAttribs); - olc_ColourMap = XCreateColormap(olc_Display, olc_WindowRoot, olc_VisualInfo->visual, AllocNone); - olc_SetWindowAttribs.colormap = olc_ColourMap; - - // Register which events we are interested in receiving - olc_SetWindowAttribs.event_mask = ExposureMask | KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | FocusChangeMask; - - // Create the window - olc_Window = XCreateWindow(olc_Display, olc_WindowRoot, 30, 30, nScreenWidth * nPixelWidth, nScreenHeight * nPixelHeight, 0, olc_VisualInfo->depth, InputOutput, olc_VisualInfo->visual, CWColormap | CWEventMask, &olc_SetWindowAttribs); - - Atom wmDelete = XInternAtom(olc_Display, "WM_DELETE_WINDOW", true); - XSetWMProtocols(olc_Display, olc_Window, &wmDelete, 1); - - XMapWindow(olc_Display, olc_Window); - XStoreName(olc_Display, olc_Window, "OneLoneCoder.com - Pixel Game Engine"); - - // Create Keyboard Mapping - mapKeys[0x61] = Key::A; mapKeys[0x62] = Key::B; mapKeys[0x63] = Key::C; mapKeys[0x64] = Key::D; mapKeys[0x65] = Key::E; - mapKeys[0x66] = Key::F; mapKeys[0x67] = Key::G; mapKeys[0x68] = Key::H; mapKeys[0x69] = Key::I; mapKeys[0x6A] = Key::J; - mapKeys[0x6B] = Key::K; mapKeys[0x6C] = Key::L; mapKeys[0x6D] = Key::M; mapKeys[0x6E] = Key::N; mapKeys[0x6F] = Key::O; - mapKeys[0x70] = Key::P; mapKeys[0x71] = Key::Q; mapKeys[0x72] = Key::R; mapKeys[0x73] = Key::S; mapKeys[0x74] = Key::T; - mapKeys[0x75] = Key::U; mapKeys[0x76] = Key::V; mapKeys[0x77] = Key::W; mapKeys[0x78] = Key::X; mapKeys[0x79] = Key::Y; - mapKeys[0x7A] = Key::Z; - - mapKeys[XK_F1] = Key::F1; mapKeys[XK_F2] = Key::F2; mapKeys[XK_F3] = Key::F3; mapKeys[XK_F4] = Key::F4; - mapKeys[XK_F5] = Key::F5; mapKeys[XK_F6] = Key::F6; mapKeys[XK_F7] = Key::F7; mapKeys[XK_F8] = Key::F8; - mapKeys[XK_F9] = Key::F9; mapKeys[XK_F10] = Key::F10; mapKeys[XK_F11] = Key::F11; mapKeys[XK_F12] = Key::F12; - - mapKeys[XK_Down] = Key::DOWN; mapKeys[XK_Left] = Key::LEFT; mapKeys[XK_Right] = Key::RIGHT; mapKeys[XK_Up] = Key::UP; - mapKeys[XK_KP_Enter] = Key::ENTER; mapKeys[XK_Return] = Key::ENTER; - - mapKeys[XK_BackSpace] = Key::BACK; mapKeys[XK_Escape] = Key::ESCAPE; mapKeys[XK_Linefeed] = Key::ENTER; mapKeys[XK_Pause] = Key::PAUSE; - mapKeys[XK_Scroll_Lock] = Key::SCROLL; mapKeys[XK_Tab] = Key::TAB; mapKeys[XK_Delete] = Key::DEL; mapKeys[XK_Home] = Key::HOME; - mapKeys[XK_End] = Key::END; mapKeys[XK_Page_Up] = Key::PGUP; mapKeys[XK_Page_Down] = Key::PGDN; mapKeys[XK_Insert] = Key::INS; - mapKeys[XK_Shift_L] = Key::SHIFT; mapKeys[XK_Shift_R] = Key::SHIFT; mapKeys[XK_Control_L] = Key::CTRL; mapKeys[XK_Control_R] = Key::CTRL; - mapKeys[XK_space] = Key::SPACE; - - mapKeys[XK_0] = Key::K0; mapKeys[XK_1] = Key::K1; mapKeys[XK_2] = Key::K2; mapKeys[XK_3] = Key::K3; mapKeys[XK_4] = Key::K4; - mapKeys[XK_5] = Key::K5; mapKeys[XK_6] = Key::K6; mapKeys[XK_7] = Key::K7; mapKeys[XK_8] = Key::K8; mapKeys[XK_9] = Key::K9; - - mapKeys[XK_KP_0] = Key::NP0; mapKeys[XK_KP_1] = Key::NP1; mapKeys[XK_KP_2] = Key::NP2; mapKeys[XK_KP_3] = Key::NP3; mapKeys[XK_KP_4] = Key::NP4; - mapKeys[XK_KP_5] = Key::NP5; mapKeys[XK_KP_6] = Key::NP6; mapKeys[XK_KP_7] = Key::NP7; mapKeys[XK_KP_8] = Key::NP8; mapKeys[XK_KP_9] = Key::NP9; - mapKeys[XK_KP_Multiply] = Key::NP_MUL; mapKeys[XK_KP_Add] = Key::NP_ADD; mapKeys[XK_KP_Divide] = Key::NP_DIV; mapKeys[XK_KP_Subtract] = Key::NP_SUB; mapKeys[XK_KP_Decimal] = Key::NP_DECIMAL; - - return olc_Display; - } - - bool PixelGameEngine::olc_OpenGLCreate() - { - glDeviceContext = glXCreateContext(olc_Display, olc_VisualInfo, nullptr, GL_TRUE); - glXMakeCurrent(olc_Display, olc_Window, glDeviceContext); - - XWindowAttributes gwa; - XGetWindowAttributes(olc_Display, olc_Window, &gwa); - glViewport(0, 0, gwa.width, gwa.height); - - glSwapIntervalEXT = nullptr; - glSwapIntervalEXT = (glSwapInterval_t*)glXGetProcAddress((unsigned char*)"glXSwapIntervalEXT"); - if (glSwapIntervalEXT) - glSwapIntervalEXT(olc_Display, olc_Window, 0); - else - { - printf("NOTE: Could not disable VSYNC, glXSwapIntervalEXT() was not found!\n"); - printf(" Don't worry though, things will still work, it's just the\n"); - printf(" frame rate will be capped to your monitors refresh rate - javidx9\n"); - } - - return true; - } - -#endif - - // Need a couple of statics as these are singleton instances - // read from multiple locations - std::atomic PixelGameEngine::bAtomActive{ false }; - std::map PixelGameEngine::mapKeys; - olc::PixelGameEngine* olc::PGEX::pge = nullptr; -#ifdef OLC_DBG_OVERDRAW - int olc::Sprite::nOverdrawCount = 0; -#endif - //============================================================= -} - -#endif \ No newline at end of file diff --git a/OneLoneCoder_PGE_Balls2.cpp b/OneLoneCoder_PGE_Balls2.cpp deleted file mode 100644 index e66b75e..0000000 --- a/OneLoneCoder_PGE_Balls2.cpp +++ /dev/null @@ -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 -#include -#include -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 vecBalls; - vector vecLines; - vector> 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> vecCollidingPairs; - vector 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; -}; - diff --git a/OneLoneCoder_PGE_ExtensionTestGFX2D.cpp b/OneLoneCoder_PGE_ExtensionTestGFX2D.cpp deleted file mode 100644 index 7794c7d..0000000 --- a/OneLoneCoder_PGE_ExtensionTestGFX2D.cpp +++ /dev/null @@ -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 -#define OLC_PGE_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 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; -} \ No newline at end of file diff --git a/OneLoneCoder_PGE_PathFinding_WaveProp.cpp b/OneLoneCoder_PGE_PathFinding_WaveProp.cpp deleted file mode 100644 index 5b22c03..0000000 --- a/OneLoneCoder_PGE_PathFinding_WaveProp.cpp +++ /dev/null @@ -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 -#include -#include -#include - - -// 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> 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> 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 &n1, const std::tuple &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 &n1, const std::tuple &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> path; - path.push_back({ nStartX, nStartY }); - int nLocX = nStartX; - int nLocY = nStartY; - bool bNoPath = false; - - while (!(nLocX == nEndX && nLocY == nEndY) && !bNoPath) - { - std::list> 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 &n1, const std::tuple &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; -} \ No newline at end of file diff --git a/OneLoneCoder_PGE_PolygonCollisions1.cpp b/OneLoneCoder_PGE_PolygonCollisions1.cpp deleted file mode 100644 index 9735fe5..0000000 --- a/OneLoneCoder_PGE_PolygonCollisions1.cpp +++ /dev/null @@ -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 -#include - -// 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 p; // Transformed Points - vec2d pos; // Position of shape - float angle; // Direction of shape - std::vector o; // "Model" of shape - bool overlap = false; // Flag to indicate if overlap has occurred - }; - - std::vector 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; -} \ No newline at end of file diff --git a/OneLoneCoder_PGE_ShadowCasting2D.cpp b/OneLoneCoder_PGE_ShadowCasting2D.cpp deleted file mode 100644 index 4399472..0000000 Binary files a/OneLoneCoder_PGE_ShadowCasting2D.cpp and /dev/null differ diff --git a/OneLoneCoder_PGE_SoundTest.cpp b/OneLoneCoder_PGE_SoundTest.cpp deleted file mode 100644 index 2b9da94..0000000 --- a/OneLoneCoder_PGE_SoundTest.cpp +++ /dev/null @@ -1,272 +0,0 @@ -/* - Simple example code for olcPGEX_Sound.h - Mind your speakers! - - You will need SampleA.wav, SampleB.wav and SampleC.wav for this demo. - - 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 - Patreon: https://www.patreon.com/javidx9 - - Author - ~~~~~~ - David Barr, aka javidx9, ŠOneLoneCoder 2018 -*/ - -#define OLC_PGE_APPLICATION -#include "olcPixelGameEngine.h" - -#define OLC_PGEX_SOUND -#include "olcPGEX_Sound.h" - -#include - -class SoundTest : public olc::PixelGameEngine -{ -public: - SoundTest() - { - sAppName = "Sound Test"; - } - -private: - int sndSampleA; - int sndSampleB; - int sndSampleC; - bool bToggle = false; - static bool bSynthPlaying; - static float fSynthFrequency; - static float fFilterVolume; - - const olc::Key keys[12] = { olc::Key::Z, olc::Key::S, olc::Key::X, olc::Key::D, olc::Key::C, - olc::Key::V, olc::Key::G, olc::Key::B, olc::Key::H, olc::Key::N, olc::Key::J, olc::Key::M}; - - static float fPreviousSamples[128]; - static int nSamplePos; - - -private: - - // This is an optional function that allows the user to generate or synthesize sounds - // in a custom way, it is fed into the output mixer bu the extension - static float MyCustomSynthFunction(int nChannel, float fGlobalTime, float fTimeStep) - { - // Just generate a sine wave of the appropriate frequency - if (bSynthPlaying) - return sin(fSynthFrequency * 2.0f * 3.14159f * fGlobalTime); - else - return 0.0f; - } - - // This is an optional function that allows the user to filter the output from - // the internal mixer of the extension. Here you could add effects or just - // control volume. I also like to use it to extract information about - // the currently playing output waveform - static float MyCustomFilterFunction(int nChannel, float fGlobalTime, float fSample) - { - // Fundamentally just control volume - float fOutput = fSample * fFilterVolume; - - // But also add sample to list of previous samples for visualisation - fPreviousSamples[nSamplePos] = fOutput; - nSamplePos++; - nSamplePos %= 128; - - return fOutput; - } - - - bool OnUserCreate() - { - olc::SOUND::InitialiseAudio(44100, 1, 8, 512); - - sndSampleA = olc::SOUND::LoadAudioSample("SampleA.wav"); - sndSampleB = olc::SOUND::LoadAudioSample("SampleB.wav"); - sndSampleC = olc::SOUND::LoadAudioSample("SampleC.wav"); - - // Give the sound engine a hook to a custom generation function - olc::SOUND::SetUserSynthFunction(MyCustomSynthFunction); - - // Give the sound engine a hook to a custom filtering function - olc::SOUND::SetUserFilterFunction(MyCustomFilterFunction); - - return true; - } - - bool OnUserUpdate(float fElapsedTime) - { - //olc::SOUND::PlaySample(sndTest); - - auto PointInRect = [&](int x, int y, int rx, int ry, int rw, int rh) - { - return x >= rx && x < (rx + rw) && y >= ry && y < (ry + rh); - }; - - int nMouseX = GetMouseX(); - int nMouseY = GetMouseY(); - - if(GetMouse(0).bPressed && PointInRect(nMouseX, nMouseY, 16, 16, 128, 24)) - olc::SOUND::PlaySample(sndSampleA); // Plays the sample once - - if (GetMouse(0).bPressed && PointInRect(nMouseX, nMouseY, 16, 48, 128, 24)) - olc::SOUND::PlaySample(sndSampleB); - - if (GetMouse(0).bPressed && PointInRect(nMouseX, nMouseY, 16, 80, 128, 24)) - { - bToggle = !bToggle; - if (bToggle) - { - olc::SOUND::PlaySample(sndSampleC, true); // Plays the sample in looping mode - } - else - { - olc::SOUND::StopSample(sndSampleC); - } - } - - if (GetMouse(0).bHeld && PointInRect(nMouseX, nMouseY, 160, 16, 90, 24)) - fFilterVolume += 2.0f * fElapsedTime; - - if (GetMouse(0).bHeld && PointInRect(nMouseX, nMouseY, 160, 48, 90, 24)) - fFilterVolume -= 2.0f * fElapsedTime; - - if (fFilterVolume < 0.0f) fFilterVolume = 0.0f; - if (fFilterVolume > 1.0f) fFilterVolume = 1.0f; - - // Detect keyboard - very simple synthesizer - if (IsFocused()) - { - bool bKeyIsPressed = false; - float fFrequency = 0.0f; - for (int i = 0; i < 12; i++) - { - if (GetKey(keys[i]).bHeld) - { - bKeyIsPressed = true; - float fOctaveBaseFrequency = 220.0f; - float f12thRootOf2 = pow(2.0f, 1.0f / 12.0f); - fFrequency = fOctaveBaseFrequency * powf(f12thRootOf2, (float)i); - } - } - - fSynthFrequency = fFrequency; - bSynthPlaying = bKeyIsPressed; - } - - - // Draw Buttons - Clear(olc::BLUE); - - DrawRect(16, 16, 128, 24); - DrawString(20, 20, "Play Sample A"); - - DrawRect(16, 48, 128, 24); - DrawString(20, 52, "Play Sample B"); - - DrawRect(16, 80, 128, 24); - DrawString(20, 84, (bToggle ? "Stop Sample C" : "Loop Sample C")); - - - DrawRect(160, 16, 90, 24); - DrawString(164, 20, "Volume +"); - - DrawRect(160, 48, 90, 24); - DrawString(164, 52, "Volume -"); - - - DrawString(164, 80, "Volume: " + std::to_string((int)(fFilterVolume * 10.0f))); - - // Draw Keyboard - - // White Keys - for (int i = 0; i < 7; i++) - { - FillRect(i * 16 + 8, 160, 16, 64); - DrawRect(i * 16 + 8, 160, 16, 64, olc::BLACK); - DrawString(i * 16 + 12, 212, std::string(1, "ZXCVBNM"[i]), olc::BLACK); - } - - // Black Keys - for (int i = 0; i < 6; i++) - { - if (i != 2) - { - FillRect(i * 16 + 18, 160, 12, 32, olc::BLACK); - DrawString(i * 16 + 20, 180, std::string(1, "SDFGHJ"[i]), olc::WHITE); - } - } - - // Draw visualisation - int nStartPos = (nSamplePos + 127) % 128; - - for (int i = 127; i >= 0; i--) - { - float fSample = fPreviousSamples[(nSamplePos + i) % 128]; - DrawLine(124 + i, 210, 124 + i, 210 + (int)(fSample * 20.0f), olc::RED); - } - - - return true; - } - - - // Note we must shut down the sound system too!! - bool OnUserDestroy() - { - olc::SOUND::DestroyAudio(); - return true; - } -}; - -bool SoundTest::bSynthPlaying = false; -float SoundTest::fSynthFrequency = 0.0f; -float SoundTest::fFilterVolume = 1.0f; -int SoundTest::nSamplePos = 0; -float SoundTest::fPreviousSamples[128]; - -int main() -{ - SoundTest demo; - if(demo.Construct(256, 240, 4, 4)) - demo.Start(); - - return 0; -} \ No newline at end of file diff --git a/OneLoneCoder_PGE_SpriteTransforms.cpp b/OneLoneCoder_PGE_SpriteTransforms.cpp deleted file mode 100644 index 6587032..0000000 --- a/OneLoneCoder_PGE_SpriteTransforms.cpp +++ /dev/null @@ -1,257 +0,0 @@ -/* - OneLoneCoder.com - 2D Sprite Affine Transformations - "No more 90 degree movements" - @Javidx9 - - - Background - ~~~~~~~~~~ - The sophistication of 2D engines is enhanced when the programmer is - able to rotate and scale sprites in a convenient manner. This program - shows the basics of how affine transformations accomplish this. - - 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.com/javidx9 - Homepage: https://www.onelonecoder.com - - Relevant Videos - ~~~~~~~~~~~~~~~ - https://youtu.be/zxwLN2blwbQ - - Author - ~~~~~~ - David Barr, aka javidx9, ŠOneLoneCoder 2018 -*/ - - -#define OLC_PGE_APPLICATION -#include "olcPixelGameEngine.h" - -#include -#undef min -#undef max - - -class SpriteTransforms : public olc::PixelGameEngine -{ -public: - SpriteTransforms() - { - sAppName = "Sprite Transforms"; - } - -private: - olc::Sprite *sprCar; - - struct matrix3x3 - { - float m[3][3]; - }; - - void Identity(matrix3x3 &mat) - { - mat.m[0][0] = 1.0f; mat.m[1][0] = 0.0f; mat.m[2][0] = 0.0f; - mat.m[0][1] = 0.0f; mat.m[1][1] = 1.0f; mat.m[2][1] = 0.0f; - mat.m[0][2] = 0.0f; mat.m[1][2] = 0.0f; mat.m[2][2] = 1.0f; - } - - void Translate(matrix3x3 &mat, float ox, float oy) - { - mat.m[0][0] = 1.0f; mat.m[1][0] = 0.0f; mat.m[2][0] = ox; - mat.m[0][1] = 0.0f; mat.m[1][1] = 1.0f; mat.m[2][1] = oy; - mat.m[0][2] = 0.0f; mat.m[1][2] = 0.0f; mat.m[2][2] = 1.0f; - } - - void Rotate(matrix3x3 &mat, float fTheta) - { - mat.m[0][0] = cosf(fTheta); mat.m[1][0] = sinf(fTheta); mat.m[2][0] = 0.0f; - mat.m[0][1] = -sinf(fTheta); mat.m[1][1] = cosf(fTheta); mat.m[2][1] = 0.0f; - mat.m[0][2] = 0.0f; mat.m[1][2] = 0.0f; mat.m[2][2] = 1.0f; - } - - void Scale(matrix3x3 &mat, float sx, float sy) - { - mat.m[0][0] = sx; mat.m[1][0] = 0.0f; mat.m[2][0] = 0.0f; - mat.m[0][1] = 0.0f; mat.m[1][1] = sy; mat.m[2][1] = 0.0f; - mat.m[0][2] = 0.0f; mat.m[1][2] = 0.0f; mat.m[2][2] = 1.0f; - } - - void Shear(matrix3x3 &mat, float sx, float sy) - { - mat.m[0][0] = 1.0f; mat.m[1][0] = sx; mat.m[2][0] = 0.0f; - mat.m[0][1] = sy; mat.m[1][1] = 1.0f; mat.m[2][1] = 0.0f; - mat.m[0][2] = 0.0f; mat.m[1][2] = 0.0f; mat.m[2][2] = 1.0f; - } - - void MatrixMultiply(matrix3x3 &matResult, matrix3x3 &matA, matrix3x3 &matB) - { - for (int c = 0; c < 3; c++) - { - for (int r = 0; r < 3; r++) - { - matResult.m[c][r] = matA.m[0][r] * matB.m[c][0] + - matA.m[1][r] * matB.m[c][1] + - matA.m[2][r] * matB.m[c][2]; - } - } - } - - void Forward(matrix3x3 &mat, float in_x, float in_y, float &out_x, float &out_y) - { - out_x = in_x * mat.m[0][0] + in_y * mat.m[1][0] + mat.m[2][0]; - out_y = in_x * mat.m[0][1] + in_y * mat.m[1][1] + mat.m[2][1]; - } - - void Invert(matrix3x3 &matIn, matrix3x3 &matOut) - { - float det = matIn.m[0][0] * (matIn.m[1][1] * matIn.m[2][2] - matIn.m[1][2] * matIn.m[2][1]) - - matIn.m[1][0] * (matIn.m[0][1] * matIn.m[2][2] - matIn.m[2][1] * matIn.m[0][2]) + - matIn.m[2][0] * (matIn.m[0][1] * matIn.m[1][2] - matIn.m[1][1] * matIn.m[0][2]); - - float idet = 1.0f / det; - matOut.m[0][0] = (matIn.m[1][1] * matIn.m[2][2] - matIn.m[1][2] * matIn.m[2][1]) * idet; - matOut.m[1][0] = (matIn.m[2][0] * matIn.m[1][2] - matIn.m[1][0] * matIn.m[2][2]) * idet; - matOut.m[2][0] = (matIn.m[1][0] * matIn.m[2][1] - matIn.m[2][0] * matIn.m[1][1]) * idet; - matOut.m[0][1] = (matIn.m[2][1] * matIn.m[0][2] - matIn.m[0][1] * matIn.m[2][2]) * idet; - matOut.m[1][1] = (matIn.m[0][0] * matIn.m[2][2] - matIn.m[2][0] * matIn.m[0][2]) * idet; - matOut.m[2][1] = (matIn.m[0][1] * matIn.m[2][0] - matIn.m[0][0] * matIn.m[2][1]) * idet; - matOut.m[0][2] = (matIn.m[0][1] * matIn.m[1][2] - matIn.m[0][2] * matIn.m[1][1]) * idet; - matOut.m[1][2] = (matIn.m[0][2] * matIn.m[1][0] - matIn.m[0][0] * matIn.m[1][2]) * idet; - matOut.m[2][2] = (matIn.m[0][0] * matIn.m[1][1] - matIn.m[0][1] * matIn.m[1][0]) * idet; - } - - - float fRotate = 0.0f; - -public: - bool OnUserCreate() override - { - sprCar = new olc::Sprite("car_top1.png"); - - return true; - } - - bool OnUserUpdate(float fElapsedTime) override - { - - if (GetKey(olc::Key::Z).bHeld) fRotate -= 2.0f * fElapsedTime; - if (GetKey(olc::Key::X).bHeld) fRotate += 2.0f * fElapsedTime; - - - Clear(olc::DARK_CYAN); - - SetPixelMode(olc::Pixel::ALPHA); - //DrawSprite(0, 0, sprCar, 3); - - - matrix3x3 matFinal, matA, matB, matC, matFinalInv; - Translate(matA, -100, -50); - Rotate(matB, fRotate); - MatrixMultiply(matC, matB, matA); - - Translate(matA, (float)ScreenWidth()/2, (float)ScreenHeight()/2); - MatrixMultiply(matFinal, matA, matC); - - Invert(matFinal, matFinalInv); - - // Draws the dumb way, but leaves gaps - /*for (int x = 0; x < sprCar->width; x++) - { - for (int y = 0; y < sprCar->height; y++) - { - olc::Pixel p = sprCar->GetPixel(x, y); - - float nx, ny; - Forward(matFinal, (float)x, (float)y, nx, ny); - Draw(nx, ny, p); - } - }*/ - - // Work out bounding box of sprite post-transformation - // by passing through sprite corner locations into - // transformation matrix - float ex, ey; - float sx, sy; - float px, py; - - Forward(matFinal, 0.0f, 0.0f, px, py); - sx = px; sy = py; - ex = px; ey = py; - - Forward(matFinal, (float)sprCar->width, (float)sprCar->height, px, py); - sx = std::min(sx, px); sy = std::min(sy, py); - ex = std::max(ex, px); ey = std::max(ey, py); - - Forward(matFinal, 0.0f, (float)sprCar->height, px, py); - sx = std::min(sx, px); sy = std::min(sy, py); - ex = std::max(ex, px); ey = std::max(ey, py); - - Forward(matFinal, (float)sprCar->width, 0.0f, px, py); - sx = std::min(sx, px); sy = std::min(sy, py); - ex = std::max(ex, px); ey = std::max(ey, py); - - // Use transformed corner locations in screen space to establish - // region of pixels to fill, using inverse transform to sample - // sprite at suitable locations. - for (int x = sx; x < ex; x++) - { - for (int y = sy; y < ey; y++) - { - float nx, ny; - Forward(matFinalInv, (float)x, (float)y, nx, ny); - olc::Pixel p = sprCar->GetPixel((int32_t)(nx + 0.5f), (int32_t)(ny + 0.5f)); - Draw(x, y, p); - } - } - - SetPixelMode(olc::Pixel::NORMAL); - - return true; - } -}; - -int main() -{ - SpriteTransforms demo; - if (demo.Construct(256, 240, 4, 4)) - demo.Start(); - return 0; -} \ No newline at end of file diff --git a/SampleA.wav b/SampleA.wav deleted file mode 100644 index 9a53e04..0000000 Binary files a/SampleA.wav and /dev/null differ diff --git a/SampleB.wav b/SampleB.wav deleted file mode 100644 index aac72c7..0000000 Binary files a/SampleB.wav and /dev/null differ diff --git a/SampleC.wav b/SampleC.wav deleted file mode 100644 index 111a57b..0000000 Binary files a/SampleC.wav and /dev/null differ diff --git a/car_top1.png b/car_top1.png deleted file mode 100644 index 15ceb1d..0000000 Binary files a/car_top1.png and /dev/null differ diff --git a/light_cast.png b/light_cast.png deleted file mode 100644 index 4ac2ce8..0000000 Binary files a/light_cast.png and /dev/null differ diff --git a/logo_long.png b/logo_long.png deleted file mode 100644 index e4ca5b3..0000000 Binary files a/logo_long.png and /dev/null differ diff --git a/olcExampleProgram.cpp b/olcExampleProgram.cpp deleted file mode 100644 index c9f3588..0000000 --- a/olcExampleProgram.cpp +++ /dev/null @@ -1,37 +0,0 @@ -#define OLC_PGE_APPLICATION -#include "olcPixelGameEngine.h" - -class Example : public olc::PixelGameEngine -{ -public: - Example() - { - sAppName = "Example"; - } - -public: - bool OnUserCreate() override - { - // Called once at the start, so create things here - return true; - } - - bool OnUserUpdate(float fElapsedTime) override - { - // called once per frame - for (int x = 0; x < ScreenWidth(); x++) - for (int y = 0; y < ScreenHeight(); y++) - Draw(x, y, olc::Pixel(rand() % 255, rand() % 255, rand()% 255)); - return true; - } -}; - - -int main() -{ - Example demo; - if (demo.Construct(256, 240, 4, 4)) - demo.Start(); - - return 0; -}