#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; std::vector> vertices; std::vector> triangles; 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.primitives; return {}; } gltf_asset::extra const * mesh_property(std::string_view mesh_name, std::string_view property_name) const override { if (auto it = meshes_.find(mesh_name); it != meshes_.end()) if (auto jt = it->second.extras.find(property_name); jt != it->second.extras.end()) return &(jt->second); return nullptr; } private: struct mesh_data { std::vector primitives; gltf_asset::extras_map extras; std::vector> vertices; std::vector> triangles; }; std::vector materials_; std::vector textures_; std::vector buffers_; std::vector> drawables_; util::hash_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) { if (buffer.data) { buffers_.emplace_back().load(buffer.data->data(), buffer.data->size(), gl::STATIC_DRAW); } else { auto data = uri_loader(buffer.uri); buffers_.emplace_back().load(data.data(), data.size(), gl::STATIC_DRAW); } } for (auto const & texture : asset.textures) { auto & target = textures_.emplace_back(); if (texture.uri) { auto data = uri_loader(*texture.uri); target.load_srgb(gfx::read_image(io::memory_istream(data.string_view()))); } else { target.load_srgb(gfx::pixmap_rgba({2, 2}, gfx::color_rgba{255, 0, 255, 255})); } 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]; target_mesh.extras = node.extras; 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[] = { {0, primitive.position}, {1, primitive.normal}, {2, primitive.texcoord}, {3, primitive.color}, }; 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, attribute_size(accessor.type), accessor.component_type, accessor.normalized, 0, reinterpret_cast(view.offset)); } { auto indices = accessor_range(asset, primitive.indices); for (auto it = indices.it_begin; it != indices.it_end;) { auto i0 = *it++; auto i1 = *it++; auto i2 = *it++; drawable.triangles.push_back({i0, i1, i2}); } } if (primitive.position) for (auto const & p : accessor_range>(asset, *primitive.position)) drawable.vertices.push_back(p); target_mesh.primitives.push_back({&drawable, primitive.material, drawable.vertices, drawable.triangles}); } } } } std::unique_ptr make_gltf_mesh(gltf_asset const & asset, std::function uri_loader) { return std::make_unique(asset, std::move(uri_loader)); } }