Merge branch 'main' into SpawnableEntityIdMapping
This commit is contained in:
@@ -137,5 +137,6 @@ enum class AnimParamType
|
||||
Invalid = static_cast<int>(0xFFFFFFFF)
|
||||
};
|
||||
|
||||
static const int OLD_APARAM_USER = 100;
|
||||
|
||||
#endif // CRYINCLUDE_CRYCOMMON_MAESTRO_TYPES_ANIMPARAMTYPE_H
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
|
||||
#include <AzFramework/IO/FileOperations.h>
|
||||
#include <AzCore/IO/FileIO.h>
|
||||
#include <AzCore/IO/Path/Path.h>
|
||||
|
||||
#ifdef WIN32
|
||||
#include <time.h>
|
||||
@@ -88,7 +89,6 @@ CLog::CLog(ISystem* pSystem)
|
||||
|
||||
m_nMainThreadId = CryGetCurrentThreadId();
|
||||
|
||||
m_logFileHandle = AZ::IO::InvalidHandle;
|
||||
#if defined(KEEP_LOG_FILE_OPEN)
|
||||
m_bFirstLine = true;
|
||||
#endif
|
||||
@@ -162,35 +162,6 @@ void CLog::RegisterConsoleVariables()
|
||||
REGISTER_COMMAND("log_flush", &LogFlushFile, 0, "Flush the log file");
|
||||
#endif
|
||||
}
|
||||
/*
|
||||
//testbed
|
||||
{
|
||||
int iSave0 = m_pLogVerbosity->GetIVal();
|
||||
int iSave1 = m_pLogFileVerbosity->GetIVal();
|
||||
|
||||
for(int i=0;i<=4;++i)
|
||||
{
|
||||
m_pLogVerbosity->Set(i);
|
||||
m_pLogFileVerbosity->Set(i);
|
||||
|
||||
LogWithType(eAlways,"CLog selftest: Verbosity=%d FileVerbosity=%d",m_pLogVerbosity->GetIVal(),m_pLogFileVerbosity->GetIVal());
|
||||
LogWithType(eAlways,"--------------");
|
||||
|
||||
LogWithType(eError,"eError");
|
||||
LogWithType(eWarning,"eWarning");
|
||||
LogWithType(eMessage,"eMessage");
|
||||
LogWithType(eInput,"eInput");
|
||||
LogWithType(eInputResponse,"eInputResponse");
|
||||
|
||||
LogWarning("LogWarning()");
|
||||
LogError("LogError()");
|
||||
LogWithType(eAlways,"--------------");
|
||||
}
|
||||
|
||||
m_pLogVerbosity->Set(iSave0);
|
||||
m_pLogFileVerbosity->Set(iSave1);
|
||||
}
|
||||
*/
|
||||
#undef DEFAULT_VERBOSITY
|
||||
}
|
||||
|
||||
@@ -210,7 +181,7 @@ CLog::~CLog()
|
||||
|
||||
UnregisterConsoleVariables();
|
||||
|
||||
CloseLogFile(true);
|
||||
CloseLogFile();
|
||||
}
|
||||
|
||||
void CLog::UnregisterConsoleVariables()
|
||||
@@ -224,31 +195,36 @@ void CLog::UnregisterConsoleVariables()
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CLog::CloseLogFile([[maybe_unused]] bool forceClose)
|
||||
void CLog::CloseLogFile()
|
||||
{
|
||||
if (m_logFileHandle != AZ::IO::InvalidHandle)
|
||||
{
|
||||
AZ::IO::FileIOBase::GetDirectInstance()->Close(m_logFileHandle);
|
||||
m_logFileHandle = AZ::IO::InvalidHandle;
|
||||
}
|
||||
m_logFileHandle.Close();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
AZ::IO::HandleType CLog::OpenLogFile(const char* filename, const char* mode)
|
||||
bool CLog::OpenLogFile(const char* filename, int mode)
|
||||
{
|
||||
using namespace AZ::IO;
|
||||
|
||||
AZ_Assert(m_logFileHandle == AZ::IO::InvalidHandle, "Attempt to open log file when one is already open. This would lead to a handle leak.");
|
||||
|
||||
if ((!filename) || (filename[0] == 0))
|
||||
if (m_logFileHandle.IsOpen())
|
||||
{
|
||||
return m_logFileHandle;
|
||||
// Can only AZ_Assert if a file is open, otherwise the AZ_Assert
|
||||
// would eventually lead to OpenLogFile being opened up again
|
||||
AZ_Assert(false, "Attempt to open log file when one is already open. This would lead to a handle leak.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (filename == nullptr || filename[0] == '\0')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// it is assumed that @log@ points at the appropriate place (so for apple, to the user profile dir)
|
||||
AZ::IO::FileIOBase::GetDirectInstance()->Open(filename, AZ::IO::GetOpenModeFromStringMode(mode), m_logFileHandle);
|
||||
AZ::IO::FileIOBase* fileSystem = AZ::IO::FileIOBase::GetDirectInstance();
|
||||
if (AZ::IO::FixedMaxPath logFilePath; fileSystem->ReplaceAlias(logFilePath, filename))
|
||||
{
|
||||
logFilePath = logFilePath.LexicallyNormal();
|
||||
m_logFileHandle.Open(logFilePath.c_str(), mode);
|
||||
}
|
||||
|
||||
if (m_logFileHandle != AZ::IO::InvalidHandle)
|
||||
if (m_logFileHandle.IsOpen())
|
||||
{
|
||||
#if defined(KEEP_LOG_FILE_OPEN)
|
||||
m_bFirstLine = true;
|
||||
@@ -257,11 +233,11 @@ AZ::IO::HandleType CLog::OpenLogFile(const char* filename, const char* mode)
|
||||
else
|
||||
{
|
||||
#if defined(LINUX) || defined(APPLE)
|
||||
syslog(LOG_NOTICE, "Failed to open log file [%s], mode [%s]", filename, mode);
|
||||
syslog(LOG_NOTICE, "Failed to open log file [%s], mode [%d]", filename, mode);
|
||||
#endif
|
||||
}
|
||||
|
||||
return m_logFileHandle;
|
||||
return m_logFileHandle.IsOpen();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@@ -1114,12 +1090,15 @@ void CLog::LogStringToFile(const char* szString, ELogType logType, bool bAdd, [[
|
||||
|
||||
if (logToFile)
|
||||
{
|
||||
if (m_logFileHandle == AZ::IO::InvalidHandle)
|
||||
if (!m_logFileHandle.IsOpen())
|
||||
{
|
||||
OpenLogFile(m_szFilename, "w+t");
|
||||
constexpr auto openMode = AZ::IO::SystemFile::OpenMode::SF_OPEN_APPEND
|
||||
| AZ::IO::SystemFile::OpenMode::SF_OPEN_CREATE
|
||||
| AZ::IO::SystemFile::OpenMode::SF_OPEN_WRITE_ONLY;
|
||||
OpenLogFile(m_szFilename, openMode);
|
||||
}
|
||||
|
||||
if (m_logFileHandle != AZ::IO::InvalidHandle)
|
||||
if (m_logFileHandle.IsOpen())
|
||||
{
|
||||
#if defined(KEEP_LOG_FILE_OPEN)
|
||||
if (m_bFirstLine)
|
||||
@@ -1130,9 +1109,9 @@ void CLog::LogStringToFile(const char* szString, ELogType logType, bool bAdd, [[
|
||||
if (bAdd)
|
||||
{
|
||||
// if adding to a prior line erase the \n at the end.
|
||||
AZ::IO::FileIOBase::GetDirectInstance()->Seek(m_logFileHandle, -2, AZ::IO::SeekType::SeekFromEnd);
|
||||
m_logFileHandle.Seek(-2, AZ::IO::SystemFile::SeekMode::SF_SEEK_END);
|
||||
}
|
||||
AZ::IO::FPutS(tempString.c_str(), m_logFileHandle);
|
||||
m_logFileHandle.Write(tempString.c_str(), tempString.size());
|
||||
#if !defined(KEEP_LOG_FILE_OPEN)
|
||||
CloseLogFile();
|
||||
#endif
|
||||
@@ -1383,6 +1362,23 @@ bool CLog::SetFileName(const char* fileNameOrAbsolutePath, bool backupLogs)
|
||||
|
||||
CreateBackupFile();
|
||||
|
||||
AZ::IO::FileIOBase* fileSystem = AZ::IO::FileIOBase::GetDirectInstance();
|
||||
AZ::IO::FixedMaxPath newLogFilePath;
|
||||
if (fileSystem->ReplaceAlias(newLogFilePath, m_szFilename))
|
||||
{
|
||||
newLogFilePath = newLogFilePath.LexicallyNormal();
|
||||
}
|
||||
if (m_logFileHandle.IsOpen() && newLogFilePath != m_logFileHandle.Name())
|
||||
{
|
||||
constexpr auto openMode = AZ::IO::SystemFile::OpenMode::SF_OPEN_APPEND
|
||||
| AZ::IO::SystemFile::OpenMode::SF_OPEN_CREATE
|
||||
| AZ::IO::SystemFile::OpenMode::SF_OPEN_WRITE_ONLY;
|
||||
if(AZ::IO::SystemFile newLogFile; newLogFile.Open(m_szFilename, openMode))
|
||||
{
|
||||
m_logFileHandle = AZStd::move(newLogFile);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1537,9 +1533,9 @@ const char* CLog::GetModuleFilter()
|
||||
void CLog::FlushAndClose()
|
||||
{
|
||||
#if defined(KEEP_LOG_FILE_OPEN)
|
||||
if (m_logFileHandle)
|
||||
if (m_logFileHandle.IsOpen())
|
||||
{
|
||||
CloseLogFile(true);
|
||||
CloseLogFile();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -137,8 +137,8 @@ private: // -------------------------------------------------------------------
|
||||
void LogStringToConsole(const char* szString, ELogType logType, bool bAdd) {}
|
||||
#endif // !defined(EXCLUDE_NORMAL_LOG)
|
||||
|
||||
AZ::IO::HandleType OpenLogFile(const char* filename, const char* mode);
|
||||
void CloseLogFile(bool force = false);
|
||||
bool OpenLogFile(const char* filename, int mode);
|
||||
void CloseLogFile();
|
||||
|
||||
// will format the message into m_szTemp
|
||||
void FormatMessage(const char* szCommand, ...) PRINTF_PARAMS(2, 3);
|
||||
@@ -152,15 +152,11 @@ private: // -------------------------------------------------------------------
|
||||
virtual const char* GetAssetScopeString();
|
||||
#endif
|
||||
|
||||
ISystem* m_pSystem; //
|
||||
float m_fLastLoadingUpdateTime; // for non-frequent streamingEngine update
|
||||
//char m_szTemp[MAX_TEMP_LENGTH_SIZE]; //
|
||||
char m_szFilename[MAX_FILENAME_SIZE]; // can be with path
|
||||
mutable char m_sBackupFilename[MAX_FILENAME_SIZE]; // can be with path
|
||||
AZ::IO::HandleType m_logFileHandle;
|
||||
CryStackStringT<char, 32> m_LogMode; //mode m_pLogFile has been opened with
|
||||
AZ::IO::HandleType m_errFileHandle;
|
||||
int m_nErrCount;
|
||||
ISystem* m_pSystem; //
|
||||
float m_fLastLoadingUpdateTime; // for non-frequent streamingEngine update
|
||||
char m_szFilename[MAX_FILENAME_SIZE]; // can be with path
|
||||
mutable char m_sBackupFilename[MAX_FILENAME_SIZE]; // can be with path
|
||||
AZ::IO::SystemFile m_logFileHandle;
|
||||
|
||||
bool m_backupLogs;
|
||||
|
||||
|
||||
@@ -1208,7 +1208,7 @@ bool CSystem::Init(const SSystemInitParams& startupParams)
|
||||
{
|
||||
assetPlatform = AzFramework::OSPlatformToDefaultAssetPlatform(AZ_TRAIT_OS_PLATFORM_CODENAME);
|
||||
AZ_Warning(AZ_TRACE_SYSTEM_WINDOW, false, R"(A valid asset platform is missing in "%s/assets" key in the SettingsRegistry.)""\n"
|
||||
R"(This typically done by setting he "assets" field in the bootstrap.cfg for within a .setreg file)""\n"
|
||||
R"(This typically done by setting the "assets" field within a .setreg file)""\n"
|
||||
R"(A fallback of %s will be used.)",
|
||||
AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey,
|
||||
assetPlatform.c_str());
|
||||
|
||||
@@ -30,7 +30,7 @@ namespace Platform
|
||||
|
||||
using FileHandleType = SystemFile::FileHandleType;
|
||||
|
||||
void Seek(FileHandleType handle, const SystemFile* systemFile, SizeType offset, SystemFile::SeekMode mode);
|
||||
void Seek(FileHandleType handle, const SystemFile* systemFile, SystemFile::SeekSizeType offset, SystemFile::SeekMode mode);
|
||||
SystemFile::SizeType Tell(FileHandleType handle, const SystemFile* systemFile);
|
||||
bool Eof(FileHandleType handle, const SystemFile* systemFile);
|
||||
AZ::u64 ModificationTime(FileHandleType handle, const SystemFile* systemFile);
|
||||
@@ -68,9 +68,8 @@ void SystemFile::CreatePath(const char* fileName)
|
||||
}
|
||||
|
||||
SystemFile::SystemFile()
|
||||
: m_handle{ AZ_TRAIT_SYSTEMFILE_INVALID_HANDLE }
|
||||
{
|
||||
m_fileName[0] = '\0';
|
||||
m_handle = AZ_TRAIT_SYSTEMFILE_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
SystemFile::~SystemFile()
|
||||
@@ -81,6 +80,25 @@ SystemFile::~SystemFile()
|
||||
}
|
||||
}
|
||||
|
||||
SystemFile::SystemFile(SystemFile&& other)
|
||||
: SystemFile{}
|
||||
{
|
||||
AZStd::swap(m_fileName, other.m_fileName);
|
||||
AZStd::swap(m_handle, other.m_handle);
|
||||
}
|
||||
|
||||
SystemFile& SystemFile::operator=(SystemFile&& other)
|
||||
{
|
||||
// Close the current file and take over the SystemFile handle and filename
|
||||
Close();
|
||||
m_fileName = AZStd::move(other.m_fileName);
|
||||
m_handle = AZStd::move(other.m_handle);
|
||||
other.m_fileName = {};
|
||||
other.m_handle = AZ_TRAIT_SYSTEMFILE_INVALID_HANDLE;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool SystemFile::Open(const char* fileName, int mode, int platformFlags)
|
||||
{
|
||||
AZ_PROFILE_INTERVAL_SCOPED(AZ::Debug::ProfileCategory::AzCore, this, "SystemFile::Open - %s", fileName);
|
||||
@@ -88,42 +106,42 @@ bool SystemFile::Open(const char* fileName, int mode, int platformFlags)
|
||||
|
||||
if (fileName) // If we reopen the file we are allowed to have NULL file name
|
||||
{
|
||||
if (strlen(fileName) > AZ_ARRAY_SIZE(m_fileName) - 1)
|
||||
if (strlen(fileName) > m_fileName.max_size())
|
||||
{
|
||||
EBUS_EVENT(FileIOEventBus, OnError, this, nullptr, 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
// store the filename
|
||||
azsnprintf(m_fileName, AZ_ARRAY_SIZE(m_fileName), "%s", fileName);
|
||||
m_fileName = fileName;
|
||||
}
|
||||
|
||||
if (FileIOBus::HasHandlers())
|
||||
{
|
||||
bool isOpen = false;
|
||||
bool isHandled = false;
|
||||
EBUS_EVENT_RESULT(isHandled, FileIOBus, OnOpen, *this, m_fileName, mode, platformFlags, isOpen);
|
||||
EBUS_EVENT_RESULT(isHandled, FileIOBus, OnOpen, *this, m_fileName.c_str(), mode, platformFlags, isOpen);
|
||||
if (isHandled)
|
||||
{
|
||||
return isOpen;
|
||||
}
|
||||
}
|
||||
|
||||
AZ_Assert(!IsOpen(), "This file (%s) is already open!", m_fileName);
|
||||
AZ_Assert(!IsOpen(), "This file (%s) is already open!", m_fileName.c_str());
|
||||
|
||||
return PlatformOpen(mode, platformFlags);
|
||||
}
|
||||
|
||||
bool SystemFile::ReOpen(int mode, int platformFlags)
|
||||
{
|
||||
AZ_Assert(strlen(m_fileName) > 0, "Missing filename. You must call open first!");
|
||||
AZ_Assert(!m_fileName.empty(), "Missing filename. You must call open first!");
|
||||
return Open(0, mode, platformFlags);
|
||||
}
|
||||
|
||||
void SystemFile::Close()
|
||||
{
|
||||
AZ_PROFILE_INTERVAL_SCOPED(AZ::Debug::ProfileCategory::AzCore, this, "SystemFile::Close - %s", m_fileName);
|
||||
AZ_PROFILE_SCOPE_STALL_DYNAMIC(AZ::Debug::ProfileCategory::AzCore, "SystemFile::Close - %s", m_fileName);
|
||||
AZ_PROFILE_INTERVAL_SCOPED(AZ::Debug::ProfileCategory::AzCore, this, "SystemFile::Close - %s", m_fileName.c_str());
|
||||
AZ_PROFILE_SCOPE_STALL_DYNAMIC(AZ::Debug::ProfileCategory::AzCore, "SystemFile::Close - %s", m_fileName.c_str());
|
||||
|
||||
if (FileIOBus::HasHandlers())
|
||||
{
|
||||
@@ -138,9 +156,9 @@ void SystemFile::Close()
|
||||
PlatformClose();
|
||||
}
|
||||
|
||||
void SystemFile::Seek(SizeType offset, SeekMode mode)
|
||||
void SystemFile::Seek(SeekSizeType offset, SeekMode mode)
|
||||
{
|
||||
AZ_PROFILE_SCOPE_STALL_DYNAMIC(AZ::Debug::ProfileCategory::AzCore, "SystemFile::Seek - %s:%i", m_fileName, offset);
|
||||
AZ_PROFILE_SCOPE_STALL_DYNAMIC(AZ::Debug::ProfileCategory::AzCore, "SystemFile::Seek - %s:%i", m_fileName.c_str(), offset);
|
||||
|
||||
if (FileIOBus::HasHandlers())
|
||||
{
|
||||
@@ -167,15 +185,15 @@ bool SystemFile::Eof()
|
||||
|
||||
AZ::u64 SystemFile::ModificationTime()
|
||||
{
|
||||
AZ_PROFILE_SCOPE_STALL_DYNAMIC(AZ::Debug::ProfileCategory::AzCore, "SystemFile::ModTime - %s", m_fileName);
|
||||
AZ_PROFILE_SCOPE_STALL_DYNAMIC(AZ::Debug::ProfileCategory::AzCore, "SystemFile::ModTime - %s", m_fileName.c_str());
|
||||
|
||||
return Platform::ModificationTime(m_handle, this);
|
||||
}
|
||||
|
||||
SystemFile::SizeType SystemFile::Read(SizeType byteSize, void* buffer)
|
||||
{
|
||||
AZ_PROFILE_INTERVAL_SCOPED(AZ::Debug::ProfileCategory::AzCore, this, "SystemFile::Read - %s:%i", m_fileName, byteSize);
|
||||
AZ_PROFILE_SCOPE_STALL_DYNAMIC(AZ::Debug::ProfileCategory::AzCore, "SystemFile::Read - %s:%i", m_fileName, byteSize);
|
||||
AZ_PROFILE_INTERVAL_SCOPED(AZ::Debug::ProfileCategory::AzCore, this, "SystemFile::Read - %s:%i", m_fileName.c_str(), byteSize);
|
||||
AZ_PROFILE_SCOPE_STALL_DYNAMIC(AZ::Debug::ProfileCategory::AzCore, "SystemFile::Read - %s:%i", m_fileName.c_str(), byteSize);
|
||||
|
||||
if (FileIOBus::HasHandlers())
|
||||
{
|
||||
@@ -193,8 +211,8 @@ SystemFile::SizeType SystemFile::Read(SizeType byteSize, void* buffer)
|
||||
|
||||
SystemFile::SizeType SystemFile::Write(const void* buffer, SizeType byteSize)
|
||||
{
|
||||
AZ_PROFILE_INTERVAL_SCOPED(AZ::Debug::ProfileCategory::AzCore, this, "SystemFile::Write - %s:%i", m_fileName, byteSize);
|
||||
AZ_PROFILE_SCOPE_STALL_DYNAMIC(AZ::Debug::ProfileCategory::AzCore, "SystemFile::Write - %s:%i", m_fileName, byteSize);
|
||||
AZ_PROFILE_INTERVAL_SCOPED(AZ::Debug::ProfileCategory::AzCore, this, "SystemFile::Write - %s:%i", m_fileName.c_str(), byteSize);
|
||||
AZ_PROFILE_SCOPE_STALL_DYNAMIC(AZ::Debug::ProfileCategory::AzCore, "SystemFile::Write - %s:%i", m_fileName.c_str(), byteSize);
|
||||
|
||||
if (FileIOBus::HasHandlers())
|
||||
{
|
||||
@@ -212,14 +230,14 @@ SystemFile::SizeType SystemFile::Write(const void* buffer, SizeType byteSize)
|
||||
|
||||
void SystemFile::Flush()
|
||||
{
|
||||
AZ_PROFILE_SCOPE_STALL_DYNAMIC(AZ::Debug::ProfileCategory::AzCore, "SystemFile::Flush - %s", m_fileName);
|
||||
AZ_PROFILE_SCOPE_STALL_DYNAMIC(AZ::Debug::ProfileCategory::AzCore, "SystemFile::Flush - %s", m_fileName.c_str());
|
||||
|
||||
Platform::Flush(m_handle, this);
|
||||
}
|
||||
|
||||
SystemFile::SizeType SystemFile::Length() const
|
||||
{
|
||||
AZ_PROFILE_SCOPE_STALL_DYNAMIC(AZ::Debug::ProfileCategory::AzCore, "SystemFile::Length - %s", m_fileName);
|
||||
AZ_PROFILE_SCOPE_STALL_DYNAMIC(AZ::Debug::ProfileCategory::AzCore, "SystemFile::Length - %s", m_fileName.c_str());
|
||||
|
||||
return Platform::Length(m_handle, this);
|
||||
}
|
||||
@@ -379,9 +397,9 @@ namespace
|
||||
HasPosixEnumOption(PermissionModeFlags::Write);
|
||||
|
||||
#undef HasPosixEnumOption
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
FileDescriptorRedirector::FileDescriptorRedirector(int sourceFileDescriptor)
|
||||
: m_sourceFileDescriptor(sourceFileDescriptor)
|
||||
{
|
||||
|
||||
@@ -12,10 +12,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/base.h>
|
||||
#include <AzCore/std/function/function_fwd.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
|
||||
#include <AzCore/IO/Path/Path_fwd.h>
|
||||
#include <AzCore/IO/SystemFile_Platform.h>
|
||||
#include <AzCore/std/function/function_fwd.h>
|
||||
#include <AzCore/std/string/fixed_string.h>
|
||||
|
||||
// Establish a consistent size that works across platforms. It's actually larger than this
|
||||
// on platforms we support, but this is a good least common denominator
|
||||
@@ -51,11 +52,15 @@ namespace AZ
|
||||
};
|
||||
|
||||
using SizeType = AZ::IO::Internal::SizeType;
|
||||
using SeekSizeType = AZ::IO::Internal::SeekSizeType;
|
||||
using FileHandleType = AZ::IO::Internal::FileHandleType;
|
||||
|
||||
SystemFile();
|
||||
~SystemFile();
|
||||
|
||||
SystemFile(SystemFile&&);
|
||||
SystemFile& operator=(SystemFile&&);
|
||||
|
||||
/**
|
||||
* Opens a file.
|
||||
* \param fileName full file name including path
|
||||
@@ -69,7 +74,7 @@ namespace AZ
|
||||
/// Closes a file, if file already close it has no effect.
|
||||
void Close();
|
||||
/// Seek in current file.
|
||||
void Seek(SizeType offset, SeekMode mode);
|
||||
void Seek(SeekSizeType offset, SeekMode mode);
|
||||
/// Get the cursor position in the current file.
|
||||
SizeType Tell();
|
||||
/// Is the cursor at the end of the file?
|
||||
@@ -87,7 +92,7 @@ namespace AZ
|
||||
/// Return disc offset if possible, otherwise 0
|
||||
SizeType DiskOffset() const;
|
||||
/// Return file name or NULL if file is not open.
|
||||
AZ_FORCE_INLINE const char* Name() const { return m_fileName; }
|
||||
AZ_FORCE_INLINE const char* Name() const { return m_fileName.c_str(); }
|
||||
bool IsOpen() const;
|
||||
|
||||
/// Return native handle to the file.
|
||||
@@ -124,12 +129,12 @@ namespace AZ
|
||||
|
||||
private:
|
||||
static void CreatePath(const char * fileName);
|
||||
|
||||
|
||||
bool PlatformOpen(int mode, int platformFlags);
|
||||
void PlatformClose();
|
||||
|
||||
FileHandleType m_handle;
|
||||
char m_fileName[AZ_MAX_PATH_LEN];
|
||||
|
||||
FileHandleType m_handle;
|
||||
AZ::IO::FixedMaxPathString m_fileName;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -127,13 +127,14 @@ namespace AZ
|
||||
*/
|
||||
class EditContext
|
||||
{
|
||||
public:
|
||||
/// @cond EXCLUDE_DOCS
|
||||
class ClassBuilder;
|
||||
class EnumBuilder;
|
||||
using ClassInfo = ClassBuilder; ///< @deprecated Use EditContext::ClassBuilder
|
||||
using EnumInfo = EnumBuilder; ///< @deprecated Use EditContext::EnumBuilder
|
||||
/// @endcond
|
||||
public:
|
||||
|
||||
AZ_CLASS_ALLOCATOR(EditContext, SystemAllocator, 0);
|
||||
|
||||
/**
|
||||
@@ -186,6 +187,7 @@ namespace AZ
|
||||
* look at the unit tests and example to see use cases.
|
||||
*
|
||||
*/
|
||||
public:
|
||||
class ClassBuilder
|
||||
{
|
||||
friend EditContext;
|
||||
@@ -399,6 +401,7 @@ namespace AZ
|
||||
EnumBuilder* Value(const char* name, E value);
|
||||
};
|
||||
|
||||
private:
|
||||
typedef AZStd::list<Edit::ClassData> ClassDataListType;
|
||||
typedef AZStd::unordered_map<AZ::Uuid, Edit::ElementData> EnumDataMapType;
|
||||
|
||||
|
||||
@@ -101,6 +101,9 @@ namespace AZ
|
||||
class SerializeContext
|
||||
: public ReflectContext
|
||||
{
|
||||
static const unsigned int VersionClassDeprecated = (unsigned int)-1;
|
||||
|
||||
public:
|
||||
/// @cond EXCLUDE_DOCS
|
||||
friend class EditContext;
|
||||
class ClassBuilder;
|
||||
@@ -108,9 +111,6 @@ namespace AZ
|
||||
/// @endcond
|
||||
class EnumBuilder;
|
||||
|
||||
static const unsigned int VersionClassDeprecated = (unsigned int)-1;
|
||||
|
||||
public:
|
||||
class ClassData;
|
||||
struct EnumerateInstanceCallContext;
|
||||
struct ClassElement;
|
||||
@@ -1131,6 +1131,7 @@ namespace AZ
|
||||
* ->Version(3,&MyVersionConverter)
|
||||
* ->Field("data",&MyStruct::m_data);
|
||||
*/
|
||||
public:
|
||||
class ClassBuilder
|
||||
{
|
||||
friend class SerializeContext;
|
||||
@@ -1330,7 +1331,8 @@ namespace AZ
|
||||
AZStd::vector<AttributeSharedPair, AZStdFunctorAllocator>* m_currentAttributes = nullptr;
|
||||
};
|
||||
|
||||
EditContext* m_editContext; ///< Pointer to optional edit context.
|
||||
private:
|
||||
EditContext* m_editContext; ///< Pointer to optional edit context.
|
||||
UuidToClassMap m_uuidMap; ///< Map for all class in this serialize context
|
||||
AZStd::unordered_multimap<AZ::Crc32, AZ::Uuid> m_classNameToUuid; /// Map all class names to their uuid
|
||||
AZStd::unordered_multimap<Uuid, GenericClassInfo*> m_uuidGenericMap; ///< Uuid to ClassData map of reflected classes with GenericTypeInfo
|
||||
|
||||
@@ -641,6 +641,8 @@ namespace AZ::SettingsRegistryMergeUtils
|
||||
}
|
||||
else
|
||||
{
|
||||
// Set the default ProjectUserPath to the <engine-root>/user directory
|
||||
registry.Set(FilePathKey_ProjectUserPath, (engineRoot / "user").LexicallyNormal().Native());
|
||||
AZ_TracePrintf("SettingsRegistryMergeUtils",
|
||||
R"(Project path isn't set in the Settings Registry at "%.*s". Project-related filepaths will not be set)" "\n",
|
||||
aznumeric_cast<int>(projectPathKey.size()), projectPathKey.data());
|
||||
|
||||
@@ -971,6 +971,10 @@ namespace AZ
|
||||
*/
|
||||
void RestoreCachedInstances();
|
||||
|
||||
/// Returns data flags for use when instantiating an instance of this slice.
|
||||
/// These data flags include those harvested from the entire slice ancestry.
|
||||
const DataFlagsPerEntity& GetDataFlagsForInstances() const;
|
||||
|
||||
protected:
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@@ -1004,9 +1008,6 @@ namespace AZ
|
||||
DataFlagsPerEntity* GetCorrectBundleOfDataFlags(EntityId entityId);
|
||||
const DataFlagsPerEntity* GetCorrectBundleOfDataFlags(EntityId entityId) const;
|
||||
|
||||
/// Returns data flags for use when instantiating an instance of this slice.
|
||||
/// These data flags include those harvested from the entire slice ancestry.
|
||||
const DataFlagsPerEntity& GetDataFlagsForInstances() const;
|
||||
void BuildDataFlagsForInstances();
|
||||
|
||||
/**
|
||||
|
||||
@@ -101,7 +101,7 @@ bool SystemFile::PlatformOpen(int mode, int platformFlags)
|
||||
createPath = (mode & SF_OPEN_CREATE_PATH) == SF_OPEN_CREATE_PATH;
|
||||
}
|
||||
|
||||
bool isApkFile = AZ::Android::Utils::IsApkPath(m_fileName);
|
||||
bool isApkFile = AZ::Android::Utils::IsApkPath(m_fileName.c_str());
|
||||
|
||||
if (createPath)
|
||||
{
|
||||
@@ -111,19 +111,19 @@ bool SystemFile::PlatformOpen(int mode, int platformFlags)
|
||||
return false;
|
||||
}
|
||||
|
||||
CreatePath(m_fileName);
|
||||
CreatePath(m_fileName.c_str());
|
||||
}
|
||||
|
||||
int errorCode = 0;
|
||||
if (isApkFile)
|
||||
{
|
||||
AZ::u64 size = 0;
|
||||
m_handle = AZ::Android::APKFileHandler::Open(m_fileName, openMode, size);
|
||||
m_handle = AZ::Android::APKFileHandler::Open(m_fileName.c_str(), openMode, size);
|
||||
errorCode = EACCES; // general error when a file can't be opened from inside the APK
|
||||
}
|
||||
else
|
||||
{
|
||||
m_handle = fopen(m_fileName, openMode);
|
||||
m_handle = fopen(m_fileName.c_str(), openMode);
|
||||
errorCode = errno;
|
||||
}
|
||||
|
||||
@@ -233,7 +233,7 @@ namespace Platform
|
||||
}
|
||||
}
|
||||
|
||||
void Seek(FileHandleType handle, const SystemFile* systemFile, SizeType offset, SystemFile::SeekMode mode)
|
||||
void Seek(FileHandleType handle, const SystemFile* systemFile, SystemFile::SeekSizeType offset, SystemFile::SeekMode mode)
|
||||
{
|
||||
if (handle != PlatformSpecificInvalidHandle)
|
||||
{
|
||||
|
||||
@@ -15,6 +15,9 @@
|
||||
#include <cstdio>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <AzCore/std/typetraits/underlying_type.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
@@ -23,6 +26,7 @@ namespace AZ
|
||||
namespace Internal
|
||||
{
|
||||
using SizeType = AZ::u64;
|
||||
using SeekSizeType = AZ::s64;
|
||||
using FileHandleType = FILE*;
|
||||
}
|
||||
|
||||
@@ -37,7 +41,7 @@ namespace AZ
|
||||
#else
|
||||
Temporary = 0, // (Not applicable for this platform) Applies only when used with CREAT. Creates a file as temporary; the file is deleted when the last file descriptor is closed. PermissionMode equired when CREAT is specified.
|
||||
#endif
|
||||
Exclusive = O_EXCL, // Applies only when used with CREAT. Returns an error value if a file specified by filename exists.
|
||||
Exclusive = O_EXCL, // Applies only when used with CREAT. Returns an error value if a file specified by filename exists.
|
||||
Truncate = O_TRUNC, // Opens a file and truncates it to zero length; the file must have write permission. Cannot be specified with RDONLY.
|
||||
// Note: The TRUNC flag destroys the contents of the specified file.
|
||||
|
||||
|
||||
@@ -22,9 +22,10 @@ namespace AZ
|
||||
namespace Internal
|
||||
{
|
||||
using SizeType = AZ::u64;
|
||||
using SeekSizeType = AZ::s64;
|
||||
using FileHandleType = int;
|
||||
}
|
||||
|
||||
|
||||
namespace PosixInternal
|
||||
{
|
||||
enum class OpenFlags : int
|
||||
@@ -36,7 +37,7 @@ namespace AZ
|
||||
#else
|
||||
Temporary = 0, // (Not applicable for this platform) Applies only when used with CREAT. Creates a file as temporary; the file is deleted when the last file descriptor is closed. PermissionMode equired when CREAT is specified.
|
||||
#endif
|
||||
Exclusive = O_EXCL, // Applies only when used with CREAT. Returns an error value if a file specified by filename exists.
|
||||
Exclusive = O_EXCL, // Applies only when used with CREAT. Returns an error value if a file specified by filename exists.
|
||||
Truncate = O_TRUNC, // Opens a file and truncates it to zero length; the file must have write permission. Cannot be specified with RDONLY.
|
||||
// Note: The TRUNC flag destroys the contents of the specified file.
|
||||
|
||||
|
||||
@@ -13,6 +13,9 @@
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <AzCore/std/typetraits/underlying_type.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
@@ -21,6 +24,7 @@ namespace AZ
|
||||
namespace Internal
|
||||
{
|
||||
using SizeType = AZ::u64;
|
||||
using SeekSizeType = AZ::s64;
|
||||
using FileHandleType = int;
|
||||
}
|
||||
|
||||
@@ -35,7 +39,7 @@ namespace AZ
|
||||
#else
|
||||
Temporary = 0, // (Not applicable for this platform) Applies only when used with CREAT. Creates a file as temporary; the file is deleted when the last file descriptor is closed. PermissionMode equired when CREAT is specified.
|
||||
#endif
|
||||
Exclusive = O_EXCL, // Applies only when used with CREAT. Returns an error value if a file specified by filename exists.
|
||||
Exclusive = O_EXCL, // Applies only when used with CREAT. Returns an error value if a file specified by filename exists.
|
||||
Truncate = O_TRUNC, // Opens a file and truncates it to zero length; the file must have write permission. Cannot be specified with RDONLY.
|
||||
// Note: The TRUNC flag destroys the contents of the specified file.
|
||||
|
||||
|
||||
+16
-7
@@ -13,9 +13,10 @@
|
||||
#include <AzCore/Module/DynamicModuleHandle.h>
|
||||
#include <AzCore/IO/Path/Path.h>
|
||||
#include <AzCore/IO/SystemFile.h>
|
||||
|
||||
#include <AzCore/Memory/OSAllocator.h>
|
||||
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <libgen.h>
|
||||
|
||||
@@ -61,10 +62,11 @@ namespace AZ
|
||||
// If it doesn't attempt to append the path to the executable path
|
||||
if (!AZ::IO::SystemFile::Exists(fullFilePath.c_str()))
|
||||
{
|
||||
auto candidatePath = Platform::GetModulePath() / fullFilePath;
|
||||
AZ::IO::FixedMaxPath candidatePath = Platform::GetModulePath() / fullFilePath;
|
||||
if (AZ::IO::SystemFile::Exists(candidatePath.c_str()))
|
||||
{
|
||||
fullFilePath = candidatePath;
|
||||
m_fileName.assign(candidatePath.Native().c_str(), candidatePath.Native().size());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,19 +76,26 @@ namespace AZ
|
||||
{
|
||||
if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
|
||||
{
|
||||
if(AZ::IO::FixedMaxPath projectModulePath;
|
||||
if (AZ::IO::FixedMaxPath projectModulePath;
|
||||
settingsRegistry->Get(projectModulePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectConfigurationBinPath))
|
||||
{
|
||||
projectModulePath /= fullFilePath;
|
||||
if (AZ::IO::SystemFile::Exists(projectModulePath.c_str()))
|
||||
{
|
||||
fullFilePath = projectModulePath;
|
||||
m_fileName.assign(projectModulePath.c_str(), projectModulePath.Native().size());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_fileName = AZStd::string_view{fullFilePath.Native()};
|
||||
else
|
||||
{
|
||||
// The module does exist (in 'cwd'), but still needs to be an absolute path for the module to be loaded.
|
||||
AZStd::optional<AZ::IO::FixedMaxPathString> absPathOptional = AZ::Utils::ConvertToAbsolutePath(m_fileName);
|
||||
if (absPathOptional.has_value())
|
||||
{
|
||||
m_fileName.assign(absPathOptional->c_str(), absPathOptional->size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
~DynamicModuleHandleUnixLike() override
|
||||
|
||||
+3
-3
@@ -86,9 +86,9 @@ bool SystemFile::PlatformOpen(int mode, int platformFlags)
|
||||
|
||||
if (createPath)
|
||||
{
|
||||
CreatePath(m_fileName);
|
||||
CreatePath(m_fileName.c_str());
|
||||
}
|
||||
m_handle = open(m_fileName, desiredAccess, permissions);
|
||||
m_handle = open(m_fileName.c_str(), desiredAccess, permissions);
|
||||
|
||||
if (m_handle == PlatformSpecificInvalidHandle)
|
||||
{
|
||||
@@ -119,7 +119,7 @@ namespace Platform
|
||||
{
|
||||
using FileHandleType = AZ::IO::SystemFile::FileHandleType;
|
||||
|
||||
void Seek(FileHandleType handle, const SystemFile* systemFile, SizeType offset, SystemFile::SeekMode mode)
|
||||
void Seek(FileHandleType handle, const SystemFile* systemFile, SystemFile::SeekSizeType offset, SystemFile::SeekMode mode)
|
||||
{
|
||||
if (handle != PlatformSpecificInvalidHandle)
|
||||
{
|
||||
|
||||
@@ -209,19 +209,19 @@ bool SystemFile::PlatformOpen(int mode, int platformFlags)
|
||||
|
||||
if (createPath)
|
||||
{
|
||||
CreatePath(m_fileName);
|
||||
CreatePath(m_fileName.c_str());
|
||||
}
|
||||
|
||||
# ifdef _UNICODE
|
||||
wchar_t fileNameW[AZ_MAX_PATH_LEN];
|
||||
size_t numCharsConverted;
|
||||
m_handle = INVALID_HANDLE_VALUE;
|
||||
if (mbstowcs_s(&numCharsConverted, fileNameW, m_fileName, AZ_ARRAY_SIZE(fileNameW) - 1) == 0)
|
||||
if (mbstowcs_s(&numCharsConverted, fileNameW, m_fileName.c_str(), AZ_ARRAY_SIZE(fileNameW) - 1) == 0)
|
||||
{
|
||||
m_handle = CreateFileW(fileNameW, dwDesiredAccess, dwShareMode, 0, dwCreationDisposition, dwFlagsAndAttributes, 0);
|
||||
}
|
||||
# else //!_UNICODE
|
||||
m_handle = CreateFile(m_fileName, dwDesiredAccess, dwShareMode, 0, dwCreationDisposition, dwFlagsAndAttributes, 0);
|
||||
m_handle = CreateFile(m_fileName.c_str(), dwDesiredAccess, dwShareMode, 0, dwCreationDisposition, dwFlagsAndAttributes, 0);
|
||||
# endif // !_UNICODE
|
||||
|
||||
if (m_handle == INVALID_HANDLE_VALUE)
|
||||
@@ -261,7 +261,7 @@ namespace Platform
|
||||
{
|
||||
using FileHandleType = AZ::IO::SystemFile::FileHandleType;
|
||||
|
||||
void Seek(FileHandleType handle, const SystemFile* systemFile, SizeType offset, SystemFile::SeekMode mode)
|
||||
void Seek(FileHandleType handle, const SystemFile* systemFile, SystemFile::SeekSizeType offset, SystemFile::SeekMode mode)
|
||||
{
|
||||
if (handle != PlatformSpecificInvalidHandle)
|
||||
{
|
||||
|
||||
@@ -13,6 +13,9 @@
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <corecrt_io.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <AzCore/std/typetraits/underlying_type.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
@@ -21,6 +24,7 @@ namespace AZ
|
||||
namespace Internal
|
||||
{
|
||||
using SizeType = AZ::u64;
|
||||
using SeekSizeType = AZ::s64;
|
||||
using FileHandleType = void*;
|
||||
}
|
||||
|
||||
@@ -31,7 +35,7 @@ namespace AZ
|
||||
Append = _O_APPEND, // Moves the file pointer to the end of the file before every write operation.
|
||||
Create = _O_CREAT, // Creates a file and opens it for writing. Has no effect if the file specified by filename exists. PermissionMode is required.
|
||||
Temporary = _O_TEMPORARY, // Applies only when used with CREAT. Creates a file as temporary; the file is deleted when the last file descriptor is closed. PermissionMode equired when CREAT is specified.
|
||||
Exclusive = _O_EXCL, // Applies only when used with CREAT. Returns an error value if a file specified by filename exists.
|
||||
Exclusive = _O_EXCL, // Applies only when used with CREAT. Returns an error value if a file specified by filename exists.
|
||||
Truncate = _O_TRUNC, // Opens a file and truncates it to zero length; the file must have write permission. Cannot be specified with RDONLY.
|
||||
// Note: The TRUNC flag destroys the contents of the specified file.
|
||||
|
||||
|
||||
+13
-3
@@ -24,9 +24,9 @@ namespace AZ
|
||||
: public DynamicModuleHandle
|
||||
{
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(DynamicModuleHandleWindows, OSAllocator, 0)
|
||||
AZ_CLASS_ALLOCATOR(DynamicModuleHandleWindows, OSAllocator, 0);
|
||||
|
||||
DynamicModuleHandleWindows(const char* fullFileName)
|
||||
DynamicModuleHandleWindows(const char* fullFileName)
|
||||
: DynamicModuleHandle(fullFileName)
|
||||
, m_handle(nullptr)
|
||||
{
|
||||
@@ -52,6 +52,7 @@ namespace AZ
|
||||
if (AZ::IO::SystemFile::Exists(candidatePath.c_str()))
|
||||
{
|
||||
m_fileName.assign(candidatePath.Native().c_str(), candidatePath.Native().size());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -65,7 +66,7 @@ namespace AZ
|
||||
// Therefore an existence check is needed
|
||||
if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
|
||||
{
|
||||
if(AZ::IO::FixedMaxPath projectModulePath;
|
||||
if (AZ::IO::FixedMaxPath projectModulePath;
|
||||
settingsRegistry->Get(projectModulePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectConfigurationBinPath))
|
||||
{
|
||||
projectModulePath /= AZStd::string_view(m_fileName);
|
||||
@@ -76,6 +77,15 @@ namespace AZ
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// The module does exist (in 'cwd'), but still needs to be an absolute path for the module to be loaded.
|
||||
AZStd::optional<AZ::IO::FixedMaxPathString> absPathOptional = AZ::Utils::ConvertToAbsolutePath(m_fileName);
|
||||
if (absPathOptional.has_value())
|
||||
{
|
||||
m_fileName.assign(absPathOptional->c_str(), absPathOptional->size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
~DynamicModuleHandleWindows() override
|
||||
|
||||
@@ -1914,7 +1914,7 @@ namespace UnitTest
|
||||
TEST_F(String, StringView_CompareIsConstexpr)
|
||||
{
|
||||
using TypeParam = char;
|
||||
auto MakeCompileTimeString1 = []() constexpr -> const TypeParam*
|
||||
auto ThisTestMakeCompileTimeString1 = []() constexpr -> const TypeParam*
|
||||
{
|
||||
return "HelloWorld";
|
||||
};
|
||||
@@ -1922,7 +1922,7 @@ namespace UnitTest
|
||||
{
|
||||
return "HelloPearl";
|
||||
};
|
||||
constexpr const TypeParam* compileTimeString1 = MakeCompileTimeString1();
|
||||
constexpr const TypeParam* compileTimeString1 = ThisTestMakeCompileTimeString1();
|
||||
constexpr const TypeParam* compileTimeString2 = MakeCompileTimeString2();
|
||||
constexpr basic_string_view<TypeParam> lhsView(compileTimeString1);
|
||||
constexpr basic_string_view<TypeParam> rhsView(compileTimeString2);
|
||||
@@ -1937,11 +1937,11 @@ namespace UnitTest
|
||||
TEST_F(String, StringView_CompareOperatorsAreConstexpr)
|
||||
{
|
||||
using TypeParam = char;
|
||||
auto MakeCompileTimeString1 = []() constexpr -> const TypeParam*
|
||||
auto TestMakeCompileTimeString1 = []() constexpr -> const TypeParam*
|
||||
{
|
||||
return "HelloWorld";
|
||||
};
|
||||
constexpr const TypeParam* compileTimeString1 = MakeCompileTimeString1();
|
||||
constexpr const TypeParam* compileTimeString1 = TestMakeCompileTimeString1();
|
||||
constexpr basic_string_view<TypeParam> compareView(compileTimeString1);
|
||||
static_assert(compareView == "HelloWorld", "string_view operator== comparison has failed");
|
||||
static_assert(compareView != "MadWorld", "string_view operator!= comparison has failed");
|
||||
@@ -1955,7 +1955,7 @@ namespace UnitTest
|
||||
{
|
||||
auto swap_test_func = []() constexpr -> basic_string_view<TypeParam>
|
||||
{
|
||||
constexpr auto MakeCompileTimeString1 = []() constexpr -> const TypeParam*
|
||||
constexpr auto ThisTestMakeCompileTimeString1 = []() constexpr -> const TypeParam*
|
||||
{
|
||||
if constexpr (AZStd::is_same_v<TypeParam, char>)
|
||||
{
|
||||
@@ -1977,7 +1977,7 @@ namespace UnitTest
|
||||
return L"InuWorld";
|
||||
}
|
||||
};
|
||||
constexpr const TypeParam* compileTimeString1 = MakeCompileTimeString1();
|
||||
constexpr const TypeParam* compileTimeString1 = ThisTestMakeCompileTimeString1();
|
||||
constexpr const TypeParam* compileTimeString2 = MakeCompileTimeString2();
|
||||
basic_string_view<TypeParam> lhsView(compileTimeString1);
|
||||
basic_string_view<TypeParam> rhsView(compileTimeString2);
|
||||
@@ -2001,7 +2001,7 @@ namespace UnitTest
|
||||
|
||||
TYPED_TEST(BasicStringViewConstexprFixture, HashString_FunctionIsConstexpr)
|
||||
{
|
||||
auto MakeCompileTimeString1 = []() constexpr -> const TypeParam*
|
||||
auto ThisTestMakeCompileTimeString1 = []() constexpr -> const TypeParam*
|
||||
{
|
||||
if constexpr (AZStd::is_same_v<TypeParam, char>)
|
||||
{
|
||||
@@ -2012,7 +2012,7 @@ namespace UnitTest
|
||||
return L"HelloWorld";
|
||||
}
|
||||
};
|
||||
constexpr const TypeParam* compileTimeString1 = MakeCompileTimeString1();
|
||||
constexpr const TypeParam* compileTimeString1 = ThisTestMakeCompileTimeString1();
|
||||
constexpr basic_string_view<TypeParam> hashView(compileTimeString1);
|
||||
constexpr size_t compileHash = AZStd::hash<basic_string_view<TypeParam>>{}(hashView);
|
||||
static_assert(compileHash != 0, "Hash of \"HelloWorld\" should not be 0");
|
||||
|
||||
@@ -59,7 +59,7 @@ namespace UnitTest
|
||||
|
||||
TEST(MATH_Matrix4x4, TestCreateFrom)
|
||||
{
|
||||
float testFloats[] =
|
||||
float thisTestFloats[] =
|
||||
{
|
||||
1.0f, 2.0f, 3.0f, 4.0f,
|
||||
5.0f, 6.0f, 7.0f, 8.0f,
|
||||
@@ -67,20 +67,20 @@ namespace UnitTest
|
||||
13.0f, 14.0f, 15.0f, 16.0f
|
||||
};
|
||||
float testFloatMtx[16];
|
||||
Matrix4x4 m1 = Matrix4x4::CreateFromRowMajorFloat16(testFloats);
|
||||
Matrix4x4 m1 = Matrix4x4::CreateFromRowMajorFloat16(thisTestFloats);
|
||||
AZ_TEST_ASSERT(m1.GetRow(0) == Vector4(1.0f, 2.0f, 3.0f, 4.0f));
|
||||
AZ_TEST_ASSERT(m1.GetRow(1) == Vector4(5.0f, 6.0f, 7.0f, 8.0f));
|
||||
AZ_TEST_ASSERT(m1.GetRow(2) == Vector4(9.0f, 10.0f, 11.0f, 12.0f));
|
||||
AZ_TEST_ASSERT(m1.GetRow(3) == Vector4(13.0f, 14.0f, 15.0f, 16.0f));
|
||||
m1.StoreToRowMajorFloat16(testFloatMtx);
|
||||
AZ_TEST_ASSERT(memcmp(testFloatMtx, testFloats, sizeof(testFloatMtx)) == 0);
|
||||
m1 = Matrix4x4::CreateFromColumnMajorFloat16(testFloats);
|
||||
AZ_TEST_ASSERT(memcmp(testFloatMtx, thisTestFloats, sizeof(testFloatMtx)) == 0);
|
||||
m1 = Matrix4x4::CreateFromColumnMajorFloat16(thisTestFloats);
|
||||
AZ_TEST_ASSERT(m1.GetRow(0) == Vector4(1.0f, 5.0f, 9.0f, 13.0f));
|
||||
AZ_TEST_ASSERT(m1.GetRow(1) == Vector4(2.0f, 6.0f, 10.0f, 14.0f));
|
||||
AZ_TEST_ASSERT(m1.GetRow(2) == Vector4(3.0f, 7.0f, 11.0f, 15.0f));
|
||||
AZ_TEST_ASSERT(m1.GetRow(3) == Vector4(4.0f, 8.0f, 12.0f, 16.0f));
|
||||
m1.StoreToColumnMajorFloat16(testFloatMtx);
|
||||
AZ_TEST_ASSERT(memcmp(testFloatMtx, testFloats, sizeof(testFloatMtx)) == 0);
|
||||
AZ_TEST_ASSERT(memcmp(testFloatMtx, thisTestFloats, sizeof(testFloatMtx)) == 0);
|
||||
}
|
||||
|
||||
TEST(MATH_Matrix4x4, TestCreateFromMatrix3x4)
|
||||
|
||||
@@ -119,10 +119,10 @@ namespace UnitTest
|
||||
|
||||
TEST(MATH_Obb, Contains)
|
||||
{
|
||||
const Vector3 position(1.0f, 2.0f, 3.0f);
|
||||
const Quaternion rotation = Quaternion::CreateRotationZ(DegToRad(30.0f));
|
||||
const Vector3 halfLengths(2.0f, 1.0f, 2.5f);
|
||||
const Obb obb = Obb::CreateFromPositionRotationAndHalfLengths(position, rotation, halfLengths);
|
||||
const Vector3 testPosition(1.0f, 2.0f, 3.0f);
|
||||
const Quaternion testRotation = Quaternion::CreateRotationZ(DegToRad(30.0f));
|
||||
const Vector3 testHalfLengths(2.0f, 1.0f, 2.5f);
|
||||
const Obb obb = Obb::CreateFromPositionRotationAndHalfLengths(testPosition, testRotation, testHalfLengths);
|
||||
// test some pairs of points which should be just either side of the Obb boundary
|
||||
EXPECT_TRUE(obb.Contains(Vector3(1.35f, 3.35f, 3.5f)));
|
||||
EXPECT_FALSE(obb.Contains(Vector3(1.35f, 3.4f, 3.5f)));
|
||||
@@ -134,10 +134,10 @@ namespace UnitTest
|
||||
|
||||
TEST(MATH_Obb, GetDistance)
|
||||
{
|
||||
const Vector3 position(5.0f, 3.0f, 2.0f);
|
||||
const Quaternion rotation = Quaternion::CreateRotationX(DegToRad(60.0f));
|
||||
const Vector3 halfLengths(0.5f, 2.0f, 1.5f);
|
||||
const Obb obb = Obb::CreateFromPositionRotationAndHalfLengths(position, rotation, halfLengths);
|
||||
const Vector3 testPosition(5.0f, 3.0f, 2.0f);
|
||||
const Quaternion testRotation = Quaternion::CreateRotationX(DegToRad(60.0f));
|
||||
const Vector3 testHalfLengths(0.5f, 2.0f, 1.5f);
|
||||
const Obb obb = Obb::CreateFromPositionRotationAndHalfLengths(testPosition, testRotation, testHalfLengths);
|
||||
EXPECT_NEAR(obb.GetDistance(Vector3(5.3f, 3.2f, 1.8f)), 0.0f, 1e-3f);
|
||||
EXPECT_NEAR(obb.GetDistance(Vector3(5.1f, 1.1f, 3.7f)), 0.9955f, 1e-3f);
|
||||
EXPECT_NEAR(obb.GetDistance(Vector3(4.7f, 4.5f, 4.2f)), 0.6553f, 1e-3f);
|
||||
@@ -146,10 +146,10 @@ namespace UnitTest
|
||||
|
||||
TEST(MATH_Obb, GetDistanceSq)
|
||||
{
|
||||
const Vector3 position(1.0f, 4.0f, 3.0f);
|
||||
const Quaternion rotation = Quaternion::CreateRotationY(DegToRad(45.0f));
|
||||
const Vector3 halfLengths(1.5f, 3.0f, 1.0f);
|
||||
const Obb obb = Obb::CreateFromPositionRotationAndHalfLengths(position, rotation, halfLengths);
|
||||
const Vector3 testPosition(1.0f, 4.0f, 3.0f);
|
||||
const Quaternion testRotation = Quaternion::CreateRotationY(DegToRad(45.0f));
|
||||
const Vector3 testHalfLengths(1.5f, 3.0f, 1.0f);
|
||||
const Obb obb = Obb::CreateFromPositionRotationAndHalfLengths(testPosition, testRotation, testHalfLengths);
|
||||
EXPECT_NEAR(obb.GetDistanceSq(Vector3(1.1f, 4.3f, 2.7f)), 0.0f, 1e-3f);
|
||||
EXPECT_NEAR(obb.GetDistanceSq(Vector3(-0.7f, 3.5f, 2.0f)), 0.8266f, 1e-3f);
|
||||
EXPECT_NEAR(obb.GetDistanceSq(Vector3(2.4f, 0.5f, 1.5f)), 0.5532f, 1e-3f);
|
||||
|
||||
@@ -711,8 +711,8 @@ namespace AzFramework
|
||||
}
|
||||
}
|
||||
|
||||
AZ::IO::FixedMaxPath projectUserPath;
|
||||
if (m_settingsRegistry->Get(projectUserPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectUserPath))
|
||||
if (AZ::IO::FixedMaxPath projectUserPath;
|
||||
m_settingsRegistry->Get(projectUserPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectUserPath))
|
||||
{
|
||||
fileIoBase->SetAlias("@user@", projectUserPath.c_str());
|
||||
AZ::IO::FixedMaxPath projectLogPath = projectUserPath / "log";
|
||||
@@ -721,6 +721,15 @@ namespace AzFramework
|
||||
|
||||
CreateUserCache(projectUserPath, *fileIoBase);
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ::IO::FixedMaxPath fallbackLogPath = GetEngineRoot();
|
||||
fallbackLogPath /= "user";
|
||||
fileIoBase->SetAlias("@user@", fallbackLogPath.c_str());
|
||||
fallbackLogPath /= "log";
|
||||
fileIoBase->SetAlias("@log@", fallbackLogPath.c_str());
|
||||
fileIoBase->CreatePath(fallbackLogPath.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+14
-11
@@ -12,17 +12,22 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzToolsFramework/Manipulators/ManipulatorManager.h>
|
||||
#include <AzToolsFramework/Manipulators/ManipulatorBus.h>
|
||||
#include <AzToolsFramework/Manipulators/LinearManipulator.h>
|
||||
#include <AzToolsFramework/Manipulators/ManipulatorBus.h>
|
||||
#include <AzToolsFramework/Manipulators/ManipulatorManager.h>
|
||||
#include <AzToolsFramework/Manipulators/PlanarManipulator.h>
|
||||
|
||||
namespace AzManipulatorTestFramework
|
||||
{
|
||||
//! Create a linear manipulator with a unit sphere bounds.
|
||||
//! Create a linear manipulator with a unit sphere bound.
|
||||
AZStd::shared_ptr<AzToolsFramework::LinearManipulator> CreateLinearManipulator(
|
||||
const AzToolsFramework::ManipulatorManagerId manipulatorManagerId,
|
||||
const AZ::Vector3& position = AZ::Vector3::CreateZero(),
|
||||
const float radius = 1.0f);
|
||||
const AzToolsFramework::ManipulatorManagerId manipulatorManagerId, const AZ::Vector3& position = AZ::Vector3::CreateZero(),
|
||||
float radius = 1.0f);
|
||||
|
||||
//! Create a planar manipulator with a unit sphere bound.
|
||||
AZStd::shared_ptr<AzToolsFramework::PlanarManipulator> CreatePlanarManipulator(
|
||||
const AzToolsFramework::ManipulatorManagerId manipulatorManagerId, const AZ::Vector3& position = AZ::Vector3::CreateZero(),
|
||||
float radius = 1.0f);
|
||||
|
||||
//! Create a mouse pick from the specified ray and screen point.
|
||||
AzToolsFramework::ViewportInteraction::MousePick CreateMousePick(
|
||||
@@ -34,14 +39,12 @@ namespace AzManipulatorTestFramework
|
||||
|
||||
//! Create a mouse interaction from the specified pick, buttons, interaction id and keyboard modifiers.
|
||||
AzToolsFramework::ViewportInteraction::MouseInteraction CreateMouseInteraction(
|
||||
const AzToolsFramework::ViewportInteraction::MousePick& mousePick,
|
||||
AzToolsFramework::ViewportInteraction::MouseButtons buttons,
|
||||
const AzToolsFramework::ViewportInteraction::MousePick& mousePick, AzToolsFramework::ViewportInteraction::MouseButtons buttons,
|
||||
AzToolsFramework::ViewportInteraction::InteractionId interactionId,
|
||||
AzToolsFramework::ViewportInteraction::KeyboardModifiers modifiers);
|
||||
|
||||
//! Create a mouse buttons from the specified mouse button.
|
||||
AzToolsFramework::ViewportInteraction::MouseButtons CreateMouseButtons(
|
||||
AzToolsFramework::ViewportInteraction::MouseButton button);
|
||||
AzToolsFramework::ViewportInteraction::MouseButtons CreateMouseButtons(AzToolsFramework::ViewportInteraction::MouseButton button);
|
||||
|
||||
//! Create a mouse interaction event from the specified interaction and event.
|
||||
AzToolsFramework::ViewportInteraction::MouseInteractionEvent CreateMouseInteractionEvent(
|
||||
@@ -61,5 +64,5 @@ namespace AzManipulatorTestFramework
|
||||
AzFramework::ScreenPoint GetCameraStateViewportCenter(const AzFramework::CameraState& cameraState);
|
||||
|
||||
//! Default viewport size (1080p) in 16:9 aspect ratio.
|
||||
const auto DefaultViewportSize = AZ::Vector2(1920.0f, 1080.0f);
|
||||
inline const auto DefaultViewportSize = AZ::Vector2(1920.0f, 1080.0f);
|
||||
} // namespace AzManipulatorTestFramework
|
||||
|
||||
+36
-22
@@ -14,7 +14,6 @@
|
||||
#include <AzFramework/Viewport/ViewportScreen.h>
|
||||
#include <AzManipulatorTestFramework/AzManipulatorTestFrameworkUtils.h>
|
||||
#include <AzToolsFramework/ViewportSelection/EditorInteractionSystemViewportSelectionRequestBus.h>
|
||||
#include <AzToolsFramework/ViewportSelection/EditorInteractionSystemViewportSelectionRequestBus.h>
|
||||
#include <AzToolsFramework/ViewportSelection/EditorTransformComponentSelectionRequestBus.h>
|
||||
|
||||
namespace AzManipulatorTestFramework
|
||||
@@ -28,22 +27,21 @@ namespace AzManipulatorTestFramework
|
||||
using MouseEvent = AzToolsFramework::ViewportInteraction::MouseEvent;
|
||||
using MousePick = AzToolsFramework::ViewportInteraction::MousePick;
|
||||
|
||||
AZStd::shared_ptr<AzToolsFramework::LinearManipulator> CreateLinearManipulator(
|
||||
const AzToolsFramework::ManipulatorManagerId manipulatorManagerId,
|
||||
const AZ::Vector3& position,
|
||||
const float radius)
|
||||
// create a default sphere view for a manipulator for simple intersection
|
||||
template<typename Manipulator>
|
||||
void SetupManipulatorView(
|
||||
AZStd::shared_ptr<Manipulator> manipulator, const AzToolsFramework::ManipulatorManagerId manipulatorManagerId,
|
||||
const AZ::Vector3& position, const float radius)
|
||||
{
|
||||
auto manipulator = AzToolsFramework::LinearManipulator::MakeShared(AZ::Transform::CreateIdentity());
|
||||
manipulator->SetLocalPosition(position);
|
||||
|
||||
// unit sphere view
|
||||
auto sphereView = AzToolsFramework::CreateManipulatorViewSphere(
|
||||
AZ::Colors::Red, radius,
|
||||
[](const MouseInteraction& /*mouseInteraction*/, const bool /*mouseOver*/,
|
||||
const AZ::Color& defaultColor)
|
||||
{
|
||||
return defaultColor;
|
||||
}, true);
|
||||
[]([[maybe_unused]] const MouseInteraction& mouseInteraction, [[maybe_unused]] const bool mouseOver,
|
||||
const AZ::Color& defaultColor)
|
||||
{
|
||||
return defaultColor;
|
||||
},
|
||||
true);
|
||||
|
||||
// unit sphere bound
|
||||
AzToolsFramework::Picking::BoundShapeSphere sphereBound;
|
||||
@@ -62,6 +60,26 @@ namespace AzManipulatorTestFramework
|
||||
// this would occur internally when the manipulator is drawn but we must do manually here to ensure that the
|
||||
// bounds will always be valid upon instantiation
|
||||
view->RefreshBound(manipulatorManagerId, manipulator->GetManipulatorId(), sphereBound);
|
||||
}
|
||||
|
||||
AZStd::shared_ptr<AzToolsFramework::LinearManipulator> CreateLinearManipulator(
|
||||
const AzToolsFramework::ManipulatorManagerId manipulatorManagerId, const AZ::Vector3& position, const float radius)
|
||||
{
|
||||
auto manipulator = AzToolsFramework::LinearManipulator::MakeShared(AZ::Transform::CreateIdentity());
|
||||
manipulator->SetLocalPosition(position);
|
||||
|
||||
SetupManipulatorView(manipulator, manipulatorManagerId, position, radius);
|
||||
|
||||
return manipulator;
|
||||
}
|
||||
|
||||
AZStd::shared_ptr<AzToolsFramework::PlanarManipulator> CreatePlanarManipulator(
|
||||
const AzToolsFramework::ManipulatorManagerId manipulatorManagerId, const AZ::Vector3& position, const float radius)
|
||||
{
|
||||
auto manipulator = AzToolsFramework::PlanarManipulator::MakeShared(AZ::Transform::CreateIdentity());
|
||||
manipulator->SetLocalPosition(position);
|
||||
|
||||
SetupManipulatorView(manipulator, manipulatorManagerId, position, radius);
|
||||
|
||||
return manipulator;
|
||||
}
|
||||
@@ -104,8 +122,7 @@ namespace AzManipulatorTestFramework
|
||||
return buttons;
|
||||
}
|
||||
|
||||
MouseInteractionEvent CreateMouseInteractionEvent(
|
||||
const MouseInteraction& mouseInteraction, MouseEvent event)
|
||||
MouseInteractionEvent CreateMouseInteractionEvent(const MouseInteraction& mouseInteraction, MouseEvent event)
|
||||
{
|
||||
return MouseInteractionEvent(mouseInteraction, event);
|
||||
}
|
||||
@@ -114,8 +131,7 @@ namespace AzManipulatorTestFramework
|
||||
{
|
||||
AzToolsFramework::EditorInteractionSystemViewportSelectionRequestBus::Event(
|
||||
AzToolsFramework::GetEntityContextId(),
|
||||
&AzToolsFramework::ViewportInteraction::InternalMouseViewportRequests::InternalHandleAllMouseInteractions,
|
||||
event);
|
||||
&AzToolsFramework::ViewportInteraction::InternalMouseViewportRequests::InternalHandleAllMouseInteractions, event);
|
||||
}
|
||||
|
||||
AzFramework::CameraState SetCameraStatePosition(const AZ::Vector3& position, AzFramework::CameraState& cameraState)
|
||||
@@ -133,9 +149,7 @@ namespace AzManipulatorTestFramework
|
||||
|
||||
AzFramework::ScreenPoint GetCameraStateViewportCenter(const AzFramework::CameraState& cameraState)
|
||||
{
|
||||
return {
|
||||
aznumeric_cast<int>(cameraState.m_viewportSize.GetX() / 2.f),
|
||||
aznumeric_cast<int>(cameraState.m_viewportSize.GetY() / 2.f)
|
||||
};
|
||||
return { aznumeric_cast<int>(cameraState.m_viewportSize.GetX() / 2.f),
|
||||
aznumeric_cast<int>(cameraState.m_viewportSize.GetY() / 2.f) };
|
||||
}
|
||||
} // namespace UnitTest
|
||||
} // namespace AzManipulatorTestFramework
|
||||
|
||||
@@ -10,52 +10,55 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "AzManipulatorTestFrameworkTestFixtures.h"
|
||||
|
||||
#include <AzCore/std/smart_ptr/unique_ptr.h>
|
||||
#include <AzFramework/Viewport/ViewportScreen.h>
|
||||
#include <AzManipulatorTestFramework/AzManipulatorTestFramework.h>
|
||||
#include "AzManipulatorTestFrameworkTestFixtures.h"
|
||||
#include <AzManipulatorTestFramework/DirectManipulatorViewportInteraction.h>
|
||||
#include <AzManipulatorTestFramework/IndirectManipulatorViewportInteraction.h>
|
||||
#include <AzManipulatorTestFramework/ImmediateModeActionDispatcher.h>
|
||||
#include <AzManipulatorTestFramework/AzManipulatorTestFrameworkUtils.h>
|
||||
#include <AzManipulatorTestFramework/DirectManipulatorViewportInteraction.h>
|
||||
#include <AzManipulatorTestFramework/ImmediateModeActionDispatcher.h>
|
||||
#include <AzManipulatorTestFramework/IndirectManipulatorViewportInteraction.h>
|
||||
#include <AzToolsFramework/Manipulators/LinearManipulator.h>
|
||||
#include <AzToolsFramework/Manipulators/PlanarManipulator.h>
|
||||
#include <AzToolsFramework/ViewportSelection/EditorSelectionUtil.h>
|
||||
#include <AZTestShared/Math/MathTestHelpers.h>
|
||||
|
||||
namespace UnitTest
|
||||
{
|
||||
class GridSnappingFixture
|
||||
: public ToolsApplicationFixture
|
||||
class GridSnappingFixture : public ToolsApplicationFixture
|
||||
{
|
||||
public:
|
||||
GridSnappingFixture()
|
||||
: m_viewportManipulatorInteraction(AZStd::make_unique<AzManipulatorTestFramework::DirectCallManipulatorViewportInteraction>())
|
||||
, m_actionDispatcher(AZStd::make_unique<AzManipulatorTestFramework::ImmediateModeActionDispatcher>(*m_viewportManipulatorInteraction))
|
||||
, m_linearManipulator(
|
||||
AzManipulatorTestFramework::CreateLinearManipulator(
|
||||
m_viewportManipulatorInteraction->GetManipulatorManager().GetId(),
|
||||
/*position=*/AZ::Vector3(0.0f, 50.0f, 0.0f),
|
||||
/*radius=*/m_boundsRadius))
|
||||
{}
|
||||
, m_actionDispatcher(
|
||||
AZStd::make_unique<AzManipulatorTestFramework::ImmediateModeActionDispatcher>(*m_viewportManipulatorInteraction))
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
void SetUpEditorFixtureImpl() override
|
||||
{
|
||||
m_cameraState = AzFramework::CreateIdentityDefaultCamera(
|
||||
AZ::Vector3::CreateZero(), AzManipulatorTestFramework::DefaultViewportSize);
|
||||
m_cameraState =
|
||||
AzFramework::CreateIdentityDefaultCamera(AZ::Vector3::CreateZero(), AzManipulatorTestFramework::DefaultViewportSize);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
const float m_boundsRadius = 1.0f;
|
||||
AZStd::unique_ptr<AzManipulatorTestFramework::ManipulatorViewportInteraction> m_viewportManipulatorInteraction;
|
||||
AZStd::unique_ptr<AzManipulatorTestFramework::ImmediateModeActionDispatcher> m_actionDispatcher;
|
||||
AZStd::shared_ptr<AzToolsFramework::LinearManipulator> m_linearManipulator;
|
||||
AzFramework::CameraState m_cameraState;
|
||||
};
|
||||
|
||||
TEST_F(GridSnappingFixture, MouseDownWithSnappingEnabledSnapsToClosestGridSize)
|
||||
{
|
||||
AZStd::shared_ptr<AzToolsFramework::LinearManipulator> linearManipulator(AzManipulatorTestFramework::CreateLinearManipulator(
|
||||
m_viewportManipulatorInteraction->GetManipulatorManager().GetId(),
|
||||
/*position=*/AZ::Vector3(0.0f, 50.0f, 0.0f),
|
||||
/*radius=*/m_boundsRadius));
|
||||
|
||||
// the initial starting position of the manipulator (in front of the camera)
|
||||
const auto initialPositionWorld = m_linearManipulator->GetLocalPosition();
|
||||
const auto initialPositionWorld = linearManipulator->GetLocalPosition();
|
||||
// where the manipulator should end up (in front and to the left of the camera)
|
||||
const auto finalPositionWorld = AZ::Vector3(-10.0f, 50.0f, 0.0f);
|
||||
// perspective scale factor for manipulator distance to camera
|
||||
@@ -66,21 +69,18 @@ namespace UnitTest
|
||||
// adjusted final world position taking into account the manipulator position relative to the camera
|
||||
const auto finalPositionWorldAdjusted = finalPositionWorld - (vectorToInitialPositionWorld * scaledRadiusBound);
|
||||
// calculate the position in screen space of the initial position of the manipulator
|
||||
const auto initialPositionScreen =
|
||||
AzFramework::WorldToScreen(initialPositionWorld, m_cameraState);
|
||||
const auto initialPositionScreen = AzFramework::WorldToScreen(initialPositionWorld, m_cameraState);
|
||||
// calculate the position in screen space of the final position of the manipulator
|
||||
const auto finalPositionScreen = AzFramework::WorldToScreen(finalPositionWorldAdjusted, m_cameraState);
|
||||
|
||||
// callback to update the manipulator's current position
|
||||
m_linearManipulator->InstallMouseMoveCallback(
|
||||
[this](const AzToolsFramework::LinearManipulator::Action& action)
|
||||
{
|
||||
auto pos = action.LocalPosition();
|
||||
m_linearManipulator->SetLocalPosition(pos);
|
||||
});
|
||||
linearManipulator->InstallMouseMoveCallback(
|
||||
[this, linearManipulator](const AzToolsFramework::LinearManipulator::Action& action)
|
||||
{
|
||||
linearManipulator->SetLocalPosition(action.LocalPosition());
|
||||
});
|
||||
|
||||
m_actionDispatcher
|
||||
->EnableSnapToGrid()
|
||||
m_actionDispatcher->EnableSnapToGrid()
|
||||
->GridSize(5.0f)
|
||||
->CameraState(m_cameraState)
|
||||
->MousePosition(initialPositionScreen)
|
||||
@@ -89,7 +89,67 @@ namespace UnitTest
|
||||
->MousePosition(finalPositionScreen)
|
||||
->MouseLButtonUp()
|
||||
->ExpectManipulatorNotBeingInteracted()
|
||||
->ExpectTrue(m_linearManipulator->GetLocalPosition().IsClose(finalPositionWorld, 0.01f))
|
||||
;
|
||||
->ExpectTrue(linearManipulator->GetLocalPosition().IsClose(finalPositionWorld, 0.01f));
|
||||
}
|
||||
|
||||
template<typename Manipulator>
|
||||
void ValidateManipulatorSnappingBehavior(
|
||||
AZStd::shared_ptr<Manipulator> manipulator, AzManipulatorTestFramework::ImmediateModeActionDispatcher* actionDispatcher,
|
||||
const AzFramework::CameraState& cameraState)
|
||||
{
|
||||
manipulator->SetLocalOrientation(AZ::Quaternion::CreateFromEulerAnglesDegrees(AZ::Vector3(180.0f, 0.0f, 135.0f)));
|
||||
|
||||
// the initial starting position of the manipulator (in front of the camera)
|
||||
const auto initialPositionWorld = manipulator->GetLocalPosition() + AZ::Vector3::CreateAxisX(0.15f);
|
||||
// where the manipulator should end up (unmoved)
|
||||
const auto finalPositionWorld = manipulator->GetLocalPosition();
|
||||
// where we should move the mouse to
|
||||
const auto attemptPositionWorld = manipulator->GetLocalPosition() + AZ::Vector3::CreateAxisX(0.35f);
|
||||
// calculate the position in screen space of the initial position of the manipulator
|
||||
const auto initialPositionScreen = AzFramework::WorldToScreen(initialPositionWorld, cameraState);
|
||||
// calculate the position in screen space of the final position of the manipulator
|
||||
const auto attemptPositionScreen = AzFramework::WorldToScreen(attemptPositionWorld, cameraState);
|
||||
|
||||
// callback to update the manipulator's current position
|
||||
manipulator->InstallMouseMoveCallback(
|
||||
[manipulator](const typename Manipulator::Action& action)
|
||||
{
|
||||
manipulator->SetLocalPosition(action.LocalPosition());
|
||||
});
|
||||
|
||||
actionDispatcher->EnableSnapToGrid()
|
||||
->GridSize(1.0f)
|
||||
->CameraState(cameraState)
|
||||
->MousePosition(initialPositionScreen)
|
||||
->MouseLButtonDown()
|
||||
->ExpectManipulatorBeingInteracted()
|
||||
->MousePosition(attemptPositionScreen)
|
||||
->MouseLButtonUp()
|
||||
->ExpectManipulatorNotBeingInteracted()
|
||||
->ExpectThat(manipulator->GetLocalPosition(), IsCloseTolerance(finalPositionWorld, 0.01f));
|
||||
}
|
||||
|
||||
TEST_F(GridSnappingFixture, MouseDownAndMoveLinearManipulatorDoesNotSnapWithMovementSmallerThanHalfGridSize)
|
||||
{
|
||||
AZStd::shared_ptr<AzToolsFramework::LinearManipulator> linearManipulator(AzManipulatorTestFramework::CreateLinearManipulator(
|
||||
m_viewportManipulatorInteraction->GetManipulatorManager().GetId(),
|
||||
/*position=*/AZ::Vector3(0.0f, 10.0f, 0.0f),
|
||||
/*radius=*/m_boundsRadius));
|
||||
|
||||
linearManipulator->SetAxis(AZ::Vector3::CreateAxisY());
|
||||
|
||||
ValidateManipulatorSnappingBehavior(linearManipulator, m_actionDispatcher.get(), m_cameraState);
|
||||
}
|
||||
|
||||
TEST_F(GridSnappingFixture, MouseDownAndMovePlanarManipulatorDoesNotSnapWithMovementSmallerThanHalfGridSize)
|
||||
{
|
||||
AZStd::shared_ptr<AzToolsFramework::PlanarManipulator> planarManipulator(AzManipulatorTestFramework::CreatePlanarManipulator(
|
||||
m_viewportManipulatorInteraction->GetManipulatorManager().GetId(),
|
||||
/*position=*/AZ::Vector3(0.0f, 10.0f, 0.0f),
|
||||
/*radius=*/m_boundsRadius));
|
||||
|
||||
planarManipulator->SetAxes(AZ::Vector3::CreateAxisY(), AZ::Vector3::CreateAxisZ());
|
||||
|
||||
ValidateManipulatorSnappingBehavior(planarManipulator, m_actionDispatcher.get(), m_cameraState);
|
||||
}
|
||||
} // namespace UnitTest
|
||||
|
||||
@@ -433,6 +433,11 @@ namespace AzToolsFramework
|
||||
return m_manipulatorSpaceWithLocalTransform.GetSpace();
|
||||
}
|
||||
|
||||
const AZ::Vector3& Manipulators::GetNonUniformScale() const
|
||||
{
|
||||
return m_manipulatorSpaceWithLocalTransform.GetNonUniformScale();
|
||||
}
|
||||
|
||||
void Manipulators::SetSpace(const AZ::Transform& worldFromLocal)
|
||||
{
|
||||
m_manipulatorSpaceWithLocalTransform.SetSpace(worldFromLocal);
|
||||
|
||||
+13
-14
@@ -192,8 +192,7 @@ namespace AzToolsFramework
|
||||
/// for each vertex associated with the translation manipulator to use with offset calculations when updating.
|
||||
template<typename Vertex>
|
||||
void InitializeVertexLookup(
|
||||
IndexedTranslationManipulator<Vertex>& translationManipulator,
|
||||
const AZ::EntityId entityId, const AZ::Vector3& snapOffset)
|
||||
IndexedTranslationManipulator<Vertex>& translationManipulator, const AZ::EntityId entityId)
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework);
|
||||
|
||||
@@ -202,7 +201,7 @@ namespace AzToolsFramework
|
||||
AZ::FixedVerticesRequestBus<Vertex>::Bind(fixedVertices, entityId);
|
||||
|
||||
translationManipulator.Process(
|
||||
[snapOffset, fixedVertices]
|
||||
[fixedVertices]
|
||||
(typename IndexedTranslationManipulator<Vertex>::VertexLookup& vertexLookup)
|
||||
{
|
||||
Vertex vertex;
|
||||
@@ -213,7 +212,7 @@ namespace AzToolsFramework
|
||||
|
||||
if (found)
|
||||
{
|
||||
vertexLookup.m_start = vertex + AZ::AdaptVertexIn<Vertex>(snapOffset);
|
||||
vertexLookup.m_start = vertex;
|
||||
vertexLookup.m_offset = Vertex::CreateZero();
|
||||
}
|
||||
});
|
||||
@@ -250,10 +249,10 @@ namespace AzToolsFramework
|
||||
|
||||
// linear manipulator callbacks
|
||||
m_translationManipulator->m_manipulator.InstallLinearManipulatorMouseDownCallback(
|
||||
[this](const LinearManipulator::Action& action)
|
||||
[this]([[maybe_unused]] const LinearManipulator::Action& action)
|
||||
{
|
||||
BeginBatchMovement();
|
||||
InitializeVertexLookup(*m_translationManipulator, GetEntityId(), action.m_start.m_positionSnapOffset);
|
||||
InitializeVertexLookup(*m_translationManipulator, GetEntityId());
|
||||
});
|
||||
|
||||
m_translationManipulator->m_manipulator.InstallLinearManipulatorMouseMoveCallback(
|
||||
@@ -264,17 +263,17 @@ namespace AzToolsFramework
|
||||
});
|
||||
|
||||
m_translationManipulator->m_manipulator.InstallLinearManipulatorMouseUpCallback(
|
||||
[this](const LinearManipulator::Action& /*action*/)
|
||||
[this]([[maybe_unused]] const LinearManipulator::Action& action)
|
||||
{
|
||||
EndBatchMovement();
|
||||
});
|
||||
|
||||
// planar manipulator callbacks
|
||||
m_translationManipulator->m_manipulator.InstallPlanarManipulatorMouseDownCallback(
|
||||
[this](const PlanarManipulator::Action& action)
|
||||
[this]([[maybe_unused]] const PlanarManipulator::Action& action)
|
||||
{
|
||||
BeginBatchMovement();
|
||||
InitializeVertexLookup(*m_translationManipulator, GetEntityId(), action.m_start.m_snapOffset);
|
||||
InitializeVertexLookup(*m_translationManipulator, GetEntityId());
|
||||
});
|
||||
|
||||
m_translationManipulator->m_manipulator.InstallPlanarManipulatorMouseMoveCallback(
|
||||
@@ -285,17 +284,17 @@ namespace AzToolsFramework
|
||||
});
|
||||
|
||||
m_translationManipulator->m_manipulator.InstallPlanarManipulatorMouseUpCallback(
|
||||
[this](const PlanarManipulator::Action& /*action*/)
|
||||
[this]([[maybe_unused]] const PlanarManipulator::Action& action)
|
||||
{
|
||||
EndBatchMovement();
|
||||
});
|
||||
|
||||
// surface manipulator callbacks
|
||||
m_translationManipulator->m_manipulator.InstallSurfaceManipulatorMouseDownCallback(
|
||||
[this](const SurfaceManipulator::Action& action)
|
||||
[this]([[maybe_unused]] const SurfaceManipulator::Action& action)
|
||||
{
|
||||
BeginBatchMovement();
|
||||
InitializeVertexLookup(*m_translationManipulator, GetEntityId(), action.m_start.m_snapOffset);
|
||||
InitializeVertexLookup(*m_translationManipulator, GetEntityId());
|
||||
});
|
||||
|
||||
m_translationManipulator->m_manipulator.InstallSurfaceManipulatorMouseMoveCallback(
|
||||
@@ -306,7 +305,7 @@ namespace AzToolsFramework
|
||||
});
|
||||
|
||||
m_translationManipulator->m_manipulator.InstallSurfaceManipulatorMouseUpCallback(
|
||||
[this](const SurfaceManipulator::Action& /*action*/)
|
||||
[this]([[maybe_unused]] const SurfaceManipulator::Action& action)
|
||||
{
|
||||
EndBatchMovement();
|
||||
});
|
||||
@@ -893,7 +892,7 @@ namespace AzToolsFramework
|
||||
{
|
||||
BeginBatchMovement();
|
||||
|
||||
InitializeVertexLookup(*m_translationManipulator, GetEntityId(), AZ::Vector3::CreateZero());
|
||||
InitializeVertexLookup(*m_translationManipulator, GetEntityId());
|
||||
// note: AdaptVertexIn/Out is to ensure we clamp the vertex local Z position to 0 if
|
||||
// dealing with Vector2s when setting the position of the manipulator.
|
||||
const AZ::Vector3 localOffset =
|
||||
|
||||
+26
-44
@@ -23,8 +23,8 @@ namespace AzToolsFramework
|
||||
{
|
||||
LinearManipulator::Starter CalculateLinearManipulationDataStart(
|
||||
const LinearManipulator::Fixed& fixed, const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale,
|
||||
const AZ::Transform& localTransform, const GridSnapAction& gridSnapAction, const ViewportInteraction::MouseInteraction& interaction,
|
||||
const float intersectionDistance, const AzFramework::CameraState& cameraState)
|
||||
const AZ::Transform& localTransform, const ViewportInteraction::MouseInteraction& interaction, const float intersectionDistance,
|
||||
const AzFramework::CameraState& cameraState)
|
||||
{
|
||||
const ManipulatorInteraction manipulatorInteraction =
|
||||
BuildManipulatorInteraction(
|
||||
@@ -50,28 +50,9 @@ namespace AzToolsFramework
|
||||
manipulatorInteraction.m_localRayOrigin, manipulatorInteraction.m_localRayDirection,
|
||||
localIntersectionPoint, startTransition.m_localNormal, start.m_localHitPosition);
|
||||
|
||||
const float gridSize = gridSnapAction.m_gridSnapParams.m_gridSize;
|
||||
const bool snapping = gridSnapAction.m_gridSnapParams.m_gridSnap;
|
||||
const float scaleRecip = manipulatorInteraction.m_scaleReciprocal;
|
||||
|
||||
// calculate position amount to snap, to align with grid
|
||||
const AZ::Vector3 positionSnapOffset = snapping && !gridSnapAction.m_localSnapping
|
||||
? CalculateSnappedOffset(localTransform.GetTranslation(), axis, gridSize * scaleRecip)
|
||||
: AZ::Vector3::CreateZero();
|
||||
|
||||
const AZ::Vector3 localScale = AZ::Vector3(localTransform.GetUniformScale());
|
||||
const AZ::Quaternion localRotation = QuaternionFromTransformNoScaling(localTransform);
|
||||
// calculate scale amount to snap, to align to round scale value
|
||||
const AZ::Vector3 scaleSnapOffset = snapping && !gridSnapAction.m_localSnapping
|
||||
? localRotation.GetInverseFull().TransformVector(CalculateSnappedOffset(
|
||||
localRotation.TransformVector(localScale), axis, gridSize * scaleRecip))
|
||||
: AZ::Vector3::CreateZero();
|
||||
|
||||
start.m_screenPosition = interaction.m_mousePick.m_screenCoordinates;
|
||||
start.m_positionSnapOffset = positionSnapOffset;
|
||||
start.m_scaleSnapOffset = scaleSnapOffset;
|
||||
start.m_localPosition = localTransform.GetTranslation() + positionSnapOffset;
|
||||
start.m_localScale = localScale + scaleSnapOffset;
|
||||
start.m_localPosition = localTransform.GetTranslation();
|
||||
start.m_localScale = AZ::Vector3(localTransform.GetUniformScale());;
|
||||
start.m_localAxis = axis;
|
||||
// sign to determine which side of the linear axis we pressed
|
||||
// (useful to know when the visual axis flips to face the camera)
|
||||
@@ -87,7 +68,7 @@ namespace AzToolsFramework
|
||||
LinearManipulator::Action CalculateLinearManipulationDataAction(
|
||||
const LinearManipulator::Fixed& fixed, const LinearManipulator::Starter& starter,
|
||||
const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale, const AZ::Transform& localTransform,
|
||||
const GridSnapAction& gridSnapAction, const ViewportInteraction::MouseInteraction& interaction)
|
||||
const GridSnapParameters& gridSnapParams, const ViewportInteraction::MouseInteraction& interaction)
|
||||
{
|
||||
const ManipulatorInteraction manipulatorInteraction =
|
||||
BuildManipulatorInteraction(
|
||||
@@ -108,31 +89,34 @@ namespace AzToolsFramework
|
||||
GetCameraState(interaction.m_interactionId.m_viewportId));
|
||||
|
||||
const AZ::Vector3 axis = TransformDirectionNoScaling(localTransform, fixed.m_axis);
|
||||
// The local positions have been transformed to the reference frame of the object being manipulated. But they appear in the world
|
||||
// with non-uniform scale applied, and the object being manipulated will want to work with unscaled deltas, so we need to divide by
|
||||
// the non-uniform scale here.
|
||||
// the local positions have been transformed to the reference frame of the object being manipulated, but they appear in the world
|
||||
// with non-uniform scale applied, the object being manipulated will want to work with unscaled deltas, so we need to divide by
|
||||
// the non-uniform scale here
|
||||
const AZ::Vector3 hitDelta = (localHitPosition - start.m_localHitPosition) / nonUniformScale;
|
||||
const AZ::Vector3 unsnappedOffset = axis * axis.Dot(hitDelta);
|
||||
|
||||
const float scaleRecip = manipulatorInteraction.m_scaleReciprocal * axis.Dot(manipulatorInteraction.m_nonUniformScaleReciprocal);
|
||||
const float gridSize = gridSnapAction.m_gridSnapParams.m_gridSize;
|
||||
const bool snapping = gridSnapAction.m_gridSnapParams.m_gridSnap;
|
||||
const float scaleRecip =
|
||||
manipulatorInteraction.m_scaleReciprocal * fixed.m_axis.Dot(manipulatorInteraction.m_nonUniformScaleReciprocal);
|
||||
const float gridSize = gridSnapParams.m_gridSize;
|
||||
const bool snapping = gridSnapParams.m_gridSnap;
|
||||
|
||||
LinearManipulator::Action action;
|
||||
action.m_fixed = fixed;
|
||||
action.m_start = start;
|
||||
action.m_current.m_localPositionOffset = snapping
|
||||
? unsnappedOffset + CalculateSnappedOffset(unsnappedOffset, axis, gridSize * scaleRecip)
|
||||
? CalculateSnappedAmount(unsnappedOffset, axis, gridSize * scaleRecip)
|
||||
: unsnappedOffset;
|
||||
action.m_current.m_screenPosition = interaction.m_mousePick.m_screenCoordinates;
|
||||
action.m_viewportId = interaction.m_interactionId.m_viewportId;
|
||||
|
||||
const AZ::Quaternion localRotation = QuaternionFromTransformNoScaling(localTransform);
|
||||
const AZ::Vector3 scaledUnsnappedOffset = unsnappedOffset * startTransition.m_screenToWorldScale * NonUniformScaleReciprocal(nonUniformScale);
|
||||
const AZ::Vector3 scaledUnsnappedOffset =
|
||||
unsnappedOffset * startTransition.m_screenToWorldScale * NonUniformScaleReciprocal(nonUniformScale);
|
||||
|
||||
// how much to adjust the scale based on movement
|
||||
const AZ::Quaternion invLocalRotation = localRotation.GetInverseFull();
|
||||
action.m_current.m_localScaleOffset = snapping
|
||||
? invLocalRotation.TransformVector((scaledUnsnappedOffset + CalculateSnappedOffset(scaledUnsnappedOffset, axis, gridSize * scaleRecip)))
|
||||
? invLocalRotation.TransformVector(CalculateSnappedAmount(scaledUnsnappedOffset, axis, gridSize * scaleRecip))
|
||||
: invLocalRotation.TransformVector(scaledUnsnappedOffset);
|
||||
|
||||
// record what modifier keys are held during this action
|
||||
@@ -171,19 +155,18 @@ namespace AzToolsFramework
|
||||
const ViewportInteraction::MouseInteraction& interaction, const float rayIntersectionDistance)
|
||||
{
|
||||
const AZ::Transform worldFromLocalUniformScale = TransformUniformScale(GetSpace());
|
||||
const GridSnapParameters gridSnapParams = GridSnapSettings(interaction.m_interactionId.m_viewportId);
|
||||
|
||||
// note: m_localTransform must not be made uniform as it may contain a local scale we want to snap
|
||||
m_starter = CalculateLinearManipulationDataStart(
|
||||
m_fixed, worldFromLocalUniformScale, GetNonUniformScale(), GetLocalTransform(),
|
||||
GridSnapAction(gridSnapParams, interaction.m_keyboardModifiers.Alt()), interaction, rayIntersectionDistance,
|
||||
m_fixed, worldFromLocalUniformScale, GetNonUniformScale(), GetLocalTransform(), interaction, rayIntersectionDistance,
|
||||
GetCameraState(interaction.m_interactionId.m_viewportId));
|
||||
|
||||
if (m_onLeftMouseDownCallback)
|
||||
{
|
||||
const GridSnapParameters gridSnapParams = GridSnapSettings(interaction.m_interactionId.m_viewportId);
|
||||
|
||||
m_onLeftMouseDownCallback(CalculateLinearManipulationDataAction(
|
||||
m_fixed, m_starter, worldFromLocalUniformScale, GetNonUniformScale(), GetLocalTransform(),
|
||||
GridSnapAction(gridSnapParams, interaction.m_keyboardModifiers.Alt()), interaction));
|
||||
m_fixed, m_starter, worldFromLocalUniformScale, GetNonUniformScale(), GetLocalTransform(), gridSnapParams, interaction));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,8 +178,8 @@ namespace AzToolsFramework
|
||||
|
||||
// note: m_localTransform must not be made uniform as it may contain a local scale we want to snap
|
||||
m_onMouseMoveCallback(CalculateLinearManipulationDataAction(
|
||||
m_fixed, m_starter, TransformUniformScale(GetSpace()), GetNonUniformScale(), GetLocalTransform(),
|
||||
GridSnapAction(gridSnapParams, interaction.m_keyboardModifiers.Alt()), interaction));
|
||||
m_fixed, m_starter, TransformUniformScale(GetSpace()), GetNonUniformScale(), GetLocalTransform(), gridSnapParams,
|
||||
interaction));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,8 +191,7 @@ namespace AzToolsFramework
|
||||
|
||||
// note: m_localTransform must not be made uniform as it may contain a local scale we want to snap
|
||||
m_onLeftMouseUpCallback(CalculateLinearManipulationDataAction(
|
||||
m_fixed, m_starter, TransformUniformScale(GetSpace()), GetNonUniformScale(), GetLocalTransform(),
|
||||
GridSnapAction(gridSnapParams, interaction.m_keyboardModifiers.Alt()), interaction));
|
||||
m_fixed, m_starter, TransformUniformScale(GetSpace()), GetNonUniformScale(), GetLocalTransform(), gridSnapParams, interaction));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,8 +214,8 @@ namespace AzToolsFramework
|
||||
GridSnapSettings(mouseInteraction.m_interactionId.m_viewportId);
|
||||
|
||||
const auto action = CalculateLinearManipulationDataAction(
|
||||
m_fixed, m_starter, TransformUniformScale(GetSpace()), GetNonUniformScale(), GetLocalTransform(),
|
||||
GridSnapAction(gridSnapParams, mouseInteraction.m_keyboardModifiers.Alt()), mouseInteraction);
|
||||
m_fixed, m_starter, TransformUniformScale(GetSpace()), GetNonUniformScale(), GetLocalTransform(), gridSnapParams,
|
||||
mouseInteraction);
|
||||
|
||||
// display the exact hit (ray intersection) of the mouse pick on the manipulator
|
||||
DrawTransformAxes(
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
struct GridSnapAction;
|
||||
struct GridSnapParameters;
|
||||
|
||||
/// LinearManipulator serves as a visual tool for users to modify values
|
||||
/// in one dimension on an axis defined in 3D space.
|
||||
@@ -68,8 +68,6 @@ namespace AzToolsFramework
|
||||
AZ::Vector3 m_localScale; ///< The current scale of the manipulator in local space.
|
||||
AZ::Vector3 m_localHitPosition; ///< The intersection point in local space between the ray and the manipulator when the mouse down event happens.
|
||||
AZ::Vector3 m_localAxis; ///< The axis in the local space of the manipulator itself.
|
||||
AZ::Vector3 m_positionSnapOffset; ///< The snap offset amount to ensure manipulator is aligned to the grid.
|
||||
AZ::Vector3 m_scaleSnapOffset; ///< The snap offset amount to ensure manipulator is aligned to round scale increments.
|
||||
float m_sign; ///< Used to determine which side of the axis we clicked on in case it's flipped to face the camera.
|
||||
AzFramework::ScreenPoint m_screenPosition; ///< The initial position in screen space of the manipulator.
|
||||
};
|
||||
@@ -91,7 +89,7 @@ namespace AzToolsFramework
|
||||
ViewportInteraction::KeyboardModifiers m_modifiers;
|
||||
int m_viewportId; ///< The id of the viewport this manipulator is being used in.
|
||||
AZ::Vector3 LocalScale() const { return m_start.m_localScale + m_current.m_localScaleOffset; }
|
||||
AZ::Vector3 LocalScaleOffset() const { return m_start.m_scaleSnapOffset + m_current.m_localScaleOffset; }
|
||||
AZ::Vector3 LocalScaleOffset() const { return m_current.m_localScaleOffset; }
|
||||
AZ::Vector3 LocalPosition() const { return m_start.m_localPosition + m_current.m_localPositionOffset; }
|
||||
AZ::Vector3 LocalPositionOffset() const { return m_current.m_localPositionOffset; }
|
||||
AZ::Vector2 ScreenOffset() const
|
||||
@@ -162,11 +160,11 @@ namespace AzToolsFramework
|
||||
|
||||
LinearManipulator::Starter CalculateLinearManipulationDataStart(
|
||||
const LinearManipulator::Fixed& fixed, const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale,
|
||||
const AZ::Transform& localTransform, const GridSnapAction& gridSnapAction, const ViewportInteraction::MouseInteraction& interaction,
|
||||
float intersectionDistance, const AzFramework::CameraState& cameraState);
|
||||
const AZ::Transform& localTransform, const ViewportInteraction::MouseInteraction& interaction, float intersectionDistance,
|
||||
const AzFramework::CameraState& cameraState);
|
||||
|
||||
LinearManipulator::Action CalculateLinearManipulationDataAction(
|
||||
const LinearManipulator::Fixed& fixed, const LinearManipulator::Starter& starter,
|
||||
const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale, const AZ::Transform& localTransform,
|
||||
const GridSnapAction& gridSnapAction, const ViewportInteraction::MouseInteraction& interaction);
|
||||
const GridSnapParameters& gridSnapParams, const ViewportInteraction::MouseInteraction& interaction);
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
+25
-11
@@ -42,12 +42,6 @@ namespace AzToolsFramework
|
||||
{
|
||||
}
|
||||
|
||||
GridSnapAction::GridSnapAction(const GridSnapParameters& gridSnapParameters, const bool localSnapping)
|
||||
: m_gridSnapParams(gridSnapParameters)
|
||||
, m_localSnapping(localSnapping)
|
||||
{
|
||||
}
|
||||
|
||||
ManipulatorInteraction BuildManipulatorInteraction(
|
||||
const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale,
|
||||
const AZ::Vector3& worldRayOrigin, const AZ::Vector3& worldRayDirection)
|
||||
@@ -57,19 +51,39 @@ namespace AzToolsFramework
|
||||
|
||||
return {localFromWorldUniform.TransformPoint(worldRayOrigin),
|
||||
TransformDirectionNoScaling(localFromWorldUniform, worldRayDirection),
|
||||
ScaleReciprocal(worldFromLocalUniform),
|
||||
NonUniformScaleReciprocal(nonUniformScale)};
|
||||
NonUniformScaleReciprocal(nonUniformScale),
|
||||
ScaleReciprocal(worldFromLocalUniform)};
|
||||
}
|
||||
|
||||
AZ::Vector3 CalculateSnappedOffset(
|
||||
const AZ::Vector3& unsnappedPosition, const AZ::Vector3& axis, const float size)
|
||||
struct SnapAdjustment
|
||||
{
|
||||
float m_existingSnapDistance; //!< How far to snap up or down to align to the grid.
|
||||
float m_nextSnapDistance; //!< The snap increment (will return full signed value (grid size) when distance
|
||||
//!< moved is greater than half of the grid size in either direction).
|
||||
};
|
||||
|
||||
static SnapAdjustment CalculateSnapDistance(const AZ::Vector3& unsnappedPosition, const AZ::Vector3& axis, const float size)
|
||||
{
|
||||
// calculate total distance along axis
|
||||
const float axisDistance = axis.Dot(unsnappedPosition);
|
||||
// round to nearest step size
|
||||
const float snappedAxisDistance = floorf((axisDistance / size) + 0.5f) * size;
|
||||
|
||||
return { axisDistance, snappedAxisDistance };
|
||||
}
|
||||
|
||||
AZ::Vector3 CalculateSnappedOffset(const AZ::Vector3& unsnappedPosition, const AZ::Vector3& axis, const float size)
|
||||
{
|
||||
const auto snapAdjustment = CalculateSnapDistance(unsnappedPosition, axis, size);
|
||||
// return offset along axis to snap to step size
|
||||
return axis * (snappedAxisDistance - axisDistance);
|
||||
return axis * (snapAdjustment.m_nextSnapDistance - snapAdjustment.m_existingSnapDistance);
|
||||
}
|
||||
|
||||
AZ::Vector3 CalculateSnappedAmount(const AZ::Vector3& unsnappedPosition, const AZ::Vector3& axis, const float size)
|
||||
{
|
||||
const auto snapAdjustment = CalculateSnapDistance(unsnappedPosition, axis, size);
|
||||
// return offset along axis to snap to step size
|
||||
return axis * snapAdjustment.m_nextSnapDistance;
|
||||
}
|
||||
|
||||
AZ::Vector3 CalculateSnappedTerrainPosition(
|
||||
|
||||
+9
-13
@@ -31,24 +31,15 @@ namespace AzToolsFramework
|
||||
float m_gridSize;
|
||||
};
|
||||
|
||||
/// Structure to encapsulate the current grid snapping state.
|
||||
struct GridSnapAction
|
||||
{
|
||||
GridSnapAction(const GridSnapParameters& gridSnapParameters, bool localSnapping);
|
||||
|
||||
GridSnapParameters m_gridSnapParams;
|
||||
bool m_localSnapping;
|
||||
};
|
||||
|
||||
/// Structure to hold transformed incoming viewport interaction from world space to manipulator space.
|
||||
struct ManipulatorInteraction
|
||||
{
|
||||
AZ::Vector3 m_localRayOrigin; ///< The ray origin (start) in the reference from of the manipulator.
|
||||
AZ::Vector3 m_localRayDirection; ///< The ray direction in the reference from of the manipulator.
|
||||
float m_scaleReciprocal; ///< The scale reciprocal (1.0 / scale) of the transform used to move the
|
||||
///< ray from world space to local space.
|
||||
AZ::Vector3 m_nonUniformScaleReciprocal; ///< Handles inverting any non-uniform scale which was applied
|
||||
///< separately from the transform.
|
||||
float m_scaleReciprocal; ///< The scale reciprocal (1.0 / scale) of the transform used to move the
|
||||
///< ray from world space to local space.
|
||||
};
|
||||
|
||||
/// Build a ManipulatorInteraction structure from the incoming viewport interaction.
|
||||
@@ -56,11 +47,16 @@ namespace AzToolsFramework
|
||||
const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale,
|
||||
const AZ::Vector3& worldRayOrigin, const AZ::Vector3& worldRayDirection);
|
||||
|
||||
/// Calculate the offset along an axis to adjust a position
|
||||
/// to stay snapped to a given grid size.
|
||||
/// Calculate the offset along an axis to adjust a position to stay snapped to a given grid size.
|
||||
/// @note This is snap up or down to the nearest grid segment (e.g. 0.2 snaps to 0.0 -> delta 0.2,
|
||||
/// 0.7 snaps to 1.0 -> delta 0.3).
|
||||
AZ::Vector3 CalculateSnappedOffset(
|
||||
const AZ::Vector3& unsnappedPosition, const AZ::Vector3& axis, float size);
|
||||
|
||||
/// Return the amount to snap from the starting position given the current grid size.
|
||||
/// @note A movement of more than half size (in either direction) will cause a snap by size.
|
||||
AZ::Vector3 CalculateSnappedAmount(const AZ::Vector3& unsnappedPosition, const AZ::Vector3& axis, float size);
|
||||
|
||||
/// For a given point on the terrain, calculate the closest xy position snapped to the grid
|
||||
/// (z position is aligned to terrain height, not snapped to z grid)
|
||||
AZ::Vector3 CalculateSnappedTerrainPosition(
|
||||
|
||||
+10
-18
@@ -59,17 +59,16 @@ namespace AzToolsFramework
|
||||
const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale, const AZ::Transform& localTransform,
|
||||
const ViewportInteraction::MouseInteraction& interaction,
|
||||
const AZStd::vector<LinearManipulator::Fixed>& fixedAxes,
|
||||
const AZStd::vector<LinearManipulator::Starter>& starterStates, const GridSnapAction& gridSnapAction)
|
||||
const AZStd::vector<LinearManipulator::Starter>& starterStates, const GridSnapParameters& gridSnapParams)
|
||||
{
|
||||
MultiLinearManipulator::Action action;
|
||||
action.m_viewportId = interaction.m_interactionId.m_viewportId;
|
||||
// build up action state for each axis
|
||||
for (size_t fixedIndex = 0; fixedIndex < fixedAxes.size(); ++fixedIndex)
|
||||
{
|
||||
action.m_actions.push_back(
|
||||
CalculateLinearManipulationDataAction(
|
||||
fixedAxes[fixedIndex], starterStates[fixedIndex], worldFromLocal, nonUniformScale, localTransform,
|
||||
gridSnapAction, interaction));
|
||||
action.m_actions.push_back(CalculateLinearManipulationDataAction(
|
||||
fixedAxes[fixedIndex], starterStates[fixedIndex], worldFromLocal, nonUniformScale, localTransform, gridSnapParams,
|
||||
interaction));
|
||||
}
|
||||
|
||||
return action;
|
||||
@@ -79,8 +78,6 @@ namespace AzToolsFramework
|
||||
const ViewportInteraction::MouseInteraction& interaction, const float rayIntersectionDistance)
|
||||
{
|
||||
const AZ::Transform worldFromLocalUniformScale = TransformUniformScale(GetSpace());
|
||||
|
||||
const GridSnapParameters gridSnapParams = GridSnapSettings(interaction.m_interactionId.m_viewportId);
|
||||
const AzFramework::CameraState cameraState = GetCameraState(interaction.m_interactionId.m_viewportId);
|
||||
|
||||
// build up initial start state for each axis
|
||||
@@ -88,20 +85,19 @@ namespace AzToolsFramework
|
||||
{
|
||||
// note: m_localTransform must not be made uniform as it may contain a local scale we want to snap
|
||||
const auto linearStart = CalculateLinearManipulationDataStart(
|
||||
fixed, worldFromLocalUniformScale, GetNonUniformScale(), GetLocalTransform(),
|
||||
GridSnapAction(gridSnapParams, interaction.m_keyboardModifiers.Alt()), interaction,
|
||||
rayIntersectionDistance, cameraState);
|
||||
fixed, worldFromLocalUniformScale, GetNonUniformScale(), GetLocalTransform(), interaction, rayIntersectionDistance,
|
||||
cameraState);
|
||||
|
||||
m_starters.push_back(linearStart);
|
||||
}
|
||||
|
||||
if (m_onLeftMouseDownCallback)
|
||||
{
|
||||
const GridSnapAction gridSnapAction = GridSnapAction(gridSnapParams, interaction.m_keyboardModifiers.Alt());
|
||||
const GridSnapParameters gridSnapParams = GridSnapSettings(interaction.m_interactionId.m_viewportId);
|
||||
// pass action containing all linear actions for each axis to handler
|
||||
m_onLeftMouseDownCallback(BuildMultiLinearManipulatorAction(
|
||||
worldFromLocalUniformScale, GetNonUniformScale(), GetLocalTransform(),
|
||||
interaction, m_fixedAxes, m_starters, gridSnapAction));
|
||||
interaction, m_fixedAxes, m_starters, gridSnapParams));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,11 +107,9 @@ namespace AzToolsFramework
|
||||
{
|
||||
const AZ::Transform worldFromLocalUniformScale = TransformUniformScale(GetSpace());
|
||||
const GridSnapParameters gridSnapParams = GridSnapSettings(interaction.m_interactionId.m_viewportId);
|
||||
const GridSnapAction gridSnapAction = GridSnapAction(gridSnapParams, interaction.m_keyboardModifiers.Alt());
|
||||
|
||||
m_onMouseMoveCallback(BuildMultiLinearManipulatorAction(
|
||||
worldFromLocalUniformScale, GetNonUniformScale(), GetLocalTransform(),
|
||||
interaction, m_fixedAxes, m_starters, gridSnapAction));
|
||||
interaction, m_fixedAxes, m_starters, gridSnapParams));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,11 +119,9 @@ namespace AzToolsFramework
|
||||
{
|
||||
const AZ::Transform worldFromLocalUniformScale = TransformUniformScale(GetSpace());
|
||||
const GridSnapParameters gridSnapParams = GridSnapSettings(interaction.m_interactionId.m_viewportId);
|
||||
const GridSnapAction gridSnapAction = GridSnapAction(gridSnapParams, interaction.m_keyboardModifiers.Alt());
|
||||
|
||||
m_onLeftMouseUpCallback(BuildMultiLinearManipulatorAction(
|
||||
worldFromLocalUniformScale, GetNonUniformScale(), GetLocalTransform(),
|
||||
interaction, m_fixedAxes, m_starters, gridSnapAction));
|
||||
interaction, m_fixedAxes, m_starters, gridSnapParams));
|
||||
|
||||
m_starters.clear();
|
||||
}
|
||||
|
||||
@@ -20,8 +20,6 @@
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
struct GridSnapAction;
|
||||
|
||||
//! MultiLinearManipulator serves as a visual tool for users to modify values
|
||||
//! in one or more dimensions on axes defined in 3D space.
|
||||
class MultiLinearManipulator
|
||||
|
||||
+15
-35
@@ -22,8 +22,7 @@
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
PlanarManipulator::StartInternal PlanarManipulator::CalculateManipulationDataStart(
|
||||
const Fixed& fixed, const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale,
|
||||
const AZ::Transform& localTransform, const GridSnapAction& gridSnapAction,
|
||||
const Fixed& fixed, const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale, const AZ::Transform& localTransform,
|
||||
const ViewportInteraction::MouseInteraction& interaction, const float intersectionDistance)
|
||||
{
|
||||
const ManipulatorInteraction manipulatorInteraction =
|
||||
@@ -31,8 +30,6 @@ namespace AzToolsFramework
|
||||
worldFromLocal, nonUniformScale, interaction.m_mousePick.m_rayOrigin, interaction.m_mousePick.m_rayDirection);
|
||||
|
||||
const AZ::Vector3 normal = TransformDirectionNoScaling(localTransform, fixed.m_normal);
|
||||
const AZ::Vector3 axis1 = TransformDirectionNoScaling(localTransform, fixed.m_axis1);
|
||||
const AZ::Vector3 axis2 = TransformDirectionNoScaling(localTransform, fixed.m_axis2);
|
||||
|
||||
// initial intersect point
|
||||
const AZ::Vector3 localIntersectionPoint =
|
||||
@@ -43,25 +40,14 @@ namespace AzToolsFramework
|
||||
manipulatorInteraction.m_localRayOrigin, manipulatorInteraction.m_localRayDirection,
|
||||
localIntersectionPoint, normal, startInternal.m_localHitPosition);
|
||||
|
||||
const float scaleRecip = manipulatorInteraction.m_scaleReciprocal;
|
||||
const float gridSize = gridSnapAction.m_gridSnapParams.m_gridSize;
|
||||
const bool snapping = gridSnapAction.m_gridSnapParams.m_gridSnap;
|
||||
|
||||
// calculate amount to snap to align with grid
|
||||
const AZ::Vector3 snapOffset = snapping && !gridSnapAction.m_localSnapping
|
||||
? CalculateSnappedOffset(localTransform.GetTranslation(), axis1, gridSize * scaleRecip) +
|
||||
CalculateSnappedOffset(localTransform.GetTranslation(), axis2, gridSize * scaleRecip)
|
||||
: AZ::Vector3::CreateZero();
|
||||
|
||||
startInternal.m_snapOffset = snapOffset;
|
||||
startInternal.m_localPosition = localTransform.GetTranslation() + snapOffset;
|
||||
startInternal.m_localPosition = localTransform.GetTranslation();
|
||||
|
||||
return startInternal;
|
||||
}
|
||||
|
||||
PlanarManipulator::Action PlanarManipulator::CalculateManipulationDataAction(
|
||||
const Fixed& fixed, const StartInternal& startInternal, const AZ::Transform& worldFromLocal,
|
||||
const AZ::Vector3& nonUniformScale, const AZ::Transform& localTransform, const GridSnapAction& gridSnapAction,
|
||||
const Fixed& fixed, const StartInternal& startInternal, const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale,
|
||||
const AZ::Transform& localTransform, const GridSnapParameters& gridSnapParams,
|
||||
const ViewportInteraction::MouseInteraction& interaction)
|
||||
{
|
||||
const ManipulatorInteraction manipulatorInteraction =
|
||||
@@ -88,20 +74,18 @@ namespace AzToolsFramework
|
||||
const AZ::Vector3 hitDelta = (localHitPosition - startInternal.m_localHitPosition) / nonUniformScale;
|
||||
const AZ::Vector3 unsnappedOffset = axis1.Dot(hitDelta) * axis1 + axis2.Dot(hitDelta) * axis2;
|
||||
|
||||
const float scaleRecip = manipulatorInteraction.m_scaleReciprocal;
|
||||
const AZ::Vector3 nonUniformScaleRecip = manipulatorInteraction.m_nonUniformScaleReciprocal;
|
||||
const float gridSize = gridSnapAction.m_gridSnapParams.m_gridSize;
|
||||
const bool snapping = gridSnapAction.m_gridSnapParams.m_gridSnap;
|
||||
const float scaleRecip = manipulatorInteraction.m_scaleReciprocal;
|
||||
const float gridSize = gridSnapParams.m_gridSize;
|
||||
const bool snapping = gridSnapParams.m_gridSnap;
|
||||
|
||||
Action action;
|
||||
action.m_fixed = fixed;
|
||||
action.m_start.m_localPosition = startInternal.m_localPosition;
|
||||
action.m_start.m_snapOffset = startInternal.m_snapOffset;
|
||||
action.m_start.m_localHitPosition = startInternal.m_localHitPosition;
|
||||
action.m_current.m_localOffset = snapping
|
||||
? unsnappedOffset +
|
||||
CalculateSnappedOffset(unsnappedOffset, axis1, gridSize * scaleRecip * nonUniformScaleRecip.Dot(axis1)) +
|
||||
CalculateSnappedOffset(unsnappedOffset, axis2, gridSize * scaleRecip * nonUniformScaleRecip.Dot(axis2))
|
||||
? CalculateSnappedAmount(unsnappedOffset, axis1, gridSize * scaleRecip * nonUniformScaleRecip.Dot(fixed.m_axis1)) +
|
||||
CalculateSnappedAmount(unsnappedOffset, axis2, gridSize * scaleRecip * nonUniformScaleRecip.Dot(fixed.m_axis2))
|
||||
: unsnappedOffset;
|
||||
|
||||
// record what modifier keys are held during this action
|
||||
@@ -141,18 +125,17 @@ namespace AzToolsFramework
|
||||
{
|
||||
const AZ::Transform worldFromLocalUniformScale = TransformUniformScale(GetSpace());
|
||||
|
||||
const GridSnapParameters gridSnapParams = GridSnapSettings(interaction.m_interactionId.m_viewportId);
|
||||
|
||||
m_startInternal = CalculateManipulationDataStart(
|
||||
m_fixed, worldFromLocalUniformScale, GetNonUniformScale(), TransformNormalizedScale(GetLocalTransform()),
|
||||
GridSnapAction(gridSnapParams, interaction.m_keyboardModifiers.Alt()),
|
||||
interaction, rayIntersectionDistance);
|
||||
|
||||
if (m_onLeftMouseDownCallback)
|
||||
{
|
||||
const GridSnapParameters gridSnapParams = GridSnapSettings(interaction.m_interactionId.m_viewportId);
|
||||
|
||||
m_onLeftMouseDownCallback(CalculateManipulationDataAction(
|
||||
m_fixed, m_startInternal, worldFromLocalUniformScale, GetNonUniformScale(), TransformNormalizedScale(GetLocalTransform()),
|
||||
GridSnapAction(gridSnapParams, interaction.m_keyboardModifiers.Alt()), interaction));
|
||||
gridSnapParams, interaction));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,8 +147,7 @@ namespace AzToolsFramework
|
||||
|
||||
m_onMouseMoveCallback(CalculateManipulationDataAction(
|
||||
m_fixed, m_startInternal, TransformUniformScale(GetSpace()), GetNonUniformScale(),
|
||||
TransformNormalizedScale(GetLocalTransform()),
|
||||
GridSnapAction(gridSnapParams, interaction.m_keyboardModifiers.Alt()), interaction));
|
||||
TransformNormalizedScale(GetLocalTransform()), gridSnapParams, interaction));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,8 +159,7 @@ namespace AzToolsFramework
|
||||
|
||||
m_onLeftMouseUpCallback(CalculateManipulationDataAction(
|
||||
m_fixed, m_startInternal, TransformUniformScale(GetSpace()), GetNonUniformScale(),
|
||||
TransformNormalizedScale(GetLocalTransform()),
|
||||
GridSnapAction(gridSnapParams, interaction.m_keyboardModifiers.Alt()), interaction));
|
||||
TransformNormalizedScale(GetLocalTransform()), gridSnapParams, interaction));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,8 +176,7 @@ namespace AzToolsFramework
|
||||
const GridSnapParameters gridSnapParams = GridSnapSettings(mouseInteraction.m_interactionId.m_viewportId);
|
||||
const auto action = CalculateManipulationDataAction(
|
||||
m_fixed, m_startInternal, TransformUniformScale(GetSpace()), GetNonUniformScale(),
|
||||
TransformNormalizedScale(GetLocalTransform()),
|
||||
GridSnapAction(gridSnapParams, mouseInteraction.m_keyboardModifiers.Alt()), mouseInteraction);
|
||||
TransformNormalizedScale(GetLocalTransform()), gridSnapParams, mouseInteraction);
|
||||
|
||||
// display the exact hit (ray intersection) of the mouse pick on the manipulator
|
||||
DrawTransformAxes(
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
class ManipulatorView;
|
||||
struct GridSnapAction;
|
||||
struct GridSnapParameters;
|
||||
|
||||
/// PlanarManipulator serves as a visual tool for users to modify values
|
||||
/// in two dimension in a plane defined two non-collinear axes in 3D space.
|
||||
@@ -58,7 +58,6 @@ namespace AzToolsFramework
|
||||
{
|
||||
AZ::Vector3 m_localPosition; ///< The current position of the manipulator in local space.
|
||||
AZ::Vector3 m_localHitPosition; ///< The intersection point in local space between the ray and the manipulator when the mouse down event happens.
|
||||
AZ::Vector3 m_snapOffset; ///< The snap offset amount to ensure manipulator is aligned to the grid.
|
||||
};
|
||||
|
||||
/// The state of the manipulator during an interaction.
|
||||
@@ -120,7 +119,6 @@ namespace AzToolsFramework
|
||||
{
|
||||
AZ::Vector3 m_localPosition; ///< The starting position of the manipulator in local space.
|
||||
AZ::Vector3 m_localHitPosition; ///< The intersection point in world space between the ray and the manipulator when the mouse down event happens.
|
||||
AZ::Vector3 m_snapOffset; ///< The snap offset amount to ensure manipulator is aligned to the grid.
|
||||
};
|
||||
|
||||
Fixed m_fixed;
|
||||
@@ -134,12 +132,11 @@ namespace AzToolsFramework
|
||||
|
||||
static StartInternal CalculateManipulationDataStart(
|
||||
const Fixed& fixed, const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale,
|
||||
const AZ::Transform& localTransform, const GridSnapAction& gridSnapAction,
|
||||
const ViewportInteraction::MouseInteraction& interaction, float intersectionDistance);
|
||||
const AZ::Transform& localTransform, const ViewportInteraction::MouseInteraction& interaction, float intersectionDistance);
|
||||
|
||||
static Action CalculateManipulationDataAction(
|
||||
const Fixed& fixed, const StartInternal& startInternal, const AZ::Transform& worldFromLocal,
|
||||
const AZ::Vector3& nonUniformScale, const AZ::Transform& localTransform,
|
||||
const GridSnapAction& gridSnapAction, const ViewportInteraction::MouseInteraction& interaction);
|
||||
const Fixed& fixed, const StartInternal& startInternal, const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale,
|
||||
const AZ::Transform& localTransform, const GridSnapParameters& gridSnapParams,
|
||||
const ViewportInteraction::MouseInteraction& interaction);
|
||||
};
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/Instance.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabDomUtils.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/InstanceEntityMapperInterface.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/TemplateInstanceMapperInterface.h>
|
||||
|
||||
@@ -49,8 +50,7 @@ namespace AzToolsFramework
|
||||
m_alias = GenerateInstanceAlias();
|
||||
m_containerEntity = containerEntity ? AZStd::move(containerEntity)
|
||||
: AZStd::make_unique<AZ::Entity>();
|
||||
EntityAlias containerEntityAlias = GenerateEntityAlias();
|
||||
RegisterEntity(m_containerEntity->GetId(), containerEntityAlias);
|
||||
RegisterEntity(m_containerEntity->GetId(), PrefabDomUtils::ContainerEntityName);
|
||||
}
|
||||
|
||||
Instance::~Instance()
|
||||
@@ -311,8 +311,15 @@ namespace AzToolsFramework
|
||||
Instance& Instance::AddInstance(AZStd::unique_ptr<Instance> instance)
|
||||
{
|
||||
InstanceAlias newInstanceAlias = GenerateInstanceAlias();
|
||||
return AddInstance(AZStd::move(instance), newInstanceAlias);
|
||||
}
|
||||
|
||||
Instance& Instance::AddInstance(AZStd::unique_ptr<Instance> instance, InstanceAlias newInstanceAlias)
|
||||
{
|
||||
AZ_Assert(instance.get(), "instance argument is nullptr");
|
||||
AZ_Assert(m_nestedInstances.find(newInstanceAlias) == m_nestedInstances.end(), "InstanceAlias' unique id collision, this should never happen.");
|
||||
AZ_Assert(
|
||||
m_nestedInstances.find(newInstanceAlias) == m_nestedInstances.end(),
|
||||
"InstanceAlias' unique id collision, this should never happen.");
|
||||
instance->m_parent = this;
|
||||
instance->m_alias = newInstanceAlias;
|
||||
return *(m_nestedInstances[newInstanceAlias] = std::move(instance));
|
||||
|
||||
@@ -93,6 +93,7 @@ namespace AzToolsFramework
|
||||
void Reset();
|
||||
|
||||
Instance& AddInstance(AZStd::unique_ptr<Instance> instance);
|
||||
Instance& AddInstance(AZStd::unique_ptr<Instance> instance, InstanceAlias instanceAlias);
|
||||
AZStd::unique_ptr<Instance> DetachNestedInstance(const InstanceAlias& instanceAlias);
|
||||
|
||||
/**
|
||||
@@ -173,6 +174,8 @@ namespace AzToolsFramework
|
||||
static EntityAlias GenerateEntityAlias();
|
||||
AliasPath GetAbsoluteInstanceAliasPath() const;
|
||||
|
||||
static InstanceAlias GenerateInstanceAlias();
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Gets the entities owned by this instance
|
||||
@@ -189,8 +192,6 @@ namespace AzToolsFramework
|
||||
bool RegisterEntity(const AZ::EntityId& entityId, const EntityAlias& entityAlias);
|
||||
AZStd::unique_ptr<AZ::Entity> DetachEntity(const EntityAlias& entityAlias);
|
||||
|
||||
static InstanceAlias GenerateInstanceAlias();
|
||||
|
||||
// Provide access to private data members in the serializer
|
||||
friend class JsonInstanceSerializer;
|
||||
friend class InstanceEntityIdMapper;
|
||||
|
||||
+24
@@ -152,6 +152,30 @@ namespace AzToolsFramework
|
||||
Instance::EntityList newEntities;
|
||||
if (PrefabDomUtils::LoadInstanceFromPrefabDom(*instanceToUpdate, newEntities, currentTemplate.GetPrefabDom()))
|
||||
{
|
||||
// If a link was created for a nested instance before the changes were propagated,
|
||||
// then we associate it correctly here
|
||||
instanceToUpdate->GetNestedInstances([&](AZStd::unique_ptr<Instance>& nestedInstance) {
|
||||
if (nestedInstance->GetLinkId() != InvalidLinkId)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto linkId : currentTemplate.GetLinks())
|
||||
{
|
||||
LinkReference nestedLink = m_prefabSystemComponentInterface->FindLink(linkId);
|
||||
if (!nestedLink.has_value())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (nestedLink->get().GetInstanceName() == nestedInstance->GetInstanceAlias())
|
||||
{
|
||||
nestedInstance->SetLinkId(linkId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
AzToolsFramework::EditorEntityContextRequestBus::Broadcast(
|
||||
&AzToolsFramework::EditorEntityContextRequests::HandleEntitiesAdded, newEntities);
|
||||
}
|
||||
|
||||
@@ -297,7 +297,7 @@ namespace AzToolsFramework
|
||||
m_instanceToTemplateInterface->AppendEntityAliasToPatchPaths(patch, containerEntityId);
|
||||
|
||||
// Update the cache - this prevents these changes from being stored in the regular undo/redo nodes
|
||||
m_prefabUndoCache.Store(containerEntityId, AZStd::move(containerEntityDomAfter));
|
||||
m_prefabUndoCache.Store(containerEntityId, AZStd::move(containerEntityDomAfter), parentEntityId);
|
||||
|
||||
return AZStd::move(patch);
|
||||
}
|
||||
@@ -595,54 +595,199 @@ namespace AzToolsFramework
|
||||
{
|
||||
// Create Undo node on entities if they belong to an instance
|
||||
InstanceOptionalReference owningInstance = m_instanceEntityMapperInterface->FindOwningInstance(entityId);
|
||||
|
||||
if (owningInstance.has_value())
|
||||
if (!owningInstance.has_value())
|
||||
{
|
||||
PrefabDom afterState;
|
||||
AZ::Entity* entity = GetEntityById(entityId);
|
||||
if (entity)
|
||||
return;
|
||||
}
|
||||
|
||||
AZ::Entity* entity = GetEntityById(entityId);
|
||||
if (!entity)
|
||||
{
|
||||
m_prefabUndoCache.PurgeCache(entityId);
|
||||
return;
|
||||
}
|
||||
|
||||
PrefabDom beforeState;
|
||||
AZ::EntityId beforeParentId;
|
||||
m_prefabUndoCache.Retrieve(entityId, beforeState, beforeParentId);
|
||||
|
||||
PrefabDom afterState;
|
||||
AZ::EntityId afterParentId;
|
||||
AZ::TransformBus::EventResult(afterParentId, entityId, &AZ::TransformBus::Events::GetParentId);
|
||||
|
||||
m_instanceToTemplateInterface->GenerateDomForEntity(afterState, *entity);
|
||||
|
||||
PrefabDom patch;
|
||||
m_instanceToTemplateInterface->GeneratePatch(patch, beforeState, afterState);
|
||||
m_instanceToTemplateInterface->AppendEntityAliasToPatchPaths(patch, entityId);
|
||||
|
||||
if (patch.IsArray() && !patch.Empty() && beforeState.IsObject())
|
||||
{
|
||||
bool isInstanceContainerEntity = IsInstanceContainerEntity(entityId) && !IsLevelInstanceContainerEntity(entityId);
|
||||
bool isNewParentOwnedByDifferentInstance = false;
|
||||
|
||||
if (beforeParentId != afterParentId)
|
||||
{
|
||||
PrefabDom beforeState;
|
||||
m_prefabUndoCache.Retrieve(entityId, beforeState);
|
||||
// If the entity parent changed, verify if the owning instance changed too
|
||||
InstanceOptionalReference beforeOwningInstance = m_instanceEntityMapperInterface->FindOwningInstance(beforeParentId);
|
||||
InstanceOptionalReference afterOwningInstance = m_instanceEntityMapperInterface->FindOwningInstance(afterParentId);
|
||||
|
||||
m_instanceToTemplateInterface->GenerateDomForEntity(afterState, *entity);
|
||||
|
||||
PrefabDom patch;
|
||||
m_instanceToTemplateInterface->GeneratePatch(patch, beforeState, afterState);
|
||||
|
||||
if (patch.IsArray() && !patch.Empty() && beforeState.IsObject())
|
||||
if (beforeOwningInstance.has_value() && afterOwningInstance.has_value() &&
|
||||
(&beforeOwningInstance->get() != &afterOwningInstance->get()))
|
||||
{
|
||||
if (IsInstanceContainerEntity(entityId) && !IsLevelInstanceContainerEntity(entityId))
|
||||
{
|
||||
m_instanceToTemplateInterface->AppendEntityAliasToPatchPaths(patch, entityId);
|
||||
|
||||
// Save these changes as patches to the link
|
||||
PrefabUndoLinkUpdate* linkUpdate =
|
||||
aznew PrefabUndoLinkUpdate(AZStd::to_string(static_cast<AZ::u64>(entityId)));
|
||||
linkUpdate->SetParent(parentUndoBatch);
|
||||
linkUpdate->Capture(patch, owningInstance->get().GetLinkId());
|
||||
|
||||
linkUpdate->Redo();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Update the state of the entity
|
||||
PrefabUndoEntityUpdate* state = aznew PrefabUndoEntityUpdate(AZStd::to_string(static_cast<AZ::u64>(entityId)));
|
||||
state->SetParent(parentUndoBatch);
|
||||
state->Capture(beforeState, afterState, entityId);
|
||||
|
||||
state->Redo();
|
||||
}
|
||||
isNewParentOwnedByDifferentInstance = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Update the cache
|
||||
m_prefabUndoCache.Store(entityId, AZStd::move(afterState));
|
||||
if (isInstanceContainerEntity)
|
||||
{
|
||||
if (isNewParentOwnedByDifferentInstance)
|
||||
{
|
||||
Internal_HandleInstanceChange(parentUndoBatch, entity, beforeParentId, afterParentId);
|
||||
|
||||
PrefabDom afterStateafterReparenting;
|
||||
m_instanceToTemplateInterface->GenerateDomForEntity(afterStateafterReparenting, *entity);
|
||||
|
||||
PrefabDom newPatch;
|
||||
m_instanceToTemplateInterface->GeneratePatch(newPatch, afterState, afterStateafterReparenting);
|
||||
m_instanceToTemplateInterface->AppendEntityAliasToPatchPaths(newPatch, entityId);
|
||||
|
||||
InstanceOptionalReference owningInstanceAfterReparenting =
|
||||
m_instanceEntityMapperInterface->FindOwningInstance(entityId);
|
||||
|
||||
Internal_HandleContainerOverride(
|
||||
parentUndoBatch, entityId, newPatch, owningInstanceAfterReparenting->get().GetLinkId());
|
||||
}
|
||||
else
|
||||
{
|
||||
Internal_HandleContainerOverride(
|
||||
parentUndoBatch, entityId, patch, owningInstance->get().GetLinkId());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_prefabUndoCache.PurgeCache(entityId);
|
||||
Internal_HandleEntityChange(parentUndoBatch, entityId, beforeState, afterState);
|
||||
|
||||
if (isNewParentOwnedByDifferentInstance)
|
||||
{
|
||||
Internal_HandleInstanceChange(parentUndoBatch, entity, beforeParentId, afterParentId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_prefabUndoCache.UpdateCache(entityId);
|
||||
}
|
||||
|
||||
void PrefabPublicHandler::Internal_HandleContainerOverride(
|
||||
UndoSystem::URSequencePoint* undoBatch, AZ::EntityId entityId, const PrefabDom& patch, const LinkId linkId)
|
||||
{
|
||||
// Save these changes as patches to the link
|
||||
PrefabUndoLinkUpdate* linkUpdate = aznew PrefabUndoLinkUpdate(AZStd::to_string(static_cast<AZ::u64>(entityId)));
|
||||
linkUpdate->SetParent(undoBatch);
|
||||
linkUpdate->Capture(patch, linkId);
|
||||
|
||||
linkUpdate->Redo();
|
||||
}
|
||||
|
||||
void PrefabPublicHandler::Internal_HandleEntityChange(
|
||||
UndoSystem::URSequencePoint* undoBatch, AZ::EntityId entityId, PrefabDom& beforeState, PrefabDom& afterState)
|
||||
{
|
||||
// Update the state of the entity
|
||||
PrefabUndoEntityUpdate* state = aznew PrefabUndoEntityUpdate(AZStd::to_string(static_cast<AZ::u64>(entityId)));
|
||||
state->SetParent(undoBatch);
|
||||
state->Capture(beforeState, afterState, entityId);
|
||||
|
||||
state->Redo();
|
||||
}
|
||||
|
||||
void PrefabPublicHandler::Internal_HandleInstanceChange(
|
||||
UndoSystem::URSequencePoint* undoBatch, AZ::Entity* entity, AZ::EntityId beforeParentId, AZ::EntityId afterParentId)
|
||||
{
|
||||
// If the entity parent changed, verify if the owning instance changed too
|
||||
InstanceOptionalReference beforeOwningInstance = m_instanceEntityMapperInterface->FindOwningInstance(beforeParentId);
|
||||
InstanceOptionalReference afterOwningInstance = m_instanceEntityMapperInterface->FindOwningInstance(afterParentId);
|
||||
|
||||
EntityList entities;
|
||||
AZStd::vector<Instance*> instances;
|
||||
|
||||
// Retrieve all descendant entities and instances of this entity that belonged to the same owning instance.
|
||||
RetrieveAndSortPrefabEntitiesAndInstances({ entity }, beforeOwningInstance->get(), entities, instances);
|
||||
|
||||
AZStd::vector<AZStd::unique_ptr<Instance>> instanceUniquePtrs;
|
||||
AZStd::vector<AZStd::pair<Instance*, PrefabDom>> instancePatches;
|
||||
|
||||
// Remove Entities and Instances from the prior instance
|
||||
{
|
||||
// Remove Instances
|
||||
for (Instance* nestedInstance : instances)
|
||||
{
|
||||
auto linkRef = m_prefabSystemComponentInterface->FindLink(nestedInstance->GetLinkId());
|
||||
|
||||
PrefabDom oldLinkPatches;
|
||||
|
||||
if (linkRef.has_value())
|
||||
{
|
||||
auto patches = linkRef->get().GetLinkPatches();
|
||||
if (patches.has_value())
|
||||
{
|
||||
oldLinkPatches.CopyFrom(patches->get(), oldLinkPatches.GetAllocator());
|
||||
}
|
||||
}
|
||||
|
||||
auto nestedInstanceUniquePtr = beforeOwningInstance->get().DetachNestedInstance(nestedInstance->GetInstanceAlias());
|
||||
RemoveLink(nestedInstanceUniquePtr, beforeOwningInstance->get().GetTemplateId(), undoBatch);
|
||||
|
||||
instancePatches.emplace_back(AZStd::make_pair(nestedInstanceUniquePtr.get(), AZStd::move(oldLinkPatches)));
|
||||
instanceUniquePtrs.emplace_back(AZStd::move(nestedInstanceUniquePtr));
|
||||
}
|
||||
|
||||
// Get the previous state of the prior instance for undo/redo purposes
|
||||
PrefabDom beforeInstanceDomBeforeRemoval;
|
||||
m_instanceToTemplateInterface->GenerateDomForInstance(beforeInstanceDomBeforeRemoval, beforeOwningInstance->get());
|
||||
|
||||
// Remove Entities
|
||||
for (AZ::Entity* nestedEntity : entities)
|
||||
{
|
||||
beforeOwningInstance->get().DetachEntity(nestedEntity->GetId()).release();
|
||||
}
|
||||
|
||||
// Create the Update node for the prior owning instance
|
||||
// Instance removal will be taken care of from the RemoveLink function for undo/redo purposes
|
||||
PrefabUndoHelpers::UpdatePrefabInstance(
|
||||
beforeOwningInstance->get(), "Update prior prefab instance", beforeInstanceDomBeforeRemoval, undoBatch);
|
||||
}
|
||||
|
||||
// Add Entities and Instances to new instance
|
||||
{
|
||||
// Add Instances
|
||||
for (auto& instanceUniquePtr : instanceUniquePtrs)
|
||||
{
|
||||
afterOwningInstance->get().AddInstance(AZStd::move(instanceUniquePtr));
|
||||
}
|
||||
|
||||
// Create Links
|
||||
for (auto& instanceInfo : instancePatches)
|
||||
{
|
||||
// Add a new link with the old dom
|
||||
CreateLink(
|
||||
*instanceInfo.first, afterOwningInstance->get().GetTemplateId(), undoBatch,
|
||||
AZStd::move(instanceInfo.second));
|
||||
}
|
||||
|
||||
// Get the previous state of the new instance for undo/redo purposes
|
||||
PrefabDom afterInstanceDomBeforeAdd;
|
||||
m_instanceToTemplateInterface->GenerateDomForInstance(afterInstanceDomBeforeAdd, afterOwningInstance->get());
|
||||
|
||||
// Add Entities
|
||||
for (AZ::Entity* nestedEntity : entities)
|
||||
{
|
||||
afterOwningInstance->get().AddEntity(*nestedEntity);
|
||||
}
|
||||
|
||||
// Create the Update node for the new owning instance
|
||||
PrefabUndoHelpers::UpdatePrefabInstance(
|
||||
afterOwningInstance->get(), "Update new prefab instance", afterInstanceDomBeforeAdd, undoBatch);
|
||||
}
|
||||
}
|
||||
|
||||
bool PrefabPublicHandler::IsInstanceContainerEntity(AZ::EntityId entityId) const
|
||||
@@ -754,16 +899,29 @@ namespace AzToolsFramework
|
||||
|
||||
if (!EntitiesBelongToSameInstance(entityIds))
|
||||
{
|
||||
return AZ::Failure(AZStd::string("Cannot duplicate multiple "
|
||||
"entities belonging to different instances with one operation."));
|
||||
return AZ::Failure(AZStd::string("Cannot duplicate multiple entities belonging to different instances with one operation."
|
||||
"Change your selection to contain entities in the same instance."));
|
||||
}
|
||||
|
||||
// We've already verified the entities are all owned by the same instance,
|
||||
// so we can just retrieve our instance from the first entity in the list.
|
||||
InstanceOptionalReference commonEntityOwningInstance = GetOwnerInstanceByEntityId(entityIds[0]);
|
||||
AZ_Assert(
|
||||
commonEntityOwningInstance.has_value(),
|
||||
"Failed to duplicate : Couldn't get a valid owning instance for the common root entity of the entities provided");
|
||||
AZ::EntityId firstEntityIdToDuplicate = entityIds[0];
|
||||
InstanceOptionalReference commonOwningInstance = GetOwnerInstanceByEntityId(firstEntityIdToDuplicate);
|
||||
if (!commonOwningInstance.has_value())
|
||||
{
|
||||
return AZ::Failure(AZStd::string("Failed to duplicate : Couldn't get a valid owning instance for the common root entity of the entities provided."));
|
||||
}
|
||||
|
||||
// If the first entity id is a container entity id, then we need to mark its parent as the common owning instance because you
|
||||
// cannot duplicate an instance from itself.
|
||||
if (commonOwningInstance->get().GetContainerEntityId() == firstEntityIdToDuplicate)
|
||||
{
|
||||
commonOwningInstance = commonOwningInstance->get().GetParentInstance();
|
||||
}
|
||||
if (!commonOwningInstance.has_value())
|
||||
{
|
||||
return AZ::Failure(AZStd::string("Failed to duplicate : Couldn't get a valid owning instance for the common root entity of the entities provided."));
|
||||
}
|
||||
|
||||
// This will cull out any entities that have ancestors in the list, since we will end up duplicating
|
||||
// the full nested hierarchy with what is returned from RetrieveAndSortPrefabEntitiesAndInstances
|
||||
@@ -776,105 +934,63 @@ namespace AzToolsFramework
|
||||
{
|
||||
AZ_PROFILE_SCOPE(AZ::Debug::ProfileCategory::AzToolsFramework, "DuplicateEntitiesInInstance::UndoCaptureAndDuplicateEntities");
|
||||
|
||||
// Take a snapshot of the instance DOM before we manipulate it
|
||||
Prefab::PrefabDom instanceDomBefore;
|
||||
m_instanceToTemplateInterface->GenerateDomForInstance(instanceDomBefore, commonEntityOwningInstance->get());
|
||||
|
||||
AZStd::vector<AZ::Entity*> entities;
|
||||
AZStd::vector<Instance*> instances;
|
||||
|
||||
// Gather all entities/instances in the hierarchy, but don't detach them because we are duplicating not deleting.
|
||||
EntityList inputEntityList = EntityIdSetToEntityList(duplicationSet);
|
||||
bool success = RetrieveAndSortPrefabEntitiesAndInstances(inputEntityList, commonEntityOwningInstance->get(), entities, instances);
|
||||
bool success = RetrieveAndSortPrefabEntitiesAndInstances(inputEntityList, commonOwningInstance->get(), entities, instances);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
return AZ::Failure(AZStd::string("Failed to retrieve entities and instances from the given list of entity ids for duplication"));
|
||||
}
|
||||
|
||||
// Make a copy of our before instance DOM where we will add our duplicated entities
|
||||
Prefab::PrefabDom instanceDomAfter;
|
||||
// Take a snapshot of the instance DOM before we manipulate it
|
||||
PrefabDom instanceDomBefore;
|
||||
m_instanceToTemplateInterface->GenerateDomForInstance(instanceDomBefore, commonOwningInstance->get());
|
||||
|
||||
// Make a copy of our before instance DOM where we will add our duplicated entities and/or instances
|
||||
PrefabDom instanceDomAfter;
|
||||
instanceDomAfter.CopyFrom(instanceDomBefore, instanceDomAfter.GetAllocator());
|
||||
|
||||
AZStd::unordered_map<EntityAlias, EntityAlias> oldAliasToNewAliasMap;
|
||||
AZStd::unordered_map<EntityAlias, QString> aliasToEntityDomMap;
|
||||
EntityIdList duplicatedEntityAndInstanceIds;
|
||||
|
||||
for (AZ::Entity* entity : entities)
|
||||
{
|
||||
EntityAliasOptionalReference oldAliasRef = commonEntityOwningInstance->get().GetEntityAlias(entity->GetId());
|
||||
AZ_Assert(oldAliasRef.has_value(), "No alias found for Entity in the DOM");
|
||||
EntityAlias oldAlias = oldAliasRef.value();
|
||||
// Duplicate any nested entities and instances as requested
|
||||
AZStd::unordered_map<InstanceAlias, Instance*> newInstanceAliasToOldInstanceMap;
|
||||
DuplicateNestedEntitiesInInstance(commonOwningInstance->get(),
|
||||
entities, instanceDomAfter, duplicatedEntityAndInstanceIds);
|
||||
DuplicateNestedInstancesInInstance(commonOwningInstance->get(),
|
||||
instances, instanceDomAfter, duplicatedEntityAndInstanceIds,
|
||||
newInstanceAliasToOldInstanceMap);
|
||||
|
||||
// Give this the outer allocator so that the memory reference will be valid when
|
||||
// it gets used for AddMember
|
||||
Prefab::PrefabDom entityDomBefore(&instanceDomAfter.GetAllocator());
|
||||
m_instanceToTemplateInterface->GenerateDomForEntity(entityDomBefore, *entity);
|
||||
|
||||
// Keep track of the old alias <-> new alias mapping for this duplicated entity
|
||||
// so we can fixup references later
|
||||
EntityAlias newEntityAlias = Instance::GenerateEntityAlias();
|
||||
oldAliasToNewAliasMap.insert(AZStd::make_pair(oldAlias, newEntityAlias));
|
||||
|
||||
rapidjson::StringBuffer buffer;
|
||||
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
|
||||
entityDomBefore.Accept(writer);
|
||||
|
||||
// Store our duplicated Entity DOM with its new alias as a string
|
||||
// so that we can fixup entity alias references before adding it
|
||||
// to the Entities member of our instance DOM
|
||||
QString entityDomString(buffer.GetString());
|
||||
aliasToEntityDomMap.insert(AZStd::make_pair(newEntityAlias, entityDomString));
|
||||
}
|
||||
|
||||
auto entitiesIter = instanceDomAfter.FindMember(PrefabDomUtils::EntitiesName);
|
||||
AZ_Assert(entitiesIter != instanceDomAfter.MemberEnd(), "Instance DOM missing the Entities member.");
|
||||
|
||||
// Now that all the duplicated Entity DOMs have been created, we need to iterate
|
||||
// through them and replace any previous EntityAlias references with the new ones.
|
||||
// These are more than just parent entity references for nested entities, this will
|
||||
// also cover any EntityId references that were made in the components between them.
|
||||
for (auto aliasEntityPair : aliasToEntityDomMap)
|
||||
{
|
||||
EntityAlias newEntityAlias = aliasEntityPair.first;
|
||||
QString newEntityDomString = aliasEntityPair.second;
|
||||
|
||||
// Replace all of the old alias references with the new ones
|
||||
// We bookend the aliases with \" and also with a / as an extra precaution to prevent
|
||||
// inadvertently replacing a matching string vs. where an actual EntityId is expected
|
||||
// This will cover both cases where an alias could be used in a normal entity vs. an instance
|
||||
for (auto aliasMapIter : oldAliasToNewAliasMap)
|
||||
{
|
||||
ReplaceOldAliases(newEntityDomString, aliasMapIter.first, aliasMapIter.second);
|
||||
}
|
||||
|
||||
// Create the new Entity DOM from parsing the JSON string
|
||||
Prefab::PrefabDom entityDomAfter(&instanceDomAfter.GetAllocator());
|
||||
entityDomAfter.Parse(newEntityDomString.toUtf8().constData());
|
||||
|
||||
// Add the new Entity DOM to the Entities member of the instance
|
||||
rapidjson::Value aliasName(newEntityAlias.c_str(), newEntityAlias.length(), instanceDomAfter.GetAllocator());
|
||||
entitiesIter->value.AddMember(AZStd::move(aliasName), entityDomAfter, instanceDomAfter.GetAllocator());
|
||||
}
|
||||
|
||||
PrefabUndoInstance* command = aznew PrefabUndoInstance("Entity duplication");
|
||||
PrefabUndoInstance* command = aznew PrefabUndoInstance("Entity/Instance duplication");
|
||||
command->SetParent(undoBatch.GetUndoBatch());
|
||||
command->Capture(instanceDomBefore, instanceDomAfter, commonEntityOwningInstance->get().GetTemplateId());
|
||||
command->RunRedo();
|
||||
command->Capture(instanceDomBefore, instanceDomAfter, commonOwningInstance->get().GetTemplateId());
|
||||
command->Redo();
|
||||
|
||||
EntityIdList duplicatedEntityIds;
|
||||
for (auto aliasMapIter : oldAliasToNewAliasMap)
|
||||
// Create links for our duplicated instances (if any were duplicated)
|
||||
for (auto [newInstanceAlias, oldInstance] : newInstanceAliasToOldInstanceMap)
|
||||
{
|
||||
EntityAlias newEntityAlias = aliasMapIter.second;
|
||||
LinkId oldLinkId = oldInstance->GetLinkId();
|
||||
auto linkRef = m_prefabSystemComponentInterface->FindLink(oldLinkId);
|
||||
AZ_Assert(
|
||||
linkRef.has_value(), "Unable to find link with id '%llu' during instance duplication.",
|
||||
oldLinkId);
|
||||
|
||||
AliasPath absoluteEntityPath = commonEntityOwningInstance->get().GetAbsoluteInstanceAliasPath();
|
||||
absoluteEntityPath.Append(newEntityAlias);
|
||||
PrefabDomValueReference linkPatches = linkRef->get().GetLinkPatches();
|
||||
AZ_Assert(
|
||||
linkPatches.has_value(), "Link with id '%llu' is missing patches.",
|
||||
oldLinkId);
|
||||
|
||||
AZ::EntityId newEntityId = InstanceEntityIdMapper::GenerateEntityIdForAliasPath(absoluteEntityPath);
|
||||
duplicatedEntityIds.push_back(newEntityId);
|
||||
PrefabDom linkPatchesCopy;
|
||||
linkPatchesCopy.CopyFrom(linkPatches->get(), linkPatchesCopy.GetAllocator());
|
||||
|
||||
m_prefabSystemComponentInterface->CreateLink(
|
||||
commonOwningInstance->get().GetTemplateId(), oldInstance->GetTemplateId(), newInstanceAlias, linkPatchesCopy);
|
||||
}
|
||||
|
||||
// Select the duplicated entities
|
||||
auto selectionUndo = aznew SelectionCommand(duplicatedEntityIds, "Select Duplicated Entities");
|
||||
// Select the duplicated entities/instances
|
||||
auto selectionUndo = aznew SelectionCommand(duplicatedEntityAndInstanceIds, "Select Duplicated Entities/Instances");
|
||||
selectionUndo->SetParent(undoBatch.GetUndoBatch());
|
||||
ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequestBus::Events::RunRedoSeparately, selectionUndo);
|
||||
}
|
||||
@@ -1363,8 +1479,159 @@ namespace AzToolsFramework
|
||||
return true;
|
||||
}
|
||||
|
||||
void PrefabPublicHandler::DuplicateNestedEntitiesInInstance(Instance& commonOwningInstance,
|
||||
const AZStd::vector<AZ::Entity*>& entities, PrefabDom& domToAddDuplicatedEntitiesUnder,
|
||||
EntityIdList& duplicatedEntityIds)
|
||||
{
|
||||
if (entities.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AZStd::unordered_map<EntityAlias, EntityAlias> oldAliasToNewAliasMap;
|
||||
AZStd::unordered_map<EntityAlias, QString> aliasToEntityDomMap;
|
||||
|
||||
for (AZ::Entity* entity : entities)
|
||||
{
|
||||
EntityAliasOptionalReference oldAliasRef = commonOwningInstance.GetEntityAlias(entity->GetId());
|
||||
AZ_Assert(oldAliasRef.has_value(), "No alias found for Entity in the DOM");
|
||||
EntityAlias oldAlias = oldAliasRef.value();
|
||||
|
||||
// Give this the outer allocator so that the memory reference will be valid when
|
||||
// it gets used for AddMember
|
||||
PrefabDom entityDomBefore(&domToAddDuplicatedEntitiesUnder.GetAllocator());
|
||||
m_instanceToTemplateInterface->GenerateDomForEntity(entityDomBefore, *entity);
|
||||
|
||||
// Keep track of the old alias <-> new alias mapping for this duplicated entity
|
||||
// so we can fixup references later
|
||||
EntityAlias newEntityAlias = Instance::GenerateEntityAlias();
|
||||
oldAliasToNewAliasMap.emplace(oldAlias, newEntityAlias);
|
||||
|
||||
rapidjson::StringBuffer buffer;
|
||||
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
|
||||
entityDomBefore.Accept(writer);
|
||||
|
||||
// Store our duplicated Entity DOM with its new alias as a string
|
||||
// so that we can fixup entity alias references before adding it
|
||||
// to the Entities member of our instance DOM
|
||||
QString entityDomString(buffer.GetString());
|
||||
aliasToEntityDomMap.emplace(newEntityAlias, entityDomString);
|
||||
}
|
||||
|
||||
auto entitiesIter = domToAddDuplicatedEntitiesUnder.FindMember(PrefabDomUtils::EntitiesName);
|
||||
AZ_Assert(entitiesIter != domToAddDuplicatedEntitiesUnder.MemberEnd(), "Instance DOM missing the Entities member.");
|
||||
|
||||
// Now that all the duplicated Entity DOMs have been created, we need to iterate
|
||||
// through them and replace any previous EntityAlias references with the new ones.
|
||||
// These are more than just parent entity references for nested entities, this will
|
||||
// also cover any EntityId references that were made in the components between them.
|
||||
for (auto [newEntityAlias, newEntityDomString] : aliasToEntityDomMap)
|
||||
{
|
||||
// Replace all of the old alias references with the new ones
|
||||
for (auto [oldAlias, newAlias] : oldAliasToNewAliasMap)
|
||||
{
|
||||
ReplaceOldAliases(newEntityDomString, oldAlias, newAlias);
|
||||
}
|
||||
|
||||
// Create the new Entity DOM from parsing the JSON string
|
||||
PrefabDom entityDomAfter(&domToAddDuplicatedEntitiesUnder.GetAllocator());
|
||||
entityDomAfter.Parse(newEntityDomString.toUtf8().constData());
|
||||
|
||||
// Add the new Entity DOM to the Entities member of the instance
|
||||
rapidjson::Value aliasName(newEntityAlias.c_str(), newEntityAlias.length(), domToAddDuplicatedEntitiesUnder.GetAllocator());
|
||||
entitiesIter->value.AddMember(AZStd::move(aliasName), entityDomAfter, domToAddDuplicatedEntitiesUnder.GetAllocator());
|
||||
}
|
||||
|
||||
for (auto aliasMapIter : oldAliasToNewAliasMap)
|
||||
{
|
||||
EntityAlias newEntityAlias = aliasMapIter.second;
|
||||
|
||||
AliasPath absoluteEntityPath = commonOwningInstance.GetAbsoluteInstanceAliasPath();
|
||||
absoluteEntityPath.Append(newEntityAlias);
|
||||
|
||||
AZ::EntityId newEntityId = InstanceEntityIdMapper::GenerateEntityIdForAliasPath(absoluteEntityPath);
|
||||
duplicatedEntityIds.push_back(newEntityId);
|
||||
}
|
||||
}
|
||||
|
||||
void PrefabPublicHandler::DuplicateNestedInstancesInInstance(Instance& commonOwningInstance,
|
||||
const AZStd::vector<Instance*>& instances, PrefabDom& domToAddDuplicatedInstancesUnder,
|
||||
EntityIdList& duplicatedEntityIds, AZStd::unordered_map<InstanceAlias, Instance*>& newInstanceAliasToOldInstanceMap)
|
||||
{
|
||||
if (instances.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AZStd::unordered_map<InstanceAlias, InstanceAlias> oldInstanceAliasToNewInstanceAliasMap;
|
||||
AZStd::unordered_map<InstanceAlias, QString> aliasToInstanceDomMap;
|
||||
|
||||
for (auto instance : instances)
|
||||
{
|
||||
PrefabDom nestedInstanceDomBefore;
|
||||
m_instanceToTemplateInterface->GenerateDomForInstance(nestedInstanceDomBefore, *instance);
|
||||
|
||||
// Keep track of the old alias <-> new alias mapping for this duplicated instance
|
||||
// so we can fixup references later
|
||||
InstanceAlias oldAlias = instance->GetInstanceAlias();
|
||||
InstanceAlias newInstanceAlias = Instance::GenerateInstanceAlias();
|
||||
oldInstanceAliasToNewInstanceAliasMap.emplace(oldAlias, newInstanceAlias);
|
||||
|
||||
// Keep track of our new instance alias with the Instance it was duplicated from,
|
||||
// so that after all instances are duplicated, we can go back and create links for them
|
||||
newInstanceAliasToOldInstanceMap.emplace(newInstanceAlias, instance);
|
||||
|
||||
rapidjson::StringBuffer buffer;
|
||||
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
|
||||
nestedInstanceDomBefore.Accept(writer);
|
||||
|
||||
// Store our duplicated Instance DOM with its new alias as a string
|
||||
// so that we can fixup instance alias references before adding it
|
||||
// to the Instances member of our instance DOM
|
||||
QString instanceDomString(buffer.GetString());
|
||||
aliasToInstanceDomMap.emplace(newInstanceAlias, instanceDomString);
|
||||
}
|
||||
|
||||
auto instancesIter = domToAddDuplicatedInstancesUnder.FindMember(PrefabDomUtils::InstancesName);
|
||||
AZ_Assert(instancesIter != domToAddDuplicatedInstancesUnder.MemberEnd(), "Instance DOM missing the Instances member.");
|
||||
|
||||
// Now that all the duplicated Instance DOMs have been created, we need to iterate
|
||||
// through them and replace any previous InstanceAlias references with the new ones.
|
||||
for (auto [newInstanceAlias, newInstanceDomString]: aliasToInstanceDomMap)
|
||||
{
|
||||
// Replace all of the old alias references with the new ones
|
||||
for (auto [oldAlias, newAlias] : oldInstanceAliasToNewInstanceAliasMap)
|
||||
{
|
||||
ReplaceOldAliases(newInstanceDomString, oldAlias, newAlias);
|
||||
}
|
||||
|
||||
// Create the new Instance DOM from parsing the JSON string
|
||||
PrefabDom nestedInstanceDomAfter(&domToAddDuplicatedInstancesUnder.GetAllocator());
|
||||
nestedInstanceDomAfter.Parse(newInstanceDomString.toUtf8().constData());
|
||||
|
||||
// Add the new Instance DOM to the Instances member of the instance
|
||||
rapidjson::Value aliasName(newInstanceAlias.c_str(), newInstanceAlias.length(), domToAddDuplicatedInstancesUnder.GetAllocator());
|
||||
instancesIter->value.AddMember(AZStd::move(aliasName), nestedInstanceDomAfter, domToAddDuplicatedInstancesUnder.GetAllocator());
|
||||
}
|
||||
|
||||
for (auto aliasMapIter : oldInstanceAliasToNewInstanceAliasMap)
|
||||
{
|
||||
InstanceAlias newInstanceAlias = aliasMapIter.second;
|
||||
|
||||
AliasPath absoluteInstancePath = commonOwningInstance.GetAbsoluteInstanceAliasPath();
|
||||
absoluteInstancePath.Append(newInstanceAlias);
|
||||
|
||||
AZ::EntityId newEntityId = InstanceEntityIdMapper::GenerateEntityIdForAliasPath(absoluteInstancePath);
|
||||
duplicatedEntityIds.push_back(newEntityId);
|
||||
}
|
||||
}
|
||||
|
||||
void PrefabPublicHandler::ReplaceOldAliases(QString& stringToReplace, AZStd::string_view oldAlias, AZStd::string_view newAlias)
|
||||
{
|
||||
// Replace all of the old alias references with the new ones
|
||||
// We bookend the aliases with \" and also with a / as an extra precaution to prevent
|
||||
// inadvertently replacing a matching string vs. where an actual EntityId is expected
|
||||
// This will cover both cases where an alias could be used in a normal entity vs. an instance
|
||||
QString oldAliasQuotes = QString("\"%1\"").arg(oldAlias.data());
|
||||
QString newAliasQuotes = QString("\"%1\"").arg(newAlias.data());
|
||||
|
||||
|
||||
@@ -73,6 +73,33 @@ namespace AzToolsFramework
|
||||
|
||||
InstanceOptionalReference GetOwnerInstanceByEntityId(AZ::EntityId entityId) const;
|
||||
bool EntitiesBelongToSameInstance(const EntityIdList& entityIds) const;
|
||||
|
||||
/**
|
||||
* Duplicate a list of entities owned by a common owning instance by directly
|
||||
* copying/modifying their entries in the instance DOM
|
||||
*
|
||||
* \param commonOwningInstance The common owning instance of all the entities being duplicated.
|
||||
* \param entities The list of Entities that will be duplicated.
|
||||
* \param domToAddDuplicatedEntitiesUnder The DOM of the common owning instance where the duplicated
|
||||
* entity DOM values will be added to.
|
||||
* \param duplicatedEntityIds A list of EntityIds corresponding to the entities that were duplicated.
|
||||
*/
|
||||
void DuplicateNestedEntitiesInInstance(Instance& commonOwningInstance,
|
||||
const AZStd::vector<AZ::Entity*>& entities, PrefabDom& domToAddDuplicatedEntitiesUnder,
|
||||
EntityIdList& duplicatedEntityIds);
|
||||
/**
|
||||
* Duplicate a list of instances owned by a common owning instance by directly
|
||||
* copying/modifying their entries in the instance DOM
|
||||
*
|
||||
* \param commonOwningInstance The common owning instance of all the instances being duplicated.
|
||||
* \param entities The list of Instances that will be duplicated.
|
||||
* \param domToAddDuplicatedInstancesUnder The DOM of the common owning instance where the duplicated
|
||||
* instance DOM values will be added to.
|
||||
* \param duplicatedEntityIds A list of EntityIds corresponding to the instances that were duplicated.
|
||||
*/
|
||||
void DuplicateNestedInstancesInInstance(Instance& commonOwningInstance,
|
||||
const AZStd::vector<Instance*>& instances, PrefabDom& domToAddDuplicatedInstancesUnder,
|
||||
EntityIdList& duplicatedEntityIds, AZStd::unordered_map<InstanceAlias, Instance*>& newInstanceAliasToOldInstanceMap);
|
||||
|
||||
/**
|
||||
* Applies the correct transform changes to the container entity based on the parent and child entities provided, and returns an appropriate patch.
|
||||
@@ -90,8 +117,8 @@ namespace AzToolsFramework
|
||||
/**
|
||||
* Creates a link between the templates of an instance and its parent.
|
||||
*
|
||||
* \param sourceInstance The instance that corresponds to the source template of the link.
|
||||
* \param targetInstance The id of the target template.
|
||||
* \param sourceInstance The instance that corresponds to the source template of the link (child).
|
||||
* \param targetInstance The id of the target template (parent).
|
||||
* \param undoBatch The undo batch to set as parent for this create link action.
|
||||
* \param patch The patch to store in the newly created link dom.
|
||||
* \param isUndoRedoSupportNeeded The flag indicating whether the link should be created with undo/redo support or not.
|
||||
@@ -134,6 +161,12 @@ namespace AzToolsFramework
|
||||
bool IsCyclicalDependencyFound(
|
||||
InstanceOptionalConstReference instance, const AZStd::unordered_set<AZ::IO::Path>& templateSourcePaths);
|
||||
|
||||
static void Internal_HandleContainerOverride(
|
||||
UndoSystem::URSequencePoint* undoBatch, AZ::EntityId entityId, const PrefabDom& patch, const LinkId linkId);
|
||||
static void Internal_HandleEntityChange(
|
||||
UndoSystem::URSequencePoint* undoBatch, AZ::EntityId entityId, PrefabDom& beforeState, PrefabDom& afterState);
|
||||
void Internal_HandleInstanceChange(UndoSystem::URSequencePoint* undoBatch, AZ::Entity* entity, AZ::EntityId beforeParentId, AZ::EntityId afterParentId);
|
||||
|
||||
void UpdateLinkPatchesWithNewEntityAliases(
|
||||
PrefabDom& linkPatch,
|
||||
const AZStd::unordered_map<AZ::EntityId, AZStd::string>& oldEntityAliases,
|
||||
|
||||
@@ -706,14 +706,14 @@ namespace AzToolsFramework
|
||||
"Prefab - PrefabSystemComponent::RemoveLink - "
|
||||
"Failed to remove Link with Id '%llu' for Instance '%s' of source Template with Id '%llu' "
|
||||
"from TemplateToLinkIdsMap.",
|
||||
linkId, link.GetSourceTemplateId(), link.GetInstanceName().c_str());
|
||||
linkId, link.GetInstanceName().c_str(), link.GetSourceTemplateId());
|
||||
|
||||
result = RemoveLinkFromTargetTemplate(linkId, link);
|
||||
AZ_Assert(result,
|
||||
"Prefab - PrefabSystemComponent::RemoveLink - "
|
||||
"Failed to remove Link with Id '%llu' for Instance '%s' of source Template with Id '%llu' "
|
||||
"from target Template with Id '%llu'.",
|
||||
linkId, link.GetSourceTemplateId(), link.GetInstanceName().c_str(), link.GetTargetTemplateId());
|
||||
linkId, link.GetInstanceName().c_str(), link.GetSourceTemplateId(), link.GetTargetTemplateId());
|
||||
|
||||
m_linkIdMap.erase(linkId);
|
||||
|
||||
|
||||
@@ -73,14 +73,16 @@ namespace AzToolsFramework
|
||||
}
|
||||
|
||||
PrefabDom oldData;
|
||||
Retrieve(entityId, oldData);
|
||||
AZ::EntityId oldParentId;
|
||||
Retrieve(entityId, oldData, oldParentId);
|
||||
|
||||
UpdateCache(entityId);
|
||||
|
||||
PrefabDom newData;
|
||||
Retrieve(entityId, newData);
|
||||
AZ::EntityId newParentId;
|
||||
Retrieve(entityId, newData, newParentId);
|
||||
|
||||
if (newData != oldData)
|
||||
if (newData != oldData || oldParentId != newParentId)
|
||||
{
|
||||
// display a useful message
|
||||
AZ::Entity* entity = nullptr;
|
||||
@@ -106,7 +108,7 @@ namespace AzToolsFramework
|
||||
// Clear out newly generated data and
|
||||
// replace with original data to ensure debug mode has the same data as profile/release
|
||||
// in the event of the consistency check failing.
|
||||
m_entitySavedStates[entityId] = AZStd::move(oldData);
|
||||
m_entitySavedStates[entityId] = {AZStd::move(oldData), oldParentId};
|
||||
|
||||
#endif // ENABLE_UNDOCACHE_CONSISTENCY_CHECKS
|
||||
}
|
||||
@@ -140,10 +142,13 @@ namespace AzToolsFramework
|
||||
return;
|
||||
}
|
||||
|
||||
AZ::EntityId parentId;
|
||||
AZ::TransformBus::EventResult(parentId, entityId, &AZ::TransformBus::Events::GetParentId);
|
||||
|
||||
// Capture it
|
||||
PrefabDom entityDom;
|
||||
m_instanceToTemplateInterface->GenerateDomForEntity(entityDom, *entity);
|
||||
m_entitySavedStates.emplace(AZStd::make_pair(entityId, AZStd::move(entityDom)));
|
||||
m_entitySavedStates[entityId] = {AZStd::move(entityDom), parentId};
|
||||
|
||||
AZLOG("Prefab Undo", "Correctly updated cache for entity of id %llu (%s)", static_cast<AZ::u64>(entityId), entity->GetName().c_str());
|
||||
|
||||
@@ -155,7 +160,7 @@ namespace AzToolsFramework
|
||||
m_entitySavedStates.erase(entityId);
|
||||
}
|
||||
|
||||
bool PrefabUndoCache::Retrieve(const AZ::EntityId& entityId, PrefabDom& outDom)
|
||||
bool PrefabUndoCache::Retrieve(const AZ::EntityId& entityId, PrefabDom& outDom, AZ::EntityId& parentId)
|
||||
{
|
||||
auto it = m_entitySavedStates.find(entityId);
|
||||
|
||||
@@ -164,14 +169,15 @@ namespace AzToolsFramework
|
||||
return false;
|
||||
}
|
||||
|
||||
outDom = AZStd::move(m_entitySavedStates[entityId]);
|
||||
outDom = AZStd::move(m_entitySavedStates[entityId].dom);
|
||||
parentId = m_entitySavedStates[entityId].parentId;
|
||||
m_entitySavedStates.erase(entityId);
|
||||
return true;
|
||||
}
|
||||
|
||||
void PrefabUndoCache::Store(const AZ::EntityId& entityId, PrefabDom&& dom)
|
||||
void PrefabUndoCache::Store(const AZ::EntityId& entityId, PrefabDom&& dom, const AZ::EntityId& parentId)
|
||||
{
|
||||
m_entitySavedStates.emplace(AZStd::make_pair(entityId, AZStd::move(dom)));
|
||||
m_entitySavedStates[entityId] = {AZStd::move(dom), parentId};
|
||||
}
|
||||
|
||||
void PrefabUndoCache::Clear()
|
||||
|
||||
@@ -46,14 +46,19 @@ namespace AzToolsFramework
|
||||
void Validate(const AZ::EntityId& entityId) override;
|
||||
|
||||
// Retrieve the last known state for an entity
|
||||
bool Retrieve(const AZ::EntityId& entityId, PrefabDom& outDom);
|
||||
bool Retrieve(const AZ::EntityId& entityId, PrefabDom& outDom, AZ::EntityId& parentId);
|
||||
|
||||
// Store dom as the cached state of entityId
|
||||
void Store(const AZ::EntityId& entityId, PrefabDom&& dom);
|
||||
void Store(const AZ::EntityId& entityId, PrefabDom&& dom, const AZ::EntityId& parentId);
|
||||
|
||||
private:
|
||||
typedef AZStd::unordered_map<AZ::EntityId, PrefabDom> EntityDomMap;
|
||||
EntityDomMap m_entitySavedStates;
|
||||
struct PrefabUndoCacheItem
|
||||
{
|
||||
PrefabDom dom;
|
||||
AZ::EntityId parentId;
|
||||
};
|
||||
typedef AZStd::unordered_map<AZ::EntityId, PrefabUndoCacheItem> EntityCache;
|
||||
EntityCache m_entitySavedStates;
|
||||
|
||||
InstanceEntityMapperInterface* m_instanceEntityMapperInterface = nullptr;
|
||||
InstanceToTemplateInterface* m_instanceToTemplateInterface = nullptr;
|
||||
|
||||
+28
-20
@@ -37,13 +37,13 @@ namespace AzToolsFramework
|
||||
axisLength, AzFramework::ViewportColors::XAxisColor, AzFramework::ViewportColors::YAxisColor,
|
||||
AzFramework::ViewportColors::ZAxisColor);
|
||||
|
||||
auto mouseDownCallback = [this](const LinearManipulator::Action& action) {
|
||||
auto mouseDownCallback = [this]([[maybe_unused]] const LinearManipulator::Action& action)
|
||||
{
|
||||
AZ::Vector3 nonUniformScale = AZ::Vector3::CreateOne();
|
||||
|
||||
AZ::NonUniformScaleRequestBus::EventResult(
|
||||
nonUniformScale, m_entityComponentIdPair.GetEntityId(), &AZ::NonUniformScaleRequests::GetScale);
|
||||
|
||||
m_initialScale = nonUniformScale + action.m_start.m_scaleSnapOffset;
|
||||
m_initialScale = nonUniformScale;
|
||||
|
||||
AZ::NonUniformScaleRequestBus::Event(
|
||||
m_entityComponentIdPair.GetEntityId(), &AZ::NonUniformScaleRequests::SetScale, m_initialScale);
|
||||
@@ -51,29 +51,37 @@ namespace AzToolsFramework
|
||||
|
||||
m_manipulators->InstallAxisLeftMouseDownCallback(mouseDownCallback);
|
||||
|
||||
m_manipulators->InstallAxisMouseMoveCallback([this](const LinearManipulator::Action& action) {
|
||||
const AZ::Vector3 scaleMultiplier =
|
||||
(AZ::Vector3::CreateOne() + ((action.LocalScaleOffset() * action.m_start.m_sign) / m_initialScale));
|
||||
m_manipulators->InstallAxisMouseMoveCallback(
|
||||
[this](const LinearManipulator::Action& action)
|
||||
{
|
||||
const AZ::Vector3 scaleMultiplier =
|
||||
(AZ::Vector3::CreateOne() + ((action.LocalScaleOffset() * action.m_start.m_sign) / m_initialScale));
|
||||
|
||||
AZ::NonUniformScaleRequestBus::Event(
|
||||
m_entityComponentIdPair.GetEntityId(), &AZ::NonUniformScaleRequests::SetScale,
|
||||
(scaleMultiplier * m_initialScale).GetClamp(AZ::Vector3(AZ::MinTransformScale), AZ::Vector3(AZ::MaxTransformScale)));
|
||||
});
|
||||
AZ::NonUniformScaleRequestBus::Event(
|
||||
m_entityComponentIdPair.GetEntityId(), &AZ::NonUniformScaleRequests::SetScale,
|
||||
(scaleMultiplier * m_initialScale)
|
||||
.GetClamp(AZ::Vector3(AZ::MinTransformScale), AZ::Vector3(AZ::MaxTransformScale)));
|
||||
});
|
||||
|
||||
m_manipulators->InstallUniformLeftMouseDownCallback(mouseDownCallback);
|
||||
|
||||
m_manipulators->InstallUniformMouseMoveCallback([this](const LinearManipulator::Action& action) {
|
||||
const auto sumVectorElements = [](const AZ::Vector3& vec) { return vec.GetX() + vec.GetY() + vec.GetZ(); };
|
||||
m_manipulators->InstallUniformMouseMoveCallback(
|
||||
[this](const LinearManipulator::Action& action)
|
||||
{
|
||||
const auto sumVectorElements = [](const AZ::Vector3& vec)
|
||||
{
|
||||
return vec.GetX() + vec.GetY() + vec.GetZ();
|
||||
};
|
||||
|
||||
const float minScaleMultiplier = AZ::MinTransformScale / m_initialScale.GetMinElement();
|
||||
const float maxScaleMultiplier = AZ::MaxTransformScale / m_initialScale.GetMaxElement();
|
||||
const float scaleMultiplier = AZ::GetClamp(
|
||||
1.0f + sumVectorElements(action.m_start.m_sign * action.LocalScaleOffset() / m_initialScale), minScaleMultiplier,
|
||||
maxScaleMultiplier);
|
||||
const float minScaleMultiplier = AZ::MinTransformScale / m_initialScale.GetMinElement();
|
||||
const float maxScaleMultiplier = AZ::MaxTransformScale / m_initialScale.GetMaxElement();
|
||||
const float scaleMultiplier = AZ::GetClamp(
|
||||
1.0f + sumVectorElements(action.m_start.m_sign * action.LocalScaleOffset() / m_initialScale), minScaleMultiplier,
|
||||
maxScaleMultiplier);
|
||||
|
||||
AZ::NonUniformScaleRequestBus::Event(
|
||||
m_entityComponentIdPair.GetEntityId(), &AZ::NonUniformScaleRequests::SetScale, scaleMultiplier * m_initialScale);
|
||||
});
|
||||
AZ::NonUniformScaleRequestBus::Event(
|
||||
m_entityComponentIdPair.GetEntityId(), &AZ::NonUniformScaleRequests::SetScale, scaleMultiplier * m_initialScale);
|
||||
});
|
||||
}
|
||||
|
||||
NonUniformScaleComponentMode::~NonUniformScaleComponentMode()
|
||||
|
||||
+11
@@ -124,4 +124,15 @@ namespace AzToolsFramework
|
||||
|
||||
return cameraState;
|
||||
}
|
||||
|
||||
float GetScreenDisplayScaling(const int viewportId)
|
||||
{
|
||||
float scaling = 1.0f;
|
||||
ViewportInteraction::ViewportInteractionRequestBus::EventResult(
|
||||
scaling, viewportId,
|
||||
&ViewportInteraction::ViewportInteractionRequestBus::Events::DeviceScalingFactor);
|
||||
|
||||
return scaling;
|
||||
}
|
||||
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
+3
@@ -60,6 +60,9 @@ namespace AzToolsFramework
|
||||
/// Wrapper for EBus call to return the CameraState for a given viewport.
|
||||
AzFramework::CameraState GetCameraState(int viewportId);
|
||||
|
||||
/// Wrapper for EBus call to return the DPI scaling for a given viewport.
|
||||
float GetScreenDisplayScaling(const int viewportId);
|
||||
|
||||
/// A utility to return the center of several points.
|
||||
/// Take several positions and store the min and max of each in
|
||||
/// turn - when all points have been added return the center/midpoint.
|
||||
|
||||
+18
-28
@@ -423,15 +423,14 @@ namespace AzToolsFramework
|
||||
}
|
||||
}
|
||||
|
||||
static void InitializeTranslationLookup(
|
||||
EntityIdManipulators& entityIdManipulators, const AZ::Vector3& snapOffset)
|
||||
static void InitializeTranslationLookup(EntityIdManipulators& entityIdManipulators)
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework);
|
||||
|
||||
for (auto& entityIdLookup : entityIdManipulators.m_lookups)
|
||||
{
|
||||
entityIdLookup.second.m_initial =
|
||||
AZ::Transform::CreateTranslation(GetWorldTranslation(entityIdLookup.first) + snapOffset);
|
||||
AZ::Transform::CreateTranslation(GetWorldTranslation(entityIdLookup.first));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -820,7 +819,7 @@ namespace AzToolsFramework
|
||||
// moving with ctrl - setting override
|
||||
pivotOverrideFrame.m_translationOverride =
|
||||
entityIdManipulators.m_manipulators->GetLocalTransform().GetTranslation();
|
||||
InitializeTranslationLookup(entityIdManipulators, -action.LocalPositionOffset());
|
||||
InitializeTranslationLookup(entityIdManipulators);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1277,12 +1276,12 @@ namespace AzToolsFramework
|
||||
|
||||
// linear
|
||||
translationManipulators->InstallLinearManipulatorMouseDownCallback(
|
||||
[this, manipulatorEntityIds](const LinearManipulator::Action& action) mutable
|
||||
[this, manipulatorEntityIds]([[maybe_unused]] const LinearManipulator::Action& action) mutable
|
||||
{
|
||||
// important to sort entityIds based on hierarchy order when updating transforms
|
||||
BuildSortedEntityIdVectorFromEntityIdMap(m_entityIdManipulators.m_lookups, manipulatorEntityIds->m_entityIds);
|
||||
|
||||
InitializeTranslationLookup(m_entityIdManipulators, action.m_start.m_positionSnapOffset);
|
||||
InitializeTranslationLookup(m_entityIdManipulators);
|
||||
|
||||
m_axisPreview.m_translation = m_entityIdManipulators.m_manipulators->GetLocalTransform().GetTranslation();
|
||||
m_axisPreview.m_orientation = QuaternionFromTransformNoScaling(
|
||||
@@ -1302,19 +1301,19 @@ namespace AzToolsFramework
|
||||
});
|
||||
|
||||
translationManipulators->InstallLinearManipulatorMouseUpCallback(
|
||||
[this](const LinearManipulator::Action& /*action*/) mutable
|
||||
[this]([[maybe_unused]] const LinearManipulator::Action& action) mutable
|
||||
{
|
||||
EndRecordManipulatorCommand();
|
||||
});
|
||||
|
||||
// planar
|
||||
translationManipulators->InstallPlanarManipulatorMouseDownCallback(
|
||||
[this, manipulatorEntityIds](const PlanarManipulator::Action& action)
|
||||
[this, manipulatorEntityIds]([[maybe_unused]] const PlanarManipulator::Action& action)
|
||||
{
|
||||
// important to sort entityIds based on hierarchy order when updating transforms
|
||||
BuildSortedEntityIdVectorFromEntityIdMap(m_entityIdManipulators.m_lookups, manipulatorEntityIds->m_entityIds);
|
||||
|
||||
InitializeTranslationLookup(m_entityIdManipulators, action.m_start.m_snapOffset);
|
||||
InitializeTranslationLookup(m_entityIdManipulators);
|
||||
|
||||
m_axisPreview.m_translation = m_entityIdManipulators.m_manipulators->GetLocalTransform().GetTranslation();
|
||||
m_axisPreview.m_orientation = QuaternionFromTransformNoScaling(
|
||||
@@ -1340,11 +1339,11 @@ namespace AzToolsFramework
|
||||
|
||||
// surface
|
||||
translationManipulators->InstallSurfaceManipulatorMouseDownCallback(
|
||||
[this, manipulatorEntityIds](const SurfaceManipulator::Action& action)
|
||||
[this, manipulatorEntityIds]([[maybe_unused]] const SurfaceManipulator::Action& action)
|
||||
{
|
||||
BuildSortedEntityIdVectorFromEntityIdMap(m_entityIdManipulators.m_lookups, manipulatorEntityIds->m_entityIds);
|
||||
|
||||
InitializeTranslationLookup(m_entityIdManipulators, action.m_start.m_snapOffset);
|
||||
InitializeTranslationLookup(m_entityIdManipulators);
|
||||
|
||||
m_axisPreview.m_translation = m_entityIdManipulators.m_manipulators->GetLocalTransform().GetTranslation();
|
||||
m_axisPreview.m_orientation = QuaternionFromTransformNoScaling(
|
||||
@@ -3326,26 +3325,16 @@ namespace AzToolsFramework
|
||||
}
|
||||
|
||||
static void DrawManipulatorGrid(
|
||||
AzFramework::DebugDisplayRequests& debugDisplay, const EntityIdManipulators& entityIdManipulators,
|
||||
const float gridSize, const float localSnapping)
|
||||
AzFramework::DebugDisplayRequests& debugDisplay, const EntityIdManipulators& entityIdManipulators, const float gridSize)
|
||||
{
|
||||
const AZ::Matrix3x3 orientation =
|
||||
AZ::Matrix3x3::CreateFromTransform(entityIdManipulators.m_manipulators->GetLocalTransform());
|
||||
|
||||
const AZ::Vector3 unsnappedTranslation =
|
||||
const AZ::Vector3 translation =
|
||||
entityIdManipulators.m_manipulators->GetLocalTransform().GetTranslation();
|
||||
|
||||
// calculate the offset to snap by to align the manipulator to the grid
|
||||
// note: only perform this if we are not snapping in local space
|
||||
const AZ::Vector3 snappedOffset = !localSnapping
|
||||
? CalculateSnappedOffset(unsnappedTranslation, orientation.GetBasisX(), gridSize) +
|
||||
CalculateSnappedOffset(unsnappedTranslation, orientation.GetBasisY(), gridSize)
|
||||
: AZ::Vector3::CreateZero();
|
||||
|
||||
const AZ::Vector3 snappedTranslation = unsnappedTranslation + snappedOffset;
|
||||
|
||||
DrawSnappingGrid(
|
||||
debugDisplay, AZ::Transform::CreateFromMatrix3x3AndTranslation(orientation, snappedTranslation),
|
||||
debugDisplay, AZ::Transform::CreateFromMatrix3x3AndTranslation(orientation, translation),
|
||||
gridSize);
|
||||
}
|
||||
|
||||
@@ -3484,7 +3473,7 @@ namespace AzToolsFramework
|
||||
const GridSnapParameters gridSnapParams = GridSnapSettings(viewportInfo.m_viewportId);
|
||||
if (gridSnapParams.m_gridSnap && m_entityIdManipulators.m_manipulators)
|
||||
{
|
||||
DrawManipulatorGrid(debugDisplay, m_entityIdManipulators, gridSnapParams.m_gridSize, modifiers.Alt());
|
||||
DrawManipulatorGrid(debugDisplay, m_entityIdManipulators, gridSnapParams.m_gridSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3573,9 +3562,10 @@ namespace AzToolsFramework
|
||||
debugDisplay.SetLineWidth(1.0f);
|
||||
|
||||
const float labelOffset = cl_viewportGizmoAxisLabelOffset;
|
||||
const auto labelXScreenPosition = (gizmoStart + (gizmoAxisX * labelOffset)) * editorCameraState.m_viewportSize;
|
||||
const auto labelYScreenPosition = (gizmoStart + (gizmoAxisY * labelOffset)) * editorCameraState.m_viewportSize;
|
||||
const auto labelZScreenPosition = (gizmoStart + (gizmoAxisZ * labelOffset)) * editorCameraState.m_viewportSize;
|
||||
const float screenScale = GetScreenDisplayScaling(viewportId);
|
||||
const auto labelXScreenPosition = (gizmoStart + (gizmoAxisX * labelOffset)) * editorCameraState.m_viewportSize * screenScale;
|
||||
const auto labelYScreenPosition = (gizmoStart + (gizmoAxisY * labelOffset)) * editorCameraState.m_viewportSize * screenScale;
|
||||
const auto labelZScreenPosition = (gizmoStart + (gizmoAxisZ * labelOffset)) * editorCameraState.m_viewportSize * screenScale;
|
||||
|
||||
// draw the label of of each axis for the gizmo
|
||||
const float labelSize = cl_viewportGizmoAxisLabelSize;
|
||||
|
||||
@@ -2281,6 +2281,14 @@ int CCryEditApp::IdleProcessing(bool bBackgroundUpdate)
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Ensure we don't get called re-entrantly
|
||||
// This can occur when a nested Qt event loop fires (e.g. by way of a modal dialog calling exec)
|
||||
if (m_idleProcessingRunning)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
QScopedValueRollback<bool> guard(m_idleProcessingRunning, true);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Call the update function of the engine
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -335,6 +335,8 @@ private:
|
||||
// If this flag is set, the next OnIdle() will update, even if the app is in the background, and then
|
||||
// this flag will be reset.
|
||||
bool m_bForceProcessIdle = false;
|
||||
// This is set while IdleProcessing is running to prevent re-entrancy
|
||||
bool m_idleProcessingRunning = false;
|
||||
// Keep the editor alive, even if no focus is set
|
||||
bool m_bKeepEditorActive = false;
|
||||
// Currently creating a new level
|
||||
|
||||
@@ -2887,9 +2887,12 @@ void EditorViewportWidget::UpdateCameraFromViewportContext()
|
||||
AZ::Matrix3x4 matrix;
|
||||
matrix.SetBasisAndTranslation(cameraState.m_side, cameraState.m_forward, cameraState.m_up, cameraState.m_position);
|
||||
auto m = AZMatrix3x4ToLYMatrix3x4(matrix);
|
||||
|
||||
m_updatingCameraPosition = true;
|
||||
SetViewTM(m);
|
||||
SetFOV(cameraState.m_fovOrZoom);
|
||||
m_Camera.SetZRange(cameraState.m_nearClip, cameraState.m_farClip);
|
||||
m_updatingCameraPosition = false;
|
||||
}
|
||||
|
||||
void EditorViewportWidget::SetAsActiveViewport()
|
||||
|
||||
@@ -121,7 +121,7 @@ protected:
|
||||
};
|
||||
#endif
|
||||
|
||||
Q_GLOBAL_STATIC(QtViewPaneManager, s_instance)
|
||||
Q_GLOBAL_STATIC(QtViewPaneManager, s_viewPaneManagerInstance)
|
||||
|
||||
|
||||
QWidget* QtViewPane::CreateWidget()
|
||||
@@ -611,12 +611,12 @@ void QtViewPaneManager::UnregisterPane(const QString& name)
|
||||
|
||||
QtViewPaneManager* QtViewPaneManager::instance()
|
||||
{
|
||||
return s_instance();
|
||||
return s_viewPaneManagerInstance();
|
||||
}
|
||||
|
||||
bool QtViewPaneManager::exists()
|
||||
{
|
||||
return s_instance.exists();
|
||||
return s_viewPaneManagerInstance.exists();
|
||||
}
|
||||
|
||||
void QtViewPaneManager::SetMainWindow(AzQtComponents::DockMainWindow* mainWindow, QSettings* settings, const QByteArray& lastMainWindowState)
|
||||
|
||||
@@ -59,7 +59,7 @@ namespace
|
||||
{
|
||||
int fps;
|
||||
const char* fpsDesc;
|
||||
} fps[] = {
|
||||
} fpsOptions[] = {
|
||||
{24, "Film(24)"}, {25, "PAL(25)"}, {30, "NTSC(30)"},
|
||||
{48, "Show(48)"}, {50, "PAL Field(50)"}, {60, "NTSC Field(60)"}
|
||||
};
|
||||
@@ -213,9 +213,9 @@ void CSequenceBatchRenderDialog::OnInitDialog()
|
||||
m_ui->m_resolutionCombo->setCurrentIndex(0);
|
||||
|
||||
// Fill the FPS combo box.
|
||||
for (int i = 0; i < AZStd::size(fps); ++i)
|
||||
for (int i = 0; i < AZStd::size(fpsOptions); ++i)
|
||||
{
|
||||
m_ui->m_fpsCombo->addItem(fps[i].fpsDesc);
|
||||
m_ui->m_fpsCombo->addItem(fpsOptions[i].fpsDesc);
|
||||
}
|
||||
m_ui->m_fpsCombo->setCurrentIndex(0);
|
||||
|
||||
@@ -306,9 +306,9 @@ void CSequenceBatchRenderDialog::OnRenderItemSelChange()
|
||||
m_ui->m_destinationEdit->setText(item.folder);
|
||||
// fps
|
||||
bool bFound = false;
|
||||
for (int i = 0; i < arraysize(fps); ++i)
|
||||
for (int i = 0; i < arraysize(fpsOptions); ++i)
|
||||
{
|
||||
if (item.fps == fps[i].fps)
|
||||
if (item.fps == fpsOptions[i].fps)
|
||||
{
|
||||
m_ui->m_fpsCombo->setCurrentIndex(i);
|
||||
bFound = true;
|
||||
@@ -621,7 +621,7 @@ void CSequenceBatchRenderDialog::OnFPSEditChange()
|
||||
|
||||
void CSequenceBatchRenderDialog::OnFPSChange(int itemIndex)
|
||||
{
|
||||
m_customFPS = fps[itemIndex].fps;
|
||||
m_customFPS = fpsOptions[itemIndex].fps;
|
||||
CheckForEnableUpdateButton();
|
||||
}
|
||||
|
||||
@@ -1543,13 +1543,13 @@ bool CSequenceBatchRenderDialog::SetUpNewRenderItem(SRenderItem& item)
|
||||
item.frameRange = Range(m_ui->m_startFrame->value() / m_fpsForTimeToFrameConversion,
|
||||
m_ui->m_endFrame->value() / m_fpsForTimeToFrameConversion);
|
||||
// fps
|
||||
if (m_ui->m_fpsCombo->currentIndex() == -1 || m_ui->m_fpsCombo->currentText() != fps[m_ui->m_fpsCombo->currentIndex()].fpsDesc)
|
||||
if (m_ui->m_fpsCombo->currentIndex() == -1 || m_ui->m_fpsCombo->currentText() != fpsOptions[m_ui->m_fpsCombo->currentIndex()].fpsDesc)
|
||||
{
|
||||
item.fps = m_customFPS;
|
||||
}
|
||||
else
|
||||
{
|
||||
item.fps = fps[m_ui->m_fpsCombo->currentIndex()].fps;
|
||||
item.fps = fpsOptions[m_ui->m_fpsCombo->currentIndex()].fps;
|
||||
}
|
||||
// prefix
|
||||
item.prefix = m_ui->BATCH_RENDER_FILE_PREFIX->text();
|
||||
|
||||
@@ -179,11 +179,11 @@ ComponentEntityEditorPlugin::ComponentEntityEditorPlugin([[maybe_unused]] IEdito
|
||||
LyViewPane::EntityOutliner,
|
||||
LyViewPane::CategoryTools,
|
||||
outlinerOptions);
|
||||
}
|
||||
|
||||
AzToolsFramework::ViewPaneOptions options;
|
||||
options.preferedDockingArea = Qt::NoDockWidgetArea;
|
||||
RegisterViewPane<SliceRelationshipWidget>(LyViewPane::SliceRelationships, LyViewPane::CategoryTools, options);
|
||||
AzToolsFramework::ViewPaneOptions options;
|
||||
options.preferedDockingArea = Qt::NoDockWidgetArea;
|
||||
RegisterViewPane<SliceRelationshipWidget>(LyViewPane::SliceRelationships, LyViewPane::CategoryTools, options);
|
||||
}
|
||||
|
||||
RegisterModuleResourceSelectors(GetIEditor()->GetResourceSelectorHost());
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
*/
|
||||
|
||||
#include <ProjectButtonWidget.h>
|
||||
|
||||
#include <AzQtComponents/Utilities/DesktopUtilities.h>
|
||||
|
||||
#include <QVBoxLayout>
|
||||
#include <QHBoxLayout>
|
||||
@@ -81,18 +81,24 @@ namespace O3DE::ProjectManager
|
||||
m_projectImageLabel = new LabelButton(this);
|
||||
m_projectImageLabel->setFixedSize(s_projectImageWidth, s_projectImageHeight);
|
||||
m_projectImageLabel->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
|
||||
connect(m_projectImageLabel, &LabelButton::triggered, [this]() { emit OpenProject(m_projectInfo.m_path); });
|
||||
vLayout->addWidget(m_projectImageLabel);
|
||||
|
||||
m_projectImageLabel->setPixmap(
|
||||
QPixmap(m_projectInfo.m_imagePath).scaled(m_projectImageLabel->size(), Qt::KeepAspectRatioByExpanding));
|
||||
|
||||
QMenu* newProjectMenu = new QMenu(this);
|
||||
m_editProjectAction = newProjectMenu->addAction(tr("Edit Project Settings..."));
|
||||
newProjectMenu->addSeparator();
|
||||
m_copyProjectAction = newProjectMenu->addAction(tr("Duplicate"));
|
||||
newProjectMenu->addSeparator();
|
||||
m_removeProjectAction = newProjectMenu->addAction(tr("Remove from O3DE"));
|
||||
m_deleteProjectAction = newProjectMenu->addAction(tr("Delete this Project"));
|
||||
QMenu* menu = new QMenu(this);
|
||||
menu->addAction(tr("Edit Project Settings..."), this, [this]() { emit EditProject(m_projectInfo.m_path); });
|
||||
menu->addSeparator();
|
||||
menu->addAction(tr("Open Project folder..."), this, [this]()
|
||||
{
|
||||
AzQtComponents::ShowFileOnDesktop(m_projectInfo.m_path);
|
||||
});
|
||||
menu->addSeparator();
|
||||
menu->addAction(tr("Duplicate"), this, [this]() { emit CopyProject(m_projectInfo.m_path); });
|
||||
menu->addSeparator();
|
||||
menu->addAction(tr("Remove from O3DE"), this, [this]() { emit RemoveProject(m_projectInfo.m_path); });
|
||||
menu->addAction(tr("Delete this Project"), this, [this]() { emit DeleteProject(m_projectInfo.m_path); });
|
||||
|
||||
QFrame* footer = new QFrame(this);
|
||||
QHBoxLayout* hLayout = new QHBoxLayout();
|
||||
@@ -104,17 +110,11 @@ namespace O3DE::ProjectManager
|
||||
|
||||
QPushButton* projectMenuButton = new QPushButton(this);
|
||||
projectMenuButton->setObjectName("projectMenuButton");
|
||||
projectMenuButton->setMenu(newProjectMenu);
|
||||
projectMenuButton->setMenu(menu);
|
||||
hLayout->addWidget(projectMenuButton);
|
||||
}
|
||||
|
||||
vLayout->addWidget(footer);
|
||||
|
||||
connect(m_projectImageLabel, &LabelButton::triggered, [this]() { emit OpenProject(m_projectInfo.m_path); });
|
||||
connect(m_editProjectAction, &QAction::triggered, [this]() { emit EditProject(m_projectInfo.m_path); });
|
||||
connect(m_copyProjectAction, &QAction::triggered, [this]() { emit CopyProject(m_projectInfo.m_path); });
|
||||
connect(m_removeProjectAction, &QAction::triggered, [this]() { emit RemoveProject(m_projectInfo.m_path); });
|
||||
connect(m_deleteProjectAction, &QAction::triggered, [this]() { emit DeleteProject(m_projectInfo.m_path); });
|
||||
}
|
||||
|
||||
void ProjectButton::SetButtonEnabled(bool enabled)
|
||||
|
||||
@@ -71,9 +71,5 @@ namespace O3DE::ProjectManager
|
||||
|
||||
ProjectInfo m_projectInfo;
|
||||
LabelButton* m_projectImageLabel;
|
||||
QAction* m_editProjectAction;
|
||||
QAction* m_copyProjectAction;
|
||||
QAction* m_removeProjectAction;
|
||||
QAction* m_deleteProjectAction;
|
||||
};
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -294,7 +294,8 @@ namespace O3DE::ProjectManager
|
||||
RegisterThisEngine();
|
||||
|
||||
return result == 0 && !PyErr_Occurred();
|
||||
} catch ([[maybe_unused]] const std::exception& e)
|
||||
}
|
||||
catch ([[maybe_unused]] const std::exception& e)
|
||||
{
|
||||
AZ_Warning("ProjectManagerWindow", false, "Py_Initialize() failed with %s", e.what());
|
||||
return false;
|
||||
@@ -320,25 +321,25 @@ namespace O3DE::ProjectManager
|
||||
bool registrationResult = true; // already registered is considered successful
|
||||
bool pythonResult = ExecuteWithLock(
|
||||
[&]
|
||||
{
|
||||
// check current engine path against all other registered engines
|
||||
// to see if we are already registered
|
||||
auto allEngines = m_manifest.attr("get_engines")();
|
||||
if (pybind11::isinstance<pybind11::list>(allEngines))
|
||||
{
|
||||
// check current engine path against all other registered engines
|
||||
// to see if we are already registered
|
||||
auto allEngines = m_manifest.attr("get_engines")();
|
||||
if (pybind11::isinstance<pybind11::list>(allEngines))
|
||||
for (auto engine : allEngines)
|
||||
{
|
||||
for (auto engine : allEngines)
|
||||
AZ::IO::FixedMaxPath enginePath(Py_To_String(engine["path"]));
|
||||
if (enginePath.Compare(m_enginePath) == 0)
|
||||
{
|
||||
AZ::IO::FixedMaxPath enginePath(Py_To_String(engine["path"]));
|
||||
if (enginePath.Compare(m_enginePath) == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto result = m_register.attr("register")(m_enginePath.c_str());
|
||||
registrationResult = (result.cast<int>() == 0);
|
||||
});
|
||||
auto result = m_register.attr("register")(m_enginePath.c_str());
|
||||
registrationResult = (result.cast<int>() == 0);
|
||||
});
|
||||
|
||||
bool finalResult = (registrationResult && pythonResult);
|
||||
AZ_Assert(finalResult, "Registration of this engine failed!");
|
||||
@@ -378,12 +379,12 @@ namespace O3DE::ProjectManager
|
||||
auto o3deData = m_manifest.attr("load_o3de_manifest")();
|
||||
if (pybind11::isinstance<pybind11::dict>(o3deData))
|
||||
{
|
||||
engineInfo.m_path = Py_To_String(enginePath);
|
||||
engineInfo.m_defaultGemsFolder = Py_To_String(o3deData["default_gems_folder"]);
|
||||
engineInfo.m_defaultProjectsFolder = Py_To_String(o3deData["default_projects_folder"]);
|
||||
engineInfo.m_path = Py_To_String(enginePath);
|
||||
engineInfo.m_defaultGemsFolder = Py_To_String(o3deData["default_gems_folder"]);
|
||||
engineInfo.m_defaultProjectsFolder = Py_To_String(o3deData["default_projects_folder"]);
|
||||
engineInfo.m_defaultRestrictedFolder = Py_To_String(o3deData["default_restricted_folder"]);
|
||||
engineInfo.m_defaultTemplatesFolder = Py_To_String(o3deData["default_templates_folder"]);
|
||||
engineInfo.m_thirdPartyPath = Py_To_String_Optional(o3deData,"third_party_path","");
|
||||
engineInfo.m_defaultTemplatesFolder = Py_To_String(o3deData["default_templates_folder"]);
|
||||
engineInfo.m_thirdPartyPath = Py_To_String(o3deData["default_third_party_folder"]);
|
||||
}
|
||||
|
||||
auto engineData = m_manifest.attr("get_engine_json_data")(pybind11::none(), enginePath);
|
||||
@@ -391,8 +392,8 @@ namespace O3DE::ProjectManager
|
||||
{
|
||||
try
|
||||
{
|
||||
engineInfo.m_version = Py_To_String_Optional(engineData,"O3DEVersion","0.0.0.0");
|
||||
engineInfo.m_name = Py_To_String_Optional(engineData,"engine_name","O3DE");
|
||||
engineInfo.m_version = Py_To_String_Optional(engineData, "O3DEVersion", "0.0.0.0");
|
||||
engineInfo.m_name = Py_To_String_Optional(engineData, "engine_name", "O3DE");
|
||||
}
|
||||
catch ([[maybe_unused]] const std::exception& e)
|
||||
{
|
||||
@@ -416,44 +417,32 @@ namespace O3DE::ProjectManager
|
||||
bool PythonBindings::SetEngineInfo(const EngineInfo& engineInfo)
|
||||
{
|
||||
bool result = ExecuteWithLock([&] {
|
||||
pybind11::str enginePath = engineInfo.m_path.toStdString();
|
||||
pybind11::str defaultProjectsFolder = engineInfo.m_defaultProjectsFolder.toStdString();
|
||||
pybind11::str defaultGemsFolder = engineInfo.m_defaultGemsFolder.toStdString();
|
||||
pybind11::str enginePath = engineInfo.m_path.toStdString();
|
||||
pybind11::str defaultProjectsFolder = engineInfo.m_defaultProjectsFolder.toStdString();
|
||||
pybind11::str defaultGemsFolder = engineInfo.m_defaultGemsFolder.toStdString();
|
||||
pybind11::str defaultTemplatesFolder = engineInfo.m_defaultTemplatesFolder.toStdString();
|
||||
pybind11::str defaultThirdPartyFolder = engineInfo.m_thirdPartyPath.toStdString();
|
||||
|
||||
auto registrationResult = m_register.attr("register")(
|
||||
enginePath, // engine_path
|
||||
pybind11::none(), // project_path
|
||||
enginePath, // engine_path
|
||||
pybind11::none(), // project_path
|
||||
pybind11::none(), // gem_path
|
||||
pybind11::none(), // external_subdir_path
|
||||
pybind11::none(), // template_path
|
||||
pybind11::none(), // restricted_path
|
||||
pybind11::none(), // repo_uri
|
||||
pybind11::none(), // external_subdir_path
|
||||
pybind11::none(), // template_path
|
||||
pybind11::none(), // restricted_path
|
||||
pybind11::none(), // repo_uri
|
||||
pybind11::none(), // default_engines_folder
|
||||
defaultProjectsFolder,
|
||||
defaultGemsFolder,
|
||||
defaultTemplatesFolder
|
||||
defaultTemplatesFolder,
|
||||
pybind11::none(), // default_restricted_folder
|
||||
defaultThirdPartyFolder
|
||||
);
|
||||
|
||||
if (registrationResult.cast<int>() != 0)
|
||||
{
|
||||
result = false;
|
||||
}
|
||||
|
||||
auto manifest = m_manifest.attr("load_o3de_manifest")();
|
||||
if (pybind11::isinstance<pybind11::dict>(manifest))
|
||||
{
|
||||
try
|
||||
{
|
||||
manifest["third_party_path"] = engineInfo.m_thirdPartyPath.toStdString();
|
||||
m_manifest.attr("save_o3de_manifest")(manifest);
|
||||
}
|
||||
catch ([[maybe_unused]] const std::exception& e)
|
||||
{
|
||||
AZ_Warning("PythonBindings", false, "Failed to set third party path.");
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
return result;
|
||||
@@ -477,12 +466,12 @@ namespace O3DE::ProjectManager
|
||||
QVector<GemInfo> gems;
|
||||
|
||||
auto result = ExecuteWithLockErrorHandling([&]
|
||||
{
|
||||
for (auto path : m_manifest.attr("get_engine_gems")())
|
||||
{
|
||||
for (auto path : m_manifest.attr("get_engine_gems")())
|
||||
{
|
||||
gems.push_back(GemInfoFromPath(path));
|
||||
}
|
||||
});
|
||||
gems.push_back(GemInfoFromPath(path));
|
||||
}
|
||||
});
|
||||
if (!result.IsSuccess())
|
||||
{
|
||||
return AZ::Failure<AZStd::string>(result.GetError().c_str());
|
||||
@@ -497,13 +486,13 @@ namespace O3DE::ProjectManager
|
||||
QVector<GemInfo> gems;
|
||||
|
||||
auto result = ExecuteWithLockErrorHandling([&]
|
||||
{
|
||||
pybind11::str pyProjectPath = projectPath.toStdString();
|
||||
for (auto path : m_manifest.attr("get_all_gems")(pyProjectPath))
|
||||
{
|
||||
pybind11::str pyProjectPath = projectPath.toStdString();
|
||||
for (auto path : m_manifest.attr("get_all_gems")(pyProjectPath))
|
||||
{
|
||||
gems.push_back(GemInfoFromPath(path));
|
||||
}
|
||||
});
|
||||
gems.push_back(GemInfoFromPath(path));
|
||||
}
|
||||
});
|
||||
if (!result.IsSuccess())
|
||||
{
|
||||
return AZ::Failure<AZStd::string>(result.GetError().c_str());
|
||||
@@ -518,12 +507,12 @@ namespace O3DE::ProjectManager
|
||||
// Retrieve the path to the cmake file that lists the enabled gems.
|
||||
pybind11::str enabledGemsFilename;
|
||||
auto result = ExecuteWithLockErrorHandling([&]
|
||||
{
|
||||
const pybind11::str pyProjectPath = projectPath.toStdString();
|
||||
enabledGemsFilename = m_cmake.attr("get_enabled_gem_cmake_file")(
|
||||
pybind11::none(), // project_name
|
||||
pyProjectPath); // project_path
|
||||
});
|
||||
{
|
||||
const pybind11::str pyProjectPath = projectPath.toStdString();
|
||||
enabledGemsFilename = m_cmake.attr("get_enabled_gem_cmake_file")(
|
||||
pybind11::none(), // project_name
|
||||
pyProjectPath); // project_path
|
||||
});
|
||||
if (!result.IsSuccess())
|
||||
{
|
||||
return AZ::Failure<AZStd::string>(result.GetError().c_str());
|
||||
@@ -532,13 +521,13 @@ namespace O3DE::ProjectManager
|
||||
// Retrieve the actual list of names from the cmake file.
|
||||
QVector<AZStd::string> gemNames;
|
||||
result = ExecuteWithLockErrorHandling([&]
|
||||
{
|
||||
const auto pyGemNames = m_cmake.attr("get_enabled_gems")(enabledGemsFilename);
|
||||
for (auto gemName : pyGemNames)
|
||||
{
|
||||
const auto pyGemNames = m_cmake.attr("get_enabled_gems")(enabledGemsFilename);
|
||||
for (auto gemName : pyGemNames)
|
||||
{
|
||||
gemNames.push_back(Py_To_String(gemName));
|
||||
}
|
||||
});
|
||||
gemNames.push_back(Py_To_String(gemName));
|
||||
}
|
||||
});
|
||||
if (!result.IsSuccess())
|
||||
{
|
||||
return AZ::Failure<AZStd::string>(result.GetError().c_str());
|
||||
@@ -552,13 +541,13 @@ namespace O3DE::ProjectManager
|
||||
bool registrationResult = false;
|
||||
bool result = ExecuteWithLock(
|
||||
[&]
|
||||
{
|
||||
pybind11::str projectPath = path.toStdString();
|
||||
auto pythonRegistrationResult = m_register.attr("register")(pybind11::none(), projectPath);
|
||||
{
|
||||
pybind11::str projectPath = path.toStdString();
|
||||
auto pythonRegistrationResult = m_register.attr("register")(pybind11::none(), projectPath);
|
||||
|
||||
// Returns an exit code so boolify it then invert result
|
||||
registrationResult = !pythonRegistrationResult.cast<bool>();
|
||||
});
|
||||
// Returns an exit code so boolify it then invert result
|
||||
registrationResult = !pythonRegistrationResult.cast<bool>();
|
||||
});
|
||||
|
||||
return result && registrationResult;
|
||||
}
|
||||
@@ -568,30 +557,31 @@ namespace O3DE::ProjectManager
|
||||
bool registrationResult = false;
|
||||
bool result = ExecuteWithLock(
|
||||
[&]
|
||||
{
|
||||
pybind11::str projectPath = path.toStdString();
|
||||
auto pythonRegistrationResult = m_register.attr("register")(
|
||||
pybind11::none(), // engine_path
|
||||
projectPath, // project_path
|
||||
pybind11::none(), // gem_path
|
||||
pybind11::none(), // external_subdir_path
|
||||
pybind11::none(), // template_path
|
||||
pybind11::none(), // restricted_path
|
||||
pybind11::none(), // repo_uri
|
||||
pybind11::none(), // default_engines_folder
|
||||
pybind11::none(), // default_projects_folder
|
||||
pybind11::none(), // default_gems_folder
|
||||
pybind11::none(), // default_templates_folder
|
||||
pybind11::none(), // default_restricted_folder
|
||||
pybind11::none(), // external_subdir_engine_path
|
||||
pybind11::none(), // external_subdir_project_path
|
||||
true, // remove
|
||||
false // force
|
||||
{
|
||||
pybind11::str projectPath = path.toStdString();
|
||||
auto pythonRegistrationResult = m_register.attr("register")(
|
||||
pybind11::none(), // engine_path
|
||||
projectPath, // project_path
|
||||
pybind11::none(), // gem_path
|
||||
pybind11::none(), // external_subdir_path
|
||||
pybind11::none(), // template_path
|
||||
pybind11::none(), // restricted_path
|
||||
pybind11::none(), // repo_uri
|
||||
pybind11::none(), // default_engines_folder
|
||||
pybind11::none(), // default_projects_folder
|
||||
pybind11::none(), // default_gems_folder
|
||||
pybind11::none(), // default_templates_folder
|
||||
pybind11::none(), // default_restricted_folder
|
||||
pybind11::none(), // default_third_party_folder
|
||||
pybind11::none(), // external_subdir_engine_path
|
||||
pybind11::none(), // external_subdir_project_path
|
||||
true, // remove
|
||||
false // force
|
||||
);
|
||||
|
||||
// Returns an exit code so boolify it then invert result
|
||||
registrationResult = !pythonRegistrationResult.cast<bool>();
|
||||
});
|
||||
|
||||
// Returns an exit code so boolify it then invert result
|
||||
registrationResult = !pythonRegistrationResult.cast<bool>();
|
||||
});
|
||||
|
||||
return result && registrationResult;
|
||||
}
|
||||
@@ -649,12 +639,12 @@ namespace O3DE::ProjectManager
|
||||
try
|
||||
{
|
||||
// required
|
||||
gemInfo.m_name = Py_To_String(data["gem_name"]);
|
||||
gemInfo.m_name = Py_To_String(data["gem_name"]);
|
||||
|
||||
// optional
|
||||
gemInfo.m_displayName = Py_To_String_Optional(data, "DisplayName", gemInfo.m_name);
|
||||
gemInfo.m_summary = Py_To_String_Optional(data, "Summary", "");
|
||||
gemInfo.m_version = Py_To_String_Optional(data, "Version", "");
|
||||
gemInfo.m_summary = Py_To_String_Optional(data, "Summary", "");
|
||||
gemInfo.m_version = Py_To_String_Optional(data, "Version", "");
|
||||
|
||||
if (data.contains("Tags"))
|
||||
{
|
||||
@@ -685,7 +675,7 @@ namespace O3DE::ProjectManager
|
||||
try
|
||||
{
|
||||
projectInfo.m_projectName = Py_To_String(projectData["project_name"]);
|
||||
projectInfo.m_displayName = Py_To_String_Optional(projectData,"display_name", projectInfo.m_projectName);
|
||||
projectInfo.m_displayName = Py_To_String_Optional(projectData, "display_name", projectInfo.m_projectName);
|
||||
}
|
||||
catch ([[maybe_unused]] const std::exception& e)
|
||||
{
|
||||
@@ -727,33 +717,33 @@ namespace O3DE::ProjectManager
|
||||
AZ::Outcome<void, AZStd::string> PythonBindings::AddGemToProject(const QString& gemPath, const QString& projectPath)
|
||||
{
|
||||
return ExecuteWithLockErrorHandling([&]
|
||||
{
|
||||
pybind11::str pyGemPath = gemPath.toStdString();
|
||||
pybind11::str pyProjectPath = projectPath.toStdString();
|
||||
{
|
||||
pybind11::str pyGemPath = gemPath.toStdString();
|
||||
pybind11::str pyProjectPath = projectPath.toStdString();
|
||||
|
||||
m_enableGemProject.attr("enable_gem_in_project")(
|
||||
pybind11::none(), // gem name not needed as path is provided
|
||||
pyGemPath,
|
||||
pybind11::none(), // project name not needed as path is provided
|
||||
pyProjectPath
|
||||
m_enableGemProject.attr("enable_gem_in_project")(
|
||||
pybind11::none(), // gem name not needed as path is provided
|
||||
pyGemPath,
|
||||
pybind11::none(), // project name not needed as path is provided
|
||||
pyProjectPath
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
AZ::Outcome<void, AZStd::string> PythonBindings::RemoveGemFromProject(const QString& gemPath, const QString& projectPath)
|
||||
{
|
||||
return ExecuteWithLockErrorHandling([&]
|
||||
{
|
||||
pybind11::str pyGemPath = gemPath.toStdString();
|
||||
pybind11::str pyProjectPath = projectPath.toStdString();
|
||||
{
|
||||
pybind11::str pyGemPath = gemPath.toStdString();
|
||||
pybind11::str pyProjectPath = projectPath.toStdString();
|
||||
|
||||
m_disableGemProject.attr("disable_gem_in_project")(
|
||||
pybind11::none(), // gem name not needed as path is provided
|
||||
pyGemPath,
|
||||
pybind11::none(), // project name not needed as path is provided
|
||||
pyProjectPath
|
||||
m_disableGemProject.attr("disable_gem_in_project")(
|
||||
pybind11::none(), // gem name not needed as path is provided
|
||||
pyGemPath,
|
||||
pybind11::none(), // project name not needed as path is provided
|
||||
pyProjectPath
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
bool PythonBindings::UpdateProject([[maybe_unused]] const ProjectInfo& projectInfo)
|
||||
@@ -773,8 +763,8 @@ namespace O3DE::ProjectManager
|
||||
{
|
||||
// required
|
||||
templateInfo.m_displayName = Py_To_String(data["display_name"]);
|
||||
templateInfo.m_name = Py_To_String(data["template_name"]);
|
||||
templateInfo.m_summary = Py_To_String(data["summary"]);
|
||||
templateInfo.m_name = Py_To_String(data["template_name"]);
|
||||
templateInfo.m_summary = Py_To_String(data["summary"]);
|
||||
|
||||
// optional
|
||||
if (data.contains("canonical_tags"))
|
||||
@@ -806,7 +796,7 @@ namespace O3DE::ProjectManager
|
||||
QVector<ProjectTemplateInfo> templates;
|
||||
|
||||
bool result = ExecuteWithLock([&] {
|
||||
for (auto path : m_manifest.attr("get_project_templates")())
|
||||
for (auto path : m_manifest.attr("get_templates_for_project_creation")())
|
||||
{
|
||||
templates.push_back(ProjectTemplateInfoFromPath(path));
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
|
||||
#include <AzToolsFramework/Thumbnails/ThumbnailerNullComponent.h>
|
||||
#include <SliceConverterEditorEntityContextComponent.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
@@ -34,6 +35,9 @@ namespace AZ
|
||||
Application::Application(int argc, char** argv)
|
||||
: AzToolsFramework::ToolsApplication(&argc, &argv)
|
||||
{
|
||||
// We need a specialized variant of EditorEntityContextCompnent for the SliceConverter, so we register the descriptor here.
|
||||
RegisterComponentDescriptor(AzToolsFramework::SliceConverterEditorEntityContextComponent::CreateDescriptor());
|
||||
|
||||
AZ::IO::FixedMaxPath projectPath = AZ::Utils::GetProjectPath();
|
||||
if (projectPath.empty())
|
||||
{
|
||||
@@ -110,10 +114,21 @@ namespace AZ
|
||||
|
||||
AZ::ComponentTypeList Application::GetRequiredSystemComponents() const
|
||||
{
|
||||
// Use all of the default system components, but also add in the ThumbnailerNullComponent so that components requiring
|
||||
// a ThumbnailService can still be started up.
|
||||
// By default, we use all of the standard system components.
|
||||
AZ::ComponentTypeList components = AzToolsFramework::ToolsApplication::GetRequiredSystemComponents();
|
||||
|
||||
// Also add in the ThumbnailerNullComponent so that components requiring a ThumbnailService can still be started up.
|
||||
components.emplace_back(azrtti_typeid<AzToolsFramework::Thumbnailer::ThumbnailerNullComponent>());
|
||||
|
||||
// The Slice Converter requires a specialized variant of the EditorEntityContextComponent that exposes the ability
|
||||
// to disable the behavior of activating entities on creation. During conversion, the creation flow will be triggered,
|
||||
// but entity activation requires a significant amount of subsystem initialization that's unneeded for conversion.
|
||||
// So, to get around this, we swap out EditorEntityContextComponent with SliceConverterEditorEntityContextComponent.
|
||||
components.erase(
|
||||
AZStd::remove(
|
||||
components.begin(), components.end(), azrtti_typeid<AzToolsFramework::EditorEntityContextComponent>()),
|
||||
components.end());
|
||||
components.emplace_back(azrtti_typeid<AzToolsFramework::SliceConverterEditorEntityContextComponent>());
|
||||
return components;
|
||||
}
|
||||
} // namespace SerializeContextTools
|
||||
|
||||
@@ -30,13 +30,16 @@
|
||||
#include <AzToolsFramework/Prefab/PrefabDomUtils.h>
|
||||
#include <AzToolsFramework/Prefab/EditorPrefabComponent.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabPublicInterface.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/InstanceUpdateExecutorInterface.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
|
||||
#include <AzToolsFramework/Entity/PrefabEditorEntityOwnershipInterface.h>
|
||||
#include <AzToolsFramework/ToolsComponents/TransformComponent.h>
|
||||
#include <Application.h>
|
||||
#include <SliceConverter.h>
|
||||
#include <SliceConverterEditorEntityContextComponent.h>
|
||||
#include <Utilities.h>
|
||||
|
||||
|
||||
// SliceConverter reads in a slice file (saved in an ObjectStream format), instantiates it, creates a prefab out of the data,
|
||||
// and saves the prefab in a JSON format. This can be used for one-time migrations of slices or slice-based levels to prefabs.
|
||||
//
|
||||
@@ -99,12 +102,26 @@ namespace AZ
|
||||
bool result = true;
|
||||
rapidjson::StringBuffer scratchBuffer;
|
||||
|
||||
// For slice conversion, disable the EditorEntityContextComponent logic that activates entities on creation.
|
||||
// This prevents a lot of error messages and crashes during conversion due to lack of full environment and subsystem setup.
|
||||
AzToolsFramework::SliceConverterEditorEntityContextComponent::DisableOnContextEntityLogic();
|
||||
|
||||
// Loop through the list of requested files and convert them.
|
||||
AZStd::vector<AZStd::string> fileList = Utilities::ReadFileListFromCommandLine(application, "files");
|
||||
for (AZStd::string& filePath : fileList)
|
||||
{
|
||||
bool convertResult = ConvertSliceFile(convertSettings.m_serializeContext, filePath, isDryRun);
|
||||
result = result && convertResult;
|
||||
|
||||
// Clear out all registered prefab templates between each top-level file that gets processed.
|
||||
auto prefabSystemComponentInterface = AZ::Interface<AzToolsFramework::Prefab::PrefabSystemComponentInterface>::Get();
|
||||
for (auto templateId : m_createdTemplateIds)
|
||||
{
|
||||
// We don't just want to call RemoveAllTemplates() because the root template should remain between file conversions.
|
||||
prefabSystemComponentInterface->RemoveTemplate(templateId);
|
||||
}
|
||||
m_aliasIdMapper.clear();
|
||||
m_createdTemplateIds.clear();
|
||||
}
|
||||
|
||||
DisconnectFromAssetProcessor();
|
||||
@@ -114,6 +131,13 @@ namespace AZ
|
||||
bool SliceConverter::ConvertSliceFile(
|
||||
AZ::SerializeContext* serializeContext, const AZStd::string& slicePath, bool isDryRun)
|
||||
{
|
||||
/* To convert a slice file, we read the input file in via ObjectStream, then use the "class ready" callback to convert
|
||||
* the data in memory to a Prefab.
|
||||
* If the input file is a level file (.ly), we actually need to load the level slice file ("levelentities.editor_xml") from
|
||||
* within the level file, which effectively is a zip file of the level slice file and a bunch of legacy level files that won't
|
||||
* be converted, since the systems that would use them no longer exist.
|
||||
*/
|
||||
|
||||
bool result = true;
|
||||
bool packOpened = false;
|
||||
|
||||
@@ -144,7 +168,7 @@ namespace AZ
|
||||
AZ_STRING_ARG(fileExtension.Native()));
|
||||
}
|
||||
|
||||
auto callback = [&outputPath, isDryRun](void* classPtr, const Uuid& classId, SerializeContext* context)
|
||||
auto callback = [this, &outputPath, isDryRun](void* classPtr, const Uuid& classId, SerializeContext* context)
|
||||
{
|
||||
if (classId != azrtti_typeid<AZ::Entity>())
|
||||
{
|
||||
@@ -178,6 +202,13 @@ namespace AZ
|
||||
bool SliceConverter::ConvertSliceToPrefab(
|
||||
AZ::SerializeContext* serializeContext, AZ::IO::PathView outputPath, bool isDryRun, AZ::Entity* rootEntity)
|
||||
{
|
||||
/* Given a root slice entity, we convert it to a prefab by doing the following:
|
||||
* - Locate the SliceComponent
|
||||
* - Take all the entities directly located on the slice, and put them into a prefab
|
||||
* - Fix up any top-level entities to have the prefab container entity as their parent
|
||||
* - If there are any nested slice instances, convert the nested slices to prefabs, then convert the instances.
|
||||
*/
|
||||
|
||||
auto prefabSystemComponentInterface = AZ::Interface<AzToolsFramework::Prefab::PrefabSystemComponentInterface>::Get();
|
||||
|
||||
// Find the slice from the root entity.
|
||||
@@ -192,9 +223,14 @@ namespace AZ
|
||||
SliceComponent::EntityList sliceEntities = sliceComponent->GetNewEntities();
|
||||
AZ_Printf("Convert-Slice", " Slice contains %zu entities.\n", sliceEntities.size());
|
||||
|
||||
// Create the Prefab with the entities from the slice
|
||||
// Create the Prefab with the entities from the slice.
|
||||
// The entities are added in a separate step so that we can give them deterministic entity aliases that match their entity Ids
|
||||
AZStd::unique_ptr<AzToolsFramework::Prefab::Instance> sourceInstance(
|
||||
prefabSystemComponentInterface->CreatePrefab(sliceEntities, {}, outputPath));
|
||||
prefabSystemComponentInterface->CreatePrefab({}, {}, outputPath));
|
||||
for (auto& entity : sliceEntities)
|
||||
{
|
||||
sourceInstance->AddEntity(*entity, AZStd::string::format("Entity_%s", entity->GetId().ToString().c_str()));
|
||||
}
|
||||
|
||||
// Dispatch events here, because prefab creation might trigger asset loads in rare circumstances.
|
||||
AZ::Data::AssetManager::Instance().DispatchEvents();
|
||||
@@ -204,12 +240,28 @@ namespace AZ
|
||||
AzToolsFramework::Prefab::EntityOptionalReference container = sourceInstance->GetContainerEntity();
|
||||
FixPrefabEntities(container->get(), sliceEntities);
|
||||
|
||||
// Keep track of the template Id we created, we're going to remove it at the end of slice file conversion to make sure
|
||||
// the data doesn't stick around between file conversions.
|
||||
auto templateId = sourceInstance->GetTemplateId();
|
||||
if (templateId == AzToolsFramework::Prefab::InvalidTemplateId)
|
||||
{
|
||||
AZ_Printf("Convert-Slice", " Path error. Path could be invalid, or the prefab may not be loaded in this level.\n");
|
||||
return false;
|
||||
}
|
||||
m_createdTemplateIds.emplace(templateId);
|
||||
|
||||
// Save off a mapping of the original slice entity IDs to the new prefab template entity aliases.
|
||||
// When converting nested slices, this mapping will be needed to fix up the parent entity hierarchy correctly.
|
||||
auto entityAliases = sourceInstance->GetEntityAliases();
|
||||
for (auto& alias : entityAliases)
|
||||
{
|
||||
auto id = sourceInstance->GetEntityId(alias);
|
||||
auto result = m_aliasIdMapper.emplace(TemplateEntityIdPair(templateId, id), alias);
|
||||
if (!result.second)
|
||||
{
|
||||
AZ_Printf("Convert-Slice", " Duplicate entity alias -> entity id entries found, conversion may not be successful.\n");
|
||||
}
|
||||
}
|
||||
|
||||
// Update the prefab template with the fixed-up data in our prefab instance.
|
||||
AzToolsFramework::Prefab::PrefabDom prefabDom;
|
||||
@@ -254,21 +306,26 @@ namespace AZ
|
||||
// via an EditorRequests EBus in CreatePrefab, but the subsystem that listens for it isn't present in this tool.)
|
||||
AzToolsFramework::EditorEntityContextRequestBus::Broadcast(
|
||||
&AzToolsFramework::EditorEntityContextRequestBus::Events::AddRequiredComponents, containerEntity);
|
||||
containerEntity.AddComponent(aznew AzToolsFramework::Prefab::EditorPrefabComponent());
|
||||
if (containerEntity.FindComponent<AzToolsFramework::Prefab::EditorPrefabComponent>() == nullptr)
|
||||
{
|
||||
containerEntity.AddComponent(aznew AzToolsFramework::Prefab::EditorPrefabComponent());
|
||||
}
|
||||
|
||||
// Make all the components on the container entity have deterministic component IDs, so that multiple runs of the tool
|
||||
// on the same slice will produce the same prefab output. We're going to cheat a bit and just use the component type hash
|
||||
// as the component ID. This would break if we had multiple components of the same type, but that currently doesn't
|
||||
// happen for the container entity.
|
||||
auto containerComponents = containerEntity.GetComponents();
|
||||
for (auto& component : containerComponents)
|
||||
{
|
||||
component->SetId(component->GetUnderlyingComponentType().GetHash());
|
||||
}
|
||||
|
||||
// Reparent any root-level slice entities to the container entity.
|
||||
for (auto entity : sliceEntities)
|
||||
{
|
||||
AzToolsFramework::Components::TransformComponent* transformComponent =
|
||||
entity->FindComponent<AzToolsFramework::Components::TransformComponent>();
|
||||
if (transformComponent)
|
||||
{
|
||||
if (!transformComponent->GetParentId().IsValid())
|
||||
{
|
||||
transformComponent->SetParent(containerEntity.GetId());
|
||||
transformComponent->UpdateCachedWorldTransform();
|
||||
}
|
||||
}
|
||||
constexpr bool onlySetIfInvalid = true;
|
||||
SetParentEntity(*entity, containerEntity.GetId(), onlySetIfInvalid);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -276,9 +333,13 @@ namespace AZ
|
||||
SliceComponent* sliceComponent, AzToolsFramework::Prefab::Instance* sourceInstance,
|
||||
AZ::SerializeContext* serializeContext, bool isDryRun)
|
||||
{
|
||||
/* Given a root slice, find all the nested slices and convert them. */
|
||||
|
||||
// Get the list of nested slices that this slice uses.
|
||||
const SliceComponent::SliceList& sliceList = sliceComponent->GetSlices();
|
||||
auto prefabSystemComponentInterface = AZ::Interface<AzToolsFramework::Prefab::PrefabSystemComponentInterface>::Get();
|
||||
|
||||
// For each nested slice, convert it.
|
||||
for (auto& slice : sliceList)
|
||||
{
|
||||
// Get the nested slice asset
|
||||
@@ -312,7 +373,7 @@ namespace AZ
|
||||
return false;
|
||||
}
|
||||
|
||||
// Load the prefab template for the newly-created nested prefab.
|
||||
// Find the prefab template we created for the newly-created nested prefab.
|
||||
// To get the template, we need to take our absolute slice path and turn it into a project-relative prefab path.
|
||||
AZ::IO::Path nestedPrefabPath = assetPath;
|
||||
nestedPrefabPath.ReplaceExtension("prefab");
|
||||
@@ -346,11 +407,25 @@ namespace AZ
|
||||
}
|
||||
|
||||
bool SliceConverter::ConvertSliceInstance(
|
||||
[[maybe_unused]] AZ::SliceComponent::SliceInstance& instance,
|
||||
[[maybe_unused]] AZ::Data::Asset<AZ::SliceAsset>& sliceAsset,
|
||||
AZ::SliceComponent::SliceInstance& instance,
|
||||
AZ::Data::Asset<AZ::SliceAsset>& sliceAsset,
|
||||
AzToolsFramework::Prefab::TemplateReference nestedTemplate,
|
||||
AzToolsFramework::Prefab::Instance* topLevelInstance)
|
||||
{
|
||||
/* To convert a slice instance, it's important to understand the similarities and differences between slices and prefabs.
|
||||
* Both slices and prefabs have the concept of instances of a nested slice/prefab, where each instance can have its own
|
||||
* set of changed data (transforms, component values, etc).
|
||||
* For slices, the changed data comes from applying a DataPatch to an instantiated set of entities from the nested slice.
|
||||
* From prefabs, the changed data comes from Json patches that are applied to the instantiated set of entities from the
|
||||
* nested prefab. The prefab instance entities also have different IDs than the slice instance entities, so we'll need
|
||||
* to remap some of them along the way.
|
||||
* To get from one to the other, we'll need to do the following:
|
||||
* - Instantiate the nested slice and nested prefab
|
||||
* - Patch the nested slice instance and fix up the entity ID references
|
||||
* - Replace the nested prefab instance entities with the fixed-up slice ones
|
||||
* - Add the nested instance (and the link patch) to the top-level prefab
|
||||
*/
|
||||
|
||||
auto instanceToTemplateInterface = AZ::Interface<AzToolsFramework::Prefab::InstanceToTemplateInterface>::Get();
|
||||
auto prefabSystemComponentInterface = AZ::Interface<AzToolsFramework::Prefab::PrefabSystemComponentInterface>::Get();
|
||||
|
||||
@@ -371,22 +446,83 @@ namespace AZ
|
||||
AzToolsFramework::Prefab::PrefabDom unmodifiedNestedInstanceDom;
|
||||
instanceToTemplateInterface->GenerateDomForInstance(unmodifiedNestedInstanceDom, *(nestedInstance.get()));
|
||||
|
||||
// Currently, DataPatch conversions for nested slices aren't implemented, so all nested slice overrides will
|
||||
// be lost.
|
||||
AZ_Warning(
|
||||
"Convert-Slice", false, " Nested slice instances will lose all of their override data during conversion.",
|
||||
nestedTemplate->get().GetFilePath().c_str());
|
||||
// Instantiate a new instance of the nested slice
|
||||
SliceComponent* dependentSlice = sliceAsset.Get()->GetComponent();
|
||||
[[maybe_unused]] AZ::SliceComponent::InstantiateResult instantiationResult = dependentSlice->Instantiate();
|
||||
AZ_Assert(instantiationResult == AZ::SliceComponent::InstantiateResult::Success, "Failed to instantiate instance");
|
||||
|
||||
// Set the container entity of the nested prefab to have the top-level prefab as the parent.
|
||||
// Once DataPatch conversions are supported, this will need to change to nest the prefab under the appropriate entity
|
||||
// within the level.
|
||||
// Apply the data patch for this instance of the nested slice. This will provide us with a version of the slice's entities
|
||||
// with all data overrides applied to them.
|
||||
DataPatch::FlagsMap sourceDataFlags = dependentSlice->GetDataFlagsForInstances().GetDataFlagsForPatching();
|
||||
DataPatch::FlagsMap targetDataFlags = instance.GetDataFlags().GetDataFlagsForPatching(&instance.GetEntityIdToBaseMap());
|
||||
AZ::ObjectStream::FilterDescriptor filterDesc(AZ::Data::AssetFilterNoAssetLoading);
|
||||
|
||||
AZ::SliceComponent::InstantiatedContainer sourceObjects(false);
|
||||
dependentSlice->GetEntities(sourceObjects.m_entities);
|
||||
dependentSlice->GetAllMetadataEntities(sourceObjects.m_metadataEntities);
|
||||
|
||||
const DataPatch& dataPatch = instance.GetDataPatch();
|
||||
auto instantiated =
|
||||
dataPatch.Apply(&sourceObjects, dependentSlice->GetSerializeContext(), filterDesc, sourceDataFlags, targetDataFlags);
|
||||
|
||||
// Run through all the instantiated entities and fix up their parent hierarchy:
|
||||
// - Invalid parents need to get set to the container.
|
||||
// - Valid parents into the top-level instance mean that the nested slice instance is also child-nested under an entity.
|
||||
// Prefabs handle this type of nesting differently - we need to set the parent to the container, and the container's
|
||||
// parent to that other instance.
|
||||
auto containerEntity = nestedInstance->GetContainerEntity();
|
||||
AzToolsFramework::Components::TransformComponent* transformComponent =
|
||||
containerEntity->get().FindComponent<AzToolsFramework::Components::TransformComponent>();
|
||||
if (transformComponent)
|
||||
auto containerEntityId = containerEntity->get().GetId();
|
||||
for (auto entity : instantiated->m_entities)
|
||||
{
|
||||
transformComponent->SetParent(topLevelInstance->GetContainerEntityId());
|
||||
transformComponent->UpdateCachedWorldTransform();
|
||||
AzToolsFramework::Components::TransformComponent* transformComponent =
|
||||
entity->FindComponent<AzToolsFramework::Components::TransformComponent>();
|
||||
if (transformComponent)
|
||||
{
|
||||
bool onlySetIfInvalid = true;
|
||||
auto parentId = transformComponent->GetParentId();
|
||||
if (parentId.IsValid())
|
||||
{
|
||||
auto parentAlias = m_aliasIdMapper.find(TemplateEntityIdPair(topLevelInstance->GetTemplateId(), parentId));
|
||||
if (parentAlias != m_aliasIdMapper.end())
|
||||
{
|
||||
// Set the container's parent to this entity's parent, and set this entity's parent to the container
|
||||
// (i.e. go from A->B to A->container->B)
|
||||
auto newParentId = topLevelInstance->GetEntityId(parentAlias->second);
|
||||
SetParentEntity(containerEntity->get(), newParentId, false);
|
||||
onlySetIfInvalid = false;
|
||||
}
|
||||
}
|
||||
|
||||
SetParentEntity(*entity, containerEntityId, onlySetIfInvalid);
|
||||
}
|
||||
}
|
||||
|
||||
// Replace all the entities in the instance with the new patched ones.
|
||||
// (This is easier than trying to figure out what the patched data changes are - we can let the JSON patch handle it for us)
|
||||
nestedInstance->RemoveNestedEntities(
|
||||
[](const AZStd::unique_ptr<AZ::Entity>&)
|
||||
{
|
||||
return true;
|
||||
});
|
||||
for (auto& entity : instantiated->m_entities)
|
||||
{
|
||||
auto entityAlias = m_aliasIdMapper.find(TemplateEntityIdPair(nestedInstance->GetTemplateId(), entity->GetId()));
|
||||
if (entityAlias != m_aliasIdMapper.end())
|
||||
{
|
||||
nestedInstance->AddEntity(*entity, entityAlias->second);
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Assert(false, "Failed to find entity alias.");
|
||||
nestedInstance->AddEntity(*entity);
|
||||
}
|
||||
}
|
||||
|
||||
// Set the container entity of the nested prefab to have the top-level prefab as the parent if it hasn't already gotten
|
||||
// another entity as its parent.
|
||||
{
|
||||
constexpr bool onlySetIfInvalid = true;
|
||||
SetParentEntity(containerEntity->get(), topLevelInstance->GetContainerEntityId(), onlySetIfInvalid);
|
||||
}
|
||||
|
||||
// Add the nested instance itself to the top-level prefab. To do this, we need to add it to our top-level instance,
|
||||
@@ -395,7 +531,22 @@ namespace AZ
|
||||
AzToolsFramework::Prefab::PrefabDom topLevelInstanceDomBefore;
|
||||
instanceToTemplateInterface->GenerateDomForInstance(topLevelInstanceDomBefore, *topLevelInstance);
|
||||
|
||||
AzToolsFramework::Prefab::Instance& addedInstance = topLevelInstance->AddInstance(AZStd::move(nestedInstance));
|
||||
// When creating the new instance, we would like to have deterministic instance aliases. Prefabs that depend on this one
|
||||
// will have patches that reference the alias, so if we reconvert this slice a second time, we would like it to produce
|
||||
// the same results. To get a deterministic and unique alias, we rely on the slice instance. The slice instance contains
|
||||
// a map of slice entity IDs to unique instance entity IDs. We'll just consistently use the first entry in the map as the
|
||||
// unique instance ID.
|
||||
AZStd::string instanceAlias;
|
||||
auto entityIdMap = instance.GetEntityIdMap();
|
||||
if (!entityIdMap.empty())
|
||||
{
|
||||
instanceAlias = AZStd::string::format("Instance_%s", entityIdMap.begin()->second.ToString().c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
instanceAlias = AZStd::string::format("Instance_%s", AZ::Entity::MakeId().ToString().c_str());
|
||||
}
|
||||
AzToolsFramework::Prefab::Instance& addedInstance = topLevelInstance->AddInstance(AZStd::move(nestedInstance), instanceAlias);
|
||||
|
||||
AzToolsFramework::Prefab::PrefabDom topLevelInstanceDomAfter;
|
||||
instanceToTemplateInterface->GenerateDomForInstance(topLevelInstanceDomAfter, *topLevelInstance);
|
||||
@@ -418,9 +569,26 @@ namespace AZ
|
||||
AzToolsFramework::Prefab::InvalidLinkId);
|
||||
prefabSystemComponentInterface->PropagateTemplateChanges(topLevelInstance->GetTemplateId());
|
||||
|
||||
AZ::Interface<AzToolsFramework::Prefab::InstanceUpdateExecutorInterface>::Get()->UpdateTemplateInstancesInQueue();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SliceConverter::SetParentEntity(const AZ::Entity& entity, const AZ::EntityId& parentId, bool onlySetIfInvalid)
|
||||
{
|
||||
AzToolsFramework::Components::TransformComponent* transformComponent =
|
||||
entity.FindComponent<AzToolsFramework::Components::TransformComponent>();
|
||||
if (transformComponent)
|
||||
{
|
||||
// Only set the parent if we didn't set the onlySetIfInvalid flag, or if we did and the parent is currently invalid
|
||||
if (!onlySetIfInvalid || !transformComponent->GetParentId().IsValid())
|
||||
{
|
||||
transformComponent->SetParent(parentId);
|
||||
transformComponent->UpdateCachedWorldTransform();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SliceConverter::PrintPrefab(AzToolsFramework::Prefab::TemplateId templateId)
|
||||
{
|
||||
auto prefabSystemComponentInterface = AZ::Interface<AzToolsFramework::Prefab::PrefabSystemComponentInterface>::Get();
|
||||
|
||||
@@ -39,24 +39,35 @@ namespace AZ
|
||||
class SliceConverter : public Converter
|
||||
{
|
||||
public:
|
||||
static bool ConvertSliceFiles(Application& application);
|
||||
bool ConvertSliceFiles(Application& application);
|
||||
|
||||
private:
|
||||
static bool ConnectToAssetProcessor();
|
||||
static void DisconnectFromAssetProcessor();
|
||||
using TemplateEntityIdPair = AZStd::pair<AzToolsFramework::Prefab::TemplateId, AZ::EntityId>;
|
||||
|
||||
static bool ConvertSliceFile(AZ::SerializeContext* serializeContext, const AZStd::string& slicePath, bool isDryRun);
|
||||
static bool ConvertSliceToPrefab(
|
||||
bool ConnectToAssetProcessor();
|
||||
void DisconnectFromAssetProcessor();
|
||||
|
||||
bool ConvertSliceFile(AZ::SerializeContext* serializeContext, const AZStd::string& slicePath, bool isDryRun);
|
||||
bool ConvertSliceToPrefab(
|
||||
AZ::SerializeContext* serializeContext, AZ::IO::PathView outputPath, bool isDryRun, AZ::Entity* rootEntity);
|
||||
static void FixPrefabEntities(AZ::Entity& containerEntity, SliceComponent::EntityList& sliceEntities);
|
||||
static bool ConvertNestedSlices(
|
||||
void FixPrefabEntities(AZ::Entity& containerEntity, SliceComponent::EntityList& sliceEntities);
|
||||
bool ConvertNestedSlices(
|
||||
SliceComponent* sliceComponent, AzToolsFramework::Prefab::Instance* sourceInstance,
|
||||
AZ::SerializeContext* serializeContext, bool isDryRun);
|
||||
static bool ConvertSliceInstance(
|
||||
bool ConvertSliceInstance(
|
||||
AZ::SliceComponent::SliceInstance& instance, AZ::Data::Asset<AZ::SliceAsset>& sliceAsset,
|
||||
AzToolsFramework::Prefab::TemplateReference nestedTemplate, AzToolsFramework::Prefab::Instance* topLevelInstance);
|
||||
static void PrintPrefab(AzToolsFramework::Prefab::TemplateId templateId);
|
||||
static bool SavePrefab(AZ::IO::PathView outputPath, AzToolsFramework::Prefab::TemplateId templateId);
|
||||
void SetParentEntity(const AZ::Entity& entity, const AZ::EntityId& parentId, bool onlySetIfInvalid);
|
||||
void PrintPrefab(AzToolsFramework::Prefab::TemplateId templateId);
|
||||
bool SavePrefab(AZ::IO::PathView outputPath, AzToolsFramework::Prefab::TemplateId templateId);
|
||||
|
||||
// Track all of the entity IDs created and the prefab entity aliases that map to them. This mapping is used
|
||||
// with nested slice conversion to remap parent entity IDs to the correct prefab entity IDs.
|
||||
AZStd::unordered_map<TemplateEntityIdPair, AzToolsFramework::Prefab::EntityAlias> m_aliasIdMapper;
|
||||
|
||||
// Track all of the created prefab template IDs on a slice conversion so that they can get removed at the end of the
|
||||
// conversion for that file.
|
||||
AZStd::unordered_set<AzToolsFramework::Prefab::TemplateId> m_createdTemplateIds;
|
||||
};
|
||||
} // namespace SerializeContextTools
|
||||
} // namespace AZ
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <AzToolsFramework/Entity/EditorEntityContextComponent.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
// This class is an inelegant workaround for use by the Slice Converter to selectively disable entity add/remove logic
|
||||
// during slice conversion in the EditorEntityContextComponent. Specifically, the standard versions of these methods will
|
||||
// attempt to activate the entities as they're added. This is both unnecessary and undesirable during slice conversion, since
|
||||
// entity activation requires a lot of subsystems to be active and valid.
|
||||
// Instead, by selectively disabling this logic, the entities can remain in an initialized state, which is sufficient for conversion,
|
||||
// without requiring those extra subsystems.
|
||||
|
||||
// This problem also could have been solved by adding APIs to the EditorEntityContextComponent or the EntityContext, but there aren't
|
||||
// any other known valid use cases for disabling this logic, so the extra APIs would simply encourage "bad behavior" by using them
|
||||
// when they likely aren't necessary or desired.
|
||||
|
||||
class SliceConverterEditorEntityContextComponent
|
||||
: public EditorEntityContextComponent
|
||||
{
|
||||
public:
|
||||
|
||||
AZ_COMPONENT(SliceConverterEditorEntityContextComponent, "{1CB0C38F-8E85-4422-91C6-E1F3B9B4B853}");
|
||||
|
||||
SliceConverterEditorEntityContextComponent() : EditorEntityContextComponent() {}
|
||||
|
||||
// Simple API to selectively disable this logic *only* when performing slice to prefab conversion.
|
||||
static void DisableOnContextEntityLogic()
|
||||
{
|
||||
m_enableOnContextEntityLogic = false;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
void OnContextEntitiesAdded([[maybe_unused]] const EntityList& entities) override
|
||||
{
|
||||
if (m_enableOnContextEntityLogic)
|
||||
{
|
||||
EditorEntityContextComponent::OnContextEntitiesAdded(entities);
|
||||
}
|
||||
}
|
||||
|
||||
void OnContextEntityRemoved([[maybe_unused]] const AZ::EntityId& id) override
|
||||
{
|
||||
if (m_enableOnContextEntityLogic)
|
||||
{
|
||||
EditorEntityContextComponent::OnContextEntityRemoved(id);
|
||||
}
|
||||
}
|
||||
|
||||
// By default, act just like the EditorEntityContextComponent
|
||||
static inline bool m_enableOnContextEntityLogic = true;
|
||||
};
|
||||
} // namespace AzToolsFramework
|
||||
@@ -125,7 +125,8 @@ int main(int argc, char** argv)
|
||||
}
|
||||
else if (AZ::StringFunc::Equal("convert-slice", action.c_str()))
|
||||
{
|
||||
result = SliceConverter::ConvertSliceFiles(application);
|
||||
SliceConverter sliceConverter;
|
||||
result = sliceConverter.ConvertSliceFiles(application);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -17,6 +17,7 @@ set(FILES
|
||||
Dumper.h
|
||||
Dumper.cpp
|
||||
main.cpp
|
||||
SliceConverterEditorEntityContextComponent.h
|
||||
SliceConverter.h
|
||||
SliceConverter.cpp
|
||||
Utilities.h
|
||||
|
||||
@@ -70,6 +70,52 @@ namespace LUAEditor
|
||||
, m_bIsModified(false)
|
||||
, m_bIsBeingSaved(false)
|
||||
, m_PresetLineAtOpen(1){}
|
||||
|
||||
// Copy constructor does not copy over open file handle
|
||||
DocumentInfo(const DocumentInfo& other)
|
||||
: m_assetId(other.m_assetId)
|
||||
, m_scriptAsset(other.m_scriptAsset)
|
||||
, m_assetName(other.m_assetName)
|
||||
, m_displayName(other.m_displayName)
|
||||
, m_lastKnownModTime(other.m_lastKnownModTime)
|
||||
, m_sourceControlInfo(other.m_sourceControlInfo)
|
||||
, m_bSourceControl_Ready(other.m_bSourceControl_Ready)
|
||||
, m_bSourceControl_BusyGettingStats(other.m_bSourceControl_BusyGettingStats)
|
||||
, m_bSourceControl_BusyRequestingEdit(other.m_bSourceControl_BusyRequestingEdit)
|
||||
, m_bSourceControl_CanWrite(other.m_bSourceControl_CanWrite)
|
||||
, m_bSourceControl_CanCheckOut(other.m_bSourceControl_CanCheckOut)
|
||||
, m_bDataIsLoaded(other.m_bDataIsLoaded)
|
||||
, m_bDataIsWritten(other.m_bDataIsWritten)
|
||||
, m_bCloseAfterSave(other.m_bCloseAfterSave)
|
||||
, m_bUntitledDocument(other.m_bUntitledDocument)
|
||||
, m_bIsModified(other.m_bIsModified)
|
||||
, m_bIsBeingSaved(other.m_bIsBeingSaved)
|
||||
, m_PresetLineAtOpen(other.m_PresetLineAtOpen)
|
||||
{}
|
||||
|
||||
DocumentInfo& operator=(const DocumentInfo& other)
|
||||
{
|
||||
m_assetId = other.m_assetId;
|
||||
m_scriptAsset = other.m_scriptAsset;
|
||||
m_assetName = other.m_assetName;
|
||||
m_displayName = other.m_displayName;
|
||||
m_lastKnownModTime = other.m_lastKnownModTime;
|
||||
m_sourceControlInfo = other.m_sourceControlInfo;
|
||||
m_bSourceControl_Ready = other.m_bSourceControl_Ready;
|
||||
m_bSourceControl_BusyGettingStats = other.m_bSourceControl_BusyGettingStats;
|
||||
m_bSourceControl_BusyRequestingEdit = other.m_bSourceControl_BusyRequestingEdit;
|
||||
m_bSourceControl_CanWrite = other.m_bSourceControl_CanWrite;
|
||||
m_bSourceControl_CanCheckOut = other.m_bSourceControl_CanCheckOut;
|
||||
m_bDataIsLoaded = other.m_bDataIsLoaded;
|
||||
m_bDataIsWritten = other.m_bDataIsWritten;
|
||||
m_bCloseAfterSave = other.m_bCloseAfterSave;
|
||||
m_bUntitledDocument = other.m_bUntitledDocument;
|
||||
m_bIsModified = other.m_bIsModified;
|
||||
m_bIsBeingSaved = other.m_bIsBeingSaved;
|
||||
m_PresetLineAtOpen = other.m_PresetLineAtOpen;
|
||||
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
class ContextInterface
|
||||
|
||||
@@ -48,7 +48,8 @@ class ConfigurationManager(object):
|
||||
def configuration(self, new_configuration: ConfigurationManager) -> None:
|
||||
self._configuration = new_configuration
|
||||
|
||||
def setup(self, config_path: str) -> None:
|
||||
def setup(self, config_path: str) -> bool:
|
||||
result: bool = True
|
||||
logger.info("Setting up default configuration ...")
|
||||
try:
|
||||
normalized_config_path: str = file_utils.normalize_file_path(config_path);
|
||||
@@ -63,5 +64,7 @@ class ConfigurationManager(object):
|
||||
self._configuration.account_id = aws_utils.get_default_account_id()
|
||||
self._configuration.region = aws_utils.get_default_region()
|
||||
except (RuntimeError, FileNotFoundError) as e:
|
||||
logger.exception(e)
|
||||
logger.error(e)
|
||||
result = False
|
||||
logger.debug(self._configuration)
|
||||
return result
|
||||
|
||||
@@ -74,11 +74,18 @@ if __name__ == "__main__":
|
||||
logger.warning("Failed to load style sheet for resource mapping tool")
|
||||
|
||||
logger.info("Initializing boto3 default session ...")
|
||||
aws_utils.setup_default_session(arguments.profile)
|
||||
try:
|
||||
aws_utils.setup_default_session(arguments.profile)
|
||||
except RuntimeError as error:
|
||||
logger.error(error)
|
||||
environment_utils.cleanup_qt_environment()
|
||||
exit(-1)
|
||||
|
||||
logger.info("Initializing configuration manager ...")
|
||||
configuration_manager: ConfigurationManager = ConfigurationManager()
|
||||
configuration_manager.setup(arguments.config_path)
|
||||
if not configuration_manager.setup(arguments.config_path):
|
||||
environment_utils.cleanup_qt_environment()
|
||||
exit(-1)
|
||||
|
||||
logger.info("Initializing thread manager ...")
|
||||
thread_manager: ThreadManager = ThreadManager()
|
||||
|
||||
+1238
-1124
File diff suppressed because it is too large
Load Diff
@@ -12,10 +12,10 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
import boto3
|
||||
from botocore.paginate import (PageIterator, Paginator)
|
||||
from botocore.client import BaseClient
|
||||
from botocore.exceptions import ClientError
|
||||
from botocore.exceptions import (ClientError, ConfigNotFound, NoCredentialsError, ProfileNotFound)
|
||||
from typing import Dict, List
|
||||
|
||||
from model import (constants, error_messages)
|
||||
from model import error_messages
|
||||
from model.basic_resource_attributes import (BasicResourceAttributes, BasicResourceAttributesBuilder)
|
||||
|
||||
"""
|
||||
@@ -65,8 +65,11 @@ def _initialize_boto3_aws_client(service: str, region: str = "") -> BaseClient:
|
||||
|
||||
|
||||
def setup_default_session(profile: str) -> None:
|
||||
global default_session
|
||||
default_session = boto3.session.Session(profile_name=profile)
|
||||
try:
|
||||
global default_session
|
||||
default_session = boto3.session.Session(profile_name=profile)
|
||||
except (ConfigNotFound, ProfileNotFound) as error:
|
||||
raise RuntimeError(error)
|
||||
|
||||
|
||||
def get_default_account_id() -> str:
|
||||
@@ -76,6 +79,8 @@ def get_default_account_id() -> str:
|
||||
except ClientError as error:
|
||||
raise RuntimeError(error_messages.AWS_SERVICE_REQUEST_CLIENT_ERROR_MESSAGE.format(
|
||||
"get_caller_identity", error.response['Error']['Code'], error.response['Error']['Message']))
|
||||
except NoCredentialsError as error:
|
||||
raise RuntimeError(error)
|
||||
|
||||
|
||||
def get_default_region() -> str:
|
||||
|
||||
@@ -8,7 +8,8 @@
|
||||
"Name": "IBLSpecular",
|
||||
"Description": "The input cubemap generates an IBL specular output cubemap.",
|
||||
"FileMasks": [
|
||||
"_iblspecularcm"
|
||||
"_iblspecularcm",
|
||||
"_iblspecularcm256"
|
||||
],
|
||||
"SourceColor": "Linear",
|
||||
"DestColor": "Linear",
|
||||
@@ -34,7 +35,8 @@
|
||||
"UUID": "{908DA68C-97FB-4C4A-97BC-5A55F30F14FA}",
|
||||
"Name": "IBLSpecular",
|
||||
"FileMasks": [
|
||||
"_iblspecularcm"
|
||||
"_iblspecularcm",
|
||||
"_iblspecularcm256"
|
||||
],
|
||||
"SourceColor": "Linear",
|
||||
"DestColor": "Linear",
|
||||
@@ -59,7 +61,8 @@
|
||||
"UUID": "{908DA68C-97FB-4C4A-97BC-5A55F30F14FA}",
|
||||
"Name": "IBLSpecular",
|
||||
"FileMasks": [
|
||||
"_iblspecularcm"
|
||||
"_iblspecularcm",
|
||||
"_iblspecularcm256"
|
||||
],
|
||||
"SourceColor": "Linear",
|
||||
"DestColor": "Linear",
|
||||
@@ -84,7 +87,8 @@
|
||||
"UUID": "{908DA68C-97FB-4C4A-97BC-5A55F30F14FA}",
|
||||
"Name": "IBLSpecular",
|
||||
"FileMasks": [
|
||||
"_iblspecularcm"
|
||||
"_iblspecularcm",
|
||||
"_iblspecularcm256"
|
||||
],
|
||||
"SourceColor": "Linear",
|
||||
"DestColor": "Linear",
|
||||
@@ -109,7 +113,8 @@
|
||||
"UUID": "{908DA68C-97FB-4C4A-97BC-5A55F30F14FA}",
|
||||
"Name": "IBLSpecular",
|
||||
"FileMasks": [
|
||||
"_iblspecularcm"
|
||||
"_iblspecularcm",
|
||||
"_iblspecularcm256"
|
||||
],
|
||||
"SourceColor": "Linear",
|
||||
"DestColor": "Linear",
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
{
|
||||
"Type": "JsonSerialization",
|
||||
"Version": 1,
|
||||
"ClassName": "MultiplatformPresetSettings",
|
||||
"ClassData": {
|
||||
"DefaultPreset": {
|
||||
"UUID": "{B66395E1-8D0E-4159-989B-FC2B9F091B75}",
|
||||
"Name": "IBLSpecularHigh",
|
||||
"Description": "The input cubemap generates an IBL specular output cubemap.",
|
||||
"FileMasks": [
|
||||
"_iblspecularcm512"
|
||||
],
|
||||
"SourceColor": "Linear",
|
||||
"DestColor": "Linear",
|
||||
"SuppressEngineReduce": true,
|
||||
"PixelFormat": "R9G9B9E5",
|
||||
"DiscardAlpha": true,
|
||||
"MinTextureSize": 512,
|
||||
"MaxTextureSize": 512,
|
||||
"IsPowerOf2": true,
|
||||
"CubemapSettings": {
|
||||
"Filter": "GGX",
|
||||
"MipAngle": 7.0,
|
||||
"MipSlope": 2.299999952316284,
|
||||
"EdgeFixup": -431602080.0,
|
||||
"SubId": 2000
|
||||
},
|
||||
"MipMapSetting": {
|
||||
"MipGenType": "Box"
|
||||
}
|
||||
},
|
||||
"PlatformsPresets": {
|
||||
"android": {
|
||||
"UUID": "{B66395E1-8D0E-4159-989B-FC2B9F091B75}",
|
||||
"Name": "IBLSpecularHigh",
|
||||
"FileMasks": [
|
||||
"_iblspecularcm512"
|
||||
],
|
||||
"SourceColor": "Linear",
|
||||
"DestColor": "Linear",
|
||||
"SuppressEngineReduce": true,
|
||||
"PixelFormat": "R9G9B9E5",
|
||||
"DiscardAlpha": true,
|
||||
"MinTextureSize": 512,
|
||||
"MaxTextureSize": 512,
|
||||
"IsPowerOf2": true,
|
||||
"CubemapSettings": {
|
||||
"Filter": "GGX",
|
||||
"MipAngle": 7.0,
|
||||
"MipSlope": 2.299999952316284,
|
||||
"EdgeFixup": -431602080.0,
|
||||
"SubId": 2000
|
||||
},
|
||||
"MipMapSetting": {
|
||||
"MipGenType": "Box"
|
||||
}
|
||||
},
|
||||
"ios": {
|
||||
"UUID": "{B66395E1-8D0E-4159-989B-FC2B9F091B75}",
|
||||
"Name": "IBLSpecularHigh",
|
||||
"FileMasks": [
|
||||
"_iblspecularcm512"
|
||||
],
|
||||
"SourceColor": "Linear",
|
||||
"DestColor": "Linear",
|
||||
"SuppressEngineReduce": true,
|
||||
"PixelFormat": "R9G9B9E5",
|
||||
"DiscardAlpha": true,
|
||||
"MinTextureSize": 512,
|
||||
"MaxTextureSize": 512,
|
||||
"IsPowerOf2": true,
|
||||
"CubemapSettings": {
|
||||
"Filter": "GGX",
|
||||
"MipAngle": 7.0,
|
||||
"MipSlope": 2.299999952316284,
|
||||
"EdgeFixup": -431602080.0,
|
||||
"SubId": 2000
|
||||
},
|
||||
"MipMapSetting": {
|
||||
"MipGenType": "Box"
|
||||
}
|
||||
},
|
||||
"mac": {
|
||||
"UUID": "{B66395E1-8D0E-4159-989B-FC2B9F091B75}",
|
||||
"Name": "IBLSpecularHigh",
|
||||
"FileMasks": [
|
||||
"_iblspecularcm512"
|
||||
],
|
||||
"SourceColor": "Linear",
|
||||
"DestColor": "Linear",
|
||||
"SuppressEngineReduce": true,
|
||||
"PixelFormat": "R9G9B9E5",
|
||||
"DiscardAlpha": true,
|
||||
"MinTextureSize": 512,
|
||||
"MaxTextureSize": 512,
|
||||
"IsPowerOf2": true,
|
||||
"CubemapSettings": {
|
||||
"Filter": "GGX",
|
||||
"MipAngle": 7.0,
|
||||
"MipSlope": 2.299999952316284,
|
||||
"EdgeFixup": -431602080.0,
|
||||
"SubId": 2000
|
||||
},
|
||||
"MipMapSetting": {
|
||||
"MipGenType": "Box"
|
||||
}
|
||||
},
|
||||
"provo": {
|
||||
"UUID": "{B66395E1-8D0E-4159-989B-FC2B9F091B75}",
|
||||
"Name": "IBLSpecularHigh",
|
||||
"FileMasks": [
|
||||
"_iblspecularcm512"
|
||||
],
|
||||
"SourceColor": "Linear",
|
||||
"DestColor": "Linear",
|
||||
"SuppressEngineReduce": true,
|
||||
"PixelFormat": "R9G9B9E5",
|
||||
"DiscardAlpha": true,
|
||||
"MinTextureSize": 512,
|
||||
"MaxTextureSize": 512,
|
||||
"IsPowerOf2": true,
|
||||
"CubemapSettings": {
|
||||
"Filter": "GGX",
|
||||
"MipAngle": 7.0,
|
||||
"MipSlope": 2.299999952316284,
|
||||
"EdgeFixup": -431602080.0,
|
||||
"SubId": 2000
|
||||
},
|
||||
"MipMapSetting": {
|
||||
"MipGenType": "Box"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
{
|
||||
"Type": "JsonSerialization",
|
||||
"Version": 1,
|
||||
"ClassName": "MultiplatformPresetSettings",
|
||||
"ClassData": {
|
||||
"DefaultPreset": {
|
||||
"UUID": "{7273ACAE-6E34-487C-AF71-99423A6E1CB0}",
|
||||
"Name": "IBLSpecularLow",
|
||||
"Description": "The input cubemap generates an IBL specular output cubemap.",
|
||||
"FileMasks": [
|
||||
"_iblspecularcm128"
|
||||
],
|
||||
"SourceColor": "Linear",
|
||||
"DestColor": "Linear",
|
||||
"SuppressEngineReduce": true,
|
||||
"PixelFormat": "R9G9B9E5",
|
||||
"DiscardAlpha": true,
|
||||
"MinTextureSize": 128,
|
||||
"MaxTextureSize": 128,
|
||||
"IsPowerOf2": true,
|
||||
"CubemapSettings": {
|
||||
"Filter": "GGX",
|
||||
"MipAngle": 7.0,
|
||||
"MipSlope": 2.299999952316284,
|
||||
"EdgeFixup": -431602080.0,
|
||||
"SubId": 2000
|
||||
},
|
||||
"MipMapSetting": {
|
||||
"MipGenType": "Box"
|
||||
}
|
||||
},
|
||||
"PlatformsPresets": {
|
||||
"android": {
|
||||
"UUID": "{7273ACAE-6E34-487C-AF71-99423A6E1CB0}",
|
||||
"Name": "IBLSpecularLow",
|
||||
"FileMasks": [
|
||||
"_iblspecularcm128"
|
||||
],
|
||||
"SourceColor": "Linear",
|
||||
"DestColor": "Linear",
|
||||
"SuppressEngineReduce": true,
|
||||
"PixelFormat": "R9G9B9E5",
|
||||
"DiscardAlpha": true,
|
||||
"MinTextureSize": 128,
|
||||
"MaxTextureSize": 128,
|
||||
"IsPowerOf2": true,
|
||||
"CubemapSettings": {
|
||||
"Filter": "GGX",
|
||||
"MipAngle": 7.0,
|
||||
"MipSlope": 2.299999952316284,
|
||||
"EdgeFixup": -431602080.0,
|
||||
"SubId": 2000
|
||||
},
|
||||
"MipMapSetting": {
|
||||
"MipGenType": "Box"
|
||||
}
|
||||
},
|
||||
"ios": {
|
||||
"UUID": "{7273ACAE-6E34-487C-AF71-99423A6E1CB0}",
|
||||
"Name": "IBLSpecularLow",
|
||||
"FileMasks": [
|
||||
"_iblspecularcm128"
|
||||
],
|
||||
"SourceColor": "Linear",
|
||||
"DestColor": "Linear",
|
||||
"SuppressEngineReduce": true,
|
||||
"PixelFormat": "R9G9B9E5",
|
||||
"DiscardAlpha": true,
|
||||
"MinTextureSize": 128,
|
||||
"MaxTextureSize": 128,
|
||||
"IsPowerOf2": true,
|
||||
"CubemapSettings": {
|
||||
"Filter": "GGX",
|
||||
"MipAngle": 7.0,
|
||||
"MipSlope": 2.299999952316284,
|
||||
"EdgeFixup": -431602080.0,
|
||||
"SubId": 2000
|
||||
},
|
||||
"MipMapSetting": {
|
||||
"MipGenType": "Box"
|
||||
}
|
||||
},
|
||||
"mac": {
|
||||
"UUID": "{7273ACAE-6E34-487C-AF71-99423A6E1CB0}",
|
||||
"Name": "IBLSpecularLow",
|
||||
"FileMasks": [
|
||||
"_iblspecularcm128"
|
||||
],
|
||||
"SourceColor": "Linear",
|
||||
"DestColor": "Linear",
|
||||
"SuppressEngineReduce": true,
|
||||
"PixelFormat": "R9G9B9E5",
|
||||
"DiscardAlpha": true,
|
||||
"MinTextureSize": 128,
|
||||
"MaxTextureSize": 128,
|
||||
"IsPowerOf2": true,
|
||||
"CubemapSettings": {
|
||||
"Filter": "GGX",
|
||||
"MipAngle": 7.0,
|
||||
"MipSlope": 2.299999952316284,
|
||||
"EdgeFixup": -431602080.0,
|
||||
"SubId": 2000
|
||||
},
|
||||
"MipMapSetting": {
|
||||
"MipGenType": "Box"
|
||||
}
|
||||
},
|
||||
"provo": {
|
||||
"UUID": "{7273ACAE-6E34-487C-AF71-99423A6E1CB0}",
|
||||
"Name": "IBLSpecularLow",
|
||||
"FileMasks": [
|
||||
"_iblspecularcm128"
|
||||
],
|
||||
"SourceColor": "Linear",
|
||||
"DestColor": "Linear",
|
||||
"SuppressEngineReduce": true,
|
||||
"PixelFormat": "R9G9B9E5",
|
||||
"DiscardAlpha": true,
|
||||
"MinTextureSize": 128,
|
||||
"MaxTextureSize": 128,
|
||||
"IsPowerOf2": true,
|
||||
"CubemapSettings": {
|
||||
"Filter": "GGX",
|
||||
"MipAngle": 7.0,
|
||||
"MipSlope": 2.299999952316284,
|
||||
"EdgeFixup": -431602080.0,
|
||||
"SubId": 2000
|
||||
},
|
||||
"MipMapSetting": {
|
||||
"MipGenType": "Box"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
{
|
||||
"Type": "JsonSerialization",
|
||||
"Version": 1,
|
||||
"ClassName": "MultiplatformPresetSettings",
|
||||
"ClassData": {
|
||||
"DefaultPreset": {
|
||||
"UUID": "{5CD1AFA6-915B-4716-893C-A5B1F4074C22}",
|
||||
"Name": "IBLSpecularVeryHigh",
|
||||
"Description": "The input cubemap generates an IBL specular output cubemap.",
|
||||
"FileMasks": [
|
||||
"_iblspecularcm1024"
|
||||
],
|
||||
"SourceColor": "Linear",
|
||||
"DestColor": "Linear",
|
||||
"SuppressEngineReduce": true,
|
||||
"PixelFormat": "R9G9B9E5",
|
||||
"DiscardAlpha": true,
|
||||
"MinTextureSize": 1024,
|
||||
"MaxTextureSize": 1024,
|
||||
"IsPowerOf2": true,
|
||||
"CubemapSettings": {
|
||||
"Filter": "GGX",
|
||||
"MipAngle": 7.0,
|
||||
"MipSlope": 2.299999952316284,
|
||||
"EdgeFixup": -431602080.0,
|
||||
"SubId": 2000
|
||||
},
|
||||
"MipMapSetting": {
|
||||
"MipGenType": "Box"
|
||||
}
|
||||
},
|
||||
"PlatformsPresets": {
|
||||
"android": {
|
||||
"UUID": "{5CD1AFA6-915B-4716-893C-A5B1F4074C22}",
|
||||
"Name": "IBLSpecularVeryHigh",
|
||||
"FileMasks": [
|
||||
"_iblspecularcm1024"
|
||||
],
|
||||
"SourceColor": "Linear",
|
||||
"DestColor": "Linear",
|
||||
"SuppressEngineReduce": true,
|
||||
"PixelFormat": "R9G9B9E5",
|
||||
"DiscardAlpha": true,
|
||||
"MinTextureSize": 1024,
|
||||
"MaxTextureSize": 1024,
|
||||
"IsPowerOf2": true,
|
||||
"CubemapSettings": {
|
||||
"Filter": "GGX",
|
||||
"MipAngle": 7.0,
|
||||
"MipSlope": 2.299999952316284,
|
||||
"EdgeFixup": -431602080.0,
|
||||
"SubId": 2000
|
||||
},
|
||||
"MipMapSetting": {
|
||||
"MipGenType": "Box"
|
||||
}
|
||||
},
|
||||
"ios": {
|
||||
"UUID": "{5CD1AFA6-915B-4716-893C-A5B1F4074C22}",
|
||||
"Name": "IBLSpecularVeryHigh",
|
||||
"FileMasks": [
|
||||
"_iblspecularcm1024"
|
||||
],
|
||||
"SourceColor": "Linear",
|
||||
"DestColor": "Linear",
|
||||
"SuppressEngineReduce": true,
|
||||
"PixelFormat": "R9G9B9E5",
|
||||
"DiscardAlpha": true,
|
||||
"MinTextureSize": 1024,
|
||||
"MaxTextureSize": 1024,
|
||||
"IsPowerOf2": true,
|
||||
"CubemapSettings": {
|
||||
"Filter": "GGX",
|
||||
"MipAngle": 7.0,
|
||||
"MipSlope": 2.299999952316284,
|
||||
"EdgeFixup": -431602080.0,
|
||||
"SubId": 2000
|
||||
},
|
||||
"MipMapSetting": {
|
||||
"MipGenType": "Box"
|
||||
}
|
||||
},
|
||||
"mac": {
|
||||
"UUID": "{5CD1AFA6-915B-4716-893C-A5B1F4074C22}",
|
||||
"Name": "IBLSpecularVeryHigh",
|
||||
"FileMasks": [
|
||||
"_iblspecularcm1024"
|
||||
],
|
||||
"SourceColor": "Linear",
|
||||
"DestColor": "Linear",
|
||||
"SuppressEngineReduce": true,
|
||||
"PixelFormat": "R9G9B9E5",
|
||||
"DiscardAlpha": true,
|
||||
"MinTextureSize": 1024,
|
||||
"MaxTextureSize": 1024,
|
||||
"IsPowerOf2": true,
|
||||
"CubemapSettings": {
|
||||
"Filter": "GGX",
|
||||
"MipAngle": 7.0,
|
||||
"MipSlope": 2.299999952316284,
|
||||
"EdgeFixup": -431602080.0,
|
||||
"SubId": 2000
|
||||
},
|
||||
"MipMapSetting": {
|
||||
"MipGenType": "Box"
|
||||
}
|
||||
},
|
||||
"provo": {
|
||||
"UUID": "{5CD1AFA6-915B-4716-893C-A5B1F4074C22}",
|
||||
"Name": "IBLSpecularVeryHigh",
|
||||
"FileMasks": [
|
||||
"_iblspecularcm1024"
|
||||
],
|
||||
"SourceColor": "Linear",
|
||||
"DestColor": "Linear",
|
||||
"SuppressEngineReduce": true,
|
||||
"PixelFormat": "R9G9B9E5",
|
||||
"DiscardAlpha": true,
|
||||
"MinTextureSize": 1024,
|
||||
"MaxTextureSize": 1024,
|
||||
"IsPowerOf2": true,
|
||||
"CubemapSettings": {
|
||||
"Filter": "GGX",
|
||||
"MipAngle": 7.0,
|
||||
"MipSlope": 2.299999952316284,
|
||||
"EdgeFixup": -431602080.0,
|
||||
"SubId": 2000
|
||||
},
|
||||
"MipMapSetting": {
|
||||
"MipGenType": "Box"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
{
|
||||
"Type": "JsonSerialization",
|
||||
"Version": 1,
|
||||
"ClassName": "MultiplatformPresetSettings",
|
||||
"ClassData": {
|
||||
"DefaultPreset": {
|
||||
"UUID": "{8293C236-D3E8-4352-8B18-C2E82EEE6547}",
|
||||
"Name": "IBLSpecularVeryLow",
|
||||
"Description": "The input cubemap generates an IBL specular output cubemap.",
|
||||
"FileMasks": [
|
||||
"_iblspecularcm64"
|
||||
],
|
||||
"SourceColor": "Linear",
|
||||
"DestColor": "Linear",
|
||||
"SuppressEngineReduce": true,
|
||||
"PixelFormat": "R9G9B9E5",
|
||||
"DiscardAlpha": true,
|
||||
"MinTextureSize": 64,
|
||||
"MaxTextureSize": 64,
|
||||
"IsPowerOf2": true,
|
||||
"CubemapSettings": {
|
||||
"Filter": "GGX",
|
||||
"MipAngle": 7.0,
|
||||
"MipSlope": 2.299999952316284,
|
||||
"EdgeFixup": -431602080.0,
|
||||
"SubId": 2000
|
||||
},
|
||||
"MipMapSetting": {
|
||||
"MipGenType": "Box"
|
||||
}
|
||||
},
|
||||
"PlatformsPresets": {
|
||||
"android": {
|
||||
"UUID": "{8293C236-D3E8-4352-8B18-C2E82EEE6547}",
|
||||
"Name": "IBLSpecularVeryLow",
|
||||
"FileMasks": [
|
||||
"_iblspecularcm64"
|
||||
],
|
||||
"SourceColor": "Linear",
|
||||
"DestColor": "Linear",
|
||||
"SuppressEngineReduce": true,
|
||||
"PixelFormat": "R9G9B9E5",
|
||||
"DiscardAlpha": true,
|
||||
"MinTextureSize": 64,
|
||||
"MaxTextureSize": 64,
|
||||
"IsPowerOf2": true,
|
||||
"CubemapSettings": {
|
||||
"Filter": "GGX",
|
||||
"MipAngle": 7.0,
|
||||
"MipSlope": 2.299999952316284,
|
||||
"EdgeFixup": -431602080.0,
|
||||
"SubId": 2000
|
||||
},
|
||||
"MipMapSetting": {
|
||||
"MipGenType": "Box"
|
||||
}
|
||||
},
|
||||
"ios": {
|
||||
"UUID": "{8293C236-D3E8-4352-8B18-C2E82EEE6547}",
|
||||
"Name": "IBLSpecularVeryLow",
|
||||
"FileMasks": [
|
||||
"_iblspecularcm64"
|
||||
],
|
||||
"SourceColor": "Linear",
|
||||
"DestColor": "Linear",
|
||||
"SuppressEngineReduce": true,
|
||||
"PixelFormat": "R9G9B9E5",
|
||||
"DiscardAlpha": true,
|
||||
"MinTextureSize": 64,
|
||||
"MaxTextureSize": 64,
|
||||
"IsPowerOf2": true,
|
||||
"CubemapSettings": {
|
||||
"Filter": "GGX",
|
||||
"MipAngle": 7.0,
|
||||
"MipSlope": 2.299999952316284,
|
||||
"EdgeFixup": -431602080.0,
|
||||
"SubId": 2000
|
||||
},
|
||||
"MipMapSetting": {
|
||||
"MipGenType": "Box"
|
||||
}
|
||||
},
|
||||
"mac": {
|
||||
"UUID": "{8293C236-D3E8-4352-8B18-C2E82EEE6547}",
|
||||
"Name": "IBLSpecularVeryLow",
|
||||
"FileMasks": [
|
||||
"_iblspecularcm64"
|
||||
],
|
||||
"SourceColor": "Linear",
|
||||
"DestColor": "Linear",
|
||||
"SuppressEngineReduce": true,
|
||||
"PixelFormat": "R9G9B9E5",
|
||||
"DiscardAlpha": true,
|
||||
"MinTextureSize": 64,
|
||||
"MaxTextureSize": 64,
|
||||
"IsPowerOf2": true,
|
||||
"CubemapSettings": {
|
||||
"Filter": "GGX",
|
||||
"MipAngle": 7.0,
|
||||
"MipSlope": 2.299999952316284,
|
||||
"EdgeFixup": -431602080.0,
|
||||
"SubId": 2000
|
||||
},
|
||||
"MipMapSetting": {
|
||||
"MipGenType": "Box"
|
||||
}
|
||||
},
|
||||
"provo": {
|
||||
"UUID": "{8293C236-D3E8-4352-8B18-C2E82EEE6547}",
|
||||
"Name": "IBLSpecularVeryLow",
|
||||
"FileMasks": [
|
||||
"_iblspecularcm64"
|
||||
],
|
||||
"SourceColor": "Linear",
|
||||
"DestColor": "Linear",
|
||||
"SuppressEngineReduce": true,
|
||||
"PixelFormat": "R9G9B9E5",
|
||||
"DiscardAlpha": true,
|
||||
"MinTextureSize": 64,
|
||||
"MaxTextureSize": 64,
|
||||
"IsPowerOf2": true,
|
||||
"CubemapSettings": {
|
||||
"Filter": "GGX",
|
||||
"MipAngle": 7.0,
|
||||
"MipSlope": 2.299999952316284,
|
||||
"EdgeFixup": -431602080.0,
|
||||
"SubId": 2000
|
||||
},
|
||||
"MipMapSetting": {
|
||||
"MipGenType": "Box"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:a476e99b55cf2a76fef6775c5a57dad29f8ffcb942c625bab04c89051a72a560
|
||||
size 62626
|
||||
oid sha256:838830c99f344f5b68e5e85c9bc52751350caf48e662c9c2b767ab77039bbd8f
|
||||
size 103472
|
||||
|
||||
-2
@@ -46,8 +46,6 @@ namespace AZ
|
||||
uint16_t m_padding; // Explicit padding.
|
||||
};
|
||||
|
||||
static constexpr size_t size = sizeof(DiskLightData);
|
||||
|
||||
//! DiskLightFeatureProcessorInterface provides an interface to acquire, release, and update a disk light. This is necessary for code outside of
|
||||
//! the Atom features gem to communicate with the DiskLightFeatureProcessor.
|
||||
class DiskLightFeatureProcessorInterface
|
||||
|
||||
@@ -45,7 +45,7 @@ namespace AZ
|
||||
Fence* fenceToSignal)
|
||||
{
|
||||
AZStd::vector<VkCommandBuffer> vkCommandBuffers;
|
||||
AZStd::vector<VkSemaphore> vkWaitSemaphores;
|
||||
AZStd::vector<VkSemaphore> vkWaitSemaphoreVector; // vulkan.h has a #define called vkWaitSemaphores, so we name this differently
|
||||
AZStd::vector<VkPipelineStageFlags> vkWaitPipelineStages;
|
||||
AZStd::vector<VkSemaphore> vkSignalSemaphores;
|
||||
VkSubmitInfo submitInfo;
|
||||
@@ -65,11 +65,11 @@ namespace AZ
|
||||
return item->GetNativeSemaphore();
|
||||
});
|
||||
vkWaitPipelineStages.reserve(waitSemaphoresInfo.size());
|
||||
vkWaitSemaphores.reserve(waitSemaphoresInfo.size());
|
||||
vkWaitSemaphoreVector.reserve(waitSemaphoresInfo.size());
|
||||
AZStd::for_each(waitSemaphoresInfo.begin(), waitSemaphoresInfo.end(), [&](auto& item)
|
||||
{
|
||||
vkWaitPipelineStages.push_back(item.first);
|
||||
vkWaitSemaphores.push_back(item.second->GetNativeSemaphore());
|
||||
vkWaitSemaphoreVector.push_back(item.second->GetNativeSemaphore());
|
||||
// Wait until the wait semaphores has been submitted for signaling.
|
||||
item.second->WaitEvent();
|
||||
});
|
||||
@@ -77,8 +77,8 @@ namespace AZ
|
||||
submitInfo = {};
|
||||
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
||||
submitInfo.pNext = nullptr;
|
||||
submitInfo.waitSemaphoreCount = static_cast<uint32_t>(vkWaitSemaphores.size());
|
||||
submitInfo.pWaitSemaphores = vkWaitSemaphores.empty() ? nullptr : vkWaitSemaphores.data();
|
||||
submitInfo.waitSemaphoreCount = static_cast<uint32_t>(vkWaitSemaphoreVector.size());
|
||||
submitInfo.pWaitSemaphores = vkWaitSemaphoreVector.empty() ? nullptr : vkWaitSemaphoreVector.data();
|
||||
submitInfo.pWaitDstStageMask = vkWaitPipelineStages.empty() ? nullptr : vkWaitPipelineStages.data();
|
||||
submitInfo.commandBufferCount = static_cast<uint32_t>(vkCommandBuffers.size());
|
||||
submitInfo.pCommandBuffers = vkCommandBuffers.empty() ? nullptr : vkCommandBuffers.data();
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
*
|
||||
*/
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderAsset.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderCommonTypes.h>
|
||||
|
||||
#include <AzCore/Casting/numeric_cast.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
@@ -34,10 +35,6 @@ namespace AZ
|
||||
|
||||
uint32_t ShaderAsset::MakeAssetProductSubId(uint32_t rhiApiUniqueIndex, uint32_t subProductType)
|
||||
{
|
||||
static constexpr uint32_t RhiIndexBitPosition = 30;
|
||||
static constexpr uint32_t RhiIndexNumBits = 32 - RhiIndexBitPosition;
|
||||
static constexpr uint32_t RhiIndexMaxValue = (1 << RhiIndexNumBits) - 1;
|
||||
|
||||
static constexpr uint32_t SubProductTypeBitPosition = 0;
|
||||
static constexpr uint32_t SubProductTypeNumBits = RhiIndexBitPosition - SubProductTypeBitPosition;
|
||||
static constexpr uint32_t SubProductTypeMaxValue = (1 << SubProductTypeNumBits) - 1;
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
*
|
||||
*/
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderVariantAsset.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderCommonTypes.h>
|
||||
|
||||
#include <AzCore/Casting/numeric_cast.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
@@ -24,10 +25,6 @@ namespace AZ
|
||||
uint32_t ShaderVariantAsset::MakeAssetProductSubId(
|
||||
uint32_t rhiApiUniqueIndex, ShaderVariantStableId variantStableId, uint32_t subProductType)
|
||||
{
|
||||
static constexpr uint32_t RhiIndexBitPosition = 30;
|
||||
static constexpr uint32_t RhiIndexNumBits = 32 - RhiIndexBitPosition;
|
||||
static constexpr uint32_t RhiIndexMaxValue = (1 << RhiIndexNumBits) - 1;
|
||||
|
||||
static constexpr uint32_t SubProductTypeBitPosition = 17;
|
||||
static constexpr uint32_t SubProductTypeNumBits = RhiIndexBitPosition - SubProductTypeBitPosition;
|
||||
static constexpr uint32_t SubProductTypeMaxValue = (1 << SubProductTypeNumBits) - 1;
|
||||
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/EBus/EBus.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace LyIntegration
|
||||
{
|
||||
namespace Thumbnails
|
||||
{
|
||||
//! ThumbnailFeatureProcessorProviderRequests allows registering custom Feature Processors for thumbnail generation
|
||||
//! Duplicates will be ignored
|
||||
//! You can check minimal feature processors that are already registered in CommonThumbnailRenderer.cpp
|
||||
class ThumbnailFeatureProcessorProviderRequests
|
||||
: public AZ::EBusTraits
|
||||
{
|
||||
public:
|
||||
//! Get a list of custom feature processors to register with thumbnail renderer
|
||||
virtual const AZStd::vector<AZStd::string>& GetCustomFeatureProcessors() const = 0;
|
||||
};
|
||||
|
||||
using ThumbnailFeatureProcessorProviderBus = AZ::EBus<ThumbnailFeatureProcessorProviderRequests>;
|
||||
} // namespace Thumbnails
|
||||
} // namespace LyIntegration
|
||||
} // namespace AZ
|
||||
@@ -54,13 +54,14 @@ namespace AZ
|
||||
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
|
||||
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
|
||||
->DataElement(AZ::Edit::UIHandlers::Default, &GridComponentConfig::m_gridSize, "Grid Size", "Grid width and depth")
|
||||
->Attribute(AZ::Edit::Attributes::Min, 0.0f)
|
||||
->Attribute(AZ::Edit::Attributes::Min, GridComponentController::MinGridSize)
|
||||
->Attribute(AZ::Edit::Attributes::Max, GridComponentController::MaxGridSize)
|
||||
->Attribute(AZ::Edit::Attributes::Suffix, " m")
|
||||
->DataElement(AZ::Edit::UIHandlers::Default, &GridComponentConfig::m_primarySpacing, "Primary Grid Spacing", "Amount of space between grid lines")
|
||||
->Attribute(AZ::Edit::Attributes::Min, 0.01f)
|
||||
->Attribute(AZ::Edit::Attributes::Min, GridComponentController::MinSpacing)
|
||||
->Attribute(AZ::Edit::Attributes::Suffix, " m")
|
||||
->DataElement(AZ::Edit::UIHandlers::Default, &GridComponentConfig::m_secondarySpacing, "Secondary Grid Spacing", "Amount of space between sub-grid lines")
|
||||
->Attribute(AZ::Edit::Attributes::Min, 0.01f)
|
||||
->Attribute(AZ::Edit::Attributes::Min, GridComponentController::MinSpacing)
|
||||
->Attribute(AZ::Edit::Attributes::Suffix, " m")
|
||||
->DataElement(AZ::Edit::UIHandlers::Color, &GridComponentConfig::m_axisColor, "Axis Color", "Color of the grid axis")
|
||||
->DataElement(AZ::Edit::UIHandlers::Color, &GridComponentConfig::m_primaryColor, "Primary Color", "Color of the primary grid lines")
|
||||
|
||||
@@ -115,7 +115,7 @@ namespace AZ
|
||||
|
||||
void GridComponentController::SetSize(float gridSize)
|
||||
{
|
||||
m_configuration.m_gridSize = gridSize;
|
||||
m_configuration.m_gridSize = AZStd::clamp(gridSize, MinGridSize, MaxGridSize);
|
||||
m_dirty = true;
|
||||
}
|
||||
|
||||
@@ -126,7 +126,7 @@ namespace AZ
|
||||
|
||||
void GridComponentController::SetPrimarySpacing(float gridPrimarySpacing)
|
||||
{
|
||||
m_configuration.m_primarySpacing = gridPrimarySpacing;
|
||||
m_configuration.m_primarySpacing = AZStd::max(gridPrimarySpacing, MinSpacing);
|
||||
m_dirty = true;
|
||||
}
|
||||
|
||||
@@ -137,7 +137,7 @@ namespace AZ
|
||||
|
||||
void GridComponentController::SetSecondarySpacing(float gridSecondarySpacing)
|
||||
{
|
||||
m_configuration.m_secondarySpacing = gridSecondarySpacing;
|
||||
m_configuration.m_secondarySpacing = AZStd::max(gridSecondarySpacing, MinSpacing);
|
||||
m_dirty = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -46,6 +46,10 @@ namespace AZ
|
||||
void SetConfiguration(const GridComponentConfig& config);
|
||||
const GridComponentConfig& GetConfiguration() const;
|
||||
|
||||
static constexpr float MinGridSize = 0.0f;
|
||||
static constexpr float MaxGridSize = 1000000.0f;
|
||||
static constexpr float MinSpacing = 0.01f;
|
||||
|
||||
private:
|
||||
AZ_DISABLE_COPY(GridComponentController);
|
||||
|
||||
|
||||
+19
-1
@@ -39,6 +39,7 @@ namespace AZ
|
||||
serializeContext->Class<EditorReflectionProbeComponent, BaseClass>()
|
||||
->Version(2, ConvertToEditorRenderComponentAdapter<1>)
|
||||
->Field("useBakedCubemap", &EditorReflectionProbeComponent::m_useBakedCubemap)
|
||||
->Field("bakedCubeMapQualityLevel", &EditorReflectionProbeComponent::m_bakedCubeMapQualityLevel)
|
||||
->Field("bakedCubeMapRelativePath", &EditorReflectionProbeComponent::m_bakedCubeMapRelativePath)
|
||||
->Field("authoredCubeMapAsset", &EditorReflectionProbeComponent::m_authoredCubeMapAsset)
|
||||
;
|
||||
@@ -67,6 +68,13 @@ namespace AZ
|
||||
->DataElement(AZ::Edit::UIHandlers::Default, &EditorReflectionProbeComponent::m_useBakedCubemap, "Use Baked Cubemap", "Selects between a cubemap that captures the environment at location in the scene or a preauthored cubemap")
|
||||
->Attribute(AZ::Edit::Attributes::ChangeValidate, &EditorReflectionProbeComponent::OnUseBakedCubemapValidate)
|
||||
->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorReflectionProbeComponent::OnUseBakedCubemapChanged)
|
||||
->DataElement(AZ::Edit::UIHandlers::ComboBox, &EditorReflectionProbeComponent::m_bakedCubeMapQualityLevel, "Baked Cubemap Quality", "Resolution of the baked cubemap")
|
||||
->Attribute(AZ::Edit::Attributes::Visibility, &EditorReflectionProbeComponent::GetBakedCubemapVisibilitySetting)
|
||||
->EnumAttribute(BakedCubeMapQualityLevel::VeryLow, "Very Low")
|
||||
->EnumAttribute(BakedCubeMapQualityLevel::Low, "Low")
|
||||
->EnumAttribute(BakedCubeMapQualityLevel::Medium, "Medium")
|
||||
->EnumAttribute(BakedCubeMapQualityLevel::High, "High")
|
||||
->EnumAttribute(BakedCubeMapQualityLevel::VeryHigh, "Very High")
|
||||
->DataElement(AZ::Edit::UIHandlers::MultiLineEdit, &EditorReflectionProbeComponent::m_bakedCubeMapRelativePath, "Baked Cubemap Path", "Baked Cubemap Path")
|
||||
->Attribute(AZ::Edit::Attributes::ReadOnly, true)
|
||||
->Attribute(AZ::Edit::Attributes::Visibility, &EditorReflectionProbeComponent::GetBakedCubemapVisibilitySetting)
|
||||
@@ -332,6 +340,12 @@ namespace AZ
|
||||
// clear it to force the generation of a new filename
|
||||
cubeMapRelativePath.clear();
|
||||
}
|
||||
|
||||
// if the quality level changed we need to generate a new filename
|
||||
if (m_controller.m_configuration.m_bakedCubeMapQualityLevel != m_bakedCubeMapQualityLevel)
|
||||
{
|
||||
cubeMapRelativePath.clear();
|
||||
}
|
||||
}
|
||||
|
||||
// build a new cubemap path if necessary
|
||||
@@ -345,7 +359,10 @@ namespace AZ
|
||||
AZStd::string uuidString;
|
||||
uuid.ToString(uuidString);
|
||||
|
||||
cubeMapRelativePath = "ReflectionProbes/" + entity->GetName() + "_" + uuidString + "_iblspecularcm.dds";
|
||||
// determine the filemask suffix from the cubemap quality level setting
|
||||
AZStd::string fileSuffix = BakedCubeMapFileSuffixes[aznumeric_cast<uint32_t>(m_bakedCubeMapQualityLevel)];
|
||||
|
||||
cubeMapRelativePath = "ReflectionProbes/" + entity->GetName() + "_" + uuidString + fileSuffix;
|
||||
|
||||
// replace any invalid filename characters
|
||||
auto invalidCharacters = [](char letter)
|
||||
@@ -384,6 +401,7 @@ namespace AZ
|
||||
// save the relative source path in the configuration
|
||||
AzToolsFramework::ScopedUndoBatch undoBatch("Cubemap path changed.");
|
||||
m_controller.m_configuration.m_bakedCubeMapRelativePath = cubeMapRelativePath;
|
||||
m_controller.m_configuration.m_bakedCubeMapQualityLevel = m_bakedCubeMapQualityLevel;
|
||||
SetDirty();
|
||||
|
||||
// update UI cubemap path display
|
||||
|
||||
+1
@@ -77,6 +77,7 @@ namespace AZ
|
||||
// UI settings
|
||||
// the user can select between a baked cubemap or an authored cubemap asset
|
||||
bool m_useBakedCubemap = true;
|
||||
BakedCubeMapQualityLevel m_bakedCubeMapQualityLevel = BakedCubeMapQualityLevel::Medium;
|
||||
AZStd::string m_bakedCubeMapRelativePath;
|
||||
Data::Asset<RPI::StreamingImageAsset> m_bakedCubeMapAsset;
|
||||
Data::Asset<RPI::StreamingImageAsset> m_authoredCubeMapAsset;
|
||||
|
||||
+1
@@ -46,6 +46,7 @@ namespace AZ
|
||||
->Field("InnerLength", &ReflectionProbeComponentConfig::m_innerLength)
|
||||
->Field("InnerWidth", &ReflectionProbeComponentConfig::m_innerWidth)
|
||||
->Field("UseBakedCubemap", &ReflectionProbeComponentConfig::m_useBakedCubemap)
|
||||
->Field("BakedCubemapQualityLevel", &ReflectionProbeComponentConfig::m_bakedCubeMapQualityLevel)
|
||||
->Field("BakedCubeMapRelativePath", &ReflectionProbeComponentConfig::m_bakedCubeMapRelativePath)
|
||||
->Field("BakedCubeMapAsset", &ReflectionProbeComponentConfig::m_bakedCubeMapAsset)
|
||||
->Field("AuthoredCubeMapAsset", &ReflectionProbeComponentConfig::m_authoredCubeMapAsset)
|
||||
|
||||
+24
@@ -24,6 +24,29 @@ namespace AZ
|
||||
{
|
||||
namespace Render
|
||||
{
|
||||
enum class BakedCubeMapQualityLevel : uint32_t
|
||||
{
|
||||
VeryLow, // 64
|
||||
Low, // 128
|
||||
Medium, // 256
|
||||
High, // 512
|
||||
VeryHigh, // 1024
|
||||
|
||||
Count
|
||||
};
|
||||
|
||||
static const char* BakedCubeMapFileSuffixes[] =
|
||||
{
|
||||
"_iblspecularcm64.dds",
|
||||
"_iblspecularcm128.dds",
|
||||
"_iblspecularcm256.dds",
|
||||
"_iblspecularcm512.dds",
|
||||
"_iblspecularcm1024.dds"
|
||||
};
|
||||
|
||||
static_assert(AZ_ARRAY_SIZE(BakedCubeMapFileSuffixes) == aznumeric_cast<uint32_t>(BakedCubeMapQualityLevel::Count),
|
||||
"BakedCubeMapFileSuffixes must have the same number of entries as BakedCubeMapQualityLevel");
|
||||
|
||||
class ReflectionProbeComponentConfig final
|
||||
: public AZ::ComponentConfig
|
||||
{
|
||||
@@ -43,6 +66,7 @@ namespace AZ
|
||||
bool m_showVisualization = true;
|
||||
bool m_useBakedCubemap = true;
|
||||
|
||||
BakedCubeMapQualityLevel m_bakedCubeMapQualityLevel = BakedCubeMapQualityLevel::Medium;
|
||||
AZStd::string m_bakedCubeMapRelativePath;
|
||||
Data::Asset<RPI::StreamingImageAsset> m_bakedCubeMapAsset;
|
||||
Data::Asset<RPI::StreamingImageAsset> m_authoredCubeMapAsset;
|
||||
|
||||
+28
@@ -34,12 +34,34 @@ namespace AZ
|
||||
AzToolsFramework::Thumbnailer::ThumbnailerRendererRequestBus::MultiHandler::BusConnect(RPI::MaterialAsset::RTTI_Type());
|
||||
AzToolsFramework::Thumbnailer::ThumbnailerRendererRequestBus::MultiHandler::BusConnect(RPI::ModelAsset::RTTI_Type());
|
||||
SystemTickBus::Handler::BusConnect();
|
||||
ThumbnailFeatureProcessorProviderBus::Handler::BusConnect();
|
||||
|
||||
m_steps[Step::Initialize] = AZStd::make_shared<InitializeStep>(this);
|
||||
m_steps[Step::FindThumbnailToRender] = AZStd::make_shared<FindThumbnailToRenderStep>(this);
|
||||
m_steps[Step::WaitForAssetsToLoad] = AZStd::make_shared<WaitForAssetsToLoadStep>(this);
|
||||
m_steps[Step::Capture] = AZStd::make_shared<CaptureStep>(this);
|
||||
m_steps[Step::ReleaseResources] = AZStd::make_shared<ReleaseResourcesStep>(this);
|
||||
|
||||
m_minimalFeatureProcessors =
|
||||
{
|
||||
"AZ::Render::TransformServiceFeatureProcessor",
|
||||
"AZ::Render::MeshFeatureProcessor",
|
||||
"AZ::Render::SimplePointLightFeatureProcessor",
|
||||
"AZ::Render::SimpleSpotLightFeatureProcessor",
|
||||
"AZ::Render::PointLightFeatureProcessor",
|
||||
// There is currently a bug where having multiple DirectionalLightFeatureProcessors active can result in shadow
|
||||
// flickering [ATOM-13568]
|
||||
// as well as continually rebuilding MeshDrawPackets [ATOM-13633]. Lets just disable the directional light FP for now.
|
||||
// Possibly re-enable with [GFX TODO][ATOM-13639]
|
||||
// "AZ::Render::DirectionalLightFeatureProcessor",
|
||||
"AZ::Render::DiskLightFeatureProcessor",
|
||||
"AZ::Render::CapsuleLightFeatureProcessor",
|
||||
"AZ::Render::QuadLightFeatureProcessor",
|
||||
"AZ::Render::DecalTextureArrayFeatureProcessor",
|
||||
"AZ::Render::ImageBasedLightFeatureProcessor",
|
||||
"AZ::Render::PostProcessFeatureProcessor",
|
||||
"AZ::Render::SkyBoxFeatureProcessor"
|
||||
};
|
||||
}
|
||||
|
||||
CommonThumbnailRenderer::~CommonThumbnailRenderer()
|
||||
@@ -50,6 +72,7 @@ namespace AZ
|
||||
}
|
||||
AzToolsFramework::Thumbnailer::ThumbnailerRendererRequestBus::MultiHandler::BusDisconnect();
|
||||
SystemTickBus::Handler::BusDisconnect();
|
||||
ThumbnailFeatureProcessorProviderBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
void CommonThumbnailRenderer::SetStep(Step step)
|
||||
@@ -77,6 +100,11 @@ namespace AZ
|
||||
AzToolsFramework::Thumbnailer::ThumbnailerRendererRequestBus::ExecuteQueuedEvents();
|
||||
}
|
||||
|
||||
const AZStd::vector<AZStd::string>& CommonThumbnailRenderer::GetCustomFeatureProcessors() const
|
||||
{
|
||||
return m_minimalFeatureProcessors;
|
||||
}
|
||||
|
||||
AZStd::shared_ptr<ThumbnailRendererData> CommonThumbnailRenderer::GetData() const
|
||||
{
|
||||
return m_data;
|
||||
|
||||
+9
-2
@@ -17,6 +17,8 @@
|
||||
#include <Thumbnails/Rendering/ThumbnailRendererContext.h>
|
||||
#include <Thumbnails/Rendering/ThumbnailRendererData.h>
|
||||
|
||||
#include <AtomLyIntegration/CommonFeatures/Thumbnails/ThumbnailFeatureProcessorProviderBus.h>
|
||||
|
||||
// Disables warning messages triggered by the Qt library
|
||||
// 4251: class needs to have dll-interface to be used by clients of class
|
||||
// 4800: forcing value to bool 'true' or 'false' (performance warning)
|
||||
@@ -34,9 +36,10 @@ namespace AZ
|
||||
|
||||
//! Provides custom rendering of material and model thumbnails
|
||||
class CommonThumbnailRenderer
|
||||
: private AzToolsFramework::Thumbnailer::ThumbnailerRendererRequestBus::MultiHandler
|
||||
: public ThumbnailRendererContext
|
||||
, private AzToolsFramework::Thumbnailer::ThumbnailerRendererRequestBus::MultiHandler
|
||||
, private SystemTickBus::Handler
|
||||
, public ThumbnailRendererContext
|
||||
, private ThumbnailFeatureProcessorProviderBus::Handler
|
||||
{
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(CommonThumbnailRenderer, AZ::SystemAllocator, 0)
|
||||
@@ -57,9 +60,13 @@ namespace AZ
|
||||
//! SystemTickBus::Handler interface overrides...
|
||||
void OnSystemTick() override;
|
||||
|
||||
//! Render::ThumbnailFeatureProcessorProviderBus::Handler interface overrides...
|
||||
const AZStd::vector<AZStd::string>& GetCustomFeatureProcessors() const override;
|
||||
|
||||
AZStd::unordered_map<Step, AZStd::shared_ptr<ThumbnailRendererStep>> m_steps;
|
||||
Step m_currentStep = Step::None;
|
||||
AZStd::shared_ptr<ThumbnailRendererData> m_data;
|
||||
AZStd::vector<AZStd::string> m_minimalFeatureProcessors;
|
||||
};
|
||||
} // namespace Thumbnails
|
||||
} // namespace LyIntegration
|
||||
|
||||
+25
-20
@@ -11,10 +11,16 @@
|
||||
*/
|
||||
|
||||
|
||||
#include <AzCore/Math/MatrixUtils.h>
|
||||
#include <AzCore/EBus/Results.h>
|
||||
|
||||
#include <AzFramework/Components/TransformComponent.h>
|
||||
|
||||
#include <Atom/Feature/ImageBasedLights/ImageBasedLightFeatureProcessorInterface.h>
|
||||
#include <Atom/Feature/PostProcess/PostProcessFeatureProcessorInterface.h>
|
||||
#include <Atom/Feature/SkyBox/SkyBoxFeatureProcessorInterface.h>
|
||||
#include <Atom/Feature/Utils/LightingPreset.h>
|
||||
|
||||
#include <Atom/RPI.Public/RenderPipeline.h>
|
||||
#include <Atom/RPI.Public/Scene.h>
|
||||
#include <Atom/RPI.Public/View.h>
|
||||
@@ -23,10 +29,11 @@
|
||||
#include <Atom/RPI.Reflect/Model/ModelAsset.h>
|
||||
#include <Atom/RPI.Reflect/System/RenderPipelineDescriptor.h>
|
||||
#include <Atom/RPI.Reflect/System/SceneDescriptor.h>
|
||||
|
||||
#include <AtomLyIntegration/CommonFeatures/Material/MaterialComponentConstants.h>
|
||||
#include <AtomLyIntegration/CommonFeatures/Mesh/MeshComponentConstants.h>
|
||||
#include <AzCore/Math/MatrixUtils.h>
|
||||
#include <AzFramework/Components/TransformComponent.h>
|
||||
#include <AtomLyIntegration/CommonFeatures/Thumbnails/ThumbnailFeatureProcessorProviderBus.h>
|
||||
|
||||
#include <Thumbnails/Rendering/ThumbnailRendererData.h>
|
||||
#include <Thumbnails/Rendering/ThumbnailRendererContext.h>
|
||||
#include <Thumbnails/Rendering/ThumbnailRendererSteps/InitializeStep.h>
|
||||
@@ -37,7 +44,6 @@ namespace AZ
|
||||
{
|
||||
namespace Thumbnails
|
||||
{
|
||||
|
||||
InitializeStep::InitializeStep(ThumbnailRendererContext* context)
|
||||
: ThumbnailRendererStep(context)
|
||||
{
|
||||
@@ -50,24 +56,23 @@ namespace AZ
|
||||
data->m_entityContext = AZStd::make_unique<AzFramework::EntityContext>();
|
||||
data->m_entityContext->InitContext();
|
||||
|
||||
// Create and register a scene with minimum required feature processors
|
||||
// Create and register a scene with all required feature processors
|
||||
RPI::SceneDescriptor sceneDesc;
|
||||
sceneDesc.m_featureProcessorNames.push_back("AZ::Render::TransformServiceFeatureProcessor");
|
||||
sceneDesc.m_featureProcessorNames.push_back("AZ::Render::MeshFeatureProcessor");
|
||||
sceneDesc.m_featureProcessorNames.push_back("AZ::Render::SimplePointLightFeatureProcessor");
|
||||
sceneDesc.m_featureProcessorNames.push_back("AZ::Render::SimpleSpotLightFeatureProcessor");
|
||||
sceneDesc.m_featureProcessorNames.push_back("AZ::Render::PointLightFeatureProcessor");
|
||||
// There is currently a bug where having multiple DirectionalLightFeatureProcessors active can result in shadow flickering [ATOM-13568]
|
||||
// as well as continually rebuilding MeshDrawPackets [ATOM-13633]. Lets just disable the directional light FP for now.
|
||||
// Possibly re-enable with [GFX TODO][ATOM-13639]
|
||||
// sceneDesc.m_featureProcessorNames.push_back("AZ::Render::DirectionalLightFeatureProcessor");
|
||||
sceneDesc.m_featureProcessorNames.push_back("AZ::Render::DiskLightFeatureProcessor");
|
||||
sceneDesc.m_featureProcessorNames.push_back("AZ::Render::CapsuleLightFeatureProcessor");
|
||||
sceneDesc.m_featureProcessorNames.push_back("AZ::Render::QuadLightFeatureProcessor");
|
||||
sceneDesc.m_featureProcessorNames.push_back("AZ::Render::DecalTextureArrayFeatureProcessor");
|
||||
sceneDesc.m_featureProcessorNames.push_back("AZ::Render::ImageBasedLightFeatureProcessor");
|
||||
sceneDesc.m_featureProcessorNames.push_back("AZ::Render::PostProcessFeatureProcessor");
|
||||
sceneDesc.m_featureProcessorNames.push_back("AZ::Render::SkyBoxFeatureProcessor");
|
||||
|
||||
AZ::EBusAggregateResults<AZStd::vector<AZStd::string>> results;
|
||||
ThumbnailFeatureProcessorProviderBus::BroadcastResult(results, &ThumbnailFeatureProcessorProviderBus::Handler::GetCustomFeatureProcessors);
|
||||
|
||||
AZStd::set<AZStd::string> featureProcessorNames;
|
||||
for (auto& resultCollection : results.values)
|
||||
{
|
||||
for (auto& featureProcessorName : resultCollection)
|
||||
{
|
||||
if (featureProcessorNames.emplace(featureProcessorName).second)
|
||||
{
|
||||
sceneDesc.m_featureProcessorNames.push_back(featureProcessorName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data->m_scene = RPI::Scene::CreateScene(sceneDesc);
|
||||
|
||||
|
||||
+3
-2
@@ -10,11 +10,12 @@
|
||||
#
|
||||
|
||||
set(FILES
|
||||
Include/AtomLyIntegration/CommonFeatures/Material/EditorMaterialSystemComponentRequestBus.h
|
||||
Include/AtomLyIntegration/CommonFeatures/ReflectionProbe/EditorReflectionProbeBus.h
|
||||
Include/AtomLyIntegration/CommonFeatures/Thumbnails/ThumbnailFeatureProcessorProviderBus.h
|
||||
Source/Module.cpp
|
||||
Source/Animation/EditorAttachmentComponent.h
|
||||
Source/Animation/EditorAttachmentComponent.cpp
|
||||
Include/AtomLyIntegration/CommonFeatures/Material/EditorMaterialSystemComponentRequestBus.h
|
||||
Include/AtomLyIntegration/CommonFeatures/ReflectionProbe/EditorReflectionProbeBus.h
|
||||
Source/EditorCommonFeaturesSystemComponent.h
|
||||
Source/EditorCommonFeaturesSystemComponent.cpp
|
||||
Source/CoreLights/EditorAreaLightComponent.h
|
||||
|
||||
@@ -9,6 +9,10 @@
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
#
|
||||
|
||||
if(NOT PAL_TRAIT_BUILD_HOST_TOOLS)
|
||||
return()
|
||||
endif()
|
||||
|
||||
ly_add_target(
|
||||
NAME DccScriptingInterface.Static STATIC
|
||||
NAMESPACE Gem
|
||||
@@ -38,3 +42,9 @@ ly_add_target(
|
||||
PRIVATE
|
||||
Gem::DccScriptingInterface.Static
|
||||
)
|
||||
|
||||
# Any 'tool' type applications should use Gem::DccScriptingInterface.Editor:
|
||||
ly_create_alias(NAME DccScriptingInterface.Tools NAMESPACE Gem TARGETS Gem::DccScriptingInterface.Editor)
|
||||
# Add an empty 'builders' alias to allow the DccScriptInterface root gem path to be added to the generated
|
||||
# cmake_dependencies.<project>.assetprocessor.setreg to allow the asset scan folder for it to be added
|
||||
ly_create_alias(NAME DccScriptingInterface.Builders NAMESPACE Gem)
|
||||
|
||||
@@ -178,7 +178,10 @@ namespace Camera
|
||||
if ((!m_viewSystem)||(!m_system))
|
||||
{
|
||||
// perform first-time init
|
||||
m_system = gEnv->pSystem;
|
||||
if (gEnv)
|
||||
{
|
||||
m_system = gEnv->pSystem;
|
||||
}
|
||||
if (m_system)
|
||||
{
|
||||
// Initialize local view.
|
||||
@@ -384,6 +387,11 @@ namespace Camera
|
||||
|
||||
void CameraComponentController::OnTransformChanged([[maybe_unused]] const AZ::Transform& local, const AZ::Transform& world)
|
||||
{
|
||||
if (m_updatingTransformFromEntity)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_view)
|
||||
{
|
||||
CCamera& camera = m_view->GetCamera();
|
||||
|
||||
@@ -94,8 +94,9 @@ namespace CommandSystem
|
||||
return false;
|
||||
}
|
||||
|
||||
actor->LoadRemainingAssets();
|
||||
actor->CheckFinalizeActor();
|
||||
// Because the actor is directly loaded from disk (without going through an actor asset), we need to ask for a blocking
|
||||
// load for the asset that actor is depend on.
|
||||
actor->Finalize(EMotionFX::Actor::LoadRequirement::RequireBlockingLoad);
|
||||
|
||||
// set the actor id in case we have specified it as parameter
|
||||
if (actorID != MCORE_INVALIDINDEX32)
|
||||
|
||||
@@ -52,7 +52,7 @@ namespace EMotionFX
|
||||
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
|
||||
if (serializeContext)
|
||||
{
|
||||
serializeContext->Class<ActorGroupExporter, AZ::SceneAPI::SceneCore::ExportingComponent>()->Version(1);
|
||||
serializeContext->Class<ActorGroupExporter, AZ::SceneAPI::SceneCore::ExportingComponent>()->Version(2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,21 +117,6 @@ namespace EMotionFX
|
||||
|
||||
ExporterLib::SaveActor(filename, m_actor.get(), MCore::Endian::ENDIAN_LITTLE, GetMeshAssetId(context));
|
||||
|
||||
#ifdef EMOTIONFX_ACTOR_DEBUG
|
||||
// Use there line to create a log file and inspect detail debug info
|
||||
AZStd::string folderPath;
|
||||
AzFramework::StringFunc::Path::GetFolderPath(filename.c_str(), folderPath);
|
||||
AZStd::string logFilename = folderPath;
|
||||
logFilename += "EMotionFXExporter_Log.txt";
|
||||
MCore::GetLogManager().CreateLogFile(logFilename.c_str());
|
||||
EMotionFX::GetImporter().SetLogDetails(true);
|
||||
filename += ".xac";
|
||||
|
||||
// use this line to load the actor from the saved actor file
|
||||
EMotionFX::Actor* testLoadingActor = EMotionFX::GetImporter().LoadActor(AZStd::string(filename.c_str()));
|
||||
MCore::Destroy(testLoadingActor);
|
||||
#endif // EMOTIONFX_ACTOR_DEBUG
|
||||
|
||||
static AZ::Data::AssetType emotionFXActorAssetType("{F67CC648-EA51-464C-9F5D-4A9CE41A7F86}"); // from ActorAsset.h in EMotionFX Gem
|
||||
AZ::SceneAPI::Events::ExportProduct& product = context.m_products.AddProduct(AZStd::move(filename), context.m_group.GetId(), emotionFXActorAssetType,
|
||||
AZStd::nullopt, AZStd::nullopt);
|
||||
@@ -141,6 +126,26 @@ namespace EMotionFX
|
||||
product.m_legacyPathDependencies.emplace_back(AZStd::move(materialPathReference));
|
||||
}
|
||||
|
||||
// Mesh asset, skin meta asset and morph target meta asset are sub assets for actor asset.
|
||||
// In here we set them as the dependency of the actor asset. That make sure those assets get automatically loaded before actor asset.
|
||||
// Default to the first product until we are able to establish a link between mesh and actor (ATOM-13590).
|
||||
const AZ::Data::AssetType assetDependencyList[] = {
|
||||
azrtti_typeid<AZ::RPI::ModelAsset>(),
|
||||
azrtti_typeid<AZ::RPI::SkinMetaAsset>(),
|
||||
azrtti_typeid<AZ::RPI::MorphTargetMetaAsset>()
|
||||
};
|
||||
|
||||
for (const AZ::Data::AssetType& assetDependency : assetDependencyList)
|
||||
{
|
||||
AZStd::optional<AZ::SceneAPI::Events::ExportProduct> result = GetFirstProductByType(context, assetDependency);
|
||||
if (result != AZStd::nullopt)
|
||||
{
|
||||
AZ::SceneAPI::Events::ExportProduct exportProduct = result.value();
|
||||
exportProduct.m_dependencyFlags = AZ::Data::ProductDependencyInfo::CreateFlags(AZ::Data::AssetLoadBehavior::PreLoad);
|
||||
product.m_productDependencies.emplace_back(exportProduct);
|
||||
}
|
||||
}
|
||||
|
||||
return SceneEvents::ProcessingResult::Success;
|
||||
}
|
||||
|
||||
@@ -171,5 +176,20 @@ namespace EMotionFX
|
||||
|
||||
return AZStd::nullopt;
|
||||
}
|
||||
|
||||
AZStd::optional<AZ::SceneAPI::Events::ExportProduct> ActorGroupExporter::GetFirstProductByType(
|
||||
const ActorGroupExportContext& context, AZ::Data::AssetType type)
|
||||
{
|
||||
const AZStd::vector<AZ::SceneAPI::Events::ExportProduct>& products = context.m_products.GetProducts();
|
||||
for (const AZ::SceneAPI::Events::ExportProduct& product : products)
|
||||
{
|
||||
if (product.m_assetType == type)
|
||||
{
|
||||
return product;
|
||||
}
|
||||
}
|
||||
|
||||
return AZStd::nullopt;
|
||||
}
|
||||
} // namespace Pipeline
|
||||
} // namespace EMotionFX
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <AzCore/std/optional.h>
|
||||
#include <EMotionFX/Source/AutoRegisteredActor.h>
|
||||
#include <SceneAPI/SceneCore/Components/ExportingComponent.h>
|
||||
#include <SceneAPI/SceneCore/Events/ExportProductList.h>
|
||||
#include <Integration/System/SystemCommon.h>
|
||||
|
||||
|
||||
@@ -44,6 +45,8 @@ namespace EMotionFX
|
||||
|
||||
//! Get the mesh asset id to which the actor is linked to by default.
|
||||
AZStd::optional<AZ::Data::AssetId> GetMeshAssetId(const ActorGroupExportContext& context) const;
|
||||
static AZStd::optional<AZ::SceneAPI::Events::ExportProduct> GetFirstProductByType(
|
||||
const ActorGroupExportContext& context, AZ::Data::AssetType type);
|
||||
|
||||
AutoRegisteredActor m_actor;
|
||||
AZStd::vector<AZStd::string> m_actorMaterialReferences;
|
||||
|
||||
@@ -125,7 +125,6 @@ namespace EMotionFX
|
||||
|
||||
Actor::~Actor()
|
||||
{
|
||||
AZ::Data::AssetBus::MultiHandler::BusDisconnect();
|
||||
ActorNotificationBus::Broadcast(&ActorNotificationBus::Events::OnActorDestroyed, this);
|
||||
GetEventManager().OnDeleteActor(this);
|
||||
|
||||
@@ -1463,108 +1462,78 @@ namespace EMotionFX
|
||||
return morphTargetMetaAssetInfo.m_assetId.IsValid();
|
||||
}
|
||||
|
||||
void Actor::OnAssetReady(AZ::Data::Asset<AZ::Data::AssetData> asset)
|
||||
{
|
||||
if (asset == m_meshAsset)
|
||||
{
|
||||
m_meshAsset = asset;
|
||||
}
|
||||
if (asset == m_skinMetaAsset)
|
||||
{
|
||||
m_skinMetaAsset = asset;
|
||||
}
|
||||
if (asset == m_morphTargetMetaAsset)
|
||||
{
|
||||
m_morphTargetMetaAsset = asset;
|
||||
}
|
||||
|
||||
CheckFinalizeActor();
|
||||
}
|
||||
|
||||
void Actor::CheckFinalizeActor()
|
||||
void Actor::Finalize(LoadRequirement loadReq)
|
||||
{
|
||||
AZStd::scoped_lock<AZStd::recursive_mutex> lock(m_mutex);
|
||||
|
||||
if (m_meshAsset.IsReady())
|
||||
// Load the mesh asset, skin meta asset and morph target asset.
|
||||
// Those sub assets should have already been setup as dependency of actor asset, so they should already be loaded when we reach here.
|
||||
// Only exception is that when the actor is not loaded by an actor asset, for which we need to do a blocking load.
|
||||
if (m_meshAssetId.IsValid())
|
||||
{
|
||||
const AZ::Data::AssetId meshAssetId = m_meshAsset.GetId();
|
||||
const bool skinMetaAssetExists = DoesSkinMetaAssetExist(meshAssetId);
|
||||
const bool morphTargetMetaAssetExists = DoesMorphTargetMetaAssetExist(m_meshAsset.GetId());
|
||||
// Get the mesh asset.
|
||||
m_meshAsset = AZ::Data::AssetManager::Instance().GetAsset<AZ::RPI::ModelAsset>(m_meshAssetId, AZ::Data::AssetLoadBehavior::PreLoad);
|
||||
|
||||
m_skinToSkeletonIndexMap.clear();
|
||||
|
||||
// Skin and morph target meta assets are ready, fill the runtime mesh data.
|
||||
if ((!skinMetaAssetExists || m_skinMetaAsset.IsReady()) &&
|
||||
(!morphTargetMetaAssetExists || m_morphTargetMetaAsset.IsReady()))
|
||||
// Get the skin meta asset.
|
||||
const AZ::Data::AssetId skinMetaAssetId = ConstructSkinMetaAssetId(m_meshAssetId);
|
||||
if (DoesSkinMetaAssetExist(m_meshAssetId) && skinMetaAssetId.IsValid())
|
||||
{
|
||||
// Optional, not all actors have a skinned meshes.
|
||||
if (skinMetaAssetExists)
|
||||
m_skinMetaAsset = AZ::Data::AssetManager::Instance().GetAsset<AZ::RPI::SkinMetaAsset>(
|
||||
skinMetaAssetId, AZ::Data::AssetLoadBehavior::PreLoad);
|
||||
}
|
||||
|
||||
// Get the morph target meta asset.
|
||||
const AZ::Data::AssetId morphTargetMetaAssetId = ConstructMorphTargetMetaAssetId(m_meshAssetId);
|
||||
if (DoesMorphTargetMetaAssetExist(m_meshAssetId) && morphTargetMetaAssetId.IsValid())
|
||||
{
|
||||
m_morphTargetMetaAsset = AZ::Data::AssetManager::Instance().GetAsset<AZ::RPI::MorphTargetMetaAsset>(
|
||||
morphTargetMetaAssetId, AZ::Data::AssetLoadBehavior::PreLoad);
|
||||
}
|
||||
|
||||
if (loadReq == LoadRequirement::RequireBlockingLoad)
|
||||
{
|
||||
if (m_skinMetaAsset.IsLoading())
|
||||
{
|
||||
m_skinToSkeletonIndexMap = ConstructSkinToSkeletonIndexMap(m_skinMetaAsset);
|
||||
m_skinMetaAsset.BlockUntilLoadComplete();
|
||||
}
|
||||
|
||||
ConstructMeshes(m_skinToSkeletonIndexMap);
|
||||
|
||||
// Optional, not all actors have morph targets.
|
||||
if (morphTargetMetaAssetExists)
|
||||
if (m_morphTargetMetaAsset.IsLoading())
|
||||
{
|
||||
ConstructMorphTargets();
|
||||
m_morphTargetMetaAsset.BlockUntilLoadComplete();
|
||||
}
|
||||
else
|
||||
if (m_meshAsset.IsLoading())
|
||||
{
|
||||
// Optional, not all actors have morph targets.
|
||||
const size_t numLODLevels = m_meshAsset->GetLodAssets().size();
|
||||
mMorphSetups.Resize(numLODLevels);
|
||||
for (AZ::u32 i = 0; i < numLODLevels; ++i)
|
||||
{
|
||||
mMorphSetups[i] = nullptr;
|
||||
}
|
||||
m_meshAsset.BlockUntilLoadComplete();
|
||||
}
|
||||
|
||||
SetActorReady();
|
||||
|
||||
// Do not release the mesh assets. We need the mesh data to initialize future instances of the render actor instances.
|
||||
//m_meshAsset.Release();
|
||||
//m_skinMetaAsset.Release();
|
||||
//m_morphTargetMetaAsset.Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Actor::LoadRemainingAssets()
|
||||
{
|
||||
// Everything is ready already or no (skeleton-only) or an invalid mesh asset assigned. Emit ready signal directly.
|
||||
if (m_isReady || !m_meshAssetId.IsValid())
|
||||
if (m_meshAsset.IsReady())
|
||||
{
|
||||
SetActorReady();
|
||||
return;
|
||||
if (m_skinMetaAsset.IsReady())
|
||||
{
|
||||
m_skinToSkeletonIndexMap = ConstructSkinToSkeletonIndexMap(m_skinMetaAsset);
|
||||
}
|
||||
ConstructMeshes();
|
||||
|
||||
if (m_morphTargetMetaAsset.IsReady())
|
||||
{
|
||||
ConstructMorphTargets();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Optional, not all actors have morph targets.
|
||||
const size_t numLODLevels = m_meshAsset->GetLodAssets().size();
|
||||
mMorphSetups.Resize(numLODLevels);
|
||||
for (AZ::u32 i = 0; i < numLODLevels; ++i)
|
||||
{
|
||||
mMorphSetups[i] = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LoadMeshAssetsQueued();
|
||||
}
|
||||
|
||||
void Actor::OnAssetReloaded(AZ::Data::Asset<AZ::Data::AssetData> asset)
|
||||
{
|
||||
if (asset == m_meshAsset)
|
||||
{
|
||||
m_meshAsset = asset;
|
||||
}
|
||||
if (asset == m_skinMetaAsset)
|
||||
{
|
||||
m_skinMetaAsset = asset;
|
||||
}
|
||||
if (asset == m_morphTargetMetaAsset)
|
||||
{
|
||||
m_morphTargetMetaAsset = asset;
|
||||
}
|
||||
|
||||
CheckFinalizeActor();
|
||||
}
|
||||
|
||||
void Actor::SetActorReady()
|
||||
{
|
||||
m_isReady = true;
|
||||
ActorNotificationBus::Broadcast(&ActorNotificationBus::Events::OnActorReady, this);
|
||||
// Do not release the mesh assets. We need the mesh data to initialize future instances of the render actor instances.
|
||||
}
|
||||
|
||||
// update the static AABB (very heavy as it has to create an actor instance, update mesh deformers, calculate the mesh based bounds etc)
|
||||
@@ -2792,37 +2761,6 @@ namespace EMotionFX
|
||||
m_meshAssetId = assetId;
|
||||
}
|
||||
|
||||
void Actor::LoadMeshAssetsQueued()
|
||||
{
|
||||
AZStd::scoped_lock<AZStd::recursive_mutex> lock(m_mutex);
|
||||
|
||||
// Mesh asset will be queue loaded on post init.
|
||||
if (m_meshAssetId.IsValid())
|
||||
{
|
||||
m_isReady = false;
|
||||
AZ::Data::AssetBus::MultiHandler::BusDisconnect();
|
||||
|
||||
AZ::Data::AssetBus::MultiHandler::BusConnect(m_meshAssetId);
|
||||
m_meshAsset = AZ::Data::AssetManager::Instance().GetAsset<AZ::RPI::ModelAsset>(m_meshAssetId, AZ::Data::AssetLoadBehavior::Default);
|
||||
|
||||
// Skin meta asset
|
||||
if (DoesSkinMetaAssetExist(m_meshAssetId))
|
||||
{
|
||||
const AZ::Data::AssetId skinMetaAssetId = ConstructSkinMetaAssetId(m_meshAssetId);
|
||||
AZ::Data::AssetBus::MultiHandler::BusConnect(skinMetaAssetId);
|
||||
m_skinMetaAsset = AZ::Data::AssetManager::Instance().GetAsset<AZ::RPI::SkinMetaAsset>(skinMetaAssetId, AZ::Data::AssetLoadBehavior::Default);
|
||||
}
|
||||
|
||||
// Morph target meta asset
|
||||
if (DoesMorphTargetMetaAssetExist(m_meshAssetId))
|
||||
{
|
||||
const AZ::Data::AssetId morphTargetMetaAssetId = ConstructMorphTargetMetaAssetId(m_meshAssetId);
|
||||
AZ::Data::AssetBus::MultiHandler::BusConnect(morphTargetMetaAssetId);
|
||||
m_morphTargetMetaAsset = AZ::Data::AssetManager::Instance().GetAsset<AZ::RPI::MorphTargetMetaAsset>(morphTargetMetaAssetId, AZ::Data::AssetLoadBehavior::Default);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Node* Actor::FindMeshJoint(const AZ::Data::Asset<AZ::RPI::ModelLodAsset>& lodModelAsset) const
|
||||
{
|
||||
const AZStd::array_view<AZ::RPI::ModelLodAsset::Mesh>& sourceMeshes = lodModelAsset->GetMeshes();
|
||||
@@ -2843,7 +2781,7 @@ namespace EMotionFX
|
||||
return mSkeleton->GetNode(0);
|
||||
}
|
||||
|
||||
void Actor::ConstructMeshes(const AZStd::unordered_map<AZ::u16, AZ::u16>& skinToSkeletonIndexMap)
|
||||
void Actor::ConstructMeshes()
|
||||
{
|
||||
AZ_Assert(m_meshAsset.IsReady(), "Mesh asset should be fully loaded and ready.");
|
||||
|
||||
@@ -2855,7 +2793,8 @@ namespace EMotionFX
|
||||
SetNumLODLevels(numLODLevels, /*adjustMorphSetup=*/false);
|
||||
const uint32 numNodes = mSkeleton->GetNumNodes();
|
||||
|
||||
// Remove all the materials and add them back based on the meshAsset. Eventually we will remove all the material from Actor and GLActor.
|
||||
// Remove all the materials and add them back based on the meshAsset. Eventually we will remove all the material from Actor and
|
||||
// GLActor.
|
||||
RemoveAllMaterials();
|
||||
mMaterials.Resize(numLODLevels);
|
||||
|
||||
@@ -2866,7 +2805,7 @@ namespace EMotionFX
|
||||
lodLevels[lodLevel].mNodeInfos.Resize(numNodes);
|
||||
|
||||
// Create a single mesh for the actor.
|
||||
Mesh* mesh = Mesh::CreateFromModelLod(lodAsset, skinToSkeletonIndexMap);
|
||||
Mesh* mesh = Mesh::CreateFromModelLod(lodAsset, m_skinToSkeletonIndexMap);
|
||||
|
||||
// Find an owning joint for the mesh.
|
||||
Node* meshJoint = FindMeshJoint(lodAsset);
|
||||
@@ -2896,13 +2835,14 @@ namespace EMotionFX
|
||||
continue;
|
||||
}
|
||||
|
||||
EMotionFX::SkinningInfoVertexAttributeLayer* skinLayer = static_cast<EMotionFX::SkinningInfoVertexAttributeLayer*>(vertexAttributeLayer);
|
||||
EMotionFX::SkinningInfoVertexAttributeLayer* skinLayer =
|
||||
static_cast<EMotionFX::SkinningInfoVertexAttributeLayer*>(vertexAttributeLayer);
|
||||
const AZ::u32 numOrgVerts = skinLayer->GetNumAttributes();
|
||||
AZStd::set<AZ::u32> localJointIndices = skinLayer->CalcLocalJointIndices(numOrgVerts);
|
||||
const AZ::u32 numLocalJoints = static_cast<AZ::u32>(localJointIndices.size());
|
||||
|
||||
// The information about if we want to use dual quat skinning is baked into the mesh chunk and we don't have access to that anymore.
|
||||
// Default to dual quat skinning.
|
||||
// The information about if we want to use dual quat skinning is baked into the mesh chunk and we don't have access to that
|
||||
// anymore. Default to dual quat skinning.
|
||||
const bool dualQuatSkinning = true;
|
||||
if (dualQuatSkinning)
|
||||
{
|
||||
@@ -2970,7 +2910,8 @@ namespace EMotionFX
|
||||
|
||||
void Actor::ConstructMorphTargets()
|
||||
{
|
||||
AZ_Assert(m_meshAsset.IsReady() && m_morphTargetMetaAsset.IsReady(), "Mesh as well as morph target meta asset asset should be fully loaded and ready.");
|
||||
AZ_Assert(m_meshAsset.IsReady() && m_morphTargetMetaAsset.IsReady(),
|
||||
"Mesh as well as morph target meta asset asset should be fully loaded and ready.");
|
||||
AZStd::vector<LODLevel>& lodLevels = m_meshLodData.m_lodLevels;
|
||||
const AZStd::array_view<AZ::Data::Asset<AZ::RPI::ModelLodAsset>>& lodAssets = m_meshAsset->GetLodAssets();
|
||||
const size_t numLODLevels = lodAssets.size();
|
||||
|
||||
@@ -63,7 +63,6 @@ namespace EMotionFX
|
||||
* still share the same data from the Actor class. The Actor contains information about the hierarchy/structure of the characters.
|
||||
*/
|
||||
class EMFX_API Actor
|
||||
: private AZ::Data::AssetBus::MultiHandler
|
||||
{
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR_DECL
|
||||
@@ -101,6 +100,12 @@ namespace EMotionFX
|
||||
uint8 mFlags; // bitfield with MIRRORFLAG_ prefix
|
||||
};
|
||||
|
||||
enum class LoadRequirement : bool
|
||||
{
|
||||
RequireBlockingLoad,
|
||||
AllowAsyncLoad
|
||||
};
|
||||
|
||||
//------------------------------------------------
|
||||
|
||||
/**
|
||||
@@ -885,36 +890,35 @@ namespace EMotionFX
|
||||
bool GetOptimizeSkeleton() const { return m_optimizeSkeleton; }
|
||||
|
||||
void SetMeshAssetId(const AZ::Data::AssetId& assetId);
|
||||
void CheckFinalizeActor();
|
||||
void LoadMeshAssetsQueued();
|
||||
void LoadRemainingAssets();
|
||||
AZ::Data::AssetId GetMeshAssetId() const { return m_meshAssetId; };
|
||||
|
||||
const AZ::Data::Asset<AZ::RPI::ModelAsset>& GetMeshAsset() const { return m_meshAsset; }
|
||||
const AZ::Data::Asset<AZ::RPI::SkinMetaAsset>& GetSkinMetaAsset() const { return m_skinMetaAsset; }
|
||||
const AZ::Data::Asset<AZ::RPI::MorphTargetMetaAsset>& GetMorphTargetMetaAsset() const { return m_morphTargetMetaAsset; }
|
||||
|
||||
const AZStd::unordered_map<AZ::u16, AZ::u16>& GetSkinToSkeletonIndexMap() const { return m_skinToSkeletonIndexMap; }
|
||||
|
||||
void SetMeshAsset(AZ::Data::Asset<AZ::RPI::ModelAsset> asset) { m_meshAsset = asset; }
|
||||
void SetSkinMetaAsset(AZ::Data::Asset<AZ::RPI::SkinMetaAsset> asset) { m_skinMetaAsset = asset; }
|
||||
void SetMorphTargetMetaAsset(AZ::Data::Asset<AZ::RPI::MorphTargetMetaAsset> asset) { m_morphTargetMetaAsset = asset; }
|
||||
/**
|
||||
* Is the actor fully ready?
|
||||
* @result True in case the actor as well as its dependent files (e.g. mesh, skin, morph targets) are fully loaded and initialized.
|
||||
**/
|
||||
bool IsReady() const { return m_isReady; }
|
||||
|
||||
/**
|
||||
* Is the actor fully ready?
|
||||
* @result True in case the actor as well as its dependent files (e.g. mesh, skin, morph targets) are fully loaded and initialized.
|
||||
**/
|
||||
bool IsReady() const { return m_isReady; }
|
||||
* Finalize the actor with preload assets (mesh, skinmeta and morph target assets).
|
||||
* LoadRequirement - We won't need a blocking load if the actor is part of the actor asset, as that will trigger the preload assets
|
||||
* to load and get ready before finalize has been reached.
|
||||
* However, if we are calling this on an actor that bypassed the asset system (e.g loading the actor directly from disk), it will require
|
||||
* a blocking load. This option is now being used because emfx editor does not fully integrate with the asset system.
|
||||
*/
|
||||
void Finalize(LoadRequirement loadReq = LoadRequirement::AllowAsyncLoad);
|
||||
|
||||
private:
|
||||
void InsertJointAndParents(AZ::u32 jointIndex, AZStd::unordered_set<AZ::u32>& includedJointIndices);
|
||||
|
||||
// AZ::Data::AssetBus::Handler
|
||||
void OnAssetReady(AZ::Data::Asset<AZ::Data::AssetData> asset) override;
|
||||
void OnAssetReloaded(AZ::Data::Asset<AZ::Data::AssetData> asset) override;
|
||||
|
||||
AZStd::unordered_map<AZ::u16, AZ::u16> ConstructSkinToSkeletonIndexMap(const AZ::Data::Asset<AZ::RPI::SkinMetaAsset>& skinMetaAsset);
|
||||
void ConstructMeshes(const AZStd::unordered_map<AZ::u16, AZ::u16>& skinToSkeletonIndexMap);
|
||||
void ConstructMeshes();
|
||||
void ConstructMorphTargets();
|
||||
|
||||
Node* FindJointByMeshName(const AZStd::string_view meshName) const;
|
||||
|
||||
// per node info (shared between lods)
|
||||
@@ -966,9 +970,6 @@ namespace EMotionFX
|
||||
|
||||
Node* FindMeshJoint(const AZ::Data::Asset<AZ::RPI::ModelLodAsset>& lodModelAsset) const;
|
||||
|
||||
void SetActorReady();
|
||||
bool m_isReady = false;
|
||||
|
||||
Skeleton* mSkeleton; /**< The skeleton, containing the nodes and bind pose. */
|
||||
MCore::Array<Dependency> mDependencies; /**< The dependencies on other actors (shared meshes and transforms). */
|
||||
AZStd::vector<NodeInfo> mNodeInfos; /**< The per node info, shared between lods. */
|
||||
@@ -992,7 +993,7 @@ namespace EMotionFX
|
||||
bool mDirtyFlag; /**< The dirty flag which indicates whether the user has made changes to the actor since the last file save operation. */
|
||||
bool mUsedForVisualization; /**< Indicates if the actor is used for visualization specific things and is not used as a normal in-game actor. */
|
||||
bool m_optimizeSkeleton; /**< Indicates if we should perform/ */
|
||||
|
||||
bool m_isReady = false; /**< If actor as well as its dependent files are fully loaded and initialized.*/
|
||||
#if defined(EMFX_DEVELOPMENT_BUILD)
|
||||
bool mIsOwnedByRuntime; /**< Set if the actor is used/owned by the engine runtime. */
|
||||
#endif // EMFX_DEVELOPMENT_BUILD
|
||||
|
||||
@@ -68,6 +68,9 @@ namespace EMotionFX
|
||||
&actorSettings,
|
||||
"");
|
||||
|
||||
assetData->m_emfxActor->Finalize();
|
||||
|
||||
// Clear out the EMFX raw asset data.
|
||||
assetData->ReleaseEMotionFXData();
|
||||
|
||||
if (!assetData->m_emfxActor)
|
||||
|
||||
@@ -154,17 +154,15 @@ namespace EMotionFX
|
||||
Actor* actor = m_configuration.m_actorAsset->GetActor();
|
||||
if (actor)
|
||||
{
|
||||
OnActorReady(actor);
|
||||
CheckActorCreation();
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
ActorComponent::ActorComponent(const Configuration* configuration)
|
||||
: m_debugDrawRoot(false)
|
||||
, m_sceneFinishSimHandler([this](
|
||||
[[maybe_unused]] AzPhysics::SceneHandle sceneHandle,
|
||||
float fixedDeltatime
|
||||
)
|
||||
, m_sceneFinishSimHandler([this]([[maybe_unused]] AzPhysics::SceneHandle sceneHandle,
|
||||
float fixedDeltatime)
|
||||
{
|
||||
if (m_actorInstance)
|
||||
{
|
||||
@@ -192,18 +190,9 @@ namespace EMotionFX
|
||||
|
||||
if (cfg.m_actorAsset.GetId().IsValid())
|
||||
{
|
||||
EMotionFX::ActorNotificationBus::Handler::BusDisconnect();
|
||||
AZ::Data::AssetBus::Handler::BusDisconnect();
|
||||
EMotionFX::ActorNotificationBus::Handler::BusConnect();
|
||||
AZ::Data::AssetBus::Handler::BusConnect(cfg.m_actorAsset.GetId());
|
||||
cfg.m_actorAsset.QueueLoad();
|
||||
|
||||
// In case the asset was already loaded fully, create the actor directly.
|
||||
if (cfg.m_actorAsset.IsReady() &&
|
||||
cfg.m_actorAsset->GetActor())
|
||||
{
|
||||
cfg.m_actorAsset->GetActor()->LoadRemainingAssets();
|
||||
}
|
||||
}
|
||||
|
||||
AZ::TickBus::Handler::BusConnect();
|
||||
@@ -231,7 +220,6 @@ namespace EMotionFX
|
||||
LmbrCentral::AttachmentComponentNotificationBus::Handler::BusDisconnect();
|
||||
AZ::TransformNotificationBus::MultiHandler::BusDisconnect();
|
||||
AZ::Data::AssetBus::Handler::BusDisconnect();
|
||||
EMotionFX::ActorNotificationBus::Handler::BusDisconnect();
|
||||
|
||||
DestroyActor();
|
||||
m_configuration.m_actorAsset.Release();
|
||||
@@ -314,28 +302,12 @@ namespace EMotionFX
|
||||
Actor* actor = m_configuration.m_actorAsset->GetActor();
|
||||
AZ_Assert(m_configuration.m_actorAsset.IsReady() && actor, "Actor asset should be loaded and actor valid.");
|
||||
|
||||
actor->LoadRemainingAssets();
|
||||
actor->CheckFinalizeActor();
|
||||
CheckActorCreation();
|
||||
}
|
||||
|
||||
void ActorComponent::OnAssetReloaded(AZ::Data::Asset<AZ::Data::AssetData> asset)
|
||||
{
|
||||
DestroyActor();
|
||||
m_configuration.m_actorAsset = asset;
|
||||
|
||||
const Actor* oldActor = m_configuration.m_actorAsset->GetActor();
|
||||
AZ::Data::Asset<AZ::RPI::ModelAsset> meshAsset = oldActor->GetMeshAsset();
|
||||
AZ::Data::Asset<AZ::RPI::SkinMetaAsset> skinMetaAsset = oldActor->GetSkinMetaAsset();
|
||||
AZ::Data::Asset<AZ::RPI::MorphTargetMetaAsset> morphTargetMetaAsset = oldActor->GetMorphTargetMetaAsset();
|
||||
|
||||
m_configuration.m_actorAsset = asset;
|
||||
Actor* newActor = m_configuration.m_actorAsset->GetActor();
|
||||
AZ_Assert(m_configuration.m_actorAsset.IsReady() && newActor, "Actor asset should be loaded and actor valid.");
|
||||
|
||||
newActor->SetMeshAsset(meshAsset);
|
||||
newActor->SetSkinMetaAsset(skinMetaAsset);
|
||||
newActor->SetMorphTargetMetaAsset(morphTargetMetaAsset);
|
||||
newActor->CheckFinalizeActor();
|
||||
OnAssetReady(asset);
|
||||
}
|
||||
|
||||
bool ActorComponent::IsPhysicsSceneSimulationFinishEventConnected() const
|
||||
@@ -850,13 +822,5 @@ namespace EMotionFX
|
||||
m_actorInstance->RemoveAttachment(targetActorInstance);
|
||||
}
|
||||
}
|
||||
|
||||
void ActorComponent::OnActorReady(Actor* actor)
|
||||
{
|
||||
if (m_configuration.m_actorAsset && m_configuration.m_actorAsset->GetActor() == actor)
|
||||
{
|
||||
CheckActorCreation();
|
||||
}
|
||||
}
|
||||
} // namespace Integration
|
||||
} // namespace EMotionFX
|
||||
|
||||
@@ -44,7 +44,6 @@ namespace EMotionFX
|
||||
, private LmbrCentral::AttachmentComponentNotificationBus::Handler
|
||||
, private AzFramework::CharacterPhysicsDataRequestBus::Handler
|
||||
, private AzFramework::RagdollPhysicsNotificationBus::Handler
|
||||
, private EMotionFX::ActorNotificationBus::Handler
|
||||
{
|
||||
public:
|
||||
AZ_COMPONENT(ActorComponent, "{BDC97E7F-A054-448B-A26F-EA2B5D78E377}");
|
||||
@@ -168,9 +167,6 @@ namespace EMotionFX
|
||||
void OnTick(float deltaTime, AZ::ScriptTimePoint time) override;
|
||||
int GetTickOrder() override;
|
||||
|
||||
// ActorNotificationBus::Handler
|
||||
void OnActorReady(Actor* actor) override;
|
||||
|
||||
void CheckActorCreation();
|
||||
void DestroyActor();
|
||||
void CheckAttachToEntity();
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user