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 947 additions and 153 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 04/03/23.
//
#ifndef FGGL_GUI_MODEL_PARSER_HPP
#define FGGL_GUI_MODEL_PARSER_HPP
#include <fggl/gui/model/structure.hpp>
#include <fggl/gui/fonts.hpp>
#include <fggl/modules/module.hpp>
namespace fggl::gui::model {
constexpr auto WIDGET_FACTORY_SERVICE = modules::make_service("gui::WidgetService");
class WidgetFactory {
public:
constexpr static auto service = WIDGET_FACTORY_SERVICE;
inline WidgetFactory(FontLibrary* lib) : m_fontLibrary(lib) {}
inline Widget* build(std::string templateName) {
return new Widget(m_templates.at(templateName).get());
}
inline Widget* buildEmpty() {
return new Widget(m_fontLibrary);
}
inline void push(std::string name, const Widget&& definition) {
m_templates.emplace(name, std::make_unique<Widget>(definition));
}
Widget* getTemplate(const std::string& name) {
auto itr = m_templates.find(name);
if ( itr == m_templates.end() ){
return nullptr;
}
return itr->second.get();
}
private:
std::map<std::string, std::unique_ptr<Widget>> m_templates;
FontLibrary* m_fontLibrary;
};
Widget* parseFile(WidgetFactory& factory, const std::string& path);
} // namespace fggl::gui::model
#endif //FGGL_GUI_MODEL_PARSER_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 04/03/23.
//
#ifndef FGGL_GUI_MODEL_STRUCTURE_HPP
#define FGGL_GUI_MODEL_STRUCTURE_HPP
#include <map>
#include <variant>
#include <vector>
#include <string>
#include <optional>
#include <fggl/math/types.hpp>
#include <fggl/gui/fonts.hpp>
namespace fggl::gui::model {
using AttrKey = std::string;
using AttrValue = std::variant<bool, int, float, std::string, math::vec2, math::vec3>;
constexpr float UNDEFINED = INFINITY;
constexpr math::vec2 UNDEFINED_SIZE{INFINITY, INFINITY};
class Widget {
public:
using ChildItr = std::vector<Widget>::iterator;
using ChildItrConst = std::vector<Widget>::const_iterator;
inline Widget(FontLibrary* library) :
m_parent(nullptr),
m_children(),
m_fontLibrary(library),
m_attrs(),
m_cachedSize(),
m_dirty(true) {
}
explicit inline Widget(Widget* parent) :
m_parent(parent),
m_children(),
m_fontLibrary(parent->m_fontLibrary),
m_attrs(),
m_cachedSize(),
m_dirty(true) {
}
inline bool isRoot() const {
return m_parent == nullptr;
}
inline bool isLeaf() const {
return m_children.empty();
}
inline bool hasAttr(const AttrKey& key) const {
return m_attrs.contains(key);
}
inline std::map<AttrKey, AttrValue> attrs() {
return m_attrs;
}
template<typename type>
inline type get(const AttrKey& key) const {
return std::get<type>(m_attrs.at(key));
}
template<typename type>
type get_or_default(const AttrKey& key) const {
auto itr = m_attrs.find(key);
if ( itr == m_attrs.end() ) {
return type();
} else {
return std::get<type>(itr->second);
}
}
template<typename type>
inline void set(const AttrKey& key, type value) {
m_attrs[key] = value;
}
inline void addChild(const Widget& element) {
auto childItr = m_children.insert( m_children.cend(), element );
childItr->m_parent = this;
}
inline ChildItr begin() {
return m_children.begin();
}
inline ChildItrConst begin() const noexcept {
return m_children.begin();
}
inline ChildItrConst cbegin() const noexcept {
return m_children.cbegin();
}
inline ChildItr end() {
return m_children.end();
}
inline ChildItrConst end() const {
return m_children.end();
}
inline ChildItrConst cend() const {
return m_children.cend();
}
inline std::optional<math::vec2> preferredSize() {
calcPrefSize(m_fontLibrary->getDefaultFont());
return m_cachedSize;
}
private:
Widget *m_parent;
std::vector<Widget> m_children;
FontLibrary* m_fontLibrary;
std::map< AttrKey, AttrValue > m_attrs;
math::vec2 m_cachedSize;
bool m_dirty;
void calcPrefSize(std::shared_ptr<FontFace> face);
};
void attr_box_set(Widget& widget, const AttrKey& key, auto top, auto right, auto bottom, auto left) {
widget.set(key + "::top", top);
widget.set(key + "::right", right);
widget.set(key + "::bottom", bottom);
widget.set(key + "::left", left);
}
inline void attr_box_set(Widget& widget, const AttrKey& key, auto vert, auto horz) {
attr_box_set(widget, key, vert, horz, vert, horz);
}
inline void attr_box_set(Widget& widget, const AttrKey& key, auto value) {
attr_box_set(widget, key, value, value, value, value);
}
} // namespace fggl::gui::model
#endif //FGGL_GUI_MODEL_STRUCTURE_HPP
......@@ -20,27 +20,53 @@
#define FGGL_GUI_MODULE_HPP
#include "fggl/gui/fonts.hpp"
#include "fggl/gui/model/parser.hpp"
#include "fggl/data/module.hpp"
#include "fggl/assets/packed/module.hpp"
namespace fggl::gui {
constexpr auto MIME_TTF = assets::from_mime("font/ttf");
constexpr auto ASSET_FONT_TTF = assets::make_asset_type("font/ttf");
static assets::AssetTypeID is_font(std::filesystem::path path) {
if ( path.extension() == ".ttf" ){
return ASSET_FONT_TTF;
}
return assets::INVALID_ASSET_TYPE;
}
struct FreeType {
constexpr static const char *name = "fggl::gui::FreeType";
constexpr static const std::array<modules::ModuleService, 1> provides = {
FontLibrary::service
constexpr static const std::array<modules::ServiceName, 2> provides = {
FontLibrary::service,
model::WidgetFactory::service
};
constexpr static const std::array<modules::ModuleService, 1> depends = {
data::Storage::service
constexpr static const std::array<modules::ServiceName, 2> depends = {
data::Storage::service,
assets::CheckinAdapted::service
};
static bool factory(modules::ModuleService name, modules::Services &serviceManager);
static bool factory(modules::ServiceName name, modules::Services &serviceManager);
};
bool FreeType::factory(modules::ModuleService service, modules::Services &services) {
bool FreeType::factory(modules::ServiceName service, modules::Services &services) {
if (service == FontLibrary::service) {
auto storage = services.get<data::Storage>();
services.create<FontLibrary>(storage);
auto* checkin = services.get<assets::CheckinAdapted>();
checkin->setLoader( MIME_TTF, assets::NEEDS_CHECKIN, is_font );
return true;
}
if ( service == model::WidgetFactory::service ) {
auto fonts = services.get<FontLibrary>();
services.create<model::WidgetFactory>(fonts);
return true;
}
return false;
}
}
......
/*
* 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 04/03/23.
//
#ifndef FGGL_GUI_RENDERER_RENDERER_HPP
#define FGGL_GUI_RENDERER_RENDERER_HPP
#include <fggl/gui/model/structure.hpp>
#include <fggl/gfx/paint.hpp>
namespace fggl::gui::renderer {
struct Box {
float top;
float right;
float bottom;
float left;
inline Box trim(Box border) const {
return {
top + border.top,
right - border.right,
bottom - border.bottom,
left + border.left
};
}
inline Box expand(Box border) const {
return {
top - border.top,
right + border.right,
bottom + border.bottom,
left - border.left
};
}
inline float width() const {
return right - left;
}
inline float height() const {
return bottom - top;
}
};
inline math::vec2 get_vec2(const model::Widget& root, const std::string& name) {
if ( !root.hasAttr(name) ) {
return {};
}
return root.get<math::vec2>(name);
}
inline math::vec3 get_vec3_rgb(const model::Widget& root, const std::string& name) {
if ( !root.hasAttr(name) ) {
return {};
}
return root.get<math::vec3>(name);
}
inline Box get_box(const model::Widget& root, const std::string& name) {
return {
root.get_or_default<float>(name + "::top"),
root.get_or_default<float>(name + "::right"),
root.get_or_default<float>(name + "::bottom"),
root.get_or_default<float>(name + "::left")
};
}
inline Box getBounds(math::vec2 pos, math::vec2 size) {
return {
pos.y,
pos.x + size.x,
pos.y + size.y,
pos.x
};
}
void draw_box(gfx::Path2D &path, glm::vec2 topLeft, glm::vec2 bottomRight);
void draw_border_patch(gfx::Paint& paint, Box& bounds, Box& size, math::vec3 colour);
void draw_border_solid(gfx::Paint& paint, Box& bounds, Box& size, math::vec3 colour);
void draw_background_solid(gfx::Paint& paint, Box& bounds, math::vec3 colour);
void layout(model::Widget& root);
void visit(const model::Widget& root, gfx::Paint& paint, Box offset = {0.0F, 1024.0F, 768.0F, 0.0F});
} // namespace fggl::gui::renderer
#endif //FGGL_GUI_RENDERER_RENDERER_HPP
......@@ -126,13 +126,27 @@ namespace fggl::gui {
return this;
}
virtual void update(float deltaTime) = 0;
virtual void render(gfx::Paint &paint) = 0;
inline virtual void layout() {};
inline virtual void activate() {};
inline virtual void onEnter() {}
inline virtual void onMouseOver(math::vec2 pos) {
if ( !m_hover ) {
onEnter(pos);
}
}
inline virtual void onEnter(math::vec2i /*pos*/) {
m_hover = true;
}
inline virtual void onExit(math::vec2i /*pos*/) {
m_hover = false;
}
inline virtual void onExit() {}
protected:
bool m_hover = false;
private:
Bounds2D m_bounds;
......
......@@ -60,7 +60,8 @@ namespace fggl::gui {
return m_naturalSize;
}
void layout();
void layout() override;
inline void update(float /*deltaTime*/) override {}
private:
std::shared_ptr<gui::FontFace> m_font;
......@@ -77,20 +78,20 @@ namespace fggl::gui {
void render(gfx::Paint &paint) override;
void activate() override;
void onEnter() override;
void onExit() override;
void label(const std::string &value);
[[nodiscard]]
std::string label() const;
inline void update(float /*deltaTime*/) override {}
void addCallback(Callback callback);
void layout() override;
private:
Label m_label;
std::string m_value;
std::vector<Callback> m_callbacks;
bool m_hover;
bool m_active;
};
......
......@@ -24,11 +24,11 @@
namespace fggl::input {
constexpr const modules::ModuleService SERVICE_INPUT = modules::make_service("fggl::input::Input");
constexpr const auto SERVICE_INPUT = modules::make_service("fggl::input::Input");
class Input {
public:
constexpr static const modules::ModuleService service = SERVICE_INPUT;
constexpr static const auto service = SERVICE_INPUT;
Input() = default;
void frame(float dt);
......
......@@ -27,14 +27,14 @@ namespace fggl::input {
struct Generic {
constexpr static const char *name = "fggl::input::Generic";
constexpr static const std::array<modules::ModuleService, 1> provides = {
constexpr static const std::array<modules::ServiceName, 1> provides = {
SERVICE_INPUT
};
constexpr static const std::array<modules::ModuleService, 0> depends = {};
static bool factory(modules::ModuleService service, modules::Services &services);
constexpr static const std::array<modules::ServiceName, 0> depends = {};
static bool factory(modules::ServiceName service, modules::Services &services);
};
bool Generic::factory(modules::ModuleService service, modules::Services &services) {
bool Generic::factory(modules::ServiceName service, modules::Services &services) {
if (service == SERVICE_INPUT) {
services.create<input::Input>();
return true;
......
......@@ -19,17 +19,19 @@
#include <array>
#include <bitset>
#include "fggl/math/types.hpp"
namespace fggl::input {
enum class MouseButton {
LEFT,
MIDDLE,
RIGHT,
EXTRA_1,
EXTRA_2,
EXTRA_3,
EXTRA_4,
EXTRA_5
LEFT = 0,
MIDDLE = 2,
RIGHT = 1,
EXTRA_1 = 3,
EXTRA_2 = 4,
EXTRA_3 = 5,
EXTRA_4 = 6,
EXTRA_5 = 7
};
enum class MouseAxis {
......@@ -74,6 +76,7 @@ namespace fggl::input {
void operator=(const MouseState &rhs);
};
class MouseInput {
public:
MouseInput() = default;
......@@ -121,6 +124,15 @@ namespace fggl::input {
MouseState m_prev;
};
// helpers
inline math::vec2 mouse_axis(const MouseInput& input) {
return { input.axis(MouseAxis::X), input.axis(MouseAxis::Y) };
}
inline math::vec2 mouse_axis_delta(const MouseInput& input) {
return { input.axisDelta(MouseAxis::X), input.axisDelta(MouseAxis::Y) };
}
};
#endif
......@@ -84,6 +84,21 @@ namespace fggl::math {
return valueOrMin > max ? max : valueOrMin;
}
constexpr float lerpImprecise(float start, float end, float t) {
return start + t * (end - start);
}
constexpr float lerp(float start, float end, float t) {
return (1 - t) * start + t * end;
}
template<typename T>
[[nodiscard]]
constexpr T smooth_add(T first, T second, const float weight) {
const float other = 1 - weight;
return (first * other) + (second * weight);
}
} // namespace fggl::math
......
......@@ -19,10 +19,12 @@
#ifndef FGGL_MATH_IMATH_HPP
#define FGGL_MATH_IMATH_HPP
#include <cstdint>
namespace fggl::math {
// wrap value in range [0, max)
inline int wrap(int value, int max) {
constexpr int wrap(int value, int max) {
if (value < 0)
return (value - 1) - (-1 - value) % value;
if (value >= max)
......@@ -31,7 +33,7 @@ namespace fggl::math {
}
// wrap value in range [min, max)
inline int wrap(int value, int const min, int const max) {
constexpr int wrap(int value, int const min, int const max) {
int range = max - min + 1;
if (value < min) {
value += range * ((min - value) / range + 1);
......@@ -39,19 +41,47 @@ namespace fggl::math {
return min + (value - min) % range;
}
inline
int clamp(int value, int min, int max) {
constexpr int clamp(int value, int min, int max) {
const int i = value > max ? max : value;
return i < min ? min : value;
}
template<typename T>
inline
T clampT(T value, T min, T max) {
constexpr T clampT(T value, T min, T max) {
const T i = value > max ? max : value;
return i < min ? min : value;
}
/**
* Calculate the sum of the first N positive integers.
*
* @param n the number to stop at
* @return sum(0 ... N)
*/
constexpr uint64_t calcSum(uint64_t n) {
return (n * (n + 1)) / 2;
}
/**
* Calculate the squared sum of the first N positive integers.
*
* @param n the number to stop at
* @return sum(0 ... N) * sum(0 ... N)
*/
constexpr uint64_t calcSumSquared(uint64_t n) {
return (n * (n + 1) * (2*n + 1)) / 6;
}
/**
* Calculate the cubed sum of the first N positive integers.
*
* @param n the number to stop at
* @return sum(0 ... N) * sum(0 ... N) * sum(0 ... N)
*/
constexpr uint64_t calcSumCubed(uint64_t n) {
return ((n * n) * ((n + 1) * (n + 1))) / 4;
}
} // namespace fggl::math
#endif //FGGL_MATH_IMATH_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 27/11/22.
//
#ifndef FGGL_MATH_STATS_HPP
#define FGGL_MATH_STATS_HPP
#include <cstdint>
#include <cmath>
namespace fggl::math {
class CumulativeAverage {
public:
inline void add(float item) {
m_current = ( item + (m_count * m_current) ) / (m_count + 1);
m_count++;
}
inline float average() {
return m_current;
}
private:
float m_current;
std::size_t m_count;
};
/**
* Useful Statistics class.
* ported to C++ from Piers' Java implementation.
*
* @tparam T
*/
template<typename T>
class Statistics {
public:
void add(T observation) {
m_count++;
m_sum += observation;
m_sumSquared += (observation * observation);
m_min = std::min(m_min, observation);
m_max = std::max(m_max, observation);
m_dirty = true;
}
inline T average() {
compute();
return m_average;
}
inline T average() const {
if ( m_dirty ) {
return m_sum / m_count;
}
return m_average;
}
inline T standardDeviation() {
compute();
return m_standardDiv;
}
inline T standardDeviation() const {
if ( !m_dirty ) {
return m_standardDiv;
}
T avg = average();
T num = m_sumSquared - (m_count * avg * avg);
return num < 0 ? 0 : sqrt( num / (m_count-1));
}
inline T standardError() {
compute();
return m_standardDiv / sqrt(m_count);
}
inline T standardError() const {
if ( !m_dirty ) {
return m_standardDiv / sqrt(m_count);
}
return standardDeviation() / sqrt(m_count);
}
inline T squareSum () const {
return m_sumSquared;
}
inline std::size_t count() const {
return m_count;
}
inline T sum() const {
return m_sum;
}
inline T min() const {
return m_min;
}
inline T max() const {
return m_max;
}
private:
T m_average{0};
T m_sum{0};
T m_sumSquared{0};
T m_standardDiv{0};
T m_min{0};
T m_max{0};
std::size_t m_count{0};
bool m_dirty{false};
void compute() {
if ( !m_dirty ) {
return;
}
m_average = m_sum / m_count;
T num = m_sumSquared - (m_count * m_average * m_average);
if ( num < 0 ) {
num = 0;
}
m_standardDiv = sqrt( num / (m_count-1) );
m_dirty = false;
}
};
using StatisticsF = Statistics<float>;
using StatisticsD = Statistics<double>;
} // namespace fggl::math
#endif //FGGL_MATH_STATS_HPP
......@@ -127,7 +127,7 @@ namespace fggl::math {
}
static data::Vertex2D pointToVertex(const math::vec2 &point) {
return data::Vertex2D{point, {1.0f, 1.0f, 1.0f}};
return data::Vertex2D{point, {1.0F, 1.0F, 1.0F}, {0.0F, 0.0F}};
}
/**
......
......@@ -61,7 +61,7 @@ namespace fggl::math {
/**
* A 4D unsigned integer vector.
*/
using vec4ui = glm::ivec4;
using vec4ui = glm::uvec4;
/**
* A 3D floating-point vector.
......@@ -76,7 +76,7 @@ namespace fggl::math {
/**
* A 3D unsigned integer vector.
*/
using vec3ui = glm::ivec3;
using vec3ui = glm::uvec3;
/**
* A 2D floating-point vector
......@@ -91,7 +91,11 @@ namespace fggl::math {
/**
* a 2D unsigned integer vector
*/
using vec2ui = glm::ivec2;
using vec2ui = glm::uvec2;
using vec2b = glm::bvec2;
using vec3b = glm::bvec3;
using vec4b = glm::bvec4;
/**
* A 2x2 floating-point matrix.
......@@ -120,6 +124,22 @@ namespace fggl::math {
constexpr static const math::vec3 AXIS_Y{0.0F, 1.0F, 0.0F};
constexpr static const math::vec3 AXIS_Z{0.0F, 0.0F, 1.0F};
constexpr auto minElm(vec3 a, vec3 b) -> vec3{
return {
a.x < b.x ? a.x : b.x,
a.y < b.y ? a.y : b.y,
a.z < b.z ? a.z : b.z
};
}
constexpr auto maxElm(vec3 a, vec3 b) -> vec3 {
return {
a.x > b.x ? a.x : b.x,
a.y > b.y ? a.y : b.y,
a.z > b.z ? a.z : b.z
};
}
// fastFloor from OpenSimplex2
inline int fastFloor(double x) {
int xi = (int) x;
......
......@@ -20,9 +20,9 @@
#define FGGL_MATH_VECTOR_HPP
#include <ostream>
#include <tuple>
#define GLM_ENABLE_EXPERIMENTAL
#define GLM_FORCE_MESSAGES
#include <glm/glm.hpp>
namespace glm {
......
/*
* 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,81 +33,33 @@
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);
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;
private:
Manager* m_manager;
std::shared_ptr<T> m_ptr;
};
inline void addVirtual(const Config &config) {
assert(!m_locked);
......@@ -116,7 +70,7 @@ namespace fggl::modules {
}
}
template<typename T>
template<ModuleType T>
void use() {
assert(!m_locked);
......@@ -133,11 +87,12 @@ 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, {});
m_dependencies.addVertex(moduleItr.first);
continue;
}
......@@ -148,24 +103,32 @@ namespace fggl::modules {
debug::log(debug::Level::warning,
"{} depends on {}, but nothing provides it",
moduleItr.first,
service);
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>
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() {
assert( !m_locked );
if (!buildGraph()) {
return;
}
......@@ -185,7 +148,7 @@ namespace fggl::modules {
debug::log(debug::Level::warning,
"{} could not create service {}",
nextToInit,
service);
service.get());
}
}
} else {
......@@ -198,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;
};
......
......@@ -26,53 +26,39 @@
#include <memory>
#include "fggl/util/safety.hpp"
#include "service.hpp"
namespace fggl::modules {
template<typename T>
concept ModuleType = requires(T type) {
{ T::provides };
{ T::depends };
};
using ModuleIdentifier = std::string;
using ModuleService = util::OpaqueName<std::string_view, struct ModuleServiceTag>;
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...);
}
virtual ~Module() = default;
template<typename Svc, typename ...Args>
Svc *create(Args... args) {
auto svc = std::make_shared<Svc>(args...);
m_services[Svc::service] = svc;
return svc.get();
}
// copying modules is bad
Module(const Module&) = delete;
Module& operator=(const Module&) = delete;
template<typename Svc>
void provide(std::shared_ptr<Svc> service) {
m_services[Svc::service] = service;
}
// moving modules is bad
Module(Module&&) = delete;
Module& operator=(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;
};
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