GCC Code Coverage Report


./
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
0 of 174, 0 excluded
0.0%
Functions:
0 of 17, 0 excluded
0.0%
Branches:
0 of 190, 0 excluded
0.0%

libs/imgui/src/eu/imgui/ui.cc
Line Branch Exec Source
1 #include "eu/imgui/ui.h"
2
3 // #include "klotter/cint.h"
4
5 #include "eu/core/ui.h"
6 #include "eu/render/texture.h"
7 #include "eu/render/dependency_glad.h"
8
9 #include <iostream>
10
11 #include "dear_imgui/imgui.h"
12
13 namespace eu::imgui
14 {
15
16 // opengl code copied from the imgui opengl3 backend with minor modifications
17 // todo(Gustav): should we use the imgui backend code for "backend" rendering or use our own shader?
18
19 static bool check_imgui_shader(GLuint handle, const char* desc)
20 {
21 GLint status = 0;
22 GLint log_length = 0;
23
24 glGetShaderiv(handle, GL_COMPILE_STATUS, &status);
25 glGetShaderiv(handle, GL_INFO_LOG_LENGTH, &log_length);
26 if (status == GL_FALSE)
27 {
28 std::cerr << "ERROR: Shader code: failed to compile " << desc << "!\n";
29 }
30
31 if (log_length > 1)
32 {
33 std::vector<GLchar> buf;
34 buf.resize(sizet_from_int(log_length + 1));
35 glGetShaderInfoLog(handle, log_length, nullptr, buf.data());
36 std::cerr << buf.data();
37 }
38 return status == GL_TRUE;
39 }
40
41 static bool imgui_check_program(GLuint handle, const char* desc)
42 {
43 GLint status = 0;
44 GLint log_length = 0;
45 glGetProgramiv(handle, GL_LINK_STATUS, &status);
46 glGetProgramiv(handle, GL_INFO_LOG_LENGTH, &log_length);
47 if (status == GL_FALSE)
48 {
49 std::cerr << "ERROR: Shader program: failed to link " << desc << "!\n";
50 }
51
52 if (log_length > 1)
53 {
54 std::vector<GLchar> buf;
55 buf.resize(sizet_from_int(log_length + 1));
56 glGetProgramInfoLog(handle, log_length, nullptr, buf.data());
57 std::cerr << buf.data();
58 }
59 return status == GL_TRUE;
60 }
61
62 void imgui_destroy_shader(ImguiShaderProgram* bd)
63 {
64 if (bd->program_handle == 0)
65 {
66 return;
67 }
68
69 glDeleteProgram(bd->program_handle);
70 bd->program_handle = 0;
71 }
72
73 ImguiShaderProgram imgui_load_shader(const char* glsl_version_string, const char* vertex_shader, const char* fragment_shader)
74 {
75 // Create shaders
76 const GLchar* vertex_shader_with_version[2] = {glsl_version_string, vertex_shader};
77 const auto vert_handle = glCreateShader(GL_VERTEX_SHADER);
78 glShaderSource(vert_handle, 2, vertex_shader_with_version, nullptr);
79 glCompileShader(vert_handle);
80 if (! check_imgui_shader(vert_handle, "vertex shader"))
81 {
82 return {};
83 }
84
85 const GLchar* fragment_shader_with_version[2] = {glsl_version_string, fragment_shader};
86 const auto frag_handle = glCreateShader(GL_FRAGMENT_SHADER);
87 glShaderSource(frag_handle, 2, fragment_shader_with_version, nullptr);
88 glCompileShader(frag_handle);
89
90 if (! check_imgui_shader(frag_handle, "fragment shader"))
91 {
92 return {};
93 }
94
95 // Link
96 ImguiShaderProgram prog;
97 prog.program_handle = glCreateProgram();
98 glAttachShader(prog.program_handle, vert_handle);
99 glAttachShader(prog.program_handle, frag_handle);
100 glLinkProgram(prog.program_handle);
101 if (! imgui_check_program(prog.program_handle, "shader program"))
102 {
103 imgui_destroy_shader(&prog);
104 return {};
105 }
106
107 glDetachShader(prog.program_handle, vert_handle);
108 glDetachShader(prog.program_handle, frag_handle);
109 glDeleteShader(vert_handle);
110 glDeleteShader(frag_handle);
111
112 prog.texture_attrib = glGetUniformLocation(prog.program_handle, "Texture");
113 prog.projection_attrib = glGetUniformLocation(prog.program_handle, "ProjMtx");
114 return prog;
115 }
116
117 constexpr auto dear_imgui_shader_version = "#version 330 core\n";
118
119 constexpr auto linear_to_gamma_glsl_vert = R"glsl(
120 layout (location = 0) in vec2 Position;
121 layout (location = 1) in vec2 UV;
122 layout (location = 2) in vec4 Color;
123 uniform mat4 ProjMtx;
124 out vec2 Frag_UV;
125 out vec4 Frag_Color;
126 void main()
127 {
128 Frag_UV = UV;
129 Frag_Color = Color;
130 gl_Position = ProjMtx * vec4(Position.xy,0,1);
131 }
132 )glsl";
133
134 constexpr auto linear_to_gamma_glsl_frag = R"glsl(
135 in vec2 Frag_UV;
136 in vec4 Frag_Color;
137 uniform sampler2D Texture;
138 layout (location = 0) out vec4 Out_Color;
139 void main()
140 {
141 vec4 sample = texture(Texture, Frag_UV.st);
142 vec3 color = sample.rgb / (sample.rgb + vec3(1.0f)); // reinhard tone mapping
143 float gamma = 2.2f;
144 vec3 gamma_corrected = pow(color.rgb, vec3(1.0f/gamma));
145 Out_Color = Frag_Color * vec4(gamma_corrected, sample.a);
146 }
147 )glsl";
148
149
150 constexpr auto depth_ortho_glsl_vert = R"glsl(
151 layout (location = 0) in vec2 Position;
152 layout (location = 1) in vec2 UV;
153 layout (location = 2) in vec4 Color;
154 uniform mat4 ProjMtx;
155 out vec2 Frag_UV;
156 out vec4 Frag_Color;
157 void main()
158 {
159 Frag_UV = UV;
160 Frag_Color = Color;
161 gl_Position = ProjMtx * vec4(Position.xy,0,1);
162 }
163 )glsl";
164
165 constexpr auto depth_ortho_glsl_frag = R"glsl(
166 in vec2 Frag_UV;
167 in vec4 Frag_Color;
168 uniform sampler2D Texture;
169 layout (location = 0) out vec4 Out_Color;
170
171 void main()
172 {
173 vec4 sample = texture(Texture, Frag_UV.st);
174 Out_Color = Frag_Color * vec4(vec3(sample.r), 1);
175 }
176 )glsl";
177
178
179 ImguiShaderCache::ImguiShaderCache()
180 : linear_to_gamma_shader(imgui_load_shader(dear_imgui_shader_version,
181 linear_to_gamma_glsl_vert, linear_to_gamma_glsl_frag))
182 , depth_ortho_shader(imgui_load_shader(dear_imgui_shader_version,
183 depth_ortho_glsl_vert, depth_ortho_glsl_frag)
184 )
185 {
186 }
187
188 ImguiShaderCache::~ImguiShaderCache()
189 {
190 imgui_destroy_shader(&linear_to_gamma_shader);
191 imgui_destroy_shader(&depth_ortho_shader);
192 }
193
194
195 void imgui_set_shared_shader_params(ImguiShaderProgram* prog)
196 {
197 ImDrawData* draw_data = ImGui::GetDrawData();
198
199 const auto le = draw_data->DisplayPos.x;
200 const auto ri = draw_data->DisplayPos.x + draw_data->DisplaySize.x;
201 const auto to = draw_data->DisplayPos.y;
202 const auto bo = draw_data->DisplayPos.y + draw_data->DisplaySize.y;
203
204 const float ortho_projection[4][4] = {
205 {2.0f / (ri - le), 0.0f, 0.0f, 0.0f},
206 {0.0f, 2.0f / (to - bo), 0.0f, 0.0f},
207 {0.0f, 0.0f, -1.0f, 0.0f},
208 {(ri + le) / (le - ri), (to + bo) / (bo - to), 0.0f, 1.0f},
209 };
210 glUseProgram(prog->program_handle);
211 glUniform1i(prog->texture_attrib, 0);
212 glUniformMatrix4fv(prog->projection_attrib, 1, GL_FALSE, &ortho_projection[0][0]);
213 }
214
215 void im_draw_callback_linear_to_gamma(const ImDrawList*, const ImDrawCmd* cmd)
216 {
217 auto* prog = static_cast<ImguiShaderProgram*>(cmd->UserCallbackData);
218
219 imgui_set_shared_shader_params(prog);
220 }
221
222 void im_draw_callback_depth_ortho(const ImDrawList*, const ImDrawCmd* cmd)
223 {
224 auto* prog = static_cast<ImguiShaderProgram*>(cmd->UserCallbackData);
225
226 imgui_set_shared_shader_params(prog);
227 }
228
229
230
231 void imgui_text(const std::string& str)
232 {
233 ImGui::Text("%s", str.c_str());
234 }
235
236
237
238 static void draw_imgui_image(const render::FrameBuffer& img, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& border_col, ImguiShaderCache* cache, ImageShader shader)
239 {
240 ImDrawList* draw_list = ImGui::GetWindowDrawList();
241
242 // set shader
243 switch (shader)
244 {
245 case ImageShader::TonemapAndGamma:
246 draw_list->AddCallback(im_draw_callback_linear_to_gamma, &cache->linear_to_gamma_shader);
247 break;
248 case ImageShader::DepthOrtho:
249 draw_list->AddCallback(im_draw_callback_depth_ortho, &cache->depth_ortho_shader);
250 break;
251 case ImageShader::None:
252 // no shader
253 break;
254 }
255
256 // draw image
257 ImGui::ImageWithBg(img.id, image_size, uv0, uv1, border_col);
258
259 // reset shader
260 if (shader != ImageShader::None)
261 {
262 draw_list->AddCallback(ImDrawCallback_ResetRenderState, nullptr);
263 }
264 }
265
266
267 static v2 v2_from_vec(const ImVec2& v)
268 {
269 return {v.x, v.y};
270 }
271
272
273 static void image_tooltip(const render::FrameBuffer& texture_id, const ImVec2 texture_size, const float& region_size, const float& hover_size,
274 const ImVec2 mouse_pos, const ImVec2 widget_size, const ImVec2 pos, const ImVec4 border_col, ImguiShaderCache* cache, ImageShader shader)
275 {
276 const auto region = core::calculate_region(v2_from_vec(mouse_pos), v2_from_vec(pos), v2_from_vec(texture_size), v2_from_vec(widget_size), region_size);
277 const auto flipped_region_y = texture_size.y - region.y;
278 const auto uv0 = ImVec2{region.x / texture_size.x, (flipped_region_y) / texture_size.y};
279 const auto uv1 = ImVec2{(region.x + region_size) / texture_size.x, (flipped_region_y - region_size) / texture_size.y};
280
281 // todo(Gustav): can we display pixel value instead of where we are looking? is the region important information?
282 imgui_text(fmt::format("UL: {} {}", region.x, region.y));
283 imgui_text(fmt::format("LR: {} {}", region.x + region_size, region.y + region_size));
284 draw_imgui_image(texture_id, ImVec2(hover_size, hover_size), uv0, uv1, border_col, cache, shader);
285 }
286
287
288
289 void imgui_image(const char* name, const render::FrameBuffer& img, ImguiShaderCache* cache, ImageShader shader)
290 {
291 const auto texture_size = ImVec2{float_from_int(img.size.width), float_from_int(img.size.height)};
292
293 // todo(Gustav): make the arguments widget_size and zoom level AND make them configurable (with scrolling)
294 static float widget_height = 100.0f;
295 static float region_size = 32.0f;
296 static float hover_size = 128.0f;
297 const auto& io = ImGui::GetIO();
298
299 imgui_text(fmt::format("{}: {}x{}", name, texture_size.x, texture_size.y));
300
301 const auto widget_width = (texture_size.x / texture_size.y) * widget_height;
302 const auto widget_size = ImVec2{widget_width, widget_height};
303
304 const auto pos = ImGui::GetCursorScreenPos();
305 constexpr auto uv_min = ImVec2{0.0f, 1.0f}; // Top-left
306 constexpr auto uv_max = ImVec2{1.0f, 0.0f}; // Lower-right
307 const auto border_col = ImGui::GetStyleColorVec4(ImGuiCol_Border);
308
309 static ImVec2 latest_tooltip;
310 static ImGuiID current_id = 0;
311
312 const constexpr char* const popup_id = "image config popup";
313
314 draw_imgui_image(img, widget_size, uv_min, uv_max, border_col, cache, shader);
315 const auto id = ImGui::GetID(name);
316
317 if (id == current_id && ImGui::BeginPopupContextItem(popup_id))
318 {
319 ImGui::DragFloat("Base", &widget_height, 1.0f);
320 ImGui::DragFloat("Size", &region_size, 0.01f);
321 ImGui::DragFloat("Scale", &hover_size, 1.0f);
322 image_tooltip(img, texture_size, region_size, hover_size, latest_tooltip, widget_size, pos, border_col, cache, shader);
323 if (ImGui::Button("Close"))
324 {
325 ImGui::CloseCurrentPopup();
326 }
327 ImGui::EndPopup();
328 }
329 ImGui::OpenPopupOnItemClick(popup_id, ImGuiPopupFlags_MouseButtonRight);
330
331 // todo(Gustav): look into SetItemTooltip and BeginItemTooltip from https://github.com/ocornut/imgui/releases/tag/v1.89.7
332 if (ImGui::IsItemHovered())
333 {
334 current_id = id;
335 latest_tooltip = io.MousePos;
336 if (ImGui::BeginTooltip())
337 {
338 image_tooltip(img, texture_size, region_size, hover_size, io.MousePos, widget_size, pos, border_col, cache, shader);
339 ImGui::EndTooltip();
340 }
341 }
342 }
343
344
345
346 bool simple_gamma_slider(const char* label, float* gamma, float curve, float min_gamma, float max_gamma)
347 {
348 if (curve < 0.0f)
349 {
350 return ImGui::SliderFloat(label, gamma, min_gamma, max_gamma);
351 }
352
353 // todo(Gustav): is this the correct way? it doesn't feel exactly right but perhaps that's just dear imgui
354 const auto gamma_range = max_gamma - min_gamma;
355 const auto t = (*gamma - min_gamma) / (gamma_range);
356
357 auto slider_value = std::pow(t, 1.0f / curve);
358 if (ImGui::SliderFloat(label, &slider_value, 0.0f, 1.0f) == false)
359 {
360 return false;
361 }
362
363 const auto perceptual = std::pow(slider_value, curve);
364 *gamma = min_gamma + perceptual * gamma_range;
365 return true;
366 }
367
368
369 bool drag(const char* const label, v3* drag)
370 {
371 return ImGui::DragFloat3(label, drag->get_data_ptr());
372 }
373
374 bool drag(const char* const label, Ypr* drag)
375 {
376 float angles[3] = {
377 drag->yaw.as_degrees(),
378 drag->pitch.as_degrees(),
379 drag->roll.as_degrees()
380 };
381
382 const auto changed = ImGui::DragFloat3(label, angles, 1.0f, -360.0f, 360.0f);
383
384 if (changed)
385 {
386 drag->yaw = An::from_degrees(angles[0]);
387 drag->pitch = An::from_degrees(angles[1]);
388 drag->roll = An::from_degrees(angles[2]);
389 }
390
391 return changed;
392 }
393
394
395 }
396
397