raytracer-c/module_raytracer/material.c

188 lines
5.9 KiB
C

#include "material.h"
#include "pattern.h"
#include "yamlloader.h"
#include <assert.h>
#include <logger.h>
#include <math.h>
#include <memory.h>
MATERIAL_Material *MATERIAL_new(void) {
MATERIAL_Material *m = malloc(sizeof(MATERIAL_Material));
TUPLES_init_color(&m->color, 1, 1, 1);
m->ambient = 0.1;
m->diffuse = 0.9;
m->specular = 0.9;
m->shininess = 200.0;
m->reflective = 0.0;
m->transparency = 0.0;
m->refractive_index = 1.0;
m->pattern = NULL;
m->shadow_calc = true;
return m;
}
void MATERIAL_delete(MATERIAL_Material *m) {
assert(m);
TUPLES_destroy(&m->color);
if (m->pattern) {
PATTERN_delete(m->pattern);
}
free(m);
}
bool MATERIAL_is_equal(const MATERIAL_Material *m1, const MATERIAL_Material *m2) {
return TUPLES_is_equal(&m1->color, &m2->color) && double_equal(m1->diffuse, m2->diffuse) && double_equal(m1->shininess, m2->shininess) &&
double_equal(m1->ambient, m2->ambient) && double_equal(m1->specular, m2->specular);
}
MATERIAL_Material *MATERIAL_new_copy(const MATERIAL_Material *src) {
assert(src);
MATERIAL_Material *dest = MATERIAL_new();
*dest = *src;
if (src->pattern) {
dest->pattern = PATTERN_new_copy(src->pattern);
}
return dest;
}
struct calculate_lighting_context {
TUPLES_Color sum;
double intensity;
const MATERIAL_Material *material;
const TUPLES_Point *position;
const TUPLES_Vector *normal_vector;
const TUPLES_Vector *eye_vector;
const TUPLES_Color *effective_color;
const TUPLES_Color *light_color;
};
static void calculate_lighting(TUPLES_Point *light_point, void *context) {
struct calculate_lighting_context *c = (struct calculate_lighting_context *)context;
TUPLES_Color diffuse;
TUPLES_Color specular;
TUPLES_Vector lightv;
TUPLES_subtract(&lightv, light_point, c->position);
TUPLES_normalize(&lightv);
double light_dot_normal = TUPLES_dot(&lightv, c->normal_vector);
if (light_dot_normal < 0) {
// negative means light is on other side of surface
TUPLES_init_color(&diffuse, 0, 0, 0);
TUPLES_init_color(&specular, 0, 0, 0);
} else {
TUPLES_multiply(&diffuse, c->effective_color, c->material->diffuse * light_dot_normal);
TUPLES_negate(&lightv);
TUPLES_Vector reflectv;
TUPLES_reflect(&reflectv, &lightv, c->normal_vector);
double reflect_dot_eye = TUPLES_dot(&reflectv, c->eye_vector);
if (reflect_dot_eye < 0 || double_equal(0.0, reflect_dot_eye)) {
// light is reflecting away
TUPLES_init_color(&specular, 0, 0, 0);
} else {
double factor = pow(reflect_dot_eye, c->material->shininess);
TUPLES_multiply(&specular, c->light_color, c->material->specular * factor);
}
}
TUPLES_add(&c->sum, &c->sum, &diffuse);
TUPLES_add(&c->sum, &c->sum, &specular);
}
void MATERIAL_lighting(TUPLES_Color *dest, const SHAPE_Shape *shape, const LIGHTS_Light *light, const TUPLES_Point *position, const TUPLES_Vector *eye_vector,
const TUPLES_Vector *normal_vector, double intensity) {
assert(dest);
assert(shape);
assert(light);
assert(position);
assert(eye_vector);
assert(normal_vector);
assert(shape->material);
TUPLES_Color color_or_pattern;
const MATERIAL_Material *material = shape->material;
if (material->pattern) {
PATTERN_color_at_shape(&color_or_pattern, material->pattern, shape, position);
} else {
TUPLES_copy(&color_or_pattern, &shape->material->color);
}
TUPLES_Color effective_color;
TUPLES_multiply_colors(&effective_color, &color_or_pattern, &light->intensity);
/* calculate ambient contribution */
TUPLES_multiply(dest, &effective_color, material->ambient);
struct calculate_lighting_context context = (struct calculate_lighting_context){
.intensity = intensity,
.material = material,
.position = position,
.normal_vector = normal_vector,
.eye_vector = eye_vector,
.effective_color = &effective_color,
.light_color = &light->intensity,
};
TUPLES_init_color(&context.sum, 0, 0, 0);
LIGHTS_iterate_points_on_light(light, &calculate_lighting, &context);
TUPLES_divide(&context.sum, &context.sum, light->samples);
TUPLES_multiply(&context.sum, &context.sum, intensity);
TUPLES_add(dest, dest, &context.sum);
}
void MATERIAL_set_pattern(MATERIAL_Material *material, const PATTERN_Pattern *pattern) {
assert(material);
assert(pattern);
if (material->pattern) {
PATTERN_delete(material->pattern);
}
material->pattern = PATTERN_new_copy(pattern);
}
bool MATERIAL_casts_shadow(const MATERIAL_Material *m) {
assert(m);
return m->shadow_calc;
}
void parse_material_map_entry(char *key, char *value, void *context) {
assert(key);
assert(value);
assert(context);
MATERIAL_Material *mat = (MATERIAL_Material *)context;
if (strcmp("color", key) == 0) {
YAMLLOADER_parse_color(value, &mat->color);
} else if (strcmp("ambient", key) == 0) {
YAMLLOADER_parse_double(value, &mat->ambient);
} else if (strcmp("diffuse", key) == 0) {
YAMLLOADER_parse_double(value, &mat->diffuse);
} else if (strcmp("specular", key) == 0) {
YAMLLOADER_parse_double(value, &mat->specular);
} else if (strcmp("shininess", key) == 0) {
YAMLLOADER_parse_double(value, &mat->shininess);
} else if (strcmp("reflective", key) == 0) {
YAMLLOADER_parse_double(value, &mat->reflective);
} else if (strcmp("transparency", key) == 0) {
YAMLLOADER_parse_double(value, &mat->transparency);
} else if (strcmp("refractive-index", key) == 0) {
YAMLLOADER_parse_double(value, &mat->refractive_index);
} else if (strcmp("shadow", key) == 0) {
YAMLLOADER_parse_bool(value, &mat->shadow_calc);
} else {
LOGGER_log(LOGGER_WARN, "Unrecognized map key while parsing material: %s", key);
}
}
MATERIAL_Material *MATERIAL_parse_material(char *data) {
assert(data);
MATERIAL_Material *material = MATERIAL_new();
//No validation, as there are no required fields...
YAMLLOADER_parse_map_entries(data, parse_material_map_entry, material);
return material;
}