Applied 'final patch' from bug #874
authorNeil Roberts <neil@openedhand.com>
Mon, 2 Jun 2008 12:34:10 +0000 (12:34 +0000)
committerNeil Roberts <neil@openedhand.com>
Mon, 2 Jun 2008 12:34:10 +0000 (12:34 +0000)
* 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
clutter/clutter-fixed.c
clutter/clutter-fixed.h
clutter/cogl/gl/cogl-context.h
clutter/cogl/gl/cogl.c
clutter/cogl/gles/cogl-context.h
clutter/cogl/gles/cogl.c
tests/Makefile.am
tests/test-clip.c [new file with mode: 0644]

index 63c11dd..beeb31f 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,29 @@
 2008-06-02  Neil Roberts  <neil@o-hand.com>
 
+       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  <neil@o-hand.com>
+
        * clutter/cogl/gles/cogl-gles2-wrapper.h: The uniform numbers are
        now stored in a separate struct so they can be stored for
        application program objects as well.
index 599e81a..4558c53 100644 (file)
@@ -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,
index a6e7d88..5382ed0 100644 (file)
@@ -147,6 +147,12 @@ typedef gint32 ClutterAngle;    /* angle such that 1024 == 2*PI */
  */
 #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:
  *
  * Fixed point representation of the number 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 */
 
 /**
index 26c090a..3280a6a 100644 (file)
@@ -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;
index b97d5be..013c81e 100644 (file)
@@ -31,6 +31,7 @@
 
 #include <string.h>
 #include <gmodule.h>
+#include <math.h>
 
 #ifdef HAVE_CLUTTER_GLX
 #include <dlfcn.h>
@@ -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) };
-
-      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 modelview[16], projection[16];
+
+      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
 }
 
index 4103620..1f7b3aa 100644 (file)
@@ -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;
index 6d07003..ffedc7e 100644 (file)
@@ -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 };
-
-      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) );
+      GLfixed modelview[16], projection[16];
+
+      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
 }
 
index 8d2c8b6..f70be79 100644 (file)
@@ -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 (file)
index 0000000..90981f0
--- /dev/null
@@ -0,0 +1,128 @@
+#include <clutter/clutter.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+
+#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;
+}