Setup 3D example.

master
parent 7568ba124e
commit 2a1645a35c
  1. 6
      C++/scripts/web.sh
  2. BIN
      C++ProjectTemplate.data
  3. 2
      C++ProjectTemplate.js
  4. BIN
      C++ProjectTemplate.wasm
  5. 0
      assets/dirtblock.png
  6. 26
      assets/unitcube.obj
  7. 91
      main.cpp
  8. 836
      olcPGEX_Graphics3D.h
  9. 27
      pixelGameEngine.h
  10. BIN
      pixelGameEngine_wasm.o
  11. 2
      sig

@ -12,12 +12,12 @@ source ../emsdk/emsdk_env.sh
if [ ! -f "pixelGameEngine_wasm.o" ] if [ ! -f "pixelGameEngine_wasm.o" ]
then then
printf "Pixel Game Engine compile object missing. Compiling for the first time..." printf "Pixel Game Engine compile object missing. Compiling for the first time..."
em++ -std=c++17 -O2 -s ALLOW_MEMORY_GROWTH=1 -s MAXIMUM_MEMORY=4GB -s MAX_WEBGL_VERSION=2 -s MIN_WEBGL_VERSION=2 -s USE_SDL_MIXER=2 -s USE_LIBPNG=1 -c pixelGameEngine.cpp -o pixelGameEngine_wasm.o em++ -std=c++17 -O2 -sASSERTIONS -s ALLOW_MEMORY_GROWTH=1 -s MAXIMUM_MEMORY=4GB -s MAX_WEBGL_VERSION=2 -s MIN_WEBGL_VERSION=2 -s USE_SDL_MIXER=2 -s USE_LIBPNG=1 -c pixelGameEngine.cpp -o pixelGameEngine_wasm.o
fi fi
if [ -d "assets" ]; then if [ -d "assets" ]; then
em++ -std=c++17 -O2 -s ALLOW_MEMORY_GROWTH=1 -s MAXIMUM_MEMORY=4GB -s MAX_WEBGL_VERSION=2 -s MIN_WEBGL_VERSION=2 -s USE_SDL_MIXER=2 -s USE_LIBPNG=1 $(find . -type f -name "*.cpp" -not -path "./test/*" -not -name "pixelGameEngine.cpp") pixelGameEngine_wasm.o -o ${PROJECT_NAME}.html --preload-file ./assets em++ -std=c++17 -O2 -sASSERTIONS -s ALLOW_MEMORY_GROWTH=1 -s MAXIMUM_MEMORY=4GB -s MAX_WEBGL_VERSION=2 -s MIN_WEBGL_VERSION=2 -s USE_SDL_MIXER=2 -s USE_LIBPNG=1 $(find . -type f -name "*.cpp" -not -path "./test/*" -not -name "pixelGameEngine.cpp") pixelGameEngine_wasm.o -o ${PROJECT_NAME}.html --preload-file ./assets
else else
em++ -std=c++17 -O2 -s ALLOW_MEMORY_GROWTH=1 -s MAXIMUM_MEMORY=4GB -s MAX_WEBGL_VERSION=2 -s MIN_WEBGL_VERSION=2 -s USE_SDL_MIXER=2 -s USE_LIBPNG=1 $(find . -type f -name "*.cpp" -not -path "./test/*" -not -name "pixelGameEngine.cpp") pixelGameEngine_wasm.o -o ${PROJECT_NAME}.html em++ -std=c++17 -O2 -sASSERTIONS -s ALLOW_MEMORY_GROWTH=1 -s MAXIMUM_MEMORY=4GB -s MAX_WEBGL_VERSION=2 -s MIN_WEBGL_VERSION=2 -s USE_SDL_MIXER=2 -s USE_LIBPNG=1 $(find . -type f -name "*.cpp" -not -path "./test/*" -not -name "pixelGameEngine.cpp") pixelGameEngine_wasm.o -o ${PROJECT_NAME}.html
fi fi
cp buildtemplate.html ${PROJECT_NAME}.html cp buildtemplate.html ${PROJECT_NAME}.html

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 35 KiB

@ -0,0 +1,26 @@
v 0 0 0
v 0 0 1
v 0 1 0
v 0 1 1
v 1 0 0
v 1 0 1
v 1 1 0
v 1 1 1
vt 0 0 #1
vt 0 1 #2
vt 1 0 #3
vt 1 1 #4
f 1/2 3/1 7/3 #South/0
f 1/2 7/3 5/4 #1
f 5/2 7/1 8/3 #East/2
f 5/2 8/3 6/4 #3
f 6/2 8/1 4/3 #North/4
f 6/2 4/3 2/4 #5
f 2/2 4/1 3/3 #West/6
f 2/2 3/3 1/4 #7
f 3/2 4/1 8/3 #Top/8
f 3/2 8/3 7/4 #9
f 6/2 2/1 1/3 #Bottom/10
f 6/2 1/3 5/4 #11

