Apply reviewer suggestions
Signed-off-by: Nemerle <nemerle5+git@gmail.com>
This commit is contained in:
@@ -878,9 +878,6 @@ namespace AzToolsFramework
|
||||
/// Return the icon texture id (from internal IconManager) for a given entity icon path.
|
||||
/// This can be passed to DrawTextureLabel to draw an entity icon.
|
||||
virtual int GetIconTextureIdFromEntityIconPath(const AZStd::string& entityIconPath) = 0;
|
||||
|
||||
/// Returns if the Display Helpers option is toggled on in the Editor.
|
||||
virtual bool DisplayHelpersVisible() = 0;
|
||||
};
|
||||
|
||||
using EditorRequestBus = AZ::EBus<EditorRequests>;
|
||||
|
||||
@@ -73,6 +73,7 @@
|
||||
#include <AzToolsFramework/Prefab/PrefabPublicInterface.h>
|
||||
#include <Entity/EntityUtilityComponent.h>
|
||||
#include <AzToolsFramework/Script/LuaSymbolsReporterSystemComponent.h>
|
||||
#include <Prefab/ProceduralPrefabSystemComponent.h>
|
||||
|
||||
#include <QtWidgets/QMessageBox>
|
||||
AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option") // 4251: 'QFileInfo::d_ptr': class 'QSharedDataPointer<QFileInfoPrivate>' needs to have dll-interface to be used by clients of class 'QFileInfo'
|
||||
@@ -272,6 +273,7 @@ namespace AzToolsFramework
|
||||
azrtti_typeid<ReadOnlyEntitySystemComponent>(),
|
||||
azrtti_typeid<SliceMetadataEntityContextComponent>(),
|
||||
azrtti_typeid<Prefab::PrefabSystemComponent>(),
|
||||
azrtti_typeid<Prefab::ProceduralPrefabSystemComponent>(),
|
||||
azrtti_typeid<EditorEntityFixupComponent>(),
|
||||
azrtti_typeid<Components::EditorComponentAPIComponent>(),
|
||||
azrtti_typeid<Components::EditorLevelComponentAPIComponent>(),
|
||||
|
||||
@@ -56,6 +56,7 @@
|
||||
#include <AzToolsFramework/ViewportSelection/EditorInteractionSystemComponent.h>
|
||||
#include <AzToolsFramework/Entity/EntityUtilityComponent.h>
|
||||
#include <AzToolsFramework/Script/LuaSymbolsReporterSystemComponent.h>
|
||||
#include <Prefab/ProceduralPrefabSystemComponent.h>
|
||||
|
||||
AZ_DEFINE_BUDGET(AzToolsFramework);
|
||||
|
||||
@@ -82,6 +83,7 @@ namespace AzToolsFramework
|
||||
SliceRequestComponent::CreateDescriptor(),
|
||||
Prefab::PrefabSystemComponent::CreateDescriptor(),
|
||||
Prefab::EditorPrefabComponent::CreateDescriptor(),
|
||||
Prefab::ProceduralPrefabSystemComponent::CreateDescriptor(),
|
||||
Components::EditorEntityActionComponent::CreateDescriptor(),
|
||||
Components::EditorEntityIconComponent::CreateDescriptor(),
|
||||
Components::EditorInspectorComponent::CreateDescriptor(),
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabDomUtils.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabSystemComponentInterface.h>
|
||||
#include <Prefab/ProceduralPrefabSystemComponentInterface.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
@@ -185,6 +186,20 @@ namespace AzToolsFramework
|
||||
TemplateReference newTemplateReference = m_prefabSystemComponentInterface->FindTemplate(newTemplateId);
|
||||
Template& newTemplate = newTemplateReference->get();
|
||||
|
||||
if(newTemplate.IsProcedural())
|
||||
{
|
||||
auto proceduralPrefabSystemComponentInterface = AZ::Interface<ProceduralPrefabSystemComponentInterface>::Get();
|
||||
|
||||
if (!proceduralPrefabSystemComponentInterface)
|
||||
{
|
||||
AZ_Error("Prefab", false, "Failed to find ProceduralPrefabSystemComponentInterface. Procedural Prefab will not be tracked for hotloading.");
|
||||
}
|
||||
else
|
||||
{
|
||||
proceduralPrefabSystemComponentInterface->RegisterProceduralPrefab(relativePath.Native(), newTemplateId);
|
||||
}
|
||||
}
|
||||
|
||||
// Mark the file as being in progress.
|
||||
progressedFilePathsSet.emplace(relativePath);
|
||||
|
||||
|
||||
@@ -26,6 +26,14 @@ namespace AzToolsFramework
|
||||
|
||||
virtual void OnPrefabTemplateDirtyFlagUpdated(
|
||||
[[maybe_unused]] TemplateId templateId, [[maybe_unused]] bool status) {}
|
||||
|
||||
// Sent after a single template has been removed
|
||||
// Does not get sent when all templates are being removed at once. Be sure to handle OnAllTemplatesRemoved as well.
|
||||
virtual void OnTemplateRemoved([[maybe_unused]] TemplateId templateId) {}
|
||||
|
||||
// Sent after all templates have been removed
|
||||
// Does not trigger individual OnTemplateRemoved events
|
||||
virtual void OnAllTemplatesRemoved() {}
|
||||
};
|
||||
|
||||
using PrefabPublicNotificationBus = AZ::EBus<PrefabPublicNotifications>;
|
||||
|
||||
@@ -160,7 +160,7 @@ namespace AzToolsFramework
|
||||
newInstance->SetTemplateId(newTemplateId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PrefabSystemComponent::PropagateTemplateChanges(TemplateId templateId, InstanceOptionalConstReference instanceToExclude)
|
||||
{
|
||||
auto templateIdToLinkIdsIterator = m_templateToLinkIdsMap.find(templateId);
|
||||
@@ -442,7 +442,7 @@ namespace AzToolsFramework
|
||||
}
|
||||
|
||||
m_templateFilePathToIdMap.emplace(AZStd::make_pair(filePath, newTemplateId));
|
||||
|
||||
|
||||
return newTemplateId;
|
||||
}
|
||||
|
||||
@@ -469,7 +469,7 @@ namespace AzToolsFramework
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
m_templateFilePathToIdMap.erase(templateToChange.GetFilePath());
|
||||
if (!m_templateFilePathToIdMap.try_emplace(filePath, templateId).second)
|
||||
{
|
||||
@@ -500,7 +500,7 @@ namespace AzToolsFramework
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
//Remove all Links owned by the Template from TemplateToLinkIdsMap.
|
||||
Template& templateToDelete = findTemplateResult->get();
|
||||
const Template::Links& linkIdsToDelete = templateToDelete.GetLinks();
|
||||
@@ -546,7 +546,7 @@ namespace AzToolsFramework
|
||||
templateId, templateToDelete.GetFilePath().c_str());
|
||||
|
||||
m_templateInstanceMapper.UnregisterTemplate(templateId);
|
||||
|
||||
|
||||
result = m_templateIdMap.erase(templateId) != 0;
|
||||
AZ_Assert(result,
|
||||
"Prefab - PrefabSystemComponent::RemoveTemplate - "
|
||||
@@ -554,7 +554,10 @@ namespace AzToolsFramework
|
||||
"from Template Id Map.",
|
||||
templateId, templateToDelete.GetFilePath().c_str());
|
||||
|
||||
return;
|
||||
if (!m_removingAllTemplates)
|
||||
{
|
||||
PrefabPublicNotificationBus::Broadcast(&PrefabPublicNotificationBus::Events::OnTemplateRemoved, templateId);
|
||||
}
|
||||
}
|
||||
|
||||
void PrefabSystemComponent::RemoveAllTemplates()
|
||||
@@ -568,10 +571,15 @@ namespace AzToolsFramework
|
||||
templateIds.emplace_back(id);
|
||||
}
|
||||
|
||||
m_removingAllTemplates = true;
|
||||
|
||||
for (auto id : templateIds)
|
||||
{
|
||||
RemoveTemplate(id);
|
||||
}
|
||||
|
||||
m_removingAllTemplates = false;
|
||||
PrefabPublicNotificationBus::Broadcast(&PrefabPublicNotificationBus::Events::OnAllTemplatesRemoved);
|
||||
}
|
||||
|
||||
LinkId PrefabSystemComponent::AddLink(
|
||||
@@ -859,7 +867,7 @@ namespace AzToolsFramework
|
||||
|
||||
void PrefabSystemComponent::SaveAllDirtyTemplates(TemplateId rootTemplateId)
|
||||
{
|
||||
AZStd::set<AZ::IO::PathView> dirtyTemplatePaths = GetDirtyTemplatePaths(rootTemplateId);
|
||||
AZStd::set<AZ::IO::PathView> dirtyTemplatePaths = GetDirtyTemplatePaths(rootTemplateId);
|
||||
|
||||
for (AZ::IO::PathView dirtyTemplatePath : dirtyTemplatePaths)
|
||||
{
|
||||
|
||||
@@ -56,7 +56,7 @@ namespace AzToolsFramework
|
||||
public:
|
||||
|
||||
using TargetTemplateIdToLinkIdMap = AZStd::unordered_map<TemplateId, AZStd::pair<AZStd::unordered_set<LinkId>, bool>>;
|
||||
|
||||
|
||||
AZ_COMPONENT(PrefabSystemComponent, "{27203AE6-A398-4614-881B-4EEB5E9B34E9}");
|
||||
|
||||
PrefabSystemComponent() = default;
|
||||
@@ -220,7 +220,7 @@ namespace AzToolsFramework
|
||||
const AZStd::vector<AZ::Entity*>& entities, AZStd::vector<AZStd::unique_ptr<Instance>>&& instancesToConsume,
|
||||
AZ::IO::PathView filePath, AZStd::unique_ptr<AZ::Entity> containerEntity = nullptr,
|
||||
InstanceOptionalReference parent = AZStd::nullopt, bool shouldCreateLinks = true) override;
|
||||
|
||||
|
||||
PrefabDom& FindTemplateDom(TemplateId templateId) override;
|
||||
|
||||
/**
|
||||
@@ -244,7 +244,7 @@ namespace AzToolsFramework
|
||||
|
||||
private:
|
||||
AZ_DISABLE_COPY_MOVE(PrefabSystemComponent);
|
||||
|
||||
|
||||
/**
|
||||
* Builds a new Prefab Template out of entities and instances and returns the first instance comprised of
|
||||
* these entities and instances.
|
||||
@@ -263,14 +263,14 @@ namespace AzToolsFramework
|
||||
/**
|
||||
* Updates all the linked Instances corresponding to the linkIds in the provided queue.
|
||||
* Queue gets populated with more linkId lists as linked instances are updated. Updating stops when the queue is empty.
|
||||
*
|
||||
*
|
||||
* @param linkIdsQueue A queue of vector of link-Ids to update.
|
||||
*/
|
||||
void UpdateLinkedInstances(AZStd::queue<LinkIds>& linkIdsQueue);
|
||||
|
||||
/**
|
||||
* Given a vector of link ids to update, splits them into smaller lists based on the target template id of the links.
|
||||
*
|
||||
*
|
||||
* @param linkIdsToUpdate The list of link ids to update.
|
||||
* @param targetTemplateIdToLinkIdMap The map of target templateIds to a pair of lists of linkIds and a bool flag indicating
|
||||
* whether any of the instances of the target template were updated.
|
||||
@@ -279,9 +279,9 @@ namespace AzToolsFramework
|
||||
TargetTemplateIdToLinkIdMap& targetTemplateIdToLinkIdMap);
|
||||
|
||||
/**
|
||||
* Updates a single linked instance corresponding to the given link Id and adds more linkIds to the
|
||||
* Updates a single linked instance corresponding to the given link Id and adds more linkIds to the
|
||||
* template change propagation queue(linkIdsQueue) when necessary.
|
||||
*
|
||||
*
|
||||
* @param linkIdToUpdate The id of the linked instance to update
|
||||
* @param targetTemplateIdToLinkIdMap The map of target templateIds to a pair of lists of linkIds and a bool flag indicating
|
||||
* whether any of the instances of the target template were updated.
|
||||
@@ -293,7 +293,7 @@ namespace AzToolsFramework
|
||||
/**
|
||||
* If all linked instances of a target template are updated and if the content of any of the linked instances changed,
|
||||
* this method fetches all the linked instances sourced by it and adds their corresponding ids to the LinkIdsQueue.
|
||||
*
|
||||
*
|
||||
* @param targetTemplateIdToLinkIdMap The map of target templateIds to a pair of lists of linkIds and a bool flag indicating
|
||||
* whether any of the instances of the target template were updated.
|
||||
* @param targetTemplateId The id of the template, whose linked instances we need to find if the template was updated.
|
||||
@@ -414,6 +414,9 @@ namespace AzToolsFramework
|
||||
PrefabPublicRequestHandler m_prefabPublicRequestHandler;
|
||||
|
||||
PrefabSystemScriptingHandler m_prefabSystemScriptingHandler;
|
||||
|
||||
// If true, individual template-remove messages will be suppressed
|
||||
bool m_removingAllTemplates = false;
|
||||
};
|
||||
} // namespace Prefab
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
+9
-5
@@ -9,12 +9,11 @@
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/Serialization/Json/RegistrationContext.h>
|
||||
#include <AzCore/Settings/SettingsRegistry.h>
|
||||
#include <AzFramework/API/ApplicationAPI.h>
|
||||
#include <AzFramework/FileFunc/FileFunc.h>
|
||||
|
||||
namespace AZ::Prefab
|
||||
{
|
||||
static constexpr const char s_useProceduralPrefabsKey[] = "/O3DE/Preferences/Prefabs/UseProceduralPrefabs";
|
||||
|
||||
// ProceduralPrefabAsset
|
||||
|
||||
ProceduralPrefabAsset::ProceduralPrefabAsset(const AZ::Data::AssetId& assetId)
|
||||
@@ -58,9 +57,14 @@ namespace AZ::Prefab
|
||||
|
||||
bool ProceduralPrefabAsset::UseProceduralPrefabs()
|
||||
{
|
||||
bool useProceduralPrefabs = false;
|
||||
bool result = AZ::SettingsRegistry::Get()->GetObject(useProceduralPrefabs, s_useProceduralPrefabsKey);
|
||||
return result && useProceduralPrefabs;
|
||||
bool prefabsEnabled = false;
|
||||
AzFramework::ApplicationRequests::Bus::Broadcast(
|
||||
[&prefabsEnabled](AzFramework::ApplicationRequests::Bus::Events* ebus)
|
||||
{
|
||||
prefabsEnabled = ebus->IsPrefabSystemEnabled();
|
||||
});
|
||||
|
||||
return prefabsEnabled;
|
||||
}
|
||||
|
||||
// PrefabDomData
|
||||
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* 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 <AssetCatalog/PlatformAddressedAssetCatalogBus.h>
|
||||
#include <AzCore/Serialization/Json/JsonUtils.h>
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
#include <AzFramework/Asset/AssetCatalogBus.h>
|
||||
#include <AzToolsFramework/Prefab/ProceduralPrefabSystemComponent.h>
|
||||
#include <Prefab/PrefabDomUtils.h>
|
||||
#include <Prefab/PrefabLoaderInterface.h>
|
||||
#include <Prefab/PrefabSystemComponentInterface.h>
|
||||
#include <Prefab/Procedural/ProceduralPrefabAsset.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
namespace Prefab
|
||||
{
|
||||
void ProceduralPrefabSystemComponent::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
if (auto serializationContext = azrtti_cast<AZ::SerializeContext*>(context))
|
||||
{
|
||||
serializationContext->Class<ProceduralPrefabSystemComponent, AZ::Component>();
|
||||
}
|
||||
}
|
||||
|
||||
void ProceduralPrefabSystemComponent::Activate()
|
||||
{
|
||||
AZ::Interface<ProceduralPrefabSystemComponentInterface>::Register(this);
|
||||
AzFramework::AssetCatalogEventBus::Handler::BusConnect();
|
||||
}
|
||||
|
||||
void ProceduralPrefabSystemComponent::Deactivate()
|
||||
{
|
||||
AzFramework::AssetCatalogEventBus::Handler::BusDisconnect();
|
||||
AZ::Interface<ProceduralPrefabSystemComponentInterface>::Unregister(this);
|
||||
|
||||
AZStd::scoped_lock lock(m_lookupMutex);
|
||||
m_assetIdToTemplateLookup.clear();
|
||||
}
|
||||
|
||||
void ProceduralPrefabSystemComponent::OnCatalogAssetChanged(const AZ::Data::AssetId& assetId)
|
||||
{
|
||||
TemplateId templateId = InvalidTemplateId;
|
||||
|
||||
{
|
||||
AZStd::scoped_lock lock(m_lookupMutex);
|
||||
auto itr = m_assetIdToTemplateLookup.find(assetId);
|
||||
|
||||
if (itr != m_assetIdToTemplateLookup.end())
|
||||
{
|
||||
templateId = itr->second;
|
||||
}
|
||||
}
|
||||
|
||||
if (templateId != InvalidTemplateId)
|
||||
{
|
||||
auto prefabSystemComponentInterface = AZ::Interface<PrefabSystemComponentInterface>::Get();
|
||||
|
||||
if (!prefabSystemComponentInterface)
|
||||
{
|
||||
AZ_Error("Prefab", false, "Failed to get PrefabSystemComponentInterface");
|
||||
return;
|
||||
}
|
||||
|
||||
AZStd::string assetPath;
|
||||
AZ::Data::AssetCatalogRequestBus::BroadcastResult(
|
||||
assetPath, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetPathById, assetId);
|
||||
|
||||
auto readResult = AZ::Utils::ReadFile(assetPath, AZStd::numeric_limits<size_t>::max());
|
||||
if (!readResult.IsSuccess())
|
||||
{
|
||||
AZ_Error(
|
||||
"Prefab", false,
|
||||
"ProceduralPrefabSystemComponent::OnCatalogAssetChanged - Failed to read Prefab file from '%.*s'."
|
||||
"Error message: '%s'",
|
||||
AZ_STRING_ARG(assetPath), readResult.GetError().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
AZ::Outcome<PrefabDom, AZStd::string> readPrefabFileResult = AZ::JsonSerializationUtils::ReadJsonString(readResult.TakeValue());
|
||||
if (!readPrefabFileResult.IsSuccess())
|
||||
{
|
||||
AZ_Error(
|
||||
"Prefab", false,
|
||||
"ProceduralPrefabSystemComponent::OnCatalogAssetChanged - Failed to read Prefab json from '%.*s'."
|
||||
"Error message: '%s'",
|
||||
AZ_STRING_ARG(assetPath), readPrefabFileResult.GetError().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
AZ::IO::Path relativePath = AZ::Interface<PrefabLoaderInterface>::Get()->GenerateRelativePath(assetPath.c_str());
|
||||
PrefabDomPath sourcePath = PrefabDomPath((AZStd::string("/") + PrefabDomUtils::SourceName).c_str());
|
||||
sourcePath.Set(readPrefabFileResult.GetValue(), relativePath.Native().c_str());
|
||||
|
||||
prefabSystemComponentInterface->UpdatePrefabTemplate(templateId, readPrefabFileResult.TakeValue());
|
||||
}
|
||||
}
|
||||
|
||||
void ProceduralPrefabSystemComponent::OnTemplateRemoved(TemplateId removedTemplateId)
|
||||
{
|
||||
AZStd::scoped_lock lock(m_lookupMutex);
|
||||
|
||||
for (const auto& [assetId, templateId] : m_assetIdToTemplateLookup)
|
||||
{
|
||||
if (templateId == removedTemplateId)
|
||||
{
|
||||
m_assetIdToTemplateLookup.erase(assetId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ProceduralPrefabSystemComponent::OnAllTemplatesRemoved()
|
||||
{
|
||||
AZStd::scoped_lock lock(m_lookupMutex);
|
||||
|
||||
m_assetIdToTemplateLookup.clear();
|
||||
}
|
||||
|
||||
void ProceduralPrefabSystemComponent::RegisterProceduralPrefab(const AZStd::string& prefabFilePath, TemplateId templateId)
|
||||
{
|
||||
AZ::Data::AssetId assetId;
|
||||
AZ::Data::AssetCatalogRequestBus::BroadcastResult(
|
||||
assetId, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetIdByPath, prefabFilePath.c_str(),
|
||||
azrtti_typeid<AZ::Prefab::ProceduralPrefabAsset>(), false);
|
||||
|
||||
if (assetId.IsValid())
|
||||
{
|
||||
AZStd::scoped_lock lock(m_lookupMutex);
|
||||
m_assetIdToTemplateLookup[assetId] = templateId;
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Error("Prefab", false, "Failed to find AssetId for prefab %s", prefabFilePath.c_str());
|
||||
}
|
||||
}
|
||||
} // namespace Prefab
|
||||
} // namespace AzToolsFramework
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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 <Prefab/ProceduralPrefabSystemComponentInterface.h>
|
||||
#include <AzFramework/Asset/AssetCatalogBus.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabPublicNotificationBus.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
namespace Prefab
|
||||
{
|
||||
class ProceduralPrefabSystemComponent
|
||||
: public AZ::Component
|
||||
, ProceduralPrefabSystemComponentInterface
|
||||
, AzFramework::AssetCatalogEventBus::Handler
|
||||
, PrefabPublicNotificationBus::Handler
|
||||
{
|
||||
public:
|
||||
AZ_COMPONENT(ProceduralPrefabSystemComponent, "{81211818-088A-49E6-894B-7A11764106B1}");
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
protected:
|
||||
void Activate() override;
|
||||
void Deactivate() override;
|
||||
|
||||
void OnCatalogAssetChanged(const AZ::Data::AssetId&) override;
|
||||
|
||||
void OnTemplateRemoved(TemplateId templateId) override;
|
||||
void OnAllTemplatesRemoved() override;
|
||||
|
||||
void RegisterProceduralPrefab(const AZStd::string& prefabFilePath, TemplateId templateId) override;
|
||||
|
||||
AZStd::mutex m_lookupMutex;
|
||||
AZStd::unordered_map<AZ::Data::AssetId, TemplateId> m_assetIdToTemplateLookup;
|
||||
};
|
||||
} // namespace Prefab
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
+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/RTTI/RTTI.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabIdTypes.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
namespace Prefab
|
||||
{
|
||||
class ProceduralPrefabSystemComponentInterface
|
||||
{
|
||||
public:
|
||||
AZ_RTTI(ProceduralPrefabSystemComponentInterface, "{C403B89C-63DD-418C-B821-FBCBE1EF42AE}");
|
||||
|
||||
virtual ~ProceduralPrefabSystemComponentInterface() = default;
|
||||
|
||||
// Registers a procedural prefab file + templateId so the system can track changes and handle updates
|
||||
virtual void RegisterProceduralPrefab(const AZStd::string& prefabFilePath, TemplateId templateId) = 0;
|
||||
};
|
||||
|
||||
} // namespace Prefab
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
+35
-12
@@ -26,7 +26,9 @@
|
||||
#include <AzToolsFramework/Entity/EditorEntityTransformBus.h>
|
||||
#include <AzToolsFramework/API/EntityPropertyEditorRequestsBus.h>
|
||||
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
|
||||
#include <AzToolsFramework/ContainerEntity/ContainerEntityInterface.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
|
||||
#include <AzToolsFramework/Entity/ReadOnly/ReadOnlyEntityInterface.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabPublicInterface.h>
|
||||
#include <AzToolsFramework/ToolsComponents/TransformComponentBus.h>
|
||||
#include <AzToolsFramework/ToolsComponents/TransformComponentSerializer.h>
|
||||
@@ -35,9 +37,8 @@
|
||||
#include <AzToolsFramework/ViewportSelection/EditorSelectionUtil.h>
|
||||
#include <AzToolsFramework/Viewport/ViewportMessages.h>
|
||||
|
||||
#include <QMessageBox>
|
||||
|
||||
#include <QMenu>
|
||||
#include <QMessageBox>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
@@ -662,7 +663,6 @@ namespace AzToolsFramework
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Prevent this from parenting to its own child. Check if this entity is in the new parent's hierarchy.
|
||||
auto potentialParentTransformComponent = GetTransformComponent(parentId);
|
||||
if (potentialParentTransformComponent && potentialParentTransformComponent->IsEntityInHierarchy(GetEntityId()))
|
||||
@@ -931,14 +931,27 @@ namespace AzToolsFramework
|
||||
{
|
||||
return AZ::Failure(AZStd::string("You cannot set an entity's parent to itself!"));
|
||||
}
|
||||
else
|
||||
|
||||
// Don't allow the change if it will result in a cycle hierarchy
|
||||
auto potentialParentTransformComponent = GetTransformComponent(actualValue);
|
||||
if (potentialParentTransformComponent && potentialParentTransformComponent->IsEntityInHierarchy(GetEntityId()))
|
||||
{
|
||||
// Don't allow the change if it will result in a cycle hierarchy
|
||||
auto potentialParentTransformComponent = GetTransformComponent(actualValue);
|
||||
if (potentialParentTransformComponent && potentialParentTransformComponent->IsEntityInHierarchy(GetEntityId()))
|
||||
{
|
||||
return AZ::Failure(AZStd::string("You cannot set an entity to be a child of one of its own children!"));
|
||||
}
|
||||
return AZ::Failure(AZStd::string("You cannot set an entity to be a child of one of its own children!"));
|
||||
}
|
||||
|
||||
// Don't allow read-only entities to be re-parented at all.
|
||||
// Also don't allow entities to be parented under read-only entities.
|
||||
if (auto readOnlyEntityPublicInterface = AZ::Interface<ReadOnlyEntityPublicInterface>::Get();
|
||||
readOnlyEntityPublicInterface->IsReadOnly(GetEntityId()) || readOnlyEntityPublicInterface->IsReadOnly(actualValue))
|
||||
{
|
||||
return AZ::Failure(AZStd::string("You cannot set an entity to be a child of a read-only entity!"));
|
||||
}
|
||||
|
||||
// Don't allow entities to be parented under closed containers.
|
||||
if (auto containerEntityInterface = AZ::Interface<ContainerEntityInterface>::Get();
|
||||
!containerEntityInterface->IsContainerOpen(actualValue))
|
||||
{
|
||||
return AZ::Failure(AZStd::string("You cannot set an entity to be a child of a closed container!"));
|
||||
}
|
||||
|
||||
return AZ::Success();
|
||||
@@ -1245,6 +1258,15 @@ namespace AzToolsFramework
|
||||
|
||||
void TransformComponent::AddContextMenuActions(QMenu* menu)
|
||||
{
|
||||
bool parentEntityIsReadOnly = false;
|
||||
|
||||
// If the parent entity is marked as read-only, don't allow actions on this transform.
|
||||
if (auto readOnlyEntityPublicInterface = AZ::Interface<ReadOnlyEntityPublicInterface>::Get();
|
||||
readOnlyEntityPublicInterface->IsReadOnly(GetEntityId()))
|
||||
{
|
||||
parentEntityIsReadOnly = true;
|
||||
}
|
||||
|
||||
if (menu)
|
||||
{
|
||||
if (!menu->actions().empty())
|
||||
@@ -1266,10 +1288,10 @@ namespace AzToolsFramework
|
||||
|
||||
AzToolsFramework::ToolsApplicationEvents::Bus::Broadcast(&AzToolsFramework::ToolsApplicationEvents::InvalidatePropertyDisplay, AzToolsFramework::Refresh_Values);
|
||||
});
|
||||
resetAction->setEnabled(!m_editorTransform.m_locked);
|
||||
resetAction->setEnabled(!m_editorTransform.m_locked && !parentEntityIsReadOnly);
|
||||
|
||||
QString lockString = m_editorTransform.m_locked ? "Unlock transform values" : "Lock transform values";
|
||||
menu->addAction(lockString, [this, lockString]()
|
||||
QAction* lockAction = menu->addAction(lockString, [this, lockString]()
|
||||
{
|
||||
{
|
||||
AzToolsFramework::ScopedUndoBatch undo(lockString.toUtf8().data());
|
||||
@@ -1278,6 +1300,7 @@ namespace AzToolsFramework
|
||||
}
|
||||
AzToolsFramework::ToolsApplicationEvents::Bus::Broadcast(&AzToolsFramework::ToolsApplicationEvents::InvalidatePropertyDisplay, AzToolsFramework::Refresh_AttributesAndValues);
|
||||
});
|
||||
lockAction->setEnabled(!parentEntityIsReadOnly);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -548,11 +548,11 @@ namespace AzToolsFramework
|
||||
AZ::EntityId entityId = GetEntityFromIndex(index);
|
||||
|
||||
// Only allow renaming the entity if the UI Handler did not block it.
|
||||
auto entityUiHandler = m_editorEntityUiInterface->GetHandler(entityId);
|
||||
auto entityUiHandler = m_editorEntityUiInterface ? m_editorEntityUiInterface->GetHandler(entityId) : nullptr;
|
||||
bool canRename = !entityUiHandler || entityUiHandler->CanRename(entityId);
|
||||
|
||||
// Disable renaming for read-only entities.
|
||||
bool isReadOnly = m_readOnlyEntityPublicInterface->IsReadOnly(entityId);
|
||||
bool isReadOnly = m_readOnlyEntityPublicInterface ? m_readOnlyEntityPublicInterface->IsReadOnly(entityId) : false;
|
||||
|
||||
Qt::ItemFlags itemFlags = QAbstractItemModel::flags(index);
|
||||
switch (index.column())
|
||||
|
||||
+25
-14
@@ -27,6 +27,7 @@
|
||||
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntityInfoBus.h>
|
||||
#include <AzToolsFramework/Entity/ReadOnly/ReadOnlyEntityInterface.h>
|
||||
#include <AzToolsFramework/FocusMode/FocusModeInterface.h>
|
||||
#include <AzToolsFramework/Viewport/ViewportMessages.h>
|
||||
#include <AzToolsFramework/UI/ComponentPalette/ComponentPaletteUtil.hxx>
|
||||
#include <AzToolsFramework/UI/EditorEntityUi/EditorEntityUiHandlerBase.h>
|
||||
@@ -154,9 +155,15 @@ namespace AzToolsFramework
|
||||
{
|
||||
initEntityOutlinerWidgetResources();
|
||||
|
||||
AzToolsFramework::EditorEntityContextRequestBus::BroadcastResult(
|
||||
m_editorEntityContextId, &AzToolsFramework::EditorEntityContextRequestBus::Events::GetEditorEntityContextId);
|
||||
|
||||
m_editorEntityUiInterface = AZ::Interface<AzToolsFramework::EditorEntityUiInterface>::Get();
|
||||
AZ_Assert(m_editorEntityUiInterface != nullptr, "EntityOutlinerWidget requires a EditorEntityUiInterface instance on Initialize.");
|
||||
|
||||
m_focusModeInterface = AZ::Interface<AzToolsFramework::FocusModeInterface>::Get();
|
||||
AZ_Assert(m_focusModeInterface != nullptr, "EntityOutlinerWidget requires a FocusModeInterface instance on Initialize.");
|
||||
|
||||
m_readOnlyEntityPublicInterface = AZ::Interface<AzToolsFramework::ReadOnlyEntityPublicInterface>::Get();
|
||||
AZ_Assert(
|
||||
(m_readOnlyEntityPublicInterface != nullptr),
|
||||
@@ -609,23 +616,27 @@ namespace AzToolsFramework
|
||||
{
|
||||
AZ::EntityId entityId = m_selectedEntityIds[0];
|
||||
|
||||
AZ::EntityId parentId;
|
||||
EditorEntityInfoRequestBus::EventResult(parentId, entityId, &EditorEntityInfoRequestBus::Events::GetParent);
|
||||
|
||||
EntityOrderArray entityOrderArray = GetEntityChildOrder(parentId);
|
||||
|
||||
if (entityOrderArray.size() > 1)
|
||||
// Don't allow moving the entity if it's the focus root.
|
||||
if (m_focusModeInterface->GetFocusRoot(m_editorEntityContextId) != entityId)
|
||||
{
|
||||
if (AZStd::find(entityOrderArray.begin(), entityOrderArray.end(), entityId) != entityOrderArray.end())
|
||||
{
|
||||
if (entityOrderArray.front() != entityId)
|
||||
{
|
||||
contextMenu->addAction(m_actionToMoveEntityUp);
|
||||
}
|
||||
AZ::EntityId parentId;
|
||||
EditorEntityInfoRequestBus::EventResult(parentId, entityId, &EditorEntityInfoRequestBus::Events::GetParent);
|
||||
|
||||
if (entityOrderArray.back() != entityId)
|
||||
EntityOrderArray entityOrderArray = GetEntityChildOrder(parentId);
|
||||
|
||||
if (entityOrderArray.size() > 1)
|
||||
{
|
||||
if (AZStd::find(entityOrderArray.begin(), entityOrderArray.end(), entityId) != entityOrderArray.end())
|
||||
{
|
||||
contextMenu->addAction(m_actionToMoveEntityDown);
|
||||
if (entityOrderArray.front() != entityId)
|
||||
{
|
||||
contextMenu->addAction(m_actionToMoveEntityUp);
|
||||
}
|
||||
|
||||
if (entityOrderArray.back() != entityId)
|
||||
{
|
||||
contextMenu->addAction(m_actionToMoveEntityDown);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ namespace AzToolsFramework
|
||||
class EntityOutlinerListModel;
|
||||
class EntityOutlinerContainerProxyModel;
|
||||
class EntityOutlinerSortFilterProxyModel;
|
||||
class FocusModeInterface;
|
||||
class ReadOnlyEntityPublicInterface;
|
||||
|
||||
namespace EntityOutliner
|
||||
@@ -204,7 +205,10 @@ namespace AzToolsFramework
|
||||
EntityOutliner::DisplaySortMode m_sortMode;
|
||||
bool m_sortContentQueued;
|
||||
|
||||
AzFramework::EntityContextId m_editorEntityContextId = AzFramework::EntityContextId::CreateNull();
|
||||
|
||||
EditorEntityUiInterface* m_editorEntityUiInterface = nullptr;
|
||||
FocusModeInterface* m_focusModeInterface = nullptr;
|
||||
ReadOnlyEntityPublicInterface* m_readOnlyEntityPublicInterface = nullptr;
|
||||
};
|
||||
|
||||
|
||||
+66
-33
@@ -277,6 +277,26 @@ namespace AzToolsFramework
|
||||
}
|
||||
}
|
||||
|
||||
bool onlySelectedEntityIsFocusedPrefabContainer = false;
|
||||
bool onlySelectedEntityIsClosedPrefabContainer = false;
|
||||
|
||||
if (selectedEntities.size() == 1)
|
||||
{
|
||||
AZ::EntityId selectedEntity = selectedEntities.front();
|
||||
|
||||
if (s_prefabPublicInterface->IsInstanceContainerEntity(selectedEntity))
|
||||
{
|
||||
if (s_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(selectedEntity))
|
||||
{
|
||||
onlySelectedEntityIsFocusedPrefabContainer = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
onlySelectedEntityIsClosedPrefabContainer = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool itemWasShown = false;
|
||||
|
||||
// Create Prefab
|
||||
@@ -284,8 +304,7 @@ namespace AzToolsFramework
|
||||
if (!selectedEntities.empty())
|
||||
{
|
||||
// Hide if the only selected entity is the Focused Instance Container
|
||||
if (selectedEntities.size() > 1 ||
|
||||
selectedEntities[0] != s_prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(s_editorEntityContextId))
|
||||
if (!onlySelectedEntityIsFocusedPrefabContainer)
|
||||
{
|
||||
bool layerInSelection = false;
|
||||
|
||||
@@ -322,7 +341,7 @@ namespace AzToolsFramework
|
||||
}
|
||||
|
||||
// Instantiate Prefab
|
||||
if (!readOnlyEntityInSelection)
|
||||
if (selectedEntities.size() == 1 && !readOnlyEntityInSelection && !onlySelectedEntityIsClosedPrefabContainer)
|
||||
{
|
||||
QAction* instantiateAction = menu->addAction(QObject::tr("Instantiate Prefab..."));
|
||||
instantiateAction->setToolTip(QObject::tr("Instantiates a prefab file in the scene."));
|
||||
@@ -330,17 +349,15 @@ namespace AzToolsFramework
|
||||
QObject::connect(
|
||||
instantiateAction, &QAction::triggered, instantiateAction, [] { ContextMenu_InstantiatePrefab(); });
|
||||
|
||||
itemWasShown = true;
|
||||
}
|
||||
// Instantiate Procedural Prefab
|
||||
if (AZ::Prefab::ProceduralPrefabAsset::UseProceduralPrefabs())
|
||||
{
|
||||
QAction* action = menu->addAction(QObject::tr("Instantiate Procedural Prefab..."));
|
||||
action->setToolTip(QObject::tr("Instantiates a procedural prefab file in a prefab."));
|
||||
|
||||
// Instantiate Procedural Prefab
|
||||
if (AZ::Prefab::ProceduralPrefabAsset::UseProceduralPrefabs() && !readOnlyEntityInSelection)
|
||||
{
|
||||
QAction* action = menu->addAction(QObject::tr("Instantiate Procedural Prefab..."));
|
||||
action->setToolTip(QObject::tr("Instantiates a procedural prefab file in a prefab."));
|
||||
|
||||
QObject::connect(
|
||||
action, &QAction::triggered, action, [] { ContextMenu_InstantiateProceduralPrefab(); });
|
||||
QObject::connect(
|
||||
action, &QAction::triggered, action, [] { ContextMenu_InstantiateProceduralPrefab(); });
|
||||
}
|
||||
|
||||
itemWasShown = true;
|
||||
}
|
||||
@@ -362,14 +379,34 @@ namespace AzToolsFramework
|
||||
{
|
||||
if (!s_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(selectedEntity))
|
||||
{
|
||||
// Edit Prefab
|
||||
QAction* editAction = menu->addAction(QObject::tr("Open/Edit Prefab"));
|
||||
editAction->setShortcut(QKeySequence(Qt::Key_Plus));
|
||||
editAction->setToolTip(QObject::tr("Edit the prefab in focus mode."));
|
||||
if (s_prefabPublicInterface->IsOwnedByProceduralPrefabInstance(selectedEntity))
|
||||
{
|
||||
// Inspect Prefab
|
||||
QAction* editAction = menu->addAction(QObject::tr("Inspect Procedural Prefab"));
|
||||
editAction->setShortcut(QKeySequence(Qt::Key_Plus));
|
||||
editAction->setToolTip(QObject::tr("See the procedural prefab contents in focus mode."));
|
||||
|
||||
QObject::connect(editAction, &QAction::triggered, editAction, [selectedEntity] {
|
||||
ContextMenu_EditPrefab(selectedEntity);
|
||||
});
|
||||
QObject::connect(
|
||||
editAction, &QAction::triggered, editAction,
|
||||
[selectedEntity]
|
||||
{
|
||||
ContextMenu_EditPrefab(selectedEntity);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
// Edit Prefab
|
||||
QAction* editAction = menu->addAction(QObject::tr("Open/Edit Prefab"));
|
||||
editAction->setShortcut(QKeySequence(Qt::Key_Plus));
|
||||
editAction->setToolTip(QObject::tr("Edit the prefab in focus mode."));
|
||||
|
||||
QObject::connect(
|
||||
editAction, &QAction::triggered, editAction,
|
||||
[selectedEntity]
|
||||
{
|
||||
ContextMenu_EditPrefab(selectedEntity);
|
||||
});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -420,21 +457,17 @@ namespace AzToolsFramework
|
||||
}
|
||||
|
||||
// Detach Prefab
|
||||
if (selectedEntities.size() == 1)
|
||||
if (onlySelectedEntityIsClosedPrefabContainer)
|
||||
{
|
||||
AZ::EntityId selectedEntityId = selectedEntities[0];
|
||||
AZ::EntityId selectedEntityId = selectedEntities.front();
|
||||
|
||||
if (s_prefabPublicInterface->IsInstanceContainerEntity(selectedEntityId) &&
|
||||
selectedEntityId != s_prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(s_editorEntityContextId))
|
||||
{
|
||||
QAction* detachPrefabAction = menu->addAction(QObject::tr("Detach Prefab..."));
|
||||
QObject::connect(
|
||||
detachPrefabAction, &QAction::triggered, detachPrefabAction,
|
||||
[selectedEntityId]
|
||||
{
|
||||
ContextMenu_DetachPrefab(selectedEntityId);
|
||||
});
|
||||
}
|
||||
QAction* detachPrefabAction = menu->addAction(QObject::tr("Detach Prefab..."));
|
||||
QObject::connect(
|
||||
detachPrefabAction, &QAction::triggered, detachPrefabAction,
|
||||
[selectedEntityId]
|
||||
{
|
||||
ContextMenu_DetachPrefab(selectedEntityId);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -75,7 +75,7 @@ namespace AzToolsFramework
|
||||
void PopulateEditorGlobalContextMenu(QMenu* menu, const AZ::Vector2& point, int flags) override;
|
||||
|
||||
// EditorEventsBus overrides ...
|
||||
void OnEscape();
|
||||
void OnEscape() override;
|
||||
|
||||
// EntityOutlinerSourceDropHandlingBus overrides ...
|
||||
void HandleSourceFileType(AZStd::string_view sourceFilePath, AZ::EntityId parentId, AZ::Vector3 position) const override;
|
||||
|
||||
@@ -89,8 +89,8 @@ namespace AzToolsFramework
|
||||
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;
|
||||
rayRequest.m_startWorldPosition = viewportRay.m_origin;
|
||||
rayRequest.m_endWorldPosition = viewportRay.m_origin + viewportRay.m_direction * rayLength;
|
||||
}
|
||||
|
||||
AZ::Vector3 FindClosestPickIntersection(
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
#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>
|
||||
|
||||
@@ -151,13 +150,6 @@ namespace AzToolsFramework
|
||||
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
|
||||
};
|
||||
|
||||
//! A ray projection, originating from a point and extending in a direction specified as a normal.
|
||||
struct ProjectedViewportRay
|
||||
{
|
||||
AZ::Vector3 origin;
|
||||
AZ::Vector3 direction;
|
||||
};
|
||||
|
||||
//! Requests that can be made to the viewport to query and modify its state.
|
||||
class ViewportInteractionRequests
|
||||
{
|
||||
@@ -183,15 +175,6 @@ 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)
|
||||
@@ -225,6 +208,10 @@ namespace AzToolsFramework
|
||||
virtual bool StickySelectEnabled() const = 0;
|
||||
//! Returns the default viewport camera position.
|
||||
virtual AZ::Vector3 DefaultEditorCameraPosition() const = 0;
|
||||
//! Returns if icons are visible in the viewport.
|
||||
virtual bool IconsVisible() const = 0;
|
||||
//! Returns if viewport helpers (additional debug drawing) are visible in the viewport.
|
||||
virtual bool HelpersVisible() const = 0;
|
||||
|
||||
protected:
|
||||
~ViewportSettingsRequests() = default;
|
||||
|
||||
@@ -20,6 +20,8 @@ namespace AzToolsFramework
|
||||
constexpr AZStd::string_view ScaleManipulatorBoxHalfExtentSetting = "/Amazon/Preferences/Editor/Manipulator/ScaleManipulatorBoxHalfExtent";
|
||||
constexpr AZStd::string_view RotationManipulatorRadiusSetting = "/Amazon/Preferences/Editor/Manipulator/RotationManipulatorRadius";
|
||||
constexpr AZStd::string_view ManipulatorViewBaseScaleSetting = "/Amazon/Preferences/Editor/Manipulator/ViewBaseScale";
|
||||
constexpr AZStd::string_view IconsVisibleSetting = "/Amazon/Preferences/Editor/IconsVisible";
|
||||
constexpr AZStd::string_view HelpersVisibleSetting = "/Amazon/Preferences/Editor/HelpersVisible";
|
||||
|
||||
bool FlipManipulatorAxesTowardsView()
|
||||
{
|
||||
@@ -120,4 +122,24 @@ namespace AzToolsFramework
|
||||
{
|
||||
SetRegistry(ManipulatorViewBaseScaleSetting, scale);
|
||||
}
|
||||
|
||||
bool IconsVisible()
|
||||
{
|
||||
return GetRegistry(IconsVisibleSetting, true);
|
||||
}
|
||||
|
||||
void SetIconsVisible(const bool visible)
|
||||
{
|
||||
SetRegistry(IconsVisibleSetting, visible);
|
||||
}
|
||||
|
||||
bool HelpersVisible()
|
||||
{
|
||||
return GetRegistry(HelpersVisibleSetting, true);
|
||||
}
|
||||
|
||||
void SetHelpersVisible(const bool visible)
|
||||
{
|
||||
SetRegistry(HelpersVisibleSetting, visible);
|
||||
}
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
@@ -66,4 +66,10 @@ namespace AzToolsFramework
|
||||
|
||||
float ManipulatorViewBaseScale();
|
||||
void SetManipulatorViewBaseScale(float scale);
|
||||
|
||||
bool IconsVisible();
|
||||
void SetIconsVisible(bool visible);
|
||||
|
||||
bool HelpersVisible();
|
||||
void SetHelpersVisible(bool visible);
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
@@ -49,5 +49,22 @@ namespace AzToolsFramework
|
||||
->Field("MouseEvent", &MouseInteractionEvent::m_mouseEvent)
|
||||
->Field("WheelDelta", &MouseInteractionEvent::m_wheelDelta);
|
||||
}
|
||||
|
||||
MouseInteraction BuildMouseInteraction(
|
||||
const MousePick& mousePick, const MouseButtons buttons, const InteractionId interactionId, const KeyboardModifiers modifiers)
|
||||
{
|
||||
MouseInteraction interaction;
|
||||
interaction.m_mousePick = mousePick;
|
||||
interaction.m_mouseButtons = buttons;
|
||||
interaction.m_interactionId = interactionId;
|
||||
interaction.m_keyboardModifiers = modifiers;
|
||||
return interaction;
|
||||
}
|
||||
|
||||
MouseInteractionEvent BuildMouseInteractionEvent(
|
||||
const MouseInteraction& mouseInteraction, const MouseEvent event, const bool cursorCaptured /*= false*/)
|
||||
{
|
||||
return MouseInteractionEvent(mouseInteraction, event, cursorCaptured);
|
||||
}
|
||||
} // namespace ViewportInteraction
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
@@ -8,11 +8,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "AzFramework/Viewport/ScreenGeometry.h"
|
||||
|
||||
#include <AzCore/Component/EntityId.h>
|
||||
#include <AzCore/Math/Vector2.h>
|
||||
#include <AzCore/Math/Vector3.h>
|
||||
#include <AzFramework/Viewport/CameraState.h>
|
||||
#include <AzFramework/Viewport/ViewportScreen.h>
|
||||
|
||||
#include <QPoint>
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace AZ
|
||||
{
|
||||
class ReflectContext;
|
||||
class SerializeContext;
|
||||
}
|
||||
} // namespace AZ
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
@@ -178,6 +178,12 @@ namespace AzToolsFramework
|
||||
//! @cond
|
||||
AZ_TYPE_INFO(MousePick, "{A69B9562-FC8C-4DE7-9137-0FF867B1513D}");
|
||||
MousePick() = default;
|
||||
MousePick(const AZ::Vector3& rayOrigin, const AZ::Vector3& rayDirection, const AzFramework::ScreenPoint& screenPoint)
|
||||
: m_rayOrigin(rayOrigin)
|
||||
, m_rayDirection(rayDirection)
|
||||
, m_screenCoordinates(screenPoint)
|
||||
{
|
||||
}
|
||||
//! @endcond
|
||||
|
||||
AZ::Vector3 m_rayOrigin = AZ::Vector3::CreateZero(); //!< World space.
|
||||
@@ -249,6 +255,22 @@ namespace AzToolsFramework
|
||||
return mouseInteractionEvent.m_wheelDelta;
|
||||
}
|
||||
|
||||
//! A ray projection, originating from a point and extending in a direction specified as a normal.
|
||||
struct ProjectedViewportRay
|
||||
{
|
||||
AZ::Vector3 m_origin;
|
||||
AZ::Vector3 m_direction;
|
||||
};
|
||||
|
||||
//! 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 ProjectedViewportRay{ rayOrigin, rayDirection };
|
||||
}
|
||||
|
||||
//! Return QPoint from AzFramework::ScreenPoint.
|
||||
inline QPoint QPointFromScreenPoint(const AzFramework::ScreenPoint& screenPoint)
|
||||
{
|
||||
@@ -301,6 +323,27 @@ namespace AzToolsFramework
|
||||
return mouseButtons;
|
||||
}
|
||||
|
||||
//! Build a mouse pick from the specified mouse position and camera state.
|
||||
inline MousePick BuildMousePick(const AzFramework::CameraState& cameraState, const AzFramework::ScreenPoint& screenPoint)
|
||||
{
|
||||
const auto ray = ViewportScreenToWorldRay(cameraState, screenPoint);
|
||||
return MousePick(ray.m_origin, ray.m_direction, screenPoint);
|
||||
}
|
||||
|
||||
//! Create a mouse interaction from the specified pick, buttons, interaction id and keyboard modifiers.
|
||||
MouseInteraction BuildMouseInteraction(
|
||||
const MousePick& mousePick, MouseButtons buttons, InteractionId interactionId, KeyboardModifiers modifiers);
|
||||
|
||||
//! Create a mouse buttons from the specified mouse button.
|
||||
inline MouseButtons BuildMouseButtons(const MouseButton button)
|
||||
{
|
||||
return MouseButtons(aznumeric_cast<AZ::u32>(button));
|
||||
}
|
||||
|
||||
//! Create a mouse interaction event from the specified interaction and event.
|
||||
MouseInteractionEvent BuildMouseInteractionEvent(
|
||||
const MouseInteraction& mouseInteraction, MouseEvent event, bool cursorCaptured = false);
|
||||
|
||||
//! Reflect all viewport related types.
|
||||
void ViewportInteractionReflect(AZ::ReflectContext* context);
|
||||
} // namespace ViewportInteraction
|
||||
|
||||
+108
-61
@@ -52,34 +52,45 @@ AZ_CVAR(
|
||||
AZ::ConsoleFunctorFlags::Null,
|
||||
"Use a lock icon when the cursor is over entities that cannot be interacted with");
|
||||
|
||||
AZ_CVAR(float, ed_iconMinScale, 0.1f, nullptr, AZ::ConsoleFunctorFlags::Null, "Minimum scale for icons in the distance");
|
||||
AZ_CVAR(float, ed_iconMaxScale, 1.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "Maximum scale for icons near the camera");
|
||||
AZ_CVAR(float, ed_iconCloseDist, 3.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "Distance at which icons are at maximum scale");
|
||||
AZ_CVAR(float, ed_iconFarDist, 40.f, nullptr, AZ::ConsoleFunctorFlags::Null, "Distance at which icons are at minimum scale");
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
AZ_CLASS_ALLOCATOR_IMPL(EditorHelpers, AZ::SystemAllocator, 0)
|
||||
|
||||
static const int s_iconSize = 36; // icon display size (in pixels)
|
||||
static const float s_iconMinScale = 0.1f; // minimum scale for icons in the distance
|
||||
static const float s_iconMaxScale = 1.0f; // maximum scale for icons near the camera
|
||||
static const float s_iconCloseDist = 3.f; // distance at which icons are at maximum scale
|
||||
static const float s_iconFarDist = 40.f; // distance at which icons are at minimum scale
|
||||
static const int IconSize = 36; // icon display size (in pixels)
|
||||
|
||||
// helper function to wrap EBus call to check if helpers are being displayed
|
||||
// note: the ['?'] icon in the top right of the editor
|
||||
static bool HelpersVisible()
|
||||
static bool HelpersVisible(const AzFramework::ViewportId viewportId)
|
||||
{
|
||||
bool helpersVisible = false;
|
||||
EditorRequestBus::BroadcastResult(helpersVisible, &EditorRequests::DisplayHelpersVisible);
|
||||
ViewportInteraction::ViewportSettingsRequestBus::EventResult(
|
||||
helpersVisible, viewportId, &ViewportInteraction::ViewportSettingsRequestBus::Events::HelpersVisible);
|
||||
return helpersVisible;
|
||||
}
|
||||
|
||||
// calculate the icon scale based on how far away it is (distanceSq) from a given point
|
||||
// note: this is mostly likely distance from the camera
|
||||
static float GetIconScale(const float distSq)
|
||||
// helper function to wrap EBus call to check if icons are being displayed
|
||||
static bool IconsVisible(const AzFramework::ViewportId viewportId)
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AzToolsFramework);
|
||||
bool iconsVisible = false;
|
||||
ViewportInteraction::ViewportSettingsRequestBus::EventResult(
|
||||
iconsVisible, viewportId, &ViewportInteraction::ViewportSettingsRequestBus::Events::IconsVisible);
|
||||
return iconsVisible;
|
||||
}
|
||||
|
||||
return s_iconMinScale +
|
||||
(s_iconMaxScale - s_iconMinScale) *
|
||||
(1.0f - AZ::GetClamp(AZ::GetMax(0.0f, sqrtf(distSq) - s_iconCloseDist) / s_iconFarDist, 0.0f, 1.0f));
|
||||
float GetIconScale(const float distance)
|
||||
{
|
||||
return ed_iconMinScale +
|
||||
(ed_iconMaxScale - ed_iconMinScale) *
|
||||
(1.0f - AZ::GetClamp(AZ::GetMax(0.0f, distance - ed_iconCloseDist) / (ed_iconFarDist - ed_iconCloseDist), 0.0f, 1.0f));
|
||||
}
|
||||
|
||||
float GetIconSize(const float distance)
|
||||
{
|
||||
return GetIconScale(distance) * IconSize;
|
||||
}
|
||||
|
||||
static void DisplayComponents(
|
||||
@@ -171,11 +182,14 @@ namespace AzToolsFramework
|
||||
|
||||
const int viewportId = mouseInteraction.m_mouseInteraction.m_interactionId.m_viewportId;
|
||||
|
||||
const bool helpersVisible = HelpersVisible();
|
||||
const bool iconsVisible = IconsVisible(viewportId);
|
||||
|
||||
const AZ::Matrix3x4 cameraView = AzFramework::CameraView(cameraState);
|
||||
const AZ::Matrix4x4 cameraProjection = AzFramework::CameraProjection(cameraState);
|
||||
|
||||
// selecting new entities
|
||||
AZ::EntityId entityIdUnderCursor;
|
||||
float closestDistance = std::numeric_limits<float>::max();
|
||||
float closestDistance = AZStd::numeric_limits<float>::max();
|
||||
for (size_t entityCacheIndex = 0; entityCacheIndex < m_entityDataCache->VisibleEntityDataCount(); ++entityCacheIndex)
|
||||
{
|
||||
const AZ::EntityId entityId = m_entityDataCache->GetVisibleEntityId(entityCacheIndex);
|
||||
@@ -185,8 +199,7 @@ namespace AzToolsFramework
|
||||
continue;
|
||||
}
|
||||
|
||||
// 2d screen space selection - did we click an icon
|
||||
if (helpersVisible)
|
||||
if (iconsVisible)
|
||||
{
|
||||
// some components choose to hide their icons (e.g. meshes)
|
||||
// we also do not want to test against icons that may not be showing as they're inside a 'closed' entity container
|
||||
@@ -197,17 +210,23 @@ namespace AzToolsFramework
|
||||
const AZ::Vector3& entityPosition = m_entityDataCache->GetVisibleEntityPosition(entityCacheIndex);
|
||||
|
||||
// selecting based on 2d icon - should only do it when visible and not selected
|
||||
const AzFramework::ScreenPoint screenPosition = AzFramework::WorldToScreen(entityPosition, cameraState);
|
||||
const AZ::Vector3 ndcPoint = AzFramework::WorldToScreenNdc(entityPosition, cameraView, cameraProjection);
|
||||
const AzFramework::ScreenPoint screenPosition =
|
||||
AzFramework::ScreenPointFromNdc(AZ::Vector3ToVector2(ndcPoint), cameraState.m_viewportSize);
|
||||
|
||||
const float distSqFromCamera = cameraState.m_position.GetDistanceSq(entityPosition);
|
||||
const auto iconRange = static_cast<float>(GetIconScale(distSqFromCamera) * s_iconSize * 0.5f);
|
||||
const float distanceFromCamera = cameraState.m_position.GetDistance(entityPosition);
|
||||
const auto iconRange = GetIconSize(distanceFromCamera) * 0.5f;
|
||||
const auto screenCoords = mouseInteraction.m_mouseInteraction.m_mousePick.m_screenCoordinates;
|
||||
|
||||
// 2d screen space selection - did we click an icon
|
||||
if (screenCoords.m_x >= screenPosition.m_x - iconRange && screenCoords.m_x <= screenPosition.m_x + iconRange &&
|
||||
screenCoords.m_y >= screenPosition.m_y - iconRange && screenCoords.m_y <= screenPosition.m_y + iconRange)
|
||||
screenCoords.m_y >= screenPosition.m_y - iconRange && screenCoords.m_y <= screenPosition.m_y + iconRange &&
|
||||
ndcPoint.GetZ() < closestDistance)
|
||||
{
|
||||
// use ndc z value for distance here which is in 0-1 range so will most likely 'win' when it comes to the
|
||||
// distance check (this is what we want as the cursor should always favor icons if they are hovered)
|
||||
closestDistance = ndcPoint.GetZ();
|
||||
entityIdUnderCursor = entityId;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -220,9 +239,14 @@ namespace AzToolsFramework
|
||||
if (AabbIntersectMouseRay(mouseInteraction.m_mouseInteraction, aabb))
|
||||
{
|
||||
// if success, pick against specific component
|
||||
if (PickEntity(entityId, mouseInteraction.m_mouseInteraction, closestDistance, viewportId))
|
||||
float closestBoundDifference = AZStd::numeric_limits<float>::max();
|
||||
if (PickEntity(entityId, mouseInteraction.m_mouseInteraction, closestBoundDifference, viewportId))
|
||||
{
|
||||
entityIdUnderCursor = entityId;
|
||||
if (closestBoundDifference < closestDistance)
|
||||
{
|
||||
closestDistance = closestBoundDifference;
|
||||
entityIdUnderCursor = entityId;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -276,55 +300,78 @@ namespace AzToolsFramework
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AzToolsFramework);
|
||||
|
||||
if (HelpersVisible())
|
||||
const bool iconsVisible = IconsVisible(viewportInfo.m_viewportId);
|
||||
const bool helpersVisible = HelpersVisible(viewportInfo.m_viewportId);
|
||||
|
||||
auto displayCheck = [this](const size_t entityCacheIndex, const AZ::EntityId entityId)
|
||||
{
|
||||
if (!m_entityDataCache->IsVisibleEntityVisible(entityCacheIndex) || !IsSelectableInViewport(entityId))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
if (helpersVisible)
|
||||
{
|
||||
for (size_t entityCacheIndex = 0; entityCacheIndex < m_entityDataCache->VisibleEntityDataCount(); ++entityCacheIndex)
|
||||
{
|
||||
const AZ::EntityId entityId = m_entityDataCache->GetVisibleEntityId(entityCacheIndex);
|
||||
|
||||
if (!m_entityDataCache->IsVisibleEntityVisible(entityCacheIndex) || !IsSelectableInViewport(entityId))
|
||||
if (const AZ::EntityId entityId = m_entityDataCache->GetVisibleEntityId(entityCacheIndex);
|
||||
displayCheck(entityCacheIndex, entityId))
|
||||
{
|
||||
continue;
|
||||
// notify components to display
|
||||
DisplayComponents(entityId, viewportInfo, debugDisplay);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// notify components to display
|
||||
DisplayComponents(entityId, viewportInfo, debugDisplay);
|
||||
if (iconsVisible)
|
||||
{
|
||||
auto editorViewportIconDisplay = EditorViewportIconDisplay::Get();
|
||||
if (!editorViewportIconDisplay)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_entityDataCache->IsVisibleEntityIconHidden(entityCacheIndex) ||
|
||||
(m_entityDataCache->IsVisibleEntitySelected(entityCacheIndex) && !showIconCheck(entityId)))
|
||||
for (size_t entityCacheIndex = 0; entityCacheIndex < m_entityDataCache->VisibleEntityDataCount(); ++entityCacheIndex)
|
||||
{
|
||||
if (const AZ::EntityId entityId = m_entityDataCache->GetVisibleEntityId(entityCacheIndex);
|
||||
displayCheck(entityCacheIndex, entityId))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int iconTextureId = 0;
|
||||
EditorEntityIconComponentRequestBus::EventResult(
|
||||
iconTextureId, entityId, &EditorEntityIconComponentRequests::GetEntityIconTextureId);
|
||||
|
||||
const AZ::Vector3& entityPosition = m_entityDataCache->GetVisibleEntityPosition(entityCacheIndex);
|
||||
const float distSqFromCamera = cameraState.m_position.GetDistanceSq(entityPosition);
|
||||
|
||||
const float iconScale = GetIconScale(distSqFromCamera);
|
||||
const float iconSize = s_iconSize * iconScale;
|
||||
|
||||
using ComponentEntityAccentType = Components::EditorSelectionAccentSystemComponent::ComponentEntityAccentType;
|
||||
const AZ::Color iconHighlight = [this, entityCacheIndex]()
|
||||
{
|
||||
if (m_entityDataCache->IsVisibleEntityLocked(entityCacheIndex))
|
||||
if (m_entityDataCache->IsVisibleEntityIconHidden(entityCacheIndex) ||
|
||||
(m_entityDataCache->IsVisibleEntitySelected(entityCacheIndex) && !showIconCheck(entityId)))
|
||||
{
|
||||
return AZ::Color(AZ::u8(100), AZ::u8(100), AZ::u8(100), AZ::u8(255));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (m_entityDataCache->GetVisibleEntityAccent(entityCacheIndex) == ComponentEntityAccentType::Hover)
|
||||
int iconTextureId = 0;
|
||||
EditorEntityIconComponentRequestBus::EventResult(
|
||||
iconTextureId, entityId, &EditorEntityIconComponentRequests::GetEntityIconTextureId);
|
||||
|
||||
using ComponentEntityAccentType = Components::EditorSelectionAccentSystemComponent::ComponentEntityAccentType;
|
||||
const AZ::Color iconHighlight = [this, entityCacheIndex]()
|
||||
{
|
||||
return AZ::Color(AZ::u8(255), AZ::u8(120), AZ::u8(0), AZ::u8(204));
|
||||
}
|
||||
if (m_entityDataCache->IsVisibleEntityLocked(entityCacheIndex))
|
||||
{
|
||||
return AZ::Color(AZ::u8(100), AZ::u8(100), AZ::u8(100), AZ::u8(255));
|
||||
}
|
||||
|
||||
return AZ::Color(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
}();
|
||||
if (m_entityDataCache->GetVisibleEntityAccent(entityCacheIndex) == ComponentEntityAccentType::Hover)
|
||||
{
|
||||
return AZ::Color(AZ::u8(255), AZ::u8(120), AZ::u8(0), AZ::u8(204));
|
||||
}
|
||||
|
||||
EditorViewportIconDisplay::Get()->DrawIcon({ viewportInfo.m_viewportId, iconTextureId, iconHighlight, entityPosition,
|
||||
EditorViewportIconDisplayInterface::CoordinateSpace::WorldSpace,
|
||||
AZ::Vector2{ iconSize, iconSize } });
|
||||
return AZ::Color(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
}();
|
||||
|
||||
const AZ::Vector3& entityPosition = m_entityDataCache->GetVisibleEntityPosition(entityCacheIndex);
|
||||
const float distanceFromCamera = cameraState.m_position.GetDistance(entityPosition);
|
||||
const float iconSize = GetIconSize(distanceFromCamera);
|
||||
|
||||
editorViewportIconDisplay->DrawIcon({ viewportInfo.m_viewportId, iconTextureId, iconHighlight, entityPosition,
|
||||
EditorViewportIconDisplayInterface::CoordinateSpace::WorldSpace,
|
||||
AZ::Vector2{ iconSize, iconSize } });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,4 +106,12 @@ namespace AzToolsFramework
|
||||
const EditorVisibleEntityDataCache* m_entityDataCache = nullptr; //!< Entity Data queried by the EditorHelpers.
|
||||
const FocusModeInterface* m_focusModeInterface = nullptr; //!< API to interact with focus mode functionality.
|
||||
};
|
||||
|
||||
//! Calculate the icon scale based on how far away it is from a given point.
|
||||
//! @note This is mostly likely distance from the camera.
|
||||
float GetIconScale(float distance);
|
||||
|
||||
//! Calculate the icon size based on how far away it is from a given point.
|
||||
//! @note This is the base icon size multiplied by the icon scale to give a final viewport size.
|
||||
float GetIconSize(float distance);
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
+6
-7
@@ -2521,10 +2521,9 @@ namespace AzToolsFramework
|
||||
AZStd::erase_if(
|
||||
m_selectedEntityIds,
|
||||
[readOnlyEntityPublicInterface](auto entityId)
|
||||
{
|
||||
return readOnlyEntityPublicInterface->IsReadOnly(entityId);
|
||||
}
|
||||
);
|
||||
{
|
||||
return readOnlyEntityPublicInterface->IsReadOnly(entityId);
|
||||
});
|
||||
}
|
||||
|
||||
// note: create/destroy pattern to be addressed
|
||||
@@ -3285,9 +3284,9 @@ namespace AzToolsFramework
|
||||
QObject::connect(
|
||||
action, &QAction::triggered, action,
|
||||
[this]
|
||||
{
|
||||
ToggleCenterPivotSelection();
|
||||
});
|
||||
{
|
||||
ToggleCenterPivotSelection();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
@@ -32,6 +32,8 @@ namespace AzToolsFramework
|
||||
constexpr inline AZ::Crc32 EditReset = AZ_CRC_CE("com.o3de.action.editortransform.editreset");
|
||||
constexpr inline AZ::Crc32 EditResetManipulator = AZ_CRC_CE("com.o3de.action.editortransform.editresetmanipulator");
|
||||
constexpr inline AZ::Crc32 ViewportUiVisible = AZ_CRC_CE("com.o3de.action.editortransform.viewportuivisible");
|
||||
constexpr inline AZ::Crc32 Helpers = AZ_CRC_CE("com.o3de.action.editor.helpers");
|
||||
constexpr inline AZ::Crc32 Icons = AZ_CRC_CE("com.o3de.action.editor.icons");
|
||||
//@}
|
||||
|
||||
//! Provide interface for EditorTransformComponentSelection requests.
|
||||
|
||||
@@ -670,6 +670,9 @@ set(FILES
|
||||
Prefab/PrefabSystemComponent.h
|
||||
Prefab/PrefabSystemComponent.cpp
|
||||
Prefab/PrefabSystemComponentInterface.h
|
||||
Prefab/ProceduralPrefabSystemComponent.h
|
||||
Prefab/ProceduralPrefabSystemComponent.cpp
|
||||
Prefab/ProceduralPrefabSystemComponentInterface.h
|
||||
Prefab/PrefabSystemScriptingBus.h
|
||||
Prefab/PrefabSystemScriptingHandler.h
|
||||
Prefab/PrefabSystemScriptingHandler.cpp
|
||||
|
||||
@@ -40,16 +40,12 @@
|
||||
|
||||
#include <Tests/BoundsTestComponent.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
std::ostream& operator<<(std::ostream& os, const EntityId entityId)
|
||||
{
|
||||
return os << entityId.ToString().c_str();
|
||||
}
|
||||
} // namespace AZ
|
||||
|
||||
namespace UnitTest
|
||||
{
|
||||
using AzToolsFramework::ViewportInteraction::BuildMouseButtons;
|
||||
using AzToolsFramework::ViewportInteraction::BuildMouseInteraction;
|
||||
using AzToolsFramework::ViewportInteraction::BuildMousePick;
|
||||
|
||||
AzToolsFramework::EntityIdList SelectedEntities()
|
||||
{
|
||||
AzToolsFramework::EntityIdList selectedEntitiesBefore;
|
||||
@@ -137,6 +133,18 @@ namespace UnitTest
|
||||
AzToolsFramework::EntityIdList m_entityIds;
|
||||
};
|
||||
|
||||
AZ::EntityId CreateEntityWithBounds(const char* entityName)
|
||||
{
|
||||
AZ::Entity* entity = nullptr;
|
||||
AZ::EntityId entityId = CreateDefaultEditorEntity(entityName, &entity);
|
||||
|
||||
entity->Deactivate();
|
||||
entity->CreateComponent<BoundsTestComponent>();
|
||||
entity->Activate();
|
||||
|
||||
return entityId;
|
||||
}
|
||||
|
||||
class EditorTransformComponentSelectionViewportPickingFixture : public ToolsApplicationFixture
|
||||
{
|
||||
public:
|
||||
@@ -146,21 +154,9 @@ namespace UnitTest
|
||||
// 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");
|
||||
m_entityId1 = CreateEntityWithBounds("Entity1");
|
||||
m_entityId2 = CreateEntityWithBounds("Entity2");
|
||||
m_entityId3 = CreateEntityWithBounds("Entity3");
|
||||
}
|
||||
|
||||
void PositionEntities()
|
||||
@@ -959,6 +955,9 @@ namespace UnitTest
|
||||
|
||||
const auto entity2ScreenPosition = AzFramework::WorldToScreen(AzToolsFramework::GetWorldTranslation(m_entityId2), m_cameraState);
|
||||
|
||||
// ensure icons are not enabled to avoid them interfering with bound detection
|
||||
m_viewportManipulatorInteraction->GetViewportInteraction().SetIconsVisible(false);
|
||||
|
||||
// click the entity in the viewport
|
||||
m_actionDispatcher->SetStickySelect(true)
|
||||
->CameraState(m_cameraState)
|
||||
@@ -974,6 +973,137 @@ namespace UnitTest
|
||||
EXPECT_THAT(selectedEntities, UnorderedElementsAreArray(expectedSelectedEntities));
|
||||
}
|
||||
|
||||
// entity can be selected using icon
|
||||
TEST_F(EditorTransformComponentSelectionViewportPickingManipulatorTestFixture, CursorOverEntityIconReturnsThatEntityId)
|
||||
{
|
||||
const AZ::EntityId boundlessEntityId = CreateDefaultEditorEntity("BoundlessEntity");
|
||||
|
||||
// camera (go to position format) -5.00, -8.00, 5.00, 0.00, 0.00
|
||||
AzFramework::SetCameraTransform(m_cameraState, AZ::Transform::CreateTranslation(AZ::Vector3(-5.0f, -8.0f, 5.0f)));
|
||||
// position entity in the world
|
||||
AZ::TransformBus::Event(boundlessEntityId, &AZ::TransformBus::Events::SetWorldTranslation, AZ::Vector3(-5.0f, -1.0f, 5.0f));
|
||||
|
||||
const float distanceFromCamera = m_cameraState.m_position.GetDistance(AzToolsFramework::GetWorldTranslation(boundlessEntityId));
|
||||
|
||||
const auto quaterIconSize = AzToolsFramework::GetIconSize(distanceFromCamera) * 0.25f;
|
||||
const auto entity1ScreenPosition =
|
||||
AzFramework::WorldToScreen(AzToolsFramework::GetWorldTranslation(boundlessEntityId), m_cameraState) +
|
||||
AzFramework::ScreenVectorFromVector2(AZ::Vector2(quaterIconSize));
|
||||
|
||||
AzToolsFramework::EditorVisibleEntityDataCache editorVisibleEntityDataCache;
|
||||
AzToolsFramework::EditorHelpers editorHelpers(&editorVisibleEntityDataCache);
|
||||
|
||||
const auto viewportId = m_viewportManipulatorInteraction->GetViewportInteraction().GetViewportId();
|
||||
const auto mousePick = BuildMousePick(m_cameraState, entity1ScreenPosition);
|
||||
const auto mouseInteraction = BuildMouseInteraction(
|
||||
mousePick, BuildMouseButtons(AzToolsFramework::ViewportInteraction::MouseButton::None),
|
||||
AzToolsFramework::ViewportInteraction::InteractionId(AZ::EntityId(), viewportId),
|
||||
AzToolsFramework::ViewportInteraction::KeyboardModifiers());
|
||||
const auto mouseInteractionEvent = AzToolsFramework::ViewportInteraction::BuildMouseInteractionEvent(
|
||||
mouseInteraction, AzToolsFramework::ViewportInteraction::MouseEvent::Move, false);
|
||||
|
||||
// mimic mouse move
|
||||
m_actionDispatcher->CameraState(m_cameraState)->MousePosition(entity1ScreenPosition);
|
||||
|
||||
// simulate hovering over an icon in the viewport
|
||||
editorVisibleEntityDataCache.CalculateVisibleEntityDatas(AzFramework::ViewportInfo{ viewportId });
|
||||
auto entityIdUnderCursor = editorHelpers.FindEntityIdUnderCursor(m_cameraState, mouseInteractionEvent);
|
||||
|
||||
using ::testing::Eq;
|
||||
EXPECT_THAT(entityIdUnderCursor.EntityIdUnderCursor(), Eq(boundlessEntityId));
|
||||
}
|
||||
|
||||
// overlapping icons, nearest is detected
|
||||
TEST_F(EditorTransformComponentSelectionViewportPickingManipulatorTestFixture, CursorOverOverlappingEntityIconsReturnsClosestEntityId)
|
||||
{
|
||||
const AZ::EntityId boundlessEntityId1 = CreateDefaultEditorEntity("BoundlessEntity1");
|
||||
const AZ::EntityId boundlessEntityId2 = CreateDefaultEditorEntity("BoundlessEntity2");
|
||||
|
||||
// camera (go to position format) -5.00, -8.00, 5.00, 0.00, 0.00
|
||||
AzFramework::SetCameraTransform(m_cameraState, AZ::Transform::CreateTranslation(AZ::Vector3(-5.0f, -8.0f, 5.0f)));
|
||||
// position entities in the world
|
||||
AZ::TransformBus::Event(boundlessEntityId1, &AZ::TransformBus::Events::SetWorldTranslation, AZ::Vector3(-5.0f, -1.0f, 5.0f));
|
||||
// note: boundlessEntityId2 is closer to the camera
|
||||
AZ::TransformBus::Event(boundlessEntityId2, &AZ::TransformBus::Events::SetWorldTranslation, AZ::Vector3(-5.0f, -3.0f, 5.0f));
|
||||
|
||||
const float distanceFromCamera = m_cameraState.m_position.GetDistance(AzToolsFramework::GetWorldTranslation(boundlessEntityId2));
|
||||
|
||||
const auto quaterIconSize = AzToolsFramework::GetIconSize(distanceFromCamera) * 0.25f;
|
||||
const auto entity2ScreenPosition =
|
||||
AzFramework::WorldToScreen(AzToolsFramework::GetWorldTranslation(boundlessEntityId2), m_cameraState) +
|
||||
AzFramework::ScreenVectorFromVector2(AZ::Vector2(quaterIconSize));
|
||||
|
||||
AzToolsFramework::EditorVisibleEntityDataCache editorVisibleEntityDataCache;
|
||||
AzToolsFramework::EditorHelpers editorHelpers(&editorVisibleEntityDataCache);
|
||||
|
||||
const auto viewportId = m_viewportManipulatorInteraction->GetViewportInteraction().GetViewportId();
|
||||
const auto mousePick = BuildMousePick(m_cameraState, entity2ScreenPosition);
|
||||
const auto mouseInteraction = BuildMouseInteraction(
|
||||
mousePick, BuildMouseButtons(AzToolsFramework::ViewportInteraction::MouseButton::None),
|
||||
AzToolsFramework::ViewportInteraction::InteractionId(AZ::EntityId(), viewportId),
|
||||
AzToolsFramework::ViewportInteraction::KeyboardModifiers());
|
||||
const auto mouseInteractionEvent = AzToolsFramework::ViewportInteraction::BuildMouseInteractionEvent(
|
||||
mouseInteraction, AzToolsFramework::ViewportInteraction::MouseEvent::Move, false);
|
||||
|
||||
// mimic mouse move
|
||||
m_actionDispatcher->CameraState(m_cameraState)->MousePosition(entity2ScreenPosition);
|
||||
|
||||
// simulate hovering over an icon in the viewport
|
||||
editorVisibleEntityDataCache.CalculateVisibleEntityDatas(AzFramework::ViewportInfo{ viewportId });
|
||||
auto entityIdUnderCursor = editorHelpers.FindEntityIdUnderCursor(m_cameraState, mouseInteractionEvent);
|
||||
|
||||
using ::testing::Eq;
|
||||
EXPECT_THAT(entityIdUnderCursor.EntityIdUnderCursor(), Eq(boundlessEntityId2));
|
||||
}
|
||||
|
||||
// if an entity with an icon is behind an entity with a bound, the entity with the icon will be selected
|
||||
// even if the bound is closer (this is because icons are treated as if they are on the near clip plane)
|
||||
TEST_F(
|
||||
EditorTransformComponentSelectionViewportPickingManipulatorTestFixture, FurtherAwayEntityWithIconReturnedWhenBoundEntityIsInFront)
|
||||
{
|
||||
const AZ::EntityId boundEntityId = CreateEntityWithBounds("BoundEntity");
|
||||
const AZ::EntityId boundlessEntityId = CreateDefaultEditorEntity("BoundlessEntity");
|
||||
|
||||
auto* boundTestComponent = AzToolsFramework::GetEntityById(boundEntityId)->FindComponent<BoundsTestComponent>();
|
||||
boundTestComponent->m_localBounds = AZ::Aabb::CreateFromMinMax(AZ::Vector3(-1.5f, -0.5f, -0.5f), AZ::Vector3(1.5f, 0.5, 0.5f));
|
||||
|
||||
// camera (go to position format) -5.00, -8.00, 5.00, 0.00, 0.00
|
||||
AzFramework::SetCameraTransform(m_cameraState, AZ::Transform::CreateTranslation(AZ::Vector3(-5.0f, -8.0f, 5.0f)));
|
||||
// position entities in the world
|
||||
AZ::TransformBus::Event(boundEntityId, &AZ::TransformBus::Events::SetWorldTranslation, AZ::Vector3(-4.0f, -3.0f, 5.0f));
|
||||
// note: boundlessEntityId2 is closer to the camera
|
||||
AZ::TransformBus::Event(boundlessEntityId, &AZ::TransformBus::Events::SetWorldTranslation, AZ::Vector3(-5.0f, -1.0f, 5.0f));
|
||||
|
||||
const float distanceFromCamera = m_cameraState.m_position.GetDistance(AzToolsFramework::GetWorldTranslation(boundlessEntityId));
|
||||
|
||||
const auto quaterIconSize = AzToolsFramework::GetIconSize(distanceFromCamera) * 0.25f;
|
||||
const auto entity2ScreenPosition =
|
||||
AzFramework::WorldToScreen(AzToolsFramework::GetWorldTranslation(boundlessEntityId), m_cameraState) +
|
||||
AzFramework::ScreenVectorFromVector2(AZ::Vector2(quaterIconSize));
|
||||
|
||||
AzToolsFramework::EditorVisibleEntityDataCache editorVisibleEntityDataCache;
|
||||
AzToolsFramework::EditorHelpers editorHelpers(&editorVisibleEntityDataCache);
|
||||
|
||||
const auto viewportId = m_viewportManipulatorInteraction->GetViewportInteraction().GetViewportId();
|
||||
const auto mousePick = BuildMousePick(m_cameraState, entity2ScreenPosition);
|
||||
const auto mouseInteraction = BuildMouseInteraction(
|
||||
mousePick, BuildMouseButtons(AzToolsFramework::ViewportInteraction::MouseButton::None),
|
||||
AzToolsFramework::ViewportInteraction::InteractionId(AZ::EntityId(), viewportId),
|
||||
AzToolsFramework::ViewportInteraction::KeyboardModifiers());
|
||||
const auto mouseInteractionEvent = AzToolsFramework::ViewportInteraction::BuildMouseInteractionEvent(
|
||||
mouseInteraction, AzToolsFramework::ViewportInteraction::MouseEvent::Move, false);
|
||||
|
||||
// mimic mouse move
|
||||
m_actionDispatcher->CameraState(m_cameraState)->MousePosition(entity2ScreenPosition);
|
||||
|
||||
// simulate hovering over an icon in the viewport
|
||||
editorVisibleEntityDataCache.CalculateVisibleEntityDatas(AzFramework::ViewportInfo{ viewportId });
|
||||
auto entityIdUnderCursor = editorHelpers.FindEntityIdUnderCursor(m_cameraState, mouseInteractionEvent);
|
||||
|
||||
using ::testing::Eq;
|
||||
EXPECT_THAT(entityIdUnderCursor.EntityIdUnderCursor(), Eq(boundlessEntityId));
|
||||
}
|
||||
|
||||
class EditorTransformComponentSelectionViewportPickingManipulatorTestFixtureParam
|
||||
: public EditorTransformComponentSelectionViewportPickingManipulatorTestFixture
|
||||
, public ::testing::WithParamInterface<bool>
|
||||
@@ -1709,8 +1839,9 @@ namespace UnitTest
|
||||
using MouseInteractionResult = AzToolsFramework::ViewportInteraction::MouseInteractionResult;
|
||||
|
||||
public:
|
||||
WheelEventWidget(QWidget* parent = nullptr)
|
||||
WheelEventWidget(const AzFramework::ViewportId viewportId, QWidget* parent = nullptr)
|
||||
: QWidget(parent)
|
||||
, m_viewportId(viewportId)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -1719,7 +1850,7 @@ namespace UnitTest
|
||||
namespace vi = AzToolsFramework::ViewportInteraction;
|
||||
vi::MouseInteraction mouseInteraction;
|
||||
mouseInteraction.m_interactionId.m_cameraId = AZ::EntityId();
|
||||
mouseInteraction.m_interactionId.m_viewportId = 0;
|
||||
mouseInteraction.m_interactionId.m_viewportId = m_viewportId;
|
||||
mouseInteraction.m_mouseButtons = vi::BuildMouseButtons(ev->buttons());
|
||||
mouseInteraction.m_mousePick = vi::MousePick();
|
||||
mouseInteraction.m_keyboardModifiers = vi::BuildKeyboardModifiers(ev->modifiers());
|
||||
@@ -1731,15 +1862,15 @@ namespace UnitTest
|
||||
}
|
||||
|
||||
MouseInteractionResult m_mouseInteractionResult;
|
||||
AzFramework::ViewportId m_viewportId;
|
||||
};
|
||||
|
||||
TEST_F(EditorTransformComponentSelectionFixture, MouseScrollWheelSwitchesTransformMode)
|
||||
TEST_F(EditorTransformComponentSelectionManipulatorTestFixture, MouseScrollWheelSwitchesTransformMode)
|
||||
{
|
||||
using ::testing::Eq;
|
||||
namespace vi = AzToolsFramework::ViewportInteraction;
|
||||
using AzToolsFramework::EditorTransformComponentSelectionRequestBus;
|
||||
|
||||
const auto transformMode = []()
|
||||
const auto transformMode = []
|
||||
{
|
||||
EditorTransformComponentSelectionRequestBus::Events::Mode transformMode;
|
||||
EditorTransformComponentSelectionRequestBus::EventResult(
|
||||
@@ -1752,7 +1883,7 @@ namespace UnitTest
|
||||
// preconditions
|
||||
EXPECT_THAT(transformMode(), EditorTransformComponentSelectionRequestBus::Events::Mode::Translation);
|
||||
|
||||
auto wheelEventWidget = WheelEventWidget();
|
||||
auto wheelEventWidget = WheelEventWidget(m_viewportManipulatorInteraction->GetViewportInteraction().GetViewportId());
|
||||
// attach the global event filter to the placeholder widget
|
||||
AzQtComponents::GlobalEventFilter globalEventFilter(QApplication::instance());
|
||||
wheelEventWidget.installEventFilter(&globalEventFilter);
|
||||
@@ -1768,6 +1899,7 @@ namespace UnitTest
|
||||
|
||||
// then
|
||||
// transform mode has changed and mouse event was handled
|
||||
using ::testing::Eq;
|
||||
EXPECT_THAT(transformMode(), Eq(EditorTransformComponentSelectionRequestBus::Events::Mode::Rotation));
|
||||
EXPECT_THAT(wheelEventWidget.m_mouseInteractionResult, Eq(vi::MouseInteractionResult::Viewport));
|
||||
}
|
||||
@@ -3100,7 +3232,7 @@ namespace UnitTest
|
||||
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();
|
||||
const auto distanceAway = (finalEntityTransform.GetTranslation() - viewportRay.m_origin).GetLength();
|
||||
|
||||
// ensure final world positions match
|
||||
EXPECT_THAT(finalEntityTransform, IsCloseTolerance(finalTransformWorld, 0.01f));
|
||||
|
||||
@@ -62,7 +62,6 @@ namespace UnitTest
|
||||
|
||||
void BrowseForAssets(AssetBrowser::AssetSelectionModel& /*selection*/) override {}
|
||||
int GetIconTextureIdFromEntityIconPath(const AZStd::string& entityIconPath) override { AZ_UNUSED(entityIconPath); return 0; }
|
||||
bool DisplayHelpersVisible() override { return false; }
|
||||
|
||||
void GoToSelectedEntitiesInViewports() override
|
||||
{
|
||||
|
||||
@@ -8,12 +8,12 @@
|
||||
|
||||
#include <Tests/FocusMode/EditorFocusModeSelectionFixture.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
namespace UnitTest
|
||||
{
|
||||
TEST_F(EditorFocusModeSelectionFixture, ContainerEntitySelectionTests_FindHighestSelectableEntityWithNoContainers)
|
||||
TEST_F(EditorFocusModeSelectionFixture, ContainerEntitySelectionFindHighestSelectableEntityWithNoContainers)
|
||||
{
|
||||
// When no containers are in the way, the function will just return the entityId of the entity that was clicked.
|
||||
|
||||
|
||||
// Click on Car Entity
|
||||
ClickAtWorldPositionOnViewport(WorldCarEntityPosition);
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace AzToolsFramework
|
||||
EXPECT_EQ(selectedEntitiesAfter.front(), m_entityMap[CarEntityName]);
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeSelectionFixture, ContainerEntitySelectionTests_FindHighestSelectableEntityWithClosedContainer)
|
||||
TEST_F(EditorFocusModeSelectionFixture, ContainerEntitySelectionFindHighestSelectableEntityWithClosedContainer)
|
||||
{
|
||||
// If a closed container is an ancestor of the queried entity, the closed container is selected.
|
||||
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[StreetEntityName]); // Containers are closed by default
|
||||
@@ -40,7 +40,7 @@ namespace AzToolsFramework
|
||||
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[StreetEntityName]);
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeSelectionFixture, ContainerEntitySelectionTests_FindHighestSelectableEntityWithOpenContainer)
|
||||
TEST_F(EditorFocusModeSelectionFixture, ContainerEntitySelectionFindHighestSelectableEntityWithOpenContainer)
|
||||
{
|
||||
// If a closed container is an ancestor of the queried entity, the closed container is selected.
|
||||
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[StreetEntityName]);
|
||||
@@ -58,7 +58,7 @@ namespace AzToolsFramework
|
||||
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[StreetEntityName]);
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeSelectionFixture, ContainerEntitySelectionTests_FindHighestSelectableEntityWithMultipleClosedContainers)
|
||||
TEST_F(EditorFocusModeSelectionFixture, ContainerEntitySelectionFindHighestSelectableEntityWithMultipleClosedContainers)
|
||||
{
|
||||
// If multiple closed containers are ancestors of the queried entity, the highest closed container is selected.
|
||||
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[StreetEntityName]);
|
||||
@@ -77,7 +77,7 @@ namespace AzToolsFramework
|
||||
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[CityEntityName]);
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeSelectionFixture, ContainerEntitySelectionTests_FindHighestSelectableEntityWithMultipleContainers)
|
||||
TEST_F(EditorFocusModeSelectionFixture, ContainerEntitySelectionFindHighestSelectableEntityWithMultipleContainers)
|
||||
{
|
||||
// If multiple containers are ancestors of the queried entity, the highest closed container is selected.
|
||||
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[StreetEntityName]);
|
||||
@@ -96,4 +96,4 @@ namespace AzToolsFramework
|
||||
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[StreetEntityName]);
|
||||
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[CityEntityName]);
|
||||
}
|
||||
}
|
||||
} // namespace UnitTest
|
||||
|
||||
@@ -8,9 +8,9 @@
|
||||
|
||||
#include <Tests/FocusMode/EditorFocusModeFixture.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
namespace UnitTest
|
||||
{
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityTests_Register)
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityRegister)
|
||||
{
|
||||
// Registering an entity is successful.
|
||||
auto outcome = m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[CarEntityName]);
|
||||
@@ -20,7 +20,7 @@ namespace AzToolsFramework
|
||||
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[CarEntityName]);
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityTests_RegisterTwice)
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityRegisterTwice)
|
||||
{
|
||||
// Registering an entity twice fails.
|
||||
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[CarEntityName]);
|
||||
@@ -31,7 +31,7 @@ namespace AzToolsFramework
|
||||
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[CarEntityName]);
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityTests_Unregister)
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityUnregister)
|
||||
{
|
||||
// Unregistering a container entity is successful.
|
||||
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[CarEntityName]);
|
||||
@@ -39,21 +39,21 @@ namespace AzToolsFramework
|
||||
EXPECT_TRUE(outcome.IsSuccess());
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityTests_UnregisterRegularEntity)
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityUnregisterRegularEntity)
|
||||
{
|
||||
// Unregistering an entity that was not previously registered fails.
|
||||
auto outcome = m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[CarEntityName]);
|
||||
EXPECT_FALSE(outcome.IsSuccess());
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityTests_UnregisterTwice)
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityUnregisterTwice)
|
||||
{
|
||||
// Unregistering a container entity twice fails.
|
||||
auto outcome = m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[CarEntityName]);
|
||||
EXPECT_FALSE(outcome.IsSuccess());
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityTests_IsContainerOnRegularEntity)
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityIsContainerOnRegularEntity)
|
||||
{
|
||||
// If a regular entity is passed, IsContainer returns false.
|
||||
// Note that we use a different entity than the tests above to validate a completely new EntityId.
|
||||
@@ -61,7 +61,7 @@ namespace AzToolsFramework
|
||||
EXPECT_FALSE(isContainer);
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityTests_IsContainerOnRegisteredContainer)
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityIsContainerOnRegisteredContainer)
|
||||
{
|
||||
// If a container entity is passed, IsContainer returns true.
|
||||
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[SportsCarEntityName]);
|
||||
@@ -72,7 +72,7 @@ namespace AzToolsFramework
|
||||
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[SportsCarEntityName]);
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityTests_IsContainerOnUnRegisteredContainer)
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityIsContainerOnUnRegisteredContainer)
|
||||
{
|
||||
// If an entity that was previously a container but was then unregistered is passed, IsContainer returns false.
|
||||
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[SportsCarEntityName]);
|
||||
@@ -82,14 +82,14 @@ namespace AzToolsFramework
|
||||
EXPECT_FALSE(isContainer);
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityTests_SetContainerOpenOnRegularEntity)
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntitySetContainerOpenOnRegularEntity)
|
||||
{
|
||||
// Setting a regular entity to open should return a failure.
|
||||
auto outcome = m_containerEntityInterface->SetContainerOpen(m_entityMap[StreetEntityName], true);
|
||||
EXPECT_FALSE(outcome.IsSuccess());
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityTests_SetContainerOpen)
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntitySetContainerOpen)
|
||||
{
|
||||
// Set a container entity to open, and verify the operation was successful.
|
||||
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[StreetEntityName]);
|
||||
@@ -100,7 +100,7 @@ namespace AzToolsFramework
|
||||
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[StreetEntityName]);
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityTests_SetContainerOpenTwice)
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntitySetContainerOpenTwice)
|
||||
{
|
||||
// Set a container entity to open twice, and verify that does not cause a failure (as intended).
|
||||
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[StreetEntityName]);
|
||||
@@ -112,7 +112,7 @@ namespace AzToolsFramework
|
||||
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[StreetEntityName]);
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityTests_SetContainerClosed)
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntitySetContainerClosed)
|
||||
{
|
||||
// Set a container entity to closed, and verify the operation was successful.
|
||||
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[StreetEntityName]);
|
||||
@@ -122,16 +122,16 @@ namespace AzToolsFramework
|
||||
// Restore default state for other tests.
|
||||
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[StreetEntityName]);
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityTests_IsContainerOpenOnRegularEntity)
|
||||
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityIsContainerOpenOnRegularEntity)
|
||||
{
|
||||
// Query open state on a regular entity, and verify it returns true.
|
||||
// Open containers behave exactly as regular entities, so this is the expected return value.
|
||||
bool isOpen = m_containerEntityInterface->IsContainerOpen(m_entityMap[CityEntityName]);
|
||||
EXPECT_TRUE(isOpen);
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityTests_IsContainerOpenOnDefaultContainerEntity)
|
||||
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityIsContainerOpenOnDefaultContainerEntity)
|
||||
{
|
||||
// Query open state on a newly registered container entity, and verify it returns false.
|
||||
// Containers are registered closed by default.
|
||||
@@ -142,8 +142,8 @@ namespace AzToolsFramework
|
||||
// Restore default state for other tests.
|
||||
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[CityEntityName]);
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityTests_IsContainerOpenOnOpenContainerEntity)
|
||||
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityIsContainerOpenOnOpenContainerEntity)
|
||||
{
|
||||
// Query open state on a container entity that was opened, and verify it returns true.
|
||||
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[CityEntityName]);
|
||||
@@ -154,8 +154,8 @@ namespace AzToolsFramework
|
||||
// Restore default state for other tests.
|
||||
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[CityEntityName]);
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityTests_IsContainerOpenOnClosedContainerEntity)
|
||||
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityIsContainerOpenOnClosedContainerEntity)
|
||||
{
|
||||
// Query open state on a container entity that was opened and then closed, and verify it returns false.
|
||||
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[CityEntityName]);
|
||||
@@ -167,8 +167,8 @@ namespace AzToolsFramework
|
||||
// Restore default state for other tests.
|
||||
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[CityEntityName]);
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityTests_ContainerOpenStateIsPreserved)
|
||||
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityContainerOpenStateIsPreserved)
|
||||
{
|
||||
// Register an entity as container, open it, then unregister it.
|
||||
// When the entity is registered again, the open state should be preserved.
|
||||
@@ -184,15 +184,15 @@ namespace AzToolsFramework
|
||||
// Restore default state for other tests.
|
||||
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[CityEntityName]);
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityTests_ClearSucceeds)
|
||||
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityClearSucceeds)
|
||||
{
|
||||
// The Clear function works if no container is registered.
|
||||
auto outcome = m_containerEntityInterface->Clear(m_editorEntityContextId);
|
||||
EXPECT_TRUE(outcome.IsSuccess());
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityTests_ClearFailsIfContainersAreStillRegistered)
|
||||
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityClearFailsIfContainersAreStillRegistered)
|
||||
{
|
||||
// The Clear function fails if a container is registered.
|
||||
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[Passenger1EntityName]);
|
||||
@@ -202,8 +202,8 @@ namespace AzToolsFramework
|
||||
// Restore default state for other tests.
|
||||
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[Passenger1EntityName]);
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityTests_ClearSucceedsIfContainersAreUnregistered)
|
||||
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityClearSucceedsIfContainersAreUnregistered)
|
||||
{
|
||||
// The Clear function fails if a container is registered.
|
||||
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[Passenger1EntityName]);
|
||||
@@ -212,7 +212,7 @@ namespace AzToolsFramework
|
||||
EXPECT_TRUE(outcome.IsSuccess());
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityTests_ClearDeletesPreservedOpenStates)
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityClearDeletesPreservedOpenStates)
|
||||
{
|
||||
// Register an entity as container, open it, unregister it, then call clear.
|
||||
// When the entity is registered again, the open state should not be preserved.
|
||||
@@ -230,14 +230,14 @@ namespace AzToolsFramework
|
||||
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[Passenger1EntityName]);
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityTests_FindHighestSelectableEntityWithNoContainers)
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityFindHighestSelectableEntityWithNoContainers)
|
||||
{
|
||||
// When no containers are in the way, the function will just return the entityId that was passed to it.
|
||||
AZ::EntityId selectedEntityId = m_containerEntityInterface->FindHighestSelectableEntity(m_entityMap[Passenger2EntityName]);
|
||||
EXPECT_EQ(selectedEntityId, m_entityMap[Passenger2EntityName]);
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityTests_FindHighestSelectableEntityWithClosedContainer)
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityFindHighestSelectableEntityWithClosedContainer)
|
||||
{
|
||||
// If a closed container is an ancestor of the queried entity, the closed container is selected.
|
||||
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[SportsCarEntityName]); // Containers are closed by default
|
||||
@@ -248,7 +248,7 @@ namespace AzToolsFramework
|
||||
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[SportsCarEntityName]);
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityTests_FindHighestSelectableEntityWithOpenContainer)
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityFindHighestSelectableEntityWithOpenContainer)
|
||||
{
|
||||
// If an open container is an ancestor of the queried entity, it is ignored.
|
||||
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[SportsCarEntityName]);
|
||||
@@ -261,7 +261,7 @@ namespace AzToolsFramework
|
||||
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[SportsCarEntityName]);
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityTests_FindHighestSelectableEntityWithMultipleClosedContainers)
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityFindHighestSelectableEntityWithMultipleClosedContainers)
|
||||
{
|
||||
// If multiple closed containers are ancestors of the queried entity, the highest closed container is selected.
|
||||
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[StreetEntityName]);
|
||||
@@ -275,7 +275,7 @@ namespace AzToolsFramework
|
||||
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[SportsCarEntityName]);
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityTests_FindHighestSelectableEntityWithMultipleContainers)
|
||||
TEST_F(EditorFocusModeFixture, ContainerEntityFindHighestSelectableEntityWithMultipleContainers)
|
||||
{
|
||||
// If multiple containers are ancestors of the queried entity, the highest closed container is selected.
|
||||
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[StreetEntityName]);
|
||||
@@ -289,5 +289,4 @@ namespace AzToolsFramework
|
||||
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[StreetEntityName]);
|
||||
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[SportsCarEntityName]);
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace UnitTest
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
#include <Tests/BoundsTestComponent.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
namespace UnitTest
|
||||
{
|
||||
void ClearSelectedEntities()
|
||||
{
|
||||
@@ -31,14 +31,14 @@ namespace AzToolsFramework
|
||||
void EditorFocusModeFixture::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
|
||||
// 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_containerEntityInterface = AZ::Interface<ContainerEntityInterface>::Get();
|
||||
m_containerEntityInterface = AZ::Interface<AzToolsFramework::ContainerEntityInterface>::Get();
|
||||
ASSERT_TRUE(m_containerEntityInterface != nullptr);
|
||||
|
||||
m_focusModeInterface = AZ::Interface<FocusModeInterface>::Get();
|
||||
m_focusModeInterface = AZ::Interface<AzToolsFramework::FocusModeInterface>::Get();
|
||||
ASSERT_TRUE(m_focusModeInterface != nullptr);
|
||||
|
||||
// register a simple component implementing BoundsRequestBus and EditorComponentSelectionRequestsBus
|
||||
@@ -68,26 +68,26 @@ namespace AzToolsFramework
|
||||
ClearSelectedEntities();
|
||||
}
|
||||
|
||||
void EditorFocusModeFixture::GenerateTestHierarchy()
|
||||
void EditorFocusModeFixture::GenerateTestHierarchy()
|
||||
{
|
||||
/*
|
||||
* City
|
||||
* |_ Street
|
||||
* |_ Car
|
||||
* | |_ Passenger
|
||||
* |_ SportsCar
|
||||
* |_ Passenger
|
||||
*/
|
||||
* City
|
||||
* |_ Street
|
||||
* |_ Car
|
||||
* | |_ Passenger
|
||||
* |_ SportsCar
|
||||
* |_ Passenger
|
||||
*/
|
||||
|
||||
m_entityMap[CityEntityName] = CreateEditorEntity(CityEntityName, AZ::EntityId());
|
||||
m_entityMap[StreetEntityName] = CreateEditorEntity(StreetEntityName, m_entityMap[CityEntityName]);
|
||||
m_entityMap[CarEntityName] = CreateEditorEntity(CarEntityName, m_entityMap[StreetEntityName]);
|
||||
m_entityMap[Passenger1EntityName] = CreateEditorEntity(Passenger1EntityName, m_entityMap[CarEntityName]);
|
||||
m_entityMap[SportsCarEntityName] = CreateEditorEntity(SportsCarEntityName, m_entityMap[StreetEntityName]);
|
||||
m_entityMap[Passenger2EntityName] = CreateEditorEntity(Passenger2EntityName, m_entityMap[SportsCarEntityName]);
|
||||
m_entityMap[CityEntityName] = CreateEditorEntity(CityEntityName, AZ::EntityId());
|
||||
m_entityMap[StreetEntityName] = CreateEditorEntity(StreetEntityName, m_entityMap[CityEntityName]);
|
||||
m_entityMap[CarEntityName] = CreateEditorEntity(CarEntityName, m_entityMap[StreetEntityName]);
|
||||
m_entityMap[Passenger1EntityName] = CreateEditorEntity(Passenger1EntityName, m_entityMap[CarEntityName]);
|
||||
m_entityMap[SportsCarEntityName] = CreateEditorEntity(SportsCarEntityName, m_entityMap[StreetEntityName]);
|
||||
m_entityMap[Passenger2EntityName] = CreateEditorEntity(Passenger2EntityName, m_entityMap[SportsCarEntityName]);
|
||||
|
||||
// Add a BoundsTestComponent to the Car entity.
|
||||
AZ::Entity* entity = GetEntityById(m_entityMap[CarEntityName]);
|
||||
AZ::Entity* entity = AzToolsFramework::GetEntityById(m_entityMap[CarEntityName]);
|
||||
|
||||
entity->Deactivate();
|
||||
entity->CreateComponent<UnitTest::BoundsTestComponent>();
|
||||
@@ -113,4 +113,4 @@ namespace AzToolsFramework
|
||||
|
||||
return entity->GetId();
|
||||
}
|
||||
}
|
||||
} // namespace UnitTest
|
||||
|
||||
@@ -18,10 +18,9 @@
|
||||
#include <AzToolsFramework/FocusMode/FocusModeInterface.h>
|
||||
#include <AzToolsFramework/UnitTest/AzToolsFrameworkTestHelpers.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
namespace UnitTest
|
||||
{
|
||||
class EditorFocusModeFixture
|
||||
: public UnitTest::ToolsApplicationFixture
|
||||
class EditorFocusModeFixture : public ToolsApplicationFixture
|
||||
{
|
||||
protected:
|
||||
void SetUpEditorFixtureImpl() override;
|
||||
@@ -32,8 +31,8 @@ namespace AzToolsFramework
|
||||
|
||||
AZStd::unordered_map<AZStd::string, AZ::EntityId> m_entityMap;
|
||||
|
||||
ContainerEntityInterface* m_containerEntityInterface = nullptr;
|
||||
FocusModeInterface* m_focusModeInterface = nullptr;
|
||||
AzToolsFramework::ContainerEntityInterface* m_containerEntityInterface = nullptr;
|
||||
AzToolsFramework::FocusModeInterface* m_focusModeInterface = nullptr;
|
||||
|
||||
public:
|
||||
AzToolsFramework::EntityIdList GetSelectedEntities();
|
||||
@@ -53,4 +52,4 @@ namespace AzToolsFramework
|
||||
inline static AZ::Vector3 WorldCarEntityPosition = AZ::Vector3(5.0f, 15.0f, 0.0f);
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace UnitTest
|
||||
|
||||
@@ -26,11 +26,17 @@
|
||||
#include <AzToolsFramework/Manipulators/ManipulatorManager.h>
|
||||
#include <AzToolsFramework/ViewportSelection/EditorVisibleEntityDataCache.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
namespace UnitTest
|
||||
{
|
||||
class EditorFocusModeSelectionFixture : public UnitTest::IndirectCallManipulatorViewportInteractionFixtureMixin<EditorFocusModeFixture>
|
||||
class EditorFocusModeSelectionFixture : public IndirectCallManipulatorViewportInteractionFixtureMixin<EditorFocusModeFixture>
|
||||
{
|
||||
public:
|
||||
void SetUpEditorFixtureImpl() override
|
||||
{
|
||||
IndirectCallManipulatorViewportInteractionFixtureMixin<EditorFocusModeFixture>::SetUpEditorFixtureImpl();
|
||||
m_viewportManipulatorInteraction->GetViewportInteraction().SetIconsVisible(false);
|
||||
}
|
||||
|
||||
void ClickAtWorldPositionOnViewport(const AZ::Vector3& worldPosition)
|
||||
{
|
||||
// Calculate the world position in screen space
|
||||
@@ -40,4 +46,4 @@ namespace AzToolsFramework
|
||||
m_actionDispatcher->CameraState(m_cameraState)->MousePosition(carScreenPosition)->MouseLButtonDown()->MouseLButtonUp();
|
||||
}
|
||||
};
|
||||
} // namespace AzToolsFramework
|
||||
} // namespace UnitTest
|
||||
|
||||
@@ -8,9 +8,9 @@
|
||||
|
||||
#include <Tests/FocusMode/EditorFocusModeSelectionFixture.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
namespace UnitTest
|
||||
{
|
||||
TEST_F(EditorFocusModeSelectionFixture, EditorFocusModeSelectionTests_SelectEntityWithFocusOnLevel)
|
||||
TEST_F(EditorFocusModeSelectionFixture, EditorFocusModeSelectionSelectEntityWithFocusOnLevel)
|
||||
{
|
||||
// Click on Car Entity
|
||||
ClickAtWorldPositionOnViewport(WorldCarEntityPosition);
|
||||
@@ -21,7 +21,7 @@ namespace AzToolsFramework
|
||||
EXPECT_EQ(selectedEntitiesAfter.front(), m_entityMap[CarEntityName]);
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeSelectionFixture, EditorFocusModeSelectionTests_SelectEntityWithFocusOnAncestor)
|
||||
TEST_F(EditorFocusModeSelectionFixture, EditorFocusModeSelectionSelectEntityWithFocusOnAncestor)
|
||||
{
|
||||
// Set the focus on the Street Entity (parent of the test entity)
|
||||
m_focusModeInterface->SetFocusRoot(m_entityMap[StreetEntityName]);
|
||||
@@ -35,7 +35,7 @@ namespace AzToolsFramework
|
||||
EXPECT_EQ(selectedEntitiesAfter.front(), m_entityMap[CarEntityName]);
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeSelectionFixture, EditorFocusModeSelectionTests_SelectEntityWithFocusOnItself)
|
||||
TEST_F(EditorFocusModeSelectionFixture, EditorFocusModeSelectionSelectEntityWithFocusOnItself)
|
||||
{
|
||||
// Set the focus on the Car Entity (test entity)
|
||||
m_focusModeInterface->SetFocusRoot(m_entityMap[CarEntityName]);
|
||||
@@ -49,7 +49,7 @@ namespace AzToolsFramework
|
||||
EXPECT_EQ(selectedEntitiesAfter.front(), m_entityMap[CarEntityName]);
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeSelectionFixture, EditorFocusModeSelectionTests_SelectEntityWithFocusOnSibling)
|
||||
TEST_F(EditorFocusModeSelectionFixture, EditorFocusModeSelectionSelectEntityWithFocusOnSibling)
|
||||
{
|
||||
// Set the focus on the SportsCar Entity (sibling of the test entity)
|
||||
m_focusModeInterface->SetFocusRoot(m_entityMap[SportsCarEntityName]);
|
||||
@@ -62,7 +62,7 @@ namespace AzToolsFramework
|
||||
EXPECT_EQ(selectedEntitiesAfter.size(), 0);
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeSelectionFixture, EditorFocusModeSelectionTests_SelectEntityWithFocusOnDescendant)
|
||||
TEST_F(EditorFocusModeSelectionFixture, EditorFocusModeSelectionSelectEntityWithFocusOnDescendant)
|
||||
{
|
||||
// Set the focus on the Passenger1 Entity (child of the entity)
|
||||
m_focusModeInterface->SetFocusRoot(m_entityMap[Passenger1EntityName]);
|
||||
@@ -74,4 +74,4 @@ namespace AzToolsFramework
|
||||
auto selectedEntitiesAfter = GetSelectedEntities();
|
||||
EXPECT_EQ(selectedEntitiesAfter.size(), 0);
|
||||
}
|
||||
}
|
||||
} // namespace UnitTest
|
||||
|
||||
@@ -8,9 +8,9 @@
|
||||
|
||||
#include <Tests/FocusMode/EditorFocusModeFixture.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
namespace UnitTest
|
||||
{
|
||||
TEST_F(EditorFocusModeFixture, EditorFocusModeTests_SetFocus)
|
||||
TEST_F(EditorFocusModeFixture, SetFocus)
|
||||
{
|
||||
// When an entity is set as the focus root, GetFocusRoot should return its EntityId.
|
||||
m_focusModeInterface->SetFocusRoot(m_entityMap[CarEntityName]);
|
||||
@@ -20,7 +20,7 @@ namespace AzToolsFramework
|
||||
m_focusModeInterface->ClearFocusRoot(m_editorEntityContextId);
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeFixture, EditorFocusModeTests_ClearFocus)
|
||||
TEST_F(EditorFocusModeFixture, ClearFocus)
|
||||
{
|
||||
// Change the value from the default.
|
||||
m_focusModeInterface->SetFocusRoot(m_entityMap[CarEntityName]);
|
||||
@@ -30,7 +30,7 @@ namespace AzToolsFramework
|
||||
EXPECT_EQ(m_focusModeInterface->GetFocusRoot(m_editorEntityContextId), AZ::EntityId());
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeFixture, EditorFocusModeTests_IsInFocusSubTree_AncestorsDescendants)
|
||||
TEST_F(EditorFocusModeFixture, IsInFocusSubTreeAncestorsDescendants)
|
||||
{
|
||||
// When the focus is set to an entity, all its descendants are in the focus subtree while the ancestors aren't.
|
||||
m_focusModeInterface->SetFocusRoot(m_entityMap[StreetEntityName]);
|
||||
@@ -43,7 +43,7 @@ namespace AzToolsFramework
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger2EntityName]), true);
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeFixture, EditorFocusModeTests_IsInFocusSubTree_Siblings)
|
||||
TEST_F(EditorFocusModeFixture, IsInFocusSubTreeSiblings)
|
||||
{
|
||||
// If the root entity has siblings, they are also outside of the focus subtree.
|
||||
m_focusModeInterface->SetFocusRoot(m_entityMap[CarEntityName]);
|
||||
@@ -56,7 +56,7 @@ namespace AzToolsFramework
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger2EntityName]), false);
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeFixture, EditorFocusModeTests_IsInFocusSubTree_Leaf)
|
||||
TEST_F(EditorFocusModeFixture, IsInFocusSubTreeLeaf)
|
||||
{
|
||||
// If the root is a leaf, then the focus subtree will consists of just that entity.
|
||||
m_focusModeInterface->SetFocusRoot(m_entityMap[Passenger2EntityName]);
|
||||
@@ -69,7 +69,7 @@ namespace AzToolsFramework
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger2EntityName]), true);
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeFixture, EditorFocusModeTests_IsInFocusSubTree_Clear)
|
||||
TEST_F(EditorFocusModeFixture, IsInFocusSubTreeClear)
|
||||
{
|
||||
// Change the value from the default.
|
||||
m_focusModeInterface->SetFocusRoot(m_entityMap[StreetEntityName]);
|
||||
@@ -84,4 +84,4 @@ namespace AzToolsFramework
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[SportsCarEntityName]), true);
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger2EntityName]), true);
|
||||
}
|
||||
}
|
||||
} // namespace UnitTest
|
||||
|
||||
+302
@@ -0,0 +1,302 @@
|
||||
/*
|
||||
* 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/IO/FileIO.h>
|
||||
#include <AzCore/IO/Path/PathReflect.h>
|
||||
#include <AzCore/Serialization/Json/JsonSystemComponent.h>
|
||||
#include <AzCore/Serialization/Json/RegistrationContext.h>
|
||||
#include <AzCore/Settings/SettingsRegistryImpl.h>
|
||||
#include <AzFramework/IO/LocalFileIO.h>
|
||||
#include <Prefab/PrefabTestFixture.h>
|
||||
#include <Prefab/ProceduralPrefabSystemComponent.h>
|
||||
#include <Utils/Utils.h>
|
||||
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
|
||||
|
||||
namespace UnitTest
|
||||
{
|
||||
struct ProceduralPrefabSystemComponentTests
|
||||
: AllocatorsTestFixture
|
||||
, AZ::ComponentApplicationBus::Handler
|
||||
{
|
||||
void SetUp() override
|
||||
{
|
||||
TestRunner::Instance().m_suppressOutput = false;
|
||||
TestRunner::Instance().m_suppressPrintf = false;
|
||||
TestRunner::Instance().m_suppressWarnings = false;
|
||||
TestRunner::Instance().m_suppressErrors = false;
|
||||
TestRunner::Instance().m_suppressAsserts = false;
|
||||
|
||||
AllocatorsTestFixture::SetUp();
|
||||
|
||||
AZ::ComponentApplicationBus::Handler::BusConnect();
|
||||
|
||||
ASSERT_TRUE(m_temporaryDirectory.IsValid());
|
||||
|
||||
m_localFileIo = AZStd::make_unique<AZ::IO::LocalFileIO>();
|
||||
|
||||
m_prevIoBase = AZ::IO::FileIOBase::GetInstance();
|
||||
AZ::IO::FileIOBase::SetInstance(nullptr); // Need to clear the previous instance first
|
||||
AZ::IO::FileIOBase::SetInstance(m_localFileIo.get());
|
||||
|
||||
AZ::JsonSystemComponent::Reflect(&m_jsonContext);
|
||||
|
||||
m_settingsRegistry = AZStd::make_unique<AZ::SettingsRegistryImpl>();
|
||||
AZ::SettingsRegistry::Register(m_settingsRegistry.get());
|
||||
|
||||
m_settingsRegistry->Set(AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectPath, m_temporaryDirectory.GetDirectory());
|
||||
|
||||
m_prefabSystem = PrefabSystemComponent::CreateDescriptor();
|
||||
m_procSystem = ProceduralPrefabSystemComponent::CreateDescriptor();
|
||||
|
||||
m_prefabSystem->Reflect(&m_context);
|
||||
m_prefabSystem->Reflect(&m_jsonContext);
|
||||
m_procSystem->Reflect(&m_context);
|
||||
m_procSystem->Reflect(&m_jsonContext);
|
||||
|
||||
AZ::Entity::Reflect(&m_context);
|
||||
AZ::Entity::Reflect(&m_jsonContext);
|
||||
AZ::IO::PathReflect(&m_context);
|
||||
|
||||
m_systemEntity = AZStd::make_unique<AZ::Entity>();
|
||||
m_systemEntity->CreateComponent<PrefabSystemComponent>();
|
||||
m_systemEntity->CreateComponent<ProceduralPrefabSystemComponent>();
|
||||
|
||||
m_systemEntity->Init();
|
||||
m_systemEntity->Activate();
|
||||
|
||||
AZ::Data::AssetManager::Create({});
|
||||
}
|
||||
|
||||
void TearDown() override
|
||||
{
|
||||
AZ::Data::AssetManager::Destroy();
|
||||
|
||||
m_systemEntity->Deactivate();
|
||||
m_systemEntity = nullptr;
|
||||
|
||||
m_jsonContext.EnableRemoveReflection();
|
||||
AZ::JsonSystemComponent::Reflect(&m_jsonContext);
|
||||
m_prefabSystem->Reflect(&m_jsonContext);
|
||||
m_procSystem->Reflect(&m_jsonContext);
|
||||
AZ::Entity::Reflect(&m_jsonContext);
|
||||
m_jsonContext.DisableRemoveReflection();
|
||||
|
||||
AZ::IO::FileIOBase::SetInstance(nullptr); // Clear the previous instance first
|
||||
AZ::IO::FileIOBase::SetInstance(m_prevIoBase);
|
||||
|
||||
m_prevIoBase = nullptr;
|
||||
|
||||
AZ::SettingsRegistry::Unregister(m_settingsRegistry.get());
|
||||
|
||||
AZ::ComponentApplicationBus::Handler::BusDisconnect();
|
||||
AllocatorsTestFixture::TearDown();
|
||||
|
||||
TestRunner::Instance().ResetSuppressionSettingsToDefault();
|
||||
}
|
||||
|
||||
// ComponentApplicationBus
|
||||
AZ::ComponentApplication* GetApplication() override
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void RegisterComponentDescriptor(const AZ::ComponentDescriptor*) override { }
|
||||
void UnregisterComponentDescriptor(const AZ::ComponentDescriptor*) override { }
|
||||
void RegisterEntityAddedEventHandler(AZ::EntityAddedEvent::Handler&) override { }
|
||||
void RegisterEntityRemovedEventHandler(AZ::EntityRemovedEvent::Handler&) override { }
|
||||
void RegisterEntityActivatedEventHandler(AZ::EntityActivatedEvent::Handler&) override { }
|
||||
void RegisterEntityDeactivatedEventHandler(AZ::EntityDeactivatedEvent::Handler&) override { }
|
||||
void SignalEntityActivated(AZ::Entity*) override { }
|
||||
void SignalEntityDeactivated(AZ::Entity*) override { }
|
||||
|
||||
bool AddEntity(AZ::Entity*) override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RemoveEntity(AZ::Entity*) override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DeleteEntity(const AZ::EntityId&) override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
AZ::Entity* FindEntity(const AZ::EntityId&) override
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AZ::SerializeContext* GetSerializeContext() override
|
||||
{
|
||||
return &m_context;
|
||||
}
|
||||
|
||||
AZ::BehaviorContext* GetBehaviorContext() override
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AZ::JsonRegistrationContext* GetJsonRegistrationContext() override
|
||||
{
|
||||
return &m_jsonContext;
|
||||
}
|
||||
|
||||
const char* GetEngineRoot() const override
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const char* GetExecutableFolder() const override
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void EnumerateEntities(const EntityCallback& /*callback*/) override { }
|
||||
void QueryApplicationType(AZ::ApplicationTypeQuery& /*appType*/) const override { }
|
||||
////
|
||||
|
||||
AZ::ComponentDescriptor* m_prefabSystem{};
|
||||
AZ::ComponentDescriptor* m_procSystem{};
|
||||
AZStd::unique_ptr<AZ::SettingsRegistryImpl> m_settingsRegistry;
|
||||
AZ::SerializeContext m_context;
|
||||
AZ::JsonRegistrationContext m_jsonContext;
|
||||
AZStd::unique_ptr<AZ::IO::LocalFileIO> m_localFileIo;
|
||||
ScopedTemporaryDirectory m_temporaryDirectory;
|
||||
AZStd::unique_ptr<AZ::Entity> m_systemEntity;
|
||||
|
||||
AZ::IO::FileIOBase* m_prevIoBase{};
|
||||
};
|
||||
|
||||
struct MockCatalog : AZ::Data::AssetCatalogRequestBus::Handler
|
||||
{
|
||||
static const inline AZ::Data::AssetId TestId{ AZ::Uuid::CreateRandom(), 1234 };
|
||||
|
||||
MockCatalog(AZStd::string testFile)
|
||||
: m_testFile(AZStd::move(testFile))
|
||||
{
|
||||
BusConnect();
|
||||
}
|
||||
|
||||
~MockCatalog() override
|
||||
{
|
||||
BusDisconnect();
|
||||
}
|
||||
|
||||
AZStd::string GetAssetPathById(const AZ::Data::AssetId& assetId) override
|
||||
{
|
||||
if (assetId == TestId)
|
||||
{
|
||||
return m_testFile;
|
||||
}
|
||||
|
||||
return "InvalidAssetId";
|
||||
}
|
||||
|
||||
AZ::Data::AssetId GetAssetIdByPath(const char* path, const AZ::Data::AssetType&, bool) override
|
||||
{
|
||||
AZ::IO::PathView pathView{ AZStd::string_view(path) };
|
||||
|
||||
if (AZ::IO::PathView(m_testFile) == pathView)
|
||||
{
|
||||
return TestId;
|
||||
}
|
||||
|
||||
AZ_Error("MockCatalog", false, "Requested path %s does not match expected asset path of %s", path, m_testFile.c_str());
|
||||
ADD_FAILURE();
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
AZStd::string m_testFile;
|
||||
};
|
||||
|
||||
struct PrefabPublicNotificationsListener : PrefabPublicNotificationBus::Handler
|
||||
{
|
||||
PrefabPublicNotificationsListener()
|
||||
{
|
||||
BusConnect();
|
||||
}
|
||||
|
||||
~PrefabPublicNotificationsListener() override
|
||||
{
|
||||
BusDisconnect();
|
||||
}
|
||||
|
||||
void OnPrefabInstancePropagationBegin() override
|
||||
{
|
||||
m_updated = true;
|
||||
}
|
||||
|
||||
bool m_updated = false;
|
||||
};
|
||||
|
||||
TEST_F(ProceduralPrefabSystemComponentTests, RegisteredPrefabUpdates)
|
||||
{
|
||||
const AZStd::string prefabFile = (AZ::IO::Path(m_temporaryDirectory.GetDirectory()) / "test.prefab").Native();
|
||||
MockCatalog catalog(prefabFile.c_str());
|
||||
|
||||
auto proceduralPrefabSystemComponentInterface = AZ::Interface<ProceduralPrefabSystemComponentInterface>::Get();
|
||||
auto prefabSystemComponentInterface = AZ::Interface<PrefabSystemComponentInterface>::Get();
|
||||
auto prefabLoaderInterface = AZ::Interface<PrefabLoaderInterface>::Get();
|
||||
|
||||
ASSERT_NE(proceduralPrefabSystemComponentInterface, nullptr);
|
||||
ASSERT_NE(prefabSystemComponentInterface, nullptr);
|
||||
ASSERT_NE(prefabLoaderInterface, nullptr);
|
||||
|
||||
auto entity = aznew AZ::Entity();
|
||||
|
||||
AZStd::unique_ptr<Instance> instance = prefabSystemComponentInterface->CreatePrefab({ entity }, {}, prefabFile.c_str());
|
||||
|
||||
ASSERT_NE(instance, nullptr);
|
||||
|
||||
prefabLoaderInterface->SaveTemplateToFile(instance->GetTemplateId(), prefabFile.c_str());
|
||||
|
||||
proceduralPrefabSystemComponentInterface->RegisterProceduralPrefab(prefabFile, instance->GetTemplateId());
|
||||
|
||||
AzFramework::AssetCatalogEventBus::Broadcast(&AzFramework::AssetCatalogEventBus::Events::OnCatalogAssetChanged, MockCatalog::TestId);
|
||||
|
||||
PrefabPublicNotificationsListener listener;
|
||||
AZ::SystemTickBus::Broadcast(&AZ::SystemTickBus::Events::OnSystemTick);
|
||||
|
||||
EXPECT_TRUE(listener.m_updated);
|
||||
}
|
||||
|
||||
TEST_F(ProceduralPrefabSystemComponentTests, UnregisteredPrefabDoesNotUpdate)
|
||||
{
|
||||
PrefabPublicNotificationsListener listener;
|
||||
|
||||
const AZStd::string prefabFile = (AZ::IO::Path(m_temporaryDirectory.GetDirectory()) / "test.prefab").Native();
|
||||
MockCatalog catalog(prefabFile.c_str());
|
||||
|
||||
auto proceduralPrefabSystemComponentInterface = AZ::Interface<ProceduralPrefabSystemComponentInterface>::Get();
|
||||
auto prefabSystemComponentInterface = AZ::Interface<PrefabSystemComponentInterface>::Get();
|
||||
auto prefabLoaderInterface = AZ::Interface<PrefabLoaderInterface>::Get();
|
||||
|
||||
ASSERT_NE(proceduralPrefabSystemComponentInterface, nullptr);
|
||||
ASSERT_NE(prefabSystemComponentInterface, nullptr);
|
||||
ASSERT_NE(prefabLoaderInterface, nullptr);
|
||||
|
||||
auto entity = aznew AZ::Entity();
|
||||
|
||||
AZStd::unique_ptr<Instance> instance = prefabSystemComponentInterface->CreatePrefab({ entity }, {}, prefabFile.c_str());
|
||||
|
||||
ASSERT_NE(instance, nullptr);
|
||||
|
||||
prefabLoaderInterface->SaveTemplateToFile(instance->GetTemplateId(), prefabFile.c_str());
|
||||
|
||||
AzFramework::AssetCatalogEventBus::Broadcast(
|
||||
&AzFramework::AssetCatalogEventBus::Events::OnCatalogAssetChanged, MockCatalog::TestId);
|
||||
|
||||
AZ::SystemTickBus::Broadcast(&AZ::SystemTickBus::Events::OnSystemTick);
|
||||
|
||||
EXPECT_FALSE(listener.m_updated);
|
||||
}
|
||||
}
|
||||
-1
@@ -137,7 +137,6 @@ namespace UnitTest
|
||||
void CreateEditorRepresentation(AZ::Entity* entity) override;
|
||||
void BrowseForAssets(AzToolsFramework::AssetBrowser::AssetSelectionModel& selection) override { AZ_UNUSED(selection); }
|
||||
int GetIconTextureIdFromEntityIconPath(const AZStd::string& entityIconPath) override { AZ_UNUSED(entityIconPath); return 0; }
|
||||
bool DisplayHelpersVisible() override { return false; }
|
||||
|
||||
/*
|
||||
* AssetSystemRequestBus
|
||||
|
||||
@@ -243,7 +243,6 @@ namespace UnitTest
|
||||
// These are required by implementing the EditorRequestBus
|
||||
void BrowseForAssets(AssetBrowser::AssetSelectionModel& /*selection*/) override {}
|
||||
int GetIconTextureIdFromEntityIconPath([[maybe_unused]] const AZStd::string& entityIconPath) override { return 0; }
|
||||
bool DisplayHelpersVisible() override { return false; }
|
||||
|
||||
public:
|
||||
EntityPropertyEditor* m_levelEditor;
|
||||
|
||||
@@ -105,6 +105,7 @@ set(FILES
|
||||
Prefab/SpawnableSortEntitiesTests.cpp
|
||||
Prefab/PrefabScriptingTests.cpp
|
||||
Prefab/ProceduralPrefabAssetTests.cpp
|
||||
Prefab/ProceduralPrefabSystemComponentTests.cpp
|
||||
PropertyIntCtrlCommonTests.cpp
|
||||
PropertyIntCtrlCommonTests.h
|
||||
PropertyIntSliderCtrlTests.cpp
|
||||
|
||||
Reference in New Issue
Block a user