ray-tracer-challenge/intersections_shapes.h
2024-08-09 19:57:21 -05:00

154 lines
3.4 KiB
C

#pragma once
#include <assert.h>
#include <stdbool.h>
#include "rays.h"
#include "shapes.h"
#include "intersections.h"
#include "spheres.h"
#include "planes.h"
#ifdef TEST
struct ray intersections_saved_ray;
#endif
inline static void intersect(struct shape const * const s, struct ray r, struct intersections * intersections)
{
struct mat4x4 m = mat4x4_inverse(&s->transform);
struct ray local_ray = ray_transform(r, &m);
switch (s->type) {
#ifdef TEST
case SHAPE_TEST:
intersections_saved_ray = local_ray;
break;
#endif
case SHAPE_SPHERE:
sphere_intersect(s, local_ray, intersections);
break;
case SHAPE_PLANE:
plane_intersect(s, local_ray, intersections);
break;
default:
assert(false);
break;
}
assert(intersections->count < INTERSECTIONS_MAX);
}
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 shape const * object;
struct tuple point;
struct tuple eyev;
struct tuple normalv;
bool inside;
struct tuple over_point;
};
inline static struct tuple normal_at(struct shape const * const s, struct tuple world_point)
{
struct mat4x4 inv = mat4x4_inverse(&s->transform);
struct mat4x4 inv_t = mat4x4_transpose(&inv);
struct tuple local_point = mat4x4_mul_t(&inv, &world_point);
struct tuple local_normal;
switch (s->type) {
#ifdef TEST
case SHAPE_TEST:
local_normal = vector(local_point.x, local_point.y, local_point.z);
break;
#endif
case SHAPE_SPHERE:
local_normal = sphere_normal_at(local_point);
break;
case SHAPE_PLANE:
local_normal = plane_normal_at(local_point);
break;
default:
assert(false);
break;
}
struct tuple world_normal = mat4x4_mul_t(&inv_t, &local_normal);
world_normal.w = 0.0f;
return tuple_normalize(world_normal);
}
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 = 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
};
}