Delete obsolete examples & fix the rest to incorporate the new application API
This commit is contained in:
parent
7f5d50787d
commit
d473ef3ea4
19 changed files with 671 additions and 2050 deletions
|
|
@ -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}")
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
#include <psemek/app/app.hpp>
|
||||
#include <psemek/app/main.hpp>
|
||||
#include <psemek/app/application_base.hpp>
|
||||
#include <psemek/app/default_application_factory.hpp>
|
||||
|
||||
#include <psemek/gfx/painter.hpp>
|
||||
|
||||
|
|
@ -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<controller> 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<float>(width()) / height();
|
||||
float ratio = static_cast<float>(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<float>::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<geom::point<float, 2>> m;
|
||||
if (mouse())
|
||||
{
|
||||
m = view_bbox.corner((*mouse())[0] * 1.f / width(), 1.f - (*mouse())[1] * 1.f / height());
|
||||
}
|
||||
geom::point<float, 2> 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<float>::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<system::selection> 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<int>(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<animation_2d_app>();
|
||||
|
||||
std::unique_ptr<application::factory> make_application_factory()
|
||||
{
|
||||
return default_application_factory<animation_2d_app>({.name = "Animation 2D example"});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,8 +17,8 @@
|
|||
#include <psemek/audio/combine/stereo.hpp>
|
||||
#include <psemek/audio/combine/mixer.hpp>
|
||||
#include <psemek/audio/midi.hpp>
|
||||
#include <psemek/app/app.hpp>
|
||||
#include <psemek/app/main.hpp>
|
||||
#include <psemek/app/application_base.hpp>
|
||||
#include <psemek/app/default_application_factory.hpp>
|
||||
#include <psemek/gfx/painter.hpp>
|
||||
#include <psemek/util/clock.hpp>
|
||||
#include <psemek/util/to_string.hpp>
|
||||
|
|
@ -31,41 +31,41 @@
|
|||
|
||||
using namespace psemek;
|
||||
|
||||
static std::map<SDL_Keycode, int> const key_to_midi
|
||||
static std::map<app::keycode, int> 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<int> 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<audio::engine> engine_;
|
||||
audio::mixer_ptr mixer_;
|
||||
std::shared_ptr<audio::volume_control_stereo> volume_control_;
|
||||
std::shared_ptr<audio::pitch_control> pitch_control_;
|
||||
|
|
@ -235,7 +228,12 @@ private:
|
|||
gfx::painter painter_;
|
||||
};
|
||||
|
||||
int main()
|
||||
namespace psemek::app
|
||||
{
|
||||
return app::main<audio_app>();
|
||||
|
||||
std::unique_ptr<application::factory> make_application_factory()
|
||||
{
|
||||
return default_application_factory<audio_app>({.name = "Audio example"});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
#include <psemek/app/app.hpp>
|
||||
#include <psemek/app/main.hpp>
|
||||
#include <psemek/app/application_base.hpp>
|
||||
#include <psemek/app/default_application_factory.hpp>
|
||||
#include <psemek/geom/box.hpp>
|
||||
#include <psemek/geom/mesh.hpp>
|
||||
#include <psemek/geom/intersection.hpp>
|
||||
|
|
@ -23,6 +23,7 @@
|
|||
#include <psemek/util/clock.hpp>
|
||||
#include <psemek/util/to_string.hpp>
|
||||
#include <psemek/async/threadpool.hpp>
|
||||
#include <psemek/log/log.hpp>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
|
|
@ -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<int, 3> 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<application::factory> 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<application> {
|
||||
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 (argc == 2)
|
||||
{
|
||||
size = util::from_string<std::size_t>(argv[1]);
|
||||
if (context.args.size() == 2)
|
||||
size = util::from_string<std::size_t>(context.args[1]);
|
||||
return std::make_unique<cloud_app>(size);
|
||||
});
|
||||
}
|
||||
return app::main<cloud_app>(size);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
#include <psemek/app/app.hpp>
|
||||
#include <psemek/app/main.hpp>
|
||||
#include <psemek/app/application_base.hpp>
|
||||
#include <psemek/app/default_application_factory.hpp>
|
||||
|
||||
#include <psemek/gfx/renderer/deferred.hpp>
|
||||
#include <psemek/gfx/effect/gamma.hpp>
|
||||
|
|
@ -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<float>(width) / height);
|
||||
app::application_base::on_event(event);
|
||||
camera.set_fov(camera.fov_y, static_cast<float>(event.size[0]) / event.size[1]);
|
||||
|
||||
pre_gamma_texture.load<geom::vector<std::uint16_t, 3>>({width, height});
|
||||
pre_gamma_texture.load<geom::vector<std::uint16_t, 3>>(geom::cast<std::size_t>(event.size));
|
||||
pre_gamma_framebuffer.color(pre_gamma_texture);
|
||||
pre_gamma_framebuffer.assert_complete();
|
||||
|
||||
pre_fxaa_texture.load<gfx::color_rgb>({width, height});
|
||||
pre_fxaa_texture.load<gfx::color_rgb>(geom::cast<std::size_t>(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<deferred_app>();
|
||||
|
||||
std::unique_ptr<application::factory> make_application_factory()
|
||||
{
|
||||
return default_application_factory<deferred_app>({.name = "Deferred shading example", .multisampling = 0});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
#include <psemek/app/app.hpp>
|
||||
#include <psemek/app/main.hpp>
|
||||
#include <psemek/app/application_base.hpp>
|
||||
#include <psemek/app/default_application_factory.hpp>
|
||||
#include <psemek/gfx/mesh.hpp>
|
||||
#include <psemek/gfx/program.hpp>
|
||||
#include <psemek/gfx/texture.hpp>
|
||||
|
|
@ -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<std::chrono::duration<float>> 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<fire_app>();
|
||||
|
||||
std::unique_ptr<application::factory> make_application_factory()
|
||||
{
|
||||
return default_application_factory<fire_app>({.name = "Fire example", .multisampling = 4});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,636 +0,0 @@
|
|||
#include <psemek/app/app.hpp>
|
||||
#include <psemek/app/main.hpp>
|
||||
#include <psemek/gfx/mesh.hpp>
|
||||
#include <psemek/gfx/program.hpp>
|
||||
#include <psemek/gfx/texture.hpp>
|
||||
#include <psemek/gfx/painter.hpp>
|
||||
#include <psemek/gfx/framebuffer.hpp>
|
||||
#include <psemek/gfx/renderbuffer.hpp>
|
||||
#include <psemek/geom/camera.hpp>
|
||||
#include <psemek/geom/math.hpp>
|
||||
#include <psemek/geom/rotation.hpp>
|
||||
#include <psemek/geom/scale.hpp>
|
||||
#include <psemek/geom/translation.hpp>
|
||||
#include <psemek/geom/easing.hpp>
|
||||
#include <psemek/geom/gradient.hpp>
|
||||
#include <psemek/geom/permutation.hpp>
|
||||
#include <psemek/random/device.hpp>
|
||||
#include <psemek/random/generator.hpp>
|
||||
#include <psemek/random/uniform_sphere.hpp>
|
||||
#include <psemek/random/uniform_ball.hpp>
|
||||
#include <psemek/random/uniform_box.hpp>
|
||||
#include <psemek/pcg/perlin.hpp>
|
||||
#include <psemek/pcg/sample.hpp>
|
||||
#include <psemek/util/clock.hpp>
|
||||
#include <psemek/util/assert.hpp>
|
||||
#include <psemek/util/moving_average.hpp>
|
||||
#include <psemek/util/to_string.hpp>
|
||||
#include <psemek/util/pretty_print.hpp>
|
||||
|
||||
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<float, 2> 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<float, 4, 4> random_transform[8];
|
||||
util::array<int, 2> random_transform_index;
|
||||
|
||||
util::clock<> frame_clock;
|
||||
util::moving_average<double> frame_time{64};
|
||||
|
||||
util::clock<std::chrono::duration<float>> 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<float, 2> d;
|
||||
|
||||
util::array<geom::vector<float, 2>, 2> grad({size / 8 + 1, size / 8 + 1});
|
||||
for (auto & v : grad) v = d(rng);
|
||||
density = pcg::perlin<float, 2>(std::move(grad));
|
||||
|
||||
for (int i = 0; i < 8; ++i)
|
||||
{
|
||||
if (i < 4)
|
||||
random_transform[i] = geom::matrix<float, 4, 4>::identity();
|
||||
else
|
||||
random_transform[i] = geom::swap<float, 3>(0, 1).homogeneous_matrix();
|
||||
|
||||
random_transform[i] = random_transform[i]
|
||||
* geom::translation<float, 3>(geom::vector{0.5f, 0.5f, 0.f}).homogeneous_matrix()
|
||||
* geom::plane_rotation<float, 3>(0, 1, i * geom::pi / 2.f).homogeneous_matrix()
|
||||
* geom::translation<float, 3>(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<int>{0, 7}(rng);
|
||||
}
|
||||
|
||||
void init_ground()
|
||||
{
|
||||
ground_mesh.setup<geom::point<float, 3>, gfx::normalized<gfx::color_rgba>>();
|
||||
|
||||
struct vertex
|
||||
{
|
||||
geom::point<float, 3> pos;
|
||||
gfx::color_rgba color;
|
||||
};
|
||||
|
||||
std::vector<vertex> vertices;
|
||||
std::vector<geom::triangle<std::uint32_t>> 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<std::uint16_t, 3> pos;
|
||||
std::uint8_t t;
|
||||
std::uint8_t density;
|
||||
|
||||
vertex(geom::point<float, 3> 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<float, gfx::color_4f> 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<gfx::color_rgba, 1> 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<geom::point<std::uint16_t, 3>>, gfx::normalized<std::uint8_t>, gfx::normalized<std::uint8_t>>();
|
||||
|
||||
std::vector<vertex> vertices;
|
||||
std::vector<geom::triangle<std::uint32_t>> triangles;
|
||||
random::uniform_box_point_distribution<float, 2> d_origin({{{0.f, 1.f}, {0.f, 1.f}}});
|
||||
random::uniform_sphere_vector_distribution<float, 2> d_orientation;
|
||||
random::uniform_real_distribution<float> d_width{1.f / 64.f, 1.f / 128.f};
|
||||
random::uniform_real_distribution<float> d_height{0.25f, 1.f};
|
||||
random::uniform_real_distribution<float> 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<float>{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<float, 3> position;
|
||||
geom::vector<float, 2> texcoord;
|
||||
};
|
||||
|
||||
int const slice_count = 4;
|
||||
float slice_width = 1.f / slice_count;
|
||||
|
||||
std::vector<vertex> 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::point<float, 3>, geom::vector<float, 2>>();
|
||||
grass_slice_z_mesh.load(vertices, gl::TRIANGLES, gl::STATIC_DRAW);
|
||||
|
||||
std::size_t slice_resolution = 256;
|
||||
|
||||
grass_slice_z_texture.load<gfx::color_rgba>({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<float, 4, 4>::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<float, 3>({-1.f + 2.f * x, -1.f + 2.f * y, 0.f}).homogeneous_matrix() * geom::scale<float, 3>({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<float, 3>(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<float, 3>(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<grass_app>();
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
#include <psemek/app/app.hpp>
|
||||
#include <psemek/app/main.hpp>
|
||||
#include <psemek/app/application_base.hpp>
|
||||
#include <psemek/app/default_application_factory.hpp>
|
||||
#include <psemek/gfx/painter.hpp>
|
||||
#include <psemek/gfx/gl.hpp>
|
||||
#include <psemek/geom/scale.hpp>
|
||||
|
|
@ -7,14 +7,12 @@
|
|||
#include <psemek/geom/constants.hpp>
|
||||
#include <psemek/util/clock.hpp>
|
||||
#include <psemek/util/to_string.hpp>
|
||||
#include <psemek/audio/engine.hpp>
|
||||
#include <psemek/audio/constants.hpp>
|
||||
#include <psemek/audio/detail/oscillator.hpp>
|
||||
#include <psemek/audio/effect/compressor.hpp>
|
||||
#include <psemek/prof/profiler.hpp>
|
||||
#include <psemek/util/moving_average.hpp>
|
||||
#include <psemek/log/log.hpp>
|
||||
|
||||
#include <random>
|
||||
#include <set>
|
||||
|
||||
/*
|
||||
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<float> d{-50.f, 50.f};
|
||||
std::uniform_real_distribution<float> 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<float>(width) / height;
|
||||
camera_ratio_ = static_cast<float>(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<float, 2> 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<int, 2> const screen_center { window_size_[0] / 2, window_size_[1] / 2 };
|
||||
auto target = camera_center_ + flip_y(geom::cast<float>(*mouse_ - screen_center) * scale);
|
||||
geom::point<int, 2> const screen_center { state().size[0] / 2,state().size[1] / 2 };
|
||||
auto target = camera_center_ + flip_y(geom::cast<float>(state().mouse - screen_center) * scale);
|
||||
force_target_ = target;
|
||||
|
||||
// geom::point<float, 2> pos{100.f, 0.f};
|
||||
|
|
@ -221,57 +219,57 @@ struct myapp : app::app
|
|||
// p.vel += 4000.f * r / geom::length_sqr(r) / p.mass;
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
void on_left_button_up() override
|
||||
if (event.button == app::mouse_button::left && !event.down)
|
||||
{
|
||||
force_target_ = std::nullopt;
|
||||
}
|
||||
|
||||
void on_right_button_down() override
|
||||
if (event.button == app::mouse_button::right && event.down)
|
||||
{
|
||||
camera_drag_ = mouse_;
|
||||
camera_drag_ = state().mouse;
|
||||
}
|
||||
|
||||
void on_right_button_up() override
|
||||
if (event.button == app::mouse_button::right && !event.down)
|
||||
{
|
||||
camera_drag_ = std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
void on_mouse_move(int x, int y, int, int) override
|
||||
void on_event(app::mouse_move_event const & event) override
|
||||
{
|
||||
mouse_ = {x, y};
|
||||
app::application_base::on_event(event);
|
||||
|
||||
if (camera_drag_)
|
||||
{
|
||||
geom::scale<float, 2> 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<float>(*camera_drag_ - *mouse_) * scale);
|
||||
camera_drag_ = mouse_;
|
||||
camera_center_ += flip_y(geom::cast<float>(*camera_drag_ - event.position) * scale);
|
||||
camera_drag_ = event.position;
|
||||
}
|
||||
|
||||
if (force_target_)
|
||||
{
|
||||
geom::scale<float, 2> const flip_y({1.f, -1.f});
|
||||
float const scale = camera_size_ / window_size_[1];
|
||||
geom::point<int, 2> const screen_center { window_size_[0] / 2, window_size_[1] / 2 };
|
||||
auto target = camera_center_ + flip_y(geom::cast<float>(*mouse_ - screen_center) * scale);
|
||||
float const scale = camera_size_ / state().size[1];
|
||||
geom::point<int, 2> const screen_center { state().size[0] / 2, state().size[1] / 2 };
|
||||
auto target = camera_center_ + flip_y(geom::cast<float>(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<std::uint8_t>(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<int, 2> window_size_;
|
||||
|
||||
std::optional<geom::point<float, 2>> force_target_;
|
||||
|
||||
geom::point<float, 2> camera_center_ { 0.f, 0.f };
|
||||
float camera_size_ = 150.f;
|
||||
float camera_ratio_ = 1.f;
|
||||
|
||||
std::optional<geom::point<int, 2>> mouse_;
|
||||
std::optional<geom::point<int, 2>> 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<float> 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<myapp>();
|
||||
|
||||
std::unique_ptr<application::factory> make_application_factory()
|
||||
{
|
||||
return default_application_factory<myapp>({.name = "Gravity example"});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
#include <psemek/parser/primitives.hpp>
|
||||
|
||||
#include <psemek/app/default_application_factory.hpp>
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <iterator>
|
||||
|
|
@ -74,8 +76,12 @@ Stream & operator << (Stream & s, std::variant<T, Ts...> const & v)
|
|||
return s;
|
||||
}
|
||||
|
||||
int main()
|
||||
namespace psemek::app
|
||||
{
|
||||
|
||||
std::unique_ptr<application::factory> make_application_factory()
|
||||
{
|
||||
return default_application_factory({.name = "Parser example"}, [](auto const & ...){
|
||||
using namespace psemek::parser;
|
||||
|
||||
auto const p = map(concat(integer<int>, ws, one_of(ch('+'), ch('-')), ws, integer<int>), [](auto const & t){
|
||||
|
|
@ -84,4 +90,9 @@ int main()
|
|||
});
|
||||
|
||||
std::cout << p.parse("45 + 67") << std::endl;
|
||||
|
||||
return nullptr;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
#include <psemek/app/app.hpp>
|
||||
#include <psemek/app/main.hpp>
|
||||
#include <psemek/app/application_base.hpp>
|
||||
#include <psemek/app/default_application_factory.hpp>
|
||||
|
||||
#include <psemek/gfx/gl.hpp>
|
||||
#include <psemek/gfx/painter.hpp>
|
||||
|
|
@ -17,6 +17,8 @@
|
|||
#include <psemek/util/to_string.hpp>
|
||||
#include <psemek/util/statistics.hpp>
|
||||
|
||||
#include <psemek/log/log.hpp>
|
||||
|
||||
#include <vector>
|
||||
|
||||
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<float> 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<float>(width) / height;
|
||||
float aspect_ratio = static_cast<float>(event.size[0]) / event.size[1];
|
||||
|
||||
float xc = view_region[0].center();
|
||||
float yl = view_region[1].length();
|
||||
|
|
@ -217,25 +215,22 @@ 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<float>(*mouse());
|
||||
drag_delta = world_to_screen(model.points[*closest_point]) - geom::cast<float>(state().mouse);
|
||||
model.vels[*closest_point] = geom::vector<float, 2>::zero();
|
||||
}
|
||||
}
|
||||
|
||||
void physics_demo_app::on_left_button_up()
|
||||
if (event.button == app::mouse_button::left && !event.down)
|
||||
{
|
||||
app::on_left_button_up();
|
||||
|
||||
drag_delta = std::nullopt;
|
||||
}
|
||||
|
||||
void physics_demo_app::on_right_button_down()
|
||||
if (event.button == app::mouse_button::right && event.down)
|
||||
{
|
||||
if (new_spring_start)
|
||||
{
|
||||
|
|
@ -247,19 +242,19 @@ void physics_demo_app::on_right_button_down()
|
|||
model.vels[*closest_point] = geom::vector<float, 2>::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<float>(*mouse());
|
||||
auto const m = geom::cast<float>(state().mouse);
|
||||
|
||||
std::size_t closest = 0;
|
||||
float distance = std::numeric_limits<float>::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<float>(*mouse())));
|
||||
model.points.push_back(screen_to_world(geom::cast<float>(state().mouse)));
|
||||
model.vels.push_back(geom::vector<float, 2>::zero());
|
||||
model.movable.push_back(false);
|
||||
model.add_stick(*new_spring_start, model.points.size() - 1);
|
||||
|
|
@ -373,38 +370,33 @@ 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<float>(*mouse())));
|
||||
model.points.push_back(screen_to_world(geom::cast<float>(state().mouse)));
|
||||
model.vels.push_back(geom::vector<float, 2>::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<float>(*mouse())));
|
||||
model.points.push_back(screen_to_world(geom::cast<float>(state().mouse)));
|
||||
model.vels.push_back(geom::vector<float, 2>::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())
|
||||
else if (event.down && event.key == app::keycode::W)
|
||||
{
|
||||
std::uint32_t const base = model.points.size();
|
||||
|
||||
int N = 12;
|
||||
|
||||
auto o = screen_to_world(geom::cast<float>(*mouse()));
|
||||
auto o = screen_to_world(geom::cast<float>(state().mouse));
|
||||
|
||||
model.points.push_back(o);
|
||||
model.vels.push_back(geom::vector<float, 2>::zero());
|
||||
|
|
@ -426,12 +418,11 @@ void physics_demo_app::on_key_down(SDL_Keycode key)
|
|||
model.add_stick(base + i + 1, base + 1 + ((i + 1) % N));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (key == SDLK_b)
|
||||
else if (event.down && event.key == app::keycode::B)
|
||||
{
|
||||
std::uint32_t const base = model.points.size();
|
||||
|
||||
auto o = screen_to_world(geom::cast<float>(*mouse()));
|
||||
auto o = screen_to_world(geom::cast<float>(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<std::size_t> fixed;
|
||||
|
||||
if (closest_point && drag_delta && mouse())
|
||||
if (closest_point && drag_delta)
|
||||
{
|
||||
auto target = screen_to_world(geom::cast<float>(*mouse()) + *drag_delta);
|
||||
auto target = screen_to_world(geom::cast<float>(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<float>(*mouse())), ball_radius / 2.f, gfx::red);
|
||||
painter.line(model.points[*new_spring_start], screen_to_world(geom::cast<float>(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<float, 2> physics_demo_app::screen_to_world(geom::point<float, 2> const & p) const
|
||||
{
|
||||
return geom::orthographic<float, 2>(view_region).inverse()(geom::point{2.f * p[0] / width() - 1.f, 1.f - 2.f * p[1] / height()});
|
||||
return geom::orthographic<float, 2>(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<float, 2> physics_demo_app::world_to_screen(geom::point<float, 2> const & p) const
|
||||
{
|
||||
auto q = geom::orthographic<float, 2>(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<physics_demo_app>();
|
||||
|
||||
std::unique_ptr<application::factory> make_application_factory()
|
||||
{
|
||||
return default_application_factory<physics_demo_app>({.name = "Physics example"});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
#include <psemek/app/app.hpp>
|
||||
#include <psemek/app/main.hpp>
|
||||
#include <psemek/app/application_base.hpp>
|
||||
#include <psemek/app/default_application_factory.hpp>
|
||||
|
||||
#include <psemek/phys/engine_2d.hpp>
|
||||
|
||||
|
|
@ -18,13 +18,15 @@
|
|||
#include <psemek/random/generator.hpp>
|
||||
#include <psemek/random/uniform.hpp>
|
||||
|
||||
#include <psemek/log/log.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
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<float>(width) / height;
|
||||
float const ratio = static_cast<float>(event.size[0]) / event.size[1];
|
||||
|
||||
float const c = view_box[0].center();
|
||||
float const l = view_box[1].length() * ratio;
|
||||
|
|
@ -213,10 +214,12 @@ 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 (event.button == app::mouse_button::left && event.down)
|
||||
{
|
||||
if (selected_ball && mouse)
|
||||
{
|
||||
auto const & s = physics.group_static_state(ball_group)[*selected_ball];
|
||||
|
|
@ -233,17 +236,13 @@ struct physics_2d_app
|
|||
}
|
||||
}
|
||||
|
||||
void on_left_button_up() override
|
||||
if (event.button == app::mouse_button::left && !event.down)
|
||||
{
|
||||
app::on_left_button_up();
|
||||
|
||||
drag_delta = std::nullopt;
|
||||
}
|
||||
|
||||
void on_right_button_down() override
|
||||
if (event.button == app::mouse_button::right && event.down)
|
||||
{
|
||||
app::on_right_button_down();
|
||||
|
||||
if (mouse)
|
||||
{
|
||||
physics.add_object(ball_group, ball_shape, material, {*mouse, 0.f}, {{0.f, 0.f}, 0.f});
|
||||
|
|
@ -252,13 +251,14 @@ struct physics_2d_app
|
|||
// physics.explode(*mouse, 10.f, 100.f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
app::application_base::on_event(event);
|
||||
|
||||
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<physics_2d_app>();
|
||||
|
||||
std::unique_ptr<application::factory> make_application_factory()
|
||||
{
|
||||
return default_application_factory<physics_2d_app>({.name = "Physics example", .multisampling = 4});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
#include <psemek/app/app.hpp>
|
||||
#include <psemek/app/main.hpp>
|
||||
#include <psemek/app/application_base.hpp>
|
||||
#include <psemek/app/default_application_factory.hpp>
|
||||
|
||||
#include <psemek/phys/engine_3d.hpp>
|
||||
|
||||
|
|
@ -23,6 +23,8 @@
|
|||
|
||||
#include <psemek/util/clock.hpp>
|
||||
|
||||
#include <psemek/log/log.hpp>
|
||||
|
||||
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<physics_3d_app>();
|
||||
|
||||
std::unique_ptr<application::factory> make_application_factory()
|
||||
{
|
||||
return default_application_factory<physics_3d_app>({.name = "Physics 3D example"});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
#include <psemek/app/app.hpp>
|
||||
#include <psemek/app/main.hpp>
|
||||
#include <psemek/app/application_base.hpp>
|
||||
#include <psemek/app/default_application_factory.hpp>
|
||||
#include <psemek/gfx/painter.hpp>
|
||||
#include <psemek/gfx/gl.hpp>
|
||||
#include <psemek/geom/scale.hpp>
|
||||
|
|
@ -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<float, 2> 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<platformer_app>();
|
||||
|
||||
std::unique_ptr<application::factory> make_application_factory()
|
||||
{
|
||||
return default_application_factory<platformer_app>({.name = "Platformer example"});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
#include <psemek/app/app.hpp>
|
||||
#include <psemek/app/main.hpp>
|
||||
#include <psemek/app/application_base.hpp>
|
||||
#include <psemek/app/default_application_factory.hpp>
|
||||
|
||||
#include <psemek/gfx/program.hpp>
|
||||
#include <psemek/gfx/mesh.hpp>
|
||||
|
|
@ -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<shadow_app>();
|
||||
|
||||
std::unique_ptr<application::factory> make_application_factory()
|
||||
{
|
||||
return default_application_factory<shadow_app>({.name = "Shadow example"});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
#include <psemek/app/app.hpp>
|
||||
#include <psemek/app/main.hpp>
|
||||
#include <psemek/app/application_base.hpp>
|
||||
#include <psemek/app/default_application_factory.hpp>
|
||||
|
||||
#include <psemek/gfx/gl.hpp>
|
||||
#include <psemek/gfx/mesh.hpp>
|
||||
|
|
@ -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<srtm_app>();
|
||||
|
||||
std::unique_ptr<application::factory> make_application_factory()
|
||||
{
|
||||
return default_application_factory<srtm_app>({.name = "SRTM example", .multisampling = 4});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,532 +0,0 @@
|
|||
#include <psemek/app/app.hpp>
|
||||
#include <psemek/app/main.hpp>
|
||||
#include <psemek/gfx/mesh.hpp>
|
||||
#include <psemek/gfx/program.hpp>
|
||||
#include <psemek/geom/camera.hpp>
|
||||
#include <psemek/geom/rotation.hpp>
|
||||
#include <psemek/geom/gram_schmidt.hpp>
|
||||
#include <psemek/gfx/color.hpp>
|
||||
#include <psemek/geom/math.hpp>
|
||||
#include <psemek/geom/interval.hpp>
|
||||
#include <psemek/random/uniform.hpp>
|
||||
#include <psemek/random/generator.hpp>
|
||||
#include <psemek/util/recursive.hpp>
|
||||
|
||||
#include <vector>
|
||||
|
||||
using namespace psemek;
|
||||
|
||||
// All spacial parameters in meters
|
||||
// All angles in degrees
|
||||
struct tree_species_description
|
||||
{
|
||||
struct curve_description
|
||||
{
|
||||
geom::interval<float> base_radius;
|
||||
geom::interval<float> segment_length;
|
||||
std::function<geom::interval<int>(float)> segment_count;
|
||||
geom::interval<float> lean_angle;
|
||||
geom::interval<float> rotation_angle;
|
||||
float up_tendency;
|
||||
|
||||
float splitting_probability;
|
||||
geom::interval<float> splitting_angle;
|
||||
|
||||
geom::interval<float> initial_branching_distance;
|
||||
geom::interval<float> branching_distance;
|
||||
geom::interval<int> branching_children_count;
|
||||
geom::interval<float> branching_rotation;
|
||||
geom::interval<float> branching_angle;
|
||||
geom::interval<float> 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<int>(1, 18 * (1.f - h));
|
||||
int max = std::min<int>(20, 20 * (1.f - h));
|
||||
|
||||
if (min > max)
|
||||
std::swap(min, max);
|
||||
|
||||
return geom::interval<int>{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<int>{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<float, 3> position;
|
||||
float radius;
|
||||
};
|
||||
|
||||
struct branch
|
||||
{
|
||||
std::size_t parent;
|
||||
std::vector<node> nodes;
|
||||
};
|
||||
|
||||
std::vector<branch> branches;
|
||||
};
|
||||
|
||||
template <typename RNG>
|
||||
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<float, 3> pos;
|
||||
geom::vector<float, 3> z;
|
||||
geom::vector<float, 3> 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<float>{curve_desc.base_radius}(rng);
|
||||
base_radius = std::min(base_radius, min_radius);
|
||||
|
||||
int segment_count = random::uniform_distribution<int>{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<float>{curve_desc.segment_length}(rng);
|
||||
|
||||
float lean = geom::rad(random::uniform_real_distribution<float>{curve_desc.lean_angle}(rng));
|
||||
|
||||
f.z = geom::axis_rotation<float>{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<float>{curve_desc.rotation_angle}(rng));
|
||||
|
||||
f.x = geom::axis_rotation<float>{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<int>{curve_desc.branching_children_count}(rng);
|
||||
|
||||
for (int c = 0; c < children_count; ++c)
|
||||
{
|
||||
float angle = random::uniform_distribution<float>{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<float>{f.z, branching_rotation + rotation}(child_f.z);
|
||||
child_f.x = geom::normalized(geom::cross(y, child_f.z));
|
||||
|
||||
float multiplier = random::uniform_distribution<float>{curve_desc.branching_size_multiplier}(rng);
|
||||
|
||||
float branching_distance = random::uniform_distribution<float>{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<float>{curve_desc.branching_rotation}(rng));
|
||||
branching_distance = random::uniform_distribution<float>{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<float>{}(rng) < curve_desc.splitting_probability)
|
||||
{
|
||||
frame f1 = f;
|
||||
frame f2 = f;
|
||||
|
||||
float a1 = geom::rad(random::uniform_distribution<float>{curve_desc.splitting_angle}(rng));
|
||||
float a2 = geom::rad(random::uniform_distribution<float>{curve_desc.splitting_angle}(rng));
|
||||
|
||||
f1.z = geom::axis_rotation<float>{f.x, a1}(f1.z);
|
||||
f2.z = geom::axis_rotation<float>{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<float>{0.f, 2.f * geom::pi}(rng);
|
||||
starting_frame.x = geom::axis_rotation<float>{starting_frame.z, initial_orientation}(starting_frame.x);
|
||||
|
||||
generate_curve(starting_frame, species.trunk, species.branch, 0, std::numeric_limits<float>::infinity(), 1024, 0.f,
|
||||
random::uniform_distribution<float>{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<float, 3> position;
|
||||
gfx::color_rgba color;
|
||||
};
|
||||
|
||||
void tree_app::update_mesh()
|
||||
{
|
||||
tree_mesh.setup<geom::point<float, 3>, gfx::normalized<gfx::color_rgba>>();
|
||||
|
||||
std::vector<vertex> vertices;
|
||||
std::vector<std::uint32_t> 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<tree_description::node> 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<tree_app>();
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
#include <psemek/app/app.hpp>
|
||||
#include <psemek/app/main.hpp>
|
||||
#include <psemek/app/application_base.hpp>
|
||||
#include <psemek/app/default_application_factory.hpp>
|
||||
#include <psemek/gfx/painter.hpp>
|
||||
#include <psemek/gfx/gl.hpp>
|
||||
#include <psemek/geom/scale.hpp>
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
||||
void on_left_button_up() override
|
||||
{
|
||||
app::on_left_button_up();
|
||||
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_mouse_wheel(int delta) override
|
||||
void on_event(app::mouse_wheel_event const & event) 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<float>(delta) * camera_size_ / (1.f * height());
|
||||
drag_start_ = mouse();
|
||||
camera_center_ -= geom::cast<float>(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<float, 2> view_bbox = geom::expand(geom::box<float, 2>::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<float, 2> 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::duration<float>, std::chrono::high_resolution_clock> clock_;
|
||||
};
|
||||
|
||||
int main()
|
||||
namespace psemek::app
|
||||
{
|
||||
return app::main<triangulation_app>();
|
||||
|
||||
std::unique_ptr<application::factory> make_application_factory()
|
||||
{
|
||||
return default_application_factory<triangulation_app>({.name = "Triangulation example"
|
||||
""
|
||||
""
|
||||
""
|
||||
""
|
||||
""
|
||||
""
|
||||
""
|
||||
""
|
||||
""
|
||||
""
|
||||
""
|
||||
""
|
||||
""});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,24 +1,25 @@
|
|||
#include <psemek/app/app.hpp>
|
||||
#include <psemek/app/main.hpp>
|
||||
#include <psemek/app/application_base.hpp>
|
||||
#include <psemek/app/default_application_factory.hpp>
|
||||
#include <psemek/gfx/painter.hpp>
|
||||
#include <psemek/geom/orthographic.hpp>
|
||||
#include <psemek/log/log.hpp>
|
||||
|
||||
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<float, 2> simulation_area;
|
||||
float aspect_ratio;
|
||||
|
||||
std::size_t const N = 50;
|
||||
std::size_t const N = 100;
|
||||
std::vector<float> bed;
|
||||
std::vector<float> height;
|
||||
std::vector<float> 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<water_1d_app>();
|
||||
|
||||
std::unique_ptr<application::factory> make_application_factory()
|
||||
{
|
||||
return default_application_factory<water_1d_app>({.name = "Water example", .multisampling = 4});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,326 +0,0 @@
|
|||
#include <psemek/app/app.hpp>
|
||||
#include <psemek/app/main.hpp>
|
||||
#include <psemek/gfx/painter.hpp>
|
||||
#include <psemek/geom/orthographic.hpp>
|
||||
#include <psemek/geom/gauss.hpp>
|
||||
|
||||
using namespace psemek;
|
||||
|
||||
static std::pair<int, int> 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<int, int> 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<float, 2> simulation_area;
|
||||
float aspect_ratio;
|
||||
|
||||
int const N = 25;
|
||||
int const first_type_count = (N * (N + 1)) / 2;
|
||||
|
||||
std::vector<float> water_height;
|
||||
std::vector<geom::vector<float, 2>> flux[3];
|
||||
|
||||
geom::point<float, 2> 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<float, 2>::zero();
|
||||
flux[1][first_type_count - i - 1] = geom::vector<float, 2>::zero();
|
||||
flux[2][first_type_count - i - 1] = geom::vector<float, 2>::zero();
|
||||
}
|
||||
}
|
||||
|
||||
void water_2d_app::update()
|
||||
{
|
||||
float const dt = 0.01f;
|
||||
float const max_speed = 10.f;
|
||||
|
||||
geom::vector<float, 2> 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<int, int> const & p)
|
||||
{
|
||||
return std::pair{N - p.second, p.first - p.second};
|
||||
};
|
||||
|
||||
auto const rot2 = [rot1](std::pair<int, int> const & p)
|
||||
{
|
||||
return rot1(rot1(p));
|
||||
};
|
||||
|
||||
std::vector<float> dh(water_height.size(), 0.f);
|
||||
std::vector<geom::vector<float, 2>> 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<int, int> 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<int, int> 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<float, 3> 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<float, 4, 4> 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<water_2d_app>();
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue