Bug 945 - Clipping+fbo cloning bugs
authorNeil Roberts <neil@openedhand.com>
Fri, 1 Aug 2008 12:23:57 +0000 (12:23 +0000)
committerNeil Roberts <neil@openedhand.com>
Fri, 1 Aug 2008 12:23:57 +0000 (12:23 +0000)
* clutter/cogl/gl/cogl.c:
* clutter/cogl/gles/cogl.c:
* clutter/cogl/cogl.h.in: Add cogl_clip_stack_save,
cogl_clip_stack_restore, cogl_viewport and cogl_frustum.

* clutter/cogl/gl/cogl-fbo.h:
* clutter/cogl/gl/cogl-fbo.c: Try to attach a stencil buffer when
creating an FBO.

* clutter/cogl/common/cogl-clip-stack.c: Add functions to save and
restore the whole state of the stack.

* clutter/clutter-texture.c (clutter_texture_paint): When
rendering the FBO source, setup a temporary asymmetric perspective
projection matrix to render it as it would appear on screen.

* clutter/clutter-private.h:
* clutter/clutter-actor.c
(_clutter_actor_apply_modelview_transform_recursive): No longer
static and exported in clutter-private.h

15 files changed:
ChangeLog
clutter/clutter-actor.c
clutter/clutter-private.h
clutter/clutter-texture.c
clutter/cogl/cogl.h.in
clutter/cogl/common/cogl-clip-stack.c
clutter/cogl/common/cogl-clip-stack.h
clutter/cogl/gl/cogl-context.h
clutter/cogl/gl/cogl-fbo.c
clutter/cogl/gl/cogl-fbo.h
clutter/cogl/gl/cogl.c
clutter/cogl/gles/cogl-gles2-wrapper.c
clutter/cogl/gles/cogl-gles2-wrapper.h
clutter/cogl/gles/cogl.c
doc/reference/cogl/cogl-sections.txt

index 43437b7..88067c0 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,30 @@
 2008-08-01  Neil Roberts  <neil@o-hand.com>
 
+       Bug 945 - Clipping+fbo cloning bugs
+
+       * clutter/cogl/gl/cogl.c: 
+       * clutter/cogl/gles/cogl.c: 
+       * clutter/cogl/cogl.h.in: Add cogl_clip_stack_save,
+       cogl_clip_stack_restore, cogl_viewport and cogl_frustum.
+
+       * clutter/cogl/gl/cogl-fbo.h: 
+       * clutter/cogl/gl/cogl-fbo.c: Try to attach a stencil buffer when
+       creating an FBO.
+
+       * clutter/cogl/common/cogl-clip-stack.c: Add functions to save and
+       restore the whole state of the stack.
+
+       * clutter/clutter-texture.c (clutter_texture_paint): When
+       rendering the FBO source, setup a temporary asymmetric perspective
+       projection matrix to render it as it would appear on screen.
+
+       * clutter/clutter-private.h: 
+       * clutter/clutter-actor.c
+       (_clutter_actor_apply_modelview_transform_recursive): No longer
+       static and exported in clutter-private.h
+
+2008-08-01  Neil Roberts  <neil@o-hand.com>
+
        Bug 1071 - clutter_timeline_get_duration doesn't always work
 
        * clutter/clutter-timeline.c: Calculate the 'duration' property
index 5e9c01b..a1710a7 100644 (file)
@@ -362,8 +362,6 @@ static guint actor_signals[LAST_SIGNAL] = { 0, };
 static void clutter_scriptable_iface_init (ClutterScriptableIface *iface);
 
 static void _clutter_actor_apply_modelview_transform           (ClutterActor *self);
-static void _clutter_actor_apply_modelview_transform_recursive (ClutterActor *self,
-                                                                ClutterActor *ancestor);
 
 static void clutter_actor_shader_pre_paint  (ClutterActor *actor,
                                              gboolean      repeat);
@@ -1373,7 +1371,7 @@ _clutter_actor_apply_modelview_transform (ClutterActor *self)
  * This function does not push/pop matrix; it is the responsibility
  * of the caller to do so as appropriate
  */
