diff --git a/libs/gfx/include/psemek/gfx/gltf_mesh.hpp b/libs/gfx/include/psemek/gfx/gltf_mesh.hpp index 83a25587..a9e379b8 100644 --- a/libs/gfx/include/psemek/gfx/gltf_mesh.hpp +++ b/libs/gfx/include/psemek/gfx/gltf_mesh.hpp @@ -1,40 +1,30 @@ #pragma once -#include -#include #include +#include +#include #include -#include -#include +#include namespace psemek::gfx { struct gltf_mesh { - struct mesh + struct primitive { - struct primitive - { - gfx::array vao; - std::optional material; - std::size_t index_count; - std::size_t index_offset; - GLenum index_type; - - void draw() const; - }; - - std::vector primitives; + gfx::drawable * drawable; + std::optional material; }; - std::vector buffers; + virtual gltf_asset::material const & material(std::size_t index) const = 0; + virtual gfx::texture_2d const & texture(std::size_t index) const = 0; + virtual util::span mesh(std::string_view name) const = 0; - std::unordered_map meshes; - - gltf_mesh() = default; - gltf_mesh(gltf_asset const & asset, std::vector> const & buffers); + virtual ~gltf_mesh() {} }; + std::unique_ptr make_gltf_mesh(gltf_asset const & asset, std::function(std::string const &)> uri_loader); + } diff --git a/libs/gfx/source/gltf_mesh.cpp b/libs/gfx/source/gltf_mesh.cpp index ba9cd233..fc1e4540 100644 --- a/libs/gfx/source/gltf_mesh.cpp +++ b/libs/gfx/source/gltf_mesh.cpp @@ -1,67 +1,132 @@ #include +#include +#include +#include +#include #include namespace psemek::gfx { - void gltf_mesh::mesh::primitive::draw() const + namespace { - vao.bind(); - gl::DrawElements(gl::TRIANGLES, index_count, index_type, reinterpret_cast(index_offset)); - } - gltf_mesh::gltf_mesh(gltf_asset const & asset, std::vector> const & buffers) - { - if (buffers.size() != asset.buffers.size()) - throw std::runtime_error("wrong number of glTF buffers"); - - for (auto buffer : buffers) - this->buffers.emplace_back().load(buffer.data(), buffer.size(), gl::STATIC_DRAW); - - for (auto const & node : asset.nodes) + struct drawable_impl + : drawable { - auto & target_mesh = meshes[node.name]; + gfx::array vao; + std::size_t index_count; + std::size_t index_offset; + GLenum index_type; - auto const & mesh = asset.meshes[node.mesh]; - - for (auto const & primitive : mesh.primitives) + void draw() const override { - auto & target_primitive = target_mesh.primitives.emplace_back(); - target_primitive.material = primitive.material; + vao.bind(); + gl::DrawElements(gl::TRIANGLES, index_count, index_type, reinterpret_cast(index_offset)); + } + }; - target_primitive.vao.bind(); + struct gltf_mesh_impl + : gltf_mesh + { + gltf_mesh_impl(gltf_asset const & asset, std::function(std::string const &)> uri_loader); + gltf_asset::material const & material(std::size_t index) const override + { + return materials_[index]; + } + + gfx::texture_2d const & texture(std::size_t index) const override + { + return textures_[index]; + } + + util::span mesh(std::string_view name) const override + { + return meshes_.at(name); + } + + private: + std::vector materials_; + std::vector textures_; + std::vector buffers_; + std::vector> drawables_; + std::unordered_map> meshes_; + }; + + gltf_mesh_impl::gltf_mesh_impl(gltf_asset const & asset, std::function(std::string const &)> uri_loader) + { + materials_ = asset.materials; + + for (auto const & buffer : asset.buffers) + { + auto data = uri_loader(buffer.uri); + buffers_.emplace_back().load(data.data(), data.size(), gl::STATIC_DRAW); + } + + for (auto const & texture : asset.textures) + { + auto data = uri_loader(texture.uri); + auto & target = textures_.emplace_back(); + target.load(gfx::read_png(io::memory_istream(data.data(), data.data() + data.size()))); + target.linear_mipmap_filter(); + target.anisotropy(); + target.generate_mipmap(); + } + + for (auto const & node : asset.nodes) + { + auto & target_mesh = meshes_[node.name]; + + auto const & mesh = asset.meshes[node.mesh]; + + for (auto const & primitive : mesh.primitives) { - auto const & indices_accessor = asset.accessors[primitive.indices]; - auto const & indices_view = asset.buffer_views[indices_accessor.buffer_view]; + drawables_.push_back(std::make_unique()); + auto & drawable = *drawables_.back(); - gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, this->buffers[indices_view.buffer].id()); - target_primitive.index_offset = indices_view.offset; - target_primitive.index_type = indices_accessor.component_type; - target_primitive.index_count = indices_accessor.count; - } + drawable.vao.bind(); - std::pair> attributes[3] = - { - {0, primitive.position}, - {1, primitive.normal}, - {2, primitive.texcoord}, - }; + { + auto const & indices_accessor = asset.accessors[primitive.indices]; + auto const & indices_view = asset.buffer_views[indices_accessor.buffer_view]; - for (auto const & attribute : attributes) - { - if (!attribute.second) continue; + gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, buffers_[indices_view.buffer].id()); + drawable.index_offset = indices_view.offset; + drawable.index_type = indices_accessor.component_type; + drawable.index_count = indices_accessor.count; + } - auto const & accessor = asset.accessors[*attribute.second]; - auto const & view = asset.buffer_views[accessor.buffer_view]; + std::pair> attributes[3] = + { + {0, primitive.position}, + {1, primitive.normal}, + {2, primitive.texcoord}, + }; - gl::BindBuffer(gl::ARRAY_BUFFER, this->buffers[view.buffer].id()); - gl::EnableVertexAttribArray(attribute.first); - gl::VertexAttribPointer(attribute.first, accessor.type, accessor.component_type, accessor.normalized, 0, reinterpret_cast(view.offset)); + for (auto const & attribute : attributes) + { + if (!attribute.second) continue; + + auto const & accessor = asset.accessors[*attribute.second]; + auto const & view = asset.buffer_views[accessor.buffer_view]; + + gl::BindBuffer(gl::ARRAY_BUFFER, buffers_[view.buffer].id()); + gl::EnableVertexAttribArray(attribute.first); + gl::VertexAttribPointer(attribute.first, accessor.type, accessor.component_type, accessor.normalized, 0, reinterpret_cast(view.offset)); + } + + target_mesh.push_back({&drawable, primitive.material}); } } } + + } + + std::unique_ptr make_gltf_mesh(gltf_asset const & asset, std::function(std::string const &)> uri_loader) + { + return std::make_unique(asset, std::move(uri_loader)); } }