Move to unions instead of structure headers

Using explicit pointer casts silences some compiler warnings that may
introduce subtle bugs in the code. The union approach is the officially
documented way of doing type punning, so there is no reason not to use
it.
This commit is contained in:
Jean-Michel Gorius 2022-11-13 13:52:56 +01:00
parent f23e454358
commit 9b0abbc5bb
5 changed files with 230 additions and 224 deletions

View File

@ -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); 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, bool hittable_hit(const Hittable *hittable, Ray r, double t_min, double t_max,
HitRecord *record) { HitRecord *record) {
switch (hittable->type) { switch (hittable->type) {
case HITTABLE_LIST: case HITTABLE_LIST:
return hittable_list_hit((const HittableList *)hittable, r, t_min, t_max, return hittable_list_hit(&hittable->list, r, t_min, t_max, record);
record);
case HITTABLE_SPHERE: 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: case HITTABLE_MOVING_SPHERE:
return moving_sphere_hit((const MovingSphere *)hittable, r, t_min, t_max, return moving_sphere_hit(&hittable->moving_sphere, r, t_min, t_max, record);
record);
case HITTABLE_BVH_NODE: 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; return false;
} }
@ -37,27 +120,21 @@ bool hittable_bounding_box(const Hittable *hittable, double time_start,
double time_end, AABB *bounding_box) { double time_end, AABB *bounding_box) {
switch (hittable->type) { switch (hittable->type) {
case HITTABLE_LIST: case HITTABLE_LIST:
return hittable_list_bounding_box((const HittableList *)hittable, return hittable_list_bounding_box(&hittable->list, time_start, time_end,
time_start, time_end, bounding_box); bounding_box);
case HITTABLE_SPHERE: 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); bounding_box);
case HITTABLE_MOVING_SPHERE: case HITTABLE_MOVING_SPHERE:
return moving_sphere_bounding_box((const MovingSphere *)hittable, return moving_sphere_bounding_box(&hittable->moving_sphere, time_start,
time_start, time_end, bounding_box); time_end, bounding_box);
case HITTABLE_BVH_NODE: case HITTABLE_BVH_NODE:
return bvh_node_bounding_box((const BVHNode *)hittable, time_start, return bvh_node_bounding_box(&hittable->bvh_node, time_start, time_end,
time_end, bounding_box); bounding_box);
} }
return false; 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) { static void hittable_list_grow(HittableList *list, size_t n, Arena *arena) {
if (list->objects) { if (list->objects) {
const Hittable **new_objects = const Hittable **new_objects =
@ -114,16 +191,6 @@ bool hittable_list_bounding_box(const HittableList *list, double time_start,
return true; 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, bool sphere_hit(const Sphere *sphere, Ray r, double t_min, double t_max,
HitRecord *record) { HitRecord *record) {
Vec3 oc = point3_sub(r.origin, sphere->center); Vec3 oc = point3_sub(r.origin, sphere->center);
@ -163,25 +230,20 @@ bool sphere_bounding_box(const Sphere *sphere, double time_start,
return true; return true;
} }
MovingSphere *moving_sphere_create(Point3 center_start, Point3 center_end, Hittable *hittable_create_moving_sphere(Point3 center_start, Point3 center_end,
double start, double end, double radius, double start, double end, double radius,
const Material *material, Arena *arena) { const Material *material,
Arena *arena) {
assert(start <= end); assert(start <= end);
MovingSphere *sphere = arena_alloc(arena, sizeof(MovingSphere)); Hittable *result = arena_alloc(arena, sizeof(Hittable));
sphere->type = HITTABLE_MOVING_SPHERE; result->type = HITTABLE_MOVING_SPHERE;
sphere->center_start = center_start; result->moving_sphere.center_start = center_start;
sphere->center_end = center_end; result->moving_sphere.center_end = center_end;
sphere->start = start; result->moving_sphere.start = start;
sphere->end = end; result->moving_sphere.end = end;
sphere->radius = radius; result->moving_sphere.radius = radius;
sphere->material = material; result->moving_sphere.material = material;
return sphere; 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));
} }
bool moving_sphere_hit(const MovingSphere *sphere, Ray r, double t_min, 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; 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, bool bvh_node_hit(const BVHNode *node, Ray r, double t_min, double t_max,
HitRecord *record) { HitRecord *record) {
if (!aabb_hit(&node->box, r, t_min, t_max)) if (!aabb_hit(&node->box, r, t_min, t_max))

View File

@ -28,23 +28,58 @@ typedef enum HittableType {
HITTABLE_BVH_NODE, HITTABLE_BVH_NODE,
} HittableType; } 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; 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, bool hittable_hit(const Hittable *hittable, Ray r, double t_min, double t_max,
HitRecord *record); HitRecord *record);
bool hittable_bounding_box(const Hittable *hittable, double time_start, bool hittable_bounding_box(const Hittable *hittable, double time_start,
double time_end, AABB *bounding_box); 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, void hittable_list_add(HittableList *list, const Hittable *hittable,
Arena *arena); Arena *arena);
bool hittable_list_hit(const HittableList *list, Ray r, double t_min, 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, bool hittable_list_bounding_box(const HittableList *list, double time_start,
double time_end, AABB *bounding_box); 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, bool sphere_hit(const Sphere *sphere, Ray r, double t_min, double t_max,
HitRecord *record); HitRecord *record);
bool sphere_bounding_box(const Sphere *sphere, double time_start, bool sphere_bounding_box(const Sphere *sphere, double time_start,
double time_end, AABB *bounding_box); 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); Point3 moving_sphere_center(const MovingSphere *sphere, double t);
bool moving_sphere_hit(const MovingSphere *sphere, Ray r, double t_min, bool moving_sphere_hit(const MovingSphere *sphere, Ray r, double t_min,
double t_max, HitRecord *record); double t_max, HitRecord *record);
bool moving_sphere_bounding_box(const MovingSphere *sphere, double time_start, bool moving_sphere_bounding_box(const MovingSphere *sphere, double time_start,
double time_end, AABB *bounding_box); 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, bool bvh_node_hit(const BVHNode *node, Ray r, double t_min, double t_max,
HitRecord *record); HitRecord *record);
bool bvh_node_bounding_box(const BVHNode *node, double time_start, bool bvh_node_bounding_box(const BVHNode *node, double time_start,

71
main.c
View File

@ -35,16 +35,15 @@ static Color ray_color(Ray r, const Hittable *world, int depth) {
} }
static const Hittable *generate_random_scene(Arena *arena) { 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 = const Material *ground_material =
lambertian_create((Color){0.5, 0.5, 0.5}, arena); material_create_lambertian((Color){0.5, 0.5, 0.5}, arena);
const Sphere *ground_sphere = const Hittable *ground_sphere = hittable_create_sphere((Point3){0.0, -1000.0, 0.0},
sphere_create((Point3){0.0, -1000.0, 0.0}, 1000.0, 1000.0, ground_material, arena);
(const Material *)ground_material, arena); hittable_list_add(&world->list, ground_sphere, arena);
hittable_list_add(world, (const Hittable *)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 a = -11; a < 11; ++a) {
for (int b = -11; b < 11; ++b) { 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 (vec3_length(point3_sub(center, (Point3){4.0, 0.2, 0.0})) > 0.9) {
if (choose_material < 0.8) { if (choose_material < 0.8) {
const Lambertian *material = lambertian_create( const Material *material = material_create_lambertian(
color_mul(color_random(), color_random()), arena); color_mul(color_random(), color_random()), arena);
Point3 center2 = point3_add( Point3 center2 = point3_add(
center, (Vec3){0, random_double_in_range(0.0, 0.5), 0}); center, (Vec3){0, random_double_in_range(0.0, 0.5), 0});
const MovingSphere *sphere = const Hittable *sphere = hittable_create_moving_sphere(
moving_sphere_create(center, center2, 0.0, 1.0, 0.2, center, center2, 0.0, 1.0, 0.2, material, arena);
(const Material *)material, arena); hittable_list_add(&world->list, sphere, arena);
hittable_list_add(world, (const Hittable *)sphere, arena);
} else if (choose_material < 0.95) { } else if (choose_material < 0.95) {
const Metal *material = const Material *material =
metal_create(color_random_in_range(0.5, 1), material_create_metal(color_random_in_range(0.5, 1),
random_double_in_range(0.5, 1.0), arena); random_double_in_range(0.5, 1.0), arena);
const Sphere *sphere = const Hittable *sphere = hittable_create_sphere(center, 0.2, material, arena);
sphere_create(center, 0.2, (const Material *)material, arena); hittable_list_add(&world->list, sphere, arena);
hittable_list_add(world, (const Hittable *)sphere, arena);
} else { } else {
const Sphere *sphere = const Hittable *sphere = hittable_create_sphere(center, 0.2, glass, arena);
sphere_create(center, 0.2, (const Material *)glass, arena); hittable_list_add(&world->list, sphere, arena);
hittable_list_add(world, (const Hittable *)sphere, arena);
} }
} }
} }
} }
const Lambertian *lambertian = const Material *lambertian =
lambertian_create((Color){0.4, 0.2, 0.1}, arena); material_create_lambertian((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 *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 Hittable *sphere1 =
(const Material *)glass, arena); hittable_create_sphere((Point3){0.0, 1.0, 0.0}, 1.0, glass, arena);
hittable_list_add(world, (const Hittable *)sphere1, arena); hittable_list_add(&world->list, sphere1, arena);
const Sphere *sphere2 = sphere_create((Point3){-4.0, 1.0, 0.0}, 1.0, const Hittable *sphere2 =
(const Material *)lambertian, arena); hittable_create_sphere((Point3){-4.0, 1.0, 0.0}, 1.0, lambertian, arena);
hittable_list_add(world, (const Hittable *)sphere2, arena); hittable_list_add(&world->list, sphere2, arena);
const Sphere *sphere3 = sphere_create((Point3){4.0, 1.0, 0.0}, 1.0, const Hittable *sphere3 =
(const Material *)metal, arena); hittable_create_sphere((Point3){4.0, 1.0, 0.0}, 1.0, metal, arena);
hittable_list_add(world, (const Hittable *)sphere3, arena); hittable_list_add(&world->list, sphere3, arena);
BVHNode *bvh_root = Hittable *bvh_root = hittable_create_bvh_node(world->list.objects, 0, world->list.size,
bvh_node_create(world->objects, 0, world->size, 0.0, 1.0, arena); 0.0, 1.0, arena);
return (const Hittable *)bvh_root; return bvh_root;
} }
int main(void) { int main(void) {

View File

@ -12,23 +12,22 @@ bool material_scatter(const Material *material, Ray r,
Ray *scattered) { Ray *scattered) {
switch (material->type) { switch (material->type) {
case MATERIAL_LAMBERTIAN: case MATERIAL_LAMBERTIAN:
return lambertian_scatter((const Lambertian *)material, r, record, return lambertian_scatter(&material->lambertian, r, record, attenuation,
attenuation, scattered); scattered);
case MATERIAL_METAL: case MATERIAL_METAL:
return metal_scatter((const Metal *)material, r, record, attenuation, return metal_scatter(&material->metal, r, record, attenuation, scattered);
scattered);
case MATERIAL_DIELECTRIC: case MATERIAL_DIELECTRIC:
return dielectric_scatter((const Dielectric *)material, r, record, return dielectric_scatter(&material->dielectric, r, record, attenuation,
attenuation, scattered); scattered);
} }
return false; return false;
} }
Lambertian *lambertian_create(Color albedo, Arena *arena) { Material *material_create_lambertian(Color albedo, Arena *arena) {
Lambertian *lambertian = arena_alloc(arena, sizeof(Lambertian)); Material *result = arena_alloc(arena, sizeof(Material));
lambertian->type = MATERIAL_LAMBERTIAN; result->type = MATERIAL_LAMBERTIAN;
lambertian->albedo = albedo; result->lambertian.albedo = albedo;
return lambertian; return result;
} }
bool lambertian_scatter(const Lambertian *lambertian, Ray r, bool lambertian_scatter(const Lambertian *lambertian, Ray r,
@ -46,12 +45,12 @@ bool lambertian_scatter(const Lambertian *lambertian, Ray r,
return true; return true;
} }
Metal *metal_create(Color albedo, double fuzziness, Arena *arena) { Material *material_create_metal(Color albedo, double fuzziness, Arena *arena) {
Metal *metal = arena_alloc(arena, sizeof(Metal)); Material *result = arena_alloc(arena, sizeof(Material));
metal->type = MATERIAL_METAL; result->type = MATERIAL_METAL;
metal->albedo = albedo; result->metal.albedo = albedo;
metal->fuzziness = fuzziness; result->metal.fuzziness = fuzziness;
return metal; return result;
} }
bool metal_scatter(const Metal *metal, Ray r, const struct HitRecord *record, 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; return vec3_dot(scattered->direction, record->normal) > 0;
} }
Dielectric *dielectric_create(double eta, Arena *arena) { Material *material_create_dielectric(double eta, Arena *arena) {
Dielectric *dielectric = arena_alloc(arena, sizeof(Dielectric)); Material *result = arena_alloc(arena, sizeof(Material));
dielectric->type = MATERIAL_DIELECTRIC; result->type = MATERIAL_DIELECTRIC;
dielectric->eta = eta; result->dielectric.eta = eta;
return dielectric; return result;
} }
static double schlick_reflectance(double cosine, double eta) { static double schlick_reflectance(double cosine, double eta) {

View File

@ -15,40 +15,42 @@ typedef enum MaterialType {
MATERIAL_DIELECTRIC, MATERIAL_DIELECTRIC,
} MaterialType; } MaterialType;
typedef struct Lambertian {
Color albedo;
} Lambertian;
typedef struct Metal {
Color albedo;
double fuzziness;
} Metal;
typedef struct Dielectric {
double eta;
} Dielectric;
typedef struct Material { typedef struct Material {
MaterialType type; MaterialType type;
union {
Lambertian lambertian;
Metal metal;
Dielectric dielectric;
};
} Material; } Material;
bool material_scatter(const Material *material, Ray r, bool material_scatter(const Material *material, Ray r,
const struct HitRecord *record, Color *attenuation, const struct HitRecord *record, Color *attenuation,
Ray *scattered); Ray *scattered);
typedef struct Lambertian { Material *material_create_lambertian(Color albedo, Arena *arena);
MaterialType type;
Color albedo;
} Lambertian;
Lambertian *lambertian_create(Color albedo, Arena *arena);
bool lambertian_scatter(const Lambertian *lambertian, Ray r, bool lambertian_scatter(const Lambertian *lambertian, Ray r,
const struct HitRecord *record, Color *attenuation, const struct HitRecord *record, Color *attenuation,
Ray *scattered); Ray *scattered);
typedef struct Metal { Material *material_create_metal(Color albedo, double fuzziness, Arena *arena);
MaterialType type;
Color albedo;
double fuzziness;
} Metal;
Metal *metal_create(Color albedo, double fuzziness, Arena *arena);
bool metal_scatter(const Metal *metal, Ray r, const struct HitRecord *record, bool metal_scatter(const Metal *metal, Ray r, const struct HitRecord *record,
Color *attenuation, Ray *scattered); Color *attenuation, Ray *scattered);
typedef struct Dielectric { Material *material_create_dielectric(double eta, Arena *arena);
MaterialType type;
double eta;
} Dielectric;
Dielectric *dielectric_create(double eta, Arena *arena);
bool dielectric_scatter(const Dielectric *dielectric, Ray r, bool dielectric_scatter(const Dielectric *dielectric, Ray r,
const struct HitRecord *record, Color *attenuation, const struct HitRecord *record, Color *attenuation,
Ray *scattered); Ray *scattered);