2008-02-01 Matthew Allum <mallum@openedhand.com>
authorMatthew Allum <mallum@openedhand.com>
Fri, 1 Feb 2008 15:29:00 +0000 (15:29 +0000)
committerMatthew Allum <mallum@openedhand.com>
Fri, 1 Feb 2008 15:29:00 +0000 (15:29 +0000)
        * clutter/clutter-feature.h:
        * clutter/clutter-texture.c:
        * clutter/clutter-texture.h:
        * clutter/cogl/cogl.h:
        * clutter/cogl/gl/cogl.c:
        * clutter/cogl/gles/cogl.c:
        * tests/Makefile.am:
        * tests/test.fbo.c:
        Add initial support for FBO's in Clutter (OpenGL only so far).
        See new clutter_texture_new_from_actor()
        Initial implementation, needs work.

        * clutter/x11/clutter-stage-x11.c:
        (clutter_stage_x11_set_cursor_visible):
        Fall back to again not relying on xfixes to hide cursor. *sigh*

        * clutter/clutter-deprecated.h:
        Add clutter_group_find_child_by_id

ChangeLog
clutter/clutter-deprecated.h
clutter/clutter-feature.h
clutter/clutter-texture.c
clutter/clutter-texture.h
clutter/cogl/cogl.h
clutter/cogl/gl/cogl.c
clutter/cogl/gles/cogl.c
clutter/x11/clutter-stage-x11.c
tests/Makefile.am
tests/test-fbo.c [new file with mode: 0644]

index 4a04302..c0905d3 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,24 @@
+2008-02-01  Matthew Allum  <mallum@openedhand.com>
+
+       * clutter/clutter-feature.h:
+       * clutter/clutter-texture.c:
+       * clutter/clutter-texture.h:
+       * clutter/cogl/cogl.h:
+       * clutter/cogl/gl/cogl.c:
+       * clutter/cogl/gles/cogl.c:
+       * tests/Makefile.am:
+       * tests/test.fbo.c:
+       Add initial support for FBO's in Clutter (OpenGL only so far).
+       See new clutter_texture_new_from_actor()
+       Initial implementation, needs work.
+
+       * clutter/x11/clutter-stage-x11.c:
+       (clutter_stage_x11_set_cursor_visible):
+       Fall back to again not relying on xfixes to hide cursor. *sigh*
+
+       * clutter/clutter-deprecated.h:
+       Add clutter_group_find_child_by_id 
+       
 2008-02-01  Øyvind Kolås  <pippin@o-hand.com>
 
        * clutter/cogl/gles/cogl.c: (cogl_rectangle_internal): increase size
index e79ffc8..1c2b529 100644 (file)
@@ -11,6 +11,8 @@
  * new functionality should giver errors containing _DEPRECATED_BY_.
  */
 
+#define clutter_group_find_child_by_id               clutter_group_find_child_by_id_REPLACED_BY_clutter_container_find_child_by_name
+
 #define clutter_behaviour_ellipse_set_angle_begin    clutter_behaviour_ellipse_set_angle_begin_REPLACED_BY_clutter_behaviour_set_angle_start
 #define clutter_behaviour_ellipse_set_angle_beginx   clutter_behaviour_ellipse_set_angle_beginx_REPLACED_BY_clutter_behaviour_set_angle_startx
 #define clutter_behaviour_ellipse_get_angle_begin    clutter_behaviour_ellipse_get_angle_begin_REPLACED_BY_clutter_behaviour_get_angle_start
index 0fc013b..ec771ba 100644 (file)
@@ -47,6 +47,7 @@ G_BEGIN_DECLS
  * @CLUTTER_FEATURE_STAGE_USER_RESIZE: Set if stage is able to be user resized.
  * @CLUTTER_FEATURE_STAGE_CURSOR: Set if stage has a graphical cursor.
  * @CLUTTER_FEATURE_SHADERS_GLSL: Set if the backend supports GLSL shaders.
+ * @CLUTTER_FEATURE_OFFSCREEN: Set if the backend supports offscreen rendering.
  *
  * Runtime flags indicating specific features available via Clutter window
  * sysytem and graphics backend.
