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 1009 additions and 944 deletions
#ifndef FGGL_ECS3_ECS_H
#define FGGL_ECS3_ECS_H
#include <fggl/ecs3/module/module.h>
#include <fggl/ecs3/prototype/world.h>
#include <fggl/math/types.hpp>
namespace fggl::ecs3 {
using World = prototype::World;
class ecsTypes : public Module {
public:
inline std::string name() const override {
return "ecs::core";
}
inline void onLoad(ModuleManager& manager, TypeRegistry& types) override {
}
};
}
#endif
\ No newline at end of file
//
// Created by webpigeon on 23/10/2021.
//
#ifndef FGGL_ECS3_CONTAINER_H
#define FGGL_ECS3_CONTAINER_H
#include <cstdint>
#include <cstdarg>
#include <cassert>
#include <fggl/ecs3/types.hpp>
namespace fggl::ecs3 {
class Container {
public:
const RecordIdentifier m_identifier;
Container(const TypeRegistry &reg, RecordIdentifier id) :
m_identifier(id),
m_types(reg),
backingStore(nullptr),
m_size(0),
m_capacity(0) {};
~Container() {
delete[] backingStore;
}
std::size_t create();
void remove(std::size_t pos);
std::size_t expand(Container &other, std::size_t otherPos, component_type_t newComp);
void contract(Container &other, std::size_t otherPos);
void ensure(std::size_t size);
inline unsigned char *data_raw(component_type_t type) {
auto seek_id = m_identifier.idx(type);
// you asked for something I don't contain...
if (seek_id == m_identifier.count) {
std::cerr << "asked for " << type << " from " << m_identifier << std::endl;
assert(seek_id != m_identifier.count);
return nullptr;
}
// figure out the offset
return backingStore + offsets[seek_id];
}
template<typename T>
inline T *data() {
auto comp_id = Component<T>::typeID();
return (T *) data_raw(comp_id);
}
template<typename T>
T *set(std::size_t entity, T *compData) {
auto *comps = data<T>();
auto entityPos = idx(entity);
auto compMeta = m_types.template meta<T>();
unsigned char *usrPtr = (unsigned char *) &comps[entityPos];
compMeta->destroy(usrPtr);
compMeta->move((unsigned char *) compData, usrPtr);
return &comps[entityPos];
}
[[nodiscard]]
inline std::size_t size() const {
return m_size;
}
inline std::size_t idx(entity_t entity) {
auto *entityData = data<EntityMeta>();
for (std::size_t i = 0; i < m_size; i++) {
if (entityData[i].id == entity) {
return i;
}
}
return m_size;
}
private:
const TypeRegistry &m_types;
unsigned char *backingStore;
std::size_t offsets[RecordIdentifier::MAX_COMPS]{};
std::size_t m_size;
std::size_t m_capacity;
void move(std::size_t newPos, Container &oldContainer, std::size_t oldPos);
};
}
#endif //FGGL_ECS3_CONTAINER_H
#ifndef FGGL_ECS_ECS3_H
#define FGGL_ECS_ECS3_H
#include <cstddef>
#include <cstdarg>
#include <cassert>
#include <cstring>
#include <map>
#include <algorithm>
#include <iostream>
#include <type_traits>
#include <fggl/ecs3/utils.hpp>
#include <fggl/ecs3/types.hpp>
#include <fggl/ecs3/fast/Container.h>
namespace fggl::ecs3::fast {
using entity_t = unsigned int;
constexpr entity_t NULL_ENTITY = 0;
class World {
public:
explicit World(TypeRegistry &reg) : m_registry(reg), m_last(NULL_ENTITY) {}
entity_t create() {
auto next = m_last++;
auto arch = make_id(1, Component<EntityMeta>::typeID());
auto &container = getContainer(arch);
m_entities[next] = container.m_identifier;
auto pos = container.create();
auto *entityMeta = container.data<EntityMeta>();
entityMeta[pos].id = next;
return next;
}
void remove(entity_t entity) {
auto arch = m_entities.at(entity);
auto container = m_records.at(arch);
auto entPos = container.idx(entity);
container.remove(entPos);
}
inline Container &getContainer(RecordIdentifier &arch) {
try {
return m_records.at(arch);
} catch (std::out_of_range &e) {
auto v = m_records.emplace(std::pair<RecordIdentifier, Container>(arch, {m_registry, arch}));
return v.first->second;
}
}
template<typename T>
T *add(const entity_t entity) {
auto currArch = m_entities.at(entity);
auto newArch = currArch.with<T>();
m_entities[entity] = newArch;
auto &oldContainer = m_records.at(currArch);
auto &newContainer = getContainer(newArch);
auto oldPos = oldContainer.idx(entity);
auto newPos = newContainer.expand(oldContainer, oldPos, Component<T>::typeID());
auto *data = newContainer.template data<T>();
return &data[newPos];
}
template<typename T>
T *set(const entity_t entity, T *record) {
auto currArch = m_entities.at(entity);
// check we already have that component type...
if (currArch.idx(Component<T>::typeID()) == currArch.count) {
add<T>(entity);
currArch = m_entities.at(entity);
}
auto &container = m_records.at(currArch);
auto pos = container.idx(entity);
return container.set<T>(pos, record);
}
template<typename T>
T *get(entity_t entity) {
auto currArch = m_entities.at(entity);
auto pos = currArch.idx(entity);
auto &container = m_records.at(currArch);
auto *data = container.template data<T>();
return &data[pos];
}
template<typename T>
const T *get(entity_t entity) const {
auto currArch = m_entities.at(entity);
auto pos = currArch.idx(entity);
auto &container = m_records.at(currArch);
auto *data = container.template data<T>();
return &data[pos];
}
template<typename T>
void remove(entity_t entity) {
auto currArch = m_entities.at(entity);
auto newArch = currArch.without<T>();
auto &oldContainer = m_records[currArch];
auto &newContainer = m_records[newArch];
auto oldPos = oldContainer.idx(entity);
auto newPos = newContainer.create();
m_records[newArch].contract(newPos, oldContainer, oldPos);
}
template<typename... T>
std::vector<entity_t> findMatching() const {
return {};
}
private:
TypeRegistry &m_registry;
std::map<RecordIdentifier, Container> m_records;
std::map<entity_t, RecordIdentifier> m_entities;
entity_t m_last{};
};
}
#endif
//
// Created by webpigeon on 23/10/2021.
//
#ifndef FGGL_ECS3_MODULE_H
#define FGGL_ECS3_MODULE_H
#include <string>
#include <map>
#include <memory>
#include <fggl/ecs3/types.hpp>
namespace fggl::ecs3 {
class Module {
public:
virtual ~Module() = default;
[[nodiscard]] virtual std::string name() const = 0;
virtual void onLoad(ModuleManager &manager, TypeRegistry &tr) {};
virtual void onUpdate();
virtual void onFrameStart();
virtual void onFrameEnd();
};
class ModuleManager {
public:
explicit ModuleManager(TypeRegistry &types) : m_types(types), m_modules() {}
~ModuleManager() = default;
template<typename C, typename... Args>
std::shared_ptr<C> load(Args &... args) {
auto ptr = std::make_shared<C>(args...);
m_modules[ptr->name()] = ptr;
ptr->onLoad(*this, m_types);
//spdlog::info("loaded ECS module: {}", ptr->name());
return ptr;
}
template<typename C>
void onAdd(const callback_t &cb) {
m_types.callbackAdd(Component<C>::typeID(), cb);
}
void onUpdate() {
for (auto &[id, ptr] : m_modules) {
ptr->onUpdate();
}
}
void onFrameStart() {
for (auto &[id, ptr] : m_modules) {
ptr->onFrameStart();
}
}
void onFrameEnd() {
for (auto &[id, ptr] : m_modules) {
ptr->onFrameEnd();
}
}
private:
TypeRegistry &m_types;
std::map<std::string, std::shared_ptr<Module>> m_modules;
};
}
#endif //FGGL_ECS3_MODULE_H
//
// Created by webpigeon on 23/10/2021.
//
#ifndef FGGL_ECS3_PROTOTYPE_WORLD_H
#define FGGL_ECS3_PROTOTYPE_WORLD_H
#include <map>
#include <fggl/ecs3/types.hpp>
/**
* A component based implementation of a game world.
*
* This is not a true ECS but exposes a similar API to it for testing (with a lot less headaches).
*/
namespace fggl::ecs3::prototype {
class Entity {
public:
bool m_abstract;
explicit Entity(entity_t id) : m_abstract(false), m_id(id) {};
Entity(const Entity &entity) : m_id(entity.m_id), m_components(entity.m_components) {
//spdlog::info("entity created fro copy: {}", m_id);
}
~Entity() = default;
template<typename C>
C *add() {
C *ptr = new C();
m_components[Component<C>::typeID()] = ptr;
return ptr;
}
void *add(std::shared_ptr<ComponentBase> t) {
void *ptr = t->construct();
m_components[t->id()] = ptr;
return ptr;
}
template<typename C>
C *set(const C *ptr) {
C *newPtr = new C(*ptr);
m_components[Component<C>::typeID()] = newPtr;
return newPtr;
}
void *set(const std::shared_ptr<ComponentBase> &t, const void *ptr) {
void *newPtr = t->copyConstruct(ptr);
m_components[t->id()] = newPtr;
return newPtr;
}
template<typename C>
C *get() const {
void *ptr = m_components.at(Component<C>::typeID());
return (C *) ptr;
}
template<typename C>
void remove(){
m_components.erase(Component<C>::typeID());
}
inline void *get(component_type_t t) {
return m_components.at(t);
}
std::vector<component_type_t> getComponentIDs() {
std::vector<component_type_t> comps{};
for (auto &[k, _] : m_components) {
comps.push_back(k);
}
return comps;
}
bool hasComponents(std::vector<component_type_t> &Cs) const {
for (auto c : Cs) {
if (m_components.find(c) == m_components.end()) {
return false;
}
}
return true;
}
private:
entity_t m_id;
std::map<component_type_t, void *> m_components;
};
class World {
public:
explicit World(TypeRegistry &reg) : m_types(reg), m_next(1), m_entities() {};
~World() = default;
entity_t create(bool abstract) {
auto nextID = m_next++;
m_entities.emplace(nextID, nextID);
auto &entity = m_entities.at(nextID);
entity.m_abstract = abstract;
// meta data
auto *meta = entity.add<ecs3::EntityMeta>();
meta->id = nextID;
meta->abstract = abstract;
return nextID;
}
entity_t copy(entity_t prototype) {
auto clone = create(false);
auto components = getComponents(prototype);
for (auto component : components) {
auto protoComp = get(prototype, component);
set(clone, component, protoComp);
}
return clone;
}
void destroy(entity_t entity) {
// TOOD resolve and clean components
m_entities.erase(entity);
}
inline TypeRegistry &types() {
return m_types;
}
std::vector<entity_t> all() {
std::vector<entity_t> entities{};
for (auto &[eid, entity] : m_entities) {
entities.push_back(eid);
}
return entities;
}
std::vector<component_type_t> getComponents(entity_t entityID) {
std::vector<component_type_t> components{};
auto &entity = m_entities.at(entityID);
auto comps = entity.getComponentIDs();
for (auto id : comps) {
components.push_back(id);
}
return components;
}
template<typename... Cs>
std::vector<entity_t> findMatching() const {
// construct the key
std::vector<ecs::component_type_t> key;
(key.push_back(Component<Cs>::typeID()), ...);
// entities
std::vector<entity_t> entities{};
for (auto &[eid, entity] : m_entities) {
if (entity.hasComponents(key) && !entity.m_abstract) {
entities.push_back(eid);
}
}
return entities;
}
template<typename C>
C *add(entity_t entity_id) {
//spdlog::info("component '{}' added to '{}'", C::name, entity_id);
auto &entity = m_entities.at(entity_id);
auto comp = entity.template add<C>();
m_types.fireAdd(this, entity_id, Component<C>::typeID());
return comp;
}
void *add(entity_t entity_id, component_type_t component_id) {
auto meta = m_types.meta(component_id);
auto &entity = m_entities.at(entity_id);
void *ptr = entity.add(meta);
m_types.fireAdd(this, entity_id, meta->id());
return ptr;
}
template<typename C>
C *set(entity_t entity_id, const C *ptr) {
//spdlog::info("component '{}' set on '{}'", C::name, entity_id);
auto &entity = m_entities.at(entity_id);
auto comp = entity.set<C>(ptr);
m_types.fireAdd(this, entity_id, Component<C>::typeID());
return comp;
}
void *set(entity_t entity_id, component_type_t cid, const void *ptr) {
auto &entity = m_entities.at(entity_id);
auto cMeta = m_types.meta(cid);
auto comp = entity.set(cMeta, ptr);
m_types.fireAdd(this, entity_id, cid);
return comp;
}
template<typename C>
C* tryGet(entity_t entity_id) const {
try {
const auto &entity = m_entities.at(entity_id);
try {
return entity.get<C>();
} catch ( std::out_of_range& e ) {
return nullptr;
}
} catch ( std::out_of_range& e) {
std::cerr << "someone requested an component that didn't exist, entity was: " << entity_id << std::endl;
return nullptr;
}
}
template<typename C>
C *get(entity_t entity_id) const {
C* ptr = tryGet<C>(entity_id);
if (ptr == nullptr) {
std::cerr << "entity " << entity_id << " does not have component "<< C::name << std::endl;
}
return ptr;
}
template<typename C>
void remove(entity_t entity_id) {
try {
auto &entity = m_entities.at(entity_id);
try {
return entity.remove<C>();
} catch ( std::out_of_range& e ) {
std::cerr << "entity " << entity_id << " does not have component "<< C::name << std::endl;
}
} catch ( std::out_of_range& e) {
std::cerr << "tried to delete component on entity that didn't exist, entity was: " << entity_id << std::endl;
}
}
void *get(entity_t entity_id, component_type_t t) {
auto &entity = m_entities.at(entity_id);
return entity.get(t);
}
private:
TypeRegistry &m_types;
entity_t m_next;
std::map<entity_t, Entity> m_entities;
};
}
#endif //FGGL_ECS3_PROTOTYPE_WORLD_H
#ifndef FGGL_ECS3_TYPES_H
#define FGGL_ECS3_TYPES_H
#include <cstdarg>
#include <utility>
#include <functional>
#include <fggl/ecs/component.hpp>
#include <fggl/ecs3/utils.hpp>
#include <iostream>
#include <memory>
#include <algorithm>
#include <map>
#include <unordered_map>
namespace fggl::ecs3 {
namespace {
using namespace fggl::ecs;
};
namespace prototype {
class World;
}
using fggl::ecs::component_type_t;
class ModuleManager;
using callback_t = std::function<void(prototype::World *, ecs3::entity_t)>;
struct TypeCallbacks {
std::vector<callback_t> add;
};
// core component types
struct EntityMeta {
constexpr static const char name[] = "meta";
entity_t id;
bool abstract;
};
struct RecordIdentifier {
constexpr static std::size_t MAX_COMPS = 32;
component_type_t types[MAX_COMPS];
std::size_t count;
[[nodiscard]]
inline std::size_t idx(component_type_t t) const {
return utils::search(types, count, t);
}
template<typename T>
[[nodiscard]]
RecordIdentifier with() const {
// check the caller wasn't a muppet
const auto typeID = ecs::Component<T>::typeID();
if (idx(typeID) != count) {
return *this;
}
RecordIdentifier re{};
re.count = count + 1;
re.types[count] = ecs::Component<T>::typeID();
// add old types
for (std::size_t i = 0; i < count; ++i) {
re.types[i] = types[i];
}
std::sort(re.types, re.types + re.count);
return re;
}
template<typename T>
[[nodiscard]]
RecordIdentifier without() const {
// check the caller wasn't a muppet
const auto typeID = ecs::Component<T>::typeID();
const auto typeIdx = idx(typeID);
if (typeIdx == count) {
return *this;
}
RecordIdentifier re{};
re.count = count - 1;
// add old types
for (std::size_t i = 0, j = 0; i < count; ++i) {
if (typeIdx != i) {
re.types[j] = types[i];
j++;
}
}
std::sort(re.types, re.types + re.count);
return re;
}
bool operator<(const RecordIdentifier &other) const {
if (count < other.count) {
return true;
} else if (count > other.count) {
return false;
} else {
for (std::size_t i = 0; i < count; i++) {
if (types[i] != other.types[i]) {
return types[i] < other.types[i];
}
}
return false;
}
}
bool operator==(const RecordIdentifier &arg) const {
if (arg.count != count) {
return false;
}
for (std::size_t i = 0; i < count; i++) {
if (types[i] != arg.types[i]) {
return false;
}
}
return true;
}
bool operator!=(const RecordIdentifier &arg) const {
return !(*this == arg);
}
};
std::ostream &operator<<(std::ostream &out, RecordIdentifier const &curr);
inline RecordIdentifier make_id(std::size_t count, ...) {
assert(count < RecordIdentifier::MAX_COMPS);
RecordIdentifier re{};
std::va_list args;
va_start(args, count);
for (std::size_t i = 0; i < count; ++i) {
re.types[i] = va_arg(args, component_type_t);
}
va_end(args);
re.count = count;
std::sort(re.types, re.types + count);
return re;
}
class TypeRegistry {
public:
TypeRegistry() : m_last_virtual(9000), m_callbacks() {
// core types always exist
make<EntityMeta>();
}
template<typename T>
void make() {
auto type_id = Component<T>::typeID();
if (m_types.find(type_id) != m_types.end())
return;
m_types[type_id] = std::make_shared<Component<T>>();
}
template<typename T>
bool exists() {
auto type_id = Component<T>::typeID();
return m_types.find(type_id) != m_types.end();
}
template<typename T>
std::shared_ptr<fggl::ecs::ComponentBase> meta() const {
auto type_id = Component<T>::typeID();
return m_types.at(type_id);
}
inline std::shared_ptr<fggl::ecs::ComponentBase> meta(component_type_t type_id) const {
try {
return m_types.at(type_id);
} catch (std::out_of_range& err) {
std::cerr << "asked for metadata on type " << type_id << " but no such type is in the type system" << std::endl;
return nullptr;
}
}
inline component_type_t find(const char *name) const {
for (auto &[type, meta] : m_types) {
if (std::strcmp(name, meta->name()) == 0) {
return type;
}
}
std::cerr << "asked for unknown component type: " << name << std::endl;
return 0;
}
inline void make_virtual(std::shared_ptr<ComponentBase> vtype) {
auto type_id = m_last_virtual++;
m_types[type_id] = std::move(vtype);
}
void callbackAdd(component_type_t component, const callback_t &callback) {
m_callbacks[component].add.push_back(callback);
}
void fireAdd(prototype::World *world, entity_t entity, component_type_t type) {
try {
auto &callbacks = m_callbacks.at(type).add;
for (auto &callback : callbacks) {
callback(world, entity);
}
} catch (std::out_of_range &e) {
//spdlog::debug("no callbacks for {}", m_types[type]->name());
}
}
private:
std::unordered_map<component_type_t, std::shared_ptr<fggl::ecs::ComponentBase>> m_types;
component_type_t m_last_virtual;
std::map<component_type_t, TypeCallbacks> m_callbacks;
};
};
#endif
#ifndef FGGL_UTILS_H
#define FGGL_UTILS_H
#include <cstddef>
namespace fggl::utils {
template<typename T>
std::size_t search(const T *data, const std::size_t size, const T &v) {
// empty list == not found
if (size == 0) {
return size;
}
std::size_t left = 0;
std::size_t right = size - 1;
while (left <= right) {
std::size_t m = (left + right) / 2;
if (data[m] == v) {
return m;
} else if (v < data[m]) {
if (m == 0) {
return size;
}
right = m - 1;
} else {
left = m + 1;
}
}
return size;
}
}
#endif
/*
* 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 24/07/22.
//
#ifndef FGGL_ENTITY_ENTITY_HPP
#define FGGL_ENTITY_ENTITY_HPP
#include <cstdint>
#include <map>
#include <vector>
#include "fggl/debug/logging.hpp"
#include "fggl/vendor/entt.hpp"
namespace fggl::entity {
using EntityID = entt::entity;
constexpr EntityID INVALID = entt::null;
class EntityManager {
public:
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.get_or_emplace<Component>(entity, std::forward<Args>(args)...);
}
template<typename Component>
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 {
return m_registry.get<Component>(entity);
}
template<typename Component>
Component *tryGet(EntityID entity) {
return m_registry.try_get<Component>(entity);
}
template<typename Component>
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>
auto find() const {
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);
}
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) );
}
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) {
return Entity{idx, manager};
}
EntityID id;
EntityManager &manager;
template<typename Component>
Component &get() {
return manager.get<Component>(id);
}
template<typename Component>
const Component &get() const {
return manager.get<Component>(id);
}
};
} // namespace fggl::entity
#endif //FGGL_ENTITY_ENTITY_HPP
/*
* 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
/*
* 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 23/07/22.
//
#ifndef FGGL_ENTITY_LOADER_LOADER_HPP
#define FGGL_ENTITY_LOADER_LOADER_HPP
#include <functional>
#include <map>
#include <utility>
#include "fggl/util/guid.hpp"
#include "fggl/modules/module.hpp"
#include "fggl/entity/entity.hpp"
#include "fggl/entity/loader/spec.hpp"
#include "fggl/assets/loader.hpp"
namespace fggl::entity {
constexpr auto ENTITY_PROTOTYPE = assets::make_asset_type("entity/prototype");
constexpr auto ENTITY_SCENE = assets::make_asset_type("entity/scene");
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::ServiceName service = modules::make_service("fggl::entity:Factory");
inline EntityFactory(modules::Services &services) : m_services(services) {}
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 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, CustomiseFunc finalise = nullptr) {
m_factories[configNode].factory = std::move(factory);
m_factories[configNode].finalise = std::move(finalise);
}
inline void unbind(const ComponentID &configNode) {
m_factories.erase(configNode);
}
inline FactoryInfo& getInfo(ComponentID comp) {
return m_factories.at(comp);
}
private:
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(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
#endif //FGGL_ENTITY_LOADER_LOADER_HPP
/*
* 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 24/07/22.
//
#ifndef FGGL_ENTITY_LOADER_SERIALISE_HPP
#define FGGL_ENTITY_LOADER_SERIALISE_HPP
#include "yaml-cpp/yaml.h"
#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) {
Node node;
node.push_back(rhs.x);
node.push_back(rhs.y);
node.push_back(rhs.z);
return node;
}
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[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) {
Node node;
node.push_back(rhs.x);
node.push_back(rhs.y);
return node;
}
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[1].as<float>();
return true;
}
};
template<>
struct convert<fggl::data::Vertex> {
static Node encode(const fggl::data::Vertex &rhs) {
Node node;
node["position"] = rhs.posititon;
node["normal"] = rhs.normal;
node["colour"] = rhs.colour;
node["texPos"] = rhs.texPos;
return node;
}
static bool decode(const Node &node, fggl::data::Vertex &rhs) {
if (!node.IsSequence() || node.size() != 2) {
return false;
}
rhs.posititon = node["position"].as<fggl::math::vec3>();
rhs.normal = node["normal"].as<fggl::math::vec3>();
rhs.colour = node["colour"].as<fggl::math::vec3>();
rhs.texPos = node["texPos"].as<fggl::math::vec2>();
return true;
}
};
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) {
Node node;
node = rhs.get();
return node;
}
static bool decode(const Node &node, fggl::util::GUID &rhs) {
auto longVal = node.as<uint64_t>(0);
if (longVal == 0) {
// probably meant to hash it...
auto stringVal = node.as<std::string>();
rhs = fggl::util::make_guid_rt(stringVal);
return true;
}
// it's probably pre-hashed...
rhs = fggl::util::GUID::make(longVal);
return true;
}
};
}
#endif //FGGL_ENTITY_LOADER_SERIALISE_HPP
/*
* 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 23/07/22.
//
#ifndef FGGL_ENTITY_LOADER_SPEC_HPP
#define FGGL_ENTITY_LOADER_SPEC_HPP
#include "fggl/util/guid.hpp"
#include "fggl/entity/loader/serialise.hpp"
#include <map>
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 {
return config[key].template as<T>(fallback);
}
template<typename T>
void set(const std::string &key, const T &value) {
config[key] = value;
}
inline bool has(const std::string &key) const {
return (bool) (config[key]);
}
YAML::Node config;
};
struct EntitySpec {
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
#endif //FGGL_ENTITY_LOADER_SPEC_HPP
/*
* 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 24/07/22.
//
#ifndef FGGL_ENTITY_MODULE_HPP
#define FGGL_ENTITY_MODULE_HPP
#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::ServiceName, 1> provides = {
EntityFactory::service
};
constexpr static const std::array<modules::ServiceName, 2> depends = {
assets::Loader::service,
assets::CheckinAdapted::service
};
static bool factory(modules::ServiceName name, modules::Services &serviceManager);
};
void install_component_factories(EntityFactory *factory);
} // namespace fggl::entity
#endif //FGGL_ENTITY_MODULE_HPP
/**
* FOSS Galaxy Game Library
* Copyright (c) 2022 Joseph Walton-Rivers
* webpigeon@fossgalaxy.com
*
* This program 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.
/*
* This file is part of FGGL.
*
* This program 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 this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* 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/>.
*/
#ifndef FGGL_H
#define FGGL_H
#ifndef FGGL_HPP
#define FGGL_HPP
#include "fggl/app.hpp"
// module system
#include "fggl/modules/manager.hpp"
#include "fggl/data/module.hpp"
#include "fggl/input/module.hpp"
#include "fggl/gui/module.hpp"
#include "fggl/display/glfw/module.hpp"
#include "fggl/gfx/ogl4/module.hpp"
#include <fggl/app.hpp>
#include "fggl/audio/null_audio.hpp"
#include "fggl/audio/openal/module.hpp"
//! Root namespace
namespace fggl {
}
......
#ifndef FGGL_GFX_ATLAS_H
#define FGGL_GFX_ATLAS_H
/*
* 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/>.
*/
#ifndef FGGL_GFX_ATLAS_HPP
#define FGGL_GFX_ATLAS_HPP
#include <fggl/math/types.hpp>
#include <vector>
......
#ifndef FGGL_GFX_CAMERA_H
#define FGGL_GFX_CAMERA_H
/*
* 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/>.
*/
#include <fggl/math/types.hpp>
#ifndef FGGL_GFX_CAMERA_HPP
#define FGGL_GFX_CAMERA_HPP
#include "fggl/math/types.hpp"
#include "fggl/entity/entity.hpp"
namespace fggl::gfx {
......@@ -12,8 +27,44 @@ 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) {
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);
const auto projMatrix = fggl::gfx::calc_proj_matrix(camComp);
const auto viewMatrix = fggl::math::calc_view_matrix(camTransform);
glm::vec4 startNDC{
position.x,
position.y,
-1.0f,
1.0f
};
glm::vec4 endNDC{
position.x,
position.y,
0.0f,
1.0f
};
fggl::math::mat4 M = glm::inverse(projMatrix * viewMatrix);
glm::vec3 start = M * startNDC;
glm::vec3 end = M * endNDC;
return {start, glm::normalize(end - start)};
}
};
#endif
#ifndef FGGL_GFX_COMMON_H
#define FGGL_GFX_COMMON_H
/*
* 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/>.
*/
#ifndef FGGL_GFX_COMMON_HPP
#define FGGL_GFX_COMMON_HPP
//
// This file exists to ensure that dependencies are loaded in the correct order.
......
#ifndef FGGL_GFX_COMPAT_HPP
#define FGGL_GFX_COMPAT_HPP
/**
* Window management Calls.
*
* This shouldn't be exposed to the demo app, but the ECS we're using isn't smart enouph to allow us to
* abstract this yet. It's next thing on the list, but this branch is about cleaning up OpenGL not about
* extending our ECS.
*
* Should be removed when the engine has suitable abstractions in place.
*/
#include <memory>
#include <utility>
#include <fggl/gfx/window.hpp>
#include <fggl/ecs3/ecs.hpp>
namespace fggl::gfx {
//
// fake module support - allows us to still RAII
//
struct ecsGlfwModule : ecs3::Module {
inline explicit
ecsGlfwModule(std::shared_ptr<fggl::input::Input> inputs) : context( std::make_shared<GlfwContext>(std::move(inputs))) {
}
inline
std::unique_ptr<GlfwWindow> createWindow(const std::string &title) {
std::unique_ptr<GlfwWindow> window = std::make_unique<GlfwWindow>(context);
window->title(title);
return std::move(window);
}
void onUpdate() override {
context->pollEvents();
}
[[nodiscard]]
std::string name() const override {
return "gfx::glfw";
}
private:
std::shared_ptr<GlfwContext> context;
};
}
#endif
#ifndef FGGL_GFX_INPUT_H
#define FGGL_GFX_INPUT_H
/*
* 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/>.
*/
#ifndef FGGL_GFX_INPUT_HPP
#define FGGL_GFX_INPUT_HPP
#include <array>
#include <bitset>
......
/*
* 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 27/06/22.
//
#ifndef FGGL_GFX_INTERFACES_HPP
#define FGGL_GFX_INTERFACES_HPP
#include "fggl/gfx/paint.hpp"
#include "fggl/entity/entity.hpp"
#include "fggl/modules/module.hpp"
//! Classes responsible for rendering content
namespace fggl::gfx {
struct Bounds {
float top;
float right;
float bottom;
float left;
};
class Graphics {
public:
constexpr static const auto service = modules::make_service("fggl::gfx::Graphics");
virtual ~Graphics() = default;
virtual void clear() = 0;
virtual void resize(int width, int height) = 0;
virtual Bounds canvasBounds() = 0;
virtual void draw2D(const Paint &paint) = 0;
virtual void drawScene(entity::EntityManager &, bool debugMode = false) = 0;
};
} // namespace fggl::gfx
#endif //FGGL_GFX_INTERFACES_HPP