From 2a16c8bdb8b285209950a4e26b3eee1d95ec1a7a Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Thu, 20 May 2021 21:50:53 +0200 Subject: 3DFileViewer: Clean up file handling This unifies how 3DFileViewer handles the initial file when starting the application and when opening files later on via the menu. Errors are shown both for the initial load as well as when loading files later on. An error during file load no longer clears the existing model. It also adds support for specifying the filename as a command-line argument. The opened file's name is shown in the titlebar. --- Userland/Applications/3DFileViewer/MeshLoader.h | 3 +- .../3DFileViewer/WavefrontOBJLoader.cpp | 12 ++-- .../Applications/3DFileViewer/WavefrontOBJLoader.h | 4 +- Userland/Applications/3DFileViewer/main.cpp | 76 ++++++++++++---------- 4 files changed, 49 insertions(+), 46 deletions(-) diff --git a/Userland/Applications/3DFileViewer/MeshLoader.h b/Userland/Applications/3DFileViewer/MeshLoader.h index fd62d49e64..9676bf2604 100644 --- a/Userland/Applications/3DFileViewer/MeshLoader.h +++ b/Userland/Applications/3DFileViewer/MeshLoader.h @@ -7,6 +7,7 @@ #pragma once #include +#include #include "Common.h" #include "Mesh.h" @@ -16,5 +17,5 @@ public: MeshLoader() { } virtual ~MeshLoader() { } - virtual RefPtr load(const String& fname) = 0; + virtual RefPtr load(Core::File& file) = 0; }; diff --git a/Userland/Applications/3DFileViewer/WavefrontOBJLoader.cpp b/Userland/Applications/3DFileViewer/WavefrontOBJLoader.cpp index 891f13dc8f..3022d5218f 100644 --- a/Userland/Applications/3DFileViewer/WavefrontOBJLoader.cpp +++ b/Userland/Applications/3DFileViewer/WavefrontOBJLoader.cpp @@ -9,19 +9,15 @@ #include #include -RefPtr WavefrontOBJLoader::load(const String& fname) +RefPtr WavefrontOBJLoader::load(Core::File& file) { - auto obj_file_or_error = Core::File::open(fname, Core::OpenMode::ReadOnly); Vector vertices; Vector triangles; - dbgln("Wavefront: Loading {}...", fname); - - if (obj_file_or_error.is_error()) - return nullptr; + dbgln("Wavefront: Loading {}...", file.name()); // Start reading file line by line - for (auto line = obj_file_or_error.value()->line_begin(); !line.at_end(); ++line) { + for (auto line = file.line_begin(); !line.at_end(); ++line) { auto object_line = *line; // FIXME: Parse texture coordinates and vertex normals @@ -67,7 +63,7 @@ RefPtr WavefrontOBJLoader::load(const String& fname) } if (vertices.is_empty()) { - dbgln("Wavefront: Failed to read any data from 3D file: {}", fname); + dbgln("Wavefront: Failed to read any data from 3D file: {}", file.name()); return nullptr; } diff --git a/Userland/Applications/3DFileViewer/WavefrontOBJLoader.h b/Userland/Applications/3DFileViewer/WavefrontOBJLoader.h index 92ef601577..afebce1384 100644 --- a/Userland/Applications/3DFileViewer/WavefrontOBJLoader.h +++ b/Userland/Applications/3DFileViewer/WavefrontOBJLoader.h @@ -12,10 +12,10 @@ #include "Mesh.h" #include "MeshLoader.h" -class WavefrontOBJLoader : public MeshLoader { +class WavefrontOBJLoader final : public MeshLoader { public: WavefrontOBJLoader() { } ~WavefrontOBJLoader() override { } - RefPtr load(const String& fname) override; + RefPtr load(Core::File& file) override; }; diff --git a/Userland/Applications/3DFileViewer/main.cpp b/Userland/Applications/3DFileViewer/main.cpp index 93c8c616d8..cb12a42431 100644 --- a/Userland/Applications/3DFileViewer/main.cpp +++ b/Userland/Applications/3DFileViewer/main.cpp @@ -59,11 +59,6 @@ private: glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } glEndList(); - - // Load the teapot - m_mesh = m_mesh_loader->load("/home/anon/Documents/3D Models/teapot.obj"); - - dbgln("3DFileViewer: teapot mesh has {} triangles.", m_mesh->triangle_count()); } virtual void paint_event(GUI::PaintEvent&) override; @@ -109,15 +104,39 @@ void GLContextWidget::timer_event(Core::TimerEvent&) update(); } -bool GLContextWidget::load(const String& fname) +bool GLContextWidget::load(const String& filename) { - m_mesh = m_mesh_loader->load(fname); - if (!m_mesh.is_null()) { - dbgln("3DFileViewer: mesh has {} triangles.", m_mesh->triangle_count()); - return true; + auto file = Core::File::construct(filename); + + if (!file->filename().ends_with(".obj")) { + GUI::MessageBox::show(window(), String::formatted("Opening \"{}\" failed: invalid file type", filename), "Error", GUI::MessageBox::Type::Error); + return false; + } + + if (!file->open(Core::OpenMode::ReadOnly) && file->error() != ENOENT) { + GUI::MessageBox::show(window(), String::formatted("Opening \"{}\" failed: {}", filename, strerror(errno)), "Error", GUI::MessageBox::Type::Error); + return false; + } + + if (file->is_device()) { + GUI::MessageBox::show(window(), String::formatted("Opening \"{}\" failed: Can't open device files", filename), "Error", GUI::MessageBox::Type::Error); + return false; } - return false; + if (file->is_directory()) { + GUI::MessageBox::show(window(), String::formatted("Opening \"{}\" failed: Can't open directories", filename), "Error", GUI::MessageBox::Type::Error); + return false; + } + + auto new_mesh = m_mesh_loader->load(file); + if (new_mesh.is_null()) { + GUI::MessageBox::show(window(), String::formatted("Reading \"{}\" failed.", filename), "Error", GUI::MessageBox::Type::Error); + return false; + } + + m_mesh = new_mesh; + dbgln("3DFileViewer: mesh has {} triangles.", m_mesh->triangle_count()); + return true; } int main(int argc, char** argv) @@ -143,36 +162,20 @@ int main(int argc, char** argv) auto menubar = GUI::Menubar::construct(); auto& file_menu = menubar->add_menu("&File"); + auto load_model = [&](StringView const& filename) { + if (widget.load(filename)) { + auto canonical_path = Core::File::real_path_for(filename); + window->set_title(String::formatted("{} - 3D File Viewer", canonical_path)); + } + }; + file_menu.add_action(GUI::CommonActions::make_open_action([&](auto&) { Optional open_path = GUI::FilePicker::get_open_filepath(window); if (!open_path.has_value()) return; - auto file = Core::File::construct(open_path.value()); - - if (!file->filename().ends_with(".obj")) { - GUI::MessageBox::show(window, String::formatted("Opening \"{}\" failed: invalid file type", open_path.value()), "Error", GUI::MessageBox::Type::Error); - return; - } - - if (!file->open(Core::OpenMode::ReadOnly) && file->error() != ENOENT) { - GUI::MessageBox::show(window, String::formatted("Opening \"{}\" failed: {}", open_path.value(), strerror(errno)), "Error", GUI::MessageBox::Type::Error); - return; - } - - if (file->is_device()) { - GUI::MessageBox::show(window, String::formatted("Opening \"{}\" failed: Can't open device files", open_path.value()), "Error", GUI::MessageBox::Type::Error); - return; - } - - if (file->is_directory()) { - GUI::MessageBox::show(window, String::formatted("Opening \"{}\" failed: Can't open directories", open_path.value()), "Error", GUI::MessageBox::Type::Error); - return; - } - - if (!widget.load(file->filename())) - GUI::MessageBox::show(window, String::formatted("Reading \"{}\" failed.", open_path.value()), "Error", GUI::MessageBox::Type::Error); + load_model(open_path.value()); })); file_menu.add_action(GUI::CommonActions::make_quit_action([&](auto&) { @@ -185,5 +188,8 @@ int main(int argc, char** argv) window->set_menubar(move(menubar)); window->show(); + auto filename = argc > 1 ? argv[1] : "/home/anon/Documents/3D Models/teapot.obj"; + load_model(filename); + return app->exec(); } -- cgit v1.2.3