summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--dimension/realize.c218
-rw-r--r--libdimension/dimension/geometry.h16
-rw-r--r--libdimension/geometry.c17
-rw-r--r--tests/dimension/demo.pov4
-rwxr-xr-xtests/dimension/demo.sh2
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)