diff options
Diffstat (limited to 'ui')
45 files changed, 618 insertions, 595 deletions
diff --git a/ui/applicationBase.cpp b/ui/applicationBase.cpp index 7764c58..961007b 100644 --- a/ui/applicationBase.cpp +++ b/ui/applicationBase.cpp @@ -1,9 +1,17 @@ #include "applicationBase.h" +#include "imgui_wrap.h" #include <SDL2/SDL.h> #include <stdexcept> ApplicationBase::ApplicationBase() { + initSDL(); + initImGUI(); +} + +void +ApplicationBase::initSDL() const +{ if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) < 0) { throw std::runtime_error(SDL_GetError()); } @@ -27,7 +35,22 @@ ApplicationBase::ApplicationBase() setGlAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1); } +void +ApplicationBase::initImGUI() const +{ + // Setup Dear ImGui context + IMGUI_CHECKVERSION(); + ImGui::CreateContext(); + ImGuiIO & io = ImGui::GetIO(); + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable docking + io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable viewports + io.IniFilename = nullptr; // Disable saving settings automagically +} + ApplicationBase::~ApplicationBase() { SDL_Quit(); + ImGui::DestroyContext(); } diff --git a/ui/applicationBase.h b/ui/applicationBase.h index 9d9b66b..082557d 100644 --- a/ui/applicationBase.h +++ b/ui/applicationBase.h @@ -9,4 +9,8 @@ public: NO_COPY(ApplicationBase); NO_MOVE(ApplicationBase); + +private: + void initSDL() const; + void initImGUI() const; }; diff --git a/ui/builders/freeExtend.cpp b/ui/builders/freeExtend.cpp index db127e6..aff7cd7 100644 --- a/ui/builders/freeExtend.cpp +++ b/ui/builders/freeExtend.cpp @@ -16,17 +16,17 @@ BuilderFreeExtend::move( { if (p1) { if (const auto p = network->intersectRayNodes(ray)) { - candidateLinks.objects = network->candidateJoins(*p1, p->pos); + candidateLinks = network->candidateJoins(*p1, p->pos); } else if (const auto p = geoData->intersectRay(ray)) { - candidateLinks.objects = network->candidateExtend(*p1, p->first); + candidateLinks = network->candidateExtend(*p1, p->first); } else { - candidateLinks.removeAll(); + candidateLinks.clear(); } } else { - candidateLinks.removeAll(); + candidateLinks.clear(); } } @@ -38,11 +38,11 @@ BuilderFreeExtend::click( case SDL_BUTTON_LEFT: if (p1) { if (const auto p = network->intersectRayNodes(ray)) { - network->addJoins(*p1, p->pos); + createJoin(network, geoData, *p1, p->pos); p1 = p->pos; } else if (const auto p = geoData->intersectRay(ray)) { - network->addExtend(*p1, p->first); + createExtend(network, geoData, *p1, p->first); p1 = p->first; } } @@ -57,3 +57,21 @@ BuilderFreeExtend::click( return; } } + +Link::CCollection +BuilderFreeExtend::createJoin( + Network * network, const GeoData * geoData, GlobalPosition3D p1, GlobalPosition3D p2) const +{ + const auto links = network->addJoins(geoData, p1, p2); + setHeightsFor(network, links); + return links; +} + +Link::CCollection +BuilderFreeExtend::createExtend( + Network * network, const GeoData * geoData, GlobalPosition3D p1, GlobalPosition3D p2) const +{ + const auto links = network->addExtend(geoData, p1, p2); + setHeightsFor(network, links); + return links; +} diff --git a/ui/builders/freeExtend.h b/ui/builders/freeExtend.h index 0d5f327..6f28493 100644 --- a/ui/builders/freeExtend.h +++ b/ui/builders/freeExtend.h @@ -5,11 +5,17 @@ class Network; class GeoData; class BuilderFreeExtend : public EditNetwork::Builder { +private: std::string hint() const override; void click(Network * network, const GeoData * geoData, const SDL_MouseButtonEvent & e, const Ray<GlobalPosition3D> & ray) override; void move(Network * network, const GeoData * geoData, const SDL_MouseMotionEvent & e, const Ray<GlobalPosition3D> & ray) override; +public: + Link::CCollection createJoin(Network * network, const GeoData *, GlobalPosition3D, GlobalPosition3D) const; + Link::CCollection createExtend(Network * network, const GeoData *, GlobalPosition3D, GlobalPosition3D) const; + +private: std::optional<GlobalPosition3D> p1; }; diff --git a/ui/builders/join.cpp b/ui/builders/join.cpp index 7474c5b..f6cbce5 100644 --- a/ui/builders/join.cpp +++ b/ui/builders/join.cpp @@ -15,25 +15,25 @@ BuilderJoin::move(Network * network, const GeoData *, const SDL_MouseMotionEvent { if (p1) { if (const auto p = network->intersectRayNodes(ray)) { - candidateLinks.objects = network->candidateJoins(p1->pos, p->pos); + candidateLinks = network->candidateJoins(p1->pos, p->pos); } else { - candidateLinks.removeAll(); + candidateLinks.clear(); } } } void BuilderJoin::click( - Network * network, const GeoData *, const SDL_MouseButtonEvent & e, const Ray<GlobalPosition3D> & ray) + Network * network, const GeoData * geoData, const SDL_MouseButtonEvent & e, const Ray<GlobalPosition3D> & ray) { switch (e.button) { case SDL_BUTTON_LEFT: if (const auto p = network->intersectRayNodes(ray)) { if (p1) { - create(network, p1, p); + create(network, geoData, p1, p); p1.reset(); - candidateLinks.removeAll(); + candidateLinks.clear(); } else { p1 = p; @@ -46,8 +46,10 @@ BuilderJoin::click( } } -void -BuilderJoin::create(Network * network, const Node::Ptr & p1, const Node::Ptr & p2) const +Link::CCollection +BuilderJoin::create(Network * network, const GeoData * geoData, const Node::Ptr & p1, const Node::Ptr & p2) const { - network->addJoins(p1->pos, p2->pos); + const auto links = network->addJoins(geoData, p1->pos, p2->pos); + setHeightsFor(network, links); + return links; } diff --git a/ui/builders/join.h b/ui/builders/join.h index dd57895..326d23d 100644 --- a/ui/builders/join.h +++ b/ui/builders/join.h @@ -5,13 +5,14 @@ class Network; class GeoData; class BuilderJoin : public EditNetwork::Builder { +private: std::string hint() const override; void click(Network * network, const GeoData * geoData, const SDL_MouseButtonEvent & e, const Ray<GlobalPosition3D> & ray) override; void move(Network * network, const GeoData * geoData, const SDL_MouseMotionEvent & e, const Ray<GlobalPosition3D> & ray) override; - void create(Network * network, const Node::Ptr & p1, const Node::Ptr & p2) const; + Link::CCollection create(Network * network, const GeoData *, const Node::Ptr & p1, const Node::Ptr & p2) const; Node::Ptr p1; }; diff --git a/ui/builders/straight.cpp b/ui/builders/straight.cpp index 43f5ec8..e7d83b5 100644 --- a/ui/builders/straight.cpp +++ b/ui/builders/straight.cpp @@ -1,4 +1,5 @@ #include "straight.h" +#include "stream_support.h" #include <game/geoData.h> std::string @@ -16,10 +17,10 @@ BuilderStraight::move( { if (p1) { if (const auto p = geoData->intersectRay(ray)) { - candidateLinks.objects = network->candidateStraight(*p1, p->first); + candidateLinks = network->candidateStraight(*p1, p->first); } else { - candidateLinks.removeAll(); + candidateLinks.clear(); } } } @@ -32,8 +33,8 @@ BuilderStraight::click( case SDL_BUTTON_LEFT: if (const auto p = geoData->intersectRay(ray)) { if (p1) { - create(network, *p1, p->first); - candidateLinks.removeAll(); + create(network, geoData, *p1, p->first); + candidateLinks.clear(); p1.reset(); } else { @@ -43,13 +44,15 @@ BuilderStraight::click( return; case SDL_BUTTON_MIDDLE: p1.reset(); - candidateLinks.removeAll(); + candidateLinks.clear(); return; } } -void -BuilderStraight::create(Network * network, GlobalPosition3D p1, GlobalPosition3D p2) const +Link::CCollection +BuilderStraight::create(Network * network, const GeoData * geoData, GlobalPosition3D p1, GlobalPosition3D p2) const { - network->addStraight(p1, p2); + const auto links = network->addStraight(geoData, p1, p2); + setHeightsFor(network, links); + return links; } diff --git a/ui/builders/straight.h b/ui/builders/straight.h index 28eb66e..0a6f290 100644 --- a/ui/builders/straight.h +++ b/ui/builders/straight.h @@ -5,13 +5,16 @@ class Network; class GeoData; class BuilderStraight : public EditNetwork::Builder { +private: std::string hint() const override; void click(Network * network, const GeoData * geoData, const SDL_MouseButtonEvent & e, const Ray<GlobalPosition3D> & ray) override; void move(Network * network, const GeoData * geoData, const SDL_MouseMotionEvent & e, const Ray<GlobalPosition3D> & ray) override; - void create(Network * network, GlobalPosition3D p1, GlobalPosition3D p2) const; +public: + Link::CCollection create(Network * network, const GeoData *, GlobalPosition3D p1, GlobalPosition3D p2) const; +private: std::optional<GlobalPosition3D> p1; }; diff --git a/ui/editNetwork.cpp b/ui/editNetwork.cpp index ac2d93d..c900191 100644 --- a/ui/editNetwork.cpp +++ b/ui/editNetwork.cpp @@ -2,31 +2,21 @@ #include "builders/freeExtend.h" #include "builders/join.h" #include "builders/straight.h" -#include "text.h" +#include "imgui_wrap.h" #include <game/gamestate.h> -#include <game/geoData.h> +#include <game/terrain.h> #include <gfx/gl/sceneShader.h> #include <gfx/models/texture.h> -const std::filesystem::path fontpath {"/usr/share/fonts/hack/Hack-Regular.ttf"}; constexpr const glm::u8vec4 TRANSPARENT_BLUE {30, 50, 255, 200}; -EditNetwork::EditNetwork(Network * n) : - network {n}, - builderToolbar { - {"ui/icon/network.png", mode.toggle<BuilderStraight>()}, - {"ui/icon/network.png", mode.toggle<BuilderJoin>()}, - {"ui/icon/network.png", mode.toggle<BuilderFreeExtend>()}, - }, - blue {1, 1, &TRANSPARENT_BLUE}, font {fontpath, 15} -{ -} +EditNetwork::EditNetwork(Network * n) : network {n}, blue {1, 1, &TRANSPARENT_BLUE} { } bool EditNetwork::click(const SDL_MouseButtonEvent & e, const Ray<GlobalPosition3D> & ray) { if (builder && (e.button == SDL_BUTTON_LEFT || e.button == SDL_BUTTON_MIDDLE)) { - builder->click(network, gameState->geoData.get(), e, ray); + builder->click(network, gameState->terrain.get(), e, ray); return true; } return false; @@ -36,38 +26,59 @@ bool EditNetwork::move(const SDL_MouseMotionEvent & e, const Ray<GlobalPosition3D> & ray) { if (builder) { - builder->move(network, gameState->geoData.get(), e, ray); + builder->move(network, gameState->terrain.get(), e, ray); } return false; } bool -EditNetwork::handleInput(const SDL_Event & e, const UIComponent::Position & parentPos) +EditNetwork::handleInput(const SDL_Event &) { - return builderToolbar.handleInput(e, parentPos); + return false; } void -EditNetwork::render(const SceneShader & shader) const +EditNetwork::render(const SceneShader & shader, const Frustum & frustum) const { if (builder) { blue.bind(); shader.absolute.use(); - builder->render(shader); + builder->render(shader, frustum); } } void -EditNetwork::Builder::render(const SceneShader & shader) const +EditNetwork::Builder::render(const SceneShader & shader, const Frustum & frustum) const { - candidateLinks.apply<const Renderable>(&Renderable::render, shader); + candidateLinks.apply<const Renderable>(&Renderable::render, shader, frustum); } void -EditNetwork::render(const UIShader & shader, const UIComponent::Position & parentPos) const +EditNetwork::Builder::setHeightsFor(Network * network, const Link::CCollection & links, GeoData::SetHeightsOpts opts) { - if (builder) { - Text {builder->hint(), font, {{50, 10}, {0, 15}}, {1, 1, 0}}.render(shader, parentPos); + opts.surface = network->getBaseSurface(); + const auto width = network->getBaseWidth(); + + for (const auto & link : links) { + gameState->terrain->setHeights(link->getBase(width), opts); } - builderToolbar.render(shader, parentPos); +} + +void +EditNetwork::render(bool & open) +{ + ImGui::SetNextWindowSize({-1, -1}); + ImGui::Begin("Edit Network", &open); + + auto builderChoice = [this]<typename Impl>(const char * name) { + if (ImGui::RadioButton(name, dynamic_cast<Impl *>(builder.get()))) { + builder = std::make_unique<Impl>(); + } + }; + builderChoice.operator()<BuilderStraight>("Straight"); + builderChoice.operator()<BuilderJoin>("Join"); + builderChoice.operator()<BuilderFreeExtend>("Free Extend"); + ImGui::TextUnformatted(builder ? builder->hint().c_str() : "Select a build mode"); + + ImGui::End(); } diff --git a/ui/editNetwork.h b/ui/editNetwork.h index ec06fa7..4155534 100644 --- a/ui/editNetwork.h +++ b/ui/editNetwork.h @@ -1,8 +1,7 @@ #pragma once +#include "game/geoData.h" #include "gameMainSelector.h" -#include "modeHelper.h" -#include "toolbar.h" #include "worldOverlay.h" #include <game/gamestate.h> #include <game/network/network.h> @@ -16,33 +15,32 @@ public: bool click(const SDL_MouseButtonEvent & e, const Ray<GlobalPosition3D> &) override; bool move(const SDL_MouseMotionEvent & e, const Ray<GlobalPosition3D> &) override; - bool handleInput(const SDL_Event & e, const UIComponent::Position &) override; - void render(const SceneShader &) const override; - void render(const UIShader & shader, const UIComponent::Position & pos) const override; + bool handleInput(const SDL_Event & e) override; + void render(const SceneShader &, const Frustum &) const override; + void render(bool & open) override; using NetworkClickPos = std::variant<GlobalPosition3D, Node::Ptr>; class Builder { public: virtual ~Builder() = default; - virtual void render(const SceneShader & shader) const; + virtual void render(const SceneShader & shader, const Frustum &) const; virtual std::string hint() const = 0; virtual void click(Network *, const GeoData *, const SDL_MouseButtonEvent &, const Ray<GlobalPosition3D> &) = 0; virtual void move(Network *, const GeoData *, const SDL_MouseMotionEvent &, const Ray<GlobalPosition3D> &) = 0; + static void setHeightsFor(Network *, const Link::CCollection &, GeoData::SetHeightsOpts = {}); + using Ptr = std::unique_ptr<Builder>; protected: - Collection<const Link> candidateLinks; + SharedCollection<const Link> candidateLinks; }; private: Network * network; Builder::Ptr builder; - Mode<Builder::Ptr, ModeSecondClick::NoAction> mode {builder}; - Toolbar builderToolbar; Texture blue; - const Font font; }; template<typename T> class EditNetworkOf : public EditNetwork { diff --git a/ui/gameMainSelector.cpp b/ui/gameMainSelector.cpp index 5bef48d..0c40abc 100644 --- a/ui/gameMainSelector.cpp +++ b/ui/gameMainSelector.cpp @@ -1,50 +1,44 @@ #include "gameMainSelector.h" -#include "collection.h" -#include "text.h" #include "ui/uiComponent.h" #include <SDL2/SDL.h> #include <game/gamestate.h> -#include <game/geoData.h> #include <game/selectable.h> +#include <game/terrain.h> #include <game/worldobject.h> // IWYU pragma: keep -#include <gfx/gl/camera.h> -#include <optional> +#include <gfx/camera.h> #include <stream_support.h> -#include <typeinfo> -const std::filesystem::path fontpath {"/usr/share/fonts/hack/Hack-Regular.ttf"}; - -GameMainSelector::GameMainSelector(const Camera * c, ScreenAbsCoord size) : - UIComponent {{{}, size}}, camera {c}, font {fontpath, 15} -{ -} +GameMainSelector::GameMainSelector(const Camera * c) : camera {c} { } constexpr ScreenAbsCoord TargetPos {5, 45}; void -GameMainSelector::render(const UIShader & shader, const Position & parentPos) const +GameMainSelector::render() { if (target) { - target->render(shader, parentPos + position + TargetPos); - } - if (!clicked.empty()) { - Text {clicked, font, {{50, 10}, {0, 15}}, {1, 1, 0}}.render(shader, parentPos); + bool open = true; + target->render(open); + if (!open) { + target.reset(); + } } } void -GameMainSelector::render(const SceneShader & shader) const +GameMainSelector::render(const SceneShader & shader, const Frustum & frustum) const { if (target) { - target->render(shader); + target->render(shader, frustum); } } bool -GameMainSelector::handleInput(const SDL_Event & e, const Position & parentPos) +GameMainSelector::handleInput(const SDL_Event & e) { - const auto getRay = [this](const auto & e) { - const auto mouse = ScreenRelCoord {e.x, e.y} / position.size; + const auto getRay = [this, &window = e.window](const auto & e) { + glm::ivec2 size {}; + SDL_GetWindowSizeInPixels(SDL_GetWindowFromID(window.windowID), &size.x, &size.y); + const auto mouse = ScreenRelCoord {e.x, e.y} / ScreenRelCoord {size}; return camera->unProject(mouse); }; if (target) { @@ -60,7 +54,7 @@ GameMainSelector::handleInput(const SDL_Event & e, const Position & parentPos) } break; } - return target->handleInput(e, parentPos + position + TargetPos); + return target->handleInput(e); } else { switch (e.type) { @@ -73,22 +67,8 @@ GameMainSelector::handleInput(const SDL_Event & e, const Position & parentPos) } void -GameMainSelector::defaultClick(const Ray<GlobalPosition3D> & ray) +GameMainSelector::defaultClick(const Ray<GlobalPosition3D> &) { - BaryPosition baryPos {}; - RelativeDistance distance {}; - - if (const auto selected = gameState->world.applyOne<Selectable>(&Selectable::intersectRay, ray, baryPos, distance); - selected != gameState->world.end()) { - const auto & ref = *selected.base()->get(); - clicked = typeid(ref).name(); - } - else if (const auto pos = gameState->geoData->intersectRay(ray)) { - clicked = streamed_string(*pos); - } - else { - clicked.clear(); - } } bool @@ -104,17 +84,17 @@ GameMainSelector::Component::move(const SDL_MouseMotionEvent &, const Ray<Global } bool -GameMainSelector::Component::handleInput(const SDL_Event &, const Position &) +GameMainSelector::Component::handleInput(const SDL_Event &) { return false; } void -GameMainSelector::Component::render(const UIShader &, const UIComponent::Position &) const +GameMainSelector::Component::render(bool &) { } void -GameMainSelector::Component::render(const SceneShader &) const +GameMainSelector::Component::render(const SceneShader &, const Frustum &) const { } diff --git a/ui/gameMainSelector.h b/ui/gameMainSelector.h index ccf0fa0..8c2be4b 100644 --- a/ui/gameMainSelector.h +++ b/ui/gameMainSelector.h @@ -2,16 +2,13 @@ #include "SDL_events.h" #include "config/types.h" -#include "font.h" #include "uiComponent.h" #include "worldOverlay.h" #include <glm/glm.hpp> #include <memory> -#include <string> class SceneShader; template<typename> class Ray; -class UIShader; class Camera; class GameMainSelector : public UIComponent, public WorldOverlay { @@ -22,17 +19,17 @@ public: virtual bool click(const SDL_MouseButtonEvent &, const Ray<GlobalPosition3D> &); virtual bool move(const SDL_MouseMotionEvent &, const Ray<GlobalPosition3D> &); - virtual bool handleInput(const SDL_Event &, const Position & pos); - virtual void render(const UIShader & shader, const Position & pos) const; - virtual void render(const SceneShader &) const; + virtual bool handleInput(const SDL_Event &); + virtual void render(bool & open); + virtual void render(const SceneShader &, const Frustum &) const; }; - GameMainSelector(const Camera * c, ScreenAbsCoord size); + GameMainSelector(const Camera * c); - void render(const UIShader & shader, const Position & pos) const override; - void render(const SceneShader & shader) const override; + void render() override; + void render(const SceneShader & shader, const Frustum &) const override; - bool handleInput(const SDL_Event & e, const Position &) override; + bool handleInput(const SDL_Event & e) override; void defaultClick(const Ray<GlobalPosition3D> & ray); @@ -40,6 +37,4 @@ public: private: const Camera * camera; - const Font font; - std::string clicked; }; diff --git a/ui/gameMainWindow.cpp b/ui/gameMainWindow.cpp index ccbcdba..dbbf8a7 100644 --- a/ui/gameMainWindow.cpp +++ b/ui/gameMainWindow.cpp @@ -1,38 +1,60 @@ #include "gameMainWindow.h" #include "editNetwork.h" #include "gameMainSelector.h" -#include "gfx/camera_controller.h" +#include "imgui_extras.h" #include "manualCameraController.h" -#include "modeHelper.h" -#include "toolbar.h" -#include "window.h" +#include "queryTool.h" +#include "svgIcon.h" #include <SDL2/SDL.h> #include <collection.h> +#include <game/environment.h> #include <game/gamestate.h> #include <game/network/rail.h> #include <game/worldobject.h> // IWYU pragma: keep +#include <gfx/camera_controller.h> #include <gfx/renderable.h> #include <glad/gl.h> #include <glm/glm.hpp> #include <memory> -class GameMainToolbar : Mode<decltype(GameMainSelector::target)>, public Toolbar { +class GameMainToolbar : public UIComponent { public: - explicit GameMainToolbar(GameMainSelector * gms_) : - Mode<decltype(GameMainSelector::target)> {gms_->target}, - Toolbar { - {"ui/icon/network.png", toggle<EditNetworkOf<RailLinks>>()}, + static constexpr auto TOOLBAR_HEIGHT = 54.F; + template<typename T> static constexpr T TOOLBAR_ICON_SIZE {32, 32}; + + explicit GameMainToolbar(GameMainSelector * gms) : gms {gms} { } + + void + render() override + { + if (IltGui::BeginToolbar("bottomBar", ImGuiDir_Down, TOOLBAR_HEIGHT)) { + if (ImGui::ImageButton("Build rails", *buildRailsIcon, TOOLBAR_ICON_SIZE<ImVec2>)) { + gms->target = std::make_unique<EditNetworkOf<RailLinks>>(); + } + if (ImGui::ImageButton("Query", *queryToolIcon, TOOLBAR_ICON_SIZE<ImVec2>)) { + gms->target = std::make_unique<QueryTool>(); + } + IltGui::EndToolbar(); } + } + + bool + handleInput(const SDL_Event &) override { + return false; } + +private: + SvgIcon buildRailsIcon {TOOLBAR_ICON_SIZE<ImageDimensions>, "ui/icon/rails.svg"}; + SvgIcon queryToolIcon {TOOLBAR_ICON_SIZE<ImageDimensions>, "ui/icon/magnifier.svg"}; + GameMainSelector * gms; }; -GameMainWindow::GameMainWindow(size_t w, size_t h) : - Window {w, h, "I Like Trains", SDL_WINDOW_OPENGL}, SceneRenderer {Window::size, 0} +GameMainWindow::GameMainWindow(ScreenAbsCoord size) : SceneRenderer {size, 0} { uiComponents.create<ManualCameraController>(glm::vec2 {310'727'624, 494'018'810}); - auto gms = uiComponents.create<GameMainSelector>(&camera, ScreenAbsCoord {w, h}); - uiComponents.create<GameMainToolbar>(gms.get()); + auto gms = uiComponents.create<GameMainSelector>(&camera); + uiComponents.create<GameMainToolbar>(gms); } void @@ -41,45 +63,66 @@ GameMainWindow::tick(TickDuration) uiComponents.apply<CameraController>(&CameraController::updateCamera, &camera); } +bool +GameMainWindow::handleInput(const SDL_Event & event) +{ + switch (event.type) { + case SDL_WINDOWEVENT: + switch (event.window.event) { + case SDL_WINDOWEVENT_RESIZED: + SceneRenderer::resize({event.window.data1, event.window.data2}); + break; + default: + break; + } + default: + break; + } + + return WindowContent::handleInput(event); +} + void -GameMainWindow::render() const +GameMainWindow::render() { SceneRenderer::render(*this); - Window::render(); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDisable(GL_DEPTH_TEST); + uiComponents.apply(&UIComponent::render); } void -GameMainWindow::content(const SceneShader & shader) const +GameMainWindow::content(const SceneShader & shader, const Frustum & frustum) const { - for (const auto & [id, asset] : gameState->assets) { - if (const auto r = std::dynamic_pointer_cast<const Renderable>(asset)) { - r->render(shader); + for (const auto & [assetId, asset] : gameState->assets) { + if (const auto renderable = asset.getAs<const Renderable>()) { + renderable->render(shader, frustum); } } - gameState->world.apply<Renderable>(&Renderable::render, shader); - uiComponents.apply<WorldOverlay>(&WorldOverlay::render, shader); + gameState->world.apply<const Renderable>(&Renderable::render, shader, frustum); + uiComponents.apply<WorldOverlay>(&WorldOverlay::render, shader, frustum); } void -GameMainWindow::environment(const SceneShader & s, const SceneRenderer & r) const +GameMainWindow::environment(const SceneShader &, const SceneRenderer & r) const { - // default for now - SceneProvider::environment(s, r); + gameState->environment->render(r, *this); } void GameMainWindow::lights(const SceneShader & shader) const { - gameState->world.apply<Renderable>(&Renderable::lights, shader); + gameState->world.apply<const Renderable>(&Renderable::lights, shader); } void -GameMainWindow::shadows(const ShadowMapper & shadowMapper) const +GameMainWindow::shadows(const ShadowMapper & shadowMapper, const Frustum & frustum) const { - for (const auto & [id, asset] : gameState->assets) { - if (const auto r = std::dynamic_pointer_cast<const Renderable>(asset)) { - r->shadows(shadowMapper); + for (const auto & [assetId, asset] : gameState->assets) { + if (const auto renderable = asset.getAs<const Renderable>()) { + renderable->shadows(shadowMapper, frustum); } } - gameState->world.apply<Renderable>(&Renderable::shadows, shadowMapper); + gameState->world.apply<const Renderable>(&Renderable::shadows, shadowMapper, frustum); } diff --git a/ui/gameMainWindow.h b/ui/gameMainWindow.h index a4e822e..71b6314 100644 --- a/ui/gameMainWindow.h +++ b/ui/gameMainWindow.h @@ -2,20 +2,20 @@ #include "chronology.h" #include "gfx/gl/sceneRenderer.h" -#include "window.h" -#include <cstddef> +#include "windowContent.h" -class GameMainWindow : public Window, SceneRenderer, public SceneProvider { +class GameMainWindow : public WindowContent, SceneRenderer, public SceneProvider { public: - GameMainWindow(size_t w, size_t h); + GameMainWindow(ScreenAbsCoord size); void tick(TickDuration) override; - - void render() const override; + void render() override; private: - void content(const SceneShader &) const override; + bool handleInput(const SDL_Event &) override; + + void content(const SceneShader &, const Frustum &) const override; void environment(const SceneShader &, const SceneRenderer &) const override; void lights(const SceneShader &) const override; - void shadows(const ShadowMapper &) const override; + void shadows(const ShadowMapper &, const Frustum &) const override; }; diff --git a/ui/icon.cpp b/ui/icon.cpp index c3b5078..0bdc91a 100644 --- a/ui/icon.cpp +++ b/ui/icon.cpp @@ -22,9 +22,10 @@ Icon::Icon(const Image & tex) : size {tex.width, tex.height} GL_RGBA, GL_UNSIGNED_BYTE, tex.data.data()); } -void -Icon::Bind() const +ImTextureID +Icon::operator*() const { - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, m_texture); + static_assert(sizeof(m_texture) <= sizeof(ImTextureID)); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast,performance-no-int-to-ptr) This is how ImGui works + return reinterpret_cast<ImTextureID>(*m_texture); } @@ -1,5 +1,6 @@ #pragma once +#include "imgui_wrap.h" #include <filesystem> #include <glArrays.h> #include <glm/glm.hpp> @@ -11,8 +12,8 @@ public: explicit Icon(const std::filesystem::path & fileName); explicit Icon(const Image & image); - void Bind() const; const glm::vec2 size; + ImTextureID operator*() const; private: glTexture m_texture; diff --git a/ui/iconButton.cpp b/ui/iconButton.cpp deleted file mode 100644 index fe8c817..0000000 --- a/ui/iconButton.cpp +++ /dev/null @@ -1,57 +0,0 @@ -#include "iconButton.h" -#include "glArrays.h" -#include "ui/icon.h" -#include "ui/uiComponent.h" -#include <SDL2/SDL.h> -#include <array> -#include <filesystem> -#include <functional> -#include <glad/gl.h> -#include <glm/gtc/type_ptr.hpp> -#include <utility> - -IconButton::IconButton(const std::string & icon_, glm::vec2 position_, UIEvent click_) : - UIComponent {{position_, ICON_SIZE}}, icon {icon_}, click {std::move(click_)} -{ - glBindVertexArray(m_vertexArrayObject); - - glBindBuffer(GL_ARRAY_BUFFER, m_vertexArrayBuffer); - glBufferData(GL_ARRAY_BUFFER, static_cast<GLsizeiptr>(sizeof(glm::vec4) * 4), nullptr, GL_DYNAMIC_DRAW); - - glEnableVertexAttribArray(0); - glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(glm::vec4), nullptr); - - glBindVertexArray(0); -} - -void -IconButton::render(const UIShader &, const Position & parentPos) const -{ - icon.Bind(); - glBindVertexArray(m_vertexArrayObject); - glBindBuffer(GL_ARRAY_BUFFER, m_vertexArrayBuffer); - const auto abs = parentPos.origin + position.origin; - const auto limit = abs + ICON_SIZE; - std::array<glm::vec4, 4> vertices {{ - {abs.x, abs.y, 0, 0}, - {limit.x, abs.y, 1, 0}, - {limit.x, limit.y, 1, 1}, - {abs.x, limit.y, 0, 1}, - }}; - glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), glm::value_ptr(vertices.front())); - glDrawArrays(GL_TRIANGLE_FAN, 0, 4); - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindVertexArray(0); -} - -bool -IconButton::handleInput(const SDL_Event & e, const Position & parentPos) -{ - const auto absPos = position + parentPos; - if (absPos & e.button) { - if (e.button.type == SDL_MOUSEBUTTONUP && e.button.button == SDL_BUTTON_LEFT) { - click(e); - } - } - return false; -} diff --git a/ui/iconButton.h b/ui/iconButton.h deleted file mode 100644 index 0afe92d..0000000 --- a/ui/iconButton.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#include "icon.h" -#include "uiComponent.h" -#include <glArrays.h> -#include <glm/glm.hpp> -#include <string> - -class UIShader; -union SDL_Event; - -static const constexpr glm::vec2 ICON_SIZE {32.F, 32.F}; - -class IconButton : public UIComponent { -public: - IconButton(const std::string & icon, glm::vec2 position, UIEvent click); - - void render(const UIShader &, const Position & parentPos) const override; - - bool handleInput(const SDL_Event & e, const Position & parentPos) override; - - Icon icon; - UIEvent click; - glVertexArray m_vertexArrayObject; - glBuffer m_vertexArrayBuffer; -}; diff --git a/ui/imgui_extras.cpp b/ui/imgui_extras.cpp new file mode 100644 index 0000000..1643f4f --- /dev/null +++ b/ui/imgui_extras.cpp @@ -0,0 +1,33 @@ +#define IMGUI_INTERNAL +#include "imgui_extras.h" + +namespace IltGui { + bool + BeginToolbar(const char * name, ImGuiDir dir, float axisSize, ImGuiWindowFlags windowFlags) + { + return BeginToolbar(name, ImGui::GetMainViewport(), dir, axisSize, windowFlags); + } + + bool + BeginToolbar( + const char * name, ImGuiViewport * viewport, ImGuiDir dir, float axisSize, ImGuiWindowFlags windowFlags) + { + bool isOpen = ImGui::BeginViewportSideBar(name, viewport, dir, axisSize, + windowFlags | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings); + if (isOpen) { + if (dir == ImGuiDir_Up || dir == ImGuiDir_Down) { + ImGui::GetCurrentWindow()->DC.LayoutType = ImGuiLayoutType_Horizontal; + } + } + else { + ImGui::End(); + } + return isOpen; + } + + void + EndToolbar() + { + ImGui::End(); + } +} diff --git a/ui/imgui_extras.h b/ui/imgui_extras.h new file mode 100644 index 0000000..0babaa3 --- /dev/null +++ b/ui/imgui_extras.h @@ -0,0 +1,10 @@ +#include "imgui_wrap.h" + +namespace IltGui { + // NOLINTBEGIN(readability-identifier-naming) + bool BeginToolbar(const char * name, ImGuiViewport * viewport, ImGuiDir dir, float axisSize, + ImGuiWindowFlags windowFlags = 0); + bool BeginToolbar(const char * name, ImGuiDir dir, float axisSize, ImGuiWindowFlags windowFlags = 0); + void EndToolbar(); + // NOLINTEND(readability-identifier-naming) +} diff --git a/ui/imgui_wrap.h b/ui/imgui_wrap.h new file mode 100644 index 0000000..520d8b8 --- /dev/null +++ b/ui/imgui_wrap.h @@ -0,0 +1,13 @@ +#pragma once + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wold-style-cast" +#include "imgui.h" // IWYU pragma: export +#ifdef IMGUI_INTERNAL +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wconversion" +# pragma GCC diagnostic ignored "-Wsign-conversion" +# include "imgui_internal.h" // IWYU pragma: export +# pragma GCC diagnostic pop +#endif +#pragma GCC diagnostic pop diff --git a/ui/mainApplication.cpp b/ui/mainApplication.cpp new file mode 100644 index 0000000..6cb1037 --- /dev/null +++ b/ui/mainApplication.cpp @@ -0,0 +1,43 @@ +#include "mainApplication.h" +#include "game/gamestate.h" +#include "game/worldobject.h" +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wold-style-cast" +#include "backends/imgui_impl_sdl2.h" +#pragma GCC diagnostic pop + +void +MainApplication::mainLoop() +{ + auto t_start = std::chrono::high_resolution_clock::now(); + while (isRunning) { + processInputs(); + const auto t_end = std::chrono::high_resolution_clock::now(); + const auto t_passed = std::chrono::duration_cast<TickDuration>(t_end - t_start); + + if (gameState) { + gameState->world.apply(&WorldObject::tick, t_passed); + } + windows.apply(&Window::tick, t_passed); + windows.apply(&Window::refresh); + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindowsDefault(); + + t_start = t_end; + } +} + +void +MainApplication::processInputs() +{ + for (SDL_Event e; SDL_PollEvent(&e);) { + if (e.type == SDL_QUIT) { + isRunning = false; + return; + } + ImGui_ImplSDL2_ProcessEvent(&e); + if (!ImGui::GetIO().WantCaptureMouse) { + windows.applyOne(&Window::handleInput, e); + } + } +} diff --git a/ui/mainApplication.h b/ui/mainApplication.h new file mode 100644 index 0000000..1489587 --- /dev/null +++ b/ui/mainApplication.h @@ -0,0 +1,18 @@ +#pragma once + +#include "applicationBase.h" +#include "collection.h" +#include "window.h" + +class MainApplication : public ApplicationBase { +public: + using Windows = SharedCollection<Window>; + void mainLoop(); + +protected: + Windows windows; + +private: + void processInputs(); + bool isRunning {true}; +}; diff --git a/ui/mainWindow.cpp b/ui/mainWindow.cpp new file mode 100644 index 0000000..57dabc0 --- /dev/null +++ b/ui/mainWindow.cpp @@ -0,0 +1,25 @@ +#include "mainWindow.h" +#include <format> +#include <stdexcept> + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wold-style-cast" +#include "backends/imgui_impl_opengl3.h" +#include "backends/imgui_impl_sdl2.h" +#pragma GCC diagnostic pop + +MainWindow::MainWindow(ScreenAbsCoord size, const char * title, Uint32 flags) : Window {size, title, flags} +{ + if (const auto version = gladLoadGL(reinterpret_cast<GLADloadfunc>(SDL_GL_GetProcAddress)); version < 30003) { + throw std::runtime_error {std::format("Insufficient OpenGL version: {}", version)}; + } + + ImGui_ImplSDL2_InitForOpenGL(m_window, glContext.get()); + ImGui_ImplOpenGL3_Init(); +} + +MainWindow::~MainWindow() +{ + ImGui_ImplOpenGL3_Shutdown(); + ImGui_ImplSDL2_Shutdown(); +} diff --git a/ui/mainWindow.h b/ui/mainWindow.h new file mode 100644 index 0000000..d2de9b3 --- /dev/null +++ b/ui/mainWindow.h @@ -0,0 +1,13 @@ +#pragma once + +#include "window.h" +#include <cstddef> + +class MainWindow : public Window { +public: + MainWindow(ScreenAbsCoord size, const char * title, Uint32 flags = SDL_WINDOW_RESIZABLE | SDL_WINDOW_OPENGL); + ~MainWindow() override; + + NO_MOVE(MainWindow); + NO_COPY(MainWindow); +}; diff --git a/ui/manualCameraController.cpp b/ui/manualCameraController.cpp index 065e1d8..553afc1 100644 --- a/ui/manualCameraController.cpp +++ b/ui/manualCameraController.cpp @@ -1,11 +1,11 @@ #include "manualCameraController.h" #include <algorithm> #include <cmath> -#include <gfx/gl/camera.h> +#include <gfx/camera.h> #include <maths.h> bool -ManualCameraController::handleInput(const SDL_Event & e, const Position &) +ManualCameraController::handleInput(const SDL_Event & e) { switch (e.type) { case SDL_KEYDOWN: @@ -72,13 +72,13 @@ ManualCameraController::handleInput(const SDL_Event & e, const Position &) } void -ManualCameraController::render(const UIShader &, const Position &) const +ManualCameraController::render() { } void ManualCameraController::updateCamera(Camera * camera) const { - const auto forward = glm::normalize(sincosf(direction) || -sin(pitch)); + const auto forward = glm::normalize(sincos(direction) || -sin(pitch)); camera->setView((focus || 0) - (forward * 3.F * std::pow(dist, 1.3F)), forward); } diff --git a/ui/manualCameraController.h b/ui/manualCameraController.h index 2f955e7..6501762 100644 --- a/ui/manualCameraController.h +++ b/ui/manualCameraController.h @@ -6,15 +6,14 @@ #include <glm/glm.hpp> #include <maths.h> -class UIShader; class Camera; class ManualCameraController : public CameraController, public UIComponent { public: - explicit ManualCameraController(GlobalPosition2D f) : UIComponent {{}}, focus {f} { } + explicit ManualCameraController(GlobalPosition2D f) : focus {f} { } - bool handleInput(const SDL_Event & e, const Position &) override; - void render(const UIShader &, const Position & parentPos) const override; + bool handleInput(const SDL_Event & e) override; + void render() override; void updateCamera(Camera * camera) const override; diff --git a/ui/modeHelper.h b/ui/modeHelper.h deleted file mode 100644 index d20f2db..0000000 --- a/ui/modeHelper.h +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once - -#include <memory> -union SDL_Event; - -enum ModeSecondClick { Unset, Reset, NoAction }; - -template<typename Target, ModeSecondClick msc = ModeSecondClick::Unset> class Mode { -public: - explicit Mode(Target & t) : target {t} { } - - Target & target; - - template<typename Mode, typename... Params> - auto - toggle(Params &&... params) - { - return [params..., this](const SDL_Event &) { - toggleSetMode<Mode>(std::forward<Params>(params)...); - }; - } - -private: - template<typename Mode, typename... Params> - void - toggleSetMode(Params &&... params) - { - if (dynamic_cast<Mode *>(target.get())) { - if constexpr (msc == ModeSecondClick::Unset) { - target.reset(); - } - if constexpr (msc == ModeSecondClick::Reset) { - target = std::make_unique<Mode>(std::forward<Params>(params)...); - } - } - else { - target = std::make_unique<Mode>(std::forward<Params>(params)...); - } - } -}; diff --git a/ui/queryTool.cpp b/ui/queryTool.cpp new file mode 100644 index 0000000..549bd9e --- /dev/null +++ b/ui/queryTool.cpp @@ -0,0 +1,42 @@ +#include "queryTool.h" +#include "imgui_wrap.h" +#include <game/gamestate.h> +#include <game/selectable.h> +#include <game/terrain.h> +#include <game/worldobject.h> +#include <ray.h> +#include <stream_support.h> + +QueryTool::QueryTool() : clicked {"Click something for details"} { } + +bool +QueryTool::click(const SDL_MouseButtonEvent & event, const Ray<GlobalPosition3D> & ray) +{ + if (event.button != SDL_BUTTON_LEFT) { + return false; + } + BaryPosition baryPos {}; + RelativeDistance distance {}; + + if (const auto selected = gameState->world.applyOne<Selectable>(&Selectable::intersectRay, ray, baryPos, distance); + selected != gameState->world.end()) { + const auto & ref = *selected.base()->get(); + clicked = typeid(ref).name(); + } + else if (const auto pos = gameState->terrain->intersectRay(ray)) { + clicked = streamed_string(*pos); + } + else { + clicked.clear(); + } + return true; +} + +void +QueryTool::render(bool & open) +{ + ImGui::SetNextWindowSize({-1, -1}); + ImGui::Begin("Query Tool", &open); + ImGui::TextUnformatted(clicked.c_str()); + ImGui::End(); +} diff --git a/ui/queryTool.h b/ui/queryTool.h new file mode 100644 index 0000000..74c5380 --- /dev/null +++ b/ui/queryTool.h @@ -0,0 +1,17 @@ +#pragma once + +#include "gameMainSelector.h" + +class QueryTool : public GameMainSelector::Component { +public: + QueryTool(); + +protected: + using GameMainSelector::Component::render; + + bool click(const SDL_MouseButtonEvent &, const Ray<GlobalPosition3D> &) override; + void render(bool & open) override; + +private: + std::string clicked; +}; diff --git a/ui/svgIcon.cpp b/ui/svgIcon.cpp new file mode 100644 index 0000000..499d9cc --- /dev/null +++ b/ui/svgIcon.cpp @@ -0,0 +1,34 @@ +#include "svgIcon.h" +#include "gl_traits.h" +#include <resource.h> + +SvgIcon::SvgIcon(ImageDimensions dim, const std::filesystem::path & path) +{ + const auto svgDoc = lunasvg::Document::loadFromFile(Resource::mapPath(path).native()); + if (!svgDoc) { + throw std::runtime_error("Failed to load SVG from " + path.string()); + } + + auto bitmap = svgDoc->renderToBitmap(dim.x, dim.y); + if (bitmap.isNull()) { + throw std::runtime_error("Failed to render SVG " + path.string()); + } + bitmap.convertToRGBA(); + + glBindTexture(GL_TEXTURE_2D, texture); + + glTexParameter(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + glTexParameter(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + + glTexParameter(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameter(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, dim.x, dim.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, bitmap.data()); +} + +ImTextureID +SvgIcon::operator*() const +{ + static_assert(sizeof(glTexture) <= sizeof(ImTextureID)); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast,performance-no-int-to-ptr) This is how ImGui works + return reinterpret_cast<ImTextureID>(*texture); +} diff --git a/ui/svgIcon.h b/ui/svgIcon.h new file mode 100644 index 0000000..106f97c --- /dev/null +++ b/ui/svgIcon.h @@ -0,0 +1,18 @@ +#pragma once + +#include "glArrays.h" +#include "imgui_wrap.h" +#include <config/types.h> +#include <filesystem> +#include <lunasvg.h> + +class SvgIcon { +public: + SvgIcon(ImageDimensions, const std::filesystem::path &); + + ImTextureID operator*() const; + +private: + friend class LoadFromFile; // Test case verifying size/content + glTexture texture; +}; diff --git a/ui/text.cpp b/ui/text.cpp deleted file mode 100644 index 5675061..0000000 --- a/ui/text.cpp +++ /dev/null @@ -1,66 +0,0 @@ -#include "text.h" -#include "font.h" -#include "gfx/gl/uiShader.h" -#include "gfx/gl/vertexArrayObject.h" -#include "uiComponent.h" -#include <array> -#include <collections.h> -#include <glArrays.h> -#include <glm/gtc/type_ptr.hpp> -#include <maths.h> -#include <numeric> -#include <utility> - -Text::Text(std::string_view s, const Font & font, Position pos, glm::vec3 c) : - UIComponent {pos}, colour {c}, font {font} -{ - VertexArrayObject {vao}.addAttribs<Font::Quad::value_type>(quads.bufferName(), 0); - operator=(s); -} - -Text & -Text::operator=(const std::string_view s) -{ - auto tquads = font.render(s); - models.resize(tquads.size()); - const auto glyphCount = std::accumulate(tquads.begin(), tquads.end(), size_t {}, [](auto && init, const auto & q) { - return init += q.second.size(); - }); - quads.resize(glyphCount); - GLint current = 0; - auto model = models.begin(); - auto quad = quads.begin(); - for (const auto & [texture, fquads] : tquads) { - model->first = texture; - model->second = {fquads.size() * 4, current * 4}; - current += static_cast<GLint>(fquads.size()); - model++; - quad = std::transform(fquads.begin(), fquads.end(), quad, [this](const Font::Quad & q) { - return q * [this](const glm::vec4 & corner) { - return corner + glm::vec4 {this->position.origin, 0, 0}; - }; - }); - } - quads.unmap(); - return *this; -} - -void -Text::render(const UIShader & shader, const Position &) const -{ - shader.text.use(colour); - glActiveTexture(GL_TEXTURE0); - glBindVertexArray(vao); - for (const auto & m : models) { - glBindTexture(GL_TEXTURE_2D, m.first); - glDrawArrays(GL_QUADS, m.second.second, m.second.first); - } - glBindVertexArray(0); - glBindTexture(GL_TEXTURE_2D, 0); -} - -bool -Text::handleInput(const SDL_Event &, const Position &) -{ - return false; -} diff --git a/ui/text.h b/ui/text.h deleted file mode 100644 index 31ed9a5..0000000 --- a/ui/text.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include "font.h" -#include "glContainer.h" -#include "uiComponent.h" -#include <glArrays.h> -#include <glad/gl.h> -#include <glm/glm.hpp> -#include <string_view> - -class UIShader; -union SDL_Event; - -class Text : public UIComponent { -public: - Text(std::string_view s, const Font &, Position, glm::vec3 colour); - - void render(const UIShader &, const Position & parentPos) const override; - bool handleInput(const SDL_Event &, const Position & parentPos) override; - - Text & operator=(const std::string_view s); - -private: - std::vector<std::pair<GLuint, std::pair<GLsizei, GLint>>> models; - glContainer<Font::Quad> quads; - glVertexArray vao; - glm::vec3 colour; - const Font & font; -}; diff --git a/ui/toolbar.cpp b/ui/toolbar.cpp deleted file mode 100644 index 31d87dc..0000000 --- a/ui/toolbar.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include "toolbar.h" -#include "gfx/gl/uiShader.h" -#include "ui/iconButton.h" -#include "ui/uiComponent.h" -#include "uiComponentPlacer.h" -#include <SDL2/SDL.h> -#include <glm/glm.hpp> - -Toolbar::Toolbar(const std::initializer_list<InitInfo> & initInfo) : UIComponent {{{}, {}}} -{ - UIComponentPlacer placer {{10, 10}, 5, 1}; - for (const auto & ii : initInfo) { - icons.create(ii.first, placer.next(ICON_SIZE), ii.second); - } - this->position.size = placer.getLimit(); -} - -void -Toolbar::render(const UIShader & uiShader, const Position & parentPos) const -{ - uiShader.icon.use(); - const auto absPos = this->position + parentPos; - icons.apply(&UIComponent::render, uiShader, absPos); -} - -bool -Toolbar::handleInput(const SDL_Event & e, const Position & parentPos) -{ - const auto absPos = this->position + parentPos; - if (absPos & e.button) { - icons.applyOne(&UIComponent::handleInput, e, absPos); - return true; - } - return false; -} diff --git a/ui/toolbar.h b/ui/toolbar.h deleted file mode 100644 index ea560f5..0000000 --- a/ui/toolbar.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include "collection.h" -#include "iconButton.h" -#include "uiComponent.h" -#include <initializer_list> -#include <string> -#include <utility> - -class UIShader; -union SDL_Event; - -class Toolbar : public UIComponent { -public: - using InitInfo = std::pair<std::string, UIEvent>; - explicit Toolbar(const std::initializer_list<InitInfo> & initInfo); - - void render(const UIShader & uiShader, const Position & parentPos) const override; - - bool handleInput(const SDL_Event & e, const Position & parentPos) override; - - Collection<IconButton, false> icons; -}; diff --git a/ui/uiComponent.cpp b/ui/uiComponent.cpp deleted file mode 100644 index aa4838d..0000000 --- a/ui/uiComponent.cpp +++ /dev/null @@ -1,33 +0,0 @@ -#include "uiComponent.h" -#include <SDL2/SDL.h> - -UIComponent::UIComponent(Position position) : position {position} { } - -UIComponent::Position -UIComponent::Position::operator+(const Position & parentPos) const -{ - return *this + parentPos.origin; -} - -UIComponent::Position -UIComponent::Position::operator+(const glm::vec2 & parentPos) const -{ - return {origin + parentPos, size}; -} - -bool -UIComponent::Position::operator&(const glm::vec2 & pos) const -{ - return (pos.x >= origin.x && pos.y >= origin.y && pos.x < origin.x + size.x && pos.y < origin.y + size.y); -} - -bool -UIComponent::Position::operator&(const SDL_MouseButtonEvent & pos) const -{ - switch (pos.type) { - case SDL_MOUSEBUTTONUP: - case SDL_MOUSEBUTTONDOWN: - return *this & glm::vec2 {pos.x, pos.y}; - } - return false; -} diff --git a/ui/uiComponent.h b/ui/uiComponent.h index 71d2659..b2c1a8f 100644 --- a/ui/uiComponent.h +++ b/ui/uiComponent.h @@ -1,32 +1,18 @@ #pragma once -#include <functional> #include <glm/glm.hpp> #include <special_members.h> -class UIShader; union SDL_Event; -struct SDL_MouseButtonEvent; -using UIEvent = std::function<void(const SDL_Event &)>; class UIComponent { public: - struct Position { - glm::vec2 origin, size; - Position operator+(const Position &) const; - Position operator+(const glm::vec2 &) const; - bool operator&(const SDL_MouseButtonEvent &) const; - bool operator&(const glm::vec2 &) const; - }; - - explicit UIComponent(Position); + UIComponent() = default; virtual ~UIComponent() = default; NO_MOVE(UIComponent); NO_COPY(UIComponent); - virtual void render(const UIShader &, const Position & parentPos) const = 0; - virtual bool handleInput(const SDL_Event &, const Position & parentPos) = 0; - - Position position; + virtual void render() = 0; + virtual bool handleInput(const SDL_Event &) = 0; }; diff --git a/ui/uiComponentPlacer.cpp b/ui/uiComponentPlacer.cpp deleted file mode 100644 index 5e645d8..0000000 --- a/ui/uiComponentPlacer.cpp +++ /dev/null @@ -1,27 +0,0 @@ -#include "uiComponentPlacer.h" -#include <algorithm> - -UIComponentPlacer::UIComponentPlacer(glm::vec2 padding, float spacing, glm::length_t axis) : - padding {padding}, spacing {spacing}, axis {axis}, current {padding[axis]} -{ -} - -glm::vec2 -UIComponentPlacer::next(glm::vec2 size) -{ - glm::vec2 n {}; - n[axis] = current; - n[1 - axis] = padding[1 - axis]; - current += spacing + size[axis]; - max = std::max(max, size[1 - axis]); - return n; -} - -glm::vec2 -UIComponentPlacer::getLimit() const -{ - glm::vec2 n {}; - n[axis] = current + padding[axis]; - n[1 - axis] = max + padding[1 - axis]; - return n; -} diff --git a/ui/uiComponentPlacer.h b/ui/uiComponentPlacer.h deleted file mode 100644 index 1e64f78..0000000 --- a/ui/uiComponentPlacer.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include <glm/glm.hpp> - -class UIComponentPlacer { -public: - UIComponentPlacer(glm::vec2 padding, float spacing, glm::length_t axis = 0); - - glm::vec2 next(glm::vec2 size); - glm::vec2 getLimit() const; - -private: - const glm::vec2 padding; - const float spacing; - const glm::length_t axis; - - float current {}; - float max {}; -}; diff --git a/ui/window.cpp b/ui/window.cpp index dd488d7..06857b2 100644 --- a/ui/window.cpp +++ b/ui/window.cpp @@ -1,28 +1,16 @@ #include "window.h" -#include "uiComponent.h" -#include <format> #include <glad/gl.h> #include <glm/glm.hpp> -#include <stdexcept> +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wold-style-cast" +#include "backends/imgui_impl_opengl3.h" +#include "backends/imgui_impl_sdl2.h" +#pragma GCC diagnostic pop -Window::GLInitHelper::GLInitHelper() -{ - [[maybe_unused]] static auto init = []() { - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) - if (const auto version = gladLoadGL(reinterpret_cast<GLADloadfunc>(SDL_GL_GetProcAddress)); version < 30003) { - throw std::runtime_error {std::format("Insufficient OpenGL version: {}", version)}; - } - else { - return version; - } - }(); -} - -Window::Window(size_t width, size_t height, const std::string & title, Uint32 flags) : - size {static_cast<int>(width), static_cast<int>(height)}, - m_window {title.c_str(), static_cast<int>(SDL_WINDOWPOS_CENTERED), static_cast<int>(SDL_WINDOWPOS_CENTERED), size.x, - size.y, flags}, - glContext {m_window}, uiShader {width, height} +Window::Window(ScreenAbsCoord size, const char * title, Uint32 flags) : + m_window {title, static_cast<int>(SDL_WINDOWPOS_CENTERED), static_cast<int>(SDL_WINDOWPOS_CENTERED), size.x, size.y, + flags}, + glContext {m_window} { } @@ -39,23 +27,21 @@ Window::swapBuffers() const SDL_GL_SwapWindow(m_window); } +void +Window::tick(TickDuration elapsed) +{ + if (content) { + content->tick(elapsed); + } +} + bool Window::handleInput(const SDL_Event & e) { if (SDL_GetWindowID(m_window) == e.window.windowID) { - SDL_Event eAdjusted {e}; - switch (e.type) { - // SDL and OpenGL have coordinates that are vertically opposed. - case SDL_MOUSEBUTTONDOWN: - case SDL_MOUSEBUTTONUP: - eAdjusted.button.y = size.y - e.button.y; - break; - case SDL_MOUSEMOTION: - eAdjusted.motion.y = size.y - e.motion.y; - break; + if (content) { + return content->handleInput(e); } - uiComponents.rapplyOne(&UIComponent::handleInput, eAdjusted, UIComponent::Position {{}, size}); - return true; } return false; } @@ -66,16 +52,15 @@ Window::refresh() const SDL_GL_MakeCurrent(m_window, glContext); clear(0.0F, 0.0F, 0.0F, 1.0F); - render(); + ImGui_ImplOpenGL3_NewFrame(); + ImGui_ImplSDL2_NewFrame(); + ImGui::NewFrame(); + if (content) { + content->render(); + // Render UI stuff here + } + ImGui::Render(); + ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); swapBuffers(); } - -void -Window::render() const -{ - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glDisable(GL_DEPTH_TEST); - uiComponents.apply(&UIComponent::render, uiShader, UIComponent::Position {}); -} diff --git a/ui/window.h b/ui/window.h index 8f2b70b..99a977f 100644 --- a/ui/window.h +++ b/ui/window.h @@ -1,13 +1,12 @@ #pragma once #include "chronology.h" -#include "collection.h" -#include "gfx/gl/uiShader.h" +#include "config/types.h" #include "ptr.h" -#include "uiComponent.h" // IWYU pragma: keep +#include "special_members.h" +#include "windowContent.h" #include <SDL2/SDL.h> #include <cstddef> -#include <special_members.h> #include <string> using SDL_WindowPtr = wrapped_ptrt<SDL_Window, SDL_CreateWindow, SDL_DestroyWindow>; @@ -16,30 +15,31 @@ using SDL_GLContextPtr = wrapped_ptrt<GL_Context, SDL_GL_CreateContext, SDL_GL_D class Window { public: - Window(size_t width, size_t height, const std::string & title, Uint32 flags); + Window(ScreenAbsCoord size, const char * title, Uint32 flags); virtual ~Window() = default; NO_COPY(Window); NO_MOVE(Window); - virtual void tick(TickDuration elapsed) = 0; + template<typename C, typename... P> + void + setContent(P &&... p) + { + glm::ivec2 size {}; + SDL_GetWindowSizeInPixels(m_window, &size.x, &size.y); + content = std::make_unique<C>(ScreenAbsCoord {size.x, size.y}, std::forward<P>(p)...); + } + + void tick(TickDuration elapsed); void refresh() const; bool handleInput(const SDL_Event & e); - void clear(float r, float g, float b, float a) const; void swapBuffers() const; protected: - virtual void render() const; - - struct GLInitHelper { - GLInitHelper(); - }; + void clear(float r, float g, float b, float a) const; - const ScreenAbsCoord size; SDL_WindowPtr m_window; SDL_GLContextPtr glContext; - GLInitHelper glInithelper; - Collection<UIComponent> uiComponents; - UIShader uiShader; + WindowContent::Ptr content; }; diff --git a/ui/windowContent.cpp b/ui/windowContent.cpp new file mode 100644 index 0000000..0f6dc04 --- /dev/null +++ b/ui/windowContent.cpp @@ -0,0 +1,30 @@ +#include "windowContent.h" +#include "SDL_events.h" + +void +WindowContent::tick(TickDuration) +{ +} + +bool +WindowContent::handleInput(const SDL_Event & e) +{ + SDL_Event eAdjusted {e}; + const auto size = [&e] { + glm::ivec2 size {}; + SDL_GetWindowSizeInPixels(SDL_GetWindowFromID(e.window.windowID), &size.x, &size.y); + return size; + }(); + switch (e.type) { + // SDL and OpenGL have coordinates that are vertically opposed. + case SDL_MOUSEBUTTONDOWN: + case SDL_MOUSEBUTTONUP: + eAdjusted.button.y = size.y - e.button.y; + break; + case SDL_MOUSEMOTION: + eAdjusted.motion.y = size.y - e.motion.y; + break; + } + uiComponents.rapplyOne(&UIComponent::handleInput, eAdjusted); + return true; +} diff --git a/ui/windowContent.h b/ui/windowContent.h new file mode 100644 index 0000000..34cbea3 --- /dev/null +++ b/ui/windowContent.h @@ -0,0 +1,22 @@ +#pragma once + +#include "chronology.h" +#include "collection.h" +#include "special_members.h" +#include "stdTypeDefs.h" +#include "uiComponent.h" // IWYU pragma: keep + +class WindowContent : public StdTypeDefs<WindowContent> { +public: + WindowContent() = default; + virtual ~WindowContent() = default; + NO_MOVE(WindowContent); + NO_COPY(WindowContent); + + virtual void tick(TickDuration); + virtual void render() = 0; + virtual bool handleInput(const SDL_Event & e); + +protected: + UniqueCollection<UIComponent> uiComponents; +}; diff --git a/ui/worldOverlay.h b/ui/worldOverlay.h index 18fab3f..a0f3b65 100644 --- a/ui/worldOverlay.h +++ b/ui/worldOverlay.h @@ -1,9 +1,10 @@ #pragma once class SceneShader; +class Frustum; class WorldOverlay { public: virtual ~WorldOverlay() = default; - virtual void render(const SceneShader &) const = 0; + virtual void render(const SceneShader &, const Frustum &) const = 0; }; |