From db4c5e28293946745e86120e3970e9313d4acc2b Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Fri, 1 Aug 2008 12:23:57 +0000 Subject: [PATCH] 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 --- ChangeLog | 25 ++++++ clutter/clutter-actor.c | 4 +- clutter/clutter-private.h | 3 + clutter/clutter-texture.c | 116 ++++++++++++++++++++++++- clutter/cogl/cogl.h.in | 60 ++++++++++++- clutter/cogl/common/cogl-clip-stack.c | 70 +++++++++++++-- clutter/cogl/common/cogl-clip-stack.h | 5 -- clutter/cogl/gl/cogl-context.h | 1 + clutter/cogl/gl/cogl-fbo.c | 48 ++++++++-- clutter/cogl/gl/cogl-fbo.h | 1 + clutter/cogl/gl/cogl.c | 67 +++++++++++++- clutter/cogl/gles/cogl-gles2-wrapper.c | 25 ++++++ clutter/cogl/gles/cogl-gles2-wrapper.h | 4 + clutter/cogl/gles/cogl.c | 55 +++++++++++- doc/reference/cogl/cogl-sections.txt | 4 + 15 files changed, 449 insertions(+), 39 deletions(-) diff --git a/ChangeLog b/ChangeLog index 43437b756..88067c0f4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,28 @@ +2008-08-01 Neil Roberts + + 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 Bug 1071 - clutter_timeline_get_duration doesn't always work diff --git a/clutter/clutter-actor.c b/clutter/clutter-actor.c index 5e9c01b7c..a1710a706 100644 --- a/clutter/clutter-actor.c +++ b/clutter/clutter-actor.c @@ -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) { diff --git a/clutter/clutter-private.h b/clutter/clutter-private.h index 2d20f80ef..9d902d6b6 100644 --- a/clutter/clutter-private.h +++ b/clutter/clutter-private.h @@ -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 */ diff --git a/clutter/clutter-texture.c b/clutter/clutter-texture.c index 4d63d87f9..adc7bb616 100644 --- a/clutter/clutter-texture.c +++ b/clutter/clutter-texture.c @@ -430,6 +430,62 @@ clutter_texture_allocate (ClutterActor *self, clutter_actor_allocate_preferred_size (priv->fbo_source, origin_changed); } +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) { @@ -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. * * + * 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. + * + * * Avoid reparenting the source with the created texture. * * diff --git a/clutter/cogl/cogl.h.in b/clutter/cogl/cogl.h.in index 8653805c2..814ca33c0 100644 --- a/clutter/cogl/cogl.h.in +++ b/clutter/cogl/cogl.h.in @@ -338,14 +338,35 @@ 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, ClutterFixed z_near, 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 @@ -369,6 +390,18 @@ void cogl_setup_viewport (guint width, ClutterFixed z_near, 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: * @@ -511,6 +544,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. diff --git a/clutter/cogl/common/cogl-clip-stack.c b/clutter/cogl/common/cogl-clip-stack.c index b34dd976a..c24068c4c 100644 --- a/clutter/cogl/common/cogl-clip-stack.c +++ b/clutter/cogl/common/cogl-clip-stack.c @@ -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); +} diff --git a/clutter/cogl/common/cogl-clip-stack.h b/clutter/cogl/common/cogl-clip-stack.h index 3f3158198..04be889ae 100644 --- a/clutter/cogl/common/cogl-clip-stack.h +++ b/clutter/cogl/common/cogl-clip-stack.h @@ -26,11 +26,6 @@ #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); diff --git a/clutter/cogl/gl/cogl-context.h b/clutter/cogl/gl/cogl-context.h index 694147c23..96b3199df 100644 --- a/clutter/cogl/gl/cogl-context.h +++ b/clutter/cogl/gl/cogl-context.h @@ -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; diff --git a/clutter/cogl/gl/cogl-fbo.c b/clutter/cogl/gl/cogl-fbo.c index 88fd86db0..d0aa13745 100644 --- a/clutter/cogl/gl/cogl-fbo.c +++ b/clutter/cogl/gl/cogl-fbo.c @@ -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); } diff --git a/clutter/cogl/gl/cogl-fbo.h b/clutter/cogl/gl/cogl-fbo.h index f2dd5298f..f0efef79f 100644 --- a/clutter/cogl/gl/cogl-fbo.h +++ b/clutter/cogl/gl/cogl-fbo.h @@ -32,6 +32,7 @@ typedef struct int width; int height; GLuint gl_handle; + GLuint gl_stencil_handle; } CoglFbo; diff --git a/clutter/cogl/gl/cogl.c b/clutter/cogl/gl/cogl.c index f79d231ca..690692765 100644 --- a/clutter/cogl/gl/cogl.c +++ b/clutter/cogl/gl/cogl.c @@ -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); @@ -786,6 +791,60 @@ cogl_perspective (ClutterFixed fovy, #undef M } +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, @@ -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"); diff --git a/clutter/cogl/gles/cogl-gles2-wrapper.c b/clutter/cogl/gles/cogl-gles2-wrapper.c index 394ab8e3d..2bdcaf2af 100644 --- a/clutter/cogl/gles/cogl-gles2-wrapper.c +++ b/clutter/cogl/gles/cogl-gles2-wrapper.c @@ -666,6 +666,31 @@ cogl_wrap_glMultMatrixx (const GLfixed *m) cogl_wrap_glMultMatrix (new_matrix); } +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) { diff --git a/clutter/cogl/gles/cogl-gles2-wrapper.h b/clutter/cogl/gles/cogl-gles2-wrapper.h index 650bbedd0..9531a486e 100644 --- a/clutter/cogl/gles/cogl-gles2-wrapper.h +++ b/clutter/cogl/gles/cogl-gles2-wrapper.h @@ -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 diff --git a/clutter/cogl/gles/cogl.c b/clutter/cogl/gles/cogl.c index a4ee269c3..95ca10eef 100644 --- a/clutter/cogl/gles/cogl.c +++ b/clutter/cogl/gles/cogl.c @@ -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); @@ -702,6 +707,51 @@ cogl_perspective (ClutterFixed fovy, #undef M } +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, @@ -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 () ); /* diff --git a/doc/reference/cogl/cogl-sections.txt b/doc/reference/cogl/cogl-sections.txt index d95398a65..ed873ce32 100644 --- a/doc/reference/cogl/cogl-sections.txt +++ b/doc/reference/cogl/cogl-sections.txt @@ -13,7 +13,9 @@ CoglPixelFormat CoglBufferTarget 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 cogl_clip_set cogl_clip_unset +cogl_clip_stack_save +cogl_clip_stack_restore cogl_enable_depth_test cogl_alpha_func