@@ -62,7 +63,8 @@ typedef enum
   CLUTTER_FEATURE_STAGE_STATIC           = (1 << 5),
   CLUTTER_FEATURE_STAGE_USER_RESIZE      = (1 << 6),
   CLUTTER_FEATURE_STAGE_CURSOR           = (1 << 7),
-  CLUTTER_FEATURE_SHADERS_GLSL           = (1 << 8)
+  CLUTTER_FEATURE_SHADERS_GLSL           = (1 << 8),
+  CLUTTER_FEATURE_OFFSCREEN              = (1 << 9)
 } ClutterFeatureFlags;
 
 gboolean            clutter_feature_available       (ClutterFeatureFlags feature);
index 8366c53..0df4041 100644 (file)
@@ -96,6 +96,9 @@ struct _ClutterTexturePrivate
   gint                         n_x_tiles;
   gint                         n_y_tiles;
   COGLuint                    *tiles;
+
+  ClutterActor                *fbo_source;
+  COGLuint                     fbo_handle;
 };
 
 enum
@@ -668,6 +671,9 @@ clutter_texture_realize (ClutterActor *actor)
   texture = CLUTTER_TEXTURE(actor);
   priv = texture->priv;
 
+  if (priv->fbo_handle)
+    return;   /* handle better */
+
   CLUTTER_MARK();
 
   if (priv->local_pixbuf != NULL)
@@ -751,6 +757,19 @@ clutter_texture_paint (ClutterActor *self)
       return;
     }
 
