ADD: PickingSystem
This commit is contained in:
parent
4ce6791343
commit
7d9b31fe12
@ -153,6 +153,9 @@ add_library(Engine STATIC
|
||||
engine/src/game/GameMode.h
|
||||
engine/src/game/TurnBasedGameMode.cpp
|
||||
engine/src/game/TurnBasedGameMode.h
|
||||
engine/src/ecs/standardComponents/PickableComponent.h
|
||||
engine/src/ecs/systems/PickingSystem.cpp
|
||||
engine/src/ecs/systems/PickingSystem.h
|
||||
)
|
||||
target_include_directories(Engine PUBLIC engine/src)
|
||||
target_link_libraries(Engine PUBLIC OpenGL::GL glfw glad glm::glm imgui spdlog::spdlog tinyobjloader stb_image)
|
||||
|
||||
13
engine/src/ecs/standardComponents/PickableComponent.h
Normal file
13
engine/src/ecs/standardComponents/PickableComponent.h
Normal file
@ -0,0 +1,13 @@
|
||||
//
|
||||
// Created by sebastian on 02.08.26.
|
||||
//
|
||||
|
||||
#ifndef COLORRACE_PICKABLECOMPONENT_H
|
||||
#define COLORRACE_PICKABLECOMPONENT_H
|
||||
namespace engine {
|
||||
struct PickableComponent {
|
||||
float radius;
|
||||
};
|
||||
}
|
||||
|
||||
#endif //COLORRACE_PICKABLECOMPONENT_H
|
||||
27
engine/src/ecs/systems/PickingSystem.cpp
Normal file
27
engine/src/ecs/systems/PickingSystem.cpp
Normal file
@ -0,0 +1,27 @@
|
||||
//
|
||||
// Created by sebastian on 02.08.26.
|
||||
//
|
||||
|
||||
#include "PickingSystem.h"
|
||||
|
||||
std::pair<glm::vec3, glm::vec3> engine::PickingSystem::screenToRay(const glm::vec2 &ndc, const Camera &camera) {
|
||||
glm::mat4 invVP = glm::inverse(camera.getProjectionMatrix() * camera.getViewMatrix());
|
||||
glm::vec4 nearPoint = invVP * glm::vec4(ndc.x, -ndc.y, -1.0f, 1.0f); // Y invertiert (Screen vs. NDC)
|
||||
glm::vec4 farPoint = invVP * glm::vec4(ndc.x, -ndc.y, 1.0f, 1.0f);
|
||||
nearPoint /= nearPoint.w;
|
||||
farPoint /= farPoint.w;
|
||||
auto origin = glm::vec3(nearPoint);
|
||||
glm::vec3 dir = glm::normalize(glm::vec3(farPoint) - glm::vec3(nearPoint));
|
||||
return {origin, dir};
|
||||
}
|
||||
|
||||
bool engine::PickingSystem::raySphereIntersect(const glm::vec3 &origin, const glm::vec3 &dir, const glm::vec3 ¢er,
|
||||
float radius, float &t) {
|
||||
glm::vec3 oc = origin - center;
|
||||
float b = glm::dot(oc, dir);
|
||||
float c = glm::dot(oc, oc) - radius * radius;
|
||||
float discriminant = b * b - c;
|
||||
if (discriminant < 0.0f) return false;
|
||||
t = -b - std::sqrt(discriminant);
|
||||
return t >= 0.0f;
|
||||
}
|
||||
54
engine/src/ecs/systems/PickingSystem.h
Normal file
54
engine/src/ecs/systems/PickingSystem.h
Normal file
@ -0,0 +1,54 @@
|
||||
//
|
||||
// Created by sebastian on 02.08.26.
|
||||
//
|
||||
|
||||
#ifndef COLORRACE_PICKINGSYSTEM_H
|
||||
#define COLORRACE_PICKINGSYSTEM_H
|
||||
#include <optional>
|
||||
|
||||
#include "imgui.h"
|
||||
#include "core/inputsOutputs/inputs/Mouse.h"
|
||||
#include "core/inputsOutputs/inputs/MouseButton.h"
|
||||
#include "ecs/EntityRegistry.h"
|
||||
#include "ecs/standardComponents/PickableComponent.h"
|
||||
#include "ecs/standardComponents/TransformComponent.h"
|
||||
#include "renderer/entities/Camera.h"
|
||||
|
||||
|
||||
namespace engine {
|
||||
class Mouse;
|
||||
|
||||
class PickingSystem {
|
||||
public:
|
||||
explicit PickingSystem(Camera& camera) : m_camera(camera) {}
|
||||
|
||||
std::optional<Entity> update(const Mouse& mouse, EntityRegistry& registry) {
|
||||
if (ImGui::GetIO().WantCaptureMouse) return std::nullopt;
|
||||
if (!mouse.isClickEvent(MouseButton::LEFT)) return std::nullopt;
|
||||
|
||||
auto [origin, dir] = screenToRay(mouse.getXY(/*ndc=*/true), m_camera);
|
||||
|
||||
float closestT = std::numeric_limits<float>::max();
|
||||
std::optional<Entity> hit;
|
||||
|
||||
for (const auto& [entity, pickable] : registry.getPool<PickableComponent>()) {
|
||||
const auto transform = registry.getComponent<TransformComponent>(entity);
|
||||
if (float t; raySphereIntersect(origin, dir, transform.m_position, pickable.radius, t) && t < closestT) {
|
||||
closestT = t;
|
||||
hit = entity;
|
||||
}
|
||||
}
|
||||
|
||||
return hit;
|
||||
}
|
||||
private:
|
||||
Camera& m_camera;
|
||||
|
||||
static std::pair<glm::vec3, glm::vec3> screenToRay(const glm::vec2& screenPos, const Camera& camera);
|
||||
|
||||
static bool raySphereIntersect(const glm::vec3& origin, const glm::vec3& dir, const glm::vec3& center, float radius, float& t);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#endif //COLORRACE_PICKINGSYSTEM_H
|
||||
@ -34,6 +34,22 @@ void GameLayer::onUpdate(float deltaTime) {
|
||||
Layer::onUpdate(deltaTime);
|
||||
m_cameraController->update(getKeyboard(), *m_camera, deltaTime);
|
||||
|
||||
for (const auto& event : m_gameMode->pollEvents()) {
|
||||
std::visit([this]<typename T>(const T& e) {
|
||||
if constexpr (std::is_same_v<T, ludo::PieceMovedEvent>) {
|
||||
m_piecePresenter.onPieceMoved(e, m_gameMode->getState());
|
||||
}
|
||||
// DiceRolledEvent/TurnChangedEvent -> ImGui-State aktualisieren
|
||||
}, event);
|
||||
}
|
||||
|
||||
if (auto entity = m_pickingSystem.update(getMouse(), m_entityRegistry)) {
|
||||
if (auto pieceIndex = m_piecePresenter.getPieceIndex(*entity)) {
|
||||
m_gameMode->sendCommand(ludo::MovePieceCommand{m_localPlayer, *pieceIndex});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
void GameLayer::onRender() {
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
|
||||
#include "core/inputsOutputs/controller/CameraController.h"
|
||||
#include "ecs/EntityRegistry.h"
|
||||
#include "ecs/systems/PickingSystem.h"
|
||||
#include "layer/Layer.h"
|
||||
#include "ludo/BoardLayout.h"
|
||||
#include "ludo/PiecePresenter.h"
|
||||
@ -21,7 +22,8 @@ public:
|
||||
GameLayer(std::shared_ptr<ludo::LudoGameMode> gameMode, engine::EntityRegistry& entityRegistry, std::shared_ptr<engine::Camera> cam, std::shared_ptr<engine::PointLight> light,
|
||||
engine::Keyboard& keyboard, engine::Mouse& mouse, std::shared_ptr<engine::Model> boardModel, std::unordered_map<std::string, std::shared_ptr<engine::Model>> pieceModdels)
|
||||
: Layer(keyboard, mouse), m_gameMode(std::move(gameMode)), m_entityRegistry(entityRegistry), m_camera(std::move(cam)), m_renderer(std::make_unique<engine::MeshRenderer>()),
|
||||
m_pointLight(std::move(light)), m_boardLayout(std::move(boardModel)), m_piecePresenter(m_entityRegistry, std::move(pieceModdels), m_boardLayout){}
|
||||
m_pointLight(std::move(light)), m_boardLayout(std::move(boardModel)), m_piecePresenter(m_entityRegistry, std::move(pieceModdels), m_boardLayout),
|
||||
m_pickingSystem(*m_camera){}
|
||||
|
||||
void onUpdate(float deltaTime) override;
|
||||
void onRender() override;
|
||||
@ -41,6 +43,7 @@ private:
|
||||
ludo::PiecePresenter m_piecePresenter;
|
||||
engine::PlayerID m_localPlayer = 0;
|
||||
std::shared_ptr<ludo::LudoGameMode> m_gameMode;
|
||||
engine::PickingSystem m_pickingSystem;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@ -16,6 +16,7 @@
|
||||
namespace ludo {
|
||||
inline constexpr int kTrackLength = 40;
|
||||
inline constexpr int kHomeStretchLength = 4;
|
||||
inline constexpr float kPieceRadius = 0.3f;
|
||||
|
||||
enum class PlayerColor : uint8_t { Red, Blue, Yellow, Green, Count};
|
||||
constexpr std::string_view toString(PlayerColor color)
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
#include "PiecePresenter.h"
|
||||
|
||||
#include "ecs/standardComponents/MeshComponent.h"
|
||||
#include "ecs/standardComponents/PickableComponent.h"
|
||||
#include "ecs/standardComponents/TransformComponent.h"
|
||||
|
||||
void ludo::PiecePresenter::spawnPieces(const ludo::LudoGameState &state) {
|
||||
@ -16,6 +17,7 @@ void ludo::PiecePresenter::spawnPieces(const ludo::LudoGameState &state) {
|
||||
{0.334296f, 0.334296f, 0.334296f});
|
||||
m_registry.addComponent<engine::TransformComponent>(entity, transform);
|
||||
m_registry.addComponent<engine::MeshComponent>(entity, engine::MeshComponent(m_pieceModels[std::string(ludo::toString(piece.color))]));
|
||||
m_registry.addComponent<engine::PickableComponent>(entity, engine::PickableComponent(kPieceRadius));
|
||||
}
|
||||
}
|
||||
|
||||
@ -26,3 +28,9 @@ void ludo::PiecePresenter::onPieceMoved(const ludo::PieceMovedEvent &event, cons
|
||||
auto& transform = m_registry.getComponent<engine::TransformComponent>(entity);
|
||||
transform.m_position = target;
|
||||
}
|
||||
|
||||
std::optional<int> ludo::PiecePresenter::getPieceIndex(std::optional<engine::Entity>::value_type entity) {
|
||||
auto it = std::ranges::find(m_pieceEntities, entity);
|
||||
if (it == m_pieceEntities.end()) return std::nullopt;
|
||||
return static_cast<int>(std::distance(m_pieceEntities.begin(), it));
|
||||
}
|
||||
|
||||
@ -4,6 +4,8 @@
|
||||
|
||||
#ifndef COLORRACE_PIECEPRESENTER_H
|
||||
#define COLORRACE_PIECEPRESENTER_H
|
||||
#include <optional>
|
||||
|
||||
#include "BoardLayout.h"
|
||||
#include "LudoGameState.h"
|
||||
#include "ecs/EntityRegistry.h"
|
||||
@ -19,6 +21,10 @@ namespace ludo {
|
||||
|
||||
void spawnPieces(const ludo::LudoGameState& state);
|
||||
void onPieceMoved(const ludo::PieceMovedEvent& event, const ludo::LudoGameState& state);
|
||||
[[nodiscard]] const std::vector<engine::Entity>& getPieceEntities() const { return m_pieceEntities; }
|
||||
|
||||
[[nodiscard]] std::optional<int> getPieceIndex(std::optional<engine::Entity>::value_type entity);
|
||||
|
||||
private:
|
||||
engine::EntityRegistry& m_registry;
|
||||
std::unordered_map<std::string, std::shared_ptr<engine::Model>> m_pieceModels;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user