From d473ef3ea41d041256b82c32363a0f8b2decd235 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Fri, 21 Jul 2023 01:31:30 +0300 Subject: [PATCH] Delete obsolete examples & fix the rest to incorporate the new application API --- examples/CMakeLists.txt | 2 +- examples/animation_2d.cpp | 87 +++-- examples/audio.cpp | 146 +++++---- examples/cloud.cpp | 64 ++-- examples/deferred.cpp | 65 ++-- examples/fire.cpp | 46 +-- examples/grass.cpp | 636 ------------------------------------- examples/gravity.cpp | 124 ++++---- examples/parser.cpp | 25 +- examples/physics.cpp | 201 ++++++------ examples/physics_2d.cpp | 101 +++--- examples/physics_3d.cpp | 53 ++-- examples/platformer.cpp | 31 +- examples/shadow.cpp | 67 ++-- examples/srtm.cpp | 73 +++-- examples/tree.cpp | 532 ------------------------------- examples/triangulation.cpp | 76 +++-- examples/water_1d.cpp | 66 ++-- examples/water_2d.cpp | 326 ------------------- 19 files changed, 671 insertions(+), 2050 deletions(-) delete mode 100644 examples/grass.cpp delete mode 100644 examples/tree.cpp delete mode 100644 examples/water_2d.cpp diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index b4fabcd4..12b4eadc 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -5,7 +5,7 @@ file(GLOB PSEMEK_EXAMPLES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURREN foreach(example ${PSEMEK_EXAMPLES}) get_filename_component(TARGET_NAME "${example}" NAME_WLE) set(TARGET_NAME psemek-example-${TARGET_NAME}) - psemek_add_executable(${TARGET_NAME} ${example}) + psemek_add_application(${TARGET_NAME} ${example}) if(TARGET ${TARGET_NAME}) target_link_libraries(${TARGET_NAME} PUBLIC psemek ZLIB::ZLIB) target_compile_definitions(${TARGET_NAME} PUBLIC -DPSEMEK_EXAMPLES_DIR="${CMAKE_CURRENT_SOURCE_DIR}") diff --git a/examples/animation_2d.cpp b/examples/animation_2d.cpp index 9d177466..9be116f6 100644 --- a/examples/animation_2d.cpp +++ b/examples/animation_2d.cpp @@ -1,5 +1,5 @@ -#include -#include +#include +#include #include @@ -651,14 +651,14 @@ controller lerp(controller const & c1, controller const & c2, float t) } struct animation_2d_app - : app::app + : app::application_base { - animation_2d_app(); + animation_2d_app(options const &, context const &); - void on_resize(int width, int height) override; - void on_mouse_wheel(int delta) override; + void on_event(app::resize_event const & event) override; + void on_event(app::mouse_wheel_event const & event) override; - void on_key_down(SDL_Keycode key) override; + void on_event(app::key_event const & event) override; void update() override; void present() override; @@ -688,6 +688,8 @@ struct animation_2d_app test, } mode = mode::train; + application::context context; + std::vector population; std::size_t const population_size = 1024; std::size_t const max_train_frames = 10.f / physics.dt; @@ -720,24 +722,24 @@ struct animation_2d_app void do_test(); }; -animation_2d_app::animation_2d_app() - : app("Animation 2D") +animation_2d_app::animation_2d_app(options const &, application::context const & context) + : context(context) { view_bbox[1] = {-1.f, 15.f}; - vsync(false); + context.vsync(false); } -void animation_2d_app::on_resize(int width, int height) +void animation_2d_app::on_event(app::resize_event const & event) { - app::on_resize(width, height); + app::application_base::on_event(event); update_camera(); } -void animation_2d_app::on_mouse_wheel(int delta) +void animation_2d_app::on_event(app::mouse_wheel_event const & event) { - float p = std::pow(0.8f, delta); + float p = std::pow(0.8f, event.delta); view_bbox[1].max *= p; update_camera(); @@ -745,7 +747,7 @@ void animation_2d_app::on_mouse_wheel(int delta) void animation_2d_app::update_camera() { - float ratio = static_cast(width()) / height(); + float ratio = static_cast(state().size[0]) / state().size[1]; float cx = 0.f; if (centered) @@ -766,22 +768,22 @@ void animation_2d_app::update_camera() view_bbox[0] = geom::expand(geom::interval::singleton(cx), ratio * view_bbox[1].length() / 2.f); } -void animation_2d_app::on_key_down(SDL_Keycode key) +void animation_2d_app::on_event(app::key_event const & event) { - if (key == SDLK_c) + if (event.down && event.key == app::keycode::C) { centered = !centered; update_camera(); } - if (key == SDLK_p) + if (event.down && event.key == app::keycode::P) { testing_control = !testing_control; } if (mode == mode::train) { - if (key == SDLK_s) + if (event.down && event.key == app::keycode::S) { train_iterations = max_train_iterations; } @@ -789,12 +791,12 @@ void animation_2d_app::on_key_down(SDL_Keycode key) if (mode == mode::test) { bool reset = false; - if (key == SDLK_LEFT) + if (event.down && event.key == app::keycode::LEFT) { reset = true; test_id = (test_id + population.size() - 1) % population.size(); } - if (key == SDLK_RIGHT) + if (event.down && event.key == app::keycode::RIGHT) { reset = true; test_id = (test_id + 1) % population.size(); @@ -844,7 +846,7 @@ void animation_2d_app::update() population[0].reset(); - vsync(true); + context.vsync(true); } } else if (mode == mode::test) @@ -1143,14 +1145,6 @@ float animation_2d_app::eval_score(controller const & c, random::generator rng) if (frame + 1 == max_train_frames) { - float mean_pos_x = 0.f; - float mass = 0.f; - for (auto const & b : physics.bones) - { - mean_pos_x += b.position[0] * b.mass; - mass += b.mass; - } - mean_pos_x /= mass; cur_score = reward / max_train_variations; } @@ -1165,7 +1159,6 @@ float animation_2d_app::eval_score(controller const & c, random::generator rng) return score; } - // Old evolutionary training implementation void animation_2d_app::do_train() { @@ -1367,17 +1360,12 @@ void animation_2d_app::do_train() void animation_2d_app::do_test() { - std::optional> m; - if (mouse()) - { - m = view_bbox.corner((*mouse())[0] * 1.f / width(), 1.f - (*mouse())[1] * 1.f / height()); - } + geom::point mouse = view_bbox.corner(state().mouse[0] * 1.f / state().size[0], 1.f - state().mouse[1] * 1.f / state().size[1]); - if (!is_left_button_down()) + if (state().mouse_button_down.contains(app::mouse_button::left)) { selected = std::nullopt; - if (m) { float selected_distance = std::numeric_limits::infinity(); @@ -1389,7 +1377,7 @@ void animation_2d_app::do_test() auto p1 = b.position + b.direction * b.length / 2.f; auto r = p1 - p0; - auto d = *m - p0; + auto d = mouse - p0; float t = geom::dot(d, r) / geom::dot(r, r); @@ -1401,8 +1389,8 @@ void animation_2d_app::do_test() } else { - float d0 = geom::distance(p0, *m); - float d1 = geom::distance(p1, *m); + float d0 = geom::distance(p0, mouse); + float d1 = geom::distance(p1, mouse); if (d0 < d1) { @@ -1427,10 +1415,10 @@ void animation_2d_app::do_test() } std::optional sel; - if (selected && is_left_button_down()) + if (selected && state().mouse_button_down.contains(app::mouse_button::left)) { auto const & b = physics.bones[*selected]; - auto delta = b.position + b.direction * selected_s * b.length / 2.f - *m; + auto delta = b.position + b.direction * selected_s * b.length / 2.f - mouse; sel = system::selection{*selected, selected_s, delta}; } @@ -1515,7 +1503,7 @@ void animation_2d_app::present() int margin = 40; float const step = 1.f; - int max_frames_shown = (width() - 2 * margin) / step; + int max_frames_shown = (state().size[0] - 2 * margin) / step; int start = std::max(0, static_cast(test_speeds.size() - max_frames_shown)); for (std::size_t i = start; i + 1 < test_speeds.size(); ++i) @@ -1542,12 +1530,17 @@ void animation_2d_app::present() // if (mode == mode::test && !test_speeds.empty()) painter.text({40.f, 136.f}, util::to_string("Speed: ", test_speeds.back()), opts); } - painter.render(geom::window_camera{width(), height()}.transform()); + painter.render(geom::window_camera{state().size[0], state().size[1]}.transform()); } } -int main() +namespace psemek::app { - return app::main(); + + std::unique_ptr make_application_factory() + { + return default_application_factory({.name = "Animation 2D example"}); + } + } diff --git a/examples/audio.cpp b/examples/audio.cpp index 4da79e78..ca0c343e 100644 --- a/examples/audio.cpp +++ b/examples/audio.cpp @@ -17,8 +17,8 @@ #include #include #include -#include -#include +#include +#include #include #include #include @@ -31,41 +31,41 @@ using namespace psemek; -static std::map const key_to_midi +static std::map const key_to_midi { - {SDLK_z, 59}, - {SDLK_x, 60}, - {SDLK_c, 61}, - {SDLK_v, 62}, - {SDLK_b, 63}, - {SDLK_n, 64}, - {SDLK_m, 65}, - {SDLK_COMMA, 66}, - {SDLK_PERIOD, 67}, - {SDLK_SLASH, 68}, - {SDLK_a, 69}, - {SDLK_s, 70}, - {SDLK_d, 71}, - {SDLK_f, 72}, - {SDLK_g, 73}, - {SDLK_h, 74}, - {SDLK_j, 75}, - {SDLK_k, 76}, - {SDLK_l, 77}, - {SDLK_SEMICOLON, 78}, - {SDLK_QUOTE, 79}, - {SDLK_q, 80}, - {SDLK_w, 81}, - {SDLK_e, 82}, - {SDLK_r, 83}, - {SDLK_t, 84}, - {SDLK_y, 85}, - {SDLK_u, 86}, - {SDLK_i, 87}, - {SDLK_o, 88}, - {SDLK_p, 89}, - {SDLK_LEFTBRACKET, 90}, - {SDLK_RIGHTBRACKET, 91}, + {app::keycode::Z, 59}, + {app::keycode::X, 60}, + {app::keycode::C, 61}, + {app::keycode::V, 62}, + {app::keycode::B, 63}, + {app::keycode::N, 64}, + {app::keycode::M, 65}, + {app::keycode::COMMA, 66}, + {app::keycode::PERIOD, 67}, + {app::keycode::SLASH, 68}, + {app::keycode::A, 69}, + {app::keycode::S, 70}, + {app::keycode::D, 71}, + {app::keycode::F, 72}, + {app::keycode::G, 73}, + {app::keycode::H, 74}, + {app::keycode::J, 75}, + {app::keycode::K, 76}, + {app::keycode::L, 77}, + {app::keycode::SEMICOLON, 78}, + {app::keycode::APOSTROPHE, 79}, + {app::keycode::Q, 80}, + {app::keycode::W, 81}, + {app::keycode::E, 82}, + {app::keycode::R, 83}, + {app::keycode::T, 84}, + {app::keycode::Y, 85}, + {app::keycode::U, 86}, + {app::keycode::I, 87}, + {app::keycode::O, 88}, + {app::keycode::P, 89}, + {app::keycode::LEFTBRACKET, 90}, + {app::keycode::RIGHTBRACKET, 91}, }; static geom::interval const key_rows[3] = { @@ -79,27 +79,27 @@ static std::string_view const midi_name[12] = { }; struct audio_app - : app::app + : app::application_base { - audio_app() - : app::app("Audio example") + audio_app(options const &, context const &) { + engine_ = audio::make_engine(); mixer_ = audio::make_mixer(); volume_control_ = audio::volume_stereo(mixer_, 0.5f, 0.5f, 0.1f); pitch_control_ = audio::pitch(volume_control_, 1.f, 0.025f); auto compressor = audio::compressor(pitch_control_, audio::from_db(-2.f), 0.95f, 0.002f, 1.f, audio::from_db(1.f)); pause_control_ = audio::pause(compressor, false, 0.01f); - engine_.output()->stream(pause_control_); + engine_->output()->stream(pause_control_); } - void on_key_down(SDL_Keycode key) override + void on_event(app::key_event const & event) override { - app::app::on_key_down(key); + app::application_base::on_event(event); - if (key_to_midi.contains(key)) + if (event.down && key_to_midi.contains(event.key)) { - int midi = key_to_midi.at(key); + int midi = key_to_midi.at(event.key); if (!channels_.contains(midi)) { auto tone = audio::karplus_strong(audio::midi_frequency(midi)); @@ -108,35 +108,28 @@ struct audio_app } } - if (key == SDLK_SPACE) + if (!event.down && key_to_midi.contains(event.key)) { - pause_control_->paused(!pause_control_->paused()); - } - - if (key == SDLK_KP_PLUS) - pitch_control_->pitch(std::pow(2.f, 1.f / 12.f)); - - if (key == SDLK_KP_MINUS) - pitch_control_->pitch(std::pow(2.f, - 1.f / 12.f)); - } - - void on_key_up(SDL_Keycode key) override - { - app::app::on_key_up(key); - - if (key_to_midi.contains(key)) - { - int midi = key_to_midi.at(key); + int midi = key_to_midi.at(event.key); auto & ch = channels_[midi]; if (auto s = ch->stream()) ch->stream(audio::fade_out(s, 0.1f)); channels_.erase(midi); } - if (key == SDLK_KP_PLUS) + if (event.down && event.key == app::keycode::SPACE) + pause_control_->paused(!pause_control_->paused()); + + if (event.down && event.key == app::keycode::KP_PLUS) + pitch_control_->pitch(std::pow(2.f, 1.f / 12.f)); + + if (event.down && event.key == app::keycode::KP_MINUS) + pitch_control_->pitch(std::pow(2.f, - 1.f / 12.f)); + + if (!event.down && event.key == app::keycode::KP_PLUS) pitch_control_->pitch(1.f); - if (key == SDLK_KP_MINUS) + if (!event.down && event.key == app::keycode::KP_MINUS) pitch_control_->pitch(1.f); } @@ -161,8 +154,8 @@ struct audio_app float margin = 4.f; float border = 8.f; - float center = width() / 2.f; - float y = height() / 2.f + size * std::size(key_rows) / 2.f; + float center = state().size[0] / 2.f; + float y = state().size[1] / 2.f + size * std::size(key_rows) / 2.f; gfx::painter::text_options opts; opts.scale = 2.f; @@ -194,11 +187,11 @@ struct audio_app opts.scale = 4.f; opts.c = pause_control_->paused() ? gfx::color_rgba{255, 0, 0, 255} : gfx::color_rgba{0, 127, 0, 255}; - painter_.text({width() / 2.f, height() - 200.f}, pause_control_->paused() ? "PAUSED" : "PLAYING", opts); + painter_.text({state().size[0] / 2.f, state().size[1] - 200.f}, pause_control_->paused() ? "PAUSED" : "PLAYING", opts); { - float x = width() - 200.f; - float y = height() / 2.f; + float x = state().size[0] - 200.f; + float y = state().size[1] / 2.f; float w = 4.f; float h = 64.f; @@ -211,16 +204,16 @@ struct audio_app painter_.rect({{{x - s, x + s}, {r - w, r + w}}}, {0, 0, 255, 255}); } - painter_.render(geom::window_camera{width(), height()}.transform()); + painter_.render(geom::window_camera{state().size[0], state().size[1]}.transform()); } - void on_scene_exit() override + ~audio_app() override { prof::dump(); } private: - audio::engine engine_; + std::unique_ptr engine_; audio::mixer_ptr mixer_; std::shared_ptr volume_control_; std::shared_ptr pitch_control_; @@ -235,7 +228,12 @@ private: gfx::painter painter_; }; -int main() +namespace psemek::app { - return app::main(); + + std::unique_ptr make_application_factory() + { + return default_application_factory({.name = "Audio example"}); + } + } diff --git a/examples/cloud.cpp b/examples/cloud.cpp index 3716577d..7adc2f3d 100644 --- a/examples/cloud.cpp +++ b/examples/cloud.cpp @@ -1,5 +1,5 @@ -#include -#include +#include +#include #include #include #include @@ -23,6 +23,7 @@ #include #include #include +#include #include @@ -161,7 +162,7 @@ void main() )"; struct cloud_app - : app::app + : app::application_base { geom::spherical_camera camera; @@ -191,7 +192,6 @@ struct cloud_app gfx::texture_3d shadow_texture; cloud_app(std::size_t size) - : app::app("Cloud") { geom::vector cloud_size{2 * size, size, size}; bbox = {{{-2.f, 2.f}, {-1.f, 1.f}, {-1.f, 1.f}}}; @@ -466,28 +466,35 @@ struct cloud_app } } - void on_resize(int width, int height) override + void on_event(app::resize_event const & event) override { - app::on_resize(width, height); + app::application_base::on_event(event); - float aspect_ratio = (1.f * width) / height; + gl::Viewport(0, 0, event.size[0], event.size[1]); + + float aspect_ratio = (1.f * event.size[0]) / event.size[1]; camera.set_fov(camera.fov_y, aspect_ratio); } - void on_mouse_move(int x, int y, int dx, int dy) override + void on_event(app::mouse_move_event const & event) override { - app::on_mouse_move(x, y, dx, dy); + auto const old_mouse = state().mouse; - if (is_middle_button_down()) + app::application_base::on_event(event); + + if (state().mouse_button_down.contains(app::mouse_button::middle)) { - camera.azimuthal_angle -= dx * 0.01f; - camera.elevation_angle += dy * 0.01f; + auto const delta = event.position - old_mouse; + camera.azimuthal_angle -= delta[0] * 0.01f; + camera.elevation_angle += delta[1] * 0.01f; } } - void on_mouse_wheel(int delta) override + void on_event(app::mouse_wheel_event const & event) override { - camera.distance *= std::pow(0.8f, delta); + app::application_base::on_event(event); + + camera.distance *= std::pow(0.8f, event.delta); } void update_slice_mesh() @@ -534,6 +541,9 @@ struct cloud_app slice_mesh.load(builder.vertices, builder.indices); } + void update() override + {} + void present() override { update_slice_mesh(); @@ -592,18 +602,24 @@ struct cloud_app } }; -int main(int argc, char ** argv) +namespace psemek::app { - if (argc != 1 && argc != 2) + + std::unique_ptr make_application_factory() { - std::cout << "Usage: " << argv[0] << " [ size ]\n"; - return 0; + return default_application_factory({.name = "Cloud example"}, [](application::options const &, application::context const & context) + -> std::unique_ptr { + if (context.args.size() != 1 && context.args.size() != 2) + { + std::cout << "Usage: " << context.args[0] << " [ size ]\n"; + return nullptr; + } + + std::size_t size = 32; + if (context.args.size() == 2) + size = util::from_string(context.args[1]); + return std::make_unique(size); + }); } - std::size_t size = 32; - if (argc == 2) - { - size = util::from_string(argv[1]); - } - return app::main(size); } diff --git a/examples/deferred.cpp b/examples/deferred.cpp index 63da031f..1e02c997 100644 --- a/examples/deferred.cpp +++ b/examples/deferred.cpp @@ -1,5 +1,5 @@ -#include -#include +#include +#include #include #include @@ -22,14 +22,14 @@ using namespace psemek; struct deferred_app - : app::app + : app::application_base { - deferred_app(); + deferred_app(options const & options, context const & context); - void on_resize(int width, int height) override; + void on_event(app::resize_event const & event) override; - void on_mouse_move(int x, int y, int dx, int dy) override; - void on_mouse_wheel(int delta) override; + void on_event(app::mouse_move_event const & event) override; + void on_event(app::mouse_wheel_event const & event) override; void update() override; void present() override; @@ -60,10 +60,9 @@ struct deferred_app gfx::painter painter; }; -deferred_app::deferred_app() - : app("Deferred shading example", 0) +deferred_app::deferred_app(options const &, context const & context) { - vsync(false); + context.vsync(false); camera.fov_y = geom::rad(45.f); camera.near_clip = 0.1f; @@ -291,34 +290,39 @@ deferred_app::deferred_app() } } -void deferred_app::on_resize(int width, int height) +void deferred_app::on_event(app::resize_event const & event) { - app::on_resize(width, height); - camera.set_fov(camera.fov_y, static_cast(width) / height); + app::application_base::on_event(event); + camera.set_fov(camera.fov_y, static_cast(event.size[0]) / event.size[1]); - pre_gamma_texture.load>({width, height}); + pre_gamma_texture.load>(geom::cast(event.size)); pre_gamma_framebuffer.color(pre_gamma_texture); pre_gamma_framebuffer.assert_complete(); - pre_fxaa_texture.load({width, height}); + pre_fxaa_texture.load(geom::cast(event.size)); pre_fxaa_framebuffer.color(pre_fxaa_texture); pre_fxaa_framebuffer.assert_complete(); } -void deferred_app::on_mouse_move(int x, int y, int dx, int dy) +void deferred_app::on_event(app::mouse_move_event const & event) { - app::on_mouse_move(x, y, dx, dy); + auto const old_mouse = state().mouse; - if (is_middle_button_down()) + app::application_base::on_event(event); + + if (state().mouse_button_down.contains(app::mouse_button::middle)) { - camera.azimuthal_angle -= dx * 0.01f; - camera.elevation_angle += dy * 0.01f; + auto delta = event.position - old_mouse; + camera.azimuthal_angle -= delta[0] * 0.01f; + camera.elevation_angle += delta[1] * 0.01f; } } -void deferred_app::on_mouse_wheel(int delta) +void deferred_app::on_event(app::mouse_wheel_event const & event) { - camera.distance *= std::pow(0.8f, delta); + app::application_base::on_event(event); + + camera.distance *= std::pow(0.8f, event.delta); } void deferred_app::update() @@ -458,7 +462,7 @@ void deferred_app::present() gfx::render_target target; target.framebuffer = &pre_gamma_framebuffer; target.draw_buffer = gl::COLOR_ATTACHMENT0; - target.viewport = {{{0, width()}, {0, height()}}}; + target.viewport = {{{0, state().size[0]}, {0, state().size[1]}}}; renderer.render(objects, target, options); } @@ -466,7 +470,7 @@ void deferred_app::present() gfx::render_target target; target.framebuffer = &pre_fxaa_framebuffer; target.draw_buffer = gl::COLOR_ATTACHMENT0; - target.viewport = {{{0, width()}, {0, height()}}}; + target.viewport = {{{0, state().size[0]}, {0, state().size[1]}}}; gamma_correction.invoke(pre_gamma_texture, target, {1.f / gamma}); } @@ -474,7 +478,7 @@ void deferred_app::present() gfx::render_target target; target.framebuffer = &gfx::framebuffer::null(); target.draw_buffer = gl::BACK_LEFT; - target.viewport = {{{0, width()}, {0, height()}}}; + target.viewport = {{{0, state().size[0]}, {0, state().size[1]}}}; fxaa.invoke(pre_fxaa_texture, target); } @@ -491,10 +495,15 @@ void deferred_app::present() gl::Enable(gl::BLEND); gl::BlendFunc(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA); - painter.render(geom::window_camera{width(), height()}.transform()); + painter.render(geom::window_camera{state().size[0], state().size[1]}.transform()); } -int main() +namespace psemek::app { - return app::main(); + + std::unique_ptr make_application_factory() + { + return default_application_factory({.name = "Deferred shading example", .multisampling = 0}); + } + } diff --git a/examples/fire.cpp b/examples/fire.cpp index 0e7d4881..13c99e9a 100644 --- a/examples/fire.cpp +++ b/examples/fire.cpp @@ -1,5 +1,5 @@ -#include -#include +#include +#include #include #include #include @@ -254,7 +254,7 @@ void candle_renderer::update_instances() } struct fire_app - : app::app + : app::application_base { geom::spherical_camera camera; @@ -263,8 +263,7 @@ struct fire_app util::clock> clock; float time = 0.f; - fire_app() - : app("Fire") + fire_app(options const &, context const &) { camera.fov_y = geom::rad(45.f); camera.near_clip = 0.01f; @@ -277,32 +276,36 @@ struct fire_app candles.add({0.f, 0.f, 0.f}, {0.f, 0.f, 1.f}, 1.f); } - void on_resize(int width, int height) override + void on_event(app::resize_event const & event) override { - app::on_resize(width, height); - camera.set_fov(camera.fov_y, (1.f * width) / height); + app::application_base::on_event(event); + camera.set_fov(camera.fov_y, (1.f * event.size[0]) / event.size[1]); } - void on_mouse_move(int x, int y, int dx, int dy) override + void on_event(app::mouse_move_event const & event) override { - app::on_mouse_move(x, y, dx, dy); + auto const old_mouse = state().mouse; - if (is_middle_button_down()) + app::application_base::on_event(event); + + if (state().mouse_button_down.contains(app::mouse_button::middle)) { - camera.azimuthal_angle -= dx * 0.01f; - camera.elevation_angle += dy * 0.01f; + auto const delta = event.position - old_mouse; + + camera.azimuthal_angle -= delta[0] * 0.01f; + camera.elevation_angle += delta[1] * 0.01f; } } - void on_mouse_wheel(int delta) override + void on_event(app::mouse_wheel_event const & event) override { - app::on_mouse_wheel(delta); - camera.distance *= std::pow(0.8f, delta); + app::application_base::on_event(event); + camera.distance *= std::pow(0.8f, event.delta); } void update() override { - if (!is_key_down(SDLK_SPACE)) + if (!state().key_down.contains(app::keycode::SPACE)) time += clock.restart().count(); } @@ -319,7 +322,12 @@ struct fire_app }; -int main() +namespace psemek::app { - return app::main(); + + std::unique_ptr make_application_factory() + { + return default_application_factory({.name = "Fire example", .multisampling = 4}); + } + } diff --git a/examples/grass.cpp b/examples/grass.cpp deleted file mode 100644 index 971750d6..00000000 --- a/examples/grass.cpp +++ /dev/null @@ -1,636 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace psemek; - -static char const ground_vs[] = -R"(#version 330 - -uniform mat4 u_transform; - -layout (location = 0) in vec4 in_position; -layout (location = 1) in vec4 in_color; - -out vec4 color; - -void main() -{ - gl_Position = u_transform * in_position; - color = in_color; -} -)"; - -static char const ground_fs[] = -R"(#version 330 - -in vec4 color; - -out vec4 out_color; - -void main() -{ - out_color = color; -} -)"; - -static char const grass_vs[] = -R"(#version 330 - -uniform mat4 u_transform; -uniform mat4 u_tile_transform; -uniform float u_height00; -uniform float u_height01; -uniform float u_height10; -uniform float u_height11; -uniform sampler1D u_texture; - -layout (location = 0) in vec4 in_position; -layout (location = 1) in float in_t; - -out vec4 color; - -void main() -{ - vec2 p = (u_tile_transform * vec4(in_position.xy * 2.0 - vec2(0.5), 0.0, 1.0)).xy; - - vec2 o = (u_tile_transform * vec4(0.5, 0.5, 0.0, 1.0)).xy; - - o = vec2(floor(o.x), floor(o.y)); - - vec2 d = p - o; - - float h = mix(mix(u_height00, u_height01, d.x), mix(u_height10, u_height11, d.x), d.y); - - gl_Position = u_transform * vec4(p, in_position.z * h, 1.0); -// color = mix(vec4(0.0, 0.0, 0.0, 1.0), texture(u_texture, in_t), in_position.z); - color = texture(u_texture, in_t); -} -)"; - -static char const grass_fs[] = -R"(#version 330 - -in vec4 color; - -out vec4 out_color; - -void main() -{ - out_color = color; -} -)"; - -static char const grass_slice_vs[] = -R"(#version 330 - -uniform mat4 u_transform; -uniform mat4 u_tile_transform; - -uniform float u_density00; -uniform float u_density01; -uniform float u_density10; -uniform float u_density11; - -layout (location = 0) in vec4 in_position; -layout (location = 1) in vec2 in_texcoord; - -out vec3 texcoord; - -void main() -{ - vec2 p = (u_tile_transform * vec4(in_position.xy, 0.0, 1.0)).xy; - - vec2 o = (u_tile_transform * vec4(0.5, 0.5, 0.0, 1.0)).xy; - - o = vec2(floor(o.x), floor(o.y)); - - vec2 d = p - o; - - float level = mix(mix(u_density00, u_density01, d.x), mix(u_density10, u_density11, d.x), d.y); - - gl_Position = u_transform * vec4(p, in_position.z, 1.0); - texcoord = vec3(in_texcoord, level); -} -)"; - -static char const grass_slice_fs[] = -R"(#version 330 - -uniform sampler2DArray u_texture; - -in vec3 texcoord; - -out vec4 out_color; - -void main() -{ - float l0 = floor(texcoord.z); - float l1 = l0 + 1; - float t = texcoord.z - l0; - vec4 c0 = texture(u_texture, vec3(texcoord.xy, l0)); - vec4 c1 = texture(u_texture, vec3(texcoord.xy, l1)); - vec4 c = mix(c0, c1, t); -// vec4 c = c0; - out_color = vec4(c.rgb / c.a, c.a); -} -)"; - -struct grass_app - : app::app -{ - geom::free_camera camera; - - int size = 64; - - int const density_level_count = 8; - - pcg::perlin density; - - gfx::program ground_program{ground_vs, ground_fs}; - gfx::mesh ground_mesh; - - gfx::program grass_program{grass_vs, grass_fs}; - gfx::mesh grass_mesh; - gfx::texture_1d grass_texture; - - gfx::program grass_slice_program{grass_slice_vs, grass_slice_fs}; - gfx::texture_2d_array grass_slice_z_texture; - gfx::mesh grass_slice_z_mesh; - - gfx::framebuffer grass_slice_framebuffer; - gfx::renderbuffer grass_slice_renderbuffer; - - geom::matrix random_transform[8]; - util::array random_transform_index; - - util::clock<> frame_clock; - util::moving_average frame_time{64}; - - util::clock> update_clock; - - gfx::painter painter; - - grass_app() - : app("Grass", 4) - { - vsync(false); - - camera.fov_y = geom::rad(45.f); - camera.near_clip = 0.1f; - camera.far_clip = 1000.f; - camera.pos = {0.5f, 0.5f, 1.5f}; - camera.rotateYZ(geom::rad(-90.f)); - - init_ground(); - init_grass(); - init_grass_slices(); - - random::generator rng; - random::uniform_sphere_vector_distribution d; - - util::array, 2> grad({size / 8 + 1, size / 8 + 1}); - for (auto & v : grad) v = d(rng); - density = pcg::perlin(std::move(grad)); - - for (int i = 0; i < 8; ++i) - { - if (i < 4) - random_transform[i] = geom::matrix::identity(); - else - random_transform[i] = geom::swap(0, 1).homogeneous_matrix(); - - random_transform[i] = random_transform[i] - * geom::translation(geom::vector{0.5f, 0.5f, 0.f}).homogeneous_matrix() - * geom::plane_rotation(0, 1, i * geom::pi / 2.f).homogeneous_matrix() - * geom::translation(geom::vector{-0.5f, -0.5f, 0.f}).homogeneous_matrix(); - } - - random_transform_index.resize({size, size}); - for (auto & i : random_transform_index) - i = random::uniform_int_distribution{0, 7}(rng); - } - - void init_ground() - { - ground_mesh.setup, gfx::normalized>(); - - struct vertex - { - geom::point pos; - gfx::color_rgba color; - }; - - std::vector vertices; - std::vector> triangles; - - gfx::color_rgba ground_color{47, 23, 11, 255}; - - for (int x = 0; x < size; ++x) - { - for (int y = 0; y < size; ++y) - { - std::uint32_t base = vertices.size(); - - float d = 0.0f; - - vertices.push_back({{x + d, y + d, 0.f}, ground_color}); - vertices.push_back({{x + 1 - d, y + d, 0.f}, ground_color}); - vertices.push_back({{x + d, y + 1 - d, 0.f}, ground_color}); - vertices.push_back({{x + 1 - d, y + 1 - d, 0.f}, ground_color}); - - triangles.push_back({base + 0, base + 1, base + 2}); - triangles.push_back({base + 2, base + 1, base + 3}); - } - } - - ground_mesh.load(vertices, triangles, gl::STATIC_DRAW); - } - - void init_grass() - { - frame_clock.restart(); - struct vertex - { - geom::point pos; - std::uint8_t t; - std::uint8_t density; - - vertex(geom::point const & p, float t, float d) - { - for (std::size_t i = 0; i < 2; ++i) - pos[i] = geom::clamp(std::round((p[i] + 0.5f) / 2.f * 65535.f), {0.f, 65535.f}); - - pos[2] = geom::clamp(std::round(p[2] * 65535.f), {0.f, 65535.f}); - - this->t = geom::clamp(std::round(t * 255.f), {0.f, 255.f}); - this->density = geom::clamp(std::round(d * 255.f), {0.f, 255.f}); - } - }; - - { - geom::gradient g - { - std::make_pair(-1.f, gfx::color_4f{0.f, 0.2f, 0.f, 1.f}), - geom::easing_type::linear, - std::pair{0.f, gfx::color_4f{0.4f, 0.65f, 0.35f, 1.f}}, - geom::easing_type::linear, - std::pair{1.f, gfx::color_4f{0.7f, 0.75f, 0.2f, 1.f}} - }; - - util::array pm({256}); - for (std::size_t i = 0; i < pm.width(); ++i) - { - pm(i) = gfx::to_coloru8(g((i + 0.5f) / pm.width())); - } - - grass_texture.load(pm); - grass_texture.linear_filter(); - grass_texture.generate_mipmap(); - } - - random::generator rng; - - std::size_t memory = 0; - - grass_mesh.setup>, gfx::normalized, gfx::normalized>(); - - std::vector vertices; - std::vector> triangles; - random::uniform_box_point_distribution d_origin({{{0.f, 1.f}, {0.f, 1.f}}}); - random::uniform_sphere_vector_distribution d_orientation; - random::uniform_real_distribution d_width{1.f / 64.f, 1.f / 128.f}; - random::uniform_real_distribution d_height{0.25f, 1.f}; - random::uniform_real_distribution d_density{0.f, 1.f}; - - for (int blade = 0; blade < 4096; ++blade) - { - int const segments = 8; - float const max_lean = 0.2f; - - int tx = (blade % 64) % 8; - int ty = (blade % 64) / 8; - - auto o = d_origin(rng); - o[0] = o[0] / 8.f + tx / 8.f; - o[1] = o[1] / 8.f + ty / 8.f; - - auto r = d_orientation(rng); - auto s = d_width(rng) * 0.5f; - auto h = d_height(rng); - - auto n = geom::ort(r); - - auto color = random::uniform_real_distribution{0.f, 1.f}(rng); - - float den = d_density(rng); - - auto get = [&](float x, float z) -> vertex - { - assert(z >= 0.f); - assert(z <= 1.f); - float y = (1.f - std::sqrt(1.f - z * z)) * max_lean; - - float w = 1.f - z / h; - - return {geom::point{o[0] + r[0] * s * x * w + n[0] * y, o[1] + r[1] * s * x * w + n[1] * y, z}, color, den}; - }; - - - for (int s = 0; s <= segments; ++s) - { - float t = (s * 1.f) / segments; - t = geom::easing(geom::easing_type::quadratic_out, t); - float z = h * t; - std::uint32_t base = vertices.size(); - - if (s < segments) - { - vertices.push_back(get(-1.f, z)); - vertices.push_back(get( 1.f, z)); - - if (s > 0) - { - triangles.push_back({base - 2, base - 1, base}); - triangles.push_back({base, base - 1, base + 1}); - } - } - else - { - vertices.push_back(get(0.f, z)); - triangles.push_back({base - 2, base - 1, base}); - } - } - } - - grass_mesh.load(vertices, triangles, gl::STATIC_DRAW); - - memory += (vertices.size() * sizeof(vertices[0]) + triangles.size() * sizeof(triangles[0])); - - log::info() << memory << " bytes"; - log::info() << vertices.size() << " vertices"; - log::info() << frame_clock.count(); - } - - void init_grass_slices() - { - struct vertex - { - geom::point position; - geom::vector texcoord; - }; - - int const slice_count = 4; - float slice_width = 1.f / slice_count; - - std::vector vertices; - vertices.push_back({{0.f, 0.f, slice_width}, {0.f, 0.f}}); - vertices.push_back({{1.f, 0.f, slice_width}, {1.f, 0.f}}); - vertices.push_back({{0.f, 1.f, slice_width}, {0.f, 1.f}}); - vertices.push_back({{0.f, 1.f, slice_width}, {0.f, 1.f}}); - vertices.push_back({{1.f, 0.f, slice_width}, {1.f, 0.f}}); - vertices.push_back({{1.f, 1.f, slice_width}, {1.f, 1.f}}); - - grass_slice_z_mesh.setup, geom::vector>(); - grass_slice_z_mesh.load(vertices, gl::TRIANGLES, gl::STATIC_DRAW); - - std::size_t slice_resolution = 256; - - grass_slice_z_texture.load({slice_resolution, slice_resolution, density_level_count}); - grass_slice_renderbuffer.storage(gl::DEPTH24_STENCIL8, {slice_resolution, slice_resolution}); - for (int d = 0; d < density_level_count; ++d) - { - grass_slice_framebuffer.color(grass_slice_z_texture, d); - grass_slice_framebuffer.depth(grass_slice_renderbuffer); - grass_slice_framebuffer.assert_complete(); - gl::Viewport(0, 0, slice_resolution, slice_resolution); - gl::ClearColor(0.f, 0.f, 0.f, 0.f); - gl::Clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT); - gl::Enable(gl::DEPTH_TEST); - gl::DepthFunc(gl::LEQUAL); - grass_program.bind(); - grass_program["u_tile_transform"] = geom::matrix::identity(); - grass_program["u_height00"] = 1.f; - grass_program["u_height01"] = 1.f; - grass_program["u_height10"] = 1.f; - grass_program["u_height11"] = 1.f; - grass_program["u_texture"] = 0; - grass_texture.bind(); - for (int x = -1; x <= 1; ++x) - { - for (int y = -1; y <= 1; ++y) - { - grass_program["u_transform"] = geom::translation({-1.f + 2.f * x, -1.f + 2.f * y, 0.f}).homogeneous_matrix() * geom::scale({2.f, 2.f, 1.f}).homogeneous_matrix(); - grass_mesh.draw(0, (grass_mesh.index_count() * (d + 1)) / density_level_count); - } - } - } - gfx::framebuffer::null().bind(); - - grass_slice_z_texture.linear_filter(); - grass_slice_z_texture.anisotropy(); - grass_slice_z_texture.generate_mipmap(); - grass_slice_z_texture.clamp(); -// grass_slice_z_texture.bind(); -// gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::NEAREST); -// gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR); - } - - void on_resize(int width, int height) override - { - app::on_resize(width, height); - camera.set_fov(camera.fov_y, (1.f * width) / height); - } - - void on_mouse_move(int x, int y, int dx, int dy) override - { - app::on_mouse_move(x, y, dx, dy); - - if (is_middle_button_down()) - { - camera.rotateZX(0.01f * dx); - camera.rotateYZ(0.01f * dy); - } - } - - void on_mouse_wheel(int delta) override - { - app::on_mouse_wheel(delta); - } - - void update() override - { - float dt = update_clock.restart().count(); - - auto d = camera.direction(); - float s = 20.f; - - if (is_key_down(SDLK_LSHIFT)) - s = 2.5f; - - if (is_key_down(SDLK_SPACE)) - { - d[2] = 0.f; - d = geom::normalized(d); - } - - auto n = geom::normalized(geom::cross(d, geom::vector{0.f, 0.f, 1.f})); - - if (is_key_down(SDLK_w)) - { - camera.pos += d * dt * s; - } - - if (is_key_down(SDLK_s)) - { - camera.pos -= d * dt * s; - } - - if (is_key_down(SDLK_a)) - { - camera.pos -= n * dt * s; - } - - if (is_key_down(SDLK_d)) - { - camera.pos += n * dt * s; - } - } - - void present() override - { - gl::ClearColor(0.8f, 0.8f, 1.f, 1.f); - gl::Clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT); - - gl::Enable(gl::DEPTH_TEST); - gl::DepthFunc(gl::LEQUAL); - gl::Enable(gl::BLEND); - gl::BlendFunc(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA); - - auto camera_transform = camera.transform(); - - ground_program.bind(); - ground_program["u_transform"] = camera_transform; - ground_mesh.draw(); - - grass_program.bind(); - grass_program["u_texture"] = 0; - grass_texture.bind(); - grass_program["u_transform"] = camera_transform; - - std::size_t triangles = 0; - - for (int x = 0; x < size; ++x) - { - for (int y = 0; y < size; ++y) - { - if (x < size / 2) continue; - - grass_program["u_tile_transform"] = geom::translation(geom::vector{x, y, 0.f}).homogeneous_matrix() - * random_transform[random_transform_index(x, y)]; - - float d = density({(x + 0.5f) / size, (y + 0.5f) / size}); - - int i = std::floor(d * density_level_count); - if (i > density_level_count - 1) i = density_level_count - 1; - - { - float h00 = density({(x + 0.f) / size, (y + 0.f) / size}); - float h01 = density({(x + 1.f) / size, (y + 0.f) / size}); - float h10 = density({(x + 0.f) / size, (y + 1.f) / size}); - float h11 = density({(x + 1.f) / size, (y + 1.f) / size}); - - grass_program["u_height00"] = h00; - grass_program["u_height01"] = h01; - grass_program["u_height10"] = h10; - grass_program["u_height11"] = h11; - grass_mesh.draw(0, ((i + 1) * grass_mesh.index_count()) / density_level_count); - - triangles += (((i + 1) * grass_mesh.index_count()) / density_level_count) / 3; - } - } - } - - grass_slice_program.bind();; - grass_slice_program["u_transform"] = camera_transform; - grass_slice_program["u_texture"] = 0; - grass_slice_z_texture.bind(); - - for (int x = 0; x < size; ++x) - { - for (int y = 0; y < size; ++y) - { - if (x >= size / 2) continue; - - grass_slice_program["u_tile_transform"] = geom::translation(geom::vector{x, y, 0.f}).homogeneous_matrix() - * random_transform[random_transform_index(x, y)]; - - float d00 = density({(x + 0.f) / size, (y + 0.f) / size}) * density_level_count; - float d01 = density({(x + 1.f) / size, (y + 0.f) / size}) * density_level_count; - float d10 = density({(x + 0.f) / size, (y + 1.f) / size}) * density_level_count; - float d11 = density({(x + 1.f) / size, (y + 1.f) / size}) * density_level_count; - - grass_slice_program["u_density00"] = d00; - grass_slice_program["u_density01"] = d01; - grass_slice_program["u_density10"] = d10; - grass_slice_program["u_density11"] = d11; - - grass_slice_z_mesh.draw(); - } - } - - frame_time.push(frame_clock.restart().count()); - - { - gfx::painter::text_options opts; - opts.x = gfx::painter::x_align::left; - opts.y = gfx::painter::y_align::top; - opts.f = gfx::painter::font::font_9x12; - opts.scale = 2.f; - opts.c = {0, 0, 0, 255}; - - painter.text({0.f, 0.f}, util::to_string("FPS: ", std::round(1.0 / frame_time.average())), opts); - painter.text({0.f, 24.f}, util::to_string("Camera: ", camera.position()), opts); - painter.text({0.f, 48.f}, util::to_string("Triangles: ", triangles), opts); - } - - gl::Disable(gl::DEPTH_TEST); - gl::Enable(gl::BLEND); - gl::BlendFunc(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA); - geom::window_camera camera; - camera.width = width(); - camera.height = height(); - painter.render(camera.transform()); - } - -}; - -int main() -{ - return app::main(); -} diff --git a/examples/gravity.cpp b/examples/gravity.cpp index 4c156cdf..3cbd7b25 100644 --- a/examples/gravity.cpp +++ b/examples/gravity.cpp @@ -1,5 +1,5 @@ -#include -#include +#include +#include #include #include #include @@ -7,14 +7,12 @@ #include #include #include -#include -#include -#include -#include #include #include +#include #include +#include /* No optimizations, 125: @@ -85,15 +83,15 @@ geom::point world_center{0.f, 0.f}; int const SOLVE_ITERATIONS = 16; float const BIAS = 1.f / 8.f; -struct myapp : app::app +struct myapp + : app::application_base { - myapp() - : app("Test app", 4) - , rng_{std::random_device{}()} + myapp(options const &, context const & context) + : rng_{std::random_device{}()} { gl::ClearColor(1.f, 1.f, 1.f, 1.f); - vsync(true); + context.vsync(true); std::uniform_real_distribution d{-50.f, 50.f}; std::uniform_real_distribution rr{0.5f, 2.f}; @@ -181,32 +179,32 @@ struct myapp : app::app p.old_pos = p.pos; } - void on_resize(int width, int height) override + void on_event(app::resize_event const & event) override { - app::on_resize(width, height); + app::application_base::on_event(event); - gl::Viewport(0, 0, width, height); + gl::Viewport(0, 0, event.size[0], event.size[1]); - window_size_ = {width, height}; - - camera_ratio_ = static_cast(width) / height; + camera_ratio_ = static_cast(event.size[0]) / event.size[1]; } - void on_mouse_wheel(int delta) override + void on_event(app::mouse_wheel_event const & event) override { - camera_size_ *= std::pow(0.8f, delta); + app::application_base::on_event(event); + + camera_size_ *= std::pow(0.8f, event.delta); } - void on_left_button_down() override + void on_event(app::mouse_button_event const & event) override { - if (mouse_) + if (event.button == app::mouse_button::left && event.down) { geom::scale const flip_y({1.f, -1.f}); - float const scale = camera_size_ / window_size_[1]; + float const scale = camera_size_ / state().size[1]; - geom::point const screen_center { window_size_[0] / 2, window_size_[1] / 2 }; - auto target = camera_center_ + flip_y(geom::cast(*mouse_ - screen_center) * scale); + geom::point const screen_center { state().size[0] / 2,state().size[1] / 2 }; + auto target = camera_center_ + flip_y(geom::cast(state().mouse - screen_center) * scale); force_target_ = target; // geom::point pos{100.f, 0.f}; @@ -221,57 +219,57 @@ struct myapp : app::app // p.vel += 4000.f * r / geom::length_sqr(r) / p.mass; // } } + + if (event.button == app::mouse_button::left && !event.down) + { + force_target_ = std::nullopt; + } + + if (event.button == app::mouse_button::right && event.down) + { + camera_drag_ = state().mouse; + } + + if (event.button == app::mouse_button::right && !event.down) + { + camera_drag_ = std::nullopt; + } } - void on_left_button_up() override + void on_event(app::mouse_move_event const & event) override { - force_target_ = std::nullopt; - } - - void on_right_button_down() override - { - camera_drag_ = mouse_; - } - - void on_right_button_up() override - { - camera_drag_ = std::nullopt; - } - - void on_mouse_move(int x, int y, int, int) override - { - mouse_ = {x, y}; + app::application_base::on_event(event); if (camera_drag_) { geom::scale const flip_y({1.f, -1.f}); - float const scale = camera_size_ / window_size_[1]; + float const scale = camera_size_ / state().size[1]; - camera_center_ += flip_y(geom::cast(*camera_drag_ - *mouse_) * scale); - camera_drag_ = mouse_; + camera_center_ += flip_y(geom::cast(*camera_drag_ - event.position) * scale); + camera_drag_ = event.position; } if (force_target_) { geom::scale const flip_y({1.f, -1.f}); - float const scale = camera_size_ / window_size_[1]; - geom::point const screen_center { window_size_[0] / 2, window_size_[1] / 2 }; - auto target = camera_center_ + flip_y(geom::cast(*mouse_ - screen_center) * scale); + float const scale = camera_size_ / state().size[1]; + geom::point const screen_center { state().size[0] / 2, state().size[1] / 2 }; + auto target = camera_center_ + flip_y(geom::cast(state().mouse - screen_center) * scale); force_target_ = target; } } - void on_key_down(SDL_Keycode key) override + void on_event(app::key_event const & event) override { - app::app::on_key_down(key); + app::application_base::on_event(event); - if (key == SDLK_SPACE) + if (event.key == app::keycode::SPACE && event.down) { paused_ = !paused_; } - if (key == SDLK_c) + if (event.key == app::keycode::C && event.down) { particles_.push_back({{200.f, 0.f}, {-5000.f, 0.f}, 0.f, 0.f, 1.f, 100.f, 1.f}); particles_.back().old_pos = particles_.back().pos; @@ -365,7 +363,7 @@ struct myapp : app::app // log::info() << "Force: #" << i << " = " << std::setprecision(10) << particles_[i].acc; // } - if (is_key_down(SDLK_m)) + if (state().key_down.contains(app::keycode::M)) { for (auto & p : particles_) p.vel *= std::exp(-100.f*dt); @@ -1188,7 +1186,7 @@ struct myapp : app::app float c = 1.f - p.density; auto x = static_cast(c * 255.f); - float s = window_size_[1] / camera_size_; + float s = state().size[1] / camera_size_; float r = std::max(p.radius * s, 1.5f) / s; painter_.circle(p.pos, r, {x, x, x, 191}); @@ -1225,15 +1223,15 @@ struct myapp : app::app painter_.text(pos + geom::vector{1.f, 0.f}, str, opts); }; - put({width() / 2.f, 30.f}, util::to_string("ITERATIONS: ", SOLVE_ITERATIONS)); - put({width() / 2.f, 60.f}, BIAS == 1.f ? "BIAS: 1" : util::to_string("BIAS: 1/", std::round(1.f / BIAS))); + put({state().size[0] / 2.f, 30.f}, util::to_string("ITERATIONS: ", SOLVE_ITERATIONS)); + put({state().size[0] / 2.f, 60.f}, BIAS == 1.f ? "BIAS: 1" : util::to_string("BIAS: 1/", std::round(1.f / BIAS))); // painter_.text({10.f, 10.f}, util::to_string("Angular velocity: ", rotation_.average()), opts); // painter_.text({10.f, 30.f}, util::to_string("Collisions: ", collisions_, " = ", (collisions_ * 1.f / particles_.size() / particles_.size()), " N^2"), opts); // painter_.text({10.f, 50.f}, util::to_string("Energy: ", energy_), opts); } - painter_.render(geom::window_camera{width(), height()}.transform()); + painter_.render(geom::window_camera{state().size[0], state().size[1]}.transform()); } ~myapp() @@ -1247,15 +1245,12 @@ struct myapp : app::app private: std::default_random_engine rng_; - geom::vector window_size_; - std::optional> force_target_; geom::point camera_center_ { 0.f, 0.f }; float camera_size_ = 150.f; float camera_ratio_ = 1.f; - std::optional> mouse_; std::optional> camera_drag_; gfx::painter painter_; @@ -1266,8 +1261,6 @@ private: float total_forces_ = 0.f; float total_collisions_ = 0.f; - audio::engine audio_; - util::moving_average rotation_{300}; int collisions_ = 0; float energy_ = 0.f; @@ -1275,7 +1268,12 @@ private: bool paused_ = true; }; -int main() +namespace psemek::app { - return app::main(); + + std::unique_ptr make_application_factory() + { + return default_application_factory({.name = "Gravity example"}); + } + } diff --git a/examples/parser.cpp b/examples/parser.cpp index 2a84a4b6..4d4be059 100644 --- a/examples/parser.cpp +++ b/examples/parser.cpp @@ -1,5 +1,7 @@ #include +#include + #include #include #include @@ -74,14 +76,23 @@ Stream & operator << (Stream & s, std::variant const & v) return s; } -int main() +namespace psemek::app { - using namespace psemek::parser; - auto const p = map(concat(integer, ws, one_of(ch('+'), ch('-')), ws, integer), [](auto const & t){ - auto id = [](auto x){ return x; }; - return std::make_tuple(std::get<0>(t), std::visit(id, std::get<2>(t)), std::get<4>(t)); - }); + std::unique_ptr make_application_factory() + { + return default_application_factory({.name = "Parser example"}, [](auto const & ...){ + using namespace psemek::parser; + + auto const p = map(concat(integer, ws, one_of(ch('+'), ch('-')), ws, integer), [](auto const & t){ + auto id = [](auto x){ return x; }; + return std::make_tuple(std::get<0>(t), std::visit(id, std::get<2>(t)), std::get<4>(t)); + }); + + std::cout << p.parse("45 + 67") << std::endl; + + return nullptr; + }); + } - std::cout << p.parse("45 + 67") << std::endl; } diff --git a/examples/physics.cpp b/examples/physics.cpp index 855fcc96..cf282188 100644 --- a/examples/physics.cpp +++ b/examples/physics.cpp @@ -1,5 +1,5 @@ -#include -#include +#include +#include #include #include @@ -17,6 +17,8 @@ #include #include +#include + #include using namespace psemek; @@ -69,23 +71,20 @@ void stick_model::add_stick(std::size_t i, std::size_t j, bool solid) static const float ball_radius = 0.1f; struct physics_demo_app - : app::app + : app::application_base { - physics_demo_app(); + physics_demo_app(options const &, context const &); ~physics_demo_app(); - void on_resize(int width, int height) override; + void on_event(app::resize_event const & event) override; - void on_left_button_down() override; - void on_left_button_up() override; + void on_event(app::mouse_button_event const & event) override; - void on_right_button_down() override; + void on_event(app::mouse_move_event const & event) override; - void on_mouse_move(int x, int y, int, int) override; + void on_event(app::key_event const & event) override; - void on_key_down(SDL_Keycode key) override; - - void on_mouse_wheel(int delta) override; + void on_event(app::mouse_wheel_event const & event) override; void update() override; void present() override; @@ -115,8 +114,7 @@ struct physics_demo_app util::statistics physics_update_stats; }; -physics_demo_app::physics_demo_app() - : app("Physics Demo", 4) +physics_demo_app::physics_demo_app(options const &, context const &) { view_region[0] = {0.f, 0.f}; view_region[1] = {-2.f, 5.f}; @@ -204,11 +202,11 @@ physics_demo_app::~physics_demo_app() log::info() << "Update: " << physics_update_stats; } -void physics_demo_app::on_resize(int width, int height) +void physics_demo_app::on_event(app::resize_event const & event) { - app::on_resize(width, height); + app::application_base::on_event(event); - float aspect_ratio = static_cast(width) / height; + float aspect_ratio = static_cast(event.size[0]) / event.size[1]; float xc = view_region[0].center(); float yl = view_region[1].length(); @@ -217,49 +215,46 @@ void physics_demo_app::on_resize(int width, int height) view_region[0].max = xc + yl / 2.f * aspect_ratio; } -void physics_demo_app::on_left_button_down() +void physics_demo_app::on_event(app::mouse_button_event const & event) { - app::on_left_button_down(); + app::application_base::on_event(event); - if (mouse() && closest_point) + if (event.button == app::mouse_button::left && event.down && closest_point) { - drag_delta = world_to_screen(model.points[*closest_point]) - geom::cast(*mouse()); + drag_delta = world_to_screen(model.points[*closest_point]) - geom::cast(state().mouse); model.vels[*closest_point] = geom::vector::zero(); } -} -void physics_demo_app::on_left_button_up() -{ - app::on_left_button_up(); - - drag_delta = std::nullopt; -} - -void physics_demo_app::on_right_button_down() -{ - if (new_spring_start) + if (event.button == app::mouse_button::left && !event.down) { - new_spring_start = std::nullopt; + drag_delta = std::nullopt; } - else if (closest_point) + + if (event.button == app::mouse_button::right && event.down) { - model.movable[*closest_point] = !model.movable[*closest_point]; - model.vels[*closest_point] = geom::vector::zero(); + if (new_spring_start) + { + new_spring_start = std::nullopt; + } + else if (closest_point) + { + model.movable[*closest_point] = !model.movable[*closest_point]; + model.vels[*closest_point] = geom::vector::zero(); + } } } -void physics_demo_app::on_mouse_move(int x, int y, int dx, int dy) +void physics_demo_app::on_event(app::mouse_move_event const & event) { - app::on_mouse_move(x, y, dx, dy); + app::application_base::on_event(event); if (!drag_delta) { closest_point = std::nullopt; closest_stick = std::nullopt; - if (mouse()) { - auto const m = geom::cast(*mouse()); + auto const m = geom::cast(state().mouse); std::size_t closest = 0; float distance = std::numeric_limits::infinity(); @@ -303,13 +298,15 @@ void physics_demo_app::on_mouse_move(int x, int y, int dx, int dy) } } -void physics_demo_app::on_key_down(SDL_Keycode key) +void physics_demo_app::on_event(app::key_event const & event) { - if (key == SDLK_SPACE) + app::application_base::on_event(event); + + if (event.down && event.key == app::keycode::SPACE) { paused = !paused; } - else if (key == SDLK_x) + else if (event.down && event.key == app::keycode::X) { if (closest_point) { @@ -350,7 +347,7 @@ void physics_demo_app::on_key_down(SDL_Keycode key) closest_stick = std::nullopt; } } - else if (key == SDLK_c) + else if (event.down && event.key == app::keycode::C) { if (new_spring_start && closest_point) { @@ -360,9 +357,9 @@ void physics_demo_app::on_key_down(SDL_Keycode key) new_spring_start = std::nullopt; } } - else if (new_spring_start && mouse()) + else if (new_spring_start) { - model.points.push_back(screen_to_world(geom::cast(*mouse()))); + model.points.push_back(screen_to_world(geom::cast(state().mouse))); model.vels.push_back(geom::vector::zero()); model.movable.push_back(false); model.add_stick(*new_spring_start, model.points.size() - 1); @@ -373,65 +370,59 @@ void physics_demo_app::on_key_down(SDL_Keycode key) new_spring_start = *closest_point; closest_point = std::nullopt; } - else if (mouse()) + else { - model.points.push_back(screen_to_world(geom::cast(*mouse()))); + model.points.push_back(screen_to_world(geom::cast(state().mouse))); model.vels.push_back(geom::vector::zero()); model.movable.push_back(false); } } - else if (key == SDLK_n) + else if (event.down && event.key == app::keycode::N) { - if (mouse()) - { - model.points.push_back(screen_to_world(geom::cast(*mouse()))); - model.vels.push_back(geom::vector::zero()); - model.movable.push_back(true); - } + model.points.push_back(screen_to_world(geom::cast(state().mouse))); + model.vels.push_back(geom::vector::zero()); + model.movable.push_back(true); } - else if (key == SDLK_f) + else if (event.down && event.key == app::keycode::F) { if (closest_stick) { model.stick_solid[*closest_stick] = !model.stick_solid[*closest_stick]; } } - else if (key == SDLK_w) - { - if (mouse()) - { - std::uint32_t const base = model.points.size(); - - int N = 12; - - auto o = screen_to_world(geom::cast(*mouse())); - - model.points.push_back(o); - model.vels.push_back(geom::vector::zero()); - model.movable.push_back(true); - - float r = 0.5f; - - for (int i = 0; i < N; ++i) - { - float a = (2.f * geom::pi * i) / N; - model.points.push_back({o[0] + std::cos(a) * r, o[1] + std::sin(a) * r}); - model.vels.push_back(geom::vector::zero()); - model.movable.push_back(true); - } - - for (int i = 0; i < N; ++i) - { - model.add_stick(base, base + i + 1, false); - model.add_stick(base + i + 1, base + 1 + ((i + 1) % N)); - } - } - } - else if (key == SDLK_b) + else if (event.down && event.key == app::keycode::W) { std::uint32_t const base = model.points.size(); - auto o = screen_to_world(geom::cast(*mouse())); + int N = 12; + + auto o = screen_to_world(geom::cast(state().mouse)); + + model.points.push_back(o); + model.vels.push_back(geom::vector::zero()); + model.movable.push_back(true); + + float r = 0.5f; + + for (int i = 0; i < N; ++i) + { + float a = (2.f * geom::pi * i) / N; + model.points.push_back({o[0] + std::cos(a) * r, o[1] + std::sin(a) * r}); + model.vels.push_back(geom::vector::zero()); + model.movable.push_back(true); + } + + for (int i = 0; i < N; ++i) + { + model.add_stick(base, base + i + 1, false); + model.add_stick(base + i + 1, base + 1 + ((i + 1) % N)); + } + } + else if (event.down && event.key == app::keycode::B) + { + std::uint32_t const base = model.points.size(); + + auto o = screen_to_world(geom::cast(state().mouse)); float w = 0.25f; @@ -458,13 +449,15 @@ void physics_demo_app::on_key_down(SDL_Keycode key) } } -void physics_demo_app::on_mouse_wheel(int delta) +void physics_demo_app::on_event(app::mouse_wheel_event const & event) { + app::application_base::on_event(event); + if (closest_stick) { auto & l = model.stick_length[*closest_stick]; - l += delta * ball_radius / 2.f; + l += event.delta * ball_radius / 2.f; l = std::max(3.f * ball_radius, l); } @@ -584,9 +577,9 @@ void physics_demo_app::update() std::optional fixed; - if (closest_point && drag_delta && mouse()) + if (closest_point && drag_delta) { - auto target = screen_to_world(geom::cast(*mouse()) + *drag_delta); + auto target = screen_to_world(geom::cast(state().mouse) + *drag_delta); auto & point = model.points[*closest_point]; auto & vel = model.vels[*closest_point]; @@ -807,7 +800,7 @@ void physics_demo_app::update() void physics_demo_app::present() { - gl::Viewport(0, 0, width(), height()); + gl::Viewport(0, 0, state().size[0], state().size[1]); gl::ClearColor(0.8f, 0.8f, 0.9f, 0.f); gl::Clear(gl::COLOR_BUFFER_BIT); @@ -821,9 +814,9 @@ void physics_demo_app::present() model.stick_solid[i] ? gfx::color_rgba{0, 0, 0, 255} : gfx::color_rgba{63, 63, 63, 255}); } - if (new_spring_start && mouse()) + if (new_spring_start) { - painter.line(model.points[*new_spring_start], screen_to_world(geom::cast(*mouse())), ball_radius / 2.f, gfx::red); + painter.line(model.points[*new_spring_start], screen_to_world(geom::cast(state().mouse)), ball_radius / 2.f, gfx::red); } if (closest_stick) @@ -872,22 +865,28 @@ void physics_demo_app::present() text.clear(); - painter.render(geom::window_camera{width(), height()}.transform()); + painter.render(geom::window_camera{state().size[0], state().size[1]}.transform()); } geom::point physics_demo_app::screen_to_world(geom::point const & p) const { - return geom::orthographic(view_region).inverse()(geom::point{2.f * p[0] / width() - 1.f, 1.f - 2.f * p[1] / height()}); + return geom::orthographic(view_region).inverse()(geom::point{2.f * p[0] / state().size[0] - 1.f, 1.f - 2.f * p[1] / state().size[1]}); } geom::point physics_demo_app::world_to_screen(geom::point const & p) const { auto q = geom::orthographic(view_region)(p); - return {(q[0] + 1.f) / 2.f * width(), (1.f - q[1]) / 2.f * height()}; + return {(q[0] + 1.f) / 2.f * state().size[0], (1.f - q[1]) / 2.f * state().size[1]}; } -int main() +namespace psemek::app { - return app::main(); + + std::unique_ptr make_application_factory() + { + return default_application_factory({.name = "Physics example"}); + } + } + diff --git a/examples/physics_2d.cpp b/examples/physics_2d.cpp index 63b258e3..b188f13f 100644 --- a/examples/physics_2d.cpp +++ b/examples/physics_2d.cpp @@ -1,5 +1,5 @@ -#include -#include +#include +#include #include @@ -18,13 +18,15 @@ #include #include +#include + #include #include using namespace psemek; struct physics_2d_app - : app::app + : app::application_base { phys2d::engine physics; @@ -66,8 +68,7 @@ struct physics_2d_app random::generator gen; - physics_2d_app() - : app("Physics 2D example", 4) + physics_2d_app(options const &, context const &) { simulation_box = {{{-world_width, world_width}, {-world_height, world_height}}}; @@ -201,11 +202,11 @@ struct physics_2d_app prof::dump(); } - void on_resize(int width, int height) override + void on_event(app::resize_event const & event) override { - app::on_resize(width, height); + app::application_base::on_event(event); - float const ratio = static_cast(width) / height; + float const ratio = static_cast(event.size[0]) / event.size[1]; float const c = view_box[0].center(); float const l = view_box[1].length() * ratio; @@ -213,52 +214,51 @@ struct physics_2d_app view_box[0] = {c - l / 2.f, c + l / 2.f}; } - void on_left_button_down() override + void on_event(app::mouse_button_event const & event) override { - app::on_left_button_down(); + app::application_base::on_event(event); - if (selected_ball && mouse) + if (event.button == app::mouse_button::left && event.down) { - auto const & s = physics.group_static_state(ball_group)[*selected_ball]; - auto r = s.position - *mouse; - r = geom::plane_rotation(0, 1, -s.rotation)(r); - drag_delta = r; + if (selected_ball && mouse) + { + auto const & s = physics.group_static_state(ball_group)[*selected_ball]; + auto r = s.position - *mouse; + r = geom::plane_rotation(0, 1, -s.rotation)(r); + drag_delta = r; + } + else if (mouse) + { + // physics.add_object(ball_group, ball_shape, material, {*mouse, 0.f}, {{0.f, 0.f}, 0.f}); + // physics.add_object(ball_group, large_ball_shape, material, {*mouse, 0.f}, {}); + physics.add_object(box_group, box_shape, material, {*mouse, 0.f}, {}); + // physics.add_object(box_group, wide_box_shape, material, {*mouse, 0.f}, {}); + } } - else if (mouse) + + if (event.button == app::mouse_button::left && !event.down) { -// physics.add_object(ball_group, ball_shape, material, {*mouse, 0.f}, {{0.f, 0.f}, 0.f}); -// physics.add_object(ball_group, large_ball_shape, material, {*mouse, 0.f}, {}); - physics.add_object(box_group, box_shape, material, {*mouse, 0.f}, {}); -// physics.add_object(box_group, wide_box_shape, material, {*mouse, 0.f}, {}); + drag_delta = std::nullopt; + } + + if (event.button == app::mouse_button::right && event.down) + { + if (mouse) + { + physics.add_object(ball_group, ball_shape, material, {*mouse, 0.f}, {{0.f, 0.f}, 0.f}); + // physics.add_object(box_group, box_shape, material, {*mouse, 0.f}, {}); + // physics.add_object(box_group, wide_box_shape, material, {*mouse, 0.f}, {}); + // physics.explode(*mouse, 10.f, 100.f); + } } } - void on_left_button_up() override + void on_event(app::mouse_move_event const & event) override { - app::on_left_button_up(); + app::application_base::on_event(event); - drag_delta = std::nullopt; - } - - void on_right_button_down() override - { - app::on_right_button_down(); - - if (mouse) - { - physics.add_object(ball_group, ball_shape, material, {*mouse, 0.f}, {{0.f, 0.f}, 0.f}); -// physics.add_object(box_group, box_shape, material, {*mouse, 0.f}, {}); -// physics.add_object(box_group, wide_box_shape, material, {*mouse, 0.f}, {}); -// physics.explode(*mouse, 10.f, 100.f); - } - } - - void on_mouse_move(int x, int y, int dx, int dy) override - { - app::on_mouse_move(x, y, dx, dy); - - float mx = x * 1.f / width(); - float my = 1.f - y * 1.f / height(); + float mx = event.position[0] * 1.f / state().size[0]; + float my = 1.f - event.position[1] * 1.f / state().size[1]; mouse = view_box.corner(mx, my); } @@ -270,9 +270,9 @@ struct physics_2d_app float MOTOR = 15.f; float m = 0.f; - if (is_key_down(SDLK_LEFT)) + if (state().key_down.contains(app::keycode::LEFT)) m += MOTOR; - if (is_key_down(SDLK_RIGHT)) + if (state().key_down.contains(app::keycode::RIGHT)) m += -MOTOR; m = 15.f; motor = m; @@ -488,7 +488,12 @@ struct physics_2d_app }; -int main() +namespace psemek::app { - return app::main(); + + std::unique_ptr make_application_factory() + { + return default_application_factory({.name = "Physics example", .multisampling = 4}); + } + } diff --git a/examples/physics_3d.cpp b/examples/physics_3d.cpp index 51e82655..40ed5928 100644 --- a/examples/physics_3d.cpp +++ b/examples/physics_3d.cpp @@ -1,5 +1,5 @@ -#include -#include +#include +#include #include @@ -23,6 +23,8 @@ #include +#include + using namespace psemek; static char const program_vs[] = @@ -85,11 +87,10 @@ void main() )"; struct physics_3d_app - : app::app + : app::application_base { - physics_3d_app() - : app::app("Physics 3D", 4) - , program_(program_vs, program_fs) + physics_3d_app(options const &, context const &) + : program_(program_vs, program_fs) , rng_{random::device{}} { camera_.near_clip = 0.1f; @@ -204,34 +205,37 @@ struct physics_3d_app // engine_.add_object(engine_.add_shape(phys3d::half_space{{ 0.f, -1.f, 0.f}, -10.f}), engine_.add_material({1.f, 1.f, 50.f}), {{0.f, 0.f, 0.f}}); } - void on_resize(int width, int height) override + void on_event(app::resize_event const & event) override { - app::app::on_resize(width, height); + app::application_base::on_event(event); - camera_.set_fov(camera_.fov_y, (1.f * width) / height); + camera_.set_fov(camera_.fov_y, (1.f * event.size[0]) / event.size[1]); } - void on_mouse_move(int x, int y, int dx, int dy) override + void on_event(app::mouse_move_event const & event) override { - app::app::on_mouse_move(x, y, dx, dy); + auto const old_mouse = state().mouse; - if (is_right_button_down()) + app::application_base::on_event(event); + + if (state().mouse_button_down.contains(app::mouse_button::right)) { - camera_elevation_angle_tgt_ += dy * 0.003f; - camera_azimuthal_angle_tgt_ -= dx * 0.003f; + auto const delta = event.position - old_mouse; + camera_elevation_angle_tgt_ += delta[1] * 0.003f; + camera_azimuthal_angle_tgt_ -= delta[0] * 0.003f; } } - void on_mouse_wheel(int delta) override + void on_event(app::mouse_wheel_event const & event) override { - app::app::on_mouse_wheel(delta); + app::application_base::on_event(event); - camera_distance_tgt_ *= std::pow(0.8f, delta); + camera_distance_tgt_ *= std::pow(0.8f, event.delta); } - void on_key_down(SDL_Keycode key) override + void on_event(app::key_event const & event) override { - if (key == SDLK_SPACE) + if (event.down && event.key == app::keycode::SPACE) { // float r = 7.f; // engine_.add_object(engine_.add_shape(phys3d::ball{3.f}), engine_.add_material({1.f, 0.125f, 50.f}), {{random::uniform(rng_, -r, r), random::uniform(rng_, -r, r), 20.f}}); @@ -249,7 +253,7 @@ struct physics_3d_app engine_.add_object(engine_.add_shape(phys3d::box{{1.5f, 1.5f, 1.5f}}), engine_.add_material({1.f, 0.f, 10.f}), state); // engine_.add_object(engine_.add_shape(phys3d::ball{1.f}), engine_.add_material({1.f, 0.5f, 100.f}), state); } - else if (key == SDLK_f) + else if (event.down && event.key == app::keycode::F) { float const f = 1.f; for (std::uint32_t h = 1; h < engine_.object_count(); ++h) @@ -358,7 +362,12 @@ private: random::generator rng_; }; -int main() +namespace psemek::app { - return app::main(); + + std::unique_ptr make_application_factory() + { + return default_application_factory({.name = "Physics 3D example"}); + } + } diff --git a/examples/platformer.cpp b/examples/platformer.cpp index 85e16683..554f4303 100644 --- a/examples/platformer.cpp +++ b/examples/platformer.cpp @@ -1,5 +1,5 @@ -#include -#include +#include +#include #include #include #include @@ -44,10 +44,10 @@ struct particle gfx::color_rgba color; }; -struct platformer_app : app::app +struct platformer_app + : app::application_base { - platformer_app() - : app("Platformer", 4) + platformer_app(options const &, context const &) { map_.platforms.push_back({{{-10.f, 10.f}, {-5.f, -4.f}}}); @@ -129,18 +129,18 @@ struct platformer_app : app::app } } - if (is_key_down(SDLK_a)) + if (state().key_down.contains(app::keycode::A)) player_.velocity[0] -= acceleration * dt; - if (is_key_down(SDLK_d)) + if (state().key_down.contains(app::keycode::D)) player_.velocity[0] += acceleration * dt; - if (player_.grounded && (is_key_down(SDLK_a) ^ is_key_down(SDLK_d))) + if (player_.grounded && (state().key_down.contains(app::keycode::A) ^ state().key_down.contains(app::keycode::D))) { move_particle_spawn_timer_ += dt; if (move_particle_spawn_timer_ > move_particle_spawn_period) { move_particle_spawn_timer_ -= move_particle_spawn_period; - float s = is_key_down(SDLK_a) ? 1.f : -1.f; + float s = state().key_down.contains(app::keycode::A) ? 1.f : -1.f; auto position = player_.position + geom::vector{s * player_.size[0], -player_.size[1]}; auto velocity = geom::direction(random::uniform(rng_, geom::rad(60.f), geom::rad(120.f))); @@ -153,7 +153,7 @@ struct platformer_app : app::app } } - if (player_.grounded && is_key_down(SDLK_w)) + if (player_.grounded && state().key_down.contains(app::keycode::W)) { player_.velocity[1] += jump_speed; @@ -228,7 +228,7 @@ struct platformer_app : app::app painter_.circle(p.position, p.size, gfx::to_coloru8(colorf)); } - float const aspect_ratio = width() * 1.f / height(); + float const aspect_ratio = state().size[0] * 1.f / state().size[1]; float const view_size = 5.f; geom::box view_box{{{-view_size * aspect_ratio, view_size * aspect_ratio}, {-view_size, view_size}}}; @@ -247,7 +247,12 @@ private: gfx::painter painter_; }; -int main() +namespace psemek::app { - return app::main(); + + std::unique_ptr make_application_factory() + { + return default_application_factory({.name = "Platformer example"}); + } + } diff --git a/examples/shadow.cpp b/examples/shadow.cpp index 3e620a44..4a3f5867 100644 --- a/examples/shadow.cpp +++ b/examples/shadow.cpp @@ -1,5 +1,5 @@ -#include -#include +#include +#include #include #include @@ -484,7 +484,7 @@ struct vertex }; struct shadow_app - : app::app + : app::application_base { geom::spherical_camera camera; @@ -509,23 +509,23 @@ struct shadow_app float time = 0.f; bool paused = false; - shadow_app(); + shadow_app(options const &, context const &); - void on_resize(int width, int height) override; + void on_event(app::resize_event const & event) override; - void on_mouse_move(int x, int y, int dx, int dy) override; + void on_event(app::mouse_move_event const & event) override; - void on_mouse_wheel(int delta) override; + void on_event(app::mouse_wheel_event const & event) override; - void on_key_down(SDL_Keycode key) override; + void on_event(app::key_event const & event) override; + void update() override {} void present() override; }; -shadow_app::shadow_app() - : app("Shadow") +shadow_app::shadow_app(options const &, context const & context) { - vsync(true); + context.vsync(true); camera.near_clip = 0.1f; camera.far_clip = 1000.f; @@ -702,34 +702,39 @@ shadow_app::shadow_app() } } -void shadow_app::on_resize(int width, int height) +void shadow_app::on_event(app::resize_event const & event) { - app::on_resize(width, height); + app::application_base::on_event(event); - camera.set_fov(camera.fov_y, (1.f * width) / height); + camera.set_fov(camera.fov_y, (1.f * event.size[0]) / event.size[1]); } -void shadow_app::on_mouse_move(int x, int y, int dx, int dy) +void shadow_app::on_event(app::mouse_move_event const & event) { - app::on_mouse_move(x, y, dx, dy); + auto const old_mouse = state().mouse; - if (is_middle_button_down()) + app::application_base::on_event(event); + + if (state().mouse_button_down.contains(app::mouse_button::middle)) { - camera.azimuthal_angle -= dx * 0.01f; - camera.elevation_angle += dy * 0.01f; + auto const delta = event.position - old_mouse; + camera.azimuthal_angle -= delta[0] * 0.01f; + camera.elevation_angle += delta[1] * 0.01f; } } -void shadow_app::on_mouse_wheel(int delta) +void shadow_app::on_event(app::mouse_wheel_event const & event) { - app::on_mouse_wheel(delta); + app::application_base::on_event(event); - camera.distance *= std::pow(0.8f, delta); + camera.distance *= std::pow(0.8f, event.delta); } -void shadow_app::on_key_down(SDL_Keycode key) +void shadow_app::on_event(app::key_event const & event) { - if (key == SDLK_SPACE) + app::application_base::on_event(event); + + if (event.down && event.key == app::keycode::SPACE) paused = !paused; } @@ -745,7 +750,7 @@ void shadow_app::present() gfx::framebuffer::null().bind(); gl::DrawBuffer(gl::BACK); - gl::Viewport(0, 0, width(), height()); + gl::Viewport(0, 0, state().size[0], state().size[1]); gl::ClearColor(0.7f, 0.7f, 1.f, 0.f); gl::Clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT); @@ -760,7 +765,7 @@ void shadow_app::present() shadow_renderer::render_options options; options.framebuffer = &gfx::framebuffer::null(); - options.viewport = {{{0, width()}, {0, height()}}}; + options.viewport = {{{0, state().size[0]}, {0, state().size[1]}}}; options.draw_buffer = gl::BACK; options.transform = camera.transform(); options.light.position = geom::homogeneous(light_dir); @@ -771,7 +776,13 @@ void shadow_app::present() gfx::check_error(); } -int main() +namespace psemek::app { - return app::main(); + + std::unique_ptr make_application_factory() + { + return default_application_factory({.name = "Shadow example"}); + } + } + diff --git a/examples/srtm.cpp b/examples/srtm.cpp index 14407dc5..1307c83c 100644 --- a/examples/srtm.cpp +++ b/examples/srtm.cpp @@ -1,5 +1,5 @@ -#include -#include +#include +#include #include #include @@ -601,13 +601,13 @@ void main() })"; struct srtm_app - : app::app + : app::application_base { - srtm_app(); + srtm_app(options const &, context const &); - void on_resize(int width, int height) override; + void on_event(app::resize_event const & event) override; - void on_mouse_move(int x, int y, int dx, int dy) override; + void on_event(app::mouse_move_event const & event) override; void update() override; void present() override; @@ -632,11 +632,10 @@ struct srtm_app gfx::painter painter; }; -srtm_app::srtm_app() - : app("SRTM", 4) +srtm_app::srtm_app(options const &, context const & context) { - vsync(true); - show_cursor(false); + context.vsync(true); + context.show_cursor(false); camera.fov_y = geom::rad(45.f); camera.near_clip = 0.0001f; @@ -688,19 +687,23 @@ srtm_app::srtm_app() } } -void srtm_app::on_resize(int width, int height) +void srtm_app::on_event(app::resize_event const & event) { - app::on_resize(width, height); - camera.set_fov(camera.fov_y, (1.f * width) / height); + app::application_base::on_event(event); + + camera.set_fov(camera.fov_y, (1.f * event.size[0]) / event.size[1]); camera_transform = camera.transform(); } -void srtm_app::on_mouse_move(int x, int y, int dx, int dy) +void srtm_app::on_event(app::mouse_move_event const & event) { - app::on_mouse_move(x, y, dx, dy); + auto const old_mouse = state().mouse; - camera.rotateZX(0.01f * dx); - camera.rotateYZ(0.01f * dy); + app::application_base::on_event(event); + + auto const delta = event.position - old_mouse; + camera.rotateZX(0.01f * delta[0]); + camera.rotateYZ(0.01f * delta[1]); } void srtm_app::update() @@ -708,12 +711,12 @@ void srtm_app::update() float dt = frame_clock.restart().count(); frame_dt_average.push(dt); - if (is_key_down(SDLK_q)) + if (state().key_down.contains(app::keycode::Q)) { camera.rotateXY(- 4.f * dt); } - if (is_key_down(SDLK_e)) + if (state().key_down.contains(app::keycode::E)) { camera.rotateXY(4.f * dt); } @@ -724,32 +727,32 @@ void srtm_app::update() auto const camera_up = camera.axis_y(); auto const camera_right = camera.axis_x(); - if (is_key_down(SDLK_w)) + if (state().key_down.contains(app::keycode::W)) { camera.pos += camera_speed * dt * camera_forward; } - if (is_key_down(SDLK_s)) + if (state().key_down.contains(app::keycode::S)) { camera.pos -= camera_speed * dt * camera_forward; } - if (is_key_down(SDLK_d)) + if (state().key_down.contains(app::keycode::D)) { camera.pos += camera_speed * dt * camera_right; } - if (is_key_down(SDLK_a)) + if (state().key_down.contains(app::keycode::A)) { camera.pos -= camera_speed * dt * camera_right; } - if (is_key_down(SDLK_LSHIFT)) + if (state().key_down.contains(app::keycode::LSHIFT)) { camera.pos += camera_speed * dt * camera_up; } - if (is_key_down(SDLK_LCTRL)) + if (state().key_down.contains(app::keycode::LCTRL)) { camera.pos -= camera_speed * dt * camera_up; } @@ -911,7 +914,7 @@ void srtm_app::present() distance = std::min(distance, geom::length(c - v[1])); distance = std::min(distance, geom::length(c - v[2])); - on_screen_unit = width() / distance / std::tan(camera.fov_x / 2.f); + on_screen_unit = state().size[0] / distance / std::tan(camera.fov_x / 2.f); } assert(on_screen_unit > 0.f); @@ -983,11 +986,14 @@ void srtm_app::present() // simple_renderer.push(gfx::simple_renderer::render_state{&selected_mesh, gfx::white.as_color_rgba()}); // simple_renderer.render(gfx::simple_renderer::render_options{camera_transform}); + int const width = state().size[0]; + int const height = state().size[1]; + info.push_back(util::to_string("Tiles: ", rendered_tiles)); { float s = 10.f; - painter.line({width() / 2.f - s, height() / 2.f}, {width() / 2.f + s, height() / 2.f}, 3.f, gfx::cyan, false); - painter.line({width() / 2.f, height() / 2.f - s}, {width() / 2.f, height() / 2.f + s}, 3.f, gfx::cyan, false); + painter.line({width / 2.f - s, height / 2.f}, {width / 2.f + s, height / 2.f}, 3.f, gfx::cyan, false); + painter.line({width / 2.f, height / 2.f - s}, {width / 2.f, height / 2.f + s}, 3.f, gfx::cyan, false); } info.push_back(util::to_string("Nodes: ", nodes.node_count())); info.push_back(util::to_string("Tasks: ", nodes.loader_queue_size())); @@ -1020,10 +1026,15 @@ void srtm_app::present() gl::Enable(gl::BLEND); gl::BlendFunc(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA); gl::Disable(gl::DEPTH_TEST); - painter.render(geom::window_camera{width(), height()}.transform()); + painter.render(geom::window_camera{width, height}.transform()); } -int main() +namespace psemek::app { - return app::main(); + + std::unique_ptr make_application_factory() + { + return default_application_factory({.name = "SRTM example", .multisampling = 4}); + } + } diff --git a/examples/tree.cpp b/examples/tree.cpp deleted file mode 100644 index 89f7e46d..00000000 --- a/examples/tree.cpp +++ /dev/null @@ -1,532 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -using namespace psemek; - -// All spacial parameters in meters -// All angles in degrees -struct tree_species_description -{ - struct curve_description - { - geom::interval base_radius; - geom::interval segment_length; - std::function(float)> segment_count; - geom::interval lean_angle; - geom::interval rotation_angle; - float up_tendency; - - float splitting_probability; - geom::interval splitting_angle; - - geom::interval initial_branching_distance; - geom::interval branching_distance; - geom::interval branching_children_count; - geom::interval branching_rotation; - geom::interval branching_angle; - geom::interval branching_size_multiplier; - }; - - curve_description trunk; - curve_description branch; -}; - -auto fir_species() -{ - tree_species_description desc; - - desc.trunk.base_radius = {0.1f, 0.15f}; - desc.trunk.segment_length = {0.5f, 0.8f}; - desc.trunk.segment_count = [](float){ - return geom::interval{7, 10}; - }; - desc.trunk.lean_angle = {-5.f, 5.f}; - desc.trunk.rotation_angle = {-5.f, 5.f}; - desc.trunk.up_tendency = 1.f; - desc.trunk.initial_branching_distance = {0.5f, 0.5f}; - desc.trunk.branching_distance = {0.4f, 0.5f}; - desc.trunk.branching_children_count = {4, 4}; - desc.trunk.branching_rotation = {30, 60.f}; - desc.trunk.branching_angle = {55.f, 65.f}; - desc.trunk.branching_size_multiplier = {1.f, 1.f}; - desc.trunk.splitting_probability = 0.f; - desc.branch.base_radius = {0.01f, 0.05f}; - desc.branch.segment_length = {0.05f, 0.20f}; - desc.branch.segment_count = [](float h){ - int min = std::max(1, 18 * (1.f - h)); - int max = std::min(20, 20 * (1.f - h)); - - if (min > max) - std::swap(min, max); - - return geom::interval{min, max}; - }; - desc.branch.lean_angle = {-5.f, 5.f}; - desc.branch.rotation_angle = {-1.f, 1.f}; - desc.branch.up_tendency = 0.0f; - desc.branch.initial_branching_distance = {0.4f, 0.4f}; - desc.branch.branching_distance = {0.1f, 0.2f}; - desc.branch.branching_children_count = {1, 1}; - desc.branch.branching_rotation = {175.f, 185.f}; - desc.branch.branching_angle = {45.f, 45.f}; - desc.branch.branching_size_multiplier = {0.8f, 0.8f}; - desc.branch.splitting_probability = 0.f; - - return desc; -} - -auto oak_species() -{ - tree_species_description desc; - - desc.trunk.base_radius = {0.2f, 0.3f}; - desc.trunk.segment_length = {0.5f, 0.8f}; - desc.trunk.segment_count = [](float){ - return geom::interval{4, 7}; - }; - desc.trunk.lean_angle = {-15.f, 15.f}; - desc.trunk.rotation_angle = {-180.f, 180.f}; - desc.trunk.up_tendency = 0.f; - desc.trunk.initial_branching_distance = {20.f, 30.f}; - desc.trunk.branching_distance = {0.1f, 0.15f}; - desc.trunk.branching_children_count = {1, 2}; - desc.trunk.branching_rotation = {60, 180.f}; - desc.trunk.branching_angle = {30.f, 75.f}; - desc.trunk.branching_size_multiplier = {1.f, 1.f}; - desc.trunk.splitting_probability = 0.1f; - desc.trunk.splitting_angle = {15.f, 60.f}; - - desc.branch.base_radius = {0.04f, 0.05f}; - desc.branch.segment_length = {0.1f, 0.15f}; - desc.branch.segment_count = [](float h){ - h = std::max(h / 4.f, 0.f); -// h = std::min(h, 1.f); - -// h = 2.f * h - 1.f; - -// float t = std::sqrt(1.f - h * h); - - return geom::interval{6, 10}; - }; - desc.branch.lean_angle = {-15.f, 15.f}; - desc.branch.rotation_angle = {-30.f, 30.f}; - desc.branch.up_tendency = 0.f; - desc.branch.initial_branching_distance = {0.4f, 0.5f}; - desc.branch.branching_distance = {0.4f, 0.5f}; - desc.branch.branching_children_count = {1, 1}; - desc.branch.branching_rotation = {175.f, 185.f}; - desc.branch.branching_angle = {30.f, 60.f}; - desc.branch.branching_size_multiplier = {0.8f, 0.8f}; - desc.branch.splitting_probability = 0.1f; - desc.branch.splitting_angle = {15.f, 60.f}; - - return desc; -} - -struct tree_description -{ - struct node - { - geom::point position; - float radius; - }; - - struct branch - { - std::size_t parent; - std::vector nodes; - }; - - std::vector branches; -}; - -template -tree_description generate(tree_species_description const & species, RNG && rng) -{ - // warm up! - for (int i = 0; i < 16; ++i) - rng(); - - tree_description result; - - struct frame - { - geom::point pos; - geom::vector z; - geom::vector x; - }; - - auto generate_curve = util::recursive([&](auto & self, frame f, tree_species_description::curve_description const & curve_desc, tree_species_description::curve_description const & children_desc, - int level, float min_radius, int max_segments, float h, float branching_distance, float branching_rotation) -> void - { - std::size_t id = result.branches.size(); - result.branches.emplace_back(); - - float base_radius = random::uniform_distribution{curve_desc.base_radius}(rng); - base_radius = std::min(base_radius, min_radius); - - int segment_count = random::uniform_distribution{curve_desc.segment_count(h)}(rng); - segment_count = std::min(max_segments, segment_count); - - float expected_height = segment_count * curve_desc.segment_length.center(); - - result.branches[id].parent = 0; - result.branches[id].nodes.push_back({f.pos, base_radius}); - - for (int i = 0; i < segment_count; ++i) - { - float t = (i + 1.f) / segment_count; - - float length = random::uniform_real_distribution{curve_desc.segment_length}(rng); - - float lean = geom::rad(random::uniform_real_distribution{curve_desc.lean_angle}(rng)); - - f.z = geom::axis_rotation{f.x, lean}(f.z); - - if (curve_desc.up_tendency > 0.f) - f.z = geom::slerp(f.z, geom::vector{0.f, 0.f, 1.f}, t * curve_desc.up_tendency); - else if (curve_desc.up_tendency < 0.f) - f.z = geom::slerp(f.z, geom::vector{0.f, 0.f, -1.f}, - t * curve_desc.up_tendency); - - float rotation = geom::rad(random::uniform_real_distribution{curve_desc.rotation_angle}(rng)); - - f.x = geom::axis_rotation{f.z, rotation}(f.x); - - auto new_pos = f.pos + f.z * length; - - float new_radius = (1.f - t) * base_radius; - - if (level < 3) - { - float available_branching_length = length; - while (branching_distance < available_branching_length) - { - available_branching_length -= branching_distance; - - int children_count = random::uniform_distribution{curve_desc.branching_children_count}(rng); - - for (int c = 0; c < children_count; ++c) - { - float angle = random::uniform_distribution{curve_desc.branching_angle}(rng); - - float rotation = 0; - if (children_count > 1) - rotation = c * 2.f * geom::pi / children_count; - - float branch_t = 1.f - available_branching_length / length; - - geom::vector y{0.f, 0.f, 1.f}; - - frame child_f; - child_f.pos = geom::lerp(f.pos, new_pos, branch_t); - child_f.z = geom::slerp(f.z, f.x, angle / 90.f); - child_f.z = geom::axis_rotation{f.z, branching_rotation + rotation}(child_f.z); - child_f.x = geom::normalized(geom::cross(y, child_f.z)); - - float multiplier = random::uniform_distribution{curve_desc.branching_size_multiplier}(rng); - - float branching_distance = random::uniform_distribution{children_desc.initial_branching_distance}(rng); - - self(child_f, children_desc, children_desc, level + 1, - geom::lerp(result.branches[id].nodes.back().radius, new_radius, branch_t) * multiplier, - (level == 0) ? 1024 : (segment_count - i) * multiplier, - (level == 0) ? child_f.pos[2] / expected_height : h, - branching_distance, 0.f); - } - - branching_rotation += geom::rad(random::uniform_distribution{curve_desc.branching_rotation}(rng)); - branching_distance = random::uniform_distribution{curve_desc.branching_distance}(rng); - } - - branching_distance -= available_branching_length; - } - - f.pos = new_pos; - - result.branches[id].nodes.push_back({f.pos, new_radius}); - - if (i + 1 < segment_count && random::uniform_distribution{}(rng) < curve_desc.splitting_probability) - { - frame f1 = f; - frame f2 = f; - - float a1 = geom::rad(random::uniform_distribution{curve_desc.splitting_angle}(rng)); - float a2 = geom::rad(random::uniform_distribution{curve_desc.splitting_angle}(rng)); - - f1.z = geom::axis_rotation{f.x, a1}(f1.z); - f2.z = geom::axis_rotation{f.x, -a2}(f2.z); - - float h = f.pos[2] / expected_height; - - self(f1, curve_desc, children_desc, level, new_radius, segment_count - i - 1, h, branching_distance, branching_rotation); - self(f2, curve_desc, children_desc, level, new_radius, segment_count - i - 1, h, branching_distance, branching_rotation); - - break; - } - } - }); - - frame starting_frame; - starting_frame.pos = {0.f, 0.f, 0.f}; - starting_frame.z = {0.f, 0.f, 1.f}; - starting_frame.x = {1.f, 0.f, 0.f}; - - float initial_orientation = random::uniform_distribution{0.f, 2.f * geom::pi}(rng); - starting_frame.x = geom::axis_rotation{starting_frame.z, initial_orientation}(starting_frame.x); - - generate_curve(starting_frame, species.trunk, species.branch, 0, std::numeric_limits::infinity(), 1024, 0.f, - random::uniform_distribution{species.trunk.initial_branching_distance}(rng), 0.f); - - return result; -} - -static char const vertex_source[] = -R"(#version 330 - -uniform mat4 u_transform; - -layout (location = 0) in vec4 in_position; -layout (location = 1) in vec4 in_color; - -out vec4 color; -out vec3 pos; - -void main() -{ - gl_Position = u_transform * in_position; - color = in_color; - pos = in_position.xyz; -} -)"; - -static char const fragment_source[] = -R"(#version 330 - -in vec4 color; -in vec3 pos; - -out vec4 out_color; - -void main() -{ - vec3 n = normalize(cross(dFdx(pos), dFdy(pos))); - - vec3 light = normalize(vec3(1.0, 1.0, 1.0)); - - float l = 0.5 + 0.5 * dot(n, light); - - out_color = vec4(l * color.rgb, 1.0); -} -)"; - -struct tree_app - : app::app -{ - tree_species_description species = oak_species(); - - tree_description tree_desc; - - gfx::mesh tree_mesh; - - gfx::program tree_program{vertex_source, fragment_source}; - - geom::spherical_camera camera; - - std::uint64_t seed = 0; - - tree_app() - : app("Tree") - { - tree_desc = generate(species, random::generator{seed, 0ull}); - update_mesh(); - - setup_camera(); - } - - void update_mesh(); - void setup_camera(); - - void on_mouse_move(int x, int y, int dx, int dy) override; - - void on_mouse_wheel(int delta) override; - - void on_resize(int width, int height) override; - - void on_key_down(SDL_Keycode key) override; - - void present() override; -}; - -struct vertex -{ - geom::point position; - gfx::color_rgba color; -}; - -void tree_app::update_mesh() -{ - tree_mesh.setup, gfx::normalized>(); - - std::vector vertices; - std::vector indices; - - { - int const basement_size = 5; - float basement_offset = 0.05f; - gfx::color_rgba basement_color { 127, 127, 127, 255 }; - - for (int x = -basement_size; x < basement_size; ++x) - { - for (int y = -basement_size; y < basement_size; ++y) - { - auto base = vertices.size(); - - vertices.push_back({{x + basement_offset, y + basement_offset, 0.f}, basement_color}); - vertices.push_back({{x + 1 - basement_offset, y + basement_offset, 0.f}, basement_color}); - vertices.push_back({{x + basement_offset, y + 1 - basement_offset, 0.f}, basement_color}); - vertices.push_back({{x + 1 - basement_offset, y + 1 - basement_offset, 0.f}, basement_color}); - - indices.push_back(base + 0); - indices.push_back(base + 1); - indices.push_back(base + 2); - indices.push_back(base + 2); - indices.push_back(base + 1); - indices.push_back(base + 3); - } - } - } - - gfx::color_rgba trunk_color { 127, 63, 0, 255 }; - - auto build_branch = [&](std::vector const & nodes) - { - int const N = 6; - - auto const base = vertices.size(); - - auto z = geom::vector{0.f, 0.f, 1.f}; - auto x = geom::ort(z); - - for (std::size_t i = 0; i < nodes.size(); ++i) - { - if (i + 1 < nodes.size()) - z = geom::normalized(nodes[i + 1].position - nodes[i].position); - else if (nodes.size() >= 2) - z = geom::normalized(nodes[i].position - nodes[i - 1].position); - - auto y = geom::cross(z, x); - - geom::gram_schmidt(z, x, y); - - for (int k = 0; k < N; ++k) - { - float a = (k * geom::pi * 2.f) / N; - - vertices.push_back({nodes[i].position + (x * std::cos(a) + y * std::sin(a)) * nodes[i].radius, trunk_color}); - } - } - - for (std::size_t i = 0; i + 1 < nodes.size(); ++i) - { - for (int k = 0; k < N; ++k) - { - int kk = (k + 1) % N; - - indices.push_back(base + i * N + k); - indices.push_back(base + i * N + kk); - indices.push_back(base + i * N + k + N); - - indices.push_back(base + i * N + k + N); - indices.push_back(base + i * N + kk); - indices.push_back(base + i * N + kk + N); - } - } - }; - - for (auto const & b : tree_desc.branches) - build_branch(b.nodes); - - tree_mesh.load(vertices, indices, gl::TRIANGLES, gl::STATIC_DRAW); -} - -void tree_app::setup_camera() -{ - camera.fov_y = geom::rad(45.f); - camera.near_clip = 0.1f; - camera.far_clip = 100.f; - camera.target = {0.f, 0.f, 0.f}; - camera.elevation_angle = geom::rad(45.f); - camera.azimuthal_angle = 0.f; - camera.distance = 10.f; -} - -void tree_app::on_mouse_move(int x, int y, int dx, int dy) -{ - app::on_mouse_move(x, y, dx, dy); - - if (is_middle_button_down()) - { - camera.azimuthal_angle -= dx * 0.01f; - camera.elevation_angle += dy * 0.01f; - } - - if (is_right_button_down()) - { - camera.target[2] += dy * 0.001f * camera.distance; - } -} - -void tree_app::on_mouse_wheel(int delta) -{ - camera.distance *= std::pow(0.8f, delta); -} - -void tree_app::on_resize(int width, int height) -{ - app::on_resize(width, height); - camera.set_fov(camera.fov_y, (1.f * width) / height); -} - -void tree_app::on_key_down(SDL_Keycode key) -{ - if (key == SDLK_SPACE) - { - ++seed; - - tree_desc = generate(species, random::generator{std::uint64_t{seed}, 0ull}); - update_mesh(); - } -} - -void tree_app::present() -{ - gl::ClearColor(0.7f, 0.7f, 1.f, 1.f); - gl::Clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT); - - gl::Enable(gl::DEPTH_TEST); - gl::DepthFunc(gl::LEQUAL); - - gl::Enable(gl::CULL_FACE); - - tree_program.bind(); - tree_program["u_transform"] = camera.transform(); - tree_mesh.draw(); -} - -int main() -{ - return app::main(); -} diff --git a/examples/triangulation.cpp b/examples/triangulation.cpp index 1b26eb1b..ad775b0c 100644 --- a/examples/triangulation.cpp +++ b/examples/triangulation.cpp @@ -1,5 +1,5 @@ -#include -#include +#include +#include #include #include #include @@ -23,10 +23,9 @@ using namespace psemek; struct triangulation_app - : app::app + : app::application_base { - triangulation_app() - : app("Triangulation example", 16) + triangulation_app(options const &, context const &) { std::ifstream in(PSEMEK_EXAMPLES_DIR "/turkey"); @@ -80,36 +79,32 @@ struct triangulation_app std::iota(closest_points_.begin(), closest_points_.end(), std::uint16_t{0}); } - void on_left_button_down() override + void on_event(app::mouse_button_event const & event) override { - app::on_left_button_down(); + app::application_base::on_event(event); - if (mouse()) - drag_start_ = mouse(); + if (event.down && event.button == app::mouse_button::left) + drag_start_ = state().mouse; + + if (!event.down && event.button == app::mouse_button::left) + drag_start_ = std::nullopt; } - void on_left_button_up() override + void on_event(app::mouse_wheel_event const & event) override { - app::on_left_button_up(); - - drag_start_ = std::nullopt; - } - - void on_mouse_wheel(int delta) override - { - camera_size_tgt_ *= std::pow(0.8f, delta); + camera_size_tgt_ *= std::pow(0.8f, event.delta); } void update() override { float const dt = clock_.restart().count(); - if (drag_start_ && mouse()) + if (drag_start_) { - auto delta = *mouse() - *drag_start_; + auto delta = state().mouse - *drag_start_; delta[1] *= -1; - camera_center_ -= geom::cast(delta) * camera_size_ / (1.f * height()); - drag_start_ = mouse(); + camera_center_ -= geom::cast(delta) * camera_size_ / (1.f * state().size[1]); + drag_start_ = state().mouse; } camera_size_ += (camera_size_tgt_ - camera_size_) * (1.f - std::exp(- 20.f * dt)); @@ -120,10 +115,10 @@ struct triangulation_app gl::ClearColor(1.f, 1.f, 1.f, 1.f); gl::Clear(gl::COLOR_BUFFER_BIT); - float aspect_ratio = (width() * 1.f) / height(); + float aspect_ratio = (state().size[0] * 1.f) / state().size[1]; geom::box view_bbox = geom::expand(geom::box::singleton(camera_center_), geom::vector{camera_size_ * 0.5f * aspect_ratio, camera_size_ * 0.5f}); - float line_width = 4.f * camera_size_ / height(); + float line_width = 4.f * camera_size_ / state().size[1]; auto edge = [this, line_width](auto i0, auto i1, gfx::color_rgba const & color) { @@ -153,11 +148,10 @@ struct triangulation_app painter_.render(camera_transform); - if (auto m = mouse(); m) { geom::point m_world; - m_world[0] = geom::lerp(view_bbox[0], (*m)[0] * 1.f / width()); - m_world[1] = geom::lerp(view_bbox[1], 1.f - (*m)[1] * 1.f / height()); + m_world[0] = geom::lerp(view_bbox[0], state().mouse[0] * 1.f / state().size[0]); + m_world[1] = geom::lerp(view_bbox[1], 1.f - state().mouse[1] * 1.f / state().size[1]); auto compare = [&](auto i, auto j){ return geom::distance(points_[i], m_world) < geom::distance(points_[j], m_world); @@ -182,13 +176,13 @@ struct triangulation_app opts.y = gfx::painter::y_align::bottom; opts.scale = 2.f; auto p = geom::swizzle<0, 1>(geom::as_point(camera_transform * geom::homogeneous(geom::swizzle<0, 1, -1>(points_[i])))); - p[0] = std::round((p[0] * 0.5f + 0.5f) * width()); - p[1] = std::round((0.5f - p[1] * 0.5f) * height()); + p[0] = std::round((p[0] * 0.5f + 0.5f) * state().size[0]); + p[1] = std::round((0.5f - p[1] * 0.5f) * state().size[1]); painter_.text(p, util::to_string(i), opts); } } - painter_.render(geom::window_camera{width(), height()}.transform()); + painter_.render(geom::window_camera{state().size[0], state().size[1]}.transform()); } private: @@ -208,7 +202,25 @@ private: util::clock, std::chrono::high_resolution_clock> clock_; }; -int main() +namespace psemek::app { - return app::main(); + + std::unique_ptr make_application_factory() + { + return default_application_factory({.name = "Triangulation example" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + ""}); + } + } diff --git a/examples/water_1d.cpp b/examples/water_1d.cpp index 86cc6179..7aec8a2e 100644 --- a/examples/water_1d.cpp +++ b/examples/water_1d.cpp @@ -1,24 +1,25 @@ -#include -#include +#include +#include #include #include +#include using namespace psemek; struct water_1d_app - : app::app + : app::application_base { - water_1d_app(); + water_1d_app(options const &, context const &); void update() override; void present() override; - void on_resize(int width, int height) override; + void on_event(app::resize_event const & event) override; geom::box simulation_area; float aspect_ratio; - std::size_t const N = 50; + std::size_t const N = 100; std::vector bed; std::vector height; std::vector discharge; @@ -28,8 +29,7 @@ struct water_1d_app gfx::painter painter; }; -water_1d_app::water_1d_app() - : app("Water 1D simulation", 4) +water_1d_app::water_1d_app(options const &, context const &) { simulation_area[0] = {-1.f, 1.f}; simulation_area[1] = {0.f, 1.f}; @@ -42,21 +42,25 @@ water_1d_app::water_1d_app() { bed[i] += (0.5f * (N - i)) / N; bed[i] += (100.f / (N - i)) / N; + bed[i] += 0.125f * std::exp(- 1000.f * geom::sqr(i * 1.f / N - 0.5f)); + // bed[i] += (0.25f * i) / N; // height[i] += (0.3f * i) / N; // height[i] = 0.3f - bed[i]; // height[i] = 0.25f; } -// bed[N * 3 / 4] = 0.2f; +// bed[N * 3 / 4] += 0.2f; discharge[0] = 0.04f; discharge[N] = 0.f; + +// height[0] = 20.f; } void water_1d_app::update() { - float const dt = 0.01f; + float const dt = 0.005f; float const dx = simulation_area[0].length() / N; float const g = 10.f; float const max_speed = 1.f; @@ -152,12 +156,33 @@ void water_1d_app::present() painter.rect(simulation_area, {255, 255, 255, 255}); - for (std::size_t i = 0; i < N; ++i) + gfx::color_rgba const earth_color{127, 63, 0, 255}; + gfx::color_rgba const water_color{0, 0, 255, 255}; + + for (std::size_t i = 0; i + 1 < N; ++i) { - painter.rect({{{x(i), x(i + 1)}, {simulation_area[1].min, simulation_area[1].min + bed[i]}}}, gfx::dark(gfx::yellow).as_color_rgba()); - painter.rect({{{x(i), x(i + 1)}, {simulation_area[1].min + bed[i], simulation_area[1].min + bed[i] + height[i]}}}, gfx::blue); + geom::point p0{x(i + 0) * 0.5f + x(i + 1) * 0.5f, simulation_area[1].min}; + geom::point p1{x(i + 1) * 0.5f + x(i + 2) * 0.5f, simulation_area[1].min}; + + geom::point q0{p0[0], simulation_area[1].min + bed[i]}; + geom::point q1{p1[0], simulation_area[1].min + bed[i + 1]}; + + geom::point w0{p0[0], simulation_area[1].min + bed[i] + height[i]}; + geom::point w1{p1[0], simulation_area[1].min + bed[i + 1] + height[i + 1]}; + + painter.triangle(p0, p1, q0, earth_color); + painter.triangle(q0, p1, q1, earth_color); + + painter.triangle(q0, q1, w0, water_color); + painter.triangle(w0, q1, w1, water_color); } +// for (std::size_t i = 0; i < N; ++i) +// { +// painter.rect({{{x(i), x(i + 1)}, {simulation_area[1].min, simulation_area[1].min + bed[i]}}}, gfx::dark(gfx::yellow).as_color_rgba()); +// painter.rect({{{x(i), x(i + 1)}, {simulation_area[1].min + bed[i], simulation_area[1].min + bed[i] + height[i]}}}, gfx::blue); +// } + for (std::size_t i = 1; i < N; ++i) { painter.line({x(i), 0.f}, {x(i), discharge[i]}, 0.005f, gfx::red); @@ -166,11 +191,11 @@ void water_1d_app::present() painter.render(transform); } -void water_1d_app::on_resize(int width, int height) +void water_1d_app::on_event(app::resize_event const & event) { - app::on_resize(width, height); + app::application_base::on_event(event); - aspect_ratio = (width * 1.f) / height; + aspect_ratio = (event.size[0] * 1.f) / event.size[1]; } float water_1d_app::x(std::size_t i) const @@ -178,7 +203,12 @@ float water_1d_app::x(std::size_t i) const return geom::lerp(simulation_area[0], (1.f * i) / N); } -int main() +namespace psemek::app { - return app::main(); + + std::unique_ptr make_application_factory() + { + return default_application_factory({.name = "Water example", .multisampling = 4}); + } + } diff --git a/examples/water_2d.cpp b/examples/water_2d.cpp deleted file mode 100644 index 3f996648..00000000 --- a/examples/water_2d.cpp +++ /dev/null @@ -1,326 +0,0 @@ -#include -#include -#include -#include -#include - -using namespace psemek; - -static std::pair unpack_triangular_id(int id) -{ - int i = int(std::floor(0.5f * (sqrt(1.f + 8.f * id) - 1.f))); - int j = id - (i * (i + 1)) / 2; - return {i, j}; -} - -static int pack_triangular_id(std::pair const & p) -{ - return (p.first * (p.first + 1)) / 2 + p.second; -} - -struct water_2d_app - : app::app -{ - water_2d_app(); - - void update() override; - void present() override; - - void on_resize(int width, int height) override; - - geom::box simulation_area; - float aspect_ratio; - - int const N = 25; - int const first_type_count = (N * (N + 1)) / 2; - - std::vector water_height; - std::vector> flux[3]; - - geom::point p[3]; - - gfx::painter painter; -}; - -water_2d_app::water_2d_app() - : app("Water 2D simulation", 4) -{ - p[0] = {- N / 2.f, 0.f}; - p[1] = { N / 2.f, 0.f}; - p[2] = {0.f, N * std::sqrt(0.75f)}; - - simulation_area |= p[0]; - simulation_area |= p[1]; - simulation_area |= p[2]; - - water_height.assign(N * N, 0.5f); - flux[0].assign(first_type_count, geom::vector{0.f, -5.f}); - flux[1].assign(first_type_count, geom::vector{0.f, -5.f}); - flux[2].assign(first_type_count, geom::vector{0.f, -5.f}); - - for (int i = 0; i < N; ++i) - { - flux[0][first_type_count - i - 1] = geom::vector::zero(); - flux[1][first_type_count - i - 1] = geom::vector::zero(); - flux[2][first_type_count - i - 1] = geom::vector::zero(); - } -} - -void water_2d_app::update() -{ - float const dt = 0.01f; - float const max_speed = 10.f; - - geom::vector const flux_normal[3] = - { - geom::vector{ std::sqrt(3.f) / 2.f, 1.f / 2.f }, - geom::vector{ - std::sqrt(3.f) / 2.f, 1.f / 2.f }, - geom::vector{ 0.f, -1.f }, - }; - - auto const rot1 = [this](std::pair const & p) - { - return std::pair{N - p.second, p.first - p.second}; - }; - - auto const rot2 = [rot1](std::pair const & p) - { - return rot1(rot1(p)); - }; - - std::vector dh(water_height.size(), 0.f); - std::vector> du[3]; - for (int s = 0; s < 3; ++s) - du[s].assign(first_type_count, geom::vector{0.f, 0.f}); - - for (int id = 0; id < N * N; ++id) - { - bool const first_type = (id < first_type_count); - auto const [i, j] = first_type ? unpack_triangular_id(id) : unpack_triangular_id(id - first_type_count); - - int e[3]; - - if (first_type) - { - e[0] = pack_triangular_id({i, j}); - e[1] = pack_triangular_id(rot1({i + 1, j + 1})); - e[2] = pack_triangular_id(rot2({i + 1, j})); - } - else - { - e[0] = pack_triangular_id({i, j}); - e[1] = pack_triangular_id(rot1({i + 2, j + 2})); - e[2] = pack_triangular_id(rot2({i + 2, j})); - } - - float const flux_sign = first_type ? -1.f : 1.f; - - for (int s = 0; s < 3; ++s) - dh[id] += flux_sign * geom::dot(flux_normal[s], flux[s][e[s]]) * dt; - } - - for (int id = 0; id < N * N; ++id) - { - water_height[id] += dh[id]; - water_height[id] = std::max(0.f, water_height[id]); - } - - for (int s = 0; s < 3; ++s) - { - for (int id = 0; id < first_type_count - N; ++id) - { - auto const [i, j] = unpack_triangular_id(id); - - std::pair tin, tout; - - if (s == 0) - { - tout = {i, j}; - tin = {i, j}; - } - else if (s == 1) - { - tout = rot2({i + 1, j}); - tin = rot2({i + 2, j}); - } - else // if (s == 2) - { - tout = rot1({i + 1, j + 1}); - tin = rot1({i + 2, j + 2}); - } - - - } - } - - for (int s = 0; s < 3; ++s) - { - for (int id = 0; id < first_type_count - N; ++id) - { - flux[s][id] += du[s][id]; - - auto const [i, j] = unpack_triangular_id(id); - - std::pair tid; - bool first_type; - - float const nflux = geom::dot(flux[s][id], flux_normal[s]); - bool const forward = nflux > 0.f; - - if (forward) - { - first_type = true; - if (s == 0) - tid = {i, j}; - else if (s == 1) - tid = rot2({i + 1, j}); - else // if (s == 2) - tid = rot1({i + 1, j + 1}); - } - else - { - first_type = false; - if (s == 0) - tid = {i, j}; - else if (s == 1) - tid = rot2({i + 2, j}); - else // if (s == 2) - tid = rot1({i + 2, j + 2}); - } - - auto const h = water_height[pack_triangular_id(tid) + (first_type ? 0 : first_type_count)]; - float const max_flux = h * max_speed; - - if (forward && nflux > max_flux) - { - flux[s][id] -= flux_normal[s] * (nflux - max_flux); - } - - if (!forward && (-nflux > max_flux)) - { - flux[s][id] += flux_normal[s] * ((-nflux) - max_flux); - } - -// if (forward) -// flux[s][id] = std::min(flux[s][id], h * max_speed); -// else -// flux[s][id] = std::max(flux[s][id], - h * max_speed); - } - } -} - -void water_2d_app::present() -{ - gl::ClearColor(0.8f, 0.8f, 0.8f, 0.8f); - gl::Clear(gl::COLOR_BUFFER_BIT); - - gl::Enable(gl::BLEND); - gl::BlendFunc(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA); - - geom::box view_area; - { - view_area[0] = simulation_area[0]; - view_area[1] = simulation_area[1]; - view_area[2] = {-1.f, 1.f}; - - float extra_y = view_area[1].length() * 0.1f; - view_area[1].min -= extra_y; - view_area[1].max += extra_y; - float extra_x = view_area[1].length() * aspect_ratio - view_area[0].length(); - view_area[0].min -= extra_x / 2.f; - view_area[0].max += extra_x / 2.f; - } - - geom::matrix const transform = geom::orthographic{view_area}.homogeneous_matrix(); - - float const pixel = 2.f / std::sqrt(width() * height() * geom::det(transform)); - - // Background - painter.triangle(p[0], p[1], p[2], gfx::white); - - // Water tiles - for (int id = 0; id < N * N; ++id) - { - bool const first_type = (id < first_type_count); - auto const [i, j] = first_type ? unpack_triangular_id(id) : unpack_triangular_id(id - first_type_count); - (void)pack_triangular_id; - - auto at = [this](int i, int j) - { - float const u = (i * 1.f) / N; - float const v = (j * 1.f) / N; - return geom::lerpn(p[0], 1.f - u, p[1], v, p[2], u - v); - }; - - auto f = [](float h) - { - float const C = 1.f; - float const e = std::exp(- C * h); - return (1.f - e) / (1.f + e); - }; - - auto color = gfx::to_coloru8(gfx::color_4f{0.f, 0.f, 1.f, f(water_height[id])}); - - if (water_height[id] < 0.f) - color = gfx::magenta; - - if (first_type) - { - painter.triangle(at(i, j), at(i + 1, j + 1), at(i + 1, j), color); - } - else - { - painter.triangle(at(i + 1, j), at(i + 1, j + 1), at(i + 2, j + 1), color); - } - } - - // Edges -// if(false) - for (int i = 1; i <= N; ++i) - { - float t = (i * 1.f) / N; - painter.line(geom::lerp(p[0], p[1], t), geom::lerp(p[0], p[2], t), 2.f * pixel, gfx::black, false); - painter.line(geom::lerp(p[1], p[0], t), geom::lerp(p[1], p[2], t), 2.f * pixel, gfx::black, false); - painter.line(geom::lerp(p[2], p[0], t), geom::lerp(p[2], p[1], t), 2.f * pixel, gfx::black, false); - } - - // Flux -// if(false) - for (int side = 0; side < 3; ++side) - { - auto at = [this, side](int i, int j) - { - float const u = (i * 1.f) / N; - float const v = (j * 1.f) / N; - return geom::lerpn(p[side], 1.f - u, p[(side + 1) % 3], v, p[(side + 2) % 3], u - v); - }; - - for (int id = 0; id < first_type_count; ++id) - { - float const scale = 0.1f; - - auto const [i, j] = unpack_triangular_id(id); - - auto const p0 = at(i + 1, j); - auto const p1 = at(i + 1, j + 1); - - auto const p = geom::lerp(p0, p1, 0.5f); - - painter.line(p, p + flux[side][id] * scale, pixel * 2.f, gfx::red, false); - } - } - - painter.render(transform); -} - -void water_2d_app::on_resize(int width, int height) -{ - app::on_resize(width, height); - - aspect_ratio = (width * 1.f) / height; -} - -int main() -{ - return app::main(); -}