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 : }
|