ray-tracer-challenge/intersections.h
2024-08-09 15:24:02 -05:00

148 lines
3.4 KiB
C

#pragma once
#include <stdarg.h>
#include <assert.h>
#include <stdbool.h>
#include "spheres.h"
#include "rays.h"
struct intersection {
float t;
struct sphere const * object;
};
struct intersection intersection(float t, struct sphere const * const object)
{
return (struct intersection){ t, object };
}
#define MAX_INTERSECTIONS 1024
struct intersections {
int count;
struct intersection i[MAX_INTERSECTIONS];
};
inline static void intersections(struct intersections * intersections, int count, ...)
{
va_list ap;
va_start(ap, count);
for (int i = 0; i < count; i++) {
intersections->i[i] = va_arg(ap, struct intersection);
}
va_end(ap);
intersections->count = count;
}
inline static void intersect(struct sphere const * const s, struct ray r, struct intersections * intersections)
{
struct mat4x4 m = mat4x4_inverse(&s->transform);
struct ray r2 = ray_transform(r, &m);
struct tuple sphere_to_ray = tuple_sub(r2.origin, point(0.0f, 0.0f, 0.0f));
float a = tuple_dot(r2.direction, r2.direction);
float b = 2 * tuple_dot(r2.direction, sphere_to_ray);
float c = tuple_dot(sphere_to_ray, sphere_to_ray) - 1;
float discriminant = b * b - 4 * a * c;
if (discriminant < 0) {
return;
} else {
float root = sqrtf(discriminant);
float t1 = (-b - root) / (2 * a);
float t2 = (-b + root) / (2 * a);
intersections->i[intersections->count++] = intersection(t1, s);
intersections->i[intersections->count++] = intersection(t2, s);
assert(intersections->count < MAX_INTERSECTIONS);
return;
}
}
inline static struct intersection * hit(struct intersections * xs)
{
struct intersection * i = NULL;
for (int n = 0; n < xs->count; n++) {
if (xs->i[n].t >= 0) {
if (i == NULL || i->t > xs->i[n].t)
i = &xs->i[n];
}
}
return i;
}
inline static void intersections_sort(struct intersections * xs)
{
#define left_child(i) (2 * i + 1)
int start = xs->count / 2;
int end = xs->count;
while (end > 1) {
if (start > 0) {
start = start - 1;
} else {
end = end - 1;
struct intersection tmp = xs->i[0];
xs->i[0] = xs->i[end];
xs->i[end] = tmp;
}
int root = start;
while (left_child(root) < end) {
int child = left_child(root);
if (child + 1 < end && xs->i[child].t < xs->i[child+1].t) {
child = child + 1;
}
if (xs->i[root].t < xs->i[child].t) {
struct intersection tmp = xs->i[root];
xs->i[root] = xs->i[child];
xs->i[child] = tmp;
root = child;
} else {
break;
}
}
}
#undef left_child
}
struct computations {
float t;
struct sphere const * object;
struct tuple point;
struct tuple eyev;
struct tuple normalv;
bool inside;
struct tuple over_point;
};
inline static struct computations prepare_computations(struct intersection intersection, struct ray ray)
{
struct tuple point = ray_position(ray, intersection.t);
struct tuple eyev = tuple_neg(ray.direction);
struct tuple normalv = sphere_normal_at(intersection.object, point);
bool inside = false;
if (tuple_dot(normalv, eyev) < 0.0f) {
inside = true;
normalv = tuple_neg(normalv);
}
struct tuple over_point = tuple_add(point, tuple_mul(normalv, epsilon * 100));
return (struct computations){
intersection.t,
intersection.object,
point,
eyev,
normalv,
inside,
over_point
};
}