diff --git a/demo/demo/GameScene.cpp b/demo/demo/GameScene.cpp index 1bb95b57c96dcfe6eb903038ba5bb334ce32e08a..0ce3b09be5e333100302f67340b7f199cd516c07 100644 --- a/demo/demo/GameScene.cpp +++ b/demo/demo/GameScene.cpp @@ -28,6 +28,10 @@ camera_type cam_mode = cam_free; static void placeObject(fggl::entity::EntityManager& world, fggl::entity::EntityID floor, fggl::entity::EntityFactory* factory, fggl::util::GUID prototype, glm::vec3 targetPos) { + #ifndef NDEBUG + fggl::debug::trace("Creating object from prototype: {}", fggl::util::guidToString(prototype)); + #endif + auto obj = factory->create(prototype, world); auto& result = world.get<fggl::math::Transform>(obj); @@ -104,13 +108,13 @@ static fggl::entity::EntityID setupTerrain(fggl::entity::EntityManager& world) { terrainData.setValue(x, z, (float)noise); } } + world.add<fggl::data::HeightMap>(terrain, terrainData); - // world.set<fggl::data::HeightMap>(terrain, &terrainData); } return terrain; } -static fggl::entity::EntityID setupEnvironment(fggl::entity::EntityManager& world) { +static fggl::entity::EntityID setup_environment(fggl::entity::EntityManager& world) { setupCamera(world); return setupTerrain(world); } @@ -120,14 +124,10 @@ static auto BUNKER_PROTOTYPE = "bunker"_fid; static void setupBunkerPrototype(fggl::entity::EntityFactory* factory) { { auto bunkerSpec = fggl::entity::EntitySpec{}; - bunkerSpec.components[fggl::util::make_guid("transform")] = {}; - - fggl::entity::ComponentSpec procMesh{}; - procMesh.set<std::string>("shader", "phong"); + bunkerSpec.addComp(fggl::math::Transform::guid, {}); // mesh int nSections = 2; - fggl::data::Mesh mesh; for (int j=-(nSections/2); j<=nSections/2; j++) { const auto shapeOffset = glm::vec3( 0.0f, 0.5f, (float)j * 1.0f ); @@ -146,9 +146,31 @@ static void setupBunkerPrototype(fggl::entity::EntityFactory* factory) { } mesh.removeDups(); - procMesh.set<std::vector<fggl::data::Vertex>>("vertexData", mesh.vertexList()); - procMesh.set<std::vector<unsigned int>>("indexData", mesh.indexList()); - bunkerSpec.components[fggl::util::make_guid("procedural_mesh")] = procMesh; + // generate mesh component data + // FIXME: find a better way to do this, avoid re-uploading the whole mesh. + fggl::entity::ComponentSpec procMesh{}; + procMesh.set<std::string>("pipeline", "redbook/debug"); + + YAML::Node vertexData; + for (auto& vertex : mesh.vertexList()) { + YAML::Node vertexNode; + vertexNode["position"] = vertex.posititon; + vertexNode["normal"] = vertex.normal; + vertexNode["colour"] = vertex.colour; + vertexNode["texPos"] = vertex.texPos; + vertexData.push_back(vertexNode); + } + + YAML::Node indexData; + for (auto& index : mesh.indexList()) { + indexData.push_back(index); + } + + YAML::Node meshData; + meshData["vertex"] = vertexData; + meshData["index"] = indexData; + procMesh.set("mesh", meshData); + bunkerSpec.addComp(fggl::data::StaticMesh::guid, procMesh); factory->define(BUNKER_PROTOTYPE, bunkerSpec); } @@ -159,7 +181,7 @@ void GameScene::setup() { auto* entityFactory = m_owner.service<fggl::entity::EntityFactory>(); - auto terrain = setupEnvironment(world()); + auto terrain = setup_environment(world()); setupBunkerPrototype(entityFactory); // create building prototype diff --git a/fggl/debug/CMakeLists.txt b/fggl/debug/CMakeLists.txt index d2d4e1ce2dce81e01e0539aa91045b3782dec460..bd85e56f53c1773349991947a93d93d04820cc6e 100644 --- a/fggl/debug/CMakeLists.txt +++ b/fggl/debug/CMakeLists.txt @@ -2,6 +2,7 @@ target_sources(fggl PRIVATE debug.cpp debug_draw.cpp + logging.cpp ) # spdlog for cleaner logging diff --git a/fggl/debug/logging.cpp b/fggl/debug/logging.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3c726c54c7256cb862e8cf7aa9cea9bcfdbed230 --- /dev/null +++ b/fggl/debug/logging.cpp @@ -0,0 +1,43 @@ +/* + * 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/09/22. +// + + +#ifdef __GNUG__ + #include <cstdlib> + #include <memory> + #include <cxxabi.h> + + namespace fggl::debug { + std::string demangle(const char *name) { + int status = -4; + + std::unique_ptr<char, decltype(&std::free)> res{ + abi::__cxa_demangle(name, nullptr, nullptr, &status), + std::free + }; + + return (status == 0) ? res.get() : name; + } + } +#else + namespace fggl::debug { + std::string demangle(const char* name) { + return name; + } + } +#endif diff --git a/fggl/gfx/ogl4/models.cpp b/fggl/gfx/ogl4/models.cpp index ed6e430698d371b9dbe5373962365b19d5b0cb9a..1095ac8ff2cb11923a4512998a14ce1debd0e2a8 100644 --- a/fggl/gfx/ogl4/models.cpp +++ b/fggl/gfx/ogl4/models.cpp @@ -108,7 +108,7 @@ namespace fggl::gfx::ogl4 { modelComp.restartIndex = heightMapMesh.restartVertex; // no active model, we need to resolve/load one. - //spdlog::info("looks like heightmap, {} needs a static mesh", renderable); + debug::info("looks like {} needs a static mesh", (uint64_t)renderable); } } @@ -152,7 +152,7 @@ namespace fggl::gfx::ogl4 { // ensure that the model pipeline actually exists... const auto &model = world.get<StaticModel>(entity); if (model.pipeline == nullptr) { - spdlog::warn("shader was null, aborting render"); + debug::warning("shader was null, aborting render"); continue; } @@ -211,14 +211,17 @@ namespace fggl::gfx::ogl4 { } // material detection with fallback - const auto &material = world.get<PhongMaterial>(entity); - - 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); + 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")) { diff --git a/include/fggl/app.hpp b/include/fggl/app.hpp index 98eab804e7aa6b84f2ecb45d882684ab1673339c..a6c6a90dd3be9991952eb4c9e6779575a0bc1ada 100644 --- a/include/fggl/app.hpp +++ b/include/fggl/app.hpp @@ -123,7 +123,7 @@ namespace fggl { try { return m_subsystems->template get<T>(); } catch (std::out_of_range &e) { - debug::log(debug::Level::error, "Service not found: {}", T::service); + debug::log(debug::Level::error, "Service not found: {}", T::service.get()); return nullptr; } } diff --git a/include/fggl/debug/impl/logging_fmt.hpp b/include/fggl/debug/impl/logging_fmt.hpp index 1c0fddbd36b5f252a7a0c2113c31b8ea34eb190c..71374b479a4cd8ef022a6e38f5725c6a4927efdd 100644 --- a/include/fggl/debug/impl/logging_fmt.hpp +++ b/include/fggl/debug/impl/logging_fmt.hpp @@ -20,6 +20,8 @@ #define FGGL_DEBUG_IMPL_LOGGING_FMT_HPP #include <fmt/format.h> +#include <fmt/color.h> + #include <iostream> #include <string> #include <string_view> @@ -78,13 +80,13 @@ namespace fggl::debug { template<typename ...T> inline void error(FmtType fmt, T &&...args) { auto fmtStr = fmt::format(fmt::runtime(fmt), args...); - fmt::print(CERR_FMT, level_to_string(Level::error), fmtStr); + fmt::print(fg(fmt::color::red), CERR_FMT, level_to_string(Level::error), fmtStr); } template<typename ...T> inline void warning(FmtType fmt, T &&...args) { auto fmtStr = fmt::format(fmt::runtime(fmt), args...); - fmt::print(CERR_FMT, level_to_string(Level::warning), fmtStr); + fmt::print(fg(fmt::color::orange), CERR_FMT, level_to_string(Level::warning), fmtStr); } template<typename ...T> @@ -101,8 +103,10 @@ namespace fggl::debug { template<typename ...T> inline void trace(FmtType fmt, T &&...args) { - auto fmtStr = fmt::format(fmt::runtime(fmt), args...); - fmt::print(CERR_FMT, level_to_string(Level::trace), fmtStr); + #ifdef FGGL_LOG_TRACE + auto fmtStr = fmt::format(fmt::runtime(fmt), args...); + fmt::print(CERR_FMT, level_to_string(Level::trace), fmtStr); + #endif } } diff --git a/include/fggl/debug/logging.hpp b/include/fggl/debug/logging.hpp index ef4955fc2ed8e70f7eac0b48cbcdb2ee5de0435c..f3ab6bfd565e8bc3fe246f780b111c808c7d0e47 100644 --- a/include/fggl/debug/logging.hpp +++ b/include/fggl/debug/logging.hpp @@ -21,8 +21,12 @@ #include <string_view> + + namespace fggl::debug { + std::string demangle(const char* name); + using FmtType = const std::string_view; enum class Level; diff --git a/include/fggl/entity/entity.hpp b/include/fggl/entity/entity.hpp index b4a3d17940b8654622232148f06634952eab5abf..0843299fc79be9dcbf7ba16c72bedebf7b9a6f60 100644 --- a/include/fggl/entity/entity.hpp +++ b/include/fggl/entity/entity.hpp @@ -48,7 +48,8 @@ namespace fggl::entity { Component &get(EntityID entity) { #ifndef NDEBUG if (!has<Component>(entity)) { - debug::error("Entity {} has no component of type {}", (uint64_t) entity, typeid(Component).name()); + debug::error("Entity {} has no component of type {}", (uint64_t) entity, debug::demangle(typeid(Component).name())); + assert(false && "Entity was missing component - use tryGet or fix definition"); } #endif return m_registry.get<Component>(entity); diff --git a/include/fggl/entity/loader/loader.hpp b/include/fggl/entity/loader/loader.hpp index cdcc10f50ac8af633af64055c30ec74dcdceb61e..d7d0703d17b95541cbec332b7454f7ffaa4aa307 100644 --- a/include/fggl/entity/loader/loader.hpp +++ b/include/fggl/entity/loader/loader.hpp @@ -45,13 +45,16 @@ namespace fggl::entity { constexpr static const modules::ModuleService service = modules::make_service("fggl::entity:Factory"); 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!" ); + try { std::vector<CustomiseFunc> finishers; // set up the components for the entity auto entity = setupComponents(spec, manager, finishers); if (entity == entity::INVALID) { - debug::error("Error attempting to create entity with type {}", std::to_string(spec.get())); + debug::error("EntityFactory: failed to build from prototype {}", std::to_string(spec.get())); return entity::INVALID; } @@ -67,15 +70,18 @@ namespace fggl::entity { return entity; } catch (std::out_of_range &ex) { - #ifndef NDEBUG - debug::log(debug::Level::error, - "EntityFactory: Unknown entity type '{}'", - fggl::util::guidToString(spec)); - #endif + debug::error("EntityFactory: Unknown entity type '{}'", spec); return fggl::entity::INVALID; } } + void log_known_types() const { + debug::debug("dumping known types:"); + for(auto& [k,v] : m_factories) { + debug::debug("\ttype: {}", k); + } + } + void define(EntityType type, const EntitySpec &spec) { m_prototypes[type] = spec; } @@ -97,12 +103,16 @@ namespace fggl::entity { entity::EntityID setupComponents(EntityType entityType, EntityManager &manager, std::vector<CustomiseFunc> &finishers) { + assert(entityType != NO_PARENT && "setup components called with NO_PARENT?!"); + auto entity = manager.create(); std::vector<ComponentID> loadedComps; auto currentType = entityType; while (currentType != NO_PARENT) { auto &entitySpec = m_prototypes.at(currentType); + debug::debug("constructing {} for {} ({} comps)", currentType, entityType, entitySpec.components.size()); + assert( entitySpec.ordering.size() == entitySpec.components.size() && "ordering incorrect size, bad things happend!" ); for (auto &component : entitySpec.ordering) { // skip comps loaded by children @@ -121,11 +131,8 @@ namespace fggl::entity { finishers.push_back(info.finalise); } } catch (std::out_of_range &ex) { - #ifndef NDEBUG - debug::log(debug::Level::error, - "EntityFactory: Unknown component factory type '{}'", - fggl::util::guidToString(component)); - #endif + debug::error( "EntityFactory: Unknown component factory type '{}'", component ); + log_known_types(); manager.destroy(entity); return entity::INVALID; } diff --git a/include/fggl/entity/loader/spec.hpp b/include/fggl/entity/loader/spec.hpp index e78ccfeac3785ab187b96af5d8f45f3f3df9997c..749fa4cc2585ea3ad5e676613dc14f455912098c 100644 --- a/include/fggl/entity/loader/spec.hpp +++ b/include/fggl/entity/loader/spec.hpp @@ -53,6 +53,11 @@ namespace fggl::entity { EntityType parent = NO_PARENT; std::vector<ComponentID> ordering; std::map<ComponentID, ComponentSpec> components; + + inline void addComp(ComponentID cmp, const ComponentSpec& spec) { + components[cmp] = spec; + ordering.push_back(cmp); + } }; } // namespace fggl::entity diff --git a/include/fggl/gfx/ogl4/models.hpp b/include/fggl/gfx/ogl4/models.hpp index ec12ed03746c526df8a903fa640756c069c888b7..9f3175a26c02b829990ca7ce9aa426394d3a5ace 100644 --- a/include/fggl/gfx/ogl4/models.hpp +++ b/include/fggl/gfx/ogl4/models.hpp @@ -49,7 +49,7 @@ namespace fggl::gfx::ogl4 { public: inline StaticModelRenderer(gfx::ShaderCache *cache) : m_shaders(cache), m_phong(nullptr), m_vao(), m_vertexList(), m_indexList() { - m_phong = std::make_shared<ogl::Shader>(cache->get("phong")); + m_phong = std::make_shared<ogl::Shader>(cache->get("redbook/debug")); } ~StaticModelRenderer() = default; diff --git a/include/fggl/modules/manager.hpp b/include/fggl/modules/manager.hpp index 791d2f003dcd813197e4e2b83261827b0b8883a8..deb5f14a418d766437781137b42471a1227e8948 100644 --- a/include/fggl/modules/manager.hpp +++ b/include/fggl/modules/manager.hpp @@ -185,7 +185,7 @@ namespace fggl::modules { debug::log(debug::Level::warning, "{} depends on {}, but nothing provides it", moduleItr.first, - service); + service.get()); // nothing can provide the service requested, setup is invalid. return false; } @@ -222,7 +222,7 @@ namespace fggl::modules { debug::log(debug::Level::warning, "{} could not create service {}", nextToInit, - service); + service.get()); } } } else { diff --git a/include/fggl/util/guid.hpp b/include/fggl/util/guid.hpp index 99902435ef2390f2a44cb5032c3777ab9149fc1a..2b6556058c44e592518bd5d45a96fa317c9ce20a 100644 --- a/include/fggl/util/guid.hpp +++ b/include/fggl/util/guid.hpp @@ -24,6 +24,7 @@ #include <cassert> #include "fggl/util/safety.hpp" +#include "fggl/debug/logging.hpp" namespace fggl::util { @@ -73,7 +74,7 @@ namespace fggl::util { #endif constexpr GUID make_guid(const char *str) { - return GUID::make(hash_fnv1a_64(str)); + return GUID(hash_fnv1a_64(str)); } inline GUID make_guid_rt(const std::string &str) { @@ -86,6 +87,24 @@ namespace fggl::util { } // namespace fggl::util +// formatter +template<> struct fmt::formatter<fggl::util::GUID> { + constexpr auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) { + auto it = ctx.begin(), end = ctx.end(); + return it; + } + + template <typename FormatContext> + auto format(const fggl::util::GUID& guid, FormatContext& ctx) const -> decltype(ctx.out()) { + #ifndef NDEBUG + return fmt::format_to(ctx.out(), "GUID[{}]", guidToString(guid)); + #else + return fmt::format_to(ctx.out(), "GUID[{}]", guid.get()); + #endif + } +}; + + fggl::util::GUID operator "" _fid(const char *str); fggl::util::GUID operator "" _fid(const char *str, std::size_t); diff --git a/include/fggl/util/safety.hpp b/include/fggl/util/safety.hpp index cd5719aff6ddbd426483d35fbc84118e264314f0..55cfa036a2f4b577fe07cf38da45bad01eac5fa8 100644 --- a/include/fggl/util/safety.hpp +++ b/include/fggl/util/safety.hpp @@ -47,8 +47,6 @@ namespace fggl::util { return m_value; } - constexpr explicit operator std::string_view() const { return m_value; } - /** * Check for equality of two handles. *