diff --git a/tests/interactive/Makefile.am b/tests/interactive/Makefile.am index 0b4d06fac..0dc5666df 100644 --- a/tests/interactive/Makefile.am +++ b/tests/interactive/Makefile.am @@ -54,7 +54,8 @@ UNIT_TESTS = \ test-drag.c \ test-constraints.c \ test-scrolling.c \ - test-bind.c + test-bind.c \ + test-cogl-point-sprites.c if X11_TESTS UNIT_TESTS += test-pixmap.c diff --git a/tests/interactive/test-cogl-point-sprites.c b/tests/interactive/test-cogl-point-sprites.c new file mode 100644 index 000000000..9898e4934 --- /dev/null +++ b/tests/interactive/test-cogl-point-sprites.c @@ -0,0 +1,280 @@ +#include +#include +#include +#include + +#define N_FIREWORKS 32 +/* Units per second per second */ +#define GRAVITY -1.5f + +#define N_SPARKS (N_FIREWORKS * 32) /* Must be a power of two */ +#define TIME_PER_SPARK 0.01f /* in seconds */ + +#define TEXTURE_SIZE 32 + +typedef struct _Firework Firework; + +struct _Firework +{ + float size; + float x, y; + float start_x, start_y; + ClutterColor color; + + /* Velocities are in units per second */ + float initial_x_velocity; + float initial_y_velocity; + + GTimer *timer; +}; + +typedef struct _Spark Spark; + +struct _Spark +{ + float x, y; + ClutterColor color; + ClutterColor base_color; +}; + +typedef struct _Data Data; + +struct _Data +{ + Firework fireworks[N_FIREWORKS]; + + int next_spark_num; + Spark sparks[N_SPARKS]; + GTimer *last_spark_time; + + CoglMaterial *material; +}; + +static CoglHandle +generate_round_texture (void) +{ + guint8 *p, *data; + int x, y; + CoglHandle tex; + + p = data = g_malloc (TEXTURE_SIZE * TEXTURE_SIZE * 4); + + /* Generate a yellow circle which gets transparent towards the edges */ + for (y = 0; y < TEXTURE_SIZE; y++) + for (x = 0; x < TEXTURE_SIZE; x++) + { + int dx = x - TEXTURE_SIZE / 2; + int dy = y - TEXTURE_SIZE / 2; + float value = sqrtf (dx * dx + dy * dy) * 255.0 / (TEXTURE_SIZE / 2); + if (value > 255.0f) + value = 255.0f; + value = 255.0f - value; + *(p++) = value; + *(p++) = value; + *(p++) = value; + *(p++) = value; + } + + tex = cogl_texture_new_from_data (TEXTURE_SIZE, TEXTURE_SIZE, + COGL_TEXTURE_NO_SLICING, + COGL_PIXEL_FORMAT_RGBA_8888_PRE, + COGL_PIXEL_FORMAT_ANY, + TEXTURE_SIZE * 4, + data); + + g_free (data); + + return tex; +} + +static void +paint_cb (ClutterActor *stage, Data *data) +{ + CoglMatrix old_matrix, new_matrix; + int i; + float diff_time; + CoglHandle vbo; + + cogl_get_projection_matrix (&old_matrix); + /* Use an orthogonal projection from -1 -> 1 in both axes */ + cogl_matrix_init_identity (&new_matrix); + cogl_set_projection_matrix (&new_matrix); + + cogl_push_matrix (); + cogl_set_modelview_matrix (&new_matrix); + + /* Update all of the firework's positions */ + for (i = 0; i < N_FIREWORKS; i++) + { + Firework *firework = data->fireworks + i; + + if ((fabsf (firework->x - firework->start_x) > 2.0f) || + firework->y < -1.0f) + { + firework->size = g_random_double_range (0.001f, 0.1f); + firework->start_x = 1.0f + firework->size; + firework->start_y = -1.0f; + firework->initial_x_velocity = g_random_double_range (-0.1f, -2.0f); + firework->initial_y_velocity = g_random_double_range (0.1f, 4.0f); + g_timer_reset (firework->timer); + + /* Pick a random color out of six */ + if (g_random_boolean ()) + { + memset (&firework->color, 0, sizeof (ClutterColor)); + ((guint8 *) &firework->color)[g_random_int_range (0, 3)] = 255; + } + else + { + memset (&firework->color, 255, sizeof (ClutterColor)); + ((guint8 *) &firework->color)[g_random_int_range (0, 3)] = 0; + } + firework->color.alpha = 255; + + /* Fire some of the fireworks from the other side */ + if (g_random_boolean ()) + { + firework->start_x = -firework->start_x; + firework->initial_x_velocity = -firework->initial_x_velocity; + } + } + + diff_time = g_timer_elapsed (firework->timer, NULL); + + firework->x = (firework->start_x + + firework->initial_x_velocity * diff_time); + + firework->y = ((firework->initial_y_velocity * diff_time + + 0.5f * GRAVITY * diff_time * diff_time) + + firework->start_y); + } + + diff_time = g_timer_elapsed (data->last_spark_time, NULL); + if (diff_time < 0.0f || diff_time >= TIME_PER_SPARK) + { + /* Add a new spark for each firework, overwriting the oldest ones */ + for (i = 0; i < N_FIREWORKS; i++) + { + Spark *spark = data->sparks + data->next_spark_num; + Firework *firework = data->fireworks + i; + + spark->x = (firework->x + + g_random_double_range (-firework->size / 2.0f, + firework->size / 2.0f)); + spark->y = (firework->y + + g_random_double_range (-firework->size / 2.0f, + firework->size / 2.0f)); + spark->base_color = firework->color; + + data->next_spark_num = (data->next_spark_num + 1) & (N_SPARKS - 1); + } + + /* Update the colour of each spark */ + for (i = 0; i < N_SPARKS; i++) + { + float color_value; + + /* First spark is the oldest */ + Spark *spark = data->sparks + ((data->next_spark_num + i) + & (N_SPARKS - 1)); + + color_value = i / (N_SPARKS - 1.0f); + spark->color.red = spark->base_color.red * color_value; + spark->color.green = spark->base_color.green * color_value; + spark->color.blue = spark->base_color.blue * color_value; + spark->color.alpha = 255.0f * color_value; + } + + g_timer_reset (data->last_spark_time); + } + + vbo = cogl_vertex_buffer_new (N_SPARKS); + cogl_vertex_buffer_add (vbo, "gl_Vertex", 2, + COGL_ATTRIBUTE_TYPE_FLOAT, FALSE, + sizeof (Spark), + &data->sparks[0].x); + cogl_vertex_buffer_add (vbo, "gl_Color", 4, + COGL_ATTRIBUTE_TYPE_UNSIGNED_BYTE, TRUE, + sizeof (Spark), + &data->sparks[0].color.red); + cogl_vertex_buffer_submit (vbo); + + cogl_set_source (data->material); + cogl_vertex_buffer_draw (vbo, COGL_VERTICES_MODE_POINTS, 0, N_SPARKS); + + cogl_handle_unref (vbo); + + cogl_set_projection_matrix (&old_matrix); + cogl_pop_matrix (); +} + +static gboolean +idle_cb (gpointer data) +{ + clutter_actor_queue_redraw (data); + + return TRUE; +} + +G_MODULE_EXPORT int +test_cogl_point_sprites_main (int argc, char *argv[]) +{ + static const ClutterColor black = { 0, 0, 0, 255 }; + ClutterActor *stage; + CoglHandle tex; + Data data; + GError *error = NULL; + int i; + + clutter_init (&argc, &argv); + + data.material = cogl_material_new (); + data.last_spark_time = g_timer_new (); + + cogl_material_set_point_size (data.material, TEXTURE_SIZE); + + tex = generate_round_texture (); + cogl_material_set_layer (data.material, 0, tex); + cogl_handle_unref (tex); + + if (!cogl_material_set_layer_point_sprite_coords_enabled (data.material, + 0, TRUE, + &error)) + { + g_warning ("Failed to enable point sprite coords: %s", error->message); + g_clear_error (&error); + } + + for (i = 0; i < N_FIREWORKS; i++) + { + data.fireworks[i].x = -FLT_MAX; + data.fireworks[i].y = FLT_MAX; + data.fireworks[i].size = 0.0f; + data.fireworks[i].timer = g_timer_new (); + } + + for (i = 0; i < N_SPARKS; i++) + { + data.sparks[i].x = 2.0f; + data.sparks[i].y = 2.0f; + } + + stage = clutter_stage_get_default (); + clutter_stage_set_color (CLUTTER_STAGE (stage), &black); + + g_signal_connect_after (stage, "paint", G_CALLBACK (paint_cb), &data); + + clutter_actor_show (stage); + + g_idle_add (idle_cb, stage); + + clutter_main (); + + cogl_object_unref (data.material); + g_timer_destroy (data.last_spark_time); + + for (i = 0; i < N_FIREWORKS; i++) + g_timer_destroy (data.fireworks[i].timer); + + return 0; +}