Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • gamedev/fggl
  • onuralpsezer/fggl
2 results
Show changes
Showing
with 714 additions and 144 deletions
......@@ -34,60 +34,59 @@ namespace fggl::debug {
* Logging levels
*/
enum class Level {
critical = spdlog::level::critical,
error = spdlog::level::err,
warning = spdlog::level::warn,
info = spdlog::level::info,
debug = spdlog::level::debug,
trace = spdlog::level::trace
critical = spdlog::level::critical,
error = spdlog::level::err,
warning = spdlog::level::warn,
info = spdlog::level::info,
debug = spdlog::level::debug,
trace = spdlog::level::trace
};
template<typename ...T>
void error(const FmtType& fmt, T&& ...args) {
void error(const FmtType &fmt, T &&...args) {
spdlog::error(fmt, args...);
}
template<typename ...T>
void warning(const FmtType& fmt, T&& ...args ){
void warning(const FmtType &fmt, T &&...args) {
spdlog::warn(fmt, args...);
}
template<typename ...T>
void info(const FmtType& fmt, T&& ...args ) {
void info(const FmtType &fmt, T &&...args) {
spdlog::info(fmt, args...);
}
template<typename ...T>
void debug(const FmtType& fmt, T&& ...args ) {
void debug(const FmtType &fmt, T &&...args) {
spdlog::debug(fmt, args...);
}
template<typename ...T>
void trace(const FmtType& fmt, T&& ...args ) {
void trace(const FmtType &fmt, T &&...args) {
spdlog::trace(fmt, args...);
}
template<typename ...T>
void log(const FmtType& fmt, T&& ...args) {
void log(const FmtType &fmt, T &&...args) {
spdlog::log(Level::info, fmt, args...);
}
template<typename ...T>
void log(Level level, const FmtType& fmt, T&& ...args) {
void log(Level level, const FmtType &fmt, T &&...args) {
spdlog::log(level, fmt, args...);
}
class Logger {
public:
Logger();
public:
Logger();
template<typename ...Args>
void log(Level level, const FmtType& fmt, Args&& ...args) {
spdlog::log(level, fmt, args...);
}
template<typename ...Args>
void log(Level level, const FmtType &fmt, Args &&...args) {
spdlog::log(level, fmt, args...);
}
};
}
#endif //FGGL_DEBUG_IMPL_LOGGING_SPDLOG_HPP
......@@ -21,33 +21,37 @@
#include <string_view>
namespace fggl::debug {
std::string demangle(const char* name);
using FmtType = const std::string_view;
enum class Level;
template<typename ...T>
void error(FmtType fmt, T&& ...args);
void error(FmtType fmt, T &&...args);
template<typename ...T>
void warning(FmtType fmt, T&& ...args );
void warning(FmtType fmt, T &&...args);
template<typename ...T>
void info(FmtType fmt, T&& ...args );
void info(FmtType fmt, T &&...args);
template<typename ...T>
void debug(FmtType fmt, T&& ...args );
void debug(FmtType fmt, T &&...args);
template<typename ...T>
void trace(FmtType fmt, T&& ...args );
void trace(FmtType fmt, T &&...args);
template<typename ...T>
void log(FmtType fmt, T&& ...args);
void log(FmtType fmt, T &&...args);
template<typename ...T>
void log(Level level, FmtType fmt, T&& ...args);
void log(Level level, FmtType fmt, T &&...args);
}
#include "fggl/debug/impl/logging_std20.hpp"
#include "fggl/debug/impl/logging_fmt.hpp"
#endif //FGGL_DEBUG_LOGGING_HPP
......@@ -28,18 +28,19 @@
namespace fggl::display {
struct GLFW {
constexpr static const char* name = "fggl::display::glfw";
constexpr static const std::array<modules::ModuleService, 1> provides = {
constexpr static const char *name = "fggl::display::glfw";
constexpr static const std::array<modules::ServiceName, 1> provides = {
WindowService::service
};
constexpr static const std::array<modules::ModuleService, 2> depends = {
constexpr static const std::array<modules::ServiceName, 2> depends = {
fggl::input::Input::service,
fggl::gfx::WindowGraphics::service
};
static const modules::ServiceFactory factory;
static bool factory(modules::ServiceName name, modules::Services &serviceManager);
};
bool glfw_factory(modules::ModuleService service, modules::Services& services) {
bool GLFW::factory(modules::ServiceName service, modules::Services &services) {
if (service == WindowService::service) {
auto input = services.get<input::Input>();
auto graphics = services.get<gfx::WindowGraphics>();
......@@ -50,7 +51,6 @@ namespace fggl::display {
}
return false;
}
const modules::ServiceFactory GLFW::factory = glfw_factory;
} // namespace fggl::display
......
......@@ -29,10 +29,12 @@ namespace fggl::display::glfw {
class WindowService : public display::WindowService {
public:
explicit WindowService(std::shared_ptr<GlfwContext> context, gfx::WindowGraphics* gfx) : m_context(std::move(context)), m_gfx(gfx), m_windows() {}
explicit WindowService(std::shared_ptr<GlfwContext> context, gfx::WindowGraphics *gfx)
: m_context(std::move(context)), m_gfx(gfx), m_windows() {}
virtual ~WindowService() = default;
display::Window* create() override {
display::Window *create() override {
m_windows.push_back(std::make_unique<Window>(m_context, m_gfx));
return m_windows.back().get();
}
......@@ -43,7 +45,7 @@ namespace fggl::display::glfw {
private:
std::shared_ptr<GlfwContext> m_context;
gfx::WindowGraphics* m_gfx;
gfx::WindowGraphics *m_gfx;
std::vector<std::unique_ptr<Window>> m_windows;
};
......
......@@ -45,7 +45,7 @@ namespace fggl::display::glfw {
class GlfwContext {
public:
explicit GlfwContext(fggl::input::Input* input);
explicit GlfwContext(fggl::input::Input *input);
~GlfwContext();
void pollEvents();
......@@ -77,7 +77,7 @@ namespace fggl::display::glfw {
class Window : public display::Window {
public:
explicit Window(std::shared_ptr<GlfwContext> context, gfx::WindowGraphics*);
explicit Window(std::shared_ptr<GlfwContext> context, gfx::WindowGraphics *);
~Window() override;
Window(Window &) = delete;
......@@ -105,8 +105,8 @@ namespace fggl::display::glfw {
inline void framesize(int width, int height) {
m_framesize = math::vec2(width, height);
if ( m_graphics != nullptr ) {
m_graphics->resize( width, height );
if (m_graphics != nullptr) {
m_graphics->resize(width, height);
}
}
......@@ -117,7 +117,7 @@ namespace fggl::display::glfw {
return glfwWindowShouldClose(m_window);
}
inline void setTitle(const char* title) override {
inline void setTitle(const char *title) override {
assert(m_window != nullptr);
glfwSetWindowTitle(m_window, title);
}
......
......@@ -44,7 +44,7 @@ namespace fggl::display::glfw {
return *instance;
}
inline void setup(input::Input* input) {
inline void setup(input::Input *input) {
m_inputs = input;
}
......@@ -95,7 +95,7 @@ namespace fggl::display::glfw {
}
private:
input::Input* m_inputs;
input::Input *m_inputs;
};
......
......@@ -21,8 +21,10 @@
#include "fggl/modules/module.hpp"
#include "fggl/math/types.hpp"
#include "fggl/gfx/interfaces.hpp"
//! Classes responsible for interacting with display managers
namespace fggl::display {
class Window {
......@@ -30,12 +32,6 @@ namespace fggl::display {
virtual ~Window() = default;
virtual void activate() const = 0;
template<typename T, typename ...Args>
void make_graphics(Args... args) {
activate();
m_graphics = std::make_unique<T>(*this, args...);
}
// window-related getters
[[nodiscard]]
virtual math::vec2i frameSize() const = 0;
......@@ -51,7 +47,7 @@ namespace fggl::display {
return *m_graphics;
}
virtual void setTitle(const char* title) = 0;
virtual void setTitle(const char *title) = 0;
virtual void setFullscreen(bool state) = 0;
......@@ -59,14 +55,15 @@ namespace fggl::display {
virtual bool isFullscreen() const = 0;
protected:
std::unique_ptr<gfx::Graphics> m_graphics;
gfx::Graphics* m_graphics;
};
class WindowService {
public:
constexpr static const modules::ModuleService service = modules::make_service("fggl::display::WindowService");
constexpr static const auto
service = modules::make_service("fggl::display::WindowService");
virtual Window* create() = 0;
virtual Window *create() = 0;
virtual void pollEvents() = 0;
};
......
/*
* This file is part of FGGL.
*
* FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with FGGL.
* If not, see <https://www.gnu.org/licenses/>.
*/
//
// Created by webpigeon on 25/03/23.
//
#ifndef FGGL_DS_GRAPH_HPP
#define FGGL_DS_GRAPH_HPP
#include <map>
#include <vector>
#include <queue>
#include <stack>
#include <set>
namespace fggl::ds {
template<typename T>
class DirectedGraph {
public:
/**
* Add a single edge to the graph.
*
* If the entry does not already exist, this will create the entry before adding the dependencies to it.
* If the entry already exists, this will append the provided dependencies to its existing list.
*
* @param start the entry which depends something else
* @param end the thing it depends on
*/
inline void addEdge(const T start, const T end) {
m_edges[start].push_back(end);
m_edges[end];
}
/**
* Add a single vertex to the graph.
*/
inline void addVertex(const T vertex) {
m_edges[vertex];
}
/**
* Add a series of dependencies for an entry.
*
* If the entry does not already exist, this will create the entry before adding the dependencies to it.
* If the entry already exists, this will append the provided dependencies to its existing list.
*
* @param name the entry having dependencies added
* @param dependencies the things it depends on
*/
void addEdges(const T name, const std::vector<T> &dependencies) {
auto existing = m_edges.find(name);
if (existing == m_edges.end()) {
m_edges[name] = dependencies;
} else {
existing->second.insert(existing->second.end(), dependencies.begin(), dependencies.end());
}
}
/**
* Clear all currently stored dependencies.
*
* This method will result in the dependency graph being empty, with no known modules.
*/
inline void clear() {
m_edges.clear();
}
inline auto begin() const {
return m_edges.begin();
}
inline auto end() const {
return m_edges.end();
}
bool getOrder(std::stack<T> &stack) {
std::set<T> visited{};
for (const auto &module : m_edges) {
if (!visited.contains(module.first)) {
sortUtil(module.first, visited, stack);
}
}
return true;
}
bool getOrderPartial(T first, std::stack<T> &stack) {
std::set<T> visited{};
sortUtil(first, visited, stack);
return true;
}
bool getOrderRev(std::queue<T> &stack) {
std::set<T> visited{};
for (const auto &module : m_edges) {
if (!visited.contains(module.first)) {
sortUtilRev(module.first, visited, stack);
}
}
return true;
}
bool getOrderPartialRev(T first, std::queue<T>& queue) {
std::set<T> visited{};
sortUtilRev(first, visited, queue);
return true;
}
private:
std::map<T, std::vector<T>> m_edges;
void sortUtil(T idx, std::set<T> &visited, std::stack<T> &stack) {
visited.emplace(idx);
for (auto dep : m_edges.at(idx)) {
if (!visited.contains(dep))
sortUtil(dep, visited, stack);
}
stack.push(idx);
}
void sortUtilRev(T idx, std::set<T> &visited, std::queue<T> &stack) {
visited.emplace(idx);
for (auto dep : m_edges.at(idx)) {
if (!visited.contains(dep))
sortUtilRev(dep, visited, stack);
}
stack.push(idx);
}
};
} // namespace fggl::ds
#endif //FGGL_DS_GRAPH_HPP
......@@ -37,7 +37,7 @@ namespace fggl::ds {
}
WeakRef allocate() {
if ( N <= m_data.size() ) {
if (N <= m_data.size()) {
assert(0 && "Fake slot map emulated out of space");
return BAD_INDEX;
}
......@@ -50,13 +50,13 @@ namespace fggl::ds {
m_data.erase(idx);
}
T& get(WeakRef idx) const {
assert( valid(idx) );
T &get(WeakRef idx) const {
assert(valid(idx));
return m_data[idx];
}
T* tryGet(WeakRef idx) const {
if( valid(idx) ) {
T *tryGet(WeakRef idx) const {
if (valid(idx)) {
return &m_data[idx];
}
return nullptr;
......@@ -67,7 +67,6 @@ namespace fggl::ds {
std::size_t m_nextIdx = 1;
};
} // namespace fggl::ds
#endif //FGGL_DS_PLACEHOLDER_HPP
......@@ -20,6 +20,10 @@
#define FGGL_ENTITY_ENTITY_HPP
#include <cstdint>
#include <map>
#include <vector>
#include "fggl/debug/logging.hpp"
#include "fggl/vendor/entt.hpp"
namespace fggl::entity {
......@@ -32,33 +36,41 @@ namespace fggl::entity {
inline EntityID create() {
return m_registry.create();
}
inline void destroy(EntityID entity) {
m_registry.destroy(entity);
}
template<typename Component, typename... Args>
inline Component& add(EntityID entity, Args&&... args) {
return m_registry.emplace<Component>(entity, std::forward<Args>(args)...);
inline Component &add(EntityID entity, Args &&... args) {
return m_registry.get_or_emplace<Component>(entity, std::forward<Args>(args)...);
}
template<typename Component>
Component& get(EntityID entity) {
Component &get(EntityID entity) {
#ifndef NDEBUG
if (!has<Component>(entity)) {
debug::error("Entity {} has no component of type {}", (uint64_t) entity, debug::demangle(typeid(Component).name()));
assert(false && "Entity was missing component - use tryGet or fix definition");
}
#endif
return m_registry.get<Component>(entity);
}
template<typename Component>
const Component& get(EntityID entity) const {
const Component &get(EntityID entity) const {
return m_registry.get<Component>(entity);
}
template<typename Component>
Component* tryGet(EntityID entity) {
Component *tryGet(EntityID entity) {
return m_registry.try_get<Component>(entity);
}
template<typename Component>
const Component* tryGet(EntityID entity) const {
return m_registry.try_get<Component>(entity);
const Component *tryGet(EntityID entity, const Component* defaultValue = nullptr) const {
auto* comp = m_registry.try_get<Component>(entity);
return comp == nullptr ? defaultValue : comp;
}
template<typename ...Components>
......@@ -66,39 +78,91 @@ namespace fggl::entity {
return m_registry.view<Components...>();
}
EntityID findByName(const std::string& name) const {
auto itr = m_names.find(name);
if ( itr == m_names.end() ){
return INVALID;
}
return m_registry.valid(itr->second) ? itr->second : INVALID;
}
inline void setName(const std::string& name, EntityID eid ) {
if ( eid == INVALID ) {
m_names.erase(name);
} else {
assert(m_registry.valid(eid));
m_names[name] = eid;
}
}
template<typename ...Components>
bool has(EntityID idx) const {
return m_registry.template all_of<Components...>(idx);
}
inline bool exists(EntityID idx) {
return m_registry.valid(idx);
bool hasTag(EntityID entity, fggl::util::GUID tag) {
const auto mapItr = m_tags.find( tag );
if ( mapItr == m_tags.end() || !m_registry.valid(entity) ) {
return false;
}
return std::find(mapItr->second.begin(), mapItr->second.end(), entity) != mapItr->second.end();
}
void addTag(const EntityID entity, const fggl::util::GUID tag) {
assert( m_registry.valid(entity) );
auto& tagged = m_tags[ tag ];
tagged.push_back( entity );
}
void removeTag(const EntityID entity, const fggl::util::GUID tag) {
auto mapItr = m_tags.find(tag);
if ( mapItr == m_tags.end() ) {
return;
}
std::remove_if( mapItr->second.begin(), mapItr->second.end(), [entity](auto other) { return other == entity;} );
}
inline std::vector<EntityID> findByTag(const char* tag) {
return findByTag( util::make_guid_rt(tag) );
}
inline bool alive(EntityID idx) {
std::vector<EntityID> findByTag(const fggl::util::GUID tag){
auto mapItr = m_tags.find(tag);
if ( mapItr == m_tags.end() ) {
return {};
}
return mapItr->second;
}
inline bool exists(EntityID idx) const {
return m_registry.valid(idx);
}
inline bool alive(EntityID idx) const {
return m_registry.valid(idx);
}
private:
entt::registry m_registry;
std::map<std::string, EntityID> m_names;
std::map<fggl::util::GUID, std::vector<EntityID>> m_tags;
};
struct Entity {
static Entity make(EntityManager& manager, EntityID idx) {
static Entity make(EntityManager &manager, EntityID idx) {
return Entity{idx, manager};
}
EntityID id;
EntityManager& manager;
EntityManager &manager;
template<typename Component>
Component& get() {
Component &get() {
return manager.get<Component>(id);
}
template<typename Component>
const Component& get() const {
const Component &get() const {
return manager.get<Component>(id);
}
};
......
/*
* This file is part of FGGL.
*
* FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with FGGL.
* If not, see <https://www.gnu.org/licenses/>.
*/
//
// Created by webpigeon on 20/08/22.
//
#ifndef FGGL_ENTITY_GRIDWORLD_ZONE_HPP
#define FGGL_ENTITY_GRIDWORLD_ZONE_HPP
#include <array>
#include <vector>
#include "fggl/math/types.hpp"
#include "fggl/assets/types.hpp"
#include "fggl/entity/entity.hpp"
namespace fggl::entity::grid {
using GridPos = math::vec2i;
constexpr auto ASSET_TILESET = assets::AssetType::make("tileset");
template<typename T, uint32_t width, uint32_t height>
struct Grid {
public:
Grid() = default;
inline T& get(GridPos pos) {
assert(inBounds(pos));
return m_cells[getCellIndex(pos)];
}
const T& get(GridPos pos) const {
assert(inBounds(pos));
return m_cells[getCellIndex(pos)];
}
inline void set(GridPos pos, T value) {
assert(inBounds(pos));
m_cells[getCellIndex(pos)] = value;
}
inline bool inBounds(GridPos pos) const {
return 0 <= pos.x && pos.x <= size.x &&
0 <= pos.y && pos.y <= size.y;
}
private:
constexpr static math::vec2i size = math::vec2ui(width, height);
std::array<T, size.x * size.y> m_cells;
inline uint32_t getCellIndex(GridPos pos) const {
assert( inBounds(pos));
return pos.y * size.x + pos.x;
}
};
struct FloorTile {
constexpr static uint8_t IMPOSSIBLE = 0;
uint8_t moveCost = IMPOSSIBLE;
math::vec3 colour = gfx::colours::CYAN;
};
struct WallTile {
bool render = false;
math::vec3 colour = gfx::colours::CYAN;
};
struct WallState {
uint32_t north = 0;
uint32_t west = 0;
};
struct TileSet {
std::vector<FloorTile> m_floors;
std::vector<WallTile> m_walls;
};
/**
* A 2D representation of a space.
*
* @tparam width the grid width in units
* @tparam height the grid height in units
*/
template<uint32_t width, uint32_t height>
struct Area2D {
public:
constexpr static std::array<math::vec2i, 4> DIRECTIONS{{ {-1, 0}, {0, -1}, {1, 0}, {0, 1} }};
inline explicit Area2D(TileSet& tiles) : m_tiles(tiles) {
clear();
}
[[nodiscard]]
inline bool inBounds(GridPos pos) const {
return 0 <= pos.x && pos.x <= width
&& 0 <= pos.y && pos.y <= height;
}
void clear() {
WallState noWall;
for (auto xPos = 0U; xPos<width; ++xPos) {
for (auto yPos=0U; yPos<height; ++yPos) {
m_floors.set({xPos, yPos}, 0);
m_walls.set({xPos, yPos}, noWall);
}
}
}
inline FloorTile& floorAt(uint32_t xPos, uint32_t yPos) {
return m_tiles.m_floors.at( m_floors.get({xPos, yPos}) );
}
inline void setFloorAt(uint32_t xPos, uint32_t yPos, uint32_t floor) {
m_floors.set({xPos, yPos}, floor);
}
inline WallTile& wallAt(uint32_t xPos, uint32_t yPos, bool north) {
if (north) {
return m_tiles.m_walls.at(m_walls.get({xPos, yPos}).north);
} else {
return m_tiles.m_walls.at(m_walls.get({xPos, yPos}).west);
}
}
inline void setWallAt(uint32_t xPos, uint32_t yPos, bool north, uint32_t wall) {
auto& state = m_walls.get({xPos, yPos});
if (north) {
state.north = wall;
} else {
state.west = wall;
}
}
inline bool canMove(GridPos pos) const {
if ( !inBounds(pos) ) {
return false;
}
return m_tiles.m_floors[m_floors.get(pos)].moveCost != FloorTile::IMPOSSIBLE;
}
inline bool canMove(GridPos pos, math::vec2i dir) const {
return canMove(pos + dir) && !blocked(pos, dir);
}
inline bool blocked(GridPos pos, math::vec2i dir) const;
EntityManager& entities() {
return m_entities;
}
inline void neighbours(math::vec2i pos, std::vector<math::vec2i> &neighbours) const {
for (auto direction : DIRECTIONS) {
if ( canMove(pos, direction) ) {
auto result = pos + direction;
neighbours.push_back(result);
}
}
}
private:
TileSet& m_tiles;
Grid<uint32_t, width, height> m_floors;
Grid<WallState, width + 1, height + 1> m_walls;
EntityManager m_entities;
};
template<uint32_t width, uint32_t height>
bool Area2D<width, height>::blocked(GridPos pos, math::vec2i dir) const {
auto targetPos = pos;
if ( dir.x == 1 || dir.y == 1 ) {
targetPos = pos + dir;
}
if ( !inBounds(targetPos) ) {
return true;
}
auto& wallObj = m_walls.get(targetPos);
if ( dir.y != 0 ) {
return wallObj.north != 0;
}
if (dir.x != 0) {
return wallObj.west != 0;
}
return true;
}
} // namespace fggl::entity::gridworld
#endif //FGGL_ENTITY_GRIDWORLD_ZONE_HPP
......@@ -31,61 +31,150 @@
namespace fggl::entity {
constexpr auto PROTOTYPE_ASSET = assets::AssetType::make("entity_prototype");
using FactoryFunc = std::function<void(const ComponentSpec& config, EntityManager&, const EntityID&)>;
constexpr auto ENTITY_PROTOTYPE = assets::make_asset_type("entity/prototype");
constexpr auto ENTITY_SCENE = assets::make_asset_type("entity/scene");
using ComponentID = util::GUID;
using EntityType = util::GUID;
using FactoryFunc = std::function<void(const ComponentSpec &config, EntityManager &, const EntityID &, modules::Services &svc)>;
using CustomiseFunc = std::function<void(EntityManager &, const EntityID &)>;
struct FactoryInfo {
FactoryFunc factory;
CustomiseFunc finalise = nullptr;
};
class EntityFactory {
public:
constexpr static const modules::ModuleService service = modules::make_service("fggl::entity:Factory");
constexpr static const modules::ServiceName service = modules::make_service("fggl::entity:Factory");
EntityID create(const EntityType& spec, EntityManager& manager) {
try {
auto &prototype = m_prototypes.at(spec);
inline EntityFactory(modules::Services &services) : m_services(services) {}
// invoke each component factory as required
auto entity = manager.create();
for (auto &[name, data] : prototype.components) {
try {
m_factories.at(name)(data, manager, entity);
} catch (std::out_of_range& ex) {
#ifndef NDEBUG
debug::log(debug::Level::error, "EntityFactory: Unknown component factory type '{}'", fggl::util::guidToString(name));
#endif
manager.destroy(entity);
return fggl::entity::INVALID;
}
}
return entity;
} catch (std::out_of_range& ex) {
#ifndef NDEBUG
debug::log(debug::Level::error, "EntityFactory: Unknown entity type '{}'", fggl::util::guidToString(spec));
#endif
return fggl::entity::INVALID;
EntityID create(const EntityType &spec, EntityManager &manager, const CustomiseFunc &customise = nullptr) {
std::vector<CustomiseFunc> finishers;
// build the setup
auto entity = setupComponents(spec, manager, finishers);
if ( entity == entity::INVALID ) {
debug::error("EntityFactory: failed to build from prototype {}", std::to_string(spec.get()));
return entity::INVALID;
}
// if requested, allow the user to customize the creation
if ( customise != nullptr ) {
customise(manager, entity);
}
// run finishers for components
for ( auto& finisher : finishers ) {
finisher( manager, entity );
}
// use metadata to finalise
processTags(manager, entity, spec);
return entity;
}
void processTags(EntityManager& manager, EntityID id, EntityType spec) {
auto type = m_prototypes.at(spec);
for ( auto& tag : type.tags ) {
manager.addTag(id, tag);
}
}
void define(EntityType type, const EntitySpec& spec) {
void log_known_types() const {
debug::debug("dumping known types:");
for(const auto& [k,v] : m_factories) {
debug::debug("\ttype: {}", k);
}
}
void define(EntityType type, const EntitySpec &spec) {
m_prototypes[type] = spec;
}
// ability to set and unset factory functions
inline void bind(const ComponentID& configNode, FactoryFunc factory) {
m_factories[configNode] = std::move(factory);
inline void bind(const ComponentID &configNode, FactoryFunc factory, CustomiseFunc finalise = nullptr) {
m_factories[configNode].factory = std::move(factory);
m_factories[configNode].finalise = std::move(finalise);
}
inline void unbind(const ComponentID& configNode) {
inline void unbind(const ComponentID &configNode) {
m_factories.erase(configNode);
}
inline FactoryInfo& getInfo(ComponentID comp) {
return m_factories.at(comp);
}
private:
std::map<ComponentID, FactoryFunc> m_factories;
modules::Services m_services;
std::map<ComponentID, FactoryInfo> m_factories;
std::map<EntityType, EntitySpec> m_prototypes;
entity::EntityID setupComponents(EntityType entityType,
EntityManager &manager,
std::vector<CustomiseFunc> &finishers) {
assert(entityType != NO_PARENT && "setup components called with NO_PARENT?!");
auto entity = manager.create();
std::vector<ComponentID> loadedComps;
auto currentType = entityType;
while (currentType != NO_PARENT) {
const auto& specEntry = m_prototypes.find( currentType );
if ( specEntry == m_prototypes.end() ) {
debug::warning("Asked to setup {}, for {} but was not a known prototype", specEntry->first, entityType);
return entity::INVALID;
}
auto entitySpec = specEntry->second;
debug::debug("constructing {} for {} ({} comps)", currentType, entityType, entitySpec.components.size());
assert( entitySpec.ordering.size() == entitySpec.components.size() && "ordering incorrect size, bad things happend!" );
for (auto &component : entitySpec.ordering) {
// skip comps loaded by children
if (std::find(loadedComps.begin(), loadedComps.end(), component) != loadedComps.end()) {
continue;
}
try {
auto &data = getComponent(entitySpec, component);
loadedComps.push_back(component);
auto &info = m_factories.at(component);
info.factory(data, manager, entity, m_services);
if (info.finalise != nullptr) {
finishers.push_back(info.finalise);
}
} catch (std::out_of_range &ex) {
debug::error( "EntityFactory: Unknown component factory type '{}'", component );
log_known_types();
manager.destroy(entity);
return entity::INVALID;
}
}
currentType = entitySpec.parent;
}
return entity;
}
ComponentSpec &getComponent(EntitySpec &prototype, util::GUID compToken) {
auto compItr = prototype.components.find(compToken);
if (compItr != prototype.components.end()) {
return compItr->second;
}
if (prototype.parent == NO_PARENT) {
throw std::out_of_range("EntityFactory: no such component!");
}
return getComponent(m_prototypes.at(prototype.parent), compToken);
}
};
assets::AssetRefRaw load_prototype(EntityFactory* factory, const assets::AssetGUID& guid, assets::AssetData data);
assets::AssetRefRaw load_prototype(assets::Loader* loader, const assets::AssetID &guid, const assets::LoaderContext& data, EntityFactory* factory);
assets::AssetRefRaw load_scene(assets::Loader* loader, const assets::AssetID& asset, const assets::LoaderContext& data, void* ptr);
} // namespace fggl::entity
......
......@@ -23,12 +23,13 @@
#include "fggl/math/types.hpp"
#include "fggl/data/model.hpp"
#include "fggl/phys/types.hpp"
namespace YAML {
template<>
struct convert<fggl::math::vec3> {
static Node encode(const fggl::math::vec3& rhs) {
static Node encode(const fggl::math::vec3 &rhs) {
Node node;
node.push_back(rhs.x);
node.push_back(rhs.y);
......@@ -36,41 +37,41 @@ namespace YAML {
return node;
}
static bool decode(const Node& node, fggl::math::vec3& rhs) {
static bool decode(const Node &node, fggl::math::vec3 &rhs) {
if (!node.IsSequence() || node.size() != 3) {
return false;
}
rhs.x = node[0].as<float>();
rhs.y = node[0].as<float>();
rhs.z = node[0].as<float>();
rhs.y = node[1].as<float>();
rhs.z = node[2].as<float>();
return true;
}
};
template<>
struct convert<fggl::math::vec2> {
static Node encode(const fggl::math::vec2& rhs) {
static Node encode(const fggl::math::vec2 &rhs) {
Node node;
node.push_back(rhs.x);
node.push_back(rhs.y);
return node;
}
static bool decode(const Node& node, fggl::math::vec2& rhs) {
static bool decode(const Node &node, fggl::math::vec2 &rhs) {
if (!node.IsSequence() || node.size() != 2) {
return false;
}
rhs.x = node[0].as<float>();
rhs.y = node[0].as<float>();
rhs.y = node[1].as<float>();
return true;
}
};
template<>
struct convert<fggl::data::Vertex> {
static Node encode(const fggl::data::Vertex& rhs) {
static Node encode(const fggl::data::Vertex &rhs) {
Node node;
node["position"] = rhs.posititon;
node["normal"] = rhs.normal;
......@@ -79,7 +80,7 @@ namespace YAML {
return node;
}
static bool decode(const Node& node, fggl::data::Vertex& rhs) {
static bool decode(const Node &node, fggl::data::Vertex &rhs) {
if (!node.IsSequence() || node.size() != 2) {
return false;
}
......@@ -92,18 +93,53 @@ namespace YAML {
}
};
template<>
struct convert<fggl::phys::BodyType> {
static Node encode(const fggl::phys::BodyType &rhs) {
Node node;
if (rhs == fggl::phys::BodyType::STATIC) {
node = "static";
} else if (rhs == fggl::phys::BodyType::DYNAMIC) {
node = "dynamic";
} else if (rhs == fggl::phys::BodyType::KINEMATIC) {
node = "kinematic";
}
return node;
}
static bool decode(const Node &node, fggl::phys::BodyType &rhs) {
auto strVal = node.as<std::string>();
if (strVal == "static") {
rhs = fggl::phys::BodyType::STATIC;
return true;
}
if (strVal == "dynamic") {
rhs = fggl::phys::BodyType::DYNAMIC;
return true;
}
if (strVal == "kinematic") {
rhs = fggl::phys::BodyType::KINEMATIC;
return true;
}
return false;
}
};
template<>
struct convert<fggl::util::GUID> {
static Node encode(const fggl::util::GUID& rhs) {
static Node encode(const fggl::util::GUID &rhs) {
Node node;
node = rhs.get();
return node;
}
static bool decode(const Node& node, fggl::util::GUID& rhs) {
static bool decode(const Node &node, fggl::util::GUID &rhs) {
auto longVal = node.as<uint64_t>(0);
if ( longVal == 0 ) {
if (longVal == 0) {
// probably meant to hash it...
auto stringVal = node.as<std::string>();
rhs = fggl::util::make_guid_rt(stringVal);
......
......@@ -26,27 +26,39 @@
namespace fggl::entity {
using ComponentID = util::GUID;
using EntityType = util::GUID;
constexpr EntityType NO_PARENT = util::make_guid("FGGL_NULL_PARENT");
struct ComponentSpec {
template<typename T>
T get(const std::string& key, const T& fallback) const {
T get(const std::string &key, const T &fallback) const {
return config[key].template as<T>(fallback);
}
template<typename T>
void set(const std::string& key, const T& value) {
void set(const std::string &key, const T &value) {
config[key] = value;
}
inline bool has(const std::string& key) const {
return (bool)(config[key]);
inline bool has(const std::string &key) const {
return (bool) (config[key]);
}
YAML::Node config;
};
struct EntitySpec {
std::map<util::GUID, ComponentSpec> components;
EntityType parent = NO_PARENT;
std::vector<util::GUID> tags;
std::vector<ComponentID> ordering;
std::map<ComponentID, ComponentSpec> components;
inline void addComp(ComponentID cmp, const ComponentSpec& spec) {
components[cmp] = spec;
ordering.push_back(cmp);
}
};
} // namespace fggl::entity
......
......@@ -22,23 +22,26 @@
#include "fggl/modules/module.hpp"
#include "fggl/assets/loader.hpp"
#include "fggl/assets/packed/adapter.hpp"
#include "fggl/entity/loader/loader.hpp"
namespace fggl::entity {
constexpr auto MIME_SCENE = assets::from_mime("x-fggl/scene");
struct ECS {
constexpr static const char* name = "fggl::entity::ECS";
constexpr static const std::array<modules::ModuleService, 1> provides = {
constexpr static const char *name = "fggl::entity::ECS";
constexpr static const std::array<modules::ServiceName, 1> provides = {
EntityFactory::service
};
constexpr static const std::array<modules::ModuleService, 1> depends = {
assets::Loader::service
constexpr static const std::array<modules::ServiceName, 2> depends = {
assets::Loader::service,
assets::CheckinAdapted::service
};
static const modules::ServiceFactory factory;
static bool factory(modules::ServiceName name, modules::Services &serviceManager);
};
void install_component_factories(EntityFactory* factory);
void install_component_factories(EntityFactory *factory);
} // namespace fggl::entity
......
......@@ -29,6 +29,7 @@
#include "fggl/audio/null_audio.hpp"
#include "fggl/audio/openal/module.hpp"
//! Root namespace
namespace fggl {
}
......
......@@ -27,36 +27,42 @@ namespace fggl::gfx {
float fov = glm::radians(45.0f);
float nearPlane = 0.1f;
float farPlane = 100.0f;
inline math::mat4 perspective() const {
return glm::perspective(fov, aspectRatio, nearPlane, farPlane);
}
};
inline math::mat4 calc_proj_matrix(const Camera& camera) {
inline math::mat4 calc_proj_matrix(const Camera &camera) {
return glm::perspective(camera.fov, camera.aspectRatio, camera.nearPlane, camera.farPlane);
}
inline math::Ray get_camera_ray(const entity::EntityManager& world, const entity::EntityID camera, math::vec2 position) {
auto& camTransform = world.get<fggl::math::Transform>(camera);
auto& camComp = world.get<fggl::gfx::Camera>(camera);
inline math::Ray get_camera_ray(const entity::EntityManager &world,
const entity::EntityID camera,
math::vec2 position) {
auto &camTransform = world.get<fggl::math::Transform>(camera);
auto &camComp = world.get<fggl::gfx::Camera>(camera);
const auto projMatrix = fggl::gfx::calc_proj_matrix(camComp);
const auto viewMatrix = fggl::math::calc_view_matrix(camTransform);
glm::vec4 startNDC {
glm::vec4 startNDC{
position.x,
position.y,
-1.0f,
1.0f
};
glm::vec4 endNDC {
glm::vec4 endNDC{
position.x,
position.y,
0.0f,
1.0f
};
fggl::math::mat4 M = glm::inverse( projMatrix * viewMatrix );
fggl::math::mat4 M = glm::inverse(projMatrix * viewMatrix);
glm::vec3 start = M * startNDC;
glm::vec3 end = M * endNDC;
return { start, glm::normalize(end - start) };
return {start, glm::normalize(end - start)};
}
};
......
......@@ -23,6 +23,7 @@
#include "fggl/entity/entity.hpp"
#include "fggl/modules/module.hpp"
//! Classes responsible for rendering content
namespace fggl::gfx {
struct Bounds {
......@@ -34,7 +35,7 @@ namespace fggl::gfx {
class Graphics {
public:
constexpr static const modules::ModuleService service = modules::make_service("fggl::gfx::Graphics");
constexpr static const auto service = modules::make_service("fggl::gfx::Graphics");
virtual ~Graphics() = default;
virtual void clear() = 0;
......@@ -43,10 +44,9 @@ namespace fggl::gfx {
virtual Bounds canvasBounds() = 0;
virtual void draw2D(const Paint &paint) = 0;
virtual void drawScene(entity::EntityManager&) = 0;
virtual void drawScene(entity::EntityManager &, bool debugMode = false) = 0;
};
} // namespace fggl::gfx
#endif //FGGL_GFX_INTERFACES_HPP
......@@ -24,7 +24,6 @@
* FGGL OpenGL 4.x rendering backend.
*/
namespace fggl::gfx {
}
#endif
......@@ -24,6 +24,7 @@
#endif
#include <glad/glad.h>
typedef void* (* GLADloadproc)(const char *name);
typedef void *(*GLADloadproc)(const char *name);
#endif