391 lines
9.8 KiB
C++
391 lines
9.8 KiB
C++
#include "rasterizer.h"
|
|
#include "display.h"
|
|
#include "rmath.h"
|
|
#include "time.h"
|
|
|
|
struct Plane {
|
|
vec3<fp> n;
|
|
fp d;
|
|
};
|
|
|
|
inline int min(int a, int b){
|
|
if(a < b)
|
|
return a;
|
|
return b;
|
|
}
|
|
|
|
inline int max(int a, int b){
|
|
if(a > b)
|
|
return a;
|
|
return b;
|
|
}
|
|
|
|
namespace Rasterizer {
|
|
Plane clippingPlanes[5];
|
|
|
|
fp *depthBuffer;
|
|
|
|
fp fov_d = 1;
|
|
|
|
void init(){
|
|
clippingPlanes[0] = {{0, 0, 1}, fp(0)-fp(NEAR_PLANE)}; // near
|
|
clippingPlanes[1] = {{fp(I_SQRT_2), 0, fp(I_SQRT_2)}, 0}; // left
|
|
clippingPlanes[2] = {{fp(-I_SQRT_2), 0, fp(I_SQRT_2)}, 0}; // right
|
|
clippingPlanes[3] = {{0, fp(I_SQRT_2), fp(I_SQRT_2)}, 0}; // bottom
|
|
clippingPlanes[4] = {{0, fp(-I_SQRT_2), fp(I_SQRT_2)}, 0}; // top
|
|
}
|
|
|
|
void reset(){
|
|
for(int i = 0; i < RENDER_WIDTH*RENDER_HEIGHT; i++){
|
|
depthBuffer[i] = -1;
|
|
}
|
|
}
|
|
|
|
void setFOV(int fov){
|
|
fp half_fov_rad = fp(fov) * fp(PI) / fp(180) / fp(2);
|
|
fov_d = fp_tan(half_fov_rad) * fp(2);
|
|
|
|
half_fov_rad = half_fov_rad + fp(0.1);
|
|
fp a = fp_cos(half_fov_rad - fp(HALF_PI));
|
|
fp b = fp_sin(half_fov_rad - fp(HALF_PI));
|
|
|
|
half_fov_rad = fp_atan(fp_tan(half_fov_rad) * (fp(RENDER_HEIGHT)/fp(RENDER_WIDTH)));
|
|
fp c = fp_cos(half_fov_rad - fp(HALF_PI));
|
|
fp d = fp_sin(half_fov_rad - fp(HALF_PI));
|
|
|
|
clippingPlanes[0] = {{0, 0, 1}, fp(0)-fp(NEAR_PLANE)}; // near
|
|
clippingPlanes[1] = {{b, 0, a}, 0}; // left
|
|
clippingPlanes[2] = {{fp(0)-b, 0, a}, 0}; // right
|
|
clippingPlanes[3] = {{0, d, c}, 0}; // bottom
|
|
clippingPlanes[4] = {{0, fp(0)-d, c}, 0}; // top
|
|
}
|
|
|
|
inline vec3<fp> toDevice(vec3<fp> p){
|
|
return {
|
|
p.x / p.z / fov_d,
|
|
p.y / p.z / fov_d,
|
|
p.z
|
|
};
|
|
}
|
|
|
|
inline vec3<int> toScreen(vec3<fp> p){
|
|
return {
|
|
p.x * fp(RENDER_WIDTH) + fp(RENDER_WIDTH / 2.0f),
|
|
p.y * fp(RENDER_WIDTH) + fp(RENDER_HEIGHT / 2.0f),
|
|
(int)((p.z - fp(NEAR_PLANE)) * (fp(1000) / fp(FAR_PLANE))) + 1
|
|
};
|
|
}
|
|
|
|
struct Edge {
|
|
int minY;
|
|
int maxY;
|
|
fp x;
|
|
fp m;
|
|
Edge *next;
|
|
};
|
|
inline Edge newEdge(vec3<int> p0, vec3<int> p1){
|
|
if(p0.y > p1.y){
|
|
return {
|
|
p1.y,
|
|
p0.y,
|
|
p1.x,
|
|
fp(p0.x - p1.x) / fp(p0.y - p1.y),
|
|
nullptr
|
|
};
|
|
} else if(p0.y < p1.y){
|
|
return {
|
|
p0.y,
|
|
p1.y,
|
|
p0.x,
|
|
fp(p1.x - p0.x) / fp(p1.y - p0.y),
|
|
nullptr
|
|
};
|
|
} else {
|
|
return {p0.y, p1.y, p0.x, 0, nullptr};
|
|
}
|
|
}
|
|
|
|
inline void _drawTriangle(Model *model, Triangle triangle, bool useDepth, bool isShaded){
|
|
vec3<fp> p0_d = toDevice(triangle.p0);
|
|
vec3<fp> p1_d = toDevice(triangle.p1);
|
|
vec3<fp> p2_d = toDevice(triangle.p2);
|
|
|
|
vec3<int> p0 = toScreen(p0_d);
|
|
vec3<int> p1 = toScreen(p1_d);
|
|
vec3<int> p2 = toScreen(p2_d);
|
|
|
|
int minY = max(min(min(p0.y, p1.y), p2.y), 0);
|
|
int maxY = min(max(max(p0.y, p1.y), p2.y), RENDER_HEIGHT);
|
|
fp z = (p0.z + p1.z + p2.z) / 3;
|
|
|
|
if(isShaded){
|
|
fp brightness = dot(mat4::toMat3(model->modelMatrix) * triangle.normal, vec3<fp>(I_SQRT_3, -I_SQRT_3, -I_SQRT_3)) * fp(0.6) + fp(0.4);
|
|
if(brightness > 1)
|
|
brightness = 1;
|
|
if(brightness < 0)
|
|
brightness = 0;
|
|
triangle.c.r = fp(triangle.c.r) * brightness;
|
|
triangle.c.g = fp(triangle.c.g) * brightness;
|
|
triangle.c.b = fp(triangle.c.b) * brightness;
|
|
triangle.c = newColor(triangle.c.r, triangle.c.g, triangle.c.b);
|
|
}
|
|
|
|
Edge *edgeTable[RENDER_HEIGHT];
|
|
Edge edges[3] = {
|
|
newEdge(p0, p1),
|
|
newEdge(p1, p2),
|
|
newEdge(p2, p0),
|
|
};
|
|
|
|
edgeTable[0] = nullptr;
|
|
for(int i = 0; i < 3; i++){
|
|
if(edges[i].minY < minY){
|
|
Edge **e = &(edgeTable[0]);
|
|
while(*e != nullptr){
|
|
e = &((*e)->next);
|
|
}
|
|
*e = &edges[i];
|
|
(*e)->x = (*e)->x + fp(-(*e)->minY) * (*e)->m;
|
|
}
|
|
}
|
|
|
|
for(int y = minY; y < maxY; y++){
|
|
if(y != 0)
|
|
edgeTable[y] = nullptr;
|
|
for(int i = 0; i < 3; i++){
|
|
if(edges[i].minY == y){
|
|
Edge **e = &(edgeTable[y]);
|
|
while(*e != nullptr){
|
|
e = &((*e)->next);
|
|
}
|
|
*e = &edges[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
Edge *activeEdgeList = nullptr;
|
|
|
|
for(int y = minY; y < maxY; y++){
|
|
// add new edges to the list
|
|
if(edgeTable[y] != nullptr){
|
|
Edge **e;
|
|
for(e = &activeEdgeList; *e != nullptr; e = &((*e)->next)){
|
|
}
|
|
*e = edgeTable[y];
|
|
}
|
|
|
|
// remove edges from the list
|
|
for(Edge **e = &activeEdgeList; *e != nullptr;){
|
|
if(y == (*e)->maxY){
|
|
*e = (*e)->next;
|
|
} else {
|
|
e = &((*e)->next);
|
|
}
|
|
}
|
|
|
|
// calculate intersection point
|
|
for(Edge *e = activeEdgeList; e != nullptr; e = e->next){
|
|
e->x = e->x + e->m;
|
|
}
|
|
|
|
// draw
|
|
for(Edge *e = activeEdgeList; e != nullptr && e->next != nullptr; e = e->next->next){
|
|
int a = e->x;
|
|
int b = e->next->x;
|
|
if(a < 0) a = 0;
|
|
else if(a > RENDER_WIDTH) a = RENDER_WIDTH;
|
|
if(b < 0) b = 0;
|
|
else if(b > RENDER_WIDTH) b = RENDER_WIDTH;
|
|
int minX, maxX;
|
|
if(a > b){
|
|
maxX = a;
|
|
minX = b;
|
|
} else {
|
|
maxX = b;
|
|
minX = a;
|
|
}
|
|
for(int x = minX; x < maxX; x++){
|
|
if(z < depthBuffer[x+y*RENDER_WIDTH] || depthBuffer[x+y*RENDER_WIDTH] == -1 || !useDepth){
|
|
if(useDepth)
|
|
depthBuffer[x+y*RENDER_WIDTH] = z;
|
|
#if PIXEL_SIZE == 1
|
|
Display::drawPoint(x, y, triangle.c);
|
|
#else
|
|
Display::fillRect(x*PIXEL_SIZE, y*PIXEL_SIZE, PIXEL_SIZE, PIXEL_SIZE, triangle.c);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
inline Mesh clipTriangleAgainstPlane(Triangle triangle, Plane plane){
|
|
int numClipped = 0;
|
|
bool clippedP0 = false;
|
|
bool clippedP1 = false;
|
|
bool clippedP2 = false;
|
|
|
|
if(dot(plane.n, triangle.p0) + plane.d < 0){
|
|
numClipped++;
|
|
clippedP0 = true;
|
|
}
|
|
if(dot(plane.n, triangle.p1) + plane.d < 0){
|
|
numClipped++;
|
|
clippedP1 = true;
|
|
}
|
|
if(dot(plane.n, triangle.p2) + plane.d < 0){
|
|
numClipped++;
|
|
clippedP2 = true;
|
|
}
|
|
|
|
if(numClipped == 0){
|
|
Mesh m = {1, (Triangle*)malloc(sizeof(Triangle))};
|
|
m.triangles[0] = triangle;
|
|
return m;
|
|
} else if(numClipped == 3){
|
|
return {0, nullptr};
|
|
} else if(numClipped == 2){
|
|
vec3<fp> clipped[2];
|
|
vec3<fp> notClipped;
|
|
if(!clippedP0){
|
|
notClipped = triangle.p0;
|
|
clipped[0] = triangle.p1;
|
|
clipped[1] = triangle.p2;
|
|
} else if(!clippedP1){
|
|
notClipped = triangle.p1;
|
|
clipped[0] = triangle.p0;
|
|
clipped[1] = triangle.p2;
|
|
} else {
|
|
notClipped = triangle.p2;
|
|
clipped[0] = triangle.p0;
|
|
clipped[1] = triangle.p1;
|
|
}
|
|
|
|
fp d0 = dot(plane.n, clipped[0] - notClipped);
|
|
fp d1 = dot(plane.n, clipped[1] - notClipped);
|
|
if(d0 == 0 || d1 == 0)
|
|
return {0, nullptr};
|
|
|
|
vec3<fp> B = notClipped + (clipped[0] - notClipped) * ((fp(0) - plane.d - dot(plane.n, notClipped))/d0);
|
|
vec3<fp> C = notClipped + (clipped[1] - notClipped) * ((fp(0) - plane.d - dot(plane.n, notClipped))/d1);
|
|
|
|
Mesh m = {1, (Triangle*)malloc(sizeof(Triangle))};
|
|
m.triangles[0] = {notClipped, B, C, triangle.normal, triangle.c};
|
|
return m;
|
|
} else if(numClipped == 1){
|
|
vec3<fp> clipped;
|
|
vec3<fp> notClipped[2];
|
|
if(clippedP0){
|
|
clipped = triangle.p0;
|
|
notClipped[0] = triangle.p1;
|
|
notClipped[1] = triangle.p2;
|
|
}
|
|
|
|
if(clippedP1){
|
|
clipped = triangle.p1;
|
|
notClipped[0] = triangle.p0;
|
|
notClipped[1] = triangle.p2;
|
|
}
|
|
|
|
if(clippedP2){
|
|
clipped = triangle.p2;
|
|
notClipped[0] = triangle.p0;
|
|
notClipped[1] = triangle.p1;
|
|
}
|
|
|
|
fp d0 = dot(plane.n, clipped - notClipped[0]);
|
|
fp d1 = dot(plane.n, clipped - notClipped[1]);
|
|
if(d0 == 0 || d1 == 0)
|
|
return {0, nullptr};
|
|
|
|
vec3<fp> A = notClipped[0] + (clipped - notClipped[0]) * ((fp(0) - plane.d - dot(plane.n, notClipped[0]))/d0);
|
|
vec3<fp> B = notClipped[1] + (clipped - notClipped[1]) * ((fp(0) - plane.d - dot(plane.n, notClipped[1]))/d1);
|
|
|
|
Mesh m = {2, (Triangle*)malloc(2*sizeof(Triangle))};
|
|
m.triangles[0] = {notClipped[0], A, notClipped[1], triangle.normal, triangle.c};
|
|
m.triangles[1] = {notClipped[1], A, B, triangle.normal, triangle.c};
|
|
return m;
|
|
}
|
|
|
|
return {0, nullptr};
|
|
}
|
|
|
|
inline Mesh clipMeshAgainstPlane(Mesh mesh, Plane plane){
|
|
Mesh r = {0, nullptr};
|
|
|
|
for(int i = 0; i < mesh.numTriangles; i++){
|
|
Mesh m = clipTriangleAgainstPlane(mesh.triangles[i], plane);
|
|
r.numTriangles += m.numTriangles;
|
|
if(r.triangles == nullptr)
|
|
r.triangles = (Triangle*) malloc(r.numTriangles*sizeof(Triangle));
|
|
else
|
|
r.triangles = (Triangle*) realloc(r.triangles, r.numTriangles*sizeof(Triangle));
|
|
|
|
for(int i = r.numTriangles - m.numTriangles; i < r.numTriangles; i++){
|
|
r.triangles[i] = m.triangles[i - (r.numTriangles - m.numTriangles)];
|
|
}
|
|
|
|
free(m.triangles);
|
|
}
|
|
free(mesh.triangles);
|
|
return r;
|
|
}
|
|
|
|
inline Mesh clipMesh(Mesh mesh){
|
|
for(int i = 0; i < 5; i++){
|
|
mesh = clipMeshAgainstPlane(mesh, clippingPlanes[i]);
|
|
}
|
|
return mesh;
|
|
}
|
|
|
|
inline Mesh clipTriangle(Triangle triangle){
|
|
Mesh m = {1, (Triangle*)malloc(sizeof(Triangle))};
|
|
m.triangles[0] = triangle;
|
|
m = clipMesh(m);
|
|
return m;
|
|
}
|
|
|
|
inline void drawTriangle(Model *model, Triangle triangle, bool useDepth, bool isShaded, bool clipTriangles){
|
|
triangle.p0 = model->viewMatrix * model->modelMatrix * triangle.p0;
|
|
triangle.p1 = model->viewMatrix * model->modelMatrix * triangle.p1;
|
|
triangle.p2 = model->viewMatrix * model->modelMatrix * triangle.p2;
|
|
|
|
if(triangle.p0 == triangle.p1 || triangle.p1 == triangle.p2 || triangle.p2 == triangle.p0){
|
|
return;
|
|
}
|
|
|
|
int inside = 5;
|
|
for(int i = 0; i < 5; i++){
|
|
if(dot(clippingPlanes[i].n, triangle.p0) + clippingPlanes[i].d < 0
|
|
|| dot(clippingPlanes[i].n, triangle.p1) + clippingPlanes[i].d < 0
|
|
|| dot(clippingPlanes[i].n, triangle.p2) + clippingPlanes[i].d < 0){
|
|
inside--;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(inside == 5){
|
|
_drawTriangle(model, triangle, useDepth, isShaded);
|
|
} else if(inside != 0 && clipTriangles){
|
|
Mesh mesh = clipTriangle(triangle);
|
|
for(int i = 0; i < mesh.numTriangles; i++){
|
|
_drawTriangle(model, mesh.triangles[i], useDepth, isShaded);
|
|
}
|
|
free(mesh.triangles);
|
|
}
|
|
}
|
|
|
|
};
|
|
|
|
Model::Model(){
|
|
mesh = {0, nullptr};
|
|
}
|
|
Model::Model(Mesh mesh){
|
|
this->mesh = mesh;
|
|
}
|
|
void Model::draw(bool useDepth, bool isShaded, bool clipTriangles){
|
|
for(int i = 0; i < mesh.numTriangles; i++){
|
|
Rasterizer::drawTriangle(this, mesh.triangles[i], useDepth, isShaded, clipTriangles);
|
|
}
|
|
}
|