summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--application/main.cpp1
-rw-r--r--res/ui/icon/network.pngbin0 -> 16793 bytes
-rw-r--r--ui/gameMainWindow.cpp25
-rw-r--r--ui/icon.cpp33
-rw-r--r--ui/icon.h28
-rw-r--r--ui/iconButton.cpp59
-rw-r--r--ui/iconButton.h27
-rw-r--r--ui/inputHandler.h18
-rw-r--r--ui/manualCameraController.cpp7
-rw-r--r--ui/manualCameraController.h10
-rw-r--r--ui/toolbar.cpp33
-rw-r--r--ui/toolbar.h26
-rw-r--r--ui/uiComponent.cpp27
-rw-r--r--ui/uiComponent.h24
-rw-r--r--ui/uiComponentPlacer.cpp26
-rw-r--r--ui/uiComponentPlacer.h22
-rw-r--r--ui/window.cpp21
-rw-r--r--ui/window.h8
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
new file mode 100644
index 0000000..7a091f3
--- /dev/null
+++ b/res/ui/icon/network.png
Binary files 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 <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;
};