Further progress in glTF loader

This commit is contained in:
2022-10-18 20:30:46 +02:00
parent fbb6890f71
commit 5b017d4725
11 changed files with 291 additions and 27 deletions

View File

@@ -15,6 +15,7 @@ add_library(fever_engine
resources/Model.cpp
util/Log.cpp
image.cpp
mesh.cpp
gltf_loader.cpp
)

View File

@@ -31,13 +31,6 @@ Controller::Controller()
m_gameWindow->dimensions().second,
postProcessingProgram)
{
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};

View File

@@ -1,20 +1,60 @@
#include "Scene.h"
#include <iostream>
static void hallo()
{
std::cout << "Testobj created!\n";
}
#include "name.h"
#include "transform.h"
#include "util/Log.h"
using namespace entt::literals;
// TODO: make scene initialization part of gltf loader as seen in bevy
Scene::Scene()
{
struct TestComponent
{
int x;
};
GltfLoader loader{.image_cache = m_image_cache,
.material_cache = m_material_cache,
.mesh_cache = m_mesh_cache,
.gltf_mesh_cache = m_gltf_mesh_cache,
.gltf_node_cache = m_gltf_node_cache,
.gltf_scene_cache = m_gltf_scene_cache};
auto entity = m_registry.create();
entt::resource_cache<Gltf, GltfLoader> gltf_resource_cache{loader};
auto sink = m_registry.on_construct<TestComponent>().before();
std::filesystem::path document_path("ABeautifulGame.glb");
// std::filesystem::path document_path("Lantern/glTF-Binary/Lantern.glb");
entt::hashed_string document_hash(document_path.c_str());
m_registry.emplace<TestComponent>(entity, TestComponent{.x = 2});
}
entt::resource<Gltf> gltf_document =
gltf_resource_cache.load(document_hash, document_path).first->second;
auto default_scene = gltf_document->default_scene;
// Spawn an entity for every node in scene
for (auto const &node : default_scene->nodes) {
auto top_level_entity = m_registry.create();
m_registry.emplace<Name>(top_level_entity, node->name);
m_registry.emplace<Transform>(top_level_entity, node->transform);
auto mesh = node->mesh;
if (mesh.has_value()) {
for (auto const &primitive : mesh.value()->primitives) {
auto mesh_entity = m_registry.create();
m_registry.emplace<Transform>(mesh_entity, Transform{});
m_registry.emplace<entt::resource<Mesh>>(mesh_entity, primitive.mesh);
m_registry.emplace<entt::resource<Material>>(mesh_entity, primitive.material);
}
}
}
auto name_view = m_registry.view<Name>();
for (auto [entity, name] : name_view.each()) {
Log::logger().info("Hello entity {}!", name);
}
auto mesh_view = m_registry.view<Mesh>();
for (auto [entity, mesh] : mesh_view.each()) {
Log::logger().info("Convert mesh {}!");
}
}
void Scene::update(std::chrono::duration<float> delta)
{
}

View File

@@ -1,12 +1,27 @@
#pragma once
#include "mesh.h"
#include "material.h"
#include "gltf_loader.h"
#include <entt/entt.hpp>
#include <chrono>
class Scene
{
public:
Scene();
void update(std::chrono::duration<float> delta);
private:
entt::registry m_registry;
// Resource caches
entt::resource_cache<Image> m_image_cache;
entt::resource_cache<Material> m_material_cache;
entt::resource_cache<Mesh> m_mesh_cache;
entt::resource_cache<GltfMesh> m_gltf_mesh_cache;
entt::resource_cache<GltfNode> m_gltf_node_cache;
entt::resource_cache<GltfScene> m_gltf_scene_cache; // May be moved out of Scene
};

View File

