Implement mesh and material loading in glTF loader
This commit is contained in:
@@ -8,7 +8,7 @@ AlwaysBreakTemplateDeclarations: 'true'
|
||||
BreakBeforeBraces: Mozilla
|
||||
BinPackParameters: 'false'
|
||||
BinPackArguments: 'false'
|
||||
ColumnLimit: '120'
|
||||
ColumnLimit: '100'
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: 'true'
|
||||
IndentWidth: '4'
|
||||
PointerAlignment: Right
|
||||
|
||||
@@ -24,13 +24,13 @@ target_compile_definitions(fever_engine PRIVATE SPDLOG_FMT_EXTERNAL)
|
||||
target_link_libraries(
|
||||
fever_engine PUBLIC
|
||||
glad
|
||||
glm
|
||||
glfw
|
||||
EnTT::EnTT
|
||||
fmt
|
||||
pthread
|
||||
spdlog
|
||||
fx-gltf::fx-gltf
|
||||
nlohmann_json::nlohmann_json
|
||||
stb
|
||||
sail
|
||||
)
|
||||
|
||||
@@ -13,35 +13,37 @@
|
||||
|
||||
#include <GLFW/glfw3.h>
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <filesystem>
|
||||
#include <fx/gltf.h>
|
||||
#include <glad/gl.h>
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
#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)
|
||||
m_postProcessFrameBuffer(m_gameWindow->dimensions().first,
|
||||
m_gameWindow->dimensions().second,
|
||||
postProcessingProgram)
|
||||
{
|
||||
gltf_loader<fx::gltf::Document>()("Lantern/glTF-Binary/Lantern.glb");
|
||||
GltfLoader loader;
|
||||
entt::resource_cache<Gltf, GltfLoader> gltf_resource_cache;
|
||||
entt::resource<Gltf> gltf_document =
|
||||
gltf_resource_cache
|
||||
.load("Lantern/glTF-Binary/Lantern.glb"_hs, "Lantern/glTF-Binary/Lantern.glb")
|
||||
.first->second;
|
||||
|
||||
fx::gltf::ReadQuotas read_quotas{.MaxFileSize = 512 * 1024 * 1024, .MaxBufferByteLength = 512 * 1024 * 1024};
|
||||
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_path = std::filesystem::path("WaterBottle/glTF/WaterBottle.gltf");
|
||||
auto gltf_path = std::filesystem::path("ABeautifulGame.glb");
|
||||
auto gltf = [&]() {
|
||||
if (gltf_path.extension() == ".gltf") {
|
||||
return fx::gltf::LoadFromText(gltf_path, read_quotas);
|
||||
@@ -73,7 +75,7 @@ Controller::Controller()
|
||||
|
||||
std::vector<Model> models;
|
||||
for (auto const &mesh : gltf.meshes) {
|
||||
std::vector<Mesh> meshes;
|
||||
std::vector<Mesh_> meshes;
|
||||
for (auto const &primitive : mesh.primitives) {
|
||||
auto const &material = gltf.materials.at(primitive.material);
|
||||
auto baseColorTexture = material.pbrMetallicRoughness.baseColorTexture.index;
|
||||
@@ -110,7 +112,7 @@ Controller::Controller()
|
||||
primitive_textures.emplace_back(m_textures.at(normalTexture));
|
||||
}
|
||||
|
||||
meshes.emplace_back(Mesh({primitive, gltf, locations}, primitive_textures));
|
||||
meshes.emplace_back(Mesh_({primitive, gltf, locations}, primitive_textures));
|
||||
}
|
||||
models.emplace_back(Model(mesh.name, std::move(meshes)));
|
||||
}
|
||||
@@ -122,16 +124,18 @@ Controller::Controller()
|
||||
continue;
|
||||
}
|
||||
|
||||
ModelEntity entity(
|
||||
Entity::Prototype(node.name, {}, {}, 1.0F), m_models[static_cast<unsigned>(node.mesh)], defaultProgram);
|
||||
ModelEntity entity(Entity::Prototype(node.name, {}, {}, 1.0F),
|
||||
m_models[static_cast<unsigned>(node.mesh)],
|
||||
defaultProgram);
|
||||
|
||||
if (!node.translation.empty()) {
|
||||
entity.setPosition(glm::vec3(node.translation[0], node.translation[1], node.translation[2]));
|
||||
entity.setPosition(
|
||||
glm::vec3(node.translation[0], node.translation[1], node.translation[2]));
|
||||
}
|
||||
|
||||
if (!node.rotation.empty()) {
|
||||
entity.setRotation(
|
||||
glm::eulerAngles(glm::quat(node.rotation[3], node.rotation[0], node.rotation[1], node.rotation[2])));
|
||||
entity.setRotation(glm::eulerAngles(
|
||||
glm::quat(node.rotation[3], node.rotation[0], node.rotation[1], node.rotation[2])));
|
||||
}
|
||||
|
||||
if (!node.scale.empty()) {
|
||||
@@ -139,13 +143,17 @@ Controller::Controller()
|
||||
}
|
||||
|
||||
entities.push_back(std::move(entity));
|
||||
}
|
||||
|
||||
for (auto const &node : gltf.nodes) {
|
||||
for (auto const &child : node.children) {
|
||||
if (!node.translation.empty()) {
|
||||
entities[child].translate(glm::vec3(node.translation[0], node.translation[1], node.translation[2]));
|
||||
entities[child].translate(
|
||||
glm::vec3(node.translation[0], node.translation[1], node.translation[2]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_entities = std::move(entities);
|
||||
}
|
||||
|
||||
@@ -155,11 +163,13 @@ void Controller::run()
|
||||
|
||||
m_camera->translate(glm::vec3(0., .5, 2.));
|
||||
DirectionalLight directional_light(
|
||||
DirectionalLight::Prototype("", glm::vec3(-0.2, -1.0, -0.3), glm::vec3(1.0f), 5.f), &defaultProgram);
|
||||
DirectionalLight::Prototype("", glm::vec3(-0.2, -1.0, -0.3), glm::vec3(1.0f), 5.f),
|
||||
&defaultProgram);
|
||||
directional_light.setActive(true);
|
||||
|
||||
PointLight point_light(PointLight::Prototype("", "", glm::vec3(4.0, 1.0, 6.0), glm::vec3(1.0F), 3.0F),
|
||||
&defaultProgram);
|
||||
PointLight point_light(
|
||||
PointLight::Prototype("", "", glm::vec3(4.0, 1.0, 6.0), glm::vec3(1.0F), 3.0F),
|
||||
&defaultProgram);
|
||||
point_light.setActive(true);
|
||||
|
||||
Log::logger().info("Startup complete. Enter game loop.");
|
||||
@@ -222,7 +232,10 @@ void Controller::limit_framerate()
|
||||
|
||||
double frameTime = 1 / (double)MAX_FPS;
|
||||
if (frameTime > lastTime) {
|
||||
Helper::sleep(static_cast<unsigned>(frameTime - lastTime) * 1000000);
|
||||
static constexpr auto MICROSECONDS_PER_SECOND = 1'000'000;
|
||||
auto sleep_time_us =
|
||||
static_cast<unsigned>((frameTime - lastTime) * MICROSECONDS_PER_SECOND);
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(sleep_time_us));
|
||||
}
|
||||
|
||||
m_deltaTime = glfwGetTime() - startingTime;
|
||||
|
||||
@@ -3,26 +3,13 @@
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#ifdef __linux__
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
void Helper::sleep(uint32_t us)
|
||||
{
|
||||
#ifdef __linux__
|
||||
usleep(us);
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
// I don't know why I have to divide by 2000 and not 1000 but this way it works even on Windows
|
||||
Sleep(us / 2000);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Helper::gl_debug_callback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length,
|
||||
const GLchar *message, const void *userParam)
|
||||
void Helper::gl_debug_callback(GLenum source,
|
||||
GLenum type,
|
||||
GLuint id,
|
||||
GLenum severity,
|
||||
GLsizei length,
|
||||
const GLchar *message,
|
||||
const void *userParam)
|
||||
{
|
||||
(void)length;
|
||||
(void)userParam;
|
||||
@@ -128,7 +115,11 @@ void Helper::gl_debug_callback(GLenum source, GLenum type, GLuint id, GLenum sev
|
||||
"Type: {}\n"
|
||||
"ID: {}\n"
|
||||
"Severity: {}\n",
|
||||
_message, _source, _type, id, _severity);
|
||||
_message,
|
||||
_source,
|
||||
_type,
|
||||
id,
|
||||
_severity);
|
||||
}
|
||||
|
||||
Helper::Timer::Timer(const std::string &name) : m_name(name)
|
||||
|
||||
@@ -6,9 +6,12 @@
|
||||
#include <string>
|
||||
|
||||
namespace Helper {
|
||||
void sleep(uint32_t us);
|
||||
|
||||
void gl_debug_callback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message,
|
||||
void gl_debug_callback(GLenum source,
|
||||
GLenum type,
|
||||
GLuint id,
|
||||
GLenum severity,
|
||||
GLsizei length,
|
||||
const GLchar *message,
|
||||
const void *userParam);
|
||||
|
||||
class Timer
|
||||
|
||||
@@ -5,12 +5,12 @@
|
||||
#include "resources/Texture.h"
|
||||
#include <utility>
|
||||
|
||||
Mesh::Mesh(VertexArray vertexArray, std::vector<std::reference_wrapper<const Texture>> textures)
|
||||
Mesh_::Mesh_(VertexArray vertexArray, std::vector<std::reference_wrapper<const Texture>> textures)
|
||||
: m_vertexArray(std::move(vertexArray)), m_textures(std::move(textures))
|
||||
{
|
||||
}
|
||||
|
||||
void Mesh::draw(ShaderProgram const &shaderProgram) const
|
||||
void Mesh_::draw(ShaderProgram const &shaderProgram) const
|
||||
{
|
||||
// Bind all textures in order to its texture unit
|
||||
std::size_t textureNum = 0;
|
||||
@@ -31,7 +31,7 @@ void Mesh::draw(ShaderProgram const &shaderProgram) const
|
||||
}
|
||||
}
|
||||
|
||||
void Mesh::drawWithoutTextures() const
|
||||
void Mesh_::drawWithoutTextures() const
|
||||
{
|
||||
m_vertexArray.bind();
|
||||
|
||||
@@ -41,7 +41,7 @@ void Mesh::drawWithoutTextures() const
|
||||
VertexArray::unbind();
|
||||
}
|
||||
|
||||
auto Mesh::getVertexArray() -> VertexArray *
|
||||
auto Mesh_::getVertexArray() -> VertexArray *
|
||||
{
|
||||
return &m_vertexArray;
|
||||
}
|
||||
|
||||
@@ -7,10 +7,10 @@
|
||||
|
||||
class ShaderProgram;
|
||||
|
||||
class Mesh
|
||||
class Mesh_
|
||||
{
|
||||
public:
|
||||
Mesh(VertexArray vertexArray, std::vector<std::reference_wrapper<const Texture>> textures);
|
||||
Mesh_(VertexArray vertexArray, std::vector<std::reference_wrapper<const Texture>> textures);
|
||||
|
||||
void draw(ShaderProgram const &shaderProgram) const;
|
||||
void drawWithoutTextures() const;
|
||||
|
||||
@@ -1,10 +1,35 @@
|
||||
#include "gltf_loader.h"
|
||||
#include "util/Log.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)
|
||||
template <typename T>
|
||||
static auto create_vertex_attribute_data(std::span<uint8_t const> vertex_attribute_data)
|
||||
-> VertexAttributeData
|
||||
{
|
||||
T attribute_data;
|
||||
attribute_data.reserve(vertex_attribute_data.size_bytes());
|
||||
|
||||
std::memcpy(
|
||||
attribute_data.data(), vertex_attribute_data.data(), vertex_attribute_data.size_bytes());
|
||||
|
||||
return VertexAttributeData{.values = std::move(attribute_data)};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static auto create_indices(std::span<uint8_t const> gltf_index_data) -> Indices
|
||||
{
|
||||
T index_data;
|
||||
index_data.reserve(gltf_index_data.size_bytes());
|
||||
|
||||
std::memcpy(index_data.data(), gltf_index_data.data(), gltf_index_data.size_bytes());
|
||||
|
||||
return Indices{.values = std::move(index_data)};
|
||||
}
|
||||
|
||||
static auto 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) -> entt::resource<Image>
|
||||
{
|
||||
auto const &gltf_image = gltf.images.at(texture.source);
|
||||
auto const base_directory = document_path.parent_path();
|
||||
@@ -13,53 +38,194 @@ static void load_texture(fx::gltf::Texture const &texture,
|
||||
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());
|
||||
std::string const image_name =
|
||||
document_path.string() + ".image." + std::to_string(texture.source);
|
||||
entt::hashed_string const 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);
|
||||
return image_cache
|
||||
.load(image_hash,
|
||||
std::span{image_buffer.data}.subspan(image_buffer_view.byteOffset,
|
||||
image_buffer_view.byteLength),
|
||||
colorFormat)
|
||||
.first->second;
|
||||
}
|
||||
|
||||
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 const image_hash(image_path.c_str());
|
||||
|
||||
return image_cache.load(image_hash, image_data, colorFormat).first->second;
|
||||
}
|
||||
|
||||
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)
|
||||
static auto load_material(fx::gltf::Material const &material,
|
||||
fx::gltf::Document const &gltf,
|
||||
std::filesystem::path const &document_path,
|
||||
entt::resource_cache<Material> &material_cache,
|
||||
entt::resource_cache<Image> &image_cache) -> entt::resource<Material>
|
||||
{
|
||||
auto base_color_texture_id = material.pbrMetallicRoughness.baseColorTexture.index;
|
||||
auto normal_texture_id = material.normalTexture.index;
|
||||
|
||||
std::optional<entt::resource<Image>> base_color_image;
|
||||
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);
|
||||
base_color_image = load_texture(
|
||||
base_color_texture, gltf, document_path, Image::ColorFormat::SRGB, image_cache);
|
||||
}
|
||||
|
||||
std::optional<entt::resource<Image>> normal_map_image;
|
||||
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);
|
||||
normal_map_image =
|
||||
load_texture(normal_texture, gltf, document_path, Image::ColorFormat::RGB, image_cache);
|
||||
}
|
||||
|
||||
entt::hashed_string material_hash(material.name.c_str());
|
||||
return material_cache
|
||||
.load(material_hash,
|
||||
Material{.base_color_texture = base_color_image,
|
||||
.normal_map_texture = normal_map_image})
|
||||
.first->second;
|
||||
}
|
||||
|
||||
auto gltf_loader::operator()(std::filesystem::path const &document_path) -> result_type
|
||||
static auto load_attribute(std::string_view attribute_name,
|
||||
uint32_t attribute_id,
|
||||
fx::gltf::Document const &gltf) -> std::optional<VertexAttributeData>
|
||||
{
|
||||
fx::gltf::ReadQuotas const read_quotas{.MaxFileSize = MAX_SIZE, .MaxBufferByteLength = MAX_SIZE};
|
||||
auto const &attribute_accessor = gltf.accessors.at(attribute_id);
|
||||
|
||||
if (attribute_accessor.componentType != fx::gltf::Accessor::ComponentType::Float) {
|
||||
Log::logger().critical("Only float attributes supported!");
|
||||
std::terminate();
|
||||
}
|
||||
|
||||
auto vertex_attribute_id = [attribute_name]() -> std::optional<std::size_t> {
|
||||
if (attribute_name == "POSITION") {
|
||||
return 0;
|
||||
}
|
||||
if (attribute_name == "TEXCOORD_0") {
|
||||
return 1;
|
||||
}
|
||||
if (attribute_name == "NORMAL") {
|
||||
return 2;
|
||||
}
|
||||
if (attribute_name == "TANGENT") {
|
||||
return 3;
|
||||
}
|
||||
|
||||
return {};
|
||||
}();
|
||||
|
||||
// Skip unsupported attribute
|
||||
if (!vertex_attribute_id.has_value()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
auto const &buffer_view = gltf.bufferViews.at(attribute_accessor.bufferView);
|
||||
auto const &buffer = gltf.buffers.at(buffer_view.buffer);
|
||||
|
||||
std::span<uint8_t const> buffer_span(buffer.data);
|
||||
std::span<uint8_t const> vertex_attribute_data_span =
|
||||
buffer_span.subspan(buffer_view.byteOffset, buffer_view.byteLength);
|
||||
|
||||
auto vertex_attribute_data = [vertex_attribute_data_span, &attribute_accessor]() {
|
||||
if (attribute_accessor.type == fx::gltf::Accessor::Type::Scalar) {
|
||||
return create_vertex_attribute_data<VertexAttributeData::Scalar>(
|
||||
vertex_attribute_data_span);
|
||||
}
|
||||
if (attribute_accessor.type == fx::gltf::Accessor::Type::Vec2) {
|
||||
return create_vertex_attribute_data<VertexAttributeData::Vec2>(
|
||||
vertex_attribute_data_span);
|
||||
}
|
||||
if (attribute_accessor.type == fx::gltf::Accessor::Type::Vec3) {
|
||||
return create_vertex_attribute_data<VertexAttributeData::Vec3>(
|
||||
vertex_attribute_data_span);
|
||||
}
|
||||
if (attribute_accessor.type == fx::gltf::Accessor::Type::Vec4) {
|
||||
return create_vertex_attribute_data<VertexAttributeData::Vec4>(
|
||||
vertex_attribute_data_span);
|
||||
}
|
||||
|
||||
Log::logger().critical("Unsupported vertex attribute type!");
|
||||
std::terminate();
|
||||
}();
|
||||
|
||||
return std::move(vertex_attribute_data);
|
||||
}
|
||||
|
||||
auto load_gltf_primitive(fx::gltf::Primitive const &gltf_primitive,
|
||||
fx::gltf::Document const &gltf,
|
||||
std::filesystem::path const &document_path,
|
||||
unsigned primitive_id,
|
||||
entt::resource_cache<Material> &material_cache,
|
||||
entt::resource_cache<Mesh> &mesh_cache) -> GltfPrimitive
|
||||
{
|
||||
// Load attributes
|
||||
std::map<Mesh::VertexAttributeId, VertexAttributeData> attributes;
|
||||
for (auto const &attribute : gltf_primitive.attributes) {
|
||||
auto vertex_attribute_data = load_attribute(attribute.first, attribute.second, gltf);
|
||||
|
||||
if (!vertex_attribute_data.has_value()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
attributes.emplace(attribute.second, std::move(vertex_attribute_data.value()));
|
||||
}
|
||||
|
||||
// Load indices
|
||||
auto const &indices_accessor = gltf.accessors.at(gltf_primitive.indices);
|
||||
auto const &indices_buffer_view = gltf.bufferViews.at(indices_accessor.bufferView);
|
||||
auto const &indices_buffer = gltf.buffers.at(indices_buffer_view.buffer);
|
||||
|
||||
std::span<uint8_t const> indices_buffer_span(indices_buffer.data);
|
||||
std::span<uint8_t const> indices_data_span =
|
||||
indices_buffer_span.subspan(indices_buffer_view.byteOffset, indices_buffer_view.byteLength);
|
||||
|
||||
auto indices = [indices_data_span, &indices_accessor]() {
|
||||
if (indices_accessor.componentType == fx::gltf::Accessor::ComponentType::UnsignedByte) {
|
||||
return create_indices<Indices::UnsignedByte>(indices_data_span);
|
||||
}
|
||||
|
||||
if (indices_accessor.componentType == fx::gltf::Accessor::ComponentType::UnsignedShort) {
|
||||
return create_indices<Indices::UnsignedShort>(indices_data_span);
|
||||
}
|
||||
if (indices_accessor.componentType == fx::gltf::Accessor::ComponentType::UnsignedInt) {
|
||||
return create_indices<Indices::UnsignedInt>(indices_data_span);
|
||||
}
|
||||
|
||||
Log::logger().critical("Unsupported indices type!");
|
||||
std::terminate();
|
||||
}();
|
||||
|
||||
std::string const mesh_name =
|
||||
document_path.string() + ".primitive." + std::to_string(primitive_id);
|
||||
entt::hashed_string const mesh_hash(mesh_name.c_str());
|
||||
|
||||
entt::resource<Mesh> mesh =
|
||||
mesh_cache.load(mesh_hash, Mesh{.attributes = attributes, .indices = indices})
|
||||
.first->second;
|
||||
|
||||
// Get material by name
|
||||
auto const &gltf_material = gltf.materials.at(gltf_primitive.material);
|
||||
entt::hashed_string material_hash(gltf_material.name.c_str());
|
||||
entt::resource<Material> material = material_cache[material_hash];
|
||||
|
||||
return GltfPrimitive{.mesh = mesh, .material = material};
|
||||
}
|
||||
|
||||
auto GltfLoader::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") {
|
||||
@@ -72,12 +238,39 @@ auto gltf_loader::operator()(std::filesystem::path const &document_path) -> resu
|
||||
// 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 materials
|
||||
std::vector<entt::resource<Material>> materials;
|
||||
for (auto const &gltf_material : gltf.materials) {
|
||||
entt::resource<Material> material =
|
||||
load_material(gltf_material, gltf, document_path, material_cache, image_cache);
|
||||
materials.push_back(material);
|
||||
}
|
||||
|
||||
// Load meshes
|
||||
std::vector<entt::resource<GltfMesh>> gltf_meshes;
|
||||
gltf_meshes.reserve(gltf.meshes.size());
|
||||
|
||||
return std::make_shared<fx::gltf::Document>(std::move(gltf));
|
||||
}
|
||||
for (auto const &gltf_mesh : gltf.meshes) {
|
||||
// Load primitives
|
||||
unsigned primitive_count = 0;
|
||||
|
||||
std::vector<GltfPrimitive> primitives;
|
||||
primitives.reserve(gltf_mesh.primitives.size());
|
||||
|
||||
for (auto const &gltf_primitive : gltf_mesh.primitives) {
|
||||
primitives.push_back(load_gltf_primitive(
|
||||
gltf_primitive, gltf, document_path, primitive_count, material_cache, mesh_cache));
|
||||
}
|
||||
|
||||
entt::hashed_string gltf_mesh_hash(gltf_mesh.name.c_str());
|
||||
entt::resource<GltfMesh> gltf_mesh_resource =
|
||||
gltf_mesh_cache.load(gltf_mesh_hash, GltfMesh{.primitives = std::move(primitives)})
|
||||
.first->second;
|
||||
|
||||
gltf_meshes.push_back(gltf_mesh_resource);
|
||||
}
|
||||
|
||||
return std::make_shared<Gltf>(Gltf{.materials = std::move(materials),
|
||||
.meshes = std::move(gltf_meshes),
|
||||
.document = std::move(gltf)});
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "image.h"
|
||||
#include "material.h"
|
||||
#include "mesh.h"
|
||||
|
||||
#include <entt/entt.hpp>
|
||||
#include <filesystem>
|
||||
@@ -9,16 +10,33 @@
|
||||
|
||||
static constexpr auto MAX_SIZE = 512 * 1024 * 1024;
|
||||
|
||||
struct GltfPrimitive
|
||||
{
|
||||
entt::resource<Mesh> mesh;
|
||||
entt::resource<Material> material;
|
||||
};
|
||||
|
||||
struct GltfMesh
|
||||
{
|
||||
std::vector<GltfPrimitive> primitives;
|
||||
};
|
||||
|
||||
// Move to own file.
|
||||
struct Gltf
|
||||
{
|
||||
std::vector<entt::resource<Material>> materials;
|
||||
std::vector<entt::resource<GltfMesh>> meshes;
|
||||
fx::gltf::Document document;
|
||||
};
|
||||
|
||||
struct gltf_loader final
|
||||
struct GltfLoader final
|
||||
{
|
||||
using result_type = std::shared_ptr<fx::gltf::Document>;
|
||||
using result_type = std::shared_ptr<Gltf>;
|
||||
|
||||
auto operator()(std::filesystem::path const &document_path) -> result_type;
|
||||
|
||||
entt::resource_cache<Image> image_cache;
|
||||
entt::resource_cache<Material> material_cache;
|
||||
entt::resource_cache<Mesh> mesh_cache;
|
||||
entt::resource_cache<GltfMesh> gltf_mesh_cache;
|
||||
};
|
||||
|
||||
@@ -3,9 +3,10 @@
|
||||
#include "image.h"
|
||||
|
||||
#include <entt/entt.hpp>
|
||||
#include <optional>
|
||||
|
||||
struct Material
|
||||
{
|
||||
entt::resource<Image> base_color_texture;
|
||||
entt::resource<Image> normal_map_texture;
|
||||
std::optional<entt::resource<Image>> base_color_texture;
|
||||
std::optional<entt::resource<Image>> normal_map_texture;
|
||||
};
|
||||
|
||||
37
src/mesh.h
Normal file
37
src/mesh.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
struct VertexAttributeData
|
||||
{
|
||||
using Scalar = std::vector<float>;
|
||||
using Vec2 = std::vector<std::array<float, 2>>;
|
||||
using Vec3 = std::vector<std::array<float, 3>>;
|
||||
using Vec4 = std::vector<std::array<float, 4>>;
|
||||
|
||||
// Possible improvement:
|
||||
// Instead of copying the data into a vector, create a span that
|
||||
// points into the original data. For that, the glTF file has to
|
||||
// persist until the data is uploaded to the GPU.
|
||||
std::variant<Scalar, Vec2, Vec3, Vec4> values;
|
||||
};
|
||||
|
||||
struct Indices
|
||||
{
|
||||
using UnsignedByte = std::vector<uint8_t>;
|
||||
using UnsignedShort = std::vector<uint16_t>;
|
||||
using UnsignedInt = std::vector<uint32_t>;
|
||||
|
||||
std::variant<UnsignedByte, UnsignedShort, UnsignedInt> values;
|
||||
};
|
||||
|
||||
struct Mesh
|
||||
{
|
||||
using VertexAttributeId = std::size_t;
|
||||
|
||||
std::map<VertexAttributeId, VertexAttributeData> attributes;
|
||||
Indices indices;
|
||||
};
|
||||
@@ -2,7 +2,7 @@
|
||||
#include "Texture.h"
|
||||
#include "../util/Log.h"
|
||||
|
||||
Model::Model(std::string_view name, std::vector<Mesh> meshes) : m_meshes(std::move(meshes)), m_name(name)
|
||||
Model::Model(std::string_view name, std::vector<Mesh_> meshes) : m_meshes(std::move(meshes)), m_name(name)
|
||||
{
|
||||
Log::logger().trace(R"(Loaded model "{}".)", name);
|
||||
}
|
||||
@@ -23,7 +23,7 @@ void Model::drawWithoutTextures() const
|
||||
}
|
||||
}
|
||||
|
||||
auto Model::getMesh(unsigned int index) -> Mesh *
|
||||
auto Model::getMesh(unsigned int index) -> Mesh_ *
|
||||
{
|
||||
return &m_meshes[index];
|
||||
}
|
||||
|
||||
@@ -9,15 +9,15 @@
|
||||
class Model
|
||||
{
|
||||
public:
|
||||
Model(std::string_view name, std::vector<Mesh> meshes);
|
||||
Model(std::string_view name, std::vector<Mesh_> meshes);
|
||||
|
||||
void draw(ShaderProgram const &shaderProgram) const;
|
||||
void drawWithoutTextures() const;
|
||||
|
||||
auto getMesh(unsigned int index) -> Mesh *; // TODO...
|
||||
auto getMesh(unsigned int index) -> Mesh_ *; // TODO...
|
||||
|
||||
private:
|
||||
std::vector<Mesh> m_meshes;
|
||||
std::vector<Mesh_> m_meshes;
|
||||
std::vector<ResourceId> m_textures;
|
||||
|
||||
std::string m_name;
|
||||
|
||||
Reference in New Issue
Block a user