[actor] Allow changing the transformations matrix

Currently, the transformation matrix for an actor is constructed
from scenegraph-related accessors. An actor, though, can call COGL
API to add new transformations inside the paint() implementation,
for instance:

  static void
  my_foo_paint (ClutterActor *a)
  {
    ...
    cogl_translate (-scroll_x, -scroll_y, 0);
    ...
  }

Unfortunately these transformations will be completely ignored by
the scenegraph machinery; for instance, getting the actor-relative
coordinates from event coordinates is going to break badly because
of this.

In order to make the scenegraph aware of the potential of additional
transformations, we need a ::apply_transform() virtual function. This
vfunc will pass a CoglMatrix which can be used to apply additional
operations:

  static void
  my_foo_apply_transform (ClutterActor *a, CoglMatrix *m)
  {
    CLUTTER_ACTOR_CLASS (my_foo_parent_class)->apply_transform (a, m);
    ...
    cogl_matrix_translate (m, -scroll_x, -scroll_y, 0);
    ...
  }

The ::paint() implementation will be called with the actor already
using the newly applied transformation matrix, as expected:

  static void
  my_foo_paint (ClutterActor *a)
  {
    ...
  }

The ::apply_transform() implementations *must* chain up, so that the
various transformations of each class are preserved. The default
implementation inside ClutterActor applies all the transformations
defined by the scenegraph-related accessors.

Actors performing transformations inside the paint() function will
continue to work as previously.
This commit is contained in:
Emmanuele Bassi 2009-07-01 13:59:13 +01:00
parent 0414daf0fb
commit 37bd35f592
3 changed files with 89 additions and 31 deletions

View File

@ -497,12 +497,12 @@ static gboolean clutter_anchor_coord_is_zero (const AnchorCoord *coord);
/* Helper macro which translates by the anchor coord, applies the
given transformation and then translates back */
#define TRANSFORM_ABOUT_ANCHOR_COORD(actor,coord,transform) G_STMT_START { \
gfloat _tx, _ty, _tz; \
clutter_anchor_coord_get_units ((actor), (coord), &_tx, &_ty, &_tz); \
cogl_translate (_tx, _ty, _tz); \
{ transform; } \
cogl_translate (-_tx, -_ty, -_tz); } G_STMT_END
#define TRANSFORM_ABOUT_ANCHOR_COORD(a,m,c,_transform) G_STMT_START { \
gfloat _tx, _ty, _tz; \
clutter_anchor_coord_get_units ((a), (c), &_tx, &_ty, &_tz); \
cogl_matrix_translate ((m), _tx, _ty, _tz); \
{ _transform; } \
cogl_matrix_translate ((m), -_tx, -_ty, -_tz); } G_STMT_END
G_DEFINE_ABSTRACT_TYPE_WITH_CODE (ClutterActor,
clutter_actor,
@ -2119,25 +2119,23 @@ clutter_actor_get_abs_allocation_vertices (ClutterActor *self,
verts);
}
/* 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)
clutter_actor_real_apply_transform (ClutterActor *self,
CoglMatrix *matrix)
{
ClutterActorPrivate *priv = self->priv;
gboolean is_stage = CLUTTER_IS_STAGE (self);
gboolean is_stage = CLUTTER_IS_STAGE (self);
if (!is_stage)
cogl_translate (priv->allocation.x1,
priv->allocation.y1,
0);
{
cogl_matrix_translate (matrix,
priv->allocation.x1,
priv->allocation.y1,
0.0);
}
if (priv->z)
cogl_translate (0, 0, priv->z);
cogl_matrix_translate (matrix, 0, 0, priv->z);
/*
* because the rotation involves translations, we must scale before
@ -2147,37 +2145,66 @@ _clutter_actor_apply_modelview_transform (ClutterActor *self)
*/
if (priv->scale_x != 1.0 || priv->scale_y != 1.0)
{
TRANSFORM_ABOUT_ANCHOR_COORD (self,
TRANSFORM_ABOUT_ANCHOR_COORD (self, matrix,
&priv->scale_center,
cogl_scale (priv->scale_x,
priv->scale_y,
1.0));
cogl_matrix_scale (matrix,
priv->scale_x,
priv->scale_y,
1.0));
}
if (priv->rzang)
TRANSFORM_ABOUT_ANCHOR_COORD (self,
TRANSFORM_ABOUT_ANCHOR_COORD (self, matrix,
&priv->rz_center,
cogl_rotate (priv->rzang, 0, 0, 1.0));
cogl_matrix_rotate (matrix,
priv->rzang,
0, 0, 1.0));
if (priv->ryang)
TRANSFORM_ABOUT_ANCHOR_COORD (self,
TRANSFORM_ABOUT_ANCHOR_COORD (self, matrix,
&priv->ry_center,
cogl_rotate (priv->ryang, 0, 1.0, 0));
cogl_matrix_rotate (matrix,
priv->ryang,
0, 1.0, 0));
if (priv->rxang)
TRANSFORM_ABOUT_ANCHOR_COORD (self,
TRANSFORM_ABOUT_ANCHOR_COORD (self, matrix,
&priv->rx_center,
cogl_rotate (priv->rxang, 1.0, 0, 0));
cogl_matrix_rotate (matrix,
priv->rxang,
1.0, 0, 0));
if (!is_stage && !clutter_anchor_coord_is_zero (&priv->anchor))
{
gfloat x, y, z;
clutter_anchor_coord_get_units (self, &priv->anchor, &x, &y, &z);
cogl_translate (-x, -y, -z);
cogl_matrix_translate (matrix, -x, -y, -z);
}
}
/* 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)
{
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);
}
/* Recursively applies the transforms associated with this actor and
* its ancestors to the OpenGL modelview matrix. Use NULL if you want this
* to go all the way down to the stage.
@ -4229,6 +4256,7 @@ clutter_actor_class_init (ClutterActorClass *klass)
klass->get_preferred_height = clutter_actor_real_get_preferred_height;
klass->allocate = clutter_actor_real_allocate;
klass->queue_redraw = clutter_actor_real_queue_redraw;
klass->apply_transform = clutter_actor_real_apply_transform;
}
static void
@ -9163,3 +9191,21 @@ clutter_actor_unset_flags (ClutterActor *self,
g_object_thaw_notify (obj);
}
/**
* clutter_actor_get_transformation_matrix:
* @self: a #ClutterActor
* @matrix: (out): the return location for a #CoglMatrix
*
* Retrieves the transformations applied to @self
*
* Since: 1.0
*/
void
clutter_actor_get_transformation_matrix (ClutterActor *self,
CoglMatrix *matrix)
{
g_return_if_fail (CLUTTER_IS_ACTOR (self));
CLUTTER_ACTOR_GET_CLASS (self)->apply_transform (self, matrix);
}

