377 lines
11 KiB
C
377 lines
11 KiB
C
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <assert.h>
|
|
#include <math.h>
|
|
#include <stdbool.h>
|
|
#include <ncurses.h>
|
|
#include <unistd.h>
|
|
#include <termios.h>
|
|
#include <limits.h>
|
|
#include <time.h>
|
|
#include <SDL2/SDL.h>
|
|
#include <SDL2/SDL_image.h>
|
|
|
|
#include "hash.h"
|
|
#include "structure.h"
|
|
#include "base.h"
|
|
#include "display.h"
|
|
#include "triangles.h"
|
|
|
|
double draw_incr = 0.0 ;
|
|
|
|
int errno = 0;
|
|
pt_2d* triangle_1 ;
|
|
pt_2d* triangle_2 ;
|
|
|
|
pt_2d* cube_t1 ;
|
|
pt_2d* cube_t2 ;
|
|
|
|
int* dOrder1 ;
|
|
int* dOrder2 ;
|
|
|
|
double* zdepths1 ;
|
|
double* zdepths2 ;
|
|
|
|
void trInit() {
|
|
triangle_1 = malloc(sizeof(pt_2d)*3);
|
|
triangle_2 = malloc(sizeof(pt_2d)*3);
|
|
cube_t1 = malloc(sizeof(pt_2d)*6);
|
|
cube_t2 = malloc(sizeof(pt_2d)*6);
|
|
dOrder1 = malloc(sizeof(int)*6);
|
|
dOrder2 = malloc(sizeof(int)*6);
|
|
zdepths1 = malloc(sizeof(double)*4);
|
|
zdepths2 = malloc(sizeof(double)*4);
|
|
}
|
|
|
|
double det2D(pt_2d* p1, pt_2d* p2, pt_2d* p3) {
|
|
return p1->x * (p2->y - p3->y)
|
|
+ p2->x * (p3->y - p1->y)
|
|
+ p3->x * (p1->y - p2->y);
|
|
}
|
|
|
|
void checkTriWinding(pt_2d * p1, pt_2d * p2, pt_2d * p3, bool allowReversed) {
|
|
double detTri = det2D(p1, p2, p3);
|
|
if (detTri < 0.0) {
|
|
if (allowReversed) {
|
|
double t = p3->x;
|
|
p3->x = p2->x;
|
|
p2->x = t;
|
|
|
|
t = p3->y;
|
|
p3->y = p2->y;
|
|
p2->y = t;
|
|
} else {
|
|
errno = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool boundaryCollideChk(pt_2d *p1, pt_2d *p2, pt_2d *p3, double eps) {
|
|
return det2D(p1, p2, p3) < eps;
|
|
}
|
|
|
|
bool boundaryDoesntCollideChk(pt_2d *p1, pt_2d *p2, pt_2d *p3, double eps) {
|
|
return det2D(p1, p2, p3) <= eps;
|
|
}
|
|
|
|
bool triTri2D(pt_2d* t1, pt_2d* t2, double eps, bool allowReversed, bool onBoundary) {
|
|
bool(*chkEdge)(pt_2d*, pt_2d*, pt_2d*, double);
|
|
int i;
|
|
errno = 0;
|
|
|
|
// Triangles must be expressed anti-clockwise
|
|
checkTriWinding(&t1[0], &t1[1], &t1[2], allowReversed);
|
|
if (errno != 0) {
|
|
return false;
|
|
}
|
|
checkTriWinding(&t2[0], &t2[1], &t2[2], allowReversed);
|
|
if (errno != 0) {
|
|
return false;
|
|
}
|
|
|
|
if (onBoundary) {
|
|
// pt_2ds on the boundary are considered as colliding
|
|
chkEdge = boundaryCollideChk;
|
|
} else {
|
|
// pt_2ds on the boundary are not considered as colliding
|
|
chkEdge = boundaryDoesntCollideChk;
|
|
}
|
|
|
|
//For edge E of trangle 1,
|
|
for (i = 0; i < 3; ++i) {
|
|
int j = (i + 1) % 3;
|
|
|
|
//Check all points of trangle 2 lay on the external side of the edge E. If
|
|
//they do, the triangles do not collide.
|
|
if (chkEdge(&t1[i], &t1[j], &t2[0], eps) &&
|
|
chkEdge(&t1[i], &t1[j], &t2[1], eps) &&
|
|
chkEdge(&t1[i], &t1[j], &t2[2], eps)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
//For edge E of trangle 2,
|
|
for (i = 0; i < 3; i++) {
|
|
int j = (i + 1) % 3;
|
|
|
|
//Check all points of trangle 1 lay on the external side of the edge E. If
|
|
//they do, the triangles do not collide.
|
|
if (chkEdge(&t2[i], &t2[j], &t1[0], eps) &&
|
|
chkEdge(&t2[i], &t2[j], &t1[1], eps) &&
|
|
chkEdge(&t2[i], &t2[j], &t1[2], eps))
|
|
return false;
|
|
}
|
|
|
|
//The triangles collide
|
|
return true;
|
|
}
|
|
|
|
bool triangleIntersection(pt_2d* tri1, pt_2d* tri2) {
|
|
return triTri2D(tri1, tri2, 0.0, false, false);
|
|
}
|
|
|
|
bool triangleIntersectionDec(pt_2d t1_1, pt_2d t1_2, pt_2d t1_3, pt_2d t2_1, pt_2d t2_2, pt_2d t2_3) {
|
|
triangle_1[0] = t1_1;
|
|
triangle_1[1] = t1_2;
|
|
triangle_1[2] = t1_3;
|
|
triangle_2[0] = t2_1;
|
|
triangle_2[1] = t2_2;
|
|
triangle_2[2] = t2_3;
|
|
return triangleIntersection(triangle_1, triangle_2);
|
|
}
|
|
|
|
bool multipleTrianglesIntersection(pt_2d* tri1, int len1, pt_2d* tri2, int len2, int* ret1, int* ret2) {
|
|
for(int k1 = 0; k1 < len1; k1+=3) {
|
|
for(int k2 = 0; k2 < len2; k2+=3) {
|
|
if(triangleIntersection(&tri1[k1], &tri2[k2])) {
|
|
if(ret1 != NULL) {*ret1 = k1;}
|
|
if(ret2 != NULL) {*ret2 = k2;}
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false ;
|
|
}
|
|
|
|
int visibleSurfaces(double x0, double y0, double z0, cube_0 cb, int* dOrd) {
|
|
// returns the number of surfaces that should be drawn, as well as filling dOrd for said surfaces :
|
|
// 0 = +x ; 1 = -x
|
|
// 2 = +y ; 3 = -y
|
|
// 4 = +z ; 5 = -z
|
|
|
|
// align cube center to (0, 0, 0)
|
|
double x = x0 - (cb.x + cb.w/2.0) ;
|
|
double y = y0 - (cb.y + cb.h/2.0) ;
|
|
double z = z0 - (cb.z + cb.d/2.0) ;
|
|
// rotate (y)
|
|
double xry = x*cos(cb.hz_angle) + z*sin(cb.hz_angle) ;
|
|
double yry = y ;
|
|
double zry = z*cos(cb.hz_angle) - x*sin(cb.hz_angle) ;
|
|
// rotate (x)
|
|
double xrx = xry ;
|
|
double yrx = yry*cos(cb.vt_angle) + zry*sin(cb.vt_angle) ;
|
|
double zrx = zry*cos(cb.vt_angle) - yry*sin(cb.vt_angle) ;
|
|
// cube is centered and aligned
|
|
int id = 0 ;
|
|
if(xrx > cb.w/2.0) {
|
|
dOrd[id] = 0 ;
|
|
id += 1 ;
|
|
} else if(xrx < -cb.w/2.0) {
|
|
dOrd[id] = 1 ;
|
|
id += 1 ;
|
|
}
|
|
if(yrx > cb.h/2.0) {
|
|
dOrd[id] = 2 ;
|
|
id += 1 ;
|
|
} else if(yrx < -cb.h/2.0) {
|
|
dOrd[id] = 3 ;
|
|
id += 1 ;
|
|
}
|
|
if(zrx > cb.d/2.0) {
|
|
dOrd[id] = 4 ;
|
|
id += 1 ;
|
|
} else if(zrx < -cb.d/2.0) {
|
|
dOrd[id] = 5 ;
|
|
id += 1 ;
|
|
}
|
|
if(id == 0) { // inside the cube
|
|
for(int k = 0; k < 6; k++) {
|
|
dOrd[k] = k ;
|
|
}
|
|
return 6;
|
|
} else {
|
|
return id ;
|
|
}
|
|
}
|
|
|
|
bool noWT;
|
|
double sign_triangle(pt_2d p1, pt_2d p2, pt_2d p3) {
|
|
return (p1.x - p3.x) * (p2.y - p3.y) - (p2.x - p3.x) * (p1.y - p3.y);
|
|
}
|
|
|
|
pt_2d to_pt2d(double x0, double y0, double z0) {
|
|
pt_2d res;
|
|
res.x = x0;
|
|
res.y = y0;
|
|
res.z = z0;
|
|
return res;
|
|
}
|
|
|
|
double dot_product_3D(pt_2d p1, pt_2d p2) {
|
|
return p1.x*p2.x + p1.y*p2.y + p1.z*p2.z ;
|
|
}
|
|
|
|
double dot_product_2D(pt_2d p1, pt_2d p2) {
|
|
return p1.x*p2.x + p1.y*p2.y ;
|
|
}
|
|
|
|
void return_barycentric(pt_2d p, pt_2d a, pt_2d b, pt_2d c, double* u, double* v, double* w) {
|
|
// get barycentric coords of p inside ABC triangle
|
|
pt_2d v0 = to_pt2d(b.x - a.x, b.y - a.y, b.z - a.z);
|
|
pt_2d v1 = to_pt2d(c.x - a.x, c.y - a.y, c.z - a.z);
|
|
pt_2d v2 = to_pt2d(p.x - a.x, p.y - a.y, p.z - a.z);
|
|
double d00 = dot_product_2D(v0, v0);
|
|
double d01 = dot_product_2D(v0, v1);
|
|
double d11 = dot_product_2D(v1, v1);
|
|
double d20 = dot_product_2D(v2, v0);
|
|
double d21 = dot_product_2D(v2, v1);
|
|
double denom = d00 * d11 - d01 * d01;
|
|
if(v != NULL) {*v = (d11 * d20 - d01 * d21) / denom;}
|
|
if(w != NULL) {*w = (d00 * d21 - d01 * d20) / denom;}
|
|
if(u != NULL && v != NULL && w != NULL) {*u = 1.0 - *v - *w;}
|
|
}
|
|
|
|
bool point_in_triangle(pt_2d pt, pt_2d v1, pt_2d v2, pt_2d v3, double* thetaA, double* thetaB, double* thetaC) {
|
|
double d1, d2, d3;
|
|
bool has_neg, has_pos;
|
|
|
|
d1 = sign_triangle(pt, v1, v2);
|
|
d2 = sign_triangle(pt, v2, v3);
|
|
d3 = sign_triangle(pt, v3, v1);
|
|
|
|
has_neg = (d1 < 0) || (d2 < 0) || (d3 < 0);
|
|
has_pos = (d1 > 0) || (d2 > 0) || (d3 > 0);
|
|
|
|
if(!(has_neg && has_pos)) {
|
|
return_barycentric(pt, v1, v2, v3, thetaA, thetaB, thetaC);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool seg_seg_inter(pt_2d p1, pt_2d p2, pt_2d p3, pt_2d p4, double* ret0, double* ret1) {
|
|
double deno = (p4.x - p3.x)*(p2.y - p1.y) - (p4.y - p3.y)*(p2.x - p1.x);
|
|
if(absf(deno) <= 0.0001) {
|
|
return false;
|
|
}
|
|
//printf("%lf\n", deno);
|
|
double alpha =
|
|
((p4.x - p3.x)*(p3.y - p1.y) - (p4.y - p3.y)*(p3.x - p1.x))/
|
|
(deno) ;
|
|
|
|
double beta =
|
|
((p2.x - p1.x)*(p3.y - p1.y) - (p2.y - p1.y)*(p3.x - p1.x))/
|
|
(deno) ;
|
|
|
|
if(ret0 != NULL) {*ret0 = alpha;}
|
|
if(ret1 != NULL) {*ret1 = beta;}
|
|
return (alpha >= 0.0 && alpha <= 1.0 && beta >= 0.0 && beta <= 1.0);
|
|
}
|
|
|
|
void get_barycentric(pt_2d p, pt_2d* tri, double* retu, double* retv, double* retw) {
|
|
pt_2d a = tri[0] ; pt_2d b = tri[1] ; pt_2d c = tri[2] ;
|
|
pt_2d v0 = vect_diff(b, a);
|
|
pt_2d v1 = vect_diff(c, a);
|
|
pt_2d v2 = vect_diff(p, a);
|
|
double d00 = dot2D(v0, v0);
|
|
double d01 = dot2D(v0, v1);
|
|
double d11 = dot2D(v1, v1);
|
|
double d20 = dot2D(v2, v0);
|
|
double d21 = dot2D(v2, v1);
|
|
double denom = d00 * d11 - d01 * d01;
|
|
if(retv != NULL) {
|
|
*retv = (d11 * d20 - d01 * d21) / denom;
|
|
}
|
|
if(retw != NULL) {
|
|
*retw = (d00 * d21 - d01 * d20) / denom;
|
|
}
|
|
if(retu != NULL) {
|
|
*retu = 1.0 - *retv - *retw ;
|
|
}
|
|
}
|
|
|
|
bool pt_equal(pt_2d p1, pt_2d p2, double epsilon) {
|
|
return (absf(p2.x - p1.x) <= epsilon && absf(p2.y - p1.y) <= epsilon && absf(p2.z - p1.z) <= epsilon) ;
|
|
}
|
|
|
|
bool is_hidden(SDL_Renderer* renderer, pt_2d p, pt_2d ogp, pt_2d* tri, pt_2d* og) {
|
|
if(pt_equal(p, tri[0], 0.0001) || pt_equal(p, tri[1], 0.0001) || pt_equal(p, tri[2], 0.0001)) {
|
|
return false;
|
|
}
|
|
double u = 0.0 ;
|
|
double v = 0.0 ;
|
|
double w = 0.0 ;
|
|
get_barycentric(p, tri, &u, &v, &w);
|
|
pt_2d mid = convex_pt2d_tri(og[0], u, og[1], v, og[2], w);
|
|
if(renderer != NULL && (u >= 0.0) && (v >= 0.0) && (w >= 0.0) && (u+v+w <= 1.0)) {
|
|
if(mid.z >= 0.4) {
|
|
SDL_Rect r;
|
|
r.x = (int)(1500.0 * (1.0 + (mid.x / (1.5 * mid.z * tan_fov))) / 2.0) -2;
|
|
r.y = (int)(1000.0 * (1.0 + (mid.y / (mid.z * tan_fov))) / 2.0) -2;
|
|
r.w = 4 ;
|
|
r.h = 4 ;
|
|
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
|
|
SDL_RenderFillRect(renderer, &r);
|
|
}
|
|
}
|
|
if((u >= 0.0) && (v >= 0.0) && (w >= 0.0) && (u+v+w <= 1.0)) {
|
|
return (proj_pt_distance_to_camera(mid) <= proj_pt_distance_to_camera(ogp));
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool nonzero(double tha, double thb, double thc, double epsilon) {
|
|
return (absf(tha) > epsilon && absf(thb) > epsilon && absf(thc) > epsilon);
|
|
}
|
|
|
|
bool is_in_front(pt_2d* tri1, pt_2d* og1, pt_2d* tri2, pt_2d* og2) {
|
|
for(int k = 0; k < 3; k++) {
|
|
pt_2d p = tri1[k];
|
|
if(pt_equal(p, tri2[0], 0.0001) || pt_equal(p, tri2[1], 0.0001) || pt_equal(p, tri2[2], 0.0001)) {
|
|
return false;
|
|
}
|
|
double u = 0.0 ;
|
|
double v = 0.0 ;
|
|
double w = 0.0 ;
|
|
get_barycentric(p, tri2, &u, &v, &w);
|
|
pt_2d mid = convex_pt2d_tri(og2[0], u, og2[1], v, og2[2], w);
|
|
if(((u >= 0.0) && (v >= 0.0) && (w >= 0.0) && (u+v+w <= 1.0)) && nonzero(u, v, w, 0.0001)) {
|
|
return !(proj_pt_distance_to_camera(mid) <= proj_pt_distance_to_camera(og1[k]));
|
|
}
|
|
}
|
|
for(int k = 0; k < 3; k++) {
|
|
pt_2d p = tri2[k];
|
|
if(pt_equal(p, tri1[0], 0.0001) || pt_equal(p, tri1[1], 0.0001) || pt_equal(p, tri1[2], 0.0001)) {
|
|
return false;
|
|
}
|
|
double u = 0.0 ;
|
|
double v = 0.0 ;
|
|
double w = 0.0 ;
|
|
get_barycentric(p, tri1, &u, &v, &w);
|
|
pt_2d mid = convex_pt2d_tri(og1[0], u, og1[1], v, og1[2], w);
|
|
if(((u >= 0.0) && (v >= 0.0) && (w >= 0.0) && (u+v+w <= 1.0)) && nonzero(u, v, w, 0.0001)) {
|
|
return !(proj_pt_distance_to_camera(mid) >= proj_pt_distance_to_camera(og2[k]));
|
|
}
|
|
}
|
|
for(int k1 = 0; k1 < 3; k1++) {
|
|
for(int k2 = 0; k2 < 3; k2++) {
|
|
double th1, th2;
|
|
if(seg_seg_inter(tri1[k1], tri1[(k1+1)%3], tri2[k2], tri2[(k2+1)%3], &th1, &th2)) {
|
|
pt_2d mid1 = convex_pt2d(tri1[k1], tri1[(k1+1)%3], th1);
|
|
pt_2d mid2 = convex_pt2d(tri2[k2], tri2[(k2+1)%3], th2);
|
|
return (proj_pt_distance_to_camera(mid1) <= proj_pt_distance_to_camera(mid2));
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
} |