A tiny software raymarcher that attempts to render "n-dimension" manofold insertions as an image appearing to be a non-euclidean 3-dimensional space. Written for the uqcs hackathon 2020. This repo is a mirror of:
https://github.com/ailrst/blackpink
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
311 lines
8.4 KiB
311 lines
8.4 KiB
#include "main.h" |
|
#include "types.h" |
|
#include "math.h" |
|
#include "vect.h" |
|
#include "distfuncs.h" |
|
#include "camera.h" |
|
|
|
#define DRAW_DIST 255.0 |
|
#define MAX_ITERATIONS 255 |
|
#define EPSILON 0.1 |
|
#define NORMAL_EPSILON 0.0001 |
|
|
|
double dabs(double yeet) { |
|
if (yeet > 0) { |
|
return yeet; |
|
} else { |
|
return -yeet; |
|
} |
|
} |
|
|
|
double dsign(double yeet) { |
|
if (yeet < 0) { |
|
return -1; |
|
} else { |
|
return 1; |
|
} |
|
} |
|
|
|
double manidist(struct vec *v) |
|
{ |
|
// return v->elements[3]; |
|
//double yeet = (SDL_GetTicks() / 10); |
|
double yeet = 300; |
|
v->elements[3] -= yeet; |
|
double out = magnitude_vec(v) - yeet; |
|
v->elements[3] += yeet; |
|
return out; |
|
} |
|
|
|
struct solid manifold = (struct solid) { |
|
.dist = manidist, |
|
}; |
|
|
|
struct colour |
|
solid_col(struct object *s, struct ray *r) { |
|
subtract_vec_ip(r->pos, &(s->sol.pos)); |
|
struct colour out = s->col(r, s); |
|
add_vec_ip(r->pos, &(s->sol.pos)); |
|
return out; |
|
} |
|
|
|
double solid_dist(struct solid *s, struct vec *v) { |
|
subtract_vec_ip(v, &(s->pos)); |
|
double out = s->dist(v); |
|
add_vec_ip(v, &s->pos); |
|
return out; |
|
} |
|
|
|
/* return a malloced vector, normal to sol at r */ |
|
struct vec * |
|
estimateNormal(struct vec *r, struct solid *sol) |
|
{ |
|
struct vec *out = new_vec(r->dimension); |
|
struct vec *tmp = new_vec(r->dimension); |
|
for (int i = 0; i < out->dimension; i++) { |
|
tmp->elements[i] = NORMAL_EPSILON; |
|
double s1 = solid_dist(sol, tmp); |
|
tmp->elements[i] = -NORMAL_EPSILON; |
|
double s2 = solid_dist(sol, tmp); |
|
|
|
out->elements[i] = s1 - s2; |
|
tmp->elements[i] = 0; |
|
} |
|
free_vec(tmp); |
|
return normalise_vec_ip(out); |
|
} |
|
|
|
struct vec * |
|
reyeet(struct vec *v, struct vec *k) { |
|
struct vec *vs [2]; |
|
// struct vec *tmp = new_vec_of(v->dimension, 1); |
|
vs[0] = k; |
|
vs[1] = v; |
|
// for (int i = 1; i < v->dimension; i++) vs[i] = k; |
|
struct vec *out = perpendicular_vec(2, vs); |
|
// free_vec(tmp); |
|
return out; |
|
} |
|
|
|
void |
|
rotateaxis(struct vec *v, struct vec *k, double a) |
|
{ |
|
double cosa = cos(a); |
|
|
|
struct vec *p = add_scaled_vec_ip( |
|
add_scaled_vec_ip(scalar_multiply_vec_ip(reyeet(v, k), sin(a)), v, cosa), |
|
k, dot_product_vec(k, v)*(1 - cosa)); |
|
|
|
free(v->elements); |
|
v->elements = p->elements; |
|
free(p); |
|
} |
|
|
|
int vectorisnan(struct vec *v) |
|
{ |
|
for (int i = 0; i < v->dimension; i++) { |
|
if (v->elements[i] != v->elements[i]) |
|
return 1; |
|
} |
|
return 0; |
|
} |
|
|
|
void |
|
manifoldstepaxees(struct vec *pos, struct vec *dir, struct vec **lads, int numlads, double distance) |
|
{ |
|
if (numlads == 0) { |
|
*((int *) 0) = 0; |
|
} |
|
|
|
struct vec *yaxisold = estimateNormal(pos, &manifold); |
|
|
|
/* move the vector foward in euclid */ |
|
add_scaled_vec_ip(pos, dir, distance); |
|
|
|
struct vec *yaxisnew = estimateNormal(pos, &manifold); |
|
|
|
/* stick it to the manifold */ |
|
add_scaled_vec_ip(pos, yaxisnew, manifold.dist(pos)); |
|
|
|
double protamtloc = acos(dot_product_vec(yaxisold,yaxisnew)); |
|
|
|
struct vec *protaxisloc = normalise_vec_ip(reyeet(yaxisold, yaxisnew)); |
|
for (int i = 0; i < numlads; i++) { |
|
struct vec *temp = copy_vec(lads[i]); |
|
rotateaxis(temp, protaxisloc, protamtloc); /* change the direction */ |
|
if (!vectorisnan(temp)) { |
|
free(lads[i]->elements); |
|
lads[i]->elements = temp->elements; |
|
free(temp); |
|
} else { |
|
free_vec(temp); |
|
} |
|
} |
|
|
|
free_vec(yaxisnew); |
|
free_vec(yaxisold); |
|
free_vec(protaxisloc); |
|
} |
|
|
|
void |
|
manifoldturn(struct ray *r, struct vec *v, double distance) |
|
{ |
|
|
|
struct vec *yaxisold = estimateNormal(r->pos, &manifold); |
|
|
|
/* move the vector foward in euclid */ |
|
add_scaled_vec_ip(r->pos, r->dir, distance); |
|
|
|
struct vec *yaxisnew = estimateNormal(r->pos, &manifold); |
|
|
|
/* stick it to the manifold */ |
|
add_scaled_vec_ip(r->pos, yaxisnew, manifold.dist(r->pos)); |
|
|
|
double protamtloc = acos(dot_product_vec(yaxisold,yaxisnew)); |
|
|
|
struct vec *protaxisloc = normalise_vec_ip(reyeet(yaxisold, yaxisnew)); |
|
|
|
struct vec *temp = copy_vec(v); |
|
rotateaxis(temp, protaxisloc, protamtloc); /* change the direction */ |
|
if (!vectorisnan(temp)) { |
|
free(v->elements); |
|
v->elements = temp->elements; |
|
free(temp); |
|
} else { |
|
free_vec(temp); |
|
} |
|
|
|
free_vec(yaxisnew); |
|
free_vec(yaxisold); |
|
free_vec(protaxisloc); |
|
} |
|
|
|
void |
|
manifoldstep(struct ray *r, double distance) |
|
{ |
|
manifoldturn(r, r->dir, distance); |
|
} |
|
|
|
void place(struct solid *v) { |
|
struct vec *dirs [v->pos.dimension]; |
|
for (int d = 0; d < v->pos.dimension; d++) { |
|
dirs[d] = new_vec(v->pos.dimension); |
|
dirs[d]->elements[d] = 1; |
|
} |
|
struct vec *tdir = new_vec(v->pos.dimension); |
|
struct vec *tpos = new_vec(v->pos.dimension); |
|
|
|
for (int d = 0; d < v->pos.dimension; d++) { |
|
for (double yee = v->pos.elements[d]; dabs(yee) > EPSILON; yee -= dsign(yee) * EPSILON) { |
|
manifoldstepaxees(tpos, dirs[d], dirs, v->pos.dimension, EPSILON); |
|
} |
|
} |
|
free_vec(tdir); |
|
free_vec(tpos); |
|
for (int d = 0; d < v->pos.dimension; d++) free_vec(dirs[d]); |
|
free(dirs); |
|
} |
|
|
|
struct pixel_info |
|
march(struct ray *r, struct object *scene) |
|
{ |
|
double travel_dist = 0; |
|
double scene_dist; |
|
double min_dist = DRAW_DIST; |
|
int i; |
|
int fligs = 0; |
|
struct colour out = (struct colour) {.sp = CS_RGB}; |
|
for (i = 0; (i < MAX_ITERATIONS) && (travel_dist < DRAW_DIST); i++) { |
|
/* calculate the distance to the scene */ |
|
scene_dist = solid_dist(&(scene->sol), r->pos); |
|
|
|
if (scene_dist < EPSILON) { /* we've hit an object */ |
|
out = scene->col(r, scene); |
|
fligs |= 0x01; |
|
break; |
|
} |
|
|
|
if (min_dist > scene_dist) |
|
min_dist = scene_dist; |
|
|
|
/* step foward the calculated distance */ |
|
manifoldstep(r, scene_dist); |
|
travel_dist += scene_dist; |
|
} |
|
|
|
/* no colour reached */ |
|
return (struct pixel_info) { |
|
.flags = fligs, |
|
.iterations = i, |
|
.col = out, |
|
.travel_dist = travel_dist, |
|
.scene_dist = min_dist |
|
}; |
|
} |
|
|
|
|
|
union badpixelformat { |
|
struct { |
|
Uint8 b; |
|
Uint8 g; |
|
Uint8 r; |
|
Uint8 a; |
|
}; |
|
Uint32 pixel; |
|
}; |
|
|
|
Uint32 get_stl_colour(struct colour *cl) { |
|
struct colour c = get_rgb(*cl); |
|
union badpixelformat p; |
|
p.r = c.r; |
|
p.g = c.g; |
|
p.b = c.b; |
|
p.a = c.a; |
|
return p.pixel; |
|
} |
|
|
|
Uint32 |
|
process_pixel(int i, int j) |
|
{ |
|
struct ray r = (struct ray) { |
|
.pos = new_vec(4), |
|
.dir = normalise_vec_ip(new_vec4(i - B_INTERNAL_WIDTH/2, j - B_INTERNAL_HEIGHT/2, 100, 0)) |
|
}; |
|
|
|
struct pixel_info p = march(&r, scene_object); |
|
|
|
|
|
// p.col.r += p.travel_dist; |
|
if (!(p.flags & 0x01)) { |
|
p.col.b = 255; |
|
p.col.r = j * 2.55; |
|
|
|
|
|
p.col.g += (16.0 / p.scene_dist); |
|
p.col.b += (16.0 / p.scene_dist); |
|
} else { |
|
p.col.r -= p.iterations; |
|
p.col.g -= p.iterations; |
|
p.col.b -= p.iterations; |
|
} |
|
// p.col.g += p.travel_dist; |
|
|
|
// p.col.g = 0; |
|
|
|
// printf("%d, ", p.iterations); |
|
|
|
if (p.col.r < 0) p.col.r = 0; |
|
if (p.col.g < 0) p.col.g = 0; |
|
if (p.col.b < 0) p.col.b = 0; |
|
if (p.col.r > 255) p.col.r = 255; |
|
if (p.col.g > 255) p.col.g = 255; |
|
if (p.col.b > 255) p.col.b = 255; |
|
|
|
// p.col.b = 255.0 / p.scene_dist; |
|
// if (p.col.b > 255) p.col.b = 255; |
|
|
|
free_vec(r.pos); |
|
free_vec(r.dir); |
|
|
|
return get_stl_colour(&p.col); |
|
}
|
|
|