2011-02-21 11:43:34 -05:00
|
|
|
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
|
|
|
|
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
|
|
|
|
|
|
|
|
<chapter id="effects" xmlns:xi="http://www.w3.org/2003/XInclude">
|
|
|
|
<title>Effects</title>
|
|
|
|
|
|
|
|
<epigraph>
|
|
|
|
<attribution>Roger Zelazny, from <citetitle>Prince of Chaos</citetitle>
|
|
|
|
</attribution>
|
|
|
|
<para>Don't wake me for the end of the world unless it has very
|
|
|
|
good special effects</para>
|
|
|
|
</epigraph>
|
|
|
|
|
|
|
|
<section id="effects-introduction">
|
|
|
|
<title>Introduction</title>
|
|
|
|
|
|
|
|
<para>Effects modify an actor's appearance, such
|
|
|
|
as how it is positioned, colored and textured.</para>
|
|
|
|
|
|
|
|
<para>The Clutter API for effects contains several
|
|
|
|
abstract classes you can subclass to create your own effects.
|
|
|
|
It also contains several built-in effects you can use to
|
|
|
|
modify the visual appearance of actors in a variety of ways.</para>
|
|
|
|
|
|
|
|
<para>The recipes in this section of the cookbook cover how to create
|
|
|
|
your own effects as well as how to apply Clutter's effects.</para>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<title>Creating effects using the abstract effect classes</title>
|
|
|
|
|
|
|
|
<tip>
|
|
|
|
<para>One of the original design goals of Clutter was to abstract
|
|
|
|
the complexity of GL. However, the effects API partially circumvents
|
|
|
|
these abstractions, to give you finer-grained access to the
|
|
|
|
graphics pipeline. Therefore, if you want to write your own effects,
|
|
|
|
some understanding of Cogl, OpenGL, and general graphics programming
|
|
|
|
is essential.</para>
|
|
|
|
</tip>
|
|
|
|
|
|
|
|
<para>Each abstract effect class is tailored to modifying different
|
|
|
|
aspects of an actor, as explained below:</para>
|
|
|
|
|
|
|
|
<itemizedlist>
|
|
|
|
|
|
|
|
<listitem>
|
|
|
|
<formalpara>
|
|
|
|
<title><type>ClutterEffect</type></title>
|
|
|
|
<para>If you're just using the Clutter and Cogl APIs to
|
|
|
|
decorate an actor, this is simplest type of effect to
|
|
|
|
implement.</para>
|
|
|
|
</formalpara>
|
|
|
|
|
|
|
|
<para>Subclassing <type>ClutterEffect</type> enables you to
|
|
|
|
"wrap" how an actor is painted, by injecting some code before
|
|
|
|
and/or after the actor's own <function>paint()</function>
|
|
|
|
implementation.</para>
|
|
|
|
|
|
|
|
<note>
|
|
|
|
<para>This is the preferred way to modify how an actor is
|
|
|
|
painted, short of creating your own actor subclass.</para>
|
|
|
|
</note>
|
|
|
|
|
|
|
|
<para><emphasis>Subclasses of
|
|
|
|
<type>ClutterEffect</type></emphasis>:</para>
|
|
|
|
|
|
|
|
<itemizedlist>
|
|
|
|
|
|
|
|
<listitem>
|
|
|
|
<formalpara>
|
|
|
|
<title><type>ClutterOffscreenEffect</type></title>
|
|
|
|
|
|
|
|
<para>Use this class as a basis if you need GL textures
|
|
|
|
for your effect.</para>
|
|
|
|
</formalpara>
|
|
|
|
|
|
|
|
<para>GL textures are required for effects which need
|
|
|
|
an offscreen framebuffer. The offscreen framebuffer is
|
|
|
|
used to store a modified rendering of an actor (e.g.
|
|
|
|
with its colors altered or with deformed geometry).
|
|
|
|
This buffer is then redirected to a texture in the
|
|
|
|
stage window.</para>
|
|
|
|
|
|
|
|
<para>An example is <type>ClutterBlurEffect</type>,
|
|
|
|
which uses a GLSL fragment shader to blur an
|
|
|
|
actor's appearance in an offscreen framebuffer.</para>
|
|
|
|
|
|
|
|
<para><emphasis>Subclasses of
|
|
|
|
<type>ClutterOffscreenEffect</type></emphasis>:</para>
|
|
|
|
|
|
|
|
<itemizedlist>
|
|
|
|
|
|
|
|
<listitem>
|
|
|
|
|
|
|
|
<formalpara>
|
|
|
|
<title><type>ClutterDeformEffect</type></title>
|
|
|
|
|
|
|
|
<para>Use this base class if you want to modify
|
|
|
|
an actor's geometry, at the level of individual
|
|
|
|
vertices.</para>
|
|
|
|
</formalpara>
|
|
|
|
|
|
|
|
<para><type>ClutterDeformEffect</type> removes the
|
|
|
|
complexity of dealing with vertex-based deformations
|
|
|
|
at the OpenGL level, instead enabling you to easily plug
|
|
|
|
a deformation callback into the graphics pipeline.</para>
|
|
|
|
|
|
|
|
<para>If you are writing your own deform effects,
|
|
|
|
a good example to work from is
|
|
|
|
<type>ClutterPageTurnEffect</type>.</para>
|
|
|
|
|
|
|
|
<para>There is also a
|
|
|
|
<link linkend="effects-custom-deform">recipe which
|
|
|
|
explains how to implement a simple custom deform
|
|
|
|
effect</link> (a page fold).</para>
|
|
|
|
|
|
|
|
</listitem>
|
|
|
|
|
|
|
|
<listitem>
|
|
|
|
<formalpara>
|
|
|
|
<title><type>ClutterShaderEffect</type></title>
|
|
|
|
|
|
|
|
<para>Use this if you want to apply custom
|
|
|
|
GLSL vertex or fragment shaders to your actors.</para>
|
|
|
|
</formalpara>
|
|
|
|
|
|
|
|
<para>Writing <type>ClutterShaderEffects</type> gives
|
|
|
|
you very fine-grained control over the GL pipeline.
|
|
|
|
However, this makes them the most complex
|
|
|
|
effects to implement.</para>
|
|
|
|
|
|
|
|
<tip>
|
|
|
|
<para>If you want to write your own GLSL shaders, the
|
|
|
|
<ulink url="http://www.opengl.org/documentation/glsl/">GLSL
|
|
|
|
specification</ulink> is a good starting point.</para>
|
|
|
|
</tip>
|
|
|
|
|
|
|
|
</listitem>
|
|
|
|
|
|
|
|
</itemizedlist>
|
|
|
|
|
|
|
|
</listitem>
|
|
|
|
|
|
|
|
</itemizedlist>
|
|
|
|
|
|
|
|
</listitem>
|
|
|
|
|
|
|
|
</itemizedlist>
|
|
|
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section id="effects-introduction-using-the-built-in-effects">
|
|
|
|
<title>Using the built-in effects</title>
|
|
|
|
|
|
|
|
<para>Clutter comes with a number of built-in effects
|
|
|
|
which can easily be applied to your actors. This section
|
|
|
|
explains how to do this.</para>
|
|
|
|
|
|
|
|
<para>First, create an actor. For this
|
|
|
|
example, we use a texture loaded with an image:</para>
|
|
|
|
|
|
|
|
<informalexample>
|
|
|
|
<programlisting>
|
|
|
|
/* filename could be set from command line or constant */
|
|
|
|
gchar *filename;
|
|
|
|
|
|
|
|
/* create a texture */
|
|
|
|
ClutterActor *texture = clutter_texture_new ();
|
|
|
|
|
|
|
|
/* ...set texture size, keep aspect ratio etc... */
|
|
|
|
|
|
|
|
/* NB ignoring missing file errors here for brevity */
|
|
|
|
clutter_texture_set_from_file (CLUTTER_TEXTURE (texture),
|
|
|
|
filename,
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
/* ...add texture to the stage... */
|
|
|
|
</programlisting>
|
|
|
|
</informalexample>
|
|
|
|
|
|
|
|
<para>Next, create an instance of an effect; here, we're
|
|
|
|
creating a <type>ClutterColorizeEffect</type> with a pink tint:</para>
|
|
|
|
|
|
|
|
<informalexample>
|
|
|
|
<programlisting>
|
|
|
|
ClutterColor *pink = clutter_color_new (230, 187, 210, 255);
|
|
|
|
ClutterEffect *effect = clutter_colorize_effect_new (pink);
|
|
|
|
</programlisting>
|
|
|
|
</informalexample>
|
|
|
|
|
|
|
|
<para>Finally, apply the effect to the actor:</para>
|
|
|
|
|
|
|
|
<informalexample>
|
|
|
|
<programlisting>
|
|
|
|
clutter_actor_add_effect (texture, effect);
|
|
|
|
</programlisting>
|
|
|
|
</informalexample>
|
|
|
|
|
|
|
|
<para>The result in this case is an image colorized with
|
|
|
|
a pink tint, like this:</para>
|
|
|
|
|
|
|
|
<screenshot>
|
|
|
|
<mediaobject>
|
|
|
|
<imageobject>
|
|
|
|
<imagedata format="PNG"
|
|
|
|
fileref="images/effects-built-in.png" />
|
|
|
|
</imageobject>
|
|
|
|
<alt>
|
|
|
|
<para>Applying a <type>ClutterColorizeEffect</type>
|
|
|
|
to a texture loaded with an image (drawing by
|
|
|
|
Madeleine Smith)</para>
|
|
|
|
</alt>
|
|
|
|
</mediaobject>
|
|
|
|
</screenshot>
|
|
|
|
|
|
|
|
<para>The same set of steps applies for any of the built-in
|
|
|
|
Clutter effects. Your own custom effects classes should also
|
|
|
|
behave in a similar way: constructors should return
|
|
|
|
<type>ClutterEffect</type> instances so your effect can
|
|
|
|
be added to an actor through the standard API.</para>
|
|
|
|
|
|
|
|
<para>One further thing worth mentioning is that because an
|
|
|
|
effect is a GObject, any properties you expose for your effect
|
|
|
|
can be animated via implicit animations,
|
|
|
|
<type>ClutterAnimator</type> or <type>ClutterState</type>. For
|
|
|
|
example, the <type>ClutterPageTurnEffect</type> can be animated
|
|
|
|
by manipulating its <varname>period</varname> property. An example
|
|
|
|
of how to do this for your own effect is given in the
|
|
|
|
<link linkend="effects-custom-deform">custom deform effect
|
|
|
|
recipe</link>.</para>
|
|
|
|
|
|
|
|
<para>The full code for the <type>ClutterColorizeEffect</type>
|
|
|
|
example is below.</para>
|
|
|
|
|
|
|
|
<example id="effects-introduction-example-1">
|
|
|
|
<title>Applying a <type>ClutterColorizeEffect</type> to
|
|
|
|
a texture loaded with an image</title>
|
|
|
|
<programlisting>
|
|
|
|
<xi:include href="examples/effects-built-in.c" parse="text">
|
|
|
|
<xi:fallback>a code sample should be here... but isn't</xi:fallback>
|
|
|
|
</xi:include>
|
|
|
|
</programlisting>
|
|
|
|
</example>
|
|
|
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
</section>
|
|
|
|
|
2011-03-16 13:44:44 -04:00
|
|
|
<section id="effects-basic">
|
|
|
|
<title>Changing an actor's paint sequence using
|
|
|
|
<type>ClutterEffect</type></title>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<title>Problem</title>
|
|
|
|
|
|
|
|
<para>You want to paint on top of or under an actor in a generic
|
|
|
|
way, without editing the actor's <function>paint()</function>
|
|
|
|
implementation. Example use cases are:</para>
|
|
|
|
|
|
|
|
<itemizedlist>
|
|
|
|
<listitem>
|
|
|
|
<para>Adding a border on top of an actor.</para>
|
|
|
|
</listitem>
|
|
|
|
<listitem>
|
|
|
|
<para>Drawing a background for an actor.</para>
|
|
|
|
</listitem>
|
|
|
|
</itemizedlist>
|
|
|
|
|
|
|
|
<para>A quick way to achieve the same thing (though not
|
|
|
|
readily portable between actors) is to connect a callback
|
|
|
|
before or after an actor's <emphasis>paint</emphasis> signal.
|
|
|
|
See <link linkend="actors-paint-wrappers">this recipe</link> for
|
|
|
|
more details. However, using a <type>ClutterEffect</type>
|
|
|
|
implementation, as explained in this recipe, is the preferred
|
|
|
|
approach.</para>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<title>Solution</title>
|
|
|
|
|
|
|
|
<para>Create a subclass of the <type>ClutterEffect</type> abstract
|
|
|
|
class; then implement the <function>pre_paint()</function> and/or
|
|
|
|
<function>post_paint()</function> virtual functions. When the
|
|
|
|
effect is applied to an actor, these functions will paint
|
|
|
|
before and after the actor's own <function>paint()</function>
|
|
|
|
implementation.</para>
|
|
|
|
|
|
|
|
<note>
|
|
|
|
<para>For this solution, we implement a simple
|
|
|
|
<type>CbBackgroundEffect</type> which draws a gray rectangle
|
|
|
|
under an actor. The full source is in
|
|
|
|
<link linkend="effects-basic-example-cbbackgroundeffect">this
|
|
|
|
section</link>. To keep it simple, the effect has no properties
|
|
|
|
and isn't configurable (the background is always gray); see the
|
|
|
|
<link linkend="effects-basic-example-cbbordereffect">border
|
|
|
|
effect</link> for a more detailed implementation with GObject
|
|
|
|
trimmings.</para>
|
|
|
|
</note>
|
|
|
|
|
|
|
|
<para>First, create a <type>ClutterEffect</type> subclass. This
|
|
|
|
requires the trappings of a GObject class; in particular,
|
|
|
|
it needs a private struct to hold the effect's state. This
|
|
|
|
should include any <type>CoglMaterials</type>,
|
|
|
|
<type>CoglColors</type> or other private member variables
|
|
|
|
you intend to use to draw the effect.</para>
|
|
|
|
|
|
|
|
<para>In the case of the background effect, we have a background
|
|
|
|
<type>CoglMaterial</type> and a <type>CoglColor</type> for that
|
|
|
|
material:</para>
|
|
|
|
|
|
|
|
<informalexample>
|
|
|
|
<programlisting>
|
|
|
|
struct _CbBackgroundEffectPrivate
|
|
|
|
{
|
|
|
|
CoglMaterial *background;
|
|
|
|
CoglColor *color;
|
|
|
|
};
|
|
|
|
</programlisting>
|
|
|
|
</informalexample>
|
|
|
|
|
|
|
|
<para>In the <function>init()</function> function for objects of
|
|
|
|
your class, create any Cogl resources which you need to draw the
|
|
|
|
effect. In the case of the background effect,
|
|
|
|
we need to create the <type>CoglMaterial</type> and
|
|
|
|
<type>CoglColor</type> for the private struct:</para>
|
|
|
|
|
|
|
|
<informalexample>
|
|
|
|
<programlisting>
|
|
|
|
<![CDATA[
|
|
|
|
static void
|
|
|
|
cb_background_effect_init (CbBackgroundEffect *self)
|
|
|
|
{
|
|
|
|
/* get the private struct for the object
|
|
|
|
CbBackgroundEffectPrivate *priv;
|
|
|
|
priv = self->priv = CB_BACKGROUND_EFFECT_GET_PRIVATE (self);
|
|
|
|
|
|
|
|
/* create the background material */
|
|
|
|
priv->background = cogl_material_new ();
|
|
|
|
|
|
|
|
/* gray color for filling the background material */
|
|
|
|
priv->color = cogl_color_new ();
|
|
|
|
cogl_color_init_from_4ub (priv->color, 122, 122, 122, 255);
|
|
|
|
|
|
|
|
/* set the color on the material; NB this isn't configurable
|
|
|
|
* for this effect, and is always gray
|
|
|
|
*/
|
|
|
|
cogl_material_set_color (priv->background, priv->color);
|
|
|
|
}
|
|
|
|
]]>
|
|
|
|
</programlisting>
|
|
|
|
</informalexample>
|
|
|
|
|
|
|
|
<para>Optionally, you can create GObject properties for
|
|
|
|
the class, if you want a configurable effect: see
|
|
|
|
<link linkend="effects-basic-discussion-properties">this
|
|
|
|
section</link> for details.</para>
|
|
|
|
|
|
|
|
<para>The <function>dispose()</function> function for your effect
|
|
|
|
should clean up any Cogl resources:</para>
|
|
|
|
|
|
|
|
<informalexample>
|
|
|
|
<programlisting>
|
|
|
|
<![CDATA[
|
|
|
|
static void
|
|
|
|
cb_background_effect_dispose (GObject *gobject)
|
|
|
|
{
|
|
|
|
CbBackgroundEffectPrivate *priv = CB_BACKGROUND_EFFECT (gobject)->priv;
|
|
|
|
|
|
|
|
if (priv->background != COGL_INVALID_HANDLE)
|
|
|
|
{
|
|
|
|
cogl_handle_unref (priv->background);
|
|
|
|
priv->background = COGL_INVALID_HANDLE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (priv->color != NULL)
|
|
|
|
{
|
|
|
|
cogl_color_free (priv->color);
|
|
|
|
priv->color = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
G_OBJECT_CLASS (cb_background_effect_parent_class)->dispose (gobject);
|
|
|
|
}
|
|
|
|
]]>
|
|
|
|
</programlisting>
|
|
|
|
</informalexample>
|
|
|
|
|
|
|
|
<para>Now, the important part: implement <function>pre_paint()</function>
|
|
|
|
and/or <function>post_paint()</function>, using Cogl to draw on the
|
2011-03-17 11:45:57 -04:00
|
|
|
material(s) set up for the effect.</para>
|
2011-03-16 13:44:44 -04:00
|
|
|
|
|
|
|
<para>For the background effect, we implement <function>pre_paint()</function>,
|
|
|
|
to draw a gray rectangle under the actor:</para>
|
|
|
|
|
|
|
|
<informalexample>
|
|
|
|
<programlisting>
|
|
|
|
<![CDATA[
|
|
|
|
/* note that if pre_paint() returns FALSE
|
|
|
|
* any post_paint() defined for the effect will not be called
|
|
|
|
*/
|
|
|
|
static gboolean
|
|
|
|
cb_background_effect_pre_paint (ClutterEffect *self)
|
|
|
|
{
|
|
|
|
ClutterActor *actor;
|
|
|
|
gfloat width;
|
|
|
|
gfloat height;
|
|
|
|
CbBackgroundEffectPrivate *priv;
|
|
|
|
|
|
|
|
priv = CB_BACKGROUND_EFFECT (self)->priv;
|
|
|
|
|
|
|
|
/* get the associated actor's dimensions */
|
|
|
|
actor = clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (self));
|
|
|
|
clutter_actor_get_size (actor, &width, &height);
|
|
|
|
|
|
|
|
/* draw a Cogl rectangle in the background using the default color */
|
|
|
|
cogl_set_source (priv->background);
|
|
|
|
|
|
|
|
/* the rectangle has the same dimensions as the actor */
|
|
|
|
cogl_rectangle (0, 0, width, height);
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
]]>
|
|
|
|
</programlisting>
|
|
|
|
</informalexample>
|
|
|
|
|
|
|
|
<para>Now, in the <function>init()</function> function for the
|
|
|
|
effect <emphasis>class</emphasis>, assign your implementations to the
|
|
|
|
virtual methods of the <type>ClutterEffect</type> abstract class:</para>
|
|
|
|
|
|
|
|
<informalexample>
|
|
|
|
<programlisting>
|
|
|
|
<![CDATA[
|
|
|
|
static void
|
|
|
|
cb_background_effect_class_init (CbBackgroundEffectClass *klass)
|
|
|
|
{
|
|
|
|
ClutterEffectClass *effect_class = CLUTTER_EFFECT_CLASS (klass);
|
|
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
|
|
|
|
|
|
effect_class->pre_paint = cb_background_effect_pre_paint;
|
|
|
|
gobject_class->dispose = cb_background_effect_dispose;
|
|
|
|
|
|
|
|
g_type_class_add_private (klass, sizeof (CbBackgroundEffectPrivate));
|
|
|
|
}
|
|
|
|
]]>
|
|
|
|
</programlisting>
|
|
|
|
</informalexample>
|
|
|
|
|
|
|
|
<para>If you intend to make your effect reusable, provide
|
|
|
|
a public constructor (as is done for the example effects in this
|
|
|
|
recipe):</para>
|
|
|
|
|
|
|
|
<informalexample>
|
|
|
|
<programlisting>
|
|
|
|
ClutterEffect *
|
|
|
|
cb_background_effect_new ()
|
|
|
|
{
|
|
|
|
return g_object_new (CB_TYPE_BACKGROUND_EFFECT,
|
|
|
|
NULL);
|
|
|
|
}
|
|
|
|
</programlisting>
|
|
|
|
</informalexample>
|
|
|
|
|
|
|
|
<para>The effect is now ready to be used. The application code
|
|
|
|
for applying your effect to an actor is the same as for any
|
|
|
|
other effect:</para>
|
|
|
|
|
|
|
|
<informalexample>
|
|
|
|
<programlisting>
|
|
|
|
ClutterActor *texture;
|
|
|
|
ClutterEffect *background_effect;
|
|
|
|
|
|
|
|
/* ...initialize texture, load image file etc... */
|
|
|
|
|
|
|
|
/* create a gray background effect */
|
|
|
|
background_effect = cb_background_effect_new ();
|
|
|
|
|
|
|
|
/* apply the effect to the actor */
|
|
|
|
clutter_actor_add_effect (texture, background_effect);
|
|
|
|
</programlisting>
|
|
|
|
</informalexample>
|
|
|
|
|
|
|
|
<para>Below is an example of applying this effect to a texture loaded
|
|
|
|
with an image; the image has a transparent background, so the
|
|
|
|
background is visible through it. The screenshot is from
|
|
|
|
the <link linkend="effects-basic-example-5">example
|
|
|
|
application</link>:</para>
|
|
|
|
|
|
|
|
<screenshot>
|
|
|
|
<mediaobject>
|
|
|
|
<imageobject>
|
|
|
|
<imagedata format="PNG"
|
|
|
|
fileref="images/effects-basic-background.png" />
|
|
|
|
</imageobject>
|
|
|
|
<alt>
|
|
|
|
<para>Applying <type>CbBackgroundEffect</type>
|
|
|
|
to a texture loaded with an image that has a transparent
|
|
|
|
background</para>
|
|
|
|
</alt>
|
|
|
|
</mediaobject>
|
|
|
|
</screenshot>
|
|
|
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<title>Discussion</title>
|
|
|
|
|
|
|
|
<para>A basic <type>ClutterEffect</type> is particularly useful for
|
|
|
|
amending the appearance of an actor on the fly: for example,
|
|
|
|
to highlight an actor in response to a button presses. This
|
|
|
|
<emphasis>could</emphasis> be done by creating a custom widget
|
|
|
|
whose appearance could be toggled. But what if you wanted to make
|
|
|
|
an arbitrary actor's appearance "togglable"? A generic effect
|
|
|
|
in the style of the border effect in this recipe can be applied
|
|
|
|
to any actor, and easily toggled by enabling/disabling the
|
|
|
|
effect.</para>
|
|
|
|
|
|
|
|
<para><type>ClutterEffect</type> works best where
|
|
|
|
you want to overlay or underlay the actor with Cogl paths or
|
|
|
|
primitives, without changing the actor's geometry. If you want
|
|
|
|
to do complicated geometry transformations, or other subtle
|
|
|
|
manipulations of an actor's appearance, it is better to use
|
|
|
|
a <type>ClutterEffect</type> subclass like
|
|
|
|
<type>ClutterOffscreenEffect</type>, <type>ClutterDeformEffect</type>,
|
|
|
|
or <type>ClutterShaderEffect</type>.</para>
|
|
|
|
|
|
|
|
<para>In a similar vein, when a <type>ClutterEffect</type> is
|
|
|
|
applied to an actor, the effect shouldn't paint outside the actor's
|
|
|
|
allocation. However, if the effect provides a
|
|
|
|
<function>get_paint_volume()</function> implementation which
|
|
|
|
returns a volume larger than the actor's allocation, the effect
|
|
|
|
<emphasis>can</emphasis> paint anywhere within that volume. Though
|
|
|
|
in most cases, creating a custom paint volume is only going to be
|
|
|
|
useful for offscreen effects, where you are changing the
|
|
|
|
actor's geometry.</para>
|
|
|
|
|
|
|
|
<section id="effects-basic-discussion-properties">
|
|
|
|
<title>Effect properties</title>
|
|
|
|
|
|
|
|
<para>If your effect has GObject properties, you should
|
|
|
|
ensure that an actor associated with the effect is queued
|
|
|
|
for a redraw when those properties change. (You only need to
|
|
|
|
do this for properties which change the effect's appearance;
|
|
|
|
but this is likely to include most of an effect's properties.)</para>
|
|
|
|
|
|
|
|
<para>In most cases, you're likely define standard GObject
|
|
|
|
properties for the class; for example,
|
|
|
|
<link linkend="effects-basic-example-2"><type>CbBorderEffect</type></link>
|
|
|
|
defines a <varname>width</varname> property like this:</para>
|
|
|
|
|
|
|
|
<informalexample>
|
|
|
|
<programlisting>
|
|
|
|
<![CDATA[
|
|
|
|
static void
|
|
|
|
cb_border_effect_class_init (CbBorderEffectClass *klass)
|
|
|
|
{
|
|
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
|
|
GParamSpec *pspec;
|
|
|
|
|
|
|
|
/* ...more class initialization code here... */
|
|
|
|
|
|
|
|
pspec = g_param_spec_float ("width",
|
|
|
|
"Width",
|
|
|
|
"The width of the border (in pixels)",
|
|
|
|
1.0, 100.0,
|
|
|
|
10.0,
|
|
|
|
G_PARAM_READWRITE);
|
|
|
|
obj_props[PROP_WIDTH] = pspec;
|
|
|
|
g_object_class_install_property (gobject_class, PROP_WIDTH, pspec);
|
|
|
|
|
|
|
|
/* ...more property definitions...*/
|
|
|
|
}
|
|
|
|
]]>
|
|
|
|
</programlisting>
|
|
|
|
</informalexample>
|
|
|
|
|
|
|
|
<para>It also defines a standard GObject
|
|
|
|
<function>set_property()</function> function for
|
|
|
|
<varname>width</varname>:</para>
|
|
|
|
|
|
|
|
<informalexample>
|
|
|
|
<programlisting>
|
|
|
|
<![CDATA[
|
|
|
|
static void
|
|
|
|
cb_border_effect_set_property (GObject *gobject,
|
|
|
|
guint prop_id,
|
|
|
|
const GValue *value,
|
|
|
|
GParamSpec *pspec)
|
|
|
|
{
|
|
|
|
CbBorderEffect *effect = CB_BORDER_EFFECT (gobject);
|
|
|
|
|
|
|
|
switch (prop_id)
|
|
|
|
{
|
|
|
|
/* ...other cases here ... */
|
|
|
|
|
|
|
|
case PROP_WIDTH:
|
|
|
|
cb_border_effect_set_width (effect, g_value_get_float (value));
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* ...default case ... */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
]]>
|
|
|
|
</programlisting>
|
|
|
|
</informalexample>
|
|
|
|
|
|
|
|
<para>Note that this calls
|
|
|
|
<function>cb_border_effect_set_width()</function>, which is
|
|
|
|
also exposed in the public API. This is where the
|
|
|
|
<varname>width</varname> member variable is actually set in
|
|
|
|
the private struct; and also where the redraw for the actor
|
|
|
|
associated with the effect should be queued:</para>
|
|
|
|
|
|
|
|
<informalexample>
|
|
|
|
<programlisting>
|
|
|
|
<![CDATA[
|
|
|
|
/* queues a redraw of the actor associated with the effect, if there is one */
|
|
|
|
static void
|
|
|
|
cb_border_effect_update (CbBorderEffect *self)
|
|
|
|
{
|
|
|
|
ClutterActor *actor = clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (self));
|
|
|
|
|
|
|
|
/* this guard is necessary as an effect's properties can be manipulated
|
|
|
|
* before it has an actor associated with it
|
|
|
|
*/
|
|
|
|
if (actor != NULL)
|
|
|
|
clutter_actor_queue_redraw (actor);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* public setter for the width property, which calls the update function */
|
|
|
|
void
|
|
|
|
cb_border_effect_set_width (CbBorderEffect *self,
|
|
|
|
gfloat width)
|
|
|
|
{
|
|
|
|
CbBorderEffectPrivate *priv;
|
|
|
|
|
|
|
|
g_return_if_fail (CB_IS_BORDER_EFFECT (self));
|
|
|
|
|
|
|
|
priv = CB_BORDER_EFFECT_GET_PRIVATE (self);
|
|
|
|
|
|
|
|
priv->width = width;
|
|
|
|
|
|
|
|
/* the property has been updated, so queue a redraw of the actor (if set) */
|
|
|
|
cb_border_effect_update (self);
|
|
|
|
}
|
|
|
|
]]>
|
|
|
|
</programlisting>
|
|
|
|
</informalexample>
|
|
|
|
|
|
|
|
<para>Any other property setters which affect the associated
|
|
|
|
actor's appearance (i.e. color in the case of
|
|
|
|
<type>CbBorderEffect</type>) should also call the update
|
|
|
|
function after setting the property.</para>
|
|
|
|
|
|
|
|
<tip>
|
|
|
|
<para>If your effect exposes GObject properties in this way,
|
|
|
|
it can also be animated with the Clutter animation API as usual.
|
|
|
|
For example, you could animate the border effect in this recipe
|
|
|
|
so that the border gradually becomes thinner or thicker.</para>
|
|
|
|
</tip>
|
|
|
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section id="effects-basic-example">
|
|
|
|
<title>Full example</title>
|
|
|
|
|
|
|
|
<para>The example application applies two effects to a
|
|
|
|
group of <type>ClutterTextures</type>:</para>
|
|
|
|
|
|
|
|
<itemizedlist>
|
|
|
|
<listitem>
|
|
|
|
<para>A <type>CbBackgroundEffect</type> which draws a gray
|
|
|
|
background under each actor. The effect is implemented in
|
|
|
|
<link linkend="effects-basic-example-1">a header
|
|
|
|
file</link> and <link linkend="effects-basic-example-2">a C
|
|
|
|
code file</link>.</para>
|
|
|
|
</listitem>
|
|
|
|
<listitem>
|
|
|
|
<para>A <type>CbBorderEffect</type> which draws a
|
|
|
|
red border on top of an actor; this is toggled by clicking
|
|
|
|
on the actor. The effect is implemented in
|
|
|
|
<link linkend="effects-basic-example-3">a header
|
|
|
|
file</link> and <link linkend="effects-basic-example-4">a C
|
|
|
|
code file</link>.</para>
|
|
|
|
</listitem>
|
|
|
|
</itemizedlist>
|
|
|
|
|
|
|
|
<para>The <link linkend="effects-basic-example-5">application</link>
|
|
|
|
creates textures from the file paths specified
|
|
|
|
on the command line then applies both of these effects to
|
|
|
|
each texture. In the case of the <type>CbBorderEffect</type>,
|
|
|
|
a 5 pixel red border is applied; this is also disabled by default,
|
|
|
|
and enabled when a texture is clicked.</para>
|
|
|
|
|
|
|
|
<para>Here is an example of the output when the application is loaded
|
|
|
|
with four images:</para>
|
|
|
|
|
|
|
|
<screenshot>
|
|
|
|
<mediaobject>
|
|
|
|
<imageobject>
|
|
|
|
<imagedata format="PNG"
|
|
|
|
fileref="images/effects-basic.png" />
|
|
|
|
</imageobject>
|
|
|
|
<alt>
|
|
|
|
<para>Applying <type>CbBackgroundEffect</type>
|
|
|
|
and a togglable <type>CbBorderEffect</type>
|
|
|
|
to a several textures</para>
|
|
|
|
</alt>
|
|
|
|
</mediaobject>
|
|
|
|
</screenshot>
|
|
|
|
|
|
|
|
<section id="effects-basic-example-cbbackgroundeffect">
|
|
|
|
<title><type>CbBackgroundEffect</type></title>
|
|
|
|
|
|
|
|
<example id="effects-basic-example-1">
|
|
|
|
<title><filename>cb-background-effect.h</filename> (header file)</title>
|
|
|
|
<programlisting>
|
|
|
|
<xi:include href="examples/cb-background-effect.h" parse="text">
|
|
|
|
<xi:fallback>a code sample should be here... but isn't</xi:fallback>
|
|
|
|
</xi:include>
|
|
|
|
</programlisting>
|
|
|
|
</example>
|
|
|
|
|
|
|
|
<example id="effects-basic-example-2">
|
|
|
|
<title><filename>cb-background-effect.c</filename> (code file)</title>
|
|
|
|
<programlisting>
|
|
|
|
<xi:include href="examples/cb-background-effect.c" parse="text">
|
|
|
|
<xi:fallback>a code sample should be here... but isn't</xi:fallback>
|
|
|
|
</xi:include>
|
|
|
|
</programlisting>
|
|
|
|
</example>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section id="effects-basic-example-cbbordereffect">
|
|
|
|
<title><type>CbBorderEffect</type></title>
|
|
|
|
|
|
|
|
<para>This is a more sophisticated effect with configurable
|
|
|
|
border color and width.</para>
|
|
|
|
|
|
|
|
<example id="effects-basic-example-3">
|
|
|
|
<title><filename>cb-border-effect.h</filename> (header file)</title>
|
|
|
|
<programlisting>
|
|
|
|
<xi:include href="examples/cb-border-effect.h" parse="text">
|
|
|
|
<xi:fallback>a code sample should be here... but isn't</xi:fallback>
|
|
|
|
</xi:include>
|
|
|
|
</programlisting>
|
|
|
|
</example>
|
|
|
|
|
|
|
|
<example id="effects-basic-example-4">
|
|
|
|
<title><filename>cb-border-effect.c</filename> (code file)</title>
|
|
|
|
<programlisting>
|
|
|
|
<xi:include href="examples/cb-border-effect.c" parse="text">
|
|
|
|
<xi:fallback>a code sample should be here... but isn't</xi:fallback>
|
|
|
|
</xi:include>
|
|
|
|
</programlisting>
|
|
|
|
</example>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section id="effects-basic-example-application">
|
|
|
|
<title>Application</title>
|
|
|
|
|
|
|
|
<example id="effects-basic-example-5">
|
|
|
|
<title>Application which applies <type>CbBorderEffect</type>
|
|
|
|
and <type>CbBackgroundEffect</type> to a group of
|
|
|
|
<type>ClutterTextures</type>.</title>
|
|
|
|
<programlisting>
|
|
|
|
<xi:include href="examples/effects-basic.c" parse="text">
|
|
|
|
<xi:fallback>a code sample should be here... but isn't</xi:fallback>
|
|
|
|
</xi:include>
|
|
|
|
</programlisting>
|
|
|
|
</example>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
</section>
|
|
|
|
|
2011-02-21 11:43:34 -05:00
|
|
|
<section id="effects-custom-deform">
|
|
|
|
<title>Creating and animating a custom <type>ClutterDeformEffect</type></title>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<title>Problem</title>
|
|
|
|
|
|
|
|
<para>You want to deform an actor's geometry: for example,
|
|
|
|
to make it appear stretched, twisted or folded.</para>
|
|
|
|
|
|
|
|
<para>This recipe demonstrates how to do this with a simple page
|
|
|
|
fold effect, which folds one half of the actor over its other half.</para>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section id="effects-custom-deform-solution">
|
|
|
|
<title>Solution</title>
|
|
|
|
|
|
|
|
<para>Subclass <type>ClutterDeformEffect</type> and
|
|
|
|
implement a <function>deform_vertex()</function> function
|
|
|
|
to modify the actor's vertices.</para>
|
|
|
|
|
|
|
|
<para>The signature for <function>deform_vertex()</function>
|
|
|
|
is:</para>
|
|
|
|
|
|
|
|
<informalexample>
|
|
|
|
<programlisting>
|
|
|
|
void
|
|
|
|
deform_vertex (ClutterDeformEffect *effect,
|
|
|
|
gfloat width,
|
|
|
|
gfloat height,
|
|
|
|
CoglTextureVertex *vertex);
|
|
|
|
</programlisting>
|
|
|
|
</informalexample>
|
|
|
|
|
|
|
|
<para>The <varname>width</varname> and <varname>height</varname>
|
|
|
|
are the width and height of the target material, stored in
|
|
|
|
the offscreen buffer. Usually the target material's size will
|
|
|
|
match the actor's transformed size; however, if the effect
|
|
|
|
implements <function>create_texture()</function>, the target
|
|
|
|
material's size may differ from the actor's transformed size.</para>
|
|
|
|
|
|
|
|
<para>The <varname>vertex</varname> contains the position
|
|
|
|
and color of a vertex, to be deformed by your effect.
|
|
|
|
Your <function>deform_vertex()</function>
|
|
|
|
function should modify the member variables of this
|
|
|
|
<type>CoglTextureVertex</type> in place. Usually, this will
|
|
|
|
mean modifying the <varname>x</varname>, <varname>y</varname>
|
|
|
|
and <varname>y</varname> member variables of the vertex,
|
|
|
|
which describe its position in 3D space.</para>
|
|
|
|
|
|
|
|
<para>The example function below, taken from
|
|
|
|
<link linkend="effects-custom-deform-example-2">the
|
|
|
|
full example</link>, applies a transformation to vertices falling
|
|
|
|
in the "right-hand" half of the actor (i.e. vertices with an
|
|
|
|
<varname>x</varname> value greater than or equal to half the
|
|
|
|
width of the actor).</para>
|
|
|
|
|
|
|
|
<informalexample>
|
|
|
|
<programlisting>
|
|
|
|
static void
|
|
|
|
cb_page_fold_effect_deform_vertex (ClutterDeformEffect *effect,
|
|
|
|
gfloat width,
|
|
|
|
gfloat height,
|
|
|
|
CoglTextureVertex *vertex)
|
|
|
|
{
|
|
|
|
CbPageFoldEffectPrivate *priv = CB_PAGE_FOLD_EFFECT (effect)->priv;
|
|
|
|
|
|
|
|
/* the rotation angle is modified by the percentage progress of the fold,
|
|
|
|
* as represented by the period variable
|
|
|
|
*/
|
|
|
|
gfloat radians = (priv->angle * priv->period) / (180.0f / G_PI);
|
|
|
|
|
|
|
|
/* rotate from the center of the actor on the y axis */
|
|
|
|
gfloat adjusted_x = vertex->x - (width / 2);
|
|
|
|
|
|
|
|
/* only rotate vertices to the right of the middle of the actor */
|
|
|
|
if (adjusted_x >= 0.0)
|
|
|
|
{
|
|
|
|
vertex->x = (vertex->z * sin (radians))
|
|
|
|
+ (adjusted_x * cos (radians))
|
|
|
|
+ width / 2;
|
|
|
|
|
|
|
|
/* NB add 1 to z to prevent "z fighting"; otherwise, when fully-folded
|
|
|
|
* the image has "stripes" where vertices from the folded part
|
|
|
|
* of the actor interfere with vertices from the unfolded part
|
|
|
|
*/
|
|
|
|
vertex->z = (vertex->z * cos (radians))
|
|
|
|
+ (adjusted_x * sin (radians))
|
|
|
|
+ 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* adjust depth of all vertices so they fit inside the actor while folding;
|
|
|
|
* this has the effect of making the image smaller within the texture,
|
|
|
|
* but does produce a cleaner fold animation
|
|
|
|
*/
|
|
|
|
vertex->z -= width / 2;
|
|
|
|
}
|
|
|
|
</programlisting>
|
|
|
|
</informalexample>
|
|
|
|
|
|
|
|
<para>Note that this effect has two properties set in its
|
|
|
|
constructor or through setters:</para>
|
|
|
|
|
|
|
|
<orderedlist>
|
|
|
|
<listitem>
|
|
|
|
<para><varname>angle</varname>, representing the angle of
|
|
|
|
the full fold; for the actor to fully fold in half, this
|
|
|
|
would be set to 180.0</para>
|
|
|
|
</listitem>
|
|
|
|
|
|
|
|
<listitem>
|
|
|
|
<para><varname>period</varname>, representing the percentage
|
|
|
|
of the fold to apply</para>
|
|
|
|
</listitem>
|
|
|
|
</orderedlist>
|
|
|
|
|
|
|
|
<para>As well as rotating the vertex, the
|
|
|
|
<function>deform_vertex()</function> function also shifts
|
|
|
|
the <varname>z</varname> coordinate "up" by 1
|
|
|
|
(towards the viewpoint) for vertices on the right-hand side of the
|
|
|
|
actor. This is so that the "folded over" vertices
|
|
|
|
are above vertices on the left-hand side. Without this small
|
|
|
|
shift, the vertices interfere with each other, which can cause striping
|
|
|
|
artefacts.</para>
|
|
|
|
|
|
|
|
<para><emphasis>All</emphasis> vertices are also shifted "down",
|
|
|
|
so that the the folding part of the actor remains within the texture.
|
|
|
|
Otherwise the part which is folding may be clipped to the allocation of
|
|
|
|
the actor.</para>
|
|
|
|
|
|
|
|
<para>This effect can now be applied to an actor, using the
|
|
|
|
approach
|
|
|
|
<link linkend="effects-introduction-using-the-built-in-effects">outlined
|
|
|
|
in the introduction</link>. The result looks like this when
|
|
|
|
<varname>period</varname> is set to 0.25 and <varname>angle</varname>
|
|
|
|
to 180.0 (i.e. the page is folded by 45 degrees):</para>
|
|
|
|
|
|
|
|
<screenshot>
|
|
|
|
<mediaobject>
|
|
|
|
<imageobject>
|
|
|
|
<imagedata format="PNG"
|
|
|
|
fileref="images/effects-custom-deform.png" />
|
|
|
|
</imageobject>
|
|
|
|
<alt>
|
|
|
|
<para>Applying a custom <type>ClutterDeformEffect</type>
|
|
|
|
to a texture loaded with an image</para>
|
|
|
|
</alt>
|
|
|
|
</mediaobject>
|
|
|
|
</screenshot>
|
|
|
|
|
|
|
|
<para>Because the effect is a GObject which exposes its
|
|
|
|
properties, it can easily be animated, as described in
|
|
|
|
<link linkend="effects-custom-deform-discussion-animating">the
|
|
|
|
discussion section</link>.</para>
|
|
|
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section id="effects-custom-deform-discussion">
|
|
|
|
<title>Discussion</title>
|
|
|
|
|
|
|
|
<para>A deform effect processes an actor as follows:</para>
|
|
|
|
|
|
|
|
<itemizedlist>
|
|
|
|
|
|
|
|
<listitem>
|
|
|
|
<para>The actor is divided into a series of
|
|
|
|
triangular tiles. The number of
|
|
|
|
horizontal and vertical tiles is configurable;
|
|
|
|
more tiles implies more vertices. See
|
|
|
|
<link linkend="effects-custom-deform-discussion-tiles">this
|
|
|
|
section</link> for more details about tiles.</para>
|
|
|
|
</listitem>
|
|
|
|
|
|
|
|
<listitem>
|
|
|
|
<para>The position of each vertex of each
|
|
|
|
tile is then modified (or not) by the
|
|
|
|
<function>deform_vertex()</function> function. In this
|
|
|
|
function, you can change the vertex's position
|
|
|
|
(<varname>x</varname>, <varname>y</varname>,
|
|
|
|
<varname>z</varname> coordinates). You can also
|
|
|
|
modify the color at the vertex if desired.</para>
|
|
|
|
|
|
|
|
<para>The resulting deformed vertices are stored
|
|
|
|
in an offscreen buffer.</para>
|
|
|
|
</listitem>
|
|
|
|
|
|
|
|
<listitem>
|
|
|
|
<para>Once the deformation has been applied to
|
|
|
|
all vertices, the content of the offscreen buffer
|
|
|
|
is painted at the onscreen position of the actor.</para>
|
|
|
|
</listitem>
|
|
|
|
|
|
|
|
</itemizedlist>
|
|
|
|
|
|
|
|
<para>You may find it useful to visualise this process by imagining
|
|
|
|
your actor's surface as a net, composed of triangles. (Something
|
|
|
|
like a fishing net, not a mathematical one.) At each corner of
|
|
|
|
each triangle is a marble; and between each pair of corners
|
|
|
|
is an infinitely flexible length of elastic. Moving a marble
|
|
|
|
doesn't change the position of its neighbours; it just stretches
|
|
|
|
or relaxes the elastic.</para>
|
|
|
|
|
|
|
|
<para>In this analogy, the marbles are the vertices; and the
|
|
|
|
surfaces between the marbles, bordered by triangles of
|
|
|
|
elastic, are the tiles. More triangles (tiles) means more
|
|
|
|
marbles (vertices).</para>
|
|
|
|
|
|
|
|
<para>When you create a <type>ClutterDeformEffect</type>,
|
|
|
|
think of it as specifying movements of marbles in the net.
|
|
|
|
Changing the position of a vertex corresponds to moving a marble
|
|
|
|
up/down (-/+ <varname>y</varname> position), left/right
|
|
|
|
(-/+ <varname>x</varname> position) or away/towards
|
|
|
|
you (-/+ <varname>z</varname> position) (ignoring color for the
|
|
|
|
moment).</para>
|
|
|
|
|
|
|
|
<para>Now imagine that you are asked to fold the whole net of
|
|
|
|
marbles; but you can't just grab the edge of the net and pull
|
|
|
|
it over: you can only move one marble at a time. However, once moved,
|
|
|
|
each marble magically stays where you put it in 3D space.</para>
|
|
|
|
|
|
|
|
<para>To do this, you could project where each marble would be if
|
|
|
|
you could fold the whole sheet in one go; then move the
|
|
|
|
marbles one by one to their projected positions. Even though
|
|
|
|
you'd be moving the marbles one at a time, it would eventually
|
|
|
|
look as though you'd folded the whole net with a single movement.</para>
|
|
|
|
|
|
|
|
<para>When you write a <type>ClutterDeformEffect</type>, you have
|
|
|
|
to accomplish a similar feat: change the shape of an actor
|
|
|
|
by individually modifying the positions of points on its surface. In
|
|
|
|
most cases, your <function>deform_vertex()</function> implementation
|
|
|
|
can take advantage of an existing geometric transformation
|
|
|
|
method to achieve this. (For example, the page fold in this recipe
|
|
|
|
is based on equations from p.412 of <citetitle pubwork="book">Computer
|
|
|
|
Graphics (C Version), 2nd Edition</citetitle> by Hearn and
|
|
|
|
Baker, 1996.)</para>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<title>Customising the back material</title>
|
|
|
|
|
|
|
|
<para>When you set up a deform effect, you
|
|
|
|
can optionally specify a material to use for the "back" of
|
|
|
|
any actor it is applied to.</para>
|
|
|
|
|
|
|
|
<para>If you think of an actor as a sheet of paper with a
|
|
|
|
picture on it, specifying a back is similar to turning the
|
|
|
|
sheet of paper over (rotating it around the
|
|
|
|
<varname>y</varname> axis) and drawing another picture on
|
|
|
|
the other side. If you then folded or twisted the paper,
|
|
|
|
you would be able to see parts of the pictures on both the
|
|
|
|
front and back of the paper.</para>
|
|
|
|
|
|
|
|
<para>Similarly, during deformation of an actor, if any
|
|
|
|
vertices of the actor are deformed such that the actor's surface
|
|
|
|
is folded or twisted over itself, parts of its back
|
|
|
|
become visible. If you set a back material, you will see parts
|
|
|
|
of that where the surface is folded over. If you don't set a back
|
|
|
|
material, you will instead see mirror images of parts of the actor's
|
|
|
|
front: as if the actor was flexible stained glass, rather than paper.
|
|
|
|
You can see this if you watch the animation in
|
|
|
|
<link linkend="effects-custom-deform-discussion-animating">this
|
|
|
|
section</link>.</para>
|
|
|
|
|
|
|
|
<para>The back material should be an instance of
|
|
|
|
<type>CoglMaterial</type>. You can either create this via
|
|
|
|
the Cogl API directly; or indirectly through the Clutter API
|
|
|
|
(for example, by getting the material from a
|
|
|
|
<type>ClutterTexture</type>). The code below gives an example
|
|
|
|
of how to do the latter:</para>
|
|
|
|
|
|
|
|
<informalexample>
|
|
|
|
<programlisting>
|
|
|
|
<![CDATA[
|
|
|
|
/* create a texture */
|
|
|
|
ClutterActor *back = clutter_texture_new ();
|
|
|
|
clutter_texture_set_keep_aspect_ratio (CLUTTER_TEXTURE (back), TRUE);
|
|
|
|
clutter_actor_set_width (back, 400);
|
|
|
|
|
|
|
|
/* load image into texture (ignoring errors for brevity) */
|
|
|
|
clutter_texture_set_from_file (CLUTTER_TEXTURE (back),
|
|
|
|
back_image_file,
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
/* get a handle to the texture's Cogl material */
|
|
|
|
CoglHandle material = clutter_texture_get_cogl_material (CLUTTER_TEXTURE (back));
|
|
|
|
|
|
|
|
/* cast the effect to ClutterDeformEffect and set its back material
|
|
|
|
* to the handle
|
|
|
|
*/
|
|
|
|
clutter_deform_effect_set_back_material (CLUTTER_DEFORM_EFFECT (effect),
|
|
|
|
material);
|
|
|
|
]]>
|
|
|
|
</programlisting>
|
|
|
|
</informalexample>
|
|
|
|
|
|
|
|
<para>See the <type>ClutterDeformEffect</type> API reference
|
|
|
|
for more details about back materials.</para>
|
|
|
|
|
|
|
|
<para>Here's a screenshot of the
|
|
|
|
<link linkend="effects-custom-deform-example-3">example</link>
|
|
|
|
with the addition of a back material, folded at an angle
|
|
|
|
of 60 degrees:</para>
|
|
|
|
|
|
|
|
<screenshot>
|
|
|
|
<mediaobject>
|
|
|
|
<imageobject>
|
|
|
|
<imagedata format="PNG"
|
|
|
|
fileref="images/effects-custom-deform-back-material.png" />
|
|
|
|
</imageobject>
|
|
|
|
<alt>
|
|
|
|
<para>Applying a custom <type>ClutterDeformEffect</type>
|
|
|
|
to a texture loaded with an image</para>
|
|
|
|
</alt>
|
|
|
|
</mediaobject>
|
|
|
|
</screenshot>
|
|
|
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section id="effects-custom-deform-discussion-animating">
|
|
|
|
<title>Animating a custom deform effect</title>
|
|
|
|
|
|
|
|
<para>Clutter's animation API can animate any GObject which
|
|
|
|
exposes its properties. In the case of the page fold effect,
|
|
|
|
we can expose the <varname>period</varname> property using
|
|
|
|
standard GObject property installation:</para>
|
|
|
|
|
|
|
|
<informalexample>
|
|
|
|
<programlisting>
|
|
|
|
/* GObject class init */
|
|
|
|
static void
|
|
|
|
cb_page_fold_effect_class_init (CbPageFoldEffectClass *klass)
|
|
|
|
{
|
|
|
|
GParamSpec *pspec;
|
|
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
|
|
|
|
|
|
/* ...other class setup code... */
|
|
|
|
|
|
|
|
/* expose the period as a GObject property */
|
|
|
|
pspec = g_param_spec_double ("period",
|
|
|
|
"Period",
|
|
|
|
"The period of the page fold",
|
|
|
|
0.0, 1.0,
|
|
|
|
0.0,
|
|
|
|
G_PARAM_READWRITE);
|
|
|
|
obj_props[PROP_PERIOD] = pspec;
|
|
|
|
g_object_class_install_property (gobject_class, PROP_PERIOD, pspec);
|
|
|
|
|
|
|
|
/* ...install other properties... */
|
|
|
|
}
|
|
|
|
</programlisting>
|
|
|
|
</informalexample>
|
|
|
|
|
|
|
|
<para>We also add a <function>get_property()</function>
|
|
|
|
implementation, as well as a setter (see
|
|
|
|
<link linkend="effects-custom-deform-example-2">the full
|
|
|
|
GObject implementation</link> for details).</para>
|
|
|
|
|
|
|
|
<para>Then set up an animation for the property; in this case,
|
|
|
|
using a <type>ClutterState</type>:</para>
|
|
|
|
|
|
|
|
<informalexample>
|
|
|
|
<programlisting>
|
|
|
|
ClutterEffect *effect = cb_page_fold_effect_new (180.0, 0.0);
|
|
|
|
|
|
|
|
ClutterState *transitions = clutter_state_new ();
|
|
|
|
clutter_state_set_duration (transitions, NULL, NULL, 500);
|
|
|
|
|
|
|
|
clutter_state_set (transitions, NULL, "unfolded",
|
|
|
|
effect, "period", CLUTTER_LINEAR, 0.0,
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
clutter_state_set (transitions, NULL, "folded",
|
|
|
|
effect, "period", CLUTTER_LINEAR, 1.0,
|
|
|
|
NULL);
|
|
|
|
</programlisting>
|
|
|
|
</informalexample>
|
|
|
|
|
|
|
|
<para>To start the animation, warp the <type>ClutterState</type>
|
|
|
|
into its <emphasis>"unfolded"</emphasis> state, then set it to
|
|
|
|
<emphasis>"folded"</emphasis>:</para>
|
|
|
|
|
|
|
|
<informalexample>
|
|
|
|
<programlisting>
|
|
|
|
/* this changes state instantaneously */
|
|
|
|
clutter_state_warp_to_state (transitions, "unfolded");
|
|
|
|
|
|
|
|
/* this starts an animation to the state */
|
|
|
|
clutter_state_set_state (transitions, "folded");
|
|
|
|
</programlisting>
|
|
|
|
</informalexample>
|
|
|
|
|
|
|
|
<para>Note that the
|
|
|
|
<link linkend="effects-custom-deform-example-3">full code
|
|
|
|
sample</link> is slightly more complex, as it triggers state
|
|
|
|
changes when a mouse button is pressed on the texture. There is
|
|
|
|
also a third "partially folded" state (used to create
|
|
|
|
the screenshot for the
|
|
|
|
<link linkend="effects-custom-deform-solution">previous
|
|
|
|
section</link>).</para>
|
|
|
|
|
|
|
|
<para>Here's what the resulting animation looks like:</para>
|
|
|
|
|
|
|
|
<inlinemediaobject>
|
|
|
|
<videoobject>
|
|
|
|
<videodata fileref="videos/effects-custom-deform.ogv"/>
|
|
|
|
</videoobject>
|
|
|
|
<alt>
|
|
|
|
<para>Video showing animation of a custom deform effect
|
|
|
|
on a texture</para>
|
|
|
|
</alt>
|
|
|
|
</inlinemediaobject>
|
|
|
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section id="effects-custom-deform-discussion-tiles">
|
|
|
|
<title>Tiles</title>
|
|
|
|
|
|
|
|
<para>A <type>ClutterDeformEffect</type> divides the actor
|
|
|
|
being deformed into a number of tiles: the larger the number
|
|
|
|
of tiles, the larger the number of vertices to be manipulated
|
|
|
|
by the effect. Increasing the number of tiles increases the number of
|
|
|
|
vertex computations required, which can slow down animations;
|
|
|
|
at the same time, finer-grained tiles can make an effect appear
|
|
|
|
smoother, particularly when animated.</para>
|
|
|
|
|
|
|
|
<para>Most of the time, the default number
|
|
|
|
of tiles in the <varname>x</varname> and <varname>y</varname>
|
|
|
|
axes should suffice. You can get the current number of
|
|
|
|
tiles associated with an effect with:</para>
|
|
|
|
|
|
|
|
<informalexample>
|
|
|
|
<programlisting>
|
|
|
|
<![CDATA[
|
|
|
|
guint x_tiles;
|
|
|
|
guint y_tiles;
|
|
|
|
|
|
|
|
/* effect must be a subclass of ClutterDeformEffect */
|
|
|
|
clutter_deform_effect_get_n_tiles (CLUTTER_DEFORM_EFFECT (effect),
|
|
|
|
&x_tiles,
|
|
|
|
&y_tiles);
|
|
|
|
]]>
|
|
|
|
</programlisting>
|
|
|
|
</informalexample>
|
|
|
|
|
|
|
|
<para>However, if an effect produces jerky or fragmented output,
|
|
|
|
you want to tweak the number of tiles. Use the
|
|
|
|
<function>clutter_deform_effect_set_n_tiles()</function> function
|
|
|
|
to do this:</para>
|
|
|
|
|
|
|
|
<informalexample>
|
|
|
|
<programlisting>
|
|
|
|
/* 64 tiles in both axes */
|
|
|
|
guint x_tiles = 64;
|
|
|
|
guint y_tiles = 64;
|
|
|
|
|
|
|
|
clutter_deform_effect_set_n_tiles (CLUTTER_DEFORM_EFFECT (effect),
|
|
|
|
x_tiles,
|
|
|
|
y_tiles);
|
|
|
|
</programlisting>
|
|
|
|
</informalexample>
|
|
|
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<title>Full example</title>
|
|
|
|
|
|
|
|
<para>This example consists of three files:</para>
|
|
|
|
|
|
|
|
<itemizedlist>
|
|
|
|
<listitem>
|
|
|
|
<para><link linkend="effects-custom-deform-example-1">A header
|
|
|
|
file</link> for the <type>CbPageFoldEffect</type> GObject.</para>
|
|
|
|
</listitem>
|
|
|
|
<listitem>
|
|
|
|
<para><link linkend="effects-custom-deform-example-2">The
|
|
|
|
code file</link> implementing <type>CbPageFoldEffect</type>.</para>
|
|
|
|
</listitem>
|
|
|
|
<listitem>
|
|
|
|
<para><link linkend="effects-custom-deform-example-3">A short
|
|
|
|
sample application</link> which applies a <type>CbPageFoldEffect</type>
|
|
|
|
instance to an actor and animates the fold when the actor is
|
|
|
|
clicked.</para>
|
|
|
|
</listitem>
|
|
|
|
</itemizedlist>
|
|
|
|
|
|
|
|
<para>As Clutter effect subclasses are written using GObject,
|
|
|
|
you might find <link linkend="actors-composite">this recipe</link>
|
|
|
|
(which goes into GObject in more detail) a useful introduction.</para>
|
|
|
|
|
|
|
|
<example id="effects-custom-deform-example-1">
|
|
|
|
<title><filename>cb-page-fold-effect.h</filename> (header file)</title>
|
|
|
|
<programlisting>
|
|
|
|
<xi:include href="examples/cb-page-fold-effect.h" parse="text">
|
|
|
|
<xi:fallback>a code sample should be here... but isn't</xi:fallback>
|
|
|
|
</xi:include>
|
|
|
|
</programlisting>
|
|
|
|
</example>
|
|
|
|
|
|
|
|
<example id="effects-custom-deform-example-2">
|
|
|
|
<title><filename>cb-page-fold-effect.c</filename> (code file)</title>
|
|
|
|
<programlisting>
|
|
|
|
<xi:include href="examples/cb-page-fold-effect.c" parse="text">
|
|
|
|
<xi:fallback>a code sample should be here... but isn't</xi:fallback>
|
|
|
|
</xi:include>
|
|
|
|
</programlisting>
|
|
|
|
</example>
|
|
|
|
|
|
|
|
<example id="effects-custom-deform-example-3">
|
|
|
|
<title>Application which uses <type>CbPageFoldEffect</type>
|
|
|
|
to do animated folding of a <type>ClutterTexture</type></title>
|
|
|
|
<programlisting>
|
|
|
|
<xi:include href="examples/effects-custom-deform.c" parse="text">
|
|
|
|
<xi:fallback>a code sample should be here... but isn't</xi:fallback>
|
|
|
|
</xi:include>
|
|
|
|
</programlisting>
|
|
|
|
</example>
|
|
|
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
</chapter>
|