[actor] Allow changing the transformations matrix
authorEmmanuele Bassi <ebassi@linux.intel.com>
Wed, 1 Jul 2009 12:59:13 +0000 (13:59 +0100)
committerEmmanuele Bassi <ebassi@linux.intel.com>
Wed, 1 Jul 2009 14:30:21 +0000 (15:30 +0100)
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.

clutter/clutter-actor.c
clutter/clutter-actor.h
doc/reference/clutter/clutter-sections.txt

index 7f8b1e9..9b4d360 100644 (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);
+}
index 365006b..080d257 100644 (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__ */
index cdc3f84..109a662 100644 (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