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 1715 additions and 103 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.
//
#ifndef FGGL_GFX_OGL4_MESHES_HPP
#define FGGL_GFX_OGL4_MESHES_HPP
#include "fggl/mesh/components.hpp"
#include "fggl/entity/entity.hpp"
#include "fggl/assets/manager.hpp"
#include "fggl/gfx/ogl/types.hpp"
#include "fggl/gfx/ogl4/fallback.hpp"
#include "fggl/gfx/camera.hpp"
#include "fggl/gfx/phong.hpp"
namespace fggl::gfx::ogl4 {
struct DrawType {
ogl::Primitive mode;
std::size_t restartIndex = ogl::NO_RESTART_IDX;
};
struct Material {
math::vec3 m_diffCol{1.0F, 1.0F, 1.0F};
math::vec3 m_specCol{1.0F, 1.0F, 1.0F};
ogl::Texture* m_diffuse;
ogl::Texture* m_normals;
ogl::Texture* m_specular;
};
struct MeshData {
std::shared_ptr<ogl::VertexArray> vao;
std::shared_ptr<ogl::ElementBuffer> elements;
std::shared_ptr<ogl::ArrayBuffer> vertexData;
std::size_t elementCount;
DrawType drawInfo;
Material* material;
void draw(std::shared_ptr<ogl::Shader> shader) const;
};
struct StaticMesh {
constexpr static auto name = "StaticMultiMesh";
std::shared_ptr<ogl::Shader> pipeline;
MeshData mesh;
inline void draw() const {
mesh.draw(pipeline);
}
};
struct StaticMultiMesh {
constexpr static auto name = "StaticMultiMesh";
std::shared_ptr<ogl::Shader> pipeline;
std::vector<MeshData> meshes;
void draw() const;
};
void setup_material(const std::shared_ptr<ogl::Shader>& shader, const PhongMaterial* material);
void setup_lighting(const std::shared_ptr<ogl::Shader>& shader, const math::mat4& viewMatrix, const math::Transform& camTransform, const math::Transform& transform, math::vec3 lightPos);
void setup_lighting(const std::shared_ptr<ogl::Shader>& shader, const DirectionalLight* light);
template<typename T>
void forward_pass(const entity::EntityID& camera, const fggl::entity::EntityManager& world, const assets::AssetManager* assets) {
// enable required OpenGL state
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
// enable depth testing
glEnable(GL_DEPTH_TEST);
// prep the fallback textures
auto *fallbackTex = assets->template get<ogl::Texture>(FALLBACK_TEX);
fallbackTex->bind(0);
fallbackTex->bind(1);
// set-up camera matrices
const auto &camTransform = world.get<fggl::math::Transform>(camera);
const auto &camComp = world.get<fggl::gfx::Camera>(camera);
const math::mat4 projectionMatrix = camComp.perspective();
const math::mat4 viewMatrix = glm::lookAt(camTransform.origin(), camComp.target, camTransform.up());
std::shared_ptr<ogl::Shader> shader = nullptr;
ogl::Location mvpMatrixUniform = 0;
ogl::Location mvMatrixUniform = 0;
auto entityView = world.find<T>();
// find directional light in scene
const DirectionalLight* light = nullptr;
auto lightEnts = world.find<DirectionalLight>();
if ( !lightEnts.empty() ) {
light = world.tryGet<DirectionalLight>(lightEnts[0]);
}
for (const auto &entity : entityView) {
// ensure that the model pipeline actually exists...
const auto &model = world.get<T>(entity);
if (model.pipeline == nullptr) {
debug::warning("shader was null, aborting render");
continue;
}
// check if we switched shaders
if (shader == nullptr || shader->shaderID() != model.pipeline->shaderID()) {
// new shader - need to re-send the view and projection matrices
shader = model.pipeline;
shader->use();
if (shader->hasUniform("projection")) {
shader->setUniformMtx(shader->uniform("view"), viewMatrix);
shader->setUniformMtx(shader->uniform("projection"), projectionMatrix);
}
mvpMatrixUniform = shader->uniform("MVPMatrix");
mvMatrixUniform = shader->uniform("MVMatrix");
if ( shader->hasUniform("diffuseTexture") ) {
shader->setUniformI(shader->uniform("diffuseTexture"), 0);
}
if ( shader->hasUniform("specularTexture") ) {
shader->setUniformI(shader->uniform("specularTexture"), 1);
}
}
// set model transform
const auto &transform = world.get<math::Transform>(entity);
shader->setUniformMtx(mvpMatrixUniform, projectionMatrix * viewMatrix * transform.model());
shader->setUniformMtx(mvMatrixUniform, viewMatrix * transform.model());
// setup lighting mode
if ( light != nullptr ) {
setup_lighting(shader, light);
}
setup_material(shader, world.tryGet<PhongMaterial>(entity, &DEFAULT_MATERIAL));
// actually draw it
model.draw();
}
}
template<typename T>
void forward_pass_normals(const entity::EntityID& camera, const fggl::entity::EntityManager& world, const std::shared_ptr<ogl::Shader>& shader) {
// enable required OpenGL state
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
// enable depth testing
glEnable(GL_DEPTH_TEST);
// set-up camera matrices
const auto &camTransform = world.get<fggl::math::Transform>(camera);
const auto &camComp = world.get<fggl::gfx::Camera>(camera);
const math::mat4 projectionMatrix = camComp.perspective();
const math::mat4 viewMatrix = glm::lookAt(camTransform.origin(), camComp.target, camTransform.up());
ogl::Location modelUniform = shader->uniform("model");
ogl::Location viewUniform = shader->uniform("view");
ogl::Location projUniform = shader->uniform("projection");
shader->use();
shader->setUniformMtx(projUniform, projectionMatrix);
shader->setUniformMtx(viewUniform, viewMatrix);
auto entities = world.find<T>();
for (const auto &entity : entities) {
// ensure that the model pipeline actually exists...
const auto &model = world.get<T>(entity);
// set model transform
const auto &transform = world.get<math::Transform>(entity);
shader->setUniformMtx(modelUniform, transform.model());
// render model
model.draw();
}
}
MeshData upload_mesh(const mesh::Mesh3D& meshComponent, assets::AssetManager* manager);
std::vector<MeshData> upload_multi_mesh(const mesh::MultiMesh3D& meshComponent, assets::AssetManager* manager);
} // namespace fggl::gfx::ogl4
#endif //FGGL_GFX_OGL4_MESHES_HPP
......@@ -26,9 +26,31 @@
#include "fggl/gfx/ogl/backend.hpp"
#include "fggl/gfx/ogl/types.hpp"
#include "fggl/data/model.hpp"
#include "fggl/mesh/mesh.hpp"
#include "fggl/assets/manager.hpp"
#define FGGL_ALLOW_DEFERRED_UPLOAD
namespace fggl::gfx::ogl4 {
const std::size_t NO_RESTART_IDX = 0;
struct StaticModelGPU {
std::shared_ptr<ogl::VertexArray> vao;
std::shared_ptr<ogl::ElementBuffer> elements;
std::shared_ptr<ogl::ArrayBuffer> vertices;
std::size_t elementCount;
ogl::Primitive drawType = ogl::Primitive::TRIANGLE;
std::size_t restartIndex = ogl::NO_RESTART_IDX;
};
struct StaticModelInstance {
constexpr static auto name = "ogl::static::model";
std::string modelName;
StaticModelGPU* model = nullptr;
std::shared_ptr<ogl::Shader> pipeline;
};
struct StaticModel {
constexpr static auto name = "StaticModel";
......@@ -41,41 +63,53 @@ namespace fggl::gfx::ogl4 {
std::shared_ptr<ogl::ArrayBuffer> vertexData;
std::size_t elementCount;
ogl::Primative drawType;
std::size_t restartIndex = NO_RESTART_IDX;
ogl::Primitive drawType;
std::size_t restartIndex = ogl::NO_RESTART_IDX;
};
class StaticModelRenderer {
public:
inline StaticModelRenderer(gfx::ShaderCache* cache) : m_shaders(cache), m_phong(nullptr), m_vao(), m_vertexList(), m_indexList() {
m_phong = std::make_shared<ogl::Shader>( cache->get("phong") );
inline StaticModelRenderer(gfx::ShaderCache *cache, assets::AssetManager *assets)
: m_assets(assets), m_shaders(cache), m_phong(nullptr), m_vao(), m_vertexList(), m_indexList() {
m_phong = cache->get("redbook/debug");
}
~StaticModelRenderer() = default;
StaticModelRenderer(const StaticModelRenderer& other) = delete;
StaticModelRenderer(StaticModelRenderer&& other) = delete;
StaticModelRenderer(const StaticModelRenderer &other) = delete;
StaticModelRenderer(StaticModelRenderer &&other) = delete;
StaticModelRenderer& operator=(const StaticModelRenderer& other) = delete;
StaticModelRenderer& operator=(StaticModelRenderer&& other) = delete;
StaticModelRenderer &operator=(const StaticModelRenderer &other) = delete;
StaticModelRenderer &operator=(StaticModelRenderer &&other) = delete;
void render(entity::EntityManager& world) {
resolveModels(world);
renderModelsForward(world);
StaticModel* uploadMesh(assets::AssetID guid, const data::Mesh& mesh, bool allowCache=true);
StaticModelGPU* uploadMesh2(const assets::AssetID& meshName, const data::Mesh& mesh);
void render(entity::EntityManager &world, bool debugMode = false) {
#ifdef FGGL_ALLOW_DEFERRED_UPLOAD
resolveModels(world);
#endif
renderModelsForward(world, debugMode);
}
private:
/**
* Attach any missing rendering components to models.
*/
void resolveModels(entity::EntityManager& world);
#ifdef FGGL_ALLOW_DEFERRED_UPLOAD
/**
* Attach any missing rendering components to models.
*/
void resolveModels(entity::EntityManager &world);
#endif
/**
* Render all visible objects according to their render tokens.
*/
void renderModelsForward(const entity::EntityManager& world);
void renderModelsForward(const entity::EntityManager &world, bool debugMode);
gfx::ShaderCache* m_shaders;
std::shared_ptr< ogl::Shader > m_phong;
assets::AssetManager *m_assets;
gfx::ShaderCache *m_shaders;
std::shared_ptr<ogl::Shader> m_phong;
ogl::VertexArray m_vao;
ogl::ArrayBuffer m_vertexList;
ogl::ElementBuffer m_indexList;
......
......@@ -20,7 +20,12 @@
#define FGGL_GFX_OGL4_MODULE_HPP
#include <array>
#include "fggl/modules/module.hpp"
#include "fggl/assets/manager.hpp"
#include "fggl/assets/packed/module.hpp"
#include "fggl/entity/loader/loader.hpp"
#include "fggl/gfx/interfaces.hpp"
......@@ -32,17 +37,19 @@
namespace fggl::gfx {
struct OpenGL4 {
constexpr static const char* name = "fggl::gfx::OpenGL4";
constexpr static const std::array<modules::ModuleService, 1> provides = {
constexpr static const char *name = "fggl::gfx::OpenGL4";
constexpr static const std::array<modules::ServiceName, 1> provides = {
WindowGraphics::service
};
constexpr static const std::array<modules::ModuleService, 3> depends = {
constexpr static const std::array<modules::ServiceName, 5> depends = {
data::Storage::service,
assets::AssetManager::service,
assets::CheckinAdapted::service,
gui::FontLibrary::service,
entity::EntityFactory::service
};
static bool factory(modules::ModuleService name, modules::Services& serviceManager);
static bool factory(modules::ServiceName name, modules::Services &serviceManager);
};
} //namespace fggl::gfx
......
......@@ -24,30 +24,39 @@
namespace fggl::gfx::ogl4 {
constexpr GraphicsDetails openGL4Details {
GraphicsAPI::OpenGL,
4,
3,
false
};
constexpr GraphicsDetails OPENGL_4_3_PRODUCTION { GraphicsAPI::OpenGL, 4, 3, false };
constexpr GraphicsDetails OPENGL_4_3_DEBUG { GraphicsAPI::OpenGL, 4, 3, true };
class WindowGraphics : public gfx::WindowGraphics {
public:
WindowGraphics(data::Storage* storage, gui::FontLibrary* fonts) : m_storage(storage), m_fonts(fonts) {};
virtual ~WindowGraphics() = default;
WindowGraphics(data::Storage *storage, gui::FontLibrary *fonts, assets::AssetManager* assets) : m_storage(storage), m_fonts(fonts), m_assets(assets) {};
~WindowGraphics() override = default;
// no copy
WindowGraphics(WindowGraphics& gfx) = delete;
WindowGraphics& operator=(const WindowGraphics& gfx) = delete;
// no move
WindowGraphics(WindowGraphics&& gfx) = delete;
WindowGraphics& operator=(WindowGraphics&& gfx) = delete;
fggl::gfx::Graphics* create(display::Window& window) override;
fggl::gfx::Graphics *create(display::Window &window) override;
[[nodiscard]]
inline GraphicsDetails config() const override {
return openGL4Details;
#ifdef NDEBUG
return OPENGL_4_3_PRODUCTION;
#else
return OPENGL_4_3_DEBUG;
#endif
}
private:
data::Storage* m_storage;
gui::FontLibrary* m_fonts;
data::Storage *m_storage;
gui::FontLibrary *m_fonts;
assets::AssetManager *m_assets;
};
} // namespace fggl::gfx::ogl4
#endif //FGGL_GFX_OGL4_SETUP_HPP
......@@ -23,6 +23,147 @@ namespace fggl::gfx {
using RadianAngle = float;
constexpr float COLOUR_MAX = 255.0F;
constexpr uint32_t BYTE_MASK = 0xFF;
constexpr uint8_t ONE_BYTE = 8;
constexpr uint8_t TWO_BYTES = 16;
constexpr math::vec3 rgb(uint32_t value) {
uint8_t red = (value & BYTE_MASK);
uint8_t green = ((value >> ONE_BYTE ) & BYTE_MASK);
uint8_t blue = ((value >> TWO_BYTES ) & BYTE_MASK);
return { (float)red / COLOUR_MAX, (float)green / COLOUR_MAX, (float)blue / COLOUR_MAX};
}
namespace colours {
constexpr math::vec3 rgb_decimal(uint8_t red, uint8_t green, uint8_t blue) {
return { (float)red / COLOUR_MAX, (float)green / COLOUR_MAX, (float)blue / COLOUR_MAX};
}
constexpr math::vec3 rgb(uint32_t value) {
uint8_t red = ( (value >> TWO_BYTES) & BYTE_MASK);
uint8_t green = ( (value >> ONE_BYTE) & BYTE_MASK);
uint8_t blue = value & BYTE_MASK;
return rgb_decimal(red, green, blue);
}
//
// Extended (CSS3) Colours
//
// pinks
constexpr auto MEDIUM_VIOLET_RED = rgb(0xC71585);
constexpr auto DEEP_PINK = rgb(0xFF1493);
constexpr auto PALE_VIOLET_RED = rgb(0xDB7093);
constexpr auto HOT_PINK = rgb(0xFF69B4);
constexpr auto LIGHT_PINK = rgb(0xFFB6C1);
constexpr auto PINK = rgb(0xFFC0CB);
// Reds
constexpr auto DARK_RED = rgb(0x8B0000);
constexpr auto RED = rgb(0xFF0000);
constexpr auto FIRE_BRICK = rgb(0xB22222);
constexpr auto CRIMSON = rgb(0xDC143C);
constexpr auto INDIAN_RED = rgb(0xCD5C5C);
constexpr auto LIGHT_CORAL = rgb(0xF08080);
constexpr auto SALMON = rgb(0xFA8072);
constexpr auto DARK_SALMON = rgb(0xE9967A);
constexpr auto LIGHT_SALMON = rgb(0xFFA07A);
// Oranges
constexpr auto ORANGE_RED = rgb(0xFF4500);
constexpr auto TOMATO = rgb(0xFF9347);
constexpr auto DARK_ORANGE = rgb(0xFF8C00);
constexpr auto CORAL = rgb(0xFF7F50);
constexpr auto ORANGE = rgb(0xFFA500);
// Yellows
constexpr auto DARK_KHAKI = rgb(0xBDB76B);
constexpr auto GOLD = rgb(0xFFD700);
constexpr auto KHAKI = rgb(0xF0E68C);
constexpr auto PEACH_PUFF = rgb(0xFFDAB9);
constexpr auto YELLOW = rgb(0xFFFF00);
constexpr auto PALE_GOLDENROD = rgb(0xEEE8AA);
constexpr auto MOCCASIN = rgb(0xFFE4B5);
constexpr auto PAPAYA_WHIP = rgb(0xFFEFD5);
constexpr auto LIGHT_GOLDENROD_YELLOW = rgb(0xFAFAD2);
constexpr auto LEMON_CHIFFON = rgb(0xFFFACD);
constexpr auto LIGHT_YELLOW = rgb(0xFFFFE0);
// Browns
constexpr auto MAROON = rgb(0x800000);
constexpr auto BROWN = rgb(0xA52A2A);
constexpr auto SADDLE_BROWN = rgb(0x8B4513);
constexpr auto SIENNA = rgb(0xA0522D);
constexpr auto CHOCOLATE = rgb(0xD2691E);
constexpr auto DARK_GOLDENROD = rgb(0xB8860B);
constexpr auto PERU = rgb(0xCD853F);
constexpr auto ROSY_BROWN = rgb(0xBC8F8F);
constexpr auto GOLDENROD = rgb(0xDAA520);
constexpr auto SANDY_BROWN = rgb(0xF4A460);
constexpr auto TAN = rgb(0xD2B48C);
constexpr auto BURLYWOOD = rgb(0xDEB887);
constexpr auto WHEAT = rgb(0xF5DEB3);
constexpr auto NAVAJO_WHITE = rgb(0xFFDEAD);
constexpr auto BISQUE = rgb(0xFFEAC4);
constexpr auto BLANCHED_ALMOND = rgb(0xFFEBCD);
constexpr auto CORNSILK = rgb(0xFFF8DC);
// Greens
constexpr auto DARK_GREEN = rgb(0x006400);
constexpr auto GREEN = rgb(0x008000);
constexpr auto DARK_OLIVE_GREEN = rgb(0x556B2F);
constexpr auto FOREST_GREEN = rgb(0x228B22);
constexpr auto SEA_GREEN = rgb(0x2E8B57);
constexpr auto OLIVE = rgb(0x808000);
constexpr auto OLIVE_DRAB = rgb(0x6B8E23);
constexpr auto MEDIUM_SEA_GREEN = rgb(0x3CB371);
constexpr auto LIME_GREEN = rgb(0x32CD32);
constexpr auto LIME = rgb(0x00FF00);
constexpr auto SPRING_GREEN = rgb(0x00FF7F);
constexpr auto MEDIUM_SPRING_GREEN = rgb(0x00FA9A);
constexpr auto DARK_SEA_GREEN = rgb(0x8FBC8F);
constexpr auto MEDIUM_AQUAMARINE = rgb(0x66CDAA);
constexpr auto YELLOW_GREEN = rgb(0x9ACD32);
constexpr auto LAWN_GREEN = rgb(0x7FFF00);
constexpr auto CHARTREUSE = rgb(0x7FFF00);
constexpr auto LIGHT_GREEN = rgb(0x90EE90);
constexpr auto GREEN_YELLOW = rgb(0xADFF2F);
constexpr auto PALE_GREEN = rgb(0x98FB98);
// Cyans
// TODO finish writing out the cyan list
constexpr auto CYAN = rgb(0xFFFF);
// Blues
// TODO finish writing out the blue list
constexpr auto MIDNIGHT_BLUE = rgb(0x191970);
/// Purples, Violets, and Magentas
// TODO finish writing out the purple list
constexpr auto FUCHSIA = rgb(0xFF00FF);
constexpr auto MAGENTA = FUCHSIA;
// Whites
//TODO finish writing out the white list
constexpr auto WHITE = rgb(0xFFFFFF);
// Grays and Blacks
constexpr auto BLACK = rgb(0x000000);
constexpr auto DARK_SLATE_GRAY = rgb(0x2F4F4F);
constexpr auto DIM_GRAY = rgb(0x696969);
constexpr auto SLATE_GRAY = rgb(0x708090);
constexpr auto GRAY = rgb(0x808080);
constexpr auto LIGHT_SLATE_GRAY = rgb(0x778899);
constexpr auto DARK_GRAY = rgb(0xA9A9A9);
constexpr auto SILVER = rgb(0xC0C0C0);
constexpr auto LIGHT_GRAY = rgb(0xD3D3D3);
constexpr auto GAINSBORO = rgb(0xDCDCDC);
}
enum class PathType {
MOVE,
PATH,
......@@ -67,6 +208,70 @@ namespace fggl::gfx {
std::vector<math::vec3> m_colours;
};
inline Path2D make_rect(math::vec2 center, math::vec2 radius, math::vec3 colour = {1.0f, 1.0f, 1.0f}) {
fggl::gfx::Path2D tileGfx(center);
tileGfx.colour(colour);
tileGfx.moveTo(center - radius);
tileGfx.pathTo({center.x + radius.x, center.y - radius.y});
tileGfx.pathTo(center + radius);
tileGfx.pathTo({center.x - radius.x, center.y + radius.y});
tileGfx.close();
return tileGfx;
}
struct ShapeOpts {
float angleOffset = 0.0F;
bool sinFirst = true;
};
inline Path2D make_shape(math::vec2 center, float radius, int sides, math::vec3 colour = colours::WHITE, ShapeOpts opts = {}) {
float angle = ((math::PI * 2.0F) / sides);
fggl::gfx::Path2D tileGfx(center);
tileGfx.colour(colour);
for (int i=0; i < sides; ++i) {
math::vec2 pos (
(float)(cosf(i * angle + opts.angleOffset) * radius),
(float)(sinf(i * angle + opts.angleOffset) * radius)
);
pos += center;
if ( i == 0 ) {
tileGfx.moveTo( pos );
} else {
tileGfx.pathTo( pos );
}
}
tileGfx.close();
return tileGfx;
}
inline Path2D make_arc(math::vec2 center, float radius, float start, float end, math::vec3 colour = colours::WHITE, int slices = 25) {
const float angle = (M_PI * 2.0) / slices;
fggl::gfx::Path2D tileGfx(center);
tileGfx.colour(colour);
tileGfx.moveTo(center);
for (float totalAngle = start; totalAngle < end; totalAngle += angle) { // NOLINT(cert-flp30-c)
float xPos = (float)(sinf(totalAngle) * radius) + center.x;
float yPos = (float)(cosf(totalAngle) * radius) + center.y;
tileGfx.pathTo({xPos, yPos});
if ( (totalAngle + angle) > end) {
break;
}
}
{
float xPos = (float) (sinf(end) * radius) + center.x;
float yPos = (float) (cosf(end) * radius) + center.y;
tileGfx.pathTo( {xPos, yPos} );
}
tileGfx.close();
return tileGfx;
}
enum class PaintType {
FILL,
STROKE
......@@ -80,6 +285,7 @@ namespace fggl::gfx {
struct TextCmd {
const std::string text;
const math::vec2 pos;
const math::vec3 colour;
};
class Paint {
......@@ -95,15 +301,15 @@ namespace fggl::gfx {
m_cmds.push_back({PaintType::STROKE, path});
}
void text(const std::string &text, const math::vec2 &pos) {
m_text.push_back({text, pos});
void text(const std::string &text, const math::vec2 &pos, const math::vec3f colour = fggl::gfx::colours::BLACK) {
m_text.push_back({text, pos, colour});
}
const std::vector<PaintCmd> &cmds() const {
return m_cmds;
}
const std::vector<TextCmd>& textCmds() const {
const std::vector<TextCmd> &textCmds() const {
return m_text;
}
......
/*
* 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 03/09/22.
//
#ifndef FGGL_GFX_PALLETS_HPP
#define FGGL_GFX_PALLETS_HPP
#include "fggl/gfx/paint.hpp"
namespace fggl::gfx::pallets {
// VGA pallet
namespace vga {
constexpr math::vec3 WHITE{1.0F, 1.0F, 1.0F};
constexpr math::vec3 SILVER{0.75F, 0.75F, 0.75F};
constexpr math::vec3 GRAY{0.5F, 0.5F, 0.5F};
constexpr math::vec3 BLACK{0.0F, 0.0F, 0.0F};
constexpr math::vec3 RED{1.0F, 0.0F, 0.0F};
constexpr math::vec3 MAROON{0.5F, 0.0F, 0.0F};
constexpr math::vec3 YELLOW{1.0F, 1.0F, 0.0F};
constexpr math::vec3 OLIVE{0.5F, 0.5F, 0.0F};
constexpr math::vec3 LIME{0.0F, 1.0F, 0.0F};
constexpr math::vec3 GREEN{0.0F, 0.5F, 0.0F};
constexpr math::vec3 AQUA{0.0F, 1.0F, 1.0F};
constexpr math::vec3 TEAL{0.0F, 0.5F, 0.5F};
constexpr math::vec3 BLUE{0.0F, 0.0F, 1.0F};
constexpr math::vec3 NAVY{0.0F, 0.0F, 0.5F};
constexpr math::vec3 FUCHSIA{1.0F, 0.0F, 1.0F};
constexpr math::vec3 PURPLE{0.5F, 0.0F, 0.5F};
}
// Tango Free Desktop Pallet
namespace tango {
constexpr auto BUTTER_0 = rgb(0xFCE94F);
constexpr auto BUTTER_1 = rgb(0xEDD400);
constexpr auto BUTTER_2 = rgb(0xC4A000);
constexpr auto ORANGE_0 = rgb(0xFCAF3E);
constexpr auto ORANGE_1 = rgb(0xF57900);
constexpr auto ORANGE_2 = rgb(0xCE5C00);
constexpr auto CHOCOLATE_0 = rgb(0xE9B96E);
constexpr auto CHOCOLATE_1 = rgb(0xC17D11);
constexpr auto CHOCOLATE_2 = rgb(0x8F5902);
constexpr auto CHAMELEON_0 = rgb(0x8AE234);
constexpr auto CHAMELEON_1 = rgb(0x73D216);
constexpr auto CHAMELEON_2 = rgb(0x4E9a06);
constexpr auto SKY_BLUE_0 = rgb(0x729FCF);
constexpr auto SKY_BLUE_1 = rgb(0x3456A4);
constexpr auto SKY_BLUE_2 = rgb(0x204A87);
constexpr auto PLUM_0 = rgb(0xAD7FA8);
constexpr auto PLUM_1 = rgb(0x75507B);
constexpr auto PLUM_2 = rgb(0x5C3566);
constexpr auto SCARLET_RED_0 = rgb(0xEF2929);
constexpr auto SCARLET_RED_1 = rgb(0xCC0000);
constexpr auto SCARLET_RED_2 = rgb(0xA40000);
constexpr auto ALUMINIUM_0 = rgb(0xEEEEEC);
constexpr auto ALUMINIUM_1 = rgb(0xD3D7CF);
constexpr auto ALUMINIUM_2 = rgb(0xBABDB6);
constexpr auto ALUMINIUM_3 = rgb(0x888a85);
constexpr auto ALUMINIUM_4 = rgb(0x555753);
constexpr auto ALUMINIUM_6 = rgb(0x2e3436);
}
// solarized
namespace solarized {
// backgrounds (dark)
constexpr auto BASE03 = rgb(0x002B36);
constexpr auto BASE02 = rgb(0x073642);
// content tones
constexpr auto BASE01 = rgb(0x586E75);
constexpr auto BASE00 = rgb(0x657B83);
constexpr auto BASE0 = rgb(0x839496);
constexpr auto BASE1 = rgb(0X93A1A1);
// backgrounds (light)
constexpr auto BASE2 = rgb(0xEEE8D5);
constexpr auto BASE3 = rgb(0xFDF6E3);
// accent colours
constexpr auto YELLOW = rgb(0xB58900);
constexpr auto ORANGE = rgb(0xCB4B16);
constexpr auto RED = rgb(0xDC322F);
constexpr auto MAGENTA = rgb(0xD33682);
constexpr auto VIOLET = rgb(0x6C71C4);
constexpr auto BLUE = rgb(0x268bd2);
constexpr auto CYAN = rgb(0x2AA198);
constexpr auto GREEN = rgb(0x859900);
}
} // namespace fggl::gfx::pallets
#endif //FGGL_GFX_PALLETS_HPP
......@@ -25,7 +25,7 @@
namespace fggl::gfx {
struct PhongMaterial {
constexpr static const char* name = "gfx::material";
constexpr static const char *name = "gfx::material";
constexpr static const util::GUID guid = util::make_guid("gfx::material");
math::vec3 emission;
math::vec3 ambient;
......@@ -34,14 +34,14 @@ namespace fggl::gfx {
float shininess;
};
constexpr math::vec3 DEFAULT_EMISSION {0.0F, 0.0F, 0.0F};
constexpr math::vec3 DEFAULT_AMBIENT { 0.05F, 0.05F, 0.05F};
constexpr math::vec3 DEFAULT_DIFFUSE { 0.5F, 0.5F, 0.5F};
constexpr math::vec3 DEFAULT_SPECULAR { 0.7F, 0.7F, 0.7F};
constexpr math::vec3 DEFAULT_EMISSION{0.0F, 0.0F, 0.0F};
constexpr math::vec3 DEFAULT_AMBIENT{0.05F, 0.05F, 0.05F};
constexpr math::vec3 DEFAULT_DIFFUSE{0.5F, 0.5F, 0.5F};
constexpr math::vec3 DEFAULT_SPECULAR{0.7F, 0.7F, 0.7F};
//constexpr float DEFAULT_SHININESS = .078125F;
constexpr float DEFAULT_SHININESS = 16.0F;
constexpr PhongMaterial DEFAULT_MATERIAL {
constexpr PhongMaterial DEFAULT_MATERIAL{
DEFAULT_EMISSION,
DEFAULT_AMBIENT,
DEFAULT_DIFFUSE,
......@@ -50,7 +50,28 @@ namespace fggl::gfx {
};
struct Light {
constexpr static const char* name = "gfx::light";
math::vec3 position;
math::vec3 ambient;
math::vec3 specular;
math::vec3 diffuse;
};
struct DirectionalLight : public Light {
constexpr static const char *name = "gfx::phong::directional";
constexpr static const util::GUID guid = util::make_guid(name);
};
struct PointLight : public Light {
constexpr static const char *name = "gfx::phong::point";
constexpr static const util::GUID guid = util::make_guid(name);
float constant = 1.0F;
float linear;
float quadratic;
};
struct Light2 {
constexpr static const char *name = "gfx::light";
constexpr static const util::GUID guid = util::make_guid("gfx::light");
bool enabled;
bool local;
......
......@@ -37,10 +37,44 @@ namespace fggl::gfx {
class WindowGraphics {
public:
constexpr const static modules::ModuleService service = modules::make_service("fggl::gfx::WindowGraphics");
constexpr const static auto service = modules::make_service("fggl::gfx::WindowGraphics");
WindowGraphics() = default;
virtual ~WindowGraphics() = default;
// no copy
WindowGraphics(const WindowGraphics& ) = delete;
WindowGraphics& operator=(const WindowGraphics&) = delete;
// no move
WindowGraphics(WindowGraphics&&) = delete;
WindowGraphics& operator=(WindowGraphics&&) = delete;
[[nodiscard]]
virtual GraphicsDetails config() const = 0;
virtual Graphics* create(display::Window& window) = 0;
[[nodiscard]]
virtual Graphics* create(display::Window &window) = 0;
inline Graphics* createMain(display::Window& window) {
assert( m_graphics == nullptr );
m_graphics = create(window);
return m_graphics;
}
inline void release() {
if ( m_graphics != nullptr ) {
delete m_graphics;
m_graphics = nullptr;
}
}
[[nodiscard]] inline Graphics* get() const {
return m_graphics;
}
private:
Graphics* m_graphics = nullptr;
};
} // namespace fggl::gfx
......
/*
* 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/12/22.
//
#ifndef FGGL_GRID_ACTIONS_H
#define FGGL_GRID_ACTIONS_H
#include <cstdint>
#include <variant>
#include "fggl/grid/hexagon.hpp"
#include "fggl/grid/tokens.hpp"
#include "fggl/util/safety.hpp"
namespace fggl::grid {
template<typename State, typename Target>
class ActionType;
template<typename State, typename Target>
class Action {
public:
virtual void progress(State* state) = 0;
virtual bool isDone() = 0;
};
template<typename State, typename Target>
class InstantAction : public Action<State, Target> {
public:
void progress(State* state) final;
bool isDone() final;
private:
ActionType<State, Target>* m_action;
TokenIdentifer m_actor;
Target m_target;
};
template<typename State, typename Target>
class DurativeAction : public Action<State, Target> {
public:
void progress(State* state) final;
bool isDone() final;
private:
ActionType<State, Target>* m_action;
TokenIdentifer m_actor;
Target m_target;
uint64_t m_completionRound;
};
template<typename State, typename Target>
class ActionType {
public:
virtual bool canApply(const State* state, TokenIdentifer actor, Target target) = 0;
virtual void apply(State* state, TokenIdentifer actor, Target target) = 0;
virtual Action<State, Target> ground(TokenIdentifer actor, Target target) = 0;
};
} // namespace fggl::grid
#endif //FGGL_GRID_ACTIONS_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/>.
*/
//
// Created by webpigeon on 27/11/22.
//
#ifndef FGGL_GRID_HEX_HPP
#define FGGL_GRID_HEX_HPP
#include <array>
#include <vector>
#include <cmath>
#include <compare>
#include <fggl/math/fmath.hpp>
/**
* Hexagonal Grid Implementation.
* Based largely off Amit's incredible grid documentation.
*/
namespace fggl::grid {
enum class HexDirPointy {
RIGHT = 0, TOP_RIGHT = 1, TOP_LEFT = 2, LEFT = 3, BOTTOM_LEFT = 4, BOTTOM_RIGHT = 5
};
enum class HexDirFlat {
BOTTOM_RIGHT = 0, TOP_RIGHT = 1, TOP = 2, TOP_LEFT = 3, BOTTOM_LEFT = 4, BOTTOM = 5
};
constexpr std::array< std::array<int, 2>, 6> HEX_DIRECTIONS {{
{1, 0}, {1, -1}, {0, 1},
{-1, 0}, {-1, 1}, {0, 1}
}};
constexpr std::array< std::array<int, 2>, 6> HEX_DIAGONALS {{
{2, -1}, {+1, -2}, {-1, -1},
{-2, 1}, {-1, 2}, {1, 1}
}};
template<typename T>
struct HexPointT {
constexpr HexPointT(T posQ, T posR) : m_pos({posQ, posR}) {}
constexpr explicit HexPointT(const std::array<int, 2>& pos) : m_pos(pos[0], pos[1]) {}
[[nodiscard]]
constexpr auto q() const -> T {
return m_pos[0];
}
[[nodiscard]]
constexpr auto r() const -> T{
return m_pos[1];
}
[[nodiscard]]
constexpr auto s() const -> T {
return -m_pos[0]-m_pos[1];
}
inline HexPointT neighbour(HexDirPointy dir) {
auto& offset = HEX_DIRECTIONS.at( (int)dir );
return { m_pos[0] + offset[0], m_pos[1] + offset[1] };
}
inline HexPointT neighbour(HexDirFlat dir) {
auto& offset = HEX_DIAGONALS.at( (int)dir );
return { m_pos[0] + offset[0], m_pos[1] + offset[1] };
}
HexPointT operator+(const HexPointT<T>& other) const {
return { m_pos[0] + other.m_pos[0], m_pos[1] + other.m_pos[1] };
}
HexPointT operator-(const HexPointT<T>& other) const {
return { m_pos[0] - other.m_pos[0], m_pos[1] - other.m_pos[1] };
}
bool operator==(const HexPointT<T>& other) const {
return m_pos[0] == other.m_pos[0] && m_pos[1] == m_pos[1];
}
auto operator<=>(const HexPointT<T>& other) const = default;
[[nodiscard]]
auto distance(const HexPointT& other) const -> T {
auto vec = *this - other;
return (
::abs(vec.q())
+ ::abs(vec.q() - vec.r())
+ ::abs(vec.r()) / 2
);
}
[[nodiscard]]
auto hexesInRange(int range) const -> std::vector<HexPointT<T>> {
std::vector<HexPointT<T>> results;
for ( auto q = -range; q <= range; ++q ) {
auto stopCount = std::min(range, -q+range);
for ( auto r = std::max(-range, -q-range); r <= stopCount; ++r ) {
results.push_back( *this + HexPointT<T>(q, r) );
}
}
return results;
}
private:
std::array<T, 2> m_pos;
};
using FloatHex = HexPointT<float>;
using IntHex = HexPointT<int>;
template<typename T>
constexpr FloatHex hexLerp(const HexPointT<T>& start, const HexPointT<T>& end, float t) {
return {
math::lerp(start.q(), end.q(), t),
math::lerp(start.r(), end.r(), t)
};
}
[[nodiscard]]
constexpr IntHex round2(const FloatHex& hex) {
auto q = std::round( hex.q() );
auto r = std::round( hex.r() );
auto s = std::round( hex.s() );
auto qDiff = std::abs( q - hex.q() );
auto rDiff = std::abs( r - hex.r() );
auto sDiff = std::abs( s - hex.r() );
if ( qDiff > rDiff && qDiff > sDiff) {
q = -r-s;
} else if ( rDiff > sDiff ) {
r = -q-s;
} else {
s = -q-r;
}
return {(int)q, (int)r};
}
std::vector<IntHex> lineTo(const IntHex& start, const IntHex& end);
} // namespace fggl::grid
#endif //FGGL_GRID_HEX_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 10/12/22.
//
#ifndef FGGL_GRID_HEXAGON_BOARD_HPP
#define FGGL_GRID_HEXAGON_BOARD_HPP
#include <map>
#include <set>
#include <optional>
#include <utility>
#include <variant>
#include "fggl/grid/hexagon.hpp"
#include "fggl/grid/tokens.hpp"
#include "fggl/grid/actions.hpp"
#include "fggl/math/types.hpp"
namespace fggl::grid {
class HexGrid;
using HexTarget = std::variant<TokenIdentifer, IntHex>;
using HexAction = Action<HexGrid, HexTarget>;
struct MaterialData {
std::string name;
math::vec3 colour;
};
struct TerrainType {
std::shared_ptr<MaterialData> data;
};
struct HexTile {
std::shared_ptr<MaterialData> terrain;
std::vector< std::shared_ptr<Token> > m_tokens;
[[nodiscard]]
inline std::optional<const MaterialData> data() const {
if (terrain == nullptr) {
{}
}
return *terrain;
}
};
class HexGrid {
public:
inline bool isValidPos(const IntHex& pos) const {
return m_tiles.contains(pos);
}
void setTerrain(const IntHex& pos, const TerrainType& terrain) {
auto& mapTile = m_tiles[pos];
mapTile.terrain = terrain.data;
}
std::optional<const MaterialData> getTerrain(const IntHex& pos) const {
const auto itr = m_tiles.find(pos);
if ( itr == m_tiles.end() ) {
return {};
}
return itr->second.data();
}
std::set<IntHex> getAllTiles() {
std::set<IntHex> posSet;
for ( auto& [pos,data] : m_tiles ) {
posSet.emplace( pos );
}
return posSet;
}
std::set<HexTile> tilesInRange(const IntHex& pos, int range) const;
std::set<HexTile> neighboursOf(const IntHex& pos) const;
private:
std::map<IntHex, HexTile> m_tiles;
std::map<uint64_t, std::shared_ptr<Token>> m_tokens;
};
} // namespace fggl::grid
#endif //FGGL_GRID_HEXAGON_BOARD_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/>.
*/
/*
* 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/12/22.
//
#ifndef FGGL_FGGL_GRID_LAYOUT_HPP
#define FGGL_FGGL_GRID_LAYOUT_HPP
#include "fggl/math/types.hpp"
#include "fggl/grid/hexagon.hpp"
namespace fggl::grid {
// factor out the call to sqrt so the matrices can be constexpr
constexpr float M_SQRT_3 = 1.73205080757F;
constexpr float M_SQRT_3_OVER_2 = M_SQRT_3 / 2.0F;
constexpr math::mat2 MAT_HEX_POINTY{
M_SQRT_3, 0.0F,
M_SQRT_3_OVER_2, 3.F / 2.F
};
constexpr math::mat2 MAT_HEX_FLAT{
3.F / 2.F, M_SQRT_3_OVER_2,
0.0F, M_SQRT_3
};
struct Orientation {
math::mat2 m_forward;
math::mat2 m_backward;
int m_angle;
inline Orientation(int angle, math::mat2 forward) : m_forward(forward), m_backward(glm::inverse(forward)), m_angle(angle) {};
static inline Orientation make_pointy() {
return { 30, MAT_HEX_POINTY};
}
static inline Orientation make_flat() {
return { 0, MAT_HEX_FLAT };
}
};
constexpr int HEX_SIDES = 6;
constexpr int DEG_PER_HEX_SIDE = 360 / HEX_SIDES; // 60 degrees per side
struct Layout {
Orientation m_orientation;
math::vec2 m_size;
math::vec2 m_origin;
Layout(Orientation orientation, math::vec2 size, math::vec2 origin) : m_orientation(orientation), m_size(size), m_origin(origin){}
Layout(Orientation orientation, float size) : m_orientation(orientation), m_size(size, size), m_origin() {}
inline void translate(float dx, float dy){
m_origin.x += dx;
m_origin.y += dy;
}
[[nodiscard]]
inline math::vec2 origin() const {
return m_origin;
}
[[nodiscard]]
inline math::vec2 size() const {
return m_size;
}
[[nodiscard]]
inline math::vec2 toScreen(IntHex gridPos) const {
const math::vec2 hexPoint{gridPos.q(), gridPos.r()};
auto point = (m_orientation.m_forward * hexPoint) * m_size;
return point + m_origin;
}
[[nodiscard]]
inline FloatHex toGrid(math::vec2 screen) const {
auto point = (screen - m_origin) / m_size;
auto hexPos = m_orientation.m_backward * point;
return {hexPos.x, hexPos.y};
}
[[nodiscard]]
inline math::vec2 cornerOffset(int corner) const {
const int angInc = DEG_PER_HEX_SIDE * corner;
const float angle = (angInc + m_orientation.m_angle) * ( fggl::math::PI / 180.0F);
return {
m_size.x * cosf(angle),
m_size.y * sinf(angle)
};
}
void paintHex(fggl::gfx::Paint& paint, IntHex pos, math::vec3 colour, math::vec2 offset) const {
const auto hexScreenCenter = toScreen(pos);
gfx::Path2D path({0,0});
path.colour(colour);
for (int i=0; i < HEX_SIDES; ++i) {
auto cornerPos = hexScreenCenter + cornerOffset(i);
if ( i == 0) {
path.moveTo(cornerPos + offset);
} else {
path.pathTo(cornerPos + offset);
}
}
paint.stroke(path);
}
};
}
#endif //FGGL_FGGL_GRID_LAYOUT_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 19/12/22.
//
#ifndef FGGL_GRID_TOKENS_HPP
#define FGGL_GRID_TOKENS_HPP
#include <cstdint>
#include <map>
#include "fggl/util/guid.hpp"
namespace fggl::grid {
using TokenIdentifer = fggl::util::OpaqueName<uint64_t, struct Token>;
class TokenType {
public:
using PropType = int64_t;
inline void setProperty(util::GUID name, PropType newVal) {
m_properties[name] = newVal;
}
[[nodiscard]]
inline PropType getProperty(util::GUID name, PropType unsetVal=0) const {
try {
return m_properties.at(name);
} catch ( std::out_of_range& e ) {
return unsetVal;
}
}
private:
std::map<util::GUID, PropType> m_properties;
std::map<util::GUID, uint64_t> m_cost;
};
class Token {
public:
Token() = default;
explicit Token(std::shared_ptr<TokenType> type) : m_type(std::move(type)) {}
inline void setProperty(util::GUID name, TokenType::PropType newVal) {
m_properties[name] = newVal;
}
[[nodiscard]]
inline TokenType::PropType getProperty(util::GUID name, TokenType::PropType unsetVal=0) const {
try {
auto prop = m_properties.at(name);
return prop;
} catch ( std::out_of_range& ex) {
return m_type->getProperty(name, unsetVal);
}
}
[[nodiscard]]
inline TokenType::PropType getPropertyDirect(util::GUID name, TokenType::PropType unsetVal=0) const {
try {
auto prop = m_properties.at(name);
return prop;
} catch ( std::out_of_range& ex) {
return unsetVal;
}
}
private:
TokenIdentifer m_id;
std::shared_ptr<TokenType> m_type;
std::map<util::GUID, TokenType::PropType> m_properties;
};
} // namespace fggl::grid
#endif //FGGL_GRID_TOKENS_HPP
......@@ -29,25 +29,48 @@ namespace fggl::gui {
}
void add(std::unique_ptr<Widget> widget);
virtual inline void layout() {}
bool contains(const math::vec2 &point) override;
Widget *getChildAt(const math::vec2 &point) override;
inline void update(float deltaTime) override {
for (auto& child : m_children) {
child->update(deltaTime);
}
}
void render(gfx::Paint &paint) override;
void onMouseOver(math::vec2 pos) override;
void onEnter(math::vec2i pos) override;
void onExit(math::vec2i pos) override;
private:
bool m_dirty;
Widget* m_hovered = nullptr;
protected:
std::vector<std::unique_ptr<Widget>> m_children;
};
class GridBox : public Container {
public:
GridBox(uint32_t rows, uint32_t cols, uint32_t padX = 8, uint32_t padY = 8);
void layout() override;
private:
uint32_t m_rows;
uint32_t m_cols;
math::vec2i m_padding;
};
class Panel : public Container {
public:
Panel() = default;
~Panel() = default;
void render(gfx::Paint& paint) override;
void render(gfx::Paint &paint) override;
};
......
......@@ -30,7 +30,10 @@
#include <ft2build.h>
#include FT_FREETYPE_H
namespace fggl::gui {
constexpr const char* DEFAULT_FONT_NAME = "LiberationSans-Regular.ttf";
struct GlyphMetrics {
math::vec2 size;
......@@ -43,45 +46,45 @@ namespace fggl::gui {
explicit FontFace(FT_Face face);
~FontFace();
FontFace(const FontFace&) = delete;
FontFace(FontFace&&) = delete;
FontFace& operator=(const FontFace&) = delete;
FontFace& operator=(FontFace&&) = delete;
FontFace(const FontFace &) = delete;
FontFace(FontFace &&) = delete;
FontFace &operator=(const FontFace &) = delete;
FontFace &operator=(FontFace &&) = delete;
inline GlyphMetrics& metrics(char letter){
inline GlyphMetrics &metrics(char letter) {
auto itr = m_metrics.find(letter);
if ( itr == m_metrics.end() ) {
if (itr == m_metrics.end()) {
return populateMetrics(letter);
}
return itr->second;
}
math::vec2 stringSize(const std::string& text);
void texture(char letter, int& width, int& height, void** buff);
math::vec2 stringSize(const std::string &text);
void texture(char letter, int &width, int &height, unsigned char **buff);
private:
FT_Face m_face;
std::map<char, GlyphMetrics> m_metrics;
GlyphMetrics& populateMetrics(char letter);
GlyphMetrics &populateMetrics(char letter);
};
class FontLibrary {
public:
constexpr static const modules::ModuleService service = modules::make_service("fggl::gui::font");
constexpr static const auto service = modules::make_service("fggl::gui::font");
FontLibrary(data::Storage* storage);
explicit FontLibrary(data::Storage *storage);
~FontLibrary();
// copy and moving not needed
FontLibrary(const FontLibrary&) = delete;
FontLibrary(FontLibrary&&) = delete;
FontLibrary& operator=(const FontLibrary&) = delete;
FontLibrary& operator=(FontLibrary&&) = delete;
FontLibrary(const FontLibrary &) = delete;
FontLibrary(FontLibrary &&) = delete;
FontLibrary &operator=(const FontLibrary &) = delete;
FontLibrary &operator=(FontLibrary &&) = delete;
inline std::shared_ptr<FontFace> getFont(const std::string& name) {
inline std::shared_ptr<FontFace> getFont(const std::string &name) {
auto fontItr = m_cache.find(name);
if ( fontItr != m_cache.end() ) {
if (fontItr != m_cache.end()) {
return fontItr->second;
}
......@@ -89,7 +92,7 @@ namespace fggl::gui {
auto path = m_storage->resolvePath(data::StorageType::Data, name);
FT_Face face;
if ( FT_New_Face(m_context, path.string().c_str(), 0, &face) ) {
if (FT_New_Face(m_context, path.string().c_str(), 0, &face)) {
return nullptr;
}
FT_Set_Pixel_Sizes(face, 0, 18);
......@@ -100,10 +103,18 @@ namespace fggl::gui {
return ptr;
}
inline void setDefaultFont(const std::string& name) {
m_defaultFont = getFont(name);
}
inline std::shared_ptr<FontFace> getDefaultFont() const {
return m_defaultFont;
}
private:
FT_Library m_context;
data::Storage* m_storage;
data::Storage *m_storage;
std::map<const std::string, std::shared_ptr<FontFace>> m_cache;
std::shared_ptr<FontFace> m_defaultFont;
};
} // nmespace fggl::gui
......
/*
* 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 char *name = "fggl::gui::FreeType";
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) {
if ( service == FontLibrary::service ) {
bool FreeType::factory(modules::ServiceName service, modules::Services &services) {
if (service == FontLibrary::service) {
auto storage = services.get<data::Storage>();
services.create< FontLibrary >(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
......@@ -45,53 +45,55 @@ namespace fggl::gui {
* @return RGB, in the range 0,1 for each component
*/
inline math::vec3 HSVtoNormal(float hue, float saturation, float value) {
assert( 0 < hue && hue <= 360);
assert( 0 < saturation && saturation <= 1);
assert( 0 < value && value <= 1);
assert(0 < hue && hue <= 360);
assert(0 < saturation && saturation <= 1);
assert(0 < value && value <= 1);
const float chroma = value * saturation;
const float x = chroma * (1 - std::fabs( std::fmod(hue / 60.0F, 2.0F) - 1.0F) );
math::vec3 tmp{0,0,0};
if ( 0 <= hue && hue < 60 ) {
tmp = {chroma,x,0};
} else if ( 60 <= hue && hue < 120 ) {
const float x = chroma * (1 - std::fabs(std::fmod(hue / 60.0F, 2.0F) - 1.0F));
math::vec3 tmp{0, 0, 0};
if (0 <= hue && hue < 60) {
tmp = {chroma, x, 0};
} else if (60 <= hue && hue < 120) {
tmp = {x, chroma, 0};
} else if (120 <= hue && hue < 180 ) {
} else if (120 <= hue && hue < 180) {
tmp = {0, chroma, x};
} else if ( 180 <= hue && hue < 240 ) {
} else if (180 <= hue && hue < 240) {
tmp = {0, x, chroma};
} else if ( 240 <= hue && hue < 300) {
} else if (240 <= hue && hue < 300) {
tmp = {x, 0, chroma};
} else if ( 300 <= hue && hue < 360) {
} else if (300 <= hue && hue < 360) {
tmp = {chroma, 0, x};
}
return tmp + (value - chroma);
}
void draw_box( gfx::Path2D& path, math::vec2 topLeft, math::vec2 bottomRight);
void draw_progress( gfx::Path2D& path, math::vec2 topLeft, math::vec2 size, float value);
void draw_slider( gfx::Path2D& path, math::vec2 topLeft, math::vec2 size, float value);
void draw_button( gfx::Path2D& path, math::vec2 topLeft, math::vec2 size, bool active, bool pressed);
void draw_box(gfx::Path2D &path, math::vec2 topLeft, math::vec2 bottomRight);
void draw_progress(gfx::Path2D &path, math::vec2 topLeft, math::vec2 size, float value);
void draw_slider(gfx::Path2D &path, math::vec2 topLeft, math::vec2 size, float value);
void draw_button(gfx::Path2D &path, math::vec2 topLeft, math::vec2 size, bool active, bool pressed);
struct Bounds2D {
math::vec2 topLeft;
math::vec2 size;
Bounds2D() = default;
inline Bounds2D(math::vec2 pos, math::vec2 a_size) : topLeft(pos), size(a_size) {}
inline bool contains(math::vec2 point) {
return ! ( (point.x > topLeft.x + size.x) ||
(point.x < topLeft.x ) ||
(point.y > topLeft.y + size.y) ||
(point.y < topLeft.y)
);
return !((point.x > topLeft.x + size.x) ||
(point.x < topLeft.x) ||
(point.y > topLeft.y + size.y) ||
(point.y < topLeft.y)
);
}
};
class Widget {
public:
Widget() = default;
inline Widget(math::vec2 pos, math::vec2 size) : m_bounds(pos, size) {}
virtual ~Widget() = default;
......@@ -113,28 +115,43 @@ namespace fggl::gui {
return m_bounds.size;
}
virtual inline bool contains(const math::vec2 &point){
virtual inline bool contains(const math::vec2 &point) {
return m_bounds.contains(point);
};
virtual inline Widget *getChildAt(const math::vec2 &point) {
if ( !contains(point) ) {
if (!contains(point)) {
return nullptr;
}
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 onExit() {}
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;
}
protected:
bool m_hover = false;
private:
Bounds2D m_bounds;
};
}; //namespace fggl::gui
#endif