#pragma once #include #include #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 }; }