3D Renderer using Hardware rendering w/Decals!
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.
SigRenderer3/main.cpp

693 lines
21 KiB

#define OLC_PGE_APPLICATION
#include "pixelGameEngine.h"
#include <strstream>
#include <algorithm>
using namespace olc;
struct vec2d
{
float u=0;
float v=0;
float w=1;
};
struct vec3d
{
float x=0;
float y=0;
float z=0;
float w=1;
};
struct triangle
{
vec3d p[3];
vec2d uv[3];
Pixel col;
};
struct mesh
{
std::vector<triangle> tris;
bool LoadFromObjectFile(std::string sFilename)
{
std::ifstream f(sFilename);
if (!f.is_open())
return false;
// Local cache of verts
std::vector<vec3d> verts;
std::vector<vec2d> uvs;
while (!f.eof())
{
char line[128];
f.getline(line, 128);
std::strstream s;
s << line;
char junk;
if (line[0] == 'v')
{
if (line[1]=='t') {
vec2d v;
s >> junk >> junk >> v.u >> v.v;
uvs.push_back(v);
//std::cout<<"Line: "<<line<<"\n";
//std::cout<<"Tex coords: "<<v.u<<","<<v.v<<"\n";
} else
if (line[1] != 'n')
{
vec3d v;
s >> junk >> v.x >> v.y >> v.z;
verts.push_back(v);
//std::cout<<"Line: "<<line<<"\n";
//std::cout<<"Vertex: "<<v.x<<","<<v.y<<","<<v.z<<"\n";
}
}
if (line[0] == 'f')
{
s >> junk;
std::string tokens[9];
int nTokenCount = -1;
while (!s.eof())
{
char c = s.get();
if (c == ' ' || c == '/')
nTokenCount++;
else
tokens[nTokenCount].append(1, c);
}
tokens[nTokenCount].pop_back();
triangle newtri{ verts[stoi(tokens[0]) - 1], verts[stoi(tokens[3]) - 1], verts[stoi(tokens[6]) - 1],
uvs[stoi(tokens[1]) - 1], uvs[stoi(tokens[4]) - 1], uvs[stoi(tokens[7]) - 1]};
if (newtri.p[0].y>16||newtri.p[1].y>16||newtri.p[2].y>16) {
newtri.col=WHITE;
} else
if (newtri.p[0].y>10||newtri.p[1].y>10||newtri.p[2].y>10) {
newtri.col=DARK_GREEN;
} else
if (newtri.p[0].y>6||newtri.p[1].y>6||newtri.p[2].y>6) {
newtri.col=Pixel(56, 38, 39);
} else {
newtri.col=Pixel(0,100,255);
}
tris.push_back(newtri);
}
}
return true;
}
};
struct mat4x4
{
float m[4][4] = { 0 };
};
class olcEngine3D : public PixelGameEngine
{
public:
Decal*texture;
olcEngine3D()
{
sAppName = "3D Demo";
}
private:
mesh meshCube;
mat4x4 matProj;
vec3d vCamera={0,0,0};
vec3d vLookDir;
float zOffset=2;
float fTheta=0;
float fYaw=0;
float pitch=-M_PI/6;
vec3d
Matrix_MultiplyVector(mat4x4 &m, 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;
}
mat4x4 Matrix_MakeIdentity()
{
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;
}
mat4x4 Matrix_MakeRotationX(float fAngleRad)
{
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;
}
mat4x4 Matrix_MakeRotationY(float fAngleRad)
{
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;
}
mat4x4 Matrix_MakeRotationZ(float fAngleRad)
{
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;
}
mat4x4 Matrix_MakeTranslation(float x, float y, float z)
{
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;
}
mat4x4 Matrix_MakeProjection(float fFovDegrees, float fAspectRatio, float fNear, float fFar)
{
float fFovRad = 1.0f / tanf(fFovDegrees * 0.5f / 180.0f * 3.14159f);
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;
}
mat4x4 Matrix_MultiplyMatrix(mat4x4 &m1, mat4x4 &m2)
{
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;
}
mat4x4 Matrix_PointAt(vec3d &pos, vec3d &target, vec3d &up)
{
// Calculate new forward direction
vec3d newForward = Vector_Sub(target, pos);
newForward = Vector_Normalise(newForward);
// Calculate new Up direction
vec3d a = Vector_Mul(newForward, Vector_DotProduct(up, newForward));
vec3d newUp = Vector_Sub(up, a);
newUp = Vector_Normalise(newUp);
// New Right direction is easy, its just cross product
vec3d newRight = Vector_CrossProduct(newUp, newForward);
// Construct Dimensioning and Translation Matrix
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;
}
mat4x4 Matrix_QuickInverse(mat4x4 &m) // Only for Rotation/Translation Matrices
{
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;
}
vec3d Vector_Add(vec3d &v1, vec3d &v2)
{
return { v1.x + v2.x, v1.y + v2.y, v1.z + v2.z };
}
vec3d Vector_Sub(vec3d &v1, vec3d &v2)
{
return { v1.x - v2.x, v1.y - v2.y, v1.z - v2.z };
}
vec3d Vector_Mul(vec3d &v1, float k)
{
return { v1.x * k, v1.y * k, v1.z * k };
}
vec3d Vector_Div(vec3d &v1, float k)
{
return { v1.x / k, v1.y / k, v1.z / k };
}
float Vector_DotProduct(vec3d &v1, vec3d &v2)
{
return v1.x*v2.x + v1.y*v2.y + v1.z * v2.z;
}
float Vector_Length(vec3d &v)
{
return sqrtf(Vector_DotProduct(v, v));
}
vec3d Vector_Normalise(vec3d &v)
{
float l = Vector_Length(v);
return { v.x / l, v.y / l, v.z / l };
}
vec3d Vector_CrossProduct(vec3d &v1, 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;
}
vec3d Vector_IntersectPlane(vec3d &plane_p, vec3d &plane_n, vec3d &lineStart, vec3d &lineEnd, float &t)
{
plane_n = Vector_Normalise(plane_n);
float plane_d = -Vector_DotProduct(plane_n, plane_p);
float ad = Vector_DotProduct(lineStart, plane_n);
float bd = Vector_DotProduct(lineEnd, plane_n);
t = (-plane_d - ad) / (bd - ad);
vec3d lineStartToEnd = Vector_Sub(lineEnd, lineStart);
vec3d lineToIntersect = Vector_Mul(lineStartToEnd, t);
return Vector_Add(lineStart, lineToIntersect);
}
int 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 = Vector_Normalise(plane_n);
// Return signed shortest distance from point to plane, plane normal must be normalised
auto dist = [&](vec3d &p)
{
vec3d n = Vector_Normalise(p);
return (plane_n.x * p.x + plane_n.y * p.y + plane_n.z * p.z - Vector_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.uv[0]; }
else {
outside_points[nOutsidePointCount++] = &in_tri.p[0]; outside_tex[nOutsideTexCount++] = &in_tri.uv[0];
}
if (d1 >= 0) {
inside_points[nInsidePointCount++] = &in_tri.p[1]; inside_tex[nInsideTexCount++] = &in_tri.uv[1];
}
else {
outside_points[nOutsidePointCount++] = &in_tri.p[1]; outside_tex[nOutsideTexCount++] = &in_tri.uv[1];
}
if (d2 >= 0) {
inside_points[nInsidePointCount++] = &in_tri.p[2]; inside_tex[nInsideTexCount++] = &in_tri.uv[2];
}
else {
outside_points[nOutsidePointCount++] = &in_tri.p[2]; outside_tex[nOutsideTexCount++] = &in_tri.uv[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 = in_tri.col;
// The inside point is valid, so keep that...
out_tri1.p[0] = *inside_points[0];
out_tri1.uv[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] = Vector_IntersectPlane(plane_p, plane_n, *inside_points[0], *outside_points[0], t);
out_tri1.uv[1].u = t * (outside_tex[0]->u - inside_tex[0]->u) + inside_tex[0]->u;
out_tri1.uv[1].v = t * (outside_tex[0]->v - inside_tex[0]->v) + inside_tex[0]->v;
out_tri1.uv[1].w = t * (outside_tex[0]->w - inside_tex[0]->w) + inside_tex[0]->w;
out_tri1.p[2] = Vector_IntersectPlane(plane_p, plane_n, *inside_points[0], *outside_points[1], t);
out_tri1.uv[2].u = t * (outside_tex[1]->u - inside_tex[0]->u) + inside_tex[0]->u;
out_tri1.uv[2].v = t * (outside_tex[1]->v - inside_tex[0]->v) + inside_tex[0]->v;
out_tri1.uv[2].w = t * (outside_tex[1]->w - inside_tex[0]->w) + inside_tex[0]->w;
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 = in_tri.col;
out_tri2.col = 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.p[1] = *inside_points[1];
out_tri1.uv[0] = *inside_tex[0];
out_tri1.uv[1] = *inside_tex[1];
float t;
out_tri1.p[2] = Vector_IntersectPlane(plane_p, plane_n, *inside_points[0], *outside_points[0], t);
out_tri1.uv[2].u = t * (outside_tex[0]->u - inside_tex[0]->u) + inside_tex[0]->u;
out_tri1.uv[2].v = t * (outside_tex[0]->v - inside_tex[0]->v) + inside_tex[0]->v;
out_tri1.uv[2].w = t * (outside_tex[0]->w - inside_tex[0]->w) + inside_tex[0]->w;
// 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[0] = *inside_points[1];
out_tri2.uv[0] = *inside_tex[1];
out_tri2.p[1] = out_tri1.p[2];
out_tri2.uv[1] = out_tri1.uv[2];
out_tri2.p[2] = Vector_IntersectPlane(plane_p, plane_n, *inside_points[1], *outside_points[0], t);
out_tri2.uv[2].u = t * (outside_tex[0]->u - inside_tex[1]->u) + inside_tex[1]->u;
out_tri2.uv[2].v = t * (outside_tex[0]->v - inside_tex[1]->v) + inside_tex[1]->v;
out_tri2.uv[2].w = t * (outside_tex[0]->w - inside_tex[1]->w) + inside_tex[1]->w;
return 2; // Return two newly formed triangles which form a quad
}
}
public:
bool OnUserCreate() override
{
//texture = new Decal(new Sprite("Body.png"));
meshCube.LoadFromObjectFile("obj_1.obj");
matProj=Matrix_MakeProjection(90.0f,(float)ScreenHeight() / (float)ScreenWidth(),0.1f,1000.0f);
return true;
}
bool OnUserUpdate(float fElapsedTime) override
{
if (GetKey(olc::DOWN).bHeld) {
pitch-=1*fElapsedTime;
}
if (GetKey(olc::UP).bHeld) {
pitch+=1*fElapsedTime;
}
vec3d vForward=Vector_Mul(vLookDir,8*fElapsedTime);
if (GetKey(olc::W).bHeld) {
vCamera=Vector_Add(vCamera,vForward);
}
if (GetKey(olc::S).bHeld) {
vCamera=Vector_Sub(vCamera,vForward);
}
if (GetKey(olc::A).bHeld) {
fYaw-=2*fElapsedTime;
}
if (GetKey(olc::D).bHeld) {
fYaw+=2*fElapsedTime;
}
// Set up rotation matrices
mat4x4 matRotZ, matRotX, matTrans, matWorld;
matRotZ=Matrix_MakeRotationZ(fTheta*0.5f);
matRotX=Matrix_MakeRotationX(fTheta);
matTrans=Matrix_MakeTranslation(0.0f,0.0f,5.0f);
matWorld=Matrix_MakeIdentity();
matWorld=Matrix_MultiplyMatrix(matRotZ,matRotX);
matWorld=Matrix_MultiplyMatrix(matWorld,matTrans);
vec3d vUp={0,1,0};
vec3d vTarget={0,sinf(pitch),cosf(pitch)};
mat4x4 matCameraRot=Matrix_MakeRotationY(fYaw);
vLookDir=Matrix_MultiplyVector(matCameraRot,vTarget);
vTarget=Vector_Add(vCamera,vLookDir);
mat4x4 matCamera = Matrix_PointAt(vCamera,vTarget,vUp);
mat4x4 matView=Matrix_QuickInverse(matCamera);
std::vector<triangle>vecTrianglesToRaster;
// Draw Triangles
for (auto&tri : meshCube.tris)
{
triangle triProjected, triTransformed,triViewed;
triTransformed.p[0]=Matrix_MultiplyVector(matWorld,tri.p[0]);
triTransformed.p[1]=Matrix_MultiplyVector(matWorld,tri.p[1]);
triTransformed.p[2]=Matrix_MultiplyVector(matWorld,tri.p[2]);
triTransformed.uv[0]=tri.uv[0];
triTransformed.uv[1]=tri.uv[1];
triTransformed.uv[2]=tri.uv[2];
vec3d normal,line1,line2;
line1=Vector_Sub(triTransformed.p[1],triTransformed.p[0]);
line2=Vector_Sub(triTransformed.p[2],triTransformed.p[0]);
normal=Vector_CrossProduct(line1,line2);
normal=Vector_Normalise(normal);
vec3d vCameraRay=Vector_Sub(triTransformed.p[0],vCamera);
if (Vector_DotProduct(normal,vCameraRay)<0) {
vec3d light_dir=Vector_Mul(vCameraRay,-1);
light_dir=Vector_Normalise(light_dir);
float dp = std::max(0.1f,Vector_DotProduct(light_dir,normal));
triViewed.p[0]=Matrix_MultiplyVector(matView,triTransformed.p[0]);
triViewed.p[1]=Matrix_MultiplyVector(matView,triTransformed.p[1]);
triViewed.p[2]=Matrix_MultiplyVector(matView,triTransformed.p[2]);
triViewed.uv[0]=triTransformed.uv[0];
triViewed.uv[1]=triTransformed.uv[1];
triViewed.uv[2]=triTransformed.uv[2];
triViewed.col=Pixel(tri.col.r*dp*dp,tri.col.g*dp*dp,tri.col.b*dp*dp);
int nClippedTriangles = 0;
triangle clipped[2];
nClippedTriangles = Triangle_ClipAgainstPlane({ 0.0f, 0.0f, 0.1f }, { 0.0f, 0.0f, 1.0f }, triViewed, clipped[0], clipped[1]);
for (int n=0;n<nClippedTriangles;n++) {
// Project triangles from 3D --> 2D
triProjected.p[0]=Matrix_MultiplyVector(matProj,clipped[n].p[0]);
triProjected.p[1]=Matrix_MultiplyVector(matProj,clipped[n].p[1]);
triProjected.p[2]=Matrix_MultiplyVector(matProj,clipped[n].p[2]);
triProjected.col=clipped[n].col;
triProjected.uv[0]=clipped[n].uv[0];
triProjected.uv[1]=clipped[n].uv[1];
triProjected.uv[2]=clipped[n].uv[2];
triProjected.uv[0].u = triProjected.uv[0].u / triProjected.p[0].w;
triProjected.uv[1].u = triProjected.uv[1].u / triProjected.p[1].w;
triProjected.uv[2].u = triProjected.uv[2].u / triProjected.p[2].w;
triProjected.uv[0].v = triProjected.uv[0].v / triProjected.p[0].w;
triProjected.uv[1].v = triProjected.uv[1].v / triProjected.p[1].w;
triProjected.uv[2].v = triProjected.uv[2].v / triProjected.p[2].w;
triProjected.uv[0].w = 1.0f / triProjected.p[0].w;
triProjected.uv[1].w = 1.0f / triProjected.p[1].w;
triProjected.uv[2].w = 1.0f / triProjected.p[2].w;
triProjected.p[0]=Vector_Div(triProjected.p[0],triProjected.p[0].w);
triProjected.p[1]=Vector_Div(triProjected.p[1],triProjected.p[1].w);
triProjected.p[2]=Vector_Div(triProjected.p[2],triProjected.p[2].w);
triProjected.p[0].x*=-1.0f;
triProjected.p[1].x*=-1.0f;
triProjected.p[2].x*=-1.0f;
triProjected.p[0].y*=-1.0f;
triProjected.p[1].y*=-1.0f;
triProjected.p[2].y*=-1.0f;
// Scale into view
vec3d vOffsetView={1,1,0};
triProjected.p[0] = Vector_Add(triProjected.p[0],vOffsetView);
triProjected.p[1] = Vector_Add(triProjected.p[1],vOffsetView);
triProjected.p[2] = Vector_Add(triProjected.p[2],vOffsetView);
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();
vecTrianglesToRaster.push_back(triProjected);
}
}
}
std::sort(vecTrianglesToRaster.begin(),vecTrianglesToRaster.end(),[](triangle&t1,triangle&t2){return (t1.p[0].z+t1.p[1].z+t1.p[2].z)/3.0f>(t2.p[0].z+t2.p[1].z+t2.p[2].z)/3.0f;});
int triRenderCount=0;
for (auto&triToRaster:vecTrianglesToRaster) {
triangle clipped[2];
std::list<triangle>listTriangles;
listTriangles.push_back(triToRaster);
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 = Triangle_ClipAgainstPlane({ 0.0f, 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }, test, clipped[0], clipped[1]); break;
case 1: nTrisToAdd = Triangle_ClipAgainstPlane({ 0.0f, (float)ScreenHeight() - 1, 0.0f }, { 0.0f, -1.0f, 0.0f }, test, clipped[0], clipped[1]); break;
case 2: nTrisToAdd = Triangle_ClipAgainstPlane({ 0.0f, 0.0f, 0.0f }, { 1.0f, 0.0f, 0.0f }, test, clipped[0], clipped[1]); break;
case 3: nTrisToAdd = Triangle_ClipAgainstPlane({ (float)ScreenWidth() - 1, 0.0f, 0.0f }, { -1.0f, 0.0f, 0.0f }, test, clipped[0], clipped[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(clipped[w]);
}
nNewTriangles = listTriangles.size();
}
for (auto&t:listTriangles) {
// Rasterize triangle
SetDecalStructure(DecalStructure::LIST);
SetDecalMode(DecalMode::NORMAL);
DrawPolygonDecal(nullptr,{
{t.p[0].x, t.p[0].y},
{t.p[1].x, t.p[1].y},
{t.p[2].x, t.p[2].y}
},{
{t.uv[0].u,t.uv[0].v},
{t.uv[1].u,t.uv[1].v},
{t.uv[2].u,t.uv[2].v},
},{{t.uv[0].w,t.uv[1].w,t.uv[2].w}},{{t.col,t.col,t.col}});
/*SetDecalMode(DecalMode::WIREFRAME);
DrawPolygonDecal(nullptr,{
{t.p[0].x, t.p[0].y},
{t.p[1].x, t.p[1].y},
{t.p[2].x, t.p[2].y}
},{
{0,0},
{0,0},
{0,0},
},WHITE);*/
SetDecalStructure(DecalStructure::FAN);
triRenderCount++;
}
}
SetDecalMode(DecalMode::NORMAL);
DrawStringDecal({0,0},"Triangles: "+std::to_string(triRenderCount),WHITE,{2,2});
return true;
}
};
int main()
{
olcEngine3D demo;
if (demo.Construct(1280, 720, 1, 1))
demo.Start();
return 0;
}