diff --git a/clutter/clutter-base-types.c b/clutter/clutter-base-types.c index 91fbd95a1..0496b5204 100644 --- a/clutter/clutter-base-types.c +++ b/clutter/clutter-base-types.c @@ -284,6 +284,17 @@ clutter_vertex_equal (const ClutterVertex *vertex_a, fabsf (vertex_a->z - vertex_b->z) < FLOAT_EPSILON; } +static void +clutter_vertex_interpolate (const ClutterVertex *a, + const ClutterVertex *b, + double progress, + ClutterVertex *res) +{ + res->x = a->x + (b->x - a->x) * progress; + res->y = a->y + (b->y - a->y) * progress; + res->z = a->z + (b->z - a->z) * progress; +} + static gboolean clutter_vertex_progress (const GValue *a, const GValue *b, @@ -294,9 +305,7 @@ clutter_vertex_progress (const GValue *a, const ClutterVertex *bv = g_value_get_boxed (b); ClutterVertex res; - res.x = av->x + (bv->x - av->x) * progress; - res.y = av->y + (bv->y - av->y) * progress; - res.z = av->z + (bv->z - av->z) * progress; + clutter_vertex_interpolate (av, bv, progress, &res); g_value_set_boxed (retval, &res); @@ -1280,12 +1289,86 @@ clutter_rect_progress (const GValue *a, static gpointer clutter_matrix_copy (gpointer data) { - return g_memdup (data, sizeof (ClutterMatrix)); + return cogl_matrix_copy (data); } -G_DEFINE_BOXED_TYPE (ClutterMatrix, clutter_matrix, - clutter_matrix_copy, - clutter_matrix_free) +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; +} + +G_DEFINE_BOXED_TYPE_WITH_CODE (ClutterMatrix, clutter_matrix, + clutter_matrix_copy, + clutter_matrix_free, + CLUTTER_REGISTER_INTERVAL_PROGRESS (clutter_matrix_progress)) /** * clutter_matrix_alloc: @@ -1313,7 +1396,7 @@ clutter_matrix_alloc (void) void clutter_matrix_free (ClutterMatrix *matrix) { - g_free (matrix); + cogl_matrix_free (matrix); } /** diff --git a/clutter/clutter-private.h b/clutter/clutter-private.h index f855727d7..1d28ee0fa 100644 --- a/clutter/clutter-private.h +++ b/clutter/clutter-private.h @@ -46,6 +46,7 @@ G_BEGIN_DECLS typedef struct _ClutterMainContext ClutterMainContext; +typedef struct _ClutterVertex4 ClutterVertex4; #define CLUTTER_REGISTER_VALUE_TRANSFORM_TO(TYPE_TO,func) { \ g_value_register_transform_func (g_define_type_id, TYPE_TO, func); \ @@ -265,6 +266,44 @@ void _clutter_util_rectangle_union (const cairo_rectangle_int_t *src1, const cairo_rectangle_int_t *src2, cairo_rectangle_int_t *dest); + +struct _ClutterVertex4 +{ + float x; + float y; + float z; + float w; +}; + +void +_clutter_util_vertex4_interpolate (const ClutterVertex4 *a, + const ClutterVertex4 *b, + double progress, + ClutterVertex4 *res); + +#define CLUTTER_MATRIX_INIT_IDENTITY { \ + 1.0f, 0.0f, 0.0f, 0.0f, \ + 0.0f, 1.0f, 0.0f, 0.0f, \ + 0.0f, 0.0f, 1.0f, 0.0f, \ + 0.0f, 0.0f, 0.0f, 1.0f, \ +} + +float _clutter_util_matrix_determinant (const ClutterMatrix *matrix); + +void _clutter_util_matrix_skew_xy (ClutterMatrix *matrix, + float factor); +void _clutter_util_matrix_skew_xz (ClutterMatrix *matrix, + float factor); +void _clutter_util_matrix_skew_yz (ClutterMatrix *matrix, + float factor); + +gboolean _clutter_util_matrix_decompose (const ClutterMatrix *src, + ClutterVertex *scale_p, + float shear_p[3], + ClutterVertex *rotate_p, + ClutterVertex *translate_p, + ClutterVertex4 *perspective_p); + typedef struct _ClutterPlane { float v0[3]; diff --git a/clutter/clutter-types.h b/clutter/clutter-types.h index d5e4662c2..431964acd 100644 --- a/clutter/clutter-types.h +++ b/clutter/clutter-types.h @@ -694,14 +694,20 @@ typedef gboolean (* ClutterProgressFunc) (const GValue *a, void clutter_interval_register_progress_func (GType value_type, ClutterProgressFunc func); +CLUTTER_AVAILABLE_IN_1_12 GType clutter_matrix_get_type (void) G_GNUC_CONST; +CLUTTER_AVAILABLE_IN_1_12 ClutterMatrix * clutter_matrix_alloc (void); +CLUTTER_AVAILABLE_IN_1_12 ClutterMatrix * clutter_matrix_init_identity (ClutterMatrix *matrix); +CLUTTER_AVAILABLE_IN_1_12 ClutterMatrix * clutter_matrix_init_from_array (ClutterMatrix *matrix, const float values[16]); +CLUTTER_AVAILABLE_IN_1_12 ClutterMatrix * clutter_matrix_init_from_matrix (ClutterMatrix *a, const ClutterMatrix *b); +CLUTTER_AVAILABLE_IN_1_12 void clutter_matrix_free (ClutterMatrix *matrix); G_END_DECLS diff --git a/clutter/clutter-util.c b/clutter/clutter-util.c index b95629c4d..20dd2f985 100644 --- a/clutter/clutter-util.c +++ b/clutter/clutter-util.c @@ -34,8 +34,11 @@ #include "config.h" #endif +#include + #include +#include "clutter-debug.h" #include "clutter-main.h" #include "clutter-interval.h" #include "clutter-private.h" @@ -84,14 +87,6 @@ _clutter_gettext (const gchar *str) #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))) -typedef struct _ClutterVertex4 -{ - float x; - float y; - float z; - float w; -} ClutterVertex4; - void _clutter_util_fully_transform_vertices (const CoglMatrix *modelview, const CoglMatrix *projection, @@ -182,6 +177,346 @@ _clutter_util_rectangle_union (const cairo_rectangle_int_t *src1, dest->y = dest_y; } +float +_clutter_util_matrix_determinant (const ClutterMatrix *matrix) +{ + return matrix->xw * matrix->yz * matrix->zy * matrix->wz + - matrix->xz * matrix->yw * matrix->zy * matrix->wz + - matrix->xw * matrix->yy * matrix->zz * matrix->wz + + matrix->xy * matrix->yw * matrix->zz * matrix->wz + + matrix->xz * matrix->yy * matrix->zw * matrix->wz + - matrix->xy * matrix->yz * matrix->zw * matrix->wz + - matrix->xw * matrix->yz * matrix->zx * matrix->wy + + matrix->xz * matrix->yw * matrix->zx * matrix->wy + + matrix->xw * matrix->yx * matrix->zz * matrix->wy + - matrix->xx * matrix->yw * matrix->zz * matrix->wy + - matrix->xz * matrix->yx * matrix->zw * matrix->wy + + matrix->xx * matrix->yz * matrix->zw * matrix->wy + + matrix->xw * matrix->yy * matrix->zx * matrix->wz + - matrix->xy * matrix->yw * matrix->zx * matrix->wz + - matrix->xw * matrix->yx * matrix->zy * matrix->wz + + matrix->xx * matrix->yw * matrix->zy * matrix->wz + + matrix->xy * matrix->yx * matrix->zw * matrix->wz + - matrix->xx * matrix->yy * matrix->zw * matrix->wz + - matrix->xz * matrix->yy * matrix->zx * matrix->ww + + matrix->xy * matrix->yz * matrix->zx * matrix->ww + + matrix->xz * matrix->yx * matrix->zy * matrix->ww + - matrix->xx * matrix->yz * matrix->zy * matrix->ww + - matrix->xy * matrix->yx * matrix->zz * matrix->ww + + matrix->xx * matrix->yy * matrix->zz * matrix->ww; +} + +static void +_clutter_util_matrix_transpose_vector4_transform (const ClutterMatrix *matrix, + const ClutterVertex4 *point, + ClutterVertex4 *res) +{ + res->x = matrix->xx * point->x + + matrix->xy * point->y + + matrix->xz * point->z + + matrix->xw * point->w; + + res->y = matrix->yx * point->x + + matrix->yy * point->y + + matrix->yz * point->z + + matrix->yw * point->w; + + res->z = matrix->zx * point->x + + matrix->zy * point->y + + matrix->zz * point->z + + matrix->zw * point->w; + + res->w = matrix->wz * point->x + + matrix->wy * point->w + + matrix->wz * point->z + + matrix->ww * point->w; +} + +void +_clutter_util_matrix_skew_xy (ClutterMatrix *matrix, + float factor) +{ + matrix->yx += matrix->xx * factor; + matrix->yy += matrix->xy * factor; + matrix->yz += matrix->xz * factor; + matrix->yw += matrix->xw * factor; +} + +void +_clutter_util_matrix_skew_xz (ClutterMatrix *matrix, + float factor) +{ + matrix->zx += matrix->xx * factor; + matrix->zy += matrix->xy * factor; + matrix->zz += matrix->xz * factor; + matrix->zw += matrix->xw * factor; +} + +void +_clutter_util_matrix_skew_yz (ClutterMatrix *matrix, + float factor) +{ + matrix->zx += matrix->yx * factor; + matrix->zy += matrix->yy * factor; + matrix->zz += matrix->yz * factor; + matrix->zw += matrix->yw * factor; +} + +static float +_clutter_util_vertex_length (const ClutterVertex *vertex) +{ + return sqrtf (vertex->x * vertex->x + vertex->y * vertex->y + vertex->z * vertex->z); +} + +static void +_clutter_util_vertex_normalize (ClutterVertex *vertex) +{ + float factor = _clutter_util_vertex_length (vertex); + + if (factor == 0.f) + return; + + vertex->x /= factor; + vertex->y /= factor; + vertex->z /= factor; +} + +static float +_clutter_util_vertex_dot (const ClutterVertex *v1, + const ClutterVertex *v2) +{ + return v1->x * v2->x + v1->y * v2->y + v1->z * v2->z; +} + +static void +_clutter_util_vertex_cross (const ClutterVertex *v1, + const ClutterVertex *v2, + ClutterVertex *res) +{ + res->x = v1->y * v2->z - v2->y * v1->z; + res->y = v1->z * v2->x - v2->z * v1->x; + res->z = v1->x * v2->y - v2->x * v1->y; +} + +static void +_clutter_util_vertex_combine (const ClutterVertex *a, + const ClutterVertex *b, + double ascl, + double bscl, + ClutterVertex *res) +{ + res->x = (ascl * a->x) + (bscl * b->x); + res->y = (ascl * a->y) + (bscl * b->y); + res->z = (ascl * a->z) + (bscl * b->z); +} + +void +_clutter_util_vertex4_interpolate (const ClutterVertex4 *a, + const ClutterVertex4 *b, + double progress, + ClutterVertex4 *res) +{ + res->x = a->x + (b->x - a->x) * progress; + res->y = a->y + (b->y - a->y) * progress; + res->z = a->z + (b->z - a->z) * progress; + res->w = a->w + (b->w - a->w) * progress; +} + +/*< private > + * clutter_util_matrix_decompose: + * @src: the matrix to decompose + * @scale_p: (out caller-allocates): return location for a vertex containing + * the scaling factors + * @shear_p: (out) (array length=3): return location for an array of 3 + * elements containing the skew factors (XY, XZ, and YZ respectively) + * @rotate_p: (out caller-allocates): return location for a vertex containing + * the Euler angles + * @translate_p: (out caller-allocates): return location for a vertex + * containing the translation vector + * @perspective_p: (out caller-allocates: return location for a 4D vertex + * containing the perspective + * + * Decomposes a #ClutterMatrix into the transformations that compose it. + * + * This code is based on the matrix decomposition algorithm as published in + * the CSS Transforms specification by the W3C CSS working group, available + * at http://www.w3.org/TR/css3-transforms/. + * + * The algorithm, in turn, is based on the "unmatrix" method published in + * "Graphics Gems II, edited by Jim Arvo", which is available at: + * http://tog.acm.org/resources/GraphicsGems/gemsii/unmatrix.c + * + * Return value: %TRUE if the decomposition was successful, and %FALSE + * if the matrix is singular + */ +gboolean +_clutter_util_matrix_decompose (const ClutterMatrix *src, + ClutterVertex *scale_p, + float shear_p[3], + ClutterVertex *rotate_p, + ClutterVertex *translate_p, + ClutterVertex4 *perspective_p) +{ + CoglMatrix matrix = *src; + CoglMatrix perspective; + ClutterVertex4 vertex_tmp; + ClutterVertex row[3], pdum; + int i, j; + +#define XY_SHEAR 0 +#define XZ_SHEAR 1 +#define YZ_SHEAR 2 +#define MAT(m,r,c) ((float *)(m))[(c) * 4 + (r)] + + /* normalize the matrix */ + if (matrix.ww == 0.f) + return FALSE; + + for (i = 0; i < 4; i++) + { + for (j = 0; j < 4; j++) + { + MAT (&matrix, j, i) /= MAT (&matrix, 3, 3); + } + } + + /* perspective is used to solve for perspective, but it also provides + * an easy way to test for singularity of the upper 3x3 component + */ + perspective = matrix; + + /* transpose */ + MAT (&perspective, 3, 0) = 0.f; + MAT (&perspective, 3, 1) = 0.f; + MAT (&perspective, 3, 2) = 0.f; + MAT (&perspective, 3, 3) = 1.f; + + if (_clutter_util_matrix_determinant (&perspective) == 0.f) + return FALSE; + + if (MAT (&matrix, 3, 0) != 0.f || + MAT (&matrix, 3, 1) != 0.f || + MAT (&matrix, 3, 2) != 0.f) + { + CoglMatrix perspective_inv; + ClutterVertex4 p; + + vertex_tmp.x = MAT (&matrix, 3, 0); + vertex_tmp.y = MAT (&matrix, 3, 1); + vertex_tmp.z = MAT (&matrix, 3, 2); + vertex_tmp.w = MAT (&matrix, 3, 3); + + /* solve the equation by inverting perspective... */ + cogl_matrix_get_inverse (&perspective, &perspective_inv); + + /* ... and multiplying vertex_tmp by the inverse */ + _clutter_util_matrix_transpose_vector4_transform (&perspective_inv, + &vertex_tmp, + &p); + + *perspective_p = p; + + /* clear the perspective part */ + MAT (&matrix, 3, 0) = 0.0f; + MAT (&matrix, 3, 1) = 0.0f; + MAT (&matrix, 3, 2) = 0.0f; + MAT (&matrix, 3, 3) = 1.0f; + } + else + { + /* no perspective */ + perspective_p->x = 0.0f; + perspective_p->y = 0.0f; + perspective_p->z = 0.0f; + perspective_p->w = 1.0f; + } + + /* translation */ + translate_p->x = MAT (&matrix, 0, 3); + MAT (&matrix, 0, 3) = 0.f; + translate_p->y = MAT (&matrix, 1, 3); + MAT (&matrix, 1, 3) = 0.f; + translate_p->z = MAT (&matrix, 2, 3); + MAT (&matrix, 2, 3) = 0.f; + + /* scale and shear; we split the upper 3x3 matrix into rows */ + for (i = 0; i < 3; i++) + { + row[i].x = MAT (&matrix, i, 0); + row[i].y = MAT (&matrix, i, 1); + row[i].z = MAT (&matrix, i, 2); + } + + /* compute scale.x and normalize the first row */ + scale_p->x = _clutter_util_vertex_length (&row[0]); + _clutter_util_vertex_normalize (&row[0]); + + /* compute XY shear and make the second row orthogonal to the first */ + shear_p[XY_SHEAR] = _clutter_util_vertex_dot (&row[0], &row[1]); + _clutter_util_vertex_combine (&row[1], &row[0], + 1.0, -shear_p[XY_SHEAR], + &row[1]); + + /* compute the Y scale and normalize the second row */ + scale_p->y = _clutter_util_vertex_length (&row[1]); + _clutter_util_vertex_normalize (&row[1]); + shear_p[XY_SHEAR] /= scale_p->y; + + /* compute XZ and YZ shears, orthogonalize the third row */ + shear_p[XZ_SHEAR] = _clutter_util_vertex_dot (&row[0], &row[2]); + _clutter_util_vertex_combine (&row[2], &row[0], + 1.0, -shear_p[XZ_SHEAR], + &row[2]); + + shear_p[YZ_SHEAR] = _clutter_util_vertex_dot (&row[1], &row[2]); + _clutter_util_vertex_combine (&row[2], &row[1], + 1.0, -shear_p[YZ_SHEAR], + &row[2]); + + /* get the Z scale and normalize the third row*/ + scale_p->z = _clutter_util_vertex_length (&row[2]); + _clutter_util_vertex_normalize (&row[2]); + shear_p[XZ_SHEAR] /= scale_p->z; + shear_p[YZ_SHEAR] /= scale_p->z; + + /* at this point, the matrix (inside row[]) is orthonormal. + * check for a coordinate system flip; if the determinant + * is -1, then negate the matrix and scaling factors + */ + _clutter_util_vertex_cross (&row[1], &row[2], &pdum); + if (_clutter_util_vertex_dot (&row[0], &pdum) < 0.f) + { + scale_p->x *= -1.f; + + for (i = 0; i < 3; i++) + { + row[i].x *= -1.f; + row[i].y *= -1.f; + row[i].z *= -1.f; + } + } + + /* now get the rotations out */ + rotate_p->y = asinf (-row[0].z); + if (cosf (rotate_p->y) != 0.f) + { + rotate_p->x = atan2f (row[1].z, row[2].z); + rotate_p->z = atan2f (row[0].y, row[0].x); + } + else + { + rotate_p->x = atan2f (-row[2].x, row[1].y); + rotate_p->z = 0.f; + } + +#undef XY_SHEAR +#undef XZ_SHEAR +#undef YZ_SHEAR +#undef MAT + + return TRUE; +} + typedef struct { GType value_type;