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 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
14 * implied. See the License for the specific language governing
15 * permissions and limitations under the License.
16 *
17 * SPDX-License-Identifier: Apache-2.0
18 */
19
28#define WIDTH 640
29#define HEIGHT 480
30
31#define ALLEGRO_UNSTABLE 1
32
33#include "nilorea/n_common.h"
34#include "nilorea/n_particles.h"
35#include "nilorea/n_anim.h"
36#include "nilorea/n_gui.h"
37#include <allegro5/allegro_ttf.h>
38
39ALLEGRO_DISPLAY* display = NULL;
40
41int DONE = 0, /* Flag to check if we are always running */
42 getoptret = 0, /* launch parameter check */
43 log_level = LOG_ERR; /* default LOG_LEVEL */
44
45ALLEGRO_BITMAP* scr_buf = NULL;
46
47ALLEGRO_TIMER* fps_timer = NULL;
48ALLEGRO_TIMER* logic_timer = NULL;
49LIST* active_object = NULL; /* list of active objects */
51
52/* GUI widget ids for info labels */
53static int lbl_particle_count = -1;
54static int lbl_particle_max = -1;
55static int lbl_mouse_pos = -1;
56static int lbl_spawn_info = -1;
57static int lbl_fps_info = -1;
58
59/* stats */
60static int total_spawned = 0;
61static int frame_count = 0;
62static double fps_display = 0.0;
63static double fps_accum = 0.0;
64
65int main(int argc, char* argv[]) {
67
68 N_STR* log_file = NULL;
69 nstrprintf(log_file, "%s.log", argv[0]);
70 /*set_log_file( _nstr( log_file ) );*/
72
73 n_log(LOG_NOTICE, "%s is starting ...", argv[0]);
74
75 /* allegro 5 + addons loading */
76 if (!al_init()) {
77 n_abort("Could not init Allegro.\n");
78 }
79 if (!al_install_audio()) {
80 n_abort("Unable to initialize audio addon\n");
81 }
82 if (!al_init_acodec_addon()) {
83 n_abort("Unable to initialize acoded addon\n");
84 }
85 if (!al_init_image_addon()) {
86 n_abort("Unable to initialize image addon\n");
87 }
88 if (!al_init_primitives_addon()) {
89 n_abort("Unable to initialize primitives addon\n");
90 }
91 if (!al_init_font_addon()) {
92 n_abort("Unable to initialize font addon\n");
93 }
94 if (!al_init_ttf_addon()) {
95 n_abort("Unable to initialize ttf_font addon\n");
96 }
97 if (!al_install_keyboard()) {
98 n_abort("Unable to initialize keyboard handler\n");
99 }
100 if (!al_install_mouse()) {
101 n_abort("Unable to initialize mouse handler\n");
102 }
103 ALLEGRO_EVENT_QUEUE* event_queue = NULL;
104
105 event_queue = al_create_event_queue();
106 if (!event_queue) {
107 fprintf(stderr, "failed to create event_queue!\n");
108 al_destroy_display(display);
109 return -1;
110 }
111
112 char ver_str[128] = "";
113 while ((getoptret = getopt(argc, argv, "hvV:L:")) != EOF) {
114 switch (getoptret) {
115 case 'h':
116 n_log(LOG_NOTICE, "\n %s -h help -v version -V DEBUGLEVEL (NOLOG,VERBOSE,NOTICE,ERROR,DEBUG)", argv[0]);
117 exit(TRUE);
118 case 'v':
119 sprintf(ver_str, "%s %s", __DATE__, __TIME__);
120 exit(TRUE);
121 break;
122 case 'V':
123 if (!strncmp("NOTICE", optarg, 6)) {
125 } else {
126 if (!strncmp("VERBOSE", optarg, 7)) {
128 } else {
129 if (!strncmp("ERROR", optarg, 5)) {
131 } else {
132 if (!strncmp("DEBUG", optarg, 5)) {
134 } else {
135 n_log(LOG_ERR, "%s is not a valid log level", optarg);
136 exit(FALSE);
137 }
138 }
139 }
140 }
141 n_log(LOG_NOTICE, "LOG LEVEL UP TO: %d", log_level);
143 break;
144 case 'L':
145 n_log(LOG_NOTICE, "LOG FILE: %s", optarg);
146 set_log_file(optarg);
147 break;
148 case '?': {
149 switch (optopt) {
150 case 'V':
151 n_log(LOG_ERR, "\nPlease specify a log level after -V. \nAvailable values: NOLOG,VERBOSE,NOTICE,ERROR,DEBUG");
152 break;
153 case 'L':
154 n_log(LOG_ERR, "\nPlease specify a log file after -L");
155 default:
156 break;
157 }
158 }
159 __attribute__((fallthrough));
160 default:
161 n_log(LOG_ERR, "\n %s -h help -v version -V DEBUGLEVEL (NOLOG,VERBOSE,NOTICE,ERROR,DEBUG) -L logfile", argv[0]);
162 exit(FALSE);
163 }
164 }
165
166 fps_timer = al_create_timer(1.0 / 30.0);
167 logic_timer = al_create_timer(1.0 / 50.0);
168
169 al_set_new_display_flags(ALLEGRO_OPENGL | ALLEGRO_WINDOWED);
170 display = al_create_display(WIDTH, HEIGHT);
171 if (!display) {
172 n_abort("Unable to create display\n");
173 }
174 al_set_window_title(display, argv[0]);
175
176 al_set_new_bitmap_flags(ALLEGRO_VIDEO_BITMAP);
177
178 DONE = 0;
179
181
182 enum APP_KEYS {
183 KEY_UP,
184 KEY_DOWN,
185 KEY_LEFT,
186 KEY_RIGHT,
187 KEY_ESC,
188 KEY_SPACE,
189 KEY_CTRL
190 };
191 int key[7] = {false, false, false, false, false, false, false};
192
193 al_register_event_source(event_queue, al_get_display_event_source(display));
194
195 al_start_timer(fps_timer);
196 al_start_timer(logic_timer);
197 al_register_event_source(event_queue, al_get_timer_event_source(fps_timer));
198 al_register_event_source(event_queue, al_get_timer_event_source(logic_timer));
199
200 al_register_event_source(event_queue, al_get_keyboard_event_source());
201 al_register_event_source(event_queue, al_get_mouse_event_source());
202
203 ALLEGRO_BITMAP* scrbuf = al_create_bitmap(WIDTH, HEIGHT);
204
205 al_hide_mouse_cursor(display);
206
207 init_particle_system(&particle_system_effects, 10000, 0, 0, 0, 100);
208
209 int mx = 0, my = 0, mouse_b1 = 0, mouse_b2 = 0;
210 int do_draw = 0, do_logic = 0;
211
212 /* Create Info GUI */
213 ALLEGRO_FONT* gui_font = al_create_builtin_font();
214 N_GUI_CTX* gui = n_gui_new_ctx(gui_font);
215 n_gui_set_display_size(gui, (float)WIDTH, (float)HEIGHT);
216 n_gui_set_virtual_size(gui, (float)WIDTH, (float)HEIGHT);
217
218 int info_win = n_gui_add_window(gui, "Particle Info", 10, 10, 220, 180);
219 lbl_particle_count = n_gui_add_label(gui, info_win, "Active: 0", 10, 10, 200, 16, N_GUI_ALIGN_LEFT);
220 lbl_particle_max = n_gui_add_label(gui, info_win, "Max: 10000", 10, 30, 200, 16, N_GUI_ALIGN_LEFT);
221 lbl_spawn_info = n_gui_add_label(gui, info_win, "Spawned: 0", 10, 50, 200, 16, N_GUI_ALIGN_LEFT);
222 lbl_mouse_pos = n_gui_add_label(gui, info_win, "Mouse: 0, 0", 10, 70, 200, 16, N_GUI_ALIGN_LEFT);
223 lbl_fps_info = n_gui_add_label(gui, info_win, "FPS: 0", 10, 90, 200, 16, N_GUI_ALIGN_LEFT);
224 n_gui_add_label(gui, info_win, "Left click to spawn", 10, 118, 200, 16, N_GUI_ALIGN_LEFT);
225
226 al_clear_keyboard_state(NULL);
227 al_flush_event_queue(event_queue);
228
229 double last_fps_time = al_get_time();
230
231 int w = al_get_display_width(display);
232 int h = al_get_display_height(display);
233
234 do {
235 do {
236 ALLEGRO_EVENT ev;
237
238 al_wait_for_event(event_queue, &ev);
239
240 if (ev.type == ALLEGRO_EVENT_KEY_DOWN) {
241 switch (ev.keyboard.keycode) {
242 case ALLEGRO_KEY_UP:
243 key[KEY_UP] = 1;
244 break;
245 case ALLEGRO_KEY_DOWN:
246 key[KEY_DOWN] = 1;
247 break;
248 case ALLEGRO_KEY_LEFT:
249 key[KEY_LEFT] = 1;
250 break;
251 case ALLEGRO_KEY_RIGHT:
252 key[KEY_RIGHT] = 1;
253 break;
254 case ALLEGRO_KEY_ESCAPE:
255 key[KEY_ESC] = 1;
256 break;
257 case ALLEGRO_KEY_SPACE:
258 key[KEY_SPACE] = 1;
259 break;
260 case ALLEGRO_KEY_LCTRL:
261 case ALLEGRO_KEY_RCTRL:
262 key[KEY_CTRL] = 1;
263 default:
264 break;
265 }
266 } else if (ev.type == ALLEGRO_EVENT_KEY_UP) {
267 switch (ev.keyboard.keycode) {
268 case ALLEGRO_KEY_UP:
269 key[KEY_UP] = 0;
270 break;
271 case ALLEGRO_KEY_DOWN:
272 key[KEY_DOWN] = 0;
273 break;
274 case ALLEGRO_KEY_LEFT:
275 key[KEY_LEFT] = 0;
276 break;
277 case ALLEGRO_KEY_RIGHT:
278 key[KEY_RIGHT] = 0;
279 break;
280 case ALLEGRO_KEY_ESCAPE:
281 key[KEY_ESC] = 0;
282 break;
283 case ALLEGRO_KEY_SPACE:
284 key[KEY_SPACE] = 0;
285 break;
286 case ALLEGRO_KEY_LCTRL:
287 case ALLEGRO_KEY_RCTRL:
288 key[KEY_CTRL] = 0;
289 default:
290 break;
291 }
292 } else if (ev.type == ALLEGRO_EVENT_TIMER) {
293 if (al_get_timer_event_source(fps_timer) == ev.any.source) {
294 do_draw = 1;
295 } else if (al_get_timer_event_source(logic_timer) == ev.any.source) {
296 do_logic = 1;
297 }
298 } else if (ev.type == ALLEGRO_EVENT_MOUSE_AXES) {
299 mx = ev.mouse.x;
300 my = ev.mouse.y;
301 } else if (ev.type == ALLEGRO_EVENT_MOUSE_BUTTON_DOWN) {
302 if (ev.mouse.button == 1)
303 mouse_b1 = 1;
304 if (ev.mouse.button == 2)
305 mouse_b2 = 1;
306 } else if (ev.type == ALLEGRO_EVENT_MOUSE_BUTTON_UP) {
307 if (ev.mouse.button == 1)
308 mouse_b1 = 0;
309 if (ev.mouse.button == 2)
310 mouse_b2 = 0;
311 } else if (ev.type == ALLEGRO_EVENT_DISPLAY_RESIZE) {
312 al_acknowledge_resize(display);
313 w = al_get_display_width(display);
314 h = al_get_display_height(display);
315 n_gui_set_display_size(gui, (float)w, (float)h);
316 } else if (ev.type == ALLEGRO_EVENT_DISPLAY_SWITCH_IN || ev.type == ALLEGRO_EVENT_DISPLAY_SWITCH_OUT) {
317 al_clear_keyboard_state(display);
318 al_flush_event_queue(event_queue);
319 }
320
321 /* pass events to the GUI.
322 * n_gui_wants_mouse() is checked below before game mouse actions. */
324
325 } while (!al_is_event_queue_empty(event_queue));
326
327 /* dev mod: right click to temporary delete a block
328 left click to temporary add a block */
329 int mouse_button = -1;
330 if (mouse_b1 == 1)
331 mouse_button = 1;
332 if (mouse_b2 == 1)
333 mouse_button = 2;
334
335 if (do_logic == 1) {
337 if (mouse_button == 1 && !n_gui_wants_mouse(gui)) {
338 PHYSICS tmp_part = {0};
339 tmp_part.sz = 10;
340 for (int it = 0; it < 100; it++) {
341 VECTOR3D_SET(tmp_part.speed,
342 500 - rand() % 1000,
343 500 - rand() % 1000,
344 0.0);
345 VECTOR3D_SET(tmp_part.position, mx, my, 0.0);
346 VECTOR3D_SET(tmp_part.acceleration, 0.0, 0.0, 0.0);
347 VECTOR3D_SET(tmp_part.orientation, 0.0, 0.0, 0.0);
348 if (add_particle(particle_system_effects, -1, PIXEL_PART, 1000 + rand() % 3000, 1 + rand() % 3,
349 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) {
351 }
352 }
353 }
354 do_logic = 0;
355 }
356
357 if (do_draw == 1) {
358 al_set_target_bitmap(scrbuf);
359 al_clear_to_color(al_map_rgba(0, 0, 0, 255));
360 draw_particle(particle_system_effects, 0, 0, w, h, 50);
361
362 al_set_target_bitmap(al_get_backbuffer(display));
363
364 al_clear_to_color(al_map_rgba(0, 0, 0, 255));
365 al_draw_bitmap(scrbuf, 0, 0, 0);
366
367 /* update info labels */
368 frame_count++;
369 double now = al_get_time();
370 fps_accum += (now - last_fps_time);
371 last_fps_time = now;
372 if (fps_accum >= 1.0) {
374 frame_count = 0;
375 fps_accum = 0.0;
376 }
377
378 char buf[128];
379 size_t active_count = 0;
380 size_t max_count = 0;
382 active_count = particle_system_effects->list->nb_items;
384 }
385
386 snprintf(buf, sizeof(buf), "Active: %zu", active_count);
388
389 snprintf(buf, sizeof(buf), "Max: %zu", max_count);
391
392 snprintf(buf, sizeof(buf), "Spawned: %d", total_spawned);
394
395 snprintf(buf, sizeof(buf), "Mouse: %d, %d", mx, my);
397
398 snprintf(buf, sizeof(buf), "FPS: %.1f", fps_display);
400
401 /* draw GUI on top of particles */
403
404 /* mouse pointer */
405 al_draw_line((float)(mx - 5), (float)my, (float)(mx + 5), (float)my, al_map_rgb(255, 0, 0), 1);
406 al_draw_line((float)mx, (float)(my + 5), (float)mx, (float)(my - 5), al_map_rgb(255, 0, 0), 1);
407
408 al_flip_display();
409 do_draw = 0;
410 }
411
412 } while (!key[KEY_ESC] && !DONE);
413
416 al_destroy_font(gui_font);
417 al_destroy_bitmap(scrbuf);
418 al_destroy_bitmap(scr_buf);
419
420 al_uninstall_system();
421
422 return 0;
423}
int main(void)
ALLEGRO_TIMER * fps_timer
Definition ex_fluid.c:65
int getoptret
Definition ex_fluid.c:59
int DONE
Definition ex_fluid.c:58
int log_level
Definition ex_fluid.c:60
ALLEGRO_TIMER * logic_timer
Definition ex_fluid.c:66
ALLEGRO_DISPLAY * display
Definition ex_fluid.c:54
#define WIDTH
Definition ex_gui.c:42
#define HEIGHT
Definition ex_gui.c:43
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:53
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:9034
#define N_GUI_ALIGN_LEFT
left aligned text
Definition n_gui.h:189
int n_gui_wants_mouse(N_GUI_CTX *ctx)
Check if the mouse is currently over any open GUI window.
Definition n_gui.c:12337
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:4511
int n_gui_process_event(N_GUI_CTX *ctx, ALLEGRO_EVENT event)
Process an allegro event through the GUI system.
Definition n_gui.c:9836
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:2966
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:8831
void n_gui_draw(N_GUI_CTX *ctx)
Draw all visible windows and their widgets.
Definition n_gui.c:8542
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:1063
N_GUI_CTX * n_gui_new_ctx(ALLEGRO_FONT *default_font)
Create a new GUI context.
Definition n_gui.c:903
void n_gui_destroy_ctx(N_GUI_CTX **ctx)
Destroy a GUI context and all its windows/widgets.
Definition n_gui.c:995
The top-level GUI context that holds all windows.
Definition n_gui.h:1354
size_t nb_max_items
Maximum number of items in the list.
Definition n_list.h:63
size_t nb_items
number of item currently in the list
Definition n_list.h:61
int list_destroy(LIST **list)
Empty and Free a list container.
Definition n_list.c:548
LIST * new_generic_list(size_t max_items)
Initialiaze a generic list container to max_items pointers.
Definition n_list.c:37
#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:75
Structure of a generic LIST container.
Definition n_list.h:59
#define n_log(__LEVEL__,...)
Logging function wrapper to get line and func.
Definition n_log.h:89
#define LOG_DEBUG
debug-level messages
Definition n_log.h:84
#define LOG_ERR
error conditions
Definition n_log.h:76
int set_log_file(char *file)
Set the logging to a file instead of stderr.
Definition n_log.c:168
void set_log_level(const int log_level)
Set the global log level value ( static int LOG_LEVEL )
Definition n_log.c:121
#define LOG_NOTICE
normal but significant condition
Definition n_log.h:80
#define LOG_INFO
informational
Definition n_log.h:82
#define free_nstr(__ptr)
free a N_STR structure and set the pointer to NULL
Definition n_str.h:203
#define nstrprintf(__nstr_var, __format,...)
Macro to quickly allocate and sprintf to N_STR.
Definition n_str.h:117
A box including a string and his lenght.
Definition n_str.h:61
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:68
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:79
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:41
Structure of a particle system.
VECTOR3D speed
vx,vy,vz actual speed
Definition n_3d.h:76
VECTOR3D orientation
ax,ay,az actual rotation position
Definition n_3d.h:80
int sz
size
Definition n_3d.h:90
VECTOR3D position
x,y,z actual position
Definition n_3d.h:74
VECTOR3D acceleration
ax,ay,az actual acceleration
Definition n_3d.h:78
#define VECTOR3D_SET(VECTOR, X, Y, Z)
helper to set a VECTOR3D position
Definition n_3d.h:62
structure of the physics of an object
Definition n_3d.h:70
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:84
__attribute__((unused))
Definition n_network.c:1286
Particles management.