summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--libdimension/csg.c140
-rw-r--r--tests/libdimension/tests.c2
2 files changed, 140 insertions, 2 deletions
diff --git a/libdimension/csg.c b/libdimension/csg.c
index 8ba99d4..78df625 100644
--- a/libdimension/csg.c
+++ b/libdimension/csg.c
@@ -281,6 +281,145 @@ dmnsn_new_csg_intersection(dmnsn_object *A, dmnsn_object *B)
return NULL;
}
+/* Differences */
+
+static dmnsn_intersection *
+dmnsn_csg_difference_intersection_fn(const dmnsn_object *csg, dmnsn_line line)
+{
+ const dmnsn_object **params = csg->ptr;
+
+ dmnsn_line line1 = dmnsn_matrix_line_mul(params[0]->trans_inv, line);
+ dmnsn_line line2 = dmnsn_matrix_line_mul(params[1]->trans_inv, line);
+ dmnsn_intersection *i1 = (*params[0]->intersection_fn)(params[0], line1);
+ dmnsn_intersection *i2 = (*params[1]->intersection_fn)(params[1], line2);
+
+ double oldt = 0.0;
+ while (i1) {
+ i1->ray = line;
+ i1->t += oldt;
+ oldt = i1->t;
+ i1->normal = dmnsn_vector_normalize(
+ dmnsn_vector_sub(
+ dmnsn_matrix_vector_mul(params[0]->trans, i1->normal),
+ dmnsn_matrix_vector_mul(params[0]->trans, dmnsn_zero)
+ )
+ );
+
+ if (!i1->texture)
+ i1->texture = csg->texture;
+ if (!i1->interior)
+ i1->interior = csg->interior;
+
+ dmnsn_vector point = dmnsn_line_point(i1->ray, i1->t);
+ point = dmnsn_matrix_vector_mul(params[1]->trans_inv, point);
+ if ((*params[1]->inside_fn)(params[1], point)) {
+ line1.x0 = dmnsn_line_point(line1, i1->t);
+ line1 = dmnsn_line_add_epsilon(line1);
+ dmnsn_delete_intersection(i1);
+ i1 = (*params[0]->intersection_fn)(params[0], line1);
+ } else {
+ break;
+ }
+ }
+
+ oldt = 0.0;
+ while (i2) {
+ i2->ray = line;
+ i2->t += oldt;
+ oldt = i2->t;
+ i2->normal = dmnsn_vector_normalize(
+ dmnsn_vector_sub(
+ dmnsn_matrix_vector_mul(params[1]->trans, i2->normal),
+ dmnsn_matrix_vector_mul(params[1]->trans, dmnsn_zero)
+ )
+ );
+
+ if (!i2->texture)
+ i2->texture = csg->texture;
+ if (!i2->interior)
+ i2->interior = csg->interior;
+
+ dmnsn_vector point = dmnsn_line_point(i2->ray, i2->t);
+ point = dmnsn_matrix_vector_mul(params[0]->trans_inv, point);
+ if (!(*params[0]->inside_fn)(params[0], point)) {
+ line2.x0 = dmnsn_line_point(line2, i2->t);
+ line2 = dmnsn_line_add_epsilon(line2);
+ dmnsn_delete_intersection(i2);
+ i2 = (*params[1]->intersection_fn)(params[1], line2);
+ } else {
+ break;
+ }
+ }
+
+ if (!i1)
+ return i2;
+ if (!i2)
+ return i1;
+
+ if (i1->t < i2->t) {
+ dmnsn_delete_intersection(i2);
+ return i1;
+ } else {
+ dmnsn_delete_intersection(i1);
+ return i2;
+ }
+}
+
+static bool
+dmnsn_csg_difference_inside_fn(const dmnsn_object *csg, dmnsn_vector point)
+{
+ dmnsn_object **params = csg->ptr;
+ return (*params[0]->inside_fn)(params[0], point)
+ && !(*params[1]->inside_fn)(params[1], point);
+}
+
+dmnsn_object *
+dmnsn_new_csg_difference(dmnsn_object *A, dmnsn_object *B)
+{
+ if (A && B) {
+ A->trans_inv = dmnsn_matrix_inverse(A->trans);
+ B->trans_inv = dmnsn_matrix_inverse(B->trans);
+
+ dmnsn_object *csg = dmnsn_new_object();
+ if (csg) {
+ dmnsn_object **params = malloc(2*sizeof(dmnsn_object *));
+ if (!params) {
+ dmnsn_delete_object(csg);
+ dmnsn_delete_object(B);
+ dmnsn_delete_object(A);
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ params[0] = A;
+ params[1] = B;
+
+ csg->ptr = params;
+ csg->intersection_fn = &dmnsn_csg_difference_intersection_fn;
+ csg->inside_fn = &dmnsn_csg_difference_inside_fn;
+ csg->free_fn = &dmnsn_csg_free_fn;
+
+ dmnsn_bounding_box Abox
+ = dmnsn_matrix_bounding_box_mul(A->trans, A->bounding_box);
+ dmnsn_bounding_box Bbox
+ = dmnsn_matrix_bounding_box_mul(B->trans, B->bounding_box);
+ csg->bounding_box.min = dmnsn_vector_min(Abox.min, Bbox.min);
+ csg->bounding_box.max = dmnsn_vector_max(Abox.max, Bbox.max);
+
+ return csg;
+ } else {
+ dmnsn_delete_object(B);
+ dmnsn_delete_object(A);
+ }
+ } else if (A) {
+ dmnsn_delete_object(B);
+ } else if (B) {
+ dmnsn_delete_object(A);
+ }
+
+ return NULL;
+}
+
/* Merges */
static dmnsn_intersection *
@@ -345,7 +484,6 @@ dmnsn_csg_merge_intersection_fn(const dmnsn_object *csg, dmnsn_line line)
line2.x0 = dmnsn_line_point(line2, i2->t);
line2 = dmnsn_line_add_epsilon(line2);
dmnsn_delete_intersection(i2);
- i2 = NULL; break;
i2 = (*params[1]->intersection_fn)(params[1], line2);
} else {
break;
diff --git a/tests/libdimension/tests.c b/tests/libdimension/tests.c
index efb2631..b23c0df 100644
--- a/tests/libdimension/tests.c
+++ b/tests/libdimension/tests.c
@@ -143,7 +143,7 @@ dmnsn_new_default_scene()
cube->trans = dmnsn_rotation_matrix(dmnsn_new_vector(0.75, 0.0, 0.0));
- dmnsn_object *csg = dmnsn_new_csg_merge(sphere, cube);
+ dmnsn_object *csg = dmnsn_new_csg_difference(cube, sphere);
if (!csg) {
dmnsn_delete_scene(scene);
return NULL;