prizm_racing/src/rasterizer.cpp

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);
}
}