@ -1,49 +1,92 @@
#include "pixelGameEngine.h" #include "pixelGameEngine.h"
#define OLC_PGEX_GRAPHICS3D
#include "olcPGEX_Graphics3D.h"
using namespace olc; using namespace olc;
#define WIDTH 640 class Graphics3DPGEX_Example : public olc::PixelGameEngine
#define HEIGHT 480
class Example : public olc::PixelGameEngine
{ {
GFX3D::mesh cube;
GFX3D::PipeLine renderer;
GFX3D::vec3d vUp = {0,1,0};
GFX3D::vec3d vEye = {0,0,-4};
GFX3D::vec3d vLookDir = {0,0,1};
float fTheta=0;
Sprite*cubeTex;
public: public:
Example() Graphics3DPGEX_Example()
{ {
sAppName = "Example"; sAppName = "Graphics3D PGEX Example";
} }
public: public:
olc::Key last;
bool OnUserCreate() override bool OnUserCreate() override
{ {
std::cout<<"Test"<<std::endl; cube.tris =
ConsoleShow(olc::Key::A); {
// SOUTH
{ 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, WHITE },
{ 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, WHITE },
// EAST
{ 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, WHITE },
{ 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, WHITE },
// NORTH
{ 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, WHITE },
{ 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, WHITE },
// WEST
{ 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, WHITE },
{ 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, WHITE },
// TOP
{ 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, WHITE },
{ 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, WHITE },
// BOTTOM
{ 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, WHITE },
{ 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, WHITE },
};
olc::GFX3D::ConfigureDisplay();
renderer.SetProjection(90.0f, (float)ScreenHeight()/(float)ScreenWidth(), 0.1f, 1000.0f, 0.0f, 0.0f, ScreenWidth(), ScreenHeight());
return true; return true;
} }
bool OnUserUpdate(float fElapsedTime) override bool OnUserUpdate(float fElapsedTime) override
{ {
std::cout<<"Test 4"<<std::endl; fTheta+=fElapsedTime;
ConsoleCaptureStdOut(true);
std::cout<<"Test 2"<<std::endl; Clear(VERY_DARK_BLUE);
ConsoleCaptureStdOut(false);
std::cout<<"Test 3"<<std::endl; GFX3D::vec3d vLookTarget = GFX3D::Math::Vec_Add(vEye, vLookDir);
std::cout<<"A"<<std::endl;
Clear(BLACK); renderer.SetCamera(vEye, vLookTarget, vUp);
DrawString({0,0},std::to_string(last));
GFX3D::mat4x4 matRotateX=GFX3D::Math::Mat_MakeRotationX(fTheta);
GFX3D::mat4x4 matRotateZ=GFX3D::Math::Mat_MakeRotationZ(fTheta/3.0f);
GFX3D::mat4x4 matWorld=GFX3D::Math::Mat_MultiplyMatrix(matRotateX,matRotateZ);
renderer.SetTransform(matWorld);
renderer.SetTexture(cubeTex);
//renderer.Render(cube.tris, GFX3D::RENDER_WIRE);
renderer.Render(cube.tris);
return true; return true;
} }
bool OnUserDestroy()override{ bool OnUserDestroy()override{
return true; return true;
} }
void GetAnyKeyPress(olc::Key pressed)override{
last=pressed;
}
}; };
enum Direction{ enum Direction{
@ -60,8 +103,8 @@ struct Data{
int main() int main()
{ {
Example demo; Graphics3DPGEX_Example demo;
if (demo.Construct(640, 480, 4, 4)) if (demo.Construct(768, 480, 2, 2))
demo.Start(); demo.Start();
return 0; return 0;

@ -0,0 +1,836 @@
/*
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, ©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)
{
pge->FillTexturedTriangle({{float(x1),float(y1)},{float(x2),float(y2)},{float(x3),float(y3)}},{{u1,v1},{u2,v2},{u3,v3}},{WHITE,WHITE,WHITE},spr);
}
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

@ -315,11 +315,9 @@
+FillTexturedTriangle() - Software rasterizes a textured, coloured, triangle +FillTexturedTriangle() - Software rasterizes a textured, coloured, triangle
+FillTexturedPolygon() - Hijacks DecalStructure for configuration +FillTexturedPolygon() - Hijacks DecalStructure for configuration
+olc::vf2d arguments for Sprite::Sample() functions +olc::vf2d arguments for Sprite::Sample() functions
2.22: = Fix typo on dragged file buffers for unicode builds
SIG Updates: 2.23: Fixed Emscripten host sizing errors - Thanks Moros
The following additions are included in Sig's version of the PGE header (this is a sig version) Fixed v2d_generic.clamp() function
+GetAnyKey() - Returns when a key is activated
+GetAnyKeyPress() - Returns when a key is pressed down (and if a keyboard key, which key it was)
!! Apple Platforms will not see these updates immediately - Sorry, I dont have a mac to test... !! !! Apple Platforms will not see these updates immediately - Sorry, I dont have a mac to test... !!
!! Volunteers willing to help appreciated, though PRs are manually integrated with credit !! !! Volunteers willing to help appreciated, though PRs are manually integrated with credit !!
@ -976,8 +974,6 @@ namespace olc
virtual bool OnUserUpdate(float fElapsedTime); virtual bool OnUserUpdate(float fElapsedTime);
// Called once on application termination, so you can be one clean coder // Called once on application termination, so you can be one clean coder
virtual bool OnUserDestroy(); virtual bool OnUserDestroy();
virtual void GetAnyKey();
virtual void GetAnyKeyPress(olc::Key pressedKey);
// Called when a text entry is confirmed with "enter" key // Called when a text entry is confirmed with "enter" key
virtual void OnTextEntryComplete(const std::string& sText); virtual void OnTextEntryComplete(const std::string& sText);
@ -3682,9 +3678,6 @@ namespace olc
bool PixelGameEngine::OnUserDestroy() bool PixelGameEngine::OnUserDestroy()
{ return true; } { return true; }
void PixelGameEngine::GetAnyKey(){};
void PixelGameEngine::GetAnyKeyPress(olc::Key pressedKey){};
void PixelGameEngine::OnTextEntryComplete(const std::string& sText) { UNUSED(sText); } void PixelGameEngine::OnTextEntryComplete(const std::string& sText) { UNUSED(sText); }
bool PixelGameEngine::OnConsoleCommand(const std::string& sCommand) { UNUSED(sCommand); return false; } bool PixelGameEngine::OnConsoleCommand(const std::string& sCommand) { UNUSED(sCommand); return false; }
@ -3842,10 +3835,8 @@ namespace olc
platform->HandleSystemEvent(); platform->HandleSystemEvent();
// Compare hardware input states from previous frame // Compare hardware input states from previous frame
auto ScanHardware = [&](HWButton* pKeys, bool* pStateOld, bool* pStateNew, uint32_t nKeyCount,bool keyboard=true) auto ScanHardware = [&](HWButton* pKeys, bool* pStateOld, bool* pStateNew, uint32_t nKeyCount)
{ {
bool pressed=false;
int key;
for (uint32_t i = 0; i < nKeyCount; i++) for (uint32_t i = 0; i < nKeyCount; i++)
{ {
pKeys[i].bPressed = false; pKeys[i].bPressed = false;
@ -3854,8 +3845,6 @@ namespace olc
{ {
if (pStateNew[i]) if (pStateNew[i])
{ {
pressed=true;
key=i;
pKeys[i].bPressed = !pKeys[i].bHeld; pKeys[i].bPressed = !pKeys[i].bHeld;
pKeys[i].bHeld = true; pKeys[i].bHeld = true;
} }
@ -3864,18 +3853,13 @@ namespace olc
pKeys[i].bReleased = true; pKeys[i].bReleased = true;
pKeys[i].bHeld = false; pKeys[i].bHeld = false;
} }
GetAnyKey();
} }
pStateOld[i] = pStateNew[i]; pStateOld[i] = pStateNew[i];
} }
if (pressed) {
if (keyboard) {GetAnyKeyPress((olc::Key)key);}
else {GetAnyKeyPress(olc::Key::NONE);}
}
}; };
ScanHardware(pKeyboardState, pKeyOldState, pKeyNewState, 256); ScanHardware(pKeyboardState, pKeyOldState, pKeyNewState, 256);
ScanHardware(pMouseState, pMouseOldState, pMouseNewState, nMouseButtons, false); ScanHardware(pMouseState, pMouseOldState, pMouseNewState, nMouseButtons);
// Cache mouse coordinates so they remain consistent during frame // Cache mouse coordinates so they remain consistent during frame
vMousePos = vMousePosCache; vMousePos = vMousePosCache;
@ -6709,3 +6693,4 @@ namespace olc
// O------------------------------------------------------------------------------O // O------------------------------------------------------------------------------O
// | END OF OLC_PGE_APPLICATION | // | END OF OLC_PGE_APPLICATION |
// O------------------------------------------------------------------------------O // O------------------------------------------------------------------------------O

Binary file not shown.

2
sig

@ -1,4 +1,4 @@
export AUTO_UPDATE=true export AUTO_UPDATE=false
source utils/define.sh source utils/define.sh

Loading…
Cancel
Save