Support navmesh generation from launcher

pull/3225/head
elsid 3 years ago
parent c9b8ba7b46
commit 9e0451c714
No known key found for this signature in database
GPG Key ID: B845CB9FEE18AB40

@ -1,4 +1,5 @@
#include "datafilespage.hpp"
#include "maindialog.hpp"
#include <QDebug>
@ -8,6 +9,7 @@
#include <QSortFilterProxyModel>
#include <thread>
#include <mutex>
#include <algorithm>
#include <apps/launcher/utils/cellnameloader.hpp>
#include <components/files/configurationmanager.hpp>
@ -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);
}

@ -2,6 +2,9 @@
#define DATAFILESPAGE_H
#include "ui_datafilespage.h"
#include <components/process/processinvoker.hpp>
#include <QWidget>
@ -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 &current, bool savePrevious);

@ -7,7 +7,8 @@
#include <QDebug>
#include <QCoreApplication>
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();
}

@ -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);

@ -2,13 +2,95 @@
<ui version="4.0">
<class>DataFilesPage</class>
<widget class="QWidget" name="DataFilesPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>571</width>
<height>384</height>
</rect>
</property>
<property name="contextMenuPolicy">
<enum>Qt::DefaultContextMenu</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="tab">
<attribute name="title">
<string>Data Files</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QWidget" name="contentSelectorWidget" native="true"/>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_2">
<attribute name="title">
<string>Navigation mesh cache</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="updateNavMeshButton">
<property name="focusPolicy">
<enum>Qt::TabFocus</enum>
</property>
<property name="toolTip">
<string>Generate navigation mesh cache for all content. Will be used by the engine to make cell loading faster.</string>
</property>
<property name="text">
<string>Update</string>
</property>
</widget>
</item>
<item>
<widget class="QProgressBar" name="navMeshProgressBar">
<property name="enabled">
<bool>false</bool>
</property>
<property name="value">
<number>0</number>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="cancelNavMeshButton">
<property name="enabled">
<bool>false</bool>
</property>
<property name="toolTip">
<string>Cancel navigation mesh generation. Already processed data will be saved.</string>
</property>
<property name="text">
<string>Cancel</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QPlainTextEdit" name="navMeshLogPlainTextEdit">
<property name="enabled">
<bool>true</bool>
</property>
<property name="lineWrapMode">
<enum>QPlainTextEdit::NoWrap</enum>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<widget class="QGroupBox" name="profileGroupBox">
<property name="focusPolicy">

Loading…
Cancel
Save