You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Util/Util.cpp

292 lines
12 KiB
C++

/*
* 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 <Atom/ImageProcessing/ImageObject.h>
#include <Atom/ImageProcessing/ImageProcessingBus.h>
#include <AtomToolsFramework/Util/Util.h>
#include <AzCore/IO/ByteContainerStream.h>
#include <AzCore/IO/SystemFile.h>
#include <AzCore/Jobs/JobFunction.h>
#include <AzCore/Math/Color.h>
#include <AzCore/Math/Vector2.h>
#include <AzCore/Math/Vector3.h>
#include <AzCore/Math/Vector4.h>
#include <AzCore/Settings/SettingsRegistry.h>
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
#include <AzCore/StringFunc/StringFunc.h>
#include <AzCore/Utils/Utils.h>
#include <AzFramework/API/ApplicationAPI.h>
#include <AzQtComponents/Components/Widgets/FileDialog.h>
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserBus.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserEntry.h>
#include <AzToolsFramework/AssetBrowser/AssetSelectionModel.h>
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT
#include <QApplication>
#include <QMessageBox>
#include <QProcess>
AZ_POP_DISABLE_WARNING
namespace AtomToolsFramework
{
bool SaveSettingsToFile(const AZ::IO::FixedMaxPath& savePath, const AZStd::vector<AZStd::string>& filters)
{
auto registry = AZ::SettingsRegistry::Get();
if (registry == nullptr)
{
AZ_Warning("AtomToolsFramework", false, "Unable to access global settings registry.");
return false;
}
AZ::SettingsRegistryMergeUtils::DumperSettings dumperSettings;
dumperSettings.m_prettifyOutput = true;
dumperSettings.m_includeFilter = [filters](AZStd::string_view path)
{
for (const auto& filter : filters)
{
if (filter.starts_with(path.substr(0, filter.size())))
{
return true;
}
}
return false;
};
AZStd::string stringBuffer;
AZ::IO::ByteContainerStream stringStream(&stringBuffer);
if (!AZ::SettingsRegistryMergeUtils::DumpSettingsRegistryToStream(*registry, "", stringStream, dumperSettings))
{
AZ_Warning("AtomToolsFramework", false, R"(Unable to save changes to the registry file at "%s"\n)", savePath.c_str());
return false;
}
bool saved = false;
constexpr auto configurationMode =
AZ::IO::SystemFile::SF_OPEN_CREATE | AZ::IO::SystemFile::SF_OPEN_CREATE_PATH | AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY;
if (AZ::IO::SystemFile outputFile; outputFile.Open(savePath.c_str(), configurationMode))
{
saved = outputFile.Write(stringBuffer.data(), stringBuffer.size()) == stringBuffer.size();
}
AZ_Warning("AtomToolsFramework", saved, R"(Unable to save registry file to path "%s"\n)", savePath.c_str());
return saved;
}
void LoadImageAsync(const AZStd::string& path, LoadImageAsyncCallback callback)
{
AZ::Job* job = AZ::CreateJobFunction(
[path, callback]()
{
ImageProcessingAtom::IImageObjectPtr imageObject;
ImageProcessingAtom::ImageProcessingRequestBus::BroadcastResult(
imageObject, &ImageProcessingAtom::ImageProcessingRequests::LoadImagePreview, path);
if (imageObject)
{
AZ::u8* imageBuf = nullptr;
AZ::u32 pitch = 0;
AZ::u32 mip = 0;
imageObject->GetImagePointer(mip, imageBuf, pitch);
const AZ::u32 width = imageObject->GetWidth(mip);
const AZ::u32 height = imageObject->GetHeight(mip);
QImage image(imageBuf, width, height, pitch, QImage::Format_RGBA8888);
if (callback)
{
callback(image);
}
}
},
true);
job->Start();
}
QFileInfo GetSaveFileInfo(const QString& initialPath)
{
const QFileInfo initialFileInfo(initialPath);
const QString initialExt(initialFileInfo.completeSuffix());
// Instead of just passing in the absolute file path, we pass in the absolute folder path and the base name to prevent the file
// dialog from displaying multiple extensions when the extension contains a "."
const QFileInfo selectedFileInfo(AzQtComponents::FileDialog::GetSaveFileName(
QApplication::activeWindow(),
"Save File",
initialFileInfo.absolutePath() +
AZ_CORRECT_FILESYSTEM_SEPARATOR_STRING +
initialFileInfo.baseName(),
QString("Files (*.%1)").arg(initialExt)));
if (selectedFileInfo.absoluteFilePath().isEmpty())
{
// Cancelled operation
return QFileInfo();
}
if (!selectedFileInfo.absoluteFilePath().endsWith(initialExt))
{
QMessageBox::critical(QApplication::activeWindow(), "Error", QString("File name must have .%1 extension.").arg(initialExt));
return QFileInfo();
}
// Reconstructing the file info from the absolute path and expected extension to compensate for an issue with the save file
// dialog adding the extension multiple times if it contains "." like *.lightingpreset.azasset
return QFileInfo(selectedFileInfo.absolutePath() + AZ_CORRECT_FILESYSTEM_SEPARATOR_STRING + selectedFileInfo.baseName() + "." + initialExt);
}
QFileInfo GetOpenFileInfo(const AZStd::vector<AZ::Data::AssetType>& assetTypes)
{
using namespace AZ::Data;
using namespace AzToolsFramework::AssetBrowser;
// [GFX TODO] Should this just be an open file dialog filtered to supported source data extensions?
auto selection = AssetSelectionModel::AssetTypesSelection(assetTypes);
// [GFX TODO] This is functional but UI is not as designed
AssetBrowserComponentRequestBus::Broadcast(&AssetBrowserComponentRequests::PickAssets, selection, QApplication::activeWindow());
if (!selection.IsValid())
{
return QFileInfo();
}
auto entry = selection.GetResult();
const SourceAssetBrowserEntry* sourceEntry = azrtti_cast<const SourceAssetBrowserEntry*>(entry);
if (!sourceEntry)
{
const ProductAssetBrowserEntry* productEntry = azrtti_cast<const ProductAssetBrowserEntry*>(entry);
if (productEntry)
{
sourceEntry = azrtti_cast<const SourceAssetBrowserEntry*>(productEntry->GetParent());
}
}
if (!sourceEntry)
{
return QFileInfo();
}
return QFileInfo(sourceEntry->GetFullPath().c_str());
}
QFileInfo GetUniqueFileInfo(const QString& initialPath)
{
int counter = 0;
QFileInfo fileInfo = initialPath;
const QString extension = "." + fileInfo.completeSuffix();
const QString basePathAndName = fileInfo.absoluteFilePath().left(fileInfo.absoluteFilePath().size() - extension.size());
while (fileInfo.exists())
{
fileInfo = QString("%1_%2%3").arg(basePathAndName).arg(++counter).arg(extension);
}
return fileInfo;
}
QFileInfo GetDuplicationFileInfo(const QString& initialPath)
{
const QFileInfo initialFileInfo(initialPath);
const QString initialExt(initialFileInfo.completeSuffix());
const QFileInfo duplicateFileInfo(AzQtComponents::FileDialog::GetSaveFileName(
QApplication::activeWindow(),
"Duplicate File",
GetUniqueFileInfo(initialPath).absoluteFilePath(),
QString("Files (*.%1)").arg(initialExt)));
if (duplicateFileInfo == initialFileInfo)
{
// Cancelled operation or selected same path
return QFileInfo();
}
if (duplicateFileInfo.absoluteFilePath().isEmpty())
{
// Cancelled operation or selected same path
return QFileInfo();
}
if (!duplicateFileInfo.absoluteFilePath().endsWith(initialExt))
{
QMessageBox::critical(QApplication::activeWindow(), "Error", QString("File name must have .%1 extension.").arg(initialExt));
return QFileInfo();
}
return duplicateFileInfo;
}
bool LaunchTool(const QString& baseName, const QStringList& arguments)
{
AZ::IO::FixedMaxPath engineRoot = AZ::Utils::GetEnginePath();
AZ_Assert(!engineRoot.empty(), "Cannot query Engine Path");
AZ::IO::FixedMaxPath launchPath =
AZ::IO::FixedMaxPath(AZ::Utils::GetExecutableDirectory()) / (baseName + AZ_TRAIT_OS_EXECUTABLE_EXTENSION).toUtf8().constData();
return QProcess::startDetached(launchPath.c_str(), arguments, engineRoot.c_str());
}
AZStd::string GetExteralReferencePath(
const AZStd::string& exportPath, const AZStd::string& referencePath, const bool relativeToExportPath)
{
if (referencePath.empty())
{
return {};
}
if (!relativeToExportPath)
{
AZStd::string watchFolder;
AZ::Data::AssetInfo assetInfo;
bool sourceInfoFound = false;
AzToolsFramework::AssetSystemRequestBus::BroadcastResult(
sourceInfoFound, &AzToolsFramework::AssetSystemRequestBus::Events::GetSourceInfoBySourcePath, referencePath.c_str(),
assetInfo, watchFolder);
if (sourceInfoFound)
{
return assetInfo.m_relativePath;
}
}
AZ::IO::BasicPath<AZStd::string> exportFolder(exportPath);
exportFolder.RemoveFilename();
return AZ::IO::PathView(referencePath).LexicallyRelative(exportFolder).StringAsPosix();
}
template<typename T>
bool ComparePropertyValues(const AZStd::any& valueA, const AZStd::any& valueB)
{
return valueA.is<T>() && valueB.is<T>() && *AZStd::any_cast<T>(&valueA) == *AZStd::any_cast<T>(&valueB);
}
bool ArePropertyValuesEqual(const AZStd::any& valueA, const AZStd::any& valueB)
{
if (valueA.type() != valueB.type())
{
return false;
}
if (ComparePropertyValues<bool>(valueA, valueB) || ComparePropertyValues<int32_t>(valueA, valueB) ||
ComparePropertyValues<uint32_t>(valueA, valueB) || ComparePropertyValues<float>(valueA, valueB) ||
ComparePropertyValues<AZ::Vector2>(valueA, valueB) || ComparePropertyValues<AZ::Vector3>(valueA, valueB) ||
ComparePropertyValues<AZ::Vector4>(valueA, valueB) || ComparePropertyValues<AZ::Color>(valueA, valueB) ||
ComparePropertyValues<AZ::Data::AssetId>(valueA, valueB) ||
ComparePropertyValues<AZ::Data::Asset<AZ::Data::AssetData>>(valueA, valueB) ||
ComparePropertyValues<AZ::Data::Asset<AZ::RPI::ImageAsset>>(valueA, valueB) ||
ComparePropertyValues<AZ::Data::Asset<AZ::RPI::StreamingImageAsset>>(valueA, valueB) ||
ComparePropertyValues<AZ::Data::Asset<AZ::RPI::MaterialAsset>>(valueA, valueB) ||
ComparePropertyValues<AZ::Data::Asset<AZ::RPI::MaterialTypeAsset>>(valueA, valueB) ||
ComparePropertyValues<AZStd::string>(valueA, valueB))
{
return true;
}
return false;
}
} // namespace AtomToolsFramework