Merge branch 'main' into Helios_AssImp_TransformImporterFix

This commit is contained in:
amzn-mike
2021-06-13 16:56:44 -05:00
999 changed files with 36569 additions and 21419 deletions
@@ -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
@@ -1,14 +0,0 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
// If you make changes in ICryPak.h, make changes here, to dirty the PCH.
#include "CrySystem_precompiled.h"
+1 -1
View File
@@ -561,7 +561,7 @@ void DebugCallStack::LogExceptionInfo(EXCEPTION_POINTERS* pex)
if (pex)
{
MINIDUMP_TYPE mdumpValue;
MINIDUMP_TYPE mdumpValue = MiniDumpNormal;
bool bDump = true;
switch (g_cvars.sys_dump_type)
{
+51 -55
View File
@@ -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
}
+7 -11
View File
@@ -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;
+1 -1
View File
@@ -66,7 +66,7 @@ LRESULT WINAPI WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
}
if (pSystem && !pSystem->IsQuitting())
{
LRESULT result;
LRESULT result = 0;
bool bAny = false;
for (std::vector<IWindowMessageHandler*>::const_iterator it = pSystem->m_windowMessageHandlers.begin(); it != pSystem->m_windowMessageHandlers.end(); ++it)
{
+4 -4
View File
@@ -634,7 +634,7 @@ ICVar* CSystem::attachVariable (const char* szVarName, int* pContainer, const ch
IConsole* pConsole = GetIConsole();
ICVar* pOldVar = pConsole->GetCVar (szVarName);
int nDefault;
int nDefault = 0;
if (pOldVar)
{
nDefault = pOldVar->GetIVal();
@@ -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());
@@ -2017,8 +2017,8 @@ void CSystem::CreateSystemVars()
REGISTER_CVAR2("sys_streaming_in_blocks", &g_cvars.sys_streaming_in_blocks, 1, VF_NULL,
"Streaming of large files happens in blocks");
#if (defined(WIN32) || defined(WIN64)) && !defined(_RELEASE)
REGISTER_CVAR2("sys_float_exceptions", &g_cvars.sys_float_exceptions, 3, 0, "Use or not use floating point exceptions.");
#if (defined(WIN32) || defined(WIN64)) && defined(_DEBUG)
REGISTER_CVAR2("sys_float_exceptions", &g_cvars.sys_float_exceptions, 2, 0, "Use or not use floating point exceptions.");
#else // Float exceptions by default disabled for console builds.
REGISTER_CVAR2("sys_float_exceptions", &g_cvars.sys_float_exceptions, 0, 0, "Use or not use floating point exceptions.");
#endif
@@ -75,6 +75,5 @@ set(FILES
ViewSystem/View.h
ViewSystem/ViewSystem.cpp
ViewSystem/ViewSystem.h
CrySystem_precompiled.cpp
WindowsErrorReporting.cpp
)
@@ -77,6 +77,27 @@
#endif // defined(AZ_ENABLE_DEBUG_TOOLS)
#include <AzCore/Module/Environment.h>
#include <AzCore/std/string/conversions.h>
static void PrintEntityName(const AZ::ConsoleCommandContainer& arguments)
{
if (arguments.empty())
{
return;
}
const auto entityIdStr = AZStd::string(arguments.front());
const auto entityIdValue = AZStd::stoull(entityIdStr);
AZStd::string entityName;
AZ::ComponentApplicationBus::BroadcastResult(
entityName, &AZ::ComponentApplicationBus::Events::GetEntityName, AZ::EntityId(entityIdValue));
AZ_Printf("Entity Debug", "EntityId: %" PRIu64 ", Entity Name: %s", entityIdValue, entityName.c_str());
}
AZ_CONSOLEFREEFUNC(
PrintEntityName, AZ::ConsoleFunctorFlags::Null, "Parameter: EntityId value, Prints the name of the entity to the console");
namespace AZ
{
@@ -219,18 +219,11 @@ namespace AZ
//! Scale modifiers
//! @{
//! Set local scale of the transform.
//! @param scale The new scale to set.
virtual void SetLocalScale([[maybe_unused]] const AZ::Vector3& scale) {}
//! Get the scale value in local space.
//! @deprecated GetLocalScale is deprecated, and is left only to allow migration of legacy vector scale.
//! Get the legacy vector scale value in local space.
//! @return The scale value in local space.
virtual AZ::Vector3 GetLocalScale() { return AZ::Vector3(FLT_MAX); }
//! Get the scale value in world space.
//! @return The scale value in world space.
virtual AZ::Vector3 GetWorldScale() { return AZ::Vector3(FLT_MAX); }
//! Set the uniform scale value in local space.
virtual void SetLocalUniformScale([[maybe_unused]] float scale) {}
+39 -21
View File
@@ -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)
{
+13 -8
View File
@@ -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;
};
/**
+1 -1
View File
@@ -227,7 +227,7 @@ namespace AZ
// the min and max of each part and sum them to get the min and max co-ordinate of the transformed box. For a given new axis,
// the coefficients for what proportion of each original axis is rotated onto that new axis are the same as the components we
// would get by performing the inverse rotation on the new axis, so we need to take the conjugate to get the inverse rotation.
axisCoeffs = transform.GetScale() * (transform.GetRotation().GetConjugate().TransformVector(axis));
axisCoeffs = transform.GetUniformScale() * (transform.GetRotation().GetConjugate().TransformVector(axis));
a = axisCoeffs * m_min;
b = axisCoeffs * m_max;
+1 -1
View File
@@ -154,7 +154,7 @@ namespace AZ
return Obb::CreateFromPositionRotationAndHalfLengths(
transform.TransformPoint(obb.GetPosition()),
transform.GetRotation() * obb.GetRotation(),
transform.GetScale() * obb.GetHalfLengths()
transform.GetUniformScale() * obb.GetHalfLengths()
);
}
}
+40 -19
View File
@@ -130,8 +130,8 @@ namespace AZ
const Transform* transform = reinterpret_cast<const Transform*>(classPtr);
float data[NumFloats];
transform->GetRotation().StoreToFloat4(data);
transform->GetScale().StoreToFloat3(&data[4]);
transform->GetTranslation().StoreToFloat3(&data[7]);
data[4] = transform->GetUniformScale();
transform->GetTranslation().StoreToFloat3(&data[5]);
for (int i = 0; i < NumFloats; i++)
{
@@ -159,8 +159,8 @@ namespace AZ
size_t TransformSerializer::TextToData(const char* text, unsigned int textVersion, IO::GenericStream& stream, bool isDataBigEndian)
{
const size_t dataBufferSize = AZStd::max(NumFloatsVersion0, NumFloats);
const size_t numElements = textVersion < 1 ? NumFloatsVersion0 : NumFloats;
const size_t dataBufferSize = AZStd::max(AZStd::max(NumFloatsVersion1, NumFloatsVersion0), NumFloats);
const size_t numElements = textVersion < 1 ? NumFloatsVersion0 : (textVersion == 1 ? NumFloatsVersion1 : NumFloats);
size_t nextNumberIndex = 0;
AZStd::array<float, dataBufferSize> data;
@@ -201,7 +201,34 @@ namespace AZ
return true;
}
// otherwise load as a separate rotation, scale and translation
// version 1 had a quaternion rotation, vector3 scale and vector3 translation
else if (version == 1)
{
float data[NumFloatsVersion1];
if (stream.GetLength() < sizeof(data))
{
return false;
}
stream.Read(sizeof(data), reinterpret_cast<void*>(data));
for (unsigned int i = 0; i < AZ_ARRAY_SIZE(data); ++i)
{
AZ_SERIALIZE_SWAP_ENDIAN(data[i], isDataBigEndian);
}
Quaternion rotation = Quaternion::CreateFromFloat4(data);
Vector3 vectorScale = Vector3::CreateFromFloat3(&data[4]);
Vector3 translation = Vector3::CreateFromFloat3(&data[7]);
float uniformScale = vectorScale.GetMaxElement();
*reinterpret_cast<Transform*>(classPtr) =
Transform::CreateFromQuaternionAndTranslation(rotation, translation) * Transform::CreateUniformScale(uniformScale);
return true;
}
// otherwise load as a quaternion rotation, float scale and vector3 translation
float data[NumFloats];
if (stream.GetLength() < sizeof(data))
{
@@ -216,11 +243,11 @@ namespace AZ
}
Quaternion rotation = Quaternion::CreateFromFloat4(data);
Vector3 scale = Vector3::CreateFromFloat3(&data[4]);
Vector3 translation = Vector3::CreateFromFloat3(&data[7]);
float scale = data[4];
Vector3 translation = Vector3::CreateFromFloat3(&data[5]);
*reinterpret_cast<Transform*>(classPtr) =
Transform::CreateFromQuaternionAndTranslation(rotation, translation) * Transform::CreateScale(scale);
Transform::CreateFromQuaternionAndTranslation(rotation, translation) * Transform::CreateUniformScale(scale);
return true;
}
@@ -237,7 +264,7 @@ namespace AZ
if (serializeContext)
{
serializeContext->Class<Transform>()
->Version(1)
->Version(2)
->Serializer<TransformSerializer>();
}
@@ -250,7 +277,7 @@ namespace AZ
Attribute(Script::Attributes::ExcludeFrom, Script::Attributes::ExcludeFlags::All)->
Attribute(Script::Attributes::Storage, Script::Attributes::StorageType::Value)->
Attribute(Script::Attributes::GenericConstructorOverride, &Internal::TransformDefaultConstructor)->
Constructor<const Vector3&, const Quaternion&, const Vector3&>()->
Constructor<const Vector3&, const Quaternion&, float>()->
Method("GetBasis", &Transform::GetBasis)->
Method("GetBasisX", &Transform::GetBasisX)->
Method("GetBasisY", &Transform::GetBasisY)->
@@ -283,15 +310,10 @@ namespace AZ
Attribute(Script::Attributes::ExcludeFrom, Script::Attributes::ExcludeFlags::All)->
Method("GetRotation", &Transform::GetRotation)->
Method<void (Transform::*)(const Quaternion&)>("SetRotation", &Transform::SetRotation)->
Method("GetScale", &Transform::GetScale)->
Method("GetUniformScale", &Transform::GetUniformScale)->
Method("SetScale", &Transform::SetScale)->
Method("SetUniformScale", &Transform::SetUniformScale)->
Method("ExtractScale", &Transform::ExtractScale)->
Attribute(Script::Attributes::ExcludeFrom, Script::Attributes::ExcludeFlags::All)->
Method("ExtractUniformScale", &Transform::ExtractUniformScale)->
Attribute(Script::Attributes::ExcludeFrom, Script::Attributes::ExcludeFlags::All)->
Method("MultiplyByScale", &Transform::MultiplyByScale)->
Method("MultiplyByUniformScale", &Transform::MultiplyByUniformScale)->
Method("GetInverse", &Transform::GetInverse)->
Method("Invert", &Transform::Invert)->
@@ -310,7 +332,6 @@ namespace AZ
Method("CreateFromQuaternionAndTranslation", &Transform::CreateFromQuaternionAndTranslation)->
Method("CreateFromMatrix3x3", &Transform::CreateFromMatrix3x3)->
Method("CreateFromMatrix3x3AndTranslation", &Transform::CreateFromMatrix3x3AndTranslation)->
Method("CreateScale", &Transform::CreateScale)->
Method("CreateUniformScale", &Transform::CreateUniformScale)->
Method("CreateTranslation", &Transform::CreateTranslation)->
Method("ConstructFromValuesNumeric", &Internal::ConstructTransformFromValues);
@@ -321,7 +342,7 @@ namespace AZ
{
Transform result;
Matrix3x3 tmp = value;
result.m_scale = tmp.ExtractScale();
result.m_scale = tmp.ExtractScale().GetMaxElement();
result.m_rotation = Quaternion::CreateFromMatrix3x3(tmp);
result.m_translation = Vector3::CreateZero();
return result;
@@ -331,7 +352,7 @@ namespace AZ
{
Transform result;
Matrix3x3 tmp = value;
result.m_scale = tmp.ExtractScale();
result.m_scale = tmp.ExtractScale().GetMaxElement();
result.m_rotation = Quaternion::CreateFromMatrix3x3(tmp);
result.m_translation = p;
return result;
@@ -341,7 +362,7 @@ namespace AZ
{
Transform result;
Matrix3x4 tmp = value;
result.m_scale = tmp.ExtractScale();
result.m_scale = tmp.ExtractScale().GetMaxElement();
result.m_rotation = Quaternion::CreateFromMatrix3x4(tmp);
result.m_translation = value.GetTranslation();
return result;
+17 -16
View File
@@ -25,10 +25,13 @@ namespace AZ
: public SerializeContext::IDataSerializer
{
public:
// number of floats in the serialized representation, 4 for rotation, 3 for scale and 3 for translation
static constexpr int NumFloats = 10;
// number of floats in the serialized representation, 4 for rotation, 1 for scale and 3 for translation
static constexpr int NumFloats = 8;
// number of floats in the old format, which stored a 3x4 matrix
// number of floats in version 1, which used 4 for rotation, 3 for scale and 3 for translation
static constexpr int NumFloatsVersion1 = 10;
// number of floats in version 0, which stored a 3x4 matrix
static constexpr int NumFloatsVersion0 = 12;
size_t Save(const void* classPtr, IO::GenericStream& stream, bool isDataBigEndian) override;
@@ -45,7 +48,7 @@ namespace AZ
static constexpr float MaxTransformScale = 1e9f;
//! @}
//! The basic transformation class, represented using a quaternion rotation, vector scale and vector translation.
//! The basic transformation class, represented using a quaternion rotation, float scale and vector translation.
//! By design, cannot represent skew transformations.
class Transform
{
@@ -63,7 +66,7 @@ namespace AZ
Transform() = default;
//! Construct a transform from components.
Transform(const Vector3& translation, const Quaternion& rotation, const Vector3& scale);
Transform(const Vector3& translation, const Quaternion& rotation, float scale);
//! Creates an identity transform.
static Transform CreateIdentity();
@@ -82,16 +85,20 @@ namespace AZ
static Transform CreateFromQuaternionAndTranslation(const class Quaternion& q, const Vector3& p);
//! Constructs from a Matrix3x3, translation is set to zero.
//! Note that Transform only allows uniform scale, so if the matrix has different scale values along its axes,
//! the largest matrix scale value will be used to uniformly scale the Transform.
static Transform CreateFromMatrix3x3(const class Matrix3x3& value);
//! Constructs from a Matrix3x3, translation is set to zero.
//! Constructs from a Matrix3x3 and translation Vector3.
//! Note that Transform only allows uniform scale, so if the matrix has different scale values along its axes,
//! the largest matrix scale value will be used to uniformly scale the Transform.
static Transform CreateFromMatrix3x3AndTranslation(const class Matrix3x3& value, const Vector3& p);
//! Constructs from a Matrix3x4.
//! Note that Transform only allows uniform scale, so if the matrix has different scale values along its axes,
//! the largest matrix scale value will be used to uniformly scale the Transform.
static Transform CreateFromMatrix3x4(const Matrix3x4& value);
//! Sets the transform to apply scale only, no rotation or translation.
static Transform CreateScale(const AZ::Vector3& scale);
//! Sets the transform to apply (uniform) scale only, no rotation or translation.
static Transform CreateUniformScale(const float scale);
@@ -122,18 +129,12 @@ namespace AZ
const Quaternion& GetRotation() const;
void SetRotation(const Quaternion& rotation);
Vector3 GetScale() const;
float GetUniformScale() const;
void SetScale(const Vector3& v);
void SetUniformScale(const float scale);
//! Sets the transform's scale to a unit value and returns the previous scale value.
Vector3 ExtractScale();
//! Sets the transform's scale to a unit value and returns the previous scale value.
float ExtractUniformScale();
void MultiplyByScale(const AZ::Vector3& scale);
void MultiplyByUniformScale(float scale);
Transform operator*(const Transform& rhs) const;
@@ -168,7 +169,7 @@ namespace AZ
private:
Quaternion m_rotation;
Vector3 m_scale;
float m_scale;
Vector3 m_translation;
};
+21 -58
View File
@@ -12,7 +12,7 @@
namespace AZ
{
AZ_MATH_INLINE Transform::Transform(const Vector3& translation, const Quaternion& rotation, const Vector3& scale)
AZ_MATH_INLINE Transform::Transform(const Vector3& translation, const Quaternion& rotation, float scale)
: m_translation(translation)
, m_rotation(rotation)
, m_scale(scale)
@@ -25,7 +25,7 @@ namespace AZ
{
Transform result;
result.m_rotation = Quaternion::CreateIdentity();
result.m_scale = Vector3::CreateOne();
result.m_scale = 1.0f;
result.m_translation = Vector3::CreateZero();
return result;
}
@@ -49,7 +49,7 @@ namespace AZ
{
Transform result;
result.m_rotation = q;
result.m_scale = Vector3::CreateOne();
result.m_scale = 1.0f;
result.m_translation = Vector3::CreateZero();
return result;
}
@@ -58,26 +58,16 @@ namespace AZ
{
Transform result;
result.m_rotation = q;
result.m_scale = Vector3::CreateOne();
result.m_scale = 1.0f;
result.m_translation = p;
return result;
}
AZ_MATH_INLINE Transform Transform::CreateScale(const Vector3& scale)
{
AZ_WarningOnce("Transform", false, "CreateScale is deprecated, please use CreateUniformScale instead.");
Transform result;
result.m_rotation = Quaternion::CreateIdentity();
result.m_scale = scale;
result.m_translation = Vector3::CreateZero();
return result;
}
AZ_MATH_INLINE Transform Transform::CreateUniformScale(float scale)
{
Transform result;
result.m_rotation = Quaternion::CreateIdentity();
result.m_scale = Vector3(scale);
result.m_scale = scale;
result.m_translation = Vector3::CreateZero();
return result;
}
@@ -86,7 +76,7 @@ namespace AZ
{
Transform result;
result.m_rotation = Quaternion::CreateIdentity();
result.m_scale = Vector3::CreateOne();
result.m_scale = 1.0f;
result.m_translation = translation;
return result;
}
@@ -114,17 +104,17 @@ namespace AZ
AZ_MATH_INLINE Vector3 Transform::GetBasisX() const
{
return m_rotation.TransformVector(Vector3::CreateAxisX(m_scale.GetX()));
return m_rotation.TransformVector(Vector3::CreateAxisX(m_scale));
}
AZ_MATH_INLINE Vector3 Transform::GetBasisY() const
{
return m_rotation.TransformVector(Vector3::CreateAxisY(m_scale.GetY()));
return m_rotation.TransformVector(Vector3::CreateAxisY(m_scale));
}
AZ_MATH_INLINE Vector3 Transform::GetBasisZ() const
{
return m_rotation.TransformVector(Vector3::CreateAxisZ(m_scale.GetZ()));
return m_rotation.TransformVector(Vector3::CreateAxisZ(m_scale));
}
AZ_MATH_INLINE void Transform::GetBasisAndTranslation(Vector3* basisX, Vector3* basisY, Vector3* basisZ, Vector3* pos) const
@@ -160,49 +150,23 @@ namespace AZ
m_rotation = rotation;
}
AZ_MATH_INLINE Vector3 Transform::GetScale() const
{
AZ_WarningOnce("Transform", false, "GetScale is deprecated, please use GetUniformScale instead.");
return m_scale;
}
AZ_MATH_INLINE float Transform::GetUniformScale() const
{
return m_scale.GetMaxElement();
}
AZ_MATH_INLINE void Transform::SetScale(const Vector3& scale)
{
AZ_WarningOnce("Transform", false, "SetScale is deprecated, please use SetUniformScale instead.");
m_scale = scale;
return m_scale;
}
AZ_MATH_INLINE void Transform::SetUniformScale(const float scale)
{
m_scale = Vector3(scale);
}
AZ_MATH_INLINE Vector3 Transform::ExtractScale()
{
AZ_WarningOnce("Transform", false, "ExtractScale is deprecated, please use ExtractUniformScale instead.");
const Vector3 scale = m_scale;
m_scale = Vector3::CreateOne();
return scale;
m_scale = scale;
}
AZ_MATH_INLINE float Transform::ExtractUniformScale()
{
const float scale = m_scale.GetMaxElement();
m_scale = Vector3::CreateOne();
const float scale = m_scale;
m_scale = 1.0f;
return scale;
}
AZ_MATH_INLINE void Transform::MultiplyByScale(const Vector3& scale)
{
AZ_WarningOnce("Transform", false, "MultiplyByScale is deprecated, please use MultiplyByUniformScale instead.");
m_scale *= scale;
}
AZ_MATH_INLINE void Transform::MultiplyByUniformScale(float scale)
{
m_scale *= scale;
@@ -240,10 +204,9 @@ namespace AZ
AZ_MATH_INLINE Transform Transform::GetInverse() const
{
// note - need to be careful about how to calculate inverse when there is non-uniform scale
Transform out;
out.m_rotation = m_rotation.GetConjugate();
out.m_scale = m_scale.GetReciprocal();
out.m_scale = 1.0f / m_scale;
out.m_translation = -out.m_scale * (out.m_rotation.TransformVector(m_translation));
return out;
}
@@ -255,27 +218,27 @@ namespace AZ
AZ_MATH_INLINE bool Transform::IsOrthogonal(float tolerance) const
{
return m_scale.IsClose(Vector3::CreateOne(), tolerance);
return AZ::IsClose(m_scale, 1.0f, tolerance);
}
AZ_MATH_INLINE Transform Transform::GetOrthogonalized() const
{
Transform result;
result.m_rotation = m_rotation;
result.m_scale = Vector3::CreateOne();
result.m_scale = 1.0f;
result.m_translation = m_translation;
return result;
}
AZ_MATH_INLINE void Transform::Orthogonalize()
{
m_scale = Vector3::CreateOne();
m_scale = 1.0f;
}
AZ_MATH_INLINE bool Transform::IsClose(const Transform& rhs, float tolerance) const
{
return m_rotation.IsClose(rhs.m_rotation, tolerance)
&& m_scale.IsClose(rhs.m_scale, tolerance)
&& AZ::IsClose(m_scale, rhs.m_scale, tolerance)
&& m_translation.IsClose(rhs.m_translation, tolerance);
}
@@ -304,21 +267,21 @@ namespace AZ
AZ_MATH_INLINE void Transform::SetFromEulerDegrees(const Vector3& eulerDegrees)
{
m_translation = Vector3::CreateZero();
m_scale = Vector3::CreateOne();
m_scale = 1.0f;
m_rotation.SetFromEulerDegrees(eulerDegrees);
}
AZ_MATH_INLINE void Transform::SetFromEulerRadians(const Vector3& eulerRadians)
{
m_translation = Vector3::CreateZero();
m_scale = Vector3::CreateOne();
m_scale = 1.0f;
m_rotation.SetFromEulerRadians(eulerRadians);
}
AZ_MATH_INLINE bool Transform::IsFinite() const
{
return m_rotation.IsFinite()
&& m_scale.IsFinite()
&& AZ::IsFiniteFloat(m_scale)
&& m_translation.IsFinite();
}
@@ -67,7 +67,7 @@ namespace AZ
result.Combine(loadResult);
transformInstance->SetScale(AZ::Vector3(scale));
transformInstance->SetUniformScale(scale);
}
return context.Report(
@@ -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;
@@ -123,6 +123,7 @@ namespace AZ
const static AZ::Crc32 NameLabelOverride = AZ_CRC("NameLabelOverride", 0x9ff79cab);
const static AZ::Crc32 AssetPickerTitle = AZ_CRC_CE("AssetPickerTitle");
const static AZ::Crc32 HideProductFilesInAssetPicker = AZ_CRC_CE("HideProductFilesInAssetPicker");
const static AZ::Crc32 ChildNameLabelOverride = AZ_CRC("ChildNameLabelOverride", 0x73dd2909);
//! Container attribute that is used to override labels for its elements given the index of the element
const static AZ::Crc32 IndexedChildNameLabelOverride = AZ_CRC("IndexedChildNameLabelOverride", 0x5f313ac2);
@@ -28,7 +28,13 @@ namespace AZ
{
namespace IdUtils
{
template<typename IdType>
/**
* \param AllowDuplicates - If true allows the same id to be registered multiple times,
with the newer value overwriting the stored value. If false, duplicates are not allowed and
the first stored value is kept.The default is false.
*/
template<typename IdType, bool AllowDuplicates = false>
struct Remapper
{
/**
@@ -138,14 +144,18 @@ namespace AZ
* \param context - The serialize context for enumerating the @classPtr elements
*/
template<typename T, typename MapType>
static void GenerateNewIdsAndFixRefs(T* object, MapType& newIdMap, AZ::SerializeContext* context = nullptr)
static void GenerateNewIdsAndFixRefs(
T* object, MapType& newIdMap, AZ::SerializeContext* context = nullptr)
{
if (!context)
{
AZ::ComponentApplicationBus::BroadcastResult(context, &AZ::ComponentApplicationRequests::GetSerializeContext);
if (!context)
{
AZ_Error("Serialization", false, "No serialize context provided! Failed to get component application default serialize context! ComponentApp is not started or input serialize context should not be null!");
AZ_Error(
"Serialization", false,
"No serialize context provided! Failed to get component application default serialize context! ComponentApp is "
"not started or input serialize context should not be null!");
return;
}
}
@@ -156,8 +166,16 @@ namespace AZ
{
if (idGenerator)
{
auto it = newIdMap.emplace(originalId, idGenerator());
return it.first->second;
if constexpr(AllowDuplicates)
{
auto it = newIdMap.insert_or_assign(originalId, idGenerator());
return it.first->second;
}
else
{
auto it = newIdMap.emplace(originalId, idGenerator());
return it.first->second;
}
}
return originalId;
}
@@ -30,8 +30,10 @@ namespace AZ
bool m_isModifiedContainer;
};
template<typename IdType>
unsigned int Remapper<IdType>::RemapIds(void* classPtr, const AZ::Uuid& classUuid, const typename Remapper<IdType>::IdMapper& mapper, AZ::SerializeContext* context, bool replaceId)
template<typename IdType, bool AllowDuplicates>
unsigned int Remapper<IdType, AllowDuplicates>::RemapIds(
void* classPtr, const AZ::Uuid& classUuid, const typename Remapper<IdType, AllowDuplicates>::IdMapper& mapper,
AZ::SerializeContext* context, bool replaceId)
{
if (!context)
{
@@ -152,16 +154,18 @@ namespace AZ
return replaced;
}
template<typename IdType>
unsigned int Remapper<IdType>::ReplaceIdsAndIdRefs(void* classPtr, const AZ::Uuid& classUuid, const IdMapper& mapper, AZ::SerializeContext* context /*= nullptr*/)
template<typename IdType, bool AllowDuplicates>
unsigned int Remapper<IdType, AllowDuplicates>::ReplaceIdsAndIdRefs(void* classPtr, const AZ::Uuid& classUuid, const IdMapper& mapper, AZ::SerializeContext* context /*= nullptr*/)
{
unsigned int replaced = RemapIds(classPtr, classUuid, mapper, context, true);
replaced += RemapIds(classPtr, classUuid, mapper, context, false);
return replaced;
}
template<typename IdType>
unsigned int Remapper<IdType>::RemapIdsAndIdRefs(void* classPtr, const AZ::Uuid& classUuid, const typename Remapper<IdType>::IdReplacer& mapper, AZ::SerializeContext* context)
template<typename IdType, bool AllowDuplicates>
unsigned int Remapper<IdType, AllowDuplicates>::RemapIdsAndIdRefs(
void* classPtr, const AZ::Uuid& classUuid, const typename Remapper<IdType, AllowDuplicates>::IdReplacer& mapper,
AZ::SerializeContext* context)
{
if (!context)
{
@@ -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();
/**
+3
View File
@@ -21,11 +21,14 @@ namespace AZStd
using std::asin;
using std::atan;
using std::atan2;
using std::ceil;
using std::cos;
using std::exp2;
using std::floor;
using std::fmod;
using std::round;
using std::sin;
using std::sqrt;
using std::tan;
using std::trunc;
} // namespace AZStd
@@ -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.
@@ -14,6 +14,9 @@
#include <sys/syslimits.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <AzCore/std/typetraits/underlying_type.h>
namespace AZ
{
@@ -22,9 +25,10 @@ namespace AZ
namespace Internal
{
using SizeType = AZ::u64;
using SeekSizeType = AZ::s64;
using FileHandleType = int;
}
namespace PosixInternal
{
enum class OpenFlags : int
@@ -36,7 +40,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.
@@ -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
@@ -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.
@@ -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
+8 -8
View File
@@ -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");
@@ -68,7 +68,7 @@ namespace AZ
return os
<< "translation: " << transform.GetTranslation()
<< " rotation: " << transform.GetRotation()
<< " scale: " << transform.GetScale();
<< " scale: " << transform.GetUniformScale();
}
std::ostream& operator<<(std::ostream& os, const Color& color)
+2 -1
View File
@@ -395,7 +395,8 @@ namespace UnitTest
}
else
{
int result1, result2;
int result1 = 0;
int result2 = 0;
Job* job1 = aznew FibonacciJob2(m_n - 1, &result1, m_context);
Job* job2 = aznew FibonacciJob2(m_n - 2, &result2, m_context);
StartAsChild(job1);
@@ -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)
+12 -12
View File
@@ -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);
@@ -44,7 +44,7 @@ namespace JsonSerializationTests
AZStd::shared_ptr<AZ::Transform> CreateFullySetInstance() override
{
return AZStd::make_shared<AZ::Transform>(
AZ::Vector3(1.0f, 2.0f, 3.0f), AZ::Quaternion(0.25f, 0.5f, 0.75f, 1.0f), AZ::Vector3(9.0f));
AZ::Vector3(1.0f, 2.0f, 3.0f), AZ::Quaternion(0.25f, 0.5f, 0.75f, 1.0f), 9.0f);
}
AZStd::string_view GetJsonForFullySetInstance() override
@@ -95,7 +95,7 @@ namespace JsonSerializationTests
AZ::Transform expectedTransform(
AZ::Vector3(2.25f, 3.5f, 4.75f),
AZ::Quaternion(0.25f, 0.5f, 0.75f, 1.0f),
AZ::Vector3(5.5f));
5.5f);
rapidjson::Document json;
json.Parse(R"({ "Translation": [ 2.25, 3.5, 4.75 ], "Rotation": [ 0.25, 0.5, 0.75, 1.0 ], "Scale": 5.5 })");
@@ -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());
}
}
}
@@ -1681,13 +1681,11 @@ namespace AZ::IO
AZStd::vector<AZStd::string> files;
do
{
if (AZStd::wildcard_match(pWildcardIn, fileIterator.m_filename))
{
AZStd::string foundFilename{ fileIterator.m_filename };
AZStd::to_lower(foundFilename.begin(), foundFilename.end());
files.emplace_back(AZStd::move(foundFilename));
}
} while (fileIterator = FindNext(fileIterator));
AZStd::string foundFilename{ fileIterator.m_filename };
AZStd::to_lower(foundFilename.begin(), foundFilename.end());
files.emplace_back(AZStd::move(foundFilename));
}
while (fileIterator = FindNext(fileIterator));
// Open files in alphabet order.
AZStd::sort(files.begin(), files.end());
@@ -406,21 +406,10 @@ namespace AzFramework
return m_localTM.GetRotation();
}
void TransformComponent::SetLocalScale(const AZ::Vector3& scale)
{
AZ::Transform newLocalTM = m_localTM;
newLocalTM.SetScale(scale);
SetLocalTM(newLocalTM);
}
AZ::Vector3 TransformComponent::GetLocalScale()
{
return m_localTM.GetScale();
}
AZ::Vector3 TransformComponent::GetWorldScale()
{
return m_worldTM.GetScale();
AZ_WarningOnce("TransformComponent", false, "GetLocalScale is deprecated, please use GetLocalUniformScale instead");
return AZ::Vector3(m_localTM.GetUniformScale());
}
void TransformComponent::SetLocalUniformScale(float scale)
@@ -756,11 +745,11 @@ namespace AzFramework
->Event("GetLocalRotationQuaternion", &AZ::TransformBus::Events::GetLocalRotationQuaternion)
->Attribute("Rotation", AZ::Edit::Attributes::PropertyRotation)
->VirtualProperty("Rotation", "GetLocalRotationQuaternion", "SetLocalRotationQuaternion")
->Event("SetLocalScale", &AZ::TransformBus::Events::SetLocalScale)
->Event("GetLocalScale", &AZ::TransformBus::Events::GetLocalScale)
->Attribute("Scale", AZ::Edit::Attributes::PropertyScale)
->VirtualProperty("Scale", "GetLocalScale", "SetLocalScale")
->Event("GetWorldScale", &AZ::TransformBus::Events::GetWorldScale)
->Event("SetLocalUniformScale", &AZ::TransformBus::Events::SetLocalUniformScale)
->Event("GetLocalUniformScale", &AZ::TransformBus::Events::GetLocalUniformScale)
->VirtualProperty("Uniform Scale", "GetLocalUniformScale", "SetLocalUniformScale")
->Event("GetChildren", &AZ::TransformBus::Events::GetChildren)
->Event("GetAllDescendants", &AZ::TransformBus::Events::GetAllDescendants)
->Event("GetEntityAndAllDescendants", &AZ::TransformBus::Events::GetEntityAndAllDescendants)
@@ -128,9 +128,7 @@ namespace AzFramework
AZ::Quaternion GetLocalRotationQuaternion() override;
// Scale Modifiers
void SetLocalScale(const AZ::Vector3& scale) override;
AZ::Vector3 GetLocalScale() override;
AZ::Vector3 GetWorldScale() override;
void SetLocalUniformScale(float scale) override;
float GetLocalUniformScale() override;
@@ -54,7 +54,7 @@ namespace AzFramework
AZ::Matrix3x4 m_transform = AZ::Matrix3x4::Identity(); //!< Transform to apply to text quads
bool m_monospace = false; //!< disable character proportional spacing
bool m_depthTest = false; //!< Test character against the depth buffer
bool m_virtual800x600ScreenSize = true; //!< Text placement and size are scaled relative to a virtual 800x600 resolution
bool m_virtual800x600ScreenSize = false; //!< Text placement and size are scaled relative to a virtual 800x600 resolution
bool m_scaleWithWindow = false; //!< Font gets bigger as the window gets bigger
bool m_multiline = true; //!< text respects ascii newline characters
};
@@ -37,9 +37,10 @@ namespace AzPhysics
if (auto* behaviorContext = azdynamic_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->Class<TriggerEvent>()
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All)
->Method("GetTriggerEntityId", &TriggerEvent::GetTriggerEntityId)
->Method("GetOtherEntityId", &TriggerEvent::GetOtherEntityId)
->Attribute(AZ::Script::Attributes::Module, "physics")
->Attribute(AZ::Script::Attributes::Category, "Physics")
->Method("Get Trigger EntityId", &TriggerEvent::GetTriggerEntityId)
->Method("Get Other EntityId", &TriggerEvent::GetOtherEntityId)
;
}
}
@@ -104,10 +105,11 @@ namespace AzPhysics
if (auto* behaviorContext = azdynamic_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->Class<CollisionEvent>()
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All)
->Property("Contacts", BehaviorValueProperty(&CollisionEvent::m_contacts))
->Method("GetBody1EntityId", &CollisionEvent::GetBody1EntityId)
->Method("GetBody2EntityId", &CollisionEvent::GetBody2EntityId)
->Attribute(AZ::Script::Attributes::Module, "physics")
->Attribute(AZ::Script::Attributes::Category, "Physics")
->Property("Contacts", BehaviorValueGetter(&CollisionEvent::m_contacts), nullptr)
->Method("Get Body 1 EntityId", &CollisionEvent::GetBody1EntityId)
->Method("Get Body 2 EntityId", &CollisionEvent::GetBody2EntityId)
;
}
}
@@ -78,7 +78,7 @@ namespace AzFramework::ProjectManager
projectJsonPath.c_str());
}
if (LaunchProjectManager(engineRootPath))
if (LaunchProjectManager())
{
AZ_TracePrintf("ProjectManager", "Project Manager launched successfully, requesting exit.");
return ProjectPathCheckResult::ProjectManagerLaunched;
@@ -87,7 +87,7 @@ namespace AzFramework::ProjectManager
return ProjectPathCheckResult::ProjectManagerLaunchFailed;
}
bool LaunchProjectManager([[maybe_unused]] const AZ::IO::FixedMaxPath& engineRootPath)
bool LaunchProjectManager(const AZStd::string& commandLineArgs)
{
bool launchSuccess = false;
#if (AZ_TRAIT_AZFRAMEWORK_USE_PROJECT_MANAGER)
@@ -109,7 +109,7 @@ namespace AzFramework::ProjectManager
}
AzFramework::ProcessLauncher::ProcessLaunchInfo processLaunchInfo;
processLaunchInfo.m_commandlineParameters = executablePath.String();
processLaunchInfo.m_commandlineParameters = executablePath.String() + commandLineArgs;
launchSuccess = AzFramework::ProcessLauncher::LaunchUnwatchedProcess(processLaunchInfo);
}
if (ownsSystemAllocator)
@@ -12,6 +12,7 @@
#pragma once
#include <AzCore/IO/Path/Path_fwd.h>
#include <AzCore/std/string/string.h>
namespace AzFramework::ProjectManager
{
@@ -21,8 +22,16 @@ namespace AzFramework::ProjectManager
ProjectManagerLaunched = 0,
ProjectPathFound = 1
};
// Check for a project name, if not found, attempts to launch project manager and returns false
//! Check for a project name, if not found, attempts to launch project manager and returns false
//! @param argc the number of arguments in argv
//! @param argv arguments provided to this executable
//! @return a ProjectPathCheckResult
ProjectPathCheckResult CheckProjectPathProvided(const int argc, char* argv[]);
// Attempt to Launch the project manager. Requires locating the engine root, project manager script, and python.
bool LaunchProjectManager(const AZ::IO::FixedMaxPath& engineRootPath);
//! Attempt to Launch the project manager, assuming the o3de executable exists in same folder as
//! current executable. Requires the o3de cli and python.
//! @param commandLineArgs additional command line arguments to provide to the project manager
//! @return true on success, false if failed to find or launch the executable
bool LaunchProjectManager(const AZStd::string& commandLineArgs = "");
} // AzFramework::ProjectManager
@@ -12,6 +12,7 @@
#pragma once
#include <AzCore/RTTI/RTTI.h>
#include <AzCore/std/string/string.h>
namespace AzFramework
@@ -49,13 +50,17 @@ namespace AzFramework
class ISessionHandlingClientRequests
{
public:
// Handle the player join session process
AZ_RTTI(ISessionHandlingClientRequests, "{41DE6BD3-72BC-4443-BFF9-5B1B9396657A}");
ISessionHandlingClientRequests() = default;
virtual ~ISessionHandlingClientRequests() = default;
// Request the player join session
// @param sessionConnectionConfig The required properties to handle the player join session process
// @return The result of player join session process
virtual bool HandlePlayerJoinSession(const SessionConnectionConfig& sessionConnectionConfig) = 0;
virtual bool RequestPlayerJoinSession(const SessionConnectionConfig& sessionConnectionConfig) = 0;
// Handle the player leave session process
virtual void HandlePlayerLeaveSession() = 0;
// Request the connected player leave session
virtual void RequestPlayerLeaveSession() = 0;
};
//! ISessionHandlingServerRequests
@@ -63,6 +68,10 @@ namespace AzFramework
class ISessionHandlingServerRequests
{
public:
AZ_RTTI(ISessionHandlingServerRequests, "{4F0C17BA-F470-4242-A8CB-EC7EA805257C}");
ISessionHandlingServerRequests() = default;
virtual ~ISessionHandlingServerRequests() = default;
// Handle the destroy session process
virtual void HandleDestroySession() = 0;
@@ -74,5 +83,10 @@ namespace AzFramework
// Handle the player leave session process
// @param playerConnectionConfig The required properties to handle the player leave session process
virtual void HandlePlayerLeaveSession(const PlayerConnectionConfig& playerConnectionConfig) = 0;
// Retrieves the file location of a pem-encoded TLS certificate
// @return If successful, returns the file location of TLS certificate file; if not successful, returns
// empty string.
virtual AZStd::string GetSessionCertificate() = 0;
};
} // namespace AzFramework
@@ -167,6 +167,9 @@ namespace AzFramework
: public AZ::EBusTraits
{
public:
// Safeguard handler for multi-threaded use case
using MutexType = AZStd::recursive_mutex;
//////////////////////////////////////////////////////////////////////////
// EBusTraits overrides
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
@@ -24,6 +24,9 @@ namespace AzFramework
: public AZ::EBusTraits
{
public:
// Safeguard handler for multi-threaded use case
using MutexType = AZStd::recursive_mutex;
//////////////////////////////////////////////////////////////////////////
// EBusTraits overrides
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
@@ -21,22 +21,6 @@ namespace AzFramework
{
}
Spawnable::Spawnable(Spawnable&& other)
: m_entities(AZStd::move(other.m_entities))
{
}
Spawnable& Spawnable::operator=(Spawnable&& other)
{
if (this != &other)
{
m_entities = AZStd::move(other.m_entities);
}
return *this;
}
const Spawnable::EntityList& Spawnable::GetEntities() const
{
return m_entities;
@@ -41,11 +41,11 @@ namespace AzFramework
Spawnable() = default;
explicit Spawnable(const AZ::Data::AssetId& id, AssetStatus status = AssetStatus::NotLoaded);
Spawnable(const Spawnable& rhs) = delete;
Spawnable(Spawnable&& other);
Spawnable(Spawnable&& other) = delete;
~Spawnable() override = default;
Spawnable& operator=(const Spawnable& rhs) = delete;
Spawnable& operator=(Spawnable&& other);
Spawnable& operator=(Spawnable&& other) = delete;
const EntityList& GetEntities() const;
EntityList& GetEntities();
@@ -10,6 +10,7 @@
*
*/
#include <AzCore/Casting/lossy_cast.h>
#include <AzCore/Serialization/Utils.h>
#include <AzCore/std/string/string.h>
#include <AzFramework/Spawnable/Spawnable.h>
@@ -88,4 +89,10 @@ namespace AzFramework
{
extensions.push_back(Spawnable::FileExtension);
}
uint32_t SpawnableAssetHandler::BuildSubId(AZStd::string_view id)
{
AZ::Uuid subIdHash = AZ::Uuid::CreateData(id.data(), id.size());
return azlossy_caster(subIdHash.GetHash());
}
} // namespace AzFramework
@@ -47,6 +47,7 @@ namespace AzFramework
const char* GetGroup() const override;
const char* GetBrowserIcon() const override;
void GetAssetTypeExtensions(AZStd::vector<AZStd::string>& extensions) override;
static uint32_t BuildSubId(AZStd::string_view id);
protected:
LoadResult LoadAssetData(
@@ -44,7 +44,8 @@ namespace AzFramework
void SpawnableEntitiesContainer::SpawnEntities(AZStd::vector<size_t> entityIndices)
{
AZ_Assert(m_threadData, "Calling SpawnEntities on a Spawnable container that's not set.");
SpawnableEntitiesInterface::Get()->SpawnEntities(m_threadData->m_spawnedEntitiesTicket, AZStd::move(entityIndices));
SpawnableEntitiesInterface::Get()->SpawnEntities(
m_threadData->m_spawnedEntitiesTicket, AZStd::move(entityIndices));
}
void SpawnableEntitiesContainer::DespawnAllEntities()
@@ -66,8 +67,9 @@ namespace AzFramework
m_monitor.Disconnect();
m_monitor.m_threadData.reset();
SpawnableEntitiesInterface::Get()->Barrier(m_threadData->m_spawnedEntitiesTicket,
[threadData = m_threadData](EntitySpawnTicket&) mutable
SpawnableEntitiesInterface::Get()->Barrier(
m_threadData->m_spawnedEntitiesTicket,
[threadData = m_threadData](EntitySpawnTicket::Id) mutable
{
threadData.reset();
});
@@ -83,8 +85,9 @@ namespace AzFramework
void SpawnableEntitiesContainer::Alert(AlertCallback callback)
{
AZ_Assert(m_threadData, "Calling DespawnEntities on a Spawnable container that's not set.");
SpawnableEntitiesInterface::Get()->Barrier(m_threadData->m_spawnedEntitiesTicket,
[generation = m_threadData->m_generation, callback = AZStd::move(callback)](EntitySpawnTicket&)
SpawnableEntitiesInterface::Get()->Barrier(
m_threadData->m_spawnedEntitiesTicket,
[generation = m_threadData->m_generation, callback = AZStd::move(callback)](EntitySpawnTicket::Id)
{
callback(generation);
});
@@ -239,7 +239,9 @@ namespace AzFramework
{
auto manager = SpawnableEntitiesInterface::Get();
AZ_Assert(manager, "Attempting to create an entity spawn ticket while the SpawnableEntitiesInterface has no implementation.");
m_payload = manager->CreateTicket(AZStd::move(spawnable));
AZStd::pair<EntitySpawnTicket::Id, void*> result = manager->CreateTicket(AZStd::move(spawnable));
m_id = result.first;
m_payload = result.second;
}
EntitySpawnTicket::~EntitySpawnTicket()
@@ -250,6 +252,7 @@ namespace AzFramework
AZ_Assert(manager, "Attempting to destroy an entity spawn ticket while the SpawnableEntitiesInterface has no implementation.");
manager->DestroyTicket(m_payload);
m_payload = nullptr;
m_id = 0;
}
}
@@ -263,12 +266,20 @@ namespace AzFramework
AZ_Assert(manager, "Attempting to destroy an entity spawn ticket while the SpawnableEntitiesInterface has no implementation.");
manager->DestroyTicket(m_payload);
}
m_id = rhs.m_id;
rhs.m_id = 0;
m_payload = rhs.m_payload;
rhs.m_payload = nullptr;
}
return *this;
}
auto EntitySpawnTicket::GetId() const -> Id
{
return m_id;
}
bool EntitySpawnTicket::IsValid() const
{
return m_payload != nullptr;
@@ -14,16 +14,26 @@
#include <AzCore/Asset/AssetCommon.h>
#include <AzCore/Interface/Interface.h>
#include <AzCore/RTTI/TypeSafeIntegral.h>
#include <AzCore/std/functional.h>
#include <AzFramework/Spawnable/Spawnable.h>
namespace AZ
{
class Entity;
class SerializeContext;
}
namespace AzFramework
{
AZ_TYPE_SAFE_INTEGRAL(SpawnablePriority, uint8_t);
inline static constexpr SpawnablePriority SpawnablePriority_Highest { 0 };
inline static constexpr SpawnablePriority SpawnablePriority_High { 32 };
inline static constexpr SpawnablePriority SpawnablePriority_Default { 128 };
inline static constexpr SpawnablePriority SpawnablePriority_Low { 192 };
inline static constexpr SpawnablePriority SpawnablePriority_Lowest { 255 };
class SpawnableEntityContainerView
{
public:
@@ -124,16 +134,18 @@ namespace AzFramework
SpawnableIndexEntityIterator m_end;
};
//! Requests to the SpawnableEntitiesInterface require a ticket with a valid spawnable that be used as a template. A ticket can
//! be reused for multiple calls on the same spawnable and is safe to use by multiple threads at the same time. Entities created
//! Requests to the SpawnableEntitiesInterface require a ticket with a valid spawnable that is used as a template. A ticket can
//! be reused for multiple calls on the same spawnable and is safe to be used by multiple threads at the same time. Entities created
//! from the spawnable may be tracked by the ticket and so using the same ticket is needed to despawn the exact entities created
//! by a call so spawn entities. The life cycle of the spawned entities is tied to the ticket and all entities spawned using a
//! by a call to spawn entities. The life cycle of the spawned entities is tied to the ticket and all entities spawned using a
//! ticket will be despawned when it's deleted.
class EntitySpawnTicket
{
public:
friend class SpawnableEntitiesDefinition;
using Id = uint64_t;
EntitySpawnTicket() = default;
EntitySpawnTicket(const EntitySpawnTicket&) = delete;
EntitySpawnTicket(EntitySpawnTicket&& rhs);
@@ -143,26 +155,108 @@ namespace AzFramework
EntitySpawnTicket& operator=(const EntitySpawnTicket&) = delete;
EntitySpawnTicket& operator=(EntitySpawnTicket&& rhs);
Id GetId() const;
bool IsValid() const;
private:
void* m_payload{ nullptr };
Id m_id { 0 }; //!< An id that uniquely identifies a ticket.
};
using EntitySpawnCallback = AZStd::function<void(EntitySpawnTicket&, SpawnableConstEntityContainerView)>;
using EntityPreInsertionCallback = AZStd::function<void(EntitySpawnTicket&, SpawnableEntityContainerView)>;
using EntityDespawnCallback = AZStd::function<void(EntitySpawnTicket&)>;
using ReloadSpawnableCallback = AZStd::function<void(EntitySpawnTicket&, SpawnableConstEntityContainerView)>;
using ListEntitiesCallback = AZStd::function<void(EntitySpawnTicket&, SpawnableConstEntityContainerView)>;
using ListIndicesEntitiesCallback = AZStd::function<void(EntitySpawnTicket&, SpawnableConstIndexEntityContainerView)>;
using ClaimEntitiesCallback = AZStd::function<void(EntitySpawnTicket&, SpawnableEntityContainerView)>;
using BarrierCallback = AZStd::function<void(EntitySpawnTicket&)>;
using EntitySpawnCallback = AZStd::function<void(EntitySpawnTicket::Id, SpawnableConstEntityContainerView)>;
using EntityPreInsertionCallback = AZStd::function<void(EntitySpawnTicket::Id, SpawnableEntityContainerView)>;
using EntityDespawnCallback = AZStd::function<void(EntitySpawnTicket::Id)>;
using ReloadSpawnableCallback = AZStd::function<void(EntitySpawnTicket::Id, SpawnableConstEntityContainerView)>;
using ListEntitiesCallback = AZStd::function<void(EntitySpawnTicket::Id, SpawnableConstEntityContainerView)>;
using ListIndicesEntitiesCallback = AZStd::function<void(EntitySpawnTicket::Id, SpawnableConstIndexEntityContainerView)>;
using ClaimEntitiesCallback = AZStd::function<void(EntitySpawnTicket::Id, SpawnableEntityContainerView)>;
using BarrierCallback = AZStd::function<void(EntitySpawnTicket::Id)>;
struct SpawnAllEntitiesOptionalArgs final
{
//! Callback that's called after instances of entities have been created, but before they're spawned into the world. This
//! gives the opportunity to modify the entities if needed such as injecting additional components or modifying components.
EntityPreInsertionCallback m_preInsertionCallback;
//! Callback that's called when spawning entities has completed. This can be triggered from a different thread than the one that
//! made the function call to spawn. The returned list of entities contains all the newly created entities.
EntitySpawnCallback m_completionCallback;
//! The Serialize Context used to clone entities with. If this is not provided the global Serialize Contetx will be used.
AZ::SerializeContext* m_serializeContext { nullptr };
//! The priority at which this call will be executed.
SpawnablePriority m_priority { SpawnablePriority_Default };
};
struct SpawnEntitiesOptionalArgs final
{
//! Callback that's called after instances of entities have been created, but before they're spawned into the world. This
//! gives the opportunity to modify the entities if needed such as injecting additional components or modifying components.
EntityPreInsertionCallback m_preInsertionCallback;
//! Callback that's called when spawning entities has completed. This can be triggered from a different thread than the one that
//! made the function call to spawn. The returned list of entities contains all the newly created entities.
EntitySpawnCallback m_completionCallback;
//! The Serialize Context used to clone entities with. If this is not provided the global Serialize Contetx will be used.
AZ::SerializeContext* m_serializeContext{ nullptr };
//! The priority at which this call will be executed.
SpawnablePriority m_priority{ SpawnablePriority_Default };
//! Entity references are resolved by referring to the last entity spawned from a template entity in the spawnable. If this
//! is set to false entities from previous spawn calls are not taken into account. If set to true entity references may be
//! resolved to a previously spawned entity. A lookup table has to be constructed when true, which may negatively impact
//! performance, especially if a large number of entities are present on a ticket.
bool m_referencePreviouslySpawnedEntities{ false };
};
struct DespawnAllEntitiesOptionalArgs final
{
//! Callback that's called when despawning entities has completed. This can be triggered from a different thread than the one that
//! made the function call to despawn. The returned list of entities contains all the newly created entities.
EntityDespawnCallback m_completionCallback;
//! The priority at which this call will be executed.
SpawnablePriority m_priority { SpawnablePriority_Default };
};
struct ReloadSpawnableOptionalArgs final
{
//! Callback that's called when respawning entities has completed. This can be triggered from a different thread than the one that
//! made the function call to respawn. The returned list of entities contains all the newly created entities.
ReloadSpawnableCallback m_completionCallback;
//! The Serialize Context used to clone entities with. If this is not provided the global Serialize Context will be used.
AZ::SerializeContext* m_serializeContext { nullptr };
//! The priority at which this call will be executed.
SpawnablePriority m_priority { SpawnablePriority_Default };
};
struct ListEntitiesOptionalArgs final
{
//! The priority at which this call will be executed.
SpawnablePriority m_priority{ SpawnablePriority_Default };
};
struct ClaimEntitiesOptionalArgs final
{
//! The priority at which this call will be executed.
SpawnablePriority m_priority{ SpawnablePriority_Default };
};
struct BarrierOptionalArgs final
{
//! The priority at which this call will be executed.
SpawnablePriority m_priority{ SpawnablePriority_Default };
};
//! Interface definition to (de)spawn entities from a spawnable into the game world.
//!
//! While the callbacks of the individual calls are being processed they will block processing any other request. Callbacks can be
//! issued from threads other than the one that issued the call, including the main thread.
//!
//! Calls on the same ticket are guaranteed to be executed in the order they are issued. Note that when issuing requests from
//! multiple threads on the same ticket the order in which the requests are assigned to the ticket is not guaranteed.
//!
//! Most calls have a priority with values that range from 0 (highest priority) to 255 (lowest priority). The implementation of this
//! interface may choose to use priority lanes which doesn't guarantee that higher priority requests happen before lower priority
//! requests if they don't pass the priority lane threshold. Priority lanes and their thresholds are implementation specific and may
//! differ between platforms. Note that if a call happened on a ticket with lower priority followed by a one with a higher priority
//! the first lower priority call will still need to complete before the second higher priority call can be executed and the priority
//! of the first call will not be updated.
class SpawnableEntitiesDefinition
{
public:
@@ -173,40 +267,35 @@ namespace AzFramework
virtual ~SpawnableEntitiesDefinition() = default;
//! Spawn instances of all entities in the spawnable.
//! @param spawnable The Spawnable asset that will be used to create entity instances from.
//! @param ticket Stores the results of the call. Use this ticket to spawn additional entities or to despawn them.
//! @param completionCallback Optional callback that's called when spawning entities has completed. This can be called from
//! a different thread than the one that made the function call. The returned list of entities contains all the newly
//! created entities.
virtual void SpawnAllEntities(EntitySpawnTicket& ticket, EntityPreInsertionCallback preInsertionCallback = {},
EntitySpawnCallback completionCallback = {}) = 0;
//! @param optionalArgs Optional additional arguments, see SpawnAllEntitiesOptionalArgs.
virtual void SpawnAllEntities(EntitySpawnTicket& ticket, SpawnAllEntitiesOptionalArgs optionalArgs = {}) = 0;
//! Spawn instances of some entities in the spawnable.
//! @param ticket Stores the results of the call. Use this ticket to spawn additional entities or to despawn them.
//! @param priority The priority at which this call will be executed.
//! @param entityIndices The indices into the template entities stored in the spawnable that will be used to spawn entities from.
//! @param completionCallback Optional callback that's called when spawning entities has completed. This can be called from
//! a different thread than the one that made this function call. The returned list of entities contains all the newly
//! created entities.
virtual void SpawnEntities(EntitySpawnTicket& ticket, AZStd::vector<size_t> entityIndices,
EntityPreInsertionCallback preInsertionCallback = {}, EntitySpawnCallback completionCallback = {}) = 0;
//! @param optionalArgs Optional additional arguments, see SpawnEntitiesOptionalArgs.
virtual void SpawnEntities(
EntitySpawnTicket& ticket, AZStd::vector<size_t> entityIndices, SpawnEntitiesOptionalArgs optionalArgs = {}) = 0;
//! Removes all entities in the provided list from the environment.
//! @param ticket The ticket previously used to spawn entities with.
//! @param completionCallback Optional callback that's called when despawning entities has completed. This can be called from
//! a different thread than the one that made this function call.
virtual void DespawnAllEntities(EntitySpawnTicket& ticket, EntityDespawnCallback completionCallback = {}) = 0;
//! @param priority The priority at which this call will be executed.
//! @param optionalArgs Optional additional arguments, see DespawnAllEntitiesOptionalArgs.
virtual void DespawnAllEntities(EntitySpawnTicket& ticket, DespawnAllEntitiesOptionalArgs optionalArgs = {}) = 0;
//! Removes all entities in the provided list from the environment and reconstructs the entities from the provided spawnable.
//! @param ticket Stores the results of the call. Use this ticket to spawn additional entities or to despawn them.
//! @param ticket Holds the information on the entities to reload.
//! @param priority The priority at which this call will be executed.
//! @param spawnable The spawnable that will replace the existing spawnable. Both need to have the same asset id.
//! @param completionCallback Optional callback that's called when the entities have been reloaded. This can be called from
//! a different thread than the one that made this function call. The returned list of entities contains all the replacement
//! entities.
virtual void ReloadSpawnable(EntitySpawnTicket& ticket, AZ::Data::Asset<Spawnable> spawnable,
ReloadSpawnableCallback completionCallback = {}) = 0;
//! @param optionalArgs Optional additional arguments, see ReloadSpawnableOptionalArgs.
virtual void ReloadSpawnable(
EntitySpawnTicket& ticket, AZ::Data::Asset<Spawnable> spawnable, ReloadSpawnableOptionalArgs optionalArgs = {}) = 0;
//! List all entities that are spawned using this ticket.
//! @param ticket Only the entities associated with this ticket will be listed.
//! @param listCallback Required callback that will be called to list the entities on.
virtual void ListEntities(EntitySpawnTicket& ticket, ListEntitiesCallback listCallback) = 0;
//! @param optionalArgs Optional additional arguments, see ListEntitiesOptionalArgs.
virtual void ListEntities(
EntitySpawnTicket& ticket, ListEntitiesCallback listCallback, ListEntitiesOptionalArgs optionalArgs = {}) = 0;
//! List all entities that are spawned using this ticket with their spawnable index.
//! Spawnables contain a flat list of entities, which are used as templates to spawn entities from. For every spawned entity
//! the index of the entity in the spawnable that was used as a template is stored. This version of ListEntities will return
@@ -215,16 +304,23 @@ namespace AzFramework
//! created.
//! @param ticket Only the entities associated with this ticket will be listed.
//! @param listCallback Required callback that will be called to list the entities and indices on.
virtual void ListIndicesAndEntities(EntitySpawnTicket& ticket, ListIndicesEntitiesCallback listCallback) = 0;
//! @param optionalArgs Optional additional arguments, see ListEntitiesOptionalArgs.
virtual void ListIndicesAndEntities(
EntitySpawnTicket& ticket, ListIndicesEntitiesCallback listCallback, ListEntitiesOptionalArgs optionalArgs = {}) = 0;
//! Claim all entities that are spawned using this ticket. Ownership of the entities is transferred from the ticket to the
//! caller through the callback. After this call the ticket will have no entities associated with it. The caller of
//! this function will need to manage the entities after this call.
//! @param ticket Only the entities associated with this ticket will be released.
//! @param listCallback Required callback that will be called to transfer the entities through.
virtual void ClaimEntities(EntitySpawnTicket& ticket, ClaimEntitiesCallback listCallback) = 0;
//! @param optionalArgs Optional additional arguments, see ClaimEntitiesOptionalArgs.
virtual void ClaimEntities(
EntitySpawnTicket& ticket, ClaimEntitiesCallback listCallback, ClaimEntitiesOptionalArgs optionalArgs = {}) = 0;
//! Blocks until all operations made on the provided ticket before the barrier call have completed.
virtual void Barrier(EntitySpawnTicket& ticket, BarrierCallback completionCallback) = 0;
//! @param ticket The ticket to monitor.
//! @param completionCallback Required callback that will be called as soon as the barrier has been reached.
//! @param optionalArgs Optional additional arguments, see BarrierOptionalArgs.
virtual void Barrier(EntitySpawnTicket& ticket, BarrierCallback completionCallback, BarrierOptionalArgs optionalArgs = {}) = 0;
//! Register a handler for OnSpawned events.
virtual void AddOnSpawnedHandler(AZ::Event<AZ::Data::Asset<Spawnable>>::Handler& handler) = 0;
@@ -233,7 +329,7 @@ namespace AzFramework
virtual void AddOnDespawnedHandler(AZ::Event<AZ::Data::Asset<Spawnable>>::Handler& handler) = 0;
protected:
[[nodiscard]] virtual void* CreateTicket(AZ::Data::Asset<Spawnable>&& spawnable) = 0;
[[nodiscard]] virtual AZStd::pair<EntitySpawnTicket::Id, void*> CreateTicket(AZ::Data::Asset<Spawnable>&& spawnable) = 0;
virtual void DestroyTicket(void* ticket) = 0;
template<typename T>
@@ -10,9 +10,11 @@
*
*/
#include <AzCore/Casting/numeric_cast.h>
#include <AzCore/Component/ComponentApplicationBus.h>
#include <AzCore/Serialization/IdUtils.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Settings/SettingsRegistry.h>
#include <AzCore/std/parallel/scoped_lock.h>
#include <AzCore/std/smart_ptr/make_shared.h>
#include <AzFramework/Components/TransformComponent.h>
@@ -22,128 +24,130 @@
namespace AzFramework
{
void SpawnableEntitiesManager::SpawnAllEntities(EntitySpawnTicket& ticket, EntityPreInsertionCallback preInsertionCallback,
EntitySpawnCallback completionCallback)
template<typename T>
void SpawnableEntitiesManager::QueueRequest(EntitySpawnTicket& ticket, SpawnablePriority priority, T&& request)
{
request.m_ticket = &GetTicketPayload<Ticket>(ticket);
Queue& queue = priority <= m_highPriorityThreshold ? m_highPriorityQueue : m_regularPriorityQueue;
{
AZStd::scoped_lock queueLock(queue.m_pendingRequestMutex);
request.m_requestId = GetTicketPayload<Ticket>(ticket).m_nextRequestId++;
queue.m_pendingRequest.push(AZStd::move(request));
}
}
SpawnableEntitiesManager::SpawnableEntitiesManager()
{
AZ::ComponentApplicationBus::BroadcastResult(m_defaultSerializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
AZ_Assert(
m_defaultSerializeContext, "Failed to retrieve serialization context during construction of the Spawnable Entities Manager.");
if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
{
AZ::u64 value = aznumeric_caster(m_highPriorityThreshold);
settingsRegistry->Get(value, "/O3DE/AzFramework/Spawnables/HighPriorityThreshold");
m_highPriorityThreshold = aznumeric_cast<SpawnablePriority>(AZStd::clamp(value, 0llu, 255llu));
}
}
void SpawnableEntitiesManager::SpawnAllEntities(EntitySpawnTicket& ticket, SpawnAllEntitiesOptionalArgs optionalArgs)
{
AZ_Assert(ticket.IsValid(), "Ticket provided to SpawnAllEntities hasn't been initialized.");
SpawnAllEntitiesCommand queueEntry;
queueEntry.m_ticket = &ticket;
queueEntry.m_completionCallback = AZStd::move(completionCallback);
queueEntry.m_preInsertionCallback = AZStd::move(preInsertionCallback);
{
AZStd::scoped_lock queueLock(m_pendingRequestQueueMutex);
queueEntry.m_ticketId = GetTicketPayload<Ticket>(ticket).m_nextTicketId++;
m_pendingRequestQueue.push(AZStd::move(queueEntry));
}
queueEntry.m_ticketId = ticket.GetId();
queueEntry.m_serializeContext =
optionalArgs.m_serializeContext == nullptr ? m_defaultSerializeContext : optionalArgs.m_serializeContext;
queueEntry.m_completionCallback = AZStd::move(optionalArgs.m_completionCallback);
queueEntry.m_preInsertionCallback = AZStd::move(optionalArgs.m_preInsertionCallback);
QueueRequest(ticket, optionalArgs.m_priority, AZStd::move(queueEntry));
}
void SpawnableEntitiesManager::SpawnEntities(
EntitySpawnTicket& ticket, AZStd::vector<size_t> entityIndices,
EntityPreInsertionCallback preInsertionCallback, EntitySpawnCallback completionCallback)
EntitySpawnTicket& ticket, AZStd::vector<size_t> entityIndices, SpawnEntitiesOptionalArgs optionalArgs)
{
AZ_Assert(ticket.IsValid(), "Ticket provided to SpawnEntities hasn't been initialized.");
SpawnEntitiesCommand queueEntry;
queueEntry.m_ticket = &ticket;
queueEntry.m_ticketId = ticket.GetId();
queueEntry.m_entityIndices = AZStd::move(entityIndices);
queueEntry.m_completionCallback = AZStd::move(completionCallback);
queueEntry.m_preInsertionCallback = AZStd::move(preInsertionCallback);
{
AZStd::scoped_lock queueLock(m_pendingRequestQueueMutex);
queueEntry.m_ticketId = GetTicketPayload<Ticket>(ticket).m_nextTicketId++;
m_pendingRequestQueue.push(AZStd::move(queueEntry));
}
queueEntry.m_serializeContext =
optionalArgs.m_serializeContext == nullptr ? m_defaultSerializeContext : optionalArgs.m_serializeContext;
queueEntry.m_completionCallback = AZStd::move(optionalArgs.m_completionCallback);
queueEntry.m_preInsertionCallback = AZStd::move(optionalArgs.m_preInsertionCallback);
queueEntry.m_referencePreviouslySpawnedEntities = optionalArgs.m_referencePreviouslySpawnedEntities;
QueueRequest(ticket, optionalArgs.m_priority, AZStd::move(queueEntry));
}
void SpawnableEntitiesManager::DespawnAllEntities(EntitySpawnTicket& ticket, EntityDespawnCallback completionCallback)
void SpawnableEntitiesManager::DespawnAllEntities(EntitySpawnTicket& ticket, DespawnAllEntitiesOptionalArgs optionalArgs)
{
AZ_Assert(ticket.IsValid(), "Ticket provided to DespawnAllEntities hasn't been initialized.");
DespawnAllEntitiesCommand queueEntry;
queueEntry.m_ticket = &ticket;
queueEntry.m_completionCallback = AZStd::move(completionCallback);
{
AZStd::scoped_lock queueLock(m_pendingRequestQueueMutex);
queueEntry.m_ticketId = GetTicketPayload<Ticket>(ticket).m_nextTicketId++;
m_pendingRequestQueue.push(AZStd::move(queueEntry));
}
queueEntry.m_ticketId = ticket.GetId();
queueEntry.m_completionCallback = AZStd::move(optionalArgs.m_completionCallback);
QueueRequest(ticket, optionalArgs.m_priority, AZStd::move(queueEntry));
}
void SpawnableEntitiesManager::ReloadSpawnable(EntitySpawnTicket& ticket, AZ::Data::Asset<Spawnable> spawnable,
ReloadSpawnableCallback completionCallback)
void SpawnableEntitiesManager::ReloadSpawnable(
EntitySpawnTicket& ticket, AZ::Data::Asset<Spawnable> spawnable, ReloadSpawnableOptionalArgs optionalArgs)
{
AZ_Assert(ticket.IsValid(), "Ticket provided to ReloadSpawnable hasn't been initialized.");
ReloadSpawnableCommand queueEntry;
queueEntry.m_ticket = &ticket;
queueEntry.m_ticketId = ticket.GetId();
queueEntry.m_spawnable = AZStd::move(spawnable);
queueEntry.m_completionCallback = AZStd::move(completionCallback);
{
AZStd::scoped_lock queueLock(m_pendingRequestQueueMutex);
queueEntry.m_ticketId = GetTicketPayload<Ticket>(ticket).m_nextTicketId++;
m_pendingRequestQueue.push(AZStd::move(queueEntry));
}
queueEntry.m_serializeContext =
optionalArgs.m_serializeContext == nullptr ? m_defaultSerializeContext : optionalArgs.m_serializeContext;
queueEntry.m_completionCallback = AZStd::move(optionalArgs.m_completionCallback);
QueueRequest(ticket, optionalArgs.m_priority, AZStd::move(queueEntry));
}
void SpawnableEntitiesManager::ListEntities(EntitySpawnTicket& ticket, ListEntitiesCallback listCallback)
void SpawnableEntitiesManager::ListEntities(
EntitySpawnTicket& ticket, ListEntitiesCallback listCallback, ListEntitiesOptionalArgs optionalArgs)
{
AZ_Assert(listCallback, "ListEntities called on spawnable entities without a valid callback to use.");
AZ_Assert(ticket.IsValid(), "Ticket provided to ListEntities hasn't been initialized.");
ListEntitiesCommand queueEntry;
queueEntry.m_ticket = &ticket;
queueEntry.m_ticketId = ticket.GetId();
queueEntry.m_listCallback = AZStd::move(listCallback);
{
AZStd::scoped_lock queueLock(m_pendingRequestQueueMutex);
queueEntry.m_ticketId = GetTicketPayload<Ticket>(ticket).m_nextTicketId++;
m_pendingRequestQueue.push(AZStd::move(queueEntry));
}
QueueRequest(ticket, optionalArgs.m_priority, AZStd::move(queueEntry));
}
void SpawnableEntitiesManager::ListIndicesAndEntities(EntitySpawnTicket& ticket, ListIndicesEntitiesCallback listCallback)
void SpawnableEntitiesManager::ListIndicesAndEntities(
EntitySpawnTicket& ticket, ListIndicesEntitiesCallback listCallback, ListEntitiesOptionalArgs optionalArgs)
{
AZ_Assert(listCallback, "ListEntities called on spawnable entities without a valid callback to use.");
AZ_Assert(ticket.IsValid(), "Ticket provided to ListEntities hasn't been initialized.");
ListIndicesEntitiesCommand queueEntry;
queueEntry.m_ticket = &ticket;
queueEntry.m_ticketId = ticket.GetId();
queueEntry.m_listCallback = AZStd::move(listCallback);
{
AZStd::scoped_lock queueLock(m_pendingRequestQueueMutex);
queueEntry.m_ticketId = GetTicketPayload<Ticket>(ticket).m_nextTicketId++;
m_pendingRequestQueue.push(AZStd::move(queueEntry));
}
QueueRequest(ticket, optionalArgs.m_priority, AZStd::move(queueEntry));
}
void SpawnableEntitiesManager::ClaimEntities(EntitySpawnTicket& ticket, ClaimEntitiesCallback listCallback)
void SpawnableEntitiesManager::ClaimEntities(
EntitySpawnTicket& ticket, ClaimEntitiesCallback listCallback, ClaimEntitiesOptionalArgs optionalArgs)
{
AZ_Assert(listCallback, "ClaimEntities called on spawnable entities without a valid callback to use.");
AZ_Assert(ticket.IsValid(), "Ticket provided to ClaimEntities hasn't been initialized.");
ClaimEntitiesCommand queueEntry;
queueEntry.m_ticket = &ticket;
queueEntry.m_ticketId = ticket.GetId();
queueEntry.m_listCallback = AZStd::move(listCallback);
{
AZStd::scoped_lock queueLock(m_pendingRequestQueueMutex);
queueEntry.m_ticketId = GetTicketPayload<Ticket>(ticket).m_nextTicketId++;
m_pendingRequestQueue.push(AZStd::move(queueEntry));
}
QueueRequest(ticket, optionalArgs.m_priority, AZStd::move(queueEntry));
}
void SpawnableEntitiesManager::Barrier(EntitySpawnTicket& ticket, BarrierCallback completionCallback)
void SpawnableEntitiesManager::Barrier(EntitySpawnTicket& ticket, BarrierCallback completionCallback, BarrierOptionalArgs optionalArgs)
{
AZ_Assert(completionCallback, "Barrier on spawnable entities called without a valid callback to use.");
AZ_Assert(ticket.IsValid(), "Ticket provided to Barrier hasn't been initialized.");
BarrierCommand queueEntry;
queueEntry.m_ticket = &ticket;
queueEntry.m_ticketId = ticket.GetId();
queueEntry.m_completionCallback = AZStd::move(completionCallback);
{
AZStd::scoped_lock queueLock(m_pendingRequestQueueMutex);
queueEntry.m_ticketId = GetTicketPayload<Ticket>(ticket).m_nextTicketId++;
m_pendingRequestQueue.push(AZStd::move(queueEntry));
}
QueueRequest(ticket, optionalArgs.m_priority, AZStd::move(queueEntry));
}
void SpawnableEntitiesManager::AddOnSpawnedHandler(AZ::Event<AZ::Data::Asset<Spawnable>>::Handler& handler)
@@ -156,69 +160,90 @@ namespace AzFramework
handler.Connect(m_onDespawnedEvent);
}
auto SpawnableEntitiesManager::ProcessQueue() -> CommandQueueStatus
auto SpawnableEntitiesManager::ProcessQueue(CommandQueuePriority priority) -> CommandQueueStatus
{
AZStd::queue<Requests> pendingRequestQueue;
CommandQueueStatus result = CommandQueueStatus::NoCommandsLeft;
if ((priority & CommandQueuePriority::High) == CommandQueuePriority::High)
{
AZStd::scoped_lock queueLock(m_pendingRequestQueueMutex);
m_pendingRequestQueue.swap(pendingRequestQueue);
if (ProcessQueue(m_highPriorityQueue) == CommandQueueStatus::HasCommandsLeft)
{
result = CommandQueueStatus::HasCommandsLeft;
}
}
if ((priority & CommandQueuePriority::Regular) == CommandQueuePriority::Regular)
{
if (ProcessQueue(m_regularPriorityQueue) == CommandQueueStatus::HasCommandsLeft)
{
result = CommandQueueStatus::HasCommandsLeft;
}
}
return result;
}
auto SpawnableEntitiesManager::ProcessQueue(Queue& queue) -> CommandQueueStatus
{
// Process delayed requests first.
// Only process the requests that are currently in this queue, not the ones that could be re-added if they still can't complete.
size_t delayedSize = queue.m_delayed.size();
for (size_t i = 0; i < delayedSize; ++i)
{
Requests& request = queue.m_delayed.front();
bool result = AZStd::visit(
[this](auto&& args) -> bool
{
return ProcessRequest(args);
},
request);
if (!result)
{
queue.m_delayed.emplace_back(AZStd::move(request));
}
queue.m_delayed.pop_front();
}
if (!pendingRequestQueue.empty() || !m_delayedQueue.empty())
// Process newly added requests.
while (true)
{
AZ::SerializeContext* serializeContext = nullptr;
AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
AZ_Assert(serializeContext, "Failed to retrieve serialization context.");
// Only process the requests that are currently in this queue, not the ones that could be re-added if they still can't complete.
size_t delayedSize = m_delayedQueue.size();
for (size_t i = 0; i < delayedSize; ++i)
AZStd::queue<Requests> pendingRequestQueue;
{
Requests& request = m_delayedQueue.front();
bool result = AZStd::visit([this, serializeContext](auto&& args) -> bool
{
return ProcessRequest(args, *serializeContext);
}, request);
if (!result)
{
m_delayedQueue.emplace_back(AZStd::move(request));
}
m_delayedQueue.pop_front();
AZStd::scoped_lock queueLock(queue.m_pendingRequestMutex);
queue.m_pendingRequest.swap(pendingRequestQueue);
}
do
if (!pendingRequestQueue.empty())
{
while (!pendingRequestQueue.empty())
{
Requests& request = pendingRequestQueue.front();
bool result = AZStd::visit([this, serializeContext](auto&& args) -> bool
bool result = AZStd::visit(
[this](auto&& args) -> bool
{
return ProcessRequest(args, *serializeContext);
}, request);
return ProcessRequest(args);
},
request);
if (!result)
{
m_delayedQueue.emplace_back(AZStd::move(request));
queue.m_delayed.emplace_back(AZStd::move(request));
}
pendingRequestQueue.pop();
}
}
else
{
break;
}
};
// Spawning entities can result in more entities being queued to spawn. Repeat spawning until the queue is
// empty to avoid a chain of entity spawning getting dragged out over multiple frames.
{
AZStd::scoped_lock queueLock(m_pendingRequestQueueMutex);
m_pendingRequestQueue.swap(pendingRequestQueue);
}
} while (!pendingRequestQueue.empty());
}
return m_delayedQueue.empty() ? CommandQueueStatus::NoCommandLeft : CommandQueueStatus::HasCommandsLeft;
return queue.m_delayed.empty() ? CommandQueueStatus::NoCommandsLeft : CommandQueueStatus::HasCommandsLeft;
}
void* SpawnableEntitiesManager::CreateTicket(AZ::Data::Asset<Spawnable>&& spawnable)
AZStd::pair<uint64_t, void*> SpawnableEntitiesManager::CreateTicket(AZ::Data::Asset<Spawnable>&& spawnable)
{
static AZStd::atomic_uint64_t idCounter { 1 };
auto result = aznew Ticket();
result->m_spawnable = AZStd::move(spawnable);
return result;
return AZStd::make_pair<EntitySpawnTicket::Id, void*>(idCounter++, result);
}
void SpawnableEntitiesManager::DestroyTicket(void* ticket)
@@ -226,33 +251,23 @@ namespace AzFramework
DestroyTicketCommand queueEntry;
queueEntry.m_ticket = reinterpret_cast<Ticket*>(ticket);
{
AZStd::scoped_lock queueLock(m_pendingRequestQueueMutex);
queueEntry.m_ticketId = reinterpret_cast<Ticket*>(ticket)->m_nextTicketId++;
m_pendingRequestQueue.push(AZStd::move(queueEntry));
AZStd::scoped_lock queueLock(m_regularPriorityQueue.m_pendingRequestMutex);
queueEntry.m_requestId = reinterpret_cast<Ticket*>(ticket)->m_nextRequestId++;
m_regularPriorityQueue.m_pendingRequest.push(AZStd::move(queueEntry));
}
}
AZ::Entity* SpawnableEntitiesManager::SpawnSingleEntity(const AZ::Entity& entityTemplate, AZ::SerializeContext& serializeContext)
{
AZ::Entity* clone = serializeContext.CloneObject(&entityTemplate);
AZ_Assert(clone != nullptr, "Failed to clone spawnable entity.");
clone->SetId(AZ::Entity::MakeId());
GameEntityContextRequestBus::Broadcast(&GameEntityContextRequestBus::Events::AddGameEntity, clone);
return clone;
}
AZ::Entity* SpawnableEntitiesManager::CloneSingleEntity(const AZ::Entity& entityTemplate,
EntityIdMap& templateToCloneEntityIdMap, AZ::SerializeContext& serializeContext)
EntityIdMap& templateToCloneMap, AZ::SerializeContext& serializeContext)
{
return AZ::IdUtils::Remapper<AZ::EntityId>::CloneObjectAndGenerateNewIdsAndFixRefs(
&entityTemplate, templateToCloneEntityIdMap, &serializeContext);
return AZ::IdUtils::Remapper<AZ::EntityId, true>::CloneObjectAndGenerateNewIdsAndFixRefs(
&entityTemplate, templateToCloneMap, &serializeContext);
}
bool SpawnableEntitiesManager::ProcessRequest(SpawnAllEntitiesCommand& request, AZ::SerializeContext& serializeContext)
bool SpawnableEntitiesManager::ProcessRequest(SpawnAllEntitiesCommand& request)
{
Ticket& ticket = GetTicketPayload<Ticket>(*request.m_ticket);
if (ticket.m_spawnable.IsReady() && request.m_ticketId == ticket.m_currentTicketId)
Ticket& ticket = *request.m_ticket;
if (ticket.m_spawnable.IsReady() && request.m_requestId == ticket.m_currentRequestId)
{
AZStd::vector<AZ::Entity*>& spawnedEntities = ticket.m_spawnedEntities;
AZStd::vector<size_t>& spawnedEntityIndices = ticket.m_spawnedEntityIndices;
@@ -273,13 +288,9 @@ namespace AzFramework
spawnedEntityIndices.reserve(spawnedEntityIndices.size() + entitiesToSpawnSize);
templateToCloneEntityIdMap.reserve(entitiesToSpawnSize);
// Mark all indices as spawned
for (size_t i = 0; i < entitiesToSpawnSize; ++i)
{
const AZ::Entity& entityTemplate = *entitiesToSpawn[i];
AZ::Entity* clone = CloneSingleEntity(entityTemplate, templateToCloneEntityIdMap, serializeContext);
AZ::Entity* clone = CloneSingleEntity(*entitiesToSpawn[i], templateToCloneEntityIdMap, *request.m_serializeContext);
AZ_Assert(clone != nullptr, "Failed to clone spawnable entity.");
spawnedEntities.emplace_back(clone);
@@ -287,40 +298,31 @@ namespace AzFramework
}
// loadAll is true if every entity has been spawned only once
if (spawnedEntities.size() == entitiesToSpawnSize)
{
ticket.m_loadAll = true;
}
else
{
// Case where there were already spawns from a previous request
ticket.m_loadAll = false;
}
ticket.m_loadAll = (spawnedEntities.size() == entitiesToSpawnSize);
// Let other systems know about newly spawned entities for any pre-processing before adding to the scene/game context.
if (request.m_preInsertionCallback)
{
request.m_preInsertionCallback(*request.m_ticket, SpawnableEntityContainerView(
request.m_preInsertionCallback(request.m_ticketId, SpawnableEntityContainerView(
ticket.m_spawnedEntities.begin() + spawnedEntitiesInitialCount, ticket.m_spawnedEntities.end()));
}
// Add to the game context, now the entities are active
AZStd::for_each(ticket.m_spawnedEntities.begin() + spawnedEntitiesInitialCount, ticket.m_spawnedEntities.end(),
[](AZ::Entity* entity)
for (auto it = ticket.m_spawnedEntities.begin() + spawnedEntitiesInitialCount; it != ticket.m_spawnedEntities.end(); ++it)
{
GameEntityContextRequestBus::Broadcast(&GameEntityContextRequestBus::Events::AddGameEntity, entity);
});
GameEntityContextRequestBus::Broadcast(&GameEntityContextRequestBus::Events::AddGameEntity, *it);
}
// Let other systems know about newly spawned entities for any post-processing after adding to the scene/game context.
if (request.m_completionCallback)
{
request.m_completionCallback(*request.m_ticket, SpawnableConstEntityContainerView(
request.m_completionCallback(request.m_ticketId, SpawnableConstEntityContainerView(
ticket.m_spawnedEntities.begin() + spawnedEntitiesInitialCount, ticket.m_spawnedEntities.end()));
}
m_onSpawnedEvent.Signal(ticket.m_spawnable);
ticket.m_currentTicketId++;
ticket.m_currentRequestId++;
return true;
}
else
@@ -329,21 +331,41 @@ namespace AzFramework
}
}
bool SpawnableEntitiesManager::ProcessRequest(SpawnEntitiesCommand& request, AZ::SerializeContext& serializeContext)
bool SpawnableEntitiesManager::ProcessRequest(SpawnEntitiesCommand& request)
{
Ticket& ticket = GetTicketPayload<Ticket>(*request.m_ticket);
if (ticket.m_spawnable.IsReady() && request.m_ticketId == ticket.m_currentTicketId)
Ticket& ticket = *request.m_ticket;
if (ticket.m_spawnable.IsReady() && request.m_requestId == ticket.m_currentRequestId)
{
AZStd::vector<AZ::Entity*>& spawnedEntities = ticket.m_spawnedEntities;
AZStd::vector<size_t>& spawnedEntityIndices = ticket.m_spawnedEntityIndices;
AZ_Assert(
spawnedEntities.size() == spawnedEntityIndices.size(),
"The indices for the spawned entities has gone out of sync with the entities.");
// Keep track how many entities there were in the array initially
// Keep track of how many entities there were in the array initially
size_t spawnedEntitiesInitialCount = spawnedEntities.size();
// These are 'template' entities we'll be cloning from
const Spawnable::EntityList& entitiesToSpawn = ticket.m_spawnable->GetEntities();
size_t entitiesToSpawnSize = request.m_entityIndices.size();
// Reconstruct the template to entity mapping.
EntityIdMap templateToCloneEntityIdMap;
if (!request.m_referencePreviouslySpawnedEntities)
{
templateToCloneEntityIdMap.reserve(entitiesToSpawnSize);
}
else
{
templateToCloneEntityIdMap.reserve(spawnedEntitiesInitialCount + entitiesToSpawnSize);
SpawnableConstIndexEntityContainerView indexEntityView(
spawnedEntities.begin(), spawnedEntityIndices.begin(), spawnedEntities.size());
for (auto& entry : indexEntityView)
{
templateToCloneEntityIdMap.insert_or_assign(entitiesToSpawn[entry.GetIndex()]->GetId(), entry.GetEntity()->GetId());
}
}
spawnedEntities.reserve(spawnedEntities.size() + entitiesToSpawnSize);
spawnedEntityIndices.reserve(spawnedEntityIndices.size() + entitiesToSpawnSize);
@@ -351,15 +373,11 @@ namespace AzFramework
{
if (index < entitiesToSpawn.size())
{
const AZ::Entity& entityTemplate = *entitiesToSpawn[index];
AZ::Entity* clone = serializeContext.CloneObject(&entityTemplate);
AZ::Entity* clone = CloneSingleEntity(*entitiesToSpawn[index], templateToCloneEntityIdMap, *request.m_serializeContext);
AZ_Assert(clone != nullptr, "Failed to clone spawnable entity.");
clone->SetId(AZ::Entity::MakeId());
spawnedEntities.push_back(clone);
spawnedEntityIndices.push_back(index);
}
}
ticket.m_loadAll = false;
@@ -367,28 +385,25 @@ namespace AzFramework
// Let other systems know about newly spawned entities for any pre-processing before adding to the scene/game context.
if (request.m_preInsertionCallback)
{
request.m_preInsertionCallback(
*request.m_ticket,
SpawnableEntityContainerView(
request.m_preInsertionCallback(request.m_ticketId, SpawnableEntityContainerView(
ticket.m_spawnedEntities.begin() + spawnedEntitiesInitialCount, ticket.m_spawnedEntities.end()));
}
// Add to the game context, now the entities are active
AZStd::for_each(ticket.m_spawnedEntities.begin() + spawnedEntitiesInitialCount, ticket.m_spawnedEntities.end(),
[](AZ::Entity* entity)
for (auto it = ticket.m_spawnedEntities.begin() + spawnedEntitiesInitialCount; it != ticket.m_spawnedEntities.end(); ++it)
{
GameEntityContextRequestBus::Broadcast(&GameEntityContextRequestBus::Events::AddGameEntity, entity);
});
GameEntityContextRequestBus::Broadcast(&GameEntityContextRequestBus::Events::AddGameEntity, *it);
}
if (request.m_completionCallback)
{
request.m_completionCallback(*request.m_ticket, SpawnableConstEntityContainerView(
request.m_completionCallback(request.m_ticketId, SpawnableConstEntityContainerView(
ticket.m_spawnedEntities.begin() + spawnedEntitiesInitialCount, ticket.m_spawnedEntities.end()));
}
m_onSpawnedEvent.Signal(ticket.m_spawnable);
ticket.m_currentTicketId++;
ticket.m_currentRequestId++;
return true;
}
else
@@ -397,11 +412,10 @@ namespace AzFramework
}
}
bool SpawnableEntitiesManager::ProcessRequest(DespawnAllEntitiesCommand& request,
[[maybe_unused]] AZ::SerializeContext& serializeContext)
bool SpawnableEntitiesManager::ProcessRequest(DespawnAllEntitiesCommand& request)
{
Ticket& ticket = GetTicketPayload<Ticket>(*request.m_ticket);
if (request.m_ticketId == ticket.m_currentTicketId)
Ticket& ticket = *request.m_ticket;
if (request.m_requestId == ticket.m_currentRequestId)
{
for (AZ::Entity* entity : ticket.m_spawnedEntities)
{
@@ -417,12 +431,12 @@ namespace AzFramework
if (request.m_completionCallback)
{
request.m_completionCallback(*request.m_ticket);
request.m_completionCallback(request.m_ticketId);
}
m_onDespawnedEvent.Signal(ticket.m_spawnable);
ticket.m_currentTicketId++;
ticket.m_currentRequestId++;
return true;
}
else
@@ -431,13 +445,13 @@ namespace AzFramework
}
}
bool SpawnableEntitiesManager::ProcessRequest(ReloadSpawnableCommand& request, AZ::SerializeContext& serializeContext)
bool SpawnableEntitiesManager::ProcessRequest(ReloadSpawnableCommand& request)
{
Ticket& ticket = GetTicketPayload<Ticket>(*request.m_ticket);
Ticket& ticket = *request.m_ticket;
AZ_Assert(ticket.m_spawnable.GetId() == request.m_spawnable.GetId(),
"Spawnable is being reloaded, but the provided spawnable has a different asset id. "
"This will likely result in unexpected entities being created.");
if (ticket.m_spawnable.IsReady() && request.m_ticketId == ticket.m_currentTicketId)
if (ticket.m_spawnable.IsReady() && request.m_requestId == ticket.m_currentRequestId)
{
// Delete the original entities.
for (AZ::Entity* entity : ticket.m_spawnedEntities)
@@ -454,50 +468,54 @@ namespace AzFramework
// Rebuild the list of entities.
ticket.m_spawnedEntities.clear();
const Spawnable::EntityList& entities = request.m_spawnable->GetEntities();
// Map keeps track of ids from template (spawnable) to clone (instance)
// Allowing patch ups of fields referring to entityIds outside of a given entity
EntityIdMap templateToCloneEntityIdMap;
if (ticket.m_loadAll)
{
// The new spawnable may have a different number of entities and since the intent of the user was
// to load every, simply start over.
// to spawn every entity, simply start over.
ticket.m_spawnedEntityIndices.clear();
size_t entitiesToSpawnSize = entities.size();
// Map keeps track of ids from template (spawnable) to clone (instance)
// Allowing patch ups of fields referring to entityIds outside of a given entity
EntityIdMap templateToCloneEntityIdMap;
templateToCloneEntityIdMap.reserve(entitiesToSpawnSize);
// Mark all indices as spawned
for (size_t i = 0; i < entitiesToSpawnSize; ++i)
{
const AZ::Entity& entityTemplate = *entities[i];
AZ::Entity* clone = CloneSingleEntity(entityTemplate, templateToCloneEntityIdMap, serializeContext);
AZ::Entity* clone = CloneSingleEntity(*entities[i], templateToCloneEntityIdMap, *request.m_serializeContext);
AZ_Assert(clone != nullptr, "Failed to clone spawnable entity.");
ticket.m_spawnedEntities.emplace_back(clone);
ticket.m_spawnedEntities.push_back(clone);
ticket.m_spawnedEntityIndices.push_back(i);
}
}
else
{
size_t entitiesSize = entities.size();
templateToCloneEntityIdMap.reserve(entitiesSize);
for (size_t index : ticket.m_spawnedEntityIndices)
{
ticket.m_spawnedEntities.push_back(
index < entitiesSize ? SpawnSingleEntity(*entities[index], serializeContext) : nullptr);
// It's possible for the new spawnable to have a different number of entities, so guard against this.
// It's also possible that the entities have moved within the spawnable to a new index. This can't be
// detected and will result in the incorrect entities being spawned.
if (index < entitiesSize)
{
AZ::Entity* clone = CloneSingleEntity(*entities[index], templateToCloneEntityIdMap, *request.m_serializeContext);
AZ_Assert(clone != nullptr, "Failed to clone spawnable entity.");
ticket.m_spawnedEntities.push_back(clone);
}
}
}
ticket.m_spawnable = AZStd::move(request.m_spawnable);
if (request.m_completionCallback)
{
request.m_completionCallback(*request.m_ticket, SpawnableConstEntityContainerView(
request.m_completionCallback(request.m_ticketId, SpawnableConstEntityContainerView(
ticket.m_spawnedEntities.begin(), ticket.m_spawnedEntities.end()));
}
ticket.m_currentTicketId++;
ticket.m_currentRequestId++;
m_onSpawnedEvent.Signal(ticket.m_spawnable);
@@ -509,14 +527,14 @@ namespace AzFramework
}
}
bool SpawnableEntitiesManager::ProcessRequest(ListEntitiesCommand& request, [[maybe_unused]] AZ::SerializeContext& serializeContext)
bool SpawnableEntitiesManager::ProcessRequest(ListEntitiesCommand& request)
{
Ticket& ticket = GetTicketPayload<Ticket>(*request.m_ticket);
if (request.m_ticketId == ticket.m_currentTicketId)
Ticket& ticket = *request.m_ticket;
if (request.m_requestId == ticket.m_currentRequestId)
{
request.m_listCallback(*request.m_ticket, SpawnableConstEntityContainerView(
request.m_listCallback(request.m_ticketId, SpawnableConstEntityContainerView(
ticket.m_spawnedEntities.begin(), ticket.m_spawnedEntities.end()));
ticket.m_currentTicketId++;
ticket.m_currentRequestId++;
return true;
}
else
@@ -525,19 +543,17 @@ namespace AzFramework
}
}
bool SpawnableEntitiesManager::ProcessRequest(ListIndicesEntitiesCommand& request, [[maybe_unused]] AZ::SerializeContext& serializeContext)
bool SpawnableEntitiesManager::ProcessRequest(ListIndicesEntitiesCommand& request)
{
Ticket& ticket = GetTicketPayload<Ticket>(*request.m_ticket);
if (request.m_ticketId == ticket.m_currentTicketId)
Ticket& ticket = *request.m_ticket;
if (request.m_requestId == ticket.m_currentRequestId)
{
AZ_Assert(
ticket.m_spawnedEntities.size() == ticket.m_spawnedEntityIndices.size(),
"Entities and indices on spawnable ticket have gone out of sync.");
request.m_listCallback(
*request.m_ticket,
SpawnableConstIndexEntityContainerView(
request.m_listCallback(request.m_ticketId, SpawnableConstIndexEntityContainerView(
ticket.m_spawnedEntities.begin(), ticket.m_spawnedEntityIndices.begin(), ticket.m_spawnedEntities.size()));
ticket.m_currentTicketId++;
ticket.m_currentRequestId++;
return true;
}
else
@@ -546,18 +562,18 @@ namespace AzFramework
}
}
bool SpawnableEntitiesManager::ProcessRequest(ClaimEntitiesCommand& request, [[maybe_unused]] AZ::SerializeContext& serializeContext)
bool SpawnableEntitiesManager::ProcessRequest(ClaimEntitiesCommand& request)
{
Ticket& ticket = GetTicketPayload<Ticket>(*request.m_ticket);
if (request.m_ticketId == ticket.m_currentTicketId)
Ticket& ticket = *request.m_ticket;
if (request.m_requestId == ticket.m_currentRequestId)
{
request.m_listCallback(*request.m_ticket, SpawnableEntityContainerView(
request.m_listCallback(request.m_ticketId, SpawnableEntityContainerView(
ticket.m_spawnedEntities.begin(), ticket.m_spawnedEntities.end()));
ticket.m_spawnedEntities.clear();
ticket.m_spawnedEntityIndices.clear();
ticket.m_currentTicketId++;
ticket.m_currentRequestId++;
return true;
}
else
@@ -566,17 +582,17 @@ namespace AzFramework
}
}
bool SpawnableEntitiesManager::ProcessRequest(BarrierCommand& request, [[maybe_unused]] AZ::SerializeContext& serializeContext)
bool SpawnableEntitiesManager::ProcessRequest(BarrierCommand& request)
{
Ticket& ticket = GetTicketPayload<Ticket>(*request.m_ticket);
if (request.m_ticketId == ticket.m_currentTicketId)
Ticket& ticket = *request.m_ticket;
if (request.m_requestId == ticket.m_currentRequestId)
{
if (request.m_completionCallback)
{
request.m_completionCallback(*request.m_ticket);
request.m_completionCallback(request.m_ticketId);
}
ticket.m_currentTicketId++;
ticket.m_currentRequestId++;
return true;
}
else
@@ -585,9 +601,9 @@ namespace AzFramework
}
}
bool SpawnableEntitiesManager::ProcessRequest(DestroyTicketCommand& request, [[maybe_unused]] AZ::SerializeContext& serializeContext)
bool SpawnableEntitiesManager::ProcessRequest(DestroyTicketCommand& request)
{
if (request.m_ticketId == request.m_ticket->m_currentTicketId)
if (request.m_requestId == request.m_ticket->m_currentRequestId)
{
for (AZ::Entity* entity : request.m_ticket->m_spawnedEntities)
{
@@ -606,24 +622,4 @@ namespace AzFramework
return false;
}
}
bool SpawnableEntitiesManager::IsEqualTicket(const EntitySpawnTicket* lhs, const EntitySpawnTicket* rhs)
{
return GetTicketPayload<Ticket>(lhs) == GetTicketPayload<Ticket>(rhs);
}
bool SpawnableEntitiesManager::IsEqualTicket(const Ticket* lhs, const EntitySpawnTicket* rhs)
{
return lhs == GetTicketPayload<Ticket>(rhs);
}
bool SpawnableEntitiesManager::IsEqualTicket(const EntitySpawnTicket* lhs, const Ticket* rhs)
{
return GetTicketPayload<Ticket>(lhs) == rhs;
}
bool SpawnableEntitiesManager::IsEqualTicket(const Ticket* lhs, const Ticket* rhs)
{
return lhs = rhs;
}
} // namespace AzFramework
@@ -29,8 +29,6 @@ namespace AZ
namespace AzFramework
{
using EntityIdMap = AZStd::unordered_map<AZ::EntityId, AZ::EntityId>;
class SpawnableEntitiesManager
: public SpawnableEntitiesInterface::Registrar
{
@@ -38,31 +36,42 @@ namespace AzFramework
AZ_RTTI(AzFramework::SpawnableEntitiesManager, "{6E14333F-128C-464C-94CA-A63B05A5E51C}");
AZ_CLASS_ALLOCATOR(SpawnableEntitiesManager, AZ::SystemAllocator, 0);
using EntityIdMap = AZStd::unordered_map<AZ::EntityId, AZ::EntityId>;
enum class CommandQueueStatus : bool
{
HasCommandsLeft,
NoCommandLeft
NoCommandsLeft
};
enum class CommandQueuePriority
{
High = 1 << 0,
Regular = 1 << 1
};
SpawnableEntitiesManager();
~SpawnableEntitiesManager() override = default;
//
// The following functions are thread safe
//
void SpawnAllEntities(EntitySpawnTicket& ticket, EntityPreInsertionCallback preInsertionCallback = {}, EntitySpawnCallback completionCallback = {}) override;
void SpawnEntities(EntitySpawnTicket& ticket, AZStd::vector<size_t> entityIndices, EntityPreInsertionCallback preInsertionCallback = {},
EntitySpawnCallback completionCallback = {}) override;
void DespawnAllEntities(EntitySpawnTicket& ticket, EntityDespawnCallback completionCallback = {}) override;
void SpawnAllEntities(EntitySpawnTicket& ticket, SpawnAllEntitiesOptionalArgs optionalArgs = {}) override;
void SpawnEntities(
EntitySpawnTicket& ticket, AZStd::vector<size_t> entityIndices, SpawnEntitiesOptionalArgs optionalArgs = {}) override;
void DespawnAllEntities(EntitySpawnTicket& ticket, DespawnAllEntitiesOptionalArgs optionalArgs = {}) override;
void ReloadSpawnable(
EntitySpawnTicket& ticket, AZ::Data::Asset<Spawnable> spawnable, ReloadSpawnableOptionalArgs optionalArgs = {}) override;
void ReloadSpawnable(EntitySpawnTicket& ticket, AZ::Data::Asset<Spawnable> spawnable,
ReloadSpawnableCallback completionCallback = {}) override;
void ListEntities(
EntitySpawnTicket& ticket, ListEntitiesCallback listCallback, ListEntitiesOptionalArgs optionalArgs = {}) override;
void ListIndicesAndEntities(
EntitySpawnTicket& ticket, ListIndicesEntitiesCallback listCallback, ListEntitiesOptionalArgs optionalArgs = {}) override;
void ClaimEntities(
EntitySpawnTicket& ticket, ClaimEntitiesCallback listCallback, ClaimEntitiesOptionalArgs optionalArgs = {}) override;
void ListEntities(EntitySpawnTicket& ticket, ListEntitiesCallback listCallback) override;
void ListIndicesAndEntities(EntitySpawnTicket& ticket, ListIndicesEntitiesCallback listCallback) override;
void ClaimEntities(EntitySpawnTicket& ticket, ClaimEntitiesCallback listCallback) override;
void Barrier(EntitySpawnTicket& spawnInfo, BarrierCallback completionCallback) override;
void Barrier(EntitySpawnTicket& spawnInfo, BarrierCallback completionCallback, BarrierOptionalArgs optionalArgs = {}) override;
void AddOnSpawnedHandler(AZ::Event<AZ::Data::Asset<Spawnable>>::Handler& handler) override;
void AddOnDespawnedHandler(AZ::Event<AZ::Data::Asset<Spawnable>>::Handler& handler) override;
@@ -71,13 +80,9 @@ namespace AzFramework
// The following function is thread safe but intended to be run from the main thread.
//
CommandQueueStatus ProcessQueue();
CommandQueueStatus ProcessQueue(CommandQueuePriority priority);
protected:
void* CreateTicket(AZ::Data::Asset<Spawnable>&& spawnable) override;
void DestroyTicket(void* ticket) override;
private:
struct Ticket
{
AZ_CLASS_ALLOCATOR(Ticket, AZ::ThreadPoolAllocator, 0);
@@ -86,8 +91,8 @@ namespace AzFramework
AZStd::vector<AZ::Entity*> m_spawnedEntities;
AZStd::vector<size_t> m_spawnedEntityIndices;
AZ::Data::Asset<Spawnable> m_spawnable;
uint32_t m_nextTicketId{ 0 }; //!< Next id for this ticket.
uint32_t m_currentTicketId{ 0 }; //!< The id for the command that should be executed.
uint32_t m_nextRequestId{ 0 }; //!< Next id for this ticket.
uint32_t m_currentRequestId { 0 }; //!< The id for the command that should be executed.
bool m_loadAll{ true };
};
@@ -95,90 +100,116 @@ namespace AzFramework
{
EntitySpawnCallback m_completionCallback;
EntityPreInsertionCallback m_preInsertionCallback;
EntitySpawnTicket* m_ticket;
uint32_t m_ticketId;
AZ::SerializeContext* m_serializeContext;
Ticket* m_ticket;
EntitySpawnTicket::Id m_ticketId;
uint32_t m_requestId;
};
struct SpawnEntitiesCommand
{
AZStd::vector<size_t> m_entityIndices;
EntitySpawnCallback m_completionCallback;
EntityPreInsertionCallback m_preInsertionCallback;
EntitySpawnTicket* m_ticket;
uint32_t m_ticketId;
AZ::SerializeContext* m_serializeContext;
Ticket* m_ticket;
EntitySpawnTicket::Id m_ticketId;
uint32_t m_requestId;
bool m_referencePreviouslySpawnedEntities;
};
struct DespawnAllEntitiesCommand
{
EntityDespawnCallback m_completionCallback;
EntitySpawnTicket* m_ticket;
uint32_t m_ticketId;
Ticket* m_ticket;
EntitySpawnTicket::Id m_ticketId;
uint32_t m_requestId;
};
struct ReloadSpawnableCommand
{
AZ::Data::Asset<Spawnable> m_spawnable;
ReloadSpawnableCallback m_completionCallback;
EntitySpawnTicket* m_ticket;
uint32_t m_ticketId;
AZ::SerializeContext* m_serializeContext;
Ticket* m_ticket;
EntitySpawnTicket::Id m_ticketId;
uint32_t m_requestId;
};
struct ListEntitiesCommand
{
ListEntitiesCallback m_listCallback;
EntitySpawnTicket* m_ticket;
uint32_t m_ticketId;
Ticket* m_ticket;
EntitySpawnTicket::Id m_ticketId;
uint32_t m_requestId;
};
struct ListIndicesEntitiesCommand
{
ListIndicesEntitiesCallback m_listCallback;
EntitySpawnTicket* m_ticket;
uint32_t m_ticketId;
Ticket* m_ticket;
EntitySpawnTicket::Id m_ticketId;
uint32_t m_requestId;
};
struct ClaimEntitiesCommand
{
ClaimEntitiesCallback m_listCallback;
EntitySpawnTicket* m_ticket;
uint32_t m_ticketId;
Ticket* m_ticket;
EntitySpawnTicket::Id m_ticketId;
uint32_t m_requestId;
};
struct BarrierCommand
{
BarrierCallback m_completionCallback;
EntitySpawnTicket* m_ticket;
uint32_t m_ticketId;
Ticket* m_ticket;
EntitySpawnTicket::Id m_ticketId;
uint32_t m_requestId;
};
struct DestroyTicketCommand
{
Ticket* m_ticket;
uint32_t m_ticketId;
uint32_t m_requestId;
};
using Requests = AZStd::variant<
SpawnAllEntitiesCommand, SpawnEntitiesCommand, DespawnAllEntitiesCommand, ReloadSpawnableCommand, ListEntitiesCommand,
ListIndicesEntitiesCommand, ClaimEntitiesCommand, BarrierCommand, DestroyTicketCommand>;
AZ::Entity* SpawnSingleEntity(const AZ::Entity& entityTemplate,
AZ::SerializeContext& serializeContext);
struct Queue
{
AZStd::deque<Requests> m_delayed; //!< Requests that were processed before, but couldn't be completed.
AZStd::queue<Requests> m_pendingRequest; //!< Requests waiting to be processed for the first time.
AZStd::mutex m_pendingRequestMutex;
};
AZ::Entity* CloneSingleEntity(const AZ::Entity& entityTemplate,
EntityIdMap& templateToCloneEntityIdMap, AZ::SerializeContext& serializeContext);
template<typename T>
void QueueRequest(EntitySpawnTicket& ticket, SpawnablePriority priority, T&& request);
AZStd::pair<EntitySpawnTicket::Id, void*> CreateTicket(AZ::Data::Asset<Spawnable>&& spawnable) override;
void DestroyTicket(void* ticket) override;
bool ProcessRequest(SpawnAllEntitiesCommand& request, AZ::SerializeContext& serializeContext);
bool ProcessRequest(SpawnEntitiesCommand& request, AZ::SerializeContext& serializeContext);
bool ProcessRequest(DespawnAllEntitiesCommand& request, AZ::SerializeContext& serializeContext);
bool ProcessRequest(ReloadSpawnableCommand& request, AZ::SerializeContext& serializeContext);
bool ProcessRequest(ListEntitiesCommand& request, AZ::SerializeContext& serializeContext);
bool ProcessRequest(ListIndicesEntitiesCommand& request, AZ::SerializeContext& serializeContext);
bool ProcessRequest(ClaimEntitiesCommand& request, AZ::SerializeContext& serializeContext);
bool ProcessRequest(BarrierCommand& request, AZ::SerializeContext& serializeContext);
bool ProcessRequest(DestroyTicketCommand& request, AZ::SerializeContext& serializeContext);
CommandQueueStatus ProcessQueue(Queue& queue);
[[nodiscard]] static bool IsEqualTicket(const EntitySpawnTicket* lhs, const EntitySpawnTicket* rhs);
[[nodiscard]] static bool IsEqualTicket(const Ticket* lhs, const EntitySpawnTicket* rhs);
[[nodiscard]] static bool IsEqualTicket(const EntitySpawnTicket* lhs, const Ticket* rhs);
[[nodiscard]] static bool IsEqualTicket(const Ticket* lhs, const Ticket* rhs);
AZ::Entity* CloneSingleEntity(
const AZ::Entity& entityTemplate, EntityIdMap& templateToCloneMap, AZ::SerializeContext& serializeContext);
bool ProcessRequest(SpawnAllEntitiesCommand& request);
bool ProcessRequest(SpawnEntitiesCommand& request);
bool ProcessRequest(DespawnAllEntitiesCommand& request);
bool ProcessRequest(ReloadSpawnableCommand& request);
bool ProcessRequest(ListEntitiesCommand& request);
bool ProcessRequest(ListIndicesEntitiesCommand& request);
bool ProcessRequest(ClaimEntitiesCommand& request);
bool ProcessRequest(BarrierCommand& request);
bool ProcessRequest(DestroyTicketCommand& request);
AZStd::deque<Requests> m_delayedQueue; //!< Requests that were processed before, but couldn't be completed.
AZStd::queue<Requests> m_pendingRequestQueue;
AZStd::mutex m_pendingRequestQueueMutex;
Queue m_highPriorityQueue;
Queue m_regularPriorityQueue;
AZ::Event<AZ::Data::Asset<Spawnable>> m_onSpawnedEvent;
AZ::Event<AZ::Data::Asset<Spawnable>> m_onDespawnedEvent;
AZ::SerializeContext* m_defaultSerializeContext { nullptr };
//! The threshold used to determine if a request goes in the regular (if bigger than the value) or high priority queue (if smaller
//! or equal to this value). The starting value of 64 is chosen as it's between default values SpawnablePriority_High and
//! SpawnablePriority_Default which gives users a bit of room to fine tune the priorities as this value can be configured
//! through the Settings Registry under the key "/O3DE/AzFramework/Spawnables/HighPriorityThreshold".
SpawnablePriority m_highPriorityThreshold { 64 };
};
AZ_DEFINE_ENUM_BITWISE_OPERATORS(AzFramework::SpawnableEntitiesManager::CommandQueuePriority);
} // namespace AzFramework
@@ -48,10 +48,23 @@ namespace AzFramework
void SpawnableSystemComponent::OnTick(float /*deltaTime*/, AZ::ScriptTimePoint /*time*/)
{
m_entitiesManager.ProcessQueue();
m_entitiesManager.ProcessQueue(
SpawnableEntitiesManager::CommandQueuePriority::High | SpawnableEntitiesManager::CommandQueuePriority::Regular);
RootSpawnableNotificationBus::ExecuteQueuedEvents();
}
int SpawnableSystemComponent::GetTickOrder()
{
return AZ::ComponentTickBus::TICK_GAME;
}
void SpawnableSystemComponent::OnSystemTick()
{
// Handle only high priority spawning events such as those created from network. These need to happen even if the client
// doesn't have focus to avoid time-out issues for instance.
m_entitiesManager.ProcessQueue(SpawnableEntitiesManager::CommandQueuePriority::High);
}
void SpawnableSystemComponent::OnCatalogLoaded([[maybe_unused]] const char* catalogFile)
{
if (!m_catalogAvailable)
@@ -168,7 +181,8 @@ namespace AzFramework
SpawnableEntitiesManager::CommandQueueStatus queueStatus;
do
{
queueStatus = m_entitiesManager.ProcessQueue();
queueStatus = m_entitiesManager.ProcessQueue(
SpawnableEntitiesManager::CommandQueuePriority::High | SpawnableEntitiesManager::CommandQueuePriority::Regular);
} while (queueStatus == SpawnableEntitiesManager::CommandQueueStatus::HasCommandsLeft);
}
@@ -28,6 +28,7 @@ namespace AzFramework
class SpawnableSystemComponent
: public AZ::Component
, public AZ::TickBus::Handler
, public AZ::SystemTickBus::Handler
, public AssetCatalogEventBus::Handler
, public RootSpawnableInterface::Registrar
, public RootSpawnableNotificationBus::Handler
@@ -58,6 +59,13 @@ namespace AzFramework
//
void OnTick(float deltaTime, AZ::ScriptTimePoint time) override;
int GetTickOrder() override;
//
// SystemTickBus
//
void OnSystemTick() override;
//
// AssetCatalogEventBus
@@ -22,14 +22,18 @@
namespace AzFramework
{
AZ_CVAR(
float, ed_cameraSystemDefaultPlaneHeight, 34.0f, nullptr, AZ::ConsoleFunctorFlags::Null,
float,
ed_cameraSystemDefaultPlaneHeight,
34.0f,
nullptr,
AZ::ConsoleFunctorFlags::Null,
"The default height of the ground plane to do intersection tests against when orbiting");
AZ_CVAR(float, ed_cameraSystemBoostMultiplier, 3.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(float, ed_cameraSystemTranslateSpeed, 10.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(float, ed_cameraSystemOrbitDollyScrollSpeed, 0.02f, nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(float, ed_cameraSystemOrbitDollyCursorSpeed, 0.01f, nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(float, ed_cameraSystemScrollTranslateSpeed, 0.02f, nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(float, ed_cameraSystemMinOrbitDistance, 6.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(float, ed_cameraSystemMinOrbitDistance, 10.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(float, ed_cameraSystemMaxOrbitDistance, 50.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(float, ed_cameraSystemLookSmoothness, 5.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(float, ed_cameraSystemTranslateSmoothness, 5.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "");
@@ -37,12 +41,15 @@ namespace AzFramework
AZ_CVAR(float, ed_cameraSystemPanSpeed, 0.01f, nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(bool, ed_cameraSystemPanInvertX, true, nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(bool, ed_cameraSystemPanInvertY, true, nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(float, ed_cameraSystemLookDeadzone, 2.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(
AZ::CVarFixedString, ed_cameraSystemTranslateForwardKey, "keyboard_key_alphanumeric_W", nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(
AZ::CVarFixedString, ed_cameraSystemTranslateBackwardKey, "keyboard_key_alphanumeric_S", nullptr, AZ::ConsoleFunctorFlags::Null,
AZ::CVarFixedString,
ed_cameraSystemTranslateBackwardKey,
"keyboard_key_alphanumeric_S",
nullptr,
AZ::ConsoleFunctorFlags::Null,
"");
AZ_CVAR(
AZ::CVarFixedString, ed_cameraSystemTranslateLeftKey, "keyboard_key_alphanumeric_A", nullptr, AZ::ConsoleFunctorFlags::Null, "");
@@ -327,7 +334,9 @@ namespace AzFramework
}
Camera RotateCameraInput::StepCamera(
const Camera& targetCamera, const ScreenVector& cursorDelta, [[maybe_unused]] const float scrollDelta,
const Camera& targetCamera,
const ScreenVector& cursorDelta,
[[maybe_unused]] const float scrollDelta,
[[maybe_unused]] const float deltaTime)
{
Camera nextCamera = targetCamera;
@@ -375,7 +384,9 @@ namespace AzFramework
}
Camera PanCameraInput::StepCamera(
const Camera& targetCamera, const ScreenVector& cursorDelta, [[maybe_unused]] const float scrollDelta,
const Camera& targetCamera,
const ScreenVector& cursorDelta,
[[maybe_unused]] const float scrollDelta,
[[maybe_unused]] const float deltaTime)
{
Camera nextCamera = targetCamera;
@@ -474,7 +485,9 @@ namespace AzFramework
}
Camera TranslateCameraInput::StepCamera(
const Camera& targetCamera, [[maybe_unused]] const ScreenVector& cursorDelta, [[maybe_unused]] const float scrollDelta,
const Camera& targetCamera,
[[maybe_unused]] const ScreenVector& cursorDelta,
[[maybe_unused]] const float scrollDelta,
const float deltaTime)
{
Camera nextCamera = targetCamera;
@@ -631,7 +644,9 @@ namespace AzFramework
}
Camera OrbitDollyScrollCameraInput::StepCamera(
const Camera& targetCamera, [[maybe_unused]] const ScreenVector& cursorDelta, const float scrollDelta,
const Camera& targetCamera,
[[maybe_unused]] const ScreenVector& cursorDelta,
const float scrollDelta,
[[maybe_unused]] const float deltaTime)
{
Camera nextCamera = targetCamera;
@@ -667,7 +682,9 @@ namespace AzFramework
}
Camera OrbitDollyCursorMoveCameraInput::StepCamera(
const Camera& targetCamera, const ScreenVector& cursorDelta, [[maybe_unused]] const float scrollDelta,
const Camera& targetCamera,
const ScreenVector& cursorDelta,
[[maybe_unused]] const float scrollDelta,
[[maybe_unused]] const float deltaTime)
{
Camera nextCamera = targetCamera;
@@ -687,7 +704,9 @@ namespace AzFramework
}
Camera ScrollTranslationCameraInput::StepCamera(
const Camera& targetCamera, [[maybe_unused]] const ScreenVector& cursorDelta, const float scrollDelta,
const Camera& targetCamera,
[[maybe_unused]] const ScreenVector& cursorDelta,
const float scrollDelta,
[[maybe_unused]] const float deltaTime)
{
Camera nextCamera = targetCamera;
@@ -128,12 +128,12 @@ namespace AzFramework
worldPosition, CameraView(cameraState), CameraProjection(cameraState), cameraState.m_viewportSize);
}
AZ::Vector3 ScreenToWorld(
const ScreenPoint& screenPosition, const AZ::Matrix4x4& inverseCameraView,
const AZ::Matrix4x4& inverseCameraProjection, const AZ::Vector2& viewportSize)
AZ::Vector3 ScreenNDCToWorld(
const AZ::Vector2& normalizedScreenPosition, const AZ::Matrix4x4& inverseCameraView,
const AZ::Matrix4x4& inverseCameraProjection)
{
// convert screen space coordinates from <0, 1> to <-1,1> range
const auto ndcPosition = NDCFromScreenPoint(screenPosition, viewportSize) * 2.0f - AZ::Vector2::CreateOne();
const auto ndcPosition = normalizedScreenPosition * 2.0f - AZ::Vector2::CreateOne();
// transform ndc space position to clip space
const auto clipSpacePosition = inverseCameraProjection * Vector2ToVector4(ndcPosition, -1.0f, 1.0f);
@@ -145,6 +145,15 @@ namespace AzFramework
return worldPosition;
}
AZ::Vector3 ScreenToWorld(
const ScreenPoint& screenPosition, const AZ::Matrix4x4& inverseCameraView,
const AZ::Matrix4x4& inverseCameraProjection, const AZ::Vector2& viewportSize)
{
const auto normalizedScreenPosition = NDCFromScreenPoint(screenPosition, viewportSize);
return ScreenNDCToWorld(normalizedScreenPosition, inverseCameraView, inverseCameraProjection);
}
AZ::Vector3 ScreenToWorld(const ScreenPoint& screenPosition, const CameraState& cameraState)
{
return ScreenToWorld(
@@ -42,7 +42,7 @@ namespace AzFramework
const AZ::Vector3& worldPosition, const AZ::Matrix4x4& cameraView, const AZ::Matrix4x4& cameraProjection,
const AZ::Vector2& viewportSize);
//! Unprojects a position in screen space to world space.
//! Unprojects a position in screen space pixel coordinates to world space.
//! Note: The position returned will be on the near clip plane of the camera in world space.
AZ::Vector3 ScreenToWorld(const ScreenPoint& screenPosition, const CameraState& cameraState);
@@ -52,6 +52,12 @@ namespace AzFramework
const ScreenPoint& screenPosition, const AZ::Matrix4x4& inverseCameraView,
const AZ::Matrix4x4& inverseCameraProjection, const AZ::Vector2& viewportSize);
//! Unprojects a position in screen space normalized device coordinates to world space.
//! Note: The position returned will be on the near clip plane of the camera in world space.
AZ::Vector3 ScreenNDCToWorld(
const AZ::Vector2& ndcPosition, const AZ::Matrix4x4& inverseCameraView,
const AZ::Matrix4x4& inverseCameraProjection);
//! Returns the camera projection for the current camera state.
AZ::Matrix4x4 CameraProjection(const CameraState& cameraState);
@@ -353,6 +353,7 @@ namespace AzFramework
// Get the dimensions of the display device on which the window is currently displayed.
MONITORINFO monitorInfo;
memset(&monitorInfo, 0, sizeof(MONITORINFO)); // C4701 potentially uninitialized local variable 'monitorInfo' used
monitorInfo.cbSize = sizeof(MONITORINFO);
const BOOL success = monitor ? GetMonitorInfo(monitor, &monitorInfo) : FALSE;
if (!success)
@@ -97,7 +97,7 @@ namespace AzManipulatorTestFramework
if (m_logging)
{
AZStd::string message = AZStd::string::format(format, args...);
std::cout << "[ActionDispatcher] " << message.c_str() << "\n";
AZ_Printf("[ActionDispatcher] %s", message.c_str());
}
}
@@ -12,17 +12,24 @@
#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);
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(
@@ -40,8 +47,7 @@ namespace AzManipulatorTestFramework
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 +67,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
@@ -12,14 +12,13 @@
#pragma once
#include <AzManipulatorTestFramework/AzManipulatorTestFramework.h>
#include <AzManipulatorTestFramework/ActionDispatcher.h>
#include <AzManipulatorTestFramework/AzManipulatorTestFramework.h>
namespace AzManipulatorTestFramework
{
//! Dispatches actions immediately to the manipulators.
class ImmediateModeActionDispatcher
: public ActionDispatcher<ImmediateModeActionDispatcher>
class ImmediateModeActionDispatcher : public ActionDispatcher<ImmediateModeActionDispatcher>
{
using KeyboardModifier = AzToolsFramework::ViewportInteraction::KeyboardModifier;
using KeyboardModifiers = AzToolsFramework::ViewportInteraction::KeyboardModifiers;
@@ -62,7 +61,7 @@ namespace AzManipulatorTestFramework
void MouseLButtonUpImpl() override;
void MousePositionImpl(const AzFramework::ScreenPoint& position) override;
void KeyboardModifierDownImpl(const KeyboardModifier& keyModifier) override;
void KeyboardModifierUpImpl(const KeyboardModifier& keyModifier) override;
void KeyboardModifierUpImpl(const KeyboardModifier& keyModifier) override;
void ExpectManipulatorBeingInteractedImpl() override;
void ExpectManipulatorNotBeingInteractedImpl() override;
void SetEntityWorldTransformImpl(AZ::EntityId entityId, const AZ::Transform& transform) override;
@@ -97,8 +96,7 @@ namespace AzManipulatorTestFramework
return this;
}
inline ImmediateModeActionDispatcher* ImmediateModeActionDispatcher::GetKeyboardModifiers(
KeyboardModifiers& keyboardModifiers)
inline ImmediateModeActionDispatcher* ImmediateModeActionDispatcher::GetKeyboardModifiers(KeyboardModifiers& keyboardModifiers)
{
keyboardModifiers = GetKeyboardModifiers();
return this;
@@ -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,23 @@ namespace AzManipulatorTestFramework
using MouseEvent = AzToolsFramework::ViewportInteraction::MouseEvent;
using MousePick = AzToolsFramework::ViewportInteraction::MousePick;
AZStd::shared_ptr<AzToolsFramework::LinearManipulator> CreateLinearManipulator(
// 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 +62,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 +124,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 +133,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 +151,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
@@ -1,14 +1,14 @@
/*
* 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.
*
*/
* 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.
*
*/
#include <AzManipulatorTestFramework/DirectManipulatorViewportInteraction.h>
#include <AzManipulatorTestFramework/ViewportInteraction.h>
@@ -19,10 +19,10 @@ namespace AzManipulatorTestFramework
using MouseInteraction = AzToolsFramework::ViewportInteraction::MouseInteraction;
using MouseInteractionEvent = AzToolsFramework::ViewportInteraction::MouseInteractionEvent;
class CustomManipulatorManager
: public AzToolsFramework::ManipulatorManager
class CustomManipulatorManager : public AzToolsFramework::ManipulatorManager
{
using ManagerBase = AzToolsFramework::ManipulatorManager;
public:
using ManagerBase::ManagerBase;
@@ -31,18 +31,17 @@ namespace AzManipulatorTestFramework
};
//! Implementation of the manipulator interface using direct access to the manipulator manager.
class DirectCallManipulatorManager
: public ManipulatorManagerInterface
class DirectCallManipulatorManager : public ManipulatorManagerInterface
{
public:
DirectCallManipulatorManager(
ViewportInteractionInterface* viewportInteraction,
AZStd::shared_ptr<CustomManipulatorManager> manipulatorManager);
ViewportInteractionInterface* viewportInteraction, AZStd::shared_ptr<CustomManipulatorManager> manipulatorManager);
// ManipulatorManagerInterface ...
void ConsumeMouseInteractionEvent(const MouseInteractionEvent& event);
AzToolsFramework::ManipulatorManagerId GetId() const override;
bool ManipulatorBeingInteracted() const override;
private:
// Trigger the updating of manipulator bounds.
void DrawManipulators(const MouseInteraction& mouseInteraction);
@@ -61,8 +60,7 @@ namespace AzManipulatorTestFramework
}
DirectCallManipulatorManager::DirectCallManipulatorManager(
ViewportInteractionInterface* viewportInteraction,
AZStd::shared_ptr<CustomManipulatorManager> manipulatorManager)
ViewportInteractionInterface* viewportInteraction, AZStd::shared_ptr<CustomManipulatorManager> manipulatorManager)
: m_viewportInteraction(viewportInteraction)
, m_manipulatorManager(AZStd::move(manipulatorManager))
{
@@ -126,11 +124,9 @@ namespace AzManipulatorTestFramework
DirectCallManipulatorViewportInteraction::DirectCallManipulatorViewportInteraction()
: m_customManager(
AZStd::make_unique<CustomManipulatorManager>(
AzToolsFramework::ManipulatorManagerId(AZ::Crc32("TestManipulatorManagerId"))))
AZStd::make_unique<CustomManipulatorManager>(AzToolsFramework::ManipulatorManagerId(AZ::Crc32("TestManipulatorManagerId"))))
, m_viewportInteraction(AZStd::make_unique<ViewportInteraction>())
, m_manipulatorManager(
AZStd::make_unique<DirectCallManipulatorManager>(m_viewportInteraction.get(), m_customManager))
, m_manipulatorManager(AZStd::make_unique<DirectCallManipulatorManager>(m_viewportInteraction.get(), m_customManager))
{
}
@@ -10,8 +10,8 @@
*
*/
#include <AzManipulatorTestFramework/ImmediateModeActionDispatcher.h>
#include <AzManipulatorTestFramework/AzManipulatorTestFrameworkUtils.h>
#include <AzManipulatorTestFramework/ImmediateModeActionDispatcher.h>
#include <AzToolsFramework/ComponentMode/EditorComponentModeBus.h>
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
@@ -33,8 +33,7 @@ namespace AzManipulatorTestFramework
using KeyboardModifier = AzToolsFramework::ViewportInteraction::KeyboardModifier;
using MouseInteractionEvent = AzToolsFramework::ViewportInteraction::MouseInteractionEvent;
ImmediateModeActionDispatcher::ImmediateModeActionDispatcher(
ManipulatorViewportInteraction& viewportManipulatorInteraction)
ImmediateModeActionDispatcher::ImmediateModeActionDispatcher(ManipulatorViewportInteraction& viewportManipulatorInteraction)
: m_viewportManipulatorInteraction(viewportManipulatorInteraction)
{
}
@@ -126,8 +125,7 @@ namespace AzManipulatorTestFramework
void ImmediateModeActionDispatcher::EnterComponentModeImpl(const AZ::Uuid& uuid)
{
using AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequestBus;
ComponentModeSystemRequestBus::Broadcast(
&ComponentModeSystemRequestBus::Events::AddSelectedComponentModesOfType, uuid);
ComponentModeSystemRequestBus::Broadcast(&ComponentModeSystemRequestBus::Events::AddSelectedComponentModesOfType, uuid);
}
const AzToolsFramework::ViewportInteraction::MouseInteractionEvent* ImmediateModeActionDispatcher::GetMouseInteractionEvent() const
@@ -144,8 +142,7 @@ namespace AzManipulatorTestFramework
AzToolsFramework::ViewportInteraction::MouseInteractionEvent* ImmediateModeActionDispatcher::GetMouseInteractionEvent()
{
return const_cast<MouseInteractionEvent*>(
static_cast<const ImmediateModeActionDispatcher*>(this)->GetMouseInteractionEvent());
return const_cast<MouseInteractionEvent*>(static_cast<const ImmediateModeActionDispatcher*>(this)->GetMouseInteractionEvent());
}
ImmediateModeActionDispatcher* ImmediateModeActionDispatcher::ExpectTrue(bool result)
@@ -162,8 +159,7 @@ namespace AzManipulatorTestFramework
return this;
}
ImmediateModeActionDispatcher* ImmediateModeActionDispatcher::GetEntityWorldTransform(
AZ::EntityId entityId, AZ::Transform& transform)
ImmediateModeActionDispatcher* ImmediateModeActionDispatcher::GetEntityWorldTransform(AZ::EntityId entityId, AZ::Transform& transform)
{
Log("Getting entity world transform");
transform = AzToolsFramework::GetWorldTransform(entityId);
@@ -11,17 +11,18 @@
*/
#include "AzManipulatorTestFrameworkTestFixtures.h"
#include <AzToolsFramework/ViewportSelection/EditorInteractionSystemViewportSelectionRequestBus.h>
#include <AzToolsFramework/ViewportSelection/EditorDefaultSelection.h>
#include <AzToolsFramework/ViewportSelection/EditorInteractionSystemViewportSelectionRequestBus.h>
namespace UnitTest
{
class AzManipulatorTestFrameworkBusCallTestFixture
: public LinearManipulatorTestFixture
class AzManipulatorTestFrameworkBusCallTestFixture : public LinearManipulatorTestFixture
{
protected:
AzManipulatorTestFrameworkBusCallTestFixture()
: LinearManipulatorTestFixture(AzToolsFramework::g_mainManipulatorManagerId) {}
: LinearManipulatorTestFixture(AzToolsFramework::g_mainManipulatorManagerId)
{
}
bool IsManipulatorInteractingBusCall() const
{
@@ -37,8 +38,8 @@ namespace UnitTest
TEST_F(AzManipulatorTestFrameworkBusCallTestFixture, ConsumeViewportLeftMouseClick)
{
// given a left mouse down ray in world space
auto event = AzManipulatorTestFramework::CreateMouseInteractionEvent(
m_interaction, AzToolsFramework::ViewportInteraction::MouseEvent::Down);
auto event =
AzManipulatorTestFramework::CreateMouseInteractionEvent(m_interaction, AzToolsFramework::ViewportInteraction::MouseEvent::Down);
// consume the mouse down and up events
AzManipulatorTestFramework::DispatchMouseInteractionEvent(event);
@@ -56,8 +57,8 @@ namespace UnitTest
TEST_F(AzManipulatorTestFrameworkBusCallTestFixture, ConsumeViewportMouseMoveHover)
{
// given a left mouse down ray in world space
const auto event = AzManipulatorTestFramework::CreateMouseInteractionEvent(
m_interaction, AzToolsFramework::ViewportInteraction::MouseEvent::Move);
const auto event =
AzManipulatorTestFramework::CreateMouseInteractionEvent(m_interaction, AzToolsFramework::ViewportInteraction::MouseEvent::Move);
// consume the mouse move event
AzManipulatorTestFramework::DispatchMouseInteractionEvent(event);
@@ -75,8 +76,8 @@ namespace UnitTest
TEST_F(AzManipulatorTestFrameworkBusCallTestFixture, ConsumeViewportMouseMoveActive)
{
// given a left mouse down ray in world space
auto event = AzManipulatorTestFramework::CreateMouseInteractionEvent(
m_interaction, AzToolsFramework::ViewportInteraction::MouseEvent::Down);
auto event =
AzManipulatorTestFramework::CreateMouseInteractionEvent(m_interaction, AzToolsFramework::ViewportInteraction::MouseEvent::Down);
// consume the mouse down event
AzManipulatorTestFramework::DispatchMouseInteractionEvent(event);
@@ -110,14 +111,14 @@ namespace UnitTest
const AZ::Vector3 initialManipulatorPosition = m_linearManipulator->GetLocalPosition();
m_linearManipulator->InstallMouseMoveCallback(
[&movementAlongAxis, this](const AzToolsFramework::LinearManipulator::Action& action)
{
movementAlongAxis = action.LocalPositionOffset();
m_linearManipulator->SetLocalPosition(action.LocalPosition());
});
{
movementAlongAxis = action.LocalPositionOffset();
m_linearManipulator->SetLocalPosition(action.LocalPosition());
});
// given a left mouse down ray in world space
auto event = AzManipulatorTestFramework::CreateMouseInteractionEvent(
m_interaction, AzToolsFramework::ViewportInteraction::MouseEvent::Down);
auto event =
AzManipulatorTestFramework::CreateMouseInteractionEvent(m_interaction, AzToolsFramework::ViewportInteraction::MouseEvent::Down);
// consume the mouse down event
AzManipulatorTestFramework::DispatchMouseInteractionEvent(event);
@@ -134,7 +135,7 @@ namespace UnitTest
// consume the mouse up event
event.m_mouseEvent = AzToolsFramework::ViewportInteraction::MouseEvent::Up;
AzManipulatorTestFramework::DispatchMouseInteractionEvent(event);
// expect the left mouse down/up sanity flags to be set
EXPECT_TRUE(m_receivedLeftMouseDown);
EXPECT_TRUE(m_receivedLeftMouseUp);
@@ -14,10 +14,10 @@
namespace UnitTest
{
class CustomManipulatorManager
: public AzToolsFramework::ManipulatorManager
class CustomManipulatorManager : public AzToolsFramework::ManipulatorManager
{
using ManagerBase = AzToolsFramework::ManipulatorManager;
public:
using ManagerBase::ManagerBase;
@@ -27,17 +27,17 @@ namespace UnitTest
}
};
class AzManipulatorTestFrameworkCustomManagerTestFixture
: public LinearManipulatorTestFixture
class AzManipulatorTestFrameworkCustomManagerTestFixture : public LinearManipulatorTestFixture
{
protected:
AzManipulatorTestFrameworkCustomManagerTestFixture()
: LinearManipulatorTestFixture(AzToolsFramework::ManipulatorManagerId(AZ::Crc32("TestManipulatorManagerId"))) {}
: LinearManipulatorTestFixture(AzToolsFramework::ManipulatorManagerId(AZ::Crc32("TestManipulatorManagerId")))
{
}
void SetUpEditorFixtureImpl() override
{
m_manipulatorManager =
AZStd::make_shared<CustomManipulatorManager>(m_manipulatorManagerId);
m_manipulatorManager = AZStd::make_shared<CustomManipulatorManager>(m_manipulatorManagerId);
LinearManipulatorTestFixture::SetUpEditorFixtureImpl();
}
@@ -115,9 +115,9 @@ namespace UnitTest
m_linearManipulator->InstallMouseMoveCallback(
[&movementAlongAxis](const AzToolsFramework::LinearManipulator::Action& action)
{
movementAlongAxis = action.m_current.m_localPositionOffset;
});
{
movementAlongAxis = action.m_current.m_localPositionOffset;
});
// consume the mouse down event
m_manipulatorManager->ConsumeViewportMousePress(m_interaction);
@@ -141,4 +141,3 @@ namespace UnitTest
EXPECT_EQ(movementAlongAxis, expectedPositionAfterMovementAlongAxis);
}
} // namespace UnitTest
@@ -10,52 +10,55 @@
*
*/
#include "AzManipulatorTestFrameworkTestFixtures.h"
#include <AZTestShared/Math/MathTestHelpers.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>
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,68 @@ 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
@@ -15,8 +15,7 @@
namespace UnitTest
{
class AValidViewportInteraction
: public ToolsApplicationFixture
class AValidViewportInteraction : public ToolsApplicationFixture
{
public:
AValidViewportInteraction()
@@ -27,8 +26,7 @@ namespace UnitTest
protected:
void SetUpEditorFixtureImpl() override
{
m_cameraState =
AzFramework::CreateIdentityDefaultCamera(AZ::Vector3::CreateZero(), AZ::Vector2(800.0f, 600.0f));
m_cameraState = AzFramework::CreateIdentityDefaultCamera(AZ::Vector3::CreateZero(), AZ::Vector2(800.0f, 600.0f));
}
public:
@@ -11,49 +11,48 @@
*/
#include <AzFramework/Viewport/ViewportScreen.h>
#include <AzManipulatorTestFramework/AzManipulatorTestFrameworkUtils.h>
#include <AzManipulatorTestFramework/AzManipulatorTestFramework.h>
#include <AzManipulatorTestFramework/AzManipulatorTestFrameworkUtils.h>
#include <AzManipulatorTestFramework/DirectManipulatorViewportInteraction.h>
#include <AzManipulatorTestFramework/IndirectManipulatorViewportInteraction.h>
#include <AzManipulatorTestFramework/ImmediateModeActionDispatcher.h>
#include <AzToolsFramework/ViewportSelection/EditorSelectionUtil.h>
#include <AzManipulatorTestFramework/IndirectManipulatorViewportInteraction.h>
#include <AzToolsFramework/UnitTest/AzToolsFrameworkTestHelpers.h>
#include <AzToolsFramework/ViewportSelection/EditorSelectionUtil.h>
namespace UnitTest
{
class AzManipulatorTestFrameworkWorldSpaceBuilderTestFixture
: public ToolsApplicationFixture
class AzManipulatorTestFrameworkWorldSpaceBuilderTestFixture : public ToolsApplicationFixture
{
protected:
struct State
{
State(AZStd::unique_ptr<AzManipulatorTestFramework::ManipulatorViewportInteraction> viewportManipulatorInteraction)
: m_viewportManipulatorInteraction(viewportManipulatorInteraction.release())
, 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))
, m_linearManipulator(AzManipulatorTestFramework::CreateLinearManipulator(
m_viewportManipulatorInteraction->GetManipulatorManager().GetId(),
/*position=*/AZ::Vector3(0.0f, 50.0f, 0.0f),
/*radius=*/m_boundsRadius))
{
// default sanity check call backs
m_linearManipulator->InstallLeftMouseDownCallback(
[this]([[maybe_unused]] const AzToolsFramework::LinearManipulator::Action& action)
{
m_receivedLeftMouseDown = true;
});
{
m_receivedLeftMouseDown = true;
});
m_linearManipulator->InstallMouseMoveCallback(
[this]([[maybe_unused]] const AzToolsFramework::LinearManipulator::Action& action)
{
m_receivedMouseMove = true;
});
{
m_receivedMouseMove = true;
});
m_linearManipulator->InstallLeftMouseUpCallback(
[this]([[maybe_unused]] const AzToolsFramework::LinearManipulator::Action& action)
{
m_receivedLeftMouseUp = true;
});
{
m_receivedLeftMouseUp = true;
});
}
~State() = default;
@@ -79,13 +78,12 @@ namespace UnitTest
protected:
void SetUpEditorFixtureImpl() override
{
m_directState = AZStd::make_unique<State>(
AZStd::make_unique<AzManipulatorTestFramework::DirectCallManipulatorViewportInteraction>());
m_busState = AZStd::make_unique<State>(
AZStd::make_unique<AzManipulatorTestFramework::IndirectCallManipulatorViewportInteraction>());
m_directState =
AZStd::make_unique<State>(AZStd::make_unique<AzManipulatorTestFramework::DirectCallManipulatorViewportInteraction>());
m_busState =
AZStd::make_unique<State>(AZStd::make_unique<AzManipulatorTestFramework::IndirectCallManipulatorViewportInteraction>());
m_cameraState =
AzFramework::CreateIdentityDefaultCamera(
AZ::Vector3::CreateZero(), AzManipulatorTestFramework::DefaultViewportSize);
AzFramework::CreateIdentityDefaultCamera(AZ::Vector3::CreateZero(), AzManipulatorTestFramework::DefaultViewportSize);
}
void TearDownEditorFixtureImpl() override
@@ -105,8 +103,7 @@ namespace UnitTest
{
// given a left mouse down ray in world space
// consume the mouse down and up events
state.m_actionDispatcher
->CameraState(m_cameraState)
state.m_actionDispatcher->CameraState(m_cameraState)
->MousePosition(AzManipulatorTestFramework::GetCameraStateViewportCenter(m_cameraState))
->MouseLButtonDown()
->Trace("Expecting left mouse button down")
@@ -126,31 +123,27 @@ namespace UnitTest
->ExpectTrue(state.m_receivedLeftMouseUp)
->ExpectTrue(state.m_receivedMouseMove)
->ExpectFalse(state.m_linearManipulator->PerformingAction())
->ExpectManipulatorNotBeingInteracted()
;
->ExpectManipulatorNotBeingInteracted();
}
void AzManipulatorTestFrameworkWorldSpaceBuilderTestFixture::ConsumeViewportMouseMoveHover(State& state)
{
// given a left mouse down ray in world space
// consume the mouse move event
state.m_actionDispatcher
->CameraState(m_cameraState)
state.m_actionDispatcher->CameraState(m_cameraState)
->MousePosition(AzManipulatorTestFramework::GetCameraStateViewportCenter(m_cameraState))
->ExpectFalse(state.m_linearManipulator->PerformingAction())
->ExpectManipulatorNotBeingInteracted()
->ExpectFalse(state.m_receivedLeftMouseDown)
->ExpectFalse(state.m_receivedMouseMove)
->ExpectFalse(state.m_receivedLeftMouseUp)
;
->ExpectFalse(state.m_receivedLeftMouseUp);
}
void AzManipulatorTestFrameworkWorldSpaceBuilderTestFixture::ConsumeViewportMouseMoveActive(State& state)
{
// given a left mouse down ray in world space
// consume the mouse move event
state.m_actionDispatcher
->CameraState(m_cameraState)
state.m_actionDispatcher->CameraState(m_cameraState)
->MouseLButtonDown()
->MousePosition(AzManipulatorTestFramework::GetCameraStateViewportCenter(m_cameraState))
->ExpectTrue(state.m_linearManipulator->PerformingAction())
@@ -158,8 +151,7 @@ namespace UnitTest
->MouseLButtonUp()
->ExpectTrue(state.m_receivedLeftMouseDown)
->ExpectTrue(state.m_receivedMouseMove)
->ExpectTrue(state.m_receivedLeftMouseUp)
;
->ExpectTrue(state.m_receivedLeftMouseUp);
}
void AzManipulatorTestFrameworkWorldSpaceBuilderTestFixture::MoveManipulatorAlongAxis(State& state)
@@ -176,8 +168,7 @@ 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);
@@ -185,12 +176,11 @@ namespace UnitTest
state.m_linearManipulator->InstallMouseMoveCallback(
[&movementAlongAxis](const AzToolsFramework::LinearManipulator::Action& action)
{
movementAlongAxis = action.LocalPosition();
});
{
movementAlongAxis = action.LocalPosition();
});
state.m_actionDispatcher
->CameraState(m_cameraState)
state.m_actionDispatcher->CameraState(m_cameraState)
->MousePosition(initialPositionScreen)
->MouseLButtonDown()
->ExpectTrue(state.m_linearManipulator->PerformingAction())
@@ -199,8 +189,7 @@ namespace UnitTest
->MouseLButtonUp()
->ExpectTrue(state.m_receivedLeftMouseDown)
->ExpectTrue(state.m_receivedLeftMouseUp)
->ExpectTrue(movementAlongAxis.IsClose(finalPositionWorld, 0.01f))
;
->ExpectTrue(movementAlongAxis.IsClose(finalPositionWorld, 0.01f));
}
TEST_F(AzManipulatorTestFrameworkWorldSpaceBuilderTestFixture, ConsumeViewportLeftMouseClick)
@@ -139,7 +139,7 @@ namespace AzNetworking
NetworkOutputSerializer networkSerializer(buffer.GetBuffer(), buffer.GetSize());
{
ISerializer& serializer = networkSerializer; // To get the default typeinfo parameters in ISerializer
ISerializer& networkISerializer = networkSerializer; // To get the default typeinfo parameters in ISerializer
// First, serialize out the header
if (!header.SerializePacketFlags(networkSerializer))
@@ -148,7 +148,7 @@ namespace AzNetworking
return false;
}
if (!serializer.Serialize(header, "Header"))
if (!networkISerializer.Serialize(header, "Header"))
{
AZLOG(NET_FragmentQueue, "Reconstructed fragmented packet failed header serialization");
return false;
@@ -46,7 +46,7 @@ namespace AzNetworking
}
else if (m_updateRate < updateTimeMs)
{
AZLOG_INFO("TimedThread bled %d ms", aznumeric_cast<int32_t>(updateTimeMs - m_updateRate));
AZLOG(NET_TimedThread, "TimedThread bled %d ms", aznumeric_cast<int32_t>(updateTimeMs - m_updateRate));
}
}
OnStop();
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Icon / Locked Status</title>
<g id="Icon-/-Locked-Status" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Group-9">
<g id="Icons-/-Icon-Grid" transform="translate(17.000000, 15.000000)" fill="#FFFFFF">
<path d="M3,0 C4.03553391,-1.90224492e-16 4.875,0.839466094 4.875,1.875 L4.875,3.6 L6,3.6 L6,8 L0,8 L0,3.6 L1.125,3.6 L1.125,1.875 C1.125,0.839466094 1.96446609,1.90224492e-16 3,0 Z M3.375,5.2 L2.625,5.2 L2.625,6.4 L3.375,6.4 L3.375,5.2 Z M3,0.8 C2.37867966,0.8 1.875,1.30367966 1.875,1.925 L1.875,1.925 L1.875,3.6 L4.125,3.6 L4.125,1.925 C4.125,1.30367966 3.62132034,0.8 3,0.8 Z" id="Combined-Shape"></path>
</g>
<rect id="Rectangle" fill="#F9F9F9" opacity="0" x="0" y="0" width="24" height="24"></rect>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1017 B

@@ -356,7 +356,8 @@
<file>img/UI20/toolbar/Load.svg</file>
<file>img/UI20/toolbar/Local.svg</file>
<file>img/UI20/toolbar/Locked.svg</file>
<file>img/UI20/toolbar/LUA.svg</file>
<file>img/UI20/toolbar/Locked_Status.svg</file>
<file>img/UI20/toolbar/LUA.svg</file>
<file>img/UI20/toolbar/Material.svg</file>
<file>img/UI20/toolbar/Measure.svg</file>
<file>img/UI20/toolbar/Move.svg</file>
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="20px" height="20px" viewBox="0 0 20 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Camera </title>
<g id="Camera-" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Icon-2" fill="#FFFFFF" transform="translate(1.666667, 4.166667)">
<path d="M11.5739175,9.2927927 C11.5739175,9.54639717 11.3384276,9.99926232 10.8312187,9.99926232 C10.3240097,9.99926232 2.37169783,10.0264342 2.13620796,10.0264342 C1.74674394,10.0264342 1.33916531,9.69131402 1.33916531,9.09353203 C1.33916531,8.76746913 1.35126587,5.13853395 1.35126587,4.84870026 C1.35126587,4.37772052 1.35727992,4.13013009 1.81014506,4.13013009 C2.2630102,4.13013009 10.840276,4.11201548 10.840276,4.11201548 C11.4199434,4.11201548 11.5829748,4.51959411 11.5829748,4.86377162 C11.5829748,5.20794912 11.5739175,9.03918822 11.5739175,9.2927927 L11.5739175,9.2927927 Z M2.65247422,1.24990779 C2.65247422,1.09593364 2.65247422,0.941959493 2.77927646,0.941959493 L9.83491536,0.941959493 C10.0070041,0.941959493 10.2062648,0.978188705 10.2062648,1.16839206 C10.2062648,1.35859542 10.1972075,2.73530545 10.1972075,2.73530545 L2.64943097,2.7111768 L2.65247422,1.24990779 Z M16.9539554,4.02144245 L16.374288,4.03049976 L14.6896297,5.50684012 L14.2548791,5.50684012 C14.2548791,5.50684012 13.1317736,3.56857731 13.0049713,3.31497283 C12.8781691,3.06136835 12.4615332,2.77153466 12.2622725,2.77153466 L11.139167,2.77153466 C11.139167,2.77153466 11.1210524,0.905730282 11.1210524,0.579667381 C11.1210524,0.253604479 10.8131041,2.81996648e-13 10.4145827,2.81996648e-13 L2.22678099,2.81996648e-13 C1.86144562,2.81996648e-13 1.66522821,0.208317965 1.66522821,0.45887919 L1.66522821,2.69907624 C1.3325716,2.7055975 1.26148988,2.70509029 1.06744623,2.70509029 C0.626681643,2.70509029 0.43343503,2.86812174 0.43343503,3.51423349 L0.43343503,9.83623086 C0.43343503,10.5698724 0.804784446,10.8778207 1.36633722,10.8778207 C1.72862933,10.8778207 11.5920321,10.8687634 12.0992411,10.8687634 C12.60645,10.8687634 12.8238253,10.4340129 13.0049713,10.198523 C13.1861174,9.9630331 14.309223,8.11534333 14.309223,8.11534333 L14.8164319,8.09722872 L16.4558037,9.60074099 L16.97207,9.60074099 L16.9539554,4.02144245 Z" id="Icon"></path>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="20px" height="20px" viewBox="0 0 20 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>debug</title>
<g id="debug" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<path d="M11.3615865,7.917238 L15.2274672,9.63843901 L16.4675037,9.16172668 C16.6979214,9.07292417 16.9553551,9.18924382 17.0442998,9.41972476 C17.1330234,9.64993705 17.0253844,9.89284574 16.7952509,9.98177479 L16.7952509,9.98177479 L15.3951151,10.4956726 C15.2842902,10.5384499 15.1889358,10.4726675 15.0806503,10.4244557 L15.0806503,10.4244557 L14.1703124,10.0191471 C14.3857263,10.9664393 14.4599954,12.0211666 14.2565873,12.952264 L14.2565873,12.952264 L15.6721159,13.5824979 C15.8976398,13.6829076 15.9908466,13.9650071 15.8904369,14.190531 C15.7900905,14.4159128 15.5180189,14.5355501 15.292495,14.4351404 L15.292495,14.4351404 L13.9556938,13.8399581 C13.6322377,14.54733 13.1335636,15.1766897 12.5630158,15.6037725 L12.5630158,15.6037725 L12.8756514,15.7429668 C12.983937,15.7911787 13.0605157,15.8995144 13.1027401,16.0104336 L13.1027401,16.0104336 L13.6353002,17.4447605 C13.7234077,17.6752093 13.6080984,17.9334334 13.3776496,18.021541 C13.2626228,18.0653422 13.1408059,18.058613 13.0364993,18.0121727 C12.9319085,17.9656059 12.8452864,17.8786807 12.8011855,17.7631798 L12.8011855,17.7631798 L12.3264968,16.520128 L11.644667,16.2165578 C10.4621412,16.7117239 8.87932984,16.9345084 7.56328989,16.4483518 L7.56328989,16.4483518 L11.3615865,7.917238 Z M5.52878415,4.3232224 C5.75937506,4.23517812 6.01946614,4.34552927 6.10779463,4.57624672 L6.10779463,4.57624672 L6.58675456,5.80894035 L10.4526353,7.53014136 L6.6529468,16.0643815 C5.4063142,15.4223335 4.43289769,14.215203 4.07288375,12.8629772 L4.07288375,12.8629772 L3.41379116,12.5695302 L2.17275891,13.0473317 C2.05755786,13.0919067 1.93523585,13.0847824 1.83064504,13.0382156 C1.72633845,12.9917753 1.64104404,12.9033978 1.59654788,12.7884022 C1.5078242,12.5581899 1.62471629,12.294881 1.85492858,12.2061574 L1.85492858,12.2061574 L3.27301757,11.6515537 C3.38384243,11.6087763 3.51545059,11.5931314 3.62387829,11.6414065 L3.62387829,11.6414065 L3.91988732,11.7731982 C3.85933759,11.0651327 3.99592287,10.2745607 4.30516824,9.5608618 L4.30516824,9.5608618 L2.97859873,8.970235 C2.7530748,8.86982528 2.6599312,8.58758374 2.76027765,8.36220193 C2.86068737,8.13667801 3.13269567,8.01718285 3.35821959,8.11759257 L3.35821959,8.11759257 L4.7450425,8.73504591 C5.26222301,7.94364898 6.01359598,7.25652125 6.89624338,6.79811742 L6.89624338,6.79811742 L6.10101221,6.4440577 C5.99258451,6.39578258 5.88032145,6.36759526 5.83803372,6.25681811 L5.83803372,6.25681811 L5.26971048,4.90243425 C5.18174504,4.67204872 5.29833534,4.41132994 5.52878415,4.3232224 Z M10.8942352,2.34455584 C11.1427316,2.24942103 11.4210854,2.3733521 11.5165098,2.62161032 L11.5165098,2.62161032 L11.9777045,3.8224019 C12.2035494,3.86678469 12.4283918,3.93403345 12.6476126,4.03163684 C12.8668334,4.12924023 13.0677354,4.25026013 13.2518379,4.38839772 L13.2518379,4.38839772 L14.4533966,3.92589245 C14.7019612,3.83060445 14.9816108,3.95162483 15.0770352,4.19988305 C15.1723915,4.44829447 15.0509158,4.72113321 14.8025726,4.81633622 L14.8025726,4.81633622 L13.9775725,5.12324418 C14.5520125,5.98310253 14.7248639,7.02632912 14.247419,8.09868801 L14.247419,8.09868801 L8.55503156,5.56427385 C9.03247651,4.49191495 9.9234901,3.92253515 10.946252,3.77379694 L10.946252,3.77379694 L10.6171125,2.96698358 C10.5217563,2.71857217 10.6457388,2.43969065 10.8942352,2.34455584 Z" id="Combined-Shape" fill="#FFFFFF"></path>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.6 KiB

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="20px" height="20px" viewBox="0 0 20 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>resolution</title>
<g id="resolution" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<path d="M17.7222222,2.5 L17.7222222,17.7222222 L2.5,17.7222222 L2.5,2.5 L17.7222222,2.5 Z M16.721,4.45 L3.499,4.45 L3.499,10.111 L10.1293705,10.1111111 L10.129,16.722 L16.722,16.722 L16.721,4.45 Z M9.128,11.722 L3.999,11.722 L4,16.222 L9.129,16.222 L9.128,11.722 Z" id="Combined-Shape" fill="#FFFFFF" fill-rule="nonzero"></path>
</g>
</svg>

After

Width:  |  Height:  |  Size: 654 B

@@ -15,4 +15,9 @@
<file alias="download.svg">Notifications/download.svg</file>
<file alias="link.svg">Notifications/link.svg</file>
</qresource>
<qresource prefix="/Menu">
<file alias="resolution.svg">Menu/resolution.svg</file>
<file alias="debug.svg">Menu/debug.svg</file>
<file alias="camera.svg">Menu/camera.svg</file>
</qresource>
</RCC>
@@ -42,6 +42,9 @@ namespace AzToolsFramework
bool detachedWindow = false; ///< set to true if the view pane should use a detached, non-dockable widget. This is to workaround a problem with QOpenGLWidget on macOS. Currently this has no effect on other platforms.
bool isDisabledInSimMode = false; ///< set to true if the view pane should not be openable from level editor menu when editor is in simulation mode.
bool showOnToolsToolbar = false; ///< set to true if the view pane should create a button on the tools toolbar to open/close the pane
QString toolbarIcon; ///< path to the icon to use for the toolbar button - only used if showOnToolsToolbar is set to true
};
} // namespace AzToolsFramework
@@ -1,13 +0,0 @@
/*
* 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.
*
*/
#include "AzToolsFramework_precompiled.h"
@@ -197,7 +197,7 @@ namespace AzToolsFramework
AZ::IO::PathView filePath, Prefab::InstanceOptionalReference instanceToParentUnder) override;
Prefab::InstanceOptionalReference GetRootPrefabInstance() override;
const AZStd::vector<AZ::Data::Asset<AZ::Data::AssetData>>& GetPlayInEditorAssetData() override;
//////////////////////////////////////////////////////////////////////////
@@ -1,28 +1,32 @@
/*
* 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.
*
*/
* 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.
*
*/
#include "AngularManipulator.h"
#include <AzFramework/Entity/EntityDebugDisplayBus.h>
#include <AzToolsFramework/Manipulators/ManipulatorView.h>
#include <AzToolsFramework/Manipulators/ManipulatorSnapping.h>
#include <AzToolsFramework/Manipulators/ManipulatorView.h>
namespace AzToolsFramework
{
static const float s_circularRotateThresholdDegrees = 80.0f;
static const float CircularRotateThresholdDegrees = 80.0f;
AngularManipulator::ActionInternal AngularManipulator::CalculateManipulationDataStart(
const Fixed& fixed, const AZ::Transform& worldFromLocal, const AZ::Transform& localTransform,
const AZ::Vector3& rayOrigin, const AZ::Vector3& rayDirection, const float rayDistance)
const Fixed& fixed,
const AZ::Transform& worldFromLocal,
const AZ::Transform& localTransform,
const AZ::Vector3& rayOrigin,
const AZ::Vector3& rayDirection,
const float rayDistance)
{
const AZ::Transform worldFromLocalWithTransform = worldFromLocal * localTransform;
const AZ::Vector3 worldAxis = TransformDirectionNoScaling(worldFromLocalWithTransform, fixed.m_axis);
@@ -35,7 +39,7 @@ namespace AzToolsFramework
// if angular manipulator axis is at right angles to us, use initial ray direction
// as plane normal and use hit position on manipulator as plane point
const float pickAngle = AZ::RadToDeg(AZ::Acos(AZ::Abs(rayDirection.Dot(worldAxis))));
if (pickAngle > s_circularRotateThresholdDegrees)
if (pickAngle > CircularRotateThresholdDegrees)
{
actionInternal.m_start.m_planeNormal = -rayDirection;
actionInternal.m_start.m_planePoint = rayOrigin + rayDirection * rayDistance;
@@ -43,8 +47,8 @@ namespace AzToolsFramework
// store initial world hit position
Internal::CalculateRayPlaneIntersectingPoint(
rayOrigin, rayDirection, actionInternal.m_start.m_planePoint,
actionInternal.m_start.m_planeNormal, actionInternal.m_current.m_worldHitPosition);
rayOrigin, rayDirection, actionInternal.m_start.m_planePoint, actionInternal.m_start.m_planeNormal,
actionInternal.m_current.m_worldHitPosition);
// store entity transform (to go from local to world space)
// and store our own starting local transform
@@ -56,31 +60,33 @@ namespace AzToolsFramework
}
AngularManipulator::Action AngularManipulator::CalculateManipulationDataAction(
const Fixed& fixed, ActionInternal& actionInternal, const AZ::Transform& worldFromLocal,
const AZ::Transform& localTransform, const bool snapping, const float angleStepDegrees,
const AZ::Vector3& rayOrigin, const AZ::Vector3& rayDirection,
const Fixed& fixed,
ActionInternal& actionInternal,
const AZ::Transform& worldFromLocal,
const AZ::Transform& localTransform,
const bool snapping,
const float angleStepDegrees,
const AZ::Vector3& rayOrigin,
const AZ::Vector3& rayDirection,
const ViewportInteraction::KeyboardModifiers keyboardModifiers)
{
const AZ::Transform worldFromLocalWithTransform = worldFromLocal * localTransform;
const AZ::Vector3 worldAxis = TransformDirectionNoScaling(worldFromLocalWithTransform, fixed.m_axis);
AZ::Vector3 worldHitPosition = AZ::Vector3::CreateZero();
Internal::CalculateRayPlaneIntersectingPoint(rayOrigin, rayDirection,
actionInternal.m_start.m_planePoint, actionInternal.m_start.m_planeNormal,
worldHitPosition);
Internal::CalculateRayPlaneIntersectingPoint(
rayOrigin, rayDirection, actionInternal.m_start.m_planePoint, actionInternal.m_start.m_planeNormal, worldHitPosition);
// get vector from center of rotation for current and previous frame
const AZ::Vector3 center = worldFromLocalWithTransform.GetTranslation();
const AZ::Vector3 currentWorldHitVector = (worldHitPosition - center).GetNormalizedSafe();
const AZ::Vector3 previousWorldHitVector =
(actionInternal.m_current.m_worldHitPosition - center).GetNormalizedSafe();
const AZ::Vector3 previousWorldHitVector = (actionInternal.m_current.m_worldHitPosition - center).GetNormalizedSafe();
// calculate which direction we rotated
const AZ::Vector3 worldAxisRight = worldAxis.Cross(previousWorldHitVector);
const float rotateSign = Sign(currentWorldHitVector.Dot(worldAxisRight));
// how far did we rotate this frame
const float rotationAngleRad = AZ::Acos(AZ::GetMin<float>(
1.0f, currentWorldHitVector.Dot(previousWorldHitVector)));
const float rotationAngleRad = AZ::Acos(AZ::GetMin<float>(1.0f, currentWorldHitVector.Dot(previousWorldHitVector)));
actionInternal.m_current.m_worldHitPosition = worldHitPosition;
// if we're snapping, only increment current radians when we know
@@ -148,16 +154,13 @@ namespace AzToolsFramework
// calculate initial state when mouse press first happens
m_actionInternal = CalculateManipulationDataStart(
m_fixed, TransformNormalizedScale(GetSpace()), TransformNormalizedScale(GetLocalTransform()),
interaction.m_mousePick.m_rayOrigin, interaction.m_mousePick.m_rayDirection,
rayIntersectionDistance);
interaction.m_mousePick.m_rayOrigin, interaction.m_mousePick.m_rayDirection, rayIntersectionDistance);
if (m_onLeftMouseDownCallback)
{
m_onLeftMouseDownCallback(CalculateManipulationDataAction(
m_fixed, m_actionInternal, m_actionInternal.m_start.m_worldFromLocal,
m_actionInternal.m_start.m_localTransform, snapping, angleStep,
interaction.m_mousePick.m_rayOrigin, interaction.m_mousePick.m_rayDirection,
interaction.m_keyboardModifiers));
m_fixed, m_actionInternal, m_actionInternal.m_start.m_worldFromLocal, m_actionInternal.m_start.m_localTransform, snapping,
angleStep, interaction.m_mousePick.m_rayOrigin, interaction.m_mousePick.m_rayDirection, interaction.m_keyboardModifiers));
}
}
@@ -167,12 +170,9 @@ namespace AzToolsFramework
{
// calculate delta rotation
m_onMouseMoveCallback(CalculateManipulationDataAction(
m_fixed, m_actionInternal, m_actionInternal.m_start.m_worldFromLocal,
m_actionInternal.m_start.m_localTransform,
AngleSnapping(interaction.m_interactionId.m_viewportId),
AngleStep(interaction.m_interactionId.m_viewportId),
interaction.m_mousePick.m_rayOrigin, interaction.m_mousePick.m_rayDirection,
interaction.m_keyboardModifiers));
m_fixed, m_actionInternal, m_actionInternal.m_start.m_worldFromLocal, m_actionInternal.m_start.m_localTransform,
AngleSnapping(interaction.m_interactionId.m_viewportId), AngleStep(interaction.m_interactionId.m_viewportId),
interaction.m_mousePick.m_rayOrigin, interaction.m_mousePick.m_rayDirection, interaction.m_keyboardModifiers));
}
}
@@ -181,12 +181,9 @@ namespace AzToolsFramework
if (m_onLeftMouseUpCallback)
{
m_onLeftMouseUpCallback(CalculateManipulationDataAction(
m_fixed, m_actionInternal, m_actionInternal.m_start.m_worldFromLocal,
m_actionInternal.m_start.m_localTransform,
AngleSnapping(interaction.m_interactionId.m_viewportId),
AngleStep(interaction.m_interactionId.m_viewportId),
interaction.m_mousePick.m_rayOrigin, interaction.m_mousePick.m_rayDirection,
interaction.m_keyboardModifiers));
m_fixed, m_actionInternal, m_actionInternal.m_start.m_worldFromLocal, m_actionInternal.m_start.m_localTransform,
AngleSnapping(interaction.m_interactionId.m_viewportId), AngleStep(interaction.m_interactionId.m_viewportId),
interaction.m_mousePick.m_rayOrigin, interaction.m_mousePick.m_rayDirection, interaction.m_keyboardModifiers));
}
}
@@ -197,12 +194,9 @@ namespace AzToolsFramework
const ViewportInteraction::MouseInteraction& mouseInteraction)
{
m_manipulatorView->Draw(
GetManipulatorManagerId(), managerState,
GetManipulatorId(), {
ApplySpace(GetLocalTransform()), GetNonUniformScale(),
AZ::Vector3::CreateZero(), MouseOver()
},
debugDisplay, cameraState, mouseInteraction);
GetManipulatorManagerId(), managerState, GetManipulatorId(),
{ ApplySpace(GetLocalTransform()), GetNonUniformScale(), AZ::Vector3::CreateZero(), MouseOver() }, debugDisplay, cameraState,
mouseInteraction);
}
void AngularManipulator::SetAxis(const AZ::Vector3& axis)
@@ -1,14 +1,14 @@
/*
* 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.
*
*/
* 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
@@ -22,14 +22,14 @@ namespace AzToolsFramework
{
class ManipulatorView;
/// AngularManipulator serves as a visual tool for users to change a component's property based on rotation
/// around an axis. The rotation angle increases if the rotation goes counter clock-wise when looking
/// in the opposite direction the rotation axis points to.
//! AngularManipulator serves as a visual tool for users to change a component's property based on rotation
//! around an axis. The rotation angle increases if the rotation goes counter clock-wise when looking
//! in the opposite direction the rotation axis points to.
class AngularManipulator
: public BaseManipulator
, public ManipulatorSpaceWithLocalTransform
{
/// Private constructor.
//! Private constructor.
explicit AngularManipulator(const AZ::Transform& worldFromLocal);
public:
@@ -42,33 +42,36 @@ namespace AzToolsFramework
~AngularManipulator() = default;
/// A Manipulator must only be created and managed through a shared_ptr.
//! A Manipulator must only be created and managed through a shared_ptr.
static AZStd::shared_ptr<AngularManipulator> MakeShared(const AZ::Transform& worldFromLocal);
/// The state of the manipulator at the start of an interaction.
//! The state of the manipulator at the start of an interaction.
struct Start
{
AZ::Quaternion m_space; ///< Starting orientation space of manipulator.
AZ::Quaternion m_rotation; ///< Starting local rotation of the manipulator.
AZ::Quaternion m_space; //!< Starting orientation space of manipulator.
AZ::Quaternion m_rotation; //!< Starting local rotation of the manipulator.
};
/// The state of the manipulator during an interaction.
//! The state of the manipulator during an interaction.
struct Current
{
AZ::Quaternion m_delta; ///< Amount of rotation to apply to manipulator during action.
AZ::Quaternion m_delta; //!< Amount of rotation to apply to manipulator during action.
};
/// Mouse action data used by MouseActionCallback (wraps Start and Current manipulator state).
//! Mouse action data used by MouseActionCallback (wraps Start and Current manipulator state).
struct Action
{
Start m_start;
Current m_current;
ViewportInteraction::KeyboardModifiers m_modifiers;
AZ::Quaternion LocalOrientation() const { return m_start.m_rotation * m_current.m_delta; }
AZ::Quaternion LocalOrientation() const
{
return m_start.m_rotation * m_current.m_delta;
}
};
/// This is the function signature of callbacks that will be invoked whenever a manipulator
/// is clicked on or dragged.
//! This is the function signature of callbacks that will be invoked whenever a manipulator
//! is clicked on or dragged.
using MouseActionCallback = AZStd::function<void(const Action&)>;
void InstallLeftMouseDownCallback(const MouseActionCallback& onMouseDownCallback);
@@ -82,46 +85,49 @@ namespace AzToolsFramework
const ViewportInteraction::MouseInteraction& mouseInteraction) override;
void SetAxis(const AZ::Vector3& axis);
const AZ::Vector3& GetAxis() const { return m_fixed.m_axis; }
const AZ::Vector3& GetAxis() const
{
return m_fixed.m_axis;
}
void SetView(AZStd::unique_ptr<ManipulatorView>&& view);
ManipulatorView* GetView() const { return m_manipulatorView.get(); }
ManipulatorView* GetView() const
{
return m_manipulatorView.get();
}
private:
void OnLeftMouseDownImpl(
const ViewportInteraction::MouseInteraction& interaction, float rayIntersectionDistance) override;
void OnLeftMouseUpImpl(
const ViewportInteraction::MouseInteraction& interaction) override;
void OnMouseMoveImpl(
const ViewportInteraction::MouseInteraction& interaction) override;
void OnLeftMouseDownImpl(const ViewportInteraction::MouseInteraction& interaction, float rayIntersectionDistance) override;
void OnLeftMouseUpImpl(const ViewportInteraction::MouseInteraction& interaction) override;
void OnMouseMoveImpl(const ViewportInteraction::MouseInteraction& interaction) override;
void SetBoundsDirtyImpl() override;
void InvalidateImpl() override;
/// Unchanging data set once for the angular manipulator.
//! Unchanging data set once for the angular manipulator.
struct Fixed
{
AZ::Vector3 m_axis = AZ::Vector3::CreateAxisX(); ///< Axis for this angular manipulator to rotate around.
AZ::Vector3 m_axis = AZ::Vector3::CreateAxisX(); //!< Axis for this angular manipulator to rotate around.
};
/// Initial data recorded when a press first happens with an angular manipulator.
//! Initial data recorded when a press first happens with an angular manipulator.
struct StartInternal
{
AZ::Transform m_worldFromLocal; ///< Initial transform when pressed.
AZ::Transform m_localTransform; ///< Additional transform (offset) to apply to manipulator.
AZ::Vector3 m_planePoint; ///< Position on plane to use for ray intersection.
AZ::Vector3 m_planeNormal; ///< Normal of plane to use for ray intersection.
AZ::Transform m_worldFromLocal; //!< Initial transform when pressed.
AZ::Transform m_localTransform; //!< Additional transform (offset) to apply to manipulator.
AZ::Vector3 m_planePoint; //!< Position on plane to use for ray intersection.
AZ::Vector3 m_planeNormal; //!< Normal of plane to use for ray intersection.
};
/// Current data recorded each frame during an interaction with an angular manipulator.
//! Current data recorded each frame during an interaction with an angular manipulator.
struct CurrentInternal
{
float m_preSnapRadians = 0.0f; ///< Amount of rotation before a snap (snap increment accumulator).
float m_radians = 0.0f; ///< Amount of rotation about the axis for this action.
AZ::Vector3 m_worldHitPosition; ///< Initial world space hit position.
float m_preSnapRadians = 0.0f; //!< Amount of rotation before a snap (snap increment accumulator).
float m_radians = 0.0f; //!< Amount of rotation about the axis for this action.
AZ::Vector3 m_worldHitPosition; //!< Initial world space hit position.
};
/// Wrap start and current internal data during an interaction with an angular manipulator.
//! Wrap start and current internal data during an interaction with an angular manipulator.
struct ActionInternal
{
StartInternal m_start;
@@ -135,16 +141,25 @@ namespace AzToolsFramework
MouseActionCallback m_onLeftMouseUpCallback = nullptr;
MouseActionCallback m_onMouseMoveCallback = nullptr;
AZStd::unique_ptr<ManipulatorView> m_manipulatorView; ///< Look of manipulator.
AZStd::unique_ptr<ManipulatorView> m_manipulatorView; //!< Look of manipulator.
static ActionInternal CalculateManipulationDataStart(
const Fixed& fixed, const AZ::Transform& worldFromLocal, const AZ::Transform& localTransform,
const AZ::Vector3& rayOrigin, const AZ::Vector3& rayDirection, float rayDistance);
const Fixed& fixed,
const AZ::Transform& worldFromLocal,
const AZ::Transform& localTransform,
const AZ::Vector3& rayOrigin,
const AZ::Vector3& rayDirection,
float rayDistance);
static Action CalculateManipulationDataAction(
const Fixed& fixed, ActionInternal& actionInternal, const AZ::Transform& worldFromLocal,
const AZ::Transform& localTransform, bool snapping, float angleStepDegrees,
const AZ::Vector3& rayOrigin, const AZ::Vector3& rayDirection,
const Fixed& fixed,
ActionInternal& actionInternal,
const AZ::Transform& worldFromLocal,
const AZ::Transform& localTransform,
bool snapping,
float angleStepDegrees,
const AZ::Vector3& rayOrigin,
const AZ::Vector3& rayDirection,
ViewportInteraction::KeyboardModifiers keyboardModifiers);
};
} // namespace AzToolsFramework
@@ -1,33 +1,30 @@
/*
* 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.
*
*/
* 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.
*
*/
#include "BaseManipulator.h"
#include <AzCore/Math/IntersectSegment.h>
#include <AzToolsFramework/Viewport/ViewportMessages.h>
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
#include <AzToolsFramework/Viewport/ViewportMessages.h>
namespace AzToolsFramework
{
AZ_CVAR(
bool, cl_manipulatorDrawDebug, false, nullptr, AZ::ConsoleFunctorFlags::Null,
"Enable debug drawing for Manipulators");
AZ_CVAR(bool, cl_manipulatorDrawDebug, false, nullptr, AZ::ConsoleFunctorFlags::Null, "Enable debug drawing for Manipulators");
const AZ::Color BaseManipulator::s_defaultMouseOverColor = AZ::Color(1.0f, 1.0f, 0.0f, 1.0f); // yellow
AZ_CLASS_ALLOCATOR_IMPL(BaseManipulator, AZ::SystemAllocator, 0)
static bool EntityIdAndEntityComponentIdComparison(
const AZ::EntityId entityId, const AZ::EntityComponentIdPair& entityComponentId)
static bool EntityIdAndEntityComponentIdComparison(const AZ::EntityId entityId, const AZ::EntityComponentIdPair& entityComponentId)
{
return entityId == entityComponentId.GetEntityId();
}
@@ -38,8 +35,7 @@ namespace AzToolsFramework
EndUndoBatch();
}
bool BaseManipulator::OnLeftMouseDown(
const ViewportInteraction::MouseInteraction& interaction, const float rayIntersectionDistance)
bool BaseManipulator::OnLeftMouseDown(const ViewportInteraction::MouseInteraction& interaction, const float rayIntersectionDistance)
{
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework);
@@ -57,8 +53,7 @@ namespace AzToolsFramework
(*this.*m_onLeftMouseDownImpl)(interaction, rayIntersectionDistance);
ToolsApplicationNotificationBus::Broadcast(
&ToolsApplicationNotificationBus::Events::InvalidatePropertyDisplay, Refresh_Values);
ToolsApplicationNotificationBus::Broadcast(&ToolsApplicationNotificationBus::Events::InvalidatePropertyDisplay, Refresh_Values);
return true;
}
@@ -66,8 +61,7 @@ namespace AzToolsFramework
return false;
}
bool BaseManipulator::OnRightMouseDown(
const ViewportInteraction::MouseInteraction& interaction, const float rayIntersectionDistance)
bool BaseManipulator::OnRightMouseDown(const ViewportInteraction::MouseInteraction& interaction, const float rayIntersectionDistance)
{
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework);
@@ -85,8 +79,7 @@ namespace AzToolsFramework
(*this.*m_onRightMouseDownImpl)(interaction, rayIntersectionDistance);
ToolsApplicationNotificationBus::Broadcast(
&ToolsApplicationNotificationBus::Events::InvalidatePropertyDisplay, Refresh_Values);
ToolsApplicationNotificationBus::Broadcast(&ToolsApplicationNotificationBus::Events::InvalidatePropertyDisplay, Refresh_Values);
return true;
}
@@ -118,8 +111,7 @@ namespace AzToolsFramework
EndUndoBatch();
}
bool BaseManipulator::OnMouseOver(
const ManipulatorId manipulatorId, const ViewportInteraction::MouseInteraction& interaction)
bool BaseManipulator::OnMouseOver(const ManipulatorId manipulatorId, const ViewportInteraction::MouseInteraction& interaction)
{
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework);
@@ -132,8 +124,7 @@ namespace AzToolsFramework
{
OnMouseWheelImpl(interaction);
ToolsApplicationNotificationBus::Broadcast(
&ToolsApplicationNotificationBus::Events::InvalidatePropertyDisplay, Refresh_Values);
ToolsApplicationNotificationBus::Broadcast(&ToolsApplicationNotificationBus::Events::InvalidatePropertyDisplay, Refresh_Values);
}
void BaseManipulator::OnMouseMove(const ViewportInteraction::MouseInteraction& interaction)
@@ -142,16 +133,13 @@ namespace AzToolsFramework
if (!m_performingAction)
{
AZ_Warning(
"Manipulators", false,
"MouseMove action received, but this manipulator is not performing an action");
AZ_Warning("Manipulators", false, "MouseMove action received, but this manipulator is not performing an action");
return;
}
// ensure property grid (entity inspector) values are refreshed
ToolsApplicationNotificationBus::Broadcast(
&ToolsApplicationNotificationBus::Events::InvalidatePropertyDisplay, Refresh_Values);
ToolsApplicationNotificationBus::Broadcast(&ToolsApplicationNotificationBus::Events::InvalidatePropertyDisplay, Refresh_Values);
OnMouseMoveImpl(interaction);
}
@@ -170,16 +158,14 @@ namespace AzToolsFramework
Unregister();
}
ManipulatorManagerRequestBus::Event(managerId,
&ManipulatorManagerRequestBus::Events::RegisterManipulator, shared_from_this());
ManipulatorManagerRequestBus::Event(managerId, &ManipulatorManagerRequestBus::Events::RegisterManipulator, shared_from_this());
}
void BaseManipulator::Unregister()
{
// if the manipulator has already been unregistered, the m_manipulatorManagerId
// should be invalid which makes the call below a no-op.
ManipulatorManagerRequestBus::Event(m_manipulatorManagerId,
&ManipulatorManagerRequestBus::Events::UnregisterManipulator, this);
ManipulatorManagerRequestBus::Event(m_manipulatorManagerId, &ManipulatorManagerRequestBus::Events::UnregisterManipulator, this);
}
void BaseManipulator::Invalidate()
@@ -197,8 +183,7 @@ namespace AzToolsFramework
if (m_performingAction)
{
AZ_Warning(
"Manipulators", false,
"MouseDown action received, but the manipulator (id: %d) is still performing an action",
"Manipulators", false, "MouseDown action received, but the manipulator (id: %d) is still performing an action",
GetManipulatorId());
return;
@@ -214,8 +199,7 @@ namespace AzToolsFramework
if (!m_performingAction)
{
AZ_Warning(
"Manipulators", false,
"MouseUp action received, but this manipulator (id: %d) didn't receive MouseDown action before",
"Manipulators", false, "MouseUp action received, but this manipulator (id: %d) didn't receive MouseDown action before",
GetManipulatorId());
return;
}
@@ -263,13 +247,13 @@ namespace AzToolsFramework
if (entityComponentIdPair.GetComponentId() != AZ::InvalidComponentId)
{
PropertyEditorEntityChangeNotificationBus::Event(
entityComponentIdPair.GetEntityId(),
&PropertyEditorEntityChangeNotifications::OnEntityComponentPropertyChanged,
entityComponentIdPair.GetEntityId(), &PropertyEditorEntityChangeNotifications::OnEntityComponentPropertyChanged,
entityComponentIdPair.GetComponentId());
}
else
{
AZ_Warning("Manipulators", false,
AZ_Warning(
"Manipulators", false,
"This Manipulator was only registered with an EntityId and not an EntityComponentIdPair. "
"Please use AddEntityComponentIdPair() instead of AddEntityId() when registering what this "
"Manipulator is changing.");
@@ -280,8 +264,7 @@ namespace AzToolsFramework
for (const AZ::Component* component : entity->GetComponents())
{
PropertyEditorEntityChangeNotificationBus::Event(
entity->GetId(), &PropertyEditorEntityChangeNotifications::OnEntityComponentPropertyChanged,
component->GetId());
entity->GetId(), &PropertyEditorEntityChangeNotifications::OnEntityComponentPropertyChanged, component->GetId());
}
}
}
@@ -298,9 +281,7 @@ namespace AzToolsFramework
{
// look for a match (keep looking in case we have several entity ids with different component ids)
const auto entityComponentPairId =
m_entityComponentIdPairs.find_as(
entityId, AZStd::hash<AZ::EntityId>(),
&EntityIdAndEntityComponentIdComparison);
m_entityComponentIdPairs.find_as(entityId, AZStd::hash<AZ::EntityId>(), &EntityIdAndEntityComponentIdComparison);
// update the afterErased variable so we can return an iterator
// to the correct position in the container.
@@ -334,9 +315,8 @@ namespace AzToolsFramework
bool BaseManipulator::HasEntityId(const AZ::EntityId entityId) const
{
return m_entityComponentIdPairs.find_as(
entityId, AZStd::hash<AZ::EntityId>(),
&EntityIdAndEntityComponentIdComparison) != m_entityComponentIdPairs.end();
return m_entityComponentIdPairs.find_as(entityId, AZStd::hash<AZ::EntityId>(), &EntityIdAndEntityComponentIdComparison) !=
m_entityComponentIdPairs.end();
}
bool BaseManipulator::HasEntityComponentIdPair(const AZ::EntityComponentIdPair& entityComponentIdPair) const
@@ -346,7 +326,8 @@ namespace AzToolsFramework
void Manipulators::Register(const ManipulatorManagerId manipulatorManagerId)
{
ProcessManipulators([manipulatorManagerId](BaseManipulator* manipulator)
ProcessManipulators(
[manipulatorManagerId](BaseManipulator* manipulator)
{
manipulator->Register(manipulatorManagerId);
});
@@ -354,7 +335,8 @@ namespace AzToolsFramework
void Manipulators::Unregister()
{
ProcessManipulators([](BaseManipulator* manipulator)
ProcessManipulators(
[](BaseManipulator* manipulator)
{
if (manipulator->Registered())
{
@@ -365,7 +347,8 @@ namespace AzToolsFramework
void Manipulators::SetBoundsDirty()
{
ProcessManipulators([](BaseManipulator* manipulator)
ProcessManipulators(
[](BaseManipulator* manipulator)
{
manipulator->SetBoundsDirty();
});
@@ -373,7 +356,8 @@ namespace AzToolsFramework
void Manipulators::AddEntityComponentIdPair(const AZ::EntityComponentIdPair& entityComponentIdPair)
{
ProcessManipulators([&entityComponentIdPair](BaseManipulator* manipulator)
ProcessManipulators(
[&entityComponentIdPair](BaseManipulator* manipulator)
{
manipulator->AddEntityComponentIdPair(entityComponentIdPair);
});
@@ -381,7 +365,8 @@ namespace AzToolsFramework
void Manipulators::RemoveEntityComponentIdPair(const AZ::EntityComponentIdPair& entityComponentIdPair)
{
ProcessManipulators([&entityComponentIdPair](BaseManipulator* manipulator)
ProcessManipulators(
[&entityComponentIdPair](BaseManipulator* manipulator)
{
manipulator->RemoveEntityComponentIdPair(entityComponentIdPair);
});
@@ -389,7 +374,8 @@ namespace AzToolsFramework
void Manipulators::RemoveEntityId(const AZ::EntityId entityId)
{
ProcessManipulators([entityId](BaseManipulator* manipulator)
ProcessManipulators(
[entityId](BaseManipulator* manipulator)
{
manipulator->RemoveEntityId(entityId);
});
@@ -398,7 +384,8 @@ namespace AzToolsFramework
bool Manipulators::PerformingAction()
{
bool performingAction = false;
ProcessManipulators([&performingAction](BaseManipulator* manipulator)
ProcessManipulators(
[&performingAction](BaseManipulator* manipulator)
{
if (manipulator->PerformingAction())
{
@@ -412,7 +399,8 @@ namespace AzToolsFramework
bool Manipulators::Registered()
{
bool registered = false;
ProcessManipulators([&registered](BaseManipulator* manipulator)
ProcessManipulators(
[&registered](BaseManipulator* manipulator)
{
if (manipulator->Registered())
{
@@ -433,6 +421,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);
@@ -465,8 +458,12 @@ namespace AzToolsFramework
namespace Internal
{
bool CalculateRayPlaneIntersectingPoint(const AZ::Vector3& rayOrigin, const AZ::Vector3& rayDirection,
const AZ::Vector3& pointOnPlane, const AZ::Vector3& planeNormal, AZ::Vector3& resultIntersectingPoint)
bool CalculateRayPlaneIntersectingPoint(
const AZ::Vector3& rayOrigin,
const AZ::Vector3& rayDirection,
const AZ::Vector3& pointOnPlane,
const AZ::Vector3& planeNormal,
AZ::Vector3& resultIntersectingPoint)
{
float t = 0.0f;
if (AZ::Intersect::IntersectRayPlane(rayOrigin, rayDirection, pointOnPlane, planeNormal, t) > 0)
@@ -479,11 +476,12 @@ namespace AzToolsFramework
}
AZ::Vector3 TryConstrainHitPositionToView(
const AZ::Vector3& currentLocalHitPosition, const AZ::Vector3& startLocalHitPosition,
const AZ::Transform& localFromWorld, const AzFramework::CameraState& cameraState)
const AZ::Vector3& currentLocalHitPosition,
const AZ::Vector3& startLocalHitPosition,
const AZ::Transform& localFromWorld,
const AzFramework::CameraState& cameraState)
{
if (currentLocalHitPosition.GetDistance(localFromWorld.TransformPoint(cameraState.m_position))
> cameraState.m_farClip)
if (currentLocalHitPosition.GetDistance(localFromWorld.TransformPoint(cameraState.m_position)) > cameraState.m_farClip)
{
return startLocalHitPosition;
}
@@ -1,14 +1,14 @@
/*
* 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.
*
*/
* 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
@@ -22,13 +22,13 @@
#include <AzCore/std/containers/set.h>
#include <AzCore/std/smart_ptr/enable_shared_from_this.h>
#include <AzToolsFramework/Manipulators/ManipulatorBus.h>
#include "ManipulatorSpace.h"
#include <AzToolsFramework/Manipulators/ManipulatorSpace.h>
namespace AzFramework
{
struct CameraState;
class DebugDisplayRequests;
}
} // namespace AzFramework
namespace AzToolsFramework
{
@@ -46,9 +46,8 @@ namespace AzToolsFramework
struct ManipulatorManagerState;
/// The base class for manipulators, providing interfaces for users of manipulators to talk to.
class BaseManipulator
: public AZStd::enable_shared_from_this<BaseManipulator>
//! The base class for manipulators, providing interfaces for users of manipulators to talk to.
class BaseManipulator : public AZStd::enable_shared_from_this<BaseManipulator>
{
public:
AZ_CLASS_ALLOCATOR_DECL
@@ -61,139 +60,181 @@ namespace AzToolsFramework
using EntityComponentIds = AZStd::unordered_set<AZ::EntityComponentIdPair>;
/// Callback for the event when the mouse pointer is over this manipulator and the left mouse button is pressed.
/// @param interaction It contains various mouse states when the event happens, as well as a ray shooting from the viewing camera through the mouse pointer.
/// @param rayIntersectionDistance The parameter value in the ray's explicit equation that represents the intersecting point on the target manipulator in world space.
/// @return Return true if OnLeftMouseDownImpl was attached and will be used.
//! Callback for the event when the mouse pointer is over this manipulator and the left mouse button is pressed.
//! @param interaction It contains various mouse states when the event happens, as well as a ray shooting from the viewing camera
//! through the mouse pointer.
//! @param rayIntersectionDistance The parameter value in the ray's explicit equation that represents the intersecting point on the
//! target manipulator in world space.
//! @return Return true if OnLeftMouseDownImpl was attached and will be used.
bool OnLeftMouseDown(const ViewportInteraction::MouseInteraction& interaction, float rayIntersectionDistance);
/// Callback for the event when this manipulator is active and the left mouse button is released.
/// @param interaction It contains various mouse states when the event happens, as well as a ray shooting from the viewing camera through the mouse pointer.
//! Callback for the event when this manipulator is active and the left mouse button is released.
//! @param interaction It contains various mouse states when the event happens, as well as a ray shooting from the viewing camera
//! through the mouse pointer.
void OnLeftMouseUp(const ViewportInteraction::MouseInteraction& interaction);
/// Callback for the event when the mouse pointer is over this manipulator and the right mouse button is pressed .
/// @param interaction It contains various mouse states when the event happens, as well as a ray shooting from the viewing camera through the mouse pointer.
/// @param rayIntersectionDistance The parameter value in the ray's explicit equation that represents the intersecting point on the target manipulator in world space.
/// @return Return true if OnRightMouseDownImpl was attached and will be used.
//! Callback for the event when the mouse pointer is over this manipulator and the right mouse button is pressed .
//! @param interaction It contains various mouse states when the event happens, as well as a ray shooting from the viewing camera
//! through the mouse pointer.
//! @param rayIntersectionDistance The parameter value in the ray's explicit equation that represents the intersecting point on the
//! target manipulator in world space.
//! @return Return true if OnRightMouseDownImpl was attached and will be used.
bool OnRightMouseDown(const ViewportInteraction::MouseInteraction& interaction, float rayIntersectionDistance);
/// Callback for the event when this manipulator is active and the right mouse button is released.
/// @param interaction It contains various mouse states when the event happens, as well as a ray shooting from the viewing camera through the mouse pointer.
//! Callback for the event when this manipulator is active and the right mouse button is released.
//! @param interaction It contains various mouse states when the event happens, as well as a ray shooting from the viewing camera
//! through the mouse pointer.
void OnRightMouseUp(const ViewportInteraction::MouseInteraction& interaction);
/// Callback for the event when this manipulator is active and the mouse is moved.
/// @param interaction It contains various mouse states when the event happens, as well as a ray shooting from the viewing camera through the mouse pointer.
//! Callback for the event when this manipulator is active and the mouse is moved.
//! @param interaction It contains various mouse states when the event happens, as well as a ray shooting from the viewing camera
//! through the mouse pointer.
void OnMouseMove(const ViewportInteraction::MouseInteraction& interaction);
/// Callback for the event when this manipulator is active and the mouse wheel is scrolled.
/// @param interaction It contains various mouse states when the event happens, as well as a ray shooting from the viewing camera through the mouse pointer.
//! Callback for the event when this manipulator is active and the mouse wheel is scrolled.
//! @param interaction It contains various mouse states when the event happens, as well as a ray shooting from the viewing camera
//! through the mouse pointer.
void OnMouseWheel(const ViewportInteraction::MouseInteraction& interaction);
/// This function changes the state indicating whether the manipulator is under the mouse pointer.
/// It is called in the event of OnMouseMove and OnMouseWheel only when there is no manipulator currently performing actions.
//! This function changes the state indicating whether the manipulator is under the mouse pointer.
//! It is called in the event of OnMouseMove and OnMouseWheel only when there is no manipulator currently performing actions.
bool OnMouseOver(ManipulatorId manipulatorId, const ViewportInteraction::MouseInteraction& interaction);
/// Register itself to a manipulator manager so that it can receive various mouse events and perform manipulations.
/// @param managerId The id identifying a unique manipulator manager.
//! Register itself to a manipulator manager so that it can receive various mouse events and perform manipulations.
//! @param managerId The id identifying a unique manipulator manager.
void Register(ManipulatorManagerId managerId);
/// Unregister itself from the manipulator manager it was registered with.
//! Unregister itself from the manipulator manager it was registered with.
void Unregister();
/// Bounds will need to be recalculated next time we render.
//! Bounds will need to be recalculated next time we render.
void SetBoundsDirty();
/// Is this manipulator currently registered with a manipulator manager.
//! Is this manipulator currently registered with a manipulator manager.
bool Registered() const
{
return m_manipulatorId != InvalidManipulatorId &&
m_manipulatorManagerId != InvalidManipulatorManagerId;
return m_manipulatorId != InvalidManipulatorId && m_manipulatorManagerId != InvalidManipulatorManagerId;
}
/// Is the manipulator in the middle of an action (between mouse down and mouse up).
bool PerformingAction() const { return m_performingAction; }
//! Is the manipulator in the middle of an action (between mouse down and mouse up).
bool PerformingAction() const
{
return m_performingAction;
}
/// Is the mouse currently over the manipulator (intersecting manipulator bound).
bool MouseOver() const { return m_mouseOver; }
//! Is the mouse currently over the manipulator (intersecting manipulator bound).
bool MouseOver() const
{
return m_mouseOver;
}
/// The unique id of this manipulator.
ManipulatorId GetManipulatorId() const { return m_manipulatorId; }
//! The unique id of this manipulator.
ManipulatorId GetManipulatorId() const
{
return m_manipulatorId;
}
/// The unique id of the manager this manipulator was registered with.
ManipulatorManagerId GetManipulatorManagerId() const { return m_manipulatorManagerId; }
//! The unique id of the manager this manipulator was registered with.
ManipulatorManagerId GetManipulatorManagerId() const
{
return m_manipulatorManagerId;
}
/// Returns all EntityComponentIdPairs associated with this manipulator.
//! Returns all EntityComponentIdPairs associated with this manipulator.
const EntityComponentIds& EntityComponentIdPairs() const
{
return m_entityComponentIdPairs;
}
/// Add an entity and component the manipulator is responsible for.
//! Add an entity and component the manipulator is responsible for.
void AddEntityComponentIdPair(const AZ::EntityComponentIdPair& entityComponentIdPair);
/// Remove an entity from being affected by this manipulator.
/// @note All components on this entity registered with the manipulator will be removed.
//! Remove an entity from being affected by this manipulator.
//! @note All components on this entity registered with the manipulator will be removed.
EntityComponentIds::iterator RemoveEntityId(AZ::EntityId entityId);
/// Remove a specific component (via a EntityComponentIdPair) being affected by this manipulator.
//! Remove a specific component (via a EntityComponentIdPair) being affected by this manipulator.
EntityComponentIds::iterator RemoveEntityComponentIdPair(const AZ::EntityComponentIdPair& entityComponentIdPair);
/// Is this entity currently being tracked by this manipulator.
//! Is this entity currently being tracked by this manipulator.
bool HasEntityId(AZ::EntityId entityId) const;
/// Is this entity component pair currently being tracked by this manipulator.
//! Is this entity component pair currently being tracked by this manipulator.
bool HasEntityComponentIdPair(const AZ::EntityComponentIdPair& entityComponentIdPair) const;
/// Forward a mouse over event in a case where we need the manipulator to immediately refresh.
/// @note Only call this when a mouse over event has just happened.
//! Forward a mouse over event in a case where we need the manipulator to immediately refresh.
//! @note Only call this when a mouse over event has just happened.
void ForwardMouseOverEvent(const ViewportInteraction::MouseInteraction& interaction);
static const AZ::Color s_defaultMouseOverColor;
protected:
/// Protected constructor.
//! Protected constructor.
BaseManipulator() = default;
/// Called when unregistering - users of manipulators should not call it directly.
//! Called when unregistering - users of manipulators should not call it directly.
void Invalidate();
/// The implementation to override in a derived class for Invalidate.
virtual void InvalidateImpl() {}
//! The implementation to override in a derived class for Invalidate.
virtual void InvalidateImpl()
{
}
/// The implementation to override in a derived class for OnLeftMouseDown.
/// Note: When implementing this function you must also call AttachLeftMouseDownImpl to ensure
/// m_onLeftMouseDownImpl is set to OnLeftMouseDownImpl, otherwise it will not be called
virtual void OnLeftMouseDownImpl(
const ViewportInteraction::MouseInteraction& /*interaction*/, float /*rayIntersectionDistance*/) {}
void AttachLeftMouseDownImpl() { m_onLeftMouseDownImpl = &BaseManipulator::OnLeftMouseDownImpl; }
//! The implementation to override in a derived class for OnLeftMouseDown.
//! Note: When implementing this function you must also call AttachLeftMouseDownImpl to ensure
//! m_onLeftMouseDownImpl is set to OnLeftMouseDownImpl, otherwise it will not be called
virtual void OnLeftMouseDownImpl(const ViewportInteraction::MouseInteraction& /*interaction*/, float /*rayIntersectionDistance*/)
{
}
/// The implementation to override in a derived class for OnRightMouseDown.
/// Note: When implementing this function you must also call AttachRightMouseDownImpl to ensure
/// m_onRightMouseDownImpl is set to OnRightMouseDownImpl, otherwise it will not be called
virtual void OnRightMouseDownImpl(
const ViewportInteraction::MouseInteraction& /*interaction*/, float /*rayIntersectionDistance*/) {}
void AttachRightMouseDownImpl() { m_onRightMouseDownImpl = &BaseManipulator::OnRightMouseDownImpl; }
void AttachLeftMouseDownImpl()
{
m_onLeftMouseDownImpl = &BaseManipulator::OnLeftMouseDownImpl;
}
/// The implementation to override in a derived class for OnLeftMouseUp.
virtual void OnLeftMouseUpImpl(const ViewportInteraction::MouseInteraction& /*interaction*/) {}
//! The implementation to override in a derived class for OnRightMouseDown.
//! Note: When implementing this function you must also call AttachRightMouseDownImpl to ensure
//! m_onRightMouseDownImpl is set to OnRightMouseDownImpl, otherwise it will not be called
virtual void OnRightMouseDownImpl(const ViewportInteraction::MouseInteraction& /*interaction*/, float /*rayIntersectionDistance*/)
{
}
/// The implementation to override in a derived class for OnRightMouseUp.
virtual void OnRightMouseUpImpl(const ViewportInteraction::MouseInteraction& /*interaction*/) {}
void AttachRightMouseDownImpl()
{
m_onRightMouseDownImpl = &BaseManipulator::OnRightMouseDownImpl;
}
/// The implementation to override in a derived class for OnMouseMove.
virtual void OnMouseMoveImpl(const ViewportInteraction::MouseInteraction& /*interaction*/) {}
//! The implementation to override in a derived class for OnLeftMouseUp.
virtual void OnLeftMouseUpImpl(const ViewportInteraction::MouseInteraction& /*interaction*/)
{
}
/// The implementation to override in a derived class for OnMouseOver.
virtual void OnMouseOverImpl(
ManipulatorId /*manipulatorId*/, const ViewportInteraction::MouseInteraction& /*interaction*/) {}
//! The implementation to override in a derived class for OnRightMouseUp.
virtual void OnRightMouseUpImpl(const ViewportInteraction::MouseInteraction& /*interaction*/)
{
}
/// The implementation to override in a derived class for OnMouseWheel.
virtual void OnMouseWheelImpl(const ViewportInteraction::MouseInteraction& /*interaction*/) {}
//! The implementation to override in a derived class for OnMouseMove.
virtual void OnMouseMoveImpl(const ViewportInteraction::MouseInteraction& /*interaction*/)
{
}
/// The implementation to override in a derived class for SetBoundsDirty.
virtual void SetBoundsDirtyImpl() {}
//! The implementation to override in a derived class for OnMouseOver.
virtual void OnMouseOverImpl(ManipulatorId /*manipulatorId*/, const ViewportInteraction::MouseInteraction& /*interaction*/)
{
}
/// Rendering for the manipulator - it is recommended drawing be delegated to a ManipulatorView.
//! The implementation to override in a derived class for OnMouseWheel.
virtual void OnMouseWheelImpl(const ViewportInteraction::MouseInteraction& /*interaction*/)
{
}
//! The implementation to override in a derived class for SetBoundsDirty.
virtual void SetBoundsDirtyImpl()
{
}
//! Rendering for the manipulator - it is recommended drawing be delegated to a ManipulatorView.
virtual void Draw(
const ManipulatorManagerState& managerState,
AzFramework::DebugDisplayRequests& debugDisplay,
@@ -202,39 +243,39 @@ namespace AzToolsFramework
private:
friend class ManipulatorManager;
AZStd::unordered_set<AZ::EntityComponentIdPair> m_entityComponentIdPairs; ///< The entities this manipulator is associated with.
AZStd::unordered_set<AZ::EntityComponentIdPair> m_entityComponentIdPairs; //!< The entities this manipulator is associated with.
ManipulatorId m_manipulatorId = InvalidManipulatorId; ///< The unique id of this manipulator.
ManipulatorManagerId m_manipulatorManagerId = InvalidManipulatorManagerId; ///< The manager this manipulator was registered with.
UndoSystem::URSequencePoint* m_undoBatch = nullptr; ///< Undo active while mouse is pressed.
bool m_performingAction = false; ///< After mouse down and before mouse up.
bool m_mouseOver = false; ///< Is the mouse pointer over the manipulator bound.
ManipulatorId m_manipulatorId = InvalidManipulatorId; //!< The unique id of this manipulator.
ManipulatorManagerId m_manipulatorManagerId = InvalidManipulatorManagerId; //!< The manager this manipulator was registered with.
UndoSystem::URSequencePoint* m_undoBatch = nullptr; //!< Undo active while mouse is pressed.
bool m_performingAction = false; //!< After mouse down and before mouse up.
bool m_mouseOver = false; //!< Is the mouse pointer over the manipulator bound.
/// Member function pointers to OnLeftMouseDownImpl and OnRightMouseDownImpl.
/// Set in AttachLeft/RightMouseDownImpl.
//! Member function pointers to OnLeftMouseDownImpl and OnRightMouseDownImpl.
//! Set in AttachLeft/RightMouseDownImpl.
void (BaseManipulator::*m_onLeftMouseDownImpl)(
const ViewportInteraction::MouseInteraction& interaction, float rayIntersectionDistance) = nullptr;
void (BaseManipulator::*m_onRightMouseDownImpl)(
const ViewportInteraction::MouseInteraction& interaction, float rayIntersectionDistance) = nullptr;
/// Update the mouseOver state for this manipulator.
//! Update the mouseOver state for this manipulator.
void UpdateMouseOver(ManipulatorId manipulatorId);
/// Manage correctly ending the undo batch.
//! Manage correctly ending the undo batch.
void EndUndoBatch();
/// Record an action as having started.
//! Record an action as having started.
void BeginAction();
/// Record an action as having stopped.
//! Record an action as having stopped.
void EndAction();
/// Let other systems (UI) know that a component property has been modified by a manipulator.
//! Let other systems (UI) know that a component property has been modified by a manipulator.
void NotifyEntityComponentPropertyChanged();
};
/// Base class to be used when composing aggregate manipulator types - wraps some
/// common functionality all manipulators need.
//! Base class to be used when composing aggregate manipulator types - wraps some
//! common functionality all manipulators need.
class Manipulators
{
public:
@@ -249,8 +290,10 @@ namespace AzToolsFramework
bool PerformingAction();
bool Registered();
/// Refresh the Manipulator and/or View based on the current view position.
virtual void RefreshView(const AZ::Vector3& /*worldViewPosition*/) {}
//! Refresh the Manipulator and/or View based on the current view position.
virtual void RefreshView(const AZ::Vector3& /*worldViewPosition*/)
{
}
const AZ::Transform& GetLocalTransform() const;
const AZ::Transform& GetSpace() const;
@@ -262,39 +305,59 @@ namespace AzToolsFramework
void SetNonUniformScale(const AZ::Vector3& nonUniformScale);
protected:
/// Common processing for base manipulator type - Implement for all
/// individual manipulators used in an aggregate manipulator.
//! Common processing for base manipulator type - Implement for all
//! individual manipulators used in an aggregate manipulator.
virtual void ProcessManipulators(const AZStd::function<void(BaseManipulator*)>&) = 0;
///@{
/// Allows implementers to perform additional logic when updating the location of the manipulator group.
virtual void SetSpaceImpl([[maybe_unused]] const AZ::Transform& worldFromLocal) {}
virtual void SetLocalTransformImpl([[maybe_unused]] const AZ::Transform& localTransform) {}
virtual void SetLocalPositionImpl([[maybe_unused]] const AZ::Vector3& localPosition) {}
virtual void SetLocalOrientationImpl([[maybe_unused]] const AZ::Quaternion& localOrientation) {}
virtual void SetNonUniformScaleImpl([[maybe_unused]] const AZ::Vector3& nonUniformScale) {}
///@}
//!@{
//! Allows implementers to perform additional logic when updating the location of the manipulator group.
virtual void SetSpaceImpl([[maybe_unused]] const AZ::Transform& worldFromLocal)
{
}
ManipulatorSpaceWithLocalTransform m_manipulatorSpaceWithLocalTransform; ///< The space and local transform for the manipulators.
virtual void SetLocalTransformImpl([[maybe_unused]] const AZ::Transform& localTransform)
{
}
virtual void SetLocalPositionImpl([[maybe_unused]] const AZ::Vector3& localPosition)
{
}
virtual void SetLocalOrientationImpl([[maybe_unused]] const AZ::Quaternion& localOrientation)
{
}
virtual void SetNonUniformScaleImpl([[maybe_unused]] const AZ::Vector3& nonUniformScale)
{
}
//!@}
ManipulatorSpaceWithLocalTransform m_manipulatorSpaceWithLocalTransform; //!< The space and local transform for the manipulators.
};
namespace Internal
{
/// This helper function calculates the intersecting point between a ray and a plane.
/// @param rayOrigin The origin of the ray to test.
/// @param rayDirection The direction of the ray to test.
/// @param maxRayLength
/// @param pointOnPlane A point on the plane.
/// @param planeNormal The normal vector of the plane.
/// @param[out] resultIntersectingPoint This stores the result intersecting point. It will be left unchanged
/// if there is no intersection between the ray and the plane.
/// @return Was there an intersection
bool CalculateRayPlaneIntersectingPoint(const AZ::Vector3& rayOrigin, const AZ::Vector3& rayDirection,
const AZ::Vector3& pointOnPlane, const AZ::Vector3& planeNormal, AZ::Vector3& resultIntersectingPoint);
//! This helper function calculates the intersecting point between a ray and a plane.
//! @param rayOrigin The origin of the ray to test.
//! @param rayDirection The direction of the ray to test.
//! @param maxRayLength The maximum length of the ray to test.
//! @param pointOnPlane A point on the plane.
//! @param planeNormal The normal vector of the plane.
//! @param[out] resultIntersectingPoint This stores the result intersecting point. It will be left unchanged
//! if there is no intersection between the ray and the plane.
//! @return Was there an intersection
bool CalculateRayPlaneIntersectingPoint(
const AZ::Vector3& rayOrigin,
const AZ::Vector3& rayDirection,
const AZ::Vector3& pointOnPlane,
const AZ::Vector3& planeNormal,
AZ::Vector3& resultIntersectingPoint);
/// Returns startLocalHitPosition if currentLocalHitPosition is further away than the camera's far clip plane.
//! Returns startLocalHitPosition if currentLocalHitPosition is further away than the camera's far clip plane.
AZ::Vector3 TryConstrainHitPositionToView(
const AZ::Vector3& currentLocalHitPosition, const AZ::Vector3& startLocalHitPosition,
const AZ::Transform& localFromWorld, const AzFramework::CameraState& cameraState);
}
const AZ::Vector3& currentLocalHitPosition,
const AZ::Vector3& startLocalHitPosition,
const AZ::Transform& localFromWorld,
const AzFramework::CameraState& cameraState);
} // namespace Internal
} // namespace AzToolsFramework
@@ -1,14 +1,14 @@
/*
* 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.
*
*/
* 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
@@ -21,31 +21,30 @@ namespace AZ
namespace AzToolsFramework
{
/// Interface for handling box manipulator requests.
/// Used by \ref BoxComponentMode.
class BoxManipulatorRequests
: public AZ::EntityComponentBus
//! Interface for handling box manipulator requests.
//! Used by \ref BoxComponentMode.
class BoxManipulatorRequests : public AZ::EntityComponentBus
{
public:
/// Get the X/Y/Z dimensions of the box shape/collider.
//! Get the X/Y/Z dimensions of the box shape/collider.
virtual AZ::Vector3 GetDimensions() = 0;
/// Set the X/Y/Z dimensions of the box shape/collider.
//! Set the X/Y/Z dimensions of the box shape/collider.
virtual void SetDimensions(const AZ::Vector3& dimensions) = 0;
/// Get the transform of the box shape/collider.
/// This is used by \ref BoxComponentMode instead of the \ref \AZ::TransformBus
/// because a collider may have an additional translation/orientation offset from
/// the Entity transform.
//! Get the transform of the box shape/collider.
//! This is used by \ref BoxComponentMode instead of the \ref \AZ::TransformBus
//! because a collider may have an additional translation/orientation offset from
//! the Entity transform.
virtual AZ::Transform GetCurrentTransform() = 0;
/// Get the scale currently applied to the box.
/// With the Box Shape, the largest x/y/z component is taken
/// so scale is always uniform, with colliders the scale may
/// be different per component.
//! Get the scale currently applied to the box.
//! With the Box Shape, the largest x/y/z component is taken
//! so scale is always uniform, with colliders the scale may
//! be different per component.
virtual AZ::Vector3 GetBoxScale() = 0;
protected:
~BoxManipulatorRequests() = default;
};
/// Type to inherit to implement BoxManipulatorRequests
//! Type to inherit to implement BoxManipulatorRequests
using BoxManipulatorRequestBus = AZ::EBus<BoxManipulatorRequests>;
} // namespace AzToolsFramework
@@ -1,22 +1,22 @@
/*
* 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.
*
*/
* 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/Math/VertexContainer.h>
#include <AzCore/Math/VertexContainerInterface.h>
#include <AzFramework/Entity/EntityDebugDisplayBus.h>
#include <AzToolsFramework/Manipulators/ManipulatorBus.h>
#include <AzToolsFramework/Manipulators/HoverSelection.h>
#include <AzToolsFramework/Manipulators/ManipulatorBus.h>
#include <AzToolsFramework/Manipulators/SelectionManipulator.h>
#include <AzToolsFramework/Manipulators/TranslationManipulators.h>
#include <AzToolsFramework/Viewport/ActionBus.h>
@@ -24,37 +24,74 @@
namespace AzToolsFramework
{
/// Concrete implementation of AZ::VariableVertices backed by an AZ::VertexContainer.
//! Concrete implementation of AZ::VariableVertices backed by an AZ::VertexContainer.
template<typename Vertex>
class VariableVerticesVertexContainer
: public AZ::VariableVertices<Vertex>
class VariableVerticesVertexContainer : public AZ::VariableVertices<Vertex>
{
public:
explicit VariableVerticesVertexContainer(AZ::VertexContainer<Vertex>& vertexContainer)
: m_vertexContainer(vertexContainer) {}
: m_vertexContainer(vertexContainer)
{
}
bool GetVertex(size_t index, Vertex& vertex) const override { return m_vertexContainer.GetVertex(index, vertex); }
bool UpdateVertex(size_t index, const Vertex& vertex) override { return m_vertexContainer.UpdateVertex(index, vertex); };
void AddVertex(const Vertex& vertex) override { m_vertexContainer.AddVertex(vertex); }
bool InsertVertex(size_t index, const Vertex& vertex) override { return m_vertexContainer.InsertVertex(index, vertex); }
bool RemoveVertex(size_t index) override { return m_vertexContainer.RemoveVertex(index); }
void SetVertices(const AZStd::vector<Vertex>& vertices) override { m_vertexContainer.SetVertices(vertices); };
void ClearVertices() override { m_vertexContainer.Clear(); }
size_t Size() const override { return m_vertexContainer.Size(); }
bool Empty() const override { return m_vertexContainer.Empty(); }
bool GetVertex(size_t index, Vertex& vertex) const override
{
return m_vertexContainer.GetVertex(index, vertex);
}
bool UpdateVertex(size_t index, const Vertex& vertex) override
{
return m_vertexContainer.UpdateVertex(index, vertex);
};
void AddVertex(const Vertex& vertex) override
{
m_vertexContainer.AddVertex(vertex);
}
bool InsertVertex(size_t index, const Vertex& vertex) override
{
return m_vertexContainer.InsertVertex(index, vertex);
}
bool RemoveVertex(size_t index) override
{
return m_vertexContainer.RemoveVertex(index);
}
void SetVertices(const AZStd::vector<Vertex>& vertices) override
{
m_vertexContainer.SetVertices(vertices);
};
void ClearVertices() override
{
m_vertexContainer.Clear();
}
size_t Size() const override
{
return m_vertexContainer.Size();
}
bool Empty() const override
{
return m_vertexContainer.Empty();
}
private:
AZ::VertexContainer<Vertex>& m_vertexContainer;
};
/// Concrete implementation of AZ::FixedVertices backed by an AZStd::array.
//! Concrete implementation of AZ::FixedVertices backed by an AZStd::array.
template<typename Vertex, size_t Count>
class FixedVerticesArray
: public AZ::FixedVertices<Vertex>
class FixedVerticesArray : public AZ::FixedVertices<Vertex>
{
public:
explicit FixedVerticesArray(AZStd::array<Vertex, Count>& array)
: m_array(array) {}
: m_array(array)
{
}
bool GetVertex(size_t index, Vertex& vertex) const override
{
@@ -72,22 +109,26 @@ namespace AzToolsFramework
if (index < m_array.size())
{
m_array[index] = vertex;
return true;;
return true;
;
}
return false;
}
size_t Size() const override { return m_array.size(); }
size_t Size() const override
{
return m_array.size();
}
private:
AZStd::array<Vertex, Count>& m_array;
};
/// EditorVertexSelection provides an interface for a collection of manipulators to expose
/// editing of vertices in a container/collection. EditorVertexSelection is templated on the
/// type of Vertex (Vector2/Vector3) stored in the container.
/// EditorVertexSelectionBase provides common behavior shared across Fixed and Variable selections.
//! EditorVertexSelection provides an interface for a collection of manipulators to expose
//! editing of vertices in a container/collection. EditorVertexSelection is templated on the
//! type of Vertex (Vector2/Vector3) stored in the container.
//! EditorVertexSelectionBase provides common behavior shared across Fixed and Variable selections.
template<typename Vertex>
class EditorVertexSelectionBase
: private AzFramework::EntityDebugDisplayEventBus::Handler
@@ -99,89 +140,110 @@ namespace AzToolsFramework
EditorVertexSelectionBase& operator=(EditorVertexSelectionBase&&) = default;
virtual ~EditorVertexSelectionBase() = default;
/// Setup and configure the EditorVertexSelection for operation.
//! Setup and configure the EditorVertexSelection for operation.
void Create(
const AZ::EntityComponentIdPair& entityComponentIdPair, ManipulatorManagerId managerId,
const AZ::EntityComponentIdPair& entityComponentIdPair,
ManipulatorManagerId managerId,
AZStd::unique_ptr<HoverSelection> hoverSelection,
TranslationManipulators::Dimensions dimensions,
TranslationManipulatorConfiguratorFn translationManipulatorConfigurator);
/// Create a translation manipulator for a given vertex.
//! Create a translation manipulator for a given vertex.
void CreateTranslationManipulator(
const AZ::EntityComponentIdPair& entityComponentIdPair,
ManipulatorManagerId managerId, const Vertex& vertex, size_t index);
const AZ::EntityComponentIdPair& entityComponentIdPair, ManipulatorManagerId managerId, const Vertex& vertex, size_t index);
/// Destroy all manipulators associated with the vertex selection.
//! Destroy all manipulators associated with the vertex selection.
void Destroy();
/// Set custom callback for when vertex positions are updated.
//! Set custom callback for when vertex positions are updated.
void SetVertexPositionsUpdatedCallback(const AZStd::function<void()>& callback);
/// Update manipulators based on local changes to vertex positions.
//! Update manipulators based on local changes to vertex positions.
void RefreshLocal();
/// Update the translation manipulator to be correctly positioned based
/// on the current selection (recenter it).
//! Update the translation manipulator to be correctly positioned based
//! on the current selection (recenter it).
void RefreshTranslationManipulator();
/// Update manipulators based on changes to the entity's transform and non-uniform scale.
//! Update manipulators based on changes to the entity's transform and non-uniform scale.
void RefreshSpace(const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale = AZ::Vector3::CreateOne());
/// Set bounds dirty (need recalculating) for all owned manipulators (selection, translation, hover).
//! Set bounds dirty (need recalculating) for all owned manipulators (selection, translation, hover).
void SetBoundsDirty();
/// How should the EditorVertexSelection respond to mouse input.
virtual bool HandleMouse(
const ViewportInteraction::MouseInteractionEvent& mouseInteraction);
//! How should the EditorVertexSelection respond to mouse input.
virtual bool HandleMouse(const ViewportInteraction::MouseInteractionEvent& mouseInteraction);
/// Snap the selected vertices to the terrain.
/// Note: With a multi-selection the manipulator will be translated to the picked
/// terrain position with all verts moved relative to it.
void SnapVerticesToTerrain(
const ViewportInteraction::MouseInteractionEvent& mouseInteraction);
//! Snap the selected vertices to the terrain.
//! Note: With a multi-selection the manipulator will be translated to the picked
//! terrain position with all vertices moved relative to it.
void SnapVerticesToTerrain(const ViewportInteraction::MouseInteractionEvent& mouseInteraction);
/// The Actions provided by the EditorVertexSelection while it is active.
/// e.g. Vertex deletion, duplication etc.
//! The Actions provided by the EditorVertexSelection while it is active.
//! e.g. Vertex deletion, duplication etc.
AZStd::vector<ActionOverride> ActionOverrides() const;
/// Let the EditorVertexSelection know a batch movement is about to begin so it
/// can avoid certain unnecessary updates.
//! Let the EditorVertexSelection know a batch movement is about to begin so it
//! can avoid certain unnecessary updates.
void BeginBatchMovement();
/// Let the EditorVertexSelection know a batch movement has ended so it can return
/// to its normal state.
//! Let the EditorVertexSelection know a batch movement has ended so it can return
//! to its normal state.
void EndBatchMovement();
/// Set the position of the TranslationManipulators (if active).
//! Set the position of the TranslationManipulators (if active).
void SetSelectedPosition(const AZ::Vector3& localPosition);
AZ::EntityId GetEntityId() const { return m_entityComponentIdPair.GetEntityId(); }
AZ::EntityId GetEntityId() const
{
return m_entityComponentIdPair.GetEntityId();
}
protected:
/// Internal interface for EditorVertexSelection.
//! Internal interface for EditorVertexSelection.
virtual void SetupSelectionManipulator(
const AZStd::shared_ptr<SelectionManipulator>& selectionManipulator,
const AZ::EntityComponentIdPair& entityComponentIdPair,
ManipulatorManagerId managerId, size_t index) = 0;
ManipulatorManagerId managerId,
size_t index) = 0;
virtual void PrepareActions() = 0;
/// Default behavior when clicking on a selection manipulator (representing a vertex).
//! Default behavior when clicking on a selection manipulator (representing a vertex).
void SelectionManipulatorSelectCallback(
size_t index, const ViewportInteraction::MouseInteraction& interaction,
const AZ::EntityComponentIdPair& entityComponentIdPair, ManipulatorManagerId managerId);
size_t index,
const ViewportInteraction::MouseInteraction& interaction,
const AZ::EntityComponentIdPair& entityComponentIdPair,
ManipulatorManagerId managerId);
/// Destroy the translation manipulator and deselect all vertices.
//! Destroy the translation manipulator and deselect all vertices.
void ClearSelected();
AZ::ComponentId GetComponentId() const { return m_entityComponentIdPair.GetComponentId(); }
const AZ::EntityComponentIdPair& GetEntityComponentIdPair() const { return m_entityComponentIdPair; }
ManipulatorManagerId GetManipulatorManagerId() const { return m_manipulatorManagerId; }
AZ::ComponentId GetComponentId() const
{
return m_entityComponentIdPair.GetComponentId();
}
/// Is the translation vertex manipulator in 2D or 3D.
TranslationManipulators::Dimensions Dimensions() const { return m_dimensions; }
const AZ::EntityComponentIdPair& GetEntityComponentIdPair() const
{
return m_entityComponentIdPair;
}
/// How to configure the translation manipulator (view and axes).
TranslationManipulatorConfiguratorFn ConfiguratorFn() const { return m_manipulatorConfiguratorFn; }
ManipulatorManagerId GetManipulatorManagerId() const
{
return m_manipulatorManagerId;
}
/// The state we are in when editing vertices.
//! Is the translation vertex manipulator in 2D or 3D.
TranslationManipulators::Dimensions Dimensions() const
{
return m_dimensions;
}
//! How to configure the translation manipulator (view and axes).
TranslationManipulatorConfiguratorFn ConfiguratorFn() const
{
return m_manipulatorConfiguratorFn;
}
//! The state we are in when editing vertices.
enum class State
{
Selecting,
@@ -190,23 +252,22 @@ namespace AzToolsFramework
void SetState(State state);
AZStd::unique_ptr<HoverSelection> m_hoverSelection = nullptr; ///< Interface to hover selection, representing bounds that can be selected.
AZStd::shared_ptr<IndexedTranslationManipulator<Vertex>> m_translationManipulator = nullptr; ///< Manipulator when vertex is selected to translate it.
AZStd::vector<AZStd::shared_ptr<SelectionManipulator>> m_selectionManipulators; ///< Manipulators for each vertex when entity is selected.
AZStd::array<AZStd::vector<ActionOverride>, 2> m_actionOverrides; ///< Available actions corresponding to each mode.
AZStd::unique_ptr<HoverSelection> m_hoverSelection =
nullptr; //!< Interface to hover selection, representing bounds that can be selected.
AZStd::shared_ptr<IndexedTranslationManipulator<Vertex>> m_translationManipulator =
nullptr; //!< Manipulator when vertex is selected to translate it.
AZStd::vector<AZStd::shared_ptr<SelectionManipulator>>
m_selectionManipulators; //!< Manipulators for each vertex when entity is selected.
AZStd::array<AZStd::vector<ActionOverride>, 2> m_actionOverrides; //!< Available actions corresponding to each mode.
private:
// AzFramework::EntityDebugDisplayEventBus
void DisplayEntityViewport(
const AzFramework::ViewportInfo& viewportInfo,
AzFramework::DebugDisplayRequests& debugDisplay) override;
void DisplayEntityViewport(const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) override;
// AzFramework::ViewportDebugDisplayEventBus
void DisplayViewport2d(
const AzFramework::ViewportInfo& viewportInfo,
AzFramework::DebugDisplayRequests& debugDisplay) override;
void DisplayViewport2d(const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) override;
/// Set selected manipulator and vertices position from offset from starting position when pressed.
//! Set selected manipulator and vertices position from offset from starting position when pressed.
void UpdateManipulatorsAndVerticesFromOffset(
IndexedTranslationManipulator<Vertex>& translationManipulator,
const AZ::Vector3& localManipulatorStartPosition,
@@ -217,23 +278,24 @@ namespace AzToolsFramework
template<typename V, typename AZStd::enable_if<AZStd::is_same<V, AZ::Vector2>::value>::type* = nullptr>
void UpdateManipulatorSpace(const AzFramework::ViewportInfo& viewportInfo) const;
EditorBoxSelect m_editorBoxSelect; ///< Provide box select support for vertex selection.
AZ::EntityComponentIdPair m_entityComponentIdPair; ///< Id of the Entity and Component this editor vertex selection was created on.
ManipulatorManagerId m_manipulatorManagerId; ///< Id of the manager manipulators created from this type will be associated with.
TranslationManipulators::Dimensions m_dimensions = TranslationManipulators::Dimensions::Three; ///< The dimensions this vertex selection was created with.
TranslationManipulatorConfiguratorFn m_manipulatorConfiguratorFn = nullptr; ///< Function pointer set on Create to decide look and functionality of translation manipulator.
AZStd::function<void()> m_onVertexPositionsUpdated = nullptr; ///< Callback for when vertex positions are changed.
State m_state = State::Selecting; ///< Different states VertexSelection can be in.
bool m_worldSpace = false; ///< Are the manipulators being used in local or world space.
bool m_batchMovementInProgress = false; ///< If a batch movement operation is in progress we do not want to
///< refresh the VertexSelection during it for performance reasons.
EditorBoxSelect m_editorBoxSelect; //!< Provide box select support for vertex selection.
AZ::EntityComponentIdPair m_entityComponentIdPair; //!< Id of the Entity and Component this editor vertex selection was created on.
ManipulatorManagerId m_manipulatorManagerId; //!< Id of the manager manipulators created from this type will be associated with.
TranslationManipulators::Dimensions m_dimensions =
TranslationManipulators::Dimensions::Three; //!< The dimensions this vertex selection was created with.
TranslationManipulatorConfiguratorFn m_manipulatorConfiguratorFn =
nullptr; //!< Function pointer set on Create to decide look and functionality of translation manipulator.
AZStd::function<void()> m_onVertexPositionsUpdated = nullptr; //!< Callback for when vertex positions are changed.
State m_state = State::Selecting; //!< Different states VertexSelection can be in.
bool m_worldSpace = false; //!< Are the manipulators being used in local or world space.
bool m_batchMovementInProgress = false; //!< If a batch movement operation is in progress we do not want to
//!< refresh the VertexSelection during it for performance reasons.
};
/// EditorVertexSelectionFixed provides selection and editing for a fixed length number of
/// vertices. New vertices cannot be inserted/added or removed.
//! EditorVertexSelectionFixed provides selection and editing for a fixed length number of
//! vertices. New vertices cannot be inserted/added or removed.
template<typename Vertex>
class EditorVertexSelectionFixed
: public EditorVertexSelectionBase<Vertex>
class EditorVertexSelectionFixed : public EditorVertexSelectionBase<Vertex>
{
public:
AZ_CLASS_ALLOCATOR_DECL
@@ -247,15 +309,15 @@ namespace AzToolsFramework
void SetupSelectionManipulator(
const AZStd::shared_ptr<SelectionManipulator>& selectionManipulator,
const AZ::EntityComponentIdPair& entityComponentIdPair,
ManipulatorManagerId managerId, size_t index) override;
ManipulatorManagerId managerId,
size_t index) override;
void PrepareActions() override;
};
/// EditorVertexSelectionVariable provides selection and editing for a variable length number of
/// vertices. New vertices can be inserted/added or removed from the collection.
//! EditorVertexSelectionVariable provides selection and editing for a variable length number of
//! vertices. New vertices can be inserted/added or removed from the collection.
template<typename Vertex>
class EditorVertexSelectionVariable
: public EditorVertexSelectionBase<Vertex>
class EditorVertexSelectionVariable : public EditorVertexSelectionBase<Vertex>
{
public:
AZ_CLASS_ALLOCATOR_DECL
@@ -272,7 +334,8 @@ namespace AzToolsFramework
void SetupSelectionManipulator(
const AZStd::shared_ptr<SelectionManipulator>& selectionManipulator,
const AZ::EntityComponentIdPair& entityComponentIdPair,
ManipulatorManagerId managerId, size_t vertIndex) override;
ManipulatorManagerId managerId,
size_t vertIndex) override;
//! Presents a warning to the user that vertices will not be deleted.
//! @note Allow overriding by derived classes to make this a noop if required.
@@ -281,21 +344,18 @@ namespace AzToolsFramework
private:
void PrepareActions() override;
/// @return The center point of the selected vertices.
Vertex InsertSelectedInPlace(
AZStd::vector<typename IndexedTranslationManipulator<Vertex>::VertexLookup>& manipulators);
//! @return The center point of the selected vertices.
Vertex InsertSelectedInPlace(AZStd::vector<typename IndexedTranslationManipulator<Vertex>::VertexLookup>& manipulators);
};
/// Helper for inserting a vertex in a variable vertices container.
//! Helper for inserting a vertex in a variable vertices container.
template<typename Vertex>
void InsertVertexAfter(
const AZ::EntityComponentIdPair& entityComponentIdPair, size_t vertIndex, const Vertex& localPosition);
void InsertVertexAfter(const AZ::EntityComponentIdPair& entityComponentIdPair, size_t vertIndex, const Vertex& localPosition);
/// Helper for removing a vertex in a variable vertices container.
/// Remove a vertex from the container and ensure the associated manipulator is unset and
/// property display values are refreshed.
//! Helper for removing a vertex in a variable vertices container.
//! Remove a vertex from the container and ensure the associated manipulator is unset and
//! property display values are refreshed.
template<typename Vertex>
void SafeRemoveVertex(
const AZ::EntityComponentIdPair& entityComponentIdPair, size_t vertexIndex);
void SafeRemoveVertex(const AZ::EntityComponentIdPair& entityComponentIdPair, size_t vertexIndex);
} // namespace AzToolsFramework
@@ -1,14 +1,14 @@
/*
* 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.
*
*/
* 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
@@ -16,10 +16,10 @@
namespace AzToolsFramework
{
/// HoverSelection provides an interface for manipulator/s offering selection when
/// the mouse is hovered over a particular bound. This interface is used to represent
/// a Spline manipulator bound, and a series of LineSegment manipulator bounds.
/// This generic interface allows EditorVertexSelection to use either Spline or LineSegment selection.
//! HoverSelection provides an interface for manipulator/s offering selection when
//! the mouse is hovered over a particular bound. This interface is used to represent
//! a Spline manipulator bound, and a series of LineSegment manipulator bounds.
//! This generic interface allows EditorVertexSelection to use either Spline or LineSegment selection.
class HoverSelection
{
public:
@@ -33,21 +33,37 @@ namespace AzToolsFramework
virtual void SetNonUniformScale(const AZ::Vector3& nonUniformScale) = 0;
};
/// NullHoverSelection is used when vertices cannot be inserted. This serves as a no-op
/// and is used to prevent the need for additional null checks in EditorVertexSelection.
class NullHoverSelection
: public HoverSelection
//! NullHoverSelection is used when vertices cannot be inserted. This serves as a no-op
//! and is used to prevent the need for additional null checks in EditorVertexSelection.
class NullHoverSelection : public HoverSelection
{
public:
NullHoverSelection() = default;
NullHoverSelection(const NullHoverSelection&) = delete;
NullHoverSelection& operator=(const NullHoverSelection&) = delete;
void Register(ManipulatorManagerId /*managerId*/) override {}
void Unregister() override {}
void SetBoundsDirty() override {}
void Refresh() override {}
void SetSpace(const AZ::Transform& /*worldFromLocal*/) override {}
void SetNonUniformScale([[maybe_unused]] const AZ::Vector3& nonUniformScale) override {}
void Register([[maybe_unused]] ManipulatorManagerId managerId) override
{
}
void Unregister() override
{
}
void SetBoundsDirty() override
{
}
void Refresh() override
{
}
void SetSpace([[maybe_unused]] const AZ::Transform& worldFromLocal) override
{
}
void SetNonUniformScale([[maybe_unused]] const AZ::Vector3& nonUniformScale) override
{
}
};
} // namespace AzToolsFramework
@@ -1,14 +1,14 @@
/*
* 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.
*
*/
* 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.
*
*/
#include "LineHoverSelection.h"
@@ -22,17 +22,15 @@
namespace AzToolsFramework
{
static const AZ::Color s_lineSelectManipulatorColor = AZ::Color(0.0f, 1.0f, 0.0f, 1.0f);
static const AZ::Color LineSelectManipulatorColor = AZ::Color(0.0f, 1.0f, 0.0f, 1.0f);
template<typename Vertex>
static void UpdateLineSegmentPosition(
const size_t vertIndex, const AZ::EntityId entityId, LineSegmentSelectionManipulator& lineSegment)
static void UpdateLineSegmentPosition(const size_t vertIndex, const AZ::EntityId entityId, LineSegmentSelectionManipulator& lineSegment)
{
Vertex start;
bool foundStart = false;
AZ::FixedVerticesRequestBus<Vertex>::EventResult(
foundStart, entityId, &AZ::FixedVerticesRequestBus<Vertex>::Handler::GetVertex,
vertIndex, start);
foundStart, entityId, &AZ::FixedVerticesRequestBus<Vertex>::Handler::GetVertex, vertIndex, start);
if (foundStart)
{
@@ -40,14 +38,12 @@ namespace AzToolsFramework
}
size_t size = 0;
AZ::FixedVerticesRequestBus<Vertex>::EventResult(
size, entityId, &AZ::FixedVerticesRequestBus<Vertex>::Handler::Size);
AZ::FixedVerticesRequestBus<Vertex>::EventResult(size, entityId, &AZ::FixedVerticesRequestBus<Vertex>::Handler::Size);
Vertex end;
bool foundEnd = false;
AZ::FixedVerticesRequestBus<Vertex>::EventResult(
foundEnd, entityId, &AZ::FixedVerticesRequestBus<Vertex>::Handler::GetVertex,
(vertIndex + 1) % size, end);
foundEnd, entityId, &AZ::FixedVerticesRequestBus<Vertex>::Handler::GetVertex, (vertIndex + 1) % size, end);
if (foundEnd)
{
@@ -56,8 +52,7 @@ namespace AzToolsFramework
// update the view
const float lineWidth = 0.05f;
lineSegment.SetView(
CreateManipulatorViewLineSelect(lineSegment, s_lineSelectManipulatorColor, lineWidth));
lineSegment.SetView(CreateManipulatorViewLineSelect(lineSegment, LineSelectManipulatorColor, lineWidth));
}
template<typename Vertex>
@@ -66,9 +61,8 @@ namespace AzToolsFramework
: m_entityId(entityComponentIdPair.GetEntityId())
{
// create a line segment manipulator from vertex positions and setup its callback
auto setupLineSegment = [this] (
const AZ::EntityComponentIdPair& entityComponentIdPair,
const ManipulatorManagerId managerId, const size_t vertIndex)
auto setupLineSegment =
[this](const AZ::EntityComponentIdPair& entityComponentIdPair, const ManipulatorManagerId managerId, const size_t vertIndex)
{
m_lineSegmentManipulators.push_back(LineSegmentSelectionManipulator::MakeShared());
AZStd::shared_ptr<LineSegmentSelectionManipulator>& lineSegmentManipulator = m_lineSegmentManipulators.back();
@@ -81,11 +75,9 @@ namespace AzToolsFramework
lineSegmentManipulator->InstallLeftMouseUpCallback(
[vertIndex, entityComponentIdPair](const LineSegmentSelectionManipulator::Action& action)
{
InsertVertexAfter<Vertex>(
entityComponentIdPair, vertIndex,
AZ::AdaptVertexIn<Vertex>(action.m_localLineHitPosition));
});
{
InsertVertexAfter<Vertex>(entityComponentIdPair, vertIndex, AZ::AdaptVertexIn<Vertex>(action.m_localLineHitPosition));
});
};
// create all line segment manipulators for the polygon prism (used for selection bounds)
@@ -150,8 +142,7 @@ namespace AzToolsFramework
void LineSegmentHoverSelection<Vertex>::Refresh()
{
size_t vertexCount = 0;
AZ::FixedVerticesRequestBus<Vertex>::EventResult(
vertexCount, m_entityId, &AZ::FixedVerticesRequestBus<Vertex>::Handler::Size);
AZ::FixedVerticesRequestBus<Vertex>::EventResult(vertexCount, m_entityId, &AZ::FixedVerticesRequestBus<Vertex>::Handler::Size);
// update the start/end positions of all the line segment manipulators to ensure
// they stay consistent with the polygon prism shape
@@ -1,14 +1,14 @@
/*
* 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.
*
*/
* 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
@@ -25,17 +25,14 @@ namespace AzToolsFramework
{
class LineSegmentSelectionManipulator;
/// LineSegmentHoverSelection is a concrete implementation of HoverSelection wrapping a collection/container
/// of vertices and a list of LineSegmentManipulators. The underlying manipulators are used to control selection
/// by highlighting where on the line a new vertex will be inserted.
//! LineSegmentHoverSelection is a concrete implementation of HoverSelection wrapping a collection/container
//! of vertices and a list of LineSegmentManipulators. The underlying manipulators are used to control selection
//! by highlighting where on the line a new vertex will be inserted.
template<typename Vertex>
class LineSegmentHoverSelection
: public HoverSelection
class LineSegmentHoverSelection : public HoverSelection
{
public:
explicit LineSegmentHoverSelection(
const AZ::EntityComponentIdPair& entityComponentIdPair,
ManipulatorManagerId managerId);
explicit LineSegmentHoverSelection(const AZ::EntityComponentIdPair& entityComponentIdPair, ManipulatorManagerId managerId);
LineSegmentHoverSelection(const LineSegmentHoverSelection&) = delete;
LineSegmentHoverSelection& operator=(const LineSegmentHoverSelection&) = delete;
~LineSegmentHoverSelection();
@@ -49,6 +46,6 @@ namespace AzToolsFramework
private:
AZ::EntityId m_entityId;
AZStd::vector<AZStd::shared_ptr<LineSegmentSelectionManipulator>> m_lineSegmentManipulators; ///< Manipulators for each line.
AZStd::vector<AZStd::shared_ptr<LineSegmentSelectionManipulator>> m_lineSegmentManipulators; //!< Manipulators for each line.
};
} // namespace AzToolsFramework
@@ -1,14 +1,14 @@
/*
* 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.
*
*/
* 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.
*
*/
#include "LineSegmentSelectionManipulator.h"
@@ -20,15 +20,20 @@
namespace AzToolsFramework
{
LineSegmentSelectionManipulator::Action CalculateManipulationDataAction(
const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale, const AZ::Vector3& rayOrigin,
const AZ::Vector3& rayDirection, const float rayLength, const AZ::Vector3& localStart, const AZ::Vector3& localEnd)
const AZ::Transform& worldFromLocal,
const AZ::Vector3& nonUniformScale,
const AZ::Vector3& rayOrigin,
const AZ::Vector3& rayDirection,
const float rayLength,
const AZ::Vector3& localStart,
const AZ::Vector3& localEnd)
{
AZ::Vector3 worldClosestPositionRay, worldClosestPositionLineSegment;
float rayProportion, lineSegmentProportion;
AZ::Intersect::ClosestSegmentSegment(
rayOrigin, rayOrigin + rayDirection * rayLength,
worldFromLocal.TransformPoint(nonUniformScale * localStart), worldFromLocal.TransformPoint(nonUniformScale * localEnd),
rayProportion, lineSegmentProportion, worldClosestPositionRay, worldClosestPositionLineSegment);
rayOrigin, rayOrigin + rayDirection * rayLength, worldFromLocal.TransformPoint(nonUniformScale * localStart),
worldFromLocal.TransformPoint(nonUniformScale * localEnd), rayProportion, lineSegmentProportion, worldClosestPositionRay,
worldClosestPositionLineSegment);
AZ::Transform worldFromLocalNormalized = worldFromLocal;
const AZ::Vector3 scale = worldFromLocalNormalized.ExtractUniformScale() * nonUniformScale;
@@ -47,7 +52,9 @@ namespace AzToolsFramework
AttachLeftMouseDownImpl();
}
LineSegmentSelectionManipulator::~LineSegmentSelectionManipulator() {}
LineSegmentSelectionManipulator::~LineSegmentSelectionManipulator()
{
}
void LineSegmentSelectionManipulator::InstallLeftMouseDownCallback(const MouseActionCallback& onMouseDownCallback)
{
@@ -112,12 +119,9 @@ namespace AzToolsFramework
if (mouseInteraction.m_keyboardModifiers.Ctrl() && !mouseInteraction.m_keyboardModifiers.Shift())
{
m_manipulatorView->Draw(
GetManipulatorManagerId(), managerState,
GetManipulatorId(), {
TransformUniformScale(GetSpace()), GetNonUniformScale(),
m_localStart, MouseOver()
},
debugDisplay, cameraState, mouseInteraction);
GetManipulatorManagerId(), managerState, GetManipulatorId(),
{ TransformUniformScale(GetSpace()), GetNonUniformScale(), m_localStart, MouseOver() }, debugDisplay, cameraState,
mouseInteraction);
}
}
@@ -135,4 +139,4 @@ namespace AzToolsFramework
{
m_manipulatorView->Invalidate(GetManipulatorManagerId());
}
}
} // namespace AzToolsFramework

Some files were not shown because too many files have changed in this diff Show More