Zoomies wip

This commit is contained in:
Nikita Lisitsa 2024-08-18 01:12:37 +03:00
parent 2468495fc6
commit 1f1dc2b35f
2 changed files with 245 additions and 57 deletions

2
psemek

@ -1 +1 @@
Subproject commit 82f7d5d429f7f04a82235dd0b7023b23feb39eae
Subproject commit a2c83633ae09f3db5c1e4d115a417021d38914ae

View file

@ -54,10 +54,10 @@ namespace gmtk
return {(coords[0] + 0.5f) * s, (coords[1] + 0.5f) * s};
}
geom::box<float, 2> bbox() const
geom::box<float, 2> bbox(float extra = 0.f) const
{
float s = std::pow(3.f, -level);
return {{{(coords[0] + 0) * s, (coords[0] + 1) * s}, {(coords[1] + 0) * s, (coords[1] + 1) * s}}};
return {{{(coords[0] - extra) * s, (coords[0] + 1.f + extra) * s}, {(coords[1] - extra) * s, (coords[1] + 1.f + extra) * s}}};
}
location down() const
@ -104,6 +104,11 @@ namespace gmtk
color output;
};
struct zoomer
{
psemek_ecs_declare_uuid("zoomer")
};
struct path_vertex
{
psemek_ecs_declare_uuid("path_vertex")
@ -133,7 +138,7 @@ namespace gmtk
bool within_grid(location const & l)
{
int max = std::pow(3, l.level + 1);
return l.coords[0] >= 0 && l.coords[0] < max && l.coords[1] >= 0 && l.coords[1] < 3;
return l.coords[0] >= 0 && l.coords[0] < max && l.coords[1] >= 0 && l.coords[1] < max;
}
struct timestamp
@ -169,9 +174,6 @@ namespace gmtk
{
ecs::container world;
int current_level = 0;
geom::point<int, 2> current_origin = {0, 0};
timestamp time = {};
float spawn_timer = 0.f;
@ -277,22 +279,41 @@ namespace gmtk
return result;
}
void draw(map & map, gfx::painter & painter)
void draw_grid(location origin, float view_level, gfx::painter & painter)
{
float const grid_width = 0.1f;
origin.level += 1;
origin.coords[0] *= 3;
origin.coords[1] *= 3;
float const grid_width = 0.1f * std::pow(3.f, -std::max<float>(view_level + 1.f, origin.level));
auto box = origin.bbox();
for (int x = 0; x <= 3; ++x)
{
painter.line({x, 0.f}, {x, 3.f}, grid_width, gfx::black, true);
painter.line({0.f, x}, {3.f, x}, grid_width, gfx::black, true);
painter.line(box.corner(x, 0), box.corner(x, 3), grid_width, gfx::black, true);
painter.line(box.corner(0, x), box.corner(3, x), grid_width, gfx::black, true);
}
}
void draw(map & map, float view_level, gfx::painter & painter)
{
draw_grid({-1, {0, 0}}, view_level, painter);
map.world.apply<vertex const, zoomer const>([&](vertex const & v, zoomer const &)
{
draw_grid(v.location, view_level, painter);
});
map.world.apply<path_vertex const>([&](path_vertex const & vertex)
{
for (auto b : vertex.belts)
{
auto q = map.world.get(b).get<path_vertex const>().location;
painter.line(vertex.location.center(), q.center(), 0.3f, {191, 191, 191, 255}, true);
float w0 = 0.3f * std::pow(3.f, 1.f - vertex.location.level);
float w1 = 0.3f * std::pow(3.f, 1.f - q.level);
gfx::color_rgba c{191, 191, 191, 255};
painter.line(vertex.location.center(), q.center(), w0, w1, c, c, true);
}
});
@ -319,42 +340,56 @@ namespace gmtk
map.world.apply<vertex const, source const>([&](vertex const & v, source const & s)
{
painter.rect(geom::shrink(v.location.bbox(), 0.2f), to_color(s.type));
painter.text(v.location.center(), "180/m", {.scale = {0.01f, -0.01f}, .c = {0, 0, 0, 255}});
float vs = std::pow(3.f, - v.location.level) * 0.01f;
painter.rect(v.location.bbox(-0.2f), to_color(s.type));
painter.text(v.location.center(), "180/m", {.scale = {vs, -vs}, .c = {0, 0, 0, 255}});
});
map.world.apply<vertex const, transformer const>([&](vertex const & v, transformer const & t)
{
auto box = geom::shrink(v.location.bbox(), 0.2f);
auto box = v.location.bbox(-0.2f);
painter.triangle(box.corner(0, 0), box.corner(1, 1), box.corner(0, 1), to_color(t.input));
painter.triangle(box.corner(0, 0), box.corner(1, 0), box.corner(1, 1), to_color(t.output));
});
map.world.apply<vertex const, task const>([&](vertex const & v, task const & t)
{
painter.rect(geom::shrink(v.location.bbox(), 0.2f), to_color(t.type));
painter.text(v.location.center(), std::format("{}/m", t.received.size() * task::freq), {.scale = {0.01f, -0.01f}, .c = {0, 0, 0, 255}});
float vs = std::pow(3.f, - v.location.level) * 0.01f;
painter.rect(v.location.bbox(-0.2f), to_color(t.type));
painter.text(v.location.center(), std::format("{}/m", t.received.size() * task::freq), {.scale = {vs, -vs}, .c = {0, 0, 0, 255}});
});
map.world.apply<item const>([&](item const & i)
{
geom::point<float, 2> pos;
float scale;
if (i.target)
pos = geom::lerp(i.start.center(), map.world.get(i.target).get<path_vertex const>().location.center(), i.state);
{
auto end = map.world.get(i.target).get<path_vertex const>().location;
pos = geom::lerp(i.start.center(), end.center(), i.state);
scale = std::pow(3.f, -geom::lerp<float>(i.start.level, end.level, i.state));
}
else
{
pos = i.start.center();
painter.circle(pos, 0.075f, {0, 0, 0, 255});
painter.circle(pos, 0.05f, to_color(i.type));
scale = std::pow(3.f, -i.start.level);
}
scale *= 3.f;
painter.circle(pos, 0.075f * scale, {0, 0, 0, 255});
painter.circle(pos, 0.05f * scale, to_color(i.type));
});
}
void draw_selection(location const & l, gfx::painter & painter)
{
auto p = l.coords;
painter.line({p[0], p[1]}, {p[0] + 1.f, p[1]}, 0.1f, {255, 0, 255, 255}, true);
painter.line({p[0] + 1.f, p[1]}, {p[0] + 1.f, p[1] + 1.f}, 0.1f, {255, 0, 255, 255}, true);
painter.line({p[0] + 1.f, p[1] + 1.f}, {p[0], p[1] + 1.f}, 0.1f, {255, 0, 255, 255}, true);
painter.line({p[0], p[1] + 1.f}, {p[0], p[1]}, 0.1f, {255, 0, 255, 255}, true);
auto b = l.bbox();
float s = std::pow(3.f, -l.level) * 0.1f;
painter.line(b.corner(0, 0), b.corner(1, 0), s, {255, 0, 255, 255}, true);
painter.line(b.corner(1, 0), b.corner(1, 1), s, {255, 0, 255, 255}, true);
painter.line(b.corner(1, 1), b.corner(0, 1), s, {255, 0, 255, 255}, true);
painter.line(b.corner(0, 1), b.corner(0, 0), s, {255, 0, 255, 255}, true);
}
struct application
@ -363,7 +398,9 @@ namespace gmtk
application(options const &, context const &)
: rng_{random::device{}}
, map_(starting_map(rng_))
{}
{
view_stack_.push_back({-1, {0, 0}});
}
void on_event(app::resize_event const & event) override
{
@ -375,6 +412,31 @@ namespace gmtk
mouse_ = event.position;
}
void on_event(app::mouse_button_event const & event) override
{
if (event.down && event.button == app::mouse_button::left)
{
if (selected_ && !view_transition_)
if (auto entity = map_.world.index<index>().find(*selected_))
if (map_.world.get(*entity).contains<zoomer>())
{
view_transition_ = {view_stack_.back()};
view_stack_.push_back(*selected_);
selected_ = std::nullopt;
}
}
if (event.down && event.button == app::mouse_button::right)
{
if (!view_transition_ && view_stack_.size() > 1)
{
view_transition_ = {view_stack_.back()};
view_stack_.pop_back();
selected_ = std::nullopt;
}
}
}
void on_event(app::key_event const & event) override
{
if (event.down && event.key == app::keycode::S)
@ -407,30 +469,64 @@ namespace gmtk
{
if (belt_start_)
{
auto d = selected_->coords - belt_start_->coords;
if (std::abs(d[0]) + std::abs(d[1]) == 1)
if (selected_->level == belt_start_->level)
{
auto & index = map_.world.index<path_index>();
for (int i = 0; i < 3; ++i)
auto d = selected_->coords - belt_start_->coords;
if (std::abs(d[0]) + std::abs(d[1]) == 1)
{
auto p = belt_start_->down();
p.coords += d * i;
auto q = p;
q.coords += d;
bool from_zoom = false;
bool to_zoom = false;
auto s = index.get(p);
auto t = index.get(q);
if (auto entity = map_.world.index<index>().find(*belt_start_))
if (map_.world.get(*entity).contains<zoomer>())
from_zoom = true;
(void)from_zoom;
auto & sv = map_.world.get(s).get<path_vertex>();
auto & tv = map_.world.get(t).get<path_vertex>();
if (auto entity = map_.world.index<index>().find(*selected_))
if (map_.world.get(*entity).contains<zoomer>())
to_zoom = true;
if (sv.belts.contains(t))
sv.belts.erase(t);
else
auto vx = [&](int i)
{
if (tv.belts.contains(s))
tv.belts.erase(s);
sv.belts.insert(t);
auto p = belt_start_->down();
if (i < 2 && from_zoom)
{
p.coords += d;
p = p.down();
}
if (i >= 2 && to_zoom)
{
p.coords += d;
p = p.down();
}
p.coords += d * i;
return p;
};
auto & index = map_.world.index<path_index>();
for (int i = 0; i < 3; ++i)
{
auto p = vx(i);
auto q = vx(i + 1);
auto s = index.get(p);
auto t = index.get(q);
auto & sv = map_.world.get(s).get<path_vertex>();
auto & tv = map_.world.get(t).get<path_vertex>();
if (sv.belts.contains(t))
sv.belts.erase(t);
else
{
if (tv.belts.contains(s))
tv.belts.erase(s);
sv.belts.insert(t);
}
}
}
}
@ -471,6 +567,17 @@ namespace gmtk
}
}
if (event.down && event.key == app::keycode::Z)
{
if (selected_ && !map_.world.index<index>().find(*selected_))
{
map_.world.create(
vertex{*selected_},
zoomer{}
);
}
}
if (event.down && event.key == app::keycode::X)
{
if (selected_)
@ -527,7 +634,6 @@ namespace gmtk
);
}
map_.world.apply<task>([&](task & t)
{
auto threshold = map_.time;
@ -540,6 +646,16 @@ namespace gmtk
{
if (i.target)
{
{
auto & v = map_.world.get(map_.world.index<path_index>().get(i.start)).get<path_vertex const>();
if (!v.belts.contains(i.target))
{
map_.world.detach<occupied>(i.target);
map_.world.destroy(entity);
return;
}
}
i.state += 3.f * dt;
if (i.state < 1.f)
return;
@ -552,7 +668,24 @@ namespace gmtk
}
else
{
map_.world.detach<occupied>(map_.world.index<path_index>().get(i.start));
auto s = map_.world.index<path_index>().get(i.start);
map_.world.detach<occupied>(s);
auto & v = map_.world.get(s).get<path_vertex const>();
if (v.belts.empty())
{
bool should_destroy = true;
map_.world.apply<path_vertex const>([&](path_vertex const & v){
if (v.belts.contains(s))
should_destroy = false;
});
if (should_destroy)
{
map_.world.destroy(entity);
return;
}
}
}
if (auto cell = map_.world.index<index>().find(i.start.up()))
@ -589,22 +722,52 @@ namespace gmtk
float aspect_ratio = (screen_size_[0] * 1.f) / screen_size_[1];
view_box_[1] = {-1.f, 4.f};
view_box_[0] = view_box_[1];
if (view_transition_)
{
auto from = view_transition_->old.bbox(1.f / 3.f);
auto to = view_stack_.back().bbox(1.f / 3.f);
float t = geom::smoothstep(view_transition_->timer * 2.f);
view_box_[0].min = geom::lerp(from[0].min, to[0].min, t);
view_box_[0].max = geom::lerp(from[0].max, to[0].max, t);
view_box_[1].min = geom::lerp(from[1].min, to[1].min, t);
view_box_[1].max = geom::lerp(from[1].max, to[1].max, t);
view_transition_->timer += dt;
if (view_transition_->timer >= 0.5f)
view_transition_ = std::nullopt;
}
else
{
view_box_ = view_stack_.back().bbox(1.f / 3.f);
}
view_box_[0] = geom::expand(view_box_[0], (view_box_[1].length() * aspect_ratio - view_box_[0].length()) / 2.f);
selected_ = std::nullopt;
if (!view_transition_)
{
auto m = screen_to_grid(geom::cast<float>(mouse_));
auto m = view_box_.corner(
mouse_[0] * 1.f / screen_size_[0],
1.f - mouse_[1] * 1.f / screen_size_[1]
);
int x = std::floor(m[0]);
int y = std::floor(m[1]);
float s = std::pow(3.f, 1 + view_stack_.back().level);
if (x >= 0 && x < 3 && y >= 0 && y < 3)
selected_ = {0, geom::point{x, y}};
else if (map_.world.index<index>().find({0, {x, y}}))
selected_ = {0, geom::point{x, y}};
m[0] *= s;
m[1] *= s;
auto l = view_stack_.back();
location p{1 + l.level, {std::floor(m[0]), std::floor(m[1])}};
if (p.coords[0] >= 3 * l.coords[0] && p.coords[0] < 3 * l.coords[0] + 3 && p.coords[1] >= 3 * l.coords[1] && p.coords[1] < 3 * l.coords[1] + 3)
selected_ = p;
else if (auto entity = map_.world.index<index>().find(p))
if (map_.world.get(*entity).contains<task>())
selected_ = p;
}
}
@ -613,7 +776,15 @@ namespace gmtk
gl::ClearColor(1.f, 1.f, 1.f, 1.f);
gl::Clear(gl::COLOR_BUFFER_BIT);
draw(map_, painter_);
float view_level = view_stack_.back().level;
if (view_transition_)
{
float t = geom::smoothstep(view_transition_->timer * 2.f);
view_level = geom::lerp<float>(view_transition_->old.level, view_stack_.back().level, t);
}
draw(map_, view_level, painter_);
if (selected_)
draw_selection(*selected_, painter_);
@ -634,6 +805,16 @@ namespace gmtk
gfx::painter painter_;
struct view_transition
{
location old;
float timer = 0.f;
};
std::optional<view_transition> view_transition_;
std::vector<location> view_stack_;
geom::box<float, 2> view_box_;
std::optional<location> selected_;
@ -641,10 +822,17 @@ namespace gmtk
geom::point<float, 2> screen_to_grid(geom::point<float, 2> const & p)
{
return view_box_.corner(
auto result = view_box_.corner(
p[0] / screen_size_[0],
1.f - p[1] / screen_size_[1]
);
float s = std::pow(3.f, 1 + view_stack_.back().level);
result[0] *= s;
result[1] *= s;
return result;
}
};