Make scene loading part of gltf loader
This commit is contained in:
@@ -3,7 +3,7 @@ add_library(fever_engine
|
||||
Controller.cpp
|
||||
Window.cpp
|
||||
Light.cpp
|
||||
Scene.cpp
|
||||
scene.cpp
|
||||
FrameBuffer.cpp
|
||||
Helper.cpp
|
||||
util/Log.cpp
|
||||
|
||||
@@ -23,29 +23,34 @@
|
||||
using namespace entt::literals;
|
||||
|
||||
Controller::Controller()
|
||||
: m_scene(m_shader_cache),
|
||||
m_gameWindow(std::make_shared<Window>()),
|
||||
: m_gameWindow(std::make_shared<Window>()),
|
||||
m_postProcessFrameBuffer(m_gameWindow->dimensions().first,
|
||||
m_gameWindow->dimensions().second,
|
||||
post_processing_shader)
|
||||
post_processing_shader),
|
||||
m_gltf_loader{.image_cache = m_image_cache,
|
||||
.material_cache = m_material_cache,
|
||||
.mesh_cache = m_mesh_cache,
|
||||
.shader_cache = m_shader_cache,
|
||||
.scene_cache = m_scene_cache,
|
||||
.gltf_mesh_cache = m_gltf_mesh_cache,
|
||||
.gltf_node_cache = m_gltf_node_cache},
|
||||
m_gltf_cache(m_gltf_loader)
|
||||
{
|
||||
// if (!gltf.cameras.empty()) {
|
||||
// auto const &gltf_camera = gltf.cameras.at(0);
|
||||
// std::filesystem::path document_path("Lantern/glTF-Binary/Lantern.glb");
|
||||
std::filesystem::path document_path("ABeautifulGame.glb");
|
||||
entt::hashed_string document_hash(document_path.c_str());
|
||||
|
||||
// assert(gltf_camera.type == fx::gltf::Camera::Type::Perspective);
|
||||
// auto const &perspective = gltf_camera.perspective;
|
||||
entt::resource<Gltf> gltf_document =
|
||||
m_gltf_cache.load(document_hash, document_path).first->second;
|
||||
|
||||
// m_camera = std::make_shared<Camera>(perspective.yfov, perspective.aspectRatio);
|
||||
// } else {
|
||||
// m_camera = std::make_shared<Camera>(90., m_gameWindow->aspectRatio());
|
||||
// }
|
||||
m_scene = gltf_document->default_scene.value().handle();
|
||||
}
|
||||
|
||||
void Controller::run()
|
||||
{
|
||||
updateExposure(post_processing_shader);
|
||||
|
||||
entt::hashed_string shader_hash (Material::SHADER_NAME.data());
|
||||
entt::hashed_string shader_hash(Material::SHADER_NAME.data());
|
||||
auto standard_material_shader =
|
||||
m_shader_cache.load(shader_hash, Material::SHADER_NAME).first->second;
|
||||
|
||||
@@ -74,7 +79,7 @@ void Controller::run()
|
||||
auto const &mouse_cursor_input = m_gameWindow->mouse_cursor_input();
|
||||
|
||||
static constexpr auto MICROSECONDS_PER_SECOND = 1'000'000;
|
||||
m_scene.update(
|
||||
m_scene->update(
|
||||
std::chrono::microseconds(static_cast<unsigned>(m_deltaTime * MICROSECONDS_PER_SECOND)),
|
||||
key_input,
|
||||
mouse_cursor_input,
|
||||
@@ -87,7 +92,7 @@ void Controller::run()
|
||||
m_postProcessFrameBuffer.bind();
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
Render::render(m_scene.registry());
|
||||
Render::render(m_scene->registry());
|
||||
|
||||
Framebuffer::unbind();
|
||||
m_postProcessFrameBuffer.drawOnEntireScreen();
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include "FrameBuffer.h"
|
||||
#include "Scene.h"
|
||||
#include "scene.h"
|
||||
#include "shader.h"
|
||||
#include "gltf_loader.h"
|
||||
|
||||
#include <entt/entt.hpp>
|
||||
#include <glm/glm.hpp>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <entt/entt.hpp>
|
||||
|
||||
class Window;
|
||||
class Scene;
|
||||
@@ -29,18 +30,28 @@ private:
|
||||
void update_window_dimensions();
|
||||
|
||||
std::shared_ptr<Window> m_gameWindow;
|
||||
|
||||
|
||||
Shader skybox_shader{"skybox", "data/shaders"};
|
||||
Shader post_processing_shader{"post_processing", "data/shaders"};
|
||||
|
||||
Framebuffer m_postProcessFrameBuffer;
|
||||
|
||||
entt::resource_cache<Shader, ShaderLoader> m_shader_cache;
|
||||
|
||||
static constexpr unsigned MAX_FPS = 60;
|
||||
|
||||
Scene m_scene;
|
||||
std::shared_ptr<Scene> m_scene;
|
||||
|
||||
double m_deltaTime{};
|
||||
float m_exposure = 1.0;
|
||||
|
||||
// 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<Scene> m_scene_cache;
|
||||
entt::resource_cache<Shader, ShaderLoader> m_shader_cache;
|
||||
entt::resource_cache<GltfMesh> m_gltf_mesh_cache;
|
||||
entt::resource_cache<GltfNode> m_gltf_node_cache;
|
||||
|
||||
GltfLoader m_gltf_loader;
|
||||
entt::resource_cache<Gltf, GltfLoader> m_gltf_cache;
|
||||
};
|
||||
|
||||
148
src/Scene.cpp
148
src/Scene.cpp
@@ -1,148 +0,0 @@
|
||||
#include "Scene.h"
|
||||
#include "camera.h"
|
||||
#include "mesh.h"
|
||||
#include "name.h"
|
||||
#include "relationship.h"
|
||||
#include "shader.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(entt::resource_cache<Shader, ShaderLoader> &shader_cache)
|
||||
{
|
||||
GltfLoader loader{.image_cache = m_image_cache,
|
||||
.material_cache = m_material_cache,
|
||||
.mesh_cache = m_mesh_cache,
|
||||
.shader_cache = shader_cache,
|
||||
.gltf_mesh_cache = m_gltf_mesh_cache,
|
||||
.gltf_node_cache = m_gltf_node_cache,
|
||||
.gltf_scene_cache = m_gltf_scene_cache};
|
||||
|
||||
entt::resource_cache<Gltf, GltfLoader> gltf_resource_cache{loader};
|
||||
|
||||
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());
|
||||
|
||||
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) {
|
||||
std::function<entt::entity(GltfNode const &, std::optional<entt::entity>)> spawn_node =
|
||||
[this, &spawn_node](GltfNode const &node, std::optional<entt::entity> parent) {
|
||||
auto entity = m_registry.create();
|
||||
m_registry.emplace<Name>(entity, node.name);
|
||||
m_registry.emplace<Transform>(entity, node.transform);
|
||||
m_registry.emplace<GlobalTransform>(entity, GlobalTransform{});
|
||||
|
||||
if (parent.has_value()) {
|
||||
m_registry.emplace<Parent>(entity, Parent{.parent = parent.value()});
|
||||
}
|
||||
|
||||
std::vector<entt::entity> child_entities;
|
||||
|
||||
auto mesh = node.mesh;
|
||||
if (mesh.has_value()) {
|
||||
for (auto const &primitive : mesh.value()->primitives) {
|
||||
auto mesh_entity = m_registry.create();
|
||||
m_registry.emplace<Parent>(mesh_entity, Parent{.parent = entity});
|
||||
m_registry.emplace<Transform>(mesh_entity, Transform{});
|
||||
m_registry.emplace<GlobalTransform>(mesh_entity, GlobalTransform{});
|
||||
m_registry.emplace<entt::resource<Mesh>>(mesh_entity, primitive.mesh);
|
||||
m_registry.emplace<entt::resource<Material>>(mesh_entity,
|
||||
primitive.material);
|
||||
|
||||
child_entities.push_back(mesh_entity);
|
||||
}
|
||||
}
|
||||
|
||||
// Spawn child nodes
|
||||
for (auto const &child : node.children) {
|
||||
auto child_entity = spawn_node(child, entity);
|
||||
child_entities.push_back(child_entity);
|
||||
}
|
||||
|
||||
m_registry.emplace<Children>(entity, Children{.children = child_entities});
|
||||
return entity;
|
||||
};
|
||||
|
||||
spawn_node(node, {});
|
||||
}
|
||||
|
||||
auto mesh_view = m_registry.view<entt::resource<Mesh>>();
|
||||
for (auto [entity, mesh] : mesh_view.each()) {
|
||||
m_registry.emplace<GpuMesh>(entity, GpuMesh(mesh));
|
||||
|
||||
// Remove Mesh resource as it is no longer needed.
|
||||
m_registry.erase<entt::resource<Mesh>>(entity);
|
||||
}
|
||||
|
||||
auto material_view = m_registry.view<entt::resource<Material>>();
|
||||
for (auto [entity, material] : material_view.each()) {
|
||||
m_registry.emplace<GpuMaterial>(entity, GpuMaterial(material));
|
||||
|
||||
// Remove Material resource as it is no longer needed.
|
||||
m_registry.erase<entt::resource<Material>>(entity);
|
||||
}
|
||||
|
||||
// Spawn the camera
|
||||
auto entity = m_registry.create();
|
||||
m_registry.emplace<Name>(entity, "Camera");
|
||||
m_registry.emplace<Transform>(entity, Transform{.translation = glm::vec3(0.0, 0.25, -1.0)});
|
||||
m_registry.emplace<GlobalTransform>(entity, GlobalTransform{});
|
||||
m_registry.emplace<Camera>(entity, Camera{.projection = Camera::Perspective{}});
|
||||
}
|
||||
|
||||
void Scene::update(std::chrono::duration<float> delta,
|
||||
KeyInput const &key_input,
|
||||
MouseCursorInput const &mouse_cursor_input,
|
||||
float aspect_ratio,
|
||||
bool cursor_catched)
|
||||
{
|
||||
{
|
||||
// Update GlobalTransform components
|
||||
// TODO: Only do this when the Transform changed.
|
||||
auto root_transform_view =
|
||||
m_registry.view<Transform const, GlobalTransform>(entt::exclude<Parent>);
|
||||
auto transform_view = m_registry.view<Transform const, GlobalTransform, Parent const>();
|
||||
|
||||
for (auto [entity, transform, global_transform] : root_transform_view.each()) {
|
||||
global_transform = transform;
|
||||
|
||||
auto parent_global_transform = global_transform;
|
||||
if (auto *children = m_registry.try_get<Children>(entity)) {
|
||||
for (auto child : children->children) {
|
||||
std::function<void(entt::entity entity,
|
||||
GlobalTransform parent_global_transform)>
|
||||
transform_propagate = [this, &transform_propagate, &transform_view](
|
||||
entt::entity entity,
|
||||
GlobalTransform parent_global_transform) {
|
||||
auto [transform, global_transform, parent] = transform_view.get(entity);
|
||||
global_transform.transform = parent_global_transform.transform *
|
||||
GlobalTransform(transform).transform;
|
||||
|
||||
if (auto *children = m_registry.try_get<Children>(entity)) {
|
||||
for (auto child : children->children) {
|
||||
transform_propagate(child, global_transform);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
transform_propagate(child, parent_global_transform);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Camera::aspect_ratio_update(m_registry, aspect_ratio);
|
||||
Camera::keyboard_movement(m_registry, key_input, delta);
|
||||
if (cursor_catched) {
|
||||
Camera::mouse_orientation(m_registry, mouse_cursor_input);
|
||||
}
|
||||
|
||||
}
|
||||
36
src/Scene.h
36
src/Scene.h
@@ -1,36 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "Window.h"
|
||||
#include "gltf_loader.h"
|
||||
#include "material.h"
|
||||
#include "mesh.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <entt/entt.hpp>
|
||||
|
||||
class Shader;
|
||||
|
||||
class Scene
|
||||
{
|
||||
public:
|
||||
Scene(entt::resource_cache<Shader, ShaderLoader> &shader_cache);
|
||||
|
||||
void update(std::chrono::duration<float> delta_time,
|
||||
KeyInput const &key_input,
|
||||
MouseCursorInput const &mouse_cursor_input,
|
||||
float aspect_ratio,
|
||||
bool cursor_catched);
|
||||
|
||||
auto registry() -> entt::registry & { return m_registry; }
|
||||
|
||||
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,162 +0,0 @@
|
||||
#include "ShaderProgram.h"
|
||||
#include "util/Log.h"
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <fstream>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
|
||||
ShaderProgram::ShaderProgram(Prototype prototype) : m_shaderProgramId(glCreateProgram())
|
||||
{
|
||||
std::string vertexShaderSource = parse(prototype.vertexPath);
|
||||
std::string fragmentShaderSource = parse(prototype.fragmentPath);
|
||||
|
||||
GLuint vertexShader = compile(vertexShaderSource, GL_VERTEX_SHADER);
|
||||
GLuint fragmentShader = compile(fragmentShaderSource, GL_FRAGMENT_SHADER);
|
||||
|
||||
glAttachShader(m_shaderProgramId, vertexShader);
|
||||
glAttachShader(m_shaderProgramId, fragmentShader);
|
||||
|
||||
glLinkProgram(m_shaderProgramId);
|
||||
|
||||
GLint isLinked = 0;
|
||||
glGetProgramiv(m_shaderProgramId, GL_LINK_STATUS, &isLinked);
|
||||
if (isLinked == 0) {
|
||||
Log::logger().warn(R"(Failed to link shaderProgram "{}", "{}")",
|
||||
prototype.vertexPath,
|
||||
prototype.fragmentPath);
|
||||
}
|
||||
|
||||
#ifdef NDEBUG
|
||||
glDetachShader(m_shaderProgramId, vertexShader);
|
||||
glDetachShader(m_shaderProgramId, fragmentShader);
|
||||
|
||||
glDeleteShader(vertexShader);
|
||||
glDeleteShader(fragmentShader);
|
||||
#endif
|
||||
|
||||
Log::logger().trace(R"(Loaded shaderprogram "{}".)", prototype.name);
|
||||
}
|
||||
|
||||
ShaderProgram::~ShaderProgram()
|
||||
{
|
||||
glDeleteProgram(m_shaderProgramId);
|
||||
}
|
||||
|
||||
void ShaderProgram::bind() const
|
||||
{
|
||||
glUseProgram(m_shaderProgramId);
|
||||
}
|
||||
|
||||
void ShaderProgram::unbind()
|
||||
{
|
||||
glUseProgram(0);
|
||||
}
|
||||
|
||||
auto ShaderProgram::parse(const std::filesystem::path &path) -> std::string
|
||||
{
|
||||
std::fstream file;
|
||||
file.open(path, std::ios::in);
|
||||
|
||||
if (!file.is_open()) {
|
||||
Log::logger().critical(R"(Shader "{}" not found!)", path.string());
|
||||
std::terminate();
|
||||
}
|
||||
|
||||
return {(std::istreambuf_iterator<char>(file)), (std::istreambuf_iterator<char>())};
|
||||
}
|
||||
|
||||
auto ShaderProgram::compile(const std::string &shaderSource, GLenum type) -> GLuint
|
||||
{
|
||||
GLuint shaderId = glCreateShader(type);
|
||||
const char *src = shaderSource.c_str();
|
||||
glShaderSource(shaderId, 1, &src, nullptr);
|
||||
glCompileShader(shaderId);
|
||||
|
||||
int result{};
|
||||
glGetShaderiv(shaderId, GL_COMPILE_STATUS, &result);
|
||||
|
||||
if (result != GL_TRUE) {
|
||||
int length{};
|
||||
glGetShaderiv(shaderId, GL_INFO_LOG_LENGTH, &length);
|
||||
|
||||
std::string message;
|
||||
message.reserve(static_cast<unsigned>(length));
|
||||
|
||||
glGetShaderInfoLog(shaderId, length, &length, message.data());
|
||||
Log::logger().error("Shader compilation failed: {}", message);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return shaderId;
|
||||
}
|
||||
|
||||
auto ShaderProgram::retrieveUniformLocation(std::string_view uniform_name) const -> GLint
|
||||
{
|
||||
if (m_uniformLocationCache.find(uniform_name.data()) != m_uniformLocationCache.end()) {
|
||||
return m_uniformLocationCache[uniform_name.data()];
|
||||
}
|
||||
|
||||
GLint location = glGetUniformLocation(m_shaderProgramId, uniform_name.data());
|
||||
|
||||
if (location != -1) {
|
||||
m_uniformLocationCache[uniform_name.data()] = location;
|
||||
} else {
|
||||
Log::logger().warn(R"(Uniform "{}" not found.)", uniform_name);
|
||||
}
|
||||
|
||||
return location;
|
||||
}
|
||||
|
||||
void ShaderProgram::set_uniform(const std::string &name, bool value) const
|
||||
{
|
||||
GLint location = retrieveUniformLocation(name);
|
||||
glUniform1i(location, (int)value);
|
||||
}
|
||||
|
||||
void ShaderProgram::set_uniform(const std::string &name, int value) const
|
||||
{
|
||||
GLint location = retrieveUniformLocation(name);
|
||||
glUniform1i(location, value);
|
||||
}
|
||||
|
||||
void ShaderProgram::set_uniform(const std::string &name, unsigned int value) const
|
||||
{
|
||||
GLint location = retrieveUniformLocation(name);
|
||||
glUniform1ui(location, value);
|
||||
}
|
||||
|
||||
void ShaderProgram::set_uniform(const std::string &name, float value) const
|
||||
{
|
||||
GLint location = retrieveUniformLocation(name);
|
||||
glUniform1f(location, value);
|
||||
}
|
||||
|
||||
void ShaderProgram::set_uniform(const std::string &name, glm::vec2 vector) const
|
||||
{
|
||||
GLint location = retrieveUniformLocation(name);
|
||||
glUniform2f(location, vector.x, vector.y);
|
||||
}
|
||||
|
||||
void ShaderProgram::set_uniform(const std::string &name, glm::vec3 vector) const
|
||||
{
|
||||
GLint location = retrieveUniformLocation(name);
|
||||
glUniform3f(location, vector.x, vector.y, vector.z);
|
||||
}
|
||||
|
||||
void ShaderProgram::set_uniform(const std::string &name, glm::mat3 matrix) const
|
||||
{
|
||||
GLint location = retrieveUniformLocation(name);
|
||||
glUniformMatrix3fv(location, 1, GL_FALSE, glm::value_ptr(matrix));
|
||||
}
|
||||
|
||||
void ShaderProgram::set_uniform(const std::string &name, glm::mat4 matrix) const
|
||||
{
|
||||
GLint location = retrieveUniformLocation(name);
|
||||
glUniformMatrix4fv(location, 1, GL_FALSE, glm::value_ptr(matrix));
|
||||
}
|
||||
|
||||
auto ShaderProgram::getShaderProgramId() const -> GLuint
|
||||
{
|
||||
return m_shaderProgramId;
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <glad/gl.h>
|
||||
#include <glm/glm.hpp>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
class ShaderProgram
|
||||
{
|
||||
public:
|
||||
struct Prototype
|
||||
{
|
||||
std::string name;
|
||||
std::string vertexPath;
|
||||
std::string fragmentPath;
|
||||
};
|
||||
|
||||
ShaderProgram(Prototype prototype);
|
||||
~ShaderProgram();
|
||||
|
||||
void bind() const;
|
||||
static void unbind();
|
||||
|
||||
auto retrieveUniformLocation(std::string_view uniform_name) const -> GLint;
|
||||
|
||||
// May be rewritten...
|
||||
void set_uniform(const std::string &name, bool value) const;
|
||||
void set_uniform(const std::string &name, int value) const;
|
||||
void set_uniform(const std::string &name, unsigned int value) const;
|
||||
void set_uniform(const std::string &name, float value) const;
|
||||
void set_uniform(const std::string &name, glm::vec2 vector) const;
|
||||
void set_uniform(const std::string &name, glm::vec3 vector) const;
|
||||
void set_uniform(const std::string &name, glm::mat3 matrix) const;
|
||||
void set_uniform(const std::string &name, glm::mat4 matrix) const;
|
||||
|
||||
auto getShaderProgramId() const -> GLuint;
|
||||
|
||||
private:
|
||||
static auto parse(const std::filesystem::path &path) -> std::string;
|
||||
static auto compile(const std::string &shaderSource, GLenum type) -> GLuint;
|
||||
|
||||
GLuint m_shaderProgramId;
|
||||
mutable std::unordered_map<std::string, GLint> m_uniformLocationCache;
|
||||
};
|
||||
@@ -20,6 +20,8 @@ struct Camera
|
||||
static constexpr float SPEED = 0.5;
|
||||
static constexpr float ACCELERATION = 5.0;
|
||||
|
||||
static constexpr glm::vec3 DEFAULT_POSITION = glm::vec3(0.0, 0.25, -1.0);
|
||||
|
||||
struct Perspective
|
||||
{
|
||||
float fov = DEFAULT_FOV;
|
||||
|
||||
51
src/gltf.h
Normal file
51
src/gltf.h
Normal file
@@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
|
||||
#include "material.h"
|
||||
#include "mesh.h"
|
||||
#include "transform.h"
|
||||
|
||||
#include <entt/entt.hpp>
|
||||
#include <fx/gltf.h>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
class Mesh;
|
||||
class Scene;
|
||||
class Material;
|
||||
class Image;
|
||||
|
||||
struct GltfPrimitive
|
||||
{
|
||||
entt::resource<Mesh> mesh;
|
||||
entt::resource<Material> material;
|
||||
};
|
||||
|
||||
struct GltfMesh
|
||||
{
|
||||
std::vector<GltfPrimitive> primitives;
|
||||
};
|
||||
|
||||
struct GltfCamera
|
||||
{
|
||||
std::variant<fx::gltf::Camera::Perspective, fx::gltf::Camera::Orthographic> projection;
|
||||
};
|
||||
|
||||
struct GltfNode
|
||||
{
|
||||
std::string name;
|
||||
Transform transform;
|
||||
std::optional<entt::resource<GltfMesh>> mesh;
|
||||
std::optional<GltfCamera> camera;
|
||||
std::vector<entt::resource<GltfNode>> children;
|
||||
};
|
||||
|
||||
struct Gltf
|
||||
{
|
||||
std::vector<entt::resource<Material>> materials;
|
||||
std::vector<entt::resource<GltfMesh>> meshes;
|
||||
std::vector<entt::resource<GltfNode>> nodes;
|
||||
std::vector<entt::resource<Scene>> scenes;
|
||||
|
||||
std::optional<entt::resource<Scene>> default_scene;
|
||||
fx::gltf::Document document;
|
||||
};
|
||||
@@ -1,5 +1,9 @@
|
||||
#include "gltf_loader.h"
|
||||
#include "camera.h"
|
||||
#include "definitions/attribute_locations.h"
|
||||
#include "name.h"
|
||||
#include "relationship.h"
|
||||
#include "scene.h"
|
||||
#include "util/Log.h"
|
||||
|
||||
#include <iterator>
|
||||
@@ -74,7 +78,8 @@ static auto load_material(fx::gltf::Material const &material,
|
||||
std::filesystem::path const &document_path,
|
||||
entt::resource_cache<Material> &material_cache,
|
||||
entt::resource_cache<Image> &image_cache,
|
||||
entt::resource_cache<Shader, ShaderLoader> &shader_cache) -> entt::resource<Material>
|
||||
entt::resource_cache<Shader, ShaderLoader> &shader_cache)
|
||||
-> entt::resource<Material>
|
||||
{
|
||||
auto base_color_texture_id = material.pbrMetallicRoughness.baseColorTexture.index;
|
||||
auto normal_texture_id = material.normalTexture.index;
|
||||
@@ -315,6 +320,23 @@ auto GltfLoader::operator()(std::filesystem::path const &document_path) -> resul
|
||||
return {};
|
||||
}();
|
||||
|
||||
auto camera = [this, &node, &gltf]() -> std::optional<GltfCamera> {
|
||||
if (node.camera != -1) {
|
||||
auto const &camera = gltf.cameras.at(node.camera);
|
||||
|
||||
if (camera.type != fx::gltf::Camera::Type::Perspective) {
|
||||
Log::logger().warn("Only perspective projections supported.");
|
||||
}
|
||||
|
||||
// Only perspective supported until now
|
||||
GltfCamera gltf_camera{.projection = camera.perspective};
|
||||
|
||||
return {gltf_camera};
|
||||
}
|
||||
|
||||
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]);
|
||||
@@ -326,12 +348,14 @@ auto GltfLoader::operator()(std::filesystem::path const &document_path) -> resul
|
||||
}
|
||||
|
||||
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;
|
||||
entt::resource<GltfNode> node_resource = gltf_node_cache
|
||||
.load(node_hash,
|
||||
GltfNode{.name = node.name,
|
||||
.transform = transform,
|
||||
.mesh = mesh,
|
||||
.camera = camera,
|
||||
.children = {}})
|
||||
.first->second;
|
||||
|
||||
nodes_map.emplace(i, node_resource);
|
||||
}
|
||||
@@ -356,35 +380,103 @@ auto GltfLoader::operator()(std::filesystem::path const &document_path) -> resul
|
||||
}
|
||||
|
||||
// Load scenes
|
||||
std::vector<entt::resource<GltfScene>> scenes;
|
||||
for (auto const &scene : gltf.scenes) {
|
||||
std::vector<entt::resource<Scene>> scenes;
|
||||
for (auto const &gltf_scene : gltf.scenes) {
|
||||
// Get nodes by hash
|
||||
std::vector<entt::resource<GltfNode>> nodes;
|
||||
nodes.reserve(scene.nodes.size());
|
||||
nodes.reserve(gltf_scene.nodes.size());
|
||||
|
||||
for (auto node_id : scene.nodes) {
|
||||
for (auto node_id : gltf_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()) {
|
||||
if (gltf_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;
|
||||
entt::registry registry;
|
||||
|
||||
// Spawn an entity for every node in scene
|
||||
for (auto const &node : nodes) {
|
||||
std::function<entt::entity(GltfNode const &, std::optional<entt::entity>)> spawn_node =
|
||||
[this, &spawn_node, ®istry](GltfNode const &node,
|
||||
std::optional<entt::entity> parent) {
|
||||
auto entity = registry.create();
|
||||
registry.emplace<Name>(entity, node.name);
|
||||
registry.emplace<Transform>(entity, node.transform);
|
||||
registry.emplace<GlobalTransform>(entity, GlobalTransform{});
|
||||
|
||||
if (parent.has_value()) {
|
||||
registry.emplace<Parent>(entity, Parent{.parent = parent.value()});
|
||||
}
|
||||
|
||||
std::vector<entt::entity> child_entities;
|
||||
|
||||
auto mesh = node.mesh;
|
||||
if (mesh.has_value()) {
|
||||
for (auto const &primitive : mesh.value()->primitives) {
|
||||
auto mesh_entity = registry.create();
|
||||
registry.emplace<Parent>(mesh_entity, Parent{.parent = entity});
|
||||
registry.emplace<Transform>(mesh_entity, Transform{});
|
||||
registry.emplace<GlobalTransform>(mesh_entity, GlobalTransform{});
|
||||
registry.emplace<entt::resource<Mesh>>(mesh_entity, primitive.mesh);
|
||||
registry.emplace<entt::resource<Material>>(mesh_entity,
|
||||
primitive.material);
|
||||
|
||||
child_entities.push_back(mesh_entity);
|
||||
}
|
||||
}
|
||||
|
||||
auto camera = node.camera;
|
||||
if (camera.has_value()) {
|
||||
auto perspective =
|
||||
std::get<fx::gltf::Camera::Perspective>(camera.value().projection);
|
||||
Camera::Perspective camera_perspective{.fov = perspective.yfov,
|
||||
.aspect_ratio =
|
||||
perspective.aspectRatio,
|
||||
.near = perspective.znear,
|
||||
.far = perspective.zfar};
|
||||
registry.emplace<Camera>(entity, Camera{.projection = camera_perspective});
|
||||
}
|
||||
|
||||
// Spawn child nodes
|
||||
for (auto const &child : node.children) {
|
||||
auto child_entity = spawn_node(child, entity);
|
||||
child_entities.push_back(child_entity);
|
||||
}
|
||||
|
||||
registry.emplace<Children>(entity, Children{.children = child_entities});
|
||||
return entity;
|
||||
};
|
||||
|
||||
spawn_node(node, {});
|
||||
}
|
||||
|
||||
auto camera_view = registry.view<Camera const>();
|
||||
if (camera_view.empty()) {
|
||||
// Spawn default camera
|
||||
auto entity = registry.create();
|
||||
registry.emplace<Name>(entity, "Camera");
|
||||
registry.emplace<Transform>(entity, Transform{.translation = Camera::DEFAULT_POSITION});
|
||||
registry.emplace<GlobalTransform>(entity, GlobalTransform{});
|
||||
registry.emplace<Camera>(entity, Camera{.projection = Camera::Perspective{}});
|
||||
}
|
||||
|
||||
entt::hashed_string scene_hash(gltf_scene.name.c_str());
|
||||
entt::resource<Scene> scene_resource =
|
||||
scene_cache.load(scene_hash, Scene{std::move(registry)}).first->second;
|
||||
scenes.push_back(scene_resource);
|
||||
}
|
||||
|
||||
// Default scene
|
||||
entt::resource<GltfScene> default_scene = [&gltf, &scenes]() {
|
||||
auto default_scene = [&gltf, &scenes]() -> std::optional<entt::resource<Scene>> {
|
||||
if (gltf.scene != -1) {
|
||||
return scenes.at(gltf.scene);
|
||||
}
|
||||
|
||||
return scenes.at(0);
|
||||
return {};
|
||||
}();
|
||||
|
||||
return std::make_shared<Gltf>(Gltf{.materials = std::move(materials),
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "image.h"
|
||||
#include "material.h"
|
||||
#include "mesh.h"
|
||||
#include "gltf.h"
|
||||
#include "transform.h"
|
||||
|
||||
#include <entt/entt.hpp>
|
||||
@@ -11,41 +9,6 @@
|
||||
|
||||
static constexpr auto MAX_SIZE = 512 * 1024 * 1024;
|
||||
|
||||
struct GltfPrimitive
|
||||
{
|
||||
entt::resource<Mesh> mesh;
|
||||
entt::resource<Material> material;
|
||||
};
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
struct GltfLoader final
|
||||
{
|
||||
using result_type = std::shared_ptr<Gltf>;
|
||||
@@ -56,8 +19,8 @@ struct GltfLoader final
|
||||
entt::resource_cache<Material> &material_cache;
|
||||
entt::resource_cache<Mesh> &mesh_cache;
|
||||
entt::resource_cache<Shader, ShaderLoader> &shader_cache;
|
||||
entt::resource_cache<Scene> &scene_cache;
|
||||
|
||||
entt::resource_cache<GltfMesh> &gltf_mesh_cache;
|
||||
entt::resource_cache<GltfNode> &gltf_node_cache;
|
||||
entt::resource_cache<GltfScene> &gltf_scene_cache;
|
||||
};
|
||||
|
||||
75
src/scene.cpp
Normal file
75
src/scene.cpp
Normal file
@@ -0,0 +1,75 @@
|
||||
#include "scene.h"
|
||||
#include "camera.h"
|
||||
#include "mesh.h"
|
||||
#include "name.h"
|
||||
#include "relationship.h"
|
||||
#include "shader.h"
|
||||
#include "transform.h"
|
||||
#include "util/Log.h"
|
||||
|
||||
Scene::Scene(entt::registry registry) : m_registry(std::move(registry))
|
||||
{
|
||||
auto mesh_view = m_registry.view<entt::resource<Mesh>>();
|
||||
for (auto [entity, mesh] : mesh_view.each()) {
|
||||
m_registry.emplace<GpuMesh>(entity, GpuMesh(mesh));
|
||||
|
||||
// Remove Mesh resource as it is no longer needed.
|
||||
m_registry.erase<entt::resource<Mesh>>(entity);
|
||||
}
|
||||
|
||||
auto material_view = m_registry.view<entt::resource<Material>>();
|
||||
for (auto [entity, material] : material_view.each()) {
|
||||
m_registry.emplace<GpuMaterial>(entity, GpuMaterial(material));
|
||||
|
||||
// Remove Material resource as it is no longer needed.
|
||||
m_registry.erase<entt::resource<Material>>(entity);
|
||||
}
|
||||
}
|
||||
|
||||
void Scene::update(std::chrono::duration<float> delta,
|
||||
KeyInput const &key_input,
|
||||
MouseCursorInput const &mouse_cursor_input,
|
||||
float aspect_ratio,
|
||||
bool cursor_catched)
|
||||
{
|
||||
{
|
||||
// Update GlobalTransform components
|
||||
// TODO: Only do this when the Transform changed.
|
||||
auto root_transform_view =
|
||||
m_registry.view<Transform const, GlobalTransform>(entt::exclude<Parent>);
|
||||
auto transform_view = m_registry.view<Transform const, GlobalTransform, Parent const>();
|
||||
|
||||
for (auto [entity, transform, global_transform] : root_transform_view.each()) {
|
||||
global_transform = transform;
|
||||
|
||||
auto parent_global_transform = global_transform;
|
||||
if (auto *children = m_registry.try_get<Children>(entity)) {
|
||||
for (auto child : children->children) {
|
||||
std::function<void(entt::entity entity,
|
||||
GlobalTransform parent_global_transform)>
|
||||
transform_propagate = [this, &transform_propagate, &transform_view](
|
||||
entt::entity entity,
|
||||
GlobalTransform parent_global_transform) {
|
||||
auto [transform, global_transform, parent] = transform_view.get(entity);
|
||||
global_transform.transform = parent_global_transform.transform *
|
||||
GlobalTransform(transform).transform;
|
||||
|
||||
if (auto *children = m_registry.try_get<Children>(entity)) {
|
||||
for (auto child : children->children) {
|
||||
transform_propagate(child, global_transform);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
transform_propagate(child, parent_global_transform);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Camera::aspect_ratio_update(m_registry, aspect_ratio);
|
||||
Camera::keyboard_movement(m_registry, key_input, delta);
|
||||
if (cursor_catched) {
|
||||
Camera::mouse_orientation(m_registry, mouse_cursor_input);
|
||||
}
|
||||
}
|
||||
26
src/scene.h
Normal file
26
src/scene.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include "input.h"
|
||||
#include "gltf.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <entt/entt.hpp>
|
||||
|
||||
class Shader;
|
||||
|
||||
class Scene
|
||||
{
|
||||
public:
|
||||
Scene(entt::registry registry);
|
||||
|
||||
void update(std::chrono::duration<float> delta_time,
|
||||
KeyInput const &key_input,
|
||||
MouseCursorInput const &mouse_cursor_input,
|
||||
float aspect_ratio,
|
||||
bool cursor_catched);
|
||||
|
||||
auto registry() -> entt::registry & { return m_registry; }
|
||||
|
||||
private:
|
||||
entt::registry m_registry;
|
||||
};
|
||||
@@ -33,11 +33,11 @@ Shader::Shader(std::string_view name, std::filesystem::path const &directory)
|
||||
}
|
||||
|
||||
#ifdef NDEBUG
|
||||
glDetachShader(m_ShaderId, vertexShader);
|
||||
glDetachShader(m_ShaderId, fragmentShader);
|
||||
glDetachShader(program, vertex_shader);
|
||||
glDetachShader(program, fragment_shader);
|
||||
|
||||
glDeleteShader(vertexShader);
|
||||
glDeleteShader(fragmentShader);
|
||||
glDeleteShader(vertex_shader);
|
||||
glDeleteShader(fragment_shader);
|
||||
#endif
|
||||
|
||||
Log::logger().trace(R"(Loaded Shader "{}")", name);
|
||||
|
||||
Reference in New Issue
Block a user