From 9e0451c714c052fb2234be20bac00b4d8c2645a0 Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 1 Aug 2021 23:35:45 +0200 Subject: [PATCH] Support navmesh generation from launcher --- apps/launcher/datafilespage.cpp | 68 +++++++++++++++++++++- apps/launcher/datafilespage.hpp | 14 ++++- components/process/processinvoker.cpp | 14 ++++- components/process/processinvoker.hpp | 6 +- files/ui/datafilespage.ui | 84 ++++++++++++++++++++++++++- 5 files changed, 179 insertions(+), 7 deletions(-) diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index eb0950dae0..097dc21dd2 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -1,4 +1,5 @@ #include "datafilespage.hpp" +#include "maindialog.hpp" #include @@ -8,6 +9,7 @@ #include #include #include +#include #include #include @@ -24,11 +26,14 @@ const char *Launcher::DataFilesPage::mDefaultContentListName = "Default"; -Launcher::DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, Config::GameSettings &gameSettings, Config::LauncherSettings &launcherSettings, QWidget *parent) +Launcher::DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, Config::GameSettings &gameSettings, + Config::LauncherSettings &launcherSettings, MainDialog *parent) : QWidget(parent) + , mMainDialog(parent) , mCfgMgr(cfg) , mGameSettings(gameSettings) , mLauncherSettings(launcherSettings) + , mNavMeshToolInvoker(new Process::ProcessInvoker(this)) { ui.setupUi (this); setObjectName ("DataFilesPage"); @@ -57,8 +62,6 @@ Launcher::DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, Config: void Launcher::DataFilesPage::buildView() { - ui.verticalLayout->insertWidget (0, mSelector->uiWidget()); - QToolButton * refreshButton = mSelector->refreshButton(); //tool buttons @@ -89,6 +92,13 @@ void Launcher::DataFilesPage::buildView() this, SLOT (slotProfileChangedByUser(QString, QString))); connect(ui.refreshDataFilesAction, SIGNAL(triggered()),this, SLOT(slotRefreshButtonClicked())); + + connect(ui.updateNavMeshButton, SIGNAL(clicked()), this, SLOT(startNavMeshTool())); + connect(ui.cancelNavMeshButton, SIGNAL(clicked()), this, SLOT(killNavMeshTool())); + + connect(mNavMeshToolInvoker->getProcess(), SIGNAL(readyReadStandardOutput()), this, SLOT(updateNavMeshProgress())); + connect(mNavMeshToolInvoker->getProcess(), SIGNAL(readyReadStandardError()), this, SLOT(updateNavMeshProgress())); + connect(mNavMeshToolInvoker->getProcess(), SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(navMeshToolFinished(int, QProcess::ExitStatus))); } bool Launcher::DataFilesPage::loadSettings() @@ -411,3 +421,55 @@ void Launcher::DataFilesPage::reloadCells(QStringList selectedFiles) std::sort(cellNamesList.begin(), cellNamesList.end()); emit signalLoadedCellsChanged(cellNamesList); } + +void Launcher::DataFilesPage::startNavMeshTool() +{ + mMainDialog->writeSettings(); + + ui.navMeshLogPlainTextEdit->clear(); + ui.navMeshProgressBar->setValue(0); + ui.navMeshProgressBar->setMaximum(1); + + if (!mNavMeshToolInvoker->startProcess(QLatin1String("openmw-navmeshtool"))) + return; + + ui.cancelNavMeshButton->setEnabled(true); + ui.navMeshProgressBar->setEnabled(true); +} + +void Launcher::DataFilesPage::killNavMeshTool() +{ + mNavMeshToolInvoker->killProcess(); +} + +void Launcher::DataFilesPage::updateNavMeshProgress() +{ + QProcess& process = *mNavMeshToolInvoker->getProcess(); + QString text; + while (process.canReadLine()) + { + const QByteArray line = process.readLine(); + const auto end = std::find_if(line.rbegin(), line.rend(), [] (auto v) { return v != '\n' && v != '\r'; }); + text = QString::fromUtf8(line.mid(0, line.size() - (end - line.rbegin()))); + ui.navMeshLogPlainTextEdit->appendPlainText(text); + } + const QRegularExpression pattern(R"([\( ](\d+)/(\d+)[\) ])"); + QRegularExpressionMatch match = pattern.match(text); + if (!match.hasMatch()) + return; + int maximum = match.captured(2).toInt(); + if (text.contains("cell")) + maximum *= 100; + ui.navMeshProgressBar->setMaximum(std::max(ui.navMeshProgressBar->maximum(), maximum)); + ui.navMeshProgressBar->setValue(match.captured(1).toInt()); +} + +void Launcher::DataFilesPage::navMeshToolFinished(int exitCode, QProcess::ExitStatus exitStatus) +{ + updateNavMeshProgress(); + ui.navMeshLogPlainTextEdit->appendPlainText(QString::fromUtf8(mNavMeshToolInvoker->getProcess()->readAll())); + if (exitCode == 0 && exitStatus == QProcess::ExitStatus::NormalExit) + ui.navMeshProgressBar->setValue(ui.navMeshProgressBar->maximum()); + ui.cancelNavMeshButton->setEnabled(false); + ui.navMeshProgressBar->setEnabled(false); +} diff --git a/apps/launcher/datafilespage.hpp b/apps/launcher/datafilespage.hpp index 5a7a6dc6e6..a039237590 100644 --- a/apps/launcher/datafilespage.hpp +++ b/apps/launcher/datafilespage.hpp @@ -2,6 +2,9 @@ #define DATAFILESPAGE_H #include "ui_datafilespage.h" + +#include + #include @@ -19,6 +22,7 @@ namespace Config { class GameSettings; namespace Launcher { + class MainDialog; class TextInputDialog; class ProfilesComboBox; @@ -31,7 +35,7 @@ namespace Launcher public: explicit DataFilesPage (Files::ConfigurationManager &cfg, Config::GameSettings &gameSettings, - Config::LauncherSettings &launcherSettings, QWidget *parent = nullptr); + Config::LauncherSettings &launcherSettings, MainDialog *parent = nullptr); QAbstractItemModel* profilesModel() const; @@ -69,12 +73,18 @@ namespace Launcher void on_cloneProfileAction_triggered(); void on_deleteProfileAction_triggered(); + void startNavMeshTool(); + void killNavMeshTool(); + void updateNavMeshProgress(); + void navMeshToolFinished(int exitCode, QProcess::ExitStatus exitStatus); + public: /// Content List that is always present const static char *mDefaultContentListName; private: + MainDialog *mMainDialog; TextInputDialog *mNewProfileDialog; TextInputDialog *mCloneProfileDialog; @@ -87,6 +97,8 @@ namespace Launcher QStringList previousSelectedFiles; QString mDataLocal; + Process::ProcessInvoker* mNavMeshToolInvoker; + void buildView(); void setProfile (int index, bool savePrevious); void setProfile (const QString &previous, const QString ¤t, bool savePrevious); diff --git a/components/process/processinvoker.cpp b/components/process/processinvoker.cpp index 78cf70038b..7c45c865a1 100644 --- a/components/process/processinvoker.cpp +++ b/components/process/processinvoker.cpp @@ -7,7 +7,8 @@ #include #include -Process::ProcessInvoker::ProcessInvoker() +Process::ProcessInvoker::ProcessInvoker(QObject* parent) + : QObject(parent) { mProcess = new QProcess(this); @@ -56,6 +57,7 @@ bool Process::ProcessInvoker::startProcess(const QString &name, const QStringLis // mProcess = new QProcess(this); mName = name; mArguments = arguments; + mIgnoreErrors = false; QString path(name); #ifdef Q_OS_WIN @@ -151,6 +153,8 @@ bool Process::ProcessInvoker::startProcess(const QString &name, const QStringLis void Process::ProcessInvoker::processError(QProcess::ProcessError error) { + if (mIgnoreErrors) + return; QMessageBox msgBox; msgBox.setWindowTitle(tr("Error running executable")); msgBox.setIcon(QMessageBox::Critical); @@ -166,6 +170,8 @@ void Process::ProcessInvoker::processError(QProcess::ProcessError error) void Process::ProcessInvoker::processFinished(int exitCode, QProcess::ExitStatus exitStatus) { if (exitCode != 0 || exitStatus == QProcess::CrashExit) { + if (mIgnoreErrors) + return; QString error(mProcess->readAllStandardError()); error.append(tr("\nArguments:\n")); error.append(mArguments.join(" ")); @@ -181,3 +187,9 @@ void Process::ProcessInvoker::processFinished(int exitCode, QProcess::ExitStatus msgBox.exec(); } } + +void Process::ProcessInvoker::killProcess() +{ + mIgnoreErrors = true; + mProcess->kill(); +} diff --git a/components/process/processinvoker.hpp b/components/process/processinvoker.hpp index 8fff6658ca..f4b402cb12 100644 --- a/components/process/processinvoker.hpp +++ b/components/process/processinvoker.hpp @@ -13,7 +13,7 @@ namespace Process public: - ProcessInvoker(); + ProcessInvoker(QObject* parent = nullptr); ~ProcessInvoker(); // void setProcessName(const QString &name); @@ -27,12 +27,16 @@ namespace Process inline bool startProcess(const QString &name, bool detached = false) { return startProcess(name, QStringList(), detached); } bool startProcess(const QString &name, const QStringList &arguments, bool detached = false); + void killProcess(); + private: QProcess *mProcess; QString mName; QStringList mArguments; + bool mIgnoreErrors = false; + private slots: void processError(QProcess::ProcessError error); void processFinished(int exitCode, QProcess::ExitStatus exitStatus); diff --git a/files/ui/datafilespage.ui b/files/ui/datafilespage.ui index ccac5050ed..ff330391d2 100644 --- a/files/ui/datafilespage.ui +++ b/files/ui/datafilespage.ui @@ -2,12 +2,94 @@ DataFilesPage + + + 0 + 0 + 571 + 384 + + Qt::DefaultContextMenu - + + + 0 + + + + Data Files + + + + + + + + + + Navigation mesh cache + + + + + + + + Qt::TabFocus + + + Generate navigation mesh cache for all content. Will be used by the engine to make cell loading faster. + + + Update + + + + + + + false + + + 0 + + + + + + + false + + + Cancel navigation mesh generation. Already processed data will be saved. + + + Cancel + + + + + + + + + true + + + QPlainTextEdit::NoWrap + + + true + + + + + +