mutter/tests/interactive/test-cogl-vertex-buffer.c
Neil Roberts c9a81f035e Don't use any GL types or defines in Clutter
Some of the Clutter code was using GL types for the primitive types
such as GLint and GLubyte and then passing these to Cogl. This doesn't
make much sense because the Cogl functions directly take native C
types. This patch just replaces them with either a native C type or a
glib type.

Some of the cogl conformance tests are trying to directly call GL for
example to test creating a foreign texture. These tests have been
changed to manually define the GL enum values instead of relying on a
GL header to define them.

This is necessary because Cogl may soon stop including a GL header
from its public headers.

Reviewed-by: Emmanuele Bassi <ebassi@linux.intel.com>
2012-03-23 17:24:42 +00: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.";
}