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 805 additions and 206 deletions
/*
* 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 22/10/22.
// FIXME HACKY IMPLEMENTATION DETAIL BECAUSE THE ASSET LOADING PIPELINE IS BAD
//
#include "fggl/mesh/mesh.hpp"
#ifndef FGGL_MESH_COMPONENTS_HPP
#define FGGL_MESH_COMPONENTS_HPP
namespace fggl::mesh {
struct StaticMesh3D {
constexpr static const char name[] = "StaticMesh3D";
constexpr static const util::GUID guid = util::make_guid(name);
util::GUID meshReference;
Mesh3D mesh;
std::string pipeline;
inline StaticMesh3D() = default;
inline StaticMesh3D(const Mesh3D &aMesh, std::string aPipeline) :
mesh(aMesh), pipeline(std::move(aPipeline)) {}
};
struct StaticMultiMesh3D {
constexpr static const char name[] = "StaticMultiMesh3D";
constexpr static const util::GUID guid = util::make_guid(name);
util::GUID meshReference;
MultiMesh3D mesh;
std::string pipeline;
inline StaticMultiMesh3D() = default;
inline StaticMultiMesh3D(const MultiMesh3D &aMesh, std::string aPipeline) :
mesh(aMesh), pipeline(std::move(aPipeline)) {}
};
}
#endif //FGGL_MESH_COMPONENTS// _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 18/10/22.
//
#ifndef FGGL_MESH_MESH_HPP
#define FGGL_MESH_MESH_HPP
#include "fggl/math/types.hpp"
#include "fggl/assets/types.hpp"
#include <vector>
#include <span>
namespace fggl::mesh {
struct Vertex3D {
math::vec3 position;
math::vec3 normal;
math::vec3 colour{ 1.0F, 1.0F, 1.0F };
math::vec2 texPos{ NAN, NAN };
static Vertex3D from_pos(math::vec3 pos) {
return {
.position = pos,
.normal {NAN, NAN, NAN},
.colour {1.0F, 1.0F, 1.0F},
.texPos { pos.x, pos.z }
};
}
};
struct Vertex2D {
math::vec2 position;
math::vec2 colour;
math::vec2 texPos;
};
enum TextureType {
DIFFUSE, NORMAL
};
constexpr auto MISSING_TEXTURE = assets::AssetID::make(0);
struct Material {
std::string name;
math::vec3 ambient;
math::vec3 diffuse;
math::vec3 specular;
std::vector<assets::AssetID> diffuseTextures{};
std::vector<assets::AssetID> normalTextures{};
std::vector<assets::AssetID> specularTextures{};
inline assets::AssetID getPrimaryDiffuse() {
assert( !diffuseTextures.empty() );
return diffuseTextures.empty() ? MISSING_TEXTURE : diffuseTextures[0];
}
inline assets::AssetID getPrimaryNormals() {
assert( !normalTextures.empty() );
return normalTextures.empty() ? MISSING_TEXTURE : normalTextures[0];
}
inline assets::AssetID getPrimarySpecular() {
assert( !specularTextures.empty() );
return specularTextures.empty() ? MISSING_TEXTURE : specularTextures[0];
}
};
template<typename VertexFormat>
struct Mesh {
std::vector<VertexFormat> data;
std::vector<uint32_t> indices;
assets::AssetID material;
inline uint32_t append(const VertexFormat& vert) {
auto nextIdx = data.size();
data.push_back(vert);
return nextIdx;
}
};
template<typename MeshFormat>
struct MultiMesh {
std::vector<MeshFormat> meshes;
std::vector<assets::AssetID> materials;
MeshFormat& generate() {
return meshes.template emplace_back();
}
};
using Mesh2D = Mesh<Vertex2D>;
using MultiMesh2D = MultiMesh<Mesh2D>;
using Mesh3D = Mesh<Vertex3D>;
using MultiMesh3D = MultiMesh<Mesh3D>;
}
#endif //FGGL_MESH_MESH_HPP
......@@ -21,7 +21,9 @@
#include "fggl/modules/module.hpp"
#include "fggl/debug/logging.hpp"
#include "fggl/ds/graph.hpp"
#include <cassert>
#include <queue>
#include <vector>
#include <map>
......@@ -31,101 +33,53 @@
namespace fggl::modules {
template<typename T>
class DependencyGraph {
/**
* Store and initialise modules present in the engine.
*
* This class is responsible for keeping track of which modules the library user has requested, and ensuring that
* their dependencies are loaded in the correct order. Once the dependency graph has been built and instances
* created, it is responsible for providing references to these initialised classes.
*/
class Manager {
public:
DependencyGraph() = default;
void clear() {
m_dependencies.clear();
}
void addAll(const T& name, const std::vector<T>& dependencies) {
auto existing = m_dependencies.find(name);
if (existing == m_dependencies.end()) {
m_dependencies[name] = dependencies;
} else {
existing->second.insert(existing->second.end(), dependencies.begin(), dependencies.end());
}
}
void add(const T& name, const T& depends) {
m_dependencies[name].push_back(depends);
}
bool getOrder(std::stack<T>& stack) {
std::set<T> visited{};
for (const auto& module : m_dependencies) {
if (!visited.contains(module.first)) {
sortUtil( module.first, visited, stack);
}
}
return true;
}
Manager() = default;
bool getOrderRev(std::queue<T>& stack) {
std::set<T> visited{};
template<ServiceType T>
class Service {
public:
inline Service(Manager* manager) : m_manager(manager) {}
for (const auto& module : m_dependencies) {
if (!visited.contains(module.first)) {
sortUtilRev( module.first, visited, stack);
inline T* operator->() {
if ( m_ptr == nullptr ) {
m_ptr = m_manager->get<T>();
}
return m_ptr;
}
}
return true;
}
private:
std::map<T, std::vector<T>> m_dependencies;
void sortUtil(T idx, std::set<T>& visited, std::stack<T>& stack) {
visited.emplace(idx);
private:
Manager* m_manager;
std::shared_ptr<T> m_ptr;
};
for ( auto dep : m_dependencies.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_dependencies.at(idx)) {
if ( !visited.contains(dep) )
sortUtilRev(dep, visited, stack);
}
stack.push(idx);
}
};
class Manager {
public:
Manager() = default;
inline void addVirtual(const Config& config) {
assert( !m_locked );
inline void addVirtual(const Config &config) {
assert(!m_locked);
m_modules[config.name] = config;
for ( const auto& service : config.provides ) {
for (const auto &service : config.provides) {
m_serviceProviders[service] = config.name;
}
}
template<typename T>
template<ModuleType T>
void use() {
assert( !m_locked );
assert(!m_locked);
Config config { .name = T::name, .provides = {}, .depends = {} };
for ( auto service : T::provides ) {
Config config{.name = T::name, .provides = {}, .depends = {}};
for (auto service : T::provides) {
config.provides.push_back(service);
}
for ( auto service : T::depends ) {
for (auto service : T::depends) {
config.depends.push_back(service);
}
config.factory = T::factory;
......@@ -133,53 +87,68 @@ namespace fggl::modules {
addVirtual(config);
}
// FIXME this should be in cpp file
bool buildGraph() {
// resolve links between modules
for (auto& moduleItr : m_modules) {
if ( moduleItr.second.depends.empty() ) {
m_dependencies.addAll(moduleItr.first, {});
for (auto &moduleItr : m_modules) {
if (moduleItr.second.depends.empty()) {
m_dependencies.addVertex(moduleItr.first);
continue;
}
for (auto& service : moduleItr.second.depends) {
for (auto &service : moduleItr.second.depends) {
auto provider = m_serviceProviders.find(service);
if ( provider == m_serviceProviders.end() ) {
debug::log(debug::Level::warning, "{} depends on {}, but nothing provides it", moduleItr.first, service);
if (provider == m_serviceProviders.end()) {
debug::log(debug::Level::warning,
"{} depends on {}, but nothing provides it",
moduleItr.first,
service.get());
// nothing can provide the service requested, setup is invalid.
return false;
}
m_dependencies.add(moduleItr.first, provider->second);
m_dependencies.addEdge(moduleItr.first, provider->second);
}
}
return true;
}
template<typename T>
T* get() const {
assert( m_locked );
template<ServiceType T>
T *get() const {
assert(m_locked);
return m_services.template get<T>();
}
template<ServiceType T>
Service<T> getLazy() const {
assert(m_locked);
return { this };
}
//FIXME this should be in cpp file!
void resolve() {
if ( !buildGraph() ) {
assert( !m_locked );
if (!buildGraph()) {
return;
}
std::queue<ModuleIdentifier> stack;
m_dependencies.getOrderRev(stack);
while ( !stack.empty() ) {
while (!stack.empty()) {
auto nextToInit = stack.front();
debug::log(debug::Level::info, "Initializing {}", nextToInit);
auto& module = m_modules.at(nextToInit);
if ( module.factory != nullptr ) {
for (auto& service : module.provides) {
auto &module = m_modules.at(nextToInit);
if (module.factory != nullptr) {
for (auto &service : module.provides) {
bool result = module.factory(service, m_services);
if ( !result ) {
debug::log(debug::Level::warning, "{} could not create service {}", nextToInit, service);
if (!result) {
debug::log(debug::Level::warning,
"{} could not create service {}",
nextToInit,
service.get());
}
}
} else {
......@@ -192,12 +161,16 @@ namespace fggl::modules {
m_locked = true;
}
inline Services& services() {
return m_services;
}
private:
bool m_locked = false;
Services m_services;
std::map<ModuleIdentifier, Config> m_modules;
DependencyGraph<ModuleIdentifier> m_dependencies;
std::map<ModuleService, ModuleIdentifier> m_serviceProviders;
ds::DirectedGraph<ModuleIdentifier> m_dependencies;
std::map<ServiceName, ModuleIdentifier> m_serviceProviders;
};
......
......@@ -24,54 +24,41 @@
#include <functional>
#include <map>
#include <memory>
#include "fggl/util/safety.hpp"
#include "service.hpp"
namespace fggl::modules {
using ModuleIdentifier = std::string;
template<typename T>
concept ModuleType = requires(T type) {
{ T::provides };
{ T::depends };
};
using ModuleService = util::OpaqueName<std::string_view, struct ModuleServiceTag>;
using ModuleIdentifier = std::string;
constexpr ModuleService make_service(const std::string_view name) {
return ModuleService::make(name);
}
using ServiceFactory = std::function<bool(ServiceName , Services &)>;
struct Config {
ModuleIdentifier name;
std::vector<ServiceName> provides;
std::vector<ServiceName> depends;
ServiceFactory factory = nullptr;
};
class Services {
class Module {
public:
template<typename Svc, typename Impl, typename ...Args>
void bind(Args... args) {
static_assert( std::is_base_of_v<Svc, Impl>, "Service type must be assignable from implementation type" );
m_services[Svc::service] = std::make_shared<Impl>(args...);
}
template<typename Svc, typename ...Args>
Svc* create(Args... args){
auto svc = std::make_shared<Svc>(args...);
m_services[Svc::service] = svc;
return svc.get();
}
virtual ~Module() = default;
template<typename Svc>
void provide(std::shared_ptr<Svc> service) {
m_services[Svc::service] = service;
}
// copying modules is bad
Module(const Module&) = delete;
Module& operator=(const Module&) = delete;
template<typename S>
S* get() const {
auto serviceWrapper = m_services.at(S::service);
auto ptr = std::static_pointer_cast<S>(serviceWrapper);
return ptr.get();
}
private:
std::map<ModuleService, std::shared_ptr<void>> m_services;
};
// moving modules is bad
Module(Module&&) = delete;
Module& operator=(Module&&) = delete;
using ServiceFactory = std::function<bool(ModuleService, Services&)>;
struct Config {
ModuleIdentifier name;
std::vector<ModuleService> provides;
std::vector<ModuleService> depends;
ServiceFactory factory = nullptr;
virtual auto create(ServiceName, Services&) -> bool = 0;
};
} // namespace fggl::modules
......
/*
* 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_MODULES_SERVICE_HPP
#define FGGL_MODULES_SERVICE_HPP
#include <map>
#include <memory>
#include "fggl/util/safety.hpp"
namespace fggl::modules {
using ServiceName = util::OpaqueName<std::string_view, struct ModuleServiceTag>;
constexpr ServiceName make_service(const std::string_view name) {
return ServiceName::make(name);
}
template<typename T>
concept ServiceType = requires(T* type) {
{ T::service };
};
template<typename T, typename U>
concept Derived = std::is_base_of<U, T>::value;
class Services {
public:
template<ServiceType Svc, Derived<Svc> Impl, typename ...Args>
void bind(Args... args) {
m_services[Svc::service] = std::make_shared<Impl>(args...);
}
template<ServiceType Svc, typename ...Args>
Svc *create(Args... args) {
auto svc = std::make_shared<Svc>(args...);
m_services[Svc::service] = svc;
return svc.get();
}
template<ServiceType Svc>
void provide(std::shared_ptr<Svc> service) {
m_services[Svc::service] = service;
}
template<ServiceType S>
S *get() const {
auto serviceWrapper = m_services.at(S::service);
auto ptr = std::static_pointer_cast<S>(serviceWrapper);
return ptr.get();
}
private:
std::map<ServiceName, std::shared_ptr<void>> m_services;
};
} // namespace fggl::modules
#endif //FGGL_MODULES_SERVICE_HPP
......@@ -25,17 +25,17 @@
namespace fggl::phys {
using CollisionCB = std::function<void(entity::EntityID , entity::EntityID)>;
using CollisionCB = std::function<void(entity::EntityID, entity::EntityID)>;
struct CollisionCallbacks {
constexpr static const char* name = "phys::Callbacks";
constexpr static const char *name = "phys::Callbacks";
CollisionCB onEnter = nullptr;
CollisionCB onExit = nullptr;
CollisionCB onStay = nullptr;
};
struct CollisionCache {
constexpr static const char* name = "phys::Cache";
constexpr static const char *name = "phys::Cache";
std::unordered_set<entity::EntityID> collisions;
std::unordered_set<entity::EntityID> lastFrame;
};
......
/*
* 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_PHYS_NULL_HPP
#define FGGL_PHYS_NULL_HPP
#include "fggl/phys/types.hpp"
#include "fggl/phys/service.hpp"
namespace fggl::phys {
inline void build_noop(const entity::ComponentSpec & /*config*/,
entity::EntityManager &manager,
const entity::EntityID &entity,
modules::Services &/*services*/) {
manager.add<fggl::phys::RigidBody>(entity);
manager.add<fggl::phys::Dynamics>(entity);
}
class NullPhysicsEngine : public PhysicsEngine {
public:
inline void step() override {}
void setDebugDraw(bool /* enable */) override {}
inline std::vector<ContactPoint> scanCollisions(entity::EntityID /*entity*/) override {
return {};
}
inline entity::EntityID raycast(math::vec3 /*from*/, math::vec3 /*to*/) override {
return entity::INVALID;
}
inline std::vector<entity::EntityID> raycastAll(math::vec3 /*from*/, math::vec3 /*to*/) override {
return {};
}
inline std::vector<entity::EntityID> sweep(PhyShape & /*shape*/,
math::Transform & /*from*/,
math::Transform & /*to*/) override {
return {};
}
};
class NullPhysicsProvider : public PhysicsProvider {
public:
virtual ~NullPhysicsProvider() = default;
inline PhysicsEngine *create(entity::EntityManager * /*entityManager*/,
entity::EntityFactory *factory) override {
factory->bind(util::make_guid("phys::Body"), build_noop);
return new NullPhysicsEngine();
}
};
struct NullPhysics {
constexpr static const char *name = "fggl::phys::null";
constexpr static const std::array<modules::ServiceName, 1> provides = {
phys::PhysicsProvider::service
};
constexpr static const std::array<modules::ServiceName, 1> depends = {
entity::EntityFactory::service
};
static bool factory(modules::ServiceName serviceName, modules::Services &serviceManager);
};
} // namespace fggl::phys
#endif //FGGL_PHYS_NULL_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 01/08/22.
//
#ifndef FGGL_PHYS_SERVICE_HPP
#define FGGL_PHYS_SERVICE_HPP
#include "fggl/modules/module.hpp"
#include "fggl/entity/module.hpp"
#include "fggl/phys/types.hpp"
namespace fggl::phys {
class PhysicsProvider {
public:
constexpr static const auto service = modules::make_service("fggl::phys::service");
virtual ~PhysicsProvider() = default;
virtual PhysicsEngine *create(entity::EntityManager *entityManager, entity::EntityFactory *factory) = 0;
};
}
#endif //FGGL_PHYS_SERVICE_HPP
......@@ -28,9 +28,9 @@ namespace fggl::phys {
constexpr math::vec3 UNIT_EXTENTS{0.5F, 0.5F, 0.5F};
enum class ShapeType {
UNSET,
BOX,
SPHERE
UNSET,
BOX,
SPHERE
};
struct PhyShape {
......@@ -42,22 +42,24 @@ namespace fggl::phys {
struct Box : public PhyShape {
math::vec3 extents;
explicit inline Box(math::vec3 ext) : PhyShape(ShapeType::BOX), extents(ext) {}
};
struct Sphere : public PhyShape {
float radius = 1.0F;
explicit inline Sphere(float rad) : PhyShape(ShapeType::SPHERE), radius(rad) {}
};
enum class BodyType {
STATIC, KINEMATIC, DYNAMIC
STATIC, KINEMATIC, DYNAMIC
};
struct RigidBody {
constexpr static const char* name = "phys::Body";
constexpr static const char *name = "phys::Body";
float mass = MASS_DEFAULT;
PhyShape* shape = nullptr;
PhyShape *shape = nullptr;
BodyType type = BodyType::DYNAMIC;
[[nodiscard]]
......@@ -67,7 +69,7 @@ namespace fggl::phys {
};
struct Dynamics {
constexpr static const char* name = "phys::Dynamics";
constexpr static const char *name = "phys::Dynamics";
math::vec3 force = math::VEC3_ZERO;
};
......@@ -98,24 +100,30 @@ namespace fggl::phys {
virtual ~PhysicsEngine() = default;
// no copy and no move
PhysicsEngine(PhysicsEngine&) = delete;
PhysicsEngine(PhysicsEngine&&) = delete;
PhysicsEngine& operator=(PhysicsEngine&) = delete;
PhysicsEngine& operator=(PhysicsEngine&&) = delete;
PhysicsEngine(PhysicsEngine &) = delete;
PhysicsEngine(PhysicsEngine &&) = delete;
PhysicsEngine &operator=(PhysicsEngine &) = delete;
PhysicsEngine &operator=(PhysicsEngine &&) = delete;
// query methods (first cut - unstable APIs)
virtual std::vector<ContactPoint> scanCollisions(entity::EntityID entity) = 0;
virtual entity::EntityID raycast(math::vec3 from, math::vec3 to) = 0;
inline entity::EntityID raycast(math::Ray ray, float maxDist = 1000.0F) {
return raycast(ray.origin, ray.origin + ray.direction * maxDist);
}
virtual std::vector<entity::EntityID> raycastAll(math::vec3 from, math::vec3 to) = 0;
virtual std::vector<entity::EntityID> sweep(PhyShape& shape, math::Transform& from, math::Transform& to) = 0;
virtual std::vector<entity::EntityID> sweep(PhyShape &shape,
math::Transform &from,
math::Transform &to) = 0;
// update
virtual void step() = 0;
// debug
virtual void setDebugDraw(bool enable) = 0;
};
} // namespace fggl::phys
......
/*
* 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 19/09/22.
//
#ifndef FGGL_PLATFORM_FALLBACK_FILE_HPP
#define FGGL_PLATFORM_FALLBACK_FILE_HPP
#include <cstdio>
#include <cassert>
namespace fggl::platform {
class File {
public:
inline File(FILE* filePtr) : m_handle(filePtr) {
assert(filePtr != nullptr);
}
inline ~File() {
release();
}
template<typename T>
inline void write(const T* dataPtr) {
assert( m_handle != nullptr );
int status = fwrite(dataPtr, sizeof(T), 1, m_handle );
assert( status == 1);
}
template<typename T>
inline void writeArr(const T* dataPtr, std::size_t numElms) {
assert( m_handle != nullptr);
int status = fwrite(dataPtr, sizeof(T), numElms, m_handle );
assert( status == 1);
}
private:
std::FILE* m_handle;
inline void release() {
if ( m_handle != NULL) {
fclose(m_handle);
}
}
};
} // namespace fggl::platform
#endif //FGGL_PLATFORM_FALLBACK_FILE_HPP
......@@ -18,8 +18,8 @@
// see https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
//
#ifndef FGGL_PLATFORM_LINUX_PATHS_HPP
#define FGGL_PLATFORM_LINUX_PATHS_HPP
#ifndef FGGL_PLATFORM_FALLBACK_PATHS_HPP
#define FGGL_PLATFORM_FALLBACK_PATHS_HPP
#include "fggl/platform/paths.hpp"
......@@ -30,13 +30,44 @@
namespace fggl::platform {
constexpr const char* ENV_USER_CONFIG = "FGGL_CONFIG_HOME";
constexpr const char* ENV_USER_DATA = "FGGL_DATA_HOME";
constexpr const char* ENV_USER_CACHE = "FGGL_CACHE_HOME";
/**
* The environment variable used for containing user configurations.
*
* The directory mentioned by this environment variable should be read/writeable.
* The directory should be used for storing user-editable configuration options (eg. preferences).
*/
constexpr const char *ENV_USER_CONFIG = "FGGL_CONFIG_HOME";
// fallback user paths defined in the XDG spec
constexpr const char* DEFAULT_USER_CONFIG = "user_config";
constexpr const char* DEFAULT_USER_DATA = "user_data";
/**
* Fallback user configuration directory.
*
* Default user configuration directory if none is set by the environment variable.
*/
constexpr const char *DEFAULT_USER_CONFIG = "user_config";
/**
* The environment variable used for containing engine data.
*
* This directory is used for storing persistent user data and therefore should be read/writable.
* The directory should be used for storing user-modifiable state (eg, save files) or downloaded modifications.
*/
constexpr const char *ENV_USER_DATA = "FGGL_DATA_HOME";
/**
* Fallback user data directory.
*
* Default user data directory if none is set by the environment variable.
*/
constexpr const char *DEFAULT_USER_DATA = "user_data";
/**
* The environment variable used for containing semi-persistent user data.
*
* The application should be able to expect this directory to exist while the application is running, but otherwise
* cannot expect the data to be persistent. It MAY be persistent but the application should not rely on this.
* It should be used for resources which can be re-generated if needed, but can be useful if already present.
*/
constexpr const char *ENV_USER_CACHE = "FGGL_CACHE_HOME";
struct EnginePaths {
std::filesystem::path userConfig;
......@@ -44,13 +75,22 @@ namespace fggl::platform {
std::filesystem::path userCache;
};
EnginePaths calc_engine_paths(const char* base);
/**
* Fallback implementation of calc engine paths.
*
* For operating systems we don't know about, this simply uses the environment variables defined above and some
* sane defaults to construct the paths used to locate our data and user configuration.
*
* @param base an application-unique string used to construct the paths.
* @return the generated paths, for use with the rest of the engine.
*/
EnginePaths calc_engine_paths(const char *base);
// search routines for finding data and configuration files
std::filesystem::path locate_data(const EnginePaths& paths, const std::filesystem::path& relPath);
std::filesystem::path locate_config(const EnginePaths& paths, const std::filesystem::path& relPath);
std::filesystem::path locate_cache(const EnginePaths& paths, const std::filesystem::path& relPath);
std::filesystem::path locate_data(const EnginePaths &paths, const std::filesystem::path &relPath);
std::filesystem::path locate_config(const EnginePaths &paths, const std::filesystem::path &relPath);
std::filesystem::path locate_cache(const EnginePaths &paths, const std::filesystem::path &relPath);
}
#endif //FGGL_PLATFORM_LINUX_PATHS_HPP
#endif //FGGL_PLATFORM_FALLBACK_PATHS_HPP
......@@ -30,21 +30,21 @@
namespace fggl::platform {
constexpr const char* ENV_USER_CONFIG = "XDG_CONFIG_HOME";
constexpr const char* ENV_USER_DATA = "XDG_DATA_HOME";
constexpr const char* ENV_USER_CACHE = "XDG_CACHE_HOME";
constexpr const char *ENV_USER_CONFIG = "XDG_CONFIG_HOME";
constexpr const char *ENV_USER_DATA = "XDG_DATA_HOME";
constexpr const char *ENV_USER_CACHE = "XDG_CACHE_HOME";
constexpr const char* ENV_DATA_DIRS = "XDG_DATA_DIRS";
constexpr const char* ENV_CONFIG_DIRS = "XDG_CONFIG_DIRS";
constexpr const char *ENV_DATA_DIRS = "XDG_DATA_DIRS";
constexpr const char *ENV_CONFIG_DIRS = "XDG_CONFIG_DIRS";
// fallback user paths defined in the XDG spec
constexpr const char* DEFAULT_USER_CONFIG = "~/.config";
constexpr const char* DEFAULT_USER_DATA = "~/.local/share";
constexpr const char* DEFAULT_USER_CACHE = "~/.cache";
constexpr const char *DEFAULT_USER_CONFIG = "~/.config";
constexpr const char *DEFAULT_USER_DATA = "~/.local/share";
constexpr const char *DEFAULT_USER_CACHE = "~/.cache";
// fallback search paths defined in the XDG spec
constexpr const std::array<const char*, 2> DEFAULT_DATA_DIRS = {"/usr/local/share/", "/usr/share/"};
constexpr const std::array<const char*, 1> DEFAULT_CONFIG_DIRS = {"/etc/xdg"};
constexpr const std::array<const char *, 2> DEFAULT_DATA_DIRS = {"/usr/local/share/", "/usr/share/"};
constexpr const std::array<const char *, 1> DEFAULT_CONFIG_DIRS = {"/etc/xdg"};
struct EnginePaths {
std::filesystem::path userConfig;
......@@ -54,12 +54,12 @@ namespace fggl::platform {
std::vector<std::filesystem::path> configDirs;
};
EnginePaths calc_engine_paths(const char* base);
EnginePaths calc_engine_paths(const char *base);
// search routines for finding data and configuration files
std::filesystem::path locate_data(const EnginePaths& paths, const std::filesystem::path& relPath);
std::filesystem::path locate_config(const EnginePaths& paths, const std::filesystem::path& relPath);
std::filesystem::path locate_cache(const EnginePaths& paths, const std::filesystem::path& relPath);
std::filesystem::path locate_data(const EnginePaths &paths, const std::filesystem::path &relPath);
std::filesystem::path locate_config(const EnginePaths &paths, const std::filesystem::path &relPath);
std::filesystem::path locate_cache(const EnginePaths &paths, const std::filesystem::path &relPath);
}
......
......@@ -23,8 +23,10 @@
#include <vector>
#ifdef __linux__
#define FGGL_PLATFORM_PATHS linux
#include "fggl/platform/linux/paths.hpp"
#else
#define FGGL_PLATFORM_PATHS fallback
#include "fggl/platform/fallback/paths.hpp"
#endif
......
......@@ -25,32 +25,62 @@
namespace fggl::scenes {
class GameBase : public fggl::AppState {
public:
explicit GameBase(fggl::App &app);
void update(float dt) override;
void render(fggl::gfx::Graphics &gfx) override = 0;
protected:
inline auto input() -> input::Input & {
return *m_input;
}
inline void returnToMenu() {
m_owner.change_state(m_previous);
}
private:
input::Input *m_input;
std::string m_previous = "menu";
};
class Game : public fggl::AppState {
public:
explicit Game(fggl::App& app);
explicit Game(fggl::App &app);
void activate() override;
void deactivate() override;
void update() override;
void render(fggl::gfx::Graphics& gfx) override;
void update(float dt) override;
void render(fggl::gfx::Graphics &gfx) override;
protected:
inline auto world() -> entity::EntityManager& {
inline auto world() -> entity::EntityManager & {
return *m_world;
}
inline auto phys() -> phys::PhysicsEngine& {
protected:
bool hasPhys() const {
return m_phys != nullptr;
}
inline auto phys() -> phys::PhysicsEngine & {
assert(m_phys != nullptr);
return *m_phys;
}
inline auto input() -> input::Input& {
inline auto input() -> input::Input & {
return *m_input;
}
bool m_debug;
private:
input::Input* m_input;
input::Input *m_input;
std::unique_ptr<entity::EntityManager> m_world;
std::unique_ptr<phys::PhysicsEngine> m_phys;
std::string m_previous = "menu";
......
......@@ -26,29 +26,29 @@
namespace fggl::scenes {
using callback = std::function<void(void)>;
using Callback = std::function<void(void)>;
class BasicMenu : public AppState {
public:
explicit BasicMenu(App &owner);
void update() override;
void update(float dt) override;
void render(gfx::Graphics &paint) override;
void activate() override;
void deactivate() override;
void add(const std::string &label, callback cb);
void add(const std::string &label, const Callback& /*cb*/);
private:
input::Input* m_inputs;
std::map<const std::string, callback> m_items;
input::Input *m_inputs;
std::map<const std::string, Callback> m_items;
// menu state
std::string m_active;
math::vec2 m_cursorPos;
gui::Container m_canvas;
gui::Widget* m_hover;
gui::Widget *m_hover;
};
} // namepace fggl::scenes
......
/*
* 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 15/10/22.
//
#ifndef FGGL_SCRIPT_ENGINE_H
#define FGGL_SCRIPT_ENGINE_H
#include "fggl/modules/module.hpp"
namespace fggl::script {
class ScriptEngine {
public:
virtual ~ScriptEngine() = default;
// TODO use protected virtual pattern
// scene callbacks
virtual void onActivate() = 0;
virtual void onDeactivate() = 0;
virtual void onUpdate() = 0;
// trigger callback
virtual void onEvent(const std::string& name) = 0;
// run code in engine
virtual bool run(const char* script) = 0;
virtual bool load(const char* filename) = 0;
virtual void setGlobal(const char* name, void* ptr) = 0;
};
class ScriptProvider {
public:
constexpr static const modules::ServiceName service = modules::make_service("fggl::script::service");
virtual ScriptEngine* create() = 0;
};
}
#endif //FGGL_SCRIPT_ENGINE_H
......@@ -24,6 +24,7 @@
#include <cassert>
#include "fggl/util/safety.hpp"
#include "fggl/debug/logging.hpp"
namespace fggl::util {
......@@ -40,7 +41,7 @@ namespace fggl::util {
* @param str the string to hash.
* @return the hashed value
*/
constexpr uint32_t hash_fnv1a_32(const char* str) {
constexpr uint32_t hash_fnv1a_32(const char *str) {
assert(str != nullptr);
uint32_t hash = FNV_OFFSET_BASIS_32;
for (int i = 0; str[i] != '\0'; i++) {
......@@ -56,7 +57,7 @@ namespace fggl::util {
* @param str the string to be hashed
* @return the hashed value
*/
constexpr uint64_t hash_fnv1a_64(const char* str) {
constexpr uint64_t hash_fnv1a_64(const char *str) {
assert(str != nullptr);
uint64_t hash = FNV_OFFSET_BASIS_64;
for (int i = 0; str[i] != '\0'; i++) {
......@@ -66,27 +67,69 @@ namespace fggl::util {
return hash;
}
template<unsigned N>
struct FString {
char c[N];
};
// https://stackoverflow.com/a/65440575
template<unsigned ...Len>
constexpr auto cat(const char (&...strings)[Len]) {
constexpr unsigned N = (... + Len) - sizeof...(Len);
FString<N + 1> result = {};
result.c[N] = '\0';
char* dst = result.c;
for (const char* src : {strings...}) {
for (; *src != '\0'; src++, dst++) {
*dst = *src;
}
}
return result;
}
// debug-only functions
#ifndef NDEBUG
GUID internString(const char* str);
std::string guidToString(GUID guid);
GUID intern_string(const char *str);
std::string guid_to_string(GUID guid);
#endif
constexpr GUID make_guid(const char* str) {
return GUID::make(hash_fnv1a_64(str));
constexpr GUID make_guid(const char *str) {
return GUID(hash_fnv1a_64(str));
}
inline GUID make_guid_rt(const std::string& str) {
inline GUID make_guid_rt(const char* str) {
#ifndef NDEBUG
return internString(str.c_str());
return intern_string(str);
#else
return make_guid(str.c_str());
return make_guid(str);
#endif
}
inline GUID make_guid_rt(const std::string &str) {
return make_guid_rt(str.c_str());
}
} // namespace fggl::util
fggl::util::GUID operator "" _fid(const char* str);
fggl::util::GUID operator "" _fid(const char* str, std::size_t);
// formatter
template<> struct fmt::formatter<fggl::util::GUID> {
constexpr auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename FormatContext>
auto format(const fggl::util::GUID& guid, FormatContext& ctx) const -> decltype(ctx.out()) {
#ifndef NDEBUG
return fmt::format_to(ctx.out(), "GUID[{}]", guid_to_string(guid));
#else
return fmt::format_to(ctx.out(), "GUID[{}]", guid.get());
#endif
}
};
fggl::util::GUID operator "" _fid(const char *str);
fggl::util::GUID operator "" _fid(const char *str, std::size_t);
#endif //FGGL_UTIL_GUID_HPP
......@@ -40,20 +40,37 @@ namespace fggl::util {
public:
explicit constexpr OpaqueName(T value) : m_value(value) {}
constexpr OpaqueName() : m_value() {}
constexpr T get() const {
return m_value;
}
constexpr explicit operator std::string_view() const { return m_value; }
/**
* Check for equality of two handles.
*
* Two values are considered the same of the values contained inside them are considered equal, and both
* types share the same tagging interface.
*
* @param other the value being compared against.
* @return true iff the contained values are equal
*/
bool operator==(const OpaqueName<T, Tag> &other) const {
return m_value == other.m_value;
}
/**
* Check for equality of two handles.
*
* Two values are considered the same of the values contained inside them are considered equal, and both
* types share the same tagging interface.
*
* @param other the value being compared against.
* @return true iff the contained values are not equal
*/
bool operator!=(const OpaqueName<T, Tag> &other) const {
return !(*this == other);
return m_value != other.m_value;
}
bool operator<(const OpaqueName<T, Tag> &other) const {
......@@ -64,6 +81,9 @@ namespace fggl::util {
return m_value <=> other.m_value;
}
/**
* Generate a new tagged instance of a handle.
*/
constexpr static OpaqueName<T, Tag> make(T value) {
return OpaqueName<T, Tag>(value);
}
......
......@@ -51,7 +51,7 @@ namespace fggl::util {
}
bool change(const Identifer &name) {
if ( m_states.find(name) == m_states.end() ) {
if (m_states.find(name) == m_states.end()) {
debug::error("attempted to change to non-existent state {}, ignoring you.", name);
return false;
}
......@@ -63,7 +63,7 @@ namespace fggl::util {
}
StateType &active() const {
assertm(m_states.find(m_active) != m_states.end(), "active state does not exist!");
ASSERT_MSG(m_states.find(m_active) != m_states.end(), "active state does not exist!");
return *(m_states.at(m_active).get());
}
......
# Integration Modules
This adds support for 3rd party libraries to the engine. These can be included in projects
to expose modules which can then be used in your applications.
## Integrations Provided
The following integration modules are povided in this folder. For more information please see the readme for that module.
| Integration | Module Name | Description |
|-------------|-----------------------|----------------------------------------------------|
| Bullet | `fggl::phys::Bullet3` | Provides Bullet3 integration into the game engine. |
| Lua | `fggl::script::Lua` | Provides LUA scripting support for the engine |
Modules that cannot be included for legal reasons will be packaged independently of this repository.
\ No newline at end of file