From de9909df038d1b6bb47bcc82fea5f8dc9dec2491 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Tue, 28 Dec 2021 15:42:50 +0000 Subject: Initial implementation for being able to click in the main window to select something --- application/main.cpp | 2 +- game/selectable.h | 16 +++++++++++++++ game/vehicles/railVehicle.cpp | 44 ++++++++++++++++++++++++++++++++++++++++++ game/vehicles/railVehicle.h | 5 ++++- game/vehicles/train.cpp | 6 ++++++ game/vehicles/train.h | 3 +++ game/vehicles/vehicle.h | 3 ++- gfx/gl/camera.cpp | 7 +++++++ gfx/gl/camera.h | 1 + iwyu.json | 8 ++++++++ lib/maths.cpp | 6 ++++++ lib/maths.h | 7 +++++++ test/test-maths.cpp | 36 ++++++++++++++++++++++++++++++++++ ui/gameMainWindow.cpp | 45 +++++++++++++++++++++++++++++++++++++++++-- ui/gameMainWindow.h | 2 +- ui/manualCameraController.cpp | 12 ++++++++++++ 16 files changed, 197 insertions(+), 6 deletions(-) create mode 100644 game/selectable.h diff --git a/application/main.cpp b/application/main.cpp index 2053385..128c9c1 100644 --- a/application/main.cpp +++ b/application/main.cpp @@ -55,7 +55,7 @@ public: run() { Windows windows; - windows.create(DISPLAY_WIDTH, DISPLAY_HEIGHT); + windows.create(DISPLAY_WIDTH, DISPLAY_HEIGHT, this); world.create(); diff --git a/game/selectable.h b/game/selectable.h new file mode 100644 index 0000000..c16246c --- /dev/null +++ b/game/selectable.h @@ -0,0 +1,16 @@ +#ifndef SELECTABLE_H +#define SELECTABLE_H + +#include +#include + +class Selectable { +public: + Selectable() = default; + virtual ~Selectable() = default; + DEFAULT_MOVE_COPY(Selectable); + + [[nodiscard]] virtual bool intersectRay(const glm::vec3 &, const glm::vec3 &, glm::vec2 *, float *) const = 0; +}; + +#endif diff --git a/game/vehicles/railVehicle.cpp b/game/vehicles/railVehicle.cpp index aea7f8c..8d7b06f 100644 --- a/game/vehicles/railVehicle.cpp +++ b/game/vehicles/railVehicle.cpp @@ -1,8 +1,11 @@ #include "railVehicle.h" #include "railVehicleClass.h" #include "train.h" +#include #include #include +#include +#include #include #include #include @@ -24,3 +27,44 @@ RailVehicle::move(const Train * t, float & trailBy) location.rot = {vector_pitch(diff), vector_yaw(diff), 0}; trailBy += 0.6F + overhang; } + +bool +RailVehicle::intersectRay(const glm::vec3 & pos, const glm::vec3 & dir, glm::vec2 * baryPos, float * eh) const +{ + constexpr const auto X = 1.35F; + const auto Y = this->rvClass->length / 2.F; + constexpr const auto Z = 3.9F; + const auto move = glm::translate(location.pos) * rotate_ypr(location.rot); + const std::array cornerVertices {{ + move * glm::vec4 {-X, Y, 0, 1}, // LFB + move * glm::vec4 {X, Y, 0, 1}, // RFB + move * glm::vec4 {-X, Y, Z, 1}, // LFT + move * glm::vec4 {X, Y, Z, 1}, // RFT + move * glm::vec4 {-X, -Y, 0, 1}, // LBB + move * glm::vec4 {X, -Y, 0, 1}, // RBB + move * glm::vec4 {-X, -Y, Z, 1}, // LBT + move * glm::vec4 {X, -Y, Z, 1}, // RBT + }}; + static constexpr const std::array triangles {{ + // Front + {0, 1, 2}, + {1, 2, 3}, + // Left + {0, 2, 4}, + {2, 4, 6}, + // Back + {4, 5, 6}, + {5, 6, 7}, + // Right + {1, 3, 5}, + {3, 5, 7}, + // Top + {2, 3, 6}, + {3, 6, 7}, + }}; + return std::any_of( + triangles.begin(), triangles.end(), [&cornerVertices, &pos, &dir, &baryPos, &eh](const glm::uvec3 idx) { + return glm::intersectRayTriangle(pos, dir, cornerVertices[idx[0]], cornerVertices[idx[1]], + cornerVertices[idx[2]], *baryPos, *eh); + }); +} diff --git a/game/vehicles/railVehicle.h b/game/vehicles/railVehicle.h index a089f1d..55cb6a2 100644 --- a/game/vehicles/railVehicle.h +++ b/game/vehicles/railVehicle.h @@ -4,6 +4,8 @@ #include "gfx/renderable.h" #include "railVehicleClass.h" #include +#include +#include #include #include #include @@ -11,13 +13,14 @@ class Shader; class Train; -class RailVehicle : public Renderable { +class RailVehicle : public Renderable, Selectable { public: explicit RailVehicle(RailVehicleClassPtr rvc) : rvClass {std::move(rvc)} { } void move(const Train *, float & trailBy); void render(const Shader & shader) const override; + [[nodiscard]] bool intersectRay(const glm::vec3 &, const glm::vec3 &, glm::vec2 *, float *) const override; Location location; diff --git a/game/vehicles/train.cpp b/game/vehicles/train.cpp index 14753c0..4f19bed 100644 --- a/game/vehicles/train.cpp +++ b/game/vehicles/train.cpp @@ -23,6 +23,12 @@ Train::getBogiePosition(float linkDist, float dist) const return b2Link.first->positionAt(b2linkDist, b2Link.second); } +bool +Train::intersectRay(const glm::vec3 & pos, const glm::vec3 & dir, glm::vec2 * baryPos, float * eh) const +{ + return applyOne(&RailVehicle::intersectRay, pos, dir, baryPos, eh) != end(); +} + void Train::tick(TickDuration dur) { diff --git a/game/vehicles/train.h b/game/vehicles/train.h index ef49209..c823aed 100644 --- a/game/vehicles/train.h +++ b/game/vehicles/train.h @@ -9,6 +9,7 @@ #include "railVehicle.h" #include "vehicle.h" #include +#include #include #include #include @@ -27,6 +28,8 @@ public: void render(const Shader & shader) const override; + [[nodiscard]] bool intersectRay(const glm::vec3 &, const glm::vec3 &, glm::vec2 *, float *) const override; + void tick(TickDuration elapsed) override; void doActivity(Go *, TickDuration) override; void doActivity(Idle *, TickDuration) override; diff --git a/game/vehicles/vehicle.h b/game/vehicles/vehicle.h index 07bd492..3ddeac5 100644 --- a/game/vehicles/vehicle.h +++ b/game/vehicles/vehicle.h @@ -6,13 +6,14 @@ #include #include #include +#include #include #include #include class Location; -class Vehicle : public WorldObject, public Renderable { +class Vehicle : public WorldObject, public Renderable, public Selectable { public: explicit Vehicle(const LinkPtr & link, float linkDist = 0); float linkDist; // distance along current link diff --git a/gfx/gl/camera.cpp b/gfx/gl/camera.cpp index 5a90d84..905f78e 100644 --- a/gfx/gl/camera.cpp +++ b/gfx/gl/camera.cpp @@ -12,3 +12,10 @@ Camera::GetViewProjection() const { return projection * glm::lookAt(pos, pos + forward, up); } + +glm::vec3 +Camera::unProject(const glm::vec2 & mouse) const +{ + static constexpr const glm::vec4 screen {0, 0, 1, 1}; + return glm::normalize(glm::unProject(mouse ^ 1, glm::lookAt(::origin, forward, up), projection, screen)); +} diff --git a/gfx/gl/camera.h b/gfx/gl/camera.h index 72f699d..1d3f5b3 100644 --- a/gfx/gl/camera.h +++ b/gfx/gl/camera.h @@ -8,6 +8,7 @@ public: Camera(glm::vec3 pos, float fov, float aspect, float zNear, float zFar); [[nodiscard]] glm::mat4 GetViewProjection() const; + [[nodiscard]] glm::vec3 unProject(const glm::vec2 &) const; glm::vec3 pos; glm::vec3 forward; diff --git a/iwyu.json b/iwyu.json index 515ab21..fb07160 100644 --- a/iwyu.json +++ b/iwyu.json @@ -47,6 +47,14 @@ "public" ] }, + { + "include": [ + "@", + "private", + "", + "public" + ] + }, { "include": [ "@", diff --git a/lib/maths.cpp b/lib/maths.cpp index 4d9f8d4..b8dbd34 100644 --- a/lib/maths.cpp +++ b/lib/maths.cpp @@ -70,6 +70,12 @@ rotate_ypr(glm::vec3 a) return rotate_yaw(a.y) * rotate_pitch(a.x) * rotate_roll(a.z); } +glm::mat4 +rotate_yp(glm::vec2 a) +{ + return rotate_yaw(a.y) * rotate_pitch(a.x); +} + float vector_yaw(const glm::vec3 & diff) { diff --git a/lib/maths.h b/lib/maths.h index ad2f3e5..a75c7a8 100644 --- a/lib/maths.h +++ b/lib/maths.h @@ -51,6 +51,7 @@ glm::mat2 rotate_flat(float); glm::mat4 rotate_roll(float); glm::mat4 rotate_yaw(float); glm::mat4 rotate_pitch(float); +glm::mat4 rotate_yp(glm::vec2); glm::mat4 rotate_ypr(glm::vec3); float vector_yaw(const glm::vec3 & diff); @@ -84,6 +85,12 @@ operator^(const glm::vec2 & v, float z) return {v.x, v.y, z}; } +constexpr inline glm::vec4 +operator^(const glm::vec3 & v, float w) +{ + return {v.x, v.y, v.z, w}; +} + constexpr inline glm::vec3 operator!(const glm::vec2 & v) { diff --git a/test/test-maths.cpp b/test/test-maths.cpp index edc3e60..86cb7b8 100644 --- a/test/test-maths.cpp +++ b/test/test-maths.cpp @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -250,3 +251,38 @@ BOOST_DATA_TEST_CASE(curve1, } } } + +BOOST_AUTO_TEST_CASE(camera_clicks) +{ + Camera camera {{0, 0, 0}, ::half_pi, 1.25F, .1F, 10000.F}; + constexpr float centre {0.5F}, right {0.9F}, left {0.1F}, top {1.F}, bottom {0.F}; + camera.forward = ::north; + BOOST_CHECK_CLOSE_VEC(camera.unProject({centre, centre}), ::north); + BOOST_CHECK_CLOSE_VEC(camera.unProject({left, centre}), glm::normalize(::north + ::west)); + BOOST_CHECK_CLOSE_VEC(camera.unProject({right, centre}), glm::normalize(::north + ::east)); + BOOST_CHECK_CLOSE_VEC(camera.unProject({centre, top}), glm::normalize(::north + ::up)); + BOOST_CHECK_CLOSE_VEC(camera.unProject({centre, bottom}), glm::normalize(::north + ::down)); + BOOST_CHECK_CLOSE_VEC(camera.unProject({left, top}), glm::normalize(::north + ::west + ::up)); + BOOST_CHECK_CLOSE_VEC(camera.unProject({right, top}), glm::normalize(::north + ::east + ::up)); + BOOST_CHECK_CLOSE_VEC(camera.unProject({left, bottom}), glm::normalize(::north + ::west + ::down)); + BOOST_CHECK_CLOSE_VEC(camera.unProject({right, bottom}), glm::normalize(::north + ::east + ::down)); + + camera.forward = ::east; + BOOST_CHECK_CLOSE_VEC(camera.unProject({centre, centre}), ::east); + BOOST_CHECK_CLOSE_VEC(camera.unProject({left, centre}), glm::normalize(::north + ::east)); + BOOST_CHECK_CLOSE_VEC(camera.unProject({right, centre}), glm::normalize(::south + ::east)); + + camera.forward = glm::normalize(::north + ::down); + BOOST_CHECK_CLOSE_VEC(camera.unProject({centre, centre}), glm::normalize(::north + ::down)); + BOOST_CHECK_CLOSE_VEC(camera.unProject({centre, top}), glm::normalize(::north)); + + camera.forward = glm::normalize(::north + ::west + ::down); + BOOST_CHECK_CLOSE_VEC(camera.unProject({centre, centre}), glm::normalize(::north + ::west + ::down)); + BOOST_CHECK_CLOSE_VEC(camera.unProject({centre, top}), glm::normalize(::north + ::west + ::up * 0.2F)); + + camera.forward = glm::normalize(::north + ::west); + BOOST_CHECK_CLOSE_VEC(camera.unProject({centre, centre}), glm::normalize(::north + ::west)); + BOOST_CHECK_CLOSE_VEC(camera.unProject({centre, top}), glm::normalize(::north + ::west + ::up * 1.2F)); + BOOST_CHECK_CLOSE_VEC(camera.unProject({right, centre}), glm::normalize(::north)); + BOOST_CHECK_CLOSE_VEC(camera.unProject({left, centre}), glm::normalize(::west)); +} diff --git a/ui/gameMainWindow.cpp b/ui/gameMainWindow.cpp index e1d9763..41d55da 100644 --- a/ui/gameMainWindow.cpp +++ b/ui/gameMainWindow.cpp @@ -3,15 +3,20 @@ #include "manualCameraController.h" #include "maths.h" #include "toolbar.h" +#include "ui/uiComponent.h" #include "window.h" #include #include #include #include +#include #include // IWYU pragma: keep #include #include #include +#include + +class UIShader; class GameMainToolbar : public Toolbar { public: @@ -26,9 +31,45 @@ public: } }; -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} +class GameMainSelector : public UIComponent { +public: + GameMainSelector(const Camera * c, glm::vec2 size, const GameState * gs) : + UIComponent {{{}, size}}, camera {c}, gameState {gs} + { + } + + void + render(const UIShader &, const Position &) const override + { + } + + bool + handleInput(const SDL_Event & e, const Position &) override + { + switch (e.type) { + case SDL_MOUSEBUTTONDOWN: + if (e.button.button == SDL_BUTTON_LEFT) { + const auto mouse = glm::vec2 {e.button.x, e.button.y} / position.size; + + glm::vec2 baryPos {}; + float eh; + + std::ignore = gameState->world.applyOne( + &Selectable::intersectRay, camera->pos, camera->unProject(mouse), &baryPos, &eh); + } + } + return false; + } + +private: + const Camera * camera; + const GameState * gameState; +}; + +GameMainWindow::GameMainWindow(size_t w, size_t h, const GameState * gameState) : + Window {w, h, "I Like Trains"}, camera {{-1250.0F, -1250.0F, 35.0F}, quarter_pi, rdiv(w, h), 0.1F, 10000.0F} { + uiComponents.create(&camera, glm::vec2 {w, h}, gameState); uiComponents.create(); uiComponents.create(glm::vec2 {-1150, -1150}); diff --git a/ui/gameMainWindow.h b/ui/gameMainWindow.h index 8b88f11..b85b8a8 100644 --- a/ui/gameMainWindow.h +++ b/ui/gameMainWindow.h @@ -11,7 +11,7 @@ class GameState; class GameMainWindow : public Window { public: - GameMainWindow(size_t w, size_t h); + GameMainWindow(size_t w, size_t h, const GameState *); void tick(TickDuration) override; diff --git a/ui/manualCameraController.cpp b/ui/manualCameraController.cpp index 568c547..74b526b 100644 --- a/ui/manualCameraController.cpp +++ b/ui/manualCameraController.cpp @@ -22,6 +22,18 @@ ManualCameraController::handleInput(const SDL_Event & e, const Position &) case SDLK_RCTRL: ctrl = false; return true; + case SDLK_KP_8: + direction = 0; + break; + case SDLK_KP_4: + direction = -half_pi; + break; + case SDLK_KP_6: + direction = half_pi; + break; + case SDLK_KP_2: + direction = pi; + break; } break; case SDL_MOUSEBUTTONDOWN: -- cgit v1.2.3