summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--libdimension/Makefile.am2
-rw-r--r--libdimension/csg.c143
-rw-r--r--libdimension/dimension.h1
-rw-r--r--libdimension/dimension/csg.h33
-rw-r--r--tests/libdimension/tests.c14
5 files changed, 191 insertions, 2 deletions
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 <tavianator@gmail.com> *
+ * *
+ * 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 *
+ * <http://www.gnu.org/licenses/>. *
+ *************************************************************************/
+
+#include "dimension.h"
+#include <errno.h>
+
+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 <dimension/interior.h>
#include <dimension/object.h>
#include <dimension/objects.h>
+#include <dimension/csg.h>
#include <dimension/light.h>
#include <dimension/lights.h>
#include <dimension/camera.h>
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 <tavianator@gmail.com> *
+ * *
+ * 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 *
+ * <http://www.gnu.org/licenses/>. *
+ *************************************************************************/
+
+/*
+ * 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(