mutter/clutter/tests/interactive/test-cogl-vertex-buffer.c
2016-04-12 20:04:26 +02:00

391 lines
12 KiB
C

#include <glib.h>
#include <gmodule.h>
#include <stdlib.h>
#include <clutter/clutter.h>
#include <cogl/cogl.h>
#include <math.h>
/* Defines the size and resolution of the quad mesh we morph:
*/
#define MESH_WIDTH 100.0 /* number of quads along x axis */
#define MESH_HEIGHT 100.0 /* number of quads along y axis */
#define QUAD_WIDTH 5.0 /* width in pixels of a single quad */
#define QUAD_HEIGHT 5.0 /* height in pixels of a single quad */
/* Defines a sine wave that sweeps across the mesh:
*/
#define WAVE_DEPTH ((MESH_WIDTH * QUAD_WIDTH) / 16.0) /* peak amplitude */
#define WAVE_PERIODS 4.0
#define WAVE_SPEED 10.0
/* Defines a rippling sine wave emitted from a point:
*/
#define RIPPLE_CENTER_X ((MESH_WIDTH / 2.0) * QUAD_WIDTH)
#define RIPPLE_CENTER_Y ((MESH_HEIGHT / 2.0) * QUAD_HEIGHT)
#define RIPPLE_RADIUS (MESH_WIDTH * QUAD_WIDTH)
#define RIPPLE_DEPTH ((MESH_WIDTH * QUAD_WIDTH) / 16.0) /* peak amplitude */
#define RIPPLE_PERIODS 4.0
#define RIPPLE_SPEED -10.0
/* Defines the width of the gaussian bell used to fade out the alpha
* towards the edges of the mesh (starting from the ripple center):
*/
#define GAUSSIAN_RADIUS ((MESH_WIDTH * QUAD_WIDTH) / 6.0)
/* Our hues lie in the range [0, 1], and this defines how we map amplitude
* to hues (before scaling by {WAVE,RIPPLE}_DEPTH)
* As we are interferring two sine waves together; amplitudes lie in the
* range [-2, 2]
*/
#define HSL_OFFSET 0.5 /* the hue that we map an amplitude of 0 too */
#define HSL_SCALE 0.25
typedef struct _TestState
{
ClutterActor *dummy;
CoglHandle buffer;
float *quad_mesh_verts;
guint8 *quad_mesh_colors;
guint16 *static_indices;
guint n_static_indices;
CoglHandle indices;
ClutterTimeline *timeline;
guint frame_id;
} TestState;
static void
frame_cb (ClutterTimeline *timeline,
gint elapsed_msecs,
TestState *state)
{
guint x, y;
float period_progress = clutter_timeline_get_progress (timeline);
float period_progress_sin = sinf (period_progress);
float wave_shift = period_progress * WAVE_SPEED;
float ripple_shift = period_progress * RIPPLE_SPEED;
for (y = 0; y <= MESH_HEIGHT; y++)
for (x = 0; x <= MESH_WIDTH; x++)
{
guint vert_index = (MESH_WIDTH + 1) * y + x;
float *vert = &state->quad_mesh_verts[3 * vert_index];
float real_x = x * QUAD_WIDTH;
float real_y = y * QUAD_HEIGHT;
float wave_offset = (float)x / (MESH_WIDTH + 1);
float wave_angle =
(WAVE_PERIODS * 2 * G_PI * wave_offset) + wave_shift;
float wave_sin = sinf (wave_angle);
float a_sqr = (RIPPLE_CENTER_X - real_x) * (RIPPLE_CENTER_X - real_x);
float b_sqr = (RIPPLE_CENTER_Y - real_y) * (RIPPLE_CENTER_Y - real_y);
float ripple_offset = sqrtf (a_sqr + b_sqr) / RIPPLE_RADIUS;
float ripple_angle =
(RIPPLE_PERIODS * 2 * G_PI * ripple_offset) + ripple_shift;
float ripple_sin = sinf (ripple_angle);
float h, s, l;
guint8 *color;
vert[2] = (wave_sin * WAVE_DEPTH) + (ripple_sin * RIPPLE_DEPTH);
/* Burn some CPU time picking a pretty color... */
h = (HSL_OFFSET
+ wave_sin
+ ripple_sin
+ period_progress_sin) * HSL_SCALE;
s = 0.5;
l = 0.25 + (period_progress_sin + 1.0) / 4.0;
color = &state->quad_mesh_colors[4 * vert_index];
/* A bit of a sneaky cast, but it seems safe to assume the ClutterColor
* typedef is set in stone... */
clutter_color_from_hls ((ClutterColor *)color, h * 360.0, l, s);
color[0] = (color[0] * color[3] + 128) / 255;
color[1] = (color[1] * color[3] + 128) / 255;
color[2] = (color[2] * color[3] + 128) / 255;
}
cogl_vertex_buffer_add (state->buffer,
"gl_Vertex",
3, /* n components */
COGL_ATTRIBUTE_TYPE_FLOAT,
FALSE, /* normalized */
0, /* stride */
state->quad_mesh_verts);
cogl_vertex_buffer_add (state->buffer,
"gl_Color",
4, /* n components */
COGL_ATTRIBUTE_TYPE_UNSIGNED_BYTE,
FALSE, /* normalized */
0, /* stride */
state->quad_mesh_colors);
cogl_vertex_buffer_submit (state->buffer);
clutter_actor_set_rotation (state->dummy,
CLUTTER_Z_AXIS,
360 * period_progress,
(MESH_WIDTH * QUAD_WIDTH) / 2,
(MESH_HEIGHT * QUAD_HEIGHT) / 2,
0);
clutter_actor_set_rotation (state->dummy,
CLUTTER_X_AXIS,
360 * period_progress,
(MESH_WIDTH * QUAD_WIDTH) / 2,
(MESH_HEIGHT * QUAD_HEIGHT) / 2,
0);
}
static void
on_paint (ClutterActor *actor, TestState *state)
{
cogl_set_source_color4ub (0xff, 0x00, 0x00, 0xff);
cogl_vertex_buffer_draw_elements (state->buffer,
COGL_VERTICES_MODE_TRIANGLE_STRIP,
state->indices,
0, /* min index */
(MESH_WIDTH + 1) *
(MESH_HEIGHT + 1) - 1, /* max index */
0, /* indices offset */
state->n_static_indices);
}
static void
init_static_index_arrays (TestState *state)
{
guint n_indices;
int x, y;
guint16 *i;
guint dir;
/* - Each row takes (2 + 2 * MESH_WIDTH indices)
* - Thats 2 to start the triangle strip then 2 indices to add 2 triangles
* per mesh quad.
* - We have MESH_HEIGHT rows
* - It takes one extra index for linking between rows (MESH_HEIGHT - 1)
* - A 2 x 3 mesh == 20 indices... */
n_indices = (2 + 2 * MESH_WIDTH) * MESH_HEIGHT + (MESH_HEIGHT - 1);
state->static_indices = g_malloc (sizeof (guint16) * n_indices);
state->n_static_indices = n_indices;
#define MESH_INDEX(X, Y) (Y) * (MESH_WIDTH + 1) + (X)
i = state->static_indices;
/* NB: front facing == anti-clockwise winding */
i[0] = MESH_INDEX (0, 0);
i[1] = MESH_INDEX (0, 1);
i += 2;
#define LEFT 0
#define RIGHT 1
dir = RIGHT;
for (y = 0; y < MESH_HEIGHT; y++)
{
for (x = 0; x < MESH_WIDTH; x++)
{
/* Add 2 triangles per mesh quad... */
if (dir == RIGHT)
{
i[0] = MESH_INDEX (x + 1, y);
i[1] = MESH_INDEX (x + 1, y + 1);
}
else
{
i[0] = MESH_INDEX (MESH_WIDTH - x - 1, y);
i[1] = MESH_INDEX (MESH_WIDTH - x - 1, y + 1);
}
i += 2;
}
/* Link rows... */
if (y == (MESH_HEIGHT - 1))
break;
if (dir == RIGHT)
{
i[0] = MESH_INDEX (MESH_WIDTH, y + 1);
i[1] = MESH_INDEX (MESH_WIDTH, y + 1);
i[2] = MESH_INDEX (MESH_WIDTH, y + 2);
}
else
{
i[0] = MESH_INDEX (0, y + 1);
i[1] = MESH_INDEX (0, y + 1);
i[2] = MESH_INDEX (0, y + 2);
}
i += 3;
dir = !dir;
}
#undef MESH_INDEX
state->indices =
cogl_vertex_buffer_indices_new (COGL_INDICES_TYPE_UNSIGNED_SHORT,
state->static_indices,
state->n_static_indices);
}
static float
gaussian (float x, float y)
{
/* Bell width */
float c = GAUSSIAN_RADIUS;
/* Peak amplitude */
float a = 1.0;
/* float a = 1.0 / (c * sqrtf (2.0 * G_PI)); */
/* Center offset */
float b = 0.0;
float dist;
x = x - RIPPLE_CENTER_X;
y = y - RIPPLE_CENTER_Y;
dist = sqrtf (x*x + y*y);
return a * exp ((- ((dist - b) * (dist - b))) / (2.0 * c * c));
}
static void
init_quad_mesh (TestState *state)
{
int x, y;
float *vert;
guint8 *color;
/* Note: we maintain the minimum number of vertices possible. This minimizes
* the work required when we come to morph the geometry.
*
* We use static indices into our mesh so that we can treat the data like a
* single triangle list and drawing can be done in one operation (Note: We
* are using degenerate triangles at the edges to link to the next row)
*/
state->quad_mesh_verts =
g_malloc0 (sizeof (float) * 3 * (MESH_WIDTH + 1) * (MESH_HEIGHT + 1));
state->quad_mesh_colors =
g_malloc0 (sizeof (guint8) * 4 * (MESH_WIDTH + 1) * (MESH_HEIGHT + 1));
vert = state->quad_mesh_verts;
color = state->quad_mesh_colors;
for (y = 0; y <= MESH_HEIGHT; y++)
for (x = 0; x <= MESH_WIDTH; x++)
{
vert[0] = x * QUAD_WIDTH;
vert[1] = y * QUAD_HEIGHT;
vert += 3;
color[3] = gaussian (x * QUAD_WIDTH,
y * QUAD_HEIGHT) * 255.0;
color += 4;
}
state->buffer = cogl_vertex_buffer_new ((MESH_WIDTH + 1)*(MESH_HEIGHT + 1));
cogl_vertex_buffer_add (state->buffer,
"gl_Vertex",
3, /* n components */
COGL_ATTRIBUTE_TYPE_FLOAT,
FALSE, /* normalized */
0, /* stride */
state->quad_mesh_verts);
cogl_vertex_buffer_add (state->buffer,
"gl_Color",
4, /* n components */
COGL_ATTRIBUTE_TYPE_UNSIGNED_BYTE,
FALSE, /* normalized */
0, /* stride */
state->quad_mesh_colors);
cogl_vertex_buffer_submit (state->buffer);
init_static_index_arrays (state);
}
/* This creates an actor that has a specific size but that does not result
* in any drawing so we can do our own drawing using Cogl... */
static ClutterActor *
create_dummy_actor (guint width, guint height)
{
ClutterActor *group, *rect;
ClutterColor clr = { 0xff, 0xff, 0xff, 0xff};
group = clutter_group_new ();
rect = clutter_rectangle_new_with_color (&clr);
clutter_actor_set_size (rect, width, height);
clutter_actor_hide (rect);
clutter_container_add_actor (CLUTTER_CONTAINER (group), rect);
return group;
}
static void
stop_and_quit (ClutterActor *actor,
TestState *state)
{
clutter_timeline_stop (state->timeline);
clutter_main_quit ();
}
G_MODULE_EXPORT int
test_cogl_vertex_buffer_main (int argc, char *argv[])
{
TestState state;
ClutterActor *stage;
gfloat stage_w, stage_h;
gint dummy_width, dummy_height;
if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS)
return 1;
stage = clutter_stage_new ();
clutter_stage_set_title (CLUTTER_STAGE (stage), "Cogl Vertex Buffers");
clutter_stage_set_color (CLUTTER_STAGE (stage), CLUTTER_COLOR_Black);
g_signal_connect (stage, "destroy", G_CALLBACK (stop_and_quit), &state);
clutter_actor_get_size (stage, &stage_w, &stage_h);
dummy_width = MESH_WIDTH * QUAD_WIDTH;
dummy_height = MESH_HEIGHT * QUAD_HEIGHT;
state.dummy = create_dummy_actor (dummy_width, dummy_height);
clutter_container_add_actor (CLUTTER_CONTAINER (stage), state.dummy);
clutter_actor_set_position (state.dummy,
(stage_w / 2.0) - (dummy_width / 2.0),
(stage_h / 2.0) - (dummy_height / 2.0));
state.timeline = clutter_timeline_new (1000);
clutter_timeline_set_loop (state.timeline, TRUE);
state.frame_id = g_signal_connect (state.timeline,
"new-frame",
G_CALLBACK (frame_cb),
&state);
g_signal_connect (state.dummy, "paint", G_CALLBACK (on_paint), &state);
init_quad_mesh (&state);
clutter_actor_show_all (stage);
clutter_timeline_start (state.timeline);
clutter_main ();
cogl_handle_unref (state.buffer);
cogl_handle_unref (state.indices);
return 0;
}
G_MODULE_EXPORT const char *
test_cogl_vertex_buffer_describe (void)
{
return "Vertex buffers support in Cogl.";
}