-static void
+void
 _clutter_actor_apply_modelview_transform_recursive (ClutterActor *self,
                                                    ClutterActor *ancestor)
 {
index 2d20f80..9d902d6 100644 (file)
@@ -210,6 +210,9 @@ gboolean _clutter_boolean_handled_accumulator (GSignalInvocationHint *ihint,
                                                const GValue          *handler_return,
                                                gpointer               dummy);
 
+void _clutter_actor_apply_modelview_transform_recursive (ClutterActor *self,
+                                                      ClutterActor *ancestor);
+
 G_END_DECLS
 
 #endif /* _HAVE_CLUTTER_PRIVATE_H */
index 4d63d87..adc7bb6 100644 (file)
@@ -431,6 +431,62 @@ clutter_texture_allocate (ClutterActor          *self,
 }
 
 static void
+clutter_texture_set_fbo_projection (ClutterActor *self)
+{
+  ClutterTexturePrivate *priv = CLUTTER_TEXTURE (self)->priv;
+  ClutterVertex verts[4];
+  ClutterFixed viewport[4];
+  ClutterFixed  x_min, x_max, y_min, y_max;
+  ClutterPerspective perspective;
+  ClutterStage *stage;
+  ClutterFixed tan_angle, near_size;
+  int i;
+
+  /* Get the bounding rectangle of the source as drawn in screen
+     coordinates */
+  clutter_actor_get_abs_allocation_vertices (priv->fbo_source, verts);
+
+  x_min = x_max = verts[0].x;
+  y_min = y_max = verts[0].y;
+
+  for (i = 1; i < G_N_ELEMENTS (verts); ++i)
+    {
+      if (verts[i].x < x_min)
+       x_min = verts[i].x;
+
+      if (verts[i].x > x_max)
+       x_max = verts[i].x;
+
+      if (verts[i].y < y_min)
+       y_min = verts[i].y;
+
+      if (verts[i].y > y_max)
+       y_max = verts[i].y;
+    }
+
+  stage = CLUTTER_STAGE (clutter_actor_get_stage (self));
+  clutter_stage_get_perspectivex (stage, &perspective);
+
+  /* Convert the coordinates back to [-1,1] range */
+  cogl_get_viewport (viewport);
+  x_min = CFX_QDIV (x_min, viewport[2]) * 2 - CFX_ONE;
+  x_max = CFX_QDIV (x_max, viewport[2]) * 2 - CFX_ONE;
+  y_min = CFX_QDIV (y_min, viewport[3]) * 2 - CFX_ONE;
+  y_max = CFX_QDIV (y_max, viewport[3]) * 2 - CFX_ONE;
+
+  /* Set up a projection matrix so that the actor will be projected as
+     if it was drawn at its original location */
+  tan_angle = clutter_tani (CLUTTER_ANGLE_FROM_DEGX (perspective.fovy / 2));
+  near_size = CFX_QMUL (perspective.z_near, tan_angle);
+
+  cogl_frustum (CFX_QMUL (x_min, near_size),
+               CFX_QMUL (x_max, near_size),
+               CFX_QMUL (-y_min, near_size),
+               CFX_QMUL (-y_max, near_size),
+               perspective.z_near, perspective.z_far);
+}
+
+static void
 clutter_texture_paint (ClutterActor *self)
 {
   ClutterTexture *texture = CLUTTER_TEXTURE (self);
@@ -447,6 +503,8 @@ clutter_texture_paint (ClutterActor *self)
     {
       ClutterMainContext *context;
       ClutterShader      *shader = NULL;
+      ClutterActor       *stage = NULL;
+      ClutterPerspective  perspective;
 
       context = clutter_context_get_default ();
 
@@ -462,15 +520,52 @@ clutter_texture_paint (ClutterActor *self)
       /* Redirect drawing to the fbo */
       cogl_draw_buffer (COGL_OFFSCREEN_BUFFER, priv->fbo_handle);
 
+      if ((stage = clutter_actor_get_stage (self)))
+       {
+         guint               stage_width, stage_height;
+         ClutterActor       *source_parent;
+
+         clutter_stage_get_perspectivex (CLUTTER_STAGE (stage), &perspective);
+         clutter_actor_get_size (stage, &stage_width, &stage_height);
+
+         /* Use below to set the modelview matrix as if the viewport
+            was still the same size as the stage */
+         cogl_setup_viewport (stage_width, stage_height,
+                              perspective.fovy,
+                              perspective.aspect,
+                              perspective.z_near,
+                              perspective.z_far);
+         /* Use a projection matrix that makes the actor appear as it
+            would if it was rendered at its normal screen location */
+         clutter_texture_set_fbo_projection (self);
+         /* Reset the viewport to the size of the FBO */
+         cogl_viewport (priv->width, priv->height);
+         /* Reapply the source's parent transformations */
+         if ((source_parent = clutter_actor_get_parent (priv->fbo_source)))
+           _clutter_actor_apply_modelview_transform_recursive (source_parent,
+                                                               NULL);
+       }
+
       /* cogl_paint_init is called to clear the buffers */
       cogl_paint_init (&transparent_col);
 
+      /* Clear the clipping stack so that if the FBO actor is being
+        clipped then it won't affect drawing the source */
+      cogl_clip_stack_save ();
+
       /* Render out actor scene to fbo */
       clutter_actor_paint (priv->fbo_source);
 
+      cogl_clip_stack_restore ();
+
       /* Restore drawing to the frame buffer */
       cogl_draw_buffer (COGL_WINDOW_BUFFER, COGL_INVALID_HANDLE);
 
+      /* Restore the perspective matrix using cogl_perspective so that
+        the inverse matrix will be right */
+      cogl_perspective (perspective.fovy, perspective.aspect,
+                       perspective.z_near, perspective.z_far);
+
       /* If there is a shader on top of the shader stack, turn it back on. */
       if (shader)
         clutter_shader_set_is_enabled (shader, TRUE);
@@ -1600,13 +1695,13 @@ on_fbo_source_size_change (GObject          *object,
       priv->width        = w;
       priv->height       = h;
 
-      priv->texture = cogl_texture_new_with_size (priv->width,
-                                                 priv->height,
+      priv->texture = cogl_texture_new_with_size (MAX (priv->width, 1),
+                                                 MAX (priv->height, 1),
                                                  -1,
                           priv->filter_quality == CLUTTER_TEXTURE_QUALITY_HIGH,
                                                  COGL_PIXEL_FORMAT_RGBA_8888);
 
-      cogl_texture_set_filters (priv->texture,   
+      cogl_texture_set_filters (priv->texture,
             clutter_texture_quality_to_cogl_min_filter (priv->filter_quality),
             clutter_texture_quality_to_cogl_mag_filter (priv->filter_quality));
 
@@ -1673,6 +1768,21 @@ on_fbo_parent_change (ClutterActor        *actor,
  *     adding it to a container.</para>
  *   </listitem>
  *   <listitem>
+ *     <para>When getting the image for the clone texture, Clutter
+ *     will attempt to render the source actor exactly as it would
+ *     appear if it was rendered on screen. The source actor's parent
+ *     transformations are taken into account. Therefore if your
+ *     source actor is rotated along the X or Y axes so that it has
+ *     some depth, the texture will appear differently depending on
+ *     the on-screen location of the source actor. While painting the
+ *     source actor, Clutter will set up a temporary asymmetric
+ *     perspective matrix as the projection matrix so that the source
+ *     actor will be projected as if a small section of the screen was
+ *     being viewed. Before version 0.8.2, an orthogonal identity
+ *     projection was used which meant that the source actor would be
+ *     clipped if any part of it was not on the zero Z-plane.</para>
+ *   </listitem>
+ *   <listitem>
  *     <para>Avoid reparenting the source with the created texture.</para>
  *   </listitem>
  *   <listitem>
index 8653805..814ca33 100644 (file)
@@ -338,8 +338,8 @@ void            cogl_get_bitmasks             (gint               *red,
  * @z_near: Nearest visible point
  * @z_far: Furthest visible point along the z-axis
  *
- * Multiplies the current set matrix with a projection matrix based
- * on the provided values.
+ * Replaces the current projection matrix with a perspective matrix
+ * based on the provided values.
  */
 void            cogl_perspective              (ClutterFixed        fovy,
                                                ClutterFixed        aspect,
@@ -347,6 +347,27 @@ void            cogl_perspective              (ClutterFixed        fovy,
                                                ClutterFixed        z_far);
 
 /**
+ * cogl_frustum:
+ * @left: Left clipping plane
+ * @right: Right clipping plane
+ * @bottom: Bottom clipping plane
+ * @top: Top clipping plane
+ * @z_near: Nearest visible point
+ * @z_far: Furthest visible point along the z-axis
+ *
+ * Replaces the current projection matrix with a perspective matrix
+ * for the given viewing frustum.
+ *
+ * Since: 0.8.2
+ */
+void            cogl_frustum                  (ClutterFixed        left,
+                                              ClutterFixed        right,
+                                              ClutterFixed        bottom,
+                                              ClutterFixed        top,
+                                               ClutterFixed        z_near,
+                                               ClutterFixed        z_far);
+
+/**
  * cogl_setup_viewport:
  * @width: Width of the viewport
  * @height: Height of the viewport
@@ -370,6 +391,18 @@ void            cogl_setup_viewport           (guint               width,
                                                ClutterFixed        z_far);
 
 /**
+ * cogl_viewport:
+ * @width: Width of the viewport
+ * @height: Height of the viewport
+ *
+ * Replace the current viewport with the given values.
+ *
+ * Since: 0.8.2
+ */
+void            cogl_viewport                 (guint               width,
+                                              guint               height);
+
+/**
  * cogl_push_matrix:
  *
  * Store the current model-view matrix on the matrix stack. The matrix
@@ -512,6 +545,29 @@ void            cogl_clip_set                 (ClutterFixed        x_offset,
 void            cogl_clip_unset               (void);
 
 /**
+ * cogl_clip_stack_save:
+ *
+ * Save the entire state of the clipping stack and then clear all
+ * clipping. The previous state can be returned to with
+ * cogl_clip_stack_restore(). Each call to cogl_clip_set() after this
+ * must be matched by a call to cogl_clip_unset() before calling
+ * cogl_clip_stack_restore().
+ *
+ * Since: 0.8.2
+ */
+void            cogl_clip_stack_save          (void);
+
+/**
+ * cogl_clip_stack_restore:
+ *
+ * Restore the state of the clipping stack that was previously saved
+ * by cogl_clip_stack_save().
+ *
+ * Since: 0.8.2
+ */
+void            cogl_clip_stack_restore       (void);
+
+/**
  * cogl_enable_depth_test:
  * @setting: %TRUE to enable depth testing or %FALSE to disable.
  *
index b34dd97..c24068c 100644 (file)
@@ -50,6 +50,12 @@ typedef struct _CoglClipStackEntry CoglClipStackEntry;
 
 struct _CoglClipStackEntry
 {
+  /* If this is set then this entry clears the clip stack. This is
+     used to clear the stack when drawing an FBO put to keep the
+     entries so they can be restored when the FBO drawing is
+     completed */
+  gboolean            clear;
+
   /* The rectangle for this clip */
   ClutterFixed        x_offset;
   ClutterFixed        y_offset;
@@ -61,7 +67,6 @@ struct _CoglClipStackEntry
 };
 
 static GList *cogl_clip_stack_top = NULL;
-static GList *cogl_clip_stack_bottom = NULL;
 static int    cogl_clip_stack_depth = 0;
 
 static void
@@ -93,6 +98,7 @@ cogl_clip_set (ClutterFixed x_offset,
   CoglClipStackEntry *entry = g_slice_new (CoglClipStackEntry);
 
   /* Make a new entry */
+  entry->clear = FALSE;
   entry->x_offset = x_offset;
   entry->y_offset = y_offset;
   entry->width = width;
@@ -105,8 +111,6 @@ cogl_clip_set (ClutterFixed x_offset,
 
   /* Store it in the stack */
   cogl_clip_stack_top = g_list_prepend (cogl_clip_stack_top, entry);
-  if (cogl_clip_stack_bottom == NULL)
-    cogl_clip_stack_bottom = cogl_clip_stack_top;
 }
 
 void
@@ -118,8 +122,6 @@ cogl_clip_unset (void)
   g_slice_free (CoglClipStackEntry, cogl_clip_stack_top->data);
   cogl_clip_stack_top = g_list_delete_link (cogl_clip_stack_top,
                                            cogl_clip_stack_top);
-  if (cogl_clip_stack_top == NULL)
-    cogl_clip_stack_bottom = NULL;
   cogl_clip_stack_depth--;
 
   /* Rebuild the clip */
@@ -131,7 +133,7 @@ _cogl_clip_stack_rebuild (gboolean just_stencil)
 {
   int has_clip_planes = cogl_features_available (COGL_FEATURE_FOUR_CLIP_PLANES);
   GList *node;
-  int depth = 1;
+  int depth = 0;
 
   /* Disable clip planes if the stack is empty */
   if (has_clip_planes && cogl_clip_stack_depth < 1)
@@ -141,8 +143,14 @@ _cogl_clip_stack_rebuild (gboolean just_stencil)
   if (cogl_clip_stack_depth < (has_clip_planes ? 2 : 1))
     _cogl_disable_stencil_buffer ();
 
+  /* Find the bottom of the stack */
+  for (node = cogl_clip_stack_top; depth < cogl_clip_stack_depth - 1;
+       node = node->next)
+    depth++;
+
   /* Re-add every entry from the bottom of the stack up */
-  for (node = cogl_clip_stack_bottom; node; node = node->prev, depth++)
+  depth = 1;
+  for (; depth <= cogl_clip_stack_depth; node = node->prev, depth++)
     if (!just_stencil || !has_clip_planes || depth > 1)
       {
        const CoglClipStackEntry *entry = (CoglClipStackEntry *) node->data;
@@ -156,12 +164,16 @@ _cogl_clip_stack_rebuild (gboolean just_stencil)
 void
 _cogl_clip_stack_merge (void)
 {
-  GList *node = cogl_clip_stack_bottom;
+  GList *node = cogl_clip_stack_top;
+  int i;
 
   /* Merge the current clip stack on top of whatever is in the stencil
      buffer */
-  if (node)
+  if (cogl_clip_stack_depth)
     {
+      for (i = 0; i < cogl_clip_stack_depth - 1; i++)
+       node = node->next;
+
       /* Skip the first entry if we have clipping planes */
       if (cogl_features_available (COGL_FEATURE_FOUR_CLIP_PLANES))
        node = node->prev;
@@ -178,3 +190,43 @@ _cogl_clip_stack_merge (void)
        }
     }
 }
+
+void
+cogl_clip_stack_save (void)
+{
+  CoglClipStackEntry *entry = g_slice_new (CoglClipStackEntry);
+
+  /* Push an entry into the stack to mark that it should be cleared */
+  entry->clear = TRUE;
+  cogl_clip_stack_top = g_list_prepend (cogl_clip_stack_top, entry);
+
+  /* Reset the depth to zero */
+  cogl_clip_stack_depth = 0;
+
+  /* Rebuilding the stack will now disabling all clipping */
+  _cogl_clip_stack_rebuild (FALSE);
+}
+
+void
+cogl_clip_stack_restore (void)
+{
+  GList *node;
+
+  /* The top of the stack should be a clear marker */
+  g_assert (cogl_clip_stack_top);
+  g_assert (((CoglClipStackEntry *) cogl_clip_stack_top->data)->clear);
+
+  /* Remove the top entry */
+  g_slice_free (CoglClipStackEntry, cogl_clip_stack_top->data);
+  cogl_clip_stack_top = g_list_delete_link (cogl_clip_stack_top,
+                                           cogl_clip_stack_top);
+
+  /* Recalculate the depth of the stack */
+  cogl_clip_stack_depth = 0;
+  for (node = cogl_clip_stack_top;
+       node && !((CoglClipStackEntry *) node->data)->clear;
+       node = node->next)
+    cogl_clip_stack_depth++;
+
+  _cogl_clip_stack_rebuild (FALSE);
+}
index 3f31581..04be889 100644 (file)
 #ifndef __COGL_CLIP_STACK_H
 #define __COGL_CLIP_STACK_H
 
-void cogl_clip_set (ClutterFixed x_offset,
-                   ClutterFixed y_offset,
-                   ClutterFixed width,
-                   ClutterFixed height);
-void cogl_clip_unset (void);
 void _cogl_clip_stack_rebuild (gboolean just_stencil);
 void _cogl_clip_stack_merge (void);
 
index 694147c..96b3199 100644 (file)
@@ -68,6 +68,7 @@ typedef struct
   
   /* Relying on glext.h to define these */
   COGL_PFNGLGENRENDERBUFFERSEXTPROC                pf_glGenRenderbuffersEXT;
+  COGL_PFNGLDELETERENDERBUFFERSEXTPROC             pf_glDeleteRenderbuffersEXT;
   COGL_PFNGLBINDRENDERBUFFEREXTPROC                pf_glBindRenderbufferEXT;
   COGL_PFNGLRENDERBUFFERSTORAGEEXTPROC             pf_glRenderbufferStorageEXT;
   COGL_PFNGLGENFRAMEBUFFERSEXTPROC                 pf_glGenFramebuffersEXT;
index 88fd86d..d0aa137 100644 (file)
@@ -37,6 +37,7 @@
 
 /* Expecting EXT functions not to be defined - redirect to pointers in context  */
 #define glGenRenderbuffersEXT                ctx->pf_glGenRenderbuffersEXT
+#define glDeleteRenderbuffersEXT             ctx->pf_glDeleteRenderbuffersEXT
 #define glBindRenderbufferEXT                ctx->pf_glBindRenderbufferEXT
 #define glRenderbufferStorageEXT             ctx->pf_glRenderbufferStorageEXT
 #define glGenFramebuffersEXT                 ctx->pf_glGenFramebuffersEXT
@@ -68,6 +69,7 @@ cogl_offscreen_new_to_texture (CoglHandle texhandle)
   CoglTexSliceSpan *y_span;
   GLuint            tex_gl_handle;
   GLuint            fbo_gl_handle;
+  GLuint            gl_stencil_handle;
   GLenum            status;
   
   _COGL_GET_CONTEXT (ctx, COGL_INVALID_HANDLE);
@@ -92,21 +94,47 @@ cogl_offscreen_new_to_texture (CoglHandle texhandle)
   x_span = &g_array_index (tex->slice_x_spans, CoglTexSliceSpan, 0);
   y_span = &g_array_index (tex->slice_y_spans, CoglTexSliceSpan, 0);
   tex_gl_handle = g_array_index (tex->slice_gl_handles, GLuint, 0);
-  
+
+  /* Create a renderbuffer for stenciling */
+  GE( glGenRenderbuffersEXT (1, &gl_stencil_handle) );
+  GE( glBindRenderbufferEXT (GL_RENDERBUFFER_EXT, gl_stencil_handle) );
+  GE( glRenderbufferStorageEXT (GL_RENDERBUFFER_EXT, GL_STENCIL_INDEX8_EXT,
+                               cogl_texture_get_width (texhandle),
+                               cogl_texture_get_height (texhandle)) );
+  GE( glBindRenderbufferEXT (GL_RENDERBUFFER_EXT, 0) );
+
   /* Generate framebuffer */
   glGenFramebuffersEXT (1, &fbo_gl_handle);
   GE( glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, fbo_gl_handle) );
   GE( glFramebufferTexture2DEXT (GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
                                 tex->gl_target, tex_gl_handle, 0) );
+  GE( glFramebufferRenderbufferEXT (GL_FRAMEBUFFER_EXT,
+                                   GL_STENCIL_ATTACHMENT_EXT,
+                                   GL_RENDERBUFFER_EXT, gl_stencil_handle) );
   
   /* Make sure it's complete */
   status = glCheckFramebufferStatusEXT (GL_FRAMEBUFFER_EXT);
   
   if (status != GL_FRAMEBUFFER_COMPLETE_EXT)
     {
-      GE( glDeleteFramebuffersEXT (1, &fbo_gl_handle) );
-      GE( glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0) );
-      return COGL_INVALID_HANDLE;
+      /* Stencil renderbuffers aren't always supported. Try again
+        without the stencil buffer */
+      GE( glFramebufferRenderbufferEXT (GL_FRAMEBUFFER_EXT,
+                                       GL_STENCIL_ATTACHMENT_EXT,
+                                       GL_RENDERBUFFER_EXT,
+                                       0) );
+      GE( glDeleteRenderbuffersEXT (1, &gl_stencil_handle) );
+      gl_stencil_handle = 0;
+      
+      status = glCheckFramebufferStatusEXT (GL_FRAMEBUFFER_EXT);
+  
+      if (status != GL_FRAMEBUFFER_COMPLETE_EXT)
+       {
+         /* Still failing, so give up */
+         GE( glDeleteFramebuffersEXT (1, &fbo_gl_handle) );
+         GE( glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0) );
+         return COGL_INVALID_HANDLE;
+       }
     }
   
   GE( glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0) );
@@ -114,10 +142,11 @@ cogl_offscreen_new_to_texture (CoglHandle texhandle)
   /* Allocate and init a CoglFbo object (store non-wasted size
      for subsequent blits and viewport setup) */
   fbo = (CoglFbo*) g_malloc (sizeof (CoglFbo));
-  fbo->ref_count = 1;
-  fbo->width     = x_span->size - x_span->waste;
-  fbo->height    = y_span->size - y_span->waste;
-  fbo->gl_handle = fbo_gl_handle;
+  fbo->ref_count         = 1;
+  fbo->width             = x_span->size - x_span->waste;
+  fbo->height            = y_span->size - y_span->waste;
+  fbo->gl_handle         = fbo_gl_handle;
+  fbo->gl_stencil_handle = gl_stencil_handle;
 
   COGL_HANDLE_DEBUG_NEW (offscreen, fbo);
   
@@ -140,7 +169,8 @@ _cogl_offscreen_free (CoglFbo *fbo)
 
   /* Frees FBO resources but its handle is not
      released! Do that separately before this! */
-
+  if (fbo->gl_stencil_handle)
+    GE( glDeleteRenderbuffersEXT (1, &fbo->gl_stencil_handle) );
   GE( glDeleteFramebuffersEXT (1, &fbo->gl_handle) );
   g_free (fbo);
 }
index f2dd529..f0efef7 100644 (file)
@@ -32,6 +32,7 @@ typedef struct
   int    width;
   int    height;
   GLuint gl_handle;
+  GLuint gl_stencil_handle;
   
 } CoglFbo;
 
index f79d231..6906927 100644 (file)
@@ -746,6 +746,9 @@ cogl_perspective (ClutterFixed fovy,
 
   memset (&m[0], 0, sizeof (m));
 
+  GE( glMatrixMode (GL_PROJECTION) );
+  GE( glLoadIdentity () );
+
   /*
    * Based on the original algorithm in perspective():
    *
@@ -773,6 +776,8 @@ cogl_perspective (ClutterFixed fovy,
 
   GE( glMultMatrixf (m) );
 
+  GE( glMatrixMode (GL_MODELVIEW) );
+
   /* Calculate and store the inverse of the matrix */
   memset (ctx->inverse_projection, 0, sizeof (GLfloat) * 16);
 
@@ -787,6 +792,60 @@ cogl_perspective (ClutterFixed fovy,
 }
 
 void
+cogl_frustum (ClutterFixed        left,
+             ClutterFixed        right,
+             ClutterFixed        bottom,
+             ClutterFixed        top,
+             ClutterFixed        z_near,
+             ClutterFixed        z_far)
+{
+  GLfloat c, d;
+
+  _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+  GE( glMatrixMode (GL_PROJECTION) );
+  GE( glLoadIdentity () );
+
+  GE( glFrustum (CLUTTER_FIXED_TO_DOUBLE (left),
+                CLUTTER_FIXED_TO_DOUBLE (right),
+                CLUTTER_FIXED_TO_DOUBLE (bottom),
+                CLUTTER_FIXED_TO_DOUBLE (top),
+                CLUTTER_FIXED_TO_DOUBLE (z_near),
+                CLUTTER_FIXED_TO_DOUBLE (z_far)) );
+
+  GE( glMatrixMode (GL_MODELVIEW) );
+
+  /* Calculate and store the inverse of the matrix */
+  memset (ctx->inverse_projection, 0, sizeof (GLfloat) * 16);
+
+  c = -CLUTTER_FIXED_TO_FLOAT (z_far + z_near)
+    / CLUTTER_FIXED_TO_FLOAT (z_far - z_near);
+  d = -CLUTTER_FIXED_TO_FLOAT (2 * CFX_QMUL (z_far, z_near))
+    / CLUTTER_FIXED_TO_FLOAT (z_far - z_near);
+
+#define M(row,col)  ctx->inverse_projection[col*4+row]
+  M(0,0) = CLUTTER_FIXED_TO_FLOAT (right - left)
+    / CLUTTER_FIXED_TO_FLOAT (2 * z_near);
+  M(0,3) = CLUTTER_FIXED_TO_FLOAT (right + left)
+    / CLUTTER_FIXED_TO_FLOAT (2 * z_near);
+  M(1,1) = CLUTTER_FIXED_TO_FLOAT (top - bottom)
+    / CLUTTER_FIXED_TO_FLOAT (2 * z_near);
+  M(1,3) = CLUTTER_FIXED_TO_FLOAT (top + bottom)
+    / CLUTTER_FIXED_TO_FLOAT (2 * z_near);
+  M(2,3) = -1.0f;
+  M(3,2) = 1.0f / d;
+  M(3,3) = c / d;
+#undef M
+}
+
+void
+cogl_viewport (guint width,
+              guint height)
+{
+  GE( glViewport (0, 0, width, height) );
+}
+
+void
 cogl_setup_viewport (guint        width,
                     guint        height,
                     ClutterFixed fovy,
@@ -798,12 +857,8 @@ cogl_setup_viewport (guint        width,
 
   GE( glViewport (0, 0, width, height) );
 
-  GE( glMatrixMode (GL_PROJECTION) );
-  GE( glLoadIdentity () );
-
   cogl_perspective (fovy, aspect, z_near, z_far);
 
-  GE( glMatrixMode (GL_MODELVIEW) );
   GE( glLoadIdentity () );
 
   /*
@@ -969,6 +1024,10 @@ _cogl_features_init ()
        (COGL_PFNGLGENRENDERBUFFERSEXTPROC)
        cogl_get_proc_address ("glGenRenderbuffersEXT");
       
+      ctx->pf_glDeleteRenderbuffersEXT =
+       (COGL_PFNGLDELETERENDERBUFFERSEXTPROC)
+       cogl_get_proc_address ("glDeleteRenderbuffersEXT");
+      
       ctx->pf_glBindRenderbufferEXT =
        (COGL_PFNGLBINDRENDERBUFFEREXTPROC)
        cogl_get_proc_address ("glBindRenderbufferEXT");
index 394ab8e..2bdcaf2 100644 (file)
@@ -667,6 +667,31 @@ cogl_wrap_glMultMatrixx (const GLfixed *m)
 }
 
 void
+cogl_wrap_glFrustumx (GLfixed left, GLfixed right,
+                     GLfixed bottom, GLfixed top,
+                     GLfixed z_near, GLfixed z_far)
+{
+  float matrix[16];
+  float two_near = CLUTTER_FIXED_TO_FLOAT (2 * z_near);
+
+  memset (matrix, 0, sizeof (matrix));
+
+  matrix[0] = two_near / CLUTTER_FIXED_TO_FLOAT (right - left);
+  matrix[5] = two_near / CLUTTER_FIXED_TO_FLOAT (top - bottom);
+  matrix[8] = CLUTTER_FIXED_TO_FLOAT (right + left)
+    / CLUTTER_FIXED_TO_FLOAT (right - left);
+  matrix[9] = CLUTTER_FIXED_TO_FLOAT (top + bottom)
+    / CLUTTER_FIXED_TO_FLOAT (top - bottom);
+  matrix[10] = -CLUTTER_FIXED_TO_FLOAT (z_far + z_near)
+    / CLUTTER_FIXED_TO_FLOAT (z_far - z_near);
+  matrix[11] = -1.0f;
+  matrix[14] = -two_near * CLUTTER_FIXED_TO_FLOAT (z_far)
+    / CLUTTER_FIXED_TO_FLOAT (z_far - z_near);
+
+  cogl_wrap_glMultMatrix (matrix);
+}
+
+void
 cogl_wrap_glScalex (GLfixed x, GLfixed y, GLfixed z)
 {
   float matrix[16];
index 650bbed..9531a48 100644 (file)
@@ -204,6 +204,9 @@ void cogl_wrap_glPopMatrix ();
 void cogl_wrap_glMatrixMode (GLenum mode);
 void cogl_wrap_glLoadIdentity ();
 void cogl_wrap_glMultMatrixx (const GLfixed *m);
+void cogl_wrap_glFrustumx (GLfixed left, GLfixed right,
+                          GLfixed bottom, GLfixed top,
+                          GLfixed z_near, GLfixed z_far);
 void cogl_wrap_glScalex (GLfixed x, GLfixed y, GLfixed z);
 void cogl_wrap_glTranslatex (GLfixed x, GLfixed y, GLfixed z);
 void cogl_wrap_glRotatex (GLfixed angle, GLfixed x, GLfixed y, GLfixed z);
@@ -268,6 +271,7 @@ void _cogl_gles2_clear_cache_for_program (CoglHandle program);
 #define cogl_wrap_glMatrixMode         glMatrixMode
 #define cogl_wrap_glLoadIdentity       glLoadIdentity
 #define cogl_wrap_glMultMatrixx        glMultMatrixx
+#define cogl_wrap_glFrustumx           glFrustumx
 #define cogl_wrap_glScalex             glScalex
 #define cogl_wrap_glTranslatex         glTranslatex
 #define cogl_wrap_glRotatex            glRotatex
index a4ee269..95ca10e 100644 (file)
@@ -661,7 +661,10 @@ cogl_perspective (ClutterFixed fovy,
 
   memset (&m[0], 0, sizeof (m));
 
-  /*
+  GE( cogl_wrap_glMatrixMode (GL_PROJECTION) );
+  GE( cogl_wrap_glLoadIdentity () );
+
+ /*
    * Based on the original algorithm in perspective():
    * 
    * 1) xmin = -xmax => xmax + xmin == 0 && xmax - xmin == 2 * xmax
@@ -688,6 +691,8 @@ cogl_perspective (ClutterFixed fovy,
 
   GE( cogl_wrap_glMultMatrixx (m) );
 
+  GE( cogl_wrap_glMatrixMode (GL_MODELVIEW) );
+
   /* Calculate and store the inverse of the matrix */
   memset (ctx->inverse_projection, 0, sizeof (ClutterFixed) * 16);
 
@@ -703,6 +708,51 @@ cogl_perspective (ClutterFixed fovy,
 }
 
 void
+cogl_frustum (ClutterFixed        left,
+             ClutterFixed        right,
+             ClutterFixed        bottom,
+             ClutterFixed        top,
+             ClutterFixed        z_near,
+             ClutterFixed        z_far)
+{
+  ClutterFixed c, d;
+
+  _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+  GE( cogl_wrap_glMatrixMode (GL_PROJECTION) );
+  GE( cogl_wrap_glLoadIdentity () );
+
+  GE( cogl_wrap_glFrustumx (left, right,
+                           bottom, top,
+                           z_near, z_far) );
+
+  GE( cogl_wrap_glMatrixMode (GL_MODELVIEW) );
+
+  /* Calculate and store the inverse of the matrix */
+  memset (ctx->inverse_projection, 0, sizeof (ClutterFixed) * 16);
+
+  c = -CFX_QDIV (z_far + z_near, z_far - z_near);
+  d = -CFX_QDIV (2 * CFX_QMUL (z_far, z_near), z_far - z_near);
+
+#define M(row,col)  ctx->inverse_projection[col*4+row]
+  M(0,0) = CFX_QDIV (right - left, 2 * z_near);
+  M(0,3) = CFX_QDIV (right + left, 2 * z_near);
+  M(1,1) = CFX_QDIV (top - bottom, 2 * z_near);
+  M(1,3) = CFX_QDIV (top + bottom, 2 * z_near);
+  M(2,3) = -CFX_ONE;
+  M(3,2) = CFX_QDIV (CFX_ONE, d);
+  M(3,3) = CFX_QDIV (c, d);
+#undef M  
+}
+
+void
+cogl_viewport (guint width,
+              guint height)
+{
+  GE( glViewport (0, 0, width, height) );
+}
+
+void
 cogl_setup_viewport (guint        w,
                     guint        h,
                     ClutterFixed fovy,
@@ -715,8 +765,6 @@ cogl_setup_viewport (guint        w,
   ClutterFixed z_camera;
   
   GE( glViewport (0, 0, width, height) );
-  GE( cogl_wrap_glMatrixMode (GL_PROJECTION) );
-  GE( cogl_wrap_glLoadIdentity () );
 
   /* For Ortho projection.
    * cogl_wrap_glOrthox (0, width << 16, 0,  height << 16,  -1 << 16, 1 << 16);
@@ -724,7 +772,6 @@ cogl_setup_viewport (guint        w,
 
   cogl_perspective (fovy, aspect, z_near, z_far);
   
-  GE( cogl_wrap_glMatrixMode (GL_MODELVIEW) );
   GE( cogl_wrap_glLoadIdentity () );
 
   /*
index d95398a..ed873ce 100644 (file)
@@ -13,7 +13,9 @@ CoglPixelFormat
 CoglBufferTarget
 <SUBSECTION>
 cogl_perspective
+cogl_frustum
 cogl_setup_viewport
+cogl_viewport
 cogl_get_modelview_matrix
 cogl_get_projection_matrix
 cogl_get_viewport
@@ -28,6 +30,8 @@ cogl_rotate
 <SUBSECTION>
 cogl_clip_set
 cogl_clip_unset
+cogl_clip_stack_save
+cogl_clip_stack_restore
 <SUBSECTION>
 cogl_enable_depth_test
 cogl_alpha_func