diff --git a/clutter/clutter-actor.c b/clutter/clutter-actor.c index 45fe60f66..a114c8e2e 100644 --- a/clutter/clutter-actor.c +++ b/clutter/clutter-actor.c @@ -588,8 +588,6 @@ static void clutter_scriptable_iface_init (ClutterScriptableIface *iface); static void clutter_animatable_iface_init (ClutterAnimatableIface *iface); static void atk_implementor_iface_init (AtkImplementorIface *iface); -static void _clutter_actor_apply_modelview_transform (ClutterActor *self); - static void clutter_actor_shader_pre_paint (ClutterActor *actor, gboolean repeat); static void clutter_actor_shader_post_paint (ClutterActor *actor); @@ -637,6 +635,10 @@ static void clutter_anchor_coord_set_gravity (AnchorCoord *coord, static gboolean clutter_anchor_coord_is_zero (const AnchorCoord *coord); +static void _clutter_actor_get_relative_modelview (ClutterActor *self, + ClutterActor *ancestor, + CoglMatrix *matrix); + /* Helper macro which translates by the anchor coord, applies the given transformation and then translates back */ #define TRANSFORM_ABOUT_ANCHOR_COORD(a,m,c,_transform) G_STMT_START { \ @@ -1863,174 +1865,6 @@ clutter_actor_real_queue_relayout (ClutterActor *self) clutter_actor_queue_relayout (priv->parent_actor); } -/* like ClutterVertex, but with a w component */ -typedef struct { - gfloat x; - gfloat y; - gfloat z; - gfloat w; -} full_vertex_t; - -/* copies a fixed vertex into a ClutterVertex */ -static inline void -full_vertex_to_units (const full_vertex_t *f, - ClutterVertex *u) -{ - u->x = f->x; - u->y = f->y; - u->z = f->z; -} - -/* transforms a 4-tuple of coordinates using @matrix and - * places the result into a @vertex - */ -static inline void -full_vertex_transform (const CoglMatrix *matrix, - gfloat x, - gfloat y, - gfloat z, - gfloat w, - full_vertex_t *vertex) -{ - cogl_matrix_transform_point (matrix, &x, &y, &z, &w); - - vertex->x = x; - vertex->y = y; - vertex->z = z; - vertex->w = w; -} - -/* Help macros to scale from OpenGL <-1,1> coordinates system to our - * X-window based <0,window-size> coordinates - */ -#define MTX_GL_SCALE_X(x,w,v1,v2) ((((((x) / (w)) + 1.0f) / 2.0f) * (v1)) + (v2)) -#define MTX_GL_SCALE_Y(y,w,v1,v2) ((v1) - (((((y) / (w)) + 1.0f) / 2.0f) * (v1)) + (v2)) -#define MTX_GL_SCALE_Z(z,w,v1,v2) (MTX_GL_SCALE_X ((z), (w), (v1), (v2))) - -/* scales a fixed @vertex using @matrix and @viewport, and - * transforms the result into a ClutterVertex, filling @vertex_p - */ -static inline void -full_vertex_scale (const CoglMatrix *matrix, - const full_vertex_t *vertex, - const gfloat viewport[], - ClutterVertex *vertex_p) -{ - gfloat v_x, v_y, v_width, v_height; - full_vertex_t tmp; - - tmp = *vertex; - - cogl_matrix_transform_point (matrix, &tmp.x, &tmp.y, &tmp.z, &tmp.w); - - v_x = viewport[0]; - v_y = viewport[1]; - v_width = viewport[2]; - v_height = viewport[3]; - - tmp.x = MTX_GL_SCALE_X (tmp.x, tmp.w, v_width, v_x); - tmp.y = MTX_GL_SCALE_Y (tmp.y, tmp.w, v_height, v_y); - tmp.z = MTX_GL_SCALE_Z (tmp.z, tmp.w, v_width, v_x); - tmp.w = 0; - - full_vertex_to_units (&tmp, vertex_p); -} - -/* Applies the transforms associated with this actor and its ancestors, - * retrieves the resulting OpenGL modelview matrix, and uses the matrix - * to transform the supplied point - * - * The point coordinates are in-out parameters - */ -static void -clutter_actor_transform_point_relative (ClutterActor *actor, - ClutterActor *ancestor, - gfloat *x, - gfloat *y, - gfloat *z, - gfloat *w) -{ - full_vertex_t vertex; - CoglMatrix matrix; - - vertex.x = (x != NULL) ? *x : 0; - vertex.y = (y != NULL) ? *y : 0; - vertex.z = (z != NULL) ? *z : 0; - vertex.w = (w != NULL) ? *w : 0; - - cogl_push_matrix(); - - _clutter_actor_apply_modelview_transform_recursive (actor, ancestor); - - cogl_get_modelview_matrix (&matrix); - cogl_matrix_transform_point (&matrix, - &vertex.x, - &vertex.y, - &vertex.z, - &vertex.w); - - - cogl_pop_matrix(); - - if (x) - *x = vertex.x; - - if (y) - *y = vertex.y; - - if (z) - *z = vertex.z; - - if (w) - *w = vertex.w; -} - -/* Applies the transforms associated with this actor and its ancestors, - * retrieves the resulting OpenGL modelview matrix, and uses the matrix - * to transform the supplied point - */ -static void -clutter_actor_transform_point (ClutterActor *actor, - gfloat *x, - gfloat *y, - gfloat *z, - gfloat *w) -{ - full_vertex_t vertex; - CoglMatrix matrix; - - vertex.x = (x != NULL) ? *x : 0; - vertex.y = (y != NULL) ? *y : 0; - vertex.z = (z != NULL) ? *z : 0; - vertex.w = (w != NULL) ? *w : 0; - - cogl_push_matrix(); - - _clutter_actor_apply_modelview_transform_recursive (actor, NULL); - - cogl_get_modelview_matrix (&matrix); - cogl_matrix_transform_point (&matrix, - &vertex.x, - &vertex.y, - &vertex.z, - &vertex.w); - - - cogl_pop_matrix(); - - if (x) - *x = vertex.x; - - if (y) - *y = vertex.y; - - if (z) - *z = vertex.z; - - if (w) - *w = vertex.w; -} - /** * clutter_actor_apply_relative_transform_to_point: * @self: A #ClutterActor @@ -2056,34 +1890,119 @@ clutter_actor_apply_relative_transform_to_point (ClutterActor *self, const ClutterVertex *point, ClutterVertex *vertex) { - gfloat x, y, z, w; - full_vertex_t tmp; - gfloat v[4]; + gfloat w; + CoglMatrix matrix; g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_return_if_fail (ancestor == NULL || CLUTTER_IS_ACTOR (ancestor)); g_return_if_fail (point != NULL); g_return_if_fail (vertex != NULL); - x = point->x; - y = point->y; - z = point->z; + *vertex = *point; w = 1.0; - /* First we tranform the point using the OpenGL modelview matrix */ - clutter_actor_transform_point_relative (self, ancestor, &x, &y, &z, &w); + if (ancestor == NULL) + ancestor = _clutter_actor_get_stage_internal (self); - cogl_get_viewport (v); + if (ancestor == NULL) + { + *vertex = *point; + return; + } - /* The w[3] parameter should always be 1.0 here, so we ignore it; otherwise - * we would have to divide the original verts with it. - */ - tmp.x = (x + 0.5) * v[2]; - tmp.y = (0.5 - y) * v[3]; - tmp.z = (z + 0.5) * v[2]; - tmp.w = 0; + _clutter_actor_get_relative_modelview (self, ancestor, &matrix); + cogl_matrix_transform_point (&matrix, &vertex->x, &vertex->y, &vertex->z, &w); +} - full_vertex_to_units (&tmp, vertex); +/* Help macros to scale from OpenGL <-1,1> coordinates system to + * window coordinates ranging [0,window-size] + */ +#define MTX_GL_SCALE_X(x,w,v1,v2) ((((((x) / (w)) + 1.0f) / 2.0f) * (v1)) + (v2)) +#define MTX_GL_SCALE_Y(y,w,v1,v2) ((v1) - (((((y) / (w)) + 1.0f) / 2.0f) * (v1)) + (v2)) +#define MTX_GL_SCALE_Z(z,w,v1,v2) (MTX_GL_SCALE_X ((z), (w), (v1), (v2))) + +static void +_fully_transform_vertices (const CoglMatrix *modelview, + const CoglMatrix *projection, + const int *viewport, + const ClutterVertex *vertices_in, + ClutterVertex *vertices_out, + int n_vertices) +{ + CoglMatrix modelview_projection; + int i; + + /* XXX: we should find a way to cache this per actor */ + cogl_matrix_multiply (&modelview_projection, + projection, + modelview); + + for (i = 0; i < n_vertices; i++) + { + const ClutterVertex *vertex_in = &vertices_in[i]; + ClutterVertex *vertex_out = &vertices_out[i]; + gfloat x, y, z, w; + + x = vertex_in->x; + y = vertex_in->y; + z = vertex_in->z; + w = 1.0; + + /* Transform the point using the modelview matrix */ + cogl_matrix_transform_point (&modelview_projection, &x, &y, &z, &w); + + /* Finally translate from OpenGL coords to window coords */ + vertex_out->x = MTX_GL_SCALE_X (x, w, viewport[2], viewport[0]); + vertex_out->y = MTX_GL_SCALE_Y (y, w, viewport[3], viewport[1]); + vertex_out->z = MTX_GL_SCALE_Z (z, w, viewport[2], viewport[0]); + } +} + +static gboolean +_clutter_actor_fully_transform_vertices (ClutterActor *self, + const ClutterVertex *vertices_in, + ClutterVertex *vertices_out, + int n_vertices) +{ + ClutterActor *stage; + CoglMatrix modelview; + CoglMatrix projection; + int viewport[4]; + + g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE); + + /* NB: _clutter_actor_apply_modelview_transform_recursive will never + * include the transformation between stage coordinates and OpenGL + * window coordinates, we have to explicitly use the + * stage->apply_transform to get that... */ + stage = _clutter_actor_get_stage_internal (self); + + /* We really can't do anything meaningful in this case so don't try + * to do any transform */ + if (stage == NULL) + return FALSE; + + /* Setup the modelview */ + cogl_matrix_init_identity (&modelview); + _clutter_actor_apply_modelview_transform (stage, &modelview); + _clutter_actor_apply_modelview_transform_recursive (self, stage, &modelview); + + /* Fetch the projection and viewport */ + _clutter_stage_get_projection_matrix (CLUTTER_STAGE (stage), &projection); + _clutter_stage_get_viewport (CLUTTER_STAGE (stage), + &viewport[0], + &viewport[1], + &viewport[2], + &viewport[3]); + + _fully_transform_vertices (&modelview, + &projection, + viewport, + vertices_in, + vertices_out, + n_vertices); + + return TRUE; } /** @@ -2103,233 +2022,75 @@ clutter_actor_apply_transform_to_point (ClutterActor *self, const ClutterVertex *point, ClutterVertex *vertex) { - full_vertex_t tmp = { 0, }; - gfloat x, y, z, w; - CoglMatrix matrix_p; - gfloat v[4]; - - g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_return_if_fail (point != NULL); g_return_if_fail (vertex != NULL); - - x = point->x; - y = point->y; - z = point->z; - w = 1.0; - - /* First we tranform the point using the OpenGL modelview matrix */ - clutter_actor_transform_point (self, &x, &y, &z, &w); - - tmp.x = x; - tmp.y = y; - tmp.z = z; - tmp.w = w; - - cogl_get_projection_matrix (&matrix_p); - cogl_get_viewport (v); - - /* Now, transform it again with the projection matrix */ - cogl_matrix_transform_point (&matrix_p, - &tmp.x, - &tmp.y, - &tmp.z, - &tmp.w); - - - /* Finaly translate from OpenGL coords to window coords */ - vertex->x = MTX_GL_SCALE_X (tmp.x, tmp.w, v[2], v[0]); - vertex->y = MTX_GL_SCALE_Y (tmp.y, tmp.w, v[3], v[1]); - vertex->z = MTX_GL_SCALE_Z (tmp.z, tmp.w, v[2], v[0]); -} - -/* Recursively tranform supplied vertices with the tranform for the current - * actor and up to the ancestor (like clutter_actor_transform_point() but - * for all the vertices in one go). - */ -static void -clutter_actor_transform_vertices_relative (ClutterActor *self, - ClutterActor *ancestor, - full_vertex_t vertices[]) -{ - ClutterActorPrivate *priv = self->priv; - gfloat width, height; - CoglMatrix mtx; - - width = priv->allocation.x2 - priv->allocation.x1; - height = priv->allocation.y2 - priv->allocation.y1; - - cogl_push_matrix(); - - _clutter_actor_apply_modelview_transform_recursive (self, ancestor); - - cogl_get_modelview_matrix (&mtx); - - full_vertex_transform (&mtx, 0, 0, 0, 1.0, &vertices[0]); - full_vertex_transform (&mtx, width, 0, 0, 1.0, &vertices[1]); - full_vertex_transform (&mtx, 0, height, 0, 1.0, &vertices[2]); - full_vertex_transform (&mtx, width, height, 0, 1.0, &vertices[3]); - - cogl_pop_matrix(); -} - -/* _clutter_actor_ensure_stage_current - * - * Ensures that the actors corresponding stage is made current so we - * have a valid viewport, projection matrix and modelview matrix stack. - */ -static void -_clutter_actor_ensure_stage_current (ClutterActor *self) -{ - ClutterActor *stage; - - /* We essentially have to dupe some code from clutter_redraw() here - * to make sure GL Matrices etc are initialised if we're called and we - * haven't yet rendered anything. - * - * Simply duping code for now in wait for Cogl cleanup that can hopefully - * address this in a nicer way. - */ - stage = _clutter_actor_get_stage_internal (self); - - /* FIXME: if were not yet added to a stage, its probably unsafe to - * return default - ideally the func should fail - */ - if (stage == NULL) - stage = clutter_stage_get_default (); - - clutter_stage_ensure_current (CLUTTER_STAGE (stage)); - _clutter_stage_maybe_setup_viewport (CLUTTER_STAGE (stage)); + _clutter_actor_fully_transform_vertices (self, point, vertex, 1); } /* _clutter_actor_get_relative_modelview: * - * Retrives the modelview transformation relative to some ancestor actor, or - * the stage if NULL is given for the ancestor. + * Retrieves the modelview transformation relative to some ancestor + * actor, or the stage if NULL is given for the ancestor. * - * It assumes you currently have an empty matrix stack. + * Note: This will never include the transformations from + * stage::apply_transform since that would give you a modelview + * transform relative to the OpenGL window coordinate space that the + * stage lies within. + * + * If you need to do a full modelview + projective transform and get + * to window coordinates then you should explicitly apply the stage + * transform to an identity matrix and use + * _clutter_actor_apply_modelview_transform like: + * + * cogl_matrix_init_identity (&mtx); + * stage = _clutter_actor_get_stage_internal (self); + * _clutter_actor_apply_modelview_transform (stage, &mtx); */ /* FIXME: We should be caching the stage relative modelview along with the * actor itself */ -/* TODO: Replace all other occurrences of this code pattern in clutter-actor.c: - * cogl_push_matrix(); - * _clutter_actor_apply_modelview_transform_recursive (self, ancestor) - * cogl_get_modelview_matrix() - * cogl_pop_matrix(); - * with a call to this function: - */ -void +static void _clutter_actor_get_relative_modelview (ClutterActor *self, ClutterActor *ancestor, CoglMatrix *matrix) { - ClutterActor *stage; - gfloat width, height; - CoglMatrix tmp_matrix; - gfloat z_camera; - ClutterPerspective perspective; + g_return_if_fail (ancestor != NULL); - _clutter_actor_ensure_stage_current (self); + cogl_matrix_init_identity (matrix); - cogl_push_matrix (); - - if (ancestor == NULL) - { - stage = _clutter_actor_get_stage_internal (self); - - clutter_stage_get_perspective (CLUTTER_STAGE (stage), &perspective); - cogl_perspective (perspective.fovy, - perspective.aspect, - perspective.z_near, - perspective.z_far); - - cogl_get_projection_matrix (&tmp_matrix); - z_camera = 0.5f * tmp_matrix.xx; - - clutter_actor_get_size (stage, &width, &height); - - /* obliterate the current modelview matrix and reset it to be - * the same as the stage's at the beginning of a paint run; this - * is done to paint the target material in screen coordinates at - * the same place as the actor would have been - */ - cogl_matrix_init_identity (&tmp_matrix); - cogl_matrix_translate (&tmp_matrix, -0.5f, -0.5f, -z_camera); - cogl_matrix_scale (&tmp_matrix, 1.0f / width, -1.0f / height, 1.0f / width); - cogl_matrix_translate (&tmp_matrix, 0.0f, -1.0f * height, 0.0f); - cogl_set_modelview_matrix (&tmp_matrix); - } - else - { - static CoglMatrix identity; - static gboolean initialized_identity = FALSE; - - if (!initialized_identity) - { - cogl_matrix_init_identity (&identity); - initialized_identity = TRUE; - } - - cogl_set_modelview_matrix (&identity); - } - - _clutter_actor_apply_modelview_transform_recursive (self, ancestor); - - cogl_get_modelview_matrix (matrix); - - cogl_pop_matrix (); + _clutter_actor_apply_modelview_transform_recursive (self, ancestor, matrix); } -/* _clutter_actor_get_projection_and_viewport - * - * Retrieves the projection matrix and viewport for the actors corresponding - * stage. - */ -void -_clutter_actor_get_projection_and_viewport (ClutterActor *self, - CoglMatrix *matrix, - float *viewport) -{ - _clutter_actor_ensure_stage_current (self); - - cogl_get_projection_matrix (matrix); - cogl_get_viewport (viewport); -} - -/* Recursively transform supplied box with the transform for the current - * actor and all its ancestors (like clutter_actor_transform_point() - * but for all the vertices in one go) and project it into screen - * coordinates - */ -void +/* Project the given @box into stage window coordinates, writing the + * transformed vertices to @verts[]. */ +gboolean _clutter_actor_transform_and_project_box (ClutterActor *self, const ClutterActorBox *box, ClutterVertex verts[]) { - CoglMatrix mtx; - CoglMatrix mtx_p; - float v[4]; - full_vertex_t vertices[4]; + ClutterVertex box_vertices[4]; - _clutter_actor_get_relative_modelview (self, NULL, &mtx); + box_vertices[0].x = box->x1; + box_vertices[0].y = box->y1; + box_vertices[0].z = 0; + box_vertices[1].x = box->x2; + box_vertices[1].y = box->y1; + box_vertices[1].z = 0; + box_vertices[2].x = box->x1; + box_vertices[2].y = box->y2; + box_vertices[2].z = 0; + box_vertices[3].x = box->x2; + box_vertices[3].y = box->y2; + box_vertices[3].z = 0; - full_vertex_transform (&mtx, box->x1, box->y1, 0, 1.0, &vertices[0]); - full_vertex_transform (&mtx, box->x2, box->y1, 0, 1.0, &vertices[1]); - full_vertex_transform (&mtx, box->x1, box->y2, 0, 1.0, &vertices[2]); - full_vertex_transform (&mtx, box->x2, box->y2, 0, 1.0, &vertices[3]); - - _clutter_actor_get_projection_and_viewport (self, &mtx_p, v); - - full_vertex_scale (&mtx_p, &vertices[0], v, &verts[0]); - full_vertex_scale (&mtx_p, &vertices[1], v, &verts[1]); - full_vertex_scale (&mtx_p, &vertices[2], v, &verts[2]); - full_vertex_scale (&mtx_p, &vertices[3], v, &verts[3]); + return + _clutter_actor_fully_transform_vertices (self, box_vertices, verts, 4); } /** * clutter_actor_get_allocation_vertices: * @self: A #ClutterActor * @ancestor: (allow-none): A #ClutterActor to calculate the vertices - * against, or %NULL to use the default #ClutterStage + * against, or %NULL to use the #ClutterStage * @verts: (out) (array fixed-size=4) (element-type Clutter.Vertex): return * location for an array of 4 #ClutterVertex in which to store the result * @@ -2356,67 +2117,70 @@ clutter_actor_get_allocation_vertices (ClutterActor *self, ClutterVertex verts[]) { ClutterActorPrivate *priv; - ClutterActor *stage; - gfloat v[4]; - full_vertex_t vertices[4]; - full_vertex_t tmp = { 0, }; + ClutterActorBox box; + ClutterVertex vertices[4]; + CoglMatrix modelview; + float w; g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_return_if_fail (ancestor == NULL || CLUTTER_IS_ACTOR (ancestor)); + if (ancestor == NULL) + ancestor = _clutter_actor_get_stage_internal (self); + + /* Fallback to a NOP transform if the actor isn't parented under a + * stage. */ + if (ancestor == NULL) + ancestor = self; + priv = self->priv; - /* We essentially have to dupe some code from clutter_redraw() here - * to make sure GL Matrices etc are initialised if we're called and we - * havn't yet rendered anything. - * - * Simply duping code for now in wait for Cogl cleanup that can hopefully - * address this in a nicer way. - */ - stage = _clutter_actor_get_stage_internal (self); - - /* FIXME: if were not yet added to a stage, its probably unsafe to - * return default - idealy the func should fail - */ - if (stage == NULL) - stage = clutter_stage_get_default (); - - clutter_stage_ensure_current (CLUTTER_STAGE (stage)); - _clutter_stage_maybe_setup_viewport (CLUTTER_STAGE (stage)); - /* if the actor needs to be allocated we force a relayout, so that - * clutter_actor_transform_vertices_relative() will have valid values - * to use in the transformations - */ + * we will have valid values to use in the transformations */ if (priv->needs_allocation) - _clutter_stage_maybe_relayout (stage); + { + ClutterActor *stage = _clutter_actor_get_stage_internal (self); + if (stage) + _clutter_stage_maybe_relayout (stage); + else + { + box.x1 = box.y1 = 0; + /* The result isn't really meaningful in this case but at + * least try to do something *vaguely* reasonable... */ + clutter_actor_get_size (self, &box.x2, &box.y2); + } + } - clutter_actor_transform_vertices_relative (self, ancestor, vertices); + clutter_actor_get_allocation_box (self, &box); - cogl_get_viewport (v); + vertices[0].x = box.x1; + vertices[0].y = box.y1; + vertices[0].z = 0; + vertices[1].x = box.x2; + vertices[1].y = box.y1; + vertices[1].z = 0; + vertices[2].x = box.x1; + vertices[2].y = box.y2; + vertices[2].z = 0; + vertices[3].x = box.x2; + vertices[3].y = box.y2; + vertices[3].z = 0; - /* The w[3] parameter should always be 1.0 here, so we ignore it; - * otherwise we would have to divide the original verts with it. - */ - tmp.x = ((vertices[0].x + 0.5) * v[2]); - tmp.y = ((0.5 - vertices[0].y) * v[3]); - tmp.z = ((vertices[0].z + 0.5) * v[2]); - full_vertex_to_units (&tmp, &verts[0]); + _clutter_actor_get_relative_modelview (self, ancestor, &modelview); - tmp.x = ((vertices[1].x + 0.5) * v[2]); - tmp.y = ((0.5 - vertices[1].y) * v[3]); - tmp.z = ((vertices[1].z + 0.5) * v[2]); - full_vertex_to_units (&tmp, &verts[1]); - - tmp.x = ((vertices[2].x + 0.5) * v[2]); - tmp.y = ((0.5 - vertices[2].y) * v[3]); - tmp.z = ((vertices[2].z + 0.5) * v[2]); - full_vertex_to_units (&tmp, &verts[2]); - - tmp.x = ((vertices[3].x + 0.5) * v[2]); - tmp.y = ((0.5 - vertices[3].y) * v[3]); - tmp.z = ((vertices[3].z + 0.5) * v[2]); - full_vertex_to_units (&tmp, &verts[3]); + w = 1; + cogl_matrix_transform_point (&modelview, + &vertices[0].x, &vertices[0].y, &vertices[0].z, + &w); + cogl_matrix_transform_point (&modelview, + &vertices[1].x, &vertices[1].y, &vertices[1].z, + &w); + cogl_matrix_transform_point (&modelview, + &vertices[2].x, &vertices[2].y, &vertices[2].z, + &w); + cogl_matrix_transform_point (&modelview, + &vertices[3].x, &vertices[3].y, &vertices[3].z, + &w); } /** @@ -2455,12 +2219,9 @@ clutter_actor_get_abs_allocation_vertices (ClutterActor *self, if (priv->needs_allocation) { ClutterActor *stage = _clutter_actor_get_stage_internal (self); - - /* FIXME: if were not yet added to a stage, its probably unsafe to - * return default - idealy the func should fail - */ - if (stage == NULL) - stage = clutter_stage_get_default (); + /* There's nothing meaningful we can do now */ + if (!stage) + return; _clutter_stage_maybe_relayout (stage); } @@ -2481,15 +2242,11 @@ clutter_actor_real_apply_transform (ClutterActor *self, CoglMatrix *matrix) { ClutterActorPrivate *priv = self->priv; - gboolean is_stage = CLUTTER_IS_STAGE (self); - if (!is_stage) - { - cogl_matrix_translate (matrix, - priv->allocation.x1, - priv->allocation.y1, - 0.0); - } + cogl_matrix_translate (matrix, + priv->allocation.x1, + priv->allocation.y1, + 0.0); if (priv->z) cogl_matrix_translate (matrix, 0, 0, priv->z); @@ -2531,7 +2288,7 @@ clutter_actor_real_apply_transform (ClutterActor *self, priv->rxang, 1.0, 0, 0)); - if (!is_stage && !clutter_anchor_coord_is_zero (&priv->anchor)) + if (!clutter_anchor_coord_is_zero (&priv->anchor)) { gfloat x, y, z; @@ -2540,26 +2297,13 @@ clutter_actor_real_apply_transform (ClutterActor *self, } } -/* Applies the transforms associated with this actor to the - * OpenGL modelview matrix. - * - * This function does not push/pop matrix; it is the responsibility - * of the caller to do so as appropriate - */ -static void -_clutter_actor_apply_modelview_transform (ClutterActor *self) +/* Applies the transforms associated with this actor to the given + * matrix. */ +void +_clutter_actor_apply_modelview_transform (ClutterActor *self, + CoglMatrix *matrix) { - CoglMatrix matrix, cur, new; - - cogl_matrix_init_identity (&matrix); - - clutter_actor_get_transformation_matrix (self, &matrix); - - cogl_get_modelview_matrix (&cur); - - cogl_matrix_multiply (&new, &cur, &matrix); - - cogl_set_modelview_matrix (&new); + CLUTTER_ACTOR_GET_CLASS (self)->apply_transform (self, matrix); } static gboolean @@ -2597,42 +2341,30 @@ _clutter_actor_effects_post_paint (ClutterActor *self) } /* Recursively applies the transforms associated with this actor and - * its ancestors to the OpenGL modelview matrix. Use NULL if you want this + * its ancestors to the given matrix. Use NULL if you want this * to go all the way down to the stage. - * - * This function does not push/pop matrix; it is the responsibility - * of the caller to do so as appropriate */ void _clutter_actor_apply_modelview_transform_recursive (ClutterActor *self, - ClutterActor *ancestor) + ClutterActor *ancestor, + CoglMatrix *matrix) { - ClutterActor *parent, *stage; + ClutterActor *parent; - parent = clutter_actor_get_parent (self); - - /* - * If we reached the ancestor, quit - * NB: NULL ancestor means the stage, and this will not trigger - * (as it should not) - */ + /* Note we terminate before ever calling stage->apply_transform() + * since that would conceptually be relative to the underlying + * window OpenGL coordinates so we'd need a special @ancestor + * value to represent the fake parent of the stage. */ if (self == ancestor) return; - stage = _clutter_actor_get_stage_internal (self); - - /* FIXME: if were not yet added to a stage, its probably unsafe to - * return default - idealy the func should fail - */ - if (stage == NULL) - stage = clutter_stage_get_default (); + parent = clutter_actor_get_parent (self); if (parent != NULL) - _clutter_actor_apply_modelview_transform_recursive (parent, ancestor); - else if (self != stage) - _clutter_actor_apply_modelview_transform (stage); + _clutter_actor_apply_modelview_transform_recursive (parent, ancestor, + matrix); - _clutter_actor_apply_modelview_transform (self); + _clutter_actor_apply_modelview_transform (self, matrix); } /** @@ -2703,7 +2435,15 @@ clutter_actor_paint (ClutterActor *self) cogl_push_matrix(); if (priv->enable_model_view_transform) - _clutter_actor_apply_modelview_transform (self); + { + CoglMatrix matrix; + /* XXX: It could be better to cache the modelview with the actor + * instead of progressively building up the transformations on + * the matrix stack every time we paint. */ + cogl_get_modelview_matrix (&matrix); + _clutter_actor_apply_modelview_transform (self, &matrix); + cogl_set_modelview_matrix (&matrix); + } if (priv->has_clip) { @@ -5071,27 +4811,6 @@ _clutter_actor_queue_redraw_with_clip (ClutterActor *self, return; } - /* SYNC_MATRICES is a flag for the stage, which means that we just - * got resized and we need to re-setup the viewport. - * IN_RESIZE is used on X11 where the resize is asynchronous, so we - * don't ask for a viewport change before we have the final size. - * - * If either of these flags are set then we won't be able to - * transform the given clip rectangle into valid stage coordinates, - * so we instead queue a full stage redraw. - * - * (Note: to some extent this is redundant because these flags - * should imply a full stage redraw will be queued, but we at least - * avoid needlessly traversing the actors ancestors to derive an - * incorrect modelview matrix.) - */ - if ((CLUTTER_PRIVATE_FLAGS (self) & CLUTTER_SYNC_MATRICES) && - !CLUTTER_STAGE_IN_RESIZE (self)) - { - clutter_actor_queue_redraw (self); - return; - } - if (flags & CLUTTER_REDRAW_CLIPPED_TO_ALLOCATION) { _clutter_actor_get_allocation_clip (self, &allocation_clip); @@ -10657,7 +10376,8 @@ clutter_actor_unset_flags (ClutterActor *self, * @self: a #ClutterActor * @matrix: (out): the return location for a #CoglMatrix * - * Retrieves the transformations applied to @self + * Retrieves the transformations applied to @self relative to its + * parent. * * Since: 1.0 */ @@ -10669,7 +10389,7 @@ clutter_actor_get_transformation_matrix (ClutterActor *self, cogl_matrix_init_identity (matrix); - CLUTTER_ACTOR_GET_CLASS (self)->apply_transform (self, matrix); + _clutter_actor_apply_modelview_transform (self, matrix); } /** diff --git a/clutter/clutter-backend.c b/clutter/clutter-backend.c index 8f54fb0bb..0e5a0d957 100644 --- a/clutter/clutter-backend.c +++ b/clutter/clutter-backend.c @@ -421,6 +421,17 @@ _clutter_backend_ensure_context (ClutterBackend *backend, clutter_actor_get_size (CLUTTER_ACTOR (stage), &width, &height); _cogl_onscreen_clutter_backend_set_size (width, height); + + /* Eventually we will have a separate CoglFramebuffer for + * each stage and each one will track private projection + * matrix and viewport state, but until then we need to make + * sure we update the projection and viewport whenever we + * switch between stages. + * + * This dirty mechanism will ensure they are asserted before + * the next paint... */ + _clutter_stage_dirty_viewport (stage); + _clutter_stage_dirty_projection (stage); } /* FIXME: With a NULL stage and thus no active context it may make more @@ -429,17 +440,6 @@ _clutter_backend_ensure_context (ClutterBackend *backend, * potential issue of GL calls with no context) */ current_context_stage = new_stage; - - /* if the new stage has a different size than the previous one - * we need to update the viewport; we do it by simply setting the - * SYNC_MATRICES flag and letting the next redraw cycle take care - * of calling glViewport() - */ - if (current_context_stage) - { - CLUTTER_SET_PRIVATE_FLAGS (current_context_stage, - CLUTTER_SYNC_MATRICES); - } } else CLUTTER_NOTE (MULTISTAGE, "Stage is the same"); diff --git a/clutter/clutter-main.c b/clutter/clutter-main.c index 67cc85084..e156dad95 100644 --- a/clutter/clutter-main.c +++ b/clutter/clutter-main.c @@ -265,34 +265,6 @@ _clutter_stage_maybe_relayout (ClutterActor *stage) } } -void -_clutter_stage_maybe_setup_viewport (ClutterStage *stage) -{ - if ((CLUTTER_PRIVATE_FLAGS (stage) & CLUTTER_SYNC_MATRICES) && - !CLUTTER_STAGE_IN_RESIZE (stage)) - { - ClutterPerspective perspective; - gfloat width, height; - - clutter_actor_get_preferred_size (CLUTTER_ACTOR (stage), - NULL, NULL, - &width, &height); - clutter_stage_get_perspective (stage, &perspective); - - CLUTTER_NOTE (PAINT, - "Setting up the viewport { w:%.2f, h:%.2f }", - width, height); - - _cogl_setup_viewport (width, height, - perspective.fovy, - perspective.aspect, - perspective.z_near, - perspective.z_far); - - CLUTTER_UNSET_PRIVATE_FLAGS (stage, CLUTTER_SYNC_MATRICES); - } -} - void _clutter_do_redraw (ClutterStage *stage) { diff --git a/clutter/clutter-private.h b/clutter/clutter-private.h index cffd8e9b1..1c27aea7c 100644 --- a/clutter/clutter-private.h +++ b/clutter/clutter-private.h @@ -75,25 +75,20 @@ typedef enum { CLUTTER_IS_TOPLEVEL = 1 << 1, CLUTTER_IN_REPARENT = 1 << 2, - /* Used by the stage to indicate GL viewport / perspective etc needs - * (re)setting. - */ - CLUTTER_SYNC_MATRICES = 1 << 3, + /* Used to avoid recursion */ + CLUTTER_IN_PAINT = 1 << 3, /* Used to avoid recursion */ - CLUTTER_IN_PAINT = 1 << 4, - - /* Used to avoid recursion */ - CLUTTER_IN_RELAYOUT = 1 << 5, + CLUTTER_IN_RELAYOUT = 1 << 4, /* Used by the stage if resizing is an asynchronous operation (like on * X11) to delay queueing relayouts until we got a notification from the * event handling */ - CLUTTER_IN_RESIZE = 1 << 6, + CLUTTER_IN_RESIZE = 1 << 5, /* a flag for internal children of Containers */ - CLUTTER_INTERNAL_CHILD = 1 << 7 + CLUTTER_INTERNAL_CHILD = 1 << 6 } ClutterPrivateFlags; struct _ClutterInputDevice @@ -251,6 +246,23 @@ void _clutter_stage_set_window (ClutterStage *sta ClutterStageWindow *stage_window); ClutterStageWindow *_clutter_stage_get_window (ClutterStage *stage); ClutterStageWindow *_clutter_stage_get_default_window (void); +void _clutter_stage_get_projection_matrix (ClutterStage *stage, + CoglMatrix *projection); + +void _clutter_stage_dirty_projection (ClutterStage *stage); +void _clutter_stage_set_viewport (ClutterStage *stage, + int x, + int y, + int width, + int height); +void _clutter_stage_get_viewport (ClutterStage *stage, + int *x, + int *y, + int *width, + int *height); +void _clutter_stage_dirty_viewport (ClutterStage *stage); + + void _clutter_stage_maybe_setup_viewport (ClutterStage *stage); void _clutter_stage_maybe_relayout (ClutterActor *stage); gboolean _clutter_stage_needs_update (ClutterStage *stage); @@ -330,8 +342,12 @@ gboolean _clutter_boolean_handled_accumulator (GSignalInvocationHint *ihint, gpointer dummy); ClutterActor *_clutter_actor_get_stage_internal (ClutterActor *actor); + +void _clutter_actor_apply_modelview_transform (ClutterActor *self, + CoglMatrix *matrix); void _clutter_actor_apply_modelview_transform_recursive (ClutterActor *self, - ClutterActor *ancestor); + ClutterActor *ancestor, + CoglMatrix *matrix); void _clutter_actor_rerealize (ClutterActor *self, ClutterCallback callback, @@ -349,9 +365,9 @@ void _clutter_actor_set_enable_paint_unmapped (ClutterActor *self, void _clutter_actor_set_has_pointer (ClutterActor *self, gboolean has_pointer); -void _clutter_actor_transform_and_project_box (ClutterActor *self, - const ClutterActorBox *box, - ClutterVertex verts[]); +gboolean _clutter_actor_transform_and_project_box (ClutterActor *self, + const ClutterActorBox *box, + ClutterVertex verts[]); void _clutter_actor_queue_redraw_with_clip (ClutterActor *self, ClutterRedrawFlags flags, diff --git a/clutter/clutter-stage.c b/clutter/clutter-stage.c index c7487f119..7b7c8c867 100644 --- a/clutter/clutter-stage.c +++ b/clutter/clutter-stage.c @@ -105,6 +105,8 @@ struct _ClutterStagePrivate ClutterColor color; ClutterPerspective perspective; + CoglMatrix projection; + int viewport[4]; ClutterFog fog; gchar *title; @@ -122,6 +124,8 @@ struct _ClutterStagePrivate guint throttle_motion_events : 1; guint use_alpha : 1; guint min_size_changed : 1; + guint dirty_viewport : 1; + guint dirty_projection : 1; }; enum @@ -291,6 +295,15 @@ clutter_stage_allocate (ClutterActor *self, klass = CLUTTER_ACTOR_CLASS (clutter_stage_parent_class); klass->allocate (self, &override, flags); } + + clutter_actor_get_allocation_geometry (self, &geom); + _clutter_stage_set_viewport (CLUTTER_STAGE (self), + 0, 0, geom.width, geom.height); + + /* Note: we don't assume that set_viewport will queue a full redraw + * since it may bail-out early if something preemptively set the + * viewport before the stage was really allocated its new size. */ + clutter_actor_queue_redraw (self); } static void @@ -376,7 +389,8 @@ clutter_stage_realize (ClutterActor *self) * first paint (which will likely occur before the ConfigureNotify * is received) */ - CLUTTER_SET_PRIVATE_FLAGS (self, CLUTTER_SYNC_MATRICES); + priv->dirty_viewport = TRUE; + priv->dirty_projection = TRUE; g_assert (priv->impl != NULL); is_realized = _clutter_stage_window_realize (priv->impl); @@ -794,6 +808,72 @@ clutter_stage_real_delete_event (ClutterStage *stage, return TRUE; } +static void +clutter_stage_real_apply_transform (ClutterActor *stage, + CoglMatrix *matrix) +{ + 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); + + cogl_matrix_init_identity (matrix); + cogl_matrix_translate (matrix, -0.5f, -0.5f, -z_camera); + 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 clutter_stage_set_property (GObject *object, guint prop_id, @@ -1003,6 +1083,7 @@ clutter_stage_class_init (ClutterStageClass *klass) actor_class->show = clutter_stage_show; actor_class->hide = clutter_stage_hide; actor_class->queue_redraw = clutter_stage_real_queue_redraw; + actor_class->apply_transform = clutter_stage_real_apply_transform; /** * ClutterStage:fullscreen: @@ -1313,6 +1394,7 @@ clutter_stage_init (ClutterStage *self) { ClutterStagePrivate *priv; ClutterBackend *backend; + ClutterGeometry geom; /* a stage is a top-level object */ CLUTTER_SET_PRIVATE_FLAGS (self, CLUTTER_IS_TOPLEVEL); @@ -1348,6 +1430,13 @@ clutter_stage_init (ClutterStage *self) priv->perspective.z_near = 0.1; priv->perspective.z_far = 100.0; + cogl_matrix_init_identity (&priv->projection); + cogl_matrix_perspective (&priv->projection, + priv->perspective.fovy, + priv->perspective.aspect, + priv->perspective.z_near, + priv->perspective.z_far); + /* depth cueing */ priv->fog.z_near = 1.0; priv->fog.z_far = 2.0; @@ -1360,6 +1449,9 @@ clutter_stage_init (ClutterStage *self) G_CALLBACK (clutter_stage_notify_min_size), NULL); g_signal_connect (self, "notify::min-height", 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); } /** @@ -1463,12 +1555,23 @@ clutter_stage_set_perspective (ClutterStage *stage, priv = stage->priv; + if (priv->perspective.fovy == perspective->fovy && + priv->perspective.aspect == perspective->aspect && + priv->perspective.z_near == perspective->z_near && + priv->perspective.z_far == perspective->z_far) + return; + priv->perspective = *perspective; - /* this will cause the viewport to be reset; see - * clutter_maybe_setup_viewport() inside clutter-main.c - */ - CLUTTER_SET_PRIVATE_FLAGS (stage, CLUTTER_SYNC_MATRICES); + cogl_matrix_init_identity (&priv->projection); + cogl_matrix_perspective (&priv->projection, + priv->perspective.fovy, + priv->perspective.aspect, + priv->perspective.z_near, + priv->perspective.z_far); + + priv->dirty_projection = TRUE; + clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); } /** @@ -1489,6 +1592,148 @@ clutter_stage_get_perspective (ClutterStage *stage, *perspective = stage->priv->perspective; } +/* + * clutter_stage_get_projection_matrix: + * @stage: A #ClutterStage + * @projection: return location for a #CoglMatrix representing the + * perspective projection applied to actors on the given + * @stage. + * + * Retrieves the @stage's projection matrix. This is derived from the + * current perspective set using clutter_stage_set_perspective(). + * + * Since: 1.6 + */ +void +_clutter_stage_get_projection_matrix (ClutterStage *stage, + CoglMatrix *projection) +{ + g_return_if_fail (CLUTTER_IS_STAGE (stage)); + g_return_if_fail (projection != NULL); + + *projection = stage->priv->projection; +} + +/* This simply provides a simple mechanism for us to ensure that + * the projection matrix gets re-asserted before painting. + * + * This is used when switching between multiple stages */ +void +_clutter_stage_dirty_projection (ClutterStage *stage) +{ + stage->priv->dirty_projection = TRUE; +} + +/* + * clutter_stage_set_viewport: + * @stage: A #ClutterStage + * @x: The X postition to render the stage at, in window coordinates + * @y: The Y position to render the stage at, in window coordinates + * @width: The width to render the stage at, in window coordinates + * @height: The height to render the stage at, in window coordinates + * + * Sets the stage viewport. The viewport defines a final scale and + * translation of your rendered stage and actors. This lets you render + * your stage into a subregion of the stage window or you could use it to + * pan a subregion of the stage if your stage window is smaller then + * the stage. (XXX: currently this isn't possible) + * + * Unlike a scale and translation done using the modelview matrix this + * is done after everything has had perspective projection applied, so + * for example if you were to pan across a subregion of the stage using + * the viewport then you would not see a change in perspective for the + * actors on the stage. + * + * Normally the stage viewport will automatically track the size of the + * stage window with no offset so the stage will fill your window. This + * behaviour can be changed with the "viewport-mimics-window" property + * which will automatically be set to FALSE if you use this API. If + * you want to revert to the original behaviour then you should set + * this property back to %TRUE using + * clutter_stage_set_viewport_mimics_window(). + * (XXX: If we were to make this API public then we might want to do + * add that property.) + * + * Since: 1.6 + */ +void +_clutter_stage_set_viewport (ClutterStage *stage, + int x, + int y, + int width, + int height) +{ + ClutterStagePrivate *priv; + + g_return_if_fail (CLUTTER_IS_STAGE (stage)); + + priv = stage->priv; + + + if (x == priv->viewport[0] && + y == priv->viewport[1] && + width == priv->viewport[2] && + height == priv->viewport[3]) + return; + + priv->viewport[0] = x; + priv->viewport[1] = y; + priv->viewport[2] = width; + priv->viewport[3] = height; + + priv->dirty_viewport = TRUE; + + clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); +} + +/* This simply provides a simple mechanism for us to ensure that + * the viewport gets re-asserted before next painting. + * + * This is used when switching between multiple stages */ +void +_clutter_stage_dirty_viewport (ClutterStage *stage) +{ + stage->priv->dirty_viewport = TRUE; +} + +/* + * clutter_stage_get_viewport: + * @stage: A #ClutterStage + * @x: A location for the X position where the stage is rendered, + * in window coordinates. + * @y: A location for the Y position where the stage is rendered, + * in window coordinates. + * @width: A location for the width the stage is rendered at, + * in window coordinates. + * @height: A location for the height the stage is rendered at, + * in window coordinates. + * + * Returns the viewport offset and size set using + * clutter_stage_set_viewport() or if the "viewport-mimics-window" property + * is TRUE then @x and @y will be set to 0 and @width and @height will equal + * the width if the stage window. + * + * Since: 1.6 + */ +void +_clutter_stage_get_viewport (ClutterStage *stage, + int *x, + int *y, + int *width, + int *height) +{ + ClutterStagePrivate *priv; + + g_return_if_fail (CLUTTER_IS_STAGE (stage)); + + priv = stage->priv; + + *x = priv->viewport[0]; + *y = priv->viewport[1]; + *width = priv->viewport[2]; + *height = priv->viewport[3]; +} + /** * clutter_stage_set_fullscreen: * @stage: a #ClutterStage @@ -1534,6 +1779,15 @@ clutter_stage_set_fullscreen (ClutterStage *stage, if (iface->set_fullscreen) iface->set_fullscreen (impl, fullscreen); } + + /* If the backend did fullscreen the stage window then we need to resize + * the stage and update its viewport so we queue a relayout. Note: if the + * fullscreen request is handled asynchronously we can't rely on this + * queue_relayout to update the viewport, but for example the X backend + * will recieve a ConfigureNotify after a successful resize which is how + * we ensure the viewport is updated on X. + */ + clutter_actor_queue_relayout (CLUTTER_ACTOR (stage)); } /** @@ -2279,11 +2533,33 @@ clutter_stage_ensure_viewport (ClutterStage *stage) { g_return_if_fail (CLUTTER_IS_STAGE (stage)); - CLUTTER_SET_PRIVATE_FLAGS (stage, CLUTTER_SYNC_MATRICES); + _clutter_stage_dirty_viewport (stage); clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); } +void +_clutter_stage_maybe_setup_viewport (ClutterStage *stage) +{ + ClutterStagePrivate *priv = stage->priv; + + if (priv->dirty_viewport) + { + CLUTTER_NOTE (PAINT, + "Setting up the viewport { w:%d, h:%d }", + priv->viewport[2], priv->viewport[3]); + + cogl_set_viewport (priv->viewport[0], + priv->viewport[1], + priv->viewport[2], + priv->viewport[3]); + + } + + if (priv->dirty_projection) + cogl_set_projection_matrix (&priv->projection); +} + /** * clutter_stage_ensure_redraw: * @stage: a #ClutterStage diff --git a/clutter/clutter-texture.c b/clutter/clutter-texture.c index 6c1cbb38f..0a7a271da 100644 --- a/clutter/clutter-texture.c +++ b/clutter/clutter-texture.c @@ -509,8 +509,22 @@ update_fbo (ClutterActor *self) /* Reapply the source's parent transformations */ if ((source_parent = clutter_actor_get_parent (priv->fbo_source))) - _clutter_actor_apply_modelview_transform_recursive (source_parent, - NULL); + { + CoglMatrix modelview; + + /* NB: _clutter_actor_apply_modelview_transform_recursive + * will never include the transformation between stage + * coordinates and OpenGL window coordinates, we have to + * explicitly use the stage->apply_transform to get that... + */ + + cogl_matrix_init_identity (&modelview); + _clutter_actor_apply_modelview_transform (stage, &modelview); + _clutter_actor_apply_modelview_transform_recursive (source_parent, + NULL, + &modelview); + cogl_set_modelview_matrix (&modelview); + } } diff --git a/clutter/osx/clutter-stage-osx.c b/clutter/osx/clutter-stage-osx.c index 1ef3330a6..4bb4ca7ad 100644 --- a/clutter/osx/clutter-stage-osx.c +++ b/clutter/osx/clutter-stage-osx.c @@ -170,8 +170,6 @@ clutter_stage_osx_get_wrapper (ClutterStageWindow *stage_window); stage_osx->requisition_height = [self bounds].size.height; clutter_actor_set_size (CLUTTER_ACTOR (self->stage_osx->wrapper), (int)[self bounds].size.width, (int)[self bounds].size.height); - /* make sure that the viewport is updated */ - CLUTTER_SET_PRIVATE_FLAGS (self->stage_osx->wrapper, CLUTTER_SYNC_MATRICES); } /* Simply forward all events that reach our view to clutter. */ @@ -300,8 +298,6 @@ clutter_stage_osx_realize (ClutterStageWindow *stage_window) initWithView: self->view UTF8Title: clutter_stage_get_title (CLUTTER_STAGE (self->wrapper)) stage: self]; - /* all next operations will cause draw operation and viewport should be setup now */ - _clutter_stage_maybe_setup_viewport(self->wrapper); /* looks better than positioning to 0,0 (bottom right) */ [self->window center]; @@ -422,9 +418,6 @@ clutter_stage_osx_resize (ClutterStageWindow *stage_window, [self->window setContentSize: size]; CLUTTER_OSX_POOL_RELEASE (); - - /* make sure that the viewport is updated */ - CLUTTER_SET_PRIVATE_FLAGS (self->wrapper, CLUTTER_SYNC_MATRICES); } /*************************************************************************/ diff --git a/clutter/win32/clutter-stage-win32.c b/clutter/win32/clutter-stage-win32.c index 8b21a9972..c52bbf4c5 100644 --- a/clutter/win32/clutter-stage-win32.c +++ b/clutter/win32/clutter-stage-win32.c @@ -63,7 +63,6 @@ clutter_stage_win32_show (ClutterStageWindow *stage_window, { ShowWindow (stage_win32->hwnd, do_raise ? SW_SHOW : SW_SHOWNA); - clutter_stage_ensure_viewport (CLUTTER_STAGE (stage_win32->wrapper)); clutter_actor_map (CLUTTER_ACTOR (stage_win32->wrapper)); } } @@ -190,9 +189,6 @@ clutter_stage_win32_resize (ClutterStageWindow *stage_window, SWP_NOZORDER | SWP_NOMOVE); } } - - CLUTTER_SET_PRIVATE_FLAGS (stage_win32->wrapper, - CLUTTER_SYNC_MATRICES); } } @@ -347,9 +343,6 @@ clutter_stage_win32_set_fullscreen (ClutterStageWindow *stage_window, full_width, full_height, SWP_NOZORDER | SWP_NOMOVE); } - - CLUTTER_SET_PRIVATE_FLAGS (stage_win32->wrapper, - CLUTTER_SYNC_MATRICES); } /* Report the state change */ diff --git a/clutter/x11/clutter-stage-x11.c b/clutter/x11/clutter-stage-x11.c index 476b8ae8d..45a67637d 100644 --- a/clutter/x11/clutter-stage-x11.c +++ b/clutter/x11/clutter-stage-x11.c @@ -225,7 +225,6 @@ clutter_stage_x11_resize (ClutterStageWindow *stage_window, ClutterBackend *backend = clutter_get_default_backend (); ClutterBackendX11 *backend_x11; ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage_window); - ClutterStage *stage = stage_x11->wrapper; gboolean resize; if (stage_x11->is_foreign_xwin) @@ -277,26 +276,14 @@ clutter_stage_x11_resize (ClutterStageWindow *stage_window, CLUTTER_SET_PRIVATE_FLAGS (stage_x11->wrapper, CLUTTER_IN_RESIZE); + /* XXX: in this case we can rely on a subsequent + * ConfigureNotify that will result in the stage + * being reallocated so we don't actively do anything + * to affect the stage allocation here. */ XResizeWindow (backend_x11->xdpy, stage_x11->xwin, width, height); - - /* If the viewport hasn't previously been initialized then even - * though we can't guarantee that the server will honour our request - * we need to ensure a valid viewport is set before our first paint. - */ - if (G_UNLIKELY (!stage_x11->viewport_initialized)) - { - ClutterPerspective perspective; - clutter_stage_get_perspective (stage, &perspective); - _cogl_setup_viewport (width, - height, - perspective.fovy, - perspective.aspect, - perspective.z_near, - perspective.z_far); - } } } } @@ -436,8 +423,6 @@ clutter_stage_x11_set_fullscreen (ClutterStageWindow *stage_window, CLUTTER_NOTE (BACKEND, "%ssetting fullscreen", is_fullscreen ? "" : "un"); - CLUTTER_SET_PRIVATE_FLAGS (stage, CLUTTER_SYNC_MATRICES); - if (is_fullscreen) { int width, height; @@ -516,8 +501,10 @@ clutter_stage_x11_set_fullscreen (ClutterStageWindow *stage_window, } } - clutter_stage_ensure_viewport (CLUTTER_STAGE (stage_x11->wrapper)); - clutter_actor_queue_relayout (CLUTTER_ACTOR (stage_x11->wrapper)); + /* XXX: Note we rely on the ConfigureNotify mechanism as the common + * mechanism to handle notifications of new X window sizes from the + * X server so we don't actively change the stage viewport here or + * queue a relayout etc. */ } static void @@ -706,7 +693,6 @@ clutter_stage_x11_init (ClutterStageX11 *stage) stage->is_foreign_xwin = FALSE; stage->fullscreening = FALSE; stage->is_cursor_visible = TRUE; - stage->viewport_initialized = FALSE; stage->title = NULL; @@ -949,7 +935,16 @@ clutter_x11_set_stage_foreign (ClutterStage *stage, set_foreign_window_callback, &fwd); - clutter_stage_ensure_viewport (stage); + /* Queue a relayout - so the stage will be allocated the new + * window size. + * + * Note also that when the stage gets allocated the new + * window size that will result in the stage's + * priv->viewport being changed, which will in turn result + * in the Cogl viewport changing when _clutter_do_redraw + * calls _clutter_stage_maybe_setup_viewport(). + */ + clutter_actor_queue_relayout (CLUTTER_ACTOR (stage)); return TRUE; } diff --git a/doc/clutter-actor-invariants.txt b/doc/clutter-actor-invariants.txt index cb282062c..ed66f1759 100644 --- a/doc/clutter-actor-invariants.txt +++ b/doc/clutter-actor-invariants.txt @@ -115,11 +115,6 @@ CLUTTER_ACTOR_ABOUT_TO_UNPARENT actor is removed from the stage, so unrealize implementations can use clutter_actor_get_stage(). -CLUTTER_ACTOR_SYNC_MATRICES - Set internally by ClutterStage implementations - Means: the size of the stage changed and the viewport must be - synchronized to the new size - CLUTTER_ACTOR_IN_PAINT: Set internally by clutter_actor_paint()