You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
211 lines
6.2 KiB
211 lines
6.2 KiB
7 years ago
|
/*
|
||
|
OneLoneCoder.com - Programming Pseudo 3D planes, aka MODE7
|
||
|
"The SNES was too advanced for its time" - @Javidx9
|
||
|
|
||
|
License
|
||
|
~~~~~~~
|
||
|
Copyright (C) 2018 Javidx9
|
||
|
This program comes with ABSOLUTELY NO WARRANTY.
|
||
|
This is free software, and you are welcome to redistribute it
|
||
|
under certain conditions; See license for details.
|
||
|
Original works located at:
|
||
|
|
||
|
https://www.github.com/onelonecoder
|
||
|
https://www.onelonecoder.com
|
||
|
https://www.youtube.com/javidx9
|
||
|
|
||
|
GNU GPLv3
|
||
|
https://github.com/OneLoneCoder/videos/blob/master/LICENSE
|
||
|
|
||
|
From Javidx9 :)
|
||
|
~~~~~~~~~~~~~~~
|
||
|
Hello! Ultimately I don't care what you use this for. It's intended to be
|
||
|
educational, and perhaps to the oddly minded - a little bit of fun.
|
||
|
Please hack this, change it and use it in any way you see fit. You acknowledge
|
||
|
that I am not responsible for anything bad that happens as a result of
|
||
|
your actions. However this code is protected by GNU GPLv3, see the license in the
|
||
|
github repo. This means you must attribute me if you use it. You can view this
|
||
|
license here: https://github.com/OneLoneCoder/videos/blob/master/LICENSE
|
||
|
|
||
|
Cheers!
|
||
|
|
||
|
Background
|
||
|
~~~~~~~~~~
|
||
|
Pseudo 3D was a technique made popular by the Super Nintendo
|
||
|
in games such as Super Mario Kart. This code demonstrates the
|
||
|
effect of that hardware mode.
|
||
|
|
||
|
Author
|
||
|
~~~~~~
|
||
|
Twitter: @javidx9
|
||
|
Blog: www.onelonecoder.com
|
||
|
|
||
|
Video:
|
||
|
~~~~~~
|
||
|
https://youtu.be/ybLZyY655iY
|
||
|
|
||
|
Last Updated: 15/04/2018
|
||
|
*/
|
||
|
|
||
|
#include "olcConsoleGameEngine.h"
|
||
|
|
||
|
class OneLoneCoder_FakeMode7 : public olcConsoleGameEngine
|
||
|
{
|
||
|
public:
|
||
|
OneLoneCoder_FakeMode7()
|
||
|
{
|
||
|
m_sAppName = L"Pseudo 3D Planes";
|
||
|
}
|
||
|
|
||
|
|
||
|
private:
|
||
|
float fWorldX = 1000.0f;
|
||
|
float fWorldY = 1000.0f;
|
||
|
float fWorldA = 0.1f;
|
||
|
float fNear = 0.005f;
|
||
|
float fFar = 0.03f;
|
||
|
float fFoVHalf = 3.14159f / 4.0f;
|
||
|
|
||
|
olcSprite *sprGround;
|
||
|
olcSprite *sprSky;
|
||
|
|
||
|
int nMapSize = 1024;
|
||
|
|
||
|
protected:
|
||
|
virtual bool OnUserCreate()
|
||
|
{
|
||
|
// Create a large sprite and fill it with horizontal and vertical lines
|
||
|
//sprGround = new olcSprite(nMapSize, nMapSize);
|
||
|
|
||
|
//for (int x = 0; x <= nMapSize; x += 32)
|
||
|
//{
|
||
|
// for (int y = 0; y < nMapSize; y++)
|
||
|
// {
|
||
|
// sprGround->SetColour(x, y, FG_MAGENTA);
|
||
|
// sprGround->SetGlyph(x, y, PIXEL_SOLID);
|
||
|
|
||
|
// sprGround->SetColour(x+1, y, FG_MAGENTA);
|
||
|
// sprGround->SetGlyph(x+1, y, PIXEL_SOLID);
|
||
|
|
||
|
// sprGround->SetColour(x-1, y, FG_MAGENTA);
|
||
|
// sprGround->SetGlyph(x-1, y, PIXEL_SOLID);
|
||
|
|
||
|
// sprGround->SetColour(y, x, FG_BLUE);
|
||
|
// sprGround->SetGlyph(y, x, PIXEL_SOLID);
|
||
|
|
||
|
// sprGround->SetColour(y, x+1, FG_BLUE);
|
||
|
// sprGround->SetGlyph(y, x+1, PIXEL_SOLID);
|
||
|
|
||
|
// sprGround->SetColour(y, x-1, FG_BLUE);
|
||
|
// sprGround->SetGlyph(y, x-1, PIXEL_SOLID);
|
||
|
// }
|
||
|
//}
|
||
|
|
||
|
// Simply load very large sprites from file
|
||
|
sprGround = new olcSprite(L"mariokart.spr");
|
||
|
sprSky = new olcSprite(L"sky1.spr");
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
virtual bool OnUserUpdate(float fElapsedTime)
|
||
|
{
|
||
|
|
||
|
// Control rendering parameters dynamically
|
||
|
if (GetKey(L'Q').bHeld) fNear += 0.1f * fElapsedTime;
|
||
|
if (GetKey(L'A').bHeld) fNear -= 0.1f * fElapsedTime;
|
||
|
|
||
|
if (GetKey(L'W').bHeld) fFar += 0.1f * fElapsedTime;
|
||
|
if (GetKey(L'S').bHeld) fFar -= 0.1f * fElapsedTime;
|
||
|
|
||
|
if (GetKey(L'Z').bHeld) fFoVHalf += 0.1f * fElapsedTime;
|
||
|
if (GetKey(L'X').bHeld) fFoVHalf -= 0.1f * fElapsedTime;
|
||
|
|
||
|
|
||
|
// Create Frustum corner points
|
||
|
float fFarX1 = fWorldX + cosf(fWorldA - fFoVHalf) * fFar;
|
||
|
float fFarY1 = fWorldY + sinf(fWorldA - fFoVHalf) * fFar;
|
||
|
|
||
|
float fNearX1 = fWorldX + cosf(fWorldA - fFoVHalf) * fNear;
|
||
|
float fNearY1 = fWorldY + sinf(fWorldA - fFoVHalf) * fNear;
|
||
|
|
||
|
float fFarX2 = fWorldX + cosf(fWorldA + fFoVHalf) * fFar;
|
||
|
float fFarY2 = fWorldY + sinf(fWorldA + fFoVHalf) * fFar;
|
||
|
|
||
|
float fNearX2 = fWorldX + cosf(fWorldA + fFoVHalf) * fNear;
|
||
|
float fNearY2 = fWorldY + sinf(fWorldA + fFoVHalf) * fNear;
|
||
|
|
||
|
// Starting with furthest away line and work towards the camera point
|
||
|
for (int y = 0; y < ScreenHeight() / 2; y++)
|
||
|
{
|
||
|
// Take a sample point for depth linearly related to rows down screen
|
||
|
float fSampleDepth = (float)y / ((float)ScreenHeight() / 2.0f);
|
||
|
|
||
|
// Use sample point in non-linear (1/x) way to enable perspective
|
||
|
// and grab start and end points for lines across the screen
|
||
|
float fStartX = (fFarX1 - fNearX1) / (fSampleDepth) + fNearX1;
|
||
|
float fStartY = (fFarY1 - fNearY1) / (fSampleDepth) + fNearY1;
|
||
|
float fEndX = (fFarX2 - fNearX2) / (fSampleDepth) + fNearX2;
|
||
|
float fEndY = (fFarY2 - fNearY2) / (fSampleDepth) + fNearY2;
|
||
|
|
||
|
// Linearly interpolate lines across the screen
|
||
|
for (int x = 0; x < ScreenWidth(); x++)
|
||
|
{
|
||
|
float fSampleWidth = (float)x / (float)ScreenWidth();
|
||
|
float fSampleX = (fEndX - fStartX) * fSampleWidth + fStartX;
|
||
|
float fSampleY = (fEndY - fStartY) * fSampleWidth + fStartY;
|
||
|
|
||
|
// Wrap sample coordinates to give "infinite" periodicity on maps
|
||
|
fSampleX = fmod(fSampleX, 1.0f);
|
||
|
fSampleY = fmod(fSampleY, 1.0f);
|
||
|
|
||
|
// Sample symbol and colour from map sprite, and draw the
|
||
|
// pixel to the screen
|
||
|
wchar_t sym = sprGround->SampleGlyph(fSampleX, fSampleY);
|
||
|
short col = sprGround->SampleColour(fSampleX, fSampleY);
|
||
|
Draw(x, y + (ScreenHeight() / 2), sym, col);
|
||
|
|
||
|
// Sample symbol and colour from sky sprite, we can use same
|
||
|
// coordinates, but we need to draw the "inverted" y-location
|
||
|
sym = sprSky->SampleGlyph(fSampleX, fSampleY);
|
||
|
col = sprSky->SampleColour(fSampleX, fSampleY);
|
||
|
Draw(x, (ScreenHeight() / 2) - y, sym, col);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Draw a blanking line to fill the gap between sky and ground
|
||
|
DrawLine(0, ScreenHeight() / 2, ScreenWidth(), ScreenHeight() / 2, PIXEL_SOLID, FG_CYAN);
|
||
|
|
||
|
// Handle user navigation with arrow keys
|
||
|
if (GetKey(VK_LEFT).bHeld)
|
||
|
fWorldA -= 1.0f * fElapsedTime;
|
||
|
|
||
|
if (GetKey(VK_RIGHT).bHeld)
|
||
|
fWorldA += 1.0f * fElapsedTime;
|
||
|
|
||
|
if (GetKey(VK_UP).bHeld)
|
||
|
{
|
||
|
fWorldX += cosf(fWorldA) * 0.2f * fElapsedTime;
|
||
|
fWorldY += sinf(fWorldA) * 0.2f * fElapsedTime;
|
||
|
}
|
||
|
|
||
|
if (GetKey(VK_DOWN).bHeld)
|
||
|
{
|
||
|
fWorldX -= cosf(fWorldA) * 0.2f * fElapsedTime;
|
||
|
fWorldY -= sinf(fWorldA) * 0.2f * fElapsedTime;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
};
|
||
|
|
||
|
int main()
|
||
|
{
|
||
|
OneLoneCoder_FakeMode7 game;
|
||
|
game.ConstructConsole(320, 240, 4, 4);
|
||
|
game.Start();
|
||
|
return 0;
|
||
|
}
|