psemek/libs/gfx/source/gltf_mesh.cpp

138 lines
3.7 KiB
C++

#include <psemek/gfx/gltf_mesh.hpp>
#include <psemek/gfx/array.hpp>
#include <psemek/gfx/buffer.hpp>
#include <psemek/util/hstring.hpp>
#include <psemek/io/memory_stream.hpp>
#include <stdexcept>
#include <unordered_map>
namespace psemek::gfx
{
namespace
{
struct drawable_impl
: drawable
{
gfx::array vao;
std::size_t index_count;
std::size_t index_offset;
GLenum index_type;
void draw() const override
{
vao.bind();
gl::DrawElements(gl::TRIANGLES, index_count, index_type, reinterpret_cast<void const *>(index_offset));
}
};
struct gltf_mesh_impl
: gltf_mesh
{
gltf_mesh_impl(gltf_asset const & asset, std::function<util::blob(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<primitive const> mesh(std::string_view name) const override
{
if (auto it = meshes_.find(name); it != meshes_.end())
return it->second;
return {};
}
private:
std::vector<gltf_asset::material> materials_;
std::vector<gfx::texture_2d> textures_;
std::vector<gfx::buffer> buffers_;
std::vector<std::unique_ptr<drawable_impl>> drawables_;
std::unordered_map<util::hstring, std::vector<primitive>> meshes_;
};
gltf_mesh_impl::gltf_mesh_impl(gltf_asset const & asset, std::function<util::blob(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_srgb(gfx::read_png(io::memory_istream(data.string_view())));
target.linear_mipmap_filter();
target.anisotropy();
target.generate_mipmap();
}
for (auto const & node : asset.nodes)
{
if (!node.mesh)
continue;
auto & target_mesh = meshes_[node.name];
auto const & mesh = asset.meshes[*node.mesh];
for (auto const & primitive : mesh.primitives)
{
drawables_.push_back(std::make_unique<drawable_impl>());
auto & drawable = *drawables_.back();
drawable.vao.bind();
{
auto const & indices_accessor = asset.accessors[primitive.indices];
auto const & indices_view = asset.buffer_views[indices_accessor.buffer_view];
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;
}
std::pair<GLuint, std::optional<std::size_t>> attributes[3] =
{
{0, primitive.position},
{1, primitive.normal},
{2, primitive.texcoord},
};
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<void const *>(view.offset));
}
target_mesh.push_back({&drawable, primitive.material});
}
}
}
}
std::unique_ptr<gltf_mesh> make_gltf_mesh(gltf_asset const & asset, std::function<util::blob(std::string const &)> uri_loader)
{
return std::make_unique<gltf_mesh_impl>(asset, std::move(uri_loader));
}
}