+  if (priv->fbo_handle)
+    {
+      cogl_offscreen_redirect_start (priv->fbo_handle, 
+                                    priv->width, priv->height);
+      clutter_actor_paint (priv->fbo_source);
+      cogl_offscreen_redirect_end (priv->fbo_handle,
+                                  CLUTTER_STAGE_WIDTH(),
+                                  CLUTTER_STAGE_HEIGHT());
+
+      glBindTexture(CGL_TEXTURE_RECTANGLE_ARB, priv->tiles[0]);
+    }
+
+
   CLUTTER_NOTE (PAINT,
                 "painting texture '%s'",
                clutter_actor_get_name (self) ? clutter_actor_get_name (self)
@@ -2083,3 +2102,90 @@ clutter_texture_set_area_from_rgb_data (ClutterTexture     *texture,
 
   return TRUE;
 }
+
+/**
+ * clutter_texture_new_from_actor:
+ * @actor: A #ClutterActor 
+ *
+ * Creates a new #ClutterTexture object with its source a prexisting 
+ * actor (and associated children).
+ *
+ * Note this function is intented as a utility call for uniformly applying
+ * shaders to groups and other potentail visual effects. It requires the
+ * #CLUTTER_FEATURE_TEXTURE_RECTANGLE & #CLUTTER_FEATURE_OFFSCREEN features
+ * are supported by both the current backend and target system
+ *
+ * Return value: A newly created #ClutterTexture object or NULL on fail.
+ **/
+ClutterActor *
+clutter_texture_new_from_actor (ClutterActor *actor)
+{
+  ClutterTexture        *texture;
+  ClutterTexturePrivate *priv;
+  guint                  w, h;
+
+
+  /*  TODO (before 0.6 release):
+   *
+   *   - Figure out getting source actor size correctly.
+   *   - Figure out refing/reparenting source actor. 
+   *   - Handle source actor resizing.
+   *   - Handle failure better.
+   *   - Handle cleanup on destruction.
+   *   - Beef up test-fbo.
+   *   - Have the source actor as a prop?
+   */
+
+  if (clutter_feature_available (CLUTTER_FEATURE_TEXTURE_RECTANGLE) == FALSE)
+    return NULL;
+
+  if (clutter_feature_available (CLUTTER_FEATURE_OFFSCREEN) == FALSE)
+    return NULL;
+
+  texture = g_object_new (CLUTTER_TYPE_TEXTURE, NULL);
+
+  priv = texture->priv;
+
+  priv->fbo_source = actor;
+
+  if (!CLUTTER_ACTOR_IS_REALIZED (priv->fbo_source))
+    clutter_actor_realize (priv->fbo_source);
+
+
+  /* FIXME: just ref ? */
+  clutter_actor_set_parent (actor, CLUTTER_ACTOR(texture));
+
+  /* FIXME abs size */
+  /*clutter_actor_get_abs_size (priv->fbo_source, &w, &h);*/
+  clutter_actor_get_size (actor, &w, &h);
+
+  /* FIXME: Check we can actually create a texture this large */
+  priv->width = w;
+  priv->height = h;
+
+  priv->target_type  = CGL_TEXTURE_RECTANGLE_ARB;
+  priv->pixel_format = CGL_RGBA;
+  priv->pixel_type = PIXEL_TYPE;
+  priv->is_tiled     = 0;
+
+  priv->tiles = g_new (COGLuint, 1);
+
+  /* FIXME: needs a cogl wrapper */
+  glGenTextures (1, priv->tiles);
+
+  cogl_texture_bind (priv->target_type, priv->tiles[0]);
+
+  cogl_texture_image_2d (priv->target_type,
+                        CGL_RGBA,
+                        w,
+                        h,
+                        priv->pixel_format,
+                        priv->pixel_type,
+                        NULL);
+
+  priv->fbo_handle = cogl_offscreen_create (priv->tiles[0]);
+
+  clutter_actor_set_size (actor, w, h);
+
+  return CLUTTER_ACTOR(texture);
+}
index 9ce9a07..8aeb749 100644 (file)
@@ -119,7 +119,7 @@ gboolean      clutter_texture_set_from_rgb_data   (ClutterTexture *texture,
                                                   gint            bpp,
                                                   ClutterTextureFlags  flags,
                                                   GError        **error);
-
+ClutterActor *clutter_texture_new_from_actor      (ClutterActor *actor);
 gboolean      clutter_texture_set_area_from_rgb_data (ClutterTexture *texture,
                                                    const guchar   *data,
                                                    gboolean        has_alpha,
index ae3d2a0..c037172 100644 (file)
@@ -264,6 +264,23 @@ void
 cogl_program_uniform_1f           (COGLint      uniform_no,
                                    gfloat       value);
 
+/* Offscreen - FBO support */
+
+COGLuint
+cogl_offscreen_create (COGLuint target_texture);
+
+void
+cogl_offscreen_destroy (COGLuint offscreen_handle);
+
+void
+cogl_offscreen_redirect_start (COGLuint offscreen_handle,
+                              gint     width,
+                              gint     height);
+
+void
+cogl_offscreen_redirect_end (COGLuint offscreen_handle,
+                            gint     width,
+                            gint     height);
 
 G_END_DECLS
 
index a09914c..25f1f1d 100644 (file)
@@ -41,6 +41,21 @@ typedef CoglFuncPtr (*GLXGetProcAddressProc) (const guint8 *procName);
 
 static gulong __enable_flags = 0;
 
+/* FBO Procs */
+typedef void (*GenFramebuffers) (GLsizei n, GLuint *ids);
+typedef void (*BindFramebuffer) (GLenum target, GLuint framebuffer);
+typedef void (*FramebufferTexture2D) (GLenum target, GLenum attachment,
+                                     GLenum textarget, GLuint texture,
+                                     GLint level);
+typedef GLenum (*CheckFramebufferStatus)(GLenum target);
+typedef void (*DeleteFramebuffers) (GLsizei n, const GLuint *framebuffers);
+
+static GenFramebuffers        _gen_framebuffers = NULL;
+static BindFramebuffer        _bind_framebuffer = NULL;
+static FramebufferTexture2D   _framebuffer_texture_2d = NULL;
+static CheckFramebufferStatus _check_framebuffer_status = NULL;
+static DeleteFramebuffers     _delete_framebuffers = NULL;
+
 #if COGL_DEBUG
 struct token_string
 {
@@ -189,7 +204,6 @@ cogl_paint_init (const ClutterColor *color)
   glDepthFunc (GL_LEQUAL);
 
   cogl_enable (CGL_ENABLE_BLEND);
-
   glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
 }
 
@@ -295,6 +309,7 @@ cogl_enable (gulong flags)
     }
 #endif
 
+#if 0
   if (flags & CGL_ENABLE_ALPHA_TEST)
     {
       if (!(__enable_flags & CGL_ENABLE_ALPHA_TEST))
@@ -308,6 +323,7 @@ cogl_enable (gulong flags)
       glDisable (GL_ALPHA_TEST);
        __enable_flags &= ~CGL_ENABLE_ALPHA_TEST;
     }
+#endif
 }
 
 void
