|
|
/*
|
|
|
olcPGEX_Graphics3D.h
|
|
|
|
|
|
+-------------------------------------------------------------+
|
|
|
| OneLoneCoder Pixel Game Engine Extension |
|
|
|
| 3D Rendering - v0.3 |
|
|
|
+-------------------------------------------------------------+
|
|
|
|
|
|
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!
|
|
|
|
|
|
Big Thanks to MaGetzUb for finding an OOB error, and joshinils
|
|
|
for pointing out sampling inaccuracy.
|
|
|
|
|
|
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>
|
|
|
#include <strstream>
|
|
|
#include <string>
|
|
|
#include <cstdint>
|
|
|
#undef min
|
|
|
#undef max
|
|
|
|
|
|
//#include <omp.h>
|
|
|
|
|
|
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[3];
|
|
|
};
|
|
|
|
|
|
struct mat4x4
|
|
|
{
|
|
|
float m[4][4] = { 0 };
|
|
|
};
|
|
|
|
|
|
struct mesh
|
|
|
{
|
|
|
std::vector<triangle> tris;
|
|
|
bool LoadOBJFile(std::string sFilename, bool bHasTexture = false);
|
|
|
};
|
|
|
|
|
|
/*class MipMap : public olc::Sprite
|
|
|
{
|
|
|
public:
|
|
|
MipMap();
|
|
|
MipMap(std::string sImageFile);
|
|
|
MipMap(std::string sImageFile, olc::ResourcePack *pack);
|
|
|
MipMap(int32_t w, int32_t h);
|
|
|
~MipMap();
|
|
|
|
|
|
public:
|
|
|
olc::rcode LoadFromFile(std::string sImageFile, olc::ResourcePack *pack = nullptr);
|
|
|
olc::rcode LoadFromPGESprFile(std::string sImageFile, olc::ResourcePack *pack = nullptr);
|
|
|
Pixel Sample(float x, float y, float z);
|
|
|
Pixel SampleBL(float u, float v, float z);
|
|
|
|
|
|
private:
|
|
|
int GenerateMipLevels();
|
|
|
std::vector<olc::Sprite> vecMipMaps;
|
|
|
|
|
|
};*/
|
|
|
|
|
|
class Math
|
|
|
{
|
|
|
public:
|
|
|
Math();
|
|
|
public:
|
|
|
static vec3d Mat_MultiplyVector(mat4x4 &m, vec3d &i);
|
|
|
static mat4x4 Mat_MultiplyMatrix(mat4x4 &m1, mat4x4 &m2);
|
|
|
static mat4x4 Mat_MakeIdentity();
|
|
|
static mat4x4 Mat_MakeRotationX(float fAngleRad);
|
|
|
static mat4x4 Mat_MakeRotationY(float fAngleRad);
|
|
|
static mat4x4 Mat_MakeRotationZ(float fAngleRad);
|
|
|
static mat4x4 Mat_MakeScale(float x, float y, float z);
|
|
|
static mat4x4 Mat_MakeTranslation(float x, float y, float z);
|
|
|
static mat4x4 Mat_MakeProjection(float fFovDegrees, float fAspectRatio, float fNear, float fFar);
|
|
|
static mat4x4 Mat_PointAt(vec3d &pos, vec3d &target, vec3d &up);
|
|
|
static mat4x4 Mat_QuickInverse(mat4x4 &m); // Only for Rotation/Translation Matrices
|
|
|
static mat4x4 Mat_Inverse(olc::GFX3D::mat4x4 &m);
|
|
|
|
|
|
static vec3d Vec_Add(vec3d &v1, vec3d &v2);
|
|
|
static vec3d Vec_Sub(vec3d &v1, vec3d &v2);
|
|
|
static vec3d Vec_Mul(vec3d &v1, float k);
|
|
|
static vec3d Vec_Div(vec3d &v1, float k);
|
|
|
static float Vec_DotProduct(vec3d &v1, vec3d &v2);
|
|
|
static float Vec_Length(vec3d &v);
|
|
|
static vec3d Vec_Normalise(vec3d &v);
|
|
|
static vec3d Vec_CrossProduct(vec3d &v1, vec3d &v2);
|
|
|
static vec3d Vec_IntersectPlane(vec3d &plane_p, vec3d &plane_n, vec3d &lineStart, vec3d &lineEnd, float &t);
|
|
|
|
|
|
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,
|
|
|
RENDER_LIGHTS = 0x40,
|
|
|
};
|
|
|
|
|
|
enum LIGHTS
|
|
|
{
|
|
|
LIGHT_DISABLED,
|
|
|
LIGHT_AMBIENT,
|
|
|
LIGHT_DIRECTIONAL,
|
|
|
LIGHT_POINT
|
|
|
};
|
|
|
|
|
|
|
|
|
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 SetMipMapTexture(olc::GFX3D::MipMap *texture);
|
|
|
void SetLightSource(uint32_t nSlot, uint32_t nType, olc::Pixel col, olc::GFX3D::vec3d pos, olc::GFX3D::vec3d dir = { 0.0f, 0.0f, 1.0f }, float fParam = 0.0f);
|
|
|
uint32_t Render(std::vector<olc::GFX3D::triangle> &triangles, uint32_t flags = RENDER_CULL_CW | RENDER_TEXTURED | RENDER_DEPTH);
|
|
|
uint32_t Render(std::vector<olc::GFX3D::triangle> &triangles, uint32_t flags, int nOffset, int nCount);
|
|
|
uint32_t RenderLine(olc::GFX3D::vec3d &p1, olc::GFX3D::vec3d &p2, olc::Pixel col = olc::WHITE);
|
|
|
uint32_t RenderCircleXZ(olc::GFX3D::vec3d &p1, float r, olc::Pixel col = olc::WHITE);
|
|
|
|
|
|
private:
|
|
|
olc::GFX3D::mat4x4 matProj;
|
|
|
olc::GFX3D::mat4x4 matView;
|
|
|
olc::GFX3D::mat4x4 matWorld;
|
|
|
olc::Sprite *sprTexture;
|
|
|
//olc::GFX3D::MipMap *sprMipMap;
|
|
|
//bool bUseMipMap;
|
|
|
float fViewX;
|
|
|
float fViewY;
|
|
|
float fViewW;
|
|
|
float fViewH;
|
|
|
|
|
|
struct sLight
|
|
|
{
|
|
|
uint32_t type;
|
|
|
olc::GFX3D::vec3d pos;
|
|
|
olc::GFX3D::vec3d dir;
|
|
|
olc::Pixel col;
|
|
|
float param;
|
|
|
} lights[4];
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
//static const int RF_TEXTURE = 0x00000001;
|
|
|
//static const int RF_ = 0x00000002;
|
|
|
|
|
|
static void ConfigureDisplay();
|
|
|
static void ClearDepth();
|
|
|
static void AddTriangleToScene(olc::GFX3D::triangle &tri);
|
|
|
static void RenderScene();
|
|
|
|
|
|
static void DrawTriangleFlat(olc::GFX3D::triangle &tri);
|
|
|
static void DrawTriangleWire(olc::GFX3D::triangle &tri, olc::Pixel col = olc::WHITE);
|
|
|
static void DrawTriangleTex(olc::GFX3D::triangle &tri, olc::Sprite* spr);
|
|
|
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);
|
|
|
|
|
|
static void RasterTriangle(int x1, int y1, float u1, float v1, float w1, olc::Pixel c1,
|
|
|
int x2, int y2, float u2, float v2, float w2, olc::Pixel c2,
|
|
|
int x3, int y3, float u3, float v3, float w3, olc::Pixel c3,
|
|
|
olc::Sprite* spr,
|
|
|
uint32_t nFlags);
|
|
|
|
|
|
// Draws a sprite with the transform applied
|
|
|
//inline static void DrawSprite(olc::Sprite *sprite, olc::GFX2D::Transform2D &transform);
|
|
|
|
|
|
private:
|
|
|
static float* m_DepthBuffer;
|
|
|
};
|
|
|
}
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
#ifdef OLC_PGEX_GRAPHICS3D
|
|
|
#undef OLC_PGEX_GRAPHICS3D
|
|
|
|
|
|
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[0] = in_tri.col[0];
|
|
|
out_tri1.col[1] = in_tri.col[1];
|
|
|
out_tri1.col[2] = in_tri.col[2];
|
|
|
|
|
|
// 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[0] = in_tri.col[0];
|
|
|
out_tri2.col[0] = in_tri.col[0];
|
|
|
out_tri1.col[1] = in_tri.col[1];
|
|
|
out_tri2.col[1] = in_tri.col[1];
|
|
|
out_tri1.col[2] = in_tri.col[2];
|
|
|
out_tri2.col[2] = in_tri.col[2];
|
|
|
|
|
|
// 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[0]);
|
|
|
}
|
|
|
|
|
|
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])
|
|
|
{
|
|
|
/*if (bMipMap)
|
|
|
pge->Draw(j, i, ((olc::GFX3D::MipMap*)spr)->Sample(tex_u / tex_w, tex_v / tex_w, tex_w));
|
|
|
else*/
|
|
|
if(pge->Draw(j, i, spr != nullptr ? spr->Sample(tex_u / tex_w, tex_v / tex_w) : olc::GREY))
|
|
|
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])
|
|
|
{
|
|
|
/*if(bMipMap)
|
|
|
pge->Draw(j, i, ((olc::GFX3D::MipMap*)spr)->Sample(tex_u / tex_w, tex_v / tex_w, tex_w));
|
|
|
else*/
|
|
|
if(pge->Draw(j, i, spr != nullptr ? spr->Sample(tex_u / tex_w, tex_v / tex_w) : olc::GREY))
|
|
|
m_DepthBuffer[i*pge->ScreenWidth() + j] = tex_w;
|
|
|
}
|
|
|
t += tstep;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
void GFX3D::DrawTriangleTex(olc::GFX3D::triangle &tri, olc::Sprite* 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));
|
|
|
}
|
|
|
|
|
|
bool GFX3D::mesh::LoadOBJFile(std::string sFilename, bool bHasTexture)
|
|
|
{
|
|
|
std::ifstream f(sFilename);
|
|
|
if (!f.is_open()) return false;
|
|
|
|
|
|
// Local cache of verts
|
|
|
std::vector<vec3d> verts;
|
|
|
std::vector<vec3d> norms;
|
|
|
std::vector<vec2d> texs;
|
|
|
|
|
|
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.x >> v.y;
|
|
|
//v.x = 1.0f - v.x;
|
|
|
v.y = 1.0f - v.y;
|
|
|
texs.push_back(v);
|
|
|
}
|
|
|
else if (line[1] == 'n')
|
|
|
{
|
|
|
vec3d v;
|
|
|
s >> junk >> junk >> v.x >> v.y >> v.z;
|
|
|
norms.push_back(v);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
vec3d v;
|
|
|
s >> junk >> v.x >> v.y >> v.z;
|
|
|
verts.push_back(v);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
/*if (!bHasTexture)
|
|
|
{
|
|
|
if (line[0] == 'f')
|
|
|
{
|
|
|
int f[3];
|
|
|
s >> junk >> f[0] >> f[1] >> f[2];
|
|
|
tris.push_back({ verts[f[0] - 1], verts[f[1] - 1], verts[f[2] - 1] });
|
|
|
}
|
|
|
}
|
|
|
else*/
|
|
|
{
|
|
|
if (line[0] == 'f')
|
|
|
{
|
|
|
s >> junk;
|
|
|
|
|
|
std::string tokens[9];
|
|
|
int nTokenCount = -1;
|
|
|
while (!s.eof())
|
|
|
{
|
|
|
char c = s.get();
|
|
|
if (c == ' ' || c == '/')
|
|
|
{
|
|
|
if (tokens[nTokenCount].size() > 0)
|
|
|
{
|
|
|
nTokenCount++;
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
tokens[nTokenCount].append(1, c);
|
|
|
}
|
|
|
|
|
|
tokens[nTokenCount].pop_back();
|
|
|
|
|
|
int stride = 1;
|
|
|
if (!texs.empty()) stride++;
|
|
|
if (!norms.empty()) stride++;
|
|
|
|
|
|
if (!texs.empty())
|
|
|
{
|
|
|
tris.push_back({
|
|
|
verts[stoi(tokens[0 * stride]) - 1],
|
|
|
verts[stoi(tokens[1 * stride]) - 1],
|
|
|
verts[stoi(tokens[2 * stride]) - 1],
|
|
|
texs[stoi(tokens[0 * stride + 1]) - 1],
|
|
|
texs[stoi(tokens[1 * stride + 1]) - 1],
|
|
|
texs[stoi(tokens[2 * stride + 1]) - 1],
|
|
|
olc::WHITE, olc::WHITE, olc::WHITE});
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
tris.push_back({
|
|
|
verts[stoi(tokens[0 * stride]) - 1],
|
|
|
verts[stoi(tokens[1 * stride]) - 1],
|
|
|
verts[stoi(tokens[2 * stride]) - 1],
|
|
|
olc::GFX3D::vec2d{0,0,0},
|
|
|
olc::GFX3D::vec2d{0,0,0},
|
|
|
olc::GFX3D::vec2d{0,0,0},
|
|
|
olc::WHITE, olc::WHITE, olc::WHITE });
|
|
|
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
|
|
|
GFX3D::PipeLine::PipeLine()
|
|
|
{
|
|
|
//bUseMipMap = false;
|
|
|
}
|
|
|
|
|
|
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;
|
|
|
//bUseMipMap = false;
|
|
|
}
|
|
|
|
|
|
/*void GFX3D::PipeLine::SetMipMapTexture(olc::GFX3D::MipMap *texture)
|
|
|
{
|
|
|
sprMipMap = texture;
|
|
|
bUseMipMap = true;
|
|
|
}*/
|
|
|
|
|
|
void GFX3D::PipeLine::SetLightSource(uint32_t nSlot, uint32_t nType, olc::Pixel col, olc::GFX3D::vec3d pos, olc::GFX3D::vec3d dir, float fParam)
|
|
|
{
|
|
|
if (nSlot < 4)
|
|
|
{
|
|
|
lights[nSlot].type = nType;
|
|
|
lights[nSlot].pos = pos;
|
|
|
lights[nSlot].dir = dir;
|
|
|
lights[nSlot].col = col;
|
|
|
lights[nSlot].param = fParam;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
uint32_t GFX3D::PipeLine::Render(std::vector<olc::GFX3D::triangle> &triangles, uint32_t flags)
|
|
|
{
|
|
|
return Render(triangles, flags, 0, triangles.size());
|
|
|
}
|
|
|
|
|
|
uint32_t GFX3D::PipeLine::RenderLine(olc::GFX3D::vec3d &p1, olc::GFX3D::vec3d &p2, olc::Pixel col)
|
|
|
{
|
|
|
// Coordinates are assumed to be in world space
|
|
|
olc::GFX3D::vec3d t1, t2;
|
|
|
|
|
|
// Transform into view
|
|
|
t1 = GFX3D::Math::Mat_MultiplyVector(matView, p1);
|
|
|
t2 = GFX3D::Math::Mat_MultiplyVector(matView, p2);
|
|
|
|
|
|
// Project onto screen
|
|
|
t1 = GFX3D::Math::Mat_MultiplyVector(matProj, t1);
|
|
|
t2 = GFX3D::Math::Mat_MultiplyVector(matProj, t2);
|
|
|
|
|
|
// Project
|
|
|
t1.x = t1.x / t1.w;
|
|
|
t1.y = t1.y / t1.w;
|
|
|
t1.z = t1.z / t1.w;
|
|
|
|
|
|
t2.x = t2.x / t2.w;
|
|
|
t2.y = t2.y / t2.w;
|
|
|
t2.z = t2.z / t2.w;
|
|
|
|
|
|
vec3d vOffsetView = { 1,1,0 };
|
|
|
t1 = Math::Vec_Add(t1, vOffsetView);
|
|
|
t2 = Math::Vec_Add(t2, vOffsetView);
|
|
|
|
|
|
t1.x *= 0.5f * fViewW;
|
|
|
t1.y *= 0.5f * fViewH;
|
|
|
t2.x *= 0.5f * fViewW;
|
|
|
t2.y *= 0.5f * fViewH;
|
|
|
|
|
|
vOffsetView = { fViewX,fViewY,0 };
|
|
|
t1 = Math::Vec_Add(t1, vOffsetView);
|
|
|
t2 = Math::Vec_Add(t2, vOffsetView);
|
|
|
|
|
|
pge->DrawLine(t1.x, t1.y, t2.x, t2.y, col);
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
uint32_t GFX3D::PipeLine::RenderCircleXZ(olc::GFX3D::vec3d &p1, float r, olc::Pixel col)
|
|
|
{
|
|
|
// Coordinates are assumed to be in world space
|
|
|
olc::GFX3D::vec3d t1;
|
|
|
olc::GFX3D::vec3d t2 = { p1.x + r, p1.y, p1.z };
|
|
|
|
|
|
// Transform into view
|
|
|
t1 = GFX3D::Math::Mat_MultiplyVector(matView, p1);
|
|
|
t2 = GFX3D::Math::Mat_MultiplyVector(matView, t2);
|
|
|
|
|
|
// Project onto screen
|
|
|
t1 = GFX3D::Math::Mat_MultiplyVector(matProj, t1);
|
|
|
t2 = GFX3D::Math::Mat_MultiplyVector(matProj, t2);
|
|
|
|
|
|
// Project
|
|
|
t1.x = t1.x / t1.w;
|
|
|
t1.y = t1.y / t1.w;
|
|
|
t1.z = t1.z / t1.w;
|
|
|
|
|
|
t2.x = t2.x / t2.w;
|
|
|
t2.y = t2.y / t2.w;
|
|
|
t2.z = t2.z / t2.w;
|
|
|
|
|
|
vec3d vOffsetView = { 1,1,0 };
|
|
|
t1 = Math::Vec_Add(t1, vOffsetView);
|
|
|
t2 = Math::Vec_Add(t2, vOffsetView);
|
|
|
|
|
|
t1.x *= 0.5f * fViewW;
|
|
|
t1.y *= 0.5f * fViewH;
|
|
|
t2.x *= 0.5f * fViewW;
|
|
|
t2.y *= 0.5f * fViewH;
|
|
|
|
|
|
vOffsetView = { fViewX,fViewY,0 };
|
|
|
t1 = Math::Vec_Add(t1, vOffsetView);
|
|
|
t2 = Math::Vec_Add(t2, vOffsetView);
|
|
|
|
|
|
pge->FillCircle(t1.x, t1.y, fabs(t2.x - t1.x), col);
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
uint32_t GFX3D::PipeLine::Render(std::vector<olc::GFX3D::triangle> &triangles, uint32_t flags, int nOffset, int nCount)
|
|
|
{
|
|
|
// 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)
|
|
|
// omp_set_dynamic(0);
|
|
|
// omp_set_num_threads(4);
|
|
|
//#pragma omp parallel for schedule(static)
|
|
|
for(int tx = nOffset; tx < nOffset+nCount; tx++)
|
|
|
{
|
|
|
GFX3D::triangle &tri = triangles[tx];
|
|
|
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!
|
|
|
|
|
|
// Dont forget vertex colours
|
|
|
triTransformed.col[0] = tri.col[0];
|
|
|
triTransformed.col[1] = tri.col[1];
|
|
|
triTransformed.col[2] = tri.col[2];
|
|
|
|
|
|
// 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
|
|
|
if (flags & RENDER_LIGHTS)
|
|
|
{
|
|
|
olc::Pixel ambient_clamp = { 0,0,0 };
|
|
|
olc::Pixel light_combined = { 0,0,0 };
|
|
|
uint32_t nLightSources = 0;
|
|
|
float nLightR = 0, nLightG = 0, nLightB = 0;
|
|
|
|
|
|
for (int i = 0; i < 4; i++)
|
|
|
{
|
|
|
switch (lights[i].type)
|
|
|
{
|
|
|
case LIGHT_DISABLED:
|
|
|
break;
|
|
|
case LIGHT_AMBIENT:
|
|
|
ambient_clamp = lights[i].col;
|
|
|
break;
|
|
|
case LIGHT_DIRECTIONAL:
|
|
|
{
|
|
|
nLightSources++;
|
|
|
GFX3D::vec3d light_dir = GFX3D::Math::Vec_Normalise(lights[i].dir);
|
|
|
float light = GFX3D::Math::Vec_DotProduct(light_dir, normal);
|
|
|
if (light > 0)
|
|
|
{
|
|
|
int j = 0;
|
|
|
}
|
|
|
|
|
|
light = std::max(light, 0.0f);
|
|
|
nLightR += light * (lights[i].col.r/255.0f);
|
|
|
nLightG += light * (lights[i].col.g/255.0f);
|
|
|
nLightB += light * (lights[i].col.b/255.0f);
|
|
|
}
|
|
|
break;
|
|
|
case LIGHT_POINT:
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
//nLightR /= nLightSources;
|
|
|
//nLightG /= nLightSources;
|
|
|
//nLightB /= nLightSources;
|
|
|
|
|
|
nLightR = std::max(nLightR, ambient_clamp.r / 255.0f);
|
|
|
nLightG = std::max(nLightG, ambient_clamp.g / 255.0f);
|
|
|
nLightB = std::max(nLightB, ambient_clamp.b / 255.0f);
|
|
|
|
|
|
triTransformed.col[0] = olc::Pixel(nLightR * triTransformed.col[0].r, nLightG * triTransformed.col[0].g, nLightB * triTransformed.col[0].b);
|
|
|
triTransformed.col[1] = olc::Pixel(nLightR * triTransformed.col[1].r, nLightG * triTransformed.col[1].g, nLightB * triTransformed.col[1].b);
|
|
|
triTransformed.col[2] = olc::Pixel(nLightR * triTransformed.col[2].r, nLightG * triTransformed.col[2].g, nLightB * triTransformed.col[2].b);
|
|
|
|
|
|
|
|
|
|
|
|
/*GFX3D::vec3d light_dir = { 1,1,1 };
|
|
|
light_dir = GFX3D::Math::Vec_Normalise(light_dir);
|
|
|
float light = GFX3D::Math::Vec_DotProduct(light_dir, normal);
|
|
|
if (light < 0) light = 0;
|
|
|
triTransformed.col[0] = olc::Pixel(light * 255.0f, light * 255.0f, light * 255.0f);
|
|
|
triTransformed.col[1] = olc::Pixel(light * 255.0f, light * 255.0f, light * 255.0f);
|
|
|
triTransformed.col[2] = olc::Pixel(light * 255.0f, light * 255.0f, light * 255.0f);*/
|
|
|
}
|
|
|
//else
|
|
|
// triTransformed.col = olc::WHITE;
|
|
|
|
|
|
// Clip triangle against near plane
|
|
|
int nClippedTriangles = 0;
|
|
|
GFX3D::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
|
|
|
GFX3D::triangle sclipped[2];
|
|
|
std::list<GFX3D::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);*/
|
|
|
|
|
|
// RasterTriangle(
|
|
|
// triRaster.p[0].x, triRaster.p[0].y, triRaster.t[0].x, triRaster.t[0].y, triRaster.t[0].z, triRaster.col,
|
|
|
// triRaster.p[1].x, triRaster.p[1].y, triRaster.t[1].x, triRaster.t[1].y, triRaster.t[1].z, triRaster.col,
|
|
|
// triRaster.p[2].x, triRaster.p[2].y, triRaster.t[2].x, triRaster.t[2].y, triRaster.t[2].z, triRaster.col,
|
|
|
// sprTexture, nFlags);
|
|
|
|
|
|
//}
|
|
|
|
|
|
if (flags & RENDER_WIRE)
|
|
|
{
|
|
|
DrawTriangleWire(triRaster, olc::RED);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
RasterTriangle(
|
|
|
triRaster.p[0].x, triRaster.p[0].y, triRaster.t[0].x, triRaster.t[0].y, triRaster.t[0].z, triRaster.col[0],
|
|
|
triRaster.p[1].x, triRaster.p[1].y, triRaster.t[1].x, triRaster.t[1].y, triRaster.t[1].z, triRaster.col[1],
|
|
|
triRaster.p[2].x, triRaster.p[2].y, triRaster.t[2].x, triRaster.t[2].y, triRaster.t[2].z, triRaster.col[2],
|
|
|
sprTexture, flags);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
nTriangleDrawnCount++;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return nTriangleDrawnCount;
|
|
|
}
|
|
|
|
|
|
void GFX3D::RasterTriangle(int x1, int y1, float u1, float v1, float w1, olc::Pixel c1,
|
|
|
int x2, int y2, float u2, float v2, float w2, olc::Pixel c2,
|
|
|
int x3, int y3, float u3, float v3, float w3, olc::Pixel c3,
|
|
|
olc::Sprite* spr,
|
|
|
uint32_t nFlags)
|
|
|
|
|
|
{
|
|
|
if (y2 < y1)
|
|
|
{
|
|
|
std::swap(y1, y2); std::swap(x1, x2); std::swap(u1, u2); std::swap(v1, v2); std::swap(w1, w2); std::swap(c1, c2);
|
|
|
}
|
|
|
|
|
|
if (y3 < y1)
|
|
|
{
|
|
|
std::swap(y1, y3); std::swap(x1, x3); std::swap(u1, u3); std::swap(v1, v3); std::swap(w1, w3); std::swap(c1, c3);
|
|
|
}
|
|
|
|
|
|
if (y3 < y2)
|
|
|
{
|
|
|
std::swap(y2, y3); std::swap(x2, x3); std::swap(u2, u3); std::swap(v2, v3); std::swap(w2, w3); std::swap(c2, c3);
|
|
|
}
|
|
|
|
|
|
int dy1 = y2 - y1;
|
|
|
int dx1 = x2 - x1;
|
|
|
float dv1 = v2 - v1;
|
|
|
float du1 = u2 - u1;
|
|
|
float dw1 = w2 - w1;
|
|
|
int dcr1 = c2.r - c1.r;
|
|
|
int dcg1 = c2.g - c1.g;
|
|
|
int dcb1 = c2.b - c1.b;
|
|
|
int dca1 = c2.a - c1.a;
|
|
|
|
|
|
int dy2 = y3 - y1;
|
|
|
int dx2 = x3 - x1;
|
|
|
float dv2 = v3 - v1;
|
|
|
float du2 = u3 - u1;
|
|
|
float dw2 = w3 - w1;
|
|
|
int dcr2 = c3.r - c1.r;
|
|
|
int dcg2 = c3.g - c1.g;
|
|
|
int dcb2 = c3.b - c1.b;
|
|
|
int dca2 = c3.a - c1.a;
|
|
|
|
|
|
float tex_u, tex_v, tex_w;
|
|
|
float col_r, col_g, col_b, col_a;
|
|
|
|
|
|
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,
|
|
|
dcr1_step = 0, dcr2_step = 0,
|
|
|
dcg1_step = 0, dcg2_step = 0,
|
|
|
dcb1_step = 0, dcb2_step = 0,
|
|
|
dca1_step = 0, dca2_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) dcr1_step = dcr1 / (float)abs(dy1);
|
|
|
if (dy1) dcg1_step = dcg1 / (float)abs(dy1);
|
|
|
if (dy1) dcb1_step = dcb1 / (float)abs(dy1);
|
|
|
if (dy1) dca1_step = dca1 / (float)abs(dy1);
|
|
|
|
|
|
if (dy2) dcr2_step = dcr2 / (float)abs(dy2);
|
|
|
if (dy2) dcg2_step = dcg2 / (float)abs(dy2);
|
|
|
if (dy2) dcb2_step = dcb2 / (float)abs(dy2);
|
|
|
if (dy2) dca2_step = dca2 / (float)abs(dy2);
|
|
|
|
|
|
float pixel_r = 0.0f;
|
|
|
float pixel_g = 0.0f;
|
|
|
float pixel_b = 0.0f;
|
|
|
float pixel_a = 1.0f;
|
|
|
|
|
|
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;
|
|
|
|
|
|
float col_sr = c1.r + (float)(i - y1) * dcr1_step;
|
|
|
float col_sg = c1.g + (float)(i - y1) * dcg1_step;
|
|
|
float col_sb = c1.b + (float)(i - y1) * dcb1_step;
|
|
|
float col_sa = c1.a + (float)(i - y1) * dca1_step;
|
|
|
|
|
|
float col_er = c1.r + (float)(i - y1) * dcr2_step;
|
|
|
float col_eg = c1.g + (float)(i - y1) * dcg2_step;
|
|
|
float col_eb = c1.b + (float)(i - y1) * dcb2_step;
|
|
|
float col_ea = c1.a + (float)(i - y1) * dca2_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);
|
|
|
std::swap(col_sr, col_er);
|
|
|
std::swap(col_sg, col_eg);
|
|
|
std::swap(col_sb, col_eb);
|
|
|
std::swap(col_sa, col_ea);
|
|
|
}
|
|
|
|
|
|
tex_u = tex_su;
|
|
|
tex_v = tex_sv;
|
|
|
tex_w = tex_sw;
|
|
|
col_r = col_sr;
|
|
|
col_g = col_sg;
|
|
|
col_b = col_sb;
|
|
|
col_a = col_sa;
|
|
|
|
|
|
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;
|
|
|
col_r = (1.0f - t) * col_sr + t * col_er;
|
|
|
col_g = (1.0f - t) * col_sg + t * col_eg;
|
|
|
col_b = (1.0f - t) * col_sb + t * col_eb;
|
|
|
col_a = (1.0f - t) * col_sa + t * col_ea;
|
|
|
|
|
|
pixel_r = col_r;
|
|
|
pixel_g = col_g;
|
|
|
pixel_b = col_b;
|
|
|
pixel_a = col_a;
|
|
|
|
|
|
if (nFlags & GFX3D::RENDER_TEXTURED)
|
|
|
{
|
|
|
if (spr != nullptr)
|
|
|
{
|
|
|
olc::Pixel sample = spr->Sample(tex_u / tex_w, tex_v / tex_w);
|
|
|
pixel_r *= sample.r / 255.0f;
|
|
|
pixel_g *= sample.g / 255.0f;
|
|
|
pixel_b *= sample.b / 255.0f;
|
|
|
pixel_a *= sample.a / 255.0f;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if (nFlags & GFX3D::RENDER_DEPTH)
|
|
|
{
|
|
|
if (tex_w > m_DepthBuffer[i*pge->ScreenWidth() + j])
|
|
|
if (pge->Draw(j, i, olc::Pixel(pixel_r * 1.0f, pixel_g * 1.0f, pixel_b * 1.0f, pixel_a * 1.0f)))
|
|
|
m_DepthBuffer[i*pge->ScreenWidth() + j] = tex_w;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
pge->Draw(j, i, olc::Pixel(pixel_r * 1.0f, pixel_g * 1.0f, pixel_b * 1.0f, pixel_a * 1.0f));
|
|
|
}
|
|
|
|
|
|
t += tstep;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
dy1 = y3 - y2;
|
|
|
dx1 = x3 - x2;
|
|
|
dv1 = v3 - v2;
|
|
|
du1 = u3 - u2;
|
|
|
dw1 = w3 - w2;
|
|
|
dcr1 = c3.r - c2.r;
|
|
|
dcg1 = c3.g - c2.g;
|
|
|
dcb1 = c3.b - c2.b;
|
|
|
dca1 = c3.a - c2.a;
|
|
|
|
|
|
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);
|
|
|
|
|
|
dcr1_step = 0; dcg1_step = 0; dcb1_step = 0; dca1_step = 0;
|
|
|
if (dy1) dcr1_step = dcr1 / (float)abs(dy1);
|
|
|
if (dy1) dcg1_step = dcg1 / (float)abs(dy1);
|
|
|
if (dy1) dcb1_step = dcb1 / (float)abs(dy1);
|
|
|
if (dy1) dca1_step = dca1 / (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;
|
|
|
|
|
|
float col_sr = c2.r + (float)(i - y2) * dcr1_step;
|
|
|
float col_sg = c2.g + (float)(i - y2) * dcg1_step;
|
|
|
float col_sb = c2.b + (float)(i - y2) * dcb1_step;
|
|
|
float col_sa = c2.a + (float)(i - y2) * dca1_step;
|
|
|
|
|
|
float col_er = c1.r + (float)(i - y1) * dcr2_step;
|
|
|
float col_eg = c1.g + (float)(i - y1) * dcg2_step;
|
|
|
float col_eb = c1.b + (float)(i - y1) * dcb2_step;
|
|
|
float col_ea = c1.a + (float)(i - y1) * dca2_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);
|
|
|
std::swap(col_sr, col_er);
|
|
|
std::swap(col_sg, col_eg);
|
|
|
std::swap(col_sb, col_eb);
|
|
|
std::swap(col_sa, col_ea);
|
|
|
}
|
|
|
|
|
|
tex_u = tex_su;
|
|
|
tex_v = tex_sv;
|
|
|
tex_w = tex_sw;
|
|
|
col_r = col_sr;
|
|
|
col_g = col_sg;
|
|
|
col_b = col_sb;
|
|
|
col_a = col_sa;
|
|
|
|
|
|
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;
|
|
|
col_r = (1.0f - t) * col_sr + t * col_er;
|
|
|
col_g = (1.0f - t) * col_sg + t * col_eg;
|
|
|
col_b = (1.0f - t) * col_sb + t * col_eb;
|
|
|
col_a = (1.0f - t) * col_sa + t * col_ea;
|
|
|
|
|
|
pixel_r = col_r;
|
|
|
pixel_g = col_g;
|
|
|
pixel_b = col_b;
|
|
|
pixel_a = col_a;
|
|
|
|
|
|
if (nFlags & GFX3D::RENDER_TEXTURED)
|
|
|
{
|
|
|
if (spr != nullptr)
|
|
|
{
|
|
|
olc::Pixel sample = spr->Sample(tex_u / tex_w, tex_v / tex_w);
|
|
|
pixel_r *= sample.r / 255.0f;
|
|
|
pixel_g *= sample.g / 255.0f;
|
|
|
pixel_b *= sample.b / 255.0f;
|
|
|
pixel_a *= sample.a / 255.0f;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if (nFlags & GFX3D::RENDER_DEPTH)
|
|
|
{
|
|
|
if (tex_w > m_DepthBuffer[i*pge->ScreenWidth() + j])
|
|
|
if (pge->Draw(j, i, olc::Pixel(pixel_r * 1.0f, pixel_g * 1.0f, pixel_b * 1.0f, pixel_a * 1.0f)))
|
|
|
m_DepthBuffer[i*pge->ScreenWidth() + j] = tex_w;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
pge->Draw(j, i, olc::Pixel(pixel_r * 1.0f, pixel_g * 1.0f, pixel_b * 1.0f, pixel_a * 1.0f));
|
|
|
}
|
|
|
|
|
|
t += tstep;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//GFX3D::MipMap::MipMap() : olc::Sprite(){}
|
|
|
//GFX3D::MipMap::MipMap(std::string sImageFile) : olc::Sprite(sImageFile)
|
|
|
//{
|
|
|
// GenerateMipLevels();
|
|
|
//}
|
|
|
//GFX3D::MipMap::MipMap(std::string sImageFile, olc::ResourcePack *pack) : olc::Sprite(sImageFile, pack){}
|
|
|
//GFX3D::MipMap::MipMap(int32_t w, int32_t h) : olc::Sprite(w, h) {}
|
|
|
|
|
|
//int GFX3D::MipMap::GenerateMipLevels()
|
|
|
//{
|
|
|
// int nLevelsW = 0;
|
|
|
// int nLevelsH = 0;
|
|
|
// int w = width;
|
|
|
// int h = height;
|
|
|
// while (w > 1) { w >>= 1; nLevelsW++; }
|
|
|
// while (h > 1) { h >>= 1; nLevelsH++; }
|
|
|
|
|
|
// int nLevels = std::min(nLevelsW, nLevelsH);
|
|
|
|
|
|
// w = width >> 1;
|
|
|
// h = height >> 1;
|
|
|
|
|
|
// vecMipMaps.emplace_back(w, h); // Level 0
|
|
|
// memcpy(vecMipMaps[0].GetData(), GetData(), w*h*sizeof(uint32_t));
|
|
|
|
|
|
// for (int i = 1; i < nLevels; i++)
|
|
|
// {
|
|
|
// vecMipMaps.emplace_back(w, h);
|
|
|
// pge->SetDrawTarget(&vecMipMaps[i]);
|
|
|
// for (int x = 0; x < w; x++)
|
|
|
// for (int y = 0; y < h; y++)
|
|
|
// pge->Draw(x, y, vecMipMaps[i-1].SampleBL((float)x / (float)w, (float)y / (float)h));
|
|
|
// w >>= 1; h >>= 1;
|
|
|
// }
|
|
|
|
|
|
// pge->SetDrawTarget(nullptr);
|
|
|
// return nLevels;
|
|
|
//}
|
|
|
//
|
|
|
//olc::rcode GFX3D::MipMap::LoadFromFile(std::string sImageFile, olc::ResourcePack *pack)
|
|
|
//{
|
|
|
// olc::rcode r = olc::Sprite::LoadFromFile(sImageFile, pack);
|
|
|
// if (r == olc::FAIL) return r;
|
|
|
// GenerateMipLevels();
|
|
|
// return r;
|
|
|
//}
|
|
|
|
|
|
//olc::rcode GFX3D::MipMap::LoadFromPGESprFile(std::string sImageFile, olc::ResourcePack *pack)
|
|
|
//{
|
|
|
// olc::rcode r = olc::Sprite::LoadFromPGESprFile(sImageFile, pack);
|
|
|
// if (r == olc::FAIL) return r;
|
|
|
// GenerateMipLevels();
|
|
|
// return r;
|
|
|
//}
|
|
|
//
|
|
|
//olc::Pixel GFX3D::MipMap::Sample(float x, float y, float z)
|
|
|
//{
|
|
|
// int nLevel = (int)(z * (float)vecMipMaps.size());
|
|
|
// return vecMipMaps[nLevel].Sample(x, y);
|
|
|
//}
|
|
|
|
|
|
//olc::Pixel GFX3D::MipMap::SampleBL(float u, float v, float z);
|
|
|
|
|
|
}
|
|
|
|
|
|
#endif |