1 /* nuklear - v1.05 - public domain */
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <stdint.h>
5 #include <stdarg.h>
6 #include <string.h>
7 #include <math.h>
8 #include <assert.h>
9 #include <math.h>
10 #include <time.h>
11 #include <limits.h>
12
13 #include <GL/glew.h>
14 #include <GLFW/glfw3.h>
15
16 #define NK_INCLUDE_FIXED_TYPES
17 #define NK_INCLUDE_STANDARD_IO
18 #define NK_INCLUDE_DEFAULT_ALLOCATOR
19 #define NK_INCLUDE_VERTEX_BUFFER_OUTPUT
20 #define NK_INCLUDE_FONT_BAKING
21 #define NK_INCLUDE_DEFAULT_FONT
22 #define NK_IMPLEMENTATION
23 #include "../nuklear.h"
24
25 #define STB_IMAGE_IMPLEMENTATION
26 #include "stb_image.h"
27
28 /* macros */
29 #define WINDOW_WIDTH 1200
30 #define WINDOW_HEIGHT 800
31
32 #define MAX_VERTEX_MEMORY 512 * 1024
33 #define MAX_ELEMENT_MEMORY 128 * 1024
34
35 #define UNUSED(a) (void)a
36 #define MIN(a,b) ((a) < (b) ? (a) : (b))
37 #define MAX(a,b) ((a) < (b) ? (b) : (a))
38 #define LEN(a) (sizeof(a)/sizeof(a)[0])
39
40 #ifdef __APPLE__
41 #define NK_SHADER_VERSION "#version 150\n"
42 #else
43 #define NK_SHADER_VERSION "#version 300 es\n"
44 #endif
45
46 struct media {
47 struct nk_font *font_14;
48 struct nk_font *font_18;
49 struct nk_font *font_20;
50 struct nk_font *font_22;
51
52 struct nk_image unchecked;
53 struct nk_image checked;
54 struct nk_image rocket;
55 struct nk_image cloud;
56 struct nk_image pen;
57 struct nk_image play;
58 struct nk_image pause;
59 struct nk_image stop;
60 struct nk_image prev;
61 struct nk_image next;
62 struct nk_image tools;
63 struct nk_image dir;
64 struct nk_image copy;
65 struct nk_image convert;
66 struct nk_image del;
67 struct nk_image edit;
68 struct nk_image images[9];
69 struct nk_image menu[6];
70 };
71
72 /* ===============================================================
73 *
74 * CUSTOM WIDGET
75 *
76 * ===============================================================*/
77 static int
ui_piemenu(struct nk_context * ctx,struct nk_vec2 pos,float radius,struct nk_image * icons,int item_count)78 ui_piemenu(struct nk_context *ctx, struct nk_vec2 pos, float radius,
79 struct nk_image *icons, int item_count)
80 {
81 int ret = -1;
82 struct nk_rect total_space;
83 struct nk_rect bounds;
84 int active_item = 0;
85
86 /* pie menu popup */
87 struct nk_color border = ctx->style.window.border_color;
88 struct nk_style_item background = ctx->style.window.fixed_background;
89 ctx->style.window.fixed_background = nk_style_item_hide();
90 ctx->style.window.border_color = nk_rgba(0,0,0,0);
91
92 total_space = nk_window_get_content_region(ctx);
93 ctx->style.window.spacing = nk_vec2(0,0);
94 ctx->style.window.padding = nk_vec2(0,0);
95
96 if (nk_popup_begin(ctx, NK_POPUP_STATIC, "piemenu", NK_WINDOW_NO_SCROLLBAR,
97 nk_rect(pos.x - total_space.x - radius, pos.y - radius - total_space.y,
98 2*radius,2*radius)))
99 {
100 int i = 0;
101 struct nk_command_buffer* out = nk_window_get_canvas(ctx);
102 const struct nk_input *in = &ctx->input;
103
104 total_space = nk_window_get_content_region(ctx);
105 ctx->style.window.spacing = nk_vec2(4,4);
106 ctx->style.window.padding = nk_vec2(8,8);
107 nk_layout_row_dynamic(ctx, total_space.h, 1);
108 nk_widget(&bounds, ctx);
109
110 /* outer circle */
111 nk_fill_circle(out, bounds, nk_rgb(50,50,50));
112 {
113 /* circle buttons */
114 float step = (2 * 3.141592654f) / (float)(MAX(1,item_count));
115 float a_min = 0; float a_max = step;
116
117 struct nk_vec2 center = nk_vec2(bounds.x + bounds.w / 2.0f, bounds.y + bounds.h / 2.0f);
118 struct nk_vec2 drag = nk_vec2(in->mouse.pos.x - center.x, in->mouse.pos.y - center.y);
119 float angle = (float)atan2(drag.y, drag.x);
120 if (angle < -0.0f) angle += 2.0f * 3.141592654f;
121 active_item = (int)(angle/step);
122
123 for (i = 0; i < item_count; ++i) {
124 struct nk_rect content;
125 float rx, ry, dx, dy, a;
126 nk_fill_arc(out, center.x, center.y, (bounds.w/2.0f),
127 a_min, a_max, (active_item == i) ? nk_rgb(45,100,255): nk_rgb(60,60,60));
128
129 /* separator line */
130 rx = bounds.w/2.0f; ry = 0;
131 dx = rx * (float)cos(a_min) - ry * (float)sin(a_min);
132 dy = rx * (float)sin(a_min) + ry * (float)cos(a_min);
133 nk_stroke_line(out, center.x, center.y,
134 center.x + dx, center.y + dy, 1.0f, nk_rgb(50,50,50));
135
136 /* button content */
137 a = a_min + (a_max - a_min)/2.0f;
138 rx = bounds.w/2.5f; ry = 0;
139 content.w = 30; content.h = 30;
140 content.x = center.x + ((rx * (float)cos(a) - ry * (float)sin(a)) - content.w/2.0f);
141 content.y = center.y + (rx * (float)sin(a) + ry * (float)cos(a) - content.h/2.0f);
142 nk_draw_image(out, content, &icons[i], nk_rgb(255,255,255));
143 a_min = a_max; a_max += step;
144 }
145 }
146 {
147 /* inner circle */
148 struct nk_rect inner;
149 inner.x = bounds.x + bounds.w/2 - bounds.w/4;
150 inner.y = bounds.y + bounds.h/2 - bounds.h/4;
151 inner.w = bounds.w/2; inner.h = bounds.h/2;
152 nk_fill_circle(out, inner, nk_rgb(45,45,45));
153
154 /* active icon content */
155 bounds.w = inner.w / 2.0f;
156 bounds.h = inner.h / 2.0f;
157 bounds.x = inner.x + inner.w/2 - bounds.w/2;
158 bounds.y = inner.y + inner.h/2 - bounds.h/2;
159 nk_draw_image(out, bounds, &icons[active_item], nk_rgb(255,255,255));
160 }
161 nk_layout_space_end(ctx);
162 if (!nk_input_is_mouse_down(&ctx->input, NK_BUTTON_RIGHT)) {
163 nk_popup_close(ctx);
164 ret = active_item;
165 }
166 } else ret = -2;
167 ctx->style.window.spacing = nk_vec2(4,4);
168 ctx->style.window.padding = nk_vec2(8,8);
169 nk_popup_end(ctx);
170
171 ctx->style.window.fixed_background = background;
172 ctx->style.window.border_color = border;
173 return ret;
174 }
175
176 /* ===============================================================
177 *
178 * GRID
179 *
180 * ===============================================================*/
181 static void
grid_demo(struct nk_context * ctx,struct media * media)182 grid_demo(struct nk_context *ctx, struct media *media)
183 {
184 static char text[3][64];
185 static int text_len[3];
186 static const char *items[] = {"Item 0","item 1","item 2"};
187 static int selected_item = 0;
188 static int check = 1;
189
190 int i;
191 nk_style_set_font(ctx, &media->font_20->handle);
192 if (nk_begin(ctx, "Grid Demo", nk_rect(600, 350, 275, 250),
193 NK_WINDOW_TITLE|NK_WINDOW_BORDER|NK_WINDOW_MOVABLE|
194 NK_WINDOW_NO_SCROLLBAR))
195 {
196 nk_style_set_font(ctx, &media->font_18->handle);
197 nk_layout_row_dynamic(ctx, 30, 2);
198 nk_label(ctx, "Floating point:", NK_TEXT_RIGHT);
199 nk_edit_string(ctx, NK_EDIT_FIELD, text[0], &text_len[0], 64, nk_filter_float);
200 nk_label(ctx, "Hexadecimal:", NK_TEXT_RIGHT);
201 nk_edit_string(ctx, NK_EDIT_FIELD, text[1], &text_len[1], 64, nk_filter_hex);
202 nk_label(ctx, "Binary:", NK_TEXT_RIGHT);
203 nk_edit_string(ctx, NK_EDIT_FIELD, text[2], &text_len[2], 64, nk_filter_binary);
204 nk_label(ctx, "Checkbox:", NK_TEXT_RIGHT);
205 nk_checkbox_label(ctx, "Check me", &check);
206 nk_label(ctx, "Combobox:", NK_TEXT_RIGHT);
207 if (nk_combo_begin_label(ctx, items[selected_item], nk_vec2(nk_widget_width(ctx), 200))) {
208 nk_layout_row_dynamic(ctx, 25, 1);
209 for (i = 0; i < 3; ++i)
210 if (nk_combo_item_label(ctx, items[i], NK_TEXT_LEFT))
211 selected_item = i;
212 nk_combo_end(ctx);
213 }
214 }
215 nk_end(ctx);
216 nk_style_set_font(ctx, &media->font_14->handle);
217 }
218
219 /* ===============================================================
220 *
221 * BUTTON DEMO
222 *
223 * ===============================================================*/
224 static void
ui_header(struct nk_context * ctx,struct media * media,const char * title)225 ui_header(struct nk_context *ctx, struct media *media, const char *title)
226 {
227 nk_style_set_font(ctx, &media->font_18->handle);
228 nk_layout_row_dynamic(ctx, 20, 1);
229 nk_label(ctx, title, NK_TEXT_LEFT);
230 }
231
232 static void
ui_widget(struct nk_context * ctx,struct media * media,float height)233 ui_widget(struct nk_context *ctx, struct media *media, float height)
234 {
235 static const float ratio[] = {0.15f, 0.85f};
236 nk_style_set_font(ctx, &media->font_22->handle);
237 nk_layout_row(ctx, NK_DYNAMIC, height, 2, ratio);
238 nk_spacing(ctx, 1);
239 }
240
241 static void
ui_widget_centered(struct nk_context * ctx,struct media * media,float height)242 ui_widget_centered(struct nk_context *ctx, struct media *media, float height)
243 {
244 static const float ratio[] = {0.15f, 0.50f, 0.35f};
245 nk_style_set_font(ctx, &media->font_22->handle);
246 nk_layout_row(ctx, NK_DYNAMIC, height, 3, ratio);
247 nk_spacing(ctx, 1);
248 }
249
250 static void
button_demo(struct nk_context * ctx,struct media * media)251 button_demo(struct nk_context *ctx, struct media *media)
252 {
253 static int option = 1;
254 static int toggle0 = 1;
255 static int toggle1 = 0;
256 static int toggle2 = 1;
257
258 nk_style_set_font(ctx, &media->font_20->handle);
259 nk_begin(ctx, "Button Demo", nk_rect(50,50,255,610),
260 NK_WINDOW_BORDER|NK_WINDOW_MOVABLE|NK_WINDOW_TITLE);
261
262 /*------------------------------------------------
263 * MENU
264 *------------------------------------------------*/
265 nk_menubar_begin(ctx);
266 {
267 /* toolbar */
268 nk_layout_row_static(ctx, 40, 40, 4);
269 if (nk_menu_begin_image(ctx, "Music", media->play, nk_vec2(110,120)))
270 {
271 /* settings */
272 nk_layout_row_dynamic(ctx, 25, 1);
273 nk_menu_item_image_label(ctx, media->play, "Play", NK_TEXT_RIGHT);
274 nk_menu_item_image_label(ctx, media->stop, "Stop", NK_TEXT_RIGHT);
275 nk_menu_item_image_label(ctx, media->pause, "Pause", NK_TEXT_RIGHT);
276 nk_menu_item_image_label(ctx, media->next, "Next", NK_TEXT_RIGHT);
277 nk_menu_item_image_label(ctx, media->prev, "Prev", NK_TEXT_RIGHT);
278 nk_menu_end(ctx);
279 }
280 nk_button_image(ctx, media->tools);
281 nk_button_image(ctx, media->cloud);
282 nk_button_image(ctx, media->pen);
283 }
284 nk_menubar_end(ctx);
285
286 /*------------------------------------------------
287 * BUTTON
288 *------------------------------------------------*/
289 ui_header(ctx, media, "Push buttons");
290 ui_widget(ctx, media, 35);
291 if (nk_button_label(ctx, "Push me"))
292 fprintf(stdout, "pushed!\n");
293 ui_widget(ctx, media, 35);
294 if (nk_button_image_label(ctx, media->rocket, "Styled", NK_TEXT_CENTERED))
295 fprintf(stdout, "rocket!\n");
296
297 /*------------------------------------------------
298 * REPEATER
299 *------------------------------------------------*/
300 ui_header(ctx, media, "Repeater");
301 ui_widget(ctx, media, 35);
302 if (nk_button_label(ctx, "Press me"))
303 fprintf(stdout, "pressed!\n");
304
305 /*------------------------------------------------
306 * TOGGLE
307 *------------------------------------------------*/
308 ui_header(ctx, media, "Toggle buttons");
309 ui_widget(ctx, media, 35);
310 if (nk_button_image_label(ctx, (toggle0) ? media->checked: media->unchecked, "Toggle", NK_TEXT_LEFT))
311 toggle0 = !toggle0;
312
313 ui_widget(ctx, media, 35);
314 if (nk_button_image_label(ctx, (toggle1) ? media->checked: media->unchecked, "Toggle", NK_TEXT_LEFT))
315 toggle1 = !toggle1;
316
317 ui_widget(ctx, media, 35);
318 if (nk_button_image_label(ctx, (toggle2) ? media->checked: media->unchecked, "Toggle", NK_TEXT_LEFT))
319 toggle2 = !toggle2;
320
321 /*------------------------------------------------
322 * RADIO
323 *------------------------------------------------*/
324 ui_header(ctx, media, "Radio buttons");
325 ui_widget(ctx, media, 35);
326 if (nk_button_symbol_label(ctx, (option == 0)?NK_SYMBOL_CIRCLE_OUTLINE:NK_SYMBOL_CIRCLE_SOLID, "Select", NK_TEXT_LEFT))
327 option = 0;
328 ui_widget(ctx, media, 35);
329 if (nk_button_symbol_label(ctx, (option == 1)?NK_SYMBOL_CIRCLE_OUTLINE:NK_SYMBOL_CIRCLE_SOLID, "Select", NK_TEXT_LEFT))
330 option = 1;
331 ui_widget(ctx, media, 35);
332 if (nk_button_symbol_label(ctx, (option == 2)?NK_SYMBOL_CIRCLE_OUTLINE:NK_SYMBOL_CIRCLE_SOLID, "Select", NK_TEXT_LEFT))
333 option = 2;
334
335 /*------------------------------------------------
336 * CONTEXTUAL
337 *------------------------------------------------*/
338 nk_style_set_font(ctx, &media->font_18->handle);
339 if (nk_contextual_begin(ctx, NK_WINDOW_NO_SCROLLBAR, nk_vec2(150, 300), nk_window_get_bounds(ctx))) {
340 nk_layout_row_dynamic(ctx, 30, 1);
341 if (nk_contextual_item_image_label(ctx, media->copy, "Clone", NK_TEXT_RIGHT))
342 fprintf(stdout, "pressed clone!\n");
343 if (nk_contextual_item_image_label(ctx, media->del, "Delete", NK_TEXT_RIGHT))
344 fprintf(stdout, "pressed delete!\n");
345 if (nk_contextual_item_image_label(ctx, media->convert, "Convert", NK_TEXT_RIGHT))
346 fprintf(stdout, "pressed convert!\n");
347 if (nk_contextual_item_image_label(ctx, media->edit, "Edit", NK_TEXT_RIGHT))
348 fprintf(stdout, "pressed edit!\n");
349 nk_contextual_end(ctx);
350 }
351 nk_style_set_font(ctx, &media->font_14->handle);
352 nk_end(ctx);
353 }
354
355 /* ===============================================================
356 *
357 * BASIC DEMO
358 *
359 * ===============================================================*/
360 static void
basic_demo(struct nk_context * ctx,struct media * media)361 basic_demo(struct nk_context *ctx, struct media *media)
362 {
363 static int image_active;
364 static int check0 = 1;
365 static int check1 = 0;
366 static size_t prog = 80;
367 static int selected_item = 0;
368 static int selected_image = 3;
369 static int selected_icon = 0;
370 static const char *items[] = {"Item 0","item 1","item 2"};
371 static int piemenu_active = 0;
372 static struct nk_vec2 piemenu_pos;
373
374 int i = 0;
375 nk_style_set_font(ctx, &media->font_20->handle);
376 nk_begin(ctx, "Basic Demo", nk_rect(320, 50, 275, 610),
377 NK_WINDOW_BORDER|NK_WINDOW_MOVABLE|NK_WINDOW_TITLE);
378
379 /*------------------------------------------------
380 * POPUP BUTTON
381 *------------------------------------------------*/
382 ui_header(ctx, media, "Popup & Scrollbar & Images");
383 ui_widget(ctx, media, 35);
384 if (nk_button_image_label(ctx, media->dir, "Images", NK_TEXT_CENTERED))
385 image_active = !image_active;
386
387 /*------------------------------------------------
388 * SELECTED IMAGE
389 *------------------------------------------------*/
390 ui_header(ctx, media, "Selected Image");
391 ui_widget_centered(ctx, media, 100);
392 nk_image(ctx, media->images[selected_image]);
393
394 /*------------------------------------------------
395 * IMAGE POPUP
396 *------------------------------------------------*/
397 if (image_active) {
398 struct nk_panel popup;
399 if (nk_popup_begin(ctx, NK_POPUP_STATIC, "Image Popup", 0, nk_rect(265, 0, 320, 220))) {
400 nk_layout_row_static(ctx, 82, 82, 3);
401 for (i = 0; i < 9; ++i) {
402 if (nk_button_image(ctx, media->images[i])) {
403 selected_image = i;
404 image_active = 0;
405 nk_popup_close(ctx);
406 }
407 }
408 nk_popup_end(ctx);
409 }
410 }
411 /*------------------------------------------------
412 * COMBOBOX
413 *------------------------------------------------*/
414 ui_header(ctx, media, "Combo box");
415 ui_widget(ctx, media, 40);
416 if (nk_combo_begin_label(ctx, items[selected_item], nk_vec2(nk_widget_width(ctx), 200))) {
417 nk_layout_row_dynamic(ctx, 35, 1);
418 for (i = 0; i < 3; ++i)
419 if (nk_combo_item_label(ctx, items[i], NK_TEXT_LEFT))
420 selected_item = i;
421 nk_combo_end(ctx);
422 }
423
424 ui_widget(ctx, media, 40);
425 if (nk_combo_begin_image_label(ctx, items[selected_icon], media->images[selected_icon], nk_vec2(nk_widget_width(ctx), 200))) {
426 nk_layout_row_dynamic(ctx, 35, 1);
427 for (i = 0; i < 3; ++i)
428 if (nk_combo_item_image_label(ctx, media->images[i], items[i], NK_TEXT_RIGHT))
429 selected_icon = i;
430 nk_combo_end(ctx);
431 }
432
433 /*------------------------------------------------
434 * CHECKBOX
435 *------------------------------------------------*/
436 ui_header(ctx, media, "Checkbox");
437 ui_widget(ctx, media, 30);
438 nk_checkbox_label(ctx, "Flag 1", &check0);
439 ui_widget(ctx, media, 30);
440 nk_checkbox_label(ctx, "Flag 2", &check1);
441
442 /*------------------------------------------------
443 * PROGRESSBAR
444 *------------------------------------------------*/
445 ui_header(ctx, media, "Progressbar");
446 ui_widget(ctx, media, 35);
447 nk_progress(ctx, &prog, 100, nk_true);
448
449 /*------------------------------------------------
450 * PIEMENU
451 *------------------------------------------------*/
452 if (nk_input_is_mouse_click_down_in_rect(&ctx->input, NK_BUTTON_RIGHT,
453 nk_window_get_bounds(ctx),nk_true)){
454 piemenu_pos = ctx->input.mouse.pos;
455 piemenu_active = 1;
456 }
457
458 if (piemenu_active) {
459 int ret = ui_piemenu(ctx, piemenu_pos, 140, &media->menu[0], 6);
460 if (ret == -2) piemenu_active = 0;
461 if (ret != -1) {
462 fprintf(stdout, "piemenu selected: %d\n", ret);
463 piemenu_active = 0;
464 }
465 }
466 nk_style_set_font(ctx, &media->font_14->handle);
467 nk_end(ctx);
468 }
469
470 /* ===============================================================
471 *
472 * DEVICE
473 *
474 * ===============================================================*/
475 struct nk_glfw_vertex {
476 float position[2];
477 float uv[2];
478 nk_byte col[4];
479 };
480
481 struct device {
482 struct nk_buffer cmds;
483 struct nk_draw_null_texture null;
484 GLuint vbo, vao, ebo;
485 GLuint prog;
486 GLuint vert_shdr;
487 GLuint frag_shdr;
488 GLint attrib_pos;
489 GLint attrib_uv;
490 GLint attrib_col;
491 GLint uniform_tex;
492 GLint uniform_proj;
493 GLuint font_tex;
494 };
495
496 static void
die(const char * fmt,...)497 die(const char *fmt, ...)
498 {
499 va_list ap;
500 va_start(ap, fmt);
501 vfprintf(stderr, fmt, ap);
502 va_end(ap);
503 fputs("\n", stderr);
504 exit(EXIT_FAILURE);
505 }
506
507 static struct nk_image
icon_load(const char * filename)508 icon_load(const char *filename)
509 {
510 int x,y,n;
511 GLuint tex;
512 unsigned char *data = stbi_load(filename, &x, &y, &n, 0);
513 if (!data) die("[SDL]: failed to load image: %s", filename);
514
515 glGenTextures(1, &tex);
516 glBindTexture(GL_TEXTURE_2D, tex);
517 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
518 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_NEAREST);
519 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
520 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
521 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, x, y, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
522 glGenerateMipmap(GL_TEXTURE_2D);
523 stbi_image_free(data);
524 return nk_image_id((int)tex);
525 }
526
527 static void
device_init(struct device * dev)528 device_init(struct device *dev)
529 {
530 GLint status;
531 static const GLchar *vertex_shader =
532 NK_SHADER_VERSION
533 "uniform mat4 ProjMtx;\n"
534 "in vec2 Position;\n"
535 "in vec2 TexCoord;\n"
536 "in vec4 Color;\n"
537 "out vec2 Frag_UV;\n"
538 "out vec4 Frag_Color;\n"
539 "void main() {\n"
540 " Frag_UV = TexCoord;\n"
541 " Frag_Color = Color;\n"
542 " gl_Position = ProjMtx * vec4(Position.xy, 0, 1);\n"
543 "}\n";
544 static const GLchar *fragment_shader =
545 NK_SHADER_VERSION
546 "precision mediump float;\n"
547 "uniform sampler2D Texture;\n"
548 "in vec2 Frag_UV;\n"
549 "in vec4 Frag_Color;\n"
550 "out vec4 Out_Color;\n"
551 "void main(){\n"
552 " Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
553 "}\n";
554
555 nk_buffer_init_default(&dev->cmds);
556 dev->prog = glCreateProgram();
557 dev->vert_shdr = glCreateShader(GL_VERTEX_SHADER);
558 dev->frag_shdr = glCreateShader(GL_FRAGMENT_SHADER);
559 glShaderSource(dev->vert_shdr, 1, &vertex_shader, 0);
560 glShaderSource(dev->frag_shdr, 1, &fragment_shader, 0);
561 glCompileShader(dev->vert_shdr);
562 glCompileShader(dev->frag_shdr);
563 glGetShaderiv(dev->vert_shdr, GL_COMPILE_STATUS, &status);
564 assert(status == GL_TRUE);
565 glGetShaderiv(dev->frag_shdr, GL_COMPILE_STATUS, &status);
566 assert(status == GL_TRUE);
567 glAttachShader(dev->prog, dev->vert_shdr);
568 glAttachShader(dev->prog, dev->frag_shdr);
569 glLinkProgram(dev->prog);
570 glGetProgramiv(dev->prog, GL_LINK_STATUS, &status);
571 assert(status == GL_TRUE);
572
573 dev->uniform_tex = glGetUniformLocation(dev->prog, "Texture");
574 dev->uniform_proj = glGetUniformLocation(dev->prog, "ProjMtx");
575 dev->attrib_pos = glGetAttribLocation(dev->prog, "Position");
576 dev->attrib_uv = glGetAttribLocation(dev->prog, "TexCoord");
577 dev->attrib_col = glGetAttribLocation(dev->prog, "Color");
578
579 {
580 /* buffer setup */
581 GLsizei vs = sizeof(struct nk_glfw_vertex);
582 size_t vp = offsetof(struct nk_glfw_vertex, position);
583 size_t vt = offsetof(struct nk_glfw_vertex, uv);
584 size_t vc = offsetof(struct nk_glfw_vertex, col);
585
586 glGenBuffers(1, &dev->vbo);
587 glGenBuffers(1, &dev->ebo);
588 glGenVertexArrays(1, &dev->vao);
589
590 glBindVertexArray(dev->vao);
591 glBindBuffer(GL_ARRAY_BUFFER, dev->vbo);
592 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, dev->ebo);
593
594 glEnableVertexAttribArray((GLuint)dev->attrib_pos);
595 glEnableVertexAttribArray((GLuint)dev->attrib_uv);
596 glEnableVertexAttribArray((GLuint)dev->attrib_col);
597
598 glVertexAttribPointer((GLuint)dev->attrib_pos, 2, GL_FLOAT, GL_FALSE, vs, (void*)vp);
599 glVertexAttribPointer((GLuint)dev->attrib_uv, 2, GL_FLOAT, GL_FALSE, vs, (void*)vt);
600 glVertexAttribPointer((GLuint)dev->attrib_col, 4, GL_UNSIGNED_BYTE, GL_TRUE, vs, (void*)vc);
601 }
602
603 glBindTexture(GL_TEXTURE_2D, 0);
604 glBindBuffer(GL_ARRAY_BUFFER, 0);
605 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
606 glBindVertexArray(0);
607 }
608
609 static void
device_upload_atlas(struct device * dev,const void * image,int width,int height)610 device_upload_atlas(struct device *dev, const void *image, int width, int height)
611 {
612 glGenTextures(1, &dev->font_tex);
613 glBindTexture(GL_TEXTURE_2D, dev->font_tex);
614 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
615 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
616 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)width, (GLsizei)height, 0,
617 GL_RGBA, GL_UNSIGNED_BYTE, image);
618 }
619
620 static void
device_shutdown(struct device * dev)621 device_shutdown(struct device *dev)
622 {
623 glDetachShader(dev->prog, dev->vert_shdr);
624 glDetachShader(dev->prog, dev->frag_shdr);
625 glDeleteShader(dev->vert_shdr);
626 glDeleteShader(dev->frag_shdr);
627 glDeleteProgram(dev->prog);
628 glDeleteTextures(1, &dev->font_tex);
629 glDeleteBuffers(1, &dev->vbo);
630 glDeleteBuffers(1, &dev->ebo);
631 nk_buffer_free(&dev->cmds);
632 }
633
634 static void
device_draw(struct device * dev,struct nk_context * ctx,int width,int height,struct nk_vec2 scale,enum nk_anti_aliasing AA)635 device_draw(struct device *dev, struct nk_context *ctx, int width, int height,
636 struct nk_vec2 scale, enum nk_anti_aliasing AA)
637 {
638 GLfloat ortho[4][4] = {
639 {2.0f, 0.0f, 0.0f, 0.0f},
640 {0.0f,-2.0f, 0.0f, 0.0f},
641 {0.0f, 0.0f,-1.0f, 0.0f},
642 {-1.0f,1.0f, 0.0f, 1.0f},
643 };
644 ortho[0][0] /= (GLfloat)width;
645 ortho[1][1] /= (GLfloat)height;
646
647 /* setup global state */
648 glEnable(GL_BLEND);
649 glBlendEquation(GL_FUNC_ADD);
650 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
651 glDisable(GL_CULL_FACE);
652 glDisable(GL_DEPTH_TEST);
653 glEnable(GL_SCISSOR_TEST);
654 glActiveTexture(GL_TEXTURE0);
655
656 /* setup program */
657 glUseProgram(dev->prog);
658 glUniform1i(dev->uniform_tex, 0);
659 glUniformMatrix4fv(dev->uniform_proj, 1, GL_FALSE, &ortho[0][0]);
660 {
661 /* convert from command queue into draw list and draw to screen */
662 const struct nk_draw_command *cmd;
663 void *vertices, *elements;
664 const nk_draw_index *offset = NULL;
665
666 /* allocate vertex and element buffer */
667 glBindVertexArray(dev->vao);
668 glBindBuffer(GL_ARRAY_BUFFER, dev->vbo);
669 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, dev->ebo);
670
671 glBufferData(GL_ARRAY_BUFFER, MAX_VERTEX_MEMORY, NULL, GL_STREAM_DRAW);
672 glBufferData(GL_ELEMENT_ARRAY_BUFFER, MAX_ELEMENT_MEMORY, NULL, GL_STREAM_DRAW);
673
674 /* load draw vertices & elements directly into vertex + element buffer */
675 vertices = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
676 elements = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY);
677 {
678 /* fill convert configuration */
679 struct nk_convert_config config;
680 static const struct nk_draw_vertex_layout_element vertex_layout[] = {
681 {NK_VERTEX_POSITION, NK_FORMAT_FLOAT, NK_OFFSETOF(struct nk_glfw_vertex, position)},
682 {NK_VERTEX_TEXCOORD, NK_FORMAT_FLOAT, NK_OFFSETOF(struct nk_glfw_vertex, uv)},
683 {NK_VERTEX_COLOR, NK_FORMAT_R8G8B8A8, NK_OFFSETOF(struct nk_glfw_vertex, col)},
684 {NK_VERTEX_LAYOUT_END}
685 };
686 NK_MEMSET(&config, 0, sizeof(config));
687 config.vertex_layout = vertex_layout;
688 config.vertex_size = sizeof(struct nk_glfw_vertex);
689 config.vertex_alignment = NK_ALIGNOF(struct nk_glfw_vertex);
690 config.null = dev->null;
691 config.circle_segment_count = 22;
692 config.curve_segment_count = 22;
693 config.arc_segment_count = 22;
694 config.global_alpha = 1.0f;
695 config.shape_AA = AA;
696 config.line_AA = AA;
697
698 /* setup buffers to load vertices and elements */
699 {struct nk_buffer vbuf, ebuf;
700 nk_buffer_init_fixed(&vbuf, vertices, MAX_VERTEX_MEMORY);
701 nk_buffer_init_fixed(&ebuf, elements, MAX_ELEMENT_MEMORY);
702 nk_convert(ctx, &dev->cmds, &vbuf, &ebuf, &config);}
703 }
704 glUnmapBuffer(GL_ARRAY_BUFFER);
705 glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER);
706
707 /* iterate over and execute each draw command */
708 nk_draw_foreach(cmd, ctx, &dev->cmds)
709 {
710 if (!cmd->elem_count) continue;
711 glBindTexture(GL_TEXTURE_2D, (GLuint)cmd->texture.id);
712 glScissor(
713 (GLint)(cmd->clip_rect.x * scale.x),
714 (GLint)((height - (GLint)(cmd->clip_rect.y + cmd->clip_rect.h)) * scale.y),
715 (GLint)(cmd->clip_rect.w * scale.x),
716 (GLint)(cmd->clip_rect.h * scale.y));
717 glDrawElements(GL_TRIANGLES, (GLsizei)cmd->elem_count, GL_UNSIGNED_SHORT, offset);
718 offset += cmd->elem_count;
719 }
720 nk_clear(ctx);
721 }
722
723 /* default OpenGL state */
724 glUseProgram(0);
725 glBindBuffer(GL_ARRAY_BUFFER, 0);
726 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
727 glBindVertexArray(0);
728 glDisable(GL_BLEND);
729 glDisable(GL_SCISSOR_TEST);
730 }
731
732 /* glfw callbacks (I don't know if there is a easier way to access text and scroll )*/
error_callback(int e,const char * d)733 static void error_callback(int e, const char *d){printf("Error %d: %s\n", e, d);}
text_input(GLFWwindow * win,unsigned int codepoint)734 static void text_input(GLFWwindow *win, unsigned int codepoint)
735 {nk_input_unicode((struct nk_context*)glfwGetWindowUserPointer(win), codepoint);}
scroll_input(GLFWwindow * win,double _,double yoff)736 static void scroll_input(GLFWwindow *win, double _, double yoff)
737 {UNUSED(_);nk_input_scroll((struct nk_context*)glfwGetWindowUserPointer(win), nk_vec2(0, (float)yoff));}
738
main(int argc,char * argv[])739 int main(int argc, char *argv[])
740 {
741 /* Platform */
742 static GLFWwindow *win;
743 int width = 0, height = 0;
744 int display_width=0, display_height=0;
745
746 /* GUI */
747 struct device device;
748 struct nk_font_atlas atlas;
749 struct media media;
750 struct nk_context ctx;
751
752 /* GLFW */
753 glfwSetErrorCallback(error_callback);
754 if (!glfwInit()) {
755 fprintf(stdout, "[GFLW] failed to init!\n");
756 exit(1);
757 }
758 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
759 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
760 glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
761 #ifdef __APPLE__
762 glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
763 #endif
764 win = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, "Demo", NULL, NULL);
765 glfwMakeContextCurrent(win);
766 glfwSetWindowUserPointer(win, &ctx);
767 glfwSetCharCallback(win, text_input);
768 glfwSetScrollCallback(win, scroll_input);
769 glfwGetWindowSize(win, &width, &height);
770 glfwGetFramebufferSize(win, &display_width, &display_height);
771
772 /* OpenGL */
773 glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
774 glewExperimental = 1;
775 if (glewInit() != GLEW_OK) {
776 fprintf(stderr, "Failed to setup GLEW\n");
777 exit(1);
778 }
779
780 {/* GUI */
781 device_init(&device);
782 {const void *image; int w, h;
783 struct nk_font_config cfg = nk_font_config(0);
784 cfg.oversample_h = 3; cfg.oversample_v = 2;
785 /* Loading one font with different heights is only required if you want higher
786 * quality text otherwise you can just set the font height directly
787 * e.g.: ctx->style.font.height = 20. */
788 nk_font_atlas_init_default(&atlas);
789 nk_font_atlas_begin(&atlas);
790 media.font_14 = nk_font_atlas_add_from_file(&atlas, "../../extra_font/Roboto-Regular.ttf", 14.0f, &cfg);
791 media.font_18 = nk_font_atlas_add_from_file(&atlas, "../../extra_font/Roboto-Regular.ttf", 18.0f, &cfg);
792 media.font_20 = nk_font_atlas_add_from_file(&atlas, "../../extra_font/Roboto-Regular.ttf", 20.0f, &cfg);
793 media.font_22 = nk_font_atlas_add_from_file(&atlas, "../../extra_font/Roboto-Regular.ttf", 22.0f, &cfg);
794 image = nk_font_atlas_bake(&atlas, &w, &h, NK_FONT_ATLAS_RGBA32);
795 device_upload_atlas(&device, image, w, h);
796 nk_font_atlas_end(&atlas, nk_handle_id((int)device.font_tex), &device.null);}
797 nk_init_default(&ctx, &media.font_14->handle);}
798
799 /* icons */
800 glEnable(GL_TEXTURE_2D);
801 media.unchecked = icon_load("../icon/unchecked.png");
802 media.checked = icon_load("../icon/checked.png");
803 media.rocket = icon_load("../icon/rocket.png");
804 media.cloud = icon_load("../icon/cloud.png");
805 media.pen = icon_load("../icon/pen.png");
806 media.play = icon_load("../icon/play.png");
807 media.pause = icon_load("../icon/pause.png");
808 media.stop = icon_load("../icon/stop.png");
809 media.next = icon_load("../icon/next.png");
810 media.prev = icon_load("../icon/prev.png");
811 media.tools = icon_load("../icon/tools.png");
812 media.dir = icon_load("../icon/directory.png");
813 media.copy = icon_load("../icon/copy.png");
814 media.convert = icon_load("../icon/export.png");
815 media.del = icon_load("../icon/delete.png");
816 media.edit = icon_load("../icon/edit.png");
817 media.menu[0] = icon_load("../icon/home.png");
818 media.menu[1] = icon_load("../icon/phone.png");
819 media.menu[2] = icon_load("../icon/plane.png");
820 media.menu[3] = icon_load("../icon/wifi.png");
821 media.menu[4] = icon_load("../icon/settings.png");
822 media.menu[5] = icon_load("../icon/volume.png");
823
824 {int i;
825 for (i = 0; i < 9; ++i) {
826 char buffer[256];
827 sprintf(buffer, "../images/image%d.png", (i+1));
828 media.images[i] = icon_load(buffer);
829 }}
830
831 while (!glfwWindowShouldClose(win))
832 {
833 /* High DPI displays */
834 struct nk_vec2 scale;
835 glfwGetWindowSize(win, &width, &height);
836 glfwGetFramebufferSize(win, &display_width, &display_height);
837 scale.x = (float)display_width/(float)width;
838 scale.y = (float)display_height/(float)height;
839
840 /* Input */
841 {double x, y;
842 nk_input_begin(&ctx);
843 glfwPollEvents();
844 nk_input_key(&ctx, NK_KEY_DEL, glfwGetKey(win, GLFW_KEY_DELETE) == GLFW_PRESS);
845 nk_input_key(&ctx, NK_KEY_ENTER, glfwGetKey(win, GLFW_KEY_ENTER) == GLFW_PRESS);
846 nk_input_key(&ctx, NK_KEY_TAB, glfwGetKey(win, GLFW_KEY_TAB) == GLFW_PRESS);
847 nk_input_key(&ctx, NK_KEY_BACKSPACE, glfwGetKey(win, GLFW_KEY_BACKSPACE) == GLFW_PRESS);
848 nk_input_key(&ctx, NK_KEY_LEFT, glfwGetKey(win, GLFW_KEY_LEFT) == GLFW_PRESS);
849 nk_input_key(&ctx, NK_KEY_RIGHT, glfwGetKey(win, GLFW_KEY_RIGHT) == GLFW_PRESS);
850 nk_input_key(&ctx, NK_KEY_UP, glfwGetKey(win, GLFW_KEY_UP) == GLFW_PRESS);
851 nk_input_key(&ctx, NK_KEY_DOWN, glfwGetKey(win, GLFW_KEY_DOWN) == GLFW_PRESS);
852 if (glfwGetKey(win, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS ||
853 glfwGetKey(win, GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS) {
854 nk_input_key(&ctx, NK_KEY_COPY, glfwGetKey(win, GLFW_KEY_C) == GLFW_PRESS);
855 nk_input_key(&ctx, NK_KEY_PASTE, glfwGetKey(win, GLFW_KEY_P) == GLFW_PRESS);
856 nk_input_key(&ctx, NK_KEY_CUT, glfwGetKey(win, GLFW_KEY_X) == GLFW_PRESS);
857 nk_input_key(&ctx, NK_KEY_CUT, glfwGetKey(win, GLFW_KEY_E) == GLFW_PRESS);
858 nk_input_key(&ctx, NK_KEY_SHIFT, 1);
859 } else {
860 nk_input_key(&ctx, NK_KEY_COPY, 0);
861 nk_input_key(&ctx, NK_KEY_PASTE, 0);
862 nk_input_key(&ctx, NK_KEY_CUT, 0);
863 nk_input_key(&ctx, NK_KEY_SHIFT, 0);
864 }
865 glfwGetCursorPos(win, &x, &y);
866 nk_input_motion(&ctx, (int)x, (int)y);
867 nk_input_button(&ctx, NK_BUTTON_LEFT, (int)x, (int)y, glfwGetMouseButton(win, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS);
868 nk_input_button(&ctx, NK_BUTTON_MIDDLE, (int)x, (int)y, glfwGetMouseButton(win, GLFW_MOUSE_BUTTON_MIDDLE) == GLFW_PRESS);
869 nk_input_button(&ctx, NK_BUTTON_RIGHT, (int)x, (int)y, glfwGetMouseButton(win, GLFW_MOUSE_BUTTON_RIGHT) == GLFW_PRESS);
870 nk_input_end(&ctx);}
871
872 /* GUI */
873 basic_demo(&ctx, &media);
874 button_demo(&ctx, &media);
875 grid_demo(&ctx, &media);
876
877 /* Draw */
878 glViewport(0, 0, display_width, display_height);
879 glClear(GL_COLOR_BUFFER_BIT);
880 glClearColor(0.3f, 0.3f, 0.3f, 1.0f);
881 device_draw(&device, &ctx, width, height, scale, NK_ANTI_ALIASING_ON);
882 glfwSwapBuffers(win);
883 }
884
885 glDeleteTextures(1,(const GLuint*)&media.unchecked.handle.id);
886 glDeleteTextures(1,(const GLuint*)&media.checked.handle.id);
887 glDeleteTextures(1,(const GLuint*)&media.rocket.handle.id);
888 glDeleteTextures(1,(const GLuint*)&media.cloud.handle.id);
889 glDeleteTextures(1,(const GLuint*)&media.pen.handle.id);
890 glDeleteTextures(1,(const GLuint*)&media.play.handle.id);
891 glDeleteTextures(1,(const GLuint*)&media.pause.handle.id);
892 glDeleteTextures(1,(const GLuint*)&media.stop.handle.id);
893 glDeleteTextures(1,(const GLuint*)&media.next.handle.id);
894 glDeleteTextures(1,(const GLuint*)&media.prev.handle.id);
895 glDeleteTextures(1,(const GLuint*)&media.tools.handle.id);
896 glDeleteTextures(1,(const GLuint*)&media.dir.handle.id);
897 glDeleteTextures(1,(const GLuint*)&media.del.handle.id);
898
899 nk_font_atlas_clear(&atlas);
900 nk_free(&ctx);
901
902 device_shutdown(&device);
903 glfwTerminate();
904 return 0;
905 }
906
907