예제 #1
0
void
test_euler_quaternion (void)
{
  CoglEuler euler;
  CoglQuaternion quaternion;
  CoglMatrix matrix_a, matrix_b;

  /* Try doing the rotation with three separate rotations */
  cogl_matrix_init_identity (&matrix_a);
  cogl_matrix_rotate (&matrix_a, -30.0f, 0.0f, 1.0f, 0.0f);
  cogl_matrix_rotate (&matrix_a, 40.0f, 1.0f, 0.0f, 0.0f);
  cogl_matrix_rotate (&matrix_a, 50.0f, 0.0f, 0.0f, 1.0f);

  /* And try the same rotation with a euler */
  cogl_euler_init (&euler, -30, 40, 50);
  cogl_matrix_init_from_euler (&matrix_b, &euler);

  /* Verify that the matrices are approximately the same */
  COMPARE_MATRICES (&matrix_a, &matrix_b);

  /* Try converting the euler to a matrix via a quaternion */
  cogl_quaternion_init_from_euler (&quaternion, &euler);
  memset (&matrix_b, 0, sizeof (matrix_b));
  cogl_matrix_init_from_quaternion (&matrix_b, &quaternion);
  COMPARE_MATRICES (&matrix_a, &matrix_b);

  /* Try applying the rotation from a euler to a framebuffer */
  cogl_framebuffer_identity_matrix (test_fb);
  cogl_framebuffer_rotate_euler (test_fb, &euler);
  memset (&matrix_b, 0, sizeof (matrix_b));
  cogl_framebuffer_get_modelview_matrix (test_fb, &matrix_b);
  COMPARE_MATRICES (&matrix_a, &matrix_b);

  /* And again with a quaternion */
  cogl_framebuffer_identity_matrix (test_fb);
  cogl_framebuffer_rotate_quaternion (test_fb, &quaternion);
  memset (&matrix_b, 0, sizeof (matrix_b));
  cogl_framebuffer_get_modelview_matrix (test_fb, &matrix_b);
  COMPARE_MATRICES (&matrix_a, &matrix_b);

  /* FIXME: This needs a lot more tests! */

  if (cogl_test_verbose ())
    g_print ("OK\n");
}
예제 #2
0
void
rut_transform_rotate (RutTransform *transform,
                      float angle,
                      float x,
                      float y,
                      float z)
{
    cogl_matrix_rotate (&transform->matrix, angle, x, y, z);
}
예제 #3
0
static void
paint_matrix_pipeline (CoglPipeline *pipeline)
{
  CoglMatrix matrices[4];
  float matrix_floats[16 * 4];
  int uniform_location;
  int i;

  for (i = 0; i < 4; i++)
    cogl_matrix_init_identity (matrices + i);

  /* Use the first matrix to make the color red */
  cogl_matrix_translate (matrices + 0, 1.0f, 0.0f, 0.0f);

  /* Rotate the vertex so that it ends up green */
  cogl_matrix_rotate (matrices + 1, 90.0f, 0.0f, 0.0f, 1.0f);

  /* Scale the vertex so it ends up halved */
  cogl_matrix_scale (matrices + 2, 0.5f, 0.5f, 0.5f);

  /* Add a blue component in the final matrix. The final matrix is
     uploaded as transposed so we need to transpose first to cancel
     that out */
  cogl_matrix_translate (matrices + 3, 0.0f, 0.0f, 1.0f);
  cogl_matrix_transpose (matrices + 3);

  for (i = 0; i < 4; i++)
    memcpy (matrix_floats + i * 16,
            cogl_matrix_get_array (matrices + i),
            sizeof (float) * 16);

  /* Set the first three matrices as transposed */
  uniform_location =
    cogl_pipeline_get_uniform_location (pipeline, "matrix_array");
  cogl_pipeline_set_uniform_matrix (pipeline,
                                    uniform_location,
                                    4, /* dimensions */
                                    3, /* count */
                                    FALSE, /* not transposed */
                                    matrix_floats);

  /* Set the last matrix as untransposed */
  uniform_location =
    cogl_pipeline_get_uniform_location (pipeline, "matrix_array[3]");
  cogl_pipeline_set_uniform_matrix (pipeline,
                                    uniform_location,
                                    4, /* dimensions */
                                    1, /* count */
                                    TRUE, /* transposed */
                                    matrix_floats + 16 * 3);

  paint_pipeline (pipeline, 12);
}
예제 #4
0
void
_cogl_matrix_stack_rotate (CoglMatrixStack *stack,
                           float            angle,
                           float            x,
                           float            y,
                           float            z)
{
  CoglMatrixState *state;

  state = _cogl_matrix_stack_top_mutable (stack, TRUE);
  cogl_matrix_rotate (&state->matrix, angle, x, y, z);
  state->is_identity = FALSE;
  stack->age++;
}
예제 #5
0
void
mai_node_draw_recursive (MaiNode *self)
{
  CoglMatrix initial_mtx;
  cogl_matrix_init_identity (&initial_mtx);
  cogl_set_modelview_matrix (&initial_mtx);

  cogl_set_source_color4ub ('\x1', '\x1', '\xFF', 255);
  cogl_set_source_texture (g_testtex);
  cogl_ortho (0, 64, 0, 64, -1, 1);

  cogl_matrix_translate (&initial_mtx, 20.0f, 20.0f, 0.0f);
  cogl_matrix_scale (&initial_mtx, 5.0f, 5.0f, 1.0f);
  cogl_matrix_rotate(&initial_mtx, -90.0f, 1.0f, 0.0f, 0.0f);

  _mai_node_draw_recursive (self, &initial_mtx);

  cogl_flush ();
}
예제 #6
0
G_MODULE_EXPORT int
test_cogl_multitexture_main (int argc, char *argv[])
{
  GError            *error = NULL;
  ClutterActor	    *stage;
  ClutterColor       stage_color = { 0x61, 0x56, 0x56, 0xff };
  TestMultiLayerMaterialState *state = g_new0 (TestMultiLayerMaterialState, 1);
  gfloat             stage_w, stage_h;
  gchar            **files;
  gfloat             tex_coords[] =
    {
    /* tx1  ty1  tx2  ty2 */
         0,   0,   1,   1,
         0,   0,   1,   1,
         0,   0,   1,   1
    };

  if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS)
    return 1;

  stage = clutter_stage_new ();
  clutter_actor_get_size (stage, &stage_w, &stage_h);

  clutter_stage_set_title (CLUTTER_STAGE (stage), "Cogl: Multi-texturing");
  clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color);

  g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL);

  /* We create a non-descript actor that we know doesn't have a
   * default paint handler, so that we can easily control
   * painting in a paint signal handler, without having to
   * sub-class anything etc. */
  state->group = clutter_group_new ();
  clutter_actor_set_position (state->group, stage_w / 2, stage_h / 2);
  g_signal_connect (state->group, "paint",
		    G_CALLBACK(material_rectangle_paint), state);

  files = g_new (gchar*, 4);
  files[0] = g_build_filename (TESTS_DATADIR, "redhand_alpha.png", NULL);
  files[1] = g_build_filename (TESTS_DATADIR, "redhand.png", NULL);
  files[2] = g_build_filename (TESTS_DATADIR, "light0.png", NULL);
  files[3] = NULL;

  state->alpha_tex =
    cogl_texture_new_from_file (files[0],
                                COGL_TEXTURE_NO_SLICING,
				COGL_PIXEL_FORMAT_ANY,
				&error);
  if (!state->alpha_tex)
    g_critical ("Failed to load redhand_alpha.png: %s", error->message);

  state->redhand_tex =
    cogl_texture_new_from_file (files[1],
                                COGL_TEXTURE_NO_SLICING,
				COGL_PIXEL_FORMAT_ANY,
				&error);
  if (!state->redhand_tex)
    g_critical ("Failed to load redhand.png: %s", error->message);

  state->light_tex0 =
    cogl_texture_new_from_file (files[2],
                                COGL_TEXTURE_NO_SLICING,
				COGL_PIXEL_FORMAT_ANY,
				&error);
  if (!state->light_tex0)
    g_critical ("Failed to load light0.png: %s", error->message);

  state->light_tex1 =
    cogl_texture_new_from_file (files[2],
                                COGL_TEXTURE_NO_SLICING,
				COGL_PIXEL_FORMAT_ANY,
				&error);
  if (!state->light_tex1)
    g_critical ("Failed to load light0.png: %s", error->message);

  g_strfreev (files);

  state->material0 = cogl_material_new ();
  cogl_material_set_layer (state->material0, 0, state->alpha_tex);
  cogl_material_set_layer (state->material0, 1, state->redhand_tex);
  cogl_material_set_layer (state->material0, 2, state->light_tex0);

  state->material1 = cogl_material_new ();
  cogl_material_set_layer (state->material1, 0, state->alpha_tex);
  cogl_material_set_layer (state->material1, 1, state->redhand_tex);
  cogl_material_set_layer (state->material1, 2, state->light_tex1);

  state->tex_coords = tex_coords;

  cogl_matrix_init_identity (&state->tex_matrix0);
  cogl_matrix_init_identity (&state->tex_matrix1);
  cogl_matrix_init_identity (&state->rot_matrix0);
  cogl_matrix_init_identity (&state->rot_matrix1);

  cogl_matrix_translate (&state->rot_matrix0, 0.5, 0.5, 0);
  cogl_matrix_rotate (&state->rot_matrix0, 10.0, 0, 0, 1.0);
  cogl_matrix_translate (&state->rot_matrix0, -0.5, -0.5, 0);

  cogl_matrix_translate (&state->rot_matrix1, 0.5, 0.5, 0);
  cogl_matrix_rotate (&state->rot_matrix1, -10.0, 0, 0, 1.0);
  cogl_matrix_translate (&state->rot_matrix1, -0.5, -0.5, 0);

  clutter_actor_set_anchor_point (state->group, 86, 125);
  clutter_container_add_actor (CLUTTER_CONTAINER(stage),
			       state->group);

  state->timeline = clutter_timeline_new (2812);

  g_signal_connect (state->timeline, "new-frame", G_CALLBACK (frame_cb), state);

  clutter_actor_animate_with_timeline (state->group,
                                       CLUTTER_LINEAR,
                                       state->timeline,
                                       "rotation-angle-y", 30.0,
                                       "signal-after::completed",
                                       animation_completed_cb, state,
                                       NULL);

  /* start the timeline and thus the animations */
  clutter_timeline_start (state->timeline);

  clutter_actor_show_all (stage);

  clutter_main();

  cogl_handle_unref (state->material1);
  cogl_handle_unref (state->material0);
  cogl_handle_unref (state->alpha_tex);
  cogl_handle_unref (state->redhand_tex);
  cogl_handle_unref (state->light_tex0);
  cogl_handle_unref (state->light_tex1);
  g_free (state);

  return 0;
}
예제 #7
0
/* In addition to writing the stack matrix into the give @matrix
 * argument this function *may* sometimes also return a pointer
 * to a matrix too so if we are querying the inverse matrix we
 * should query from the return matrix so that the result can
 * be cached within the stack. */
