/************************************************************************* * Copyright (C) 2009-2011 Tavian Barnes * * * * This file is part of Dimension. * * * * Dimension 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. * * * * Dimension 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 . * *************************************************************************/ #include "realize.h" #include "parse.h" #include "utility.h" #include #include #include #include static long dmnsn_realize_integer(dmnsn_astnode astnode) { switch (astnode.type) { case DMNSN_AST_INTEGER: return *(long *)astnode.ptr; case DMNSN_AST_FLOAT: { feclearexcept(FE_ALL_EXCEPT); long ret = lrint(*(double *)astnode.ptr); if (fetestexcept(FE_INVALID | FE_DIVBYZERO | FE_OVERFLOW | FE_UNDERFLOW)) dmnsn_error("Float out of range of integer."); return ret; } default: dmnsn_assert(false, "Invalid integer."); return 0; /* Silence compiler warning */ } } static double dmnsn_realize_float(dmnsn_astnode astnode) { switch (astnode.type) { case DMNSN_AST_FLOAT: return *(double *)astnode.ptr; case DMNSN_AST_INTEGER: return *(long *)astnode.ptr; default: dmnsn_assert(false, "Invalid float."); return 0; /* Silence compiler warning */ } } /* dmnsn_realize_string is an API function, so call this dmnsn_realize_str */ static const char* dmnsn_realize_str(dmnsn_astnode astnode) { dmnsn_assert(astnode.type == DMNSN_AST_STRING, "Expected a string."); return astnode.ptr; } static dmnsn_vector dmnsn_realize_vector(dmnsn_astnode astnode) { dmnsn_assert(astnode.type == DMNSN_AST_VECTOR, "Expected a vector."); dmnsn_astnode xnode, ynode, znode; dmnsn_array_get(astnode.children, 0, &xnode); dmnsn_array_get(astnode.children, 1, &ynode); dmnsn_array_get(astnode.children, 2, &znode); double x = dmnsn_realize_float(xnode), y = dmnsn_realize_float(ynode), z = dmnsn_realize_float(znode); return dmnsn_new_vector(x, y, z); } static dmnsn_color dmnsn_realize_color(dmnsn_astnode astnode) { dmnsn_assert(astnode.type == DMNSN_AST_VECTOR, "Expected a vector."); dmnsn_astnode rnode, gnode, bnode, fnode, tnode; dmnsn_array_get(astnode.children, 0, &rnode); dmnsn_array_get(astnode.children, 1, &gnode); dmnsn_array_get(astnode.children, 2, &bnode); dmnsn_array_get(astnode.children, 3, &fnode); dmnsn_array_get(astnode.children, 4, &tnode); return dmnsn_new_color5(dmnsn_realize_float(rnode), dmnsn_realize_float(gnode), dmnsn_realize_float(bnode), dmnsn_realize_float(fnode), dmnsn_realize_float(tnode)); } static dmnsn_matrix dmnsn_realize_translation(dmnsn_astnode astnode) { dmnsn_assert(astnode.type == DMNSN_AST_TRANSLATION, "Expected a translation."); dmnsn_astnode trans_node; dmnsn_array_get(astnode.children, 0, &trans_node); dmnsn_vector trans = dmnsn_realize_vector(trans_node); return dmnsn_translation_matrix(trans); } static dmnsn_matrix dmnsn_realize_scale(dmnsn_astnode astnode) { dmnsn_assert(astnode.type == DMNSN_AST_SCALE, "Expected a scale."); dmnsn_astnode scale_node; dmnsn_array_get(astnode.children, 0, &scale_node); dmnsn_vector scale = dmnsn_realize_vector(scale_node); return dmnsn_scale_matrix(scale); } static dmnsn_matrix dmnsn_realize_rotation(dmnsn_astnode astnode) { dmnsn_assert(astnode.type == DMNSN_AST_ROTATION, "Expected a rotation."); dmnsn_astnode angle_node; dmnsn_array_get(astnode.children, 0, &angle_node); dmnsn_vector angle = dmnsn_vector_mul( dmnsn_radians(1.0), dmnsn_realize_vector(angle_node) ); dmnsn_matrix trans = dmnsn_rotation_matrix( dmnsn_new_vector(angle.x, 0.0, 0.0) ); trans = dmnsn_matrix_mul( dmnsn_rotation_matrix(dmnsn_new_vector(0.0, angle.y, 0.0)), trans ); trans = dmnsn_matrix_mul( dmnsn_rotation_matrix(dmnsn_new_vector(0.0, 0.0, angle.z)), trans ); return trans; } static dmnsn_matrix dmnsn_realize_matrix(dmnsn_astnode astnode) { dmnsn_assert(astnode.type == DMNSN_AST_MATRIX, "Expected a matrix."); dmnsn_astnode *children = dmnsn_array_first(astnode.children); dmnsn_matrix trans; trans.n[0][0] = dmnsn_realize_float(children[0]); trans.n[0][1] = dmnsn_realize_float(children[1]); trans.n[0][2] = dmnsn_realize_float(children[2]); trans.n[0][3] = dmnsn_realize_float(children[9]); trans.n[1][0] = dmnsn_realize_float(children[3]); trans.n[1][1] = dmnsn_realize_float(children[4]); trans.n[1][2] = dmnsn_realize_float(children[5]); trans.n[1][3] = dmnsn_realize_float(children[10]); trans.n[2][0] = dmnsn_realize_float(children[6]); trans.n[2][1] = dmnsn_realize_float(children[7]); trans.n[2][2] = dmnsn_realize_float(children[8]); trans.n[2][3] = dmnsn_realize_float(children[11]); return trans; } static dmnsn_matrix dmnsn_realize_transformation(dmnsn_astnode astnode) { dmnsn_assert(astnode.type == DMNSN_AST_TRANSFORMATION, "Expected a transformation."); dmnsn_matrix trans = dmnsn_identity_matrix(); DMNSN_ARRAY_FOREACH (dmnsn_astnode *, child, astnode.children) { switch (child->type) { case DMNSN_AST_TRANSLATION: trans = dmnsn_matrix_mul(trans, dmnsn_realize_translation(*child)); break; case DMNSN_AST_SCALE: trans = dmnsn_matrix_mul(trans, dmnsn_realize_scale(*child)); break; case DMNSN_AST_ROTATION: trans = dmnsn_matrix_mul(trans, dmnsn_realize_rotation(*child)); break; case DMNSN_AST_MATRIX: trans = dmnsn_matrix_mul(trans, dmnsn_realize_matrix(*child)); break; case DMNSN_AST_INVERSE: trans = dmnsn_matrix_inverse(trans); break; case DMNSN_AST_TRANSFORMATION: trans = dmnsn_matrix_mul(trans, dmnsn_realize_transformation(*child)); break; default: dmnsn_assert(false, "Invalid transformation type."); break; } } return trans; } static void dmnsn_realize_global_settings(dmnsn_astnode astnode, dmnsn_scene *scene) { dmnsn_assert(astnode.type == DMNSN_AST_GLOBAL_SETTINGS, "Expected global settings."); DMNSN_ARRAY_FOREACH (dmnsn_astnode *, item, astnode.children) { dmnsn_astnode child; switch (item->type) { case DMNSN_AST_ADC_BAILOUT: dmnsn_array_get(item->children, 0, &child); scene->adc_bailout = dmnsn_realize_float(child); break; case DMNSN_AST_AMBIENT: dmnsn_array_get(item->children, 0, &child); scene->ambient = dmnsn_realize_color(child); scene->ambient.filter = 0.0; scene->ambient.trans = 0.0; break; case DMNSN_AST_MAX_TRACE_LEVEL: dmnsn_array_get(item->children, 0, &child); scene->reclimit = dmnsn_realize_integer(child); break; case DMNSN_AST_ASSUMED_GAMMA: case DMNSN_AST_CHARSET: case DMNSN_AST_MAX_INTERSECTIONS: /* Ignored settings */ break; default: dmnsn_assert(false, "Invalid global settings item."); } } } static dmnsn_pigment *dmnsn_realize_pigment(dmnsn_astnode astnode); static dmnsn_sky_sphere * dmnsn_realize_sky_sphere(dmnsn_astnode astnode) { dmnsn_assert(astnode.type == DMNSN_AST_SKY_SPHERE, "Expected a sky sphere."); dmnsn_sky_sphere *sky_sphere = dmnsn_new_sky_sphere(); DMNSN_ARRAY_FOREACH (dmnsn_astnode *, item, astnode.children) { switch (item->type) { case DMNSN_AST_PIGMENT: { dmnsn_pigment *pigment = dmnsn_realize_pigment(*item); dmnsn_array_push(sky_sphere->pigments, &pigment); break; } case DMNSN_AST_TRANSFORMATION: sky_sphere->trans = dmnsn_matrix_mul( dmnsn_realize_transformation(*item), sky_sphere->trans ); break; default: dmnsn_assert(false, "Invalid sky sphere item."); } } return sky_sphere; } static dmnsn_camera * dmnsn_realize_camera(dmnsn_astnode astnode) { dmnsn_assert(astnode.type == DMNSN_AST_CAMERA, "Expected a camera."); dmnsn_astnode_type camera_type = DMNSN_AST_PERSPECTIVE; dmnsn_vector location = dmnsn_new_vector(0.0, 0.0, 0.0); dmnsn_vector direction = dmnsn_new_vector(0.0, 0.0, 1.0); dmnsn_vector right = dmnsn_new_vector(4.0/3.0, 0.0, 0.0); dmnsn_vector up = dmnsn_new_vector(0.0, 1.0, 0.0); dmnsn_vector sky = dmnsn_new_vector(0.0, 1.0, 0.0); dmnsn_matrix trans = dmnsn_identity_matrix(); dmnsn_camera *camera = NULL; DMNSN_ARRAY_FOREACH (dmnsn_astnode *, item, astnode.children) { dmnsn_astnode child; switch (item->type) { /* Camera types */ case DMNSN_AST_PERSPECTIVE: camera_type = item->type; break; /* Camera vectors */ case DMNSN_AST_LOCATION: dmnsn_array_get(item->children, 0, &child); location = dmnsn_realize_vector(child); break; case DMNSN_AST_RIGHT: dmnsn_array_get(item->children, 0, &child); right = dmnsn_realize_vector(child); break; case DMNSN_AST_UP: dmnsn_array_get(item->children, 0, &child); right = dmnsn_realize_vector(child); break; case DMNSN_AST_SKY: dmnsn_array_get(item->children, 0, &child); sky = dmnsn_realize_vector(child); break; case DMNSN_AST_DIRECTION: dmnsn_array_get(item->children, 0, &child); direction = dmnsn_realize_vector(child); break; /* Camera modifiers */ case DMNSN_AST_LOOK_AT: { dmnsn_array_get(item->children, 0, &child); dmnsn_vector look_at = dmnsn_realize_vector(child); /* Line the camera up with the sky */ dmnsn_matrix sky1 = dmnsn_rotation_matrix( dmnsn_vector_mul( dmnsn_vector_axis_angle(up, sky, direction), dmnsn_vector_normalize(direction) ) ); up = dmnsn_transform_vector(sky1, up); right = dmnsn_transform_vector(sky1, right); dmnsn_matrix sky2 = dmnsn_rotation_matrix( dmnsn_vector_mul( dmnsn_vector_axis_angle(up, sky, right), dmnsn_vector_normalize(right) ) ); up = dmnsn_transform_vector(sky2, up); direction = dmnsn_transform_vector(sky2, direction); /* Line up the camera with the look_at */ look_at = dmnsn_vector_sub(look_at, location); dmnsn_matrix look_at1 = dmnsn_rotation_matrix( dmnsn_vector_mul( dmnsn_vector_axis_angle(direction, look_at, up), dmnsn_vector_normalize(up) ) ); right = dmnsn_transform_vector(look_at1, right); direction = dmnsn_transform_vector(look_at1, direction); dmnsn_matrix look_at2 = dmnsn_rotation_matrix( dmnsn_vector_mul( dmnsn_vector_axis_angle(direction, look_at, right), dmnsn_vector_normalize(right) ) ); up = dmnsn_transform_vector(look_at2, up); direction = dmnsn_transform_vector(look_at2, direction); break; } case DMNSN_AST_ANGLE: { dmnsn_array_get(item->children, 0, &child); double angle = dmnsn_radians(dmnsn_realize_float(child)); direction = dmnsn_vector_mul( 0.5*dmnsn_vector_norm(right)/tan(angle/2.0), dmnsn_vector_normalize(direction) ); break; } /* Transformations */ case DMNSN_AST_TRANSFORMATION: trans = dmnsn_matrix_mul(dmnsn_realize_transformation(*item), trans); break; default: dmnsn_assert(false, "Invalid camera item."); break; } } switch (camera_type) { case DMNSN_AST_PERSPECTIVE: { /* These multiplications are in right-to-left order so that user transformations happen after camera alignment */ trans = dmnsn_matrix_mul(trans, dmnsn_translation_matrix(location)); /* Align y with `up' */ dmnsn_matrix align = dmnsn_rotation_matrix( dmnsn_vector_mul( dmnsn_vector_axis_angle(dmnsn_y, up, dmnsn_z), dmnsn_z ) ); dmnsn_vector x = dmnsn_transform_vector(align, dmnsn_x); dmnsn_vector y = dmnsn_transform_vector(align, dmnsn_y); align = dmnsn_matrix_mul( dmnsn_rotation_matrix( dmnsn_vector_mul( dmnsn_vector_axis_angle(y, up, x), x ) ), align ); /* Align x with `right' */ align = dmnsn_matrix_mul( dmnsn_rotation_matrix( dmnsn_vector_mul( dmnsn_vector_axis_angle(x, right, up), dmnsn_vector_normalize(up) ) ), align ); trans = dmnsn_matrix_mul(trans, align); /* Scale the camera with `up', `right', and `direction' */ trans = dmnsn_matrix_mul( trans, dmnsn_scale_matrix( dmnsn_new_vector( dmnsn_vector_norm(right), dmnsn_vector_norm(up), dmnsn_vector_norm(direction) ) ) ); camera = dmnsn_new_perspective_camera(); break; } default: dmnsn_assert(false, "Unsupported camera type."); } camera->trans = trans; return camera; } static dmnsn_pattern * dmnsn_realize_pattern(dmnsn_astnode astnode) { dmnsn_assert(astnode.type == DMNSN_AST_PATTERN, "Expected a pattern."); dmnsn_astnode type; dmnsn_array_get(astnode.children, 0, &type); dmnsn_pattern *pattern = NULL; switch (type.type) { case DMNSN_AST_CHECKER: pattern = dmnsn_new_checker_pattern(); break; case DMNSN_AST_GRADIENT: { dmnsn_astnode orientation; dmnsn_array_get(type.children, 0, &orientation); dmnsn_vector v = dmnsn_realize_vector(orientation); pattern = dmnsn_new_gradient_pattern(v); break; } default: dmnsn_assert(false, "Unexpected pattern type."); } return pattern; } static dmnsn_map * dmnsn_realize_color_list(dmnsn_astnode astnode) { dmnsn_assert(astnode.type == DMNSN_AST_COLOR_LIST, "Expected a color list."); dmnsn_map *color_map = dmnsn_new_color_map(); double n = 0.0, i = 1.0/(dmnsn_array_size(astnode.children) - 1); DMNSN_ARRAY_FOREACH (dmnsn_astnode *, entry, astnode.children) { dmnsn_color color = dmnsn_realize_color(*entry); dmnsn_add_map_entry(color_map, n, &color); n += i; } return color_map; } static dmnsn_map * dmnsn_realize_color_map(dmnsn_astnode astnode) { dmnsn_assert(astnode.type == DMNSN_AST_COLOR_MAP, "Expected a color_map."); dmnsn_map *color_map = dmnsn_new_color_map(); DMNSN_ARRAY_FOREACH (dmnsn_astnode *, entry, astnode.children) { dmnsn_assert(entry->type == DMNSN_AST_COLOR_MAP_ENTRY, "Expected a color_map entry."); dmnsn_astnode n_node, color_node; dmnsn_array_get(entry->children, 0, &n_node); dmnsn_array_get(entry->children, 1, &color_node); double n = dmnsn_realize_float(n_node); dmnsn_color color = dmnsn_realize_color(color_node); dmnsn_add_map_entry(color_map, n, &color); } return color_map; } static dmnsn_map * dmnsn_realize_pigment_list(dmnsn_astnode astnode) { dmnsn_assert(astnode.type == DMNSN_AST_PIGMENT_LIST, "Expected a pigment list."); dmnsn_map *pigment_map = dmnsn_new_pigment_map(); double n = 0.0, i = 1.0/(dmnsn_array_size(astnode.children) - 1); DMNSN_ARRAY_FOREACH (dmnsn_astnode *, entry, astnode.children) { dmnsn_pigment *pigment = dmnsn_realize_pigment(*entry); dmnsn_add_map_entry(pigment_map, n, &pigment); n += i; } return pigment_map; } static dmnsn_map * dmnsn_realize_pigment_map(dmnsn_astnode astnode) { dmnsn_assert(astnode.type == DMNSN_AST_PIGMENT_MAP, "Expected a pigment_map."); dmnsn_map *pigment_map = dmnsn_new_pigment_map(); DMNSN_ARRAY_FOREACH (dmnsn_astnode *, entry, astnode.children) { dmnsn_assert(entry->type == DMNSN_AST_PIGMENT_MAP_ENTRY, "Expected a pigment_map entry."); dmnsn_astnode n_node, pigment_node; dmnsn_array_get(entry->children, 0, &n_node); dmnsn_array_get(entry->children, 1, &pigment_node); double n = dmnsn_realize_float(n_node); dmnsn_pigment *pigment = dmnsn_realize_pigment(pigment_node); dmnsn_add_map_entry(pigment_map, n, &pigment); } return pigment_map; } static dmnsn_pigment * dmnsn_realize_pattern_pigment(dmnsn_astnode type, dmnsn_astnode modifiers) { dmnsn_assert(modifiers.type == DMNSN_AST_PIGMENT_MODIFIERS, "Expected pigment modifiers"); dmnsn_pattern *pattern = dmnsn_realize_pattern(type); dmnsn_map *color_map = NULL, *pigment_map = NULL; /* Set up the map */ DMNSN_ARRAY_FOREACH_REVERSE (dmnsn_astnode *, modifier, modifiers.children) { switch (modifier->type) { case DMNSN_AST_COLOR_LIST: color_map = dmnsn_realize_color_list(*modifier); break; case DMNSN_AST_COLOR_MAP: color_map = dmnsn_realize_color_map(*modifier); break; case DMNSN_AST_PIGMENT_LIST: pigment_map = dmnsn_realize_pigment_list(*modifier); break; case DMNSN_AST_PIGMENT_MAP: pigment_map = dmnsn_realize_pigment_map(*modifier); break; default: break; } if (color_map || pigment_map) break; } /* Now add the default values */ dmnsn_astnode pattern_type; dmnsn_array_get(type.children, 0, &pattern_type); switch (pattern_type.type) { case DMNSN_AST_CHECKER: /* Default checker pattern is blue and green */ if (!color_map && !pigment_map) { color_map = dmnsn_new_color_map(); if (dmnsn_map_size(color_map) < 1) dmnsn_add_map_entry(color_map, 0.0, &dmnsn_blue); if (dmnsn_map_size(color_map) < 2) dmnsn_add_map_entry(color_map, 1.0, &dmnsn_green); } break; default: /* Default map is grayscale */ if (!color_map && !pigment_map) { color_map = dmnsn_new_color_map(); dmnsn_add_map_entry(color_map, 0.0, &dmnsn_black); dmnsn_add_map_entry(color_map, 1.0, &dmnsn_white); } break; } dmnsn_pigment *pigment = NULL; if (color_map) { pigment = dmnsn_new_color_map_pigment(pattern, color_map); } else if (pigment_map) { pigment = dmnsn_new_pigment_map_pigment(pattern, pigment_map); } else { dmnsn_assert(false, "No appropriate map constructed."); } return pigment; } static void dmnsn_realize_pigment_modifiers(dmnsn_astnode astnode, dmnsn_pigment *pigment) { dmnsn_assert(astnode.type == DMNSN_AST_PIGMENT_MODIFIERS, "Expected pigment modifiers."); DMNSN_ARRAY_FOREACH (dmnsn_astnode *, modifier, astnode.children) { switch (modifier->type) { case DMNSN_AST_TRANSFORMATION: pigment->trans = dmnsn_matrix_mul( dmnsn_realize_transformation(*modifier), pigment->trans ); break; case DMNSN_AST_QUICK_COLOR: { dmnsn_astnode quick_color; dmnsn_array_get(modifier->children, 0, &quick_color); pigment->quick_color = dmnsn_realize_color(quick_color); break; } case DMNSN_AST_COLOR_LIST: case DMNSN_AST_COLOR_MAP: case DMNSN_AST_PIGMENT_LIST: case DMNSN_AST_PIGMENT_MAP: /* Already handled by dmnsn_realize_pattern_pigment() */ break; default: dmnsn_assert(false, "Invalid pigment modifier."); } } } static dmnsn_pigment * dmnsn_realize_pigment(dmnsn_astnode astnode) { dmnsn_assert(astnode.type == DMNSN_AST_PIGMENT, "Expected a pigment."); dmnsn_pigment *pigment = NULL; dmnsn_astnode type_node, modifiers; dmnsn_array_get(astnode.children, 0, &type_node); dmnsn_array_get(astnode.children, 1, &modifiers); switch (type_node.type) { case DMNSN_AST_NONE: break; case DMNSN_AST_VECTOR: { dmnsn_color color = dmnsn_realize_color(type_node); pigment = dmnsn_new_solid_pigment(color); break; } case DMNSN_AST_PATTERN: { pigment = dmnsn_realize_pattern_pigment(type_node, modifiers); break; } case DMNSN_AST_IMAGE_MAP: { dmnsn_astnode filetype, strnode; dmnsn_array_get(type_node.children, 0, &filetype); dmnsn_array_get(type_node.children, 1, &strnode); const char *path = dmnsn_realize_str(strnode); FILE *file = fopen(path, "rb"); if (!file) { dmnsn_error("Couldn't open image file."); } dmnsn_canvas *canvas; switch (filetype.type) { case DMNSN_AST_PNG: canvas = dmnsn_png_read_canvas(file); if (!canvas) { dmnsn_error("Invalid PNG file."); } pigment = dmnsn_new_canvas_pigment(canvas); break; default: dmnsn_assert(false, "Invalid image_map type."); break; } break; } default: dmnsn_assert(false, "Invalid pigment type."); } dmnsn_realize_pigment_modifiers(modifiers, pigment); return pigment; } static dmnsn_finish * dmnsn_realize_reflection(dmnsn_astnode astnode) { dmnsn_assert(astnode.type == DMNSN_AST_REFLECTION, "Expected a reflection."); dmnsn_astnode min_node, max_node; dmnsn_array_get(astnode.children, 0, &min_node); dmnsn_array_get(astnode.children, 1, &max_node); dmnsn_color min = dmnsn_realize_color(min_node); dmnsn_color max = dmnsn_realize_color(max_node); double falloff = 1.0; dmnsn_astnode items; dmnsn_array_get(astnode.children, 2, &items); DMNSN_ARRAY_FOREACH (dmnsn_astnode *, item, items.children) { dmnsn_astnode child; switch (item->type) { case DMNSN_AST_FALLOFF: dmnsn_array_get(item->children, 0, &child); falloff = dmnsn_realize_float(child); break; default: dmnsn_assert(false, "Invalid reflection item."); } } dmnsn_finish *reflection = dmnsn_new_reflective_finish(min, max, falloff); return reflection; } static dmnsn_finish * dmnsn_realize_finish(dmnsn_astnode astnode) { dmnsn_assert(astnode.type == DMNSN_AST_FINISH, "Expected a finish."); dmnsn_finish *finish = dmnsn_new_finish(); dmnsn_color ambient = dmnsn_black; bool ambient_set = false; double diffuse = 0.0; bool diffuse_set = false; double phong = 0.0; double phong_size = 40.0; dmnsn_finish *reflection = NULL; DMNSN_ARRAY_FOREACH (dmnsn_astnode *, item, astnode.children) { dmnsn_astnode child; switch (item->type) { case DMNSN_AST_AMBIENT: dmnsn_array_get(item->children, 0, &child); ambient = dmnsn_realize_color(child); ambient_set = true; break; case DMNSN_AST_DIFFUSE: dmnsn_array_get(item->children, 0, &child); diffuse = dmnsn_realize_float(child); diffuse_set = true; break; case DMNSN_AST_PHONG: dmnsn_array_get(item->children, 0, &child); phong = dmnsn_realize_float(child); break; case DMNSN_AST_PHONG_SIZE: dmnsn_array_get(item->children, 0, &child); phong_size = dmnsn_realize_float(child); break; case DMNSN_AST_REFLECTION: dmnsn_delete_finish(reflection); reflection = dmnsn_realize_reflection(*item); break; default: dmnsn_assert(false, "Invalid finish item."); } } if (ambient_set) { finish = dmnsn_new_finish_combination( dmnsn_new_ambient_finish(ambient), finish ); } if (diffuse_set) { finish = dmnsn_new_finish_combination( dmnsn_new_diffuse_finish(diffuse), finish ); } if (phong) { finish = dmnsn_new_finish_combination( dmnsn_new_phong_finish(phong, phong_size), finish ); } if (reflection) { finish = dmnsn_new_finish_combination(reflection, finish); } return finish; } static dmnsn_texture * dmnsn_realize_texture(dmnsn_astnode astnode) { dmnsn_assert(astnode.type == DMNSN_AST_TEXTURE, "Expected a texture."); dmnsn_texture *texture = dmnsn_new_texture(); DMNSN_ARRAY_FOREACH (dmnsn_astnode *, item, astnode.children) { switch (item->type) { case DMNSN_AST_PIGMENT: dmnsn_delete_pigment(texture->pigment); texture->pigment = dmnsn_realize_pigment(*item); break; case DMNSN_AST_FINISH: dmnsn_delete_finish(texture->finish); texture->finish = dmnsn_realize_finish(*item); break; case DMNSN_AST_TRANSFORMATION: texture->trans = dmnsn_matrix_mul( dmnsn_realize_transformation(*item), texture->trans ); break; default: dmnsn_assert(false, "Invalid texture item."); } } return texture; } static dmnsn_interior * dmnsn_realize_interior(dmnsn_astnode astnode) { dmnsn_assert(astnode.type == DMNSN_AST_INTERIOR, "Expected a texture."); dmnsn_interior *interior = dmnsn_new_interior(); DMNSN_ARRAY_FOREACH (dmnsn_astnode *, item, astnode.children) { dmnsn_astnode child; switch (item->type) { case DMNSN_AST_IOR: dmnsn_array_get(item->children, 0, &child); interior->ior = dmnsn_realize_float(child); break; default: dmnsn_assert(false, "Invalid interior item."); } } return interior; } static void dmnsn_realize_object_modifiers(dmnsn_astnode astnode, dmnsn_object *object) { dmnsn_assert(astnode.type == DMNSN_AST_OBJECT_MODIFIERS, "Expected object modifiers."); /* Save the pre-existing transformations */ dmnsn_matrix existing_trans = dmnsn_matrix_inverse(object->trans); DMNSN_ARRAY_FOREACH (dmnsn_astnode *, modifier, astnode.children) { switch (modifier->type) { case DMNSN_AST_TRANSFORMATION: object->trans = dmnsn_matrix_mul( dmnsn_realize_transformation(*modifier), object->trans ); break; case DMNSN_AST_TEXTURE: dmnsn_delete_texture(object->texture); object->texture = dmnsn_realize_texture(*modifier); break; case DMNSN_AST_PIGMENT: if (!object->texture) object->texture = dmnsn_new_texture(); dmnsn_delete_pigment(object->texture->pigment); object->texture->pigment = dmnsn_realize_pigment(*modifier); break; case DMNSN_AST_FINISH: if (!object->texture) object->texture = dmnsn_new_texture(); dmnsn_delete_finish(object->texture->finish); object->texture->finish = dmnsn_realize_finish(*modifier); break; case DMNSN_AST_INTERIOR: dmnsn_delete_interior(object->interior); object->interior = dmnsn_realize_interior(*modifier); break; case DMNSN_AST_STURM: /* Ignored */ break; default: dmnsn_assert(false, "Invalid object modifier."); } } if (object->texture) { /* Right-multiply to counteract any pre-existing transformations -- this means, for example, that the transformation that makes a sphere have radius 2 doesn't scale the texture by a factor of 2 */ object->texture->trans = dmnsn_matrix_mul( object->texture->trans, existing_trans ); } } static void dmnsn_realize_light_source_modifiers(dmnsn_astnode astnode, dmnsn_light *light) { dmnsn_assert(astnode.type == DMNSN_AST_OBJECT_MODIFIERS, "Expected object modifiers."); DMNSN_ARRAY_FOREACH (dmnsn_astnode *, modifier, astnode.children) { switch (modifier->type) { case DMNSN_AST_TRANSFORMATION: light->x0 = dmnsn_transform_vector( dmnsn_realize_transformation(*modifier), light->x0 ); break; case DMNSN_AST_TEXTURE: case DMNSN_AST_PIGMENT: case DMNSN_AST_FINISH: case DMNSN_AST_INTERIOR: /* Ignore other object modifiers */ 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) { dmnsn_assert(astnode.type == DMNSN_AST_BOX, "Expected a box."); dmnsn_astnode corner1, corner2; dmnsn_array_get(astnode.children, 0, &corner1); dmnsn_array_get(astnode.children, 1, &corner2); dmnsn_vector x1 = dmnsn_realize_vector(corner1), x2 = dmnsn_realize_vector(corner2); dmnsn_object *box = dmnsn_new_cube(); box->trans = dmnsn_scale_matrix( dmnsn_new_vector(fabs(x2.x - x1.x)/2.0, fabs(x2.y - x1.y)/2.0, fabs(x2.z - x1.z)/2.0) ); box->trans = dmnsn_matrix_mul( dmnsn_translation_matrix(dmnsn_new_vector((x2.x + x1.x)/2.0, (x2.y + x1.y)/2.0, (x2.z + x1.z)/2.0)), box->trans ); return box; } static dmnsn_object * dmnsn_realize_cone(dmnsn_astnode astnode) { dmnsn_astnode pnode1, rnode1, pnode2, rnode2, open; if (astnode.type == DMNSN_AST_CONE) { dmnsn_array_get(astnode.children, 0, &pnode1); dmnsn_array_get(astnode.children, 1, &rnode1); dmnsn_array_get(astnode.children, 2, &pnode2); dmnsn_array_get(astnode.children, 3, &rnode2); dmnsn_array_get(astnode.children, 4, &open); } else if (astnode.type == DMNSN_AST_CYLINDER) { dmnsn_array_get(astnode.children, 0, &pnode1); dmnsn_array_get(astnode.children, 1, &pnode2); dmnsn_array_get(astnode.children, 2, &rnode1); dmnsn_array_get(astnode.children, 2, &rnode2); dmnsn_array_get(astnode.children, 3, &open); } else { dmnsn_assert(false, "Expected a cone or cylinder."); } dmnsn_vector p1 = dmnsn_realize_vector(pnode1); dmnsn_vector p2 = dmnsn_realize_vector(pnode2); double r1 = dmnsn_realize_float(rnode1); double r2 = dmnsn_realize_float(rnode2); dmnsn_vector dir = dmnsn_vector_sub(p2, p1); double l = dmnsn_vector_norm(dir); double theta1 = dmnsn_vector_axis_angle(dmnsn_y, dir, dmnsn_x); double theta2 = dmnsn_vector_axis_angle(dmnsn_y, dir, dmnsn_z); dmnsn_object *cone = dmnsn_new_cone(r1, r2, dmnsn_realize_integer(open)); /* Transformations: lift the cone to start at the origin, scale, rotate, and translate properly */ cone->trans = dmnsn_translation_matrix(dmnsn_new_vector(0.0, 1.0, 0.0)); cone->trans = dmnsn_matrix_mul( dmnsn_scale_matrix(dmnsn_new_vector(1.0, l/2.0, 1.0)), cone->trans ); cone->trans = dmnsn_matrix_mul( dmnsn_rotation_matrix(dmnsn_new_vector(theta1, 0.0, 0.0)), cone->trans ); cone->trans = dmnsn_matrix_mul( dmnsn_rotation_matrix(dmnsn_new_vector(0.0, 0.0, theta2)), cone->trans ); cone->trans = dmnsn_matrix_mul( dmnsn_translation_matrix(p1), cone->trans ); return cone; } static dmnsn_object * dmnsn_realize_sphere(dmnsn_astnode astnode) { dmnsn_assert(astnode.type == DMNSN_AST_SPHERE, "Expected a sphere."); dmnsn_astnode center, radius; dmnsn_array_get(astnode.children, 0, ¢er); dmnsn_array_get(astnode.children, 1, &radius); dmnsn_vector x0 = dmnsn_realize_vector(center); double r = dmnsn_realize_float(radius); dmnsn_object *sphere = dmnsn_new_sphere(); sphere->trans = dmnsn_scale_matrix(dmnsn_new_vector(r, r, r)); sphere->trans = dmnsn_matrix_mul(dmnsn_translation_matrix(x0), sphere->trans); return sphere; } static dmnsn_object * dmnsn_realize_torus(dmnsn_astnode astnode) { dmnsn_assert(astnode.type == DMNSN_AST_TORUS, "Expected a torus."); dmnsn_astnode major, minor; dmnsn_array_get(astnode.children, 0, &major); dmnsn_array_get(astnode.children, 1, &minor); double R = dmnsn_realize_float(major); double r = dmnsn_realize_float(minor); dmnsn_object *torus = dmnsn_new_torus(R, r); return torus; } static dmnsn_object * dmnsn_realize_plane(dmnsn_astnode astnode) { dmnsn_assert(astnode.type == DMNSN_AST_PLANE, "Expected a plane."); dmnsn_astnode normal, distance; dmnsn_array_get(astnode.children, 0, &normal); dmnsn_array_get(astnode.children, 1, &distance); dmnsn_vector n = dmnsn_vector_normalize(dmnsn_realize_vector(normal)); double d = dmnsn_realize_float(distance); dmnsn_object *plane = dmnsn_new_plane(n); plane->trans = dmnsn_translation_matrix(dmnsn_vector_mul(d, n)); return plane; } /* Bulk-load a union */ static dmnsn_object * dmnsn_realize_union(dmnsn_astnode astnode, dmnsn_astnode modifiers, dmnsn_array *lights) { dmnsn_assert(astnode.type == DMNSN_AST_UNION, "Expected a union."); dmnsn_array *children = dmnsn_new_array(sizeof(dmnsn_object *)); DMNSN_ARRAY_FOREACH (dmnsn_astnode *, onode, astnode.children) { 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); if (object) dmnsn_array_push(children, &object); } } dmnsn_object *csg = NULL; if (dmnsn_array_size(children) > 0) csg = dmnsn_new_csg_union(children); dmnsn_delete_array(children); return csg; } typedef dmnsn_object *dmnsn_csg_object_fn(dmnsn_object *a, dmnsn_object *b); /* Generalized CSG realizer */ static dmnsn_object * dmnsn_realize_csg(dmnsn_astnode astnode, dmnsn_astnode modifiers, dmnsn_array *lights, dmnsn_csg_object_fn *csg_object_fn) { dmnsn_object *csg = NULL; dmnsn_astnode *onode; for (onode = dmnsn_array_first(astnode.children); onode <= (dmnsn_astnode *)dmnsn_array_last(astnode.children); ++onode) { 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); break; } } for (++onode; onode <= (dmnsn_astnode *)dmnsn_array_last(astnode.children); ++onode) { 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); } } return csg; } static dmnsn_object * dmnsn_realize_intersection(dmnsn_astnode astnode, dmnsn_astnode modifiers, dmnsn_array *lights) { dmnsn_assert(astnode.type == DMNSN_AST_INTERSECTION, "Expected an intersection."); return dmnsn_realize_csg(astnode, modifiers, lights, dmnsn_new_csg_intersection); } static dmnsn_object * dmnsn_realize_difference(dmnsn_astnode astnode, dmnsn_astnode modifiers, dmnsn_array *lights) { dmnsn_assert(astnode.type == DMNSN_AST_DIFFERENCE, "Expected a difference."); return dmnsn_realize_csg(astnode, modifiers, lights, dmnsn_new_csg_difference); } static dmnsn_object * dmnsn_realize_merge(dmnsn_astnode astnode, dmnsn_astnode modifiers, dmnsn_array *lights) { dmnsn_assert(astnode.type == DMNSN_AST_MERGE, "Expected a merge."); return dmnsn_realize_csg(astnode, modifiers, lights, dmnsn_new_csg_merge); } /* Realize an object, or maybe a light */ static dmnsn_object * dmnsn_realize_object(dmnsn_astnode astnode, dmnsn_array *lights) { dmnsn_assert(astnode.type == DMNSN_AST_OBJECT || astnode.type == DMNSN_AST_LIGHT_SOURCE, "Expected an object."); dmnsn_astnode onode, modifiers; dmnsn_array_get(astnode.children, 0, &onode); dmnsn_array_get(astnode.children, 1, &modifiers); dmnsn_object *object = NULL; switch (onode.type) { case DMNSN_AST_BOX: object = dmnsn_realize_box(onode); break; case DMNSN_AST_CONE: case DMNSN_AST_CYLINDER: object = dmnsn_realize_cone(onode); break; case DMNSN_AST_DIFFERENCE: object = dmnsn_realize_difference(onode, modifiers, lights); break; case DMNSN_AST_INTERSECTION: object = dmnsn_realize_intersection(onode, modifiers, lights); break; case DMNSN_AST_MERGE: object = dmnsn_realize_merge(onode, modifiers, lights); break; case DMNSN_AST_PLANE: object = dmnsn_realize_plane(onode); break; case DMNSN_AST_SPHERE: object = dmnsn_realize_sphere(onode); break; case DMNSN_AST_TORUS: object = dmnsn_realize_torus(onode); break; case DMNSN_AST_UNION: object = dmnsn_realize_union(onode, modifiers, lights); break; 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 type."); } if (object) { dmnsn_realize_object_modifiers(modifiers, object); } return object; } static dmnsn_scene * dmnsn_realize_astree(const dmnsn_astree *astree) { dmnsn_scene *scene = dmnsn_new_scene(); /* Default finish */ scene->default_texture->finish = dmnsn_new_finish_combination( dmnsn_new_ambient_finish( dmnsn_color_mul(0.1, dmnsn_white) ), dmnsn_new_diffuse_finish(0.6) ); /* Background color */ scene->background = dmnsn_black; /* Create the default perspective camera */ scene->camera = dmnsn_new_perspective_camera(); /* * Now parse the abstract syntax tree */ DMNSN_ARRAY_FOREACH (dmnsn_astnode *, astnode, astree) { dmnsn_astnode child; dmnsn_light *light; dmnsn_object *object; switch (astnode->type) { case DMNSN_AST_GLOBAL_SETTINGS: dmnsn_realize_global_settings(*astnode, scene); break; case DMNSN_AST_BACKGROUND: dmnsn_array_get(astnode->children, 0, &child); scene->background = dmnsn_realize_color(child); break; case DMNSN_AST_SKY_SPHERE: dmnsn_delete_sky_sphere(scene->sky_sphere); scene->sky_sphere = dmnsn_realize_sky_sphere(*astnode); break; case DMNSN_AST_CAMERA: dmnsn_delete_camera(scene->camera); scene->camera = dmnsn_realize_camera(*astnode); break; case DMNSN_AST_OBJECT: object = dmnsn_realize_object(*astnode, scene->lights); if (object) dmnsn_array_push(scene->objects, &object); break; case DMNSN_AST_LIGHT_SOURCE: light = dmnsn_realize_light_source(*astnode); dmnsn_array_push(scene->lights, &light); break; default: dmnsn_assert(false, "Unrecognised syntax element."); } } return scene; } dmnsn_scene * dmnsn_realize(FILE *file, dmnsn_symbol_table *symtable) { if (!symtable) { symtable = dmnsn_new_symbol_table(); } dmnsn_astree *astree = dmnsn_parse(file, symtable); if (!astree) { return NULL; } dmnsn_scene *scene = dmnsn_realize_astree(astree); dmnsn_delete_astree(astree); return scene; } dmnsn_scene * dmnsn_realize_string(const char *str, dmnsn_symbol_table *symtable) { if (!symtable) { symtable = dmnsn_new_symbol_table(); } dmnsn_astree *astree = dmnsn_parse_string(str, symtable); if (!astree) { return NULL; } dmnsn_scene *scene = dmnsn_realize_astree(astree); dmnsn_delete_astree(astree); return scene; }