View File

@ -178,7 +178,11 @@ struct _ClutterActor
* and natural heights of an actor for a given width; it is used by
* clutter_actor_get_preferred_height()
* @allocate: virtual function, used when settings the coordinates of an
* actor; it is used by clutter_actor_allocate()
* actor; it is used by clutter_actor_allocate(); it must chain up to
* the parent's implementation
* @apply_transform: virtual function, used when applying the transformations
* to an actor before painting it or when transforming coordinates or
* the allocation; it must chain up to the parent's implementation
* @parent_set: signal class handler for the #ClutterActor::parent-set
* @destroy: signal class handler for #ClutterActor::destroy
* @pick: virtual function, used to draw an outline of the actor with
@ -238,6 +242,11 @@ struct _ClutterActorClass
void (* allocate) (ClutterActor *actor,
const ClutterActorBox *box,
ClutterAllocationFlags flags);
/* transformations */
void (* apply_transform) (ClutterActor *actor,
CoglMatrix *matrix);
/* event signals */
gboolean (* event) (ClutterActor *actor,
ClutterEvent *event);
@ -325,7 +334,6 @@ void clutter_actor_get_allocation_geometry (ClutterActor
void clutter_actor_get_allocation_vertices (ClutterActor *self,
ClutterActor *ancestor,
ClutterVertex verts[4]);
void clutter_actor_set_geometry (ClutterActor *self,
const ClutterGeometry *geometry);
void clutter_actor_get_geometry (ClutterActor *self,
@ -514,6 +522,9 @@ PangoContext *clutter_actor_create_pango_context (ClutterActor *self
PangoLayout * clutter_actor_create_pango_layout (ClutterActor *self,
const gchar *text);
void clutter_actor_get_transformation_matrix (ClutterActor *self,
CoglMatrix *matrix);
G_END_DECLS
#endif /* __CLUTTER_ACTOR_H__ */

View File

@ -363,6 +363,7 @@ clutter_actor_get_transformed_size
clutter_actor_get_paint_opacity
clutter_actor_get_paint_visibility
clutter_actor_get_abs_allocation_vertices
clutter_actor_get_transformation_matrix
<SUBSECTION>
clutter_actor_set_anchor_point