Merge branch 'stabilization/2106' into LYN-4507

This commit is contained in:
AMZN-Phil
2021-07-02 09:31:22 -07:00
committed by GitHub
124 changed files with 994 additions and 613 deletions
@@ -84,7 +84,10 @@ namespace AssetProcessor
AssetProcessorTest::TearDown();
}
void OnError([[maybe_unused]] const AZ::IO::SystemFile* file, const char* fileName, int errorCode) override
void OnError(
[[maybe_unused]] const AZ::IO::SystemFile* file,
[[maybe_unused]] const char* fileName,
[[maybe_unused]] int errorCode) override
{
AZ_Error("LegacyTestAdapter", false, "File error detected with %s with code %d", fileName, errorCode);
}
@@ -7,4 +7,5 @@
set(FILES
Python_linux.cpp
ProjectBuilderWorker_linux.cpp
)
@@ -0,0 +1,19 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <ProjectBuilderWorker.h>
namespace O3DE::ProjectManager
{
AZ::Outcome<void, QString> ProjectBuilderWorker::BuildProjectForPlatform()
{
QString error = tr("Automatic building on Linux not currently supported!");
QStringToAZTracePrint(error);
return AZ::Failure(error);
}
} // namespace O3DE::ProjectManager
@@ -7,4 +7,5 @@
set(FILES
Python_mac.cpp
ProjectBuilderWorker_mac.cpp
)
@@ -0,0 +1,19 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <ProjectBuilderWorker.h>
namespace O3DE::ProjectManager
{
AZ::Outcome<void, QString> ProjectBuilderWorker::BuildProjectForPlatform()
{
QString error = tr("Automatic building on MacOS not currently supported!");
QStringToAZTracePrint(error);
return AZ::Failure(error);
}
} // namespace O3DE::ProjectManager
@@ -7,4 +7,5 @@
set(FILES
Python_windows.cpp
ProjectBuilderWorker_windows.cpp
)
@@ -0,0 +1,175 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <ProjectBuilderWorker.h>
#include <ProjectManagerDefs.h>
#include <PythonBindingsInterface.h>
#include <QDir>
#include <QFile>
#include <QProcess>
#include <QProcessEnvironment>
#include <QTextStream>
#include <QThread>
namespace O3DE::ProjectManager
{
AZ::Outcome<void, QString> ProjectBuilderWorker::BuildProjectForPlatform()
{
// Check if we are trying to cancel task
if (QThread::currentThread()->isInterruptionRequested())
{
QStringToAZTracePrint(BuildCancelled);
return AZ::Failure(BuildCancelled);
}
QFile logFile(GetLogFilePath());
if (!logFile.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate))
{
QString error = tr("Failed to open log file.");
QStringToAZTracePrint(error);
return AZ::Failure(error);
}
EngineInfo engineInfo;
AZ::Outcome<EngineInfo> engineInfoResult = PythonBindingsInterface::Get()->GetEngineInfo();
if (engineInfoResult.IsSuccess())
{
engineInfo = engineInfoResult.GetValue();
}
else
{
QString error = tr("Failed to get engine info.");
QStringToAZTracePrint(error);
return AZ::Failure(error);
}
QTextStream logStream(&logFile);
if (QThread::currentThread()->isInterruptionRequested())
{
logFile.close();
QStringToAZTracePrint(BuildCancelled);
return AZ::Failure(BuildCancelled);
}
// Show some kind of progress with very approximate estimates
UpdateProgress(++m_progressEstimate);
QProcessEnvironment currentEnvironment(QProcessEnvironment::systemEnvironment());
// Append cmake path to PATH incase it is missing
QDir cmakePath(engineInfo.m_path);
cmakePath.cd("cmake/runtime/bin");
QString pathValue = currentEnvironment.value("PATH");
pathValue += ";" + cmakePath.path();
currentEnvironment.insert("PATH", pathValue);
m_configProjectProcess = new QProcess(this);
m_configProjectProcess->setProcessChannelMode(QProcess::MergedChannels);
m_configProjectProcess->setWorkingDirectory(m_projectInfo.m_path);
m_configProjectProcess->setProcessEnvironment(currentEnvironment);
m_configProjectProcess->start(
"cmake",
QStringList{ "-B", QDir(m_projectInfo.m_path).filePath(ProjectBuildPathPostfix), "-S", m_projectInfo.m_path, "-G",
"Visual Studio 16", "-DLY_3RDPARTY_PATH=" + engineInfo.m_thirdPartyPath });
if (!m_configProjectProcess->waitForStarted())
{
QString error = tr("Configuring project failed to start.");
QStringToAZTracePrint(error);
return AZ::Failure(error);
}
bool containsGeneratingDone = false;
while (m_configProjectProcess->waitForReadyRead(MaxBuildTimeMSecs))
{
QString configOutput = m_configProjectProcess->readAllStandardOutput();
if (configOutput.contains("Generating done"))
{
containsGeneratingDone = true;
}
logStream << configOutput;
logStream.flush();
UpdateProgress(qMin(++m_progressEstimate, 19));
if (QThread::currentThread()->isInterruptionRequested())
{
logFile.close();
m_configProjectProcess->close();
QStringToAZTracePrint(BuildCancelled);
return AZ::Failure(BuildCancelled);
}
}
if (m_configProjectProcess->exitCode() != 0 || !containsGeneratingDone)
{
QString error = tr("Configuring project failed. See log for details.");
QStringToAZTracePrint(error);
return AZ::Failure(error);
}
UpdateProgress(++m_progressEstimate);
m_buildProjectProcess = new QProcess(this);
m_buildProjectProcess->setProcessChannelMode(QProcess::MergedChannels);
m_buildProjectProcess->setWorkingDirectory(m_projectInfo.m_path);
m_buildProjectProcess->setProcessEnvironment(currentEnvironment);
m_buildProjectProcess->start(
"cmake",
QStringList{ "--build", QDir(m_projectInfo.m_path).filePath(ProjectBuildPathPostfix), "--target",
m_projectInfo.m_projectName + ".GameLauncher", "Editor", "--config", "profile" });
if (!m_buildProjectProcess->waitForStarted())
{
QString error = tr("Building project failed to start.");
QStringToAZTracePrint(error);
return AZ::Failure(error);
}
// There are a lot of steps when building so estimate around 800 more steps ((100 - 20) * 10) remaining
m_progressEstimate = 200;
while (m_buildProjectProcess->waitForReadyRead(MaxBuildTimeMSecs))
{
logStream << m_buildProjectProcess->readAllStandardOutput();
logStream.flush();
// Show 1% progress for every 10 steps completed
UpdateProgress(qMin(++m_progressEstimate / 10, 99));
if (QThread::currentThread()->isInterruptionRequested())
{
// QProcess is unable to kill its child processes so we need to ask the operating system to do that for us
QProcess killBuildProcess;
killBuildProcess.setProcessChannelMode(QProcess::MergedChannels);
killBuildProcess.start(
"cmd.exe", QStringList{ "/C", "taskkill", "/pid", QString::number(m_buildProjectProcess->processId()), "/f", "/t" });
killBuildProcess.waitForFinished();
logStream << "Killing Project Build.";
logStream << killBuildProcess.readAllStandardOutput();
m_buildProjectProcess->kill();
logFile.close();
QStringToAZTracePrint(BuildCancelled);
return AZ::Failure(BuildCancelled);
}
}
if (m_configProjectProcess->exitCode() != 0)
{
QString error = tr("Building project failed. See log for details.");
QStringToAZTracePrint(error);
return AZ::Failure(error);
}
return AZ::Success();
}
} // namespace O3DE::ProjectManager
@@ -131,6 +131,17 @@ namespace O3DE::ProjectManager
return false;
}
if (event->type() == QEvent::KeyPress)
{
auto keyEvent = static_cast<const QKeyEvent*>(event);
if (keyEvent->key() == Qt::Key_Space)
{
const bool isAdded = GemModel::IsAdded(modelIndex);
GemModel::SetIsAdded(*model, modelIndex, !isAdded);
return true;
}
}
if (event->type() == QEvent::MouseButtonPress)
{
QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
@@ -1,244 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <ProjectBuilder.h>
#include <ProjectManagerDefs.h>
#include <ProjectButtonWidget.h>
#include <PythonBindingsInterface.h>
#include <QProcess>
#include <QFile>
#include <QTextStream>
#include <QMessageBox>
#include <QDesktopServices>
#include <QUrl>
#include <QDir>
#include <QProcessEnvironment>
//#define MOCK_BUILD_PROJECT true
namespace O3DE::ProjectManager
{
// QProcess::waitForFinished uses -1 to indicate that the process should not timeout
constexpr int MaxBuildTimeMSecs = -1;
ProjectBuilderWorker::ProjectBuilderWorker(const ProjectInfo& projectInfo)
: QObject()
, m_projectInfo(projectInfo)
{
}
void ProjectBuilderWorker::BuildProject()
{
#ifdef MOCK_BUILD_PROJECT
for (int i = 0; i < 10; ++i)
{
QThread::sleep(1);
UpdateProgress(i * 10);
}
Done(m_projectPath);
#else
EngineInfo engineInfo;
AZ::Outcome<EngineInfo> engineInfoResult = PythonBindingsInterface::Get()->GetEngineInfo();
if (engineInfoResult.IsSuccess())
{
engineInfo = engineInfoResult.GetValue();
}
else
{
emit Done(tr("Failed to get engine info."));
return;
}
// Show some kind of progress with very approximate estimates
UpdateProgress(1);
QProcessEnvironment currentEnvironment(QProcessEnvironment::systemEnvironment());
// Append cmake path to PATH incase it is missing
QDir cmakePath(engineInfo.m_path);
cmakePath.cd("cmake/runtime/bin");
QString pathValue = currentEnvironment.value("PATH");
pathValue += ";" + cmakePath.path();
currentEnvironment.insert("PATH", pathValue);
QProcess configProjectProcess;
configProjectProcess.setProcessChannelMode(QProcess::MergedChannels);
configProjectProcess.setWorkingDirectory(m_projectInfo.m_path);
configProjectProcess.setProcessEnvironment(currentEnvironment);
configProjectProcess.start(
"cmake",
QStringList
{
"-B",
QDir(m_projectInfo.m_path).filePath(ProjectBuildPathPostfix),
"-S",
m_projectInfo.m_path,
"-G",
"Visual Studio 16",
"-DLY_3RDPARTY_PATH=" + engineInfo.m_thirdPartyPath
});
if (!configProjectProcess.waitForStarted())
{
emit Done(tr("Configuring project failed to start."));
return;
}
if (!configProjectProcess.waitForFinished(MaxBuildTimeMSecs))
{
WriteErrorLog(configProjectProcess.readAllStandardOutput());
emit Done(tr("Configuring project timed out. See log for details"));
return;
}
QString configProjectOutput(configProjectProcess.readAllStandardOutput());
if (configProjectProcess.exitCode() != 0 || !configProjectOutput.contains("Generating done"))
{
WriteErrorLog(configProjectOutput);
emit Done(tr("Configuring project failed. See log for details."));
return;
}
UpdateProgress(20);
QProcess buildProjectProcess;
buildProjectProcess.setProcessChannelMode(QProcess::MergedChannels);
buildProjectProcess.setWorkingDirectory(m_projectInfo.m_path);
buildProjectProcess.setProcessEnvironment(currentEnvironment);
buildProjectProcess.start(
"cmake",
QStringList
{
"--build",
QDir(m_projectInfo.m_path).filePath(ProjectBuildPathPostfix),
"--target",
m_projectInfo.m_projectName + ".GameLauncher",
"Editor",
"--config",
"profile"
});
if (!buildProjectProcess.waitForStarted())
{
emit Done(tr("Building project failed to start."));
return;
}
if (!buildProjectProcess.waitForFinished(MaxBuildTimeMSecs))
{
WriteErrorLog(configProjectProcess.readAllStandardOutput());
emit Done(tr("Building project timed out. See log for details"));
return;
}
QString buildProjectOutput(buildProjectProcess.readAllStandardOutput());
if (configProjectProcess.exitCode() != 0)
{
WriteErrorLog(buildProjectOutput);
emit Done(tr("Building project failed. See log for details."));
}
else
{
emit Done("");
}
#endif
}
QString ProjectBuilderWorker::LogFilePath() const
{
QDir logFilePath(m_projectInfo.m_path);
logFilePath.cd(ProjectBuildPathPostfix);
return logFilePath.filePath(ProjectBuildErrorLogPathPostfix);
}
void ProjectBuilderWorker::WriteErrorLog(const QString& log)
{
QFile logFile(LogFilePath());
// Overwrite file with truncate
if (logFile.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text))
{
QTextStream output(&logFile);
output << log;
logFile.close();
}
}
ProjectBuilderController::ProjectBuilderController(const ProjectInfo& projectInfo, ProjectButton* projectButton, QWidget* parent)
: QObject()
, m_projectInfo(projectInfo)
, m_projectButton(projectButton)
, m_parent(parent)
{
m_worker = new ProjectBuilderWorker(m_projectInfo);
m_worker->moveToThread(&m_workerThread);
connect(&m_workerThread, &QThread::finished, m_worker, &ProjectBuilderWorker::deleteLater);
connect(&m_workerThread, &QThread::started, m_worker, &ProjectBuilderWorker::BuildProject);
connect(m_worker, &ProjectBuilderWorker::Done, this, &ProjectBuilderController::HandleResults);
connect(m_worker, &ProjectBuilderWorker::UpdateProgress, this, &ProjectBuilderController::UpdateUIProgress);
}
ProjectBuilderController::~ProjectBuilderController()
{
m_workerThread.quit();
m_workerThread.wait();
}
void ProjectBuilderController::Start()
{
m_workerThread.start();
UpdateUIProgress(0);
}
void ProjectBuilderController::SetProjectButton(ProjectButton* projectButton)
{
m_projectButton = projectButton;
}
QString ProjectBuilderController::GetProjectPath() const
{
return m_projectInfo.m_path;
}
void ProjectBuilderController::UpdateUIProgress(int progress)
{
if (m_projectButton)
{
m_projectButton->SetButtonOverlayText(QString("%1 (%2%)\n\n").arg(tr("Building Project..."), QString::number(progress)));
m_projectButton->SetProgressBarValue(progress);
}
}
void ProjectBuilderController::HandleResults(const QString& result)
{
if (!result.isEmpty())
{
if (result.contains(tr("log")))
{
QMessageBox::StandardButton openLog = QMessageBox::critical(
m_parent,
tr("Project Failed to Build!"),
result + tr("\n\nWould you like to view log?"),
QMessageBox::No | QMessageBox::Yes);
if (openLog == QMessageBox::Yes)
{
// Open application assigned to this file type
QDesktopServices::openUrl(QUrl("file:///" + m_worker->LogFilePath()));
}
}
else
{
QMessageBox::critical(m_parent, tr("Project Failed to Build!"), result);
}
}
emit Done();
}
} // namespace O3DE::ProjectManager
@@ -0,0 +1,113 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <ProjectBuilderController.h>
#include <ProjectBuilderWorker.h>
#include <ProjectButtonWidget.h>
#include <QMessageBox>
#include <QDesktopServices>
#include <QUrl>
namespace O3DE::ProjectManager
{
ProjectBuilderController::ProjectBuilderController(const ProjectInfo& projectInfo, ProjectButton* projectButton, QWidget* parent)
: QObject()
, m_projectInfo(projectInfo)
, m_projectButton(projectButton)
, m_lastProgress(0)
, m_parent(parent)
{
m_worker = new ProjectBuilderWorker(m_projectInfo);
m_worker->moveToThread(&m_workerThread);
connect(&m_workerThread, &QThread::finished, m_worker, &ProjectBuilderWorker::deleteLater);
connect(&m_workerThread, &QThread::started, m_worker, &ProjectBuilderWorker::BuildProject);
connect(m_worker, &ProjectBuilderWorker::Done, this, &ProjectBuilderController::HandleResults);
connect(m_worker, &ProjectBuilderWorker::UpdateProgress, this, &ProjectBuilderController::UpdateUIProgress);
}
ProjectBuilderController::~ProjectBuilderController()
{
m_workerThread.requestInterruption();
m_workerThread.quit();
m_workerThread.wait();
}
void ProjectBuilderController::Start()
{
m_workerThread.start();
UpdateUIProgress(0);
}
void ProjectBuilderController::SetProjectButton(ProjectButton* projectButton)
{
m_projectButton = projectButton;
if (projectButton)
{
projectButton->SetProjectButtonAction(tr("Cancel Build"), [this] { HandleCancel(); });
if (m_lastProgress != 0)
{
UpdateUIProgress(m_lastProgress);
}
}
}
const ProjectInfo& ProjectBuilderController::GetProjectInfo() const
{
return m_projectInfo;
}
void ProjectBuilderController::UpdateUIProgress(int progress)
{
m_lastProgress = progress;
if (m_projectButton)
{
m_projectButton->SetButtonOverlayText(QString("%1 (%2%)\n\n").arg(tr("Building Project..."), QString::number(progress)));
m_projectButton->SetProgressBarValue(progress);
}
}
void ProjectBuilderController::HandleResults(const QString& result)
{
if (!result.isEmpty())
{
if (result.contains(tr("log")))
{
QMessageBox::StandardButton openLog = QMessageBox::critical(
m_parent,
tr("Project Failed to Build!"),
result + tr("\n\nWould you like to view log?"),
QMessageBox::No | QMessageBox::Yes);
if (openLog == QMessageBox::Yes)
{
// Open application assigned to this file type
QDesktopServices::openUrl(QUrl("file:///" + m_worker->GetLogFilePath()));
}
}
else
{
QMessageBox::critical(m_parent, tr("Project Failed to Build!"), result);
}
emit Done(false);
return;
}
emit Done(true);
}
void ProjectBuilderController::HandleCancel()
{
m_workerThread.quit();
emit Done(false);
}
} // namespace O3DE::ProjectManager
@@ -12,32 +12,12 @@
#include <QThread>
#endif
QT_FORWARD_DECLARE_CLASS(QProcess)
namespace O3DE::ProjectManager
{
QT_FORWARD_DECLARE_CLASS(ProjectButton)
class ProjectBuilderWorker : public QObject
{
Q_OBJECT
public:
explicit ProjectBuilderWorker(const ProjectInfo& projectInfo);
~ProjectBuilderWorker() = default;
QString LogFilePath() const;
public slots:
void BuildProject();
signals:
void UpdateProgress(int progress);
void Done(QString result);
private:
void WriteErrorLog(const QString& log);
ProjectInfo m_projectInfo;
};
QT_FORWARD_DECLARE_CLASS(ProjectBuilderWorker)
class ProjectBuilderController : public QObject
{
@@ -48,15 +28,16 @@ namespace O3DE::ProjectManager
~ProjectBuilderController();
void SetProjectButton(ProjectButton* projectButton);
QString GetProjectPath() const;
const ProjectInfo& GetProjectInfo() const;
public slots:
void Start();
void UpdateUIProgress(int progress);
void HandleResults(const QString& result);
void HandleCancel();
signals:
void Done();
void Done(bool success = true);
private:
ProjectInfo m_projectInfo;
@@ -64,5 +45,7 @@ namespace O3DE::ProjectManager
QThread m_workerThread;
ProjectButton* m_projectButton;
QWidget* m_parent;
int m_lastProgress;
};
} // namespace O3DE::ProjectManager
@@ -0,0 +1,71 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <ProjectBuilderWorker.h>
#include <ProjectManagerDefs.h>
#include <QDir>
//#define MOCK_BUILD_PROJECT true
namespace O3DE::ProjectManager
{
const QString ProjectBuilderWorker::BuildCancelled = ProjectBuilderWorker::tr("Build Cancelled.");
ProjectBuilderWorker::ProjectBuilderWorker(const ProjectInfo& projectInfo)
: QObject()
, m_projectInfo(projectInfo)
, m_progressEstimate(0)
{
}
void ProjectBuilderWorker::BuildProject()
{
#ifdef MOCK_BUILD_PROJECT
for (int i = 0; i < 10; ++i)
{
QThread::sleep(1);
UpdateProgress(i * 10);
}
Done("");
#else
auto result = BuildProjectForPlatform();
if (result.IsSuccess())
{
emit Done();
}
else
{
emit Done(result.GetError());
}
#endif
}
QString ProjectBuilderWorker::GetLogFilePath() const
{
QDir logFilePath(m_projectInfo.m_path);
// Make directories if they aren't on disk
if (!logFilePath.cd(ProjectBuildPathPostfix))
{
logFilePath.mkpath(ProjectBuildPathPostfix);
logFilePath.cd(ProjectBuildPathPostfix);
}
if (!logFilePath.cd(ProjectBuildPathCmakeFiles))
{
logFilePath.mkpath(ProjectBuildPathCmakeFiles);
logFilePath.cd(ProjectBuildPathCmakeFiles);
}
return logFilePath.filePath(ProjectBuildErrorLogName);
}
void ProjectBuilderWorker::QStringToAZTracePrint([[maybe_unused]] const QString& error)
{
AZ_TracePrintf("Project Manager", error.toStdString().c_str());
}
} // namespace O3DE::ProjectManager
@@ -0,0 +1,52 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#if !defined(Q_MOC_RUN)
#include <ProjectInfo.h>
#include <AzCore/Outcome/Outcome.h>
#include <QObject>
#endif
QT_FORWARD_DECLARE_CLASS(QProcess)
namespace O3DE::ProjectManager
{
class ProjectBuilderWorker : public QObject
{
// QProcess::waitForFinished uses -1 to indicate that the process should not timeout
static constexpr int MaxBuildTimeMSecs = -1;
// Build was cancelled
static const QString BuildCancelled;
Q_OBJECT
public:
explicit ProjectBuilderWorker(const ProjectInfo& projectInfo);
~ProjectBuilderWorker() = default;
QString GetLogFilePath() const;
public slots:
void BuildProject();
signals:
void UpdateProgress(int progress);
void Done(QString result = "");
private:
AZ::Outcome<void, QString> BuildProjectForPlatform();
void QStringToAZTracePrint(const QString& error);
QProcess* m_configProjectProcess = nullptr;
QProcess* m_buildProjectProcess = nullptr;
ProjectInfo m_projectInfo;
int m_progressEstimate;
};
} // namespace O3DE::ProjectManager
@@ -40,8 +40,8 @@ namespace O3DE::ProjectManager
m_overlayLabel->setVisible(false);
vLayout->addWidget(m_overlayLabel);
m_buildButton = new QPushButton(tr("Build Project"), this);
m_buildButton->setVisible(false);
m_actionButton = new QPushButton(tr("Project Action"), this);
m_actionButton->setVisible(false);
m_progressBar = new QProgressBar(this);
m_progressBar->setObjectName("labelButtonProgressBar");
@@ -78,9 +78,9 @@ namespace O3DE::ProjectManager
return m_progressBar;
}
QPushButton* LabelButton::GetBuildButton()
QPushButton* LabelButton::GetActionButton()
{
return m_buildButton;
return m_actionButton;
}
ProjectButton::ProjectButton(const ProjectInfo& projectInfo, QWidget* parent, bool processing)
@@ -135,7 +135,6 @@ namespace O3DE::ProjectManager
void ProjectButton::ProcessingSetup()
{
m_projectImageLabel->GetOverlayLabel()->setAlignment(Qt::AlignHCenter | Qt::AlignBottom);
m_projectImageLabel->SetEnabled(false);
m_projectImageLabel->SetOverlayText(tr("Processing...\n\n"));
@@ -146,8 +145,6 @@ namespace O3DE::ProjectManager
void ProjectButton::ReadySetup()
{
connect(m_projectImageLabel->GetBuildButton(), &QPushButton::clicked, [this](){ emit BuildProject(m_projectInfo); });
QMenu* menu = new QMenu(this);
menu->addAction(tr("Edit Project Settings..."), this, [this]() { emit EditProject(m_projectInfo.m_path); });
menu->addAction(tr("Build"), this, [this]() { emit BuildProject(m_projectInfo); });
@@ -168,20 +165,40 @@ namespace O3DE::ProjectManager
m_projectFooter->layout()->addWidget(projectMenuButton);
}
void ProjectButton::SetProjectButtonAction(const QString& text, AZStd::function<void()> lambda)
{
QPushButton* projectActionButton = m_projectImageLabel->GetActionButton();
if (!m_actionButtonConnection)
{
QSpacerItem* buttonSpacer = new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding);
m_projectImageLabel->layout()->addItem(buttonSpacer);
m_projectImageLabel->layout()->addWidget(projectActionButton);
projectActionButton->setVisible(true);
}
else
{
disconnect(m_actionButtonConnection);
}
projectActionButton->setText(text);
m_actionButtonConnection = connect(projectActionButton, &QPushButton::clicked, lambda);
}
void ProjectButton::SetProjectBuildButtonAction()
{
SetProjectButtonAction(tr("Build Project"), [this]() { emit BuildProject(m_projectInfo); });
}
void ProjectButton::BuildThisProject()
{
emit BuildProject(m_projectInfo);
}
void ProjectButton::SetLaunchButtonEnabled(bool enabled)
{
m_projectImageLabel->SetEnabled(enabled);
}
void ProjectButton::ShowBuildButton(bool show)
{
QSpacerItem* buttonSpacer = new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Expanding);
m_projectImageLabel->layout()->addItem(buttonSpacer);
m_projectImageLabel->layout()->addWidget(m_projectImageLabel->GetBuildButton());
m_projectImageLabel->GetBuildButton()->setVisible(show);
}
void ProjectButton::SetButtonOverlayText(const QString& text)
{
m_projectImageLabel->SetOverlayText(text);
@@ -191,4 +208,9 @@ namespace O3DE::ProjectManager
{
m_projectImageLabel->GetProgressBar()->setValue(progress);
}
LabelButton* ProjectButton::GetLabelButton()
{
return m_projectImageLabel;
}
} // namespace O3DE::ProjectManager
@@ -9,12 +9,15 @@
#if !defined(Q_MOC_RUN)
#include <ProjectInfo.h>
#include <AzCore/std/functional.h>
#include <QLabel>
#include <QPushButton>
#include <QSpacerItem>
#include <QLayout>
#endif
QT_FORWARD_DECLARE_CLASS(QPixmap)
QT_FORWARD_DECLARE_CLASS(QPushButton)
QT_FORWARD_DECLARE_CLASS(QAction)
QT_FORWARD_DECLARE_CLASS(QProgressBar)
@@ -34,7 +37,7 @@ namespace O3DE::ProjectManager
QLabel* GetOverlayLabel();
QProgressBar* GetProgressBar();
QPushButton* GetBuildButton();
QPushButton* GetActionButton();
signals:
void triggered();
@@ -45,7 +48,7 @@ namespace O3DE::ProjectManager
private:
QLabel* m_overlayLabel;
QProgressBar* m_progressBar;
QPushButton* m_buildButton;
QPushButton* m_actionButton;
bool m_enabled = true;
};
@@ -58,10 +61,13 @@ namespace O3DE::ProjectManager
explicit ProjectButton(const ProjectInfo& m_projectInfo, QWidget* parent = nullptr, bool processing = false);
~ProjectButton() = default;
void SetProjectButtonAction(const QString& text, AZStd::function<void()> lambda);
void SetProjectBuildButtonAction();
void SetLaunchButtonEnabled(bool enabled);
void ShowBuildButton(bool show);
void SetButtonOverlayText(const QString& text);
void SetProgressBarValue(int progress);
LabelButton* GetLabelButton();
signals:
void OpenProject(const QString& projectName);
@@ -75,9 +81,12 @@ namespace O3DE::ProjectManager
void BaseSetup();
void ProcessingSetup();
void ReadySetup();
void BuildThisProject();
ProjectInfo m_projectInfo;
LabelButton* m_projectImageLabel;
QFrame* m_projectFooter;
QMetaObject::Connection m_actionButtonConnection;
};
} // namespace O3DE::ProjectManager
@@ -14,6 +14,7 @@ namespace O3DE::ProjectManager
inline constexpr static int ProjectPreviewImageHeight = 280;
static const QString ProjectBuildPathPostfix = "build/windows_vs2019";
static const QString ProjectBuildErrorLogPathPostfix = "CMakeFiles/CMakeProjectBuildError.log";
static const QString ProjectBuildPathCmakeFiles = "CMakeFiles";
static const QString ProjectBuildErrorLogName = "CMakeProjectBuildError.log";
static const QString ProjectPreviewImagePath = "preview.png";
} // namespace O3DE::ProjectManager
@@ -11,7 +11,7 @@
#include <ProjectButtonWidget.h>
#include <PythonBindingsInterface.h>
#include <ProjectUtils.h>
#include <ProjectBuilder.h>
#include <ProjectBuilderController.h>
#include <ScreensCtrl.h>
#include <AzQtComponents/Components/FlowLayout.h>
@@ -43,8 +43,6 @@
#include <QDir>
#include <QGuiApplication>
//#define DISPLAY_PROJECT_DEV_DATA true
namespace O3DE::ProjectManager
{
ProjectsScreen::ProjectsScreen(QWidget* parent)
@@ -86,7 +84,7 @@ namespace O3DE::ProjectManager
layout->setAlignment(Qt::AlignTop);
frame->setLayout(layout);
QLabel* titleLabel = new QLabel(tr("Ready. Set. Create."), this);
QLabel* titleLabel = new QLabel(tr("Ready? Set. Create!"), this);
titleLabel->setObjectName("titleLabel");
layout->addWidget(titleLabel);
@@ -166,43 +164,38 @@ namespace O3DE::ProjectManager
projectsScrollArea->setWidget(scrollWidget);
projectsScrollArea->setWidgetResizable(true);
#ifndef DISPLAY_PROJECT_DEV_DATA
// Iterate once to insert building project first
if (!buildProjectPath.isEmpty())
QVector<ProjectInfo> nonProcessingProjects;
buildProjectPath = QDir::fromNativeSeparators(buildProjectPath);
for (auto& project : projectsResult.GetValue())
{
buildProjectPath = QDir::fromNativeSeparators(buildProjectPath);
for (auto project : projectsResult.GetValue())
if (projectButton && !*projectButton)
{
if (QDir::fromNativeSeparators(project.m_path) == buildProjectPath)
{
ProjectButton* buildingProjectButton = CreateProjectButton(project, flowLayout, true);
if (projectButton)
{
*projectButton = buildingProjectButton;
}
break;
*projectButton = CreateProjectButton(project, flowLayout, true);
continue;
}
}
nonProcessingProjects.append(project);
}
for (auto project : projectsResult.GetValue())
#else
ProjectInfo project = projectsResult.GetValue().at(0);
for (int i = 0; i < 15; i++)
#endif
for (auto& project : nonProcessingProjects)
{
// Add all other projects skipping building project
// Safe if no building project because it is just an empty string
if (project.m_path != buildProjectPath)
{
ProjectButton* projectButtonWidget = CreateProjectButton(project, flowLayout);
ProjectButton* projectButtonWidget = CreateProjectButton(project, flowLayout);
if (RequiresBuildProjectIterator(project.m_path) != m_requiresBuild.end())
{
projectButtonWidget->ShowBuildButton(true);
}
if (BuildQueueContainsProject(project.m_path))
{
projectButtonWidget->SetProjectButtonAction(tr("Cancel Queued Build"),
[this, project]
{
UnqueueBuildProject(project);
SuggestBuildProjectMsg(project, false);
});
}
else if (RequiresBuildProjectIterator(project.m_path) != m_requiresBuild.end())
{
projectButtonWidget->SetProjectBuildButtonAction();
}
}
@@ -244,9 +237,9 @@ namespace O3DE::ProjectManager
// Make sure to update builder with latest Project Button
if (m_currentBuilder)
{
ProjectButton* projectButtonPtr;
ProjectButton* projectButtonPtr = nullptr;
m_projectsContent = CreateProjectsContent(m_currentBuilder->GetProjectPath(), &projectButtonPtr);
m_projectsContent = CreateProjectsContent(m_currentBuilder->GetProjectInfo().m_path, &projectButtonPtr);
m_currentBuilder->SetProjectButton(projectButtonPtr);
}
else
@@ -414,17 +407,15 @@ namespace O3DE::ProjectManager
}
}
void ProjectsScreen::SuggestBuildProject(const ProjectInfo& projectInfo)
void ProjectsScreen::SuggestBuildProjectMsg(const ProjectInfo& projectInfo, bool showMessage)
{
if (projectInfo.m_needsBuild)
if (RequiresBuildProjectIterator(projectInfo.m_path) == m_requiresBuild.end())
{
if (RequiresBuildProjectIterator(projectInfo.m_path) == m_requiresBuild.end())
{
m_requiresBuild.append(projectInfo);
}
ResetProjectsContent();
m_requiresBuild.append(projectInfo);
}
else
ResetProjectsContent();
if (showMessage)
{
QMessageBox::information(this,
tr("Project Should be rebuilt."),
@@ -432,6 +423,11 @@ namespace O3DE::ProjectManager
}
}
void ProjectsScreen::SuggestBuildProject(const ProjectInfo& projectInfo)
{
SuggestBuildProjectMsg(projectInfo, true);
}
void ProjectsScreen::QueueBuildProject(const ProjectInfo& projectInfo)
{
auto requiredIter = RequiresBuildProjectIterator(projectInfo.m_path);
@@ -445,14 +441,22 @@ namespace O3DE::ProjectManager
if (m_buildQueue.empty() && !m_currentBuilder)
{
StartProjectBuild(projectInfo);
// Projects Content is already reset in fuction
}
else
{
m_buildQueue.append(projectInfo);
ResetProjectsContent();
}
}
}
void ProjectsScreen::UnqueueBuildProject(const ProjectInfo& projectInfo)
{
m_buildQueue.removeAll(projectInfo);
ResetProjectsContent();
}
void ProjectsScreen::NotifyCurrentScreen()
{
if (ShouldDisplayFirstTimeContent())
@@ -488,7 +492,7 @@ namespace O3DE::ProjectManager
return PythonBindingsInterface::Get()->RemoveInvalidProjects();
}
void ProjectsScreen::StartProjectBuild(const ProjectInfo& projectInfo)
bool ProjectsScreen::StartProjectBuild(const ProjectInfo& projectInfo)
{
if (ProjectUtils::IsVS2019Installed())
{
@@ -508,25 +512,42 @@ namespace O3DE::ProjectManager
}
else
{
ProjectBuildDone();
SuggestBuildProjectMsg(projectInfo, false);
return false;
}
return true;
}
return false;
}
void ProjectsScreen::ProjectBuildDone()
void ProjectsScreen::ProjectBuildDone(bool success)
{
ProjectInfo currentBuilderProject;
if (!success)
{
currentBuilderProject = m_currentBuilder->GetProjectInfo();
}
delete m_currentBuilder;
m_currentBuilder = nullptr;
if (!success)
{
SuggestBuildProjectMsg(currentBuilderProject, false);
}
if (!m_buildQueue.empty())
{
StartProjectBuild(m_buildQueue.front());
while (!StartProjectBuild(m_buildQueue.front()) && m_buildQueue.size() > 1)
{
m_buildQueue.pop_front();
}
m_buildQueue.pop_front();
}
else
{
ResetProjectsContent();
}
ResetProjectsContent();
}
QList<ProjectInfo>::iterator ProjectsScreen::RequiresBuildProjectIterator(const QString& projectPath)
@@ -37,7 +37,7 @@ namespace O3DE::ProjectManager
protected:
void NotifyCurrentScreen() override;
void ProjectBuildDone();
void SuggestBuildProjectMsg(const ProjectInfo& projectInfo, bool showMessage);
protected slots:
void HandleNewProjectButton();
@@ -50,6 +50,9 @@ namespace O3DE::ProjectManager
void SuggestBuildProject(const ProjectInfo& projectInfo);
void QueueBuildProject(const ProjectInfo& projectInfo);
void UnqueueBuildProject(const ProjectInfo& projectInfo);
void ProjectBuildDone(bool success = true);
void paintEvent(QPaintEvent* event) override;
@@ -61,7 +64,7 @@ namespace O3DE::ProjectManager
bool ShouldDisplayFirstTimeContent();
bool RemoveInvalidProjects();
void StartProjectBuild(const ProjectInfo& projectInfo);
bool StartProjectBuild(const ProjectInfo& projectInfo);
QList<ProjectInfo>::iterator RequiresBuildProjectIterator(const QString& projectPath);
bool BuildQueueContainsProject(const QString& projectPath);
bool WarnIfInBuildQueue(const QString& projectPath);
@@ -39,8 +39,10 @@ set(FILES
Source/ProjectInfo.cpp
Source/ProjectUtils.h
Source/ProjectUtils.cpp
Source/ProjectBuilder.h
Source/ProjectBuilder.cpp
Source/ProjectBuilderWorker.h
Source/ProjectBuilderWorker.cpp
Source/ProjectBuilderController.h
Source/ProjectBuilderController.cpp
Source/UpdateProjectSettingsScreen.h
Source/UpdateProjectSettingsScreen.cpp
Source/NewProjectSettingsScreen.h
@@ -86,7 +86,7 @@ namespace AZ
// AssImp only has one bitangentStream per mesh.
bitangentStream->SetBitangentSetIndex(0);
bitangentStream->SetTangentSpace(AZ::SceneAPI::DataTypes::TangentSpace::FromFbx);
bitangentStream->SetTangentSpace(AZ::SceneAPI::DataTypes::TangentSpace::FromSourceScene);
bitangentStream->ReserveContainerSpace(vertexCount);
for (int sdkMeshIndex = 0; sdkMeshIndex < currentNode->mNumMeshes; ++sdkMeshIndex)
{
@@ -88,7 +88,7 @@ namespace AZ
// AssImp only has one tangentStream per mesh.
tangentStream->SetTangentSetIndex(0);
tangentStream->SetTangentSpace(AZ::SceneAPI::DataTypes::TangentSpace::FromFbx);
tangentStream->SetTangentSpace(AZ::SceneAPI::DataTypes::TangentSpace::FromSourceScene);
tangentStream->ReserveContainerSpace(vertexCount);
for (int sdkMeshIndex = 0; sdkMeshIndex < currentNode->mNumMeshes; ++sdkMeshIndex)
{
@@ -23,9 +23,9 @@ namespace AZ
{
enum class TangentSpace
{
FromFbx = 0,
MikkT = 1,
EMotionFX = 2
FromSourceScene = 0,
MikkT = 1,
EMotionFX = 2
};
enum class BitangentMethod
@@ -20,7 +20,7 @@ namespace AZ
SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<MeshVertexBitangentData>()->Version(1);
serializeContext->Class<MeshVertexBitangentData>()->Version(2);
}
BehaviorContext* behaviorContext = azrtti_cast<BehaviorContext*>(context);
@@ -34,7 +34,7 @@ namespace AZ
->Method("GetBitangentSetIndex", &MeshVertexBitangentData::GetBitangentSetIndex)
->Method("GetTangentSpace", &MeshVertexBitangentData::GetTangentSpace)
->Enum<(int)SceneAPI::DataTypes::TangentSpace::EMotionFX>("EMotionFX")
->Enum<(int)SceneAPI::DataTypes::TangentSpace::FromFbx>("FromFbx")
->Enum<(int)SceneAPI::DataTypes::TangentSpace::FromSourceScene>("FromSourceScene")
->Enum<(int)SceneAPI::DataTypes::TangentSpace::MikkT>("MikkT");
}
}
@@ -50,7 +50,7 @@ namespace AZ
SCENE_DATA_API void GetDebugOutput(AZ::SceneAPI::Utilities::DebugOutput& output) const override;
protected:
AZStd::vector<AZ::Vector3> m_bitangents;
AZ::SceneAPI::DataTypes::TangentSpace m_tangentSpace = AZ::SceneAPI::DataTypes::TangentSpace::FromFbx;
AZ::SceneAPI::DataTypes::TangentSpace m_tangentSpace = AZ::SceneAPI::DataTypes::TangentSpace::FromSourceScene;
size_t m_setIndex = 0;
};
@@ -20,7 +20,7 @@ namespace AZ
SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<MeshVertexTangentData>()->Version(1);
serializeContext->Class<MeshVertexTangentData>()->Version(2);
}
BehaviorContext* behaviorContext = azrtti_cast<BehaviorContext*>(context);
@@ -34,7 +34,7 @@ namespace AZ
->Method("GetTangentSetIndex", &MeshVertexTangentData::GetTangentSetIndex)
->Method("GetTangentSpace", &MeshVertexTangentData::GetTangentSpace)
->Enum<(int)SceneAPI::DataTypes::TangentSpace::EMotionFX>("EMotionFX")
->Enum<(int)SceneAPI::DataTypes::TangentSpace::FromFbx>("FromFbx")
->Enum<(int)SceneAPI::DataTypes::TangentSpace::FromSourceScene>("FromSourceScene")
->Enum<(int)SceneAPI::DataTypes::TangentSpace::MikkT>("MikkT");
}
}
@@ -49,7 +49,7 @@ namespace AZ
SCENE_DATA_API void GetDebugOutput(AZ::SceneAPI::Utilities::DebugOutput& output) const override;
protected:
AZStd::vector<AZ::Vector4> m_tangents;
AZ::SceneAPI::DataTypes::TangentSpace m_tangentSpace = AZ::SceneAPI::DataTypes::TangentSpace::FromFbx;
AZ::SceneAPI::DataTypes::TangentSpace m_tangentSpace = AZ::SceneAPI::DataTypes::TangentSpace::FromSourceScene;
size_t m_setIndex = 0;
};
@@ -142,7 +142,7 @@ namespace AZ
return;
}
serializeContext->Class<TangentsRule, DataTypes::IRule>()->Version(1)
serializeContext->Class<TangentsRule, DataTypes::IRule>()->Version(2)
->Field("tangentSpace", &TangentsRule::m_tangentSpace)
->Field("bitangentMethod", &TangentsRule::m_bitangentMethod)
->Field("normalize", &TangentsRule::m_normalize)
@@ -156,7 +156,7 @@ namespace AZ
->Attribute("AutoExpand", true)
->Attribute(AZ::Edit::Attributes::NameLabelOverride, "")
->DataElement(AZ::Edit::UIHandlers::ComboBox, &AZ::SceneAPI::SceneData::TangentsRule::m_tangentSpace, "Tangent space", "Specify the tangent space used for normal map baking. Choose 'From Fbx' to extract the tangents and bitangents directly from the Fbx file. When there is no tangents rule or the Fbx has no tangents stored inside it, the 'MikkT' option will be used with orthogonal tangents of unit length, so with the normalize option enabled, using the first UV set.")
->EnumAttribute(AZ::SceneAPI::DataTypes::TangentSpace::FromFbx, "From Fbx")
->EnumAttribute(AZ::SceneAPI::DataTypes::TangentSpace::FromSourceScene, "From Source Scene")
->EnumAttribute(AZ::SceneAPI::DataTypes::TangentSpace::MikkT, "MikkT")
->EnumAttribute(AZ::SceneAPI::DataTypes::TangentSpace::EMotionFX, "EMotion FX")
->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree)
@@ -83,7 +83,7 @@ namespace AZ
auto* bitangentData = AZStd::any_cast<AZ::SceneData::GraphData::MeshVertexBitangentData>(&data);
bitangentData->AppendBitangent(AZ::Vector3{0.12f, 0.34f, 0.56f});
bitangentData->AppendBitangent(AZ::Vector3{0.77f, 0.88f, 0.99f});
bitangentData->SetTangentSpace(AZ::SceneAPI::DataTypes::TangentSpace::FromFbx);
bitangentData->SetTangentSpace(AZ::SceneAPI::DataTypes::TangentSpace::FromSourceScene);
bitangentData->SetBitangentSetIndex(1);
return true;
}
@@ -317,7 +317,7 @@ namespace AZ
ExpectExecute("TestExpectFloatEquals(bitangentData.y, 0.88)");
ExpectExecute("TestExpectFloatEquals(bitangentData.z, 0.99)");
ExpectExecute("TestExpectIntegerEquals(meshVertexBitangentData:GetBitangentSetIndex(), 1)");
ExpectExecute("TestExpectTrue(meshVertexBitangentData:GetTangentSpace(), MeshVertexBitangentData.FromFbx)");
ExpectExecute("TestExpectTrue(meshVertexBitangentData:GetTangentSpace(), MeshVertexBitangentData.FromSourceScene)");
}
TEST_F(GrapDatahBehaviorScriptTest, SceneGraph_MeshVertexTangentData_AccessWorks)