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
+11 -2
View File
@@ -22,7 +22,9 @@ AZ_PUSH_DISABLE_WARNING(4251 4355 4996, "-Wunknown-warning-option")
AZ_POP_DISABLE_WARNING
// AzCore
#include <AzCore/IO/Path/Path.h>
#include <AzCore/JSON/document.h>
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
// AzFramework
#include <AzFramework/Terrain/TerrainDataRequestBus.h>
@@ -195,8 +197,15 @@ CEditorImpl::CEditorImpl()
m_pAssetBrowserRequestHandler = nullptr;
m_assetEditorRequestsHandler = nullptr;
AZ::IO::SystemFile::CreateDir("SessionStatus");
QFile::setPermissions(m_crashLogFileName, QFileDevice::ReadOther | QFileDevice::WriteOther);
if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
{
if (AZ::IO::FixedMaxPath crashLogPath; settingsRegistry->Get(crashLogPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectUserPath))
{
crashLogPath /= m_crashLogFileName;
AZ::IO::SystemFile::CreateDir(crashLogPath.ParentPath().FixedMaxPathString().c_str());
QFile::setPermissions(crashLogPath.c_str(), QFileDevice::ReadOther | QFileDevice::WriteOther);
}
}
}
void CEditorImpl::Initialize()
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:263e95489560dac6e5944ef3caba13e598f83ddead324b943ad7735ba015e1a9
size 70727
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:1cf8339fb51f82a68d2ab475c0476960e75a79d96d239c5b7cd272fbe4990ffe
size 2770
@@ -75,6 +75,21 @@ WelcomeScreenDialog::WelcomeScreenDialog(QWidget* pParent)
{
ui->setupUi(this);
// Set the project preview image
QString projectPreviewPath = QDir(AZ::Utils::GetProjectPath().c_str()).filePath("preview.png");
QFileInfo projectPreviewPathInfo(projectPreviewPath);
if (!projectPreviewPathInfo.exists() || !projectPreviewPathInfo.isFile())
{
projectPreviewPath = ":/WelcomeScreenDialog/DefaultProjectImage.png";
}
ui->activeProjectIcon->setPixmap(
QPixmap(projectPreviewPath).scaled(
ui->activeProjectIcon->size(),
Qt::KeepAspectRatioByExpanding,
Qt::SmoothTransformation
)
);
ui->recentLevelTable->setColumnCount(3);
ui->recentLevelTable->setMouseTracking(true);
ui->recentLevelTable->setContextMenuPolicy(Qt::CustomContextMenu);
@@ -1,5 +1,5 @@
<RCC>
<qresource prefix="WelcomeScreenDialog">
<file>DefaultActiveProject.png</file>
<file>DefaultProjectImage.png</file>
</qresource>
</RCC>
@@ -158,11 +158,8 @@
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap resource="WelcomeScreenDialog.qrc">:/WelcomeScreenDialog/DefaultActiveProject.png</pixmap>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
<set>Qt::AlignHCenter | Qt::AlignVCenter</set>
</property>
</widget>
</item>
-1
View File
@@ -229,7 +229,6 @@ set(FILES
res/warning16x16.ico
res/water.bmp
res/work_in_progress_icon.ico
res/LegacyLogo.bmp
res/MannFileManagerImageList.bmp
res/infobar/CameraCollision-default.svg
res/infobar/GotoLocation-default.svg
-3
View File
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:97f4004da6ec1bf07d522d6675f814e53e36923a45084ff5d7e4113e293d4fec
size 117656
+1 -1
View File
@@ -41,7 +41,7 @@ ly_add_target(
)
ly_add_target(
NAME AmazonQtControlGallery APPLICATION
NAME O3DEQtControlGallery APPLICATION
NAMESPACE AZ
AUTOMOC
AUTOUIC
@@ -45,6 +45,7 @@
#include <AzToolsFramework/UI/UICore/QTreeViewStateSaver.hxx>
#include <AzToolsFramework/UI/UICore/QWidgetSavedState.h>
#include <AzToolsFramework/UI/UICore/ProgressShield.hxx>
#include <AzToolsFramework/UI/UICore/WidgetHelpers.h>
#include <AzToolsFramework/Slice/SliceUtilities.h>
#include <AzToolsFramework/ToolsComponents/EditorInspectorComponent.h>
#include <AzToolsFramework/API/ComponentEntityObjectBus.h>
@@ -1589,7 +1590,13 @@ namespace AzToolsFramework
// Multiple changes to the same entity are just split between different undo nodes.
for (AZ::EntityId entityId : m_dirtyEntities)
{
prefabPublicInterface->GenerateUndoNodesForEntityChangeAndUpdateCache(entityId, m_currentBatchUndo);
auto outcome = prefabPublicInterface->GenerateUndoNodesForEntityChangeAndUpdateCache(entityId, m_currentBatchUndo);
if (!outcome.IsSuccess())
{
QMessageBox::warning(
AzToolsFramework::GetActiveWindow(), QString("Error"), QString(outcome.GetError().c_str()), QMessageBox::Ok, QMessageBox::Ok);
}
}
}
}
@@ -587,21 +587,21 @@ namespace AzToolsFramework
return AZ::Success(entityId);
}
void PrefabPublicHandler::GenerateUndoNodesForEntityChangeAndUpdateCache(
PrefabOperationResult PrefabPublicHandler::GenerateUndoNodesForEntityChangeAndUpdateCache(
AZ::EntityId entityId, UndoSystem::URSequencePoint* parentUndoBatch)
{
// Create Undo node on entities if they belong to an instance
InstanceOptionalReference owningInstance = m_instanceEntityMapperInterface->FindOwningInstance(entityId);
if (!owningInstance.has_value())
{
return;
return AZ::Success();
}
AZ::Entity* entity = GetEntityById(entityId);
if (!entity)
{
m_prefabUndoCache.PurgeCache(entityId);
return;
return AZ::Success();
}
PrefabDom beforeState;
@@ -633,6 +633,41 @@ namespace AzToolsFramework
(&beforeOwningInstance->get() != &afterOwningInstance->get()))
{
isNewParentOwnedByDifferentInstance = true;
// Detect loops. Assert if an instance has been reparented in such a way to generate circular dependencies.
AZStd::vector<Instance*> instancesInvolved;
if (isInstanceContainerEntity)
{
instancesInvolved.push_back(&owningInstance->get());
}
else
{
// Retrieve all nested instances that are part of the subtree under the current entity.
EntityList entities;
RetrieveAndSortPrefabEntitiesAndInstances({ entity }, beforeOwningInstance->get(), entities, instancesInvolved);
}
for (Instance* instance : instancesInvolved)
{
const PrefabDom& templateDom =
m_prefabSystemComponentInterface->FindTemplateDom(instance->GetTemplateId());
AZStd::unordered_set<AZ::IO::Path> templatePaths;
PrefabDomUtils::GetTemplateSourcePaths(templateDom, templatePaths);
if (IsCyclicalDependencyFound(afterOwningInstance->get(), templatePaths))
{
// Cancel the operation by restoring the previous parent
AZ::TransformBus::Event(entityId, &AZ::TransformBus::Events::SetParent, beforeParentId);
m_prefabUndoCache.UpdateCache(entityId);
// Skip the creation of an undo node
return AZ::Failure(AZStd::string::format(
"Reparent Prefab operation aborted - Cyclical dependency detected\n(%s depends on %s).",
instance->GetTemplateSourcePath().Native().c_str(),
afterOwningInstance->get().GetTemplateSourcePath().Native().c_str()));
}
}
}
}
@@ -673,6 +708,8 @@ namespace AzToolsFramework
}
m_prefabUndoCache.UpdateCache(entityId);
return AZ::Success();
}
void PrefabPublicHandler::Internal_HandleContainerOverride(
@@ -46,7 +46,7 @@ namespace AzToolsFramework
PrefabOperationResult SavePrefab(AZ::IO::Path filePath) override;
PrefabEntityResult CreateEntity(AZ::EntityId parentId, const AZ::Vector3& position) override;
void GenerateUndoNodesForEntityChangeAndUpdateCache(AZ::EntityId entityId, UndoSystem::URSequencePoint* parentUndoBatch) override;
PrefabOperationResult GenerateUndoNodesForEntityChangeAndUpdateCache(AZ::EntityId entityId, UndoSystem::URSequencePoint* parentUndoBatch) override;
bool IsInstanceContainerEntity(AZ::EntityId entityId) const override;
bool IsLevelInstanceContainerEntity(AZ::EntityId entityId) const override;
@@ -79,8 +79,9 @@ namespace AzToolsFramework
*
* @param entityId The entity to patch.
* @param parentUndoBatch The undo batch the undo nodes should be parented to.
* @return Returns Success if the node was generated correctly, or an error message otherwise.
*/
virtual void GenerateUndoNodesForEntityChangeAndUpdateCache(
virtual PrefabOperationResult GenerateUndoNodesForEntityChangeAndUpdateCache(
AZ::EntityId entityId, UndoSystem::URSequencePoint* parentUndoBatch) = 0;
/**
@@ -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)