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 1391 additions and 244 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 20/08/22.
//
#ifndef FGGL_ENTITY_GRIDWORLD_ZONE_HPP
#define FGGL_ENTITY_GRIDWORLD_ZONE_HPP
#include <array>
#include <vector>
#include "fggl/math/types.hpp"
#include "fggl/assets/types.hpp"
#include "fggl/entity/entity.hpp"
namespace fggl::entity::grid {
using GridPos = math::vec2i;
constexpr auto ASSET_TILESET = assets::AssetType::make("tileset");
template<typename T, uint32_t width, uint32_t height>
struct Grid {
public:
Grid() = default;
inline T& get(GridPos pos) {
assert(inBounds(pos));
return m_cells[getCellIndex(pos)];
}
const T& get(GridPos pos) const {
assert(inBounds(pos));
return m_cells[getCellIndex(pos)];
}
inline void set(GridPos pos, T value) {
assert(inBounds(pos));
m_cells[getCellIndex(pos)] = value;
}
inline bool inBounds(GridPos pos) const {
return 0 <= pos.x && pos.x <= size.x &&
0 <= pos.y && pos.y <= size.y;
}
private:
constexpr static math::vec2i size = math::vec2ui(width, height);
std::array<T, size.x * size.y> m_cells;
inline uint32_t getCellIndex(GridPos pos) const {
assert( inBounds(pos));
return pos.y * size.x + pos.x;
}
};
struct FloorTile {
constexpr static uint8_t IMPOSSIBLE = 0;
uint8_t moveCost = IMPOSSIBLE;
math::vec3 colour = gfx::colours::CYAN;
};
struct WallTile {
bool render = false;
math::vec3 colour = gfx::colours::CYAN;
};
struct WallState {
uint32_t north = 0;
uint32_t west = 0;
};
struct TileSet {
std::vector<FloorTile> m_floors;
std::vector<WallTile> m_walls;
};
/**
* A 2D representation of a space.
*
* @tparam width the grid width in units
* @tparam height the grid height in units
*/
template<uint32_t width, uint32_t height>
struct Area2D {
public:
constexpr static std::array<math::vec2i, 4> DIRECTIONS{{ {-1, 0}, {0, -1}, {1, 0}, {0, 1} }};
inline explicit Area2D(TileSet& tiles) : m_tiles(tiles) {
clear();
}
[[nodiscard]]
inline bool inBounds(GridPos pos) const {
return 0 <= pos.x && pos.x <= width
&& 0 <= pos.y && pos.y <= height;
}
void clear() {
WallState noWall;
for (auto xPos = 0U; xPos<width; ++xPos) {
for (auto yPos=0U; yPos<height; ++yPos) {
m_floors.set({xPos, yPos}, 0);
m_walls.set({xPos, yPos}, noWall);
}
}
}
inline FloorTile& floorAt(uint32_t xPos, uint32_t yPos) {
return m_tiles.m_floors.at( m_floors.get({xPos, yPos}) );
}
inline void setFloorAt(uint32_t xPos, uint32_t yPos, uint32_t floor) {
m_floors.set({xPos, yPos}, floor);
}
inline WallTile& wallAt(uint32_t xPos, uint32_t yPos, bool north) {
if (north) {
return m_tiles.m_walls.at(m_walls.get({xPos, yPos}).north);
} else {
return m_tiles.m_walls.at(m_walls.get({xPos, yPos}).west);
}
}
inline void setWallAt(uint32_t xPos, uint32_t yPos, bool north, uint32_t wall) {
auto& state = m_walls.get({xPos, yPos});
if (north) {
state.north = wall;
} else {
state.west = wall;
}
}
inline bool canMove(GridPos pos) const {
if ( !inBounds(pos) ) {
return false;
}
return m_tiles.m_floors[m_floors.get(pos)].moveCost != FloorTile::IMPOSSIBLE;
}
inline bool canMove(GridPos pos, math::vec2i dir) const {
return canMove(pos + dir) && !blocked(pos, dir);
}
inline bool blocked(GridPos pos, math::vec2i dir) const;
EntityManager& entities() {
return m_entities;
}
inline void neighbours(math::vec2i pos, std::vector<math::vec2i> &neighbours) const {
for (auto direction : DIRECTIONS) {
if ( canMove(pos, direction) ) {
auto result = pos + direction;
neighbours.push_back(result);
}
}
}
private:
TileSet& m_tiles;
Grid<uint32_t, width, height> m_floors;
Grid<WallState, width + 1, height + 1> m_walls;
EntityManager m_entities;
};
template<uint32_t width, uint32_t height>
bool Area2D<width, height>::blocked(GridPos pos, math::vec2i dir) const {
auto targetPos = pos;
if ( dir.x == 1 || dir.y == 1 ) {
targetPos = pos + dir;
}
if ( !inBounds(targetPos) ) {
return true;
}
auto& wallObj = m_walls.get(targetPos);
if ( dir.y != 0 ) {
return wallObj.north != 0;
}
if (dir.x != 0) {
return wallObj.west != 0;
}
return true;
}
} // namespace fggl::entity::gridworld
#endif //FGGL_ENTITY_GRIDWORLD_ZONE_HPP
/*
* This file is part of FGGL.
*
* FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with FGGL.
* If not, see <https://www.gnu.org/licenses/>.
*/
//
// Created by webpigeon on 23/07/22.
//
#ifndef FGGL_ENTITY_LOADER_LOADER_HPP
#define FGGL_ENTITY_LOADER_LOADER_HPP
#include <functional>
#include <map>
#include <utility>
#include "fggl/util/guid.hpp"
#include "fggl/modules/module.hpp"
#include "fggl/entity/entity.hpp"
#include "fggl/entity/loader/spec.hpp"
#include "fggl/assets/loader.hpp"
namespace fggl::entity {
constexpr auto ENTITY_PROTOTYPE = assets::make_asset_type("entity/prototype");
constexpr auto ENTITY_SCENE = assets::make_asset_type("entity/scene");
using FactoryFunc = std::function<void(const ComponentSpec &config, EntityManager &, const EntityID &, modules::Services &svc)>;
using CustomiseFunc = std::function<void(EntityManager &, const EntityID &)>;
struct FactoryInfo {
FactoryFunc factory;
CustomiseFunc finalise = nullptr;
};
class EntityFactory {
public:
constexpr static const modules::ServiceName service = modules::make_service("fggl::entity:Factory");
inline EntityFactory(modules::Services &services) : m_services(services) {}
EntityID create(const EntityType &spec, EntityManager &manager, const CustomiseFunc &customise = nullptr) {
std::vector<CustomiseFunc> finishers;
// build the setup
auto entity = setupComponents(spec, manager, finishers);
if ( entity == entity::INVALID ) {
debug::error("EntityFactory: failed to build from prototype {}", std::to_string(spec.get()));
return entity::INVALID;
}
// if requested, allow the user to customize the creation
if ( customise != nullptr ) {
customise(manager, entity);
}
// run finishers for components
for ( auto& finisher : finishers ) {
finisher( manager, entity );
}
// use metadata to finalise
processTags(manager, entity, spec);
return entity;
}
void processTags(EntityManager& manager, EntityID id, EntityType spec) {
auto type = m_prototypes.at(spec);
for ( auto& tag : type.tags ) {
manager.addTag(id, tag);
}
}
void log_known_types() const {
debug::debug("dumping known types:");
for(const auto& [k,v] : m_factories) {
debug::debug("\ttype: {}", k);
}
}
void define(EntityType type, const EntitySpec &spec) {
m_prototypes[type] = spec;
}
// ability to set and unset factory functions
inline void bind(const ComponentID &configNode, FactoryFunc factory, CustomiseFunc finalise = nullptr) {
m_factories[configNode].factory = std::move(factory);
m_factories[configNode].finalise = std::move(finalise);
}
inline void unbind(const ComponentID &configNode) {
m_factories.erase(configNode);
}
inline FactoryInfo& getInfo(ComponentID comp) {
return m_factories.at(comp);
}
private:
modules::Services m_services;
std::map<ComponentID, FactoryInfo> m_factories;
std::map<EntityType, EntitySpec> m_prototypes;
entity::EntityID setupComponents(EntityType entityType,
EntityManager &manager,
std::vector<CustomiseFunc> &finishers) {
assert(entityType != NO_PARENT && "setup components called with NO_PARENT?!");
auto entity = manager.create();
std::vector<ComponentID> loadedComps;
auto currentType = entityType;
while (currentType != NO_PARENT) {
const auto& specEntry = m_prototypes.find( currentType );
if ( specEntry == m_prototypes.end() ) {
debug::warning("Asked to setup {}, for {} but was not a known prototype", specEntry->first, entityType);
return entity::INVALID;
}
auto entitySpec = specEntry->second;
debug::debug("constructing {} for {} ({} comps)", currentType, entityType, entitySpec.components.size());
assert( entitySpec.ordering.size() == entitySpec.components.size() && "ordering incorrect size, bad things happend!" );
for (auto &component : entitySpec.ordering) {
// skip comps loaded by children
if (std::find(loadedComps.begin(), loadedComps.end(), component) != loadedComps.end()) {
continue;
}
try {
auto &data = getComponent(entitySpec, component);
loadedComps.push_back(component);
auto &info = m_factories.at(component);
info.factory(data, manager, entity, m_services);
if (info.finalise != nullptr) {
finishers.push_back(info.finalise);
}
} catch (std::out_of_range &ex) {
debug::error( "EntityFactory: Unknown component factory type '{}'", component );
log_known_types();
manager.destroy(entity);
return entity::INVALID;
}
}
currentType = entitySpec.parent;
}
return entity;
}
ComponentSpec &getComponent(EntitySpec &prototype, util::GUID compToken) {
auto compItr = prototype.components.find(compToken);
if (compItr != prototype.components.end()) {
return compItr->second;
}
if (prototype.parent == NO_PARENT) {
throw std::out_of_range("EntityFactory: no such component!");
}
return getComponent(m_prototypes.at(prototype.parent), compToken);
}
};
assets::AssetRefRaw load_prototype(assets::Loader* loader, const assets::AssetID &guid, const assets::LoaderContext& data, EntityFactory* factory);
assets::AssetRefRaw load_scene(assets::Loader* loader, const assets::AssetID& asset, const assets::LoaderContext& data, void* ptr);
} // namespace fggl::entity
#endif //FGGL_ENTITY_LOADER_LOADER_HPP
......@@ -13,37 +13,23 @@
*/
//
// Created by webpigeon on 04/06/22.
// Created by webpigeon on 24/07/22.
//
#ifndef FGGL_ECS3_PROTOTYPE_LOADER_HPP
#define FGGL_ECS3_PROTOTYPE_LOADER_HPP
#ifndef FGGL_ENTITY_LOADER_SERIALISE_HPP
#define FGGL_ENTITY_LOADER_SERIALISE_HPP
#include "yaml-cpp/yaml.h"
#include "fggl/math/types.hpp"
#include "fggl/phys/types.hpp"
#include "fggl/gfx/phong.hpp"
#include "fggl/data/storage.hpp"
#include "fggl/data/model.hpp"
#include "fggl/data/procedural.hpp"
#include "fggl/ecs3/ecs.hpp"
#include "fggl/ecs/component_fwd.hpp"
namespace fggl::ecs3 {
void load_prototype_node( ecs3::World& world, const YAML::Node& node);
void load_prototype_file( ecs3::World& world, data::Storage& storage, const std::string& name );
} // namespace fggl::ecs3
#include "fggl/phys/types.hpp"
namespace YAML {
template<>
struct convert<fggl::math::vec3> {
static Node encode(const fggl::math::vec3& rhs){
static Node encode(const fggl::math::vec3 &rhs) {
Node node;
node.push_back(rhs.x);
node.push_back(rhs.y);
......@@ -51,8 +37,8 @@ namespace YAML {
return node;
}
static bool decode(const Node& node, fggl::math::vec3& rhs) {
if ( !node.IsSequence() || node.size() != 3) {
static bool decode(const Node &node, fggl::math::vec3 &rhs) {
if (!node.IsSequence() || node.size() != 3) {
return false;
}
......@@ -64,71 +50,108 @@ namespace YAML {
};
template<>
struct convert<fggl::math::quat> {
static Node encode(const fggl::math::quat& rhs){
struct convert<fggl::math::vec2> {
static Node encode(const fggl::math::vec2 &rhs) {
Node node;
node.push_back(rhs.x);
node.push_back(rhs.y);
node.push_back(rhs.z);
node.push_back(rhs.w);
return node;
}
static bool decode(const Node& node, fggl::math::quat& rhs) {
if ( !node.IsSequence() || node.size() != 3) {
static bool decode(const Node &node, fggl::math::vec2 &rhs) {
if (!node.IsSequence() || node.size() != 2) {
return false;
}
rhs.x = node[0].as<float>();
rhs.y = node[1].as<float>();
rhs.z = node[2].as<float>();
rhs.w = node[3].as<float>();
return true;
}
};
constexpr const char* TYPE_KINEMATIC = "kinematic";
constexpr const char* TYPE_STATIC = "static";
constexpr const char* TYPE_DYNAMIC = "dynamic";
template<>
struct convert<fggl::data::Vertex> {
static Node encode(const fggl::data::Vertex &rhs) {
Node node;
node["position"] = rhs.posititon;
node["normal"] = rhs.normal;
node["colour"] = rhs.colour;
node["texPos"] = rhs.texPos;
return node;
}
static bool decode(const Node &node, fggl::data::Vertex &rhs) {
if (!node.IsSequence() || node.size() != 2) {
return false;
}
rhs.posititon = node["position"].as<fggl::math::vec3>();
rhs.normal = node["normal"].as<fggl::math::vec3>();
rhs.colour = node["colour"].as<fggl::math::vec3>();
rhs.texPos = node["texPos"].as<fggl::math::vec2>();
return true;
}
};
template<>
struct convert<fggl::phys::BodyType> {
static Node encode(const fggl::phys::BodyType& rhs) {
switch (rhs) {
case fggl::phys::BodyType::KINEMATIC:
return Node(TYPE_KINEMATIC);
case fggl::phys::BodyType::STATIC:
return Node(TYPE_STATIC);
default:
case fggl::phys::BodyType::DYNAMIC:
return Node(TYPE_DYNAMIC);
static Node encode(const fggl::phys::BodyType &rhs) {
Node node;
if (rhs == fggl::phys::BodyType::STATIC) {
node = "static";
} else if (rhs == fggl::phys::BodyType::DYNAMIC) {
node = "dynamic";
} else if (rhs == fggl::phys::BodyType::KINEMATIC) {
node = "kinematic";
}
return node;
}
static bool decode(const Node& node, fggl::phys::BodyType& rhs) {
const auto value = node.as<std::string>();
if ( value == TYPE_KINEMATIC ) {
rhs = fggl::phys::BodyType::KINEMATIC;
} else if ( value == TYPE_STATIC ) {
static bool decode(const Node &node, fggl::phys::BodyType &rhs) {
auto strVal = node.as<std::string>();
if (strVal == "static") {
rhs = fggl::phys::BodyType::STATIC;
} else {
return true;
}
if (strVal == "dynamic") {
rhs = fggl::phys::BodyType::DYNAMIC;
return true;
}
return true;
if (strVal == "kinematic") {
rhs = fggl::phys::BodyType::KINEMATIC;
return true;
}
return false;
}
};
template<>
struct convert<fggl::util::GUID> {
static Node encode(const fggl::util::GUID &rhs) {
Node node;
node = rhs.get();
return node;
}
}
static bool decode(const Node &node, fggl::util::GUID &rhs) {
auto longVal = node.as<uint64_t>(0);
namespace fggl::ecs {
if (longVal == 0) {
// probably meant to hash it...
auto stringVal = node.as<std::string>();
rhs = fggl::util::make_guid_rt(stringVal);
return true;
}
constexpr int DEFAULT_STACKS = 16;
constexpr int DEFAULT_SLICES = 16;
constexpr const char* SHAPE_SPHERE_VALUE{"sphere"};
constexpr const char* SHAPE_BOX_VALUE{"box"};
// it's probably pre-hashed...
rhs = fggl::util::GUID::make(longVal);
return true;
}
};
// scene template specialisations
}
#endif //FGGL_ECS3_PROTOTYPE_LOADER_HPP
#endif //FGGL_ENTITY_LOADER_SERIALISE_HPP
/*
* This file is part of FGGL.
*
* FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with FGGL.
* If not, see <https://www.gnu.org/licenses/>.
*/
//
// Created by webpigeon on 23/07/22.
//
#ifndef FGGL_ENTITY_LOADER_SPEC_HPP
#define FGGL_ENTITY_LOADER_SPEC_HPP
#include "fggl/util/guid.hpp"
#include "fggl/entity/loader/serialise.hpp"
#include <map>
namespace fggl::entity {
using ComponentID = util::GUID;
using EntityType = util::GUID;
constexpr EntityType NO_PARENT = util::make_guid("FGGL_NULL_PARENT");
struct ComponentSpec {
template<typename T>
T get(const std::string &key, const T &fallback) const {
return config[key].template as<T>(fallback);
}
template<typename T>
void set(const std::string &key, const T &value) {
config[key] = value;
}
inline bool has(const std::string &key) const {
return (bool) (config[key]);
}
YAML::Node config;
};
struct EntitySpec {
EntityType parent = NO_PARENT;
std::vector<util::GUID> tags;
std::vector<ComponentID> ordering;
std::map<ComponentID, ComponentSpec> components;
inline void addComp(ComponentID cmp, const ComponentSpec& spec) {
components[cmp] = spec;
ordering.push_back(cmp);
}
};
} // namespace fggl::entity
#endif //FGGL_ENTITY_LOADER_SPEC_HPP
/*
* This file is part of FGGL.
*
* FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with FGGL.
* If not, see <https://www.gnu.org/licenses/>.
*/
//
// Created by webpigeon on 24/07/22.
//
#ifndef FGGL_ENTITY_MODULE_HPP
#define FGGL_ENTITY_MODULE_HPP
#include "fggl/modules/module.hpp"
#include "fggl/assets/loader.hpp"
#include "fggl/assets/packed/adapter.hpp"
#include "fggl/entity/loader/loader.hpp"
namespace fggl::entity {
constexpr auto MIME_SCENE = assets::from_mime("x-fggl/scene");
struct ECS {
constexpr static const char *name = "fggl::entity::ECS";
constexpr static const std::array<modules::ServiceName, 1> provides = {
EntityFactory::service
};
constexpr static const std::array<modules::ServiceName, 2> depends = {
assets::Loader::service,
assets::CheckinAdapted::service
};
static bool factory(modules::ServiceName name, modules::Services &serviceManager);
};
void install_component_factories(EntityFactory *factory);
} // namespace fggl::entity
#endif //FGGL_ENTITY_MODULE_HPP
......@@ -29,6 +29,7 @@
#include "fggl/audio/null_audio.hpp"
#include "fggl/audio/openal/module.hpp"
//! Root namespace
namespace fggl {
}
......
......@@ -15,7 +15,8 @@
#ifndef FGGL_GFX_CAMERA_HPP
#define FGGL_GFX_CAMERA_HPP
#include <fggl/math/types.hpp>
#include "fggl/math/types.hpp"
#include "fggl/entity/entity.hpp"
namespace fggl::gfx {
......@@ -26,36 +27,42 @@ namespace fggl::gfx {
float fov = glm::radians(45.0f);
float nearPlane = 0.1f;
float farPlane = 100.0f;
inline math::mat4 perspective() const {
return glm::perspective(fov, aspectRatio, nearPlane, farPlane);
}
};
inline math::mat4 calc_proj_matrix(const Camera* camera) {
return glm::perspective(camera->fov, camera->aspectRatio, camera->nearPlane, camera->farPlane);
inline math::mat4 calc_proj_matrix(const Camera &camera) {
return glm::perspective(camera.fov, camera.aspectRatio, camera.nearPlane, camera.farPlane);
}
inline math::Ray get_camera_ray(const ecs3::World& world, const ecs3::entity_t camera, math::vec2 position) {
auto* const camTransform = world.get<fggl::math::Transform>(camera);
auto* const camComp = world.get<fggl::gfx::Camera>(camera);
inline math::Ray get_camera_ray(const entity::EntityManager &world,
const entity::EntityID camera,
math::vec2 position) {
auto &camTransform = world.get<fggl::math::Transform>(camera);
auto &camComp = world.get<fggl::gfx::Camera>(camera);
const auto projMatrix = fggl::gfx::calc_proj_matrix(camComp);
const auto viewMatrix = fggl::math::calc_view_matrix(camTransform);
glm::vec4 startNDC {
glm::vec4 startNDC{
position.x,
position.y,
-1.0f,
1.0f
};
glm::vec4 endNDC {
glm::vec4 endNDC{
position.x,
position.y,
0.0f,
1.0f
};
fggl::math::mat4 M = glm::inverse( projMatrix * viewMatrix );
fggl::math::mat4 M = glm::inverse(projMatrix * viewMatrix);
glm::vec3 start = M * startNDC;
glm::vec3 end = M * endNDC;
return { start, glm::normalize(end - start) };
return {start, glm::normalize(end - start)};
}
};
......
......@@ -20,9 +20,10 @@
#define FGGL_GFX_INTERFACES_HPP
#include "fggl/gfx/paint.hpp"
#include "fggl/ecs3/ecs.hpp"
#include "fggl/entity/entity.hpp"
#include "fggl/modules/module.hpp"
//! Classes responsible for rendering content
namespace fggl::gfx {
struct Bounds {
......@@ -34,7 +35,7 @@ namespace fggl::gfx {
class Graphics {
public:
constexpr static const modules::ModuleService service = modules::make_service("fggl::gfx::Graphics");
constexpr static const auto service = modules::make_service("fggl::gfx::Graphics");
virtual ~Graphics() = default;
virtual void clear() = 0;
......@@ -43,10 +44,9 @@ namespace fggl::gfx {
virtual Bounds canvasBounds() = 0;
virtual void draw2D(const Paint &paint) = 0;
virtual void drawScene(ecs3::World&) = 0;
virtual void drawScene(entity::EntityManager &, bool debugMode = false) = 0;
};
} // namespace fggl::gfx
#endif //FGGL_GFX_INTERFACES_HPP
......@@ -24,7 +24,6 @@
* FGGL OpenGL 4.x rendering backend.
*/
namespace fggl::gfx {
}
#endif
......@@ -24,6 +24,7 @@
#endif
#include <glad/glad.h>
typedef void* (* GLADloadproc)(const char *name);
typedef void *(*GLADloadproc)(const char *name);
#endif
......@@ -16,10 +16,9 @@
#define FGGL_GFX_OGL_RENDERER_HPP
#include <fggl/data/model.hpp>
#include <fggl/ecs3/ecs.hpp>
#include <fggl/gfx/ogl/backend.hpp>
#include <fggl/gfx/ogl/shader.hpp>
#include "fggl/gfx/ogl4/models.hpp"
#include "fggl/gfx/ogl4/canvas.hpp"
#include "fggl/gfx/ogl4/debug.hpp"
......@@ -31,32 +30,7 @@
namespace fggl::gfx {
enum GlRenderType {
triangles = GL_TRIANGLES,
triangle_strip = GL_TRIANGLE_STRIP
};
struct GlRenderToken {
constexpr static const char name[] = "RenderToken";
GLuint vao;
GLuint buffs[2];
GLuint idxOffset;
GLsizei idxSize;
GLuint pipeline;
GLuint restartVertex;
GlRenderType renderType = triangles;
};
class GlMeshRenderer {
public:
using token_t = GlRenderToken;
token_t upload(fggl::data::Mesh &mesh);
void render(ecs3::World &ecs, ecs3::entity_t camera, float dt);
float total;
};
using GlFunctionLoader = GLADloadproc;
/**
* Class responsible for managing the OpenGL context.
......@@ -67,16 +41,16 @@ namespace fggl::gfx {
*/
class OpenGL4Backend : public Graphics {
public:
explicit OpenGL4Backend(data::Storage* storage, gui::FontLibrary* fonts);
explicit OpenGL4Backend(data::Storage *storage, gui::FontLibrary *fonts, assets::AssetManager *assets, GlFunctionLoader loader);
~OpenGL4Backend() override = default;
// copy bad
OpenGL4Backend(const OpenGL4Backend&) = delete;
OpenGL4Backend& operator=(const OpenGL4Backend&) = delete;
OpenGL4Backend(const OpenGL4Backend &) = delete;
OpenGL4Backend &operator=(const OpenGL4Backend &) = delete;
// move (probably) bad
OpenGL4Backend(OpenGL4Backend&&) = delete;
OpenGL4Backend&& operator=(OpenGL4Backend&&) = delete;
OpenGL4Backend(OpenGL4Backend &&) = delete;
OpenGL4Backend &&operator=(OpenGL4Backend &&) = delete;
/**
* Clear the backing buffer.
......@@ -103,12 +77,12 @@ namespace fggl::gfx {
*
* @param world the world to render
*/
void drawScene(ecs3::World& world) override;
void drawScene(entity::EntityManager &world, bool debugMode=false) override;
/**
* Get the 2D canvas bounds.
*
* @return
* @return the canvas bounds
*/
inline Bounds canvasBounds() override {
return m_canvasRenderer->bounds();
......@@ -119,9 +93,9 @@ namespace fggl::gfx {
std::unique_ptr<ogl4::CanvasRenderer> m_canvasRenderer;
std::unique_ptr<ogl4::DebugRenderer> m_debugRenderer;
std::unique_ptr<ShaderCache> m_cache;
GLuint m_canvasPipeline;
data::Storage* m_storage;
gui::FontLibrary* m_fontLibrary;
std::shared_ptr<ogl::Shader> m_canvasPipeline;
data::Storage *m_storage;
gui::FontLibrary *m_fontLibrary;
};
}; // namespace fggl::gfx
......
......@@ -17,6 +17,8 @@
#include <cstdio>
#include <fggl/gfx/ogl/backend.hpp>
#include "fggl/gfx/ogl/types.hpp"
#include <fggl/data/storage.hpp>
#include <filesystem>
......@@ -36,6 +38,16 @@ namespace fggl::gfx {
// optional parts
std::string geometry;
bool hasGeom = false;
static ShaderConfig named(const std::string &name, bool hasGeom = false) {
return {
.name = name,
.vertex = name + "_vert.glsl",
.fragment = name + "_frag.glsl",
.geometry = hasGeom ? name + "_geom.glsl" : "",
.hasGeom = hasGeom
};
}
};
struct ShaderSources {
......@@ -45,14 +57,6 @@ namespace fggl::gfx {
std::string geometrySource;
};
inline ShaderConfig ShaderFromName(const std::string &name) {
return {
name,
name + "_vert.glsl",
name + "_frag.glsl"
};
}
struct BinaryCache {
void *data = nullptr;
GLsizei size = 0;
......@@ -61,15 +65,17 @@ namespace fggl::gfx {
class ShaderCache {
public:
ShaderCache(fggl::data::Storage* storage);
using ShaderPtr = std::shared_ptr<ogl::Shader>;
ShaderCache(fggl::data::Storage *storage);
~ShaderCache() = default;
GLuint load(const ShaderConfig &config);
GLuint load(const ShaderSources &sources, bool allowBinaryCache);
ShaderPtr load(const ShaderConfig &config);
ShaderPtr load(const ShaderSources &sources, bool allowBinaryCache);
GLuint getOrLoad(const ShaderConfig &config);
ShaderPtr getOrLoad(const ShaderConfig &config);
GLuint get(const std::string &name);
ShaderPtr get(const std::string &name);
/**
* Fallback pipelines.
......@@ -80,15 +86,15 @@ namespace fggl::gfx {
void initFallbackPipelines();
private:
std::shared_ptr<fggl::data::Storage> m_storage;
std::unordered_map<std::string, GLuint> m_shaders;
fggl::data::Storage* m_storage;
std::unordered_map<std::string, ShaderPtr> m_shaders;
// extensions
void setupIncludes();
// opengl operations
bool readAndCompileShader(const std::string& filename, GLuint shader);
bool compileShaderFromSource(const std::string& source, GLuint);
bool readAndCompileShader(const std::string &filename, GLuint shader);
bool compileShaderFromSource(const std::string &source, GLuint);
// file io operations
bool loadFromDisk(GLuint pid, const std::string &pipelineName);
......
......@@ -27,6 +27,7 @@
#include "fggl/gfx/ogl/common.hpp"
#include "fggl/math/types.hpp"
#include "fggl/data/texture.hpp"
namespace fggl::gfx::ogl {
......@@ -45,37 +46,43 @@ namespace fggl::gfx::ogl {
public:
Shader() = default;
inline Shader(GLuint obj) : m_obj(obj) {}
// copy constructor bad
Shader(const Shader&) = delete;
Shader& operator=(const Shader&) = delete;
Shader(const Shader &) = delete;
Shader &operator=(const Shader &) = delete;
// Move ok - if handled properly
Shader(Shader&& other);
Shader& operator=(Shader&& other);
Shader(Shader &&other);
Shader &operator=(Shader &&other);
void use() {
glUseProgram( m_obj );
glUseProgram(m_obj);
}
inline bool hasUniform(const std::string_view& name) const {
auto location = glGetUniformLocation( m_obj, name.data() );
inline bool hasUniform(const std::string_view &name) const {
auto location = glGetUniformLocation(m_obj, name.data());
return location != -1;
}
inline Location uniform(const std::string_view& name) const {
auto location = glGetUniformLocation( m_obj, name.data() );
if ( location == -1 ) {
std::cerr << "error: " << name << " does not exist" << std::endl;
inline Location uniform(const std::string_view &name) const {
auto location = glGetUniformLocation(m_obj, name.data());
if (location == -1) {
debug::warning("uniform {} does not exist", name);
}
return location;
}
inline GLuint shaderID() {
return m_obj;
}
// primatives
inline void setUniformF(Location name, GLfloat value) {
glProgramUniform1f(m_obj, name, value);
}
inline void setUniformI(Location name, GLint value) {
glProgramUniform1i(m_obj, name, value);
}
......@@ -85,15 +92,15 @@ namespace fggl::gfx::ogl {
}
// vector versions (float)
inline void setUniformF(Location name, const math::vec2f& value) {
inline void setUniformF(Location name, const math::vec2f &value) {
glProgramUniform2f(m_obj, name, value.x, value.y);
}
inline void setUniformF(Location name, const math::vec3f& value) {
inline void setUniformF(Location name, const math::vec3f &value) {
glProgramUniform3f(m_obj, name, value.x, value.y, value.z);
}
inline void setUniformF(Location name, const math::vec4f& value) {
inline void setUniformF(Location name, const math::vec4f &value) {
glProgramUniform4f(m_obj, name, value.x, value.y, value.z, value.w);
}
......@@ -111,15 +118,15 @@ namespace fggl::gfx::ogl {
}
// matrix versions
inline void setUniformMtx(Location name, const math::mat2& mtx) {
inline void setUniformMtx(Location name, const math::mat2 &mtx) {
glProgramUniformMatrix2fv(m_obj, name, 1, GL_FALSE, glm::value_ptr(mtx));
}
void setUniformMtx(Location name, const math::mat3& mtx) {
void setUniformMtx(Location name, const math::mat3 &mtx) {
glProgramUniformMatrix3fv(m_obj, name, 1, GL_FALSE, glm::value_ptr(mtx));
}
void setUniformMtx(Location name, const math::mat4& mtx) {
void setUniformMtx(Location name, const math::mat4 &mtx) {
glProgramUniformMatrix4fv(m_obj, name, 1, GL_FALSE, glm::value_ptr(mtx));
}
};
......@@ -165,61 +172,215 @@ namespace fggl::gfx::ogl {
};
enum class PixelFormat {
UNSIGNED_BYTE = GL_UNSIGNED_BYTE,
BYTE,
UNSIGNED_SHORT,
SHORT,
UNSIGNED_INT,
INT,
HALF_FLOAT,
FLOAT,
UNSIGNED_BYTE_3_3_2,
UNSIGNED_BYTE_2_3_3_REV,
UNSIGNED_SHORT_5_6_5,
UNSIGNED_SHORT_5_6_5_REV,
UNSINGED_SHORT_4_4_4_4,
UNSIGNED_SHORT_4_4_4_4_REV,
UNSIGNED_SHORT_5_5_5_1,
UNSIGNED_SHORT_1_5_5_5_REV,
UNSIGNED_INT_8_8_8_8,
UNSIGNED_INT_8_8_8_8_REV,
UNSIGNED_INT_10_10_10_10_2,
UNSIGNED_INT_10_10_10_10_2_REV,
UNSIGNED_BYTE = GL_UNSIGNED_BYTE,
BYTE = GL_BYTE,
UNSIGNED_SHORT = GL_UNSIGNED_SHORT,
SHORT = GL_SHORT,
UNSIGNED_INT = GL_UNSIGNED_INT,
INT = GL_INT,
//HALF_FLOAT = GL_HALF_FLOAT,
FLOAT = GL_FLOAT,
/*
UNSIGNED_BYTE_3_3_2,
UNSIGNED_BYTE_2_3_3_REV,
UNSIGNED_SHORT_5_6_5,
UNSIGNED_SHORT_5_6_5_REV,
UNSINGED_SHORT_4_4_4_4,
UNSIGNED_SHORT_4_4_4_4_REV,
UNSIGNED_SHORT_5_5_5_1,
UNSIGNED_SHORT_1_5_5_5_REV,
UNSIGNED_INT_8_8_8_8,
UNSIGNED_INT_8_8_8_8_REV,
UNSIGNED_INT_10_10_10_10_2,
UNSIGNED_INT_10_10_10_10_2_REV,*/
};
enum class InternalImageFormat {
DepthComponent = GL_DEPTH_COMPONENT,
DepthStencil = GL_DEPTH_STENCIL,
Red = GL_RED,
RedGreen = GL_RG,
RedGreenBlue = GL_RGB,
RedGreenBlueAlpha = GL_RGBA
DepthComponent = GL_DEPTH_COMPONENT,
DepthStencil = GL_DEPTH_STENCIL,
Red = GL_RED,
RedGreen = GL_RG,
RedGreenBlue = GL_RGB,
RedGreenBlueAlpha = GL_RGBA
};
enum class ImageFormat {
R = GL_RED,
RG,
RGB,
RGBA,
R_INT,
RG_INT,
RGB_INT,
RGBA_INT,
BGR,
BGRA,
BGR_INT,
BGRA_INT,
STENTICL_INDEX,
DEPTH_COMPONENT,
DEPTH_STENCIL
R = GL_RED,
RG = GL_RG,
RGB = GL_RGB,
RGBA = GL_RGBA,
R_INT = GL_RED_INTEGER,
RG_INT = GL_RG_INTEGER,
RGB_INT = GL_RGB_INTEGER,
RGBA_INT = GL_RGBA_INTEGER,
BGR = GL_BGR,
BGRA = GL_BGRA,
BGR_INT = GL_BGR_INTEGER,
BGRA_INT = GL_BGRA_INTEGER,
STENTICL_INDEX = GL_STENCIL_INDEX,
DEPTH_COMPONENT = GL_DEPTH_COMPONENT,
DEPTH_STENCIL = GL_DEPTH_STENCIL
};
struct Image {
struct PixelDataArray {
PixelFormat type;
union {
unsigned char *uc;
char *c;
std::uint16_t *us;
std::int16_t *s;
float *f;
std::int32_t *i;
std::uint32_t *ui;
};
bool owning;
inline PixelDataArray(PixelFormat fmt, std::size_t size) : type(fmt), owning(true) {
switch (type) {
case PixelFormat::UNSIGNED_BYTE:
uc = new unsigned char[size];
break;
case PixelFormat::BYTE:
c = new char[size];
break;
case PixelFormat::UNSIGNED_SHORT:
us = new std::uint16_t[size];
break;
case PixelFormat::SHORT:
s = new std::int16_t[size];
break;
case PixelFormat::FLOAT:
f = new float[size];
break;
case PixelFormat::INT:
i = new std::int32_t[size];
break;
case PixelFormat::UNSIGNED_INT:
ui = new std::uint32_t[size];
break;
}
}
inline explicit PixelDataArray(unsigned char* data) : type(PixelFormat::UNSIGNED_BYTE), uc(data), owning(false) {}
inline explicit PixelDataArray(char* data) : type(PixelFormat::BYTE), c(data), owning(false) {}
// no copy
PixelDataArray(const PixelDataArray&) = delete;
PixelDataArray& operator=(const PixelDataArray&) = delete;
// move ok
PixelDataArray(PixelDataArray&& other) : type(other.type), owning(other.owning) {
switch (type) {
case PixelFormat::UNSIGNED_BYTE:
uc = other.uc;
other.uc = nullptr;
break;
case PixelFormat::BYTE:
c = other.c;
other.c = nullptr;
break;
case PixelFormat::UNSIGNED_SHORT:
us = other.us;
other.us = nullptr;
break;
case PixelFormat::SHORT:
s = other.s;
other.s = nullptr;
break;
case PixelFormat::FLOAT:
f = other.f;
other.f = nullptr;
break;
case PixelFormat::INT:
i = other.i;
other.i = nullptr;
break;
case PixelFormat::UNSIGNED_INT:
ui = other.ui;
other.ui = nullptr;
break;
}
}
inline ~PixelDataArray() {
if (owning) {
switch (type) {
case PixelFormat::UNSIGNED_BYTE: delete[] uc;
uc = nullptr;
break;
case PixelFormat::BYTE: delete[] c;
c = nullptr;
break;
case PixelFormat::UNSIGNED_SHORT: delete[] us;
us = nullptr;
break;
case PixelFormat::SHORT: delete[] s;
s = nullptr;
break;
case PixelFormat::FLOAT: delete[] f;
f = nullptr;
break;
case PixelFormat::INT: delete[] i;
i = nullptr;
break;
case PixelFormat::UNSIGNED_INT:
delete[] ui;
ui = nullptr;
break;
}
}
}
void* data() {
switch (type) {
case PixelFormat::UNSIGNED_BYTE:
return uc;
case PixelFormat::BYTE:
return c;
case PixelFormat::UNSIGNED_SHORT:
return us;
case PixelFormat::SHORT:
return s;
case PixelFormat::FLOAT:
return f;
case PixelFormat::INT:
return i;
case PixelFormat::UNSIGNED_INT:
return ui;
}
// unknown type?
return nullptr;
}
};
struct Image {
ImageFormat format;
math::vec2i size;
void* data;
PixelDataArray data;
//Image() = default;
inline Image(ImageFormat fmt, PixelFormat pxFmt, math::vec2i asize) :
format(fmt),
size(asize),
data(pxFmt, asize.x * asize.y){}
inline Image(ImageFormat fmt, math::vec2i asize, PixelDataArray&& adata) :
format(fmt),
size(asize),
data(std::move(adata)) {}
Image(const Image&) = delete;
inline PixelFormat type() const {
return data.type;
}
void* dataPtr() {
return data.data();
}
};
class Texture {
......@@ -227,30 +388,112 @@ namespace fggl::gfx::ogl {
inline explicit Texture(TextureType type) : m_type(type) {
glGenTextures(1, &m_obj);
}
~Texture() {
glDeleteTextures(1, &m_obj);
}
void setup(InternalImageFormat iFmt, math::vec2i size) {
//bind();
glBindTexture( (GLenum)m_type, m_obj );
if ( iFmt == InternalImageFormat::DepthComponent ) {
glTexImage2D((GLenum)
m_type, 0, (GLint) iFmt, size.x, size.y, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, nullptr);
glBindTexture((GLenum) m_type, m_obj);
if (iFmt == InternalImageFormat::DepthComponent) {
glTexImage2D((GLenum)m_type,
0,
(GLint) iFmt,
size.x,
size.y,
0,
GL_DEPTH_COMPONENT,
GL_UNSIGNED_BYTE,
nullptr);
} else {
glTexImage2D((GLenum)
m_type, 0, (GLint) iFmt, size.x, size.y, 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr);
m_type, 0, (GLint) iFmt, size.x, size.y, 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr);
}
}
void setData(InternalImageFormat iFmt, Image& image) {
void setData(InternalImageFormat iFmt, Image &image, PixelFormat extFormat) {
//bind();
glBindTexture( (GLenum)m_type, m_obj );
glTexImage2D( (GLenum)m_type, 0, (GLint)iFmt, image.size.x, image.size.y, 0, (GLenum)image.format, (GLenum)image.type, image.data);
glBindTexture((GLenum) m_type, m_obj);
glTexImage2D((GLenum) m_type,
0,
(GLint) iFmt,
image.size.x,
image.size.y,
0,
(GLenum) image.format,
(GLenum) extFormat,
image.dataPtr());
if ( m_type == TextureType::Tex2D ) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
}
}
void setDataPart(math::vec2i offset, Image& image) {
glTexImage2D( (GLenum)m_type, 0, offset.x, offset.y, image.size.x, image.size.y, (GLenum)image.format, (GLenum)image.type, image.data);
void setData(InternalImageFormat iFmt, Image &image) {
//bind();
glBindTexture((GLenum) m_type, m_obj);
glTexImage2D((GLenum) m_type,
0,
(GLint) iFmt,
image.size.x,
image.size.y,
0,
(GLenum) image.format,
(GLenum) image.type(),
image.dataPtr());
if ( m_type == TextureType::Tex2D ) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
}
}
void setData(InternalImageFormat iFmt, const data::Texture2D *image) {
ImageFormat imageFormat;
if (image->channels == 1) {
imageFormat = ImageFormat::R;
} else if ( image->channels == 2) {
imageFormat = ImageFormat::RG;
} else if ( image->channels == 3) {
imageFormat = ImageFormat::RGB;
} else if ( image->channels == 4) {
imageFormat = ImageFormat::RGBA;
} else {
// unknown image format -> channels mapping, having a bad day!
return;
}
//bind();
glBindTexture((GLenum) m_type, m_obj);
glTexImage2D((GLenum) m_type,
0,
(GLint) iFmt,
image->size.x,
image->size.y,
0,
(GLenum) imageFormat,
GL_UNSIGNED_BYTE,
image->data);
if ( m_type == TextureType::Tex2D ) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
}
}
void setDataPart(math::vec2i offset, Image &image) {
glTexImage2D((GLenum) m_type,
0,
offset.x,
offset.y,
image.size.x,
image.size.y,
(GLenum) image.format,
(GLenum) image.type(),
image.dataPtr());
}
void wrapMode(Wrapping wrap);
......@@ -263,9 +506,9 @@ namespace fggl::gfx::ogl {
* @param textureUnit the texture unit to bind to
*/
inline void bind(unsigned int textureUnit) {
assert( 0 <= textureUnit && textureUnit < GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS );
glActiveTexture( GL_TEXTURE0 + textureUnit );
glBindTexture( (GLenum) m_type, m_obj );
assert(textureUnit < GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS);
glActiveTexture(GL_TEXTURE0 + textureUnit);
glBindTexture((GLenum) m_type, m_obj);
}
private:
......@@ -275,6 +518,8 @@ namespace fggl::gfx::ogl {
void release();
};
constexpr std::size_t NO_RESTART_IDX = 0;
enum class BuffAttrF {
HALF_FLOAT = GL_HALF_FLOAT,
FLOAT = GL_FLOAT,
......@@ -282,7 +527,7 @@ namespace fggl::gfx::ogl {
FIXED = GL_FIXED
};
enum class BuffAttrI{
enum class BuffAttrI {
BYTE = GL_BYTE,
UBYTE = GL_UNSIGNED_BYTE,
SHORT = GL_SHORT,
......@@ -296,7 +541,7 @@ namespace fggl::gfx::ogl {
UINT_PACKED_F = GL_UNSIGNED_INT_10F_11F_11F_REV
};
enum class Primative {
enum class Primitive {
POINT = GL_POINT,
LINE = GL_LINES,
LINE_STRIP = GL_LINE_STRIP,
......@@ -365,51 +610,51 @@ namespace fggl::gfx::ogl {
}
// copy constructor bad
Buffer(const Buffer&) = delete;
Buffer& operator=(const Buffer&) = delete;
Buffer(const Buffer &) = delete;
Buffer &operator=(const Buffer &) = delete;
Buffer(Buffer&& other) : m_obj(other.m_obj), m_capacity(other.m_capacity) {
Buffer(Buffer &&other) : m_obj(other.m_obj), m_capacity(other.m_capacity) {
other.obj_ = 0;
other.m_capacity = 0;
}
Buffer& operator=(Buffer&& other) {
if ( this != &other) {
Buffer &operator=(Buffer &&other) {
if (this != &other) {
release();
std::swap( m_obj, other.m_obj );
std::swap( m_capacity, other.m_capacity );
std::swap(m_obj, other.m_obj);
std::swap(m_capacity, other.m_capacity);
}
}
void bind() const {
assert( m_obj != 0 );
glBindBuffer( (GLenum)T, m_obj );
assert(m_obj != 0);
glBindBuffer((GLenum) T, m_obj);
}
inline bool isValid() const {
return m_obj != 0;
};
void write(GLsizeiptr size, const GLvoid* data, BufUsage usage) {
void write(GLsizeiptr size, const GLvoid *data, BufUsage usage) {
bind();
glBufferData( (GLenum)T, size, data, (GLenum)usage);
glBufferData((GLenum) T, size, data, (GLenum) usage);
}
void update(GLintptr offset, GLsizeiptr size, const void* data) {
void update(GLintptr offset, GLsizeiptr size, const void *data) {
}
template<typename D>
void replace(std::size_t size, const D* data) {
void replace(std::size_t size, const D *data) {
bind();
GLsizeiptr sizePtr = size * sizeof(D);
if ( sizePtr > m_capacity) {
glBufferData( (GLenum)T, sizePtr, data, GL_STREAM_DRAW );
if (sizePtr > m_capacity) {
glBufferData((GLenum) T, sizePtr, data, GL_STREAM_DRAW);
m_capacity = sizePtr;
} else {
glBufferSubData((GLenum)T, 0, sizePtr, data);
glBufferSubData((GLenum) T, 0, sizePtr, data);
}
glBindBuffer( (GLenum)T, 0 );
glBindBuffer((GLenum) T, 0);
}
};
......@@ -432,9 +677,9 @@ namespace fggl::gfx::ogl {
std::size_t offset;
};
// type intrincs to make interface nicer
// type intrinsics to make interface nicer
template<typename T>
struct attr_type{
struct attr_type {
const static BuffAttrF attr;
const static GLint size;
};
......@@ -465,32 +710,32 @@ namespace fggl::gfx::ogl {
~VertexArray();
// copy constructors bad
VertexArray(const VertexArray&) = delete;
VertexArray& operator=(const VertexArray) = delete;
VertexArray(const VertexArray &) = delete;
VertexArray &operator=(const VertexArray) = delete;
// move constructors might be ok
VertexArray(VertexArray&& other) noexcept;
VertexArray& operator=(VertexArray&& other);
VertexArray(VertexArray &&other) noexcept;
VertexArray &operator=(VertexArray &&other);
inline void bind() const {
assert( m_obj != 0);
glBindVertexArray( m_obj );
assert(m_obj != 0);
glBindVertexArray(m_obj);
}
void setAttribute(const ArrayBuffer& buffer, GLuint idx, AttributeF& attr);
void setAttribute(const ArrayBuffer& buffer, GLuint idx, AttributeI& attr, bool normalized);
void setAttributeI(const ArrayBuffer& buffer, GLuint idx, AttributeI& attr);
void setAttribute(const ArrayBuffer &buffer, GLuint idx, AttributeF &attr);
void setAttribute(const ArrayBuffer &buffer, GLuint idx, AttributeI &attr, bool normalized);
void setAttributeI(const ArrayBuffer &buffer, GLuint idx, AttributeI &attr);
void drawElements(const ElementBuffer& buff, Primative drawType, std::size_t size);
void draw(Primative drawType, int first, std::size_t count);
void drawElements(const ElementBuffer &buff, Primitive drawType, std::size_t size);
void draw(Primitive drawType, int first, std::size_t count);
};
// paranoid functions
void bind_vertex_array(GLuint& marker);
void unbind_vertex_array(GLuint& marker);
void bind_vertex_array(GLuint &marker);
void unbind_vertex_array(GLuint &marker);
template<BufType T>
void bind_buffer(GLuint* marker, const Buffer<T>& buff) {
void bind_buffer(GLuint *marker, const Buffer<T> &buff) {
#ifdef FGGL_GL_PARANOID
assert( marker != nullptr );
glGetIntegerv( (GLenum)T, (GLint*) marker );
......@@ -499,7 +744,7 @@ namespace fggl::gfx::ogl {
}
template<BufType T>
void unbind_buffer(GLuint* marker, const Buffer<T>& buff) {
void unbind_buffer(GLuint *marker, const Buffer<T> &buff) {
#ifdef GL_FGGL_PARANOID
assert( marker != nullptr );
glBindVertexArray(marker);
......
......@@ -29,8 +29,8 @@ namespace fggl::gfx::ogl4 {
class CanvasRenderer {
public:
CanvasRenderer(gui::FontLibrary* fonts);
void render(GLuint shader, const gfx::Paint& paint);
CanvasRenderer(gui::FontLibrary *fonts);
void render(ogl::Shader& shader, const gfx::Paint &paint);
inline gfx::Bounds bounds() const {
return m_bounds;
......@@ -41,11 +41,11 @@ namespace fggl::gfx::ogl4 {
ogl::VertexArray m_vao;
ogl::ArrayBuffer m_vertexList;
ogl::ElementBuffer m_indexList;
gui::FontLibrary* m_fonts;
gui::FontLibrary *m_fonts;
ogl::Texture m_fontTex;
void renderShapes(const Paint &paint, GLuint shader);
void renderText(const Paint&, GLuint shader);
void renderShapes(const Paint &paint, ogl::Shader& shader);
void renderText(const Paint &, ogl::Shader& shader);
};
} // namespace fggl::gfx::ogl4
......
......@@ -25,13 +25,13 @@ namespace fggl::gfx::ogl4 {
class DebugRenderer : public dd::RenderInterface {
public:
explicit DebugRenderer(GLuint shader);
explicit DebugRenderer(std::shared_ptr<ogl::Shader> shader);
~DebugRenderer() override = default;
void drawLineList(const dd::DrawVertex * lines, int count, bool depthEnabled) override;
void drawLineList(const dd::DrawVertex *lines, int count, bool depthEnabled) override;
math::mat4 mvpMatrix;
private:
ogl::Shader m_lineShader;
std::shared_ptr<ogl::Shader> m_lineShader;
ogl::Location m_lineShaderMVP;
ogl::VertexArray m_lineVao;
ogl::ArrayBuffer m_lineVbo;
......
......@@ -19,6 +19,8 @@
#ifndef FGGL_GFX_OGL4_FALLBACK_HPP
#define FGGL_GFX_OGL4_FALLBACK_HPP
#include "fggl/assets/types.hpp"
/**
* Fallback shaders.
*
......@@ -26,9 +28,9 @@
*/
namespace fggl::gfx::ogl4 {
constexpr const char* FALLBACK_CANVAS_PIPELINE = "fallback_canvas";
constexpr const char *FALLBACK_CANVAS_PIPELINE = "fallback_canvas";
constexpr const char* FALLBACK_CANVAS_VERTEX_SHADER = R"glsl(
constexpr const char *FALLBACK_CANVAS_VERTEX_SHADER = R"glsl(
#version 330 core
layout (location = 0) in vec2 aPos;
layout (location = 1) in vec3 aColour;
......@@ -45,7 +47,7 @@ namespace fggl::gfx::ogl4 {
texPos = aTexPos;
})glsl";
constexpr const char* FALLBACK_CANVAS_FRAGMENT_SHADER = R"glsl(
constexpr const char *FALLBACK_CANVAS_FRAGMENT_SHADER = R"glsl(
#version 330 core
uniform sampler2D tex;
......@@ -58,6 +60,14 @@ namespace fggl::gfx::ogl4 {
fragColour = vec4(colour.xyz, texture(tex, texPos).r);
})glsl";
constexpr const GLuint TEX_CHECKER = 0x11FF11FF; //FIXME pixel order is reversed?!
constexpr const GLuint TEX_WHITE = 0xFF0000FF;
constexpr const assets::AssetID FALLBACK_TEX = assets::make_asset_id("fallback", "FALLBACK_TEX");
constexpr const assets::AssetID FALLBACK_MAT = assets::make_asset_id("fallback", "FALLBACK_MAT");
constexpr const assets::AssetID SOLID_TEX = assets::make_asset_id("fallback", "SOLID_TEX");
constexpr const math::vec3 FALLBACK_COLOUR {1.0F, 0.0F, 1.0F};
} // namespace fggl::gfx::ogl4
#endif //FGGL_GFX_OGL4_FALLBACK_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 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
......@@ -25,11 +25,32 @@
#include "fggl/gfx/ogl/shader.hpp"
#include "fggl/gfx/ogl/backend.hpp"
#include "fggl/gfx/ogl/types.hpp"
#include "fggl/ecs3/ecs.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";
......@@ -42,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(ecs3::World& 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(ecs3::World& 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 ecs3::World& 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,14 @@
#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"
#include "fggl/gfx/setup.hpp"
......@@ -30,27 +37,20 @@
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, 2> depends = {
constexpr static const std::array<modules::ServiceName, 5> depends = {
data::Storage::service,
gui::FontLibrary::service
assets::AssetManager::service,
assets::CheckinAdapted::service,
gui::FontLibrary::service,
entity::EntityFactory::service
};
static const modules::ServiceFactory factory;
};
bool ogl4_factory(modules::ModuleService service, modules::Services& services) {
if (service == WindowGraphics::service) {
auto storage = services.get<data::Storage>();
auto font_library = services.get<gui::FontLibrary>();
services.bind<WindowGraphics, ogl4::WindowGraphics>(storage, font_library);
return true;
}
return false;
}
const modules::ServiceFactory OpenGL4::factory = ogl4_factory;
static bool factory(modules::ServiceName name, modules::Services &serviceManager);
};
} //namespace fggl::gfx
......
......@@ -24,33 +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;
};
fggl::gfx::Graphics *WindowGraphics::create(display::Window &window) {
return new OpenGL4Backend(m_storage, m_fonts);
}
} // namespace fggl::gfx::ogl4
#endif //FGGL_GFX_OGL4_SETUP_HPP