Merge branch 'main' into LYN-1767-AB
This commit is contained in:
@@ -97,16 +97,19 @@ class Cdk:
|
||||
env=self._cdk_env,
|
||||
shell=True)
|
||||
|
||||
def deploy(self, context_variable: str = '') -> List[str]:
|
||||
def deploy(self, context_variable: str = '', additonal_params: List[str] = None) -> List[str]:
|
||||
"""
|
||||
Deploys all the CDK stacks.
|
||||
:param context_variable: Context variable for enabling optional features.
|
||||
:param additonal_params: Additonal parameters like --all can be passed in this way.
|
||||
:return List of deployed stack arns.
|
||||
"""
|
||||
if not self._cdk_path:
|
||||
return []
|
||||
|
||||
deploy_cdk_application_cmd = ['cdk', 'deploy', '--require-approval', 'never']
|
||||
if additonal_params:
|
||||
deploy_cdk_application_cmd.extend(additonal_params)
|
||||
if context_variable:
|
||||
deploy_cdk_application_cmd.extend(['-c', f'{context_variable}'])
|
||||
|
||||
|
||||
@@ -9,11 +9,6 @@ remove or modify any license notices. This file is distributed on an "AS IS" BAS
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
"""
|
||||
C24064528: The File menu options function normally
|
||||
C16780778: The File menu options function normally-New view interaction Model enabled
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
@@ -54,7 +49,10 @@ class TestFileMenuOptions(EditorTestHelper):
|
||||
("Save",),
|
||||
("Save As",),
|
||||
("Save Level Statistics",),
|
||||
("Project Settings", "Project Settings Tool"),
|
||||
("Edit Project Settings",),
|
||||
("Edit Platform Settings",),
|
||||
("New Project",),
|
||||
("Open Project",),
|
||||
("Show Log File",),
|
||||
("Resave All Slices",),
|
||||
("Exit",),
|
||||
|
||||
@@ -15,6 +15,7 @@ import pytest
|
||||
# Bail on the test if ly_test_tools doesn't exist.
|
||||
pytest.importorskip('ly_test_tools')
|
||||
import ly_test_tools.environment.file_system as file_system
|
||||
import ly_test_tools._internal.pytest_plugin as internal_plugin
|
||||
import editor_python_test_tools.hydra_test_utils as hydra
|
||||
|
||||
test_directory = os.path.join(os.path.dirname(__file__), "EditorScripts")
|
||||
@@ -40,6 +41,10 @@ class TestBasicEditorWorkflows(object):
|
||||
@pytest.mark.SUITE_main
|
||||
def test_BasicEditorWorkflows_LevelEntityComponentCRUD(self, request, editor, level, launcher_platform):
|
||||
|
||||
# Skip test if running against Debug build
|
||||
if "debug" in internal_plugin.build_directory:
|
||||
pytest.skip("Does not execute against debug builds.")
|
||||
|
||||
expected_lines = [
|
||||
"Create and load new level: True",
|
||||
"New entity creation: True",
|
||||
|
||||
@@ -7,8 +7,6 @@ distribution (the "License"). All use of this software is governed by the Licens
|
||||
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.
|
||||
|
||||
C16780783: Base Edit Menu Options (New Viewport Interaction Model)
|
||||
"""
|
||||
|
||||
import os
|
||||
@@ -17,6 +15,7 @@ import pytest
|
||||
# Bail on the test if ly_test_tools doesn't exist.
|
||||
pytest.importorskip('ly_test_tools')
|
||||
import ly_test_tools.environment.file_system as file_system
|
||||
import ly_test_tools.environment.process_utils as process_utils
|
||||
import editor_python_test_tools.hydra_test_utils as hydra
|
||||
|
||||
test_directory = os.path.join(os.path.dirname(__file__), "EditorScripts")
|
||||
@@ -33,6 +32,7 @@ class TestMenus(object):
|
||||
def setup_teardown(self, request, workspace, project, level):
|
||||
def teardown():
|
||||
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
|
||||
process_utils.kill_processes_named("o3de", ignore_extensions=True) # Kill ProjectManager windows
|
||||
|
||||
request.addfinalizer(teardown)
|
||||
|
||||
@@ -80,8 +80,7 @@ class TestMenus(object):
|
||||
expected_lines,
|
||||
cfg_args=[level],
|
||||
run_python="--runpython",
|
||||
auto_test_mode=True,
|
||||
timeout=log_monitor_timeout,
|
||||
timeout=log_monitor_timeout
|
||||
)
|
||||
|
||||
@pytest.mark.test_case_id("C16780807")
|
||||
@@ -107,13 +106,13 @@ class TestMenus(object):
|
||||
"Menus_ViewMenuOptions.py",
|
||||
expected_lines,
|
||||
cfg_args=[level],
|
||||
auto_test_mode=True,
|
||||
run_python="--runpython",
|
||||
timeout=log_monitor_timeout,
|
||||
timeout=log_monitor_timeout
|
||||
)
|
||||
|
||||
@pytest.mark.test_case_id("C16780778")
|
||||
@pytest.mark.SUITE_sandbox
|
||||
@pytest.mark.xfail # LYN-4208
|
||||
def test_Menus_FileMenuOptions_Work(self, request, editor, level, launcher_platform):
|
||||
expected_lines = [
|
||||
"New Level Action triggered",
|
||||
@@ -122,7 +121,10 @@ class TestMenus(object):
|
||||
"Save Action triggered",
|
||||
"Save As Action triggered",
|
||||
"Save Level Statistics Action triggered",
|
||||
"Project Settings Tool Action triggered",
|
||||
"Edit Project Settings Action triggered",
|
||||
"Edit Platform Settings Action triggered",
|
||||
"New Project Action triggered",
|
||||
"Open Project Action triggered",
|
||||
"Show Log File Action triggered",
|
||||
"Resave All Slices Action triggered",
|
||||
"Exit Action triggered",
|
||||
@@ -135,7 +137,6 @@ class TestMenus(object):
|
||||
"Menus_FileMenuOptions.py",
|
||||
expected_lines,
|
||||
cfg_args=[level],
|
||||
auto_test_mode=True,
|
||||
run_python="--runpython",
|
||||
timeout=log_monitor_timeout,
|
||||
)
|
||||
timeout=log_monitor_timeout
|
||||
)
|
||||
|
||||
+6
@@ -16,6 +16,7 @@ import logging
|
||||
# Bail on the test if ly_test_tools doesn't exist.
|
||||
pytest.importorskip('ly_test_tools')
|
||||
import ly_test_tools.environment.file_system as file_system
|
||||
import ly_test_tools._internal.pytest_plugin as internal_plugin
|
||||
import editor_python_test_tools.hydra_test_utils as hydra
|
||||
from ly_remote_console.remote_console_commands import RemoteConsole as RemoteConsole
|
||||
|
||||
@@ -46,6 +47,11 @@ class TestDynamicSliceInstanceSpawner(object):
|
||||
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
|
||||
def test_DynamicSliceInstanceSpawner_DynamicSliceSpawnerWorks(self, request, editor, level, workspace, project,
|
||||
launcher_platform):
|
||||
|
||||
# Skip test if running against Debug build
|
||||
if "debug" in internal_plugin.build_directory:
|
||||
pytest.skip("Does not execute against debug builds.")
|
||||
|
||||
# Ensure temp level does not already exist
|
||||
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ import logging
|
||||
# Bail on the test if ly_test_tools doesn't exist.
|
||||
pytest.importorskip('ly_test_tools')
|
||||
import ly_test_tools.environment.file_system as file_system
|
||||
import ly_test_tools._internal.pytest_plugin as internal_plugin
|
||||
import editor_python_test_tools.hydra_test_utils as hydra
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -40,6 +41,11 @@ class TestEmptyInstanceSpawner(object):
|
||||
@pytest.mark.SUITE_main
|
||||
@pytest.mark.dynveg_area
|
||||
def test_EmptyInstanceSpawner_EmptySpawnerWorks(self, request, editor, level, launcher_platform):
|
||||
|
||||
# Skip test if running against Debug build
|
||||
if "debug" in internal_plugin.build_directory:
|
||||
pytest.skip("Does not execute against debug builds.")
|
||||
|
||||
cfg_args = [level]
|
||||
|
||||
expected_lines = [
|
||||
|
||||
+11
@@ -23,6 +23,7 @@ import pytest
|
||||
# Bail on the test if ly_test_tools doesn't exist.
|
||||
pytest.importorskip('ly_test_tools')
|
||||
import ly_test_tools.environment.file_system as file_system
|
||||
import ly_test_tools._internal.pytest_plugin as internal_plugin
|
||||
import editor_python_test_tools.hydra_test_utils as hydra
|
||||
|
||||
test_directory = os.path.join(os.path.dirname(__file__), 'EditorScripts')
|
||||
@@ -46,6 +47,11 @@ class TestGraphComponentSync(object):
|
||||
@pytest.mark.BAT
|
||||
@pytest.mark.SUITE_main
|
||||
def test_LandscapeCanvas_SlotConnections_UpdateComponentReferences(self, request, editor, level, launcher_platform):
|
||||
|
||||
# Skip test if running against Debug build
|
||||
if "debug" in internal_plugin.build_directory:
|
||||
pytest.skip("Does not execute against debug builds.")
|
||||
|
||||
cfg_args = [level]
|
||||
|
||||
expected_lines = [
|
||||
@@ -122,6 +128,11 @@ class TestGraphComponentSync(object):
|
||||
"""
|
||||
Verifies a Gradient Mixer can be setup in Landscape Canvas and all references are property set.
|
||||
"""
|
||||
|
||||
# Skip test if running against Debug build
|
||||
if "debug" in internal_plugin.build_directory:
|
||||
pytest.skip("Does not execute against debug builds.")
|
||||
|
||||
cfg_args = [level]
|
||||
|
||||
expected_lines = [
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
|
||||
#include <AzFramework/IO/FileOperations.h>
|
||||
#include <AzCore/IO/FileIO.h>
|
||||
#include <AzCore/IO/Path/Path.h>
|
||||
|
||||
#ifdef WIN32
|
||||
#include <time.h>
|
||||
@@ -88,7 +89,6 @@ CLog::CLog(ISystem* pSystem)
|
||||
|
||||
m_nMainThreadId = CryGetCurrentThreadId();
|
||||
|
||||
m_logFileHandle = AZ::IO::InvalidHandle;
|
||||
#if defined(KEEP_LOG_FILE_OPEN)
|
||||
m_bFirstLine = true;
|
||||
#endif
|
||||
@@ -162,35 +162,6 @@ void CLog::RegisterConsoleVariables()
|
||||
REGISTER_COMMAND("log_flush", &LogFlushFile, 0, "Flush the log file");
|
||||
#endif
|
||||
}
|
||||
/*
|
||||
//testbed
|
||||
{
|
||||
int iSave0 = m_pLogVerbosity->GetIVal();
|
||||
int iSave1 = m_pLogFileVerbosity->GetIVal();
|
||||
|
||||
for(int i=0;i<=4;++i)
|
||||
{
|
||||
m_pLogVerbosity->Set(i);
|
||||
m_pLogFileVerbosity->Set(i);
|
||||
|
||||
LogWithType(eAlways,"CLog selftest: Verbosity=%d FileVerbosity=%d",m_pLogVerbosity->GetIVal(),m_pLogFileVerbosity->GetIVal());
|
||||
LogWithType(eAlways,"--------------");
|
||||
|
||||
LogWithType(eError,"eError");
|
||||
LogWithType(eWarning,"eWarning");
|
||||
LogWithType(eMessage,"eMessage");
|
||||
LogWithType(eInput,"eInput");
|
||||
LogWithType(eInputResponse,"eInputResponse");
|
||||
|
||||
LogWarning("LogWarning()");
|
||||
LogError("LogError()");
|
||||
LogWithType(eAlways,"--------------");
|
||||
}
|
||||
|
||||
m_pLogVerbosity->Set(iSave0);
|
||||
m_pLogFileVerbosity->Set(iSave1);
|
||||
}
|
||||
*/
|
||||
#undef DEFAULT_VERBOSITY
|
||||
}
|
||||
|
||||
@@ -210,7 +181,7 @@ CLog::~CLog()
|
||||
|
||||
UnregisterConsoleVariables();
|
||||
|
||||
CloseLogFile(true);
|
||||
CloseLogFile();
|
||||
}
|
||||
|
||||
void CLog::UnregisterConsoleVariables()
|
||||
@@ -224,31 +195,36 @@ void CLog::UnregisterConsoleVariables()
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CLog::CloseLogFile([[maybe_unused]] bool forceClose)
|
||||
void CLog::CloseLogFile()
|
||||
{
|
||||
if (m_logFileHandle != AZ::IO::InvalidHandle)
|
||||
{
|
||||
AZ::IO::FileIOBase::GetDirectInstance()->Close(m_logFileHandle);
|
||||
m_logFileHandle = AZ::IO::InvalidHandle;
|
||||
}
|
||||
m_logFileHandle.Close();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
AZ::IO::HandleType CLog::OpenLogFile(const char* filename, const char* mode)
|
||||
bool CLog::OpenLogFile(const char* filename, int mode)
|
||||
{
|
||||
using namespace AZ::IO;
|
||||
|
||||
AZ_Assert(m_logFileHandle == AZ::IO::InvalidHandle, "Attempt to open log file when one is already open. This would lead to a handle leak.");
|
||||
|
||||
if ((!filename) || (filename[0] == 0))
|
||||
if (m_logFileHandle.IsOpen())
|
||||
{
|
||||
return m_logFileHandle;
|
||||
// Can only AZ_Assert if a file is open, otherwise the AZ_Assert
|
||||
// would eventually lead to OpenLogFile being opened up again
|
||||
AZ_Assert(false, "Attempt to open log file when one is already open. This would lead to a handle leak.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (filename == nullptr || filename[0] == '\0')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// it is assumed that @log@ points at the appropriate place (so for apple, to the user profile dir)
|
||||
AZ::IO::FileIOBase::GetDirectInstance()->Open(filename, AZ::IO::GetOpenModeFromStringMode(mode), m_logFileHandle);
|
||||
AZ::IO::FileIOBase* fileSystem = AZ::IO::FileIOBase::GetDirectInstance();
|
||||
if (AZ::IO::FixedMaxPath logFilePath; fileSystem->ReplaceAlias(logFilePath, filename))
|
||||
{
|
||||
logFilePath = logFilePath.LexicallyNormal();
|
||||
m_logFileHandle.Open(logFilePath.c_str(), mode);
|
||||
}
|
||||
|
||||
if (m_logFileHandle != AZ::IO::InvalidHandle)
|
||||
if (m_logFileHandle.IsOpen())
|
||||
{
|
||||
#if defined(KEEP_LOG_FILE_OPEN)
|
||||
m_bFirstLine = true;
|
||||
@@ -257,11 +233,11 @@ AZ::IO::HandleType CLog::OpenLogFile(const char* filename, const char* mode)
|
||||
else
|
||||
{
|
||||
#if defined(LINUX) || defined(APPLE)
|
||||
syslog(LOG_NOTICE, "Failed to open log file [%s], mode [%s]", filename, mode);
|
||||
syslog(LOG_NOTICE, "Failed to open log file [%s], mode [%d]", filename, mode);
|
||||
#endif
|
||||
}
|
||||
|
||||
return m_logFileHandle;
|
||||
return m_logFileHandle.IsOpen();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@@ -1114,12 +1090,15 @@ void CLog::LogStringToFile(const char* szString, ELogType logType, bool bAdd, [[
|
||||
|
||||
if (logToFile)
|
||||
{
|
||||
if (m_logFileHandle == AZ::IO::InvalidHandle)
|
||||
if (!m_logFileHandle.IsOpen())
|
||||
{
|
||||
OpenLogFile(m_szFilename, "w+t");
|
||||
constexpr auto openMode = AZ::IO::SystemFile::OpenMode::SF_OPEN_APPEND
|
||||
| AZ::IO::SystemFile::OpenMode::SF_OPEN_CREATE
|
||||
| AZ::IO::SystemFile::OpenMode::SF_OPEN_WRITE_ONLY;
|
||||
OpenLogFile(m_szFilename, openMode);
|
||||
}
|
||||
|
||||
if (m_logFileHandle != AZ::IO::InvalidHandle)
|
||||
if (m_logFileHandle.IsOpen())
|
||||
{
|
||||
#if defined(KEEP_LOG_FILE_OPEN)
|
||||
if (m_bFirstLine)
|
||||
@@ -1130,9 +1109,9 @@ void CLog::LogStringToFile(const char* szString, ELogType logType, bool bAdd, [[
|
||||
if (bAdd)
|
||||
{
|
||||
// if adding to a prior line erase the \n at the end.
|
||||
AZ::IO::FileIOBase::GetDirectInstance()->Seek(m_logFileHandle, -2, AZ::IO::SeekType::SeekFromEnd);
|
||||
m_logFileHandle.Seek(-2, AZ::IO::SystemFile::SeekMode::SF_SEEK_END);
|
||||
}
|
||||
AZ::IO::FPutS(tempString.c_str(), m_logFileHandle);
|
||||
m_logFileHandle.Write(tempString.c_str(), tempString.size());
|
||||
#if !defined(KEEP_LOG_FILE_OPEN)
|
||||
CloseLogFile();
|
||||
#endif
|
||||
@@ -1383,6 +1362,23 @@ bool CLog::SetFileName(const char* fileNameOrAbsolutePath, bool backupLogs)
|
||||
|
||||
CreateBackupFile();
|
||||
|
||||
AZ::IO::FileIOBase* fileSystem = AZ::IO::FileIOBase::GetDirectInstance();
|
||||
AZ::IO::FixedMaxPath newLogFilePath;
|
||||
if (fileSystem->ReplaceAlias(newLogFilePath, m_szFilename))
|
||||
{
|
||||
newLogFilePath = newLogFilePath.LexicallyNormal();
|
||||
}
|
||||
if (m_logFileHandle.IsOpen() && newLogFilePath != m_logFileHandle.Name())
|
||||
{
|
||||
constexpr auto openMode = AZ::IO::SystemFile::OpenMode::SF_OPEN_APPEND
|
||||
| AZ::IO::SystemFile::OpenMode::SF_OPEN_CREATE
|
||||
| AZ::IO::SystemFile::OpenMode::SF_OPEN_WRITE_ONLY;
|
||||
if(AZ::IO::SystemFile newLogFile; newLogFile.Open(m_szFilename, openMode))
|
||||
{
|
||||
m_logFileHandle = AZStd::move(newLogFile);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1537,9 +1533,9 @@ const char* CLog::GetModuleFilter()
|
||||
void CLog::FlushAndClose()
|
||||
{
|
||||
#if defined(KEEP_LOG_FILE_OPEN)
|
||||
if (m_logFileHandle)
|
||||
if (m_logFileHandle.IsOpen())
|
||||
{
|
||||
CloseLogFile(true);
|
||||
CloseLogFile();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -137,8 +137,8 @@ private: // -------------------------------------------------------------------
|
||||
void LogStringToConsole(const char* szString, ELogType logType, bool bAdd) {}
|
||||
#endif // !defined(EXCLUDE_NORMAL_LOG)
|
||||
|
||||
AZ::IO::HandleType OpenLogFile(const char* filename, const char* mode);
|
||||
void CloseLogFile(bool force = false);
|
||||
bool OpenLogFile(const char* filename, int mode);
|
||||
void CloseLogFile();
|
||||
|
||||
// will format the message into m_szTemp
|
||||
void FormatMessage(const char* szCommand, ...) PRINTF_PARAMS(2, 3);
|
||||
@@ -152,15 +152,11 @@ private: // -------------------------------------------------------------------
|
||||
virtual const char* GetAssetScopeString();
|
||||
#endif
|
||||
|
||||
ISystem* m_pSystem; //
|
||||
float m_fLastLoadingUpdateTime; // for non-frequent streamingEngine update
|
||||
//char m_szTemp[MAX_TEMP_LENGTH_SIZE]; //
|
||||
char m_szFilename[MAX_FILENAME_SIZE]; // can be with path
|
||||
mutable char m_sBackupFilename[MAX_FILENAME_SIZE]; // can be with path
|
||||
AZ::IO::HandleType m_logFileHandle;
|
||||
CryStackStringT<char, 32> m_LogMode; //mode m_pLogFile has been opened with
|
||||
AZ::IO::HandleType m_errFileHandle;
|
||||
int m_nErrCount;
|
||||
ISystem* m_pSystem; //
|
||||
float m_fLastLoadingUpdateTime; // for non-frequent streamingEngine update
|
||||
char m_szFilename[MAX_FILENAME_SIZE]; // can be with path
|
||||
mutable char m_sBackupFilename[MAX_FILENAME_SIZE]; // can be with path
|
||||
AZ::IO::SystemFile m_logFileHandle;
|
||||
|
||||
bool m_backupLogs;
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -30,7 +30,7 @@ namespace Platform
|
||||
|
||||
using FileHandleType = SystemFile::FileHandleType;
|
||||
|
||||
void Seek(FileHandleType handle, const SystemFile* systemFile, SizeType offset, SystemFile::SeekMode mode);
|
||||
void Seek(FileHandleType handle, const SystemFile* systemFile, SystemFile::SeekSizeType offset, SystemFile::SeekMode mode);
|
||||
SystemFile::SizeType Tell(FileHandleType handle, const SystemFile* systemFile);
|
||||
bool Eof(FileHandleType handle, const SystemFile* systemFile);
|
||||
AZ::u64 ModificationTime(FileHandleType handle, const SystemFile* systemFile);
|
||||
@@ -68,9 +68,8 @@ void SystemFile::CreatePath(const char* fileName)
|
||||
}
|
||||
|
||||
SystemFile::SystemFile()
|
||||
: m_handle{ AZ_TRAIT_SYSTEMFILE_INVALID_HANDLE }
|
||||
{
|
||||
m_fileName[0] = '\0';
|
||||
m_handle = AZ_TRAIT_SYSTEMFILE_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
SystemFile::~SystemFile()
|
||||
@@ -81,6 +80,25 @@ SystemFile::~SystemFile()
|
||||
}
|
||||
}
|
||||
|
||||
SystemFile::SystemFile(SystemFile&& other)
|
||||
: SystemFile{}
|
||||
{
|
||||
AZStd::swap(m_fileName, other.m_fileName);
|
||||
AZStd::swap(m_handle, other.m_handle);
|
||||
}
|
||||
|
||||
SystemFile& SystemFile::operator=(SystemFile&& other)
|
||||
{
|
||||
// Close the current file and take over the SystemFile handle and filename
|
||||
Close();
|
||||
m_fileName = AZStd::move(other.m_fileName);
|
||||
m_handle = AZStd::move(other.m_handle);
|
||||
other.m_fileName = {};
|
||||
other.m_handle = AZ_TRAIT_SYSTEMFILE_INVALID_HANDLE;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool SystemFile::Open(const char* fileName, int mode, int platformFlags)
|
||||
{
|
||||
AZ_PROFILE_INTERVAL_SCOPED(AZ::Debug::ProfileCategory::AzCore, this, "SystemFile::Open - %s", fileName);
|
||||
@@ -88,42 +106,42 @@ bool SystemFile::Open(const char* fileName, int mode, int platformFlags)
|
||||
|
||||
if (fileName) // If we reopen the file we are allowed to have NULL file name
|
||||
{
|
||||
if (strlen(fileName) > AZ_ARRAY_SIZE(m_fileName) - 1)
|
||||
if (strlen(fileName) > m_fileName.max_size())
|
||||
{
|
||||
EBUS_EVENT(FileIOEventBus, OnError, this, nullptr, 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
// store the filename
|
||||
azsnprintf(m_fileName, AZ_ARRAY_SIZE(m_fileName), "%s", fileName);
|
||||
m_fileName = fileName;
|
||||
}
|
||||
|
||||
if (FileIOBus::HasHandlers())
|
||||
{
|
||||
bool isOpen = false;
|
||||
bool isHandled = false;
|
||||
EBUS_EVENT_RESULT(isHandled, FileIOBus, OnOpen, *this, m_fileName, mode, platformFlags, isOpen);
|
||||
EBUS_EVENT_RESULT(isHandled, FileIOBus, OnOpen, *this, m_fileName.c_str(), mode, platformFlags, isOpen);
|
||||
if (isHandled)
|
||||
{
|
||||
return isOpen;
|
||||
}
|
||||
}
|
||||
|
||||
AZ_Assert(!IsOpen(), "This file (%s) is already open!", m_fileName);
|
||||
AZ_Assert(!IsOpen(), "This file (%s) is already open!", m_fileName.c_str());
|
||||
|
||||
return PlatformOpen(mode, platformFlags);
|
||||
}
|
||||
|
||||
bool SystemFile::ReOpen(int mode, int platformFlags)
|
||||
{
|
||||
AZ_Assert(strlen(m_fileName) > 0, "Missing filename. You must call open first!");
|
||||
AZ_Assert(!m_fileName.empty(), "Missing filename. You must call open first!");
|
||||
return Open(0, mode, platformFlags);
|
||||
}
|
||||
|
||||
void SystemFile::Close()
|
||||
{
|
||||
AZ_PROFILE_INTERVAL_SCOPED(AZ::Debug::ProfileCategory::AzCore, this, "SystemFile::Close - %s", m_fileName);
|
||||
AZ_PROFILE_SCOPE_STALL_DYNAMIC(AZ::Debug::ProfileCategory::AzCore, "SystemFile::Close - %s", m_fileName);
|
||||
AZ_PROFILE_INTERVAL_SCOPED(AZ::Debug::ProfileCategory::AzCore, this, "SystemFile::Close - %s", m_fileName.c_str());
|
||||
AZ_PROFILE_SCOPE_STALL_DYNAMIC(AZ::Debug::ProfileCategory::AzCore, "SystemFile::Close - %s", m_fileName.c_str());
|
||||
|
||||
if (FileIOBus::HasHandlers())
|
||||
{
|
||||
@@ -138,9 +156,9 @@ void SystemFile::Close()
|
||||
PlatformClose();
|
||||
}
|
||||
|
||||
void SystemFile::Seek(SizeType offset, SeekMode mode)
|
||||
void SystemFile::Seek(SeekSizeType offset, SeekMode mode)
|
||||
{
|
||||
AZ_PROFILE_SCOPE_STALL_DYNAMIC(AZ::Debug::ProfileCategory::AzCore, "SystemFile::Seek - %s:%i", m_fileName, offset);
|
||||
AZ_PROFILE_SCOPE_STALL_DYNAMIC(AZ::Debug::ProfileCategory::AzCore, "SystemFile::Seek - %s:%i", m_fileName.c_str(), offset);
|
||||
|
||||
if (FileIOBus::HasHandlers())
|
||||
{
|
||||
@@ -167,15 +185,15 @@ bool SystemFile::Eof()
|
||||
|
||||
AZ::u64 SystemFile::ModificationTime()
|
||||
{
|
||||
AZ_PROFILE_SCOPE_STALL_DYNAMIC(AZ::Debug::ProfileCategory::AzCore, "SystemFile::ModTime - %s", m_fileName);
|
||||
AZ_PROFILE_SCOPE_STALL_DYNAMIC(AZ::Debug::ProfileCategory::AzCore, "SystemFile::ModTime - %s", m_fileName.c_str());
|
||||
|
||||
return Platform::ModificationTime(m_handle, this);
|
||||
}
|
||||
|
||||
SystemFile::SizeType SystemFile::Read(SizeType byteSize, void* buffer)
|
||||
{
|
||||
AZ_PROFILE_INTERVAL_SCOPED(AZ::Debug::ProfileCategory::AzCore, this, "SystemFile::Read - %s:%i", m_fileName, byteSize);
|
||||
AZ_PROFILE_SCOPE_STALL_DYNAMIC(AZ::Debug::ProfileCategory::AzCore, "SystemFile::Read - %s:%i", m_fileName, byteSize);
|
||||
AZ_PROFILE_INTERVAL_SCOPED(AZ::Debug::ProfileCategory::AzCore, this, "SystemFile::Read - %s:%i", m_fileName.c_str(), byteSize);
|
||||
AZ_PROFILE_SCOPE_STALL_DYNAMIC(AZ::Debug::ProfileCategory::AzCore, "SystemFile::Read - %s:%i", m_fileName.c_str(), byteSize);
|
||||
|
||||
if (FileIOBus::HasHandlers())
|
||||
{
|
||||
@@ -193,8 +211,8 @@ SystemFile::SizeType SystemFile::Read(SizeType byteSize, void* buffer)
|
||||
|
||||
SystemFile::SizeType SystemFile::Write(const void* buffer, SizeType byteSize)
|
||||
{
|
||||
AZ_PROFILE_INTERVAL_SCOPED(AZ::Debug::ProfileCategory::AzCore, this, "SystemFile::Write - %s:%i", m_fileName, byteSize);
|
||||
AZ_PROFILE_SCOPE_STALL_DYNAMIC(AZ::Debug::ProfileCategory::AzCore, "SystemFile::Write - %s:%i", m_fileName, byteSize);
|
||||
AZ_PROFILE_INTERVAL_SCOPED(AZ::Debug::ProfileCategory::AzCore, this, "SystemFile::Write - %s:%i", m_fileName.c_str(), byteSize);
|
||||
AZ_PROFILE_SCOPE_STALL_DYNAMIC(AZ::Debug::ProfileCategory::AzCore, "SystemFile::Write - %s:%i", m_fileName.c_str(), byteSize);
|
||||
|
||||
if (FileIOBus::HasHandlers())
|
||||
{
|
||||
@@ -212,14 +230,14 @@ SystemFile::SizeType SystemFile::Write(const void* buffer, SizeType byteSize)
|
||||
|
||||
void SystemFile::Flush()
|
||||
{
|
||||
AZ_PROFILE_SCOPE_STALL_DYNAMIC(AZ::Debug::ProfileCategory::AzCore, "SystemFile::Flush - %s", m_fileName);
|
||||
AZ_PROFILE_SCOPE_STALL_DYNAMIC(AZ::Debug::ProfileCategory::AzCore, "SystemFile::Flush - %s", m_fileName.c_str());
|
||||
|
||||
Platform::Flush(m_handle, this);
|
||||
}
|
||||
|
||||
SystemFile::SizeType SystemFile::Length() const
|
||||
{
|
||||
AZ_PROFILE_SCOPE_STALL_DYNAMIC(AZ::Debug::ProfileCategory::AzCore, "SystemFile::Length - %s", m_fileName);
|
||||
AZ_PROFILE_SCOPE_STALL_DYNAMIC(AZ::Debug::ProfileCategory::AzCore, "SystemFile::Length - %s", m_fileName.c_str());
|
||||
|
||||
return Platform::Length(m_handle, this);
|
||||
}
|
||||
@@ -379,9 +397,9 @@ namespace
|
||||
HasPosixEnumOption(PermissionModeFlags::Write);
|
||||
|
||||
#undef HasPosixEnumOption
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
FileDescriptorRedirector::FileDescriptorRedirector(int sourceFileDescriptor)
|
||||
: m_sourceFileDescriptor(sourceFileDescriptor)
|
||||
{
|
||||
|
||||
@@ -12,10 +12,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/base.h>
|
||||
#include <AzCore/std/function/function_fwd.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
|
||||
#include <AzCore/IO/Path/Path_fwd.h>
|
||||
#include <AzCore/IO/SystemFile_Platform.h>
|
||||
#include <AzCore/std/function/function_fwd.h>
|
||||
#include <AzCore/std/string/fixed_string.h>
|
||||
|
||||
// Establish a consistent size that works across platforms. It's actually larger than this
|
||||
// on platforms we support, but this is a good least common denominator
|
||||
@@ -51,11 +52,15 @@ namespace AZ
|
||||
};
|
||||
|
||||
using SizeType = AZ::IO::Internal::SizeType;
|
||||
using SeekSizeType = AZ::IO::Internal::SeekSizeType;
|
||||
using FileHandleType = AZ::IO::Internal::FileHandleType;
|
||||
|
||||
SystemFile();
|
||||
~SystemFile();
|
||||
|
||||
SystemFile(SystemFile&&);
|
||||
SystemFile& operator=(SystemFile&&);
|
||||
|
||||
/**
|
||||
* Opens a file.
|
||||
* \param fileName full file name including path
|
||||
@@ -69,7 +74,7 @@ namespace AZ
|
||||
/// Closes a file, if file already close it has no effect.
|
||||
void Close();
|
||||
/// Seek in current file.
|
||||
void Seek(SizeType offset, SeekMode mode);
|
||||
void Seek(SeekSizeType offset, SeekMode mode);
|
||||
/// Get the cursor position in the current file.
|
||||
SizeType Tell();
|
||||
/// Is the cursor at the end of the file?
|
||||
@@ -87,7 +92,7 @@ namespace AZ
|
||||
/// Return disc offset if possible, otherwise 0
|
||||
SizeType DiskOffset() const;
|
||||
/// Return file name or NULL if file is not open.
|
||||
AZ_FORCE_INLINE const char* Name() const { return m_fileName; }
|
||||
AZ_FORCE_INLINE const char* Name() const { return m_fileName.c_str(); }
|
||||
bool IsOpen() const;
|
||||
|
||||
/// Return native handle to the file.
|
||||
@@ -124,12 +129,12 @@ namespace AZ
|
||||
|
||||
private:
|
||||
static void CreatePath(const char * fileName);
|
||||
|
||||
|
||||
bool PlatformOpen(int mode, int platformFlags);
|
||||
void PlatformClose();
|
||||
|
||||
FileHandleType m_handle;
|
||||
char m_fileName[AZ_MAX_PATH_LEN];
|
||||
|
||||
FileHandleType m_handle;
|
||||
AZ::IO::FixedMaxPathString m_fileName;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -127,13 +127,14 @@ namespace AZ
|
||||
*/
|
||||
class EditContext
|
||||
{
|
||||
public:
|
||||
/// @cond EXCLUDE_DOCS
|
||||
class ClassBuilder;
|
||||
class EnumBuilder;
|
||||
using ClassInfo = ClassBuilder; ///< @deprecated Use EditContext::ClassBuilder
|
||||
using EnumInfo = EnumBuilder; ///< @deprecated Use EditContext::EnumBuilder
|
||||
/// @endcond
|
||||
public:
|
||||
|
||||
AZ_CLASS_ALLOCATOR(EditContext, SystemAllocator, 0);
|
||||
|
||||
/**
|
||||
@@ -186,6 +187,7 @@ namespace AZ
|
||||
* look at the unit tests and example to see use cases.
|
||||
*
|
||||
*/
|
||||
public:
|
||||
class ClassBuilder
|
||||
{
|
||||
friend EditContext;
|
||||
@@ -399,6 +401,7 @@ namespace AZ
|
||||
EnumBuilder* Value(const char* name, E value);
|
||||
};
|
||||
|
||||
private:
|
||||
typedef AZStd::list<Edit::ClassData> ClassDataListType;
|
||||
typedef AZStd::unordered_map<AZ::Uuid, Edit::ElementData> EnumDataMapType;
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
/**
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
+16
-7
@@ -13,9 +13,10 @@
|
||||
#include <AzCore/Module/DynamicModuleHandle.h>
|
||||
#include <AzCore/IO/Path/Path.h>
|
||||
#include <AzCore/IO/SystemFile.h>
|
||||
|
||||
#include <AzCore/Memory/OSAllocator.h>
|
||||
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <libgen.h>
|
||||
|
||||
@@ -61,10 +62,11 @@ namespace AZ
|
||||
// If it doesn't attempt to append the path to the executable path
|
||||
if (!AZ::IO::SystemFile::Exists(fullFilePath.c_str()))
|
||||
{
|
||||
auto candidatePath = Platform::GetModulePath() / fullFilePath;
|
||||
AZ::IO::FixedMaxPath candidatePath = Platform::GetModulePath() / fullFilePath;
|
||||
if (AZ::IO::SystemFile::Exists(candidatePath.c_str()))
|
||||
{
|
||||
fullFilePath = candidatePath;
|
||||
m_fileName.assign(candidatePath.Native().c_str(), candidatePath.Native().size());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,19 +76,26 @@ namespace AZ
|
||||
{
|
||||
if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
|
||||
{
|
||||
if(AZ::IO::FixedMaxPath projectModulePath;
|
||||
if (AZ::IO::FixedMaxPath projectModulePath;
|
||||
settingsRegistry->Get(projectModulePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectConfigurationBinPath))
|
||||
{
|
||||
projectModulePath /= fullFilePath;
|
||||
if (AZ::IO::SystemFile::Exists(projectModulePath.c_str()))
|
||||
{
|
||||
fullFilePath = projectModulePath;
|
||||
m_fileName.assign(projectModulePath.c_str(), projectModulePath.Native().size());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_fileName = AZStd::string_view{fullFilePath.Native()};
|
||||
else
|
||||
{
|
||||
// The module does exist (in 'cwd'), but still needs to be an absolute path for the module to be loaded.
|
||||
AZStd::optional<AZ::IO::FixedMaxPathString> absPathOptional = AZ::Utils::ConvertToAbsolutePath(m_fileName);
|
||||
if (absPathOptional.has_value())
|
||||
{
|
||||
m_fileName.assign(absPathOptional->c_str(), absPathOptional->size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
~DynamicModuleHandleUnixLike() override
|
||||
|
||||
+3
-3
@@ -86,9 +86,9 @@ bool SystemFile::PlatformOpen(int mode, int platformFlags)
|
||||
|
||||
if (createPath)
|
||||
{
|
||||
CreatePath(m_fileName);
|
||||
CreatePath(m_fileName.c_str());
|
||||
}
|
||||
m_handle = open(m_fileName, desiredAccess, permissions);
|
||||
m_handle = open(m_fileName.c_str(), desiredAccess, permissions);
|
||||
|
||||
if (m_handle == PlatformSpecificInvalidHandle)
|
||||
{
|
||||
@@ -119,7 +119,7 @@ namespace Platform
|
||||
{
|
||||
using FileHandleType = AZ::IO::SystemFile::FileHandleType;
|
||||
|
||||
void Seek(FileHandleType handle, const SystemFile* systemFile, SizeType offset, SystemFile::SeekMode mode)
|
||||
void Seek(FileHandleType handle, const SystemFile* systemFile, SystemFile::SeekSizeType offset, SystemFile::SeekMode mode)
|
||||
{
|
||||
if (handle != PlatformSpecificInvalidHandle)
|
||||
{
|
||||
|
||||
@@ -209,19 +209,19 @@ bool SystemFile::PlatformOpen(int mode, int platformFlags)
|
||||
|
||||
if (createPath)
|
||||
{
|
||||
CreatePath(m_fileName);
|
||||
CreatePath(m_fileName.c_str());
|
||||
}
|
||||
|
||||
# ifdef _UNICODE
|
||||
wchar_t fileNameW[AZ_MAX_PATH_LEN];
|
||||
size_t numCharsConverted;
|
||||
m_handle = INVALID_HANDLE_VALUE;
|
||||
if (mbstowcs_s(&numCharsConverted, fileNameW, m_fileName, AZ_ARRAY_SIZE(fileNameW) - 1) == 0)
|
||||
if (mbstowcs_s(&numCharsConverted, fileNameW, m_fileName.c_str(), AZ_ARRAY_SIZE(fileNameW) - 1) == 0)
|
||||
{
|
||||
m_handle = CreateFileW(fileNameW, dwDesiredAccess, dwShareMode, 0, dwCreationDisposition, dwFlagsAndAttributes, 0);
|
||||
}
|
||||
# else //!_UNICODE
|
||||
m_handle = CreateFile(m_fileName, dwDesiredAccess, dwShareMode, 0, dwCreationDisposition, dwFlagsAndAttributes, 0);
|
||||
m_handle = CreateFile(m_fileName.c_str(), dwDesiredAccess, dwShareMode, 0, dwCreationDisposition, dwFlagsAndAttributes, 0);
|
||||
# endif // !_UNICODE
|
||||
|
||||
if (m_handle == INVALID_HANDLE_VALUE)
|
||||
@@ -261,7 +261,7 @@ namespace Platform
|
||||
{
|
||||
using FileHandleType = AZ::IO::SystemFile::FileHandleType;
|
||||
|
||||
void Seek(FileHandleType handle, const SystemFile* systemFile, SizeType offset, SystemFile::SeekMode mode)
|
||||
void Seek(FileHandleType handle, const SystemFile* systemFile, SystemFile::SeekSizeType offset, SystemFile::SeekMode mode)
|
||||
{
|
||||
if (handle != PlatformSpecificInvalidHandle)
|
||||
{
|
||||
|
||||
@@ -13,6 +13,9 @@
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <corecrt_io.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <AzCore/std/typetraits/underlying_type.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
@@ -21,6 +24,7 @@ namespace AZ
|
||||
namespace Internal
|
||||
{
|
||||
using SizeType = AZ::u64;
|
||||
using SeekSizeType = AZ::s64;
|
||||
using FileHandleType = void*;
|
||||
}
|
||||
|
||||
@@ -31,7 +35,7 @@ namespace AZ
|
||||
Append = _O_APPEND, // Moves the file pointer to the end of the file before every write operation.
|
||||
Create = _O_CREAT, // Creates a file and opens it for writing. Has no effect if the file specified by filename exists. PermissionMode is required.
|
||||
Temporary = _O_TEMPORARY, // Applies only when used with CREAT. Creates a file as temporary; the file is deleted when the last file descriptor is closed. PermissionMode equired when CREAT is specified.
|
||||
Exclusive = _O_EXCL, // Applies only when used with CREAT. Returns an error value if a file specified by filename exists.
|
||||
Exclusive = _O_EXCL, // Applies only when used with CREAT. Returns an error value if a file specified by filename exists.
|
||||
Truncate = _O_TRUNC, // Opens a file and truncates it to zero length; the file must have write permission. Cannot be specified with RDONLY.
|
||||
// Note: The TRUNC flag destroys the contents of the specified file.
|
||||
|
||||
|
||||
+13
-3
@@ -24,9 +24,9 @@ namespace AZ
|
||||
: public DynamicModuleHandle
|
||||
{
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(DynamicModuleHandleWindows, OSAllocator, 0)
|
||||
AZ_CLASS_ALLOCATOR(DynamicModuleHandleWindows, OSAllocator, 0);
|
||||
|
||||
DynamicModuleHandleWindows(const char* fullFileName)
|
||||
DynamicModuleHandleWindows(const char* fullFileName)
|
||||
: DynamicModuleHandle(fullFileName)
|
||||
, m_handle(nullptr)
|
||||
{
|
||||
@@ -52,6 +52,7 @@ namespace AZ
|
||||
if (AZ::IO::SystemFile::Exists(candidatePath.c_str()))
|
||||
{
|
||||
m_fileName.assign(candidatePath.Native().c_str(), candidatePath.Native().size());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -65,7 +66,7 @@ namespace AZ
|
||||
// Therefore an existence check is needed
|
||||
if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
|
||||
{
|
||||
if(AZ::IO::FixedMaxPath projectModulePath;
|
||||
if (AZ::IO::FixedMaxPath projectModulePath;
|
||||
settingsRegistry->Get(projectModulePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectConfigurationBinPath))
|
||||
{
|
||||
projectModulePath /= AZStd::string_view(m_fileName);
|
||||
@@ -76,6 +77,15 @@ namespace AZ
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// The module does exist (in 'cwd'), but still needs to be an absolute path for the module to be loaded.
|
||||
AZStd::optional<AZ::IO::FixedMaxPathString> absPathOptional = AZ::Utils::ConvertToAbsolutePath(m_fileName);
|
||||
if (absPathOptional.has_value())
|
||||
{
|
||||
m_fileName.assign(absPathOptional->c_str(), absPathOptional->size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
~DynamicModuleHandleWindows() override
|
||||
|
||||
@@ -1914,7 +1914,7 @@ namespace UnitTest
|
||||
TEST_F(String, StringView_CompareIsConstexpr)
|
||||
{
|
||||
using TypeParam = char;
|
||||
auto MakeCompileTimeString1 = []() constexpr -> const TypeParam*
|
||||
auto ThisTestMakeCompileTimeString1 = []() constexpr -> const TypeParam*
|
||||
{
|
||||
return "HelloWorld";
|
||||
};
|
||||
@@ -1922,7 +1922,7 @@ namespace UnitTest
|
||||
{
|
||||
return "HelloPearl";
|
||||
};
|
||||
constexpr const TypeParam* compileTimeString1 = MakeCompileTimeString1();
|
||||
constexpr const TypeParam* compileTimeString1 = ThisTestMakeCompileTimeString1();
|
||||
constexpr const TypeParam* compileTimeString2 = MakeCompileTimeString2();
|
||||
constexpr basic_string_view<TypeParam> lhsView(compileTimeString1);
|
||||
constexpr basic_string_view<TypeParam> rhsView(compileTimeString2);
|
||||
@@ -1937,11 +1937,11 @@ namespace UnitTest
|
||||
TEST_F(String, StringView_CompareOperatorsAreConstexpr)
|
||||
{
|
||||
using TypeParam = char;
|
||||
auto MakeCompileTimeString1 = []() constexpr -> const TypeParam*
|
||||
auto TestMakeCompileTimeString1 = []() constexpr -> const TypeParam*
|
||||
{
|
||||
return "HelloWorld";
|
||||
};
|
||||
constexpr const TypeParam* compileTimeString1 = MakeCompileTimeString1();
|
||||
constexpr const TypeParam* compileTimeString1 = TestMakeCompileTimeString1();
|
||||
constexpr basic_string_view<TypeParam> compareView(compileTimeString1);
|
||||
static_assert(compareView == "HelloWorld", "string_view operator== comparison has failed");
|
||||
static_assert(compareView != "MadWorld", "string_view operator!= comparison has failed");
|
||||
@@ -1955,7 +1955,7 @@ namespace UnitTest
|
||||
{
|
||||
auto swap_test_func = []() constexpr -> basic_string_view<TypeParam>
|
||||
{
|
||||
constexpr auto MakeCompileTimeString1 = []() constexpr -> const TypeParam*
|
||||
constexpr auto ThisTestMakeCompileTimeString1 = []() constexpr -> const TypeParam*
|
||||
{
|
||||
if constexpr (AZStd::is_same_v<TypeParam, char>)
|
||||
{
|
||||
@@ -1977,7 +1977,7 @@ namespace UnitTest
|
||||
return L"InuWorld";
|
||||
}
|
||||
};
|
||||
constexpr const TypeParam* compileTimeString1 = MakeCompileTimeString1();
|
||||
constexpr const TypeParam* compileTimeString1 = ThisTestMakeCompileTimeString1();
|
||||
constexpr const TypeParam* compileTimeString2 = MakeCompileTimeString2();
|
||||
basic_string_view<TypeParam> lhsView(compileTimeString1);
|
||||
basic_string_view<TypeParam> rhsView(compileTimeString2);
|
||||
@@ -2001,7 +2001,7 @@ namespace UnitTest
|
||||
|
||||
TYPED_TEST(BasicStringViewConstexprFixture, HashString_FunctionIsConstexpr)
|
||||
{
|
||||
auto MakeCompileTimeString1 = []() constexpr -> const TypeParam*
|
||||
auto ThisTestMakeCompileTimeString1 = []() constexpr -> const TypeParam*
|
||||
{
|
||||
if constexpr (AZStd::is_same_v<TypeParam, char>)
|
||||
{
|
||||
@@ -2012,7 +2012,7 @@ namespace UnitTest
|
||||
return L"HelloWorld";
|
||||
}
|
||||
};
|
||||
constexpr const TypeParam* compileTimeString1 = MakeCompileTimeString1();
|
||||
constexpr const TypeParam* compileTimeString1 = ThisTestMakeCompileTimeString1();
|
||||
constexpr basic_string_view<TypeParam> hashView(compileTimeString1);
|
||||
constexpr size_t compileHash = AZStd::hash<basic_string_view<TypeParam>>{}(hashView);
|
||||
static_assert(compileHash != 0, "Hash of \"HelloWorld\" should not be 0");
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -119,10 +119,10 @@ namespace UnitTest
|
||||
|
||||
TEST(MATH_Obb, Contains)
|
||||
{
|
||||
const Vector3 position(1.0f, 2.0f, 3.0f);
|
||||
const Quaternion rotation = Quaternion::CreateRotationZ(DegToRad(30.0f));
|
||||
const Vector3 halfLengths(2.0f, 1.0f, 2.5f);
|
||||
const Obb obb = Obb::CreateFromPositionRotationAndHalfLengths(position, rotation, halfLengths);
|
||||
const Vector3 testPosition(1.0f, 2.0f, 3.0f);
|
||||
const Quaternion testRotation = Quaternion::CreateRotationZ(DegToRad(30.0f));
|
||||
const Vector3 testHalfLengths(2.0f, 1.0f, 2.5f);
|
||||
const Obb obb = Obb::CreateFromPositionRotationAndHalfLengths(testPosition, testRotation, testHalfLengths);
|
||||
// test some pairs of points which should be just either side of the Obb boundary
|
||||
EXPECT_TRUE(obb.Contains(Vector3(1.35f, 3.35f, 3.5f)));
|
||||
EXPECT_FALSE(obb.Contains(Vector3(1.35f, 3.4f, 3.5f)));
|
||||
@@ -134,10 +134,10 @@ namespace UnitTest
|
||||
|
||||
TEST(MATH_Obb, GetDistance)
|
||||
{
|
||||
const Vector3 position(5.0f, 3.0f, 2.0f);
|
||||
const Quaternion rotation = Quaternion::CreateRotationX(DegToRad(60.0f));
|
||||
const Vector3 halfLengths(0.5f, 2.0f, 1.5f);
|
||||
const Obb obb = Obb::CreateFromPositionRotationAndHalfLengths(position, rotation, halfLengths);
|
||||
const Vector3 testPosition(5.0f, 3.0f, 2.0f);
|
||||
const Quaternion testRotation = Quaternion::CreateRotationX(DegToRad(60.0f));
|
||||
const Vector3 testHalfLengths(0.5f, 2.0f, 1.5f);
|
||||
const Obb obb = Obb::CreateFromPositionRotationAndHalfLengths(testPosition, testRotation, testHalfLengths);
|
||||
EXPECT_NEAR(obb.GetDistance(Vector3(5.3f, 3.2f, 1.8f)), 0.0f, 1e-3f);
|
||||
EXPECT_NEAR(obb.GetDistance(Vector3(5.1f, 1.1f, 3.7f)), 0.9955f, 1e-3f);
|
||||
EXPECT_NEAR(obb.GetDistance(Vector3(4.7f, 4.5f, 4.2f)), 0.6553f, 1e-3f);
|
||||
@@ -146,10 +146,10 @@ namespace UnitTest
|
||||
|
||||
TEST(MATH_Obb, GetDistanceSq)
|
||||
{
|
||||
const Vector3 position(1.0f, 4.0f, 3.0f);
|
||||
const Quaternion rotation = Quaternion::CreateRotationY(DegToRad(45.0f));
|
||||
const Vector3 halfLengths(1.5f, 3.0f, 1.0f);
|
||||
const Obb obb = Obb::CreateFromPositionRotationAndHalfLengths(position, rotation, halfLengths);
|
||||
const Vector3 testPosition(1.0f, 4.0f, 3.0f);
|
||||
const Quaternion testRotation = Quaternion::CreateRotationY(DegToRad(45.0f));
|
||||
const Vector3 testHalfLengths(1.5f, 3.0f, 1.0f);
|
||||
const Obb obb = Obb::CreateFromPositionRotationAndHalfLengths(testPosition, testRotation, testHalfLengths);
|
||||
EXPECT_NEAR(obb.GetDistanceSq(Vector3(1.1f, 4.3f, 2.7f)), 0.0f, 1e-3f);
|
||||
EXPECT_NEAR(obb.GetDistanceSq(Vector3(-0.7f, 3.5f, 2.0f)), 0.8266f, 1e-3f);
|
||||
EXPECT_NEAR(obb.GetDistanceSq(Vector3(2.4f, 0.5f, 1.5f)), 0.5532f, 1e-3f);
|
||||
|
||||
@@ -711,8 +711,8 @@ namespace AzFramework
|
||||
}
|
||||
}
|
||||
|
||||
AZ::IO::FixedMaxPath projectUserPath;
|
||||
if (m_settingsRegistry->Get(projectUserPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectUserPath))
|
||||
if (AZ::IO::FixedMaxPath projectUserPath;
|
||||
m_settingsRegistry->Get(projectUserPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectUserPath))
|
||||
{
|
||||
fileIoBase->SetAlias("@user@", projectUserPath.c_str());
|
||||
AZ::IO::FixedMaxPath projectLogPath = projectUserPath / "log";
|
||||
@@ -721,6 +721,15 @@ namespace AzFramework
|
||||
|
||||
CreateUserCache(projectUserPath, *fileIoBase);
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ::IO::FixedMaxPath fallbackLogPath = GetEngineRoot();
|
||||
fallbackLogPath /= "user";
|
||||
fileIoBase->SetAlias("@user@", fallbackLogPath.c_str());
|
||||
fallbackLogPath /= "log";
|
||||
fileIoBase->SetAlias("@log@", fallbackLogPath.c_str());
|
||||
fileIoBase->CreatePath(fallbackLogPath.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -38,20 +38,20 @@ namespace AzFramework
|
||||
void SpawnableEntitiesContainer::SpawnAllEntities()
|
||||
{
|
||||
AZ_Assert(m_threadData, "Calling SpawnAllEntities on a Spawnable container that's not set.");
|
||||
SpawnableEntitiesInterface::Get()->SpawnAllEntities(m_threadData->m_spawnedEntitiesTicket, SpawnablePriority_Default);
|
||||
SpawnableEntitiesInterface::Get()->SpawnAllEntities(m_threadData->m_spawnedEntitiesTicket);
|
||||
}
|
||||
|
||||
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, SpawnablePriority_Default, AZStd::move(entityIndices));
|
||||
m_threadData->m_spawnedEntitiesTicket, AZStd::move(entityIndices));
|
||||
}
|
||||
|
||||
void SpawnableEntitiesContainer::DespawnAllEntities()
|
||||
{
|
||||
AZ_Assert(m_threadData, "Calling DespawnEntities on a Spawnable container that's not set.");
|
||||
SpawnableEntitiesInterface::Get()->DespawnAllEntities(m_threadData->m_spawnedEntitiesTicket, SpawnablePriority_Default);
|
||||
SpawnableEntitiesInterface::Get()->DespawnAllEntities(m_threadData->m_spawnedEntitiesTicket);
|
||||
}
|
||||
|
||||
void SpawnableEntitiesContainer::Reset(AZ::Data::Asset<Spawnable> spawnable)
|
||||
@@ -69,7 +69,6 @@ namespace AzFramework
|
||||
|
||||
SpawnableEntitiesInterface::Get()->Barrier(
|
||||
m_threadData->m_spawnedEntitiesTicket,
|
||||
SpawnablePriority_Default,
|
||||
[threadData = m_threadData](EntitySpawnTicket::Id) mutable
|
||||
{
|
||||
threadData.reset();
|
||||
@@ -88,7 +87,6 @@ namespace AzFramework
|
||||
AZ_Assert(m_threadData, "Calling DespawnEntities on a Spawnable container that's not set.");
|
||||
SpawnableEntitiesInterface::Get()->Barrier(
|
||||
m_threadData->m_spawnedEntitiesTicket,
|
||||
SpawnablePriority_Default,
|
||||
[generation = m_threadData->m_generation, callback = AZStd::move(callback)](EntitySpawnTicket::Id)
|
||||
{
|
||||
callback(generation);
|
||||
@@ -115,7 +113,6 @@ namespace AzFramework
|
||||
AZ_Assert(m_threadData, "SpawnableEntitiesContainer is monitoring a spawnable, but doesn't have the associated data.");
|
||||
|
||||
AZ_TracePrintf("Spawnables", "Reloading spawnable '%s'.\n", replacementAsset.GetHint().c_str());
|
||||
SpawnableEntitiesInterface::Get()->ReloadSpawnable(
|
||||
m_threadData->m_spawnedEntitiesTicket, SpawnablePriority_Default, AZStd::move(replacementAsset));
|
||||
SpawnableEntitiesInterface::Get()->ReloadSpawnable(m_threadData->m_spawnedEntitiesTicket, AZStd::move(replacementAsset));
|
||||
}
|
||||
} // namespace AzFramework
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
namespace AZ
|
||||
{
|
||||
class Entity;
|
||||
class SerializeContext;
|
||||
}
|
||||
|
||||
namespace AzFramework
|
||||
@@ -171,6 +172,77 @@ namespace AzFramework
|
||||
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
|
||||
@@ -196,47 +268,34 @@ namespace AzFramework
|
||||
|
||||
//! Spawn instances of all 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 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, SpawnablePriority priority, 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.
|
||||
//! @param optionalArgs Optional additional arguments, see SpawnEntitiesOptionalArgs.
|
||||
virtual void SpawnEntities(
|
||||
EntitySpawnTicket& ticket, SpawnablePriority priority, AZStd::vector<size_t> entityIndices,
|
||||
EntityPreInsertionCallback preInsertionCallback = {}, EntitySpawnCallback completionCallback = {}) = 0;
|
||||
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 priority The priority at which this call will be executed.
|
||||
//! @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, SpawnablePriority priority, EntityDespawnCallback completionCallback = {}) = 0;
|
||||
|
||||
//! @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 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.
|
||||
//! @param optionalArgs Optional additional arguments, see ReloadSpawnableOptionalArgs.
|
||||
virtual void ReloadSpawnable(
|
||||
EntitySpawnTicket& ticket, SpawnablePriority priority, AZ::Data::Asset<Spawnable> spawnable,
|
||||
ReloadSpawnableCallback completionCallback = {}) = 0;
|
||||
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 priority The priority at which this call will be executed.
|
||||
//! @param listCallback Required callback that will be called to list the entities on.
|
||||
virtual void ListEntities(EntitySpawnTicket& ticket, SpawnablePriority priority, 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
|
||||
@@ -244,23 +303,24 @@ namespace AzFramework
|
||||
//! the same index may appear multiple times as there are no restriction on how many instance of a specific entity can be
|
||||
//! created.
|
||||
//! @param ticket Only the entities associated with this ticket will be listed.
|
||||
//! @param priority The priority at which this call will be executed.
|
||||
//! @param listCallback Required callback that will be called to list the entities and indices on.
|
||||
//! @param optionalArgs Optional additional arguments, see ListEntitiesOptionalArgs.
|
||||
virtual void ListIndicesAndEntities(
|
||||
EntitySpawnTicket& ticket, SpawnablePriority priority, ListIndicesEntitiesCallback listCallback) = 0;
|
||||
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 priority The priority at which this call will be executed.
|
||||
//! @param listCallback Required callback that will be called to transfer the entities through.
|
||||
virtual void ClaimEntities(EntitySpawnTicket& ticket, SpawnablePriority priority, 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.
|
||||
//! @param ticket The ticket to monitor.
|
||||
//! @param priority The priority at which this call will be executed.
|
||||
//! @param completionCallback Required callback that will be called as soon as the barrier has been reached.
|
||||
virtual void Barrier(EntitySpawnTicket& ticket, SpawnablePriority priority, BarrierCallback completionCallback) = 0;
|
||||
//! @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;
|
||||
|
||||
@@ -38,6 +38,10 @@ namespace AzFramework
|
||||
|
||||
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);
|
||||
@@ -46,58 +50,61 @@ namespace AzFramework
|
||||
}
|
||||
}
|
||||
|
||||
void SpawnableEntitiesManager::SpawnAllEntities(
|
||||
EntitySpawnTicket& ticket, SpawnablePriority priority, EntityPreInsertionCallback preInsertionCallback,
|
||||
EntitySpawnCallback completionCallback)
|
||||
void SpawnableEntitiesManager::SpawnAllEntities(EntitySpawnTicket& ticket, SpawnAllEntitiesOptionalArgs optionalArgs)
|
||||
{
|
||||
AZ_Assert(ticket.IsValid(), "Ticket provided to SpawnAllEntities hasn't been initialized.");
|
||||
|
||||
SpawnAllEntitiesCommand queueEntry;
|
||||
queueEntry.m_ticketId = ticket.GetId();
|
||||
queueEntry.m_completionCallback = AZStd::move(completionCallback);
|
||||
queueEntry.m_preInsertionCallback = AZStd::move(preInsertionCallback);
|
||||
QueueRequest(ticket, priority, 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);
|
||||
QueueRequest(ticket, optionalArgs.m_priority, AZStd::move(queueEntry));
|
||||
}
|
||||
|
||||
void SpawnableEntitiesManager::SpawnEntities(
|
||||
EntitySpawnTicket& ticket, SpawnablePriority priority, 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_ticketId = ticket.GetId();
|
||||
queueEntry.m_entityIndices = AZStd::move(entityIndices);
|
||||
queueEntry.m_completionCallback = AZStd::move(completionCallback);
|
||||
queueEntry.m_preInsertionCallback = AZStd::move(preInsertionCallback);
|
||||
QueueRequest(ticket, priority, 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, SpawnablePriority priority, 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_ticketId = ticket.GetId();
|
||||
queueEntry.m_completionCallback = AZStd::move(completionCallback);
|
||||
QueueRequest(ticket, priority, AZStd::move(queueEntry));
|
||||
queueEntry.m_completionCallback = AZStd::move(optionalArgs.m_completionCallback);
|
||||
QueueRequest(ticket, optionalArgs.m_priority, AZStd::move(queueEntry));
|
||||
}
|
||||
|
||||
void SpawnableEntitiesManager::ReloadSpawnable(
|
||||
EntitySpawnTicket& ticket, SpawnablePriority priority, AZ::Data::Asset<Spawnable> spawnable,
|
||||
ReloadSpawnableCallback completionCallback)
|
||||
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_ticketId = ticket.GetId();
|
||||
queueEntry.m_spawnable = AZStd::move(spawnable);
|
||||
queueEntry.m_completionCallback = AZStd::move(completionCallback);
|
||||
QueueRequest(ticket, priority, 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, SpawnablePriority priority, 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.");
|
||||
@@ -105,11 +112,11 @@ namespace AzFramework
|
||||
ListEntitiesCommand queueEntry;
|
||||
queueEntry.m_ticketId = ticket.GetId();
|
||||
queueEntry.m_listCallback = AZStd::move(listCallback);
|
||||
QueueRequest(ticket, priority, AZStd::move(queueEntry));
|
||||
QueueRequest(ticket, optionalArgs.m_priority, AZStd::move(queueEntry));
|
||||
}
|
||||
|
||||
void SpawnableEntitiesManager::ListIndicesAndEntities(
|
||||
EntitySpawnTicket& ticket, SpawnablePriority priority, ListIndicesEntitiesCallback listCallback)
|
||||
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.");
|
||||
@@ -117,10 +124,11 @@ namespace AzFramework
|
||||
ListIndicesEntitiesCommand queueEntry;
|
||||
queueEntry.m_ticketId = ticket.GetId();
|
||||
queueEntry.m_listCallback = AZStd::move(listCallback);
|
||||
QueueRequest(ticket, priority, AZStd::move(queueEntry));
|
||||
QueueRequest(ticket, optionalArgs.m_priority, AZStd::move(queueEntry));
|
||||
}
|
||||
|
||||
void SpawnableEntitiesManager::ClaimEntities(EntitySpawnTicket& ticket, SpawnablePriority priority, 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.");
|
||||
@@ -128,10 +136,10 @@ namespace AzFramework
|
||||
ClaimEntitiesCommand queueEntry;
|
||||
queueEntry.m_ticketId = ticket.GetId();
|
||||
queueEntry.m_listCallback = AZStd::move(listCallback);
|
||||
QueueRequest(ticket, priority, AZStd::move(queueEntry));
|
||||
QueueRequest(ticket, optionalArgs.m_priority, AZStd::move(queueEntry));
|
||||
}
|
||||
|
||||
void SpawnableEntitiesManager::Barrier(EntitySpawnTicket& ticket, SpawnablePriority priority, 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.");
|
||||
@@ -139,7 +147,7 @@ namespace AzFramework
|
||||
BarrierCommand queueEntry;
|
||||
queueEntry.m_ticketId = ticket.GetId();
|
||||
queueEntry.m_completionCallback = AZStd::move(completionCallback);
|
||||
QueueRequest(ticket, priority, AZStd::move(queueEntry));
|
||||
QueueRequest(ticket, optionalArgs.m_priority, AZStd::move(queueEntry));
|
||||
}
|
||||
|
||||
void SpawnableEntitiesManager::AddOnSpawnedHandler(AZ::Event<AZ::Data::Asset<Spawnable>>::Handler& handler)
|
||||
@@ -174,58 +182,57 @@ namespace AzFramework
|
||||
|
||||
auto SpawnableEntitiesManager::ProcessQueue(Queue& queue) -> CommandQueueStatus
|
||||
{
|
||||
AZStd::queue<Requests> pendingRequestQueue;
|
||||
// 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)
|
||||
{
|
||||
AZStd::scoped_lock queueLock(queue.m_pendingRequestMutex);
|
||||
queue.m_pendingRequest.swap(pendingRequestQueue);
|
||||
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() || !queue.m_delayed.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 = queue.m_delayed.size();
|
||||
for (size_t i = 0; i < delayedSize; ++i)
|
||||
AZStd::queue<Requests> pendingRequestQueue;
|
||||
{
|
||||
Requests& request = queue.m_delayed.front();
|
||||
bool result = AZStd::visit([this, serializeContext](auto&& args) -> bool
|
||||
{
|
||||
return ProcessRequest(args, *serializeContext);
|
||||
}, request);
|
||||
if (!result)
|
||||
{
|
||||
queue.m_delayed.emplace_back(AZStd::move(request));
|
||||
}
|
||||
queue.m_delayed.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)
|
||||
{
|
||||
queue.m_delayed.emplace_back(AZStd::move(request));
|
||||
}
|
||||
pendingRequestQueue.pop();
|
||||
}
|
||||
|
||||
// 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(queue.m_pendingRequestMutex);
|
||||
queue.m_pendingRequest.swap(pendingRequestQueue);
|
||||
}
|
||||
} while (!pendingRequestQueue.empty());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
return queue.m_delayed.empty() ? CommandQueueStatus::NoCommandsLeft : CommandQueueStatus::HasCommandsLeft;
|
||||
}
|
||||
@@ -250,24 +257,14 @@ namespace AzFramework
|
||||
}
|
||||
}
|
||||
|
||||
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 = *request.m_ticket;
|
||||
if (ticket.m_spawnable.IsReady() && request.m_requestId == ticket.m_currentRequestId)
|
||||
@@ -291,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);
|
||||
@@ -305,16 +298,8 @@ 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)
|
||||
{
|
||||
@@ -323,11 +308,10 @@ namespace AzFramework
|
||||
}
|
||||
|
||||
// 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)
|
||||
@@ -347,21 +331,41 @@ namespace AzFramework
|
||||
}
|
||||
}
|
||||
|
||||
bool SpawnableEntitiesManager::ProcessRequest(SpawnEntitiesCommand& request, AZ::SerializeContext& serializeContext)
|
||||
bool SpawnableEntitiesManager::ProcessRequest(SpawnEntitiesCommand& request)
|
||||
{
|
||||
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);
|
||||
|
||||
@@ -369,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;
|
||||
@@ -390,11 +390,10 @@ namespace AzFramework
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
@@ -413,8 +412,7 @@ namespace AzFramework
|
||||
}
|
||||
}
|
||||
|
||||
bool SpawnableEntitiesManager::ProcessRequest(DespawnAllEntitiesCommand& request,
|
||||
[[maybe_unused]] AZ::SerializeContext& serializeContext)
|
||||
bool SpawnableEntitiesManager::ProcessRequest(DespawnAllEntitiesCommand& request)
|
||||
{
|
||||
Ticket& ticket = *request.m_ticket;
|
||||
if (request.m_requestId == ticket.m_currentRequestId)
|
||||
@@ -447,7 +445,7 @@ namespace AzFramework
|
||||
}
|
||||
}
|
||||
|
||||
bool SpawnableEntitiesManager::ProcessRequest(ReloadSpawnableCommand& request, AZ::SerializeContext& serializeContext)
|
||||
bool SpawnableEntitiesManager::ProcessRequest(ReloadSpawnableCommand& request)
|
||||
{
|
||||
Ticket& ticket = *request.m_ticket;
|
||||
AZ_Assert(ticket.m_spawnable.GetId() == request.m_spawnable.GetId(),
|
||||
@@ -470,39 +468,43 @@ 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);
|
||||
@@ -525,7 +527,7 @@ namespace AzFramework
|
||||
}
|
||||
}
|
||||
|
||||
bool SpawnableEntitiesManager::ProcessRequest(ListEntitiesCommand& request, [[maybe_unused]] AZ::SerializeContext& serializeContext)
|
||||
bool SpawnableEntitiesManager::ProcessRequest(ListEntitiesCommand& request)
|
||||
{
|
||||
Ticket& ticket = *request.m_ticket;
|
||||
if (request.m_requestId == ticket.m_currentRequestId)
|
||||
@@ -541,7 +543,7 @@ namespace AzFramework
|
||||
}
|
||||
}
|
||||
|
||||
bool SpawnableEntitiesManager::ProcessRequest(ListIndicesEntitiesCommand& request, [[maybe_unused]] AZ::SerializeContext& serializeContext)
|
||||
bool SpawnableEntitiesManager::ProcessRequest(ListIndicesEntitiesCommand& request)
|
||||
{
|
||||
Ticket& ticket = *request.m_ticket;
|
||||
if (request.m_requestId == ticket.m_currentRequestId)
|
||||
@@ -560,7 +562,7 @@ namespace AzFramework
|
||||
}
|
||||
}
|
||||
|
||||
bool SpawnableEntitiesManager::ProcessRequest(ClaimEntitiesCommand& request, [[maybe_unused]] AZ::SerializeContext& serializeContext)
|
||||
bool SpawnableEntitiesManager::ProcessRequest(ClaimEntitiesCommand& request)
|
||||
{
|
||||
Ticket& ticket = *request.m_ticket;
|
||||
if (request.m_requestId == ticket.m_currentRequestId)
|
||||
@@ -580,7 +582,7 @@ namespace AzFramework
|
||||
}
|
||||
}
|
||||
|
||||
bool SpawnableEntitiesManager::ProcessRequest(BarrierCommand& request, [[maybe_unused]] AZ::SerializeContext& serializeContext)
|
||||
bool SpawnableEntitiesManager::ProcessRequest(BarrierCommand& request)
|
||||
{
|
||||
Ticket& ticket = *request.m_ticket;
|
||||
if (request.m_requestId == ticket.m_currentRequestId)
|
||||
@@ -599,7 +601,7 @@ namespace AzFramework
|
||||
}
|
||||
}
|
||||
|
||||
bool SpawnableEntitiesManager::ProcessRequest(DestroyTicketCommand& request, [[maybe_unused]] AZ::SerializeContext& serializeContext)
|
||||
bool SpawnableEntitiesManager::ProcessRequest(DestroyTicketCommand& request)
|
||||
{
|
||||
if (request.m_requestId == request.m_ticket->m_currentRequestId)
|
||||
{
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace AzFramework
|
||||
AZ_CLASS_ALLOCATOR(SpawnableEntitiesManager, AZ::SystemAllocator, 0);
|
||||
|
||||
using EntityIdMap = AZStd::unordered_map<AZ::EntityId, AZ::EntityId>;
|
||||
|
||||
|
||||
enum class CommandQueueStatus : bool
|
||||
{
|
||||
HasCommandsLeft,
|
||||
@@ -57,26 +57,21 @@ namespace AzFramework
|
||||
// The following functions are thread safe
|
||||
//
|
||||
|
||||
void SpawnAllEntities(
|
||||
EntitySpawnTicket& ticket, SpawnablePriority priority, EntityPreInsertionCallback preInsertionCallback = {},
|
||||
EntitySpawnCallback completionCallback = {}) override;
|
||||
void SpawnAllEntities(EntitySpawnTicket& ticket, SpawnAllEntitiesOptionalArgs optionalArgs = {}) override;
|
||||
void SpawnEntities(
|
||||
EntitySpawnTicket& ticket, SpawnablePriority priority, AZStd::vector<size_t> entityIndices,
|
||||
EntityPreInsertionCallback preInsertionCallback = {},
|
||||
EntitySpawnCallback completionCallback = {}) override;
|
||||
void DespawnAllEntities(
|
||||
EntitySpawnTicket& ticket, SpawnablePriority priority, EntityDespawnCallback completionCallback = {}) override;
|
||||
|
||||
EntitySpawnTicket& ticket, AZStd::vector<size_t> entityIndices, SpawnEntitiesOptionalArgs optionalArgs = {}) override;
|
||||
void DespawnAllEntities(EntitySpawnTicket& ticket, DespawnAllEntitiesOptionalArgs optionalArgs = {}) override;
|
||||
void ReloadSpawnable(
|
||||
EntitySpawnTicket& ticket, SpawnablePriority priority, AZ::Data::Asset<Spawnable> spawnable,
|
||||
ReloadSpawnableCallback completionCallback = {}) override;
|
||||
EntitySpawnTicket& ticket, AZ::Data::Asset<Spawnable> spawnable, ReloadSpawnableOptionalArgs optionalArgs = {}) override;
|
||||
|
||||
void ListEntities(EntitySpawnTicket& ticket, SpawnablePriority priority, ListEntitiesCallback listCallback) override;
|
||||
void ListEntities(
|
||||
EntitySpawnTicket& ticket, ListEntitiesCallback listCallback, ListEntitiesOptionalArgs optionalArgs = {}) override;
|
||||
void ListIndicesAndEntities(
|
||||
EntitySpawnTicket& ticket, SpawnablePriority priority, ListIndicesEntitiesCallback listCallback) override;
|
||||
void ClaimEntities(EntitySpawnTicket& ticket, SpawnablePriority priority, ClaimEntitiesCallback listCallback) override;
|
||||
EntitySpawnTicket& ticket, ListIndicesEntitiesCallback listCallback, ListEntitiesOptionalArgs optionalArgs = {}) override;
|
||||
void ClaimEntities(
|
||||
EntitySpawnTicket& ticket, ClaimEntitiesCallback listCallback, ClaimEntitiesOptionalArgs optionalArgs = {}) override;
|
||||
|
||||
void Barrier(EntitySpawnTicket& spawnInfo, SpawnablePriority priority, 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;
|
||||
@@ -105,6 +100,7 @@ namespace AzFramework
|
||||
{
|
||||
EntitySpawnCallback m_completionCallback;
|
||||
EntityPreInsertionCallback m_preInsertionCallback;
|
||||
AZ::SerializeContext* m_serializeContext;
|
||||
Ticket* m_ticket;
|
||||
EntitySpawnTicket::Id m_ticketId;
|
||||
uint32_t m_requestId;
|
||||
@@ -114,9 +110,11 @@ namespace AzFramework
|
||||
AZStd::vector<size_t> m_entityIndices;
|
||||
EntitySpawnCallback m_completionCallback;
|
||||
EntityPreInsertionCallback m_preInsertionCallback;
|
||||
AZ::SerializeContext* m_serializeContext;
|
||||
Ticket* m_ticket;
|
||||
EntitySpawnTicket::Id m_ticketId;
|
||||
uint32_t m_requestId;
|
||||
bool m_referencePreviouslySpawnedEntities;
|
||||
};
|
||||
struct DespawnAllEntitiesCommand
|
||||
{
|
||||
@@ -129,6 +127,7 @@ namespace AzFramework
|
||||
{
|
||||
AZ::Data::Asset<Spawnable> m_spawnable;
|
||||
ReloadSpawnableCallback m_completionCallback;
|
||||
AZ::SerializeContext* m_serializeContext;
|
||||
Ticket* m_ticket;
|
||||
EntitySpawnTicket::Id m_ticketId;
|
||||
uint32_t m_requestId;
|
||||
@@ -185,21 +184,18 @@ namespace AzFramework
|
||||
|
||||
CommandQueueStatus ProcessQueue(Queue& queue);
|
||||
|
||||
AZ::Entity* SpawnSingleEntity(const AZ::Entity& entityTemplate,
|
||||
AZ::SerializeContext& serializeContext);
|
||||
|
||||
AZ::Entity* CloneSingleEntity(const AZ::Entity& entityTemplate,
|
||||
EntityIdMap& templateToCloneEntityIdMap, AZ::SerializeContext& serializeContext);
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
Queue m_highPriorityQueue;
|
||||
Queue m_regularPriorityQueue;
|
||||
@@ -207,6 +203,7 @@ namespace AzFramework
|
||||
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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
+1
@@ -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)
|
||||
|
||||
+14
-11
@@ -12,17 +12,22 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzToolsFramework/Manipulators/ManipulatorManager.h>
|
||||
#include <AzToolsFramework/Manipulators/ManipulatorBus.h>
|
||||
#include <AzToolsFramework/Manipulators/LinearManipulator.h>
|
||||
#include <AzToolsFramework/Manipulators/ManipulatorBus.h>
|
||||
#include <AzToolsFramework/Manipulators/ManipulatorManager.h>
|
||||
#include <AzToolsFramework/Manipulators/PlanarManipulator.h>
|
||||
|
||||
namespace AzManipulatorTestFramework
|
||||
{
|
||||
//! Create a linear manipulator with a unit sphere bounds.
|
||||
//! Create a linear manipulator with a unit sphere bound.
|
||||
AZStd::shared_ptr<AzToolsFramework::LinearManipulator> CreateLinearManipulator(
|
||||
const AzToolsFramework::ManipulatorManagerId manipulatorManagerId,
|
||||
const AZ::Vector3& position = AZ::Vector3::CreateZero(),
|
||||
const float radius = 1.0f);
|
||||
const AzToolsFramework::ManipulatorManagerId manipulatorManagerId, const AZ::Vector3& position = AZ::Vector3::CreateZero(),
|
||||
float radius = 1.0f);
|
||||
|
||||
//! Create a planar manipulator with a unit sphere bound.
|
||||
AZStd::shared_ptr<AzToolsFramework::PlanarManipulator> CreatePlanarManipulator(
|
||||
const AzToolsFramework::ManipulatorManagerId manipulatorManagerId, const AZ::Vector3& position = AZ::Vector3::CreateZero(),
|
||||
float radius = 1.0f);
|
||||
|
||||
//! Create a mouse pick from the specified ray and screen point.
|
||||
AzToolsFramework::ViewportInteraction::MousePick CreateMousePick(
|
||||
@@ -34,14 +39,12 @@ namespace AzManipulatorTestFramework
|
||||
|
||||
//! Create a mouse interaction from the specified pick, buttons, interaction id and keyboard modifiers.
|
||||
AzToolsFramework::ViewportInteraction::MouseInteraction CreateMouseInteraction(
|
||||
const AzToolsFramework::ViewportInteraction::MousePick& mousePick,
|
||||
AzToolsFramework::ViewportInteraction::MouseButtons buttons,
|
||||
const AzToolsFramework::ViewportInteraction::MousePick& mousePick, AzToolsFramework::ViewportInteraction::MouseButtons buttons,
|
||||
AzToolsFramework::ViewportInteraction::InteractionId interactionId,
|
||||
AzToolsFramework::ViewportInteraction::KeyboardModifiers modifiers);
|
||||
|
||||
//! Create a mouse buttons from the specified mouse button.
|
||||
AzToolsFramework::ViewportInteraction::MouseButtons CreateMouseButtons(
|
||||
AzToolsFramework::ViewportInteraction::MouseButton button);
|
||||
AzToolsFramework::ViewportInteraction::MouseButtons CreateMouseButtons(AzToolsFramework::ViewportInteraction::MouseButton button);
|
||||
|
||||
//! Create a mouse interaction event from the specified interaction and event.
|
||||
AzToolsFramework::ViewportInteraction::MouseInteractionEvent CreateMouseInteractionEvent(
|
||||
@@ -61,5 +64,5 @@ namespace AzManipulatorTestFramework
|
||||
AzFramework::ScreenPoint GetCameraStateViewportCenter(const AzFramework::CameraState& cameraState);
|
||||
|
||||
//! Default viewport size (1080p) in 16:9 aspect ratio.
|
||||
const auto DefaultViewportSize = AZ::Vector2(1920.0f, 1080.0f);
|
||||
inline const auto DefaultViewportSize = AZ::Vector2(1920.0f, 1080.0f);
|
||||
} // namespace AzManipulatorTestFramework
|
||||
|
||||
+36
-22
@@ -14,7 +14,6 @@
|
||||
#include <AzFramework/Viewport/ViewportScreen.h>
|
||||
#include <AzManipulatorTestFramework/AzManipulatorTestFrameworkUtils.h>
|
||||
#include <AzToolsFramework/ViewportSelection/EditorInteractionSystemViewportSelectionRequestBus.h>
|
||||
#include <AzToolsFramework/ViewportSelection/EditorInteractionSystemViewportSelectionRequestBus.h>
|
||||
#include <AzToolsFramework/ViewportSelection/EditorTransformComponentSelectionRequestBus.h>
|
||||
|
||||
namespace AzManipulatorTestFramework
|
||||
@@ -28,22 +27,21 @@ namespace AzManipulatorTestFramework
|
||||
using MouseEvent = AzToolsFramework::ViewportInteraction::MouseEvent;
|
||||
using MousePick = AzToolsFramework::ViewportInteraction::MousePick;
|
||||
|
||||
AZStd::shared_ptr<AzToolsFramework::LinearManipulator> CreateLinearManipulator(
|
||||
const AzToolsFramework::ManipulatorManagerId manipulatorManagerId,
|
||||
const AZ::Vector3& position,
|
||||
const float radius)
|
||||
// create a default sphere view for a manipulator for simple intersection
|
||||
template<typename Manipulator>
|
||||
void SetupManipulatorView(
|
||||
AZStd::shared_ptr<Manipulator> manipulator, const AzToolsFramework::ManipulatorManagerId manipulatorManagerId,
|
||||
const AZ::Vector3& position, const float radius)
|
||||
{
|
||||
auto manipulator = AzToolsFramework::LinearManipulator::MakeShared(AZ::Transform::CreateIdentity());
|
||||
manipulator->SetLocalPosition(position);
|
||||
|
||||
// unit sphere view
|
||||
auto sphereView = AzToolsFramework::CreateManipulatorViewSphere(
|
||||
AZ::Colors::Red, radius,
|
||||
[](const MouseInteraction& /*mouseInteraction*/, const bool /*mouseOver*/,
|
||||
const AZ::Color& defaultColor)
|
||||
{
|
||||
return defaultColor;
|
||||
}, true);
|
||||
[]([[maybe_unused]] const MouseInteraction& mouseInteraction, [[maybe_unused]] const bool mouseOver,
|
||||
const AZ::Color& defaultColor)
|
||||
{
|
||||
return defaultColor;
|
||||
},
|
||||
true);
|
||||
|
||||
// unit sphere bound
|
||||
AzToolsFramework::Picking::BoundShapeSphere sphereBound;
|
||||
@@ -62,6 +60,26 @@ namespace AzManipulatorTestFramework
|
||||
// this would occur internally when the manipulator is drawn but we must do manually here to ensure that the
|
||||
// bounds will always be valid upon instantiation
|
||||
view->RefreshBound(manipulatorManagerId, manipulator->GetManipulatorId(), sphereBound);
|
||||
}
|
||||
|
||||
AZStd::shared_ptr<AzToolsFramework::LinearManipulator> CreateLinearManipulator(
|
||||
const AzToolsFramework::ManipulatorManagerId manipulatorManagerId, const AZ::Vector3& position, const float radius)
|
||||
{
|
||||
auto manipulator = AzToolsFramework::LinearManipulator::MakeShared(AZ::Transform::CreateIdentity());
|
||||
manipulator->SetLocalPosition(position);
|
||||
|
||||
SetupManipulatorView(manipulator, manipulatorManagerId, position, radius);
|
||||
|
||||
return manipulator;
|
||||
}
|
||||
|
||||
AZStd::shared_ptr<AzToolsFramework::PlanarManipulator> CreatePlanarManipulator(
|
||||
const AzToolsFramework::ManipulatorManagerId manipulatorManagerId, const AZ::Vector3& position, const float radius)
|
||||
{
|
||||
auto manipulator = AzToolsFramework::PlanarManipulator::MakeShared(AZ::Transform::CreateIdentity());
|
||||
manipulator->SetLocalPosition(position);
|
||||
|
||||
SetupManipulatorView(manipulator, manipulatorManagerId, position, radius);
|
||||
|
||||
return manipulator;
|
||||
}
|
||||
@@ -104,8 +122,7 @@ namespace AzManipulatorTestFramework
|
||||
return buttons;
|
||||
}
|
||||
|
||||
MouseInteractionEvent CreateMouseInteractionEvent(
|
||||
const MouseInteraction& mouseInteraction, MouseEvent event)
|
||||
MouseInteractionEvent CreateMouseInteractionEvent(const MouseInteraction& mouseInteraction, MouseEvent event)
|
||||
{
|
||||
return MouseInteractionEvent(mouseInteraction, event);
|
||||
}
|
||||
@@ -114,8 +131,7 @@ namespace AzManipulatorTestFramework
|
||||
{
|
||||
AzToolsFramework::EditorInteractionSystemViewportSelectionRequestBus::Event(
|
||||
AzToolsFramework::GetEntityContextId(),
|
||||
&AzToolsFramework::ViewportInteraction::InternalMouseViewportRequests::InternalHandleAllMouseInteractions,
|
||||
event);
|
||||
&AzToolsFramework::ViewportInteraction::InternalMouseViewportRequests::InternalHandleAllMouseInteractions, event);
|
||||
}
|
||||
|
||||
AzFramework::CameraState SetCameraStatePosition(const AZ::Vector3& position, AzFramework::CameraState& cameraState)
|
||||
@@ -133,9 +149,7 @@ namespace AzManipulatorTestFramework
|
||||
|
||||
AzFramework::ScreenPoint GetCameraStateViewportCenter(const AzFramework::CameraState& cameraState)
|
||||
{
|
||||
return {
|
||||
aznumeric_cast<int>(cameraState.m_viewportSize.GetX() / 2.f),
|
||||
aznumeric_cast<int>(cameraState.m_viewportSize.GetY() / 2.f)
|
||||
};
|
||||
return { aznumeric_cast<int>(cameraState.m_viewportSize.GetX() / 2.f),
|
||||
aznumeric_cast<int>(cameraState.m_viewportSize.GetY() / 2.f) };
|
||||
}
|
||||
} // namespace UnitTest
|
||||
} // namespace AzManipulatorTestFramework
|
||||
|
||||
@@ -10,52 +10,55 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "AzManipulatorTestFrameworkTestFixtures.h"
|
||||
|
||||
#include <AzCore/std/smart_ptr/unique_ptr.h>
|
||||
#include <AzFramework/Viewport/ViewportScreen.h>
|
||||
#include <AzManipulatorTestFramework/AzManipulatorTestFramework.h>
|
||||
#include "AzManipulatorTestFrameworkTestFixtures.h"
|
||||
#include <AzManipulatorTestFramework/DirectManipulatorViewportInteraction.h>
|
||||
#include <AzManipulatorTestFramework/IndirectManipulatorViewportInteraction.h>
|
||||
#include <AzManipulatorTestFramework/ImmediateModeActionDispatcher.h>
|
||||
#include <AzManipulatorTestFramework/AzManipulatorTestFrameworkUtils.h>
|
||||
#include <AzManipulatorTestFramework/DirectManipulatorViewportInteraction.h>
|
||||
#include <AzManipulatorTestFramework/ImmediateModeActionDispatcher.h>
|
||||
#include <AzManipulatorTestFramework/IndirectManipulatorViewportInteraction.h>
|
||||
#include <AzToolsFramework/Manipulators/LinearManipulator.h>
|
||||
#include <AzToolsFramework/Manipulators/PlanarManipulator.h>
|
||||
#include <AzToolsFramework/ViewportSelection/EditorSelectionUtil.h>
|
||||
#include <AZTestShared/Math/MathTestHelpers.h>
|
||||
|
||||
namespace UnitTest
|
||||
{
|
||||
class GridSnappingFixture
|
||||
: public ToolsApplicationFixture
|
||||
class GridSnappingFixture : public ToolsApplicationFixture
|
||||
{
|
||||
public:
|
||||
GridSnappingFixture()
|
||||
: m_viewportManipulatorInteraction(AZStd::make_unique<AzManipulatorTestFramework::DirectCallManipulatorViewportInteraction>())
|
||||
, m_actionDispatcher(AZStd::make_unique<AzManipulatorTestFramework::ImmediateModeActionDispatcher>(*m_viewportManipulatorInteraction))
|
||||
, m_linearManipulator(
|
||||
AzManipulatorTestFramework::CreateLinearManipulator(
|
||||
m_viewportManipulatorInteraction->GetManipulatorManager().GetId(),
|
||||
/*position=*/AZ::Vector3(0.0f, 50.0f, 0.0f),
|
||||
/*radius=*/m_boundsRadius))
|
||||
{}
|
||||
, m_actionDispatcher(
|
||||
AZStd::make_unique<AzManipulatorTestFramework::ImmediateModeActionDispatcher>(*m_viewportManipulatorInteraction))
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
void SetUpEditorFixtureImpl() override
|
||||
{
|
||||
m_cameraState = AzFramework::CreateIdentityDefaultCamera(
|
||||
AZ::Vector3::CreateZero(), AzManipulatorTestFramework::DefaultViewportSize);
|
||||
m_cameraState =
|
||||
AzFramework::CreateIdentityDefaultCamera(AZ::Vector3::CreateZero(), AzManipulatorTestFramework::DefaultViewportSize);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
const float m_boundsRadius = 1.0f;
|
||||
AZStd::unique_ptr<AzManipulatorTestFramework::ManipulatorViewportInteraction> m_viewportManipulatorInteraction;
|
||||
AZStd::unique_ptr<AzManipulatorTestFramework::ImmediateModeActionDispatcher> m_actionDispatcher;
|
||||
AZStd::shared_ptr<AzToolsFramework::LinearManipulator> m_linearManipulator;
|
||||
AzFramework::CameraState m_cameraState;
|
||||
};
|
||||
|
||||
TEST_F(GridSnappingFixture, MouseDownWithSnappingEnabledSnapsToClosestGridSize)
|
||||
{
|
||||
AZStd::shared_ptr<AzToolsFramework::LinearManipulator> linearManipulator(AzManipulatorTestFramework::CreateLinearManipulator(
|
||||
m_viewportManipulatorInteraction->GetManipulatorManager().GetId(),
|
||||
/*position=*/AZ::Vector3(0.0f, 50.0f, 0.0f),
|
||||
/*radius=*/m_boundsRadius));
|
||||
|
||||
// the initial starting position of the manipulator (in front of the camera)
|
||||
const auto initialPositionWorld = m_linearManipulator->GetLocalPosition();
|
||||
const auto initialPositionWorld = linearManipulator->GetLocalPosition();
|
||||
// where the manipulator should end up (in front and to the left of the camera)
|
||||
const auto finalPositionWorld = AZ::Vector3(-10.0f, 50.0f, 0.0f);
|
||||
// perspective scale factor for manipulator distance to camera
|
||||
@@ -66,21 +69,18 @@ namespace UnitTest
|
||||
// adjusted final world position taking into account the manipulator position relative to the camera
|
||||
const auto finalPositionWorldAdjusted = finalPositionWorld - (vectorToInitialPositionWorld * scaledRadiusBound);
|
||||
// calculate the position in screen space of the initial position of the manipulator
|
||||
const auto initialPositionScreen =
|
||||
AzFramework::WorldToScreen(initialPositionWorld, m_cameraState);
|
||||
const auto initialPositionScreen = AzFramework::WorldToScreen(initialPositionWorld, m_cameraState);
|
||||
// calculate the position in screen space of the final position of the manipulator
|
||||
const auto finalPositionScreen = AzFramework::WorldToScreen(finalPositionWorldAdjusted, m_cameraState);
|
||||
|
||||
// callback to update the manipulator's current position
|
||||
m_linearManipulator->InstallMouseMoveCallback(
|
||||
[this](const AzToolsFramework::LinearManipulator::Action& action)
|
||||
{
|
||||
auto pos = action.LocalPosition();
|
||||
m_linearManipulator->SetLocalPosition(pos);
|
||||
});
|
||||
linearManipulator->InstallMouseMoveCallback(
|
||||
[this, linearManipulator](const AzToolsFramework::LinearManipulator::Action& action)
|
||||
{
|
||||
linearManipulator->SetLocalPosition(action.LocalPosition());
|
||||
});
|
||||
|
||||
m_actionDispatcher
|
||||
->EnableSnapToGrid()
|
||||
m_actionDispatcher->EnableSnapToGrid()
|
||||
->GridSize(5.0f)
|
||||
->CameraState(m_cameraState)
|
||||
->MousePosition(initialPositionScreen)
|
||||
@@ -89,7 +89,67 @@ namespace UnitTest
|
||||
->MousePosition(finalPositionScreen)
|
||||
->MouseLButtonUp()
|
||||
->ExpectManipulatorNotBeingInteracted()
|
||||
->ExpectTrue(m_linearManipulator->GetLocalPosition().IsClose(finalPositionWorld, 0.01f))
|
||||
;
|
||||
->ExpectTrue(linearManipulator->GetLocalPosition().IsClose(finalPositionWorld, 0.01f));
|
||||
}
|
||||
|
||||
template<typename Manipulator>
|
||||
void ValidateManipulatorSnappingBehavior(
|
||||
AZStd::shared_ptr<Manipulator> manipulator, AzManipulatorTestFramework::ImmediateModeActionDispatcher* actionDispatcher,
|
||||
const AzFramework::CameraState& cameraState)
|
||||
{
|
||||
manipulator->SetLocalOrientation(AZ::Quaternion::CreateFromEulerAnglesDegrees(AZ::Vector3(180.0f, 0.0f, 135.0f)));
|
||||
|
||||
// the initial starting position of the manipulator (in front of the camera)
|
||||
const auto initialPositionWorld = manipulator->GetLocalPosition() + AZ::Vector3::CreateAxisX(0.15f);
|
||||
// where the manipulator should end up (unmoved)
|
||||
const auto finalPositionWorld = manipulator->GetLocalPosition();
|
||||
// where we should move the mouse to
|
||||
const auto attemptPositionWorld = manipulator->GetLocalPosition() + AZ::Vector3::CreateAxisX(0.35f);
|
||||
// calculate the position in screen space of the initial position of the manipulator
|
||||
const auto initialPositionScreen = AzFramework::WorldToScreen(initialPositionWorld, cameraState);
|
||||
// calculate the position in screen space of the final position of the manipulator
|
||||
const auto attemptPositionScreen = AzFramework::WorldToScreen(attemptPositionWorld, cameraState);
|
||||
|
||||
// callback to update the manipulator's current position
|
||||
manipulator->InstallMouseMoveCallback(
|
||||
[manipulator](const typename Manipulator::Action& action)
|
||||
{
|
||||
manipulator->SetLocalPosition(action.LocalPosition());
|
||||
});
|
||||
|
||||
actionDispatcher->EnableSnapToGrid()
|
||||
->GridSize(1.0f)
|
||||
->CameraState(cameraState)
|
||||
->MousePosition(initialPositionScreen)
|
||||
->MouseLButtonDown()
|
||||
->ExpectManipulatorBeingInteracted()
|
||||
->MousePosition(attemptPositionScreen)
|
||||
->MouseLButtonUp()
|
||||
->ExpectManipulatorNotBeingInteracted()
|
||||
->ExpectThat(manipulator->GetLocalPosition(), IsCloseTolerance(finalPositionWorld, 0.01f));
|
||||
}
|
||||
|
||||
TEST_F(GridSnappingFixture, MouseDownAndMoveLinearManipulatorDoesNotSnapWithMovementSmallerThanHalfGridSize)
|
||||
{
|
||||
AZStd::shared_ptr<AzToolsFramework::LinearManipulator> linearManipulator(AzManipulatorTestFramework::CreateLinearManipulator(
|
||||
m_viewportManipulatorInteraction->GetManipulatorManager().GetId(),
|
||||
/*position=*/AZ::Vector3(0.0f, 10.0f, 0.0f),
|
||||
/*radius=*/m_boundsRadius));
|
||||
|
||||
linearManipulator->SetAxis(AZ::Vector3::CreateAxisY());
|
||||
|
||||
ValidateManipulatorSnappingBehavior(linearManipulator, m_actionDispatcher.get(), m_cameraState);
|
||||
}
|
||||
|
||||
TEST_F(GridSnappingFixture, MouseDownAndMovePlanarManipulatorDoesNotSnapWithMovementSmallerThanHalfGridSize)
|
||||
{
|
||||
AZStd::shared_ptr<AzToolsFramework::PlanarManipulator> planarManipulator(AzManipulatorTestFramework::CreatePlanarManipulator(
|
||||
m_viewportManipulatorInteraction->GetManipulatorManager().GetId(),
|
||||
/*position=*/AZ::Vector3(0.0f, 10.0f, 0.0f),
|
||||
/*radius=*/m_boundsRadius));
|
||||
|
||||
planarManipulator->SetAxes(AZ::Vector3::CreateAxisY(), AZ::Vector3::CreateAxisZ());
|
||||
|
||||
ValidateManipulatorSnappingBehavior(planarManipulator, m_actionDispatcher.get(), m_cameraState);
|
||||
}
|
||||
} // namespace UnitTest
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -433,6 +433,11 @@ namespace AzToolsFramework
|
||||
return m_manipulatorSpaceWithLocalTransform.GetSpace();
|
||||
}
|
||||
|
||||
const AZ::Vector3& Manipulators::GetNonUniformScale() const
|
||||
{
|
||||
return m_manipulatorSpaceWithLocalTransform.GetNonUniformScale();
|
||||
}
|
||||
|
||||
void Manipulators::SetSpace(const AZ::Transform& worldFromLocal)
|
||||
{
|
||||
m_manipulatorSpaceWithLocalTransform.SetSpace(worldFromLocal);
|
||||
|
||||
+13
-14
@@ -192,8 +192,7 @@ namespace AzToolsFramework
|
||||
/// for each vertex associated with the translation manipulator to use with offset calculations when updating.
|
||||
template<typename Vertex>
|
||||
void InitializeVertexLookup(
|
||||
IndexedTranslationManipulator<Vertex>& translationManipulator,
|
||||
const AZ::EntityId entityId, const AZ::Vector3& snapOffset)
|
||||
IndexedTranslationManipulator<Vertex>& translationManipulator, const AZ::EntityId entityId)
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework);
|
||||
|
||||
@@ -202,7 +201,7 @@ namespace AzToolsFramework
|
||||
AZ::FixedVerticesRequestBus<Vertex>::Bind(fixedVertices, entityId);
|
||||
|
||||
translationManipulator.Process(
|
||||
[snapOffset, fixedVertices]
|
||||
[fixedVertices]
|
||||
(typename IndexedTranslationManipulator<Vertex>::VertexLookup& vertexLookup)
|
||||
{
|
||||
Vertex vertex;
|
||||
@@ -213,7 +212,7 @@ namespace AzToolsFramework
|
||||
|
||||
if (found)
|
||||
{
|
||||
vertexLookup.m_start = vertex + AZ::AdaptVertexIn<Vertex>(snapOffset);
|
||||
vertexLookup.m_start = vertex;
|
||||
vertexLookup.m_offset = Vertex::CreateZero();
|
||||
}
|
||||
});
|
||||
@@ -250,10 +249,10 @@ namespace AzToolsFramework
|
||||
|
||||
// linear manipulator callbacks
|
||||
m_translationManipulator->m_manipulator.InstallLinearManipulatorMouseDownCallback(
|
||||
[this](const LinearManipulator::Action& action)
|
||||
[this]([[maybe_unused]] const LinearManipulator::Action& action)
|
||||
{
|
||||
BeginBatchMovement();
|
||||
InitializeVertexLookup(*m_translationManipulator, GetEntityId(), action.m_start.m_positionSnapOffset);
|
||||
InitializeVertexLookup(*m_translationManipulator, GetEntityId());
|
||||
});
|
||||
|
||||
m_translationManipulator->m_manipulator.InstallLinearManipulatorMouseMoveCallback(
|
||||
@@ -264,17 +263,17 @@ namespace AzToolsFramework
|
||||
});
|
||||
|
||||
m_translationManipulator->m_manipulator.InstallLinearManipulatorMouseUpCallback(
|
||||
[this](const LinearManipulator::Action& /*action*/)
|
||||
[this]([[maybe_unused]] const LinearManipulator::Action& action)
|
||||
{
|
||||
EndBatchMovement();
|
||||
});
|
||||
|
||||
// planar manipulator callbacks
|
||||
m_translationManipulator->m_manipulator.InstallPlanarManipulatorMouseDownCallback(
|
||||
[this](const PlanarManipulator::Action& action)
|
||||
[this]([[maybe_unused]] const PlanarManipulator::Action& action)
|
||||
{
|
||||
BeginBatchMovement();
|
||||
InitializeVertexLookup(*m_translationManipulator, GetEntityId(), action.m_start.m_snapOffset);
|
||||
InitializeVertexLookup(*m_translationManipulator, GetEntityId());
|
||||
});
|
||||
|
||||
m_translationManipulator->m_manipulator.InstallPlanarManipulatorMouseMoveCallback(
|
||||
@@ -285,17 +284,17 @@ namespace AzToolsFramework
|
||||
});
|
||||
|
||||
m_translationManipulator->m_manipulator.InstallPlanarManipulatorMouseUpCallback(
|
||||
[this](const PlanarManipulator::Action& /*action*/)
|
||||
[this]([[maybe_unused]] const PlanarManipulator::Action& action)
|
||||
{
|
||||
EndBatchMovement();
|
||||
});
|
||||
|
||||
// surface manipulator callbacks
|
||||
m_translationManipulator->m_manipulator.InstallSurfaceManipulatorMouseDownCallback(
|
||||
[this](const SurfaceManipulator::Action& action)
|
||||
[this]([[maybe_unused]] const SurfaceManipulator::Action& action)
|
||||
{
|
||||
BeginBatchMovement();
|
||||
InitializeVertexLookup(*m_translationManipulator, GetEntityId(), action.m_start.m_snapOffset);
|
||||
InitializeVertexLookup(*m_translationManipulator, GetEntityId());
|
||||
});
|
||||
|
||||
m_translationManipulator->m_manipulator.InstallSurfaceManipulatorMouseMoveCallback(
|
||||
@@ -306,7 +305,7 @@ namespace AzToolsFramework
|
||||
});
|
||||
|
||||
m_translationManipulator->m_manipulator.InstallSurfaceManipulatorMouseUpCallback(
|
||||
[this](const SurfaceManipulator::Action& /*action*/)
|
||||
[this]([[maybe_unused]] const SurfaceManipulator::Action& action)
|
||||
{
|
||||
EndBatchMovement();
|
||||
});
|
||||
@@ -893,7 +892,7 @@ namespace AzToolsFramework
|
||||
{
|
||||
BeginBatchMovement();
|
||||
|
||||
InitializeVertexLookup(*m_translationManipulator, GetEntityId(), AZ::Vector3::CreateZero());
|
||||
InitializeVertexLookup(*m_translationManipulator, GetEntityId());
|
||||
// note: AdaptVertexIn/Out is to ensure we clamp the vertex local Z position to 0 if
|
||||
// dealing with Vector2s when setting the position of the manipulator.
|
||||
const AZ::Vector3 localOffset =
|
||||
|
||||
+26
-44
@@ -23,8 +23,8 @@ namespace AzToolsFramework
|
||||
{
|
||||
LinearManipulator::Starter CalculateLinearManipulationDataStart(
|
||||
const LinearManipulator::Fixed& fixed, const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale,
|
||||
const AZ::Transform& localTransform, const GridSnapAction& gridSnapAction, const ViewportInteraction::MouseInteraction& interaction,
|
||||
const float intersectionDistance, const AzFramework::CameraState& cameraState)
|
||||
const AZ::Transform& localTransform, const ViewportInteraction::MouseInteraction& interaction, const float intersectionDistance,
|
||||
const AzFramework::CameraState& cameraState)
|
||||
{
|
||||
const ManipulatorInteraction manipulatorInteraction =
|
||||
BuildManipulatorInteraction(
|
||||
@@ -50,28 +50,9 @@ namespace AzToolsFramework
|
||||
manipulatorInteraction.m_localRayOrigin, manipulatorInteraction.m_localRayDirection,
|
||||
localIntersectionPoint, startTransition.m_localNormal, start.m_localHitPosition);
|
||||
|
||||
const float gridSize = gridSnapAction.m_gridSnapParams.m_gridSize;
|
||||
const bool snapping = gridSnapAction.m_gridSnapParams.m_gridSnap;
|
||||
const float scaleRecip = manipulatorInteraction.m_scaleReciprocal;
|
||||
|
||||
// calculate position amount to snap, to align with grid
|
||||
const AZ::Vector3 positionSnapOffset = snapping && !gridSnapAction.m_localSnapping
|
||||
? CalculateSnappedOffset(localTransform.GetTranslation(), axis, gridSize * scaleRecip)
|
||||
: AZ::Vector3::CreateZero();
|
||||
|
||||
const AZ::Vector3 localScale = AZ::Vector3(localTransform.GetUniformScale());
|
||||
const AZ::Quaternion localRotation = QuaternionFromTransformNoScaling(localTransform);
|
||||
// calculate scale amount to snap, to align to round scale value
|
||||
const AZ::Vector3 scaleSnapOffset = snapping && !gridSnapAction.m_localSnapping
|
||||
? localRotation.GetInverseFull().TransformVector(CalculateSnappedOffset(
|
||||
localRotation.TransformVector(localScale), axis, gridSize * scaleRecip))
|
||||
: AZ::Vector3::CreateZero();
|
||||
|
||||
start.m_screenPosition = interaction.m_mousePick.m_screenCoordinates;
|
||||
start.m_positionSnapOffset = positionSnapOffset;
|
||||
start.m_scaleSnapOffset = scaleSnapOffset;
|
||||
start.m_localPosition = localTransform.GetTranslation() + positionSnapOffset;
|
||||
start.m_localScale = localScale + scaleSnapOffset;
|
||||
start.m_localPosition = localTransform.GetTranslation();
|
||||
start.m_localScale = AZ::Vector3(localTransform.GetUniformScale());;
|
||||
start.m_localAxis = axis;
|
||||
// sign to determine which side of the linear axis we pressed
|
||||
// (useful to know when the visual axis flips to face the camera)
|
||||
@@ -87,7 +68,7 @@ namespace AzToolsFramework
|
||||
LinearManipulator::Action CalculateLinearManipulationDataAction(
|
||||
const LinearManipulator::Fixed& fixed, const LinearManipulator::Starter& starter,
|
||||
const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale, const AZ::Transform& localTransform,
|
||||
const GridSnapAction& gridSnapAction, const ViewportInteraction::MouseInteraction& interaction)
|
||||
const GridSnapParameters& gridSnapParams, const ViewportInteraction::MouseInteraction& interaction)
|
||||
{
|
||||
const ManipulatorInteraction manipulatorInteraction =
|
||||
BuildManipulatorInteraction(
|
||||
@@ -108,31 +89,34 @@ namespace AzToolsFramework
|
||||
GetCameraState(interaction.m_interactionId.m_viewportId));
|
||||
|
||||
const AZ::Vector3 axis = TransformDirectionNoScaling(localTransform, fixed.m_axis);
|
||||
// The local positions have been transformed to the reference frame of the object being manipulated. But they appear in the world
|
||||
// with non-uniform scale applied, and the object being manipulated will want to work with unscaled deltas, so we need to divide by
|
||||
// the non-uniform scale here.
|
||||
// the local positions have been transformed to the reference frame of the object being manipulated, but they appear in the world
|
||||
// with non-uniform scale applied, the object being manipulated will want to work with unscaled deltas, so we need to divide by
|
||||
// the non-uniform scale here
|
||||
const AZ::Vector3 hitDelta = (localHitPosition - start.m_localHitPosition) / nonUniformScale;
|
||||
const AZ::Vector3 unsnappedOffset = axis * axis.Dot(hitDelta);
|
||||
|
||||
const float scaleRecip = manipulatorInteraction.m_scaleReciprocal * axis.Dot(manipulatorInteraction.m_nonUniformScaleReciprocal);
|
||||
const float gridSize = gridSnapAction.m_gridSnapParams.m_gridSize;
|
||||
const bool snapping = gridSnapAction.m_gridSnapParams.m_gridSnap;
|
||||
const float scaleRecip =
|
||||
manipulatorInteraction.m_scaleReciprocal * fixed.m_axis.Dot(manipulatorInteraction.m_nonUniformScaleReciprocal);
|
||||
const float gridSize = gridSnapParams.m_gridSize;
|
||||
const bool snapping = gridSnapParams.m_gridSnap;
|
||||
|
||||
LinearManipulator::Action action;
|
||||
action.m_fixed = fixed;
|
||||
action.m_start = start;
|
||||
action.m_current.m_localPositionOffset = snapping
|
||||
? unsnappedOffset + CalculateSnappedOffset(unsnappedOffset, axis, gridSize * scaleRecip)
|
||||
? CalculateSnappedAmount(unsnappedOffset, axis, gridSize * scaleRecip)
|
||||
: unsnappedOffset;
|
||||
action.m_current.m_screenPosition = interaction.m_mousePick.m_screenCoordinates;
|
||||
action.m_viewportId = interaction.m_interactionId.m_viewportId;
|
||||
|
||||
const AZ::Quaternion localRotation = QuaternionFromTransformNoScaling(localTransform);
|
||||
const AZ::Vector3 scaledUnsnappedOffset = unsnappedOffset * startTransition.m_screenToWorldScale * NonUniformScaleReciprocal(nonUniformScale);
|
||||
const AZ::Vector3 scaledUnsnappedOffset =
|
||||
unsnappedOffset * startTransition.m_screenToWorldScale * NonUniformScaleReciprocal(nonUniformScale);
|
||||
|
||||
// how much to adjust the scale based on movement
|
||||
const AZ::Quaternion invLocalRotation = localRotation.GetInverseFull();
|
||||
action.m_current.m_localScaleOffset = snapping
|
||||
? invLocalRotation.TransformVector((scaledUnsnappedOffset + CalculateSnappedOffset(scaledUnsnappedOffset, axis, gridSize * scaleRecip)))
|
||||
? invLocalRotation.TransformVector(CalculateSnappedAmount(scaledUnsnappedOffset, axis, gridSize * scaleRecip))
|
||||
: invLocalRotation.TransformVector(scaledUnsnappedOffset);
|
||||
|
||||
// record what modifier keys are held during this action
|
||||
@@ -171,19 +155,18 @@ namespace AzToolsFramework
|
||||
const ViewportInteraction::MouseInteraction& interaction, const float rayIntersectionDistance)
|
||||
{
|
||||
const AZ::Transform worldFromLocalUniformScale = TransformUniformScale(GetSpace());
|
||||
const GridSnapParameters gridSnapParams = GridSnapSettings(interaction.m_interactionId.m_viewportId);
|
||||
|
||||
// note: m_localTransform must not be made uniform as it may contain a local scale we want to snap
|
||||
m_starter = CalculateLinearManipulationDataStart(
|
||||
m_fixed, worldFromLocalUniformScale, GetNonUniformScale(), GetLocalTransform(),
|
||||
GridSnapAction(gridSnapParams, interaction.m_keyboardModifiers.Alt()), interaction, rayIntersectionDistance,
|
||||
m_fixed, worldFromLocalUniformScale, GetNonUniformScale(), GetLocalTransform(), interaction, rayIntersectionDistance,
|
||||
GetCameraState(interaction.m_interactionId.m_viewportId));
|
||||
|
||||
if (m_onLeftMouseDownCallback)
|
||||
{
|
||||
const GridSnapParameters gridSnapParams = GridSnapSettings(interaction.m_interactionId.m_viewportId);
|
||||
|
||||
m_onLeftMouseDownCallback(CalculateLinearManipulationDataAction(
|
||||
m_fixed, m_starter, worldFromLocalUniformScale, GetNonUniformScale(), GetLocalTransform(),
|
||||
GridSnapAction(gridSnapParams, interaction.m_keyboardModifiers.Alt()), interaction));
|
||||
m_fixed, m_starter, worldFromLocalUniformScale, GetNonUniformScale(), GetLocalTransform(), gridSnapParams, interaction));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,8 +178,8 @@ namespace AzToolsFramework
|
||||
|
||||
// note: m_localTransform must not be made uniform as it may contain a local scale we want to snap
|
||||
m_onMouseMoveCallback(CalculateLinearManipulationDataAction(
|
||||
m_fixed, m_starter, TransformUniformScale(GetSpace()), GetNonUniformScale(), GetLocalTransform(),
|
||||
GridSnapAction(gridSnapParams, interaction.m_keyboardModifiers.Alt()), interaction));
|
||||
m_fixed, m_starter, TransformUniformScale(GetSpace()), GetNonUniformScale(), GetLocalTransform(), gridSnapParams,
|
||||
interaction));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,8 +191,7 @@ namespace AzToolsFramework
|
||||
|
||||
// note: m_localTransform must not be made uniform as it may contain a local scale we want to snap
|
||||
m_onLeftMouseUpCallback(CalculateLinearManipulationDataAction(
|
||||
m_fixed, m_starter, TransformUniformScale(GetSpace()), GetNonUniformScale(), GetLocalTransform(),
|
||||
GridSnapAction(gridSnapParams, interaction.m_keyboardModifiers.Alt()), interaction));
|
||||
m_fixed, m_starter, TransformUniformScale(GetSpace()), GetNonUniformScale(), GetLocalTransform(), gridSnapParams, interaction));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,8 +214,8 @@ namespace AzToolsFramework
|
||||
GridSnapSettings(mouseInteraction.m_interactionId.m_viewportId);
|
||||
|
||||
const auto action = CalculateLinearManipulationDataAction(
|
||||
m_fixed, m_starter, TransformUniformScale(GetSpace()), GetNonUniformScale(), GetLocalTransform(),
|
||||
GridSnapAction(gridSnapParams, mouseInteraction.m_keyboardModifiers.Alt()), mouseInteraction);
|
||||
m_fixed, m_starter, TransformUniformScale(GetSpace()), GetNonUniformScale(), GetLocalTransform(), gridSnapParams,
|
||||
mouseInteraction);
|
||||
|
||||
// display the exact hit (ray intersection) of the mouse pick on the manipulator
|
||||
DrawTransformAxes(
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
struct GridSnapAction;
|
||||
struct GridSnapParameters;
|
||||
|
||||
/// LinearManipulator serves as a visual tool for users to modify values
|
||||
/// in one dimension on an axis defined in 3D space.
|
||||
@@ -68,8 +68,6 @@ namespace AzToolsFramework
|
||||
AZ::Vector3 m_localScale; ///< The current scale of the manipulator in local space.
|
||||
AZ::Vector3 m_localHitPosition; ///< The intersection point in local space between the ray and the manipulator when the mouse down event happens.
|
||||
AZ::Vector3 m_localAxis; ///< The axis in the local space of the manipulator itself.
|
||||
AZ::Vector3 m_positionSnapOffset; ///< The snap offset amount to ensure manipulator is aligned to the grid.
|
||||
AZ::Vector3 m_scaleSnapOffset; ///< The snap offset amount to ensure manipulator is aligned to round scale increments.
|
||||
float m_sign; ///< Used to determine which side of the axis we clicked on in case it's flipped to face the camera.
|
||||
AzFramework::ScreenPoint m_screenPosition; ///< The initial position in screen space of the manipulator.
|
||||
};
|
||||
@@ -91,7 +89,7 @@ namespace AzToolsFramework
|
||||
ViewportInteraction::KeyboardModifiers m_modifiers;
|
||||
int m_viewportId; ///< The id of the viewport this manipulator is being used in.
|
||||
AZ::Vector3 LocalScale() const { return m_start.m_localScale + m_current.m_localScaleOffset; }
|
||||
AZ::Vector3 LocalScaleOffset() const { return m_start.m_scaleSnapOffset + m_current.m_localScaleOffset; }
|
||||
AZ::Vector3 LocalScaleOffset() const { return m_current.m_localScaleOffset; }
|
||||
AZ::Vector3 LocalPosition() const { return m_start.m_localPosition + m_current.m_localPositionOffset; }
|
||||
AZ::Vector3 LocalPositionOffset() const { return m_current.m_localPositionOffset; }
|
||||
AZ::Vector2 ScreenOffset() const
|
||||
@@ -162,11 +160,11 @@ namespace AzToolsFramework
|
||||
|
||||
LinearManipulator::Starter CalculateLinearManipulationDataStart(
|
||||
const LinearManipulator::Fixed& fixed, const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale,
|
||||
const AZ::Transform& localTransform, const GridSnapAction& gridSnapAction, const ViewportInteraction::MouseInteraction& interaction,
|
||||
float intersectionDistance, const AzFramework::CameraState& cameraState);
|
||||
const AZ::Transform& localTransform, const ViewportInteraction::MouseInteraction& interaction, float intersectionDistance,
|
||||
const AzFramework::CameraState& cameraState);
|
||||
|
||||
LinearManipulator::Action CalculateLinearManipulationDataAction(
|
||||
const LinearManipulator::Fixed& fixed, const LinearManipulator::Starter& starter,
|
||||
const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale, const AZ::Transform& localTransform,
|
||||
const GridSnapAction& gridSnapAction, const ViewportInteraction::MouseInteraction& interaction);
|
||||
const GridSnapParameters& gridSnapParams, const ViewportInteraction::MouseInteraction& interaction);
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
+25
-11
@@ -42,12 +42,6 @@ namespace AzToolsFramework
|
||||
{
|
||||
}
|
||||
|
||||
GridSnapAction::GridSnapAction(const GridSnapParameters& gridSnapParameters, const bool localSnapping)
|
||||
: m_gridSnapParams(gridSnapParameters)
|
||||
, m_localSnapping(localSnapping)
|
||||
{
|
||||
}
|
||||
|
||||
ManipulatorInteraction BuildManipulatorInteraction(
|
||||
const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale,
|
||||
const AZ::Vector3& worldRayOrigin, const AZ::Vector3& worldRayDirection)
|
||||
@@ -57,19 +51,39 @@ namespace AzToolsFramework
|
||||
|
||||
return {localFromWorldUniform.TransformPoint(worldRayOrigin),
|
||||
TransformDirectionNoScaling(localFromWorldUniform, worldRayDirection),
|
||||
ScaleReciprocal(worldFromLocalUniform),
|
||||
NonUniformScaleReciprocal(nonUniformScale)};
|
||||
NonUniformScaleReciprocal(nonUniformScale),
|
||||
ScaleReciprocal(worldFromLocalUniform)};
|
||||
}
|
||||
|
||||
AZ::Vector3 CalculateSnappedOffset(
|
||||
const AZ::Vector3& unsnappedPosition, const AZ::Vector3& axis, const float size)
|
||||
struct SnapAdjustment
|
||||
{
|
||||
float m_existingSnapDistance; //!< How far to snap up or down to align to the grid.
|
||||
float m_nextSnapDistance; //!< The snap increment (will return full signed value (grid size) when distance
|
||||
//!< moved is greater than half of the grid size in either direction).
|
||||
};
|
||||
|
||||
static SnapAdjustment CalculateSnapDistance(const AZ::Vector3& unsnappedPosition, const AZ::Vector3& axis, const float size)
|
||||
{
|
||||
// calculate total distance along axis
|
||||
const float axisDistance = axis.Dot(unsnappedPosition);
|
||||
// round to nearest step size
|
||||
const float snappedAxisDistance = floorf((axisDistance / size) + 0.5f) * size;
|
||||
|
||||
return { axisDistance, snappedAxisDistance };
|
||||
}
|
||||
|
||||
AZ::Vector3 CalculateSnappedOffset(const AZ::Vector3& unsnappedPosition, const AZ::Vector3& axis, const float size)
|
||||
{
|
||||
const auto snapAdjustment = CalculateSnapDistance(unsnappedPosition, axis, size);
|
||||
// return offset along axis to snap to step size
|
||||
return axis * (snappedAxisDistance - axisDistance);
|
||||
return axis * (snapAdjustment.m_nextSnapDistance - snapAdjustment.m_existingSnapDistance);
|
||||
}
|
||||
|
||||
AZ::Vector3 CalculateSnappedAmount(const AZ::Vector3& unsnappedPosition, const AZ::Vector3& axis, const float size)
|
||||
{
|
||||
const auto snapAdjustment = CalculateSnapDistance(unsnappedPosition, axis, size);
|
||||
// return offset along axis to snap to step size
|
||||
return axis * snapAdjustment.m_nextSnapDistance;
|
||||
}
|
||||
|
||||
AZ::Vector3 CalculateSnappedTerrainPosition(
|
||||
|
||||
+9
-13
@@ -31,24 +31,15 @@ namespace AzToolsFramework
|
||||
float m_gridSize;
|
||||
};
|
||||
|
||||
/// Structure to encapsulate the current grid snapping state.
|
||||
struct GridSnapAction
|
||||
{
|
||||
GridSnapAction(const GridSnapParameters& gridSnapParameters, bool localSnapping);
|
||||
|
||||
GridSnapParameters m_gridSnapParams;
|
||||
bool m_localSnapping;
|
||||
};
|
||||
|
||||
/// Structure to hold transformed incoming viewport interaction from world space to manipulator space.
|
||||
struct ManipulatorInteraction
|
||||
{
|
||||
AZ::Vector3 m_localRayOrigin; ///< The ray origin (start) in the reference from of the manipulator.
|
||||
AZ::Vector3 m_localRayDirection; ///< The ray direction in the reference from of the manipulator.
|
||||
float m_scaleReciprocal; ///< The scale reciprocal (1.0 / scale) of the transform used to move the
|
||||
///< ray from world space to local space.
|
||||
AZ::Vector3 m_nonUniformScaleReciprocal; ///< Handles inverting any non-uniform scale which was applied
|
||||
///< separately from the transform.
|
||||
float m_scaleReciprocal; ///< The scale reciprocal (1.0 / scale) of the transform used to move the
|
||||
///< ray from world space to local space.
|
||||
};
|
||||
|
||||
/// Build a ManipulatorInteraction structure from the incoming viewport interaction.
|
||||
@@ -56,11 +47,16 @@ namespace AzToolsFramework
|
||||
const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale,
|
||||
const AZ::Vector3& worldRayOrigin, const AZ::Vector3& worldRayDirection);
|
||||
|
||||
/// Calculate the offset along an axis to adjust a position
|
||||
/// to stay snapped to a given grid size.
|
||||
/// Calculate the offset along an axis to adjust a position to stay snapped to a given grid size.
|
||||
/// @note This is snap up or down to the nearest grid segment (e.g. 0.2 snaps to 0.0 -> delta 0.2,
|
||||
/// 0.7 snaps to 1.0 -> delta 0.3).
|
||||
AZ::Vector3 CalculateSnappedOffset(
|
||||
const AZ::Vector3& unsnappedPosition, const AZ::Vector3& axis, float size);
|
||||
|
||||
/// Return the amount to snap from the starting position given the current grid size.
|
||||
/// @note A movement of more than half size (in either direction) will cause a snap by size.
|
||||
AZ::Vector3 CalculateSnappedAmount(const AZ::Vector3& unsnappedPosition, const AZ::Vector3& axis, float size);
|
||||
|
||||
/// For a given point on the terrain, calculate the closest xy position snapped to the grid
|
||||
/// (z position is aligned to terrain height, not snapped to z grid)
|
||||
AZ::Vector3 CalculateSnappedTerrainPosition(
|
||||
|
||||
+10
-18
@@ -59,17 +59,16 @@ namespace AzToolsFramework
|
||||
const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale, const AZ::Transform& localTransform,
|
||||
const ViewportInteraction::MouseInteraction& interaction,
|
||||
const AZStd::vector<LinearManipulator::Fixed>& fixedAxes,
|
||||
const AZStd::vector<LinearManipulator::Starter>& starterStates, const GridSnapAction& gridSnapAction)
|
||||
const AZStd::vector<LinearManipulator::Starter>& starterStates, const GridSnapParameters& gridSnapParams)
|
||||
{
|
||||
MultiLinearManipulator::Action action;
|
||||
action.m_viewportId = interaction.m_interactionId.m_viewportId;
|
||||
// build up action state for each axis
|
||||
for (size_t fixedIndex = 0; fixedIndex < fixedAxes.size(); ++fixedIndex)
|
||||
{
|
||||
action.m_actions.push_back(
|
||||
CalculateLinearManipulationDataAction(
|
||||
fixedAxes[fixedIndex], starterStates[fixedIndex], worldFromLocal, nonUniformScale, localTransform,
|
||||
gridSnapAction, interaction));
|
||||
action.m_actions.push_back(CalculateLinearManipulationDataAction(
|
||||
fixedAxes[fixedIndex], starterStates[fixedIndex], worldFromLocal, nonUniformScale, localTransform, gridSnapParams,
|
||||
interaction));
|
||||
}
|
||||
|
||||
return action;
|
||||
@@ -79,8 +78,6 @@ namespace AzToolsFramework
|
||||
const ViewportInteraction::MouseInteraction& interaction, const float rayIntersectionDistance)
|
||||
{
|
||||
const AZ::Transform worldFromLocalUniformScale = TransformUniformScale(GetSpace());
|
||||
|
||||
const GridSnapParameters gridSnapParams = GridSnapSettings(interaction.m_interactionId.m_viewportId);
|
||||
const AzFramework::CameraState cameraState = GetCameraState(interaction.m_interactionId.m_viewportId);
|
||||
|
||||
// build up initial start state for each axis
|
||||
@@ -88,20 +85,19 @@ namespace AzToolsFramework
|
||||
{
|
||||
// note: m_localTransform must not be made uniform as it may contain a local scale we want to snap
|
||||
const auto linearStart = CalculateLinearManipulationDataStart(
|
||||
fixed, worldFromLocalUniformScale, GetNonUniformScale(), GetLocalTransform(),
|
||||
GridSnapAction(gridSnapParams, interaction.m_keyboardModifiers.Alt()), interaction,
|
||||
rayIntersectionDistance, cameraState);
|
||||
fixed, worldFromLocalUniformScale, GetNonUniformScale(), GetLocalTransform(), interaction, rayIntersectionDistance,
|
||||
cameraState);
|
||||
|
||||
m_starters.push_back(linearStart);
|
||||
}
|
||||
|
||||
if (m_onLeftMouseDownCallback)
|
||||
{
|
||||
const GridSnapAction gridSnapAction = GridSnapAction(gridSnapParams, interaction.m_keyboardModifiers.Alt());
|
||||
const GridSnapParameters gridSnapParams = GridSnapSettings(interaction.m_interactionId.m_viewportId);
|
||||
// pass action containing all linear actions for each axis to handler
|
||||
m_onLeftMouseDownCallback(BuildMultiLinearManipulatorAction(
|
||||
worldFromLocalUniformScale, GetNonUniformScale(), GetLocalTransform(),
|
||||
interaction, m_fixedAxes, m_starters, gridSnapAction));
|
||||
interaction, m_fixedAxes, m_starters, gridSnapParams));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,11 +107,9 @@ namespace AzToolsFramework
|
||||
{
|
||||
const AZ::Transform worldFromLocalUniformScale = TransformUniformScale(GetSpace());
|
||||
const GridSnapParameters gridSnapParams = GridSnapSettings(interaction.m_interactionId.m_viewportId);
|
||||
const GridSnapAction gridSnapAction = GridSnapAction(gridSnapParams, interaction.m_keyboardModifiers.Alt());
|
||||
|
||||
m_onMouseMoveCallback(BuildMultiLinearManipulatorAction(
|
||||
worldFromLocalUniformScale, GetNonUniformScale(), GetLocalTransform(),
|
||||
interaction, m_fixedAxes, m_starters, gridSnapAction));
|
||||
interaction, m_fixedAxes, m_starters, gridSnapParams));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,11 +119,9 @@ namespace AzToolsFramework
|
||||
{
|
||||
const AZ::Transform worldFromLocalUniformScale = TransformUniformScale(GetSpace());
|
||||
const GridSnapParameters gridSnapParams = GridSnapSettings(interaction.m_interactionId.m_viewportId);
|
||||
const GridSnapAction gridSnapAction = GridSnapAction(gridSnapParams, interaction.m_keyboardModifiers.Alt());
|
||||
|
||||
m_onLeftMouseUpCallback(BuildMultiLinearManipulatorAction(
|
||||
worldFromLocalUniformScale, GetNonUniformScale(), GetLocalTransform(),
|
||||
interaction, m_fixedAxes, m_starters, gridSnapAction));
|
||||
interaction, m_fixedAxes, m_starters, gridSnapParams));
|
||||
|
||||
m_starters.clear();
|
||||
}
|
||||
|
||||
@@ -20,8 +20,6 @@
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
struct GridSnapAction;
|
||||
|
||||
//! MultiLinearManipulator serves as a visual tool for users to modify values
|
||||
//! in one or more dimensions on axes defined in 3D space.
|
||||
class MultiLinearManipulator
|
||||
|
||||
+15
-35
@@ -22,8 +22,7 @@
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
PlanarManipulator::StartInternal PlanarManipulator::CalculateManipulationDataStart(
|
||||
const Fixed& fixed, const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale,
|
||||
const AZ::Transform& localTransform, const GridSnapAction& gridSnapAction,
|
||||
const Fixed& fixed, const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale, const AZ::Transform& localTransform,
|
||||
const ViewportInteraction::MouseInteraction& interaction, const float intersectionDistance)
|
||||
{
|
||||
const ManipulatorInteraction manipulatorInteraction =
|
||||
@@ -31,8 +30,6 @@ namespace AzToolsFramework
|
||||
worldFromLocal, nonUniformScale, interaction.m_mousePick.m_rayOrigin, interaction.m_mousePick.m_rayDirection);
|
||||
|
||||
const AZ::Vector3 normal = TransformDirectionNoScaling(localTransform, fixed.m_normal);
|
||||
const AZ::Vector3 axis1 = TransformDirectionNoScaling(localTransform, fixed.m_axis1);
|
||||
const AZ::Vector3 axis2 = TransformDirectionNoScaling(localTransform, fixed.m_axis2);
|
||||
|
||||
// initial intersect point
|
||||
const AZ::Vector3 localIntersectionPoint =
|
||||
@@ -43,25 +40,14 @@ namespace AzToolsFramework
|
||||
manipulatorInteraction.m_localRayOrigin, manipulatorInteraction.m_localRayDirection,
|
||||
localIntersectionPoint, normal, startInternal.m_localHitPosition);
|
||||
|
||||
const float scaleRecip = manipulatorInteraction.m_scaleReciprocal;
|
||||
const float gridSize = gridSnapAction.m_gridSnapParams.m_gridSize;
|
||||
const bool snapping = gridSnapAction.m_gridSnapParams.m_gridSnap;
|
||||
|
||||
// calculate amount to snap to align with grid
|
||||
const AZ::Vector3 snapOffset = snapping && !gridSnapAction.m_localSnapping
|
||||
? CalculateSnappedOffset(localTransform.GetTranslation(), axis1, gridSize * scaleRecip) +
|
||||
CalculateSnappedOffset(localTransform.GetTranslation(), axis2, gridSize * scaleRecip)
|
||||
: AZ::Vector3::CreateZero();
|
||||
|
||||
startInternal.m_snapOffset = snapOffset;
|
||||
startInternal.m_localPosition = localTransform.GetTranslation() + snapOffset;
|
||||
startInternal.m_localPosition = localTransform.GetTranslation();
|
||||
|
||||
return startInternal;
|
||||
}
|
||||
|
||||
PlanarManipulator::Action PlanarManipulator::CalculateManipulationDataAction(
|
||||
const Fixed& fixed, const StartInternal& startInternal, const AZ::Transform& worldFromLocal,
|
||||
const AZ::Vector3& nonUniformScale, const AZ::Transform& localTransform, const GridSnapAction& gridSnapAction,
|
||||
const Fixed& fixed, const StartInternal& startInternal, const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale,
|
||||
const AZ::Transform& localTransform, const GridSnapParameters& gridSnapParams,
|
||||
const ViewportInteraction::MouseInteraction& interaction)
|
||||
{
|
||||
const ManipulatorInteraction manipulatorInteraction =
|
||||
@@ -88,20 +74,18 @@ namespace AzToolsFramework
|
||||
const AZ::Vector3 hitDelta = (localHitPosition - startInternal.m_localHitPosition) / nonUniformScale;
|
||||
const AZ::Vector3 unsnappedOffset = axis1.Dot(hitDelta) * axis1 + axis2.Dot(hitDelta) * axis2;
|
||||
|
||||
const float scaleRecip = manipulatorInteraction.m_scaleReciprocal;
|
||||
const AZ::Vector3 nonUniformScaleRecip = manipulatorInteraction.m_nonUniformScaleReciprocal;
|
||||
const float gridSize = gridSnapAction.m_gridSnapParams.m_gridSize;
|
||||
const bool snapping = gridSnapAction.m_gridSnapParams.m_gridSnap;
|
||||
const float scaleRecip = manipulatorInteraction.m_scaleReciprocal;
|
||||
const float gridSize = gridSnapParams.m_gridSize;
|
||||
const bool snapping = gridSnapParams.m_gridSnap;
|
||||
|
||||
Action action;
|
||||
action.m_fixed = fixed;
|
||||
action.m_start.m_localPosition = startInternal.m_localPosition;
|
||||
action.m_start.m_snapOffset = startInternal.m_snapOffset;
|
||||
action.m_start.m_localHitPosition = startInternal.m_localHitPosition;
|
||||
action.m_current.m_localOffset = snapping
|
||||
? unsnappedOffset +
|
||||
CalculateSnappedOffset(unsnappedOffset, axis1, gridSize * scaleRecip * nonUniformScaleRecip.Dot(axis1)) +
|
||||
CalculateSnappedOffset(unsnappedOffset, axis2, gridSize * scaleRecip * nonUniformScaleRecip.Dot(axis2))
|
||||
? CalculateSnappedAmount(unsnappedOffset, axis1, gridSize * scaleRecip * nonUniformScaleRecip.Dot(fixed.m_axis1)) +
|
||||
CalculateSnappedAmount(unsnappedOffset, axis2, gridSize * scaleRecip * nonUniformScaleRecip.Dot(fixed.m_axis2))
|
||||
: unsnappedOffset;
|
||||
|
||||
// record what modifier keys are held during this action
|
||||
@@ -141,18 +125,17 @@ namespace AzToolsFramework
|
||||
{
|
||||
const AZ::Transform worldFromLocalUniformScale = TransformUniformScale(GetSpace());
|
||||
|
||||
const GridSnapParameters gridSnapParams = GridSnapSettings(interaction.m_interactionId.m_viewportId);
|
||||
|
||||
m_startInternal = CalculateManipulationDataStart(
|
||||
m_fixed, worldFromLocalUniformScale, GetNonUniformScale(), TransformNormalizedScale(GetLocalTransform()),
|
||||
GridSnapAction(gridSnapParams, interaction.m_keyboardModifiers.Alt()),
|
||||
interaction, rayIntersectionDistance);
|
||||
|
||||
if (m_onLeftMouseDownCallback)
|
||||
{
|
||||
const GridSnapParameters gridSnapParams = GridSnapSettings(interaction.m_interactionId.m_viewportId);
|
||||
|
||||
m_onLeftMouseDownCallback(CalculateManipulationDataAction(
|
||||
m_fixed, m_startInternal, worldFromLocalUniformScale, GetNonUniformScale(), TransformNormalizedScale(GetLocalTransform()),
|
||||
GridSnapAction(gridSnapParams, interaction.m_keyboardModifiers.Alt()), interaction));
|
||||
gridSnapParams, interaction));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,8 +147,7 @@ namespace AzToolsFramework
|
||||
|
||||
m_onMouseMoveCallback(CalculateManipulationDataAction(
|
||||
m_fixed, m_startInternal, TransformUniformScale(GetSpace()), GetNonUniformScale(),
|
||||
TransformNormalizedScale(GetLocalTransform()),
|
||||
GridSnapAction(gridSnapParams, interaction.m_keyboardModifiers.Alt()), interaction));
|
||||
TransformNormalizedScale(GetLocalTransform()), gridSnapParams, interaction));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,8 +159,7 @@ namespace AzToolsFramework
|
||||
|
||||
m_onLeftMouseUpCallback(CalculateManipulationDataAction(
|
||||
m_fixed, m_startInternal, TransformUniformScale(GetSpace()), GetNonUniformScale(),
|
||||
TransformNormalizedScale(GetLocalTransform()),
|
||||
GridSnapAction(gridSnapParams, interaction.m_keyboardModifiers.Alt()), interaction));
|
||||
TransformNormalizedScale(GetLocalTransform()), gridSnapParams, interaction));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,8 +176,7 @@ namespace AzToolsFramework
|
||||
const GridSnapParameters gridSnapParams = GridSnapSettings(mouseInteraction.m_interactionId.m_viewportId);
|
||||
const auto action = CalculateManipulationDataAction(
|
||||
m_fixed, m_startInternal, TransformUniformScale(GetSpace()), GetNonUniformScale(),
|
||||
TransformNormalizedScale(GetLocalTransform()),
|
||||
GridSnapAction(gridSnapParams, mouseInteraction.m_keyboardModifiers.Alt()), mouseInteraction);
|
||||
TransformNormalizedScale(GetLocalTransform()), gridSnapParams, mouseInteraction);
|
||||
|
||||
// display the exact hit (ray intersection) of the mouse pick on the manipulator
|
||||
DrawTransformAxes(
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
class ManipulatorView;
|
||||
struct GridSnapAction;
|
||||
struct GridSnapParameters;
|
||||
|
||||
/// PlanarManipulator serves as a visual tool for users to modify values
|
||||
/// in two dimension in a plane defined two non-collinear axes in 3D space.
|
||||
@@ -58,7 +58,6 @@ namespace AzToolsFramework
|
||||
{
|
||||
AZ::Vector3 m_localPosition; ///< The current position of the manipulator in local space.
|
||||
AZ::Vector3 m_localHitPosition; ///< The intersection point in local space between the ray and the manipulator when the mouse down event happens.
|
||||
AZ::Vector3 m_snapOffset; ///< The snap offset amount to ensure manipulator is aligned to the grid.
|
||||
};
|
||||
|
||||
/// The state of the manipulator during an interaction.
|
||||
@@ -120,7 +119,6 @@ namespace AzToolsFramework
|
||||
{
|
||||
AZ::Vector3 m_localPosition; ///< The starting position of the manipulator in local space.
|
||||
AZ::Vector3 m_localHitPosition; ///< The intersection point in world space between the ray and the manipulator when the mouse down event happens.
|
||||
AZ::Vector3 m_snapOffset; ///< The snap offset amount to ensure manipulator is aligned to the grid.
|
||||
};
|
||||
|
||||
Fixed m_fixed;
|
||||
@@ -134,12 +132,11 @@ namespace AzToolsFramework
|
||||
|
||||
static StartInternal CalculateManipulationDataStart(
|
||||
const Fixed& fixed, const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale,
|
||||
const AZ::Transform& localTransform, const GridSnapAction& gridSnapAction,
|
||||
const ViewportInteraction::MouseInteraction& interaction, float intersectionDistance);
|
||||
const AZ::Transform& localTransform, const ViewportInteraction::MouseInteraction& interaction, float intersectionDistance);
|
||||
|
||||
static Action CalculateManipulationDataAction(
|
||||
const Fixed& fixed, const StartInternal& startInternal, const AZ::Transform& worldFromLocal,
|
||||
const AZ::Vector3& nonUniformScale, const AZ::Transform& localTransform,
|
||||
const GridSnapAction& gridSnapAction, const ViewportInteraction::MouseInteraction& interaction);
|
||||
const Fixed& fixed, const StartInternal& startInternal, const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale,
|
||||
const AZ::Transform& localTransform, const GridSnapParameters& gridSnapParams,
|
||||
const ViewportInteraction::MouseInteraction& interaction);
|
||||
};
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/Instance.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabDomUtils.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/InstanceEntityMapperInterface.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/TemplateInstanceMapperInterface.h>
|
||||
|
||||
@@ -49,8 +50,7 @@ namespace AzToolsFramework
|
||||
m_alias = GenerateInstanceAlias();
|
||||
m_containerEntity = containerEntity ? AZStd::move(containerEntity)
|
||||
: AZStd::make_unique<AZ::Entity>();
|
||||
EntityAlias containerEntityAlias = GenerateEntityAlias();
|
||||
RegisterEntity(m_containerEntity->GetId(), containerEntityAlias);
|
||||
RegisterEntity(m_containerEntity->GetId(), PrefabDomUtils::ContainerEntityName);
|
||||
}
|
||||
|
||||
Instance::~Instance()
|
||||
@@ -311,8 +311,15 @@ namespace AzToolsFramework
|
||||
Instance& Instance::AddInstance(AZStd::unique_ptr<Instance> instance)
|
||||
{
|
||||
InstanceAlias newInstanceAlias = GenerateInstanceAlias();
|
||||
return AddInstance(AZStd::move(instance), newInstanceAlias);
|
||||
}
|
||||
|
||||
Instance& Instance::AddInstance(AZStd::unique_ptr<Instance> instance, InstanceAlias newInstanceAlias)
|
||||
{
|
||||
AZ_Assert(instance.get(), "instance argument is nullptr");
|
||||
AZ_Assert(m_nestedInstances.find(newInstanceAlias) == m_nestedInstances.end(), "InstanceAlias' unique id collision, this should never happen.");
|
||||
AZ_Assert(
|
||||
m_nestedInstances.find(newInstanceAlias) == m_nestedInstances.end(),
|
||||
"InstanceAlias' unique id collision, this should never happen.");
|
||||
instance->m_parent = this;
|
||||
instance->m_alias = newInstanceAlias;
|
||||
return *(m_nestedInstances[newInstanceAlias] = std::move(instance));
|
||||
@@ -613,6 +620,7 @@ namespace AzToolsFramework
|
||||
|
||||
AZStd::unique_ptr<AZ::Entity> Instance::DetachContainerEntity()
|
||||
{
|
||||
m_instanceEntityMapper->UnregisterEntity(m_containerEntity->GetId());
|
||||
return AZStd::move(m_containerEntity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ namespace AzToolsFramework
|
||||
using EntityAliasOptionalReference = AZStd::optional<AZStd::reference_wrapper<EntityAlias>>;
|
||||
using InstanceOptionalReference = AZStd::optional<AZStd::reference_wrapper<Instance>>;
|
||||
using InstanceOptionalConstReference = AZStd::optional<AZStd::reference_wrapper<const Instance>>;
|
||||
|
||||
using InstanceSet = AZStd::unordered_set<Instance*>;
|
||||
using InstanceSetConstReference = AZStd::optional<AZStd::reference_wrapper<const InstanceSet>>;
|
||||
using EntityOptionalReference = AZStd::optional<AZStd::reference_wrapper<AZ::Entity>>;
|
||||
@@ -85,12 +86,14 @@ namespace AzToolsFramework
|
||||
bool AddEntity(AZ::Entity& entity);
|
||||
bool AddEntity(AZ::Entity& entity, EntityAlias entityAlias);
|
||||
AZStd::unique_ptr<AZ::Entity> DetachEntity(const AZ::EntityId& entityId);
|
||||
void DetachEntities(const AZStd::function<void(AZStd::unique_ptr<AZ::Entity>)>& callback);
|
||||
void DetachNestedEntities(const AZStd::function<void(AZStd::unique_ptr<AZ::Entity>)>& callback);
|
||||
void RemoveNestedEntities(const AZStd::function<bool(const AZStd::unique_ptr<AZ::Entity>&)>& filter);
|
||||
|
||||
void Reset();
|
||||
|
||||
Instance& AddInstance(AZStd::unique_ptr<Instance> instance);
|
||||
Instance& AddInstance(AZStd::unique_ptr<Instance> instance, InstanceAlias instanceAlias);
|
||||
AZStd::unique_ptr<Instance> DetachNestedInstance(const InstanceAlias& instanceAlias);
|
||||
|
||||
/**
|
||||
@@ -171,6 +174,8 @@ namespace AzToolsFramework
|
||||
static EntityAlias GenerateEntityAlias();
|
||||
AliasPath GetAbsoluteInstanceAliasPath() const;
|
||||
|
||||
static InstanceAlias GenerateInstanceAlias();
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Gets the entities owned by this instance
|
||||
@@ -182,14 +187,11 @@ namespace AzToolsFramework
|
||||
|
||||
void ClearEntities();
|
||||
|
||||
void DetachEntities(const AZStd::function<void(AZStd::unique_ptr<AZ::Entity>)>& callback);
|
||||
void RemoveEntities(const AZStd::function<bool(const AZStd::unique_ptr<AZ::Entity>&)>& filter);
|
||||
|
||||
bool RegisterEntity(const AZ::EntityId& entityId, const EntityAlias& entityAlias);
|
||||
AZStd::unique_ptr<AZ::Entity> DetachEntity(const EntityAlias& entityAlias);
|
||||
|
||||
static InstanceAlias GenerateInstanceAlias();
|
||||
|
||||
// Provide access to private data members in the serializer
|
||||
friend class JsonInstanceSerializer;
|
||||
friend class InstanceEntityIdMapper;
|
||||
|
||||
+24
@@ -152,6 +152,30 @@ namespace AzToolsFramework
|
||||
Instance::EntityList newEntities;
|
||||
if (PrefabDomUtils::LoadInstanceFromPrefabDom(*instanceToUpdate, newEntities, currentTemplate.GetPrefabDom()))
|
||||
{
|
||||
// If a link was created for a nested instance before the changes were propagated,
|
||||
// then we associate it correctly here
|
||||
instanceToUpdate->GetNestedInstances([&](AZStd::unique_ptr<Instance>& nestedInstance) {
|
||||
if (nestedInstance->GetLinkId() != InvalidLinkId)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto linkId : currentTemplate.GetLinks())
|
||||
{
|
||||
LinkReference nestedLink = m_prefabSystemComponentInterface->FindLink(linkId);
|
||||
if (!nestedLink.has_value())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (nestedLink->get().GetInstanceName() == nestedInstance->GetInstanceAlias())
|
||||
{
|
||||
nestedInstance->SetLinkId(linkId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
AzToolsFramework::EditorEntityContextRequestBus::Broadcast(
|
||||
&AzToolsFramework::EditorEntityContextRequests::HandleEntitiesAdded, newEntities);
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntityInfoBus.h>
|
||||
#include <AzToolsFramework/Prefab/EditorPrefabComponent.h>
|
||||
#include <AzToolsFramework/Entity/PrefabEditorEntityOwnershipInterface.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/Instance.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/InstanceEntityIdMapper.h>
|
||||
@@ -191,24 +192,7 @@ namespace AzToolsFramework
|
||||
if (nestedInstanceLinkPatchesMap.contains(nestedInstance.get()))
|
||||
{
|
||||
previousPatch = AZStd::move(nestedInstanceLinkPatchesMap[nestedInstance.get()]);
|
||||
rapidjson::StringBuffer buffer;
|
||||
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
|
||||
previousPatch.Accept(writer);
|
||||
QString previousPatchString(buffer.GetString());
|
||||
|
||||
for (AZ::Entity* entity : entities)
|
||||
{
|
||||
AZ::EntityId entityId = entity->GetId();
|
||||
AZStd::string oldEntityAlias = oldEntityAliases[entityId];
|
||||
EntityAliasOptionalReference newEntityAlias = instanceToCreate->get().GetEntityAlias(entityId);
|
||||
AZ_Assert(
|
||||
newEntityAlias.has_value(),
|
||||
"Could not fetch entity alias for entity with id '%llu' during prefab creation.",
|
||||
static_cast<AZ::u64>(entityId));
|
||||
ReplaceOldAliases(previousPatchString, oldEntityAlias, newEntityAlias->get());
|
||||
}
|
||||
|
||||
previousPatch.Parse(previousPatchString.toUtf8().constData());
|
||||
UpdateLinkPatchesWithNewEntityAliases(previousPatch, oldEntityAliases, instanceToCreate->get());
|
||||
}
|
||||
|
||||
// These link creations shouldn't be undone because that would put the template in a non-usable state if a user
|
||||
@@ -243,11 +227,33 @@ namespace AzToolsFramework
|
||||
instanceToCreate->get(), commonRootEntityOwningInstance->get().GetTemplateId(), undoBatch.GetUndoBatch(),
|
||||
AZStd::move(patch));
|
||||
|
||||
// Reset the transform of the container entity so that the new values aren't saved in the new prefab's dom.
|
||||
// The new values were saved in the link, so propagation will apply them correctly.
|
||||
{
|
||||
AZ::Entity* containerEntity = GetEntityById(containerEntityId);
|
||||
|
||||
PrefabDom containerBeforeReset;
|
||||
m_instanceToTemplateInterface->GenerateDomForEntity(containerBeforeReset, *containerEntity);
|
||||
|
||||
AZ::TransformBus::Event(containerEntityId, &AZ::TransformBus::Events::SetParent, AZ::EntityId());
|
||||
AZ::TransformBus::Event(containerEntityId, &AZ::TransformBus::Events::SetLocalTM, AZ::Transform::CreateIdentity());
|
||||
|
||||
PrefabDom containerAfterReset;
|
||||
m_instanceToTemplateInterface->GenerateDomForEntity(containerAfterReset, *containerEntity);
|
||||
|
||||
// Update the state of the entity
|
||||
PrefabUndoEntityUpdate* state = aznew PrefabUndoEntityUpdate(AZStd::to_string(static_cast<AZ::u64>(containerEntityId)));
|
||||
state->SetParent(undoBatch.GetUndoBatch());
|
||||
state->Capture(containerBeforeReset, containerAfterReset, containerEntityId);
|
||||
|
||||
state->Redo();
|
||||
}
|
||||
|
||||
// This clears any entities marked as dirty due to reparenting of entities during the process of creating a prefab.
|
||||
// We are doing this so that the changes in those enities are not queued up twice for propagation.
|
||||
// We are doing this so that the changes in those entities are not queued up twice for propagation.
|
||||
AzToolsFramework::ToolsApplicationRequestBus::Broadcast(
|
||||
&AzToolsFramework::ToolsApplicationRequestBus::Events::ClearDirtyEntities);
|
||||
|
||||
|
||||
// Select Container Entity
|
||||
{
|
||||
auto selectionUndo = aznew SelectionCommand({containerEntityId}, "Select Prefab Container Entity");
|
||||
@@ -291,7 +297,7 @@ namespace AzToolsFramework
|
||||
m_instanceToTemplateInterface->AppendEntityAliasToPatchPaths(patch, containerEntityId);
|
||||
|
||||
// Update the cache - this prevents these changes from being stored in the regular undo/redo nodes
|
||||
m_prefabUndoCache.Store(containerEntityId, AZStd::move(containerEntityDomAfter));
|
||||
m_prefabUndoCache.Store(containerEntityId, AZStd::move(containerEntityDomAfter), parentEntityId);
|
||||
|
||||
return AZStd::move(patch);
|
||||
}
|
||||
@@ -385,8 +391,8 @@ namespace AzToolsFramework
|
||||
|
||||
CreateLink(instanceToCreate->get(), instanceToParentUnder->get().GetTemplateId(), undoBatch.GetUndoBatch(), AZStd::move(patch));
|
||||
|
||||
// Update the cache - this prevents these changes from being stored in the regular undo/redo nodes
|
||||
m_prefabUndoCache.Store(containerEntityId, AZStd::move(containerEntityDomAfter));
|
||||
AzToolsFramework::ToolsApplicationRequestBus::Broadcast(
|
||||
&AzToolsFramework::ToolsApplicationRequestBus::Events::ClearDirtyEntities);
|
||||
}
|
||||
|
||||
return AZ::Success();
|
||||
@@ -589,54 +595,199 @@ namespace AzToolsFramework
|
||||
{
|
||||
// Create Undo node on entities if they belong to an instance
|
||||
InstanceOptionalReference owningInstance = m_instanceEntityMapperInterface->FindOwningInstance(entityId);
|
||||
|
||||
if (owningInstance.has_value())
|
||||
if (!owningInstance.has_value())
|
||||
{
|
||||
PrefabDom afterState;
|
||||
AZ::Entity* entity = GetEntityById(entityId);
|
||||
if (entity)
|
||||
return;
|
||||
}
|
||||
|
||||
AZ::Entity* entity = GetEntityById(entityId);
|
||||
if (!entity)
|
||||
{
|
||||
m_prefabUndoCache.PurgeCache(entityId);
|
||||
return;
|
||||
}
|
||||
|
||||
PrefabDom beforeState;
|
||||
AZ::EntityId beforeParentId;
|
||||
m_prefabUndoCache.Retrieve(entityId, beforeState, beforeParentId);
|
||||
|
||||
PrefabDom afterState;
|
||||
AZ::EntityId afterParentId;
|
||||
AZ::TransformBus::EventResult(afterParentId, entityId, &AZ::TransformBus::Events::GetParentId);
|
||||
|
||||
m_instanceToTemplateInterface->GenerateDomForEntity(afterState, *entity);
|
||||
|
||||
PrefabDom patch;
|
||||
m_instanceToTemplateInterface->GeneratePatch(patch, beforeState, afterState);
|
||||
m_instanceToTemplateInterface->AppendEntityAliasToPatchPaths(patch, entityId);
|
||||
|
||||
if (patch.IsArray() && !patch.Empty() && beforeState.IsObject())
|
||||
{
|
||||
bool isInstanceContainerEntity = IsInstanceContainerEntity(entityId) && !IsLevelInstanceContainerEntity(entityId);
|
||||
bool isNewParentOwnedByDifferentInstance = false;
|
||||
|
||||
if (beforeParentId != afterParentId)
|
||||
{
|
||||
PrefabDom beforeState;
|
||||
m_prefabUndoCache.Retrieve(entityId, beforeState);
|
||||
// If the entity parent changed, verify if the owning instance changed too
|
||||
InstanceOptionalReference beforeOwningInstance = m_instanceEntityMapperInterface->FindOwningInstance(beforeParentId);
|
||||
InstanceOptionalReference afterOwningInstance = m_instanceEntityMapperInterface->FindOwningInstance(afterParentId);
|
||||
|
||||
m_instanceToTemplateInterface->GenerateDomForEntity(afterState, *entity);
|
||||
|
||||
PrefabDom patch;
|
||||
m_instanceToTemplateInterface->GeneratePatch(patch, beforeState, afterState);
|
||||
|
||||
if (patch.IsArray() && !patch.Empty() && beforeState.IsObject())
|
||||
if (beforeOwningInstance.has_value() && afterOwningInstance.has_value() &&
|
||||
(&beforeOwningInstance->get() != &afterOwningInstance->get()))
|
||||
{
|
||||
if (IsInstanceContainerEntity(entityId) && !IsLevelInstanceContainerEntity(entityId))
|
||||
{
|
||||
m_instanceToTemplateInterface->AppendEntityAliasToPatchPaths(patch, entityId);
|
||||
|
||||
// Save these changes as patches to the link
|
||||
PrefabUndoLinkUpdate* linkUpdate =
|
||||
aznew PrefabUndoLinkUpdate(AZStd::to_string(static_cast<AZ::u64>(entityId)));
|
||||
linkUpdate->SetParent(parentUndoBatch);
|
||||
linkUpdate->Capture(patch, owningInstance->get().GetLinkId());
|
||||
|
||||
linkUpdate->Redo();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Update the state of the entity
|
||||
PrefabUndoEntityUpdate* state = aznew PrefabUndoEntityUpdate(AZStd::to_string(static_cast<AZ::u64>(entityId)));
|
||||
state->SetParent(parentUndoBatch);
|
||||
state->Capture(beforeState, afterState, entityId);
|
||||
|
||||
state->Redo();
|
||||
}
|
||||
isNewParentOwnedByDifferentInstance = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Update the cache
|
||||
m_prefabUndoCache.Store(entityId, AZStd::move(afterState));
|
||||
if (isInstanceContainerEntity)
|
||||
{
|
||||
if (isNewParentOwnedByDifferentInstance)
|
||||
{
|
||||
Internal_HandleInstanceChange(parentUndoBatch, entity, beforeParentId, afterParentId);
|
||||
|
||||
PrefabDom afterStateafterReparenting;
|
||||
m_instanceToTemplateInterface->GenerateDomForEntity(afterStateafterReparenting, *entity);
|
||||
|
||||
PrefabDom newPatch;
|
||||
m_instanceToTemplateInterface->GeneratePatch(newPatch, afterState, afterStateafterReparenting);
|
||||
m_instanceToTemplateInterface->AppendEntityAliasToPatchPaths(newPatch, entityId);
|
||||
|
||||
InstanceOptionalReference owningInstanceAfterReparenting =
|
||||
m_instanceEntityMapperInterface->FindOwningInstance(entityId);
|
||||
|
||||
Internal_HandleContainerOverride(
|
||||
parentUndoBatch, entityId, newPatch, owningInstanceAfterReparenting->get().GetLinkId());
|
||||
}
|
||||
else
|
||||
{
|
||||
Internal_HandleContainerOverride(
|
||||
parentUndoBatch, entityId, patch, owningInstance->get().GetLinkId());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_prefabUndoCache.PurgeCache(entityId);
|
||||
Internal_HandleEntityChange(parentUndoBatch, entityId, beforeState, afterState);
|
||||
|
||||
if (isNewParentOwnedByDifferentInstance)
|
||||
{
|
||||
Internal_HandleInstanceChange(parentUndoBatch, entity, beforeParentId, afterParentId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_prefabUndoCache.UpdateCache(entityId);
|
||||
}
|
||||
|
||||
void PrefabPublicHandler::Internal_HandleContainerOverride(
|
||||
UndoSystem::URSequencePoint* undoBatch, AZ::EntityId entityId, const PrefabDom& patch, const LinkId linkId)
|
||||
{
|
||||
// Save these changes as patches to the link
|
||||
PrefabUndoLinkUpdate* linkUpdate = aznew PrefabUndoLinkUpdate(AZStd::to_string(static_cast<AZ::u64>(entityId)));
|
||||
linkUpdate->SetParent(undoBatch);
|
||||
linkUpdate->Capture(patch, linkId);
|
||||
|
||||
linkUpdate->Redo();
|
||||
}
|
||||
|
||||
void PrefabPublicHandler::Internal_HandleEntityChange(
|
||||
UndoSystem::URSequencePoint* undoBatch, AZ::EntityId entityId, PrefabDom& beforeState, PrefabDom& afterState)
|
||||
{
|
||||
// Update the state of the entity
|
||||
PrefabUndoEntityUpdate* state = aznew PrefabUndoEntityUpdate(AZStd::to_string(static_cast<AZ::u64>(entityId)));
|
||||
state->SetParent(undoBatch);
|
||||
state->Capture(beforeState, afterState, entityId);
|
||||
|
||||
state->Redo();
|
||||
}
|
||||
|
||||
void PrefabPublicHandler::Internal_HandleInstanceChange(
|
||||
UndoSystem::URSequencePoint* undoBatch, AZ::Entity* entity, AZ::EntityId beforeParentId, AZ::EntityId afterParentId)
|
||||
{
|
||||
// If the entity parent changed, verify if the owning instance changed too
|
||||
InstanceOptionalReference beforeOwningInstance = m_instanceEntityMapperInterface->FindOwningInstance(beforeParentId);
|
||||
InstanceOptionalReference afterOwningInstance = m_instanceEntityMapperInterface->FindOwningInstance(afterParentId);
|
||||
|
||||
EntityList entities;
|
||||
AZStd::vector<Instance*> instances;
|
||||
|
||||
// Retrieve all descendant entities and instances of this entity that belonged to the same owning instance.
|
||||
RetrieveAndSortPrefabEntitiesAndInstances({ entity }, beforeOwningInstance->get(), entities, instances);
|
||||
|
||||
AZStd::vector<AZStd::unique_ptr<Instance>> instanceUniquePtrs;
|
||||
AZStd::vector<AZStd::pair<Instance*, PrefabDom>> instancePatches;
|
||||
|
||||
// Remove Entities and Instances from the prior instance
|
||||
{
|
||||
// Remove Instances
|
||||
for (Instance* nestedInstance : instances)
|
||||
{
|
||||
auto linkRef = m_prefabSystemComponentInterface->FindLink(nestedInstance->GetLinkId());
|
||||
|
||||
PrefabDom oldLinkPatches;
|
||||
|
||||
if (linkRef.has_value())
|
||||
{
|
||||
auto patches = linkRef->get().GetLinkPatches();
|
||||
if (patches.has_value())
|
||||
{
|
||||
oldLinkPatches.CopyFrom(patches->get(), oldLinkPatches.GetAllocator());
|
||||
}
|
||||
}
|
||||
|
||||
auto nestedInstanceUniquePtr = beforeOwningInstance->get().DetachNestedInstance(nestedInstance->GetInstanceAlias());
|
||||
RemoveLink(nestedInstanceUniquePtr, beforeOwningInstance->get().GetTemplateId(), undoBatch);
|
||||
|
||||
instancePatches.emplace_back(AZStd::make_pair(nestedInstanceUniquePtr.get(), AZStd::move(oldLinkPatches)));
|
||||
instanceUniquePtrs.emplace_back(AZStd::move(nestedInstanceUniquePtr));
|
||||
}
|
||||
|
||||
// Get the previous state of the prior instance for undo/redo purposes
|
||||
PrefabDom beforeInstanceDomBeforeRemoval;
|
||||
m_instanceToTemplateInterface->GenerateDomForInstance(beforeInstanceDomBeforeRemoval, beforeOwningInstance->get());
|
||||
|
||||
// Remove Entities
|
||||
for (AZ::Entity* nestedEntity : entities)
|
||||
{
|
||||
beforeOwningInstance->get().DetachEntity(nestedEntity->GetId()).release();
|
||||
}
|
||||
|
||||
// Create the Update node for the prior owning instance
|
||||
// Instance removal will be taken care of from the RemoveLink function for undo/redo purposes
|
||||
PrefabUndoHelpers::UpdatePrefabInstance(
|
||||
beforeOwningInstance->get(), "Update prior prefab instance", beforeInstanceDomBeforeRemoval, undoBatch);
|
||||
}
|
||||
|
||||
// Add Entities and Instances to new instance
|
||||
{
|
||||
// Add Instances
|
||||
for (auto& instanceUniquePtr : instanceUniquePtrs)
|
||||
{
|
||||
afterOwningInstance->get().AddInstance(AZStd::move(instanceUniquePtr));
|
||||
}
|
||||
|
||||
// Create Links
|
||||
for (auto& instanceInfo : instancePatches)
|
||||
{
|
||||
// Add a new link with the old dom
|
||||
CreateLink(
|
||||
*instanceInfo.first, afterOwningInstance->get().GetTemplateId(), undoBatch,
|
||||
AZStd::move(instanceInfo.second));
|
||||
}
|
||||
|
||||
// Get the previous state of the new instance for undo/redo purposes
|
||||
PrefabDom afterInstanceDomBeforeAdd;
|
||||
m_instanceToTemplateInterface->GenerateDomForInstance(afterInstanceDomBeforeAdd, afterOwningInstance->get());
|
||||
|
||||
// Add Entities
|
||||
for (AZ::Entity* nestedEntity : entities)
|
||||
{
|
||||
afterOwningInstance->get().AddEntity(*nestedEntity);
|
||||
}
|
||||
|
||||
// Create the Update node for the new owning instance
|
||||
PrefabUndoHelpers::UpdatePrefabInstance(
|
||||
afterOwningInstance->get(), "Update new prefab instance", afterInstanceDomBeforeAdd, undoBatch);
|
||||
}
|
||||
}
|
||||
|
||||
bool PrefabPublicHandler::IsInstanceContainerEntity(AZ::EntityId entityId) const
|
||||
@@ -748,16 +899,29 @@ namespace AzToolsFramework
|
||||
|
||||
if (!EntitiesBelongToSameInstance(entityIds))
|
||||
{
|
||||
return AZ::Failure(AZStd::string("Cannot duplicate multiple "
|
||||
"entities belonging to different instances with one operation."));
|
||||
return AZ::Failure(AZStd::string("Cannot duplicate multiple entities belonging to different instances with one operation."
|
||||
"Change your selection to contain entities in the same instance."));
|
||||
}
|
||||
|
||||
// We've already verified the entities are all owned by the same instance,
|
||||
// so we can just retrieve our instance from the first entity in the list.
|
||||
InstanceOptionalReference commonEntityOwningInstance = GetOwnerInstanceByEntityId(entityIds[0]);
|
||||
AZ_Assert(
|
||||
commonEntityOwningInstance.has_value(),
|
||||
"Failed to duplicate : Couldn't get a valid owning instance for the common root entity of the entities provided");
|
||||
AZ::EntityId firstEntityIdToDuplicate = entityIds[0];
|
||||
InstanceOptionalReference commonOwningInstance = GetOwnerInstanceByEntityId(firstEntityIdToDuplicate);
|
||||
if (!commonOwningInstance.has_value())
|
||||
{
|
||||
return AZ::Failure(AZStd::string("Failed to duplicate : Couldn't get a valid owning instance for the common root entity of the entities provided."));
|
||||
}
|
||||
|
||||
// If the first entity id is a container entity id, then we need to mark its parent as the common owning instance because you
|
||||
// cannot duplicate an instance from itself.
|
||||
if (commonOwningInstance->get().GetContainerEntityId() == firstEntityIdToDuplicate)
|
||||
{
|
||||
commonOwningInstance = commonOwningInstance->get().GetParentInstance();
|
||||
}
|
||||
if (!commonOwningInstance.has_value())
|
||||
{
|
||||
return AZ::Failure(AZStd::string("Failed to duplicate : Couldn't get a valid owning instance for the common root entity of the entities provided."));
|
||||
}
|
||||
|
||||
// This will cull out any entities that have ancestors in the list, since we will end up duplicating
|
||||
// the full nested hierarchy with what is returned from RetrieveAndSortPrefabEntitiesAndInstances
|
||||
@@ -770,105 +934,63 @@ namespace AzToolsFramework
|
||||
{
|
||||
AZ_PROFILE_SCOPE(AZ::Debug::ProfileCategory::AzToolsFramework, "DuplicateEntitiesInInstance::UndoCaptureAndDuplicateEntities");
|
||||
|
||||
// Take a snapshot of the instance DOM before we manipulate it
|
||||
Prefab::PrefabDom instanceDomBefore;
|
||||
m_instanceToTemplateInterface->GenerateDomForInstance(instanceDomBefore, commonEntityOwningInstance->get());
|
||||
|
||||
AZStd::vector<AZ::Entity*> entities;
|
||||
AZStd::vector<Instance*> instances;
|
||||
|
||||
// Gather all entities/instances in the hierarchy, but don't detach them because we are duplicating not deleting.
|
||||
EntityList inputEntityList = EntityIdSetToEntityList(duplicationSet);
|
||||
bool success = RetrieveAndSortPrefabEntitiesAndInstances(inputEntityList, commonEntityOwningInstance->get(), entities, instances);
|
||||
bool success = RetrieveAndSortPrefabEntitiesAndInstances(inputEntityList, commonOwningInstance->get(), entities, instances);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
return AZ::Failure(AZStd::string("Failed to retrieve entities and instances from the given list of entity ids for duplication"));
|
||||
}
|
||||
|
||||
// Make a copy of our before instance DOM where we will add our duplicated entities
|
||||
Prefab::PrefabDom instanceDomAfter;
|
||||
// Take a snapshot of the instance DOM before we manipulate it
|
||||
PrefabDom instanceDomBefore;
|
||||
m_instanceToTemplateInterface->GenerateDomForInstance(instanceDomBefore, commonOwningInstance->get());
|
||||
|
||||
// Make a copy of our before instance DOM where we will add our duplicated entities and/or instances
|
||||
PrefabDom instanceDomAfter;
|
||||
instanceDomAfter.CopyFrom(instanceDomBefore, instanceDomAfter.GetAllocator());
|
||||
|
||||
AZStd::unordered_map<EntityAlias, EntityAlias> oldAliasToNewAliasMap;
|
||||
AZStd::unordered_map<EntityAlias, QString> aliasToEntityDomMap;
|
||||
EntityIdList duplicatedEntityAndInstanceIds;
|
||||
|
||||
for (AZ::Entity* entity : entities)
|
||||
{
|
||||
EntityAliasOptionalReference oldAliasRef = commonEntityOwningInstance->get().GetEntityAlias(entity->GetId());
|
||||
AZ_Assert(oldAliasRef.has_value(), "No alias found for Entity in the DOM");
|
||||
EntityAlias oldAlias = oldAliasRef.value();
|
||||
// Duplicate any nested entities and instances as requested
|
||||
AZStd::unordered_map<InstanceAlias, Instance*> newInstanceAliasToOldInstanceMap;
|
||||
DuplicateNestedEntitiesInInstance(commonOwningInstance->get(),
|
||||
entities, instanceDomAfter, duplicatedEntityAndInstanceIds);
|
||||
DuplicateNestedInstancesInInstance(commonOwningInstance->get(),
|
||||
instances, instanceDomAfter, duplicatedEntityAndInstanceIds,
|
||||
newInstanceAliasToOldInstanceMap);
|
||||
|
||||
// Give this the outer allocator so that the memory reference will be valid when
|
||||
// it gets used for AddMember
|
||||
Prefab::PrefabDom entityDomBefore(&instanceDomAfter.GetAllocator());
|
||||
m_instanceToTemplateInterface->GenerateDomForEntity(entityDomBefore, *entity);
|
||||
|
||||
// Keep track of the old alias <-> new alias mapping for this duplicated entity
|
||||
// so we can fixup references later
|
||||
EntityAlias newEntityAlias = Instance::GenerateEntityAlias();
|
||||
oldAliasToNewAliasMap.insert(AZStd::make_pair(oldAlias, newEntityAlias));
|
||||
|
||||
rapidjson::StringBuffer buffer;
|
||||
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
|
||||
entityDomBefore.Accept(writer);
|
||||
|
||||
// Store our duplicated Entity DOM with its new alias as a string
|
||||
// so that we can fixup entity alias references before adding it
|
||||
// to the Entities member of our instance DOM
|
||||
QString entityDomString(buffer.GetString());
|
||||
aliasToEntityDomMap.insert(AZStd::make_pair(newEntityAlias, entityDomString));
|
||||
}
|
||||
|
||||
auto entitiesIter = instanceDomAfter.FindMember(PrefabDomUtils::EntitiesName);
|
||||
AZ_Assert(entitiesIter != instanceDomAfter.MemberEnd(), "Instance DOM missing the Entities member.");
|
||||
|
||||
// Now that all the duplicated Entity DOMs have been created, we need to iterate
|
||||
// through them and replace any previous EntityAlias references with the new ones.
|
||||
// These are more than just parent entity references for nested entities, this will
|
||||
// also cover any EntityId references that were made in the components between them.
|
||||
for (auto aliasEntityPair : aliasToEntityDomMap)
|
||||
{
|
||||
EntityAlias newEntityAlias = aliasEntityPair.first;
|
||||
QString newEntityDomString = aliasEntityPair.second;
|
||||
|
||||
// Replace all of the old alias references with the new ones
|
||||
// We bookend the aliases with \" and also with a / as an extra precaution to prevent
|
||||
// inadvertently replacing a matching string vs. where an actual EntityId is expected
|
||||
// This will cover both cases where an alias could be used in a normal entity vs. an instance
|
||||
for (auto aliasMapIter : oldAliasToNewAliasMap)
|
||||
{
|
||||
ReplaceOldAliases(newEntityDomString, aliasMapIter.first, aliasMapIter.second);
|
||||
}
|
||||
|
||||
// Create the new Entity DOM from parsing the JSON string
|
||||
Prefab::PrefabDom entityDomAfter(&instanceDomAfter.GetAllocator());
|
||||
entityDomAfter.Parse(newEntityDomString.toUtf8().constData());
|
||||
|
||||
// Add the new Entity DOM to the Entities member of the instance
|
||||
rapidjson::Value aliasName(newEntityAlias.c_str(), newEntityAlias.length(), instanceDomAfter.GetAllocator());
|
||||
entitiesIter->value.AddMember(AZStd::move(aliasName), entityDomAfter, instanceDomAfter.GetAllocator());
|
||||
}
|
||||
|
||||
PrefabUndoInstance* command = aznew PrefabUndoInstance("Entity duplication");
|
||||
PrefabUndoInstance* command = aznew PrefabUndoInstance("Entity/Instance duplication");
|
||||
command->SetParent(undoBatch.GetUndoBatch());
|
||||
command->Capture(instanceDomBefore, instanceDomAfter, commonEntityOwningInstance->get().GetTemplateId());
|
||||
command->RunRedo();
|
||||
command->Capture(instanceDomBefore, instanceDomAfter, commonOwningInstance->get().GetTemplateId());
|
||||
command->Redo();
|
||||
|
||||
EntityIdList duplicatedEntityIds;
|
||||
for (auto aliasMapIter : oldAliasToNewAliasMap)
|
||||
// Create links for our duplicated instances (if any were duplicated)
|
||||
for (auto [newInstanceAlias, oldInstance] : newInstanceAliasToOldInstanceMap)
|
||||
{
|
||||
EntityAlias newEntityAlias = aliasMapIter.second;
|
||||
LinkId oldLinkId = oldInstance->GetLinkId();
|
||||
auto linkRef = m_prefabSystemComponentInterface->FindLink(oldLinkId);
|
||||
AZ_Assert(
|
||||
linkRef.has_value(), "Unable to find link with id '%llu' during instance duplication.",
|
||||
oldLinkId);
|
||||
|
||||
AliasPath absoluteEntityPath = commonEntityOwningInstance->get().GetAbsoluteInstanceAliasPath();
|
||||
absoluteEntityPath.Append(newEntityAlias);
|
||||
PrefabDomValueReference linkPatches = linkRef->get().GetLinkPatches();
|
||||
AZ_Assert(
|
||||
linkPatches.has_value(), "Link with id '%llu' is missing patches.",
|
||||
oldLinkId);
|
||||
|
||||
AZ::EntityId newEntityId = InstanceEntityIdMapper::GenerateEntityIdForAliasPath(absoluteEntityPath);
|
||||
duplicatedEntityIds.push_back(newEntityId);
|
||||
PrefabDom linkPatchesCopy;
|
||||
linkPatchesCopy.CopyFrom(linkPatches->get(), linkPatchesCopy.GetAllocator());
|
||||
|
||||
m_prefabSystemComponentInterface->CreateLink(
|
||||
commonOwningInstance->get().GetTemplateId(), oldInstance->GetTemplateId(), newInstanceAlias, linkPatchesCopy);
|
||||
}
|
||||
|
||||
// Select the duplicated entities
|
||||
auto selectionUndo = aznew SelectionCommand(duplicatedEntityIds, "Select Duplicated Entities");
|
||||
// Select the duplicated entities/instances
|
||||
auto selectionUndo = aznew SelectionCommand(duplicatedEntityAndInstanceIds, "Select Duplicated Entities/Instances");
|
||||
selectionUndo->SetParent(undoBatch.GetUndoBatch());
|
||||
ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequestBus::Events::RunRedoSeparately, selectionUndo);
|
||||
}
|
||||
@@ -989,6 +1111,123 @@ namespace AzToolsFramework
|
||||
return AZ::Success();
|
||||
}
|
||||
|
||||
PrefabOperationResult PrefabPublicHandler::DetachPrefab(const AZ::EntityId& containerEntityId)
|
||||
{
|
||||
if (!containerEntityId.IsValid())
|
||||
{
|
||||
return AZ::Failure(AZStd::string("Cannot detach Prefab Instance with invalid container entity."));
|
||||
}
|
||||
|
||||
if (IsLevelInstanceContainerEntity(containerEntityId))
|
||||
{
|
||||
return AZ::Failure(AZStd::string("Cannot detach level Prefab Instance."));
|
||||
}
|
||||
|
||||
InstanceOptionalReference owningInstance = GetOwnerInstanceByEntityId(containerEntityId);
|
||||
if (owningInstance->get().GetContainerEntityId() != containerEntityId)
|
||||
{
|
||||
return AZ::Failure(AZStd::string("Input entity should be its owning Instance's container entity."));
|
||||
}
|
||||
|
||||
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework);
|
||||
|
||||
{
|
||||
AZ_PROFILE_SCOPE(AZ::Debug::ProfileCategory::AzToolsFramework, "Internal::DetachPrefab:UndoCapture");
|
||||
|
||||
ScopedUndoBatch undoBatch("Detach Prefab");
|
||||
|
||||
InstanceOptionalReference getParentInstanceResult = owningInstance->get().GetParentInstance();
|
||||
AZ_Assert(getParentInstanceResult.has_value(), "Can't get parent Instance from Instance of given container entity.");
|
||||
|
||||
auto& parentInstance = getParentInstanceResult->get();
|
||||
const auto parentTemplateId = parentInstance.GetTemplateId();
|
||||
|
||||
{
|
||||
auto instancePtr = parentInstance.DetachNestedInstance(owningInstance->get().GetInstanceAlias());
|
||||
AZ_Assert(instancePtr, "Can't detach selected Instance from its parent Instance.");
|
||||
|
||||
RemoveLink(instancePtr, parentTemplateId, undoBatch.GetUndoBatch());
|
||||
|
||||
Prefab::PrefabDom instanceDomBefore;
|
||||
m_instanceToTemplateInterface->GenerateDomForInstance(instanceDomBefore, parentInstance);
|
||||
|
||||
AZStd::unordered_map<AZ::EntityId, AZStd::string> oldEntityAliases;
|
||||
oldEntityAliases.emplace(containerEntityId, instancePtr->GetEntityAlias(containerEntityId)->get());
|
||||
|
||||
auto containerEntityPtr = instancePtr->DetachContainerEntity();
|
||||
auto& containerEntity = *containerEntityPtr.release();
|
||||
auto editorPrefabComponent = containerEntity.FindComponent<EditorPrefabComponent>();
|
||||
containerEntity.Deactivate();
|
||||
const bool editorPrefabComponentRemoved = containerEntity.RemoveComponent(editorPrefabComponent);
|
||||
AZ_Assert(editorPrefabComponentRemoved, "Remove EditorPrefabComponent failed.");
|
||||
delete editorPrefabComponent;
|
||||
containerEntity.Activate();
|
||||
|
||||
const bool containerEntityAdded = parentInstance.AddEntity(containerEntity);
|
||||
AZ_Assert(containerEntityAdded, "Add target Instance's container entity to its parent Instance failed.");
|
||||
|
||||
EntityIdList entityIds;
|
||||
entityIds.emplace_back(containerEntity.GetId());
|
||||
|
||||
instancePtr->GetEntities(
|
||||
[&](AZStd::unique_ptr<AZ::Entity>& entityPtr)
|
||||
{
|
||||
oldEntityAliases.emplace(entityPtr->GetId(), instancePtr->GetEntityAlias(entityPtr->GetId())->get());
|
||||
return true;
|
||||
});
|
||||
|
||||
instancePtr->DetachEntities(
|
||||
[&](AZStd::unique_ptr<AZ::Entity> entityPtr)
|
||||
{
|
||||
auto& entity = *entityPtr.release();
|
||||
const bool entityAdded = parentInstance.AddEntity(entity);
|
||||
AZ_Assert(entityAdded, "Add target Instance's entity to its parent Instance failed.");
|
||||
|
||||
entityIds.emplace_back(entity.GetId());
|
||||
});
|
||||
|
||||
Prefab::PrefabDom instanceDomAfter;
|
||||
m_instanceToTemplateInterface->GenerateDomForInstance(instanceDomAfter, parentInstance);
|
||||
|
||||
PrefabUndoInstance* command = aznew PrefabUndoInstance("Instance detachment");
|
||||
command->Capture(instanceDomBefore, instanceDomAfter, parentTemplateId);
|
||||
command->SetParent(undoBatch.GetUndoBatch());
|
||||
{
|
||||
AZ_PROFILE_SCOPE(AZ::Debug::ProfileCategory::AzToolsFramework, "Internal::DetachPrefab:RunRedo");
|
||||
command->RunRedo();
|
||||
}
|
||||
|
||||
const auto instanceTemplateId = instancePtr->GetTemplateId();
|
||||
auto parentContainerEntityId = parentInstance.GetContainerEntityId();
|
||||
instancePtr->GetNestedInstances(
|
||||
[&](AZStd::unique_ptr<Instance>& nestedInstancePtr)
|
||||
{
|
||||
//get previous link patch
|
||||
auto linkRef = m_prefabSystemComponentInterface->FindLink(nestedInstancePtr->GetLinkId());
|
||||
PrefabDomValueReference linkPatches = linkRef->get().GetLinkPatches();
|
||||
AZ_Assert(
|
||||
linkPatches.has_value(), "Unable to get patches on link with id '%llu' during prefab creation.",
|
||||
nestedInstancePtr->GetLinkId());
|
||||
|
||||
PrefabDom linkPatchesCopy;
|
||||
linkPatchesCopy.CopyFrom(linkPatches->get(), linkPatchesCopy.GetAllocator());
|
||||
|
||||
RemoveLink(nestedInstancePtr, instanceTemplateId, undoBatch.GetUndoBatch());
|
||||
|
||||
UpdateLinkPatchesWithNewEntityAliases(linkPatchesCopy, oldEntityAliases, parentInstance);
|
||||
|
||||
CreateLink(*nestedInstancePtr, parentTemplateId, undoBatch.GetUndoBatch(),
|
||||
AZStd::move(linkPatchesCopy), true);
|
||||
});
|
||||
}
|
||||
|
||||
AzToolsFramework::ToolsApplicationRequestBus::Broadcast(
|
||||
&AzToolsFramework::ToolsApplicationRequestBus::Events::ClearDirtyEntities);
|
||||
}
|
||||
|
||||
return AZ::Success();
|
||||
}
|
||||
|
||||
void PrefabPublicHandler::GenerateContainerEntityTransform(const EntityList& topLevelEntities,
|
||||
AZ::Vector3& translation, AZ::Quaternion& rotation)
|
||||
{
|
||||
@@ -1240,8 +1479,159 @@ namespace AzToolsFramework
|
||||
return true;
|
||||
}
|
||||
|
||||
void PrefabPublicHandler::DuplicateNestedEntitiesInInstance(Instance& commonOwningInstance,
|
||||
const AZStd::vector<AZ::Entity*>& entities, PrefabDom& domToAddDuplicatedEntitiesUnder,
|
||||
EntityIdList& duplicatedEntityIds)
|
||||
{
|
||||
if (entities.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AZStd::unordered_map<EntityAlias, EntityAlias> oldAliasToNewAliasMap;
|
||||
AZStd::unordered_map<EntityAlias, QString> aliasToEntityDomMap;
|
||||
|
||||
for (AZ::Entity* entity : entities)
|
||||
{
|
||||
EntityAliasOptionalReference oldAliasRef = commonOwningInstance.GetEntityAlias(entity->GetId());
|
||||
AZ_Assert(oldAliasRef.has_value(), "No alias found for Entity in the DOM");
|
||||
EntityAlias oldAlias = oldAliasRef.value();
|
||||
|
||||
// Give this the outer allocator so that the memory reference will be valid when
|
||||
// it gets used for AddMember
|
||||
PrefabDom entityDomBefore(&domToAddDuplicatedEntitiesUnder.GetAllocator());
|
||||
m_instanceToTemplateInterface->GenerateDomForEntity(entityDomBefore, *entity);
|
||||
|
||||
// Keep track of the old alias <-> new alias mapping for this duplicated entity
|
||||
// so we can fixup references later
|
||||
EntityAlias newEntityAlias = Instance::GenerateEntityAlias();
|
||||
oldAliasToNewAliasMap.emplace(oldAlias, newEntityAlias);
|
||||
|
||||
rapidjson::StringBuffer buffer;
|
||||
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
|
||||
entityDomBefore.Accept(writer);
|
||||
|
||||
// Store our duplicated Entity DOM with its new alias as a string
|
||||
// so that we can fixup entity alias references before adding it
|
||||
// to the Entities member of our instance DOM
|
||||
QString entityDomString(buffer.GetString());
|
||||
aliasToEntityDomMap.emplace(newEntityAlias, entityDomString);
|
||||
}
|
||||
|
||||
auto entitiesIter = domToAddDuplicatedEntitiesUnder.FindMember(PrefabDomUtils::EntitiesName);
|
||||
AZ_Assert(entitiesIter != domToAddDuplicatedEntitiesUnder.MemberEnd(), "Instance DOM missing the Entities member.");
|
||||
|
||||
// Now that all the duplicated Entity DOMs have been created, we need to iterate
|
||||
// through them and replace any previous EntityAlias references with the new ones.
|
||||
// These are more than just parent entity references for nested entities, this will
|
||||
// also cover any EntityId references that were made in the components between them.
|
||||
for (auto [newEntityAlias, newEntityDomString] : aliasToEntityDomMap)
|
||||
{
|
||||
// Replace all of the old alias references with the new ones
|
||||
for (auto [oldAlias, newAlias] : oldAliasToNewAliasMap)
|
||||
{
|
||||
ReplaceOldAliases(newEntityDomString, oldAlias, newAlias);
|
||||
}
|
||||
|
||||
// Create the new Entity DOM from parsing the JSON string
|
||||
PrefabDom entityDomAfter(&domToAddDuplicatedEntitiesUnder.GetAllocator());
|
||||
entityDomAfter.Parse(newEntityDomString.toUtf8().constData());
|
||||
|
||||
// Add the new Entity DOM to the Entities member of the instance
|
||||
rapidjson::Value aliasName(newEntityAlias.c_str(), newEntityAlias.length(), domToAddDuplicatedEntitiesUnder.GetAllocator());
|
||||
entitiesIter->value.AddMember(AZStd::move(aliasName), entityDomAfter, domToAddDuplicatedEntitiesUnder.GetAllocator());
|
||||
}
|
||||
|
||||
for (auto aliasMapIter : oldAliasToNewAliasMap)
|
||||
{
|
||||
EntityAlias newEntityAlias = aliasMapIter.second;
|
||||
|
||||
AliasPath absoluteEntityPath = commonOwningInstance.GetAbsoluteInstanceAliasPath();
|
||||
absoluteEntityPath.Append(newEntityAlias);
|
||||
|
||||
AZ::EntityId newEntityId = InstanceEntityIdMapper::GenerateEntityIdForAliasPath(absoluteEntityPath);
|
||||
duplicatedEntityIds.push_back(newEntityId);
|
||||
}
|
||||
}
|
||||
|
||||
void PrefabPublicHandler::DuplicateNestedInstancesInInstance(Instance& commonOwningInstance,
|
||||
const AZStd::vector<Instance*>& instances, PrefabDom& domToAddDuplicatedInstancesUnder,
|
||||
EntityIdList& duplicatedEntityIds, AZStd::unordered_map<InstanceAlias, Instance*>& newInstanceAliasToOldInstanceMap)
|
||||
{
|
||||
if (instances.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AZStd::unordered_map<InstanceAlias, InstanceAlias> oldInstanceAliasToNewInstanceAliasMap;
|
||||
AZStd::unordered_map<InstanceAlias, QString> aliasToInstanceDomMap;
|
||||
|
||||
for (auto instance : instances)
|
||||
{
|
||||
PrefabDom nestedInstanceDomBefore;
|
||||
m_instanceToTemplateInterface->GenerateDomForInstance(nestedInstanceDomBefore, *instance);
|
||||
|
||||
// Keep track of the old alias <-> new alias mapping for this duplicated instance
|
||||
// so we can fixup references later
|
||||
InstanceAlias oldAlias = instance->GetInstanceAlias();
|
||||
InstanceAlias newInstanceAlias = Instance::GenerateInstanceAlias();
|
||||
oldInstanceAliasToNewInstanceAliasMap.emplace(oldAlias, newInstanceAlias);
|
||||
|
||||
// Keep track of our new instance alias with the Instance it was duplicated from,
|
||||
// so that after all instances are duplicated, we can go back and create links for them
|
||||
newInstanceAliasToOldInstanceMap.emplace(newInstanceAlias, instance);
|
||||
|
||||
rapidjson::StringBuffer buffer;
|
||||
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
|
||||
nestedInstanceDomBefore.Accept(writer);
|
||||
|
||||
// Store our duplicated Instance DOM with its new alias as a string
|
||||
// so that we can fixup instance alias references before adding it
|
||||
// to the Instances member of our instance DOM
|
||||
QString instanceDomString(buffer.GetString());
|
||||
aliasToInstanceDomMap.emplace(newInstanceAlias, instanceDomString);
|
||||
}
|
||||
|
||||
auto instancesIter = domToAddDuplicatedInstancesUnder.FindMember(PrefabDomUtils::InstancesName);
|
||||
AZ_Assert(instancesIter != domToAddDuplicatedInstancesUnder.MemberEnd(), "Instance DOM missing the Instances member.");
|
||||
|
||||
// Now that all the duplicated Instance DOMs have been created, we need to iterate
|
||||
// through them and replace any previous InstanceAlias references with the new ones.
|
||||
for (auto [newInstanceAlias, newInstanceDomString]: aliasToInstanceDomMap)
|
||||
{
|
||||
// Replace all of the old alias references with the new ones
|
||||
for (auto [oldAlias, newAlias] : oldInstanceAliasToNewInstanceAliasMap)
|
||||
{
|
||||
ReplaceOldAliases(newInstanceDomString, oldAlias, newAlias);
|
||||
}
|
||||
|
||||
// Create the new Instance DOM from parsing the JSON string
|
||||
PrefabDom nestedInstanceDomAfter(&domToAddDuplicatedInstancesUnder.GetAllocator());
|
||||
nestedInstanceDomAfter.Parse(newInstanceDomString.toUtf8().constData());
|
||||
|
||||
// Add the new Instance DOM to the Instances member of the instance
|
||||
rapidjson::Value aliasName(newInstanceAlias.c_str(), newInstanceAlias.length(), domToAddDuplicatedInstancesUnder.GetAllocator());
|
||||
instancesIter->value.AddMember(AZStd::move(aliasName), nestedInstanceDomAfter, domToAddDuplicatedInstancesUnder.GetAllocator());
|
||||
}
|
||||
|
||||
for (auto aliasMapIter : oldInstanceAliasToNewInstanceAliasMap)
|
||||
{
|
||||
InstanceAlias newInstanceAlias = aliasMapIter.second;
|
||||
|
||||
AliasPath absoluteInstancePath = commonOwningInstance.GetAbsoluteInstanceAliasPath();
|
||||
absoluteInstancePath.Append(newInstanceAlias);
|
||||
|
||||
AZ::EntityId newEntityId = InstanceEntityIdMapper::GenerateEntityIdForAliasPath(absoluteInstancePath);
|
||||
duplicatedEntityIds.push_back(newEntityId);
|
||||
}
|
||||
}
|
||||
|
||||
void PrefabPublicHandler::ReplaceOldAliases(QString& stringToReplace, AZStd::string_view oldAlias, AZStd::string_view newAlias)
|
||||
{
|
||||
// Replace all of the old alias references with the new ones
|
||||
// We bookend the aliases with \" and also with a / as an extra precaution to prevent
|
||||
// inadvertently replacing a matching string vs. where an actual EntityId is expected
|
||||
// This will cover both cases where an alias could be used in a normal entity vs. an instance
|
||||
QString oldAliasQuotes = QString("\"%1\"").arg(oldAlias.data());
|
||||
QString newAliasQuotes = QString("\"%1\"").arg(newAlias.data());
|
||||
|
||||
@@ -1252,5 +1642,30 @@ namespace AzToolsFramework
|
||||
|
||||
stringToReplace.replace(oldAliasPathRef, newAliasPathRef);
|
||||
}
|
||||
|
||||
void PrefabPublicHandler::UpdateLinkPatchesWithNewEntityAliases(
|
||||
PrefabDom& linkPatch,
|
||||
const AZStd::unordered_map<AZ::EntityId, AZStd::string>& oldEntityAliases,
|
||||
Instance& newParent)
|
||||
{
|
||||
rapidjson::StringBuffer buffer;
|
||||
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
|
||||
linkPatch.Accept(writer);
|
||||
QString previousPatchString(buffer.GetString());
|
||||
|
||||
for (const auto& [entityId, oldEntityAlias] : oldEntityAliases)
|
||||
{
|
||||
EntityAliasOptionalReference newEntityAlias = newParent.GetEntityAlias(entityId);
|
||||
AZ_Assert(
|
||||
newEntityAlias.has_value(),
|
||||
"Could not fetch entity alias for entity with id '%llu' during prefab creation.",
|
||||
static_cast<AZ::u64>(entityId));
|
||||
|
||||
ReplaceOldAliases(previousPatchString, oldEntityAlias, newEntityAlias->get());
|
||||
}
|
||||
|
||||
linkPatch.Parse(previousPatchString.toUtf8().constData());
|
||||
}
|
||||
|
||||
} // namespace Prefab
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
@@ -64,6 +64,8 @@ namespace AzToolsFramework
|
||||
PrefabOperationResult DeleteEntitiesAndAllDescendantsInInstance(const EntityIdList& entityIds) override;
|
||||
PrefabOperationResult DuplicateEntitiesInInstance(const EntityIdList& entityIds) override;
|
||||
|
||||
PrefabOperationResult DetachPrefab(const AZ::EntityId& containerEntityId) override;
|
||||
|
||||
private:
|
||||
PrefabOperationResult DeleteFromInstance(const EntityIdList& entityIds, bool deleteDescendants);
|
||||
bool RetrieveAndSortPrefabEntitiesAndInstances(const EntityList& inputEntities, Instance& commonRootEntityOwningInstance,
|
||||
@@ -71,6 +73,33 @@ namespace AzToolsFramework
|
||||
|
||||
InstanceOptionalReference GetOwnerInstanceByEntityId(AZ::EntityId entityId) const;
|
||||
bool EntitiesBelongToSameInstance(const EntityIdList& entityIds) const;
|
||||
|
||||
/**
|
||||
* Duplicate a list of entities owned by a common owning instance by directly
|
||||
* copying/modifying their entries in the instance DOM
|
||||
*
|
||||
* \param commonOwningInstance The common owning instance of all the entities being duplicated.
|
||||
* \param entities The list of Entities that will be duplicated.
|
||||
* \param domToAddDuplicatedEntitiesUnder The DOM of the common owning instance where the duplicated
|
||||
* entity DOM values will be added to.
|
||||
* \param duplicatedEntityIds A list of EntityIds corresponding to the entities that were duplicated.
|
||||
*/
|
||||
void DuplicateNestedEntitiesInInstance(Instance& commonOwningInstance,
|
||||
const AZStd::vector<AZ::Entity*>& entities, PrefabDom& domToAddDuplicatedEntitiesUnder,
|
||||
EntityIdList& duplicatedEntityIds);
|
||||
/**
|
||||
* Duplicate a list of instances owned by a common owning instance by directly
|
||||
* copying/modifying their entries in the instance DOM
|
||||
*
|
||||
* \param commonOwningInstance The common owning instance of all the instances being duplicated.
|
||||
* \param entities The list of Instances that will be duplicated.
|
||||
* \param domToAddDuplicatedInstancesUnder The DOM of the common owning instance where the duplicated
|
||||
* instance DOM values will be added to.
|
||||
* \param duplicatedEntityIds A list of EntityIds corresponding to the instances that were duplicated.
|
||||
*/
|
||||
void DuplicateNestedInstancesInInstance(Instance& commonOwningInstance,
|
||||
const AZStd::vector<Instance*>& instances, PrefabDom& domToAddDuplicatedInstancesUnder,
|
||||
EntityIdList& duplicatedEntityIds, AZStd::unordered_map<InstanceAlias, Instance*>& newInstanceAliasToOldInstanceMap);
|
||||
|
||||
/**
|
||||
* Applies the correct transform changes to the container entity based on the parent and child entities provided, and returns an appropriate patch.
|
||||
@@ -88,8 +117,8 @@ namespace AzToolsFramework
|
||||
/**
|
||||
* Creates a link between the templates of an instance and its parent.
|
||||
*
|
||||
* \param sourceInstance The instance that corresponds to the source template of the link.
|
||||
* \param targetInstance The id of the target template.
|
||||
* \param sourceInstance The instance that corresponds to the source template of the link (child).
|
||||
* \param targetInstance The id of the target template (parent).
|
||||
* \param undoBatch The undo batch to set as parent for this create link action.
|
||||
* \param patch The patch to store in the newly created link dom.
|
||||
* \param isUndoRedoSupportNeeded The flag indicating whether the link should be created with undo/redo support or not.
|
||||
@@ -132,7 +161,18 @@ namespace AzToolsFramework
|
||||
bool IsCyclicalDependencyFound(
|
||||
InstanceOptionalConstReference instance, const AZStd::unordered_set<AZ::IO::Path>& templateSourcePaths);
|
||||
|
||||
void ReplaceOldAliases(QString& stringToReplace, AZStd::string_view oldAlias, AZStd::string_view newAlias);
|
||||
static void Internal_HandleContainerOverride(
|
||||
UndoSystem::URSequencePoint* undoBatch, AZ::EntityId entityId, const PrefabDom& patch, const LinkId linkId);
|
||||
static void Internal_HandleEntityChange(
|
||||
UndoSystem::URSequencePoint* undoBatch, AZ::EntityId entityId, PrefabDom& beforeState, PrefabDom& afterState);
|
||||
void Internal_HandleInstanceChange(UndoSystem::URSequencePoint* undoBatch, AZ::Entity* entity, AZ::EntityId beforeParentId, AZ::EntityId afterParentId);
|
||||
|
||||
void UpdateLinkPatchesWithNewEntityAliases(
|
||||
PrefabDom& linkPatch,
|
||||
const AZStd::unordered_map<AZ::EntityId, AZStd::string>& oldEntityAliases,
|
||||
Instance& newParent);
|
||||
|
||||
static void ReplaceOldAliases(QString& stringToReplace, AZStd::string_view oldAlias, AZStd::string_view newAlias);
|
||||
|
||||
static Instance* GetParentInstance(Instance* instance);
|
||||
static Instance* GetAncestorOfInstanceThatIsChildOfRoot(const Instance* ancestor, Instance* descendant);
|
||||
|
||||
@@ -150,6 +150,17 @@ namespace AzToolsFramework
|
||||
* @return An outcome object; on failure, it comes with an error message detailing the cause of the error.
|
||||
*/
|
||||
virtual PrefabOperationResult DuplicateEntitiesInInstance(const EntityIdList& entityIds) = 0;
|
||||
|
||||
/**
|
||||
* If the entity id is a container entity id, detaches the prefab instance corresponding to it. This includes converting
|
||||
* the container entity into a regular entity and putting it under the parent prefab, removing the link between this
|
||||
* instance and the parent, removing links between this instance and it's nested instances, adding entities directly
|
||||
* owned by this instance under the parent instance.
|
||||
* Bails if the entity is not a container entity or belongs to the level prefab instance.
|
||||
* @param containerEntityId The container entity id of the instance to detach.
|
||||
* @return An outcome object; on failure, it comes with an error message detailing the cause of the error.
|
||||
*/
|
||||
virtual PrefabOperationResult DetachPrefab(const AZ::EntityId& containerEntityId) = 0;
|
||||
};
|
||||
|
||||
} // namespace Prefab
|
||||
|
||||
@@ -652,7 +652,8 @@ namespace AzToolsFramework
|
||||
if (instancesValue->get().FindMember(rapidjson::StringRef(instanceAlias.c_str())) == instancesValue->get().MemberEnd())
|
||||
{
|
||||
instancesValue->get().AddMember(
|
||||
rapidjson::StringRef(instanceAlias.c_str()), PrefabDomValue(), targetTemplateDom.GetAllocator());
|
||||
rapidjson::Value(instanceAlias.c_str(), targetTemplateDom.GetAllocator()), PrefabDomValue(),
|
||||
targetTemplateDom.GetAllocator());
|
||||
}
|
||||
|
||||
Template& sourceTemplate = sourceTemplateRef->get();
|
||||
@@ -705,14 +706,14 @@ namespace AzToolsFramework
|
||||
"Prefab - PrefabSystemComponent::RemoveLink - "
|
||||
"Failed to remove Link with Id '%llu' for Instance '%s' of source Template with Id '%llu' "
|
||||
"from TemplateToLinkIdsMap.",
|
||||
linkId, link.GetSourceTemplateId(), link.GetInstanceName().c_str());
|
||||
linkId, link.GetInstanceName().c_str(), link.GetSourceTemplateId());
|
||||
|
||||
result = RemoveLinkFromTargetTemplate(linkId, link);
|
||||
AZ_Assert(result,
|
||||
"Prefab - PrefabSystemComponent::RemoveLink - "
|
||||
"Failed to remove Link with Id '%llu' for Instance '%s' of source Template with Id '%llu' "
|
||||
"from target Template with Id '%llu'.",
|
||||
linkId, link.GetSourceTemplateId(), link.GetInstanceName().c_str(), link.GetTargetTemplateId());
|
||||
linkId, link.GetInstanceName().c_str(), link.GetSourceTemplateId(), link.GetTargetTemplateId());
|
||||
|
||||
m_linkIdMap.erase(linkId);
|
||||
|
||||
|
||||
@@ -73,14 +73,16 @@ namespace AzToolsFramework
|
||||
}
|
||||
|
||||
PrefabDom oldData;
|
||||
Retrieve(entityId, oldData);
|
||||
AZ::EntityId oldParentId;
|
||||
Retrieve(entityId, oldData, oldParentId);
|
||||
|
||||
UpdateCache(entityId);
|
||||
|
||||
PrefabDom newData;
|
||||
Retrieve(entityId, newData);
|
||||
AZ::EntityId newParentId;
|
||||
Retrieve(entityId, newData, newParentId);
|
||||
|
||||
if (newData != oldData)
|
||||
if (newData != oldData || oldParentId != newParentId)
|
||||
{
|
||||
// display a useful message
|
||||
AZ::Entity* entity = nullptr;
|
||||
@@ -106,7 +108,7 @@ namespace AzToolsFramework
|
||||
// Clear out newly generated data and
|
||||
// replace with original data to ensure debug mode has the same data as profile/release
|
||||
// in the event of the consistency check failing.
|
||||
m_entitySavedStates[entityId] = AZStd::move(oldData);
|
||||
m_entitySavedStates[entityId] = {AZStd::move(oldData), oldParentId};
|
||||
|
||||
#endif // ENABLE_UNDOCACHE_CONSISTENCY_CHECKS
|
||||
}
|
||||
@@ -140,10 +142,13 @@ namespace AzToolsFramework
|
||||
return;
|
||||
}
|
||||
|
||||
AZ::EntityId parentId;
|
||||
AZ::TransformBus::EventResult(parentId, entityId, &AZ::TransformBus::Events::GetParentId);
|
||||
|
||||
// Capture it
|
||||
PrefabDom entityDom;
|
||||
m_instanceToTemplateInterface->GenerateDomForEntity(entityDom, *entity);
|
||||
m_entitySavedStates.emplace(AZStd::make_pair(entityId, AZStd::move(entityDom)));
|
||||
m_entitySavedStates[entityId] = {AZStd::move(entityDom), parentId};
|
||||
|
||||
AZLOG("Prefab Undo", "Correctly updated cache for entity of id %llu (%s)", static_cast<AZ::u64>(entityId), entity->GetName().c_str());
|
||||
|
||||
@@ -155,7 +160,7 @@ namespace AzToolsFramework
|
||||
m_entitySavedStates.erase(entityId);
|
||||
}
|
||||
|
||||
bool PrefabUndoCache::Retrieve(const AZ::EntityId& entityId, PrefabDom& outDom)
|
||||
bool PrefabUndoCache::Retrieve(const AZ::EntityId& entityId, PrefabDom& outDom, AZ::EntityId& parentId)
|
||||
{
|
||||
auto it = m_entitySavedStates.find(entityId);
|
||||
|
||||
@@ -164,14 +169,15 @@ namespace AzToolsFramework
|
||||
return false;
|
||||
}
|
||||
|
||||
outDom = AZStd::move(m_entitySavedStates[entityId]);
|
||||
outDom = AZStd::move(m_entitySavedStates[entityId].dom);
|
||||
parentId = m_entitySavedStates[entityId].parentId;
|
||||
m_entitySavedStates.erase(entityId);
|
||||
return true;
|
||||
}
|
||||
|
||||
void PrefabUndoCache::Store(const AZ::EntityId& entityId, PrefabDom&& dom)
|
||||
void PrefabUndoCache::Store(const AZ::EntityId& entityId, PrefabDom&& dom, const AZ::EntityId& parentId)
|
||||
{
|
||||
m_entitySavedStates.emplace(AZStd::make_pair(entityId, AZStd::move(dom)));
|
||||
m_entitySavedStates[entityId] = {AZStd::move(dom), parentId};
|
||||
}
|
||||
|
||||
void PrefabUndoCache::Clear()
|
||||
|
||||
@@ -46,14 +46,19 @@ namespace AzToolsFramework
|
||||
void Validate(const AZ::EntityId& entityId) override;
|
||||
|
||||
// Retrieve the last known state for an entity
|
||||
bool Retrieve(const AZ::EntityId& entityId, PrefabDom& outDom);
|
||||
bool Retrieve(const AZ::EntityId& entityId, PrefabDom& outDom, AZ::EntityId& parentId);
|
||||
|
||||
// Store dom as the cached state of entityId
|
||||
void Store(const AZ::EntityId& entityId, PrefabDom&& dom);
|
||||
void Store(const AZ::EntityId& entityId, PrefabDom&& dom, const AZ::EntityId& parentId);
|
||||
|
||||
private:
|
||||
typedef AZStd::unordered_map<AZ::EntityId, PrefabDom> EntityDomMap;
|
||||
EntityDomMap m_entitySavedStates;
|
||||
struct PrefabUndoCacheItem
|
||||
{
|
||||
PrefabDom dom;
|
||||
AZ::EntityId parentId;
|
||||
};
|
||||
typedef AZStd::unordered_map<AZ::EntityId, PrefabUndoCacheItem> EntityCache;
|
||||
EntityCache m_entitySavedStates;
|
||||
|
||||
InstanceEntityMapperInterface* m_instanceEntityMapperInterface = nullptr;
|
||||
InstanceToTemplateInterface* m_instanceToTemplateInterface = nullptr;
|
||||
|
||||
+28
-20
@@ -37,13 +37,13 @@ namespace AzToolsFramework
|
||||
axisLength, AzFramework::ViewportColors::XAxisColor, AzFramework::ViewportColors::YAxisColor,
|
||||
AzFramework::ViewportColors::ZAxisColor);
|
||||
|
||||
auto mouseDownCallback = [this](const LinearManipulator::Action& action) {
|
||||
auto mouseDownCallback = [this]([[maybe_unused]] const LinearManipulator::Action& action)
|
||||
{
|
||||
AZ::Vector3 nonUniformScale = AZ::Vector3::CreateOne();
|
||||
|
||||
AZ::NonUniformScaleRequestBus::EventResult(
|
||||
nonUniformScale, m_entityComponentIdPair.GetEntityId(), &AZ::NonUniformScaleRequests::GetScale);
|
||||
|
||||
m_initialScale = nonUniformScale + action.m_start.m_scaleSnapOffset;
|
||||
m_initialScale = nonUniformScale;
|
||||
|
||||
AZ::NonUniformScaleRequestBus::Event(
|
||||
m_entityComponentIdPair.GetEntityId(), &AZ::NonUniformScaleRequests::SetScale, m_initialScale);
|
||||
@@ -51,29 +51,37 @@ namespace AzToolsFramework
|
||||
|
||||
m_manipulators->InstallAxisLeftMouseDownCallback(mouseDownCallback);
|
||||
|
||||
m_manipulators->InstallAxisMouseMoveCallback([this](const LinearManipulator::Action& action) {
|
||||
const AZ::Vector3 scaleMultiplier =
|
||||
(AZ::Vector3::CreateOne() + ((action.LocalScaleOffset() * action.m_start.m_sign) / m_initialScale));
|
||||
m_manipulators->InstallAxisMouseMoveCallback(
|
||||
[this](const LinearManipulator::Action& action)
|
||||
{
|
||||
const AZ::Vector3 scaleMultiplier =
|
||||
(AZ::Vector3::CreateOne() + ((action.LocalScaleOffset() * action.m_start.m_sign) / m_initialScale));
|
||||
|
||||
AZ::NonUniformScaleRequestBus::Event(
|
||||
m_entityComponentIdPair.GetEntityId(), &AZ::NonUniformScaleRequests::SetScale,
|
||||
(scaleMultiplier * m_initialScale).GetClamp(AZ::Vector3(AZ::MinTransformScale), AZ::Vector3(AZ::MaxTransformScale)));
|
||||
});
|
||||
AZ::NonUniformScaleRequestBus::Event(
|
||||
m_entityComponentIdPair.GetEntityId(), &AZ::NonUniformScaleRequests::SetScale,
|
||||
(scaleMultiplier * m_initialScale)
|
||||
.GetClamp(AZ::Vector3(AZ::MinTransformScale), AZ::Vector3(AZ::MaxTransformScale)));
|
||||
});
|
||||
|
||||
m_manipulators->InstallUniformLeftMouseDownCallback(mouseDownCallback);
|
||||
|
||||
m_manipulators->InstallUniformMouseMoveCallback([this](const LinearManipulator::Action& action) {
|
||||
const auto sumVectorElements = [](const AZ::Vector3& vec) { return vec.GetX() + vec.GetY() + vec.GetZ(); };
|
||||
m_manipulators->InstallUniformMouseMoveCallback(
|
||||
[this](const LinearManipulator::Action& action)
|
||||
{
|
||||
const auto sumVectorElements = [](const AZ::Vector3& vec)
|
||||
{
|
||||
return vec.GetX() + vec.GetY() + vec.GetZ();
|
||||
};
|
||||
|
||||
const float minScaleMultiplier = AZ::MinTransformScale / m_initialScale.GetMinElement();
|
||||
const float maxScaleMultiplier = AZ::MaxTransformScale / m_initialScale.GetMaxElement();
|
||||
const float scaleMultiplier = AZ::GetClamp(
|
||||
1.0f + sumVectorElements(action.m_start.m_sign * action.LocalScaleOffset() / m_initialScale), minScaleMultiplier,
|
||||
maxScaleMultiplier);
|
||||
const float minScaleMultiplier = AZ::MinTransformScale / m_initialScale.GetMinElement();
|
||||
const float maxScaleMultiplier = AZ::MaxTransformScale / m_initialScale.GetMaxElement();
|
||||
const float scaleMultiplier = AZ::GetClamp(
|
||||
1.0f + sumVectorElements(action.m_start.m_sign * action.LocalScaleOffset() / m_initialScale), minScaleMultiplier,
|
||||
maxScaleMultiplier);
|
||||
|
||||
AZ::NonUniformScaleRequestBus::Event(
|
||||
m_entityComponentIdPair.GetEntityId(), &AZ::NonUniformScaleRequests::SetScale, scaleMultiplier * m_initialScale);
|
||||
});
|
||||
AZ::NonUniformScaleRequestBus::Event(
|
||||
m_entityComponentIdPair.GetEntityId(), &AZ::NonUniformScaleRequests::SetScale, scaleMultiplier * m_initialScale);
|
||||
});
|
||||
}
|
||||
|
||||
NonUniformScaleComponentMode::~NonUniformScaleComponentMode()
|
||||
|
||||
+29
@@ -237,6 +237,24 @@ namespace AzToolsFramework
|
||||
{
|
||||
deleteAction->setDisabled(true);
|
||||
}
|
||||
|
||||
// Detach Prefab
|
||||
if (selectedEntities.size() == 1)
|
||||
{
|
||||
AZ::EntityId selectedEntity = selectedEntities[0];
|
||||
|
||||
if (s_prefabPublicInterface->IsInstanceContainerEntity(selectedEntity) &&
|
||||
!s_prefabPublicInterface->IsLevelInstanceContainerEntity(selectedEntity))
|
||||
{
|
||||
QAction* detachPrefabAction = menu->addAction(QObject::tr("Detach Prefab..."));
|
||||
QObject::connect(
|
||||
detachPrefabAction, &QAction::triggered, detachPrefabAction,
|
||||
[this, selectedEntity]
|
||||
{
|
||||
ContextMenu_DetachPrefab(selectedEntity);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PrefabIntegrationManager::HandleSourceFileType(AZStd::string_view sourceFilePath, AZ::EntityId parentId, AZ::Vector3 position) const
|
||||
@@ -392,6 +410,17 @@ namespace AzToolsFramework
|
||||
}
|
||||
}
|
||||
|
||||
void PrefabIntegrationManager::ContextMenu_DetachPrefab(AZ::EntityId containerEntity)
|
||||
{
|
||||
PrefabOperationResult detachPrefabResult =
|
||||
s_prefabPublicInterface->DetachPrefab(containerEntity);
|
||||
|
||||
if (!detachPrefabResult.IsSuccess())
|
||||
{
|
||||
WarnUserOfError("Detach Prefab error", detachPrefabResult.GetError());
|
||||
}
|
||||
}
|
||||
|
||||
void PrefabIntegrationManager::GenerateSuggestedFilenameFromEntities(const EntityIdList& entityIds, AZStd::string& outName)
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework);
|
||||
|
||||
@@ -93,6 +93,7 @@ namespace AzToolsFramework
|
||||
static void ContextMenu_EditPrefab(AZ::EntityId containerEntity);
|
||||
static void ContextMenu_SavePrefab(AZ::EntityId containerEntity);
|
||||
static void ContextMenu_DeleteSelected();
|
||||
static void ContextMenu_DetachPrefab(AZ::EntityId containerEntity);
|
||||
|
||||
// Prompt and resolve dialogs
|
||||
static bool QueryUserForPrefabSaveLocation(
|
||||
|
||||
+2
-1
@@ -969,7 +969,8 @@ namespace AzToolsFramework
|
||||
{
|
||||
// Build up components to display
|
||||
SharedComponentArray sharedComponentArray;
|
||||
BuildSharedComponentArray(sharedComponentArray, selectionEntityTypeInfo != SelectionEntityTypeInfo::OnlyStandardEntities);
|
||||
BuildSharedComponentArray(sharedComponentArray,
|
||||
!(selectionEntityTypeInfo == SelectionEntityTypeInfo::OnlyStandardEntities || selectionEntityTypeInfo == SelectionEntityTypeInfo::OnlyPrefabEntities));
|
||||
|
||||
if (sharedComponentArray.size() == 0)
|
||||
{
|
||||
|
||||
+11
@@ -124,4 +124,15 @@ namespace AzToolsFramework
|
||||
|
||||
return cameraState;
|
||||
}
|
||||
|
||||
float GetScreenDisplayScaling(const int viewportId)
|
||||
{
|
||||
float scaling = 1.0f;
|
||||
ViewportInteraction::ViewportInteractionRequestBus::EventResult(
|
||||
scaling, viewportId,
|
||||
&ViewportInteraction::ViewportInteractionRequestBus::Events::DeviceScalingFactor);
|
||||
|
||||
return scaling;
|
||||
}
|
||||
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
+3
@@ -60,6 +60,9 @@ namespace AzToolsFramework
|
||||
/// Wrapper for EBus call to return the CameraState for a given viewport.
|
||||
AzFramework::CameraState GetCameraState(int viewportId);
|
||||
|
||||
/// Wrapper for EBus call to return the DPI scaling for a given viewport.
|
||||
float GetScreenDisplayScaling(const int viewportId);
|
||||
|
||||
/// A utility to return the center of several points.
|
||||
/// Take several positions and store the min and max of each in
|
||||
/// turn - when all points have been added return the center/midpoint.
|
||||
|
||||
+29
-29
@@ -423,15 +423,14 @@ namespace AzToolsFramework
|
||||
}
|
||||
}
|
||||
|
||||
static void InitializeTranslationLookup(
|
||||
EntityIdManipulators& entityIdManipulators, const AZ::Vector3& snapOffset)
|
||||
static void InitializeTranslationLookup(EntityIdManipulators& entityIdManipulators)
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework);
|
||||
|
||||
for (auto& entityIdLookup : entityIdManipulators.m_lookups)
|
||||
{
|
||||
entityIdLookup.second.m_initial =
|
||||
AZ::Transform::CreateTranslation(GetWorldTranslation(entityIdLookup.first) + snapOffset);
|
||||
AZ::Transform::CreateTranslation(GetWorldTranslation(entityIdLookup.first));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -820,7 +819,7 @@ namespace AzToolsFramework
|
||||
// moving with ctrl - setting override
|
||||
pivotOverrideFrame.m_translationOverride =
|
||||
entityIdManipulators.m_manipulators->GetLocalTransform().GetTranslation();
|
||||
InitializeTranslationLookup(entityIdManipulators, -action.LocalPositionOffset());
|
||||
InitializeTranslationLookup(entityIdManipulators);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1277,12 +1276,12 @@ namespace AzToolsFramework
|
||||
|
||||
// linear
|
||||
translationManipulators->InstallLinearManipulatorMouseDownCallback(
|
||||
[this, manipulatorEntityIds](const LinearManipulator::Action& action) mutable
|
||||
[this, manipulatorEntityIds]([[maybe_unused]] const LinearManipulator::Action& action) mutable
|
||||
{
|
||||
// important to sort entityIds based on hierarchy order when updating transforms
|
||||
BuildSortedEntityIdVectorFromEntityIdMap(m_entityIdManipulators.m_lookups, manipulatorEntityIds->m_entityIds);
|
||||
|
||||
InitializeTranslationLookup(m_entityIdManipulators, action.m_start.m_positionSnapOffset);
|
||||
InitializeTranslationLookup(m_entityIdManipulators);
|
||||
|
||||
m_axisPreview.m_translation = m_entityIdManipulators.m_manipulators->GetLocalTransform().GetTranslation();
|
||||
m_axisPreview.m_orientation = QuaternionFromTransformNoScaling(
|
||||
@@ -1302,19 +1301,19 @@ namespace AzToolsFramework
|
||||
});
|
||||
|
||||
translationManipulators->InstallLinearManipulatorMouseUpCallback(
|
||||
[this](const LinearManipulator::Action& /*action*/) mutable
|
||||
[this]([[maybe_unused]] const LinearManipulator::Action& action) mutable
|
||||
{
|
||||
EndRecordManipulatorCommand();
|
||||
});
|
||||
|
||||
// planar
|
||||
translationManipulators->InstallPlanarManipulatorMouseDownCallback(
|
||||
[this, manipulatorEntityIds](const PlanarManipulator::Action& action)
|
||||
[this, manipulatorEntityIds]([[maybe_unused]] const PlanarManipulator::Action& action)
|
||||
{
|
||||
// important to sort entityIds based on hierarchy order when updating transforms
|
||||
BuildSortedEntityIdVectorFromEntityIdMap(m_entityIdManipulators.m_lookups, manipulatorEntityIds->m_entityIds);
|
||||
|
||||
InitializeTranslationLookup(m_entityIdManipulators, action.m_start.m_snapOffset);
|
||||
InitializeTranslationLookup(m_entityIdManipulators);
|
||||
|
||||
m_axisPreview.m_translation = m_entityIdManipulators.m_manipulators->GetLocalTransform().GetTranslation();
|
||||
m_axisPreview.m_orientation = QuaternionFromTransformNoScaling(
|
||||
@@ -1340,11 +1339,11 @@ namespace AzToolsFramework
|
||||
|
||||
// surface
|
||||
translationManipulators->InstallSurfaceManipulatorMouseDownCallback(
|
||||
[this, manipulatorEntityIds](const SurfaceManipulator::Action& action)
|
||||
[this, manipulatorEntityIds]([[maybe_unused]] const SurfaceManipulator::Action& action)
|
||||
{
|
||||
BuildSortedEntityIdVectorFromEntityIdMap(m_entityIdManipulators.m_lookups, manipulatorEntityIds->m_entityIds);
|
||||
|
||||
InitializeTranslationLookup(m_entityIdManipulators, action.m_start.m_snapOffset);
|
||||
InitializeTranslationLookup(m_entityIdManipulators);
|
||||
|
||||
m_axisPreview.m_translation = m_entityIdManipulators.m_manipulators->GetLocalTransform().GetTranslation();
|
||||
m_axisPreview.m_orientation = QuaternionFromTransformNoScaling(
|
||||
@@ -2471,7 +2470,17 @@ namespace AzToolsFramework
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
AddAction(
|
||||
m_actions, { QKeySequence(Qt::Key_U) },
|
||||
/*ID_VIEWPORTUI_VISIBLE=*/50040, "Toggle ViewportUI", "Hide/Unhide Viewport UI",
|
||||
[this]()
|
||||
{
|
||||
SetViewportUiClusterVisible(m_transformModeClusterId, !m_viewportUiVisible);
|
||||
SetViewportUiClusterVisible(m_spaceCluster.m_spaceClusterId, !m_viewportUiVisible);
|
||||
m_viewportUiVisible = !m_viewportUiVisible;
|
||||
});
|
||||
|
||||
EditorMenuRequestBus::Broadcast(&EditorMenuRequests::RestoreEditMenuToDefault);
|
||||
}
|
||||
|
||||
@@ -3316,26 +3325,16 @@ namespace AzToolsFramework
|
||||
}
|
||||
|
||||
static void DrawManipulatorGrid(
|
||||
AzFramework::DebugDisplayRequests& debugDisplay, const EntityIdManipulators& entityIdManipulators,
|
||||
const float gridSize, const float localSnapping)
|
||||
AzFramework::DebugDisplayRequests& debugDisplay, const EntityIdManipulators& entityIdManipulators, const float gridSize)
|
||||
{
|
||||
const AZ::Matrix3x3 orientation =
|
||||
AZ::Matrix3x3::CreateFromTransform(entityIdManipulators.m_manipulators->GetLocalTransform());
|
||||
|
||||
const AZ::Vector3 unsnappedTranslation =
|
||||
const AZ::Vector3 translation =
|
||||
entityIdManipulators.m_manipulators->GetLocalTransform().GetTranslation();
|
||||
|
||||
// calculate the offset to snap by to align the manipulator to the grid
|
||||
// note: only perform this if we are not snapping in local space
|
||||
const AZ::Vector3 snappedOffset = !localSnapping
|
||||
? CalculateSnappedOffset(unsnappedTranslation, orientation.GetBasisX(), gridSize) +
|
||||
CalculateSnappedOffset(unsnappedTranslation, orientation.GetBasisY(), gridSize)
|
||||
: AZ::Vector3::CreateZero();
|
||||
|
||||
const AZ::Vector3 snappedTranslation = unsnappedTranslation + snappedOffset;
|
||||
|
||||
DrawSnappingGrid(
|
||||
debugDisplay, AZ::Transform::CreateFromMatrix3x3AndTranslation(orientation, snappedTranslation),
|
||||
debugDisplay, AZ::Transform::CreateFromMatrix3x3AndTranslation(orientation, translation),
|
||||
gridSize);
|
||||
}
|
||||
|
||||
@@ -3474,7 +3473,7 @@ namespace AzToolsFramework
|
||||
const GridSnapParameters gridSnapParams = GridSnapSettings(viewportInfo.m_viewportId);
|
||||
if (gridSnapParams.m_gridSnap && m_entityIdManipulators.m_manipulators)
|
||||
{
|
||||
DrawManipulatorGrid(debugDisplay, m_entityIdManipulators, gridSnapParams.m_gridSize, modifiers.Alt());
|
||||
DrawManipulatorGrid(debugDisplay, m_entityIdManipulators, gridSnapParams.m_gridSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3563,9 +3562,10 @@ namespace AzToolsFramework
|
||||
debugDisplay.SetLineWidth(1.0f);
|
||||
|
||||
const float labelOffset = cl_viewportGizmoAxisLabelOffset;
|
||||
const auto labelXScreenPosition = (gizmoStart + (gizmoAxisX * labelOffset)) * editorCameraState.m_viewportSize;
|
||||
const auto labelYScreenPosition = (gizmoStart + (gizmoAxisY * labelOffset)) * editorCameraState.m_viewportSize;
|
||||
const auto labelZScreenPosition = (gizmoStart + (gizmoAxisZ * labelOffset)) * editorCameraState.m_viewportSize;
|
||||
const float screenScale = GetScreenDisplayScaling(viewportId);
|
||||
const auto labelXScreenPosition = (gizmoStart + (gizmoAxisX * labelOffset)) * editorCameraState.m_viewportSize * screenScale;
|
||||
const auto labelYScreenPosition = (gizmoStart + (gizmoAxisY * labelOffset)) * editorCameraState.m_viewportSize * screenScale;
|
||||
const auto labelZScreenPosition = (gizmoStart + (gizmoAxisZ * labelOffset)) * editorCameraState.m_viewportSize * screenScale;
|
||||
|
||||
// draw the label of of each axis for the gizmo
|
||||
const float labelSize = cl_viewportGizmoAxisLabelSize;
|
||||
|
||||
+1
@@ -306,6 +306,7 @@ namespace AzToolsFramework
|
||||
AzFramework::ClickDetector m_clickDetector; //!< Detect different types of mouse click.
|
||||
AzFramework::CursorState m_cursorState; //!< Track the mouse position and delta movement each frame.
|
||||
SpaceCluster m_spaceCluster; //!< Related viewport ui state for controlling the current reference space.
|
||||
bool m_viewportUiVisible = true; //!< Used to hide/show the viewport ui elements.
|
||||
};
|
||||
|
||||
//! The ETCS (EntityTransformComponentSelection) namespace contains functions and data used exclusively by
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <AzCore/Math/Matrix3x3.h>
|
||||
#include <AzCore/Math/Matrix4x4.h>
|
||||
#include <AzCore/Math/Transform.h>
|
||||
#include <AzCore/Math/VectorConversions.h>
|
||||
#include <AzCore/UnitTest/TestTypes.h>
|
||||
#include <AzFramework/Viewport/CameraState.h>
|
||||
#include <AzFramework/Viewport/ViewportScreen.h>
|
||||
@@ -23,6 +24,15 @@
|
||||
|
||||
namespace UnitTest
|
||||
{
|
||||
// transform a point from normalized device coordinates to world space, and then from world space back to normalized device coordinates
|
||||
AZ::Vector2 ScreenNDCToWorldToScreenNDC(
|
||||
const AZ::Vector2& ndcPoint, const AzFramework::CameraState& cameraState)
|
||||
{
|
||||
const auto worldResult = AzFramework::ScreenNDCToWorld(ndcPoint, InverseCameraView(cameraState), InverseCameraProjection(cameraState));
|
||||
const auto ndcResult = AzFramework::WorldToScreenNDC(worldResult, CameraView(cameraState), CameraProjection(cameraState));
|
||||
return AZ::Vector3ToVector2(ndcResult);
|
||||
}
|
||||
|
||||
// transform a point from screen space to world space, and then from world space back to screen space
|
||||
AzFramework::ScreenPoint ScreenToWorldToScreen(
|
||||
const AzFramework::ScreenPoint& screenPoint, const AzFramework::CameraState& cameraState)
|
||||
@@ -30,7 +40,8 @@ namespace UnitTest
|
||||
const auto worldResult = AzFramework::ScreenToWorld(screenPoint, cameraState);
|
||||
return AzFramework::WorldToScreen(worldResult, cameraState);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// ScreenPoint tests
|
||||
TEST(ViewportScreen, WorldToScreenAndScreenToWorldReturnsTheSameValueIdentityCameraOffsetFromOrigin)
|
||||
{
|
||||
using AzFramework::ScreenPoint;
|
||||
@@ -38,8 +49,6 @@ namespace UnitTest
|
||||
const auto screenDimensions = AZ::Vector2(800.0f, 600.0f);
|
||||
const auto cameraPosition = AZ::Vector3::CreateAxisY(-10.0f);
|
||||
|
||||
// note: nearClip is 0.1 - the world space value returned will be aligned to the near clip
|
||||
// plane of the camera so use that to confirm the mapping to/from is correct
|
||||
const auto cameraState = AzFramework::CreateIdentityDefaultCamera(cameraPosition, screenDimensions);
|
||||
{
|
||||
const auto expectedScreenPoint = ScreenPoint{600, 450};
|
||||
@@ -81,6 +90,8 @@ namespace UnitTest
|
||||
EXPECT_EQ(resultScreenPoint, expectedScreenPoint);
|
||||
}
|
||||
|
||||
// note: nearClip is 0.1 - the world space value returned will be aligned to the near clip
|
||||
// plane of the camera so use that to confirm the mapping to/from is correct
|
||||
TEST(ViewportScreen, ScreenToWorldReturnsPositionOnNearClipPlaneInWorldSpace)
|
||||
{
|
||||
using AzFramework::ScreenPoint;
|
||||
@@ -94,7 +105,75 @@ namespace UnitTest
|
||||
const auto worldResult = AzFramework::ScreenToWorld(ScreenPoint{400, 300}, cameraState);
|
||||
EXPECT_THAT(worldResult, IsClose(AZ::Vector3(10.1f, 0.0f, 0.0f)));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// NDC tests
|
||||
TEST(ViewportScreen, WorldToScreenNDCAndScreenNDCToWorldReturnsTheSameValueIdentityCameraOffsetFromOrigin)
|
||||
{
|
||||
using NdcPoint = AZ::Vector2;
|
||||
|
||||
const auto screenDimensions = AZ::Vector2(800.0f, 600.0f);
|
||||
const auto cameraPosition = AZ::Vector3::CreateAxisY(-10.0f);
|
||||
|
||||
const auto cameraState = AzFramework::CreateIdentityDefaultCamera(cameraPosition, screenDimensions);
|
||||
{
|
||||
const auto expectedNdcPoint = NdcPoint{0.75f, 0.75f};
|
||||
const auto resultNdcPoint = ScreenNDCToWorldToScreenNDC(expectedNdcPoint, cameraState);
|
||||
EXPECT_THAT(resultNdcPoint, IsClose(expectedNdcPoint));
|
||||
}
|
||||
|
||||
{
|
||||
const auto expectedNdcPoint = NdcPoint{0.5f, 0.5f};
|
||||
const auto resultNdcPoint = ScreenNDCToWorldToScreenNDC(expectedNdcPoint, cameraState);
|
||||
EXPECT_THAT(resultNdcPoint, IsClose(expectedNdcPoint));
|
||||
}
|
||||
|
||||
{
|
||||
const auto expectedNdcPoint = NdcPoint{0.0f, 0.0f};
|
||||
const auto resultNdcPoint = ScreenNDCToWorldToScreenNDC(expectedNdcPoint, cameraState);
|
||||
EXPECT_THAT(resultNdcPoint, IsClose(expectedNdcPoint));
|
||||
}
|
||||
|
||||
{
|
||||
const auto expectedNdcPoint = NdcPoint{1.0f, 1.0f};
|
||||
const auto resultNdcPoint = ScreenNDCToWorldToScreenNDC(expectedNdcPoint, cameraState);
|
||||
EXPECT_THAT(resultNdcPoint, IsClose(expectedNdcPoint));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ViewportScreen, WorldToScreenNDCAndScreenNDCToWorldReturnsTheSameValueOrientatedCamera)
|
||||
{
|
||||
using NdcPoint = AZ::Vector2;
|
||||
|
||||
const auto screenDimensions = AZ::Vector2(800.0f, 600.0f);
|
||||
const auto cameraTransform =
|
||||
AZ::Transform::CreateRotationX(AZ::DegToRad(45.0f)) * AZ::Transform::CreateRotationZ(AZ::DegToRad(90.0f));
|
||||
|
||||
const auto cameraState = AzFramework::CreateDefaultCamera(cameraTransform, screenDimensions);
|
||||
|
||||
const auto expectedNdcPoint = NdcPoint{0.25f, 0.5f};
|
||||
const auto resultNdcPoint = ScreenNDCToWorldToScreenNDC(expectedNdcPoint, cameraState);
|
||||
EXPECT_THAT(resultNdcPoint, IsClose(expectedNdcPoint));
|
||||
}
|
||||
|
||||
// note: nearClip is 0.1 - the world space value returned will be aligned to the near clip
|
||||
// plane of the camera so use that to confirm the mapping to/from is correct
|
||||
TEST(ViewportScreen, ScreenNDCToWorldReturnsPositionOnNearClipPlaneInWorldSpace)
|
||||
{
|
||||
using NdcPoint = AZ::Vector2;
|
||||
|
||||
const auto screenDimensions = AZ::Vector2(800.0f, 600.0f);
|
||||
const auto cameraTransform = AZ::Transform::CreateTranslation(AZ::Vector3(10.0f, 0.0f, 0.0f)) *
|
||||
AZ::Transform::CreateRotationZ(AZ::DegToRad(-90.0f));
|
||||
|
||||
const auto cameraState = AzFramework::CreateDefaultCamera(cameraTransform, screenDimensions);
|
||||
|
||||
const auto worldResult = AzFramework::ScreenNDCToWorld(NdcPoint{0.5f, 0.5f}, InverseCameraView(cameraState), InverseCameraProjection(cameraState));
|
||||
EXPECT_THAT(worldResult, IsClose(AZ::Vector3(10.1f, 0.0f, 0.0f)));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// ScreenVector tests
|
||||
TEST(ViewportScreen, SubstractingScreenPointGivesScreenVector)
|
||||
{
|
||||
using AzFramework::ScreenPoint;
|
||||
@@ -220,6 +299,8 @@ namespace UnitTest
|
||||
EXPECT_NEAR(AzFramework::ScreenVectorLength(ScreenVector(12, 15)), 19.20937f, 0.001f);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Other tests
|
||||
TEST(ViewportScreen, CanGetCameraTransformFromCameraViewAndBack)
|
||||
{
|
||||
const auto screenDimensions = AZ::Vector2(1024.0f, 768.0f);
|
||||
|
||||
@@ -11,9 +11,11 @@
|
||||
*/
|
||||
|
||||
#include <AzCore/UnitTest/TestTypes.h>
|
||||
#include <AzCore/UserSettings/UserSettingsComponent.h>
|
||||
#include <AzFramework/Application/Application.h>
|
||||
#include <AzFramework/Spawnable/SpawnableAssetHandler.h>
|
||||
#include <AzFramework/Spawnable/SpawnableEntitiesManager.h>
|
||||
#include <AzFramework/Components/TransformComponent.h>
|
||||
#include <AzTest/AzTest.h>
|
||||
|
||||
namespace UnitTest
|
||||
@@ -40,6 +42,10 @@ namespace UnitTest
|
||||
m_application = new TestApplication();
|
||||
AZ::ComponentApplication::Descriptor descriptor;
|
||||
m_application->Start(descriptor);
|
||||
// Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is
|
||||
// shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash
|
||||
// in the unit tests.
|
||||
AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize);
|
||||
|
||||
m_spawnable = aznew AzFramework::Spawnable(
|
||||
AZ::Data::AssetId::CreateString("{EB2E8A2B-F253-4A90-BBF4-55F2EED786B8}:0"), AZ::Data::AssetData::AssetStatus::Ready);
|
||||
@@ -81,6 +87,42 @@ namespace UnitTest
|
||||
}
|
||||
}
|
||||
|
||||
void CreateRecursiveHierarchy()
|
||||
{
|
||||
AzFramework::Spawnable::EntityList& entities = m_spawnable->GetEntities();
|
||||
size_t numElements = entities.size();
|
||||
AZ::EntityId parent;
|
||||
for (size_t i=0; i<numElements; ++i)
|
||||
{
|
||||
AZStd::unique_ptr<AZ::Entity>& entity = entities[i];
|
||||
auto component = entity->CreateComponent<AzFramework::TransformComponent>();
|
||||
if (i > 0)
|
||||
{
|
||||
component->SetParent(parent);
|
||||
}
|
||||
parent = entity->GetId();
|
||||
}
|
||||
}
|
||||
|
||||
void CreateSingleParent()
|
||||
{
|
||||
AzFramework::Spawnable::EntityList& entities = m_spawnable->GetEntities();
|
||||
size_t numElements = entities.size();
|
||||
if (numElements > 0)
|
||||
{
|
||||
AZ::EntityId parent = entities[0]->GetId();
|
||||
for (size_t i = 0; i < numElements; ++i)
|
||||
{
|
||||
AZStd::unique_ptr<AZ::Entity>& entity = entities[i];
|
||||
auto component = entity->CreateComponent<AzFramework::TransformComponent>();
|
||||
if (i > 0)
|
||||
{
|
||||
component->SetParent(parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
AZ::Data::Asset<AzFramework::Spawnable>* m_spawnableAsset { nullptr };
|
||||
AzFramework::SpawnableEntitiesManager* m_manager { nullptr };
|
||||
@@ -104,17 +146,50 @@ namespace UnitTest
|
||||
{
|
||||
spawnedEntitiesCount += entities.size();
|
||||
};
|
||||
m_manager->SpawnAllEntities(*m_ticket, AzFramework::SpawnablePriority_Default, {}, AZStd::move(callback));
|
||||
AzFramework::SpawnAllEntitiesOptionalArgs optionalArgs;
|
||||
optionalArgs.m_completionCallback = AZStd::move(callback);
|
||||
m_manager->SpawnAllEntities(*m_ticket, AZStd::move(optionalArgs));
|
||||
m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular);
|
||||
|
||||
EXPECT_EQ(NumEntities, spawnedEntitiesCount);
|
||||
}
|
||||
|
||||
TEST_F(SpawnableEntitiesManagerTest, SpawnAllEntities_SetParentOnSpawnedEntities_LineageIsPreserved)
|
||||
{
|
||||
static constexpr size_t NumEntities = 4;
|
||||
FillSpawnable(NumEntities);
|
||||
CreateRecursiveHierarchy();
|
||||
|
||||
auto callback = [](AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities)
|
||||
{
|
||||
AZ::EntityId parentId;
|
||||
bool isFirst = true;
|
||||
for (const AZ::Entity* entity : entities)
|
||||
{
|
||||
if (!isFirst)
|
||||
{
|
||||
auto transform = entity->GetTransform();
|
||||
ASSERT_NE(nullptr, transform);
|
||||
EXPECT_EQ(parentId, transform->GetParentId());
|
||||
}
|
||||
else
|
||||
{
|
||||
isFirst = false;
|
||||
}
|
||||
parentId = entity->GetId();
|
||||
}
|
||||
};
|
||||
AzFramework::SpawnAllEntitiesOptionalArgs optionalArgs;
|
||||
optionalArgs.m_completionCallback = AZStd::move(callback);
|
||||
m_manager->SpawnAllEntities(*m_ticket, AZStd::move(optionalArgs));
|
||||
m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular);
|
||||
}
|
||||
|
||||
TEST_F(SpawnableEntitiesManagerTest, SpawnAllEntities_DeleteTicketBeforeCall_NoCrash)
|
||||
{
|
||||
{
|
||||
AzFramework::EntitySpawnTicket ticket(*m_spawnableAsset);
|
||||
m_manager->SpawnAllEntities(ticket, AzFramework::SpawnablePriority_Default);
|
||||
m_manager->SpawnAllEntities(ticket);
|
||||
}
|
||||
m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular);
|
||||
}
|
||||
@@ -124,11 +199,175 @@ namespace UnitTest
|
||||
// SpawnEntities
|
||||
//
|
||||
|
||||
TEST_F(SpawnableEntitiesManagerTest, SpawnEntities_Call_AllEntitiesSpawned)
|
||||
{
|
||||
static constexpr size_t NumEntities = 4;
|
||||
FillSpawnable(NumEntities);
|
||||
|
||||
AZStd::vector<size_t> indices = { 0, 2, 3, 1 };
|
||||
|
||||
size_t spawnedEntitiesCount = 0;
|
||||
auto callback = [&spawnedEntitiesCount](AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities)
|
||||
{
|
||||
spawnedEntitiesCount += entities.size();
|
||||
};
|
||||
AzFramework::SpawnEntitiesOptionalArgs optionalArgs;
|
||||
optionalArgs.m_completionCallback = AZStd::move(callback);
|
||||
m_manager->SpawnEntities(*m_ticket, AZStd::move(indices), AZStd::move(optionalArgs));
|
||||
m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular);
|
||||
|
||||
EXPECT_EQ(NumEntities, spawnedEntitiesCount);
|
||||
}
|
||||
|
||||
TEST_F(SpawnableEntitiesManagerTest, SpawnEntities_SpawnTheSameEntity_AllEntitiesSpawned)
|
||||
{
|
||||
static constexpr size_t NumEntities = 1;
|
||||
FillSpawnable(NumEntities);
|
||||
|
||||
AZStd::vector<size_t> indices = { 0, 0 };
|
||||
|
||||
size_t spawnedEntitiesCount = 0;
|
||||
auto callback =
|
||||
[&spawnedEntitiesCount](AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities)
|
||||
{
|
||||
spawnedEntitiesCount += entities.size();
|
||||
};
|
||||
AzFramework::SpawnEntitiesOptionalArgs optionalArgs;
|
||||
optionalArgs.m_completionCallback = AZStd::move(callback);
|
||||
m_manager->SpawnEntities(*m_ticket, AZStd::move(indices), AZStd::move(optionalArgs));
|
||||
m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular);
|
||||
|
||||
EXPECT_EQ(NumEntities * 2, spawnedEntitiesCount);
|
||||
}
|
||||
|
||||
TEST_F(SpawnableEntitiesManagerTest, SpawnEntities_MultipleSpawns_AllEntitiesSpawned)
|
||||
{
|
||||
static constexpr size_t NumEntities = 4;
|
||||
FillSpawnable(NumEntities);
|
||||
|
||||
AZStd::vector<size_t> indices = { 0, 2, 3, 1 };
|
||||
|
||||
size_t spawnedEntitiesCount = 0;
|
||||
auto callback =
|
||||
[&spawnedEntitiesCount](AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities)
|
||||
{
|
||||
spawnedEntitiesCount += entities.size();
|
||||
};
|
||||
AzFramework::SpawnEntitiesOptionalArgs optionalArgs;
|
||||
optionalArgs.m_completionCallback = AZStd::move(callback);
|
||||
m_manager->SpawnEntities(*m_ticket, indices, optionalArgs);
|
||||
m_manager->SpawnEntities(*m_ticket, AZStd::move(indices), AZStd::move(optionalArgs));
|
||||
m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular);
|
||||
|
||||
EXPECT_EQ(NumEntities * 2, spawnedEntitiesCount);
|
||||
}
|
||||
|
||||
TEST_F(SpawnableEntitiesManagerTest, SpawnEntities_ReferencesAreRemappedForNewBatch_AllPointToLatestParent)
|
||||
{
|
||||
static constexpr size_t NumEntities = 4;
|
||||
FillSpawnable(NumEntities);
|
||||
CreateSingleParent();
|
||||
|
||||
AZStd::vector<size_t> indices = { 0, 1, 2, 3 };
|
||||
AZStd::vector<AZ::EntityId> parents;
|
||||
|
||||
auto callback = [&parents](AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities)
|
||||
{
|
||||
AZ::EntityId parent = (*entities.begin())->GetId();
|
||||
parents.push_back(parent);
|
||||
auto it = entities.begin();
|
||||
++it; // Skip the first as that is the parent.
|
||||
for (; it != entities.end(); ++it)
|
||||
{
|
||||
AZ::TransformInterface* transform = (*it)->GetTransform();
|
||||
ASSERT_NE(nullptr, transform);
|
||||
ASSERT_EQ(parent, transform->GetParentId());
|
||||
}
|
||||
};
|
||||
AzFramework::SpawnEntitiesOptionalArgs optionalArgs;
|
||||
optionalArgs.m_completionCallback = AZStd::move(callback);
|
||||
optionalArgs.m_referencePreviouslySpawnedEntities = false;
|
||||
m_manager->SpawnEntities(*m_ticket, indices, optionalArgs);
|
||||
m_manager->SpawnEntities(*m_ticket, AZStd::move(indices), AZStd::move(optionalArgs));
|
||||
m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular);
|
||||
|
||||
EXPECT_NE(parents[0], parents[1]);
|
||||
}
|
||||
|
||||
TEST_F(SpawnableEntitiesManagerTest, SpawnEntities_ReferencesAreRemappedForContinuedBatch_AllPointToLatestParent)
|
||||
{
|
||||
static constexpr size_t NumEntities = 4;
|
||||
FillSpawnable(NumEntities);
|
||||
CreateSingleParent();
|
||||
|
||||
AZStd::vector<size_t> indices = { 0, 1, 2, 3 };
|
||||
AZStd::vector<AZ::EntityId> parents;
|
||||
|
||||
auto callback =
|
||||
[&parents](AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities)
|
||||
{
|
||||
AZ::EntityId parent = (*entities.begin())->GetId();
|
||||
parents.push_back(parent);
|
||||
auto it = entities.begin();
|
||||
++it; // Skip the first as that is the parent.
|
||||
for (; it!=entities.end(); ++it)
|
||||
{
|
||||
AZ::TransformInterface* transform = (*it)->GetTransform();
|
||||
ASSERT_NE(nullptr, transform);
|
||||
ASSERT_EQ(parent, transform->GetParentId());
|
||||
}
|
||||
};
|
||||
AzFramework::SpawnEntitiesOptionalArgs optionalArgs;
|
||||
optionalArgs.m_completionCallback = AZStd::move(callback);
|
||||
optionalArgs.m_referencePreviouslySpawnedEntities = true;
|
||||
m_manager->SpawnEntities(*m_ticket, indices, optionalArgs);
|
||||
m_manager->SpawnEntities(*m_ticket, AZStd::move(indices), AZStd::move(optionalArgs));
|
||||
m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular);
|
||||
|
||||
EXPECT_NE(parents[0], parents[1]);
|
||||
}
|
||||
|
||||
TEST_F(SpawnableEntitiesManagerTest, SpawnEntities_ReferencesAreRemappedAcrossBatches_AllPointToLatestParent)
|
||||
{
|
||||
FillSpawnable(4);
|
||||
CreateSingleParent();
|
||||
|
||||
// Spawn a regular batch but with two parents and store the id of the last entity. This will the parent for the next batch.
|
||||
AZ::EntityId parent;
|
||||
auto getParent = [&parent](AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities)
|
||||
{
|
||||
ASSERT_NE(entities.begin(), entities.end());
|
||||
parent = (*AZStd::prev(entities.end()))->GetId();
|
||||
};
|
||||
|
||||
AzFramework::SpawnEntitiesOptionalArgs optionalArgsFirstBatch;
|
||||
optionalArgsFirstBatch.m_completionCallback = AZStd::move(getParent);
|
||||
optionalArgsFirstBatch.m_referencePreviouslySpawnedEntities = true;
|
||||
m_manager->SpawnEntities(*m_ticket, {0, 1, 2, 3, 0}, AZStd::move(optionalArgsFirstBatch));
|
||||
|
||||
// Next, spawn all the entities that have a reference to the parent that was just stored.
|
||||
auto parentCheck = [&parent](AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities)
|
||||
{
|
||||
for (auto& it : entities)
|
||||
{
|
||||
AZ::TransformInterface* transform = it->GetTransform();
|
||||
ASSERT_NE(nullptr, transform);
|
||||
ASSERT_EQ(parent, transform->GetParentId());
|
||||
}
|
||||
};
|
||||
AzFramework::SpawnEntitiesOptionalArgs optionalArgsSecondBatch;
|
||||
optionalArgsSecondBatch.m_completionCallback = AZStd::move(parentCheck);
|
||||
optionalArgsSecondBatch.m_referencePreviouslySpawnedEntities = true;
|
||||
m_manager->SpawnEntities(*m_ticket, {1, 2, 3}, AZStd::move(optionalArgsSecondBatch));
|
||||
|
||||
m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular);
|
||||
}
|
||||
|
||||
TEST_F(SpawnableEntitiesManagerTest, SpawnEntities_DeleteTicketBeforeCall_NoCrash)
|
||||
{
|
||||
{
|
||||
AzFramework::EntitySpawnTicket ticket(*m_spawnableAsset);
|
||||
m_manager->SpawnEntities(ticket, AzFramework::SpawnablePriority_Default, {});
|
||||
m_manager->SpawnEntities(ticket, {/* Deliberate empty list of indices. */});
|
||||
}
|
||||
m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular);
|
||||
}
|
||||
@@ -142,7 +381,7 @@ namespace UnitTest
|
||||
{
|
||||
{
|
||||
AzFramework::EntitySpawnTicket ticket(*m_spawnableAsset);
|
||||
m_manager->DespawnAllEntities(ticket, AzFramework::SpawnablePriority_Default);
|
||||
m_manager->DespawnAllEntities(ticket);
|
||||
}
|
||||
m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular);
|
||||
}
|
||||
@@ -156,7 +395,7 @@ namespace UnitTest
|
||||
{
|
||||
{
|
||||
AzFramework::EntitySpawnTicket ticket(*m_spawnableAsset);
|
||||
m_manager->ReloadSpawnable(ticket, AzFramework::SpawnablePriority_Default, *m_spawnableAsset);
|
||||
m_manager->ReloadSpawnable(ticket, *m_spawnableAsset);
|
||||
}
|
||||
m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular);
|
||||
}
|
||||
@@ -183,8 +422,8 @@ namespace UnitTest
|
||||
spawnedEntitiesCount += entities.size();
|
||||
};
|
||||
|
||||
m_manager->SpawnAllEntities(*m_ticket, AzFramework::SpawnablePriority_Default);
|
||||
m_manager->ListEntities(*m_ticket, AzFramework::SpawnablePriority_Default, AZStd::move(callback));
|
||||
m_manager->SpawnAllEntities(*m_ticket);
|
||||
m_manager->ListEntities(*m_ticket, AZStd::move(callback));
|
||||
m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular);
|
||||
|
||||
EXPECT_TRUE(allValidEntityIds);
|
||||
@@ -197,7 +436,7 @@ namespace UnitTest
|
||||
|
||||
{
|
||||
AzFramework::EntitySpawnTicket ticket(*m_spawnableAsset);
|
||||
m_manager->ListEntities(ticket, AzFramework::SpawnablePriority_Default, AZStd::move(callback));
|
||||
m_manager->ListEntities(ticket, AZStd::move(callback));
|
||||
}
|
||||
m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular);
|
||||
}
|
||||
@@ -228,8 +467,8 @@ namespace UnitTest
|
||||
}
|
||||
};
|
||||
|
||||
m_manager->SpawnAllEntities(*m_ticket, AzFramework::SpawnablePriority_Default);
|
||||
m_manager->ListIndicesAndEntities(*m_ticket, AzFramework::SpawnablePriority_Default, AZStd::move(callback));
|
||||
m_manager->SpawnAllEntities(*m_ticket);
|
||||
m_manager->ListIndicesAndEntities(*m_ticket, AZStd::move(callback));
|
||||
m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular);
|
||||
|
||||
EXPECT_TRUE(allValidEntityIds);
|
||||
@@ -242,7 +481,7 @@ namespace UnitTest
|
||||
|
||||
{
|
||||
AzFramework::EntitySpawnTicket ticket(*m_spawnableAsset);
|
||||
m_manager->ListIndicesAndEntities(ticket, AzFramework::SpawnablePriority_Default, AZStd::move(callback));
|
||||
m_manager->ListIndicesAndEntities(ticket, AZStd::move(callback));
|
||||
}
|
||||
m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular);
|
||||
}
|
||||
@@ -258,7 +497,7 @@ namespace UnitTest
|
||||
|
||||
{
|
||||
AzFramework::EntitySpawnTicket ticket(*m_spawnableAsset);
|
||||
m_manager->ClaimEntities(ticket, AzFramework::SpawnablePriority_Default, AZStd::move(callback));
|
||||
m_manager->ClaimEntities(ticket, AZStd::move(callback));
|
||||
}
|
||||
m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular);
|
||||
}
|
||||
@@ -274,7 +513,7 @@ namespace UnitTest
|
||||
|
||||
{
|
||||
AzFramework::EntitySpawnTicket ticket(*m_spawnableAsset);
|
||||
m_manager->Barrier(ticket, AzFramework::SpawnablePriority_Default, AZStd::move(callback));
|
||||
m_manager->Barrier(ticket, AZStd::move(callback));
|
||||
}
|
||||
m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular);
|
||||
}
|
||||
@@ -305,8 +544,16 @@ namespace UnitTest
|
||||
defaultPriorityCallId = callCounter++;
|
||||
};
|
||||
|
||||
m_manager->SpawnAllEntities(*m_ticket, AzFramework::SpawnablePriority_Default, {}, AZStd::move(defaultCallback));
|
||||
m_manager->SpawnAllEntities(highPriorityTicket, AzFramework::SpawnablePriority_High, {}, AZStd::move(highCallback));
|
||||
AzFramework::SpawnAllEntitiesOptionalArgs optionalArgs;
|
||||
optionalArgs.m_completionCallback = AZStd::move(defaultCallback);
|
||||
optionalArgs.m_priority = AzFramework::SpawnablePriority_Default;
|
||||
m_manager->SpawnAllEntities(*m_ticket, AZStd::move(optionalArgs));
|
||||
|
||||
AzFramework::SpawnAllEntitiesOptionalArgs highPriortyOptionalArgs;
|
||||
highPriortyOptionalArgs.m_completionCallback = AZStd::move(highCallback);
|
||||
highPriortyOptionalArgs.m_priority = AzFramework::SpawnablePriority_High;
|
||||
m_manager->SpawnAllEntities(highPriorityTicket, AZStd::move(highPriortyOptionalArgs));
|
||||
|
||||
m_manager->ProcessQueue(
|
||||
AzFramework::SpawnableEntitiesManager::CommandQueuePriority::High |
|
||||
AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular);
|
||||
@@ -333,8 +580,16 @@ namespace UnitTest
|
||||
defaultPriorityCallId = callCounter++;
|
||||
};
|
||||
|
||||
m_manager->SpawnAllEntities(*m_ticket, AzFramework::SpawnablePriority_Default, {}, AZStd::move(defaultCallback));
|
||||
m_manager->SpawnAllEntities(*m_ticket, AzFramework::SpawnablePriority_High, {}, AZStd::move(highCallback));
|
||||
AzFramework::SpawnAllEntitiesOptionalArgs optionalArgs;
|
||||
optionalArgs.m_completionCallback = AZStd::move(defaultCallback);
|
||||
optionalArgs.m_priority = AzFramework::SpawnablePriority_Default;
|
||||
m_manager->SpawnAllEntities(*m_ticket, AZStd::move(optionalArgs));
|
||||
|
||||
AzFramework::SpawnAllEntitiesOptionalArgs highPriortyOptionalArgs;
|
||||
highPriortyOptionalArgs.m_completionCallback = AZStd::move(highCallback);
|
||||
highPriortyOptionalArgs.m_priority = AzFramework::SpawnablePriority_High;
|
||||
m_manager->SpawnAllEntities(*m_ticket, AZStd::move(highPriortyOptionalArgs));
|
||||
|
||||
m_manager->ProcessQueue(
|
||||
AzFramework::SpawnableEntitiesManager::CommandQueuePriority::High |
|
||||
AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular);
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <Launcher.h>
|
||||
|
||||
#include <AzCore/Casting/numeric_cast.h>
|
||||
@@ -22,6 +23,8 @@
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
#include <AzFramework/Asset/AssetSystemBus.h>
|
||||
#include <AzFramework/IO/RemoteStorageDrive.h>
|
||||
#include <AzFramework/Windowing/NativeWindow.h>
|
||||
#include <AzFramework/Windowing/WindowBus.h>
|
||||
|
||||
#include <AzGameFramework/Application/GameApplication.h>
|
||||
|
||||
@@ -45,6 +48,19 @@ extern "C" void CreateStaticModules(AZStd::vector<AZ::Module*>& modulesOut);
|
||||
|
||||
namespace
|
||||
{
|
||||
void OnViewportResize(const AZ::Vector2& value);
|
||||
|
||||
AZ_CVAR(AZ::Vector2, r_viewportSize, AZ::Vector2::CreateZero(), OnViewportResize, AZ::ConsoleFunctorFlags::DontReplicate,
|
||||
"The default size for the launcher viewport, 0 0 means full screen");
|
||||
|
||||
void OnViewportResize(const AZ::Vector2& value)
|
||||
{
|
||||
AzFramework::NativeWindowHandle windowHandle = nullptr;
|
||||
AzFramework::WindowSystemRequestBus::BroadcastResult(windowHandle, &AzFramework::WindowSystemRequestBus::Events::GetDefaultWindowHandle);
|
||||
AzFramework::WindowSize newSize = AzFramework::WindowSize(aznumeric_cast<int32_t>(value.GetX()), aznumeric_cast<int32_t>(value.GetY()));
|
||||
AzFramework::WindowRequestBus::Broadcast(&AzFramework::WindowRequestBus::Events::ResizeClientArea, newSize);
|
||||
}
|
||||
|
||||
void ExecuteConsoleCommandFile(AzFramework::Application& application)
|
||||
{
|
||||
const AZStd::string_view customConCmdKey = "console-command-file";
|
||||
|
||||
@@ -75,14 +75,14 @@
|
||||
<widget class="QSvgWidget" name="m_logo" native="true">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>161</width>
|
||||
<height>49</height>
|
||||
<width>175</width>
|
||||
<height>66</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>161</width>
|
||||
<height>49</height>
|
||||
<width>175</width>
|
||||
<height>66</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
|
||||
@@ -177,6 +177,11 @@ ly_add_target(
|
||||
Legacy::EditorLib
|
||||
ProjectManager
|
||||
)
|
||||
set_property(SOURCE
|
||||
CryEdit.cpp
|
||||
APPEND PROPERTY
|
||||
COMPILE_DEFINITIONS LY_CMAKE_TARGET="Editor"
|
||||
)
|
||||
ly_add_translations(
|
||||
TARGETS Editor
|
||||
PREFIX Translations
|
||||
@@ -186,15 +191,8 @@ ly_add_translations(
|
||||
)
|
||||
ly_add_dependencies(Editor AssetProcessor)
|
||||
|
||||
if(TARGET Editor)
|
||||
set_property(SOURCE
|
||||
CryEdit.cpp
|
||||
APPEND PROPERTY
|
||||
COMPILE_DEFINITIONS LY_CMAKE_TARGET="Editor"
|
||||
)
|
||||
else()
|
||||
message(FATAL_ERROR "Cannot set LY_CMAKE_TARGET define to Editor as the target doesn't exist anymore."
|
||||
" Perhaps it has been renamed")
|
||||
if(LY_DEFAULT_PROJECT_PATH)
|
||||
set_property(TARGET Editor APPEND PROPERTY VS_DEBUGGER_COMMAND_ARGUMENTS "--project-path=\"${LY_DEFAULT_PROJECT_PATH}\"")
|
||||
endif()
|
||||
|
||||
################################################################################
|
||||
|
||||
@@ -118,10 +118,10 @@ void CVarMenu::AddUniqueCVarsItem(QString displayName,
|
||||
// Otherwise we could have just used the action's currently checked
|
||||
// state and updated the CVar's value only
|
||||
bool cVarOn = (cVar->GetFVal() == availableCVar.m_onValue);
|
||||
bool checked = !cVarOn;
|
||||
SetCVar(cVar, checked ? availableCVar.m_onValue : availableCVar.m_offValue);
|
||||
action->setChecked(checked);
|
||||
if (checked)
|
||||
bool cVarChecked = !cVarOn;
|
||||
SetCVar(cVar, cVarChecked ? availableCVar.m_onValue : availableCVar.m_offValue);
|
||||
action->setChecked(cVarChecked);
|
||||
if (cVarChecked)
|
||||
{
|
||||
// Set the rest of the CVars in the group to their off values
|
||||
SetCVarsToOffValue(availableCVars, availableCVar);
|
||||
@@ -132,9 +132,9 @@ void CVarMenu::AddUniqueCVarsItem(QString displayName,
|
||||
|
||||
// Initialize the action's checked state based on its associated CVar's current value
|
||||
ICVar* cVar = gEnv->pConsole->GetCVar(availableCVar.m_cVarName.toUtf8().data());
|
||||
bool checked = (cVar && cVar->GetFVal() == availableCVar.m_onValue);
|
||||
action->setChecked(checked);
|
||||
if (checked)
|
||||
bool cVarChecked = (cVar && cVar->GetFVal() == availableCVar.m_onValue);
|
||||
action->setChecked(cVarChecked);
|
||||
if (cVarChecked)
|
||||
{
|
||||
// Set the rest of the CVars in the group to their off values
|
||||
SetCVarsToOffValue(availableCVars, availableCVar);
|
||||
|
||||
@@ -2281,6 +2281,14 @@ int CCryEditApp::IdleProcessing(bool bBackgroundUpdate)
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Ensure we don't get called re-entrantly
|
||||
// This can occur when a nested Qt event loop fires (e.g. by way of a modal dialog calling exec)
|
||||
if (m_idleProcessingRunning)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
QScopedValueRollback<bool> guard(m_idleProcessingRunning, true);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Call the update function of the engine
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -335,6 +335,8 @@ private:
|
||||
// If this flag is set, the next OnIdle() will update, even if the app is in the background, and then
|
||||
// this flag will be reset.
|
||||
bool m_bForceProcessIdle = false;
|
||||
// This is set while IdleProcessing is running to prevent re-entrancy
|
||||
bool m_idleProcessingRunning = false;
|
||||
// Keep the editor alive, even if no focus is set
|
||||
bool m_bKeepEditorActive = false;
|
||||
// Currently creating a new level
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
#include "EditorPreferencesPageViewportMovement.h"
|
||||
#include "EditorPreferencesPageViewportDebug.h"
|
||||
#include "EditorPreferencesPageExperimentalLighting.h"
|
||||
#include "EditorPreferencesPageAWS.h"
|
||||
#include "LyViewPaneNames.h"
|
||||
|
||||
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
|
||||
@@ -72,6 +73,7 @@ EditorPreferencesDialog::EditorPreferencesDialog(QWidget* pParent)
|
||||
CEditorPreferencesPage_ViewportMovement::Reflect(*serializeContext);
|
||||
CEditorPreferencesPage_ViewportDebug::Reflect(*serializeContext);
|
||||
CEditorPreferencesPage_ExperimentalLighting::Reflect(*serializeContext);
|
||||
CEditorPreferencesPage_AWS::Reflect(*serializeContext);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* 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 "EditorDefs.h"
|
||||
|
||||
#include "EditorPreferencesPageAWS.h"
|
||||
|
||||
// AzCore
|
||||
#include <AzCore/Serialization/EditContext.h>
|
||||
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
|
||||
#include <AzCore/Jobs/JobFunction.h>
|
||||
|
||||
void CEditorPreferencesPage_AWS::Reflect(AZ::SerializeContext& serialize)
|
||||
{
|
||||
serialize.Class<UsageOptions>()
|
||||
->Version(1)
|
||||
->Field("AWSAttributionEnabled", &UsageOptions::m_awsAttributionEnabled);
|
||||
|
||||
serialize.Class<CEditorPreferencesPage_AWS>()
|
||||
->Version(1)
|
||||
->Field("UsageOptions", &CEditorPreferencesPage_AWS::m_usageOptions);
|
||||
|
||||
AZ::EditContext* editContext = serialize.GetEditContext();
|
||||
if (editContext)
|
||||
{
|
||||
editContext->Class<UsageOptions>("Options", "")
|
||||
->DataElement(AZ::Edit::UIHandlers::CheckBox, &UsageOptions::m_awsAttributionEnabled, "Send Metrics usage to AWS",
|
||||
"Reports Gem usage to AWS on Editor launch");
|
||||
|
||||
editContext->Class<CEditorPreferencesPage_AWS>("AWS Preferences", "AWS Preferences")
|
||||
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
|
||||
->Attribute(AZ::Edit::Attributes::Visibility, AZ_CRC("PropertyVisibility_ShowChildrenOnly", 0xef428f20))
|
||||
->DataElement(AZ::Edit::UIHandlers::Default, &CEditorPreferencesPage_AWS::m_usageOptions, "AWS Usage Data", "AWS Usage Options");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
CEditorPreferencesPage_AWS::CEditorPreferencesPage_AWS()
|
||||
{
|
||||
m_settingsRegistry = AZStd::make_unique<AZ::SettingsRegistryImpl>();
|
||||
InitializeSettings();
|
||||
|
||||
// TODO Update with AWS svg.
|
||||
m_icon = QIcon(":/res/AWS_preferences_icon.svg");
|
||||
}
|
||||
|
||||
CEditorPreferencesPage_AWS::~CEditorPreferencesPage_AWS()
|
||||
{
|
||||
m_settingsRegistry.reset();
|
||||
}
|
||||
|
||||
const char* CEditorPreferencesPage_AWS::GetTitle()
|
||||
{
|
||||
return "AWS";
|
||||
}
|
||||
|
||||
QIcon& CEditorPreferencesPage_AWS::GetIcon()
|
||||
{
|
||||
return m_icon;
|
||||
}
|
||||
|
||||
void CEditorPreferencesPage_AWS::OnApply()
|
||||
{
|
||||
m_settingsRegistry->Set(AWSAttributionEnabledKey, m_usageOptions.m_awsAttributionEnabled);
|
||||
SaveSettingsRegistryFile();
|
||||
}
|
||||
|
||||
const CEditorPreferencesPage_AWS::UsageOptions& CEditorPreferencesPage_AWS::GetUsageOptions()
|
||||
{
|
||||
return m_usageOptions;
|
||||
}
|
||||
|
||||
void CEditorPreferencesPage_AWS::SaveSettingsRegistryFile()
|
||||
{
|
||||
AZ::Job* job = AZ::CreateJobFunction(
|
||||
[this]()
|
||||
{
|
||||
AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance();
|
||||
AZ_Assert(fileIO, "File IO is not initialized.");
|
||||
|
||||
// Resolve path to editor_aws_preferences.setreg
|
||||
AZStd::string editorPreferencesFilePath =
|
||||
AZStd::string::format("@user@/%s/%s", AZ::SettingsRegistryInterface::RegistryFolder, EditorAWSPreferencesFileName);
|
||||
AZStd::array<char, AZ::IO::MaxPathLength> resolvedPath{};
|
||||
fileIO->ResolvePath(editorPreferencesFilePath.c_str(), resolvedPath.data(), resolvedPath.size());
|
||||
|
||||
AZ::SettingsRegistryMergeUtils::DumperSettings dumperSettings;
|
||||
dumperSettings.m_prettifyOutput = true;
|
||||
dumperSettings.m_jsonPointerPrefix = AWSAttributionSettingsPrefixKey;
|
||||
|
||||
AZStd::string stringBuffer;
|
||||
AZ::IO::ByteContainerStream stringStream(&stringBuffer);
|
||||
if (!AZ::SettingsRegistryMergeUtils::DumpSettingsRegistryToStream(
|
||||
*m_settingsRegistry, AWSAttributionSettingsPrefixKey, stringStream, dumperSettings))
|
||||
{
|
||||
AZ_Warning(
|
||||
"AWSAttributionManager", false, R"(Unable to save changes to the Editor AWS Preferences registry file at "%s"\n)",
|
||||
resolvedPath.data());
|
||||
return;
|
||||
}
|
||||
|
||||
bool saved{};
|
||||
constexpr auto configurationMode =
|
||||
AZ::IO::SystemFile::SF_OPEN_CREATE | AZ::IO::SystemFile::SF_OPEN_CREATE_PATH | AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY;
|
||||
if (AZ::IO::SystemFile outputFile; outputFile.Open(resolvedPath.data(), configurationMode))
|
||||
{
|
||||
saved = outputFile.Write(stringBuffer.data(), stringBuffer.size()) == stringBuffer.size();
|
||||
}
|
||||
|
||||
AZ_Warning(
|
||||
"AWSAttributionManager", saved, R"(Unable to save Editor AWS Preferences registry file to path "%s"\n)",
|
||||
editorPreferencesFilePath.c_str());
|
||||
},
|
||||
true);
|
||||
job->Start();
|
||||
}
|
||||
|
||||
void CEditorPreferencesPage_AWS::InitializeSettings()
|
||||
{
|
||||
AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance();
|
||||
AZ_Assert(fileIO, "File IO is not initialized.");
|
||||
|
||||
// Resolve path to editor_aws_preferences.setreg
|
||||
AZStd::string editorAWSPreferencesFilePath =
|
||||
AZStd::string::format("@user@/%s/%s", AZ::SettingsRegistryInterface::RegistryFolder, EditorAWSPreferencesFileName);
|
||||
AZStd::array<char, AZ::IO::MaxPathLength> resolvedPathAWSPreference{};
|
||||
if (!fileIO->ResolvePath(editorAWSPreferencesFilePath.c_str(), resolvedPathAWSPreference.data(), resolvedPathAWSPreference.size()))
|
||||
{
|
||||
AZ_Warning("AWSAttributionManager", false, "Error resolving path %s", resolvedPathAWSPreference.data());
|
||||
return;
|
||||
}
|
||||
|
||||
if (fileIO->Exists(resolvedPathAWSPreference.data()))
|
||||
{
|
||||
m_settingsRegistry->MergeSettingsFile(resolvedPathAWSPreference.data(), AZ::SettingsRegistryInterface::Format::JsonMergePatch, "");
|
||||
}
|
||||
|
||||
if (!m_settingsRegistry->Get(m_usageOptions.m_awsAttributionEnabled, AWSAttributionEnabledKey))
|
||||
{
|
||||
// If key is missing default to on.
|
||||
m_usageOptions.m_awsAttributionEnabled = true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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 "Include/IPreferencesPage.h"
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/Settings/SettingsRegistryImpl.h>
|
||||
#include <AzCore/RTTI/RTTI.h>
|
||||
#include <QIcon>
|
||||
|
||||
class CEditorPreferencesPage_AWS
|
||||
: public IPreferencesPage
|
||||
{
|
||||
public:
|
||||
AZ_RTTI(CEditorPreferencesPage_AWS, "{51FB9557-ABA3-4FD7-803A-1784F5B06F5F}", IPreferencesPage)
|
||||
|
||||
static void Reflect(AZ::SerializeContext& serialize);
|
||||
|
||||
CEditorPreferencesPage_AWS();
|
||||
virtual ~CEditorPreferencesPage_AWS();
|
||||
|
||||
// IPreferencesPage interface methods.
|
||||
virtual const char* GetCategory() override { return "AWS"; }
|
||||
virtual const char* GetTitle() override;
|
||||
virtual QIcon& GetIcon() override;
|
||||
virtual void OnApply() override;
|
||||
virtual void OnCancel() override {}
|
||||
virtual bool OnQueryCancel() override { return true; }
|
||||
|
||||
protected:
|
||||
struct UsageOptions
|
||||
{
|
||||
AZ_TYPE_INFO(UsageOptions, "{2B7D9B19-D13B-4E54-B724-B2FD8D0828B3}")
|
||||
|
||||
bool m_awsAttributionEnabled;
|
||||
};
|
||||
|
||||
const UsageOptions& GetUsageOptions();
|
||||
|
||||
private:
|
||||
void InitializeSettings();
|
||||
void SaveSettingsRegistryFile();
|
||||
UsageOptions m_usageOptions;
|
||||
QIcon m_icon;
|
||||
AZStd::unique_ptr<AZ::SettingsRegistryImpl> m_settingsRegistry;
|
||||
|
||||
static constexpr char AWSAttributionEnabledKey[] = "/Amazon/AWS/Preferences/AWSAttributionEnabled";
|
||||
static constexpr char EditorPreferencesFileName[] = "editorpreferences.setreg";
|
||||
static constexpr char EditorAWSPreferencesFileName[] = "editor_aws_preferences.setreg";
|
||||
static constexpr char AWSAttributionSettingsPrefixKey[] = "/Amazon/AWS/Preferences";
|
||||
};
|
||||
@@ -1233,7 +1233,7 @@ void EditorViewportWidget::SetViewportId(int id)
|
||||
|
||||
auto controller = AZStd::make_shared<AtomToolsFramework::ModularViewportCameraController>();
|
||||
controller->SetCameraListBuilderCallback(
|
||||
[](AzFramework::Cameras& cameras)
|
||||
[id](AzFramework::Cameras& cameras)
|
||||
{
|
||||
auto firstPersonRotateCamera = AZStd::make_shared<AzFramework::RotateCameraInput>(AzFramework::CameraFreeLookButton);
|
||||
auto firstPersonPanCamera =
|
||||
@@ -1243,17 +1243,17 @@ void EditorViewportWidget::SetViewportId(int id)
|
||||
|
||||
auto orbitCamera = AZStd::make_shared<AzFramework::OrbitCameraInput>();
|
||||
orbitCamera->SetLookAtFn(
|
||||
[](const AZ::Vector3& position, const AZ::Vector3& direction) -> AZStd::optional<AZ::Vector3>
|
||||
[id](const AZ::Vector3& position, const AZ::Vector3& direction) -> AZStd::optional<AZ::Vector3>
|
||||
{
|
||||
AZStd::optional<AZ::Transform> manipulatorTransform;
|
||||
AzToolsFramework::EditorTransformComponentSelectionRequestBus::EventResult(
|
||||
manipulatorTransform, AzToolsFramework::GetEntityContextId(),
|
||||
&AzToolsFramework::EditorTransformComponentSelectionRequestBus::Events::GetManipulatorTransform);
|
||||
AZStd::optional<AZ::Vector3> lookAtAfterInterpolation;
|
||||
AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult(
|
||||
lookAtAfterInterpolation, id,
|
||||
&AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::LookAtAfterInterpolation);
|
||||
|
||||
// initially attempt to use manipulator transform if one exists (there is a selection)
|
||||
if (manipulatorTransform)
|
||||
// initially attempt to use the last set look at point after an interpolation has finished
|
||||
if (lookAtAfterInterpolation.has_value())
|
||||
{
|
||||
return manipulatorTransform->GetTranslation();
|
||||
return *lookAtAfterInterpolation;
|
||||
}
|
||||
|
||||
const float RayDistance = 1000.0f;
|
||||
@@ -2887,9 +2887,12 @@ void EditorViewportWidget::UpdateCameraFromViewportContext()
|
||||
AZ::Matrix3x4 matrix;
|
||||
matrix.SetBasisAndTranslation(cameraState.m_side, cameraState.m_forward, cameraState.m_up, cameraState.m_position);
|
||||
auto m = AZMatrix3x4ToLYMatrix3x4(matrix);
|
||||
|
||||
m_updatingCameraPosition = true;
|
||||
SetViewTM(m);
|
||||
SetFOV(cameraState.m_fovOrZoom);
|
||||
m_Camera.SetZRange(cameraState.m_nearClip, cameraState.m_farClip);
|
||||
m_updatingCameraPosition = false;
|
||||
}
|
||||
|
||||
void EditorViewportWidget::SetAsActiveViewport()
|
||||
|
||||
@@ -553,7 +553,7 @@ void CLogFile::OnWriteToConsole(const char* sText, bool bNewLine)
|
||||
|
||||
// remember selection and the top row
|
||||
int len = m_hWndEditBox->document()->toPlainText().length();
|
||||
int top;
|
||||
int top = 0;
|
||||
int from = m_hWndEditBox->textCursor().selectionStart();
|
||||
int to = from + m_hWndEditBox->textCursor().selectionEnd();
|
||||
bool keepPos = false;
|
||||
|
||||
@@ -143,6 +143,7 @@
|
||||
<file>res/Camera.svg</file>
|
||||
<file>res/Debug.svg</file>
|
||||
<file>res/Experimental.svg</file>
|
||||
<file>res/AWS_preferences_icon.svg</file>
|
||||
<file>res/Files.svg</file>
|
||||
<file>res/Gizmos.svg</file>
|
||||
<file>res/Global.svg</file>
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
|
||||
#include "PreferencesStdPages.h"
|
||||
|
||||
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
|
||||
|
||||
// Editor
|
||||
#include "EditorPreferencesPageGeneral.h"
|
||||
#include "EditorPreferencesPageFiles.h"
|
||||
@@ -23,6 +25,7 @@
|
||||
#include "EditorPreferencesPageViewportMovement.h"
|
||||
#include "EditorPreferencesPageViewportDebug.h"
|
||||
#include "EditorPreferencesPageExperimentalLighting.h"
|
||||
#include "EditorPreferencesPageAWS.h"
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@@ -42,6 +45,11 @@ CStdPreferencesClassDesc::CStdPreferencesClassDesc()
|
||||
};
|
||||
|
||||
m_pageCreators.push_back([]() { return new CEditorPreferencesPage_ExperimentalLighting(); });
|
||||
|
||||
if (AzToolsFramework::IsComponentWithServiceRegistered(AZ_CRC_CE("AWSCoreEditorService")))
|
||||
{
|
||||
m_pageCreators.push_back([]() { return new CEditorPreferencesPage_AWS(); });
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT CStdPreferencesClassDesc::QueryInterface(const IID& riid, void** ppvObj)
|
||||
|
||||
@@ -121,7 +121,7 @@ protected:
|
||||
};
|
||||
#endif
|
||||
|
||||
Q_GLOBAL_STATIC(QtViewPaneManager, s_instance)
|
||||
Q_GLOBAL_STATIC(QtViewPaneManager, s_viewPaneManagerInstance)
|
||||
|
||||
|
||||
QWidget* QtViewPane::CreateWidget()
|
||||
@@ -611,12 +611,12 @@ void QtViewPaneManager::UnregisterPane(const QString& name)
|
||||
|
||||
QtViewPaneManager* QtViewPaneManager::instance()
|
||||
{
|
||||
return s_instance();
|
||||
return s_viewPaneManagerInstance();
|
||||
}
|
||||
|
||||
bool QtViewPaneManager::exists()
|
||||
{
|
||||
return s_instance.exists();
|
||||
return s_viewPaneManagerInstance.exists();
|
||||
}
|
||||
|
||||
void QtViewPaneManager::SetMainWindow(AzQtComponents::DockMainWindow* mainWindow, QSettings* settings, const QByteArray& lastMainWindowState)
|
||||
|
||||
@@ -369,3 +369,4 @@
|
||||
#define ID_TOOLBAR_WIDGET_SPACER_RIGHT 50013
|
||||
#define ID_TOOLBAR_WIDGET_PLAYCONSOLE_LABEL 50014
|
||||
#define ID_TOOLBAR_WIDGET_LAST 50020
|
||||
#define ID_VIEWPORTUI_VISIBLE 50040
|
||||
|
||||
@@ -59,7 +59,7 @@ namespace
|
||||
{
|
||||
int fps;
|
||||
const char* fpsDesc;
|
||||
} fps[] = {
|
||||
} fpsOptions[] = {
|
||||
{24, "Film(24)"}, {25, "PAL(25)"}, {30, "NTSC(30)"},
|
||||
{48, "Show(48)"}, {50, "PAL Field(50)"}, {60, "NTSC Field(60)"}
|
||||
};
|
||||
@@ -213,9 +213,9 @@ void CSequenceBatchRenderDialog::OnInitDialog()
|
||||
m_ui->m_resolutionCombo->setCurrentIndex(0);
|
||||
|
||||
// Fill the FPS combo box.
|
||||
for (int i = 0; i < AZStd::size(fps); ++i)
|
||||
for (int i = 0; i < AZStd::size(fpsOptions); ++i)
|
||||
{
|
||||
m_ui->m_fpsCombo->addItem(fps[i].fpsDesc);
|
||||
m_ui->m_fpsCombo->addItem(fpsOptions[i].fpsDesc);
|
||||
}
|
||||
m_ui->m_fpsCombo->setCurrentIndex(0);
|
||||
|
||||
@@ -306,9 +306,9 @@ void CSequenceBatchRenderDialog::OnRenderItemSelChange()
|
||||
m_ui->m_destinationEdit->setText(item.folder);
|
||||
// fps
|
||||
bool bFound = false;
|
||||
for (int i = 0; i < arraysize(fps); ++i)
|
||||
for (int i = 0; i < arraysize(fpsOptions); ++i)
|
||||
{
|
||||
if (item.fps == fps[i].fps)
|
||||
if (item.fps == fpsOptions[i].fps)
|
||||
{
|
||||
m_ui->m_fpsCombo->setCurrentIndex(i);
|
||||
bFound = true;
|
||||
@@ -621,7 +621,7 @@ void CSequenceBatchRenderDialog::OnFPSEditChange()
|
||||
|
||||
void CSequenceBatchRenderDialog::OnFPSChange(int itemIndex)
|
||||
{
|
||||
m_customFPS = fps[itemIndex].fps;
|
||||
m_customFPS = fpsOptions[itemIndex].fps;
|
||||
CheckForEnableUpdateButton();
|
||||
}
|
||||
|
||||
@@ -1543,13 +1543,13 @@ bool CSequenceBatchRenderDialog::SetUpNewRenderItem(SRenderItem& item)
|
||||
item.frameRange = Range(m_ui->m_startFrame->value() / m_fpsForTimeToFrameConversion,
|
||||
m_ui->m_endFrame->value() / m_fpsForTimeToFrameConversion);
|
||||
// fps
|
||||
if (m_ui->m_fpsCombo->currentIndex() == -1 || m_ui->m_fpsCombo->currentText() != fps[m_ui->m_fpsCombo->currentIndex()].fpsDesc)
|
||||
if (m_ui->m_fpsCombo->currentIndex() == -1 || m_ui->m_fpsCombo->currentText() != fpsOptions[m_ui->m_fpsCombo->currentIndex()].fpsDesc)
|
||||
{
|
||||
item.fps = m_customFPS;
|
||||
}
|
||||
else
|
||||
{
|
||||
item.fps = fps[m_ui->m_fpsCombo->currentIndex()].fps;
|
||||
item.fps = fpsOptions[m_ui->m_fpsCombo->currentIndex()].fps;
|
||||
}
|
||||
// prefix
|
||||
item.prefix = m_ui->BATCH_RENDER_FILE_PREFIX->text();
|
||||
|
||||
@@ -205,10 +205,10 @@ CTrackViewAnimNode::CTrackViewAnimNode(IAnimSequence* pSequence, IAnimNode* anim
|
||||
for (int i = 0; i < nodeCount; ++i)
|
||||
{
|
||||
IAnimNode* node = pSequence->GetNode(i);
|
||||
IAnimNode* pParentNode = node->GetParent();
|
||||
IAnimNode* pNodeParentNode = node->GetParent();
|
||||
|
||||
// If our node is the parent, then the current node is a child of it
|
||||
if (animNode == pParentNode)
|
||||
if (animNode == pNodeParentNode)
|
||||
{
|
||||
CTrackViewAnimNodeFactory animNodeFactory;
|
||||
CTrackViewAnimNode* pNewTVAnimNode = animNodeFactory.BuildAnimNode(pSequence, node, this);
|
||||
|
||||
@@ -68,12 +68,12 @@ CTrackViewTrack::CTrackViewTrack(IAnimTrack* pTrack, CTrackViewAnimNode* pTrackA
|
||||
{
|
||||
// Search for child tracks
|
||||
const unsigned int subTrackCount = m_pAnimTrack->GetSubTrackCount();
|
||||
for (unsigned int subTrackIndex = 0; subTrackIndex < subTrackCount; ++subTrackIndex)
|
||||
for (unsigned int subTrackI = 0; subTrackI < subTrackCount; ++subTrackI)
|
||||
{
|
||||
IAnimTrack* pSubTrack = m_pAnimTrack->GetSubTrack(subTrackIndex);
|
||||
IAnimTrack* pSubTrack = m_pAnimTrack->GetSubTrack(subTrackI);
|
||||
|
||||
CTrackViewTrackFactory trackFactory;
|
||||
CTrackViewTrack* pNewTVTrack = trackFactory.BuildTrack(pSubTrack, pTrackAnimNode, this, true, subTrackIndex);
|
||||
CTrackViewTrack* pNewTVTrack = trackFactory.BuildTrack(pSubTrack, pTrackAnimNode, this, true, subTrackI);
|
||||
m_childNodes.push_back(std::unique_ptr<CTrackViewNode>(pNewTVTrack));
|
||||
}
|
||||
|
||||
|
||||
@@ -157,7 +157,7 @@ static Quatern Qt_FromMatrix(HMatrix mat)
|
||||
* |w| is greater than 1/2, which is as small as a largest component can be.
|
||||
* Otherwise, the largest diagonal entry corresponds to the largest of |x|,
|
||||
* |y|, or |z|, one of which must be larger than |w|, and at least 1/2. */
|
||||
Quatern qu;
|
||||
Quatern qu = { 0.0f, 0.0f, 0.0f, 1.0f };
|
||||
double tr, s;
|
||||
|
||||
tr = mat[X][X] + mat[Y][Y] + mat[Z][Z];
|
||||
@@ -531,7 +531,7 @@ Quatern snuggle(Quatern q, HVect* k)
|
||||
#define swap(a, i, j) {a[3] = a[i]; a[i] = a[j]; a[j] = a[3]; }
|
||||
#define cycle(a, p) if (p) {a[3] = a[0]; a[0] = a[1]; a[1] = a[2]; a[2] = a[3]; } \
|
||||
else {a[3] = a[2]; a[2] = a[1]; a[1] = a[0]; a[0] = a[3]; }
|
||||
Quatern p;
|
||||
Quatern p = { 0.0f, 0.0f, 0.0f, 1.0f };
|
||||
float ka[4];
|
||||
int i, turn = -1;
|
||||
ka[X] = k->x;
|
||||
|
||||
@@ -2239,7 +2239,8 @@ uint32 CFileUtil::GetAttributes(const char* filename, bool bUseSourceControl /*=
|
||||
bool CFileUtil::CompareFiles(const QString& strFilePath1, const QString& strFilePath2)
|
||||
{
|
||||
// Get the size of both files. If either fails we say they are different (most likely one doesn't exist)
|
||||
uint64 size1, size2;
|
||||
uint64 size1 = 0;
|
||||
uint64 size2 = 0;
|
||||
if (!GetDiskFileSize(strFilePath1.toUtf8().data(), size1) || !GetDiskFileSize(strFilePath2.toUtf8().data(), size2))
|
||||
{
|
||||
return false;
|
||||
|
||||
@@ -116,6 +116,7 @@ bool CImageBT::Load(const QString& fileName, CFloatImage& image)
|
||||
|
||||
// Get the BT header data
|
||||
BtHeader header;
|
||||
memset(&header, 0, sizeof(BtHeader)); // C4701 potentially uninitialized local variable 'header' used
|
||||
bool validData = true;
|
||||
validData = validData && (fread(&header, sizeof(BtHeader), 1, file) != 0);
|
||||
|
||||
|
||||
@@ -419,7 +419,7 @@ static inline bool MatchesWildcardsIgnoreCaseExt_Tpl(const TS& str, const TS& wi
|
||||
const typename TS::value_type* savedStrBegin = 0;
|
||||
const typename TS::value_type* savedStrEnd = 0;
|
||||
const typename TS::value_type* savedWild = 0;
|
||||
size_t savedWildCount;
|
||||
size_t savedWildCount = 0;
|
||||
|
||||
const typename TS::value_type* pStr = str.c_str();
|
||||
const typename TS::value_type* pWild = wildcards.c_str();
|
||||
|
||||
@@ -586,6 +586,8 @@ set(FILES
|
||||
EditorPreferencesPageViewportDebug.cpp
|
||||
EditorPreferencesPageExperimentalLighting.h
|
||||
EditorPreferencesPageExperimentalLighting.cpp
|
||||
EditorPreferencesPageAWS.h
|
||||
EditorPreferencesPageAWS.cpp
|
||||
EditorPreferencesDialog.h
|
||||
EditorPreferencesDialog.cpp
|
||||
EditorPreferencesDialog.ui
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M21.4629 11.2764C20.6447 10.508 19.5748 10.0719 18.4419 10.0719C17.8965 10.0719 17.351 10.1758 16.8475 10.3834C16.5748 9.26204 15.9664 8.2237 15.1063 7.37226C13.8685 6.16779 12.6717 5.47607 10.4147 5.47607C7.92095 5.47607 6.586 6.16779 5.32725 7.37226C4.08949 8.57673 3.30673 10.1907 3.30673 11.8935C3.30673 12.1635 3.32771 12.4335 3.34869 12.7035C2.84519 12.8281 2.49509 13.0831 2.13844 13.4362C1.57201 13.9761 1.25732 14.7029 1.25732 15.4713C1.25732 17.1534 2.66292 18.5032 4.3832 18.5032L18.2741 18.524C20.7286 18.524 22.7426 16.5927 22.7426 14.2045C22.7217 13.1039 22.2811 12.0656 21.4629 11.2764ZM17.2975 16.6659L4.65419 16.6867C3.62622 16.6867 3.45969 16.0387 3.37771 15.6437C3.30673 15.3017 3.37861 14.7882 3.53455 14.5799C3.74825 14.2944 4.10093 14.1649 4.5273 14.1216C4.76182 14.0979 5.1687 14.0552 5.32721 13.8355C5.43211 13.6901 5.47406 13.5032 5.45308 13.3163C5.36917 12.901 5.32721 12.5272 5.32721 12.1119C5.32721 10.7413 5.6315 9.73925 6.63849 8.76321C7.64549 7.78717 8.98815 7.24724 10.4147 7.24724C11.8413 7.24724 12.9769 7.56395 13.9839 8.53999C14.865 9.39143 15.3895 10.4921 15.5154 11.655C15.5363 11.8627 15.6622 12.0703 15.872 12.1534C16.0608 12.2365 16.3126 12.2365 16.4804 12.1119C17.6342 11.3643 18.848 11.1981 19.876 12.1742C20.4424 12.7141 20.7426 13.4362 20.7426 14.2045C20.7426 15.9074 19.0808 16.6659 17.2975 16.6659Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user