diff options
author | Dan Goodliffe <dan@randomdan.homeip.net> | 2021-12-22 12:16:38 +0000 |
---|---|---|
committer | Dan Goodliffe <dan@randomdan.homeip.net> | 2021-12-22 12:16:38 +0000 |
commit | e806d41c8703ddc4bcaf2186d0c1701bd1e1ada3 (patch) | |
tree | 1612baf22456c0b5a1bef82980177afb34b2756c | |
parent | Window handles UIComponent rendering (diff) | |
download | ilt-e806d41c8703ddc4bcaf2186d0c1701bd1e1ada3.tar.bz2 ilt-e806d41c8703ddc4bcaf2186d0c1701bd1e1ada3.tar.xz ilt-e806d41c8703ddc4bcaf2186d0c1701bd1e1ada3.zip |
Initial commit with some basic UI
-rw-r--r-- | application/main.cpp | 1 | ||||
-rw-r--r-- | res/ui/icon/network.png | bin | 0 -> 16793 bytes | |||
-rw-r--r-- | ui/gameMainWindow.cpp | 25 | ||||
-rw-r--r-- | ui/icon.cpp | 33 | ||||
-rw-r--r-- | ui/icon.h | 28 | ||||
-rw-r--r-- | ui/iconButton.cpp | 59 | ||||
-rw-r--r-- | ui/iconButton.h | 27 | ||||
-rw-r--r-- | ui/inputHandler.h | 18 | ||||
-rw-r--r-- | ui/manualCameraController.cpp | 7 | ||||
-rw-r--r-- | ui/manualCameraController.h | 10 | ||||
-rw-r--r-- | ui/toolbar.cpp | 33 | ||||
-rw-r--r-- | ui/toolbar.h | 26 | ||||
-rw-r--r-- | ui/uiComponent.cpp | 27 | ||||
-rw-r--r-- | ui/uiComponent.h | 24 | ||||
-rw-r--r-- | ui/uiComponentPlacer.cpp | 26 | ||||
-rw-r--r-- | ui/uiComponentPlacer.h | 22 | ||||
-rw-r--r-- | ui/window.cpp | 21 | ||||
-rw-r--r-- | ui/window.h | 8 |
18 files changed, 356 insertions, 39 deletions
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 <memory> #include <special_members.hpp> #include <ui/gameMainWindow.h> -#include <ui/inputHandler.h> #include <ui/window.h> #include <vector> diff --git a/res/ui/icon/network.png b/res/ui/icon/network.png Binary files differnew file mode 100644 index 0000000..7a091f3 --- /dev/null +++ b/res/ui/icon/network.png 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 <GL/glew.h> #include <SDL2/SDL.h> +#include <collection.hpp> #include <game/gamestate.h> #include <game/worldobject.h> // IWYU pragma: keep #include <gfx/renderable.h> #include <glm/glm.hpp> -#include <maths.h> #include <memory> +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<ManualCameraController>(glm::vec2 {-1150, -1150}); + uiComponents.create<GameMainToolbar>(); + uiComponents.create<ManualCameraController>(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>(&CameraController::updateCamera, &camera); + uiComponents.apply<CameraController>(&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 <gfx/image.h>
+#include <resource.h>
+#include <stb/stb_image.h>
+
+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<GLsizei>(tex.width), static_cast<GLsizei>(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 <GL/glew.h>
+#include <filesystem>
+#include <glm/glm.hpp>
+#include <special_members.hpp>
+
+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 <SDL2/SDL.h> +#include <array> +#include <filesystem> +#include <functional> +#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_)}, 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<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 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 <GL/glew.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; + 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 <special_members.hpp> - -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 <maths.h> bool -ManualCameraController::handleInput(const SDL_Event & e) +ManualCameraController::handleInput(const SDL_Event & e, const Position &) { switch (e.type) { case SDL_KEYDOWN: @@ -59,6 +59,11 @@ ManualCameraController::handleInput(const SDL_Event & e) } void +ManualCameraController::render(const UIShader &, const Position &) const +{ +} + +void ManualCameraController::updateCamera(Camera * camera) const { camera->forward = glm::normalize(sincosf(direction) ^ -sin(pitch)); 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 <SDL2/SDL.h> #include <gfx/camera_controller.h> #include <glm/glm.hpp> #include <maths.h> +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 <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 +{ + 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 <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; +}; + +#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 <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 +{ + 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 <functional> +#include <glm/glm.hpp> +#include <special_members.hpp> + 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); 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 <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 new file mode 100644 index 0000000..b68c4d8 --- /dev/null +++ b/ui/uiComponentPlacer.h @@ -0,0 +1,22 @@ +#ifndef UICOMPONENTPLACER_H +#define UICOMPONENTPLACER_H + +#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 {}; +}; + +#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 <GL/glew.h>
-#include <optional>
+#include <glm/glm.hpp>
#include <stdexcept>
+#include <tuple>
+#include <type_traits>
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<int>(width), static_cast<int>(height), static_cast<Uint32>(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>(&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 <SDL2/SDL.h>
+#include <cstddef>
#include <special_members.hpp>
#include <string>
-#include <type_traits>
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_Window, SDL_CreateWindow, SDL_DestroyWindow>;
SDL_WindowPtr m_window;
- Collection<InputHandler> inputStack;
+ Collection<UIComponent> uiComponents;
UIShader uiShader;
};
|