@@ -1,6 +1,8 @@
#include "gltf_loader.h"
#include "util/Log.h"
#include <iterator>
template <typename T>
static auto create_vertex_attribute_data(std::span<uint8_t const> vertex_attribute_data)
-> VertexAttributeData
@@ -89,6 +91,10 @@ static auto load_material(fx::gltf::Material const &material,
load_texture(normal_texture, gltf, document_path, Image::ColorFormat::RGB, image_cache);
}
if (material.name.empty()) {
Log::logger().warn("glTF material has no name.");
}
entt::hashed_string material_hash(material.name.c_str());
return material_cache
.load(material_hash,
@@ -170,6 +176,16 @@ auto load_gltf_primitive(fx::gltf::Primitive const &gltf_primitive,
entt::resource_cache<Mesh> &mesh_cache) -> GltfPrimitive
{
// Load attributes
auto tangent_it =
std::find_if(gltf_primitive.attributes.cbegin(),
gltf_primitive.attributes.cend(),
[](auto const &attribute) { return attribute.first == "TANGENT"; });
if (tangent_it == gltf_primitive.attributes.cend()) {
Log::logger().critical("glTF scene has to include tangent and normal components!");
std::terminate();
}
std::map<Mesh::VertexAttributeId, VertexAttributeData> attributes;
for (auto const &attribute : gltf_primitive.attributes) {
auto vertex_attribute_data = load_attribute(attribute.first, attribute.second, gltf);
@@ -214,7 +230,7 @@ auto load_gltf_primitive(fx::gltf::Primitive const &gltf_primitive,
mesh_cache.load(mesh_hash, Mesh{.attributes = attributes, .indices = indices})
.first->second;
// Get material by name
// Get material by hash
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];
@@ -262,6 +278,10 @@ auto GltfLoader::operator()(std::filesystem::path const &document_path) -> resul
gltf_primitive, gltf, document_path, primitive_count, material_cache, mesh_cache));
}
if (gltf_mesh.name.empty()) {
Log::logger().warn("glTF mesh has no name.");
}
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)})
@@ -270,7 +290,96 @@ auto GltfLoader::operator()(std::filesystem::path const &document_path) -> resul
gltf_meshes.push_back(gltf_mesh_resource);
}
// Load nodes
std::unordered_map<std::size_t, entt::resource<GltfNode>> nodes_map;
nodes_map.reserve(gltf.nodes.size());
for (std::size_t i = 0; i < gltf.nodes.size(); ++i) {
auto const &node = gltf.nodes.at(i);
auto mesh = [this, &node, &gltf]() -> std::optional<entt::resource<GltfMesh>> {
if (node.mesh != -1) {
// Get mesh by hash
auto const &gltf_mesh = gltf.meshes.at(node.mesh);
entt::hashed_string mesh_hash(gltf_mesh.name.c_str());
entt::resource<GltfMesh> mesh = gltf_mesh_cache[mesh_hash];
return {mesh};
}
return {};
}();
glm::vec3 translation(node.translation[0], node.translation[1], node.translation[2]);
glm::quat rotation(node.rotation[3], node.rotation[0], node.rotation[1], node.rotation[2]);
glm::vec3 scale(node.scale[0], node.scale[1], node.scale[2]);
Transform transform{.translation = translation, .rotation = rotation, .scale = scale};
if (node.name.empty()) {
Log::logger().warn("glTF node has no name.");
}
entt::hashed_string node_hash(node.name.c_str());
entt::resource<GltfNode> node_resource =
gltf_node_cache
.load(node_hash,
GltfNode{
.name = node.name, .transform = transform, .mesh = mesh, .children = {}})
.first->second;
nodes_map.emplace(i, node_resource);
}
// Resolve child hierarchy
for (auto const &gltf_node : gltf.nodes) {
std::vector<entt::resource<GltfNode>> children;
for (int child_node_id : gltf_node.children) {
auto child_node = nodes_map.extract(child_node_id);
children.push_back(child_node.mapped());
}
}
std::vector<entt::resource<GltfNode>> nodes;
nodes.reserve(nodes_map.size());
for (auto const &node : nodes_map) {
nodes.push_back(node.second);
}
// Load scenes
std::vector<entt::resource<GltfScene>> scenes;
for (auto const &scene : gltf.scenes) {
// Get nodes by hash
std::vector<entt::resource<GltfNode>> nodes;
nodes.reserve(scene.nodes.size());
for (auto node_id : scene.nodes) {
auto const &node = gltf.nodes.at(node_id);
entt::hashed_string node_hash(node.name.c_str());
nodes.push_back(gltf_node_cache[node_hash]);
}
if (scene.name.empty()) {
Log::logger().warn("glTF scene has no name.");
}
entt::hashed_string scene_hash(scene.name.c_str());
entt::resource<GltfScene> scene_resource =
gltf_scene_cache.load(scene_hash, GltfScene{.nodes = std::move(nodes)}).first->second;
scenes.push_back(scene_resource);
}
// Default scene
entt::resource<GltfScene> default_scene = [&gltf, &scenes]() {
if (gltf.scene != -1) {
return scenes.at(gltf.scene);
}
return scenes.at(0);
}();
return std::make_shared<Gltf>(Gltf{.materials = std::move(materials),
.meshes = std::move(gltf_meshes),
.nodes = std::move(nodes),
.scenes = std::move(scenes),
.default_scene = default_scene,
.document = std::move(gltf)});
}

