From fbb6890f71202e9c7d44bde17378b33433d40a02 Mon Sep 17 00:00:00 2001 From: Derek Christ Date: Mon, 17 Oct 2022 21:28:17 +0200 Subject: [PATCH] Implement mesh and material loading in glTF loader --- .clang-format | 2 +- src/CMakeLists.txt | 2 +- src/Controller.cpp | 63 ++++++---- src/Helper.cpp | 33 ++--- src/Helper.h | 9 +- src/Mesh.cpp | 8 +- src/Mesh.h | 4 +- src/gltf_loader.cpp | 271 ++++++++++++++++++++++++++++++++++------ src/gltf_loader.h | 22 +++- src/material.h | 5 +- src/mesh.h | 37 ++++++ src/resources/Model.cpp | 4 +- src/resources/Model.h | 6 +- 13 files changed, 361 insertions(+), 105 deletions(-) create mode 100644 src/mesh.h diff --git a/.clang-format b/.clang-format index 4770ca3..38055b6 100644 --- a/.clang-format +++ b/.clang-format @@ -8,7 +8,7 @@ AlwaysBreakTemplateDeclarations: 'true' BreakBeforeBraces: Mozilla BinPackParameters: 'false' BinPackArguments: 'false' -ColumnLimit: '120' +ColumnLimit: '100' ConstructorInitializerAllOnOneLineOrOnePerLine: 'true' IndentWidth: '4' PointerAlignment: Right diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 90aa569..c6bf870 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -24,13 +24,13 @@ target_compile_definitions(fever_engine PRIVATE SPDLOG_FMT_EXTERNAL) target_link_libraries( fever_engine PUBLIC glad - glm glfw EnTT::EnTT fmt pthread spdlog fx-gltf::fx-gltf + nlohmann_json::nlohmann_json stb sail ) diff --git a/src/Controller.cpp b/src/Controller.cpp index aecad1b..1d82492 100644 --- a/src/Controller.cpp +++ b/src/Controller.cpp @@ -13,35 +13,37 @@ #include #include +#include #include #include #include #include #include #include +#include #include -struct MyRes -{ - MyRes(std::filesystem::path const &path, int) - { - std::cout << "Path: " << std::filesystem::weakly_canonical(path) << std::endl; - }; - int x = 0; -}; using namespace entt::literals; Controller::Controller() : m_gameWindow(std::make_shared()), - m_postProcessFrameBuffer( - m_gameWindow->dimensions().first, m_gameWindow->dimensions().second, postProcessingProgram) + m_postProcessFrameBuffer(m_gameWindow->dimensions().first, + m_gameWindow->dimensions().second, + postProcessingProgram) { - gltf_loader()("Lantern/glTF-Binary/Lantern.glb"); + GltfLoader loader; + entt::resource_cache gltf_resource_cache; + entt::resource 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}; + fx::gltf::ReadQuotas read_quotas{.MaxFileSize = 512 * 1024 * 1024, + .MaxBufferByteLength = 512 * 1024 * 1024}; // auto gltf_path = std::filesystem::path("Lantern/glTF/Lantern.gltf"); - auto gltf_path = std::filesystem::path("WaterBottle/glTF/WaterBottle.gltf"); + // auto gltf_path = std::filesystem::path("WaterBottle/glTF/WaterBottle.gltf"); + auto gltf_path = std::filesystem::path("ABeautifulGame.glb"); auto gltf = [&]() { if (gltf_path.extension() == ".gltf") { return fx::gltf::LoadFromText(gltf_path, read_quotas); @@ -73,7 +75,7 @@ Controller::Controller() std::vector models; for (auto const &mesh : gltf.meshes) { - std::vector meshes; + std::vector meshes; for (auto const &primitive : mesh.primitives) { auto const &material = gltf.materials.at(primitive.material); auto baseColorTexture = material.pbrMetallicRoughness.baseColorTexture.index; @@ -110,7 +112,7 @@ Controller::Controller() primitive_textures.emplace_back(m_textures.at(normalTexture)); } - meshes.emplace_back(Mesh({primitive, gltf, locations}, primitive_textures)); + meshes.emplace_back(Mesh_({primitive, gltf, locations}, primitive_textures)); } models.emplace_back(Model(mesh.name, std::move(meshes))); } @@ -122,16 +124,18 @@ Controller::Controller() continue; } - ModelEntity entity( - Entity::Prototype(node.name, {}, {}, 1.0F), m_models[static_cast(node.mesh)], defaultProgram); + ModelEntity entity(Entity::Prototype(node.name, {}, {}, 1.0F), + m_models[static_cast(node.mesh)], + defaultProgram); if (!node.translation.empty()) { - entity.setPosition(glm::vec3(node.translation[0], node.translation[1], node.translation[2])); + entity.setPosition( + glm::vec3(node.translation[0], node.translation[1], node.translation[2])); } if (!node.rotation.empty()) { - entity.setRotation( - glm::eulerAngles(glm::quat(node.rotation[3], node.rotation[0], node.rotation[1], node.rotation[2]))); + entity.setRotation(glm::eulerAngles( + glm::quat(node.rotation[3], node.rotation[0], node.rotation[1], node.rotation[2]))); } if (!node.scale.empty()) { @@ -139,13 +143,17 @@ Controller::Controller() } entities.push_back(std::move(entity)); + } + for (auto const &node : gltf.nodes) { for (auto const &child : node.children) { if (!node.translation.empty()) { - entities[child].translate(glm::vec3(node.translation[0], node.translation[1], node.translation[2])); + entities[child].translate( + glm::vec3(node.translation[0], node.translation[1], node.translation[2])); } } } + m_entities = std::move(entities); } @@ -155,11 +163,13 @@ void Controller::run() m_camera->translate(glm::vec3(0., .5, 2.)); DirectionalLight directional_light( - DirectionalLight::Prototype("", glm::vec3(-0.2, -1.0, -0.3), glm::vec3(1.0f), 5.f), &defaultProgram); + DirectionalLight::Prototype("", glm::vec3(-0.2, -1.0, -0.3), glm::vec3(1.0f), 5.f), + &defaultProgram); directional_light.setActive(true); - PointLight point_light(PointLight::Prototype("", "", glm::vec3(4.0, 1.0, 6.0), glm::vec3(1.0F), 3.0F), - &defaultProgram); + PointLight point_light( + PointLight::Prototype("", "", glm::vec3(4.0, 1.0, 6.0), glm::vec3(1.0F), 3.0F), + &defaultProgram); point_light.setActive(true); Log::logger().info("Startup complete. Enter game loop."); @@ -222,7 +232,10 @@ void Controller::limit_framerate() double frameTime = 1 / (double)MAX_FPS; if (frameTime > lastTime) { - Helper::sleep(static_cast(frameTime - lastTime) * 1000000); + static constexpr auto MICROSECONDS_PER_SECOND = 1'000'000; + auto sleep_time_us = + static_cast((frameTime - lastTime) * MICROSECONDS_PER_SECOND); + std::this_thread::sleep_for(std::chrono::microseconds(sleep_time_us)); } m_deltaTime = glfwGetTime() - startingTime; diff --git a/src/Helper.cpp b/src/Helper.cpp index 96587d7..2543616 100644 --- a/src/Helper.cpp +++ b/src/Helper.cpp @@ -3,26 +3,13 @@ #include -#ifdef __linux__ -#include -#endif -#ifdef _WIN32 -#include -#endif - -void Helper::sleep(uint32_t us) -{ -#ifdef __linux__ - usleep(us); -#endif -#ifdef _WIN32 - // I don't know why I have to divide by 2000 and not 1000 but this way it works even on Windows - Sleep(us / 2000); -#endif -} - -void Helper::gl_debug_callback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, - const GLchar *message, const void *userParam) +void Helper::gl_debug_callback(GLenum source, + GLenum type, + GLuint id, + GLenum severity, + GLsizei length, + const GLchar *message, + const void *userParam) { (void)length; (void)userParam; @@ -128,7 +115,11 @@ void Helper::gl_debug_callback(GLenum source, GLenum type, GLuint id, GLenum sev "Type: {}\n" "ID: {}\n" "Severity: {}\n", - _message, _source, _type, id, _severity); + _message, + _source, + _type, + id, + _severity); } Helper::Timer::Timer(const std::string &name) : m_name(name) diff --git a/src/Helper.h b/src/Helper.h index 23e59a8..338d50d 100644 --- a/src/Helper.h +++ b/src/Helper.h @@ -6,9 +6,12 @@ #include namespace Helper { -void sleep(uint32_t us); - -void gl_debug_callback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, +void gl_debug_callback(GLenum source, + GLenum type, + GLuint id, + GLenum severity, + GLsizei length, + const GLchar *message, const void *userParam); class Timer diff --git a/src/Mesh.cpp b/src/Mesh.cpp index f2febf8..63caebd 100644 --- a/src/Mesh.cpp +++ b/src/Mesh.cpp @@ -5,12 +5,12 @@ #include "resources/Texture.h" #include -Mesh::Mesh(VertexArray vertexArray, std::vector> textures) +Mesh_::Mesh_(VertexArray vertexArray, std::vector> textures) : m_vertexArray(std::move(vertexArray)), m_textures(std::move(textures)) { } -void Mesh::draw(ShaderProgram const &shaderProgram) const +void Mesh_::draw(ShaderProgram const &shaderProgram) const { // Bind all textures in order to its texture unit std::size_t textureNum = 0; @@ -31,7 +31,7 @@ void Mesh::draw(ShaderProgram const &shaderProgram) const } } -void Mesh::drawWithoutTextures() const +void Mesh_::drawWithoutTextures() const { m_vertexArray.bind(); @@ -41,7 +41,7 @@ void Mesh::drawWithoutTextures() const VertexArray::unbind(); } -auto Mesh::getVertexArray() -> VertexArray * +auto Mesh_::getVertexArray() -> VertexArray * { return &m_vertexArray; } diff --git a/src/Mesh.h b/src/Mesh.h index 0375017..ea09962 100644 --- a/src/Mesh.h +++ b/src/Mesh.h @@ -7,10 +7,10 @@ class ShaderProgram; -class Mesh +class Mesh_ { public: - Mesh(VertexArray vertexArray, std::vector> textures); + Mesh_(VertexArray vertexArray, std::vector> textures); void draw(ShaderProgram const &shaderProgram) const; void drawWithoutTextures() const; diff --git a/src/gltf_loader.cpp b/src/gltf_loader.cpp index 175d01d..ad61645 100644 --- a/src/gltf_loader.cpp +++ b/src/gltf_loader.cpp @@ -1,10 +1,35 @@ #include "gltf_loader.h" +#include "util/Log.h" -static void load_texture(fx::gltf::Texture const &texture, - fx::gltf::Document const &gltf, - std::filesystem::path const &document_path, - Image::ColorFormat colorFormat, - entt::resource_cache &image_cache) +template +static auto create_vertex_attribute_data(std::span vertex_attribute_data) + -> VertexAttributeData +{ + T attribute_data; + attribute_data.reserve(vertex_attribute_data.size_bytes()); + + std::memcpy( + attribute_data.data(), vertex_attribute_data.data(), vertex_attribute_data.size_bytes()); + + return VertexAttributeData{.values = std::move(attribute_data)}; +} + +template +static auto create_indices(std::span gltf_index_data) -> Indices +{ + T index_data; + index_data.reserve(gltf_index_data.size_bytes()); + + std::memcpy(index_data.data(), gltf_index_data.data(), gltf_index_data.size_bytes()); + + return Indices{.values = std::move(index_data)}; +} + +static auto load_texture(fx::gltf::Texture const &texture, + fx::gltf::Document const &gltf, + std::filesystem::path const &document_path, + Image::ColorFormat colorFormat, + entt::resource_cache &image_cache) -> entt::resource { auto const &gltf_image = gltf.images.at(texture.source); auto const base_directory = document_path.parent_path(); @@ -13,53 +38,194 @@ static void load_texture(fx::gltf::Texture const &texture, auto const &image_buffer_view = gltf.bufferViews.at(gltf_image.bufferView); auto const &image_buffer = gltf.buffers.at(image_buffer_view.buffer); - std::string const image_name = document_path.string() + ".image." + std::to_string(texture.source); - entt::hashed_string image_hash(image_name.c_str()); + std::string const image_name = + document_path.string() + ".image." + std::to_string(texture.source); + entt::hashed_string const image_hash(image_name.c_str()); - image_cache.load( - image_hash, - std::span{image_buffer.data}.subspan(image_buffer_view.byteOffset, image_buffer_view.byteLength), - colorFormat); - } else { - auto const image_path = base_directory / gltf_image.uri; - std::size_t const image_size = std::filesystem::file_size(image_path); - auto image_ifstream = std::ifstream(image_path, std::ios::binary); - - std::vector image_data; - image_data.reserve(image_size); - - std::copy(std::istreambuf_iterator(image_ifstream), - std::istreambuf_iterator(), - std::back_inserter(image_data)); - - entt::hashed_string image_hash(image_path.c_str()); - - image_cache.load(image_hash, image_data, colorFormat); + return image_cache + .load(image_hash, + std::span{image_buffer.data}.subspan(image_buffer_view.byteOffset, + image_buffer_view.byteLength), + colorFormat) + .first->second; } + + auto const image_path = base_directory / gltf_image.uri; + std::size_t const image_size = std::filesystem::file_size(image_path); + auto image_ifstream = std::ifstream(image_path, std::ios::binary); + + std::vector image_data; + image_data.reserve(image_size); + + std::copy(std::istreambuf_iterator(image_ifstream), + std::istreambuf_iterator(), + std::back_inserter(image_data)); + + entt::hashed_string const image_hash(image_path.c_str()); + + return image_cache.load(image_hash, image_data, colorFormat).first->second; } -static void load_material(fx::gltf::Material const &material, - fx::gltf::Document const &gltf, - std::filesystem::path const &document_path, - entt::resource_cache &image_cache) +static auto load_material(fx::gltf::Material const &material, + fx::gltf::Document const &gltf, + std::filesystem::path const &document_path, + entt::resource_cache &material_cache, + entt::resource_cache &image_cache) -> entt::resource { auto base_color_texture_id = material.pbrMetallicRoughness.baseColorTexture.index; auto normal_texture_id = material.normalTexture.index; + std::optional> base_color_image; if (base_color_texture_id != -1) { auto const &base_color_texture = gltf.textures.at(base_color_texture_id); - load_texture(base_color_texture, gltf, document_path, Image::ColorFormat::SRGB, image_cache); + base_color_image = load_texture( + base_color_texture, gltf, document_path, Image::ColorFormat::SRGB, image_cache); } + std::optional> normal_map_image; if (normal_texture_id != -1) { auto const &normal_texture = gltf.textures.at(normal_texture_id); - load_texture(normal_texture, gltf, document_path, Image::ColorFormat::RGB, image_cache); + normal_map_image = + load_texture(normal_texture, gltf, document_path, Image::ColorFormat::RGB, image_cache); } + + entt::hashed_string material_hash(material.name.c_str()); + return material_cache + .load(material_hash, + Material{.base_color_texture = base_color_image, + .normal_map_texture = normal_map_image}) + .first->second; } -auto gltf_loader::operator()(std::filesystem::path const &document_path) -> result_type +static auto load_attribute(std::string_view attribute_name, + uint32_t attribute_id, + fx::gltf::Document const &gltf) -> std::optional { - fx::gltf::ReadQuotas const read_quotas{.MaxFileSize = MAX_SIZE, .MaxBufferByteLength = MAX_SIZE}; + auto const &attribute_accessor = gltf.accessors.at(attribute_id); + + if (attribute_accessor.componentType != fx::gltf::Accessor::ComponentType::Float) { + Log::logger().critical("Only float attributes supported!"); + std::terminate(); + } + + auto vertex_attribute_id = [attribute_name]() -> std::optional { + if (attribute_name == "POSITION") { + return 0; + } + if (attribute_name == "TEXCOORD_0") { + return 1; + } + if (attribute_name == "NORMAL") { + return 2; + } + if (attribute_name == "TANGENT") { + return 3; + } + + return {}; + }(); + + // Skip unsupported attribute + if (!vertex_attribute_id.has_value()) { + return {}; + } + + auto const &buffer_view = gltf.bufferViews.at(attribute_accessor.bufferView); + auto const &buffer = gltf.buffers.at(buffer_view.buffer); + + std::span buffer_span(buffer.data); + std::span vertex_attribute_data_span = + buffer_span.subspan(buffer_view.byteOffset, buffer_view.byteLength); + + auto vertex_attribute_data = [vertex_attribute_data_span, &attribute_accessor]() { + if (attribute_accessor.type == fx::gltf::Accessor::Type::Scalar) { + return create_vertex_attribute_data( + vertex_attribute_data_span); + } + if (attribute_accessor.type == fx::gltf::Accessor::Type::Vec2) { + return create_vertex_attribute_data( + vertex_attribute_data_span); + } + if (attribute_accessor.type == fx::gltf::Accessor::Type::Vec3) { + return create_vertex_attribute_data( + vertex_attribute_data_span); + } + if (attribute_accessor.type == fx::gltf::Accessor::Type::Vec4) { + return create_vertex_attribute_data( + vertex_attribute_data_span); + } + + Log::logger().critical("Unsupported vertex attribute type!"); + std::terminate(); + }(); + + return std::move(vertex_attribute_data); +} + +auto load_gltf_primitive(fx::gltf::Primitive const &gltf_primitive, + fx::gltf::Document const &gltf, + std::filesystem::path const &document_path, + unsigned primitive_id, + entt::resource_cache &material_cache, + entt::resource_cache &mesh_cache) -> GltfPrimitive +{ + // Load attributes + std::map attributes; + for (auto const &attribute : gltf_primitive.attributes) { + auto vertex_attribute_data = load_attribute(attribute.first, attribute.second, gltf); + + if (!vertex_attribute_data.has_value()) { + continue; + } + + attributes.emplace(attribute.second, std::move(vertex_attribute_data.value())); + } + + // Load indices + auto const &indices_accessor = gltf.accessors.at(gltf_primitive.indices); + auto const &indices_buffer_view = gltf.bufferViews.at(indices_accessor.bufferView); + auto const &indices_buffer = gltf.buffers.at(indices_buffer_view.buffer); + + std::span indices_buffer_span(indices_buffer.data); + std::span indices_data_span = + indices_buffer_span.subspan(indices_buffer_view.byteOffset, indices_buffer_view.byteLength); + + auto indices = [indices_data_span, &indices_accessor]() { + if (indices_accessor.componentType == fx::gltf::Accessor::ComponentType::UnsignedByte) { + return create_indices(indices_data_span); + } + + if (indices_accessor.componentType == fx::gltf::Accessor::ComponentType::UnsignedShort) { + return create_indices(indices_data_span); + } + if (indices_accessor.componentType == fx::gltf::Accessor::ComponentType::UnsignedInt) { + return create_indices(indices_data_span); + } + + Log::logger().critical("Unsupported indices type!"); + std::terminate(); + }(); + + std::string const mesh_name = + document_path.string() + ".primitive." + std::to_string(primitive_id); + entt::hashed_string const mesh_hash(mesh_name.c_str()); + + entt::resource mesh = + mesh_cache.load(mesh_hash, Mesh{.attributes = attributes, .indices = indices}) + .first->second; + + // Get material by name + auto const &gltf_material = gltf.materials.at(gltf_primitive.material); + entt::hashed_string material_hash(gltf_material.name.c_str()); + entt::resource material = material_cache[material_hash]; + + return GltfPrimitive{.mesh = mesh, .material = material}; +} + +auto GltfLoader::operator()(std::filesystem::path const &document_path) -> result_type +{ + fx::gltf::ReadQuotas const read_quotas{.MaxFileSize = MAX_SIZE, + .MaxBufferByteLength = MAX_SIZE}; fx::gltf::Document gltf = [&]() { if (document_path.extension() == ".gltf") { @@ -72,12 +238,39 @@ auto gltf_loader::operator()(std::filesystem::path const &document_path) -> resu // Load here all the rest... auto const base_directory = document_path.parent_path(); - // Load textures - for (auto const &material : gltf.materials) { - load_material(material, gltf, document_path, image_cache); + // Load materials + std::vector> materials; + for (auto const &gltf_material : gltf.materials) { + entt::resource material = + load_material(gltf_material, gltf, document_path, material_cache, image_cache); + materials.push_back(material); } // Load meshes + std::vector> gltf_meshes; + gltf_meshes.reserve(gltf.meshes.size()); - return std::make_shared(std::move(gltf)); -} + for (auto const &gltf_mesh : gltf.meshes) { + // Load primitives + unsigned primitive_count = 0; + + std::vector primitives; + primitives.reserve(gltf_mesh.primitives.size()); + + for (auto const &gltf_primitive : gltf_mesh.primitives) { + primitives.push_back(load_gltf_primitive( + gltf_primitive, gltf, document_path, primitive_count, material_cache, mesh_cache)); + } + + entt::hashed_string gltf_mesh_hash(gltf_mesh.name.c_str()); + entt::resource gltf_mesh_resource = + gltf_mesh_cache.load(gltf_mesh_hash, GltfMesh{.primitives = std::move(primitives)}) + .first->second; + + gltf_meshes.push_back(gltf_mesh_resource); + } + + return std::make_shared(Gltf{.materials = std::move(materials), + .meshes = std::move(gltf_meshes), + .document = std::move(gltf)}); +} \ No newline at end of file diff --git a/src/gltf_loader.h b/src/gltf_loader.h index c699f17..f8d8d24 100644 --- a/src/gltf_loader.h +++ b/src/gltf_loader.h @@ -2,6 +2,7 @@ #include "image.h" #include "material.h" +#include "mesh.h" #include #include @@ -9,16 +10,33 @@ static constexpr auto MAX_SIZE = 512 * 1024 * 1024; +struct GltfPrimitive +{ + entt::resource mesh; + entt::resource material; +}; + +struct GltfMesh +{ + std::vector primitives; +}; + +// Move to own file. struct Gltf { std::vector> materials; + std::vector> meshes; + fx::gltf::Document document; }; -struct gltf_loader final +struct GltfLoader final { - using result_type = std::shared_ptr; + using result_type = std::shared_ptr; auto operator()(std::filesystem::path const &document_path) -> result_type; entt::resource_cache image_cache; + entt::resource_cache material_cache; + entt::resource_cache mesh_cache; + entt::resource_cache gltf_mesh_cache; }; diff --git a/src/material.h b/src/material.h index 1dbfc85..27dd731 100644 --- a/src/material.h +++ b/src/material.h @@ -3,9 +3,10 @@ #include "image.h" #include +#include struct Material { - entt::resource base_color_texture; - entt::resource normal_map_texture; + std::optional> base_color_texture; + std::optional> normal_map_texture; }; diff --git a/src/mesh.h b/src/mesh.h new file mode 100644 index 0000000..bb07b1a --- /dev/null +++ b/src/mesh.h @@ -0,0 +1,37 @@ +#pragma once + +#include +#include +#include +#include + +struct VertexAttributeData +{ + using Scalar = std::vector; + using Vec2 = std::vector>; + using Vec3 = std::vector>; + using Vec4 = std::vector>; + + // Possible improvement: + // Instead of copying the data into a vector, create a span that + // points into the original data. For that, the glTF file has to + // persist until the data is uploaded to the GPU. + std::variant values; +}; + +struct Indices +{ + using UnsignedByte = std::vector; + using UnsignedShort = std::vector; + using UnsignedInt = std::vector; + + std::variant values; +}; + +struct Mesh +{ + using VertexAttributeId = std::size_t; + + std::map attributes; + Indices indices; +}; diff --git a/src/resources/Model.cpp b/src/resources/Model.cpp index 586b15c..65c2b81 100644 --- a/src/resources/Model.cpp +++ b/src/resources/Model.cpp @@ -2,7 +2,7 @@ #include "Texture.h" #include "../util/Log.h" -Model::Model(std::string_view name, std::vector meshes) : m_meshes(std::move(meshes)), m_name(name) +Model::Model(std::string_view name, std::vector meshes) : m_meshes(std::move(meshes)), m_name(name) { Log::logger().trace(R"(Loaded model "{}".)", name); } @@ -23,7 +23,7 @@ void Model::drawWithoutTextures() const } } -auto Model::getMesh(unsigned int index) -> Mesh * +auto Model::getMesh(unsigned int index) -> Mesh_ * { return &m_meshes[index]; } diff --git a/src/resources/Model.h b/src/resources/Model.h index ed3ab1a..16d08c4 100644 --- a/src/resources/Model.h +++ b/src/resources/Model.h @@ -9,15 +9,15 @@ class Model { public: - Model(std::string_view name, std::vector meshes); + Model(std::string_view name, std::vector meshes); void draw(ShaderProgram const &shaderProgram) const; void drawWithoutTextures() const; - auto getMesh(unsigned int index) -> Mesh *; // TODO... + auto getMesh(unsigned int index) -> Mesh_ *; // TODO... private: - std::vector m_meshes; + std::vector m_meshes; std::vector m_textures; std::string m_name;