get glTF loader working

This commit is contained in:
Bailey Harrison 2024-02-14 00:30:51 +00:00
parent 41b39d33fb
commit 6d726e6501
7 changed files with 178 additions and 109 deletions

View File

@ -21,8 +21,11 @@ namespace engine {
class Application { class Application {
public: public:
Application(const char* app_name, const char* app_version, struct Configuration {
gfx::GraphicsSettings graphics_settings); bool enable_frame_limiter;
};
Application(const char* app_name, const char* app_version, gfx::GraphicsSettings graphics_settings, Configuration configuration);
~Application(); ~Application();
Application(const Application&) = delete; Application(const Application&) = delete;
Application& operator=(const Application&) = delete; Application& operator=(const Application&) = delete;
@ -30,22 +33,23 @@ class Application {
/* resource stuff */ /* resource stuff */
template <typename T> template <typename T>
void RegisterResourceManager() { void RegisterResourceManager()
{
size_t hash = typeid(T).hash_code(); size_t hash = typeid(T).hash_code();
assert(resource_managers_.contains(hash) == false && assert(resource_managers_.contains(hash) == false && "Registering resource manager type more than once.");
"Registering resource manager type more than once.");
resource_managers_.emplace(hash, std::make_unique<ResourceManager<T>>()); resource_managers_.emplace(hash, std::make_unique<ResourceManager<T>>());
} }
template <typename T> template <typename T>
std::shared_ptr<T> AddResource(const std::string& name, std::shared_ptr<T> AddResource(const std::string& name, std::unique_ptr<T>&& resource)
std::unique_ptr<T>&& resource) { {
auto resource_manager = GetResourceManager<T>(); auto resource_manager = GetResourceManager<T>();
return resource_manager->Add(name, std::move(resource)); return resource_manager->Add(name, std::move(resource));
} }
template <typename T> template <typename T>
std::shared_ptr<T> GetResource(const std::string& name) { std::shared_ptr<T> GetResource(const std::string& name)
{
auto resource_manager = GetResourceManager<T>(); auto resource_manager = GetResourceManager<T>();
return resource_manager->Get(name); return resource_manager->Get(name);
} }
@ -53,7 +57,7 @@ class Application {
/* methods */ /* methods */
void GameLoop(); void GameLoop();
void SetFrameLimiter(bool on) { enable_frame_limiter_ = on; } void SetFrameLimiter(bool on) { configuration_.enable_frame_limiter = on; }
/* getters */ /* getters */
Window* window() { return window_.get(); } Window* window() { return window_.get(); }
@ -61,25 +65,23 @@ class Application {
SceneManager* scene_manager() { return scene_manager_.get(); } SceneManager* scene_manager() { return scene_manager_.get(); }
Renderer* renderer() { return renderer_.get(); } Renderer* renderer() { return renderer_.get(); }
std::string GetResourcePath(const std::string relative_path) { std::string GetResourcePath(const std::string relative_path) const { return (resources_path_ / relative_path).string(); }
return (resources_path_ / relative_path).string();
}
private: private:
std::unique_ptr<Window> window_; std::unique_ptr<Window> window_;
std::unique_ptr<InputManager> input_manager_; std::unique_ptr<InputManager> input_manager_;
std::unique_ptr<Renderer> renderer_; std::unique_ptr<Renderer> renderer_;
std::unordered_map<size_t, std::unique_ptr<IResourceManager>> std::unordered_map<size_t, std::unique_ptr<IResourceManager>> resource_managers_{};
resource_managers_{};
std::filesystem::path resources_path_; std::filesystem::path resources_path_;
// Most resources and class instances in the game exist in this object // Most resources and class instances in the game exist in this object
std::unique_ptr<SceneManager> scene_manager_; std::unique_ptr<SceneManager> scene_manager_;
bool enable_frame_limiter_ = true; Configuration configuration_;
template <typename T> template <typename T>
ResourceManager<T>* GetResourceManager() { ResourceManager<T>* GetResourceManager()
{
size_t hash = typeid(T).hash_code(); size_t hash = typeid(T).hash_code();
auto it = resource_managers_.find(hash); auto it = resource_managers_.find(hash);
if (it == resource_managers_.end()) { if (it == resource_managers_.end()) {

View File

@ -37,6 +37,7 @@ struct GraphicsSettings {
// not all GPUs/drivers support immediate present with V-sync enabled // not all GPUs/drivers support immediate present with V-sync enabled
wait_for_present = true; wait_for_present = true;
msaa_level = MSAALevel::kOff; msaa_level = MSAALevel::kOff;
enable_anisotropy = false; // anisotropic filtering can severely affect performance on intel iGPUs
} }
bool enable_validation; bool enable_validation;
@ -45,6 +46,7 @@ struct GraphicsSettings {
// (no affect with V-sync disabled) // (no affect with V-sync disabled)
bool wait_for_present; bool wait_for_present;
MSAALevel msaa_level; MSAALevel msaa_level;
bool enable_anisotropy;
}; };
enum class ImageFormat { enum class ImageFormat {
@ -131,7 +133,7 @@ struct SamplerInfo {
Filter minify = gfx::Filter::kLinear; Filter minify = gfx::Filter::kLinear;
Filter magnify = gfx::Filter::kLinear; Filter magnify = gfx::Filter::kLinear;
Filter mipmap = gfx::Filter::kLinear; Filter mipmap = gfx::Filter::kLinear;
bool anisotropic_filtering = true; bool anisotropic_filtering = true; // this can be force disabled by a global setting
bool operator==(const SamplerInfo&) const = default; bool operator==(const SamplerInfo&) const = default;
}; };

View File

@ -73,7 +73,8 @@ static std::filesystem::path getResourcesPath()
return resourcesPath; return resourcesPath;
} }
Application::Application(const char* appName, const char* appVersion, gfx::GraphicsSettings graphicsSettings) Application::Application(const char* appName, const char* appVersion, gfx::GraphicsSettings graphicsSettings, Configuration configuration)
: configuration_(configuration)
{ {
window_ = std::make_unique<Window>(appName, true, false); window_ = std::make_unique<Window>(appName, true, false);
input_manager_ = std::make_unique<InputManager>(window_.get()); input_manager_ = std::make_unique<InputManager>(window_.get());
@ -260,8 +261,7 @@ void Application::GameLoop()
return find_depth(parent, current_depth + 1); return find_depth(parent, current_depth + 1);
} }
}; };
if (scene) if (scene) {
{
for (Entity i = 1; i < scene->next_entity_id_; ++i) { for (Entity i = 1; i < scene->next_entity_id_; ++i) {
auto t = scene->GetComponent<TransformComponent>(i); auto t = scene->GetComponent<TransformComponent>(i);
std::string tabs{}; std::string tabs{};
@ -296,7 +296,7 @@ void Application::GameLoop()
window_->GetInputAndEvents(); window_->GetInputAndEvents();
/* fps limiter */ /* fps limiter */
if (enable_frame_limiter_) { if (configuration_.enable_frame_limiter) {
std::this_thread::sleep_until(endFrame); std::this_thread::sleep_until(endFrame);
} }
beginFrame = endFrame; beginFrame = endFrame;

View File

@ -233,6 +233,8 @@ static VkShaderStageFlags getShaderStageFlags(gfx::ShaderStageFlags::Flags flags
return VK_CULL_MODE_BACK_BIT; return VK_CULL_MODE_BACK_BIT;
case gfx::CullMode::kCullFrontAndBack: case gfx::CullMode::kCullFrontAndBack:
return VK_CULL_MODE_FRONT_AND_BACK; return VK_CULL_MODE_FRONT_AND_BACK;
default:
throw std::runtime_error("Unknown cull mode");
} }
} }
@ -243,6 +245,8 @@ static VkFormat getImageFormat(gfx::ImageFormat format)
return VK_FORMAT_R8G8B8A8_UNORM; return VK_FORMAT_R8G8B8A8_UNORM;
case gfx::ImageFormat::kSRGB: case gfx::ImageFormat::kSRGB:
return VK_FORMAT_R8G8B8A8_SRGB; return VK_FORMAT_R8G8B8A8_SRGB;
default:
throw std::runtime_error("Unknown image format");
} }
} }
@ -352,7 +356,7 @@ struct GFXDevice::Impl {
SwapchainInfo swapchainInfo{}; SwapchainInfo swapchainInfo{};
Swapchain swapchain{}; Swapchain swapchain{};
VkDescriptorPool descriptorPool; VkDescriptorPool descriptorPool = VK_NULL_HANDLE;
struct WriteQueues { struct WriteQueues {
std::unordered_set<gfx::UniformBuffer*> uniform_buffer_writes{}; std::unordered_set<gfx::UniformBuffer*> uniform_buffer_writes{};
@ -1640,7 +1644,7 @@ const gfx::Sampler* GFXDevice::CreateSampler(const gfx::SamplerInfo& info)
samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
samplerInfo.mipLodBias = 0.0f; samplerInfo.mipLodBias = 0.0f;
samplerInfo.anisotropyEnable = info.anisotropic_filtering ? VK_TRUE : VK_FALSE; samplerInfo.anisotropyEnable = (info.anisotropic_filtering && pimpl->graphicsSettings.enable_anisotropy) ? VK_TRUE : VK_FALSE;
samplerInfo.maxAnisotropy = pimpl->device.properties.limits.maxSamplerAnisotropy; samplerInfo.maxAnisotropy = pimpl->device.properties.limits.maxSamplerAnisotropy;
samplerInfo.minLod = 0.0f; samplerInfo.minLod = 0.0f;
samplerInfo.maxLod = VK_LOD_CLAMP_NONE; samplerInfo.maxLod = VK_LOD_CLAMP_NONE;

View File

@ -53,10 +53,10 @@ static glm::mat4 MatFromDoubleArray(const std::vector<double>& arr)
{ {
glm::mat4 mat{}; glm::mat4 mat{};
for (int i = 0; i < 4; ++i) { for (int i = 0; i < 4; ++i) {
mat[i][0] = static_cast<float>(arr[i * 4 + 0]); mat[i][0] = static_cast<float>(arr[static_cast<size_t>(i) * 4 + 0]);
mat[i][1] = static_cast<float>(arr[i * 4 + 1]); mat[i][1] = static_cast<float>(arr[static_cast<size_t>(i) * 4 + 1]);
mat[i][2] = static_cast<float>(arr[i * 4 + 2]); mat[i][2] = static_cast<float>(arr[static_cast<size_t>(i) * 4 + 2]);
mat[i][3] = static_cast<float>(arr[i * 4 + 3]); mat[i][3] = static_cast<float>(arr[static_cast<size_t>(i) * 4 + 3]);
} }
return mat; return mat;
} }
@ -84,8 +84,6 @@ engine::Entity LoadGLTF(Scene& scene, const std::string& path, bool isStatic)
throw std::runtime_error("Failed to load glTF file!"); throw std::runtime_error("Failed to load glTF file!");
} }
LOG_INFO("Loaded glTF model: {}, contains {} scenes", path, model.scenes.size());
// test model loading // test model loading
if (model.scenes.size() < 1) { if (model.scenes.size() < 1) {
@ -210,7 +208,8 @@ engine::Entity LoadGLTF(Scene& scene, const std::string& path, bool isStatic)
if (material.pbrMetallicRoughness.baseColorTexture.index != -1) { if (material.pbrMetallicRoughness.baseColorTexture.index != -1) {
if (material.pbrMetallicRoughness.baseColorTexture.texCoord == 0) { if (material.pbrMetallicRoughness.baseColorTexture.texCoord == 0) {
materials.back()->SetAlbedoTexture(textures.at(material.pbrMetallicRoughness.baseColorTexture.index)); materials.back()->SetAlbedoTexture(textures.at(material.pbrMetallicRoughness.baseColorTexture.index));
} else { }
else {
LOG_WARN("Material {} base color texture specifies a UV channel other than zero which is unsupported."); LOG_WARN("Material {} base color texture specifies a UV channel other than zero which is unsupported.");
LOG_WARN("Material will be created with a white base color"); LOG_WARN("Material will be created with a white base color");
} }
@ -331,11 +330,7 @@ engine::Entity LoadGLTF(Scene& scene, const std::string& path, bool isStatic)
std::vector<Vertex> vertices; std::vector<Vertex> vertices;
vertices.reserve(num_vertices); vertices.reserve(num_vertices);
for (size_t i = 0; i < num_vertices; ++i) { for (size_t i = 0; i < num_vertices; ++i) {
Vertex v; Vertex v{.pos = positions[i], .norm = normals[i], .tangent = tangents[i], .uv = uv0s[i]};
v.pos = positions[i];
v.norm = normals[i];
v.tangent = tangents[i];
v.uv = uv0s[i];
vertices.push_back(v); vertices.push_back(v);
} }
@ -360,13 +355,76 @@ engine::Entity LoadGLTF(Scene& scene, const std::string& path, bool isStatic)
} }
} }
// glTF uses the Y-up convention so objects must be rotated to Z-up
const Entity parent = const Entity parent =
// scene.CreateEntity("test_node", 0, glm::vec3{}, glm::quat{glm::one_over_root_two<float>(), glm::one_over_root_two<float>(), 0.0f, 0.0f}); scene.CreateEntity("test_node", 0, glm::vec3{}, glm::quat{glm::one_over_root_two<float>(), glm::one_over_root_two<float>(), 0.0f, 0.0f});
scene.CreateEntity("test_node", 0);
auto ren = scene.AddComponent<MeshRenderableComponent>(parent); std::vector<Entity> entities(model.nodes.size(), 0);
ren->material = primitive_arrays.at(0).at(0).material; std::function<void(Entity, const tg::Node&)> generateEntities = [&](Entity parent_entity, const tg::Node& node) -> void {
ren->mesh = primitive_arrays.at(0).at(0).mesh; const Entity e = scene.CreateEntity(node.name.empty() ? "anode" : node.name, parent_entity);
// transform
auto t = scene.GetComponent<TransformComponent>(e);
t->position.x = 0.0f;
t->position.y = 0.0f;
t->position.z = 0.0f;
t->rotation.x = 0.0f;
t->rotation.y = 0.0f;
t->rotation.z = 0.0f;
t->rotation.w = 1.0f;
t->scale.x = 1.0f;
t->scale.y = 1.0f;
t->scale.z = 1.0f;
if (node.matrix.size() == 16) {
const glm::mat4 matrix = MatFromDoubleArray(node.matrix);
DecomposeTransform(matrix, t->position, t->rotation, t->scale);
}
else {
if (node.translation.size() == 3) {
t->position.x = static_cast<float>(node.translation[0]);
t->position.y = static_cast<float>(node.translation[1]);
t->position.z = static_cast<float>(node.translation[2]);
}
if (node.rotation.size() == 4) {
t->rotation.x = static_cast<float>(node.rotation[0]);
t->rotation.y = static_cast<float>(node.rotation[1]);
t->rotation.z = static_cast<float>(node.rotation[2]);
t->rotation.w = static_cast<float>(node.rotation[3]);
}
if (node.scale.size() == 3) {
t->scale.x = static_cast<float>(node.scale[0]);
t->scale.y = static_cast<float>(node.scale[1]);
t->scale.z = static_cast<float>(node.scale[2]);
}
}
// ignoring cameras
// ignoring skin
// ignoring weights
if (node.mesh != -1) {
const auto& primitives = primitive_arrays.at(node.mesh);
int i = 0;
for (const EnginePrimitive& prim : primitives) {
auto prim_entity = scene.CreateEntity(std::string("_mesh") + std::to_string(i), e);
auto meshren = scene.AddComponent<MeshRenderableComponent>(prim_entity);
meshren->mesh = prim.mesh;
meshren->material = prim.material;
++i;
}
}
for (int i : node.children) {
generateEntities(e, model.nodes.at(i));
}
};
for (int i : s.nodes) {
generateEntities(parent, model.nodes.at(i));
}
LOG_INFO("Loaded glTF model: {}", path);
return parent; return parent;
} }

Binary file not shown.

View File

@ -22,22 +22,22 @@
#include "config.h" #include "config.h"
static void ConfigureInputs(engine::InputManager* input_manager) static void ConfigureInputs(engine::InputManager& input_manager)
{ {
// user interface mappings // user interface mappings
input_manager->AddInputButton("fullscreen", engine::inputs::Key::K_F11); input_manager.AddInputButton("fullscreen", engine::inputs::Key::K_F11);
input_manager->AddInputButton("exit", engine::inputs::Key::K_ESCAPE); input_manager.AddInputButton("exit", engine::inputs::Key::K_ESCAPE);
// game buttons // game buttons
input_manager->AddInputButton("fire", engine::inputs::MouseButton::M_LEFT); input_manager.AddInputButton("fire", engine::inputs::MouseButton::M_LEFT);
input_manager->AddInputButton("aim", engine::inputs::MouseButton::M_RIGHT); input_manager.AddInputButton("aim", engine::inputs::MouseButton::M_RIGHT);
input_manager->AddInputButton("jump", engine::inputs::Key::K_SPACE); input_manager.AddInputButton("jump", engine::inputs::Key::K_SPACE);
input_manager->AddInputButton("sprint", engine::inputs::Key::K_LSHIFT); input_manager.AddInputButton("sprint", engine::inputs::Key::K_LSHIFT);
// game movement // game movement
input_manager->AddInputButtonAsAxis("movex", engine::inputs::Key::K_D, engine::inputs::Key::K_A); input_manager.AddInputButtonAsAxis("movex", engine::inputs::Key::K_D, engine::inputs::Key::K_A);
input_manager->AddInputButtonAsAxis("movey", engine::inputs::Key::K_W, engine::inputs::Key::K_S); input_manager.AddInputButtonAsAxis("movey", engine::inputs::Key::K_W, engine::inputs::Key::K_S);
// looking around // looking around
input_manager->AddInputAxis("lookx", engine::inputs::MouseAxis::X); input_manager.AddInputAxis("lookx", engine::inputs::MouseAxis::X);
input_manager->AddInputAxis("looky", engine::inputs::MouseAxis::Y); input_manager.AddInputAxis("looky", engine::inputs::MouseAxis::Y);
} }
void PlayGame(GameSettings settings) void PlayGame(GameSettings settings)
@ -50,11 +50,14 @@ void PlayGame(GameSettings settings)
graphics_settings.vsync = true; graphics_settings.vsync = true;
graphics_settings.wait_for_present = false; graphics_settings.wait_for_present = false;
graphics_settings.msaa_level = engine::gfx::MSAALevel::kOff; graphics_settings.msaa_level = engine::gfx::MSAALevel::kOff;
graphics_settings.enable_anisotropy = false;
engine::Application app(PROJECT_NAME, PROJECT_VERSION, graphics_settings); engine::Application::Configuration configuration{};
app.SetFrameLimiter(settings.enable_frame_limiter); configuration.enable_frame_limiter = settings.enable_frame_limiter;
engine::Application app(PROJECT_NAME, PROJECT_VERSION, graphics_settings, configuration);
app.window()->SetRelativeMouseMode(true); app.window()->SetRelativeMouseMode(true);
ConfigureInputs(app.input_manager()); ConfigureInputs(*app.input_manager());
engine::Scene* my_scene = app.scene_manager()->CreateEmptyScene(); engine::Scene* my_scene = app.scene_manager()->CreateEmptyScene();
{ {
@ -201,7 +204,7 @@ void PlayGame(GameSettings settings)
//scene2->GetComponent<engine::TransformComponent>(teapot2)->position.y += 5.0f; //scene2->GetComponent<engine::TransformComponent>(teapot2)->position.y += 5.0f;
//scene2->GetComponent<engine::TransformComponent>(teapot2)->rotation = glm::angleAxis(glm::pi<float>(), glm::vec3{ 0.0f, 0.0f, 1.0f }); //scene2->GetComponent<engine::TransformComponent>(teapot2)->rotation = glm::angleAxis(glm::pi<float>(), glm::vec3{ 0.0f, 0.0f, 1.0f });
//scene2->GetComponent<engine::TransformComponent>(teapot2)->rotation *= glm::angleAxis(glm::half_pi<float>(), glm::vec3{1.0f, 0.0f, 0.0f}); //scene2->GetComponent<engine::TransformComponent>(teapot2)->rotation *= glm::angleAxis(glm::half_pi<float>(), glm::vec3{1.0f, 0.0f, 0.0f});
//auto walls = engine::util::LoadGLTF(*scene2, app.GetResourcePath("models/walls.glb")); auto walls = engine::util::LoadGLTF(*scene2, app.GetResourcePath("models/walls_with_tangents.glb"));
} }
my_scene->GetSystem<CameraControllerSystem>()->next_scene_ = scene2; my_scene->GetSystem<CameraControllerSystem>()->next_scene_ = scene2;