@@ -648,6 +664,35 @@ cogl_get_features ()
       flags |= CLUTTER_FEATURE_SHADERS_GLSL;
     }
 
+  if (cogl_check_extension ("GL_EXT_framebuffer_object", gl_extensions) ||
+      cogl_check_extension ("GL_ARB_framebuffer_object", gl_extensions))
+    {
+      _gen_framebuffers = 
+       (GenFramebuffers) cogl_get_proc_address ("glGenFramebuffersEXT");
+
+      _bind_framebuffer = 
+       (BindFramebuffer) cogl_get_proc_address ("glBindFramebufferEXT");
+
+      _framebuffer_texture_2d =
+       (FramebufferTexture2D) 
+          cogl_get_proc_address ("glFramebufferTexture2DEXT");
+
+      _check_framebuffer_status =
+       (CheckFramebufferStatus)
+          cogl_get_proc_address ("glCheckFramebufferStatusEXT");
+
+      _delete_framebuffers =
+       (DeleteFramebuffers)
+          cogl_get_proc_address ("glDeleteFramebuffersEXT");
+
+      if (_gen_framebuffers 
+         && _bind_framebuffer
+         && _framebuffer_texture_2d
+         && _check_framebuffer_status
+         && _delete_framebuffers)
+       flags |= CLUTTER_FEATURE_OFFSCREEN;
+    }
+
   return flags;
 }
 
@@ -774,6 +819,112 @@ cogl_fog_set (const ClutterColor *fog_color,
   glFogf (GL_FOG_END, CLUTTER_FIXED_TO_FLOAT (stop));
 }
 
+/* FBOs - offscreen */
+
+COGLuint
+cogl_offscreen_create (COGLuint target_texture)
+{
+#ifdef GL_FRAMEBUFFER_EXT
+  COGLuint handle;
+  GLenum status;
+
+  if (_gen_framebuffers == NULL
+      || _bind_framebuffer == NULL
+      || _framebuffer_texture_2d == NULL
+      || _check_framebuffer_status == NULL)
+    {
+      /* tmp warning - need error reporting */
+      g_warning("Missing GL_FRAMEBUFFER_EXT API\n");
+      return 0;
+    }
+
+  _gen_framebuffers (1, &handle);
+  _bind_framebuffer (GL_FRAMEBUFFER_EXT, handle);
+
+  _framebuffer_texture_2d (GL_FRAMEBUFFER_EXT, 
+                          GL_COLOR_ATTACHMENT0_EXT,
+                          GL_TEXTURE_RECTANGLE_ARB, 
+                          target_texture,
+                          0);
+
+  status = _check_framebuffer_status (GL_FRAMEBUFFER_EXT);
+
+  if (status != GL_FRAMEBUFFER_COMPLETE_EXT)
+    {
+      _delete_framebuffers (1, &handle);
+      return 0;
+    }
+
+  _bind_framebuffer (GL_FRAMEBUFFER_EXT, 0);
+
+  return handle;
+#else
+  /* tmp warning - need error reporting */
+  g_warning("No GL_FRAMEBUFFER_EXT\n");
+  return 0;
+#endif
+}
+
+void
+cogl_offscreen_destroy (COGLuint offscreen_handle)
+{
+  if (_delete_framebuffers)
+    _delete_framebuffers (1, &offscreen_handle);
+}
+
+void
+cogl_offscreen_redirect_start (COGLuint offscreen_handle,
+                              gint     width,
+                              gint     height)
+{
+  /* FIXME: silly we need to pass width / height to init viewport */
+#ifdef GL_FRAMEBUFFER_EXT
+
+  if (_bind_framebuffer == NULL)
+    return;
+
+  _bind_framebuffer (GL_FRAMEBUFFER_EXT, offscreen_handle);
+
+  glViewport (0, 0, width, height);
+
+  glMatrixMode (GL_PROJECTION);
+  glPushMatrix();
+  glLoadIdentity ();
+
+  glMatrixMode (GL_MODELVIEW);
+  glPushMatrix();
+  glLoadIdentity ();
+
+  glTranslatef (-1.0f, -1.0f, 0.0f);
+  glScalef (2.0f / (float)width, 2.0f / (float)height, 1.0f);
+#endif
+}
+
+void
+cogl_offscreen_redirect_end (COGLuint offscreen_handle,
+                            gint     width,
+                            gint     height)
+{
+  /* FIXME: silly we need to pass width / height to reset to */
+  if (_bind_framebuffer == NULL)
+    return;
+
+#ifdef GL_FRAMEBUFFER_EXT
+  glViewport (0, 0, width, height);
+
+  glMatrixMode (GL_PROJECTION);
+  glPopMatrix();
+
+  glMatrixMode (GL_MODELVIEW);
+  glPopMatrix();
+
+  _bind_framebuffer (GL_FRAMEBUFFER_EXT, 0);
+#endif
+}
+
+
+/* Shader Magic follows */
+
 #ifdef __GNUC__
 
 #define PROC(rettype, retval, procname, args...) \
