diff --git a/Gemfile b/Gemfile index e21fae76d..352450d52 100644 --- a/Gemfile +++ b/Gemfile @@ -105,8 +105,9 @@ gem "acts_as_favoritor", "~> 6.0" gem "sqlite3_ar_regexp", "~> 3.0" gem "mittsu", "~> 0.5" -gem "mittsu-mesh_analysis" +gem "mittsu-mesh_analysis", github: "manyfold3d/mittsu-mesh_analysis" gem "mittsu-3mf" +gem "mittsu-gltf", github: "manyfold3d/mittsu-gltf" gem "rails-controller-testing", "~> 1.0", group: :test diff --git a/Gemfile.lock b/Gemfile.lock index 3f559c878..efa2b994b 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -14,6 +14,21 @@ GIT caber (0.4.0) rails (>= 7.1.4) +GIT + remote: https://github.com/manyfold3d/mittsu-gltf.git + revision: 82ca727a6f84d23765bb67c75240b6577885fa6a + specs: + mittsu-gltf (0.2.0) + jbuilder (~> 2.13) + mittsu (~> 0.4) + +GIT + remote: https://github.com/manyfold3d/mittsu-mesh_analysis.git + revision: 63bff9274a054e67973956048d3b057fea4ce8f6 + specs: + mittsu-mesh_analysis (0.1.0) + mittsu (~> 0.4) + GIT remote: https://github.com/manyfold3d/rubocop-pundit.git revision: 1f511e087a58867279ca8d77e0bb97dc2ce2da75 @@ -489,8 +504,6 @@ GEM builder (~> 3.3) mittsu (~> 0.4) rubyzip (~> 2.3) - mittsu-mesh_analysis (0.1.0) - mittsu (~> 0.4) msgpack (1.8.0) multi_json (1.15.0) mysql2 (0.5.6) @@ -977,7 +990,8 @@ DEPENDENCIES memoist (~> 0.16) mittsu (~> 0.5) mittsu-3mf - mittsu-mesh_analysis + mittsu-gltf! + mittsu-mesh_analysis! mysql2 (~> 0.5) nanoid (~> 2.0) omniauth (~> 2.1) diff --git a/app/controllers/settings_controller.rb b/app/controllers/settings_controller.rb index c058e93e4..e198d79e6 100644 --- a/app/controllers/settings_controller.rb +++ b/app/controllers/settings_controller.rb @@ -96,6 +96,7 @@ def update_download_settings(settings) SiteSettings.pregenerate_downloads = (settings[:pregenerate] == "1") SiteSettings.download_expiry_time_in_hours = (settings[:expiry].to_i) SiteSettings.generate_image_derivatives = (settings[:image_derivatives] == "1") + SiteSettings.generate_progressive_meshes = (settings[:progressive_meshes] == "1") end def update_integrations_settings(settings) diff --git a/app/jobs/analysis/geometric_analysis_job.rb b/app/jobs/analysis/geometric_analysis_job.rb index 01e721726..c18053f2c 100644 --- a/app/jobs/analysis/geometric_analysis_job.rb +++ b/app/jobs/analysis/geometric_analysis_job.rb @@ -34,6 +34,20 @@ def perform(file_id) # !mesh.solid? # ) # end + if SiteSettings.generate_progressive_meshes && mesh.manifold? + pmesh = Mittsu::MeshAnalysis::ProgressiveMesh.new( + file.mesh.geometry, + file.mesh.material + ) + pmesh.progressify(ratio: 0.9) + exporter = Mittsu::GLTFExporter.new + Tempfile.create do |f| + exporter.export(pmesh, f.path, mode: :binary) + f.rewind + file.attachment_attacher.add_derivatives({progressive: f}) + file.save(touch: false) + end + end else raise MeshLoadError.new end diff --git a/app/models/site_settings.rb b/app/models/site_settings.rb index 67a7255c5..7bb8fd57f 100644 --- a/app/models/site_settings.rb +++ b/app/models/site_settings.rb @@ -39,6 +39,7 @@ class SiteSettings < RailsSettings::Base field :pregenerate_downloads, type: :boolean, default: false field :download_expiry_time_in_hours, type: :integer, default: 24 field :generate_image_derivatives, type: :boolean, default: false + field :generate_progressive_meshes, type: :boolean, default: false field :allow_robots, type: :boolean, default: false field :allow_ai_bots, type: :boolean, default: false diff --git a/app/views/settings/downloads.html.erb b/app/views/settings/downloads.html.erb index 05f0b8e60..ca78ac964 100644 --- a/app/views/settings/downloads.html.erb +++ b/app/views/settings/downloads.html.erb @@ -30,5 +30,12 @@ <%= t(".derivatives.images.help") %> +
+ <%= form.label nil, t(".derivatives.progressive_meshes.label"), for: "downloads[progressive_meshes]", class: "col-sm-4 col-form-label" %> +
+ <%= form.check_box "downloads[progressive_meshes]", checked: SiteSettings.generate_progressive_meshes, class: "form-check-input" %> + <%= t(".derivatives.progressive_meshes.help") %> +
+
<%= render "submit", form: form %> <% end %> diff --git a/config/locales/settings/en.yml b/config/locales/settings/en.yml index 41b0d02c7..c2c256cef 100644 --- a/config/locales/settings/en.yml +++ b/config/locales/settings/en.yml @@ -65,11 +65,14 @@ en: title: New Domain Block downloads: derivatives: - description: Manyfold can create alternative versions of some files to increase site performance. Note that this will use more disk space. + description: Manyfold can create alternative versions of some files to increase site performance. Note that this will use more disk space; it won't clutter up your model folders though, the derived files are kept separate. heading: File Derivatives images: help: Generates lower-resolution versions of images that will load quicker on list and model pages. label: Image thumbnails + progressive_meshes: + help: Generates streamable versions of 3d models that will load quickly at a low resolution, then refine. + label: Streamable meshes description: Manyfold will generate ZIP files of models on request, but for busy sites you may want these available more quickly. Here you can tell Manyfold to create ZIP files immediately, and also set how long they are kept in the download cache. Beware; this may use a lot of disk space! expiry: help: A setting of 0 means keep forever. diff --git a/spec/jobs/analysis/geometric_analysis_job_spec.rb b/spec/jobs/analysis/geometric_analysis_job_spec.rb index 0ec556da7..8c9724189 100644 --- a/spec/jobs/analysis/geometric_analysis_job_spec.rb +++ b/spec/jobs/analysis/geometric_analysis_job_spec.rb @@ -21,6 +21,13 @@ expect { described_class.perform_now(file.id) }.not_to change(Problem, :count) end + it "creates progressive derivative" do + allow(SiteSettings).to receive(:generate_progressive_meshes).and_return(true) + allow(file).to receive(:mesh).and_return(mesh) + described_class.perform_now(file.id) + expect(file.reload.attachment(:progressive)).to be_present + end + it "creates a Problem for a non-manifold mesh" do # rubocop:todo RSpec/MultipleExpectations allow(mesh).to receive(:manifold?).and_return(false) allow(file).to receive(:mesh).and_return(mesh)