diff --git a/CMakeLists.txt b/CMakeLists.txt index 3b1a019..431f024 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.25) -set(BLT_GRAPHICS_VERSION 0.13.30) +set(BLT_GRAPHICS_VERSION 0.13.31) set(BLT_GRAPHICS_TEST_VERSION 0.0.1) project(BLT_WITH_GRAPHICS VERSION ${BLT_GRAPHICS_VERSION}) @@ -30,7 +30,7 @@ else () endif () if (NOT TARGET BLT) -add_subdirectory(libraries/BLT) + add_subdirectory(libraries/BLT) endif () add_subdirectory(libraries/freetype-2.13.2) diff --git a/include/blt/gfx/renderer/resource_manager.h b/include/blt/gfx/renderer/resource_manager.h index 5d5453c..ab041cd 100644 --- a/include/blt/gfx/renderer/resource_manager.h +++ b/include/blt/gfx/renderer/resource_manager.h @@ -20,40 +20,122 @@ #define BLT_WITH_GRAPHICS_RESOURCE_MANAGER_H #include +#include +#include #include #include #include #include #include #include "blt/compatibility.h" +#include "blt/std/ranges.h" namespace blt::gfx { - - struct loadable_texture + struct texture_info { - std::string path; - std::string name; - - loadable_texture() = default; - - loadable_texture(std::string path, std::string name): path(std::move(path)), name(std::move(name)) - {} + public: + texture_info() = default; + + explicit texture_info(std::string path, std::string name): path(std::move(path)), name(std::move(name)) + {} + + BLT_MAKE_GETTER(std::string, path); + + BLT_MAKE_GETTER(std::string, name); + + BLT_MAKE_GETTER_AND_SETTER(blt::i32, desired_width); + + BLT_MAKE_GETTER_AND_SETTER(blt::i32, desired_height); + private: + std::string path; + std::string name; + blt::i32 desired_width = 0; + blt::i32 desired_height = 0; + }; + + class texture_array + { + public: + texture_array(std::string name, i32 width, i32 height): name(std::move(name)), width(width), height(height) + {} + + void add_texture(std::string path, std::string t_name = "") + { + textures.push_back(texture_info{std::move(path), std::move(t_name)}.set_desired_width(width).set_desired_height(height)); + } + + BLT_MAKE_GETTER(std::vector, textures); + + BLT_MAKE_GETTER(std::string, name); + private: + std::vector textures; + std::string name; + i32 width; + i32 height; }; class resource_manager { private: + struct texture_destination + { + std::vector* vec = nullptr; + std::optional order = {}; + + texture_destination() = default; + + explicit texture_destination(std::vector* vec): vec(vec) + {} + + explicit texture_destination(std::vector* vec, blt::size_t order): vec(vec), order(order) + {} + + void add(texture_file* file) const + { + if (order) + vec->insert(vec->begin() + static_cast(*order), file); + else + vec->push_back(file); + } + }; + std::mutex load_lock; std::mutex save_lock; - std::vector textures_to_load; + std::vector> textures_to_load; std::vector loaded_textures; + std::vector>> loaded_arrays; blt::hashmap_t textures_2d; + blt::hashmap_t texture_arrays_2d; std::string resource_prefix; + + void load_to_gl(); public: resource_manager() = default; - void enqueue(std::string path, std::string name = ""); + explicit resource_manager(std::string_view resource_prefix): resource_prefix(resource_prefix) + { + if (!blt::string::ends_with(this->resource_prefix, "/")) + this->resource_prefix += '/'; + } + + void enqueue(std::string path, std::string name = "") + { + textures_to_load.emplace_back(texture_destination{&loaded_textures}, texture_info{std::move(path), std::move(name)}); + } + + void with(texture_info info) + { + textures_to_load.emplace_back(texture_destination{&loaded_textures}, std::move(info)); + } + + void with(texture_array array) + { + loaded_arrays.emplace_back(array.get_name(), std::vector{}); + auto& dest = loaded_arrays.back(); + for (const auto& [index, texture] : blt::enumerate(array.get_textures())) + textures_to_load.emplace_back(texture_destination{&dest.second, index}, std::move(texture)); + } void load_resources(std::size_t threads = 8); @@ -78,6 +160,8 @@ namespace blt::gfx */ inline void setPrefixDirectory(std::string path) { + if (!blt::string::ends_with(resource_prefix, "/")) + resource_prefix += '/'; resource_prefix = std::move(path); } diff --git a/libraries/BLT b/libraries/BLT index 0869509..e3c925e 160000 --- a/libraries/BLT +++ b/libraries/BLT @@ -1 +1 @@ -Subproject commit 0869509c6a5fd530efbd57469d2b99b89c22b769 +Subproject commit e3c925ed1186aa6924527b08671293b98be784c7 diff --git a/src/blt/gfx/renderer/camera.cpp b/src/blt/gfx/renderer/camera.cpp index 7c295a2..d8cc32e 100644 --- a/src/blt/gfx/renderer/camera.cpp +++ b/src/blt/gfx/renderer/camera.cpp @@ -100,12 +100,16 @@ void blt::gfx::first_person_camera::update() void blt::gfx::first_person_camera::update_view(blt::gfx::matrix_state_manager& manager) { + // setup perspective matrix blt::mat4x4 view; view.rotateX(toRadians(rotation_.x())); view.rotateY(toRadians(rotation_.y())); view.rotateZ(toRadians(rotation_.z())); view.translate(-position_); manager.setView(view); + // setup ortho-view matrix + blt::mat4x4 view2d; + manager.setView2D(view2d, 1); } float easeInOutCubic(float x) diff --git a/src/blt/gfx/renderer/resource_manager.cpp b/src/blt/gfx/renderer/resource_manager.cpp index eae706d..b44da4c 100644 --- a/src/blt/gfx/renderer/resource_manager.cpp +++ b/src/blt/gfx/renderer/resource_manager.cpp @@ -29,38 +29,36 @@ namespace blt::gfx delete p.second; } - void resource_manager::enqueue(std::string path, std::string name) - { - textures_to_load.emplace_back(resource_prefix + std::move(path), std::move(name)); - } - void resource_manager::load_resources(std::size_t threads) { #ifndef __EMSCRIPTEN__ blt::thread_pool pool{threads, [&]() { - loadable_texture texture; + texture_info texture; + texture_destination destination; { std::scoped_lock lock(load_lock); if (textures_to_load.empty()) return; - texture = textures_to_load.back(); + destination = textures_to_load.back().first; + texture = std::move(textures_to_load.back().second); textures_to_load.pop_back(); } - BLT_DEBUG("Loading texture file %s", texture.path.c_str()); - if (!std::filesystem::exists(texture.path)) + auto path = resource_prefix + texture.get_path(); + BLT_DEBUG("Loading texture file %s", path.c_str()); + if (!std::filesystem::exists(path)) { - BLT_WARN("Texture '%s' does not exist on disk!", texture.path.c_str()); + BLT_WARN("Texture '%s' does not exist on disk!", path.c_str()); return; } - auto* file = new texture_file(texture.path, texture.name); + auto* file = new texture_file(path, texture.get_name()); + file->resize(texture.get_desired_width(), texture.get_desired_height()); { std::scoped_lock lock(save_lock); - loaded_textures.push_back(file); + destination.add(file); } }}; do { - std::this_thread::sleep_for(std::chrono::milliseconds(100)); { std::scoped_lock lock(load_lock); if (textures_to_load.empty()) @@ -68,15 +66,9 @@ namespace blt::gfx } { std::scoped_lock lock(save_lock); - while (!loaded_textures.empty()) - { - auto* back = loaded_textures.back(); - textures_2d.insert({back->getName(), new texture_gl2D(back->texture())}); - BLT_DEBUG("Loaded Texture '%s'", back->getName().c_str()); - delete back; - loaded_textures.pop_back(); - } + load_to_gl(); } + std::this_thread::sleep_for(std::chrono::milliseconds(100)); } while (true); #else for (const auto& texture : textures_to_load) @@ -91,6 +83,11 @@ namespace blt::gfx pool.stop(); while (!pool.complete()); #endif + load_to_gl(); + } + + void resource_manager::load_to_gl() + { while (!loaded_textures.empty()) { auto* back = loaded_textures.back(); @@ -99,5 +96,19 @@ namespace blt::gfx delete back; loaded_textures.pop_back(); } + while (!loaded_arrays.empty()) + { + auto& back = loaded_arrays.back(); + auto& reference_v = back.second.back(); + // textures in an array are all the same size. we don't need to store the array width/height on this assumption + auto gl = new gl_texture2D_array(reference_v->width(), reference_v->height(), static_cast(back.second.size())); + for (auto [index, texture] : blt::enumerate(back.second)) + { + gl->upload(texture->texture().data(), static_cast(index)); + delete texture; + } + texture_arrays_2d.insert({back.first.c_str(), gl}); + loaded_arrays.pop_back(); + BLT_DEBUG("Loaded texture array '%s' of size %ld", back.first.c_str(), back.second.size()); + } } -} diff --git a/src/blt/gfx/texture.cpp b/src/blt/gfx/texture.cpp index de0da55..a14f663 100644 --- a/src/blt/gfx/texture.cpp +++ b/src/blt/gfx/texture.cpp @@ -114,21 +114,24 @@ blt::gfx::texture_file::texture_file(const std::string& path, const std::string& stbi_set_flip_vertically_on_load(true); m_texture.m_data = stbi_load( m_path.c_str(), - reinterpret_cast(&m_texture.m_width), reinterpret_cast(&m_texture.m_height), - reinterpret_cast(&m_texture.m_channels), - 0 - ); + &m_texture.m_width, &m_texture.m_height, + &m_texture.m_channels, + 0); } blt::gfx::texture_file& blt::gfx::texture_file::resize(int target_width, int target_height) { + if (target_width == 0) + target_width = m_texture.width(); + if (target_height == 0) + target_height = m_texture.height(); if (target_width == m_texture.width() && target_height == m_texture.height()) return *this; // since we will be replacing the loaded data pointer, is it wise to use the allocator // that matches with what stb image uses, which is malloc, since we unload with stbi_image_free -> (free) auto* output_Data = (unsigned char*) malloc( target_width * target_height * m_texture.channels() - ); + ); // resize the texture if (stbir_resize_uint8_linear( @@ -138,7 +141,7 @@ blt::gfx::texture_file& blt::gfx::texture_file::resize(int target_width, int tar output_Data, target_width, target_height, 0, // channels static_cast(m_texture.channels()) - )) + ) == nullptr) { BLT_WARN("Error resizing block texture image!"); }