Merge remote-tracking branch 'origin/development' into sc-editor-asset-redux
Signed-off-by: carlitosan <82187351+carlitosan@users.noreply.github.com>
This commit is contained in:
@@ -10,6 +10,7 @@
|
||||
|
||||
#include <AzCore/base.h>
|
||||
#include <AzCore/EBus/EBus.h>
|
||||
#include <AzCore/Math/Vector2.h>
|
||||
|
||||
class CVegetationMap;
|
||||
struct CVegetationInstance;
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace AzToolsFramework::EmbeddedPython
|
||||
{
|
||||
// When using embedded Python, some platforms need to explicitly load the python library.
|
||||
// For any modules that depend on 3rdParty::Python package, the AZ::Module should inherit this class.
|
||||
class PythonLoader
|
||||
{
|
||||
public:
|
||||
PythonLoader();
|
||||
~PythonLoader();
|
||||
|
||||
private:
|
||||
[[maybe_unused]] void* m_embeddedLibPythonHandle{ nullptr };
|
||||
};
|
||||
|
||||
} // namespace AzToolsFramework::EmbeddedPython
|
||||
@@ -927,6 +927,9 @@ namespace AzToolsFramework
|
||||
/// Notify that the MainWindow has been fully initialized
|
||||
virtual void NotifyMainWindowInitialized(QMainWindow* /*mainWindow*/) {}
|
||||
|
||||
/// Notify that the Editor has been fully initialized
|
||||
virtual void NotifyEditorInitialized() {}
|
||||
|
||||
/// Signal that an asset should be highlighted / selected
|
||||
virtual void SelectAsset(const QString& /* assetPath */) {}
|
||||
};
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include <AzToolsFramework/ContainerEntity/ContainerEntitySystemComponent.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntityContextComponent.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntityInfoBus.h>
|
||||
#include <AzToolsFramework/Entity/ReadOnly/ReadOnlyEntitySystemComponent.h>
|
||||
#include <AzToolsFramework/FocusMode/FocusModeSystemComponent.h>
|
||||
#include <AzToolsFramework/Slice/SliceMetadataEntityContextComponent.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabSystemComponent.h>
|
||||
@@ -54,6 +55,7 @@
|
||||
#include <AzToolsFramework/API/ComponentEntityObjectBus.h>
|
||||
#include <AzToolsFramework/API/EditorCameraBus.h>
|
||||
#include <AzToolsFramework/API/ViewPaneOptions.h>
|
||||
#include <AzToolsFramework/API/ViewportEditorModeTrackerNotificationBus.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntitySearchComponent.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntitySortComponent.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntityModelComponent.h>
|
||||
@@ -215,12 +217,17 @@ namespace AzToolsFramework
|
||||
, public AZ::BehaviorEBusHandler
|
||||
{
|
||||
AZ_EBUS_BEHAVIOR_BINDER(EditorEventsBusHandler, "{352F80BB-469A-40B6-B322-FE57AB51E4DA}", AZ::SystemAllocator,
|
||||
NotifyRegisterViews);
|
||||
NotifyRegisterViews, NotifyEditorInitialized);
|
||||
|
||||
void NotifyRegisterViews() override
|
||||
{
|
||||
Call(FN_NotifyRegisterViews);
|
||||
}
|
||||
|
||||
void NotifyEditorInitialized() override
|
||||
{
|
||||
Call(FN_NotifyEditorInitialized);
|
||||
}
|
||||
};
|
||||
|
||||
} // Internal
|
||||
@@ -234,12 +241,14 @@ namespace AzToolsFramework
|
||||
, m_isInIsolationMode(false)
|
||||
{
|
||||
ToolsApplicationRequests::Bus::Handler::BusConnect();
|
||||
AzToolsFramework::Prefab::PrefabPublicNotificationBus::Handler::BusConnect();
|
||||
|
||||
m_undoCache.RegisterToUndoCacheInterface();
|
||||
}
|
||||
|
||||
ToolsApplication::~ToolsApplication()
|
||||
{
|
||||
AzToolsFramework::Prefab::PrefabPublicNotificationBus::Handler::BusDisconnect();
|
||||
ToolsApplicationRequests::Bus::Handler::BusDisconnect();
|
||||
Stop();
|
||||
}
|
||||
@@ -260,6 +269,7 @@ namespace AzToolsFramework
|
||||
azrtti_typeid<Components::EditorEntityUiSystemComponent>(),
|
||||
azrtti_typeid<FocusModeSystemComponent>(),
|
||||
azrtti_typeid<ContainerEntitySystemComponent>(),
|
||||
azrtti_typeid<ReadOnlyEntitySystemComponent>(),
|
||||
azrtti_typeid<SliceMetadataEntityContextComponent>(),
|
||||
azrtti_typeid<Prefab::PrefabSystemComponent>(),
|
||||
azrtti_typeid<EditorEntityFixupComponent>(),
|
||||
@@ -378,6 +388,7 @@ namespace AzToolsFramework
|
||||
ComponentModeFramework::ComponentModeDelegate::Reflect(context);
|
||||
|
||||
ViewportInteraction::ViewportInteractionReflect(context);
|
||||
ViewportEditorModeNotifications::Reflect(context);
|
||||
|
||||
Camera::EditorCameraRequests::Reflect(context);
|
||||
AzToolsFramework::EditorTransformComponentSelectionRequests::Reflect(context);
|
||||
@@ -445,6 +456,7 @@ namespace AzToolsFramework
|
||||
->Attribute(AZ::Script::Attributes::Module, "editor")
|
||||
->Handler<Internal::EditorEventsBusHandler>()
|
||||
->Event("NotifyRegisterViews", &EditorEvents::NotifyRegisterViews)
|
||||
->Event("NotifyEditorInitialized", &EditorEvents::NotifyEditorInitialized)
|
||||
;
|
||||
|
||||
behaviorContext->EBus<ViewPaneCallbackBus>("ViewPaneCallbackBus")
|
||||
@@ -560,6 +572,12 @@ namespace AzToolsFramework
|
||||
void ToolsApplication::MarkEntitySelected(AZ::EntityId entityId)
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AzToolsFramework);
|
||||
|
||||
if (m_freezeSelectionUpdates)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AZ_Assert(entityId.IsValid(), "Invalid entity Id being marked as selected.");
|
||||
|
||||
EntityIdList::iterator foundIter = AZStd::find(m_selectedEntities.begin(), m_selectedEntities.end(), entityId);
|
||||
@@ -579,6 +597,11 @@ namespace AzToolsFramework
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AzToolsFramework);
|
||||
|
||||
if (m_freezeSelectionUpdates)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
EntityIdList entitiesSelected;
|
||||
entitiesSelected.reserve(entitiesToSelect.size());
|
||||
|
||||
@@ -602,6 +625,12 @@ namespace AzToolsFramework
|
||||
void ToolsApplication::MarkEntityDeselected(AZ::EntityId entityId)
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AzToolsFramework);
|
||||
|
||||
if (m_freezeSelectionUpdates)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto foundIter = AZStd::find(m_selectedEntities.begin(), m_selectedEntities.end(), entityId);
|
||||
if (foundIter != m_selectedEntities.end())
|
||||
{
|
||||
@@ -619,6 +648,11 @@ namespace AzToolsFramework
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AzToolsFramework);
|
||||
|
||||
if (m_freezeSelectionUpdates)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ToolsApplicationEvents::Bus::Broadcast(&ToolsApplicationEvents::BeforeEntitySelectionChanged);
|
||||
|
||||
EntityIdSet entitySetToDeselect(entitiesToDeselect.begin(), entitiesToDeselect.end());
|
||||
@@ -673,6 +707,11 @@ namespace AzToolsFramework
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AzToolsFramework);
|
||||
|
||||
if (m_freezeSelectionUpdates)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// We're setting the selection set as a batch from an external caller.
|
||||
// * Filter out any unselectable entities
|
||||
// * Calculate selection/deselection delta so we can notify specific entities only on change.
|
||||
@@ -1192,14 +1231,25 @@ namespace AzToolsFramework
|
||||
|
||||
AZ::EntityId ToolsApplication::GetCurrentLevelEntityId()
|
||||
{
|
||||
AzFramework::EntityContextId editorEntityContextId = AzFramework::EntityContextId::CreateNull();
|
||||
AzToolsFramework::EditorEntityContextRequestBus::BroadcastResult(editorEntityContextId, &AzToolsFramework::EditorEntityContextRequestBus::Events::GetEditorEntityContextId);
|
||||
AZ::SliceComponent* rootSliceComponent = nullptr;
|
||||
AzFramework::SliceEntityOwnershipServiceRequestBus::EventResult(rootSliceComponent, editorEntityContextId,
|
||||
&AzFramework::SliceEntityOwnershipServiceRequestBus::Events::GetRootSlice);
|
||||
if (rootSliceComponent && rootSliceComponent->GetMetadataEntity())
|
||||
if (IsPrefabSystemEnabled())
|
||||
{
|
||||
return rootSliceComponent->GetMetadataEntity()->GetId();
|
||||
if (auto prefabPublicInterface = AZ::Interface<Prefab::PrefabPublicInterface>::Get())
|
||||
{
|
||||
return prefabPublicInterface->GetLevelInstanceContainerEntityId();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AzFramework::EntityContextId editorEntityContextId = AzFramework::EntityContextId::CreateNull();
|
||||
AzToolsFramework::EditorEntityContextRequestBus::BroadcastResult(
|
||||
editorEntityContextId, &AzToolsFramework::EditorEntityContextRequestBus::Events::GetEditorEntityContextId);
|
||||
AZ::SliceComponent* rootSliceComponent = nullptr;
|
||||
AzFramework::SliceEntityOwnershipServiceRequestBus::EventResult(
|
||||
rootSliceComponent, editorEntityContextId, &AzFramework::SliceEntityOwnershipServiceRequestBus::Events::GetRootSlice);
|
||||
if (rootSliceComponent && rootSliceComponent->GetMetadataEntity())
|
||||
{
|
||||
return rootSliceComponent->GetMetadataEntity()->GetId();
|
||||
}
|
||||
}
|
||||
|
||||
return AZ::EntityId();
|
||||
@@ -1552,6 +1602,16 @@ namespace AzToolsFramework
|
||||
}
|
||||
}
|
||||
|
||||
void ToolsApplication::OnPrefabInstancePropagationBegin()
|
||||
{
|
||||
m_freezeSelectionUpdates = true;
|
||||
}
|
||||
|
||||
void ToolsApplication::OnPrefabInstancePropagationEnd()
|
||||
{
|
||||
m_freezeSelectionUpdates = false;
|
||||
}
|
||||
|
||||
void ToolsApplication::CreateUndosForDirtyEntities()
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AzToolsFramework);
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include <AzToolsFramework/API/EditorEntityAPI.h>
|
||||
#include <AzToolsFramework/Application/EditorEntityManager.h>
|
||||
#include <AzToolsFramework/Commands/PreemptiveUndoCache.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabPublicNotificationBus.h>
|
||||
|
||||
#pragma once
|
||||
|
||||
@@ -29,6 +30,7 @@ namespace AzToolsFramework
|
||||
class ToolsApplication
|
||||
: public AzFramework::Application
|
||||
, public ToolsApplicationRequests::Bus::Handler
|
||||
, public AzToolsFramework::Prefab::PrefabPublicNotificationBus::Handler
|
||||
{
|
||||
public:
|
||||
AZ_RTTI(ToolsApplication, "{2895561E-BE90-4CC3-8370-DD46FCF74C01}", AzFramework::Application);
|
||||
@@ -169,6 +171,14 @@ namespace AzToolsFramework
|
||||
};
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// PrefabPublicNotificationBus::Handler
|
||||
|
||||
void OnPrefabInstancePropagationBegin() override;
|
||||
void OnPrefabInstancePropagationEnd() override;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void CreateUndosForDirtyEntities();
|
||||
void ConsistencyCheckUndoCache();
|
||||
AZ::Aabb m_selectionBounds;
|
||||
@@ -181,6 +191,7 @@ namespace AzToolsFramework
|
||||
bool m_isDuringUndoRedo;
|
||||
bool m_isInIsolationMode;
|
||||
EntityIdSet m_isolatedEntityIdSet;
|
||||
bool m_freezeSelectionUpdates = false;
|
||||
|
||||
EditorEntityAPI* m_editorEntityAPI = nullptr;
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
#include <AzCore/Module/DynamicModuleHandle.h>
|
||||
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
|
||||
#include <AzCore/StringFunc/StringFunc.h>
|
||||
#include <AzFramework/API/ApplicationAPI.h>
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
#include <AzFramework/IO/LocalFileIO.h>
|
||||
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
|
||||
#include <AzToolsFramework/Asset/AssetUtils.h>
|
||||
@@ -205,7 +205,7 @@ namespace AzToolsFramework::AssetUtils
|
||||
return platformConfigFilePathsAdded;
|
||||
}
|
||||
|
||||
AZStd::vector<AZ::IO::Path> GetConfigFiles(AZStd::string_view engineRoot, AZStd::string_view assetRoot, AZStd::string_view projectPath,
|
||||
AZStd::vector<AZ::IO::Path> GetConfigFiles(AZStd::string_view engineRoot, AZStd::string_view projectPath,
|
||||
bool addPlatformConfigs, bool addGemsConfigs, AZ::SettingsRegistryInterface* settingsRegistry)
|
||||
{
|
||||
constexpr const char* AssetProcessorGamePlatformConfigFileName = "AssetProcessorGamePlatformConfig.ini";
|
||||
@@ -232,14 +232,13 @@ namespace AzToolsFramework::AssetUtils
|
||||
Internal::AddGemConfigFiles(gemInfoList, configFiles);
|
||||
}
|
||||
|
||||
AZ::IO::Path assetRootDir(assetRoot);
|
||||
assetRootDir /= projectPath;
|
||||
AZ::IO::Path projectRoot(projectPath);
|
||||
|
||||
AZ::IO::Path projectConfigFile = assetRootDir / AssetProcessorGamePlatformConfigFileName;
|
||||
AZ::IO::Path projectConfigFile = projectRoot / AssetProcessorGamePlatformConfigFileName;
|
||||
configFiles.push_back(projectConfigFile);
|
||||
|
||||
// Add a file entry for the Project AssetProcessor setreg file
|
||||
projectConfigFile = assetRootDir / AssetProcessorGamePlatformConfigSetreg;
|
||||
projectConfigFile = projectRoot / AssetProcessorGamePlatformConfigSetreg;
|
||||
configFiles.push_back(projectConfigFile);
|
||||
|
||||
return configFiles;
|
||||
@@ -251,10 +250,10 @@ namespace AzToolsFramework::AssetUtils
|
||||
AZStd::vector<AZStd::string> tokens;
|
||||
AZ::StringFunc::Tokenize(relPathFromRoot.c_str(), tokens, AZ_CORRECT_FILESYSTEM_SEPARATOR_STRING);
|
||||
|
||||
AZStd::string validatedPath;
|
||||
AZ::IO::FixedMaxPath validatedPath;
|
||||
if (rootPath.empty())
|
||||
{
|
||||
AzFramework::ApplicationRequests::Bus::BroadcastResult(validatedPath, &AzFramework::ApplicationRequests::GetEngineRoot);
|
||||
validatedPath = AZ::Utils::GetEnginePath();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -299,10 +298,7 @@ namespace AzToolsFramework::AssetUtils
|
||||
break;
|
||||
}
|
||||
|
||||
AZStd::string absoluteFilePath;
|
||||
AZ::StringFunc::Path::ConstructFull(validatedPath.c_str(), element.c_str(), absoluteFilePath);
|
||||
|
||||
validatedPath = absoluteFilePath; // go one step deeper.
|
||||
validatedPath /= element; // go one step deeper.
|
||||
}
|
||||
|
||||
if (success)
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace AzToolsFramework::AssetUtils
|
||||
//! Also note that if the project has any "game project gems", then those will also be inserted last,
|
||||
//! and thus have a higher priority than the root or non - project gems.
|
||||
//! Also note that the game project could be in a different location to the engine therefore we need the assetRoot param.
|
||||
AZStd::vector<AZ::IO::Path> GetConfigFiles(AZStd::string_view engineRoot, AZStd::string_view assetRoot, AZStd::string_view projectPath,
|
||||
AZStd::vector<AZ::IO::Path> GetConfigFiles(AZStd::string_view engineRoot, AZStd::string_view projectPath,
|
||||
bool addPlatformConfigs = true, bool addGemsConfigs = true, AZ::SettingsRegistryInterface* settingsRegistry = nullptr);
|
||||
|
||||
//! A utility function which checks the given path starting at the root and updates the relative path to be the actual case correct path.
|
||||
|
||||
-5
@@ -234,11 +234,6 @@ namespace AzToolsFramework
|
||||
return SourceFileDetails("Icons/AssetBrowser/Lua_16.svg");
|
||||
}
|
||||
|
||||
if (AzFramework::StringFunc::Equal(extension.c_str(), ".mtl"))
|
||||
{
|
||||
return SourceFileDetails("Icons/AssetBrowser/Material_16.svg");
|
||||
}
|
||||
|
||||
if (AzFramework::StringFunc::Equal(extension.c_str(), AzToolsFramework::SliceUtilities::GetSliceFileExtension().c_str()))
|
||||
{
|
||||
return SourceFileDetails("Icons/AssetBrowser/Slice_16.svg");
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option")
|
||||
AZ_POP_DISABLE_WARNING
|
||||
|
||||
AZ_CVAR(
|
||||
bool, ed_useNewAssetBrowserTableView, false, nullptr, AZ::ConsoleFunctorFlags::Null,
|
||||
bool, ed_useNewAssetBrowserTableView, true, nullptr, AZ::ConsoleFunctorFlags::Null,
|
||||
"Use the new AssetBrowser TableView for searching assets.");
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
|
||||
+1
@@ -30,6 +30,7 @@ namespace AzToolsFramework
|
||||
connect(m_filterModel, &QAbstractItemModel::rowsRemoved, this, &AssetBrowserTableModel::UpdateTableModelMaps);
|
||||
connect(m_filterModel, &QAbstractItemModel::layoutChanged, this, &AssetBrowserTableModel::UpdateTableModelMaps);
|
||||
connect(m_filterModel, &AssetBrowserFilterModel::filterChanged, this, &AssetBrowserTableModel::beginResetModel);
|
||||
connect(m_filterModel, &AssetBrowserFilterModel::filterChanged, this, &AssetBrowserTableModel::UpdateTableModelMaps);
|
||||
connect(m_filterModel, &QAbstractItemModel::dataChanged, this, &AssetBrowserTableModel::SourceDataChanged);
|
||||
}
|
||||
|
||||
|
||||
+5
-2
@@ -31,7 +31,10 @@ AZ_POP_DISABLE_WARNING
|
||||
AZ_CVAR(
|
||||
bool, ed_hideAssetPickerPathColumn, true, nullptr, AZ::ConsoleFunctorFlags::Null,
|
||||
"Hide AssetPicker path column for a clearer view.");
|
||||
AZ_CVAR_EXTERNED(bool, ed_useNewAssetBrowserTableView);
|
||||
|
||||
AZ_CVAR(
|
||||
bool, ed_useNewAssetPickerView, false, nullptr, AZ::ConsoleFunctorFlags::Null,
|
||||
"Uses the new Asset Picker View.");
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
@@ -118,7 +121,7 @@ namespace AzToolsFramework
|
||||
m_persistentState = AZ::UserSettings::CreateFind<AzToolsFramework::QWidgetSavedState>(AZ::Crc32(("AssetBrowserTreeView_Dialog_" + name).toUtf8().data()), AZ::UserSettings::CT_GLOBAL);
|
||||
|
||||
m_ui->m_assetBrowserTableViewWidget->setVisible(false);
|
||||
if (ed_useNewAssetBrowserTableView)
|
||||
if (ed_useNewAssetPickerView)
|
||||
{
|
||||
m_ui->m_assetBrowserTreeViewWidget->setVisible(false);
|
||||
m_ui->m_assetBrowserTableViewWidget->setVisible(true);
|
||||
|
||||
+4
-6
@@ -9,11 +9,11 @@
|
||||
#include <AzCore/EBus/Results.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
#include <AzFramework/StringFunc/StringFunc.h>
|
||||
#include <AzToolsFramework/AssetBrowser/AssetBrowserBus.h>
|
||||
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
|
||||
#include <AzToolsFramework/AssetBrowser/Thumbnails/SourceThumbnail.h>
|
||||
#include <AzFramework/API/ApplicationAPI.h>
|
||||
#include <QString>
|
||||
|
||||
namespace AzToolsFramework
|
||||
@@ -113,11 +113,9 @@ namespace AzToolsFramework
|
||||
|
||||
if (iconPathToUse.isEmpty())
|
||||
{
|
||||
const char* engineRoot = nullptr;
|
||||
AzFramework::ApplicationRequests::Bus::BroadcastResult(engineRoot, &AzFramework::ApplicationRequests::GetEngineRoot);
|
||||
AZ_Assert(engineRoot, "Engine Root not initialized");
|
||||
AZStd::string iconPath = AZStd::string::format("%s%s", engineRoot, DefaultFileIconPath);
|
||||
iconPathToUse = iconPath.c_str();
|
||||
AZ::IO::FixedMaxPath engineRoot = AZ::Utils::GetEnginePath();
|
||||
AZ_Assert(!engineRoot.empty(), "Engine Root not initialized");
|
||||
iconPathToUse = (engineRoot / DefaultFileIconPath).c_str();
|
||||
}
|
||||
|
||||
m_pixmap.load(iconPathToUse);
|
||||
|
||||
+4
-4
@@ -202,9 +202,9 @@ namespace AzToolsFramework
|
||||
AZ_TracePrintf(logWindowName, "Creating new asset bundle manifest file \"%s\" for source pak \"%s\".\n", AzFramework::AssetBundleManifest::s_manifestFileName, sourcePak.c_str());
|
||||
bool manifestSaved = false;
|
||||
AZStd::string manifestDirectory;
|
||||
AZStd::vector<AZStd::string> levelDirs;
|
||||
AzFramework::StringFunc::Path::GetFullPath(sourcePak.c_str(), manifestDirectory);
|
||||
AssetCatalogRequestBus::BroadcastResult(manifestSaved, &AssetCatalogRequestBus::Events::CreateBundleManifest, outCatalogPath, AZStd::vector<AZStd::string>(), manifestDirectory, AzFramework::AssetBundleManifest::CurrentBundleVersion, levelDirs);
|
||||
AssetCatalogRequestBus::BroadcastResult(manifestSaved, &AssetCatalogRequestBus::Events::CreateBundleManifest, outCatalogPath,
|
||||
AZStd::vector<AZStd::string>(), manifestDirectory, AzFramework::AssetBundleManifest::CurrentBundleVersion, AZStd::vector<AZ::IO::Path>{});
|
||||
|
||||
AZStd::string manifestPath;
|
||||
AzFramework::StringFunc::Path::Join(manifestDirectory.c_str(), AzFramework::AssetBundleManifest::s_manifestFileName, manifestPath);
|
||||
@@ -263,7 +263,7 @@ namespace AzToolsFramework
|
||||
AZStd::string tempBundleFilePath = bundleFilePath.Native() + "_temp";
|
||||
|
||||
AZStd::vector<AZStd::string> dependentBundleNames;
|
||||
AZStd::vector<AZStd::string> levelDirs;
|
||||
AZStd::vector<AZ::IO::Path> levelDirs;
|
||||
AZStd::vector<AZStd::pair<AZStd::string, AZStd::string>> bundlePathDeltaCatalogPair;
|
||||
bundlePathDeltaCatalogPair.emplace_back(AZStd::make_pair(tempBundleFilePath, DeltaCatalogName));
|
||||
|
||||
@@ -515,7 +515,7 @@ namespace AzToolsFramework
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AssetBundleComponent::AddManifestFileToBundles(const AZStd::vector<AZStd::pair<AZStd::string, AZStd::string>>& bundlePathDeltaCatalogPair, const AZStd::vector<AZStd::string>& dependentBundleNames, const AZStd::string& bundleFolder, const AzToolsFramework::AssetBundleSettings& assetBundleSettings, const AZStd::vector<AZStd::string>& levelDirs)
|
||||
bool AssetBundleComponent::AddManifestFileToBundles(const AZStd::vector<AZStd::pair<AZStd::string, AZStd::string>>& bundlePathDeltaCatalogPair, const AZStd::vector<AZStd::string>& dependentBundleNames, const AZStd::string& bundleFolder, const AzToolsFramework::AssetBundleSettings& assetBundleSettings, const AZStd::vector<AZ::IO::Path>& levelDirs)
|
||||
{
|
||||
if (!MakePath(bundleFolder))
|
||||
{
|
||||
|
||||
@@ -86,7 +86,7 @@ namespace AzToolsFramework
|
||||
|
||||
//! Adds the manifest file to all the bundles
|
||||
//! The parent bundle manifest file is special since it will contain information of all dependent bundles names.
|
||||
bool AddManifestFileToBundles(const AZStd::vector<AZStd::pair<AZStd::string, AZStd::string>>& bundlePathDeltaCatalogPair, const AZStd::vector<AZStd::string>& dependentBundleNames, const AZStd::string& bundleFolder, const AzToolsFramework::AssetBundleSettings& assetBundleSettings, const AZStd::vector<AZStd::string>& levelDirs);
|
||||
bool AddManifestFileToBundles(const AZStd::vector<AZStd::pair<AZStd::string, AZStd::string>>& bundlePathDeltaCatalogPair, const AZStd::vector<AZStd::string>& dependentBundleNames, const AZStd::string& bundleFolder, const AzToolsFramework::AssetBundleSettings& assetBundleSettings, const AZStd::vector<AZ::IO::Path>& levelDirs);
|
||||
|
||||
//! Adds the delta catalog and any remaining files to the bundle
|
||||
//! We only create the delta catalog once we are sure about what all the files that will go in it.
|
||||
|
||||
+1
-1
@@ -143,7 +143,7 @@ namespace AzToolsFramework
|
||||
return AssetCatalog::RemoveDeltaCatalog(deltaCatalog);
|
||||
}
|
||||
|
||||
bool PlatformAddressedAssetCatalog::CreateBundleManifest(const AZStd::string& deltaCatalogPath, const AZStd::vector<AZStd::string>& dependentBundleNames, const AZStd::string& fileDirectory, int bundleVersion, const AZStd::vector<AZStd::string>& levelDirs)
|
||||
bool PlatformAddressedAssetCatalog::CreateBundleManifest(const AZStd::string& deltaCatalogPath, const AZStd::vector<AZStd::string>& dependentBundleNames, const AZStd::string& fileDirectory, int bundleVersion, const AZStd::vector<AZ::IO::Path>& levelDirs)
|
||||
{
|
||||
return AssetCatalog::CreateBundleManifest(deltaCatalogPath, dependentBundleNames, fileDirectory, bundleVersion, levelDirs);
|
||||
}
|
||||
|
||||
+1
-1
@@ -66,7 +66,7 @@ namespace AzToolsFramework
|
||||
bool InsertDeltaCatalogBefore(AZStd::shared_ptr<AzFramework::AssetRegistry> deltaCatalog, AZStd::shared_ptr<AzFramework::AssetRegistry> afterDeltaCatalog) override;
|
||||
bool RemoveDeltaCatalog(AZStd::shared_ptr<AzFramework::AssetRegistry> deltaCatalog) override;
|
||||
|
||||
bool CreateBundleManifest(const AZStd::string& deltaCatalogPath, const AZStd::vector<AZStd::string>& dependentBundleNames, const AZStd::string& fileDirectory, int bundleVersion, const AZStd::vector<AZStd::string>& levelDirs) override;
|
||||
bool CreateBundleManifest(const AZStd::string& deltaCatalogPath, const AZStd::vector<AZStd::string>& dependentBundleNames, const AZStd::string& fileDirectory, int bundleVersion, const AZStd::vector<AZ::IO::Path>& levelDirs) override;
|
||||
bool CreateDeltaCatalog(const AZStd::vector<AZStd::string>& files, const AZStd::string& filePath) override;
|
||||
void AddExtension(const char* extension) override;
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <AzToolsFramework/Entity/EditorEntityModelComponent.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntitySearchComponent.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntitySortComponent.h>
|
||||
#include <AzToolsFramework/Entity/ReadOnly/ReadOnlyEntitySystemComponent.h>
|
||||
#include <AzToolsFramework/FocusMode/FocusModeSystemComponent.h>
|
||||
#include <AzToolsFramework/PropertyTreeEditor/PropertyTreeEditorComponent.h>
|
||||
#include <AzToolsFramework/Render/EditorIntersectorComponent.h>
|
||||
@@ -75,6 +76,7 @@ namespace AzToolsFramework
|
||||
EditorEntityFixupComponent::CreateDescriptor(),
|
||||
EntityUtilityComponent::CreateDescriptor(),
|
||||
ContainerEntitySystemComponent::CreateDescriptor(),
|
||||
ReadOnlyEntitySystemComponent::CreateDescriptor(),
|
||||
FocusModeSystemComponent::CreateDescriptor(),
|
||||
SliceMetadataEntityContextComponent::CreateDescriptor(),
|
||||
SliceRequestComponent::CreateDescriptor(),
|
||||
|
||||
+2
@@ -597,11 +597,13 @@ namespace AzToolsFramework
|
||||
pte.SetVisibleEnforcement(true);
|
||||
}
|
||||
|
||||
ScopedUndoBatch undo("Modify Entity Property");
|
||||
PropertyOutcome result = pte.SetProperty(propertyPath, value);
|
||||
if (result.IsSuccess())
|
||||
{
|
||||
PropertyEditorEntityChangeNotificationBus::Event(componentInstance.GetEntityId(), &PropertyEditorEntityChangeNotifications::OnEntityComponentPropertyChanged, componentInstance.GetComponentId());
|
||||
}
|
||||
undo.MarkEntityDirty(componentInstance.GetEntityId());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
+5
-1
@@ -448,7 +448,11 @@ namespace AzToolsFramework
|
||||
|
||||
ViewportUi::ViewportUiRequestBus::Event(
|
||||
ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::CreateViewportBorder,
|
||||
componentMode.m_componentMode->GetComponentModeName().c_str());
|
||||
componentMode.m_componentMode->GetComponentModeName().c_str(),
|
||||
[]
|
||||
{
|
||||
ComponentModeSystemRequestBus::Broadcast(&ComponentModeSystemRequests::EndComponentMode);
|
||||
});
|
||||
}
|
||||
|
||||
RefreshActions();
|
||||
|
||||
+5
-2
@@ -55,8 +55,11 @@ namespace AzToolsFramework
|
||||
GetEntityComponentIdPair(), elementIdsToDisplay);
|
||||
// create the component mode border with the specific name for this component mode
|
||||
ViewportUi::ViewportUiRequestBus::Event(
|
||||
ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::CreateViewportBorder,
|
||||
GetComponentModeName());
|
||||
ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::CreateViewportBorder, GetComponentModeName(),
|
||||
[]
|
||||
{
|
||||
ComponentModeSystemRequestBus::Broadcast(&ComponentModeSystemRequests::EndComponentMode);
|
||||
});
|
||||
// set the EntityComponentId for this ComponentMode to active in the ComponentModeViewportUi system
|
||||
ComponentModeViewportUiRequestBus::Event(
|
||||
GetComponentType(), &ComponentModeViewportUiRequestBus::Events::SetViewportUiActiveEntityComponentId,
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <AzCore/std/sort.h>
|
||||
#include <AzCore/RTTI/AttributeReader.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzFramework/API/ApplicationAPI.h>
|
||||
#include <AzToolsFramework/Commands/EntityStateCommand.h>
|
||||
#include <AzToolsFramework/ContainerEntity/ContainerEntityInterface.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntityInfoBus.h>
|
||||
@@ -468,6 +469,18 @@ namespace AzToolsFramework
|
||||
EntityIdList children;
|
||||
EditorEntityInfoRequestBus::EventResult(children, parentId, &EditorEntityInfoRequestBus::Events::GetChildren);
|
||||
|
||||
// If Prefabs are enabled, don't check the order for an invalid parent, just return its children (i.e. the root container entity)
|
||||
// There will currently always be one root container entity, so there's no order to retrieve
|
||||
if (!parentId.IsValid())
|
||||
{
|
||||
bool isPrefabEnabled = false;
|
||||
AzFramework::ApplicationRequests::Bus::BroadcastResult(isPrefabEnabled, &AzFramework::ApplicationRequests::IsPrefabSystemEnabled);
|
||||
if (isPrefabEnabled)
|
||||
{
|
||||
return children;
|
||||
}
|
||||
}
|
||||
|
||||
EntityIdList entityChildOrder;
|
||||
AZ::EntityId sortEntityId = GetEntityIdForSortInfo(parentId);
|
||||
EditorEntitySortRequestBus::EventResult(entityChildOrder, sortEntityId, &EditorEntitySortRequestBus::Events::GetChildEntityOrderArray);
|
||||
|
||||
@@ -449,16 +449,23 @@ namespace AzToolsFramework
|
||||
AZStd::unordered_map<AZ::EntityId, AZStd::pair<AZ::EntityId, AZ::u64>>::const_iterator orderItr = m_savedOrderInfo.find(childId);
|
||||
if (orderItr != m_savedOrderInfo.end() && orderItr->second.first == parentId)
|
||||
{
|
||||
bool sortOrderUpdated = AzToolsFramework::RecoverEntitySortInfo(parentId, childId, orderItr->second.second);
|
||||
m_savedOrderInfo.erase(childId);
|
||||
|
||||
// force notify the child sort order changed on the parent entity info, but only if the restore didn't actually modify
|
||||
// the order internally (and sent ChildEntityOrderArrayUpdated). that may seem heavy handed, and it is, but necessary
|
||||
// to combat scenarios when the initial override detection returns a false positive (see comment about IDH comparisons
|
||||
// in OnChildSortOrderChanged) and the slice instance source-to-live mapping hasn't been fully reconstructed yet.
|
||||
if (!sortOrderUpdated)
|
||||
bool isPrefabEnabled = false;
|
||||
AzFramework::ApplicationRequests::Bus::BroadcastResult(
|
||||
isPrefabEnabled, &AzFramework::ApplicationRequests::IsPrefabSystemEnabled);
|
||||
// If prefabs are enabled, rely on the component to do a sanity check instead of restoring the order from the model
|
||||
if (!isPrefabEnabled)
|
||||
{
|
||||
parentInfo.OnChildSortOrderChanged();
|
||||
bool sortOrderUpdated = AzToolsFramework::RecoverEntitySortInfo(parentId, childId, orderItr->second.second);
|
||||
m_savedOrderInfo.erase(childId);
|
||||
|
||||
// force notify the child sort order changed on the parent entity info, but only if the restore didn't actually modify
|
||||
// the order internally (and sent ChildEntityOrderArrayUpdated). that may seem heavy handed, and it is, but necessary
|
||||
// to combat scenarios when the initial override detection returns a false positive (see comment about IDH comparisons
|
||||
// in OnChildSortOrderChanged) and the slice instance source-to-live mapping hasn't been fully reconstructed yet.
|
||||
if (!sortOrderUpdated)
|
||||
{
|
||||
parentInfo.OnChildSortOrderChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
+126
-7
@@ -8,9 +8,17 @@
|
||||
#include "EditorEntitySortComponent.h"
|
||||
#include "EditorEntityInfoBus.h"
|
||||
#include "EditorEntityHelpers.h"
|
||||
#include <AzCore/Component/TransformBus.h>
|
||||
#include <AzCore/Debug/Profiler.h>
|
||||
#include <AzCore/Serialization/EditContext.h>
|
||||
#include <AzCore/Serialization/Json/RegistrationContext.h>
|
||||
#include <AzCore/std/sort.h>
|
||||
#include <AzFramework/API/ApplicationAPI.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabPublicInterface.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabPublicRequestBus.h>
|
||||
#include <AzToolsFramework/Undo/UndoSystem.h>
|
||||
#include <AzCore/Serialization/Json/RegistrationContext.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntitySortComponentSerializer.h>
|
||||
|
||||
static_assert(sizeof(AZ::u64) == sizeof(AZ::EntityId), "We use AZ::EntityId for Persistent ID, which is a u64 under the hood. These must be the same size otherwise the persistent id will have to be rewritten");
|
||||
|
||||
@@ -49,6 +57,12 @@ namespace AzToolsFramework
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
AZ::JsonRegistrationContext* jsonRegistration = azrtti_cast<AZ::JsonRegistrationContext*>(context);
|
||||
if (jsonRegistration)
|
||||
{
|
||||
jsonRegistration->Serializer<JsonEditorEntitySortComponentSerializer>()->HandlesType<EditorEntitySortComponent>();
|
||||
}
|
||||
}
|
||||
|
||||
void EditorEntitySortComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services)
|
||||
@@ -144,6 +158,12 @@ namespace AzToolsFramework
|
||||
bool EditorEntitySortComponent::AddChildEntityInternal(const AZ::EntityId& entityId, bool addToBack, EntityOrderArray::iterator insertPosition)
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AzToolsFramework);
|
||||
|
||||
if (m_ignoreIncomingOrderChanges)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
auto entityItr = m_childEntityOrderCache.find(entityId);
|
||||
if (entityItr == m_childEntityOrderCache.end())
|
||||
{
|
||||
@@ -159,9 +179,6 @@ namespace AzToolsFramework
|
||||
}
|
||||
|
||||
MarkDirtyAndSendChangedEvent();
|
||||
|
||||
// Use the ToolsApplication to mark the entity dirty, this will only do something if we already have an undo batch
|
||||
ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequestBus::Events::AddDirtyEntity, GetEntityId());
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -179,6 +196,10 @@ namespace AzToolsFramework
|
||||
else
|
||||
{
|
||||
EntityOrderArray::iterator insertPosition = GetFirstSelectedEntityPosition();
|
||||
if (insertPosition != m_childEntityOrderArray.end())
|
||||
{
|
||||
++insertPosition;
|
||||
}
|
||||
retval = AddChildEntityInternal(entityId, false, insertPosition);
|
||||
}
|
||||
|
||||
@@ -198,6 +219,12 @@ namespace AzToolsFramework
|
||||
bool EditorEntitySortComponent::RemoveChildEntity(const AZ::EntityId& entityId)
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AzToolsFramework);
|
||||
|
||||
if (m_ignoreIncomingOrderChanges)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
auto entityItr = m_childEntityOrderCache.find(entityId);
|
||||
if (entityItr != m_childEntityOrderCache.end())
|
||||
{
|
||||
@@ -206,9 +233,6 @@ namespace AzToolsFramework
|
||||
|
||||
MarkDirtyAndSendChangedEvent();
|
||||
|
||||
// Use the ToolsApplication to mark the entity dirty, this will only do something if we already have an undo batch
|
||||
ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequestBus::Events::AddDirtyEntity, GetEntityId());
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -250,11 +274,30 @@ namespace AzToolsFramework
|
||||
}
|
||||
}
|
||||
|
||||
void EditorEntitySortComponent::OnPrefabInstancePropagationBegin()
|
||||
{
|
||||
m_ignoreIncomingOrderChanges = true;
|
||||
}
|
||||
|
||||
void EditorEntitySortComponent::OnPrefabInstancePropagationEnd()
|
||||
{
|
||||
m_ignoreIncomingOrderChanges = false;
|
||||
|
||||
if (m_shouldSanityCheckStateAfterPropagation)
|
||||
{
|
||||
SanitizeOrderEntryArray();
|
||||
m_shouldSanityCheckStateAfterPropagation = false;
|
||||
}
|
||||
}
|
||||
|
||||
void EditorEntitySortComponent::MarkDirtyAndSendChangedEvent()
|
||||
{
|
||||
// mark the order as dirty before sending the ChildEntityOrderArrayUpdated event in order for PrepareSave to be properly handled in the case
|
||||
// one of the event listeners needs to build the InstanceDataHierarchy
|
||||
m_entityOrderIsDirty = true;
|
||||
|
||||
// Use the ToolsApplication to mark the entity dirty, this will only do something if we already have an undo batch
|
||||
ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequestBus::Events::AddDirtyEntity, GetEntityId());
|
||||
EditorEntitySortNotificationBus::Event(GetEntityId(), &EditorEntitySortNotificationBus::Events::ChildEntityOrderArrayUpdated);
|
||||
}
|
||||
|
||||
@@ -264,10 +307,19 @@ namespace AzToolsFramework
|
||||
// This is a special case for certain EditorComponents only!
|
||||
EditorEntitySortRequestBus::Handler::BusConnect(GetEntityId());
|
||||
EditorEntityContextNotificationBus::Handler::BusConnect();
|
||||
AzToolsFramework::Prefab::PrefabPublicNotificationBus::Handler::BusConnect();
|
||||
}
|
||||
|
||||
void EditorEntitySortComponent::Activate()
|
||||
{
|
||||
// Run the post-serialize handler if prefabs are enabled because PostLoad won't be called automatically
|
||||
bool isPrefabEnabled = false;
|
||||
AzFramework::ApplicationRequests::Bus::BroadcastResult(
|
||||
isPrefabEnabled, &AzFramework::ApplicationRequests::IsPrefabSystemEnabled);
|
||||
if (isPrefabEnabled)
|
||||
{
|
||||
m_shouldSanityCheckStateAfterPropagation = true;
|
||||
}
|
||||
// Send out that the order for our entity is now updated
|
||||
EditorEntitySortNotificationBus::Event(GetEntityId(), &EditorEntitySortNotificationBus::Events::ChildEntityOrderArrayUpdated);
|
||||
}
|
||||
@@ -293,6 +345,73 @@ namespace AzToolsFramework
|
||||
m_entityOrderIsDirty = false;
|
||||
}
|
||||
|
||||
void EditorEntitySortComponent::SanitizeOrderEntryArray()
|
||||
{
|
||||
bool shouldEmitDirtyState = false;
|
||||
|
||||
// Remove invalid and duplicate entries that point at non-existent entities
|
||||
AZStd::unordered_set<AZ::EntityId> duplicateIds;
|
||||
for (auto it = m_childEntityOrderArray.begin(); it != m_childEntityOrderArray.end();)
|
||||
{
|
||||
if (!it->IsValid() || GetEntityById(*it) == nullptr || duplicateIds.contains(*it))
|
||||
{
|
||||
it = m_childEntityOrderArray.erase(it);
|
||||
shouldEmitDirtyState = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
duplicateIds.insert(*it);
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
// Append any missing children
|
||||
EntityIdList children;
|
||||
AZ::TransformBus::EventResult(children, GetEntityId(), &AZ::TransformBus::Events::GetChildren);
|
||||
for (auto it = m_childEntityOrderArray.begin(); it != m_childEntityOrderArray.end(); ++it)
|
||||
{
|
||||
if (auto removedChildrenIt = AZStd::remove(children.begin(), children.end(), *it); removedChildrenIt != children.end())
|
||||
{
|
||||
children.erase(removedChildrenIt);
|
||||
}
|
||||
}
|
||||
|
||||
AZStd::sort(children.begin(), children.end(), [](AZ::EntityId lhs, AZ::EntityId rhs)
|
||||
{
|
||||
return GetEntityById(lhs)->GetName() < GetEntityById(rhs)->GetName();
|
||||
});
|
||||
|
||||
if (!children.empty())
|
||||
{
|
||||
shouldEmitDirtyState = true;
|
||||
EntityOrderArray::iterator insertPosition = GetFirstSelectedEntityPosition();
|
||||
if (insertPosition != m_childEntityOrderArray.end())
|
||||
{
|
||||
++insertPosition;
|
||||
}
|
||||
m_childEntityOrderArray.insert(insertPosition, children.begin(), children.end());
|
||||
}
|
||||
|
||||
// Clear out the vector to be rebuilt from persistent id
|
||||
m_childEntityOrderEntryArray.resize(m_childEntityOrderArray.size());
|
||||
for (size_t i = 0; i < m_childEntityOrderArray.size(); ++i)
|
||||
{
|
||||
m_childEntityOrderEntryArray[i] = {
|
||||
m_childEntityOrderArray[i],
|
||||
static_cast<AZ::u64>(i)
|
||||
};
|
||||
}
|
||||
|
||||
RebuildEntityOrderCache();
|
||||
|
||||
if (shouldEmitDirtyState)
|
||||
{
|
||||
ToolsApplicationRequests::Bus::Broadcast(&ToolsApplicationRequests::Bus::Events::AddDirtyEntity, GetEntityId());
|
||||
}
|
||||
|
||||
m_entityOrderIsDirty = false;
|
||||
}
|
||||
|
||||
void EditorEntitySortComponent::PostLoad()
|
||||
{
|
||||
// Clear out the vector to be rebuilt from persistent id
|
||||
@@ -340,7 +459,7 @@ namespace AzToolsFramework
|
||||
firstSelectedEntityPos = selectedEntityPos < firstSelectedEntityPos ? selectedEntityPos : firstSelectedEntityPos;
|
||||
}
|
||||
|
||||
return firstSelectedEntityPos == m_childEntityOrderArray.end() ? m_childEntityOrderArray.begin() : firstSelectedEntityPos;
|
||||
return firstSelectedEntityPos;
|
||||
}
|
||||
}
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "EditorEntitySortBus.h"
|
||||
#include <AzToolsFramework/ToolsComponents/EditorComponentBase.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabPublicNotificationBus.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
@@ -20,7 +21,10 @@ namespace AzToolsFramework
|
||||
: public AzToolsFramework::Components::EditorComponentBase
|
||||
, public EditorEntitySortRequestBus::Handler
|
||||
, public EditorEntityContextNotificationBus::Handler
|
||||
, public AzToolsFramework::Prefab::PrefabPublicNotificationBus::Handler
|
||||
{
|
||||
friend class JsonEditorEntitySortComponentSerializer;
|
||||
|
||||
public:
|
||||
AZ_COMPONENT(EditorEntitySortComponent, "{6EA1E03D-68B2-466D-97F7-83998C8C27F0}", EditorComponentBase);
|
||||
|
||||
@@ -45,6 +49,9 @@ namespace AzToolsFramework
|
||||
// EditorEntityContextNotificationBus::Handler
|
||||
void OnEntityStreamLoadSuccess() override;
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void OnPrefabInstancePropagationBegin() override;
|
||||
void OnPrefabInstancePropagationEnd() override;
|
||||
private:
|
||||
void MarkDirtyAndSendChangedEvent();
|
||||
bool AddChildEntityInternal(const AZ::EntityId& entityId, bool addToBack, EntityOrderArray::iterator insertPosition);
|
||||
@@ -59,6 +66,8 @@ namespace AzToolsFramework
|
||||
void PrepareSave();
|
||||
void PostLoad();
|
||||
|
||||
void SanitizeOrderEntryArray();
|
||||
|
||||
class EntitySortSerializationEvents
|
||||
: public AZ::SerializeContext::IEventHandler
|
||||
{
|
||||
@@ -106,6 +115,8 @@ namespace AzToolsFramework
|
||||
EntityOrderCache m_childEntityOrderCache; ///< The map of entity id to index for quick look up
|
||||
|
||||
bool m_entityOrderIsDirty = true; ///< This flag indicates our stored serialization order data is out of date and must be rebuilt before serialization occurs
|
||||
bool m_ignoreIncomingOrderChanges = false; ///< This is set when prefab propagation occurs so that non-authored order changes can be ignored
|
||||
bool m_shouldSanityCheckStateAfterPropagation = false; //< This is set after activation, to queue a cleanup of any invalid state after the next prefab propagation.
|
||||
};
|
||||
}
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
+137
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/Serialization/Json/JsonSerializationResult.h>
|
||||
#include <AzCore/std/sort.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntitySortComponent.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntitySortComponentSerializer.h>
|
||||
|
||||
namespace AzToolsFramework::Components
|
||||
{
|
||||
AZ_CLASS_ALLOCATOR_IMPL(JsonEditorEntitySortComponentSerializer, AZ::SystemAllocator, 0);
|
||||
|
||||
AZ::JsonSerializationResult::Result JsonEditorEntitySortComponentSerializer::Load(
|
||||
void* outputValue,
|
||||
[[maybe_unused]] const AZ::Uuid& outputValueTypeId,
|
||||
const rapidjson::Value& inputValue,
|
||||
AZ::JsonDeserializerContext& context)
|
||||
{
|
||||
namespace JSR = AZ::JsonSerializationResult;
|
||||
|
||||
AZ_Assert(
|
||||
azrtti_typeid<EditorEntitySortComponent>() == outputValueTypeId,
|
||||
"Unable to deserialize EditorEntitySortComponent from json because the provided type is %s.",
|
||||
outputValueTypeId.ToString<AZStd::string>().c_str());
|
||||
|
||||
EditorEntitySortComponent* sortComponentInstance = reinterpret_cast<EditorEntitySortComponent*>(outputValue);
|
||||
AZ_Assert(sortComponentInstance, "Output value for JsonEditorEntitySortComponentSerializer can't be null.");
|
||||
|
||||
JSR::ResultCode result(JSR::Tasks::ReadField);
|
||||
{
|
||||
JSR::ResultCode componentIdLoadResult = ContinueLoadingFromJsonObjectField(
|
||||
&sortComponentInstance->m_id, azrtti_typeid<decltype(sortComponentInstance->m_id)>(), inputValue,
|
||||
"Id", context);
|
||||
|
||||
result.Combine(componentIdLoadResult);
|
||||
}
|
||||
|
||||
{
|
||||
sortComponentInstance->m_childEntityOrderArray.clear();
|
||||
JSR::ResultCode enryLoadResult = ContinueLoadingFromJsonObjectField(
|
||||
&sortComponentInstance->m_childEntityOrderArray,
|
||||
azrtti_typeid<decltype(sortComponentInstance->m_childEntityOrderArray)>(), inputValue, "Child Entity Order",
|
||||
context);
|
||||
|
||||
// Migrate ChildEntityOrderEntryArray -> ChildEntityOrderArray
|
||||
if (sortComponentInstance->m_childEntityOrderArray.empty())
|
||||
{
|
||||
enryLoadResult = ContinueLoadingFromJsonObjectField(
|
||||
&sortComponentInstance->m_childEntityOrderEntryArray,
|
||||
azrtti_typeid<decltype(sortComponentInstance->m_childEntityOrderEntryArray)>(), inputValue,
|
||||
"ChildEntityOrderEntryArray", context);
|
||||
|
||||
AZStd::sort(
|
||||
sortComponentInstance->m_childEntityOrderEntryArray.begin(),
|
||||
sortComponentInstance->m_childEntityOrderEntryArray.end(),
|
||||
[](const EditorEntitySortComponent::EntityOrderEntry& lhs,
|
||||
const EditorEntitySortComponent::EntityOrderEntry& rhs) -> bool
|
||||
{
|
||||
return lhs.m_sortIndex < rhs.m_sortIndex;
|
||||
});
|
||||
|
||||
// Sort by index and copy to the order array, any duplicates or invalid entries will be cleaned up by the sanitization pass
|
||||
sortComponentInstance->m_childEntityOrderArray.resize(sortComponentInstance->m_childEntityOrderEntryArray.size());
|
||||
for (size_t i = 0; i < sortComponentInstance->m_childEntityOrderEntryArray.size(); ++i)
|
||||
{
|
||||
sortComponentInstance->m_childEntityOrderArray[i] = sortComponentInstance->m_childEntityOrderEntryArray[i].m_entityId;
|
||||
}
|
||||
}
|
||||
|
||||
sortComponentInstance->RebuildEntityOrderCache();
|
||||
|
||||
result.Combine(enryLoadResult);
|
||||
}
|
||||
|
||||
return context.Report(
|
||||
result,
|
||||
result.GetProcessing() != JSR::Processing::Halted ? "Successfully loaded EditorEntitySortComponent information."
|
||||
: "Failed to load EditorEntitySortComponent information.");
|
||||
}
|
||||
|
||||
AZ::JsonSerializationResult::Result JsonEditorEntitySortComponentSerializer::Store(
|
||||
rapidjson::Value& outputValue,
|
||||
const void* inputValue,
|
||||
const void* defaultValue,
|
||||
[[maybe_unused]] const AZ::Uuid& valueTypeId,
|
||||
AZ::JsonSerializerContext& context)
|
||||
{
|
||||
namespace JSR = AZ::JsonSerializationResult;
|
||||
|
||||
AZ_Assert(
|
||||
azrtti_typeid<EditorEntitySortComponent>() == valueTypeId,
|
||||
"Unable to Serialize EditorEntitySortComponent because the provided type is %s.",
|
||||
valueTypeId.ToString<AZStd::string>().c_str());
|
||||
|
||||
const EditorEntitySortComponent* sortComponentInstance = reinterpret_cast<const EditorEntitySortComponent*>(inputValue);
|
||||
AZ_Assert(sortComponentInstance, "Input value for JsonEditorEntitySortComponentSerializer can't be null.");
|
||||
const EditorEntitySortComponent* defaultsortComponentInstance =
|
||||
reinterpret_cast<const EditorEntitySortComponent*>(defaultValue);
|
||||
|
||||
JSR::ResultCode result(JSR::Tasks::WriteValue);
|
||||
{
|
||||
AZ::ScopedContextPath subPathName(context, "m_id");
|
||||
const AZ::ComponentId* componentId = &sortComponentInstance->m_id;
|
||||
const AZ::ComponentId* defaultComponentId =
|
||||
defaultsortComponentInstance ? &defaultsortComponentInstance->m_id : nullptr;
|
||||
|
||||
JSR::ResultCode resultComponentId = ContinueStoringToJsonObjectField(
|
||||
outputValue, "Id", componentId, defaultComponentId, azrtti_typeid<decltype(sortComponentInstance->m_id)>(),
|
||||
context);
|
||||
|
||||
result.Combine(resultComponentId);
|
||||
}
|
||||
|
||||
{
|
||||
AZ::ScopedContextPath subPathName(context, "m_childEntityOrderArray");
|
||||
const EntityOrderArray* childEntityOrderArray = &sortComponentInstance->m_childEntityOrderArray;
|
||||
const EntityOrderArray* defaultChildEntityOrderArray =
|
||||
defaultsortComponentInstance ? &defaultsortComponentInstance->m_childEntityOrderArray : nullptr;
|
||||
|
||||
JSR::ResultCode resultParentEntityId = ContinueStoringToJsonObjectField(
|
||||
outputValue, "Child Entity Order", childEntityOrderArray, defaultChildEntityOrderArray,
|
||||
azrtti_typeid<decltype(sortComponentInstance->m_childEntityOrderArray)>(), context);
|
||||
|
||||
result.Combine(resultParentEntityId);
|
||||
}
|
||||
|
||||
return context.Report(
|
||||
result,
|
||||
result.GetProcessing() != JSR::Processing::Halted ? "Successfully stored EditorEntitySortComponent information."
|
||||
: "Failed to store EditorEntitySortComponent information.");
|
||||
}
|
||||
} // namespace AzToolsFramework::Components
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Memory/Memory.h>
|
||||
#include <AzCore/Serialization/Json/BaseJsonSerializer.h>
|
||||
|
||||
namespace AzToolsFramework::Components
|
||||
{
|
||||
class JsonEditorEntitySortComponentSerializer
|
||||
: public AZ::BaseJsonSerializer
|
||||
{
|
||||
public:
|
||||
AZ_RTTI(JsonEditorEntitySortComponentSerializer, "{5104782E-B34F-4D87-B1DF-BDFB1AF20D58}", BaseJsonSerializer);
|
||||
AZ_CLASS_ALLOCATOR_DECL;
|
||||
|
||||
AZ::JsonSerializationResult::Result Load(
|
||||
void* outputValue, const AZ::Uuid& outputValueTypeId, const rapidjson::Value& inputValue,
|
||||
AZ::JsonDeserializerContext& context) override;
|
||||
|
||||
AZ::JsonSerializationResult::Result Store(
|
||||
rapidjson::Value& outputValue, const void* inputValue, const void* defaultValue, const AZ::Uuid& valueTypeId,
|
||||
AZ::JsonSerializerContext& context) override;
|
||||
};
|
||||
} // namespace AzToolsFramework::Components
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Component/EntityId.h>
|
||||
#include <AzCore/EBus/EBus.h>
|
||||
|
||||
#include <AzFramework/Entity/EntityContext.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
//! Used to notify changes of state for read-only entities.
|
||||
class ReadOnlyEntityPublicNotifications
|
||||
: public AZ::EBusTraits
|
||||
{
|
||||
public:
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// EBusTraits overrides
|
||||
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
|
||||
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
|
||||
using BusIdType = AzFramework::EntityContextId;
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//! Triggered when an entity's read-only status changes.
|
||||
//! @param entityId The entity whose status has changed.
|
||||
//! @param readOnly The read-only state the container was changed to.
|
||||
virtual void OnReadOnlyEntityStatusChanged([[maybe_unused]] const AZ::EntityId& entityId, [[maybe_unused]] bool readOnly) {}
|
||||
|
||||
protected:
|
||||
~ReadOnlyEntityPublicNotifications() = default;
|
||||
};
|
||||
using ReadOnlyEntityPublicNotificationBus = AZ::EBus<ReadOnlyEntityPublicNotifications>;
|
||||
|
||||
//! Used by the ReadOnlyEntitySystemComponent to query the read-only state of entities as set by systems using the API.
|
||||
class ReadOnlyEntityQueryRequests
|
||||
: public AZ::EBusTraits
|
||||
{
|
||||
public:
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// EBusTraits overrides
|
||||
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
|
||||
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
|
||||
using BusIdType = AzFramework::EntityContextId;
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//! Triggered when an entity's read-only status is queried.
|
||||
//! Allows multiple systems to weigh in on the read-only status of an entity.
|
||||
//! @param entityId The entity whose status has changed.
|
||||
//! @param[out] isReadOnly The output of the query. Should only be changed to true, and left untouched if false.
|
||||
virtual void IsReadOnly(const AZ::EntityId& entityId, bool& isReadOnly) = 0;
|
||||
|
||||
protected:
|
||||
~ReadOnlyEntityQueryRequests() = default;
|
||||
};
|
||||
using ReadOnlyEntityQueryRequestBus = AZ::EBus<ReadOnlyEntityQueryRequests>;
|
||||
|
||||
} // namespace AzToolsFramework
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
|
||||
#include <AzFramework/Entity/EntityContextBus.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
//! An entity registered as read-only cannot be altered in the editor.
|
||||
class ReadOnlyEntityPublicInterface
|
||||
{
|
||||
public:
|
||||
AZ_RTTI(ReadOnlyEntityPublicInterface, "{921FE15B-6EBD-47F0-8238-BC63318DEDEA}");
|
||||
|
||||
//! Returns whether the entity id provided is registered as read-only.
|
||||
virtual bool IsReadOnly(const AZ::EntityId& entityId) = 0;
|
||||
};
|
||||
|
||||
//! An entity registered as read-only cannot be altered in the editor.
|
||||
class ReadOnlyEntityQueryInterface
|
||||
{
|
||||
public:
|
||||
AZ_RTTI(ReadOnlyEntityQueryInterface, "{2ACD63C5-1F3E-4DE8-880E-8115F857D329}");
|
||||
|
||||
//! Refreshes the cached read-only status for the entities provided.
|
||||
//! @param entityIds The entityIds whose read-only state will be queried again.
|
||||
virtual void RefreshReadOnlyState(const EntityIdList& entityIds) = 0;
|
||||
|
||||
//! Refreshes the cached read-only status for all entities.
|
||||
//! Useful when disconnecting a handler at runtime.
|
||||
virtual void RefreshReadOnlyStateForAllEntities() = 0;
|
||||
};
|
||||
|
||||
} // namespace AzToolsFramework
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzToolsFramework/Entity/ReadOnly/ReadOnlyEntitySystemComponent.h>
|
||||
|
||||
#include <AzToolsFramework/Entity/ReadOnly/ReadOnlyEntityBus.h>
|
||||
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
void ReadOnlyEntitySystemComponent::Activate()
|
||||
{
|
||||
AZ::Interface<ReadOnlyEntityQueryInterface>::Register(this);
|
||||
AZ::Interface<ReadOnlyEntityPublicInterface>::Register(this);
|
||||
EditorEntityContextNotificationBus::Handler::BusConnect();
|
||||
}
|
||||
|
||||
void ReadOnlyEntitySystemComponent::Deactivate()
|
||||
{
|
||||
EditorEntityContextNotificationBus::Handler::BusDisconnect();
|
||||
AZ::Interface<ReadOnlyEntityPublicInterface>::Unregister(this);
|
||||
AZ::Interface<ReadOnlyEntityQueryInterface>::Unregister(this);
|
||||
}
|
||||
|
||||
void ReadOnlyEntitySystemComponent::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
|
||||
{
|
||||
serializeContext->Class<ReadOnlyEntitySystemComponent, AZ::Component>()->Version(1);
|
||||
}
|
||||
}
|
||||
|
||||
void ReadOnlyEntitySystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
|
||||
{
|
||||
provided.push_back(AZ_CRC_CE("ReadOnlyEntityService"));
|
||||
}
|
||||
|
||||
bool ReadOnlyEntitySystemComponent::IsReadOnly(const AZ::EntityId& entityId)
|
||||
{
|
||||
if (!m_readOnlystates.contains(entityId))
|
||||
{
|
||||
QueryReadOnlyStateForEntity(entityId);
|
||||
}
|
||||
|
||||
return m_readOnlystates[entityId];
|
||||
}
|
||||
|
||||
void ReadOnlyEntitySystemComponent::RefreshReadOnlyState(const EntityIdList& entityIds)
|
||||
{
|
||||
for (const AZ::EntityId entityId : entityIds)
|
||||
{
|
||||
bool wasReadOnly = m_readOnlystates[entityId];
|
||||
QueryReadOnlyStateForEntity(entityId);
|
||||
|
||||
if (bool isReadOnly = m_readOnlystates[entityId]; wasReadOnly != isReadOnly)
|
||||
{
|
||||
ReadOnlyEntityPublicNotificationBus::Broadcast(
|
||||
&ReadOnlyEntityPublicNotificationBus::Events::OnReadOnlyEntityStatusChanged, entityId, isReadOnly);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ReadOnlyEntitySystemComponent::RefreshReadOnlyStateForAllEntities()
|
||||
{
|
||||
for (auto elem : m_readOnlystates)
|
||||
{
|
||||
AZ::EntityId entityId = elem.first;
|
||||
bool wasReadOnly = m_readOnlystates[entityId];
|
||||
QueryReadOnlyStateForEntity(entityId);
|
||||
|
||||
if (bool isReadOnly = m_readOnlystates[entityId]; wasReadOnly != isReadOnly)
|
||||
{
|
||||
ReadOnlyEntityPublicNotificationBus::Broadcast(
|
||||
&ReadOnlyEntityPublicNotificationBus::Events::OnReadOnlyEntityStatusChanged, entityId, isReadOnly);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ReadOnlyEntitySystemComponent::OnContextReset()
|
||||
{
|
||||
m_readOnlystates.clear();
|
||||
}
|
||||
|
||||
void ReadOnlyEntitySystemComponent::QueryReadOnlyStateForEntity(const AZ::EntityId& entityId)
|
||||
{
|
||||
bool isReadOnly = false;
|
||||
|
||||
ReadOnlyEntityQueryRequestBus::Broadcast(
|
||||
&ReadOnlyEntityQueryRequestBus::Events::IsReadOnly, entityId, isReadOnly);
|
||||
|
||||
m_readOnlystates[entityId] = isReadOnly;
|
||||
}
|
||||
|
||||
} // namespace AzToolsFramework
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Component/Component.h>
|
||||
#include <AzCore/Memory/SystemAllocator.h>
|
||||
|
||||
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
|
||||
#include <AzToolsFramework/Entity/ReadOnly/ReadOnlyEntityInterface.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
//! System Component to track read-only entity registration.
|
||||
//! An entity registered as ReadOnly cannot be altered in the Editor.
|
||||
class ReadOnlyEntitySystemComponent final
|
||||
: public AZ::Component
|
||||
, private ReadOnlyEntityPublicInterface
|
||||
, private ReadOnlyEntityQueryInterface
|
||||
, private EditorEntityContextNotificationBus::Handler
|
||||
{
|
||||
public:
|
||||
AZ_COMPONENT(ReadOnlyEntitySystemComponent, "{B32EB03F-D88F-4B3A-9C16-071AF04DA646}");
|
||||
|
||||
ReadOnlyEntitySystemComponent() = default;
|
||||
virtual ~ReadOnlyEntitySystemComponent() = default;
|
||||
|
||||
// AZ::Component overrides ...
|
||||
void Activate() override;
|
||||
void Deactivate() override;
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided);
|
||||
|
||||
// ReadOnlyEntityPublicNotifications overrides ...
|
||||
bool IsReadOnly(const AZ::EntityId& entityId) override;
|
||||
|
||||
// ReadOnlyEntityQueryInterface overrides ...
|
||||
void RefreshReadOnlyState(const EntityIdList& entityIds) override;
|
||||
void RefreshReadOnlyStateForAllEntities() override;
|
||||
|
||||
// EditorEntityContextNotificationBus overrides ...
|
||||
void OnContextReset() override;
|
||||
|
||||
private:
|
||||
void QueryReadOnlyStateForEntity(const AZ::EntityId& entityId);
|
||||
|
||||
AZStd::unordered_map<AZ::EntityId, bool> m_readOnlystates;
|
||||
};
|
||||
|
||||
} // namespace AzToolsFramework
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzToolsFramework/Input/QtEventToAzInputManager.h>
|
||||
#include <AzToolsFramework/Input/QtEventToAzInputMapper.h>
|
||||
|
||||
#include <AzCore/std/smart_ptr/make_shared.h>
|
||||
|
||||
+9
-15
@@ -84,12 +84,12 @@ namespace AzToolsFramework
|
||||
break;
|
||||
case State::Translating:
|
||||
{
|
||||
if (mouseInteraction.m_mouseInteraction.m_mouseButtons.Left() &&
|
||||
if (mouseInteraction.m_mouseInteraction.m_mouseButtons.Middle() &&
|
||||
mouseInteraction.m_mouseEvent == ViewportInteraction::MouseEvent::Down &&
|
||||
mouseInteraction.m_mouseInteraction.m_keyboardModifiers.Shift() &&
|
||||
mouseInteraction.m_mouseInteraction.m_keyboardModifiers.Ctrl())
|
||||
{
|
||||
SnapVerticesToTerrain(mouseInteraction);
|
||||
SnapVerticesToSurface(mouseInteraction);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -109,29 +109,23 @@ namespace AzToolsFramework
|
||||
}
|
||||
|
||||
template<typename Vertex>
|
||||
void EditorVertexSelectionBase<Vertex>::SnapVerticesToTerrain(const ViewportInteraction::MouseInteractionEvent& mouseInteraction)
|
||||
void EditorVertexSelectionBase<Vertex>::SnapVerticesToSurface(const ViewportInteraction::MouseInteractionEvent& mouseInteraction)
|
||||
{
|
||||
ScopedUndoBatch surfaceSnapUndo("Snap to Surface");
|
||||
ScopedUndoBatch::MarkEntityDirty(GetEntityId());
|
||||
|
||||
const int viewportId = mouseInteraction.m_mouseInteraction.m_interactionId.m_viewportId;
|
||||
// get unsnapped terrain position (world space)
|
||||
AZ::Vector3 worldSurfacePosition = AZ::Vector3::CreateZero();
|
||||
;
|
||||
ViewportInteraction::MainEditorViewportInteractionRequestBus::EventResult(
|
||||
worldSurfacePosition, viewportId, &ViewportInteraction::MainEditorViewportInteractionRequestBus::Events::PickTerrain,
|
||||
mouseInteraction.m_mouseInteraction.m_mousePick.m_screenCoordinates);
|
||||
// get unsnapped surface position (world space)
|
||||
const AZ::Vector3 worldSurfacePosition = FindClosestPickIntersection(
|
||||
viewportId, mouseInteraction.m_mouseInteraction.m_mousePick.m_screenCoordinates, EditorPickRayLength,
|
||||
GetDefaultEntityPlacementDistance());
|
||||
|
||||
AZ::Transform worldFromLocal;
|
||||
AZ::TransformBus::EventResult(worldFromLocal, GetEntityId(), &AZ::TransformBus::Events::GetWorldTM);
|
||||
const AZ::Transform localFromWorld = worldFromLocal.GetInverse();
|
||||
|
||||
// convert to local space - snap if enabled
|
||||
const GridSnapParameters gridSnapParams = GridSnapSettings(viewportId);
|
||||
const AZ::Vector3 localFinalSurfacePosition = gridSnapParams.m_gridSnap
|
||||
? CalculateSnappedTerrainPosition(worldSurfacePosition, worldFromLocal, viewportId, gridSnapParams.m_gridSize)
|
||||
: localFromWorld.TransformPoint(worldSurfacePosition);
|
||||
|
||||
// convert to local space
|
||||
const AZ::Vector3 localFinalSurfacePosition = localFromWorld.TransformPoint(worldSurfacePosition);
|
||||
SetSelectedPosition(localFinalSurfacePosition);
|
||||
|
||||
OnEntityComponentPropertyChanged(GetEntityComponentIdPair());
|
||||
|
||||
+1
-1
@@ -171,7 +171,7 @@ namespace AzToolsFramework
|
||||
//! Snap the selected vertices to the terrain.
|
||||
//! Note: With a multi-selection the manipulator will be translated to the picked
|
||||
//! terrain position with all vertices moved relative to it.
|
||||
void SnapVerticesToTerrain(const ViewportInteraction::MouseInteractionEvent& mouseInteraction);
|
||||
void SnapVerticesToSurface(const ViewportInteraction::MouseInteractionEvent& mouseInteraction);
|
||||
|
||||
//! The Actions provided by the EditorVertexSelection while it is active.
|
||||
//! e.g. Vertex deletion, duplication etc.
|
||||
|
||||
@@ -110,30 +110,6 @@ namespace AzToolsFramework
|
||||
return unsnappedPosition + CalculateSnappedOffset(unsnappedPosition, snapAxes, snapAxesCount, size);
|
||||
}
|
||||
|
||||
AZ::Vector3 CalculateSnappedTerrainPosition(
|
||||
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(), size) +
|
||||
CalculateSnappedOffset(localSurfacePosition, AZ::Vector3::CreateAxisY(), size);
|
||||
|
||||
// find terrain height at xy snapped location
|
||||
float terrainHeight = 0.0f;
|
||||
ViewportInteraction::MainEditorViewportInteractionRequestBus::EventResult(
|
||||
terrainHeight, viewportId, &ViewportInteraction::MainEditorViewportInteractionRequestBus::Events::TerrainHeight,
|
||||
Vector3ToVector2(worldFromLocal.TransformPoint(localSnappedSurfacePosition)));
|
||||
|
||||
// set snapped z value to terrain height
|
||||
AZ::Vector3 localTerrainHeight = localFromWorld.TransformPoint(AZ::Vector3(0.0f, 0.0f, terrainHeight));
|
||||
localSnappedSurfacePosition.SetZ(localTerrainHeight.GetZ());
|
||||
|
||||
return localSnappedSurfacePosition;
|
||||
}
|
||||
|
||||
bool GridSnapping(const int viewportId)
|
||||
{
|
||||
bool snapping = false;
|
||||
|
||||
@@ -63,11 +63,6 @@ namespace AzToolsFramework
|
||||
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 size);
|
||||
|
||||
//! Wrapper for grid snapping and grid size bus calls.
|
||||
GridSnapParameters GridSnapSettings(int viewportId);
|
||||
|
||||
|
||||
+51
-53
@@ -10,6 +10,7 @@
|
||||
|
||||
#include <AzToolsFramework/Manipulators/ManipulatorSnapping.h>
|
||||
#include <AzToolsFramework/Manipulators/ManipulatorView.h>
|
||||
#include <AzToolsFramework/ViewportSelection/EditorSelectionUtil.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
@@ -17,28 +18,16 @@ namespace AzToolsFramework
|
||||
const AZ::Transform& worldFromLocal,
|
||||
const AZ::Vector3& worldSurfacePosition,
|
||||
const AZ::Vector3& localStartPosition,
|
||||
const bool snapping,
|
||||
const float gridSize,
|
||||
const int viewportId)
|
||||
[[maybe_unused]] const bool snapping,
|
||||
[[maybe_unused]] const float gridSize,
|
||||
[[maybe_unused]] const int viewportId)
|
||||
{
|
||||
const AZ::Transform worldFromLocalUniform = AzToolsFramework::TransformUniformScale(worldFromLocal);
|
||||
const AZ::Transform localFromWorldUniform = worldFromLocalUniform.GetInverse();
|
||||
|
||||
const AZ::Vector3 localFinalSurfacePosition = snapping
|
||||
// note: gridSize is not scaled by scaleRecip here as localStartPosition is
|
||||
// unscaled itself so the position returned by CalculateSnappedTerrainPosition
|
||||
// must be in the same space (if localStartPosition were also scaled, gridSize
|
||||
// would need to be multiplied by scaleRecip)
|
||||
? CalculateSnappedTerrainPosition(worldSurfacePosition, worldFromLocalUniform, viewportId, gridSize)
|
||||
: localFromWorldUniform.TransformPoint(worldSurfacePosition);
|
||||
|
||||
// delta/offset between initial vertex position and terrain pick position
|
||||
const AZ::Vector3 localSurfaceOffset = localFinalSurfacePosition - localStartPosition;
|
||||
const AZ::Transform localFromWorld = worldFromLocal.GetInverse();
|
||||
|
||||
StartInternal startInternal;
|
||||
startInternal.m_snapOffset = localSurfaceOffset;
|
||||
startInternal.m_localPosition = localStartPosition + localSurfaceOffset;
|
||||
startInternal.m_localHitPosition = localFromWorldUniform.TransformVector(worldSurfacePosition);
|
||||
startInternal.m_snapOffset = AZ::Vector3::CreateZero();
|
||||
startInternal.m_localPosition = localStartPosition;
|
||||
startInternal.m_localHitPosition = localFromWorld.TransformPoint(worldSurfacePosition);
|
||||
return startInternal;
|
||||
}
|
||||
|
||||
@@ -46,26 +35,19 @@ namespace AzToolsFramework
|
||||
const StartInternal& startInternal,
|
||||
const AZ::Transform& worldFromLocal,
|
||||
const AZ::Vector3& worldSurfacePosition,
|
||||
const bool snapping,
|
||||
const float gridSize,
|
||||
[[maybe_unused]] const bool snapping,
|
||||
[[maybe_unused]] const float gridSize,
|
||||
const ViewportInteraction::KeyboardModifiers keyboardModifiers,
|
||||
const int viewportId)
|
||||
[[maybe_unused]] const int viewportId)
|
||||
{
|
||||
const AZ::Transform worldFromLocalUniform = AzToolsFramework::TransformUniformScale(worldFromLocal);
|
||||
const AZ::Transform localFromWorldUniform = worldFromLocalUniform.GetInverse();
|
||||
|
||||
const float scaleRecip = ScaleReciprocal(worldFromLocalUniform);
|
||||
|
||||
const AZ::Vector3 localFinalSurfacePosition = snapping
|
||||
? CalculateSnappedTerrainPosition(worldSurfacePosition, worldFromLocalUniform, viewportId, gridSize * scaleRecip)
|
||||
: localFromWorldUniform.TransformPoint(worldSurfacePosition);
|
||||
const AZ::Transform localFromWorld = worldFromLocal.GetInverse();
|
||||
const AZ::Vector3 localFinalSurfacePosition = localFromWorld.TransformPoint(worldSurfacePosition);
|
||||
|
||||
Action action;
|
||||
action.m_start.m_localPosition = startInternal.m_localPosition;
|
||||
action.m_start.m_snapOffset = startInternal.m_snapOffset;
|
||||
action.m_start.m_snapOffset = AZ::Vector3::CreateZero();
|
||||
action.m_current.m_localOffset = localFinalSurfacePosition - startInternal.m_localPosition;
|
||||
// record what modifier keys are held during this action
|
||||
action.m_modifiers = keyboardModifiers;
|
||||
action.m_modifiers = keyboardModifiers; // record what modifier keys are held during this action
|
||||
return action;
|
||||
}
|
||||
|
||||
@@ -78,12 +60,16 @@ namespace AzToolsFramework
|
||||
{
|
||||
SetSpace(worldFromLocal);
|
||||
AttachLeftMouseDownImpl();
|
||||
|
||||
// only cast rays against objects (entities/meshes etc.) we can actually see
|
||||
m_rayRequest.m_onlyVisible = true;
|
||||
}
|
||||
|
||||
void SurfaceManipulator::InstallLeftMouseDownCallback(const MouseActionCallback& onMouseDownCallback)
|
||||
{
|
||||
m_onLeftMouseDownCallback = onMouseDownCallback;
|
||||
}
|
||||
|
||||
void SurfaceManipulator::InstallLeftMouseUpCallback(const MouseActionCallback& onMouseUpCallback)
|
||||
{
|
||||
m_onLeftMouseUpCallback = onMouseUpCallback;
|
||||
@@ -95,17 +81,30 @@ namespace AzToolsFramework
|
||||
}
|
||||
|
||||
void SurfaceManipulator::OnLeftMouseDownImpl(
|
||||
const ViewportInteraction::MouseInteraction& interaction, float /*rayIntersectionDistance*/)
|
||||
const ViewportInteraction::MouseInteraction& interaction, [[maybe_unused]] float rayIntersectionDistance)
|
||||
{
|
||||
const AZ::Transform worldFromLocalUniformScale = TransformUniformScale(GetSpace());
|
||||
|
||||
const GridSnapParameters gridSnapParams = GridSnapSettings(interaction.m_interactionId.m_viewportId);
|
||||
const AzFramework::ViewportId viewportId = interaction.m_interactionId.m_viewportId;
|
||||
|
||||
AZ::Vector3 worldSurfacePosition;
|
||||
ViewportInteraction::MainEditorViewportInteractionRequestBus::EventResult(
|
||||
worldSurfacePosition, interaction.m_interactionId.m_viewportId,
|
||||
&ViewportInteraction::MainEditorViewportInteractionRequestBus::Events::PickTerrain,
|
||||
interaction.m_mousePick.m_screenCoordinates);
|
||||
const auto& entityComponentIdPairs = EntityComponentIdPairs();
|
||||
m_rayRequest.m_entityFilter.m_ignoreEntities.clear();
|
||||
m_rayRequest.m_entityFilter.m_ignoreEntities.reserve(entityComponentIdPairs.size());
|
||||
AZStd::transform(
|
||||
entityComponentIdPairs.begin(), entityComponentIdPairs.end(),
|
||||
AZStd::inserter(m_rayRequest.m_entityFilter.m_ignoreEntities, m_rayRequest.m_entityFilter.m_ignoreEntities.begin()),
|
||||
[](const AZ::EntityComponentIdPair& entityComponentIdPair)
|
||||
{
|
||||
return entityComponentIdPair.GetEntityId();
|
||||
});
|
||||
|
||||
// calculate the start and end of the ray
|
||||
RefreshRayRequest(
|
||||
m_rayRequest, ViewportInteraction::ViewportScreenToWorldRay(viewportId, interaction.m_mousePick.m_screenCoordinates),
|
||||
EditorPickRayLength);
|
||||
|
||||
const GridSnapParameters gridSnapParams = GridSnapSettings(viewportId);
|
||||
const AZ::Vector3 worldSurfacePosition = FindClosestPickIntersection(m_rayRequest, GetDefaultEntityPlacementDistance());
|
||||
|
||||
m_startInternal = CalculateManipulationDataStart(
|
||||
worldFromLocalUniformScale, worldSurfacePosition, GetLocalPosition(), gridSnapParams.m_gridSnap, gridSnapParams.m_gridSize,
|
||||
@@ -123,17 +122,14 @@ namespace AzToolsFramework
|
||||
{
|
||||
if (m_onLeftMouseUpCallback)
|
||||
{
|
||||
AZ::Vector3 worldSurfacePosition = AZ::Vector3::CreateZero();
|
||||
ViewportInteraction::MainEditorViewportInteractionRequestBus::EventResult(
|
||||
worldSurfacePosition, interaction.m_interactionId.m_viewportId,
|
||||
&ViewportInteraction::MainEditorViewportInteractionRequestBus::Events::PickTerrain,
|
||||
interaction.m_mousePick.m_screenCoordinates);
|
||||
const AzFramework::ViewportId viewportId = interaction.m_interactionId.m_viewportId;
|
||||
|
||||
const GridSnapParameters gridSnapParams = GridSnapSettings(interaction.m_interactionId.m_viewportId);
|
||||
const GridSnapParameters gridSnapParams = GridSnapSettings(viewportId);
|
||||
const AZ::Vector3 worldSurfacePosition = FindClosestPickIntersection(m_rayRequest, GetDefaultEntityPlacementDistance());
|
||||
|
||||
m_onLeftMouseUpCallback(CalculateManipulationDataAction(
|
||||
m_startInternal, TransformUniformScale(GetSpace()), worldSurfacePosition, gridSnapParams.m_gridSnap,
|
||||
gridSnapParams.m_gridSize, interaction.m_keyboardModifiers, interaction.m_interactionId.m_viewportId));
|
||||
gridSnapParams.m_gridSize, interaction.m_keyboardModifiers, viewportId));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,13 +137,15 @@ namespace AzToolsFramework
|
||||
{
|
||||
if (m_onMouseMoveCallback)
|
||||
{
|
||||
AZ::Vector3 worldSurfacePosition = AZ::Vector3::CreateZero();
|
||||
ViewportInteraction::MainEditorViewportInteractionRequestBus::EventResult(
|
||||
worldSurfacePosition, interaction.m_interactionId.m_viewportId,
|
||||
&ViewportInteraction::MainEditorViewportInteractionRequestBus::Events::PickTerrain,
|
||||
interaction.m_mousePick.m_screenCoordinates);
|
||||
const AzFramework::ViewportId viewportId = interaction.m_interactionId.m_viewportId;
|
||||
|
||||
const GridSnapParameters gridSnapParams = GridSnapSettings(interaction.m_interactionId.m_viewportId);
|
||||
// update the start and end of the ray
|
||||
RefreshRayRequest(
|
||||
m_rayRequest, ViewportInteraction::ViewportScreenToWorldRay(viewportId, interaction.m_mousePick.m_screenCoordinates),
|
||||
EditorPickRayLength);
|
||||
|
||||
const GridSnapParameters gridSnapParams = GridSnapSettings(viewportId);
|
||||
const AZ::Vector3 worldSurfacePosition = FindClosestPickIntersection(m_rayRequest, GetDefaultEntityPlacementDistance());
|
||||
|
||||
m_onMouseMoveCallback(CalculateManipulationDataAction(
|
||||
m_startInternal, TransformUniformScale(GetSpace()), worldSurfacePosition, gridSnapParams.m_gridSnap,
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "BaseManipulator.h"
|
||||
|
||||
#include <AzCore/Memory/SystemAllocator.h>
|
||||
#include <AzFramework/Render/GeometryIntersectionStructures.h>
|
||||
#include <AzToolsFramework/Viewport/ViewportMessages.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
@@ -58,10 +59,12 @@ namespace AzToolsFramework
|
||||
Start m_start;
|
||||
Current m_current;
|
||||
ViewportInteraction::KeyboardModifiers m_modifiers;
|
||||
|
||||
AZ::Vector3 LocalPosition() const
|
||||
{
|
||||
return m_start.m_localPosition + m_current.m_localOffset;
|
||||
}
|
||||
|
||||
AZ::Vector3 LocalPositionOffset() const
|
||||
{
|
||||
return m_current.m_localOffset;
|
||||
@@ -106,6 +109,9 @@ namespace AzToolsFramework
|
||||
MouseActionCallback m_onLeftMouseUpCallback = nullptr;
|
||||
MouseActionCallback m_onMouseMoveCallback = nullptr;
|
||||
|
||||
//! Cached ray request initialized at mouse down and updated during mouse move.
|
||||
AzFramework::RenderGeometry::RayRequest m_rayRequest;
|
||||
|
||||
static StartInternal CalculateManipulationDataStart(
|
||||
const AZ::Transform& worldFromLocal,
|
||||
const AZ::Vector3& worldSurfacePosition,
|
||||
|
||||
+3
-2
@@ -20,6 +20,7 @@
|
||||
#include <AzToolsFramework/Prefab/PrefabPublicNotificationBus.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabSystemComponentInterface.h>
|
||||
#include <AzToolsFramework/Prefab/Template/Template.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabPublicRequestBus.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
@@ -244,10 +245,10 @@ namespace AzToolsFramework
|
||||
selectedEntityIds.erase(entityIdIterator--);
|
||||
}
|
||||
}
|
||||
ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequests::SetSelectedEntities, selectedEntityIds);
|
||||
|
||||
// Notify Propagation has ended
|
||||
// Notify Propagation has ended, then update selection (which is frozen during propagation, so this order matters)
|
||||
PrefabPublicNotificationBus::Broadcast(&PrefabPublicNotifications::OnPrefabInstancePropagationEnd);
|
||||
ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequests::SetSelectedEntities, selectedEntityIds);
|
||||
}
|
||||
|
||||
m_updatingTemplateInstancesInQueue = false;
|
||||
|
||||
@@ -28,6 +28,9 @@ namespace AzToolsFramework
|
||||
inline static const char* EntityIdName = "Id";
|
||||
inline static const char* EntitiesName = "Entities";
|
||||
inline static const char* ContainerEntityName = "ContainerEntity";
|
||||
inline static const char* ComponentsName = "Components";
|
||||
inline static const char* EntityOrderName = "Child Entity Order";
|
||||
inline static const char* TypeName = "$type";
|
||||
|
||||
/**
|
||||
* Find Prefab value from given parent value and target value's name.
|
||||
|
||||
@@ -34,10 +34,12 @@ namespace AzToolsFramework::Prefab
|
||||
PrefabPublicNotificationBus::Handler::BusConnect();
|
||||
AZ::Interface<PrefabFocusInterface>::Register(this);
|
||||
AZ::Interface<PrefabFocusPublicInterface>::Register(this);
|
||||
PrefabFocusPublicRequestBus::Handler::BusConnect();
|
||||
}
|
||||
|
||||
PrefabFocusHandler::~PrefabFocusHandler()
|
||||
{
|
||||
PrefabFocusPublicRequestBus::Handler::BusDisconnect();
|
||||
AZ::Interface<PrefabFocusPublicInterface>::Unregister(this);
|
||||
AZ::Interface<PrefabFocusInterface>::Unregister(this);
|
||||
PrefabPublicNotificationBus::Handler::BusDisconnect();
|
||||
@@ -45,6 +47,18 @@ namespace AzToolsFramework::Prefab
|
||||
EditorEntityInfoNotificationBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
void PrefabFocusHandler::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context); behaviorContext)
|
||||
{
|
||||
behaviorContext->EBus<PrefabFocusPublicRequestBus>("PrefabFocusPublicRequestBus")
|
||||
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation)
|
||||
->Attribute(AZ::Script::Attributes::Category, "Prefab")
|
||||
->Attribute(AZ::Script::Attributes::Module, "prefab")
|
||||
->Event("FocusOnOwningPrefab", &PrefabFocusPublicInterface::FocusOnOwningPrefab);
|
||||
}
|
||||
}
|
||||
|
||||
void PrefabFocusHandler::InitializeEditorInterfaces()
|
||||
{
|
||||
m_containerEntityInterface = AZ::Interface<ContainerEntityInterface>::Get();
|
||||
@@ -98,7 +112,7 @@ namespace AzToolsFramework::Prefab
|
||||
}
|
||||
|
||||
// Retrieve parent of currently focused prefab.
|
||||
InstanceOptionalReference parentInstance = m_instanceFocusHierarchy[hierarchySize - 2];
|
||||
InstanceOptionalReference parentInstance = GetReferenceFromContainerEntityId(m_instanceFocusHierarchy[hierarchySize - 2]);
|
||||
|
||||
// Use container entity of parent Instance for focus operations.
|
||||
AZ::EntityId entityId = parentInstance->get().GetContainerEntityId();
|
||||
@@ -132,7 +146,7 @@ namespace AzToolsFramework::Prefab
|
||||
return AZ::Failure(AZStd::string("Prefab Focus Handler: Invalid index on FocusOnPathIndex."));
|
||||
}
|
||||
|
||||
InstanceOptionalReference focusedInstance = m_instanceFocusHierarchy[index];
|
||||
InstanceOptionalReference focusedInstance = GetReferenceFromContainerEntityId(m_instanceFocusHierarchy[index]);
|
||||
|
||||
return FocusOnOwningPrefab(focusedInstance->get().GetContainerEntityId());
|
||||
}
|
||||
@@ -172,23 +186,18 @@ namespace AzToolsFramework::Prefab
|
||||
// Close all container entities in the old path.
|
||||
CloseInstanceContainers(m_instanceFocusHierarchy);
|
||||
|
||||
m_focusedInstance = focusedInstance;
|
||||
// Do not store the container for the root instance, use an invalid EntityId instead.
|
||||
m_focusedInstanceContainerEntityId = focusedInstance->get().GetParentInstance().has_value() ? focusedInstance->get().GetContainerEntityId() : AZ::EntityId();
|
||||
m_focusedTemplateId = focusedInstance->get().GetTemplateId();
|
||||
|
||||
AZ::EntityId containerEntityId;
|
||||
|
||||
if (focusedInstance->get().GetParentInstance() != AZStd::nullopt)
|
||||
{
|
||||
containerEntityId = focusedInstance->get().GetContainerEntityId();
|
||||
}
|
||||
else
|
||||
{
|
||||
containerEntityId = AZ::EntityId();
|
||||
}
|
||||
|
||||
// Focus on the descendants of the container entity in the Editor, if the interface is initialized.
|
||||
if (m_focusModeInterface)
|
||||
{
|
||||
const AZ::EntityId containerEntityId =
|
||||
(focusedInstance->get().GetParentInstance() != AZStd::nullopt)
|
||||
? focusedInstance->get().GetContainerEntityId()
|
||||
: AZ::EntityId();
|
||||
|
||||
m_focusModeInterface->SetFocusRoot(containerEntityId);
|
||||
}
|
||||
|
||||
@@ -212,56 +221,65 @@ namespace AzToolsFramework::Prefab
|
||||
InstanceOptionalReference PrefabFocusHandler::GetFocusedPrefabInstance(
|
||||
[[maybe_unused]] AzFramework::EntityContextId entityContextId) const
|
||||
{
|
||||
return m_focusedInstance;
|
||||
return GetReferenceFromContainerEntityId(m_focusedInstanceContainerEntityId);
|
||||
}
|
||||
|
||||
AZ::EntityId PrefabFocusHandler::GetFocusedPrefabContainerEntityId([[maybe_unused]] AzFramework::EntityContextId entityContextId) const
|
||||
{
|
||||
if (!m_focusedInstance.has_value())
|
||||
if (m_focusedInstanceContainerEntityId.IsValid())
|
||||
{
|
||||
// PrefabFocusHandler has not been initialized yet.
|
||||
return AZ::EntityId();
|
||||
return m_focusedInstanceContainerEntityId;
|
||||
}
|
||||
|
||||
return m_focusedInstance->get().GetContainerEntityId();
|
||||
if (auto instance = GetReferenceFromContainerEntityId(m_focusedInstanceContainerEntityId); instance.has_value())
|
||||
{
|
||||
return instance->get().GetContainerEntityId();
|
||||
}
|
||||
|
||||
return AZ::EntityId();
|
||||
}
|
||||
|
||||
bool PrefabFocusHandler::IsOwningPrefabBeingFocused(AZ::EntityId entityId) const
|
||||
{
|
||||
if (!m_focusedInstance.has_value())
|
||||
{
|
||||
// PrefabFocusHandler has not been initialized yet.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!entityId.IsValid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
InstanceOptionalReference instance = m_instanceEntityMapperInterface->FindOwningInstance(entityId);
|
||||
if (!instance.has_value())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return instance.has_value() && (&instance->get() == &m_focusedInstance->get());
|
||||
// If this is owned by the root instance, that corresponds to an invalid m_focusedInstanceContainerEntityId.
|
||||
if (!instance->get().GetParentInstance().has_value())
|
||||
{
|
||||
return !m_focusedInstanceContainerEntityId.IsValid();
|
||||
}
|
||||
|
||||
return (instance->get().GetContainerEntityId() == m_focusedInstanceContainerEntityId);
|
||||
}
|
||||
|
||||
bool PrefabFocusHandler::IsOwningPrefabInFocusHierarchy(AZ::EntityId entityId) const
|
||||
{
|
||||
if (!m_focusedInstance.has_value())
|
||||
{
|
||||
// PrefabFocusHandler has not been initialized yet.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!entityId.IsValid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the focus is on the root, m_focusedInstanceContainerEntityId will be the invalid id.
|
||||
// In those case all entities are in the focus hierarchy and should return true.
|
||||
if (!m_focusedInstanceContainerEntityId.IsValid())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
InstanceOptionalReference instance = m_instanceEntityMapperInterface->FindOwningInstance(entityId);
|
||||
|
||||
while (instance.has_value())
|
||||
{
|
||||
if (&instance->get() == &m_focusedInstance->get())
|
||||
if (instance->get().GetContainerEntityId() == m_focusedInstanceContainerEntityId)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -296,8 +314,9 @@ namespace AzToolsFramework::Prefab
|
||||
// Determine if the entityId is the container for any of the instances in the vector.
|
||||
auto result = AZStd::find_if(
|
||||
m_instanceFocusHierarchy.begin(), m_instanceFocusHierarchy.end(),
|
||||
[entityId](const InstanceOptionalReference& instance)
|
||||
[&, entityId](const AZ::EntityId& containerEntityId)
|
||||
{
|
||||
InstanceOptionalReference instance = GetReferenceFromContainerEntityId(containerEntityId);
|
||||
return (instance->get().GetContainerEntityId() == entityId);
|
||||
}
|
||||
);
|
||||
@@ -322,8 +341,9 @@ namespace AzToolsFramework::Prefab
|
||||
// Determine if the templateId matches any of the instances in the vector.
|
||||
auto result = AZStd::find_if(
|
||||
m_instanceFocusHierarchy.begin(), m_instanceFocusHierarchy.end(),
|
||||
[templateId](const InstanceOptionalReference& instance)
|
||||
[&, templateId](const AZ::EntityId& containerEntityId)
|
||||
{
|
||||
InstanceOptionalReference instance = GetReferenceFromContainerEntityId(containerEntityId);
|
||||
return (instance->get().GetTemplateId() == templateId);
|
||||
}
|
||||
);
|
||||
@@ -342,10 +362,17 @@ namespace AzToolsFramework::Prefab
|
||||
|
||||
AZStd::list<InstanceOptionalReference> instanceFocusList;
|
||||
|
||||
InstanceOptionalReference currentInstance = m_focusedInstance;
|
||||
InstanceOptionalReference currentInstance = GetReferenceFromContainerEntityId(m_focusedInstanceContainerEntityId);
|
||||
while (currentInstance.has_value())
|
||||
{
|
||||
m_instanceFocusHierarchy.emplace_back(currentInstance);
|
||||
if (currentInstance->get().GetParentInstance().has_value())
|
||||
{
|
||||
m_instanceFocusHierarchy.emplace_back(currentInstance->get().GetContainerEntityId());
|
||||
}
|
||||
else
|
||||
{
|
||||
m_instanceFocusHierarchy.emplace_back(AZ::EntityId());
|
||||
}
|
||||
|
||||
currentInstance = currentInstance->get().GetParentInstance();
|
||||
}
|
||||
@@ -363,42 +390,48 @@ namespace AzToolsFramework::Prefab
|
||||
size_t index = 0;
|
||||
size_t maxIndex = m_instanceFocusHierarchy.size() - 1;
|
||||
|
||||
for (const InstanceOptionalReference& instance : m_instanceFocusHierarchy)
|
||||
for (const AZ::EntityId& containerEntityId : m_instanceFocusHierarchy)
|
||||
{
|
||||
AZStd::string prefabName;
|
||||
|
||||
if (index < maxIndex)
|
||||
InstanceOptionalReference instance = GetReferenceFromContainerEntityId(containerEntityId);
|
||||
if (instance.has_value())
|
||||
{
|
||||
// Get the filename without the extension (stem).
|
||||
prefabName = instance->get().GetTemplateSourcePath().Stem().Native();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Get the full filename.
|
||||
prefabName = instance->get().GetTemplateSourcePath().Filename().Native();
|
||||
}
|
||||
AZStd::string prefabName;
|
||||
|
||||
if (prefabSystemComponentInterface->IsTemplateDirty(instance->get().GetTemplateId()))
|
||||
{
|
||||
prefabName += "*";
|
||||
}
|
||||
if (index < maxIndex)
|
||||
{
|
||||
// Get the filename without the extension (stem).
|
||||
prefabName = instance->get().GetTemplateSourcePath().Stem().Native();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Get the full filename.
|
||||
prefabName = instance->get().GetTemplateSourcePath().Filename().Native();
|
||||
}
|
||||
|
||||
m_instanceFocusPath.Append(prefabName);
|
||||
if (prefabSystemComponentInterface->IsTemplateDirty(instance->get().GetTemplateId()))
|
||||
{
|
||||
prefabName += "*";
|
||||
}
|
||||
|
||||
m_instanceFocusPath.Append(prefabName);
|
||||
}
|
||||
|
||||
++index;
|
||||
}
|
||||
}
|
||||
|
||||
void PrefabFocusHandler::OpenInstanceContainers(const AZStd::vector<InstanceOptionalReference>& instances) const
|
||||
void PrefabFocusHandler::OpenInstanceContainers(const AZStd::vector<AZ::EntityId>& instances) const
|
||||
{
|
||||
// If this is called outside the Editor, this interface won't be initialized.
|
||||
if (!m_containerEntityInterface)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (const InstanceOptionalReference& instance : instances)
|
||||
|
||||
for (const AZ::EntityId& containerEntityId : instances)
|
||||
{
|
||||
InstanceOptionalReference instance = GetReferenceFromContainerEntityId(containerEntityId);
|
||||
|
||||
if (instance.has_value())
|
||||
{
|
||||
m_containerEntityInterface->SetContainerOpen(instance->get().GetContainerEntityId(), true);
|
||||
@@ -406,7 +439,7 @@ namespace AzToolsFramework::Prefab
|
||||
}
|
||||
}
|
||||
|
||||
void PrefabFocusHandler::CloseInstanceContainers(const AZStd::vector<InstanceOptionalReference>& instances) const
|
||||
void PrefabFocusHandler::CloseInstanceContainers(const AZStd::vector<AZ::EntityId>& instances) const
|
||||
{
|
||||
// If this is called outside the Editor, this interface won't be initialized.
|
||||
if (!m_containerEntityInterface)
|
||||
@@ -414,8 +447,10 @@ namespace AzToolsFramework::Prefab
|
||||
return;
|
||||
}
|
||||
|
||||
for (const InstanceOptionalReference& instance : instances)
|
||||
for (const AZ::EntityId& containerEntityId : instances)
|
||||
{
|
||||
InstanceOptionalReference instance = GetReferenceFromContainerEntityId(containerEntityId);
|
||||
|
||||
if (instance.has_value())
|
||||
{
|
||||
m_containerEntityInterface->SetContainerOpen(instance->get().GetContainerEntityId(), false);
|
||||
@@ -423,4 +458,22 @@ namespace AzToolsFramework::Prefab
|
||||
}
|
||||
}
|
||||
|
||||
InstanceOptionalReference PrefabFocusHandler::GetReferenceFromContainerEntityId(AZ::EntityId containerEntityId) const
|
||||
{
|
||||
if (!containerEntityId.IsValid())
|
||||
{
|
||||
PrefabEditorEntityOwnershipInterface* prefabEditorEntityOwnershipInterface =
|
||||
AZ::Interface<PrefabEditorEntityOwnershipInterface>::Get();
|
||||
|
||||
if (!prefabEditorEntityOwnershipInterface)
|
||||
{
|
||||
return AZStd::nullopt;
|
||||
}
|
||||
|
||||
return prefabEditorEntityOwnershipInterface->GetRootPrefabInstance();
|
||||
}
|
||||
|
||||
return m_instanceEntityMapperInterface->FindOwningInstance(containerEntityId);
|
||||
}
|
||||
|
||||
} // namespace AzToolsFramework::Prefab
|
||||
|
||||
@@ -30,8 +30,8 @@ namespace AzToolsFramework::Prefab
|
||||
|
||||
//! Handles Prefab Focus mode, determining which prefab file entity changes will target.
|
||||
class PrefabFocusHandler final
|
||||
: private PrefabFocusInterface
|
||||
, private PrefabFocusPublicInterface
|
||||
: public PrefabFocusPublicRequestBus::Handler
|
||||
, private PrefabFocusInterface
|
||||
, private PrefabPublicNotificationBus::Handler
|
||||
, private EditorEntityContextNotificationBus::Handler
|
||||
, private EditorEntityInfoNotificationBus::Handler
|
||||
@@ -42,13 +42,15 @@ namespace AzToolsFramework::Prefab
|
||||
PrefabFocusHandler();
|
||||
~PrefabFocusHandler();
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
// PrefabFocusInterface overrides ...
|
||||
void InitializeEditorInterfaces() override;
|
||||
PrefabFocusOperationResult FocusOnPrefabInstanceOwningEntityId(AZ::EntityId entityId) override;
|
||||
TemplateId GetFocusedPrefabTemplateId(AzFramework::EntityContextId entityContextId) const override;
|
||||
InstanceOptionalReference GetFocusedPrefabInstance(AzFramework::EntityContextId entityContextId) const override;
|
||||
|
||||
// PrefabFocusPublicInterface overrides ...
|
||||
// PrefabFocusPublicInterface and PrefabFocusPublicRequestBus overrides ...
|
||||
PrefabFocusOperationResult FocusOnOwningPrefab(AZ::EntityId entityId) override;
|
||||
PrefabFocusOperationResult FocusOnParentOfFocusedPrefab(AzFramework::EntityContextId entityContextId) override;
|
||||
PrefabFocusOperationResult FocusOnPathIndex(AzFramework::EntityContextId entityContextId, int index) override;
|
||||
@@ -73,16 +75,19 @@ namespace AzToolsFramework::Prefab
|
||||
void RefreshInstanceFocusList();
|
||||
void RefreshInstanceFocusPath();
|
||||
|
||||
void OpenInstanceContainers(const AZStd::vector<InstanceOptionalReference>& instances) const;
|
||||
void CloseInstanceContainers(const AZStd::vector<InstanceOptionalReference>& instances) const;
|
||||
void OpenInstanceContainers(const AZStd::vector<AZ::EntityId>& instances) const;
|
||||
void CloseInstanceContainers(const AZStd::vector<AZ::EntityId>& instances) const;
|
||||
|
||||
//! The instance the editor is currently focusing on.
|
||||
InstanceOptionalReference m_focusedInstance;
|
||||
InstanceOptionalReference GetReferenceFromContainerEntityId(AZ::EntityId containerEntityId) const;
|
||||
|
||||
//! The EntityId of the prefab container entity for the instance the editor is currently focusing on.
|
||||
AZ::EntityId m_focusedInstanceContainerEntityId = AZ::EntityId();
|
||||
//! The templateId of the focused instance.
|
||||
TemplateId m_focusedTemplateId;
|
||||
//! The list of instances going from the root (index 0) to the focused instance.
|
||||
AZStd::vector<InstanceOptionalReference> m_instanceFocusHierarchy;
|
||||
//! A path containing the names of the containers in the instance focus hierarchy, separated with a /.
|
||||
//! The list of instances going from the root (index 0) to the focused instance,
|
||||
//! referenced by their prefab container's EntityId.
|
||||
AZStd::vector<AZ::EntityId> m_instanceFocusHierarchy;
|
||||
//! A path containing the filenames of the instances in the focus hierarchy, separated with a /.
|
||||
AZ::IO::Path m_instanceFocusPath;
|
||||
|
||||
ContainerEntityInterface* m_containerEntityInterface = nullptr;
|
||||
|
||||
@@ -58,4 +58,18 @@ namespace AzToolsFramework::Prefab
|
||||
virtual const int GetPrefabFocusPathLength(AzFramework::EntityContextId entityContextId) const = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* The primary purpose of this bus is to facilitate writing automated tests for prefab focus mode.
|
||||
* If you would like to integrate prefabs focus mode into your system, please call PrefabFocusPublicInterface
|
||||
* for better performance.
|
||||
*/
|
||||
class PrefabFocusPublicRequests
|
||||
: public AZ::EBusTraits
|
||||
{
|
||||
public:
|
||||
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
|
||||
};
|
||||
|
||||
using PrefabFocusPublicRequestBus = AZ::EBus<PrefabFocusPublicInterface, PrefabFocusPublicRequests>;
|
||||
|
||||
} // namespace AzToolsFramework::Prefab
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <AzCore/Component/TransformBus.h>
|
||||
#include <AzCore/JSON/stringbuffer.h>
|
||||
#include <AzCore/JSON/writer.h>
|
||||
#include <AzCore/Serialization/Json/JsonSerialization.h>
|
||||
#include <AzCore/Utils/TypeHash.h>
|
||||
|
||||
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
|
||||
@@ -29,6 +30,7 @@
|
||||
#include <AzToolsFramework/Prefab/PrefabUndo.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabUndoHelpers.h>
|
||||
#include <AzToolsFramework/ToolsComponents/TransformComponent.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntitySortComponent.h>
|
||||
|
||||
#include <QString>
|
||||
|
||||
@@ -519,21 +521,18 @@ namespace AzToolsFramework
|
||||
nestedInstanceLink.has_value(),
|
||||
"A valid link was not found for one of the instances provided as input for the CreatePrefab operation.");
|
||||
|
||||
PrefabDomReference nestedInstanceLinkDom = nestedInstanceLink->get().GetLinkDom();
|
||||
AZ_Assert(
|
||||
nestedInstanceLinkDom.has_value(),
|
||||
"A valid DOM was not found for the link corresponding to one of the instances provided as input for the "
|
||||
"CreatePrefab operation.");
|
||||
|
||||
PrefabDomValueReference nestedInstanceLinkPatches =
|
||||
PrefabDomUtils::FindPrefabDomValue(nestedInstanceLinkDom->get(), PrefabDomUtils::PatchesName);
|
||||
AZ_Assert(
|
||||
nestedInstanceLinkPatches.has_value(),
|
||||
"A valid DOM for patches was not found for the link corresponding to one of the instances provided as input for the "
|
||||
"CreatePrefab operation.");
|
||||
|
||||
PrefabDom patchesCopyForUndoSupport;
|
||||
patchesCopyForUndoSupport.CopyFrom(nestedInstanceLinkPatches->get(), patchesCopyForUndoSupport.GetAllocator());
|
||||
PrefabDomReference nestedInstanceLinkDom = nestedInstanceLink->get().GetLinkDom();
|
||||
if (nestedInstanceLinkDom.has_value())
|
||||
{
|
||||
PrefabDomValueReference nestedInstanceLinkPatches =
|
||||
PrefabDomUtils::FindPrefabDomValue(nestedInstanceLinkDom->get(), PrefabDomUtils::PatchesName);
|
||||
if (nestedInstanceLinkPatches.has_value())
|
||||
{
|
||||
patchesCopyForUndoSupport.CopyFrom(nestedInstanceLinkPatches->get(), patchesCopyForUndoSupport.GetAllocator());
|
||||
}
|
||||
}
|
||||
|
||||
PrefabUndoHelpers::RemoveLink(
|
||||
sourceInstance->GetTemplateId(), targetTemplateId, sourceInstance->GetInstanceAlias(), sourceInstance->GetLinkId(),
|
||||
AZStd::move(patchesCopyForUndoSupport), undoBatch);
|
||||
@@ -595,8 +594,41 @@ namespace AzToolsFramework
|
||||
|
||||
Instance& entityOwningInstance = owningInstanceOfParentEntity->get();
|
||||
|
||||
// Get the template for our owning instance from the root prefab DOM and use that to generate our patch
|
||||
AZStd::vector<InstanceOptionalConstReference> pathOfInstances;
|
||||
|
||||
InstanceOptionalReference rootInstance = owningInstanceOfParentEntity;
|
||||
while (rootInstance->get().GetParentInstance() != AZStd::nullopt)
|
||||
{
|
||||
pathOfInstances.emplace_back(rootInstance);
|
||||
rootInstance = rootInstance->get().GetParentInstance();
|
||||
}
|
||||
|
||||
AZStd::string aliasPathResult = "";
|
||||
for (auto instanceIter = pathOfInstances.rbegin(); instanceIter != pathOfInstances.rend(); ++instanceIter)
|
||||
{
|
||||
aliasPathResult.append("/Instances/");
|
||||
aliasPathResult.append((*instanceIter)->get().GetInstanceAlias());
|
||||
}
|
||||
|
||||
PrefabDomPath rootPrefabDomPath(aliasPathResult.c_str());
|
||||
|
||||
PrefabDom& rootPrefabTemplateDom = m_prefabSystemComponentInterface->FindTemplateDom(rootInstance->get().GetTemplateId());
|
||||
|
||||
auto instanceDomFromRootValue = rootPrefabDomPath.Get(rootPrefabTemplateDom);
|
||||
if (!instanceDomFromRootValue)
|
||||
{
|
||||
return AZ::Failure<AZStd::string>("Could not load Instance DOM from the top level ancestor's DOM.");
|
||||
}
|
||||
|
||||
PrefabDomValueReference instanceDomFromRoot = *instanceDomFromRootValue;
|
||||
if (!instanceDomFromRoot.has_value())
|
||||
{
|
||||
return AZ::Failure<AZStd::string>("Could not load Instance DOM from the top level ancestor's DOM.");
|
||||
}
|
||||
|
||||
PrefabDom instanceDomBeforeUpdate;
|
||||
m_instanceToTemplateInterface->GenerateDomForInstance(instanceDomBeforeUpdate, entityOwningInstance);
|
||||
instanceDomBeforeUpdate.CopyFrom(instanceDomFromRoot.value().get(), instanceDomBeforeUpdate.GetAllocator());
|
||||
|
||||
ScopedUndoBatch undoBatch("Add Entity");
|
||||
|
||||
@@ -674,6 +706,9 @@ namespace AzToolsFramework
|
||||
bool isInstanceContainerEntity = IsInstanceContainerEntity(entityId) && !IsLevelInstanceContainerEntity(entityId);
|
||||
bool isNewParentOwnedByDifferentInstance = false;
|
||||
|
||||
bool isInFocusTree = m_prefabFocusPublicInterface->IsOwningPrefabInFocusHierarchy(entityId);
|
||||
bool isOwnedByFocusedPrefabInstance = m_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(entityId);
|
||||
|
||||
if (beforeParentId != afterParentId)
|
||||
{
|
||||
// If the entity parent changed, verify if the owning instance changed too
|
||||
@@ -727,7 +762,7 @@ namespace AzToolsFramework
|
||||
}
|
||||
}
|
||||
|
||||
if (isInstanceContainerEntity)
|
||||
if (isInFocusTree && !isOwnedByFocusedPrefabInstance)
|
||||
{
|
||||
if (isNewParentOwnedByDifferentInstance)
|
||||
{
|
||||
@@ -1144,6 +1179,10 @@ namespace AzToolsFramework
|
||||
|
||||
AZ::EntityId firstEntityIdToDelete = entityIdsNoFocusContainer[0];
|
||||
InstanceOptionalReference commonOwningInstance = GetOwnerInstanceByEntityId(firstEntityIdToDelete);
|
||||
if (!commonOwningInstance.has_value())
|
||||
{
|
||||
return AZ::Failure(AZStd::string("Cannot delete entities belonging to an invalid instance"));
|
||||
}
|
||||
|
||||
// If the first entity id is a container entity id, then we need to mark its parent as the common owning instance because you
|
||||
// cannot delete an instance from itself.
|
||||
@@ -1644,6 +1683,144 @@ namespace AzToolsFramework
|
||||
return true;
|
||||
}
|
||||
|
||||
void PrefabPublicHandler::AddNewEntityToSortOrder(
|
||||
Instance& owningInstance,
|
||||
PrefabDom& domToAddEntityUnder,
|
||||
const EntityAlias& parentEntityAlias,
|
||||
const EntityAlias& entityToAddAlias)
|
||||
{
|
||||
// Find the parent entity to get its sort order component
|
||||
auto findParentEntity = [&]() -> rapidjson::Value*
|
||||
{
|
||||
if (auto containerEntityIter = domToAddEntityUnder.FindMember(PrefabDomUtils::ContainerEntityName);
|
||||
containerEntityIter != domToAddEntityUnder.MemberEnd())
|
||||
{
|
||||
if (parentEntityAlias == containerEntityIter->value[PrefabDomUtils::EntityIdName].GetString())
|
||||
{
|
||||
return &containerEntityIter->value;
|
||||
}
|
||||
}
|
||||
|
||||
if (auto entitiesIter = domToAddEntityUnder.FindMember(PrefabDomUtils::EntitiesName);
|
||||
entitiesIter != domToAddEntityUnder.MemberEnd())
|
||||
{
|
||||
for (auto entityIter = entitiesIter->value.MemberBegin(); entityIter != entitiesIter->value.MemberEnd(); ++entityIter)
|
||||
{
|
||||
if (parentEntityAlias == entityIter->value[PrefabDomUtils::EntityIdName].GetString())
|
||||
{
|
||||
return &entityIter->value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
rapidjson::Value* parentEntityValue = findParentEntity();
|
||||
if (parentEntityValue == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the list of selected entities, we'll insert our duplicated entities after the last selected
|
||||
// sibling in their parent's list, e.g. for:
|
||||
// - Entity1
|
||||
// - Entity2 (selected)
|
||||
// - Entity3
|
||||
// - Entity4 (selected)
|
||||
// - Entity5
|
||||
// Our duplicate selection command would create duplicate Entity2 and Entity4 and insert them after Entity4:
|
||||
// - Entity1
|
||||
// - Entity2
|
||||
// - Entity3
|
||||
// - Entity4
|
||||
// - Entity2 (new, selected after duplicate)
|
||||
// - Entity4 (new, selected after duplicate)
|
||||
// - Entity5
|
||||
AzToolsFramework::EntityIdList selectedEntities;
|
||||
AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(
|
||||
selectedEntities, &AzToolsFramework::ToolsApplicationRequests::GetSelectedEntities);
|
||||
|
||||
// Find the EditorEntitySortComponent DOM
|
||||
auto componentsIter = parentEntityValue->FindMember(PrefabDomUtils::ComponentsName);
|
||||
if (componentsIter == parentEntityValue->MemberEnd())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto componentIter = componentsIter->value.MemberBegin(); componentIter != componentIter->value.MemberEnd();
|
||||
++componentIter)
|
||||
{
|
||||
// Check the component type
|
||||
auto typeFieldIter = componentIter->value.FindMember(PrefabDomUtils::TypeName);
|
||||
if (typeFieldIter == componentIter->value.MemberEnd())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
AZ::JsonDeserializerSettings jsonDeserializerSettings;
|
||||
AZ::Uuid typeId = AZ::Uuid::CreateNull();
|
||||
AZ::JsonSerialization::LoadTypeId(typeId, typeFieldIter->value);
|
||||
|
||||
if (typeId != azrtti_typeid<Components::EditorEntitySortComponent>())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check for the entity order field
|
||||
auto orderMembersIter = componentIter->value.FindMember(PrefabDomUtils::EntityOrderName);
|
||||
if (orderMembersIter == componentIter->value.MemberEnd() || !orderMembersIter->value.IsArray())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Scan for the last selected entity in the list (if any) to determine where to add our entries
|
||||
rapidjson::Value newOrder(rapidjson::kArrayType);
|
||||
auto insertValuesAfter = orderMembersIter->value.End();
|
||||
for (auto orderMemberIter = orderMembersIter->value.Begin(); orderMemberIter != orderMembersIter->value.End();
|
||||
++orderMemberIter)
|
||||
{
|
||||
if (!orderMemberIter->IsString())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const char* value = orderMemberIter->GetString();
|
||||
for (AZ::EntityId selectedEntity : selectedEntities)
|
||||
{
|
||||
auto alias = owningInstance.GetEntityAlias(selectedEntity);
|
||||
if (alias.has_value() && alias.value().get() == value)
|
||||
{
|
||||
insertValuesAfter = orderMemberIter;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Construct our new array with the new order - insertion may happen at end, so check for that in the loop itself
|
||||
for (auto orderMemberIter = orderMembersIter->value.Begin();; ++orderMemberIter)
|
||||
{
|
||||
if (orderMemberIter != orderMembersIter->value.End())
|
||||
{
|
||||
newOrder.PushBack(orderMemberIter->Move(), domToAddEntityUnder.GetAllocator());
|
||||
}
|
||||
if (orderMemberIter == insertValuesAfter)
|
||||
{
|
||||
newOrder.PushBack(
|
||||
rapidjson::Value(entityToAddAlias.c_str(), domToAddEntityUnder.GetAllocator()),
|
||||
domToAddEntityUnder.GetAllocator());
|
||||
}
|
||||
if (orderMemberIter == orderMembersIter->value.End())
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Replace the order with our newly constructed one
|
||||
orderMembersIter->value.Swap(newOrder);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void PrefabPublicHandler::DuplicateNestedEntitiesInInstance(Instance& commonOwningInstance,
|
||||
const AZStd::vector<AZ::Entity*>& entities, PrefabDom& domToAddDuplicatedEntitiesUnder,
|
||||
EntityIdList& duplicatedEntityIds, AZStd::unordered_map<EntityAlias, EntityAlias>& oldAliasToNewAliasMap)
|
||||
@@ -1701,6 +1878,73 @@ namespace AzToolsFramework
|
||||
PrefabDom entityDomAfter(&domToAddDuplicatedEntitiesUnder.GetAllocator());
|
||||
entityDomAfter.Parse(newEntityDomString.toUtf8().constData());
|
||||
|
||||
EntityAlias parentEntityAlias;
|
||||
if (auto componentsIter = entityDomAfter.FindMember(PrefabDomUtils::ComponentsName);
|
||||
componentsIter != entityDomAfter.MemberEnd())
|
||||
{
|
||||
auto checkComponent = [&](const rapidjson::Value& value) -> bool
|
||||
{
|
||||
if (!value.IsObject())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check the component type
|
||||
auto typeFieldIter = value.FindMember(PrefabDomUtils::TypeName);
|
||||
if (typeFieldIter == value.MemberEnd())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
AZ::JsonDeserializerSettings jsonDeserializerSettings;
|
||||
AZ::Uuid typeId = AZ::Uuid::CreateNull();
|
||||
AZ::JsonSerialization::LoadTypeId(typeId, typeFieldIter->value);
|
||||
|
||||
// Prefabs get serialized with the Editor transform component type, check for that
|
||||
if (typeId != azrtti_typeid<Components::TransformComponent>())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (auto parentEntityIter = value.FindMember("Parent Entity");
|
||||
parentEntityIter != value.MemberEnd())
|
||||
{
|
||||
parentEntityAlias = parentEntityIter->value.GetString();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
if (componentsIter->value.IsObject())
|
||||
{
|
||||
for (auto componentIter = componentsIter->value.MemberBegin(); componentIter != componentsIter->value.MemberEnd();
|
||||
++componentIter)
|
||||
{
|
||||
if (checkComponent(componentIter->value))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (componentsIter->value.IsArray())
|
||||
{
|
||||
for (auto componentIter = componentsIter->value.Begin(); componentIter != componentsIter->value.End();
|
||||
++componentIter)
|
||||
{
|
||||
if (checkComponent(*componentIter))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Insert our entity into its parent's sort order
|
||||
if (!parentEntityAlias.empty())
|
||||
{
|
||||
AddNewEntityToSortOrder(commonOwningInstance, domToAddDuplicatedEntitiesUnder, parentEntityAlias, newEntityAlias);
|
||||
}
|
||||
|
||||
// Add the new Entity DOM to the Entities member of the instance
|
||||
rapidjson::Value aliasName(newEntityAlias.c_str(), static_cast<rapidjson::SizeType>(newEntityAlias.length()), domToAddDuplicatedEntitiesUnder.GetAllocator());
|
||||
entitiesIter->value.AddMember(AZStd::move(aliasName), entityDomAfter, domToAddDuplicatedEntitiesUnder.GetAllocator());
|
||||
|
||||
@@ -78,6 +78,8 @@ namespace AzToolsFramework
|
||||
|
||||
InstanceOptionalReference GetOwnerInstanceByEntityId(AZ::EntityId entityId) const;
|
||||
bool EntitiesBelongToSameInstance(const EntityIdList& entityIds) const;
|
||||
void AddNewEntityToSortOrder(Instance& owningInstance, PrefabDom& domToAddEntityUnder,
|
||||
const EntityAlias& parentEntityAlias, const EntityAlias& entityToAddAlias);
|
||||
|
||||
/**
|
||||
* Duplicate a list of entities owned by a common owning instance by directly
|
||||
|
||||
@@ -60,6 +60,7 @@ namespace AzToolsFramework
|
||||
AzToolsFramework::Prefab::PrefabConversionUtils::PrefabCatchmentProcessor::Reflect(context);
|
||||
AzToolsFramework::Prefab::PrefabConversionUtils::EditorInfoRemover::Reflect(context);
|
||||
PrefabPublicRequestHandler::Reflect(context);
|
||||
PrefabFocusHandler::Reflect(context);
|
||||
PrefabLoader::Reflect(context);
|
||||
PrefabSystemScriptingHandler::Reflect(context);
|
||||
|
||||
|
||||
+9
-9
@@ -6,10 +6,10 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzFramework/StringFunc/StringFunc.h>
|
||||
#include <AzCore/StringFunc/StringFunc.h>
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
#include <AzToolsFramework/Thumbnails/SourceControlThumbnail.h>
|
||||
#include <AzToolsFramework/SourceControl/SourceControlAPI.h>
|
||||
#include <AzFramework/API/ApplicationAPI.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
@@ -68,12 +68,12 @@ namespace AzToolsFramework
|
||||
SourceControlThumbnail::SourceControlThumbnail(SharedThumbnailKey key)
|
||||
: Thumbnail(key)
|
||||
{
|
||||
const char* engineRoot = nullptr;
|
||||
AzFramework::ApplicationRequests::Bus::BroadcastResult(engineRoot, &AzFramework::ApplicationRequests::GetEngineRoot);
|
||||
AZ_Assert(engineRoot, "Engine Root not initialized");
|
||||
AZ::IO::FixedMaxPath engineRoot = AZ::Utils::GetEnginePath();
|
||||
AZ_Assert(!engineRoot.empty(), "Engine Root not initialized");
|
||||
|
||||
m_writableIconPath = (engineRoot / WRITABLE_ICON_PATH).String();
|
||||
m_nonWritableIconPath = (engineRoot / NONWRITABLE_ICON_PATH).String();
|
||||
|
||||
AzFramework::StringFunc::Path::Join(engineRoot, WRITABLE_ICON_PATH, m_writableIconPath);
|
||||
AzFramework::StringFunc::Path::Join(engineRoot, NONWRITABLE_ICON_PATH, m_nonWritableIconPath);
|
||||
|
||||
BusConnect();
|
||||
}
|
||||
@@ -90,8 +90,8 @@ namespace AzToolsFramework
|
||||
AZ_Assert(sourceControlKey, "Incorrect key type, excpected SourceControlThumbnailKey");
|
||||
|
||||
AZStd::string myFileName(sourceControlKey->GetFileName());
|
||||
AzFramework::StringFunc::Path::Normalize(myFileName);
|
||||
if (AzFramework::StringFunc::Equal(myFileName.c_str(), filename))
|
||||
AZ::StringFunc::Path::Normalize(myFileName);
|
||||
if (AZ::StringFunc::Equal(myFileName.c_str(), filename))
|
||||
{
|
||||
Update();
|
||||
}
|
||||
|
||||
-2
@@ -23,7 +23,6 @@ namespace AzToolsFramework
|
||||
// save 2k at a time :( need a better way to do this.
|
||||
AZStd::size_t pos = 0;
|
||||
AZStd::size_t remaining = m_windowState.size();
|
||||
AZ::u8* charData = (AZ::u8*)windowState.begin();
|
||||
|
||||
while (remaining > 0)
|
||||
{
|
||||
@@ -31,7 +30,6 @@ namespace AzToolsFramework
|
||||
m_serializableWindowState.push_back();
|
||||
m_serializableWindowState.back().assign((AZ::u8*)windowState.begin() + pos, (AZ::u8*)windowState.begin() + pos + bytes_this_gulp);
|
||||
pos += bytes_this_gulp;
|
||||
charData += bytes_this_gulp;
|
||||
remaining -= bytes_this_gulp;
|
||||
}
|
||||
}
|
||||
|
||||
+4
-16
@@ -362,23 +362,11 @@ namespace AzToolsFramework
|
||||
// Tick the component app.
|
||||
AZ::ComponentApplication* pApp = nullptr;
|
||||
EBUS_EVENT_RESULT(pApp, AZ::ComponentApplicationBus, GetApplication);
|
||||
if (pApp)
|
||||
if (pApp && m_ptrTicker)
|
||||
{
|
||||
AZStd::chrono::system_clock::time_point now = AZStd::chrono::system_clock::now();
|
||||
static AZStd::chrono::system_clock::time_point lastUpdate = now;
|
||||
|
||||
AZStd::chrono::duration<float> delta = now - lastUpdate;
|
||||
float deltaTime = delta.count();
|
||||
|
||||
lastUpdate = now;
|
||||
|
||||
if (m_ptrTicker)
|
||||
{
|
||||
AZ::SystemTickBus::ExecuteQueuedEvents();
|
||||
AZ::SystemTickBus::Broadcast(&AZ::SystemTickEvents::OnSystemTick);
|
||||
pApp->Tick(deltaTime);
|
||||
}
|
||||
|
||||
AZ::SystemTickBus::ExecuteQueuedEvents();
|
||||
AZ::SystemTickBus::Broadcast(&AZ::SystemTickEvents::OnSystemTick);
|
||||
pApp->Tick();
|
||||
}
|
||||
|
||||
m_bTicking = false;
|
||||
|
||||
+3
@@ -64,6 +64,9 @@ namespace AzToolsFramework::Prefab
|
||||
);
|
||||
|
||||
m_backButton->setToolTip("Up one level (-)");
|
||||
|
||||
// Currently hide this button until we can correctly disable/enable it based on context.
|
||||
m_backButton->hide();
|
||||
}
|
||||
|
||||
void PrefabViewportFocusPathHandler::OnPrefabFocusChanged()
|
||||
|
||||
+58
-34
@@ -45,6 +45,7 @@ AZ_POP_DISABLE_WARNING
|
||||
#include <AzToolsFramework/AssetBrowser/EBusFindAssetTypeByName.h>
|
||||
#include <AzToolsFramework/ComponentMode/ComponentModeDelegate.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabFocusPublicInterface.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabPublicInterface.h>
|
||||
#include <AzToolsFramework/Slice/SliceDataFlagsCommand.h>
|
||||
#include <AzToolsFramework/Slice/SliceMetadataEntityContextBus.h>
|
||||
@@ -606,6 +607,7 @@ namespace AzToolsFramework
|
||||
|
||||
AzToolsFramework::ComponentModeFramework::EditorComponentModeNotificationBus::Handler::BusConnect(
|
||||
AzToolsFramework::GetEntityContextId());
|
||||
ViewportEditorModeNotificationsBus::Handler::BusConnect(GetEntityContextId());
|
||||
}
|
||||
|
||||
EntityPropertyEditor::~EntityPropertyEditor()
|
||||
@@ -618,7 +620,8 @@ namespace AzToolsFramework
|
||||
AZ::EntitySystemBus::Handler::BusDisconnect();
|
||||
EditorEntityContextNotificationBus::Handler::BusDisconnect();
|
||||
AzToolsFramework::ComponentModeFramework::EditorComponentModeNotificationBus::Handler::BusDisconnect();
|
||||
|
||||
ViewportEditorModeNotificationsBus::Handler::BusDisconnect();
|
||||
|
||||
for (auto& entityId : m_overrideSelectedEntityIds)
|
||||
{
|
||||
DisconnectFromEntityBuses(entityId);
|
||||
@@ -892,25 +895,51 @@ namespace AzToolsFramework
|
||||
{
|
||||
if (!m_prefabsAreEnabled)
|
||||
{
|
||||
return m_isLevelEntityEditor ? InspectorLayout::LEVEL : InspectorLayout::ENTITY;
|
||||
return m_isLevelEntityEditor ? InspectorLayout::Level : InspectorLayout::Entity;
|
||||
}
|
||||
|
||||
// Prefabs layout logic
|
||||
|
||||
// If this is the container entity for the root instance, treat it like a level entity.
|
||||
AZ::EntityId levelContainerEntityId = m_prefabPublicInterface->GetLevelInstanceContainerEntityId();
|
||||
if (AZStd::find(m_selectedEntityIds.begin(), m_selectedEntityIds.end(), levelContainerEntityId) != m_selectedEntityIds.end())
|
||||
{
|
||||
if (m_selectedEntityIds.size() > 1)
|
||||
{
|
||||
return InspectorLayout::INVALID;
|
||||
return InspectorLayout::Invalid;
|
||||
}
|
||||
else
|
||||
{
|
||||
return InspectorLayout::LEVEL;
|
||||
return InspectorLayout::Level;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return InspectorLayout::ENTITY;
|
||||
// If this is the container entity for the currently focused prefab, utilize a separate layout.
|
||||
if (auto prefabFocusPublicInterface = AZ::Interface<AzToolsFramework::Prefab::PrefabFocusPublicInterface>::Get())
|
||||
{
|
||||
AzFramework::EntityContextId editorEntityContextId = AzFramework::EntityContextId::CreateNull();
|
||||
EditorEntityContextRequestBus::BroadcastResult(
|
||||
editorEntityContextId, &EditorEntityContextRequests::GetEditorEntityContextId);
|
||||
|
||||
AZ::EntityId focusedPrefabContainerEntityId =
|
||||
prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(editorEntityContextId);
|
||||
if (AZStd::find(m_selectedEntityIds.begin(), m_selectedEntityIds.end(), focusedPrefabContainerEntityId) !=
|
||||
m_selectedEntityIds.end())
|
||||
{
|
||||
if (m_selectedEntityIds.size() > 1)
|
||||
{
|
||||
return InspectorLayout::Invalid;
|
||||
}
|
||||
else
|
||||
{
|
||||
return InspectorLayout::ContainerEntityOfFocusedPrefab;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return InspectorLayout::Entity;
|
||||
}
|
||||
|
||||
void EntityPropertyEditor::UpdateEntityDisplay()
|
||||
@@ -919,7 +948,7 @@ namespace AzToolsFramework
|
||||
|
||||
InspectorLayout layout = GetCurrentInspectorLayout();
|
||||
|
||||
if (layout == InspectorLayout::LEVEL)
|
||||
if (!m_prefabsAreEnabled && layout == InspectorLayout::Level)
|
||||
{
|
||||
AZStd::string levelName;
|
||||
AzToolsFramework::EditorRequestBus::BroadcastResult(levelName, &AzToolsFramework::EditorRequests::GetLevelName);
|
||||
@@ -961,14 +990,19 @@ namespace AzToolsFramework
|
||||
|
||||
InspectorLayout layout = GetCurrentInspectorLayout();
|
||||
|
||||
if (layout == InspectorLayout::LEVEL)
|
||||
if (layout == InspectorLayout::Level)
|
||||
{
|
||||
// The Level Inspector should only have a list of selectable components after the
|
||||
// level entity itself is valid (i.e. "selected").
|
||||
return selection.empty() ? SelectionEntityTypeInfo::None : SelectionEntityTypeInfo::LevelEntity;
|
||||
}
|
||||
|
||||
if (layout == InspectorLayout::INVALID)
|
||||
if (layout == InspectorLayout::ContainerEntityOfFocusedPrefab)
|
||||
{
|
||||
return selection.empty() ? SelectionEntityTypeInfo::None : SelectionEntityTypeInfo::ContainerEntityOfFocusedPrefab;
|
||||
}
|
||||
|
||||
if (layout == InspectorLayout::Invalid)
|
||||
{
|
||||
return SelectionEntityTypeInfo::Mixed;
|
||||
}
|
||||
@@ -1138,7 +1172,8 @@ namespace AzToolsFramework
|
||||
}
|
||||
}
|
||||
|
||||
bool isLevelLayout = GetCurrentInspectorLayout() == InspectorLayout::LEVEL;
|
||||
bool isLevelLayout = GetCurrentInspectorLayout() == InspectorLayout::Level;
|
||||
bool isContainerOfFocusedPrefabLayout = GetCurrentInspectorLayout() == InspectorLayout::ContainerEntityOfFocusedPrefab;
|
||||
|
||||
m_gui->m_entityDetailsLabel->setText(entityDetailsLabelText);
|
||||
m_gui->m_entityDetailsLabel->setVisible(entityDetailsVisible);
|
||||
@@ -1146,10 +1181,14 @@ namespace AzToolsFramework
|
||||
m_gui->m_entityNameLabel->setVisible(hasEntitiesDisplayed);
|
||||
m_gui->m_entityIcon->setVisible(hasEntitiesDisplayed);
|
||||
m_gui->m_pinButton->setVisible(m_overrideSelectedEntityIds.empty() && hasEntitiesDisplayed && !m_isSystemEntityEditor);
|
||||
m_gui->m_statusLabel->setVisible(hasEntitiesDisplayed && !m_isSystemEntityEditor && !isLevelLayout);
|
||||
m_gui->m_statusComboBox->setVisible(hasEntitiesDisplayed && !m_isSystemEntityEditor && !isLevelLayout);
|
||||
m_gui->m_entityIdLabel->setVisible(hasEntitiesDisplayed && !m_isSystemEntityEditor && !isLevelLayout);
|
||||
m_gui->m_entityIdText->setVisible(hasEntitiesDisplayed && !m_isSystemEntityEditor && !isLevelLayout);
|
||||
m_gui->m_statusLabel->setVisible(
|
||||
hasEntitiesDisplayed && !m_isSystemEntityEditor && !isLevelLayout);
|
||||
m_gui->m_statusComboBox->setVisible(
|
||||
hasEntitiesDisplayed && !m_isSystemEntityEditor && !isLevelLayout);
|
||||
m_gui->m_entityIdLabel->setVisible(
|
||||
hasEntitiesDisplayed && !m_isSystemEntityEditor && !isLevelLayout);
|
||||
m_gui->m_entityIdText->setVisible(
|
||||
hasEntitiesDisplayed && !m_isSystemEntityEditor && !isLevelLayout);
|
||||
|
||||
bool displayComponentSearchBox = hasEntitiesDisplayed;
|
||||
if (hasEntitiesDisplayed)
|
||||
@@ -1157,7 +1196,9 @@ namespace AzToolsFramework
|
||||
// Build up components to display
|
||||
SharedComponentArray sharedComponentArray;
|
||||
BuildSharedComponentArray(sharedComponentArray,
|
||||
!(selectionEntityTypeInfo == SelectionEntityTypeInfo::OnlyStandardEntities || selectionEntityTypeInfo == SelectionEntityTypeInfo::OnlyPrefabEntities));
|
||||
!(selectionEntityTypeInfo == SelectionEntityTypeInfo::OnlyStandardEntities ||
|
||||
selectionEntityTypeInfo == SelectionEntityTypeInfo::OnlyPrefabEntities) ||
|
||||
selectionEntityTypeInfo == SelectionEntityTypeInfo::ContainerEntityOfFocusedPrefab);
|
||||
|
||||
if (sharedComponentArray.size() == 0)
|
||||
{
|
||||
@@ -1173,7 +1214,8 @@ namespace AzToolsFramework
|
||||
UpdateEntityDisplay();
|
||||
}
|
||||
|
||||
m_gui->m_darkBox->setVisible(displayComponentSearchBox && !m_isSystemEntityEditor && !isLevelLayout);
|
||||
m_gui->m_darkBox->setVisible(
|
||||
displayComponentSearchBox && !m_isSystemEntityEditor && !isLevelLayout && !isContainerOfFocusedPrefabLayout);
|
||||
m_gui->m_entitySearchBox->setVisible(displayComponentSearchBox);
|
||||
|
||||
bool displayAddComponentMenu = CanAddComponentsToSelection(selectionEntityTypeInfo);
|
||||
@@ -1557,7 +1599,6 @@ namespace AzToolsFramework
|
||||
for (size_t entityIndex = 1; entityIndex < m_selectedEntityIds.size(); ++entityIndex)
|
||||
{
|
||||
entity = GetSelectedEntityById(m_selectedEntityIds[entityIndex]);
|
||||
AZ_Assert(entity, "Entity id selected for display but no such entity exists");
|
||||
if (!entity)
|
||||
{
|
||||
continue;
|
||||
@@ -4663,13 +4704,6 @@ namespace AzToolsFramework
|
||||
{
|
||||
if (mimeData->hasFormat(AssetBrowser::AssetBrowserEntry::GetMimeType()))
|
||||
{
|
||||
// extra special case: MTLs from FBX drags are ignored. are we dragging a FBX file?
|
||||
bool isDraggingFBXFile = false;
|
||||
AssetBrowser::AssetBrowserEntry::ForEachEntryInMimeData<AssetBrowser::SourceAssetBrowserEntry>(mimeData, [&](const AssetBrowser::SourceAssetBrowserEntry* source)
|
||||
{
|
||||
isDraggingFBXFile = isDraggingFBXFile || AzFramework::StringFunc::Equal(source->GetExtension().c_str(), ".fbx", false);
|
||||
});
|
||||
|
||||
// the usual case - we only allow asset browser drops of assets that have actually been associated with a kind of component.
|
||||
AssetBrowser::AssetBrowserEntry::ForEachEntryInMimeData<AssetBrowser::ProductAssetBrowserEntry>(mimeData, [&](const AssetBrowser::ProductAssetBrowserEntry* product)
|
||||
{
|
||||
@@ -4681,17 +4715,7 @@ namespace AzToolsFramework
|
||||
|
||||
if (canCreateComponent && !componentTypeId.IsNull())
|
||||
{
|
||||
// we have a component type that handles this asset.
|
||||
// but we disallow it if its a MTL file from a FBX and the FBX itself is being dragged. Its still allowed
|
||||
// to drag the actual MTL.
|
||||
EBusFindAssetTypeByName materialAssetTypeResult("Material");
|
||||
AZ::AssetTypeInfoBus::BroadcastResult(materialAssetTypeResult, &AZ::AssetTypeInfo::GetAssetType);
|
||||
AZ::Data::AssetType materialAssetType = materialAssetTypeResult.GetAssetType();
|
||||
|
||||
if ((!isDraggingFBXFile) || (product->GetAssetType() != materialAssetType))
|
||||
{
|
||||
callbackFunction(product);
|
||||
}
|
||||
callbackFunction(product);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
+7
-5
@@ -354,7 +354,8 @@ namespace AzToolsFramework
|
||||
OnlyLayerEntities,
|
||||
OnlyPrefabEntities,
|
||||
Mixed,
|
||||
LevelEntity
|
||||
LevelEntity,
|
||||
ContainerEntityOfFocusedPrefab
|
||||
};
|
||||
/**
|
||||
* Returns what kinds of entities are in the current selection. This is used because mixed selection
|
||||
@@ -364,7 +365,7 @@ namespace AzToolsFramework
|
||||
SelectionEntityTypeInfo GetSelectionEntityTypeInfo(const EntityIdList& selection) const;
|
||||
|
||||
/**
|
||||
* Returns true if a selection matching the passed in selection informatation allows components to be added.
|
||||
* Returns true if a selection matching the passed in selection information allows components to be added.
|
||||
*/
|
||||
bool CanAddComponentsToSelection(const SelectionEntityTypeInfo& selectionEntityTypeInfo) const;
|
||||
|
||||
@@ -581,9 +582,10 @@ namespace AzToolsFramework
|
||||
|
||||
enum class InspectorLayout
|
||||
{
|
||||
ENTITY = 0, // All selected entities are regular entities
|
||||
LEVEL, // The selected entity is the level prefab container entity
|
||||
INVALID // Other entities are selected alongside the level prefab container entity
|
||||
Entity = 0, // All selected entities are regular entities.
|
||||
Level, // The selected entity is the prefab container entity for the level prefab, or the slice level entity.
|
||||
ContainerEntityOfFocusedPrefab, // The selected entity is the prefab container entity for the focused prefab.
|
||||
Invalid // Other entities are selected alongside the level prefab container entity.
|
||||
};
|
||||
|
||||
InspectorLayout GetCurrentInspectorLayout() const;
|
||||
|
||||
+28
-5
@@ -527,8 +527,8 @@ namespace AzToolsFramework
|
||||
m_errorButton = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void PropertyAssetCtrl::UpdateErrorButton(const AZStd::string& errorLog)
|
||||
|
||||
void PropertyAssetCtrl::UpdateErrorButton()
|
||||
{
|
||||
if (m_errorButton)
|
||||
{
|
||||
@@ -543,12 +543,17 @@ namespace AzToolsFramework
|
||||
m_errorButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||
m_errorButton->setFixedSize(QSize(16, 16));
|
||||
m_errorButton->setMouseTracking(true);
|
||||
m_errorButton->setIcon(QIcon("Icons/PropertyEditor/error_icon.png"));
|
||||
m_errorButton->setIcon(QIcon(":/PropertyEditor/Resources/error_icon.png"));
|
||||
m_errorButton->setToolTip("Show Errors");
|
||||
|
||||
// Insert the error button after the asset label
|
||||
qobject_cast<QHBoxLayout*>(layout())->insertWidget(1, m_errorButton);
|
||||
}
|
||||
}
|
||||
|
||||
void PropertyAssetCtrl::UpdateErrorButtonWithLog(const AZStd::string& errorLog)
|
||||
{
|
||||
UpdateErrorButton();
|
||||
|
||||
// Connect pressed to opening the error dialog
|
||||
// Must capture this for call to QObject::connect
|
||||
@@ -587,6 +592,21 @@ namespace AzToolsFramework
|
||||
logDialog->show();
|
||||
});
|
||||
}
|
||||
|
||||
void PropertyAssetCtrl::UpdateErrorButtonWithMessage(const AZStd::string& message)
|
||||
{
|
||||
UpdateErrorButton();
|
||||
|
||||
connect(m_errorButton, &QPushButton::clicked, this, [this, message]() {
|
||||
QMessageBox::critical(nullptr, "Error", message.c_str());
|
||||
|
||||
// Without this, the error button would maintain focus after clicking, which left the red error icon in a blue-highlighted state
|
||||
if (parentWidget())
|
||||
{
|
||||
parentWidget()->setFocus();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void PropertyAssetCtrl::ClearAssetInternal()
|
||||
{
|
||||
@@ -960,7 +980,6 @@ namespace AzToolsFramework
|
||||
else
|
||||
{
|
||||
const AZ::Data::AssetId assetID = GetCurrentAssetID();
|
||||
m_currentAssetHint = "";
|
||||
|
||||
AZ::Outcome<AssetSystem::JobInfoContainer> jobOutcome = AZ::Failure();
|
||||
AssetSystemJobRequestBus::BroadcastResult(jobOutcome, &AssetSystemJobRequestBus::Events::GetAssetJobsInfoByAssetID, assetID, false, false);
|
||||
@@ -1018,7 +1037,7 @@ namespace AzToolsFramework
|
||||
// In case of failure, render failure icon
|
||||
case AssetSystem::JobStatus::Failed:
|
||||
{
|
||||
UpdateErrorButton(errorLog);
|
||||
UpdateErrorButtonWithLog(errorLog);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -1043,6 +1062,10 @@ namespace AzToolsFramework
|
||||
m_currentAssetHint = assetPath;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateErrorButtonWithMessage(AZStd::string::format("Asset is missing.\n\nID: %s\nHint:%s", assetID.ToString<AZStd::string>().c_str(), GetCurrentAssetHint().c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
// Get the asset file name
|
||||
|
||||
+3
-1
@@ -168,7 +168,9 @@ namespace AzToolsFramework
|
||||
|
||||
bool IsCorrectMimeData(const QMimeData* pData, AZ::Data::AssetId* pAssetId = nullptr, AZ::Data::AssetType* pAssetType = nullptr) const;
|
||||
void ClearErrorButton();
|
||||
void UpdateErrorButton(const AZStd::string& errorLog);
|
||||
void UpdateErrorButton();
|
||||
void UpdateErrorButtonWithLog(const AZStd::string& errorLog);
|
||||
void UpdateErrorButtonWithMessage(const AZStd::string& message);
|
||||
virtual const AZStd::string GetFolderSelection() const { return AZStd::string(); }
|
||||
virtual void SetFolderSelection(const AZStd::string& /* folderPath */) {}
|
||||
virtual void ClearAssetInternal();
|
||||
|
||||
+4
@@ -169,6 +169,10 @@ namespace AzToolsFramework
|
||||
{
|
||||
AzToolsFramework::PropertyEditorGUIMessages::Bus::Broadcast(&PropertyEditorGUIMessages::Bus::Events::RequestWrite, newCtrl);
|
||||
});
|
||||
this->connect(newCtrl, &PropertyControl::editingFinished, this, [newCtrl]()
|
||||
{
|
||||
AzToolsFramework::PropertyEditorGUIMessages::Bus::Broadcast(&PropertyEditorGUIMessages::Bus::Handler::OnEditingFinished, newCtrl);
|
||||
});
|
||||
// note: Qt automatically disconnects objects from each other when either end is destroyed, no need to worry about delete.
|
||||
|
||||
// Set the value range to that of ValueType as clamped to the range of QtWidgetValueType
|
||||
|
||||
+1
-4
@@ -150,10 +150,7 @@ namespace AzToolsFramework
|
||||
TypeBeingHandled actualValue = instance;
|
||||
for (int idx = 0; idx < m_common.GetElementCount(); ++idx)
|
||||
{
|
||||
if (elements[idx]->wasValueEditedByUser())
|
||||
{
|
||||
actualValue.SetElement(idx, static_cast<float>(elements[idx]->getValue()));
|
||||
}
|
||||
actualValue.SetElement(idx, static_cast<float>(elements[idx]->getValue()));
|
||||
}
|
||||
instance = actualValue;
|
||||
}
|
||||
|
||||
@@ -1201,8 +1201,6 @@ namespace AzToolsFramework
|
||||
.arg((item->parent() == nullptr) ? item->m_entity->GetName().c_str() : GetNodeDisplayName(*item->m_node).c_str()));
|
||||
}
|
||||
|
||||
SliceTargetTreeItem* parent = nullptr;
|
||||
|
||||
AZStd::vector<SliceAssetPtr> validSliceAssets = GetValidTargetAssetsForField(*item);
|
||||
|
||||
// For the selected item populate the tree of all valid slice targets.
|
||||
@@ -1274,7 +1272,6 @@ namespace AzToolsFramework
|
||||
selectButton->setChecked(true);
|
||||
}
|
||||
|
||||
parent = sliceItem;
|
||||
++level;
|
||||
}
|
||||
}
|
||||
|
||||
+4
-1
@@ -158,7 +158,10 @@ namespace UnitTest
|
||||
{
|
||||
// Create & Start a new ToolsApplication if there's no existing one
|
||||
m_app = CreateTestApplication();
|
||||
m_app->Start(AzFramework::Application::Descriptor());
|
||||
AZ::ComponentApplication::StartupParameters startupParameters;
|
||||
startupParameters.m_loadAssetCatalog = false;
|
||||
|
||||
m_app->Start(AzFramework::Application::Descriptor(), startupParameters);
|
||||
}
|
||||
|
||||
// without this, the user settings component would attempt to save on finalize/shutdown. Since the file is
|
||||
|
||||
@@ -23,11 +23,11 @@ namespace AzToolsFramework
|
||||
/// @name Reverse URLs.
|
||||
/// Used to identify common actions and override them when necessary.
|
||||
//@{
|
||||
static const AZ::Crc32 s_backAction = AZ_CRC("com.o3de.action.common.back", 0xd772a2af);
|
||||
static const AZ::Crc32 s_deleteAction = AZ_CRC("com.o3de.action.common.delete", 0x5731f6cb);
|
||||
static const AZ::Crc32 s_duplicateAction = AZ_CRC("com.o3de.action.common.duplicate", 0x08ccf461);
|
||||
static const AZ::Crc32 s_nextComponentMode = AZ_CRC("com.o3de.action.common.nextComponentMode", 0xcc26094f);
|
||||
static const AZ::Crc32 s_previousComponentMode = AZ_CRC("com.o3de.action.common.previousComponentMode", 0x0d18ff39);
|
||||
static const AZ::Crc32 s_backAction = AZ_CRC("com.o3de.action.common.back", 0x80c3030f);
|
||||
static const AZ::Crc32 s_deleteAction = AZ_CRC("com.o3de.action.common.delete", 0x58e78eed);
|
||||
static const AZ::Crc32 s_duplicateAction = AZ_CRC("com.o3de.action.common.duplicate", 0xbc5a4a23);
|
||||
static const AZ::Crc32 s_nextComponentMode = AZ_CRC("com.o3de.action.common.nextComponentMode", 0xf9aca3a8);
|
||||
static const AZ::Crc32 s_previousComponentMode = AZ_CRC("com.o3de.action.common.previousComponentMode", 0x0580eaec);
|
||||
//@}
|
||||
|
||||
/// Specific Action properties to be sent to a type implementing
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzFramework/Render/IntersectorInterface.h>
|
||||
#include <AzToolsFramework/Viewport/ViewportMessages.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
@@ -62,4 +63,47 @@ namespace AzToolsFramework
|
||||
|
||||
return circleBoundWidth;
|
||||
}
|
||||
|
||||
AZ::Vector3 FindClosestPickIntersection(const AzFramework::RenderGeometry::RayRequest& rayRequest, const float defaultDistance)
|
||||
{
|
||||
AzFramework::RenderGeometry::RayResult renderGeometryIntersectionResult;
|
||||
AzFramework::RenderGeometry::IntersectorBus::EventResult(
|
||||
renderGeometryIntersectionResult, AzToolsFramework::GetEntityContextId(),
|
||||
&AzFramework::RenderGeometry::IntersectorBus::Events::RayIntersect, rayRequest);
|
||||
|
||||
// attempt a ray intersection with any visible mesh and return the intersection position if successful
|
||||
if (renderGeometryIntersectionResult)
|
||||
{
|
||||
return renderGeometryIntersectionResult.m_worldPosition;
|
||||
}
|
||||
else
|
||||
{
|
||||
const AZ::Vector3 rayDirection = (rayRequest.m_endWorldPosition - rayRequest.m_startWorldPosition).GetNormalized();
|
||||
return rayRequest.m_startWorldPosition + rayDirection * defaultDistance;
|
||||
}
|
||||
}
|
||||
|
||||
void RefreshRayRequest(
|
||||
AzFramework::RenderGeometry::RayRequest& rayRequest,
|
||||
const ViewportInteraction::ProjectedViewportRay& viewportRay,
|
||||
const float rayLength)
|
||||
{
|
||||
AZ_Assert(rayLength > 0.0f, "Invalid ray length passed to RefreshRayRequest");
|
||||
rayRequest.m_startWorldPosition = viewportRay.origin;
|
||||
rayRequest.m_endWorldPosition = viewportRay.origin + viewportRay.direction * rayLength;
|
||||
}
|
||||
|
||||
AZ::Vector3 FindClosestPickIntersection(
|
||||
const AzFramework::ViewportId viewportId,
|
||||
const AzFramework::ScreenPoint& screenPoint,
|
||||
const float rayLength,
|
||||
const float defaultDistance)
|
||||
{
|
||||
AzFramework::RenderGeometry::RayRequest ray;
|
||||
ray.m_onlyVisible = true; // only consider visible objects
|
||||
|
||||
RefreshRayRequest(ray, ViewportInteraction::ViewportScreenToWorldRay(viewportId, screenPoint), rayLength);
|
||||
|
||||
return FindClosestPickIntersection(ray, defaultDistance);
|
||||
}
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
@@ -15,13 +15,19 @@
|
||||
#include <AzFramework/Viewport/CameraState.h>
|
||||
#include <AzFramework/Viewport/ClickDetector.h>
|
||||
#include <AzFramework/Viewport/ViewportId.h>
|
||||
#include <AzFramework/Viewport/ViewportScreen.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
|
||||
#include <AzToolsFramework/Viewport/ViewportTypes.h>
|
||||
|
||||
namespace AzFramework
|
||||
{
|
||||
struct ScreenPoint;
|
||||
}
|
||||
|
||||
namespace RenderGeometry
|
||||
{
|
||||
struct RayRequest;
|
||||
}
|
||||
} // namespace AzFramework
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
@@ -162,12 +168,11 @@ namespace AzToolsFramework
|
||||
//! Multiply by DeviceScalingFactor to get the position in viewport pixel space.
|
||||
virtual AzFramework::ScreenPoint ViewportWorldToScreen(const AZ::Vector3& worldPosition) = 0;
|
||||
//! Transforms a point from Qt widget screen space to world space based on the given clip space depth.
|
||||
//! Depth specifies a relative camera depth to project in the range of [0.f, 1.f].
|
||||
//! Returns the world space position if successful.
|
||||
virtual AZStd::optional<AZ::Vector3> ViewportScreenToWorld(const AzFramework::ScreenPoint& screenPosition, float depth) = 0;
|
||||
virtual AZ::Vector3 ViewportScreenToWorld(const AzFramework::ScreenPoint& screenPosition) = 0;
|
||||
//! Casts a point in screen space to a ray in world space originating from the viewport camera frustum's near plane.
|
||||
//! Returns a ray containing the ray's origin and a direction normal, if successful.
|
||||
virtual AZStd::optional<ProjectedViewportRay> ViewportScreenToWorldRay(const AzFramework::ScreenPoint& screenPosition) = 0;
|
||||
virtual ProjectedViewportRay ViewportScreenToWorldRay(const AzFramework::ScreenPoint& screenPosition) = 0;
|
||||
//! Gets the DPI scaling factor that translates Qt widget space into viewport pixel space.
|
||||
virtual float DeviceScalingFactor() = 0;
|
||||
|
||||
@@ -178,6 +183,26 @@ namespace AzToolsFramework
|
||||
//! Type to inherit to implement ViewportInteractionRequests.
|
||||
using ViewportInteractionRequestBus = AZ::EBus<ViewportInteractionRequests, ViewportEBusTraits>;
|
||||
|
||||
//! Utility function to return a viewport ray.
|
||||
inline ProjectedViewportRay ViewportScreenToWorldRay(
|
||||
const AzFramework::CameraState& cameraState, const AzFramework::ScreenPoint& screenPoint)
|
||||
{
|
||||
const AZ::Vector3 rayOrigin = AzFramework::ScreenToWorld(screenPoint, cameraState);
|
||||
const AZ::Vector3 rayDirection = (rayOrigin - cameraState.m_position).GetNormalized();
|
||||
return AzToolsFramework::ViewportInteraction::ProjectedViewportRay{ rayOrigin, rayDirection };
|
||||
}
|
||||
|
||||
//! Utility function to return a viewport ray using the ViewportInteractionRequestBus.
|
||||
inline ProjectedViewportRay ViewportScreenToWorldRay(
|
||||
const AzFramework::ViewportId viewportId, const AzFramework::ScreenPoint& screenPoint)
|
||||
{
|
||||
ProjectedViewportRay viewportRay{};
|
||||
ViewportInteractionRequestBus::EventResult(
|
||||
viewportRay, viewportId, &ViewportInteractionRequestBus::Events::ViewportScreenToWorldRay, screenPoint);
|
||||
|
||||
return viewportRay;
|
||||
}
|
||||
|
||||
//! Interface to return only viewport specific settings (e.g. snapping).
|
||||
class ViewportSettingsRequests
|
||||
{
|
||||
@@ -229,13 +254,6 @@ namespace AzToolsFramework
|
||||
class MainEditorViewportInteractionRequests
|
||||
{
|
||||
public:
|
||||
//! Given a point in screen space, return the picked entity (if any).
|
||||
//! Picked EntityId will be returned, InvalidEntityId will be returned on failure.
|
||||
virtual AZ::EntityId PickEntity(const AzFramework::ScreenPoint& point) = 0;
|
||||
//! Given a point in screen space, return the terrain position in world space.
|
||||
virtual AZ::Vector3 PickTerrain(const AzFramework::ScreenPoint& point) = 0;
|
||||
//! Return the terrain height given a world position in 2d (xy plane).
|
||||
virtual float TerrainHeight(const AZ::Vector2& position) = 0;
|
||||
//! Is the user holding a modifier key to move the manipulator space from local to world.
|
||||
virtual bool ShowingWorldSpace() = 0;
|
||||
//! Return the widget to use as the parent for the viewport context menu.
|
||||
@@ -266,7 +284,6 @@ namespace AzToolsFramework
|
||||
{
|
||||
public:
|
||||
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
|
||||
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
|
||||
|
||||
//! Returns the current state of the keyboard modifier keys.
|
||||
virtual KeyboardModifiers QueryKeyboardModifiers() = 0;
|
||||
@@ -290,7 +307,6 @@ namespace AzToolsFramework
|
||||
{
|
||||
public:
|
||||
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
|
||||
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
|
||||
|
||||
//! Returns the current time in seconds.
|
||||
//! This interface can be overridden for the purposes of testing to simplify viewport input requests.
|
||||
@@ -340,6 +356,21 @@ namespace AzToolsFramework
|
||||
return entityContextId;
|
||||
}
|
||||
|
||||
//! Performs an intersection test against meshes in the scene, if there is a hit (the ray intersects
|
||||
//! a mesh), that position is returned, otherwise a point projected defaultDistance from the
|
||||
//! origin of the ray will be returned.
|
||||
//! @note The intersection will only consider visible objects.
|
||||
AZ::Vector3 FindClosestPickIntersection(
|
||||
AzFramework::ViewportId viewportId, const AzFramework::ScreenPoint& screenPoint, float rayLength, float defaultDistance);
|
||||
|
||||
//! Overload of FindClosestPickIntersection taking a RenderGeometry::RayRequest directly.
|
||||
//! @note rayRequest must contain a valid ray/line segment (start/endWorldPosition must not be at the same position).
|
||||
AZ::Vector3 FindClosestPickIntersection(const AzFramework::RenderGeometry::RayRequest& rayRequest, float defaultDistance);
|
||||
|
||||
//! Update the in/out parameter rayRequest based on the latest viewport ray.
|
||||
void RefreshRayRequest(
|
||||
AzFramework::RenderGeometry::RayRequest& rayRequest, const ViewportInteraction::ProjectedViewportRay& viewportRay, float rayLength);
|
||||
|
||||
//! Maps a mouse interaction event to a ClickDetector event.
|
||||
//! @note Function only cares about up or down events, all other events are mapped to Nil (ignored).
|
||||
AzFramework::ClickDetector::ClickEvent ClickDetectorEventFromViewportInteraction(
|
||||
|
||||
+5
-3
@@ -148,7 +148,6 @@ namespace AzToolsFramework
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
EditorHelpers::EditorHelpers(const EditorVisibleEntityDataCache* entityDataCache)
|
||||
: m_entityDataCache(entityDataCache)
|
||||
{
|
||||
@@ -190,7 +189,10 @@ namespace AzToolsFramework
|
||||
if (helpersVisible)
|
||||
{
|
||||
// some components choose to hide their icons (e.g. meshes)
|
||||
if (!m_entityDataCache->IsVisibleEntityIconHidden(entityCacheIndex))
|
||||
// we also do not want to test against icons that may not be showing as they're inside a 'closed' entity container
|
||||
// (these icons only become visible when it is opened for editing)
|
||||
if (!m_entityDataCache->IsVisibleEntityIconHidden(entityCacheIndex) &&
|
||||
m_entityDataCache->IsVisibleEntityIndividuallySelectableInViewport(entityCacheIndex))
|
||||
{
|
||||
const AZ::Vector3& entityPosition = m_entityDataCache->GetVisibleEntityPosition(entityCacheIndex);
|
||||
|
||||
@@ -235,7 +237,7 @@ namespace AzToolsFramework
|
||||
viewportId, &ViewportInteraction::ViewportMouseCursorRequestBus::Events::SetOverrideCursor,
|
||||
ViewportInteraction::CursorStyleOverride::Forbidden);
|
||||
}
|
||||
|
||||
|
||||
if (mouseInteraction.m_mouseInteraction.m_mouseButtons.Left() &&
|
||||
mouseInteraction.m_mouseEvent == ViewportInteraction::MouseEvent::Down ||
|
||||
mouseInteraction.m_mouseEvent == ViewportInteraction::MouseEvent::DoubleClick)
|
||||
|
||||
+13
-2
@@ -9,6 +9,7 @@
|
||||
#include "EditorSelectionUtil.h"
|
||||
|
||||
#include <AzCore/Component/ComponentApplicationBus.h>
|
||||
#include <AzCore/Console/IConsole.h>
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
#include <AzCore/Math/Aabb.h>
|
||||
#include <AzCore/Math/IntersectSegment.h>
|
||||
@@ -16,10 +17,20 @@
|
||||
#include <AzToolsFramework/API/ComponentEntitySelectionBus.h>
|
||||
#include <AzToolsFramework/Viewport/ViewportMessages.h>
|
||||
|
||||
AZ_CVAR(
|
||||
float,
|
||||
ed_defaultEntityPlacementDistance,
|
||||
10.0f,
|
||||
nullptr,
|
||||
AZ::ConsoleFunctorFlags::Null,
|
||||
"The default distance to place an entity from the camera if no intersection is found");
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
// default ray length for picking in the viewport
|
||||
static const float EditorPickRayLength = 1000.0f;
|
||||
float GetDefaultEntityPlacementDistance()
|
||||
{
|
||||
return ed_defaultEntityPlacementDistance;
|
||||
}
|
||||
|
||||
AZ::Vector3 CalculateCenterOffset(const AZ::EntityId entityId, const EditorTransformComponentSelectionRequests::Pivot pivot)
|
||||
{
|
||||
|
||||
+6
@@ -26,6 +26,9 @@ namespace AzFramework
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
//! Default ray length for picking in the viewport.
|
||||
inline constexpr float EditorPickRayLength = 1000.0f;
|
||||
|
||||
//! Is the pivot at the center of the object (middle of extents) or at the
|
||||
//! exported authored object root position.
|
||||
inline bool Centered(const EditorTransformComponentSelectionRequests::Pivot pivot)
|
||||
@@ -57,6 +60,9 @@ namespace AzToolsFramework
|
||||
//! Wrapper for EBus call to return the DPI scaling for a given viewport.
|
||||
float GetScreenDisplayScaling(int viewportId);
|
||||
|
||||
//! The default distance an entity is placed from the camera if there is no intersection.
|
||||
float GetDefaultEntityPlacementDistance();
|
||||
|
||||
//! A utility to return the center of several points.
|
||||
//! Take several positions and store the min and max of each in
|
||||
//! turn - when all points have been added return the center/midpoint.
|
||||
|
||||
+95
-76
@@ -28,6 +28,7 @@
|
||||
#include <AzToolsFramework/Manipulators/TranslationManipulators.h>
|
||||
#include <AzToolsFramework/Maths/TransformUtils.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabFocusInterface.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabFocusPublicInterface.h>
|
||||
#include <AzToolsFramework/ToolsComponents/EditorLockComponentBus.h>
|
||||
#include <AzToolsFramework/ToolsComponents/EditorVisibilityBus.h>
|
||||
#include <AzToolsFramework/ToolsComponents/TransformComponent.h>
|
||||
@@ -230,8 +231,8 @@ namespace AzToolsFramework
|
||||
{
|
||||
return mouseInteraction.m_mouseInteraction.m_mouseButtons.Middle() &&
|
||||
mouseInteraction.m_mouseEvent == ViewportInteraction::MouseEvent::Down &&
|
||||
mouseInteraction.m_mouseInteraction.m_keyboardModifiers.Ctrl() &&
|
||||
!mouseInteraction.m_mouseInteraction.m_keyboardModifiers.Alt();
|
||||
!mouseInteraction.m_mouseInteraction.m_keyboardModifiers.Alt() &&
|
||||
mouseInteraction.m_mouseInteraction.m_keyboardModifiers.Ctrl();
|
||||
}
|
||||
|
||||
static bool IndividualDitto(const ViewportInteraction::MouseInteractionEvent& mouseInteraction)
|
||||
@@ -242,12 +243,12 @@ namespace AzToolsFramework
|
||||
mouseInteraction.m_mouseInteraction.m_keyboardModifiers.Ctrl();
|
||||
}
|
||||
|
||||
static bool SnapTerrain(const ViewportInteraction::MouseInteractionEvent& mouseInteraction)
|
||||
static bool SnapSurface(const ViewportInteraction::MouseInteractionEvent& mouseInteraction)
|
||||
{
|
||||
return mouseInteraction.m_mouseInteraction.m_mouseButtons.Middle() &&
|
||||
mouseInteraction.m_mouseEvent == ViewportInteraction::MouseEvent::Down &&
|
||||
(mouseInteraction.m_mouseInteraction.m_keyboardModifiers.Alt() ||
|
||||
mouseInteraction.m_mouseInteraction.m_keyboardModifiers.Ctrl());
|
||||
mouseInteraction.m_mouseInteraction.m_keyboardModifiers.Shift() &&
|
||||
mouseInteraction.m_mouseInteraction.m_keyboardModifiers.Ctrl();
|
||||
}
|
||||
|
||||
static bool ManipulatorDitto(
|
||||
@@ -408,7 +409,7 @@ namespace AzToolsFramework
|
||||
const AzFramework::CameraState cameraState = GetCameraState(viewportId);
|
||||
for (size_t entityCacheIndex = 0; entityCacheIndex < entityDataCache.VisibleEntityDataCount(); ++entityCacheIndex)
|
||||
{
|
||||
if (!entityDataCache.IsVisibleEntitySelectableInViewport(entityCacheIndex))
|
||||
if (!entityDataCache.IsVisibleEntityIndividuallySelectableInViewport(entityCacheIndex))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -891,60 +892,37 @@ namespace AzToolsFramework
|
||||
prevModifiers = action.m_modifiers;
|
||||
}
|
||||
|
||||
static void HandleAccents(
|
||||
const bool hasSelectedEntities,
|
||||
const AZ::EntityId entityIdUnderCursor,
|
||||
const bool ctrlHeld,
|
||||
AZ::EntityId& hoveredEntityId,
|
||||
void HandleAccents(
|
||||
const AZ::EntityId currentEntityIdUnderCursor,
|
||||
AZ::EntityId& hoveredEntityIdUnderCursor,
|
||||
const HandleAccentsContext& handleAccentsContext,
|
||||
const ViewportInteraction::MouseButtons mouseButtons,
|
||||
const bool usingBoxSelect)
|
||||
const AZStd::function<void(AZ::EntityId, bool)>& setEntityAccentedFn)
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AzToolsFramework);
|
||||
|
||||
const bool invalidMouseButtonHeld = mouseButtons.Middle() || mouseButtons.Right();
|
||||
const bool hasSelectedEntities = handleAccentsContext.m_hasSelectedEntities;
|
||||
const bool ctrlHeld = handleAccentsContext.m_ctrlHeld;
|
||||
const bool boxSelect = handleAccentsContext.m_usingBoxSelect;
|
||||
const bool stickySelect = handleAccentsContext.m_usingStickySelect;
|
||||
const bool canSelect = stickySelect ? !hasSelectedEntities || ctrlHeld : true;
|
||||
|
||||
if ((hoveredEntityId.IsValid() && hoveredEntityId != entityIdUnderCursor) ||
|
||||
(hasSelectedEntities && !ctrlHeld && hoveredEntityId.IsValid()) || invalidMouseButtonHeld)
|
||||
const bool removePreviousAccent =
|
||||
(currentEntityIdUnderCursor != hoveredEntityIdUnderCursor && hoveredEntityIdUnderCursor.IsValid()) || invalidMouseButtonHeld;
|
||||
const bool addNextAccent = currentEntityIdUnderCursor.IsValid() && canSelect && !invalidMouseButtonHeld && !boxSelect;
|
||||
|
||||
if (removePreviousAccent)
|
||||
{
|
||||
if (hoveredEntityId.IsValid())
|
||||
{
|
||||
ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequests::SetEntityHighlighted, hoveredEntityId, false);
|
||||
|
||||
hoveredEntityId.SetInvalid();
|
||||
}
|
||||
setEntityAccentedFn(hoveredEntityIdUnderCursor, false);
|
||||
hoveredEntityIdUnderCursor.SetInvalid();
|
||||
}
|
||||
|
||||
if (!invalidMouseButtonHeld && !usingBoxSelect && (!hasSelectedEntities || ctrlHeld))
|
||||
if (addNextAccent)
|
||||
{
|
||||
if (entityIdUnderCursor.IsValid())
|
||||
{
|
||||
ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequests::SetEntityHighlighted, entityIdUnderCursor, true);
|
||||
|
||||
hoveredEntityId = entityIdUnderCursor;
|
||||
}
|
||||
setEntityAccentedFn(currentEntityIdUnderCursor, true);
|
||||
hoveredEntityIdUnderCursor = currentEntityIdUnderCursor;
|
||||
}
|
||||
}
|
||||
|
||||
static AZ::Vector3 PickTerrainPosition(const ViewportInteraction::MouseInteraction& mouseInteraction)
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AzToolsFramework);
|
||||
|
||||
const int viewportId = mouseInteraction.m_interactionId.m_viewportId;
|
||||
// get unsnapped terrain position (world space)
|
||||
AZ::Vector3 worldSurfacePosition;
|
||||
ViewportInteraction::MainEditorViewportInteractionRequestBus::EventResult(
|
||||
worldSurfacePosition, viewportId, &ViewportInteraction::MainEditorViewportInteractionRequestBus::Events::PickTerrain,
|
||||
mouseInteraction.m_mousePick.m_screenCoordinates);
|
||||
|
||||
// convert to local space - snap if enabled
|
||||
const GridSnapParameters gridSnapParams = GridSnapSettings(viewportId);
|
||||
const AZ::Vector3 finalSurfacePosition = gridSnapParams.m_gridSnap
|
||||
? CalculateSnappedTerrainPosition(worldSurfacePosition, AZ::Transform::CreateIdentity(), viewportId, gridSnapParams.m_gridSize)
|
||||
: worldSurfacePosition;
|
||||
|
||||
return finalSurfacePosition;
|
||||
}
|
||||
|
||||
// is the passed entity id contained with in the entity id list
|
||||
template<typename EntityIdContainer>
|
||||
static bool IsEntitySelectedInternal(AZ::EntityId entityId, const EntityIdContainer& selectedEntityIds)
|
||||
@@ -982,7 +960,7 @@ namespace AzToolsFramework
|
||||
{
|
||||
if (auto entityIndex = entityDataCache.GetVisibleEntityIndexFromId(entityId))
|
||||
{
|
||||
if (entityDataCache.IsVisibleEntitySelectableInViewport(*entityIndex))
|
||||
if (entityDataCache.IsVisibleEntityIndividuallySelectableInViewport(*entityIndex))
|
||||
{
|
||||
return *entityIndex;
|
||||
}
|
||||
@@ -1013,6 +991,15 @@ namespace AzToolsFramework
|
||||
ToolsApplicationNotificationBus::Broadcast(&ToolsApplicationNotificationBus::Events::InvalidatePropertyDisplay, Refresh_Values);
|
||||
}
|
||||
|
||||
// leaves focus mode by focusing on the parent of the current perfab in the entity outliner
|
||||
static void LeaveFocusMode()
|
||||
{
|
||||
if (auto prefabFocusPublicInterface = AZ::Interface<Prefab::PrefabFocusPublicInterface>::Get())
|
||||
{
|
||||
prefabFocusPublicInterface->FocusOnParentOfFocusedPrefab(GetEntityContextId());
|
||||
}
|
||||
}
|
||||
|
||||
EditorTransformComponentSelection::EditorTransformComponentSelection(const EditorVisibleEntityDataCache* entityDataCache)
|
||||
: m_entityDataCache(entityDataCache)
|
||||
{
|
||||
@@ -1177,8 +1164,10 @@ namespace AzToolsFramework
|
||||
continue;
|
||||
}
|
||||
|
||||
const AZ::Aabb bound = CalculateEditorEntitySelectionBounds(entityId, viewportInfo);
|
||||
debugDisplay.DrawSolidBox(bound.GetMin(), bound.GetMax());
|
||||
if (const AZ::Aabb bound = CalculateEditorEntitySelectionBounds(entityId, viewportInfo); bound.IsValid())
|
||||
{
|
||||
debugDisplay.DrawSolidBox(bound.GetMin(), bound.GetMax());
|
||||
}
|
||||
}
|
||||
|
||||
debugDisplay.DepthTestOn();
|
||||
@@ -1277,7 +1266,7 @@ namespace AzToolsFramework
|
||||
m_axisPreview.m_translation = m_entityIdManipulators.m_manipulators->GetLocalTransform().GetTranslation();
|
||||
m_axisPreview.m_orientation = QuaternionFromTransformNoScaling(m_entityIdManipulators.m_manipulators->GetLocalTransform());
|
||||
|
||||
// [ref 1.]
|
||||
// see comment [ref 1.] above
|
||||
BeginRecordManipulatorCommand();
|
||||
});
|
||||
|
||||
@@ -1312,7 +1301,7 @@ namespace AzToolsFramework
|
||||
m_axisPreview.m_translation = m_entityIdManipulators.m_manipulators->GetLocalTransform().GetTranslation();
|
||||
m_axisPreview.m_orientation = QuaternionFromTransformNoScaling(m_entityIdManipulators.m_manipulators->GetLocalTransform());
|
||||
|
||||
// [ref 1.]
|
||||
// see comment [ref 1.] above
|
||||
BeginRecordManipulatorCommand();
|
||||
});
|
||||
|
||||
@@ -1345,7 +1334,7 @@ namespace AzToolsFramework
|
||||
m_axisPreview.m_translation = m_entityIdManipulators.m_manipulators->GetLocalTransform().GetTranslation();
|
||||
m_axisPreview.m_orientation = QuaternionFromTransformNoScaling(m_entityIdManipulators.m_manipulators->GetLocalTransform());
|
||||
|
||||
// [ref 1.]
|
||||
// see comment [ref 1.] above
|
||||
BeginRecordManipulatorCommand();
|
||||
});
|
||||
|
||||
@@ -1419,7 +1408,7 @@ namespace AzToolsFramework
|
||||
m_axisPreview.m_translation = m_entityIdManipulators.m_manipulators->GetLocalTransform().GetTranslation();
|
||||
m_axisPreview.m_orientation = QuaternionFromTransformNoScaling(m_entityIdManipulators.m_manipulators->GetLocalTransform());
|
||||
|
||||
// [ref 1.]
|
||||
// see comment [ref 1.] above
|
||||
BeginRecordManipulatorCommand();
|
||||
});
|
||||
|
||||
@@ -1801,7 +1790,7 @@ namespace AzToolsFramework
|
||||
const AzFramework::CameraState cameraState = GetCameraState(viewportId);
|
||||
|
||||
const auto cursorEntityIdQuery = m_editorHelpers->FindEntityIdUnderCursor(cameraState, mouseInteraction);
|
||||
m_cachedEntityIdUnderCursor = cursorEntityIdQuery.ContainerAncestorEntityId();
|
||||
m_currentEntityIdUnderCursor = cursorEntityIdQuery.ContainerAncestorEntityId();
|
||||
|
||||
const auto selectClickEvent = ClickDetectorEventFromViewportInteraction(mouseInteraction);
|
||||
m_cursorState.SetCurrentPosition(mouseInteraction.m_mouseInteraction.m_mousePick.m_screenCoordinates);
|
||||
@@ -1822,7 +1811,7 @@ namespace AzToolsFramework
|
||||
mouseInteraction.m_mouseInteraction,
|
||||
AZ::Aabb::CreateFromMinMax(boxPosition - scaledSize, boxPosition + scaledSize)))
|
||||
{
|
||||
m_cachedEntityIdUnderCursor = entityId;
|
||||
m_currentEntityIdUnderCursor = entityId;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1842,7 +1831,7 @@ namespace AzToolsFramework
|
||||
return true;
|
||||
}
|
||||
|
||||
const AZ::EntityId entityIdUnderCursor = m_cachedEntityIdUnderCursor;
|
||||
const AZ::EntityId entityIdUnderCursor = m_currentEntityIdUnderCursor;
|
||||
|
||||
if (mouseInteraction.m_mouseEvent == ViewportInteraction::MouseEvent::DoubleClick &&
|
||||
mouseInteraction.m_mouseInteraction.m_mouseButtons.Left())
|
||||
@@ -1891,6 +1880,13 @@ namespace AzToolsFramework
|
||||
|
||||
if (!m_selectedEntityIds.empty())
|
||||
{
|
||||
// try snapping to a surface (mesh) if in Translation mode
|
||||
if (Input::SnapSurface(mouseInteraction))
|
||||
{
|
||||
PerformSnapToSurface(mouseInteraction);
|
||||
return false;
|
||||
}
|
||||
|
||||
// group copying/alignment to specific entity - 'ditto' position/orientation for group
|
||||
if (Input::GroupDitto(mouseInteraction) && PerformGroupDitto(entityIdUnderCursor))
|
||||
{
|
||||
@@ -1903,13 +1899,6 @@ namespace AzToolsFramework
|
||||
return false;
|
||||
}
|
||||
|
||||
// try snapping to the terrain (if in Translation mode) and entity wasn't picked
|
||||
if (Input::SnapTerrain(mouseInteraction))
|
||||
{
|
||||
PerformSnapToTerrain(mouseInteraction);
|
||||
return false;
|
||||
}
|
||||
|
||||
// set manipulator pivot override translation or orientation (update manipulators)
|
||||
if (Input::ManipulatorDitto(clickOutcome, mouseInteraction))
|
||||
{
|
||||
@@ -1997,25 +1986,28 @@ namespace AzToolsFramework
|
||||
return false;
|
||||
}
|
||||
|
||||
void EditorTransformComponentSelection::PerformSnapToTerrain(const ViewportInteraction::MouseInteractionEvent& mouseInteraction)
|
||||
void EditorTransformComponentSelection::PerformSnapToSurface(const ViewportInteraction::MouseInteractionEvent& mouseInteraction)
|
||||
{
|
||||
for (AZ::EntityId entityId : m_selectedEntityIds)
|
||||
for (const AZ::EntityId& entityId : m_selectedEntityIds)
|
||||
{
|
||||
ScopedUndoBatch::MarkEntityDirty(entityId);
|
||||
}
|
||||
|
||||
if (m_mode == Mode::Translation)
|
||||
{
|
||||
const AZ::Vector3 finalSurfacePosition = PickTerrainPosition(mouseInteraction.m_mouseInteraction);
|
||||
const AZ::Vector3 worldPosition = FindClosestPickIntersection(
|
||||
mouseInteraction.m_mouseInteraction.m_interactionId.m_viewportId,
|
||||
mouseInteraction.m_mouseInteraction.m_mousePick.m_screenCoordinates, AzToolsFramework::EditorPickRayLength,
|
||||
GetDefaultEntityPlacementDistance());
|
||||
|
||||
// handle modifier alternatives
|
||||
if (Input::IndividualDitto(mouseInteraction))
|
||||
{
|
||||
CopyTranslationToSelectedEntitiesIndividual(finalSurfacePosition);
|
||||
CopyTranslationToSelectedEntitiesIndividual(worldPosition);
|
||||
}
|
||||
else if (Input::GroupDitto(mouseInteraction))
|
||||
{
|
||||
CopyTranslationToSelectedEntitiesGroup(finalSurfacePosition);
|
||||
CopyTranslationToSelectedEntitiesGroup(worldPosition);
|
||||
}
|
||||
}
|
||||
else if (m_mode == Mode::Rotation)
|
||||
@@ -3361,9 +3353,23 @@ namespace AzToolsFramework
|
||||
|
||||
m_cursorState.Update();
|
||||
|
||||
bool stickySelect = false;
|
||||
ViewportInteraction::ViewportSettingsRequestBus::EventResult(
|
||||
stickySelect, viewportInfo.m_viewportId, &ViewportInteraction::ViewportSettingsRequestBus::Events::StickySelectEnabled);
|
||||
|
||||
HandleAccentsContext handleAccentsContext;
|
||||
handleAccentsContext.m_ctrlHeld = keyboardModifiers.Ctrl();
|
||||
handleAccentsContext.m_hasSelectedEntities = !m_selectedEntityIds.empty();
|
||||
handleAccentsContext.m_usingBoxSelect = m_boxSelect.Active();
|
||||
handleAccentsContext.m_usingStickySelect = stickySelect;
|
||||
|
||||
HandleAccents(
|
||||
!m_selectedEntityIds.empty(), m_cachedEntityIdUnderCursor, keyboardModifiers.Ctrl(), m_hoveredEntityId,
|
||||
ViewportInteraction::BuildMouseButtons(QGuiApplication::mouseButtons()), m_boxSelect.Active());
|
||||
m_currentEntityIdUnderCursor, m_hoveredEntityId, handleAccentsContext,
|
||||
ViewportInteraction::BuildMouseButtons(QGuiApplication::mouseButtons()),
|
||||
[](const AZ::EntityId entityId, bool highlighted)
|
||||
{
|
||||
ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequests::SetEntityHighlighted, entityId, highlighted);
|
||||
});
|
||||
|
||||
const ReferenceFrame referenceFrame = m_spaceCluster.m_spaceLock.value_or(ReferenceFrameFromModifiers(keyboardModifiers));
|
||||
|
||||
@@ -3507,7 +3513,7 @@ namespace AzToolsFramework
|
||||
// get the editor cameras current orientation
|
||||
const int viewportId = viewportInfo.m_viewportId;
|
||||
const AzFramework::CameraState editorCameraState = GetCameraState(viewportId);
|
||||
const AZ::Matrix3x3& editorCameraOrientation = AZ::Matrix3x3::CreateFromMatrix4x4(AzFramework::CameraTransform(editorCameraState));
|
||||
const AZ::Matrix3x3& editorCameraOrientation = AZ::Matrix3x3::CreateFromMatrix3x4(AzFramework::CameraTransform(editorCameraState));
|
||||
|
||||
// create a gizmo camera transform about the origin matching the orientation of the editor camera
|
||||
// (10 units back in the y axis to produce an orbit effect)
|
||||
@@ -3604,6 +3610,17 @@ namespace AzToolsFramework
|
||||
m_selectedEntityIds.clear();
|
||||
m_selectedEntityIds.reserve(selectedEntityIds.size());
|
||||
AZStd::copy(selectedEntityIds.begin(), selectedEntityIds.end(), AZStd::inserter(m_selectedEntityIds, m_selectedEntityIds.end()));
|
||||
|
||||
// Do not create manipulators for the container entity of the focused prefab.
|
||||
if (auto prefabFocusPublicInterface = AZ::Interface<AzToolsFramework::Prefab::PrefabFocusPublicInterface>::Get())
|
||||
{
|
||||
AzFramework::EntityContextId editorEntityContextId = GetEntityContextId();
|
||||
if (AZ::EntityId focusRoot = prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(editorEntityContextId);
|
||||
focusRoot.IsValid())
|
||||
{
|
||||
m_selectedEntityIds.erase(focusRoot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EditorTransformComponentSelection::OnTransformChanged(
|
||||
@@ -3694,7 +3711,8 @@ namespace AzToolsFramework
|
||||
case ViewportEditorMode::Focus:
|
||||
{
|
||||
ViewportUi::ViewportUiRequestBus::Event(
|
||||
ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::CreateViewportBorder, "Focus Mode");
|
||||
ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::CreateViewportBorder, "Focus Mode",
|
||||
LeaveFocusMode);
|
||||
}
|
||||
break;
|
||||
case ViewportEditorMode::Default:
|
||||
@@ -3723,7 +3741,8 @@ namespace AzToolsFramework
|
||||
if (editorModeState.IsModeActive(ViewportEditorMode::Focus))
|
||||
{
|
||||
ViewportUi::ViewportUiRequestBus::Event(
|
||||
ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::CreateViewportBorder, "Focus Mode");
|
||||
ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::CreateViewportBorder, "Focus Mode",
|
||||
LeaveFocusMode);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
+19
-2
@@ -308,7 +308,7 @@ namespace AzToolsFramework
|
||||
bool PerformGroupDitto(AZ::EntityId entityId);
|
||||
bool PerformIndividualDitto(AZ::EntityId entityId);
|
||||
void PerformManipulatorDitto(AZ::EntityId entityId);
|
||||
void PerformSnapToTerrain(const ViewportInteraction::MouseInteractionEvent& mouseInteraction);
|
||||
void PerformSnapToSurface(const ViewportInteraction::MouseInteractionEvent& mouseInteraction);
|
||||
|
||||
//! Responsible for keeping the space cluster in sync with the current reference frame.
|
||||
void UpdateSpaceCluster(ReferenceFrame referenceFrame);
|
||||
@@ -317,7 +317,7 @@ namespace AzToolsFramework
|
||||
void SetAllViewportUiVisible(bool visible);
|
||||
|
||||
AZ::EntityId m_hoveredEntityId; //!< What EntityId is the mouse currently hovering over (if any).
|
||||
AZ::EntityId m_cachedEntityIdUnderCursor; //!< Store the EntityId on each mouse move for use in Display.
|
||||
AZ::EntityId m_currentEntityIdUnderCursor; //!< Store the EntityId on each mouse move for use in Display.
|
||||
AZ::EntityId m_editorCameraComponentEntityId; //!< The EditorCameraComponent EntityId if it is set.
|
||||
EntityIdSet m_selectedEntityIds; //!< Represents the current entities in the selection.
|
||||
|
||||
@@ -357,6 +357,23 @@ namespace AzToolsFramework
|
||||
bool m_viewportUiVisible = true; //!< Used to hide/show the viewport ui elements.
|
||||
};
|
||||
|
||||
//! Bundles viewport state that impacts how accents are added/removed in HandleAccents.
|
||||
struct HandleAccentsContext
|
||||
{
|
||||
bool m_hasSelectedEntities;
|
||||
bool m_ctrlHeld;
|
||||
bool m_usingBoxSelect;
|
||||
bool m_usingStickySelect;
|
||||
};
|
||||
|
||||
//! Updates whether accents (icon highlights) are added/removed for a given entity based on the cursor position.
|
||||
void HandleAccents(
|
||||
AZ::EntityId currentEntityIdUnderCursor,
|
||||
AZ::EntityId& hoveredEntityIdUnderCursor,
|
||||
const HandleAccentsContext& handleAccentsContext,
|
||||
ViewportInteraction::MouseButtons mouseButtons,
|
||||
const AZStd::function<void(AZ::EntityId, bool)>& setEntityAccentedFn);
|
||||
|
||||
//! The ETCS (EntityTransformComponentSelection) namespace contains functions and data used exclusively by
|
||||
//! the EditorTransformComponentSelection type. Functions in this namespace are exposed to facilitate testing
|
||||
//! and should not be used outside of EditorTransformComponentSelection or EditorTransformComponentSelectionTests.
|
||||
|
||||
+3
-5
@@ -293,12 +293,10 @@ namespace AzToolsFramework
|
||||
return m_impl->m_visibleEntityDatas[index].m_iconHidden;
|
||||
}
|
||||
|
||||
bool EditorVisibleEntityDataCache::IsVisibleEntitySelectableInViewport(size_t index) const
|
||||
bool EditorVisibleEntityDataCache::IsVisibleEntityIndividuallySelectableInViewport(const size_t index) const
|
||||
{
|
||||
return m_impl->m_visibleEntityDatas[index].m_visible
|
||||
&& !m_impl->m_visibleEntityDatas[index].m_locked
|
||||
&& m_impl->m_visibleEntityDatas[index].m_inFocus
|
||||
&& !m_impl->m_visibleEntityDatas[index].m_descendantOfClosedContainer;
|
||||
return m_impl->m_visibleEntityDatas[index].m_visible && !m_impl->m_visibleEntityDatas[index].m_locked &&
|
||||
m_impl->m_visibleEntityDatas[index].m_inFocus && !m_impl->m_visibleEntityDatas[index].m_descendantOfClosedContainer;
|
||||
}
|
||||
|
||||
AZStd::optional<size_t> EditorVisibleEntityDataCache::GetVisibleEntityIndexFromId(const AZ::EntityId entityId) const
|
||||
|
||||
+4
-1
@@ -55,7 +55,10 @@ namespace AzToolsFramework
|
||||
bool IsVisibleEntityVisible(size_t index) const;
|
||||
bool IsVisibleEntitySelected(size_t index) const;
|
||||
bool IsVisibleEntityIconHidden(size_t index) const;
|
||||
bool IsVisibleEntitySelectableInViewport(size_t index) const;
|
||||
//! Returns true if the entity is individually selectable (none of its ancestors are a closed container entity).
|
||||
//! @note It may still be desirable to be able to 'click' an entity that is a descendant of a closed container
|
||||
//! to select the container itself, not the individual entity.
|
||||
bool IsVisibleEntityIndividuallySelectableInViewport(size_t index) const;
|
||||
|
||||
AZStd::optional<size_t> GetVisibleEntityIndexFromId(AZ::EntityId entityId) const;
|
||||
|
||||
|
||||
@@ -62,9 +62,6 @@ namespace AzToolsFramework::ViewportUi::Internal
|
||||
return;
|
||||
}
|
||||
|
||||
// set hover to true by default
|
||||
action->setProperty("IconHasHoverEffect", true);
|
||||
|
||||
// add the action
|
||||
addAction(action);
|
||||
|
||||
|
||||
@@ -20,7 +20,9 @@
|
||||
namespace AzToolsFramework::ViewportUi::Internal
|
||||
{
|
||||
const static int HighlightBorderSize = 5;
|
||||
const static char* HighlightBorderColor = "#4A90E2";
|
||||
const static char* const HighlightBorderColor = "#4A90E2";
|
||||
const static int HighlightBorderBackButtonIconSize = 20;
|
||||
const static char* const HighlightBorderBackButtonIconFile = "X_axis.svg";
|
||||
|
||||
static void UnparentWidgets(ViewportUiElementIdInfoLookup& viewportUiElementIdInfoLookup)
|
||||
{
|
||||
@@ -62,6 +64,7 @@ namespace AzToolsFramework::ViewportUi::Internal
|
||||
, m_fullScreenLayout(&m_uiOverlay)
|
||||
, m_uiOverlayLayout()
|
||||
, m_viewportBorderText(&m_uiOverlay)
|
||||
, m_viewportBorderBackButton(&m_uiOverlay)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -254,7 +257,7 @@ namespace AzToolsFramework::ViewportUi::Internal
|
||||
auto viewportUiMapElement = m_viewportUiElements.find(elementId);
|
||||
if (viewportUiMapElement != m_viewportUiElements.end())
|
||||
{
|
||||
viewportUiMapElement->second.m_widget->setVisible(false);
|
||||
viewportUiMapElement->second.m_widget->hide();
|
||||
viewportUiMapElement->second.m_widget->setParent(nullptr);
|
||||
m_viewportUiElements.erase(viewportUiMapElement);
|
||||
}
|
||||
@@ -269,7 +272,7 @@ namespace AzToolsFramework::ViewportUi::Internal
|
||||
{
|
||||
if (ViewportUiElementInfo element = GetViewportUiElementInfo(elementId); element.m_widget)
|
||||
{
|
||||
element.m_widget->setVisible(true);
|
||||
element.m_widget->show();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -277,7 +280,7 @@ namespace AzToolsFramework::ViewportUi::Internal
|
||||
{
|
||||
if (ViewportUiElementInfo element = GetViewportUiElementInfo(elementId); element.m_widget)
|
||||
{
|
||||
element.m_widget->setVisible(false);
|
||||
element.m_widget->hide();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -291,27 +294,34 @@ namespace AzToolsFramework::ViewportUi::Internal
|
||||
return false;
|
||||
}
|
||||
|
||||
void ViewportUiDisplay::CreateViewportBorder(const AZStd::string& borderTitle)
|
||||
void ViewportUiDisplay::CreateViewportBorder(
|
||||
const AZStd::string& borderTitle, AZStd::optional<ViewportUiBackButtonCallback> backButtonCallback)
|
||||
{
|
||||
const AZStd::string styleSheet = AZStd::string::format(
|
||||
"border: %dpx solid %s; border-top: %dpx solid %s;", HighlightBorderSize, HighlightBorderColor, ViewportUiTopBorderSize,
|
||||
HighlightBorderColor);
|
||||
m_uiOverlay.setStyleSheet(styleSheet.c_str());
|
||||
m_uiOverlay.setStyleSheet(QString("border: %1px solid %2; border-top: %3px solid %4;")
|
||||
.arg(
|
||||
QString::number(HighlightBorderSize), HighlightBorderColor,
|
||||
QString::number(ViewportUiTopBorderSize), HighlightBorderColor));
|
||||
m_uiOverlayLayout.setContentsMargins(
|
||||
HighlightBorderSize + ViewportUiOverlayMargin, ViewportUiTopBorderSize + ViewportUiOverlayMargin,
|
||||
HighlightBorderSize + ViewportUiOverlayMargin, HighlightBorderSize + ViewportUiOverlayMargin);
|
||||
m_viewportBorderText.setVisible(true);
|
||||
m_viewportBorderText.show();
|
||||
m_viewportBorderText.setText(borderTitle.c_str());
|
||||
UpdateUiOverlayGeometry();
|
||||
|
||||
// only display the back button if a callback was provided
|
||||
m_viewportBorderBackButtonCallback = backButtonCallback;
|
||||
m_viewportBorderBackButton.setVisible(m_viewportBorderBackButtonCallback.has_value());
|
||||
}
|
||||
|
||||
void ViewportUiDisplay::RemoveViewportBorder()
|
||||
{
|
||||
m_viewportBorderText.setVisible(false);
|
||||
m_viewportBorderText.hide();
|
||||
m_uiOverlay.setStyleSheet("border: none;");
|
||||
m_uiOverlayLayout.setContentsMargins(
|
||||
ViewportUiOverlayMargin, ViewportUiOverlayMargin + ViewportUiOverlayTopMarginPadding, ViewportUiOverlayMargin,
|
||||
ViewportUiOverlayMargin);
|
||||
m_viewportBorderBackButtonCallback.reset();
|
||||
m_viewportBorderBackButton.hide();
|
||||
}
|
||||
|
||||
void ViewportUiDisplay::PositionViewportUiElementFromWorldSpace(ViewportUiElementId elementId, const AZ::Vector3& pos)
|
||||
@@ -350,23 +360,46 @@ namespace AzToolsFramework::ViewportUi::Internal
|
||||
{
|
||||
m_uiMainWindow.setObjectName(QString("ViewportUiWindow"));
|
||||
ConfigureWindowForViewportUi(&m_uiMainWindow);
|
||||
m_uiMainWindow.setVisible(false);
|
||||
m_uiMainWindow.hide();
|
||||
|
||||
m_uiOverlay.setObjectName(QString("ViewportUiOverlay"));
|
||||
m_uiMainWindow.setCentralWidget(&m_uiOverlay);
|
||||
m_uiOverlay.setVisible(false);
|
||||
m_uiOverlay.hide();
|
||||
|
||||
// remove any spacing and margins from the UI Overlay Layout
|
||||
m_fullScreenLayout.setSpacing(0);
|
||||
m_fullScreenLayout.setContentsMargins(0, 0, 0, 0);
|
||||
m_fullScreenLayout.addLayout(&m_uiOverlayLayout, 0, 0, 1, 1);
|
||||
|
||||
// format the label which will appear on top of the highlight border
|
||||
AZStd::string styleSheet = AZStd::string::format("background-color: %s; border: none;", HighlightBorderColor);
|
||||
m_viewportBorderText.setStyleSheet(styleSheet.c_str());
|
||||
// style the label which will appear on top of the highlight border
|
||||
m_viewportBorderText.setStyleSheet(QString("background-color: %1; border: none").arg(HighlightBorderColor));
|
||||
m_viewportBorderText.setFixedHeight(ViewportUiTopBorderSize);
|
||||
m_viewportBorderText.setVisible(false);
|
||||
m_viewportBorderText.hide();
|
||||
m_fullScreenLayout.addWidget(&m_viewportBorderText, 0, 0, Qt::AlignTop | Qt::AlignHCenter);
|
||||
|
||||
m_viewportBorderBackButton.setAutoRaise(true); // hover highlight
|
||||
m_viewportBorderBackButton.hide();
|
||||
|
||||
QIcon backButtonIcon(QString(":/stylesheet/img/UI20/toolbar/%1").arg(HighlightBorderBackButtonIconFile));
|
||||
m_viewportBorderBackButton.setIcon(backButtonIcon);
|
||||
m_viewportBorderBackButton.setIconSize(QSize(HighlightBorderBackButtonIconSize, HighlightBorderBackButtonIconSize));
|
||||
|
||||
// setup the handler for the back button to call the user provided callback (if any)
|
||||
QObject::connect(
|
||||
&m_viewportBorderBackButton, &QToolButton::clicked,
|
||||
[this]
|
||||
{
|
||||
if (m_viewportBorderBackButtonCallback.has_value())
|
||||
{
|
||||
// we need to swap out the existing back button callback because it will be reset in RemoveViewportBorder()
|
||||
// so preserve the lifetime with this temporary callback until after the call to RemoveViewportBorder()
|
||||
AZStd::optional<ViewportUiBackButtonCallback> backButtonCallback;
|
||||
m_viewportBorderBackButtonCallback.swap(backButtonCallback);
|
||||
RemoveViewportBorder();
|
||||
(*backButtonCallback)();
|
||||
}
|
||||
});
|
||||
m_fullScreenLayout.addWidget(&m_viewportBorderBackButton, 0, 0, Qt::AlignTop | Qt::AlignRight);
|
||||
}
|
||||
|
||||
void ViewportUiDisplay::PrepareWidgetForViewportUi(QPointer<QWidget> widget)
|
||||
@@ -414,16 +447,9 @@ namespace AzToolsFramework::ViewportUi::Internal
|
||||
region += m_uiOverlay.childrenRegion();
|
||||
|
||||
// set viewport ui visibility depending on if elements are present
|
||||
if (region.isEmpty() || !UiDisplayEnabled())
|
||||
{
|
||||
m_uiMainWindow.setVisible(false);
|
||||
m_uiOverlay.setVisible(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_uiMainWindow.setVisible(true);
|
||||
m_uiOverlay.setVisible(true);
|
||||
}
|
||||
const bool visible = !region.isEmpty() && UiDisplayEnabled();
|
||||
m_uiMainWindow.setVisible(visible);
|
||||
m_uiOverlay.setVisible(visible);
|
||||
|
||||
m_uiMainWindow.setMask(region);
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <QLabel>
|
||||
#include <QMainWindow>
|
||||
#include <QPointer>
|
||||
#include <QToolButton>
|
||||
|
||||
AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option")
|
||||
#include <QGridLayout>
|
||||
@@ -89,7 +90,7 @@ namespace AzToolsFramework::ViewportUi::Internal
|
||||
AZStd::shared_ptr<QWidget> GetViewportUiElement(ViewportUiElementId elementId);
|
||||
bool IsViewportUiElementVisible(ViewportUiElementId elementId);
|
||||
|
||||
void CreateViewportBorder(const AZStd::string& borderTitle);
|
||||
void CreateViewportBorder(const AZStd::string& borderTitle, AZStd::optional<ViewportUiBackButtonCallback> backButtonCallback);
|
||||
void RemoveViewportBorder();
|
||||
|
||||
private:
|
||||
@@ -113,7 +114,10 @@ namespace AzToolsFramework::ViewportUi::Internal
|
||||
QWidget m_uiOverlay; //!< The UI Overlay which displays Viewport UI Elements.
|
||||
QGridLayout m_fullScreenLayout; //!< The layout which extends across the full screen.
|
||||
ViewportUiDisplayLayout m_uiOverlayLayout; //!< The layout used for optionally anchoring Viewport UI Elements.
|
||||
QLabel m_viewportBorderText; //!< The text used for the viewport border.
|
||||
QLabel m_viewportBorderText; //!< The text used for the viewport highlight border.
|
||||
QToolButton m_viewportBorderBackButton; //!< The button to return from the viewport highlight border (only displayed if callback provided).
|
||||
//! The optional callback for when the viewport highlight border back button is pressed.
|
||||
AZStd::optional<ViewportUiBackButtonCallback> m_viewportBorderBackButtonCallback;
|
||||
|
||||
QWidget* m_renderOverlay;
|
||||
QPointer<QWidget> m_fullScreenWidget; //!< Reference to the widget attached to m_fullScreenLayout if any.
|
||||
|
||||
@@ -240,9 +240,10 @@ namespace AzToolsFramework::ViewportUi
|
||||
}
|
||||
}
|
||||
|
||||
void ViewportUiManager::CreateViewportBorder(const AZStd::string& borderTitle)
|
||||
void ViewportUiManager::CreateViewportBorder(
|
||||
const AZStd::string& borderTitle, AZStd::optional<ViewportUiBackButtonCallback> backButtonCallback)
|
||||
{
|
||||
m_viewportUi->CreateViewportBorder(borderTitle);
|
||||
m_viewportUi->CreateViewportBorder(borderTitle, backButtonCallback);
|
||||
}
|
||||
|
||||
void ViewportUiManager::RemoveViewportBorder()
|
||||
|
||||
@@ -50,7 +50,8 @@ namespace AzToolsFramework::ViewportUi
|
||||
void RegisterTextFieldCallback(TextFieldId textFieldId, AZ::Event<AZStd::string>::Handler& handler) override;
|
||||
void RemoveTextField(TextFieldId textFieldId) override;
|
||||
void SetTextFieldVisible(TextFieldId textFieldId, bool visible) override;
|
||||
void CreateViewportBorder(const AZStd::string& borderTitle) override;
|
||||
void CreateViewportBorder(
|
||||
const AZStd::string& borderTitle, AZStd::optional<ViewportUiBackButtonCallback> backButtonCallback) override;
|
||||
void RemoveViewportBorder() override;
|
||||
void PressButton(ClusterId clusterId, ButtonId buttonId) override;
|
||||
void PressButton(SwitcherId switcherId, ButtonId buttonId) override;
|
||||
|
||||
@@ -22,6 +22,9 @@ namespace AzToolsFramework::ViewportUi
|
||||
using SwitcherId = IdType<struct SwitcherIdType>;
|
||||
using TextFieldId = IdType<struct TextFieldIdType>;
|
||||
|
||||
//! Callback function for viewport UI back button.
|
||||
using ViewportUiBackButtonCallback = AZStd::function<void()>;
|
||||
|
||||
inline const ViewportUiElementId InvalidViewportUiElementId = ViewportUiElementId(0);
|
||||
inline const ButtonId InvalidButtonId = ButtonId(0);
|
||||
inline const ClusterId InvalidClusterId = ClusterId(0);
|
||||
@@ -95,9 +98,9 @@ namespace AzToolsFramework::ViewportUi
|
||||
virtual void RemoveTextField(TextFieldId textFieldId) = 0;
|
||||
//! Sets the visibility of the text field.
|
||||
virtual void SetTextFieldVisible(TextFieldId textFieldId, bool visible) = 0;
|
||||
//! Create the highlight border for Component Mode.
|
||||
virtual void CreateViewportBorder(const AZStd::string& borderTitle) = 0;
|
||||
//! Remove the highlight border for Component Mode.
|
||||
//! Create the highlight border with optional back button to exit the given editor mode.
|
||||
virtual void CreateViewportBorder(const AZStd::string& borderTitle, AZStd::optional<ViewportUiBackButtonCallback> backButtonCallback) = 0;
|
||||
//! Remove the highlight border.
|
||||
virtual void RemoveViewportBorder() = 0;
|
||||
//! Invoke a button press on a cluster.
|
||||
virtual void PressButton(ClusterId clusterId, ButtonId buttonId) = 0;
|
||||
|
||||
@@ -22,8 +22,6 @@ namespace AzToolsFramework::ViewportUi::Internal
|
||||
|
||||
// Add am empty active button (is set in the call to SetActiveMode)
|
||||
m_activeButton = new QToolButton();
|
||||
// No hover effect for the main button as it's not clickable
|
||||
m_activeButton->setProperty("IconHasHoverEffect", false);
|
||||
m_activeButton->setCheckable(false);
|
||||
m_activeButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
|
||||
addWidget(m_activeButton);
|
||||
@@ -56,9 +54,6 @@ namespace AzToolsFramework::ViewportUi::Internal
|
||||
return;
|
||||
}
|
||||
|
||||
// set hover to true by default
|
||||
action->setProperty("IconHasHoverEffect", true);
|
||||
|
||||
// add the action
|
||||
addAction(action);
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@ set(FILES
|
||||
API/EntityCompositionRequestBus.h
|
||||
API/EntityCompositionNotificationBus.h
|
||||
API/EditorViewportIconDisplayInterface.h
|
||||
API/PythonLoader.h
|
||||
API/ViewPaneOptions.h
|
||||
API/ViewportEditorModeTrackerInterface.h
|
||||
Application/Ticker.h
|
||||
@@ -147,6 +148,8 @@ set(FILES
|
||||
Entity/EditorEntitySortBus.h
|
||||
Entity/EditorEntitySortComponent.cpp
|
||||
Entity/EditorEntitySortComponent.h
|
||||
Entity/EditorEntitySortComponentSerializer.cpp
|
||||
Entity/EditorEntitySortComponentSerializer.h
|
||||
Entity/EditorEntityTransformBus.h
|
||||
Entity/PrefabEditorEntityOwnershipInterface.h
|
||||
Entity/PrefabEditorEntityOwnershipService.h
|
||||
@@ -156,6 +159,10 @@ set(FILES
|
||||
Entity/SliceEditorEntityOwnershipServiceBus.h
|
||||
Entity/EntityUtilityComponent.h
|
||||
Entity/EntityUtilityComponent.cpp
|
||||
Entity/ReadOnly/ReadOnlyEntityInterface.h
|
||||
Entity/ReadOnly/ReadOnlyEntityBus.h
|
||||
Entity/ReadOnly/ReadOnlyEntitySystemComponent.cpp
|
||||
Entity/ReadOnly/ReadOnlyEntitySystemComponent.h
|
||||
Fingerprinting/TypeFingerprinter.h
|
||||
Fingerprinting/TypeFingerprinter.cpp
|
||||
FocusMode/FocusModeInterface.h
|
||||
@@ -768,8 +775,8 @@ set(FILES
|
||||
PythonTerminal/ScriptTermDialog.cpp
|
||||
PythonTerminal/ScriptTermDialog.h
|
||||
PythonTerminal/ScriptTermDialog.ui
|
||||
Input/QtEventToAzInputManager.h
|
||||
Input/QtEventToAzInputManager.cpp
|
||||
Input/QtEventToAzInputMapper.h
|
||||
Input/QtEventToAzInputMapper.cpp
|
||||
Script/LuaSymbolsReporterBus.h
|
||||
Script/LuaSymbolsReporterSystemComponent.h
|
||||
Script/LuaSymbolsReporterSystemComponent.cpp
|
||||
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzToolsFramework/API/PythonLoader.h>
|
||||
|
||||
namespace AzToolsFramework::EmbeddedPython
|
||||
{
|
||||
PythonLoader::PythonLoader()
|
||||
{
|
||||
}
|
||||
|
||||
PythonLoader::~PythonLoader()
|
||||
{
|
||||
}
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzToolsFramework/API/PythonLoader.h>
|
||||
#include <AzCore/Debug/Trace.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
namespace AzToolsFramework::EmbeddedPython
|
||||
{
|
||||
PythonLoader::PythonLoader()
|
||||
{
|
||||
constexpr char libPythonName[] = "libpython3.7m.so.1.0";
|
||||
m_embeddedLibPythonHandle = dlopen(libPythonName, RTLD_NOW | RTLD_GLOBAL);
|
||||
if (m_embeddedLibPythonHandle == nullptr)
|
||||
{
|
||||
[[maybe_unused]] const char* err = dlerror();
|
||||
AZ_Error("PythonLoader", false, "Failed to load %s with error: %s\n", libPythonName, err ? err : "Unknown Error");
|
||||
}
|
||||
}
|
||||
|
||||
PythonLoader::~PythonLoader()
|
||||
{
|
||||
if (m_embeddedLibPythonHandle)
|
||||
{
|
||||
dlclose(m_embeddedLibPythonHandle);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace AzToolsFramework::EmbeddedPython
|
||||
@@ -7,4 +7,5 @@
|
||||
#
|
||||
|
||||
set(FILES
|
||||
AzToolsFramework/API/PythonLoader_Linux.cpp
|
||||
)
|
||||
|
||||
@@ -7,4 +7,5 @@
|
||||
#
|
||||
|
||||
set(FILES
|
||||
../Common/Default/AzToolsFramework/API/PythonLoader_Default.cpp
|
||||
)
|
||||
|
||||
@@ -7,4 +7,5 @@
|
||||
#
|
||||
|
||||
set(FILES
|
||||
../Common/Default/AzToolsFramework/API/PythonLoader_Default.cpp
|
||||
)
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
|
||||
#include <Tests/BoundsTestComponent.h>
|
||||
|
||||
#include <AzCore/Math/Obb.h>
|
||||
#include <AzCore/Math/IntersectSegment.h>
|
||||
#include <AzToolsFramework/ViewportSelection/EditorSelectionUtil.h>
|
||||
|
||||
namespace UnitTest
|
||||
@@ -40,6 +42,9 @@ namespace UnitTest
|
||||
{
|
||||
AzFramework::BoundsRequestBus::Handler::BusConnect(GetEntityId());
|
||||
AzToolsFramework::EditorComponentSelectionRequestsBus::Handler::BusConnect(GetEntityId());
|
||||
|
||||
// default local bounds to unit cube
|
||||
m_localBounds = AZ::Aabb::CreateFromMinMax(AZ::Vector3(-0.5f), AZ::Vector3(0.5f));
|
||||
}
|
||||
|
||||
void BoundsTestComponent::Deactivate()
|
||||
@@ -57,7 +62,53 @@ namespace UnitTest
|
||||
|
||||
AZ::Aabb BoundsTestComponent::GetLocalBounds()
|
||||
{
|
||||
return AZ::Aabb::CreateFromMinMax(AZ::Vector3(-0.5f), AZ::Vector3(0.5f));
|
||||
return m_localBounds;
|
||||
}
|
||||
|
||||
void RenderGeometryIntersectionTestComponent::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
|
||||
{
|
||||
serializeContext->Class<RenderGeometryIntersectionTestComponent, BoundsTestComponent>()->Version(1);
|
||||
}
|
||||
}
|
||||
|
||||
void RenderGeometryIntersectionTestComponent::Activate()
|
||||
{
|
||||
BoundsTestComponent::Activate();
|
||||
|
||||
const AZ::EntityId entityId = GetEntityId();
|
||||
AzFramework::EntityContextId contextId = AzFramework::EntityContextId::CreateNull();
|
||||
AzFramework::EntityIdContextQueryBus::EventResult(contextId, entityId, &AzFramework::EntityIdContextQueries::GetOwningContextId);
|
||||
AzFramework::RenderGeometry::IntersectionRequestBus::Handler::BusConnect({entityId, contextId});
|
||||
}
|
||||
|
||||
void RenderGeometryIntersectionTestComponent::Deactivate()
|
||||
{
|
||||
AzFramework::RenderGeometry::IntersectionRequestBus::Handler::BusDisconnect();
|
||||
BoundsTestComponent::Deactivate();
|
||||
}
|
||||
|
||||
AzFramework::RenderGeometry::RayResult RenderGeometryIntersectionTestComponent::RenderGeometryIntersect(
|
||||
const AzFramework::RenderGeometry::RayRequest& ray)
|
||||
{
|
||||
AZ::Transform worldFromLocal = AZ::Transform::CreateIdentity();
|
||||
AZ::TransformBus::EventResult(worldFromLocal, GetEntityId(), &AZ::TransformBus::Events::GetWorldTM);
|
||||
|
||||
AzFramework::RenderGeometry::RayResult rayResult;
|
||||
|
||||
float t = 0.0f;
|
||||
const AZ::Obb obb = GetLocalBounds().GetTransformedObb(worldFromLocal);
|
||||
const AZ::Vector3 rayDirection = ray.m_endWorldPosition - ray.m_startWorldPosition;
|
||||
if (AZ::Intersect::IntersectRayObb(ray.m_startWorldPosition, rayDirection, obb, t))
|
||||
{
|
||||
rayResult.m_worldPosition = ray.m_startWorldPosition + rayDirection * t;
|
||||
rayResult.m_entityAndComponent = AZ::EntityComponentIdPair(GetEntityId(), GetId());
|
||||
rayResult.m_distance = t;
|
||||
rayResult.m_uv = AZ::Vector2::CreateZero();
|
||||
rayResult.m_worldNormal = AZ::Vector3::CreateZero();
|
||||
}
|
||||
|
||||
return rayResult;
|
||||
}
|
||||
} // namespace UnitTest
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzFramework/Render/GeometryIntersectionBus.h>
|
||||
#include <AzFramework/Visibility/BoundsBus.h>
|
||||
#include <AzToolsFramework/API/ComponentEntitySelectionBus.h>
|
||||
#include <AzToolsFramework/ToolsComponents/EditorComponentBase.h>
|
||||
@@ -41,5 +42,24 @@ namespace UnitTest
|
||||
// BoundsRequestBus overrides ...
|
||||
AZ::Aabb GetWorldBounds() override;
|
||||
AZ::Aabb GetLocalBounds() override;
|
||||
|
||||
AZ::Aabb m_localBounds; //!< Local bounds that can be modified for certain tests (defaults to unit cube).
|
||||
};
|
||||
|
||||
class RenderGeometryIntersectionTestComponent
|
||||
: public BoundsTestComponent
|
||||
, public AzFramework::RenderGeometry::IntersectionRequestBus::Handler
|
||||
{
|
||||
public:
|
||||
AZ_EDITOR_COMPONENT(RenderGeometryIntersectionTestComponent, "{6F46B5BF-60DF-4BDD-9BA7-9658E85B99C2}", BoundsTestComponent);
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
// AZ::Component overrides ...
|
||||
void Activate() override;
|
||||
void Deactivate() override;
|
||||
|
||||
// IntersectionRequestBus overrides ...
|
||||
AzFramework::RenderGeometry::RayResult RenderGeometryIntersect(const AzFramework::RenderGeometry::RayRequest& ray) override;
|
||||
};
|
||||
} // namespace UnitTest
|
||||
|
||||
@@ -1116,7 +1116,6 @@ namespace UnitTest
|
||||
SerializeContext* GetSerializeContext() override { return m_serializeContext.get(); }
|
||||
BehaviorContext* GetBehaviorContext() override { return nullptr; }
|
||||
JsonRegistrationContext* GetJsonRegistrationContext() override { return nullptr; }
|
||||
const char* GetAppRoot() const override { return nullptr; }
|
||||
const char* GetEngineRoot() const override { return nullptr; }
|
||||
const char* GetExecutableFolder() const override { return nullptr; }
|
||||
void EnumerateEntities(const EntityCallback& /*callback*/) override {}
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
#include <AzToolsFramework/ViewportSelection/EditorVisibleEntityDataCache.h>
|
||||
#include <AzToolsFramework/ViewportUi/ViewportUiManager.h>
|
||||
|
||||
#include<Tests/BoundsTestComponent.h>
|
||||
#include <Tests/BoundsTestComponent.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
@@ -493,12 +493,8 @@ namespace UnitTest
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Then
|
||||
AzToolsFramework::EntityIdList selectedEntities;
|
||||
AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(
|
||||
selectedEntities, &AzToolsFramework::ToolsApplicationRequestBus::Events::GetSelectedEntities);
|
||||
|
||||
AzToolsFramework::EntityIdList expectedSelectedEntities = { entity4, entity5, entity6 };
|
||||
|
||||
const AzToolsFramework::EntityIdList selectedEntities = SelectedEntities();
|
||||
const AzToolsFramework::EntityIdList expectedSelectedEntities = { entity4, entity5, entity6 };
|
||||
EXPECT_THAT(selectedEntities, UnorderedElementsAreArray(expectedSelectedEntities));
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
}
|
||||
@@ -527,12 +523,8 @@ namespace UnitTest
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Then
|
||||
AzToolsFramework::EntityIdList selectedEntities;
|
||||
AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(
|
||||
selectedEntities, &AzToolsFramework::ToolsApplicationRequestBus::Events::GetSelectedEntities);
|
||||
|
||||
AzToolsFramework::EntityIdList expectedSelectedEntities = { m_entityId1, entity2, entity3, entity4 };
|
||||
|
||||
const AzToolsFramework::EntityIdList selectedEntities = SelectedEntities();
|
||||
const AzToolsFramework::EntityIdList expectedSelectedEntities = { m_entityId1, entity2, entity3, entity4 };
|
||||
EXPECT_THAT(selectedEntities, UnorderedElementsAreArray(expectedSelectedEntities));
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
}
|
||||
@@ -946,6 +938,42 @@ namespace UnitTest
|
||||
EXPECT_THAT(selectedEntitiesAfter, UnorderedElementsAre(m_entityId1));
|
||||
}
|
||||
|
||||
TEST_F(
|
||||
EditorTransformComponentSelectionViewportPickingManipulatorTestFixture, BoundsBetweenCameraAndNearClipPlaneDoesNotIntersectMouseRay)
|
||||
{
|
||||
// move camera to 10 units along the y-axis
|
||||
AzFramework::SetCameraTransform(m_cameraState, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(10.0f)));
|
||||
|
||||
// send a very narrow bounds for entity1
|
||||
AZ::Entity* entity1 = AzToolsFramework::GetEntityById(m_entityId1);
|
||||
auto* boundTestComponent = entity1->FindComponent<BoundsTestComponent>();
|
||||
boundTestComponent->m_localBounds =
|
||||
AZ::Aabb::CreateFromMinMax(AZ::Vector3(-0.5f, -0.0025f, -0.5f), AZ::Vector3(0.5f, 0.0025f, 0.5f));
|
||||
|
||||
// move entity1 in front of the camera between it and the near clip plane
|
||||
AZ::TransformBus::Event(
|
||||
m_entityId1, &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(10.05f)));
|
||||
// move entity2 behind entity1
|
||||
AZ::TransformBus::Event(
|
||||
m_entityId2, &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(15.0f)));
|
||||
|
||||
const auto entity2ScreenPosition = AzFramework::WorldToScreen(AzToolsFramework::GetWorldTranslation(m_entityId2), m_cameraState);
|
||||
|
||||
// click the entity in the viewport
|
||||
m_actionDispatcher->SetStickySelect(true)
|
||||
->CameraState(m_cameraState)
|
||||
->MousePosition(entity2ScreenPosition)
|
||||
->CameraState(m_cameraState)
|
||||
->MouseLButtonDown()
|
||||
->MouseLButtonUp();
|
||||
|
||||
// ensure entity1 is not selected as it is before the near clip plane
|
||||
using ::testing::UnorderedElementsAreArray;
|
||||
const AzToolsFramework::EntityIdList selectedEntities = SelectedEntities();
|
||||
const AzToolsFramework::EntityIdList expectedSelectedEntities = { m_entityId2 };
|
||||
EXPECT_THAT(selectedEntities, UnorderedElementsAreArray(expectedSelectedEntities));
|
||||
}
|
||||
|
||||
class EditorTransformComponentSelectionViewportPickingManipulatorTestFixtureParam
|
||||
: public EditorTransformComponentSelectionViewportPickingManipulatorTestFixture
|
||||
, public ::testing::WithParamInterface<bool>
|
||||
@@ -1635,7 +1663,7 @@ namespace UnitTest
|
||||
const AZ::Transform finalEntityTransform = AzToolsFramework::GetWorldTransform(m_entityId1);
|
||||
|
||||
// ensure final world positions match
|
||||
EXPECT_TRUE(finalEntityTransform.IsClose(finalTransformWorld, 0.01f));
|
||||
EXPECT_THAT(finalEntityTransform, IsCloseTolerance(finalTransformWorld, 0.01f));
|
||||
}
|
||||
|
||||
TEST_F(EditorTransformComponentSelectionManipulatorTestFixture, TranslatingEntityWithLinearManipulatorNotifiesOnEntityTransformChanged)
|
||||
@@ -2753,4 +2781,368 @@ namespace UnitTest
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
}
|
||||
|
||||
TEST(HandleAccents, CurrentValidEntityIdBecomesHoveredWithNoSelectionAndUnstickySelect)
|
||||
{
|
||||
namespace azvi = AzToolsFramework::ViewportInteraction;
|
||||
|
||||
const AZ::EntityId currentEntityId = AZ::EntityId(12345);
|
||||
AZ::EntityId hoveredEntityEntityId;
|
||||
|
||||
AzToolsFramework::HandleAccentsContext handleAccentsContext;
|
||||
handleAccentsContext.m_ctrlHeld = false;
|
||||
handleAccentsContext.m_hasSelectedEntities = false;
|
||||
handleAccentsContext.m_usingBoxSelect = false;
|
||||
handleAccentsContext.m_usingStickySelect = false;
|
||||
|
||||
bool currentEntityIdAccentAdded = false;
|
||||
AzToolsFramework::HandleAccents(
|
||||
currentEntityId, hoveredEntityEntityId, handleAccentsContext, azvi::MouseButtonsFromButton(azvi::MouseButton::None),
|
||||
[¤tEntityIdAccentAdded, currentEntityId](const AZ::EntityId entityId, const bool accent)
|
||||
{
|
||||
if (entityId == currentEntityId && accent)
|
||||
{
|
||||
currentEntityIdAccentAdded = true;
|
||||
}
|
||||
});
|
||||
|
||||
using ::testing::Eq;
|
||||
using ::testing::IsTrue;
|
||||
EXPECT_THAT(currentEntityId, Eq(hoveredEntityEntityId));
|
||||
EXPECT_THAT(currentEntityIdAccentAdded, IsTrue());
|
||||
}
|
||||
|
||||
TEST(HandleAccents, CurrentValidEntityIdBecomesHoveredWithSelectionAndUnstickySelect)
|
||||
{
|
||||
namespace azvi = AzToolsFramework::ViewportInteraction;
|
||||
|
||||
const AZ::EntityId currentEntityId = AZ::EntityId(12345);
|
||||
AZ::EntityId hoveredEntityEntityId;
|
||||
|
||||
AzToolsFramework::HandleAccentsContext handleAccentsContext;
|
||||
handleAccentsContext.m_ctrlHeld = false;
|
||||
handleAccentsContext.m_hasSelectedEntities = true;
|
||||
handleAccentsContext.m_usingBoxSelect = false;
|
||||
handleAccentsContext.m_usingStickySelect = false;
|
||||
|
||||
bool currentEntityIdAccentAdded = false;
|
||||
AzToolsFramework::HandleAccents(
|
||||
currentEntityId, hoveredEntityEntityId, handleAccentsContext, azvi::MouseButtonsFromButton(azvi::MouseButton::None),
|
||||
[¤tEntityIdAccentAdded, currentEntityId](const AZ::EntityId entityId, const bool accent)
|
||||
{
|
||||
if (entityId == currentEntityId && accent)
|
||||
{
|
||||
currentEntityIdAccentAdded = true;
|
||||
}
|
||||
});
|
||||
|
||||
using ::testing::Eq;
|
||||
using ::testing::IsTrue;
|
||||
EXPECT_THAT(currentEntityId, Eq(hoveredEntityEntityId));
|
||||
EXPECT_THAT(currentEntityIdAccentAdded, IsTrue());
|
||||
}
|
||||
|
||||
TEST(HandleAccents, CurrentValidEntityIdDoesNotBecomeHoveredWithSelectionUnstickySelectAndInvalidButton)
|
||||
{
|
||||
namespace azvi = AzToolsFramework::ViewportInteraction;
|
||||
|
||||
const AZ::EntityId currentEntityId = AZ::EntityId(12345);
|
||||
AZ::EntityId hoveredEntityEntityId = AZ::EntityId(54321);
|
||||
|
||||
AzToolsFramework::HandleAccentsContext handleAccentsContext;
|
||||
handleAccentsContext.m_ctrlHeld = false;
|
||||
handleAccentsContext.m_hasSelectedEntities = false;
|
||||
handleAccentsContext.m_usingBoxSelect = false;
|
||||
handleAccentsContext.m_usingStickySelect = false;
|
||||
|
||||
bool hoveredEntityIdAccentRemoved = false;
|
||||
AzToolsFramework::HandleAccents(
|
||||
currentEntityId, hoveredEntityEntityId, handleAccentsContext, azvi::MouseButtonsFromButton(azvi::MouseButton::Middle),
|
||||
[&hoveredEntityIdAccentRemoved, hoveredEntityEntityId](const AZ::EntityId entityId, const bool accent)
|
||||
{
|
||||
if (entityId == hoveredEntityEntityId && !accent)
|
||||
{
|
||||
hoveredEntityIdAccentRemoved = true;
|
||||
}
|
||||
});
|
||||
|
||||
using ::testing::Eq;
|
||||
using ::testing::IsFalse;
|
||||
using ::testing::IsTrue;
|
||||
EXPECT_THAT(hoveredEntityEntityId.IsValid(), IsFalse());
|
||||
EXPECT_THAT(hoveredEntityIdAccentRemoved, IsTrue());
|
||||
}
|
||||
|
||||
TEST(HandleAccents, CurrentValidEntityIdDoesNotBecomeHoveredWithSelectionUnstickySelectAndDoingBoxSelect)
|
||||
{
|
||||
namespace azvi = AzToolsFramework::ViewportInteraction;
|
||||
|
||||
const AZ::EntityId currentEntityId = AZ::EntityId(12345);
|
||||
AZ::EntityId hoveredEntityEntityId = AZ::EntityId(54321);
|
||||
|
||||
AzToolsFramework::HandleAccentsContext handleAccentsContext;
|
||||
handleAccentsContext.m_ctrlHeld = false;
|
||||
handleAccentsContext.m_hasSelectedEntities = false;
|
||||
handleAccentsContext.m_usingBoxSelect = true;
|
||||
handleAccentsContext.m_usingStickySelect = false;
|
||||
|
||||
bool hoveredEntityIdAccentRemoved = false;
|
||||
AzToolsFramework::HandleAccents(
|
||||
currentEntityId, hoveredEntityEntityId, handleAccentsContext, azvi::MouseButtonsFromButton(azvi::MouseButton::None),
|
||||
[&hoveredEntityIdAccentRemoved, hoveredEntityEntityId](const AZ::EntityId entityId, const bool accent)
|
||||
{
|
||||
if (entityId == hoveredEntityEntityId && !accent)
|
||||
{
|
||||
hoveredEntityIdAccentRemoved = true;
|
||||
}
|
||||
});
|
||||
|
||||
using ::testing::Eq;
|
||||
using ::testing::IsFalse;
|
||||
using ::testing::IsTrue;
|
||||
EXPECT_THAT(hoveredEntityEntityId.IsValid(), IsFalse());
|
||||
EXPECT_THAT(hoveredEntityIdAccentRemoved, IsTrue());
|
||||
}
|
||||
|
||||
// mimics the mouse moving off of hovered entity onto a new entity with sticky select enabled
|
||||
TEST(HandleAccents, CurrentValidEntityIdDoesNotBecomeHoveredWithSelectionAndStickySelect)
|
||||
{
|
||||
namespace azvi = AzToolsFramework::ViewportInteraction;
|
||||
|
||||
const AZ::EntityId currentEntityId = AZ::EntityId(12345);
|
||||
AZ::EntityId hoveredEntityEntityId = AZ::EntityId(54321);
|
||||
|
||||
AzToolsFramework::HandleAccentsContext handleAccentsContext;
|
||||
handleAccentsContext.m_ctrlHeld = false;
|
||||
handleAccentsContext.m_hasSelectedEntities = true;
|
||||
handleAccentsContext.m_usingBoxSelect = false;
|
||||
handleAccentsContext.m_usingStickySelect = true;
|
||||
|
||||
bool hoveredEntityIdAccentRemoved = false;
|
||||
AzToolsFramework::HandleAccents(
|
||||
currentEntityId, hoveredEntityEntityId, handleAccentsContext, azvi::MouseButtonsFromButton(azvi::MouseButton::None),
|
||||
[&hoveredEntityIdAccentRemoved, hoveredEntityEntityId](const AZ::EntityId entityId, const bool accent)
|
||||
{
|
||||
if (entityId == hoveredEntityEntityId && !accent)
|
||||
{
|
||||
hoveredEntityIdAccentRemoved = true;
|
||||
}
|
||||
});
|
||||
|
||||
using ::testing::Eq;
|
||||
using ::testing::IsFalse;
|
||||
using ::testing::IsTrue;
|
||||
EXPECT_THAT(hoveredEntityIdAccentRemoved, IsTrue());
|
||||
EXPECT_THAT(hoveredEntityEntityId.IsValid(), IsFalse());
|
||||
}
|
||||
|
||||
TEST(HandleAccents, CurrentValidEntityIdDoesBecomeHoveredWithSelectionAndStickySelectAndCtrl)
|
||||
{
|
||||
namespace azvi = AzToolsFramework::ViewportInteraction;
|
||||
|
||||
const AZ::EntityId currentEntityId = AZ::EntityId(12345);
|
||||
AZ::EntityId hoveredEntityEntityId = AZ::EntityId(54321);
|
||||
|
||||
AzToolsFramework::HandleAccentsContext handleAccentsContext;
|
||||
handleAccentsContext.m_ctrlHeld = true;
|
||||
handleAccentsContext.m_hasSelectedEntities = true;
|
||||
handleAccentsContext.m_usingBoxSelect = false;
|
||||
handleAccentsContext.m_usingStickySelect = true;
|
||||
|
||||
bool currentEntityIdAccentAdded = false;
|
||||
bool hoveredEntityIdAccentRemoved = false;
|
||||
AzToolsFramework::HandleAccents(
|
||||
currentEntityId, hoveredEntityEntityId, handleAccentsContext, azvi::MouseButtonsFromButton(azvi::MouseButton::None),
|
||||
[&hoveredEntityIdAccentRemoved, ¤tEntityIdAccentAdded, currentEntityId,
|
||||
hoveredEntityEntityId](const AZ::EntityId entityId, const bool accent)
|
||||
{
|
||||
if (entityId == currentEntityId && accent)
|
||||
{
|
||||
currentEntityIdAccentAdded = true;
|
||||
}
|
||||
|
||||
if (entityId == hoveredEntityEntityId && !accent)
|
||||
{
|
||||
hoveredEntityIdAccentRemoved = true;
|
||||
}
|
||||
});
|
||||
|
||||
using ::testing::Eq;
|
||||
using ::testing::IsFalse;
|
||||
using ::testing::IsTrue;
|
||||
EXPECT_THAT(currentEntityIdAccentAdded, IsTrue());
|
||||
EXPECT_THAT(hoveredEntityIdAccentRemoved, IsTrue());
|
||||
EXPECT_THAT(hoveredEntityEntityId, Eq(AZ::EntityId(12345)));
|
||||
}
|
||||
|
||||
class EditorTransformComponentSelectionRenderGeometryIntersectionFixture : public ToolsApplicationFixture
|
||||
{
|
||||
public:
|
||||
void SetUpEditorFixtureImpl() override
|
||||
{
|
||||
auto* app = GetApplication();
|
||||
// register a simple component implementing BoundsRequestBus and EditorComponentSelectionRequestsBus
|
||||
app->RegisterComponentDescriptor(BoundsTestComponent::CreateDescriptor());
|
||||
// register a component implementing RenderGeometry::IntersectionRequestBus
|
||||
app->RegisterComponentDescriptor(RenderGeometryIntersectionTestComponent::CreateDescriptor());
|
||||
|
||||
auto createEntityWithGeometryIntersectionFn = [](const char* entityName)
|
||||
{
|
||||
AZ::Entity* entity = nullptr;
|
||||
AZ::EntityId entityId = CreateDefaultEditorEntity(entityName, &entity);
|
||||
|
||||
entity->Deactivate();
|
||||
entity->CreateComponent<RenderGeometryIntersectionTestComponent>();
|
||||
entity->Activate();
|
||||
|
||||
return entityId;
|
||||
};
|
||||
|
||||
m_entityIdGround = createEntityWithGeometryIntersectionFn("Entity1");
|
||||
m_entityIdBox = createEntityWithGeometryIntersectionFn("Entity2");
|
||||
|
||||
if (auto* ground = AzToolsFramework::GetEntityById(m_entityIdGround)->FindComponent<RenderGeometryIntersectionTestComponent>())
|
||||
{
|
||||
ground->m_localBounds = AZ::Aabb::CreateFromMinMax(AZ::Vector3(-10.0f, -10.0f, -0.5f), AZ::Vector3(10.0f, 10.0f, 0.5f));
|
||||
}
|
||||
|
||||
AzToolsFramework::SetWorldTransform(m_entityIdGround, AZ::Transform::CreateTranslation(AZ::Vector3(0.0f, 10.0f, 5.0f)));
|
||||
|
||||
if (auto* box = AzToolsFramework::GetEntityById(m_entityIdBox)->FindComponent<RenderGeometryIntersectionTestComponent>())
|
||||
{
|
||||
box->m_localBounds = AZ::Aabb::CreateFromMinMax(AZ::Vector3(-0.5f), AZ::Vector3(0.5f));
|
||||
}
|
||||
|
||||
AzToolsFramework::SetWorldTransform(
|
||||
m_entityIdBox,
|
||||
AZ::Transform::CreateFromMatrix3x3AndTranslation(
|
||||
AZ::Matrix3x3::CreateRotationZ(AZ::DegToRad(45.0f)), AZ::Vector3(0.0f, 10.0f, 7.0f)));
|
||||
}
|
||||
|
||||
AZ::EntityId m_entityIdGround;
|
||||
AZ::EntityId m_entityIdBox;
|
||||
};
|
||||
|
||||
using EditorTransformComponentSelectionRenderGeometryIntersectionManipulatorFixture =
|
||||
IndirectCallManipulatorViewportInteractionFixtureMixin<EditorTransformComponentSelectionRenderGeometryIntersectionFixture>;
|
||||
|
||||
TEST_F(
|
||||
EditorTransformComponentSelectionRenderGeometryIntersectionManipulatorFixture, BoxCanBePlacedOnMeshSurfaceUsingSurfaceManipulator)
|
||||
{
|
||||
// camera (go to position format) - 0.00, 20.00, 12.00, -35.00, -180.00
|
||||
m_cameraState.m_viewportSize = AZ::Vector2(1280.0f, 720.0f);
|
||||
AzFramework::SetCameraTransform(
|
||||
m_cameraState,
|
||||
AZ::Transform::CreateFromMatrix3x3AndTranslation(
|
||||
AZ::Matrix3x3::CreateRotationZ(AZ::DegToRad(-180.0f)) * AZ::Matrix3x3::CreateRotationX(AZ::DegToRad(-35.0f)),
|
||||
AZ::Vector3(0.0f, 20.0f, 12.0f)));
|
||||
|
||||
// the initial starting position of the entity
|
||||
const auto initialTransformWorld = AzToolsFramework::GetWorldTransform(m_entityIdBox);
|
||||
// where the entity should end up (snapped to the larger ground surface)
|
||||
const auto finalTransformWorld =
|
||||
AZ::Transform::CreateFromQuaternionAndTranslation(initialTransformWorld.GetRotation(), AZ::Vector3(2.5f, 12.5f, 5.5f));
|
||||
|
||||
// calculate the position in screen space of the initial position of the entity
|
||||
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);
|
||||
|
||||
// select the entity (this will cause the manipulators to appear in EditorTransformComponentSelection)
|
||||
AzToolsFramework::SelectEntity(m_entityIdBox);
|
||||
|
||||
// press and drag the mouse (starting where the surface manipulator is)
|
||||
m_actionDispatcher->CameraState(m_cameraState)
|
||||
->MousePosition(initialPositionScreen)
|
||||
->MouseLButtonDown()
|
||||
->MousePosition(finalPositionScreen)
|
||||
->MouseLButtonUp();
|
||||
|
||||
// read back the position of the entity now
|
||||
const AZ::Transform finalEntityTransform = AzToolsFramework::GetWorldTransform(m_entityIdBox);
|
||||
|
||||
// ensure final world positions match
|
||||
EXPECT_THAT(finalEntityTransform, IsCloseTolerance(finalTransformWorld, 0.01f));
|
||||
}
|
||||
|
||||
TEST_F(
|
||||
EditorTransformComponentSelectionRenderGeometryIntersectionManipulatorFixture,
|
||||
SurfaceManipulatorFollowsMouseAtDefaultEditorDistanceFromCameraWhenNoMeshIntersection)
|
||||
{
|
||||
// camera (go to position format) - 0.00, 25.00, 12.00, 0.00, -180.00
|
||||
m_cameraState.m_viewportSize = AZ::Vector2(1280.0f, 720.0f);
|
||||
AzFramework::SetCameraTransform(
|
||||
m_cameraState,
|
||||
AZ::Transform::CreateFromMatrix3x3AndTranslation(
|
||||
AZ::Matrix3x3::CreateRotationZ(AZ::DegToRad(-180.0f)), AZ::Vector3(0.0f, 25.0f, 12.0f)));
|
||||
|
||||
// the initial starting position of the entity
|
||||
const auto initialTransformWorld = AzToolsFramework::GetWorldTransform(m_entityIdBox);
|
||||
// where the entity should end up (default distance away from the camera/near clip under where the mouse is)
|
||||
const auto finalTransformWorld =
|
||||
AZ::Transform::CreateFromQuaternionAndTranslation(initialTransformWorld.GetRotation(), AZ::Vector3(0.0f, 14.9f, 12.0f));
|
||||
|
||||
// calculate the position in screen space of the initial position of the entity
|
||||
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);
|
||||
|
||||
// select the entity (this will cause the manipulators to appear in EditorTransformComponentSelection)
|
||||
AzToolsFramework::SelectEntity(m_entityIdBox);
|
||||
|
||||
// press and drag the mouse (starting where the surface manipulator is)
|
||||
m_actionDispatcher->CameraState(m_cameraState)
|
||||
->MousePosition(initialPositionScreen)
|
||||
->MouseLButtonDown()
|
||||
->MousePosition(finalPositionScreen)
|
||||
->MouseLButtonUp();
|
||||
|
||||
// read back the position of the entity now
|
||||
const AZ::Transform finalEntityTransform = AzToolsFramework::GetWorldTransform(m_entityIdBox);
|
||||
|
||||
const auto viewportRay = AzToolsFramework::ViewportInteraction::ViewportScreenToWorldRay(m_cameraState, initialPositionScreen);
|
||||
const auto distanceAway = (finalEntityTransform.GetTranslation() - viewportRay.origin).GetLength();
|
||||
|
||||
// ensure final world positions match
|
||||
EXPECT_THAT(finalEntityTransform, IsCloseTolerance(finalTransformWorld, 0.01f));
|
||||
// ensure distance away is what we expect
|
||||
EXPECT_NEAR(distanceAway, AzToolsFramework::GetDefaultEntityPlacementDistance(), 0.001f);
|
||||
}
|
||||
|
||||
TEST_F(
|
||||
EditorTransformComponentSelectionRenderGeometryIntersectionManipulatorFixture,
|
||||
MiddleMouseButtonWithShiftAndCtrlHeldOnMeshSurfaceWillSnapSelectedEntityToIntersectionPoint)
|
||||
{
|
||||
// camera (go to position format) - 21.00, 8.00, 11.00, -22.00, 150.00
|
||||
m_cameraState.m_viewportSize = AZ::Vector2(1280.0f, 720.0f);
|
||||
AzFramework::SetCameraTransform(
|
||||
m_cameraState,
|
||||
AZ::Transform::CreateFromMatrix3x3AndTranslation(
|
||||
AZ::Matrix3x3::CreateRotationZ(AZ::DegToRad(150.0f)) * AZ::Matrix3x3::CreateRotationX(AZ::DegToRad(-22.0f)),
|
||||
AZ::Vector3(21.0f, 8.0f, 11.0f)));
|
||||
|
||||
// position the ground entity
|
||||
AzToolsFramework::SetWorldTransform(
|
||||
m_entityIdGround,
|
||||
AZ::Transform::CreateFromMatrix3x3AndTranslation(
|
||||
AZ::Matrix3x3::CreateRotationY(AZ::DegToRad(40.0f)) * AZ::Matrix3x3::CreateRotationZ(AZ::DegToRad(60.0f)),
|
||||
AZ::Vector3(14.0f, -6.0f, 5.0f)));
|
||||
|
||||
// select the other entity (a 1x1x1 box)
|
||||
AzToolsFramework::SelectEntity(m_entityIdBox);
|
||||
|
||||
// expected world position (value taken from editor scenario)
|
||||
const auto expectedWorldPosition = AZ::Vector3(13.606657f, -2.6753534f, 5.9827675f);
|
||||
const auto screenPosition = AzFramework::WorldToScreen(expectedWorldPosition, m_cameraState);
|
||||
|
||||
// perform snap action
|
||||
m_actionDispatcher->CameraState(m_cameraState)
|
||||
->MousePosition(screenPosition)
|
||||
->KeyboardModifierDown(AzToolsFramework::ViewportInteraction::KeyboardModifier::Control)
|
||||
->KeyboardModifierDown(AzToolsFramework::ViewportInteraction::KeyboardModifier::Shift)
|
||||
->MouseMButtonDown();
|
||||
|
||||
// read back the current entity transform after placement
|
||||
const AZ::Transform finalEntityTransform = AzToolsFramework::GetWorldTransform(m_entityIdBox);
|
||||
EXPECT_THAT(finalEntityTransform.GetTranslation(), IsCloseTolerance(expectedWorldPosition, 0.01f));
|
||||
}
|
||||
} // namespace UnitTest
|
||||
|
||||
@@ -10,21 +10,23 @@
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/UnitTest/TestTypes.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 <AzTest/AzTest.h>
|
||||
#include <AzToolsFramework/Application/ToolsApplication.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
|
||||
#include <AzToolsFramework/Manipulators/EditorVertexSelection.h>
|
||||
#include <AzToolsFramework/Manipulators/HoverSelection.h>
|
||||
#include <AzToolsFramework/Manipulators/ManipulatorManager.h>
|
||||
#include <AzToolsFramework/Manipulators/TranslationManipulators.h>
|
||||
#include <AzToolsFramework/UnitTest/AzToolsFrameworkTestHelpers.h>
|
||||
#include <AzToolsFramework/ViewportSelection/EditorInteractionSystemViewportSelectionRequestBus.h>
|
||||
#include <AzManipulatorTestFramework/AzManipulatorTestFramework.h>
|
||||
#include <AzManipulatorTestFramework/AzManipulatorTestFrameworkUtils.h>
|
||||
#include <AzManipulatorTestFramework/AzManipulatorTestFrameworkTestHelpers.h>
|
||||
#include <AzManipulatorTestFramework/IndirectManipulatorViewportInteraction.h>
|
||||
#include <AzManipulatorTestFramework/ImmediateModeActionDispatcher.h>
|
||||
#include <Tests/Utils/Printers.h>
|
||||
|
||||
using namespace AzToolsFramework;
|
||||
#include <Tests/BoundsTestComponent.h>
|
||||
|
||||
namespace UnitTest
|
||||
{
|
||||
@@ -41,20 +43,65 @@ namespace UnitTest
|
||||
void Disconnect();
|
||||
|
||||
// FixedVerticesRequestBus/VariableVerticesRequestBus ...
|
||||
bool GetVertex(size_t index, AZ::Vector3& vertex) const override { return m_vertexContainer.GetVertex(index, vertex); }
|
||||
bool UpdateVertex(size_t index, const AZ::Vector3& vertex) override { return m_vertexContainer.UpdateVertex(index, vertex); };
|
||||
void AddVertex(const AZ::Vector3& vertex) override { m_vertexContainer.AddVertex(vertex); }
|
||||
bool InsertVertex(size_t index, const AZ::Vector3& vertex) override { return m_vertexContainer.InsertVertex(index, vertex); }
|
||||
bool RemoveVertex(size_t index) override { return m_vertexContainer.RemoveVertex(index); }
|
||||
void SetVertices(const AZStd::vector<AZ::Vector3>& vertices) override { m_vertexContainer.SetVertices(vertices); };
|
||||
void ClearVertices() override { m_vertexContainer.Clear(); }
|
||||
size_t Size() const override { return m_vertexContainer.Size(); }
|
||||
bool Empty() const override { return m_vertexContainer.Empty(); }
|
||||
bool GetVertex(size_t index, AZ::Vector3& vertex) const override;
|
||||
bool UpdateVertex(size_t index, const AZ::Vector3& vertex) override;
|
||||
void AddVertex(const AZ::Vector3& vertex) override;
|
||||
bool InsertVertex(size_t index, const AZ::Vector3& vertex) override;
|
||||
bool RemoveVertex(size_t index) override;
|
||||
void SetVertices(const AZStd::vector<AZ::Vector3>& vertices) override;
|
||||
void ClearVertices() override;
|
||||
size_t Size() const override;
|
||||
bool Empty() const override;
|
||||
|
||||
private:
|
||||
AZ::VertexContainer<AZ::Vector3> m_vertexContainer;
|
||||
};
|
||||
|
||||
bool TestVariableVerticesVertexContainer::GetVertex(size_t index, AZ::Vector3& vertex) const
|
||||
{
|
||||
return m_vertexContainer.GetVertex(index, vertex);
|
||||
}
|
||||
|
||||
bool TestVariableVerticesVertexContainer::UpdateVertex(size_t index, const AZ::Vector3& vertex)
|
||||
{
|
||||
return m_vertexContainer.UpdateVertex(index, vertex);
|
||||
}
|
||||
|
||||
void TestVariableVerticesVertexContainer::AddVertex(const AZ::Vector3& vertex)
|
||||
{
|
||||
m_vertexContainer.AddVertex(vertex);
|
||||
}
|
||||
|
||||
bool TestVariableVerticesVertexContainer::InsertVertex(size_t index, const AZ::Vector3& vertex)
|
||||
{
|
||||
return m_vertexContainer.InsertVertex(index, vertex);
|
||||
}
|
||||
|
||||
bool TestVariableVerticesVertexContainer::RemoveVertex(size_t index)
|
||||
{
|
||||
return m_vertexContainer.RemoveVertex(index);
|
||||
}
|
||||
|
||||
void TestVariableVerticesVertexContainer::SetVertices(const AZStd::vector<AZ::Vector3>& vertices)
|
||||
{
|
||||
m_vertexContainer.SetVertices(vertices);
|
||||
}
|
||||
|
||||
void TestVariableVerticesVertexContainer::ClearVertices()
|
||||
{
|
||||
m_vertexContainer.Clear();
|
||||
}
|
||||
|
||||
size_t TestVariableVerticesVertexContainer::Size() const
|
||||
{
|
||||
return m_vertexContainer.Size();
|
||||
}
|
||||
|
||||
bool TestVariableVerticesVertexContainer::Empty() const
|
||||
{
|
||||
return m_vertexContainer.Empty();
|
||||
}
|
||||
|
||||
void TestVariableVerticesVertexContainer::Connect(const AZ::EntityId entityId)
|
||||
{
|
||||
AZ::VariableVerticesRequestBus<AZ::Vector3>::Handler::BusConnect(entityId);
|
||||
@@ -67,17 +114,18 @@ namespace UnitTest
|
||||
AZ::VariableVerticesRequestBus<AZ::Vector3>::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
class TestEditorVertexSelectionVariable
|
||||
: public EditorVertexSelectionVariable<AZ::Vector3>
|
||||
class TestEditorVertexSelectionVariable : public AzToolsFramework::EditorVertexSelectionVariable<AZ::Vector3>
|
||||
{
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(TestEditorVertexSelectionVariable, AZ::SystemAllocator, 0)
|
||||
|
||||
void ShowVertexDeletionWarning() override { /*noop*/ }
|
||||
void ShowVertexDeletionWarning() override
|
||||
{
|
||||
// noop
|
||||
}
|
||||
};
|
||||
|
||||
class EditorVertexSelectionFixture
|
||||
: public ToolsApplicationFixture
|
||||
class EditorVertexSelectionFixture : public ToolsApplicationFixture
|
||||
{
|
||||
public:
|
||||
void SetUpEditorFixtureImpl() override
|
||||
@@ -111,26 +159,25 @@ namespace UnitTest
|
||||
|
||||
void EditorVertexSelectionFixture::RecreateVertexSelection()
|
||||
{
|
||||
namespace aztf = AzToolsFramework;
|
||||
m_vertexSelection.Create(
|
||||
AZ::EntityComponentIdPair(m_entityId, TestComponentId),
|
||||
g_mainManipulatorManagerId, AZStd::make_unique<NullHoverSelection>(),
|
||||
TranslationManipulators::Dimensions::Three, ConfigureTranslationManipulatorAppearance3d);
|
||||
AZ::EntityComponentIdPair(m_entityId, TestComponentId), aztf::g_mainManipulatorManagerId,
|
||||
AZStd::make_unique<aztf::NullHoverSelection>(), aztf::TranslationManipulators::Dimensions::Three,
|
||||
aztf::ConfigureTranslationManipulatorAppearance3d);
|
||||
}
|
||||
|
||||
void EditorVertexSelectionFixture::PopulateVertices()
|
||||
{
|
||||
for (size_t vertIndex = 0; vertIndex < EditorVertexSelectionFixture::VertexCount; ++vertIndex)
|
||||
{
|
||||
InsertVertexAfter(
|
||||
AZ::EntityComponentIdPair(m_entityId, TestComponentId), 0, AZ::Vector3::CreateZero());
|
||||
AzToolsFramework::InsertVertexAfter(AZ::EntityComponentIdPair(m_entityId, TestComponentId), 0, AZ::Vector3::CreateZero());
|
||||
}
|
||||
}
|
||||
void EditorVertexSelectionFixture::ClearVertices()
|
||||
{
|
||||
for (size_t vertIndex = 0; vertIndex < EditorVertexSelectionFixture::VertexCount; ++vertIndex)
|
||||
{
|
||||
SafeRemoveVertex<AZ::Vector3>(
|
||||
AZ::EntityComponentIdPair(m_entityId, TestComponentId), 0);
|
||||
AzToolsFramework::SafeRemoveVertex<AZ::Vector3>(AZ::EntityComponentIdPair(m_entityId, TestComponentId), 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,7 +234,7 @@ namespace UnitTest
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// When
|
||||
// just provide a placeholder mouse interaction event in this case
|
||||
m_vertexSelection.SnapVerticesToTerrain(ViewportInteraction::MouseInteractionEvent{});
|
||||
m_vertexSelection.SnapVerticesToSurface(AzToolsFramework::ViewportInteraction::MouseInteractionEvent{});
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -196,8 +243,7 @@ namespace UnitTest
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
}
|
||||
|
||||
using EditorVertexSelectionManipulatorFixture =
|
||||
IndirectCallManipulatorViewportInteractionFixtureMixin<EditorVertexSelectionFixture>;
|
||||
using EditorVertexSelectionManipulatorFixture = IndirectCallManipulatorViewportInteractionFixtureMixin<EditorVertexSelectionFixture>;
|
||||
|
||||
TEST_F(EditorVertexSelectionManipulatorFixture, CannotDeleteAllVertices)
|
||||
{
|
||||
@@ -205,25 +251,23 @@ namespace UnitTest
|
||||
|
||||
const auto entityComponentIdPair = AZ::EntityComponentIdPair(m_entityId, TestComponentId);
|
||||
|
||||
const float horizontalPositions[] = {-1.5f, -0.5f, 0.5f, 1.5f};
|
||||
for (size_t vertIndex = 0; vertIndex < std::size(horizontalPositions); ++vertIndex)
|
||||
const float horizontalPositions[] = { -1.5f, -0.5f, 0.5f, 1.5f };
|
||||
for (size_t vertIndex = 0; vertIndex < AZStd::size(horizontalPositions); ++vertIndex)
|
||||
{
|
||||
InsertVertexAfter(
|
||||
entityComponentIdPair, vertIndex, AZ::Vector3(horizontalPositions[vertIndex], 5.0f, 0.0f));
|
||||
AzToolsFramework::InsertVertexAfter(entityComponentIdPair, vertIndex, AZ::Vector3(horizontalPositions[vertIndex], 5.0f, 0.0f));
|
||||
}
|
||||
|
||||
// rebuild the vertex selection after adding the new verts
|
||||
// rebuild the vertex selection after adding the new vertices
|
||||
RecreateVertexSelection();
|
||||
|
||||
// build a vector of the vertex positions in screen space
|
||||
AZStd::vector<AzFramework::ScreenPoint> vertexScreenPositions;
|
||||
for (size_t vertIndex = 0; vertIndex < std::size(horizontalPositions); ++vertIndex)
|
||||
for (size_t vertIndex = 0; vertIndex < AZStd::size(horizontalPositions); ++vertIndex)
|
||||
{
|
||||
AZ::Vector3 localVertex;
|
||||
AZ::Vector3 localVertex = AZ::Vector3::CreateZero();
|
||||
bool found = false;
|
||||
AZ::FixedVerticesRequestBus<AZ::Vector3>::EventResult(
|
||||
found, m_entityId, &AZ::FixedVerticesRequestBus<AZ::Vector3>::Handler::GetVertex,
|
||||
vertIndex, localVertex);
|
||||
found, m_entityId, &AZ::FixedVerticesRequestBus<AZ::Vector3>::Handler::GetVertex, vertIndex, localVertex);
|
||||
|
||||
if (found)
|
||||
{
|
||||
@@ -266,9 +310,9 @@ namespace UnitTest
|
||||
const auto entityComponentIdPair = AZ::EntityComponentIdPair(m_entityId, TestComponentId);
|
||||
|
||||
// add a single vertex (in front of the camera)
|
||||
InsertVertexAfter(entityComponentIdPair, 0, AZ::Vector3::CreateAxisY(5.0f));
|
||||
AzToolsFramework::InsertVertexAfter(entityComponentIdPair, 0, AZ::Vector3::CreateAxisY(5.0f));
|
||||
|
||||
// rebuild the vertex selection after adding the new verts
|
||||
// rebuild the vertex selection after adding the new vertices
|
||||
RecreateVertexSelection();
|
||||
|
||||
AzFramework::ScreenPoint vertexScreenPosition;
|
||||
@@ -299,4 +343,132 @@ namespace UnitTest
|
||||
// deleting the last vertex through a manipulator is disallowed - size should remain the same
|
||||
EXPECT_THAT(vertexCountAfter, Eq(1));
|
||||
}
|
||||
|
||||
static AZ::EntityId CreateEntityForVertexIntersectionPlacement(EditorVertexSelectionManipulatorFixture& fixture)
|
||||
{
|
||||
auto* app = fixture.GetApplication();
|
||||
app->RegisterComponentDescriptor(BoundsTestComponent::CreateDescriptor());
|
||||
app->RegisterComponentDescriptor(RenderGeometryIntersectionTestComponent::CreateDescriptor());
|
||||
|
||||
AZ::Entity* entityGround = nullptr;
|
||||
AZ::EntityId entityIdGround = CreateDefaultEditorEntity("EntityGround", &entityGround);
|
||||
|
||||
entityGround->Deactivate();
|
||||
auto ground = entityGround->CreateComponent<RenderGeometryIntersectionTestComponent>();
|
||||
entityGround->Activate();
|
||||
|
||||
ground->m_localBounds = AZ::Aabb::CreateFromMinMax(AZ::Vector3(-10.0f, -10.0f, -0.5f), AZ::Vector3(10.0f, 10.0f, 0.5f));
|
||||
|
||||
return entityIdGround;
|
||||
}
|
||||
|
||||
static AZStd::vector<AzFramework::ScreenPoint> SetupVertices(
|
||||
const AZ::EntityId entityId, EditorVertexSelectionManipulatorFixture& fixture)
|
||||
{
|
||||
const auto entityComponentIdPair = AZ::EntityComponentIdPair(entityId, TestComponentId);
|
||||
const float horizontalPositions[] = { -3.0f, -1.0f, 1.0f, 3.0f };
|
||||
for (size_t vertIndex = 0; vertIndex < AZStd::size(horizontalPositions); ++vertIndex)
|
||||
{
|
||||
AzToolsFramework::InsertVertexAfter(entityComponentIdPair, vertIndex, AZ::Vector3(horizontalPositions[vertIndex], 0.0f, 0.0f));
|
||||
}
|
||||
|
||||
// rebuild the vertex selection after adding the new vertices
|
||||
fixture.RecreateVertexSelection();
|
||||
|
||||
// build a vector of the vertex positions in screen space
|
||||
AZStd::vector<AzFramework::ScreenPoint> vertexScreenPositions;
|
||||
for (size_t vertIndex = 0; vertIndex < AZStd::size(horizontalPositions); ++vertIndex)
|
||||
{
|
||||
AZ::Vector3 localVertex;
|
||||
bool found = false;
|
||||
AZ::FixedVerticesRequestBus<AZ::Vector3>::EventResult(
|
||||
found, entityId, &AZ::FixedVerticesRequestBus<AZ::Vector3>::Handler::GetVertex, vertIndex, localVertex);
|
||||
|
||||
if (found)
|
||||
{
|
||||
const AZ::Vector3 worldVertex = AzToolsFramework::GetWorldTransform(entityId).TransformPoint(localVertex);
|
||||
vertexScreenPositions.push_back(AzFramework::WorldToScreen(worldVertex, fixture.m_cameraState));
|
||||
}
|
||||
}
|
||||
|
||||
return vertexScreenPositions;
|
||||
}
|
||||
|
||||
AzToolsFramework::ViewportInteraction::MouseInteractionEvent BuildMiddleMouseDownEvent(
|
||||
const AzFramework::ScreenPoint& screenPosition, const AzFramework::ViewportId viewportId)
|
||||
{
|
||||
AzToolsFramework::ViewportInteraction::MousePick mousePick;
|
||||
mousePick.m_screenCoordinates = screenPosition;
|
||||
|
||||
AzToolsFramework::ViewportInteraction::MouseInteraction mouseInteraction;
|
||||
mouseInteraction.m_interactionId.m_cameraId = AZ::EntityId();
|
||||
mouseInteraction.m_interactionId.m_viewportId = viewportId;
|
||||
mouseInteraction.m_mouseButtons =
|
||||
AzToolsFramework::ViewportInteraction::MouseButtonsFromButton(AzToolsFramework::ViewportInteraction::MouseButton::Middle);
|
||||
mouseInteraction.m_mousePick = mousePick;
|
||||
mouseInteraction.m_keyboardModifiers = AzToolsFramework::ViewportInteraction::KeyboardModifiers(
|
||||
static_cast<AZ::u32>(AzToolsFramework::ViewportInteraction::KeyboardModifier::Shift) |
|
||||
static_cast<AZ::u32>(AzToolsFramework::ViewportInteraction::KeyboardModifier::Ctrl));
|
||||
|
||||
return AzToolsFramework::ViewportInteraction::MouseInteractionEvent(
|
||||
mouseInteraction, AzToolsFramework::ViewportInteraction::MouseEvent::Down, /*captured=*/false);
|
||||
}
|
||||
|
||||
TEST_F(EditorVertexSelectionManipulatorFixture, VertexPlacedWhereIntersectionPointIsFoundWithCustomReferenceSpace)
|
||||
{
|
||||
const AZ::EntityId entityIdGround = CreateEntityForVertexIntersectionPlacement(*this);
|
||||
|
||||
// position ground
|
||||
AzToolsFramework::SetWorldTransform(
|
||||
entityIdGround,
|
||||
AZ::Transform::CreateFromMatrix3x3AndTranslation(
|
||||
AZ::Matrix3x3::CreateRotationX(AZ::DegToRad(-20.0f)) * AZ::Matrix3x3::CreateRotationY(AZ::DegToRad(-40.0f)) *
|
||||
AZ::Matrix3x3::CreateRotationZ(AZ::DegToRad(60.0f)),
|
||||
AZ::Vector3(14.0f, -6.0f, 5.0f)));
|
||||
|
||||
// camera (go to position format) - 12.00, 18.00, 16.00, -38.00, -175.00
|
||||
m_cameraState.m_viewportSize = AZ::Vector2(1280.0f, 720.0f);
|
||||
AzFramework::SetCameraTransform(
|
||||
m_cameraState,
|
||||
AZ::Transform::CreateFromMatrix3x3AndTranslation(
|
||||
AZ::Matrix3x3::CreateRotationZ(AZ::DegToRad(-175.0f)) * AZ::Matrix3x3::CreateRotationX(AZ::DegToRad(-38.0f)),
|
||||
AZ::Vector3(12.0f, 18.0f, 16.0f)));
|
||||
|
||||
// create orientated and scaled transform for vertex selection entity transform
|
||||
auto vertexSelectionTransform = AZ::Transform::CreateFromMatrix3x3AndTranslation(
|
||||
AZ::Matrix3x3::CreateRotationZ(AZ::DegToRad(45.0f)), AZ::Vector3(14.0f, 7.0f, 5.0f));
|
||||
vertexSelectionTransform.MultiplyByUniformScale(3.0f);
|
||||
|
||||
// set the initial starting position of the vertex selection
|
||||
AzToolsFramework::SetWorldTransform(m_entityId, vertexSelectionTransform);
|
||||
|
||||
auto vertexScreenPositions = SetupVertices(m_entityId, *this);
|
||||
|
||||
// press and drag the mouse (starting where the surface manipulator is)
|
||||
// select each vertex (by holding ctrl)
|
||||
m_actionDispatcher->CameraState(m_cameraState)->MousePosition(vertexScreenPositions[0])->MouseLButtonDown()->MouseLButtonUp();
|
||||
|
||||
const auto finalPositionWorld = AZ::Vector3(14.3573294f, -8.94695091f, 7.08627319f);
|
||||
// calculate the position in screen space of the final position of the entity
|
||||
const auto finalPositionScreen = AzFramework::WorldToScreen(finalPositionWorld, m_cameraState);
|
||||
|
||||
auto middleMouseDownEvent =
|
||||
BuildMiddleMouseDownEvent(finalPositionScreen, m_viewportManipulatorInteraction->GetViewportInteraction().GetViewportId());
|
||||
|
||||
// explicitly handle mouse event in vertex selection instance
|
||||
m_vertexSelection.HandleMouse(middleMouseDownEvent);
|
||||
|
||||
// read back the position of the vertex now
|
||||
AZ::Vector3 localVertex = AZ::Vector3::CreateZero();
|
||||
bool found = false;
|
||||
AZ::FixedVerticesRequestBus<AZ::Vector3>::EventResult(
|
||||
found, m_entityId, &AZ::FixedVerticesRequestBus<AZ::Vector3>::Handler::GetVertex, 0, localVertex);
|
||||
|
||||
// transform to world space
|
||||
const AZ::Vector3 worldVertex = vertexSelectionTransform.TransformPoint(localVertex);
|
||||
|
||||
EXPECT_THAT(found, ::testing::IsTrue());
|
||||
// ensure final world positions match
|
||||
EXPECT_THAT(worldVertex, IsCloseTolerance(finalPositionWorld, 0.01f));
|
||||
}
|
||||
} // namespace UnitTest
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <Tests/Entity/ReadOnly/ReadOnlyEntityFixture.h>
|
||||
|
||||
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
void ReadOnlyEntityFixture::SetUpEditorFixtureImpl()
|
||||
{
|
||||
// Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is
|
||||
// shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash
|
||||
// in the unit tests.
|
||||
AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize);
|
||||
|
||||
m_readOnlyEntityPublicInterface = AZ::Interface<ReadOnlyEntityPublicInterface>::Get();
|
||||
ASSERT_TRUE(m_readOnlyEntityPublicInterface != nullptr);
|
||||
|
||||
GenerateTestHierarchy();
|
||||
}
|
||||
|
||||
void ReadOnlyEntityFixture::TearDownEditorFixtureImpl()
|
||||
{
|
||||
}
|
||||
|
||||
void ReadOnlyEntityFixture::GenerateTestHierarchy()
|
||||
{
|
||||
/*
|
||||
* Root
|
||||
* |_ Child
|
||||
* |_ GrandChild1
|
||||
* |_ GrandChild2
|
||||
*/
|
||||
|
||||
m_entityMap[RootEntityName] = CreateEditorEntity(RootEntityName, AZ::EntityId());
|
||||
m_entityMap[ChildEntityName] = CreateEditorEntity(ChildEntityName, m_entityMap[RootEntityName]);
|
||||
m_entityMap[GrandChild1EntityName] = CreateEditorEntity(GrandChild1EntityName, m_entityMap[ChildEntityName]);
|
||||
m_entityMap[GrandChild2EntityName] = CreateEditorEntity(GrandChild2EntityName, m_entityMap[ChildEntityName]);
|
||||
}
|
||||
|
||||
AZ::EntityId ReadOnlyEntityFixture::CreateEditorEntity(const char* name, AZ::EntityId parentId)
|
||||
{
|
||||
AZ::Entity* entity = nullptr;
|
||||
UnitTest::CreateDefaultEditorEntity(name, &entity);
|
||||
|
||||
// Parent
|
||||
AZ::TransformBus::Event(entity->GetId(), &AZ::TransformInterface::SetParent, parentId);
|
||||
|
||||
return entity->GetId();
|
||||
}
|
||||
|
||||
ReadOnlyHandlerAlwaysTrue::ReadOnlyHandlerAlwaysTrue()
|
||||
{
|
||||
auto editorEntityContextId = AzFramework::EntityContextId::CreateNull();
|
||||
EditorEntityContextRequestBus::BroadcastResult(editorEntityContextId, &EditorEntityContextRequests::GetEditorEntityContextId);
|
||||
|
||||
ReadOnlyEntityQueryRequestBus::Handler::BusConnect(editorEntityContextId);
|
||||
}
|
||||
|
||||
ReadOnlyHandlerAlwaysTrue::~ReadOnlyHandlerAlwaysTrue()
|
||||
{
|
||||
ReadOnlyEntityQueryRequestBus::Handler::BusDisconnect();
|
||||
|
||||
if (auto readOnlyEntityQueryInterface = AZ::Interface<ReadOnlyEntityQueryInterface>::Get())
|
||||
{
|
||||
readOnlyEntityQueryInterface->RefreshReadOnlyStateForAllEntities();
|
||||
}
|
||||
}
|
||||
|
||||
void ReadOnlyHandlerAlwaysTrue::IsReadOnly([[maybe_unused]] const AZ::EntityId& entityId, bool& isReadOnly)
|
||||
{
|
||||
isReadOnly = true;
|
||||
}
|
||||
|
||||
ReadOnlyHandlerAlwaysFalse::ReadOnlyHandlerAlwaysFalse()
|
||||
{
|
||||
auto editorEntityContextId = AzFramework::EntityContextId::CreateNull();
|
||||
EditorEntityContextRequestBus::BroadcastResult(editorEntityContextId, &EditorEntityContextRequests::GetEditorEntityContextId);
|
||||
|
||||
ReadOnlyEntityQueryRequestBus::Handler::BusConnect(editorEntityContextId);
|
||||
}
|
||||
|
||||
ReadOnlyHandlerAlwaysFalse::~ReadOnlyHandlerAlwaysFalse()
|
||||
{
|
||||
ReadOnlyEntityQueryRequestBus::Handler::BusDisconnect();
|
||||
|
||||
if (auto readOnlyEntityQueryInterface = AZ::Interface<ReadOnlyEntityQueryInterface>::Get())
|
||||
{
|
||||
readOnlyEntityQueryInterface->RefreshReadOnlyStateForAllEntities();
|
||||
}
|
||||
}
|
||||
|
||||
ReadOnlyHandlerEntityId::ReadOnlyHandlerEntityId(AZ::EntityId entityId)
|
||||
: m_entityId(entityId)
|
||||
{
|
||||
auto editorEntityContextId = AzFramework::EntityContextId::CreateNull();
|
||||
EditorEntityContextRequestBus::BroadcastResult(editorEntityContextId, &EditorEntityContextRequests::GetEditorEntityContextId);
|
||||
|
||||
ReadOnlyEntityQueryRequestBus::Handler::BusConnect(editorEntityContextId);
|
||||
}
|
||||
|
||||
ReadOnlyHandlerEntityId::~ReadOnlyHandlerEntityId()
|
||||
{
|
||||
ReadOnlyEntityQueryRequestBus::Handler::BusDisconnect();
|
||||
|
||||
if (auto readOnlyEntityQueryInterface = AZ::Interface<ReadOnlyEntityQueryInterface>::Get())
|
||||
{
|
||||
readOnlyEntityQueryInterface->RefreshReadOnlyStateForAllEntities();
|
||||
}
|
||||
}
|
||||
|
||||
void ReadOnlyHandlerEntityId::IsReadOnly(const AZ::EntityId& entityId, bool& isReadOnly)
|
||||
{
|
||||
if (entityId == m_entityId)
|
||||
{
|
||||
isReadOnly = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Component/TransformBus.h>
|
||||
#include <AzCore/UnitTest/TestTypes.h>
|
||||
|
||||
#include <AzTest/AzTest.h>
|
||||
|
||||
#include <AzToolsFramework/UnitTest/AzToolsFrameworkTestHelpers.h>
|
||||
#include <AzToolsFramework/Entity/ReadOnly/ReadOnlyEntityBus.h>
|
||||
#include <AzToolsFramework/Entity/ReadOnly/ReadOnlyEntityInterface.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
class ReadOnlyEntityFixture
|
||||
: public UnitTest::ToolsApplicationFixture
|
||||
{
|
||||
protected:
|
||||
void SetUpEditorFixtureImpl() override;
|
||||
void TearDownEditorFixtureImpl() override;
|
||||
|
||||
void GenerateTestHierarchy();
|
||||
AZ::EntityId CreateEditorEntity(const char* name, AZ::EntityId parentId);
|
||||
|
||||
AZStd::unordered_map<AZStd::string, AZ::EntityId> m_entityMap;
|
||||
|
||||
ReadOnlyEntityPublicInterface* m_readOnlyEntityPublicInterface = nullptr;
|
||||
|
||||
public:
|
||||
inline static const char* RootEntityName = "Root";
|
||||
inline static const char* ChildEntityName = "Child";
|
||||
inline static const char* GrandChild1EntityName = "GrandChild1";
|
||||
inline static const char* GrandChild2EntityName = "GrandChild2";
|
||||
};
|
||||
|
||||
class ReadOnlyHandlerAlwaysTrue
|
||||
: public ReadOnlyEntityQueryRequestBus::Handler
|
||||
{
|
||||
public:
|
||||
ReadOnlyHandlerAlwaysTrue();
|
||||
~ReadOnlyHandlerAlwaysTrue();
|
||||
|
||||
// ReadOnlyEntityQueryNotificationBus overrides ...
|
||||
void IsReadOnly(const AZ::EntityId& entityId, bool& isReadOnly) override;
|
||||
};
|
||||
|
||||
class ReadOnlyHandlerAlwaysFalse
|
||||
: public ReadOnlyEntityQueryRequestBus::Handler
|
||||
{
|
||||
public:
|
||||
ReadOnlyHandlerAlwaysFalse();
|
||||
~ReadOnlyHandlerAlwaysFalse();
|
||||
|
||||
// ReadOnlyEntityQueryNotificationBus overrides ...
|
||||
void IsReadOnly([[maybe_unused]] const AZ::EntityId& entityId, [[maybe_unused]] bool& isReadOnly) override {}
|
||||
};
|
||||
|
||||
class ReadOnlyHandlerEntityId
|
||||
: public ReadOnlyEntityQueryRequestBus::Handler
|
||||
{
|
||||
public:
|
||||
ReadOnlyHandlerEntityId(AZ::EntityId entityId);
|
||||
~ReadOnlyHandlerEntityId();
|
||||
|
||||
// ReadOnlyEntityQueryNotificationBus overrides ...
|
||||
void IsReadOnly(const AZ::EntityId& entityId, bool& isReadOnly) override;
|
||||
|
||||
private:
|
||||
AZ::EntityId m_entityId;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <Tests/Entity/ReadOnly/ReadOnlyEntityFixture.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
TEST_F(ReadOnlyEntityFixture, NoHandlerEntityIsNotReadOnlyByDefault)
|
||||
{
|
||||
EXPECT_FALSE(m_readOnlyEntityPublicInterface->IsReadOnly(m_entityMap[ChildEntityName]));
|
||||
}
|
||||
|
||||
TEST_F(ReadOnlyEntityFixture, SingleHandlerEntityIsReadOnly)
|
||||
{
|
||||
// Create a handler that sets all entities to read-only.
|
||||
ReadOnlyHandlerAlwaysTrue alwaysTrueHandler;
|
||||
|
||||
// All entities should be marked read-only now.
|
||||
EXPECT_TRUE(m_readOnlyEntityPublicInterface->IsReadOnly(m_entityMap[RootEntityName]));
|
||||
EXPECT_TRUE(m_readOnlyEntityPublicInterface->IsReadOnly(m_entityMap[ChildEntityName]));
|
||||
EXPECT_TRUE(m_readOnlyEntityPublicInterface->IsReadOnly(m_entityMap[GrandChild1EntityName]));
|
||||
EXPECT_TRUE(m_readOnlyEntityPublicInterface->IsReadOnly(m_entityMap[GrandChild2EntityName]));
|
||||
}
|
||||
|
||||
TEST_F(ReadOnlyEntityFixture, SingleHandlerEntityIsNotReadOnly)
|
||||
{
|
||||
// Create a handler that sets all entities to read-only.
|
||||
ReadOnlyHandlerAlwaysFalse alwaysFalseHandler;
|
||||
|
||||
// All entities should not be marked read-only now.
|
||||
EXPECT_FALSE(m_readOnlyEntityPublicInterface->IsReadOnly(m_entityMap[RootEntityName]));
|
||||
EXPECT_FALSE(m_readOnlyEntityPublicInterface->IsReadOnly(m_entityMap[ChildEntityName]));
|
||||
EXPECT_FALSE(m_readOnlyEntityPublicInterface->IsReadOnly(m_entityMap[GrandChild1EntityName]));
|
||||
EXPECT_FALSE(m_readOnlyEntityPublicInterface->IsReadOnly(m_entityMap[GrandChild2EntityName]));
|
||||
}
|
||||
|
||||
TEST_F(ReadOnlyEntityFixture, SingleHandlerWithLogic)
|
||||
{
|
||||
// Create a handler that sets just the child entity to read-only.
|
||||
ReadOnlyHandlerEntityId entityIdHandler(m_entityMap[ChildEntityName]);
|
||||
|
||||
EXPECT_FALSE(m_readOnlyEntityPublicInterface->IsReadOnly(m_entityMap[RootEntityName]));
|
||||
EXPECT_TRUE(m_readOnlyEntityPublicInterface->IsReadOnly(m_entityMap[ChildEntityName]));
|
||||
EXPECT_FALSE(m_readOnlyEntityPublicInterface->IsReadOnly(m_entityMap[GrandChild1EntityName]));
|
||||
EXPECT_FALSE(m_readOnlyEntityPublicInterface->IsReadOnly(m_entityMap[GrandChild2EntityName]));
|
||||
}
|
||||
|
||||
TEST_F(ReadOnlyEntityFixture, TwoHandlersCanOverlap)
|
||||
{
|
||||
// Create two handlers that set different entities to read-only.
|
||||
ReadOnlyHandlerEntityId entityIdHandler1(m_entityMap[ChildEntityName]);
|
||||
ReadOnlyHandlerEntityId entityIdHandler2(m_entityMap[GrandChild2EntityName]);
|
||||
|
||||
// Both entities should be marked as read-only, while others aren't.
|
||||
EXPECT_FALSE(m_readOnlyEntityPublicInterface->IsReadOnly(m_entityMap[RootEntityName]));
|
||||
EXPECT_TRUE(m_readOnlyEntityPublicInterface->IsReadOnly(m_entityMap[ChildEntityName]));
|
||||
EXPECT_FALSE(m_readOnlyEntityPublicInterface->IsReadOnly(m_entityMap[GrandChild1EntityName]));
|
||||
EXPECT_TRUE(m_readOnlyEntityPublicInterface->IsReadOnly(m_entityMap[GrandChild2EntityName]));
|
||||
}
|
||||
|
||||
TEST_F(ReadOnlyEntityFixture, EnsureCacheIsRefreshedCorrectly)
|
||||
{
|
||||
// Verify the child entity is not marked as read-only
|
||||
EXPECT_FALSE(m_readOnlyEntityPublicInterface->IsReadOnly(m_entityMap[ChildEntityName]));
|
||||
|
||||
// Create a handler that sets the child entity to read-only.
|
||||
ReadOnlyHandlerEntityId entityIdHandler(m_entityMap[ChildEntityName]);
|
||||
|
||||
// Communicate to the ReadOnlyEntitySystemComponent that the read-only state for the child entity may have changed.
|
||||
// Note that this operation would usually be executed by the handler, hence the Query interface call.
|
||||
if (auto readOnlyEntityQueryInterface = AZ::Interface<ReadOnlyEntityQueryInterface>::Get())
|
||||
{
|
||||
readOnlyEntityQueryInterface->RefreshReadOnlyState({ m_entityMap[ChildEntityName] });
|
||||
}
|
||||
|
||||
// Verify the child entity is marked as read-only
|
||||
EXPECT_TRUE(m_readOnlyEntityPublicInterface->IsReadOnly(m_entityMap[ChildEntityName]));
|
||||
}
|
||||
|
||||
TEST_F(ReadOnlyEntityFixture, EnsureCacheIsClearedCorrectly)
|
||||
{
|
||||
{
|
||||
// Create a handler that sets the child entity to read-only.
|
||||
ReadOnlyHandlerEntityId entityIdHandler(m_entityMap[ChildEntityName]);
|
||||
|
||||
// Verify the child entity is marked as read-only
|
||||
EXPECT_TRUE(m_readOnlyEntityPublicInterface->IsReadOnly(m_entityMap[ChildEntityName]));
|
||||
}
|
||||
// When the handler goes out of scope, it calls RefreshReadOnlyStateForAllEntities and refreshes the cache.
|
||||
|
||||
// Verify the child entity is no longer marked as read-only
|
||||
EXPECT_FALSE(m_readOnlyEntityPublicInterface->IsReadOnly(m_entityMap[ChildEntityName]));
|
||||
}
|
||||
}
|
||||
@@ -710,7 +710,7 @@ namespace UnitTest
|
||||
|
||||
auto getEnumData = [&ec](const AzToolsFramework::InstanceDataNode& node) -> Uuid
|
||||
{
|
||||
Uuid id;
|
||||
Uuid id = Uuid::CreateNull();
|
||||
auto attribute = node.GetElementMetadata()->FindAttribute(AZ_CRC("EnumType"));
|
||||
auto attributeData = azrtti_cast<AttributeData<AZ::TypeId>*>(attribute);
|
||||
if (attributeData)
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
|
||||
#include <AzToolsFramework/ToolsComponents/TransformComponent.h>
|
||||
|
||||
#include <Prefab/PrefabTestComponent.h>
|
||||
#include <Prefab/PrefabTestDomUtils.h>
|
||||
#include <Prefab/PrefabTestFixture.h>
|
||||
|
||||
namespace UnitTest
|
||||
{
|
||||
using PrefabDeleteTest = PrefabTestFixture;
|
||||
|
||||
TEST_F(PrefabDeleteTest, DeleteEntitiesInInstance_DeleteSingleEntitySucceeds)
|
||||
{
|
||||
PrefabEntityResult createEntityResult = m_prefabPublicInterface->CreateEntity(AZ::EntityId(), AZ::Vector3());
|
||||
|
||||
// Verify that a valid entity is created.
|
||||
AZ::EntityId testEntityId = createEntityResult.GetValue();
|
||||
ASSERT_TRUE(testEntityId.IsValid());
|
||||
AZ::Entity* testEntity = AzToolsFramework::GetEntityById(testEntityId);
|
||||
ASSERT_TRUE(testEntity != nullptr);
|
||||
|
||||
m_prefabPublicInterface->DeleteEntitiesInInstance(AzToolsFramework::EntityIdList{ testEntityId });
|
||||
|
||||
// Verify that entity can't be found after deletion.
|
||||
testEntity = AzToolsFramework::GetEntityById(testEntityId);
|
||||
EXPECT_TRUE(testEntity == nullptr);
|
||||
}
|
||||
|
||||
TEST_F(PrefabDeleteTest, DeleteEntitiesInInstance_DeleteSinglePrefabSucceeds)
|
||||
{
|
||||
PrefabEntityResult createEntityResult = m_prefabPublicInterface->CreateEntity(AZ::EntityId(), AZ::Vector3());
|
||||
|
||||
// Verify that a valid entity is created.
|
||||
AZ::EntityId createdEntityId = createEntityResult.GetValue();
|
||||
ASSERT_TRUE(createdEntityId.IsValid());
|
||||
AZ::Entity* createdEntity = AzToolsFramework::GetEntityById(createdEntityId);
|
||||
ASSERT_TRUE(createdEntity != nullptr);
|
||||
|
||||
// Rather than hardcode a path, use a path from settings registry since that will work on all platforms.
|
||||
AZ::SettingsRegistryInterface* registry = AZ::SettingsRegistry::Get();
|
||||
AZ::IO::FixedMaxPath path;
|
||||
registry->Get(path.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder);
|
||||
CreatePrefabResult createPrefabResult =
|
||||
m_prefabPublicInterface->CreatePrefabInMemory(AzToolsFramework::EntityIdList{ createdEntityId }, path);
|
||||
|
||||
AZ::EntityId createdPrefabContainerId = createPrefabResult.GetValue();
|
||||
ASSERT_TRUE(createdPrefabContainerId.IsValid());
|
||||
AZ::Entity* prefabContainerEntity = AzToolsFramework::GetEntityById(createdPrefabContainerId);
|
||||
ASSERT_TRUE(prefabContainerEntity != nullptr);
|
||||
|
||||
// Verify that the prefab container entity and the entity within are deleted.
|
||||
m_prefabPublicInterface->DeleteEntitiesInInstance(AzToolsFramework::EntityIdList{ createdPrefabContainerId });
|
||||
prefabContainerEntity = AzToolsFramework::GetEntityById(createdPrefabContainerId);
|
||||
EXPECT_TRUE(prefabContainerEntity == nullptr);
|
||||
createdEntity = AzToolsFramework::GetEntityById(createdEntityId);
|
||||
EXPECT_TRUE(createdEntity == nullptr);
|
||||
}
|
||||
|
||||
TEST_F(PrefabDeleteTest, DeleteEntitiesAndAllDescendantsInInstance_DeletingEntityDeletesChildEntityToo)
|
||||
{
|
||||
PrefabEntityResult parentEntityCreationResult = m_prefabPublicInterface->CreateEntity(AZ::EntityId(), AZ::Vector3());
|
||||
|
||||
// Verify that valid parent entity is created.
|
||||
AZ::EntityId parentEntityId = parentEntityCreationResult.GetValue();
|
||||
ASSERT_TRUE(parentEntityId.IsValid());
|
||||
AZ::Entity* parentEntity = AzToolsFramework::GetEntityById(parentEntityId);
|
||||
ASSERT_TRUE(parentEntity != nullptr);
|
||||
|
||||
// Verify that valid child entity is created.
|
||||
PrefabEntityResult childEntityCreationResult = m_prefabPublicInterface->CreateEntity(parentEntityId, AZ::Vector3());
|
||||
AZ::EntityId childEntityId = childEntityCreationResult.GetValue();
|
||||
ASSERT_TRUE(childEntityId.IsValid());
|
||||
AZ::Entity* childEntity = AzToolsFramework::GetEntityById(childEntityId);
|
||||
ASSERT_TRUE(childEntity != nullptr);
|
||||
|
||||
// PrefabTestFixture won't add required editor components by default. Hence we add them here.
|
||||
AddRequiredEditorComponents(childEntity);
|
||||
AddRequiredEditorComponents(parentEntity);
|
||||
|
||||
// Parent the child entity under the parent entity.
|
||||
AZ::TransformBus::Event(childEntityId, &AZ::TransformBus::Events::SetParent, parentEntityId);
|
||||
|
||||
// Delete parent entity and its children.
|
||||
m_prefabPublicInterface->DeleteEntitiesAndAllDescendantsInInstance(AzToolsFramework::EntityIdList{ parentEntityId });
|
||||
|
||||
// Verify that both the parent and child entities are deleted.
|
||||
parentEntity = AzToolsFramework::GetEntityById(parentEntityId);
|
||||
EXPECT_TRUE(parentEntity == nullptr);
|
||||
childEntity = AzToolsFramework::GetEntityById(childEntityId);
|
||||
EXPECT_TRUE(childEntity == nullptr);
|
||||
}
|
||||
|
||||
TEST_F(PrefabDeleteTest, DeleteEntitiesAndAllDescendantsInInstance_DeletingEntityDeletesChildPrefabToo)
|
||||
{
|
||||
PrefabEntityResult entityToBePutUnderPrefabResult = m_prefabPublicInterface->CreateEntity(AZ::EntityId(), AZ::Vector3());
|
||||
|
||||
// Verify that a valid entity is created that will be put in a prefab later.
|
||||
AZ::EntityId entityToBePutUnderPrefabId = entityToBePutUnderPrefabResult.GetValue();
|
||||
ASSERT_TRUE(entityToBePutUnderPrefabId.IsValid());
|
||||
AZ::Entity* entityToBePutUnderPrefab = AzToolsFramework::GetEntityById(entityToBePutUnderPrefabId);
|
||||
ASSERT_TRUE(entityToBePutUnderPrefab != nullptr);
|
||||
|
||||
// Verify that a valid parent entity is created.
|
||||
PrefabEntityResult parentEntityCreationResult = m_prefabPublicInterface->CreateEntity(AZ::EntityId(), AZ::Vector3());
|
||||
AZ::EntityId parentEntityId = parentEntityCreationResult.GetValue();
|
||||
ASSERT_TRUE(parentEntityId.IsValid());
|
||||
AZ::Entity* parentEntity = AzToolsFramework::GetEntityById(parentEntityId);
|
||||
ASSERT_TRUE(parentEntity != nullptr);
|
||||
|
||||
// Rather than hardcode a path, use a path from settings registry since that will work on all platforms.
|
||||
AZ::SettingsRegistryInterface* registry = AZ::SettingsRegistry::Get();
|
||||
AZ::IO::FixedMaxPath path;
|
||||
registry->Get(path.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder);
|
||||
CreatePrefabResult createPrefabResult =
|
||||
m_prefabPublicInterface->CreatePrefabInMemory(AzToolsFramework::EntityIdList{ entityToBePutUnderPrefabId }, path);
|
||||
|
||||
// Verify that a valid prefab container entity is created.
|
||||
AZ::EntityId createdPrefabContainerId = createPrefabResult.GetValue();
|
||||
ASSERT_TRUE(createdPrefabContainerId.IsValid());
|
||||
AZ::Entity* prefabContainerEntity = AzToolsFramework::GetEntityById(createdPrefabContainerId);
|
||||
ASSERT_TRUE(prefabContainerEntity != nullptr);
|
||||
|
||||
// PrefabTestFixture won't add required editor components by default. Hence we add them here.
|
||||
AddRequiredEditorComponents(parentEntity);
|
||||
AddRequiredEditorComponents(prefabContainerEntity);
|
||||
|
||||
// Parent the prefab under the parent entity.
|
||||
AZ::TransformBus::Event(createdPrefabContainerId, &AZ::TransformBus::Events::SetParent, parentEntityId);
|
||||
|
||||
// Delete the parent entity.
|
||||
m_prefabPublicInterface->DeleteEntitiesAndAllDescendantsInInstance(AzToolsFramework::EntityIdList{ parentEntityId });
|
||||
|
||||
// Validate that the parent and the prefab under it and the entity inside the prefab are all deleted.
|
||||
parentEntity = AzToolsFramework::GetEntityById(parentEntityId);
|
||||
ASSERT_TRUE(parentEntity == nullptr);
|
||||
entityToBePutUnderPrefab = AzToolsFramework::GetEntityById(entityToBePutUnderPrefabId);
|
||||
ASSERT_TRUE(entityToBePutUnderPrefab == nullptr);
|
||||
prefabContainerEntity = AzToolsFramework::GetEntityById(createdPrefabContainerId);
|
||||
EXPECT_TRUE(prefabContainerEntity == nullptr);
|
||||
}
|
||||
} // namespace UnitTest
|
||||
@@ -106,7 +106,9 @@ namespace UnitTest
|
||||
inline static const char* Passenger2EntityName = "Passenger2";
|
||||
};
|
||||
|
||||
TEST_F(PrefabFocusTests, PrefabFocus_FocusOnOwningPrefab_RootContainer)
|
||||
// Test was disabled because the implementation of GetFocusedPrefabInstance now relies on the Prefab EOS,
|
||||
// which is not used by our test environment. This can be restored once Instance handles are implemented.
|
||||
TEST_F(PrefabFocusTests, DISABLED_PrefabFocus_FocusOnOwningPrefab_RootContainer)
|
||||
{
|
||||
// Verify FocusOnOwningPrefab works when passing the container entity of the root prefab.
|
||||
{
|
||||
@@ -121,7 +123,9 @@ namespace UnitTest
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(PrefabFocusTests, PrefabFocus_FocusOnOwningPrefab_RootEntity)
|
||||
// Test was disabled because the implementation of GetFocusedPrefabInstance now relies on the Prefab EOS,
|
||||
// which is not used by our test environment. This can be restored once Instance handles are implemented.
|
||||
TEST_F(PrefabFocusTests, DISABLED_PrefabFocus_FocusOnOwningPrefab_RootEntity)
|
||||
{
|
||||
// Verify FocusOnOwningPrefab works when passing a nested entity of the root prefab.
|
||||
{
|
||||
|
||||
@@ -57,6 +57,11 @@ namespace UnitTest
|
||||
return AZStd::make_unique<PrefabTestToolsApplication>("PrefabTestApplication");
|
||||
}
|
||||
|
||||
void PrefabTestFixture::PropagateAllTemplateChanges()
|
||||
{
|
||||
m_prefabSystemComponent->OnSystemTick();
|
||||
}
|
||||
|
||||
AZ::Entity* PrefabTestFixture::CreateEntity(const char* entityName, const bool shouldActivate)
|
||||
{
|
||||
// Circumvent the EntityContext system and generate a new entity with a transformcomponent
|
||||
@@ -125,4 +130,13 @@ namespace UnitTest
|
||||
EXPECT_EQ(entityInInstance->GetState(), AZ::Entity::State::Active);
|
||||
}
|
||||
}
|
||||
|
||||
void PrefabTestFixture::AddRequiredEditorComponents(AZ::Entity* entity)
|
||||
{
|
||||
ASSERT_TRUE(entity != nullptr);
|
||||
entity->Deactivate();
|
||||
AzToolsFramework::EditorEntityContextRequestBus::Broadcast(
|
||||
&AzToolsFramework::EditorEntityContextRequests::AddRequiredComponents, *entity);
|
||||
entity->Activate();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,6 +52,8 @@ namespace UnitTest
|
||||
|
||||
AZStd::unique_ptr<ToolsTestApplication> CreateTestApplication() override;
|
||||
|
||||
void PropagateAllTemplateChanges();
|
||||
|
||||
AZ::Entity* CreateEntity(const char* entityName, const bool shouldActivate = true);
|
||||
|
||||
void CompareInstances(const Instance& instanceA, const Instance& instanceB, bool shouldCompareLinkIds = true,
|
||||
@@ -62,6 +64,8 @@ namespace UnitTest
|
||||
//! Validates that all entities within a prefab instance are in 'Active' state.
|
||||
void ValidateInstanceEntitiesActive(Instance& instance);
|
||||
|
||||
void AddRequiredEditorComponents(AZ::Entity* entity);
|
||||
|
||||
PrefabSystemComponent* m_prefabSystemComponent = nullptr;
|
||||
PrefabLoaderInterface* m_prefabLoaderInterface = nullptr;
|
||||
PrefabPublicInterface* m_prefabPublicInterface = nullptr;
|
||||
|
||||
@@ -6,7 +6,9 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "IntegerPrimtitiveTestConfig.h"
|
||||
#include <AzToolsFramework/UI/PropertyEditor/PropertyIntCtrlCommon.h>
|
||||
#include <AzToolsFramework/UI/PropertyEditor/QtWidgetLimits.h>
|
||||
#include <Tests/IntegerPrimtitiveTestConfig.h>
|
||||
|
||||
namespace UnitTest
|
||||
{
|
||||
|
||||
@@ -156,6 +156,16 @@ namespace UnitTest
|
||||
EXPECT_STREQ(tooltip.toStdString().c_str(), expected.str().c_str());
|
||||
}
|
||||
|
||||
void EmitWidgetValueChanged()
|
||||
{
|
||||
emit m_widget->valueChanged(ValueType(0));
|
||||
}
|
||||
|
||||
void EmitWidgetEditingFinished()
|
||||
{
|
||||
emit m_widget->editingFinished();
|
||||
}
|
||||
|
||||
AZStd::unique_ptr<QWidget> m_dummyWidget;
|
||||
AZStd::unique_ptr<HandlerAPI> m_handler;
|
||||
WidgetType* m_widget;
|
||||
|
||||
@@ -47,4 +47,71 @@ namespace UnitTest
|
||||
{
|
||||
this->HandlerMinMaxLessLimit_ModifyHandler_ExpectSuccessAndValidLessLimitToolTipString();
|
||||
}
|
||||
|
||||
struct PropertyEditorHandler
|
||||
: public AzToolsFramework::PropertyEditorGUIMessages::Bus::Handler
|
||||
{
|
||||
PropertyEditorHandler()
|
||||
{
|
||||
AzToolsFramework::PropertyEditorGUIMessages::Bus::Handler::BusConnect();
|
||||
}
|
||||
|
||||
~PropertyEditorHandler()
|
||||
{
|
||||
AzToolsFramework::PropertyEditorGUIMessages::Bus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
// AzToolsFramework::PropertyEditorGUIMessages::Bus overrides ...
|
||||
void RequestWrite([[maybe_unused]] QWidget* editorGUI) override
|
||||
{
|
||||
m_requestWriteCallCount++;
|
||||
}
|
||||
|
||||
void RequestRefresh([[maybe_unused]] PropertyModificationRefreshLevel level) override
|
||||
{
|
||||
}
|
||||
|
||||
void AddElementsToParentContainer(
|
||||
[[maybe_unused]] QWidget* editorGUI,
|
||||
[[maybe_unused]] size_t numElements,
|
||||
[[maybe_unused]] const InstanceDataNode::FillDataClassCallback& fillDataCallback) override
|
||||
{
|
||||
}
|
||||
|
||||
void RequestPropertyNotify([[maybe_unused]] QWidget* editorGUI) override
|
||||
{
|
||||
}
|
||||
|
||||
void OnEditingFinished([[maybe_unused]]QWidget* editorGUI) override
|
||||
{
|
||||
m_onEditingFinishedCallCount++;
|
||||
}
|
||||
|
||||
int m_requestWriteCallCount = 0;
|
||||
int m_onEditingFinishedCallCount = 0;
|
||||
};
|
||||
|
||||
TYPED_TEST(PropertySpinCtrlFixture, SpinBoxWidgetValueChangedInvokesPropertyEditorGUIMessages)
|
||||
{
|
||||
// setup the event handler
|
||||
PropertyEditorHandler eventHandler;
|
||||
|
||||
// trigger the QT signal
|
||||
this->EmitWidgetValueChanged();
|
||||
|
||||
// there should be at least 1 call to RequestWrite.
|
||||
EXPECT_GT(eventHandler.m_requestWriteCallCount, 0);
|
||||
}
|
||||
|
||||
TYPED_TEST(PropertySpinCtrlFixture, SpinBoxWidgetEditingFinishedInvokesPropertyEditorGUIMessages)
|
||||
{
|
||||
// setup the event handler
|
||||
PropertyEditorHandler eventHandler;
|
||||
|
||||
// trigger the QT signal
|
||||
this->EmitWidgetEditingFinished();
|
||||
|
||||
// there should be at least 1 call to OnEditingFinished.
|
||||
EXPECT_GT(eventHandler.m_onEditingFinishedCallCount, 0);
|
||||
}
|
||||
} // namespace UnitTest
|
||||
|
||||
@@ -128,7 +128,7 @@ namespace UnitTest
|
||||
void ProcessDeferredUpdates()
|
||||
{
|
||||
// Force a prefab propagation for updates that are deferred to the next tick.
|
||||
m_prefabSystemComponent->OnSystemTick();
|
||||
PropagateAllTemplateChanges();
|
||||
|
||||
// Ensure the model process its entity update queue
|
||||
m_model->ProcessEntityUpdates();
|
||||
|
||||
@@ -36,11 +36,6 @@ namespace UnitTest
|
||||
: public ComponentApplication
|
||||
{
|
||||
public:
|
||||
void SetExecutableFolder(const char* path)
|
||||
{
|
||||
m_exeDirectory = path;
|
||||
}
|
||||
|
||||
void SetSettingsRegistrySpecializations(SettingsRegistryInterface::Specializations& specializations) override
|
||||
{
|
||||
ComponentApplication::SetSettingsRegistrySpecializations(specializations);
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzFramework/Viewport/ViewportScreen.h>
|
||||
#include <AzManipulatorTestFramework/AzManipulatorTestFrameworkTestHelpers.h>
|
||||
#include <AzManipulatorTestFramework/AzManipulatorTestFrameworkUtils.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
|
||||
#include <AzToolsFramework/UnitTest/AzToolsFrameworkTestHelpers.h>
|
||||
#include <AzToolsFramework/Viewport/ViewportMessages.h>
|
||||
#include <AzToolsFramework/ViewportSelection/EditorSelectionUtil.h>
|
||||
#include <Tests/BoundsTestComponent.h>
|
||||
|
||||
namespace UnitTest
|
||||
{
|
||||
class IndirectCallViewportInteractionIntersectionFixture : public ToolsApplicationFixture
|
||||
{
|
||||
public:
|
||||
void SetUpEditorFixtureImpl() override
|
||||
{
|
||||
auto* app = GetApplication();
|
||||
// register a simple component implementing BoundsRequestBus and EditorComponentSelectionRequestsBus
|
||||
app->RegisterComponentDescriptor(BoundsTestComponent::CreateDescriptor());
|
||||
// register a component implementing RenderGeometry::IntersectionRequestBus
|
||||
app->RegisterComponentDescriptor(RenderGeometryIntersectionTestComponent::CreateDescriptor());
|
||||
|
||||
AZ::Entity* entityGround = nullptr;
|
||||
m_entityIdGround = CreateDefaultEditorEntity("EntityGround", &entityGround);
|
||||
|
||||
entityGround->Deactivate();
|
||||
auto ground = entityGround->CreateComponent<RenderGeometryIntersectionTestComponent>();
|
||||
entityGround->Activate();
|
||||
|
||||
ground->m_localBounds = AZ::Aabb::CreateFromMinMax(AZ::Vector3(-10.0f, -10.0f, -0.5f), AZ::Vector3(10.0f, 10.0f, 0.5f));
|
||||
}
|
||||
|
||||
AZ::EntityId m_entityIdGround;
|
||||
};
|
||||
|
||||
using IndirectCallManipulatorViewportInteractionIntersectionFixture =
|
||||
IndirectCallManipulatorViewportInteractionFixtureMixin<IndirectCallViewportInteractionIntersectionFixture>;
|
||||
|
||||
TEST_F(IndirectCallManipulatorViewportInteractionIntersectionFixture, FindClosestPickIntersectionReturnsExpectedSurfacePoint)
|
||||
{
|
||||
// camera - 21.00, 8.00, 11.00, -22.00, 150.00
|
||||
m_cameraState.m_viewportSize = AZ::Vector2(1280.0f, 720.0f);
|
||||
AzFramework::SetCameraTransform(
|
||||
m_cameraState,
|
||||
AZ::Transform::CreateFromMatrix3x3AndTranslation(
|
||||
AZ::Matrix3x3::CreateRotationZ(AZ::DegToRad(150.0f)) * AZ::Matrix3x3::CreateRotationX(AZ::DegToRad(-22.0f)),
|
||||
AZ::Vector3(21.0f, 8.0f, 11.0f)));
|
||||
|
||||
m_actionDispatcher->CameraState(m_cameraState);
|
||||
|
||||
AzToolsFramework::SetWorldTransform(
|
||||
m_entityIdGround,
|
||||
AZ::Transform::CreateFromMatrix3x3AndTranslation(
|
||||
AZ::Matrix3x3::CreateRotationY(AZ::DegToRad(40.0f)) * AZ::Matrix3x3::CreateRotationZ(AZ::DegToRad(60.0f)),
|
||||
AZ::Vector3(14.0f, -6.0f, 5.0f)));
|
||||
|
||||
// expected world position (value taken from editor scenario)
|
||||
const auto expectedWorldPosition = AZ::Vector3(13.606657f, -2.6753534f, 5.9827675f);
|
||||
const auto screenPosition = AzFramework::WorldToScreen(expectedWorldPosition, m_cameraState);
|
||||
|
||||
// perform ray intersection against mesh
|
||||
const auto worldIntersectionPoint = AzToolsFramework::FindClosestPickIntersection(
|
||||
m_viewportManipulatorInteraction->GetViewportInteraction().GetViewportId(), screenPosition,
|
||||
AzToolsFramework::EditorPickRayLength, AzToolsFramework::GetDefaultEntityPlacementDistance());
|
||||
|
||||
EXPECT_THAT(worldIntersectionPoint, IsCloseTolerance(expectedWorldPosition, 0.01f));
|
||||
}
|
||||
} // namespace UnitTest
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user