Commit 03be0527 authored by Georg Schäfer's avatar Georg Schäfer
Browse files

Add ray level intersection.

parent f3fde2ae
Pipeline #3252 failed with stage
in 1 minute and 47 seconds
......@@ -3,7 +3,7 @@
#include <sstream>
namespace phase_shifter::level {
Level::Level(Tileset_ptr tileset, vector_2d<char> tiles) : _tileset(tileset), _tiles(tiles) {}
Level::Level(Tileset_ptr tileset, vector_2d<char> tiles) : tileset(tileset), tiles(tiles) {}
} // namespace phase_shifter::level
namespace mirrage::asset {
......
......@@ -25,19 +25,14 @@ namespace phase_shifter::level {
using Tileset_ptr = mirrage::asset::Ptr<Tileset>;
class Level {
friend class Level_system;
public:
struct Level {
template <typename T>
using vector_2d = std::vector<std::vector<T>>;
Level(Tileset_ptr tileset, vector_2d<char> tiles);
private:
Tileset_ptr _tileset;
vector_2d<char> _tiles;
Tileset_ptr tileset;
vector_2d<char> tiles;
};
using Level_ptr = mirrage::asset::Ptr<Level>;
......
......@@ -36,28 +36,72 @@ namespace phase_shifter::level {
});
}
auto Level_system::load(const std::string& name) -> void
{
_current_level = _assets.load<Level>(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::function<auto(const Tileset& tileset,
const std::string& key,
const Tile& tile,
const glm::vec3& position)
->void>;
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<Level>(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<bool, util::Contact>
{
auto& level = _current_level.get_blocking();
std::pair<bool, util::Contact> 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)
......
#pragma once
#include "../util/collision.hpp"
#include "level.hpp"
#include <functional>
......@@ -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<bool, util::Contact>;
private:
auto _spawn_function(const std::string& key)
......
#include "collision.hpp"
namespace phase_shifter::util {
auto intersect(const Ray& ray, const Aabb& aabb) -> std::pair<bool, Contact>
{
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
#pragma once
#include <tuple>
#include <glm/vec3.hpp>
namespace phase_shifter::util {
struct Contact {
glm::vec2 point{0.f};
float distance2 = std::numeric_limits<float>::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<bool, Contact>;
} // namespace phase_shifter::util
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment