Merge branch 'development' of https://github.com/aws-lumberyard/o3de into mp_systemcomp_cleanup

This commit is contained in:
puvvadar
2021-06-16 15:46:22 -07:00
249 changed files with 13964 additions and 16908 deletions
+1 -1
View File
@@ -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
View File
@@ -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);
-2
View File
@@ -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
+1
View 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} }
@@ -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>
+22 -20
View File
@@ -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()
@@ -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()
@@ -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;
@@ -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"
@@ -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
@@ -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();
}
@@ -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;
@@ -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);
}
@@ -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)
@@ -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"
@@ -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
@@ -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());
@@ -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);
}
}
@@ -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)
@@ -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;
@@ -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);
@@ -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))
@@ -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;
@@ -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.");
@@ -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();
}
@@ -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.
};
@@ -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
@@ -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
)
+8 -3
View File
@@ -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);
+8 -1
View File
@@ -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)
{
+164 -5
View File
@@ -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;
+2
View File
@@ -113,6 +113,8 @@ public:
//! Switch 2D viewports.
void Cycle2DViewport();
using AzQtComponents::ToolBarArea::SetMainWidget;
public slots:
void ResetLayout();
+25 -4
View File
@@ -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)
+1
View File
@@ -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
+3
View File
@@ -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)
+1
View File
@@ -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;
+2 -1
View File
@@ -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);
+2 -2
View File
@@ -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;
+1 -1
View File
@@ -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}
+58 -5
View File
@@ -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