Merge branch 'development' of https://github.com/aws-lumberyard/o3de into mp_systemcomp_cleanup
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()
|
||||
|
||||
@@ -321,7 +321,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;
|
||||
}
|
||||
@@ -352,7 +352,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(
|
||||
@@ -974,7 +974,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
|
||||
|
||||
@@ -26,6 +26,7 @@ namespace AZStd
|
||||
using std::exp2;
|
||||
using std::floor;
|
||||
using std::fmod;
|
||||
using std::pow;
|
||||
using std::round;
|
||||
using std::sin;
|
||||
using std::sqrt;
|
||||
|
||||
@@ -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} }
|
||||
|
||||
|
||||
+1
@@ -13,6 +13,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <AzManipulatorTestFramework/AzManipulatorTestFramework.h>
|
||||
#include <AzManipulatorTestFramework/AzManipulatorTestFrameworkUtils.h>
|
||||
#include <AzManipulatorTestFramework/ImmediateModeActionDispatcher.h>
|
||||
#include <AzManipulatorTestFramework/IndirectManipulatorViewportInteraction.h>
|
||||
#include <AzToolsFramework/ViewportSelection/EditorInteractionSystemViewportSelectionRequestBus.h>
|
||||
|
||||
@@ -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()
|
||||
|
||||
+49
-11
@@ -12,6 +12,7 @@
|
||||
#include <AzToolsFramework/AssetBrowser/Search/Filter.h>
|
||||
#include <AzToolsFramework/AssetBrowser/Entries/FolderAssetBrowserEntry.h>
|
||||
#include <AzToolsFramework/AssetBrowser/Entries/SourceAssetBrowserEntry.h>
|
||||
#include <AzCore/Console/IConsole.h>
|
||||
|
||||
AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option")
|
||||
#include <AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.h>
|
||||
@@ -22,6 +23,9 @@ AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option")
|
||||
#include <QCollator>
|
||||
AZ_POP_DISABLE_WARNING
|
||||
|
||||
AZ_CVAR(
|
||||
bool, ed_useNewAssetBrowserTableView, false, nullptr, AZ::ConsoleFunctorFlags::Null,
|
||||
"Use the new AssetBrowser TableView for searching assets.");
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
namespace AssetBrowser
|
||||
@@ -31,7 +35,11 @@ namespace AzToolsFramework
|
||||
AssetBrowserFilterModel::AssetBrowserFilterModel(QObject* parent)
|
||||
: QSortFilterProxyModel(parent)
|
||||
{
|
||||
m_showColumn.insert(AssetBrowserModel::m_column);
|
||||
m_showColumn.insert(aznumeric_cast<int>(AssetBrowserEntry::Column::DisplayName));
|
||||
if (ed_useNewAssetBrowserTableView)
|
||||
{
|
||||
m_showColumn.insert(aznumeric_cast<int>(AssetBrowserEntry::Column::Path));
|
||||
}
|
||||
m_collator.setNumericMode(true);
|
||||
AssetBrowserComponentNotificationBus::Handler::BusConnect();
|
||||
}
|
||||
@@ -128,28 +136,58 @@ namespace AzToolsFramework
|
||||
auto compFilter = qobject_cast<QSharedPointer<const CompositeFilter> >(m_filter);
|
||||
if (compFilter)
|
||||
{
|
||||
auto& subFilters = compFilter->GetSubFilters();
|
||||
auto it = AZStd::find_if(subFilters.begin(), subFilters.end(), [subFilters](FilterConstType filter) -> bool
|
||||
const auto& subFilters = compFilter->GetSubFilters();
|
||||
|
||||
const auto compositeFilterIterator = AZStd::find_if(subFilters.cbegin(), subFilters.cend(), [subFilters](FilterConstType filter) -> bool
|
||||
{
|
||||
auto assetTypeFilter = qobject_cast<QSharedPointer<const CompositeFilter> >(filter);
|
||||
const auto assetTypeFilter = qobject_cast<QSharedPointer<const CompositeFilter> >(filter);
|
||||
return !assetTypeFilter.isNull();
|
||||
});
|
||||
if (it != subFilters.end())
|
||||
|
||||
if (compositeFilterIterator != subFilters.end())
|
||||
{
|
||||
m_assetTypeFilter = qobject_cast<QSharedPointer<const CompositeFilter> >(*it);
|
||||
m_assetTypeFilter = qobject_cast<QSharedPointer<const CompositeFilter> >(*compositeFilterIterator);
|
||||
}
|
||||
it = AZStd::find_if(subFilters.begin(), subFilters.end(), [subFilters](FilterConstType filter) -> bool
|
||||
|
||||
const auto compStringFilterIter = AZStd::find_if(subFilters.cbegin(), subFilters.cend(), [](FilterConstType filter) -> bool
|
||||
{
|
||||
auto stringFilter = qobject_cast<QSharedPointer<const StringFilter> >(filter);
|
||||
return !stringFilter.isNull();
|
||||
//The real StringFilter is really a CompositeFilter with just one StringFilter in its subfilter list
|
||||
//To know if it is actually a StringFilter we have to get that subfilter and check if it is a Stringfilter.
|
||||
const auto stringCompositeFilter = qobject_cast<QSharedPointer<const CompositeFilter> >(filter);
|
||||
bool isStringFilter = false;
|
||||
if (stringCompositeFilter)
|
||||
{
|
||||
const auto& stringSubfilters = stringCompositeFilter->GetSubFilters();
|
||||
auto canBeCasted = [](FilterConstType filt) -> bool
|
||||
{
|
||||
auto strFilter = qobject_cast<QSharedPointer<const StringFilter>>(filt);
|
||||
return !strFilter.isNull();
|
||||
};
|
||||
const auto stringSubfliterConstIter = AZStd::find_if(stringSubfilters.cbegin(), stringSubfilters.cend(), canBeCasted);
|
||||
|
||||
//A Composite StringFilter will only have just one subfilter and nothing more.
|
||||
if (stringSubfliterConstIter != stringSubfilters.end() && stringSubfilters.size() == 1)
|
||||
{
|
||||
isStringFilter = true;
|
||||
}
|
||||
}
|
||||
|
||||
return isStringFilter;
|
||||
});
|
||||
if (it != subFilters.end())
|
||||
if (compStringFilterIter != subFilters.end())
|
||||
{
|
||||
m_stringFilter = qobject_cast<QSharedPointer<const StringFilter> >(*it);
|
||||
const auto compStringFilter = qobject_cast<QSharedPointer<const CompositeFilter>>(*compStringFilterIter);
|
||||
|
||||
if (!compStringFilter->GetSubFilters().isEmpty() && compStringFilter->GetSubFilters()[0])
|
||||
{
|
||||
m_stringFilter = qobject_cast<QSharedPointer<const StringFilter>>(compStringFilter->GetSubFilters()[0]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
invalidateFilter();
|
||||
Q_EMIT filterChanged();
|
||||
emit stringFilterPopulated(!m_stringFilter.isNull());
|
||||
}
|
||||
|
||||
void AssetBrowserFilterModel::filterUpdatedSlot()
|
||||
|
||||
+3
-3
@@ -45,15 +45,15 @@ namespace AzToolsFramework
|
||||
//asset type filtering
|
||||
void SetFilter(FilterConstType filter);
|
||||
void FilterUpdatedSlotImmediate();
|
||||
|
||||
const FilterConstType& GetFilter() const { return m_filter; }
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// AssetBrowserComponentNotificationBus
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void OnAssetBrowserComponentReady() override;
|
||||
|
||||
Q_SIGNALS:
|
||||
void stringFilterPopulated(bool);
|
||||
void filterChanged();
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//QSortFilterProxyModel
|
||||
protected:
|
||||
@@ -68,7 +68,7 @@ namespace AzToolsFramework
|
||||
protected:
|
||||
//set for filtering columns
|
||||
//if the column is in the set the column is not filtered and is shown
|
||||
AZStd::fixed_unordered_set<int, 3, static_cast<int>(AssetBrowserEntry::Column::Count)> m_showColumn;
|
||||
AZStd::fixed_unordered_set<int, 3, aznumeric_cast<int>(AssetBrowserEntry::Column::Count)> m_showColumn;
|
||||
bool m_alreadyRecomputingFilters = false;
|
||||
//asset source name match filter
|
||||
FilterConstType m_filter;
|
||||
|
||||
@@ -27,8 +27,6 @@ namespace AzToolsFramework
|
||||
{
|
||||
namespace AssetBrowser
|
||||
{
|
||||
const int AssetBrowserModel::m_column = static_cast<int>(AssetBrowserEntry::Column::DisplayName);
|
||||
|
||||
AssetBrowserModel::AssetBrowserModel(QObject* parent)
|
||||
: QAbstractItemModel(parent)
|
||||
, m_rootEntry(nullptr)
|
||||
@@ -143,8 +141,9 @@ namespace AzToolsFramework
|
||||
|
||||
if (parent.isValid())
|
||||
{
|
||||
if ((parent.column() != static_cast<int>(AssetBrowserEntry::Column::DisplayName)) &&
|
||||
(parent.column() != static_cast<int>(AssetBrowserEntry::Column::Name)))
|
||||
if ((parent.column() != aznumeric_cast<int>(AssetBrowserEntry::Column::DisplayName)) &&
|
||||
(parent.column() != aznumeric_cast<int>(AssetBrowserEntry::Column::Name)) &&
|
||||
(parent.column() != aznumeric_cast<int>(AssetBrowserEntry::Column::Path)))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@@ -164,7 +163,7 @@ namespace AzToolsFramework
|
||||
|
||||
int AssetBrowserModel::columnCount(const QModelIndex& /*parent*/) const
|
||||
{
|
||||
return static_cast<int>(AssetBrowserEntry::Column::Count);
|
||||
return aznumeric_cast<int>(AssetBrowserEntry::Column::Count);
|
||||
}
|
||||
|
||||
QVariant AssetBrowserModel::data(const QModelIndex& index, int role) const
|
||||
@@ -393,7 +392,7 @@ namespace AzToolsFramework
|
||||
}
|
||||
|
||||
int row = entry->row();
|
||||
index = createIndex(row, m_column, entry);
|
||||
index = createIndex(row, aznumeric_cast<int>(AssetBrowserEntry::Column::DisplayName), entry);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -91,8 +91,6 @@ namespace AzToolsFramework
|
||||
static void SourceIndexesToAssetIds(const QModelIndexList& indexes, AZStd::vector<AZ::Data::AssetId>& assetIds);
|
||||
static void SourceIndexesToAssetDatabaseEntries(const QModelIndexList& indexes, AZStd::vector<AssetBrowserEntry*>& entries);
|
||||
|
||||
const static int m_column;
|
||||
|
||||
private:
|
||||
AZStd::shared_ptr<RootAssetBrowserEntry> m_rootEntry;
|
||||
bool m_loaded;
|
||||
|
||||
+136
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* 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 <AssetBrowser/AssetBrowserFilterModel.h>
|
||||
#include <AssetBrowser/AssetBrowserTableModel.h>
|
||||
#include <AzToolsFramework/AssetBrowser/Entries/AssetBrowserEntry.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
namespace AssetBrowser
|
||||
{
|
||||
AssetBrowserTableModel::AssetBrowserTableModel(QObject* parent /* = nullptr */)
|
||||
: QSortFilterProxyModel(parent)
|
||||
{
|
||||
setDynamicSortFilter(false);
|
||||
}
|
||||
|
||||
void AssetBrowserTableModel::setSourceModel(QAbstractItemModel* sourceModel)
|
||||
{
|
||||
m_filterModel = qobject_cast<AssetBrowserFilterModel*>(sourceModel);
|
||||
AZ_Assert(
|
||||
m_filterModel,
|
||||
"Error in AssetBrowserTableModel initialization, class expects source model to be an AssetBrowserFilterModel.");
|
||||
QSortFilterProxyModel::setSourceModel(sourceModel);
|
||||
}
|
||||
|
||||
QModelIndex AssetBrowserTableModel::mapToSource(const QModelIndex& proxyIndex) const
|
||||
{
|
||||
Q_ASSERT(!proxyIndex.isValid() || proxyIndex.model() == this);
|
||||
if (!proxyIndex.isValid())
|
||||
{
|
||||
return QModelIndex();
|
||||
}
|
||||
return m_indexMap[proxyIndex.row()];
|
||||
}
|
||||
|
||||
QVariant AssetBrowserTableModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
{
|
||||
if (role == Qt::DisplayRole && orientation == Qt::Horizontal)
|
||||
{
|
||||
return tr(AssetBrowserEntry::m_columnNames[section]);
|
||||
}
|
||||
return QSortFilterProxyModel::headerData(section, orientation, role);
|
||||
}
|
||||
|
||||
QVariant AssetBrowserTableModel::data(const QModelIndex& index, int role) const
|
||||
{
|
||||
auto sourceIndex = mapToSource(index);
|
||||
if (!sourceIndex.isValid())
|
||||
{
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
AssetBrowserEntry* entry = GetAssetEntry(sourceIndex);
|
||||
if (entry == nullptr)
|
||||
{
|
||||
AZ_Assert(false, "AssetBrowserTableModel - QModelIndex does not reference an AssetEntry. Source model is not valid.");
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
return sourceIndex.data(role);
|
||||
}
|
||||
|
||||
QModelIndex AssetBrowserTableModel::index(int row, int column, const QModelIndex& parent) const
|
||||
{
|
||||
return parent.isValid() ? QModelIndex() : createIndex(row, column, m_indexMap[row].internalPointer());
|
||||
}
|
||||
|
||||
int AssetBrowserTableModel::rowCount(const QModelIndex& parent) const
|
||||
{
|
||||
return !parent.isValid() ? m_indexMap.size() : 0;
|
||||
}
|
||||
|
||||
int AssetBrowserTableModel::BuildTableModelMap(
|
||||
const QAbstractItemModel* model, const QModelIndex& parent /*= QModelIndex()*/, int row /*= 0*/)
|
||||
{
|
||||
int rows = model ? model->rowCount(parent) : 0;
|
||||
for (int i = 0; i < rows; ++i)
|
||||
{
|
||||
QModelIndex index = model->index(i, 0, parent);
|
||||
AssetBrowserEntry* entry = GetAssetEntry(m_filterModel->mapToSource(index));
|
||||
//We only wanna see the source assets.
|
||||
if (entry->GetEntryType() == AssetBrowserEntry::AssetEntryType::Source)
|
||||
{
|
||||
beginInsertRows(parent, row, row);
|
||||
m_indexMap[row] = index;
|
||||
endInsertRows();
|
||||
|
||||
Q_EMIT dataChanged(index, index);
|
||||
++row;
|
||||
}
|
||||
|
||||
if (model->hasChildren(index))
|
||||
{
|
||||
row = BuildTableModelMap(model, index, row);
|
||||
}
|
||||
}
|
||||
return row;
|
||||
}
|
||||
|
||||
AssetBrowserEntry* AssetBrowserTableModel::GetAssetEntry(QModelIndex index) const
|
||||
{
|
||||
if (index.isValid())
|
||||
{
|
||||
return static_cast<AssetBrowserEntry*>(index.internalPointer());
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Error("AssetBrowser", false, "Invalid Source Index provided to GetAssetEntry.");
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void AssetBrowserTableModel::UpdateTableModelMaps()
|
||||
{
|
||||
emit layoutAboutToBeChanged();
|
||||
if (!m_indexMap.isEmpty())
|
||||
{
|
||||
beginRemoveRows(m_indexMap.first(), m_indexMap.first().row(), m_indexMap.last().row());
|
||||
m_indexMap.clear();
|
||||
endRemoveRows();
|
||||
}
|
||||
BuildTableModelMap(sourceModel());
|
||||
emit layoutChanged();
|
||||
}
|
||||
} // namespace AssetBrowser
|
||||
} // namespace AzToolsFramework
|
||||
#include "AssetBrowser/moc_AssetBrowserTableModel.cpp"
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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 <AzCore/Asset/AssetCommon.h>
|
||||
#include <QSortFilterProxyModel>
|
||||
#include <QPointer>
|
||||
#endif
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
namespace AssetBrowser
|
||||
{
|
||||
class AssetBrowserFilterModel;
|
||||
class AssetBrowserEntry;
|
||||
|
||||
class AssetBrowserTableModel
|
||||
: public QSortFilterProxyModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(AssetBrowserTableModel, AZ::SystemAllocator, 0);
|
||||
explicit AssetBrowserTableModel(QObject* parent = nullptr);
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// QSortFilterProxyModel
|
||||
void setSourceModel(QAbstractItemModel* sourceModel) override;
|
||||
QModelIndex mapToSource(const QModelIndex& proxyIndex) const override;
|
||||
QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override;
|
||||
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
|
||||
|
||||
public Q_SLOTS:
|
||||
void UpdateTableModelMaps();
|
||||
|
||||
protected:
|
||||
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||
QVariant headerData(int section, Qt::Orientation orientation, int role /* = Qt::DisplayRole */) const override;
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
private:
|
||||
AssetBrowserEntry* GetAssetEntry(QModelIndex index) const;
|
||||
int BuildTableModelMap(const QAbstractItemModel* model, const QModelIndex& parent = QModelIndex(), int row = 0);
|
||||
|
||||
private:
|
||||
QPointer<AssetBrowserFilterModel> m_filterModel;
|
||||
QMap<int, QModelIndex> m_indexMap;
|
||||
};
|
||||
} // namespace AssetBrowser
|
||||
} // namespace AzToolsFramework
|
||||
+3
@@ -46,6 +46,7 @@ namespace AzToolsFramework
|
||||
const char* AssetBrowserEntry::m_columnNames[] =
|
||||
{
|
||||
"Name",
|
||||
"Path",
|
||||
"Source ID",
|
||||
"Fingerprint",
|
||||
"Guid",
|
||||
@@ -128,6 +129,8 @@ namespace AzToolsFramework
|
||||
return QString::fromUtf8(m_name.c_str());
|
||||
case Column::DisplayName:
|
||||
return m_displayName;
|
||||
case Column::Path:
|
||||
return m_displayPath;
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
+2
@@ -68,6 +68,7 @@ namespace AzToolsFramework
|
||||
enum class Column
|
||||
{
|
||||
Name,
|
||||
Path,
|
||||
SourceID,
|
||||
Fingerprint,
|
||||
Guid,
|
||||
@@ -135,6 +136,7 @@ namespace AzToolsFramework
|
||||
protected:
|
||||
AZStd::string m_name;
|
||||
QString m_displayName;
|
||||
QString m_displayPath;
|
||||
AZStd::string m_relativePath;
|
||||
AZStd::string m_fullPath;
|
||||
AZStd::vector<AssetBrowserEntry*> m_children;
|
||||
|
||||
+1
@@ -43,6 +43,7 @@ namespace AzToolsFramework
|
||||
void FolderAssetBrowserEntry::UpdateChildPaths(AssetBrowserEntry* child) const
|
||||
{
|
||||
child->m_relativePath = m_relativePath + AZ_CORRECT_DATABASE_SEPARATOR + child->m_name;
|
||||
child->m_displayPath = QString::fromUtf8(child->m_relativePath.c_str());
|
||||
child->m_fullPath = m_fullPath + AZ_CORRECT_DATABASE_SEPARATOR + child->m_name;
|
||||
AssetBrowserEntry::UpdateChildPaths(child);
|
||||
}
|
||||
|
||||
+3
@@ -286,6 +286,9 @@ namespace AzToolsFramework
|
||||
product->m_assetType = productWithUuidDatabaseEntry.second.m_assetType;
|
||||
product->m_assetType.ToString(product->m_assetTypeString);
|
||||
AZ::Data::AssetCatalogRequestBus::BroadcastResult(product->m_relativePath, &AZ::Data::AssetCatalogRequests::GetAssetPathById, assetId);
|
||||
QString displayPath = QString::fromUtf8(product->m_relativePath.c_str());
|
||||
displayPath.remove(QString(AZ_CORRECT_DATABASE_SEPARATOR + QString::fromUtf8(product->m_name.c_str())));
|
||||
product->m_displayPath = displayPath;
|
||||
EntryCache::GetInstance()->m_productAssetIdMap[assetId] = product;
|
||||
|
||||
if (needsAdd)
|
||||
|
||||
+178
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
* 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 <API/EditorAssetSystemAPI.h>
|
||||
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
|
||||
#include <AzFramework/StringFunc/StringFunc.h>
|
||||
|
||||
#include <AzToolsFramework/AssetBrowser/AssetBrowserBus.h>
|
||||
#include <AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.h>
|
||||
#include <AzToolsFramework/AssetBrowser/AssetBrowserModel.h>
|
||||
#include <AzToolsFramework/AssetBrowser/Entries/AssetBrowserEntryCache.h>
|
||||
#include <AzToolsFramework/AssetBrowser/Entries/ProductAssetBrowserEntry.h>
|
||||
#include <AzToolsFramework/AssetBrowser/Entries/SourceAssetBrowserEntry.h>
|
||||
#include <AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.h>
|
||||
#include <AzToolsFramework/AssetBrowser/Views/EntryDelegate.h>
|
||||
|
||||
AZ_PUSH_DISABLE_WARNING(
|
||||
4244 4251 4800, "-Wunknown-warning-option") // conversion from 'int' to 'float', possible loss of data, needs to have dll-interface to
|
||||
// be used by clients of class 'QFlags<QPainter::RenderHint>::Int': forcing value to bool
|
||||
// 'true' or 'false' (performance warning)
|
||||
#include <QCoreApplication>
|
||||
#include <QHeaderView>
|
||||
#include <QMenu>
|
||||
#include <QMouseEvent>
|
||||
#include <QPainter>
|
||||
#include <QPen>
|
||||
#include <QTimer>
|
||||
AZ_POP_DISABLE_WARNING
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
namespace AssetBrowser
|
||||
{
|
||||
AssetBrowserTableView::AssetBrowserTableView(QWidget* parent)
|
||||
: QTableView(parent)
|
||||
, m_delegate(new EntryDelegate(this))
|
||||
{
|
||||
setSortingEnabled(true);
|
||||
setItemDelegate(m_delegate);
|
||||
verticalHeader()->hide();
|
||||
setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
|
||||
setMouseTracking(true);
|
||||
setSortingEnabled(false);
|
||||
setSelectionMode(QAbstractItemView::SingleSelection);
|
||||
|
||||
connect(this, &QTableView::customContextMenuRequested, this, &AssetBrowserTableView::OnContextMenu);
|
||||
|
||||
AssetBrowserViewRequestBus::Handler::BusConnect();
|
||||
AssetBrowserComponentNotificationBus::Handler::BusConnect();
|
||||
}
|
||||
|
||||
AssetBrowserTableView::~AssetBrowserTableView()
|
||||
{
|
||||
AssetBrowserViewRequestBus::Handler::BusDisconnect();
|
||||
AssetBrowserComponentNotificationBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
void AssetBrowserTableView::setModel(QAbstractItemModel* model)
|
||||
{
|
||||
m_tableModel = qobject_cast<AssetBrowserTableModel*>(model);
|
||||
AZ_Assert(m_tableModel, "Expecting AssetBrowserTableModel");
|
||||
m_sourceFilterModel = qobject_cast<AssetBrowserFilterModel*>(m_tableModel->sourceModel());
|
||||
QTableView::setModel(model);
|
||||
connect(m_tableModel, &AssetBrowserTableModel::layoutChanged, this, &AssetBrowserTableView::layoutChangedSlot);
|
||||
|
||||
horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeMode::Stretch);
|
||||
horizontalHeader()->setSectionResizeMode(1, QHeaderView::ResizeMode::Stretch);
|
||||
}
|
||||
|
||||
void AssetBrowserTableView::SetName(const QString& name)
|
||||
{
|
||||
m_name = name;
|
||||
bool isAssetBrowserComponentReady = false;
|
||||
AssetBrowserComponentRequestBus::BroadcastResult(isAssetBrowserComponentReady, &AssetBrowserComponentRequests::AreEntriesReady);
|
||||
if (isAssetBrowserComponentReady)
|
||||
{
|
||||
OnAssetBrowserComponentReady();
|
||||
}
|
||||
}
|
||||
|
||||
AZStd::vector<AssetBrowserEntry*> AssetBrowserTableView::GetSelectedAssets() const
|
||||
{
|
||||
QModelIndexList sourceIndexes;
|
||||
for (const auto& index : selectedIndexes())
|
||||
{
|
||||
if (index.column() == 0)
|
||||
{
|
||||
sourceIndexes.push_back(m_sourceFilterModel->mapToSource(m_tableModel->mapToSource(index)));
|
||||
}
|
||||
}
|
||||
|
||||
AZStd::vector<AssetBrowserEntry*> entries;
|
||||
AssetBrowserModel::SourceIndexesToAssetDatabaseEntries(sourceIndexes, entries);
|
||||
return entries;
|
||||
}
|
||||
|
||||
void AssetBrowserTableView::selectionChanged(const QItemSelection& selected, const QItemSelection& deselected)
|
||||
{
|
||||
QTableView::selectionChanged(selected, deselected);
|
||||
Q_EMIT selectionChangedSignal(selected, deselected);
|
||||
}
|
||||
|
||||
void AssetBrowserTableView::rowsAboutToBeRemoved(const QModelIndex& parent, int start, int end)
|
||||
{
|
||||
// if selected entry is being removed, clear selection so not to select (and attempt to preview) other entries potentially
|
||||
// marked for deletion
|
||||
if (selectionModel() && selectionModel()->selectedIndexes().size() == 1)
|
||||
{
|
||||
QModelIndex selectedIndex = selectionModel()->selectedIndexes().first();
|
||||
QModelIndex parentSelectedIndex = selectedIndex.parent();
|
||||
if (parentSelectedIndex == parent && selectedIndex.row() >= start && selectedIndex.row() <= end)
|
||||
{
|
||||
selectionModel()->clear();
|
||||
}
|
||||
}
|
||||
QTableView::rowsAboutToBeRemoved(parent, start, end);
|
||||
}
|
||||
|
||||
void AssetBrowserTableView::layoutChangedSlot(
|
||||
[[maybe_unused]] const QList<QPersistentModelIndex>& parents, [[maybe_unused]] QAbstractItemModel::LayoutChangeHint hint)
|
||||
{
|
||||
scrollToTop();
|
||||
}
|
||||
|
||||
void AssetBrowserTableView::SelectProduct([[maybe_unused]] AZ::Data::AssetId assetID)
|
||||
{
|
||||
}
|
||||
|
||||
void AssetBrowserTableView::SelectFileAtPath([[maybe_unused]] const AZStd::string& assetPath)
|
||||
{
|
||||
}
|
||||
|
||||
void AssetBrowserTableView::ClearFilter()
|
||||
{
|
||||
emit ClearStringFilter();
|
||||
emit ClearTypeFilter();
|
||||
m_sourceFilterModel->FilterUpdatedSlotImmediate();
|
||||
}
|
||||
|
||||
void AssetBrowserTableView::Update()
|
||||
{
|
||||
update();
|
||||
}
|
||||
|
||||
void AssetBrowserTableView::OnAssetBrowserComponentReady()
|
||||
{
|
||||
}
|
||||
|
||||
void AssetBrowserTableView::OnContextMenu([[maybe_unused]] const QPoint& point)
|
||||
{
|
||||
const auto& selectedAssets = GetSelectedAssets();
|
||||
if (selectedAssets.size() != 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
QMenu menu(this);
|
||||
AssetBrowserInteractionNotificationBus::Broadcast(
|
||||
&AssetBrowserInteractionNotificationBus::Events::AddContextMenuActions, this, &menu, selectedAssets);
|
||||
if (!menu.isEmpty())
|
||||
{
|
||||
menu.exec(QCursor::pos());
|
||||
}
|
||||
}
|
||||
} // namespace AssetBrowser
|
||||
} // namespace AzToolsFramework
|
||||
#include "AssetBrowser/Views/moc_AssetBrowserTableView.cpp"
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 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 <AzCore/Asset/AssetCommon.h>
|
||||
#include <AzCore/Memory/SystemAllocator.h>
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
|
||||
#include <AzToolsFramework/AssetBrowser/AssetBrowserBus.h>
|
||||
#include <AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h>
|
||||
|
||||
#include <QModelIndex>
|
||||
#include <QPointer>
|
||||
#include <QTableView>
|
||||
#endif
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
namespace AssetBrowser
|
||||
{
|
||||
class AssetBrowserEntry;
|
||||
class AssetBrowserTableModel;
|
||||
class AssetBrowserFilterModel;
|
||||
class EntryDelegate;
|
||||
|
||||
class AssetBrowserTableView //! Table view that displays the asset browser entries in a list.
|
||||
: public QTableView
|
||||
, public AssetBrowserViewRequestBus::Handler
|
||||
, public AssetBrowserComponentNotificationBus::Handler
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit AssetBrowserTableView(QWidget* parent = nullptr);
|
||||
~AssetBrowserTableView() override;
|
||||
|
||||
void setModel(QAbstractItemModel *model) override;
|
||||
void SetName(const QString& name);
|
||||
|
||||
AZStd::vector<AssetBrowserEntry*> GetSelectedAssets() const;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// AssetBrowserViewRequestBus
|
||||
virtual void SelectProduct(AZ::Data::AssetId assetID) override;
|
||||
virtual void SelectFileAtPath(const AZStd::string& assetPath) override;
|
||||
virtual void ClearFilter() override;
|
||||
virtual void Update() override;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// AssetBrowserComponentNotificationBus
|
||||
void OnAssetBrowserComponentReady() override;
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
Q_SIGNALS:
|
||||
void selectionChangedSignal(const QItemSelection& selected, const QItemSelection& deselected);
|
||||
void ClearStringFilter();
|
||||
void ClearTypeFilter();
|
||||
|
||||
protected Q_SLOTS:
|
||||
void selectionChanged(const QItemSelection& selected, const QItemSelection& deselected) override;
|
||||
void rowsAboutToBeRemoved(const QModelIndex& parent, int start, int end) override;
|
||||
void layoutChangedSlot(const QList<QPersistentModelIndex> &parents = QList<QPersistentModelIndex>(),
|
||||
QAbstractItemModel::LayoutChangeHint hint = QAbstractItemModel::NoLayoutChangeHint);
|
||||
|
||||
private:
|
||||
QString m_name;
|
||||
QPointer<AssetBrowserTableModel> m_tableModel = nullptr;
|
||||
QPointer<AssetBrowserFilterModel> m_sourceFilterModel = nullptr;
|
||||
EntryDelegate* m_delegate = nullptr;
|
||||
|
||||
private Q_SLOTS:
|
||||
void OnContextMenu(const QPoint& point);
|
||||
};
|
||||
} // namespace AssetBrowser
|
||||
} // namespace AzToolsFramework
|
||||
+4
-1
@@ -53,6 +53,7 @@ namespace AzToolsFramework
|
||||
setSortingEnabled(true);
|
||||
setItemDelegate(m_delegate);
|
||||
header()->hide();
|
||||
|
||||
setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
|
||||
setMouseTracking(true);
|
||||
@@ -99,8 +100,9 @@ namespace AzToolsFramework
|
||||
|
||||
AZStd::vector<AssetBrowserEntry*> AssetBrowserTreeView::GetSelectedAssets() const
|
||||
{
|
||||
const QModelIndexList& selectedIndexes = selectionModel()->selectedRows();
|
||||
QModelIndexList sourceIndexes;
|
||||
for (const auto& index : selectedIndexes())
|
||||
for (const auto& index : selectedIndexes)
|
||||
{
|
||||
sourceIndexes.push_back(m_assetBrowserSortFilterProxyModel->mapToSource(index));
|
||||
}
|
||||
@@ -172,6 +174,7 @@ namespace AzToolsFramework
|
||||
|
||||
void AssetBrowserTreeView::OnAssetBrowserComponentReady()
|
||||
{
|
||||
hideColumn(aznumeric_cast<int>(AssetBrowserEntry::Column::Path));
|
||||
if (!m_name.isEmpty())
|
||||
{
|
||||
auto crc = AZ::Crc32(m_name.toUtf8().data());
|
||||
|
||||
+22
-22
@@ -74,34 +74,34 @@ namespace AzToolsFramework
|
||||
QPoint iconTopLeft(remainingRect.x(), remainingRect.y() + (remainingRect.height() / 2) - (m_iconSize / 2));
|
||||
|
||||
auto sourceEntry = azrtti_cast<const SourceAssetBrowserEntry*>(entry);
|
||||
|
||||
int thumbX = DrawThumbnail(painter, iconTopLeft, iconSize, entry->GetThumbnailKey());
|
||||
|
||||
QPalette actualPalette(option.palette);
|
||||
|
||||
if (sourceEntry)
|
||||
if (index.column() == aznumeric_cast<int>(AssetBrowserEntry::Column::Name))
|
||||
{
|
||||
if (m_showSourceControl)
|
||||
int thumbX = DrawThumbnail(painter, iconTopLeft, iconSize, entry->GetThumbnailKey());
|
||||
if (sourceEntry)
|
||||
{
|
||||
DrawThumbnail(painter, iconTopLeft, iconSize, sourceEntry->GetSourceControlThumbnailKey());
|
||||
}
|
||||
// sources with no children should be greyed out.
|
||||
if (sourceEntry->GetChildCount() == 0)
|
||||
{
|
||||
isEnabled = false; // draw in disabled style.
|
||||
actualPalette.setCurrentColorGroup(QPalette::Disabled);
|
||||
if (m_showSourceControl)
|
||||
{
|
||||
DrawThumbnail(painter, iconTopLeft, iconSize, sourceEntry->GetSourceControlThumbnailKey());
|
||||
}
|
||||
// sources with no children should be greyed out.
|
||||
if (sourceEntry->GetChildCount() == 0)
|
||||
{
|
||||
isEnabled = false; // draw in disabled style.
|
||||
actualPalette.setCurrentColorGroup(QPalette::Disabled);
|
||||
}
|
||||
}
|
||||
|
||||
remainingRect.adjust(thumbX, 0, 0, 0); // bump it to the right by the size of the thumbnail
|
||||
remainingRect.adjust(ENTRY_SPACING_LEFT_PIXELS, 0, 0, 0); // bump it to the right by the spacing.
|
||||
}
|
||||
QString displayString = index.column() == aznumeric_cast<int>(AssetBrowserEntry::Column::Name)
|
||||
? qvariant_cast<QString>(entry->data(aznumeric_cast<int>(AssetBrowserEntry::Column::Name)))
|
||||
: qvariant_cast<QString>(entry->data(aznumeric_cast<int>(AssetBrowserEntry::Column::Path)));
|
||||
|
||||
remainingRect.adjust(thumbX, 0, 0, 0); // bump it to the right by the size of the thumbnail
|
||||
remainingRect.adjust(ENTRY_SPACING_LEFT_PIXELS, 0, 0, 0); // bump it to the right by the spacing.
|
||||
|
||||
style->drawItemText(painter,
|
||||
remainingRect,
|
||||
option.displayAlignment,
|
||||
actualPalette,
|
||||
isEnabled,
|
||||
entry->GetDisplayName(),
|
||||
style->drawItemText(
|
||||
painter, remainingRect, option.displayAlignment, actualPalette, isEnabled,
|
||||
displayString,
|
||||
isSelected ? QPalette::HighlightedText : QPalette::Text);
|
||||
}
|
||||
}
|
||||
|
||||
+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)
|
||||
|
||||
+22
-3
@@ -14,6 +14,7 @@
|
||||
|
||||
#include <AzCore/Console/Console.h>
|
||||
#include <AzCore/Math/Internal/VectorConversions.inl>
|
||||
#include <AzCore/std/numeric.h>
|
||||
#include <AzFramework/Entity/EntityDebugDisplayBus.h>
|
||||
#include <AzToolsFramework/Maths/TransformUtils.h>
|
||||
#include <AzToolsFramework/Viewport/ViewportMessages.h>
|
||||
@@ -95,16 +96,34 @@ namespace AzToolsFramework
|
||||
return axis * snapAdjustment.m_nextSnapDistance;
|
||||
}
|
||||
|
||||
AZ::Vector3 CalculateSnappedOffset(
|
||||
const AZ::Vector3& unsnappedPosition, const AZ::Vector3* snapAxes, const size_t snapAxesCount, const float size)
|
||||
{
|
||||
return AZStd::accumulate(
|
||||
snapAxes, snapAxes + snapAxesCount, AZ::Vector3::CreateZero(),
|
||||
[&unsnappedPosition, size](AZ::Vector3 acc, const AZ::Vector3& snapAxis)
|
||||
{
|
||||
acc += CalculateSnappedOffset(unsnappedPosition, snapAxis, size);
|
||||
return acc;
|
||||
});
|
||||
}
|
||||
|
||||
AZ::Vector3 CalculateSnappedPosition(
|
||||
const AZ::Vector3& unsnappedPosition, const AZ::Vector3* snapAxes, const size_t snapAxesCount, const float size)
|
||||
{
|
||||
return unsnappedPosition + CalculateSnappedOffset(unsnappedPosition, snapAxes, snapAxesCount, size);
|
||||
}
|
||||
|
||||
AZ::Vector3 CalculateSnappedTerrainPosition(
|
||||
const AZ::Vector3& worldSurfacePosition, const AZ::Transform& worldFromLocal, const int viewportId, const float gridSize)
|
||||
const AZ::Vector3& worldSurfacePosition, const AZ::Transform& worldFromLocal, const int viewportId, const float size)
|
||||
{
|
||||
const AZ::Transform localFromWorld = worldFromLocal.GetInverse();
|
||||
const AZ::Vector3 localSurfacePosition = localFromWorld.TransformPoint(worldSurfacePosition);
|
||||
|
||||
// snap in xy plane
|
||||
AZ::Vector3 localSnappedSurfacePosition = localSurfacePosition +
|
||||
CalculateSnappedOffset(localSurfacePosition, AZ::Vector3::CreateAxisX(), gridSize) +
|
||||
CalculateSnappedOffset(localSurfacePosition, AZ::Vector3::CreateAxisY(), gridSize);
|
||||
CalculateSnappedOffset(localSurfacePosition, AZ::Vector3::CreateAxisX(), size) +
|
||||
CalculateSnappedOffset(localSurfacePosition, AZ::Vector3::CreateAxisY(), size);
|
||||
|
||||
// find terrain height at xy snapped location
|
||||
float terrainHeight = 0.0f;
|
||||
|
||||
+13
-4
@@ -12,6 +12,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/std/math.h>
|
||||
#include <AzCore/Math/Transform.h>
|
||||
#include <AzCore/Math/Vector3.h>
|
||||
|
||||
@@ -58,10 +59,18 @@ namespace AzToolsFramework
|
||||
//! @note A movement of more than half size (in either direction) will cause a snap by size.
|
||||
AZ::Vector3 CalculateSnappedAmount(const AZ::Vector3& unsnappedPosition, const AZ::Vector3& axis, float size);
|
||||
|
||||
//! Overload of CalculateSnappedOffset taking multiple axes.
|
||||
AZ::Vector3 CalculateSnappedOffset(
|
||||
const AZ::Vector3& unsnappedPosition, const AZ::Vector3* snapAxes, size_t snapAxesCount, float size);
|
||||
|
||||
//! Return the final snapped position according to size (unsnappedPosition + CalculateSnappedOffset).
|
||||
AZ::Vector3 CalculateSnappedPosition(
|
||||
const AZ::Vector3& unsnappedPosition, const AZ::Vector3* snapAxes, size_t snapAxesCount, float size);
|
||||
|
||||
//! For a given point on the terrain, calculate the closest xy position snapped to the grid
|
||||
//! (z position is aligned to terrain height, not snapped to z grid)
|
||||
AZ::Vector3 CalculateSnappedTerrainPosition(
|
||||
const AZ::Vector3& worldSurfacePosition, const AZ::Transform& worldFromLocal, int viewportId, float gridSize);
|
||||
const AZ::Vector3& worldSurfacePosition, const AZ::Transform& worldFromLocal, int viewportId, float size);
|
||||
|
||||
//! Wrapper for grid snapping and grid size bus calls.
|
||||
GridSnapParameters GridSnapSettings(int viewportId);
|
||||
@@ -84,8 +93,8 @@ namespace AzToolsFramework
|
||||
//! @param exponent Precision to use when rounding.
|
||||
inline float Round(const float value, const float exponent)
|
||||
{
|
||||
const float precision = std::pow(10.0f, exponent);
|
||||
return roundf(value * precision) / precision;
|
||||
const float precision = AZStd::pow(10.0f, exponent);
|
||||
return AZStd::round(value * precision) / precision;
|
||||
}
|
||||
|
||||
//! Round to 3 significant digits (3 digits common usage).
|
||||
@@ -116,7 +125,7 @@ namespace AzToolsFramework
|
||||
//! when dealing with values far from the origin.
|
||||
inline AZ::Vector3 NonUniformScaleReciprocal(const AZ::Vector3& nonUniformScale)
|
||||
{
|
||||
AZ::Vector3 scaleReciprocal = nonUniformScale.GetReciprocal();
|
||||
const AZ::Vector3 scaleReciprocal = nonUniformScale.GetReciprocal();
|
||||
return AZ::Vector3(Round3(scaleReciprocal.GetX()), Round3(scaleReciprocal.GetY()), Round3(scaleReciprocal.GetZ()));
|
||||
}
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
@@ -373,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)
|
||||
{
|
||||
@@ -394,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)
|
||||
@@ -417,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))
|
||||
|
||||
@@ -121,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);
|
||||
|
||||
/**
|
||||
@@ -184,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 = '/';
|
||||
|
||||
@@ -197,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);
|
||||
|
||||
|
||||
+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,
|
||||
|
||||
@@ -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.");
|
||||
|
||||
+89
-29
@@ -127,6 +127,7 @@ namespace AzToolsFramework
|
||||
static const char* const s_dittoTranslationIndividualUndoRedoDesc = "Ditto translation individual";
|
||||
static const char* const s_dittoScaleIndividualWorldUndoRedoDesc = "Ditto scale individual world";
|
||||
static const char* const s_dittoScaleIndividualLocalUndoRedoDesc = "Ditto scale individual local";
|
||||
static const char* const s_snapToWorldGridUndoRedoDesc = "Snap to world grid";
|
||||
static const char* const s_showAllEntitiesUndoRedoDesc = s_showAllTitle;
|
||||
static const char* const s_lockSelectionUndoRedoDesc = s_lockSelectionTitle;
|
||||
static const char* const s_hideSelectionUndoRedoDesc = s_hideSelectionTitle;
|
||||
@@ -142,6 +143,7 @@ namespace AzToolsFramework
|
||||
static const char* const SpaceClusterWorldTooltip = "Toggle world space lock";
|
||||
static const char* const SpaceClusterParentTooltip = "Toggle parent space lock";
|
||||
static const char* const SpaceClusterLocalTooltip = "Toggle local space lock";
|
||||
static const char* const SnappingClusterSnapToWorldTooltip = "Snap selected entities to the world space grid";
|
||||
|
||||
static const AZ::Color s_fadedXAxisColor = AZ::Color(AZ::u8(200), AZ::u8(127), AZ::u8(127), AZ::u8(255));
|
||||
static const AZ::Color s_fadedYAxisColor = AZ::Color(AZ::u8(127), AZ::u8(190), AZ::u8(127), AZ::u8(255));
|
||||
@@ -150,8 +152,6 @@ namespace AzToolsFramework
|
||||
static const AZ::Color s_pickedOrientationColor = AZ::Color(0.0f, 1.0f, 0.0f, 1.0f);
|
||||
static const AZ::Color s_selectedEntityAabbColor = AZ::Color(0.6f, 0.6f, 0.6f, 0.4f);
|
||||
|
||||
static const int s_defaultViewportId = 0;
|
||||
|
||||
static const float s_pivotSize = 0.075f; // the size of the pivot (box) to render when selected
|
||||
|
||||
// data passed to manipulators when processing mouse interactions
|
||||
@@ -503,7 +503,8 @@ namespace AzToolsFramework
|
||||
void EditorTransformComponentSelection::SetAllViewportUiVisible(const bool visible)
|
||||
{
|
||||
SetViewportUiClusterVisible(m_transformModeClusterId, visible);
|
||||
SetViewportUiClusterVisible(m_spaceCluster.m_spaceClusterId, visible);
|
||||
SetViewportUiClusterVisible(m_spaceCluster.m_clusterId, visible);
|
||||
SetViewportUiClusterVisible(m_snappingCluster.m_clusterId, visible);
|
||||
m_viewportUiVisible = visible;
|
||||
}
|
||||
|
||||
@@ -524,8 +525,8 @@ namespace AzToolsFramework
|
||||
};
|
||||
|
||||
ViewportUi::ViewportUiRequestBus::Event(
|
||||
ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::SetClusterActiveButton,
|
||||
m_spaceCluster.m_spaceClusterId, buttonIdFromFrameFn(referenceFrame));
|
||||
ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::SetClusterActiveButton, m_spaceCluster.m_clusterId,
|
||||
buttonIdFromFrameFn(referenceFrame));
|
||||
}
|
||||
|
||||
namespace ETCS
|
||||
@@ -1037,6 +1038,8 @@ namespace AzToolsFramework
|
||||
|
||||
CreateTransformModeSelectionCluster();
|
||||
CreateSpaceSelectionCluster();
|
||||
CreateSnappingCluster();
|
||||
|
||||
RegisterActions();
|
||||
SetupBoxSelect();
|
||||
RefreshSelectedEntityIdsAndRegenerateManipulators();
|
||||
@@ -1048,7 +1051,8 @@ namespace AzToolsFramework
|
||||
DestroyManipulators(m_entityIdManipulators);
|
||||
|
||||
DestroyCluster(m_transformModeClusterId);
|
||||
DestroyCluster(m_spaceCluster.m_spaceClusterId);
|
||||
DestroyCluster(m_spaceCluster.m_clusterId);
|
||||
DestroyCluster(m_snappingCluster.m_clusterId);
|
||||
|
||||
UnregisterActions();
|
||||
|
||||
@@ -2513,28 +2517,64 @@ namespace AzToolsFramework
|
||||
m_transformModeSelectionHandler);
|
||||
}
|
||||
|
||||
void EditorTransformComponentSelection::CreateSpaceSelectionCluster()
|
||||
void EditorTransformComponentSelection::CreateSnappingCluster()
|
||||
{
|
||||
// create the cluster for switching spaces/reference frames
|
||||
ViewportUi::ViewportUiRequestBus::EventResult(
|
||||
m_spaceCluster.m_spaceClusterId, ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::CreateCluster,
|
||||
m_snappingCluster.m_clusterId, ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::CreateCluster,
|
||||
ViewportUi::Alignment::TopRight);
|
||||
|
||||
// create and register the buttons (strings correspond to icons even if the values appear different)
|
||||
m_spaceCluster.m_worldButtonId = RegisterClusterButton(m_spaceCluster.m_spaceClusterId, "World");
|
||||
m_spaceCluster.m_parentButtonId = RegisterClusterButton(m_spaceCluster.m_spaceClusterId, "Parent");
|
||||
m_spaceCluster.m_localButtonId = RegisterClusterButton(m_spaceCluster.m_spaceClusterId, "Local");
|
||||
m_snappingCluster.m_snapToWorldButtonId = RegisterClusterButton(m_snappingCluster.m_clusterId, "Grid");
|
||||
|
||||
// set button tooltips
|
||||
ViewportUi::ViewportUiRequestBus::Event(
|
||||
ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::SetClusterButtonTooltip,
|
||||
m_spaceCluster.m_spaceClusterId, m_spaceCluster.m_worldButtonId, SpaceClusterWorldTooltip);
|
||||
m_snappingCluster.m_clusterId, m_snappingCluster.m_snapToWorldButtonId, SnappingClusterSnapToWorldTooltip);
|
||||
|
||||
const auto onButtonClicked = [this](const ViewportUi::ButtonId buttonId)
|
||||
{
|
||||
if (buttonId == m_snappingCluster.m_snapToWorldButtonId)
|
||||
{
|
||||
float gridSize = 1.0f;
|
||||
ViewportInteraction::ViewportInteractionRequestBus::EventResult(
|
||||
gridSize, ViewportUi::DefaultViewportId, &ViewportInteraction::ViewportInteractionRequestBus::Events::GridSize);
|
||||
|
||||
SnapSelectedEntitiesToWorldGrid(gridSize);
|
||||
}
|
||||
};
|
||||
|
||||
m_snappingCluster.m_snappingHandler = AZ::Event<ViewportUi::ButtonId>::Handler(onButtonClicked);
|
||||
|
||||
ViewportUi::ViewportUiRequestBus::Event(
|
||||
ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::SetClusterButtonTooltip,
|
||||
m_spaceCluster.m_spaceClusterId, m_spaceCluster.m_parentButtonId, SpaceClusterParentTooltip);
|
||||
ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::RegisterClusterEventHandler,
|
||||
m_snappingCluster.m_clusterId, m_snappingCluster.m_snappingHandler);
|
||||
|
||||
// hide initially
|
||||
SetViewportUiClusterVisible(m_snappingCluster.m_clusterId, false);
|
||||
}
|
||||
|
||||
void EditorTransformComponentSelection::CreateSpaceSelectionCluster()
|
||||
{
|
||||
// create the cluster for switching spaces/reference frames
|
||||
ViewportUi::ViewportUiRequestBus::EventResult(
|
||||
m_spaceCluster.m_clusterId, ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::CreateCluster,
|
||||
ViewportUi::Alignment::TopRight);
|
||||
|
||||
// create and register the buttons (strings correspond to icons even if the values appear different)
|
||||
m_spaceCluster.m_worldButtonId = RegisterClusterButton(m_spaceCluster.m_clusterId, "World");
|
||||
m_spaceCluster.m_parentButtonId = RegisterClusterButton(m_spaceCluster.m_clusterId, "Parent");
|
||||
m_spaceCluster.m_localButtonId = RegisterClusterButton(m_spaceCluster.m_clusterId, "Local");
|
||||
|
||||
// set button tooltips
|
||||
ViewportUi::ViewportUiRequestBus::Event(
|
||||
ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::SetClusterButtonTooltip,
|
||||
m_spaceCluster.m_spaceClusterId, m_spaceCluster.m_localButtonId, SpaceClusterLocalTooltip);
|
||||
ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::SetClusterButtonTooltip, m_spaceCluster.m_clusterId,
|
||||
m_spaceCluster.m_worldButtonId, SpaceClusterWorldTooltip);
|
||||
ViewportUi::ViewportUiRequestBus::Event(
|
||||
ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::SetClusterButtonTooltip, m_spaceCluster.m_clusterId,
|
||||
m_spaceCluster.m_parentButtonId, SpaceClusterParentTooltip);
|
||||
ViewportUi::ViewportUiRequestBus::Event(
|
||||
ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::SetClusterButtonTooltip, m_spaceCluster.m_clusterId,
|
||||
m_spaceCluster.m_localButtonId, SpaceClusterLocalTooltip);
|
||||
|
||||
auto onButtonClicked = [this](ViewportUi::ButtonId buttonId)
|
||||
{
|
||||
@@ -2576,14 +2616,31 @@ namespace AzToolsFramework
|
||||
}
|
||||
ViewportUi::ViewportUiRequestBus::Event(
|
||||
ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::SetClusterButtonLocked,
|
||||
m_spaceCluster.m_spaceClusterId, buttonId, m_spaceCluster.m_spaceLock.has_value());
|
||||
m_spaceCluster.m_clusterId, buttonId, m_spaceCluster.m_spaceLock.has_value());
|
||||
};
|
||||
|
||||
m_spaceCluster.m_spaceSelectionHandler = AZ::Event<ViewportUi::ButtonId>::Handler(onButtonClicked);
|
||||
m_spaceCluster.m_spaceHandler = AZ::Event<ViewportUi::ButtonId>::Handler(onButtonClicked);
|
||||
|
||||
ViewportUi::ViewportUiRequestBus::Event(
|
||||
ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::RegisterClusterEventHandler,
|
||||
m_spaceCluster.m_spaceClusterId, m_spaceCluster.m_spaceSelectionHandler);
|
||||
m_spaceCluster.m_clusterId, m_spaceCluster.m_spaceHandler);
|
||||
}
|
||||
|
||||
void EditorTransformComponentSelection::SnapSelectedEntitiesToWorldGrid(const float gridSize)
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework);
|
||||
|
||||
const AZStd::array snapAxes = { AZ::Vector3::CreateAxisX(), AZ::Vector3::CreateAxisY(), AZ::Vector3::CreateAxisZ() };
|
||||
|
||||
ScopedUndoBatch undoBatch(s_snapToWorldGridUndoRedoDesc);
|
||||
for (const AZ::EntityId& entityId : m_selectedEntityIds)
|
||||
{
|
||||
ScopedUndoBatch::MarkEntityDirty(entityId);
|
||||
SetEntityWorldTranslation(
|
||||
entityId, CalculateSnappedPosition(GetWorldTranslation(entityId), snapAxes.data(), snapAxes.size(), gridSize));
|
||||
}
|
||||
|
||||
RefreshManipulators(RefreshType::Translation);
|
||||
}
|
||||
|
||||
EditorTransformComponentSelectionRequests::Mode EditorTransformComponentSelection::GetTransformMode()
|
||||
@@ -3145,15 +3202,16 @@ namespace AzToolsFramework
|
||||
return "Transform Component";
|
||||
}
|
||||
|
||||
void EditorTransformComponentSelection::PopulateEditorGlobalContextMenu(QMenu* menu, [[maybe_unused]] const AZ::Vector2& point, [[maybe_unused]] int flags)
|
||||
void EditorTransformComponentSelection::PopulateEditorGlobalContextMenu(
|
||||
QMenu* menu, [[maybe_unused]] const AZ::Vector2& point, [[maybe_unused]] int flags)
|
||||
{
|
||||
QAction* action = menu->addAction(QObject::tr(s_togglePivotTitleRightClick));
|
||||
QObject::connect(
|
||||
action, &QAction::triggered, action,
|
||||
[this]()
|
||||
{
|
||||
ToggleCenterPivotSelection();
|
||||
});
|
||||
QAction* action = menu->addAction(QObject::tr(s_togglePivotTitleRightClick));
|
||||
QObject::connect(
|
||||
action, &QAction::triggered, action,
|
||||
[this]()
|
||||
{
|
||||
ToggleCenterPivotSelection();
|
||||
});
|
||||
}
|
||||
|
||||
void EditorTransformComponentSelection::BeforeEntitySelectionChanged()
|
||||
@@ -3175,7 +3233,7 @@ namespace AzToolsFramework
|
||||
}
|
||||
|
||||
void EditorTransformComponentSelection::AfterEntitySelectionChanged(
|
||||
const EntityIdList& /*newlySelectedEntities*/, const EntityIdList& /*newlyDeselectedEntities*/)
|
||||
[[maybe_unused]] const EntityIdList& newlySelectedEntities, [[maybe_unused]] const EntityIdList& newlyDeselectedEntities)
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework);
|
||||
|
||||
@@ -3195,6 +3253,8 @@ namespace AzToolsFramework
|
||||
m_didSetSelectedEntities = false;
|
||||
}
|
||||
|
||||
SetViewportUiClusterVisible(m_snappingCluster.m_clusterId, m_viewportUiVisible && !m_selectedEntityIds.empty());
|
||||
|
||||
RegenerateManipulators();
|
||||
}
|
||||
|
||||
|
||||
+26
-10
@@ -115,12 +115,25 @@ namespace AzToolsFramework
|
||||
SpaceCluster(const SpaceCluster&) = delete;
|
||||
SpaceCluster& operator=(const SpaceCluster&) = delete;
|
||||
|
||||
ViewportUi::ClusterId m_spaceClusterId; //!< The id identifying the reference space cluster.
|
||||
ViewportUi::ClusterId m_clusterId; //!< The id identifying the reference space cluster.
|
||||
ViewportUi::ButtonId m_localButtonId; //!< Local reference space button id.
|
||||
ViewportUi::ButtonId m_parentButtonId; //!< Parent reference space button id.
|
||||
ViewportUi::ButtonId m_worldButtonId; //!< World reference space button id.
|
||||
AZ::Event<ViewportUi::ButtonId>::Handler m_spaceSelectionHandler; //!< Callback for when a space cluster button is pressed.
|
||||
AZStd::optional<ReferenceFrame> m_spaceLock; //!< Locked reference frame to use if set.
|
||||
AZ::Event<ViewportUi::ButtonId>::Handler m_spaceHandler; //!< Callback for when a space cluster button is pressed.
|
||||
};
|
||||
|
||||
//! Grouping of viewport ui related state for aligning transforms to a grid.
|
||||
struct SnappingCluster
|
||||
{
|
||||
SnappingCluster() = default;
|
||||
// disable copying and moving (implicit)
|
||||
SnappingCluster(const SnappingCluster&) = delete;
|
||||
SnappingCluster& operator=(const SnappingCluster&) = delete;
|
||||
|
||||
ViewportUi::ClusterId m_clusterId; //!< The cluster id for all snapping buttons.
|
||||
ViewportUi::ButtonId m_snapToWorldButtonId; //!< The button id for snapping all axes to the world.
|
||||
AZ::Event<ViewportUi::ButtonId>::Handler m_snappingHandler; //!< Callback for when a snapping cluster button is pressed.
|
||||
};
|
||||
|
||||
//! Entity selection/interaction handling.
|
||||
@@ -180,6 +193,7 @@ namespace AzToolsFramework
|
||||
|
||||
void CreateTransformModeSelectionCluster();
|
||||
void CreateSpaceSelectionCluster();
|
||||
void CreateSnappingCluster();
|
||||
|
||||
void ClearManipulatorTranslationOverride();
|
||||
void ClearManipulatorOrientationOverride();
|
||||
@@ -228,14 +242,15 @@ namespace AzToolsFramework
|
||||
AZStd::optional<AZ::Transform> GetManipulatorTransform() override;
|
||||
void OverrideManipulatorOrientation(const AZ::Quaternion& orientation) override;
|
||||
void OverrideManipulatorTranslation(const AZ::Vector3& translation) override;
|
||||
void CopyTranslationToSelectedEntitiesIndividual(const AZ::Vector3& translation);
|
||||
void CopyTranslationToSelectedEntitiesGroup(const AZ::Vector3& translation);
|
||||
void ResetTranslationForSelectedEntitiesLocal();
|
||||
void CopyOrientationToSelectedEntitiesIndividual(const AZ::Quaternion& orientation);
|
||||
void CopyOrientationToSelectedEntitiesGroup(const AZ::Quaternion& orientation);
|
||||
void ResetOrientationForSelectedEntitiesLocal();
|
||||
void CopyScaleToSelectedEntitiesIndividualLocal(float scale);
|
||||
void CopyScaleToSelectedEntitiesIndividualWorld(float scale);
|
||||
void CopyTranslationToSelectedEntitiesIndividual(const AZ::Vector3& translation) override;
|
||||
void CopyTranslationToSelectedEntitiesGroup(const AZ::Vector3& translation) override;
|
||||
void ResetTranslationForSelectedEntitiesLocal() override;
|
||||
void CopyOrientationToSelectedEntitiesIndividual(const AZ::Quaternion& orientation) override;
|
||||
void CopyOrientationToSelectedEntitiesGroup(const AZ::Quaternion& orientation) override;
|
||||
void ResetOrientationForSelectedEntitiesLocal() override;
|
||||
void CopyScaleToSelectedEntitiesIndividualLocal(float scale) override;
|
||||
void CopyScaleToSelectedEntitiesIndividualWorld(float scale) override;
|
||||
void SnapSelectedEntitiesToWorldGrid(float gridSize) override;
|
||||
|
||||
// EditorManipulatorCommandUndoRedoRequestBus ...
|
||||
void UndoRedoEntityManipulatorCommand(
|
||||
@@ -320,6 +335,7 @@ namespace AzToolsFramework
|
||||
AzFramework::ClickDetector m_clickDetector; //!< Detect different types of mouse click.
|
||||
AzFramework::CursorState m_cursorState; //!< Track the mouse position and delta movement each frame.
|
||||
SpaceCluster m_spaceCluster; //!< Related viewport ui state for controlling the current reference space.
|
||||
SnappingCluster m_snappingCluster; //!< Related viewport ui state for aligning positions to a grid or reference frame.
|
||||
bool m_viewportUiVisible = true; //!< Used to hide/show the viewport ui elements.
|
||||
};
|
||||
|
||||
|
||||
+3
@@ -104,6 +104,9 @@ namespace AzToolsFramework
|
||||
//! Copy scale to to each individual entity in world (absolute) space.
|
||||
virtual void CopyScaleToSelectedEntitiesIndividualWorld(float scale) = 0;
|
||||
|
||||
//! Snap selected entities to be aligned with the world space grid.
|
||||
virtual void SnapSelectedEntitiesToWorldGrid(float gridSize) = 0;
|
||||
|
||||
protected:
|
||||
~EditorTransformComponentSelectionRequests() = default;
|
||||
};
|
||||
|
||||
@@ -281,7 +281,7 @@ namespace AzToolsFramework::ViewportUi::Internal
|
||||
void ViewportUiDisplay::HideViewportUiElement(ViewportUiElementId elementId)
|
||||
{
|
||||
if (ViewportUiElementInfo element = GetViewportUiElementInfo(elementId);
|
||||
element.m_widget && UiDisplayEnabled())
|
||||
element.m_widget)
|
||||
{
|
||||
element.m_widget->setVisible(false);
|
||||
}
|
||||
|
||||
@@ -544,6 +544,8 @@ set(FILES
|
||||
AssetBrowser/AssetBrowserEntry.h
|
||||
AssetBrowser/AssetBrowserFilterModel.cpp
|
||||
AssetBrowser/AssetBrowserFilterModel.h
|
||||
AssetBrowser/AssetBrowserTableModel.cpp
|
||||
AssetBrowser/AssetBrowserTableModel.h
|
||||
AssetBrowser/AssetBrowserModel.cpp
|
||||
AssetBrowser/AssetBrowserModel.h
|
||||
AssetBrowser/AssetEntryChange.h
|
||||
@@ -554,6 +556,8 @@ set(FILES
|
||||
AssetBrowser/EBusFindAssetTypeByName.h
|
||||
AssetBrowser/Views/AssetBrowserTreeView.cpp
|
||||
AssetBrowser/Views/AssetBrowserTreeView.h
|
||||
AssetBrowser/Views/AssetBrowserTableView.cpp
|
||||
AssetBrowser/Views/AssetBrowserTableView.h
|
||||
AssetBrowser/Views/EntryDelegate.cpp
|
||||
AssetBrowser/Views/EntryDelegate.h
|
||||
AssetBrowser/Views/AssetBrowserFolderWidget.cpp
|
||||
|
||||
+208
-209
@@ -1,42 +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.
|
||||
*
|
||||
*/
|
||||
* 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/Math/ToString.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/UnitTest/TestTypes.h>
|
||||
#include <AzFramework/Entity/EntityContext.h>
|
||||
#include <AzFramework/Components/TransformComponent.h>
|
||||
#include <AzFramework/Entity/EntityContext.h>
|
||||
#include <AzFramework/Viewport/ViewportScreen.h>
|
||||
#include <AzManipulatorTestFramework/AzManipulatorTestFramework.h>
|
||||
#include <AzManipulatorTestFramework/AzManipulatorTestFrameworkTestHelpers.h>
|
||||
#include <AzManipulatorTestFramework/AzManipulatorTestFrameworkUtils.h>
|
||||
#include <AzManipulatorTestFramework/ImmediateModeActionDispatcher.h>
|
||||
#include <AzManipulatorTestFramework/IndirectManipulatorViewportInteraction.h>
|
||||
#include <AzManipulatorTestFramework/ViewportInteraction.h>
|
||||
#include <AzQtComponents/Components/GlobalEventFilter.h>
|
||||
#include <AzTest/AzTest.h>
|
||||
#include <AzToolsFramework/Application/ToolsApplication.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntityActionComponent.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntityModel.h>
|
||||
#include <AzToolsFramework/ToolsComponents/TransformComponent.h>
|
||||
#include <AzToolsFramework/ToolsComponents/EditorLockComponent.h>
|
||||
#include <AzToolsFramework/ToolsComponents/EditorVisibilityComponent.h>
|
||||
#include <AzToolsFramework/ToolsComponents/TransformComponent.h>
|
||||
#include <AzToolsFramework/UnitTest/AzToolsFrameworkTestHelpers.h>
|
||||
#include <AzToolsFramework/Viewport/ActionBus.h>
|
||||
#include <AzToolsFramework/ViewportSelection/EditorDefaultSelection.h>
|
||||
#include <AzToolsFramework/ViewportSelection/EditorInteractionSystemViewportSelectionRequestBus.h>
|
||||
#include <AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.h>
|
||||
#include <AzToolsFramework/ViewportSelection/EditorVisibleEntityDataCache.h>
|
||||
#include <AzManipulatorTestFramework/AzManipulatorTestFramework.h>
|
||||
#include <AzManipulatorTestFramework/AzManipulatorTestFrameworkUtils.h>
|
||||
#include <AzManipulatorTestFramework/AzManipulatorTestFrameworkTestHelpers.h>
|
||||
#include <AzManipulatorTestFramework/ImmediateModeActionDispatcher.h>
|
||||
#include <AzManipulatorTestFramework/IndirectManipulatorViewportInteraction.h>
|
||||
#include <AzManipulatorTestFramework/ViewportInteraction.h>
|
||||
#include <AzQtComponents/Components/GlobalEventFilter.h>
|
||||
|
||||
using namespace AzToolsFramework;
|
||||
|
||||
@@ -46,12 +46,11 @@ namespace AZ
|
||||
{
|
||||
return os << entityId.ToString().c_str();
|
||||
}
|
||||
}
|
||||
} // namespace AZ
|
||||
|
||||
namespace UnitTest
|
||||
{
|
||||
class EditorEntityVisibilityCacheFixture
|
||||
: public ToolsApplicationFixture
|
||||
class EditorEntityVisibilityCacheFixture : public ToolsApplicationFixture
|
||||
{
|
||||
public:
|
||||
void CreateLayerAndEntityHierarchy()
|
||||
@@ -116,8 +115,7 @@ namespace UnitTest
|
||||
}
|
||||
|
||||
// Fixture to support testing EditorTransformComponentSelection functionality on an Entity selection.
|
||||
class EditorTransformComponentSelectionFixture
|
||||
: public ToolsApplicationFixture
|
||||
class EditorTransformComponentSelectionFixture : public ToolsApplicationFixture
|
||||
{
|
||||
public:
|
||||
void SetUpEditorFixtureImpl() override
|
||||
@@ -138,13 +136,11 @@ namespace UnitTest
|
||||
EntityIdList m_entityIds;
|
||||
};
|
||||
|
||||
void EditorTransformComponentSelectionFixture::ArrangeIndividualRotatedEntitySelection(
|
||||
const AZ::Quaternion& orientation)
|
||||
void EditorTransformComponentSelectionFixture::ArrangeIndividualRotatedEntitySelection(const AZ::Quaternion& orientation)
|
||||
{
|
||||
for (auto entityId : m_entityIds)
|
||||
{
|
||||
AZ::TransformBus::Event(
|
||||
entityId, &AZ::TransformBus::Events::SetLocalRotationQuaternion, orientation);
|
||||
AZ::TransformBus::Event(entityId, &AZ::TransformBus::Events::SetLocalRotationQuaternion, orientation);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,40 +148,32 @@ namespace UnitTest
|
||||
{
|
||||
AZStd::optional<AZ::Transform> manipulatorTransform;
|
||||
EditorTransformComponentSelectionRequestBus::EventResult(
|
||||
manipulatorTransform, GetEntityContextId(),
|
||||
&EditorTransformComponentSelectionRequests::GetManipulatorTransform);
|
||||
manipulatorTransform, GetEntityContextId(), &EditorTransformComponentSelectionRequests::GetManipulatorTransform);
|
||||
return manipulatorTransform;
|
||||
}
|
||||
|
||||
void EditorTransformComponentSelectionFixture::RefreshManipulators(
|
||||
EditorTransformComponentSelectionRequests::RefreshType refreshType)
|
||||
void EditorTransformComponentSelectionFixture::RefreshManipulators(EditorTransformComponentSelectionRequests::RefreshType refreshType)
|
||||
{
|
||||
EditorTransformComponentSelectionRequestBus::Event(
|
||||
GetEntityContextId(), &EditorTransformComponentSelectionRequests::RefreshManipulators, refreshType);
|
||||
}
|
||||
|
||||
void EditorTransformComponentSelectionFixture::SetTransformMode(
|
||||
EditorTransformComponentSelectionRequests::Mode transformMode)
|
||||
void EditorTransformComponentSelectionFixture::SetTransformMode(EditorTransformComponentSelectionRequests::Mode transformMode)
|
||||
{
|
||||
EditorTransformComponentSelectionRequestBus::Event(
|
||||
GetEntityContextId(), &EditorTransformComponentSelectionRequests::SetTransformMode,
|
||||
transformMode);
|
||||
GetEntityContextId(), &EditorTransformComponentSelectionRequests::SetTransformMode, transformMode);
|
||||
}
|
||||
|
||||
void EditorTransformComponentSelectionFixture::OverrideManipulatorOrientation(
|
||||
const AZ::Quaternion& orientation)
|
||||
void EditorTransformComponentSelectionFixture::OverrideManipulatorOrientation(const AZ::Quaternion& orientation)
|
||||
{
|
||||
EditorTransformComponentSelectionRequestBus::Event(
|
||||
GetEntityContextId(), &EditorTransformComponentSelectionRequests::OverrideManipulatorOrientation,
|
||||
orientation);
|
||||
GetEntityContextId(), &EditorTransformComponentSelectionRequests::OverrideManipulatorOrientation, orientation);
|
||||
}
|
||||
|
||||
void EditorTransformComponentSelectionFixture::OverrideManipulatorTranslation(
|
||||
const AZ::Vector3& translation)
|
||||
void EditorTransformComponentSelectionFixture::OverrideManipulatorTranslation(const AZ::Vector3& translation)
|
||||
{
|
||||
EditorTransformComponentSelectionRequestBus::Event(
|
||||
GetEntityContextId(), &EditorTransformComponentSelectionRequests::OverrideManipulatorTranslation,
|
||||
translation);
|
||||
GetEntityContextId(), &EditorTransformComponentSelectionRequests::OverrideManipulatorTranslation, translation);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -202,8 +190,7 @@ namespace UnitTest
|
||||
|
||||
SetTransformMode(EditorTransformComponentSelectionRequests::Mode::Rotation);
|
||||
|
||||
const AZ::Transform manipulatorTransformBefore =
|
||||
GetManipulatorTransform().value_or(AZ::Transform::CreateIdentity());
|
||||
const AZ::Transform manipulatorTransformBefore = GetManipulatorTransform().value_or(AZ::Transform::CreateIdentity());
|
||||
|
||||
// check preconditions - manipulator transform matches parent/world transform (identity)
|
||||
EXPECT_THAT(manipulatorTransformBefore.GetBasisY(), IsClose(AZ::Vector3::CreateAxisY()));
|
||||
@@ -218,8 +205,7 @@ namespace UnitTest
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Then
|
||||
const AZ::Transform manipulatorTransformAfter =
|
||||
GetManipulatorTransform().value_or(AZ::Transform::CreateIdentity());
|
||||
const AZ::Transform manipulatorTransformAfter = GetManipulatorTransform().value_or(AZ::Transform::CreateIdentity());
|
||||
|
||||
// check postconditions - manipulator transform matches parent/world transform (identity)
|
||||
EXPECT_THAT(manipulatorTransformAfter.GetBasisY(), IsClose(AZ::Vector3::CreateAxisY()));
|
||||
@@ -229,8 +215,7 @@ namespace UnitTest
|
||||
{
|
||||
// create invalid starting orientation to guarantee correct data is coming from GetLocalRotationQuaternion
|
||||
AZ::Quaternion entityOrientation = AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisX(), 90.0f);
|
||||
AZ::TransformBus::EventResult(
|
||||
entityOrientation, entityId, &AZ::TransformBus::Events::GetLocalRotationQuaternion);
|
||||
AZ::TransformBus::EventResult(entityOrientation, entityId, &AZ::TransformBus::Events::GetLocalRotationQuaternion);
|
||||
|
||||
// manipulator orientation matches entity orientation
|
||||
EXPECT_THAT(entityOrientation, IsClose(manipulatorTransformAfter.GetRotation()));
|
||||
@@ -252,8 +237,7 @@ namespace UnitTest
|
||||
|
||||
SetTransformMode(EditorTransformComponentSelectionRequests::Mode::Rotation);
|
||||
|
||||
const AZ::Transform manipulatorTransformBefore =
|
||||
GetManipulatorTransform().value_or(AZ::Transform::CreateIdentity());
|
||||
const AZ::Transform manipulatorTransformBefore = GetManipulatorTransform().value_or(AZ::Transform::CreateIdentity());
|
||||
|
||||
// check preconditions - manipulator transform matches manipulator orientation override (not entity transform)
|
||||
EXPECT_THAT(manipulatorTransformBefore.GetBasisX(), IsClose(AZ::Vector3::CreateAxisY()));
|
||||
@@ -268,8 +252,7 @@ namespace UnitTest
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Then
|
||||
const AZ::Transform manipulatorTransformAfter =
|
||||
GetManipulatorTransform().value_or(AZ::Transform::CreateIdentity());
|
||||
const AZ::Transform manipulatorTransformAfter = GetManipulatorTransform().value_or(AZ::Transform::CreateIdentity());
|
||||
|
||||
// check postconditions - manipulator transform matches parent/world space (manipulator override was cleared)
|
||||
EXPECT_THAT(manipulatorTransformAfter.GetBasisY(), IsClose(AZ::Vector3::CreateAxisY()));
|
||||
@@ -278,8 +261,7 @@ namespace UnitTest
|
||||
for (auto entityId : m_entityIds)
|
||||
{
|
||||
AZ::Quaternion entityOrientation;
|
||||
AZ::TransformBus::EventResult(
|
||||
entityOrientation, entityId, &AZ::TransformBus::Events::GetLocalRotationQuaternion);
|
||||
AZ::TransformBus::EventResult(entityOrientation, entityId, &AZ::TransformBus::Events::GetLocalRotationQuaternion);
|
||||
|
||||
// entity transform matches initial (entity transform was not reset, only manipulator was)
|
||||
EXPECT_THAT(entityOrientation, IsClose(initialEntityOrientation));
|
||||
@@ -301,16 +283,13 @@ namespace UnitTest
|
||||
AZ::EntityId parentId = CreateDefaultEditorEntity("Parent", &parent);
|
||||
AZ::EntityId childId = CreateDefaultEditorEntity("Child", &child);
|
||||
|
||||
AZ::TransformBus::Event(
|
||||
childId, &AZ::TransformInterface::SetParent, parentId);
|
||||
AZ::TransformBus::Event(
|
||||
parentId, &AZ::TransformInterface::SetParent, grandParentId);
|
||||
AZ::TransformBus::Event(childId, &AZ::TransformInterface::SetParent, parentId);
|
||||
AZ::TransformBus::Event(parentId, &AZ::TransformInterface::SetParent, grandParentId);
|
||||
|
||||
UnitTest::SliceAssets sliceAssets;
|
||||
const auto sliceAssetId = UnitTest::SaveAsSlice({ grandParent }, GetApplication(), sliceAssets);
|
||||
|
||||
EntityList instantiatedEntities =
|
||||
UnitTest::InstantiateSlice(sliceAssetId, sliceAssets);
|
||||
EntityList instantiatedEntities = UnitTest::InstantiateSlice(sliceAssetId, sliceAssets);
|
||||
|
||||
const AZ::EntityId entityIdToMove = instantiatedEntities.back()->GetId();
|
||||
EditorEntityComponentChangeDetector editorEntityChangeDetector(entityIdToMove);
|
||||
@@ -321,8 +300,7 @@ namespace UnitTest
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// When
|
||||
EditorTransformComponentSelectionRequestBus::Event(
|
||||
GetEntityContextId(),
|
||||
&EditorTransformComponentSelectionRequests::CopyOrientationToSelectedEntitiesIndividual,
|
||||
GetEntityContextId(), &EditorTransformComponentSelectionRequests::CopyOrientationToSelectedEntitiesIndividual,
|
||||
AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisX(), AZ::DegToRad(90.0f)));
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -362,10 +340,9 @@ namespace UnitTest
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Then
|
||||
AzToolsFramework::EntityIdList selectedEntities;
|
||||
ToolsApplicationRequestBus::BroadcastResult(
|
||||
selectedEntities, &ToolsApplicationRequestBus::Events::GetSelectedEntities);
|
||||
ToolsApplicationRequestBus::BroadcastResult(selectedEntities, &ToolsApplicationRequestBus::Events::GetSelectedEntities);
|
||||
|
||||
AzToolsFramework::EntityIdList expectedSelectedEntities = {entity4, entity5, entity6};
|
||||
AzToolsFramework::EntityIdList expectedSelectedEntities = { entity4, entity5, entity6 };
|
||||
|
||||
EXPECT_THAT(selectedEntities, UnorderedElementsAreArray(expectedSelectedEntities));
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -396,10 +373,9 @@ namespace UnitTest
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Then
|
||||
AzToolsFramework::EntityIdList selectedEntities;
|
||||
ToolsApplicationRequestBus::BroadcastResult(
|
||||
selectedEntities, &ToolsApplicationRequestBus::Events::GetSelectedEntities);
|
||||
ToolsApplicationRequestBus::BroadcastResult(selectedEntities, &ToolsApplicationRequestBus::Events::GetSelectedEntities);
|
||||
|
||||
AzToolsFramework::EntityIdList expectedSelectedEntities = {m_entity1, entity2, entity3, entity4};
|
||||
AzToolsFramework::EntityIdList expectedSelectedEntities = { m_entity1, entity2, entity3, entity4 };
|
||||
|
||||
EXPECT_THAT(selectedEntities, UnorderedElementsAreArray(expectedSelectedEntities));
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -416,11 +392,9 @@ namespace UnitTest
|
||||
const auto finalTransformWorld = AZ::Transform::CreateTranslation(AZ::Vector3(10.0f, 10.0f, 0.0f));
|
||||
|
||||
// calculate the position in screen space of the initial position of the entity
|
||||
const auto initialPositionScreen =
|
||||
AzFramework::WorldToScreen(initialTransformWorld.GetTranslation(), m_cameraState);
|
||||
const auto initialPositionScreen = AzFramework::WorldToScreen(initialTransformWorld.GetTranslation(), m_cameraState);
|
||||
// calculate the position in screen space of the final position of the entity
|
||||
const auto finalPositionScreen =
|
||||
AzFramework::WorldToScreen(finalTransformWorld.GetTranslation(), m_cameraState);
|
||||
const auto finalPositionScreen = AzFramework::WorldToScreen(finalTransformWorld.GetTranslation(), m_cameraState);
|
||||
|
||||
// select the entity (this will cause the manipulators to appear in EditorTransformComponentSelection)
|
||||
AzToolsFramework::SelectEntity(m_entity1);
|
||||
@@ -452,10 +426,10 @@ namespace UnitTest
|
||||
}
|
||||
|
||||
// simple widget to listen for a mouse wheel event and then forward it on to the ViewportSelectionRequestBus
|
||||
class WheelEventWidget
|
||||
: public QWidget
|
||||
class WheelEventWidget : public QWidget
|
||||
{
|
||||
using MouseInteractionResult = AzToolsFramework::ViewportInteraction::MouseInteractionResult;
|
||||
|
||||
public:
|
||||
WheelEventWidget(QWidget* parent = nullptr)
|
||||
: QWidget(parent)
|
||||
@@ -490,8 +464,7 @@ namespace UnitTest
|
||||
{
|
||||
EditorTransformComponentSelectionRequests::Mode transformMode;
|
||||
EditorTransformComponentSelectionRequestBus::EventResult(
|
||||
transformMode, GetEntityContextId(),
|
||||
&EditorTransformComponentSelectionRequestBus::Events::GetTransformMode);
|
||||
transformMode, GetEntityContextId(), &EditorTransformComponentSelectionRequestBus::Events::GetTransformMode);
|
||||
return transformMode;
|
||||
};
|
||||
|
||||
@@ -519,6 +492,56 @@ namespace UnitTest
|
||||
EXPECT_THAT(wheelEventWidget.m_mouseInteractionResult, Eq(vi::MouseInteractionResult::Viewport));
|
||||
}
|
||||
|
||||
TEST_F(EditorTransformComponentSelectionFixture, EntityPositionsCanBeSnappedToGrid)
|
||||
{
|
||||
using ::testing::Pointwise;
|
||||
|
||||
m_entityIds.push_back(CreateDefaultEditorEntity("Entity2"));
|
||||
m_entityIds.push_back(CreateDefaultEditorEntity("Entity3"));
|
||||
|
||||
const AZStd::vector<AZ::Vector3> initialUnsnappedPositions = { AZ::Vector3(1.2f, 3.5f, 6.7f), AZ::Vector3(13.2f, 15.6f, 11.4f),
|
||||
AZ::Vector3(4.2f, 103.2f, 16.6f) };
|
||||
AZ::TransformBus::Event(m_entityIds[0], &AZ::TransformBus::Events::SetWorldTranslation, initialUnsnappedPositions[0]);
|
||||
AZ::TransformBus::Event(m_entityIds[1], &AZ::TransformBus::Events::SetWorldTranslation, initialUnsnappedPositions[1]);
|
||||
AZ::TransformBus::Event(m_entityIds[2], &AZ::TransformBus::Events::SetWorldTranslation, initialUnsnappedPositions[2]);
|
||||
|
||||
AzToolsFramework::SelectEntities(m_entityIds);
|
||||
|
||||
EditorTransformComponentSelectionRequestBus::Event(
|
||||
GetEntityContextId(), &EditorTransformComponentSelectionRequestBus::Events::SnapSelectedEntitiesToWorldGrid, 2.0f);
|
||||
|
||||
AZStd::vector<AZ::Vector3> entityPositionsAfterSnap;
|
||||
AZStd::transform(
|
||||
m_entityIds.cbegin(), m_entityIds.cend(), AZStd::back_inserter(entityPositionsAfterSnap),
|
||||
[](const AZ::EntityId& entityId)
|
||||
{
|
||||
return GetWorldTranslation(entityId);
|
||||
});
|
||||
|
||||
const AZStd::vector<AZ::Vector3> expectedSnappedPositions = { AZ::Vector3(2.0f, 4.0f, 6.0f), AZ::Vector3(14.0f, 16.0f, 12.0f),
|
||||
AZ::Vector3(4.0f, 104.0f, 16.0f) };
|
||||
EXPECT_THAT(entityPositionsAfterSnap, Pointwise(ContainerIsClose(), expectedSnappedPositions));
|
||||
}
|
||||
|
||||
TEST_F(EditorTransformComponentSelectionFixture, ManipulatorStaysAlignedToEntityTranslationAfterSnap)
|
||||
{
|
||||
const auto initialUnsnappedPosition = AZ::Vector3(1.2f, 3.5f, 6.7f);
|
||||
AZ::TransformBus::Event(m_entityIds[0], &AZ::TransformBus::Events::SetWorldTranslation, initialUnsnappedPosition);
|
||||
|
||||
AzToolsFramework::SelectEntities(m_entityIds);
|
||||
|
||||
EditorTransformComponentSelectionRequestBus::Event(
|
||||
GetEntityContextId(), &EditorTransformComponentSelectionRequestBus::Events::SnapSelectedEntitiesToWorldGrid, 1.0f);
|
||||
|
||||
const auto entityPositionAfterSnap = GetWorldTranslation(m_entity1);
|
||||
const AZ::Vector3 manipulatorPositionAfterSnap =
|
||||
GetManipulatorTransform().value_or(AZ::Transform::CreateIdentity()).GetTranslation();
|
||||
|
||||
const auto expectedSnappedPosition = AZ::Vector3(1.0f, 4.0f, 7.0f);
|
||||
EXPECT_THAT(entityPositionAfterSnap, IsClose(expectedSnappedPosition));
|
||||
EXPECT_THAT(expectedSnappedPosition, IsClose(manipulatorPositionAfterSnap));
|
||||
}
|
||||
|
||||
// struct to contain input reference frame and expected orientation outcome based on
|
||||
// the reference frame, selection and entity hierarchy
|
||||
struct ReferenceFrameWithOrientation
|
||||
@@ -541,19 +564,20 @@ namespace UnitTest
|
||||
|
||||
class EditorTransformComponentSelectionSingleEntityPivotFixture
|
||||
: public EditorTransformComponentSelectionFixture
|
||||
, public ::testing::WithParamInterface<ReferenceFrameWithOrientation> {};
|
||||
, public ::testing::WithParamInterface<ReferenceFrameWithOrientation>
|
||||
{
|
||||
};
|
||||
|
||||
TEST_P(EditorTransformComponentSelectionSingleEntityPivotFixture, PivotOrientationMatchesReferenceFrameSingleEntity)
|
||||
{
|
||||
using ETCS::PivotOrientationResult;
|
||||
using ETCS::CalculatePivotOrientation;
|
||||
using ETCS::PivotOrientationResult;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Given
|
||||
AZ::TransformBus::Event(
|
||||
m_entityIds[0], &AZ::TransformBus::Events::SetWorldTM,
|
||||
AZ::Transform::CreateFromQuaternionAndTranslation(
|
||||
ChildExpectedPivotLocalOrientationInWorldSpace, AZ::Vector3::CreateZero()));
|
||||
AZ::Transform::CreateFromQuaternionAndTranslation(ChildExpectedPivotLocalOrientationInWorldSpace, AZ::Vector3::CreateZero()));
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -574,20 +598,20 @@ namespace UnitTest
|
||||
All,
|
||||
EditorTransformComponentSelectionSingleEntityPivotFixture,
|
||||
testing::Values(
|
||||
ReferenceFrameWithOrientation{ReferenceFrame::Local, ChildExpectedPivotLocalOrientationInWorldSpace},
|
||||
ReferenceFrameWithOrientation{ReferenceFrame::Parent, AZ::Quaternion::CreateIdentity()},
|
||||
ReferenceFrameWithOrientation{ReferenceFrame::World, AZ::Quaternion::CreateIdentity()}));
|
||||
ReferenceFrameWithOrientation{ ReferenceFrame::Local, ChildExpectedPivotLocalOrientationInWorldSpace },
|
||||
ReferenceFrameWithOrientation{ ReferenceFrame::Parent, AZ::Quaternion::CreateIdentity() },
|
||||
ReferenceFrameWithOrientation{ ReferenceFrame::World, AZ::Quaternion::CreateIdentity() }));
|
||||
|
||||
class EditorTransformComponentSelectionSingleEntityWithParentPivotFixture
|
||||
: public EditorTransformComponentSelectionFixture
|
||||
, public ::testing::WithParamInterface<ReferenceFrameWithOrientation> {};
|
||||
|
||||
TEST_P(
|
||||
EditorTransformComponentSelectionSingleEntityWithParentPivotFixture,
|
||||
PivotOrientationMatchesReferenceFrameEntityWithParent)
|
||||
, public ::testing::WithParamInterface<ReferenceFrameWithOrientation>
|
||||
{
|
||||
};
|
||||
|
||||
TEST_P(EditorTransformComponentSelectionSingleEntityWithParentPivotFixture, PivotOrientationMatchesReferenceFrameEntityWithParent)
|
||||
{
|
||||
using ETCS::PivotOrientationResult;
|
||||
using ETCS::CalculatePivotOrientation;
|
||||
using ETCS::PivotOrientationResult;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Given
|
||||
@@ -596,8 +620,7 @@ namespace UnitTest
|
||||
|
||||
AZ::TransformBus::Event(
|
||||
parentEntityId, &AZ::TransformBus::Events::SetWorldTM,
|
||||
AZ::Transform::CreateFromQuaternionAndTranslation(
|
||||
ParentExpectedPivotLocalOrientationInWorldSpace, AZ::Vector3::CreateZero()));
|
||||
AZ::Transform::CreateFromQuaternionAndTranslation(ParentExpectedPivotLocalOrientationInWorldSpace, AZ::Vector3::CreateZero()));
|
||||
|
||||
AZ::TransformBus::Event(
|
||||
m_entityIds[0], &AZ::TransformBus::Events::SetWorldTM,
|
||||
@@ -624,20 +647,20 @@ namespace UnitTest
|
||||
All,
|
||||
EditorTransformComponentSelectionSingleEntityWithParentPivotFixture,
|
||||
testing::Values(
|
||||
ReferenceFrameWithOrientation{ReferenceFrame::Local, ChildExpectedPivotLocalOrientationInWorldSpace},
|
||||
ReferenceFrameWithOrientation{ReferenceFrame::Parent, ParentExpectedPivotLocalOrientationInWorldSpace},
|
||||
ReferenceFrameWithOrientation{ReferenceFrame::World, AZ::Quaternion::CreateIdentity()}));
|
||||
ReferenceFrameWithOrientation{ ReferenceFrame::Local, ChildExpectedPivotLocalOrientationInWorldSpace },
|
||||
ReferenceFrameWithOrientation{ ReferenceFrame::Parent, ParentExpectedPivotLocalOrientationInWorldSpace },
|
||||
ReferenceFrameWithOrientation{ ReferenceFrame::World, AZ::Quaternion::CreateIdentity() }));
|
||||
|
||||
class EditorTransformComponentSelectionMultipleEntitiesPivotFixture
|
||||
: public EditorTransformComponentSelectionFixture
|
||||
, public ::testing::WithParamInterface<ReferenceFrameWithOrientation> {};
|
||||
|
||||
TEST_P(
|
||||
EditorTransformComponentSelectionMultipleEntitiesPivotFixture,
|
||||
PivotOrientationMatchesReferenceFrameMultipleEntities)
|
||||
, public ::testing::WithParamInterface<ReferenceFrameWithOrientation>
|
||||
{
|
||||
};
|
||||
|
||||
TEST_P(EditorTransformComponentSelectionMultipleEntitiesPivotFixture, PivotOrientationMatchesReferenceFrameMultipleEntities)
|
||||
{
|
||||
using ETCS::PivotOrientationResult;
|
||||
using ETCS::CalculatePivotOrientationForEntityIds;
|
||||
using ETCS::PivotOrientationResult;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Given
|
||||
@@ -646,23 +669,18 @@ namespace UnitTest
|
||||
|
||||
// setup entities in arbitrary triangle arrangement
|
||||
AZ::TransformBus::Event(
|
||||
m_entityIds[0], &AZ::TransformBus::Events::SetWorldTM,
|
||||
AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(-10.0f)));
|
||||
m_entityIds[0], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(-10.0f)));
|
||||
|
||||
AZ::TransformBus::Event(
|
||||
m_entityIds[1], &AZ::TransformBus::Events::SetWorldTM,
|
||||
AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(10.0f)));
|
||||
m_entityIds[1], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(10.0f)));
|
||||
|
||||
AZ::TransformBus::Event(
|
||||
m_entityIds[2], &AZ::TransformBus::Events::SetWorldTM,
|
||||
AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(10.0f)));
|
||||
m_entityIds[2], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(10.0f)));
|
||||
|
||||
// note: EntityIdManipulatorLookup{} is unused during this test
|
||||
EntityIdManipulatorLookups lookups {
|
||||
{m_entityIds[0], EntityIdManipulatorLookup{}},
|
||||
{m_entityIds[1], EntityIdManipulatorLookup{}},
|
||||
{m_entityIds[2], EntityIdManipulatorLookup{}}
|
||||
};
|
||||
EntityIdManipulatorLookups lookups{ { m_entityIds[0], EntityIdManipulatorLookup{} },
|
||||
{ m_entityIds[1], EntityIdManipulatorLookup{} },
|
||||
{ m_entityIds[2], EntityIdManipulatorLookup{} } };
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -691,14 +709,16 @@ namespace UnitTest
|
||||
|
||||
class EditorTransformComponentSelectionMultipleEntitiesWithSameParentPivotFixture
|
||||
: public EditorTransformComponentSelectionFixture
|
||||
, public ::testing::WithParamInterface<ReferenceFrameWithOrientation> {};
|
||||
, public ::testing::WithParamInterface<ReferenceFrameWithOrientation>
|
||||
{
|
||||
};
|
||||
|
||||
TEST_P(
|
||||
EditorTransformComponentSelectionMultipleEntitiesWithSameParentPivotFixture,
|
||||
PivotOrientationMatchesReferenceFrameMultipleEntitiesSameParent)
|
||||
{
|
||||
using ETCS::PivotOrientationResult;
|
||||
using ETCS::CalculatePivotOrientationForEntityIds;
|
||||
using ETCS::PivotOrientationResult;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Given
|
||||
@@ -711,22 +731,18 @@ namespace UnitTest
|
||||
ParentExpectedPivotLocalOrientationInWorldSpace, AZ::Vector3::CreateAxisZ(-5.0f)));
|
||||
|
||||
AZ::TransformBus::Event(
|
||||
m_entityIds[1], &AZ::TransformBus::Events::SetWorldTM,
|
||||
AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(10.0f)));
|
||||
m_entityIds[1], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(10.0f)));
|
||||
|
||||
AZ::TransformBus::Event(
|
||||
m_entityIds[2], &AZ::TransformBus::Events::SetWorldTM,
|
||||
AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(10.0f)));
|
||||
m_entityIds[2], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(10.0f)));
|
||||
|
||||
AZ::TransformBus::Event(m_entityIds[1], &AZ::TransformBus::Events::SetParent, m_entityIds[0]);
|
||||
AZ::TransformBus::Event(m_entityIds[2], &AZ::TransformBus::Events::SetParent, m_entityIds[0]);
|
||||
|
||||
// note: EntityIdManipulatorLookup{} is unused during this test
|
||||
// only select second two entities that are children of m_entityIds[0]
|
||||
EntityIdManipulatorLookups lookups{
|
||||
{m_entityIds[1], EntityIdManipulatorLookup{}},
|
||||
{m_entityIds[2], EntityIdManipulatorLookup{}}
|
||||
};
|
||||
EntityIdManipulatorLookups lookups{ { m_entityIds[1], EntityIdManipulatorLookup{} },
|
||||
{ m_entityIds[2], EntityIdManipulatorLookup{} } };
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -755,14 +771,16 @@ namespace UnitTest
|
||||
|
||||
class EditorTransformComponentSelectionMultipleEntitiesWithDifferentParentPivotFixture
|
||||
: public EditorTransformComponentSelectionFixture
|
||||
, public ::testing::WithParamInterface<ReferenceFrameWithOrientation> {};
|
||||
, public ::testing::WithParamInterface<ReferenceFrameWithOrientation>
|
||||
{
|
||||
};
|
||||
|
||||
TEST_P(
|
||||
EditorTransformComponentSelectionMultipleEntitiesWithDifferentParentPivotFixture,
|
||||
PivotOrientationMatchesReferenceFrameMultipleEntitiesDifferentParent)
|
||||
{
|
||||
using ETCS::PivotOrientationResult;
|
||||
using ETCS::CalculatePivotOrientationForEntityIds;
|
||||
using ETCS::PivotOrientationResult;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Given
|
||||
@@ -776,22 +794,18 @@ namespace UnitTest
|
||||
ParentExpectedPivotLocalOrientationInWorldSpace, AZ::Vector3::CreateAxisZ(-5.0f)));
|
||||
|
||||
AZ::TransformBus::Event(
|
||||
m_entityIds[1], &AZ::TransformBus::Events::SetWorldTM,
|
||||
AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(10.0f)));
|
||||
m_entityIds[1], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(10.0f)));
|
||||
|
||||
AZ::TransformBus::Event(
|
||||
m_entityIds[2], &AZ::TransformBus::Events::SetWorldTM,
|
||||
AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(10.0f)));
|
||||
m_entityIds[2], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(10.0f)));
|
||||
|
||||
AZ::TransformBus::Event(m_entityIds[1], &AZ::TransformBus::Events::SetParent, m_entityIds[0]);
|
||||
AZ::TransformBus::Event(m_entityIds[2], &AZ::TransformBus::Events::SetParent, m_entityIds[3]);
|
||||
|
||||
// note: EntityIdManipulatorLookup{} is unused during this test
|
||||
// only select second two entities that are children of different m_entities
|
||||
EntityIdManipulatorLookups lookups{
|
||||
{m_entityIds[1], EntityIdManipulatorLookup{}},
|
||||
{m_entityIds[2], EntityIdManipulatorLookup{}}
|
||||
};
|
||||
EntityIdManipulatorLookups lookups{ { m_entityIds[1], EntityIdManipulatorLookup{} },
|
||||
{ m_entityIds[2], EntityIdManipulatorLookup{} } };
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -819,30 +833,29 @@ namespace UnitTest
|
||||
|
||||
class EditorTransformComponentSelectionSingleEntityPivotAndOverrideFixture
|
||||
: public EditorTransformComponentSelectionFixture
|
||||
, public ::testing::WithParamInterface<ReferenceFrameWithOrientation> {};
|
||||
, public ::testing::WithParamInterface<ReferenceFrameWithOrientation>
|
||||
{
|
||||
};
|
||||
|
||||
TEST_P(
|
||||
EditorTransformComponentSelectionSingleEntityPivotAndOverrideFixture,
|
||||
PivotOrientationMatchesReferenceFrameSingleEntityOptionalOverride)
|
||||
{
|
||||
using ETCS::PivotOrientationResult;
|
||||
using ETCS::CalculateSelectionPivotOrientation;
|
||||
using ETCS::PivotOrientationResult;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Given
|
||||
AZ::TransformBus::Event(
|
||||
m_entityIds[0], &AZ::TransformBus::Events::SetWorldTM,
|
||||
AZ::Transform::CreateFromQuaternionAndTranslation(
|
||||
ChildExpectedPivotLocalOrientationInWorldSpace, AZ::Vector3::CreateZero()));
|
||||
AZ::Transform::CreateFromQuaternionAndTranslation(ChildExpectedPivotLocalOrientationInWorldSpace, AZ::Vector3::CreateZero()));
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// When
|
||||
const ReferenceFrameWithOrientation referenceFrameWithOrientation = GetParam();
|
||||
|
||||
EntityIdManipulatorLookups lookups{
|
||||
{m_entityIds[0], EntityIdManipulatorLookup{}}
|
||||
};
|
||||
EntityIdManipulatorLookups lookups{ { m_entityIds[0], EntityIdManipulatorLookup{} } };
|
||||
|
||||
// set override frame (orientation only)
|
||||
OptionalFrame optionalFrame;
|
||||
@@ -870,14 +883,16 @@ namespace UnitTest
|
||||
|
||||
class EditorTransformComponentSelectionMultipleEntitiesPivotAndOverrideFixture
|
||||
: public EditorTransformComponentSelectionFixture
|
||||
, public ::testing::WithParamInterface<ReferenceFrameWithOrientation> {};
|
||||
, public ::testing::WithParamInterface<ReferenceFrameWithOrientation>
|
||||
{
|
||||
};
|
||||
|
||||
TEST_P(
|
||||
EditorTransformComponentSelectionMultipleEntitiesPivotAndOverrideFixture,
|
||||
PivotOrientationMatchesReferenceFrameMultipleEntitiesOptionalOverride)
|
||||
{
|
||||
using ETCS::PivotOrientationResult;
|
||||
using ETCS::CalculateSelectionPivotOrientation;
|
||||
using ETCS::PivotOrientationResult;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Given
|
||||
@@ -885,23 +900,18 @@ namespace UnitTest
|
||||
m_entityIds.push_back(CreateDefaultEditorEntity("Entity3"));
|
||||
|
||||
AZ::TransformBus::Event(
|
||||
m_entityIds[0], &AZ::TransformBus::Events::SetWorldTM,
|
||||
AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(-10.0f)));
|
||||
m_entityIds[0], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(-10.0f)));
|
||||
|
||||
AZ::TransformBus::Event(
|
||||
m_entityIds[1], &AZ::TransformBus::Events::SetWorldTM,
|
||||
AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(10.0f)));
|
||||
m_entityIds[1], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(10.0f)));
|
||||
|
||||
AZ::TransformBus::Event(
|
||||
m_entityIds[2], &AZ::TransformBus::Events::SetWorldTM,
|
||||
AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(10.0f)));
|
||||
m_entityIds[2], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(10.0f)));
|
||||
|
||||
// note: EntityIdManipulatorLookup{} is unused during this test
|
||||
EntityIdManipulatorLookups lookups{
|
||||
{m_entityIds[0], EntityIdManipulatorLookup{}},
|
||||
{m_entityIds[1], EntityIdManipulatorLookup{}},
|
||||
{m_entityIds[2], EntityIdManipulatorLookup{}}
|
||||
};
|
||||
EntityIdManipulatorLookups lookups{ { m_entityIds[0], EntityIdManipulatorLookup{} },
|
||||
{ m_entityIds[1], EntityIdManipulatorLookup{} },
|
||||
{ m_entityIds[2], EntityIdManipulatorLookup{} } };
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -932,14 +942,16 @@ namespace UnitTest
|
||||
|
||||
class EditorTransformComponentSelectionMultipleEntitiesPivotAndNoOverrideFixture
|
||||
: public EditorTransformComponentSelectionFixture
|
||||
, public ::testing::WithParamInterface<ReferenceFrameWithOrientation> {};
|
||||
, public ::testing::WithParamInterface<ReferenceFrameWithOrientation>
|
||||
{
|
||||
};
|
||||
|
||||
TEST_P(
|
||||
EditorTransformComponentSelectionMultipleEntitiesPivotAndNoOverrideFixture,
|
||||
PivotOrientationMatchesReferenceFrameMultipleEntitiesNoOptionalOverride)
|
||||
{
|
||||
using ETCS::PivotOrientationResult;
|
||||
using ETCS::CalculateSelectionPivotOrientation;
|
||||
using ETCS::PivotOrientationResult;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Given
|
||||
@@ -947,23 +959,18 @@ namespace UnitTest
|
||||
m_entityIds.push_back(CreateDefaultEditorEntity("Entity3"));
|
||||
|
||||
AZ::TransformBus::Event(
|
||||
m_entityIds[0], &AZ::TransformBus::Events::SetWorldTM,
|
||||
AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(-10.0f)));
|
||||
m_entityIds[0], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(-10.0f)));
|
||||
|
||||
AZ::TransformBus::Event(
|
||||
m_entityIds[1], &AZ::TransformBus::Events::SetWorldTM,
|
||||
AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(10.0f)));
|
||||
m_entityIds[1], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(10.0f)));
|
||||
|
||||
AZ::TransformBus::Event(
|
||||
m_entityIds[2], &AZ::TransformBus::Events::SetWorldTM,
|
||||
AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(10.0f)));
|
||||
m_entityIds[2], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(10.0f)));
|
||||
|
||||
// note: EntityIdManipulatorLookup{} is unused during this test
|
||||
EntityIdManipulatorLookups lookups{
|
||||
{m_entityIds[0], EntityIdManipulatorLookup{}},
|
||||
{m_entityIds[1], EntityIdManipulatorLookup{}},
|
||||
{m_entityIds[2], EntityIdManipulatorLookup{}}
|
||||
};
|
||||
EntityIdManipulatorLookups lookups{ { m_entityIds[0], EntityIdManipulatorLookup{} },
|
||||
{ m_entityIds[1], EntityIdManipulatorLookup{} },
|
||||
{ m_entityIds[2], EntityIdManipulatorLookup{} } };
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -992,14 +999,16 @@ namespace UnitTest
|
||||
|
||||
class EditorTransformComponentSelectionMultipleEntitiesSameParentPivotAndNoOverrideFixture
|
||||
: public EditorTransformComponentSelectionFixture
|
||||
, public ::testing::WithParamInterface<ReferenceFrameWithOrientation> {};
|
||||
, public ::testing::WithParamInterface<ReferenceFrameWithOrientation>
|
||||
{
|
||||
};
|
||||
|
||||
TEST_P(
|
||||
EditorTransformComponentSelectionMultipleEntitiesSameParentPivotAndNoOverrideFixture,
|
||||
PivotOrientationMatchesReferenceFrameMultipleEntitiesSameParentNoOptionalOverride)
|
||||
{
|
||||
using ETCS::PivotOrientationResult;
|
||||
using ETCS::CalculateSelectionPivotOrientation;
|
||||
using ETCS::PivotOrientationResult;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Given
|
||||
@@ -1012,21 +1021,17 @@ namespace UnitTest
|
||||
ParentExpectedPivotLocalOrientationInWorldSpace, AZ::Vector3::CreateAxisZ(-5.0f)));
|
||||
|
||||
AZ::TransformBus::Event(
|
||||
m_entityIds[1], &AZ::TransformBus::Events::SetWorldTM,
|
||||
AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(10.0f)));
|
||||
m_entityIds[1], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(10.0f)));
|
||||
|
||||
AZ::TransformBus::Event(
|
||||
m_entityIds[2], &AZ::TransformBus::Events::SetWorldTM,
|
||||
AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(10.0f)));
|
||||
m_entityIds[2], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(10.0f)));
|
||||
|
||||
AZ::TransformBus::Event(m_entityIds[1], &AZ::TransformBus::Events::SetParent, m_entityIds[0]);
|
||||
AZ::TransformBus::Event(m_entityIds[2], &AZ::TransformBus::Events::SetParent, m_entityIds[0]);
|
||||
|
||||
// note: EntityIdManipulatorLookup{} is unused during this test
|
||||
EntityIdManipulatorLookups lookups{
|
||||
{m_entityIds[1], EntityIdManipulatorLookup{}},
|
||||
{m_entityIds[2], EntityIdManipulatorLookup{}}
|
||||
};
|
||||
EntityIdManipulatorLookups lookups{ { m_entityIds[1], EntityIdManipulatorLookup{} },
|
||||
{ m_entityIds[2], EntityIdManipulatorLookup{} } };
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -1174,13 +1179,13 @@ namespace UnitTest
|
||||
AZ::TransformBus::Event(f, &AZ::TransformBus::Events::SetParent, secondLayerId);
|
||||
|
||||
// Layer1
|
||||
// A
|
||||
// B
|
||||
// C
|
||||
// Layer2
|
||||
// D
|
||||
// E
|
||||
// F
|
||||
// A
|
||||
// B
|
||||
// C
|
||||
// Layer2
|
||||
// D
|
||||
// E
|
||||
// F
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -1270,13 +1275,13 @@ namespace UnitTest
|
||||
AZ::TransformBus::Event(f, &AZ::TransformBus::Events::SetParent, secondLayerId);
|
||||
|
||||
// Layer1
|
||||
// A
|
||||
// B
|
||||
// C
|
||||
// Layer2
|
||||
// D
|
||||
// E
|
||||
// F
|
||||
// A
|
||||
// B
|
||||
// C
|
||||
// Layer2
|
||||
// D
|
||||
// E
|
||||
// F
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -1368,8 +1373,7 @@ namespace UnitTest
|
||||
EXPECT_TRUE(!IsEntityVisible(m_layerId));
|
||||
|
||||
bool flagSetVisible = false;
|
||||
EditorVisibilityRequestBus::EventResult(
|
||||
flagSetVisible, m_layerId, &EditorVisibilityRequestBus::Events::GetVisibilityFlag);
|
||||
EditorVisibilityRequestBus::EventResult(flagSetVisible, m_layerId, &EditorVisibilityRequestBus::Events::GetVisibilityFlag);
|
||||
|
||||
// even though a layer is set to not be visible, this is recorded by SetLayerChildrenVisibility
|
||||
// and AreLayerChildrenVisible - the visibility flag will not be modified and remains true
|
||||
@@ -1377,12 +1381,12 @@ namespace UnitTest
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
}
|
||||
|
||||
class EditorEntityInfoRequestActivateTestComponent
|
||||
: public AzToolsFramework::Components::EditorComponentBase
|
||||
class EditorEntityInfoRequestActivateTestComponent : public AzToolsFramework::Components::EditorComponentBase
|
||||
{
|
||||
public:
|
||||
AZ_EDITOR_COMPONENT(
|
||||
EditorEntityInfoRequestActivateTestComponent, "{849DA1FC-6A0C-4CB8-A0BB-D90DEE7FF7F7}",
|
||||
EditorEntityInfoRequestActivateTestComponent,
|
||||
"{849DA1FC-6A0C-4CB8-A0BB-D90DEE7FF7F7}",
|
||||
AzToolsFramework::Components::EditorComponentBase);
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
@@ -1391,13 +1395,13 @@ namespace UnitTest
|
||||
void Activate() override
|
||||
{
|
||||
// ensure we can successfully read IsVisible and IsLocked (bus will be connected to in entity Init)
|
||||
EditorEntityInfoRequestBus::EventResult(
|
||||
m_visible, GetEntityId(), &EditorEntityInfoRequestBus::Events::IsVisible);
|
||||
EditorEntityInfoRequestBus::EventResult(
|
||||
m_locked, GetEntityId(), &EditorEntityInfoRequestBus::Events::IsLocked);
|
||||
EditorEntityInfoRequestBus::EventResult(m_visible, GetEntityId(), &EditorEntityInfoRequestBus::Events::IsVisible);
|
||||
EditorEntityInfoRequestBus::EventResult(m_locked, GetEntityId(), &EditorEntityInfoRequestBus::Events::IsLocked);
|
||||
}
|
||||
|
||||
void Deactivate() override {}
|
||||
void Deactivate() override
|
||||
{
|
||||
}
|
||||
|
||||
bool m_visible = false;
|
||||
bool m_locked = true;
|
||||
@@ -1407,14 +1411,11 @@ namespace UnitTest
|
||||
{
|
||||
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
|
||||
{
|
||||
serializeContext->Class<EditorEntityInfoRequestActivateTestComponent>()
|
||||
->Version(0)
|
||||
;
|
||||
serializeContext->Class<EditorEntityInfoRequestActivateTestComponent>()->Version(0);
|
||||
}
|
||||
}
|
||||
|
||||
class EditorEntityModelEntityInfoRequestFixture
|
||||
: public ToolsApplicationFixture
|
||||
class EditorEntityModelEntityInfoRequestFixture : public ToolsApplicationFixture
|
||||
{
|
||||
public:
|
||||
void SetUpEditorFixtureImpl() override
|
||||
@@ -1435,8 +1436,7 @@ namespace UnitTest
|
||||
|
||||
// This is necessary to prevent a warning in the undo system.
|
||||
AzToolsFramework::ToolsApplicationRequests::Bus::Broadcast(
|
||||
&AzToolsFramework::ToolsApplicationRequests::Bus::Events::AddDirtyEntity,
|
||||
entity->GetId());
|
||||
&AzToolsFramework::ToolsApplicationRequests::Bus::Events::AddDirtyEntity, entity->GetId());
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -1469,8 +1469,7 @@ namespace UnitTest
|
||||
|
||||
// This is necessary to prevent a warning in the undo system.
|
||||
AzToolsFramework::ToolsApplicationRequests::Bus::Broadcast(
|
||||
&AzToolsFramework::ToolsApplicationRequests::Bus::Events::AddDirtyEntity,
|
||||
entity->GetId());
|
||||
&AzToolsFramework::ToolsApplicationRequests::Bus::Events::AddDirtyEntity, entity->GetId());
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,878 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates, or
|
||||
* a third party where indicated.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
/*
|
||||
* Temporary dynamic tree structure used internally by GridMate.
|
||||
* To be replaced with a general Vis framework when that becomes available.
|
||||
*/
|
||||
|
||||
#ifndef RR_DYNAMIC_BOUNDING_VOLUME_TREE_H
|
||||
#define RR_DYNAMIC_BOUNDING_VOLUME_TREE_H
|
||||
|
||||
#include <AzCore/Math/Vector3.h>
|
||||
#include <AzCore/Math/Aabb.h>
|
||||
#include <AzCore/Math/Plane.h>
|
||||
|
||||
#include <GridMate/Containers/vector.h>
|
||||
#include <AzCore/std/containers/fixed_vector.h>
|
||||
|
||||
namespace GridMate
|
||||
{
|
||||
namespace Internal
|
||||
{
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class DynamicTreeAabb : public AZ::Aabb
|
||||
{
|
||||
public:
|
||||
GM_CLASS_ALLOCATOR(DynamicTreeAabb);
|
||||
|
||||
AZ_FORCE_INLINE explicit DynamicTreeAabb() {}
|
||||
AZ_FORCE_INLINE DynamicTreeAabb(const AZ::Aabb& aabb) : AZ::Aabb(aabb) {}
|
||||
AZ_FORCE_INLINE explicit DynamicTreeAabb(const AZ::Vector3& min,const AZ::Vector3& max) : AZ::Aabb(AZ::Aabb::CreateFromMinMax(min,max)) {}
|
||||
|
||||
AZ_FORCE_INLINE static DynamicTreeAabb CreateFromFacePoints(const AZ::Vector3& a, const AZ::Vector3& b, const AZ::Vector3& c)
|
||||
{
|
||||
DynamicTreeAabb vol(a,a);
|
||||
vol.AddPoint(b);
|
||||
vol.AddPoint(c);
|
||||
return vol;
|
||||
}
|
||||
|
||||
AZ_FORCE_INLINE void SignedExpand(const AZ::Vector3& e)
|
||||
{
|
||||
AZ::Vector3 zero = AZ::Vector3::CreateZero();
|
||||
AZ::Vector3 mxE = m_max + e;
|
||||
AZ::Vector3 miE = m_min + e;
|
||||
m_max = AZ::Vector3::CreateSelectCmpGreater(e,zero,mxE,m_max );
|
||||
m_min = AZ::Vector3::CreateSelectCmpGreater(e,zero,m_min,miE);
|
||||
}
|
||||
AZ_FORCE_INLINE int Classify(const AZ::Vector3& n,const float o,int s) const
|
||||
{
|
||||
AZ::Vector3 pi, px;
|
||||
switch(s)
|
||||
{
|
||||
case (0+0+0): px=m_min;
|
||||
pi=m_max; break;
|
||||
case (1+0+0): px=AZ::Vector3(m_max.GetX(),m_min.GetY(),m_min.GetZ());
|
||||
pi=AZ::Vector3(m_min.GetX(),m_max.GetY(),m_max.GetZ());break;
|
||||
case (0+2+0): px=AZ::Vector3(m_min.GetX(),m_max.GetY(),m_min.GetZ());
|
||||
pi=AZ::Vector3(m_max.GetX(),m_min.GetY(),m_max.GetZ());break;
|
||||
case (1+2+0): px=AZ::Vector3(m_max.GetX(),m_max.GetY(),m_min.GetZ());
|
||||
pi=AZ::Vector3(m_min.GetX(),m_min.GetY(),m_max.GetZ());break;
|
||||
case (0+0+4): px=AZ::Vector3(m_min.GetX(),m_min.GetY(),m_max.GetZ());
|
||||
pi=AZ::Vector3(m_max.GetX(),m_max.GetY(),m_min.GetZ());break;
|
||||
case (1+0+4): px=AZ::Vector3(m_max.GetX(),m_min.GetY(),m_max.GetZ());
|
||||
pi=AZ::Vector3(m_min.GetX(),m_max.GetY(),m_min.GetZ());break;
|
||||
case (0+2+4): px=AZ::Vector3(m_min.GetX(),m_max.GetY(),m_max.GetZ());
|
||||
pi=AZ::Vector3(m_max.GetX(),m_min.GetY(),m_min.GetZ());break;
|
||||
case (1+2+4): px=m_max;
|
||||
pi=m_min;break;
|
||||
}
|
||||
|
||||
if (n.Dot(px) + o < 0.0f)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (n.Dot(pi) + o > 0.0f)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
AZ_FORCE_INLINE float ProjectMinimum(const AZ::Vector3& v, unsigned signs) const
|
||||
{
|
||||
const AZ::Vector3* b[]={&m_max,&m_min};
|
||||
const AZ::Vector3 p( b[(signs>>0)&1]->GetX(),b[(signs>>1)&1]->GetY(),b[(signs>>2)&1]->GetZ());
|
||||
return p.Dot(v);
|
||||
}
|
||||
|
||||
// Move the code here
|
||||
AZ_FORCE_INLINE friend bool IntersectAabbAabb(const DynamicTreeAabb& a,const DynamicTreeAabb& b);
|
||||
AZ_FORCE_INLINE friend bool IntersectAabbPoint(const DynamicTreeAabb& a, const AZ::Vector3& b);
|
||||
AZ_FORCE_INLINE friend bool IntersectAabbPlane(const DynamicTreeAabb& a, const AZ::Plane& b);
|
||||
AZ_FORCE_INLINE friend float Proximity(const DynamicTreeAabb& a, const DynamicTreeAabb& b);
|
||||
AZ_FORCE_INLINE friend int Select(const DynamicTreeAabb& o, const DynamicTreeAabb& a, const DynamicTreeAabb& b);
|
||||
AZ_FORCE_INLINE friend void Merge(const DynamicTreeAabb& a, const DynamicTreeAabb& b, DynamicTreeAabb& r);
|
||||
AZ_FORCE_INLINE friend bool NotEqual(const DynamicTreeAabb& a, const DynamicTreeAabb& b);
|
||||
private:
|
||||
AZ_FORCE_INLINE void AddSpan(const AZ::Vector3& d, float& smi, float& smx) const
|
||||
{
|
||||
AZ::Vector3 vecZero = AZ::Vector3::CreateZero();
|
||||
AZ::Vector3 mxD = m_max*d;
|
||||
AZ::Vector3 miD = m_min*d;
|
||||
AZ::Vector3 smiAdd = AZ::Vector3::CreateSelectCmpGreater(vecZero,d,mxD,miD);
|
||||
AZ::Vector3 smxAdd = AZ::Vector3::CreateSelectCmpGreater(vecZero,d,miD,mxD);
|
||||
AZ::Vector3 vecOne = AZ::Vector3::CreateOne();
|
||||
// sum components
|
||||
smi += smiAdd.Dot(vecOne);
|
||||
smx += smxAdd.Dot(vecOne);
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
AZ_FORCE_INLINE bool IntersectAabbAabb(const DynamicTreeAabb& a, const DynamicTreeAabb& b)
|
||||
{
|
||||
return a.Overlaps(b);
|
||||
}
|
||||
|
||||
AZ_FORCE_INLINE bool IntersectAabbPlane(const DynamicTreeAabb& a, const AZ::Plane& b)
|
||||
{
|
||||
//use plane normal to quickly select the nearest corner of the aabb
|
||||
AZ::Vector3 testPoint = AZ::Vector3::CreateSelectCmpGreater(b.GetNormal(), AZ::Vector3::CreateZero(), a.GetMin(), a.GetMax());
|
||||
//test if nearest point is inside the plane
|
||||
return b.GetPointDist(testPoint) <= 0.0f;
|
||||
}
|
||||
|
||||
//
|
||||
AZ_FORCE_INLINE float Proximity(const DynamicTreeAabb& a, const DynamicTreeAabb& b)
|
||||
{
|
||||
const AZ::Vector3 d=(a.m_min+a.m_max)-(b.m_min+b.m_max);
|
||||
// get abs and sum
|
||||
return d.GetAbs().Dot(AZ::Vector3::CreateOne());
|
||||
}
|
||||
|
||||
//
|
||||
AZ_FORCE_INLINE int Select( const DynamicTreeAabb& o, const DynamicTreeAabb& a, const DynamicTreeAabb& b)
|
||||
{
|
||||
return Proximity(o,a) < Proximity(o,b);
|
||||
}
|
||||
|
||||
//
|
||||
AZ_FORCE_INLINE void Merge(const DynamicTreeAabb& a, const DynamicTreeAabb& b, DynamicTreeAabb& r)
|
||||
{
|
||||
r.m_min = AZ::Vector3::CreateSelectCmpGreater(b.m_min,a.m_min,a.m_min,b.m_min);
|
||||
r.m_max = AZ::Vector3::CreateSelectCmpGreater(a.m_max,b.m_max,a.m_max,b.m_max);
|
||||
}
|
||||
|
||||
//
|
||||
AZ_FORCE_INLINE bool NotEqual( const DynamicTreeAabb& a, const DynamicTreeAabb& b)
|
||||
{
|
||||
return (a.m_min != b.m_min || a.m_max != b.m_max);
|
||||
}
|
||||
|
||||
|
||||
/* NodeType */
|
||||
struct DynamicTreeNode
|
||||
{
|
||||
GM_CLASS_ALLOCATOR(DynamicTreeNode);
|
||||
|
||||
DynamicTreeAabb m_volume;
|
||||
DynamicTreeNode* m_parent;
|
||||
AZ_FORCE_INLINE bool IsLeaf() const { return(m_childs[1]==0); }
|
||||
AZ_FORCE_INLINE bool IsInternal() const { return(!IsLeaf()); }
|
||||
union
|
||||
{
|
||||
DynamicTreeNode* m_childs[2];
|
||||
void* m_data;
|
||||
int m_dataAsInt;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of dynamic aabb tree, based on the bullet dynamic tree (btDbvt).
|
||||
*
|
||||
* The BvDynamicTree class implements a fast dynamic bounding volume tree based on axis aligned bounding boxes (aabb tree).
|
||||
* This BvDynamicTree is used for soft body collision detection and for the btDbvtBroadphase. It has a fast insert, remove and update of nodes.
|
||||
* Unlike the BvTreeQuantized, nodes can be dynamically moved around, which allows for change in topology of the underlying data structure.
|
||||
*/
|
||||
class BvDynamicTree
|
||||
{
|
||||
public:
|
||||
using Ptr = AZStd::intrusive_ptr<BvDynamicTree>;
|
||||
|
||||
GM_CLASS_ALLOCATOR(BvDynamicTree);
|
||||
|
||||
typedef Internal::DynamicTreeAabb VolumeType;
|
||||
typedef Internal::DynamicTreeNode NodeType;
|
||||
|
||||
typedef vector<NodeType*> NodeArrayType;
|
||||
typedef vector<const NodeType*> ConstNodeArrayType;
|
||||
|
||||
private:
|
||||
|
||||
/* Stack element */
|
||||
struct sStkNN
|
||||
{
|
||||
const NodeType* a;
|
||||
const NodeType* b;
|
||||
sStkNN() {}
|
||||
sStkNN(const NodeType* na,const NodeType* nb) : a(na), b(nb) {}
|
||||
};
|
||||
struct sStkNP
|
||||
{
|
||||
const NodeType* node;
|
||||
int mask;
|
||||
sStkNP(const NodeType* n, unsigned m) : node(n), mask(m) {}
|
||||
};
|
||||
struct sStkNPS
|
||||
{
|
||||
const NodeType* node;
|
||||
int mask;
|
||||
float value;
|
||||
sStkNPS() {}
|
||||
sStkNPS(const NodeType* n, unsigned m, const float v) : node(n), mask(m), value(v) {}
|
||||
};
|
||||
struct sStkCLN
|
||||
{
|
||||
const NodeType* node;
|
||||
NodeType* parent;
|
||||
sStkCLN(const NodeType* n, NodeType* p) : node(n), parent(p) {}
|
||||
};
|
||||
|
||||
public:
|
||||
/* ICollideCollector templated collectors should implement this functions or inherit from this class */
|
||||
struct ICollideCollector
|
||||
{
|
||||
void Process(const NodeType*, const NodeType*) {}
|
||||
void Process(const NodeType*) {}
|
||||
void Process(const NodeType* n, const float) { Process(n); }
|
||||
bool Descent(const NodeType*) { return true; }
|
||||
bool AllLeaves(const NodeType*) { return true; }
|
||||
};
|
||||
|
||||
/* IWriter */
|
||||
struct IWriter
|
||||
{
|
||||
virtual ~IWriter() {}
|
||||
virtual void Prepare(const NodeType* root,int numnodes) = 0;
|
||||
virtual void WriteNode(const NodeType*, int index, int parent, int child0, int child1) = 0;
|
||||
virtual void WriteLeaf(const NodeType*, int index, int parent) = 0;
|
||||
};
|
||||
/* IClone */
|
||||
struct IClone
|
||||
{
|
||||
virtual ~IClone() {}
|
||||
virtual void CloneLeaf(NodeType*) {}
|
||||
};
|
||||
|
||||
// Constants
|
||||
enum
|
||||
{
|
||||
SIMPLE_STACKSIZE = 64,
|
||||
DOUBLE_STACKSIZE = SIMPLE_STACKSIZE * 2
|
||||
};
|
||||
|
||||
// Methods
|
||||
BvDynamicTree();
|
||||
~BvDynamicTree();
|
||||
|
||||
NodeType* GetRoot() const { return m_root; }
|
||||
void Clear();
|
||||
bool Empty() const { return 0 == m_root; }
|
||||
int GetNumLeaves() const { return m_leaves; }
|
||||
void OptimizeBottomUp();
|
||||
void OptimizeTopDown(int bu_treshold = 128);
|
||||
void OptimizeIncremental(int passes);
|
||||
NodeType* Insert(const VolumeType& box,void* data);
|
||||
void Update(NodeType* leaf, int lookahead=-1);
|
||||
void Update(NodeType* leaf, VolumeType& volume);
|
||||
bool Update(NodeType* leaf, VolumeType& volume, const AZ::Vector3& velocity, const float margin);
|
||||
bool Update(NodeType* leaf, VolumeType& volume, const AZ::Vector3& velocity);
|
||||
bool Update(NodeType* leaf, VolumeType& volume, const float margin);
|
||||
void Remove(NodeType* leaf);
|
||||
void Write(IWriter* iwriter) const;
|
||||
void Clone(BvDynamicTree& dest, IClone* iclone=0) const;
|
||||
static int GetMaxDepth(const NodeType* node);
|
||||
static int CountLeaves(const NodeType* node);
|
||||
static void ExtractLeaves(const NodeType* node, /*btAlignedObjectArray<const NodeType*>&*/vector<const NodeType*>& leaves);
|
||||
#if DBVT_ENABLE_BENCHMARK
|
||||
static void Benchmark();
|
||||
#else
|
||||
static void Benchmark(){}
|
||||
#endif
|
||||
/**
|
||||
* Collector should inherit from ICollide
|
||||
*/
|
||||
template<class Collector>
|
||||
static inline void enumNodes( const NodeType* root, Collector& collector)
|
||||
{
|
||||
collector.Process(root);
|
||||
if(root->IsInternal())
|
||||
{
|
||||
enumNodes(root->m_childs[0],collector);
|
||||
enumNodes(root->m_childs[1],collector);
|
||||
}
|
||||
}
|
||||
template<class Collector>
|
||||
static void enumLeaves( const NodeType* root,Collector& collector)
|
||||
{
|
||||
if(root->IsInternal())
|
||||
{
|
||||
enumLeaves(root->m_childs[0],collector);
|
||||
enumLeaves(root->m_childs[1],collector);
|
||||
}
|
||||
else
|
||||
{
|
||||
collector.Process(root);
|
||||
}
|
||||
}
|
||||
template<class Collector>
|
||||
void collideTT( const NodeType* root0,const NodeType* root1,Collector& collector) const
|
||||
{
|
||||
if(root0&&root1)
|
||||
{
|
||||
size_t depth=1;
|
||||
size_t treshold=DOUBLE_STACKSIZE-4;
|
||||
vector<sStkNN> stkStack;
|
||||
stkStack.resize(DOUBLE_STACKSIZE);
|
||||
stkStack[0]=sStkNN(root0,root1);
|
||||
do {
|
||||
sStkNN p=stkStack[--depth];
|
||||
if(depth>treshold)
|
||||
{
|
||||
stkStack.resize(stkStack.size()*2);
|
||||
treshold=stkStack.size()-4;
|
||||
}
|
||||
if(p.a==p.b)
|
||||
{
|
||||
if(p.a->IsInternal())
|
||||
{
|
||||
stkStack[depth++]=sStkNN(p.a->m_childs[0],p.a->m_childs[0]);
|
||||
stkStack[depth++]=sStkNN(p.a->m_childs[1],p.a->m_childs[1]);
|
||||
stkStack[depth++]=sStkNN(p.a->m_childs[0],p.a->m_childs[1]);
|
||||
}
|
||||
}
|
||||
else if(IntersectAabbAabb(p.a->m_volume,p.b->m_volume))
|
||||
{
|
||||
if(p.a->IsInternal())
|
||||
{
|
||||
if(p.b->IsInternal())
|
||||
{
|
||||
stkStack[depth++]=sStkNN(p.a->m_childs[0],p.b->m_childs[0]);
|
||||
stkStack[depth++]=sStkNN(p.a->m_childs[1],p.b->m_childs[0]);
|
||||
stkStack[depth++]=sStkNN(p.a->m_childs[0],p.b->m_childs[1]);
|
||||
stkStack[depth++]=sStkNN(p.a->m_childs[1],p.b->m_childs[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
stkStack[depth++]=sStkNN(p.a->m_childs[0],p.b);
|
||||
stkStack[depth++]=sStkNN(p.a->m_childs[1],p.b);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(p.b->IsInternal())
|
||||
{
|
||||
stkStack[depth++]=sStkNN(p.a,p.b->m_childs[0]);
|
||||
stkStack[depth++]=sStkNN(p.a,p.b->m_childs[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
collector.Process(p.a,p.b);
|
||||
}
|
||||
}
|
||||
}
|
||||
} while(depth);
|
||||
}
|
||||
}
|
||||
template<class Collector>
|
||||
void collideTTpersistentStack( const NodeType* root0, const NodeType* root1,Collector& collector)
|
||||
{
|
||||
if(root0&&root1)
|
||||
{
|
||||
size_t depth=1;
|
||||
size_t treshold=DOUBLE_STACKSIZE-4;
|
||||
|
||||
m_stkStack.resize(DOUBLE_STACKSIZE);
|
||||
m_stkStack[0]=sStkNN(root0,root1);
|
||||
do
|
||||
{
|
||||
sStkNN p=m_stkStack[--depth];
|
||||
if(depth>treshold)
|
||||
{
|
||||
m_stkStack.resize(m_stkStack.size()*2);
|
||||
treshold=m_stkStack.size()-4;
|
||||
}
|
||||
if(p.a==p.b)
|
||||
{
|
||||
if(p.a->IsInternal())
|
||||
{
|
||||
m_stkStack[depth++]=sStkNN(p.a->m_childs[0],p.a->m_childs[0]);
|
||||
m_stkStack[depth++]=sStkNN(p.a->m_childs[1],p.a->m_childs[1]);
|
||||
m_stkStack[depth++]=sStkNN(p.a->m_childs[0],p.a->m_childs[1]);
|
||||
}
|
||||
}
|
||||
else if(IntersectAabbAabb(p.a->m_volume,p.b->m_volume))
|
||||
{
|
||||
if(p.a->IsInternal())
|
||||
{
|
||||
if(p.b->IsInternal())
|
||||
{
|
||||
m_stkStack[depth++]=sStkNN(p.a->m_childs[0],p.b->m_childs[0]);
|
||||
m_stkStack[depth++]=sStkNN(p.a->m_childs[1],p.b->m_childs[0]);
|
||||
m_stkStack[depth++]=sStkNN(p.a->m_childs[0],p.b->m_childs[1]);
|
||||
m_stkStack[depth++]=sStkNN(p.a->m_childs[1],p.b->m_childs[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_stkStack[depth++]=sStkNN(p.a->m_childs[0],p.b);
|
||||
m_stkStack[depth++]=sStkNN(p.a->m_childs[1],p.b);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(p.b->IsInternal())
|
||||
{
|
||||
m_stkStack[depth++]=sStkNN(p.a,p.b->m_childs[0]);
|
||||
m_stkStack[depth++]=sStkNN(p.a,p.b->m_childs[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
collector.Process(p.a,p.b);
|
||||
}
|
||||
}
|
||||
}
|
||||
} while(depth);
|
||||
}
|
||||
}
|
||||
template<class Collector>
|
||||
void collideTV( const NodeType* root, const VolumeType& volume, Collector& collector) const
|
||||
{
|
||||
if(root)
|
||||
{
|
||||
// ATTRIBUTE_ALIGNED16(VolumeType) volume(vol);
|
||||
// btAlignedObjectArray<const NodeType*> stack;
|
||||
AZStd::fixed_vector<const NodeType*,SIMPLE_STACKSIZE> stack;
|
||||
//stack.reserve(SIMPLE_STACKSIZE);
|
||||
stack.push_back(root);
|
||||
do {
|
||||
const NodeType* n=stack[stack.size()-1];
|
||||
stack.pop_back();
|
||||
if(IntersectAabbAabb(n->m_volume,volume))
|
||||
{
|
||||
if(n->IsInternal())
|
||||
{
|
||||
stack.push_back(n->m_childs[0]);
|
||||
stack.push_back(n->m_childs[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
collector.Process(n);
|
||||
}
|
||||
}
|
||||
} while(!stack.empty());
|
||||
}
|
||||
}
|
||||
|
||||
template<class Collector>
|
||||
void collideTP(const NodeType* root, const AZ::Plane& plane, Collector& collector) const
|
||||
{
|
||||
if (root)
|
||||
{
|
||||
AZStd::fixed_vector<const NodeType*,SIMPLE_STACKSIZE> stack;
|
||||
stack.push_back(root);
|
||||
do
|
||||
{
|
||||
const NodeType* n=stack[stack.size()-1];
|
||||
stack.pop_back();
|
||||
if (IntersectAabbPlane(n->m_volume, plane))
|
||||
{
|
||||
if(n->IsInternal())
|
||||
{
|
||||
stack.push_back(n->m_childs[0]);
|
||||
stack.push_back(n->m_childs[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
collector.Process(n);
|
||||
}
|
||||
}
|
||||
} while (!stack.empty());
|
||||
}
|
||||
}
|
||||
|
||||
///rayTest is a re-entrant ray test, and can be called in parallel as long as the btAlignedAlloc is thread-safe (uses locking etc)
|
||||
///rayTest is slower than rayTestInternal, because it builds a local stack, using memory allocations, and it recomputes signs/rayDirectionInverses each time
|
||||
template<class Collector>
|
||||
static void rayTest( const NodeType* root, const AZ::Vector3& rayFrom, const AZ::Vector3& rayTo, Collector& collector)
|
||||
{
|
||||
if(root)
|
||||
{
|
||||
AZ::Vector3 ray = rayTo-rayFrom;
|
||||
AZ::Vector3 rayDir = ray.GetNormalized();
|
||||
|
||||
///what about division by zero? --> just set rayDirection[i] to INF/1e30
|
||||
AZ::Vector3 rayDirectionInverse = AZ::Vector3::CreateSelectCmpEqual(rayDir,AZ::Vector3::CreateZero(),AZ::Vector3(1e30),rayDir.GetReciprocal());
|
||||
|
||||
unsigned int signs[3];// = { rayDirectionInverse[0] < 0.0f, rayDirectionInverse[1] < 0.0f, rayDirectionInverse[2] < 0.0f };
|
||||
signs[0] = rayDirectionInverse.GetX() < 0.0f;
|
||||
signs[1] = rayDirectionInverse.GetY() < 0.0f;
|
||||
signs[2] = rayDirectionInverse.GetZ() < 0.0f;
|
||||
|
||||
//float lambda_max = rayDir.Dot(ray);
|
||||
|
||||
AZ::Vector3 resultNormal;
|
||||
|
||||
//btAlignedObjectArray<const NodeType*> stack;
|
||||
vector<const NodeType*> stack;
|
||||
|
||||
int depth=1;
|
||||
int treshold=DOUBLE_STACKSIZE-2;
|
||||
|
||||
stack.resize(DOUBLE_STACKSIZE);
|
||||
stack[0]=root;
|
||||
AZ::Vector3 bounds[2];
|
||||
do {
|
||||
const NodeType* node=stack[--depth];
|
||||
|
||||
bounds[0] = node->m_volume.GetMin();
|
||||
bounds[1] = node->m_volume.GetMax();
|
||||
|
||||
//float tmin = 1.0f;
|
||||
//float lambda_min = 0.0f;
|
||||
// todo..
|
||||
unsigned int result1 = /*btRayAabb2(rayFrom,rayDirectionInverse,signs,bounds,tmin,lambda_min,lambda_max)*/0;
|
||||
#ifdef COMPARE_BTRAY_AABB2
|
||||
float param = 1.0f;
|
||||
bool result2 = /*btRayAabb(rayFrom,rayTo,node->volume.GetMin(),node->volume.GetMax(),param,resultNormal)*/0;
|
||||
AZ_Assert(result1 == result2, "");
|
||||
#endif //TEST_BTRAY_AABB2
|
||||
if(result1)
|
||||
{
|
||||
if(node->IsInternal())
|
||||
{
|
||||
if(depth>treshold)
|
||||
{
|
||||
stack.resize(stack.size()*2);
|
||||
treshold=stack.size()-2;
|
||||
}
|
||||
stack[depth++]=node->m_childs[0];
|
||||
stack[depth++]=node->m_childs[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
collector.Process(node);
|
||||
}
|
||||
}
|
||||
} while(depth);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
///rayTestInternal is faster than rayTest, because it uses a persistent stack (to reduce dynamic memory allocations to a minimum) and it uses precomputed signs/rayInverseDirections
|
||||
///rayTestInternal is used by btDbvtBroadphase to accelerate world ray casts
|
||||
template<class Collector>
|
||||
void rayTestInternal(const NodeType* root, const AZ::Vector3& rayFrom, const AZ::Vector3& rayTo, const AZ::Vector3& rayDirectionInverse, unsigned int signs[3], const float lambda_max, const AZ::Vector3& aabbMin, const AZ::Vector3& aabbMax, Collector& collector) const
|
||||
{
|
||||
(void)rayFrom;(void)rayTo;(void)rayDirectionInverse;(void)signs;(void)lambda_max;
|
||||
if(root)
|
||||
{
|
||||
AZ::Vector3 resultNormal;
|
||||
|
||||
int depth=1;
|
||||
int treshold=DOUBLE_STACKSIZE-2;
|
||||
vector<const NodeType*> stack;
|
||||
stack.resize(DOUBLE_STACKSIZE);
|
||||
stack[0]=root;
|
||||
AZ::Vector3 bounds[2];
|
||||
do
|
||||
{
|
||||
const NodeType* node=stack[--depth];
|
||||
bounds[0] = node->m_volume.GetMin()+aabbMin;
|
||||
bounds[1] = node->m_volume.GetMax()+aabbMax;
|
||||
|
||||
//float tmin = 1.0f;
|
||||
//float lambda_min = 0.0f;
|
||||
unsigned int result1=false;
|
||||
// todo...
|
||||
result1 = /*btRayAabb2(rayFrom,rayDirectionInverse,signs,bounds,tmin,lambda_min,lambda_max)*/false;
|
||||
if(result1)
|
||||
{
|
||||
if(node->IsInternal())
|
||||
{
|
||||
if(depth>treshold)
|
||||
{
|
||||
stack.resize(stack.size()*2);
|
||||
treshold=stack.size()-2;
|
||||
}
|
||||
stack[depth++]=node->m_childs[0];
|
||||
stack[depth++]=node->m_childs[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
collector.Process(node);
|
||||
}
|
||||
}
|
||||
} while(depth);
|
||||
}
|
||||
}
|
||||
|
||||
template<class Collector>
|
||||
static void collideKDOP(const NodeType* root, const AZ::Vector3* normals, const float* offsets, int count, Collector& collector)
|
||||
{
|
||||
(void)root;(void)normals;(void)offsets;(void)count;(void)collector;
|
||||
/* if(root)
|
||||
{
|
||||
const int inside=(1<<count)-1;
|
||||
btAlignedObjectArray<sStkNP> stack;
|
||||
int signs[sizeof(unsigned)*8];
|
||||
btAssert(count<int (sizeof(signs)/sizeof(signs[0])));
|
||||
for(int i=0;i<count;++i)
|
||||
{
|
||||
signs[i]= ((normals[i].x()>=0)?1:0)+
|
||||
((normals[i].y()>=0)?2:0)+
|
||||
((normals[i].z()>=0)?4:0);
|
||||
}
|
||||
stack.reserve(SIMPLE_STACKSIZE);
|
||||
stack.push_back(sStkNP(root,0));
|
||||
do {
|
||||
sStkNP se=stack[stack.size()-1];
|
||||
bool out=false;
|
||||
stack.pop_back();
|
||||
for(int i=0,j=1;(!out)&&(i<count);++i,j<<=1)
|
||||
{
|
||||
if(0==(se.mask&j))
|
||||
{
|
||||
const int side=se.node->volume.Classify(normals[i],offsets[i],signs[i]);
|
||||
switch(side)
|
||||
{
|
||||
case -1: out=true;break;
|
||||
case +1: se.mask|=j;break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!out)
|
||||
{
|
||||
if((se.mask!=inside)&&(se.node->isinternal()))
|
||||
{
|
||||
stack.push_back(sStkNP(se.node->childs[0],se.mask));
|
||||
stack.push_back(sStkNP(se.node->childs[1],se.mask));
|
||||
}
|
||||
else
|
||||
{
|
||||
if(policy.AllLeaves(se.node)) enumLeaves(se.node,policy);
|
||||
}
|
||||
}
|
||||
} while(!stack.empty());
|
||||
}*/
|
||||
}
|
||||
template<class Collector>
|
||||
static void collideOCL( const NodeType* root, const AZ::Vector3* normals, const float* offsets, const AZ::Vector3& sortaxis, int count, Collector& collector, bool fullsort=true)
|
||||
{
|
||||
(void)root;(void)normals;(void)offsets;(void)sortaxis;(void)count;(void)offsets;(void)collector;(void)fullsort;
|
||||
/* if(root)
|
||||
{
|
||||
const unsigned srtsgns=(sortaxis[0]>=0?1:0)+
|
||||
(sortaxis[1]>=0?2:0)+
|
||||
(sortaxis[2]>=0?4:0);
|
||||
const int inside=(1<<count)-1;
|
||||
btAlignedObjectArray<sStkNPS> stock;
|
||||
btAlignedObjectArray<int> ifree;
|
||||
btAlignedObjectArray<int> stack;
|
||||
int signs[sizeof(unsigned)*8];
|
||||
btAssert(count<int (sizeof(signs)/sizeof(signs[0])));
|
||||
for(int i=0;i<count;++i)
|
||||
{
|
||||
signs[i]= ((normals[i].x()>=0)?1:0)+
|
||||
((normals[i].y()>=0)?2:0)+
|
||||
((normals[i].z()>=0)?4:0);
|
||||
}
|
||||
stock.reserve(SIMPLE_STACKSIZE);
|
||||
stack.reserve(SIMPLE_STACKSIZE);
|
||||
ifree.reserve(SIMPLE_STACKSIZE);
|
||||
stack.push_back(allocate(ifree,stock,sStkNPS(root,0,root->volume.ProjectMinimum(sortaxis,srtsgns))));
|
||||
do {
|
||||
const int id=stack[stack.size()-1];
|
||||
sStkNPS se=stock[id];
|
||||
stack.pop_back();ifree.push_back(id);
|
||||
if(se.mask!=inside)
|
||||
{
|
||||
bool out=false;
|
||||
for(int i=0,j=1;(!out)&&(i<count);++i,j<<=1)
|
||||
{
|
||||
if(0==(se.mask&j))
|
||||
{
|
||||
const int side=se.node->volume.Classify(normals[i],offsets[i],signs[i]);
|
||||
switch(side)
|
||||
{
|
||||
case -1: out=true;break;
|
||||
case +1: se.mask|=j;break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(out) continue;
|
||||
}
|
||||
if(policy.Descent(se.node))
|
||||
{
|
||||
if(se.node->isinternal())
|
||||
{
|
||||
const NodeType* pns[]={ se.node->childs[0],se.node->childs[1]};
|
||||
sStkNPS nes[]={ sStkNPS(pns[0],se.mask,pns[0]->volume.ProjectMinimum(sortaxis,srtsgns)),
|
||||
sStkNPS(pns[1],se.mask,pns[1]->volume.ProjectMinimum(sortaxis,srtsgns))};
|
||||
const int q=nes[0].value<nes[1].value?1:0;
|
||||
int j=stack.size();
|
||||
if(fsort&&(j>0))
|
||||
{
|
||||
// Insert 0
|
||||
j=nearest(&stack[0],&stock[0],nes[q].value,0,stack.size());
|
||||
stack.push_back(0);
|
||||
#if DBVT_USE_MEMMOVE
|
||||
memmove(&stack[j+1],&stack[j],sizeof(int)*(stack.size()-j-1));
|
||||
#else
|
||||
for(int k=stack.size()-1;k>j;--k) stack[k]=stack[k-1];
|
||||
#endif
|
||||
stack[j]=allocate(ifree,stock,nes[q]);
|
||||
// Insert 1
|
||||
j=nearest(&stack[0],&stock[0],nes[1-q].value,j,stack.size());
|
||||
stack.push_back(0);
|
||||
#if DBVT_USE_MEMMOVE
|
||||
memmove(&stack[j+1],&stack[j],sizeof(int)*(stack.size()-j-1));
|
||||
#else
|
||||
for(int k=stack.size()-1;k>j;--k) stack[k]=stack[k-1];
|
||||
#endif
|
||||
stack[j]=allocate(ifree,stock,nes[1-q]);
|
||||
}
|
||||
else
|
||||
{
|
||||
stack.push_back(allocate(ifree,stock,nes[q]));
|
||||
stack.push_back(allocate(ifree,stock,nes[1-q]));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
policy.Process(se.node,se.value);
|
||||
}
|
||||
}
|
||||
} while(stack.size());
|
||||
}*/
|
||||
}
|
||||
|
||||
template<class Collector>
|
||||
static void collideTU(const NodeType* root, Collector& collector)
|
||||
{
|
||||
(void)root;(void)collector;
|
||||
/* if(root)
|
||||
{
|
||||
btAlignedObjectArray<const NodeType*> stack;
|
||||
stack.reserve(SIMPLE_STACKSIZE);
|
||||
stack.push_back(root);
|
||||
do {
|
||||
const NodeType* n=stack[stack.size()-1];
|
||||
stack.pop_back();
|
||||
if(policy.Descent(n))
|
||||
{
|
||||
if(n->isinternal())
|
||||
{ stack.push_back(n->childs[0]);stack.push_back(n->childs[1]); }
|
||||
else
|
||||
{ policy.Process(n); }
|
||||
}
|
||||
} while(stack.size()>0);
|
||||
}*/
|
||||
}
|
||||
|
||||
private:
|
||||
BvDynamicTree(const BvDynamicTree&) {}
|
||||
|
||||
// Helpers
|
||||
//static AZ_FORCE_INLINE int nearest(const int* i,const BvDynamicTree::sStkNPS* a,const float& v,int l,int h)
|
||||
//{
|
||||
// int m=0;
|
||||
// while(l<h)
|
||||
// {
|
||||
// m=(l+h)>>1;
|
||||
// if(a[i[m]].value>=v) l=m+1; else h=m;
|
||||
// }
|
||||
// return h;
|
||||
//}
|
||||
//static AZ_FORCE_INLINE int allocate( int_fixed_stack_type& ifree, stknps_fixed_stack_type& stock, const sStkNPS& value)
|
||||
//{
|
||||
// int i;
|
||||
// if( !ifree.empty() )
|
||||
// {
|
||||
// i=ifree[ifree.size()-1];
|
||||
// ifree.pop_back();
|
||||
// stock[i]=value;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// i=stock.size();
|
||||
// stock.push_back(value);
|
||||
// }
|
||||
// return i;
|
||||
//}
|
||||
//
|
||||
|
||||
AZ_FORCE_INLINE void deletenode( NodeType* node)
|
||||
{
|
||||
//btAlignedFree(pdbvt->m_free);
|
||||
delete m_free;
|
||||
m_free=node;
|
||||
}
|
||||
|
||||
void recursedeletenode( NodeType* node)
|
||||
{
|
||||
if(!node->IsLeaf())
|
||||
{
|
||||
recursedeletenode(node->m_childs[0]);
|
||||
recursedeletenode(node->m_childs[1]);
|
||||
}
|
||||
|
||||
if( node == m_root ) m_root=0;
|
||||
deletenode(node);
|
||||
}
|
||||
|
||||
|
||||
AZ_FORCE_INLINE NodeType* createnode( NodeType* parent, void* data)
|
||||
{
|
||||
NodeType* node;
|
||||
if(m_free)
|
||||
{ node=m_free;m_free=0; }
|
||||
else
|
||||
{ node = aznew NodeType(); }
|
||||
node->m_parent = parent;
|
||||
node->m_data = data;
|
||||
node->m_childs[1] = 0;
|
||||
return node;
|
||||
}
|
||||
AZ_FORCE_INLINE NodeType* createnode( BvDynamicTree::NodeType* parent, const VolumeType& volume, void* data)
|
||||
{
|
||||
NodeType* node = createnode(parent,data);
|
||||
node->m_volume=volume;
|
||||
return node;
|
||||
}
|
||||
//
|
||||
AZ_FORCE_INLINE NodeType* createnode( BvDynamicTree::NodeType* parent, const VolumeType& volume0, const VolumeType& volume1, void* data)
|
||||
{
|
||||
NodeType* node = createnode(parent,data);
|
||||
Merge(volume0,volume1,node->m_volume);
|
||||
return node;
|
||||
}
|
||||
void insertleaf( NodeType* root, NodeType* leaf);
|
||||
NodeType* removeleaf( NodeType* leaf);
|
||||
void fetchleaves(NodeType* root,NodeArrayType& leaves,int depth=-1);
|
||||
void split(const NodeArrayType& leaves,NodeArrayType& left,NodeArrayType& right,const AZ::Vector3& org,const AZ::Vector3& axis);
|
||||
VolumeType bounds(const NodeArrayType& leaves);
|
||||
void bottomup( NodeArrayType& leaves );
|
||||
NodeType* topdown(NodeArrayType& leaves,int bu_treshold);
|
||||
AZ_FORCE_INLINE NodeType* sort(NodeType* n,NodeType*& r);
|
||||
|
||||
NodeType* m_root;
|
||||
NodeType* m_free;
|
||||
int m_lkhd;
|
||||
int m_leaves;
|
||||
unsigned m_opath;
|
||||
|
||||
//btAlignedObjectArray<sStkNN> m_stkStack;
|
||||
// Profile and choose static or dynamic vector.
|
||||
typedef AZStd::fixed_vector<sStkNN,DOUBLE_STACKSIZE> stknn_fixed_stack_type;
|
||||
typedef AZStd::fixed_vector<int,SIMPLE_STACKSIZE> int_fixed_stack_type;
|
||||
typedef AZStd::fixed_vector<sStkNPS,SIMPLE_STACKSIZE> stknps_fixed_stack_type;
|
||||
|
||||
stknn_fixed_stack_type m_stkStack;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // RR_DYNAMIC_BOUNDING_VOLUME_TREE_H
|
||||
#pragma once
|
||||
@@ -1,597 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <GridMate/Replica/Interest/ProximityInterestHandler.h>
|
||||
|
||||
#include <GridMate/Replica/Replica.h>
|
||||
#include <GridMate/Replica/ReplicaFunctions.h>
|
||||
#include <GridMate/Replica/ReplicaMgr.h>
|
||||
|
||||
#include <GridMate/Replica/Interest/InterestManager.h>
|
||||
#include <GridMate/Replica/Interest/BvDynamicTree.h>
|
||||
|
||||
// for highly verbose internal debugging
|
||||
//#define INTERNAL_DEBUG_PROXIMITY
|
||||
|
||||
namespace GridMate
|
||||
{
|
||||
|
||||
void ProximityInterestChunk::OnReplicaActivate(const ReplicaContext& rc)
|
||||
{
|
||||
m_interestHandler = static_cast<ProximityInterestHandler*>(rc.m_rm->GetUserContext(AZ_CRC("ProximityInterestHandler", 0x3a90b3e4)));
|
||||
AZ_Warning("GridMate", m_interestHandler, "No proximity interest handler in the user context");
|
||||
|
||||
if (m_interestHandler)
|
||||
{
|
||||
m_interestHandler->OnNewRulesChunk(this, rc.m_peer);
|
||||
}
|
||||
}
|
||||
|
||||
void ProximityInterestChunk::OnReplicaDeactivate(const ReplicaContext& rc)
|
||||
{
|
||||
if (rc.m_peer && m_interestHandler)
|
||||
{
|
||||
m_interestHandler->OnDeleteRulesChunk(this, rc.m_peer);
|
||||
}
|
||||
}
|
||||
|
||||
bool ProximityInterestChunk::AddRuleFn(RuleNetworkId netId, AZ::Aabb bbox, const RpcContext& ctx)
|
||||
{
|
||||
if (IsProxy())
|
||||
{
|
||||
auto rulePtr = m_interestHandler->CreateRule(ctx.m_sourcePeer);
|
||||
rulePtr->Set(bbox);
|
||||
m_rules.insert(AZStd::make_pair(netId, rulePtr));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ProximityInterestChunk::RemoveRuleFn(RuleNetworkId netId, const RpcContext&)
|
||||
{
|
||||
if (IsProxy())
|
||||
{
|
||||
m_rules.erase(netId);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ProximityInterestChunk::UpdateRuleFn(RuleNetworkId netId, AZ::Aabb bbox, const RpcContext&)
|
||||
{
|
||||
if (IsProxy())
|
||||
{
|
||||
auto it = m_rules.find(netId);
|
||||
if (it != m_rules.end())
|
||||
{
|
||||
it->second->Set(bbox);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ProximityInterestChunk::AddRuleForPeerFn(RuleNetworkId netId, PeerId peerId, AZ::Aabb bbox, const RpcContext&)
|
||||
{
|
||||
ProximityInterestChunk* peerChunk = m_interestHandler->FindRulesChunkByPeerId(peerId);
|
||||
if (peerChunk)
|
||||
{
|
||||
auto it = peerChunk->m_rules.find(netId);
|
||||
if (it == peerChunk->m_rules.end())
|
||||
{
|
||||
auto rulePtr = m_interestHandler->CreateRule(peerId);
|
||||
peerChunk->m_rules.insert(AZStd::make_pair(netId, rulePtr));
|
||||
rulePtr->Set(bbox);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
/*
|
||||
* ProximityInterest
|
||||
*/
|
||||
ProximityInterest::ProximityInterest(ProximityInterestHandler* handler)
|
||||
: m_handler(handler)
|
||||
, m_bbox(AZ::Aabb::CreateNull())
|
||||
{
|
||||
AZ_Assert(m_handler, "Invalid interest handler");
|
||||
}
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
/*
|
||||
* ProximityInterestRule
|
||||
*/
|
||||
void ProximityInterestRule::Set(const AZ::Aabb& bbox)
|
||||
{
|
||||
m_bbox = bbox;
|
||||
m_handler->UpdateRule(this);
|
||||
}
|
||||
|
||||
void ProximityInterestRule::Destroy()
|
||||
{
|
||||
m_handler->DestroyRule(this);
|
||||
}
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
/*
|
||||
* ProximityInterestAttribute
|
||||
*/
|
||||
void ProximityInterestAttribute::Set(const AZ::Aabb& bbox)
|
||||
{
|
||||
m_bbox = bbox;
|
||||
m_handler->UpdateAttribute(this);
|
||||
}
|
||||
|
||||
void ProximityInterestAttribute::Destroy()
|
||||
{
|
||||
m_handler->DestroyAttribute(this);
|
||||
}
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
/*
|
||||
* ProximityInterestHandler
|
||||
*/
|
||||
ProximityInterestHandler::ProximityInterestHandler()
|
||||
: m_im(nullptr)
|
||||
, m_rm(nullptr)
|
||||
, m_lastRuleNetId(0)
|
||||
, m_rulesReplica(nullptr)
|
||||
{
|
||||
m_attributeWorld = AZStd::make_unique<SpatialIndex>();
|
||||
AZ_Assert(m_attributeWorld, "Out of memory");
|
||||
}
|
||||
|
||||
ProximityInterestRule::Ptr ProximityInterestHandler::CreateRule(PeerId peerId)
|
||||
{
|
||||
ProximityInterestRule* rulePtr = aznew ProximityInterestRule(this, peerId, GetNewRuleNetId());
|
||||
if (m_rm && peerId == m_rm->GetLocalPeerId())
|
||||
{
|
||||
m_rulesReplica->AddRuleRpc(rulePtr->GetNetworkId(), rulePtr->Get());
|
||||
}
|
||||
|
||||
CreateAndInsertIntoSpatialStructure(rulePtr);
|
||||
|
||||
return rulePtr;
|
||||
}
|
||||
|
||||
ProximityInterestAttribute::Ptr ProximityInterestHandler::CreateAttribute(ReplicaId replicaId)
|
||||
{
|
||||
auto newAttribute = aznew ProximityInterestAttribute(this, replicaId);
|
||||
AZ_Assert(newAttribute, "Out of memory");
|
||||
|
||||
CreateAndInsertIntoSpatialStructure(newAttribute);
|
||||
|
||||
return newAttribute;
|
||||
}
|
||||
|
||||
void ProximityInterestHandler::FreeRule(ProximityInterestRule* rule)
|
||||
{
|
||||
//TODO: should be pool-allocated
|
||||
delete rule;
|
||||
}
|
||||
|
||||
void ProximityInterestHandler::DestroyRule(ProximityInterestRule* rule)
|
||||
{
|
||||
if (m_rm && rule->GetPeerId() == m_rm->GetLocalPeerId())
|
||||
{
|
||||
m_rulesReplica->RemoveRuleRpc(rule->GetNetworkId());
|
||||
}
|
||||
|
||||
MarkAttributesDirtyInRule(rule);
|
||||
|
||||
rule->m_bbox = AZ::Aabb::CreateNull();
|
||||
m_removedRules.insert(rule);
|
||||
m_localRules.erase(rule);
|
||||
}
|
||||
|
||||
void ProximityInterestHandler::UpdateRule(ProximityInterestRule* rule)
|
||||
{
|
||||
if (m_rm && rule->GetPeerId() == m_rm->GetLocalPeerId())
|
||||
{
|
||||
m_rulesReplica->UpdateRuleRpc(rule->GetNetworkId(), rule->Get());
|
||||
}
|
||||
|
||||
m_dirtyRules.insert(rule);
|
||||
}
|
||||
|
||||
void ProximityInterestHandler::FreeAttribute(ProximityInterestAttribute* attrib)
|
||||
{
|
||||
delete attrib;
|
||||
}
|
||||
|
||||
void ProximityInterestHandler::DestroyAttribute(ProximityInterestAttribute* attrib)
|
||||
{
|
||||
RemoveFromSpatialStructure(attrib);
|
||||
|
||||
m_attributes.erase(attrib);
|
||||
m_removedAttributes.insert(attrib);
|
||||
}
|
||||
|
||||
void ProximityInterestHandler::RemoveFromSpatialStructure(ProximityInterestAttribute* attribute)
|
||||
{
|
||||
attribute->m_bbox = AZ::Aabb::CreateNull();
|
||||
m_attributeWorld->Remove(attribute->GetNode());
|
||||
attribute->SetNode(nullptr);
|
||||
}
|
||||
|
||||
void ProximityInterestHandler::UpdateAttribute(ProximityInterestAttribute* attrib)
|
||||
{
|
||||
auto node = attrib->GetNode();
|
||||
AZ_Assert(node, "Attribute wasn't created correctly");
|
||||
node->m_volume = attrib->Get();
|
||||
m_attributeWorld->Update(node);
|
||||
|
||||
m_dirtyAttributes.insert(attrib);
|
||||
}
|
||||
|
||||
void ProximityInterestHandler::OnNewRulesChunk(ProximityInterestChunk* chunk, ReplicaPeer* peer)
|
||||
{
|
||||
if (chunk != m_rulesReplica) // non-local
|
||||
{
|
||||
m_peerChunks.insert(AZStd::make_pair(peer->GetId(), chunk));
|
||||
|
||||
for (auto& rule : m_localRules)
|
||||
{
|
||||
chunk->AddRuleForPeerRpc(rule->GetNetworkId(), rule->GetPeerId(), rule->Get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ProximityInterestHandler::OnDeleteRulesChunk(ProximityInterestChunk* chunk, ReplicaPeer* peer)
|
||||
{
|
||||
(void)chunk;
|
||||
m_peerChunks.erase(peer->GetId());
|
||||
}
|
||||
|
||||
RuleNetworkId ProximityInterestHandler::GetNewRuleNetId()
|
||||
{
|
||||
++m_lastRuleNetId;
|
||||
|
||||
if (m_rulesReplica)
|
||||
{
|
||||
return m_rulesReplica->GetReplicaId() | (static_cast<AZ::u64>(m_lastRuleNetId) << 32);
|
||||
}
|
||||
|
||||
return (static_cast<AZ::u64>(m_lastRuleNetId) << 32);
|
||||
}
|
||||
|
||||
ProximityInterestChunk* ProximityInterestHandler::FindRulesChunkByPeerId(PeerId peerId)
|
||||
{
|
||||
auto it = m_peerChunks.find(peerId);
|
||||
if (it == m_peerChunks.end())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return it->second;
|
||||
}
|
||||
|
||||
const InterestMatchResult& ProximityInterestHandler::GetLastResult()
|
||||
{
|
||||
return m_resultCache;
|
||||
}
|
||||
|
||||
ProximityInterestHandler::RuleSet& ProximityInterestHandler::GetAffectedRules()
|
||||
{
|
||||
/*
|
||||
* The expectation that lots of attributes will change frequently,
|
||||
* so there is no point in trying to optimize cases
|
||||
* where only a few attributes have changed.
|
||||
*/
|
||||
if (m_dirtyAttributes.empty() && !m_dirtyRules.empty())
|
||||
{
|
||||
return m_dirtyRules;
|
||||
}
|
||||
|
||||
/*
|
||||
* Assuming all rules might have been affected.
|
||||
*
|
||||
* There is an optimization chance here if the number of rules is large, as in 1,000+ rules.
|
||||
* To handle such scale we would need another spatial structure for rules.
|
||||
*/
|
||||
return m_localRules;
|
||||
}
|
||||
|
||||
void ProximityInterestHandler::GetAttributesWithinRule(ProximityInterestRule* rule, SpatialIndex::NodeCollector& nodes)
|
||||
{
|
||||
m_attributeWorld->Query(rule->Get(), nodes);
|
||||
}
|
||||
|
||||
void ProximityInterestHandler::ClearDirtyState()
|
||||
{
|
||||
m_dirtyAttributes.clear();
|
||||
m_dirtyRules.clear();
|
||||
}
|
||||
|
||||
void ProximityInterestHandler::CreateAndInsertIntoSpatialStructure(ProximityInterestAttribute* attribute)
|
||||
{
|
||||
m_attributes.insert(attribute);
|
||||
SpatialIndex::Node* node = m_attributeWorld->Insert(attribute->Get(), attribute);
|
||||
attribute->SetNode(node);
|
||||
}
|
||||
|
||||
void ProximityInterestHandler::CreateAndInsertIntoSpatialStructure(ProximityInterestRule* rule)
|
||||
{
|
||||
m_localRules.insert(rule);
|
||||
}
|
||||
|
||||
void ProximityInterestHandler::UpdateInternal(InterestMatchResult& result)
|
||||
{
|
||||
/*
|
||||
* The goal is to return all dirty attributes that were either dirty because:
|
||||
* 1) they changed which rules have apply to
|
||||
* 2) rules have changed and no longer apply to those attributes
|
||||
* and thus resulted in different peer(s) associated with a given replica.
|
||||
*/
|
||||
|
||||
const RuleSet& rules = GetAffectedRules();
|
||||
|
||||
for (auto& dirtyAttribute : m_dirtyAttributes)
|
||||
{
|
||||
result.insert(dirtyAttribute->GetReplicaId());
|
||||
}
|
||||
|
||||
/*
|
||||
* The exectation is to have a lot more attributes than rules.
|
||||
* The amount of rules should grow linear with amount of peers,
|
||||
* so it should be OK to iterate through all rules each update.
|
||||
*/
|
||||
for (auto& rule : rules)
|
||||
{
|
||||
CheckChangesForRule(rule, result);
|
||||
}
|
||||
|
||||
for (auto& removedRule : m_removedRules)
|
||||
{
|
||||
FreeRule(removedRule);
|
||||
}
|
||||
m_removedRules.clear();
|
||||
|
||||
// mark removed attribute as having no peers
|
||||
for (auto& removedAttribute : m_removedAttributes)
|
||||
{
|
||||
result.insert(removedAttribute->GetReplicaId());
|
||||
FreeAttribute(removedAttribute);
|
||||
}
|
||||
m_removedAttributes.clear();
|
||||
}
|
||||
|
||||
void ProximityInterestHandler::CheckChangesForRule(ProximityInterestRule* rule, InterestMatchResult& result)
|
||||
{
|
||||
SpatialIndex::NodeCollector collector;
|
||||
GetAttributesWithinRule(rule, collector);
|
||||
|
||||
auto peerId = rule->GetPeerId();
|
||||
for (ProximityInterestAttribute* attr : collector.GetNodes())
|
||||
{
|
||||
AZ_Assert(attr, "bad node?");
|
||||
|
||||
auto findIt = result.find(attr->GetReplicaId());
|
||||
if (findIt != result.end())
|
||||
{
|
||||
findIt->second.insert(peerId);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto resultIt = result.insert(attr->GetReplicaId());
|
||||
AZ_Assert(resultIt.second, "Successfully inserted");
|
||||
resultIt.first->second.insert(peerId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ProximityInterestHandler::MarkAttributesDirtyInRule(ProximityInterestRule* rule)
|
||||
{
|
||||
SpatialIndex::NodeCollector collector;
|
||||
GetAttributesWithinRule(rule, collector);
|
||||
|
||||
for (ProximityInterestAttribute* attr : collector.GetNodes())
|
||||
{
|
||||
AZ_Assert(attr, "bad node?");
|
||||
|
||||
UpdateAttribute(attr);
|
||||
}
|
||||
}
|
||||
|
||||
void ProximityInterestHandler::ProduceChanges(const InterestMatchResult& before, const InterestMatchResult& after)
|
||||
{
|
||||
m_resultCache.clear();
|
||||
|
||||
#if defined(INTERNAL_DEBUG_PROXIMITY)
|
||||
before.PrintMatchResult("before");
|
||||
after.PrintMatchResult("after");
|
||||
#endif
|
||||
|
||||
/*
|
||||
* 'after' contains only the stuff that might have changed
|
||||
*/
|
||||
for (auto& possiblyDirty : after)
|
||||
{
|
||||
ReplicaId repId = possiblyDirty.first;
|
||||
const InterestPeerSet& peerSet = possiblyDirty.second;
|
||||
|
||||
auto foundInBefore = before.find(repId);
|
||||
if (foundInBefore != before.end())
|
||||
{
|
||||
if (!HasSamePeers(foundInBefore->second, peerSet))
|
||||
{
|
||||
// was in the last calculation but has a different peer set now
|
||||
m_resultCache.insert(AZStd::make_pair(repId, peerSet));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// since it wasn't present during last calculation
|
||||
m_resultCache.insert(AZStd::make_pair(repId, peerSet));
|
||||
}
|
||||
}
|
||||
|
||||
// Mark attributes (replicas) for removal that have not moved but a rule (clients) no longer sees it
|
||||
for (auto& possiblyDirty : before)
|
||||
{
|
||||
ReplicaId repId = possiblyDirty.first;
|
||||
|
||||
const auto foundInAfter = after.find(repId);
|
||||
/*
|
||||
* If the prior state was a replica A present on peer X: "A{X}", and now A should no longer be present on any peer: "A{}"
|
||||
* then by the rules of InterestHandlers interacting with InterestManager, we should return in @m_resultCache the following:
|
||||
*
|
||||
* A{} - indicating that replica A must be removed all peers.
|
||||
*
|
||||
* On the next pass, the prior state would be: "A{}" and the current state would be "A{}" as well. At that point, we have
|
||||
* already sent the update to remove A from X, so @m_resultCache should no longer mention A at all.
|
||||
*/
|
||||
if (foundInAfter == after.end() && !possiblyDirty.second.empty() /* "not A{}" see the above comment */)
|
||||
{
|
||||
m_resultCache.insert(AZStd::make_pair(repId, InterestPeerSet()));
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(INTERNAL_DEBUG_PROXIMITY)
|
||||
m_resultCache.PrintMatchResult("changes");
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
bool ProximityInterestHandler::HasSamePeers(const InterestPeerSet& one, const InterestPeerSet& another)
|
||||
{
|
||||
if (one.size() != another.size())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto& peerFromOne : one)
|
||||
{
|
||||
if (another.find(peerFromOne) == another.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Safe to assume it's the same sets since all entries are unique in a peer sets
|
||||
return true;
|
||||
}
|
||||
|
||||
void ProximityInterestHandler::Update()
|
||||
{
|
||||
InterestMatchResult newResult;
|
||||
|
||||
UpdateInternal(newResult);
|
||||
ProduceChanges(m_lastResult, newResult);
|
||||
|
||||
m_lastResult = std::move(newResult);
|
||||
ClearDirtyState();
|
||||
}
|
||||
|
||||
void ProximityInterestHandler::OnRulesHandlerRegistered(InterestManager* manager)
|
||||
{
|
||||
AZ_Assert(m_im == nullptr, "Handler is already registered with manager %p (%p)\n", m_im, manager);
|
||||
AZ_Assert(m_rulesReplica == nullptr, "Rules replica is already created\n");
|
||||
AZ_TracePrintf("GridMate", "Proximity interest handler is registered\n");
|
||||
m_im = manager;
|
||||
m_rm = m_im->GetReplicaManager();
|
||||
m_rm->RegisterUserContext(AZ_CRC("ProximityInterestHandler", 0x3a90b3e4), this);
|
||||
|
||||
auto replica = Replica::CreateReplica("ProximityInterestHandlerRules");
|
||||
m_rulesReplica = CreateAndAttachReplicaChunk<ProximityInterestChunk>(replica);
|
||||
m_rm->AddPrimary(replica);
|
||||
}
|
||||
|
||||
void ProximityInterestHandler::OnRulesHandlerUnregistered(InterestManager* manager)
|
||||
{
|
||||
(void)manager;
|
||||
AZ_Assert(m_im == manager, "Handler was not registered with manager %p (%p)\n", manager, m_im);
|
||||
AZ_TracePrintf("GridMate", "Proximity interest handler is unregistered\n");
|
||||
m_rulesReplica = nullptr;
|
||||
m_im = nullptr;
|
||||
m_rm->UnregisterUserContext(AZ_CRC("ProximityInterestHandler", 0x3a90b3e4));
|
||||
m_rm = nullptr;
|
||||
|
||||
for (auto& chunk : m_peerChunks)
|
||||
{
|
||||
chunk.second->m_interestHandler = nullptr;
|
||||
}
|
||||
m_peerChunks.clear();
|
||||
|
||||
ClearDirtyState();
|
||||
DestroyAll();
|
||||
|
||||
m_resultCache.clear();
|
||||
}
|
||||
|
||||
void ProximityInterestHandler::DestroyAll()
|
||||
{
|
||||
for (ProximityInterestRule* rule : m_localRules)
|
||||
{
|
||||
FreeRule(rule);
|
||||
}
|
||||
m_localRules.clear();
|
||||
|
||||
for (ProximityInterestAttribute* attr : m_attributes)
|
||||
{
|
||||
FreeAttribute(attr);
|
||||
}
|
||||
m_attributes.clear();
|
||||
|
||||
for (auto& removedRule : m_removedRules)
|
||||
{
|
||||
FreeRule(removedRule);
|
||||
}
|
||||
m_removedRules.clear();
|
||||
|
||||
for (auto& removedAttribute : m_removedAttributes)
|
||||
{
|
||||
FreeAttribute(removedAttribute);
|
||||
}
|
||||
m_removedAttributes.clear();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
ProximityInterestHandler::~ProximityInterestHandler()
|
||||
{
|
||||
/*
|
||||
* If a handler was registered with a InterestManager, then InterestManager ought to have called OnRulesHandlerUnregistered
|
||||
* but this is a safety pre-caution.
|
||||
*/
|
||||
DestroyAll();
|
||||
}
|
||||
|
||||
SpatialIndex::SpatialIndex()
|
||||
{
|
||||
m_tree.reset(aznew GridMate::BvDynamicTree());
|
||||
}
|
||||
|
||||
void SpatialIndex::Remove(Node* node)
|
||||
{
|
||||
m_tree->Remove(node);
|
||||
}
|
||||
|
||||
void SpatialIndex::Update(Node* node)
|
||||
{
|
||||
m_tree->Update(node);
|
||||
}
|
||||
|
||||
SpatialIndex::Node* SpatialIndex::Insert(const AZ::Aabb& get, ProximityInterestAttribute* attribute)
|
||||
{
|
||||
return m_tree->Insert(get, attribute);
|
||||
}
|
||||
|
||||
void SpatialIndex::Query(const AZ::Aabb& shape, NodeCollector& nodes)
|
||||
{
|
||||
m_tree->collideTV(m_tree->GetRoot(), shape, nodes);
|
||||
}
|
||||
}
|
||||
@@ -1,314 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GM_REPLICA_PROXIMITYINTERESTHANDLER_H
|
||||
#define GM_REPLICA_PROXIMITYINTERESTHANDLER_H
|
||||
|
||||
#include <GridMate/Replica/RemoteProcedureCall.h>
|
||||
#include <GridMate/Replica/ReplicaChunk.h>
|
||||
#include <GridMate/Replica/Interest/RulesHandler.h>
|
||||
#include <GridMate/Replica/Interest/BvDynamicTree.h>
|
||||
#include <GridMate/Serialize/UtilityMarshal.h>
|
||||
|
||||
#include <AzCore/Math/Aabb.h>
|
||||
#include <AzCore/std/smart_ptr/unique_ptr.h>
|
||||
|
||||
namespace GridMate
|
||||
{
|
||||
class ProximityInterestHandler;
|
||||
class ProximityInterestAttribute;
|
||||
|
||||
/*
|
||||
* Base interest
|
||||
*/
|
||||
class ProximityInterest
|
||||
{
|
||||
friend class ProximityInterestHandler;
|
||||
|
||||
public:
|
||||
const AZ::Aabb& Get() const { return m_bbox; }
|
||||
|
||||
protected:
|
||||
explicit ProximityInterest(ProximityInterestHandler* handler);
|
||||
|
||||
ProximityInterestHandler* m_handler;
|
||||
AZ::Aabb m_bbox;
|
||||
};
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
/*
|
||||
* Proximity rule
|
||||
*/
|
||||
class ProximityInterestRule
|
||||
: public InterestRule
|
||||
, public ProximityInterest
|
||||
{
|
||||
friend class ProximityInterestHandler;
|
||||
|
||||
public:
|
||||
using Ptr = AZStd::intrusive_ptr<ProximityInterestRule>;
|
||||
|
||||
GM_CLASS_ALLOCATOR(ProximityInterestRule);
|
||||
|
||||
void Set(const AZ::Aabb& bbox);
|
||||
|
||||
private:
|
||||
|
||||
// Intrusive ptr
|
||||
template<class T>
|
||||
friend struct AZStd::IntrusivePtrCountPolicy;
|
||||
unsigned int m_refCount = 0;
|
||||
AZ_FORCE_INLINE void add_ref() { ++m_refCount; }
|
||||
AZ_FORCE_INLINE void release() { --m_refCount; if (!m_refCount) Destroy(); }
|
||||
AZ_FORCE_INLINE bool IsDeleted() const { return m_refCount == 0; }
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ProximityInterestRule(ProximityInterestHandler* handler, PeerId peerId, RuleNetworkId netId)
|
||||
: InterestRule(peerId, netId)
|
||||
, ProximityInterest(handler)
|
||||
{}
|
||||
|
||||
void Destroy();
|
||||
};
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class SpatialIndex
|
||||
{
|
||||
public:
|
||||
typedef Internal::DynamicTreeNode Node;
|
||||
|
||||
class NodeCollector
|
||||
{
|
||||
typedef AZStd::vector<ProximityInterestAttribute*> Type;
|
||||
|
||||
public:
|
||||
void Process(const Internal::DynamicTreeNode* node)
|
||||
{
|
||||
m_nodes.push_back(reinterpret_cast<ProximityInterestAttribute*>(node->m_data));
|
||||
}
|
||||
|
||||
const Type& GetNodes() const
|
||||
{
|
||||
return m_nodes;
|
||||
}
|
||||
|
||||
private:
|
||||
Type m_nodes;
|
||||
};
|
||||
|
||||
SpatialIndex();
|
||||
~SpatialIndex() = default;
|
||||
|
||||
AZ_FORCE_INLINE void Remove(Node* node);
|
||||
AZ_FORCE_INLINE void Update(Node* node);
|
||||
AZ_FORCE_INLINE Node* Insert(const AZ::Aabb& get, ProximityInterestAttribute* attribute);
|
||||
AZ_FORCE_INLINE void Query(const AZ::Aabb& get, NodeCollector& nodes);
|
||||
|
||||
private:
|
||||
AZStd::unique_ptr<BvDynamicTree> m_tree;
|
||||
};
|
||||
|
||||
/*
|
||||
* Proximity attribute
|
||||
*/
|
||||
class ProximityInterestAttribute
|
||||
: public InterestAttribute
|
||||
, public ProximityInterest
|
||||
{
|
||||
friend class ProximityInterestHandler;
|
||||
template<class T> friend class InterestPtr;
|
||||
|
||||
public:
|
||||
using Ptr = AZStd::intrusive_ptr<ProximityInterestAttribute>;
|
||||
|
||||
GM_CLASS_ALLOCATOR(ProximityInterestAttribute);
|
||||
|
||||
void Set(const AZ::Aabb& bbox);
|
||||
|
||||
private:
|
||||
|
||||
// Intrusive ptr
|
||||
template<class T>
|
||||
friend struct AZStd::IntrusivePtrCountPolicy;
|
||||
unsigned int m_refCount = 0;
|
||||
AZ_FORCE_INLINE void add_ref() { ++m_refCount; }
|
||||
AZ_FORCE_INLINE void release() { Destroy(); }
|
||||
AZ_FORCE_INLINE bool IsDeleted() const { return m_refCount == 0; }
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ProximityInterestAttribute(ProximityInterestHandler* handler, ReplicaId repId)
|
||||
: InterestAttribute(repId)
|
||||
, ProximityInterest(handler)
|
||||
, m_worldNode(nullptr)
|
||||
{}
|
||||
|
||||
void Destroy();
|
||||
|
||||
void SetNode(SpatialIndex::Node* node) { m_worldNode = node; }
|
||||
SpatialIndex::Node* GetNode() const { return m_worldNode; }
|
||||
SpatialIndex::Node* m_worldNode; ///< non-owning pointer
|
||||
};
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class ProximityInterestChunk
|
||||
: public ReplicaChunk
|
||||
{
|
||||
public:
|
||||
GM_CLASS_ALLOCATOR(ProximityInterestChunk);
|
||||
|
||||
// ReplicaChunk
|
||||
typedef AZStd::intrusive_ptr<ProximityInterestChunk> Ptr;
|
||||
bool IsReplicaMigratable() override { return false; }
|
||||
bool IsBroadcast() override { return true; }
|
||||
static const char* GetChunkName() { return "ProximityInterestChunk"; }
|
||||
|
||||
ProximityInterestChunk()
|
||||
: AddRuleRpc("AddRule")
|
||||
, RemoveRuleRpc("RemoveRule")
|
||||
, UpdateRuleRpc("UpdateRule")
|
||||
, AddRuleForPeerRpc("AddRuleForPeerRpc")
|
||||
, m_interestHandler(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
void OnReplicaActivate(const ReplicaContext& rc) override;
|
||||
void OnReplicaDeactivate(const ReplicaContext& rc) override;
|
||||
|
||||
bool AddRuleFn(RuleNetworkId netId, AZ::Aabb bbox, const RpcContext& ctx);
|
||||
bool RemoveRuleFn(RuleNetworkId netId, const RpcContext&);
|
||||
bool UpdateRuleFn(RuleNetworkId netId, AZ::Aabb bbox, const RpcContext&);
|
||||
bool AddRuleForPeerFn(RuleNetworkId netId, PeerId peerId, AZ::Aabb bbox, const RpcContext&);
|
||||
|
||||
Rpc<RpcArg<RuleNetworkId>, RpcArg<AZ::Aabb>>::BindInterface<ProximityInterestChunk, &ProximityInterestChunk::AddRuleFn> AddRuleRpc;
|
||||
Rpc<RpcArg<RuleNetworkId>>::BindInterface<ProximityInterestChunk, &ProximityInterestChunk::RemoveRuleFn> RemoveRuleRpc;
|
||||
Rpc<RpcArg<RuleNetworkId>, RpcArg<AZ::Aabb>>::BindInterface<ProximityInterestChunk, &ProximityInterestChunk::UpdateRuleFn> UpdateRuleRpc;
|
||||
|
||||
Rpc<RpcArg<RuleNetworkId>, RpcArg<PeerId>, RpcArg<AZ::Aabb>>::BindInterface<ProximityInterestChunk, &ProximityInterestChunk::AddRuleForPeerFn> AddRuleForPeerRpc;
|
||||
|
||||
unordered_map<RuleNetworkId, ProximityInterestRule::Ptr> m_rules;
|
||||
ProximityInterestHandler* m_interestHandler;
|
||||
};
|
||||
|
||||
/*
|
||||
* Rules handler
|
||||
*/
|
||||
class ProximityInterestHandler
|
||||
: public BaseRulesHandler
|
||||
{
|
||||
friend class ProximityInterestRule;
|
||||
friend class ProximityInterestAttribute;
|
||||
friend class ProximityInterestChunk;
|
||||
|
||||
public:
|
||||
|
||||
typedef unordered_set<ProximityInterestAttribute*> AttributeSet;
|
||||
typedef unordered_set<ProximityInterestRule*> RuleSet;
|
||||
|
||||
GM_CLASS_ALLOCATOR(ProximityInterestHandler);
|
||||
|
||||
ProximityInterestHandler();
|
||||
~ProximityInterestHandler();
|
||||
|
||||
/*
|
||||
* Creates new proximity rule and binds it to the peer.
|
||||
* Note: the lifetime of the created rule is tied to the lifetime of this handler.
|
||||
*/
|
||||
ProximityInterestRule::Ptr CreateRule(PeerId peerId);
|
||||
|
||||
/*
|
||||
* Creates new proximity attribute and binds it to the replica.
|
||||
* Note: the lifetime of the created attribute is tied to the lifetime of this handler.
|
||||
*/
|
||||
ProximityInterestAttribute::Ptr CreateAttribute(ReplicaId replicaId);
|
||||
|
||||
// Calculates rules and attributes matches
|
||||
void Update() override;
|
||||
|
||||
// Returns last recalculated results
|
||||
const InterestMatchResult& GetLastResult() override;
|
||||
|
||||
// Returns the manager it's bound to
|
||||
InterestManager* GetManager() override { return m_im; }
|
||||
|
||||
// Rules that this handler is aware of
|
||||
const RuleSet& GetLocalRules() const { return m_localRules; }
|
||||
|
||||
private:
|
||||
|
||||
// BaseRulesHandler
|
||||
void OnRulesHandlerRegistered(InterestManager* manager) override;
|
||||
void OnRulesHandlerUnregistered(InterestManager* manager) override;
|
||||
|
||||
void DestroyRule(ProximityInterestRule* rule);
|
||||
void FreeRule(ProximityInterestRule* rule);
|
||||
void UpdateRule(ProximityInterestRule* rule);
|
||||
|
||||
void DestroyAttribute(ProximityInterestAttribute* attrib);
|
||||
void FreeAttribute(ProximityInterestAttribute* attrib);
|
||||
void UpdateAttribute(ProximityInterestAttribute* attrib);
|
||||
|
||||
void OnNewRulesChunk(ProximityInterestChunk* chunk, ReplicaPeer* peer);
|
||||
void OnDeleteRulesChunk(ProximityInterestChunk* chunk, ReplicaPeer* peer);
|
||||
|
||||
RuleNetworkId GetNewRuleNetId();
|
||||
|
||||
ProximityInterestChunk* FindRulesChunkByPeerId(PeerId peerId);
|
||||
|
||||
void DestroyAll();
|
||||
|
||||
InterestManager* m_im;
|
||||
ReplicaManager* m_rm;
|
||||
|
||||
AZ::u32 m_lastRuleNetId;
|
||||
|
||||
unordered_map<PeerId, ProximityInterestChunk*> m_peerChunks;
|
||||
|
||||
RuleSet m_localRules;
|
||||
RuleSet m_removedRules;
|
||||
RuleSet m_dirtyRules;
|
||||
|
||||
AttributeSet m_attributes;
|
||||
AttributeSet m_removedAttributes;
|
||||
AttributeSet m_dirtyAttributes;
|
||||
|
||||
ProximityInterestChunk* m_rulesReplica;
|
||||
|
||||
// collection of all known attributes
|
||||
AZStd::unique_ptr<SpatialIndex> m_attributeWorld;
|
||||
|
||||
InterestMatchResult m_resultCache;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// internal processing helpers
|
||||
AZ_FORCE_INLINE RuleSet& GetAffectedRules();
|
||||
AZ_FORCE_INLINE void GetAttributesWithinRule(ProximityInterestRule* rule, SpatialIndex::NodeCollector& nodes);
|
||||
AZ_FORCE_INLINE void ClearDirtyState();
|
||||
|
||||
AZ_FORCE_INLINE void CreateAndInsertIntoSpatialStructure(ProximityInterestAttribute* attribute);
|
||||
AZ_FORCE_INLINE void RemoveFromSpatialStructure(ProximityInterestAttribute* attribute);
|
||||
AZ_FORCE_INLINE void CreateAndInsertIntoSpatialStructure(ProximityInterestRule* rule);
|
||||
|
||||
void UpdateInternal(InterestMatchResult& result);
|
||||
void CheckChangesForRule(ProximityInterestRule* rule, InterestMatchResult& result);
|
||||
void MarkAttributesDirtyInRule(ProximityInterestRule* rule);
|
||||
|
||||
static bool HasSamePeers(const InterestPeerSet& one, const InterestPeerSet& another);
|
||||
void ProduceChanges(const InterestMatchResult& before, const InterestMatchResult& after);
|
||||
|
||||
InterestMatchResult m_lastResult;
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
};
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
}
|
||||
|
||||
#endif // GM_REPLICA_PROXIMITYINTERESTHANDLER_H
|
||||
@@ -100,14 +100,10 @@ set(FILES
|
||||
Replica/Tasks/ReplicaPriorityPolicy.h
|
||||
Replica/Interest/BitmaskInterestHandler.cpp
|
||||
Replica/Interest/BitmaskInterestHandler.h
|
||||
Replica/Interest/ProximityInterestHandler.cpp
|
||||
Replica/Interest/ProximityInterestHandler.h
|
||||
Replica/Interest/InterestDefs.h
|
||||
Replica/Interest/InterestManager.cpp
|
||||
Replica/Interest/InterestManager.h
|
||||
Replica/Interest/InterestQueryResult.h
|
||||
Replica/Interest/BvDynamicTree.cpp
|
||||
Replica/Interest/BvDynamicTree.h
|
||||
Replica/Interest/RulesHandler.h
|
||||
Serialize/Buffer.cpp
|
||||
Serialize/Buffer.h
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -24,5 +24,4 @@ set(FILES
|
||||
StreamSocketDriverTests.cpp
|
||||
CarrierStreamSocketDriverTests.cpp
|
||||
Carrier.cpp
|
||||
Interest.cpp
|
||||
)
|
||||
|
||||
@@ -46,20 +46,25 @@ extern "C" void CreateStaticModules(AZStd::vector<AZ::Module*>& modulesOut);
|
||||
# define REMOTE_ASSET_PROCESSOR
|
||||
#endif
|
||||
|
||||
void CVar_OnViewportPosition(const AZ::Vector2& value);
|
||||
|
||||
namespace
|
||||
{
|
||||
void OnViewportResize(const AZ::Vector2& value);
|
||||
void CVar_OnViewportResize(const AZ::Vector2& value);
|
||||
|
||||
AZ_CVAR(AZ::Vector2, r_viewportSize, AZ::Vector2::CreateZero(), OnViewportResize, AZ::ConsoleFunctorFlags::DontReplicate,
|
||||
AZ_CVAR(AZ::Vector2, r_viewportSize, AZ::Vector2::CreateZero(), CVar_OnViewportResize, AZ::ConsoleFunctorFlags::DontReplicate,
|
||||
"The default size for the launcher viewport, 0 0 means full screen");
|
||||
|
||||
void OnViewportResize(const AZ::Vector2& value)
|
||||
void CVar_OnViewportResize(const AZ::Vector2& value)
|
||||
{
|
||||
AzFramework::NativeWindowHandle windowHandle = nullptr;
|
||||
AzFramework::WindowSystemRequestBus::BroadcastResult(windowHandle, &AzFramework::WindowSystemRequestBus::Events::GetDefaultWindowHandle);
|
||||
AzFramework::WindowSize newSize = AzFramework::WindowSize(aznumeric_cast<int32_t>(value.GetX()), aznumeric_cast<int32_t>(value.GetY()));
|
||||
AzFramework::WindowRequestBus::Broadcast(&AzFramework::WindowRequestBus::Events::ResizeClientArea, newSize);
|
||||
}
|
||||
|
||||
AZ_CVAR(AZ::Vector2, r_viewportPos, AZ::Vector2::CreateZero(), CVar_OnViewportPosition, AZ::ConsoleFunctorFlags::DontReplicate,
|
||||
"The default position for the launcher viewport, 0 0 means top left corner of your main desktop");
|
||||
|
||||
void ExecuteConsoleCommandFile(AzFramework::Application& application)
|
||||
{
|
||||
|
||||
@@ -433,3 +433,5 @@ void android_main(android_app* appState)
|
||||
MAIN_EXIT_FAILURE(appState, GetReturnCodeString(status));
|
||||
}
|
||||
}
|
||||
|
||||
void CVar_OnViewportPosition([[maybe_unused]] const AZ::Vector2& value) {}
|
||||
|
||||
@@ -113,3 +113,5 @@ int main(int argc, char** argv)
|
||||
|
||||
return static_cast<int>(status);
|
||||
}
|
||||
|
||||
void CVar_OnViewportPosition([[maybe_unused]] const AZ::Vector2& value) {}
|
||||
|
||||
@@ -63,3 +63,5 @@ int main(int argc, char* argv[])
|
||||
}
|
||||
|
||||
#endif // AZ_TESTS_ENABLED
|
||||
|
||||
void CVar_OnViewportPosition([[maybe_unused]] const AZ::Vector2& value) {}
|
||||
|
||||
@@ -69,3 +69,14 @@ int APIENTRY WinMain([[maybe_unused]] HINSTANCE hInstance, [[maybe_unused]] HINS
|
||||
|
||||
return static_cast<int>(status);
|
||||
}
|
||||
|
||||
void CVar_OnViewportPosition(const AZ::Vector2& value)
|
||||
{
|
||||
if (HWND windowHandle = GetActiveWindow())
|
||||
{
|
||||
SetWindowPos(windowHandle, nullptr,
|
||||
value.GetX(),
|
||||
value.GetY(),
|
||||
0, 0, SWP_NOOWNERZORDER | SWP_NOSIZE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,3 +23,5 @@ int main(int argc, char* argv[])
|
||||
[pool release];
|
||||
return 0;
|
||||
}
|
||||
|
||||
void CVar_OnViewportPosition([[maybe_unused]] const AZ::Vector2& value) {}
|
||||
|
||||
@@ -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})
|
||||
|
||||
|
||||
@@ -1,24 +1,26 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "EditorDefs.h"
|
||||
|
||||
#include "AzAssetBrowserWindow.h"
|
||||
|
||||
// AzToolsFramework
|
||||
#include <AzCore/Console/IConsole.h>
|
||||
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
|
||||
#include <AzToolsFramework/API/ViewPaneOptions.h>
|
||||
#include <AzToolsFramework/AssetBrowser/AssetBrowserModel.h>
|
||||
#include <AzToolsFramework/AssetBrowser/AssetBrowserEntry.h>
|
||||
#include <AzToolsFramework/AssetBrowser/AssetBrowserModel.h>
|
||||
#include <AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h>
|
||||
|
||||
// AzQtComponents
|
||||
#include <AzQtComponents/Utilities/QtWindowUtilities.h>
|
||||
@@ -31,6 +33,7 @@ AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
|
||||
#include <AzAssetBrowser/ui_AzAssetBrowserWindow.h>
|
||||
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
|
||||
|
||||
AZ_CVAR_EXTERNED(bool, ed_useNewAssetBrowserTableView);
|
||||
|
||||
class ListenerForShowAssetEditorEvent
|
||||
: public QObject
|
||||
@@ -66,32 +69,75 @@ AzAssetBrowserWindow::AzAssetBrowserWindow(QWidget* parent)
|
||||
: QWidget(parent)
|
||||
, m_ui(new Ui::AzAssetBrowserWindowClass())
|
||||
, m_filterModel(new AzToolsFramework::AssetBrowser::AssetBrowserFilterModel(parent))
|
||||
, m_tableModel(new AzToolsFramework::AssetBrowser::AssetBrowserTableModel(parent))
|
||||
{
|
||||
m_ui->setupUi(this);
|
||||
m_ui->m_searchWidget->Setup(true, true);
|
||||
|
||||
using namespace AzToolsFramework::AssetBrowser;
|
||||
AssetBrowserComponentRequestBus::BroadcastResult(m_assetBrowserModel, &AssetBrowserComponentRequests::GetAssetBrowserModel);
|
||||
namespace AzAssetBrowser = AzToolsFramework::AssetBrowser;
|
||||
|
||||
AzAssetBrowser::AssetBrowserComponentRequestBus::BroadcastResult(m_assetBrowserModel, &AzAssetBrowser::AssetBrowserComponentRequests::GetAssetBrowserModel);
|
||||
AZ_Assert(m_assetBrowserModel, "Failed to get filebrowser model");
|
||||
m_filterModel->setSourceModel(m_assetBrowserModel);
|
||||
m_filterModel->SetFilter(m_ui->m_searchWidget->GetFilter());
|
||||
|
||||
m_ui->m_viewSwitcherCheckBox->setVisible(false);
|
||||
m_ui->m_assetBrowserTableViewWidget->setVisible(false);
|
||||
if (ed_useNewAssetBrowserTableView)
|
||||
{
|
||||
m_ui->m_viewSwitcherCheckBox->setVisible(true);
|
||||
m_tableModel->setFilterRole(Qt::DisplayRole);
|
||||
m_tableModel->setSourceModel(m_filterModel.data());
|
||||
m_ui->m_assetBrowserTableViewWidget->setModel(m_tableModel.data());
|
||||
connect(
|
||||
m_filterModel.data(), &AzAssetBrowser::AssetBrowserFilterModel::filterChanged, m_tableModel.data(),
|
||||
&AzAssetBrowser::AssetBrowserTableModel::UpdateTableModelMaps);
|
||||
connect(
|
||||
m_ui->m_assetBrowserTableViewWidget, &AzAssetBrowser::AssetBrowserTableView::selectionChangedSignal, this,
|
||||
&AzAssetBrowserWindow::SelectionChangedSlot);
|
||||
connect(
|
||||
m_ui->m_assetBrowserTableViewWidget, &QAbstractItemView::doubleClicked, this,
|
||||
&AzAssetBrowserWindow::DoubleClickedItem);
|
||||
connect(
|
||||
m_ui->m_assetBrowserTableViewWidget, &AzAssetBrowser::AssetBrowserTableView::ClearStringFilter, m_ui->m_searchWidget,
|
||||
&AzAssetBrowser::SearchWidget::ClearStringFilter);
|
||||
connect(
|
||||
m_ui->m_assetBrowserTableViewWidget, &AzAssetBrowser::AssetBrowserTableView::ClearTypeFilter, m_ui->m_searchWidget,
|
||||
&AzAssetBrowser::SearchWidget::ClearTypeFilter);
|
||||
|
||||
m_ui->m_assetBrowserTableViewWidget->SetName("AssetBrowserTableView_main");
|
||||
|
||||
connect(m_filterModel.data(), &AzAssetBrowser::AssetBrowserFilterModel::stringFilterPopulated, this, &AzAssetBrowserWindow::SwitchDisplayView);
|
||||
connect(m_ui->m_viewSwitcherCheckBox, &QCheckBox::stateChanged, this, &AzAssetBrowserWindow::LockToDefaultView);
|
||||
}
|
||||
|
||||
m_ui->m_assetBrowserTreeViewWidget->setModel(m_filterModel.data());
|
||||
|
||||
connect(m_ui->m_searchWidget->GetFilter().data(), &AssetBrowserEntryFilter::updatedSignal,
|
||||
m_filterModel.data(), &AssetBrowserFilterModel::filterUpdatedSlot);
|
||||
connect(m_filterModel.data(), &AssetBrowserFilterModel::filterChanged, this, [this]()
|
||||
{
|
||||
const bool hasFilter = !m_ui->m_searchWidget->GetFilterString().isEmpty();
|
||||
const bool selectFirstFilteredIndex = false;
|
||||
m_ui->m_assetBrowserTreeViewWidget->UpdateAfterFilter(hasFilter, selectFirstFilteredIndex);
|
||||
});
|
||||
connect(m_ui->m_assetBrowserTreeViewWidget, &AssetBrowserTreeView::selectionChangedSignal,
|
||||
this, &AzAssetBrowserWindow::SelectionChangedSlot);
|
||||
connect(
|
||||
m_ui->m_searchWidget->GetFilter().data(), &AzAssetBrowser::AssetBrowserEntryFilter::updatedSignal, m_filterModel.data(),
|
||||
&AzAssetBrowser::AssetBrowserFilterModel::filterUpdatedSlot);
|
||||
connect(
|
||||
m_filterModel.data(), &AzAssetBrowser::AssetBrowserFilterModel::filterChanged, this,
|
||||
[this]()
|
||||
{
|
||||
const bool hasFilter = !m_ui->m_searchWidget->GetFilterString().isEmpty();
|
||||
const bool selectFirstFilteredIndex = false;
|
||||
m_ui->m_assetBrowserTreeViewWidget->UpdateAfterFilter(hasFilter, selectFirstFilteredIndex);
|
||||
});
|
||||
|
||||
connect(
|
||||
m_ui->m_assetBrowserTreeViewWidget, &AzAssetBrowser::AssetBrowserTreeView::selectionChangedSignal, this,
|
||||
&AzAssetBrowserWindow::SelectionChangedSlot);
|
||||
|
||||
connect(m_ui->m_assetBrowserTreeViewWidget, &QAbstractItemView::doubleClicked, this, &AzAssetBrowserWindow::DoubleClickedItem);
|
||||
|
||||
connect(m_ui->m_assetBrowserTreeViewWidget, &AssetBrowserTreeView::ClearStringFilter, m_ui->m_searchWidget, &SearchWidget::ClearStringFilter);
|
||||
connect(m_ui->m_assetBrowserTreeViewWidget, &AssetBrowserTreeView::ClearTypeFilter, m_ui->m_searchWidget, &SearchWidget::ClearTypeFilter);
|
||||
connect(
|
||||
m_ui->m_assetBrowserTreeViewWidget, &AzAssetBrowser::AssetBrowserTreeView::ClearStringFilter, m_ui->m_searchWidget,
|
||||
&AzAssetBrowser::SearchWidget::ClearStringFilter);
|
||||
connect(
|
||||
m_ui->m_assetBrowserTreeViewWidget, &AzAssetBrowser::AssetBrowserTreeView::ClearTypeFilter, m_ui->m_searchWidget,
|
||||
&AzAssetBrowser::SearchWidget::ClearTypeFilter);
|
||||
|
||||
m_ui->m_assetBrowserTreeViewWidget->SetName("AssetBrowserTreeView_main");
|
||||
}
|
||||
|
||||
@@ -117,7 +163,10 @@ QObject* AzAssetBrowserWindow::createListenerForShowAssetEditorEvent(QObject* pa
|
||||
|
||||
void AzAssetBrowserWindow::UpdatePreview() const
|
||||
{
|
||||
auto selectedAssets = m_ui->m_assetBrowserTreeViewWidget->GetSelectedAssets();
|
||||
const auto& selectedAssets = m_ui->m_assetBrowserTreeViewWidget->isVisible()
|
||||
? m_ui->m_assetBrowserTreeViewWidget->GetSelectedAssets()
|
||||
: m_ui->m_assetBrowserTableViewWidget->GetSelectedAssets();
|
||||
|
||||
if (selectedAssets.size() != 1)
|
||||
{
|
||||
m_ui->m_previewerFrame->Clear();
|
||||
@@ -148,8 +197,6 @@ static void ExpandTreeToIndex(QTreeView* treeView, const QModelIndex& index)
|
||||
|
||||
void AzAssetBrowserWindow::SelectAsset(const QString& assetPath)
|
||||
{
|
||||
using namespace AzToolsFramework::AssetBrowser;
|
||||
|
||||
QModelIndex index = m_assetBrowserModel->findIndex(assetPath);
|
||||
if (index.isValid())
|
||||
{
|
||||
@@ -161,18 +208,21 @@ void AzAssetBrowserWindow::SelectAsset(const QString& assetPath)
|
||||
// interferes with the update from the select and expand, and if you don't
|
||||
// queue it, the tree doesn't expand reliably.
|
||||
|
||||
QTimer::singleShot(0, this, [this, filteredIndex = index] {
|
||||
// the treeview has a filter model so we have to backwards go from that
|
||||
QModelIndex index = m_filterModel->mapFromSource(filteredIndex);
|
||||
QTimer::singleShot(
|
||||
0, this,
|
||||
[this, filteredIndex = index]
|
||||
{
|
||||
// the treeview has a filter model so we have to backwards go from that
|
||||
QModelIndex index = m_filterModel->mapFromSource(filteredIndex);
|
||||
|
||||
QTreeView* treeView = m_ui->m_assetBrowserTreeViewWidget;
|
||||
ExpandTreeToIndex(treeView, index);
|
||||
QTreeView* treeView = m_ui->m_assetBrowserTreeViewWidget;
|
||||
ExpandTreeToIndex(treeView, index);
|
||||
|
||||
treeView->scrollTo(index);
|
||||
treeView->setCurrentIndex(index);
|
||||
treeView->scrollTo(index);
|
||||
treeView->setCurrentIndex(index);
|
||||
|
||||
treeView->selectionModel()->select(index, QItemSelectionModel::ClearAndSelect);
|
||||
});
|
||||
treeView->selectionModel()->select(index, QItemSelectionModel::ClearAndSelect);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,31 +235,34 @@ void AzAssetBrowserWindow::SelectionChangedSlot(const QItemSelection& /*selected
|
||||
// just becuase on some OS clicking once is activation.
|
||||
void AzAssetBrowserWindow::DoubleClickedItem([[maybe_unused]] const QModelIndex& element)
|
||||
{
|
||||
using namespace AzToolsFramework;
|
||||
using namespace AzToolsFramework::AssetBrowser;
|
||||
// assumption: Double clicking an item selects it before telling us we double clicked it.
|
||||
auto selectedAssets = m_ui->m_assetBrowserTreeViewWidget->GetSelectedAssets();
|
||||
for (const AssetBrowserEntry* entry : selectedAssets)
|
||||
namespace AzAssetBrowser = AzToolsFramework::AssetBrowser;
|
||||
|
||||
const auto& selectedAssets = m_ui->m_assetBrowserTreeViewWidget->isVisible()
|
||||
? m_ui->m_assetBrowserTreeViewWidget->GetSelectedAssets()
|
||||
: m_ui->m_assetBrowserTableViewWidget->GetSelectedAssets();
|
||||
|
||||
for (const AzAssetBrowser::AssetBrowserEntry* entry : selectedAssets)
|
||||
{
|
||||
AZ::Data::AssetId assetIdToOpen;
|
||||
AZStd::string fullFilePath;
|
||||
|
||||
if (const ProductAssetBrowserEntry* productEntry = azrtti_cast<const ProductAssetBrowserEntry*>(entry))
|
||||
if (const AzAssetBrowser::ProductAssetBrowserEntry* productEntry = azrtti_cast<const AzAssetBrowser::ProductAssetBrowserEntry*>(entry))
|
||||
{
|
||||
assetIdToOpen = productEntry->GetAssetId();
|
||||
fullFilePath = entry->GetFullPath();
|
||||
}
|
||||
else if (const SourceAssetBrowserEntry* sourceEntry = azrtti_cast<const SourceAssetBrowserEntry*>(entry))
|
||||
else if (const AzAssetBrowser::SourceAssetBrowserEntry* sourceEntry = azrtti_cast<const AzAssetBrowser::SourceAssetBrowserEntry*>(entry))
|
||||
{
|
||||
// manufacture an empty AssetID with the source's UUID
|
||||
assetIdToOpen = AZ::Data::AssetId(sourceEntry->GetSourceUuid(), 0);
|
||||
fullFilePath = entry->GetFullPath();
|
||||
}
|
||||
|
||||
|
||||
bool handledBySomeone = false;
|
||||
if (assetIdToOpen.IsValid())
|
||||
{
|
||||
AssetBrowserInteractionNotificationBus::Broadcast(&AssetBrowserInteractionNotifications::OpenAssetInAssociatedEditor, assetIdToOpen, handledBySomeone);
|
||||
AzAssetBrowser::AssetBrowserInteractionNotificationBus::Broadcast(
|
||||
&AzAssetBrowser::AssetBrowserInteractionNotifications::OpenAssetInAssociatedEditor, assetIdToOpen, handledBySomeone);
|
||||
}
|
||||
|
||||
if (!handledBySomeone && !fullFilePath.empty())
|
||||
@@ -217,7 +270,27 @@ void AzAssetBrowserWindow::DoubleClickedItem([[maybe_unused]] const QModelIndex&
|
||||
AzAssetBrowserRequestHandler::OpenWithOS(fullFilePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AzAssetBrowserWindow::SwitchDisplayView(bool state)
|
||||
{
|
||||
m_ui->m_assetBrowserTableViewWidget->setVisible(state);
|
||||
m_ui->m_assetBrowserTreeViewWidget->setVisible(!state);
|
||||
}
|
||||
|
||||
void AzAssetBrowserWindow::LockToDefaultView(bool state)
|
||||
{
|
||||
using AzToolsFramework::AssetBrowser::AssetBrowserFilterModel;
|
||||
SwitchDisplayView(!state);
|
||||
if (state == true)
|
||||
{
|
||||
disconnect(
|
||||
m_filterModel.data(), &AssetBrowserFilterModel::stringFilterPopulated, this, &AzAssetBrowserWindow::SwitchDisplayView);
|
||||
}
|
||||
else
|
||||
{
|
||||
connect(m_filterModel.data(), &AssetBrowserFilterModel::stringFilterPopulated, this, &AzAssetBrowserWindow::SwitchDisplayView);
|
||||
}
|
||||
}
|
||||
|
||||
#include <AzAssetBrowser/moc_AzAssetBrowserWindow.cpp>
|
||||
|
||||
@@ -29,7 +29,9 @@ namespace AzToolsFramework
|
||||
namespace AssetBrowser
|
||||
{
|
||||
class AssetBrowserFilterModel;
|
||||
class AssetBrowserTableModel;
|
||||
class AssetBrowserModel;
|
||||
class AssetBrowserTableFilterModel;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,6 +55,7 @@ private:
|
||||
|
||||
QScopedPointer<Ui::AzAssetBrowserWindowClass> m_ui;
|
||||
QScopedPointer<AzToolsFramework::AssetBrowser::AssetBrowserFilterModel> m_filterModel;
|
||||
QScopedPointer<AzToolsFramework::AssetBrowser::AssetBrowserTableModel> m_tableModel;
|
||||
AzToolsFramework::AssetBrowser::AssetBrowserModel* m_assetBrowserModel;
|
||||
|
||||
void UpdatePreview() const;
|
||||
@@ -60,6 +63,8 @@ private:
|
||||
private Q_SLOTS:
|
||||
void SelectionChangedSlot(const QItemSelection& selected, const QItemSelection& deselected) const;
|
||||
void DoubleClickedItem(const QModelIndex& element);
|
||||
void SwitchDisplayView(bool state);
|
||||
void LockToDefaultView(bool state);
|
||||
};
|
||||
|
||||
extern const char* AZ_ASSET_BROWSER_PREVIEW_NAME;
|
||||
|
||||
@@ -65,6 +65,13 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="m_viewSwitcherCheckBox">
|
||||
<property name="text">
|
||||
<string>Switch View</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
@@ -107,6 +114,49 @@
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="AzToolsFramework::AssetBrowser::AssetBrowserTableView" name="m_assetBrowserTableViewWidget">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::DoubleClicked|QAbstractItemView::EditKeyPressed</set>
|
||||
</property>
|
||||
<property name="dragDropOverwriteMode">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="dragDropMode">
|
||||
<enum>QAbstractItemView::DragOnly</enum>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::SingleSelection</enum>
|
||||
</property>
|
||||
<property name="selectionBehavior">
|
||||
<enum>QAbstractItemView::SelectRows</enum>
|
||||
</property>
|
||||
<property name="horizontalScrollMode">
|
||||
<enum>QAbstractItemView::ScrollPerPixel</enum>
|
||||
</property>
|
||||
<property name="showGrid">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="sortingEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<attribute name="horizontalHeaderShowSortIndicator" stdset="0">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="horizontalHeaderStretchLastSection">
|
||||
<bool>true</bool>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="AzToolsFramework::AssetBrowser::AssetBrowserTreeView" name="m_assetBrowserTreeViewWidget">
|
||||
<property name="sizePolicy">
|
||||
@@ -162,6 +212,11 @@
|
||||
<header>AzToolsFramework/AssetBrowser/Previewer/PreviewerFrame.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>AzToolsFramework::AssetBrowser::AssetBrowserTableView</class>
|
||||
<extends>QTableView</extends>
|
||||
<header>AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
|
||||
@@ -715,10 +715,11 @@ QMenu* LevelEditorMenuHandler::CreateViewMenu()
|
||||
{
|
||||
return view.IsViewportPane();
|
||||
});
|
||||
#endif
|
||||
|
||||
viewportViewsMenuWrapper.AddSeparator();
|
||||
|
||||
#endif
|
||||
|
||||
if (CViewManager::IsMultiViewportEnabled())
|
||||
{
|
||||
viewportViewsMenuWrapper.AddAction(ID_VIEW_CONFIGURELAYOUT);
|
||||
|
||||
@@ -53,6 +53,7 @@ AZ_POP_DISABLE_WARNING
|
||||
#include <AzCore/std/smart_ptr/make_shared.h>
|
||||
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
#include <AzCore/Console/IConsole.h>
|
||||
|
||||
// AzFramework
|
||||
#include <AzFramework/Components/CameraBus.h>
|
||||
@@ -356,6 +357,8 @@ CCryEditDoc* CCryDocManager::OpenDocumentFile(LPCTSTR lpszFileName, BOOL bAddToM
|
||||
for (int i = idStart; i <= idEnd; ++i) \
|
||||
ON_COMMAND(i, method);
|
||||
|
||||
AZ_CVAR_EXTERNED(bool, ed_previewGameInFullscreen_once);
|
||||
|
||||
void CCryEditApp::RegisterActionHandlers()
|
||||
{
|
||||
ON_COMMAND(ID_APP_ABOUT, OnAppAbout)
|
||||
@@ -376,6 +379,10 @@ void CCryEditApp::RegisterActionHandlers()
|
||||
ON_COMMAND(ID_EDIT_FETCH, OnEditFetch)
|
||||
ON_COMMAND(ID_FILE_EXPORTTOGAMENOSURFACETEXTURE, OnFileExportToGameNoSurfaceTexture)
|
||||
ON_COMMAND(ID_VIEW_SWITCHTOGAME, OnViewSwitchToGame)
|
||||
MainWindow::instance()->GetActionManager()->RegisterActionHandler(ID_VIEW_SWITCHTOGAME_FULLSCREEN, [this]() {
|
||||
ed_previewGameInFullscreen_once = true;
|
||||
OnViewSwitchToGame();
|
||||
});
|
||||
ON_COMMAND(ID_MOVE_OBJECT, OnMoveObject)
|
||||
ON_COMMAND(ID_RENAME_OBJ, OnRenameObj)
|
||||
ON_COMMAND(ID_EDITMODE_MOVE, OnEditmodeMove)
|
||||
@@ -2886,7 +2893,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)
|
||||
{
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include <AzCore/Component/EntityId.h>
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
#include <AzCore/Math/VectorConversions.h>
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
#include <AzCore/Console/IConsole.h>
|
||||
|
||||
// AzFramework
|
||||
@@ -82,6 +83,8 @@
|
||||
#include "AnimationContext.h"
|
||||
#include "Objects/SelectionGroup.h"
|
||||
#include "Core/QtEditorApplication.h"
|
||||
#include "MainWindow.h"
|
||||
#include "LayoutWnd.h"
|
||||
|
||||
// ComponentEntityEditorPlugin
|
||||
#include <Plugins/ComponentEntityEditorPlugin/Objects/ComponentEntityObject.h>
|
||||
@@ -513,18 +516,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();
|
||||
@@ -694,6 +707,11 @@ void EditorViewportWidget::OnEditorNotifyEvent(EEditorNotifyEvent event)
|
||||
}
|
||||
}
|
||||
SetCurrentCursor(STD_CURSOR_GAME);
|
||||
|
||||
if (ShouldPreviewFullscreen())
|
||||
{
|
||||
StartFullscreenPreview();
|
||||
}
|
||||
}
|
||||
|
||||
if (m_renderViewport)
|
||||
@@ -712,6 +730,11 @@ void EditorViewportWidget::OnEditorNotifyEvent(EEditorNotifyEvent event)
|
||||
m_bInOrbitMode = false;
|
||||
m_bInZoomMode = false;
|
||||
|
||||
if (m_inFullscreenPreview)
|
||||
{
|
||||
StopFullscreenPreview();
|
||||
}
|
||||
|
||||
RestoreViewportAfterGameMode();
|
||||
}
|
||||
|
||||
@@ -1368,11 +1391,35 @@ void EditorViewportWidget::SetViewportId(int id)
|
||||
{
|
||||
CViewport::SetViewportId(id);
|
||||
|
||||
// Clear the cached debugdisplay pointer. we're about to delete that render viewport, and deleting the render
|
||||
// viewport invalidates the debugdisplay.
|
||||
m_debugDisplay = nullptr;
|
||||
|
||||
// First delete any existing layout
|
||||
// This also deletes any existing render viewport widget (since it will be added to the layout)
|
||||
// Below is the typical method of clearing a QLayout, see e.g. https://doc.qt.io/qt-5/qlayout.html#takeAt
|
||||
if (QLayout* thisLayout = layout())
|
||||
{
|
||||
QLayoutItem* item;
|
||||
while ((item = thisLayout->takeAt(0)) != nullptr)
|
||||
{
|
||||
if (QWidget* widget = item->widget())
|
||||
{
|
||||
delete widget;
|
||||
}
|
||||
thisLayout->removeItem(item);
|
||||
delete item;
|
||||
}
|
||||
delete thisLayout;
|
||||
}
|
||||
|
||||
// Now that we have an ID, we can initialize our viewport.
|
||||
m_renderViewport = new AtomToolsFramework::RenderViewportWidget(this, false);
|
||||
if (!m_renderViewport->InitializeViewportContext(id))
|
||||
{
|
||||
AZ_Warning("EditorViewportWidget", false, "Failed to initialize RenderViewportWidget's ViewportContext");
|
||||
delete m_renderViewport;
|
||||
m_renderViewport = nullptr;
|
||||
return;
|
||||
}
|
||||
auto viewportContext = m_renderViewport->GetViewportContext();
|
||||
@@ -3027,4 +3074,116 @@ float EditorViewportSettings::AngleStep() const
|
||||
return SandboxEditor::AngleSnappingSize();
|
||||
}
|
||||
|
||||
AZ_CVAR_EXTERNED(bool, ed_previewGameInFullscreen_once);
|
||||
|
||||
bool EditorViewportWidget::ShouldPreviewFullscreen() const
|
||||
{
|
||||
CLayoutWnd* layout = GetIEditor()->GetViewManager()->GetLayout();
|
||||
if (!layout)
|
||||
{
|
||||
AZ_Assert(false, "CRenderViewport: No View Manager layout");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Doesn't work with split layout
|
||||
if (layout->GetLayout() != EViewLayout::ET_Layout0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Not supported in VR
|
||||
if (gSettings.bEnableGameModeVR)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// If level not loaded, don't preview in fullscreen (preview shouldn't work at all without a level, but it does)
|
||||
if (auto ge = GetIEditor()->GetGameEngine())
|
||||
{
|
||||
if (!ge->IsLevelLoaded())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Check 'ed_previewGameInFullscreen_once'
|
||||
if (ed_previewGameInFullscreen_once)
|
||||
{
|
||||
ed_previewGameInFullscreen_once = true;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void EditorViewportWidget::StartFullscreenPreview()
|
||||
{
|
||||
AZ_Assert(!m_inFullscreenPreview, "EditorViewportWidget::StartFullscreenPreview called when already in full screen preview");
|
||||
m_inFullscreenPreview = true;
|
||||
|
||||
// Pick the screen on which the main window lies to use as the screen for the full screen preview
|
||||
const QScreen* screen = MainWindow::instance()->screen();
|
||||
const QRect screenGeometry = screen->geometry();
|
||||
|
||||
// Unparent this and show it, which turns it into a free floating window
|
||||
// Also set style to frameless and disable resizing by user
|
||||
setParent(nullptr);
|
||||
setWindowFlag(Qt::FramelessWindowHint, true);
|
||||
setWindowFlag(Qt::MSWindowsFixedSizeDialogHint, true);
|
||||
setFixedSize(screenGeometry.size());
|
||||
move(QPoint(screenGeometry.x(), screenGeometry.y()));
|
||||
showMaximized();
|
||||
|
||||
// This must be done after unparenting this widget above
|
||||
MainWindow::instance()->hide();
|
||||
}
|
||||
|
||||
void EditorViewportWidget::StopFullscreenPreview()
|
||||
{
|
||||
AZ_Assert(m_inFullscreenPreview, "EditorViewportWidget::StartFullscreenPreview called when not in full screen preview");
|
||||
m_inFullscreenPreview = false;
|
||||
|
||||
// Unset frameless window flags
|
||||
setWindowFlag(Qt::FramelessWindowHint, false);
|
||||
setWindowFlag(Qt::MSWindowsFixedSizeDialogHint, false);
|
||||
|
||||
// Unset fixed size (note that 50x50 is the minimum set in the constructor)
|
||||
setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
|
||||
setMinimumSize(50, 50);
|
||||
|
||||
// Attach this viewport to the primary view pane (whose index is 0).
|
||||
if (CLayoutWnd* layout = GetIEditor()->GetViewManager()->GetLayout())
|
||||
{
|
||||
if (CLayoutViewPane* viewPane = layout->GetViewPaneByIndex(0))
|
||||
{
|
||||
// Force-reattach this viewport to its view pane by first detaching
|
||||
viewPane->DetachViewport();
|
||||
viewPane->AttachViewport(this);
|
||||
|
||||
// Set the main widget of the layout, which causes this widgets size to be bound to the layout
|
||||
// and the viewport title bar to be displayed
|
||||
layout->SetMainWidget(viewPane);
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Assert(false, "CRenderViewport: No view pane with ID 0 (primary view pane)");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Assert(false, "CRenderViewport: No View Manager layout");
|
||||
}
|
||||
|
||||
// Set this as the selected viewport
|
||||
GetIEditor()->GetViewManager()->SelectViewport(this);
|
||||
|
||||
// Show this widget (setting flags may hide it)
|
||||
showNormal();
|
||||
|
||||
// Show the main window
|
||||
MainWindow::instance()->show();
|
||||
}
|
||||
|
||||
#include <moc_EditorViewportWidget.cpp>
|
||||
|
||||
@@ -385,6 +385,11 @@ protected:
|
||||
};
|
||||
void ResetToViewSourceType(const ViewSourceType& viewSourType);
|
||||
|
||||
bool ShouldPreviewFullscreen() const;
|
||||
void StartFullscreenPreview();
|
||||
void StopFullscreenPreview();
|
||||
|
||||
bool m_inFullscreenPreview = false;
|
||||
bool m_bRenderContextCreated = false;
|
||||
bool m_bInRotateMode = false;
|
||||
bool m_bInMoveMode = false;
|
||||
|
||||
@@ -113,6 +113,8 @@ public:
|
||||
//! Switch 2D viewports.
|
||||
void Cycle2DViewport();
|
||||
|
||||
using AzQtComponents::ToolBarArea::SetMainWidget;
|
||||
|
||||
public slots:
|
||||
void ResetLayout();
|
||||
|
||||
|
||||
@@ -949,6 +949,12 @@ void MainWindow::InitActions()
|
||||
.SetApplyHoverEffect()
|
||||
.SetCheckable(true)
|
||||
.RegisterUpdateCallback(cryEdit, &CCryEditApp::OnUpdatePlayGame);
|
||||
am->AddAction(ID_VIEW_SWITCHTOGAME_FULLSCREEN, tr("Play &Game (Maximized)"))
|
||||
.SetShortcut(tr("Ctrl+Shift+G"))
|
||||
.SetStatusTip(tr("Activate the game input mode (maximized)"))
|
||||
.SetIcon(Style::icon("Play"))
|
||||
.SetApplyHoverEffect()
|
||||
.SetCheckable(true);
|
||||
am->AddAction(ID_TOOLBAR_WIDGET_PLAYCONSOLE_LABEL, tr("Play Controls"))
|
||||
.SetText(tr("Play Controls"));
|
||||
am->AddAction(ID_SWITCH_PHYSICS, tr("Simulate"))
|
||||
@@ -1266,10 +1272,25 @@ void MainWindow::OnGameModeChanged(bool inGameMode)
|
||||
{
|
||||
menuBar()->setDisabled(inGameMode);
|
||||
m_toolbarManager->SetEnabled(!inGameMode);
|
||||
QAction* action = m_actionManager->GetAction(ID_VIEW_SWITCHTOGAME);
|
||||
action->blockSignals(true); // avoid a loop
|
||||
action->setChecked(inGameMode);
|
||||
action->blockSignals(false);
|
||||
|
||||
// block signals on the switch to game actions before setting the checked state, as
|
||||
// setting the checked state triggers the action, which will re-enter this function
|
||||
// and result in an infinite loop
|
||||
AZStd::vector<QAction*> actions = { m_actionManager->GetAction(ID_VIEW_SWITCHTOGAME), m_actionManager->GetAction(ID_VIEW_SWITCHTOGAME_FULLSCREEN) };
|
||||
for (auto action : actions)
|
||||
{
|
||||
action->blockSignals(true);
|
||||
}
|
||||
|
||||
for (auto action : actions)
|
||||
{
|
||||
action->setChecked(inGameMode);
|
||||
}
|
||||
|
||||
for (auto action : actions)
|
||||
{
|
||||
action->blockSignals(false);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::OnEditorNotifyEvent(EEditorNotifyEvent ev)
|
||||
|
||||
@@ -111,6 +111,7 @@
|
||||
#define ID_EDIT_FETCH 33465
|
||||
#define ID_FILE_EXPORTTOGAMENOSURFACETEXTURE 33473
|
||||
#define ID_VIEW_SWITCHTOGAME 33477
|
||||
#define ID_VIEW_SWITCHTOGAME_FULLSCREEN 33478
|
||||
#define ID_EDIT_DELETE 33480
|
||||
#define ID_MOVE_OBJECT 33481
|
||||
#define ID_RENAME_OBJ 33483
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
|
||||
#include <AzCore/StringFunc/StringFunc.h>
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
#include <AzCore/Console/IConsole.h>
|
||||
|
||||
// AzFramework
|
||||
#include <AzFramework/API/ApplicationAPI.h>
|
||||
@@ -926,6 +927,8 @@ void SEditorSettings::Load()
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
AZ_CVAR(bool, ed_previewGameInFullscreen_once, false, nullptr, AZ::ConsoleFunctorFlags::IsInvisible, "Preview the game (Ctrl+G, \"Play Game\", etc.) in fullscreen once");
|
||||
|
||||
void SEditorSettings::PostInitApply()
|
||||
{
|
||||
if (!gEnv || !gEnv->pConsole)
|
||||
|
||||
@@ -603,6 +603,7 @@ AmazonToolbar ToolbarManager::GetPlayConsoleToolbar() const
|
||||
t.AddAction(ID_TOOLBAR_SEPARATOR, ORIGINAL_TOOLBAR_VERSION);
|
||||
t.AddAction(ID_TOOLBAR_WIDGET_PLAYCONSOLE_LABEL, ORIGINAL_TOOLBAR_VERSION);
|
||||
t.AddAction(ID_VIEW_SWITCHTOGAME, TOOLBARS_WITH_PLAY_GAME);
|
||||
t.AddAction(ID_VIEW_SWITCHTOGAME_FULLSCREEN, TOOLBARS_WITH_PLAY_GAME);
|
||||
t.AddAction(ID_TOOLBAR_SEPARATOR, ORIGINAL_TOOLBAR_VERSION);
|
||||
t.AddAction(ID_SWITCH_PHYSICS, TOOLBARS_WITH_PLAY_GAME);
|
||||
return t;
|
||||
|
||||
@@ -181,7 +181,8 @@ void CViewportTitleDlg::SetupCameraDropdownMenu()
|
||||
auto comboBoxTextChanged = static_cast<void (QComboBox::*)(const QString&)>(&QComboBox::currentTextChanged);
|
||||
|
||||
SetSpeedComboBox(cameraMoveSpeed);
|
||||
m_cameraSpeed->setInsertPolicy(QComboBox::NoInsert);
|
||||
m_cameraSpeed->setInsertPolicy(QComboBox::InsertAtBottom);
|
||||
m_cameraSpeed->setDuplicatesEnabled(false);
|
||||
connect(m_cameraSpeed, comboBoxTextChanged, this, &CViewportTitleDlg::OnUpdateMoveSpeedText);
|
||||
connect(m_cameraSpeed->lineEdit(), &QLineEdit::returnPressed, this, &CViewportTitleDlg::OnSpeedComboBoxEnter);
|
||||
|
||||
|
||||
@@ -117,11 +117,11 @@ protected:
|
||||
// Speed combobox/lineEdit settings
|
||||
double m_minSpeed = 0.01;
|
||||
double m_maxSpeed = 100.0;
|
||||
double m_speedStep = 0.01;
|
||||
double m_speedStep = 0.001;
|
||||
int m_numDecimals = 3;
|
||||
|
||||
// Speed presets
|
||||
float m_speedPresetValues[3] = { 0.1f, 1.0f, 10.0f };
|
||||
float m_speedPresetValues[4] = { 0.01f, 0.1f, 1.0f, 10.0f };
|
||||
|
||||
double m_fieldWidthMultiplier = 1.8;
|
||||
|
||||
|
||||
@@ -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 = {};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user