From ffb8da7d94a130f2d152f706d1af113fc4640246 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Tue, 6 Apr 2010 01:28:29 -0400 Subject: Add CSG unions to libdimension. --- libdimension/Makefile.am | 2 + libdimension/csg.c | 143 +++++++++++++++++++++++++++++++++++++++++++ libdimension/dimension.h | 1 + libdimension/dimension/csg.h | 33 ++++++++++ tests/libdimension/tests.c | 14 ++++- 5 files changed, 191 insertions(+), 2 deletions(-) create mode 100644 libdimension/csg.c create mode 100644 libdimension/dimension/csg.h diff --git a/libdimension/Makefile.am b/libdimension/Makefile.am index 4f427d0..2dda68c 100644 --- a/libdimension/Makefile.am +++ b/libdimension/Makefile.am @@ -26,6 +26,7 @@ nobase_include_HEADERS = dimension.h \ dimension/cameras.h \ dimension/canvas.h \ dimension/color.h \ + dimension/csg.h \ dimension/error.h \ dimension/finishes.h \ dimension/geometry.h \ @@ -52,6 +53,7 @@ libdimension_la_SOURCES = $(nobase_include_HEADERS) \ canvas.c \ color.c \ cube.c \ + csg.c \ diffuse.c \ dimension_impl.h \ error.c \ diff --git a/libdimension/csg.c b/libdimension/csg.c new file mode 100644 index 0000000..5b3cf9a --- /dev/null +++ b/libdimension/csg.c @@ -0,0 +1,143 @@ +/************************************************************************* + * Copyright (C) 2010 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +#include "dimension.h" +#include + +static void +dmnsn_csg_free_fn(void *ptr) +{ + dmnsn_object **params = ptr; + dmnsn_delete_object(params[1]); + dmnsn_delete_object(params[0]); + free(ptr); +} + +/* Unions */ + +static dmnsn_intersection * +dmnsn_csg_union_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); + + if (i1) { + /* Transform the intersection back to the observer's view */ + i1->ray = line; + 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; + } + + if (i2) { + i2->ray = line; + 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; + } + + 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_union_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_union(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_union_intersection_fn; + csg->inside_fn = &dmnsn_csg_union_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; +} diff --git a/libdimension/dimension.h b/libdimension/dimension.h index be0e9bb..e693841 100644 --- a/libdimension/dimension.h +++ b/libdimension/dimension.h @@ -74,6 +74,7 @@ typedef void dmnsn_free_fn(void *ptr); #include #include #include +#include #include #include #include diff --git a/libdimension/dimension/csg.h b/libdimension/dimension/csg.h new file mode 100644 index 0000000..383efb4 --- /dev/null +++ b/libdimension/dimension/csg.h @@ -0,0 +1,33 @@ +/************************************************************************* + * Copyright (C) 2010 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/* + * Constructive solid geometry + */ + +#ifndef DIMENSION_CSG_H +#define DIMENSION_CSG_H + +dmnsn_object *dmnsn_new_csg_union(dmnsn_object *A, dmnsn_object *B); +dmnsn_object *dmnsn_new_csg_intersection(dmnsn_object *A, dmnsn_object *B); +dmnsn_object *dmnsn_new_csg_difference(dmnsn_object *A, dmnsn_object *B); +dmnsn_object *dmnsn_new_csg_merge(dmnsn_object *A, dmnsn_object *B); + +#endif /* DIMENSION_CSG_H */ diff --git a/tests/libdimension/tests.c b/tests/libdimension/tests.c index d5aeb69..47d9b7c 100644 --- a/tests/libdimension/tests.c +++ b/tests/libdimension/tests.c @@ -86,7 +86,6 @@ dmnsn_new_default_scene() dmnsn_delete_scene(scene); return NULL; } - dmnsn_array_push(scene->objects, &sphere); sphere->texture = dmnsn_new_texture(); if (!sphere->texture) { @@ -104,13 +103,14 @@ dmnsn_new_default_scene() dmnsn_object *cube = dmnsn_new_cube(); if (!cube) { + dmnsn_delete_object(sphere); dmnsn_delete_scene(scene); return NULL; } - dmnsn_array_push(scene->objects, &cube); cube->texture = dmnsn_new_texture(); if (!cube->texture) { + dmnsn_delete_object(sphere); dmnsn_delete_scene(scene); return NULL; } @@ -120,6 +120,7 @@ dmnsn_new_default_scene() cube_color.trans = 0.5; cube->texture->pigment = dmnsn_new_solid_pigment(cube_color); if (!cube->texture->pigment) { + dmnsn_delete_object(sphere); dmnsn_delete_scene(scene); return NULL; } @@ -127,12 +128,14 @@ dmnsn_new_default_scene() dmnsn_color reflect = dmnsn_color_mul(0.5, dmnsn_white); cube->texture->finish = dmnsn_new_reflective_finish(reflect, reflect, 1.0); if (!cube->texture->finish) { + dmnsn_delete_object(sphere); dmnsn_delete_scene(scene); return NULL; } cube->interior = dmnsn_new_interior(); if (!cube->interior) { + dmnsn_delete_object(sphere); dmnsn_delete_scene(scene); return NULL; } @@ -140,6 +143,13 @@ dmnsn_new_default_scene() cube->trans = dmnsn_rotation_matrix(dmnsn_new_vector(0.75, 0.0, 0.0)); + dmnsn_object *csg = dmnsn_new_csg_union(sphere, cube); + if (!csg) { + dmnsn_delete_scene(scene); + return NULL; + } + dmnsn_array_push(scene->objects, &csg); + /* Now make a light */ dmnsn_light *light = dmnsn_new_point_light( -- cgit v1.2.3