providing GLSL support.
* clutter/cogl/cogl.h: added rather direct binding of needed for glsl
shaders.
* clutter/cogl/gl/cogl-defines.h.in:
* clutter/cogl/gl/cogl.c:
* clutter/cogl/gles/cogl-defines.h: added stubs.
* clutter/cogl/gles/cogl.c: added stubs.
* clutter/glx/clutter-stage-glx.c:
(clutter_stage_glx_realize): unrelated memory management sanity fix.
(clutter_stage_glx_unrealize): unbind all shaders on stage unrealize.
* clutter/Makefile.am: added clutter-shader.[ch]
* clutter/clutter-actor.[ch]: adding shader capability to
actors.
* clutter/clutter-feature.h: added CLUTTER_FEATURE_SHADERS_GLSL
* clutter/clutter-private.h: added stack of shaders to context.
* clutter/clutter-shader.[ch]: new.
* tests/Makefile.am: added shader test.
* tests/test-shader.c: (frame_cb), (main): simple shader test,
cycle through the inline shader with right/left mouse buttons.
2007-12-03 Øyvind Kolås <pippin@o-hand.com>
+ Support for shaders in clutter. At the moment limited to drivers
+ providing GLSL support.
+
+ * clutter/cogl/cogl.h: added rather direct binding of needed for glsl
+ shaders.
+ * clutter/cogl/gl/cogl-defines.h.in:
+ * clutter/cogl/gl/cogl.c:
+ * clutter/cogl/gles/cogl-defines.h: added stubs.
+ * clutter/cogl/gles/cogl.c: added stubs.
+
+ * clutter/glx/clutter-stage-glx.c:
+ (clutter_stage_glx_realize): unrelated memory management sanity fix.
+ (clutter_stage_glx_unrealize): unbind all shaders on stage unrealize.
+
+ * clutter/Makefile.am: added clutter-shader.[ch]
+ * clutter/clutter-actor.[ch]: adding shader capability to
+ actors.
+ * clutter/clutter-feature.h: added CLUTTER_FEATURE_SHADERS_GLSL
+ * clutter/clutter-private.h: added stack of shaders to context.
+ * clutter/clutter-shader.[ch]: new.
+
+ * tests/Makefile.am: added shader test.
+ * tests/test-shader.c: (frame_cb), (main): simple shader test,
+ cycle through the inline shader with right/left mouse buttons.
+
+2007-12-03 Øyvind Kolås <pippin@o-hand.com>
+
* clutter/glx/clutter-stage-glx.c: for extra sanity, set
stage_x11->xvisinfo = NULL after XFree. Makes using the root window
as foreign window possible.
$(srcdir)/clutter-score.h \
$(srcdir)/clutter-script.h \
$(srcdir)/clutter-scriptable.h \
+ $(srcdir)/clutter-shader.h \
$(srcdir)/clutter-stage.h \
$(srcdir)/clutter-texture.h \
$(srcdir)/clutter-timeline.h \
clutter-script.c \
clutter-script-parser.c \
clutter-scriptable.c \
+ clutter-shader.c \
clutter-stage.c \
clutter-texture.c \
clutter-timeline.c \
#define CLUTTER_ACTOR_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_ACTOR, ClutterActorPrivate))
+typedef struct _ShaderData ShaderData;
+
struct _ClutterActorPrivate
{
ClutterActorBox coords;
gchar *name;
ClutterFixed scale_x, scale_y;
guint32 id; /* Unique ID */
+
+ ShaderData *shader_data;
ClutterUnit anchor_x, anchor_y;
};
static void _clutter_actor_apply_modelview_transform (ClutterActor *self);
static void _clutter_actor_apply_modelview_transform_recursive (ClutterActor *self);
+static void clutter_actor_shader_pre_paint (ClutterActor *actor,
+ gboolean repeat);
+static void clutter_actor_shader_post_paint (ClutterActor *actor);
+static void destroy_shader_data (ClutterActor *self);
+
G_DEFINE_ABSTRACT_TYPE_WITH_CODE (ClutterActor,
clutter_actor,
G_TYPE_INITIALLY_UNOWNED,
}
else
{
+ clutter_actor_shader_pre_paint (self, FALSE);
if (G_LIKELY(klass->paint))
(klass->paint) (self);
+ clutter_actor_shader_post_paint (self);
}
if (priv->has_clip)
g_type_name (G_OBJECT_TYPE (self)),
object->ref_count);
+ destroy_shader_data (self);
+
if (!(CLUTTER_PRIVATE_FLAGS (self) & CLUTTER_ACTOR_IN_DESTRUCTION))
{
CLUTTER_SET_PRIVATE_FLAGS (self, CLUTTER_ACTOR_IN_DESTRUCTION);
priv->id = __id++;
priv->scale_x = CFX_ONE;
priv->scale_y = CFX_ONE;
+ priv->shader_data = NULL;
clutter_actor_request_coords (self, &box);
}
(GBoxedFreeFunc) clutter_actor_box_free);
return our_type;
}
+
+/******************************************************************************/
+
+typedef struct _BoxedFloat BoxedFloat;
+struct _BoxedFloat
+{
+ gfloat value;
+};
+
+struct _ShaderData
+{
+ ClutterShader *shader;
+ GHashTable *float1f_hash; /*< list of values that should be set
+ * on the shader before each paint cycle
+ */
+};
+
+static void
+destroy_shader_data (ClutterActor *self)
+{
+ ClutterActorPrivate *actor_priv = self->priv;
+ ShaderData *shader_data = actor_priv->shader_data;
+
+ if (!shader_data)
+ return;
+
+ if (shader_data->shader)
+ {
+ g_object_unref (shader_data->shader);
+ }
+ shader_data->shader = NULL;
+ if (shader_data->float1f_hash)
+ {
+ g_hash_table_destroy (shader_data->float1f_hash);
+ shader_data->float1f_hash = NULL;
+ }
+ g_free (shader_data);
+ actor_priv->shader_data = NULL;
+}
+
+gboolean clutter_actor_apply_shader (ClutterActor *self,
+ ClutterShader *shader)
+{
+ ClutterActorPrivate *actor_priv;
+ ShaderData *shader_data;
+
+ g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
+
+ actor_priv = self->priv;
+ shader_data = actor_priv->shader_data;
+
+ if (shader_data == NULL)
+ {
+ actor_priv->shader_data = shader_data = g_new0 (ShaderData, 1);
+ shader_data->float1f_hash = g_hash_table_new_full (
+ g_str_hash, g_str_equal,
+ g_free, g_free);
+ }
+ if (shader_data->shader)
+ {
+ g_object_unref (shader_data->shader);
+ }
+ if (shader)
+ {
+ shader_data->shader = g_object_ref (shader);
+ }
+ else
+ {
+ shader_data->shader = NULL;
+ }
+ return TRUE;
+}
+
+static void
+each_param (gpointer key,
+ gpointer value,
+ gpointer user_data)
+{
+ ClutterShader *shader = CLUTTER_SHADER (user_data);
+ BoxedFloat *box = value;
+ clutter_shader_set_uniform_1f (shader, key, box->value);
+}
+
+static void
+clutter_actor_shader_pre_paint (ClutterActor *actor,
+ gboolean repeat)
+{
+ ClutterActorPrivate *actor_priv;
+ ShaderData *shader_data;
+ ClutterShader *shader;
+ ClutterMainContext *context;
+
+ g_return_if_fail (CLUTTER_IS_ACTOR (actor));
+
+ actor_priv = actor->priv;
+ shader_data = actor_priv->shader_data;
+
+ if (!shader_data)
+ return;
+
+ context = clutter_context_get_default ();
+ shader = shader_data->shader;
+
+ if (shader)
+ {
+ clutter_shader_enable (shader);
+
+ g_hash_table_foreach (shader_data->float1f_hash, each_param, shader);
+
+ if (!repeat)
+ {
+ context->shaders = g_slist_prepend (context->shaders, actor);
+ }
+ }
+}
+
+static void
+clutter_actor_shader_post_paint (ClutterActor *actor)
+{
+ ClutterActorPrivate *actor_priv;
+ ShaderData *shader_data;
+ ClutterShader *shader;
+ ClutterMainContext *context;
+
+ g_return_if_fail (CLUTTER_IS_ACTOR (actor));
+
+ actor_priv = actor->priv;
+ shader_data = actor_priv->shader_data;
+
+ if (!shader_data)
+ return;
+
+ context = clutter_context_get_default ();
+ shader = shader_data->shader;
+
+ if (shader)
+ {
+ clutter_shader_disable (shader);
+
+ context->shaders = g_slist_remove (context->shaders, actor);
+ if (context->shaders)
+ {
+ /* call pre-paint again, this time with the second argument being
+ * TRUE, indicating that we are reapplying the shader and thus
+ * should not be prepended to the stack
+ */
+ clutter_actor_shader_pre_paint (context->shaders->data, TRUE);
+ }
+ }
+}
+
+void
+clutter_actor_set_shader_param (ClutterActor *actor,
+ const gchar *param,
+ gfloat value)
+{
+ ClutterActorPrivate *actor_priv;
+ ShaderData *shader_data;
+ BoxedFloat *box;
+
+ g_return_if_fail (CLUTTER_IS_ACTOR (actor));
+
+ actor_priv = actor->priv;
+ shader_data = actor_priv->shader_data;
+
+ if (!shader_data)
+ return;
+
+ box = g_malloc (sizeof (BoxedFloat));
+ box->value = value;
+ g_hash_table_insert (shader_data->float1f_hash, g_strdup (param), box);
+}
#include <clutter/clutter-types.h>
#include <clutter/clutter-units.h>
#include <clutter/clutter-event.h>
+#include <clutter/clutter-shader.h>
G_BEGIN_DECLS
void (* focus_in) (ClutterActor *actor);
void (* focus_out) (ClutterActor *actor);
+ gboolean shadable;
/*< private >*/
/* padding for future expansion */
- gpointer _padding_dummy[32];
+ gpointer _padding_dummy[31];
};
GType clutter_actor_get_type (void) G_GNUC_CONST;
gboolean clutter_actor_should_pick_paint (ClutterActor *self);
+gboolean clutter_actor_apply_shader (ClutterActor *self,
+ ClutterShader *shader);
+
+void clutter_actor_set_shader_param (ClutterActor *self,
+ const gchar *param,
+ gfloat value);
+
+
void clutter_actor_set_anchor_point (ClutterActor *self,
gint anchor_x,
gint anchor_y);
* @CLUTTER_FEATURE_STAGE_STATIC: Set if stage size if fixed (i.e framebuffer)
* @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_SHADER_GLSL: Set if the backend supports GLSL shaders.
*
* Runtime flags indicating specific features available via Clutter window
* sysytem and graphics backend.
CLUTTER_FEATURE_TEXTURE_READ_PIXELS = (1 << 4),
CLUTTER_FEATURE_STAGE_STATIC = (1 << 5),
CLUTTER_FEATURE_STAGE_USER_RESIZE = (1 << 6),
- CLUTTER_FEATURE_STAGE_CURSOR = (1 << 7)
+ CLUTTER_FEATURE_STAGE_CURSOR = (1 << 7),
+ CLUTTER_FEATURE_SHADERS_GLSL = (1 << 8)
} ClutterFeatureFlags;
gboolean clutter_feature_available (ClutterFeatureFlags feature);
ClutterActor *keyboard_grab_actor; /* The actor having the pointer grab
(or NULL if there is no pointer grab)
*/
+ GSList *shaders; /* stack of overridden shaders */
};
#define CLUTTER_CONTEXT() (clutter_context_get_default ())
--- /dev/null
+/*
+ * Clutter.
+ *
+ * An OpenGL based 'interactive canvas' library.
+ *
+ * Authored By Matthew Allum <mallum@openedhand.com>
+ * Øyvind Kolås <pippin@o-hand.com>
+ *
+ * Copyright (C) 2007 OpenedHand
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "clutter.h"
+#include "clutter-private.h"
+#include "clutter-shader.h"
+
+#include <unistd.h>
+#include <glib.h>
+#include <cogl/cogl.h>
+#include <string.h>
+#include <stdlib.h>
+
+#define CLUTTER_SHADER_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
+ CLUTTER_TYPE_SHADER, ClutterShaderPrivate))
+
+static GList *shader_list;
+
+static void clutter_shader_class_init (ClutterShaderClass *klass);
+static void clutter_shader_init (ClutterShader *sp);
+static void clutter_shader_finalize (GObject *object);
+static GObject *clutter_shader_constructor (GType type,
+ guint n_params,
+ GObjectConstructParam *params);
+static void clutter_shader_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void clutter_shader_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+struct _ClutterShaderPrivate
+{
+ gboolean glsl; /* The shader is a GLSL shader */
+ gboolean bound; /* The shader is bound to the GL context */
+
+ gchar *vertex_shader_source; /* source (or asm) for vertex shader */
+ gchar *fragment_shader_source;/* source (or asm) for fragment shader*/
+
+ GLhandleARB program;
+
+ GLhandleARB vertex_shader;
+ GLhandleARB fragment_shader;
+};
+
+enum
+{
+ PROP_0,
+ PROP_VERTEX_SOURCE,
+ PROP_FRAGMENT_SOURCE
+};
+
+G_DEFINE_TYPE (ClutterShader, clutter_shader, G_TYPE_OBJECT);
+
+static void
+clutter_shader_class_init (ClutterShaderClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ clutter_shader_parent_class = g_type_class_peek_parent (klass);
+ object_class->finalize = clutter_shader_finalize;
+ object_class->set_property = clutter_shader_set_property;
+ object_class->get_property = clutter_shader_get_property;
+ object_class->constructor = clutter_shader_constructor;
+ g_type_class_add_private (klass, sizeof (ClutterShaderPrivate));
+
+ g_object_class_install_property (object_class,
+ PROP_VERTEX_SOURCE,
+ g_param_spec_string ("vertex-source",
+ "Vertex Source",
+ "Source of vertex shader",
+ NULL,
+ CLUTTER_PARAM_READWRITE|
+ G_PARAM_CONSTRUCT_ONLY));
+ g_object_class_install_property (object_class,
+ PROP_FRAGMENT_SOURCE,
+ g_param_spec_string ("fragment-source",
+ "Fragment Source",
+ "Source of fragment shader",
+ NULL,
+ CLUTTER_PARAM_READWRITE|
+ G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+clutter_shader_init (ClutterShader *self)
+{
+ ClutterShaderPrivate *priv;
+
+ priv = self->priv = CLUTTER_SHADER_GET_PRIVATE (self);
+
+ priv->glsl = FALSE;
+ priv->bound = FALSE;
+ priv->vertex_shader_source = NULL;
+ priv->fragment_shader_source = NULL;
+ priv->program = 0;
+ priv->vertex_shader = 0;
+ priv->fragment_shader = 0;
+}
+
+static gboolean bind_glsl_shader (ClutterShader *self)
+{
+ ClutterShaderPrivate *priv;
+ priv = self->priv;
+
+ cogl_enable (CGL_FRAGMENT_SHADER);
+ cogl_enable (CGL_VERTEX_SHADER);
+
+ priv->glsl = TRUE;
+ priv->program = cogl_create_program ();
+
+ if (priv->vertex_shader_source)
+ {
+ priv->vertex_shader = cogl_create_shader (CGL_VERTEX_SHADER);
+ cogl_shader_source (priv->vertex_shader, priv->vertex_shader_source);
+ cogl_shader_compile (priv->vertex_shader);
+ cogl_program_attach_shader (priv->program, priv->vertex_shader);
+ }
+ if (priv->fragment_shader_source)
+ {
+ GLint compiled = CGL_FALSE;
+ priv->fragment_shader = cogl_create_shader (CGL_FRAGMENT_SHADER);
+ cogl_shader_source (priv->fragment_shader, priv->fragment_shader_source);
+ cogl_shader_compile (priv->fragment_shader);
+
+ cogl_shader_get_parameteriv (priv->fragment_shader,
+ CGL_OBJECT_COMPILE_STATUS,
+ &compiled);
+ if (compiled != CGL_TRUE)
+ {
+ GLcharARB *buffer;
+ gint max_length = 512;
+ buffer = g_malloc (max_length);
+ cogl_shader_get_info_log (priv->fragment_shader, max_length, buffer);
+ g_print ("Shader compilation failed:\n%s", buffer);
+ g_free (buffer);
+ g_object_unref (self);
+ return FALSE;
+ }
+ cogl_program_attach_shader (priv->program, priv->fragment_shader);
+ }
+ cogl_program_link (priv->program);
+ return TRUE;
+}
+
+gboolean
+clutter_shader_bind (ClutterShader *self)
+{
+ ClutterShaderPrivate *priv;
+
+ priv = self->priv;
+ if (priv->bound)
+ return priv->bound;
+
+ if (priv->glsl)
+ {
+ priv->bound = bind_glsl_shader (self);
+ }
+
+ return priv->bound;
+}
+
+void
+clutter_shader_release (ClutterShader *self)
+{
+ ClutterShaderPrivate *priv;
+
+ priv = self->priv;
+ if (!priv->bound)
+ return;
+
+ g_assert (priv->program);
+
+ if (priv->glsl)
+ {
+ if (priv->vertex_shader)
+ cogl_shader_destroy (priv->vertex_shader);
+ if (priv->fragment_shader)
+ cogl_shader_destroy (priv->fragment_shader);
+ if (priv->program)
+ cogl_program_destroy (priv->program);
+ priv->vertex_shader = 0;
+ priv->fragment_shader = 0;
+ priv->program = 0;
+ }
+ priv->bound = FALSE;
+}
+
+static void
+clutter_shader_finalize (GObject *object)
+{
+ ClutterShader *shader;
+ ClutterShaderPrivate *priv;
+
+ shader = CLUTTER_SHADER (object);
+ priv = shader->priv;
+
+ clutter_shader_release (shader);
+
+ shader_list = g_list_remove (shader_list, object);
+
+ if (priv->fragment_shader_source)
+ g_free (priv->fragment_shader_source);
+ if (priv->vertex_shader_source)
+ g_free (priv->vertex_shader_source);
+
+ G_OBJECT_CLASS (clutter_shader_parent_class)->finalize (object);
+}
+
+
+void
+clutter_shader_enable (ClutterShader *self)
+{
+ ClutterShaderPrivate *priv = self->priv;
+
+ clutter_shader_bind (self);
+
+ cogl_program_use (priv->program);
+}
+
+void
+clutter_shader_disable (ClutterShader *self)
+{
+ cogl_program_use (0);
+}
+
+void
+clutter_shader_set_uniform_1f (ClutterShader *self,
+ const gchar *name,
+ gfloat value)
+{
+ ClutterShaderPrivate *priv = self->priv;
+ GLint location = 0;
+ GLfloat foo = value;
+
+ location =cogl_program_get_uniform_location (priv->program, name);
+ cogl_program_uniform_1f (location, foo);
+}
+
+void
+clutter_shader_release_all (void)
+{
+ GList *iter;
+ for (iter = shader_list; iter; iter = g_list_next (iter))
+ {
+ clutter_shader_release (iter->data);
+ }
+}
+
+static void
+clutter_shader_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ ClutterShader *shader;
+ ClutterShaderPrivate *priv;
+
+ shader = CLUTTER_SHADER(object);
+ priv = shader->priv;
+
+ switch (prop_id)
+ {
+ case PROP_VERTEX_SOURCE:
+ if (priv->vertex_shader_source)
+ {
+ g_free (priv->vertex_shader_source);
+ priv->vertex_shader_source = NULL;
+ }
+ priv->vertex_shader_source = g_value_dup_string (value);
+ break;
+ case PROP_FRAGMENT_SOURCE:
+ if (priv->fragment_shader_source)
+ {
+ g_free (priv->fragment_shader_source);
+ priv->fragment_shader_source = NULL;
+ }
+ priv->fragment_shader_source = g_value_dup_string (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+clutter_shader_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ ClutterShader *shader;
+ ClutterShaderPrivate *priv;
+
+ shader = CLUTTER_SHADER(object);
+ priv = shader->priv;
+
+ switch (prop_id)
+ {
+ case PROP_VERTEX_SOURCE:
+ g_value_set_string (value, priv->vertex_shader_source);
+ break;
+ case PROP_FRAGMENT_SOURCE:
+ g_value_set_string (value, priv->fragment_shader_source);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static GObject *
+clutter_shader_constructor (GType type,
+ guint n_params,
+ GObjectConstructParam *params)
+{
+ GObject *object;
+ ClutterShader *shader;
+ ClutterShaderPrivate *priv;
+
+ object = G_OBJECT_CLASS (clutter_shader_parent_class)->constructor (
+ type, n_params, params);
+ shader = CLUTTER_SHADER (object);
+ priv = shader->priv;
+
+ priv->glsl = !((priv->vertex_shader_source &&
+ g_str_has_prefix (priv->vertex_shader_source, "!!ARBvp")) ||
+ (priv->fragment_shader_source &&
+ g_str_has_prefix (priv->fragment_shader_source, "!!ARBfp")));
+ if (!priv->glsl)
+ {
+ g_warning ("ASM shader support not available");
+ g_object_unref (object);
+ return NULL;
+ }
+ if (priv->glsl && !clutter_feature_available (CLUTTER_FEATURE_SHADERS_GLSL))
+ {
+ g_warning ("GLSL shaders not supported\n");
+ g_object_unref (object);
+ return NULL;
+ }
+
+ shader_list = g_list_prepend (shader_list, object);
+ return object;
+}
+
+ClutterShader *
+clutter_shader_new_from_strings (const gchar *vertex_shader_program,
+ const gchar *fragment_shader_program)
+{
+ /* evil hack, since g_object_new would interpret a NULL passed as
+ * a argument termination
+ */
+ if (vertex_shader_program &&
+ fragment_shader_program)
+ return g_object_new (CLUTTER_TYPE_SHADER,
+ "vertex-source", vertex_shader_program,
+ "fragment-source", fragment_shader_program,
+ NULL);
+ else if (fragment_shader_program)
+ return g_object_new (CLUTTER_TYPE_SHADER,
+ "fragment-source", fragment_shader_program,
+ NULL);
+ else if (vertex_shader_program)
+ return g_object_new (CLUTTER_TYPE_SHADER,
+ "vertex-source", vertex_shader_program,
+ NULL);
+ else {
+ g_warning ("neither fragment nor vertex shader provided");
+ return NULL;
+ }
+}
+
+ClutterShader *
+clutter_shader_new_from_files (const gchar *vertex_file,
+ const gchar *fragment_file)
+{
+ ClutterShader *shader;
+ gchar *vertex_shader_program = NULL;
+ gchar *fragment_shader_program = NULL;
+
+ g_assert (vertex_file != NULL ||
+ fragment_file != NULL);
+
+ if (vertex_file)
+ {
+ g_file_get_contents (vertex_file, &vertex_shader_program,
+ NULL, NULL);
+ }
+ if (fragment_file)
+ {
+ g_file_get_contents (fragment_file, &fragment_shader_program,
+ NULL, NULL);
+ }
+ shader = clutter_shader_new_from_strings (vertex_shader_program,
+ fragment_shader_program);
+
+ if (vertex_shader_program)
+ {
+ g_free (vertex_shader_program);
+ }
+ if (fragment_shader_program)
+ {
+ g_free (fragment_shader_program);
+ }
+ return shader;
+}
--- /dev/null
+/*
+ * Clutter.
+ *
+ * An OpenGL based 'interactive canvas' library.
+ *
+ * Authored By Matthew Allum <mallum@openedhand.com>
+ * Øyvind Kolås <pippin@o-hand.com>
+ *
+ * Copyright (C) 2007 OpenedHand
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef CLUTTER_SHADER_H
+#define CLUTTER_SHADER_H
+
+#include <GL/gl.h>
+#include <glib.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define CLUTTER_TYPE_SHADER (clutter_shader_get_type ())
+#define CLUTTER_SHADER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CLUTTER_TYPE_SHADER, ClutterShader))
+#define CLUTTER_SHADER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), CLUTTER_TYPE_SHADER, ClutterShaderClass))
+#define CLUTTER_IS_SHADER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), CLUTTER_TYPE_SHADER))
+#define CLUTTER_IS_SHADER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), CLUTTER_TYPE_SHADER))
+#define CLUTTER_SHADER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), CLUTTER_TYPE_SHADER, ClutterShaderClass))
+
+typedef struct _ClutterShader ClutterShader;
+typedef struct _ClutterShaderPrivate ClutterShaderPrivate;
+typedef struct _ClutterShaderClass ClutterShaderClass;
+
+struct _ClutterShader
+{
+ GObject parent;
+ ClutterShaderPrivate *priv;
+};
+
+struct _ClutterShaderClass
+{
+ GObjectClass parent_class;
+};
+
+GType clutter_shader_get_type ();
+
+ClutterShader * clutter_shader_new_from_files (const gchar *vertex_file,
+ const gchar *fragment_file);
+ClutterShader * clutter_shader_new_from_strings (const gchar *vertex_file,
+ const gchar *fragment_file);
+void clutter_shader_enable (ClutterShader *self);
+void clutter_shader_disable (ClutterShader *self);
+
+gboolean clutter_shader_bind (ClutterShader *self);
+void clutter_shader_release (ClutterShader *self);
+void clutter_shader_set_uniform_1f (ClutterShader *self,
+ const gchar *name,
+ gfloat value);
+/* should be private and internal */
+void clutter_shader_release_all (void);
+gboolean clutter_shader_has_glsl (void);
+
+
+G_END_DECLS
+
+#endif /* CLUTTER_SHADER_H */
ClutterFixed z_near,
ClutterFixed z_far);
+
+COGLint
+cogl_create_shader (COGLenum shaderType);
+
+void
+cogl_shader_destroy (COGLint handle);
+
+
+void
+cogl_shader_source (COGLint shader,
+ const gchar *source);
+void
+cogl_shader_compile (COGLint shader_handle);
+
+void
+cogl_shader_get_info_log (COGLint handle,
+ guint size,
+ gchar *buffer);
+
+void
+cogl_shader_get_parameteriv (COGLint handle,
+ COGLenum pname,
+ COGLint *dest);
+
+
+COGLint
+cogl_create_program (void);
+
+void
+cogl_program_destroy (COGLint handle);
+
+void
+cogl_program_attach_shader (COGLint program_handle,
+ COGLint shader_handle);
+
+/* 0 to use none */
+void
+cogl_program_link (COGLint program_handle);
+
+void
+cogl_program_use (COGLint program_handle);
+
+COGLint
+cogl_program_get_uniform_location (COGLint program_int,
+ const gchar *uniform_name);
+
+
+void
+cogl_program_uniform_1f (COGLint uniform_no,
+ gfloat value);
+
+
G_END_DECLS
#endif /* __COGL_H__ */
#define CGL_UNSIGNED_SHORT_8_8_MESA 0
#endif
+#define CGL_FRAGMENT_SHADER GL_FRAGMENT_SHADER_ARB
+#define CGL_VERTEX_SHADER GL_VERTEX_SHADER_ARB
+
+#define CGL_OBJECT_COMPILE_STATUS GL_OBJECT_COMPILE_STATUS_ARB
+
+
G_END_DECLS
#endif
}
#endif
+ if (cogl_check_extension ("GL_ARB_vertex_shader", gl_extensions) &&
+ cogl_check_extension ("GL_ARB_fragment_shader", gl_extensions))
+ {
+ flags |= CLUTTER_FEATURE_SHADERS_GLSL;
+ }
+
return flags;
}
glFogf (GL_FOG_START, CLUTTER_FIXED_TO_FLOAT (start));
glFogf (GL_FOG_END, CLUTTER_FIXED_TO_FLOAT (stop));
}
+
+#ifdef __GNUC__
+
+#define PROC(rettype, retval, procname, args...) \
+ static rettype (*proc) (args) = NULL; \
+ if (proc == NULL) \
+ { \
+ proc = (void*)cogl_get_proc_address (#procname);\
+ if (!proc)\
+ {\
+ g_warning ("failed to lookup proc: %s", #procname);\
+ return retval;\
+ }\
+ }
+#else
+
+#define PROC(rettype, retval, procname, ...) \
+ static rettype (*proc) (__VA_ARGS__) = NULL; \
+ if (proc == NULL) \
+ { \
+ proc = (void*)cogl_get_proc_address (#procname);\
+ if (!proc)\
+ {\
+ g_warning ("failed to lookup proc: %s", #procname);\
+ return retval;\
+ }\
+ }
+
+#endif
+
+COGLint
+cogl_create_program (void)
+{
+ PROC (GLhandleARB, 0, glCreateProgramObjectARB, void);
+ return proc ();
+}
+
+COGLint
+cogl_create_shader (COGLenum shaderType)
+{
+ PROC (GLhandleARB, 0, glCreateShaderObjectARB, GLenum);
+ return proc (shaderType);
+}
+
+void
+cogl_shader_source (COGLint shader,
+ const gchar *source)
+{
+ PROC (GLvoid,, glShaderSourceARB, GLhandleARB, GLsizei, const GLcharARB **, const GLint *)
+ proc (shader, 1, &source, NULL);
+}
+
+void
+cogl_shader_compile (COGLint shader_handle)
+{
+ PROC (GLvoid,, glCompileShaderARB, GLhandleARB);
+ proc (shader_handle);
+}
+
+void
+cogl_program_attach_shader (COGLint program_handle,
+ COGLint shader_handle)
+{
+ PROC (GLvoid,, glAttachObjectARB, GLhandleARB, GLhandleARB);
+ proc (program_handle, shader_handle);
+}
+
+void
+cogl_program_link (COGLint program_handle)
+{
+ PROC (GLvoid,, glLinkProgramARB, GLhandleARB);
+ proc (program_handle);
+}
+
+void
+cogl_program_use (COGLint program_handle)
+{
+ PROC (GLvoid,, glUseProgramObjectARB, GLhandleARB);
+ proc (program_handle);
+}
+
+COGLint
+cogl_program_get_uniform_location (COGLint program_handle,
+ const gchar *uniform_name)
+{
+ PROC (GLint,0, glGetUniformLocationARB, GLhandleARB, const GLcharARB *)
+ return proc (program_handle, uniform_name);
+}
+
+void
+cogl_program_destroy (COGLint handle)
+{
+ PROC (GLvoid,, glDeleteObjectARB, GLhandleARB);
+ proc (handle);
+}
+
+void
+cogl_shader_destroy (COGLint handle)
+{
+ PROC (GLvoid,, glDeleteObjectARB, GLhandleARB);
+ proc (handle);
+}
+
+void
+cogl_shader_get_info_log (COGLint handle,
+ guint size,
+ gchar *buffer)
+{
+ gint len;
+ PROC (GLvoid,, glGetInfoLogARB, GLhandleARB, GLsizei, GLsizei *, GLcharARB *);
+ proc (handle, size-1, &len, buffer);
+ buffer[len]='\0';
+}
+
+void
+cogl_shader_get_parameteriv (COGLint handle,
+ COGLenum pname,
+ COGLint *dest)
+{
+ PROC (GLvoid,, glGetObjectParameterivARB, GLhandleARB, GLenum, GLint*)
+ proc (handle, pname, dest);
+}
+
+
+void
+cogl_program_uniform_1f (COGLint uniform_no,
+ gfloat value)
+{
+ PROC (GLvoid,, glUniform1fARB, GLint, GLfloat);
+ proc (uniform_no, value);
+}
#define CGL_UNSIGNED_SHORT_8_8_REV_MESA 0
#define CGL_UNSIGNED_SHORT_8_8_MESA 0
+#ifdef GL_FRAGMENT_SHADER
+#define CGL_FRAGMENT_SHADER GL_FRAGMENT_SHADER
+#else
+#define CGL_FRAGMENT_SHADER 0
+#endif
+
+#ifdef GL_VERTEX_SHADER
+#define CGL_VERTEX_SHADER GL_VERTEX_SHADER
+#else
+#define CGL_VERTEX_SHADER 0
+#endif
+
+#ifdef GL_OBJECT_COMPILE_STATUS
+#define CGL_OBJECT_COMPILE_STATUS GL_OBJECT_COMPILE_STATUS
+#else
+#define CGL_OBJECT_COMPILE_STATUS 0
+#endif
+
G_END_DECLS
#endif
if (flags & CGL_ENABLE_TEXTURE_RECT)
{
if (!(__enable_flags & CGL_ENABLE_TEXTURE_RECT))
- glEnable (GL_TEXTURE_RECTANGLE_ARB);
+ glEnable (GL_TEXTURE_RECTANGLE_);
__enable_flags |= CGL_ENABLE_TEXTURE_RECT;
}
else if (__enable_flags & CGL_ENABLE_TEXTURE_RECT)
{
- glDisable (GL_TEXTURE_RECTANGLE_ARB);
+ glDisable (GL_TEXTURE_RECTANGLE_);
__enable_flags &= ~CGL_ENABLE_TEXTURE_RECT;
}
#endif
glFogx (GL_FOG_START, (GLfixed) z_near);
glFogx (GL_FOG_END, (GLfixed) z_far);
}
+
+COGLint cogl_create_program (void)
+{
+ return 0;
+}
+
+COGLint cogl_create_shader (COGLenum shaderType)
+{
+ return 0;
+}
+
+void cogl_shader_source (COGLint shader,
+ const gchar *source)
+{
+}
+
+void cogl_shader_compile (COGLint shader_handle)
+{
+}
+
+void cogl_program_attach_shader (COGLint program_handle,
+ COGLint shader_handle)
+{
+}
+
+void cogl_program_link (COGLint program_handle)
+{
+}
+
+void cogl_program_use (COGLint program_handle)
+{
+}
+
+COGLint cogl_program_get_uniform_location (COGLint program_handle,
+ const gchar *uniform_name)
+{
+ return 0;
+}
+
+void cogl_program_destroy (COGLint handle)
+{
+}
+
+void cogl_shader_destroy (COGLint handle)
+{
+}
+
+void cogl_shader_get_info_log (COGLint handle,
+ guint size,
+ gchar *buffer)
+{
+}
+
+void cogl_shader_get_parameteriv (COGLint handle,
+ COGLenum pname,
+ COGLint *dest)
+{
+}
+
+
+void cogl_program_uniform_1f (COGLint uniform_no,
+ gfloat value)
+{
+}
#include "../clutter-private.h"
#include "../clutter-debug.h"
#include "../clutter-units.h"
+#include "../clutter-shader.h"
#include "cogl.h"
clutter_x11_trap_x_errors ();
+ /* Unrealize all shaders, since the GL context is going away */
+ clutter_shader_release_all ();
+
if (G_UNLIKELY (was_offscreen))
{
if (stage_glx->glxpixmap)
test-actors test-behave test-text test-entry test-project \
test-perspective test-rotate test-depth \
test-threads test-timeline test-score test-script \
- test-model test-grab test-effects test-fullscreen
+ test-model test-grab test-effects test-fullscreen test-shader
INCLUDES = -I$(top_srcdir)/
LDADD = $(top_builddir)/clutter/libclutter-@CLUTTER_FLAVOUR@-@CLUTTER_MAJORMINOR@.la
test_depth_SOURCES = test-depth.c
test_threads_SOURCES = test-threads.c
test_timeline_SOURCES = test-timeline.c
+test_shader_SOURCES = test-shader.c
test_score_SOURCES = test-score.c
test_script_SOURCES = test-script.c
test_model_SOURCES = test-model.c
--- /dev/null
+/* #define TEST_GROUP 1 */
+
+#include <clutter/clutter.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <glib.h>
+
+typedef struct
+{
+ gchar *name;
+ gchar *source;
+} ShaderSource;
+
+static ShaderSource shaders[]=
+ {
+ {"brightness-contrast",
+
+ "uniform float brightness;"
+ "uniform float contrast;"
+ "uniform sampler2DRect pend_s3_tex;"
+ ""
+ "void main()"
+ "{"
+ " vec4 pend_s4_result;"
+ " pend_s4_result = texture2DRect(pend_s3_tex, gl_TexCoord[0].xy);"
+ " pend_s4_result.x = (pend_s4_result.x - 0.5)*contrast + brightness + 0.5;"
+ " pend_s4_result.y = (pend_s4_result.y - 0.5)*contrast + brightness + 0.5;"
+ " pend_s4_result.z = (pend_s4_result.z - 0.5)*contrast + brightness + 0.5;"
+ " gl_FragColor = pend_s4_result;"
+ "}",
+ },
+ {"box-blur",
+
+ "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, gl_TexCoord[0].t +v * 2));"
+ " count ++;"
+ " }"
+ ""
+ " gl_FragColor = color / count;"
+ "}"
+ },
+ {"brightness-contrast.asm",
+
+ "!!ARBfp1.0\n"
+ "PARAM brightness = program.local[0];\n"
+ "PARAM contrast = program.local[1];\n"
+ "\n"
+ "TEMP R0;\n"
+ "TEX R0, fragment.texcoord[0], texture[0], RECT;\n"
+ "ADD R0.z, R0, -0.5;\n"
+ "MUL R0.z, R0, contrast.x;\n"
+ "ADD R0.z, R0, brightness.x;\n"
+ "ADD R0.y, R0, -0.5;\n"
+ "ADD R0.x, R0, -0.5;\n"
+ "MUL R0.y, R0, contrast.x;\n"
+ "MUL R0.x, R0, contrast.x;\n"
+ "ADD R0.y, R0, brightness.x;\n"
+ "ADD R0.x, R0, brightness.x;\n"
+ "ADD result.color.z, R0, 0.5;\n"
+ "ADD result.color.y, R0, 0.5;\n"
+ "ADD result.color.x, R0, 0.5;\n"
+ "MOV result.color.w, R0;\n"
+ "END ",
+ },
+ {"invert",
+
+ "uniform sampler2DRect tex;\n"
+ "void main ()\n"
+ "{\n"
+ " vec4 color = texture2DRect (tex, vec2(gl_TexCoord[0].st));\n"
+ " gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0) - color;\n"
+ " gl_FragColor.a = color.a;\n"
+ "}"
+ },
+ {"brightness-contrast",
+
+ "uniform sampler2DRect tex;"
+ "uniform float brightness;"
+ "uniform float contrast;"
+ "void main ()"
+ "{"
+ " vec4 color = texture2DRect (tex, vec2(gl_TexCoord[0].st));"
+ " color.r = (color.r - 0.5) * contrast + brightness + 0.5;"
+ " color.g = (color.g - 0.5) * contrast + brightness + 0.5;"
+ " color.b = (color.b - 0.5) * contrast + brightness + 0.5;"
+ " gl_FragColor = color;"
+ "}",
+ },
+ {"gray",
+ "uniform sampler2DRect tex;"
+ "void main ()"
+ "{"
+ " vec4 color = texture2DRect (tex, vec2(gl_TexCoord[0].st));"
+ " float avg = (color.r + color.g + color.b) / 3;"
+ " color.r = avg;"
+ " color.g = avg;"
+ " color.b = avg;"
+ " gl_FragColor = color;"
+ "}",
+ },
+ {"combined-mirror",
+ "uniform sampler2DRect tex;"
+ "void main ()"
+ "{"
+ " vec4 color = texture2DRect (tex, vec2(gl_TexCoord[0].st));"
+ " vec4 colorB = texture2DRect (tex, vec2(gl_TexCoord[0].ts));"
+ " float avg = (color.r + color.g + color.b) / 3;"
+ " color.r = avg;"
+ " color.g = avg;"
+ " color.b = avg;"
+ " color = (color + colorB)/2;"
+ " gl_FragColor = color;"
+ "}",
+ },
+ {NULL, NULL}
+};
+
+static gint shader_no=0;
+
+static gboolean
+button_release_cb (ClutterActor *actor,
+ ClutterEvent *event,
+ gpointer data)
+{
+ gint new_no;
+ if (event->button.button == 1)
+ {
+ new_no = shader_no-1;
+ }
+ else
+ {
+ new_no = shader_no+1;
+ }
+
+ if (new_no >= 0 && shaders[new_no].name)
+ {
+ ClutterShader *shader;
+ shader_no = new_no;
+
+ g_print ("setting shaders[%i] named '%s'\n", shader_no, shaders[shader_no].name);
+ shader = clutter_shader_new_from_strings (NULL, shaders[shader_no].source);
+ clutter_actor_apply_shader (actor, shader);
+
+ clutter_actor_set_shader_param (actor, "radius", 3.0);
+ clutter_redraw();
+ }
+ return FALSE;
+}
+
+
+gint
+main (gint argc,
+ gchar *argv[])
+{
+ ClutterTimeline *timeline;
+ ClutterAlpha *alpha;
+ ClutterActor *actor;
+ ClutterActor *stage;
+ ClutterColor stage_color = { 0x61, 0x64, 0x8c, 0xff };
+ GdkPixbuf *pixbuf;
+ GError *error;
+ ClutterShader *shader;
+
+ error = NULL;
+
+ clutter_init (&argc, &argv);
+ g_print ("applying shaders[%i] named '%s'\n", shader_no, shaders[shader_no].name);
+ shader = clutter_shader_new_from_strings (NULL, shaders[shader_no].source);
+
+ stage = clutter_stage_get_default ();
+ clutter_actor_set_size (stage, 512, 384);
+
+ pixbuf = gdk_pixbuf_new_from_file ("redhand.png", NULL);
+
+ if (!pixbuf)
+ g_error("pixbuf load failed");
+
+ clutter_stage_set_title (CLUTTER_STAGE (stage), "Shader Test");
+ clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color);
+
+ /* Create a timeline to manage animation */
+ timeline = clutter_timeline_new (360, 60); /* num frames, fps */
+ g_object_set (timeline, "loop", TRUE, NULL); /* have it loop */
+
+#ifndef TEST_GROUP
+ actor = clutter_texture_new_from_pixbuf (pixbuf);
+#else
+ actor = clutter_group_new ();
+ {
+ ClutterActor *child1, *child2, *child3, *child4;
+ ClutterColor color={0xff, 0x22, 0x66, 0x99};
+
+ child1 = clutter_texture_new_from_pixbuf (pixbuf);
+ child2 = clutter_texture_new_from_pixbuf (pixbuf);
+ child3 = clutter_rectangle_new ();
+ child4 = clutter_label_new_with_text ("Sans 20px", "Shady stuff");
+
+ clutter_rectangle_set_color (child3, &color);
+ clutter_actor_set_size (child3, 50, 50);
+ clutter_actor_set_position (child1, 0, 0);
+ clutter_actor_set_position (child2, 50, 100);
+ clutter_actor_set_position (child3, 30, -30);
+ clutter_actor_set_position (child4, -50, 20);
+
+ clutter_group_add (CLUTTER_GROUP (actor), child1);
+ clutter_group_add (CLUTTER_GROUP (actor), child2);
+ clutter_group_add (CLUTTER_GROUP (actor), child3);
+ clutter_group_add (CLUTTER_GROUP (actor), child4);
+ clutter_actor_show_all (actor);
+ }
+#endif
+ clutter_actor_set_position (actor, 100, 100);
+
+ clutter_container_add_actor (CLUTTER_CONTAINER (stage), actor);
+
+ clutter_actor_apply_shader (actor, shader);
+
+ clutter_actor_set_shader_param (actor, "brightness", 0.4);
+ clutter_actor_set_shader_param (actor, "contrast", -1.9);
+
+ clutter_actor_set_reactive (actor, TRUE);
+ g_signal_connect (actor, "button-release-event",
+ G_CALLBACK (button_release_cb), NULL);
+
+ /* Show everying ( and map window ) */
+ clutter_actor_show_all (stage);
+
+ /* and start it */
+ clutter_timeline_start (timeline);
+
+ clutter_main ();
+
+ return 0;
+}