1 /*
2 * Nuklear - 1.32.0 - public domain
3 * no warrenty implied; use at your own risk.
4 * authored from 2015-2016 by Micha Mettke
5 */
6 /*
7 * ==============================================================
8 *
9 * API
10 *
11 * ===============================================================
12 */
13 #ifndef NK_SDL_GL3_H_
14 #define NK_SDL_GL3_H_
15
16 #include <SDL2/SDL.h>
17 #include <SDL2/SDL_opengl.h>
18
19 NK_API struct nk_context* nk_sdl_init(SDL_Window *win);
20 NK_API void nk_sdl_font_stash_begin(struct nk_font_atlas **atlas);
21 NK_API void nk_sdl_font_stash_end(void);
22 NK_API int nk_sdl_handle_event(SDL_Event *evt);
23 NK_API void nk_sdl_render(enum nk_anti_aliasing , int max_vertex_buffer, int max_element_buffer);
24 NK_API void nk_sdl_shutdown(void);
25 NK_API void nk_sdl_device_destroy(void);
26 NK_API void nk_sdl_device_create(void);
27
28 #endif
29
30 /*
31 * ==============================================================
32 *
33 * IMPLEMENTATION
34 *
35 * ===============================================================
36 */
37 #ifdef NK_SDL_GL3_IMPLEMENTATION
38
39 #include <string.h>
40
41 struct nk_sdl_device {
42 struct nk_buffer cmds;
43 struct nk_draw_null_texture null;
44 GLuint vbo, vao, ebo;
45 GLuint prog;
46 GLuint vert_shdr;
47 GLuint frag_shdr;
48 GLint attrib_pos;
49 GLint attrib_uv;
50 GLint attrib_col;
51 GLint uniform_tex;
52 GLint uniform_proj;
53 GLuint font_tex;
54 };
55
56 struct nk_sdl_vertex {
57 float position[2];
58 float uv[2];
59 nk_byte col[4];
60 };
61
62 static struct nk_sdl {
63 SDL_Window *win;
64 struct nk_sdl_device ogl;
65 struct nk_context ctx;
66 struct nk_font_atlas atlas;
67 } sdl;
68
69 #ifdef __APPLE__
70 #define NK_SHADER_VERSION "#version 150\n"
71 #else
72 #define NK_SHADER_VERSION "#version 300 es\n"
73 #endif
74 NK_API void
nk_sdl_device_create(void)75 nk_sdl_device_create(void)
76 {
77 GLint status;
78 static const GLchar *vertex_shader =
79 NK_SHADER_VERSION
80 "uniform mat4 ProjMtx;\n"
81 "in vec2 Position;\n"
82 "in vec2 TexCoord;\n"
83 "in vec4 Color;\n"
84 "out vec2 Frag_UV;\n"
85 "out vec4 Frag_Color;\n"
86 "void main() {\n"
87 " Frag_UV = TexCoord;\n"
88 " Frag_Color = Color;\n"
89 " gl_Position = ProjMtx * vec4(Position.xy, 0, 1);\n"
90 "}\n";
91 static const GLchar *fragment_shader =
92 NK_SHADER_VERSION
93 "precision mediump float;\n"
94 "uniform sampler2D Texture;\n"
95 "in vec2 Frag_UV;\n"
96 "in vec4 Frag_Color;\n"
97 "out vec4 Out_Color;\n"
98 "void main(){\n"
99 " Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
100 "}\n";
101
102 struct nk_sdl_device *dev = &sdl.ogl;
103 nk_buffer_init_default(&dev->cmds);
104 dev->prog = glCreateProgram();
105 dev->vert_shdr = glCreateShader(GL_VERTEX_SHADER);
106 dev->frag_shdr = glCreateShader(GL_FRAGMENT_SHADER);
107 glShaderSource(dev->vert_shdr, 1, &vertex_shader, 0);
108 glShaderSource(dev->frag_shdr, 1, &fragment_shader, 0);
109 glCompileShader(dev->vert_shdr);
110 glCompileShader(dev->frag_shdr);
111 glGetShaderiv(dev->vert_shdr, GL_COMPILE_STATUS, &status);
112 assert(status == GL_TRUE);
113 glGetShaderiv(dev->frag_shdr, GL_COMPILE_STATUS, &status);
114 assert(status == GL_TRUE);
115 glAttachShader(dev->prog, dev->vert_shdr);
116 glAttachShader(dev->prog, dev->frag_shdr);
117 glLinkProgram(dev->prog);
118 glGetProgramiv(dev->prog, GL_LINK_STATUS, &status);
119 assert(status == GL_TRUE);
120
121 dev->uniform_tex = glGetUniformLocation(dev->prog, "Texture");
122 dev->uniform_proj = glGetUniformLocation(dev->prog, "ProjMtx");
123 dev->attrib_pos = glGetAttribLocation(dev->prog, "Position");
124 dev->attrib_uv = glGetAttribLocation(dev->prog, "TexCoord");
125 dev->attrib_col = glGetAttribLocation(dev->prog, "Color");
126
127 {
128 /* buffer setup */
129 GLsizei vs = sizeof(struct nk_sdl_vertex);
130 size_t vp = offsetof(struct nk_sdl_vertex, position);
131 size_t vt = offsetof(struct nk_sdl_vertex, uv);
132 size_t vc = offsetof(struct nk_sdl_vertex, col);
133
134 glGenBuffers(1, &dev->vbo);
135 glGenBuffers(1, &dev->ebo);
136 glGenVertexArrays(1, &dev->vao);
137
138 glBindVertexArray(dev->vao);
139 glBindBuffer(GL_ARRAY_BUFFER, dev->vbo);
140 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, dev->ebo);
141
142 glEnableVertexAttribArray((GLuint)dev->attrib_pos);
143 glEnableVertexAttribArray((GLuint)dev->attrib_uv);
144 glEnableVertexAttribArray((GLuint)dev->attrib_col);
145
146 glVertexAttribPointer((GLuint)dev->attrib_pos, 2, GL_FLOAT, GL_FALSE, vs, (void*)vp);
147 glVertexAttribPointer((GLuint)dev->attrib_uv, 2, GL_FLOAT, GL_FALSE, vs, (void*)vt);
148 glVertexAttribPointer((GLuint)dev->attrib_col, 4, GL_UNSIGNED_BYTE, GL_TRUE, vs, (void*)vc);
149 }
150
151 glBindTexture(GL_TEXTURE_2D, 0);
152 glBindBuffer(GL_ARRAY_BUFFER, 0);
153 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
154 glBindVertexArray(0);
155 }
156
157 NK_INTERN void
nk_sdl_device_upload_atlas(const void * image,int width,int height)158 nk_sdl_device_upload_atlas(const void *image, int width, int height)
159 {
160 struct nk_sdl_device *dev = &sdl.ogl;
161 glGenTextures(1, &dev->font_tex);
162 glBindTexture(GL_TEXTURE_2D, dev->font_tex);
163 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
164 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
165 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)width, (GLsizei)height, 0,
166 GL_RGBA, GL_UNSIGNED_BYTE, image);
167 }
168
169 NK_API void
nk_sdl_device_destroy(void)170 nk_sdl_device_destroy(void)
171 {
172 struct nk_sdl_device *dev = &sdl.ogl;
173 glDetachShader(dev->prog, dev->vert_shdr);
174 glDetachShader(dev->prog, dev->frag_shdr);
175 glDeleteShader(dev->vert_shdr);
176 glDeleteShader(dev->frag_shdr);
177 glDeleteProgram(dev->prog);
178 glDeleteTextures(1, &dev->font_tex);
179 glDeleteBuffers(1, &dev->vbo);
180 glDeleteBuffers(1, &dev->ebo);
181 nk_buffer_free(&dev->cmds);
182 }
183
184 NK_API void
nk_sdl_render(enum nk_anti_aliasing AA,int max_vertex_buffer,int max_element_buffer)185 nk_sdl_render(enum nk_anti_aliasing AA, int max_vertex_buffer, int max_element_buffer)
186 {
187 struct nk_sdl_device *dev = &sdl.ogl;
188 int width, height;
189 int display_width, display_height;
190 struct nk_vec2 scale;
191 GLfloat ortho[4][4] = {
192 {2.0f, 0.0f, 0.0f, 0.0f},
193 {0.0f,-2.0f, 0.0f, 0.0f},
194 {0.0f, 0.0f,-1.0f, 0.0f},
195 {-1.0f,1.0f, 0.0f, 1.0f},
196 };
197 SDL_GetWindowSize(sdl.win, &width, &height);
198 SDL_GL_GetDrawableSize(sdl.win, &display_width, &display_height);
199 ortho[0][0] /= (GLfloat)width;
200 ortho[1][1] /= (GLfloat)height;
201
202 scale.x = (float)display_width/(float)width;
203 scale.y = (float)display_height/(float)height;
204
205 /* setup global state */
206 glViewport(0,0,display_width,display_height);
207 glEnable(GL_BLEND);
208 glBlendEquation(GL_FUNC_ADD);
209 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
210 glDisable(GL_CULL_FACE);
211 glDisable(GL_DEPTH_TEST);
212 glEnable(GL_SCISSOR_TEST);
213 glActiveTexture(GL_TEXTURE0);
214
215 /* setup program */
216 glUseProgram(dev->prog);
217 glUniform1i(dev->uniform_tex, 0);
218 glUniformMatrix4fv(dev->uniform_proj, 1, GL_FALSE, &ortho[0][0]);
219 {
220 /* convert from command queue into draw list and draw to screen */
221 const struct nk_draw_command *cmd;
222 void *vertices, *elements;
223 const nk_draw_index *offset = NULL;
224 struct nk_buffer vbuf, ebuf;
225
226 /* allocate vertex and element buffer */
227 glBindVertexArray(dev->vao);
228 glBindBuffer(GL_ARRAY_BUFFER, dev->vbo);
229 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, dev->ebo);
230
231 glBufferData(GL_ARRAY_BUFFER, max_vertex_buffer, NULL, GL_STREAM_DRAW);
232 glBufferData(GL_ELEMENT_ARRAY_BUFFER, max_element_buffer, NULL, GL_STREAM_DRAW);
233
234 /* load vertices/elements directly into vertex/element buffer */
235 vertices = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
236 elements = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY);
237 {
238 /* fill convert configuration */
239 struct nk_convert_config config;
240 static const struct nk_draw_vertex_layout_element vertex_layout[] = {
241 {NK_VERTEX_POSITION, NK_FORMAT_FLOAT, NK_OFFSETOF(struct nk_sdl_vertex, position)},
242 {NK_VERTEX_TEXCOORD, NK_FORMAT_FLOAT, NK_OFFSETOF(struct nk_sdl_vertex, uv)},
243 {NK_VERTEX_COLOR, NK_FORMAT_R8G8B8A8, NK_OFFSETOF(struct nk_sdl_vertex, col)},
244 {NK_VERTEX_LAYOUT_END}
245 };
246 NK_MEMSET(&config, 0, sizeof(config));
247 config.vertex_layout = vertex_layout;
248 config.vertex_size = sizeof(struct nk_sdl_vertex);
249 config.vertex_alignment = NK_ALIGNOF(struct nk_sdl_vertex);
250 config.null = dev->null;
251 config.circle_segment_count = 22;
252 config.curve_segment_count = 22;
253 config.arc_segment_count = 22;
254 config.global_alpha = 1.0f;
255 config.shape_AA = AA;
256 config.line_AA = AA;
257
258 /* setup buffers to load vertices and elements */
259 nk_buffer_init_fixed(&vbuf, vertices, (nk_size)max_vertex_buffer);
260 nk_buffer_init_fixed(&ebuf, elements, (nk_size)max_element_buffer);
261 nk_convert(&sdl.ctx, &dev->cmds, &vbuf, &ebuf, &config);
262 }
263 glUnmapBuffer(GL_ARRAY_BUFFER);
264 glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER);
265
266 /* iterate over and execute each draw command */
267 nk_draw_foreach(cmd, &sdl.ctx, &dev->cmds) {
268 if (!cmd->elem_count) continue;
269 glBindTexture(GL_TEXTURE_2D, (GLuint)cmd->texture.id);
270 glScissor((GLint)(cmd->clip_rect.x * scale.x),
271 (GLint)((height - (GLint)(cmd->clip_rect.y + cmd->clip_rect.h)) * scale.y),
272 (GLint)(cmd->clip_rect.w * scale.x),
273 (GLint)(cmd->clip_rect.h * scale.y));
274 glDrawElements(GL_TRIANGLES, (GLsizei)cmd->elem_count, GL_UNSIGNED_SHORT, offset);
275 offset += cmd->elem_count;
276 }
277 nk_clear(&sdl.ctx);
278 }
279
280 glUseProgram(0);
281 glBindBuffer(GL_ARRAY_BUFFER, 0);
282 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
283 glBindVertexArray(0);
284 glDisable(GL_BLEND);
285 glDisable(GL_SCISSOR_TEST);
286 }
287
288 static void
nk_sdl_clipbard_paste(nk_handle usr,struct nk_text_edit * edit)289 nk_sdl_clipbard_paste(nk_handle usr, struct nk_text_edit *edit)
290 {
291 const char *text = SDL_GetClipboardText();
292 if (text) nk_textedit_paste(edit, text, nk_strlen(text));
293 (void)usr;
294 }
295
296 static void
nk_sdl_clipbard_copy(nk_handle usr,const char * text,int len)297 nk_sdl_clipbard_copy(nk_handle usr, const char *text, int len)
298 {
299 char *str = 0;
300 (void)usr;
301 if (!len) return;
302 str = (char*)malloc((size_t)len+1);
303 if (!str) return;
304 memcpy(str, text, (size_t)len);
305 str[len] = '\0';
306 SDL_SetClipboardText(str);
307 free(str);
308 }
309
310 NK_API struct nk_context*
nk_sdl_init(SDL_Window * win)311 nk_sdl_init(SDL_Window *win)
312 {
313 sdl.win = win;
314 nk_init_default(&sdl.ctx, 0);
315 sdl.ctx.clip.copy = nk_sdl_clipbard_copy;
316 sdl.ctx.clip.paste = nk_sdl_clipbard_paste;
317 sdl.ctx.clip.userdata = nk_handle_ptr(0);
318 nk_sdl_device_create();
319 return &sdl.ctx;
320 }
321
322 NK_API void
nk_sdl_font_stash_begin(struct nk_font_atlas ** atlas)323 nk_sdl_font_stash_begin(struct nk_font_atlas **atlas)
324 {
325 nk_font_atlas_init_default(&sdl.atlas);
326 nk_font_atlas_begin(&sdl.atlas);
327 *atlas = &sdl.atlas;
328 }
329
330 NK_API void
nk_sdl_font_stash_end(void)331 nk_sdl_font_stash_end(void)
332 {
333 const void *image; int w, h;
334 image = nk_font_atlas_bake(&sdl.atlas, &w, &h, NK_FONT_ATLAS_RGBA32);
335 nk_sdl_device_upload_atlas(image, w, h);
336 nk_font_atlas_end(&sdl.atlas, nk_handle_id((int)sdl.ogl.font_tex), &sdl.ogl.null);
337 if (sdl.atlas.default_font)
338 nk_style_set_font(&sdl.ctx, &sdl.atlas.default_font->handle);
339
340 }
341
342 NK_API int
nk_sdl_handle_event(SDL_Event * evt)343 nk_sdl_handle_event(SDL_Event *evt)
344 {
345 struct nk_context *ctx = &sdl.ctx;
346 if (evt->type == SDL_KEYUP || evt->type == SDL_KEYDOWN) {
347 /* key events */
348 int down = evt->type == SDL_KEYDOWN;
349 const Uint8* state = SDL_GetKeyboardState(0);
350 SDL_Keycode sym = evt->key.keysym.sym;
351 if (sym == SDLK_RSHIFT || sym == SDLK_LSHIFT)
352 nk_input_key(ctx, NK_KEY_SHIFT, down);
353 else if (sym == SDLK_DELETE)
354 nk_input_key(ctx, NK_KEY_DEL, down);
355 else if (sym == SDLK_RETURN)
356 nk_input_key(ctx, NK_KEY_ENTER, down);
357 else if (sym == SDLK_TAB)
358 nk_input_key(ctx, NK_KEY_TAB, down);
359 else if (sym == SDLK_BACKSPACE)
360 nk_input_key(ctx, NK_KEY_BACKSPACE, down);
361 else if (sym == SDLK_HOME) {
362 nk_input_key(ctx, NK_KEY_TEXT_START, down);
363 nk_input_key(ctx, NK_KEY_SCROLL_START, down);
364 } else if (sym == SDLK_END) {
365 nk_input_key(ctx, NK_KEY_TEXT_END, down);
366 nk_input_key(ctx, NK_KEY_SCROLL_END, down);
367 } else if (sym == SDLK_PAGEDOWN) {
368 nk_input_key(ctx, NK_KEY_SCROLL_DOWN, down);
369 } else if (sym == SDLK_PAGEUP) {
370 nk_input_key(ctx, NK_KEY_SCROLL_UP, down);
371 } else if (sym == SDLK_z)
372 nk_input_key(ctx, NK_KEY_TEXT_UNDO, down && state[SDL_SCANCODE_LCTRL]);
373 else if (sym == SDLK_r)
374 nk_input_key(ctx, NK_KEY_TEXT_REDO, down && state[SDL_SCANCODE_LCTRL]);
375 else if (sym == SDLK_c)
376 nk_input_key(ctx, NK_KEY_COPY, down && state[SDL_SCANCODE_LCTRL]);
377 else if (sym == SDLK_v)
378 nk_input_key(ctx, NK_KEY_PASTE, down && state[SDL_SCANCODE_LCTRL]);
379 else if (sym == SDLK_x)
380 nk_input_key(ctx, NK_KEY_CUT, down && state[SDL_SCANCODE_LCTRL]);
381 else if (sym == SDLK_b)
382 nk_input_key(ctx, NK_KEY_TEXT_LINE_START, down && state[SDL_SCANCODE_LCTRL]);
383 else if (sym == SDLK_e)
384 nk_input_key(ctx, NK_KEY_TEXT_LINE_END, down && state[SDL_SCANCODE_LCTRL]);
385 else if (sym == SDLK_UP)
386 nk_input_key(ctx, NK_KEY_UP, down);
387 else if (sym == SDLK_DOWN)
388 nk_input_key(ctx, NK_KEY_DOWN, down);
389 else if (sym == SDLK_LEFT) {
390 if (state[SDL_SCANCODE_LCTRL])
391 nk_input_key(ctx, NK_KEY_TEXT_WORD_LEFT, down);
392 else nk_input_key(ctx, NK_KEY_LEFT, down);
393 } else if (sym == SDLK_RIGHT) {
394 if (state[SDL_SCANCODE_LCTRL])
395 nk_input_key(ctx, NK_KEY_TEXT_WORD_RIGHT, down);
396 else nk_input_key(ctx, NK_KEY_RIGHT, down);
397 } else return 0;
398 return 1;
399 } else if (evt->type == SDL_MOUSEBUTTONDOWN || evt->type == SDL_MOUSEBUTTONUP) {
400 /* mouse button */
401 int down = evt->type == SDL_MOUSEBUTTONDOWN;
402 const int x = evt->button.x, y = evt->button.y;
403 if (evt->button.button == SDL_BUTTON_LEFT) {
404 if (evt->button.clicks > 1)
405 nk_input_button(ctx, NK_BUTTON_DOUBLE, x, y, down);
406 nk_input_button(ctx, NK_BUTTON_LEFT, x, y, down);
407 } else if (evt->button.button == SDL_BUTTON_MIDDLE)
408 nk_input_button(ctx, NK_BUTTON_MIDDLE, x, y, down);
409 else if (evt->button.button == SDL_BUTTON_RIGHT)
410 nk_input_button(ctx, NK_BUTTON_RIGHT, x, y, down);
411 return 1;
412 } else if (evt->type == SDL_MOUSEMOTION) {
413 /* mouse motion */
414 if (ctx->input.mouse.grabbed) {
415 int x = (int)ctx->input.mouse.prev.x, y = (int)ctx->input.mouse.prev.y;
416 nk_input_motion(ctx, x + evt->motion.xrel, y + evt->motion.yrel);
417 } else nk_input_motion(ctx, evt->motion.x, evt->motion.y);
418 return 1;
419 } else if (evt->type == SDL_TEXTINPUT) {
420 /* text input */
421 nk_glyph glyph;
422 memcpy(glyph, evt->text.text, NK_UTF_SIZE);
423 nk_input_glyph(ctx, glyph);
424 return 1;
425 } else if (evt->type == SDL_MOUSEWHEEL) {
426 /* mouse wheel */
427 nk_input_scroll(ctx,nk_vec2((float)evt->wheel.x,(float)evt->wheel.y));
428 return 1;
429 }
430 return 0;
431 }
432
433 NK_API
nk_sdl_shutdown(void)434 void nk_sdl_shutdown(void)
435 {
436 nk_font_atlas_clear(&sdl.atlas);
437 nk_free(&sdl.ctx);
438 nk_sdl_device_destroy();
439 memset(&sdl, 0, sizeof(sdl));
440 }
441
442 #endif
443