From 18df6dae18a9127bca4e790adb61b24ba2a49a17 Mon Sep 17 00:00:00 2001 From: Derek Christ Date: Sun, 16 Oct 2022 16:51:35 +0200 Subject: [PATCH] Work in progress - glTF resource loader --- .gitmodules | 3 ++ lib/CMakeLists.txt | 5 +++ lib/sail | 1 + src/CMakeLists.txt | 2 + src/Controller.cpp | 30 ++++++++++--- src/color.h | 16 +++++++ src/gltf_loader.cpp | 83 ++++++++++++++++++++++++++++++++++++ src/gltf_loader.h | 24 +++++++++++ src/image.cpp | 88 +++++++++++++++++++++++++++++++++++++++ src/image.h | 74 ++++++++++++++++++++++++++++++-- src/material.h | 11 +++++ src/resources/Texture.cpp | 6 +-- 12 files changed, 332 insertions(+), 11 deletions(-) create mode 160000 lib/sail create mode 100644 src/color.h create mode 100644 src/gltf_loader.cpp create mode 100644 src/gltf_loader.h create mode 100644 src/material.h diff --git a/.gitmodules b/.gitmodules index 17a50c2..d843eba 100644 --- a/.gitmodules +++ b/.gitmodules @@ -16,3 +16,6 @@ [submodule "lib/fx-gltf"] path = lib/fx-gltf url = https://github.com/jessey-git/fx-gltf.git +[submodule "lib/sail"] + path = lib/sail + url = https://github.com/HappySeaFox/sail.git diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 900e726..d1a0669 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -10,3 +10,8 @@ option(FX_GLTF_INSTALL "" OFF) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/fx-gltf) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/stb) + +option(SAIL_BUILD_EXAMPLES "" OFF) +option(SAIL_BUILD_TESTS "" OFF) +option(SAIL_BUILD_APPS "" OFF) +add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/sail) diff --git a/lib/sail b/lib/sail new file mode 160000 index 0000000..c0c1e57 --- /dev/null +++ b/lib/sail @@ -0,0 +1 @@ +Subproject commit c0c1e57d8fe8c63216dc9f130e67415a46c7488f diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d2b847d..90aa569 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -15,6 +15,7 @@ add_library(fever_engine resources/Model.cpp util/Log.cpp image.cpp + gltf_loader.cpp ) target_compile_features(fever_engine PUBLIC cxx_std_20) @@ -31,6 +32,7 @@ target_link_libraries( spdlog fx-gltf::fx-gltf stb + sail ) add_executable(Fall-Fever diff --git a/src/Controller.cpp b/src/Controller.cpp index 91c56d9..aecad1b 100644 --- a/src/Controller.cpp +++ b/src/Controller.cpp @@ -7,6 +7,7 @@ #include "ShaderProgram.h" #include "Window.h" #include "definitions/attribute_locations.h" +#include "gltf_loader.h" #include "resources/Model.h" #include "util/Log.h" @@ -17,18 +18,37 @@ #include #include #include +#include +#include + +struct MyRes +{ + MyRes(std::filesystem::path const &path, int) + { + std::cout << "Path: " << std::filesystem::weakly_canonical(path) << std::endl; + }; + int x = 0; +}; +using namespace entt::literals; Controller::Controller() : m_gameWindow(std::make_shared()), m_postProcessFrameBuffer( m_gameWindow->dimensions().first, m_gameWindow->dimensions().second, postProcessingProgram) { - // auto gltf = fx::gltf::LoadFromBinary("ABeautifulGame/ABeautifulGame.glb", - // {.MaxFileSize = 512 * 1024 * 1024, .MaxBufferByteLength = 512 * 1024 * - // 1024}); + gltf_loader()("Lantern/glTF-Binary/Lantern.glb"); - auto gltf_path = std::filesystem::path("Lantern/glTF/Lantern.gltf"); - auto gltf = fx::gltf::LoadFromText(gltf_path); + fx::gltf::ReadQuotas read_quotas{.MaxFileSize = 512 * 1024 * 1024, .MaxBufferByteLength = 512 * 1024 * 1024}; + + // auto gltf_path = std::filesystem::path("Lantern/glTF/Lantern.gltf"); + auto gltf_path = std::filesystem::path("WaterBottle/glTF/WaterBottle.gltf"); + auto gltf = [&]() { + if (gltf_path.extension() == ".gltf") { + return fx::gltf::LoadFromText(gltf_path, read_quotas); + } + + return fx::gltf::LoadFromBinary(gltf_path, read_quotas); + }(); defaultProgram.bind(); AttributeLocations locations{}; diff --git a/src/color.h b/src/color.h new file mode 100644 index 0000000..90fd22c --- /dev/null +++ b/src/color.h @@ -0,0 +1,16 @@ +#pragma once + +struct Color +{ + double r{}, g{}, b{}; + double a = 1.0; + bool linear = false; +}; + +namespace ColorConstant { +static constexpr Color WHITE{.r = 1.0, .g = 1.0, .b = 1.0}; +static constexpr Color BLACK{.r = 0.0, .g = 0.0, .b = 0.0}; +static constexpr Color RED{.r = 1.0, .g = 0.0, .b = 0.0}; +static constexpr Color GREEN{.r = 0.0, .g = 1.0, .b = 0.0}; +static constexpr Color BLUE{.r = 0.0, .g = 0.0, .b = 1.0}; +} // namespace ColorConstant diff --git a/src/gltf_loader.cpp b/src/gltf_loader.cpp new file mode 100644 index 0000000..175d01d --- /dev/null +++ b/src/gltf_loader.cpp @@ -0,0 +1,83 @@ +#include "gltf_loader.h" + +static void load_texture(fx::gltf::Texture const &texture, + fx::gltf::Document const &gltf, + std::filesystem::path const &document_path, + Image::ColorFormat colorFormat, + entt::resource_cache &image_cache) +{ + auto const &gltf_image = gltf.images.at(texture.source); + auto const base_directory = document_path.parent_path(); + + if (gltf_image.uri.empty()) { + auto const &image_buffer_view = gltf.bufferViews.at(gltf_image.bufferView); + auto const &image_buffer = gltf.buffers.at(image_buffer_view.buffer); + + std::string const image_name = document_path.string() + ".image." + std::to_string(texture.source); + entt::hashed_string image_hash(image_name.c_str()); + + image_cache.load( + image_hash, + std::span{image_buffer.data}.subspan(image_buffer_view.byteOffset, image_buffer_view.byteLength), + colorFormat); + } else { + auto const image_path = base_directory / gltf_image.uri; + std::size_t const image_size = std::filesystem::file_size(image_path); + auto image_ifstream = std::ifstream(image_path, std::ios::binary); + + std::vector image_data; + image_data.reserve(image_size); + + std::copy(std::istreambuf_iterator(image_ifstream), + std::istreambuf_iterator(), + std::back_inserter(image_data)); + + entt::hashed_string image_hash(image_path.c_str()); + + image_cache.load(image_hash, image_data, colorFormat); + } +} + +static void load_material(fx::gltf::Material const &material, + fx::gltf::Document const &gltf, + std::filesystem::path const &document_path, + entt::resource_cache &image_cache) +{ + auto base_color_texture_id = material.pbrMetallicRoughness.baseColorTexture.index; + auto normal_texture_id = material.normalTexture.index; + + if (base_color_texture_id != -1) { + auto const &base_color_texture = gltf.textures.at(base_color_texture_id); + load_texture(base_color_texture, gltf, document_path, Image::ColorFormat::SRGB, image_cache); + } + + if (normal_texture_id != -1) { + auto const &normal_texture = gltf.textures.at(normal_texture_id); + load_texture(normal_texture, gltf, document_path, Image::ColorFormat::RGB, image_cache); + } +} + +auto gltf_loader::operator()(std::filesystem::path const &document_path) -> result_type +{ + fx::gltf::ReadQuotas const read_quotas{.MaxFileSize = MAX_SIZE, .MaxBufferByteLength = MAX_SIZE}; + + fx::gltf::Document gltf = [&]() { + if (document_path.extension() == ".gltf") { + return fx::gltf::LoadFromText(document_path, read_quotas); + } + + return fx::gltf::LoadFromBinary(document_path, read_quotas); + }(); + + // Load here all the rest... + auto const base_directory = document_path.parent_path(); + + // Load textures + for (auto const &material : gltf.materials) { + load_material(material, gltf, document_path, image_cache); + } + + // Load meshes + + return std::make_shared(std::move(gltf)); +} diff --git a/src/gltf_loader.h b/src/gltf_loader.h new file mode 100644 index 0000000..c699f17 --- /dev/null +++ b/src/gltf_loader.h @@ -0,0 +1,24 @@ +#pragma once + +#include "image.h" +#include "material.h" + +#include +#include +#include + +static constexpr auto MAX_SIZE = 512 * 1024 * 1024; + +struct Gltf +{ + std::vector> materials; +}; + +struct gltf_loader final +{ + using result_type = std::shared_ptr; + + auto operator()(std::filesystem::path const &document_path) -> result_type; + + entt::resource_cache image_cache; +}; diff --git a/src/image.cpp b/src/image.cpp index e69de29..10d457e 100644 --- a/src/image.cpp +++ b/src/image.cpp @@ -0,0 +1,88 @@ +#include "image.h" +#include "util/Log.h" + +#define STB_IMAGE_IMPLEMENTATION +#include + +Image::Image(std::span bytes, ColorFormat colorFormat) : colorFormat(colorFormat) +{ + int width{}; + int height{}; + int components{}; + + uint8_t *stbi_image = + stbi_load_from_memory(bytes.data(), static_cast(bytes.size()), &width, &height, &components, 0); + + std::size_t const buffer_length = static_cast(width * height * components); + + // Copy the image data into a vector as stbi currently does not support writing into user-defined buffers. + std::copy(stbi_image, + &stbi_image[buffer_length], // NOLINT (cppcoreguidelines-pro-bounds-pointer-arithmetic) + std::back_inserter(data)); + + dataFormat = [components]() { + switch (components) { + case 1: + return DataFormat::R8Uint; + + case 3: + return DataFormat::RGB8Uint; + + case 4: + return DataFormat::RGBA8Uint; + + default: + Log::logger().warn("Unsupported data format for image."); + return DataFormat::RGBA8Uint; + } + }(); + + extent = Extent{.width = static_cast(width), .height = static_cast(height)}; + + stbi_image_free(stbi_image); +} + +GpuImage::GpuImage(Image const &image) +{ + GLenum internalFormat{}; + GLenum dataFormat{}; + + switch (image.dataFormat) { + case Image::DataFormat::R8Uint: + internalFormat = GL_RED; + dataFormat = GL_RED; + break; + case Image::DataFormat::RGB8Uint: + internalFormat = (image.colorFormat == Image::ColorFormat::SRGB) ? GL_SRGB8 : GL_RGB8; + dataFormat = GL_RGB; + break; + case Image::DataFormat::RGBA8Uint: + internalFormat = (image.colorFormat == Image::ColorFormat::SRGB) ? GL_SRGB8_ALPHA8 : GL_RGBA8; + dataFormat = GL_RGBA; + break; + } + + glGenTextures(1, &texture); + glBindTexture(GL_TEXTURE_2D, texture); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, static_cast(image.sampler.magFilter)); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, static_cast(image.sampler.minFilter)); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, LOD_BIAS); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, static_cast(image.sampler.wrapS)); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, static_cast(image.sampler.wrapT)); + + glTexImage2D(GL_TEXTURE_2D, + 0, + static_cast(internalFormat), + static_cast(image.extent.width), + static_cast(image.extent.height), + 0, + dataFormat, + GL_UNSIGNED_BYTE, + image.data.data()); + + glGenerateMipmap(GL_TEXTURE_2D); + + glBindTexture(GL_TEXTURE_2D, 0); +} diff --git a/src/image.h b/src/image.h index 254aca7..1d15605 100644 --- a/src/image.h +++ b/src/image.h @@ -1,5 +1,73 @@ #pragma once -struct Image { - -}; \ No newline at end of file +#include +#include +#include +#include + +struct Sampler +{ + enum class MagFilter : uint16_t + { + None, + Nearest = 9728, + Linear = 9729 + }; + + enum class MinFilter : uint16_t + { + None, + Nearest = 9728, + Linear = 9729, + NearestMipMapNearest = 9984, + LinearMipMapNearest = 9985, + NearestMipMapLinear = 9986, + LinearMipMapLinear = 9987 + }; + + enum class WrappingMode : uint16_t + { + ClampToEdge = 33071, + MirroredRepeat = 33648, + Repeat = 10497 + }; + + MagFilter magFilter = MagFilter::Linear; + MinFilter minFilter = MinFilter::LinearMipMapLinear; + WrappingMode wrapS = WrappingMode::Repeat; + WrappingMode wrapT = WrappingMode::Repeat; +}; + +struct Image +{ + std::vector data; + Sampler sampler; + + struct Extent { + unsigned int width; + unsigned int height; + } extent{}; + + enum class DataFormat + { + R8Uint, + RGB8Uint, + RGBA8Uint, + } dataFormat; + + enum class ColorFormat { + SRGB, + RGB + } colorFormat; + + Image(std::span bytes, ColorFormat colorFormat); +}; + +struct GpuImage +{ + static constexpr float LOD_BIAS = -2.0; + + GpuImage(Image const &image); + + GLuint texture{}; +}; diff --git a/src/material.h b/src/material.h new file mode 100644 index 0000000..1dbfc85 --- /dev/null +++ b/src/material.h @@ -0,0 +1,11 @@ +#pragma once + +#include "image.h" + +#include + +struct Material +{ + entt::resource base_color_texture; + entt::resource normal_map_texture; +}; diff --git a/src/resources/Texture.cpp b/src/resources/Texture.cpp index 05bf3df..823d163 100644 --- a/src/resources/Texture.cpp +++ b/src/resources/Texture.cpp @@ -2,7 +2,7 @@ #include "../ShaderProgram.h" #include "../util/Log.h" -#define STB_IMAGE_IMPLEMENTATION +// #define STB_IMAGE_IMPLEMENTATION #include Texture::Texture(fx::gltf::Texture const &texture, @@ -34,7 +34,7 @@ Texture::Texture(fx::gltf::Texture const &texture, int width{}; int height{}; int components{}; - + auto *stbi_image = [&]() { if (!image.uri.empty()) { auto image_path = working_directory / image.uri; @@ -84,7 +84,7 @@ Texture::Texture(fx::gltf::Texture const &texture, static_cast(height), 0, dataFormat, - GL_UNSIGNED_BYTE, // read from bufferview? + GL_UNSIGNED_BYTE, stbi_image); glGenerateMipmap(GL_TEXTURE_2D);