#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace psemek; struct physics_2d_app : app::app { phys2d::engine physics; geom::box view_box; geom::box simulation_box; gfx::painter painter; util::clock> frame_clock; float physics_lag = 0.f; phys2d::engine::material_handle material; phys2d::engine::shape_handle ball_shape, box_shape; phys2d::engine::group_handle ball_group, box_group; float const world_size = 5.f; float const line_width = 0.05f; float const ball_radius = 0.25f; float const box_width = 2.f * ball_radius; float const box_height = 2.f * ball_radius; std::vector radiuses; std::optional> mouse; std::optional selected_ball; async::event_loop loop; pcg::generator gen; physics_2d_app() : app("Physics 2D example", 4) { simulation_box = {{{-world_size, world_size}, {-world_size, world_size}}}; view_box = expand(simulation_box, 1.f); physics.set_gravity({0.f, -9.8f}); float const inf = std::numeric_limits::infinity(); material = physics.add_material({1.f, 0.5f, 0.5f}); ball_shape = physics.add_shape(phys2d::ball{ball_radius}); ball_group = physics.create_group(); box_shape = physics.add_shape(phys2d::box{box_width, box_height}); box_group = physics.create_group(); int nx = std::ceil(simulation_box[0].length() / box_width); int ny = std::ceil(simulation_box[1].length() / box_height); for (int x = 0; x < nx; ++x) { for (int y = 0; y < ny / 3; ++y) { geom::point pos{simulation_box.corner((x + 0.5f) / nx, (y + 0.5f) / ny)}; physics.add_object(box_group, box_shape, material, {pos, 0.f}, {}); } } auto wall_group = physics.create_group(); auto wall_material = physics.add_material({inf, 0.5f, 0.5f}); auto wall_0_shape = physics.add_shape(phys2d::half_space{{1.f, 0.f}, simulation_box[0].min}); physics.add_object(wall_group, wall_0_shape, wall_material, {}, {}); auto wall_1_shape = physics.add_shape(phys2d::half_space{{-1.f, 0.f}, -simulation_box[0].max}); physics.add_object(wall_group, wall_1_shape, wall_material, {}, {}); auto wall_2_shape = physics.add_shape(phys2d::half_space{{0.f, 1.f}, simulation_box[1].min}); physics.add_object(wall_group, wall_2_shape, wall_material, {}, {}); auto wall_3_shape = physics.add_shape(phys2d::half_space{{0.f, -1.f}, -simulation_box[1].max}); physics.add_object(wall_group, wall_3_shape, wall_material, {}, {}); auto task = util::recursive([this, dx = box_width * 0.4f](auto && self) mutable -> void { physics.add_object(box_group, box_shape, material, {simulation_box.corner(0.5f, 0.9f) + geom::vector{dx, 0.f}, 0.f}, {}); // physics.add_object(ball_group, ball_shape, material, {simulation_box.corner(0.5f, 0.9f) + geom::vector{dx, 0.f}, 0.f}, {}); // float r = pcg::uniform_distribution{0.05f, 0.5f}(gen); // auto new_shape = physics.add_shape(phys2d::ball{r}); // physics.add_object(ball_group, new_shape, material, {simulation_box.corner(0.5f, 0.9f) + geom::vector{dx, 0.f}, 0.f}, {}); // radiuses.push_back(r); dx *= -1.f; if (physics.group_size(box_group) < 100) loop.dispatch_at(std::chrono::system_clock::now() + std::chrono::milliseconds{100}, self); }); (void)task; // task(); } void on_resize(int width, int height) override { app::on_resize(width, height); float const ratio = static_cast(width) / height; float const c = view_box[0].center(); float const l = view_box[1].length() * ratio; view_box[0] = {c - l / 2.f, c + l / 2.f}; } void on_left_button_down() override { app::on_left_button_down(); if (mouse) { physics.add_object(ball_group, ball_shape, material, {*mouse, 0.f}, {{0.f, 0.f}, 25.f}); } } void on_left_button_up() override { app::on_left_button_up(); } void on_right_button_down() override { app::on_right_button_down(); if (mouse) { physics.explode(*mouse, 10.f, 1.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(); mouse = view_box.corner(mx, my); } void update() override { util::profiler prof("update"); loop.pump(); float const frame_dt = frame_clock.restart().count(); physics_lag += frame_dt; float const physics_dt = 0.001f; while (physics_lag > physics_dt) // if (physics_lag > physics_dt) { physics_lag -= physics_dt; physics.update(physics_dt); } selected_ball = std::nullopt; if (mouse) { float closest = std::numeric_limits::infinity(); for (std::size_t i = 0; i < physics.group_size(ball_group); ++i) { auto p = physics.group_static_state(ball_group)[i].position; float d = geom::distance(p, *mouse); if (d <= std::min(closest, ball_radius)) { closest = d; selected_ball = i; } } } } void present() override { util::profiler prof("present"); gl::ClearColor(0.8f, 0.8f, 0.8f, 0.f); gl::Clear(gl::COLOR_BUFFER_BIT); for (std::size_t i = 0; i < physics.group_size(ball_group); ++i) { bool selected = selected_ball ? (*selected_ball == i) : false; auto p = physics.group_static_state(ball_group)[i].position; auto a = physics.group_static_state(ball_group)[i].rotation; painter.circle(p, ball_radius, gfx::black); painter.circle(p, ball_radius - line_width, gfx::light(gfx::blue, selected ? 0.8f : 1.f).as_color_rgba()); geom::vector const n{std::cos(a), std::sin(a)}; geom::vector const m = geom::ort(n); painter.line(p - n * ball_radius / 2.f, p + n * ball_radius / 2.f, line_width, gfx::black, false); painter.line(p - m * ball_radius / 2.f, p + m * ball_radius / 2.f, line_width, gfx::black, false); } for (std::size_t i = 0; i < physics.group_size(box_group); ++i) { // bool selected = false; auto p = physics.group_static_state(box_group)[i].position; auto a = physics.group_static_state(box_group)[i].rotation; geom::vector q[4]; q[0] = {-box_width / 2.f, -box_height / 2.f}; q[1] = { box_width / 2.f, -box_height / 2.f}; q[2] = { box_width / 2.f, box_height / 2.f}; q[3] = {-box_width / 2.f, box_height / 2.f}; auto m = geom::plane_rotation(0, 1, a).linear_matrix(); for (std::size_t j = 0; j < 4; ++j) q[j] = m * q[j]; painter.triangle(p + q[0], p + q[1], p + q[3], gfx::white); painter.triangle(p + q[3], p + q[1], p + q[2], gfx::white); painter.line(p + q[0], p + q[1], line_width, gfx::black, false); painter.line(p + q[1], p + q[2], line_width, gfx::black, false); painter.line(p + q[2], p + q[3], line_width, gfx::black, false); painter.line(p + q[3], p + q[0], line_width, gfx::black, false); } painter.line(simulation_box.corner(0, 0), simulation_box.corner(1, 0), line_width, gfx::black, false); painter.line(simulation_box.corner(1, 0), simulation_box.corner(1, 1), line_width, gfx::black, false); painter.line(simulation_box.corner(1, 1), simulation_box.corner(0, 1), line_width, gfx::black, false); painter.line(simulation_box.corner(0, 1), simulation_box.corner(0, 0), line_width, gfx::black, false); painter.render(geom::orthographic_camera{view_box}.transform()); } }; int main() { return app::main(); }