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.
236 lines
13 KiB
236 lines
13 KiB
/*
|
|
OneLoneCoder.com - 3D Graphics Part #1 - Triangles & Projections
|
|
"Tredimensjonal Grafikk" - @Javidx9
|
|
|
|
License
|
|
~~~~~~~
|
|
One Lone Coder Console Game Engine 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
|
|
~~~~~~~~~~
|
|
3D Graphics is an interesting, visually pleasing suite of algorithms. This is the
|
|
first video in a series that will demonstrate the fundamentals required to
|
|
build your own software based 3D graphics systems.
|
|
|
|
Video
|
|
~~~~~
|
|
https://youtu.be/ih20l3pJoeU
|
|
|
|
Author
|
|
~~~~~~
|
|
Twitter: @javidx9
|
|
Blog: http://www.onelonecoder.com
|
|
Discord: https://discord.gg/WhwHUMV
|
|
|
|
|
|
Last Updated: 14/07/2018
|
|
*/
|
|
|
|
|
|
#include "olcConsoleGameEngine.h"
|
|
using namespace std;
|
|
|
|
|
|
struct vec3d
|
|
{
|
|
float x, y, z;
|
|
};
|
|
|
|
struct triangle
|
|
{
|
|
vec3d p[3];
|
|
};
|
|
|
|
struct mesh
|
|
{
|
|
vector<triangle> tris;
|
|
};
|
|
|
|
struct mat4x4
|
|
{
|
|
float m[4][4] = { 0 };
|
|
};
|
|
|
|
class olcEngine3D : public olcConsoleGameEngine
|
|
{
|
|
public:
|
|
olcEngine3D()
|
|
{
|
|
m_sAppName = L"3D Demo";
|
|
}
|
|
|
|
|
|
private:
|
|
mesh meshCube;
|
|
mat4x4 matProj;
|
|
|
|
float fTheta;
|
|
|
|
void MultiplyMatrixVector(vec3d &i, vec3d &o, mat4x4 &m)
|
|
{
|
|
o.x = i.x * m.m[0][0] + i.y * m.m[1][0] + i.z * m.m[2][0] + m.m[3][0];
|
|
o.y = i.x * m.m[0][1] + i.y * m.m[1][1] + i.z * m.m[2][1] + m.m[3][1];
|
|
o.z = i.x * m.m[0][2] + i.y * m.m[1][2] + i.z * m.m[2][2] + m.m[3][2];
|
|
float w = i.x * m.m[0][3] + i.y * m.m[1][3] + i.z * m.m[2][3] + m.m[3][3];
|
|
|
|
if (w != 0.0f)
|
|
{
|
|
o.x /= w; o.y /= w; o.z /= w;
|
|
}
|
|
}
|
|
|
|
public:
|
|
bool OnUserCreate() override
|
|
{
|
|
meshCube.tris = {
|
|
|
|
// SOUTH
|
|
{ 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, 0.0f, 1.0f, 0.0f, 0.0f },
|
|
|
|
// EAST
|
|
{ 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f },
|
|
{ 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f },
|
|
|
|
// NORTH
|
|
{ 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f },
|
|
{ 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f },
|
|
|
|
// WEST
|
|
{ 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f },
|
|
{ 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f },
|
|
|
|
// TOP
|
|
{ 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f },
|
|
{ 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f },
|
|
|
|
// BOTTOM
|
|
{ 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f },
|
|
{ 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f },
|
|
|
|
};
|
|
|
|
// Projection Matrix
|
|
float fNear = 0.1f;
|
|
float fFar = 1000.0f;
|
|
float fFov = 90.0f;
|
|
float fAspectRatio = (float)ScreenHeight() / (float)ScreenWidth();
|
|
float fFovRad = 1.0f / tanf(fFov * 0.5f / 180.0f * 3.14159f);
|
|
|
|
matProj.m[0][0] = fAspectRatio * fFovRad;
|
|
matProj.m[1][1] = fFovRad;
|
|
matProj.m[2][2] = fFar / (fFar - fNear);
|
|
matProj.m[3][2] = (-fFar * fNear) / (fFar - fNear);
|
|
matProj.m[2][3] = 1.0f;
|
|
matProj.m[3][3] = 0.0f;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool OnUserUpdate(float fElapsedTime) override
|
|
{
|
|
// Clear Screen
|
|
Fill(0, 0, ScreenWidth(), ScreenHeight(), PIXEL_SOLID, FG_BLACK);
|
|
|
|
// Set up rotation matrices
|
|
mat4x4 matRotZ, matRotX;
|
|
fTheta += 1.0f * fElapsedTime;
|
|
|
|
// Rotation Z
|
|
matRotZ.m[0][0] = cosf(fTheta);
|
|
matRotZ.m[0][1] = sinf(fTheta);
|
|
matRotZ.m[1][0] = -sinf(fTheta);
|
|
matRotZ.m[1][1] = cosf(fTheta);
|
|
matRotZ.m[2][2] = 1;
|
|
matRotZ.m[3][3] = 1;
|
|
|
|
// Rotation X
|
|
matRotX.m[0][0] = 1;
|
|
matRotX.m[1][1] = cosf(fTheta * 0.5f);
|
|
matRotX.m[1][2] = sinf(fTheta * 0.5f);
|
|
matRotX.m[2][1] = -sinf(fTheta * 0.5f);
|
|
matRotX.m[2][2] = cosf(fTheta * 0.5f);
|
|
matRotX.m[3][3] = 1;
|
|
|
|
// Draw Triangles
|
|
for (auto tri : meshCube.tris)
|
|
{
|
|
triangle triProjected, triTranslated, triRotatedZ, triRotatedZX;
|
|
|
|
// Rotate in Z-Axis
|
|
MultiplyMatrixVector(tri.p[0], triRotatedZ.p[0], matRotZ);
|
|
MultiplyMatrixVector(tri.p[1], triRotatedZ.p[1], matRotZ);
|
|
MultiplyMatrixVector(tri.p[2], triRotatedZ.p[2], matRotZ);
|
|
|
|
// Rotate in X-Axis
|
|
MultiplyMatrixVector(triRotatedZ.p[0], triRotatedZX.p[0], matRotX);
|
|
MultiplyMatrixVector(triRotatedZ.p[1], triRotatedZX.p[1], matRotX);
|
|
MultiplyMatrixVector(triRotatedZ.p[2], triRotatedZX.p[2], matRotX);
|
|
|
|
// Offset into the screen
|
|
triTranslated = triRotatedZX;
|
|
triTranslated.p[0].z = triRotatedZX.p[0].z + 3.0f;
|
|
triTranslated.p[1].z = triRotatedZX.p[1].z + 3.0f;
|
|
triTranslated.p[2].z = triRotatedZX.p[2].z + 3.0f;
|
|
|
|
// Project triangles from 3D --> 2D
|
|
MultiplyMatrixVector(triTranslated.p[0], triProjected.p[0], matProj);
|
|
MultiplyMatrixVector(triTranslated.p[1], triProjected.p[1], matProj);
|
|
MultiplyMatrixVector(triTranslated.p[2], triProjected.p[2], matProj);
|
|
|
|
// Scale into view
|
|
triProjected.p[0].x += 1.0f; triProjected.p[0].y += 1.0f;
|
|
triProjected.p[1].x += 1.0f; triProjected.p[1].y += 1.0f;
|
|
triProjected.p[2].x += 1.0f; triProjected.p[2].y += 1.0f;
|
|
triProjected.p[0].x *= 0.5f * (float)ScreenWidth();
|
|
triProjected.p[0].y *= 0.5f * (float)ScreenHeight();
|
|
triProjected.p[1].x *= 0.5f * (float)ScreenWidth();
|
|
triProjected.p[1].y *= 0.5f * (float)ScreenHeight();
|
|
triProjected.p[2].x *= 0.5f * (float)ScreenWidth();
|
|
triProjected.p[2].y *= 0.5f * (float)ScreenHeight();
|
|
|
|
// Rasterize triangle
|
|
DrawTriangle(triProjected.p[0].x, triProjected.p[0].y,
|
|
triProjected.p[1].x, triProjected.p[1].y,
|
|
triProjected.p[2].x, triProjected.p[2].y,
|
|
PIXEL_SOLID, FG_WHITE);
|
|
|
|
}
|
|
|
|
|
|
return true;
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
int main()
|
|
{
|
|
olcEngine3D demo;
|
|
if (demo.ConstructConsole(256, 240, 4, 4))
|
|
demo.Start();
|
|
return 0;
|
|
}
|
|
|
|
|