From 03be05270e9f99cb35f1c5c2c49470c12a3d29c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Georg=20Sch=C3=A4fer?= Date: Wed, 18 Sep 2019 13:17:32 +0200 Subject: [PATCH 1/5] Add ray level intersection. --- src/level/level.cpp | 2 +- src/level/level.hpp | 11 ++---- src/level/level_system.cpp | 80 +++++++++++++++++++++++++++++--------- src/level/level_system.hpp | 2 + src/util/collision.cpp | 34 ++++++++++++++++ src/util/collision.hpp | 24 ++++++++++++ 6 files changed, 126 insertions(+), 27 deletions(-) create mode 100644 src/util/collision.cpp create mode 100644 src/util/collision.hpp diff --git a/src/level/level.cpp b/src/level/level.cpp index cee2901..d1e2185 100644 --- a/src/level/level.cpp +++ b/src/level/level.cpp @@ -3,7 +3,7 @@ #include namespace phase_shifter::level { - Level::Level(Tileset_ptr tileset, vector_2d tiles) : _tileset(tileset), _tiles(tiles) {} + Level::Level(Tileset_ptr tileset, vector_2d tiles) : tileset(tileset), tiles(tiles) {} } // namespace phase_shifter::level namespace mirrage::asset { diff --git a/src/level/level.hpp b/src/level/level.hpp index 3deef89..73359db 100644 --- a/src/level/level.hpp +++ b/src/level/level.hpp @@ -25,19 +25,14 @@ namespace phase_shifter::level { using Tileset_ptr = mirrage::asset::Ptr; - class Level { - friend class Level_system; - - public: + struct Level { template using vector_2d = std::vector>; Level(Tileset_ptr tileset, vector_2d tiles); - - private: - Tileset_ptr _tileset; - vector_2d _tiles; + Tileset_ptr tileset; + vector_2d tiles; }; using Level_ptr = mirrage::asset::Ptr; diff --git a/src/level/level_system.cpp b/src/level/level_system.cpp index 01e394c..d7cb732 100644 --- a/src/level/level_system.cpp +++ b/src/level/level_system.cpp @@ -36,28 +36,72 @@ namespace phase_shifter::level { }); } - auto Level_system::load(const std::string& name) -> void - { - _current_level = _assets.load(mirrage::asset::AID("level:" + name)); - auto& level = _current_level.get_blocking(); - auto& tiles = level._tileset->tiles; - auto offset = level._tileset->tile_size; - glm::vec3 position{0.f}; - for(auto&& row : level._tiles) { - position.x = 0.f; - for(auto&& tile_key : row) { - std::string key(1, tile_key); - if(auto tile_it = tiles.find(key); tile_it != tiles.end()) { - auto& tile = tile_it->second; - _entities.entity_builder(tile.blueprint).position(position).create(); - if(tile.spawn) { - _spawn_function(key)(tile, position); + namespace { + using action = std::functionvoid>; + + auto for_each(const Level& level, const action& action) + { + auto& tiles = level.tileset->tiles; + auto offset = level.tileset->tile_size; + glm::vec3 position{0.f}; + for(auto&& row : level.tiles) { + position.x = 0.f; + for(auto&& tile_key : row) { + std::string key(1, tile_key); + if(auto tile_it = tiles.find(key); tile_it != tiles.end()) { + auto& tile = tile_it->second; + action(level.tileset.get_blocking(), key, tile, position); + position.x += offset; } - position.x += offset; } + position.z += offset; } - position.z += offset; } + } // namespace + + auto Level_system::load(const std::string& name) -> void + { + _current_level = _assets.load(mirrage::asset::AID("level:" + name)); + auto& level = _current_level.get_blocking(); + for_each(level, [&](auto&, const std::string& key, const Tile& tile, const glm::vec3& position) { + _entities.entity_builder(tile.blueprint).position(position).create(); + if(tile.spawn) { + _spawn_function(key)(tile, position); + } + }); + } + + auto Level_system::ray_cast(const glm::vec2& origin, const glm::vec2& direction) + -> std::pair + { + auto& level = _current_level.get_blocking(); + std::pair result{false, {}}; + for_each(level, + [&](const Tileset& tileset, + const std::string& key, + const Tile& tile, + const glm::vec3& position) { + if(!tile.solid) { + return; + } + + glm::vec2 tile_origin{position.x, position.z}; + auto half_size = tileset.tile_size / 2.f; + glm::vec2 half_vector{half_size, half_size}; + auto [hit, contact] = util::intersect({origin, direction}, {tile_origin, half_vector}); + if(!hit) { + return; + } + if(contact.distance2 < result.second.distance2) { + result.first = true; + result.second = contact; + } + }); + return result; } auto Level_system::_spawn_function(const std::string& key) diff --git a/src/level/level_system.hpp b/src/level/level_system.hpp index e6bb271..277ab03 100644 --- a/src/level/level_system.hpp +++ b/src/level/level_system.hpp @@ -1,5 +1,6 @@ #pragma once +#include "../util/collision.hpp" #include "level.hpp" #include @@ -21,6 +22,7 @@ namespace phase_shifter::level { Level_system(mirrage::ecs::Entity_manager& entities, mirrage::asset::Asset_manager& assets); auto load(const std::string& name) -> void; + auto ray_cast(const glm::vec2& origin, const glm::vec2& direction) -> std::pair; private: auto _spawn_function(const std::string& key) diff --git a/src/util/collision.cpp b/src/util/collision.cpp new file mode 100644 index 0000000..e262345 --- /dev/null +++ b/src/util/collision.cpp @@ -0,0 +1,34 @@ +#include "collision.hpp" + +namespace phase_shifter::util { + auto intersect(const Ray& ray, const Aabb& aabb) -> std::pair + { + auto min = aabb.origin - aabb.half_vec; + auto max = aabb.origin + aabb.half_vec; + float tmin = (min.x - ray.origin.x) / ray.direction.x; + float tmax = (max.x - ray.origin.x) / ray.direction.x; + + if(tmin > tmax) { + tmin = tmax; + } + + float tymin = (min.y - ray.origin.y) / ray.direction.y; + float tymax = (max.y - ray.origin.y) / ray.direction.y; + + if(tymin > tymax) { + tymin = tymax; + } + + if((tmin > tymax) || (tymin > tmax)) { + return {false, {}}; + } + + if(tymin > tmin) { + tmin = tymin; + } + + glm::vec2 point{ray.origin + tmin * ray.direction}; + + return {true, {point, glm::distance2(point, ray.origin)}}; + } +} // namespace phase_shifter::util diff --git a/src/util/collision.hpp b/src/util/collision.hpp new file mode 100644 index 0000000..d898c15 --- /dev/null +++ b/src/util/collision.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include + +#include + +namespace phase_shifter::util { + struct Contact { + glm::vec2 point{0.f}; + float distance2 = std::numeric_limits::max(); + }; + + struct Ray { + glm::vec2 origin; + glm::vec2 direction; + }; + + struct Aabb { + glm::vec2 origin; + glm::vec2 half_vec; + }; + + auto intersect(const Ray& ray, const Aabb& aabb) -> std::pair; +} // namespace phase_shifter::util -- GitLab From 16a597d3ac3a88e69e594ed8de555d73c65625bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Georg=20Sch=C3=A4fer?= Date: Wed, 18 Sep 2019 14:10:45 +0200 Subject: [PATCH 2/5] Add rigid body component. --- assets/game_assets/blueprints/player.json | 16 +++++++++++----- src/gameplay/rigid_body_comp.cpp | 1 + src/gameplay/rigid_body_comp.hpp | 14 ++++++++++++++ src/level/level_system.cpp | 23 +++++++++++++++++++++++ src/level/level_system.hpp | 3 +++ src/meta_system.cpp | 1 + 6 files changed, 53 insertions(+), 5 deletions(-) create mode 100644 src/gameplay/rigid_body_comp.cpp create mode 100644 src/gameplay/rigid_body_comp.hpp diff --git a/assets/game_assets/blueprints/player.json b/assets/game_assets/blueprints/player.json index 5beeaaa..fd51827 100644 --- a/assets/game_assets/blueprints/player.json +++ b/assets/game_assets/blueprints/player.json @@ -1,12 +1,15 @@ { - "Transform":{ - "scale": {"x": 0.5, "y": 0.5, "z": 0.5} + "Transform": { + "scale": { + "x": 0.5, + "y": 0.5, + "z": 0.5 + } }, "Model": { "aid": "model:cube" }, - "Shadowcaster": { - }, + "Shadowcaster": {}, "Movement": { "beats_per_step": 1, "distance_per_step": 1, @@ -14,5 +17,8 @@ "off_beat_threshold": 0.2 }, "Input_controller": {}, - "Player": {} + "Player": {}, + "Rigid_body": { + "radius": 1.0 + } } diff --git a/src/gameplay/rigid_body_comp.cpp b/src/gameplay/rigid_body_comp.cpp new file mode 100644 index 0000000..1eaa463 --- /dev/null +++ b/src/gameplay/rigid_body_comp.cpp @@ -0,0 +1 @@ +#include "rigid_body_comp.hpp" diff --git a/src/gameplay/rigid_body_comp.hpp b/src/gameplay/rigid_body_comp.hpp new file mode 100644 index 0000000..77cbb5c --- /dev/null +++ b/src/gameplay/rigid_body_comp.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include + +namespace phase_shifter::gameplay { + struct Rigid_body_comp : public mirrage::ecs::Component { + static constexpr const char* name() { return "Rigid_body"; } + using Component::Component; + + float radius; + }; + + sf2_structDef(Rigid_body_comp, radius); +} // namespace phase_shifter::gameplay diff --git a/src/level/level_system.cpp b/src/level/level_system.cpp index d7cb732..cd66065 100644 --- a/src/level/level_system.cpp +++ b/src/level/level_system.cpp @@ -1,5 +1,8 @@ #include "level_system.hpp" +#include "../gameplay/movement_comp.hpp" +#include "../gameplay/player_comp.hpp" +#include "../gameplay/rigid_body_comp.hpp" #include "../gameplay/viewtarget_comp.hpp" #include @@ -12,6 +15,8 @@ namespace phase_shifter::level { Level_system::Level_system(mirrage::ecs::Entity_manager& entities, mirrage::asset::Asset_manager& assets) : _entities(entities), _assets(assets) { + _entities.register_component_type(); + _spawners.emplace("p", [&](const Tile& tile, const glm::vec3& position) { mirrage::ecs::Entity_facet player = _entities.entity_builder(tile.spawns).position(position).create(); @@ -36,6 +41,24 @@ namespace phase_shifter::level { }); } + auto Level_system::update(const mirrage::util::Time& time) -> void + { + for(auto&& [player, movement, transform, rigid_body] : _entities.list()) { + auto origin = glm::vec2{transform.position.x, transform.position.z}; + auto direction = glm::normalize(movement.aim); + auto [hit, contact] = ray_cast(origin, direction); + if(!hit) { + continue; + } + if(contact.distance2 <= rigid_body.radius * rigid_body.radius) { + movement.move = false; + } + } + } + namespace { using action = std::function + #include #include @@ -21,6 +23,7 @@ namespace phase_shifter::level { public: Level_system(mirrage::ecs::Entity_manager& entities, mirrage::asset::Asset_manager& assets); + auto update(const mirrage::util::Time& time) -> void; auto load(const std::string& name) -> void; auto ray_cast(const glm::vec2& origin, const glm::vec2& direction) -> std::pair; diff --git a/src/meta_system.cpp b/src/meta_system.cpp index 011a6a3..4bd9178 100644 --- a/src/meta_system.cpp +++ b/src/meta_system.cpp @@ -107,6 +107,7 @@ namespace phase_shifter { _beat_system->update(dt); _input_system->update(dt); + _level_system->update(dt); _movement_system->update(dt); _combat_system->update(dt); _camera_system->update(dt); -- GitLab From 3043db13b690c981a4fb741f293dc51fc9e0c6da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Georg=20Sch=C3=A4fer?= Date: Wed, 18 Sep 2019 16:48:07 +0200 Subject: [PATCH 3/5] Add additional intersection tests. --- src/level/level_system.cpp | 18 +-------------- src/util/collision.cpp | 45 ++++++++++++++++++++++++++++++++++---- src/util/collision.hpp | 8 +++++++ 3 files changed, 50 insertions(+), 21 deletions(-) diff --git a/src/level/level_system.cpp b/src/level/level_system.cpp index cd66065..8d111e2 100644 --- a/src/level/level_system.cpp +++ b/src/level/level_system.cpp @@ -41,23 +41,7 @@ namespace phase_shifter::level { }); } - auto Level_system::update(const mirrage::util::Time& time) -> void - { - for(auto&& [player, movement, transform, rigid_body] : _entities.list()) { - auto origin = glm::vec2{transform.position.x, transform.position.z}; - auto direction = glm::normalize(movement.aim); - auto [hit, contact] = ray_cast(origin, direction); - if(!hit) { - continue; - } - if(contact.distance2 <= rigid_body.radius * rigid_body.radius) { - movement.move = false; - } - } - } + auto Level_system::update(const mirrage::util::Time& time) -> void {} namespace { using action = std::function + namespace phase_shifter::util { auto intersect(const Ray& ray, const Aabb& aabb) -> std::pair { @@ -9,26 +11,61 @@ namespace phase_shifter::util { float tmax = (max.x - ray.origin.x) / ray.direction.x; if(tmin > tmax) { - tmin = tmax; + std::swap(tmin, tmax); } float tymin = (min.y - ray.origin.y) / ray.direction.y; float tymax = (max.y - ray.origin.y) / ray.direction.y; if(tymin > tymax) { - tymin = tymax; + std::swap(tymin, tymax); } if((tmin > tymax) || (tymin > tmax)) { return {false, {}}; } + glm::vec2 normal{-ray.direction.x, 0.f}; if(tymin > tmin) { - tmin = tymin; + tmin = tymin; + normal.x = 0.f; + normal.y = -ray.direction.y; } glm::vec2 point{ray.origin + tmin * ray.direction}; - return {true, {point, glm::distance2(point, ray.origin)}}; + return {true, {point, glm::normalize(normal), glm::distance2(point, ray.origin)}}; + } + + auto intersect(const Ray& ray, const Circle& circle) -> std::pair + { + glm::vec3 point; + glm::vec3 normal; + if(glm::intersectRaySphere(glm::vec3{ray.origin.x, 0.f, ray.origin.y}, + glm::vec3{ray.direction.x, 0.f, ray.direction.y}, + glm::vec3{circle.origin.x, 0.f, circle.origin.y}, + circle.radius, + point, + normal)) { + return {true, + {glm::vec2{point.x, point.y}, + glm::vec2{normal.x, normal.y}, + glm::distance2(glm::vec3{ray.origin.x, 0.f, ray.origin.y}, point)}}; + } + return {false, {}}; + } + + auto intersect(const Circle& circle1, const Circle& circle2) -> std::pair + { + auto c2_to_c1 = circle1.origin - circle2.origin; + auto distance2 = glm::length2(c2_to_c1); + auto combined_radius = circle1.radius + circle2.radius; + auto distance = distance2 - combined_radius * combined_radius; + if(glm::abs(distance) <= glm::epsilon()) { + auto normal = glm::normalize(c2_to_c1); + return {true, {circle2.origin + circle2.radius * normal, normal, distance}}; + } + + return {false, {}}; } } // namespace phase_shifter::util diff --git a/src/util/collision.hpp b/src/util/collision.hpp index d898c15..70da8fd 100644 --- a/src/util/collision.hpp +++ b/src/util/collision.hpp @@ -7,6 +7,7 @@ namespace phase_shifter::util { struct Contact { glm::vec2 point{0.f}; + glm::vec2 normal{0.f}; float distance2 = std::numeric_limits::max(); }; @@ -20,5 +21,12 @@ namespace phase_shifter::util { glm::vec2 half_vec; }; + struct Circle { + glm::vec2 origin; + float radius; + }; + auto intersect(const Ray& ray, const Aabb& aabb) -> std::pair; + auto intersect(const Ray& ray, const Circle& circle) -> std::pair; + auto intersect(const Circle& circle1, const Circle& circle2) -> std::pair; } // namespace phase_shifter::util -- GitLab From 56877340ea0f360ff00c5a4fad05229dc6b6941c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Georg=20Sch=C3=A4fer?= Date: Wed, 18 Sep 2019 16:54:59 +0200 Subject: [PATCH 4/5] Include missing glm header. --- src/util/collision.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/util/collision.hpp b/src/util/collision.hpp index 70da8fd..c595814 100644 --- a/src/util/collision.hpp +++ b/src/util/collision.hpp @@ -2,6 +2,7 @@ #include +#include #include namespace phase_shifter::util { -- GitLab From d6ef9ae9713286798006d60ca879855f1b747e49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Georg=20Sch=C3=A4fer?= Date: Wed, 18 Sep 2019 16:57:52 +0200 Subject: [PATCH 5/5] Include missing glm header. --- src/util/collision.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/util/collision.cpp b/src/util/collision.cpp index 8f00cf2..4dde2d4 100644 --- a/src/util/collision.cpp +++ b/src/util/collision.cpp @@ -1,6 +1,8 @@ #include "collision.hpp" +#include #include +#include namespace phase_shifter::util { auto intersect(const Ray& ray, const Aabb& aabb) -> std::pair -- GitLab