Work in progress - glTF resource loader
This commit is contained in:
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -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
|
||||
|
||||
5
lib/CMakeLists.txt
vendored
5
lib/CMakeLists.txt
vendored
@@ -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)
|
||||
|
||||
1
lib/sail
vendored
Submodule
1
lib/sail
vendored
Submodule
Submodule lib/sail added at c0c1e57d8f
@@ -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
|
||||
|
||||
@@ -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 <glad/gl.h>
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include <iostream>
|
||||
#include <utility>
|
||||
|
||||
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<Window>()),
|
||||
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<fx::gltf::Document>()("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{};
|
||||
|
||||
16
src/color.h
Normal file
16
src/color.h
Normal file
@@ -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
|
||||
83
src/gltf_loader.cpp
Normal file
83
src/gltf_loader.cpp
Normal file
@@ -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> &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<uint8_t> image_data;
|
||||
image_data.reserve(image_size);
|
||||
|
||||
std::copy(std::istreambuf_iterator<char>(image_ifstream),
|
||||
std::istreambuf_iterator<char>(),
|
||||
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> &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<fx::gltf::Document>(std::move(gltf));
|
||||
}
|
||||
24
src/gltf_loader.h
Normal file
24
src/gltf_loader.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include "image.h"
|
||||
#include "material.h"
|
||||
|
||||
#include <entt/entt.hpp>
|
||||
#include <filesystem>
|
||||
#include <fx/gltf.h>
|
||||
|
||||
static constexpr auto MAX_SIZE = 512 * 1024 * 1024;
|
||||
|
||||
struct Gltf
|
||||
{
|
||||
std::vector<entt::resource<Material>> materials;
|
||||
};
|
||||
|
||||
struct gltf_loader final
|
||||
{
|
||||
using result_type = std::shared_ptr<fx::gltf::Document>;
|
||||
|
||||
auto operator()(std::filesystem::path const &document_path) -> result_type;
|
||||
|
||||
entt::resource_cache<Image> image_cache;
|
||||
};
|
||||
@@ -0,0 +1,88 @@
|
||||
#include "image.h"
|
||||
#include "util/Log.h"
|
||||
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include <stb_image.h>
|
||||
|
||||
Image::Image(std::span<uint8_t const> bytes, ColorFormat colorFormat) : colorFormat(colorFormat)
|
||||
{
|
||||
int width{};
|
||||
int height{};
|
||||
int components{};
|
||||
|
||||
uint8_t *stbi_image =
|
||||
stbi_load_from_memory(bytes.data(), static_cast<int>(bytes.size()), &width, &height, &components, 0);
|
||||
|
||||
std::size_t const buffer_length = static_cast<unsigned>(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<unsigned>(width), .height = static_cast<unsigned>(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<GLint>(image.sampler.magFilter));
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, static_cast<GLint>(image.sampler.minFilter));
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, LOD_BIAS);
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, static_cast<GLint>(image.sampler.wrapS));
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, static_cast<GLint>(image.sampler.wrapT));
|
||||
|
||||
glTexImage2D(GL_TEXTURE_2D,
|
||||
0,
|
||||
static_cast<GLint>(internalFormat),
|
||||
static_cast<GLsizei>(image.extent.width),
|
||||
static_cast<GLsizei>(image.extent.height),
|
||||
0,
|
||||
dataFormat,
|
||||
GL_UNSIGNED_BYTE,
|
||||
image.data.data());
|
||||
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
70
src/image.h
70
src/image.h
@@ -1,5 +1,73 @@
|
||||
#pragma once
|
||||
|
||||
struct Image {
|
||||
#include <filesystem>
|
||||
#include <glad/gl.h>
|
||||
#include <span>
|
||||
#include <vector>
|
||||
|
||||
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<uint8_t> 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<uint8_t const> bytes, ColorFormat colorFormat);
|
||||
};
|
||||
|
||||
struct GpuImage
|
||||
{
|
||||
static constexpr float LOD_BIAS = -2.0;
|
||||
|
||||
GpuImage(Image const &image);
|
||||
|
||||
GLuint texture{};
|
||||
};
|
||||
11
src/material.h
Normal file
11
src/material.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include "image.h"
|
||||
|
||||
#include <entt/entt.hpp>
|
||||
|
||||
struct Material
|
||||
{
|
||||
entt::resource<Image> base_color_texture;
|
||||
entt::resource<Image> normal_map_texture;
|
||||
};
|
||||
@@ -2,7 +2,7 @@
|
||||
#include "../ShaderProgram.h"
|
||||
#include "../util/Log.h"
|
||||
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
// #define STB_IMAGE_IMPLEMENTATION
|
||||
#include <stb_image.h>
|
||||
|
||||
Texture::Texture(fx::gltf::Texture const &texture,
|
||||
@@ -84,7 +84,7 @@ Texture::Texture(fx::gltf::Texture const &texture,
|
||||
static_cast<GLsizei>(height),
|
||||
0,
|
||||
dataFormat,
|
||||
GL_UNSIGNED_BYTE, // read from bufferview?
|
||||
GL_UNSIGNED_BYTE,
|
||||
stbi_image);
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user