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

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

View File

@ -37,6 +37,7 @@ struct GraphicsSettings {
// not all GPUs/drivers support immediate present with V-sync enabled
wait_for_present = true;
msaa_level = MSAALevel::kOff;
enable_anisotropy = false; // anisotropic filtering can severely affect performance on intel iGPUs
}
bool enable_validation;
@ -45,6 +46,7 @@ struct GraphicsSettings {
// (no affect with V-sync disabled)
bool wait_for_present;
MSAALevel msaa_level;
bool enable_anisotropy;
};
enum class ImageFormat {
@ -131,7 +133,7 @@ struct SamplerInfo {
Filter minify = gfx::Filter::kLinear;
Filter magnify = 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;
};

View File

@ -73,7 +73,8 @@ static std::filesystem::path getResourcesPath()
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);
input_manager_ = std::make_unique<InputManager>(window_.get());
@ -114,7 +115,7 @@ Application::Application(const char* appName, const char* appVersion, gfx::Graph
shaderSettings.write_z = true;
shaderSettings.render_order = 0;
auto texturedShader = std::make_unique<Shader>(renderer(), GetResourcePath("engine/shaders/standard.vert").c_str(),
GetResourcePath("engine/shaders/standard.frag").c_str(), shaderSettings);
GetResourcePath("engine/shaders/standard.frag").c_str(), shaderSettings);
GetResourceManager<Shader>()->AddPersistent("builtin.standard", std::move(texturedShader));
}
{
@ -129,7 +130,7 @@ Application::Application(const char* appName, const char* appVersion, gfx::Graph
shaderSettings.write_z = true;
shaderSettings.render_order = 0;
auto fancyShader = std::make_unique<Shader>(renderer(), GetResourcePath("engine/shaders/fancy.vert").c_str(),
GetResourcePath("engine/shaders/fancy.frag").c_str(), shaderSettings);
GetResourcePath("engine/shaders/fancy.frag").c_str(), shaderSettings);
GetResourceManager<Shader>()->AddPersistent("builtin.fancy", std::move(fancyShader));
}
{
@ -144,7 +145,7 @@ Application::Application(const char* appName, const char* appVersion, gfx::Graph
shaderSettings.write_z = true;
shaderSettings.render_order = 0;
auto skyboxShader = std::make_unique<Shader>(renderer(), GetResourcePath("engine/shaders/skybox.vert").c_str(),
GetResourcePath("engine/shaders/skybox.frag").c_str(), shaderSettings);
GetResourcePath("engine/shaders/skybox.frag").c_str(), shaderSettings);
GetResourceManager<Shader>()->AddPersistent("builtin.skybox", std::move(skyboxShader));
}
#if 0
@ -260,8 +261,7 @@ void Application::GameLoop()
return find_depth(parent, current_depth + 1);
}
};
if (scene)
{
if (scene) {
for (Entity i = 1; i < scene->next_entity_id_; ++i) {
auto t = scene->GetComponent<TransformComponent>(i);
std::string tabs{};
@ -296,7 +296,7 @@ void Application::GameLoop()
window_->GetInputAndEvents();
/* fps limiter */
if (enable_frame_limiter_) {
if (configuration_.enable_frame_limiter) {
std::this_thread::sleep_until(endFrame);
}
beginFrame = endFrame;

View File

@ -233,6 +233,8 @@ static VkShaderStageFlags getShaderStageFlags(gfx::ShaderStageFlags::Flags flags
return VK_CULL_MODE_BACK_BIT;
case gfx::CullMode::kCullFrontAndBack:
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;
case gfx::ImageFormat::kSRGB:
return VK_FORMAT_R8G8B8A8_SRGB;
default:
throw std::runtime_error("Unknown image format");
}
}
@ -352,7 +356,7 @@ struct GFXDevice::Impl {
SwapchainInfo swapchainInfo{};
Swapchain swapchain{};
VkDescriptorPool descriptorPool;
VkDescriptorPool descriptorPool = VK_NULL_HANDLE;
struct WriteQueues {
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.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
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.minLod = 0.0f;
samplerInfo.maxLod = VK_LOD_CLAMP_NONE;

View File

@ -53,10 +53,10 @@ static glm::mat4 MatFromDoubleArray(const std::vector<double>& arr)
{
glm::mat4 mat{};
for (int i = 0; i < 4; ++i) {
mat[i][0] = static_cast<float>(arr[i * 4 + 0]);
mat[i][1] = static_cast<float>(arr[i * 4 + 1]);
mat[i][2] = static_cast<float>(arr[i * 4 + 2]);
mat[i][3] = static_cast<float>(arr[i * 4 + 3]);
mat[i][0] = static_cast<float>(arr[static_cast<size_t>(i) * 4 + 0]);
mat[i][1] = static_cast<float>(arr[static_cast<size_t>(i) * 4 + 1]);
mat[i][2] = static_cast<float>(arr[static_cast<size_t>(i) * 4 + 2]);
mat[i][3] = static_cast<float>(arr[static_cast<size_t>(i) * 4 + 3]);
}
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!");
}
LOG_INFO("Loaded glTF model: {}, contains {} scenes", path, model.scenes.size());
// test model loading
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.texCoord == 0) {
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 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;
vertices.reserve(num_vertices);
for (size_t i = 0; i < num_vertices; ++i) {
Vertex v;
v.pos = positions[i];
v.norm = normals[i];
v.tangent = tangents[i];
v.uv = uv0s[i];
Vertex v{.pos = positions[i], .norm = normals[i], .tangent = tangents[i], .uv = uv0s[i]};
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 =
// 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);
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});
auto ren = scene.AddComponent<MeshRenderableComponent>(parent);
ren->material = primitive_arrays.at(0).at(0).material;
ren->mesh = primitive_arrays.at(0).at(0).mesh;
std::vector<Entity> entities(model.nodes.size(), 0);
std::function<void(Entity, const tg::Node&)> generateEntities = [&](Entity parent_entity, const tg::Node& node) -> void {
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;
}

Binary file not shown.

View File

@ -22,22 +22,22 @@
#include "config.h"
static void ConfigureInputs(engine::InputManager* input_manager)
static void ConfigureInputs(engine::InputManager& input_manager)
{
// user interface mappings
input_manager->AddInputButton("fullscreen", engine::inputs::Key::K_F11);
input_manager->AddInputButton("exit", engine::inputs::Key::K_ESCAPE);
input_manager.AddInputButton("fullscreen", engine::inputs::Key::K_F11);
input_manager.AddInputButton("exit", engine::inputs::Key::K_ESCAPE);
// game buttons
input_manager->AddInputButton("fire", engine::inputs::MouseButton::M_LEFT);
input_manager->AddInputButton("aim", engine::inputs::MouseButton::M_RIGHT);
input_manager->AddInputButton("jump", engine::inputs::Key::K_SPACE);
input_manager->AddInputButton("sprint", engine::inputs::Key::K_LSHIFT);
input_manager.AddInputButton("fire", engine::inputs::MouseButton::M_LEFT);
input_manager.AddInputButton("aim", engine::inputs::MouseButton::M_RIGHT);
input_manager.AddInputButton("jump", engine::inputs::Key::K_SPACE);
input_manager.AddInputButton("sprint", engine::inputs::Key::K_LSHIFT);
// game movement
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("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);
// looking around
input_manager->AddInputAxis("lookx", engine::inputs::MouseAxis::X);
input_manager->AddInputAxis("looky", engine::inputs::MouseAxis::Y);
input_manager.AddInputAxis("lookx", engine::inputs::MouseAxis::X);
input_manager.AddInputAxis("looky", engine::inputs::MouseAxis::Y);
}
void PlayGame(GameSettings settings)
@ -50,11 +50,14 @@ void PlayGame(GameSettings settings)
graphics_settings.vsync = true;
graphics_settings.wait_for_present = false;
graphics_settings.msaa_level = engine::gfx::MSAALevel::kOff;
graphics_settings.enable_anisotropy = false;
engine::Application app(PROJECT_NAME, PROJECT_VERSION, graphics_settings);
app.SetFrameLimiter(settings.enable_frame_limiter);
engine::Application::Configuration configuration{};
configuration.enable_frame_limiter = settings.enable_frame_limiter;
engine::Application app(PROJECT_NAME, PROJECT_VERSION, graphics_settings, configuration);
app.window()->SetRelativeMouseMode(true);
ConfigureInputs(app.input_manager());
ConfigureInputs(*app.input_manager());
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)->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});
//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;