1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-27 22:17:44 +00:00

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.
This commit is contained in:
Gunnar Beutner 2021-05-20 21:50:53 +02:00 committed by Andreas Kling
parent 728e6dad73
commit 2a16c8bdb8
4 changed files with 49 additions and 46 deletions

View file

@ -7,6 +7,7 @@
#pragma once
#include <AK/String.h>
#include <LibCore/File.h>
#include "Common.h"
#include "Mesh.h"
@ -16,5 +17,5 @@ public:
MeshLoader() { }
virtual ~MeshLoader() { }
virtual RefPtr<Mesh> load(const String& fname) = 0;
virtual RefPtr<Mesh> load(Core::File& file) = 0;
};

View file

@ -9,19 +9,15 @@
#include <LibCore/File.h>
#include <stdlib.h>
RefPtr<Mesh> WavefrontOBJLoader::load(const String& fname)
RefPtr<Mesh> WavefrontOBJLoader::load(Core::File& file)
{
auto obj_file_or_error = Core::File::open(fname, Core::OpenMode::ReadOnly);
Vector<Vertex> vertices;
Vector<Triangle> 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<Mesh> 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;
}

View file

@ -12,10 +12,10 @@
#include "Mesh.h"
#include "MeshLoader.h"
class WavefrontOBJLoader : public MeshLoader {
class WavefrontOBJLoader final : public MeshLoader {
public:
WavefrontOBJLoader() { }
~WavefrontOBJLoader() override { }
RefPtr<Mesh> load(const String& fname) override;
RefPtr<Mesh> load(Core::File& file) override;
};

View file

@ -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;
}
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;
}
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<String> 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();
}