Further progress in glTF loader
This commit is contained in:
@@ -15,6 +15,7 @@ add_library(fever_engine
|
||||
resources/Model.cpp
|
||||
util/Log.cpp
|
||||
image.cpp
|
||||
mesh.cpp
|
||||
gltf_loader.cpp
|
||||
)
|
||||
|
||||
|
||||
@@ -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};
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
15
src/Scene.h
15
src/Scene.h
@@ -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
|
||||
};
|
||||
|
||||
@@ -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)});
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
19
src/image.h
19
src/image.h
@@ -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
30
src/mesh.cpp
Normal 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)
|
||||
}
|
||||
}
|
||||
23
src/mesh.h
23
src/mesh.h
@@ -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
5
src/name.h
Normal file
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
using Name = std::string;
|
||||
13
src/transform.h
Normal file
13
src/transform.h
Normal 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;
|
||||
Reference in New Issue
Block a user