GCC Code Coverage Report


./
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
0 of 239, 0 excluded
0.0%
Functions:
0 of 8, 0 excluded
0.0%
Branches:
0 of 512, 0 excluded
0.0%

apps/runner/src/eu/runner/main.cc
Line Branch Exec Source
1 #include "SDL.h"
2
3 // #include <string>
4
5 #include "eu/log/log.h"
6 #include "eu/base/memorychunk.h"
7
8 #include "eu/core/geom.builder.h"
9 #include "eu/core/geom.h"
10
11 #include "eu/io/file.h"
12 #include "eu/io/mesh.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 #include "eu/render/camera.h"
20 #include "eu/render/compiledmesh.h"
21 #include "eu/render/enable_high_performance_graphics.h"
22 #include "eu/render/postproc.h"
23 #include "eu/render/renderer.h"
24 #include "eu/render/world.h"
25
26 #include "eu/runner/script.h"
27 #include "eu/runner/input.h"
28
29 #include "eu/kdl/kdl.h"
30
31
32 #if FF_HAS(EU_DEBUG_RUNNER)
33 #include "eu/imgui/ui.h"
34
35 #include "dear_imgui/imgui.h"
36 #include "dear_imgui/backends/imgui_impl_sdl2.h"
37 #include "dear_imgui/backends/imgui_impl_opengl3.h"
38
39 static void imgui_color(const char* const label, eu::Rgb* color)
40 {
41 ImGui::ColorEdit3(label, &color->r);
42 }
43 #endif
44
45
46 ENABLE_HIGH_PERFORMANCE_GRAPHICS
47
48 using namespace eu;
49
50 // todo(Gustav): move to io
51 eu::render::ShaderSource load_shader(const std::string& name)
52 {
53 return {
54 .vertex = eu::io::string_from_file(name + ".vert.glsl"),
55 .fragment = eu::io::string_from_file(name + ".frag.glsl")
56 };
57 }
58
59 eu::Size size_from_v2(const eu::v2& v)
60 {
61 return eu::Size{ .width = eu::int_from_float(v.x), .height = eu::int_from_float(v.y) };
62 }
63
64 std::shared_ptr<eu::render::Texture2d> load_texture(const std::string& path, eu::render::ColorData cd,
65 eu::render::TextureEdge texture_edge = eu::render::TextureEdge::repeat,
66 eu::render::Transparency transparency = eu::render::Transparency::exclude)
67 {
68 const auto bin = eu::io::bytes_from_file(path);
69 return std::make_shared<eu::render::Texture2d>(
70 load_image_from_embedded(SEND_DEBUG_LABEL_MANY(path) embedded_binary { reinterpret_cast<const unsigned int*>(bin.data()), static_cast<unsigned int>(bin.size()) }, texture_edge, eu::render::TextureRenderStyle::mipmap, transparency, cd)
71 );
72 }
73
74 std::shared_ptr<eu::render::Texture2d> create_texture(DEBUG_LABEL_ARG_MANY eu::core::SingleColor pixel_color, eu::render::ColorData cd)
75 {
76 return std::make_shared<eu::render::Texture2d>(
77 load_image_from_color(SEND_DEBUG_LABEL_MANY(debug_label) pixel_color, eu::render::TextureEdge::repeat, eu::render::TextureRenderStyle::pixel, eu::render::Transparency::exclude, cd)
78 );
79 }
80
81 struct Level
82 {
83 std::unordered_map<std::string, std::unique_ptr<eu::render::CompiledMesh>> meshes;
84 std::vector<std::unique_ptr<render::MeshInWorld>> instances;
85
86 void load(const std::string& path, render::World* world, std::shared_ptr<render::DefaultMaterial> material, const core::CompiledGeomVertexAttributes& layout)
87 {
88 // todo(Gustav): this is very hacky but just something so we can play a level
89 v3 offset = { 0.0f, 0.0f, 0.0f };
90 v3 size = { 1.0f, 1.0f, 1.0f };
91
92 const auto doc = kdl::parse(eu::io::string_from_file(path));
93 for (const auto& node: doc)
94 {
95 if (node.name() == "mesh")
96 {
97 const auto key = node.args()[0].as_string();
98 const auto file = node.args()[1].as_string();
99
100 const auto mesh = eu::io::mesh_from_file(file);
101 auto compiled = eu::render::compile_mesh(USE_DEBUG_LABEL_MANY(file) mesh, layout);
102
103 meshes[key] = std::make_unique<eu::render::CompiledMesh>(compiled);
104 }
105 else if (node.name() == "offset")
106 {
107 const auto x = node.args()[0].as_number().as<float>();
108 const auto y = node.args()[1].as_number().as<float>();
109 const auto z = node.args()[2].as_number().as<float>();
110 offset = { x, y, z };
111 }
112 else if (node.name() == "cell_size")
113 {
114 const auto x = node.args()[0].as_number().as<float>();
115 const auto y = node.args()[1].as_number().as<float>();
116 const auto z = node.args()[2].as_number().as<float>();
117 size = {x, y, z};
118 }
119 else if (node.name() == "item")
120 {
121 const auto x = node.args()[0].as_number().as<float>();
122 const auto y = node.args()[1].as_number().as<float>();
123 const auto z = node.args()[2].as_number().as<float>();
124 const v3 pos = { x*size.x, y*size.y, z*size.z };
125 const auto item = node.properties().at("item").as_string();
126 const auto found = meshes.find(item);
127 if (found != meshes.end())
128 {
129 render::MeshInWorld car;
130 car.add_to_world(found->second.get(), world, material);
131 car.set_transform(eu::render::transform_from_rotation(offset + pos, Ypr{0_deg, 0_deg, 0_deg}));
132 }
133 else
134 {
135 LOG_WARN("Mesh '{}' not found for item in level file", item);
136 }
137 }
138 else
139 {
140 LOG_WARN("Unknown node in level file: '{}'", node.name());
141 }
142 }
143 }
144 };
145
146
147 struct Time
148 {
149 Uint64 now = SDL_GetPerformanceCounter();
150 Uint64 last = 0;
151
152
153 float calculate()
154 {
155 last = now;
156 now = SDL_GetPerformanceCounter();
157
158 const auto diff = static_cast<float>(now - last);
159 const auto pfc = static_cast<float>(SDL_GetPerformanceFrequency());
160 return diff / pfc;
161 }
162
163 };
164
165
166 int main(int, char**)
167 {
168 int window_width = 1280;
169 int window_height = 720;
170 const char* glsl_version = "#version 130";
171
172 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_EVENTS) < 0) {
173 LOG_ERR("Error initializing SDL: {}", SDL_GetError());
174 return -1;
175 }
176
177 #if defined(__APPLE__)
178 // GL 3.2 Core + GLSL 150
179 const char* glsl_version = "#version 150";
180 SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG); // Always required on Mac
181 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
182 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
183 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
184 #else
185 // GL 3.0 + GLSL 130
186 // const char* glsl_version = "#version 130";
187 SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG);
188 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
189 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
190 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); // was 0 in dear imgui example??
191 #endif
192
193 SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
194 SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
195
196 SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
197 SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);
198
199 SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
200 SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
201 SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
202 SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
203
204 SDL_Window* window = SDL_CreateWindow("Runner", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
205 window_width, window_height, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_RESIZABLE);
206 if (!window) {
207 LOG_ERR("Error creating window: {}", SDL_GetError());
208 return -1;
209 }
210
211 auto* glContext = SDL_GL_CreateContext(window);
212 SDL_GetWindowSize(window, &window_width, &window_height);
213 if (!glContext) {
214 LOG_ERR("Error creating gl context: {}", SDL_GetError());
215 return -1;
216 }
217
218 /* OpenGL setup */
219 const int glad_result = gladLoadGLLoader(SDL_GL_GetProcAddress);
220 if (glad_result == 0)
221 {
222 LOG_ERR("Failed to init glad, error: {0}", glad_result);
223 return -1;
224 }
225
226 {
227 if (GLAD_GL_VERSION_1_0) {
228 const std::string gl_vendor = reinterpret_cast<const char*>(glGetString(GL_VENDOR));
229 const std::string gl_renderer = reinterpret_cast<const char*>(glGetString(GL_RENDERER));
230 const std::string gl_version = reinterpret_cast<const char*>(glGetString(GL_VERSION));
231 LOG_INFO("OpenGL Vendor: {0}", gl_vendor);
232 LOG_INFO("OpenGL Renderer: {0}", gl_renderer);
233 LOG_INFO("OpenGL Version: {0}", gl_version);
234 }
235
236 if (GLAD_GL_VERSION_2_0) {
237 const std::string gl_shading_language_version = reinterpret_cast<const char*>(glGetString(GL_SHADING_LANGUAGE_VERSION));
238 LOG_INFO("Version GLSL: {0}", gl_shading_language_version);
239 }
240 }
241
242 if (!GLAD_GL_VERSION_4_3)
243 {
244 LOG_ERR("OpenGL 4.3 not supported ({0}.{1} detected), aborting...", GLVersion.major, GLVersion.minor);
245 return -1;
246 }
247
248 #if FF_HAS(EU_DEBUG_RUNNER)
249 IMGUI_CHECKVERSION();
250 ImGui::CreateContext();
251 ImGuiIO& io = ImGui::GetIO(); (void)io;
252 ImGui::StyleColorsDark();
253 if (false == ImGui_ImplSDL2_InitForOpenGL(window, glContext))
254 {
255 LOG_ERR("Failed to init ImGui SDL2 backend");
256 return -1;
257 }
258 if (false == ImGui_ImplOpenGL3_Init(glsl_version))
259 {
260 LOG_ERR("Failed to init ImGui OpenGL3 backend");
261 return -1;
262 }
263
264 bool show_demo_window = true;
265 #endif
266
267 eu::render::State states;
268 eu::render::Render2 render{ &states };
269
270 eu::render::Assets assets;
271
272 assets.black = create_texture(SEND_DEBUG_LABEL_MANY("black") eu::core::color_from_rgba(0, 0, 0, 255), eu::render::ColorData::dont_care);
273 assets.white = create_texture(SEND_DEBUG_LABEL_MANY("white") eu::core::color_from_rgba(255, 255, 255, 255), eu::render::ColorData::dont_care);
274
275 assets.default_shader_source = load_shader("default_shader");
276 assets.skybox_shader_source = load_shader("skybox");
277
278 assets.pp_vert_glsl = eu::io::string_from_file("pp_vert.glsl");
279 assets.pp_realize_frag_glsl = eu::io::string_from_file("pp_realize.frag.glsl");
280 assets.pp_extract_frag_glsl = eu::io::string_from_file("pp_extract.frag.glsl");
281 assets.pp_ping_pong_blur_frag_glsl = eu::io::string_from_file("pp_ping_pong_blur.frag.glsl");
282
283 eu::render::Renderer renderer{ &states, &assets, eu::render::RenderSettings{} };
284 if (renderer.is_loaded() == false)
285 {
286 return -1;
287 }
288
289 eu::runner::Input input;
290 if (false == input.load_from_file("input.kdl"))
291 {
292 return -1;
293 }
294
295 eu::runner::Script script;
296 if (false == script.run_file("main.lax"))
297 {
298 return -1;
299 }
300
301 eu::render::World world;
302 eu::render::EffectStack effects;
303
304 #if FF_HAS(EU_DEBUG_RUNNER)
305 imgui::ImguiShaderCache imgui_shader_cache;
306 #endif
307
308 // add demo world
309 {
310 constexpr auto PLANE_SIZE = 100.0f;
311 auto plane_geom = eu::render::compile_geom(
312 USE_DEBUG_LABEL_MANY("plane")
313 eu::core::geom::create_xz_plane(PLANE_SIZE, PLANE_SIZE, false).to_geom(),
314 renderer.default_geom_layout()
315 );
316
317 auto material = renderer.make_default_material();
318 material->diffuse = load_texture("sprites/smoke.png", eu::render::ColorData::dont_care);
319 material->specular = assets.white;
320
321 auto plane = make_mesh_instance(plane_geom, material);
322 world.meshes.emplace_back(plane);
323 plane->transform = eu::m4::from_translation({ 0.0f, -3.0f, 0.0f });
324
325 world.lights.point_lights.emplace_back();
326 }
327
328 // add demo mesh
329 render::CompiledMesh carmesh;
330 render::MeshInWorld car;
331 eu::v3 position = { 0.0f, -1.8f, -10.0f };
332 eu::Ypr rotation = { .yaw = 30.0_deg, .pitch = 30.0_deg, .roll = 30.0_deg };
333 {
334 const auto mesh = eu::io::mesh_from_file("debug/axis.glb");
335 // const auto mesh = eu::io::mesh_from_file("models/vehicle-truck-purple.glb");
336 carmesh = eu::render::compile_mesh(USE_DEBUG_LABEL_MANY("truck") mesh, renderer.default_geom_layout());
337
338 auto material = renderer.make_default_material();
339 material->diffuse = load_texture("models/textures/colormap.png", eu::render::ColorData::color_data);
340 material->specular = assets.white;
341
342 car.add_to_world(&carmesh, &world, material);
343 car.set_transform(eu::render::transform_from_rotation(position, rotation));
344
345 Level level;
346 level.load("level.kdl", &world, material, renderer.default_geom_layout());
347 }
348
349 LOG_INFO("Runner started");
350 bool running = true;
351 Time time;
352 eu::render::Camera camera;
353 while (running)
354 {
355 const auto dt = time.calculate();
356 SDL_Event e;
357
358 input.update();
359 while (SDL_PollEvent(&e) != 0)
360 {
361 #if FF_HAS(EU_DEBUG_RUNNER)
362 ImGui_ImplSDL2_ProcessEvent(&e);
363 #endif
364
365 switch (e.type)
366 {
367 case SDL_KEYDOWN:
368 case SDL_KEYUP:
369 input.on_key(e.key.keysym.sym, e.type == SDL_KEYDOWN);
370 break;
371 case SDL_WINDOWEVENT:
372 if (e.window.event == SDL_WINDOWEVENT_RESIZED || e.window.event == SDL_WINDOWEVENT_SIZE_CHANGED)
373 {
374 LOG_INFO("Resized");
375 SDL_GetWindowSize(window, &window_width, &window_height);
376 }
377 break;
378 case SDL_QUIT: running = false; break;
379 default:
380 // ignore other events
381 break;
382 }
383 }
384
385 if (input.get_current_frame().binds[0].current > 0.5f)
386 {
387 position += kk::in * dt;
388 }
389
390 #if FF_HAS(EU_DEBUG_RUNNER)
391 ImGui_ImplOpenGL3_NewFrame();
392 ImGui_ImplSDL2_NewFrame();
393 ImGui::NewFrame();
394
395 if (show_demo_window)
396 {
397 ImGui::ShowDemoWindow(&show_demo_window);
398 }
399
400 if (ImGui::Begin("Renderer"))
401 {
402 imgui_color("Clear color", &world.clear_color);
403 eu::imgui::simple_gamma_slider("Gamma/Brightness", &renderer.settings.gamma, -1.0f);
404 ImGui::Checkbox("Use bloom", &renderer.settings.use_bloom);
405 ImGui::DragFloat("Bloom cutoff", &renderer.settings.bloom_cutoff, 0.001f);
406 ImGui::SliderFloat("Softness", &renderer.settings.bloom_softness, 0.0f, 1.0f);
407 ImGui::DragInt("Bloom blur steps", &renderer.settings.bloom_blur_steps);
408 effects.gui(&imgui_shader_cache);
409 }
410 ImGui::End();
411
412 if (ImGui::Begin("Demo"))
413 {
414 imgui::drag("Position", &position);
415 imgui::drag("Rotation", &rotation);
416 }
417 ImGui::End();
418 #endif
419
420 car.set_transform(eu::render::transform_from_rotation(position, rotation));
421
422 eu::render::RenderCommand cmd{ .states = &states, .render = &render, .size = {.width = window_width, .height = window_height} };
423
424 // clear
425 {
426 {
427 const auto screen = eu::render::LayoutData{ .style = eu::render::ViewportStyle::extended,
428 .requested_width = 800, .requested_height = 600 };
429 cmd.clear(eu::colors::black, screen);
430 }
431 }
432
433 // render 3d world
434 {
435 const auto screen = eu::render::LayoutData{ .style = eu::render::ViewportStyle::black_bars,
436 .requested_width = 800, .requested_height = 600 };
437 cmd.clear(eu::colors::blue_sky, screen);
438 auto layer = eu::render::with_layer3(cmd, screen);
439 camera.position = position + kk::out * 3;
440 effects.update(dt);
441 effects.render(eu::render::PostProcArg{
442 .world = &world,
443 .window_size = size_from_v2(layer.screen.get_size()),
444 .final_rect = layer.screen,
445 .camera = &camera,
446 .renderer = &renderer
447 });
448 }
449
450 // render hud
451 {
452 const auto screen = eu::render::LayoutData{ .style = eu::render::ViewportStyle::black_bars,
453 .requested_width = 1280, .requested_height = 720 };
454
455 auto layer = eu::render::with_layer2(cmd, screen);
456 const float hud_size = 100.0f;
457 eu::render::Quad{ .tint = eu::colors::blue_sky }.draw(layer.batch, layer.viewport_aabb_in_worldspace.get_bottom(hud_size).get_right(hud_size).with_inset(eu::Lrtb{ 5.0f }));
458 }
459
460 #if FF_HAS(EU_DEBUG_RUNNER)
461 ImGui::Render();
462 ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
463 #endif
464 SDL_GL_SwapWindow(window);
465 }
466
467 #if FF_HAS(EU_DEBUG_RUNNER)
468 ImGui_ImplOpenGL3_Shutdown();
469 ImGui_ImplSDL2_Shutdown();
470 ImGui::DestroyContext();
471 #endif
472
473 LOG_INFO("Shutting down");
474 SDL_GL_DeleteContext(glContext);
475 SDL_DestroyWindow(window);
476 SDL_Quit();
477
478 return 0;
479 }
480