add Constructive Solid Geometry support #11

Merged
twistdroach merged 1 commits from csg into master 2020-10-04 20:33:42 -04:00
24 changed files with 492 additions and 164 deletions
Showing only changes of commit e7e9a85f77 - Show all commits

View File

@ -90,3 +90,9 @@ An obvious performance improvement would be to add some group bounding, so we do
In any event, flamegraphs are a fun way to see what is going on. In any event, flamegraphs are a fun way to see what is going on.
![](images/glass_teapot_flamegraph.svg) ![](images/glass_teapot_flamegraph.svg)
## main/csg
Add some constructive solid geometry - aka CSG...this is a sphere with a cubice bite taken out of it.
![](images/csg.png)

BIN
images/csg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

View File

@ -87,3 +87,11 @@ target_link_libraries(render_obj PRIVATE
module_patterns module_patterns
module_raytracer module_raytracer
module_shapes) module_shapes)
add_executable(csg
csg.c)
target_link_libraries(csg PRIVATE
module_patterns
module_raytracer
module_shapes)

115
main/csg.c Normal file
View File

@ -0,0 +1,115 @@
#include "logger.h"
#include "canvas.h"
#include "exceptions.h"
#include "ray.h"
#include <stdio.h>
#include <sphere.h>
#include <plane.h>
#include <camera.h>
#include "pattern.h"
#include <math.h>
#include <cube.h>
#include <csg.h>
CEXCEPTION_T e;
void build_world(WORLD_World* world) {
TUPLES_Color red, green, blue;
TUPLES_init_color(&red, 1, 0, 0);
TUPLES_init_color(&green, 0, 1, 0);
TUPLES_init_color(&blue, 0, 0, 1);
PLANE_Plane* floor = PLANE_new();
WORLD_add_object(world, floor);
MATERIAL_Material* material = MATERIAL_new();
material->specular = 0;
PATTERN_Pattern* floor_pattern = PATTERN_new_stripe(&red, &green);
MATRIX_Matrix* pattern_transform = MATRIX_new_rotation_y(-M_PI / 5);
PATTERN_set_transform(floor_pattern, pattern_transform);
MATRIX_delete(pattern_transform);
MATERIAL_set_pattern(material, floor_pattern);
PATTERN_delete(floor_pattern);
PLANE_set_material(floor, material);
MATERIAL_delete(material);
SPHERE_Sphere* middle = SPHERE_new();
MATRIX_Matrix* middle_transform = MATRIX_new_translation(-0.5, 1, 0.5);
SPHERE_set_transform(middle, middle_transform);
MATRIX_delete(middle_transform);
MATERIAL_Material* middle_material = MATERIAL_new();
PATTERN_Pattern* middle_pattern = PATTERN_new_gradient(&red, &blue);
MATRIX_Matrix* middle_pattern_translate = MATRIX_new_translation(0.5, 0, 0);
MATRIX_Matrix* middle_pattern_scale = MATRIX_new_scaling(2, 2, 2);
MATRIX_Matrix* middle_pattern_transform = MATRIX_multiply(middle_pattern_scale, middle_pattern_translate);
PATTERN_set_transform(middle_pattern, middle_pattern_transform);
MATRIX_delete_all(middle_pattern_scale, middle_pattern_transform, middle_pattern_translate);
MATERIAL_set_pattern(middle_material, middle_pattern);
PATTERN_delete(middle_pattern);
middle_material->diffuse = 0.7;
middle_material->specular = 0.3;
SPHERE_set_material(middle, middle_material);
MATERIAL_delete(middle_material);
CUBE_Cube* cube = CUBE_new();
MATRIX_Matrix* cube_translate = MATRIX_new_translation(0.5, 0.5, 0.35);
MATRIX_Matrix* cube_rot = MATRIX_new_rotation_y(M_PI_4);
MATRIX_Matrix* cube_transform = MATRIX_multiply_many(cube_rot, cube_translate);
CUBE_set_transform(cube, cube_transform);
MATRIX_delete_all(cube_transform, cube_rot, cube_translate);
MATERIAL_Material* cube_mat = MATERIAL_new();
cube_mat->transparency = 1.0;
cube_mat->ambient = 0.0;
cube_mat->diffuse = 0.0;
cube_mat->specular = 0.0;
cube_mat->shadow_calc = false;
CUBE_set_material(cube, cube_mat);
MATERIAL_delete(cube_mat);
CSG_Csg* csg = CSG_new(CSG_Difference, middle, cube);
WORLD_add_object(world, csg);
}
int main(void) {
Try {
LOGGER_log(LOGGER_INFO, "Building world...\n");
TUPLES_Point* light_position = TUPLES_new_point(10, 10, -10);
TUPLES_Color* light_color = TUPLES_new_color(1, 1, 1);
LIGHTS_PointLight* light = LIGHTS_new_pointlight(light_position, light_color);
TUPLES_delete_all(light_position, light_color);
CAMERA_Camera* camera = CAMERA_new(1000, 500, M_PI / 3.0);
TUPLES_Point* from = TUPLES_new_point(0, 1.5, -5);
TUPLES_Point* to = TUPLES_new_point(0, 1, 0);
TUPLES_Vector* up = TUPLES_new_vector(0, 1, 0);
MATRIX_Matrix* camera_transform = CAMERA_view_transform(from, to, up);
CAMERA_set_transform(camera, camera_transform);
TUPLES_delete_all(from, to, up);
MATRIX_delete(camera_transform);
WORLD_World* world = WORLD_new(light);
build_world(world);
LOGGER_log(LOGGER_INFO, "Rendering...\n");
UTILITIES_Timer* render_timer = UTILITIES_Timer_start();
CANVAS_Canvas* canvas = CAMERA_render(camera, world);
UTILITIES_Timer_Results render_results = UTILITIES_Timer_stop(render_timer);
char *filename = "csg.ppm";
LOGGER_log(LOGGER_INFO, "Writing file %s...\n", filename);
CANVAS_write_to_file(canvas, filename);
LOGGER_log(LOGGER_INFO, "Cleaning up...\n");
WORLD_delete_all_objects(world);
WORLD_delete(world);
LIGHTS_delete_pointlight(light);
CAMERA_delete(camera);
CANVAS_delete(canvas);
LOGGER_log(LOGGER_INFO, "Wall: %.2f User: %.2f System: %.2f\n", render_results.wall_time_seconds,
render_results.user_time_seconds, render_results.system_time_seconds);
} Catch(e) {
if (e == E_MALLOC_FAILED)
printf("Malloc failed. Exiting\n");
else if (e == E_FILE_FAILED)
printf("Failed to open test.ppm\n");
else
printf("Unknown exception %i occurred\n", e);
}
return 0;
}

