GCC Code Coverage Report


./
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
0 of 402, 0 excluded
0.0%
Functions:
0 of 51, 0 excluded
0.0%
Branches:
0 of 678, 0 excluded
0.0%

apps/runner/src/eu/runner/main.cc
Line Branch Exec Source
1 #include <utility>
2
3 #include "SDL.h"
4
5 // #include <string>
6
7 #include "eu/log/log.h"
8 #include "eu/base/memorychunk.h"
9
10 #include "eu/core/geom.builder.h"
11 #include "eu/core/geom.h"
12
13 #include "eu/io/file.h"
14 #include "eu/io/mesh.h"
15
16 #include "eu/render/canvas.h"
17 #include "eu/render/state.h"
18 #include "eu/render/font.h"
19 #include "eu/render/opengl_utils.h"
20 #include "eu/render/texture.io.h"
21 #include "eu/render/camera.h"
22 #include "eu/render/compiledmesh.h"
23 #include "eu/render/enable_high_performance_graphics.h"
24 #include "eu/render/postproc.h"
25 #include "eu/render/renderer.h"
26 #include "eu/render/world.h"
27
28 #include "eu/runner/script.h"
29 #include "eu/runner/input.h"
30 #include "eu/runner/entity.h"
31
32 #include "eu/kdl/kdl.h"
33
34
35 #if FF_HAS(EU_DEBUG_RUNNER)
36 #include "eu/imgui/ui.h"
37
38 #include "dear_imgui/imgui.h"
39 #include "dear_imgui/backends/imgui_impl_sdl2.h"
40 #include "dear_imgui/backends/imgui_impl_opengl3.h"
41
42 static void imgui_color(const char* const label, eu::Rgb* color)
43 {
44 ImGui::ColorEdit3(label, &color->r);
45 }
46 #endif
47
48
49 ENABLE_HIGH_PERFORMANCE_GRAPHICS
50
51 using namespace eu;
52 using namespace std::string_view_literals;
53
54 // todo(Gustav): move to io
55 eu::render::ShaderSource load_shader(const std::string& name)
56 {
57 return {
58 .vertex = eu::io::string_from_file(name + ".vert.glsl"),
59 .fragment = eu::io::string_from_file(name + ".frag.glsl")
60 };
61 }
62
63 eu::Size size_from_v2(const eu::v2& v)
64 {
65 return eu::Size{ .width = eu::int_from_float(v.x), .height = eu::int_from_float(v.y) };
66 }
67
68 std::shared_ptr<eu::render::Texture2d> load_texture(const std::string& path, eu::render::ColorData cd,
69 eu::render::TextureEdge texture_edge = eu::render::TextureEdge::repeat,
70 eu::render::Transparency transparency = eu::render::Transparency::exclude)
71 {
72 const auto bin = eu::io::bytes_from_file(path);
73 return std::make_shared<eu::render::Texture2d>(
74 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)
75 );
76 }
77
78 std::shared_ptr<eu::render::Texture2d> create_texture(DEBUG_LABEL_ARG_MANY eu::core::SingleColor pixel_color, eu::render::ColorData cd)
79 {
80 return std::make_shared<eu::render::Texture2d>(
81 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)
82 );
83 }
84
85 struct Level
86 {
87 std::unordered_map<std::string, std::unique_ptr<eu::render::CompiledMesh>> meshes;
88 std::vector<std::unique_ptr<render::MeshInWorld>> instances;
89
90 void load(const std::string& path, render::World* world, std::shared_ptr<render::DefaultMaterial> material, const core::CompiledGeomVertexAttributes& layout)
91 {
92 // todo(Gustav): this is very hacky but just something so we can play a level
93 v3 offset = { 0.0f, 0.0f, 0.0f };
94 v3 size = { 1.0f, 1.0f, 1.0f };
95
96 const auto doc = kdl::parse(eu::io::string_from_file(path));
97 for (const auto& node: doc)
98 {
99 if (node.name() == "mesh")
100 {
101 const auto key = node.args()[0].as_string();
102 const auto file = node.args()[1].as_string();
103
104 const auto mesh = eu::io::mesh_from_file(file);
105 auto compiled = eu::render::compile_mesh(USE_DEBUG_LABEL_MANY(file) mesh, layout);
106
107 meshes[key] = std::make_unique<eu::render::CompiledMesh>(compiled);
108 }
109 else if (node.name() == "offset")
110 {
111 const auto x = node.args()[0].as_number().as<float>();
112 const auto y = node.args()[1].as_number().as<float>();
113 const auto z = node.args()[2].as_number().as<float>();
114 offset = { x, y, z };
115 }
116 else if (node.name() == "cell_size")
117 {
118 const auto x = node.args()[0].as_number().as<float>();
119 const auto y = node.args()[1].as_number().as<float>();
120 const auto z = node.args()[2].as_number().as<float>();
121 size = {x, y, z};
122 }
123 else if (node.name() == "item")
124 {
125 const auto x = node.args()[0].as_number().as<float>();
126 const auto y = node.args()[1].as_number().as<float>();
127 const auto z = node.args()[2].as_number().as<float>();
128 const v3 pos = { x*size.x, y*size.y, z*size.z };
129 const auto item = node.properties().at("item").as_string();
130 const auto found = meshes.find(item);
131 if (found != meshes.end())
132 {
133 render::MeshInWorld car;
134 car.add_to_world(found->second.get(), world, material);
135 car.set_transform(eu::render::transform_from_rotation(offset + pos, Ypr{0_deg, 0_deg, 0_deg}));
136 }
137 else
138 {
139 LOG_WARN("Mesh '{}' not found for item in level file", item);
140 }
141 }
142 else
143 {
144 LOG_WARN("Unknown node in level file: '{}'", node.name());
145 }
146 }
147 }
148 };
149
150
151 struct Time
152 {
153 Uint64 now = SDL_GetPerformanceCounter();
154 Uint64 last = 0;
155
156
157 float calculate()
158 {
159 last = now;
160 now = SDL_GetPerformanceCounter();
161
162 const auto diff = static_cast<float>(now - last);
163 const auto pfc = static_cast<float>(SDL_GetPerformanceFrequency());
164 return diff / pfc;
165 }
166 };
167
168
169 struct MeshSpatialComponent : runner::SpatialComponent
170 {
171 render::MeshInWorld car;
172
173 eu::v3 position = { 0.0f, -1.8f, -10.0f };
174 eu::Ypr rotation = { .yaw = 30.0_deg, .pitch = 30.0_deg, .roll = 30.0_deg };
175
176 const char* display() override
177 {
178 return "MeshSpatialComponent";
179 }
180
181 void imgui() override
182 {
183 runner::SpatialComponent::imgui();
184 imgui::drag("Position", &position);
185 imgui::drag("Rotation", &rotation);
186 }
187
188 void update_transform()
189 {
190 set_transform(eu::render::transform_from_rotation(position, rotation));
191 car.set_transform(get_transform());
192 }
193
194 MeshSpatialComponent(render::CompiledMesh* mesh, render::World* render_world, std::shared_ptr<render::Material> material)
195 {
196 car.add_to_world(mesh, render_world, std::move(material));
197 update_transform();
198 }
199
200 EU_DEC_COMPONENT_TYPE();
201 };
202
203 EU_IMP_COMPONENT_TYPE(MeshSpatialComponent, runner::Component)
204
205 void boolean(const char* label, bool b)
206 {
207 ImGui::TextUnformatted(label);
208 ImGui::SameLine();
209 ImGui::TextUnformatted(b ? "TRUE" : "false");
210 }
211
212 struct InputSystem : runner::EntitySystem
213 {
214 runner::Input* input;
215 MeshSpatialComponent* mesh = nullptr;
216
217 const char* display() override
218 {
219 return "Input";
220 }
221
222 void imgui() override
223 {
224 boolean("has mesh", mesh != nullptr);
225 }
226
227 explicit InputSystem(runner::Input* in) : input(in)
228 {
229 }
230
231 runner::UpdateStageAndPrio get_stage() override
232 {
233 return
234 {
235 .stage = runner::UpdateStage::before_physics,
236 .prio = 100
237 };
238 }
239
240 void on_root_changed(runner::SpatialComponent*) override
241 {
242 }
243
244 void add_component(runner::Component* component) override
245 {
246 if (MeshSpatialComponent* mc = runner::component_cast<MeshSpatialComponent>(component); mc)
247 {
248 mesh = mc;
249 }
250 }
251
252 void update(float dt) override
253 {
254 if (mesh && input && input->get_current_frame().binds[0].current > 0.5f)
255 {
256 mesh->position += kk::in * dt;
257 }
258
259 if (mesh)
260 {
261 mesh->update_transform();
262 }
263 }
264 };
265
266 struct TargetComponent : runner::Component
267 {
268 m4 target = m4_identity;
269
270 const char* display() override
271 {
272 return "target";
273 }
274
275 void imgui() override
276 {
277 // todo(Gustav): display matrix
278 }
279
280 EU_DEC_COMPONENT_TYPE();
281 };
282 EU_IMP_COMPONENT_TYPE(TargetComponent, runner::Component)
283
284 struct CameraSpatialComponent : runner::SpatialComponent
285 {
286 const char* display() override
287 {
288 return "camera spatial";
289 }
290
291 void imgui() override
292 {
293 runner::SpatialComponent::imgui();
294 }
295 // todo(Gustav): add extra camera settings...
296 EU_DEC_COMPONENT_TYPE();
297 };
298 EU_IMP_COMPONENT_TYPE(CameraSpatialComponent, runner::SpatialComponent);
299
300 struct FollowCameraSystem : runner::EntitySystem
301 {
302 runner::UpdateStageAndPrio get_stage() override
303 {
304 return
305 {
306 .stage = runner::UpdateStage::after_physics,
307 .prio = 100
308 };
309 }
310
311 TargetComponent* target = nullptr;
312 CameraSpatialComponent* camera = nullptr;
313
314 const char* display() override
315 {
316 return "Follow camera";
317 }
318
319 v3 offset = kk::out * 3;
320 void imgui() override
321 {
322 boolean("Target", target != nullptr);
323 boolean("camera", camera != nullptr);
324 imgui::drag("offset", &offset);
325 imgui::gear("gear", &offset.x);
326 }
327
328 void on_root_changed(runner::SpatialComponent*) override
329 {
330 }
331
332 void add_component(runner::Component* component) override
333 {
334 if (auto* tar = runner::component_cast<TargetComponent>(component))
335 {
336 target = tar;
337 LOG_INFO("Follow camera found target");
338 }
339 else if (auto* cam = runner::component_cast<CameraSpatialComponent>(component))
340 {
341 camera = cam;
342 LOG_INFO("Follow camera found camera");
343 }
344 }
345 void update(float) override
346 {
347 if (!target) { return; }
348 if (!camera) { return; }
349
350 const auto nt = target->target.get_translated(offset);
351 camera->set_transform(nt);
352 }
353 };
354
355 namespace tag
356 {
357 constexpr Hsh CameraSpatialSource = "camera_spatial_src_tag"sv;
358 constexpr Hsh CameraDestination = "camera_dst_tag"sv;
359 }
360
361 struct UpdateCameraTarget : runner::WorldSystem
362 {
363 runner::SpatialComponent* src_entity = nullptr;
364 TargetComponent* dst_entity = nullptr;
365
366 const char* display() override
367 {
368 return "Update camera target";
369 }
370
371 void imgui() override
372 {
373 boolean("src entity", src_entity != nullptr);
374 boolean("dst entity", dst_entity != nullptr);
375 }
376
377 runner::UpdateStageAndPrio get_stage() override
378 {
379 return {
380 .stage = runner::UpdateStage::after_physics,
381 .prio = 100
382 };
383 }
384
385 void on_root_changed(runner::Entity* entity, runner::SpatialComponent* component) override
386 {
387 if (entity->has_tag(tag::CameraSpatialSource))
388 {
389 LOG_INFO("Found spatial source");
390 src_entity = component;
391 }
392 }
393
394 void add_component(runner::Entity* entity, runner::Component* component) override
395 {
396 if (entity->has_tag(tag::CameraDestination))
397 {
398 if (auto* target = runner::component_cast<TargetComponent>(component))
399 {
400 LOG_INFO("Found camera destination on {}", entity->name);
401 dst_entity = target;
402 }
403 }
404 }
405
406 void update(float) override
407 {
408 if (!src_entity) { return; }
409 if (!dst_entity) { return; }
410
411 dst_entity->target = src_entity->get_transform();
412 }
413 };
414
415 struct CameraFetcherSystem : runner::WorldSystem
416 {
417 runner::UpdateStageAndPrio get_stage() override
418 {
419 return {
420 .stage = runner::UpdateStage::end_frame,
421 .prio = 100
422 };
423 }
424
425 CameraSpatialComponent* camera = nullptr;
426 m4 transform = m4_identity;
427
428 const char* display() override
429 {
430 return "Camera fetcher";
431 }
432
433 void imgui() override
434 {
435 boolean("camera", camera != nullptr);
436 //todo(Gustav): display transform
437 }
438
439 void add_component(runner::Entity*, runner::Component* component) override
440 {
441 if (auto cam = runner::component_cast<CameraSpatialComponent>(component))
442 {
443 LOG_INFO("Fetcher found camera");
444 camera = cam;
445 }
446 }
447
448 void on_root_changed(runner::Entity*, runner::SpatialComponent*) override
449 {
450 }
451
452 void update(float) override
453 {
454 if (!camera) return;
455 transform = camera->get_transform();
456 }
457
458 render::Camera fetch() const
459 {
460 render::Camera r;
461 r.position = transform.get_translation();
462 return r;
463 }
464 };
465
466 int main(int, char**)
467 {
468 int window_width = 1280;
469 int window_height = 720;
470 const char* glsl_version = "#version 130";
471
472 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_EVENTS) < 0) {
473 LOG_ERR("Error initializing SDL: {}", SDL_GetError());
474 return -1;
475 }
476
477 #if defined(__APPLE__)
478 // GL 3.2 Core + GLSL 150
479 const char* glsl_version = "#version 150";
480 SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG); // Always required on Mac
481 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
482 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
483 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
484 #else
485 // GL 3.0 + GLSL 130
486 // const char* glsl_version = "#version 130";
487 SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG);
488 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
489 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
490 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); // was 0 in dear imgui example??
491 #endif
492
493 SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
494 SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
495
496 SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
497 SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);
498
499 SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
500 SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
501 SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
502 SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
503
504 SDL_Window* window = SDL_CreateWindow("Runner", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
505 window_width, window_height, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_RESIZABLE);
506 if (!window) {
507 LOG_ERR("Error creating window: {}", SDL_GetError());
508 return -1;
509 }
510
511 auto* glContext = SDL_GL_CreateContext(window);
512 SDL_GetWindowSize(window, &window_width, &window_height);
513 if (!glContext) {
514 LOG_ERR("Error creating gl context: {}", SDL_GetError());
515 return -1;
516 }
517
518 /* OpenGL setup */
519 const int glad_result = gladLoadGLLoader(SDL_GL_GetProcAddress);
520 if (glad_result == 0)
521 {
522 LOG_ERR("Failed to init glad, error: {0}", glad_result);
523 return -1;
524 }
525
526 {
527 if (GLAD_GL_VERSION_1_0) {
528 const std::string gl_vendor = reinterpret_cast<const char*>(glGetString(GL_VENDOR));
529 const std::string gl_renderer = reinterpret_cast<const char*>(glGetString(GL_RENDERER));
530 const std::string gl_version = reinterpret_cast<const char*>(glGetString(GL_VERSION));
531 LOG_INFO("OpenGL Vendor: {0}", gl_vendor);
532 LOG_INFO("OpenGL Renderer: {0}", gl_renderer);
533 LOG_INFO("OpenGL Version: {0}", gl_version);
534 }
535
536 if (GLAD_GL_VERSION_2_0) {
537 const std::string gl_shading_language_version = reinterpret_cast<const char*>(glGetString(GL_SHADING_LANGUAGE_VERSION));
538 LOG_INFO("Version GLSL: {0}", gl_shading_language_version);
539 }
540 }
541
542 if (!GLAD_GL_VERSION_4_3)
543 {
544 LOG_ERR("OpenGL 4.3 not supported ({0}.{1} detected), aborting...", GLVersion.major, GLVersion.minor);
545 return -1;
546 }
547
548 #if FF_HAS(EU_DEBUG_RUNNER)
549 IMGUI_CHECKVERSION();
550 ImGui::CreateContext();
551 ImGuiIO& io = ImGui::GetIO(); (void)io;
552 ImGui::StyleColorsDark();
553 if (false == ImGui_ImplSDL2_InitForOpenGL(window, glContext))
554 {
555 LOG_ERR("Failed to init ImGui SDL2 backend");
556 return -1;
557 }
558 if (false == ImGui_ImplOpenGL3_Init(glsl_version))
559 {
560 LOG_ERR("Failed to init ImGui OpenGL3 backend");
561 return -1;
562 }
563
564 bool show_demo_window = true;
565 #endif
566
567 eu::render::State states;
568 eu::render::Render2 render{ &states };
569
570 eu::render::Assets assets;
571
572 assets.black = create_texture(SEND_DEBUG_LABEL_MANY("black") eu::core::color_from_rgba(0, 0, 0, 255), eu::render::ColorData::dont_care);
573 assets.white = create_texture(SEND_DEBUG_LABEL_MANY("white") eu::core::color_from_rgba(255, 255, 255, 255), eu::render::ColorData::dont_care);
574
575 assets.default_shader_source = load_shader("default_shader");
576 assets.skybox_shader_source = load_shader("skybox");
577
578 assets.pp_vert_glsl = eu::io::string_from_file("pp_vert.glsl");
579 assets.pp_realize_frag_glsl = eu::io::string_from_file("pp_realize.frag.glsl");
580 assets.pp_extract_frag_glsl = eu::io::string_from_file("pp_extract.frag.glsl");
581 assets.pp_ping_pong_blur_frag_glsl = eu::io::string_from_file("pp_ping_pong_blur.frag.glsl");
582
583 eu::render::Renderer renderer{ &states, &assets, eu::render::RenderSettings{} };
584 if (renderer.is_loaded() == false)
585 {
586 return -1;
587 }
588
589 eu::runner::Input input;
590 if (false == input.load_from_file("input.kdl"))
591 {
592 return -1;
593 }
594
595 eu::runner::Script script;
596 if (false == script.run_file("main.lax"))
597 {
598 return -1;
599 }
600
601 eu::render::World render_world;
602 eu::render::EffectStack effects;
603
604 #if FF_HAS(EU_DEBUG_RUNNER)
605 imgui::ImguiShaderCache imgui_shader_cache;
606 #endif
607
608 // add demo world
609 {
610 constexpr auto PLANE_SIZE = 100.0f;
611 auto plane_geom = eu::render::compile_geom(
612 USE_DEBUG_LABEL_MANY("plane")
613 eu::core::geom::create_xz_plane(PLANE_SIZE, PLANE_SIZE, false).to_geom(),
614 renderer.default_geom_layout()
615 );
616
617 auto material = renderer.make_default_material();
618 material->diffuse = load_texture("sprites/smoke.png", eu::render::ColorData::dont_care);
619 material->specular = assets.white;
620
621 auto plane = make_mesh_instance(plane_geom, material);
622 render_world.meshes.emplace_back(plane);
623 plane->transform = eu::m4::from_translation({ 0.0f, -3.0f, 0.0f });
624
625 render_world.lights.point_lights.emplace_back();
626 }
627
628 // add demo mesh
629 eu::runner::World runner_world;
630 std::unordered_map<std::string, std::unique_ptr<render::CompiledMesh>> meshes;
631 std::unordered_map<std::string, std::shared_ptr<eu::render::Texture2d>> textures;
632
633 const auto load_mesh = [&](const std::string& f) -> render::CompiledMesh*
634 {
635 const auto found = meshes.find(f);
636 if (found != meshes.end())
637 {
638 return found->second.get();
639 }
640 const auto mesh = eu::io::mesh_from_file(f);
641 const auto compiled = eu::render::compile_mesh(USE_DEBUG_LABEL_MANY(f) mesh, renderer.default_geom_layout());
642 auto data = std::make_unique<render::CompiledMesh>(compiled);
643 auto* ret = data.get();
644 meshes.insert_or_assign(f, std::move(data));
645 return ret;
646 };
647
648 const auto load_color_texture = [&](const std::string& f) -> std::shared_ptr<render::Texture2d>
649 {
650 const auto found = textures.find(f);
651 if (found != textures.end())
652 {
653 return found->second;
654 }
655 const auto loaded = load_texture(f, eu::render::ColorData::color_data);
656 textures.insert_or_assign(f, loaded);
657 return loaded;
658 };
659
660 runner_world.add_system(std::make_unique<UpdateCameraTarget>());
661
662 CameraFetcherSystem* camera_fetcher;
663 {
664 auto fetch = std::make_unique<CameraFetcherSystem>();
665 camera_fetcher = fetch.get();
666 runner_world.add_system(std::move(fetch));
667 }
668
669 {
670 auto* car = runner_world.add_entity("car", { tag::CameraSpatialSource });
671
672 // mesh component
673 // debug/axis.glb
674 // "models/vehicle-truck-purple.glb"
675 // "models/textures/colormap.png"
676 // render::CompiledMesh* mesh, render::World* render_world, std::shared_ptr<render::Material> material
677 auto* mesh = load_mesh("models/vehicle-truck-purple.glb");
678 auto material = renderer.make_default_material();
679 material->diffuse = load_color_texture("models/textures/colormap.png");
680 material->specular = assets.white;
681 auto comp = std::make_unique<MeshSpatialComponent>(mesh, &render_world, material);
682 car->set_root(comp.get());
683 car->add_component(std::move(comp));
684
685 // input component
686 car->add_system(std::make_unique<InputSystem>(&input));
687 }
688 {
689 auto* cam = runner_world.add_entity("camera", { tag::CameraDestination });
690
691 cam->add_component(std::make_unique<TargetComponent>());
692 auto spat = std::make_unique<CameraSpatialComponent>();
693 cam->set_root(spat.get());
694 cam->add_component(std::move(spat));
695 cam->add_system(std::make_unique<FollowCameraSystem>());
696 }
697 {
698 Level level;
699 auto material = renderer.make_default_material();
700 material->diffuse = load_color_texture("models/textures/colormap.png");
701 material->specular = assets.white;
702 level.load("level.kdl", &render_world, material, renderer.default_geom_layout());
703 }
704
705 LOG_INFO("Runner started");
706 bool running = true;
707 Time time;
708 while (running)
709 {
710 const auto dt = time.calculate();
711
712 runner_world.update(runner::UpdateStage::start_frame, dt);
713
714 SDL_Event e;
715 input.update();
716 while (SDL_PollEvent(&e) != 0)
717 {
718 #if FF_HAS(EU_DEBUG_RUNNER)
719 ImGui_ImplSDL2_ProcessEvent(&e);
720 #endif
721
722 switch (e.type)
723 {
724 case SDL_KEYDOWN:
725 case SDL_KEYUP:
726 input.on_key(e.key.keysym.sym, e.type == SDL_KEYDOWN);
727 break;
728 case SDL_WINDOWEVENT:
729 if (e.window.event == SDL_WINDOWEVENT_RESIZED || e.window.event == SDL_WINDOWEVENT_SIZE_CHANGED)
730 {
731 LOG_INFO("Resized");
732 SDL_GetWindowSize(window, &window_width, &window_height);
733 }
734 break;
735 case SDL_QUIT: running = false; break;
736 default:
737 // ignore other events
738 break;
739 }
740 }
741
742 // todo(Gustav): lookup unity frame, when updates are called and update stages
743 runner_world.update(runner::UpdateStage::before_physics, dt);
744 runner_world.update(runner::UpdateStage::physics, dt);
745 runner_world.update(runner::UpdateStage::after_physics, dt);
746
747 #if FF_HAS(EU_DEBUG_RUNNER)
748 ImGui_ImplOpenGL3_NewFrame();
749 ImGui_ImplSDL2_NewFrame();
750 ImGui::NewFrame();
751
752 if (show_demo_window)
753 {
754 ImGui::ShowDemoWindow(&show_demo_window);
755 }
756
757 if (ImGui::Begin("Renderer"))
758 {
759 imgui_color("Clear color", &render_world.clear_color);
760 eu::imgui::simple_gamma_slider("Gamma/Brightness", &renderer.settings.gamma, -1.0f);
761 ImGui::Checkbox("Use bloom", &renderer.settings.use_bloom);
762 ImGui::DragFloat("Bloom cutoff", &renderer.settings.bloom_cutoff, 0.001f);
763 ImGui::SliderFloat("Softness", &renderer.settings.bloom_softness, 0.0f, 1.0f);
764 ImGui::DragInt("Bloom blur steps", &renderer.settings.bloom_blur_steps);
765 effects.gui(&imgui_shader_cache);
766 }
767 ImGui::End();
768
769 // todo(Gustav): add introspection ui
770 if (ImGui::Begin("Demo"))
771 {
772 runner_world.gui();
773 // imgui::drag("Position", &position);
774 // imgui::drag("Rotation", &rotation);
775 }
776 ImGui::End();
777 #endif
778
779 eu::render::RenderCommand cmd{ .states = &states, .render = &render, .size = {.width = window_width, .height = window_height} };
780
781 // clear
782 {
783 {
784 const auto screen = eu::render::LayoutData{ .style = eu::render::ViewportStyle::extended,
785 .requested_width = 800, .requested_height = 600 };
786 cmd.clear(eu::colors::black, screen);
787 }
788 }
789
790 // render 3d world
791 {
792 const auto screen = eu::render::LayoutData{ .style = eu::render::ViewportStyle::black_bars,
793 .requested_width = 800, .requested_height = 600 };
794 cmd.clear(eu::colors::blue_sky, screen);
795 auto layer = eu::render::with_layer3(cmd, screen);
796 // todo(Gustav): get transform from camera entity
797 effects.update(dt);
798 eu::render::Camera camera = camera_fetcher->fetch();
799 effects.render(eu::render::PostProcArg{
800 .world = &render_world,
801 .window_size = size_from_v2(layer.screen.get_size()),
802 .final_rect = layer.screen,
803 .camera = &camera,
804 .renderer = &renderer
805 });
806 }
807
808 // render hud
809 {
810 const auto screen = eu::render::LayoutData{ .style = eu::render::ViewportStyle::black_bars,
811 .requested_width = 1280, .requested_height = 720 };
812
813 auto layer = eu::render::with_layer2(cmd, screen);
814 const float hud_size = 100.0f;
815 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 }));
816 }
817
818 #if FF_HAS(EU_DEBUG_RUNNER)
819 ImGui::Render();
820 ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
821 #endif
822 SDL_GL_SwapWindow(window);
823
824 runner_world.update(runner::UpdateStage::end_frame, dt);
825 }
826
827 #if FF_HAS(EU_DEBUG_RUNNER)
828 ImGui_ImplOpenGL3_Shutdown();
829 ImGui_ImplSDL2_Shutdown();
830 ImGui::DestroyContext();
831 #endif
832
833 LOG_INFO("Shutting down");
834 SDL_GL_DeleteContext(glContext);
835 SDL_DestroyWindow(window);
836 SDL_Quit();
837
838 return 0;
839 }
840