From da0b16d79c05a83d14195eaf509352effbe5a215 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Fri, 27 Nov 2009 01:43:36 -0500 Subject: Fake camera semantics until I figure out the real ones. --- dimension/realize.c | 218 ++++++++++++++++++++++++++++++-------- libdimension/dimension/geometry.h | 16 +++ libdimension/geometry.c | 17 +++ tests/dimension/demo.pov | 4 +- tests/dimension/demo.sh | 2 +- 5 files changed, 212 insertions(+), 45 deletions(-) diff --git a/dimension/realize.c b/dimension/realize.c index 4832fd2..ea7fce8 100644 --- a/dimension/realize.c +++ b/dimension/realize.c @@ -85,8 +85,8 @@ dmnsn_realize_color(dmnsn_astnode astnode) return color; } -static dmnsn_object * -dmnsn_realize_rotation(dmnsn_astnode astnode, dmnsn_object *object) +static dmnsn_matrix +dmnsn_realize_rotation(dmnsn_astnode astnode) { if (astnode.type != DMNSN_AST_ROTATION) { dmnsn_error(DMNSN_SEVERITY_HIGH, "Expected a rotation."); @@ -102,25 +102,23 @@ dmnsn_realize_rotation(dmnsn_astnode astnode, dmnsn_object *object) dmnsn_realize_vector(angle_node) ); - /* Rotations in POV-Ray are done about the X-axis first, then Y, then Z */ - object->trans = dmnsn_matrix_mul( - dmnsn_rotation_matrix(dmnsn_new_vector(angle.x, 0.0, 0.0)), - object->trans + dmnsn_matrix trans = dmnsn_rotation_matrix( + dmnsn_new_vector(angle.x, 0.0, 0.0) ); - object->trans = dmnsn_matrix_mul( + trans = dmnsn_matrix_mul( dmnsn_rotation_matrix(dmnsn_new_vector(0.0, angle.y, 0.0)), - object->trans + trans ); - object->trans = dmnsn_matrix_mul( + trans = dmnsn_matrix_mul( dmnsn_rotation_matrix(dmnsn_new_vector(0.0, 0.0, angle.z)), - object->trans + trans ); - return object; + return trans; } -static dmnsn_object * -dmnsn_realize_scale(dmnsn_astnode astnode, dmnsn_object *object) +static dmnsn_matrix +dmnsn_realize_scale(dmnsn_astnode astnode) { if (astnode.type != DMNSN_AST_SCALE) { dmnsn_error(DMNSN_SEVERITY_HIGH, "Expected a scale."); @@ -130,12 +128,11 @@ dmnsn_realize_scale(dmnsn_astnode astnode, dmnsn_object *object) dmnsn_array_get(astnode.children, 0, &scale_node); dmnsn_vector scale = dmnsn_realize_vector(scale_node); - object->trans = dmnsn_matrix_mul(dmnsn_scale_matrix(scale), object->trans); - return object; + return dmnsn_scale_matrix(scale); } -static dmnsn_object * -dmnsn_realize_translation(dmnsn_astnode astnode, dmnsn_object *object) +static dmnsn_matrix +dmnsn_realize_translation(dmnsn_astnode astnode) { if (astnode.type != DMNSN_AST_TRANSLATION) { dmnsn_error(DMNSN_SEVERITY_HIGH, "Expected a translation."); @@ -145,11 +142,153 @@ dmnsn_realize_translation(dmnsn_astnode astnode, dmnsn_object *object) dmnsn_array_get(astnode.children, 0, &trans_node); dmnsn_vector trans = dmnsn_realize_vector(trans_node); - object->trans = dmnsn_matrix_mul( - dmnsn_translation_matrix(trans), - object->trans - ); - return object; + return dmnsn_translation_matrix(trans); +} + +static dmnsn_camera * +dmnsn_realize_camera(dmnsn_astnode astnode) +{ + const double deg2rad = atan(1.0)/45.0; + + dmnsn_astnode_type camera_type = DMNSN_AST_PERSPECTIVE; + dmnsn_vector location = dmnsn_new_vector(0.0, 0.0, 0.0); + dmnsn_vector look_at = 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_vector direction = dmnsn_new_vector(0.0, 0.0, 1.0); + double angle = 0.0; + dmnsn_matrix trans = dmnsn_identity_matrix(); + + dmnsn_camera *camera = NULL; + + unsigned int i; + for (i = 0; i < dmnsn_array_size(astnode.children); ++i) { + dmnsn_astnode item; + dmnsn_array_get(astnode.children, i, &item); + + 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, &item); + location = dmnsn_realize_vector(item); + break; + case DMNSN_AST_RIGHT: + dmnsn_array_get(item.children, 0, &item); + right = dmnsn_realize_vector(item); + break; + case DMNSN_AST_UP: + dmnsn_array_get(item.children, 0, &item); + right = dmnsn_realize_vector(item); + break; + case DMNSN_AST_SKY: + dmnsn_array_get(item.children, 0, &item); + sky = dmnsn_realize_vector(item); + break; + + /* Camera modifiers */ + case DMNSN_AST_LOOK_AT: + dmnsn_array_get(item.children, 0, &item); + look_at = dmnsn_realize_vector(item); + break; + case DMNSN_AST_ANGLE: + dmnsn_array_get(item.children, 0, &item); + angle = deg2rad*dmnsn_realize_float(item); + break; + + /* Transformations */ + case DMNSN_AST_ROTATION: + trans = dmnsn_matrix_mul(dmnsn_realize_rotation(item), trans); + break; + case DMNSN_AST_SCALE: + trans = dmnsn_matrix_mul(dmnsn_realize_scale(item), trans); + break; + case DMNSN_AST_TRANSLATION: + trans = dmnsn_matrix_mul(dmnsn_realize_translation(item), trans); + break; + + default: + dmnsn_error(DMNSN_SEVERITY_HIGH, "Invalid camera item."); + break; + } + } + + switch (camera_type) { + case DMNSN_AST_PERSPECTIVE: + { + /* TODO: this is not quite right, but it handles cameras specified with + `look_at' and `angle' correctly. `right' and `up' are assumed to point + in the x and y directions, respectively. `direction' and `sky' are + broken. */ + + /* Set the direction vector if we were given an angle */ + if (angle) { + direction = dmnsn_new_vector( + 0.0, + 0.0, + 0.5*dmnsn_vector_norm(right)/tan(angle/2) + ); + } + + /* These multiplications are in right-to-left order */ + + trans = dmnsn_matrix_mul(trans, dmnsn_translation_matrix(location)); + + /* Pan left-right */ + trans = dmnsn_matrix_mul( + trans, + dmnsn_rotation_matrix( + dmnsn_vector_mul( + dmnsn_vector_axis_angle( + direction, + dmnsn_vector_sub(look_at, location), + sky + ), + dmnsn_vector_normalize(sky) + ) + ) + ); + /* Pan up-down */ + trans = dmnsn_matrix_mul( + trans, + dmnsn_rotation_matrix( + dmnsn_vector_mul( + dmnsn_vector_axis_angle( + direction, + dmnsn_vector_sub(look_at, location), + right + ), + dmnsn_vector_normalize(right) + ) + ) + ); + + 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(); + dmnsn_set_perspective_camera_trans(camera, trans); + break; + } + + default: + dmnsn_error(DMNSN_SEVERITY_HIGH, "Unsupported camera type."); + } + + return camera; } static dmnsn_object * @@ -229,13 +368,22 @@ dmnsn_realize_object_modifiers(dmnsn_astnode astnode, dmnsn_object *object) switch (modifier.type) { case DMNSN_AST_ROTATION: - object = dmnsn_realize_rotation(modifier, object); + object->trans = dmnsn_matrix_mul( + dmnsn_realize_rotation(modifier), + object->trans + ); break; case DMNSN_AST_SCALE: - object = dmnsn_realize_scale(modifier, object); + object->trans = dmnsn_matrix_mul( + dmnsn_realize_scale(modifier), + object->trans + ); break; case DMNSN_AST_TRANSLATION: - object = dmnsn_realize_translation(modifier, object); + object->trans = dmnsn_matrix_mul( + dmnsn_realize_translation(modifier), + object->trans + ); break; case DMNSN_AST_TEXTURE: @@ -365,28 +513,12 @@ dmnsn_realize(const dmnsn_array *astree) return NULL; } - /* Set up the transformation matrix for the perspective camera */ - dmnsn_matrix trans = dmnsn_scale_matrix( - dmnsn_new_vector( - ((double)scene->canvas->x)/scene->canvas->y, 1.0, 1.0 - ) - ); - trans = dmnsn_matrix_mul( - dmnsn_translation_matrix(dmnsn_new_vector(0.0, 0.0, -4.0)), - trans - ); - trans = dmnsn_matrix_mul( - dmnsn_rotation_matrix(dmnsn_new_vector(0.0, 1.0, 0.0)), - trans - ); - - /* Create a perspective camera */ + /* Create the default perspective camera */ scene->camera = dmnsn_new_perspective_camera(); if (!scene->camera) { dmnsn_delete_scene(scene); return NULL; } - dmnsn_set_perspective_camera_trans(scene->camera, trans); /* * Now parse the abstract syntax tree @@ -402,6 +534,8 @@ dmnsn_realize(const dmnsn_array *astree) dmnsn_object *object; switch (astnode.type) { case DMNSN_AST_CAMERA: + dmnsn_delete_camera(scene->camera); + scene->camera = dmnsn_realize_camera(astnode); break; case DMNSN_AST_BACKGROUND: diff --git a/libdimension/dimension/geometry.h b/libdimension/dimension/geometry.h index 68359aa..3011216 100644 --- a/libdimension/dimension/geometry.h +++ b/libdimension/dimension/geometry.h @@ -39,6 +39,12 @@ typedef struct { dmnsn_vector n; /* A normal vector; the direction of the line */ } dmnsn_line; +/* Vector constants */ +static const dmnsn_vector dmnsn_zero = { 0.0, 0.0, 0.0 }; +static const dmnsn_vector dmnsn_x = { 1.0, 0.0, 0.0 }; +static const dmnsn_vector dmnsn_y = { 0.0, 1.0, 0.0 }; +static const dmnsn_vector dmnsn_z = { 0.0, 0.0, 1.0 }; + /* Shorthand for vector/matrix construction */ DMNSN_INLINE dmnsn_vector @@ -133,6 +139,13 @@ dmnsn_vector_cross(dmnsn_vector lhs, dmnsn_vector rhs) return v; } +DMNSN_INLINE dmnsn_vector +dmnsn_vector_proj(dmnsn_vector u, dmnsn_vector d) +{ + /* 1 division, 9 multiplications, 4 additions */ + return dmnsn_vector_mul(dmnsn_vector_dot(u, d)/dmnsn_vector_dot(d, d), d); +} + DMNSN_INLINE double dmnsn_vector_norm(dmnsn_vector n) { @@ -147,6 +160,9 @@ dmnsn_vector_normalize(dmnsn_vector n) return dmnsn_vector_div(n, dmnsn_vector_norm(n)); } +double dmnsn_vector_axis_angle(dmnsn_vector v1, dmnsn_vector v2, + dmnsn_vector axis); + dmnsn_matrix dmnsn_matrix_inverse(dmnsn_matrix A); dmnsn_matrix dmnsn_matrix_mul(dmnsn_matrix lhs, dmnsn_matrix rhs); dmnsn_vector dmnsn_matrix_vector_mul(dmnsn_matrix lhs, dmnsn_vector rhs); diff --git a/libdimension/geometry.c b/libdimension/geometry.c index cbcc2c1..4aa46ec 100644 --- a/libdimension/geometry.c +++ b/libdimension/geometry.c @@ -80,6 +80,23 @@ dmnsn_rotation_matrix(dmnsn_vector theta) ); } +/* Find the angle between two vectors with respect to an axis */ +double +dmnsn_vector_axis_angle(dmnsn_vector v1, dmnsn_vector v2, dmnsn_vector axis) +{ + dmnsn_vector d = dmnsn_vector_sub(v1, v2); + dmnsn_vector proj = dmnsn_vector_add(dmnsn_vector_proj(d, axis), v2); + + double c = dmnsn_vector_dot(dmnsn_vector_normalize(v1), + dmnsn_vector_normalize(proj)); + double angle = acos(c); + + if (dmnsn_vector_dot(dmnsn_vector_cross(v1, proj), axis) > 0) + return angle; + else + return -angle; +} + /* Matrix inversion helper functions */ typedef struct { double n[2][2]; } dmnsn_matrix2; diff --git a/tests/dimension/demo.pov b/tests/dimension/demo.pov index 67113cf..b99184b 100644 --- a/tests/dimension/demo.pov +++ b/tests/dimension/demo.pov @@ -21,8 +21,8 @@ camera { perspective - location <0, 0, -4> - right <768/480, 0, 0> + location <0, 0.25, -4> + right <1.6, 0, 0> rotate <0, 53, 0> look_at <0, 0, 0> } diff --git a/tests/dimension/demo.sh b/tests/dimension/demo.sh index 31bb3ce..d129730 100755 --- a/tests/dimension/demo.sh +++ b/tests/dimension/demo.sh @@ -23,7 +23,7 @@ demo=$(${top_builddir}/dimension/dimension --parse ${srcdir}/demo.pov) demo_exp=$(echo -n \ '((camera perspective - (location (vector (integer 0) (integer 0) (integer -4) + (location (vector (integer 0) (float 0.25) (integer -4) (integer 0) (integer 0))) (right (vector (float 1.6) (integer 0) (integer 0) (integer 0) (integer 0))) (rotate (vector (integer 0) (integer 53) (integer 0) -- cgit v1.2.3