stage: simplify apply_transform

This simplifies the implementation of the ClutterStage apply_transform
vfunc by using the new cogl_matrix_view_2d_in_perspective utility API
which can setup up a view transform for a given perspective so that a
cross section of the view frustum at an arbitrary depth can be mapped
directly to 2D stage coordinates with (0,0) at the top left.
This commit is contained in:
Robert Bragg 2011-02-08 14:46:31 +00:00
parent ca30ac7835
commit eef9078f89

View File

@ -76,6 +76,8 @@
#include "cogl/cogl.h" #include "cogl/cogl.h"
#include <math.h>
G_DEFINE_TYPE (ClutterStage, clutter_stage, CLUTTER_TYPE_GROUP); G_DEFINE_TYPE (ClutterStage, clutter_stage, CLUTTER_TYPE_GROUP);
#define CLUTTER_STAGE_GET_PRIVATE(obj) \ #define CLUTTER_STAGE_GET_PRIVATE(obj) \
@ -115,6 +117,8 @@ struct _ClutterStagePrivate
ClutterColor color; ClutterColor color;
ClutterPerspective perspective; ClutterPerspective perspective;
CoglMatrix projection; CoglMatrix projection;
CoglMatrix inverse_projection;
CoglMatrix view;
float viewport[4]; float viewport[4];
ClutterFog fog; ClutterFog fog;
@ -1019,65 +1023,11 @@ clutter_stage_real_apply_transform (ClutterActor *stage,
CoglMatrix *matrix) CoglMatrix *matrix)
{ {
ClutterStagePrivate *priv = CLUTTER_STAGE (stage)->priv; ClutterStagePrivate *priv = CLUTTER_STAGE (stage)->priv;
CoglMatrix perspective;
gfloat z_camera;
gfloat width, height;
/*
* In theory, we can compute the camera distance from screen as:
*
* 0.5 * tan (FOV)
*
* However, it's better to compute the z_camera from our projection
* matrix so that we get a 1:1 mapping at the screen distance. Consider
* the upper-left corner of the screen. It has object coordinates
* (0,0,0), so by the transform below, ends up with eye coordinate
*
* x_eye = x_object / width - 0.5 = - 0.5
* y_eye = (height - y_object) / width - 0.5 = 0.5
* z_eye = z_object / width - z_camera = - z_camera
*
* From cogl_perspective(), we know that the projection matrix has
* the form:
*
* (x, 0, 0, 0)
* (0, y, 0, 0)
* (0, 0, c, d)
* (0, 0, -1, 0)
*
* Applied to the above, we get clip coordinates of
*
* x_clip = x * (- 0.5)
* y_clip = y * 0.5
* w_clip = - 1 * (- z_camera) = z_camera
*
* Dividing through by w to get normalized device coordinates, we
* have, x_nd = x * 0.5 / z_camera, y_nd = - y * 0.5 / z_camera.
* The upper left corner of the screen has normalized device coordinates,
* (-1, 1), so to have the correct 1:1 mapping, we have to have:
*
* z_camera = 0.5 * x = 0.5 * y
*
* If x != y, then we have a non-uniform aspect ration, and a 1:1 mapping
* doesn't make sense.
*/
cogl_matrix_init_identity (&perspective);
cogl_matrix_perspective (&perspective,
priv->perspective.fovy,
priv->perspective.aspect,
priv->perspective.z_near,
priv->perspective.z_far);
z_camera = 0.5f * perspective.xx;
clutter_actor_get_size (stage, &width, &height);
/* FIXME: we probably shouldn't be explicitly reseting the matrix
* here... */
cogl_matrix_init_identity (matrix); cogl_matrix_init_identity (matrix);
cogl_matrix_translate (matrix, -0.5f, -0.5f, -z_camera); cogl_matrix_multiply (matrix, matrix, &priv->view);
cogl_matrix_scale (matrix,
1.0f / width, -1.0f / height, 1.0f / width);
cogl_matrix_translate (matrix, 0.0f, -1.0f * height, 0.0f);
} }
static void static void
@ -1641,8 +1591,10 @@ clutter_stage_init (ClutterStage *self)
priv->color = default_stage_color; priv->color = default_stage_color;
_clutter_stage_window_get_geometry (priv->impl, &geom);
priv->perspective.fovy = 60.0; /* 60 Degrees */ priv->perspective.fovy = 60.0; /* 60 Degrees */
priv->perspective.aspect = 1.0; priv->perspective.aspect = (float)geom.width / (float)geom.height;
priv->perspective.z_near = 0.1; priv->perspective.z_near = 0.1;
priv->perspective.z_far = 100.0; priv->perspective.z_far = 100.0;
@ -1652,6 +1604,17 @@ clutter_stage_init (ClutterStage *self)
priv->perspective.aspect, priv->perspective.aspect,
priv->perspective.z_near, priv->perspective.z_near,
priv->perspective.z_far); priv->perspective.z_far);
cogl_matrix_get_inverse (&priv->projection,
&priv->inverse_projection);
cogl_matrix_init_identity (&priv->view);
cogl_matrix_view_2d_in_perspective (&priv->view,
priv->perspective.fovy,
priv->perspective.aspect,
priv->perspective.z_near,
50, /* distance to 2d plane */
geom.width,
geom.height);
/* depth cueing */ /* depth cueing */
priv->fog.z_near = 1.0; priv->fog.z_near = 1.0;
@ -1668,7 +1631,6 @@ clutter_stage_init (ClutterStage *self)
g_signal_connect (self, "notify::min-height", g_signal_connect (self, "notify::min-height",
G_CALLBACK (clutter_stage_notify_min_size), NULL); G_CALLBACK (clutter_stage_notify_min_size), NULL);
_clutter_stage_window_get_geometry (priv->impl, &geom);
_clutter_stage_set_viewport (self, 0, 0, geom.width, geom.height); _clutter_stage_set_viewport (self, 0, 0, geom.width, geom.height);
_clutter_stage_set_pick_buffer_valid (self, FALSE, CLUTTER_PICK_ALL); _clutter_stage_set_pick_buffer_valid (self, FALSE, CLUTTER_PICK_ALL);
@ -1795,6 +1757,8 @@ clutter_stage_set_perspective (ClutterStage *stage,
priv->perspective.aspect, priv->perspective.aspect,
priv->perspective.z_near, priv->perspective.z_near,
priv->perspective.z_far); priv->perspective.z_far);
cogl_matrix_get_inverse (&priv->projection,
&priv->inverse_projection);
priv->dirty_projection = TRUE; priv->dirty_projection = TRUE;
clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
@ -2746,6 +2710,8 @@ _clutter_stage_maybe_setup_viewport (ClutterStage *stage)
if (priv->dirty_viewport) if (priv->dirty_viewport)
{ {
ClutterPerspective perspective;
CLUTTER_NOTE (PAINT, CLUTTER_NOTE (PAINT,
"Setting up the viewport { w:%f, h:%f }", "Setting up the viewport { w:%f, h:%f }",
priv->viewport[2], priv->viewport[3]); priv->viewport[2], priv->viewport[3]);
@ -2755,6 +2721,18 @@ _clutter_stage_maybe_setup_viewport (ClutterStage *stage)
priv->viewport[2], priv->viewport[2],
priv->viewport[3]); priv->viewport[3]);
perspective = priv->perspective;
perspective.aspect = priv->viewport[2] / priv->viewport[3];
clutter_stage_set_perspective (stage, &perspective);
cogl_matrix_init_identity (&priv->view);
cogl_matrix_view_2d_in_perspective (&priv->view,
perspective.fovy,
perspective.aspect,
perspective.z_near,
50, /* depth of 2d plane */
priv->viewport[2],
priv->viewport[3]);
priv->dirty_viewport = FALSE; priv->dirty_viewport = FALSE;
} }