#include #include #include #include #include #include #include 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(index_offset)); } }; struct gltf_mesh_impl : gltf_mesh { gltf_mesh_impl(gltf_asset const & asset, std::function 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 { if (auto it = meshes_.find(name); it != meshes_.end()) return it->second; return {}; } 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 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()); 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> 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(view.offset)); } target_mesh.push_back({&drawable, primitive.material}); } } } } std::unique_ptr make_gltf_mesh(gltf_asset const & asset, std::function uri_loader) { return std::make_unique(asset, std::move(uri_loader)); } }