diff --git a/hittable.c b/hittable.c index e3bf5de..a753618 100644 --- a/hittable.c +++ b/hittable.c @@ -16,19 +16,102 @@ void hit_record_set_face_normal(HitRecord *record, Ray r, Vec3 outward_normal) { record->front_face ? outward_normal : vec3_neg(outward_normal); } +Hittable *hittable_create_hittable_list(Arena *arena) { + Hittable *result = arena_alloc(arena, sizeof(Hittable)); + result->type = HITTABLE_LIST; + return result; +} + +Hittable *hittable_create_sphere(Point3 center, double radius, + const Material *material, Arena *arena) { + Hittable *result = arena_alloc(arena, sizeof(Hittable)); + result->type = HITTABLE_SPHERE; + result->sphere.center = center; + result->sphere.radius = radius; + result->sphere.material = material; + return result; +} + +Point3 moving_sphere_center(const MovingSphere *sphere, double t) { + Vec3 dir = point3_sub(sphere->center_end, sphere->center_start); + double c = (t - sphere->start) / (sphere->end - sphere->start); + return point3_add(sphere->center_start, vec3_mul(c, dir)); +} + +typedef int BoxCompareFunc(const void *lhs, const void *rhs); + +#define BOX_COMPARATOR(axis) \ + static int box_##axis##_compare(const void *lhs, const void *rhs) { \ + AABB lhs_box, rhs_box; \ + if (!hittable_bounding_box(*(const Hittable **)lhs, 0, 0, &lhs_box) || \ + !hittable_bounding_box(*(const Hittable **)rhs, 0, 0, &rhs_box)) { \ + fprintf(stderr, "No bounding-box in BVH node"); \ + exit(1); \ + } \ + \ + return lhs_box.min.axis < rhs_box.min.axis; \ + } +BOX_COMPARATOR(x) +BOX_COMPARATOR(y) +BOX_COMPARATOR(z) +#undef BOX_COMPARATOR + +Hittable *hittable_create_bvh_node(const Hittable **objects, size_t start, + size_t end, double time_start, + double time_end, Arena *arena) { + Hittable *result = arena_alloc(arena, sizeof(Hittable)); + result->type = HITTABLE_BVH_NODE; + + int axis = random_int_in_range(0, 2); + BoxCompareFunc *comparator = (axis == 0) ? box_x_compare + : (axis == 1) ? box_y_compare + : box_z_compare; + + size_t object_span = end - start; + if (object_span == 1) { + result->bvh_node.left = result->bvh_node.right = objects[start]; + } else if (object_span == 2) { + if (comparator(&objects[start], &objects[start + 1])) { + result->bvh_node.left = objects[start]; + result->bvh_node.right = objects[start + 1]; + } else { + result->bvh_node.left = objects[start + 1]; + result->bvh_node.right = objects[start]; + } + } else { + qsort(objects + start, object_span, sizeof(const Hittable *), comparator); + size_t mid = start + object_span / 2; + result->bvh_node.left = hittable_create_bvh_node( + objects, start, mid, time_start, time_end, arena); + result->bvh_node.right = hittable_create_bvh_node( + objects, mid, end, time_start, time_end, arena); + } + + AABB left_box, right_box; + + if (!hittable_bounding_box(result->bvh_node.left, time_start, time_end, + &left_box) || + !hittable_bounding_box(result->bvh_node.right, time_start, time_end, + &right_box)) { + fprintf(stderr, "No bounding-box in BVH node"); + exit(1); + } + + result->bvh_node.box = aabb_surrounding_box(&left_box, &right_box); + return result; +} + bool hittable_hit(const Hittable *hittable, Ray r, double t_min, double t_max, HitRecord *record) { switch (hittable->type) { case HITTABLE_LIST: - return hittable_list_hit((const HittableList *)hittable, r, t_min, t_max, - record); + return hittable_list_hit(&hittable->list, r, t_min, t_max, record); case HITTABLE_SPHERE: - return sphere_hit((const Sphere *)hittable, r, t_min, t_max, record); + return sphere_hit(&hittable->sphere, r, t_min, t_max, record); case HITTABLE_MOVING_SPHERE: - return moving_sphere_hit((const MovingSphere *)hittable, r, t_min, t_max, - record); + return moving_sphere_hit(&hittable->moving_sphere, r, t_min, t_max, record); case HITTABLE_BVH_NODE: - return bvh_node_hit((const BVHNode *)hittable, r, t_min, t_max, record); + return bvh_node_hit(&hittable->bvh_node, r, t_min, t_max, record); } return false; } @@ -37,27 +120,21 @@ bool hittable_bounding_box(const Hittable *hittable, double time_start, double time_end, AABB *bounding_box) { switch (hittable->type) { case HITTABLE_LIST: - return hittable_list_bounding_box((const HittableList *)hittable, - time_start, time_end, bounding_box); + return hittable_list_bounding_box(&hittable->list, time_start, time_end, + bounding_box); case HITTABLE_SPHERE: - return sphere_bounding_box((const Sphere *)hittable, time_start, time_end, + return sphere_bounding_box(&hittable->sphere, time_start, time_end, bounding_box); case HITTABLE_MOVING_SPHERE: - return moving_sphere_bounding_box((const MovingSphere *)hittable, - time_start, time_end, bounding_box); + return moving_sphere_bounding_box(&hittable->moving_sphere, time_start, + time_end, bounding_box); case HITTABLE_BVH_NODE: - return bvh_node_bounding_box((const BVHNode *)hittable, time_start, - time_end, bounding_box); + return bvh_node_bounding_box(&hittable->bvh_node, time_start, time_end, + bounding_box); } return false; } -HittableList *hittable_list_create(Arena *arena) { - HittableList *list = arena_alloc(arena, sizeof(HittableList)); - list->type = HITTABLE_LIST; - return list; -} - static void hittable_list_grow(HittableList *list, size_t n, Arena *arena) { if (list->objects) { const Hittable **new_objects = @@ -114,16 +191,6 @@ bool hittable_list_bounding_box(const HittableList *list, double time_start, return true; } -Sphere *sphere_create(Point3 center, double radius, const Material *material, - Arena *arena) { - Sphere *sphere = arena_alloc(arena, sizeof(Sphere)); - sphere->type = HITTABLE_SPHERE; - sphere->center = center; - sphere->radius = radius; - sphere->material = material; - return sphere; -} - bool sphere_hit(const Sphere *sphere, Ray r, double t_min, double t_max, HitRecord *record) { Vec3 oc = point3_sub(r.origin, sphere->center); @@ -163,25 +230,20 @@ bool sphere_bounding_box(const Sphere *sphere, double time_start, return true; } -MovingSphere *moving_sphere_create(Point3 center_start, Point3 center_end, - double start, double end, double radius, - const Material *material, Arena *arena) { +Hittable *hittable_create_moving_sphere(Point3 center_start, Point3 center_end, + double start, double end, double radius, + const Material *material, + Arena *arena) { assert(start <= end); - MovingSphere *sphere = arena_alloc(arena, sizeof(MovingSphere)); - sphere->type = HITTABLE_MOVING_SPHERE; - sphere->center_start = center_start; - sphere->center_end = center_end; - sphere->start = start; - sphere->end = end; - sphere->radius = radius; - sphere->material = material; - return sphere; -} - -Point3 moving_sphere_center(const MovingSphere *sphere, double t) { - Vec3 dir = point3_sub(sphere->center_end, sphere->center_start); - double c = (t - sphere->start) / (sphere->end - sphere->start); - return point3_add(sphere->center_start, vec3_mul(c, dir)); + Hittable *result = arena_alloc(arena, sizeof(Hittable)); + result->type = HITTABLE_MOVING_SPHERE; + result->moving_sphere.center_start = center_start; + result->moving_sphere.center_end = center_end; + result->moving_sphere.start = start; + result->moving_sphere.end = end; + result->moving_sphere.radius = radius; + result->moving_sphere.material = material; + return result; } bool moving_sphere_hit(const MovingSphere *sphere, Ray r, double t_min, @@ -232,66 +294,6 @@ bool moving_sphere_bounding_box(const MovingSphere *sphere, double time_start, return true; } -typedef int BoxCompareFunc(const void *lhs, const void *rhs); - -#define BOX_COMPARATOR(axis) \ - static int box_##axis##_compare(const void *lhs, const void *rhs) { \ - AABB lhs_box, rhs_box; \ - if (!hittable_bounding_box(*(const Hittable **)lhs, 0, 0, &lhs_box) || \ - !hittable_bounding_box(*(const Hittable **)rhs, 0, 0, &rhs_box)) { \ - fprintf(stderr, "No bounding-box in BVH node"); \ - exit(1); \ - } \ - \ - return lhs_box.min.axis < rhs_box.min.axis; \ - } -BOX_COMPARATOR(x) -BOX_COMPARATOR(y) -BOX_COMPARATOR(z) -#undef BOX_COMPARATOR - -BVHNode *bvh_node_create(const Hittable **objects, size_t start, size_t end, - double time_start, double time_end, Arena *arena) { - BVHNode *node = arena_alloc(arena, sizeof(BVHNode)); - node->type = HITTABLE_BVH_NODE; - - int axis = random_int_in_range(0, 2); - BoxCompareFunc *comparator = (axis == 0) ? box_x_compare - : (axis == 1) ? box_y_compare - : box_z_compare; - - size_t object_span = end - start; - if (object_span == 1) { - node->left = node->right = objects[start]; - } else if (object_span == 2) { - if (comparator(&objects[start], &objects[start + 1])) { - node->left = objects[start]; - node->right = objects[start + 1]; - } else { - node->left = objects[start + 1]; - node->right = objects[start]; - } - } else { - qsort(objects + start, object_span, sizeof(const Hittable *), comparator); - size_t mid = start + object_span / 2; - node->left = (const Hittable *)bvh_node_create(objects, start, mid, - time_start, time_end, arena); - node->right = (const Hittable *)bvh_node_create( - objects, mid, end, time_start, time_end, arena); - } - - AABB left_box, right_box; - - if (!hittable_bounding_box(node->left, time_start, time_end, &left_box) || - !hittable_bounding_box(node->right, time_start, time_end, &right_box)) { - fprintf(stderr, "No bounding-box in BVH node"); - exit(1); - } - - node->box = aabb_surrounding_box(&left_box, &right_box); - return node; -} - bool bvh_node_hit(const BVHNode *node, Ray r, double t_min, double t_max, HitRecord *record) { if (!aabb_hit(&node->box, r, t_min, t_max)) diff --git a/hittable.h b/hittable.h index 10e8b96..6757bac 100644 --- a/hittable.h +++ b/hittable.h @@ -28,23 +28,58 @@ typedef enum HittableType { HITTABLE_BVH_NODE, } HittableType; -typedef struct Hittable { +typedef struct Hittable Hittable; + +typedef struct HittableList { + const Hittable **objects; + size_t size; + size_t capacity; +} HittableList; + +typedef struct Sphere { + const Material *material; + Point3 center; + double radius; +} Sphere; + +typedef struct MovingSphere { + const Material *material; + Point3 center_start, center_end; + double start, end; + double radius; +} MovingSphere; + +typedef struct BVHNode { + const Hittable *left; + const Hittable *right; + AABB box; +} BVHNode; + +struct Hittable { HittableType type; -} Hittable; + union { + HittableList list; + Sphere sphere; + MovingSphere moving_sphere; + BVHNode bvh_node; + }; +}; + +Hittable *hittable_create_hittable_list(Arena *arena); +Hittable *hittable_create_sphere(Point3 center, double radius, + const Material *material, Arena *arena); +Hittable *hittable_create_moving_sphere(Point3 center_start, Point3 center_end, + double start, double end, double radius, + const Material *material, Arena *arena); +Hittable *hittable_create_bvh_node(const Hittable **objects, size_t start, + size_t end, double time_start, + double time_end, Arena *arena); bool hittable_hit(const Hittable *hittable, Ray r, double t_min, double t_max, HitRecord *record); bool hittable_bounding_box(const Hittable *hittable, double time_start, double time_end, AABB *bounding_box); -typedef struct HittableList { - HittableType type; - const Hittable **objects; - size_t size; - size_t capacity; -} HittableList; - -HittableList *hittable_list_create(Arena *arena); void hittable_list_add(HittableList *list, const Hittable *hittable, Arena *arena); bool hittable_list_hit(const HittableList *list, Ray r, double t_min, @@ -52,46 +87,17 @@ bool hittable_list_hit(const HittableList *list, Ray r, double t_min, bool hittable_list_bounding_box(const HittableList *list, double time_start, double time_end, AABB *bounding_box); -typedef struct Sphere { - HittableType type; - const Material *material; - Point3 center; - double radius; -} Sphere; - -Sphere *sphere_create(Point3 center, double radius, const Material *material, - Arena *arena); bool sphere_hit(const Sphere *sphere, Ray r, double t_min, double t_max, HitRecord *record); bool sphere_bounding_box(const Sphere *sphere, double time_start, double time_end, AABB *bounding_box); -typedef struct MovingSphere { - HittableType type; - const Material *material; - Point3 center_start, center_end; - double start, end; - double radius; -} MovingSphere; - -MovingSphere *moving_sphere_create(Point3 center_start, Point3 center_end, - double start, double end, double radius, - const Material *material, Arena *arena); Point3 moving_sphere_center(const MovingSphere *sphere, double t); bool moving_sphere_hit(const MovingSphere *sphere, Ray r, double t_min, double t_max, HitRecord *record); bool moving_sphere_bounding_box(const MovingSphere *sphere, double time_start, double time_end, AABB *bounding_box); -typedef struct BVHNode { - HittableType type; - const Hittable *left; - const Hittable *right; - AABB box; -} BVHNode; - -BVHNode *bvh_node_create(const Hittable **objects, size_t start, size_t end, - double time_start, double time_end, Arena *arena); bool bvh_node_hit(const BVHNode *node, Ray r, double t_min, double t_max, HitRecord *record); bool bvh_node_bounding_box(const BVHNode *node, double time_start, diff --git a/main.c b/main.c index 7ddd265..3309a53 100644 --- a/main.c +++ b/main.c @@ -35,16 +35,15 @@ static Color ray_color(Ray r, const Hittable *world, int depth) { } static const Hittable *generate_random_scene(Arena *arena) { - HittableList *world = hittable_list_create(arena); + Hittable *world = hittable_create_hittable_list(arena); - const Lambertian *ground_material = - lambertian_create((Color){0.5, 0.5, 0.5}, arena); - const Sphere *ground_sphere = - sphere_create((Point3){0.0, -1000.0, 0.0}, 1000.0, - (const Material *)ground_material, arena); - hittable_list_add(world, (const Hittable *)ground_sphere, arena); + const Material *ground_material = + material_create_lambertian((Color){0.5, 0.5, 0.5}, arena); + const Hittable *ground_sphere = hittable_create_sphere((Point3){0.0, -1000.0, 0.0}, + 1000.0, ground_material, arena); + hittable_list_add(&world->list, ground_sphere, arena); - const Dielectric *glass = dielectric_create(1.5, arena); + const Material *glass = material_create_dielectric(1.5, arena); for (int a = -11; a < 11; ++a) { for (int b = -11; b < 11; ++b) { @@ -54,47 +53,45 @@ static const Hittable *generate_random_scene(Arena *arena) { if (vec3_length(point3_sub(center, (Point3){4.0, 0.2, 0.0})) > 0.9) { if (choose_material < 0.8) { - const Lambertian *material = lambertian_create( + const Material *material = material_create_lambertian( color_mul(color_random(), color_random()), arena); Point3 center2 = point3_add( center, (Vec3){0, random_double_in_range(0.0, 0.5), 0}); - const MovingSphere *sphere = - moving_sphere_create(center, center2, 0.0, 1.0, 0.2, - (const Material *)material, arena); - hittable_list_add(world, (const Hittable *)sphere, arena); + const Hittable *sphere = hittable_create_moving_sphere( + center, center2, 0.0, 1.0, 0.2, material, arena); + hittable_list_add(&world->list, sphere, arena); } else if (choose_material < 0.95) { - const Metal *material = - metal_create(color_random_in_range(0.5, 1), - random_double_in_range(0.5, 1.0), arena); - const Sphere *sphere = - sphere_create(center, 0.2, (const Material *)material, arena); - hittable_list_add(world, (const Hittable *)sphere, arena); + const Material *material = + material_create_metal(color_random_in_range(0.5, 1), + random_double_in_range(0.5, 1.0), arena); + const Hittable *sphere = hittable_create_sphere(center, 0.2, material, arena); + hittable_list_add(&world->list, sphere, arena); } else { - const Sphere *sphere = - sphere_create(center, 0.2, (const Material *)glass, arena); - hittable_list_add(world, (const Hittable *)sphere, arena); + const Hittable *sphere = hittable_create_sphere(center, 0.2, glass, arena); + hittable_list_add(&world->list, sphere, arena); } } } } - const Lambertian *lambertian = - lambertian_create((Color){0.4, 0.2, 0.1}, arena); - const Metal *metal = metal_create((Color){0.7, 0.6, 0.5}, 0.0, arena); + const Material *lambertian = + material_create_lambertian((Color){0.4, 0.2, 0.1}, arena); + const Material *metal = + material_create_metal((Color){0.7, 0.6, 0.5}, 0.0, arena); - const Sphere *sphere1 = sphere_create((Point3){0.0, 1.0, 0.0}, 1.0, - (const Material *)glass, arena); - hittable_list_add(world, (const Hittable *)sphere1, arena); - const Sphere *sphere2 = sphere_create((Point3){-4.0, 1.0, 0.0}, 1.0, - (const Material *)lambertian, arena); - hittable_list_add(world, (const Hittable *)sphere2, arena); - const Sphere *sphere3 = sphere_create((Point3){4.0, 1.0, 0.0}, 1.0, - (const Material *)metal, arena); - hittable_list_add(world, (const Hittable *)sphere3, arena); + const Hittable *sphere1 = + hittable_create_sphere((Point3){0.0, 1.0, 0.0}, 1.0, glass, arena); + hittable_list_add(&world->list, sphere1, arena); + const Hittable *sphere2 = + hittable_create_sphere((Point3){-4.0, 1.0, 0.0}, 1.0, lambertian, arena); + hittable_list_add(&world->list, sphere2, arena); + const Hittable *sphere3 = + hittable_create_sphere((Point3){4.0, 1.0, 0.0}, 1.0, metal, arena); + hittable_list_add(&world->list, sphere3, arena); - BVHNode *bvh_root = - bvh_node_create(world->objects, 0, world->size, 0.0, 1.0, arena); - return (const Hittable *)bvh_root; + Hittable *bvh_root = hittable_create_bvh_node(world->list.objects, 0, world->list.size, + 0.0, 1.0, arena); + return bvh_root; } int main(void) { diff --git a/material.c b/material.c index 4d3fda5..b249d5f 100644 --- a/material.c +++ b/material.c @@ -12,23 +12,22 @@ bool material_scatter(const Material *material, Ray r, Ray *scattered) { switch (material->type) { case MATERIAL_LAMBERTIAN: - return lambertian_scatter((const Lambertian *)material, r, record, - attenuation, scattered); + return lambertian_scatter(&material->lambertian, r, record, attenuation, + scattered); case MATERIAL_METAL: - return metal_scatter((const Metal *)material, r, record, attenuation, - scattered); + return metal_scatter(&material->metal, r, record, attenuation, scattered); case MATERIAL_DIELECTRIC: - return dielectric_scatter((const Dielectric *)material, r, record, - attenuation, scattered); + return dielectric_scatter(&material->dielectric, r, record, attenuation, + scattered); } return false; } -Lambertian *lambertian_create(Color albedo, Arena *arena) { - Lambertian *lambertian = arena_alloc(arena, sizeof(Lambertian)); - lambertian->type = MATERIAL_LAMBERTIAN; - lambertian->albedo = albedo; - return lambertian; +Material *material_create_lambertian(Color albedo, Arena *arena) { + Material *result = arena_alloc(arena, sizeof(Material)); + result->type = MATERIAL_LAMBERTIAN; + result->lambertian.albedo = albedo; + return result; } bool lambertian_scatter(const Lambertian *lambertian, Ray r, @@ -46,12 +45,12 @@ bool lambertian_scatter(const Lambertian *lambertian, Ray r, return true; } -Metal *metal_create(Color albedo, double fuzziness, Arena *arena) { - Metal *metal = arena_alloc(arena, sizeof(Metal)); - metal->type = MATERIAL_METAL; - metal->albedo = albedo; - metal->fuzziness = fuzziness; - return metal; +Material *material_create_metal(Color albedo, double fuzziness, Arena *arena) { + Material *result = arena_alloc(arena, sizeof(Material)); + result->type = MATERIAL_METAL; + result->metal.albedo = albedo; + result->metal.fuzziness = fuzziness; + return result; } bool metal_scatter(const Metal *metal, Ray r, const struct HitRecord *record, @@ -68,11 +67,11 @@ bool metal_scatter(const Metal *metal, Ray r, const struct HitRecord *record, return vec3_dot(scattered->direction, record->normal) > 0; } -Dielectric *dielectric_create(double eta, Arena *arena) { - Dielectric *dielectric = arena_alloc(arena, sizeof(Dielectric)); - dielectric->type = MATERIAL_DIELECTRIC; - dielectric->eta = eta; - return dielectric; +Material *material_create_dielectric(double eta, Arena *arena) { + Material *result = arena_alloc(arena, sizeof(Material)); + result->type = MATERIAL_DIELECTRIC; + result->dielectric.eta = eta; + return result; } static double schlick_reflectance(double cosine, double eta) { diff --git a/material.h b/material.h index b6e12bd..fdf2ecb 100644 --- a/material.h +++ b/material.h @@ -15,40 +15,42 @@ typedef enum MaterialType { MATERIAL_DIELECTRIC, } MaterialType; +typedef struct Lambertian { + Color albedo; +} Lambertian; + +typedef struct Metal { + Color albedo; + double fuzziness; +} Metal; + +typedef struct Dielectric { + double eta; +} Dielectric; + typedef struct Material { MaterialType type; + union { + Lambertian lambertian; + Metal metal; + Dielectric dielectric; + }; } Material; bool material_scatter(const Material *material, Ray r, const struct HitRecord *record, Color *attenuation, Ray *scattered); -typedef struct Lambertian { - MaterialType type; - Color albedo; -} Lambertian; - -Lambertian *lambertian_create(Color albedo, Arena *arena); +Material *material_create_lambertian(Color albedo, Arena *arena); bool lambertian_scatter(const Lambertian *lambertian, Ray r, const struct HitRecord *record, Color *attenuation, Ray *scattered); -typedef struct Metal { - MaterialType type; - Color albedo; - double fuzziness; -} Metal; - -Metal *metal_create(Color albedo, double fuzziness, Arena *arena); +Material *material_create_metal(Color albedo, double fuzziness, Arena *arena); bool metal_scatter(const Metal *metal, Ray r, const struct HitRecord *record, Color *attenuation, Ray *scattered); -typedef struct Dielectric { - MaterialType type; - double eta; -} Dielectric; - -Dielectric *dielectric_create(double eta, Arena *arena); +Material *material_create_dielectric(double eta, Arena *arena); bool dielectric_scatter(const Dielectric *dielectric, Ray r, const struct HitRecord *record, Color *attenuation, Ray *scattered);