LCOV - code coverage report
Current view: top level - examples/raycaster - main.cpp (source / functions) Coverage Total Hit
Test: Wren Engine Coverage Lines: 0.0 % 200 0
Test Date: 1980-01-01 00:00:00 Functions: 0.0 % 35 0

            Line data    Source code
       1              : #include <cstdlib>
       2              : #include <wren/application.hpp>
       3              : #include <wren/assets/manager.hpp>
       4              : #include <wren/physics/ray.hpp>
       5              : #include <wren/scene/components/collider.hpp>
       6              : #include <wren/scene/components/mesh.hpp>
       7              : #include <wren/scene/entity.hpp>
       8              : #include <wren/scene/scene.hpp>
       9              : 
      10              : #include "wren/logging/log.hpp"
      11              : #include "wren/scene/components/tag.hpp"
      12              : 
      13              : namespace components = wren::scene::components;
      14              : 
      15              : constexpr static std::array<std::array<int, 10>, 10> kMaps = {
      16              :     {
      17              :         {1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
      18              :         {1, 2, 0, 0, 1, 2, 0, 0, 3, 1},
      19              :         {1, 0, 0, 1, 0, 0, 0, 0, 0, 1},
      20              :         {1, 0, 0, 1, 0, 0, 0, 0, 0, 1},
      21              :         {1, 0, 0, 1, 0, 0, 0, 0, 0, 1},
      22              :         {1, 0, 0, 0, 0, 0, 0, 0, 0, 1},
      23              :         {1, 0, 0, 0, 0, 0, 0, 0, 0, 1},
      24              :         {1, 0, 0, 0, 0, 0, 0, 0, 0, 1},
      25              :         {1, 5, 0, 0, 0, 2, 0, 0, 4, 1},
      26              :         {1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
      27              :     },
      28              : };
      29              : 
      30              : constexpr static auto kGridScale = 1;
      31              : 
      32              : struct GameData {
      33              :   std::shared_ptr<wren::Application> app;
      34              :   std::shared_ptr<wren::scene::Scene> scene;
      35              :   wren::math::Mat4f ortho_proj;
      36              : 
      37              :   wren::math::Vec2f resolution;
      38              : 
      39              :   static constexpr auto kSpeed = 5.;
      40              : 
      41              :   wren::scene::components::Transform player;
      42              :   float player_rot = 0;
      43              : 
      44              :   std::vector<wren::scene::Entity> raycasted;
      45              :   std::unordered_map<wren::KeyCode, bool> key_inputs;
      46              : };
      47              : 
      48            0 : auto initialize(const std::shared_ptr<GameData>& data) -> wren::expected<void> {
      49            0 :   wren::log::info("Intializing shaders");
      50              : 
      51            0 :   std::vector<std::filesystem::path> asset_paths = {
      52              : #ifdef WREN_BUILD_ASSETS_DIR
      53            0 :       WREN_BUILD_ASSETS_DIR
      54              : #endif
      55              :   };
      56              : 
      57            0 :   wren::assets::Manager asset_manager(asset_paths);
      58            0 :   TRY_RESULT(const auto asset_path,
      59              :              asset_manager.find_asset("shaders/mesh.slang"));
      60              : 
      61            0 :   TRY_RESULT(
      62              :       auto mesh_shader,
      63              :       wren::vk::Shader::create(
      64              :           data->app->context()->graphics_context->Device().get(), asset_path));
      65              : 
      66            0 :   wren::GraphBuilder builder(data->app->context());
      67              : 
      68              :   // TODO Get Scene here
      69              :   auto render_query =
      70            0 :       data->scene->world()
      71            0 :           .query_builder<const wren::scene::components::Tag,
      72              :                          const wren::scene::components::Transform,
      73              :                          wren::scene::components::MeshRenderer>()
      74            0 :           .build();
      75              : 
      76            0 :   auto camera_transform = wren::scene::components::Transform{
      77            0 :       .position = {0, 0, -1}, .rotation = {0, 0, 0}, .scale = {1, 1, 1}};
      78              : 
      79            0 :   data->app->context()->event_dispatcher.on<wren::event::WindowResized>(
      80            0 :       [data](const auto& size) {
      81            0 :         data->resolution = {size.width, size.height};
      82            0 :         data->ortho_proj = wren::math::ortho(0.0F, size.width, 0.0F,
      83            0 :                                              size.height, 0.1F, 100.0F);
      84            0 :       });
      85              : 
      86            0 :   builder
      87            0 :       .add_pass(
      88            0 :           "main",
      89            0 :           wren::PassResources("swapchain_target")
      90            0 :               .add_shader("mesh", mesh_shader)
      91            0 :               .add_colour_target(),
      92            0 :           [data, app = data->app, mesh_shader, render_query, camera_transform](
      93              :               wren::RenderPass& pass, const vk::CommandBuffer& cmd) {
      94              :             struct GLOBALS {
      95            0 :               wren::math::Mat4f view = wren::math::Mat4f::identity();
      96            0 :               wren::math::Mat4f proj = wren::math::Mat4f::identity();
      97              :             };
      98            0 :             GLOBALS ubo{};
      99              : 
     100            0 :             ubo.view = camera_transform.matrix();
     101            0 :             ubo.proj = data->ortho_proj;
     102              : 
     103            0 :             pass.bind_pipeline("mesh");
     104            0 :             pass.write_scratch_buffer(cmd, 0, 0, ubo);
     105              : 
     106            0 :             render_query.each(
     107            0 :                 [cmd, app, mesh_shader](
     108              :                     const wren::scene::components::Tag& tag,
     109              :                     const wren::scene::components::Transform& transform,
     110              :                     wren::scene::components::MeshRenderer& mesh_renderer) {
     111            0 :                   mesh_renderer.bind(app->context(), mesh_shader, cmd,
     112            0 :                                      transform.matrix());
     113            0 :                 });
     114            0 :           })
     115            0 :       .build()
     116            0 :       .value();
     117              : 
     118            0 :   return {};
     119            0 : }
     120              : 
     121              : void update(const std::chrono::duration<float>& delta,
     122              :             const std::shared_ptr<wren::scene::Scene>& scene,
     123              :             const std::shared_ptr<GameData>& game_data);
     124              : 
     125            0 : auto main() -> int {
     126              :   // wren::log::set_level(spdlog::level::debug);
     127              : 
     128            0 :   const auto& err = wren::Application::create("raycaster");
     129            0 :   if (!err.has_value()) {
     130            0 :     wren::log::error("failed to create application: {}", err.error().message());
     131            0 :     return EXIT_FAILURE;
     132              :   }
     133              : 
     134            0 :   const auto app = err.value();
     135            0 :   const auto scene = wren::scene::Scene::create();
     136              : 
     137            0 :   const auto& data = std::make_shared<GameData>(
     138            0 :       app, scene, wren::math::ortho(0.0F, 80.0F, 0.0F, 60.0F, .01f, 1.0f));
     139              : 
     140            0 :   const auto init_err = initialize(data);
     141            0 :   if (!init_err.has_value()) {
     142            0 :     wren::log::error("failed to initialize: {}", init_err.error().message());
     143            0 :     return EXIT_FAILURE;
     144              :   }
     145              : 
     146            0 :   auto sky_quad = scene->create_entity("sky");
     147              :   {
     148              :     // Setup sky quad
     149            0 :     sky_quad.add_component<components::MeshRenderer>();
     150            0 :     auto& mesh_renderer = sky_quad.get_component<components::MeshRenderer>();
     151            0 :     mesh_renderer.update_mesh(wren::Mesh::create_quad());
     152              : 
     153            0 :     auto& renderer = sky_quad.get_component<components::MeshRenderer>();
     154            0 :     renderer.colour({0.0, 0.1, 0.75, 1.0});
     155              :   }
     156              : 
     157            0 :   auto ground_quad = scene->create_entity("ground");
     158              :   {
     159              :     // Setup ground quad
     160            0 :     ground_quad.add_component<components::MeshRenderer>();
     161            0 :     auto& mesh_renderer = ground_quad.get_component<components::MeshRenderer>();
     162            0 :     mesh_renderer.update_mesh(wren::Mesh::create_quad());
     163              : 
     164            0 :     auto& renderer = ground_quad.get_component<components::MeshRenderer>();
     165            0 :     renderer.colour({0.1, 0.1, 0.1, 1.0});
     166              :   }
     167              : 
     168            0 :   app->context()->event_dispatcher.on<wren::event::WindowResized>(
     169            0 :       [&sky_quad, &ground_quad](const auto& size) {
     170              :         {
     171              :           // Update sky
     172            0 :           auto& transform = sky_quad.get_component<components::Transform>();
     173            0 :           transform.scale = {size.width, size.height / 2, 1};
     174            0 :           transform.position = {transform.scale.x() / 2,
     175            0 :                                 (transform.scale.y() / 2) + (size.height / 2),
     176              :                                 0.0};
     177              :         }
     178              :         {
     179              :           // Update ground
     180            0 :           auto& transform = ground_quad.get_component<components::Transform>();
     181            0 :           transform.scale = {size.width, size.height / 2, 1};
     182            0 :           transform.position = {transform.scale.x() / 2,
     183            0 :                                 (transform.scale.y() / 2), 0.0};
     184              :         }
     185            0 :       });
     186              : 
     187            0 :   app->add_callback_to_startup_phase([scene, &data]() {
     188            0 :     data->player.position =
     189            0 :         wren::math::Vec3f{(kMaps.at(kMaps.size() / 2).size() / 2) * kGridScale,
     190            0 :                           (kMaps.size() / 2) * kGridScale, 0};
     191            0 :   });
     192              : 
     193            0 :   app->add_callback_to_update_phase(
     194            0 :       [scene, &data](auto delta) { update(delta, scene, data); });
     195              : 
     196            0 :   app->context()->event_dispatcher.on<wren::event::KeyDown>(
     197            0 :       [&data](const auto& e) { data->key_inputs[e.key] = true; });
     198              : 
     199            0 :   app->context()->event_dispatcher.on<wren::event::KeyUp>(
     200            0 :       [&data](const auto& e) { data->key_inputs[e.key] = false; });
     201              : 
     202            0 :   app->run();
     203              : 
     204            0 :   return EXIT_SUCCESS;
     205            0 : }
     206              : 
     207            0 : void update(const std::chrono::duration<float>& delta,
     208              :             const std::shared_ptr<wren::scene::Scene>& scene,
     209              :             const std::shared_ptr<GameData>& game_data) {
     210            0 :   const auto& inputs = game_data->app->context()->input;
     211            0 :   const auto delta_time = delta.count();
     212              : 
     213            0 :   const wren::math::Vec2f input = [&]() {
     214            0 :     wren::math::Vec2f input;
     215            0 :     if (inputs.is_pressed(wren::KeyCode::kW)) {
     216            0 :       input.y(1);
     217            0 :     } else if (game_data->key_inputs.contains(wren::KeyCode::kS) &&
     218            0 :                game_data->key_inputs.at(wren::KeyCode::kS)) {
     219            0 :       input.y(-1);
     220            0 :     }
     221            0 :     if (inputs.is_pressed(wren::KeyCode::kA)) {
     222            0 :       input.x(-1);
     223            0 :     } else if (inputs.is_pressed(wren::KeyCode::kD)) {
     224            0 :       input.x(1);
     225            0 :     }
     226            0 :     return input;
     227              :   }();
     228              : 
     229            0 :   const float rot_input = [&]() {
     230            0 :     if (inputs.is_pressed(wren::KeyCode::kComma)) {
     231            0 :       return 1.f;
     232            0 :     } else if (inputs.is_pressed(wren::KeyCode::kPeriod)) {
     233            0 :       return -1.f;
     234              :     }
     235            0 :     return 0.f;
     236            0 :   }();
     237              : 
     238              :   // Setup rotation
     239            0 :   const wren::math::Vec3f& forward = game_data->player.right();
     240            0 :   const wren::math::Vec3f right = game_data->player.right();
     241              : 
     242            0 :   const wren::math::Vec2f dir = {forward.x(), forward.y()};
     243            0 :   const wren::math::Vec2f plane = {-dir.y(), dir.x()};
     244              : 
     245            0 :   const wren::math::Vec2f movement = dir * input.y() + right * input.x();
     246              : 
     247            0 :   game_data->player.position +=
     248            0 :       wren::math::Vec3f(movement * GameData::kSpeed * delta_time, 0.f);
     249            0 :   game_data->player.rotation.z() += rot_input * delta_time;
     250              : 
     251            0 :   const auto pos = game_data->player.position;
     252              : 
     253            0 :   const int w = int(game_data->resolution.x()) / 10;
     254            0 :   const auto h = game_data->resolution.y();
     255              : 
     256            0 :   for (auto x = 0; x < w; ++x) {
     257            0 :     const float camera_x = 2.0f * float(x) / float(w) - 1.0f;
     258            0 :     const float ray_dir_x = dir.x() + plane.x() * camera_x;
     259            0 :     const float ray_dir_y = dir.y() + plane.y() * camera_x;
     260              : 
     261            0 :     int ray_pos_x = int(pos.x() / kGridScale);
     262            0 :     int ray_pos_y = int(pos.y() / kGridScale);
     263              : 
     264              :     float side_dist_x;
     265              :     float side_dist_y;
     266              : 
     267            0 :     float delta_dist_x = std::abs(1.f / ray_dir_x);
     268            0 :     float delta_dist_y = std::abs(1.f / ray_dir_y);
     269              : 
     270              :     int step_x;
     271              :     int step_y;
     272              : 
     273            0 :     int hit = 0;
     274              :     int side;
     275              : 
     276            0 :     if (ray_dir_x < 0) {
     277            0 :       step_x = -1;
     278            0 :       side_dist_x = (pos.x() - float(ray_pos_x)) * delta_dist_x;
     279            0 :     } else {
     280            0 :       step_x = 1;
     281            0 :       side_dist_x = (float(ray_pos_x) + 1.0f - pos.x()) * delta_dist_x;
     282              :     }
     283              : 
     284            0 :     if (ray_dir_y < 0) {
     285            0 :       step_y = -1;
     286            0 :       side_dist_y = (pos.y() - float(ray_pos_y)) * delta_dist_y;
     287            0 :     } else {
     288            0 :       step_y = 1;
     289            0 :       side_dist_y = (float(ray_pos_y) + 1.0f - pos.y()) * delta_dist_y;
     290              :     }
     291              : 
     292            0 :     while (hit == 0) {
     293            0 :       if (side_dist_x < side_dist_y) {
     294            0 :         side_dist_x += delta_dist_x;
     295            0 :         ray_pos_x += step_x;
     296            0 :         side = 0;
     297            0 :       } else {
     298            0 :         side_dist_y += delta_dist_y;
     299            0 :         ray_pos_y += step_y;
     300            0 :         side = 1;
     301              :       }
     302            0 :       if (kMaps.at(ray_pos_y).at(ray_pos_x) > 0) hit = 1;
     303              :     }
     304              : 
     305            0 :     float perp_wall_dist = 0;
     306            0 :     if (side == 0)
     307            0 :       perp_wall_dist = (side_dist_x - delta_dist_x);
     308              :     else
     309            0 :       perp_wall_dist = (side_dist_y - delta_dist_y);
     310              : 
     311            0 :     float line_height = (h / perp_wall_dist);
     312              : 
     313            0 :     if (x >= game_data->raycasted.size()) {
     314            0 :       auto e = game_data->scene->create_entity(std::format("raycasted_{}", x));
     315            0 :       e.add_component<wren::scene::components::MeshRenderer>();
     316            0 :       auto& mesh_renderer =
     317            0 :           e.get_component<wren::scene::components::MeshRenderer>();
     318            0 :       mesh_renderer.update_mesh(wren::Mesh::create_quad());
     319            0 :       game_data->raycasted.push_back(e);
     320            0 :     }
     321            0 :     auto entity = game_data->raycasted.at(x);
     322            0 :     auto& mesh_renderer =
     323            0 :         entity.get_component<wren::scene::components::MeshRenderer>();
     324              : 
     325            0 :     const auto val = kMaps.at(ray_pos_y).at(ray_pos_x);
     326            0 :     wren::math::Vec3f colour{};
     327            0 :     switch (val) {
     328              :       case 1:
     329            0 :         colour = {1, 0, 0};
     330            0 :         break;
     331              :       case 2:
     332            0 :         colour = {0, 1, 0};
     333            0 :         break;
     334              :       case 3:
     335            0 :         colour = {0, 0, 1};
     336            0 :         break;
     337              :       case 4:
     338            0 :         colour = {1, 1, 0};
     339            0 :         break;
     340              :       case 5:
     341            0 :         colour = {0, 1, 1};
     342            0 :         break;
     343              :       default:
     344            0 :         colour = {1, 1, 1};
     345            0 :         break;
     346              :     }
     347              : 
     348            0 :     if (side == 1) colour = colour / 2;
     349              : 
     350            0 :     mesh_renderer.colour({colour, 1.0});
     351              : 
     352            0 :     auto& transform =
     353            0 :         entity.get_component<wren::scene::components::Transform>();
     354            0 :     transform.scale.y(line_height);
     355            0 :     transform.scale.x(10);
     356            0 :     transform.position.x(static_cast<float>(x) * 10);
     357            0 :     transform.position.y(h / 2);
     358            0 :   }
     359            0 : }
        

Generated by: LCOV version 2.3.2-1