summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--application/main.cpp2
-rw-r--r--game/selectable.h16
-rw-r--r--game/vehicles/railVehicle.cpp44
-rw-r--r--game/vehicles/railVehicle.h5
-rw-r--r--game/vehicles/train.cpp6
-rw-r--r--game/vehicles/train.h3
-rw-r--r--game/vehicles/vehicle.h3
-rw-r--r--gfx/gl/camera.cpp7
-rw-r--r--gfx/gl/camera.h1
-rw-r--r--iwyu.json8
-rw-r--r--lib/maths.cpp6
-rw-r--r--lib/maths.h7
-rw-r--r--test/test-maths.cpp36
-rw-r--r--ui/gameMainWindow.cpp45
-rw-r--r--ui/gameMainWindow.h2
-rw-r--r--ui/manualCameraController.cpp12
16 files changed, 197 insertions, 6 deletions
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<GameMainWindow>(DISPLAY_WIDTH, DISPLAY_HEIGHT);
+ windows.create<GameMainWindow>(DISPLAY_WIDTH, DISPLAY_HEIGHT, this);
world.create<Terrain>();
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 <glm/glm.hpp>
+#include <special_members.hpp>
+
+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 <algorithm>
#include <array>
#include <glm/glm.hpp>
+#include <glm/gtx/intersect.hpp>
+#include <glm/gtx/transform.hpp>
#include <location.hpp>
#include <maths.h>
#include <memory>
@@ -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<glm::vec3, 8> 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<glm::uvec3, 10> 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 <array>
+#include <game/selectable.h>
+#include <glm/glm.hpp>
#include <location.hpp>
#include <memory>
#include <utility>
@@ -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 <collection.hpp>
+#include <glm/glm.hpp>
#include <location.hpp>
#include <memory>
#include <vector>
@@ -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 <game/activity.h>
#include <game/network/link.h>
#include <game/orders.h>
+#include <game/selectable.h>
#include <game/worldobject.h>
#include <gfx/renderable.h>
#include <memory>
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
@@ -49,6 +49,14 @@
},
{
"include": [
+ "@<glm/gtx/intersect.inl>",
+ "private",
+ "<glm/gtx/intersect.hpp>",
+ "public"
+ ]
+ },
+ {
+ "include": [
"@<glm/gtx/rotate_vector.inl>",
"private",
"<glm/gtx/rotate_vector.hpp>",
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 <type_traits>
#include <game/network/link.h>
+#include <gfx/gl/camera.h>
#include <glm/glm.hpp>
#include <maths.h>
#include <tuple>
@@ -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 <GL/glew.h>
#include <SDL2/SDL.h>
#include <collection.hpp>
#include <game/gamestate.h>
+#include <game/selectable.h>
#include <game/worldobject.h> // IWYU pragma: keep
#include <gfx/renderable.h>
#include <glm/glm.hpp>
#include <memory>
+#include <tuple>
+
+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>(
+ &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<GameMainSelector>(&camera, glm::vec2 {w, h}, gameState);
uiComponents.create<GameMainToolbar>();
uiComponents.create<ManualCameraController>(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: