211 lines
6.2 KiB
C
211 lines
6.2 KiB
C
#ifdef __NetBSD__
|
|
/* for reallocarray on NetBSD */
|
|
#define _OPENBSD_SOURCE
|
|
#endif
|
|
|
|
#include <assert.h>
|
|
#include <math.h>
|
|
|
|
#include "exceptions.h"
|
|
#include "shape.h"
|
|
#include "world.h"
|
|
|
|
typedef struct WORLD_World {
|
|
LIGHTS_Light *light;
|
|
void **objects;
|
|
unsigned int object_count;
|
|
} WORLD_World;
|
|
|
|
const unsigned int WORLD_default_ttl = 5;
|
|
|
|
void WORLD_init(WORLD_World *world, LIGHTS_Light *light) {
|
|
assert(world);
|
|
world->light = light;
|
|
world->objects = NULL;
|
|
world->object_count = 0;
|
|
}
|
|
|
|
WORLD_World *WORLD_new(LIGHTS_Light *light) {
|
|
WORLD_World *world = malloc(sizeof(WORLD_World));
|
|
if (!world)
|
|
Throw(E_MALLOC_FAILED);
|
|
WORLD_init(world, light);
|
|
return world;
|
|
}
|
|
|
|
void WORLD_destroy(WORLD_World *world) {
|
|
assert(world);
|
|
free(world->objects);
|
|
world->objects = NULL;
|
|
world->object_count = 0;
|
|
}
|
|
|
|
void WORLD_delete(WORLD_World *world) {
|
|
assert(world);
|
|
WORLD_destroy(world);
|
|
free(world);
|
|
}
|
|
|
|
void WORLD_set_light(WORLD_World *world, LIGHTS_Light *light) {
|
|
assert(world);
|
|
assert(light);
|
|
world->light = light;
|
|
}
|
|
|
|
inline LIGHTS_Light *WORLD_get_light(const WORLD_World *world) {
|
|
assert(world);
|
|
return world->light;
|
|
}
|
|
|
|
void WORLD_add_object(WORLD_World *world, void *shape) {
|
|
assert(world);
|
|
assert(shape);
|
|
void **tmpptr = realloc(world->objects, sizeof(void *) * (world->object_count + 1));
|
|
if (!tmpptr) {
|
|
Throw(E_MALLOC_FAILED);
|
|
} else {
|
|
world->objects = tmpptr;
|
|
}
|
|
world->objects[world->object_count] = shape;
|
|
world->object_count++;
|
|
}
|
|
|
|
inline void *WORLD_get_object(const WORLD_World *world, unsigned int index) {
|
|
assert(world);
|
|
assert(index < world->object_count);
|
|
return world->objects[index];
|
|
}
|
|
|
|
inline unsigned int WORLD_get_object_count(const WORLD_World *world) {
|
|
assert(world);
|
|
return world->object_count;
|
|
}
|
|
|
|
RAY_Intersections *WORLD_intersect(const WORLD_World *world, const RAY_Ray *ray) {
|
|
assert(world);
|
|
assert(ray);
|
|
RAY_Intersections *intersections = RAY_new_intersections();
|
|
for (unsigned int i = 0; i < world->object_count; i++) {
|
|
SHAPE_intersect(intersections, WORLD_get_object(world, i), ray);
|
|
}
|
|
RAY_sort_intersections(intersections);
|
|
return intersections;
|
|
}
|
|
|
|
void WORLD_shade_hit(TUPLES_Color *dest, const WORLD_World *world, const RAY_Computations *computation, unsigned int ttl) {
|
|
assert(world);
|
|
assert(computation);
|
|
|
|
LIGHTS_Light *light = WORLD_get_light(world);
|
|
double intensity = LIGHTS_intensity_at(light, &computation->over_point, world);
|
|
|
|
MATERIAL_lighting(dest, (SHAPE_Shape *)computation->object, light, &computation->over_point, &computation->eyev, &computation->normalv, intensity);
|
|
|
|
TUPLES_Color reflected, refracted;
|
|
WORLD_reflected_color(&reflected, world, computation, ttl);
|
|
WORLD_refracted_color(&refracted, world, computation, ttl);
|
|
const MATERIAL_Material *material = SHAPE_get_material(computation->object);
|
|
if (material->reflective > 0 && material->transparency > 0) {
|
|
double reflectance = RAY_schlick(computation);
|
|
TUPLES_multiply(&reflected, &reflected, reflectance);
|
|
TUPLES_multiply(&refracted, &refracted, 1 - reflectance);
|
|
}
|
|
TUPLES_add(dest, dest, &reflected);
|
|
TUPLES_add(dest, dest, &refracted);
|
|
}
|
|
|
|
void WORLD_color_at(TUPLES_Color *dest, const WORLD_World *world, const RAY_Ray *ray, unsigned int ttl) {
|
|
assert(dest);
|
|
assert(world);
|
|
assert(ray);
|
|
RAY_Intersections *intersections = WORLD_intersect(world, ray);
|
|
RAY_Xs *hit = RAY_hit(intersections);
|
|
if (!hit) {
|
|
TUPLES_init_color(dest, 0, 0, 0);
|
|
} else {
|
|
RAY_Computations comps;
|
|
RAY_prepare_computations(&comps, hit, ray, intersections);
|
|
WORLD_shade_hit(dest, world, &comps, ttl);
|
|
}
|
|
RAY_delete_intersections(intersections);
|
|
}
|
|
|
|
bool WORLD_is_shadowed(const WORLD_World *world, const TUPLES_Point *light_position, const TUPLES_Point *point) {
|
|
assert(world);
|
|
assert(light_position);
|
|
assert(point);
|
|
|
|
TUPLES_Vector direction;
|
|
TUPLES_subtract(&direction, light_position, point);
|
|
double distance = TUPLES_magnitude(&direction);
|
|
if (double_equal(0, distance))
|
|
return false;
|
|
TUPLES_normalize(&direction);
|
|
|
|
RAY_Ray ray;
|
|
RAY_init_from_tuples(&ray, point, &direction);
|
|
struct RAY_Intersections *intersections = WORLD_intersect(world, &ray);
|
|
RAY_Xs *hit = RAY_shadow_hit(intersections);
|
|
bool is_shadowed = false;
|
|
if (hit && hit->t < distance) {
|
|
is_shadowed = true;
|
|
}
|
|
|
|
RAY_delete_intersections(intersections);
|
|
|
|
return is_shadowed;
|
|
}
|
|
|
|
void WORLD_delete_all_objects(WORLD_World *world) {
|
|
assert(world);
|
|
for (unsigned int ndx = 0; ndx < world->object_count; ndx++) {
|
|
SHAPE_delete_any_type(world->objects[ndx]);
|
|
}
|
|
}
|
|
|
|
void WORLD_reflected_color(TUPLES_Color *dest, const WORLD_World *world, const RAY_Computations *comps, unsigned int ttl) {
|
|
assert(dest);
|
|
assert(world);
|
|
assert(comps);
|
|
const MATERIAL_Material *material = SHAPE_get_material(comps->object);
|
|
if (ttl < 1 || double_equal(0.0, material->reflective)) {
|
|
TUPLES_init_color(dest, 0, 0, 0);
|
|
} else {
|
|
RAY_Ray reflect_ray;
|
|
RAY_init_from_tuples(&reflect_ray, &comps->over_point, &comps->reflectv);
|
|
WORLD_color_at(dest, world, &reflect_ray, ttl - 1);
|
|
TUPLES_multiply(dest, dest, material->reflective);
|
|
}
|
|
}
|
|
|
|
void WORLD_refracted_color(TUPLES_Color *dest, const WORLD_World *world, const RAY_Computations *comps, unsigned int ttl) {
|
|
assert(dest);
|
|
assert(world);
|
|
assert(comps);
|
|
const MATERIAL_Material *material = SHAPE_get_material(comps->object);
|
|
if (ttl < 1 || double_equal(0.0, material->transparency)) {
|
|
TUPLES_init_color(dest, 0, 0, 0);
|
|
} else {
|
|
double n_ratio = comps->n1 / comps->n2;
|
|
double cos_i = TUPLES_dot(&comps->eyev, &comps->normalv);
|
|
double sin2_t = pow(n_ratio, 2) * (1 - pow(cos_i, 2));
|
|
if (sin2_t > 1.0) {
|
|
// total internal reflection
|
|
TUPLES_init_color(dest, 0, 0, 0);
|
|
} else {
|
|
// calculate actual color
|
|
double cos_t = sqrt(1.0 - sin2_t);
|
|
TUPLES_Vector normalv_calc, eyev_calc, direction;
|
|
TUPLES_multiply(&normalv_calc, &comps->normalv, n_ratio * cos_i - cos_t);
|
|
TUPLES_multiply(&eyev_calc, &comps->eyev, n_ratio);
|
|
TUPLES_subtract(&direction, &normalv_calc, &eyev_calc);
|
|
|
|
RAY_Ray refract_ray;
|
|
RAY_init_from_tuples(&refract_ray, &comps->under_point, &direction);
|
|
|
|
WORLD_color_at(dest, world, &refract_ray, ttl - 1);
|
|
TUPLES_multiply(dest, dest, material->transparency);
|
|
}
|
|
}
|
|
}
|