forked from OneLoneCoder/Javidx9
-
Notifications
You must be signed in to change notification settings - Fork 0
/
OneLoneCoder_Pseudo3DPlanesMode7.cpp
211 lines (162 loc) · 6.22 KB
/
OneLoneCoder_Pseudo3DPlanesMode7.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
/*
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;
}