2018-09-06 01:53:11 +00:00
|
|
|
#include "color.h"
|
2018-09-08 01:30:02 +00:00
|
|
|
#include "engine.h"
|
2018-09-06 01:53:11 +00:00
|
|
|
#include "geometry.h"
|
|
|
|
#include "util.h"
|
2018-09-08 01:30:02 +00:00
|
|
|
#include <cstdio>
|
2018-09-06 01:53:11 +00:00
|
|
|
|
|
|
|
|
|
|
|
// MACROS
|
|
|
|
#define DrawPixel(buffer, width, color, x, y)\
|
|
|
|
{\
|
|
|
|
buffer[width * y + x] = color;\
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-09-08 01:30:02 +00:00
|
|
|
// STRUCTURES
|
|
|
|
struct BoundingBox
|
2018-09-06 01:53:11 +00:00
|
|
|
{
|
2018-09-08 01:30:02 +00:00
|
|
|
BoundingBox(Point &v0, Point &v1, Point &v2)
|
2018-09-06 01:53:11 +00:00
|
|
|
{
|
2018-09-08 01:30:02 +00:00
|
|
|
yMin = MIN(v0.y, MIN(v1.y, v2.y));
|
|
|
|
yMax = MAX(v0.y, MAX(v1.y, v2.y));
|
2018-09-06 01:53:11 +00:00
|
|
|
|
2018-09-08 01:30:02 +00:00
|
|
|
xMin = MIN(v0.x, MIN(v1.x, v2.x));
|
|
|
|
xMax = MAX(v0.x, MAX(v1.x, v2.x));
|
2018-09-06 01:53:11 +00:00
|
|
|
}
|
|
|
|
|
2018-09-08 01:30:02 +00:00
|
|
|
float yMin, yMax;
|
|
|
|
float xMin, xMax;
|
|
|
|
};
|
2018-09-06 01:53:11 +00:00
|
|
|
|
|
|
|
|
2018-09-08 01:30:02 +00:00
|
|
|
// PUBLIC FUNCTIONS
|
2018-09-11 03:11:52 +00:00
|
|
|
void CullBackfaces(
|
|
|
|
Mesh_LocalData &local, Mesh_TransformedData &transformed,
|
|
|
|
Point &camPosition)
|
2018-09-06 01:53:11 +00:00
|
|
|
{
|
2018-09-11 03:11:52 +00:00
|
|
|
transformed.faces.clear();
|
|
|
|
|
|
|
|
for (size_t f = 0; f < local.faces.size(); ++f)
|
|
|
|
{
|
|
|
|
unsigned int v0 = local.faces[f].vertIndex[0];
|
|
|
|
unsigned int v1 = local.faces[f].vertIndex[1];
|
|
|
|
unsigned int v2 = local.faces[f].vertIndex[2];
|
|
|
|
|
|
|
|
Vector v01 = transformed.verts[v1].point - transformed.verts[v0].point;
|
|
|
|
Vector v02 = transformed.verts[v2].point - transformed.verts[v0].point;
|
|
|
|
Vector normal = Vector::Cross(v01, v02);
|
|
|
|
|
|
|
|
// Store normal for flat shading
|
|
|
|
local.faces[f].normal = normal;
|
|
|
|
local.faces[f].normal.Normalize();
|
|
|
|
|
|
|
|
// Invert for Blender-compatibility
|
|
|
|
normal = -normal;
|
|
|
|
|
|
|
|
// Eye vector to viewport
|
|
|
|
Vector view = camPosition - transformed.verts[v0].point;
|
|
|
|
|
|
|
|
float dot = Vector::Dot(normal, view);
|
|
|
|
|
|
|
|
if (dot < EPSILON_E3)
|
|
|
|
{
|
|
|
|
transformed.faces.push_back(local.faces[f]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2018-09-14 01:57:06 +00:00
|
|
|
void RenderMesh(Engine_Buffer &buffer, Mesh_TransformedData &mesh, bool smooth, Texture &texture,
|
|
|
|
std::vector<TextureCoord> &uvs)
|
2018-09-11 03:11:52 +00:00
|
|
|
{
|
|
|
|
for(size_t f = 0; f < mesh.faces.size(); ++f)
|
2018-09-06 01:53:11 +00:00
|
|
|
{
|
2018-09-14 01:57:06 +00:00
|
|
|
// The vertices of this face
|
2018-09-11 03:11:52 +00:00
|
|
|
unsigned int vIndex0 = mesh.faces[f].vertIndex[0];
|
|
|
|
unsigned int vIndex1 = mesh.faces[f].vertIndex[1];
|
|
|
|
unsigned int vIndex2 = mesh.faces[f].vertIndex[2];
|
2018-09-14 01:57:06 +00:00
|
|
|
Vertex &v0 = mesh.verts[vIndex0];
|
|
|
|
Vertex &v1 = mesh.verts[vIndex1];
|
|
|
|
Vertex &v2 = mesh.verts[vIndex2];
|
2018-09-11 02:51:59 +00:00
|
|
|
|
|
|
|
|
2018-09-14 01:57:06 +00:00
|
|
|
// The UVs of this face's vertices
|
|
|
|
unsigned int tIndex0 = mesh.faces[f].textureIndex[0];
|
|
|
|
unsigned int tIndex1 = mesh.faces[f].textureIndex[1];
|
|
|
|
unsigned int tIndex2 = mesh.faces[f].textureIndex[2];
|
|
|
|
TextureCoord &t0 = uvs[tIndex0];
|
|
|
|
TextureCoord &t1 = uvs[tIndex1];
|
|
|
|
TextureCoord &t2 = uvs[tIndex2];
|
|
|
|
|
|
|
|
|
|
|
|
// Bounding box for barycentric calculations (top-left fill convention)
|
2018-09-11 02:51:59 +00:00
|
|
|
BoundingBox box(v0.point, v1.point, v2.point);
|
|
|
|
int yMin = (int)ceilf(box.yMin);
|
|
|
|
int yMax = (int)ceilf(box.yMax) - 1;
|
|
|
|
int xMin = (int)ceilf(box.xMin);
|
|
|
|
int xMax = (int)ceilf(box.xMax) - 1;
|
|
|
|
|
2018-09-14 01:57:06 +00:00
|
|
|
|
2018-09-11 02:51:59 +00:00
|
|
|
// Constants for this triangle used for barycentric calculations
|
|
|
|
Vector v01 = v1.point - v0.point;
|
|
|
|
Vector v02 = v2.point - v0.point;
|
|
|
|
float dot0101 = Vector::Dot(v01, v01);
|
|
|
|
float dot0102 = Vector::Dot(v01, v02);
|
|
|
|
float dot0202 = Vector::Dot(v02, v02);
|
|
|
|
|
2018-09-14 01:57:06 +00:00
|
|
|
|
|
|
|
// Iterate over the bounding box and determine if each point is in the triangle
|
2018-09-11 02:51:59 +00:00
|
|
|
for (int y = yMin; y <= yMax; ++y)
|
2018-09-06 01:53:11 +00:00
|
|
|
{
|
2018-09-11 02:51:59 +00:00
|
|
|
for (int x = xMin; x <= xMax; ++x)
|
|
|
|
{
|
2018-09-14 01:57:06 +00:00
|
|
|
// Constant terms used for barycentric calculation
|
2018-09-11 02:51:59 +00:00
|
|
|
Point p(x, y, 1.0f);
|
|
|
|
Vector v0P = p - v0.point;
|
|
|
|
float dot0P01 = Vector::Dot(v0P, v01);
|
|
|
|
float dot0P02 = Vector::Dot(v0P, v02);
|
|
|
|
float denomInv = 1.0f / ((dot0101 * dot0202) - (dot0102 * dot0102));
|
|
|
|
|
2018-09-14 01:57:06 +00:00
|
|
|
|
|
|
|
// Calculate the barycentric coordinate of this point
|
2018-09-11 02:51:59 +00:00
|
|
|
float barycenter[3];
|
|
|
|
barycenter[1] = (dot0202 * dot0P01 - dot0102 * dot0P02) * denomInv;
|
|
|
|
barycenter[2] = (dot0101 * dot0P02 - dot0102 * dot0P01) * denomInv;
|
|
|
|
barycenter[0] = 1.0f - barycenter[1] - barycenter[2];
|
|
|
|
|
2018-09-14 01:57:06 +00:00
|
|
|
|
2018-09-11 02:51:59 +00:00
|
|
|
// Point is inside the triangle
|
|
|
|
if ( (barycenter[0] >= 0.0f)
|
2018-09-14 01:57:06 +00:00
|
|
|
&& (barycenter[1] >= 0.0f)
|
|
|
|
&& (barycenter[2] >= 0.0f))
|
2018-09-11 02:51:59 +00:00
|
|
|
{
|
2018-09-14 01:57:06 +00:00
|
|
|
// Constant terms used in the U and V interpolation
|
|
|
|
float a = barycenter[0] * v1.point.w * v2.point.w;
|
|
|
|
float b = barycenter[1] * v0.point.w * v2.point.w;
|
|
|
|
float c = barycenter[2] * v0.point.w * v1.point.w;
|
|
|
|
float abc = 1.0f / (a + b + c);
|
|
|
|
|
2018-09-06 01:53:11 +00:00
|
|
|
|
2018-09-14 01:57:06 +00:00
|
|
|
// Interpolate U and V
|
|
|
|
float u = ((a * t0.u) + (b * t1.u) + (c * t2.u)) * abc;
|
|
|
|
float v = ((a * t0.v) + (b * t1.v) + (c * t2.v)) * abc;
|
|
|
|
|
|
|
|
|
|
|
|
// Convert U and V to pixels in the texture image
|
|
|
|
unsigned int uPixel = (unsigned int)(u * texture.width);
|
|
|
|
unsigned int vPixel = (unsigned int)(v * texture.height);
|
|
|
|
|
|
|
|
|
|
|
|
ColorF32 shading;
|
|
|
|
|
|
|
|
// Gouraud shading - interpolate color based on vertices
|
2018-09-11 03:11:52 +00:00
|
|
|
if (smooth)
|
2018-09-11 02:51:59 +00:00
|
|
|
{
|
2018-09-14 01:57:06 +00:00
|
|
|
shading =
|
2018-09-11 02:51:59 +00:00
|
|
|
(barycenter[0] * v0.color)
|
|
|
|
+ (barycenter[1] * v1.color)
|
|
|
|
+ (barycenter[2] * v2.color);
|
|
|
|
}
|
2018-09-14 01:57:06 +00:00
|
|
|
// Flat shading - base color on single face color
|
2018-09-11 02:51:59 +00:00
|
|
|
else
|
|
|
|
{
|
2018-09-14 01:57:06 +00:00
|
|
|
shading = mesh.faces[f].color;
|
2018-09-11 02:51:59 +00:00
|
|
|
}
|
2018-09-06 01:53:11 +00:00
|
|
|
|
2018-09-07 01:32:15 +00:00
|
|
|
|
2018-09-14 01:57:06 +00:00
|
|
|
// Shade the texel with lighting calculations
|
|
|
|
ColorU32 texel = texture.texels[vPixel][uPixel];
|
|
|
|
texel.r *= shading.r;
|
|
|
|
texel.g *= shading.g;
|
|
|
|
texel.b *= shading.b;
|
2018-09-06 01:53:11 +00:00
|
|
|
|
2018-09-07 01:32:15 +00:00
|
|
|
|
2018-09-14 01:57:06 +00:00
|
|
|
// Interpolate 1/z for the z-buffer
|
|
|
|
float zInv =
|
|
|
|
1.0f /
|
|
|
|
((barycenter[0] * v0.point.w)
|
|
|
|
+ (barycenter[1] * v1.point.w)
|
|
|
|
+ (barycenter[2] * v2.point.w));
|
2018-09-11 02:51:59 +00:00
|
|
|
|
|
|
|
|
2018-09-14 01:57:06 +00:00
|
|
|
|
|
|
|
// Draw the pixel if it's closer than what's in the z-buffer
|
|
|
|
int pixel = (y * buffer.width + x);
|
2018-09-11 02:51:59 +00:00
|
|
|
if (zInv > buffer.zbuffer[pixel])
|
|
|
|
{
|
2018-09-14 01:57:06 +00:00
|
|
|
DrawPixel(buffer.buffer, buffer.width, texel.u32, x, y);
|
2018-09-07 01:32:15 +00:00
|
|
|
|
2018-09-11 02:51:59 +00:00
|
|
|
buffer.zbuffer[pixel] = zInv;
|
|
|
|
}
|
2018-09-08 01:30:02 +00:00
|
|
|
}
|
2018-09-07 01:32:15 +00:00
|
|
|
}
|
2018-09-06 01:53:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|