From 26a6f5d017be9ec0ec03cd20d082546a4e435746 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Sat, 10 Apr 2010 22:09:45 -0400 Subject: Improve CSG and light handling. - Lights can be inside CSG objects - Lights can be modified by transformations - CSG objects can contain only one object --- dimension/common.rules | 7 +- dimension/parse.c | 9 +- dimension/realize.c | 256 ++++++++++++++++++++++---------------------- tests/dimension/Makefile.am | 10 +- tests/dimension/csg.pov | 65 +++++++++++ tests/dimension/csg.sh | 85 +++++++++++++++ tests/dimension/demo.sh | 3 +- 7 files changed, 296 insertions(+), 139 deletions(-) create mode 100644 tests/dimension/csg.pov create mode 100755 tests/dimension/csg.sh diff --git a/dimension/common.rules b/dimension/common.rules index e604b9f..c5f9eea 100644 --- a/dimension/common.rules +++ b/dimension/common.rules @@ -236,8 +236,8 @@ MERGE: "merge" "{" } ; -OBJECTS: OBJECT OBJECT { - $$ = dmnsn_new_astnode2(DMNSN_AST_ARRAY, @$, $1, $2); +OBJECTS: OBJECT { + $$ = dmnsn_new_astnode1(DMNSN_AST_ARRAY, @$, $1); } | OBJECTS OBJECT { $$ = $1; @@ -246,9 +246,10 @@ OBJECTS: OBJECT OBJECT { LIGHT_SOURCE: "light_source" "{" VECTOR "," COLOR + OBJECT_MODIFIERS "}" { - $$ = dmnsn_new_astnode2(DMNSN_AST_LIGHT_SOURCE, @$, $3, $5); + $$ = dmnsn_new_astnode3(DMNSN_AST_LIGHT_SOURCE, @$, $3, $5, $6); } ; diff --git a/dimension/parse.c b/dimension/parse.c index 19ba24c..2f717f9 100644 --- a/dimension/parse.c +++ b/dimension/parse.c @@ -536,12 +536,7 @@ dmnsn_vector_promote(dmnsn_astnode astnode, dmnsn_symbol_table *symtable) while (dmnsn_array_size(promoted.children) < DMNSN_VECTOR_NELEM) { component = dmnsn_copy_astnode(component); - component.type = DMNSN_AST_INTEGER; - - long *val = dmnsn_malloc(sizeof(long)); - *val = 0; - - component.ptr = val; + dmnsn_make_ast_integer(&component, 0); dmnsn_array_push(promoted.children, &component); } } else { @@ -1759,6 +1754,8 @@ dmnsn_print_astnode(FILE *file, dmnsn_astnode astnode) case DMNSN_AST_FLOAT: dvalue = *(double *)astnode.ptr; + /* Don't print -0 */ + if (dvalue == 0.0) dvalue = 0.0; fprintf(file, "(%s %g)", dmnsn_astnode_string(astnode.type), dvalue); break; diff --git a/dimension/realize.c b/dimension/realize.c index 215d891..b5c6066 100644 --- a/dimension/realize.c +++ b/dimension/realize.c @@ -643,7 +643,76 @@ dmnsn_realize_object_modifiers(dmnsn_astnode astnode, dmnsn_object *object) } } -static dmnsn_object *dmnsn_realize_object(dmnsn_astnode astnode); +static void +dmnsn_realize_light_source_modifiers(dmnsn_astnode astnode, dmnsn_light *light) +{ + dmnsn_assert(astnode.type == DMNSN_AST_OBJECT_MODIFIERS, + "Expected object modifiers."); + + unsigned int i; + for (i = 0; i < dmnsn_array_size(astnode.children); ++i) { + dmnsn_astnode modifier; + dmnsn_array_get(astnode.children, i, &modifier); + + switch (modifier.type) { + case DMNSN_AST_ROTATION: + light->x0 = dmnsn_matrix_vector_mul( + dmnsn_realize_rotation(modifier), + light->x0 + ); + break; + case DMNSN_AST_SCALE: + light->x0 = dmnsn_matrix_vector_mul( + dmnsn_realize_scale(modifier), + light->x0 + ); + break; + case DMNSN_AST_TRANSLATION: + light->x0 = dmnsn_matrix_vector_mul( + dmnsn_realize_translation(modifier), + light->x0 + ); + break; + + case DMNSN_AST_TEXTURE: + case DMNSN_AST_PIGMENT: + case DMNSN_AST_FINISH: + case DMNSN_AST_INTERIOR: + dmnsn_diagnostic(modifier.filename, modifier.line, modifier.col, + "WARNING: ignoring %s applied to light source", + dmnsn_astnode_string(modifier.type)); + break; + + default: + dmnsn_assert(false, "Invalid object modifier."); + } + } +} + +static dmnsn_light * +dmnsn_realize_light_source(dmnsn_astnode astnode) +{ + dmnsn_assert(astnode.type == DMNSN_AST_LIGHT_SOURCE, + "Expected a light source."); + + dmnsn_astnode point, color_node; + dmnsn_array_get(astnode.children, 0, &point); + dmnsn_array_get(astnode.children, 1, &color_node); + + dmnsn_vector x0 = dmnsn_realize_vector(point); + dmnsn_color color = dmnsn_realize_color(color_node); + + dmnsn_light *light = dmnsn_new_point_light(x0, color); + + dmnsn_astnode modifiers; + dmnsn_array_get(astnode.children, 2, &modifiers); + dmnsn_realize_light_source_modifiers(modifiers, light); + + return light; +} + +static dmnsn_object *dmnsn_realize_object(dmnsn_astnode astnode, + dmnsn_array *lights); static dmnsn_object * dmnsn_realize_box(dmnsn_astnode astnode) @@ -702,155 +771,103 @@ dmnsn_realize_sphere(dmnsn_astnode astnode) return sphere; } +typedef dmnsn_object *dmnsn_csg_object_fn(dmnsn_object *a, dmnsn_object *b); + +/* Generalized CSG realizer */ static dmnsn_object * -dmnsn_realize_union(dmnsn_astnode astnode) +dmnsn_realize_csg(dmnsn_astnode astnode, dmnsn_array *lights, + dmnsn_csg_object_fn *csg_object_fn) { - dmnsn_assert(astnode.type == DMNSN_AST_UNION, "Expected a union."); - - dmnsn_astnode objects; + dmnsn_astnode objects, modifiers; dmnsn_array_get(astnode.children, 0, &objects); + dmnsn_array_get(astnode.children, 1, &modifiers); - dmnsn_astnode o1node, o2node; - dmnsn_array_get(objects.children, 0, &o1node); - dmnsn_array_get(objects.children, 1, &o2node); - - dmnsn_object *o1 = dmnsn_realize_object(o1node); - dmnsn_object *o2 = dmnsn_realize_object(o2node); + unsigned int i; + dmnsn_object *csg = NULL; + for (i = 0; i < dmnsn_array_size(objects.children) && !csg; ++i) { + dmnsn_astnode onode; + dmnsn_array_get(objects.children, i, &onode); - dmnsn_object *csg = dmnsn_new_csg_union(o1, o2); + if (onode.type == DMNSN_AST_LIGHT_SOURCE) { + dmnsn_light *light = dmnsn_realize_light_source(onode); + dmnsn_realize_light_source_modifiers(modifiers, light); + dmnsn_array_push(lights, &light); + } else { + csg = dmnsn_realize_object(onode, lights); + } + } - unsigned int i; - for (i = 2; i < dmnsn_array_size(objects.children); ++i) { + for (; i < dmnsn_array_size(objects.children); ++i) { dmnsn_astnode onode; dmnsn_array_get(objects.children, i, &onode); - dmnsn_object *object = dmnsn_realize_object(onode); - csg = dmnsn_new_csg_union(csg, object); + if (onode.type == DMNSN_AST_LIGHT_SOURCE) { + dmnsn_light *light = dmnsn_realize_light_source(onode); + dmnsn_realize_light_source_modifiers(modifiers, light); + dmnsn_array_push(lights, &light); + } else { + dmnsn_object *object = dmnsn_realize_object(onode, lights); + csg = (*csg_object_fn)(csg, object); + } } - dmnsn_astnode modifiers; - dmnsn_array_get(astnode.children, 1, &modifiers); dmnsn_realize_object_modifiers(modifiers, csg); - return csg; } static dmnsn_object * -dmnsn_realize_intersection(dmnsn_astnode astnode) +dmnsn_realize_union(dmnsn_astnode astnode, dmnsn_array *lights) +{ + dmnsn_assert(astnode.type == DMNSN_AST_UNION, "Expected a union."); + return dmnsn_realize_csg(astnode, lights, &dmnsn_new_csg_union); +} + +static dmnsn_object * +dmnsn_realize_intersection(dmnsn_astnode astnode, dmnsn_array *lights) { dmnsn_assert(astnode.type == DMNSN_AST_INTERSECTION, "Expected an intersection."); - - dmnsn_astnode objects; - dmnsn_array_get(astnode.children, 0, &objects); - - dmnsn_astnode o1node, o2node; - dmnsn_array_get(objects.children, 0, &o1node); - dmnsn_array_get(objects.children, 1, &o2node); - - dmnsn_object *o1 = dmnsn_realize_object(o1node); - dmnsn_object *o2 = dmnsn_realize_object(o2node); - - dmnsn_object *csg = dmnsn_new_csg_intersection(o1, o2); - - unsigned int i; - for (i = 2; i < dmnsn_array_size(objects.children); ++i) { - dmnsn_astnode onode; - dmnsn_array_get(objects.children, i, &onode); - - dmnsn_object *object = dmnsn_realize_object(onode); - csg = dmnsn_new_csg_intersection(csg, object); - } - - dmnsn_astnode modifiers; - dmnsn_array_get(astnode.children, 1, &modifiers); - dmnsn_realize_object_modifiers(modifiers, csg); - - return csg; + return dmnsn_realize_csg(astnode, lights, &dmnsn_new_csg_intersection); } static dmnsn_object * -dmnsn_realize_difference(dmnsn_astnode astnode) +dmnsn_realize_difference(dmnsn_astnode astnode, dmnsn_array *lights) { dmnsn_assert(astnode.type == DMNSN_AST_DIFFERENCE, "Expected a difference."); - - dmnsn_astnode objects; - dmnsn_array_get(astnode.children, 0, &objects); - - dmnsn_astnode o1node, o2node; - dmnsn_array_get(objects.children, 0, &o1node); - dmnsn_array_get(objects.children, 1, &o2node); - - dmnsn_object *o1 = dmnsn_realize_object(o1node); - dmnsn_object *o2 = dmnsn_realize_object(o2node); - - dmnsn_object *csg = dmnsn_new_csg_difference(o1, o2); - - unsigned int i; - for (i = 2; i < dmnsn_array_size(objects.children); ++i) { - dmnsn_astnode onode; - dmnsn_array_get(objects.children, i, &onode); - - dmnsn_object *object = dmnsn_realize_object(onode); - csg = dmnsn_new_csg_difference(csg, object); - } - - dmnsn_astnode modifiers; - dmnsn_array_get(astnode.children, 1, &modifiers); - dmnsn_realize_object_modifiers(modifiers, csg); - - return csg; + return dmnsn_realize_csg(astnode, lights, &dmnsn_new_csg_difference); } static dmnsn_object * -dmnsn_realize_merge(dmnsn_astnode astnode) +dmnsn_realize_merge(dmnsn_astnode astnode, dmnsn_array *lights) { dmnsn_assert(astnode.type == DMNSN_AST_MERGE, "Expected a merge."); - - dmnsn_astnode objects; - dmnsn_array_get(astnode.children, 0, &objects); - - dmnsn_astnode o1node, o2node; - dmnsn_array_get(objects.children, 0, &o1node); - dmnsn_array_get(objects.children, 1, &o2node); - - dmnsn_object *o1 = dmnsn_realize_object(o1node); - dmnsn_object *o2 = dmnsn_realize_object(o2node); - - dmnsn_object *csg = dmnsn_new_csg_merge(o1, o2); - - unsigned int i; - for (i = 2; i < dmnsn_array_size(objects.children); ++i) { - dmnsn_astnode onode; - dmnsn_array_get(objects.children, i, &onode); - - dmnsn_object *object = dmnsn_realize_object(onode); - csg = dmnsn_new_csg_merge(csg, object); - } - - dmnsn_astnode modifiers; - dmnsn_array_get(astnode.children, 1, &modifiers); - dmnsn_realize_object_modifiers(modifiers, csg); - - return csg; + return dmnsn_realize_csg(astnode, lights, &dmnsn_new_csg_merge); } +/* Realize an object, or maybe a light */ static dmnsn_object * -dmnsn_realize_object(dmnsn_astnode astnode) +dmnsn_realize_object(dmnsn_astnode astnode, dmnsn_array *lights) { switch (astnode.type) { case DMNSN_AST_BOX: return dmnsn_realize_box(astnode); case DMNSN_AST_DIFFERENCE: - return dmnsn_realize_difference(astnode); + return dmnsn_realize_difference(astnode, lights); case DMNSN_AST_INTERSECTION: - return dmnsn_realize_intersection(astnode); + return dmnsn_realize_intersection(astnode, lights); case DMNSN_AST_MERGE: - return dmnsn_realize_merge(astnode); + return dmnsn_realize_merge(astnode, lights); case DMNSN_AST_SPHERE: return dmnsn_realize_sphere(astnode); case DMNSN_AST_UNION: - return dmnsn_realize_union(astnode); + return dmnsn_realize_union(astnode, lights); + + case DMNSN_AST_LIGHT_SOURCE: + { + dmnsn_light *light = dmnsn_realize_light_source(astnode); + dmnsn_array_push(lights, &light); + return NULL; + } default: dmnsn_assert(false, "Expected an object."); @@ -858,24 +875,6 @@ dmnsn_realize_object(dmnsn_astnode astnode) } } -static dmnsn_light * -dmnsn_realize_light_source(dmnsn_astnode astnode) -{ - dmnsn_assert(astnode.type == DMNSN_AST_LIGHT_SOURCE, - "Expected a light source."); - - dmnsn_astnode point, color_node; - dmnsn_array_get(astnode.children, 0, &point); - dmnsn_array_get(astnode.children, 1, &color_node); - - dmnsn_vector x0 = dmnsn_realize_vector(point); - dmnsn_color color = dmnsn_realize_color(color_node); - - dmnsn_light *light = dmnsn_new_point_light(x0, color); - - return light; -} - static dmnsn_scene * dmnsn_realize_astree(const dmnsn_astree *astree) { @@ -928,8 +927,9 @@ dmnsn_realize_astree(const dmnsn_astree *astree) case DMNSN_AST_MERGE: case DMNSN_AST_SPHERE: case DMNSN_AST_UNION: - object = dmnsn_realize_object(astnode); - dmnsn_array_push(scene->objects, &object); + object = dmnsn_realize_object(astnode, scene->lights); + if (object) + dmnsn_array_push(scene->objects, &object); break; case DMNSN_AST_LIGHT_SOURCE: diff --git a/tests/dimension/Makefile.am b/tests/dimension/Makefile.am index bb4aa0f..44afead 100644 --- a/tests/dimension/Makefile.am +++ b/tests/dimension/Makefile.am @@ -19,7 +19,14 @@ INCLUDES = -I$(top_srcdir)/libdimension -TESTS = punctuation.sh numeric.sh strings.sh labels.sh directives.sh arithexp.sh demo.sh +TESTS = punctuation.sh \ + numeric.sh \ + strings.sh \ + labels.sh \ + directives.sh \ + arithexp.sh \ + csg.sh \ + demo.sh TESTS_ENVIRONMENT = top_builddir=$(top_builddir) .sh: @@ -33,6 +40,7 @@ EXTRA_DIST = $(TESTS) \ directives.inc \ directives.pov \ arithexp.pov \ + csg.pov \ demo.pov clean-local: diff --git a/tests/dimension/csg.pov b/tests/dimension/csg.pov new file mode 100644 index 0000000..046510f --- /dev/null +++ b/tests/dimension/csg.pov @@ -0,0 +1,65 @@ +/************************************************************************* + * Copyright (C) 2010 Tavian Barnes * + * * + * This file is part of The Dimension Test Suite. * + * * + * The Dimension Test Suite is free software; you can redistribute it * + * and/or modify it under the terms of the GNU 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 Test Suite 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 * + * General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + +// Test constructive solid geometry + +camera { + perspective + location -4*z + right 1.6*x + look_at 0 +} + +background { + color rgbf <0, 0.1, 0.2, 0.1> +} + +/* One-object unions */ + +union { + sphere { + -1.5*x, 1 + pigment { color red 1 } + } +} + +union { + light_source { + 20*y, color rgb 0.5 + } +} + +/* CSG with lights */ +difference { + light_source { + -15*x, color rgb 0.5 + } + sphere { + 1.5*x - 20*y, 1 + pigment { color green 1 } + } + light_source { + 15*x, color rgb 0.5 + } + box { + <0.7, -20.8, -0.8>, <2.3, -19.2, 0.8> + pigment { color blue 1 } + } + translate 20*y +} diff --git a/tests/dimension/csg.sh b/tests/dimension/csg.sh new file mode 100755 index 0000000..c27e448 --- /dev/null +++ b/tests/dimension/csg.sh @@ -0,0 +1,85 @@ +#!/bin/sh + +######################################################################### +# Copyright (C) 2010 Tavian Barnes # +# # +# This file is part of The Dimension Test Suite. # +# # +# The Dimension Test Suite is free software; you can redistribute it # +# and/or modify it under the terms of the GNU 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 Test Suite 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 # +# General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, see . # +######################################################################### + +csg=$(${top_builddir}/dimension/dimension --parse ${srcdir}/csg.pov) +csg_exp="$(echo -n \ +'((camera + perspective + (location (vector (integer 0) (integer 0) (integer -4) + (integer 0) (integer 0))) + (right (vector (float 1.6) (float 0) (float 0) (float 0) (float 0))) + (look_at (vector (integer 0) (integer 0) (integer 0) + (integer 0) (integer 0)))) + (background + (vector (integer 0) (float 0.1) (float 0.2) (float 0.1) (integer 0))) + (union + (array + (sphere + (vector (float -1.5) (float 0) (float 0) (float 0) (float 0)) + (integer 1) + (object-modifiers + (pigment + (vector (integer 1) (integer 0) (integer 0) + (integer 0) (integer 0)))))) + object-modifiers) + (union + (array + (light_source + (vector (integer 0) (integer 20) (integer 0) (integer 0) (integer 0)) + (vector (float 0.5) (float 0.5) (float 0.5) (integer 0) (integer 0)) + object-modifiers)) + object-modifiers) + (difference + (array + (light_source + (vector (integer -15) (integer 0) (integer 0) (integer 0) (integer 0)) + (vector (float 0.5) (float 0.5) (float 0.5) (integer 0) (integer 0)) + object-modifiers) + (sphere + (vector (float 1.5) (float -20) (float 0) (float 0) (float 0)) + (integer 1) + (object-modifiers + (pigment + (vector (integer 0) (integer 1) (integer 0) + (integer 0) (integer 0))))) + (light_source + (vector (integer 15) (integer 0) (integer 0) (integer 0) (integer 0)) + (vector (float 0.5) (float 0.5) (float 0.5) (integer 0) (integer 0)) + object-modifiers) + (box + (vector (float 0.7) (float -20.8) (float -0.8) (integer 0) (integer 0)) + (vector (float 2.3) (float -19.2) (float 0.8) (integer 0) (integer 0)) + (object-modifiers + (pigment + (vector (integer 0) (integer 0) (integer 1) + (integer 0) (integer 0)))))) + (object-modifiers + (translate (vector (integer 0) (integer 20) (integer 0) + (integer 0) (integer 0))))))' \ +| tr '\n' ' ' | sed -r 's/[[:space:]]+/ /g')" + +if [ "$csg" != "$csg_exp" ]; then + echo "csg.pov parsed as \"$csg\"" >&2 + echo " -- expected \"$csg_exp\"" >&2 + exit 1 +fi + +${top_builddir}/dimension/dimension -w768 -h480 -o csg.png ${srcdir}/csg.pov diff --git a/tests/dimension/demo.sh b/tests/dimension/demo.sh index 687d43c..7e9306a 100755 --- a/tests/dimension/demo.sh +++ b/tests/dimension/demo.sh @@ -34,7 +34,8 @@ demo_exp=$(echo -n \ (vector (integer 0) (float 0.1) (float 0.2) (float 0.1) (integer 0))) (light_source (vector (integer -15) (integer 20) (integer 10) (integer 0) (integer 0)) - (vector (integer 1) (integer 1) (integer 1) (integer 0) (integer 0))) + (vector (integer 1) (integer 1) (integer 1) (integer 0) (integer 0)) + object-modifiers) (difference (array (box -- cgit v1.2.3