GCC Code Coverage Report


./
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
0 of 143, 0 excluded
0.0%
Functions:
0 of 12, 0 excluded
0.0%
Branches:
0 of 203, 0 excluded
0.0%

apps/editor/src/eu/editor/main.cc
Line Branch Exec Source
1 // todo(Gustav): look into glad support
2 #define SDL_FUNCTION_POINTER_IS_VOID_POINTER
3
4 #include "SDL.h"
5
6 #include <cassert>
7 #include <string>
8
9 #include "OpenSans-Regular.ttf.h"
10
11 #include "eu/log/log.h"
12 #include "eu/base/memorychunk.h"
13
14 #include "eu/render/canvas.h"
15 #include "eu/render/state.h"
16 #include "eu/render/font.h"
17 #include "eu/render/opengl_utils.h"
18 #include "eu/render/texture.io.h"
19
20 #include "eu/render/enable_high_performance_graphics.h"
21
22 #include "dear_imgui/imgui.h"
23 #include "dear_imgui/imgui_internal.h"
24
25 #include "dear_imgui/backends/imgui_impl_sdl3.h"
26 #include "dear_imgui/backends/imgui_impl_opengl3.h"
27
28 #include "eu/imgui/ui.h"
29 #include "eu/imgui/init.h"
30
31 #define SDL_MAIN_USE_CALLBACKS
32 #include <SDL3/SDL_main.h>
33
34 ENABLE_HIGH_PERFORMANCE_GRAPHICS
35
36 ✗ eu::MemoryChunk chunk_from_embed(const embedded_binary& binary)
37 {
38 ✗ return { .bytes = reinterpret_cast<const char*>(binary.data), .size = binary.size };
39 }
40
41 enum class AppState
42 {
43 continue_running, exit_failure, exit_ok
44 };
45
46 struct App
47 {
48 ✗ App() = default;
49 ~App();
50
51 App(const App&) = delete;
52 App(App&&) = delete;
53 void operator=(const App&) = delete;
54 void operator=(App&&) = delete;
55
56 AppState create();
57 AppState iterate();
58 AppState on_event(const SDL_Event& ev);
59 void destroy();
60
61 int window_width = 0;
62 int window_height = 0;
63 SDL_Window* window = nullptr;
64 SDL_GLContextState* glContext = nullptr;
65 bool show_demo_window = true;
66
67 std::unique_ptr<eu::render::State> states;
68 std::unique_ptr<eu::render::Render2> render;
69 };
70
71 ✗ App::~App()
72 {
73 ✗ destroy();
74 ✗ }
75
76 ✗ AppState App::create()
77 {
78 ✗ const char* glsl_version = "#version 130";
79
80 ✗ SDL_SetAppMetadata("Euphoria editor", "v0.1", "com.madeso.euphoria");
81
82 ✗ if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) == false)
83 {
84 ✗ LOG_ERR("Error initializing SDL: {}", SDL_GetError());
85 ✗ return AppState::exit_failure;
86 }
87
88 ✗ const auto app_scale = eu::imgui::calculate_app_scale();
89 ✗ std::tie(window_width, window_height) = eu::imgui::rescale_window(1280, 720, app_scale);
90
91 #if defined(__APPLE__)
92 // GL 3.2 Core + GLSL 150
93 const char* glsl_version = "#version 150";
94 SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG); // Always required on Mac
95 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
96 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
97 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
98 #else
99 // GL 3.0 + GLSL 130
100 // const char* glsl_version = "#version 130";
101 ✗ SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG);
102 ✗ SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
103 ✗ SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
104 ✗ SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); // was 0 in dear imgui example??
105 #endif
106
107 ✗ SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
108 ✗ SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
109
110 ✗ SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
111 ✗ SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);
112
113 ✗ SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
114 ✗ SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
115 ✗ SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
116 ✗ SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
117
118 ✗ window = SDL_CreateWindow("Editor sample",
119 window_width, window_height, SDL_WINDOW_OPENGL | SDL_WINDOW_HIGH_PIXEL_DENSITY | SDL_WINDOW_RESIZABLE);
120 ✗ if (!window)
121 {
122 ✗ LOG_ERR("Error creating window: {}", SDL_GetError());
123 ✗ return AppState::exit_failure;
124 }
125
126 ✗ glContext = SDL_GL_CreateContext(window);
127 ✗ SDL_GetWindowSize(window, &window_width, &window_height);
128 ✗ if (!glContext) {
129 ✗ LOG_ERR("Error creating gl context: {}", SDL_GetError());
130 ✗ return AppState::exit_failure;
131 }
132
133 /* OpenGL setup */
134 ✗ const int glad_result = gladLoadGLLoader(SDL_GL_GetProcAddress);
135 ✗ if (glad_result == 0)
136 {
137 ✗ LOG_ERR("Failed to init glad, error: {0}", glad_result);
138 ✗ return AppState::exit_failure;
139 }
140
141 ✗ IMGUI_CHECKVERSION();
142 ✗ ImGui::CreateContext();
143 ✗ ImGuiIO& io = ImGui::GetIO();
144 ✗ ImGui::StyleColorsDark();
145 ✗ ImGui_ImplSDL3_InitForOpenGL(window, glContext);
146 ✗ ImGui_ImplOpenGL3_Init(glsl_version);
147
148 {
149 ✗ const std::string gl_vendor = reinterpret_cast<const char*>(glGetString(GL_VENDOR));
150 ✗ const std::string gl_renderer = reinterpret_cast<const char*>(glGetString(GL_RENDERER));
151 ✗ const std::string gl_version = reinterpret_cast<const char*>(glGetString(GL_VERSION));
152 ✗ const std::string gl_shading_language_version = reinterpret_cast<const char*>(glGetString(GL_SHADING_LANGUAGE_VERSION));
153
154 ✗ LOG_INFO("Vendor: {0}", gl_vendor);
155 ✗ LOG_INFO("Renderer: {0}", gl_renderer);
156 ✗ LOG_INFO("Version OpenGL: {0}", gl_version);
157 ✗ LOG_INFO("Version GLSL: {0}", gl_shading_language_version);
158 ✗ }
159
160 {
161 ✗ ImFontConfig config;
162 ✗ config.FontDataOwnedByAtlas = false;
163 ✗ ImFont* font = io.Fonts->AddFontFromMemoryTTF(
164 const_cast<void*>(static_cast<const void*>(OPENSANS_REGULAR_TTF_data)),
165 OPENSANS_REGULAR_TTF_size,
166 18.0f,
167 &config
168 );
169 ✗ IM_ASSERT(font != nullptr);
170
171 ✗ eu::imgui::setup_scale(app_scale);
172 }
173
174 ✗ states.reset(new eu::render::State());
175 ✗ render.reset(new eu::render::Render2(states.get()));
176
177 ✗ LOG_INFO("Editor started");
178 ✗ return AppState::continue_running;
179 }
180
181 ✗ AppState App::on_event(const SDL_Event& ev)
182 {
183 ✗ ImGui_ImplSDL3_ProcessEvent(&ev);
184 ✗ switch (ev.type)
185 {
186 ✗ case SDL_EVENT_WINDOW_RESIZED:
187 ✗ LOG_INFO("Resized");
188 ✗ SDL_GetWindowSize(window, &window_width, &window_height);
189 ✗ break;
190 ✗ case SDL_EVENT_QUIT:
191 ✗ return AppState::exit_ok;
192 ✗ default:
193 // ignore other events
194 ✗ break;
195 }
196
197 ✗ return AppState::continue_running;
198 }
199
200 ✗ AppState App::iterate()
201 {
202 ✗ ImGui_ImplOpenGL3_NewFrame();
203 ✗ ImGui_ImplSDL3_NewFrame();
204 ✗ ImGui::NewFrame();
205
206 ✗ if (show_demo_window)
207 {
208 ✗ ImGui::ShowDemoWindow(&show_demo_window);
209 }
210
211 ✗ if (ImGui::Begin("Properties"))
212 {
213 static eu::v3 pos = {0,0,0};
214 static eu::v3 rot = {0, 0, 0};
215 static eu::v3 scale = {1, 1, 1};
216
217 ✗ eu::imgui::label("Position");
218 ✗ ImGui::DragFloat3("##Position", pos.get_data_ptr());
219
220 ✗ eu::imgui::label("Rotation");
221 ✗ ImGui::DragFloat3("##Rotation", rot.get_data_ptr());
222
223 ✗ eu::imgui::label("Scale");
224 ✗ ImGui::DragFloat3("##Scale", scale.get_data_ptr());
225
226 ✗ eu::imgui::centered_button("Add component");
227 }
228 ✗ ImGui::End();
229
230 {
231 ✗ eu::render::RenderCommand cmd {.states = states.get(), .render = render.get(), .size = {.width = window_width, .height = window_height} };
232
233 // todo(Gustav): provide a pixel layout
234 ✗ const auto screen = eu::render::LayoutData{ .style = eu::render::ViewportStyle::extended,
235 ✗ .requested_width = static_cast<float>(window_width), .requested_height = static_cast<float>(window_height) };
236
237 ✗ cmd.clear(eu::colors::blue_sky, screen);
238
239 ✗ auto layer = eu::render::with_layer2(cmd, screen);
240 ✗ eu::render::Quad{ .tint = eu::colors::green_bluish }.draw(layer.batch, layer.viewport_aabb_in_worldspace.get_bottom(50));
241 ✗ }
242
243 ✗ ImGui::Render();
244 ✗ ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
245 ✗ SDL_GL_SwapWindow(window);
246
247 ✗ return AppState::continue_running;
248 }
249
250 ✗ void App::destroy()
251 {
252 ✗ render.reset();
253 ✗ states.reset();
254
255 ✗ ImGui_ImplOpenGL3_Shutdown();
256 ✗ ImGui_ImplSDL3_Shutdown();
257 ✗ ImGui::DestroyContext();
258
259 ✗ LOG_INFO("Shutting down");
260 ✗ SDL_GL_DestroyContext(glContext);
261 ✗ glContext = nullptr;
262
263 ✗ SDL_DestroyWindow(window);
264 ✗ window = nullptr;
265 ✗ SDL_Quit();
266 ✗ }
267
268 // binding
269 ✗ SDL_AppResult to_sdl_result(AppState state)
270 {
271 ✗ switch (state)
272 {
273 ✗ case AppState::continue_running: return SDL_APP_CONTINUE;
274 ✗ case AppState::exit_failure: return SDL_APP_FAILURE;
275 ✗ case AppState::exit_ok: return SDL_APP_SUCCESS;
276 }
277
278 ✗ return SDL_APP_FAILURE;
279 }
280
281 ✗ SDL_AppResult SDL_AppInit(void** out_app, int, char**)
282 {
283 ✗ auto app = new App();
284 ✗ const auto create_result = app->create();
285 ✗ if (create_result == AppState::continue_running)
286 {
287 ✗ *out_app = app;
288 ✗ return SDL_APP_CONTINUE;
289 }
290
291 ✗ delete app;
292 ✗ return to_sdl_result(create_result);
293 }
294
295 ✗ SDL_AppResult SDL_AppIterate(void* app_arg)
296 {
297 ✗ auto app = static_cast<App*>(app_arg);
298 ✗ const auto result = app->iterate();
299 ✗ return to_sdl_result(result);
300 }
301
302 ✗ SDL_AppResult SDL_AppEvent(void* app_arg, SDL_Event* ev)
303 {
304 ✗ auto app = static_cast<App*>(app_arg);
305 ✗ const auto result = app->on_event(*ev);
306 ✗ return to_sdl_result(result);
307 }
308
309 ✗ void SDL_AppQuit(void* app_arg, SDL_AppResult result)
310 {
311 ✗ if (result == SDL_APP_FAILURE)
312 {
313 ✗ LOG_ERR("App crashed :(");
314 }
315
316 ✗ auto app = static_cast<App*>(app_arg);
317 ✗ delete app;
318 ✗ app = nullptr;
319 ✗ }
320