154 lines
3.4 KiB
C
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
|
|
};
|
|
}
|