View File

@@ -3,6 +3,7 @@
#include "image.h"
#include "material.h"
#include "mesh.h"
#include "transform.h"
#include <entt/entt.hpp>
#include <filesystem>
@@ -21,11 +22,27 @@ struct GltfMesh
std::vector<GltfPrimitive> primitives;
};
struct GltfNode
{
std::string name;
Transform transform;
std::optional<entt::resource<GltfMesh>> mesh;
std::vector<entt::resource<GltfNode>> children;
};
struct GltfScene {
std::vector<entt::resource<GltfNode>> nodes;
};
// Move to own file.
struct Gltf
{
std::vector<entt::resource<Material>> materials;
std::vector<entt::resource<GltfMesh>> meshes;
std::vector<entt::resource<GltfNode>> nodes;
std::vector<entt::resource<GltfScene>> scenes;
entt::resource<GltfScene> default_scene;
fx::gltf::Document document;
};
@@ -35,8 +52,11 @@ struct GltfLoader final
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;
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;
entt::resource_cache<GltfNode> &gltf_node_cache;
entt::resource_cache<GltfScene> &gltf_scene_cache;
};

View File

@@ -43,7 +43,8 @@ struct Image
std::vector<uint8_t> data;
Sampler sampler;
struct Extent {
struct Extent
{
unsigned int width;
unsigned int height;
} extent{};
@@ -55,7 +56,8 @@ struct Image
RGBA8Uint,
} dataFormat;
enum class ColorFormat {
enum class ColorFormat
{
SRGB,
RGB
} colorFormat;
@@ -69,5 +71,18 @@ struct GpuImage
GpuImage(Image const &image);
GpuImage(GpuImage const &) = delete;
auto operator=(GpuImage const &) -> GpuImage & = delete;
GpuImage(GpuImage &&other) noexcept : texture(other.texture) { other.texture = 0; }
auto operator=(GpuImage &&other) noexcept -> GpuImage &
{
texture = other.texture;
other.texture = 0;
return *this;
};
~GpuImage() { glDeleteTextures(1, &texture); };
GLuint texture{};
};

30
src/mesh.cpp Normal file
View File

@@ -0,0 +1,30 @@
#include "mesh.h"
GpuMesh::GpuMesh(Mesh const &mesh)
{
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
for (auto const &[attribute_id, attribute_data] : mesh.attributes) {
// BUG: https://github.com/llvm/llvm-project/issues/48582
auto attr_id = attribute_id;
std::visit([=](auto&& arg) {
GLuint vbo{};
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, arg.size(), arg.data(), GL_STATIC_DRAW);
glEnableVertexAttribArray(attr_id);
glVertexAttribPointer(
attr_id,
accessor_byte_size(position_accessor.type),
static_cast<GLenum>(position_accessor.componentType),
position_accessor.normalized ? GL_TRUE : GL_FALSE,
position_buffer_view.byteStride,
reinterpret_cast<void *>(
position_accessor
.byteOffset)); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast,
// performance-no-int-to-ptr)
}
}

View File

@@ -1,9 +1,11 @@
#pragma once
#include <cstdint>
#include <glad/gl.h>
#include <map>
#include <variant>
#include <vector>
#include <array>
struct VertexAttributeData
{
@@ -35,3 +37,24 @@ struct Mesh
std::map<VertexAttributeId, VertexAttributeData> attributes;
Indices indices;
};
struct GpuMesh
{
GpuMesh(Mesh const &mesh);
GpuMesh(GpuMesh const &) = delete;
auto operator=(GpuMesh const &) -> GpuMesh & = delete;
GpuMesh(GpuMesh &&other) noexcept : vao(other.vao) { other.vao = 0; }
auto operator=(GpuMesh &&other) noexcept -> GpuMesh &
{
vao = other.vao;
other.vao = 0;
return *this;
};
~GpuMesh() { glDeleteVertexArrays(1, &vao); };
// TODO: also store vertex buffers.
GLuint vao{};
};

5
src/name.h Normal file
View File

@@ -0,0 +1,5 @@
#pragma once
#include <string>
using Name = std::string;

13
src/transform.h Normal file
View File

@@ -0,0 +1,13 @@
#pragma once
#include <glm/glm.hpp>
#include <glm/gtx/quaternion.hpp>
struct Transform
{
glm::vec3 translation;
glm::quat rotation;
glm::vec3 scale{1.0, 1.0, 1.0};
};
using GlobalTransform = Transform;