diff --git a/demo/data/rollball.yml b/demo/data/rollball.yml index bdf74dea1e672f028743257984a932323d16e986..015c22605f03d8895b67d13da92d582e9ffa0416 100644 --- a/demo/data/rollball.yml +++ b/demo/data/rollball.yml @@ -13,6 +13,7 @@ prefabs: Transform: StaticMesh: pipeline: redbook/debug + shape_id: "rb_wall_x" shape: type: box scale: [1.0, 5.0, 41] @@ -28,6 +29,7 @@ prefabs: Transform: StaticMesh: pipeline: redbook/debug + shape_id: "rb_wall_z" shape: type: box scale: [39, 5, 1] @@ -42,6 +44,7 @@ prefabs: Transform: StaticMesh: pipeline: redbook/debug + shape_id: "rb_floor" shape: type: box # we don't (currently) support planes... scale: [39, 0.5, 39] @@ -55,6 +58,7 @@ prefabs: Transform: StaticMesh: pipeline: redbook/debug + shape_id: "rb_player" shape: type: sphere gfx::material: @@ -71,6 +75,7 @@ prefabs: Transform: StaticMesh: pipeline: redbook/debug + shape_id: "rb_collect" shape: type: box gfx::material: diff --git a/demo/data/topdown.yml b/demo/data/topdown.yml index 0b1c8c5f32d48192480a222ebde0f76785e7a91f..bf6f533596c7977c39b36f0f0d417bc5d34c268d 100644 --- a/demo/data/topdown.yml +++ b/demo/data/topdown.yml @@ -5,6 +5,7 @@ prefabs: Transform: StaticMesh: pipeline: redbook/debug + shape_id: td_wall_x shape: type: box scale: [1.0, 5.0, 41] @@ -24,6 +25,7 @@ prefabs: Transform: StaticMesh: pipeline: redbook/debug + shape_id: td_wall_y shape: type: box scale: [39, 5, 1] @@ -42,6 +44,7 @@ prefabs: Transform: StaticMesh: pipeline: redbook/debug + shape_id: td_floor shape: type: box # we don't (currently) support planes... scale: [39, 0.5, 39] @@ -60,6 +63,7 @@ prefabs: Transform: StaticMesh: pipeline: redbook/lighting + shape_id: td_player shape: type: sphere gfx::material: @@ -76,6 +80,7 @@ prefabs: Transform: StaticMesh: pipeline: redbook/lighting + shape_id: td_collect shape: type: box gfx::material: diff --git a/fggl/entity/loader/loader.cpp b/fggl/entity/loader/loader.cpp index 316aae5a809e9a090584b2d49461d3a0af180008..c38715bb9acc7f01ac8f10399b0bd384bee0787c 100644 --- a/fggl/entity/loader/loader.cpp +++ b/fggl/entity/loader/loader.cpp @@ -21,7 +21,7 @@ namespace fggl::entity { - assets::AssetRefRaw load_prototype(EntityFactory *factory, const assets::AssetGUID &guid, assets::AssetData data) { + assets::AssetRefRaw load_prototype(assets::Loader* loader, EntityFactory *factory, const assets::AssetGUID &guid, assets::AssetData data) { auto *filePath = std::get<assets::AssetPath *>(data); // We need to process the prototypes, and load them into the asset system. @@ -31,10 +31,7 @@ namespace fggl::entity { for (const auto &prefab : prefabs) { auto name = prefab["name"].as<fggl::util::GUID>(); - - #ifndef NDEBUG - debug::info("found prefab: {}", fggl::util::guidToString(name)); - #endif + debug::info("found prefab: {}", name); // set up the components EntitySpec entity{}; @@ -48,11 +45,7 @@ namespace fggl::entity { entity.components[compId] = compSpec; entity.ordering.push_back(compId); - #ifndef NDEBUG - debug::trace("{} has component {}", - fggl::util::guidToString(name), - fggl::util::guidToString(compId)); - #endif + debug::trace("prefab {} has component {}", name, compId); } factory->define(name, entity); diff --git a/fggl/entity/module.cpp b/fggl/entity/module.cpp index c2936f4845db8d90227f61bb14b61212efe4a6fd..752b108862750eb54798a3f3ff571b2f2514e659 100644 --- a/fggl/entity/module.cpp +++ b/fggl/entity/module.cpp @@ -21,7 +21,7 @@ namespace fggl::entity { - void make_transform(const entity::ComponentSpec &spec, EntityManager &manager, const entity::EntityID entity) { + void make_transform(const entity::ComponentSpec &spec, EntityManager &manager, const entity::EntityID entity, modules::Services &/*svc*/) { auto &transform = manager.add<math::Transform>(entity); transform.origin(spec.get<math::vec3>("origin", math::VEC3_ZERO)); transform.euler(spec.get<math::vec3>("rotation", math::VEC3_ZERO)); @@ -36,13 +36,13 @@ namespace fggl::entity { bool ECS::factory(modules::ModuleService service, modules::Services &services) { if (service == EntityFactory::service) { - auto *factory = services.create<EntityFactory>(); + 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](const assets::AssetGUID &a, assets::AssetData b) { - return load_prototype(factory, a, b); + assetLoader->setFactory(PROTOTYPE_ASSET, [factory](assets::Loader* loader, const assets::AssetGUID &a, assets::AssetData b) { + return load_prototype(loader, factory, a, b); }, assets::LoadType::PATH); return true; diff --git a/fggl/gfx/ogl/renderer.cpp b/fggl/gfx/ogl/renderer.cpp index b92d80e7425818320a367c96f31c957038e256f2..e95da195a8e0f373a82ceb212c3f18c13120d9f7 100644 --- a/fggl/gfx/ogl/renderer.cpp +++ b/fggl/gfx/ogl/renderer.cpp @@ -139,8 +139,8 @@ namespace fggl::gfx { using data::Mesh2D; using data::Vertex2D; - OpenGL4Backend::OpenGL4Backend(data::Storage *storage, gui::FontLibrary *fonts) - : fggl::gfx::Graphics(), m_canvasPipeline(INVALID_SHADER_ID), m_storage(storage) { + OpenGL4Backend::OpenGL4Backend(data::Storage *storage, gui::FontLibrary *fonts, assets::AssetManager *assets) + : 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); @@ -169,7 +169,7 @@ namespace fggl::gfx { // setup 2D rendering system ShaderConfig shader2DConfig = ShaderConfig::named("canvas"); m_canvasPipeline = m_cache->load(shader2DConfig); - if (m_canvasPipeline == INVALID_SHADER_ID) { + if (m_canvasPipeline == nullptr) { debug::error("failed to load shader2D - using fallback"); m_canvasPipeline = m_cache->get(ogl4::FALLBACK_CANVAS_PIPELINE); } @@ -181,7 +181,7 @@ namespace fggl::gfx { // rendering helpers m_canvasRenderer = std::make_unique<ogl4::CanvasRenderer>(fonts); - m_modelRenderer = std::make_unique<ogl4::StaticModelRenderer>(m_cache.get()); + m_modelRenderer = std::make_unique<ogl4::StaticModelRenderer>(m_cache.get(), assets); m_debugRenderer = std::make_unique<ogl4::DebugRenderer>(m_cache->getOrLoad(ShaderConfig::named("debug"))); if (m_debugRenderer) { @@ -199,7 +199,7 @@ namespace fggl::gfx { return; } - m_canvasRenderer->render(m_canvasPipeline, paint); + m_canvasRenderer->render(*m_canvasPipeline, paint); } void OpenGL4Backend::drawScene(entity::EntityManager &world) { diff --git a/fggl/gfx/ogl/shader.cpp b/fggl/gfx/ogl/shader.cpp index 953751d50bfa5328ca3082df7d554bc3f69017f7..2d95c2d087f336e1a144c18f2162911bb926a37f 100644 --- a/fggl/gfx/ogl/shader.cpp +++ b/fggl/gfx/ogl/shader.cpp @@ -20,325 +20,329 @@ #include <vector> #include <spdlog/spdlog.h> -using namespace fggl::gfx; - -bool ShaderCache::compileShaderFromSource(const std::string &source, GLuint sid) { - // upload and compile shader - const char *src = source.c_str(); - glShaderSource(sid, 1, &src, nullptr); - glCompileShader(sid); - - // check it worked - GLint compiled = GL_FALSE; - glGetShaderiv(sid, GL_COMPILE_STATUS, &compiled); - if (compiled == GL_FALSE) { - - GLint maxLength = 0; - glGetShaderiv(sid, GL_INFO_LOG_LENGTH, &maxLength); - char *infoLog = new char[maxLength]; - glGetShaderInfoLog(sid, maxLength, &maxLength, infoLog); - - spdlog::warn("could not compile shader source: {}", infoLog); - delete[] infoLog; - return false; +namespace fggl::gfx { + + bool ShaderCache::compileShaderFromSource(const std::string &source, GLuint sid) { + // upload and compile shader + const char *src = source.c_str(); + glShaderSource(sid, 1, &src, nullptr); + glCompileShader(sid); + + // check it worked + GLint compiled = GL_FALSE; + glGetShaderiv(sid, GL_COMPILE_STATUS, &compiled); + if (compiled == GL_FALSE) { + + GLint maxLength = 0; + glGetShaderiv(sid, GL_INFO_LOG_LENGTH, &maxLength); + char *infoLog = new char[maxLength]; + glGetShaderInfoLog(sid, maxLength, &maxLength, infoLog); + + spdlog::warn("could not compile shader source: {}", infoLog); + delete[] infoLog; + return false; + } + + return true; } - return true; -} + bool ShaderCache::readAndCompileShader(const std::string &filename, GLuint shader) { + std::string source; + bool result = m_storage->load(fggl::data::Data, filename, &source); + if (!result) { + spdlog::warn("could not load shader source from disk: {}", filename.c_str()); + return false; + } -bool ShaderCache::readAndCompileShader(const std::string &filename, GLuint shader) { - std::string source; - bool result = m_storage->load(fggl::data::Data, filename, &source); - if (!result) { - spdlog::warn("could not load shader source from disk: {}", filename.c_str()); - return false; + return compileShaderFromSource(source, shader); } - return compileShaderFromSource(source, shader); -} + ShaderCache::ShaderCache(fggl::data::Storage *storage) : m_storage(storage), m_shaders(), m_binary(true) { -ShaderCache::ShaderCache(fggl::data::Storage *storage) : m_storage(storage), m_shaders(), m_binary(true) { + if (!GLAD_GL_ARB_get_program_binary) { + spdlog::warn("the graphics card doesn support shader caching, disabling"); + m_binary = false; + } else { + // debug - disable shader cache + m_binary = false; + } - if (!GLAD_GL_ARB_get_program_binary) { - spdlog::warn("the graphics card doesn support shader caching, disabling"); - m_binary = false; - } else { - // debug - disable shader cache - m_binary = false; + if (GLAD_GL_ARB_shading_language_include) { + setupIncludes(); + } + initFallbackPipelines(); } - if (GLAD_GL_ARB_shading_language_include) { - setupIncludes(); - } - initFallbackPipelines(); -} + void ShaderCache::setupIncludes() { + auto root = m_storage->resolvePath(data::StorageType::Data, "include"); + auto paths = m_storage->findResources(root, ".glsl"); -void ShaderCache::setupIncludes() { - auto root = m_storage->resolvePath(data::StorageType::Data, "include"); - auto paths = m_storage->findResources(root, ".glsl"); + for (auto &path : paths) { + std::string source; + m_storage->load(fggl::data::Data, path.string(), &source); - for (auto &path : paths) { - std::string source; - m_storage->load(fggl::data::Data, path.string(), &source); + auto relPath = std::filesystem::relative(path, root); + const auto relPathStr = "/" + relPath.string(); + glNamedStringARB(GL_SHADER_INCLUDE_ARB, -1, relPathStr.c_str(), -1, source.c_str()); + } - auto relPath = std::filesystem::relative(path, root); - const auto relPathStr = "/" + relPath.string(); - glNamedStringARB(GL_SHADER_INCLUDE_ARB, -1, relPathStr.c_str(), -1, source.c_str()); } -} + bool ShaderCache::loadFromDisk(GLuint pid, const std::string &pipelineName) { -bool ShaderCache::loadFromDisk(GLuint pid, const std::string &pipelineName) { + BinaryCache cache; + auto fname = "shader_" + pipelineName + ".bin"; + bool status = m_storage->load(fggl::data::Cache, fname, &cache); - BinaryCache cache; - auto fname = "shader_" + pipelineName + ".bin"; - bool status = m_storage->load(fggl::data::Cache, fname, &cache); + if (!status) { + spdlog::info("cached shader '{}' could not be loaded from disk", pipelineName); + return false; + } - if (!status) { - spdlog::info("cached shader '{}' could not be loaded from disk", pipelineName); - return false; + bool result = cacheLoad(pid, &cache); + std::free(cache.data); + return result; } - bool result = cacheLoad(pid, &cache); - std::free(cache.data); - return result; -} - -void ShaderCache::saveToDisk(GLuint pid, const std::string &pipelineName) { - BinaryCache cache; - cacheSave(pid, &cache); + void ShaderCache::saveToDisk(GLuint pid, const std::string &pipelineName) { + BinaryCache cache; + cacheSave(pid, &cache); - auto fname = "shader_" + pipelineName + ".bin"; - m_storage->save(fggl::data::Cache, fname, &cache); -} + auto fname = "shader_" + pipelineName + ".bin"; + m_storage->save(fggl::data::Cache, fname, &cache); + } -GLuint ShaderCache::getOrLoad(const ShaderConfig &config) { - try { - return m_shaders.at(config.name); - } catch (std::out_of_range &e) { - return load(config); + ShaderCache::ShaderPtr ShaderCache::getOrLoad(const ShaderConfig &config) { + try { + return m_shaders.at(config.name); + } catch (std::out_of_range &e) { + return load(config); + } } -} -GLuint ShaderCache::get(const std::string &name) { - return m_shaders.at(name); -} + ShaderCache::ShaderPtr ShaderCache::get(const std::string &name) { + return m_shaders.at(name); + } -GLuint ShaderCache::load(const ShaderConfig &config) { + ShaderCache::ShaderPtr ShaderCache::load(const ShaderConfig &config) { + spdlog::debug("starting shader program generation for {}", config.name); - spdlog::debug("starting shader program generation for {}", config.name); + GLuint pid = glCreateProgram(); - GLuint pid = glCreateProgram(); + if (m_binary) { + // if we have support for shader cache, give that a go + bool worked = loadFromDisk(pid, config.name); + if (worked) { + auto shader = std::make_shared<ogl::Shader>(pid); + m_shaders[config.name] = shader; + return shader; + } - if (m_binary) { - // if we have support for shader cache, give that a go - bool worked = loadFromDisk(pid, config.name); - if (worked) { - m_shaders[config.name] = pid; - return pid; + spdlog::debug("could not use cached shader for '{}', doing full compile.", config.name); } - spdlog::debug("could not use cached shader for '{}', doing full compile.", config.name); - } + // TODO actual shader loading + GLuint vertShader = glCreateShader(GL_VERTEX_SHADER); + readAndCompileShader(config.vertex, vertShader); + glAttachShader(pid, vertShader); - // TODO actual shader loading - GLuint vertShader = glCreateShader(GL_VERTEX_SHADER); - readAndCompileShader(config.vertex, vertShader); - glAttachShader(pid, vertShader); + GLuint fragShader = glCreateShader(GL_FRAGMENT_SHADER); + readAndCompileShader(config.fragment, fragShader); + glAttachShader(pid, fragShader); - GLuint fragShader = glCreateShader(GL_FRAGMENT_SHADER); - readAndCompileShader(config.fragment, fragShader); - glAttachShader(pid, fragShader); + GLuint geomShader = 0; + if (config.hasGeom) { + geomShader = glCreateShader(GL_GEOMETRY_SHADER); + readAndCompileShader(config.geometry, geomShader); + glAttachShader(pid, geomShader); + } - GLuint geomShader = 0; - if (config.hasGeom) { - geomShader = glCreateShader(GL_GEOMETRY_SHADER); - readAndCompileShader(config.geometry, geomShader); - glAttachShader(pid, geomShader); - } + glLinkProgram(pid); + glDetachShader(pid, vertShader); + glDetachShader(pid, fragShader); - glLinkProgram(pid); - glDetachShader(pid, vertShader); - glDetachShader(pid, fragShader); + if (config.hasGeom) { + glDetachShader(pid, geomShader); + } - if (config.hasGeom) { - glDetachShader(pid, geomShader); - } + GLint linked = GL_FALSE; + glGetProgramiv(pid, GL_LINK_STATUS, &linked); + if (linked == GL_FALSE) { - GLint linked = GL_FALSE; - glGetProgramiv(pid, GL_LINK_STATUS, &linked); - if (linked == GL_FALSE) { + // get the error + std::array<char, 512> infoLog; + glGetProgramInfoLog(pid, infoLog.size(), nullptr, infoLog.data()); + spdlog::warn("linking shader program '{}' failed: {}", config.name, infoLog.data()); - // get the error - std::array<char, 512> infoLog; - glGetProgramInfoLog(pid, infoLog.size(), nullptr, infoLog.data()); - spdlog::warn("linking shader program '{}' failed: {}", config.name, infoLog.data()); + // cleanup + glDeleteProgram(pid); + glDeleteShader(vertShader); + glDeleteShader(fragShader); + if (config.hasGeom) { + glDeleteShader(geomShader); + } - // cleanup - glDeleteProgram(pid); - glDeleteShader(vertShader); - glDeleteShader(fragShader); - if (config.hasGeom) { - glDeleteShader(geomShader); + return nullptr; } - return INVALID_SHADER_ID; - } + if (m_binary) { + saveToDisk(pid, config.name); + } - if (m_binary) { - saveToDisk(pid, config.name); + // update the cache and return + auto shaderPtr = std::make_shared<ogl::Shader>(pid); + m_shaders[config.name] = shaderPtr; + return shaderPtr; } - // update the cache and return - m_shaders[config.name] = pid; - return pid; -} + ShaderCache::ShaderPtr ShaderCache::load(const ShaderSources &sources, bool allowBinaryCache) { -GLuint ShaderCache::load(const ShaderSources &sources, bool allowBinaryCache) { + spdlog::debug("starting shader program generation for {}", sources.name); - spdlog::debug("starting shader program generation for {}", sources.name); + GLuint pid = glCreateProgram(); - GLuint pid = glCreateProgram(); + if (m_binary && allowBinaryCache) { + // if we have support for shader cache, give that a go + bool worked = loadFromDisk(pid, sources.name); + if (worked) { + auto shader = std::make_shared<ogl::Shader>(pid); + m_shaders[sources.name] = shader; + return shader; + } - if (m_binary && allowBinaryCache) { - // if we have support for shader cache, give that a go - bool worked = loadFromDisk(pid, sources.name); - if (worked) { - m_shaders[sources.name] = pid; - return pid; + spdlog::debug("could not use cached shader for '{}', doing full compile.", sources.name); } - spdlog::debug("could not use cached shader for '{}', doing full compile.", sources.name); - } + // TODO actual shader loading + GLuint vertShader = glCreateShader(GL_VERTEX_SHADER); + compileShaderFromSource(sources.vertexSource, vertShader); + glAttachShader(pid, vertShader); - // TODO actual shader loading - GLuint vertShader = glCreateShader(GL_VERTEX_SHADER); - compileShaderFromSource(sources.vertexSource, vertShader); - glAttachShader(pid, vertShader); + GLuint fragShader = glCreateShader(GL_FRAGMENT_SHADER); + compileShaderFromSource(sources.fragmentSource, fragShader); + glAttachShader(pid, fragShader); - GLuint fragShader = glCreateShader(GL_FRAGMENT_SHADER); - compileShaderFromSource(sources.fragmentSource, fragShader); - glAttachShader(pid, fragShader); + GLuint geomShader = INVALID_SHADER_ID; + if (!sources.geometrySource.empty()) { + geomShader = glCreateShader(GL_GEOMETRY_SHADER); + compileShaderFromSource(sources.geometrySource, geomShader); + glAttachShader(pid, geomShader); + } - GLuint geomShader = INVALID_SHADER_ID; - if (!sources.geometrySource.empty()) { - geomShader = glCreateShader(GL_GEOMETRY_SHADER); - compileShaderFromSource(sources.geometrySource, geomShader); - glAttachShader(pid, geomShader); - } + glLinkProgram(pid); + glDetachShader(pid, vertShader); + glDetachShader(pid, fragShader); - glLinkProgram(pid); - glDetachShader(pid, vertShader); - glDetachShader(pid, fragShader); + if (geomShader != INVALID_SHADER_ID) { + glDetachShader(pid, geomShader); + } - if (geomShader != INVALID_SHADER_ID) { - glDetachShader(pid, geomShader); - } + GLint linked = GL_FALSE; + glGetProgramiv(pid, GL_LINK_STATUS, &linked); + if (linked == GL_FALSE) { - GLint linked = GL_FALSE; - glGetProgramiv(pid, GL_LINK_STATUS, &linked); - if (linked == GL_FALSE) { + // get the error + std::array<char, 512> infoLog{}; + glGetProgramInfoLog(pid, infoLog.size(), nullptr, infoLog.data()); + spdlog::warn("linking shader program '{}' failed: {}", sources.name, infoLog.data()); - // get the error - std::array<char, 512> infoLog; - glGetProgramInfoLog(pid, infoLog.size(), nullptr, infoLog.data()); - spdlog::warn("linking shader program '{}' failed: {}", sources.name, infoLog.data()); + // cleanup + glDeleteProgram(pid); + glDeleteShader(vertShader); + glDeleteShader(fragShader); + if (geomShader != INVALID_SHADER_ID) { + glDeleteShader(geomShader); + } - // cleanup - glDeleteProgram(pid); - glDeleteShader(vertShader); - glDeleteShader(fragShader); - if (geomShader != INVALID_SHADER_ID) { - glDeleteShader(geomShader); + return nullptr; } - return INVALID_SHADER_ID; - } + if (m_binary && allowBinaryCache) { + saveToDisk(pid, sources.name); + } - if (m_binary && allowBinaryCache) { - saveToDisk(pid, sources.name); + // update the cache and return + m_shaders[sources.name] = std::make_shared<ogl::Shader>(pid); + return m_shaders[sources.name]; } - // update the cache and return - m_shaders[sources.name] = pid; - return pid; -} + void ShaderCache::cacheSave(GLuint pid, BinaryCache *cache) { + GLsizei length; + glGetProgramiv(pid, GL_PROGRAM_BINARY_LENGTH, &length); -void ShaderCache::cacheSave(GLuint pid, BinaryCache *cache) { - GLsizei length; - glGetProgramiv(pid, GL_PROGRAM_BINARY_LENGTH, &length); + cache->data = std::malloc(length); + cache->size = length; - cache->data = std::malloc(length); - cache->size = length; + glGetProgramBinary(pid, length, &cache->size, &cache->format, cache->data); + } - glGetProgramBinary(pid, length, &cache->size, &cache->format, cache->data); -} + bool ShaderCache::cacheLoad(GLuint pid, const BinaryCache *cache) { + if (!m_binary) { + return false; + } + glProgramBinary(pid, cache->format, cache->data, cache->size); -bool ShaderCache::cacheLoad(GLuint pid, const BinaryCache *cache) { - if (!m_binary) { - return false; + // check it loaded correctly + GLint status = GL_FALSE; + glGetProgramiv(pid, GL_LINK_STATUS, &status); + return status == GL_TRUE; } - glProgramBinary(pid, cache->format, cache->data, cache->size); - // check it loaded correctly - GLint status = GL_FALSE; - glGetProgramiv(pid, GL_LINK_STATUS, &status); - return status == GL_TRUE; -} + void ShaderCache::initFallbackPipelines() { + // canvas fallback pipeline + load({ + .name = ogl4::FALLBACK_CANVAS_PIPELINE, + .vertexSource = ogl4::FALLBACK_CANVAS_VERTEX_SHADER, + .fragmentSource = ogl4::FALLBACK_CANVAS_FRAGMENT_SHADER, + .geometrySource = "" + }, false); + } -void ShaderCache::initFallbackPipelines() { - // canvas fallback pipeline - load({ - .name = ogl4::FALLBACK_CANVAS_PIPELINE, - .vertexSource = ogl4::FALLBACK_CANVAS_VERTEX_SHADER, - .fragmentSource = ogl4::FALLBACK_CANVAS_FRAGMENT_SHADER, - .geometrySource = "" - }, false); } -template<> -bool fggl::data::fggl_deserialize(std::filesystem::path &data, fggl::gfx::BinaryCache *out) { - auto f = - #ifdef _MSC_VER - _wfopen(data.c_str(), L"r"); - #else - std::fopen(data.c_str(), "r"); - #endif + template<> + bool fggl::data::fggl_deserialize(std::filesystem::path &data, fggl::gfx::BinaryCache *out) { + auto f = + #ifdef _MSC_VER + _wfopen(data.c_str(), L"r"); + #else + std::fopen(data.c_str(), "r"); + #endif + + if (f == nullptr) { + spdlog::warn("could not load cached shader, fp was null"); + return false; + } - if (f == nullptr) { - spdlog::warn("could not load cached shader, fp was null"); - return false; - } + auto rsize = std::fread(&out->format, sizeof(GLenum), 1, f); + if (rsize != 1) { + spdlog::warn("could not load cached shader: type read failed"); + std::fclose(f); + return false; + } - auto rsize = std::fread(&out->format, sizeof(GLenum), 1, f); - if (rsize != 1) { - spdlog::warn("could not load cached shader: type read failed"); - std::fclose(f); - return false; - } + out->size = 0; + rsize = std::fread(&out->size, sizeof(GLsizei), 1, f); + if (rsize != 1) { + spdlog::warn("could not load cached shader: size read failed"); + std::fclose(f); + return false; + } - out->size = 0; - rsize = std::fread(&out->size, sizeof(GLsizei), 1, f); - if (rsize != 1) { - spdlog::warn("could not load cached shader: size read failed"); - std::fclose(f); - return false; - } + out->data = std::malloc(out->size); + auto readSize = std::fread(out->data, out->size, 1, f); - out->data = std::malloc(out->size); - auto readSize = std::fread(out->data, out->size, 1, f); + auto result = true; + if (readSize != 1) { + spdlog::warn("could not load cached shader: reading failed!"); + std::free(out->data); + result = false; + } - auto result = true; - if (readSize != 1) { - spdlog::warn("could not load cached shader: reading failed!"); - std::free(out->data); - result = false; + std::fclose(f); + return result; } - std::fclose(f); - return result; -} - #include <iostream> #include <fstream> diff --git a/fggl/gfx/ogl4/canvas.cpp b/fggl/gfx/ogl4/canvas.cpp index 750e884ce58989d115190736d862c0d70cf0a9da..ca3f430e6471f69ebfef037d4a4fd49b3dd06981 100644 --- a/fggl/gfx/ogl4/canvas.cpp +++ b/fggl/gfx/ogl4/canvas.cpp @@ -114,7 +114,7 @@ namespace fggl::gfx::ogl4 { glBindVertexArray(0); } - void CanvasRenderer::renderShapes(const gfx::Paint &paint, GLuint shader) { + void CanvasRenderer::renderShapes(const gfx::Paint &paint, ogl::Shader& shader) { data::Mesh2D mesh; convert_to_mesh(paint, mesh); @@ -132,12 +132,11 @@ namespace fggl::gfx::ogl4 { glDisable(GL_DEPTH_TEST); glDisable(GL_CULL_FACE); - // FIXME: this should be abstracted into the shader class - glUseProgram(shader); auto projMat = glm::ortho(m_bounds.left, m_bounds.right, m_bounds.bottom, m_bounds.top); - glUniformMatrix4fv(glGetUniformLocation(shader, "projection"), 1, GL_FALSE, - glm::value_ptr(projMat)); + shader.use(); + shader.setUniformMtx(shader.uniform("projection"), projMat); + // draw elements m_vao.drawElements(m_indexList, ogl::Primative::TRIANGLE, mesh.indexList.size()); // cleanup @@ -147,7 +146,7 @@ namespace fggl::gfx::ogl4 { } // slow version - void CanvasRenderer::renderText(const Paint &paint, GLuint shader) { + void CanvasRenderer::renderText(const Paint &paint, ogl::Shader& shader) { if (paint.textCmds().empty()) { return; } @@ -161,10 +160,9 @@ namespace fggl::gfx::ogl4 { } // setup the shader - glUseProgram(shader); + shader.use(); auto projMat = glm::ortho(0.0f, 1920.0f, 1080.0f, 0.f); - glUniformMatrix4fv(glGetUniformLocation(shader, "projection"), 1, GL_FALSE, - glm::value_ptr(projMat)); + shader.setUniformMtx(shader.uniform("projection"), projMat); // bind the vbo we'll use for writing m_vao.bind(); @@ -196,7 +194,7 @@ namespace fggl::gfx::ogl4 { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); m_fontTex.bind(1); - glUniform1i(glGetUniformLocation(shader, "tex"), 1); + shader.setUniformI(shader.uniform("tex"), 1); // this is why this is called the slow version, we render each quad as a single call auto &metrics = face->metrics(letter); @@ -277,7 +275,7 @@ namespace fggl::gfx::ogl4 { glUseProgram( 0 ); }*/ - void CanvasRenderer::render(GLuint shader, const gfx::Paint &paint) { + void CanvasRenderer::render(ogl::Shader& shader, const gfx::Paint &paint) { renderShapes(paint, shader); renderText(paint, shader); } diff --git a/fggl/gfx/ogl4/debug.cpp b/fggl/gfx/ogl4/debug.cpp index e84a000c96caac6c1927b11a9682d2385667d913..cf0d8cc444a5ccfe1c16d1872dd90222b64e3b57 100644 --- a/fggl/gfx/ogl4/debug.cpp +++ b/fggl/gfx/ogl4/debug.cpp @@ -21,13 +21,14 @@ #include "fggl/gfx/ogl4/debug.hpp" #include <cassert> +#include <utility> namespace fggl::gfx::ogl4 { - DebugRenderer::DebugRenderer(GLuint shader) : - mvpMatrix(1.0f), - m_lineShader(shader), - m_lineShaderMVP(m_lineShader.uniform("u_MvpMatrix")) { + DebugRenderer::DebugRenderer(std::shared_ptr<ogl::Shader> shader) : + mvpMatrix(1.0F), + m_lineShader(std::move(shader)), + m_lineShaderMVP(m_lineShader->uniform("u_MvpMatrix")) { // define our attributes auto posAttr = ogl::attribute<dd::DrawVertex, math::vec3>(0); auto colAttr = ogl::attribute<dd::DrawVertex, math::vec3>(sizeof(float) * 3); @@ -42,8 +43,8 @@ namespace fggl::gfx::ogl4 { assert(count > 0 && count <= DEBUG_DRAW_VERTEX_BUFFER_SIZE); m_lineVao.bind(); - m_lineShader.use(); - m_lineShader.setUniformMtx(m_lineShaderMVP, mvpMatrix); + m_lineShader->use(); + m_lineShader->setUniformMtx(m_lineShaderMVP, mvpMatrix); if (depthEnabled) { glEnable(GL_DEPTH_TEST); diff --git a/fggl/gfx/ogl4/models.cpp b/fggl/gfx/ogl4/models.cpp index 1095ac8ff2cb11923a4512998a14ce1debd0e2a8..3849fb77385ae8bf0750389b17fa4ac84316741c 100644 --- a/fggl/gfx/ogl4/models.cpp +++ b/fggl/gfx/ogl4/models.cpp @@ -27,7 +27,7 @@ namespace fggl::gfx::ogl4 { static std::shared_ptr<ogl::ArrayBuffer> setupArrayBuffer(std::shared_ptr<ogl::VertexArray> &vao, - std::vector<data::Vertex> &data) { + const std::vector<data::Vertex> &data) { auto buff = std::make_shared<ogl::ArrayBuffer>(); buff->write(data.size() * sizeof(data::Vertex), data.data(), ogl::BufUsage::STATIC_DRAW); @@ -43,7 +43,7 @@ namespace fggl::gfx::ogl4 { } static std::shared_ptr<ogl::ElementBuffer> setupIndexBuffer(std::shared_ptr<ogl::VertexArray> &vao, - std::vector<uint32_t> &data) { + const std::vector<uint32_t> &data) { auto elementBuffer = std::make_shared<ogl::ElementBuffer>(); elementBuffer->write(data.size() * sizeof(uint32_t), data.data(), ogl::BufUsage::STATIC_DRAW); @@ -64,53 +64,95 @@ namespace fggl::gfx::ogl4 { modelComp.drawType = ogl::Primative::TRIANGLE; } - void StaticModelRenderer::resolveModels(entity::EntityManager &world) { - // FIXME: this needs something reactive or performance will suck. - auto renderables = world.find<data::StaticMesh>(); - for (const auto &renderable : renderables) { - auto *currModel = world.tryGet<StaticModel>(renderable); - if (currModel != nullptr) { - continue; - } + StaticModel* StaticModelRenderer::uploadMesh(assets::AssetGUID guid, const data::Mesh &mesh) { + assert( m_assets != nullptr ); - auto &meshComp = world.get<data::StaticMesh>(renderable); - auto &modelComp = world.add<StaticModel>(renderable); + // if the asset has already been uploaded, we don't need to do anything + if ( m_assets->has(guid) ) { + m_assets->require(guid); + return m_assets->get<StaticModel>(guid); + } - auto shader = m_phong; - try { - shader = std::make_shared<ogl::Shader>(m_shaders->get(meshComp.pipeline)); - } catch (std::out_of_range &e) { - debug::log(debug::Level::warning, "Could not find shader: {}", meshComp.pipeline); - } + // 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->elementCount = mesh.indexCount(); + modelAsset->drawType = ogl::Primative::TRIANGLE; + m_assets->set(guid, modelAsset); + + return modelAsset; + } + + StaticModelGPU* StaticModelRenderer::uploadMesh2(const assets::AssetGUID& meshName, const data::Mesh &mesh) { + assert( m_assets != nullptr ); - setupComponent(modelComp, shader, meshComp.mesh); - debug::log(debug::Level::info, "Added static mesh to {}", (uint64_t) renderable); + if ( m_assets->has(meshName) ) { + // we've already uploaded it... + return m_assets->get<StaticModelGPU>(meshName); } - // terrain - auto terrain = world.find<data::HeightMap>(); - for (auto &renderable : terrain) { - auto currModel = world.tryGet<StaticModel>(renderable); - if (currModel != nullptr) { - continue; + 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->elementCount = mesh.indexCount(); + modelAsset->drawType = ogl::Primative::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 renderables = world.find<data::StaticMesh>(); + for (const auto &renderable : renderables) { + auto *currModel = world.tryGet<StaticModel>(renderable); + if (currModel != nullptr) { + continue; + } + + auto &meshComp = world.get<data::StaticMesh>(renderable); + + // model loading (should be via asset system) + auto loadedModel = uploadMesh("DEFER_ENT_"+ std::to_string((uint64_t)renderable), meshComp.mesh); + auto loadedShader = m_shaders->get(meshComp.pipeline); + + // splice the loaded asset into the ecs + auto& entityModel = world.add<StaticModel>(renderable); + entityModel = *loadedModel; + entityModel.pipeline = loadedShader; + + // 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 &heightmap = world.get<data::HeightMap>(renderable); - data::Mesh heightMapMesh{}; - data::generateHeightMesh(heightmap, heightMapMesh); + // terrain + auto terrain = world.find<data::HeightMap>(); + for (auto &renderable : terrain) { + auto currModel = world.tryGet<StaticModel>(renderable); + if (currModel != nullptr) { + continue; + } + + auto &heightmap = world.get<data::HeightMap>(renderable); + data::Mesh heightMapMesh{}; + data::generateHeightMesh(heightmap, heightMapMesh); - auto &modelComp = world.add<StaticModel>(renderable); - setupComponent(modelComp, m_phong, heightMapMesh); + auto &modelComp = world.add<StaticModel>(renderable); + setupComponent(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.restartIndex = heightMapMesh.restartVertex; + // 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.restartIndex = heightMapMesh.restartVertex; - // no active model, we need to resolve/load one. - debug::info("looks like {} needs a static mesh", (uint64_t)renderable); + // no active model, we need to resolve/load one. + debug::info("looks like {} needs a static mesh", (uint64_t)renderable); + } } - } + #endif void StaticModelRenderer::renderModelsForward(const entity::EntityManager &world) { @@ -144,7 +186,10 @@ namespace fggl::gfx::ogl4 { // 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) { @@ -157,7 +202,7 @@ namespace fggl::gfx::ogl4 { } // check if we switched shaders - if (shader != model.pipeline) { + if (shader == nullptr || shader->shaderID() != model.pipeline->shaderID()) { // new shader - need to re-send the view and projection matrices shader = model.pipeline; shader->use(); @@ -165,12 +210,14 @@ namespace fggl::gfx::ogl4 { 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(shader->uniform("MVPMatrix"), projectionMatrix * viewMatrix * transform.model()); - shader->setUniformMtx(shader->uniform("MVMatrix"), viewMatrix * transform.model()); + 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); diff --git a/fggl/gfx/ogl4/module.cpp b/fggl/gfx/ogl4/module.cpp index dc6767b2c4d6845e9e74f949958f65438a32048e..5c5289dea52994bfd12b8545ecc9ebc63f782d31 100644 --- a/fggl/gfx/ogl4/module.cpp +++ b/fggl/gfx/ogl4/module.cpp @@ -20,6 +20,7 @@ #include "fggl/gfx/phong.hpp" #include "fggl/data/procedural.hpp" +#include "fggl/assets/loader.hpp" #include <string> namespace fggl::gfx { @@ -52,28 +53,84 @@ namespace fggl::gfx { } } - void attach_mesh(const entity::ComponentSpec &spec, entity::EntityManager &manager, const entity::EntityID &id) { - auto &meshComp = manager.add<data::StaticMesh>(id); - meshComp.pipeline = spec.get<std::string>("pipeline", ""); + void attach_mesh(const entity::ComponentSpec &spec, entity::EntityManager &manager, const entity::EntityID &id, modules::Services &services) { + // check for the asset service + auto* assetService = services.get<assets::AssetManager>(); + auto* assetLoader = services.get<assets::Loader>(); + if ( assetService == nullptr || assetLoader == nullptr ) { + // no asset service, give up + return; + } + + // asset is a procedural mesh description if (spec.has("shape")) { - // procedural mesh - data::Mesh mesh; - if (spec.config["shape"].IsSequence()) { - for (const auto &node : spec.config["shape"]) { - process_shape(node, mesh); + 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", ""); + 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 (spec.config["shape"].IsSequence()) { + for (const auto &node : spec.config["shape"]) { + process_shape(node, *meshTmp); + } + } else { + process_shape(spec.config["shape"], *meshTmp); + } + meshTmp->removeDups(); + + if (!shapeName.empty()) { + meshAsset = assetService->set(shapeName, meshTmp); + } else { + meshAsset = meshTmp; } - } else { - process_shape(spec.config["shape"], mesh); } - mesh.removeDups(); - meshComp.mesh = mesh; + + // 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; + } + + // asset is a model from the resource cache + if ( spec.has("model") ) { + // figure out what model we want + auto assetStr = spec.get<std::string>("model", ""); + auto* asset = assetService->get<ogl4::StaticModel>(assetStr); + + if ( asset == nullptr ) { + // the asset is not loaded/does not exist + debug::error("requested model {} but it was not loaded.", assetStr); + return; + } + + // copy the asset to the model + auto& model = manager.add<ogl4::StaticModel>(id); + model = *asset; } } void attach_material(const entity::ComponentSpec &spec, entity::EntityManager &manager, - const entity::EntityID &id) { + const entity::EntityID &id, + 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); @@ -81,7 +138,7 @@ 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) { + void attach_light(const entity::ComponentSpec &spec, entity::EntityManager &manager, const entity::EntityID &id, modules::Services& services) { auto &light = manager.add<gfx::Light>(id); } @@ -90,7 +147,9 @@ namespace fggl::gfx { // setup the thing responsible for graphics auto *storage = services.get<data::Storage>(); auto *fontLibrary = services.get<gui::FontLibrary>(); - services.bind<WindowGraphics, ogl4::WindowGraphics>(storage, 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>(); diff --git a/fggl/gfx/ogl4/setup.cpp b/fggl/gfx/ogl4/setup.cpp index cf1a07e947018b38707bec3c250507a7bbd3d3bf..ba1a9483e44b16907367b2de833fbb3b547e9a14 100644 --- a/fggl/gfx/ogl4/setup.cpp +++ b/fggl/gfx/ogl4/setup.cpp @@ -21,7 +21,7 @@ namespace fggl::gfx::ogl4 { Graphics *WindowGraphics::create(display::Window &window) { - return new OpenGL4Backend(m_storage, m_fonts); + return new OpenGL4Backend(m_storage, m_fonts, m_assets); } } \ No newline at end of file diff --git a/fggl/gfx/window.cpp b/fggl/gfx/window.cpp index b0e86d1068e272936db6767cfa5346f7944a7ebb..a71a273f09b159882508a066eecff5b7e6a61866 100644 --- a/fggl/gfx/window.cpp +++ b/fggl/gfx/window.cpp @@ -99,7 +99,7 @@ namespace fggl::display::glfw { auto &gamepadCtl = input.gamepads(); for (int jid = 0; jid < GLFW_JOYSTICK_LAST; jid++) { - if (glfwJoystickPresent(jid)) { + if (glfwJoystickPresent(jid) == GLFW_TRUE) { fggl_update_joystick(gamepadCtl, jid); } else { gamepadCtl.setActive(jid, false); @@ -184,12 +184,16 @@ namespace fggl::display::glfw { // bind the graphics API glfwMakeContextCurrent(m_window); - m_graphics = std::unique_ptr<gfx::Graphics>(graphics->create(*this)); + m_graphics = graphics->createMain(*this); spdlog::debug("[glfw] window creation complete"); } Window::~Window() { + if ( m_graphics != nullptr ) { + m_graphics = nullptr; + } + if (m_window != nullptr) { // prevent dangling pointers glfwSetWindowUserPointer(m_window, nullptr); diff --git a/include/fggl/assets/loader.hpp b/include/fggl/assets/loader.hpp index a0cd15d39bf082924e6edba535849e512f169320..48fd85321afeb97edb14ac0931327292121255e3 100644 --- a/include/fggl/assets/loader.hpp +++ b/include/fggl/assets/loader.hpp @@ -81,7 +81,7 @@ namespace fggl::assets { // TODO we load the data into temp memory and give a pointer to it. debug::log(debug::Level::error, "Tried to load staged asset - no one wrote that yet!"); break; - case LoadType::PATH: config.first(guid, AssetData(&path)); + case LoadType::PATH: config.first(this, guid, AssetData(&path)); break; } } diff --git a/include/fggl/assets/manager.hpp b/include/fggl/assets/manager.hpp index 0872282bbde35064630b0a803a88299a1d36d567..fef3c4efdb5cd2e494d8ebde805e76466a4a81b2 100644 --- a/include/fggl/assets/manager.hpp +++ b/include/fggl/assets/manager.hpp @@ -29,10 +29,23 @@ namespace fggl::assets { + struct AssetBox { + virtual ~AssetBox() = default; + virtual void release() = 0; + }; + + template<typename T> + struct AssetBoxT : public AssetBox { + T* asset = nullptr; + + inline void release() override { + asset = nullptr; + } + }; + class AssetManager { public: constexpr const static modules::ModuleService service = modules::make_service("fggl::assets::Manager"); - using AssetGUID = std::string; AssetManager() = default; virtual ~AssetManager() = default; @@ -43,36 +56,40 @@ namespace fggl::assets { AssetManager(AssetManager &&) = delete; AssetManager &operator=(AssetManager &&) = delete; - inline AssetRefRaw getRaw(const AssetGUID &guid) const { - return m_registry.at(guid).asset; + template<typename T> + T* get(const AssetGUID &guid) const { + try { + const auto &assetRecord = m_registry.at(guid); + std::shared_ptr<AssetBoxT<T>> casted = std::dynamic_pointer_cast<AssetBoxT<T>>(assetRecord); + return casted->asset; + } catch (std::out_of_range& e) { + return nullptr; + } } template<typename T> - AssetRef<T> get(const AssetGUID &guid) const { - return std::dynamic_pointer_cast<T>(getRaw(guid)); + T* set(const AssetGUID &guid, T* assetRef) { + auto ptr = std::make_shared<AssetBoxT<T>>(); + ptr->asset = assetRef; + + m_registry[guid] = ptr; + return (*ptr).asset; } inline void require(const AssetGUID &guid) { - m_registry.at(guid).refCount++; + //m_registry.at(guid).refCount++; + } + + inline bool has(const AssetGUID &guid) { + return m_registry.contains(guid); } inline void release(const AssetGUID &guid) { - m_registry.at(guid).refCount--; + //m_registry.at(guid).refCount--; } private: - struct AssetRecord { - std::shared_ptr<Asset> asset = nullptr; - std::size_t refCount; - - inline ~AssetRecord() { - if (asset != nullptr) { - asset->release(); - } - } - }; - - std::map<AssetGUID, AssetRecord> m_registry; + std::map<AssetGUID, std::shared_ptr<AssetBox>> m_registry; }; } // namespace fggl::assets diff --git a/include/fggl/assets/types.hpp b/include/fggl/assets/types.hpp index f31cd1b037ba22ebe1a0096af17f1441a3db008f..7d84ddfc7f938555c2d87f6b2ae705a28dc259ce 100644 --- a/include/fggl/assets/types.hpp +++ b/include/fggl/assets/types.hpp @@ -41,13 +41,14 @@ namespace fggl::assets { std::size_t size; }; - using AssetRefRaw = std::shared_ptr<Asset>; + using AssetRefRaw = std::shared_ptr<void>; template<typename T> using AssetRef = std::shared_ptr<T>; + class Loader; using AssetData = std::variant<MemoryBlock, AssetPath *, FILE *>; - using Checkin = std::function<AssetRefRaw(const AssetGUID &, const AssetData &)>; + using Checkin = std::function<AssetRefRaw(Loader* loader, const AssetGUID &, const AssetData &)>; } #endif //FGGL_ASSETS_TYPES_HPP diff --git a/include/fggl/data/model.hpp b/include/fggl/data/model.hpp index 900b1e698440695f958c88e3b251a0f050e58c55..61cc4b582df4d58b1c79836435c9956da2d4c7a6 100644 --- a/include/fggl/data/model.hpp +++ b/include/fggl/data/model.hpp @@ -134,19 +134,19 @@ namespace fggl::data { */ IndexType indexOf(Vertex vert); - inline std::vector<Vertex> &vertexList() { + inline const std::vector<Vertex> &vertexList() const { return m_verts; } - inline std::size_t vertexCount() { + inline std::size_t vertexCount() const { return m_verts.size(); } - inline std::vector<IndexType> &indexList() { + inline const std::vector<IndexType> &indexList() const { return m_index; } - inline std::size_t indexCount() { + inline std::size_t indexCount() const { return m_index.size(); } diff --git a/include/fggl/display/window.hpp b/include/fggl/display/window.hpp index 194ffaf3ace43819247b5ef43877afedfce13feb..1d0f9f7272e3f37c8bdd7e71061cf7e875af87dd 100644 --- a/include/fggl/display/window.hpp +++ b/include/fggl/display/window.hpp @@ -21,6 +21,7 @@ #include "fggl/modules/module.hpp" #include "fggl/math/types.hpp" + #include "fggl/gfx/interfaces.hpp" namespace fggl::display { @@ -30,12 +31,6 @@ namespace fggl::display { virtual ~Window() = default; virtual void activate() const = 0; - template<typename T, typename ...Args> - void make_graphics(Args... args) { - activate(); - m_graphics = std::make_unique<T>(*this, args...); - } - // window-related getters [[nodiscard]] virtual math::vec2i frameSize() const = 0; @@ -59,7 +54,7 @@ namespace fggl::display { virtual bool isFullscreen() const = 0; protected: - std::unique_ptr<gfx::Graphics> m_graphics; + gfx::Graphics* m_graphics; }; class WindowService { diff --git a/include/fggl/entity/loader/loader.hpp b/include/fggl/entity/loader/loader.hpp index d7d0703d17b95541cbec332b7454f7ffaa4aa307..2ba077519b4175f8c63d20abe57e69547b460a50 100644 --- a/include/fggl/entity/loader/loader.hpp +++ b/include/fggl/entity/loader/loader.hpp @@ -32,7 +32,7 @@ namespace fggl::entity { constexpr auto PROTOTYPE_ASSET = assets::AssetType::make("entity_prototype"); - using FactoryFunc = std::function<void(const ComponentSpec &config, EntityManager &, const EntityID &)>; + using FactoryFunc = std::function<void(const ComponentSpec &config, EntityManager &, const EntityID &, modules::Services &svc)>; using CustomiseFunc = std::function<void(EntityManager &, const EntityID &)>; struct FactoryInfo { @@ -44,6 +44,8 @@ namespace fggl::entity { public: constexpr static const modules::ModuleService 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) { debug::warning("creating {}", spec); assert( m_prototypes.contains(spec) && "asked to create undefined prototype!" ); @@ -97,6 +99,7 @@ namespace fggl::entity { } private: + modules::Services m_services; std::map<ComponentID, FactoryInfo> m_factories; std::map<EntityType, EntitySpec> m_prototypes; @@ -125,7 +128,7 @@ namespace fggl::entity { loadedComps.push_back(component); auto &info = m_factories.at(component); - info.factory(data, manager, entity); + info.factory(data, manager, entity, m_services); if (info.finalise != nullptr) { finishers.push_back(info.finalise); @@ -156,7 +159,7 @@ namespace fggl::entity { } }; - assets::AssetRefRaw load_prototype(EntityFactory *factory, const assets::AssetGUID &guid, assets::AssetData data); + assets::AssetRefRaw load_prototype(assets::Loader* loader, EntityFactory *factory, const assets::AssetGUID &guid, assets::AssetData data); } // namespace fggl::entity diff --git a/include/fggl/gfx/ogl/renderer.hpp b/include/fggl/gfx/ogl/renderer.hpp index 3928154eb57adf25d5d3c67f21a95aa57e72e8c4..fd88e2dad3dd140796b233be440892d4aa1f3074 100644 --- a/include/fggl/gfx/ogl/renderer.hpp +++ b/include/fggl/gfx/ogl/renderer.hpp @@ -55,7 +55,7 @@ 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); ~OpenGL4Backend() override = default; // copy bad @@ -107,7 +107,7 @@ 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; + std::shared_ptr<ogl::Shader> m_canvasPipeline; data::Storage *m_storage; gui::FontLibrary *m_fontLibrary; }; diff --git a/include/fggl/gfx/ogl/shader.hpp b/include/fggl/gfx/ogl/shader.hpp index 3828f43d59e06a7080715951c15e29c43f940a2e..28d690f44a10dafa8d73b33d319a00f0eaabb7e5 100644 --- a/include/fggl/gfx/ogl/shader.hpp +++ b/include/fggl/gfx/ogl/shader.hpp @@ -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> @@ -63,15 +65,17 @@ namespace fggl::gfx { class ShaderCache { public: + 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. @@ -83,7 +87,7 @@ namespace fggl::gfx { private: fggl::data::Storage* m_storage; - std::unordered_map<std::string, GLuint> m_shaders; + std::unordered_map<std::string, ShaderPtr> m_shaders; // extensions void setupIncludes(); diff --git a/include/fggl/gfx/ogl/types.hpp b/include/fggl/gfx/ogl/types.hpp index 832cda43d569adaacf114ca0f43311dc4ad66a64..2d082d2040f4fe48f4f6283f0dc37ab8d291a1a5 100644 --- a/include/fggl/gfx/ogl/types.hpp +++ b/include/fggl/gfx/ogl/types.hpp @@ -68,11 +68,15 @@ namespace fggl::gfx::ogl { 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; + 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); diff --git a/include/fggl/gfx/ogl4/canvas.hpp b/include/fggl/gfx/ogl4/canvas.hpp index f546a0affcb85e239cfecbcd6f069a7b1d17bb68..e1fb2456125877230ce04ca90e2fab4c465b6980 100644 --- a/include/fggl/gfx/ogl4/canvas.hpp +++ b/include/fggl/gfx/ogl4/canvas.hpp @@ -30,7 +30,7 @@ namespace fggl::gfx::ogl4 { class CanvasRenderer { public: CanvasRenderer(gui::FontLibrary *fonts); - void render(GLuint shader, const gfx::Paint &paint); + void render(ogl::Shader& shader, const gfx::Paint &paint); inline gfx::Bounds bounds() const { return m_bounds; @@ -44,8 +44,8 @@ namespace fggl::gfx::ogl4 { 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 diff --git a/include/fggl/gfx/ogl4/debug.hpp b/include/fggl/gfx/ogl4/debug.hpp index b4b1f19fa5b4c1c24e2ae0161831d9447746c3df..feaf37891ebbad098f652ce31d3fdc3fb928d038 100644 --- a/include/fggl/gfx/ogl4/debug.hpp +++ b/include/fggl/gfx/ogl4/debug.hpp @@ -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; 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; diff --git a/include/fggl/gfx/ogl4/models.hpp b/include/fggl/gfx/ogl4/models.hpp index 9f3175a26c02b829990ca7ce9aa426394d3a5ace..1306735f8f54cbe29f0ab548a20871dba47ece30 100644 --- a/include/fggl/gfx/ogl4/models.hpp +++ b/include/fggl/gfx/ogl4/models.hpp @@ -26,10 +26,31 @@ #include "fggl/gfx/ogl/backend.hpp" #include "fggl/gfx/ogl/types.hpp" +#include "fggl/data/model.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::Primative drawType = ogl::Primative::TRIANGLE; + std::size_t restartIndex = 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"; @@ -47,9 +68,9 @@ namespace fggl::gfx::ogl4 { 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("redbook/debug")); + 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; @@ -60,22 +81,31 @@ namespace fggl::gfx::ogl4 { StaticModelRenderer &operator=(const StaticModelRenderer &other) = delete; StaticModelRenderer &operator=(StaticModelRenderer &&other) = delete; + StaticModel* uploadMesh(assets::AssetGUID guid, const data::Mesh& mesh); + StaticModelGPU* uploadMesh2(const assets::AssetGUID& meshName, const data::Mesh& mesh); + void render(entity::EntityManager &world) { - resolveModels(world); + #ifdef FGGL_ALLOW_DEFERRED_UPLOAD + resolveModels(world); + #endif renderModelsForward(world); } private: - /** - * Attach any missing rendering components to models. - */ - void resolveModels(entity::EntityManager &world); + + #ifdef FGGL_ALLOW_DEFERRED_UPLOAD + /** + * Attach any missing rendering components to models. + */ + void resolveModels(entity::EntityManager &world); + #endif /** * Render all visible objects according to their render tokens. */ void renderModelsForward(const entity::EntityManager &world); + assets::AssetManager *m_assets; gfx::ShaderCache *m_shaders; std::shared_ptr<ogl::Shader> m_phong; ogl::VertexArray m_vao; diff --git a/include/fggl/gfx/ogl4/module.hpp b/include/fggl/gfx/ogl4/module.hpp index 47d1dfacf36f82704eafeadf13b4d1babfc4293c..9de8e7903a67d16c18e6ae05fbc051717c5140c0 100644 --- a/include/fggl/gfx/ogl4/module.hpp +++ b/include/fggl/gfx/ogl4/module.hpp @@ -20,7 +20,10 @@ #define FGGL_GFX_OGL4_MODULE_HPP #include <array> + #include "fggl/modules/module.hpp" + +#include "fggl/assets/manager.hpp" #include "fggl/entity/loader/loader.hpp" #include "fggl/gfx/interfaces.hpp" @@ -36,8 +39,9 @@ namespace fggl::gfx { constexpr static const std::array<modules::ModuleService, 1> provides = { WindowGraphics::service }; - constexpr static const std::array<modules::ModuleService, 3> depends = { + constexpr static const std::array<modules::ModuleService, 4> depends = { data::Storage::service, + assets::AssetManager::service, gui::FontLibrary::service, entity::EntityFactory::service }; diff --git a/include/fggl/gfx/ogl4/setup.hpp b/include/fggl/gfx/ogl4/setup.hpp index d6ab931b0c9b29ae76d490bca3b35daa07211580..30d26aaa4ae1231ef4bed277014a0f0406a205ef 100644 --- a/include/fggl/gfx/ogl4/setup.hpp +++ b/include/fggl/gfx/ogl4/setup.hpp @@ -24,28 +24,29 @@ 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) {}; + WindowGraphics(data::Storage *storage, gui::FontLibrary *fonts, assets::AssetManager* assets) : m_storage(storage), m_fonts(fonts), m_assets(assets) {}; virtual ~WindowGraphics() = default; 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; + assets::AssetManager *m_assets; }; } // namespace fggl::gfx::ogl4 diff --git a/include/fggl/gfx/setup.hpp b/include/fggl/gfx/setup.hpp index 908d02dea468b7456afe08fc2301208c80b79130..9f1a65c3e618029f323d66226829f67b1935951a 100644 --- a/include/fggl/gfx/setup.hpp +++ b/include/fggl/gfx/setup.hpp @@ -39,8 +39,42 @@ namespace fggl::gfx { public: constexpr const static modules::ModuleService service = modules::make_service("fggl::gfx::WindowGraphics"); + WindowGraphics() = default; + virtual ~WindowGraphics() = default; + + // no copy + WindowGraphics(const WindowGraphics& ) = delete; + WindowGraphics& operator=(const WindowGraphics&) = delete; + + // no move + WindowGraphics(WindowGraphics&&) = delete; + WindowGraphics& operator=(WindowGraphics&&) = delete; + + [[nodiscard]] virtual GraphicsDetails config() const = 0; - virtual Graphics *create(display::Window &window) = 0; + + [[nodiscard]] + virtual Graphics* create(display::Window &window) = 0; + + inline Graphics* createMain(display::Window& window) { + assert( m_graphics == nullptr ); + m_graphics = create(window); + return m_graphics; + } + + inline void release() { + if ( m_graphics != nullptr ) { + delete m_graphics; + m_graphics = nullptr; + } + } + + [[nodiscard]] inline Graphics* get() const { + return m_graphics; + } + + private: + Graphics* m_graphics = nullptr; }; } // namespace fggl::gfx diff --git a/include/fggl/phys/null.hpp b/include/fggl/phys/null.hpp index 79c296eae27e6e57febdd92756b96907475342d6..276e22fe07e34d57c4a4a9224c44471bc85c5cb9 100644 --- a/include/fggl/phys/null.hpp +++ b/include/fggl/phys/null.hpp @@ -25,8 +25,9 @@ namespace fggl::phys { inline void build_noop(const entity::ComponentSpec & /*config*/, - entity::EntityManager & manager, - const entity::EntityID & entity) { + entity::EntityManager &manager, + const entity::EntityID &entity, + modules::Services &/*services*/) { manager.add<fggl::phys::RigidBody>(entity); manager.add<fggl::phys::Dynamics>(entity); }