binding-of-isaac/src/triangles.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;
}