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 776 additions and 326 deletions
......@@ -37,21 +37,54 @@ namespace fggl::entity {
factory->bind(math::Transform::guid, make_transform);
}
bool ECS::factory(modules::ModuleService service, modules::Services &services) {
static auto is_scene(std::filesystem::path path) -> assets::AssetTypeID {
if ( path.extension() == ".yml" ) {
return ENTITY_SCENE;
}
return assets::INVALID_ASSET_TYPE;
}
auto get_scene_deps(const std::string& /*packName*/, std::filesystem::path /*packRoot*/, assets::ResourceRecord& rr) -> bool {
auto nodes = YAML::LoadAllFromFile( rr.m_path );
for ( auto& node : nodes ) {
auto scripts = node["scripts"];
if ( !scripts ) {
continue;
}
for (auto script : scripts) {
auto scriptName = script.as<std::string>();
auto scriptRef = assets::asset_from_user(scriptName, rr.m_pack);
rr.m_requires.push_back(scriptRef);
}
}
return true;
}
auto ECS::factory(modules::ServiceName service, modules::Services &services) -> bool {
if (service == EntityFactory::service) {
auto *factory = services.create<EntityFactory>(services);
install_component_factories(factory);
// we are responsible for prefabs...
auto *assetLoader = services.get<assets::Loader>();
assetLoader->setFactory(PROTOTYPE_ASSET, [factory](assets::Loader* loader, const assets::AssetGUID &a, assets::AssetData b, void* ptr) {
assetLoader->setFactory(ENTITY_PROTOTYPE, [factory](assets::Loader* loader, const assets::AssetID& a, assets::LoaderContext b, void* ptr) {
EntityFactory* facPtr = factory;
if ( ptr != nullptr ) {
facPtr = (EntityFactory*)ptr;
}
return load_prototype(loader, a, b, facPtr);
}, assets::LoadType::PATH);
assetLoader->setFactory(SCENE, load_scene, assets::LoadType::PATH);
assetLoader->setFactory(ENTITY_SCENE, load_scene, assets::LoadType::PATH);
// allow auto-detection
auto *checkin = services.get<assets::CheckinAdapted>();
checkin->setLoader(MIME_SCENE, assets::NEEDS_CHECKIN, is_scene);
checkin->setProcessor(MIME_SCENE, get_scene_deps);
return true;
}
......
......@@ -14,10 +14,8 @@
#include <fggl/gfx/atlas.hpp>
#include <fggl/math/types.hpp>
#include <array>
#include <vector>
#include <string>
#define STBRP_STATIC
#define STB_RECT_PACK_IMPLEMENTATION
......@@ -44,8 +42,8 @@ static void unpack_stbrp_query(Query &query, std::vector<stbrp_rect> &data) {
}
}
bool pack_iter(int width, int height, std::vector<stbrp_rect> &query) {
stbrp_node *tmp = new stbrp_node[width];
auto pack_iter(int width, int height, std::vector<stbrp_rect> &query) -> bool {
auto *tmp = new stbrp_node[width];
// setup context
stbrp_context context;
......@@ -58,7 +56,7 @@ bool pack_iter(int width, int height, std::vector<stbrp_rect> &query) {
namespace fggl::gfx {
bool pack(std::vector<Bounds2D> &pack) {
auto pack(std::vector<Bounds2D> &pack) -> bool {
// setup query structure
std::vector<stbrp_rect> query;
query.reserve(pack.size());
......
......@@ -13,10 +13,6 @@
*/
#include <fggl/gfx/input.hpp>
#include <cassert>
#include <bitset>
#include <iostream>
using fggl::gfx::Input;
......@@ -57,15 +53,15 @@ void Input::mousePos(double x, double y) {
m_mouse_curr.cursor[1] = y;
}
double Input::cursorDeltaX() const {
auto Input::cursorDeltaX() const -> double {
return m_mouse_last.cursor[0] - m_mouse_curr.cursor[0];
}
double Input::cursorDeltaY() const {
auto Input::cursorDeltaY() const -> double {
return m_mouse_last.cursor[1] - m_mouse_curr.cursor[1];
}
const double *Input::mousePos() const {
auto Input::mousePos() const -> const double * {
return m_mouse_curr.scroll.data();
}
......@@ -74,15 +70,15 @@ void Input::mouseScroll(double deltaX, double deltaY) {
m_mouse_curr.scroll[1] = deltaY;
}
const double *Input::mouseScroll() const {
auto Input::mouseScroll() const -> const double * {
return m_mouse_curr.scroll.data();
}
double Input::scrollDeltaX() const {
auto Input::scrollDeltaX() const -> double {
return m_mouse_curr.scroll[0];
}
double Input::scrollDeltaY() const {
auto Input::scrollDeltaY() const -> double {
return m_mouse_curr.scroll[1];
}
......@@ -94,15 +90,15 @@ void Input::mouseBtn(const MouseButton btn, bool state) {
}
}
bool Input::mouseDown(const MouseButton btn) const {
auto Input::mouseDown(const MouseButton btn) const -> bool {
return m_mouse_curr.buttons & btn;
}
bool Input::mousePressed(const MouseButton btn) const {
auto Input::mousePressed(const MouseButton btn) const -> bool {
return (m_mouse_curr.buttons & btn) && !(m_mouse_last.buttons & btn);
}
bool Input::mouseReleased(const MouseButton btn) const {
auto Input::mouseReleased(const MouseButton btn) const -> bool {
return !(m_mouse_curr.buttons & btn) && (m_mouse_last.buttons & btn);
}
......@@ -118,11 +114,11 @@ void Input::joystickDisconnect(int id) {
m_joydata[id] = Joystick();
}
bool Input::hasJoystick(int id) const {
auto Input::hasJoystick(int id) const -> bool {
return m_joysticks[id];
}
const fggl::gfx::Joystick &Input::joystick(int id) const {
auto Input::joystick(int id) const -> const fggl::gfx::Joystick & {
return m_joydata[id];
}
......@@ -130,22 +126,22 @@ void Input::padState(int id, const PadState &state) {
m_pad_curr[id] = state;
}
bool Input::padDown(int id, PadButton btn) {
auto Input::padDown(int id, PadButton btn) -> bool {
return m_pad_curr[id].buttons[(int) btn];
}
bool Input::padPressed(int id, PadButton btn) {
auto Input::padPressed(int id, PadButton btn) -> bool {
return m_pad_curr[id].buttons[(int) btn] && !m_pad_last[id].buttons[(int) btn];
}
bool Input::padReleased(int id, PadButton btn) {
auto Input::padReleased(int id, PadButton btn) -> bool {
return !m_pad_curr[id].buttons[(int) btn] && m_pad_last[id].buttons[(int) btn];
}
float Input::padAxis(int id, PadAxis axis) {
auto Input::padAxis(int id, PadAxis axis) -> float {
return m_pad_curr[id].axes[(int) axis];
}
float Input::padAxisDelta(int id, PadAxis axis) {
auto Input::padAxisDelta(int id, PadAxis axis) -> float {
return m_pad_last[id].axes[(int) axis] - m_pad_curr[id].axes[(int) axis];
}
......@@ -27,7 +27,6 @@
#include <glm/ext/matrix_clip_space.hpp>
#include <glm/ext/matrix_transform.hpp>
#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <memory>
......@@ -64,24 +63,15 @@ constexpr auto fggl_ogl_source(GLenum source) -> const char * {
constexpr auto static fggl_ogl_type(GLenum type) -> const char * {
switch (type) {
case GL_DEBUG_TYPE_ERROR: return "Error";
break;
case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: return "Deprecated Behaviour";
break;
case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: return "Undefined Behaviour";
break;
case GL_DEBUG_TYPE_PERFORMANCE: return "Performance";
break;
case GL_DEBUG_TYPE_PORTABILITY: return "Portability";
break;
case GL_DEBUG_TYPE_MARKER: return "Marker";
break;
case GL_DEBUG_TYPE_PUSH_GROUP: return "Push Group";
break;
case GL_DEBUG_TYPE_POP_GROUP: return "Pop Group";
break;
default:
case GL_DEBUG_TYPE_OTHER: return "Other";
break;
}
assert(false);
return "unknown";
......@@ -92,7 +82,7 @@ PRAGMA_DIAGNOSTIC_PUSH
constexpr const char *OGL_LOG_FMT{"[GL] {}, {}: [{}]: {}"};
void static GLAPIENTRY fggl_ogl_to_spdlog(GLenum source,
void static GLAPIENTRY fggl_ogl_log(GLenum source,
GLenum type,
unsigned int msgId,
GLenum severity,
......@@ -139,13 +129,73 @@ namespace fggl::gfx {
using data::Mesh2D;
using data::Vertex2D;
OpenGL4Backend::OpenGL4Backend(data::Storage *storage, gui::FontLibrary *fonts, assets::AssetManager *assets)
static void setup_shaders(ShaderCache* cache) {
// FIXME this should not be hard-coded, it should be part of the scene loading process
cache->load(ShaderConfig::named("phong"));
cache->load(ShaderConfig::named("redbook/lighting"));
cache->load(ShaderConfig::named("redbook/debug"));
// debug shaders
cache->load(ShaderConfig::named("normals", true));
cache->load(ShaderConfig::named("debug"));
}
static void splat_checkerboard(GLuint* memory, unsigned int width = 128, unsigned int height = 128) {
int counter = 0;
auto colour = ogl4::TEX_CHECKER;
for ( auto i = 0u; i < width * height; ++i) {
memory[i] = ogl4::TEX_CHECKER;
counter++;
if (counter == 5) {
counter = 0;
colour = colour == ogl4::TEX_CHECKER ? ogl4::TEX_WHITE : ogl4::TEX_CHECKER;
}
}
}
static auto make_solid(uint8_t width, uint8_t height, GLuint colour) -> ogl::Image {
ogl::PixelDataArray data(ogl::PixelFormat::UNSIGNED_INT, width * height);
for ( auto i = 0u; i < width * height; ++i) {
data.ui[i] = colour;
}
return ogl::Image(
ogl::ImageFormat::RGBA,
{width, height},
std::move(data)
);
}
static void setup_fallback_texture(assets::AssetManager* assets) {
{
// generate the image
ogl::Image image(ogl::ImageFormat::RGBA, ogl::PixelFormat::UNSIGNED_INT, {128,128});
splat_checkerboard( image.data.ui );
// setup the texture
auto *fallback2D = new ogl::Texture(ogl::TextureType::Tex2D);
fallback2D->setData(ogl::InternalImageFormat::RedGreenBlueAlpha, image, ogl::PixelFormat::UNSIGNED_BYTE);
assets->set(ogl4::FALLBACK_TEX, fallback2D);
}
{
ogl::Image image = make_solid(128, 128, ogl4::TEX_WHITE);
auto *solid2D = new ogl::Texture(ogl::TextureType::Tex2D);
solid2D->setData(ogl::InternalImageFormat::RedGreenBlueAlpha, image);
assets->set(ogl4::SOLID_TEX, solid2D);
}
}
OpenGL4Backend::OpenGL4Backend(data::Storage *storage, gui::FontLibrary *fonts, assets::AssetManager *assets, GlFunctionLoader loader)
: fggl::gfx::Graphics(), m_canvasPipeline(nullptr), m_storage(storage) {
// initialise GLEW, or fail
// FIXME this binds the graphics stack to GLFW :'(
int version = gladLoadGLLoader((GLADloadproc) glfwGetProcAddress);
// load OpenGL context, or fail.
int version = gladLoadGLLoader(loader);
if (version == 0) {
printf("Failed to initialize OpenGL context\n");
debug::error("Failed to initialize OpenGL context\n");
return;
}
// OpenGL debug Support
......@@ -155,7 +205,7 @@ namespace fggl::gfx {
debug::info("enabling OpenGL debug output");
glEnable(GL_DEBUG_OUTPUT);
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
glDebugMessageCallback(fggl_ogl_to_spdlog, nullptr);
glDebugMessageCallback(fggl_ogl_log, nullptr);
glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, nullptr, GL_TRUE);
}
......@@ -165,6 +215,7 @@ namespace fggl::gfx {
// setup the shader cache
m_cache = std::make_unique<ShaderCache>(m_storage);
setup_shaders(m_cache.get());
// setup 2D rendering system
ShaderConfig shader2DConfig = ShaderConfig::named("canvas");
......@@ -174,10 +225,8 @@ namespace fggl::gfx {
m_canvasPipeline = m_cache->get(ogl4::FALLBACK_CANVAS_PIPELINE);
}
// FIXME this should not be hard-coded, it should be part of the scene loading process
m_cache->load(ShaderConfig::named("phong"));
m_cache->load(ShaderConfig::named("redbook/lighting"));
m_cache->load(ShaderConfig::named("redbook/debug"));
// fallback textures
setup_fallback_texture(assets);
// rendering helpers
m_canvasRenderer = std::make_unique<ogl4::CanvasRenderer>(fonts);
......@@ -202,9 +251,9 @@ namespace fggl::gfx {
m_canvasRenderer->render(*m_canvasPipeline, paint);
}
void OpenGL4Backend::drawScene(entity::EntityManager &world) {
void OpenGL4Backend::drawScene(entity::EntityManager &world, bool debugMode) {
if (m_modelRenderer) {
m_modelRenderer->render(world);
m_modelRenderer->render(world, debugMode);
}
if (m_debugRenderer) {
......
......@@ -17,12 +17,13 @@
#include "fggl/gfx/ogl4/fallback.hpp"
#include <iostream>
#include <fstream>
#include <vector>
#include <spdlog/spdlog.h>
namespace fggl::gfx {
bool ShaderCache::compileShaderFromSource(const std::string &source, GLuint sid) {
auto ShaderCache::compileShaderFromSource(const std::string &source, GLuint sid) -> bool {
// upload and compile shader
const char *src = source.c_str();
glShaderSource(sid, 1, &src, nullptr);
......@@ -46,7 +47,7 @@ namespace fggl::gfx {
return true;
}
bool ShaderCache::readAndCompileShader(const std::string &filename, GLuint shader) {
auto ShaderCache::readAndCompileShader(const std::string &filename, GLuint shader) -> bool {
std::string source;
bool result = m_storage->load(fggl::data::Data, filename, &source);
if (!result) {
......@@ -88,7 +89,7 @@ namespace fggl::gfx {
}
bool ShaderCache::loadFromDisk(GLuint pid, const std::string &pipelineName) {
auto ShaderCache::loadFromDisk(GLuint pid, const std::string &pipelineName) -> bool {
BinaryCache cache;
auto fname = "shader_" + pipelineName + ".bin";
......@@ -112,7 +113,7 @@ namespace fggl::gfx {
m_storage->save(fggl::data::Cache, fname, &cache);
}
ShaderCache::ShaderPtr ShaderCache::getOrLoad(const ShaderConfig &config) {
auto ShaderCache::getOrLoad(const ShaderConfig &config) -> ShaderCache::ShaderPtr {
try {
return m_shaders.at(config.name);
} catch (std::out_of_range &e) {
......@@ -120,11 +121,15 @@ namespace fggl::gfx {
}
}
ShaderCache::ShaderPtr ShaderCache::get(const std::string &name) {
return m_shaders.at(name);
auto ShaderCache::get(const std::string &name) -> ShaderCache::ShaderPtr {
auto itr = m_shaders.find(name);
if ( itr != m_shaders.end() ){
return itr->second;
}
return nullptr;
}
ShaderCache::ShaderPtr ShaderCache::load(const ShaderConfig &config) {
auto ShaderCache::load(const ShaderConfig &config) -> ShaderCache::ShaderPtr {
spdlog::debug("starting shader program generation for {}", config.name);
GLuint pid = glCreateProgram();
......@@ -195,7 +200,7 @@ namespace fggl::gfx {
return shaderPtr;
}
ShaderCache::ShaderPtr ShaderCache::load(const ShaderSources &sources, bool allowBinaryCache) {
auto ShaderCache::load(const ShaderSources &sources, bool allowBinaryCache) -> ShaderCache::ShaderPtr {
spdlog::debug("starting shader program generation for {}", sources.name);
......@@ -276,7 +281,7 @@ namespace fggl::gfx {
glGetProgramBinary(pid, length, &cache->size, &cache->format, cache->data);
}
bool ShaderCache::cacheLoad(GLuint pid, const BinaryCache *cache) {
auto ShaderCache::cacheLoad(GLuint pid, const BinaryCache *cache) -> bool {
if (!m_binary) {
return false;
}
......@@ -301,8 +306,8 @@ namespace fggl::gfx {
}
template<>
bool fggl::data::fggl_deserialize(std::filesystem::path &data, fggl::gfx::BinaryCache *out) {
auto f =
auto fggl::data::fggl_deserialize(std::filesystem::path &data, fggl::gfx::BinaryCache *out) -> bool {
auto* f =
#ifdef _MSC_VER
_wfopen(data.c_str(), L"r");
#else
......@@ -343,11 +348,9 @@ namespace fggl::gfx {
return result;
}
#include <iostream>
#include <fstream>
template<>
bool fggl::data::fggl_deserialize(std::filesystem::path &data, std::string *out) {
auto fggl::data::fggl_deserialize(std::filesystem::path &data, std::string *out) -> bool {
std::ifstream ifs(data);
out->assign((std::istreambuf_iterator<char>(ifs)),
(std::istreambuf_iterator<char>()));
......@@ -355,10 +358,10 @@ bool fggl::data::fggl_deserialize(std::filesystem::path &data, std::string *out)
}
template<>
bool fggl::data::fggl_serialize(std::filesystem::path &data, const fggl::gfx::BinaryCache *out) {
auto fggl::data::fggl_serialize(std::filesystem::path &data, const fggl::gfx::BinaryCache *out) -> bool {
// try and write
auto f =
auto *f =
#ifdef _MSC_VER
_wfopen( data.c_str(), L"w");
#else
......
......@@ -44,7 +44,7 @@ namespace fggl::gfx::ogl {
other.m_obj = 0;
}
VertexArray &VertexArray::operator=(VertexArray &&other) {
auto VertexArray::operator=(VertexArray &&other) -> VertexArray & {
if (this != &other) {
release();
std::swap(m_obj, other.m_obj);
......@@ -115,7 +115,7 @@ namespace fggl::gfx::ogl {
#endif
}
void VertexArray::drawElements(const ElementBuffer &buff, Primative drawType, std::size_t size) {
void VertexArray::drawElements(const ElementBuffer &buff, Primitive drawType, std::size_t size) {
bind();
#ifndef FGGL_I_BOUND
......@@ -130,7 +130,7 @@ namespace fggl::gfx::ogl {
#endif
}
void VertexArray::draw(Primative drawType, int first, std::size_t count) {
void VertexArray::draw(Primitive drawType, int first, std::size_t count) {
glDrawArrays((GLenum) drawType, first, count);
}
......
......@@ -6,5 +6,6 @@ target_sources(fggl
canvas.cpp
models.cpp
debug.cpp
meshes.cpp
module.cpp
)
......@@ -137,7 +137,7 @@ namespace fggl::gfx::ogl4 {
shader.setUniformMtx(shader.uniform("projection"), projMat);
// draw elements
m_vao.drawElements(m_indexList, ogl::Primative::TRIANGLE, mesh.indexList.size());
m_vao.drawElements(m_indexList, ogl::Primitive::TRIANGLE, mesh.indexList.size());
// cleanup
glUseProgram(0);
......@@ -153,21 +153,21 @@ namespace fggl::gfx::ogl4 {
// get the expected font
std::shared_ptr<gui::FontFace> face = m_fonts->getFont("LiberationSans-Regular.ttf");
std::shared_ptr<gui::FontFace> face = m_fonts->getDefaultFont();
if (face == nullptr) {
// we don't know about that font...
return;
}
// setup the shader
// set up the shader
shader.use();
auto projMat = glm::ortho(0.0f, 1920.0f, 1080.0f, 0.f);
auto projMat = glm::ortho(0.0F, 1920.0F, 1080.0F, 0.F);
shader.setUniformMtx(shader.uniform("projection"), projMat);
// bind the vbo we'll use for writing
m_vao.bind();
// setup the openGL state we expect for rendering
// set up the openGL state we expect for rendering
glDisable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
......@@ -179,14 +179,15 @@ namespace fggl::gfx::ogl4 {
const auto label = textCmd.text;
math::vec2 penPos(textCmd.pos);
// set up a non-owning holder for the characters
data::Texture2D tex;
tex.channels = 1;
for (auto letter : label) {
ogl::Image img{};
img.format = ogl::ImageFormat::R;
img.type = ogl::PixelFormat::UNSIGNED_BYTE;
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
face->texture(letter, img.size.x, img.size.y, &img.data);
m_fontTex.setData(ogl::InternalImageFormat::Red, img);
face->texture(letter, tex.size.x, tex.size.y, &tex.data);
m_fontTex.setData(ogl::InternalImageFormat::Red, &tex);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
......@@ -214,10 +215,13 @@ namespace fggl::gfx::ogl4 {
m_vertexList.replace(verts.size(), verts.data());
m_vao.bind();
m_vao.draw(ogl::Primative::TRIANGLE, 0, verts.size());
m_vao.draw(ogl::Primitive::TRIANGLE, 0, verts.size());
penPos.x += (metrics.advance >> 6);
}
// textures assume they own their contained data, we need to make sure we clear it
tex.data = nullptr;
}
glDisable(GL_BLEND);
......
......@@ -55,7 +55,7 @@ namespace fggl::gfx::ogl4 {
m_lineVbo.bind();
m_lineVbo.replace<dd::DrawVertex>(count, lines);
m_lineVao.draw(ogl::Primative::LINE, 0, count);
m_lineVao.draw(ogl::Primitive::LINE, 0, count);
glUseProgram(0);
glBindVertexArray(0);
......
/*
* This file is part of FGGL.
*
* FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with FGGL.
* If not, see <https://www.gnu.org/licenses/>.
*/
#include "fggl/gfx/ogl4/meshes.hpp"
#include "fggl/gfx/phong.hpp"
#include "fggl/data/texture.hpp"
//
// Created by webpigeon on 22/10/22.
//
namespace fggl::gfx::ogl4 {
static auto setup_array_buffer(std::shared_ptr<ogl::VertexArray> &vao,
const std::vector<mesh::Vertex3D> &data) -> std::shared_ptr<ogl::ArrayBuffer> {
// upload the data to the GPU
auto buff = std::make_shared<ogl::ArrayBuffer>();
buff->write(data.size() * sizeof(mesh::Vertex3D), data.data(), ogl::BufUsage::STATIC_DRAW);
// set up the vertex attributes
auto posAttr = ogl::attribute<mesh::Vertex3D, math::vec3>(offsetof(mesh::Vertex3D, position));
auto normalAttr = ogl::attribute<mesh::Vertex3D, math::vec3>(offsetof(mesh::Vertex3D, normal));
auto colAttr = ogl::attribute<mesh::Vertex3D, math::vec3>(offsetof(mesh::Vertex3D, colour));
auto texAttr = ogl::attribute<mesh::Vertex3D, math::vec2>(offsetof(mesh::Vertex3D, texPos));
vao->setAttribute(*buff, 0, posAttr);
vao->setAttribute(*buff, 1, normalAttr);
vao->setAttribute(*buff, 2, colAttr);
vao->setAttribute(*buff, 3, texAttr);
return buff;
}
static auto setup_index_buffer(const std::vector<uint32_t>& data) -> std::shared_ptr<ogl::ElementBuffer> {
auto elementBuffer = std::make_shared<ogl::ElementBuffer>();
elementBuffer->write(data.size() * sizeof(uint32_t),
data.data(), ogl::BufUsage::STATIC_DRAW);
return elementBuffer;
}
void setup_material(const std::shared_ptr<ogl::Shader>& shader, const PhongMaterial* material) {
if ( !shader->hasUniform("material.ambient") ) {
return;
}
// setup material block
shader->setUniformF(shader->uniform("material.emission"), material->emission);
shader->setUniformF(shader->uniform("material.ambient"), material->ambient);
shader->setUniformF(shader->uniform("material.diffuse"), material->diffuse);
shader->setUniformF(shader->uniform("material.specular"), material->specular);
//shader->setUniformF(shader->uniform("material.shininess"), material->shininess);
}
void setup_material(const std::shared_ptr<ogl::Shader>& shader, const Material* material) {
if ( shader->hasUniform("material.diffuse") ) {
// setup material block
// shader->setUniformF(shader->uniform("material.emission"), material->emission);
// shader->setUniformF(shader->uniform("material.ambient"), material->ambient);
shader->setUniformF(shader->uniform("material.diffuse"), material->m_diffCol);
shader->setUniformF(shader->uniform("material.specular"), material->m_specCol);
}
// setup diffuse texture
if (material->m_diffuse != nullptr) {
material->m_diffuse->bind(0);
if (shader->hasUniform("diffuseTexture")) {
shader->setUniformI(shader->uniform("diffuseTexture"), 0);
}
}
// setup specular texture
if (material->m_specular != nullptr) {
material->m_specular->bind(1);
if (shader->hasUniform("specularTexture")) {
shader->setUniformI(shader->uniform("specularTexture"), 1);
}
}
//shader->setUniformF(shader->uniform("material.shininess"), material->shininess);
}
void MeshData::draw(std::shared_ptr<ogl::Shader> shader) const {
vao->bind();
vertexData->bind();
if ( material != nullptr ) {
setup_material(shader, material);
} else {
debug::info("no material is active, cannot bind textures!");
}
if ( drawInfo.restartIndex != ogl::NO_RESTART_IDX) {
glEnable(GL_PRIMITIVE_RESTART);
glPrimitiveRestartIndex( drawInfo.restartIndex );
}
vao->drawElements(*elements, drawInfo.mode, elementCount);
if ( drawInfo.restartIndex != ogl::NO_RESTART_IDX ) {
glDisable(GL_PRIMITIVE_RESTART);
}
}
void StaticMultiMesh::draw() const {
for ( const auto& mesh : meshes ) {
mesh.draw(pipeline);
}
}
void setup_lighting(const std::shared_ptr<ogl::Shader>& shader, const DirectionalLight* light) {
assert( light != nullptr );
if ( !shader->hasUniform("light.direction") ) {
fggl::debug::warning("asked for directional lighting, but shader does not support!");
return;
}
shader->setUniformF( shader->uniform("light.direction"), light->position);
shader->setUniformF( shader->uniform("light.ambient"), light->ambient);
shader->setUniformF( shader->uniform("light.diffuse"), light->diffuse);
shader->setUniformF( shader->uniform("light.specular"), light->specular);
}
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) {
if (shader->hasUniform("lightPos")) {
shader->setUniformF(shader->uniform("lightPos"), lightPos);
}
if ( !shader->hasUniform("lights[0].isEnabled") ) {
return;
}
bool local = true;
shader->setUniformI(shader->uniform("lights[0].isEnabled"), 1);
shader->setUniformI(shader->uniform("lights[0].isLocal"), local);
shader->setUniformI(shader->uniform("lights[0].isSpot"), 0);
shader->setUniformF(shader->uniform("lights[0].constantAttenuation"), 5.0F);
shader->setUniformF(shader->uniform("lights[0].linearAttenuation"), 0.0F);
shader->setUniformF(shader->uniform("lights[0].quadraticAttenuation"), 0.0F);
shader->setUniformF(shader->uniform("Strength"), 0.6F);
if (!local) {
lightPos = glm::normalize(lightPos);
auto viewDir = glm::normalize(camTransform.origin() - transform.origin());
auto halfVector = glm::normalize(lightPos + viewDir);
shader->setUniformF(shader->uniform("lights[0].halfVector"), halfVector);
shader->setUniformF(shader->uniform("EyeDirection"), viewDir);
shader->setUniformF(shader->uniform("lights[0].position"), lightPos);
} else {
auto camModelView = (viewMatrix * camTransform.model() * math::vec4(0.0F, 0.0F, 0.0F, 1.0F));
auto modelModelView = (viewMatrix * transform.model() * math::vec4(0.0F, 0.0F, 0.0F, 1.0F));
math::vec3 viewDir = glm::normalize(camModelView - modelModelView);
shader->setUniformF(shader->uniform("EyeDirection"), viewDir);
shader->setUniformF(shader->uniform("lights[0].position"),
math::vec3(viewMatrix * math::vec4(lightPos, 1.0F)));
}
shader->setUniformF(shader->uniform("lights[0].ambient"), {0.0F, 0.5F, 0.0F});
shader->setUniformF(shader->uniform("lights[0].colour"), {0.5F, 0.5F, 0.5F});
}
static auto upload_texture( assets::AssetID name, assets::AssetManager* manager ) -> ogl::Texture* {
debug::info("loading texture: {}", name.get());
auto uploadedTex = assets::make_asset_id_rt("ogl", std::to_string(name.get()) );
auto* texture = manager->get<ogl::Texture>( uploadedTex );
if ( texture != nullptr ) {
return texture;
}
// get the texture data we plan to load
auto *textureData = manager->get<data::Texture2D>(name);
// create a texture
texture = new ogl::Texture(ogl::TextureType::Tex2D);
texture->setData( ogl::InternalImageFormat::RedGreenBlue, textureData);
return manager->set(uploadedTex, texture);
}
static auto get_fallback_material(assets::AssetManager* manager) -> Material* {
auto* fallback = manager->get<Material>(FALLBACK_MAT);
if ( fallback != nullptr ) {
return fallback;
}
auto* mat = new Material();
mat->m_diffCol = FALLBACK_COLOUR;
mat->m_specCol= FALLBACK_COLOUR;
mat->m_diffuse = manager->get<ogl::Texture>(FALLBACK_TEX);
mat->m_specular = manager->get<ogl::Texture>(FALLBACK_TEX);
mat->m_normals = manager->get<ogl::Texture>(SOLID_TEX);
return manager->set(FALLBACK_MAT, mat);
}
static auto upload_material( assets::AssetID name, assets::AssetManager* manager ) -> Material* {
auto* meshMaterial = manager->get<mesh::Material>(name);
if ( meshMaterial == nullptr ){
debug::error("attempted to load material {}, but did not exist!", name.get());
return get_fallback_material(manager);
}
auto uploadedMat = assets::make_asset_id_rt("ogl", std::to_string(name.get()) );
auto* material = manager->get<Material>( uploadedMat );
if ( material != nullptr ) {
return material;
}
material = new Material();
material->m_diffCol = meshMaterial->diffuse;
material->m_specCol = meshMaterial->specular;
// do we have a diffuse texture?
if ( !meshMaterial->diffuseTextures.empty() ) {
material->m_diffuse = upload_texture(meshMaterial->getPrimaryDiffuse(), manager);
} else {
material->m_diffuse = manager->get<ogl::Texture>( ogl4::SOLID_TEX );
}
// do we have a normal Texture?
if ( !meshMaterial->normalTextures.empty() ) {
material->m_normals = upload_texture(meshMaterial->getPrimaryNormals(), manager);
} else {
material->m_normals = manager->get<ogl::Texture>( ogl4::SOLID_TEX );
}
// do we have a specular texture?
if ( !meshMaterial->specularTextures.empty() ) {
material->m_specular = upload_texture(meshMaterial->getPrimarySpecular(), manager);
} else {
material->m_specular = manager->get<ogl::Texture>( ogl4::SOLID_TEX );
}
return manager->set( uploadedMat, material);
}
auto upload_mesh(const mesh::Mesh3D& rawMesh, assets::AssetManager* manager) -> MeshData {
auto vao = std::make_shared<ogl::VertexArray>();
return {
.vao = vao,
.elements = setup_index_buffer( rawMesh.indices ),
.vertexData = setup_array_buffer(vao, rawMesh.data ),
.elementCount = rawMesh.indices.size(),
.drawInfo = { .mode = ogl::Primitive::TRIANGLE },
.material = upload_material( rawMesh.material, manager )
};
}
auto upload_multi_mesh(const mesh::MultiMesh3D& rawMesh, assets::AssetManager* manager) -> std::vector<MeshData> {
std::vector<MeshData> gpuMeshes;
for (const auto& mesh : rawMesh.meshes) {
gpuMeshes.push_back( upload_mesh(mesh, manager) );
}
return gpuMeshes;
}
}
......@@ -17,17 +17,19 @@
//
#include "fggl/gfx/ogl4/models.hpp"
#include "fggl/gfx/ogl4/meshes.hpp"
#include "fggl/data/heightmap.hpp"
#include "fggl/data/texture.hpp"
#include "fggl/debug/logging.hpp"
#include "fggl/gfx/camera.hpp"
#include "fggl/gfx/phong.hpp"
#include <spdlog/spdlog.h>
namespace fggl::gfx::ogl4 {
static std::shared_ptr<ogl::ArrayBuffer> setupArrayBuffer(std::shared_ptr<ogl::VertexArray> &vao,
const std::vector<data::Vertex> &data) {
static auto setup_array_buffer(std::shared_ptr<ogl::VertexArray> &vao,
const std::vector<data::Vertex> &data) -> std::shared_ptr<ogl::ArrayBuffer> {
auto buff = std::make_shared<ogl::ArrayBuffer>();
buff->write(data.size() * sizeof(data::Vertex), data.data(), ogl::BufUsage::STATIC_DRAW);
......@@ -42,18 +44,19 @@ namespace fggl::gfx::ogl4 {
return buff;
}
static std::shared_ptr<ogl::ElementBuffer> setupIndexBuffer(std::shared_ptr<ogl::VertexArray> &vao,
const std::vector<uint32_t> &data) {
static auto setup_index_buffer(std::shared_ptr<ogl::VertexArray> &vao,
const std::vector<uint32_t> &data) -> std::shared_ptr<ogl::ElementBuffer> {
auto elementBuffer = std::make_shared<ogl::ElementBuffer>();
elementBuffer->write(data.size() * sizeof(uint32_t),
data.data(), ogl::BufUsage::STATIC_DRAW);
return elementBuffer;
}
static void setupComponent(StaticModel &modelComp, std::shared_ptr<ogl::Shader> &shader, data::Mesh &mesh) {
static void setup_component(StaticModel &modelComp, std::shared_ptr<ogl::Shader> &shader, data::Mesh &mesh) {
auto vao = std::make_shared<ogl::VertexArray>();
auto meshBuffer = setupArrayBuffer(vao, mesh.vertexList());
auto elementBuffer = setupIndexBuffer(vao, mesh.indexList());
auto meshBuffer = setup_array_buffer(vao, mesh.vertexList());
auto elementBuffer = setup_index_buffer(vao, mesh.indexList());
// set up the element attributes
modelComp.vao = vao;
......@@ -61,10 +64,10 @@ namespace fggl::gfx::ogl4 {
modelComp.elements = elementBuffer;
modelComp.pipeline = shader;
modelComp.elementCount = mesh.indexCount();
modelComp.drawType = ogl::Primative::TRIANGLE;
modelComp.drawType = ogl::Primitive::TRIANGLE;
}
StaticModel* StaticModelRenderer::uploadMesh(assets::AssetGUID guid, const data::Mesh &mesh, bool allowCache) {
auto StaticModelRenderer::uploadMesh(assets::AssetID guid, const data::Mesh &mesh, bool allowCache) -> StaticModel* {
assert( m_assets != nullptr );
// if the asset has already been uploaded, we don't need to do anything
......@@ -76,10 +79,10 @@ namespace fggl::gfx::ogl4 {
// the asset does not exist, we need to upload it
auto* modelAsset = new StaticModel();
modelAsset->vao = std::make_shared<ogl::VertexArray>();
modelAsset->vertexData = setupArrayBuffer(modelAsset->vao, mesh.vertexList());
modelAsset->elements = setupIndexBuffer(modelAsset->vao, mesh.indexList());
modelAsset->vertexData = setup_array_buffer(modelAsset->vao, mesh.vertexList());
modelAsset->elements = setup_index_buffer(modelAsset->vao, mesh.indexList());
modelAsset->elementCount = mesh.indexCount();
modelAsset->drawType = ogl::Primative::TRIANGLE;
modelAsset->drawType = ogl::Primitive::TRIANGLE;
// if caching is enabled, then use the cache
if ( allowCache ) {
......@@ -89,7 +92,32 @@ namespace fggl::gfx::ogl4 {
return modelAsset;
}
StaticModelGPU* StaticModelRenderer::uploadMesh2(const assets::AssetGUID& meshName, const data::Mesh &mesh) {
/*MeshData* StaticModelRenderer::uploadMesh(assets::AssetGUID guid, const mesh::Mesh3D &mesh, bool allowCache) {
assert( m_assets != nullptr );
// if the asset has already been uploaded, we don't need to do anything
if ( allowCache && m_assets->has(guid) ) {
m_assets->require(guid);
return m_assets->get<MeshData>(guid);
}
// the asset does not exist, we need to upload it
auto* modelAsset = new MeshData();
modelAsset->vao = std::make_shared<ogl::VertexArray>();
modelAsset->vertexData = setupArrayBuffer(modelAsset->vao, mesh.data);
modelAsset->elements = setupIndexBuffer(modelAsset->vao, mesh.indices);
modelAsset->elementCount = mesh.indices.size();
modelAsset->drawInfo.mode = ogl::Primitive::TRIANGLE;
// if caching is enabled, then use the cache
if ( allowCache ) {
m_assets->set(guid, modelAsset);
}
return modelAsset;
}*/
auto StaticModelRenderer::uploadMesh2(const assets::AssetID& meshName, const data::Mesh &mesh) -> StaticModelGPU* {
assert( m_assets != nullptr );
if ( m_assets->has(meshName) ) {
......@@ -99,43 +127,72 @@ namespace fggl::gfx::ogl4 {
auto* modelAsset = new StaticModelGPU();
modelAsset->vao = std::make_shared<ogl::VertexArray>();
modelAsset->vertices = setupArrayBuffer(modelAsset->vao, mesh.vertexList());
modelAsset->elements = setupIndexBuffer(modelAsset->vao, mesh.indexList());
modelAsset->vertices = setup_array_buffer(modelAsset->vao, mesh.vertexList());
modelAsset->elements = setup_index_buffer(modelAsset->vao, mesh.indexList());
modelAsset->elementCount = mesh.indexCount();
modelAsset->drawType = ogl::Primative::TRIANGLE;
modelAsset->drawType = ogl::Primitive::TRIANGLE;
return m_assets->set(meshName, modelAsset);
}
#ifdef FGGL_ALLOW_DEFERRED_UPLOAD
void StaticModelRenderer::resolveModels(entity::EntityManager &world) {
// FIXME: this needs something reactive or performance will suck.
auto renderableEnts = world.find<data::StaticMesh>();
for (const auto &renderable : renderableEnts) {
auto *currModel = world.tryGet<StaticModel>(renderable);
if (currModel != nullptr) {
static void setup_meshes(entity::EntityManager& world, ShaderCache* shaders, assets::AssetManager* manager) {
auto entsWithModels = world.find<mesh::StaticMesh3D>();
for ( const auto& mesher : entsWithModels ) {
// check if this entity already has a mesh...
const auto* currModel = world.tryGet<StaticMesh>(mesher);
if ( currModel != nullptr ) {
continue;
}
auto &meshComp = world.get<data::StaticMesh>(renderable);
// figure out the requirements
auto& rawMesh = world.get<mesh::StaticMesh3D>(mesher);
if ( rawMesh.guid != util::make_guid("__NO_CACHE__") ){
// TODO support fetching of loaded meshes
debug::warning("multiple entities sharing a mesh has not been implemented yet...");
}
// model loading (should be via asset system)
auto loadedModel = uploadMesh("DEFER_ENT_"+ std::to_string((uint64_t)renderable), meshComp.mesh, false);
auto loadedShader = m_shaders->get(meshComp.pipeline);
auto& entityMesh = world.add<StaticMesh>(mesher);
entityMesh.pipeline = shaders->get( rawMesh.pipeline );
entityMesh.mesh = upload_mesh( rawMesh.mesh, manager );
// splice the loaded asset into the ecs
auto& entityModel = world.add<StaticModel>(renderable);
entityModel = *loadedModel;
entityModel.pipeline = loadedShader;
}
}
static void setup_multi_meshes(entity::EntityManager& world, ShaderCache* shaders, assets::AssetManager* manager) {
auto entsWithModels = world.find<mesh::StaticMultiMesh3D>();
for ( const auto& mesher : entsWithModels ) {
// check if this entity already has a mesh...
const auto* currModel = world.tryGet<StaticMultiMesh>(mesher);
if ( currModel != nullptr ) {
continue;
}
// figure out the requirements
auto& multiMesh = world.get<mesh::StaticMultiMesh3D>(mesher);
if ( multiMesh.guid != util::make_guid("__NO_CACHE__") ){
// TODO support fetching of loaded meshes
debug::warning("multiple entities sharing a mesh has not been implemented yet...");
}
// let the user know we just did this...
debug::log(debug::Level::info, "Added static mesh to {}, pipeline was: {}", (uint64_t) renderable, meshComp.pipeline);
auto& entityMesh = world.add<StaticMultiMesh>(mesher);
entityMesh.pipeline = shaders->get( multiMesh.pipeline );
entityMesh.meshes = upload_multi_mesh(multiMesh.mesh, manager);
//entityMesh.material = setup_material( multiMesh.mesh.materials[0], manager);
}
}
void StaticModelRenderer::resolveModels(entity::EntityManager &world) {
// new mesh formats
setup_meshes(world, m_shaders, m_assets);
setup_multi_meshes(world, m_shaders, m_assets);
// terrain
auto terrain = world.find<data::HeightMap>();
for (auto &renderable : terrain) {
auto currModel = world.tryGet<StaticModel>(renderable);
for (const auto &renderable : terrain) {
auto *currModel = world.tryGet<StaticModel>(renderable);
if (currModel != nullptr) {
continue;
}
......@@ -145,11 +202,11 @@ namespace fggl::gfx::ogl4 {
data::generateHeightMesh(heightmap, heightMapMesh);
auto &modelComp = world.add<StaticModel>(renderable);
setupComponent(modelComp, m_phong, heightMapMesh);
setup_component(modelComp, m_phong, heightMapMesh);
// we know this is a triangle strip with a restart vertex...
// FIXME the model should be telling us this...
modelComp.drawType = ogl::Primative::TRIANGLE_STRIP;
modelComp.drawType = ogl::Primitive::TRIANGLE_STRIP;
modelComp.restartIndex = heightMapMesh.restartVertex;
// no active model, we need to resolve/load one.
......@@ -158,145 +215,33 @@ namespace fggl::gfx::ogl4 {
}
#endif
static void forward_camera_pass(const entity::EntityID& camera, const fggl::entity::EntityManager& world) {
// 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 =
glm::perspective(camComp.fov, camComp.aspectRatio, camComp.nearPlane, camComp.farPlane);
const math::mat4 viewMatrix = glm::lookAt(camTransform.origin(), camComp.target, camTransform.up());
// TODO lighting needs to not be this...
math::vec3 lightPos{0.0f, 10.0f, 0.0f};
std::shared_ptr<ogl::Shader> shader = nullptr;
ogl::Location mvpMatrixUniform = 0;
ogl::Location mvMatrixUniform = 0;
auto renderables = world.find<StaticModel>();
for (const auto &entity : renderables) {
// ensure that the model pipeline actually exists...
const auto &model = world.get<StaticModel>(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");
}
// set model transform
const auto &transform = world.get<math::Transform>(entity);
shader->setUniformMtx(mvpMatrixUniform, projectionMatrix * viewMatrix * transform.model());
shader->setUniformMtx(mvMatrixUniform, viewMatrix * transform.model());
auto normalMatrix = glm::mat3(glm::transpose(inverse(transform.model())));
shader->setUniformMtx(shader->uniform("NormalMatrix"), normalMatrix);
// setup lighting mode
if (shader->hasUniform("lights[0].isEnabled")) {
bool local = true;
shader->setUniformI(shader->uniform("lights[0].isEnabled"), 1);
shader->setUniformI(shader->uniform("lights[0].isLocal"), local);
shader->setUniformI(shader->uniform("lights[0].isSpot"), 0);
shader->setUniformF(shader->uniform("lights[0].constantAttenuation"), 5.0f);
shader->setUniformF(shader->uniform("lights[0].linearAttenuation"), 0.0f);
shader->setUniformF(shader->uniform("lights[0].quadraticAttenuation"), 0.0f);
shader->setUniformF(shader->uniform("Strength"), 0.6F);
if (!local) {
lightPos = glm::normalize(lightPos);
auto viewDir = glm::normalize(camTransform.origin() - transform.origin());
auto halfVector = glm::normalize(lightPos + viewDir);
shader->setUniformF(shader->uniform("lights[0].halfVector"), halfVector);
shader->setUniformF(shader->uniform("EyeDirection"), viewDir);
shader->setUniformF(shader->uniform("lights[0].position"), lightPos);
} else {
auto camModelView = (viewMatrix * camTransform.model() * math::vec4(0.0f, 0.0f, 0.0f, 1.0f));
auto modelModelView = (viewMatrix * transform.model() * math::vec4(0.0f, 0.0f, 0.0f, 1.0f));
math::vec3 viewDir = glm::normalize(camModelView - modelModelView);
shader->setUniformF(shader->uniform("EyeDirection"), viewDir);
shader->setUniformF(shader->uniform("lights[0].position"),
math::vec3(viewMatrix * math::vec4(lightPos, 1.0f)));
}
shader->setUniformF(shader->uniform("lights[0].ambient"), {0.0f, 0.5f, 0.0f});
shader->setUniformF(shader->uniform("lights[0].colour"), {0.5f, 0.5f, 0.5f});
}
// material detection with fallback
auto* material = world.tryGet<PhongMaterial>(entity);
if ( material == nullptr ) {
material = &DEFAULT_MATERIAL;
}
if ( shader->hasUniform("materials[0].ambient") ) {
shader->setUniformF(shader->uniform("materials[0].emission"), material->emission);
shader->setUniformF(shader->uniform("materials[0].ambient"), material->ambient);
shader->setUniformF(shader->uniform("materials[0].diffuse"), material->diffuse);
shader->setUniformF(shader->uniform("materials[0].specular"), material->specular);
shader->setUniformF(shader->uniform("materials[0].shininess"), material->shininess);
}
if (shader->hasUniform("lightPos")) {
shader->setUniformF(shader->uniform("lightPos"), lightPos);
}
auto vao = model.vao;
vao->bind();
model.vertexData->bind();
if (model.restartIndex != NO_RESTART_IDX) {
glEnable(GL_PRIMITIVE_RESTART);
glPrimitiveRestartIndex(model.restartIndex);
}
auto *elements = model.elements.get();
vao->drawElements(*elements, model.drawType, model.elementCount);
if (model.restartIndex != NO_RESTART_IDX) {
glDisable(GL_PRIMITIVE_RESTART);
}
}
}
void StaticModelRenderer::renderModelsForward(const entity::EntityManager &world) {
void StaticModelRenderer::renderModelsForward(const entity::EntityManager &world, bool debugMode) {
// fetch cameras we will need to render with
auto cameras = world.find<gfx::Camera>();
// if there are no cameras, we can't do anything...
if (cameras.empty()) {
spdlog::warn("asked to render static models, but there were no cameras");
debug::warning("asked to render static models, but there were no cameras");
return;
}
// perform a rendering pass for each camera (will usually only be one...)
for (const auto &cameraEnt : cameras) {
//TODO should be clipping this to only visible objects
forward_camera_pass(cameraEnt, world);
//forward_camera_pass(cameraEnt, world);
forward_pass<ogl4::StaticMesh>(cameraEnt, world, m_assets);
forward_pass<ogl4::StaticMultiMesh>(cameraEnt, world, m_assets);
// enable rendering normals
if ( debugMode ) {
auto normalShader = m_shaders->get("normals");
forward_pass_normals<StaticMesh>(cameraEnt, world, normalShader);
forward_pass_normals<StaticMultiMesh>(cameraEnt, world, normalShader);
//forward_normal_pass(cameraEnt, world, normalShader);
}
}
}
......
......@@ -18,6 +18,7 @@
#include "fggl/gfx/ogl4/module.hpp"
#include "fggl/gfx/phong.hpp"
#include "fggl/mesh/components.hpp"
#include "fggl/data/procedural.hpp"
#include "fggl/assets/loader.hpp"
......@@ -30,29 +31,32 @@ namespace fggl::gfx {
constexpr const char *SHAPE_SPHERE{"sphere"};
constexpr const char *SHAPE_BOX{"box"};
static void process_shape(const YAML::Node &node, data::Mesh &mesh) {
auto transform = data::OFFSET_NONE;
auto offset = node["offset"].as<math::vec3>(math::VEC3_ZERO);
transform = glm::translate(transform, offset);
auto scale = node["scale"].as<math::vec3>(math::VEC3_ONES);
transform = glm::scale(transform, scale);
debug::debug("scale: {}, {}, {}", scale.x, scale.y, scale.z);
// now the shape itself
auto type = node["type"].as<std::string>();
if (type == SHAPE_BOX) {
data::make_cube(mesh, transform);
} else if (type == SHAPE_SPHERE) {
auto stacks = node["stacks"].as<uint32_t>(DEFAULT_STACKS);
auto slices = node["slices"].as<uint32_t>(DEFAULT_SLICES);
data::make_sphere(mesh, transform, stacks, slices);
} else {
debug::log(debug::Level::warning, "unknown shape type requested: {}", type);
namespace {
void process_shape(const YAML::Node &node, mesh::Mesh3D &mesh) {
auto transform = data::OFFSET_NONE;
auto offset = node["offset"].as<math::vec3>(math::VEC3_ZERO);
transform = glm::translate(transform, offset);
auto scale = node["scale"].as<math::vec3>(math::VEC3_ONES);
transform = glm::scale(transform, scale);
debug::debug("scale: {}, {}, {}", scale.x, scale.y, scale.z);
// now the shape itself
auto type = node["type"].as<std::string>();
if (type == SHAPE_BOX) {
data::make_cube(mesh, transform);
} else if (type == SHAPE_SPHERE) {
auto stacks = node["stacks"].as<uint32_t>(DEFAULT_STACKS);
auto slices = node["slices"].as<uint32_t>(DEFAULT_SLICES);
data::make_sphere(mesh, transform, stacks, slices);
} else {
debug::log(debug::Level::warning, "unknown shape type requested: {}", type);
}
}
}
void attach_mesh(const entity::ComponentSpec &spec, entity::EntityManager &manager, const entity::EntityID &id, modules::Services &services) {
// check for the asset service
......@@ -65,46 +69,57 @@ namespace fggl::gfx {
// asset is a procedural mesh description
if (spec.has("shape")) {
data::Mesh* meshAsset = nullptr;
auto pipeline = spec.get<std::string>("pipeline", "");
// check if we had previously loaded this asset
const auto shapeName = spec.get<std::string>("shape_id", "");
/*const auto shapeName = spec.get<std::string>("shape_id", "");
if ( !shapeName.empty() ) {
meshAsset = assetService->get<data::Mesh>(shapeName);
}
}*/
// we've not loaded this before - generate mesh
if ( meshAsset == nullptr ) {
// procedural meshes are build as static meshes first
auto* meshTmp = new data::Mesh();
if ( true ) {
// procedural meshes are build as static meshes first
if (spec.config["shape"].IsSequence()) {
mesh::MultiMesh3D* multiMesh;
for (const auto &node : spec.config["shape"]) {
process_shape(node, *meshTmp);
auto* meshAsset = new mesh::Mesh3D();
process_shape(node, *meshAsset);
multiMesh->meshes.push_back(*meshAsset);
delete meshAsset;
}
} else {
process_shape(spec.config["shape"], *meshTmp);
}
meshTmp->removeDups();
if (!shapeName.empty()) {
meshAsset = assetService->set(shapeName, meshTmp);
#ifdef FGGL_ALLOW_DEFERRED_UPLOAD
// the graphics stack can detect static meshes without a rendering proxy at runtime and fix it but this
// requires loading the whole model into the ECS (and should be removed in the future). instead we should
// be triggering the upload at this point (if needed).
auto &entityMesh = manager.add<mesh::StaticMultiMesh3D>(id);
entityMesh.mesh = *multiMesh;
entityMesh.pipeline = pipeline;
debug::warning("HACKY: Triggered proc mesh - using deferred upload");
#endif
} else {
meshAsset = meshTmp;
auto* meshAsset = new mesh::Mesh3D();
process_shape(spec.config["shape"], *meshAsset);
#ifdef FGGL_ALLOW_DEFERRED_UPLOAD
// the graphics stack can detect static meshes without a rendering proxy at runtime and fix it but this
// requires loading the whole model into the ECS (and should be removed in the future). instead we should
// be triggering the upload at this point (if needed).
auto &entityMesh = manager.add<mesh::StaticMesh3D>(id);
entityMesh.mesh = *meshAsset;
entityMesh.pipeline = pipeline;
debug::warning("HACKY: Triggered proc mesh - using deferred upload");
#endif
}
//assetService->set(shapeName, meshTmp);
}
// TODO we need to trigger an upload to the GPU (somehow)
#ifdef FGGL_ALLOW_DEFERRED_UPLOAD
// the graphics stack can detect static meshes without a rendering proxy at runtime and fix it but this
// requires loading the whole model into the ECS (and should be removed in the future). instead we should
// be triggering the upload at this point (if needed).
auto &entityMesh = manager.add<data::StaticMesh>(id);
entityMesh.pipeline = pipeline;
entityMesh.mesh = *meshAsset;
debug::warning("HACKY: Triggered proc mesh - using deferred upload");
#endif
return;
}
......@@ -113,7 +128,9 @@ namespace fggl::gfx {
if ( spec.has("model") ) {
// figure out what model we want
auto assetStr = spec.get<std::string>("model", "");
auto* asset = assetService->get<ogl4::StaticModel>(assetStr);
auto assetId = assets::asset_from_user( assetStr );
auto* asset = assetService->get<ogl4::StaticModel>(assetId);
if ( asset == nullptr ) {
// the asset is not loaded/does not exist
......@@ -130,7 +147,7 @@ namespace fggl::gfx {
void attach_material(const entity::ComponentSpec &spec,
entity::EntityManager &manager,
const entity::EntityID &id,
modules::Services& services ) {
modules::Services& /*services*/ ) {
auto &mat = manager.add<gfx::PhongMaterial>(id);
mat.ambient = spec.get<math::vec3>("ambient", gfx::DEFAULT_AMBIENT);
mat.diffuse = spec.get<math::vec3>("diffuse", gfx::DEFAULT_DIFFUSE);
......@@ -138,24 +155,40 @@ namespace fggl::gfx {
mat.shininess = spec.get<float>("ambient", gfx::DEFAULT_SHININESS);
}
void attach_light(const entity::ComponentSpec &spec, entity::EntityManager &manager, const entity::EntityID &id, modules::Services& services) {
auto &light = manager.add<gfx::Light>(id);
void attach_light_directional(const entity::ComponentSpec &spec, entity::EntityManager &manager, const entity::EntityID &id, modules::Services& /*services*/) {
auto &light = manager.add<gfx::DirectionalLight>(id);
light.position = spec.get<math::vec3>("direction", -math::UP);
light.ambient = spec.get<math::vec3>("ambient", gfx::colours::WHITE);
light.specular = spec.get<math::vec3>("specular", gfx::colours::WHITE);
light.diffuse = spec.get<math::vec3>("diffuse", gfx::colours::WHITE);
}
void attach_light_point(const entity::ComponentSpec &spec, entity::EntityManager &manager, const entity::EntityID &id, modules::Services& /*services*/) {
auto &light = manager.add<gfx::PointLight>(id);
light.position = spec.get<math::vec3>("position", math::VEC3_ZERO);
light.constant = spec.get<float>("constant", 1.0F);
light.linear = spec.get<float>("linear", 0.0014F);
light.quadratic = spec.get<float>("quadratic", 0.000007F);
}
bool OpenGL4::factory(modules::ModuleService service, modules::Services &services) {
auto OpenGL4::factory(modules::ServiceName service, modules::Services &services) -> bool {
if (service == WindowGraphics::service) {
// setup the thing responsible for graphics
auto *storage = services.get<data::Storage>();
auto *fontLibrary = services.get<gui::FontLibrary>();
auto *assets = services.get<assets::AssetManager>();
services.bind<WindowGraphics, ogl4::WindowGraphics>(storage, fontLibrary, assets);
// register as responsible for creating rendering components
auto *entityFactory = services.get<entity::EntityFactory>();
entityFactory->bind(data::StaticMesh::guid, attach_mesh);
entityFactory->bind(mesh::StaticMesh3D::guid, attach_mesh);
entityFactory->bind(mesh::StaticMultiMesh3D::guid, attach_mesh);
entityFactory->bind(gfx::PhongMaterial::guid, attach_material);
entityFactory->bind(gfx::Light::guid, attach_light);
entityFactory->bind(gfx::DirectionalLight::guid, attach_light_directional);
entityFactory->bind(gfx::PointLight::guid, attach_light_point);
return true;
}
......
......@@ -20,8 +20,8 @@
namespace fggl::gfx::ogl4 {
Graphics *WindowGraphics::create(display::Window &window) {
return new OpenGL4Backend(m_storage, m_fonts, m_assets);
auto WindowGraphics::create(display::Window &window) -> Graphics * {
return new OpenGL4Backend(m_storage, m_fonts, m_assets, (GlFunctionLoader)glfwGetProcAddress);
}
}
\ No newline at end of file
......@@ -21,7 +21,6 @@
#include <string>
#include <stdexcept>
#include <GLFW/glfw3.h>
#include <spdlog/spdlog.h>
namespace fggl::display::glfw {
......@@ -34,37 +33,37 @@ namespace fggl::display::glfw {
fgglWindow->framesize(width, height);
}
static void fggl_input_cursor(GLFWwindow *window, double x, double y) {
static void fggl_input_cursor(GLFWwindow *window, double xPos, double yPos) {
auto &input = GlfwInputManager::instance();
auto *fgglWin = static_cast<Window *>(glfwGetWindowUserPointer(window));
#ifndef FGGL_INPUT_SCREEN_COORDS
// convert to nice ranges...
x = (x / fgglWin->width() * 2) - 1.0; // [-1, 1]
y = (y / fgglWin->height() * 2) - 1.0; // [-1, 1]
xPos = (xPos / fgglWin->width() * 2) - 1.0; // [-1, 1]
yPos = (yPos / fgglWin->height() * 2) - 1.0; // [-1, 1]
#endif
// inform the input system
input.onMouseMove(x, y);
input.onMouseMove(xPos, yPos);
}
static void fggl_input_scroll(GLFWwindow *window, double x, double y) {
static void fggl_input_scroll(GLFWwindow */*window*/, double xPos, double yPos) {
auto &input = GlfwInputManager::instance();
input.onMouseScroll(x, y);
input.onMouseScroll(xPos, yPos);
}
static void fggl_input_mouse_btn(GLFWwindow *window, int btn, int action, int mods) {
static void fggl_input_mouse_btn(GLFWwindow */*window*/, int btn, int action, int /*mods*/) {
auto &input = GlfwInputManager::instance();
input.onMouseButton(btn, action == GLFW_PRESS);
}
static void fggl_input_keyboard(GLFWwindow *window, int key, int scancode, int action, int mods) {
static void fggl_input_keyboard(GLFWwindow */*window*/, int /*key*/, int scancode, int action, int /*mods*/) {
auto &input = GlfwInputManager::instance();
input.onKeyEvent(scancode, action == GLFW_PRESS || action == GLFW_REPEAT);
}
static void fggl_update_joystick(fggl::input::GamepadInput &input, int jid) {
bool isGamepad = glfwJoystickIsGamepad(jid);
bool isGamepad = (glfwJoystickIsGamepad(jid) == GLFW_TRUE);
if (isGamepad) {
if (!input.present(jid)) {
......@@ -144,7 +143,7 @@ namespace fggl::display::glfw {
GlfwContext::~GlfwContext() {
glfwTerminate();
spdlog::debug("[glfw] context terminated");
debug::trace("[glfw] context terminated");
}
void GlfwContext::pollEvents() {
......@@ -157,7 +156,9 @@ namespace fggl::display::glfw {
Window::Window(std::shared_ptr<GlfwContext> context, gfx::WindowGraphics *graphics)
: m_context(std::move(context)), m_window(nullptr), m_framesize() {
spdlog::debug("[glfw] creating window");
// don't iconify when focus is lost.
glfwWindowHint( GLFW_AUTO_ICONIFY, GLFW_FALSE );
// FIXME - this ties the graphics API before window creation
auto graphicsConfig = graphics->config();
......@@ -185,12 +186,11 @@ namespace fggl::display::glfw {
// bind the graphics API
glfwMakeContextCurrent(m_window);
m_graphics = graphics->createMain(*this);
spdlog::debug("[glfw] window creation complete");
}
Window::~Window() {
if ( m_graphics != nullptr ) {
delete m_graphics;
m_graphics = nullptr;
}
......@@ -219,14 +219,14 @@ namespace fggl::display::glfw {
glfwMakeContextCurrent(m_window);
}
fggl::math::vec2i Window::frameSize() const {
auto Window::frameSize() const -> fggl::math::vec2i {
assert(m_window != nullptr);
math::vec2i size;
glfwGetFramebufferSize(m_window, &size.x, &size.y);
return size;
}
bool Window::wantClose() const {
auto Window::wantClose() const -> bool {
assert(m_window != nullptr);
return glfwWindowShouldClose(m_window) == GLFW_TRUE;
}
......
target_sources( fggl
PRIVATE
hexagon.cpp
)
\ No newline at end of file
/*
* 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.
//
#include "fggl/grid/hexagon.hpp"
namespace fggl::grid {
std::vector<IntHex> lineTo(const IntHex& start, const IntHex& end) {
const int distance = start.distance(end);
std::vector<IntHex> line;
for (auto i=0; i < distance; ++i) {
line.push_back( round2(hexLerp(start, end, 1.0F/distance * i)) );
}
return line;
}
} // namespace fggl:grid
target_sources( ${PROJECT_NAME}
PRIVATE
widget.cpp
widgets.cpp
containers.cpp
fonts.cpp
model/parser.cpp
model/structure.cpp
renderer/renderer.cpp
)
find_package(Freetype)
target_link_libraries(${PROJECT_NAME} PUBLIC Freetype::Freetype)
......@@ -17,7 +17,7 @@
namespace fggl::gui {
Widget *Container::getChildAt(const math::vec2 &point) {
auto Container::getChildAt(const math::vec2 &point) -> Widget * {
for (auto &child : m_children) {
if (child->contains(point)) {
return child->getChildAt(point);
......@@ -27,7 +27,7 @@ namespace fggl::gui {
return nullptr;
}
bool Container::contains(const math::vec2 &point) {
auto Container::contains(const math::vec2 &point) -> bool {
return true;
}
......@@ -82,8 +82,8 @@ namespace fggl::gui {
int rows = m_children.size() / m_cols;
// figure out the width and heights
float* widths = new float[m_cols]{0.0F};
float* heights = new float[rows]{0.0F};
auto* widths = new float[m_cols]{0.0F};
auto* heights = new float[rows]{0.0F};
for ( auto idx = 0U; idx < m_children.size(); ++idx) {
auto& child = m_children[idx];
int col = idx % m_cols;
......@@ -95,8 +95,8 @@ namespace fggl::gui {
// populate the grid
fggl::math::vec2i pos{0, 0};
int row = 0;
int col = 0;
unsigned int row = 0;
unsigned int col = 0;
for ( auto& child : m_children ) {
fggl::math::vec2i size{ widths[col], heights[row] };
child->size(pos, size);
......@@ -113,6 +113,9 @@ namespace fggl::gui {
}
}
// cleanup variables
delete[] widths;
delete[] heights;
}
}
......@@ -145,7 +148,7 @@ namespace fggl::gui {
void Panel::render(gfx::Paint &paint) {
// background painting time
gfx::Path2D background(topLeft());
background.colour(math::vec3(32.0f / 255.0f, 74.0F / 255.0F, 135.0F / 255.0F));
background.colour(math::vec3(32.0F / 255.0F, 74.0F / 255.0F, 135.0F / 255.0F));
draw_box(background, topLeft(), bottomRight());
paint.fill(background);
......
......@@ -25,19 +25,19 @@ namespace fggl::gui {
FontLibrary::FontLibrary(data::Storage *storage) : m_context(nullptr), m_storage(storage) {
FT_Init_FreeType(&m_context);
m_defaultFont = getFont(DEFAULT_FONT_NAME);
}
FontLibrary::~FontLibrary() {
// free all fonts
for (auto &face : m_cache) {
face.second = nullptr;
}
m_defaultFont = nullptr;
m_cache.clear();
// shut the library down
FT_Done_FreeType(m_context);
}
GlyphMetrics &FontFace::populateMetrics(char letter) {
auto FontFace::populateMetrics(char letter) -> GlyphMetrics & {
if (FT_Load_Char(m_face, letter, FT_LOAD_RENDER)) {
// something bad happened
return m_metrics['?'];
......@@ -54,7 +54,7 @@ namespace fggl::gui {
return it.first->second;
}
void FontFace::texture(char letter, int &width, int &height, void **buff) {
void FontFace::texture(char letter, int &width, int &height, unsigned char **buff) {
if (FT_Load_Char(m_face, letter, FT_LOAD_RENDER)) {
// something bad happened
return;
......
/*
* 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 11/03/23.
//
#include "fggl/gui/model/parser.hpp"
#include <yaml-cpp/yaml.h>
namespace fggl::gui::model {
Widget* YamlToWidgetTree(WidgetFactory& factory, const YAML::Node& config) {
Widget* root;
if ( config["template"] ) {
root = factory.build( config["template"].as<std::string>());
} else {
root = factory.buildEmpty();
}
// deal with attrs
for ( auto attr : config["attrs"] ) {
root->set(attr.first.as<std::string>(), attr.second.as<std::string>());
}
// deal with child nodes
for ( auto child : config["children"] ) {
Widget* childWidget = YamlToWidgetTree(factory, child);
root->addChild(*childWidget);
}
// are we a template definition?
if ( config["define"] ) {
factory.push( config["define"].as<std::string>(), std::move(*root) );
return factory.getTemplate( config["define"].as<std::string>() );
}
return root;
}
inline Widget* parseFile(WidgetFactory& factory, const std::string& path) {
YAML::Node root = YAML::LoadFile(path);
if ( !root ){
return nullptr;
}
return YamlToWidgetTree(factory, root);
}
}
\ No newline at end of file