diff options
| author | s-ol <s-ol@users.noreply.github.com> | 2019-10-21 20:52:37 +0000 |
|---|---|---|
| committer | s-ol <s-ol@users.noreply.github.com> | 2019-10-21 20:52:37 +0000 |
| commit | f0315f0c554b5622694948d65d0cf347a5dca07b (patch) | |
| tree | c07a81ac792f541e70a1afd61ba18f3f24047bd6 /src | |
| parent | add cimgui in the repo (diff) | |
| download | zig-imgui-f0315f0c554b5622694948d65d0cf347a5dca07b.tar.gz zig-imgui-f0315f0c554b5622694948d65d0cf347a5dca07b.zip | |
zig-fmt everything
Diffstat (limited to 'src')
| -rw-r--r-- | src/c.zig | 8 | ||||
| -rw-r--r-- | src/gl3_impl.zig | 795 | ||||
| -rw-r--r-- | src/glfw_impl.zig | 497 | ||||
| -rw-r--r-- | src/main.zig | 159 |
4 files changed, 731 insertions, 728 deletions
@@ -1,6 +1,6 @@ pub usingnamespace @cImport({ - @cInclude("epoxy/gl.h"); - @cDefine("CIMGUI_DEFINE_ENUMS_AND_STRUCTS", "1"); - @cInclude("cimgui.h"); - @cInclude("GLFW/glfw3.h"); + @cInclude("epoxy/gl.h"); + @cDefine("CIMGUI_DEFINE_ENUMS_AND_STRUCTS", "1"); + @cInclude("cimgui.h"); + @cInclude("GLFW/glfw3.h"); }); diff --git a/src/gl3_impl.zig b/src/gl3_impl.zig index 70a60b5..b056056 100644 --- a/src/gl3_impl.zig +++ b/src/gl3_impl.zig @@ -1,3 +1,4 @@ +/// this is a port of cimgui/imgui/examples/imgui_impl_opengl3.cpp const c = @import("c.zig"); const mem = @import("std").mem; const math = @import("std").math; @@ -5,440 +6,444 @@ const debug = @import("std").debug; const builtin = @import("builtin"); const OpenGLHasDrawWithBaseVertex = @hasField(c, "IMGUI_IMPL_OPENGL_ES2") or - @hasField(c, "IMGUI_IMPL_OPENGL_ES3"); + @hasField(c, "IMGUI_IMPL_OPENGL_ES3"); // OpenGL Data -var g_GlslVersionString_buf : [32]u8 = undefined; -var g_GlslVersionString : []u8 = g_GlslVersionString_buf[0..0]; +var g_GlslVersionString_buf: [32]u8 = undefined; +var g_GlslVersionString: []u8 = g_GlslVersionString_buf[0..0]; -var g_FontTexture : c.GLuint = 0; -var g_ShaderHandle : c.GLuint = 0; -var g_VertHandle : c.GLuint = 0; +var g_FontTexture: c.GLuint = 0; +var g_ShaderHandle: c.GLuint = 0; +var g_VertHandle: c.GLuint = 0; var g_FragHandle: c.GLuint = 0; -var g_AttribLocationTex : c.GLint = 0; -var g_AttribLocationProjMtx : c.GLint = 0; -var g_AttribLocationVtxPos : c.GLint = 0; -var g_AttribLocationVtxUV : c.GLint = 0; -var g_AttribLocationVtxColor : c.GLint = 0; -var g_VboHandle : c.GLuint = 0; -var g_ElementsHandle : c.GLuint = 0; +var g_AttribLocationTex: c.GLint = 0; +var g_AttribLocationProjMtx: c.GLint = 0; +var g_AttribLocationVtxPos: c.GLint = 0; +var g_AttribLocationVtxUV: c.GLint = 0; +var g_AttribLocationVtxColor: c.GLint = 0; +var g_VboHandle: c.GLuint = 0; +var g_ElementsHandle: c.GLuint = 0; pub fn Init() void { - const io = c.igGetIO(); - io.*.BackendRendererName = c"imgui_impl_gl3.zig"; - if (OpenGLHasDrawWithBaseVertex) - io.*.BackendFlags |= @enumToInt(c.ImGuiBackendFlags_RendererHasVtxOffset); - // @TODO: Viewports - // io.*.BackendFlags |= @enumToInt(c.ImGuiBackendFlags_RendererHasViewports); - - // @TODO: GLSL versions? - // g_GlslVersionString = g_GlslVersionString_buf[0..glsl_version.len]; - // mem.copy(u8, g_GlslVersionString, glsl_version); - - // @FIXME: just for testing: - var tex : c.GLint = undefined; - c.glGetIntegerv(c.GL_TEXTURE_BINDING_2D, &tex); - - // @TODO: Viewports - // if (io.*.ConfigFlags & @enumToInt(c.ImGuiConfigFlags_ViewportsEnable) != 0) - // InitPlatformInterface(); + const io = c.igGetIO(); + io.*.BackendRendererName = c"imgui_impl_gl3.zig"; + if (OpenGLHasDrawWithBaseVertex) + io.*.BackendFlags |= @enumToInt(c.ImGuiBackendFlags_RendererHasVtxOffset); + // @TODO: Viewports + // io.*.BackendFlags |= @enumToInt(c.ImGuiBackendFlags_RendererHasViewports); + + // @TODO: GLSL versions? + // g_GlslVersionString = g_GlslVersionString_buf[0..glsl_version.len]; + // mem.copy(u8, g_GlslVersionString, glsl_version); + + // @FIXME: just for testing: + var tex: c.GLint = undefined; + c.glGetIntegerv(c.GL_TEXTURE_BINDING_2D, &tex); + + // @TODO: Viewports + // if (io.*.ConfigFlags & @enumToInt(c.ImGuiConfigFlags_ViewportsEnable) != 0) + // InitPlatformInterface(); } pub fn Shutdown() void { - // ImGui_ImplOpenGL3_ShutdownPlatformInterface(); - DestroyDeviceObjects(); + // ImGui_ImplOpenGL3_ShutdownPlatformInterface(); + DestroyDeviceObjects(); } pub fn NewFrame() !void { - if (g_ShaderHandle == 0) - try CreateDeviceObjects(); + if (g_ShaderHandle == 0) + try CreateDeviceObjects(); } fn CreateDeviceObjects() !void { - // back up GL state - var last_texture : c.GLint = undefined; - var last_array_buffer : c.GLint = undefined; - var last_vertex_array : c.GLint = undefined; - - c.glGetIntegerv(c.GL_TEXTURE_BINDING_2D, &last_texture); - defer c.glBindTexture(c.GL_TEXTURE_2D, @intCast(c.GLuint, last_texture)); - - c.glGetIntegerv(c.GL_ARRAY_BUFFER_BINDING, &last_array_buffer); - c.glBindBuffer(c.GL_ARRAY_BUFFER, @intCast(c.GLuint, last_array_buffer)); - - if (!@hasField(c, "IMGUI_IMPL_OPENGL_ES2")) - c.glGetIntegerv(c.GL_VERTEX_ARRAY_BINDING, &last_vertex_array); - defer if (!@hasField(c, "IMGUI_IMPL_OPENGL_ES2")) - c.glBindVertexArray(@intCast(c.GLuint, last_vertex_array)); - - // @TODO: GLSL versions? - const vertex_shader_glsl : [*]const c.GLchar = - c\\#version 150 - c\\uniform mat4 ProjMtx; - c\\in vec2 Position; - c\\in vec2 UV; - c\\in vec4 Color; - c\\out vec2 Frag_UV; - c\\out vec4 Frag_Color; - c\\void main() - c\\{ - c\\ Frag_UV = UV; - c\\ Frag_Color = Color; - c\\ gl_Position = ProjMtx * vec4(Position.xy, 0, 1); - c\\} - ; - - const fragment_shader_glsl : [*]const c.GLchar = - c\\#version 150 - c\\uniform sampler2D Texture; - c\\in vec2 Frag_UV; - c\\in vec4 Frag_Color; - c\\out vec4 Out_Color; - c\\void main() - c\\{ - c\\ Out_Color = Frag_Color * texture(Texture, Frag_UV.st); - c\\} - ; - - // Create shaders / programs - g_VertHandle = c.glCreateShader(c.GL_VERTEX_SHADER); - c.glShaderSource(g_VertHandle, 1, &vertex_shader_glsl, null); - c.glCompileShader(g_VertHandle); - try CheckThing(.Shader, g_VertHandle, "vertex shader"); - - g_FragHandle = c.glCreateShader(c.GL_FRAGMENT_SHADER); - c.glShaderSource(g_FragHandle, 1, &fragment_shader_glsl, null); - c.glCompileShader(g_FragHandle); - try CheckThing(.Shader, g_FragHandle, "fragment shader"); - - g_ShaderHandle = c.glCreateProgram(); - c.glAttachShader(g_ShaderHandle, g_VertHandle); - c.glAttachShader(g_ShaderHandle, g_FragHandle); - c.glLinkProgram(g_ShaderHandle); - try CheckThing(.Program, g_ShaderHandle, "shader program"); - - g_AttribLocationTex = c.glGetUniformLocation(g_ShaderHandle, c"Texture"); - g_AttribLocationProjMtx = c.glGetUniformLocation(g_ShaderHandle, c"ProjMtx"); - g_AttribLocationVtxPos = c.glGetAttribLocation(g_ShaderHandle, c"Position"); - g_AttribLocationVtxUV = c.glGetAttribLocation(g_ShaderHandle, c"UV"); - g_AttribLocationVtxColor = c.glGetAttribLocation(g_ShaderHandle, c"Color"); - - // Create buffers - c.glGenBuffers(1, &g_VboHandle); - c.glGenBuffers(1, &g_ElementsHandle); - - CreateFontsTexture(); -} + // back up GL state + var last_texture: c.GLint = undefined; + var last_array_buffer: c.GLint = undefined; + var last_vertex_array: c.GLint = undefined; + + c.glGetIntegerv(c.GL_TEXTURE_BINDING_2D, &last_texture); + defer c.glBindTexture(c.GL_TEXTURE_2D, @intCast(c.GLuint, last_texture)); + c.glGetIntegerv(c.GL_ARRAY_BUFFER_BINDING, &last_array_buffer); + c.glBindBuffer(c.GL_ARRAY_BUFFER, @intCast(c.GLuint, last_array_buffer)); + + if (!@hasField(c, "IMGUI_IMPL_OPENGL_ES2")) + c.glGetIntegerv(c.GL_VERTEX_ARRAY_BINDING, &last_vertex_array); + defer if (!@hasField(c, "IMGUI_IMPL_OPENGL_ES2")) + c.glBindVertexArray(@intCast(c.GLuint, last_vertex_array)); + + // @TODO: GLSL versions? + const vertex_shader_glsl: [*]const c.GLchar = + c\\#version 150 + c\\uniform mat4 ProjMtx; + c\\in vec2 Position; + c\\in vec2 UV; + c\\in vec4 Color; + c\\out vec2 Frag_UV; + c\\out vec4 Frag_Color; + c\\void main() + c\\{ + c\\ Frag_UV = UV; + c\\ Frag_Color = Color; + c\\ gl_Position = ProjMtx * vec4(Position.xy, 0, 1); + c\\} + ; + + const fragment_shader_glsl: [*]const c.GLchar = + c\\#version 150 + c\\uniform sampler2D Texture; + c\\in vec2 Frag_UV; + c\\in vec4 Frag_Color; + c\\out vec4 Out_Color; + c\\void main() + c\\{ + c\\ Out_Color = Frag_Color * texture(Texture, Frag_UV.st); + c\\} + ; + + // Create shaders / programs + g_VertHandle = c.glCreateShader(c.GL_VERTEX_SHADER); + c.glShaderSource(g_VertHandle, 1, &vertex_shader_glsl, null); + c.glCompileShader(g_VertHandle); + try CheckThing(.Shader, g_VertHandle, "vertex shader"); + + g_FragHandle = c.glCreateShader(c.GL_FRAGMENT_SHADER); + c.glShaderSource(g_FragHandle, 1, &fragment_shader_glsl, null); + c.glCompileShader(g_FragHandle); + try CheckThing(.Shader, g_FragHandle, "fragment shader"); + + g_ShaderHandle = c.glCreateProgram(); + c.glAttachShader(g_ShaderHandle, g_VertHandle); + c.glAttachShader(g_ShaderHandle, g_FragHandle); + c.glLinkProgram(g_ShaderHandle); + try CheckThing(.Program, g_ShaderHandle, "shader program"); + + g_AttribLocationTex = c.glGetUniformLocation(g_ShaderHandle, c"Texture"); + g_AttribLocationProjMtx = c.glGetUniformLocation(g_ShaderHandle, c"ProjMtx"); + g_AttribLocationVtxPos = c.glGetAttribLocation(g_ShaderHandle, c"Position"); + g_AttribLocationVtxUV = c.glGetAttribLocation(g_ShaderHandle, c"UV"); + g_AttribLocationVtxColor = c.glGetAttribLocation(g_ShaderHandle, c"Color"); + + // Create buffers + c.glGenBuffers(1, &g_VboHandle); + c.glGenBuffers(1, &g_ElementsHandle); + + CreateFontsTexture(); +} const CheckableThing = enum { - Shader, - Program, + Shader, + Program, }; fn CheckThing(comptime thingType: CheckableThing, handle: c.GLuint, desc: []const u8) !void { - var status : c.GLint = undefined; - var log_length : c.GLint = undefined; - const getInfoLogFunc = switch (thingType) { - .Shader => blk: { - c.glGetShaderiv(handle, c.GL_COMPILE_STATUS, &status); - c.glGetShaderiv(handle, c.GL_INFO_LOG_LENGTH, &log_length); - break :blk c.glGetShaderInfoLog; - }, - .Program => blk: { - c.glGetProgramiv(handle, c.GL_LINK_STATUS, &status); - c.glGetProgramiv(handle, c.GL_INFO_LOG_LENGTH, &log_length); - break :blk c.glGetProgramInfoLog; - }, - }; - - if (log_length > 1) - { - var buf : [1024]u8 = undefined; - var length : c.GLsizei = undefined; - getInfoLogFunc(handle, buf.len, &length, &buf[0]); - debug.warn("{}\n", buf[0..@intCast(usize, length)]); - } - - if (@intCast(c.GLboolean, status) == c.GL_FALSE) { - debug.warn("ERROR: CreateDeviceObjects: failed to compile/link {}! (with GLSL '{}')\n", desc, g_GlslVersionString); - return error.ShaderLinkError; - } + var status: c.GLint = undefined; + var log_length: c.GLint = undefined; + const getInfoLogFunc = switch (thingType) { + .Shader => blk: { + c.glGetShaderiv(handle, c.GL_COMPILE_STATUS, &status); + c.glGetShaderiv(handle, c.GL_INFO_LOG_LENGTH, &log_length); + break :blk c.glGetShaderInfoLog; + }, + .Program => blk: { + c.glGetProgramiv(handle, c.GL_LINK_STATUS, &status); + c.glGetProgramiv(handle, c.GL_INFO_LOG_LENGTH, &log_length); + break :blk c.glGetProgramInfoLog; + }, + }; + + if (log_length > 1) { + var buf: [1024]u8 = undefined; + var length: c.GLsizei = undefined; + getInfoLogFunc(handle, buf.len, &length, &buf[0]); + debug.warn("{}\n", buf[0..@intCast(usize, length)]); + } + + if (@intCast(c.GLboolean, status) == c.GL_FALSE) { + debug.warn("ERROR: CreateDeviceObjects: failed to compile/link {}! (with GLSL '{}')\n", desc, g_GlslVersionString); + return error.ShaderLinkError; + } } fn DestroyDeviceObjects() void { - if (g_VboHandle != 0) { - c.glDeleteBuffers(1, &g_VboHandle); - g_VboHandle = 0; - } - - if (g_ElementsHandle != 0) { - c.glDeleteBuffers(1, &g_ElementsHandle); - g_ElementsHandle = 0; - } - - if (g_ShaderHandle != 0 and g_VertHandle != 0) - c.glDetachShader(g_ShaderHandle, g_VertHandle); - - if (g_ShaderHandle != 0 and g_FragHandle != 0) - c.glDetachShader(g_ShaderHandle, g_FragHandle); - - if (g_VertHandle != 0) { - c.glDeleteShader(g_VertHandle); - g_VertHandle = 0; - } - - if (g_FragHandle != 0) { - c.glDeleteShader(g_FragHandle); - g_FragHandle = 0; - } - - if (g_ShaderHandle != 0) { - c.glDeleteProgram(g_ShaderHandle); - g_ShaderHandle = 0; - } - - DestroyFontsTexture(); + if (g_VboHandle != 0) { + c.glDeleteBuffers(1, &g_VboHandle); + g_VboHandle = 0; + } + + if (g_ElementsHandle != 0) { + c.glDeleteBuffers(1, &g_ElementsHandle); + g_ElementsHandle = 0; + } + + if (g_ShaderHandle != 0 and g_VertHandle != 0) + c.glDetachShader(g_ShaderHandle, g_VertHandle); + + if (g_ShaderHandle != 0 and g_FragHandle != 0) + c.glDetachShader(g_ShaderHandle, g_FragHandle); + + if (g_VertHandle != 0) { + c.glDeleteShader(g_VertHandle); + g_VertHandle = 0; + } + + if (g_FragHandle != 0) { + c.glDeleteShader(g_FragHandle); + g_FragHandle = 0; + } + + if (g_ShaderHandle != 0) { + c.glDeleteProgram(g_ShaderHandle); + g_ShaderHandle = 0; + } + + DestroyFontsTexture(); } fn CreateFontsTexture() void { - const io = c.igGetIO(); - - // Get current font image data - var width : c_int = undefined; - var height : c_int = undefined; - var pixels : [*c]u8 = undefined; - c.ImFontAtlas_GetTexDataAsRGBA32(io.*.Fonts, &pixels, &width, &height, null); - - // backup & restore state - var last_texture : c.GLint = undefined; - c.glGetIntegerv(c.GL_TEXTURE_BINDING_2D, &last_texture); - defer c.glBindTexture(c.GL_TEXTURE_2D, @intCast(c.GLuint, last_texture)); - - // Upload texture to graphics system - c.glGenTextures(1, &g_FontTexture); - c.glBindTexture(c.GL_TEXTURE_2D, g_FontTexture); - c.glTexParameteri(c.GL_TEXTURE_2D, c.GL_TEXTURE_MIN_FILTER, c.GL_LINEAR); - c.glTexParameteri(c.GL_TEXTURE_2D, c.GL_TEXTURE_MAG_FILTER, c.GL_LINEAR); - if (@hasField(c, "GL_UNPACK_ROW_LENGTH")) - c.glPixelStorei(c.GL_UNPACK_ROW_LENGTH, 0); - c.glTexImage2D(c.GL_TEXTURE_2D, 0, c.GL_RGBA, width, height, 0, c.GL_RGBA, c.GL_UNSIGNED_BYTE, pixels); - - // Store texture ID - io.*.Fonts.*.TexID = @intToPtr(c.ImTextureID, g_FontTexture); + const io = c.igGetIO(); + + // Get current font image data + var width: c_int = undefined; + var height: c_int = undefined; + var pixels: [*c]u8 = undefined; + c.ImFontAtlas_GetTexDataAsRGBA32(io.*.Fonts, &pixels, &width, &height, null); + + // backup & restore state + var last_texture: c.GLint = undefined; + c.glGetIntegerv(c.GL_TEXTURE_BINDING_2D, &last_texture); + defer c.glBindTexture(c.GL_TEXTURE_2D, @intCast(c.GLuint, last_texture)); + + // Upload texture to graphics system + c.glGenTextures(1, &g_FontTexture); + c.glBindTexture(c.GL_TEXTURE_2D, g_FontTexture); + c.glTexParameteri(c.GL_TEXTURE_2D, c.GL_TEXTURE_MIN_FILTER, c.GL_LINEAR); + c.glTexParameteri(c.GL_TEXTURE_2D, c.GL_TEXTURE_MAG_FILTER, c.GL_LINEAR); + if (@hasField(c, "GL_UNPACK_ROW_LENGTH")) + c.glPixelStorei(c.GL_UNPACK_ROW_LENGTH, 0); + c.glTexImage2D(c.GL_TEXTURE_2D, 0, c.GL_RGBA, width, height, 0, c.GL_RGBA, c.GL_UNSIGNED_BYTE, pixels); + + // Store texture ID + io.*.Fonts.*.TexID = @intToPtr(c.ImTextureID, g_FontTexture); } fn DestroyFontsTexture() void { - if (g_FontTexture == 0) - return; + if (g_FontTexture == 0) + return; - const io = c.igGetIO(); - c.glDeleteTextures(1, &g_FontTexture); - io.*.Fonts.*.TexID = @intToPtr(c.ImTextureID, 0); - g_FontTexture = 0; + const io = c.igGetIO(); + c.glDeleteTextures(1, &g_FontTexture); + io.*.Fonts.*.TexID = @intToPtr(c.ImTextureID, 0); + g_FontTexture = 0; } -pub fn RenderDrawData(draw_data : *c.ImDrawData) void { - // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates) - const fb_width = @floatToInt(i32, draw_data.*.DisplaySize.x * draw_data.*.FramebufferScale.x); - const fb_height = @floatToInt(i32, draw_data.*.DisplaySize.y * draw_data.*.FramebufferScale.y); - if (fb_width <= 0 or fb_height <= 0) - return; - - // Backup GL state - var last_program : c.GLint = undefined; - var last_texture : c.GLint = undefined; - var last_sampler : c.GLint = undefined; - var last_array_buffer : c.GLint = undefined; - var last_vertex_array_object : c.GLint = undefined; - var last_polygon_mode : [2]c.GLint = undefined; - var last_viewport : [4]c.GLint = undefined; - var last_scissor_box : [4]c.GLint = undefined; - var last_blend_src_rgb : c.GLint = undefined; - var last_blend_dst_rgb : c.GLint = undefined; - var last_blend_src_alpha : c.GLint = undefined; - var last_blend_dst_alpha : c.GLint = undefined; - var last_blend_equation_rgb : c.GLint = undefined; - var last_blend_equation_alpha : c.GLint = undefined; - var clip_origin_lower_left : bool = true; - var last_clip_origin : c.GLint = 0; - var last_active_texture : c.GLint = undefined; - - c.glGetIntegerv(c.GL_ACTIVE_TEXTURE, &last_active_texture); - c.glActiveTexture(c.GL_TEXTURE0); - c.glGetIntegerv(c.GL_CURRENT_PROGRAM, &last_program); - c.glGetIntegerv(c.GL_TEXTURE_BINDING_2D, &last_texture); - if (@hasField(c, "GL_SAMPLER_BINDING")) - c.glGetIntegerv(c.GL_SAMPLER_BINDING, &last_sampler); - c.glGetIntegerv(c.GL_ARRAY_BUFFER_BINDING, &last_array_buffer); - if (!@hasField(c, "IMGUI_IMPL_OPENc.GL_ES2")) - c.glGetIntegerv(c.GL_VERTEX_ARRAY_BINDING, &last_vertex_array_object); - if (@hasField(c, "GL_POLYGON_MODE")) - c.glGetIntegerv(c.GL_POLYGON_MODE, last_polygon_mode); - c.glGetIntegerv(c.GL_VIEWPORT, &last_viewport[0]); - c.glGetIntegerv(c.GL_SCISSOR_BOX, &last_scissor_box[0]); - c.glGetIntegerv(c.GL_BLEND_SRC_RGB, &last_blend_src_rgb); - c.glGetIntegerv(c.GL_BLEND_DST_RGB, &last_blend_dst_rgb); - c.glGetIntegerv(c.GL_BLEND_SRC_ALPHA, &last_blend_src_alpha); - c.glGetIntegerv(c.GL_BLEND_DST_ALPHA, &last_blend_dst_alpha); - c.glGetIntegerv(c.GL_BLEND_EQUATION_RGB, &last_blend_equation_rgb); - c.glGetIntegerv(c.GL_BLEND_EQUATION_ALPHA, &last_blend_equation_alpha); - var last_enable_blend : c.GLboolean = c.glIsEnabled(c.GL_BLEND); - var last_enable_cull_face : c.GLboolean = c.glIsEnabled(c.GL_CULL_FACE); - var last_enable_depth_test : c.GLboolean = c.glIsEnabled(c.GL_DEPTH_TEST); - var last_enable_scissor_test : c.GLboolean = c.glIsEnabled(c.GL_SCISSOR_TEST); - if (@hasField(c, "GL_CLIP_ORIGIN") and builtin.os != builtin.Os.osx) { - // Support for GL 4.5's glClipControl(GL_UPPER_LEFT) - c.glGetIntegerv(c.GL_CLIP_ORIGIN, &last_clip_origin); - if (last_clip_origin == c.GL_UPPER_LEFT) - clip_origin_lower_left = false; - } - - defer { - // Restore modified GL state - c.glUseProgram(@intCast(c.GLuint, last_program)); - c.glBindTexture(c.GL_TEXTURE_2D, @intCast(c.GLuint, last_texture)); +pub fn RenderDrawData(draw_data: *c.ImDrawData) void { + // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates) + const fb_width = @floatToInt(i32, draw_data.*.DisplaySize.x * draw_data.*.FramebufferScale.x); + const fb_height = @floatToInt(i32, draw_data.*.DisplaySize.y * draw_data.*.FramebufferScale.y); + if (fb_width <= 0 or fb_height <= 0) + return; + + // Backup GL state + var last_program: c.GLint = undefined; + var last_texture: c.GLint = undefined; + var last_sampler: c.GLint = undefined; + var last_array_buffer: c.GLint = undefined; + var last_vertex_array_object: c.GLint = undefined; + var last_polygon_mode: [2]c.GLint = undefined; + var last_viewport: [4]c.GLint = undefined; + var last_scissor_box: [4]c.GLint = undefined; + var last_blend_src_rgb: c.GLint = undefined; + var last_blend_dst_rgb: c.GLint = undefined; + var last_blend_src_alpha: c.GLint = undefined; + var last_blend_dst_alpha: c.GLint = undefined; + var last_blend_equation_rgb: c.GLint = undefined; + var last_blend_equation_alpha: c.GLint = undefined; + var clip_origin_lower_left: bool = true; + var last_clip_origin: c.GLint = 0; + var last_active_texture: c.GLint = undefined; + + c.glGetIntegerv(c.GL_ACTIVE_TEXTURE, &last_active_texture); + c.glActiveTexture(c.GL_TEXTURE0); + c.glGetIntegerv(c.GL_CURRENT_PROGRAM, &last_program); + c.glGetIntegerv(c.GL_TEXTURE_BINDING_2D, &last_texture); if (@hasField(c, "GL_SAMPLER_BINDING")) - c.glBindSampler(0, @intCast(c.GLuint, last_sampler)); - c.glActiveTexture(@intCast(c.GLuint, last_active_texture)); - if (!@hasField(c, "IMGUI_IMPL_OPENGL_ES2")) - c.glBindVertexArray(@intCast(c.GLuint, last_vertex_array_object)); - c.glBindBuffer(c.GL_ARRAY_BUFFER, @intCast(c.GLuint, last_array_buffer)); - c.glBlendEquationSeparate(@intCast(c.GLuint, last_blend_equation_rgb), @intCast(c.GLuint, last_blend_equation_alpha)); - c.glBlendFuncSeparate(@intCast(c.GLuint, last_blend_src_rgb), - @intCast(c.GLuint, last_blend_dst_rgb), - @intCast(c.GLuint, last_blend_src_alpha), - @intCast(c.GLuint, last_blend_dst_alpha)); - if (last_enable_blend == c.GL_TRUE) { c.glEnable(c.GL_BLEND); } - else { c.glDisable(c.GL_BLEND); } - if (last_enable_cull_face == c.GL_TRUE) { c.glEnable(c.GL_CULL_FACE); } - else { c.glDisable(c.GL_CULL_FACE); } - if (last_enable_depth_test == c.GL_TRUE) { c.glEnable(c.GL_DEPTH_TEST); } - else { c.glDisable(c.GL_DEPTH_TEST); } - if (last_enable_scissor_test == c.GL_TRUE) { c.glEnable(c.GL_SCISSOR_TEST); } - else { c.glDisable(c.GL_SCISSOR_TEST); } + c.glGetIntegerv(c.GL_SAMPLER_BINDING, &last_sampler); + c.glGetIntegerv(c.GL_ARRAY_BUFFER_BINDING, &last_array_buffer); + if (!@hasField(c, "IMGUI_IMPL_OPENc.GL_ES2")) + c.glGetIntegerv(c.GL_VERTEX_ARRAY_BINDING, &last_vertex_array_object); if (@hasField(c, "GL_POLYGON_MODE")) - c.glPolygonMode(c.GL_FRONT_AND_BACK, @intCast(c.GLenum, last_polygon_mode[0])); - c.glViewport(last_viewport[0], last_viewport[1], @intCast(c.GLsizei, last_viewport[2]), @intCast(c.GLsizei, last_viewport[3])); - c.glScissor(last_scissor_box[0], last_scissor_box[1], @intCast(c.GLsizei, last_scissor_box[2]), @intCast(c.GLsizei, last_scissor_box[3])); - } - - // Setup desired GL state - // Recreate the VAO every time (this is to easily allow multiple GL contexts to be rendered to. VAO are not shared among GL contexts) - // The renderer would actually work without any VAO bound, but then our VertexAttrib calls would overwrite the default one currently bound. - var vertex_array_object : c.GLuint = 0; - if (!@hasField(c, "IMGUI_IMPL_OPENGL_ES2")) - c.glGenVertexArrays(1, &vertex_array_object); - defer if (!@hasField(c, "IMGUI_IMPL_OPENGL_ES2")) - c.glDeleteVertexArrays(1, &vertex_array_object); - - SetupRenderState(draw_data, fb_width, fb_height, vertex_array_object); - - // Will project scissor/clipping rectangles into framebuffer space - const clip_off = draw_data.*.DisplayPos; // (0,0) unless using multi-viewports - const clip_scale = draw_data.*.FramebufferScale; // (1,1) unless using retina display which are often (2,2) - - // Render command lists - var n : usize = 0; - while (n < @intCast(usize, draw_data.*.CmdListsCount)) : (n += 1) { - const cmd_list = draw_data.*.CmdLists[n]; - - // Upload vertex/index buffers - c.glBufferData(c.GL_ARRAY_BUFFER, cmd_list.*.VtxBuffer.Size * @sizeOf(c.ImDrawVert), @ptrCast(?*c.GLvoid, cmd_list.*.VtxBuffer.Data), c.GL_STREAM_DRAW); - c.glBufferData(c.GL_ELEMENT_ARRAY_BUFFER, cmd_list.*.IdxBuffer.Size * @sizeOf(c.ImDrawIdx), @ptrCast(*c.GLvoid, cmd_list.*.IdxBuffer.Data), c.GL_STREAM_DRAW); - - var cmd_i : usize = 0; - while (cmd_i < @intCast(usize, cmd_list.*.CmdBuffer.Size)) : (cmd_i += 1) { - const pcmd = &cmd_list.*.CmdBuffer.Data[cmd_i]; - - if (pcmd.*.UserCallback != null) { - // User callback, registered via ImDrawList::AddCallback() - // (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.) - const ImDrawCallback_ResetRenderState = @intToPtr(c.ImDrawCallback, math.maxInt(usize)); - if (pcmd.*.UserCallback == ImDrawCallback_ResetRenderState) { - SetupRenderState(draw_data, fb_width, fb_height, vertex_array_object); + c.glGetIntegerv(c.GL_POLYGON_MODE, last_polygon_mode); + c.glGetIntegerv(c.GL_VIEWPORT, &last_viewport[0]); + c.glGetIntegerv(c.GL_SCISSOR_BOX, &last_scissor_box[0]); + c.glGetIntegerv(c.GL_BLEND_SRC_RGB, &last_blend_src_rgb); + c.glGetIntegerv(c.GL_BLEND_DST_RGB, &last_blend_dst_rgb); + c.glGetIntegerv(c.GL_BLEND_SRC_ALPHA, &last_blend_src_alpha); + c.glGetIntegerv(c.GL_BLEND_DST_ALPHA, &last_blend_dst_alpha); + c.glGetIntegerv(c.GL_BLEND_EQUATION_RGB, &last_blend_equation_rgb); + c.glGetIntegerv(c.GL_BLEND_EQUATION_ALPHA, &last_blend_equation_alpha); + var last_enable_blend: c.GLboolean = c.glIsEnabled(c.GL_BLEND); + var last_enable_cull_face: c.GLboolean = c.glIsEnabled(c.GL_CULL_FACE); + var last_enable_depth_test: c.GLboolean = c.glIsEnabled(c.GL_DEPTH_TEST); + var last_enable_scissor_test: c.GLboolean = c.glIsEnabled(c.GL_SCISSOR_TEST); + if (@hasField(c, "GL_CLIP_ORIGIN") and builtin.os != builtin.Os.osx) { + // Support for GL 4.5's glClipControl(GL_UPPER_LEFT) + c.glGetIntegerv(c.GL_CLIP_ORIGIN, &last_clip_origin); + if (last_clip_origin == c.GL_UPPER_LEFT) + clip_origin_lower_left = false; + } + + defer { + // Restore modified GL state + c.glUseProgram(@intCast(c.GLuint, last_program)); + c.glBindTexture(c.GL_TEXTURE_2D, @intCast(c.GLuint, last_texture)); + if (@hasField(c, "GL_SAMPLER_BINDING")) + c.glBindSampler(0, @intCast(c.GLuint, last_sampler)); + c.glActiveTexture(@intCast(c.GLuint, last_active_texture)); + if (!@hasField(c, "IMGUI_IMPL_OPENGL_ES2")) + c.glBindVertexArray(@intCast(c.GLuint, last_vertex_array_object)); + c.glBindBuffer(c.GL_ARRAY_BUFFER, @intCast(c.GLuint, last_array_buffer)); + c.glBlendEquationSeparate(@intCast(c.GLuint, last_blend_equation_rgb), @intCast(c.GLuint, last_blend_equation_alpha)); + c.glBlendFuncSeparate(@intCast(c.GLuint, last_blend_src_rgb), @intCast(c.GLuint, last_blend_dst_rgb), @intCast(c.GLuint, last_blend_src_alpha), @intCast(c.GLuint, last_blend_dst_alpha)); + if (last_enable_blend == c.GL_TRUE) { + c.glEnable(c.GL_BLEND); } else { - if (pcmd.*.UserCallback) |callback| { - callback(cmd_list, pcmd); - } else { - unreachable; - } + c.glDisable(c.GL_BLEND); } - } else { - // Project scissor/clipping rectangles into framebuffer space - var clip_rect : c.ImVec4 = undefined; - clip_rect.x = (pcmd.*.ClipRect.x - clip_off.x) * clip_scale.x; - clip_rect.y = (pcmd.*.ClipRect.y - clip_off.y) * clip_scale.y; - clip_rect.z = (pcmd.*.ClipRect.z - clip_off.x) * clip_scale.x; - clip_rect.w = (pcmd.*.ClipRect.w - clip_off.y) * clip_scale.y; - - if (clip_rect.x < @intToFloat(f32, fb_width) and clip_rect.y < @intToFloat(f32, fb_height) - and clip_rect.z >= 0.0 and clip_rect.w >= 0.0) { - // Apply scissor/clipping rectangle - if (clip_origin_lower_left) { - c.glScissor(@floatToInt(c.GLint, clip_rect.x), fb_height - @floatToInt(c.GLint, clip_rect.w), - @floatToInt(c.GLint, clip_rect.z - clip_rect.x), @floatToInt(c.GLint, clip_rect.w - clip_rect.y)); - } else { - // Support for GL 4.5 rarely used glClipControl(GL_UPPER_LEFT) - c.glScissor(@floatToInt(c.GLint, clip_rect.x), @floatToInt(c.GLint, clip_rect.y), - @floatToInt(c.GLint, clip_rect.z), @floatToInt(c.GLint, clip_rect.w)); - } - - // Bind texture, Draw - c.glBindTexture(c.GL_TEXTURE_2D, @intCast(c.GLuint, @ptrToInt(pcmd.*.TextureId))); - const drawIndexSize = @sizeOf(c.ImDrawIdx); - const drawIndexType = if (drawIndexSize == 2) c.GL_UNSIGNED_SHORT else c.GL_UNSIGNED_INT; - const offset = @intToPtr(?*c.GLvoid, pcmd.*.IdxOffset * drawIndexSize); - if (@hasField(c, "IMGUI_IMPL_OPENGL_HAS_DRAW_WITH_BASE_VERTEX")) { - c.glDrawElementsBaseVertex(c.GL_TRIANGLES, @intCast(c.GLsizei, pcmd.*.ElemCount), drawIndexType, offset, @intCast(c.GLint, pcmd.*.VtxOffset)); - } else { - c.glDrawElements(c.GL_TRIANGLES, @intCast(c.GLsizei, pcmd.*.ElemCount), drawIndexType, offset); - } + if (last_enable_cull_face == c.GL_TRUE) { + c.glEnable(c.GL_CULL_FACE); + } else { + c.glDisable(c.GL_CULL_FACE); + } + if (last_enable_depth_test == c.GL_TRUE) { + c.glEnable(c.GL_DEPTH_TEST); + } else { + c.glDisable(c.GL_DEPTH_TEST); + } + if (last_enable_scissor_test == c.GL_TRUE) { + c.glEnable(c.GL_SCISSOR_TEST); + } else { + c.glDisable(c.GL_SCISSOR_TEST); + } + if (@hasField(c, "GL_POLYGON_MODE")) + c.glPolygonMode(c.GL_FRONT_AND_BACK, @intCast(c.GLenum, last_polygon_mode[0])); + c.glViewport(last_viewport[0], last_viewport[1], @intCast(c.GLsizei, last_viewport[2]), @intCast(c.GLsizei, last_viewport[3])); + c.glScissor(last_scissor_box[0], last_scissor_box[1], @intCast(c.GLsizei, last_scissor_box[2]), @intCast(c.GLsizei, last_scissor_box[3])); + } + + // Setup desired GL state + // Recreate the VAO every time (this is to easily allow multiple GL contexts to be rendered to. VAO are not shared among GL contexts) + // The renderer would actually work without any VAO bound, but then our VertexAttrib calls would overwrite the default one currently bound. + var vertex_array_object: c.GLuint = 0; + if (!@hasField(c, "IMGUI_IMPL_OPENGL_ES2")) + c.glGenVertexArrays(1, &vertex_array_object); + defer if (!@hasField(c, "IMGUI_IMPL_OPENGL_ES2")) + c.glDeleteVertexArrays(1, &vertex_array_object); + + SetupRenderState(draw_data, fb_width, fb_height, vertex_array_object); + + // Will project scissor/clipping rectangles into framebuffer space + const clip_off = draw_data.*.DisplayPos; // (0,0) unless using multi-viewports + const clip_scale = draw_data.*.FramebufferScale; // (1,1) unless using retina display which are often (2,2) + + // Render command lists + var n: usize = 0; + while (n < @intCast(usize, draw_data.*.CmdListsCount)) : (n += 1) { + const cmd_list = draw_data.*.CmdLists[n]; + + // Upload vertex/index buffers + c.glBufferData(c.GL_ARRAY_BUFFER, cmd_list.*.VtxBuffer.Size * @sizeOf(c.ImDrawVert), @ptrCast(?*c.GLvoid, cmd_list.*.VtxBuffer.Data), c.GL_STREAM_DRAW); + c.glBufferData(c.GL_ELEMENT_ARRAY_BUFFER, cmd_list.*.IdxBuffer.Size * @sizeOf(c.ImDrawIdx), @ptrCast(*c.GLvoid, cmd_list.*.IdxBuffer.Data), c.GL_STREAM_DRAW); + + var cmd_i: usize = 0; + while (cmd_i < @intCast(usize, cmd_list.*.CmdBuffer.Size)) : (cmd_i += 1) { + const pcmd = &cmd_list.*.CmdBuffer.Data[cmd_i]; + + if (pcmd.*.UserCallback != null) { + // User callback, registered via ImDrawList::AddCallback() + // (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.) + const ImDrawCallback_ResetRenderState = @intToPtr(c.ImDrawCallback, math.maxInt(usize)); + if (pcmd.*.UserCallback == ImDrawCallback_ResetRenderState) { + SetupRenderState(draw_data, fb_width, fb_height, vertex_array_object); + } else { + if (pcmd.*.UserCallback) |callback| { + callback(cmd_list, pcmd); + } else { + unreachable; + } + } + } else { + // Project scissor/clipping rectangles into framebuffer space + var clip_rect: c.ImVec4 = undefined; + clip_rect.x = (pcmd.*.ClipRect.x - clip_off.x) * clip_scale.x; + clip_rect.y = (pcmd.*.ClipRect.y - clip_off.y) * clip_scale.y; + clip_rect.z = (pcmd.*.ClipRect.z - clip_off.x) * clip_scale.x; + clip_rect.w = (pcmd.*.ClipRect.w - clip_off.y) * clip_scale.y; + + if (clip_rect.x < @intToFloat(f32, fb_width) and clip_rect.y < @intToFloat(f32, fb_height) and clip_rect.z >= 0.0 and clip_rect.w >= 0.0) { + // Apply scissor/clipping rectangle + if (clip_origin_lower_left) { + c.glScissor(@floatToInt(c.GLint, clip_rect.x), fb_height - @floatToInt(c.GLint, clip_rect.w), @floatToInt(c.GLint, clip_rect.z - clip_rect.x), @floatToInt(c.GLint, clip_rect.w - clip_rect.y)); + } else { + // Support for GL 4.5 rarely used glClipControl(GL_UPPER_LEFT) + c.glScissor(@floatToInt(c.GLint, clip_rect.x), @floatToInt(c.GLint, clip_rect.y), @floatToInt(c.GLint, clip_rect.z), @floatToInt(c.GLint, clip_rect.w)); + } + + // Bind texture, Draw + c.glBindTexture(c.GL_TEXTURE_2D, @intCast(c.GLuint, @ptrToInt(pcmd.*.TextureId))); + const drawIndexSize = @sizeOf(c.ImDrawIdx); + const drawIndexType = if (drawIndexSize == 2) c.GL_UNSIGNED_SHORT else c.GL_UNSIGNED_INT; + const offset = @intToPtr(?*c.GLvoid, pcmd.*.IdxOffset * drawIndexSize); + if (@hasField(c, "IMGUI_IMPL_OPENGL_HAS_DRAW_WITH_BASE_VERTEX")) { + c.glDrawElementsBaseVertex(c.GL_TRIANGLES, @intCast(c.GLsizei, pcmd.*.ElemCount), drawIndexType, offset, @intCast(c.GLint, pcmd.*.VtxOffset)); + } else { + c.glDrawElements(c.GL_TRIANGLES, @intCast(c.GLsizei, pcmd.*.ElemCount), drawIndexType, offset); + } + } + } } - } } - } } fn SetupRenderState(draw_data: *c.ImDrawData, fb_width: i32, fb_height: i32, vertex_array_object: c.GLuint) void { - // Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, polygon fill - c.glEnable(c.GL_BLEND); - c.glBlendEquation(c.GL_FUNC_ADD); - c.glBlendFunc(c.GL_SRC_ALPHA, c.GL_ONE_MINUS_SRC_ALPHA); - c.glDisable(c.GL_CULL_FACE); - c.glDisable(c.GL_DEPTH_TEST); - c.glEnable(c.GL_SCISSOR_TEST); - if (@hasField(c, "GL_POLYGON_MODE")) - c.glPolygonMode(c.GL_FRONT_AND_BACK, c.GL_FILL); - - // Setup viewport, orthographic projection matrix - // Our visible imgui space lies from draw_data.*.DisplayPos (top left) to draw_data.*.DisplayPos+data_data.*.DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps. - c.glViewport(0, 0, @intCast(c.GLsizei, fb_width), @intCast(c.GLsizei, fb_height)); - const L = draw_data.*.DisplayPos.x; - const R = draw_data.*.DisplayPos.x + draw_data.*.DisplaySize.x; - const T = draw_data.*.DisplayPos.y; - const B = draw_data.*.DisplayPos.y + draw_data.*.DisplaySize.y; - const ortho_projection = [4][4]f32{ - [_]f32{ 2.0/(R-L), 0.0, 0.0, 0.0 }, - [_]f32{ 0.0, 2.0/(T-B), 0.0, 0.0 }, - [_]f32{ 0.0, 0.0, -1.0, 0.0 }, - [_]f32{ (R+L)/(L-R), (T+B)/(B-T), 0.0, 1.0 }, - }; - c.glUseProgram(g_ShaderHandle); - c.glUniform1i(g_AttribLocationTex, 0); - c.glUniformMatrix4fv(g_AttribLocationProjMtx, 1, c.GL_FALSE, &ortho_projection[0][0]); - if (@hasField(c, "GL_SAMPLER_BINDING")) - c.glBindSampler(0, 0); // We use combined texture/sampler state. Applications using GL 3.3 may set that otherwise. - - if (!@hasField(c, "IMGUI_IMPL_OPENGL_ES2")) - c.glBindVertexArray(vertex_array_object); - - // Bind vertex/index buffers and setup attributes for ImDrawVert - c.glBindBuffer(c.GL_ARRAY_BUFFER, g_VboHandle); - c.glBindBuffer(c.GL_ELEMENT_ARRAY_BUFFER, g_ElementsHandle); - c.glEnableVertexAttribArray(@intCast(c.GLuint, g_AttribLocationVtxPos)); - c.glEnableVertexAttribArray(@intCast(c.GLuint, g_AttribLocationVtxUV)); - c.glEnableVertexAttribArray(@intCast(c.GLuint, g_AttribLocationVtxColor)); - c.glVertexAttribPointer(@intCast(c.GLuint, g_AttribLocationVtxPos), 2, c.GL_FLOAT, c.GL_FALSE, @sizeOf(c.ImDrawVert), @intToPtr(?*c.GLvoid, @byteOffsetOf(c.ImDrawVert, "pos"))); - c.glVertexAttribPointer(@intCast(c.GLuint, g_AttribLocationVtxUV), 2, c.GL_FLOAT, c.GL_FALSE, @sizeOf(c.ImDrawVert), @intToPtr(?*c.GLvoid, @byteOffsetOf(c.ImDrawVert, "uv"))); - c.glVertexAttribPointer(@intCast(c.GLuint, g_AttribLocationVtxColor), 4, c.GL_UNSIGNED_BYTE, c.GL_TRUE, @sizeOf(c.ImDrawVert), @intToPtr(?*c.GLvoid, @byteOffsetOf(c.ImDrawVert, "col"))); + // Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, polygon fill + c.glEnable(c.GL_BLEND); + c.glBlendEquation(c.GL_FUNC_ADD); + c.glBlendFunc(c.GL_SRC_ALPHA, c.GL_ONE_MINUS_SRC_ALPHA); + c.glDisable(c.GL_CULL_FACE); + c.glDisable(c.GL_DEPTH_TEST); + c.glEnable(c.GL_SCISSOR_TEST); + if (@hasField(c, "GL_POLYGON_MODE")) + c.glPolygonMode(c.GL_FRONT_AND_BACK, c.GL_FILL); + + // Setup viewport, orthographic projection matrix + // Our visible imgui space lies from draw_data.*.DisplayPos (top left) to draw_data.*.DisplayPos+data_data.*.DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps. + c.glViewport(0, 0, @intCast(c.GLsizei, fb_width), @intCast(c.GLsizei, fb_height)); + const L = draw_data.*.DisplayPos.x; + const R = draw_data.*.DisplayPos.x + draw_data.*.DisplaySize.x; + const T = draw_data.*.DisplayPos.y; + const B = draw_data.*.DisplayPos.y + draw_data.*.DisplaySize.y; + const ortho_projection = [4][4]f32{ + [_]f32{ 2.0 / (R - L), 0.0, 0.0, 0.0 }, + [_]f32{ 0.0, 2.0 / (T - B), 0.0, 0.0 }, + [_]f32{ 0.0, 0.0, -1.0, 0.0 }, + [_]f32{ (R + L) / (L - R), (T + B) / (B - T), 0.0, 1.0 }, + }; + c.glUseProgram(g_ShaderHandle); + c.glUniform1i(g_AttribLocationTex, 0); + c.glUniformMatrix4fv(g_AttribLocationProjMtx, 1, c.GL_FALSE, &ortho_projection[0][0]); + if (@hasField(c, "GL_SAMPLER_BINDING")) + c.glBindSampler(0, 0); // We use combined texture/sampler state. Applications using GL 3.3 may set that otherwise. + + if (!@hasField(c, "IMGUI_IMPL_OPENGL_ES2")) + c.glBindVertexArray(vertex_array_object); + + // Bind vertex/index buffers and setup attributes for ImDrawVert + c.glBindBuffer(c.GL_ARRAY_BUFFER, g_VboHandle); + c.glBindBuffer(c.GL_ELEMENT_ARRAY_BUFFER, g_ElementsHandle); + c.glEnableVertexAttribArray(@intCast(c.GLuint, g_AttribLocationVtxPos)); + c.glEnableVertexAttribArray(@intCast(c.GLuint, g_AttribLocationVtxUV)); + c.glEnableVertexAttribArray(@intCast(c.GLuint, g_AttribLocationVtxColor)); + c.glVertexAttribPointer(@intCast(c.GLuint, g_AttribLocationVtxPos), 2, c.GL_FLOAT, c.GL_FALSE, @sizeOf(c.ImDrawVert), @intToPtr(?*c.GLvoid, @byteOffsetOf(c.ImDrawVert, "pos"))); + c.glVertexAttribPointer(@intCast(c.GLuint, g_AttribLocationVtxUV), 2, c.GL_FLOAT, c.GL_FALSE, @sizeOf(c.ImDrawVert), @intToPtr(?*c.GLvoid, @byteOffsetOf(c.ImDrawVert, "uv"))); + c.glVertexAttribPointer(@intCast(c.GLuint, g_AttribLocationVtxColor), 4, c.GL_UNSIGNED_BYTE, c.GL_TRUE, @sizeOf(c.ImDrawVert), @intToPtr(?*c.GLvoid, @byteOffsetOf(c.ImDrawVert, "col"))); } diff --git a/src/glfw_impl.zig b/src/glfw_impl.zig index dffd7bf..d2defa5 100644 --- a/src/glfw_impl.zig +++ b/src/glfw_impl.zig @@ -1,20 +1,21 @@ +/// this is a port of cimgui/imgui/examples/imgui_impl_glfw.cpp const c = @import("c.zig"); const builtin = @import("builtin"); const debug = @import("std").debug; const math = @import("std").math; pub const ClientApi = enum { - Unknown, - OpenGL, - Vulkan, + Unknown, + OpenGL, + Vulkan, }; // Data var g_Window: ?*c.GLFWwindow = null; var g_ClientApi: ClientApi = .Unknown; var g_Time: f64 = 0.0; -var g_MouseJustPressed = [_]bool{ false } ** 5; -var g_MouseCursors = [_]?*c.GLFWcursor{ null } ** @enumToInt(c.ImGuiMouseCursor_COUNT); +var g_MouseJustPressed = [_]bool{false} ** 5; +var g_MouseCursors = [_]?*c.GLFWcursor{null} ** @enumToInt(c.ImGuiMouseCursor_COUNT); var g_WantUpdateMonitors = true; // Chain GLFW callbacks for main viewport: @@ -25,290 +26,286 @@ var g_PrevUserCallbackKey: c.GLFWkeyfun = null; var g_PrevUserCallbackChar: c.GLFWcharfun = null; pub fn Init(window: *c.GLFWwindow, install_callbacks: bool, client_api: ClientApi) void { - g_Window = window; - g_Time = 0.0; - - // Setup back-end capabilities flags - const io = c.igGetIO(); - io.*.BackendFlags |= @enumToInt(c.ImGuiBackendFlags_HasMouseCursors); // We can honor GetMouseCursor() values (optional) - io.*.BackendFlags |= @enumToInt(c.ImGuiBackendFlags_HasSetMousePos); // We can honor io.WantSetMousePos requests (optional, rarely used) - if (false) io.*.BackendFlags |= @enumToInt(c.ImGuiBackendFlags_PlatformHasViewports); // We can create multi-viewports on the Platform side (optional) - if (false and @hasField(c, "GLFW_HAS_GLFW_HOVERED") and builtin.os == builtin.Os.windows) { - io.*.BackendFlags |= @enumToInt(ImGuiBackendFlags_HasMouseHoveredViewport); // We can set io.MouseHoveredViewport correctly (optional, not easy) - } - io.*.BackendPlatformName = c"imgui_impl_glfw.zig"; - - // Keyboard mapping. ImGui will use those indices to peek into the io.KeysDown[] array. - io.*.KeyMap[@enumToInt(c.ImGuiKey_Tab)] = c.GLFW_KEY_TAB; - io.*.KeyMap[@enumToInt(c.ImGuiKey_LeftArrow)] = c.GLFW_KEY_LEFT; - io.*.KeyMap[@enumToInt(c.ImGuiKey_RightArrow)] = c.GLFW_KEY_RIGHT; - io.*.KeyMap[@enumToInt(c.ImGuiKey_UpArrow)] = c.GLFW_KEY_UP; - io.*.KeyMap[@enumToInt(c.ImGuiKey_DownArrow)] = c.GLFW_KEY_DOWN; - io.*.KeyMap[@enumToInt(c.ImGuiKey_PageUp)] = c.GLFW_KEY_PAGE_UP; - io.*.KeyMap[@enumToInt(c.ImGuiKey_PageDown)] = c.GLFW_KEY_PAGE_DOWN; - io.*.KeyMap[@enumToInt(c.ImGuiKey_Home)] = c.GLFW_KEY_HOME; - io.*.KeyMap[@enumToInt(c.ImGuiKey_End)] = c.GLFW_KEY_END; - io.*.KeyMap[@enumToInt(c.ImGuiKey_Insert)] = c.GLFW_KEY_INSERT; - io.*.KeyMap[@enumToInt(c.ImGuiKey_Delete)] = c.GLFW_KEY_DELETE; - io.*.KeyMap[@enumToInt(c.ImGuiKey_Backspace)] = c.GLFW_KEY_BACKSPACE; - io.*.KeyMap[@enumToInt(c.ImGuiKey_Space)] = c.GLFW_KEY_SPACE; - io.*.KeyMap[@enumToInt(c.ImGuiKey_Enter)] = c.GLFW_KEY_ENTER; - io.*.KeyMap[@enumToInt(c.ImGuiKey_Escape)] = c.GLFW_KEY_ESCAPE; - io.*.KeyMap[@enumToInt(c.ImGuiKey_KeyPadEnter)] = c.GLFW_KEY_KP_ENTER; - io.*.KeyMap[@enumToInt(c.ImGuiKey_A)] = c.GLFW_KEY_A; - io.*.KeyMap[@enumToInt(c.ImGuiKey_C)] = c.GLFW_KEY_C; - io.*.KeyMap[@enumToInt(c.ImGuiKey_V)] = c.GLFW_KEY_V; - io.*.KeyMap[@enumToInt(c.ImGuiKey_X)] = c.GLFW_KEY_X; - io.*.KeyMap[@enumToInt(c.ImGuiKey_Y)] = c.GLFW_KEY_Y; - io.*.KeyMap[@enumToInt(c.ImGuiKey_Z)] = c.GLFW_KEY_Z; - - // @TODO: Clipboard - // io.SetClipboardTextFn = ImGui_ImplGlfw_SetClipboardText; - // io.GetClipboardTextFn = ImGui_ImplGlfw_GetClipboardText; - io.*.ClipboardUserData = g_Window; - - g_MouseCursors[@enumToInt(c.ImGuiMouseCursor_Arrow)] = c.glfwCreateStandardCursor(c.GLFW_ARROW_CURSOR); - g_MouseCursors[@enumToInt(c.ImGuiMouseCursor_TextInput)] = c.glfwCreateStandardCursor(c.GLFW_IBEAM_CURSOR); - g_MouseCursors[@enumToInt(c.ImGuiMouseCursor_ResizeAll)] = c.glfwCreateStandardCursor(c.GLFW_ARROW_CURSOR); // FIXME: GLFW doesn't have this. - g_MouseCursors[@enumToInt(c.ImGuiMouseCursor_ResizeNS)] = c.glfwCreateStandardCursor(c.GLFW_VRESIZE_CURSOR); - g_MouseCursors[@enumToInt(c.ImGuiMouseCursor_ResizeEW)] = c.glfwCreateStandardCursor(c.GLFW_HRESIZE_CURSOR); - g_MouseCursors[@enumToInt(c.ImGuiMouseCursor_ResizeNESW)] = c.glfwCreateStandardCursor(c.GLFW_ARROW_CURSOR); // FIXME: GLFW doesn't have this. - g_MouseCursors[@enumToInt(c.ImGuiMouseCursor_ResizeNWSE)] = c.glfwCreateStandardCursor(c.GLFW_ARROW_CURSOR); // FIXME: GLFW doesn't have this. - g_MouseCursors[@enumToInt(c.ImGuiMouseCursor_Hand)] = c.glfwCreateStandardCursor(c.GLFW_HAND_CURSOR); - - // Chain GLFW callbacks: our callbacks will call the user's previously installed callbacks, if any. - g_PrevUserCallbackMousebutton = null; - g_PrevUserCallbackScroll = null; - g_PrevUserCallbackKey = null; - g_PrevUserCallbackChar = null; - if (install_callbacks) { - g_PrevUserCallbackMousebutton = c.glfwSetMouseButtonCallback(window, Callback_MouseButton); - g_PrevUserCallbackScroll = c.glfwSetScrollCallback(window, Callback_Scroll); - g_PrevUserCallbackKey = c.glfwSetKeyCallback(window, Callback_Key); - g_PrevUserCallbackChar = c.glfwSetCharCallback(window, Callback_Char); - } - - // Our mouse update function expect PlatformHandle to be filled for the main viewport - const main_viewport = c.igGetMainViewport(); - main_viewport.*.PlatformHandle = g_Window; - if (builtin.os == builtin.Os.windows) - main_viewport.*.PlatformHandleRaw = c.glfwGetWin32Window(g_Window); - - // @TODO: Platform Interface (Viewport) - if (io.*.ConfigFlags & @enumToInt(c.ImGuiConfigFlags_ViewportsEnable) != 0) - unreachable; + g_Window = window; + g_Time = 0.0; + + // Setup back-end capabilities flags + const io = c.igGetIO(); + io.*.BackendFlags |= @enumToInt(c.ImGuiBackendFlags_HasMouseCursors); // We can honor GetMouseCursor() values (optional) + io.*.BackendFlags |= @enumToInt(c.ImGuiBackendFlags_HasSetMousePos); // We can honor io.WantSetMousePos requests (optional, rarely used) + if (false) io.*.BackendFlags |= @enumToInt(c.ImGuiBackendFlags_PlatformHasViewports); // We can create multi-viewports on the Platform side (optional) + if (false and @hasField(c, "GLFW_HAS_GLFW_HOVERED") and builtin.os == builtin.Os.windows) { + io.*.BackendFlags |= @enumToInt(ImGuiBackendFlags_HasMouseHoveredViewport); // We can set io.MouseHoveredViewport correctly (optional, not easy) + } + io.*.BackendPlatformName = c"imgui_impl_glfw.zig"; + + // Keyboard mapping. ImGui will use those indices to peek into the io.KeysDown[] array. + io.*.KeyMap[@enumToInt(c.ImGuiKey_Tab)] = c.GLFW_KEY_TAB; + io.*.KeyMap[@enumToInt(c.ImGuiKey_LeftArrow)] = c.GLFW_KEY_LEFT; + io.*.KeyMap[@enumToInt(c.ImGuiKey_RightArrow)] = c.GLFW_KEY_RIGHT; + io.*.KeyMap[@enumToInt(c.ImGuiKey_UpArrow)] = c.GLFW_KEY_UP; + io.*.KeyMap[@enumToInt(c.ImGuiKey_DownArrow)] = c.GLFW_KEY_DOWN; + io.*.KeyMap[@enumToInt(c.ImGuiKey_PageUp)] = c.GLFW_KEY_PAGE_UP; + io.*.KeyMap[@enumToInt(c.ImGuiKey_PageDown)] = c.GLFW_KEY_PAGE_DOWN; + io.*.KeyMap[@enumToInt(c.ImGuiKey_Home)] = c.GLFW_KEY_HOME; + io.*.KeyMap[@enumToInt(c.ImGuiKey_End)] = c.GLFW_KEY_END; + io.*.KeyMap[@enumToInt(c.ImGuiKey_Insert)] = c.GLFW_KEY_INSERT; + io.*.KeyMap[@enumToInt(c.ImGuiKey_Delete)] = c.GLFW_KEY_DELETE; + io.*.KeyMap[@enumToInt(c.ImGuiKey_Backspace)] = c.GLFW_KEY_BACKSPACE; + io.*.KeyMap[@enumToInt(c.ImGuiKey_Space)] = c.GLFW_KEY_SPACE; + io.*.KeyMap[@enumToInt(c.ImGuiKey_Enter)] = c.GLFW_KEY_ENTER; + io.*.KeyMap[@enumToInt(c.ImGuiKey_Escape)] = c.GLFW_KEY_ESCAPE; + io.*.KeyMap[@enumToInt(c.ImGuiKey_KeyPadEnter)] = c.GLFW_KEY_KP_ENTER; + io.*.KeyMap[@enumToInt(c.ImGuiKey_A)] = c.GLFW_KEY_A; + io.*.KeyMap[@enumToInt(c.ImGuiKey_C)] = c.GLFW_KEY_C; + io.*.KeyMap[@enumToInt(c.ImGuiKey_V)] = c.GLFW_KEY_V; + io.*.KeyMap[@enumToInt(c.ImGuiKey_X)] = c.GLFW_KEY_X; + io.*.KeyMap[@enumToInt(c.ImGuiKey_Y)] = c.GLFW_KEY_Y; + io.*.KeyMap[@enumToInt(c.ImGuiKey_Z)] = c.GLFW_KEY_Z; + + // @TODO: Clipboard + // io.SetClipboardTextFn = ImGui_ImplGlfw_SetClipboardText; + // io.GetClipboardTextFn = ImGui_ImplGlfw_GetClipboardText; + io.*.ClipboardUserData = g_Window; + + g_MouseCursors[@enumToInt(c.ImGuiMouseCursor_Arrow)] = c.glfwCreateStandardCursor(c.GLFW_ARROW_CURSOR); + g_MouseCursors[@enumToInt(c.ImGuiMouseCursor_TextInput)] = c.glfwCreateStandardCursor(c.GLFW_IBEAM_CURSOR); + g_MouseCursors[@enumToInt(c.ImGuiMouseCursor_ResizeAll)] = c.glfwCreateStandardCursor(c.GLFW_ARROW_CURSOR); // FIXME: GLFW doesn't have this. + g_MouseCursors[@enumToInt(c.ImGuiMouseCursor_ResizeNS)] = c.glfwCreateStandardCursor(c.GLFW_VRESIZE_CURSOR); + g_MouseCursors[@enumToInt(c.ImGuiMouseCursor_ResizeEW)] = c.glfwCreateStandardCursor(c.GLFW_HRESIZE_CURSOR); + g_MouseCursors[@enumToInt(c.ImGuiMouseCursor_ResizeNESW)] = c.glfwCreateStandardCursor(c.GLFW_ARROW_CURSOR); // FIXME: GLFW doesn't have this. + g_MouseCursors[@enumToInt(c.ImGuiMouseCursor_ResizeNWSE)] = c.glfwCreateStandardCursor(c.GLFW_ARROW_CURSOR); // FIXME: GLFW doesn't have this. + g_MouseCursors[@enumToInt(c.ImGuiMouseCursor_Hand)] = c.glfwCreateStandardCursor(c.GLFW_HAND_CURSOR); + + // Chain GLFW callbacks: our callbacks will call the user's previously installed callbacks, if any. + g_PrevUserCallbackMousebutton = null; + g_PrevUserCallbackScroll = null; + g_PrevUserCallbackKey = null; + g_PrevUserCallbackChar = null; + if (install_callbacks) { + g_PrevUserCallbackMousebutton = c.glfwSetMouseButtonCallback(window, Callback_MouseButton); + g_PrevUserCallbackScroll = c.glfwSetScrollCallback(window, Callback_Scroll); + g_PrevUserCallbackKey = c.glfwSetKeyCallback(window, Callback_Key); + g_PrevUserCallbackChar = c.glfwSetCharCallback(window, Callback_Char); + } + + // Our mouse update function expect PlatformHandle to be filled for the main viewport + const main_viewport = c.igGetMainViewport(); + main_viewport.*.PlatformHandle = g_Window; + if (builtin.os == builtin.Os.windows) + main_viewport.*.PlatformHandleRaw = c.glfwGetWin32Window(g_Window); + + // @TODO: Platform Interface (Viewport) + if (io.*.ConfigFlags & @enumToInt(c.ImGuiConfigFlags_ViewportsEnable) != 0) + unreachable; // ImGui_ImplGlfw_InitPlatformInterface(); - g_ClientApi = client_api; + g_ClientApi = client_api; } pub fn Shutdown() void { - // @TODO: Platform Interface (Viewport) - // ImGui_ImplGlfw_ShutdownPlatformInterface(); - - for (g_MouseCursors) |*cursor| { - c.glfwDestroyCursor(cursor.*); - cursor.* = null; - } - g_ClientApi = .Unknown; + // @TODO: Platform Interface (Viewport) + // ImGui_ImplGlfw_ShutdownPlatformInterface(); + for (g_MouseCursors) |*cursor| { + c.glfwDestroyCursor(cursor.*); + cursor.* = null; + } + g_ClientApi = .Unknown; } pub fn NewFrame() void { - const io = c.igGetIO(); - debug.assert(c.ImFontAtlas_IsBuilt(io.*.Fonts)); - - var w : c_int = undefined; - var h : c_int = undefined; - var display_w : c_int = undefined; - var display_h : c_int = undefined; - c.glfwGetWindowSize(g_Window, &w, &h); - c.glfwGetFramebufferSize(g_Window, &display_w, &display_h); - io.*.DisplaySize = c.ImVec2{ .x = @intToFloat(f32, w), .y = @intToFloat(f32, h) }; - - if (w > 0 and h > 0) - io.*.DisplayFramebufferScale = c.ImVec2{ - .x = @intToFloat(f32, display_w) / @intToFloat(f32, w), - .y = @intToFloat(f32, display_h) / @intToFloat(f32, h), - }; - if (g_WantUpdateMonitors) - UpdateMonitors(); - - // Setup time step - const current_time = c.glfwGetTime(); - io.*.DeltaTime = if (g_Time > 0.0) @floatCast(f32, current_time - g_Time) else 1.0 / 60.0; - g_Time = current_time; - - UpdateMousePosAndButtons(); - UpdateMouseCursor(); - - UpdateGamepads(); + const io = c.igGetIO(); + debug.assert(c.ImFontAtlas_IsBuilt(io.*.Fonts)); + + var w: c_int = undefined; + var h: c_int = undefined; + var display_w: c_int = undefined; + var display_h: c_int = undefined; + c.glfwGetWindowSize(g_Window, &w, &h); + c.glfwGetFramebufferSize(g_Window, &display_w, &display_h); + io.*.DisplaySize = c.ImVec2{ .x = @intToFloat(f32, w), .y = @intToFloat(f32, h) }; + + if (w > 0 and h > 0) + io.*.DisplayFramebufferScale = c.ImVec2{ + .x = @intToFloat(f32, display_w) / @intToFloat(f32, w), + .y = @intToFloat(f32, display_h) / @intToFloat(f32, h), + }; + if (g_WantUpdateMonitors) + UpdateMonitors(); + + // Setup time step + const current_time = c.glfwGetTime(); + io.*.DeltaTime = if (g_Time > 0.0) @floatCast(f32, current_time - g_Time) else 1.0 / 60.0; + g_Time = current_time; + + UpdateMousePosAndButtons(); + UpdateMouseCursor(); + + UpdateGamepads(); } fn UpdateMonitors() void { - const platform_io = c.igGetPlatformIO(); - var monitors_count : c_int = 0; - const glfw_monitors = c.glfwGetMonitors(&monitors_count)[0..@intCast(usize, monitors_count)]; - - c.ImVector_ImGuiPlatformMonitor_Resize(&platform_io.*.Monitors, monitors_count); - for (glfw_monitors) |glfw_monitor, n| { - var monitor = &platform_io.*.Monitors.Data[n]; - var x : c_int = undefined; - var y : c_int = undefined; - c.glfwGetMonitorPos(glfw_monitor, &x, &y); - const vid_mode = c.glfwGetVideoMode(glfw_monitor); // glfw_monitors[n]); - - monitor.*.MainPos = c.ImVec2{ .x = @intToFloat(f32, x), .y = @intToFloat(f32, y) }; - monitor.*.MainSize = c.ImVec2{ - .x = @intToFloat(f32, vid_mode.*.width), - .y = @intToFloat(f32, vid_mode.*.height), - }; - if (false and c.GLFW_HAS_MONITOR_WORK_AREA) { - var w : c_int = undefined; - var h : c_int = undefined; - c.glfwGetMonitorWorkarea(glfw_monitor, &x, &y, &w, &h); - monitor.*.WorkPos = ImVec2{ .x = @intToFloat(f32, x), .y = @intToFloat(f32, y) }; - monitor.*.WorkSize = ImVec2{ .x = @intToFloat(f32, w), .y = @intToFloat(f32, h) }; - } - if (false and c.GLFW_HAS_PER_MONITOR_DPI) { - // Warning: the validity of monitor DPI information on Windows depends on the application DPI awareness settings, - // which generally needs to be set in the manifest or at runtime. - var x_scale : f32 = undefined; - var y_scale : f32 = undefined; - c.glfwGetMonitorContentScale(glfw_monitor, &x_scale, &y_scale); - monitor.*.DpiScale = x_scale; + const platform_io = c.igGetPlatformIO(); + var monitors_count: c_int = 0; + const glfw_monitors = c.glfwGetMonitors(&monitors_count)[0..@intCast(usize, monitors_count)]; + + c.ImVector_ImGuiPlatformMonitor_Resize(&platform_io.*.Monitors, monitors_count); + for (glfw_monitors) |glfw_monitor, n| { + var monitor = &platform_io.*.Monitors.Data[n]; + var x: c_int = undefined; + var y: c_int = undefined; + c.glfwGetMonitorPos(glfw_monitor, &x, &y); + const vid_mode = c.glfwGetVideoMode(glfw_monitor); // glfw_monitors[n]); + + monitor.*.MainPos = c.ImVec2{ .x = @intToFloat(f32, x), .y = @intToFloat(f32, y) }; + monitor.*.MainSize = c.ImVec2{ + .x = @intToFloat(f32, vid_mode.*.width), + .y = @intToFloat(f32, vid_mode.*.height), + }; + if (false and c.GLFW_HAS_MONITOR_WORK_AREA) { + var w: c_int = undefined; + var h: c_int = undefined; + c.glfwGetMonitorWorkarea(glfw_monitor, &x, &y, &w, &h); + monitor.*.WorkPos = ImVec2{ .x = @intToFloat(f32, x), .y = @intToFloat(f32, y) }; + monitor.*.WorkSize = ImVec2{ .x = @intToFloat(f32, w), .y = @intToFloat(f32, h) }; + } + if (false and c.GLFW_HAS_PER_MONITOR_DPI) { + // Warning: the validity of monitor DPI information on Windows depends on the application DPI awareness settings, + // which generally needs to be set in the manifest or at runtime. + var x_scale: f32 = undefined; + var y_scale: f32 = undefined; + c.glfwGetMonitorContentScale(glfw_monitor, &x_scale, &y_scale); + monitor.*.DpiScale = x_scale; + } } - } - g_WantUpdateMonitors = false; + g_WantUpdateMonitors = false; } fn UpdateMousePosAndButtons() void { - // Update buttons - const io = c.igGetIO(); - for (io.*.MouseDown) |*isDown, i| { - // If a mouse press event came, always pass it as "mouse held this frame", so we don't miss click-release events that are shorter than 1 frame. - isDown.* = g_MouseJustPressed[i] or c.glfwGetMouseButton(g_Window, @intCast(c_int, i)) != 0; - g_MouseJustPressed[i] = false; - } - - // Update mouse position - const mouse_pos_backup = io.*.MousePos; - io.*.MousePos = c.ImVec2{ .x = math.f32_min, .y = math.f32_min }; - io.*.MouseHoveredViewport = 0; - const platform_io = c.igGetPlatformIO(); - var n : usize = 0; - while (n < @intCast(usize, platform_io.*.Viewports.Size)) : (n += 1) { - const viewport = platform_io.*.Viewports.Data[n]; - const window = @ptrCast(*c.GLFWwindow, viewport.*.PlatformHandle); - const focused = c.glfwGetWindowAttrib(window, c.GLFW_FOCUSED) != 0; - if (focused) { - if (io.*.WantSetMousePos) { - c.glfwSetCursorPos(window, mouse_pos_backup.x - viewport.*.Pos.x, mouse_pos_backup.y - viewport.*.Pos.y); - } else { - var mouse_x : f64 = undefined; - var mouse_y : f64 = undefined; - c.glfwGetCursorPos(window, &mouse_x, &mouse_y); - if (io.*.ConfigFlags & @enumToInt(c.ImGuiConfigFlags_ViewportsEnable) != 0) { - // Multi-viewport mode: mouse position in OS absolute coordinates (io.MousePos is (0,0) when the mouse is on the upper-left of the primary monitor) - var window_x : c_int = undefined; - var window_y : c_int = undefined; - c.glfwGetWindowPos(window, &window_x, &window_y); - io.*.MousePos = c.ImVec2{ - .x = @floatCast(f32, mouse_x) + @intToFloat(f32, window_x), - .y = @floatCast(f32, mouse_y) + @intToFloat(f32, window_y), - }; - } else { - // Single viewport mode: mouse position in client window coordinates (io.MousePos is (0,0) when the mouse is on the upper-left corner of the app window) - io.*.MousePos = c.ImVec2{ .x = @floatCast(f32, mouse_x), .y = @floatCast(f32, mouse_y) }; - } - } + // Update buttons + const io = c.igGetIO(); + for (io.*.MouseDown) |*isDown, i| { + // If a mouse press event came, always pass it as "mouse held this frame", so we don't miss click-release events that are shorter than 1 frame. + isDown.* = g_MouseJustPressed[i] or c.glfwGetMouseButton(g_Window, @intCast(c_int, i)) != 0; + g_MouseJustPressed[i] = false; + } - for (io.*.MouseDown) |*isDown, i| - isDown.* = isDown.* or c.glfwGetMouseButton(window, @intCast(c_int, i)) != 0; + // Update mouse position + const mouse_pos_backup = io.*.MousePos; + io.*.MousePos = c.ImVec2{ .x = math.f32_min, .y = math.f32_min }; + io.*.MouseHoveredViewport = 0; + const platform_io = c.igGetPlatformIO(); + var n: usize = 0; + while (n < @intCast(usize, platform_io.*.Viewports.Size)) : (n += 1) { + const viewport = platform_io.*.Viewports.Data[n]; + const window = @ptrCast(*c.GLFWwindow, viewport.*.PlatformHandle); + const focused = c.glfwGetWindowAttrib(window, c.GLFW_FOCUSED) != 0; + if (focused) { + if (io.*.WantSetMousePos) { + c.glfwSetCursorPos(window, mouse_pos_backup.x - viewport.*.Pos.x, mouse_pos_backup.y - viewport.*.Pos.y); + } else { + var mouse_x: f64 = undefined; + var mouse_y: f64 = undefined; + c.glfwGetCursorPos(window, &mouse_x, &mouse_y); + if (io.*.ConfigFlags & @enumToInt(c.ImGuiConfigFlags_ViewportsEnable) != 0) { + // Multi-viewport mode: mouse position in OS absolute coordinates (io.MousePos is (0,0) when the mouse is on the upper-left of the primary monitor) + var window_x: c_int = undefined; + var window_y: c_int = undefined; + c.glfwGetWindowPos(window, &window_x, &window_y); + io.*.MousePos = c.ImVec2{ + .x = @floatCast(f32, mouse_x) + @intToFloat(f32, window_x), + .y = @floatCast(f32, mouse_y) + @intToFloat(f32, window_y), + }; + } else { + // Single viewport mode: mouse position in client window coordinates (io.MousePos is (0,0) when the mouse is on the upper-left corner of the app window) + io.*.MousePos = c.ImVec2{ .x = @floatCast(f32, mouse_x), .y = @floatCast(f32, mouse_y) }; + } + } + + for (io.*.MouseDown) |*isDown, i| + isDown.* = isDown.* or c.glfwGetMouseButton(window, @intCast(c_int, i)) != 0; + } } - } } fn UpdateMouseCursor() void { - const io = c.igGetIO(); - if (io.*.ConfigFlags & @enumToInt(c.ImGuiConfigFlags_NoMouseCursorChange) != 0 - or c.glfwGetInputMode(g_Window, c.GLFW_CURSOR) == c.GLFW_CURSOR_DISABLED) - return; - - const imgui_cursor = c.igGetMouseCursor(); - const platform_io = c.igGetPlatformIO(); - var n : usize = 0; - while (n < @intCast(usize, platform_io.*.Viewports.Size)) : (n += 1) { - const window = @ptrCast(*c.GLFWwindow, platform_io.*.Viewports.Data[n].*.PlatformHandle); - if (imgui_cursor == @enumToInt(c.ImGuiMouseCursor_None) or io.*.MouseDrawCursor) { - // Hide OS mouse cursor if imgui is drawing it or if it wants no cursor - c.glfwSetInputMode(window, c.GLFW_CURSOR, c.GLFW_CURSOR_HIDDEN); - } else { - // Show OS mouse cursor - // FIXME-PLATFORM: Unfocused windows seems to fail changing the mouse cursor with GLFW 3.2, but 3.3 works here. - c.glfwSetCursor(window, - if (g_MouseCursors[@intCast(usize, imgui_cursor)]) |cursor| cursor - else g_MouseCursors[@enumToInt(c.ImGuiMouseCursor_Arrow)]); - c.glfwSetInputMode(window, c.GLFW_CURSOR, c.GLFW_CURSOR_NORMAL); + const io = c.igGetIO(); + if (io.*.ConfigFlags & @enumToInt(c.ImGuiConfigFlags_NoMouseCursorChange) != 0 or c.glfwGetInputMode(g_Window, c.GLFW_CURSOR) == c.GLFW_CURSOR_DISABLED) + return; + + const imgui_cursor = c.igGetMouseCursor(); + const platform_io = c.igGetPlatformIO(); + var n: usize = 0; + while (n < @intCast(usize, platform_io.*.Viewports.Size)) : (n += 1) { + const window = @ptrCast(*c.GLFWwindow, platform_io.*.Viewports.Data[n].*.PlatformHandle); + if (imgui_cursor == @enumToInt(c.ImGuiMouseCursor_None) or io.*.MouseDrawCursor) { + // Hide OS mouse cursor if imgui is drawing it or if it wants no cursor + c.glfwSetInputMode(window, c.GLFW_CURSOR, c.GLFW_CURSOR_HIDDEN); + } else { + // Show OS mouse cursor + // FIXME-PLATFORM: Unfocused windows seems to fail changing the mouse cursor with GLFW 3.2, but 3.3 works here. + c.glfwSetCursor(window, if (g_MouseCursors[@intCast(usize, imgui_cursor)]) |cursor| cursor else g_MouseCursors[@enumToInt(c.ImGuiMouseCursor_Arrow)]); + c.glfwSetInputMode(window, c.GLFW_CURSOR, c.GLFW_CURSOR_NORMAL); + } } - } } fn UpdateGamepads() void { - // @TODO + // @TODO } // GLFW Callbacks extern fn Callback_MouseButton(window: ?*c.GLFWwindow, button: c_int, action: c_int, mods: c_int) void { - if (g_PrevUserCallbackMousebutton) |prev| { - prev(window, button, action, mods); - } + if (g_PrevUserCallbackMousebutton) |prev| { + prev(window, button, action, mods); + } - if (button < 0) - return; + if (button < 0) + return; - const button_u = @intCast(usize, button); - if (action == c.GLFW_PRESS and button_u < g_MouseJustPressed.len) - g_MouseJustPressed[button_u] = true; + const button_u = @intCast(usize, button); + if (action == c.GLFW_PRESS and button_u < g_MouseJustPressed.len) + g_MouseJustPressed[button_u] = true; } extern fn Callback_Scroll(window: ?*c.GLFWwindow, dx: f64, dy: f64) void { - if (g_PrevUserCallbackScroll) |prev| { - prev(window, dx, dy); - } + if (g_PrevUserCallbackScroll) |prev| { + prev(window, dx, dy); + } - const io = c.igGetIO(); - io.*.MouseWheelH += @floatCast(f32, dx); - io.*.MouseWheel += @floatCast(f32, dy); + const io = c.igGetIO(); + io.*.MouseWheelH += @floatCast(f32, dx); + io.*.MouseWheel += @floatCast(f32, dy); } extern fn Callback_Key(window: ?*c.GLFWwindow, key: c_int, scancode: c_int, action: c_int, modifiers: c_int) void { - if (g_PrevUserCallbackKey) |prev| { - prev(window, key, scancode, action, modifiers); - } - - if (key < 0) - unreachable; - - const key_u = @intCast(usize, key); - - const io = c.igGetIO(); - if (action == c.GLFW_PRESS) - io.*.KeysDown[key_u] = true; - if (action == c.GLFW_RELEASE) - io.*.KeysDown[key_u] = false; - - // Modifiers are not reliable across systems - io.*.KeyCtrl = io.*.KeysDown[c.GLFW_KEY_LEFT_CONTROL] or io.*.KeysDown[c.GLFW_KEY_RIGHT_CONTROL]; - io.*.KeyShift = io.*.KeysDown[c.GLFW_KEY_LEFT_SHIFT] or io.*.KeysDown[c.GLFW_KEY_RIGHT_SHIFT]; - io.*.KeyAlt = io.*.KeysDown[c.GLFW_KEY_LEFT_ALT] or io.*.KeysDown[c.GLFW_KEY_RIGHT_ALT]; - io.*.KeySuper = io.*.KeysDown[c.GLFW_KEY_LEFT_SUPER] or io.*.KeysDown[c.GLFW_KEY_RIGHT_SUPER]; + if (g_PrevUserCallbackKey) |prev| { + prev(window, key, scancode, action, modifiers); + } + + if (key < 0) + unreachable; + + const key_u = @intCast(usize, key); + + const io = c.igGetIO(); + if (action == c.GLFW_PRESS) + io.*.KeysDown[key_u] = true; + if (action == c.GLFW_RELEASE) + io.*.KeysDown[key_u] = false; + + // Modifiers are not reliable across systems + io.*.KeyCtrl = io.*.KeysDown[c.GLFW_KEY_LEFT_CONTROL] or io.*.KeysDown[c.GLFW_KEY_RIGHT_CONTROL]; + io.*.KeyShift = io.*.KeysDown[c.GLFW_KEY_LEFT_SHIFT] or io.*.KeysDown[c.GLFW_KEY_RIGHT_SHIFT]; + io.*.KeyAlt = io.*.KeysDown[c.GLFW_KEY_LEFT_ALT] or io.*.KeysDown[c.GLFW_KEY_RIGHT_ALT]; + io.*.KeySuper = io.*.KeysDown[c.GLFW_KEY_LEFT_SUPER] or io.*.KeysDown[c.GLFW_KEY_RIGHT_SUPER]; } extern fn Callback_Char(window: ?*c.GLFWwindow, char: c_uint) void { - if (g_PrevUserCallbackChar) |prev| { - prev(window, char); - } + if (g_PrevUserCallbackChar) |prev| { + prev(window, char); + } - const io = c.igGetIO(); - c.ImGuiIO_AddInputCharacter(io, char); + const io = c.igGetIO(); + c.ImGuiIO_AddInputCharacter(io, char); } diff --git a/src/main.zig b/src/main.zig index 5d5f24a..79dab91 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1,3 +1,4 @@ +/// this is a port of cimgui/imgui/examples/example_glfw_opengl3/main.cpp const c = @import("c.zig"); const std = @import("std"); const panic = std.debug.panic; @@ -6,88 +7,88 @@ const glfw_impl = @import("glfw_impl.zig"); const gl3_impl = @import("gl3_impl.zig"); extern fn errorCallback(err: c_int, description: [*c]const u8) void { - panic("Error: {}\n", description); + panic("Error: {}\n", description); } var window: *c.GLFWwindow = undefined; pub fn main() !void { - _ = c.glfwSetErrorCallback(errorCallback); - - if (c.glfwInit() == c.GL_FALSE) { - panic("GLFW init failure\n"); - } - defer c.glfwTerminate(); - - c.glfwWindowHint(c.GLFW_CONTEXT_VERSION_MAJOR, 3); - c.glfwWindowHint(c.GLFW_CONTEXT_VERSION_MINOR, 2); - c.glfwWindowHint(c.GLFW_OPENGL_FORWARD_COMPAT, c.GL_TRUE); - c.glfwWindowHint(c.GLFW_OPENGL_DEBUG_CONTEXT, debug_gl.is_on); - c.glfwWindowHint(c.GLFW_OPENGL_PROFILE, c.GLFW_OPENGL_CORE_PROFILE); - // c.glfwWindowHint(c.GLFW_DEPTH_BITS, 0); - // c.glfwWindowHint(c.GLFW_STENCIL_BITS, 8); - c.glfwWindowHint(c.GLFW_RESIZABLE, c.GL_TRUE); - - const window_width = 640; - const window_height = 480; - window = c.glfwCreateWindow(window_width, window_height, c"ImGUI Test", null, null) orelse { - panic("unable to create window\n"); - }; - defer c.glfwDestroyWindow(window); - - c.glfwMakeContextCurrent(window); - c.glfwSwapInterval(1); - - const context = c.igCreateContext(null); - defer c.igDestroyContext(context); - - const io = c.igGetIO(); - io.*.ConfigFlags |= @enumToInt(c.ImGuiConfigFlags_NavEnableKeyboard); - // io.*.ConfigFlags |= @enumToInt(c.ImGuiConfigFlags_DockingEnable); - // io.*.ConfigFlags |= @enumToInt(c.ImGuiConfigFlags_ViewportsEnable); - - const style = c.igGetStyle(); - c.igStyleColorsDark(style); - - if (false and io.*.ConfigFlags & @enumToInt(c.ImGuiConfigFlags_ViewportsEnable) != 0) { - style.*.WindowRounding = 0.0; - style.*.Colors[@enumToInt(c.ImGuiCol_WindowBg)].w = 1.0; - } - - glfw_impl.Init(window, true, glfw_impl.ClientApi.OpenGL); - defer glfw_impl.Shutdown(); - - gl3_impl.Init(); // #version 150 - defer gl3_impl.Shutdown(); - - const start_time = c.glfwGetTime(); - var prev_time = start_time; - - while (c.glfwWindowShouldClose(window) == c.GL_FALSE) { - c.glfwPollEvents(); - - try gl3_impl.NewFrame(); - glfw_impl.NewFrame(); - c.igNewFrame(); - - // main part - c.igShowDemoWindow(null); - - c.igRender(); - var w: c_int = undefined; - var h: c_int = undefined; - c.glfwGetFramebufferSize(window, &w, &h); - c.glViewport(0, 0, w, h); - c.glClearColor(0.0, 0.0, 0.0, 0.0); - c.glClear(c.GL_COLOR_BUFFER_BIT); - gl3_impl.RenderDrawData(c.igGetDrawData()); - - // const now_time = c.glfwGetTime(); - // const elapsed = now_time - prev_time; - // prev_time = now_time; - // nextFrame(t, elapsed); - // draw(t, @This()); - - c.glfwSwapBuffers(window); - } + _ = c.glfwSetErrorCallback(errorCallback); + + if (c.glfwInit() == c.GL_FALSE) { + panic("GLFW init failure\n"); + } + defer c.glfwTerminate(); + + c.glfwWindowHint(c.GLFW_CONTEXT_VERSION_MAJOR, 3); + c.glfwWindowHint(c.GLFW_CONTEXT_VERSION_MINOR, 2); + c.glfwWindowHint(c.GLFW_OPENGL_FORWARD_COMPAT, c.GL_TRUE); + c.glfwWindowHint(c.GLFW_OPENGL_DEBUG_CONTEXT, debug_gl.is_on); + c.glfwWindowHint(c.GLFW_OPENGL_PROFILE, c.GLFW_OPENGL_CORE_PROFILE); + // c.glfwWindowHint(c.GLFW_DEPTH_BITS, 0); + // c.glfwWindowHint(c.GLFW_STENCIL_BITS, 8); + c.glfwWindowHint(c.GLFW_RESIZABLE, c.GL_TRUE); + + const window_width = 640; + const window_height = 480; + window = c.glfwCreateWindow(window_width, window_height, c"ImGUI Test", null, null) orelse { + panic("unable to create window\n"); + }; + defer c.glfwDestroyWindow(window); + + c.glfwMakeContextCurrent(window); + c.glfwSwapInterval(1); + + const context = c.igCreateContext(null); + defer c.igDestroyContext(context); + + const io = c.igGetIO(); + io.*.ConfigFlags |= @enumToInt(c.ImGuiConfigFlags_NavEnableKeyboard); + // io.*.ConfigFlags |= @enumToInt(c.ImGuiConfigFlags_DockingEnable); + // io.*.ConfigFlags |= @enumToInt(c.ImGuiConfigFlags_ViewportsEnable); + + const style = c.igGetStyle(); + c.igStyleColorsDark(style); + + if (false and io.*.ConfigFlags & @enumToInt(c.ImGuiConfigFlags_ViewportsEnable) != 0) { + style.*.WindowRounding = 0.0; + style.*.Colors[@enumToInt(c.ImGuiCol_WindowBg)].w = 1.0; + } + + glfw_impl.Init(window, true, glfw_impl.ClientApi.OpenGL); + defer glfw_impl.Shutdown(); + + gl3_impl.Init(); // #version 150 + defer gl3_impl.Shutdown(); + + const start_time = c.glfwGetTime(); + var prev_time = start_time; + + while (c.glfwWindowShouldClose(window) == c.GL_FALSE) { + c.glfwPollEvents(); + + try gl3_impl.NewFrame(); + glfw_impl.NewFrame(); + c.igNewFrame(); + + // main part + c.igShowDemoWindow(null); + + c.igRender(); + var w: c_int = undefined; + var h: c_int = undefined; + c.glfwGetFramebufferSize(window, &w, &h); + c.glViewport(0, 0, w, h); + c.glClearColor(0.0, 0.0, 0.0, 0.0); + c.glClear(c.GL_COLOR_BUFFER_BIT); + gl3_impl.RenderDrawData(c.igGetDrawData()); + + // const now_time = c.glfwGetTime(); + // const elapsed = now_time - prev_time; + // prev_time = now_time; + // nextFrame(t, elapsed); + // draw(t, @This()); + + c.glfwSwapBuffers(window); + } } |