CoglMatrix *
cogl_matrix_entry_get (CoglMatrixEntry *entry,
                        CoglMatrix *matrix)
{
  int depth;
  CoglMatrixEntry *current;
  CoglMatrixEntry **children;
  int i;

  for (depth = 0, current = entry;
       current;
       current = current->parent, depth++)
    {
      switch (current->op)
        {
        case COGL_MATRIX_OP_LOAD_IDENTITY:
          cogl_matrix_init_identity (matrix);
          goto initialized;
        case COGL_MATRIX_OP_LOAD:
          {
            CoglMatrixEntryLoad *load = (CoglMatrixEntryLoad *)current;
            *matrix = *load->matrix;
            goto initialized;
          }
        case COGL_MATRIX_OP_SAVE:
          {
            CoglMatrixEntrySave *save = (CoglMatrixEntrySave *)current;
            if (!save->cache_valid)
              {
                CoglMagazine *matrices_magazine =
                  cogl_matrix_stack_matrices_magazine;
                save->cache = _cogl_magazine_chunk_alloc (matrices_magazine);
                cogl_matrix_entry_get (current->parent, save->cache);
                save->cache_valid = TRUE;
              }
            *matrix = *save->cache;
            goto initialized;
          }
        default:
          continue;
        }
    }

initialized:

  if (depth == 0)
    {
      switch (entry->op)
        {
        case COGL_MATRIX_OP_LOAD_IDENTITY:
        case COGL_MATRIX_OP_TRANSLATE:
        case COGL_MATRIX_OP_ROTATE:
        case COGL_MATRIX_OP_ROTATE_QUATERNION:
        case COGL_MATRIX_OP_ROTATE_EULER:
        case COGL_MATRIX_OP_SCALE:
        case COGL_MATRIX_OP_MULTIPLY:
          return NULL;

        case COGL_MATRIX_OP_LOAD:
          {
            CoglMatrixEntryLoad *load = (CoglMatrixEntryLoad *)entry;
            return load->matrix;
          }
        case COGL_MATRIX_OP_SAVE:
          {
            CoglMatrixEntrySave *save = (CoglMatrixEntrySave *)entry;
            return save->cache;
          }
        }
      g_warn_if_reached ();
      return NULL;
    }

#ifdef COGL_ENABLE_DEBUG
  if (!current)
    {
      g_warning ("Inconsistent matrix stack");
      return NULL;
    }

  entry->composite_gets++;
#endif

  children = g_alloca (sizeof (CoglMatrixEntry) * depth);

  /* We need walk the list of entries from the init/load/save entry
   * back towards the leaf node but the nodes don't link to their
   * children so we need to re-walk them here to add to a separate
   * array. */
  for (i = depth - 1, current = entry;
       i >= 0 && current;
       i--, current = current->parent)
    {
      children[i] = current;
    }

#ifdef COGL_ENABLE_DEBUG
  if (COGL_DEBUG_ENABLED (COGL_DEBUG_PERFORMANCE) &&
      entry->composite_gets >= 2)
    {
      COGL_NOTE (PERFORMANCE,
                 "Re-composing a matrix stack entry multiple times");
    }
#endif

  for (i = 0; i < depth; i++)
    {
      switch (children[i]->op)
        {
        case COGL_MATRIX_OP_TRANSLATE:
          {
            CoglMatrixEntryTranslate *translate =
              (CoglMatrixEntryTranslate *)children[i];
            cogl_matrix_translate (matrix,
                                   translate->x,
                                   translate->y,
                                   translate->z);
            continue;
          }
        case COGL_MATRIX_OP_ROTATE:
          {
            CoglMatrixEntryRotate *rotate=
              (CoglMatrixEntryRotate *)children[i];
            cogl_matrix_rotate (matrix,
                                rotate->angle,
                                rotate->x,
                                rotate->y,
                                rotate->z);
            continue;
          }
        case COGL_MATRIX_OP_ROTATE_EULER:
          {
            CoglMatrixEntryRotateEuler *rotate =
              (CoglMatrixEntryRotateEuler *)children[i];
            CoglEuler euler;
            cogl_euler_init (&euler,
                             rotate->heading,
                             rotate->pitch,
                             rotate->roll);
            cogl_matrix_rotate_euler (matrix,
                                      &euler);
            continue;
          }
        case COGL_MATRIX_OP_ROTATE_QUATERNION:
          {
            CoglMatrixEntryRotateQuaternion *rotate =
              (CoglMatrixEntryRotateQuaternion *)children[i];
            CoglQuaternion quaternion;
            cogl_quaternion_init_from_array (&quaternion, rotate->values);
            cogl_matrix_rotate_quaternion (matrix, &quaternion);
            continue;
          }
        case COGL_MATRIX_OP_SCALE:
          {
            CoglMatrixEntryScale *scale =
              (CoglMatrixEntryScale *)children[i];
            cogl_matrix_scale (matrix,
                               scale->x,
                               scale->y,
                               scale->z);
            continue;
          }
        case COGL_MATRIX_OP_MULTIPLY:
          {
            CoglMatrixEntryMultiply *multiply =
              (CoglMatrixEntryMultiply *)children[i];
            cogl_matrix_multiply (matrix, matrix, multiply->matrix);
            continue;
          }

        case COGL_MATRIX_OP_LOAD_IDENTITY:
        case COGL_MATRIX_OP_LOAD:
        case COGL_MATRIX_OP_SAVE:
          g_warn_if_reached ();
          continue;
        }
    }

  return NULL;
}
예제 #8
0
static RutDiamondSlice *
diamond_slice_new (RutContext *ctx,
                   float size,
                   int tex_width,
                   int tex_height)
{
  RutDiamondSlice *diamond_slice = g_slice_new (RutDiamondSlice);
  float width = size;
  float height = size;
#define DIAMOND_SLICE_CORNER_RADIUS 20
  CoglMatrix matrix;
  float tex_aspect;

  rut_object_init (&diamond_slice->_parent, &rut_diamond_slice_type);

  diamond_slice->ref_count = 1;

  diamond_slice->size = size;

    {
      /* x0,y0,x1,y1 and s0,t0,s1,t1 define the postion and texture
       * coordinates for the center rectangle... */
      float x0 = DIAMOND_SLICE_CORNER_RADIUS;
      float y0 = DIAMOND_SLICE_CORNER_RADIUS;
      float x1 = width - DIAMOND_SLICE_CORNER_RADIUS;
      float y1 = height - DIAMOND_SLICE_CORNER_RADIUS;

      /* The center region of the nine-slice can simply map to the
       * degenerate center of the circle */
      float s0 = 0.5;
      float t0 = 0.5;
      float s1 = 0.5;
      float t1 = 0.5;

      int n_vertices;
      int i;

      /*
       * 0,0      x0,0      x1,0      width,0
       * 0,0      s0,0      s1,0      1,0
       * 0        1         2         3
       *
       * 0,y0     x0,y0     x1,y0     width,y0
       * 0,t0     s0,t0     s1,t0     1,t0
       * 4        5         6         7
       *
       * 0,y1     x0,y1     x1,y1     width,y1
       * 0,t1     s0,t1     s1,t1     1,t1
       * 8        9         10        11
       *
       * 0,height x0,height x1,height width,height
       * 0,1      s0,1      s1,1      1,1
       * 12       13        14        15
       */

      VertexP2T2T2 vertices[] =
        {
          { 0,  0, 0, 0, 0, 0 },
          { x0, 0, s0, 0, x0, 0},
          { x1, 0, s1, 0, x1, 0},
          { width, 0, 1, 0, width, 0},

          { 0, y0, 0, t0, 0, y0},
          { x0, y0, s0, t0, x0, y0},
          { x1, y0, s1, t0, x1, y0},
          { width, y0, 1, t0, width, y0},

          { 0, y1, 0, t1, 0, y1},
          { x0, y1, s0, t1, x0, y1},
          { x1, y1, s1, t1, x1, y1},
          { width, y1, 1, t1, width, y1},

          { 0, height, 0, 1, 0, height},
          { x0, height, s0, 1, x0, height},
          { x1, height, s1, 1, x1, height},
          { width, height, 1, 1, width, height},
        };

      cogl_matrix_init_identity (&diamond_slice->rotate_matrix);
      cogl_matrix_rotate (&diamond_slice->rotate_matrix, 45, 0, 0, 1);
      cogl_matrix_translate (&diamond_slice->rotate_matrix, - width / 2.0, - height / 2.0, 0);

      n_vertices = sizeof (vertices) / sizeof (VertexP2T2T2);
      for (i = 0; i < n_vertices; i++)
        {
          float z = 0, w = 1;

          cogl_matrix_transform_point (&diamond_slice->rotate_matrix,
                                       &vertices[i].x,
                                       &vertices[i].y,
                                       &z,
                                       &w);

#ifdef MESA_CONST_ATTRIB_BUG_WORKAROUND
          vertices[i].Nx = 0;
          vertices[i].Ny = 0;
          vertices[i].Nz = 1;

          vertices[i].Tx = 1;
          vertices[i].Ty = 0;
          vertices[i].Tz = 0;
#endif
        }

      cogl_matrix_init_identity (&matrix);

      {
        float s_scale = 1.0, t_scale = 1.0;
        float s0, t0;
        float diagonal_size_scale = 1.0 / (sinf (G_PI_4) * 2.0);

        tex_aspect = (float)tex_width / (float)tex_height;

        if (tex_aspect < 1) /* taller than it is wide */
          t_scale *= tex_aspect;
        else /* wider than it is tall */
          {
            float inverse_aspect = 1.0f / tex_aspect;
            s_scale *= inverse_aspect;
          }

        s_scale *= diagonal_size_scale;
        t_scale *= diagonal_size_scale;

        s0 = 0.5 - (s_scale / 2.0);
        t0 = 0.5 - (t_scale / 2.0);

        cogl_matrix_translate (&matrix, s0, t0, 0);
        cogl_matrix_scale (&matrix, s_scale / width, t_scale / height, 1);

        cogl_matrix_translate (&matrix, width / 2.0, height / 2.0, 1);
        cogl_matrix_rotate (&matrix, 45, 0, 0, 1);
        cogl_matrix_translate (&matrix, -width / 2.0, -height / 2.0, 1);
      }

      n_vertices = sizeof (vertices) / sizeof (VertexP2T2T2);
      for (i = 0; i < n_vertices; i++)
        {
          float z = 0, w = 1;

          cogl_matrix_transform_point (&matrix,
                                       &vertices[i].s1,
                                       &vertices[i].t1,
                                       &z,
                                       &w);
        }


      diamond_slice->primitive =
        primitive_new_p2t2t2 (ctx->cogl_context,
                              COGL_VERTICES_MODE_TRIANGLES,
                              n_vertices,
                              vertices);

      /* The vertices uploaded only map to the key intersection points of the
       * 9-slice grid which isn't a topology that GPUs can handle directly so
       * this specifies an array of indices that allow the GPU to interpret the
       * vertices as a list of triangles... */
      cogl_primitive_set_indices (diamond_slice->primitive,
                                  ctx->nine_slice_indices,
                                  sizeof (_rut_nine_slice_indices_data) /
                                  sizeof (_rut_nine_slice_indices_data[0]));
    }

  return diamond_slice;
}
예제 #9
0
static gboolean
clutter_matrix_progress (const GValue *a,
                         const GValue *b,
                         gdouble       progress,
                         GValue       *retval)
{
  const ClutterMatrix *matrix1 = g_value_get_boxed (a);
  const ClutterMatrix *matrix2 = g_value_get_boxed (b);
  ClutterVertex scale1 = CLUTTER_VERTEX_INIT (1.f, 1.f, 1.f);
  float shear1[3] = { 0.f, 0.f, 0.f };
  ClutterVertex rotate1 = CLUTTER_VERTEX_INIT_ZERO;
  ClutterVertex translate1 = CLUTTER_VERTEX_INIT_ZERO;
  ClutterVertex4 perspective1 = { 0.f, 0.f, 0.f, 0.f };
  ClutterVertex scale2 = CLUTTER_VERTEX_INIT (1.f, 1.f, 1.f);
  float shear2[3] = { 0.f, 0.f, 0.f };
  ClutterVertex rotate2 = CLUTTER_VERTEX_INIT_ZERO;
  ClutterVertex translate2 = CLUTTER_VERTEX_INIT_ZERO;
  ClutterVertex4 perspective2 = { 0.f, 0.f, 0.f, 0.f };
  ClutterVertex scale_res = CLUTTER_VERTEX_INIT (1.f, 1.f, 1.f);
  float shear_res = 0.f;
  ClutterVertex rotate_res = CLUTTER_VERTEX_INIT_ZERO;
  ClutterVertex translate_res = CLUTTER_VERTEX_INIT_ZERO;
  ClutterVertex4 perspective_res = { 0.f, 0.f, 0.f, 0.f };
  ClutterMatrix res;

  clutter_matrix_init_identity (&res);

  _clutter_util_matrix_decompose (matrix1,
                                  &scale1, shear1, &rotate1, &translate1,
                                  &perspective1);
  _clutter_util_matrix_decompose (matrix2,
                                  &scale2, shear2, &rotate2, &translate2,
                                  &perspective2);

  /* perspective */
  _clutter_util_vertex4_interpolate (&perspective1, &perspective2, progress, &perspective_res);
  res.wx = perspective_res.x;
  res.wy = perspective_res.y;
  res.wz = perspective_res.z;
  res.ww = perspective_res.w;

  /* translation */
  clutter_vertex_interpolate (&translate1, &translate2, progress, &translate_res);
  cogl_matrix_translate (&res, translate_res.x, translate_res.y, translate_res.z);

  /* rotation */
  clutter_vertex_interpolate (&rotate1, &rotate2, progress, &rotate_res);
  cogl_matrix_rotate (&res, rotate_res.x, 1.0f, 0.0f, 0.0f);
  cogl_matrix_rotate (&res, rotate_res.y, 0.0f, 1.0f, 0.0f);
  cogl_matrix_rotate (&res, rotate_res.z, 0.0f, 0.0f, 1.0f);

  /* skew */
  shear_res = shear1[2] + (shear2[2] - shear1[2]) * progress; /* YZ */
  if (shear_res != 0.f)
    _clutter_util_matrix_skew_yz (&res, shear_res);

  shear_res = shear1[1] + (shear2[1] - shear1[1]) * progress; /* XZ */
  if (shear_res != 0.f)
    _clutter_util_matrix_skew_xz (&res, shear_res);

  shear_res = shear1[0] + (shear2[0] - shear1[0]) * progress; /* XY */
  if (shear_res != 0.f)
    _clutter_util_matrix_skew_xy (&res, shear_res);

  /* scale */
  clutter_vertex_interpolate (&scale1, &scale2, progress, &scale_res);
  cogl_matrix_scale (&res, scale_res.x, scale_res.y, scale_res.z);

  g_value_set_boxed (retval, &res);

  return TRUE;
}