1 #include <cogl/cogl.h>
2 #include <glib.h>
3 #include <stdlib.h>
4 #include <math.h>
5 #include <string.h>
6 
7 #define N_FIREWORKS 32
8 /* Units per second per second */
9 #define GRAVITY -1.5f
10 
11 #define N_SPARKS (N_FIREWORKS * 32) /* Must be a power of two */
12 #define TIME_PER_SPARK 0.01f /* in seconds */
13 
14 #define TEXTURE_SIZE 32
15 
16 typedef struct
17 {
18   uint8_t red, green, blue, alpha;
19 } Color;
20 
21 typedef struct
22 {
23   float size;
24   float x, y;
25   float start_x, start_y;
26   Color color;
27 
28   /* Velocities are in units per second */
29   float initial_x_velocity;
30   float initial_y_velocity;
31 
32   GTimer *timer;
33 } Firework;
34 
35 typedef struct
36 {
37   float x, y;
38   Color color;
39   Color base_color;
40 } Spark;
41 
42 typedef struct
43 {
44   Firework fireworks[N_FIREWORKS];
45 
46   int next_spark_num;
47   Spark sparks[N_SPARKS];
48   GTimer *last_spark_time;
49 
50   CoglContext *context;
51   CoglFramebuffer *fb;
52   CoglPipeline *pipeline;
53   CoglPrimitive *primitive;
54   CoglAttributeBuffer *attribute_buffer;
55 } Data;
56 
57 static CoglTexture *
generate_round_texture(CoglContext * context)58 generate_round_texture (CoglContext *context)
59 {
60   uint8_t *p, *data;
61   int x, y;
62   CoglTexture2D *tex;
63 
64   p = data = g_malloc (TEXTURE_SIZE * TEXTURE_SIZE * 4);
65 
66   /* Generate a white circle which gets transparent towards the edges */
67   for (y = 0; y < TEXTURE_SIZE; y++)
68     for (x = 0; x < TEXTURE_SIZE; x++)
69       {
70         int dx = x - TEXTURE_SIZE / 2;
71         int dy = y - TEXTURE_SIZE / 2;
72         float value = sqrtf (dx * dx + dy * dy) * 255.0 / (TEXTURE_SIZE / 2);
73         if (value > 255.0f)
74           value = 255.0f;
75         value = 255.0f - value;
76         *(p++) = value;
77         *(p++) = value;
78         *(p++) = value;
79         *(p++) = value;
80       }
81 
82   tex = cogl_texture_2d_new_from_data (context,
83                                        TEXTURE_SIZE, TEXTURE_SIZE,
84                                        COGL_PIXEL_FORMAT_RGBA_8888_PRE,
85                                        TEXTURE_SIZE * 4,
86                                        data,
87                                        NULL /* error */);
88 
89   g_free (data);
90 
91   return tex;
92 }
93 
94 static void
paint(Data * data)95 paint (Data *data)
96 {
97   int i;
98   float diff_time;
99 
100   /* Update all of the firework's positions */
101   for (i = 0; i < N_FIREWORKS; i++)
102     {
103       Firework *firework = data->fireworks + i;
104 
105       if ((fabsf (firework->x - firework->start_x) > 2.0f) ||
106           firework->y < -1.0f)
107         {
108           firework->size = g_random_double_range (0.001f, 0.1f);
109           firework->start_x = 1.0f + firework->size;
110           firework->start_y = -1.0f;
111           firework->initial_x_velocity = g_random_double_range (-0.1f, -2.0f);
112           firework->initial_y_velocity = g_random_double_range (0.1f, 4.0f);
113           g_timer_reset (firework->timer);
114 
115           /* Pick a random color out of six */
116           if (g_random_boolean ())
117             {
118               memset (&firework->color, 0, sizeof (Color));
119               ((uint8_t *) &firework->color)[g_random_int_range (0, 3)] = 255;
120             }
121           else
122             {
123               memset (&firework->color, 255, sizeof (Color));
124               ((uint8_t *) &firework->color)[g_random_int_range (0, 3)] = 0;
125             }
126           firework->color.alpha = 255;
127 
128           /* Fire some of the fireworks from the other side */
129           if (g_random_boolean ())
130             {
131               firework->start_x = -firework->start_x;
132               firework->initial_x_velocity = -firework->initial_x_velocity;
133             }
134         }
135 
136       diff_time = g_timer_elapsed (firework->timer, NULL);
137 
138       firework->x = (firework->start_x +
139                      firework->initial_x_velocity * diff_time);
140 
141       firework->y = ((firework->initial_y_velocity * diff_time +
142                       0.5f * GRAVITY * diff_time * diff_time) +
143                      firework->start_y);
144     }
145 
146   diff_time = g_timer_elapsed (data->last_spark_time, NULL);
147   if (diff_time < 0.0f || diff_time >= TIME_PER_SPARK)
148     {
149       /* Add a new spark for each firework, overwriting the oldest ones */
150       for (i = 0; i < N_FIREWORKS; i++)
151         {
152           Spark *spark = data->sparks + data->next_spark_num;
153           Firework *firework = data->fireworks + i;
154 
155           spark->x = (firework->x +
156                       g_random_double_range (-firework->size / 2.0f,
157                                              firework->size / 2.0f));
158           spark->y = (firework->y +
159                       g_random_double_range (-firework->size / 2.0f,
160                                              firework->size / 2.0f));
161           spark->base_color = firework->color;
162 
163           data->next_spark_num = (data->next_spark_num + 1) & (N_SPARKS - 1);
164         }
165 
166       /* Update the colour of each spark */
167       for (i = 0; i < N_SPARKS; i++)
168         {
169           float color_value;
170 
171           /* First spark is the oldest */
172           Spark *spark = data->sparks + ((data->next_spark_num + i)
173                                          & (N_SPARKS - 1));
174 
175           color_value = i / (N_SPARKS - 1.0f);
176           spark->color.red = spark->base_color.red * color_value;
177           spark->color.green = spark->base_color.green * color_value;
178           spark->color.blue = spark->base_color.blue * color_value;
179           spark->color.alpha = 255.0f * color_value;
180         }
181 
182       g_timer_reset (data->last_spark_time);
183     }
184 
185   cogl_buffer_set_data (data->attribute_buffer,
186                         0, /* offset */
187                         data->sparks,
188                         sizeof (data->sparks));
189 
190   cogl_framebuffer_clear4f (data->fb, COGL_BUFFER_BIT_COLOR, 0, 0, 0, 1);
191 
192   cogl_primitive_draw (data->primitive,
193                        data->fb,
194                        data->pipeline);
195 
196   cogl_onscreen_swap_buffers (data->fb);
197 }
198 
199 static void
create_primitive(Data * data)200 create_primitive (Data *data)
201 {
202   CoglAttribute *attributes[2];
203   int i;
204 
205   data->attribute_buffer =
206     cogl_attribute_buffer_new_with_size (data->context,
207                                          sizeof (data->sparks));
208   cogl_buffer_set_update_hint (data->attribute_buffer,
209                                COGL_BUFFER_UPDATE_HINT_DYNAMIC);
210 
211   attributes[0] = cogl_attribute_new (data->attribute_buffer,
212                                       "cogl_position_in",
213                                       sizeof (Spark),
214                                       G_STRUCT_OFFSET (Spark, x),
215                                       2, /* n_components */
216                                       COGL_ATTRIBUTE_TYPE_FLOAT);
217   attributes[1] = cogl_attribute_new (data->attribute_buffer,
218                                       "cogl_color_in",
219                                       sizeof (Spark),
220                                       G_STRUCT_OFFSET (Spark, color),
221                                       4, /* n_components */
222                                       COGL_ATTRIBUTE_TYPE_UNSIGNED_BYTE);
223 
224   data->primitive =
225     cogl_primitive_new_with_attributes (COGL_VERTICES_MODE_POINTS,
226                                         N_SPARKS,
227                                         attributes,
228                                         G_N_ELEMENTS (attributes));
229 
230   for (i = 0; i < G_N_ELEMENTS (attributes); i++)
231     cogl_object_unref (attributes[i]);
232 }
233 
234 static void
frame_event_cb(CoglOnscreen * onscreen,CoglFrameEvent event,CoglFrameInfo * info,void * user_data)235 frame_event_cb (CoglOnscreen *onscreen,
236                 CoglFrameEvent event,
237                 CoglFrameInfo *info,
238                 void *user_data)
239 {
240   Data *data = user_data;
241 
242   if (event == COGL_FRAME_EVENT_SYNC)
243     paint (data);
244 }
245 
246 int
main(int argc,char * argv[])247 main (int argc, char *argv[])
248 {
249   CoglTexture *tex;
250   CoglOnscreen *onscreen;
251   GSource *cogl_source;
252   GMainLoop *loop;
253   Data data;
254   int i;
255 
256   data.context = cogl_context_new (NULL, NULL);
257 
258   create_primitive (&data);
259 
260   data.pipeline = cogl_pipeline_new (data.context);
261   data.last_spark_time = g_timer_new ();
262   data.next_spark_num = 0;
263   cogl_pipeline_set_point_size (data.pipeline, TEXTURE_SIZE);
264 
265   tex = generate_round_texture (data.context);
266   cogl_pipeline_set_layer_texture (data.pipeline, 0, tex);
267   cogl_object_unref (tex);
268 
269   cogl_pipeline_set_layer_point_sprite_coords_enabled (data.pipeline,
270                                                        0, /* layer */
271                                                        TRUE,
272                                                        NULL /* error */);
273 
274   for (i = 0; i < N_FIREWORKS; i++)
275     {
276       data.fireworks[i].x = -FLT_MAX;
277       data.fireworks[i].y = FLT_MAX;
278       data.fireworks[i].size = 0.0f;
279       data.fireworks[i].timer = g_timer_new ();
280     }
281 
282   for (i = 0; i < N_SPARKS; i++)
283     {
284       data.sparks[i].x = 2.0f;
285       data.sparks[i].y = 2.0f;
286     }
287 
288   onscreen = cogl_onscreen_new (data.context, 800, 600);
289   cogl_onscreen_show (onscreen);
290   data.fb = onscreen;
291 
292   cogl_source = cogl_glib_source_new (data.context, G_PRIORITY_DEFAULT);
293 
294   g_source_attach (cogl_source, NULL);
295 
296   cogl_onscreen_add_frame_callback (onscreen,
297                                     frame_event_cb,
298                                     &data,
299                                     NULL /* destroy notify */);
300 
301   loop = g_main_loop_new (NULL, TRUE);
302 
303   paint (&data);
304 
305   g_main_loop_run (loop);
306 
307   g_main_loop_unref (loop);
308 
309   g_source_destroy (cogl_source);
310 
311   cogl_object_unref (data.pipeline);
312   cogl_object_unref (data.attribute_buffer);
313   cogl_object_unref (data.primitive);
314   cogl_object_unref (onscreen);
315   cogl_object_unref (data.context);
316 
317   g_timer_destroy (data.last_spark_time);
318 
319   for (i = 0; i < N_FIREWORKS; i++)
320     g_timer_destroy (data.fireworks[i].timer);
321 
322   return 0;
323 }
324