Nilorea Library
C utilities for networking, threading, graphics
Loading...
Searching...
No Matches
ex_gui_particles.c
Go to the documentation of this file.
1/*
2 * Nilorea Library
3 * Copyright (C) 2005-2026 Castagnier Mickael
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <https://www.gnu.org/licenses/>.
17 */
18
27#define WIDTH 640
28#define HEIGHT 480
29
30#define ALLEGRO_UNSTABLE 1
31
32#include "nilorea/n_common.h"
33#include "nilorea/n_particles.h"
34#include "nilorea/n_anim.h"
35#include "nilorea/n_gui.h"
36#include <allegro5/allegro_ttf.h>
37
38ALLEGRO_DISPLAY* display = NULL;
39
40int DONE = 0, /* Flag to check if we are always running */
41 getoptret = 0, /* launch parameter check */
42 log_level = LOG_ERR; /* default LOG_LEVEL */
43
44ALLEGRO_BITMAP* scr_buf = NULL;
45
46ALLEGRO_TIMER* fps_timer = NULL;
47ALLEGRO_TIMER* logic_timer = NULL;
48LIST* active_object = NULL; /* list of active objects */
50
51/* GUI widget ids for info labels */
52static int lbl_particle_count = -1;
53static int lbl_particle_max = -1;
54static int lbl_mouse_pos = -1;
55static int lbl_spawn_info = -1;
56static int lbl_fps_info = -1;
57
58/* stats */
59static int total_spawned = 0;
60static int frame_count = 0;
61static double fps_display = 0.0;
62static double fps_accum = 0.0;
63
64int main(int argc, char* argv[]) {
66
67 N_STR* log_file = NULL;
68 nstrprintf(log_file, "%s.log", argv[0]);
69 /*set_log_file( _nstr( log_file ) );*/
71
72 n_log(LOG_NOTICE, "%s is starting ...", argv[0]);
73
74 /* allegro 5 + addons loading */
75 if (!al_init()) {
76 n_abort("Could not init Allegro.\n");
77 }
78 if (!al_install_audio()) {
79 n_abort("Unable to initialize audio addon\n");
80 }
81 if (!al_init_acodec_addon()) {
82 n_abort("Unable to initialize acoded addon\n");
83 }
84 if (!al_init_image_addon()) {
85 n_abort("Unable to initialize image addon\n");
86 }
87 if (!al_init_primitives_addon()) {
88 n_abort("Unable to initialize primitives addon\n");
89 }
90 if (!al_init_font_addon()) {
91 n_abort("Unable to initialize font addon\n");
92 }
93 if (!al_init_ttf_addon()) {
94 n_abort("Unable to initialize ttf_font addon\n");
95 }
96 if (!al_install_keyboard()) {
97 n_abort("Unable to initialize keyboard handler\n");
98 }
99 if (!al_install_mouse()) {
100 n_abort("Unable to initialize mouse handler\n");
101 }
102 ALLEGRO_EVENT_QUEUE* event_queue = NULL;
103
104 event_queue = al_create_event_queue();
105 if (!event_queue) {
106 fprintf(stderr, "failed to create event_queue!\n");
107 al_destroy_display(display);
108 return -1;
109 }
110
111 char ver_str[128] = "";
112 while ((getoptret = getopt(argc, argv, "hvV:L:")) != EOF) {
113 switch (getoptret) {
114 case 'h':
115 n_log(LOG_NOTICE, "\n %s -h help -v version -V DEBUGLEVEL (NOLOG,VERBOSE,NOTICE,ERROR,DEBUG)", argv[0]);
116 exit(TRUE);
117 case 'v':
118 sprintf(ver_str, "%s %s", __DATE__, __TIME__);
119 exit(TRUE);
120 break;
121 case 'V':
122 if (!strncmp("NOTICE", optarg, 6)) {
124 } else {
125 if (!strncmp("VERBOSE", optarg, 7)) {
127 } else {
128 if (!strncmp("ERROR", optarg, 5)) {
130 } else {
131 if (!strncmp("DEBUG", optarg, 5)) {
133 } else {
134 n_log(LOG_ERR, "%s is not a valid log level", optarg);
135 exit(FALSE);
136 }
137 }
138 }
139 }
140 n_log(LOG_NOTICE, "LOG LEVEL UP TO: %d", log_level);
142 break;
143 case 'L':
144 n_log(LOG_NOTICE, "LOG FILE: %s", optarg);
145 set_log_file(optarg);
146 break;
147 case '?': {
148 switch (optopt) {
149 case 'V':
150 n_log(LOG_ERR, "\nPlease specify a log level after -V. \nAvailable values: NOLOG,VERBOSE,NOTICE,ERROR,DEBUG");
151 break;
152 case 'L':
153 n_log(LOG_ERR, "\nPlease specify a log file after -L");
154 default:
155 break;
156 }
157 }
158 __attribute__((fallthrough));
159 default:
160 n_log(LOG_ERR, "\n %s -h help -v version -V DEBUGLEVEL (NOLOG,VERBOSE,NOTICE,ERROR,DEBUG) -L logfile", argv[0]);
161 exit(FALSE);
162 }
163 }
164
165 fps_timer = al_create_timer(1.0 / 30.0);
166 logic_timer = al_create_timer(1.0 / 50.0);
167
168 al_set_new_display_flags(ALLEGRO_OPENGL | ALLEGRO_WINDOWED);
169 display = al_create_display(WIDTH, HEIGHT);
170 if (!display) {
171 n_abort("Unable to create display\n");
172 }
173 al_set_window_title(display, argv[0]);
174
175 al_set_new_bitmap_flags(ALLEGRO_VIDEO_BITMAP);
176
177 DONE = 0;
178
180
181 enum APP_KEYS {
182 KEY_UP,
183 KEY_DOWN,
184 KEY_LEFT,
185 KEY_RIGHT,
186 KEY_ESC,
187 KEY_SPACE,
188 KEY_CTRL
189 };
190 int key[7] = {false, false, false, false, false, false, false};
191
192 al_register_event_source(event_queue, al_get_display_event_source(display));
193
194 al_start_timer(fps_timer);
195 al_start_timer(logic_timer);
196 al_register_event_source(event_queue, al_get_timer_event_source(fps_timer));
197 al_register_event_source(event_queue, al_get_timer_event_source(logic_timer));
198
199 al_register_event_source(event_queue, al_get_keyboard_event_source());
200 al_register_event_source(event_queue, al_get_mouse_event_source());
201
202 ALLEGRO_BITMAP* scrbuf = al_create_bitmap(WIDTH, HEIGHT);
203
204 al_hide_mouse_cursor(display);
205
206 init_particle_system(&particle_system_effects, 10000, 0, 0, 0, 100);
207
208 int mx = 0, my = 0, mouse_b1 = 0, mouse_b2 = 0;
209 int do_draw = 0, do_logic = 0;
210
211 /* ---- Create Info GUI ---- */
212 ALLEGRO_FONT* gui_font = al_create_builtin_font();
213 N_GUI_CTX* gui = n_gui_new_ctx(gui_font);
214 n_gui_set_display_size(gui, (float)WIDTH, (float)HEIGHT);
215 n_gui_set_virtual_size(gui, (float)WIDTH, (float)HEIGHT);
216
217 int info_win = n_gui_add_window(gui, "Particle Info", 10, 10, 220, 180);
218 lbl_particle_count = n_gui_add_label(gui, info_win, "Active: 0", 10, 10, 200, 16, N_GUI_ALIGN_LEFT);
219 lbl_particle_max = n_gui_add_label(gui, info_win, "Max: 10000", 10, 30, 200, 16, N_GUI_ALIGN_LEFT);
220 lbl_spawn_info = n_gui_add_label(gui, info_win, "Spawned: 0", 10, 50, 200, 16, N_GUI_ALIGN_LEFT);
221 lbl_mouse_pos = n_gui_add_label(gui, info_win, "Mouse: 0, 0", 10, 70, 200, 16, N_GUI_ALIGN_LEFT);
222 lbl_fps_info = n_gui_add_label(gui, info_win, "FPS: 0", 10, 90, 200, 16, N_GUI_ALIGN_LEFT);
223 n_gui_add_label(gui, info_win, "Left click to spawn", 10, 118, 200, 16, N_GUI_ALIGN_LEFT);
224
225 al_clear_keyboard_state(NULL);
226 al_flush_event_queue(event_queue);
227
228 double last_fps_time = al_get_time();
229
230 int w = al_get_display_width(display);
231 int h = al_get_display_height(display);
232
233 do {
234 do {
235 ALLEGRO_EVENT ev;
236
237 al_wait_for_event(event_queue, &ev);
238
239 if (ev.type == ALLEGRO_EVENT_KEY_DOWN) {
240 switch (ev.keyboard.keycode) {
241 case ALLEGRO_KEY_UP:
242 key[KEY_UP] = 1;
243 break;
244 case ALLEGRO_KEY_DOWN:
245 key[KEY_DOWN] = 1;
246 break;
247 case ALLEGRO_KEY_LEFT:
248 key[KEY_LEFT] = 1;
249 break;
250 case ALLEGRO_KEY_RIGHT:
251 key[KEY_RIGHT] = 1;
252 break;
253 case ALLEGRO_KEY_ESCAPE:
254 key[KEY_ESC] = 1;
255 break;
256 case ALLEGRO_KEY_SPACE:
257 key[KEY_SPACE] = 1;
258 break;
259 case ALLEGRO_KEY_LCTRL:
260 case ALLEGRO_KEY_RCTRL:
261 key[KEY_CTRL] = 1;
262 default:
263 break;
264 }
265 } else if (ev.type == ALLEGRO_EVENT_KEY_UP) {
266 switch (ev.keyboard.keycode) {
267 case ALLEGRO_KEY_UP:
268 key[KEY_UP] = 0;
269 break;
270 case ALLEGRO_KEY_DOWN:
271 key[KEY_DOWN] = 0;
272 break;
273 case ALLEGRO_KEY_LEFT:
274 key[KEY_LEFT] = 0;
275 break;
276 case ALLEGRO_KEY_RIGHT:
277 key[KEY_RIGHT] = 0;
278 break;
279 case ALLEGRO_KEY_ESCAPE:
280 key[KEY_ESC] = 0;
281 break;
282 case ALLEGRO_KEY_SPACE:
283 key[KEY_SPACE] = 0;
284 break;
285 case ALLEGRO_KEY_LCTRL:
286 case ALLEGRO_KEY_RCTRL:
287 key[KEY_CTRL] = 0;
288 default:
289 break;
290 }
291 } else if (ev.type == ALLEGRO_EVENT_TIMER) {
292 if (al_get_timer_event_source(fps_timer) == ev.any.source) {
293 do_draw = 1;
294 } else if (al_get_timer_event_source(logic_timer) == ev.any.source) {
295 do_logic = 1;
296 }
297 } else if (ev.type == ALLEGRO_EVENT_MOUSE_AXES) {
298 mx = ev.mouse.x;
299 my = ev.mouse.y;
300 } else if (ev.type == ALLEGRO_EVENT_MOUSE_BUTTON_DOWN) {
301 if (ev.mouse.button == 1)
302 mouse_b1 = 1;
303 if (ev.mouse.button == 2)
304 mouse_b2 = 1;
305 } else if (ev.type == ALLEGRO_EVENT_MOUSE_BUTTON_UP) {
306 if (ev.mouse.button == 1)
307 mouse_b1 = 0;
308 if (ev.mouse.button == 2)
309 mouse_b2 = 0;
310 } else if (ev.type == ALLEGRO_EVENT_DISPLAY_RESIZE) {
311 al_acknowledge_resize(display);
312 w = al_get_display_width(display);
313 h = al_get_display_height(display);
314 n_gui_set_display_size(gui, (float)w, (float)h);
315 } else if (ev.type == ALLEGRO_EVENT_DISPLAY_SWITCH_IN || ev.type == ALLEGRO_EVENT_DISPLAY_SWITCH_OUT) {
316 al_clear_keyboard_state(display);
317 al_flush_event_queue(event_queue);
318 }
319
320 /* pass events to the GUI.
321 * n_gui_wants_mouse() is checked below before game mouse actions. */
323
324 } while (!al_is_event_queue_empty(event_queue));
325
326 /* dev mod: right click to temporary delete a block
327 left click to temporary add a block */
328 int mouse_button = -1;
329 if (mouse_b1 == 1)
330 mouse_button = 1;
331 if (mouse_b2 == 1)
332 mouse_button = 2;
333
334 if (do_logic == 1) {
336 if (mouse_button == 1 && !n_gui_wants_mouse(gui)) {
337 PHYSICS tmp_part = {0};
338 tmp_part.sz = 10;
339 for (int it = 0; it < 100; it++) {
340 VECTOR3D_SET(tmp_part.speed,
341 500 - rand() % 1000,
342 500 - rand() % 1000,
343 0.0);
344 VECTOR3D_SET(tmp_part.position, mx, my, 0.0);
345 VECTOR3D_SET(tmp_part.acceleration, 0.0, 0.0, 0.0);
346 VECTOR3D_SET(tmp_part.orientation, 0.0, 0.0, 0.0);
347 if (add_particle(particle_system_effects, -1, PIXEL_PART, 1000 + rand() % 3000, 1 + rand() % 3,
348 al_map_rgba((unsigned char)(55 + rand() % 200), (unsigned char)(55 + rand() % 200), (unsigned char)(55 + rand() % 200), (unsigned char)(10 + rand() % 245)), tmp_part) == TRUE) {
350 }
351 }
352 }
353 do_logic = 0;
354 }
355
356 if (do_draw == 1) {
357 al_set_target_bitmap(scrbuf);
358 al_clear_to_color(al_map_rgba(0, 0, 0, 255));
359 draw_particle(particle_system_effects, 0, 0, w, h, 50);
360
361 al_set_target_bitmap(al_get_backbuffer(display));
362
363 al_clear_to_color(al_map_rgba(0, 0, 0, 255));
364 al_draw_bitmap(scrbuf, 0, 0, 0);
365
366 /* update info labels */
367 frame_count++;
368 double now = al_get_time();
369 fps_accum += (now - last_fps_time);
370 last_fps_time = now;
371 if (fps_accum >= 1.0) {
373 frame_count = 0;
374 fps_accum = 0.0;
375 }
376
377 char buf[128];
378 size_t active_count = 0;
379 size_t max_count = 0;
381 active_count = particle_system_effects->list->nb_items;
383 }
384
385 snprintf(buf, sizeof(buf), "Active: %zu", active_count);
387
388 snprintf(buf, sizeof(buf), "Max: %zu", max_count);
390
391 snprintf(buf, sizeof(buf), "Spawned: %d", total_spawned);
393
394 snprintf(buf, sizeof(buf), "Mouse: %d, %d", mx, my);
396
397 snprintf(buf, sizeof(buf), "FPS: %.1f", fps_display);
399
400 /* draw GUI on top of particles */
402
403 /* mouse pointer */
404 al_draw_line((float)(mx - 5), (float)my, (float)(mx + 5), (float)my, al_map_rgb(255, 0, 0), 1);
405 al_draw_line((float)mx, (float)(my + 5), (float)mx, (float)(my - 5), al_map_rgb(255, 0, 0), 1);
406
407 al_flip_display();
408 do_draw = 0;
409 }
410
411 } while (!key[KEY_ESC] && !DONE);
412
415 al_destroy_font(gui_font);
416 al_destroy_bitmap(scrbuf);
417 al_destroy_bitmap(scr_buf);
418
419 al_uninstall_system();
420
421 return 0;
422}
int main(void)
ALLEGRO_TIMER * fps_timer
Definition ex_fluid.c:66
int getoptret
Definition ex_fluid.c:60
int DONE
Definition ex_fluid.c:59
int log_level
Definition ex_fluid.c:61
ALLEGRO_TIMER * logic_timer
Definition ex_fluid.c:67
ALLEGRO_DISPLAY * display
Definition ex_fluid.c:53
#define WIDTH
Definition ex_gui.c:36
#define HEIGHT
Definition ex_gui.c:37
static N_GUI_CTX * gui
LIST * active_object
static double fps_accum
static int lbl_mouse_pos
static int lbl_particle_count
static double fps_display
static int total_spawned
static int lbl_fps_info
static int lbl_spawn_info
static int lbl_particle_max
ALLEGRO_BITMAP * scr_buf
PARTICLE_SYSTEM * particle_system_effects
static int frame_count
char * key
void n_abort(char const *format,...)
abort program with a text
Definition n_common.c:52
void n_gui_set_display_size(N_GUI_CTX *ctx, float w, float h)
Set the display (viewport) size for global scrollbar computation.
Definition n_gui.c:5176
#define N_GUI_ALIGN_LEFT
left aligned text
Definition n_gui.h:156
int n_gui_wants_mouse(N_GUI_CTX *ctx)
Check if the mouse is currently over any open GUI window.
Definition n_gui.c:7344
void n_gui_label_set_text(N_GUI_CTX *ctx, int widget_id, const char *text)
set the text of a label widget
Definition n_gui.c:2178
int n_gui_process_event(N_GUI_CTX *ctx, ALLEGRO_EVENT event)
Process an allegro event through the GUI system.
Definition n_gui.c:5767
int n_gui_add_label(N_GUI_CTX *ctx, int window_id, const char *text, float x, float y, float w, float h, int align)
Add a static text label.
Definition n_gui.c:1497
void n_gui_set_virtual_size(N_GUI_CTX *ctx, float w, float h)
Set the virtual canvas size for resolution-independent scaling.
Definition n_gui.c:4974
void n_gui_draw(N_GUI_CTX *ctx)
Draw all visible windows and their widgets.
Definition n_gui.c:4800
int n_gui_add_window(N_GUI_CTX *ctx, const char *title, float x, float y, float w, float h)
Add a new pseudo-window to the context.
Definition n_gui.c:581
N_GUI_CTX * n_gui_new_ctx(ALLEGRO_FONT *default_font)
Create a new GUI context.
Definition n_gui.c:466
void n_gui_destroy_ctx(N_GUI_CTX **ctx)
Destroy a GUI context and all its windows/widgets.
Definition n_gui.c:523
The top-level GUI context that holds all windows.
Definition n_gui.h:882
size_t nb_max_items
Maximum number of items in the list.
Definition n_list.h:62
size_t nb_items
number of item currently in the list
Definition n_list.h:60
int list_destroy(LIST **list)
Empty and Free a list container.
Definition n_list.c:547
LIST * new_generic_list(size_t max_items)
Initialiaze a generic list container to max_items pointers.
Definition n_list.c:36
#define MAX_LIST_ITEMS
flag to pass to new_generic_list for the maximum possible number of item in a list
Definition n_list.h:74
Structure of a generic LIST container.
Definition n_list.h:58
#define n_log(__LEVEL__,...)
Logging function wrapper to get line and func.
Definition n_log.h:88
#define LOG_DEBUG
debug-level messages
Definition n_log.h:83
#define LOG_ERR
error conditions
Definition n_log.h:75
int set_log_file(char *file)
Set the logging to a file instead of stderr.
Definition n_log.c:167
void set_log_level(const int log_level)
Set the global log level value ( static int LOG_LEVEL )
Definition n_log.c:120
#define LOG_NOTICE
normal but significant condition
Definition n_log.h:79
#define LOG_INFO
informational
Definition n_log.h:81
#define free_nstr(__ptr)
free a N_STR structure and set the pointer to NULL
Definition n_str.h:201
#define nstrprintf(__nstr_var, __format,...)
Macro to quickly allocate and sprintf to N_STR.
Definition n_str.h:115
A box including a string and his lenght.
Definition n_str.h:60
LIST * list
list of PARTICLE pointers
int draw_particle(PARTICLE_SYSTEM *psys, double xpos, double ypos, int w, int h, double range)
draw particles of a particle system
#define PIXEL_PART
pixel particle
Definition n_particles.h:67
int manage_particle(PARTICLE_SYSTEM *psys)
update particles positions using particle system internal timer
int add_particle(PARTICLE_SYSTEM *psys, int spr, int mode, int lifetime, int size, ALLEGRO_COLOR color, PHYSICS object)
add a particle to a particle system
Definition n_particles.c:78
int init_particle_system(PARTICLE_SYSTEM **psys, int max, double x, double y, double z, int max_sprites)
initialize a particle system
Definition n_particles.c:40
Structure of a particle system.
VECTOR3D speed
vx,vy,vz actual speed
Definition n_3d.h:75
VECTOR3D orientation
ax,ay,az actual rotation position
Definition n_3d.h:79
int sz
size
Definition n_3d.h:89
VECTOR3D position
x,y,z actual position
Definition n_3d.h:73
VECTOR3D acceleration
ax,ay,az actual acceleration
Definition n_3d.h:77
#define VECTOR3D_SET(VECTOR, X, Y, Z)
helper to set a VECTOR3D position
Definition n_3d.h:61
structure of the physics of an object
Definition n_3d.h:69
Animations graphics and animations parameters.
Common headers and low-level functions & define.
GUI system: buttons, sliders, text areas, checkboxes, scrollbars, dropdown menus, windows.
static FILE * log_file
static FILE handling if logging to file is enabled
Definition n_log.c:83
__attribute__((unused))
Definition n_network.c:1233
Particles management.