index b1a15da..f972dc9 100644 (file)
@@ -645,6 +645,36 @@ cogl_fog_set (const ClutterColor *fog_color,
   glFogx (GL_FOG_END, (GLfixed) z_far);
 }
 
+/* Offscreen - TODO: possible support from FBO's */
+
+COGLuint
+cogl_offscreen_create (COGLuint target_texture)
+{
+  return 0;
+}
+
+void
+cogl_offscreen_destroy (COGLuint offscreen_handle)
+{
+}
+
+void
+cogl_offscreen_redirect_start (COGLuint offscreen_handle,
+                              gint     width,
+                              gint     height)
+{
+}
+
+void
+cogl_offscreen_redirect_end (COGLuint offscreen_handle,
+                            gint     width,
+                            gint     height)
+{
+}
+
+
+/* Shaders, no support on regular OpenGL 1.1 */
+
 COGLhandle
 cogl_create_program (void)
 {
index 8017aff..7e7fcbc 100644 (file)
@@ -308,7 +308,7 @@ clutter_stage_x11_set_cursor_visible (ClutterStage *stage,
 
   if (show_cursor)
     {
-#if HAVE_XFIXES
+#if 0 /* HAVE_XFIXES - seems buggy/unreliable */
       XFixesShowCursor (stage_x11->xdpy, stage_x11->xwin);
 #else
       XUndefineCursor (stage_x11->xdpy, stage_x11->xwin);
@@ -316,7 +316,9 @@ clutter_stage_x11_set_cursor_visible (ClutterStage *stage,
     }
   else
     {
-#if HAVE_XFIXES
+#if 0 /* HAVE_XFIXES - seems buggy/unreliable, check cursor in firefox 
+       *               loading page after hiding.  
+      */
       XFixesHideCursor (stage_x11->xdpy, stage_x11->xwin);
 #else
       XColor col;
index 17a06b0..e6f8698 100644 (file)
@@ -3,7 +3,7 @@ noinst_PROGRAMS = test-textures test-events test-offscreen test-scale \
                  test-perspective test-rotate test-depth \
                  test-threads test-timeline test-score test-script \
                  test-model test-grab test-effects test-fullscreen \
-                 test-shader test-unproject test-viewport
+                 test-shader test-unproject test-viewport test-fbo
 
 INCLUDES = -I$(top_srcdir)/
 LDADD = $(top_builddir)/clutter/libclutter-@CLUTTER_FLAVOUR@-@CLUTTER_MAJORMINOR@.la
@@ -33,5 +33,6 @@ test_model_SOURCES       = test-model.c
 test_effects_SOURCES     = test-effects.c
 test_fullscreen_SOURCES  = test-fullscreen.c
 test_viewport_SOURCES    = test-viewport.c
+test_fbo_SOURCES    = test-fbo.c
 
 EXTRA_DIST = redhand.png test-script.json
diff --git a/tests/test-fbo.c b/tests/test-fbo.c
new file mode 100644 (file)
index 0000000..0bacced
--- /dev/null
@@ -0,0 +1,100 @@
+/*#define TEST_GROUP */
+
+#include <clutter/clutter.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <glib.h>
+
+
+gint
+main (gint   argc,
+      gchar *argv[])
+{
+  ClutterColor      color={0x33, 0x44, 0x55, 0xff};
+  ClutterActor     *fbo;
+  ClutterActor     *actor;
+  ClutterActor     *actor2;
+  ClutterActor     *group;
+  ClutterShader    *shader;
+  ClutterActor     *stage;
+  ClutterActor     *rectangle;
+  ClutterActor     *clone;
+  GdkPixbuf        *pixbuf;
+  GError           *error = NULL;
+
+  clutter_init (&argc, &argv);
+
+  stage = clutter_stage_get_default ();
+  clutter_stage_set_color (CLUTTER_STAGE (stage), &color);
+
+  pixbuf = gdk_pixbuf_new_from_file ("redhand.png", &error);
+  if (!pixbuf)
+    g_error("pixbuf load failed: %s", error ? error->message : "Unknown");
+
+  /* actor = clutter_texture_new_from_pixbuf (pixbuf);*/
+
+  group = clutter_group_new ();
+  {
+    ClutterColor nothing = {0, 0,0,0};
+    rectangle = clutter_rectangle_new_with_color (&nothing);
+    clutter_actor_set_size (rectangle, 800, 270);
+  }
+  
+  actor2 = clutter_texture_new_from_pixbuf (pixbuf);
+  clutter_container_add_actor (CLUTTER_CONTAINER (group), actor2);
+  {
+    ClutterColor yellow = {0xff, 0xff, 0x00, 0xff};
+    actor = clutter_label_new_with_text ("Sans 50px", "Hello hadyness");
+    clutter_label_set_color (CLUTTER_LABEL (actor), &yellow);
+  }
+
+  clutter_container_add_actor (CLUTTER_CONTAINER (group), actor);
+  clutter_container_add_actor (CLUTTER_CONTAINER (group), rectangle);
+  clutter_actor_set_position (actor, 0, 15);
+
+  clutter_actor_show_all (group);
+
+  fbo = clutter_texture_new_from_actor (group);
+
+  clutter_container_add_actor (CLUTTER_CONTAINER (stage), fbo);
+  clutter_actor_set_position (fbo, 20, 120);
+  clutter_actor_set_position (actor2, 130, 20);
+
+  shader = clutter_shader_new ();
+  clutter_shader_set_fragment_source (shader, 
+   
+        "uniform float radius ;"
+        "uniform sampler2DRect rectTexture;"
+        ""
+        "void main()"
+        "{"
+        "    vec4 color = texture2DRect(rectTexture, gl_TexCoord[0].st);"
+        "    float u;"
+        "    float v;"
+        "    int count = 1;"
+        "    for (u=-radius;u<radius;u++)"
+        "      for (v=-radius;v<radius;v++)"
+        "        {"
+        "          color += texture2DRect(rectTexture, vec2(gl_TexCoord[0].s + u * 2.0, gl_TexCoord[0].t +v * 2.0));"
+        "          count ++;"
+        "        }"
+        ""
+        "    gl_FragColor = color / float(count);"
+
+        "}",
+        -1
+   );
+
+  clone = clutter_clone_texture_new (fbo);
+  clutter_container_add_actor (CLUTTER_CONTAINER (stage), clone);
+  clutter_actor_set_position (clone, 40, 300);
+
+  if(1)clutter_actor_apply_shader (clone, shader);
+  if(1)clutter_actor_set_shader_param (clone, "radius", 2.0);
+  
+
+  clutter_actor_show_all (stage);
+  clutter_main ();
+}