Merge branch 'development' into cmake/warn_virtual
Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com>
This commit is contained in:
@@ -96,7 +96,7 @@ namespace AzToolsFramework
|
||||
{
|
||||
AZ::EBusReduceResult<AZ::Aabb, AzFramework::AabbUnionAggregator> aabbResult(AZ::Aabb::CreateNull());
|
||||
EditorComponentSelectionRequestsBus::EventResult(
|
||||
aabbResult, entityId, &EditorComponentSelectionRequests::GetEditorSelectionBoundsViewport, viewportInfo);
|
||||
aabbResult, entityId, &EditorComponentSelectionRequestsBus::Events::GetEditorSelectionBoundsViewport, viewportInfo);
|
||||
|
||||
return aabbResult.value;
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ namespace AzToolsFramework
|
||||
const char AssetBundleSettingsFileExtension[] = "bundlesettings";
|
||||
const char BundleFileExtension[] = "pak";
|
||||
const char ComparisonRulesFileExtension[] = "rules";
|
||||
const char ErrorWindowName[] = "AssetBundler";
|
||||
[[maybe_unused]] const char ErrorWindowName[] = "AssetBundler";
|
||||
const char* AssetFileInfoListComparison::ComparisonTypeNames[] = { "delta", "union", "intersection", "complement", "filepattern", "intersectioncount" };
|
||||
const char* AssetFileInfoListComparison::FilePatternTypeNames[] = { "wildcard", "regex" };
|
||||
const char DefaultTypeName[] = "default";
|
||||
|
||||
+2
@@ -43,6 +43,8 @@ namespace AzToolsFramework
|
||||
|
||||
virtual Prefab::InstanceOptionalReference GetRootPrefabInstance() = 0;
|
||||
|
||||
virtual Prefab::TemplateId GetRootPrefabTemplateId() = 0;
|
||||
|
||||
//! Get all Assets generated by Prefab processing when entering Play-In Editor mode (Ctrl+G)
|
||||
//! /return The vector of Assets generated by Prefab processing
|
||||
virtual const AZStd::vector<AZ::Data::Asset<AZ::Data::AssetData>>& GetPlayInEditorAssetData() = 0;
|
||||
|
||||
+6
@@ -359,6 +359,12 @@ namespace AzToolsFramework
|
||||
return AZStd::nullopt;
|
||||
}
|
||||
|
||||
Prefab::TemplateId PrefabEditorEntityOwnershipService::GetRootPrefabTemplateId()
|
||||
{
|
||||
AZ_Assert(m_rootInstance, "A valid root prefab instance couldn't be found in PrefabEditorEntityOwnershipService.");
|
||||
return m_rootInstance ? m_rootInstance->GetTemplateId() : Prefab::InvalidTemplateId;
|
||||
}
|
||||
|
||||
const AZStd::vector<AZ::Data::Asset<AZ::Data::AssetData>>& PrefabEditorEntityOwnershipService::GetPlayInEditorAssetData()
|
||||
{
|
||||
return m_playInEditorData.m_assets;
|
||||
|
||||
+1
@@ -193,6 +193,7 @@ namespace AzToolsFramework
|
||||
AZ::IO::PathView filePath, Prefab::InstanceOptionalReference instanceToParentUnder) override;
|
||||
|
||||
Prefab::InstanceOptionalReference GetRootPrefabInstance() override;
|
||||
Prefab::TemplateId GetRootPrefabTemplateId() override;
|
||||
|
||||
const AZStd::vector<AZ::Data::Asset<AZ::Data::AssetData>>& GetPlayInEditorAssetData() override;
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -26,6 +26,19 @@ namespace AzToolsFramework
|
||||
{
|
||||
namespace Prefab
|
||||
{
|
||||
static constexpr const char s_saveAllPrefabsKey[] = "/O3DE/Preferences/Prefabs/SaveAllPrefabs";
|
||||
|
||||
void PrefabLoader::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
|
||||
{
|
||||
serializeContext->Enum<SaveAllPrefabsPreference>()
|
||||
->Value("Ask every time", SaveAllPrefabsPreference::AskEveryTime)
|
||||
->Value("Save all", SaveAllPrefabsPreference::SaveAll)
|
||||
->Value("Save none", SaveAllPrefabsPreference::SaveNone);
|
||||
}
|
||||
}
|
||||
|
||||
void PrefabLoader::RegisterPrefabLoaderInterface()
|
||||
{
|
||||
m_prefabSystemComponentInterface = AZ::Interface<PrefabSystemComponentInterface>::Get();
|
||||
@@ -657,6 +670,24 @@ namespace AzToolsFramework
|
||||
return finalPath;
|
||||
}
|
||||
|
||||
SaveAllPrefabsPreference PrefabLoader::GetSaveAllPrefabsPreference() const
|
||||
{
|
||||
SaveAllPrefabsPreference saveAllPrefabsPreference = SaveAllPrefabsPreference::AskEveryTime;
|
||||
if (auto* registry = AZ::SettingsRegistry::Get())
|
||||
{
|
||||
registry->GetObject(saveAllPrefabsPreference, s_saveAllPrefabsKey);
|
||||
}
|
||||
return saveAllPrefabsPreference;
|
||||
}
|
||||
|
||||
void PrefabLoader::SetSaveAllPrefabsPreference(SaveAllPrefabsPreference saveAllPrefabsPreference)
|
||||
{
|
||||
if (auto* registry = AZ::SettingsRegistry::Get())
|
||||
{
|
||||
registry->SetObject(s_saveAllPrefabsKey, saveAllPrefabsPreference);
|
||||
}
|
||||
}
|
||||
|
||||
AZ::IO::Path PrefabLoaderInterface::GeneratePath()
|
||||
{
|
||||
return AZStd::string::format("Prefab_%s", AZ::Entity::MakeId().ToString().c_str());
|
||||
|
||||
@@ -39,6 +39,8 @@ namespace AzToolsFramework
|
||||
AZ_CLASS_ALLOCATOR(PrefabLoader, AZ::SystemAllocator, 0);
|
||||
AZ_RTTI(PrefabLoader, "{A302B072-4DC4-4B7E-9188-226F56A3429C8}", PrefabLoaderInterface);
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// PrefabLoaderInterface interface implementation
|
||||
|
||||
@@ -108,6 +110,9 @@ namespace AzToolsFramework
|
||||
//! Returns if the path is a valid path for a prefab
|
||||
static bool IsValidPrefabPath(AZ::IO::PathView path);
|
||||
|
||||
SaveAllPrefabsPreference GetSaveAllPrefabsPreference() const override;
|
||||
void SetSaveAllPrefabsPreference(SaveAllPrefabsPreference saveAllPrefabsPreference) override;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Copies the template dom provided and manipulates it into the proper format to be saved to disk.
|
||||
|
||||
@@ -17,6 +17,13 @@ namespace AzToolsFramework
|
||||
{
|
||||
namespace Prefab
|
||||
{
|
||||
enum class SaveAllPrefabsPreference
|
||||
{
|
||||
AskEveryTime,
|
||||
SaveAll,
|
||||
SaveNone
|
||||
};
|
||||
|
||||
/*!
|
||||
* PrefabLoaderInterface
|
||||
* Interface for saving/loading Prefab files.
|
||||
@@ -84,6 +91,9 @@ namespace AzToolsFramework
|
||||
//! The path will always use the '/' separator.
|
||||
virtual AZ::IO::Path GenerateRelativePath(AZ::IO::PathView path) = 0;
|
||||
|
||||
virtual SaveAllPrefabsPreference GetSaveAllPrefabsPreference() const = 0;
|
||||
virtual void SetSaveAllPrefabsPreference(SaveAllPrefabsPreference saveAllPrefabsPreference) = 0;
|
||||
|
||||
protected:
|
||||
|
||||
// Generates a new path
|
||||
@@ -93,3 +103,8 @@ namespace AzToolsFramework
|
||||
} // namespace Prefab
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
AZ_TYPE_INFO_SPECIALIZE(AzToolsFramework::Prefab::SaveAllPrefabsPreference, "{7E61EA82-4DE4-4A3F-945F-C8FEDC1114B5}");
|
||||
}
|
||||
|
||||
|
||||
@@ -57,6 +57,7 @@ namespace AzToolsFramework
|
||||
AzToolsFramework::Prefab::PrefabConversionUtils::PrefabCatchmentProcessor::Reflect(context);
|
||||
AzToolsFramework::Prefab::PrefabConversionUtils::EditorInfoRemover::Reflect(context);
|
||||
PrefabPublicRequestHandler::Reflect(context);
|
||||
PrefabLoader::Reflect(context);
|
||||
|
||||
AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
|
||||
if (serialize)
|
||||
@@ -369,9 +370,11 @@ namespace AzToolsFramework
|
||||
PrefabDom& PrefabSystemComponent::FindTemplateDom(TemplateId templateId)
|
||||
{
|
||||
AZStd::optional<AZStd::reference_wrapper<Template>> findTemplateResult = FindTemplate(templateId);
|
||||
AZ_Assert(findTemplateResult.has_value(),
|
||||
AZ_Assert(
|
||||
findTemplateResult.has_value(),
|
||||
"PrefabSystemComponent::FindTemplateDom - Unable to retrieve Prefab template with id: '%llu'. "
|
||||
"Template could not be found", templateId);
|
||||
"Template could not be found",
|
||||
templateId);
|
||||
|
||||
AZ_Assert(findTemplateResult->get().IsValid(),
|
||||
"PrefabSystemComponent::FindTemplateDom - Unable to retrieve Prefab template with id: '%llu'. "
|
||||
@@ -526,12 +529,10 @@ namespace AzToolsFramework
|
||||
|
||||
Template& targetTemplate = targetTemplateReference->get();
|
||||
|
||||
#if defined(AZ_ENABLE_TRACING)
|
||||
Template& sourceTemplate = sourceTemplateReference->get();
|
||||
AZStd::string_view instanceName(instanceIterator->name.GetString(), instanceIterator->name.GetStringLength());
|
||||
const AZStd::string& targetTemplateFilePath = targetTemplate.GetFilePath().Native();
|
||||
const AZStd::string& sourceTemplateFilePath = sourceTemplate.GetFilePath().Native();
|
||||
#endif
|
||||
|
||||
LinkId newLinkId = CreateUniqueLinkId();
|
||||
Link newLink(newLinkId);
|
||||
@@ -753,6 +754,89 @@ namespace AzToolsFramework
|
||||
}
|
||||
}
|
||||
|
||||
bool PrefabSystemComponent::AreDirtyTemplatesPresent(TemplateId rootTemplateId)
|
||||
{
|
||||
TemplateReference prefabTemplate = FindTemplate(rootTemplateId);
|
||||
|
||||
if (!prefabTemplate.has_value())
|
||||
{
|
||||
AZ_Assert(false, "Template with id %llu is not found", rootTemplateId);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IsTemplateDirty(rootTemplateId))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
const Template::Links& linkIds = prefabTemplate->get().GetLinks();
|
||||
|
||||
for (LinkId linkId : linkIds)
|
||||
{
|
||||
auto linkIterator = m_linkIdMap.find(linkId);
|
||||
if (linkIterator != m_linkIdMap.end())
|
||||
{
|
||||
return AreDirtyTemplatesPresent(linkIterator->second.GetSourceTemplateId());
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void PrefabSystemComponent::SaveAllDirtyTemplates(TemplateId rootTemplateId)
|
||||
{
|
||||
AZStd::set<AZ::IO::PathView> dirtyTemplatePaths = GetDirtyTemplatePaths(rootTemplateId);
|
||||
|
||||
for (AZ::IO::PathView dirtyTemplatePath : dirtyTemplatePaths)
|
||||
{
|
||||
auto dirtyTemplateIterator = m_templateFilePathToIdMap.find(dirtyTemplatePath);
|
||||
if (dirtyTemplateIterator == m_templateFilePathToIdMap.end())
|
||||
{
|
||||
AZ_Assert(false, "Template id for template with path '%s' is not found.", dirtyTemplatePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_prefabLoader.SaveTemplate(dirtyTemplateIterator->second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AZStd::set<AZ::IO::PathView> PrefabSystemComponent::GetDirtyTemplatePaths(TemplateId rootTemplateId)
|
||||
{
|
||||
AZStd::vector<AZ::IO::PathView> dirtyTemplatePathVector;
|
||||
GetDirtyTemplatePathsHelper(rootTemplateId, dirtyTemplatePathVector);
|
||||
AZStd::set<AZ::IO::PathView> dirtyTemplatePaths;
|
||||
dirtyTemplatePaths.insert(dirtyTemplatePathVector.begin(), dirtyTemplatePathVector.end());
|
||||
return AZStd::move(dirtyTemplatePaths);
|
||||
}
|
||||
|
||||
void PrefabSystemComponent::GetDirtyTemplatePathsHelper(
|
||||
TemplateId rootTemplateId, AZStd::vector<AZ::IO::PathView>& dirtyTemplatePaths)
|
||||
{
|
||||
TemplateReference prefabTemplate = FindTemplate(rootTemplateId);
|
||||
|
||||
if (!prefabTemplate.has_value())
|
||||
{
|
||||
AZ_Assert(false, "Template with id %llu is not found", rootTemplateId);
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsTemplateDirty(rootTemplateId))
|
||||
{
|
||||
dirtyTemplatePaths.emplace_back(prefabTemplate->get().GetFilePath());
|
||||
}
|
||||
|
||||
const Template::Links& linkIds = prefabTemplate->get().GetLinks();
|
||||
|
||||
for (LinkId linkId : linkIds)
|
||||
{
|
||||
auto linkIterator = m_linkIdMap.find(linkId);
|
||||
if (linkIterator != m_linkIdMap.end())
|
||||
{
|
||||
GetDirtyTemplatePathsHelper(linkIterator->second.GetSourceTemplateId(), dirtyTemplatePaths);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool PrefabSystemComponent::ConnectTemplates(
|
||||
Link& link,
|
||||
TemplateId sourceTemplateId,
|
||||
@@ -770,10 +854,8 @@ namespace AzToolsFramework
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined(AZ_ENABLE_TRACING)
|
||||
Template& sourceTemplate = sourceTemplateReference->get();
|
||||
Template& targetTemplate = targetTemplateReference->get();
|
||||
#endif
|
||||
|
||||
AZStd::string_view instanceName(instanceIterator->name.GetString(), instanceIterator->name.GetStringLength());
|
||||
|
||||
@@ -783,10 +865,9 @@ namespace AzToolsFramework
|
||||
|
||||
PrefabDomValue& instance = instanceIterator->value;
|
||||
AZ_Assert(instance.IsObject(), "Nested instance DOM provided is not a valid JSON object.");
|
||||
[[maybe_unused]] PrefabDomValueReference sourceTemplateName = PrefabDomUtils::FindPrefabDomValue(instance, PrefabDomUtils::SourceName);
|
||||
PrefabDomValueReference sourceTemplateName = PrefabDomUtils::FindPrefabDomValue(instance, PrefabDomUtils::SourceName);
|
||||
AZ_Assert(sourceTemplateName, "Couldn't find source template name in the DOM of the nested instance while creating a link.");
|
||||
AZ_Assert(
|
||||
sourceTemplateName->get() == sourceTemplate.GetFilePath().c_str(),
|
||||
AZ_Assert(sourceTemplateName->get() == sourceTemplate.GetFilePath().c_str(),
|
||||
"The name of the source template in the nested instance DOM does not match the name of the source template already loaded");
|
||||
|
||||
PrefabDomValueReference patchesReference = PrefabDomUtils::FindPrefabDomValue(instance, PrefabDomUtils::PatchesName);
|
||||
|
||||
@@ -183,6 +183,12 @@ namespace AzToolsFramework
|
||||
*/
|
||||
void SetTemplateDirtyFlag(const TemplateId& templateId, bool dirty) override;
|
||||
|
||||
bool AreDirtyTemplatesPresent(TemplateId rootTemplateId) override;
|
||||
|
||||
void SaveAllDirtyTemplates(TemplateId rootTemplateId) override;
|
||||
|
||||
AZStd::set<AZ::IO::PathView> GetDirtyTemplatePaths(TemplateId rootTemplateId) override;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
@@ -335,6 +341,9 @@ namespace AzToolsFramework
|
||||
*/
|
||||
bool RemoveLinkFromTargetTemplate(const LinkId& linkId, const Link& link);
|
||||
|
||||
// Helper function for GetDirtyTemplatePaths(). It uses vector to speed up iteration times.
|
||||
void GetDirtyTemplatePathsHelper(TemplateId rootTemplateId, AZStd::vector<AZ::IO::PathView>& dirtyTemplatePaths);
|
||||
|
||||
// A container for mapping Templates to the Links they may propagate changes to.
|
||||
AZStd::unordered_map<TemplateId, AZStd::unordered_set<LinkId>> m_templateToLinkIdsMap;
|
||||
|
||||
|
||||
+15
-1
@@ -9,11 +9,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
#include <AzCore/std/containers/set.h>
|
||||
#include <AzCore/std/smart_ptr/unique_ptr.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/Instance.h>
|
||||
#include <AzToolsFramework/Prefab/Link/Link.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabIdTypes.h>
|
||||
#include <AzToolsFramework/Prefab/Template/Template.h>
|
||||
#include <AzCore/std/smart_ptr/unique_ptr.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
@@ -50,6 +51,19 @@ namespace AzToolsFramework
|
||||
virtual bool IsTemplateDirty(const TemplateId& templateId) = 0;
|
||||
virtual void SetTemplateDirtyFlag(const TemplateId& templateId, bool dirty) = 0;
|
||||
|
||||
//! Recursive function to check if the template is dirty or if any dirty templates are presents in the links of the template.
|
||||
//! @param rootTemplateId The id of the template provided as the beginning template to check the outgoing links.
|
||||
virtual bool AreDirtyTemplatesPresent(TemplateId rootTemplateId) = 0;
|
||||
|
||||
//! Recursive function to save if the template is dirty and save all the dirty templates in the links of the template.
|
||||
//! @param rootTemplateId The id of the template provided as the beginning template to check the outgoing links.
|
||||
virtual void SaveAllDirtyTemplates(TemplateId rootTemplateId) = 0;
|
||||
|
||||
//! Recursive function that fetches the set of dirty templates given a starting template to check for outgoing links.
|
||||
//! @param rootTemplateId The id of the template provided as the beginning template to check the outgoing links.
|
||||
//! @return The set of dirty template paths populated.
|
||||
virtual AZStd::set<AZ::IO::PathView> GetDirtyTemplatePaths(TemplateId rootTemplateId) = 0;
|
||||
|
||||
virtual PrefabDom& FindTemplateDom(TemplateId templateId) = 0;
|
||||
virtual void UpdatePrefabTemplate(TemplateId templateId, const PrefabDom& updatedDom) = 0;
|
||||
virtual void PropagateTemplateChanges(TemplateId templateId, InstanceOptionalReference instanceToExclude = AZStd::nullopt) = 0;
|
||||
|
||||
+10
@@ -11,6 +11,7 @@
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
#include <AzCore/Math/Vector3.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabIdTypes.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
@@ -28,6 +29,15 @@ namespace AzToolsFramework
|
||||
* @return The id of the newly created entity.
|
||||
*/
|
||||
virtual AZ::EntityId CreateNewEntityAtPosition(const AZ::Vector3& position, AZ::EntityId parentId) = 0;
|
||||
|
||||
//! Constructs and executes the close dialog on a prefab template corresponding to templateId.
|
||||
//! @param templateId The id of the template the user chose to close.
|
||||
virtual int ExecuteClosePrefabDialog(TemplateId templateId) = 0;
|
||||
|
||||
//! Constructs and executes the save dialog on a prefab template corresponding to templateId.
|
||||
//! @param templateId The id of the template the user chose to save.
|
||||
//! @param useSaveAllPrefabsPreference A flag indicating whether SaveAllPrefabsPreference should be used for saving templates.
|
||||
virtual void ExecuteSavePrefabDialog(TemplateId templateId, bool useSaveAllPrefabsPreference = false) = 0;
|
||||
};
|
||||
|
||||
} // namespace Prefab
|
||||
|
||||
+269
@@ -12,6 +12,7 @@
|
||||
#include <AzCore/IO/SystemFile.h>
|
||||
#include <AzCore/StringFunc/StringFunc.h>
|
||||
#include <AzCore/Component/TransformBus.h>
|
||||
#include <AzCore/std/smart_ptr/make_shared.h>
|
||||
|
||||
#include <AzFramework/API/ApplicationAPI.h>
|
||||
#include <AzFramework/Asset/AssetSystemBus.h>
|
||||
@@ -27,12 +28,28 @@
|
||||
#include <AzToolsFramework/UI/Prefab/PrefabIntegrationInterface.h>
|
||||
#include <AzToolsFramework/UI/UICore/WidgetHelpers.h>
|
||||
|
||||
#include <AzQtComponents/Components/Widgets/CheckBox.h>
|
||||
#include <AzQtComponents/Components/FlowLayout.h>
|
||||
#include <AzQtComponents/Components/StyleManager.h>
|
||||
#include <AzQtComponents/Components/Widgets/CardHeader.h>
|
||||
|
||||
|
||||
#include <QApplication>
|
||||
#include <QCheckBox>
|
||||
#include <QDialog>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QFileDialog>
|
||||
#include <QFileInfo>
|
||||
#include <QFrame>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QMainWindow>
|
||||
#include <QMenu>
|
||||
#include <QMessageBox>
|
||||
#include <QScrollArea>
|
||||
#include <QVBoxLayout>
|
||||
#include <QWidget>
|
||||
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
@@ -43,8 +60,19 @@ namespace AzToolsFramework
|
||||
PrefabPublicInterface* PrefabIntegrationManager::s_prefabPublicInterface = nullptr;
|
||||
PrefabEditInterface* PrefabIntegrationManager::s_prefabEditInterface = nullptr;
|
||||
PrefabLoaderInterface* PrefabIntegrationManager::s_prefabLoaderInterface = nullptr;
|
||||
PrefabSystemComponentInterface* PrefabIntegrationManager::s_prefabSystemComponentInterface = nullptr;
|
||||
|
||||
const AZStd::string PrefabIntegrationManager::s_prefabFileExtension = ".prefab";
|
||||
|
||||
static const char* const ClosePrefabDialog = "ClosePrefabDialog";
|
||||
static const char* const FooterSeparatorLine = "FooterSeparatorLine";
|
||||
static const char* const PrefabSavedMessageFrame = "PrefabSavedMessageFrame";
|
||||
static const char* const PrefabSavePreferenceHint = "PrefabSavePreferenceHint";
|
||||
static const char* const PrefabSaveWarningFrame = "PrefabSaveWarningFrame";
|
||||
static const char* const SaveDependentPrefabsCard = "SaveDependentPrefabsCard";
|
||||
static const char* const SavePrefabDialog = "SavePrefabDialog";
|
||||
static const char* const UnsavedPrefabFileName = "UnsavedPrefabFileName";
|
||||
|
||||
|
||||
void PrefabUserSettings::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
@@ -88,6 +116,13 @@ namespace AzToolsFramework
|
||||
return;
|
||||
}
|
||||
|
||||
s_prefabSystemComponentInterface = AZ::Interface<PrefabSystemComponentInterface>::Get();
|
||||
if (s_prefabSystemComponentInterface == nullptr)
|
||||
{
|
||||
AZ_Assert(false, "Prefab - could not get PrefabSystemComponentInterface on PrefabIntegrationManager construction.");
|
||||
return;
|
||||
}
|
||||
|
||||
EditorContextMenuBus::Handler::BusConnect();
|
||||
PrefabInstanceContainerNotificationBus::Handler::BusConnect();
|
||||
AZ::Interface<PrefabIntegrationInterface>::Register(this);
|
||||
@@ -1050,5 +1085,239 @@ namespace AzToolsFramework
|
||||
return AZ::EntityId();
|
||||
}
|
||||
}
|
||||
|
||||
int PrefabIntegrationManager::ExecuteClosePrefabDialog(TemplateId templateId)
|
||||
{
|
||||
if (s_prefabSystemComponentInterface->AreDirtyTemplatesPresent(templateId))
|
||||
{
|
||||
auto prefabSaveSelectionDialog = ConstructClosePrefabDialog(templateId);
|
||||
|
||||
int prefabSaveSelection = prefabSaveSelectionDialog->exec();
|
||||
|
||||
if (prefabSaveSelection == QDialog::Accepted)
|
||||
{
|
||||
SavePrefabsInDialog(prefabSaveSelectionDialog.get());
|
||||
}
|
||||
|
||||
return prefabSaveSelection;
|
||||
}
|
||||
|
||||
return QDialogButtonBox::DestructiveRole;
|
||||
}
|
||||
|
||||
void PrefabIntegrationManager::ExecuteSavePrefabDialog(TemplateId templateId, bool useSaveAllPrefabsPreference)
|
||||
{
|
||||
auto prefabTemplate = s_prefabSystemComponentInterface->FindTemplate(templateId);
|
||||
AZ::IO::Path prefabTemplatePath = prefabTemplate->get().GetFilePath();
|
||||
|
||||
if (s_prefabSystemComponentInterface->IsTemplateDirty(templateId))
|
||||
{
|
||||
if (s_prefabLoaderInterface->SaveTemplate(templateId) == false)
|
||||
{
|
||||
AZ_Error("Prefab", false, "Template '%s' could not be saved successfully.", prefabTemplatePath.c_str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (s_prefabSystemComponentInterface->AreDirtyTemplatesPresent(templateId))
|
||||
{
|
||||
if (useSaveAllPrefabsPreference)
|
||||
{
|
||||
SaveAllPrefabsPreference saveAllPrefabsPreference = s_prefabLoaderInterface->GetSaveAllPrefabsPreference();
|
||||
|
||||
if (saveAllPrefabsPreference == SaveAllPrefabsPreference::SaveAll)
|
||||
{
|
||||
s_prefabSystemComponentInterface->SaveAllDirtyTemplates(templateId);
|
||||
return;
|
||||
}
|
||||
else if (saveAllPrefabsPreference == SaveAllPrefabsPreference::SaveNone)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
AZStd::unique_ptr<QDialog> savePrefabDialog = ConstructSavePrefabDialog(templateId, useSaveAllPrefabsPreference);
|
||||
if (savePrefabDialog)
|
||||
{
|
||||
int prefabSaveSelection = savePrefabDialog->exec();
|
||||
|
||||
if (prefabSaveSelection == QDialog::Accepted)
|
||||
{
|
||||
SavePrefabsInDialog(savePrefabDialog.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PrefabIntegrationManager::SavePrefabsInDialog(QDialog* unsavedPrefabsDialog)
|
||||
{
|
||||
QList<QLabel*> unsavedPrefabFileLabels = unsavedPrefabsDialog->findChildren<QLabel*>(UnsavedPrefabFileName);
|
||||
if (unsavedPrefabFileLabels.size() > 0)
|
||||
{
|
||||
for (const QLabel* unsavedPrefabFileLabel : unsavedPrefabFileLabels)
|
||||
{
|
||||
AZStd::string unsavedPrefabFileName = unsavedPrefabFileLabel->property("FilePath").toString().toUtf8().data();
|
||||
AzToolsFramework::Prefab::TemplateId unsavedPrefabTemplateId =
|
||||
s_prefabSystemComponentInterface->GetTemplateIdFromFilePath(unsavedPrefabFileName.data());
|
||||
bool isTemplateSavedSuccessfully = s_prefabLoaderInterface->SaveTemplate(unsavedPrefabTemplateId);
|
||||
AZ_Error("Prefab", isTemplateSavedSuccessfully, "Prefab '%s' could not be saved successfully.", unsavedPrefabFileName.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AZStd::unique_ptr<QDialog> PrefabIntegrationManager::ConstructSavePrefabDialog(TemplateId templateId, bool useSaveAllPrefabsPreference)
|
||||
{
|
||||
AZStd::unique_ptr<QDialog> savePrefabDialog = AZStd::make_unique<QDialog>(AzToolsFramework::GetActiveWindow());
|
||||
|
||||
savePrefabDialog->setWindowTitle("Unsaved files detected");
|
||||
|
||||
// Main Content section begins.
|
||||
savePrefabDialog->setObjectName(SavePrefabDialog);
|
||||
QBoxLayout* contentLayout = new QVBoxLayout(savePrefabDialog.get());
|
||||
|
||||
QFrame* prefabSavedMessageFrame = new QFrame(savePrefabDialog.get());
|
||||
QHBoxLayout* prefabSavedMessageLayout = new QHBoxLayout(savePrefabDialog.get());
|
||||
prefabSavedMessageFrame->setObjectName(PrefabSavedMessageFrame);
|
||||
prefabSavedMessageFrame->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum);
|
||||
|
||||
// Add a checkMark icon next to the level entities saved message.
|
||||
QPixmap checkMarkIcon(QString(":/Notifications/checkmark.svg"));
|
||||
QLabel* prefabSavedSuccessfullyIconContainer = new QLabel(savePrefabDialog.get());
|
||||
prefabSavedSuccessfullyIconContainer->setPixmap(checkMarkIcon);
|
||||
prefabSavedSuccessfullyIconContainer->setFixedWidth(checkMarkIcon.width());
|
||||
|
||||
// Add a message that level entities are saved successfully.
|
||||
|
||||
auto prefabTemplate = s_prefabSystemComponentInterface->FindTemplate(templateId);
|
||||
AZ::IO::Path prefabTemplatePath = prefabTemplate->get().GetFilePath();
|
||||
QLabel* prefabSavedSuccessfullyLabel = new QLabel(
|
||||
QString("Prefab '<b>%1</b>' has been saved. Do you want to save the below dependent prefabs too?").arg(prefabTemplatePath.c_str()),
|
||||
savePrefabDialog.get());
|
||||
prefabSavedMessageLayout->addWidget(prefabSavedSuccessfullyIconContainer);
|
||||
prefabSavedMessageLayout->addWidget(prefabSavedSuccessfullyLabel);
|
||||
prefabSavedMessageFrame->setLayout(prefabSavedMessageLayout);
|
||||
contentLayout->addWidget(prefabSavedMessageFrame);
|
||||
|
||||
AZStd::unique_ptr<AzQtComponents::Card> unsavedPrefabsContainer = ConstructUnsavedPrefabsCard(templateId);
|
||||
contentLayout->addWidget(unsavedPrefabsContainer.release());
|
||||
|
||||
contentLayout->addStretch();
|
||||
|
||||
// Footer section begins.
|
||||
QHBoxLayout* footerLayout = new QHBoxLayout(savePrefabDialog.get());
|
||||
|
||||
if (useSaveAllPrefabsPreference)
|
||||
{
|
||||
QFrame* footerSeparatorLine = new QFrame(savePrefabDialog.get());
|
||||
footerSeparatorLine->setObjectName(FooterSeparatorLine);
|
||||
footerSeparatorLine->setFrameShape(QFrame::HLine);
|
||||
contentLayout->addWidget(footerSeparatorLine);
|
||||
|
||||
QLabel* prefabSavePreferenceHint = new QLabel(
|
||||
"<u>You can prevent this window from showing in the future by updating your global save preferences.</u>",
|
||||
savePrefabDialog.get());
|
||||
prefabSavePreferenceHint->setToolTip(
|
||||
"Go to 'Edit > Editor Settings > Global Preferences... > Global save preferences' to update your preference");
|
||||
prefabSavePreferenceHint->setObjectName(PrefabSavePreferenceHint);
|
||||
footerLayout->addWidget(prefabSavePreferenceHint);
|
||||
}
|
||||
|
||||
QDialogButtonBox* prefabSaveConfirmationButtons =
|
||||
new QDialogButtonBox(QDialogButtonBox::Save | QDialogButtonBox::No, savePrefabDialog.get());
|
||||
footerLayout->addWidget(prefabSaveConfirmationButtons);
|
||||
contentLayout->addLayout(footerLayout);
|
||||
connect(prefabSaveConfirmationButtons, &QDialogButtonBox::accepted, savePrefabDialog.get(), &QDialog::accept);
|
||||
connect(prefabSaveConfirmationButtons, &QDialogButtonBox::rejected, savePrefabDialog.get(), &QDialog::reject);
|
||||
AzQtComponents::StyleManager::setStyleSheet(savePrefabDialog->parentWidget(), QStringLiteral("style:Editor.qss"));
|
||||
|
||||
savePrefabDialog->setLayout(contentLayout);
|
||||
return AZStd::move(savePrefabDialog);
|
||||
}
|
||||
|
||||
AZStd::shared_ptr<QDialog> PrefabIntegrationManager::ConstructClosePrefabDialog(TemplateId templateId)
|
||||
{
|
||||
AZStd::shared_ptr<QDialog> closePrefabDialog = AZStd::make_shared<QDialog>(AzToolsFramework::GetActiveWindow());
|
||||
closePrefabDialog->setWindowTitle("Unsaved files detected");
|
||||
AZStd::weak_ptr<QDialog> closePrefabDialogWeakPtr(closePrefabDialog);
|
||||
closePrefabDialog->setObjectName(ClosePrefabDialog);
|
||||
|
||||
// Main Content section begins.
|
||||
QVBoxLayout* contentLayout = new QVBoxLayout(closePrefabDialog.get());
|
||||
QFrame* prefabSaveWarningFrame = new QFrame(closePrefabDialog.get());
|
||||
QHBoxLayout* levelEntitiesSaveQuestionLayout = new QHBoxLayout(closePrefabDialog.get());
|
||||
prefabSaveWarningFrame->setObjectName(PrefabSaveWarningFrame);
|
||||
|
||||
// Add a warning icon next to save prefab warning.
|
||||
prefabSaveWarningFrame->setLayout(levelEntitiesSaveQuestionLayout);
|
||||
QPixmap warningIcon(QString(":/Notifications/warning.svg"));
|
||||
QLabel* warningIconContainer = new QLabel(closePrefabDialog.get());
|
||||
warningIconContainer->setPixmap(warningIcon);
|
||||
warningIconContainer->setFixedWidth(warningIcon.width());
|
||||
levelEntitiesSaveQuestionLayout->addWidget(warningIconContainer);
|
||||
|
||||
// Ask user if they want to save entities in level.
|
||||
QLabel* prefabSaveQuestionLabel = new QLabel("Do you want to save the below unsaved prefabs?", closePrefabDialog.get());
|
||||
levelEntitiesSaveQuestionLayout->addWidget(prefabSaveQuestionLabel);
|
||||
contentLayout->addWidget(prefabSaveWarningFrame);
|
||||
|
||||
auto templateToSave = s_prefabSystemComponentInterface->FindTemplate(templateId);
|
||||
AZ::IO::Path templateToSaveFilePath = templateToSave->get().GetFilePath();
|
||||
AZStd::unique_ptr<AzQtComponents::Card> unsavedPrefabsCard = ConstructUnsavedPrefabsCard(templateId);
|
||||
contentLayout->addWidget(unsavedPrefabsCard.release());
|
||||
|
||||
contentLayout->addStretch();
|
||||
|
||||
QHBoxLayout* footerLayout = new QHBoxLayout(closePrefabDialog.get());
|
||||
|
||||
QDialogButtonBox* prefabSaveConfirmationButtons = new QDialogButtonBox(
|
||||
QDialogButtonBox::Save | QDialogButtonBox::Discard | QDialogButtonBox::Cancel, closePrefabDialog.get());
|
||||
footerLayout->addWidget(prefabSaveConfirmationButtons);
|
||||
contentLayout->addLayout(footerLayout);
|
||||
QObject::connect(prefabSaveConfirmationButtons, &QDialogButtonBox::accepted, closePrefabDialog.get(), &QDialog::accept);
|
||||
QObject::connect(prefabSaveConfirmationButtons, &QDialogButtonBox::rejected, closePrefabDialog.get(), &QDialog::reject);
|
||||
QObject::connect(
|
||||
prefabSaveConfirmationButtons, &QDialogButtonBox::clicked, closePrefabDialog.get(),
|
||||
[closePrefabDialogWeakPtr, prefabSaveConfirmationButtons](QAbstractButton* button)
|
||||
{
|
||||
int prefabSaveSelection = prefabSaveConfirmationButtons->buttonRole(button);
|
||||
closePrefabDialogWeakPtr.lock()->done(prefabSaveSelection);
|
||||
});
|
||||
AzQtComponents::StyleManager::setStyleSheet(closePrefabDialog.get(), QStringLiteral("style:Editor.qss"));
|
||||
closePrefabDialog->setLayout(contentLayout);
|
||||
return closePrefabDialog;
|
||||
}
|
||||
|
||||
AZStd::unique_ptr<AzQtComponents::Card> PrefabIntegrationManager::ConstructUnsavedPrefabsCard(TemplateId templateId)
|
||||
{
|
||||
FlowLayout* unsavedPrefabsLayout = new FlowLayout(AzToolsFramework::GetActiveWindow());
|
||||
|
||||
AZStd::set<AZ::IO::PathView> dirtyTemplatePaths = s_prefabSystemComponentInterface->GetDirtyTemplatePaths(templateId);
|
||||
|
||||
for (AZ::IO::PathView dirtyTemplatePath : dirtyTemplatePaths)
|
||||
{
|
||||
QLabel* prefabNameLabel =
|
||||
new QLabel(QString("<u>%1</u>").arg(dirtyTemplatePath.Filename().Native().data()), AzToolsFramework::GetActiveWindow());
|
||||
prefabNameLabel->setObjectName(UnsavedPrefabFileName);
|
||||
prefabNameLabel->setWordWrap(true);
|
||||
prefabNameLabel->setToolTip(dirtyTemplatePath.Native().data());
|
||||
prefabNameLabel->setProperty("FilePath", dirtyTemplatePath.Native().data());
|
||||
unsavedPrefabsLayout->addWidget(prefabNameLabel);
|
||||
}
|
||||
|
||||
AZStd::unique_ptr<AzQtComponents::Card> unsavedPrefabsContainer = AZStd::make_unique<AzQtComponents::Card>(AzToolsFramework::GetActiveWindow());
|
||||
unsavedPrefabsContainer->setObjectName(SaveDependentPrefabsCard);
|
||||
unsavedPrefabsContainer->setTitle("Unsaved Prefabs");
|
||||
unsavedPrefabsContainer->header()->setHasContextMenu(false);
|
||||
unsavedPrefabsContainer->header()->setIcon(QIcon(QStringLiteral(":/Entity/prefab_edit.svg")));
|
||||
|
||||
QFrame* unsavedPrefabsFrame = new QFrame(unsavedPrefabsContainer.get());
|
||||
unsavedPrefabsFrame->setLayout(unsavedPrefabsLayout);
|
||||
QScrollArea* unsavedPrefabsScrollArea = new QScrollArea(unsavedPrefabsContainer.get());
|
||||
unsavedPrefabsScrollArea->setWidget(unsavedPrefabsFrame);
|
||||
unsavedPrefabsScrollArea->setWidgetResizable(true);
|
||||
unsavedPrefabsContainer->setContentWidget(unsavedPrefabsScrollArea);
|
||||
|
||||
return AZStd::move(unsavedPrefabsContainer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,12 +15,15 @@
|
||||
#include <AzToolsFramework/AssetBrowser/AssetBrowserSourceDropBus.h>
|
||||
#include <AzToolsFramework/Editor/EditorContextMenuBus.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabPublicInterface.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabSystemComponentInterface.h>
|
||||
#include <AzToolsFramework/UI/Prefab/LevelRootUiHandler.h>
|
||||
#include <AzToolsFramework/UI/Prefab/PrefabEditManager.h>
|
||||
#include <AzToolsFramework/UI/Prefab/PrefabIntegrationBus.h>
|
||||
#include <AzToolsFramework/UI/Prefab/PrefabIntegrationInterface.h>
|
||||
#include <AzToolsFramework/UI/Prefab/PrefabUiHandler.h>
|
||||
|
||||
#include <AzQtComponents/Components/Widgets/Card.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
namespace Prefab
|
||||
@@ -49,6 +52,7 @@ namespace AzToolsFramework
|
||||
, public AssetBrowser::AssetBrowserSourceDropBus::Handler
|
||||
, public PrefabInstanceContainerNotificationBus::Handler
|
||||
, public PrefabIntegrationInterface
|
||||
, public QObject
|
||||
{
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(PrefabIntegrationManager, AZ::SystemAllocator, 0);
|
||||
@@ -72,6 +76,8 @@ namespace AzToolsFramework
|
||||
|
||||
// PrefabIntegrationInterface...
|
||||
AZ::EntityId CreateNewEntityAtPosition(const AZ::Vector3& position, AZ::EntityId parentId) override;
|
||||
int ExecuteClosePrefabDialog(TemplateId templateId) override;
|
||||
void ExecuteSavePrefabDialog(TemplateId templateId, bool useSaveAllPrefabsPreference) override;
|
||||
|
||||
private:
|
||||
// Manages the Edit Mode UI for prefabs
|
||||
@@ -124,12 +130,19 @@ namespace AzToolsFramework
|
||||
|
||||
static AZ::u32 GetSliceFlags(const AZ::Edit::ElementData* editData, const AZ::Edit::ClassData* classData);
|
||||
|
||||
AZStd::shared_ptr<QDialog> ConstructClosePrefabDialog(TemplateId templateId);
|
||||
AZStd::unique_ptr<AzQtComponents::Card> ConstructUnsavedPrefabsCard(TemplateId templateId);
|
||||
AZStd::unique_ptr<QDialog> ConstructSavePrefabDialog(TemplateId templateId, bool useSaveAllPrefabsPreference);
|
||||
void SavePrefabsInDialog(QDialog* unsavedPrefabsDialog);
|
||||
|
||||
|
||||
static const AZStd::string s_prefabFileExtension;
|
||||
|
||||
static EditorEntityUiInterface* s_editorEntityUiInterface;
|
||||
static PrefabPublicInterface* s_prefabPublicInterface;
|
||||
static PrefabEditInterface* s_prefabEditInterface;
|
||||
static PrefabLoaderInterface* s_prefabLoaderInterface;
|
||||
static PrefabSystemComponentInterface* s_prefabSystemComponentInterface;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,25 @@
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
AzFramework::ClickDetector::ClickEvent ClickDetectorEventFromViewportInteraction(
|
||||
const ViewportInteraction::MouseInteractionEvent& mouseInteraction)
|
||||
{
|
||||
if (mouseInteraction.m_mouseInteraction.m_mouseButtons.Left())
|
||||
{
|
||||
if (mouseInteraction.m_mouseEvent == ViewportInteraction::MouseEvent::Down)
|
||||
{
|
||||
return AzFramework::ClickDetector::ClickEvent::Down;
|
||||
}
|
||||
|
||||
if (mouseInteraction.m_mouseEvent == ViewportInteraction::MouseEvent::Up)
|
||||
{
|
||||
return AzFramework::ClickDetector::ClickEvent::Up;
|
||||
}
|
||||
}
|
||||
|
||||
return AzFramework::ClickDetector::ClickEvent::Nil;
|
||||
}
|
||||
|
||||
float ManipulatorLineBoundWidth(const AzFramework::ViewportId viewportId /*= AzFramework::InvalidViewportId*/)
|
||||
{
|
||||
float lineBoundWidth = 0.0f;
|
||||
|
||||
@@ -250,8 +250,6 @@ namespace AzToolsFramework
|
||||
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;
|
||||
//! Given the current view frustum (viewport) return all visible entities.
|
||||
virtual void FindVisibleEntities(AZStd::vector<AZ::EntityId>& visibleEntities) = 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.
|
||||
@@ -269,7 +267,20 @@ namespace AzToolsFramework
|
||||
//! Type to inherit to implement MainEditorViewportInteractionRequests.
|
||||
using MainEditorViewportInteractionRequestBus = AZ::EBus<MainEditorViewportInteractionRequests, ViewportEBusTraits>;
|
||||
|
||||
//! Viewport requests for managing the viewport's cursor state.
|
||||
//! Editor entity requests to be made about the viewport.
|
||||
class EditorEntityViewportInteractionRequests
|
||||
{
|
||||
public:
|
||||
//! Given the current view frustum (viewport) return all visible entities.
|
||||
virtual void FindVisibleEntities(AZStd::vector<AZ::EntityId>& visibleEntities) = 0;
|
||||
|
||||
protected:
|
||||
~EditorEntityViewportInteractionRequests() = default;
|
||||
};
|
||||
|
||||
using EditorEntityViewportInteractionRequestBus = AZ::EBus<EditorEntityViewportInteractionRequests, ViewportEBusTraits>;
|
||||
|
||||
//! Viewport requests for managing the viewport cursor state.
|
||||
class ViewportMouseCursorRequests
|
||||
{
|
||||
public:
|
||||
@@ -321,23 +332,8 @@ namespace AzToolsFramework
|
||||
|
||||
//! 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).
|
||||
inline AzFramework::ClickDetector::ClickEvent ClickDetectorEventFromViewportInteraction(
|
||||
const ViewportInteraction::MouseInteractionEvent& mouseInteraction)
|
||||
{
|
||||
if (mouseInteraction.m_mouseInteraction.m_mouseButtons.Left())
|
||||
{
|
||||
if (mouseInteraction.m_mouseEvent == ViewportInteraction::MouseEvent::Down)
|
||||
{
|
||||
return AzFramework::ClickDetector::ClickEvent::Down;
|
||||
}
|
||||
|
||||
if (mouseInteraction.m_mouseEvent == ViewportInteraction::MouseEvent::Up)
|
||||
{
|
||||
return AzFramework::ClickDetector::ClickEvent::Up;
|
||||
}
|
||||
}
|
||||
return AzFramework::ClickDetector::ClickEvent::Nil;
|
||||
}
|
||||
AzFramework::ClickDetector::ClickEvent ClickDetectorEventFromViewportInteraction(
|
||||
const ViewportInteraction::MouseInteractionEvent& mouseInteraction);
|
||||
|
||||
//! Wrap EBus call to retrieve manipulator line bound width.
|
||||
//! @note It is possible to pass AzFramework::InvalidViewportId (the default) to perform a Broadcast as opposed to a targeted Event.
|
||||
|
||||
+15
-18
@@ -9,8 +9,8 @@
|
||||
#include "EditorBoxSelect.h"
|
||||
|
||||
#include <AzFramework/Entity/EntityDebugDisplayBus.h>
|
||||
#include <AzToolsFramework/ViewportSelection/EditorSelectionUtil.h>
|
||||
#include <AzToolsFramework/Viewport/ViewportMessages.h>
|
||||
#include <AzToolsFramework/ViewportSelection/EditorSelectionUtil.h>
|
||||
|
||||
#include <QApplication>
|
||||
|
||||
@@ -19,11 +19,15 @@ namespace AzToolsFramework
|
||||
static const AZ::Color s_boxSelectColor = AZ::Color(1.0f, 1.0f, 1.0f, 0.4f);
|
||||
static const float s_boxSelectLineWidth = 2.0f;
|
||||
|
||||
void EditorBoxSelect::HandleMouseInteraction(
|
||||
const ViewportInteraction::MouseInteractionEvent& mouseInteraction)
|
||||
void EditorBoxSelect::HandleMouseInteraction(const ViewportInteraction::MouseInteractionEvent& mouseInteraction)
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AzToolsFramework);
|
||||
|
||||
if (mouseInteraction.m_mouseEvent == ViewportInteraction::MouseEvent::Down)
|
||||
{
|
||||
m_cursorPositionAtDownEvent = mouseInteraction.m_mouseInteraction.m_mousePick.m_screenCoordinates;
|
||||
}
|
||||
|
||||
m_cursorState.SetCurrentPosition(mouseInteraction.m_mouseInteraction.m_mousePick.m_screenCoordinates);
|
||||
|
||||
const auto selectClickEvent = ClickDetectorEventFromViewportInteraction(mouseInteraction);
|
||||
@@ -35,12 +39,7 @@ namespace AzToolsFramework
|
||||
m_leftMouseDown(mouseInteraction);
|
||||
}
|
||||
|
||||
m_boxSelectRegion = QRect
|
||||
{
|
||||
ViewportInteraction::QPointFromScreenPoint(
|
||||
mouseInteraction.m_mouseInteraction.m_mousePick.m_screenCoordinates),
|
||||
QSize { 0, 0 }
|
||||
};
|
||||
m_boxSelectRegion = QRect{ ViewportInteraction::QPointFromScreenPoint(m_cursorPositionAtDownEvent), QSize{ 0, 0 } };
|
||||
}
|
||||
|
||||
if (m_boxSelectRegion)
|
||||
@@ -87,11 +86,11 @@ namespace AzToolsFramework
|
||||
AZ::Vector2 viewportSize = AzToolsFramework::GetCameraState(viewportInfo.m_viewportId).m_viewportSize;
|
||||
|
||||
debugDisplay.DrawWireQuad2d(
|
||||
AZ::Vector2(
|
||||
aznumeric_cast<float>(m_boxSelectRegion->x()), aznumeric_cast<float>(m_boxSelectRegion->y())) / viewportSize,
|
||||
AZ::Vector2(aznumeric_cast<float>(m_boxSelectRegion->x()), aznumeric_cast<float>(m_boxSelectRegion->y())) / viewportSize,
|
||||
AZ::Vector2(
|
||||
aznumeric_cast<float>(m_boxSelectRegion->x()) + aznumeric_cast<float>(m_boxSelectRegion->width()),
|
||||
aznumeric_cast<float>(m_boxSelectRegion->y()) + aznumeric_cast<float>(m_boxSelectRegion->height())) / viewportSize,
|
||||
aznumeric_cast<float>(m_boxSelectRegion->y()) + aznumeric_cast<float>(m_boxSelectRegion->height())) /
|
||||
viewportSize,
|
||||
0.f);
|
||||
|
||||
debugDisplay.DepthTestOn();
|
||||
@@ -101,8 +100,7 @@ namespace AzToolsFramework
|
||||
}
|
||||
}
|
||||
|
||||
void EditorBoxSelect::DisplayScene(
|
||||
const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay)
|
||||
void EditorBoxSelect::DisplayScene(const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay)
|
||||
{
|
||||
if (m_displayScene)
|
||||
{
|
||||
@@ -122,15 +120,14 @@ namespace AzToolsFramework
|
||||
m_mouseMove = mouseMove;
|
||||
}
|
||||
|
||||
void EditorBoxSelect::InstallLeftMouseUp(
|
||||
const AZStd::function<void()>& leftMouseUp)
|
||||
void EditorBoxSelect::InstallLeftMouseUp(const AZStd::function<void()>& leftMouseUp)
|
||||
{
|
||||
m_leftMouseUp = leftMouseUp;
|
||||
}
|
||||
|
||||
void EditorBoxSelect::InstallDisplayScene(
|
||||
const AZStd::function<void(const AzFramework::ViewportInfo& viewportInfo,
|
||||
AzFramework::DebugDisplayRequests& debugDisplay)>& displayScene)
|
||||
const AZStd::function<void(const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay)>&
|
||||
displayScene)
|
||||
{
|
||||
m_displayScene = displayScene;
|
||||
}
|
||||
|
||||
@@ -81,5 +81,6 @@ namespace AzToolsFramework
|
||||
ViewportInteraction::KeyboardModifiers m_previousModifiers; //!< Modifier keys active on the previous frame.
|
||||
AzFramework::ClickDetector m_clickDetector; //!< Utility type to detect if a mouse click or move has occurred.
|
||||
AzFramework::CursorState m_cursorState; //!< Utility type to track the current cursor position (and movement/delta).
|
||||
AzFramework::ScreenPoint m_cursorPositionAtDownEvent; //!< The position of the cursor when first potentially starting a box select.
|
||||
};
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
+18
-8
@@ -19,7 +19,7 @@
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
// default ray length for picking in the viewport
|
||||
static const float s_pickRayLength = 1000.0f;
|
||||
static const float EditorPickRayLength = 1000.0f;
|
||||
|
||||
AZ::Vector3 CalculateCenterOffset(const AZ::EntityId entityId, const EditorTransformComponentSelectionRequests::Pivot pivot)
|
||||
{
|
||||
@@ -60,16 +60,27 @@ namespace AzToolsFramework
|
||||
return screenPosition;
|
||||
}
|
||||
|
||||
bool AabbIntersectMouseRay(const ViewportInteraction::MouseInteraction& mouseInteraction, const AZ::Aabb& aabb)
|
||||
bool AabbIntersectRay(const AZ::Vector3& origin, const AZ::Vector3& direction, const AZ::Aabb& aabb, float& distance)
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AzToolsFramework);
|
||||
|
||||
const AZ::Vector3 rayScaledDir = mouseInteraction.m_mousePick.m_rayDirection * s_pickRayLength;
|
||||
const AZ::Vector3 rayScaledDir = direction * EditorPickRayLength;
|
||||
|
||||
AZ::Vector3 startNormal;
|
||||
float t, end;
|
||||
return AZ::Intersect::IntersectRayAABB(
|
||||
mouseInteraction.m_mousePick.m_rayOrigin, rayScaledDir, rayScaledDir.GetReciprocal(), aabb, t, end, startNormal) > 0;
|
||||
AZ::Vector3 startNormal;
|
||||
if (AZ::Intersect::IntersectRayAABB(origin, rayScaledDir, rayScaledDir.GetReciprocal(), aabb, t, end, startNormal) > 0)
|
||||
{
|
||||
distance = t * EditorPickRayLength;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AabbIntersectMouseRay(const ViewportInteraction::MouseInteraction& mouseInteraction, const AZ::Aabb& aabb)
|
||||
{
|
||||
float unused;
|
||||
return AabbIntersectRay(mouseInteraction.m_mousePick.m_rayOrigin, mouseInteraction.m_mousePick.m_rayDirection, aabb, unused);
|
||||
}
|
||||
|
||||
bool PickEntity(
|
||||
@@ -117,8 +128,7 @@ namespace AzToolsFramework
|
||||
{
|
||||
float scaling = 1.0f;
|
||||
ViewportInteraction::ViewportInteractionRequestBus::EventResult(
|
||||
scaling, viewportId,
|
||||
&ViewportInteraction::ViewportInteractionRequestBus::Events::DeviceScalingFactor);
|
||||
scaling, viewportId, &ViewportInteraction::ViewportInteractionRequestBus::Events::DeviceScalingFactor);
|
||||
|
||||
return scaling;
|
||||
}
|
||||
|
||||
+4
@@ -46,6 +46,10 @@ namespace AzToolsFramework
|
||||
//! in screen space intersected an aabb in world space.
|
||||
bool AabbIntersectMouseRay(const ViewportInteraction::MouseInteraction& mouseInteraction, const AZ::Aabb& aabb);
|
||||
|
||||
//! Wrapper to perform an intersection between a ray and an aabb.
|
||||
//! Note: direction should be normalized (it is scaled internally by the editor pick distance).
|
||||
bool AabbIntersectRay(const AZ::Vector3& origin, const AZ::Vector3& direction, const AZ::Aabb& aabb, float& distance);
|
||||
|
||||
//! Return if a mouse interaction (pick ray) did intersect the tested EntityId.
|
||||
bool PickEntity(
|
||||
AZ::EntityId entityId, const ViewportInteraction::MouseInteraction& mouseInteraction, float& closestDistance, int viewportId);
|
||||
|
||||
+42
-34
@@ -44,39 +44,46 @@ namespace AzToolsFramework
|
||||
|
||||
AZ_CVAR(
|
||||
float,
|
||||
cl_viewportGizmoAxisLineWidth,
|
||||
ed_viewportGizmoAxisLineWidth,
|
||||
4.0f,
|
||||
nullptr,
|
||||
AZ::ConsoleFunctorFlags::Null,
|
||||
"The width of the line for the viewport axis gizmo");
|
||||
AZ_CVAR(
|
||||
float,
|
||||
cl_viewportGizmoAxisLineLength,
|
||||
ed_viewportGizmoAxisLineLength,
|
||||
0.7f,
|
||||
nullptr,
|
||||
AZ::ConsoleFunctorFlags::Null,
|
||||
"The length of the line for the viewport axis gizmo");
|
||||
AZ_CVAR(
|
||||
float,
|
||||
cl_viewportGizmoAxisLabelOffset,
|
||||
ed_viewportGizmoAxisLabelOffset,
|
||||
1.15f,
|
||||
nullptr,
|
||||
AZ::ConsoleFunctorFlags::Null,
|
||||
"The offset of the label for the viewport axis gizmo");
|
||||
AZ_CVAR(
|
||||
float,
|
||||
cl_viewportGizmoAxisLabelSize,
|
||||
ed_viewportGizmoAxisLabelSize,
|
||||
1.0f,
|
||||
nullptr,
|
||||
AZ::ConsoleFunctorFlags::Null,
|
||||
"The size of each label for the viewport axis gizmo");
|
||||
AZ_CVAR(
|
||||
AZ::Vector2,
|
||||
cl_viewportGizmoAxisScreenPosition,
|
||||
ed_viewportGizmoAxisScreenPosition,
|
||||
AZ::Vector2(0.045f, 0.9f),
|
||||
nullptr,
|
||||
AZ::ConsoleFunctorFlags::Null,
|
||||
"The screen position of the gizmo in normalized (0-1) ndc space");
|
||||
AZ_CVAR(
|
||||
bool,
|
||||
ed_viewportStickySelect,
|
||||
true,
|
||||
nullptr,
|
||||
AZ::ConsoleFunctorFlags::Null,
|
||||
"Sticky select implies a single click will not change selection with an entity already selected");
|
||||
|
||||
// strings related to new viewport interaction model (EditorTransformComponentSelection)
|
||||
static const char* const s_togglePivotTitleRightClick = "Toggle pivot";
|
||||
@@ -991,14 +998,19 @@ namespace AzToolsFramework
|
||||
|
||||
// ask the visible entity data cache if the entity is selectable in the viewport
|
||||
// (useful in the context of drawing when we only care about entities we can see)
|
||||
static bool SelectableInVisibleViewportCache(const EditorVisibleEntityDataCache& entityDataCache, const AZ::EntityId entityId)
|
||||
// note: return the index if it is selectable, nullopt otherwise
|
||||
static AZStd::optional<size_t> SelectableInVisibleViewportCache(
|
||||
const EditorVisibleEntityDataCache& entityDataCache, const AZ::EntityId entityId)
|
||||
{
|
||||
if (auto entityIndex = entityDataCache.GetVisibleEntityIndexFromId(entityId))
|
||||
{
|
||||
return entityDataCache.IsVisibleEntitySelectableInViewport(*entityIndex);
|
||||
if (entityDataCache.IsVisibleEntitySelectableInViewport(*entityIndex))
|
||||
{
|
||||
return *entityIndex;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return AZStd::nullopt;
|
||||
}
|
||||
|
||||
static AZ::ComponentId GetTransformComponentId(const AZ::EntityId entityId)
|
||||
@@ -1709,17 +1721,17 @@ namespace AzToolsFramework
|
||||
m_pivotOverrideFrame.Reset();
|
||||
}
|
||||
|
||||
bool EditorTransformComponentSelection::SelectDeselect(const AZ::EntityId entityIdUnderCursor)
|
||||
bool EditorTransformComponentSelection::SelectDeselect(const AZ::EntityId entityId)
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AzToolsFramework);
|
||||
|
||||
if (entityIdUnderCursor.IsValid())
|
||||
if (entityId.IsValid())
|
||||
{
|
||||
if (IsEntitySelectedInternal(entityIdUnderCursor, m_selectedEntityIds))
|
||||
if (IsEntitySelectedInternal(entityId, m_selectedEntityIds))
|
||||
{
|
||||
if (!UndoRedoOperationInProgress())
|
||||
{
|
||||
RemoveEntityFromSelection(entityIdUnderCursor);
|
||||
RemoveEntityFromSelection(entityId);
|
||||
|
||||
const auto nextEntityIds = EntityIdVectorFromContainer(m_selectedEntityIds);
|
||||
|
||||
@@ -1742,7 +1754,7 @@ namespace AzToolsFramework
|
||||
{
|
||||
if (!UndoRedoOperationInProgress())
|
||||
{
|
||||
AddEntityToSelection(entityIdUnderCursor);
|
||||
AddEntityToSelection(entityId);
|
||||
|
||||
const auto nextEntityIds = EntityIdVectorFromContainer(m_selectedEntityIds);
|
||||
|
||||
@@ -1783,25 +1795,21 @@ namespace AzToolsFramework
|
||||
|
||||
// for entities selected with no bounds of their own (just TransformComponent)
|
||||
// check selection against the selection indicator aabb
|
||||
for (AZ::EntityId entityId : m_selectedEntityIds)
|
||||
for (const AZ::EntityId& entityId : m_selectedEntityIds)
|
||||
{
|
||||
if (!SelectableInVisibleViewportCache(*m_entityDataCache, entityId))
|
||||
if (const auto entityIndex = SelectableInVisibleViewportCache(*m_entityDataCache, entityId); entityIndex.has_value())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const AZ::Transform& worldFromLocal = m_entityDataCache->GetVisibleEntityTransform(*entityIndex);
|
||||
const AZ::Vector3 boxPosition = worldFromLocal.TransformPoint(CalculateCenterOffset(entityId, m_pivotMode));
|
||||
const AZ::Vector3 scaledSize =
|
||||
AZ::Vector3(s_pivotSize) * CalculateScreenToWorldMultiplier(worldFromLocal.GetTranslation(), cameraState);
|
||||
|
||||
AZ::Transform worldFromLocal;
|
||||
AZ::TransformBus::EventResult(worldFromLocal, entityId, &AZ::TransformBus::Events::GetWorldTM);
|
||||
|
||||
const AZ::Vector3 boxPosition = worldFromLocal.TransformPoint(CalculateCenterOffset(entityId, m_pivotMode));
|
||||
|
||||
const AZ::Vector3 scaledSize =
|
||||
AZ::Vector3(s_pivotSize) * CalculateScreenToWorldMultiplier(worldFromLocal.GetTranslation(), cameraState);
|
||||
|
||||
if (AabbIntersectMouseRay(
|
||||
mouseInteraction.m_mouseInteraction, AZ::Aabb::CreateFromMinMax(boxPosition - scaledSize, boxPosition + scaledSize)))
|
||||
{
|
||||
m_cachedEntityIdUnderCursor = entityId;
|
||||
if (AabbIntersectMouseRay(
|
||||
mouseInteraction.m_mouseInteraction,
|
||||
AZ::Aabb::CreateFromMinMax(boxPosition - scaledSize, boxPosition + scaledSize)))
|
||||
{
|
||||
m_cachedEntityIdUnderCursor = entityId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3490,7 +3498,7 @@ namespace AzToolsFramework
|
||||
const auto cameraProjection = AzFramework::CameraProjection(gizmoCameraState);
|
||||
|
||||
// screen space offset to move the 2d gizmo around
|
||||
const AZ::Vector2 screenOffset = AZ::Vector2(cl_viewportGizmoAxisScreenPosition) - AZ::Vector2(0.5f, 0.5f);
|
||||
const AZ::Vector2 screenOffset = AZ::Vector2(ed_viewportGizmoAxisScreenPosition) - AZ::Vector2(0.5f, 0.5f);
|
||||
|
||||
// map from a position in world space (relative to the the gizmo camera near the origin) to a position in
|
||||
// screen space
|
||||
@@ -3502,7 +3510,7 @@ namespace AzToolsFramework
|
||||
};
|
||||
|
||||
// get all important axis positions in screen space
|
||||
const float lineLength = cl_viewportGizmoAxisLineLength;
|
||||
const float lineLength = ed_viewportGizmoAxisLineLength;
|
||||
const auto gizmoStart = calculateGizmoAxis(AZ::Vector3::CreateZero());
|
||||
const auto gizmoEndAxisX = calculateGizmoAxis(-AZ::Vector3::CreateAxisX() * lineLength);
|
||||
const auto gizmoEndAxisY = calculateGizmoAxis(-AZ::Vector3::CreateAxisY() * lineLength);
|
||||
@@ -3513,7 +3521,7 @@ namespace AzToolsFramework
|
||||
const AZ::Vector2 gizmoAxisZ = gizmoEndAxisZ - gizmoStart;
|
||||
|
||||
// draw the axes of the gizmo
|
||||
debugDisplay.SetLineWidth(cl_viewportGizmoAxisLineWidth);
|
||||
debugDisplay.SetLineWidth(ed_viewportGizmoAxisLineWidth);
|
||||
debugDisplay.SetColor(AZ::Colors::Red);
|
||||
debugDisplay.DrawLine2d(gizmoStart, gizmoEndAxisX, 1.0f);
|
||||
debugDisplay.SetColor(AZ::Colors::Lime);
|
||||
@@ -3522,14 +3530,14 @@ namespace AzToolsFramework
|
||||
debugDisplay.DrawLine2d(gizmoStart, gizmoEndAxisZ, 1.0f);
|
||||
debugDisplay.SetLineWidth(1.0f);
|
||||
|
||||
const float labelOffset = cl_viewportGizmoAxisLabelOffset;
|
||||
const float labelOffset = ed_viewportGizmoAxisLabelOffset;
|
||||
const float screenScale = GetScreenDisplayScaling(viewportId);
|
||||
const auto labelXScreenPosition = (gizmoStart + (gizmoAxisX * labelOffset)) * editorCameraState.m_viewportSize * screenScale;
|
||||
const auto labelYScreenPosition = (gizmoStart + (gizmoAxisY * labelOffset)) * editorCameraState.m_viewportSize * screenScale;
|
||||
const auto labelZScreenPosition = (gizmoStart + (gizmoAxisZ * labelOffset)) * editorCameraState.m_viewportSize * screenScale;
|
||||
|
||||
// draw the label of of each axis for the gizmo
|
||||
const float labelSize = cl_viewportGizmoAxisLabelSize;
|
||||
const float labelSize = ed_viewportGizmoAxisLabelSize;
|
||||
debugDisplay.SetColor(AZ::Colors::White);
|
||||
debugDisplay.Draw2dTextLabel(labelXScreenPosition.GetX(), labelXScreenPosition.GetY(), labelSize, "X", true);
|
||||
debugDisplay.Draw2dTextLabel(labelYScreenPosition.GetX(), labelYScreenPosition.GetY(), labelSize, "Y", true);
|
||||
|
||||
+18
-19
@@ -9,6 +9,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Component/TransformBus.h>
|
||||
#include <AzCore/Console/IConsole.h>
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
#include <AzCore/std/optional.h>
|
||||
#include <AzCore/std/smart_ptr/unique_ptr.h>
|
||||
@@ -32,6 +33,8 @@
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
AZ_CVAR_EXTERNED(bool, ed_viewportStickySelect);
|
||||
|
||||
class EditorVisibleEntityDataCache;
|
||||
|
||||
using EntityIdSet = AZStd::unordered_set<AZ::EntityId>; //!< Alias for unordered_set of EntityIds.
|
||||
@@ -170,14 +173,11 @@ namespace AzToolsFramework
|
||||
|
||||
//! ViewportInteraction::ViewportSelectionRequests
|
||||
//! Intercept all viewport mouse events and respond to inputs.
|
||||
bool HandleMouseInteraction(
|
||||
const ViewportInteraction::MouseInteractionEvent& mouseInteraction) override;
|
||||
bool HandleMouseInteraction(const ViewportInteraction::MouseInteractionEvent& mouseInteraction) override;
|
||||
void DisplayViewportSelection(
|
||||
const AzFramework::ViewportInfo& viewportInfo,
|
||||
AzFramework::DebugDisplayRequests& debugDisplay) override;
|
||||
const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) override;
|
||||
void DisplayViewportSelection2d(
|
||||
const AzFramework::ViewportInfo& viewportInfo,
|
||||
AzFramework::DebugDisplayRequests& debugDisplay) override;
|
||||
const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) override;
|
||||
|
||||
//! Add an entity to the current selection
|
||||
void AddEntityToSelection(AZ::EntityId entityId);
|
||||
@@ -206,7 +206,7 @@ namespace AzToolsFramework
|
||||
bool IsEntitySelected(AZ::EntityId entityId) const;
|
||||
void SetSelectedEntities(const EntityIdList& entityIds);
|
||||
void DeselectEntities();
|
||||
bool SelectDeselect(AZ::EntityId entityIdUnderCursor);
|
||||
bool SelectDeselect(AZ::EntityId entityId);
|
||||
|
||||
void RefreshSelectedEntityIds();
|
||||
void RefreshSelectedEntityIds(const EntityIdList& selectedEntityIds);
|
||||
@@ -253,11 +253,10 @@ namespace AzToolsFramework
|
||||
void SnapSelectedEntitiesToWorldGrid(float gridSize) override;
|
||||
|
||||
// EditorManipulatorCommandUndoRedoRequestBus ...
|
||||
void UndoRedoEntityManipulatorCommand(
|
||||
AZ::u8 pivotOverride, const AZ::Transform& transform, AZ::EntityId entityId) override;
|
||||
void UndoRedoEntityManipulatorCommand(AZ::u8 pivotOverride, const AZ::Transform& transform, AZ::EntityId entityId) override;
|
||||
|
||||
// EditorContextMenuBus...
|
||||
void PopulateEditorGlobalContextMenu(QMenu* menu, const AZ::Vector2 & point, int flags) override;
|
||||
void PopulateEditorGlobalContextMenu(QMenu* menu, const AZ::Vector2& point, int flags) override;
|
||||
int GetMenuPosition() const override;
|
||||
AZStd::string GetMenuIdentifier() const override;
|
||||
|
||||
@@ -266,8 +265,7 @@ namespace AzToolsFramework
|
||||
|
||||
// ToolsApplicationNotificationBus ...
|
||||
void BeforeEntitySelectionChanged() override;
|
||||
void AfterEntitySelectionChanged(
|
||||
const EntityIdList& newlySelectedEntities, const EntityIdList& newlyDeselectedEntities) override;
|
||||
void AfterEntitySelectionChanged(const EntityIdList& newlySelectedEntities, const EntityIdList& newlyDeselectedEntities) override;
|
||||
|
||||
// TransformNotificationBus ...
|
||||
void OnTransformChanged(const AZ::Transform& localTM, const AZ::Transform& worldTM) override;
|
||||
@@ -318,7 +316,8 @@ namespace AzToolsFramework
|
||||
EntityIdManipulators m_entityIdManipulators; //!< Mapping from a Manipulator to potentially many EntityIds.
|
||||
|
||||
EditorBoxSelect m_boxSelect; //!< Type responsible for handling box select.
|
||||
AZStd::unique_ptr<EntityManipulatorCommand> m_manipulatorMoveCommand; //!< Track adjustments to manipulator translation and orientation (during mouse press/move).
|
||||
//! Track adjustments to manipulator translation and orientation (during mouse press/move).
|
||||
AZStd::unique_ptr<EntityManipulatorCommand> m_manipulatorMoveCommand;
|
||||
AZStd::vector<AZStd::unique_ptr<QAction>> m_actions; //!< What actions are tied to this handler.
|
||||
ViewportInteraction::KeyboardModifiers m_previousModifiers; //!< What modifiers were held last frame.
|
||||
EditorContextMenu m_contextMenu; //!< Viewport right click context menu.
|
||||
@@ -328,8 +327,10 @@ namespace AzToolsFramework
|
||||
ReferenceFrame m_referenceFrame = ReferenceFrame::Parent; //!< What reference frame is the Manipulator currently operating in.
|
||||
Frame m_axisPreview; //!< Axes of entity at the time of mouse down to indicate delta of translation.
|
||||
bool m_triedToRefresh = false; //!< Did a refresh event occur to recalculate the current Manipulator transform.
|
||||
bool m_didSetSelectedEntities = false; //!< Was EditorTransformComponentSelection responsible for the most recent entity selection change.
|
||||
bool m_selectedEntityIdsAndManipulatorsDirty = false; //!< Do the active manipulators need to recalculated after a modification (lock/visibility etc).
|
||||
//! Was EditorTransformComponentSelection responsible for the most recent entity selection change.
|
||||
bool m_didSetSelectedEntities = false;
|
||||
//! Do the active manipulators need to recalculated after a modification (lock/visibility etc).
|
||||
bool m_selectedEntityIdsAndManipulatorsDirty = false;
|
||||
bool m_transformChangedInternally = false; //!< Was an OnTransformChanged event triggered internally or not.
|
||||
ViewportUi::ClusterId m_transformModeClusterId; //!< Id of the Viewport UI cluster for changing transform mode.
|
||||
ViewportUi::ButtonId m_translateButtonId; //!< Id of the Viewport UI button for translate mode.
|
||||
@@ -363,15 +364,13 @@ namespace AzToolsFramework
|
||||
|
||||
//! Calculate the orientation for a group of entities based on the incoming reference frame.
|
||||
template<typename EntityIdMap>
|
||||
PivotOrientationResult CalculatePivotOrientationForEntityIds(
|
||||
const EntityIdMap& entityIdMap, const ReferenceFrame referenceFrame);
|
||||
PivotOrientationResult CalculatePivotOrientationForEntityIds(const EntityIdMap& entityIdMap, const ReferenceFrame referenceFrame);
|
||||
|
||||
//! Calculate the orientation for a group of entities based on the incoming
|
||||
//! reference frame with possible pivot override.
|
||||
template<typename EntityIdMap>
|
||||
PivotOrientationResult CalculateSelectionPivotOrientation(
|
||||
const EntityIdMap& entityIdMap, const OptionalFrame& pivotOverrideFrame,
|
||||
const ReferenceFrame referenceFrame);
|
||||
const EntityIdMap& entityIdMap, const OptionalFrame& pivotOverrideFrame, const ReferenceFrame referenceFrame);
|
||||
|
||||
void SetEntityWorldTranslation(AZ::EntityId entityId, const AZ::Vector3& worldTranslation, bool& internal);
|
||||
void SetEntityLocalTranslation(AZ::EntityId entityId, const AZ::Vector3& localTranslation, bool& internal);
|
||||
|
||||
+2
-2
@@ -161,8 +161,8 @@ namespace AzToolsFramework
|
||||
|
||||
// request list of visible entities from authoritative system
|
||||
EntityIdList nextVisibleEntityIds;
|
||||
ViewportInteraction::MainEditorViewportInteractionRequestBus::Event(
|
||||
viewportInfo.m_viewportId, &ViewportInteraction::MainEditorViewportInteractionRequestBus::Events::FindVisibleEntities,
|
||||
ViewportInteraction::EditorEntityViewportInteractionRequestBus::Event(
|
||||
viewportInfo.m_viewportId, &ViewportInteraction::EditorEntityViewportInteractionRequestBus::Events::FindVisibleEntities,
|
||||
nextVisibleEntityIds);
|
||||
|
||||
// only bother resorting if we know the lists have changed
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ namespace AzToolsFramework
|
||||
{
|
||||
namespace Platform
|
||||
{
|
||||
static const char ErrorChannel[] = "ArchiveComponent_Linux";
|
||||
[[maybe_unused]] static const char ErrorChannel[] = "ArchiveComponent_Linux";
|
||||
|
||||
static const char ZipExePath[] = R"(/usr/bin/zip)";
|
||||
static const char UnzipExePath[] = R"(/usr/bin/unzip)";
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ComponentModeTestDoubles.h"
|
||||
#include "ComponentModeTestFixture.h"
|
||||
#include "ComponentModeTestDoubles.h"
|
||||
|
||||
#include <AzCore/UserSettings/UserSettingsComponent.h>
|
||||
|
||||
@@ -15,17 +15,15 @@ namespace UnitTest
|
||||
{
|
||||
void ComponentModeTestFixture::SetUpEditorFixtureImpl()
|
||||
{
|
||||
using namespace AzToolsFramework;
|
||||
using namespace AzToolsFramework::ComponentModeFramework;
|
||||
namespace AztfCmf = AzToolsFramework::ComponentModeFramework;
|
||||
|
||||
auto* app = GetApplication();
|
||||
ASSERT_TRUE(app);
|
||||
|
||||
app->RegisterComponentDescriptor(PlaceholderEditorComponent::CreateDescriptor());
|
||||
app->RegisterComponentDescriptor(AnotherPlaceholderEditorComponent::CreateDescriptor());
|
||||
app->RegisterComponentDescriptor(DependentPlaceholderEditorComponent::CreateDescriptor());
|
||||
app->RegisterComponentDescriptor(AztfCmf::PlaceholderEditorComponent::CreateDescriptor());
|
||||
app->RegisterComponentDescriptor(AztfCmf::AnotherPlaceholderEditorComponent::CreateDescriptor());
|
||||
app->RegisterComponentDescriptor(AztfCmf::DependentPlaceholderEditorComponent::CreateDescriptor());
|
||||
app->RegisterComponentDescriptor(
|
||||
TestComponentModeComponent<OverrideMouseInteractionComponentMode>::CreateDescriptor());
|
||||
app->RegisterComponentDescriptor(IncompatiblePlaceholderEditorComponent::CreateDescriptor());
|
||||
AztfCmf::TestComponentModeComponent<AztfCmf::OverrideMouseInteractionComponentMode>::CreateDescriptor());
|
||||
app->RegisterComponentDescriptor(AztfCmf::IncompatiblePlaceholderEditorComponent::CreateDescriptor());
|
||||
}
|
||||
} // namespace UnitTest
|
||||
|
||||
@@ -6,12 +6,14 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/Math/IntersectSegment.h>
|
||||
#include <AzCore/Math/ToString.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/UnitTest/TestTypes.h>
|
||||
#include <AzFramework/Components/TransformComponent.h>
|
||||
#include <AzFramework/Entity/EntityContext.h>
|
||||
#include <AzFramework/Viewport/ViewportScreen.h>
|
||||
#include <AzFramework/Visibility/BoundsBus.h>
|
||||
#include <AzManipulatorTestFramework/AzManipulatorTestFramework.h>
|
||||
#include <AzManipulatorTestFramework/AzManipulatorTestFrameworkTestHelpers.h>
|
||||
#include <AzManipulatorTestFramework/AzManipulatorTestFrameworkUtils.h>
|
||||
@@ -20,10 +22,12 @@
|
||||
#include <AzManipulatorTestFramework/ViewportInteraction.h>
|
||||
#include <AzQtComponents/Components/GlobalEventFilter.h>
|
||||
#include <AzTest/AzTest.h>
|
||||
#include <AzToolsFramework/API/ComponentEntitySelectionBus.h>
|
||||
#include <AzToolsFramework/Application/ToolsApplication.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntityActionComponent.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntityModel.h>
|
||||
#include <AzToolsFramework/ToolsComponents/EditorComponentBase.h>
|
||||
#include <AzToolsFramework/ToolsComponents/EditorLockComponent.h>
|
||||
#include <AzToolsFramework/ToolsComponents/EditorVisibilityComponent.h>
|
||||
#include <AzToolsFramework/ToolsComponents/TransformComponent.h>
|
||||
@@ -32,6 +36,7 @@
|
||||
#include <AzToolsFramework/ViewportSelection/EditorDefaultSelection.h>
|
||||
#include <AzToolsFramework/ViewportSelection/EditorInteractionSystemViewportSelectionRequestBus.h>
|
||||
#include <AzToolsFramework/ViewportSelection/EditorPickEntitySelection.h>
|
||||
#include <AzToolsFramework/ViewportSelection/EditorSelectionUtil.h>
|
||||
#include <AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.h>
|
||||
#include <AzToolsFramework/ViewportSelection/EditorVisibleEntityDataCache.h>
|
||||
#include <AzToolsFramework/ViewportUi/ViewportUiManager.h>
|
||||
@@ -46,6 +51,14 @@ namespace AZ
|
||||
|
||||
namespace UnitTest
|
||||
{
|
||||
AzToolsFramework::EntityIdList SelectedEntities()
|
||||
{
|
||||
AzToolsFramework::EntityIdList selectedEntitiesBefore;
|
||||
AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(
|
||||
selectedEntitiesBefore, &AzToolsFramework::ToolsApplicationRequestBus::Events::GetSelectedEntities);
|
||||
return selectedEntitiesBefore;
|
||||
}
|
||||
|
||||
class EditorEntityVisibilityCacheFixture : public ToolsApplicationFixture
|
||||
{
|
||||
public:
|
||||
@@ -110,6 +123,80 @@ namespace UnitTest
|
||||
EXPECT_FALSE(m_cache.IsVisibleEntityVisible(m_cache.GetVisibleEntityIndexFromId(m_entityIds[2]).value()));
|
||||
}
|
||||
|
||||
//! Basic component that implements BoundsRequestBus and EditorComponentSelectionRequestsBus to be compatible
|
||||
//! with the Editor visibility system.
|
||||
//! Note: Used for simulating selection (picking) in the viewport.
|
||||
class BoundsTestComponent
|
||||
: public AzToolsFramework::Components::EditorComponentBase
|
||||
, public AzFramework::BoundsRequestBus::Handler
|
||||
, public AzToolsFramework::EditorComponentSelectionRequestsBus::Handler
|
||||
{
|
||||
public:
|
||||
AZ_EDITOR_COMPONENT(
|
||||
BoundsTestComponent, "{E6312E9D-8489-4677-9980-C93C328BC92C}", AzToolsFramework::Components::EditorComponentBase);
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
// AZ::Component overrides ...
|
||||
void Activate() override;
|
||||
void Deactivate() override;
|
||||
|
||||
// EditorComponentSelectionRequestsBus overrides ...
|
||||
AZ::Aabb GetEditorSelectionBoundsViewport(const AzFramework::ViewportInfo& viewportInfo) override;
|
||||
bool EditorSelectionIntersectRayViewport(
|
||||
const AzFramework::ViewportInfo& viewportInfo, const AZ::Vector3& src, const AZ::Vector3& dir, float& distance) override;
|
||||
bool SupportsEditorRayIntersect() override;
|
||||
|
||||
// BoundsRequestBus overrides ...
|
||||
AZ::Aabb GetWorldBounds() override;
|
||||
AZ::Aabb GetLocalBounds() override;
|
||||
};
|
||||
|
||||
AZ::Aabb BoundsTestComponent::GetEditorSelectionBoundsViewport([[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo)
|
||||
{
|
||||
return GetWorldBounds();
|
||||
}
|
||||
|
||||
bool BoundsTestComponent::EditorSelectionIntersectRayViewport(
|
||||
[[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo, const AZ::Vector3& src, const AZ::Vector3& dir, float& distance)
|
||||
{
|
||||
return AzToolsFramework::AabbIntersectRay(src, dir, GetWorldBounds(), distance);
|
||||
}
|
||||
|
||||
bool BoundsTestComponent::SupportsEditorRayIntersect()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void BoundsTestComponent::Reflect([[maybe_unused]] AZ::ReflectContext* context)
|
||||
{
|
||||
// noop
|
||||
}
|
||||
|
||||
void BoundsTestComponent::Activate()
|
||||
{
|
||||
AzFramework::BoundsRequestBus::Handler::BusConnect(GetEntityId());
|
||||
AzToolsFramework::EditorComponentSelectionRequestsBus::Handler::BusConnect(GetEntityId());
|
||||
}
|
||||
|
||||
void BoundsTestComponent::Deactivate()
|
||||
{
|
||||
AzToolsFramework::EditorComponentSelectionRequestsBus::Handler::BusDisconnect();
|
||||
AzFramework::BoundsRequestBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
AZ::Aabb BoundsTestComponent::GetWorldBounds()
|
||||
{
|
||||
AZ::Transform worldFromLocal = AZ::Transform::CreateIdentity();
|
||||
AZ::TransformBus::EventResult(worldFromLocal, GetEntityId(), &AZ::TransformBus::Events::GetWorldTM);
|
||||
return GetLocalBounds().GetTransformedAabb(worldFromLocal);
|
||||
}
|
||||
|
||||
AZ::Aabb BoundsTestComponent::GetLocalBounds()
|
||||
{
|
||||
return AZ::Aabb::CreateFromMinMax(AZ::Vector3(-0.5f), AZ::Vector3(0.5f));
|
||||
}
|
||||
|
||||
// Fixture to support testing EditorTransformComponentSelection functionality on an Entity selection.
|
||||
class EditorTransformComponentSelectionFixture : public ToolsApplicationFixture
|
||||
{
|
||||
@@ -120,27 +207,52 @@ namespace UnitTest
|
||||
m_entityIds.push_back(m_entityId1);
|
||||
}
|
||||
|
||||
void ArrangeIndividualRotatedEntitySelection(const AZ::Quaternion& orientation);
|
||||
AZStd::optional<AZ::Transform> GetManipulatorTransform() const;
|
||||
void RefreshManipulators(AzToolsFramework::EditorTransformComponentSelectionRequestBus::Events::RefreshType refreshType);
|
||||
void SetTransformMode(AzToolsFramework::EditorTransformComponentSelectionRequestBus::Events::Mode transformMode);
|
||||
void OverrideManipulatorOrientation(const AZ::Quaternion& orientation);
|
||||
void OverrideManipulatorTranslation(const AZ::Vector3& translation);
|
||||
|
||||
public:
|
||||
AZ::EntityId m_entityId1;
|
||||
AzToolsFramework::EntityIdList m_entityIds;
|
||||
};
|
||||
|
||||
void EditorTransformComponentSelectionFixture::ArrangeIndividualRotatedEntitySelection(const AZ::Quaternion& orientation)
|
||||
class EditorTransformComponentSelectionViewportPickingFixture : public ToolsApplicationFixture
|
||||
{
|
||||
for (auto entityId : m_entityIds)
|
||||
public:
|
||||
void SetUpEditorFixtureImpl() override
|
||||
{
|
||||
auto* app = GetApplication();
|
||||
// register a simple component implementing BoundsRequestBus and EditorComponentSelectionRequestsBus
|
||||
app->RegisterComponentDescriptor(BoundsTestComponent::CreateDescriptor());
|
||||
|
||||
auto createEntityWithBoundsFn = [](const char* entityName)
|
||||
{
|
||||
AZ::Entity* entity = nullptr;
|
||||
AZ::EntityId entityId = CreateDefaultEditorEntity(entityName, &entity);
|
||||
|
||||
entity->Deactivate();
|
||||
entity->CreateComponent<BoundsTestComponent>();
|
||||
entity->Activate();
|
||||
|
||||
return entityId;
|
||||
};
|
||||
|
||||
m_entityId1 = createEntityWithBoundsFn("Entity1");
|
||||
m_entityId2 = createEntityWithBoundsFn("Entity2");
|
||||
m_entityId3 = createEntityWithBoundsFn("Entity3");
|
||||
}
|
||||
|
||||
public:
|
||||
AZ::EntityId m_entityId1;
|
||||
AZ::EntityId m_entityId2;
|
||||
AZ::EntityId m_entityId3;
|
||||
};
|
||||
|
||||
void ArrangeIndividualRotatedEntitySelection(const AzToolsFramework::EntityIdList& entityIds, const AZ::Quaternion& orientation)
|
||||
{
|
||||
for (auto entityId : entityIds)
|
||||
{
|
||||
AZ::TransformBus::Event(entityId, &AZ::TransformBus::Events::SetLocalRotationQuaternion, orientation);
|
||||
}
|
||||
}
|
||||
|
||||
AZStd::optional<AZ::Transform> EditorTransformComponentSelectionFixture::GetManipulatorTransform() const
|
||||
AZStd::optional<AZ::Transform> GetManipulatorTransform()
|
||||
{
|
||||
using AzToolsFramework::EditorTransformComponentSelectionRequestBus;
|
||||
|
||||
@@ -151,8 +263,7 @@ namespace UnitTest
|
||||
return manipulatorTransform;
|
||||
}
|
||||
|
||||
void EditorTransformComponentSelectionFixture::RefreshManipulators(
|
||||
AzToolsFramework::EditorTransformComponentSelectionRequestBus::Events::RefreshType refreshType)
|
||||
void RefreshManipulators(const AzToolsFramework::EditorTransformComponentSelectionRequestBus::Events::RefreshType refreshType)
|
||||
{
|
||||
using AzToolsFramework::EditorTransformComponentSelectionRequestBus;
|
||||
|
||||
@@ -160,8 +271,7 @@ namespace UnitTest
|
||||
AzToolsFramework::GetEntityContextId(), &EditorTransformComponentSelectionRequestBus::Events::RefreshManipulators, refreshType);
|
||||
}
|
||||
|
||||
void EditorTransformComponentSelectionFixture::SetTransformMode(
|
||||
AzToolsFramework::EditorTransformComponentSelectionRequestBus::Events::Mode transformMode)
|
||||
void SetTransformMode(const AzToolsFramework::EditorTransformComponentSelectionRequestBus::Events::Mode transformMode)
|
||||
{
|
||||
using AzToolsFramework::EditorTransformComponentSelectionRequestBus;
|
||||
|
||||
@@ -169,7 +279,7 @@ namespace UnitTest
|
||||
AzToolsFramework::GetEntityContextId(), &EditorTransformComponentSelectionRequestBus::Events::SetTransformMode, transformMode);
|
||||
}
|
||||
|
||||
void EditorTransformComponentSelectionFixture::OverrideManipulatorOrientation(const AZ::Quaternion& orientation)
|
||||
void OverrideManipulatorOrientation(const AZ::Quaternion& orientation)
|
||||
{
|
||||
using AzToolsFramework::EditorTransformComponentSelectionRequestBus;
|
||||
|
||||
@@ -178,7 +288,7 @@ namespace UnitTest
|
||||
orientation);
|
||||
}
|
||||
|
||||
void EditorTransformComponentSelectionFixture::OverrideManipulatorTranslation(const AZ::Vector3& translation)
|
||||
void OverrideManipulatorTranslation(const AZ::Vector3& translation)
|
||||
{
|
||||
using AzToolsFramework::EditorTransformComponentSelectionRequestBus;
|
||||
|
||||
@@ -190,7 +300,7 @@ namespace UnitTest
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// EditorTransformComponentSelection Tests
|
||||
|
||||
TEST_F(EditorTransformComponentSelectionFixture, Focus_is_not_changed_while_switching_viewport_interaction_request_instance)
|
||||
TEST_F(EditorTransformComponentSelectionFixture, FocusIsNotChangedWhileSwitchingViewportInteractionRequestInstance)
|
||||
{
|
||||
// setup a dummy widget and make it the active window to ensure focus in/out events are fired
|
||||
auto dummyWidget = AZStd::make_unique<QWidget>();
|
||||
@@ -239,7 +349,7 @@ namespace UnitTest
|
||||
// Given
|
||||
AzToolsFramework::SelectEntity(m_entityId1);
|
||||
|
||||
ArrangeIndividualRotatedEntitySelection(AZ::Quaternion::CreateRotationX(AZ::DegToRad(90.0f)));
|
||||
ArrangeIndividualRotatedEntitySelection(m_entityIds, AZ::Quaternion::CreateRotationX(AZ::DegToRad(90.0f)));
|
||||
RefreshManipulators(EditorTransformComponentSelectionRequestBus::Events::RefreshType::All);
|
||||
|
||||
SetTransformMode(EditorTransformComponentSelectionRequestBus::Events::Mode::Rotation);
|
||||
@@ -286,7 +396,7 @@ namespace UnitTest
|
||||
AzToolsFramework::SelectEntity(m_entityId1);
|
||||
|
||||
const AZ::Quaternion initialEntityOrientation = AZ::Quaternion::CreateRotationX(AZ::DegToRad(90.0f));
|
||||
ArrangeIndividualRotatedEntitySelection(initialEntityOrientation);
|
||||
ArrangeIndividualRotatedEntitySelection(m_entityIds, initialEntityOrientation);
|
||||
|
||||
// assign new orientation to manipulator which does not match entity orientation
|
||||
OverrideManipulatorOrientation(AZ::Quaternion::CreateRotationZ(AZ::DegToRad(90.0f)));
|
||||
@@ -478,6 +588,227 @@ namespace UnitTest
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
}
|
||||
|
||||
// fixture for use with the indirect manipulator test framework
|
||||
using EditorTransformComponentSelectionViewportPickingManipulatorTestFixture =
|
||||
IndirectCallManipulatorViewportInteractionFixtureMixin<EditorTransformComponentSelectionViewportPickingFixture>;
|
||||
|
||||
TEST_F(EditorTransformComponentSelectionViewportPickingManipulatorTestFixture, SingleClickWithNoSelectionWillSelectEntity)
|
||||
{
|
||||
AzToolsFramework::ed_viewportStickySelect = true;
|
||||
|
||||
// the initial starting position of the entity
|
||||
const auto initialTransformWorld = AZ::Transform::CreateTranslation(AZ::Vector3(5.0f, 15.0f, 10.0f));
|
||||
AZ::TransformBus::Event(m_entityId1, &AZ::TransformBus::Events::SetWorldTM, initialTransformWorld);
|
||||
|
||||
// initial camera position (looking down the negative x-axis)
|
||||
AzFramework::SetCameraTransform(
|
||||
m_cameraState,
|
||||
AZ::Transform::CreateFromQuaternionAndTranslation(
|
||||
AZ::Quaternion::CreateFromEulerAnglesDegrees(AZ::Vector3(0.0f, 0.0f, 90.0f)), AZ::Vector3(10.0f, 15.0f, 10.0f)));
|
||||
|
||||
using ::testing::Eq;
|
||||
auto selectedEntitiesBefore = SelectedEntities();
|
||||
EXPECT_TRUE(selectedEntitiesBefore.empty());
|
||||
|
||||
// calculate the position in screen space of the initial entity position
|
||||
const auto initialPositionScreen = AzFramework::WorldToScreen(initialTransformWorld.GetTranslation(), m_cameraState);
|
||||
|
||||
// click the entity in the viewport
|
||||
m_actionDispatcher->CameraState(m_cameraState)->MousePosition(initialPositionScreen)->MouseLButtonDown()->MouseLButtonUp();
|
||||
|
||||
// entity is selected
|
||||
auto selectedEntitiesAfter = SelectedEntities();
|
||||
EXPECT_THAT(selectedEntitiesAfter.size(), Eq(1));
|
||||
EXPECT_THAT(selectedEntitiesAfter.front(), Eq(m_entityId1));
|
||||
}
|
||||
|
||||
TEST_F(EditorTransformComponentSelectionViewportPickingManipulatorTestFixture, SingleClickOffEntityWithSelectionWillNotDeselectEntity)
|
||||
{
|
||||
AzToolsFramework::ed_viewportStickySelect = true;
|
||||
|
||||
// the initial starting position of the entity
|
||||
AZ::TransformBus::Event(
|
||||
m_entityId1, &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3(5.0f, 15.0f, 10.0f)));
|
||||
|
||||
// position in space above the entity
|
||||
const auto clickOffPositionWorld = AZ::Vector3(5.0f, 15.0f, 12.0f);
|
||||
|
||||
// initial camera position (looking down the negative x-axis)
|
||||
AzFramework::SetCameraTransform(
|
||||
m_cameraState,
|
||||
AZ::Transform::CreateFromQuaternionAndTranslation(
|
||||
AZ::Quaternion::CreateFromEulerAnglesDegrees(AZ::Vector3(0.0f, 0.0f, 90.0f)), AZ::Vector3(10.0f, 15.0f, 10.0f)));
|
||||
|
||||
AzToolsFramework::SelectEntity(m_entityId1);
|
||||
|
||||
// calculate the position in screen space of the initial position of the entity
|
||||
const auto clickOffPositionScreen = AzFramework::WorldToScreen(clickOffPositionWorld, m_cameraState);
|
||||
|
||||
// click the empty space in the viewport
|
||||
m_actionDispatcher->CameraState(m_cameraState)->MousePosition(clickOffPositionScreen)->MouseLButtonDown()->MouseLButtonUp();
|
||||
|
||||
// entity was not deselected
|
||||
using ::testing::Eq;
|
||||
auto selectedEntitiesAfter = SelectedEntities();
|
||||
EXPECT_THAT(selectedEntitiesAfter.size(), Eq(1));
|
||||
EXPECT_THAT(selectedEntitiesAfter.front(), Eq(m_entityId1));
|
||||
}
|
||||
|
||||
TEST_F(
|
||||
EditorTransformComponentSelectionViewportPickingManipulatorTestFixture,
|
||||
SingleClickOnNewEntityWithSelectionWillNotChangeSelectedEntity)
|
||||
{
|
||||
AzToolsFramework::ed_viewportStickySelect = true;
|
||||
|
||||
// the initial starting position of the entity
|
||||
AZ::TransformBus::Event(
|
||||
m_entityId1, &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3(5.0f, 15.0f, 10.0f)));
|
||||
|
||||
const auto initialTransformWorldSecondEntity = AZ::Transform::CreateTranslation(AZ::Vector3(5.0f, 10.0f, 10.0f));
|
||||
AZ::TransformBus::Event(m_entityId2, &AZ::TransformBus::Events::SetWorldTM, initialTransformWorldSecondEntity);
|
||||
|
||||
// initial camera position (looking down the negative x-axis)
|
||||
AzFramework::SetCameraTransform(
|
||||
m_cameraState,
|
||||
AZ::Transform::CreateFromQuaternionAndTranslation(
|
||||
AZ::Quaternion::CreateFromEulerAnglesDegrees(AZ::Vector3(0.0f, 0.0f, 90.0f)), AZ::Vector3(10.0f, 15.0f, 10.0f)));
|
||||
|
||||
AzToolsFramework::SelectEntity(m_entityId1);
|
||||
|
||||
// calculate the position in screen space of the second entity
|
||||
const auto initialPositionScreenSecondEntity =
|
||||
AzFramework::WorldToScreen(initialTransformWorldSecondEntity.GetTranslation(), m_cameraState);
|
||||
|
||||
// click the entity in the viewport
|
||||
m_actionDispatcher->CameraState(m_cameraState)
|
||||
->MousePosition(initialPositionScreenSecondEntity)
|
||||
->MouseLButtonDown()
|
||||
->MouseLButtonUp();
|
||||
|
||||
// entity selection was not changed
|
||||
using ::testing::Eq;
|
||||
auto selectedEntitiesAfter = SelectedEntities();
|
||||
EXPECT_THAT(selectedEntitiesAfter.size(), Eq(1));
|
||||
EXPECT_THAT(selectedEntitiesAfter.front(), Eq(m_entityId1));
|
||||
}
|
||||
|
||||
TEST_F(
|
||||
EditorTransformComponentSelectionViewportPickingManipulatorTestFixture,
|
||||
CtrlSingleClickOnNewEntityWithSelectionWillAppendSelectedEntityToSelection)
|
||||
{
|
||||
AzToolsFramework::ed_viewportStickySelect = true;
|
||||
|
||||
// the initial starting position of the entity
|
||||
AZ::TransformBus::Event(
|
||||
m_entityId1, &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3(5.0f, 15.0f, 10.0f)));
|
||||
|
||||
const auto initialTransformWorldSecondEntity = AZ::Transform::CreateTranslation(AZ::Vector3(5.0f, 10.0f, 10.0f));
|
||||
AZ::TransformBus::Event(m_entityId2, &AZ::TransformBus::Events::SetWorldTM, initialTransformWorldSecondEntity);
|
||||
|
||||
// initial camera position (looking down the negative x-axis)
|
||||
AzFramework::SetCameraTransform(
|
||||
m_cameraState,
|
||||
AZ::Transform::CreateFromQuaternionAndTranslation(
|
||||
AZ::Quaternion::CreateFromEulerAnglesDegrees(AZ::Vector3(0.0f, 0.0f, 90.0f)), AZ::Vector3(10.0f, 15.0f, 10.0f)));
|
||||
|
||||
AzToolsFramework::SelectEntity(m_entityId1);
|
||||
|
||||
// calculate the position in screen space of the second entity
|
||||
const auto initialPositionScreenSecondEntity =
|
||||
AzFramework::WorldToScreen(initialTransformWorldSecondEntity.GetTranslation(), m_cameraState);
|
||||
|
||||
// click the entity in the viewport
|
||||
m_actionDispatcher->CameraState(m_cameraState)
|
||||
->MousePosition(initialPositionScreenSecondEntity)
|
||||
->KeyboardModifierDown(AzToolsFramework::ViewportInteraction::KeyboardModifier::Control)
|
||||
->MouseLButtonDown()
|
||||
->MouseLButtonUp();
|
||||
|
||||
// entity selection was changed (one entity selected to two)
|
||||
using ::testing::UnorderedElementsAre;
|
||||
auto selectedEntitiesAfter = SelectedEntities();
|
||||
EXPECT_THAT(selectedEntitiesAfter, UnorderedElementsAre(m_entityId1, m_entityId2));
|
||||
}
|
||||
|
||||
TEST_F(
|
||||
EditorTransformComponentSelectionViewportPickingManipulatorTestFixture,
|
||||
CtrlSingleClickOnEntityInSelectionWillRemoveEntityFromSelection)
|
||||
{
|
||||
AzToolsFramework::ed_viewportStickySelect = true;
|
||||
|
||||
// the initial starting position of the entity
|
||||
AZ::TransformBus::Event(
|
||||
m_entityId1, &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3(5.0f, 15.0f, 10.0f)));
|
||||
|
||||
const auto initialTransformWorldSecondEntity = AZ::Transform::CreateTranslation(AZ::Vector3(5.0f, 10.0f, 10.0f));
|
||||
AZ::TransformBus::Event(m_entityId2, &AZ::TransformBus::Events::SetWorldTM, initialTransformWorldSecondEntity);
|
||||
|
||||
// initial camera position (looking down the negative x-axis)
|
||||
AzFramework::SetCameraTransform(
|
||||
m_cameraState,
|
||||
AZ::Transform::CreateFromQuaternionAndTranslation(
|
||||
AZ::Quaternion::CreateFromEulerAnglesDegrees(AZ::Vector3(0.0f, 0.0f, 90.0f)), AZ::Vector3(10.0f, 15.0f, 10.0f)));
|
||||
|
||||
AzToolsFramework::SelectEntities({ m_entityId1, m_entityId2 });
|
||||
|
||||
// calculate the position in screen space of the second entity
|
||||
const auto initialPositionScreenSecondEntity =
|
||||
AzFramework::WorldToScreen(initialTransformWorldSecondEntity.GetTranslation(), m_cameraState);
|
||||
|
||||
// click the entity in the viewport
|
||||
m_actionDispatcher->CameraState(m_cameraState)
|
||||
->MousePosition(initialPositionScreenSecondEntity)
|
||||
->KeyboardModifierDown(AzToolsFramework::ViewportInteraction::KeyboardModifier::Control)
|
||||
->MouseLButtonDown()
|
||||
->MouseLButtonUp();
|
||||
|
||||
// entity selection was changed (entity2 was deselected)
|
||||
using ::testing::UnorderedElementsAre;
|
||||
auto selectedEntitiesAfter = SelectedEntities();
|
||||
EXPECT_THAT(selectedEntitiesAfter, UnorderedElementsAre(m_entityId1));
|
||||
}
|
||||
|
||||
TEST_F(EditorTransformComponentSelectionViewportPickingManipulatorTestFixture, DISABLED_BoxSelectWithNoInitialSelectionAddsEntitiesToSelection)
|
||||
{
|
||||
AzToolsFramework::ed_viewportStickySelect = true;
|
||||
|
||||
// the initial starting position of the entities
|
||||
AZ::TransformBus::Event(
|
||||
m_entityId1, &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3(5.0f, 15.0f, 10.0f)));
|
||||
AZ::TransformBus::Event(
|
||||
m_entityId2, &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3(5.0f, 14.0f, 10.0f)));
|
||||
AZ::TransformBus::Event(
|
||||
m_entityId3, &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3(5.0f, 16.0f, 10.0f)));
|
||||
|
||||
// initial camera position (looking down the negative x-axis)
|
||||
AzFramework::SetCameraTransform(
|
||||
m_cameraState,
|
||||
AZ::Transform::CreateFromQuaternionAndTranslation(
|
||||
AZ::Quaternion::CreateFromEulerAnglesDegrees(AZ::Vector3(0.0f, 0.0f, 90.0f)), AZ::Vector3(10.0f, 15.0f, 10.0f)));
|
||||
|
||||
using ::testing::Eq;
|
||||
auto selectedEntitiesBefore = SelectedEntities();
|
||||
EXPECT_THAT(selectedEntitiesBefore.size(), Eq(0));
|
||||
|
||||
// calculate the position in screen space of where to begin and end the box select action
|
||||
const auto beginningPositionWorldBoxSelectStart = AzFramework::WorldToScreen(AZ::Vector3(5.0f, 13.5f, 10.5f), m_cameraState);
|
||||
const auto middlePositionWorldBoxSelectStart = AzFramework::WorldToScreen(AZ::Vector3(5.0f, 15.0f, 10.0f), m_cameraState);
|
||||
const auto endingPositionWorldBoxSelectStart = AzFramework::WorldToScreen(AZ::Vector3(5.0f, 16.5f, 9.5f), m_cameraState);
|
||||
|
||||
// perform a box select in the viewport
|
||||
m_actionDispatcher->CameraState(m_cameraState)
|
||||
->MousePosition(beginningPositionWorldBoxSelectStart)
|
||||
->MouseLButtonDown()
|
||||
->MousePosition(middlePositionWorldBoxSelectStart)
|
||||
->MousePosition(endingPositionWorldBoxSelectStart)
|
||||
->MouseLButtonUp();
|
||||
|
||||
// entities are selected
|
||||
using ::testing::UnorderedElementsAre;
|
||||
auto selectedEntitiesAfter = SelectedEntities();
|
||||
EXPECT_THAT(selectedEntitiesAfter, UnorderedElementsAre(m_entityId1, m_entityId2, m_entityId3));
|
||||
}
|
||||
|
||||
using EditorTransformComponentSelectionManipulatorTestFixture =
|
||||
IndirectCallManipulatorViewportInteractionFixtureMixin<EditorTransformComponentSelectionFixture>;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user