diff --git a/examples/Makefile.am b/examples/Makefile.am index 5ad7cef53..feb391a11 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -33,7 +33,7 @@ cogl_info_SOURCES = cogl-info.c cogl_info_LDADD = $(common_ldadd) if USE_GLIB -programs += cogl-hello cogl-msaa cogl-gles2-context +programs += cogl-hello cogl-msaa cogl-gles2-context cogl-point-sprites examples_datadir = $(pkgdatadir)/examples-data examples_data_DATA = @@ -41,6 +41,8 @@ cogl_hello_SOURCES = cogl-hello.c cogl_hello_LDADD = $(common_ldadd) cogl_msaa_SOURCES = cogl-msaa.c cogl_msaa_LDADD = $(common_ldadd) +cogl_point_sprites_SOURCES = cogl-point-sprites.c +cogl_point_sprites_LDADD = $(common_ldadd) if BUILD_COGL_PANGO programs += cogl-crate diff --git a/examples/cogl-point-sprites.c b/examples/cogl-point-sprites.c new file mode 100644 index 000000000..2208cfa25 --- /dev/null +++ b/examples/cogl-point-sprites.c @@ -0,0 +1,324 @@ +#include +#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 +{ + uint8_t red, green, blue, alpha; +} Color; + +typedef struct +{ + float size; + float x, y; + float start_x, start_y; + Color color; + + /* Velocities are in units per second */ + float initial_x_velocity; + float initial_y_velocity; + + GTimer *timer; +} Firework; + +typedef struct +{ + float x, y; + Color color; + Color base_color; +} Spark; + +typedef struct +{ + Firework fireworks[N_FIREWORKS]; + + int next_spark_num; + Spark sparks[N_SPARKS]; + GTimer *last_spark_time; + + CoglContext *context; + CoglFramebuffer *fb; + CoglPipeline *pipeline; + CoglPrimitive *primitive; + CoglAttributeBuffer *attribute_buffer; +} Data; + +static CoglTexture * +generate_round_texture (CoglContext *context) +{ + uint8_t *p, *data; + int x, y; + CoglTexture2D *tex; + + p = data = g_malloc (TEXTURE_SIZE * TEXTURE_SIZE * 4); + + /* Generate a white 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_2d_new_from_data (context, + TEXTURE_SIZE, TEXTURE_SIZE, + COGL_PIXEL_FORMAT_RGBA_8888_PRE, + COGL_PIXEL_FORMAT_ANY, + TEXTURE_SIZE * 4, + data, + NULL /* error */); + + g_free (data); + + return COGL_TEXTURE (tex); +} + +static void +paint (Data *data) +{ + int i; + float diff_time; + + /* 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 (Color)); + ((uint8_t *) &firework->color)[g_random_int_range (0, 3)] = 255; + } + else + { + memset (&firework->color, 255, sizeof (Color)); + ((uint8_t *) &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); + } + + cogl_buffer_set_data (COGL_BUFFER (data->attribute_buffer), + 0, /* offset */ + data->sparks, + sizeof (data->sparks)); + + cogl_framebuffer_clear4f (data->fb, COGL_BUFFER_BIT_COLOR, 0, 0, 0, 1); + + cogl_primitive_draw (data->primitive, + data->fb, + data->pipeline); + + cogl_onscreen_swap_buffers (COGL_ONSCREEN (data->fb)); +} + +static void +create_primitive (Data *data) +{ + CoglAttribute *attributes[2]; + int i; + + data->attribute_buffer = + cogl_attribute_buffer_new_with_size (data->context, + sizeof (data->sparks)); + cogl_buffer_set_update_hint (COGL_BUFFER (data->attribute_buffer), + COGL_BUFFER_UPDATE_HINT_DYNAMIC); + + attributes[0] = cogl_attribute_new (data->attribute_buffer, + "cogl_position_in", + sizeof (Spark), + G_STRUCT_OFFSET (Spark, x), + 2, /* n_components */ + COGL_ATTRIBUTE_TYPE_FLOAT); + attributes[1] = cogl_attribute_new (data->attribute_buffer, + "cogl_color_in", + sizeof (Spark), + G_STRUCT_OFFSET (Spark, color), + 4, /* n_components */ + COGL_ATTRIBUTE_TYPE_UNSIGNED_BYTE); + + data->primitive = + cogl_primitive_new_with_attributes (COGL_VERTICES_MODE_POINTS, + N_SPARKS, + attributes, + G_N_ELEMENTS (attributes)); + + for (i = 0; i < G_N_ELEMENTS (attributes); i++) + cogl_object_unref (attributes[i]); +} + +static void +frame_event_cb (CoglOnscreen *onscreen, + CoglFrameEvent event, + CoglFrameInfo *info, + void *user_data) +{ + Data *data = user_data; + + if (event == COGL_FRAME_EVENT_SYNC) + paint (data); +} + +int +main (int argc, char *argv[]) +{ + CoglTexture *tex; + CoglOnscreen *onscreen; + GSource *cogl_source; + GMainLoop *loop; + Data data; + int i; + + data.context = cogl_context_new (NULL, NULL); + + create_primitive (&data); + + data.pipeline = cogl_pipeline_new (data.context); + data.last_spark_time = g_timer_new (); + data.next_spark_num = 0; + cogl_pipeline_set_point_size (data.pipeline, TEXTURE_SIZE); + + tex = generate_round_texture (data.context); + cogl_pipeline_set_layer_texture (data.pipeline, 0, tex); + cogl_object_unref (tex); + + cogl_pipeline_set_layer_point_sprite_coords_enabled (data.pipeline, + 0, /* layer */ + TRUE, + NULL /* 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; + } + + onscreen = cogl_onscreen_new (data.context, 800, 600); + cogl_onscreen_show (onscreen); + data.fb = COGL_FRAMEBUFFER (onscreen); + + cogl_source = cogl_glib_source_new (data.context, G_PRIORITY_DEFAULT); + + g_source_attach (cogl_source, NULL); + + cogl_onscreen_add_frame_callback (onscreen, + frame_event_cb, + &data, + NULL /* destroy notify */); + + loop = g_main_loop_new (NULL, TRUE); + + paint (&data); + + g_main_loop_run (loop); + + g_main_loop_unref (loop); + + g_source_destroy (cogl_source); + + cogl_object_unref (data.pipeline); + cogl_object_unref (data.attribute_buffer); + cogl_object_unref (data.primitive); + cogl_object_unref (onscreen); + cogl_object_unref (data.context); + + g_timer_destroy (data.last_spark_time); + + for (i = 0; i < N_FIREWORKS; i++) + g_timer_destroy (data.fireworks[i].timer); + + return 0; +}