View File

@ -33,7 +33,12 @@ void ARRLIST_add(ARRLIST_List* list, void* object) {
list->count++; list->count++;
} }
void* ARRLIST_safe_get(ARRLIST_List* list, unsigned int item) { unsigned int ARRLIST_item_count(const ARRLIST_List* list) {
assert(list);
return list->count;
}
void* ARRLIST_safe_get(const ARRLIST_List* list, unsigned int item) {
assert(list); assert(list);
if (item >= list->count) { if (item >= list->count) {
Throw(E_INDEX_OUT_OF_BOUNDS); Throw(E_INDEX_OUT_OF_BOUNDS);

View File

@ -15,7 +15,9 @@ void ARRLIST_add(ARRLIST_List* list, void* object);
* @param item * @param item
* @return * @return
*/ */
void* ARRLIST_safe_get(ARRLIST_List* list, unsigned int item); void* ARRLIST_safe_get(const ARRLIST_List* list, unsigned int item);
unsigned int ARRLIST_item_count(const ARRLIST_List* list);
/** /**
* Removes an item from the list and moves the remaining ptrs up. * Removes an item from the list and moves the remaining ptrs up.

View File

@ -1,98 +0,0 @@
#include <stdlib.h>
#include <assert.h>
#include "exceptions.h"
#include "arrlist.h"
typedef struct ARRLIST_List {
void** items;
unsigned int count;
} ARRLIST_List;
ARRLIST_List* ARRLIST_new() {
ARRLIST_List* list = malloc(sizeof(ARRLIST_List));
if (!list) {
Throw(E_MALLOC_FAILED);
}
list->items = NULL;
list->count = 0;
return list;
}
void ARRLIST_add(ARRLIST_List* list, void* object) {
assert(list);
assert(object);
void** tmpptr = reallocarray(list->items, sizeof(void*), list->count + 1);
if (!tmpptr) {
Throw(E_MALLOC_FAILED);
} else {
list->items = tmpptr;
}
list->items[list->count] = object;
list->count++;
}
void* ARRLIST_safe_get(ARRLIST_List* list, unsigned int item) {
assert(list);
if (item >= list->count) {
Throw(E_INDEX_OUT_OF_BOUNDS);
}
return list->items[item];
}
void ARRLIST_remove(ARRLIST_List* list, const void* object) {
assert(list);
assert(object);
bool found = false;
for (unsigned int ndx = 0; ndx < list->count; ndx++) {
if (list->items[ndx] == object) {
found = true;
}
if (found) {
if (ndx + 1 != list->count) {
list->items[ndx] = list->items[ndx + 1];
} else {
list->items[ndx] = NULL;
}
}
}
if (found) {
list->count--;
}
}
void ARRLIST_iterator(ARRLIST_List* list, void (*apply_each_ptr)(void* ptr, void* context), void* context) {
assert(list);
assert(apply_each_ptr);
for (unsigned int ndx = 0; ndx < list->count; ndx++) {
apply_each_ptr(list->items[ndx], context);
}
}
bool ARRLIST_is_empty(const ARRLIST_List* list) {
assert(list);
return (list->count == 0);
}
bool ARRLIST_contains(const ARRLIST_List* list, const void* object) {
assert(list);
assert(object);
for (unsigned int ndx = 0; ndx < list->count; ndx++) {
if (list->items[ndx] == object) {
return true;
}
}
return false;
}
void* ARRLIST_last(const ARRLIST_List* list) {
assert(list);
return list->items[list->count - 1];
}
void ARRLIST_delete(ARRLIST_List* list) {
assert(list);
free(list->items);
free(list);
}

View File

@ -1,54 +0,0 @@
#ifndef DATA_STRUCTURES_ARRLIST_H
#define DATA_STRUCTURES_ARRLIST_H
#include <stdbool.h>
typedef struct ARRLIST_List ARRLIST_List;
ARRLIST_List* ARRLIST_new();
void ARRLIST_add(ARRLIST_List* list, void* object);
/**
* gets a point to an item in the list. throws a E_INDEX_OUT_OF_BOUNDS
* when an item that doesn't exist is accessed.
* @param list
* @param item
* @return
*/
void* ARRLIST_safe_get(ARRLIST_List* list, unsigned int item);
/**
* Removes an item from the list and moves the remaining ptrs up.
* Doesn't bother to resize the array.
* @param list
* @param object
*/
void ARRLIST_remove(ARRLIST_List* list, const void* object);
bool ARRLIST_is_empty(const ARRLIST_List* list);
void ARRLIST_iterator(ARRLIST_List* list, void (*apply_each_ptr)(void* ptr, void* context), void* context);
/**
* Performs linear search of the list. Comparing only the ptr values!
* @param list
* @param object
* @return
*/
bool ARRLIST_contains(const ARRLIST_List* list, const void* object);
/**
* Returns the last item in the list
* @param ARRLIST_List
* @return
*/
void* ARRLIST_last(const ARRLIST_List* list);
/**
* Frees the list and backing array, but doesn't free anything pointed to
* the ptrs that have been added to the list.
* @param list
*/
void ARRLIST_delete(ARRLIST_List* list);
#endif //DATA_STRUCTURES_ARRLIST_H

View File

@ -7,18 +7,21 @@ add_library(module_shapes STATIC
cylinder.c cylinder.h cylinder.c cylinder.h
cone.c cone.h cone.c cone.h
group.c group.h group.c group.h
triangle.c triangle.h) triangle.c triangle.h
csg.c csg.h)
target_include_directories(module_shapes PUBLIC target_include_directories(module_shapes PUBLIC
${CMAKE_CURRENT_LIST_DIR} ${CMAKE_CURRENT_LIST_DIR}
module_raytracer module_raytracer
module_utilities module_utilities
module_datastructures
CException CException
) )
target_link_libraries(module_shapes target_link_libraries(module_shapes
module_raytracer module_raytracer
module_utilities module_utilities
module_datastructures
CException CException
m m
) )

View File

@ -7,7 +7,8 @@
const SHAPE_vtable CONE_vtable = { const SHAPE_vtable CONE_vtable = {
&CONE_local_intersect, &CONE_local_intersect,
&CYLINDER_delete_shape, &CYLINDER_delete_shape,
&CONE_local_normal_at &CONE_local_normal_at,
&SHAPE_default_shape_contains
}; };
CONE_Cone* CONE_new() { CONE_Cone* CONE_new() {

124
module_shapes/csg.c Normal file
View File

@ -0,0 +1,124 @@
#include <assert.h>
#include "csg.h"
bool CSG_shape_contains(const SHAPE_Shape* a, const SHAPE_Shape* b) {
assert(a);
assert(b);
CSG_Csg* acsg = (CSG_Csg*) a;
return acsg->left->vtable->contains(acsg->left, b) ||
acsg->right->vtable->contains(acsg->right, b);
}
const SHAPE_vtable CSG_vtable = {
&CSG_local_intersect,
&CSG_delete_shape,
&CSG_local_normal_at,
&CSG_shape_contains
};
bool CSG_intersection_allowed(CSG_Operation op, bool lhit, bool inl, bool inr) {
switch(op) {
case CSG_Union:
return (lhit && !inr) || (!lhit && !inl);
case CSG_Intersection:
return (lhit && inr) || (!lhit && inl);
case CSG_Difference:
return (lhit && !inr) || (!lhit && inl);
}
assert(0); // should never get here
return false;
}
RAY_Intersections* CSG_filter_intersections(const CSG_Csg* csg, const RAY_Intersections* intersections) {
assert(csg);
assert(intersections);
bool inl = false;
bool inr = false;
RAY_Intersections* result = RAY_new_intersections();
for (uint i=0; i<intersections->count; i++) {
const RAY_Xs* xs = &intersections->xs[i];
bool lhit = csg->left->vtable->contains(csg->left, xs->object);
if (CSG_intersection_allowed(csg->operation, lhit, inl, inr)) {
RAY_add_intersection_tri(result, xs->t, xs->object, xs->u, xs->v);
}
if (lhit) {
inl = !inl;
} else {
inr = !inr;
}
}
return result;
}
CSG_Csg* CSG_new(CSG_Operation op, SHAPE_Shape* left, SHAPE_Shape* right) {
assert(left);
assert(right);
CSG_Csg* csg = malloc(sizeof(CSG_Csg));
if (!csg) {
Throw(E_MALLOC_FAILED);
}
CSG_init(csg, op, left, right);
return csg;
}
void CSG_init(CSG_Csg* csg, CSG_Operation op, SHAPE_Shape* left, SHAPE_Shape* right) {
assert(csg);
assert(left);
assert(right);
SHAPE_init(&csg->shape, &CSG_vtable);
csg->left = left;
SHAPE_set_parent(left, csg);
csg->right = right;
SHAPE_set_parent(right, csg);
csg->operation = op;
}
void CSG_destroy(CSG_Csg* csg) {
assert(csg);
SHAPE_destroy(&csg->shape);
}
void CSG_delete(CSG_Csg* csg) {
assert(csg);
CSG_destroy(csg);
free(csg);
}
void CSG_delete_shape(SHAPE_Shape* shape) {
assert(shape);
CSG_Csg* csg = (CSG_Csg*) shape;
CSG_delete(csg);
}
void CSG_local_normal_at(TUPLES_Vector* local_normal, SHAPE_Shape* csg, const TUPLES_Point* local_point, const RAY_Xs* hit) {
assert(local_normal);
assert(csg);
assert(local_point);
assert(hit);
UNUSED(local_normal);
UNUSED(csg);
UNUSED(local_point);
UNUSED(hit);
assert(0);
}
void CSG_local_intersect(RAY_Intersections* intersections, SHAPE_Shape* shape, const RAY_Ray* local_ray) {
assert(intersections);
assert(shape);
assert(local_ray);
CSG_Csg* csg = (CSG_Csg*) shape;
RAY_Intersections* temp_xs = RAY_new_intersections();
SHAPE_intersect(temp_xs, csg->left, local_ray);
SHAPE_intersect(temp_xs, csg->right, local_ray);
RAY_sort_intersections(temp_xs);
RAY_Intersections* filtered_xs = CSG_filter_intersections(csg, temp_xs);
RAY_add_intersections(intersections, filtered_xs);
RAY_delete_intersections(temp_xs);
RAY_delete_intersections(filtered_xs);
}

35
module_shapes/csg.h Normal file
View File

@ -0,0 +1,35 @@
#ifndef SIMPLE_RAYTRACER_CSG_H
#define SIMPLE_RAYTRACER_CSG_H
#include "shape.h"
typedef enum CSG_Operation {
CSG_Union,
CSG_Intersection,
CSG_Difference
} CSG_Operation;
/**
* \extends SHAPE_Shape
*/
typedef struct CSG_Csg {
SHAPE_Shape shape;
SHAPE_Shape* left;
SHAPE_Shape* right;
CSG_Operation operation;
} CSG_Csg;
const SHAPE_vtable CSG_vtable;
CSG_Csg* CSG_new(CSG_Operation op, SHAPE_Shape* left, SHAPE_Shape* right);
void CSG_init(CSG_Csg* csg, CSG_Operation op, SHAPE_Shape* left, SHAPE_Shape* right);
void CSG_destroy(CSG_Csg* csg);
void CSG_delete(CSG_Csg* csg);
void CSG_delete_shape(SHAPE_Shape* shape);
void CSG_local_normal_at(TUPLES_Vector* local_normal, SHAPE_Shape* csg, const TUPLES_Point* local_point, const RAY_Xs* hit);
void CSG_local_intersect(RAY_Intersections* intersections, SHAPE_Shape* csg, const RAY_Ray* local_ray);
bool CSG_intersection_allowed(CSG_Operation op, bool lhit, bool inl, bool inr);
RAY_Intersections* CSG_filter_intersections(const CSG_Csg* csg, const RAY_Intersections* intersections);
#endif //SIMPLE_RAYTRACER_CSG_H

View File

@ -6,7 +6,8 @@
const SHAPE_vtable CUBE_vtable = { const SHAPE_vtable CUBE_vtable = {
&CUBE_local_intersect, &CUBE_local_intersect,
&SHAPE_delete, &SHAPE_delete,
&CUBE_local_normal_at &CUBE_local_normal_at,
&SHAPE_default_shape_contains
}; };
static double max(double x, double y, double z) { static double max(double x, double y, double z) {

View File

@ -13,7 +13,8 @@ void CYLINDER_delete_shape(SHAPE_Shape* shape) {
const SHAPE_vtable CYLINDER_vtable = { const SHAPE_vtable CYLINDER_vtable = {
&CYLINDER_local_intersect, &CYLINDER_local_intersect,
&CYLINDER_delete_shape, &CYLINDER_delete_shape,
&CYLINDER_local_normal_at &CYLINDER_local_normal_at,
&SHAPE_default_shape_contains
}; };
CYLINDER_Cylinder* CYLINDER_new() { CYLINDER_Cylinder* CYLINDER_new() {

View File

@ -1,11 +1,25 @@
#include <assert.h> #include <assert.h>
#include <exceptions.h> #include <exceptions.h>
#include <arrlist.h>
#include "group.h" #include "group.h"
bool GROUP_shape_contains(const SHAPE_Shape* a, const SHAPE_Shape* b) {
if (a == b) return true;
GROUP_Group* a_group = (GROUP_Group*) a;
for (unsigned int i=0; i < ARRLIST_item_count(a_group->list); i++) {
SHAPE_Shape* shape = (SHAPE_Shape*)ARRLIST_safe_get(a_group->list, i);
if (shape->vtable->contains(shape, b)) {
return true;
}
}
return false;
}
const SHAPE_vtable GROUP_vtable = { const SHAPE_vtable GROUP_vtable = {
&GROUP_local_intersect, &GROUP_local_intersect,
&GROUP_delete_shape, &GROUP_delete_shape,
&GROUP_local_normal_at &GROUP_local_normal_at,
&GROUP_shape_contains
}; };
GROUP_Group* GROUP_new() { GROUP_Group* GROUP_new() {

View File

@ -6,7 +6,8 @@
const SHAPE_vtable PLANE_vtable = { const SHAPE_vtable PLANE_vtable = {
&PLANE_local_intersect, &PLANE_local_intersect,
&SHAPE_delete, &SHAPE_delete,
&PLANE_local_normal_at &PLANE_local_normal_at,
&SHAPE_default_shape_contains
}; };
void PLANE_local_normal_at(TUPLES_Vector* local_normal, SHAPE_Shape* shape, const TUPLES_Point* local_point, const RAY_Xs* hit) { void PLANE_local_normal_at(TUPLES_Vector* local_normal, SHAPE_Shape* shape, const TUPLES_Point* local_point, const RAY_Xs* hit) {

View File

@ -147,3 +147,9 @@ void SHAPE_normal_to_world(TUPLES_Vector* result, const SHAPE_Shape* shape, cons
SHAPE_normal_to_world(result, parent, result); SHAPE_normal_to_world(result, parent, result);
} }
} }
bool SHAPE_default_shape_contains(const SHAPE_Shape* a, const SHAPE_Shape* b) {
assert(a);
assert(b);
return a == b;
}

View File

@ -14,6 +14,7 @@ typedef struct SHAPE_vtable {
void (*local_intersect)(RAY_Intersections* intersections, SHAPE_Shape* shape, const RAY_Ray* ray); void (*local_intersect)(RAY_Intersections* intersections, SHAPE_Shape* shape, const RAY_Ray* ray);
void (*delete)(SHAPE_Shape*); void (*delete)(SHAPE_Shape*);
void (*local_normal_at)(TUPLES_Vector* local_normal, SHAPE_Shape* shape, const TUPLES_Point* local_point, const RAY_Xs* hit); void (*local_normal_at)(TUPLES_Vector* local_normal, SHAPE_Shape* shape, const TUPLES_Point* local_point, const RAY_Xs* hit);
bool (*contains)(const SHAPE_Shape* a, const SHAPE_Shape* b);
} SHAPE_vtable; } SHAPE_vtable;
/** /**
@ -65,4 +66,6 @@ void SHAPE_normal_at(TUPLES_Vector* world_normal, SHAPE_Shape* shape, const TUPL
void SHAPE_world_to_object(TUPLES_Point* result, const SHAPE_Shape* shape, const TUPLES_Point* world_point); void SHAPE_world_to_object(TUPLES_Point* result, const SHAPE_Shape* shape, const TUPLES_Point* world_point);
void SHAPE_normal_to_world(TUPLES_Vector* result, const SHAPE_Shape* shape, const TUPLES_Vector* normal); void SHAPE_normal_to_world(TUPLES_Vector* result, const SHAPE_Shape* shape, const TUPLES_Vector* normal);
bool SHAPE_default_shape_contains(const SHAPE_Shape* a, const SHAPE_Shape* b);
#endif //DATA_STRUCTURES_SHAPE_H #endif //DATA_STRUCTURES_SHAPE_H

View File

@ -5,7 +5,8 @@
const SHAPE_vtable SPHERE_vtable = { const SHAPE_vtable SPHERE_vtable = {
&SPHERE_local_intersect, &SPHERE_local_intersect,
&SHAPE_delete, &SHAPE_delete,
&SPHERE_local_normal_at &SPHERE_local_normal_at,
&SHAPE_default_shape_contains
}; };
void SPHERE_local_intersect(RAY_Intersections* intersections, SHAPE_Shape* shape, const RAY_Ray* local_ray) { void SPHERE_local_intersect(RAY_Intersections* intersections, SHAPE_Shape* shape, const RAY_Ray* local_ray) {

View File

@ -5,7 +5,8 @@
const SHAPE_vtable TESTSHAPE_vtable = { const SHAPE_vtable TESTSHAPE_vtable = {
&TESTSHAPE_local_intersect, &TESTSHAPE_local_intersect,
&SHAPE_delete, &SHAPE_delete,
&TESTSHAPE_local_normal_at &TESTSHAPE_local_normal_at,
&SHAPE_default_shape_contains
}; };
void TESTSHAPE_init(TESTSHAPE_TestShape* shape) { void TESTSHAPE_init(TESTSHAPE_TestShape* shape) {

View File

@ -5,13 +5,15 @@
const SHAPE_vtable TRIANGLE_vtable = { const SHAPE_vtable TRIANGLE_vtable = {
&TRIANGLE_local_intersect, &TRIANGLE_local_intersect,
&TRIANGLE_delete_shape, &TRIANGLE_delete_shape,
&TRIANGLE_local_normal_at &TRIANGLE_local_normal_at,
&SHAPE_default_shape_contains
}; };
const SHAPE_vtable TRIANGLE_smooth_vtable = { const SHAPE_vtable TRIANGLE_smooth_vtable = {
&TRIANGLE_local_intersect, &TRIANGLE_local_intersect,
&TRIANGLE_delete_shape, &TRIANGLE_delete_shape,
&TRIANGLE_smooth_local_normal_at &TRIANGLE_smooth_local_normal_at,
&SHAPE_default_shape_contains
}; };
TRIANGLE_Triangle* TRIANGLE_new_from_points(const TUPLES_Point* p1, const TUPLES_Point* p2, const TUPLES_Point* p3) { TRIANGLE_Triangle* TRIANGLE_new_from_points(const TUPLES_Point* p1, const TUPLES_Point* p2, const TUPLES_Point* p3) {

View File

@ -104,5 +104,6 @@ int main(void) {
RUN_TEST(test_arrlist_remove); RUN_TEST(test_arrlist_remove);
RUN_TEST(test_arrlist_last); RUN_TEST(test_arrlist_last);
RUN_TEST(test_arrlist_safe_get_should_throw_on_bad_index); RUN_TEST(test_arrlist_safe_get_should_throw_on_bad_index);
RUN_TEST(test_arrlist_safe_get);
UNITY_END(); UNITY_END();
} }

View File

@ -63,3 +63,11 @@ target_link_libraries(module_shapes_triangle
Unity Unity
m) m)
add_test(shapes_triangle module_shapes_triangle) add_test(shapes_triangle module_shapes_triangle)
add_executable(module_shapes_csg
test_csg.c)
target_link_libraries(module_shapes_csg
module_shapes
Unity
m)
add_test(shapes_csg module_shapes_csg)

View File

@ -0,0 +1,142 @@
#include <unity.h>
#include <csg.h>
#include <sphere.h>
#include <cube.h>
void setUp() {}
void tearDown() {}
void test_create_csg() {
SPHERE_Sphere* s = SPHERE_new();
CUBE_Cube* c = CUBE_new();
CSG_Csg* csg = CSG_new(CSG_Union, s, c);
TEST_ASSERT_EQUAL(CSG_Union, csg->operation);
TEST_ASSERT_EQUAL_PTR(c, csg->right);
TEST_ASSERT_EQUAL_PTR(s, csg->left);
TEST_ASSERT_EQUAL_PTR(csg, SHAPE_get_parent(s));
TEST_ASSERT_EQUAL_PTR(csg, SHAPE_get_parent(c));
SPHERE_delete(s);
CUBE_delete(c);
CSG_delete(csg);
}
void test_csg_intersection_allowed(CSG_Operation op, bool lhit, bool inl, bool inr, bool expected) {
bool result = CSG_intersection_allowed(op, lhit, inl, inr);
if (expected != result) {
printf("Testing intersection allowed op(%u) lhit(%d) inl(%d) inr(%d) expected(%d) got(%d)\n", op, lhit, inl, inr, expected, result);
TEST_FAIL();
}
}
void test_csg_intersection_allowed_union_op() {
test_csg_intersection_allowed(CSG_Union, true, true, true, false);
test_csg_intersection_allowed(CSG_Union, true, true, false, true);
test_csg_intersection_allowed(CSG_Union, true, false, true, false);
test_csg_intersection_allowed(CSG_Union, true, false, false, true);
test_csg_intersection_allowed(CSG_Union, false, true, true, false);
test_csg_intersection_allowed(CSG_Union, false, true, false, false);
test_csg_intersection_allowed(CSG_Union, false, false, true, true);
test_csg_intersection_allowed(CSG_Union, false, false, false, true);
test_csg_intersection_allowed(CSG_Intersection, true, true, true, true);
test_csg_intersection_allowed(CSG_Intersection, true, true, false, false);
test_csg_intersection_allowed(CSG_Intersection, true, false, true, true);
test_csg_intersection_allowed(CSG_Intersection, true, false, false, false);
test_csg_intersection_allowed(CSG_Intersection, false, true, true, true);
test_csg_intersection_allowed(CSG_Intersection, false, true, false, true);
test_csg_intersection_allowed(CSG_Intersection, false, false, true, false);
test_csg_intersection_allowed(CSG_Intersection, false, false, false, false);
test_csg_intersection_allowed(CSG_Difference, true, true, true, false);
test_csg_intersection_allowed(CSG_Difference, true, true, false, true);
test_csg_intersection_allowed(CSG_Difference, true, false, true, false);
test_csg_intersection_allowed(CSG_Difference, true, false, false, true);
test_csg_intersection_allowed(CSG_Difference, false, true, true, true);
test_csg_intersection_allowed(CSG_Difference, false, true, false, true);
test_csg_intersection_allowed(CSG_Difference, false, false, true, false);
test_csg_intersection_allowed(CSG_Difference, false, false, false, false);
}
void filter_list_of_intersections(CSG_Operation op, uint first, uint second) {
SPHERE_Sphere* s1 = SPHERE_new();
CUBE_Cube* s2 = CUBE_new();
CSG_Csg* csg = CSG_new(op, s1, s2);
RAY_Intersections* xs = RAY_new_intersections();
RAY_add_intersection(xs, 1, s1);
RAY_add_intersection(xs, 2, s2);
RAY_add_intersection(xs, 3, s1);
RAY_add_intersection(xs, 4, s2);
RAY_Intersections* filtered_xs = CSG_filter_intersections(csg, xs);
TEST_ASSERT_EQUAL(2, filtered_xs->count);
TEST_ASSERT_EQUAL_PTR(xs->xs[first].object, filtered_xs->xs[0].object);
TEST_ASSERT_EQUAL_DOUBLE(xs->xs[first].t, filtered_xs->xs[0].t);
TEST_ASSERT_EQUAL_PTR(xs->xs[second].object, filtered_xs->xs[1].object);
TEST_ASSERT_EQUAL_DOUBLE(xs->xs[second].t, filtered_xs->xs[1].t);
RAY_delete_intersections(xs);
RAY_delete_intersections(filtered_xs);
CSG_delete(csg);
SPHERE_delete(s1);
CUBE_delete(s2);
}
void test_filter_list_of_intersections() {
filter_list_of_intersections(CSG_Union, 0, 3);
filter_list_of_intersections(CSG_Intersection, 1, 2);
filter_list_of_intersections(CSG_Difference, 0, 1);
}
void test_ray_misses_a_csg_object() {
SPHERE_Sphere* s = SPHERE_new();
CUBE_Cube* c = CUBE_new();
CSG_Csg* csg = CSG_new(CSG_Union, s, c);
RAY_Ray ray;
RAY_init(&ray, 0, 2, -5, 0, 0, 1);
RAY_Intersections *xs = RAY_new_intersections();
CSG_local_intersect(xs, (SHAPE_Shape*)csg, &ray);
TEST_ASSERT_EQUAL(0, xs->count);
RAY_delete_intersections(xs);
CSG_delete(csg);
CUBE_delete(c);
SPHERE_delete(s);
}
void test_ray_hits_csg_object() {
SPHERE_Sphere* s1 = SPHERE_new();
SPHERE_Sphere* s2 = SPHERE_new();
MATRIX_Matrix* s2_transform = MATRIX_new_translation(0, 0, 0.5);
SPHERE_set_transform(s2, s2_transform);
CSG_Csg* csg = CSG_new(CSG_Union, s1, s2);
MATRIX_delete(s2_transform);
RAY_Ray ray;
RAY_init(&ray, 0, 0, -5, 0, 0, 1);
RAY_Intersections* xs = RAY_new_intersections();
CSG_local_intersect(xs, (SHAPE_Shape*)csg, &ray);
TEST_ASSERT_EQUAL(2, xs->count);
TEST_ASSERT_EQUAL_DOUBLE(4, xs->xs[0].t);
TEST_ASSERT_EQUAL_PTR(s1, xs->xs[0].object);
TEST_ASSERT_EQUAL_DOUBLE(6.5, xs->xs[1].t);
TEST_ASSERT_EQUAL_PTR(s2, xs->xs[1].object);
RAY_delete_intersections(xs);
CSG_delete(csg);
SPHERE_delete_all(s1, s2);
}
int main(void)
{
UNITY_BEGIN();
RUN_TEST(test_create_csg);
RUN_TEST(test_csg_intersection_allowed_union_op);
RUN_TEST(test_filter_list_of_intersections);
RUN_TEST(test_ray_misses_a_csg_object);
RUN_TEST(test_ray_hits_csg_object);
return UNITY_END();
}