Upstream for PGE updates.
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.
olcPixelGameEngine/Videos/olcPGEX_Graphics3D.h

1174 lines
40 KiB

/*
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, <EFBFBD>OneLoneCoder 2018
*/
#ifndef OLC_PGEX_GFX3D
#define OLC_PGEX_GFX3D
#include <algorithm>
#include <vector>
#include <list>
#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<triangle> 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<olc::GFX3D::triangle> &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<olc::GFX3D::triangle> &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<GFX3D::triangle> 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<triangle> 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