diff --git a/examples/physics_2d.cpp b/examples/physics_2d.cpp new file mode 100644 index 00000000..ab6c19cf --- /dev/null +++ b/examples/physics_2d.cpp @@ -0,0 +1,270 @@ +#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.9f}); + + 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(); + + if(false) + for (int x = -4; x <= 4; ++x) + { + for (int y = 0; y < 3; ++y) + { + geom::point pos{simulation_box[0].center() + x * box_width, simulation_box[1].min + (y + 0.5f) * box_height}; + 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 = 0.1f](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) < 200) + loop.dispatch_at(std::chrono::system_clock::now() + std::chrono::milliseconds{250}, 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}, {}); + } + } + + 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.add_object(box_group, box_shape, material, {*mouse, 0.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(); +}