From 8bdb98736dcbefabc14a50f0adaea74db328c443 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Mon, 2 Jun 2008 12:34:10 +0000 Subject: [PATCH] Applied 'final patch' from bug #874 * clutter/cogl/gles/cogl.c: * clutter/cogl/gl/cogl.c: The clip planes are now set using the inverse projection matrix as the modelview matrix so that they can be specified in screen coordinates. * clutter/cogl/gles/cogl-context.h (CoglContext): * clutter/cogl/gl/cogl-context.h (CoglContext): Added a member to cache the inverse projection matrix * clutter/clutter-fixed.h: Added a constant for converting from radians to degrees. * clutter/clutter-fixed.c (clutter_atani, clutter_atan2i): Added fixed-point versions of atan and atan2. * tests/test-clip.c: Added a test for clipping with various rotations and depths. * tests/Makefile.am (noinst_PROGRAMS): Added test-clip --- ChangeLog | 24 ++++++ clutter/clutter-fixed.c | 98 ++++++++++++++++++++++ clutter/clutter-fixed.h | 9 ++ clutter/cogl/gl/cogl-context.h | 3 + clutter/cogl/gl/cogl.c | 137 +++++++++++++++++++++++++++---- clutter/cogl/gles/cogl-context.h | 3 + clutter/cogl/gles/cogl.c | 130 ++++++++++++++++++++++++++--- tests/Makefile.am | 2 +- tests/test-clip.c | 128 +++++++++++++++++++++++++++++ 9 files changed, 506 insertions(+), 28 deletions(-) create mode 100644 tests/test-clip.c diff --git a/ChangeLog b/ChangeLog index 63c11dd73..beeb31f60 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,27 @@ +2008-06-02 Neil Roberts + + Applied 'final patch' from bug #874 + + * clutter/cogl/gles/cogl.c: + * clutter/cogl/gl/cogl.c: The clip planes are now set using the + inverse projection matrix as the modelview matrix so that they can + be specified in screen coordinates. + + * clutter/cogl/gles/cogl-context.h (CoglContext): + * clutter/cogl/gl/cogl-context.h (CoglContext): Added a member to + cache the inverse projection matrix + + * clutter/clutter-fixed.h: Added a constant for converting from + radians to degrees. + + * clutter/clutter-fixed.c (clutter_atani, clutter_atan2i): Added + fixed-point versions of atan and atan2. + + * tests/test-clip.c: Added a test for clipping with various + rotations and depths. + + * tests/Makefile.am (noinst_PROGRAMS): Added test-clip + 2008-06-02 Neil Roberts * clutter/cogl/gles/cogl-gles2-wrapper.h: The uniform numbers are diff --git a/clutter/clutter-fixed.c b/clutter/clutter-fixed.c index 599e81a7f..4558c534a 100644 --- a/clutter/clutter-fixed.c +++ b/clutter/clutter-fixed.c @@ -505,6 +505,104 @@ clutter_tani (ClutterAngle angle) return result; } +/* 257-value table of atan. atan_tbl[0] is atan(0.0) and atan_tbl[256] + is atan(1). The angles are radians in ClutterFixed + truncated to 16-bit (they're all less than one) */ +static guint16 atan_tbl[] = + { + 0x0000, 0x00FF, 0x01FF, 0x02FF, 0x03FF, 0x04FF, 0x05FF, 0x06FF, + 0x07FF, 0x08FF, 0x09FE, 0x0AFE, 0x0BFD, 0x0CFD, 0x0DFC, 0x0EFB, + 0x0FFA, 0x10F9, 0x11F8, 0x12F7, 0x13F5, 0x14F3, 0x15F2, 0x16F0, + 0x17EE, 0x18EB, 0x19E9, 0x1AE6, 0x1BE3, 0x1CE0, 0x1DDD, 0x1ED9, + 0x1FD5, 0x20D1, 0x21CD, 0x22C8, 0x23C3, 0x24BE, 0x25B9, 0x26B3, + 0x27AD, 0x28A7, 0x29A1, 0x2A9A, 0x2B93, 0x2C8B, 0x2D83, 0x2E7B, + 0x2F72, 0x306A, 0x3160, 0x3257, 0x334D, 0x3442, 0x3538, 0x362D, + 0x3721, 0x3815, 0x3909, 0x39FC, 0x3AEF, 0x3BE2, 0x3CD4, 0x3DC5, + 0x3EB6, 0x3FA7, 0x4097, 0x4187, 0x4277, 0x4365, 0x4454, 0x4542, + 0x462F, 0x471C, 0x4809, 0x48F5, 0x49E0, 0x4ACB, 0x4BB6, 0x4CA0, + 0x4D89, 0x4E72, 0x4F5B, 0x5043, 0x512A, 0x5211, 0x52F7, 0x53DD, + 0x54C2, 0x55A7, 0x568B, 0x576F, 0x5852, 0x5934, 0x5A16, 0x5AF7, + 0x5BD8, 0x5CB8, 0x5D98, 0x5E77, 0x5F55, 0x6033, 0x6110, 0x61ED, + 0x62C9, 0x63A4, 0x647F, 0x6559, 0x6633, 0x670C, 0x67E4, 0x68BC, + 0x6993, 0x6A6A, 0x6B40, 0x6C15, 0x6CEA, 0x6DBE, 0x6E91, 0x6F64, + 0x7036, 0x7108, 0x71D9, 0x72A9, 0x7379, 0x7448, 0x7516, 0x75E4, + 0x76B1, 0x777E, 0x7849, 0x7915, 0x79DF, 0x7AA9, 0x7B72, 0x7C3B, + 0x7D03, 0x7DCA, 0x7E91, 0x7F57, 0x801C, 0x80E1, 0x81A5, 0x8269, + 0x832B, 0x83EE, 0x84AF, 0x8570, 0x8630, 0x86F0, 0x87AF, 0x886D, + 0x892A, 0x89E7, 0x8AA4, 0x8B5F, 0x8C1A, 0x8CD5, 0x8D8E, 0x8E47, + 0x8F00, 0x8FB8, 0x906F, 0x9125, 0x91DB, 0x9290, 0x9345, 0x93F9, + 0x94AC, 0x955F, 0x9611, 0x96C2, 0x9773, 0x9823, 0x98D2, 0x9981, + 0x9A2F, 0x9ADD, 0x9B89, 0x9C36, 0x9CE1, 0x9D8C, 0x9E37, 0x9EE0, + 0x9F89, 0xA032, 0xA0DA, 0xA181, 0xA228, 0xA2CE, 0xA373, 0xA418, + 0xA4BC, 0xA560, 0xA602, 0xA6A5, 0xA746, 0xA7E8, 0xA888, 0xA928, + 0xA9C7, 0xAA66, 0xAB04, 0xABA1, 0xAC3E, 0xACDB, 0xAD76, 0xAE11, + 0xAEAC, 0xAF46, 0xAFDF, 0xB078, 0xB110, 0xB1A7, 0xB23E, 0xB2D5, + 0xB36B, 0xB400, 0xB495, 0xB529, 0xB5BC, 0xB64F, 0xB6E2, 0xB773, + 0xB805, 0xB895, 0xB926, 0xB9B5, 0xBA44, 0xBAD3, 0xBB61, 0xBBEE, + 0xBC7B, 0xBD07, 0xBD93, 0xBE1E, 0xBEA9, 0xBF33, 0xBFBC, 0xC046, + 0xC0CE, 0xC156, 0xC1DD, 0xC264, 0xC2EB, 0xC371, 0xC3F6, 0xC47B, + 0xC4FF, 0xC583, 0xC606, 0xC689, 0xC70B, 0xC78D, 0xC80E, 0xC88F, + 0xC90F + }; + +/** + * clutter_atani: + * @x: The tangent to calculate the angle for + * + * Fast fixed-point version of the arctangent function. + * + * Return value: The angle in radians represented as a #ClutterFixed + * for which the tangent is @x. + */ +ClutterFixed +clutter_atani (ClutterFixed x) +{ + gboolean negative = FALSE; + ClutterFixed angle; + + if (x < 0) + { + negative = TRUE; + x = -x; + } + + if (x > CFX_ONE) + /* if x > 1 then atan(x) = pi/2 - atan(1/x) */ + angle = CFX_PI / 2 - atan_tbl[CFX_QDIV (CFX_ONE, x) >> 8]; + else + angle = atan_tbl[x >> 8]; + + return negative ? -angle : angle; +} + +/** + * clutter_atan2i: + * @y: Numerator of tangent + * @x: Denominator of tangent + * + * Calculates the arctangent of @y / @x but uses the sign of both + * arguments to return the angle in right quadrant. + * + * Return value: The arctangent of @y / @x + */ +ClutterFixed +clutter_atan2i (ClutterFixed y, ClutterFixed x) +{ + ClutterFixed angle; + + if (x == 0) + angle = y >= 0 ? CFX_PI_2 : -CFX_PI_2; + else + { + angle = clutter_atani (CFX_QDIV (y, x)); + + if (x < 0) + angle += y >= 0 ? CFX_PI : -CFX_PI; + } + + return angle; +} + ClutterFixed sqrt_tbl [] = { 0x00000000L, 0x00010000L, 0x00016A0AL, 0x0001BB68L, diff --git a/clutter/clutter-fixed.h b/clutter/clutter-fixed.h index a6e7d88b6..5382ed07d 100644 --- a/clutter/clutter-fixed.h +++ b/clutter/clutter-fixed.h @@ -146,6 +146,12 @@ typedef gint32 ClutterAngle; /* angle such that 1024 == 2*PI */ * Fixed point representation of the number 60 */ #define CFX_60 CLUTTER_INT_TO_FIXED (60) +/** + * CFX_RADIANS_TO_DEGREES: + * + * Fixed point representation of the number 180 / pi + */ +#define CFX_RADIANS_TO_DEGREES 0x394bb8 /** * CFX_255: * @@ -291,6 +297,9 @@ ClutterFixed clutter_sini (ClutterAngle angle); ClutterFixed clutter_tani (ClutterAngle angle); +ClutterFixed clutter_atani (ClutterFixed x); +ClutterFixed clutter_atan2i (ClutterFixed y, ClutterFixed x); + /* convenience macros for the cos functions */ /** diff --git a/clutter/cogl/gl/cogl-context.h b/clutter/cogl/gl/cogl-context.h index 26c090ac5..3280a6ac9 100644 --- a/clutter/cogl/gl/cogl-context.h +++ b/clutter/cogl/gl/cogl-context.h @@ -48,6 +48,9 @@ typedef struct guint path_nodes_size; CoglFixedVec2 path_nodes_min; CoglFixedVec2 path_nodes_max; + + /* Cache of inverse projection matrix */ + GLfloat inverse_projection[16]; /* Textures */ GArray *texture_handles; diff --git a/clutter/cogl/gl/cogl.c b/clutter/cogl/gl/cogl.c index b97d5be90..013c81e91 100644 --- a/clutter/cogl/gl/cogl.c +++ b/clutter/cogl/gl/cogl.c @@ -31,6 +31,7 @@ #include #include +#include #ifdef HAVE_CLUTTER_GLX #include @@ -385,6 +386,71 @@ cogl_color (const ClutterColor *color) ctx->color_alpha = color->alpha; } +static void +apply_matrix (const GLfloat *matrix, GLfloat *vertex) +{ + int x, y; + GLfloat vertex_out[4] = { 0 }; + + for (y = 0; y < 4; y++) + for (x = 0; x < 4; x++) + vertex_out[y] += vertex[x] * matrix[y + x * 4]; + + memcpy (vertex, vertex_out, sizeof (vertex_out)); +} + +static void +project_vertex (GLfloat *modelview, GLfloat *project, GLfloat *vertex) +{ + int i; + + /* Apply the modelview matrix */ + apply_matrix (modelview, vertex); + /* Apply the projection matrix */ + apply_matrix (project, vertex); + /* Convert from homogenized coordinates */ + for (i = 0; i < 4; i++) + vertex[i] /= vertex[3]; +} + +static void +set_clip_plane (GLint plane_num, + const GLfloat *vertex_a, + const GLfloat *vertex_b) +{ + GLdouble plane[4]; + GLfloat angle; + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + /* Calculate the angle between the axes and the line crossing the + two points */ + angle = atan2f ((vertex_b[1] - vertex_a[1]), + (vertex_b[0] - vertex_a[0])) * 180.0f / M_PI; + + GE( glPushMatrix () ); + /* Load the identity matrix and multiply by the reverse of the + projection matrix so we can specify the plane in screen + coordinates */ + GE( glLoadIdentity () ); + GE( glMultMatrixf (ctx->inverse_projection) ); + /* Rotate about point a */ + GE( glTranslatef (vertex_a[0], vertex_a[1], vertex_a[2]) ); + /* Rotate the plane by the calculated angle so that it will connect + the two points */ + GE( glRotatef (angle, 0.0f, 0.0f, 1.0f) ); + GE( glTranslatef (-vertex_a[0], -vertex_a[1], -vertex_a[2]) ); + + plane[0] = 0.0f; + plane[1] = -1.0f; + plane[2] = 0.0f; + plane[3] = vertex_a[1]; + GE( glClipPlane (plane_num, plane) ); + + GE( glPopMatrix () ); + + GE( glEnable (plane_num) ); +} + void cogl_clip_set (ClutterFixed x_offset, ClutterFixed y_offset, @@ -393,22 +459,50 @@ cogl_clip_set (ClutterFixed x_offset, { if (cogl_features_available (COGL_FEATURE_FOUR_CLIP_PLANES)) { - GLdouble eqn_left[4] = { 1.0, 0, 0, - -CLUTTER_FIXED_TO_FLOAT (x_offset) }; - GLdouble eqn_right[4] = { -1.0, 0, 0, - CLUTTER_FIXED_TO_FLOAT (x_offset + width) }; - GLdouble eqn_top[4] = { 0, 1.0, 0, -CLUTTER_FIXED_TO_FLOAT (y_offset) }; - GLdouble eqn_bottom[4] = { 0, -1.0, 0, CLUTTER_FIXED_TO_FLOAT - (y_offset + height) }; + GLfloat modelview[16], projection[16]; - GE( glClipPlane (GL_CLIP_PLANE0, eqn_left) ); - GE( glClipPlane (GL_CLIP_PLANE1, eqn_right) ); - GE( glClipPlane (GL_CLIP_PLANE2, eqn_top) ); - GE( glClipPlane (GL_CLIP_PLANE3, eqn_bottom) ); - GE( glEnable (GL_CLIP_PLANE0) ); - GE( glEnable (GL_CLIP_PLANE1) ); - GE( glEnable (GL_CLIP_PLANE2) ); - GE( glEnable (GL_CLIP_PLANE3) ); + GLfloat vertex_tl[4] = { CLUTTER_FIXED_TO_FLOAT (x_offset), + CLUTTER_FIXED_TO_FLOAT (y_offset), + 0.0f, 1.0f }; + GLfloat vertex_tr[4] = { CLUTTER_FIXED_TO_FLOAT (x_offset + width), + CLUTTER_FIXED_TO_FLOAT (y_offset), + 0.0f, 1.0f }; + GLfloat vertex_bl[4] = { CLUTTER_FIXED_TO_FLOAT (x_offset), + CLUTTER_FIXED_TO_FLOAT (y_offset + height), + 0.0f, 1.0f }; + GLfloat vertex_br[4] = { CLUTTER_FIXED_TO_FLOAT (x_offset + width), + CLUTTER_FIXED_TO_FLOAT (y_offset + height), + 0.0f, 1.0f }; + + GE( glGetFloatv (GL_MODELVIEW_MATRIX, modelview) ); + GE( glGetFloatv (GL_PROJECTION_MATRIX, projection) ); + + project_vertex (modelview, projection, vertex_tl); + project_vertex (modelview, projection, vertex_tr); + project_vertex (modelview, projection, vertex_bl); + project_vertex (modelview, projection, vertex_br); + + /* If the order of the top and bottom lines is different from + the order of the left and right lines then the clip rect must + have been transformed so that the back is visible. We + therefore need to swap one pair of vertices otherwise all of + the planes will be the wrong way around */ + if ((vertex_tl[0] < vertex_tr[0] ? 1 : 0) + != (vertex_bl[1] < vertex_tl[1] ? 1 : 0)) + { + GLfloat temp[4]; + memcpy (temp, vertex_tl, sizeof (temp)); + memcpy (vertex_tl, vertex_tr, sizeof (temp)); + memcpy (vertex_tr, temp, sizeof (temp)); + memcpy (temp, vertex_bl, sizeof (temp)); + memcpy (vertex_bl, vertex_br, sizeof (temp)); + memcpy (vertex_br, temp, sizeof (temp)); + } + + set_clip_plane (GL_CLIP_PLANE0, vertex_tl, vertex_tr); + set_clip_plane (GL_CLIP_PLANE1, vertex_tr, vertex_br); + set_clip_plane (GL_CLIP_PLANE2, vertex_br, vertex_bl); + set_clip_plane (GL_CLIP_PLANE3, vertex_bl, vertex_tl); } else if (cogl_features_available (COGL_FEATURE_STENCIL_BUFFER)) { @@ -467,6 +561,8 @@ cogl_perspective (ClutterFixed fovy, GLfloat m[16]; + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + memset (&m[0], 0, sizeof (m)); /* @@ -495,6 +591,17 @@ cogl_perspective (ClutterFixed fovy, M(3,2) = -1.0F; GE( glMultMatrixf (m) ); + + /* Calculate and store the inverse of the matrix */ + memset (ctx->inverse_projection, 0, sizeof (GLfloat) * 16); + +#define m ctx->inverse_projection + M(0, 0) = 1.0f / CLUTTER_FIXED_TO_FLOAT (x); + M(1, 1) = 1.0f / CLUTTER_FIXED_TO_FLOAT (y); + M(2, 3) = -1.0f; + M(3, 2) = 1.0f / CLUTTER_FIXED_TO_FLOAT (d); + M(3, 3) = CLUTTER_FIXED_TO_FLOAT (c) / CLUTTER_FIXED_TO_FLOAT (d); +#undef m #undef M } diff --git a/clutter/cogl/gles/cogl-context.h b/clutter/cogl/gles/cogl-context.h index 4103620d5..1f7b3aac1 100644 --- a/clutter/cogl/gles/cogl-context.h +++ b/clutter/cogl/gles/cogl-context.h @@ -58,6 +58,9 @@ typedef struct CoglFixedVec2 path_nodes_min; CoglFixedVec2 path_nodes_max; + /* Cache of inverse projection matrix */ + ClutterFixed inverse_projection[16]; + /* Textures */ GArray *texture_handles; CoglTextureGLVertex *texture_vertices; diff --git a/clutter/cogl/gles/cogl.c b/clutter/cogl/gles/cogl.c index 6d070036b..ffedc7e7a 100644 --- a/clutter/cogl/gles/cogl.c +++ b/clutter/cogl/gles/cogl.c @@ -312,6 +312,74 @@ cogl_color (const ClutterColor *color) ctx->color_alpha = color->alpha; } +static void +apply_matrix (const ClutterFixed *matrix, ClutterFixed *vertex) +{ + int x, y; + ClutterFixed vertex_out[4] = { 0 }; + + for (y = 0; y < 4; y++) + for (x = 0; x < 4; x++) + vertex_out[y] += CFX_QMUL (vertex[x], matrix[y + x * 4]); + + memcpy (vertex, vertex_out, sizeof (vertex_out)); +} + +static void +project_vertex (ClutterFixed *modelview, + ClutterFixed *project, + ClutterFixed *vertex) +{ + int i; + + /* Apply the modelview matrix */ + apply_matrix (modelview, vertex); + /* Apply the projection matrix */ + apply_matrix (project, vertex); + /* Convert from homogenized coordinates */ + for (i = 0; i < 4; i++) + vertex[i] = CFX_QDIV (vertex[i], vertex[3]); +} + +static void +set_clip_plane (GLint plane_num, + const ClutterFixed *vertex_a, + const ClutterFixed *vertex_b) +{ + GLfixed plane[4]; + GLfixed angle; + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + /* Calculate the angle between the axes and the line crossing the + two points */ + angle = CFX_QMUL (clutter_atan2i (vertex_b[1] - vertex_a[1], + vertex_b[0] - vertex_a[0]), + CFX_RADIANS_TO_DEGREES); + + GE( cogl_wrap_glPushMatrix () ); + /* Load the identity matrix and multiply by the reverse of the + projection matrix so we can specify the plane in screen + coordinates */ + GE( cogl_wrap_glLoadIdentity () ); + GE( cogl_wrap_glMultMatrixx ((GLfixed *) ctx->inverse_projection) ); + /* Rotate about point a */ + GE( cogl_wrap_glTranslatex (vertex_a[0], vertex_a[1], vertex_a[2]) ); + /* Rotate the plane by the calculated angle so that it will connect + the two points */ + GE( cogl_wrap_glRotatex (angle, 0.0f, 0.0f, 1.0f) ); + GE( cogl_wrap_glTranslatex (-vertex_a[0], -vertex_a[1], -vertex_a[2]) ); + + plane[0] = 0; + plane[1] = -CFX_ONE; + plane[2] = 0; + plane[3] = vertex_a[1]; + GE( cogl_wrap_glClipPlanex (plane_num, plane) ); + + GE( cogl_wrap_glPopMatrix () ); + + GE( cogl_wrap_glEnable (plane_num) ); +} + void cogl_clip_set (ClutterFixed x_offset, ClutterFixed y_offset, @@ -320,19 +388,43 @@ cogl_clip_set (ClutterFixed x_offset, { if (cogl_features_available (COGL_FEATURE_FOUR_CLIP_PLANES)) { - GLfixed eqn_left[4] = { CFX_ONE, 0, 0, -x_offset }; - GLfixed eqn_right[4] = { -CFX_ONE, 0, 0, x_offset + width }; - GLfixed eqn_top[4] = { 0, CFX_ONE, 0, -y_offset }; - GLfixed eqn_bottom[4] = { 0, -CFX_ONE, 0, y_offset + height }; + GLfixed modelview[16], projection[16]; - GE( cogl_wrap_glClipPlanex (GL_CLIP_PLANE0, eqn_left) ); - GE( cogl_wrap_glClipPlanex (GL_CLIP_PLANE1, eqn_right) ); - GE( cogl_wrap_glClipPlanex (GL_CLIP_PLANE2, eqn_top) ); - GE( cogl_wrap_glClipPlanex (GL_CLIP_PLANE3, eqn_bottom) ); - GE( cogl_wrap_glEnable (GL_CLIP_PLANE0) ); - GE( cogl_wrap_glEnable (GL_CLIP_PLANE1) ); - GE( cogl_wrap_glEnable (GL_CLIP_PLANE2) ); - GE( cogl_wrap_glEnable (GL_CLIP_PLANE3) ); + ClutterFixed vertex_tl[4] = { x_offset, y_offset, 0, CFX_ONE }; + ClutterFixed vertex_tr[4] = { x_offset + width, y_offset, 0, CFX_ONE }; + ClutterFixed vertex_bl[4] = { x_offset, y_offset + height, 0, CFX_ONE }; + ClutterFixed vertex_br[4] = { x_offset + width, y_offset + height, + 0, CFX_ONE }; + + GE( cogl_wrap_glGetFixedv (GL_MODELVIEW_MATRIX, modelview) ); + GE( cogl_wrap_glGetFixedv (GL_PROJECTION_MATRIX, projection) ); + + project_vertex (modelview, projection, vertex_tl); + project_vertex (modelview, projection, vertex_tr); + project_vertex (modelview, projection, vertex_bl); + project_vertex (modelview, projection, vertex_br); + + /* If the order of the top and bottom lines is different from + the order of the left and right lines then the clip rect must + have been transformed so that the back is visible. We + therefore need to swap one pair of vertices otherwise all of + the planes will be the wrong way around */ + if ((vertex_tl[0] < vertex_tr[0] ? 1 : 0) + != (vertex_bl[1] < vertex_tl[1] ? 1 : 0)) + { + ClutterFixed temp[4]; + memcpy (temp, vertex_tl, sizeof (temp)); + memcpy (vertex_tl, vertex_tr, sizeof (temp)); + memcpy (vertex_tr, temp, sizeof (temp)); + memcpy (temp, vertex_bl, sizeof (temp)); + memcpy (vertex_bl, vertex_br, sizeof (temp)); + memcpy (vertex_br, temp, sizeof (temp)); + } + + set_clip_plane (GL_CLIP_PLANE0, vertex_tl, vertex_tr); + set_clip_plane (GL_CLIP_PLANE1, vertex_tr, vertex_br); + set_clip_plane (GL_CLIP_PLANE2, vertex_br, vertex_bl); + set_clip_plane (GL_CLIP_PLANE3, vertex_bl, vertex_tl); } else if (cogl_features_available (COGL_FEATURE_STENCIL_BUFFER)) { @@ -391,6 +483,8 @@ cogl_perspective (ClutterFixed fovy, GLfixed m[16]; + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + memset (&m[0], 0, sizeof (m)); /* @@ -419,6 +513,18 @@ cogl_perspective (ClutterFixed fovy, M(3,2) = 1 + ~CFX_ONE; GE( cogl_wrap_glMultMatrixx (m) ); + + /* Calculate and store the inverse of the matrix */ + memset (ctx->inverse_projection, 0, sizeof (ClutterFixed) * 16); + +#define m ctx->inverse_projection + M(0, 0) = CFX_QDIV (CFX_ONE, x); + M(1, 1) = CFX_QDIV (CFX_ONE, y); + M(2, 3) = -CFX_ONE; + M(3, 2) = CFX_QDIV (CFX_ONE, d); + M(3, 3) = CFX_QDIV (c, d); +#undef m + #undef M } diff --git a/tests/Makefile.am b/tests/Makefile.am index 8d2c8b672..f70be79a5 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -12,7 +12,7 @@ noinst_PROGRAMS = test-textures test-events test-offscreen test-scale \ test-cogl-tex-convert test-cogl-tex-foreign \ test-cogl-tex-getset test-cogl-offscreen \ test-cogl-tex-polygon test-stage-read-pixels \ - test-random-text + test-random-text test-clip if X11_TESTS noinst_PROGRAMS += test-pixmap diff --git a/tests/test-clip.c b/tests/test-clip.c new file mode 100644 index 000000000..90981f003 --- /dev/null +++ b/tests/test-clip.c @@ -0,0 +1,128 @@ +#include +#include +#include +#include +#include + +#define TL_SCALE 5.0f + +typedef struct _CallbackData CallbackData; + +struct _CallbackData +{ + ClutterActor *stage, *group, *rect, *hand; +}; + +static void +on_new_frame (ClutterTimeline *tl, int frame_num, CallbackData *data) +{ + int i; + int stage_width = clutter_actor_get_width (data->stage); + int stage_height = clutter_actor_get_height (data->stage); + gdouble progress = clutter_timeline_get_progress (tl); + gdouble angle = progress * 2 * M_PI * TL_SCALE; + gdouble rotation[3]; + + gdouble xpos = stage_width * 0.45 * sin (angle) + stage_width / 8; + gdouble ypos = stage_height * 0.45 * sin (angle) + stage_height / 8; + gdouble zpos = stage_width * cos (angle) - stage_width / 2; + + clutter_actor_set_position (data->hand, xpos, ypos); + clutter_actor_set_depth (data->hand, zpos); + clutter_actor_set_rotation (data->hand, CLUTTER_Y_AXIS, + angle / M_PI * 180.0 * 3, + clutter_actor_get_width (data->hand) / 2, + clutter_actor_get_height (data->hand) / 2, + 0); + + memset (rotation, 0, sizeof (rotation)); + + if (progress < 1 / 3.0) + rotation[2] = 360 * progress * 3; + else if (progress < 2 / 3.0) + rotation[1] = 360 * progress * 3; + else + rotation[0] = 360 * progress * 3; + + for (i = 0; i < 3; i++) + { + clutter_actor_set_rotation (data->group, i, + rotation[i], + clutter_actor_get_width (data->rect) / 2, + clutter_actor_get_height (data->rect) / 2, + 0); + clutter_actor_set_rotation (data->rect, i, + rotation[i], + clutter_actor_get_width (data->rect) / 2, + clutter_actor_get_height (data->rect) / 2, + 0); + } +} + +int +main (int argc, char **argv) +{ + ClutterGeometry geom; + ClutterTimeline *tl; + ClutterColor blue = { 0x40, 0x40, 0xff, 0xff }; + CallbackData data; + ClutterActor *other_hand; + int x, y; + + clutter_init (&argc, &argv); + + data.stage = clutter_stage_get_default (); + + data.group = clutter_group_new (); + + clutter_actor_get_geometry (data.stage, &geom); + geom.x = geom.width / 4; + geom.y = geom.height / 4; + geom.width /= 2; + geom.height /= 2; + clutter_actor_set_geometry (data.group, &geom); + + data.rect = clutter_rectangle_new_with_color (&blue); + clutter_actor_set_geometry (data.rect, &geom); + clutter_container_add (CLUTTER_CONTAINER (data.stage), data.rect, NULL); + + clutter_container_add (CLUTTER_CONTAINER (data.stage), data.group, NULL); + + clutter_actor_set_clip (data.group, 0, 0, geom.width, geom.height); + + data.hand = clutter_texture_new_from_file ("redhand.png", NULL); + if (data.hand == NULL) + { + g_critical ("pixbuf loading failed"); + exit (1); + } + clutter_container_add (CLUTTER_CONTAINER (data.group), data.hand, NULL); + + /* Add a hand at each of the four corners of the group */ + for (y = 0; y < 2; y++) + for (x = 0; x < 2; x++) + { + other_hand = clutter_clone_texture_new (CLUTTER_TEXTURE (data.hand)); + clutter_actor_set_anchor_point_from_gravity + (other_hand, CLUTTER_GRAVITY_CENTER); + clutter_actor_set_position (other_hand, + x * geom.width, + y * geom.height); + clutter_container_add (CLUTTER_CONTAINER (data.group), + other_hand, NULL); + } + + clutter_actor_raise_top (data.hand); + + tl = clutter_timeline_new (360 * TL_SCALE, 60); + clutter_timeline_start (tl); + clutter_timeline_set_loop (tl, TRUE); + + g_signal_connect (tl, "new-frame", G_CALLBACK (on_new_frame), &data); + + clutter_actor_show (data.stage); + + clutter_main (); + + return 0; +}