From e806d41c8703ddc4bcaf2186d0c1701bd1e1ada3 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Wed, 22 Dec 2021 12:16:38 +0000 Subject: Initial commit with some basic UI --- application/main.cpp | 1 - res/ui/icon/network.png | Bin 0 -> 16793 bytes ui/gameMainWindow.cpp | 25 ++++++++++++++---- ui/icon.cpp | 33 +++++++++++++++++++++++ ui/icon.h | 28 ++++++++++++++++++++ ui/iconButton.cpp | 59 ++++++++++++++++++++++++++++++++++++++++++ ui/iconButton.h | 27 +++++++++++++++++++ ui/inputHandler.h | 18 ------------- ui/manualCameraController.cpp | 7 ++++- ui/manualCameraController.h | 10 ++++--- ui/toolbar.cpp | 33 +++++++++++++++++++++++ ui/toolbar.h | 26 +++++++++++++++++++ ui/uiComponent.cpp | 27 +++++++++++++++++++ ui/uiComponent.h | 24 ++++++++++++++++- ui/uiComponentPlacer.cpp | 26 +++++++++++++++++++ ui/uiComponentPlacer.h | 22 ++++++++++++++++ ui/window.cpp | 21 +++++++++++---- ui/window.h | 8 +++--- 18 files changed, 356 insertions(+), 39 deletions(-) create mode 100644 res/ui/icon/network.png create mode 100644 ui/icon.cpp create mode 100644 ui/icon.h create mode 100644 ui/iconButton.cpp create mode 100644 ui/iconButton.h delete mode 100644 ui/inputHandler.h create mode 100644 ui/toolbar.cpp create mode 100644 ui/toolbar.h create mode 100644 ui/uiComponent.cpp create mode 100644 ui/uiComponentPlacer.cpp create mode 100644 ui/uiComponentPlacer.h diff --git a/application/main.cpp b/application/main.cpp index 68bc191..2053385 100644 --- a/application/main.cpp +++ b/application/main.cpp @@ -21,7 +21,6 @@ #include #include #include -#include #include #include diff --git a/res/ui/icon/network.png b/res/ui/icon/network.png new file mode 100644 index 0000000..7a091f3 Binary files /dev/null and b/res/ui/icon/network.png differ diff --git a/ui/gameMainWindow.cpp b/ui/gameMainWindow.cpp index cd9d3e4..e1d9763 100644 --- a/ui/gameMainWindow.cpp +++ b/ui/gameMainWindow.cpp @@ -1,21 +1,36 @@ #include "gameMainWindow.h" -#include "collection.hpp" #include "gfx/camera_controller.h" #include "manualCameraController.h" -#include "ui/window.h" +#include "maths.h" +#include "toolbar.h" +#include "window.h" #include #include +#include #include #include // IWYU pragma: keep #include #include -#include #include +class GameMainToolbar : public Toolbar { +public: + GameMainToolbar() : + Toolbar { + {"ui/icon/network.png", + [](const SDL_Event &) { + // fprintf(stderr, "network click\n"); + }}, + } + { + } +}; + GameMainWindow::GameMainWindow(size_t w, size_t h) : Window {w, h, "I Like Trains"}, camera {{-1250.0F, -1250.0F, 35.0F}, 70.0F, rdiv(w, h), 0.1F, 10000.0F} { - inputStack.create(glm::vec2 {-1150, -1150}); + uiComponents.create(); + uiComponents.create(glm::vec2 {-1150, -1150}); shader.setUniform("lightDirection", glm::normalize(glm::vec3 {1, 0, -1})); shader.setUniform("lightColor", {.6, .6, .6}); @@ -25,7 +40,7 @@ GameMainWindow::GameMainWindow(size_t w, size_t h) : void GameMainWindow::tick(TickDuration) { - inputStack.apply(&CameraController::updateCamera, &camera); + uiComponents.apply(&CameraController::updateCamera, &camera); shader.setView(camera.GetViewProjection()); } diff --git a/ui/icon.cpp b/ui/icon.cpp new file mode 100644 index 0000000..371410a --- /dev/null +++ b/ui/icon.cpp @@ -0,0 +1,33 @@ +#include "icon.h" +#include +#include +#include + +Icon::Icon(const std::filesystem::path & fileName) : Icon {Image {Resource::mapPath(fileName).c_str(), STBI_rgb_alpha}} +{ +} + +Icon::Icon(const Image & tex) : size {tex.width, tex.height}, m_texture {} +{ + glGenTextures(1, &m_texture); + glBindTexture(GL_TEXTURE_2D, m_texture); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, static_cast(tex.width), static_cast(tex.height), 0, + GL_RGBA, GL_UNSIGNED_BYTE, tex.data.data()); +} + +Icon::~Icon() +{ + glDeleteTextures(1, &m_texture); +} + +void +Icon::Bind() const +{ + glBindTexture(GL_TEXTURE_2D, m_texture); +} diff --git a/ui/icon.h b/ui/icon.h new file mode 100644 index 0000000..cc6df87 --- /dev/null +++ b/ui/icon.h @@ -0,0 +1,28 @@ +#ifndef ICON_H +#define ICON_H + +#include +#include +#include +#include + +class Image; + +class Icon { +public: + explicit Icon(const std::filesystem::path & fileName); + explicit Icon(const Image & image); + + virtual ~Icon(); + + NO_COPY(Icon); + NO_MOVE(Icon); + + void Bind() const; + const glm::vec2 size; + +private: + GLuint m_texture; +}; + +#endif diff --git a/ui/iconButton.cpp b/ui/iconButton.cpp new file mode 100644 index 0000000..c35303f --- /dev/null +++ b/ui/iconButton.cpp @@ -0,0 +1,59 @@ +#include "iconButton.h" +#include "ui/icon.h" +#include "ui/uiComponent.h" +#include +#include +#include +#include +#include +#include + +IconButton::IconButton(const std::string & icon_, glm::vec2 position_, UIEvent click_) : + UIComponent {{position_, ICON_SIZE}}, icon {icon_}, click {std::move(click_)}, m_vertexArrayObject {}, + m_vertexArrayBuffer {} +{ + glGenVertexArrays(1, &m_vertexArrayObject); + glBindVertexArray(m_vertexArrayObject); + + glGenBuffers(1, &m_vertexArrayBuffer); + + glBindBuffer(GL_ARRAY_BUFFER, m_vertexArrayBuffer); + glBufferData(GL_ARRAY_BUFFER, static_cast(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 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 new file mode 100644 index 0000000..ec3f357 --- /dev/null +++ b/ui/iconButton.h @@ -0,0 +1,27 @@ +#ifndef ICONBUTTON_H +#define ICONBUTTON_H + +#include "icon.h" +#include "uiComponent.h" +#include +#include +#include + +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; + GLuint m_vertexArrayObject, m_vertexArrayBuffer; +}; + +#endif diff --git a/ui/inputHandler.h b/ui/inputHandler.h deleted file mode 100644 index aac323b..0000000 --- a/ui/inputHandler.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef INPUT_HANDLER_H -#define INPUT_HANDLER_H - -#include - -union SDL_Event; - -class InputHandler { -public: - InputHandler() = default; - virtual ~InputHandler() = default; - - DEFAULT_MOVE_COPY(InputHandler); - - virtual bool handleInput(const SDL_Event &) = 0; -}; - -#endif diff --git a/ui/manualCameraController.cpp b/ui/manualCameraController.cpp index 1d3a067..568c547 100644 --- a/ui/manualCameraController.cpp +++ b/ui/manualCameraController.cpp @@ -5,7 +5,7 @@ #include bool -ManualCameraController::handleInput(const SDL_Event & e) +ManualCameraController::handleInput(const SDL_Event & e, const Position &) { switch (e.type) { case SDL_KEYDOWN: @@ -58,6 +58,11 @@ ManualCameraController::handleInput(const SDL_Event & e) return false; } +void +ManualCameraController::render(const UIShader &, const Position &) const +{ +} + void ManualCameraController::updateCamera(Camera * camera) const { diff --git a/ui/manualCameraController.h b/ui/manualCameraController.h index ceff9ac..5ed45f5 100644 --- a/ui/manualCameraController.h +++ b/ui/manualCameraController.h @@ -1,19 +1,21 @@ #ifndef MANUAL_CAMERA_CONTROLLER_H #define MANUAL_CAMERA_CONTROLLER_H -#include "inputHandler.h" +#include "uiComponent.h" #include #include #include #include +class UIShader; class Camera; -class ManualCameraController : public CameraController, public InputHandler { +class ManualCameraController : public CameraController, public UIComponent { public: - explicit ManualCameraController(glm::vec2 f) : focus {f} { } + explicit ManualCameraController(glm::vec2 f) : UIComponent {{}}, focus {f} { } - bool handleInput(const SDL_Event & e) override; + bool handleInput(const SDL_Event & e, const Position &) override; + void render(const UIShader &, const Position & parentPos) const override; void updateCamera(Camera * camera) const override; diff --git a/ui/toolbar.cpp b/ui/toolbar.cpp new file mode 100644 index 0000000..c8febce --- /dev/null +++ b/ui/toolbar.cpp @@ -0,0 +1,33 @@ +#include "toolbar.h" +#include "ui/iconButton.h" +#include "ui/uiComponent.h" +#include "uiComponentPlacer.h" +#include +#include + +Toolbar::Toolbar(const std::initializer_list & 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 +{ + 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 new file mode 100644 index 0000000..28aba6c --- /dev/null +++ b/ui/toolbar.h @@ -0,0 +1,26 @@ +#ifndef TOOLBAR_H +#define TOOLBAR_H + +#include "collection.hpp" +#include "iconButton.h" +#include "uiComponent.h" +#include +#include +#include + +class UIShader; +union SDL_Event; + +class Toolbar : public UIComponent { +public: + using InitInfo = std::pair; + explicit Toolbar(const std::initializer_list & initInfo); + + void render(const UIShader & uiShader, const Position & parentPos) const override; + + bool handleInput(const SDL_Event & e, const Position & parentPos) override; + + Collection icons; +}; + +#endif diff --git a/ui/uiComponent.cpp b/ui/uiComponent.cpp new file mode 100644 index 0000000..ef62db9 --- /dev/null +++ b/ui/uiComponent.cpp @@ -0,0 +1,27 @@ +#include "uiComponent.h" +#include + +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 +{ + return *this & glm::vec2 {pos.x, pos.y}; +} diff --git a/ui/uiComponent.h b/ui/uiComponent.h index cd21de3..e7b3a0b 100644 --- a/ui/uiComponent.h +++ b/ui/uiComponent.h @@ -1,12 +1,34 @@ #ifndef UICOMPONENT_H #define UICOMPONENT_H +#include +#include +#include + class UIShader; +union SDL_Event; +struct SDL_MouseButtonEvent; +using UIEvent = std::function; 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); virtual ~UIComponent() = default; - virtual void render(const UIShader &) const = 0; + + 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; }; #endif diff --git a/ui/uiComponentPlacer.cpp b/ui/uiComponentPlacer.cpp new file mode 100644 index 0000000..368e772 --- /dev/null +++ b/ui/uiComponentPlacer.cpp @@ -0,0 +1,26 @@ +#include "uiComponentPlacer.h" +#include + +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 new file mode 100644 index 0000000..b68c4d8 --- /dev/null +++ b/ui/uiComponentPlacer.h @@ -0,0 +1,22 @@ +#ifndef UICOMPONENTPLACER_H +#define UICOMPONENTPLACER_H + +#include + +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 {}; +}; + +#endif diff --git a/ui/window.cpp b/ui/window.cpp index cd4a202..13c7d95 100644 --- a/ui/window.cpp +++ b/ui/window.cpp @@ -1,9 +1,10 @@ #include "window.h" -#include "inputHandler.h" #include "uiComponent.h" #include -#include +#include #include +#include +#include static SDL_GLContext SDL_GL_CreateContextAndGlewInit(SDL_Window * w) @@ -27,7 +28,7 @@ Window::Window(size_t width, size_t height, const std::string & title) : static_cast(width), static_cast(height), static_cast(SDL_WINDOW_OPENGL)}, uiShader {[this](auto w) { // must call glContent before creating the shader - glContext(); + std::ignore = glContext(); return w; }(width), height} @@ -51,7 +52,17 @@ bool Window::handleInput(const SDL_Event & e) { if (SDL_GetWindowID(m_window) == e.window.windowID) { - inputStack.applyOne(&InputHandler::handleInput, e); + SDL_Event eAdjusted {e}; + glm::ivec2 size {}; + switch (e.type) { + // SDL and OpenGL have coordinates that are vertically opposed. + case SDL_MOUSEBUTTONDOWN: + case SDL_MOUSEBUTTONUP: + SDL_GetWindowSize(m_window, &size.x, &size.y); + eAdjusted.button.y = size.y - e.button.y; + break; + } + uiComponents.applyOne(&UIComponent::handleInput, eAdjusted, UIComponent::Position {{}, size}); return true; } return false; @@ -80,5 +91,5 @@ Window::render(const GameState *) const { uiShader.use(); glDisable(GL_DEPTH_TEST); - inputStack.apply(&UIComponent::render, uiShader); + uiComponents.apply(&UIComponent::render, uiShader, UIComponent::Position {}); } diff --git a/ui/window.h b/ui/window.h index 9c6d023..25443bd 100644 --- a/ui/window.h +++ b/ui/window.h @@ -4,12 +4,12 @@ #include "chronology.hpp" #include "collection.hpp" #include "gfx/gl/uiShader.h" -#include "inputHandler.h" // IWYU pragma: keep #include "ptr.hpp" +#include "uiComponent.h" // IWYU pragma: keep #include +#include #include #include -#include class GameState; @@ -29,12 +29,12 @@ public: void swapBuffers() const; protected: - SDL_GLContext glContext() const; + [[nodiscard]] SDL_GLContext glContext() const; virtual void render(const GameState *) const; using SDL_WindowPtr = wrapped_ptrt; SDL_WindowPtr m_window; - Collection inputStack; + Collection uiComponents; UIShader uiShader; }; -- cgit v1.2.3