cdaa1f4bda
All modifed state changed by the demo rendering functions is now set to default values after being called. I tried to save and restore all relavent state but for some reason function glBlendEquationSeperate is not defined for me. So I decided to make it as easy as possible for me and just set everything to default. This means you have to either save and restore or reset your own OpenGL state after calling `nk_xxx_render`. I know it is not perfect but I have no better solution at this point in time.
366 lines
13 KiB
C
366 lines
13 KiB
C
#include <string.h>
|
|
#include <SDL2/SDL.h>
|
|
|
|
#include "nuklear_sdl.h"
|
|
#define NK_IMPLEMENTATION
|
|
#include "../../nuklear.h"
|
|
|
|
struct nk_sdl_device {
|
|
struct nk_buffer cmds;
|
|
struct nk_draw_null_texture null;
|
|
GLuint vbo, vao, ebo;
|
|
GLuint prog;
|
|
GLuint vert_shdr;
|
|
GLuint frag_shdr;
|
|
GLint attrib_pos;
|
|
GLint attrib_uv;
|
|
GLint attrib_col;
|
|
GLint uniform_tex;
|
|
GLint uniform_proj;
|
|
GLuint font_tex;
|
|
};
|
|
|
|
static struct nk_sdl {
|
|
SDL_Window *win;
|
|
struct nk_sdl_device ogl;
|
|
struct nk_context ctx;
|
|
struct nk_font_atlas atlas;
|
|
} sdl;
|
|
|
|
#ifdef __APPLE__
|
|
#define NK_SHADER_VERSION "#version 150\n"
|
|
#else
|
|
#define NK_SHADER_VERSION "#version 300 es\n"
|
|
#endif
|
|
NK_API void
|
|
nk_sdl_device_create(void)
|
|
{
|
|
GLint status;
|
|
static const GLchar *vertex_shader =
|
|
NK_SHADER_VERSION
|
|
"uniform mat4 ProjMtx;\n"
|
|
"in vec2 Position;\n"
|
|
"in vec2 TexCoord;\n"
|
|
"in vec4 Color;\n"
|
|
"out vec2 Frag_UV;\n"
|
|
"out vec4 Frag_Color;\n"
|
|
"void main() {\n"
|
|
" Frag_UV = TexCoord;\n"
|
|
" Frag_Color = Color;\n"
|
|
" gl_Position = ProjMtx * vec4(Position.xy, 0, 1);\n"
|
|
"}\n";
|
|
static const GLchar *fragment_shader =
|
|
NK_SHADER_VERSION
|
|
"precision mediump float;\n"
|
|
"uniform sampler2D Texture;\n"
|
|
"in vec2 Frag_UV;\n"
|
|
"in vec4 Frag_Color;\n"
|
|
"out vec4 Out_Color;\n"
|
|
"void main(){\n"
|
|
" Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
|
|
"}\n";
|
|
|
|
struct nk_sdl_device *dev = &sdl.ogl;
|
|
nk_buffer_init_default(&dev->cmds);
|
|
dev->prog = glCreateProgram();
|
|
dev->vert_shdr = glCreateShader(GL_VERTEX_SHADER);
|
|
dev->frag_shdr = glCreateShader(GL_FRAGMENT_SHADER);
|
|
glShaderSource(dev->vert_shdr, 1, &vertex_shader, 0);
|
|
glShaderSource(dev->frag_shdr, 1, &fragment_shader, 0);
|
|
glCompileShader(dev->vert_shdr);
|
|
glCompileShader(dev->frag_shdr);
|
|
glGetShaderiv(dev->vert_shdr, GL_COMPILE_STATUS, &status);
|
|
assert(status == GL_TRUE);
|
|
glGetShaderiv(dev->frag_shdr, GL_COMPILE_STATUS, &status);
|
|
assert(status == GL_TRUE);
|
|
glAttachShader(dev->prog, dev->vert_shdr);
|
|
glAttachShader(dev->prog, dev->frag_shdr);
|
|
glLinkProgram(dev->prog);
|
|
glGetProgramiv(dev->prog, GL_LINK_STATUS, &status);
|
|
assert(status == GL_TRUE);
|
|
|
|
dev->uniform_tex = glGetUniformLocation(dev->prog, "Texture");
|
|
dev->uniform_proj = glGetUniformLocation(dev->prog, "ProjMtx");
|
|
dev->attrib_pos = glGetAttribLocation(dev->prog, "Position");
|
|
dev->attrib_uv = glGetAttribLocation(dev->prog, "TexCoord");
|
|
dev->attrib_col = glGetAttribLocation(dev->prog, "Color");
|
|
|
|
{
|
|
/* buffer setup */
|
|
GLsizei vs = sizeof(struct nk_draw_vertex);
|
|
size_t vp = offsetof(struct nk_draw_vertex, position);
|
|
size_t vt = offsetof(struct nk_draw_vertex, uv);
|
|
size_t vc = offsetof(struct nk_draw_vertex, col);
|
|
|
|
glGenBuffers(1, &dev->vbo);
|
|
glGenBuffers(1, &dev->ebo);
|
|
glGenVertexArrays(1, &dev->vao);
|
|
|
|
glBindVertexArray(dev->vao);
|
|
glBindBuffer(GL_ARRAY_BUFFER, dev->vbo);
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, dev->ebo);
|
|
|
|
glEnableVertexAttribArray((GLuint)dev->attrib_pos);
|
|
glEnableVertexAttribArray((GLuint)dev->attrib_uv);
|
|
glEnableVertexAttribArray((GLuint)dev->attrib_col);
|
|
|
|
glVertexAttribPointer((GLuint)dev->attrib_pos, 2, GL_FLOAT, GL_FALSE, vs, (void*)vp);
|
|
glVertexAttribPointer((GLuint)dev->attrib_uv, 2, GL_FLOAT, GL_FALSE, vs, (void*)vt);
|
|
glVertexAttribPointer((GLuint)dev->attrib_col, 4, GL_UNSIGNED_BYTE, GL_TRUE, vs, (void*)vc);
|
|
}
|
|
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
|
glBindVertexArray(0);
|
|
}
|
|
|
|
NK_INTERN void
|
|
nk_sdl_device_upload_atlas(const void *image, int width, int height)
|
|
{
|
|
struct nk_sdl_device *dev = &sdl.ogl;
|
|
glGenTextures(1, &dev->font_tex);
|
|
glBindTexture(GL_TEXTURE_2D, dev->font_tex);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)width, (GLsizei)height, 0,
|
|
GL_RGBA, GL_UNSIGNED_BYTE, image);
|
|
}
|
|
|
|
NK_API void
|
|
nk_sdl_device_destroy(void)
|
|
{
|
|
struct nk_sdl_device *dev = &sdl.ogl;
|
|
glDetachShader(dev->prog, dev->vert_shdr);
|
|
glDetachShader(dev->prog, dev->frag_shdr);
|
|
glDeleteShader(dev->vert_shdr);
|
|
glDeleteShader(dev->frag_shdr);
|
|
glDeleteProgram(dev->prog);
|
|
glDeleteTextures(1, &dev->font_tex);
|
|
glDeleteBuffers(1, &dev->vbo);
|
|
glDeleteBuffers(1, &dev->ebo);
|
|
nk_buffer_free(&dev->cmds);
|
|
}
|
|
|
|
NK_API void
|
|
nk_sdl_render(enum nk_anti_aliasing AA, int max_vertex_buffer, int max_element_buffer)
|
|
{
|
|
|
|
struct nk_sdl_device *dev = &sdl.ogl;
|
|
int width, height;
|
|
GLfloat ortho[4][4] = {
|
|
{2.0f, 0.0f, 0.0f, 0.0f},
|
|
{0.0f,-2.0f, 0.0f, 0.0f},
|
|
{0.0f, 0.0f,-1.0f, 0.0f},
|
|
{-1.0f,1.0f, 0.0f, 1.0f},
|
|
};
|
|
SDL_GetWindowSize(sdl.win, &width, &height);
|
|
ortho[0][0] /= (GLfloat)width;
|
|
ortho[1][1] /= (GLfloat)height;
|
|
|
|
/* setup global state */
|
|
glEnable(GL_BLEND);
|
|
glBlendEquation(GL_FUNC_ADD);
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
glDisable(GL_CULL_FACE);
|
|
glDisable(GL_DEPTH_TEST);
|
|
glEnable(GL_SCISSOR_TEST);
|
|
glActiveTexture(GL_TEXTURE0);
|
|
|
|
/* setup program */
|
|
glUseProgram(dev->prog);
|
|
glUniform1i(dev->uniform_tex, 0);
|
|
glUniformMatrix4fv(dev->uniform_proj, 1, GL_FALSE, &ortho[0][0]);
|
|
{
|
|
/* convert from command queue into draw list and draw to screen */
|
|
const struct nk_draw_command *cmd;
|
|
void *vertices, *elements;
|
|
const nk_draw_index *offset = NULL;
|
|
|
|
/* allocate vertex and element buffer */
|
|
glBindVertexArray(dev->vao);
|
|
glBindBuffer(GL_ARRAY_BUFFER, dev->vbo);
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, dev->ebo);
|
|
|
|
glBufferData(GL_ARRAY_BUFFER, max_vertex_buffer, NULL, GL_STREAM_DRAW);
|
|
glBufferData(GL_ELEMENT_ARRAY_BUFFER, max_element_buffer, NULL, GL_STREAM_DRAW);
|
|
|
|
/* load draw vertices & elements directly into vertex + element buffer */
|
|
vertices = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
|
|
elements = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY);
|
|
{
|
|
/* fill converting configuration */
|
|
struct nk_convert_config config;
|
|
memset(&config, 0, sizeof(config));
|
|
config.global_alpha = 1.0f;
|
|
config.shape_AA = AA;
|
|
config.line_AA = AA;
|
|
config.circle_segment_count = 22;
|
|
config.curve_segment_count = 22;
|
|
config.arc_segment_count = 22;
|
|
config.null = dev->null;
|
|
|
|
/* setup buffers to load vertices and elements */
|
|
{struct nk_buffer vbuf, ebuf;
|
|
nk_buffer_init_fixed(&vbuf, vertices, (size_t)max_vertex_buffer);
|
|
nk_buffer_init_fixed(&ebuf, elements, (size_t)max_element_buffer);
|
|
nk_convert(&sdl.ctx, &dev->cmds, &vbuf, &ebuf, &config);}
|
|
}
|
|
glUnmapBuffer(GL_ARRAY_BUFFER);
|
|
glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER);
|
|
|
|
/* iterate over and execute each draw command */
|
|
nk_draw_foreach(cmd, &sdl.ctx, &dev->cmds) {
|
|
if (!cmd->elem_count) continue;
|
|
glBindTexture(GL_TEXTURE_2D, (GLuint)cmd->texture.id);
|
|
glScissor((GLint)cmd->clip_rect.x,
|
|
height - (GLint)(cmd->clip_rect.y + cmd->clip_rect.h),
|
|
(GLint)cmd->clip_rect.w, (GLint)cmd->clip_rect.h);
|
|
glDrawElements(GL_TRIANGLES, (GLsizei)cmd->elem_count, GL_UNSIGNED_SHORT, offset);
|
|
offset += cmd->elem_count;
|
|
}
|
|
nk_clear(&sdl.ctx);
|
|
}
|
|
|
|
glUseProgram(0);
|
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
|
glBindVertexArray(0);
|
|
glDisable(GL_BLEND);
|
|
glDisable(GL_SCISSOR_TEST);
|
|
}
|
|
|
|
static void
|
|
nk_sdl_clipbard_paste(nk_handle usr, struct nk_text_edit *edit)
|
|
{
|
|
const char *text = SDL_GetClipboardText();
|
|
if (text) nk_textedit_paste(edit, text, nk_strlen(text));
|
|
(void)usr;
|
|
}
|
|
|
|
static void
|
|
nk_sdl_clipbard_copy(nk_handle usr, const char *text, int len)
|
|
{
|
|
char *str = 0;
|
|
(void)usr;
|
|
if (!len) return;
|
|
str = malloc((size_t)len+1);
|
|
if (!str) return;
|
|
memcpy(str, text, (size_t)len);
|
|
str[len] = '\0';
|
|
SDL_SetClipboardText(str);
|
|
free(str);
|
|
}
|
|
|
|
NK_API struct nk_context*
|
|
nk_sdl_init(SDL_Window *win)
|
|
{
|
|
sdl.win = win;
|
|
nk_init_default(&sdl.ctx, 0);
|
|
sdl.ctx.clip.copy = nk_sdl_clipbard_copy;
|
|
sdl.ctx.clip.paste = nk_sdl_clipbard_paste;
|
|
sdl.ctx.clip.userdata = nk_handle_ptr(0);
|
|
nk_sdl_device_create();
|
|
return &sdl.ctx;
|
|
}
|
|
|
|
NK_API void
|
|
nk_sdl_font_stash_begin(struct nk_font_atlas **atlas)
|
|
{
|
|
nk_font_atlas_init_default(&sdl.atlas);
|
|
nk_font_atlas_begin(&sdl.atlas);
|
|
*atlas = &sdl.atlas;
|
|
}
|
|
|
|
NK_API void
|
|
nk_sdl_font_stash_end(void)
|
|
{
|
|
const void *image; int w, h;
|
|
image = nk_font_atlas_bake(&sdl.atlas, &w, &h, NK_FONT_ATLAS_RGBA32);
|
|
nk_sdl_device_upload_atlas(image, w, h);
|
|
nk_font_atlas_end(&sdl.atlas, nk_handle_id((int)sdl.ogl.font_tex), &sdl.ogl.null);
|
|
if (sdl.atlas.default_font)
|
|
nk_style_set_font(&sdl.ctx, &sdl.atlas.default_font->handle);
|
|
|
|
}
|
|
|
|
NK_API void
|
|
nk_sdl_handle_event(SDL_Event *evt)
|
|
{
|
|
struct nk_context *ctx = &sdl.ctx;
|
|
if (evt->type == SDL_WINDOWEVENT) {
|
|
/* handle window resizing */
|
|
if (evt->window.event != SDL_WINDOWEVENT_RESIZED) return;
|
|
glViewport(0, 0, evt->window.data1, evt->window.data2);
|
|
} else if (evt->type == SDL_KEYUP || evt->type == SDL_KEYDOWN) {
|
|
/* key events */
|
|
int down = evt->type == SDL_KEYDOWN;
|
|
const Uint8* state = SDL_GetKeyboardState(0);
|
|
SDL_Keycode sym = evt->key.keysym.sym;
|
|
if (sym == SDLK_RSHIFT || sym == SDLK_LSHIFT)
|
|
nk_input_key(ctx, NK_KEY_SHIFT, down);
|
|
else if (sym == SDLK_DELETE)
|
|
nk_input_key(ctx, NK_KEY_DEL, down);
|
|
else if (sym == SDLK_RETURN)
|
|
nk_input_key(ctx, NK_KEY_ENTER, down);
|
|
else if (sym == SDLK_TAB)
|
|
nk_input_key(ctx, NK_KEY_TAB, down);
|
|
else if (sym == SDLK_BACKSPACE)
|
|
nk_input_key(ctx, NK_KEY_BACKSPACE, down);
|
|
else if (sym == SDLK_HOME)
|
|
nk_input_key(ctx, NK_KEY_TEXT_START, down);
|
|
else if (sym == SDLK_END)
|
|
nk_input_key(ctx, NK_KEY_TEXT_END, down);
|
|
else if (sym == SDLK_z)
|
|
nk_input_key(ctx, NK_KEY_TEXT_UNDO, down && state[SDL_SCANCODE_LCTRL]);
|
|
else if (sym == SDLK_r)
|
|
nk_input_key(ctx, NK_KEY_TEXT_REDO, down && state[SDL_SCANCODE_LCTRL]);
|
|
else if (sym == SDLK_c)
|
|
nk_input_key(ctx, NK_KEY_COPY, down && state[SDL_SCANCODE_LCTRL]);
|
|
else if (sym == SDLK_v)
|
|
nk_input_key(ctx, NK_KEY_PASTE, down && state[SDL_SCANCODE_LCTRL]);
|
|
else if (sym == SDLK_x)
|
|
nk_input_key(ctx, NK_KEY_CUT, down && state[SDL_SCANCODE_LCTRL]);
|
|
else if (sym == SDLK_b)
|
|
nk_input_key(ctx, NK_KEY_TEXT_LINE_START, down && state[SDL_SCANCODE_LCTRL]);
|
|
else if (sym == SDLK_e)
|
|
nk_input_key(ctx, NK_KEY_TEXT_LINE_END, down && state[SDL_SCANCODE_LCTRL]);
|
|
else if (sym == SDLK_LEFT) {
|
|
if (state[SDL_SCANCODE_LCTRL])
|
|
nk_input_key(ctx, NK_KEY_TEXT_WORD_LEFT, down);
|
|
else nk_input_key(ctx, NK_KEY_LEFT, down);
|
|
} else if (sym == SDLK_RIGHT) {
|
|
if (state[SDL_SCANCODE_LCTRL])
|
|
nk_input_key(ctx, NK_KEY_TEXT_WORD_RIGHT, down);
|
|
else nk_input_key(ctx, NK_KEY_RIGHT, down);
|
|
}
|
|
} else if (evt->type == SDL_MOUSEBUTTONDOWN || evt->type == SDL_MOUSEBUTTONUP) {
|
|
/* mouse button */
|
|
int down = evt->type == SDL_MOUSEBUTTONDOWN;
|
|
const int x = evt->button.x, y = evt->button.y;
|
|
if (evt->button.button == SDL_BUTTON_LEFT)
|
|
nk_input_button(ctx, NK_BUTTON_LEFT, x, y, down);
|
|
if (evt->button.button == SDL_BUTTON_MIDDLE)
|
|
nk_input_button(ctx, NK_BUTTON_MIDDLE, x, y, down);
|
|
if (evt->button.button == SDL_BUTTON_RIGHT)
|
|
nk_input_button(ctx, NK_BUTTON_RIGHT, x, y, down);
|
|
} else if (evt->type == SDL_MOUSEMOTION) {
|
|
nk_input_motion(ctx, evt->motion.x, evt->motion.y);
|
|
} else if (evt->type == SDL_TEXTINPUT) {
|
|
nk_glyph glyph;
|
|
memcpy(glyph, evt->text.text, NK_UTF_SIZE);
|
|
nk_input_glyph(ctx, glyph);
|
|
} else if (evt->type == SDL_MOUSEWHEEL) {
|
|
nk_input_scroll(ctx,(float)evt->wheel.y);
|
|
}
|
|
}
|
|
|
|
NK_API
|
|
void nk_sdl_shutdown(void)
|
|
{
|
|
nk_font_atlas_clear(&sdl.atlas);
|
|
nk_free(&sdl.ctx);
|
|
nk_sdl_device_destroy();
|
|
}
|
|
|