From e306b0135cd9fc4e5b03e1f6a89199c68e60bfbd 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 --- gl/cogl-context.h | 3 + gl/cogl.c | 137 +++++++++++++++++++++++++++++++++++++++----- gles/cogl-context.h | 3 + gles/cogl.c | 130 +++++++++++++++++++++++++++++++++++++---- 4 files changed, 246 insertions(+), 27 deletions(-) diff --git a/gl/cogl-context.h b/gl/cogl-context.h index 26c090ac5..3280a6ac9 100644 --- a/gl/cogl-context.h +++ b/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/gl/cogl.c b/gl/cogl.c index b97d5be90..013c81e91 100644 --- a/gl/cogl.c +++ b/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/gles/cogl-context.h b/gles/cogl-context.h index 4103620d5..1f7b3aac1 100644 --- a/gles/cogl-context.h +++ b/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/gles/cogl.c b/gles/cogl.c index 6d070036b..ffedc7e7a 100644 --- a/gles/cogl.c +++ b/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 }