ec5a8276f0ca9fdd0f364e9b16b88f8a61912a24
[vrlugburz] / tools / dunger / src / main.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <GL/glut.h>
5 #include <utk/cubertk.h>
6 #include <drawtext.h>
7 #include "level.h"
8 #include "lview.h"
9
10 static int init(void);
11 static void cleanup(void);
12 static void display(void);
13 static void reshape(int x, int y);
14 static void keyb(unsigned char key, int x, int y);
15 static void keyup(unsigned char key, int x, int y);
16 static void mouse(int bn, int st, int x, int y);
17 static void motion(int x, int y);
18
19 static void cb_new(utk_event *ev, void *data);
20 static void cb_new_ok(utk_event *ev, void *data);
21 static void cb_open(utk_event *ev, void *data);
22 static void cb_open_ok(utk_event *ev, void *data);
23 static void cb_save(utk_event *ev, void *data);
24 static void cb_save_ok(utk_event *ev, void *data);
25
26 static void cb_cancel(utk_event *ev, void *data);
27
28 static int parse_args(int argc, char **argv);
29
30
31 static void ucolor(int r, int g, int b, int a);
32 static void uclip(int x1, int y1, int x2, int y2);
33 static void uimage(int x, int y, const void *pix, int xsz, int ysz);
34 static void urect(int x1, int y1, int x2, int y2);
35 static void uline(int x1, int y1, int x2, int y2, int width);
36 static void utext(int x, int y, const char *txt, int sz);
37 static int utextspacing(void);
38 static int utextwidth(const char *txt, int sz);
39
40
41 int win_width, win_height;
42 int view_width, view_height;
43 float view_panx, view_pany, view_zoom = 1.0f;
44
45 static int bnstate[8];
46 int mousex, mousey, clickx, clicky;
47
48 static float uiscale = 1.0f;
49 #define UISPLIT 150
50 static int splitx;
51
52 #define FONTSZ  16
53 static struct dtx_font *uifont;
54 static utk_widget *uiroot, *uiwin_new;
55 static utk_widget *uigrab;
56 static utk_widget *cbox_newsz;
57
58 static struct level lvl;
59
60 static const char *opt_fname;
61
62
63 int main(int argc, char **argv)
64 {
65         glutInit(&argc, argv);
66
67         if(parse_args(argc, argv) == -1) {
68                 return 1;
69         }
70
71         glutInitWindowSize(1280, 800);
72         glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_MULTISAMPLE);
73         glutCreateWindow("dunger");
74
75         win_width = glutGet(GLUT_WINDOW_WIDTH);
76         win_height = glutGet(GLUT_WINDOW_HEIGHT);
77
78         glutDisplayFunc(display);
79         glutReshapeFunc(reshape);
80         glutKeyboardFunc(keyb);
81         glutKeyboardUpFunc(keyup);
82         glutMouseFunc(mouse);
83         glutMotionFunc(motion);
84         glutPassiveMotionFunc(motion);
85
86         if(init() == -1) {
87                 return 1;
88         }
89         atexit(cleanup);
90
91         glutMainLoop();
92         return 0;
93 }
94
95
96 static int init(void)
97 {
98         int pad;
99         utk_widget *win, *hbox, *vbox;
100
101         glEnable(GL_MULTISAMPLE);
102
103         glClearColor(0.15, 0.15, 0.15, 1);
104
105         if(!(uifont = dtx_open_font("uifont.ttf", 0))) {
106                 fprintf(stderr, "failed to open uifont.ttf\n");
107                 return -1;
108         }
109         dtx_prepare_range(uifont, FONTSZ, ' ', 'z');
110         dtx_use_font(uifont, FONTSZ);
111
112         if(!(uiroot = utk_init(win_width / uiscale, win_height / uiscale))) {
113                 fprintf(stderr, "failed to initialized ubertk\n");
114                 return -1;
115         }
116         utk_set_color_func(ucolor);
117         utk_set_clip_func(uclip);
118         utk_set_image_func(uimage);
119         utk_set_rect_func(urect);
120         utk_set_line_func(uline);
121         utk_set_text_func(utext);
122         utk_set_text_spacing_func(utextspacing);
123         utk_set_text_width_func(utextwidth);
124
125         win = utk_vbox(uiroot, 0, UTK_DEF_SPACING);
126         utk_set_pos(win, 15, 15);
127         utk_button(win, "New", 0, 0, cb_new, 0);
128         utk_button(win, "Open ...", 0, 0, cb_open, 0);
129         utk_button(win, "Save ...", 0, 0, cb_save, 0);
130
131         uiwin_new = utk_window(uiroot, (win_width - 220) / 2, (win_height - 150) / 2,
132                         220, 150, "New level");
133         vbox = utk_vbox(uiwin_new, UTK_DEF_PADDING, UTK_DEF_SPACING);
134         {
135                 const char *items[] = {"small (16x16)", "medium (24x24)", "large (32x32)"};
136                 cbox_newsz = utk_combobox_items(vbox, items, sizeof items / sizeof *items, 0, 0);
137                 utk_select(cbox_newsz, 2);
138         }
139         hbox = utk_hbox(vbox, UTK_DEF_PADDING, UTK_DEF_SPACING);
140         utk_button(hbox, "OK", 0, 0, cb_new_ok, 0);
141         utk_button(hbox, "Cancel", 0, 0, cb_cancel, uiwin_new);
142         pad = utk_get_padding(uiwin_new);
143         utk_set_size(uiwin_new, utk_get_width(vbox) + pad * 2.0f, utk_get_height(vbox) + pad * 2.0f);
144
145
146         if(opt_fname) {
147                 if(load_level(&lvl, opt_fname) == -1) {
148                         fprintf(stderr, "failed to load level: %s\n", opt_fname);
149                         return -1;
150                 }
151         } else {
152                 if(init_level(&lvl, 32, 32) == -1) {
153                         fprintf(stderr, "failed to create level\n");
154                         return -1;
155                 }
156         }
157         if(init_lview(&lvl) == -1) {
158                 return -1;
159         }
160
161         splitx = UISPLIT * uiscale;
162         view_width = win_width - splitx;
163         view_height = win_height;
164
165         return 0;
166 }
167
168 static void cleanup(void)
169 {
170         destroy_lview();
171         destroy_level(&lvl);
172         dtx_close_font(uifont);
173         utk_close(uiroot);
174 }
175
176 static void display(void)
177 {
178         glClear(GL_COLOR_BUFFER_BIT);
179
180         glMatrixMode(GL_MODELVIEW);
181         glLoadIdentity();
182
183         /* draw view */
184
185         glMatrixMode(GL_PROJECTION);
186         glLoadIdentity();
187         glOrtho(0, view_width, 0, view_height, -1, 1);
188         glViewport(splitx, 0, view_width, view_height);
189
190         glBegin(GL_QUADS);
191         glColor3f(0.1, 0.1, 0.1);
192         glVertex2f(0, 0);
193         glVertex2f(view_width, 0);
194         glVertex2f(view_width, view_height);
195         glVertex2f(0, view_height);
196         glEnd();
197
198         draw_lview();
199
200         /* draw UI */
201
202         glMatrixMode(GL_PROJECTION);
203         glLoadIdentity();
204         glOrtho(0, win_width, win_height, 0, -1, 1);
205         glViewport(0, 0, win_width, win_height);
206
207         glBegin(GL_QUADS);
208         glColor3f(0.25, 0.25, 0.25);
209         glVertex2f(0, 0);
210         glVertex2f(splitx, 0);
211         glVertex2f(splitx, win_height);
212         glVertex2f(0, win_height);
213         glEnd();
214
215         glEnable(GL_BLEND);
216         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
217         utk_draw(uiroot);
218         glDisable(GL_BLEND);
219
220
221         glutSwapBuffers();
222 }
223
224 static void reshape(int x, int y)
225 {
226         win_width = x;
227         win_height = y;
228
229         if(uiroot) {
230                 utk_set_size(uiroot, x / uiscale, y / uiscale);
231         }
232
233         lview_viewport(splitx, 0, x - splitx, y);
234 }
235
236 static void keyb(unsigned char key, int x, int y)
237 {
238         switch(key) {
239         case 27:
240                 if(uigrab) {
241                         if(utk_is_dialog(uigrab)) {
242                                 utk_destroy_window(uigrab);
243                         } else {
244                                 utk_hide(uigrab);
245                         }
246                         uigrab = 0;
247                 } else {
248                         exit(0);
249                 }
250                 return;
251
252         default:
253                 break;
254         }
255
256         utk_keyboard_event(key, 1);
257         glutPostRedisplay();
258 }
259
260 static void keyup(unsigned char key, int x, int y)
261 {
262         utk_keyboard_event(key, 0);
263         glutPostRedisplay();
264 }
265
266 static void mouse(int bn, int st, int x, int y)
267 {
268         int bidx = bn - GLUT_LEFT_BUTTON;
269         int press = st == GLUT_DOWN;
270
271         bnstate[bidx] = press;
272         mousex = x;
273         mousey = y;
274
275         if(bn <= 2) {
276                 if(press) {
277                         clickx = x;
278                         clicky = y;
279                 } else {
280                         clickx = clicky = -1;
281                 }
282         } else if(bn == 3) {
283                 if(press) zoom_lview(0.1);
284         } else if(bn == 4) {
285                 if(press) zoom_lview(-0.1);
286         }
287
288         if(!uigrab) {
289                 lview_mbutton(bidx, press, x, y);
290         }
291
292         utk_mbutton_event(bidx, press, x / uiscale, y / uiscale);
293         glutPostRedisplay();
294 }
295
296 static void motion(int x, int y)
297 {
298         int dx, dy;
299         dx = x - mousex;
300         dy = y - mousey;
301         mousex = x;
302         mousey = y;
303
304         if(clickx >= splitx) {
305                 if(bnstate[1]) {
306                         pan_lview(-dx, dy);
307                 }
308         }
309
310         if(!uigrab) {
311                 lview_mouse(x, y);
312         }
313
314         utk_mmotion_event(x / uiscale, y / uiscale);
315         glutPostRedisplay();
316 }
317
318 static void cb_new(utk_event *ev, void *data)
319 {
320         utk_show(uiwin_new);
321         uigrab = uiwin_new;
322 }
323
324 static void cb_new_ok(utk_event *ev, void *data)
325 {
326         static int levsz[] = {16, 24, 32};
327         int sz;
328         struct level newlvl;
329
330         sz = levsz[utk_get_selected(cbox_newsz)];
331
332         if(init_level(&newlvl, sz, sz) == -1) {
333                 utk_message_dialog("failed to create new level", UTK_MSG_TYPE_ERROR,
334                                 UTK_MSG_BN_OK, cb_cancel, 0);
335                 return;
336         }
337
338         destroy_level(&lvl);
339         destroy_lview();
340
341         lvl = newlvl;
342         init_lview(&lvl);
343
344         utk_hide(uiwin_new);
345         uigrab = 0;
346 }
347
348 static void cb_open(utk_event *ev, void *data)
349 {
350         uigrab = utk_file_dialog(UTK_FILE_DIALOG_OPEN, 0, "Level file (*.lvl) [.lvl]", 0, cb_open_ok, 0);
351 }
352
353 static void cb_open_ok(utk_event *ev, void *data)
354 {
355         char *errmsg = 0;
356         const char *path;
357
358         utk_widget *dlg = utk_event_widget(ev);
359         path = utk_file_dialog_path(dlg);
360         printf("selected: %s\n", path);
361         if(load_level(&lvl, path) == -1) {
362                 errmsg = alloca(strlen(path) + 32);
363                 sprintf(errmsg, "Failed to load level: %s", path);
364         }
365         utk_destroy_window(dlg);
366         if(uigrab == dlg) uigrab = 0;
367
368         if(errmsg) {
369                 uigrab = utk_message_dialog(errmsg, UTK_MSG_TYPE_ERROR, UTK_MSG_BN_OK, cb_cancel, 0);
370         }
371 }
372
373 static void cb_save(utk_event *ev, void *data)
374 {
375         uigrab = utk_file_dialog(UTK_FILE_DIALOG_SAVE, 0, "Level file (*.lvl) [.lvl]", 0, cb_save_ok, 0);
376 }
377
378 static void cb_save_ok(utk_event *ev, void *data)
379 {
380         char *errmsg = 0;
381         const char *path;
382
383         utk_widget *dlg = utk_event_widget(ev);
384         path = utk_file_dialog_path(dlg);
385         printf("selected: %s\n", path);
386         if(save_level(&lvl, path) == -1) {
387                 errmsg = alloca(strlen(path) + 32);
388                 sprintf(errmsg, "Failed to save level file: %s", path);
389         }
390         utk_destroy_window(dlg);
391         if(uigrab == dlg) uigrab = 0;
392
393         if(errmsg) {
394                 uigrab = utk_message_dialog(errmsg, UTK_MSG_TYPE_ERROR, UTK_MSG_BN_OK, cb_cancel, 0);
395         }
396 }
397
398 static void cb_cancel(utk_event *ev, void *data)
399 {
400         if(!data) data = utk_get_window(utk_event_widget(ev));
401
402         if(utk_is_dialog(data)) {
403                 utk_destroy_window(data);
404         } else {
405                 utk_hide(data);
406         }
407         uigrab = 0;
408 }
409
410 static int parse_args(int argc, char **argv)
411 {
412         int i;
413
414         for(i=1; i<argc; i++) {
415                 if(argv[i][0] == '-') {
416                         if(strcmp(argv[i], "-uiscale") == 0) {
417                                 if(!argv[++i] || !(uiscale = atoi(argv[i]))) {
418                                         fprintf(stderr, "-uiscale should be followed by a positive number\n");
419                                         return -1;
420                                 }
421                         } else if(strcmp(argv[i], "-help") == 0 || strcmp(argv[i], "-h") == 0) {
422                                 printf("Usage: %s [options]\n", argv[0]);
423                                 printf("Options:\n");
424                                 printf(" -uiscale <scale>: UI scale factor (default: 1)\n");
425                                 printf(" -h,-help: print usage and exit\n");
426                                 exit(0);
427                         } else {
428                                 fprintf(stderr, "unknown option: %s\n", argv[i]);
429                                 return -1;
430                         }
431                 } else {
432                         if(opt_fname) {
433                                 fprintf(stderr, "unexpected argument: %s\n", argv[i]);
434                                 return -1;
435                         }
436                         opt_fname = argv[i];
437                 }
438         }
439         return 0;
440 }
441
442 /* --- ubertk callbacks --- */
443
444 static void ucolor(int r, int g, int b, int a)
445 {
446         glColor4ub(r, g, b, a);
447 }
448
449 static void uclip(int x1, int y1, int x2, int y2)
450 {
451         if(!(x1 | y1 | x2 | y2)) {
452                 glDisable(GL_SCISSOR_TEST);
453         } else {
454                 glEnable(GL_SCISSOR_TEST);
455         }
456
457         x1 *= uiscale;
458         y1 *= uiscale;
459         x2 *= uiscale;
460         y2 *= uiscale;
461
462         glScissor(x1, win_height - y2, x2 - x1, y2 - y1);
463 }
464
465 static void uimage(int x, int y, const void *pix, int xsz, int ysz)
466 {
467         glPixelZoom(1, -1);
468         glRasterPos2f(x * uiscale, y * uiscale);
469         glDrawPixels(xsz, ysz, GL_BGRA, GL_UNSIGNED_BYTE, pix);
470 }
471
472 static void urect(int x1, int y1, int x2, int y2)
473 {
474         glRectf(x1 * uiscale, y1 * uiscale, x2 * uiscale, y2 * uiscale);
475 }
476
477 static void uline(int x1, int y1, int x2, int y2, int width)
478 {
479         glLineWidth(width);
480         glBegin(GL_LINES);
481         glVertex2f(x1 * uiscale, y1 * uiscale);
482         glVertex2f(x2 * uiscale, y2 * uiscale);
483         glEnd();
484 }
485
486 static void utext(int x, int y, const char *txt, int sz)
487 {
488         glMatrixMode(GL_PROJECTION);
489         glPushMatrix();
490         glTranslatef(x * uiscale, (y - dtx_baseline()) * uiscale, 0);
491         glScalef(uiscale, -uiscale, 1);
492
493         dtx_string(txt);
494         dtx_flush();
495
496         glPopMatrix();
497 }
498
499 static int utextspacing(void)
500 {
501         return dtx_line_height();
502 }
503
504 static int utextwidth(const char *txt, int sz)
505 {
506         return dtx_string_width(txt);
507 }