Merge branch 'stabilization/2106' into BenchMarksReset
This commit is contained in:
@@ -10,7 +10,7 @@
|
||||
#
|
||||
|
||||
if(NOT PROJECT_NAME)
|
||||
cmake_minimum_required(VERSION 3.19)
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
project(AutomatedTesting
|
||||
LANGUAGES C CXX
|
||||
VERSION 1.0.0.0
|
||||
|
||||
@@ -49,7 +49,7 @@ class TestViewMenuOptions(EditorTestHelper):
|
||||
view_menu_options = [
|
||||
("Center on Selection",),
|
||||
("Show Quick Access Bar",),
|
||||
("Viewport", "Wireframe"),
|
||||
("Viewport", "Configure Layout"),
|
||||
("Viewport", "Go to Position"),
|
||||
("Viewport", "Center on Selection"),
|
||||
("Viewport", "Go to Location"),
|
||||
|
||||
@@ -89,7 +89,7 @@ class TestMenus(object):
|
||||
expected_lines = [
|
||||
"Center on Selection Action triggered",
|
||||
"Show Quick Access Bar Action triggered",
|
||||
"Wireframe Action triggered",
|
||||
"Configure Layout Action triggered",
|
||||
"Go to Position Action triggered",
|
||||
"Center on Selection Action triggered",
|
||||
"Go to Location Action triggered",
|
||||
|
||||
+9
-17
@@ -9,17 +9,8 @@
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
#
|
||||
|
||||
# Cmake version 3.19 is the minimum version needed for all of Open 3D Engine's supported platforms
|
||||
cmake_minimum_required(VERSION 3.19)
|
||||
|
||||
# CMP0111 introduced in 3.19 has a bug that produces the policy to warn every time there is an
|
||||
# INTERFACE IMPORTED library. We use this type of libraries for handling 3rdParty. The rest of
|
||||
# the documentation states that INTERFACE IMPORTED libraries do not require to set locations, but
|
||||
# the policy still warns about it. Issue: https://gitlab.kitware.com/cmake/cmake/-/issues/21470
|
||||
# The issue was fixed in 3.19.1 so we just disable the policy for 3.19
|
||||
if(CMAKE_VERSION VERSION_EQUAL 3.19)
|
||||
cmake_policy(SET CMP0111 OLD)
|
||||
endif()
|
||||
# Cmake version 3.20 is the minimum version needed for all of Open 3D Engine's supported platforms
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
include(cmake/LySet.cmake)
|
||||
include(cmake/Version.cmake)
|
||||
@@ -114,24 +105,25 @@ endforeach()
|
||||
# Post-processing
|
||||
################################################################################
|
||||
# The following steps have to be done after all targets are registered:
|
||||
# Defer generation of the StaticModules.inl file which is needed to create the AZ::Module derived class in monolithic
|
||||
# builds until after all the targets are known
|
||||
ly_delayed_generate_static_modules_inl()
|
||||
|
||||
# 1. Add any dependencies registered via ly_enable_gems
|
||||
ly_enable_gems_delayed()
|
||||
|
||||
# 2. generate a settings registry .setreg file for all ly_add_project_dependencies() and ly_add_target_dependencies() calls
|
||||
# 2. Defer generation of the StaticModules.inl file which is needed to create the AZ::Module derived class in monolithic
|
||||
# builds until after all the targets are known and all the gems are enabled
|
||||
ly_delayed_generate_static_modules_inl()
|
||||
|
||||
# 3. generate a settings registry .setreg file for all ly_add_project_dependencies() and ly_add_target_dependencies() calls
|
||||
# to provide applications with the filenames of gem modules to load
|
||||
# This must be done before ly_delayed_target_link_libraries() as that inserts BUILD_DEPENDENCIES as MANUALLY_ADDED_DEPENDENCIES
|
||||
# if the build dependency is a MODULE_LIBRARY. That would cause a false load dependency to be generated
|
||||
ly_delayed_generate_settings_registry()
|
||||
|
||||
# 3. link targets where the dependency was yet not declared, we need to have the declaration so we do different
|
||||
# 4. link targets where the dependency was yet not declared, we need to have the declaration so we do different
|
||||
# linking logic depending on the type of target
|
||||
ly_delayed_target_link_libraries()
|
||||
|
||||
# 4. generate a registry file for unit testing for platforms that support unit testing
|
||||
# 5. generate a registry file for unit testing for platforms that support unit testing
|
||||
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
|
||||
ly_delayed_generate_unit_test_module_registry()
|
||||
endif()
|
||||
|
||||
@@ -67,7 +67,7 @@ struct CryPakMock
|
||||
MOCK_METHOD1(PoolMalloc, void*(size_t size));
|
||||
MOCK_METHOD1(PoolFree, void(void* p));
|
||||
MOCK_METHOD3(PoolAllocMemoryBlock, AZStd::intrusive_ptr<AZ::IO::MemoryBlock> (size_t nSize, const char* sUsage, size_t nAlign));
|
||||
MOCK_METHOD3(FindFirst, AZ::IO::ArchiveFileIterator(AZStd::string_view pDir, uint32_t nFlags, bool bAllOwUseFileSystem));
|
||||
MOCK_METHOD2(FindFirst, AZ::IO::ArchiveFileIterator(AZStd::string_view pDir, AZ::IO::IArchive::EFileSearchType));
|
||||
MOCK_METHOD1(FindNext, AZ::IO::ArchiveFileIterator(AZ::IO::ArchiveFileIterator handle));
|
||||
MOCK_METHOD1(FindClose, bool(AZ::IO::ArchiveFileIterator));
|
||||
MOCK_METHOD1(GetModificationTime, AZ::IO::IArchive::FileTime(AZ::IO::HandleType f));
|
||||
|
||||
@@ -306,8 +306,7 @@ void CLevelSystem::ScanFolder(const char* subfolder, bool modFolder)
|
||||
|
||||
AZStd::unordered_set<AZStd::string> pakList;
|
||||
|
||||
bool allowFileSystem = true;
|
||||
AZ::IO::ArchiveFileIterator handle = pPak->FindFirst(search.c_str(), 0, allowFileSystem);
|
||||
AZ::IO::ArchiveFileIterator handle = pPak->FindFirst(search.c_str(), AZ::IO::IArchive::eFileSearchType_AllowOnDiskOnly);
|
||||
|
||||
if (handle)
|
||||
{
|
||||
@@ -320,7 +319,7 @@ void CLevelSystem::ScanFolder(const char* subfolder, bool modFolder)
|
||||
{
|
||||
if (AZ::StringFunc::Equal(handle.m_filename.data(), LevelPakName))
|
||||
{
|
||||
// level folder contain pak files like 'level.pak'
|
||||
// level folder contain pak files like 'level.pak'
|
||||
// which we only want to load during level loading.
|
||||
continue;
|
||||
}
|
||||
@@ -351,7 +350,7 @@ void CLevelSystem::ScanFolder(const char* subfolder, bool modFolder)
|
||||
PopulateLevels(search, folder, pPak, modFolder, false);
|
||||
// Load levels outside of the bundles to maintain backward compatibility.
|
||||
PopulateLevels(search, folder, pPak, modFolder, true);
|
||||
|
||||
|
||||
}
|
||||
|
||||
void CLevelSystem::PopulateLevels(
|
||||
@@ -360,7 +359,7 @@ void CLevelSystem::PopulateLevels(
|
||||
{
|
||||
// allow this find first to actually touch the file system
|
||||
// (causes small overhead but with minimal amount of levels this should only be around 150ms on actual DVD Emu)
|
||||
AZ::IO::ArchiveFileIterator handle = pPak->FindFirst(searchPattern.c_str(), 0, fromFileSystemOnly);
|
||||
AZ::IO::ArchiveFileIterator handle = pPak->FindFirst(searchPattern.c_str(), AZ::IO::IArchive::eFileSearchType_AllowOnDiskOnly);
|
||||
|
||||
if (handle)
|
||||
{
|
||||
@@ -973,7 +972,7 @@ void CLevelSystem::UnloadLevel()
|
||||
m_lastLevelName.clear();
|
||||
|
||||
SAFE_RELEASE(m_pCurrentLevel);
|
||||
|
||||
|
||||
// Force Lua garbage collection (may no longer be needed now the legacy renderer has been removed).
|
||||
// Normally the GC step is triggered at the end of this method (by the ESYSTEM_EVENT_LEVEL_POST_UNLOAD event).
|
||||
EBUS_EVENT(AZ::ScriptSystemRequestBus, GarbageCollect);
|
||||
|
||||
@@ -9,8 +9,6 @@
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
#
|
||||
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
|
||||
ly_add_target(
|
||||
NAME AzAutoGen HEADERONLY
|
||||
NAMESPACE AZ
|
||||
|
||||
@@ -292,8 +292,13 @@ namespace AZ
|
||||
const typename VecType::FloatType cmp2 = VecType::AndNot(cmp0, cmp1);
|
||||
|
||||
// -1/x
|
||||
// this step is calculated for all values of x, but only used if x > Sqrt(2) + 1
|
||||
// in order to avoid a division by zero, detect if xabs is zero here and replace it with an arbitrary value
|
||||
// if xabs does equal zero, the value here doesn't matter because the result will be thrown away
|
||||
typename VecType::FloatType xabsSafe =
|
||||
VecType::Add(xabs, VecType::And(VecType::CmpEq(xabs, VecType::ZeroFloat()), FastLoadConstant<VecType>(Simd::g_vec1111)));
|
||||
const typename VecType::FloatType y0 = VecType::And(cmp0, FastLoadConstant<VecType>(Simd::g_HalfPi));
|
||||
typename VecType::FloatType x0 = VecType::Div(FastLoadConstant<VecType>(Simd::g_vec1111), xabs);
|
||||
typename VecType::FloatType x0 = VecType::Div(FastLoadConstant<VecType>(Simd::g_vec1111), xabsSafe);
|
||||
x0 = VecType::Xor(x0, VecType::CastToFloat(FastLoadConstant<VecType>(Simd::g_negateMask)));
|
||||
|
||||
const typename VecType::FloatType y1 = VecType::And(cmp2, FastLoadConstant<VecType>(Simd::g_QuarterPi));
|
||||
@@ -368,8 +373,12 @@ namespace AZ
|
||||
|
||||
typename VecType::FloatType offset = VecType::And(x_lt_0, offset1);
|
||||
|
||||
// the result of this part of the computation is thrown away if x equals 0,
|
||||
// but if x does equal 0, it will cause a division by zero
|
||||
// so replace zero by an arbitrary value here in that case
|
||||
typename VecType::FloatType xSafe = VecType::Add(x, VecType::And(x_eq_0, FastLoadConstant<VecType>(Simd::g_vec1111)));
|
||||
const typename VecType::FloatType atan_mask = VecType::Not(VecType::Or(x_eq_0, y_eq_0));
|
||||
const typename VecType::FloatType atan_arg = VecType::Div(y, x);
|
||||
const typename VecType::FloatType atan_arg = VecType::Div(y, xSafe);
|
||||
typename VecType::FloatType atan_result = VecType::Atan(atan_arg);
|
||||
atan_result = VecType::Add(atan_result, offset);
|
||||
atan_result = VecType::AndNot(pio2_mask, atan_result);
|
||||
|
||||
@@ -471,6 +471,7 @@ namespace AZ
|
||||
|
||||
AZ_MATH_INLINE Vec2::FloatType Vec2::Reciprocal(FloatArgType value)
|
||||
{
|
||||
value = Sse::ReplaceFourth(Sse::ReplaceThird(value, 1.0f), 1.0f);
|
||||
return Sse::Reciprocal(value);
|
||||
}
|
||||
|
||||
@@ -513,6 +514,7 @@ namespace AZ
|
||||
|
||||
AZ_MATH_INLINE Vec2::FloatType Vec2::SqrtInv(FloatArgType value)
|
||||
{
|
||||
value = Sse::ReplaceFourth(Sse::ReplaceThird(value, 1.0f), 1.0f);
|
||||
return Sse::SqrtInv(value);
|
||||
}
|
||||
|
||||
|
||||
@@ -507,6 +507,7 @@ namespace AZ
|
||||
|
||||
AZ_MATH_INLINE Vec3::FloatType Vec3::Reciprocal(FloatArgType value)
|
||||
{
|
||||
value = Sse::ReplaceFourth(value, 1.0f);
|
||||
return Sse::Reciprocal(value);
|
||||
}
|
||||
|
||||
@@ -549,6 +550,7 @@ namespace AZ
|
||||
|
||||
AZ_MATH_INLINE Vec3::FloatType Vec3::SqrtInv(FloatArgType value)
|
||||
{
|
||||
value = Sse::ReplaceFourth(value, 1.0f);
|
||||
return Sse::SqrtInv(value);
|
||||
}
|
||||
|
||||
|
||||
@@ -175,4 +175,11 @@ namespace AZ::Utils
|
||||
path /= ".o3de";
|
||||
return path.Native();
|
||||
}
|
||||
|
||||
AZ::IO::FixedMaxPathString GetO3deLogsDirectory()
|
||||
{
|
||||
AZ::IO::FixedMaxPath path = GetO3deManifestDirectory();
|
||||
path /= "Logs";
|
||||
return path.Native();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,6 +97,9 @@ namespace AZ
|
||||
//! Retrieves the full path where the manifest file lives, i.e. "<userhome>/.o3de/o3de_manifest.json"
|
||||
AZ::IO::FixedMaxPathString GetEngineManifestPath();
|
||||
|
||||
//! Retrieves the full directory to the O3DE logs directory, i.e. "<userhome>/.o3de/Logs"
|
||||
AZ::IO::FixedMaxPathString GetO3deLogsDirectory();
|
||||
|
||||
//! Retrieves the App root path to use on the current platform
|
||||
//! If the optional is not engaged the AppRootPath should be calculated based
|
||||
//! on the location of the bootstrap.cfg file
|
||||
|
||||
@@ -1290,7 +1290,7 @@ namespace AZ::IO
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
AZ::IO::ArchiveFileIterator Archive::FindFirst(AZStd::string_view pDir, [[maybe_unused]] uint32_t nPathFlags, bool bAllowUseFileSystem)
|
||||
AZ::IO::ArchiveFileIterator Archive::FindFirst(AZStd::string_view pDir, EFileSearchType searchType)
|
||||
{
|
||||
auto szFullPath = AZ::IO::FileIOBase::GetDirectInstance()->ResolvePath(pDir);
|
||||
if (!szFullPath)
|
||||
@@ -1299,8 +1299,26 @@ namespace AZ::IO
|
||||
return {};
|
||||
}
|
||||
|
||||
bool bScanZips{};
|
||||
bool bAllowUseFileSystem{};
|
||||
switch (searchType)
|
||||
{
|
||||
case IArchive::eFileSearchType_AllowInZipsOnly:
|
||||
bAllowUseFileSystem = false;
|
||||
bScanZips = true;
|
||||
break;
|
||||
case IArchive::eFileSearchType_AllowOnDiskAndInZips:
|
||||
bAllowUseFileSystem = true;
|
||||
bScanZips = true;
|
||||
break;
|
||||
case IArchive::eFileSearchType_AllowOnDiskOnly:
|
||||
bAllowUseFileSystem = true;
|
||||
bScanZips = false;
|
||||
break;
|
||||
}
|
||||
|
||||
AZStd::intrusive_ptr<AZ::IO::FindData> pFindData = new AZ::IO::FindData();
|
||||
pFindData->Scan(this, szFullPath->Native(), bAllowUseFileSystem);
|
||||
pFindData->Scan(this, szFullPath->Native(), bAllowUseFileSystem, bScanZips);
|
||||
|
||||
return pFindData->Fetch();
|
||||
}
|
||||
@@ -1676,7 +1694,7 @@ namespace AZ::IO
|
||||
return true;
|
||||
}
|
||||
|
||||
if (AZ::IO::ArchiveFileIterator fileIterator = FindFirst(pWildcardIn, 0, true); fileIterator)
|
||||
if (AZ::IO::ArchiveFileIterator fileIterator = FindFirst(pWildcardIn, IArchive::eFileSearchType_AllowOnDiskOnly); fileIterator)
|
||||
{
|
||||
AZStd::vector<AZStd::string> files;
|
||||
do
|
||||
|
||||
@@ -234,7 +234,7 @@ namespace AZ::IO
|
||||
uint64_t FTell(AZ::IO::HandleType handle) override;
|
||||
int FFlush(AZ::IO::HandleType handle) override;
|
||||
int FClose(AZ::IO::HandleType handle) override;
|
||||
AZ::IO::ArchiveFileIterator FindFirst(AZStd::string_view pDir, uint32_t nPathFlags = 0, bool bAllOwUseFileSystem = false) override;
|
||||
AZ::IO::ArchiveFileIterator FindFirst(AZStd::string_view pDir, EFileSearchType searchType = eFileSearchType_AllowInZipsOnly) override;
|
||||
AZ::IO::ArchiveFileIterator FindNext(AZ::IO::ArchiveFileIterator fileIterator) override;
|
||||
bool FindClose(AZ::IO::ArchiveFileIterator fileIterator) override;
|
||||
int FEof(AZ::IO::HandleType handle) override;
|
||||
|
||||
@@ -77,7 +77,7 @@ namespace AZ::IO
|
||||
return m_findData && m_lastFetchValid;
|
||||
}
|
||||
|
||||
void FindData::Scan(IArchive* archive, AZStd::string_view szDir, bool bAllowUseFS)
|
||||
void FindData::Scan(IArchive* archive, AZStd::string_view szDir, bool bAllowUseFS, bool bScanZips)
|
||||
{
|
||||
// get the priority into local variable to avoid it changing in the course of
|
||||
// this function execution
|
||||
@@ -87,12 +87,18 @@ namespace AZ::IO
|
||||
{
|
||||
// first, find the file system files
|
||||
ScanFS(archive, szDir);
|
||||
ScanZips(archive, szDir);
|
||||
if (bScanZips)
|
||||
{
|
||||
ScanZips(archive, szDir);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// first, find the zip files
|
||||
ScanZips(archive, szDir);
|
||||
if (bScanZips)
|
||||
{
|
||||
ScanZips(archive, szDir);
|
||||
}
|
||||
if (bAllowUseFS || nVarPakPriority != ArchiveLocationPriority::ePakPriorityPakOnly)
|
||||
{
|
||||
ScanFS(archive, szDir);
|
||||
@@ -111,30 +117,31 @@ namespace AZ::IO
|
||||
}
|
||||
AZ::IO::FileIOBase::GetDirectInstance()->FindFiles(searchDirectory.c_str(), pattern.c_str(), [&](const char* filePath) -> bool
|
||||
{
|
||||
AZ::IO::FileDesc fileDesc;
|
||||
AZStd::string filePathEntry{filePath};
|
||||
AZ::IO::ArchiveFileIterator fileIterator;
|
||||
fileIterator.m_filename = AZ::IO::PathView(filePath).Filename().Native();
|
||||
fileIterator.m_fileDesc.nAttrib = {};
|
||||
|
||||
if (AZ::IO::FileIOBase::GetDirectInstance()->IsDirectory(filePath))
|
||||
{
|
||||
fileDesc.nAttrib = fileDesc.nAttrib | AZ::IO::FileDesc::Attribute::Subdirectory;
|
||||
fileIterator.m_fileDesc.nAttrib = fileIterator.m_fileDesc.nAttrib | AZ::IO::FileDesc::Attribute::Subdirectory;
|
||||
m_fileStack.emplace_back(AZStd::move(fileIterator));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (AZ::IO::FileIOBase::GetDirectInstance()->IsReadOnly(filePath))
|
||||
{
|
||||
fileDesc.nAttrib = fileDesc.nAttrib | AZ::IO::FileDesc::Attribute::ReadOnly;
|
||||
fileIterator.m_fileDesc.nAttrib = fileIterator.m_fileDesc.nAttrib | AZ::IO::FileDesc::Attribute::ReadOnly;
|
||||
}
|
||||
AZ::u64 fileSize = 0;
|
||||
AZ::IO::FileIOBase::GetDirectInstance()->Size(filePath, fileSize);
|
||||
fileDesc.nSize = fileSize;
|
||||
fileDesc.tWrite = AZ::IO::FileIOBase::GetDirectInstance()->ModificationTime(filePath);
|
||||
fileIterator.m_fileDesc.nSize = fileSize;
|
||||
fileIterator.m_fileDesc.tWrite = AZ::IO::FileIOBase::GetDirectInstance()->ModificationTime(filePath);
|
||||
|
||||
// These times are not supported by our file interface
|
||||
fileDesc.tAccess = fileDesc.tWrite;
|
||||
fileDesc.tCreate = fileDesc.tWrite;
|
||||
fileIterator.m_fileDesc.tAccess = fileIterator.m_fileDesc.tWrite;
|
||||
fileIterator.m_fileDesc.tCreate = fileIterator.m_fileDesc.tWrite;
|
||||
m_fileStack.emplace_back(AZStd::move(fileIterator));
|
||||
}
|
||||
[[maybe_unused]] auto result = m_mapFiles.emplace(AZStd::move(filePathEntry), fileDesc);
|
||||
AZ_Assert(result.second, "Failed to insert FindData entry for filePath %s", filePath);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
@@ -164,7 +171,7 @@ namespace AZ::IO
|
||||
fileDesc.nAttrib = AZ::IO::FileDesc::Attribute::ReadOnly | AZ::IO::FileDesc::Attribute::Archive;
|
||||
fileDesc.nSize = fileEntry->desc.lSizeUncompressed;
|
||||
fileDesc.tWrite = fileEntry->GetModificationTime();
|
||||
m_mapFiles.emplace(fname, fileDesc);
|
||||
m_fileStack.emplace_back(AZ::IO::ArchiveFileIterator{ this, fname, fileDesc });
|
||||
}
|
||||
|
||||
ZipDir::FindDir findDirectoryEntry(zipCache);
|
||||
@@ -177,7 +184,7 @@ namespace AZ::IO
|
||||
}
|
||||
AZ::IO::FileDesc fileDesc;
|
||||
fileDesc.nAttrib = AZ::IO::FileDesc::Attribute::ReadOnly | AZ::IO::FileDesc::Attribute::Archive | AZ::IO::FileDesc::Attribute::Subdirectory;
|
||||
m_mapFiles.emplace(fname, fileDesc);
|
||||
m_fileStack.emplace_back(AZ::IO::ArchiveFileIterator{ this, fname, fileDesc });
|
||||
}
|
||||
};
|
||||
|
||||
@@ -246,7 +253,7 @@ namespace AZ::IO
|
||||
if (!bindRootIter->empty() && AZStd::wildcard_match(sourcePathRemainder.Native(), bindRootIter->Native()))
|
||||
{
|
||||
AZ::IO::FileDesc fileDesc{ AZ::IO::FileDesc::Attribute::ReadOnly | AZ::IO::FileDesc::Attribute::Archive | AZ::IO::FileDesc::Attribute::Subdirectory };
|
||||
m_mapFiles.emplace(bindRootIter->Native(), fileDesc);
|
||||
m_fileStack.emplace_back(AZ::IO::ArchiveFileIterator{ this, bindRootIter->Native(), fileDesc });
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -262,22 +269,19 @@ namespace AZ::IO
|
||||
|
||||
AZ::IO::ArchiveFileIterator FindData::Fetch()
|
||||
{
|
||||
AZ::IO::ArchiveFileIterator fileIterator;
|
||||
fileIterator.m_findData = this;
|
||||
if (m_mapFiles.empty())
|
||||
if (m_fileStack.empty())
|
||||
{
|
||||
return fileIterator;
|
||||
AZ::IO::ArchiveFileIterator emptyFileIterator;
|
||||
emptyFileIterator.m_lastFetchValid = false;
|
||||
emptyFileIterator.m_findData = this;
|
||||
return emptyFileIterator;
|
||||
}
|
||||
|
||||
auto pakFileIter = m_mapFiles.begin();
|
||||
AZStd::string fullFilePath;
|
||||
AZ::StringFunc::Path::GetFullFileName(pakFileIter->first.c_str(), fullFilePath);
|
||||
fileIterator.m_filename = AZStd::move(fullFilePath);
|
||||
fileIterator.m_fileDesc = pakFileIter->second;
|
||||
fileIterator.m_lastFetchValid = true;
|
||||
|
||||
// Remove Fetched item from the FindData map so that the iteration continues
|
||||
m_mapFiles.erase(pakFileIter);
|
||||
AZ::IO::ArchiveFileIterator fileIterator{ m_fileStack.back() };
|
||||
fileIterator.m_lastFetchValid = true;
|
||||
fileIterator.m_findData = this;
|
||||
m_fileStack.pop_back();
|
||||
return fileIterator;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
#include <AzCore/std/smart_ptr/intrusive_base.h>
|
||||
#include <AzCore/std/string/fixed_string.h>
|
||||
|
||||
|
||||
namespace AZ::IO
|
||||
{
|
||||
struct IArchive;
|
||||
@@ -74,13 +73,13 @@ namespace AZ::IO
|
||||
AZ_CLASS_ALLOCATOR(FindData, AZ::SystemAllocator, 0);
|
||||
FindData() = default;
|
||||
AZ::IO::ArchiveFileIterator Fetch();
|
||||
void Scan(IArchive* archive, AZStd::string_view path, bool bAllowUseFS = false);
|
||||
void Scan(IArchive* archive, AZStd::string_view path, bool bAllowUseFS = false, bool bScanZips = true);
|
||||
|
||||
protected:
|
||||
void ScanFS(IArchive* archive, AZStd::string_view path);
|
||||
void ScanZips(IArchive* archive, AZStd::string_view path);
|
||||
|
||||
using FileMap = AZStd::map<AZStd::string, AZ::IO::FileDesc, AZStdStringLessCaseInsensitive>;
|
||||
FileMap m_mapFiles;
|
||||
using FileStack = AZStd::vector<ArchiveFileIterator>;
|
||||
FileStack m_fileStack;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -197,6 +197,13 @@ namespace AZ::IO
|
||||
eInMemoryPakLocale_PAK,
|
||||
};
|
||||
|
||||
enum EFileSearchType
|
||||
{
|
||||
eFileSearchType_AllowInZipsOnly = 0,
|
||||
eFileSearchType_AllowOnDiskAndInZips,
|
||||
eFileSearchType_AllowOnDiskOnly
|
||||
};
|
||||
|
||||
using SignedFileSize = int64_t;
|
||||
|
||||
virtual ~IArchive() = default;
|
||||
@@ -315,7 +322,7 @@ namespace AZ::IO
|
||||
|
||||
// Arguments:
|
||||
// nFlags is a combination of EPathResolutionRules flags.
|
||||
virtual ArchiveFileIterator FindFirst(AZStd::string_view pDir, uint32_t nFlags = 0, bool bAllowUseFileSystem = false) = 0;
|
||||
virtual ArchiveFileIterator FindFirst(AZStd::string_view pDir, EFileSearchType searchType = eFileSearchType_AllowInZipsOnly) = 0;
|
||||
virtual ArchiveFileIterator FindNext(AZ::IO::ArchiveFileIterator handle) = 0;
|
||||
virtual bool FindClose(AZ::IO::ArchiveFileIterator handle) = 0;
|
||||
//returns file modification time
|
||||
|
||||
@@ -693,11 +693,11 @@ namespace AzFramework
|
||||
// set the __index so we can read values in case we change the script
|
||||
// after we export the component
|
||||
lua_pushliteral(lua, "__index");
|
||||
lua_pushcclosure(lua, &Internal::Properties__Index, 1);
|
||||
lua_pushcclosure(lua, &Internal::Properties__Index, 0);
|
||||
lua_rawset(lua, -3);
|
||||
|
||||
lua_pushliteral(lua, "__newindex");
|
||||
lua_pushcclosure(lua, &Internal::Properties__NewIndex, 1);
|
||||
lua_pushcclosure(lua, &Internal::Properties__NewIndex, 0);
|
||||
lua_rawset(lua, -3);
|
||||
}
|
||||
lua_pop(lua, 1); // pop the properties table (or the nil value)
|
||||
@@ -900,11 +900,11 @@ namespace AzFramework
|
||||
// Ensure that this instance of Properties table has the proper __index and __newIndex metamethods.
|
||||
lua_newtable(lua); // This new table will become the Properties instance metatable. Stack: ScriptRootTable PropertiesTable EntityTable "Properties" {} {}
|
||||
lua_pushliteral(lua, "__index"); // Stack: ScriptRootTable PropertiesTable EntityTable "Properties" {} {} __index
|
||||
lua_pushcclosure(lua, &Internal::Properties__Index, 1); // Stack: ScriptRootTable PropertiesTable EntityTable "Properties" {} {} __index function
|
||||
lua_pushcclosure(lua, &Internal::Properties__Index, 0); // Stack: ScriptRootTable PropertiesTable EntityTable "Properties" {} {} __index function
|
||||
lua_rawset(lua, -3); // Stack: ScriptRootTable PropertiesTable EntityTable "Properties" {} {__index=Internal::Properties__Index}
|
||||
|
||||
lua_pushliteral(lua, "__newindex");
|
||||
lua_pushcclosure(lua, &Internal::Properties__NewIndex, 1);
|
||||
lua_pushcclosure(lua, &Internal::Properties__NewIndex, 0);
|
||||
lua_rawset(lua, -3); // Stack: ScriptRootTable PropertiesTable EntityTable "Properties" {} {__index=Internal::Properties__Index __newindex=Internal::Properties__NewIndex}
|
||||
lua_setmetatable(lua, -2); // Stack: ScriptRootTable PropertiesTable EntityTable "Properties" {Meta{__index=Internal::Properties__Index __newindex=Internal::Properties__NewIndex} }
|
||||
|
||||
|
||||
@@ -8,25 +8,27 @@
|
||||
# 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.
|
||||
#
|
||||
|
||||
ly_get_list_relative_pal_filename(pal_dir ${CMAKE_CURRENT_LIST_DIR}/AzTest/Platform/${PAL_PLATFORM_NAME})
|
||||
|
||||
ly_add_target(
|
||||
NAME AzTest STATIC
|
||||
NAMESPACE AZ
|
||||
FILES_CMAKE
|
||||
AzTest/aztest_files.cmake
|
||||
${pal_dir}/platform_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake
|
||||
INCLUDE_DIRECTORIES
|
||||
PUBLIC
|
||||
.
|
||||
${pal_dir}
|
||||
BUILD_DEPENDENCIES
|
||||
PUBLIC
|
||||
3rdParty::googletest::GMock
|
||||
3rdParty::googletest::GTest
|
||||
3rdParty::GoogleBenchmark
|
||||
AZ::AzCore
|
||||
PLATFORM_INCLUDE_FILES
|
||||
if(NOT LY_MONOLITHIC_GAME)
|
||||
ly_get_list_relative_pal_filename(pal_dir ${CMAKE_CURRENT_LIST_DIR}/AzTest/Platform/${PAL_PLATFORM_NAME})
|
||||
|
||||
ly_add_target(
|
||||
NAME AzTest STATIC
|
||||
NAMESPACE AZ
|
||||
FILES_CMAKE
|
||||
AzTest/aztest_files.cmake
|
||||
${pal_dir}/platform_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake
|
||||
INCLUDE_DIRECTORIES
|
||||
PUBLIC
|
||||
.
|
||||
${pal_dir}
|
||||
BUILD_DEPENDENCIES
|
||||
PUBLIC
|
||||
3rdParty::googletest::GMock
|
||||
3rdParty::googletest::GTest
|
||||
3rdParty::GoogleBenchmark
|
||||
AZ::AzCore
|
||||
PLATFORM_INCLUDE_FILES
|
||||
${pal_dir}/platform_${PAL_PLATFORM_NAME_LOWERCASE}.cmake
|
||||
)
|
||||
)
|
||||
endif()
|
||||
|
||||
+26
-8
@@ -159,12 +159,23 @@ namespace AzToolsFramework
|
||||
|
||||
void PrefabEditorEntityOwnershipService::GetNonPrefabEntities(EntityList& entities)
|
||||
{
|
||||
m_rootInstance->GetEntities(entities, false);
|
||||
m_rootInstance->GetEntities(
|
||||
[&entities](const AZStd::unique_ptr<AZ::Entity>& entity)
|
||||
{
|
||||
entities.emplace_back(entity.get());
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
bool PrefabEditorEntityOwnershipService::GetAllEntities(EntityList& entities)
|
||||
{
|
||||
m_rootInstance->GetEntities(entities, true);
|
||||
m_rootInstance->GetAllEntitiesInHierarchy(
|
||||
[&entities](const AZStd::unique_ptr<AZ::Entity>& entity)
|
||||
{
|
||||
entities.emplace_back(entity.get());
|
||||
return true;
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -252,13 +263,20 @@ namespace AzToolsFramework
|
||||
}
|
||||
|
||||
AZStd::string out;
|
||||
if (m_loaderInterface->SaveTemplateToString(m_rootInstance->GetTemplateId(), out))
|
||||
|
||||
if (!m_loaderInterface->SaveTemplateToString(m_rootInstance->GetTemplateId(), out))
|
||||
{
|
||||
const size_t bytesToWrite = out.size();
|
||||
const size_t bytesWritten = stream.Write(bytesToWrite, out.data());
|
||||
return bytesWritten == bytesToWrite;
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
|
||||
const size_t bytesToWrite = out.size();
|
||||
const size_t bytesWritten = stream.Write(bytesToWrite, out.data());
|
||||
if(bytesWritten != bytesToWrite)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
m_prefabSystemComponent->SetTemplateDirtyFlag(templateId, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
void PrefabEditorEntityOwnershipService::CreateNewLevelPrefab(AZStd::string_view filename, const AZStd::string& templateFilename)
|
||||
@@ -544,7 +562,7 @@ namespace AzToolsFramework
|
||||
return;
|
||||
}
|
||||
|
||||
m_rootInstance->GetNestedEntities([this](AZStd::unique_ptr<AZ::Entity>& entity)
|
||||
m_rootInstance->GetAllEntitiesInHierarchy([this](AZStd::unique_ptr<AZ::Entity>& entity)
|
||||
{
|
||||
AZ_Assert(entity, "Invalid entity found in root instance while starting play in editor.");
|
||||
if (entity->GetState() == AZ::Entity::State::Active)
|
||||
|
||||
@@ -187,13 +187,14 @@ namespace AzToolsFramework
|
||||
return removedEntity;
|
||||
}
|
||||
|
||||
void Instance::DetachNestedEntities(const AZStd::function<void(AZStd::unique_ptr<AZ::Entity>)>& callback)
|
||||
void Instance::DetachAllEntitiesInHierarchy(const AZStd::function<void(AZStd::unique_ptr<AZ::Entity>)>& callback)
|
||||
{
|
||||
callback(AZStd::move(DetachContainerEntity()));
|
||||
DetachEntities(callback);
|
||||
|
||||
for (const auto& [instanceAlias, instance] : m_nestedInstances)
|
||||
{
|
||||
instance->DetachNestedEntities(callback);
|
||||
instance->DetachAllEntitiesInHierarchy(callback);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -372,17 +373,25 @@ namespace AzToolsFramework
|
||||
}
|
||||
}
|
||||
|
||||
void Instance::GetConstNestedEntities(const AZStd::function<bool(const AZ::Entity&)>& callback)
|
||||
bool Instance::GetEntities_Impl(const AZStd::function<bool(AZStd::unique_ptr<AZ::Entity>&)>& callback)
|
||||
{
|
||||
GetConstEntities(callback);
|
||||
|
||||
for (const auto& [instanceAlias, instance] : m_nestedInstances)
|
||||
for (auto& [entityAlias, entity] : m_entities)
|
||||
{
|
||||
instance->GetConstNestedEntities(callback);
|
||||
if (!entity)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!callback(entity))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Instance::GetConstEntities(const AZStd::function<bool(const AZ::Entity&)>& callback)
|
||||
bool Instance::GetConstEntities_Impl(const AZStd::function<bool(const AZ::Entity&)>& callback) const
|
||||
{
|
||||
for (const auto& [entityAlias, entity] : m_entities)
|
||||
{
|
||||
@@ -393,19 +402,83 @@ namespace AzToolsFramework
|
||||
|
||||
if (!callback(*entity))
|
||||
{
|
||||
break;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Instance::GetNestedEntities(const AZStd::function<bool(AZStd::unique_ptr<AZ::Entity>&)>& callback)
|
||||
bool Instance::GetAllEntitiesInHierarchy_Impl(const AZStd::function<bool(AZStd::unique_ptr<AZ::Entity>&)>& callback)
|
||||
{
|
||||
GetEntities(callback);
|
||||
if (HasContainerEntity())
|
||||
{
|
||||
if (!callback(m_containerEntity))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!GetEntities_Impl(callback))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto& [instanceAlias, instance] : m_nestedInstances)
|
||||
{
|
||||
instance->GetNestedEntities(callback);
|
||||
if (!instance->GetAllEntitiesInHierarchy_Impl(callback))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Instance::GetAllEntitiesInHierarchyConst_Impl(const AZStd::function<bool(const AZ::Entity&)>& callback) const
|
||||
{
|
||||
if (HasContainerEntity())
|
||||
{
|
||||
if (!callback(*m_containerEntity))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!GetConstEntities_Impl(callback))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const auto& [instanceAlias, instance] : m_nestedInstances)
|
||||
{
|
||||
if (!instance->GetAllEntitiesInHierarchyConst_Impl(callback))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Instance::GetEntities(const AZStd::function<bool(AZStd::unique_ptr<AZ::Entity>&)>& callback)
|
||||
{
|
||||
GetEntities_Impl(callback);
|
||||
}
|
||||
|
||||
void Instance::GetConstEntities(const AZStd::function<bool(const AZ::Entity&)>& callback) const
|
||||
{
|
||||
GetConstEntities_Impl(callback);
|
||||
}
|
||||
|
||||
void Instance::GetAllEntitiesInHierarchy(const AZStd::function<bool(AZStd::unique_ptr<AZ::Entity>&)>& callback)
|
||||
{
|
||||
GetAllEntitiesInHierarchy_Impl(callback);
|
||||
}
|
||||
|
||||
void Instance::GetAllEntitiesInHierarchyConst(const AZStd::function<bool(const AZ::Entity&)>& callback) const
|
||||
{
|
||||
GetAllEntitiesInHierarchyConst_Impl(callback);
|
||||
}
|
||||
|
||||
void Instance::GetNestedInstances(const AZStd::function<void(AZStd::unique_ptr<Instance>&)>& callback)
|
||||
@@ -416,44 +489,6 @@ namespace AzToolsFramework
|
||||
}
|
||||
}
|
||||
|
||||
void Instance::GetEntities(const AZStd::function<bool(AZStd::unique_ptr<AZ::Entity>&)>& callback)
|
||||
{
|
||||
for (auto& [entityAlias, entity] : m_entities)
|
||||
{
|
||||
if (!callback(entity))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Instance::GetEntities(EntityList& entities, bool includeNestedEntities)
|
||||
{
|
||||
// Non-recursive traversal of instances
|
||||
AZStd::vector<Instance*> instancesToTraverse = { this };
|
||||
while (!instancesToTraverse.empty())
|
||||
{
|
||||
Instance* currentInstance = instancesToTraverse.back();
|
||||
instancesToTraverse.pop_back();
|
||||
if (includeNestedEntities)
|
||||
{
|
||||
instancesToTraverse.reserve(instancesToTraverse.size() + currentInstance->m_nestedInstances.size());
|
||||
for (const auto& instanceByAlias : currentInstance->m_nestedInstances)
|
||||
{
|
||||
instancesToTraverse.push_back(instanceByAlias.second.get());
|
||||
}
|
||||
}
|
||||
|
||||
// Size increases by 1 for each instance because we have to count the container entity also.
|
||||
entities.reserve(entities.size() + currentInstance->m_entities.size() + 1);
|
||||
entities.push_back(m_containerEntity.get());
|
||||
for (const auto& entityByAlias : currentInstance->m_entities)
|
||||
{
|
||||
entities.push_back(entityByAlias.second.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EntityAliasOptionalReference Instance::GetEntityAlias(const AZ::EntityId& id)
|
||||
{
|
||||
if (m_instanceToTemplateEntityIdMap.count(id))
|
||||
|
||||
@@ -87,7 +87,15 @@ namespace AzToolsFramework
|
||||
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);
|
||||
|
||||
/**
|
||||
* Detaches all entities in the instance hierarchy.
|
||||
* Includes all direct entities, all nested entities, and all container entities.
|
||||
* Note that without container entities the hierarchy that remains cannot be used further without restoring new ones.
|
||||
* @param callback A user provided callback that can be used to capture ownership and manipulate the detached entities.
|
||||
*/
|
||||
void DetachAllEntitiesInHierarchy(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();
|
||||
@@ -113,10 +121,10 @@ namespace AzToolsFramework
|
||||
/**
|
||||
* Gets the entities in the Instance DOM. Can recursively trace all nested instances.
|
||||
*/
|
||||
void GetConstNestedEntities(const AZStd::function<bool(const AZ::Entity&)>& callback);
|
||||
void GetConstEntities(const AZStd::function<bool(const AZ::Entity&)>& callback);
|
||||
void GetNestedEntities(const AZStd::function<bool(AZStd::unique_ptr<AZ::Entity>&)>& callback);
|
||||
void GetEntities(const AZStd::function<bool(AZStd::unique_ptr<AZ::Entity>&)>& callback);
|
||||
void GetConstEntities(const AZStd::function<bool(const AZ::Entity&)>& callback) const;
|
||||
void GetAllEntitiesInHierarchy(const AZStd::function<bool(AZStd::unique_ptr<AZ::Entity>&)>& callback);
|
||||
void GetAllEntitiesInHierarchyConst(const AZStd::function<bool(const AZ::Entity&)>& callback) const;
|
||||
void GetNestedInstances(const AZStd::function<void(AZStd::unique_ptr<Instance>&)>& callback);
|
||||
|
||||
/**
|
||||
@@ -176,12 +184,6 @@ namespace AzToolsFramework
|
||||
|
||||
static InstanceAlias GenerateInstanceAlias();
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Gets the entities owned by this instance
|
||||
*/
|
||||
void GetEntities(EntityList& entities, bool includeNestedEntities = false);
|
||||
|
||||
private:
|
||||
static constexpr const char s_aliasPathSeparator = '/';
|
||||
|
||||
@@ -189,6 +191,11 @@ namespace AzToolsFramework
|
||||
|
||||
void RemoveEntities(const AZStd::function<bool(const AZStd::unique_ptr<AZ::Entity>&)>& filter);
|
||||
|
||||
bool GetEntities_Impl(const AZStd::function<bool(AZStd::unique_ptr<AZ::Entity>&)>& callback);
|
||||
bool GetConstEntities_Impl(const AZStd::function<bool(const AZ::Entity&)>& callback) const;
|
||||
bool GetAllEntitiesInHierarchy_Impl(const AZStd::function<bool(AZStd::unique_ptr<AZ::Entity>&)>& callback);
|
||||
bool GetAllEntitiesInHierarchyConst_Impl(const AZStd::function<bool(const AZ::Entity&)>& callback) const;
|
||||
|
||||
bool RegisterEntity(const AZ::EntityId& entityId, const EntityAlias& entityAlias);
|
||||
AZStd::unique_ptr<AZ::Entity> DetachEntity(const EntityAlias& entityAlias);
|
||||
|
||||
|
||||
+2
-2
@@ -154,7 +154,7 @@ namespace AzToolsFramework
|
||||
InstanceOptionalReference owningInstanceReference = m_storingInstance->m_instanceEntityMapper->FindOwningInstance(entityId);
|
||||
|
||||
// Start with an empty alias to build out our reference path
|
||||
// If we can't resolve this id we'll return a random new alias instead of a reference path
|
||||
// If we can't resolve this id we'll return a new alias based on the entity ID instead of a reference path
|
||||
AliasPath relativeEntityAliasPath;
|
||||
if (!owningInstanceReference)
|
||||
{
|
||||
@@ -162,7 +162,7 @@ namespace AzToolsFramework
|
||||
"Prefab - EntityIdMapper: Entity with Id %s has no registered owning instance",
|
||||
entityId.ToString().c_str());
|
||||
|
||||
return Instance::GenerateEntityAlias();
|
||||
return AZStd::string::format("Entity_%s", entityId.ToString().c_str());
|
||||
}
|
||||
|
||||
Instance* owningInstance = &(owningInstanceReference->get());
|
||||
|
||||
+9
-24
@@ -62,25 +62,16 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
}
|
||||
}
|
||||
|
||||
AZStd::vector<AZ::Entity*> EditorInfoRemover::GetEntitiesFromInstance(AZStd::unique_ptr<Instance>& instance)
|
||||
void EditorInfoRemover::GetEntitiesFromInstance(
|
||||
AZStd::unique_ptr<AzToolsFramework::Prefab::Instance>& instance, EntityList& hierarchyEntities)
|
||||
{
|
||||
AZStd::vector<AZ::Entity*> result;
|
||||
|
||||
instance->GetNestedEntities(
|
||||
[&result](const AZStd::unique_ptr<AZ::Entity>& entity)
|
||||
instance->GetAllEntitiesInHierarchy(
|
||||
[&hierarchyEntities](const AZStd::unique_ptr<AZ::Entity>& entity)
|
||||
{
|
||||
result.emplace_back(entity.get());
|
||||
hierarchyEntities.emplace_back(entity.get());
|
||||
return true;
|
||||
}
|
||||
);
|
||||
|
||||
if (instance->HasContainerEntity())
|
||||
{
|
||||
auto containerEntityReference = instance->GetContainerEntity();
|
||||
result.emplace_back(&containerEntityReference->get());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void EditorInfoRemover::SetEditorOnlyEntityHandlerFromCandidates(const EntityList& entities)
|
||||
@@ -543,7 +534,9 @@ exportComponent, prefabProcessorContext);
|
||||
}
|
||||
|
||||
// grab all nested entities from the Instance as source entities.
|
||||
EntityList sourceEntities = GetEntitiesFromInstance(instance);
|
||||
EntityList sourceEntities;
|
||||
GetEntitiesFromInstance(instance, sourceEntities);
|
||||
|
||||
EntityList exportEntities;
|
||||
|
||||
// prepare for validation of component requirements.
|
||||
@@ -616,7 +609,7 @@ exportComponent, prefabProcessorContext);
|
||||
);
|
||||
|
||||
// replace entities of instance with exported ones.
|
||||
instance->GetNestedEntities(
|
||||
instance->GetAllEntitiesInHierarchy(
|
||||
[&exportEntitiesMap](AZStd::unique_ptr<AZ::Entity>& entity)
|
||||
{
|
||||
auto entityId = entity->GetId();
|
||||
@@ -625,14 +618,6 @@ exportComponent, prefabProcessorContext);
|
||||
}
|
||||
);
|
||||
|
||||
if (instance->HasContainerEntity())
|
||||
{
|
||||
if (auto found = exportEntitiesMap.find(instance->GetContainerEntityId()); found != exportEntitiesMap.end())
|
||||
{
|
||||
instance->SetContainerEntity(*found->second);
|
||||
}
|
||||
}
|
||||
|
||||
// save the final result in the target Prefab DOM.
|
||||
PrefabDom filteredPrefab;
|
||||
if (!PrefabDomUtils::StoreInstanceInPrefabDom(*instance, filteredPrefab))
|
||||
|
||||
+2
-2
@@ -55,8 +55,8 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
|
||||
protected:
|
||||
using EntityList = AZStd::vector<AZ::Entity*>;
|
||||
static EntityList GetEntitiesFromInstance(
|
||||
AZStd::unique_ptr<AzToolsFramework::Prefab::Instance>& instance);
|
||||
static void GetEntitiesFromInstance(
|
||||
AZStd::unique_ptr<AzToolsFramework::Prefab::Instance>& instance, EntityList& hierarchyEntities);
|
||||
|
||||
static bool ReadComponentAttribute(
|
||||
AZ::Component* component,
|
||||
|
||||
+1
-5
@@ -38,11 +38,7 @@ namespace AzToolsFramework::Prefab::SpawnableUtils
|
||||
// going to be used to create clones of the entities.
|
||||
{
|
||||
AzFramework::Spawnable::EntityList& entities = spawnable.GetEntities();
|
||||
if (instance.HasContainerEntity())
|
||||
{
|
||||
entities.emplace_back(AZStd::move(instance.DetachContainerEntity()));
|
||||
}
|
||||
instance.DetachNestedEntities(
|
||||
instance.DetachAllEntitiesInHierarchy(
|
||||
[&entities](AZStd::unique_ptr<AZ::Entity> entity)
|
||||
{
|
||||
entities.emplace_back(AZStd::move(entity));
|
||||
|
||||
+1
-1
@@ -1206,7 +1206,7 @@ namespace AzToolsFramework
|
||||
Attribute(AZ::Edit::Attributes::SliceFlags, AZ::Edit::SliceFlags::NotPushableOnSliceRoot)->
|
||||
Attribute(AZ::Edit::Attributes::ReadOnly, &EditorTransform::m_locked)->
|
||||
DataElement(AZ::Edit::UIHandlers::Default, &EditorTransform::m_rotate, "Rotate", "Local Rotation (Relative to parent) in degrees.")->
|
||||
Attribute(AZ::Edit::Attributes::Step, 0.1f)->
|
||||
Attribute(AZ::Edit::Attributes::Step, 1.0f)->
|
||||
Attribute(AZ::Edit::Attributes::Suffix, " deg")->
|
||||
Attribute(AZ::Edit::Attributes::ReadOnly, &EditorTransform::m_locked)->
|
||||
Attribute(AZ::Edit::Attributes::SliceFlags, AZ::Edit::SliceFlags::NotPushableOnSliceRoot)->
|
||||
|
||||
@@ -58,8 +58,17 @@ namespace AzToolsFramework
|
||||
|
||||
if (!path.empty())
|
||||
{
|
||||
infoString =
|
||||
QObject::tr("<span style=\"font-style: italic; font-weight: 400;\">(%1)</span>").arg(path.Filename().Native().data());
|
||||
QString saveFlag = "";
|
||||
auto dirtyOutcome = m_prefabPublicInterface->HasUnsavedChanges(path);
|
||||
|
||||
if (dirtyOutcome.IsSuccess() && dirtyOutcome.GetValue() == true)
|
||||
{
|
||||
saveFlag = "*";
|
||||
}
|
||||
|
||||
infoString = QObject::tr("<span style=\"font-style: italic; font-weight: 400;\">(%1%2)</span>")
|
||||
.arg(path.Filename().Native().data())
|
||||
.arg(saveFlag);
|
||||
}
|
||||
|
||||
return infoString;
|
||||
|
||||
+3
-16
@@ -28,6 +28,7 @@
|
||||
#include <AzToolsFramework/ToolsComponents/EditorLayerComponentBus.h>
|
||||
#include <AzToolsFramework/UI/EditorEntityUi/EditorEntityUiInterface.h>
|
||||
#include <AzToolsFramework/UI/Prefab/PrefabIntegrationInterface.h>
|
||||
#include <AzToolsFramework/UI/UICore/WidgetHelpers.h>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QFileDialog>
|
||||
@@ -588,15 +589,6 @@ namespace AzToolsFramework
|
||||
|
||||
bool PrefabIntegrationManager::QueryUserForPrefabFilePath(AZStd::string& outPrefabFilePath)
|
||||
{
|
||||
QWidget* mainWindow = nullptr;
|
||||
EditorRequests::Bus::BroadcastResult(mainWindow, &EditorRequests::Bus::Events::GetMainWindow);
|
||||
|
||||
if (mainWindow == nullptr)
|
||||
{
|
||||
AZ_Assert(false, "Prefab - Could not detect Editor main window to generate the asset picker.");
|
||||
return false;
|
||||
}
|
||||
|
||||
AssetSelectionModel selection;
|
||||
|
||||
// Note, stringfilter will match every source file CONTAINING ".prefab".
|
||||
@@ -624,7 +616,7 @@ namespace AzToolsFramework
|
||||
selection.SetDisplayFilter(compositeFilterPtr);
|
||||
selection.SetSelectionFilter(compositeFilterPtr);
|
||||
|
||||
AssetBrowserComponentRequestBus::Broadcast(&AssetBrowserComponentRequests::PickAssets, selection, mainWindow);
|
||||
AssetBrowserComponentRequestBus::Broadcast(&AssetBrowserComponentRequests::PickAssets, selection, AzToolsFramework::GetActiveWindow());
|
||||
|
||||
if (!selection.IsValid())
|
||||
{
|
||||
@@ -983,12 +975,7 @@ namespace AzToolsFramework
|
||||
includedEntities.c_str(),
|
||||
referencedEntities.c_str());
|
||||
|
||||
QWidget* mainWindow = nullptr;
|
||||
AzToolsFramework::EditorRequests::Bus::BroadcastResult(
|
||||
mainWindow,
|
||||
&AzToolsFramework::EditorRequests::Bus::Events::GetMainWindow);
|
||||
|
||||
QMessageBox msgBox(mainWindow);
|
||||
QMessageBox msgBox(AzToolsFramework::GetActiveWindow());
|
||||
msgBox.setWindowTitle("External Entity References");
|
||||
msgBox.setText("The prefab contains references to external entities that are not selected.");
|
||||
msgBox.setInformativeText("You can move the referenced entities into this prefab or retain the external references.");
|
||||
|
||||
@@ -93,7 +93,7 @@ namespace UnitTest
|
||||
|
||||
// Retrieve the entity pointer from the component application bus.
|
||||
AZ::Entity* wheelEntityUnderAxle = nullptr;
|
||||
axleInstance->GetNestedEntities([&wheelEntityUnderAxle, wheelEntityIdUnderAxle](AZStd::unique_ptr<AZ::Entity>& entity)
|
||||
axleInstance->GetAllEntitiesInHierarchy([&wheelEntityUnderAxle, wheelEntityIdUnderAxle](AZStd::unique_ptr<AZ::Entity>& entity)
|
||||
{
|
||||
if (entity->GetId() == wheelEntityIdUnderAxle)
|
||||
{
|
||||
|
||||
@@ -71,24 +71,29 @@ namespace UnitTest
|
||||
m_prefabSystemComponent->CreatePrefab({ entitiesCreated[0] }, {}, "test/path1"));
|
||||
ASSERT_TRUE(firstInstance);
|
||||
|
||||
ASSERT_TRUE(firstInstance->HasContainerEntity());
|
||||
expectedEntityNameSet.insert(firstInstance->GetContainerEntity()->get().GetName());
|
||||
|
||||
AZStd::unique_ptr<AzToolsFramework::Prefab::Instance> secondInstance(
|
||||
m_prefabSystemComponent->CreatePrefab({ entitiesCreated[1] }, MakeInstanceList(AZStd::move(firstInstance)), "test/path2"));
|
||||
ASSERT_TRUE(secondInstance);
|
||||
|
||||
ASSERT_TRUE(secondInstance->HasContainerEntity());
|
||||
expectedEntityNameSet.insert(secondInstance->GetContainerEntity()->get().GetName());
|
||||
|
||||
AZStd::unique_ptr<AzToolsFramework::Prefab::Instance> thirdInstance(
|
||||
m_prefabSystemComponent->CreatePrefab({ entitiesCreated[2] }, MakeInstanceList(AZStd::move(secondInstance)), "test/path3"));
|
||||
ASSERT_TRUE(thirdInstance);
|
||||
|
||||
ASSERT_TRUE(thirdInstance->HasContainerEntity());
|
||||
auto& containerEntity = thirdInstance->GetContainerEntity()->get();
|
||||
expectedEntityNameSet.insert(containerEntity.GetName());
|
||||
expectedEntityNameSet.insert(thirdInstance->GetContainerEntity()->get().GetName());
|
||||
|
||||
//Create Spawnable
|
||||
auto& prefabDom = m_prefabSystemComponent->FindTemplateDom(thirdInstance->GetTemplateId());
|
||||
AzFramework::Spawnable spawnable;
|
||||
AzToolsFramework::Prefab::SpawnableUtils::CreateSpawnable(spawnable, prefabDom);
|
||||
|
||||
EXPECT_EQ(spawnable.GetEntities().size() - 1, normalEntityCount); // 1 for container entity
|
||||
EXPECT_EQ(spawnable.GetEntities().size(), normalEntityCount + 3); // +1 for each container entity
|
||||
const auto& spawnableEntities = spawnable.GetEntities();
|
||||
AZStd::unordered_set<AZStd::string> actualEntityNameSet;
|
||||
|
||||
@@ -97,6 +102,6 @@ namespace UnitTest
|
||||
actualEntityNameSet.insert(spawnableEntity->GetName());
|
||||
}
|
||||
|
||||
EXPECT_EQ(expectedEntityNameSet, actualEntityNameSet);
|
||||
EXPECT_EQ(actualEntityNameSet, expectedEntityNameSet);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -213,7 +213,7 @@ namespace UnitTest
|
||||
AZStd::unique_ptr<Instance> convertedInstance(aznew Instance());
|
||||
ASSERT_TRUE(AzToolsFramework::Prefab::PrefabDomUtils::LoadInstanceFromPrefabDom(*convertedInstance, m_prefabDom));
|
||||
|
||||
convertedInstance->DetachNestedEntities(
|
||||
convertedInstance->DetachAllEntitiesInHierarchy(
|
||||
[this](AZStd::unique_ptr<AZ::Entity> entity)
|
||||
{
|
||||
m_runtimeEntities.emplace_back(entity.release());
|
||||
|
||||
@@ -196,6 +196,16 @@ function(ly_delayed_generate_static_modules_inl)
|
||||
ly_get_gem_load_dependencies(all_game_gem_dependencies ${project_name}.GameLauncher)
|
||||
|
||||
foreach(game_gem_dependency ${all_game_gem_dependencies})
|
||||
# Sometimes, a gem's Client variant may be an interface library
|
||||
# which dependes on multiple gem targets. The interface libraries
|
||||
# should be skipped; the real dependencies of the interface will be processed
|
||||
if(TARGET ${game_gem_dependency})
|
||||
get_target_property(target_type ${game_gem_dependency} TYPE)
|
||||
if(${target_type} STREQUAL "INTERFACE_LIBRARY")
|
||||
continue()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# To match the convention on how gems targets vs gem modules are named,
|
||||
# we remove the ".Static" from the suffix
|
||||
# Replace "." with "_"
|
||||
@@ -224,6 +234,14 @@ function(ly_delayed_generate_static_modules_inl)
|
||||
list(APPEND all_server_gem_dependencies ${server_gem_load_dependencies} ${server_gem_dependency})
|
||||
endforeach()
|
||||
foreach(server_gem_dependency ${all_server_gem_dependencies})
|
||||
# Skip interface libraries
|
||||
if(TARGET ${server_gem_dependency})
|
||||
get_target_property(target_type ${server_gem_dependency} TYPE)
|
||||
if(${target_type} STREQUAL "INTERFACE_LIBRARY")
|
||||
continue()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Replace "." with "_"
|
||||
string(REPLACE "." "_" server_gem_dependency ${server_gem_dependency})
|
||||
|
||||
|
||||
@@ -715,11 +715,11 @@ QMenu* LevelEditorMenuHandler::CreateViewMenu()
|
||||
{
|
||||
return view.IsViewportPane();
|
||||
});
|
||||
#endif
|
||||
|
||||
viewportViewsMenuWrapper.AddAction(ID_WIREFRAME);
|
||||
viewportViewsMenuWrapper.AddSeparator();
|
||||
|
||||
#endif
|
||||
|
||||
if (CViewManager::IsMultiViewportEnabled())
|
||||
{
|
||||
viewportViewsMenuWrapper.AddAction(ID_VIEW_CONFIGURELAYOUT);
|
||||
|
||||
@@ -2891,7 +2891,7 @@ void CCryEditApp::OpenProjectManager(const AZStd::string& screen)
|
||||
{
|
||||
// provide the current project path for in case we want to update the project
|
||||
AZ::IO::FixedMaxPathString projectPath = AZ::Utils::GetProjectPath();
|
||||
const AZStd::string commandLineOptions = AZStd::string::format(" --screen %s --project_path %s", screen.c_str(), projectPath.c_str());
|
||||
const AZStd::string commandLineOptions = AZStd::string::format(" --screen %s --project-path %s", screen.c_str(), projectPath.c_str());
|
||||
bool launchSuccess = AzFramework::ProjectManager::LaunchProjectManager(commandLineOptions);
|
||||
if (!launchSuccess)
|
||||
{
|
||||
|
||||
@@ -1139,7 +1139,7 @@ bool CCryEditDoc::SaveLevel(const QString& filename)
|
||||
const QString oldLevelPattern = QDir(oldLevelFolder).absoluteFilePath("*.*");
|
||||
const QString oldLevelName = Path::GetFile(GetLevelPathName());
|
||||
const QString oldLevelXml = Path::ReplaceExtension(oldLevelName, "xml");
|
||||
AZ::IO::ArchiveFileIterator findHandle = pIPak->FindFirst(oldLevelPattern.toUtf8().data(), 0, true);
|
||||
AZ::IO::ArchiveFileIterator findHandle = pIPak->FindFirst(oldLevelPattern.toUtf8().data(), AZ::IO::IArchive::eFileSearchType_AllowOnDiskAndInZips);
|
||||
if (findHandle)
|
||||
{
|
||||
do
|
||||
|
||||
@@ -514,18 +514,28 @@ void EditorViewportWidget::Update()
|
||||
// Disable rendering to avoid recursion into Update()
|
||||
PushDisableRendering();
|
||||
|
||||
|
||||
//get debug display interface for the viewport
|
||||
AzFramework::DebugDisplayRequestBus::BusPtr debugDisplayBus;
|
||||
AzFramework::DebugDisplayRequestBus::Bind(debugDisplayBus, GetViewportId());
|
||||
AZ_Assert(debugDisplayBus, "Invalid DebugDisplayRequestBus.");
|
||||
|
||||
AzFramework::DebugDisplayRequests* debugDisplay =
|
||||
AzFramework::DebugDisplayRequestBus::FindFirstHandler(debugDisplayBus);
|
||||
|
||||
|
||||
// draw debug visualizations
|
||||
if (m_debugDisplay)
|
||||
if (debugDisplay)
|
||||
{
|
||||
const AZ::u32 prevState = m_debugDisplay->GetState();
|
||||
m_debugDisplay->SetState(
|
||||
const AZ::u32 prevState = debugDisplay->GetState();
|
||||
debugDisplay->SetState(
|
||||
e_Mode3D | e_AlphaBlended | e_FillModeSolid | e_CullModeBack | e_DepthWriteOn | e_DepthTestOn);
|
||||
|
||||
AzFramework::EntityDebugDisplayEventBus::Broadcast(
|
||||
&AzFramework::EntityDebugDisplayEvents::DisplayEntityViewport,
|
||||
AzFramework::ViewportInfo{ GetViewportId() }, *m_debugDisplay);
|
||||
AzFramework::ViewportInfo{ GetViewportId() }, *debugDisplay);
|
||||
|
||||
m_debugDisplay->SetState(prevState);
|
||||
debugDisplay->SetState(prevState);
|
||||
}
|
||||
|
||||
QtViewport::Update();
|
||||
@@ -2664,6 +2674,18 @@ bool EditorViewportWidget::GetActiveCameraPosition(AZ::Vector3& cameraPos)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool EditorViewportWidget::GetActiveCameraState(AzFramework::CameraState& cameraState)
|
||||
{
|
||||
if (m_pPrimaryViewport == this)
|
||||
{
|
||||
cameraState = GetCameraState();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void EditorViewportWidget::OnStartPlayInEditor()
|
||||
{
|
||||
if (m_viewEntityId.IsValid())
|
||||
|
||||
@@ -184,6 +184,7 @@ public:
|
||||
void SetViewAndMovementLockFromEntityPerspective(const AZ::EntityId& entityId, bool lockCameraMovement) override;
|
||||
AZ::EntityId GetCurrentViewEntityId() override { return m_viewEntityId; }
|
||||
bool GetActiveCameraPosition(AZ::Vector3& cameraPos) override;
|
||||
bool GetActiveCameraState(AzFramework::CameraState& cameraState) override;
|
||||
|
||||
// AzToolsFramework::EditorEntityContextNotificationBus (handler moved to cpp to resolve link issues in unity builds)
|
||||
virtual void OnStartPlayInEditor();
|
||||
|
||||
@@ -833,13 +833,6 @@ void MainWindow::InitActions()
|
||||
.Connect(&QAction::triggered, []() { SandboxEditor::SetAngleSnapping(!SandboxEditor::AngleSnappingEnabled()); });
|
||||
|
||||
// Display actions
|
||||
am->AddAction(ID_WIREFRAME, tr("&Wireframe"))
|
||||
.SetShortcut(tr("F3"))
|
||||
.SetToolTip(tr("Wireframe (F3)"))
|
||||
.SetCheckable(true)
|
||||
.SetStatusTip(tr("Render in Wireframe Mode."))
|
||||
.RegisterUpdateCallback(cryEdit, &CCryEditApp::OnUpdateWireframe);
|
||||
|
||||
am->AddAction(ID_SWITCHCAMERA_DEFAULTCAMERA, tr("Default Camera")).SetCheckable(true)
|
||||
.RegisterUpdateCallback(cryEdit, &CCryEditApp::OnUpdateSwitchToDefaultCamera);
|
||||
am->AddAction(ID_SWITCHCAMERA_SEQUENCECAMERA, tr("Sequence Camera")).SetCheckable(true)
|
||||
|
||||
@@ -115,10 +115,10 @@ protected:
|
||||
float m_prevMoveSpeed;
|
||||
|
||||
// Speed combobox/lineEdit settings
|
||||
double m_minSpeed = 0.1;
|
||||
double m_minSpeed = 0.01;
|
||||
double m_maxSpeed = 100.0;
|
||||
double m_speedStep = 0.1;
|
||||
int m_numDecimals = 1;
|
||||
double m_speedStep = 0.01;
|
||||
int m_numDecimals = 3;
|
||||
|
||||
// Speed presets
|
||||
float m_speedPresetValues[3] = { 0.1f, 1.0f, 10.0f };
|
||||
|
||||
@@ -154,11 +154,23 @@ SandboxIntegrationManager::SandboxIntegrationManager()
|
||||
{
|
||||
// Required to receive events from the Cry Engine undo system
|
||||
GetIEditor()->GetUndoManager()->AddListener(this);
|
||||
|
||||
// Only create the PrefabIntegrationManager if prefabs are enabled
|
||||
bool prefabSystemEnabled = false;
|
||||
AzFramework::ApplicationRequests::Bus::BroadcastResult(
|
||||
prefabSystemEnabled, &AzFramework::ApplicationRequests::IsPrefabSystemEnabled);
|
||||
if (prefabSystemEnabled)
|
||||
{
|
||||
m_prefabIntegrationManager = aznew AzToolsFramework::Prefab::PrefabIntegrationManager();
|
||||
}
|
||||
}
|
||||
|
||||
SandboxIntegrationManager::~SandboxIntegrationManager()
|
||||
{
|
||||
GetIEditor()->GetUndoManager()->RemoveListener(this);
|
||||
|
||||
delete m_prefabIntegrationManager;
|
||||
m_prefabIntegrationManager = nullptr;
|
||||
}
|
||||
|
||||
void SandboxIntegrationManager::Setup()
|
||||
@@ -187,11 +199,16 @@ void SandboxIntegrationManager::Setup()
|
||||
AZ_Assert((m_editorEntityUiInterface != nullptr),
|
||||
"SandboxIntegrationManager requires a EditorEntityUiInterface instance to be present on Setup().");
|
||||
|
||||
m_prefabIntegrationInterface = AZ::Interface<AzToolsFramework::Prefab::PrefabIntegrationInterface>::Get();
|
||||
|
||||
AZ_Assert(
|
||||
(m_prefabIntegrationInterface != nullptr),
|
||||
"SandboxIntegrationManager requires a PrefabIntegrationInterface instance to be present on Setup().");
|
||||
bool prefabSystemEnabled = false;
|
||||
AzFramework::ApplicationRequests::Bus::BroadcastResult(
|
||||
prefabSystemEnabled, &AzFramework::ApplicationRequests::IsPrefabSystemEnabled);
|
||||
if (prefabSystemEnabled)
|
||||
{
|
||||
m_prefabIntegrationInterface = AZ::Interface<AzToolsFramework::Prefab::PrefabIntegrationInterface>::Get();
|
||||
AZ_Assert(
|
||||
(m_prefabIntegrationInterface != nullptr),
|
||||
"SandboxIntegrationManager requires a PrefabIntegrationInterface instance to be present on Setup().");
|
||||
}
|
||||
|
||||
m_editorEntityAPI = AZ::Interface<AzToolsFramework::EditorEntityAPI>::Get();
|
||||
AZ_Assert(m_editorEntityAPI, "SandboxIntegrationManager requires an EditorEntityAPI instance to be present on Setup().");
|
||||
|
||||
@@ -305,7 +305,7 @@ private:
|
||||
|
||||
bool m_debugDisplayBusImplementationActive = false;
|
||||
|
||||
AzToolsFramework::Prefab::PrefabIntegrationManager m_prefabIntegrationManager;
|
||||
AzToolsFramework::Prefab::PrefabIntegrationManager* m_prefabIntegrationManager = nullptr;
|
||||
|
||||
AzToolsFramework::EditorEntityUiInterface* m_editorEntityUiInterface = nullptr;
|
||||
AzToolsFramework::Prefab::PrefabIntegrationInterface* m_prefabIntegrationInterface = nullptr;
|
||||
|
||||
@@ -19,6 +19,7 @@ set(FILES
|
||||
native/AssetManager/AssetRequestHandler.cpp
|
||||
native/AssetManager/AssetRequestHandler.h
|
||||
native/AssetManager/assetScanFolderInfo.h
|
||||
native/AssetManager/assetScanFolderInfo.cpp
|
||||
native/AssetManager/assetScanner.cpp
|
||||
native/AssetManager/assetScanner.h
|
||||
native/AssetManager/assetScannerWorker.cpp
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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 <native/AssetManager/assetScanFolderInfo.h>
|
||||
#include <native/utilities/assetUtils.h>
|
||||
|
||||
namespace AssetProcessor
|
||||
{
|
||||
ScanFolderInfo::ScanFolderInfo(
|
||||
QString path,
|
||||
QString displayName,
|
||||
QString portableKey,
|
||||
bool isRoot,
|
||||
bool recurseSubFolders,
|
||||
AZStd::vector<AssetBuilderSDK::PlatformInfo> platforms,
|
||||
int order,
|
||||
AZ::s64 scanFolderID,
|
||||
bool canSaveNewAssets)
|
||||
: m_scanPath(path)
|
||||
, m_displayName(displayName)
|
||||
, m_portableKey (portableKey)
|
||||
, m_isRoot(isRoot)
|
||||
, m_recurseSubFolders(recurseSubFolders)
|
||||
, m_order(order)
|
||||
, m_scanFolderID(scanFolderID)
|
||||
, m_platforms(platforms)
|
||||
, m_canSaveNewAssets(canSaveNewAssets)
|
||||
{
|
||||
m_scanPath = AssetUtilities::NormalizeFilePath(m_scanPath);
|
||||
// note that m_scanFolderID is 0 unless its filled in from the DB.
|
||||
}
|
||||
|
||||
} // end namespace AssetProcessor
|
||||
@@ -33,19 +33,7 @@ namespace AssetProcessor
|
||||
AZStd::vector<AssetBuilderSDK::PlatformInfo> platforms = AZStd::vector<AssetBuilderSDK::PlatformInfo>{},
|
||||
int order = 0,
|
||||
AZ::s64 scanFolderID = 0,
|
||||
bool canSaveNewAssets = false)
|
||||
: m_scanPath(path)
|
||||
, m_displayName(displayName)
|
||||
, m_portableKey (portableKey)
|
||||
, m_isRoot(isRoot)
|
||||
, m_recurseSubFolders(recurseSubFolders)
|
||||
, m_order(order)
|
||||
, m_scanFolderID(scanFolderID)
|
||||
, m_platforms(platforms)
|
||||
, m_canSaveNewAssets(canSaveNewAssets)
|
||||
{
|
||||
// note that m_scanFolderID is 0 unless its filled in from the DB.
|
||||
}
|
||||
bool canSaveNewAssets = false);
|
||||
|
||||
ScanFolderInfo() = default;
|
||||
ScanFolderInfo(const ScanFolderInfo& other) = default;
|
||||
|
||||
@@ -13,7 +13,7 @@ ly_get_list_relative_pal_filename(pal_dir ${CMAKE_CURRENT_LIST_DIR}/Platform/${P
|
||||
|
||||
include(${pal_dir}/platform_traits_${PAL_PLATFORM_NAME_LOWERCASE}.cmake)
|
||||
|
||||
if(PAL_TRAIT_AZTESTRUNNER_SUPPORTED)
|
||||
if(PAL_TRAIT_AZTESTRUNNER_SUPPORTED AND NOT LY_MONOLITHIC_GAME)
|
||||
|
||||
ly_add_target(
|
||||
NAME AzTestRunner ${PAL_TRAIT_AZTESTRUNNER_LAUNCHER_TYPE}
|
||||
|
||||
@@ -20,12 +20,11 @@ if (NOT python_package_name)
|
||||
message(WARNING "Python was not found in the package assocation list. Did someone call ly_associate_package(xxxxxxx Python) ?")
|
||||
endif()
|
||||
|
||||
|
||||
ly_add_target(
|
||||
NAME ProjectManager APPLICATION
|
||||
OUTPUT_NAME o3de
|
||||
NAME ProjectManager.Static STATIC
|
||||
NAMESPACE AZ
|
||||
AUTOMOC
|
||||
AUTORCC
|
||||
FILES_CMAKE
|
||||
project_manager_files.cmake
|
||||
Platform/${PAL_PLATFORM_NAME}/PAL_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake
|
||||
@@ -47,6 +46,60 @@ ly_add_target(
|
||||
3rdParty::pybind11
|
||||
AZ::AzCore
|
||||
AZ::AzFramework
|
||||
AZ::AzToolsFramework
|
||||
AZ::AzQtComponents
|
||||
)
|
||||
)
|
||||
|
||||
ly_add_target(
|
||||
NAME ProjectManager APPLICATION
|
||||
OUTPUT_NAME o3de
|
||||
NAMESPACE AZ
|
||||
AUTORCC
|
||||
FILES_CMAKE
|
||||
project_manager_app_files.cmake
|
||||
INCLUDE_DIRECTORIES
|
||||
PRIVATE
|
||||
Source
|
||||
BUILD_DEPENDENCIES
|
||||
PRIVATE
|
||||
3rdParty::Qt::Core
|
||||
3rdParty::Qt::Concurrent
|
||||
3rdParty::Qt::Widgets
|
||||
3rdParty::Python
|
||||
3rdParty::pybind11
|
||||
AZ::AzCore
|
||||
AZ::AzFramework
|
||||
AZ::AzQtComponents
|
||||
AZ::ProjectManager.Static
|
||||
)
|
||||
|
||||
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
|
||||
ly_add_target(
|
||||
NAME ProjectManager.Tests EXECUTABLE
|
||||
NAMESPACE AZ
|
||||
AUTORCC
|
||||
FILES_CMAKE
|
||||
project_manager_tests_files.cmake
|
||||
Platform/${PAL_PLATFORM_NAME}/PAL_${PAL_PLATFORM_NAME_LOWERCASE}_tests_files.cmake
|
||||
INCLUDE_DIRECTORIES
|
||||
PRIVATE
|
||||
Source
|
||||
Platform/${PAL_PLATFORM_NAME}
|
||||
BUILD_DEPENDENCIES
|
||||
PRIVATE
|
||||
3rdParty::Qt::Core
|
||||
3rdParty::Qt::Concurrent
|
||||
3rdParty::Qt::Widgets
|
||||
3rdParty::Python
|
||||
3rdParty::pybind11
|
||||
AZ::AzTest
|
||||
AZ::AzFramework
|
||||
AZ::AzFrameworkTestShared
|
||||
AZ::ProjectManager.Static
|
||||
)
|
||||
|
||||
ly_add_googletest(
|
||||
NAME AZ::ProjectManager.Tests
|
||||
TEST_COMMAND $<TARGET_FILE:AZ::ProjectManager.Tests> --unittest
|
||||
)
|
||||
|
||||
endif()
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
set(FILES
|
||||
ProjectManager_Test_Traits_Platform.h
|
||||
ProjectManager_Test_Traits_Linux.h
|
||||
)
|
||||
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
#define AZ_TRAIT_DISABLE_FAILED_PROJECT_MANAGER_TESTS true
|
||||
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* 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 <ProjectManager_Test_Traits_Linux.h>
|
||||
@@ -0,0 +1,15 @@
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
set(FILES
|
||||
ProjectManager_Test_Traits_Platform.h
|
||||
ProjectManager_Test_Traits_Mac.h
|
||||
)
|
||||
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
#define AZ_TRAIT_DISABLE_FAILED_PROJECT_MANAGER_TESTS false
|
||||
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* 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 <ProjectManager_Test_Traits_Mac.h>
|
||||
@@ -0,0 +1,15 @@
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
set(FILES
|
||||
ProjectManager_Test_Traits_Platform.h
|
||||
ProjectManager_Test_Traits_Windows.h
|
||||
)
|
||||
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* 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 <ProjectManager_Test_Traits_Windows.h>
|
||||
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
#define AZ_TRAIT_DISABLE_FAILED_PROJECT_MANAGER_TESTS false
|
||||
@@ -7,6 +7,11 @@ QMainWindow {
|
||||
margin:0;
|
||||
}
|
||||
|
||||
#ScreensCtrl {
|
||||
min-width:1200px;
|
||||
min-height:800px;
|
||||
}
|
||||
|
||||
QPushButton:focus {
|
||||
outline: none;
|
||||
border:1px solid #1e70eb;
|
||||
|
||||
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* 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 <Application.h>
|
||||
#include <ProjectUtils.h>
|
||||
|
||||
#include <AzCore/IO/FileIO.h>
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
|
||||
#include <AzFramework/Logging/LoggingComponent.h>
|
||||
#include <AzQtComponents/Utilities/HandleDpiAwareness.h>
|
||||
#include <AzQtComponents/Components/StyleManager.h>
|
||||
#include <AzQtComponents/Components/WindowDecorationWrapper.h>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QDir>
|
||||
#include <QMessageBox>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
Application::~Application()
|
||||
{
|
||||
TearDown();
|
||||
}
|
||||
|
||||
bool Application::Init(bool interactive)
|
||||
{
|
||||
constexpr const char* applicationName { "O3DE" };
|
||||
|
||||
QApplication::setOrganizationName(applicationName);
|
||||
QApplication::setOrganizationDomain("o3de.org");
|
||||
|
||||
QCoreApplication::setApplicationName(applicationName);
|
||||
QCoreApplication::setApplicationVersion("1.0");
|
||||
|
||||
// Use the LogComponent for non-dev logging log
|
||||
RegisterComponentDescriptor(AzFramework::LogComponent::CreateDescriptor());
|
||||
|
||||
// set the log alias to .o3de/Logs instead of the default user/logs
|
||||
AZ::IO::FixedMaxPath path = AZ::Utils::GetO3deLogsDirectory();
|
||||
|
||||
// DevWriteStorage is where the event log is written during development
|
||||
m_settingsRegistry->Set(AZ::SettingsRegistryMergeUtils::FilePathKey_DevWriteStorage, path.LexicallyNormal().Native());
|
||||
|
||||
// Save event logs to .o3de/Logs/eventlogger/EventLogO3DE.azsl
|
||||
m_settingsRegistry->Set(AZ::SettingsRegistryMergeUtils::BuildTargetNameKey, applicationName);
|
||||
|
||||
Start(AzFramework::Application::Descriptor());
|
||||
|
||||
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
|
||||
QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
|
||||
QCoreApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
|
||||
|
||||
QLocale::setDefault(QLocale(QLocale::English, QLocale::UnitedStates));
|
||||
|
||||
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
|
||||
AzQtComponents::Utilities::HandleDpiAwareness(AzQtComponents::Utilities::SystemDpiAware);
|
||||
|
||||
// Create the actual Qt Application - this needs to happen before using QMessageBox
|
||||
m_app.reset(new QApplication(*GetArgC(), *GetArgV()));
|
||||
|
||||
if(!InitLog(applicationName))
|
||||
{
|
||||
AZ_Warning("ProjectManager", false, "Failed to init logging");
|
||||
}
|
||||
|
||||
m_pythonBindings = AZStd::make_unique<PythonBindings>(GetEngineRoot());
|
||||
if (!m_pythonBindings || !m_pythonBindings->PythonStarted())
|
||||
{
|
||||
if (interactive)
|
||||
{
|
||||
QMessageBox::critical(nullptr, QObject::tr("Failed to start Python"),
|
||||
QObject::tr("This tool requires an O3DE engine with a Python runtime, "
|
||||
"but either Python is missing or mis-configured. Please rename "
|
||||
"your python/runtime folder to python/runtime_bak, then run "
|
||||
"python/get_python.bat to restore the Python runtime folder."));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const AZ::CommandLine* commandLine = GetCommandLine();
|
||||
AZ_Assert(commandLine, "Failed to get command line");
|
||||
|
||||
ProjectManagerScreen startScreen = ProjectManagerScreen::Projects;
|
||||
if (size_t screenSwitchCount = commandLine->GetNumSwitchValues("screen"); screenSwitchCount > 0)
|
||||
{
|
||||
QString screenOption = commandLine->GetSwitchValue("screen", screenSwitchCount - 1).c_str();
|
||||
ProjectManagerScreen screen = ProjectUtils::GetProjectManagerScreen(screenOption);
|
||||
if (screen != ProjectManagerScreen::Invalid)
|
||||
{
|
||||
startScreen = screen;
|
||||
}
|
||||
}
|
||||
|
||||
AZ::IO::FixedMaxPath projectPath;
|
||||
if (size_t projectSwitchCount = commandLine->GetNumSwitchValues("project-path"); projectSwitchCount > 0)
|
||||
{
|
||||
projectPath = commandLine->GetSwitchValue("project-path", projectSwitchCount - 1).c_str();
|
||||
}
|
||||
|
||||
m_mainWindow.reset(new ProjectManagerWindow(nullptr, projectPath, startScreen));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Application::InitLog(const char* logName)
|
||||
{
|
||||
if (!m_entity)
|
||||
{
|
||||
// override the log alias to the O3de Logs directory instead of the default project user/Logs folder
|
||||
AZ::IO::FixedMaxPath path = AZ::Utils::GetO3deLogsDirectory();
|
||||
AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance();
|
||||
AZ_Assert(fileIO, "Failed to get FileIOBase instance");
|
||||
|
||||
fileIO->SetAlias("@log@", path.LexicallyNormal().Native().c_str());
|
||||
|
||||
// this entity exists because we need a home for LogComponent
|
||||
// and cannot use the system entity because we need to be able to call SetLogFileBaseName
|
||||
// so the log will be named O3DE.log
|
||||
m_entity = aznew AZ::Entity("Application Entity");
|
||||
if (m_entity)
|
||||
{
|
||||
AzFramework::LogComponent* logger = aznew AzFramework::LogComponent();
|
||||
AZ_Assert(logger, "Failed to create LogComponent");
|
||||
logger->SetLogFileBaseName(logName);
|
||||
m_entity->AddComponent(logger);
|
||||
m_entity->Init();
|
||||
m_entity->Activate();
|
||||
}
|
||||
}
|
||||
|
||||
return m_entity != nullptr;
|
||||
}
|
||||
|
||||
void Application::TearDown()
|
||||
{
|
||||
if (m_entity)
|
||||
{
|
||||
m_entity->Deactivate();
|
||||
delete m_entity;
|
||||
m_entity = nullptr;
|
||||
}
|
||||
|
||||
m_pythonBindings.reset();
|
||||
m_mainWindow.reset();
|
||||
m_app.reset();
|
||||
}
|
||||
|
||||
bool Application::Run()
|
||||
{
|
||||
// Set up the Style Manager
|
||||
AzQtComponents::StyleManager styleManager(qApp);
|
||||
styleManager.initialize(qApp, GetEngineRoot());
|
||||
|
||||
// setup stylesheets and hot reloading
|
||||
AZ::IO::FixedMaxPath engineRoot(GetEngineRoot());
|
||||
QDir rootDir(engineRoot.c_str());
|
||||
const auto pathOnDisk = rootDir.absoluteFilePath("Code/Tools/ProjectManager/Resources");
|
||||
const auto qrcPath = QStringLiteral(":/ProjectManager/style");
|
||||
AzQtComponents::StyleManager::addSearchPaths("style", pathOnDisk, qrcPath, engineRoot);
|
||||
|
||||
// set stylesheet after creating the main window or their styles won't get updated
|
||||
AzQtComponents::StyleManager::setStyleSheet(m_mainWindow.data(), QStringLiteral("style:ProjectManager.qss"));
|
||||
|
||||
// the decoration wrapper is intended to remember window positioning and sizing
|
||||
auto wrapper = new AzQtComponents::WindowDecorationWrapper();
|
||||
wrapper->setGuest(m_mainWindow.data());
|
||||
wrapper->show();
|
||||
m_mainWindow->show();
|
||||
|
||||
qApp->setQuitOnLastWindowClosed(true);
|
||||
|
||||
// Run the application
|
||||
return qApp->exec();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
#if !defined(Q_MOC_RUN)
|
||||
#include <AzFramework/Application/Application.h>
|
||||
#include <QCoreApplication>
|
||||
#include <PythonBindings.h>
|
||||
#include <ProjectManagerWindow.h>
|
||||
#endif
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
class Entity;
|
||||
}
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
class Application
|
||||
: public AzFramework::Application
|
||||
{
|
||||
public:
|
||||
using AzFramework::Application::Application;
|
||||
virtual ~Application();
|
||||
|
||||
bool Init(bool interactive = true);
|
||||
bool Run();
|
||||
void TearDown();
|
||||
|
||||
private:
|
||||
bool InitLog(const char* logName);
|
||||
|
||||
AZStd::unique_ptr<PythonBindings> m_pythonBindings;
|
||||
QSharedPointer<QCoreApplication> m_app;
|
||||
QSharedPointer<ProjectManagerWindow> m_mainWindow;
|
||||
|
||||
AZ::Entity* m_entity = nullptr;
|
||||
};
|
||||
}
|
||||
@@ -25,7 +25,7 @@ namespace O3DE::ProjectManager
|
||||
EngineSettingsScreen::EngineSettingsScreen(QWidget* parent)
|
||||
: ScreenWidget(parent)
|
||||
{
|
||||
auto* layout = new QVBoxLayout(this);
|
||||
auto* layout = new QVBoxLayout();
|
||||
layout->setAlignment(Qt::AlignTop);
|
||||
|
||||
setObjectName("engineSettingsScreen");
|
||||
|
||||
@@ -82,6 +82,8 @@ namespace O3DE::ProjectManager
|
||||
|
||||
m_headerWidget->ReinitForProject();
|
||||
|
||||
connect(m_gemModel, &GemModel::dataChanged, m_filterWidget, &GemFilterWidget::ResetGemStatusFilter);
|
||||
|
||||
// Select the first entry after everything got correctly sized
|
||||
QTimer::singleShot(200, [=]{
|
||||
QModelIndex firstModelIndex = m_gemListView->model()->index(0,0);
|
||||
|
||||
@@ -26,6 +26,7 @@ namespace O3DE::ProjectManager
|
||||
const QVector<QString>& elementNames,
|
||||
const QVector<int>& elementCounts,
|
||||
bool showAllLessButton,
|
||||
bool collapsed,
|
||||
int defaultShowCount,
|
||||
QWidget* parent)
|
||||
: QWidget(parent)
|
||||
@@ -40,6 +41,7 @@ namespace O3DE::ProjectManager
|
||||
QHBoxLayout* collapseLayout = new QHBoxLayout();
|
||||
m_collapseButton = new QPushButton();
|
||||
m_collapseButton->setCheckable(true);
|
||||
m_collapseButton->setChecked(collapsed);
|
||||
m_collapseButton->setFlat(true);
|
||||
m_collapseButton->setFocusPolicy(Qt::NoFocus);
|
||||
m_collapseButton->setFixedWidth(s_collapseButtonSize);
|
||||
@@ -178,6 +180,11 @@ namespace O3DE::ProjectManager
|
||||
return m_buttonGroup;
|
||||
}
|
||||
|
||||
bool FilterCategoryWidget::IsCollapsed()
|
||||
{
|
||||
return m_collapseButton->isChecked();
|
||||
}
|
||||
|
||||
GemFilterWidget::GemFilterWidget(GemSortFilterProxyModel* filterProxyModel, QWidget* parent)
|
||||
: QScrollArea(parent)
|
||||
, m_filterProxyModel(filterProxyModel)
|
||||
@@ -193,20 +200,106 @@ namespace O3DE::ProjectManager
|
||||
QWidget* mainWidget = new QWidget();
|
||||
setWidget(mainWidget);
|
||||
|
||||
m_mainLayout = new QVBoxLayout();
|
||||
m_mainLayout->setAlignment(Qt::AlignTop);
|
||||
mainWidget->setLayout(m_mainLayout);
|
||||
QVBoxLayout* mainLayout = new QVBoxLayout();
|
||||
mainLayout->setAlignment(Qt::AlignTop);
|
||||
mainWidget->setLayout(mainLayout);
|
||||
|
||||
QLabel* filterByLabel = new QLabel("Filter by");
|
||||
filterByLabel->setStyleSheet("font-size: 16px;");
|
||||
m_mainLayout->addWidget(filterByLabel);
|
||||
mainLayout->addWidget(filterByLabel);
|
||||
|
||||
QWidget* filterSection = new QWidget(this);
|
||||
mainLayout->addWidget(filterSection);
|
||||
|
||||
m_filterLayout = new QVBoxLayout();
|
||||
m_filterLayout->setAlignment(Qt::AlignTop);
|
||||
m_filterLayout->setContentsMargins(0, 0, 0, 0);
|
||||
filterSection->setLayout(m_filterLayout);
|
||||
|
||||
ResetGemStatusFilter();
|
||||
AddGemOriginFilter();
|
||||
AddTypeFilter();
|
||||
AddPlatformFilter();
|
||||
AddFeatureFilter();
|
||||
}
|
||||
|
||||
void GemFilterWidget::ResetGemStatusFilter()
|
||||
{
|
||||
QVector<QString> elementNames;
|
||||
QVector<int> elementCounts;
|
||||
const int totalGems = m_gemModel->rowCount();
|
||||
const int selectedGemTotal = m_gemModel->TotalAddedGems();
|
||||
|
||||
elementNames.push_back(GemSortFilterProxyModel::GetGemStatusString(GemSortFilterProxyModel::GemStatus::Unselected));
|
||||
elementCounts.push_back(totalGems - selectedGemTotal);
|
||||
|
||||
elementNames.push_back(GemSortFilterProxyModel::GetGemStatusString(GemSortFilterProxyModel::GemStatus::Selected));
|
||||
elementCounts.push_back(selectedGemTotal);
|
||||
|
||||
bool wasCollapsed = false;
|
||||
if (m_statusFilter)
|
||||
{
|
||||
wasCollapsed = m_statusFilter->IsCollapsed();
|
||||
}
|
||||
|
||||
FilterCategoryWidget* filterWidget =
|
||||
new FilterCategoryWidget("Status", elementNames, elementCounts, /*showAllLessButton=*/false, /*collapsed*/wasCollapsed);
|
||||
if (m_statusFilter)
|
||||
{
|
||||
m_filterLayout->replaceWidget(m_statusFilter, filterWidget);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_filterLayout->addWidget(filterWidget);
|
||||
}
|
||||
|
||||
m_statusFilter->deleteLater();
|
||||
m_statusFilter = filterWidget;
|
||||
|
||||
const GemSortFilterProxyModel::GemStatus currentFilterState = m_filterProxyModel->GetGemStatus();
|
||||
const QList<QAbstractButton*> buttons = m_statusFilter->GetButtonGroup()->buttons();
|
||||
for (int statusFilterIndex = 0; statusFilterIndex < buttons.size(); ++statusFilterIndex)
|
||||
{
|
||||
const GemSortFilterProxyModel::GemStatus gemStatus = static_cast<GemSortFilterProxyModel::GemStatus>(statusFilterIndex);
|
||||
QAbstractButton* button = buttons[statusFilterIndex];
|
||||
|
||||
if (static_cast<GemSortFilterProxyModel::GemStatus>(statusFilterIndex) == currentFilterState)
|
||||
{
|
||||
button->setChecked(true);
|
||||
}
|
||||
|
||||
connect(
|
||||
button, &QAbstractButton::toggled, this,
|
||||
[=](bool checked)
|
||||
{
|
||||
GemSortFilterProxyModel::GemStatus filterStatus = m_filterProxyModel->GetGemStatus();
|
||||
if (checked)
|
||||
{
|
||||
if (filterStatus == GemSortFilterProxyModel::GemStatus::NoFilter)
|
||||
{
|
||||
filterStatus = gemStatus;
|
||||
}
|
||||
else
|
||||
{
|
||||
filterStatus = GemSortFilterProxyModel::GemStatus::NoFilter;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (filterStatus != gemStatus)
|
||||
{
|
||||
filterStatus = static_cast<GemSortFilterProxyModel::GemStatus>(!gemStatus);
|
||||
}
|
||||
else
|
||||
{
|
||||
filterStatus = GemSortFilterProxyModel::GemStatus::NoFilter;
|
||||
}
|
||||
}
|
||||
m_filterProxyModel->SetGemStatus(filterStatus);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void GemFilterWidget::AddGemOriginFilter()
|
||||
{
|
||||
QVector<QString> elementNames;
|
||||
@@ -233,7 +326,7 @@ namespace O3DE::ProjectManager
|
||||
}
|
||||
|
||||
FilterCategoryWidget* filterWidget = new FilterCategoryWidget("Provider", elementNames, elementCounts, /*showAllLessButton=*/false);
|
||||
m_mainLayout->addWidget(filterWidget);
|
||||
m_filterLayout->addWidget(filterWidget);
|
||||
|
||||
const QList<QAbstractButton*> buttons = filterWidget->GetButtonGroup()->buttons();
|
||||
for (int i = 0; i < buttons.size(); ++i)
|
||||
@@ -283,7 +376,7 @@ namespace O3DE::ProjectManager
|
||||
}
|
||||
|
||||
FilterCategoryWidget* filterWidget = new FilterCategoryWidget("Type", elementNames, elementCounts, /*showAllLessButton=*/false);
|
||||
m_mainLayout->addWidget(filterWidget);
|
||||
m_filterLayout->addWidget(filterWidget);
|
||||
|
||||
const QList<QAbstractButton*> buttons = filterWidget->GetButtonGroup()->buttons();
|
||||
for (int i = 0; i < buttons.size(); ++i)
|
||||
@@ -333,7 +426,7 @@ namespace O3DE::ProjectManager
|
||||
}
|
||||
|
||||
FilterCategoryWidget* filterWidget = new FilterCategoryWidget("Supported Platforms", elementNames, elementCounts, /*showAllLessButton=*/false);
|
||||
m_mainLayout->addWidget(filterWidget);
|
||||
m_filterLayout->addWidget(filterWidget);
|
||||
|
||||
const QList<QAbstractButton*> buttons = filterWidget->GetButtonGroup()->buttons();
|
||||
for (int i = 0; i < buttons.size(); ++i)
|
||||
@@ -388,8 +481,8 @@ namespace O3DE::ProjectManager
|
||||
}
|
||||
|
||||
FilterCategoryWidget* filterWidget = new FilterCategoryWidget("Features", elementNames, elementCounts,
|
||||
/*showAllLessButton=*/true, /*defaultShowCount=*/5);
|
||||
m_mainLayout->addWidget(filterWidget);
|
||||
/*showAllLessButton=*/true, false, /*defaultShowCount=*/5);
|
||||
m_filterLayout->addWidget(filterWidget);
|
||||
|
||||
const QList<QAbstractButton*> buttons = filterWidget->GetButtonGroup()->buttons();
|
||||
for (int i = 0; i < buttons.size(); ++i)
|
||||
|
||||
@@ -37,11 +37,14 @@ namespace O3DE::ProjectManager
|
||||
const QVector<QString>& elementNames,
|
||||
const QVector<int>& elementCounts,
|
||||
bool showAllLessButton = true,
|
||||
bool collapsed = false,
|
||||
int defaultShowCount = 4,
|
||||
QWidget* parent = nullptr);
|
||||
|
||||
QButtonGroup* GetButtonGroup();
|
||||
|
||||
bool IsCollapsed();
|
||||
|
||||
private:
|
||||
void UpdateCollapseState();
|
||||
void UpdateSeeMoreLess();
|
||||
@@ -66,14 +69,18 @@ namespace O3DE::ProjectManager
|
||||
explicit GemFilterWidget(GemSortFilterProxyModel* filterProxyModel, QWidget* parent = nullptr);
|
||||
~GemFilterWidget() = default;
|
||||
|
||||
public slots:
|
||||
void ResetGemStatusFilter();
|
||||
|
||||
private:
|
||||
void AddGemOriginFilter();
|
||||
void AddTypeFilter();
|
||||
void AddPlatformFilter();
|
||||
void AddFeatureFilter();
|
||||
|
||||
QVBoxLayout* m_mainLayout = nullptr;
|
||||
QVBoxLayout* m_filterLayout = nullptr;
|
||||
GemModel* m_gemModel = nullptr;
|
||||
GemSortFilterProxyModel* m_filterProxyModel = nullptr;
|
||||
FilterCategoryWidget* m_statusFilter = nullptr;
|
||||
};
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -204,7 +204,6 @@ namespace O3DE::ProjectManager
|
||||
painter->save();
|
||||
const QRect buttonRect = CalcButtonRect(contentRect);
|
||||
QPoint circleCenter;
|
||||
QString buttonText;
|
||||
|
||||
const bool isAdded = GemModel::IsAdded(modelIndex);
|
||||
if (isAdded)
|
||||
@@ -213,34 +212,15 @@ namespace O3DE::ProjectManager
|
||||
painter->setPen(m_buttonEnabledColor);
|
||||
|
||||
circleCenter = buttonRect.center() + QPoint(buttonRect.width() / 2 - s_buttonBorderRadius + 1, 1);
|
||||
buttonText = "Added";
|
||||
}
|
||||
else
|
||||
{
|
||||
circleCenter = buttonRect.center() + QPoint(-buttonRect.width() / 2 + s_buttonBorderRadius, 1);
|
||||
buttonText = "Get";
|
||||
}
|
||||
|
||||
// Rounded rect
|
||||
painter->drawRoundedRect(buttonRect, s_buttonBorderRadius, s_buttonBorderRadius);
|
||||
|
||||
// Text
|
||||
QFont font;
|
||||
QRect textRect = GetTextRect(font, buttonText, s_buttonFontSize);
|
||||
if (isAdded)
|
||||
{
|
||||
textRect = QRect(buttonRect.left(), buttonRect.top(), buttonRect.width() - s_buttonCircleRadius * 2.0, buttonRect.height());
|
||||
}
|
||||
else
|
||||
{
|
||||
textRect = QRect(buttonRect.left() + s_buttonCircleRadius * 2.0, buttonRect.top(), buttonRect.width() - s_buttonCircleRadius * 2.0, buttonRect.height());
|
||||
}
|
||||
|
||||
font.setPixelSize(s_buttonFontSize);
|
||||
painter->setFont(font);
|
||||
painter->setPen(m_textColor);
|
||||
painter->drawText(textRect, Qt::AlignCenter, buttonText);
|
||||
|
||||
// Circle
|
||||
painter->setBrush(m_textColor);
|
||||
painter->drawEllipse(circleCenter, s_buttonCircleRadius, s_buttonCircleRadius);
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <QStandardItemModel>
|
||||
#include <QLabel>
|
||||
#include <QVBoxLayout>
|
||||
#include <QSpacerItem>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
@@ -74,6 +75,15 @@ namespace O3DE::ProjectManager
|
||||
gemSummaryLabel->setStyleSheet("font-size: 12px;");
|
||||
columnHeaderLayout->addWidget(gemSummaryLabel);
|
||||
|
||||
QSpacerItem* horizontalSpacer = new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum);
|
||||
columnHeaderLayout->addSpacerItem(horizontalSpacer);
|
||||
|
||||
QLabel* gemSelectedLabel = new QLabel(tr("Selected"));
|
||||
gemSelectedLabel->setStyleSheet("font-size: 12px;");
|
||||
columnHeaderLayout->addWidget(gemSelectedLabel);
|
||||
|
||||
columnHeaderLayout->addSpacing(60);
|
||||
|
||||
vLayout->addLayout(columnHeaderLayout);
|
||||
}
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -235,4 +235,19 @@ namespace O3DE::ProjectManager
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int GemModel::TotalAddedGems() const
|
||||
{
|
||||
int result = 0;
|
||||
for (int row = 0; row < rowCount(); ++row)
|
||||
{
|
||||
const QModelIndex modelIndex = index(row, 0);
|
||||
if (IsAdded(modelIndex))
|
||||
{
|
||||
++result;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -63,6 +63,8 @@ namespace O3DE::ProjectManager
|
||||
QVector<QModelIndex> GatherGemsToBeAdded() const;
|
||||
QVector<QModelIndex> GatherGemsToBeRemoved() const;
|
||||
|
||||
int TotalAddedGems() const;
|
||||
|
||||
private:
|
||||
enum UserRole
|
||||
{
|
||||
|
||||
@@ -37,6 +37,16 @@ namespace O3DE::ProjectManager
|
||||
return false;
|
||||
}
|
||||
|
||||
// Gem status
|
||||
if (m_gemStatusFilter != GemStatus::NoFilter)
|
||||
{
|
||||
const GemStatus sourceGemStatus = static_cast<GemStatus>(GemModel::IsAdded(sourceIndex));
|
||||
if (m_gemStatusFilter != sourceGemStatus)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Gem origins
|
||||
if (m_gemOriginFilter)
|
||||
{
|
||||
@@ -125,6 +135,19 @@ namespace O3DE::ProjectManager
|
||||
return true;
|
||||
}
|
||||
|
||||
QString GemSortFilterProxyModel::GetGemStatusString(GemStatus status)
|
||||
{
|
||||
switch (status)
|
||||
{
|
||||
case Unselected:
|
||||
return "Unselected";
|
||||
case Selected:
|
||||
return "Selected";
|
||||
default:
|
||||
return "<Unknown Gem Status>";
|
||||
}
|
||||
}
|
||||
|
||||
void GemSortFilterProxyModel::InvalidateFilter()
|
||||
{
|
||||
invalidate();
|
||||
|
||||
@@ -29,8 +29,17 @@ namespace O3DE::ProjectManager
|
||||
Q_OBJECT // AUTOMOC
|
||||
|
||||
public:
|
||||
enum GemStatus
|
||||
{
|
||||
NoFilter = -1,
|
||||
Unselected,
|
||||
Selected
|
||||
};
|
||||
|
||||
GemSortFilterProxyModel(GemModel* sourceModel, QObject* parent = nullptr);
|
||||
|
||||
static QString GetGemStatusString(GemStatus status);
|
||||
|
||||
bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const override;
|
||||
|
||||
GemModel* GetSourceModel() const { return m_sourceModel; }
|
||||
@@ -38,6 +47,9 @@ namespace O3DE::ProjectManager
|
||||
|
||||
void SetSearchString(const QString& searchString) { m_searchString = searchString; InvalidateFilter(); }
|
||||
|
||||
GemStatus GetGemStatus() const { return m_gemStatusFilter; }
|
||||
void SetGemStatus(GemStatus gemStatus) { m_gemStatusFilter = gemStatus; InvalidateFilter(); }
|
||||
|
||||
GemInfo::GemOrigins GetGemOrigins() const { return m_gemOriginFilter; }
|
||||
void SetGemOrigins(const GemInfo::GemOrigins& gemOrigins) { m_gemOriginFilter = gemOrigins; InvalidateFilter(); }
|
||||
|
||||
@@ -61,6 +73,7 @@ namespace O3DE::ProjectManager
|
||||
AzQtComponents::SelectionProxyModel* m_selectionProxyModel = nullptr;
|
||||
|
||||
QString m_searchString;
|
||||
GemStatus m_gemStatusFilter = GemStatus::NoFilter;
|
||||
GemInfo::GemOrigins m_gemOriginFilter = {};
|
||||
GemInfo::Platforms m_platformFilter = {};
|
||||
GemInfo::Types m_typeFilter = {};
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace O3DE::ProjectManager
|
||||
{
|
||||
setObjectName("labelButton");
|
||||
|
||||
QVBoxLayout* vLayout = new QVBoxLayout(this);
|
||||
QVBoxLayout* vLayout = new QVBoxLayout();
|
||||
vLayout->setContentsMargins(0, 0, 0, 0);
|
||||
vLayout->setSpacing(5);
|
||||
|
||||
|
||||
@@ -13,21 +13,11 @@
|
||||
#include <ProjectManagerWindow.h>
|
||||
#include <ScreensCtrl.h>
|
||||
|
||||
#include <AzQtComponents/Components/StyleManager.h>
|
||||
#include <AzCore/IO/FileIO.h>
|
||||
#include <AzCore/IO/Path/Path.h>
|
||||
#include <AzFramework/CommandLine/CommandLine.h>
|
||||
#include <AzFramework/Application/Application.h>
|
||||
|
||||
#include <QDir>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
ProjectManagerWindow::ProjectManagerWindow(QWidget* parent, const AZ::IO::PathView& engineRootPath, const AZ::IO::PathView& projectPath, ProjectManagerScreen startScreen)
|
||||
ProjectManagerWindow::ProjectManagerWindow(QWidget* parent, const AZ::IO::PathView& projectPath, ProjectManagerScreen startScreen)
|
||||
: QMainWindow(parent)
|
||||
{
|
||||
m_pythonBindings = AZStd::make_unique<PythonBindings>(engineRootPath);
|
||||
|
||||
setWindowTitle(tr("O3DE Project Manager"));
|
||||
|
||||
ScreensCtrl* screensCtrl = new ScreensCtrl();
|
||||
@@ -44,15 +34,6 @@ namespace O3DE::ProjectManager
|
||||
|
||||
setCentralWidget(screensCtrl);
|
||||
|
||||
// setup stylesheets and hot reloading
|
||||
QDir rootDir = QString::fromUtf8(engineRootPath.Native().data(), aznumeric_cast<int>(engineRootPath.Native().size()));
|
||||
const auto pathOnDisk = rootDir.absoluteFilePath("Code/Tools/ProjectManager/Resources");
|
||||
const auto qrcPath = QStringLiteral(":/ProjectManager/style");
|
||||
AzQtComponents::StyleManager::addSearchPaths("style", pathOnDisk, qrcPath, engineRootPath);
|
||||
|
||||
// set stylesheet after creating the screens or their styles won't get updated
|
||||
AzQtComponents::StyleManager::setStyleSheet(this, QStringLiteral("style:ProjectManager.qss"));
|
||||
|
||||
// always push the projects screen first so we have something to come back to
|
||||
if (startScreen != ProjectManagerScreen::Projects)
|
||||
{
|
||||
@@ -66,10 +47,4 @@ namespace O3DE::ProjectManager
|
||||
emit screensCtrl->NotifyCurrentProject(path);
|
||||
}
|
||||
}
|
||||
|
||||
ProjectManagerWindow::~ProjectManagerWindow()
|
||||
{
|
||||
m_pythonBindings.reset();
|
||||
}
|
||||
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
#if !defined(Q_MOC_RUN)
|
||||
#include <QMainWindow>
|
||||
#include <PythonBindings.h>
|
||||
#include <AzCore/IO/Path/Path.h>
|
||||
#include <ScreenDefs.h>
|
||||
#endif
|
||||
|
||||
@@ -25,12 +25,8 @@ namespace O3DE::ProjectManager
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ProjectManagerWindow(QWidget* parent, const AZ::IO::PathView& engineRootPath, const AZ::IO::PathView& projectPath,
|
||||
explicit ProjectManagerWindow(QWidget* parent, const AZ::IO::PathView& projectPath,
|
||||
ProjectManagerScreen startScreen = ProjectManagerScreen::Projects);
|
||||
~ProjectManagerWindow();
|
||||
|
||||
private:
|
||||
AZStd::unique_ptr<PythonBindings> m_pythonBindings;
|
||||
};
|
||||
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace O3DE::ProjectManager
|
||||
// if we don't set this in a frame (just use a sub-layout) all the content will align incorrectly horizontally
|
||||
QFrame* projectSettingsFrame = new QFrame(this);
|
||||
projectSettingsFrame->setObjectName("projectSettings");
|
||||
m_verticalLayout = new QVBoxLayout(this);
|
||||
m_verticalLayout = new QVBoxLayout();
|
||||
|
||||
// you cannot remove content margins in qss
|
||||
m_verticalLayout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
@@ -85,7 +85,7 @@ namespace O3DE::ProjectManager
|
||||
QFrame* frame = new QFrame(this);
|
||||
frame->setObjectName("firstTimeContent");
|
||||
{
|
||||
QVBoxLayout* layout = new QVBoxLayout(this);
|
||||
QVBoxLayout* layout = new QVBoxLayout();
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
layout->setAlignment(Qt::AlignTop);
|
||||
frame->setLayout(layout);
|
||||
@@ -100,7 +100,7 @@ namespace O3DE::ProjectManager
|
||||
"available by downloading our sample project."));
|
||||
layout->addWidget(introLabel);
|
||||
|
||||
QHBoxLayout* buttonLayout = new QHBoxLayout(this);
|
||||
QHBoxLayout* buttonLayout = new QHBoxLayout();
|
||||
buttonLayout->setAlignment(Qt::AlignLeft);
|
||||
buttonLayout->setSpacing(s_spacerSize);
|
||||
|
||||
|
||||
@@ -226,7 +226,7 @@ namespace O3DE::ProjectManager
|
||||
PythonBindings::PythonBindings(const AZ::IO::PathView& enginePath)
|
||||
: m_enginePath(enginePath)
|
||||
{
|
||||
StartPython();
|
||||
m_pythonStarted = StartPython();
|
||||
}
|
||||
|
||||
PythonBindings::~PythonBindings()
|
||||
@@ -234,6 +234,11 @@ namespace O3DE::ProjectManager
|
||||
StopPython();
|
||||
}
|
||||
|
||||
bool PythonBindings::PythonStarted()
|
||||
{
|
||||
return m_pythonStarted && Py_IsInitialized();
|
||||
}
|
||||
|
||||
bool PythonBindings::StartPython()
|
||||
{
|
||||
if (Py_IsInitialized())
|
||||
@@ -246,7 +251,7 @@ namespace O3DE::ProjectManager
|
||||
AZStd::string pyBasePath = Platform::GetPythonHomePath(PY_PACKAGE, m_enginePath.c_str());
|
||||
if (!AZ::IO::SystemFile::Exists(pyBasePath.c_str()))
|
||||
{
|
||||
AZ_Assert(false, "Python home path must exist. path:%s", pyBasePath.c_str());
|
||||
AZ_Error("python", false, "Python home path does not exist: %s", pyBasePath.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -351,6 +356,11 @@ namespace O3DE::ProjectManager
|
||||
|
||||
AZ::Outcome<void, AZStd::string> PythonBindings::ExecuteWithLockErrorHandling(AZStd::function<void()> executionCallback)
|
||||
{
|
||||
if (!Py_IsInitialized())
|
||||
{
|
||||
return AZ::Failure<AZStd::string>("Python is not initialized");
|
||||
}
|
||||
|
||||
AZStd::lock_guard<decltype(m_lock)> lock(m_lock);
|
||||
pybind11::gil_scoped_release release;
|
||||
pybind11::gil_scoped_acquire acquire;
|
||||
|
||||
@@ -34,6 +34,8 @@ namespace O3DE::ProjectManager
|
||||
~PythonBindings() override;
|
||||
|
||||
// PythonBindings overrides
|
||||
bool PythonStarted() override;
|
||||
|
||||
// Engine
|
||||
AZ::Outcome<EngineInfo> GetEngineInfo() override;
|
||||
bool SetEngineInfo(const EngineInfo& engineInfo) override;
|
||||
@@ -70,6 +72,8 @@ namespace O3DE::ProjectManager
|
||||
bool StopPython();
|
||||
|
||||
|
||||
bool m_pythonStarted = false;
|
||||
|
||||
AZ::IO::FixedMaxPath m_enginePath;
|
||||
pybind11::handle m_engineTemplate;
|
||||
AZStd::recursive_mutex m_lock;
|
||||
|
||||
@@ -34,6 +34,12 @@ namespace O3DE::ProjectManager
|
||||
IPythonBindings() = default;
|
||||
virtual ~IPythonBindings() = default;
|
||||
|
||||
/**
|
||||
* Get whether Python was started or not. All Python functionality will fail if Python
|
||||
* failed to start.
|
||||
* @return true if Python was started successfully, false on failure
|
||||
*/
|
||||
virtual bool PythonStarted() = 0;
|
||||
|
||||
// Engine
|
||||
|
||||
|
||||
@@ -189,11 +189,13 @@ namespace O3DE::ProjectManager
|
||||
{
|
||||
if (m_stack->currentIndex() == ScreenOrder::Gems)
|
||||
{
|
||||
m_header->setSubTitle(QString(tr("Configure Gems for \"%1\"")).arg(m_projectInfo.m_projectName));
|
||||
m_nextButton->setText(tr("Confirm"));
|
||||
m_header->setTitle(QString(tr("Edit Project Settings: \"%1\"")).arg(m_projectInfo.m_projectName));
|
||||
m_header->setSubTitle(QString(tr("Configure Gems")));
|
||||
m_nextButton->setText(tr("Finalize"));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_header->setTitle("");
|
||||
m_header->setSubTitle(QString(tr("Edit Project Settings: \"%1\"")).arg(m_projectInfo.m_projectName));
|
||||
m_nextButton->setText(tr("Save"));
|
||||
}
|
||||
|
||||
@@ -10,85 +10,26 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzQtComponents/Utilities/HandleDpiAwareness.h>
|
||||
#include <AzQtComponents/Components/StyleManager.h>
|
||||
#include <AzCore/Component/ComponentApplication.h>
|
||||
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
|
||||
#include <AzCore/IO/Path/Path.h>
|
||||
#include <AzFramework/CommandLine/CommandLine.h>
|
||||
|
||||
#include <ProjectManagerWindow.h>
|
||||
#include <ProjectUtils.h>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QCoreApplication>
|
||||
#include <QGuiApplication>
|
||||
|
||||
using namespace O3DE::ProjectManager;
|
||||
#include <AzQtComponents/Utilities/QtPluginPaths.h>
|
||||
#include <Application.h>
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
QApplication::setOrganizationName("O3DE");
|
||||
QApplication::setOrganizationDomain("o3de.org");
|
||||
QCoreApplication::setApplicationName("ProjectManager");
|
||||
QCoreApplication::setApplicationVersion("1.0");
|
||||
|
||||
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
|
||||
QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
|
||||
QCoreApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
|
||||
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
|
||||
AzQtComponents::Utilities::HandleDpiAwareness(AzQtComponents::Utilities::SystemDpiAware);
|
||||
|
||||
AZ::AllocatorInstance<AZ::SystemAllocator>::Create();
|
||||
int runSuccess = 0;
|
||||
|
||||
// Call before using any Qt, or the app may not be able to locate Qt libs
|
||||
AzQtComponents::PrepareQtPaths();
|
||||
|
||||
O3DE::ProjectManager::Application application(&argc, &argv);
|
||||
if (!application.Init())
|
||||
{
|
||||
QApplication app(argc, argv);
|
||||
|
||||
// Need to use settings registry to get EngineRootFolder
|
||||
AZ::IO::FixedMaxPath engineRootPath;
|
||||
{
|
||||
AZ::ComponentApplication componentApplication;
|
||||
auto settingsRegistry = AZ::SettingsRegistry::Get();
|
||||
settingsRegistry->Get(engineRootPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder);
|
||||
}
|
||||
|
||||
AzQtComponents::StyleManager styleManager(&app);
|
||||
styleManager.initialize(&app, engineRootPath);
|
||||
|
||||
// Get the initial start screen if one is provided via command line
|
||||
constexpr char optionPrefix[] = "--";
|
||||
AZ::CommandLine commandLine(optionPrefix);
|
||||
commandLine.Parse(argc, argv);
|
||||
|
||||
ProjectManagerScreen startScreen = ProjectManagerScreen::Projects;
|
||||
if(commandLine.HasSwitch("screen"))
|
||||
{
|
||||
QString screenOption = commandLine.GetSwitchValue("screen", 0).c_str();
|
||||
ProjectManagerScreen screen = ProjectUtils::GetProjectManagerScreen(screenOption);
|
||||
if (screen != ProjectManagerScreen::Invalid)
|
||||
{
|
||||
startScreen = screen;
|
||||
}
|
||||
}
|
||||
|
||||
AZ::IO::FixedMaxPath projectPath;
|
||||
if (commandLine.HasSwitch("project-path"))
|
||||
{
|
||||
projectPath = commandLine.GetSwitchValue("project-path", 0).c_str();
|
||||
}
|
||||
|
||||
ProjectManagerWindow window(nullptr, engineRootPath, projectPath, startScreen);
|
||||
window.show();
|
||||
|
||||
// somethings is preventing us from moving the window to the center of the
|
||||
// primary screen - likely an Az style or component helper
|
||||
constexpr int width = 1200;
|
||||
constexpr int height = 800;
|
||||
window.resize(width, height);
|
||||
|
||||
runSuccess = app.exec();
|
||||
AZ_Error("ProjectManager", false, "Failed to initialize");
|
||||
runSuccess = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
runSuccess = application.Run() ? 0 : 1;
|
||||
}
|
||||
AZ::AllocatorInstance<AZ::SystemAllocator>::Destroy();
|
||||
|
||||
return runSuccess;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
set(FILES
|
||||
Resources/ProjectManager.rc
|
||||
Resources/ProjectManager.qrc
|
||||
Resources/ProjectManager.qss
|
||||
Source/main.cpp
|
||||
)
|
||||
@@ -1,4 +1,5 @@
|
||||
#
|
||||
#
|
||||
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
# its licensors.
|
||||
#
|
||||
@@ -10,10 +11,8 @@
|
||||
#
|
||||
|
||||
set(FILES
|
||||
Resources/ProjectManager.rc
|
||||
Resources/ProjectManager.qrc
|
||||
Resources/ProjectManager.qss
|
||||
Source/main.cpp
|
||||
Source/Application.h
|
||||
Source/Application.cpp
|
||||
Source/ScreenDefs.h
|
||||
Source/ScreenFactory.h
|
||||
Source/ScreenFactory.cpp
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
set(FILES
|
||||
Resources/ProjectManager.qrc
|
||||
Resources/ProjectManager.qss
|
||||
tests/ApplicationTests.cpp
|
||||
tests/main.cpp
|
||||
)
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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 <AzCore/UnitTest/TestTypes.h>
|
||||
#include <AzCore/std/smart_ptr/make_shared.h>
|
||||
#include <Application.h>
|
||||
#include <ProjectManager_Test_Traits_Platform.h>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
class ProjectManagerApplicationTests
|
||||
: public ::UnitTest::ScopedAllocatorSetupFixture
|
||||
{
|
||||
public:
|
||||
|
||||
ProjectManagerApplicationTests()
|
||||
{
|
||||
m_application = AZStd::make_unique<ProjectManager::Application>();
|
||||
}
|
||||
|
||||
~ProjectManagerApplicationTests()
|
||||
{
|
||||
m_application.reset();
|
||||
}
|
||||
|
||||
AZStd::unique_ptr<ProjectManager::Application> m_application;
|
||||
};
|
||||
|
||||
#if AZ_TRAIT_DISABLE_FAILED_PROJECT_MANAGER_TESTS
|
||||
TEST_F(ProjectManagerApplicationTests, DISABLED_Application_Init_Succeeds)
|
||||
#else
|
||||
TEST_F(ProjectManagerApplicationTests, Application_Init_Succeeds)
|
||||
#endif // !AZ_TRAIT_DISABLE_FAILED_PROJECT_MANAGER_TESTS
|
||||
{
|
||||
// we don't want to interact with actual GUI or display it
|
||||
EXPECT_TRUE(m_application->Init(/*interactive=*/false));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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 <AzTest/AzTest.h>
|
||||
|
||||
DECLARE_AZ_UNIT_TEST_MAIN();
|
||||
|
||||
int runDefaultRunner(int argc, char* argv[])
|
||||
{
|
||||
INVOKE_AZ_UNIT_TEST_MAIN(nullptr)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
if (argc == 1)
|
||||
{
|
||||
// if no parameters are provided, add the --unittests parameter
|
||||
constexpr int defaultArgc = 2;
|
||||
char unittest_arg[] = "--unittests"; // Conversion from string literal to char* is not allowed per ISO C++11
|
||||
char* defaultArgv[defaultArgc] = { argv[0], unittest_arg };
|
||||
return runDefaultRunner(defaultArgc, defaultArgv);
|
||||
}
|
||||
INVOKE_AZ_UNIT_TEST_MAIN(nullptr);
|
||||
return 0;
|
||||
}
|
||||
@@ -46,22 +46,69 @@ namespace AZ
|
||||
serializeContext->Class<AssImpTransformImporter, SceneCore::LoadingComponent>()->Version(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void GetAllBones(const aiScene* scene, AZStd::unordered_map<AZStd::string, const aiBone*>& boneLookup)
|
||||
{
|
||||
for (unsigned meshIndex = 0; meshIndex < scene->mNumMeshes; ++meshIndex)
|
||||
{
|
||||
const aiMesh* mesh = scene->mMeshes[meshIndex];
|
||||
|
||||
for (unsigned boneIndex = 0; boneIndex < mesh->mNumBones; ++boneIndex)
|
||||
{
|
||||
const aiBone* bone = mesh->mBones[boneIndex];
|
||||
|
||||
boneLookup[bone->mName.C_Str()] = bone;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Events::ProcessingResult AssImpTransformImporter::ImportTransform(AssImpSceneNodeAppendedContext& context)
|
||||
{
|
||||
AZ_TraceContext("Importer", "transform");
|
||||
const aiNode* currentNode = context.m_sourceNode.GetAssImpNode();
|
||||
const aiScene* scene = context.m_sourceScene.GetAssImpScene();
|
||||
|
||||
|
||||
if (currentNode == scene->mRootNode || IsPivotNode(currentNode->mName))
|
||||
{
|
||||
return Events::ProcessingResult::Ignored;
|
||||
}
|
||||
|
||||
aiMatrix4x4 combinedTransform = GetConcatenatedLocalTransform(currentNode);
|
||||
AZStd::unordered_map<AZStd::string, const aiBone*> boneLookup;
|
||||
GetAllBones(scene, boneLookup);
|
||||
|
||||
auto boneIterator = boneLookup.find(currentNode->mName.C_Str());
|
||||
const bool isBone = boneIterator != boneLookup.end();
|
||||
|
||||
aiMatrix4x4 combinedTransform;
|
||||
|
||||
if (isBone)
|
||||
{
|
||||
auto parentNode = currentNode->mParent;
|
||||
|
||||
aiMatrix4x4 offsetMatrix = boneIterator->second->mOffsetMatrix;
|
||||
aiMatrix4x4 parentOffset {};
|
||||
|
||||
auto parentBoneIterator = boneLookup.find(parentNode->mName.C_Str());
|
||||
|
||||
if (parentNode && parentBoneIterator != boneLookup.end())
|
||||
{
|
||||
const auto& parentBone = parentBoneIterator->second;
|
||||
|
||||
parentOffset = parentBone->mOffsetMatrix;
|
||||
}
|
||||
|
||||
auto inverseOffset = offsetMatrix;
|
||||
inverseOffset.Inverse();
|
||||
|
||||
combinedTransform = parentOffset * inverseOffset;
|
||||
}
|
||||
else
|
||||
{
|
||||
combinedTransform = GetConcatenatedLocalTransform(currentNode);
|
||||
}
|
||||
|
||||
DataTypes::MatrixType localTransform = AssImpSDKWrapper::AssImpTypeConverter::ToTransform(combinedTransform);
|
||||
|
||||
|
||||
context.m_sourceSceneSystem.SwapTransformForUpAxis(localTransform);
|
||||
context.m_sourceSceneSystem.ConvertUnit(localTransform);
|
||||
|
||||
@@ -105,9 +152,7 @@ namespace AZ
|
||||
}
|
||||
else
|
||||
{
|
||||
bool addedData = context.m_scene.GetGraph().SetContent(
|
||||
context.m_currentGraphPosition,
|
||||
transformData);
|
||||
bool addedData = context.m_scene.GetGraph().SetContent(context.m_currentGraphPosition, transformData);
|
||||
|
||||
AZ_Error(SceneAPI::Utilities::ErrorWindow, addedData, "Failed to add node data");
|
||||
return addedData ? Events::ProcessingResult::Success : Events::ProcessingResult::Failure;
|
||||
|
||||
@@ -69,13 +69,14 @@ namespace AZ
|
||||
// aiProcess_JoinIdenticalVertices is not enabled because O3DE has a mesh optimizer that also does this,
|
||||
// this flag is disabled to keep AssImp output similar to FBX SDK to reduce downstream bugs for the initial AssImp release.
|
||||
// There's currently a minimum of properties and flags set to maximize compatibility with the existing node graph.
|
||||
|
||||
// aiProcess_LimitBoneWeights is not enabled because it will remove bones which are not associated with a mesh.
|
||||
// This results in the loss of the offset matrix data for nodes without a mesh which is required for the Transform Importer.
|
||||
m_importer.SetPropertyBool(AI_CONFIG_IMPORT_FBX_PRESERVE_PIVOTS, false);
|
||||
m_importer.SetPropertyBool(AI_CONFIG_IMPORT_FBX_OPTIMIZE_EMPTY_ANIMATION_CURVES, false);
|
||||
m_sceneFileName = fileName;
|
||||
m_assImpScene = m_importer.ReadFile(fileName,
|
||||
aiProcess_Triangulate //Triangulates all faces of all meshes
|
||||
| aiProcess_LimitBoneWeights //Limits the number of bones that can affect a vertex to a maximum value
|
||||
//dropping the least important and re-normalizing
|
||||
| aiProcess_GenNormals); //Generate normals for meshes
|
||||
|
||||
#if AZ_TRAIT_COMPILER_SUPPORT_CSIGNAL
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace AZ
|
||||
Application::Application(int argc, char** argv)
|
||||
: AzToolsFramework::ToolsApplication(&argc, &argv)
|
||||
{
|
||||
// We need a specialized variant of EditorEntityContextCompnent for the SliceConverter, so we register the descriptor here.
|
||||
// We need a specialized variant of EditorEntityContextComponent for the SliceConverter, so we register the descriptor here.
|
||||
RegisterComponentDescriptor(AzToolsFramework::SliceConverterEditorEntityContextComponent::CreateDescriptor());
|
||||
|
||||
AZ::IO::FixedMaxPath projectPath = AZ::Utils::GetProjectPath();
|
||||
|
||||
@@ -128,8 +128,7 @@ namespace AZ
|
||||
return result;
|
||||
}
|
||||
|
||||
bool SliceConverter::ConvertSliceFile(
|
||||
AZ::SerializeContext* serializeContext, const AZStd::string& slicePath, bool isDryRun)
|
||||
bool SliceConverter::ConvertSliceFile(AZ::SerializeContext* serializeContext, const AZStd::string& slicePath, bool isDryRun)
|
||||
{
|
||||
/* To convert a slice file, we read the input file in via ObjectStream, then use the "class ready" callback to convert
|
||||
* the data in memory to a Prefab.
|
||||
@@ -177,11 +176,23 @@ namespace AZ
|
||||
}
|
||||
|
||||
AZ::Entity* rootEntity = reinterpret_cast<AZ::Entity*>(classPtr);
|
||||
return ConvertSliceToPrefab(context, outputPath, isDryRun, rootEntity);
|
||||
bool convertResult = ConvertSliceToPrefab(context, outputPath, isDryRun, rootEntity);
|
||||
|
||||
// Delete the root entity pointer. Otherwise, it will leak itself along with all of the slice asset references held
|
||||
// within it.
|
||||
delete rootEntity;
|
||||
return convertResult;
|
||||
};
|
||||
|
||||
// Read in the slice file and call the callback on completion to convert the read-in slice to a prefab.
|
||||
if (!Utilities::InspectSerializedFile(inputPath.c_str(), serializeContext, callback))
|
||||
// This will also load dependent slice assets, but no other dependent asset types.
|
||||
// Since we're not actually initializing any of the entities, we don't need any of the non-slice assets to be loaded.
|
||||
if (!Utilities::InspectSerializedFile(
|
||||
inputPath.c_str(), serializeContext, callback,
|
||||
[](const AZ::Data::AssetFilterInfo& filterInfo)
|
||||
{
|
||||
return (filterInfo.m_assetType == azrtti_typeid<AZ::SliceAsset>());
|
||||
}))
|
||||
{
|
||||
AZ_Warning("Convert-Slice", false, "Failed to load '%s'. File may not contain an object stream.", inputPath.c_str());
|
||||
result = false;
|
||||
@@ -219,8 +230,12 @@ namespace AZ
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get all of the entities from the slice.
|
||||
// Get all of the entities from the slice. We're taking ownership of them, so we also remove them from the slice component
|
||||
// without deleting them.
|
||||
constexpr bool deleteEntities = false;
|
||||
constexpr bool removeEmptyInstances = true;
|
||||
SliceComponent::EntityList sliceEntities = sliceComponent->GetNewEntities();
|
||||
sliceComponent->RemoveAllEntities(deleteEntities, removeEmptyInstances);
|
||||
AZ_Printf("Convert-Slice", " Slice contains %zu entities.\n", sliceEntities.size());
|
||||
|
||||
// Create the Prefab with the entities from the slice.
|
||||
@@ -256,13 +271,19 @@ namespace AZ
|
||||
for (auto& alias : entityAliases)
|
||||
{
|
||||
auto id = sourceInstance->GetEntityId(alias);
|
||||
auto result = m_aliasIdMapper.emplace(TemplateEntityIdPair(templateId, id), alias);
|
||||
auto result = m_aliasIdMapper.emplace(id, SliceEntityMappingInfo(templateId, alias));
|
||||
if (!result.second)
|
||||
{
|
||||
AZ_Printf("Convert-Slice", " Duplicate entity alias -> entity id entries found, conversion may not be successful.\n");
|
||||
}
|
||||
}
|
||||
|
||||
// Save off a mapping of the slice's metadata entity ID as well, even though we never converted the entity itself.
|
||||
// This will help us better detect entity ID mapping errors for nested slice instances.
|
||||
AZ::Entity* metadataEntity = sliceComponent->GetMetadataEntity();
|
||||
constexpr bool isMetadataEntity = true;
|
||||
m_aliasIdMapper.emplace(metadataEntity->GetId(), SliceEntityMappingInfo(templateId, "MetadataEntity", isMetadataEntity));
|
||||
|
||||
// Update the prefab template with the fixed-up data in our prefab instance.
|
||||
AzToolsFramework::Prefab::PrefabDom prefabDom;
|
||||
bool storeResult = AzToolsFramework::Prefab::PrefabDomUtils::StoreInstanceInPrefabDom(*sourceInstance, prefabDom);
|
||||
@@ -342,10 +363,9 @@ namespace AZ
|
||||
// For each nested slice, convert it.
|
||||
for (auto& slice : sliceList)
|
||||
{
|
||||
// Get the nested slice asset
|
||||
// Get the nested slice asset. These should already be preloaded due to loading the root asset.
|
||||
auto sliceAsset = slice.GetSliceAsset();
|
||||
sliceAsset.QueueLoad();
|
||||
sliceAsset.BlockUntilLoadComplete();
|
||||
AZ_Assert(sliceAsset.IsReady(), "slice asset hasn't been loaded yet!");
|
||||
|
||||
// The slice list gives us asset IDs, and we need to get to the source path. So first we get the asset path from the ID,
|
||||
// then we get the source path from the asset path.
|
||||
@@ -393,6 +413,21 @@ namespace AZ
|
||||
"Convert-Slice", " Attaching %zu instances of nested slice '%s'.\n", instances.size(),
|
||||
nestedPrefabPath.Native().c_str());
|
||||
|
||||
// Before processing any further, save off all the known entity IDs from all the instances and how they map back to
|
||||
// the base nested prefab that they've come from (i.e. this one). As we proceed up the chain of nesting, this will
|
||||
// build out a hierarchical list of owning instances for each entity that we can trace upwards to know where to add
|
||||
// the entity into our nested prefab instance.
|
||||
// This step needs to occur *before* converting the instances themselves, because while converting instances, they
|
||||
// might have entity ID references that point to other instances. By having the full instance entity ID map in place
|
||||
// before conversion, we'll be able to fix them up appropriately.
|
||||
|
||||
for (auto& instance : instances)
|
||||
{
|
||||
AZStd::string instanceAlias = GetInstanceAlias(instance);
|
||||
UpdateSliceEntityInstanceMappings(instance.GetEntityIdToBaseMap(), instanceAlias);
|
||||
}
|
||||
|
||||
// Now that we have all the entity ID mappings, convert all the instances.
|
||||
for (auto& instance : instances)
|
||||
{
|
||||
bool instanceConvertResult = ConvertSliceInstance(instance, sliceAsset, nestedTemplate, sourceInstance);
|
||||
@@ -406,6 +441,28 @@ namespace AZ
|
||||
return true;
|
||||
}
|
||||
|
||||
AZStd::string SliceConverter::GetInstanceAlias(const AZ::SliceComponent::SliceInstance& instance)
|
||||
{
|
||||
// When creating the new instance, we would like to have deterministic instance aliases. Prefabs that depend on this one
|
||||
// will have patches that reference the alias, so if we reconvert this slice a second time, we would like it to produce
|
||||
// the same results. To get a deterministic and unique alias, we rely on the slice instance. The slice instance contains
|
||||
// a map of slice entity IDs to unique instance entity IDs. We'll just consistently use the first entry in the map as the
|
||||
// unique instance ID.
|
||||
AZStd::string instanceAlias;
|
||||
auto entityIdMap = instance.GetEntityIdMap();
|
||||
if (!entityIdMap.empty())
|
||||
{
|
||||
instanceAlias = AZStd::string::format("Instance_%s", entityIdMap.begin()->second.ToString().c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Error("Convert-Slice", false, " Couldn't create deterministic instance alias.");
|
||||
instanceAlias = AZStd::string::format("Instance_%s", AZ::Entity::MakeId().ToString().c_str());
|
||||
}
|
||||
return instanceAlias;
|
||||
}
|
||||
|
||||
|
||||
bool SliceConverter::ConvertSliceInstance(
|
||||
AZ::SliceComponent::SliceInstance& instance,
|
||||
AZ::Data::Asset<AZ::SliceAsset>& sliceAsset,
|
||||
@@ -429,6 +486,8 @@ namespace AZ
|
||||
auto instanceToTemplateInterface = AZ::Interface<AzToolsFramework::Prefab::InstanceToTemplateInterface>::Get();
|
||||
auto prefabSystemComponentInterface = AZ::Interface<AzToolsFramework::Prefab::PrefabSystemComponentInterface>::Get();
|
||||
|
||||
AZStd::string instanceAlias = GetInstanceAlias(instance);
|
||||
|
||||
// Create a new unmodified prefab Instance for the nested slice instance.
|
||||
auto nestedInstance = AZStd::make_unique<AzToolsFramework::Prefab::Instance>();
|
||||
AzToolsFramework::Prefab::Instance::EntityList newEntities;
|
||||
@@ -465,15 +524,62 @@ namespace AZ
|
||||
auto instantiated =
|
||||
dataPatch.Apply(&sourceObjects, dependentSlice->GetSerializeContext(), filterDesc, sourceDataFlags, targetDataFlags);
|
||||
|
||||
// Run through all the instantiated entities and fix up their parent hierarchy:
|
||||
// - Invalid parents need to get set to the container.
|
||||
// - Valid parents into the top-level instance mean that the nested slice instance is also child-nested under an entity.
|
||||
// Prefabs handle this type of nesting differently - we need to set the parent to the container, and the container's
|
||||
// parent to that other instance.
|
||||
auto containerEntity = nestedInstance->GetContainerEntity();
|
||||
auto containerEntityId = containerEntity->get().GetId();
|
||||
for (auto entity : instantiated->m_entities)
|
||||
// Replace all the entities in the instance with the new patched ones. To do this, we'll remove all existing entities
|
||||
// throughout the entire nested hierarchy, then add the new patched entities back in at the appropriate place in the hierarchy.
|
||||
// (This is easier than trying to figure out what the patched data changes are - we can let the JSON patch handle it for us)
|
||||
|
||||
nestedInstance->RemoveNestedEntities(
|
||||
[](const AZStd::unique_ptr<AZ::Entity>&)
|
||||
{
|
||||
return true;
|
||||
});
|
||||
|
||||
AZStd::vector<AZStd::pair<AZ::Entity*, AzToolsFramework::Prefab::Instance*>> addedEntityList;
|
||||
|
||||
for (auto& entity : instantiated->m_entities)
|
||||
{
|
||||
auto entityEntry = m_aliasIdMapper.find(entity->GetId());
|
||||
if (entityEntry != m_aliasIdMapper.end())
|
||||
{
|
||||
auto& mappingStruct = entityEntry->second;
|
||||
|
||||
// Starting with the current nested instance, walk downwards through the nesting hierarchy until we're at the
|
||||
// correct level for this instanced entity ID, then add it. Because we're adding it with the non-instanced alias,
|
||||
// it doesn't matter what the slice's instanced entity ID is, and the JSON patch will correctly pick up the changes
|
||||
// we've made for this instance.
|
||||
AzToolsFramework::Prefab::Instance* addingInstance = nestedInstance.get();
|
||||
for (auto it = mappingStruct.m_nestedInstanceAliases.rbegin(); it != mappingStruct.m_nestedInstanceAliases.rend(); it++)
|
||||
{
|
||||
auto foundInstance = addingInstance->FindNestedInstance(*it);
|
||||
if (foundInstance.has_value())
|
||||
{
|
||||
addingInstance = &(foundInstance->get());
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Assert(false, "Couldn't find nested instance %s", it->c_str());
|
||||
}
|
||||
}
|
||||
addingInstance->AddEntity(*entity, mappingStruct.m_entityAlias);
|
||||
addedEntityList.emplace_back(entity, addingInstance);
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Assert(false, "Failed to find entity alias.");
|
||||
nestedInstance->AddEntity(*entity);
|
||||
addedEntityList.emplace_back(entity, nestedInstance.get());
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& [entity, addingInstance] : addedEntityList)
|
||||
{
|
||||
// Fix up the parent hierarchy:
|
||||
// - Invalid parents need to get set to the container.
|
||||
// - Valid parents into the top-level instance mean that the nested slice instance is also child-nested under an entity.
|
||||
// Prefabs handle this type of nesting differently - we need to set the parent to the container, and the container's
|
||||
// parent to that other instance.
|
||||
auto containerEntity = addingInstance->GetContainerEntity();
|
||||
auto containerEntityId = containerEntity->get().GetId();
|
||||
AzToolsFramework::Components::TransformComponent* transformComponent =
|
||||
entity->FindComponent<AzToolsFramework::Components::TransformComponent>();
|
||||
if (transformComponent)
|
||||
@@ -482,70 +588,76 @@ namespace AZ
|
||||
auto parentId = transformComponent->GetParentId();
|
||||
if (parentId.IsValid())
|
||||
{
|
||||
auto parentAlias = m_aliasIdMapper.find(TemplateEntityIdPair(topLevelInstance->GetTemplateId(), parentId));
|
||||
if (parentAlias != m_aliasIdMapper.end())
|
||||
// Look to see if the parent ID exists in the same instance (i.e. an entity in the nested slice is a
|
||||
// child of an entity in the containing slice). If this case exists, we need to adjust the parents so that
|
||||
// the child entity connects to the prefab container, and the *container* is the child of the entity in the
|
||||
// containing slice. (i.e. go from A->B to A->container->B)
|
||||
auto parentEntry = m_aliasIdMapper.find(parentId);
|
||||
if (parentEntry != m_aliasIdMapper.end())
|
||||
{
|
||||
// Set the container's parent to this entity's parent, and set this entity's parent to the container
|
||||
// (i.e. go from A->B to A->container->B)
|
||||
auto newParentId = topLevelInstance->GetEntityId(parentAlias->second);
|
||||
SetParentEntity(containerEntity->get(), newParentId, false);
|
||||
onlySetIfInvalid = false;
|
||||
auto& parentMappingInfo = parentEntry->second;
|
||||
if (parentMappingInfo.m_templateId != addingInstance->GetTemplateId())
|
||||
{
|
||||
if (topLevelInstance->GetTemplateId() == parentMappingInfo.m_templateId)
|
||||
{
|
||||
parentId = topLevelInstance->GetEntityId(parentMappingInfo.m_entityAlias);
|
||||
}
|
||||
else
|
||||
{
|
||||
AzToolsFramework::Prefab::Instance* parentInstance = addingInstance;
|
||||
|
||||
while ((parentInstance->GetParentInstance().has_value()) &&
|
||||
(parentInstance->GetTemplateId() != parentMappingInfo.m_templateId))
|
||||
{
|
||||
parentInstance = &(parentInstance->GetParentInstance()->get());
|
||||
}
|
||||
|
||||
if (parentInstance->GetTemplateId() == parentMappingInfo.m_templateId)
|
||||
{
|
||||
parentId = parentInstance->GetEntityId(parentMappingInfo.m_entityAlias);
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Assert(false, "Could not find parent instance");
|
||||
}
|
||||
}
|
||||
|
||||
// Set the container's parent to this entity's parent, and set this entity's parent to the container
|
||||
// auto newParentId = topLevelInstance->GetEntityId(parentMappingInfo.m_entityAlias);
|
||||
SetParentEntity(containerEntity->get(), parentId, false);
|
||||
onlySetIfInvalid = false;
|
||||
}
|
||||
}
|
||||
|
||||
// If the parent ID is valid, but NOT in the top-level instance, then it's just a nested hierarchy inside
|
||||
// the slice and we don't need to adjust anything. "onlySetIfInvalid" will still be true, which means we
|
||||
// won't change the parent ID below.
|
||||
}
|
||||
|
||||
SetParentEntity(*entity, containerEntityId, onlySetIfInvalid);
|
||||
SetParentEntity(*entity, containerEntityId, onlySetIfInvalid);
|
||||
}
|
||||
}
|
||||
|
||||
// Replace all the entities in the instance with the new patched ones.
|
||||
// (This is easier than trying to figure out what the patched data changes are - we can let the JSON patch handle it for us)
|
||||
nestedInstance->RemoveNestedEntities(
|
||||
[](const AZStd::unique_ptr<AZ::Entity>&)
|
||||
{
|
||||
return true;
|
||||
});
|
||||
for (auto& entity : instantiated->m_entities)
|
||||
{
|
||||
auto entityAlias = m_aliasIdMapper.find(TemplateEntityIdPair(nestedInstance->GetTemplateId(), entity->GetId()));
|
||||
if (entityAlias != m_aliasIdMapper.end())
|
||||
{
|
||||
nestedInstance->AddEntity(*entity, entityAlias->second);
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Assert(false, "Failed to find entity alias.");
|
||||
nestedInstance->AddEntity(*entity);
|
||||
}
|
||||
}
|
||||
|
||||
// Set the container entity of the nested prefab to have the top-level prefab as the parent if it hasn't already gotten
|
||||
// another entity as its parent.
|
||||
{
|
||||
auto containerEntity = nestedInstance->GetContainerEntity();
|
||||
constexpr bool onlySetIfInvalid = true;
|
||||
SetParentEntity(containerEntity->get(), topLevelInstance->GetContainerEntityId(), onlySetIfInvalid);
|
||||
}
|
||||
|
||||
// After doing all of the above, run through entity references in any of the patched entities, and fix up the entity IDs to
|
||||
// match the new ones in our prefabs.
|
||||
RemapIdReferences(m_aliasIdMapper, topLevelInstance, nestedInstance.get(), instantiated, dependentSlice->GetSerializeContext());
|
||||
|
||||
// Add the nested instance itself to the top-level prefab. To do this, we need to add it to our top-level instance,
|
||||
// create a patch out of it, and patch the top-level prefab template.
|
||||
|
||||
AzToolsFramework::Prefab::PrefabDom topLevelInstanceDomBefore;
|
||||
instanceToTemplateInterface->GenerateDomForInstance(topLevelInstanceDomBefore, *topLevelInstance);
|
||||
|
||||
// When creating the new instance, we would like to have deterministic instance aliases. Prefabs that depend on this one
|
||||
// will have patches that reference the alias, so if we reconvert this slice a second time, we would like it to produce
|
||||
// the same results. To get a deterministic and unique alias, we rely on the slice instance. The slice instance contains
|
||||
// a map of slice entity IDs to unique instance entity IDs. We'll just consistently use the first entry in the map as the
|
||||
// unique instance ID.
|
||||
AZStd::string instanceAlias;
|
||||
auto entityIdMap = instance.GetEntityIdMap();
|
||||
if (!entityIdMap.empty())
|
||||
{
|
||||
instanceAlias = AZStd::string::format("Instance_%s", entityIdMap.begin()->second.ToString().c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
instanceAlias = AZStd::string::format("Instance_%s", AZ::Entity::MakeId().ToString().c_str());
|
||||
}
|
||||
// Use the deterministic instance alias for this new instance
|
||||
AzToolsFramework::Prefab::Instance& addedInstance = topLevelInstance->AddInstance(AZStd::move(nestedInstance), instanceAlias);
|
||||
|
||||
AzToolsFramework::Prefab::PrefabDom topLevelInstanceDomAfter;
|
||||
@@ -670,5 +782,136 @@ namespace AZ
|
||||
AZ_Error("Convert-Slice", disconnected, "Asset Processor failed to disconnect successfully.");
|
||||
}
|
||||
|
||||
void SliceConverter::UpdateSliceEntityInstanceMappings(
|
||||
const AZ::SliceComponent::EntityIdToEntityIdMap& sliceEntityIdMap, const AZStd::string& currentInstanceAlias)
|
||||
{
|
||||
// For each instanced entity, map its ID all the way back to the original prefab template and entity ID that it came from.
|
||||
// This counts on being run recursively from the leaf nodes upwards, so we first get B->A,
|
||||
// then C->B which becomes a C->A entry, then D->C which becomes D->A, etc.
|
||||
for (auto& [newId, oldId] : sliceEntityIdMap)
|
||||
{
|
||||
// Try to find the conversion chain from the old ID. if it's there, copy it and use it for the new ID, plus add this
|
||||
// instance's name to the end of the chain. If it's not there, skip it, since it's probably the slice metadata entity,
|
||||
// which we didn't convert.
|
||||
auto parentEntry = m_aliasIdMapper.find(oldId);
|
||||
if (parentEntry != m_aliasIdMapper.end())
|
||||
{
|
||||
// Only add this instance's name if we don't already have an entry for the new ID.
|
||||
if (m_aliasIdMapper.find(newId) == m_aliasIdMapper.end())
|
||||
{
|
||||
auto newMappingEntry = m_aliasIdMapper.emplace(newId, parentEntry->second).first;
|
||||
newMappingEntry->second.m_nestedInstanceAliases.emplace_back(currentInstanceAlias);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we already had an entry for the new ID, it might be because the old and new ID are the same. This happens
|
||||
// when nesting multiple prefabs directly underneath each other without a nesting entity in-between.
|
||||
// If the IDs are different, it's an unexpected error condition.
|
||||
AZ_Assert(oldId == newId, "The same entity instance ID has unexpectedly appeared twice in the same nested prefab.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Warning("Convert-Slice", false, " Couldn't find an entity ID conversion for %s.", oldId.ToString().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SliceConverter::RemapIdReferences(
|
||||
const AZStd::unordered_map<AZ::EntityId, SliceEntityMappingInfo>& idMapper,
|
||||
AzToolsFramework::Prefab::Instance* topLevelInstance,
|
||||
AzToolsFramework::Prefab::Instance* nestedInstance,
|
||||
SliceComponent::InstantiatedContainer* instantiatedEntities,
|
||||
SerializeContext* context)
|
||||
{
|
||||
// Given a set of instantiated entities, run through all of them, look for entity references, and replace the entity IDs with
|
||||
// new ones that match up with our prefabs.
|
||||
|
||||
IdUtils::Remapper<EntityId>::ReplaceIdsAndIdRefs(
|
||||
instantiatedEntities,
|
||||
[idMapper, &topLevelInstance, &nestedInstance](
|
||||
const EntityId& sourceId, bool isEntityId, [[maybe_unused]] const AZStd::function<EntityId()>& idGenerator) -> EntityId
|
||||
{
|
||||
EntityId newId = sourceId;
|
||||
|
||||
// Only convert valid entity references. Actual entity IDs have already been taken care of elsewhere, so ignore them.
|
||||
if (!isEntityId && sourceId.IsValid())
|
||||
{
|
||||
auto entityEntry = idMapper.find(sourceId);
|
||||
|
||||
// Since we've already remapped transform hierarchies to include container entities, it's possible that our entity
|
||||
// reference is pointing to a container, which means it won't be in our slice mapping table. In that case, just
|
||||
// return it as-is.
|
||||
if (entityEntry == idMapper.end())
|
||||
{
|
||||
return sourceId;
|
||||
}
|
||||
|
||||
// We've got a slice->prefab mapping entry, so now we need to use it.
|
||||
auto& mappingStruct = entityEntry->second;
|
||||
|
||||
if (mappingStruct.m_nestedInstanceAliases.empty())
|
||||
{
|
||||
// If we don't have a chain of nested instance aliases, then this entity reference is either within the
|
||||
// current nested instance or it's pointing to an entity in the top-level instance. We'll try them both
|
||||
// to look for a match.
|
||||
|
||||
EntityId prefabId = nestedInstance->GetEntityId(mappingStruct.m_entityAlias);
|
||||
if (!prefabId.IsValid())
|
||||
{
|
||||
prefabId = topLevelInstance->GetEntityId(mappingStruct.m_entityAlias);
|
||||
}
|
||||
|
||||
if (prefabId.IsValid())
|
||||
{
|
||||
newId = prefabId;
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Error("Convert-Slice", false, " Couldn't find source ID %s", sourceId.ToString().c_str());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// We *do* have a chain of nested instance aliases. This chain could either be relative to the nested instance
|
||||
// or the top-level instance. We can tell which one it is by which one can find the first nested instance
|
||||
// alias.
|
||||
|
||||
AzToolsFramework::Prefab::Instance* entityInstance = nestedInstance;
|
||||
auto it = mappingStruct.m_nestedInstanceAliases.rbegin();
|
||||
if (!entityInstance->FindNestedInstance(*it).has_value())
|
||||
{
|
||||
entityInstance = topLevelInstance;
|
||||
}
|
||||
|
||||
// Now that we've got a starting point, iterate through the chain of nested instance aliases to find the
|
||||
// correct instance to get the entity ID for. We have to go from slice IDs -> entity aliases -> entity IDs
|
||||
// because prefab instance creation can change some of our entity IDs along the way.
|
||||
for (; it != mappingStruct.m_nestedInstanceAliases.rend(); it++)
|
||||
{
|
||||
auto foundInstance = entityInstance->FindNestedInstance(*it);
|
||||
if (foundInstance.has_value())
|
||||
{
|
||||
entityInstance = &(foundInstance->get());
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Assert(false, "Couldn't find nested instance %s", it->c_str());
|
||||
}
|
||||
}
|
||||
|
||||
EntityId prefabId = entityInstance->GetEntityId(mappingStruct.m_entityAlias);
|
||||
if (prefabId.IsValid())
|
||||
{
|
||||
newId = prefabId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return newId;
|
||||
},
|
||||
context);
|
||||
}
|
||||
|
||||
} // namespace SerializeContextTools
|
||||
} // namespace AZ
|
||||
|
||||
@@ -42,7 +42,27 @@ namespace AZ
|
||||
bool ConvertSliceFiles(Application& application);
|
||||
|
||||
private:
|
||||
using TemplateEntityIdPair = AZStd::pair<AzToolsFramework::Prefab::TemplateId, AZ::EntityId>;
|
||||
// When converting slice entities, especially for nested slices, we need to keep track of the original
|
||||
// entity ID, the entity alias it uses in the prefab, and which template and nested instance path it maps to.
|
||||
// As we encounter each instanced entity ID, we can look it up in this structure and use this to determine how to properly
|
||||
// add it to the correct place in the hierarchy.
|
||||
struct SliceEntityMappingInfo
|
||||
{
|
||||
SliceEntityMappingInfo(
|
||||
AzToolsFramework::Prefab::TemplateId templateId,
|
||||
AzToolsFramework::Prefab::EntityAlias entityAlias,
|
||||
bool isMetadataEntity = false)
|
||||
: m_templateId(templateId)
|
||||
, m_entityAlias(entityAlias)
|
||||
, m_isMetadataEntity(isMetadataEntity)
|
||||
{
|
||||
}
|
||||
|
||||
AzToolsFramework::Prefab::TemplateId m_templateId;
|
||||
AzToolsFramework::Prefab::EntityAlias m_entityAlias;
|
||||
AZStd::vector<AzToolsFramework::Prefab::InstanceAlias> m_nestedInstanceAliases;
|
||||
bool m_isMetadataEntity{ false };
|
||||
};
|
||||
|
||||
bool ConnectToAssetProcessor();
|
||||
void DisconnectFromAssetProcessor();
|
||||
@@ -60,10 +80,22 @@ namespace AZ
|
||||
void SetParentEntity(const AZ::Entity& entity, const AZ::EntityId& parentId, bool onlySetIfInvalid);
|
||||
void PrintPrefab(AzToolsFramework::Prefab::TemplateId templateId);
|
||||
bool SavePrefab(AZ::IO::PathView outputPath, AzToolsFramework::Prefab::TemplateId templateId);
|
||||
void UpdateSliceEntityInstanceMappings(
|
||||
const AZ::SliceComponent::EntityIdToEntityIdMap& sliceEntityIdMap,
|
||||
const AZStd::string& currentInstanceAlias);
|
||||
AZStd::string GetInstanceAlias(const AZ::SliceComponent::SliceInstance& instance);
|
||||
|
||||
// Track all of the entity IDs created and the prefab entity aliases that map to them. This mapping is used
|
||||
// with nested slice conversion to remap parent entity IDs to the correct prefab entity IDs.
|
||||
AZStd::unordered_map<TemplateEntityIdPair, AzToolsFramework::Prefab::EntityAlias> m_aliasIdMapper;
|
||||
void RemapIdReferences(
|
||||
const AZStd::unordered_map<AZ::EntityId, SliceEntityMappingInfo>& idMapper,
|
||||
AzToolsFramework::Prefab::Instance* topLevelInstance,
|
||||
AzToolsFramework::Prefab::Instance* nestedInstance,
|
||||
SliceComponent::InstantiatedContainer* instantiatedEntities,
|
||||
SerializeContext* context);
|
||||
|
||||
// Track all of the entity IDs created and associate them with enough conversion information to know how to place the
|
||||
// entities in the correct place in the prefab hierarchy and fix up parent entity ID mappings to work with the nested
|
||||
// prefab schema.
|
||||
AZStd::unordered_map<AZ::EntityId, SliceEntityMappingInfo> m_aliasIdMapper;
|
||||
|
||||
// Track all of the created prefab template IDs on a slice conversion so that they can get removed at the end of the
|
||||
// conversion for that file.
|
||||
|
||||
@@ -36,7 +36,7 @@ namespace AzToolsFramework
|
||||
SliceConverterEditorEntityContextComponent() : EditorEntityContextComponent() {}
|
||||
|
||||
// Simple API to selectively disable this logic *only* when performing slice to prefab conversion.
|
||||
static void DisableOnContextEntityLogic()
|
||||
static inline void DisableOnContextEntityLogic()
|
||||
{
|
||||
m_enableOnContextEntityLogic = false;
|
||||
}
|
||||
|
||||
@@ -209,7 +209,11 @@ namespace AZ::SerializeContextTools
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Utilities::InspectSerializedFile(const char* filePath, SerializeContext* sc, const ObjectStream::ClassReadyCB& classCallback)
|
||||
bool Utilities::InspectSerializedFile(
|
||||
const char* filePath,
|
||||
SerializeContext* sc,
|
||||
const ObjectStream::ClassReadyCB& classCallback,
|
||||
Data::AssetFilterCB assetFilterCallback)
|
||||
{
|
||||
if (!AZ::IO::FileIOBase::GetInstance()->Exists(filePath))
|
||||
{
|
||||
@@ -248,9 +252,9 @@ namespace AZ::SerializeContextTools
|
||||
AZ::IO::MemoryStream stream(data.data(), fileLength);
|
||||
|
||||
ObjectStream::FilterDescriptor filter;
|
||||
// Never load dependencies. That's another file that would need to be processed
|
||||
// By default, never load dependencies. That's another file that would need to be processed
|
||||
// separately from this one.
|
||||
filter.m_assetCB = AZ::Data::AssetFilterNoAssetLoading;
|
||||
filter.m_assetCB = assetFilterCallback;
|
||||
if (!ObjectStream::LoadBlocking(&stream, *sc, classCallback, filter))
|
||||
{
|
||||
AZ_Printf("Verify", "Failed to deserialize '%s'\n", filePath);
|
||||
|
||||
@@ -39,7 +39,11 @@ namespace AZ
|
||||
|
||||
static AZStd::vector<AZ::Uuid> GetSystemComponents(const Application& application);
|
||||
|
||||
static bool InspectSerializedFile(const char* filePath, SerializeContext* sc, const ObjectStream::ClassReadyCB& classCallback);
|
||||
static bool InspectSerializedFile(
|
||||
const char* filePath,
|
||||
SerializeContext* sc,
|
||||
const ObjectStream::ClassReadyCB& classCallback,
|
||||
Data::AssetFilterCB assetFilterCallback = AZ::Data::AssetFilterNoAssetLoading);
|
||||
|
||||
private:
|
||||
Utilities() = delete;
|
||||
|
||||
@@ -122,29 +122,22 @@ namespace AWSMetrics
|
||||
//! @return Outcome of the operation.
|
||||
AZ::Outcome<void, AZStd::string> SendMetricsToFile(AZStd::shared_ptr<MetricsQueue> metricsQueue);
|
||||
|
||||
//! Check whether the consumer should flush the metrics queue.
|
||||
//! @return whether the limit is hit.
|
||||
bool ShouldSendMetrics();
|
||||
|
||||
//! Push metrics events to the front of the queue for retry.
|
||||
//! @param metricsEventsForRetry Metrics events for retry.
|
||||
void PushMetricsForRetry(MetricsQueue& metricsEventsForRetry);
|
||||
|
||||
void SubmitLocalMetricsAsync();
|
||||
|
||||
////////////////////////////////////////////
|
||||
// These data are protected by m_metricsMutex.
|
||||
AZStd::mutex m_metricsMutex;
|
||||
AZStd::chrono::system_clock::time_point m_lastSendMetricsTime;
|
||||
MetricsQueue m_metricsQueue;
|
||||
////////////////////////////////////////////
|
||||
AZStd::mutex m_metricsMutex; //!< Mutex to protect the metrics queue
|
||||
MetricsQueue m_metricsQueue; //!< Queue fo buffering the metrics events
|
||||
|
||||
AZStd::mutex m_metricsFileMutex; //!< Local metrics file is protected by m_metricsFileMutex
|
||||
AZStd::mutex m_metricsFileMutex; //!< Mutex to protect the local metrics file
|
||||
|
||||
AZStd::atomic<int> m_sendMetricsId;//!< Request ID for sending metrics
|
||||
|
||||
AZStd::thread m_consumerThread; //!< Thread to monitor and consume the metrics queue
|
||||
AZStd::atomic<bool> m_consumerTerminated;
|
||||
AZStd::thread m_monitorThread; //!< Thread to monitor and consume the metrics queue
|
||||
AZStd::atomic<bool> m_monitorTerminated;
|
||||
AZStd::binary_semaphore m_waitEvent;
|
||||
|
||||
// Client Configurations.
|
||||
AZStd::unique_ptr<ClientConfiguration> m_clientConfiguration;
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace AWSMetrics
|
||||
MetricsManager::MetricsManager()
|
||||
: m_clientConfiguration(AZStd::make_unique<ClientConfiguration>())
|
||||
, m_clientIdProvider(IdentityProvider::CreateIdentityProvider())
|
||||
, m_consumerTerminated(true)
|
||||
, m_monitorTerminated(true)
|
||||
, m_sendMetricsId(0)
|
||||
{
|
||||
}
|
||||
@@ -53,31 +53,27 @@ namespace AWSMetrics
|
||||
|
||||
void MetricsManager::StartMetrics()
|
||||
{
|
||||
if (!m_consumerTerminated)
|
||||
if (!m_monitorTerminated)
|
||||
{
|
||||
// The background thread has been started.
|
||||
return;
|
||||
}
|
||||
|
||||
m_consumerTerminated = false;
|
||||
|
||||
AZStd::lock_guard<AZStd::mutex> lock(m_metricsMutex);
|
||||
m_lastSendMetricsTime = AZStd::chrono::system_clock::now();
|
||||
m_monitorTerminated = false;
|
||||
|
||||
// Start a separate thread to monitor and consume the metrics queue.
|
||||
// Avoid using the job system since the worker is long-running over multiple frames
|
||||
m_consumerThread = AZStd::thread(AZStd::bind(&MetricsManager::MonitorMetricsQueue, this));
|
||||
m_monitorThread = AZStd::thread(AZStd::bind(&MetricsManager::MonitorMetricsQueue, this));
|
||||
}
|
||||
|
||||
void MetricsManager::MonitorMetricsQueue()
|
||||
{
|
||||
while (!m_consumerTerminated)
|
||||
// Continue to loop until the monitor is terminated.
|
||||
while (!m_monitorTerminated)
|
||||
{
|
||||
if (ShouldSendMetrics())
|
||||
{
|
||||
// Flush the metrics queue when the accumulated metrics size or time period hits the limit
|
||||
FlushMetricsAsync();
|
||||
}
|
||||
// The thread will wake up either when the metrics event queue is full (try_acquire_for call returns true),
|
||||
// or the flush period limit is hit (try_acquire_for call returns false).
|
||||
m_waitEvent.try_acquire_for(AZStd::chrono::seconds(m_clientConfiguration->GetQueueFlushPeriodInSeconds()));
|
||||
FlushMetricsAsync();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,6 +110,12 @@ namespace AWSMetrics
|
||||
AZStd::lock_guard<AZStd::mutex> lock(m_metricsMutex);
|
||||
m_metricsQueue.AddMetrics(metricsEvent);
|
||||
|
||||
if (m_metricsQueue.GetSizeInBytes() >= m_clientConfiguration->GetMaxQueueSizeInBytes())
|
||||
{
|
||||
// Flush the metrics queue when the accumulated metrics size hits the limit
|
||||
m_waitEvent.release();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -348,9 +350,6 @@ namespace AWSMetrics
|
||||
void MetricsManager::FlushMetricsAsync()
|
||||
{
|
||||
AZStd::lock_guard<AZStd::mutex> lock(m_metricsMutex);
|
||||
|
||||
m_lastSendMetricsTime = AZStd::chrono::system_clock::now();
|
||||
|
||||
if (m_metricsQueue.GetNumMetrics() == 0)
|
||||
{
|
||||
return;
|
||||
@@ -363,34 +362,20 @@ namespace AWSMetrics
|
||||
SendMetricsAsync(metricsToFlush);
|
||||
}
|
||||
|
||||
bool MetricsManager::ShouldSendMetrics()
|
||||
{
|
||||
AZStd::lock_guard<AZStd::mutex> lock(m_metricsMutex);
|
||||
|
||||
auto secondsSinceLastFlush = AZStd::chrono::duration_cast<AZStd::chrono::seconds>(AZStd::chrono::system_clock::now() - m_lastSendMetricsTime);
|
||||
if (secondsSinceLastFlush >= AZStd::chrono::seconds(m_clientConfiguration->GetQueueFlushPeriodInSeconds()) ||
|
||||
m_metricsQueue.GetSizeInBytes() >= m_clientConfiguration->GetMaxQueueSizeInBytes())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void MetricsManager::ShutdownMetrics()
|
||||
{
|
||||
if (m_consumerTerminated)
|
||||
if (m_monitorTerminated)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Terminate the consumer thread
|
||||
m_consumerTerminated = true;
|
||||
FlushMetricsAsync();
|
||||
// Terminate the monitor thread
|
||||
m_monitorTerminated = true;
|
||||
m_waitEvent.release();
|
||||
|
||||
if (m_consumerThread.joinable())
|
||||
if (m_monitorThread.joinable())
|
||||
{
|
||||
m_consumerThread.join();
|
||||
m_monitorThread.join();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -449,6 +434,12 @@ namespace AWSMetrics
|
||||
{
|
||||
AZStd::lock_guard<AZStd::mutex> lock(m_metricsMutex);
|
||||
m_metricsQueue.AddMetrics(offlineRecords[index]);
|
||||
|
||||
if (m_metricsQueue.GetSizeInBytes() >= m_clientConfiguration->GetMaxQueueSizeInBytes())
|
||||
{
|
||||
// Flush the metrics queue when the accumulated metrics size hits the limit
|
||||
m_waitEvent.release();
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the local metrics file after reading all its content.
|
||||
|
||||
@@ -355,6 +355,9 @@ namespace AWSMetrics
|
||||
|
||||
TEST_F(MetricsManagerTest, FlushMetrics_NonEmptyQueue_Success)
|
||||
{
|
||||
ResetClientConfig(true, (double)TestMetricsEventSizeInBytes * (MaxNumMetricsEvents + 1) / MbToBytes,
|
||||
DefaultFlushPeriodInSeconds, 1);
|
||||
|
||||
for (int index = 0; index < MaxNumMetricsEvents; ++index)
|
||||
{
|
||||
AZStd::vector<MetricsAttribute> metricsAttributes;
|
||||
@@ -377,7 +380,7 @@ namespace AWSMetrics
|
||||
TEST_F(MetricsManagerTest, ResetOfflineRecordingStatus_ResubmitLocalMetrics_Success)
|
||||
{
|
||||
// Disable offline recording in the config file.
|
||||
ResetClientConfig(false, 0.0, 0, 0);
|
||||
ResetClientConfig(false, (double)TestMetricsEventSizeInBytes * 2 / MbToBytes, 0, 0);
|
||||
|
||||
// Enable offline recording after initialize the metric manager.
|
||||
m_metricsManager->UpdateOfflineRecordingStatus(true);
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
"$type": "DX12::PlatformLimitsDescriptor",
|
||||
|
||||
"m_descriptorHeapLimits": {
|
||||
"DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV": [16384, 262144],
|
||||
"DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV": [1000000, 1000000],
|
||||
"DESCRIPTOR_HEAP_TYPE_SAMPLER": [2048, 2048],
|
||||
"DESCRIPTOR_HEAP_TYPE_RTV": [2048, 0],
|
||||
"DESCRIPTOR_HEAP_TYPE_DSV": [2048, 0]
|
||||
|
||||
@@ -295,6 +295,10 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float
|
||||
lightingOutput.m_diffuseColor.rgb += lightingOutput.m_specularColor.rgb; // add specular
|
||||
lightingOutput.m_specularColor.rgb = baseColor * (1.0 - lightingOutput.m_diffuseColor.w);
|
||||
}
|
||||
else
|
||||
{
|
||||
lightingOutput.m_diffuseColor.w = -1; // Disable subsurface scattering
|
||||
}
|
||||
|
||||
return lightingOutput;
|
||||
}
|
||||
@@ -341,4 +345,3 @@ ForwardPassOutput StandardPbr_ForwardPassPS_EDS(VSOutput IN, bool isFrontFace :
|
||||
#endif
|
||||
return OUT;
|
||||
}
|
||||
|
||||
|
||||
@@ -322,7 +322,14 @@
|
||||
"Pass": "AuxGeomPass",
|
||||
"Attachment": "ColorInputOutput"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"LocalSlot": "DepthInputOutput",
|
||||
"AttachmentRef": {
|
||||
"Pass": "DepthPrePass",
|
||||
"Attachment": "Depth"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -427,6 +427,13 @@
|
||||
"Pass": "DebugOverlayPass",
|
||||
"Attachment": "InputOutput"
|
||||
}
|
||||
},
|
||||
{
|
||||
"LocalSlot": "DepthInputOutput",
|
||||
"AttachmentRef": {
|
||||
"Pass": "DepthPrePass",
|
||||
"Attachment": "Depth"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -24,9 +24,9 @@
|
||||
},
|
||||
"ImageDescriptor": {
|
||||
"Format": "R16G16B16A16_FLOAT",
|
||||
"MipLevels": "8",
|
||||
"SharedQueueMask": "Graphics"
|
||||
}
|
||||
},
|
||||
"GenerateFullMipChain": true
|
||||
}
|
||||
],
|
||||
"Connections": [
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user