diff options
Diffstat (limited to 'libdimension')
-rw-r--r-- | libdimension/csg.c | 140 |
1 files changed, 139 insertions, 1 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; |