diff --git a/cimgui.dll b/cimgui.dll new file mode 100644 index 0000000..ba9becb Binary files /dev/null and b/cimgui.dll differ diff --git a/dub.json b/dub.json index bd949d9..44d6983 100644 --- a/dub.json +++ b/dub.json @@ -4,7 +4,8 @@ "profan" ], "dependencies": { - "derelict-sdl2": "~>3.1.0-alpha.4" + "derelict-sdl2": "~>3.1.0-alpha.4", + "derelict-imgui": "~>0.10.0" }, "description": "A minimal D application.", "copyright": "Copyright © 2018, profan", diff --git a/dub.selections.json b/dub.selections.json index c4c9272..fa384ab 100644 --- a/dub.selections.json +++ b/dub.selections.json @@ -2,6 +2,7 @@ "fileVersion": 1, "versions": { "derelict-gl3": "2.0.0-beta.6", + "derelict-imgui": "0.10.0", "derelict-sdl2": "3.1.0-alpha.4", "derelict-util": "3.0.0-beta.2" } diff --git a/source/app.d b/source/app.d index b2e1735..e2134c8 100644 --- a/source/app.d +++ b/source/app.d @@ -1,7 +1,11 @@ import std.stdio; import derelict.sdl2.sdl; +import derelict.imgui.imgui; import glad.gl.all; +import window; +import imgui; + string doCapture(string sym, uint start, uint end)(){ import std.string : format; @@ -426,6 +430,10 @@ struct Chip8 { } // step + void handle_event(ref SDL_Event ev) { + + } // handle_event + void tick() { if (run_flag) { @@ -448,49 +456,12 @@ struct Chip8 { } // Emulator -struct Window { - - import core.stdc.stdio; - import core.stdc.stdlib; - - SDL_Window* window; - SDL_GLContext context; - - void create_window(int w, int h, const char* title = "Chipd8 Emu") { - assert(w > 0, "window width must be > 0"); - assert(h > 0, "window height must be > 0"); - - // minimum OpenGL 3.3 - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); - - SDL_Window* new_win = SDL_CreateWindow(title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, w, h, SDL_WINDOW_OPENGL); - if (new_win == null) { - printf("SDL2 - Window could not be created: %s\n", SDL_GetError()); - return; - } - - SDL_GLContext new_con = SDL_GL_CreateContext(new_win); - if (new_con == null) { - printf("SDL2 - failed creating OpenGL 3.3 context: %s\n", SDL_GetError()); - return; - } - - // assign em yes - window = new_win; - context = new_con; - - atexit(SDL_Quit); - - } // create_window - -} // Window - void load_libs() { import glad.gl.loader; DerelictSDL2.load(); + DerelictImgui.load(); auto status = gladLoadGL(); } @@ -534,28 +505,10 @@ struct Emulator { SDL_Event event; while (SDL_PollEvent(&event)) { + + chip8.handle_event(event); + switch (event.type) with (SDL_EventType) { - case SDL_TEXTINPUT: { - break; - } - case SDL_TEXTEDITING: { - break; - } - case SDL_MOUSEBUTTONDOWN: { - break; - } - case SDL_MOUSEBUTTONUP: { - break; - } - case SDL_MOUSEWHEEL: { - break; - } - case SDL_KEYDOWN: { - break; - } - case SDL_KEYUP: { - break; - } case SDL_QUIT: { running = false; break; @@ -564,6 +517,7 @@ struct Emulator { break; } } + } } // handle_events diff --git a/source/imgui.d b/source/imgui.d new file mode 100644 index 0000000..54c2bf5 --- /dev/null +++ b/source/imgui.d @@ -0,0 +1,278 @@ +module imgui; + +import glad.gl.all; +import derelict.sdl2.sdl; +import derelict.imgui.imgui; + +import window; + +import std.traits : isDelegate, ReturnType, ParameterTypeTuple; +auto bindDelegate(T, string file = __FILE__, size_t line = __LINE__)(T t) if(isDelegate!T) { + + static T dg; + dg = t; + + extern(C) static ReturnType!T func(ParameterTypeTuple!T args) { + return dg(args); + } + + return &func; + +} // bindDelegate (thanks Destructionator) + +struct Imgui { + + // OpenGL data, cleanup soon + GLuint g_FontTexture = 0; + int g_ShaderHandle = 0, g_VertHandle = 0, g_FragHandle = 0; + int g_AttribLocationTex = 0, g_AttribLocationProjMtx = 0; + int g_AttribLocationPosition = 0, g_AttribLocationUV = 0, g_AttribLocationColor = 0; + uint g_VboHandle = 0, g_VaoHandle = 0, g_ElementsHandle = 0; + + void initialize() { + + auto io = igGetIO(); + io.KeyMap[ImGuiKey_Tab] = SDL_SCANCODE_TAB; + io.KeyMap[ImGuiKey_LeftArrow] = SDL_SCANCODE_LEFT; + io.KeyMap[ImGuiKey_RightArrow] = SDL_SCANCODE_RIGHT; + io.KeyMap[ImGuiKey_UpArrow] = SDL_SCANCODE_UP; + io.KeyMap[ImGuiKey_DownArrow] = SDL_SCANCODE_DOWN; + io.KeyMap[ImGuiKey_PageUp] = SDL_SCANCODE_PAGEUP; + io.KeyMap[ImGuiKey_PageDown] = SDL_SCANCODE_PAGEDOWN; + io.KeyMap[ImGuiKey_Home] = SDL_SCANCODE_HOME; + io.KeyMap[ImGuiKey_End] = SDL_SCANCODE_END; + io.KeyMap[ImGuiKey_Delete] = SDL_SCANCODE_DELETE; + io.KeyMap[ImGuiKey_Backspace] = SDL_SCANCODE_BACKSPACE; + io.KeyMap[ImGuiKey_Enter] = SDL_SCANCODE_RETURN; + io.KeyMap[ImGuiKey_Escape] = SDL_SCANCODE_ESCAPE; + io.KeyMap[ImGuiKey_A] = SDL_SCANCODE_A; + io.KeyMap[ImGuiKey_C] = SDL_SCANCODE_C; + io.KeyMap[ImGuiKey_V] = SDL_SCANCODE_V; + io.KeyMap[ImGuiKey_X] = SDL_SCANCODE_X; + io.KeyMap[ImGuiKey_Y] = SDL_SCANCODE_Y; + io.KeyMap[ImGuiKey_Z] = SDL_SCANCODE_Z; + + io.RenderDrawListsFn = bindDelegate(&render_draw_lists); + io.SetClipboardTextFn = bindDelegate(&set_clipboard_text); + io.GetClipboardTextFn = bindDelegate(&get_clipboard_text); + + } + + void create_font_texture() { + + } + + void create_device_objects() { + + // backup GL state + GLint last_texture, last_array_buffer, last_vertex_array; + glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); + glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer); + glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array); + + const GLchar *vertex_shader = q{ + #version 330 + uniform mat4 ProjMtx; + in vec2 Position; + in vec2 UV; + in vec4 Color; + out vec2 Frag_UV; + out vec4 Frag_Color; + void main() { + Frag_UV = UV; + Frag_Color = Color; + gl_Position = ProjMtx * vec4(Position.xy,0,1); + } + }; + + const GLchar* fragment_shader = q{ + #version 330 + uniform sampler2D Texture; + in vec2 Frag_UV; + in vec4 Frag_Color; + out vec4 Out_Color; + void main() { + Out_Color = Frag_Color * texture( Texture, Frag_UV.st); + } + }; + + g_ShaderHandle = glCreateProgram(); + g_VertHandle = glCreateShader(GL_VERTEX_SHADER); + g_FragHandle = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(g_VertHandle, 1, &vertex_shader, null); + glShaderSource(g_FragHandle, 1, &fragment_shader, null); + glCompileShader(g_VertHandle); + glCompileShader(g_FragHandle); + glAttachShader(g_ShaderHandle, g_VertHandle); + glAttachShader(g_ShaderHandle, g_FragHandle); + glLinkProgram(g_ShaderHandle); + + g_AttribLocationTex = glGetUniformLocation(g_ShaderHandle, "Texture"); + g_AttribLocationProjMtx = glGetUniformLocation(g_ShaderHandle, "ProjMtx"); + g_AttribLocationPosition = glGetAttribLocation(g_ShaderHandle, "Position"); + g_AttribLocationUV = glGetAttribLocation(g_ShaderHandle, "UV"); + g_AttribLocationColor = glGetAttribLocation(g_ShaderHandle, "Color"); + + glGenBuffers(1, &g_VboHandle); + glGenBuffers(1, &g_ElementsHandle); + + glGenVertexArrays(1, &g_VaoHandle); + glBindVertexArray(g_VaoHandle); + glBindBuffer(GL_ARRAY_BUFFER, g_VboHandle); + glEnableVertexAttribArray(g_AttribLocationPosition); + glEnableVertexAttribArray(g_AttribLocationUV); + glEnableVertexAttribArray(g_AttribLocationColor); + + glVertexAttribPointer(g_AttribLocationPosition, 2, GL_FLOAT, GL_FALSE, ImDrawVert.sizeof, cast(void*)ImDrawVert.pos.offsetof); + glVertexAttribPointer(g_AttribLocationUV, 2, GL_FLOAT, GL_FALSE, ImDrawVert.sizeof, cast(void*)ImDrawVert.uv.offsetof); + glVertexAttribPointer(g_AttribLocationColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, ImDrawVert.sizeof, cast(void*)ImDrawVert.col.offsetof); + + create_font_texture(); + + // restore modified GL state + glBindTexture(GL_TEXTURE_2D, last_texture); + glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer); + glBindVertexArray(last_vertex_array); + + } + + extern(C) nothrow + void render_draw_lists(ImDrawData* draw_data) { + + // avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates) + auto io = igGetIO(); + int fb_width = cast(int)(io.DisplaySize.x * io.DisplayFramebufferScale.x); + int fb_height = cast(int)(io.DisplaySize.y * io.DisplayFramebufferScale.y); + if (fb_width == 0 || fb_height == 0) { return; } + draw_data.ScaleClipRects(io.DisplayFramebufferScale); + + // backup GL state + GLenum last_active_texture; glGetIntegerv(GL_ACTIVE_TEXTURE, cast(GLint*)&last_active_texture); + glActiveTexture(GL_TEXTURE0); + GLint last_program; glGetIntegerv(GL_CURRENT_PROGRAM, &last_program); + GLint last_texture; glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); + GLint last_array_buffer; glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer); + GLint last_element_array_buffer; glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &last_element_array_buffer); + GLint last_vertex_array; glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array); + GLint[4] last_viewport; glGetIntegerv(GL_VIEWPORT, last_viewport.ptr); + GLint[4] last_scissor_box; glGetIntegerv(GL_SCISSOR_BOX, last_scissor_box.ptr); + GLenum last_blend_src_rgb; glGetIntegerv(GL_BLEND_SRC_RGB, cast(GLint*)&last_blend_src_rgb); + GLenum last_blend_dst_rgb; glGetIntegerv(GL_BLEND_DST_RGB, cast(GLint*)&last_blend_dst_rgb); + GLenum last_blend_src_alpha; glGetIntegerv(GL_BLEND_SRC_ALPHA, cast(GLint*)&last_blend_src_alpha); + GLenum last_blend_dst_alpha; glGetIntegerv(GL_BLEND_DST_ALPHA, cast(GLint*)&last_blend_dst_alpha); + GLenum last_blend_equation_rgb; glGetIntegerv(GL_BLEND_EQUATION_RGB, cast(GLint*)&last_blend_equation_rgb); + GLenum last_blend_equation_alpha; glGetIntegerv(GL_BLEND_EQUATION_ALPHA, cast(GLint*)&last_blend_equation_alpha); + GLboolean last_enable_blend = glIsEnabled(GL_BLEND); + GLboolean last_enable_cull_face = glIsEnabled(GL_CULL_FACE); + GLboolean last_enable_depth_test = glIsEnabled(GL_DEPTH_TEST); + GLboolean last_enable_scissor_test = glIsEnabled(GL_SCISSOR_TEST); + + // Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled + 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); + + // Setup viewport, orthographic projection matrix + glViewport(0, 0, cast(GLsizei)fb_width, cast(GLsizei)fb_height); + const float[4][4] ortho_projection = [ + [ 2.0f/io.DisplaySize.x, 0.0f, 0.0f, 0.0f ], + [ 0.0f, 2.0f/-io.DisplaySize.y, 0.0f, 0.0f ], + [ 0.0f, 0.0f, -1.0f, 0.0f ], + [-1.0f, 1.0f, 0.0f, 1.0f ], + ]; + glUseProgram(g_ShaderHandle); + glUniform1i(g_AttribLocationTex, 0); + glUniformMatrix4fv(g_AttribLocationProjMtx, 1, GL_FALSE, &ortho_projection[0][0]); + glBindVertexArray(g_VaoHandle); + + foreach (int n; 0 .. draw_data.CmdListsCount) { + ImDrawList* cmd_list = draw_data.CmdLists[n]; + ImDrawIdx* idx_buffer_offset; + + auto countVertices = ImDrawList_GetVertexBufferSize(cmd_list); + auto countIndices = ImDrawList_GetIndexBufferSize(cmd_list); + + glBindBuffer(GL_ARRAY_BUFFER, g_VboHandle); + glBufferData(GL_ARRAY_BUFFER, countVertices * ImDrawVert.sizeof, cast(GLvoid*)ImDrawList_GetVertexPtr(cmd_list,0), GL_STREAM_DRAW); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_ElementsHandle); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, countIndices * ImDrawIdx.sizeof, cast(GLvoid*)ImDrawList_GetIndexPtr(cmd_list,0), GL_STREAM_DRAW); + + auto cmdCnt = ImDrawList_GetCmdSize(cmd_list); + + foreach(int cmd_i; 0 .. cmdCnt) { + auto pcmd = ImDrawList_GetCmdPtr(cmd_list, cmd_i); + + if (pcmd.UserCallback) { + pcmd.UserCallback(cmd_list, pcmd); + } else { + glBindTexture(GL_TEXTURE_2D, cast(GLuint)pcmd.TextureId); + glScissor(cast(int)pcmd.ClipRect.x, cast(int)(fb_height - pcmd.ClipRect.w), cast(int)(pcmd.ClipRect.z - pcmd.ClipRect.x), cast(int)(pcmd.ClipRect.w - pcmd.ClipRect.y)); + glDrawElements(GL_TRIANGLES, cast(GLsizei)pcmd.ElemCount, ImDrawIdx.sizeof == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset); + } + idx_buffer_offset += pcmd.ElemCount; + } + } + + // Restore modified GL state + glUseProgram(last_program); + glBindTexture(GL_TEXTURE_2D, last_texture); + glActiveTexture(last_active_texture); + glBindVertexArray(last_vertex_array); + glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, last_element_array_buffer); + glBlendEquationSeparate(last_blend_equation_rgb, last_blend_equation_alpha); + glBlendFuncSeparate(last_blend_src_rgb, last_blend_dst_rgb, last_blend_src_alpha, last_blend_dst_alpha); + if (last_enable_blend) glEnable(GL_BLEND); else glDisable(GL_BLEND); + if (last_enable_cull_face) glEnable(GL_CULL_FACE); else glDisable(GL_CULL_FACE); + if (last_enable_depth_test) glEnable(GL_DEPTH_TEST); else glDisable(GL_DEPTH_TEST); + if (last_enable_scissor_test) glEnable(GL_SCISSOR_TEST); else glDisable(GL_SCISSOR_TEST); + glViewport(last_viewport[0], last_viewport[1], cast(GLsizei)last_viewport[2], cast(GLsizei)last_viewport[3]); + glScissor(last_scissor_box[0], last_scissor_box[1], cast(GLsizei)last_scissor_box[2], cast(GLsizei)last_scissor_box[3]); + + } + + extern(C) nothrow + const(char)* get_clipboard_text(void* user_data) { + return SDL_GetClipboardText(); + } + + extern(C) nothrow + void set_clipboard_text(void* user_data, const (char)* text) { + SDL_SetClipboardText(text); + } + + void new_frame(ref Window win) { + + } + + void handle_event(ref SDL_Event ev) { + + switch (ev.type) with (SDL_EventType) { + + case SDL_TEXTINPUT: + break; + + case SDL_MOUSEBUTTONDOWN: + break; + case SDL_MOUSEBUTTONUP: + break; + case SDL_MOUSEWHEEL: + break; + + case SDL_KEYDOWN: + break; + case SDL_KEYUP: + break; + + default: + break; + + } + + } + +} \ No newline at end of file diff --git a/source/window.d b/source/window.d new file mode 100644 index 0000000..237eb88 --- /dev/null +++ b/source/window.d @@ -0,0 +1,95 @@ +module window; + +import derelict.sdl2.sdl; +import glad.gl.all; + +/** + * Converts an integer representing a colour, for example 0x428bca into a 4 element + * int array for passing to OpenGL. +*/ +GLfloat[4] to(T : GLfloat[4])(int color, ubyte alpha = 255) nothrow @nogc pure { + + GLfloat[4] gl_color = [ //mask out r, g, b components from int + cast(float)cast(ubyte)(color>>16)/255, + cast(float)cast(ubyte)(color>>8)/255, + cast(float)cast(ubyte)(color)/255, + cast(float)cast(ubyte)(alpha)/255 + ]; + + return gl_color; + +} // to!GLfloat[4] + +struct Window { + + import core.stdc.stdio; + import core.stdc.stdlib; + + SDL_Window* window; + SDL_GLContext context; + + void create_window(int w, int h, const char* title = "Chipd8 Emu") { + assert(w > 0, "window width must be > 0"); + assert(h > 0, "window height must be > 0"); + + // minimum OpenGL 3.3 + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 32); + + SDL_Window* new_win = SDL_CreateWindow(title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, w, h, SDL_WINDOW_OPENGL); + if (new_win == null) { + printf("SDL2 - Window could not be created: %s\n", SDL_GetError()); + return; + } + + SDL_GLContext new_con = SDL_GL_CreateContext(new_win); + if (new_con == null) { + printf("SDL2 - failed creating OpenGL 3.3 context: %s\n", SDL_GetError()); + return; + } + + // assign em yes + window = new_win; + context = new_con; + + atexit(SDL_Quit); + + } // create_window + + void handle_event(ref SDL_Event ev) { + + } + + void render_clear(int colour) { + + auto col = to!GLColor(color, 255); + glClearColor(col[0], col[1], col[2], col[3]); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + } + + void render_present() { + + SDL_GL_SwapWindow(window); + + } + + @property nothrow @nogc { + + int[2] window_size() { + int w, h; + SDL_GetWindowSize(window, &w, &h); + return [w, h]; + } + + int[2] framebuffer_size() { + int w, h; + SDL_GL_GetDrawableSize(window, &w, &h); + return [w, h]; + } + + } + +} // Window \ No newline at end of file