270 lines
7.6 KiB
C++
270 lines
7.6 KiB
C++
#include <psemek/app/app.hpp>
|
|
#include <psemek/app/main.hpp>
|
|
|
|
#include <psemek/phys/engine_2d.hpp>
|
|
|
|
#include <psemek/gfx/painter.hpp>
|
|
|
|
#include <psemek/geom/box.hpp>
|
|
#include <psemek/geom/camera.hpp>
|
|
#include <psemek/geom/rotation.hpp>
|
|
|
|
#include <psemek/util/clock.hpp>
|
|
#include <psemek/util/recursive.hpp>
|
|
#include <psemek/util/profiler.hpp>
|
|
|
|
#include <psemek/async/event_loop.hpp>
|
|
|
|
#include <psemek/pcg/random/generator.hpp>
|
|
#include <psemek/pcg/random/uniform.hpp>
|
|
|
|
#include <cstdint>
|
|
#include <vector>
|
|
|
|
using namespace psemek;
|
|
|
|
struct physics_2d_app
|
|
: app::app
|
|
{
|
|
phys2d::engine physics;
|
|
|
|
geom::box<float, 2> view_box;
|
|
geom::box<float, 2> simulation_box;
|
|
|
|
gfx::painter painter;
|
|
|
|
util::clock<std::chrono::duration<float>> 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<float> radiuses;
|
|
|
|
std::optional<geom::point<float, 2>> mouse;
|
|
|
|
std::optional<std::size_t> 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<float>::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<float, 2> 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<float>{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<float>(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<float>::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<float, 2> 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<float, 2>(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<physics_2d_app>();
|
|
}
|