Brenta Engine 1.2
Loading...
Searching...
No Matches
gui.cpp
1// SPDX-License-Identifier: MIT
2// Author: Giovanni Santini
3// Mail: giovanni.santini@proton.me
4// Github: @San7o
5
6#include <brenta/renderer/opengl/gl.hpp>
7#include <brenta/renderer/opengl/texture.hpp>
8#include <brenta/renderer/opengl/framebuffer.hpp>
9#include <brenta/gui.hpp>
10#include <brenta/window.hpp>
11#include <brenta/logger.hpp>
12#include <brenta/asset.hpp>
13
14#include <imgui.h>
15#include <imgui_impl_glfw.h>
16#include <imgui_impl_opengl3.h>
17#include <implot/implot.h>
18
19#include <cmath>
20
21#ifndef BRENTA_NO_IMGUI
22
23namespace brenta
24{
25
27{
28 int max_size;
29 int offset;
30 ImVector<ImVec2> data;
31
32 TimePlotBuffer(int max_size = 2000)
33 {
34 this->max_size = max_size;
35 this->offset = 0;
36 this->data.reserve(max_size);
37 }
38
39 void add_point(float x, float y)
40 {
41 if (this->data.size() < this->max_size)
42 this->data.push_back(ImVec2(x,y));
43 else {
44 this->data[this->offset] = ImVec2(x,y);
45 this->offset = (this->offset + 1) % this->max_size;
46 }
47 }
48
49 void erase()
50 {
51 if (this->data.size() > 0)
52 {
53 this->data.shrink(0);
54 this->offset = 0;
55 }
56 }
57};
58
59} // namespace brenta
60
61using namespace brenta;
62
63//
64// Static variables
65//
66
67const std::string Gui::subsystem_name = "gui";
68bool Gui::initialized = false;
69ImFont *Gui::font = nullptr;
70
71static void setup_style()
72{
73 ImGuiStyle& style = ImGui::GetStyle();
74 ImVec4* colors = style.Colors;
75
76 // Color palette
77 const ImVec4 base = ImVec4(0.0f, 0.0f, 0.0f, 1.0f); // #000000
78 const ImVec4 mantle = ImVec4(0.169f, 0.169f, 0.169f, 1.0f); // #2b2b2b
79 const ImVec4 surface0 = ImVec4(0.169f, 0.169f, 0.169f, 1.0f); // #2b2b2b
80 const ImVec4 surface1 = ImVec4(0.100f, 0.100f, 0.100f, 1.0f); //
81 const ImVec4 surface2 = ImVec4(0.050f, 0.050f, 0.050f, 1.0f); //
82 const ImVec4 overlay0 = ImVec4(0.396f, 0.403f, 0.486f, 1.0f); // #65677c
83 const ImVec4 overlay2 = ImVec4(0.576f, 0.584f, 0.654f, 1.0f); // #9399b2
84 const ImVec4 text = ImVec4(0.803f, 0.815f, 0.878f, 1.0f); // #cdd6f4
85 const ImVec4 subtext0 = ImVec4(0.639f, 0.658f, 0.764f, 1.0f); // #a3a8c3
86 const ImVec4 mauve = ImVec4(0.796f, 0.698f, 0.972f, 1.0f); // #cba6f7
87 const ImVec4 peach = ImVec4(0.980f, 0.709f, 0.572f, 1.0f); // #fab387
88 const ImVec4 yellow = ImVec4(0.980f, 0.913f, 0.596f, 1.0f); // #f9e2af
89 const ImVec4 green = ImVec4(0.650f, 0.890f, 0.631f, 1.0f); // #a6e3a1
90 const ImVec4 teal = ImVec4(0.580f, 0.886f, 0.819f, 1.0f); // #94e2d5
91 const ImVec4 sapphire = ImVec4(0.458f, 0.784f, 0.878f, 1.0f); // #74c7ec
92 const ImVec4 blue = ImVec4(0.533f, 0.698f, 0.976f, 1.0f); // #89b4fa
93 const ImVec4 lavender = ImVec4(0.709f, 0.764f, 0.980f, 1.0f); // #b4befe
94
95 // Main window and backgrounds
96 colors[ImGuiCol_WindowBg] = base;
97 colors[ImGuiCol_ChildBg] = base;
98 colors[ImGuiCol_PopupBg] = surface0;
99 colors[ImGuiCol_Border] = surface1;
100 colors[ImGuiCol_BorderShadow] = ImVec4(0.0f, 0.0f, 0.0f, 0.0f);
101 colors[ImGuiCol_FrameBg] = surface0;
102 colors[ImGuiCol_FrameBgHovered] = surface1;
103 colors[ImGuiCol_FrameBgActive] = surface2;
104 colors[ImGuiCol_TitleBg] = mantle;
105 colors[ImGuiCol_TitleBgActive] = surface0;
106 colors[ImGuiCol_TitleBgCollapsed] = mantle;
107 colors[ImGuiCol_MenuBarBg] = mantle;
108 colors[ImGuiCol_ScrollbarBg] = surface0;
109 colors[ImGuiCol_ScrollbarGrab] = surface2;
110 colors[ImGuiCol_ScrollbarGrabHovered] = overlay0;
111 colors[ImGuiCol_ScrollbarGrabActive] = overlay2;
112 colors[ImGuiCol_CheckMark] = green;
113 colors[ImGuiCol_SliderGrab] = sapphire;
114 colors[ImGuiCol_SliderGrabActive] = blue;
115 colors[ImGuiCol_Button] = surface0;
116 colors[ImGuiCol_ButtonHovered] = surface1;
117 colors[ImGuiCol_ButtonActive] = surface2;
118 colors[ImGuiCol_Header] = surface0;
119 colors[ImGuiCol_HeaderHovered] = surface1;
120 colors[ImGuiCol_HeaderActive] = surface2;
121 colors[ImGuiCol_Separator] = surface1;
122 colors[ImGuiCol_SeparatorHovered] = mauve;
123 colors[ImGuiCol_SeparatorActive] = mauve;
124 colors[ImGuiCol_ResizeGrip] = surface2;
125 colors[ImGuiCol_ResizeGripHovered] = mauve;
126 colors[ImGuiCol_ResizeGripActive] = mauve;
127 colors[ImGuiCol_Tab] = surface0;
128 colors[ImGuiCol_TabHovered] = surface2;
129 colors[ImGuiCol_TabActive] = surface1;
130 colors[ImGuiCol_TabUnfocused] = surface0;
131 colors[ImGuiCol_TabUnfocusedActive] = surface1;
132 colors[ImGuiCol_DockingPreview] = sapphire;
133 colors[ImGuiCol_DockingEmptyBg] = base;
134 colors[ImGuiCol_PlotLines] = blue;
135 colors[ImGuiCol_PlotLinesHovered] = peach;
136 colors[ImGuiCol_PlotHistogram] = teal;
137 colors[ImGuiCol_PlotHistogramHovered] = green;
138 colors[ImGuiCol_TableHeaderBg] = surface0;
139 colors[ImGuiCol_TableBorderStrong] = surface1;
140 colors[ImGuiCol_TableBorderLight] = surface0;
141 colors[ImGuiCol_TableRowBg] = ImVec4(0.0f, 0.0f, 0.0f, 0.0f);
142 colors[ImGuiCol_TableRowBgAlt] = ImVec4(1.0f, 1.0f, 1.0f, 0.06f);
143 colors[ImGuiCol_TextSelectedBg] = surface2;
144 colors[ImGuiCol_DragDropTarget] = yellow;
145 colors[ImGuiCol_NavHighlight] = lavender;
146 colors[ImGuiCol_NavWindowingHighlight]= ImVec4(1.0f, 1.0f, 1.0f, 0.7f);
147 colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.8f, 0.8f, 0.8f, 0.2f);
148 colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.0f, 0.0f, 0.0f, 0.35f);
149 colors[ImGuiCol_Text] = text;
150 colors[ImGuiCol_TextDisabled] = subtext0;
151
152 // Rounded corners
153 style.WindowRounding = 6.0f;
154 style.ChildRounding = 6.0f;
155 style.FrameRounding = 4.0f;
156 style.PopupRounding = 4.0f;
157 style.ScrollbarRounding = 9.0f;
158 style.GrabRounding = 4.0f;
159 style.TabRounding = 4.0f;
160
161 // Padding and spacing
162 style.WindowPadding = ImVec2(8.0f, 8.0f);
163 style.FramePadding = ImVec2(5.0f, 3.0f);
164 style.ItemSpacing = ImVec2(8.0f, 4.0f);
165 style.ItemInnerSpacing = ImVec2(4.0f, 4.0f);
166 style.IndentSpacing = 21.0f;
167 style.ScrollbarSize = 14.0f;
168 style.GrabMinSize = 10.0f;
169
170 // Borders
171 style.WindowBorderSize = 1.0f;
172 style.ChildBorderSize = 1.0f;
173 style.PopupBorderSize = 1.0f;
174 style.FrameBorderSize = 0.0f;
175 style.TabBorderSize = 0.0f;
176}
177
178//
179// Subsystem interface
180//
181
182std::expected<void, Subsystem::Error> Gui::initialize()
183{
184 if (this->is_initialized()) return {};
185
186 IMGUI_CHECKVERSION();
187 ImGui::CreateContext();
188 ImPlot::CreateContext();
189 ImGuiIO &io = ImGui::GetIO();
190
191 // Enable Keyboard and Gamepad Controls
192 io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
193 io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;
194 io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
195
196 // Setup Platform/Renderer backends
197 ImGui_ImplGlfw_InitForOpenGL((GLFWwindow*)Window::get_window(), true);
198 ImGui_ImplOpenGL3_Init();
199 ImGui::SetNextWindowPos(ImVec2(0, 0));
200 setup_style();
201
202 Gui::initialized = true;
203 INFO("{} initialized", Gui::subsystem_name);
204 return {};
205}
206
207std::expected<void, Subsystem::Error> Gui::terminate()
208{
209 if (!this->is_initialized()) return {};
210
211 ImGui_ImplOpenGL3_Shutdown();
212 ImGui_ImplGlfw_Shutdown();
213
214 ImPlot::DestroyContext();
215 ImGui::DestroyContext();
216
217 Gui::initialized = false;
218 INFO("{}: terminated", Gui::subsystem_name);
219 return {};
220}
221
222std::string Gui::name()
223{
224 return Gui::subsystem_name;
225}
226
227bool Gui::is_initialized()
228{
229 return Gui::initialized;
230}
231
232//
233// Member functions
234//
235
236Gui &Gui::instance()
237{
238 static Gui _gui;
239 return _gui;
240}
241
242void Gui::load_font(const std::filesystem::path &path, float size)
243{
244 ImGuiIO& io = ImGui::GetIO();
245 Gui::font = io.Fonts->AddFontFromFileTTF(path.string().c_str(), size);
246}
247
248void Gui::push_font(ImFont *f)
249{
250 ImGui::PushFont(f);
251}
252
253void Gui::pop_font()
254{
255 ImGui::PopFont();
256}
257
258void Gui::new_frame(tenno::shared_ptr<FrameBuffer> fb, std::string name)
259{
260 ImGui_ImplOpenGL3_NewFrame();
261 ImGui_ImplGlfw_NewFrame();
262 ImGui::NewFrame();
263
264 ImGui::DockSpaceOverViewport(0, ImGui::GetMainViewport());
265
266 //
267 // Game window
268 //
269
270 ImGui::SetNextWindowSize(ImVec2(Window::get_width() * 0.8,
271 Window::get_width() * 0.8),
272 ImGuiCond_FirstUseEver);
273 ImGui::Begin(name.c_str());
274
275 float window_width = ImGui::GetContentRegionAvail().x;
276 float window_height = ImGui::GetContentRegionAvail().y;
277
278 fb->rescale(window_width, window_height);
279
280 ImGui::Image((void *) (intptr_t) fb->texture_id,
281 ImGui::GetContentRegionAvail(), ImVec2(0, 1), ImVec2(1, 0));
282
283 ImGui::End();
284 return;
285}
286
287void Gui::render()
288{
289 ImGui::Render();
290 ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
291 return;
292}
293
294static float gui_debug_last_time = 0;
295
296void Gui::debug_stats()
297{
298 ImGui::Begin("Stats");
299
300 //
301 // FPS information
302 //
303
304 static TimePlotBuffer fps_data;
305
306 auto time = Window::get_time();
307 const float interval = 0.1f; // seconds
308
309 float fps = time.get_fps();
310
311 if (time.elapsed > gui_debug_last_time + interval)
312 {
313 gui_debug_last_time = time.elapsed;
314 fps_data.add_point(time.elapsed, fps);
315 }
316
317 const ImPlotAxisFlags flags = 0;
318 static float history = 10.0f;
319 ImGui::SliderFloat("History", &history, 1, 60, "%.1f s");
320
321 if (ImPlot::BeginPlot("##FPS", ImVec2(-1,ImGui::GetTextLineHeight()*10)))
322 {
323 ImPlot::SetupAxes(nullptr, nullptr, flags, flags);
324
325 // X axis limit
326 ImPlot::SetupAxisLimits(ImAxis_X1, gui_debug_last_time - history,
327 gui_debug_last_time, ImGuiCond_Always);
328 // Y axis limit
329 ImPlot::SetupAxisLimits(ImAxis_Y1, 0, 100);
330
331 ImPlotSpec spec;
332 spec.Offset = fps_data.offset;
333 spec.Stride = 2 * sizeof(float);
334 spec.FillAlpha = 0.5f;
335 ImPlot::PlotShaded("FPS", &fps_data.data[0].x, &fps_data.data[0].y,
336 fps_data.data.size(), -INFINITY, spec);
337 ImPlot::EndPlot();
338 }
339
340 ImGui::Separator();
341
342 //
343 // Memory information
344 //
345
346 ImGui::Text("Tot GlBuffer Memory: %f MB",
347 Buffer::tot_memory / 1024.0 / 1024.0);
348 ImGui::Text("Tot GlFrameByffer Memory: %f MB",
349 FrameBuffer::tot_memory / 1024.0 / 1024.0);
350 ImGui::Text("Tot GlTexture Memory: %f MB",
351 Texture::tot_memory / 1024.0 / 1024.0);
352
353 ImGui::Separator();
354
355 //
356 // Asset manager information
357 //
358
359 ImGui::Text("Number of assets loaded with AssetManager");
360
361 ImGui::BulletText("Models: %ld", AssetManager::models.size());
362 ImGui::BulletText("Textures: %ld", AssetManager::textures.size());
363 ImGui::BulletText("Materials: %ld", AssetManager::materials.size());
364 ImGui::BulletText("Shaders: %ld", AssetManager::shaders.size());
365 ImGui::BulletText("Fonts: %ld", AssetManager::fonts.size());
366 ImGui::BulletText("Scenes: %ld", AssetManager::scenes.size());
367 ImGui::BulletText("Sound Assets: %ld", AssetManager::sound_assets.size());
368
369 ImGui::End();
370}
371
372//
373// Builder
374//
375
376Subsystem &Gui::Builder::build()
377{
378 return Gui::instance();
379}
380
381#endif // BRENTA_NO_IMGUI