Merge branch 'development' into Atom/mriegger/stopdecalflickering

Signed-off-by: mrieggeramzn <mriegger@amazon.com>
This commit is contained in:
mrieggeramzn
2021-09-30 11:04:20 -07:00
235 changed files with 6328 additions and 5660 deletions
+1 -1
View File
@@ -44,7 +44,7 @@ enum
{
// in milliseconds
GameModeIdleFrequency = 0,
EditorModeIdleFrequency = 0,
EditorModeIdleFrequency = 1,
InactiveModeFrequency = 10,
UninitializedFrequency = 9999,
};
@@ -175,27 +175,15 @@ namespace SandboxEditor
m_pivotCamera = AZStd::make_shared<AzFramework::PivotCameraInput>(SandboxEditor::CameraPivotChannelId());
m_pivotCamera->SetPivotFn(
[viewportId = m_viewportId]([[maybe_unused]] const AZ::Vector3& position, [[maybe_unused]] const AZ::Vector3& direction)
[]([[maybe_unused]] const AZ::Vector3& position, [[maybe_unused]] const AZ::Vector3& direction)
{
AZStd::optional<AZ::Vector3> lookAtAfterInterpolation;
AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult(
lookAtAfterInterpolation, viewportId,
&AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::LookAtAfterInterpolation);
// initially attempt to use the last set look at point after an interpolation has finished
// note: ignore this if it is the same location as the camera (e.g. after go to position)
if (lookAtAfterInterpolation.has_value() && !lookAtAfterInterpolation->IsClose(position))
{
return *lookAtAfterInterpolation;
}
// otherwise fall back to the selected entity pivot
// use the manipulator transform as the pivot point
AZStd::optional<AZ::Transform> entityPivot;
AzToolsFramework::EditorTransformComponentSelectionRequestBus::EventResult(
entityPivot, AzToolsFramework::GetEntityContextId(),
&AzToolsFramework::EditorTransformComponentSelectionRequestBus::Events::GetManipulatorTransform);
// finally just use the identity
// otherwise just use the identity
return entityPivot.value_or(AZ::Transform::CreateIdentity()).GetTranslation();
});
+1 -1
View File
@@ -48,7 +48,7 @@ namespace SandboxEditor
{
AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event(
viewportContext->GetId(), &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::InterpolateToTransform,
AZ::Transform::CreateFromQuaternionAndTranslation(CameraRotation(pitch, yaw), position), 0.0f);
AZ::Transform::CreateFromQuaternionAndTranslation(CameraRotation(pitch, yaw), position));
}
}
@@ -167,7 +167,7 @@ namespace UnitTest
AZ::Quaternion::CreateRotationZ(AZ::DegToRad(90.0f)), AZ::Vector3(20.0f, 40.0f, 60.0f));
AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event(
TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::InterpolateToTransform,
transformToInterpolateTo, 0.0f);
transformToInterpolateTo);
// simulate interpolation
m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(0.5f), AZ::ScriptTimePoint() });
@@ -193,7 +193,7 @@ namespace UnitTest
// When
AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event(
TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::InterpolateToTransform,
transformToInterpolateTo, 0.0f);
transformToInterpolateTo);
// simulate interpolation
m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(0.5f), AZ::ScriptTimePoint() });
@@ -1739,8 +1739,7 @@ void SandboxIntegrationManager::GoToEntitiesInViewports(const AzToolsFramework::
AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event(
viewportContext->GetId(),
&AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::InterpolateToTransform, nextCameraTransform,
distanceToLookAt);
&AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::InterpolateToTransform, nextCameraTransform);
}
}
}
@@ -36,9 +36,6 @@
#include "CryEdit.h"
#include "Viewport.h"
// Atom Renderer
#include <Atom/RPI.Public/RPISystemInterface.h>
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
#include <TrackView/ui_SequenceBatchRenderDialog.h>
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
@@ -1237,13 +1234,6 @@ void CSequenceBatchRenderDialog::OnKickIdleTimout()
{
componentApplication->TickSystem();
}
// Directly tick the renderer, as it's no longer part of the system tick
if (auto rpiSystem = AZ::RPI::RPISystemInterface::Get())
{
rpiSystem->SimulationTick();
rpiSystem->RenderTick();
}
}
}
@@ -340,6 +340,14 @@ namespace AZ
// (Load jobs will attempt to reuse blocked threads before spinning off new job threads)
ProcessLoadJob();
}
// Pump the AssetBus function queue once more after the load has completed in case additional
// functions have been queued between the last call to DispatchEvents and the completion
// of the current load job
if (m_shouldDispatchEvents)
{
AssetManager::Instance().DispatchEvents();
}
}
void Finish()
@@ -22,6 +22,7 @@
#include <AzCore/Memory/Memory.h>
#include <AzCore/Memory/SystemAllocator.h> // Used as the allocator for most components.
#include <AzCore/Outcome/Outcome.h>
#include <AzCore/std/containers/unordered_set.h>
namespace AZ
{
@@ -74,8 +74,6 @@
#include <AzCore/Module/Environment.h>
#include <AzCore/std/string/conversions.h>
AZ_CVAR(float, g_simulation_tick_rate, 0, nullptr, AZ::ConsoleFunctorFlags::Null, "The rate at which the game simulation tick loop runs, or 0 for as fast as possible");
static void PrintEntityName(const AZ::ConsoleCommandContainer& arguments)
{
if (arguments.empty())
@@ -1396,23 +1394,6 @@ namespace AZ
AZ_PROFILE_SCOPE(AzCore, "ComponentApplication::Tick:OnTick");
EBUS_EVENT(TickBus, OnTick, m_deltaTime, ScriptTimePoint(now));
}
// If tick rate limiting is on, ensure (1 / g_simulation_tick_rate) ms has elapsed since the last frame,
// sleeping if there's still time remaining.
if (g_simulation_tick_rate > 0.f)
{
now = AZStd::chrono::system_clock::now();
// Work in microsecond durations here as that's the native measurement time for time_point
constexpr float microsecondsPerSecond = 1000.f * 1000.f;
const AZStd::chrono::microseconds timeBudgetPerTick(static_cast<int>(microsecondsPerSecond / g_simulation_tick_rate));
AZStd::chrono::microseconds timeUntilNextTick = m_currentTime + timeBudgetPerTick - now;
if (timeUntilNextTick.count() > 0)
{
AZStd::this_thread::sleep_for(timeUntilNextTick);
}
}
}
}
@@ -46,8 +46,6 @@ namespace AZ
TICK_PRE_RENDER = 750, ///< Suggested tick handler position to update render-related data.
TICK_RENDER = 800, ///< Suggested tick handler position for rendering.
TICK_DEFAULT = 1000, ///< Default tick handler position when the handler is constructed.
TICK_UI = 2000, ///< Suggested tick handler position for UI components.
@@ -8,6 +8,7 @@
#pragma once
#include <AzCore/Memory/OSAllocator.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzCore/EBus/Policies.h>
+7 -10
View File
@@ -19,14 +19,11 @@
#pragma once
#include <AzCore/EBus/BusImpl.h>
#include <AzCore/EBus/Environment.h>
#include <AzCore/EBus/Results.h>
#include <AzCore/EBus/Internal/Debug.h>
// Included for backwards compatibility purposes
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzCore/std/containers/unordered_set.h>
#include <AzCore/std/typetraits/is_same.h>
// End backwards compat
#include <AzCore/std/utils.h>
#include <AzCore/std/parallel/scoped_lock.h>
@@ -90,14 +87,14 @@ namespace AZ
* For available settings, see AZ::EBusHandlerPolicy.
* By default, an EBus supports any number of handlers.
*/
static const EBusHandlerPolicy HandlerPolicy = EBusHandlerPolicy::Multiple;
static constexpr EBusHandlerPolicy HandlerPolicy = EBusHandlerPolicy::Multiple;
/**
* Defines how many addresses exist on the EBus.
* For available settings, see AZ::EBusAddressPolicy.
* By default, an EBus uses a single address.
*/
static const EBusAddressPolicy AddressPolicy = EBusAddressPolicy::Single;
static constexpr EBusAddressPolicy AddressPolicy = EBusAddressPolicy::Single;
/**
* The type of ID that is used to address the EBus.
@@ -152,14 +149,14 @@ namespace AZ
* `<BusName>::ExecuteQueuedEvents()`.
* By default, the event queue is disabled.
*/
static const bool EnableEventQueue = false;
static constexpr bool EnableEventQueue = false;
/**
* Specifies whether the bus should accept queued messages by default or not.
* If set to false, Bus::AllowFunctionQueuing(true) must be called before events are accepted.
* Used only when #EnableEventQueue is true.
*/
static const bool EventQueueingActiveByDefault = true;
static constexpr bool EventQueueingActiveByDefault = true;
/**
* Specifies whether the EBus supports queueing functions which take reference
@@ -168,7 +165,7 @@ namespace AZ
* You should only use this if you know that the data being passed as arguments will
* outlive the dispatch of the queued event.
*/
static const bool EnableQueuedReferences = false;
static constexpr bool EnableQueuedReferences = false;
/**
* Locking primitive that is used when adding and removing
@@ -197,7 +194,7 @@ namespace AZ
* to do.
* By default, the standard policy is used, which locks around all dispatches
*/
static const bool LocklessDispatch = false;
static constexpr bool LocklessDispatch = false;
/**
* Specifies where EBus data is stored.
@@ -13,6 +13,7 @@
#include <AzCore/EBus/EBus.h>
#include <AzCore/RTTI/RTTI.h>
#include <AzCore/RTTI/TypeSafeIntegral.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
namespace AZ
{
+14 -23
View File
@@ -18,9 +18,8 @@
#include <AzCore/std/function/invoke.h>
#include <AzCore/std/containers/queue.h>
#include <AzCore/std/containers/intrusive_set.h>
#include <AzCore/std/parallel/scoped_lock.h>
#include <AzCore/Module/Environment.h>
#include <AzCore/EBus/Environment.h>
namespace AZ
{
@@ -251,29 +250,21 @@ namespace AZ
void Execute()
{
AZ_Warning("System", m_isActive, "You are calling execute queued functions on a bus which has not activated its function queuing! Call YourBus::AllowFunctionQueuing(true)!");
while (true)
MessageQueueType localMessages;
// Swap the current list of queue functions with a local instance
{
BusMessageCall invoke;
AZStd::scoped_lock lock(m_messagesMutex);
AZStd::swap(localMessages, m_messages);
}
//////////////////////////////////////////////////////////////////////////
// Pop element from the queue.
{
AZStd::lock_guard<MutexType> lock(m_messagesMutex);
size_t numMessages = m_messages.size();
if (numMessages == 0)
{
break;
}
AZStd::swap(invoke, m_messages.front());
m_messages.pop();
if (numMessages == 1)
{
m_messages = {};
}
}
//////////////////////////////////////////////////////////////////////////
invoke();
// Execute the queue functions safely now that are owned by the function
while (!localMessages.empty())
{
const BusMessageCall& localMessage = localMessages.front();
localMessage();
localMessages.pop();
}
}
@@ -14,6 +14,7 @@
#include <AzCore/RTTI/ReflectContext.h>
#include <AzCore/std/containers/array.h>
#include <AzCore/std/containers/unordered_map.h>
#include <AzCore/std/containers/unordered_set.h>
#include <AzCore/std/function/invoke.h>
#include <AzCore/std/string/string.h>
#include <AzCore/std/string/string_view.h>
@@ -366,6 +366,42 @@ namespace UnitTest
};
static constexpr AZStd::chrono::seconds MaxDispatchTimeoutSeconds = BaseAssetManagerTest::DefaultTimeoutSeconds * 12;
template <typename Pred>
bool DispatchEventsUntilCondition(AZ::Data::AssetManager& assetManager, Pred&& conditionPredicate,
AZStd::chrono::seconds logIntervalSeconds = BaseAssetManagerTest::DefaultTimeoutSeconds,
AZStd::chrono::seconds maxTimeoutSeconds = MaxDispatchTimeoutSeconds)
{
// If the Max Timeout is hit the test will be marked as a failure
AZStd::chrono::time_point dispatchEventTimeStart = AZStd::chrono::system_clock::now();
AZStd::chrono::seconds dispatchEventNextLogTime = logIntervalSeconds;
while (!conditionPredicate())
{
AZStd::chrono::time_point currentTime = AZStd::chrono::system_clock::now();
if (AZStd::chrono::seconds elapsedTime{ currentTime - dispatchEventTimeStart };
elapsedTime >= dispatchEventNextLogTime)
{
const testing::TestInfo* test_info = ::testing::UnitTest::GetInstance()->current_test_info();
AZ_Printf("AssetManagerLoadingTest", "The DispatchEventsUntiTimeout function has been waiting for %llu seconds"
" in test %s.%s", elapsedTime.count(), test_info->test_case_name(), test_info->name());
// Update the next log time to be the next multiple of DefaultTimeout Seconds
// after current elapsed time
dispatchEventNextLogTime = elapsedTime + logIntervalSeconds - ((elapsedTime + logIntervalSeconds) % logIntervalSeconds);
if (elapsedTime >= maxTimeoutSeconds)
{
return false;
}
}
assetManager.DispatchEvents();
AZStd::this_thread::yield();
}
return true;
}
#if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS || AZ_TRAIT_DISABLE_ASSET_MANAGER_FLOOD_TEST
TEST_F(AssetJobsFloodTest, DISABLED_FloodTest)
#else
@@ -1358,42 +1394,74 @@ namespace UnitTest
m_assetHandlerAndCatalog->m_numCreations = 0;
m_assetHandlerAndCatalog->m_numDestructions = 0;
{
ContainerReadyListener containerLoadingCompleteListener(NoLoadAssetId);
OnAssetReadyListener readyListener(NoLoadAssetId, azrtti_typeid<AssetWithAssetReference>());
OnAssetReadyListener depenencyListener(MyAsset2Id, azrtti_typeid<AssetWithAssetReference>());
OnAssetReadyListener dependencyListener(MyAsset2Id, azrtti_typeid<AssetWithAssetReference>());
SCOPED_TRACE("LoadDependencies_BehaviorObeyed");
auto AssetOnlyReady = [&readyListener]() -> bool
{
return readyListener.m_ready;
};
auto AssetAndDependencyReady = [&readyListener, &dependencyListener]() -> bool
{
return readyListener.m_ready && dependencyListener.m_ready;
};
auto AssetContainerReady = [&containerLoadingCompleteListener]() -> bool
{
return containerLoadingCompleteListener.m_ready;
};
auto noLoadRef = m_testAssetManager->GetAsset(NoLoadAssetId, azrtti_typeid<AssetWithAssetReference>(),
AZ::Data::AssetLoadBehavior::Default);
auto maxTimeout = AZStd::chrono::system_clock::now() + DefaultTimeoutSeconds;
// Dispatch AssetBus events until the NoLoadAssetId has signaled an OnAssetReady
// event or the timeout has been reached
EXPECT_TRUE(DispatchEventsUntilCondition(*m_testAssetManager, AssetOnlyReady))
<< "The DispatchEventsUntiTimeout function has not completed in "
<< MaxDispatchTimeoutSeconds.count() << " seconds. The test will be marked as a failure\n";
// Dispatch AssetBus events until the asset container used to load
// NoLoadAssetId has signaled an OnAssetContainerReady event
// or the timeout has been reached
// Wait until the current asset container has finished loading the NoLoadAssetId
// before trigger another load
// If the wait does not occur here, most likely what would occur is
// the AssetManager::m_ownedAssetContainers object is still loading the NoLoadAssetId
// using the default AssetLoadParameters
// If a call to GetAsset occurs at this point while the Asset is still loading
// it will ignore the new loadParams below and instead just re-use the existing
// AssetContainerReader instance, resulting in the dependent MyAsset2Id not
// being loaded
// The function that can return an existing AssetContainer instance is the
// AssetManager::GetAssetContainer. Since it can be in the middle of a load,
// updating the AssetLoadParams would have an effect on the current in progress
// load
EXPECT_TRUE(DispatchEventsUntilCondition(*m_testAssetManager, AssetContainerReady))
<< "The DispatchEventsUntiTimeout function has not completed in "
<< MaxDispatchTimeoutSeconds.count() << " seconds. The test will be marked as a failure\n";
// Reset the ContainerLoadingComplete ready status back to 0
containerLoadingCompleteListener.m_ready = 0;
while (!readyListener.m_ready)
{
m_testAssetManager->DispatchEvents();
if (AZStd::chrono::system_clock::now() > maxTimeout)
{
break;
}
AZStd::this_thread::yield();
}
EXPECT_EQ(readyListener.m_ready, 1);
EXPECT_EQ(depenencyListener.m_ready, 0);
AZ::Data::AssetLoadParameters loadParams(nullptr, AZ::Data::AssetDependencyLoadRules::LoadAll);
loadParams.m_reloadMissingDependencies = true;
auto loadDependencyRef = m_testAssetManager->GetAsset(NoLoadAssetId, azrtti_typeid<AssetWithAssetReference>(),
AZ::Data::AssetLoadBehavior::Default, loadParams);
while (!depenencyListener.m_ready || !readyListener.m_ready)
{
m_testAssetManager->DispatchEvents();
if (AZStd::chrono::system_clock::now() > maxTimeout)
{
break;
}
AZStd::this_thread::yield();
}
// Dispatch AssetBus events until the NoLoadAssetId and the MyAsset2Id has signaled
// an OnAssetReady event or the timeout has been reached
EXPECT_TRUE(DispatchEventsUntilCondition(*m_testAssetManager, AssetAndDependencyReady))
<< "The DispatchEventsUntiTimeout function has not completed in "
<< MaxDispatchTimeoutSeconds.count() << " seconds. The test will be marked as a failure\n";
EXPECT_EQ(readyListener.m_ready, 1);
EXPECT_EQ(depenencyListener.m_ready, 1);
EXPECT_EQ(dependencyListener.m_ready, 1);
EXPECT_TRUE(DispatchEventsUntilCondition(*m_testAssetManager, AssetContainerReady))
<< "The DispatchEventsUntiTimeout function has not completed in "
<< MaxDispatchTimeoutSeconds.count() << " seconds. The test will be marked as a failure\n";
}
CheckFinishedCreationsAndDestructions();
@@ -7,6 +7,7 @@
*/
#include <AzCore/UnitTest/TestTypes.h>
#include <AzCore/Math/Uuid.h>
#include <AzCore/std/containers/unordered_set.h>
using namespace AZ;
@@ -18,6 +18,7 @@
#include <AzCore/IO/FileIO.h>
#include <AzCore/IO/Path/Path.h>
#include <AzCore/Memory/PoolAllocator.h>
#include <AzCore/std/containers/unordered_set.h>
#include <AzCore/std/smart_ptr/intrusive_base.h>
#include <AzFramework/Archive/Codec.h>
#include <AzFramework/Archive/ZipDirStructures.h>
@@ -8,6 +8,7 @@
#pragma once
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzFramework/Logging/MissingAssetNotificationBus.h>
namespace AzFramework { class LogFile; }
@@ -11,6 +11,7 @@
#include <AzCore/Math/Vector2.h>
#include <AzCore/Math/Vector3.h>
#include <AzCore/Component/ComponentBus.h>
#include <AzCore/std/containers/unordered_set.h>
#include <AzCore/std/functional.h>
//! Common structures for Render geometry queries
@@ -9,6 +9,7 @@
#pragma once
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzFramework/Windowing/WindowBus.h>
@@ -32,17 +32,14 @@ namespace AzGameFramework
// at the Assets alias, otherwise to attempting to mount the engine pak
// from the Cache folder
AZ::IO::FixedMaxPath enginePakPath = AZ::Utils::GetExecutableDirectory();
enginePakPath /= "Engine.pak";
if (m_archiveFileIO->Exists(enginePakPath.c_str()))
enginePakPath /= "engine.pak";
if (!m_archive->OpenPack("@assets@", enginePakPath.Native()))
{
m_archive->OpenPack("@assets@", enginePakPath.Native());
}
else if (enginePakPath.clear(); m_settingsRegistry->Get(enginePakPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_CacheRootFolder))
{
// fall back to checking if there is an Engine.pak in the Asset Cache
enginePakPath /= "Engine.pak";
if (m_archiveFileIO->Exists(enginePakPath.c_str()))
enginePakPath.clear();
if (m_settingsRegistry->Get(enginePakPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_CacheRootFolder))
{
// fall back to checking Project Cache Root.
enginePakPath /= "engine.pak";
m_archive->OpenPack("@assets@", enginePakPath.Native());
}
}
@@ -15,12 +15,12 @@ namespace AzNetworking
, m_startOffset(startOffset)
, m_count(startOffset < bitset.GetValidBitCount() && startOffset + count <= bitset.GetValidBitCount() ? count : 0)
{
AZ_Assert(startOffset + count <= bitset.GetValidBitCount(), "Out of bounds setup in BitsetSubset. Defaulting to 0 bit count.");
AZ_Warning("FixedSizeBitsetView", startOffset + count <= bitset.GetValidBitCount(), "Out of bounds setup in BitsetSubset. Defaulting to 0 bit count.");
}
inline void FixedSizeBitsetView::SetBit(uint32_t index, bool value)
{
AZ_Assert(index < m_count, "Out of bounds access in BitsetSubset (requested %u, count %u)", index, m_count);
AZ_Warning("FixedSizeBitsetView", index < m_count, "Out of bounds access in BitsetSubset (requested %u, count %u)", index, m_count);
if (m_count)
{
m_bitset.SetBit(m_startOffset + index, value);
@@ -29,7 +29,7 @@ namespace AzNetworking
inline bool FixedSizeBitsetView::GetBit(uint32_t index) const
{
AZ_Assert(index < m_count, "Out of bounds access in BitsetSubset (requested %u, count %u)", index, m_count);
AZ_Warning("FixedSizeBitsetView", index < m_count, "Out of bounds access in BitsetSubset (requested %u, count %u)", index, m_count);
if (m_count)
{
return m_bitset.GetBit(m_startOffset + index);
@@ -12,6 +12,7 @@
#include <AzCore/Interface/Interface.h>
#include <AzCore/Console/IConsole.h>
#include <AzCore/Console/ILogger.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/Serialization/SerializeContext.h>
namespace AzNetworking
@@ -11,6 +11,7 @@
#include <AzNetworking/Utilities/IpAddress.h>
#include <AzNetworking/ConnectionLayer/IConnectionSet.h>
#include <AzCore/std/containers/unordered_map.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
namespace AzNetworking
{
@@ -55,5 +55,8 @@ namespace UnitTest
unusedBitTest.SetBit(i, false);
}
EXPECT_FALSE(unusedBitTest.AnySet());
unusedBitTest.SetBit(0, true);
EXPECT_TRUE(unusedBitTest.AnySet());
}
}
@@ -42,4 +42,29 @@ namespace UnitTest
EXPECT_FALSE(view.GetBit(0));
}
}
TEST(FixedSizeBitsetView, EmptyBitset)
{
AzNetworking::FixedSizeBitset<32> bitset;
AzNetworking::FixedSizeBitsetView view(bitset, 10, 0);
EXPECT_FALSE(view.GetBit(0));
}
TEST(FixedSizeBitsetView, TestAnySet)
{
const uint32_t VIEW_SIZE = 5;
AzNetworking::FixedSizeBitset<9> unusedBitTest(true);
AzNetworking::FixedSizeBitsetView view(unusedBitTest, 0, VIEW_SIZE);
for (uint32_t i = 0; i < VIEW_SIZE; ++i)
{
view.SetBit(i, false);
}
EXPECT_FALSE(view.AnySet());
view.SetBit(0, true);
EXPECT_TRUE(view.AnySet());
EXPECT_EQ(view.GetValidBitCount(), VIEW_SIZE);
}
}
@@ -148,6 +148,11 @@ namespace UnitTest
EXPECT_EQ(testServer.m_serverNetworkInterface->GetConnectionSet().GetConnectionCount(), 1);
EXPECT_EQ(testClient.m_clientNetworkInterface->GetConnectionSet().GetConnectionCount(), 1);
testClient.m_clientNetworkInterface->SetTimeoutEnabled(true);
EXPECT_TRUE(testClient.m_clientNetworkInterface->IsTimeoutEnabled());
EXPECT_TRUE(testServer.m_serverNetworkInterface->StopListening());
}
#if AZ_TRAIT_DISABLE_FAILED_NETWORKING_TESTS
@@ -125,6 +125,18 @@ namespace UnitTest
AzNetworking::NetworkingSystemComponent* m_networkingSystemComponent;
};
TEST_F(UdpTransportTests, PacketIdWrap)
{
const uint32_t SEQUENCE_BOUNDARY = 0xFFFF;
UdpPacketTracker tracker;
for (uint32_t i = 0; i < SEQUENCE_BOUNDARY; ++i)
{
tracker.GetNextPacketId();
}
EXPECT_EQ(tracker.GetNextPacketId(), PacketId(SEQUENCE_BOUNDARY + 1));
}
TEST_F(UdpTransportTests, AckReplication)
{
static const SequenceId TestReliableSequenceId = InvalidSequenceId;
@@ -266,6 +278,15 @@ namespace UnitTest
EXPECT_EQ(testServer.m_serverNetworkInterface->GetConnectionSet().GetConnectionCount(), 1);
EXPECT_EQ(testClient.m_clientNetworkInterface->GetConnectionSet().GetConnectionCount(), 1);
testClient.m_clientNetworkInterface->SetTimeoutEnabled(true);
EXPECT_TRUE(testClient.m_clientNetworkInterface->IsTimeoutEnabled());
EXPECT_FALSE(dynamic_cast<UdpNetworkInterface*>(testClient.m_clientNetworkInterface)->IsEncrypted());
EXPECT_TRUE(testServer.m_serverNetworkInterface->StopListening());
EXPECT_FALSE(testServer.m_serverNetworkInterface->StopListening());
EXPECT_FALSE(dynamic_cast<UdpNetworkInterface*>(testServer.m_serverNetworkInterface)->IsOpen());
}
TEST_F(UdpTransportTests, TestMultipleClients)
@@ -11,4 +11,16 @@
namespace UnitTest
{
TEST(IpAddressTests, TestIpQuads)
{
const AzNetworking::IpAddress ip = AzNetworking::IpAddress(127, 0, 0, 1, 12345);
EXPECT_EQ(ip.GetQuadA(), 127);
EXPECT_EQ(ip.GetQuadB(), 0);
EXPECT_EQ(ip.GetQuadC(), 0);
EXPECT_EQ(ip.GetQuadD(), 1);
EXPECT_EQ(ip.GetString(), "127.0.0.1:12345");
EXPECT_EQ(ip.GetIpString(), "127.0.0.1");
}
}
@@ -79,13 +79,12 @@ namespace UnitTest
template <uint32_t NUM_ELEMENTS, uint32_t NUM_BYTES>
void TestQuantizedValuesHelper01()
{
AzNetworking::QuantizedValues<NUM_ELEMENTS, NUM_BYTES, 0, 1> testIn, testOut; // Transmits float values between 0 and 1 using NUM_BYTES
AzNetworking::QuantizedValues<NUM_ELEMENTS, NUM_BYTES, 0, 1> testIn(ValueFromFloat<NUM_ELEMENTS>::Construct(0.0f)), testOut; // Transmits float values between 0 and 1 using NUM_BYTES
AZStd::array<uint8_t, 1024> buffer;
AzNetworking::NetworkInputSerializer inputSerializer(buffer.data(), static_cast<uint32_t>(buffer.size()));
AzNetworking::NetworkOutputSerializer outputSerializer(buffer.data(), static_cast<uint32_t>(buffer.size()));
testIn = ValueFromFloat<NUM_ELEMENTS>::Construct(0.0f);
EXPECT_EQ(static_cast<typename ValueFromFloat<NUM_ELEMENTS>::ValueType>(testIn), ValueFromFloat<NUM_ELEMENTS>::Construct(0.0f));
testIn.Serialize(inputSerializer);
EXPECT_EQ(inputSerializer.GetSize(), NUM_BYTES * NUM_ELEMENTS);
@@ -95,6 +94,8 @@ namespace UnitTest
testIn = ValueFromFloat<NUM_ELEMENTS>::Construct(1.0f);
EXPECT_EQ(static_cast<typename ValueFromFloat<NUM_ELEMENTS>::ValueType>(testIn), ValueFromFloat<NUM_ELEMENTS>::Construct(1.0f));
testIn.Serialize(inputSerializer);
EXPECT_NE(testIn, testOut);
EXPECT_NE(testIn.GetQuantizedIntegralValues()[0], testOut.GetQuantizedIntegralValues()[0]);
testOut.Serialize(outputSerializer);
EXPECT_EQ(testIn, testOut);
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.4.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<g>
<path class="st0" d="M16.9,16.7c0,0.7-0.6,1.3-1.3,1.3h-7c-0.7,0-1.3-0.6-1.3-1.3v-0.5h-2v0.5c0,1.8,1.5,3.3,3.3,3.3h2
c-0.2,0.5-0.1,1.2,0.3,1.6c0.6,0.6,1.5,0.6,2.1,0c0.4-0.4,0.5-1,0.3-1.6h2.3c1.8,0,3.3-1.5,3.3-3.3v-0.5l-2,0V16.7z M12.3,20.9
c-0.2,0.2-0.6,0.2-0.8,0c-0.2-0.2-0.2-0.5,0-0.8c0.2-0.2,0.5-0.2,0.8,0c0,0,0,0,0,0C12.5,20.3,12.5,20.7,12.3,20.9z"/>
<rect x="6.5" y="8" transform="matrix(0.9939 -0.1101 0.1101 0.9939 -0.8771 1.3776)" class="st0" width="11" height="1.4"/>
<rect x="6.5" y="10.6" transform="matrix(0.9939 -0.1101 0.1101 0.9939 -1.1681 1.3937)" class="st0" width="11" height="1.4"/>
<rect x="6.5" y="13.2" transform="matrix(0.9939 -0.1101 0.1101 0.9939 -1.4588 1.4055)" class="st0" width="11" height="1.4"/>
<path class="st0" d="M13.3,4c0.1-0.2,0.1-0.3,0.1-0.5c0-0.8-0.6-1.4-1.4-1.4s-1.4,0.6-1.4,1.4c0,0.2,0,0.4,0.1,0.5H5.2v2.3h13.6V4
H13.3z M12,4c-0.3,0-0.6-0.3-0.6-0.6c0-0.3,0.3-0.6,0.6-0.6s0.6,0.3,0.6,0.6C12.6,3.7,12.3,4,12,4z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.4.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<g>
<path class="st0" d="M3,17.9v1.3c5.9,1.2,12.1,1.2,18,0v-1.3C15.1,19.1,8.9,19.1,3,17.9z"/>
<path class="st0" d="M3,15.9v1.3c5.9,1.2,12.1,1.2,18,0v-1.3C15.1,17.2,8.9,17.2,3,15.9z"/>
<path class="st0" d="M3,14v1.3c5.9,1.2,12.1,1.2,18-0.1v-1.3C15.1,15.2,8.9,15.3,3,14z"/>
<path class="st0" d="M6.5,11.5c0.1,0.1,0.2,0.2,0.4,0.2c0.1,0,0.3-0.1,0.4-0.2l2-2.1c0.2-0.2,0.2-0.5,0-0.8c-0.2-0.2-0.5-0.2-0.7,0
c0,0,0,0,0,0L7.4,9.9V3.3C7.4,3.2,7.3,3,7.2,3c0,0,0,0,0,0H6.7C6.5,3,6.4,3.1,6.4,3.3v0.2v6.4L5.3,8.7C5,8.5,4.7,8.5,4.5,8.7
C4.4,8.8,4.4,8.9,4.3,9.1c0,0.1,0.1,0.3,0.2,0.4L6.5,11.5z"/>
<path class="st0" d="M16.7,11.5c0.1,0.1,0.2,0.2,0.4,0.2c0.1,0,0.3-0.1,0.4-0.2l2-2.1c0.2-0.2,0.2-0.5,0-0.8
c-0.2-0.2-0.5-0.2-0.7,0c0,0,0,0,0,0l-1.1,1.1V3.3c0-0.2-0.1-0.3-0.2-0.3c0,0,0,0,0,0h-0.5c-0.2,0-0.3,0.1-0.3,0.3v0.2v6.4
l-1.1-1.2c-0.2-0.2-0.6-0.2-0.8,0c-0.1,0.1-0.1,0.2-0.1,0.4c0,0.1,0.1,0.3,0.1,0.4L16.7,11.5z"/>
<path class="st0" d="M9.6,11.3l2,2.1c0.1,0.1,0.2,0.2,0.4,0.2c0.1,0,0.3-0.1,0.4-0.2l2-2.1c0.2-0.2,0.2-0.5,0-0.8c0,0,0,0,0,0
c-0.2-0.2-0.5-0.2-0.7,0c0,0,0,0,0,0l-1.1,1.1V5.1c0-0.2-0.1-0.3-0.3-0.3c0,0,0,0,0,0h-0.5c-0.2,0-0.3,0.1-0.3,0.3v0.1v6.4
l-1.1-1.1c-0.2-0.2-0.5-0.2-0.7,0c0,0,0,0,0,0c-0.1,0.1-0.2,0.2-0.2,0.4C9.4,11,9.5,11.2,9.6,11.3z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.4.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<path class="st0" d="M12,12.2c-5.9,0-7-1.6-7-1.6v7.2c0.1,1,3,1.9,6.7,1.9V21c0,0.2,0.1,0.3,0.3,0.3s0.3-0.1,0.3-0.3v-1.3
c3.6,0,6.5-0.9,6.7-1.9v-7.2C19,10.6,17.9,12.2,12,12.2z"/>
<path class="st0" d="M12.3,7.6V6.9l0.8,0.4V6.6c2.7-0.1,5.2-0.7,5.2-1.4c0-0.8-2.7-1.2-6-1.2V3c0-0.2-0.1-0.3-0.3-0.3
c-0.2,0-0.3,0.1-0.3,0.3v1c-3.3,0-6,0.4-6,1.2c0,0.2,0.5,0.4,0.8,0.5C7.2,5.9,8,6.1,8.8,6.1c0.3,0,0.5,0,0.8,0C9.4,6,9.2,6,9,6
C7.6,5.9,7.2,5.5,7.2,5.2c0-0.5,1.8-0.7,4.4-0.7v1.1l-1,0.5l1,0.5v1.1C8,7.7,5,8.5,5,9.6c0,1.1,3.1,2,7,2s7-0.9,7-2
C19,8.5,16,7.7,12.3,7.6z M12.3,4.6c2.6,0,4.5,0.2,4.5,0.7c0,0.5-1.7,0.6-3.6,0.7v-1l-0.9,0.4V4.6z M8.4,9.6c0-0.5,1.4-0.9,3.2-1v2
C9.8,10.6,8.4,10.1,8.4,9.6z M12.3,10.6v-2c1.8,0,3.2,0.5,3.2,1C15.6,10.1,14.2,10.6,12.3,10.6z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.4.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<g>
<path class="st0" d="M19.6,11.7l-2.2-2.2l0,0l-1.7-1.7l0,0h0l0,0l0,0h0l0,0L13.4,10l-0.1,0.1l0,0l0,0l1.7,1.7l0,0l1.6,1.6
c1,0.8,1.1,2.2,0.4,3.2c-0.3,0.3-0.8,0.5-1.2,0.5c-0.7,0-1.5-0.3-2-0.8l-1.6-1.6l0,0L10.5,13l-0.7,0.7l-1.7,1.7l3.9,3.9
c2.2,2,5.6,1.7,7.6-0.5C21.3,16.7,21.3,13.7,19.6,11.7z M18.4,18.5c-1.6,1.4-4.1,1.4-5.7,0l-2.2-2.2l0.9-0.9L13,17
c0.7,0.7,1.7,1.1,2.7,1.1c0.7,0,1.5-0.3,2-0.8c1.2-1.4,1-3.5-0.3-4.7l-1.6-1.6l0.9-0.9l2.2,2.2C20.4,14.2,20.2,16.9,18.4,18.5z"/>
<polygon class="st0" points="6.1,7.5 5.1,7.5 5.1,5.9 3,8 5.1,10.1 5.1,8.5 6.1,8.5 "/>
<polygon class="st0" points="7.5,6.1 8.5,6.1 8.5,5.1 10.1,5.1 8,3 5.9,5.1 7.5,5.1 "/>
<polygon class="st0" points="8.5,11 8.5,10 7.5,10 7.5,10.9 5.9,10.9 8,13 10.1,11 "/>
<polygon class="st0" points="10,7.5 10,8.5 10.9,8.5 10.9,10.1 13,8 10.9,5.9 11,7.5 "/>
<rect x="6.5" y="6.5" class="st0" width="3" height="3"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.4.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<g>
<path class="st0" d="M19.6,11.7l-2.2-2.2l0,0l-1.7-1.7l0,0h0l0,0l0,0h0l0,0L13.4,10l-0.1,0.1l0,0l0,0l1.7,1.7l0,0l1.6,1.6
c1,0.8,1.1,2.2,0.4,3.2c-0.3,0.3-0.8,0.5-1.2,0.5c-0.7,0-1.5-0.3-2-0.8l-1.6-1.6l0,0L10.5,13l-0.7,0.7l-1.7,1.7l3.9,3.9
c2.2,2,5.6,1.7,7.6-0.5C21.3,16.7,21.3,13.7,19.6,11.7z M18.4,18.5c-1.6,1.4-4.1,1.4-5.7,0l-2.2-2.2l0.9-0.9L13,17
c0.7,0.7,1.7,1.1,2.7,1.1c0.7,0,1.5-0.3,2-0.8c1.2-1.4,1-3.5-0.3-4.7l-1.6-1.6l0.9-0.9l2.2,2.2C20.4,14.2,20.2,16.9,18.4,18.5z"/>
<path class="st0" d="M7.1,9.5l2.1,2.1v-1.2c2.3-0.3,4-1.4,4-3.4c0-2.3-2.2-3.5-5-3.5s-5,1.2-5,3.5c0,0.5,0.2,1.1,0.6,1.5
C4.3,9,5,9.4,5.8,9.5c0.4,0.1,0.6,0.2,0.6,0.1S6.2,9.4,5.9,9.2C5,8.9,4.4,8,4.5,7c0-1.4,1.5-2,3.7-2S12,5.6,12,7
c0,1.2-1.1,2-2.8,2.2V7.4L7.1,9.5z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.4.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<g>
<path class="st0" d="M14.5,11.3L14.5,11.3c-0.1,0-0.2,0-0.3,0.1c-0.2,0.1-0.4,0.2-0.6,0.4c-0.5,0.4-1,0.6-1.6,0.7
c-0.6-0.1-1.2-0.4-1.6-0.7c-0.2-0.1-0.4-0.3-0.6-0.4c-0.1,0-0.2-0.1-0.3-0.1H9.5c-2.4,0-3,1-3,1c-0.1,0.2-0.2,0.3-0.3,0.5v1.5
c0,0.4,0.5,0.8,0.5,0.8l1.6,1.6c0.5,0.5,0.8,1.3,0.9,2V21c0,0.4,0.3,0.7,0.7,0.7c0,0,0,0,0,0h4.1c0.4,0,0.7-0.3,0.7-0.7
c0,0,0,0,0,0v-2.3c0-0.7,0.3-1.5,0.9-2l1.6-1.6c0,0,0.5-0.4,0.5-0.8v-1.5c-0.1-0.2-0.2-0.4-0.3-0.5C17.5,12.3,16.9,11.3,14.5,11.3z
"/>
<path class="st0" d="M9.5,10.3c0.2,0,0.3,0.1,0.5,0.1c0.6,0.3,1.3,1.1,2.1,1.1s1.5-0.8,2.1-1.1c0.1-0.1,0.3-0.1,0.5-0.1
c2.7,0,3.3,1.1,3.3,1.1V11c0-0.6-0.3-1.2-0.7-1.7l-1.5-1.6c-0.5-0.7-0.7-1.5-0.7-2.4V3c0-0.6-0.4-1-1-1h-3.8c-0.6,0-1,0.4-1,1v2.3
C9.1,6.2,8.9,7,8.4,7.7L6.9,9.3C6.5,9.7,6.2,10.4,6.2,11v0.5C6.2,11.5,6.8,10.3,9.5,10.3z"/>
<path class="st0" d="M19.6,7.8c-0.1-0.2-0.3-0.2-0.5-0.1C19,7.7,18.9,7.9,19,8.1c1.4,2.5,1.4,5.4,0,7.9c0,0,0,0,0,0
c-0.1,0.2,0,0.4,0.1,0.4c0.1,0,0.1,0,0.2,0c0.1,0,0.2,0,0.3-0.1C21.1,13.7,21.1,10.4,19.6,7.8z"/>
<path class="st0" d="M4.1,11.7c0-1.2,0.3-2.4,0.8-3.4c0.1-0.2,0-0.4-0.2-0.4C4.5,7.8,4.3,7.8,4.3,8c-1.2,2.5-1.1,5.4,0.2,7.8
C4.5,15.9,4.6,16,4.7,16c0,0,0.1,0,0.1,0c0,0,0,0,0,0C5,15.9,5.1,15.7,5,15.5C4.4,14.3,4.1,13,4.1,11.7z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.4.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<g>
<path class="st0" d="M19.3,10.7l0.1-2.5c0-0.2-0.2-0.4-0.4-0.4c-0.3,0-0.5,0.2-0.5,0.5l-0.1,1.6L18,9.5l0,0
c-0.1-0.1-0.2-0.2-0.2-0.2c-0.4-0.4-0.9-0.7-1.3-0.9L17,6.7c0.1-0.3,0-0.6-0.3-0.7c0,0-0.1,0-0.1,0l-3.1-0.6
c-0.3-0.1-0.6,0.1-0.7,0.4c0,0,0,0.1,0,0.1l-0.1,1.4C10.3,7,7.9,7.8,6,9.5l0,0L5.6,9.8V8.2c0-0.3-0.2-0.5-0.5-0.5c0,0,0,0,0,0
c-0.2,0-0.5,0.2-0.5,0.4c0,0,0,0,0,0v2.5c0,0.3,0.2,0.5,0.5,0.5l2.5,0.1c0.3,0,0.5-0.2,0.5-0.5c0-0.3-0.2-0.5-0.5-0.5l-1.5-0.1
C6.3,10,6.4,9.8,6.6,9.7c1.8-1.5,4-2.1,6.1-1.9l-0.1,1.9c0,0.4,0.3,0.7,0.6,0.8l0.3,0.1l-1.8,9.2c-0.6,0.1-1.1,0.6-1.1,1.2
c0,0.7,0.6,1.3,1.3,1.3s1.3-0.6,1.3-1.3c0-0.4-0.2-0.8-0.6-1l1.8-9.2l0.3,0.1c0.4,0.1,0.7-0.1,0.9-0.5l0.5-1.4
c0.6,0.3,1.1,0.7,1.6,1.2l0.1,0.1c0,0,0.1,0.1,0.1,0.1h-1.6c-0.1,0-0.3,0-0.3,0.1c-0.1,0.1-0.1,0.2-0.2,0.3c0,0.2,0.2,0.4,0.4,0.5
c0,0,0,0,0,0h2.5C19.1,11.2,19.3,11,19.3,10.7z"/>
<path class="st0" d="M2.8,10.1c-0.1-0.1-0.3-0.1-0.4,0c-0.1,0.1-0.1,0.3,0,0.4l2.9,2.7c0.1,0,0.1,0.1,0.2,0.1c0.1,0,0.2,0,0.2-0.1
c0.1-0.1,0.1-0.3,0-0.4L2.8,10.1z"/>
<path class="st0" d="M21.5,10c-0.1-0.1-0.3-0.1-0.4,0l-2.7,2.9c0,0.1-0.1,0.1-0.1,0.2c0,0.1,0,0.1,0.1,0.2c0.1,0.1,0.3,0.1,0.4,0
l2.6-2.9C21.6,10.3,21.6,10.1,21.5,10z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.4.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<g>
<path class="st0" d="M17.5,18.1c-0.1,0-0.1,0-0.2,0H6.9l7.4-7.3c0.3-0.2,0.3-0.6,0-0.9c0,0,0,0,0,0c-0.1-0.1-0.3-0.2-0.5-0.2
c-0.2,0-0.3,0.1-0.5,0.2L6,17.2V6.9c0-0.4-0.3-0.7-0.7-0.7C5,6.2,4.7,6.5,4.7,6.9c0,0,0,0,0,0L4.6,17.1c0,0,0,0,0,0
c-1.3,0-2.3,1-2.3,2.3s1,2.3,2.3,2.3s2.3-1,2.3-2.3h10.4c0.4,0,0.7-0.2,0.7-0.6C18.1,18.5,17.9,18.1,17.5,18.1z"/>
<path class="st0" d="M21.8,13.2c-0.2-0.2-0.6-0.2-0.9,0l-1.3,1.3V14c-0.1-5.2-4.3-9.4-9.5-9.4H9.6l1.3-1.2c0.2-0.2,0.2-0.6,0-0.9
c-0.1-0.1-0.3-0.2-0.4-0.2c-0.2,0-0.3,0.1-0.4,0.2L7.7,4.8C7.6,4.9,7.5,5.1,7.5,5.2c0,0.2,0.1,0.3,0.2,0.4l2.3,2.3
c0.2,0.2,0.6,0.2,0.9,0c0.2-0.2,0.2-0.6,0-0.8L9.6,5.8h0.5c4.6,0,8.3,3.6,8.3,8.2c0,0,0,0,0,0v0.5l-1.3-1.3
c-0.1-0.1-0.3-0.2-0.4-0.2c-0.2,0-0.3,0.1-0.4,0.2c-0.2,0.2-0.2,0.6,0,0.8c0,0,0,0,0,0l2.4,2.3c0.2,0.2,0.6,0.2,0.9,0l2.3-2.3
C22.1,13.8,22.1,13.4,21.8,13.2z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

@@ -390,6 +390,14 @@
<file>img/UI20/toolbar/Y_axis.svg</file>
<file>img/UI20/toolbar/Z_axis.svg</file>
<file>img/UI20/toolbar/XY2_copy.svg</file>
<file>img/UI20/toolbar/joints/Damping.svg</file>
<file>img/UI20/toolbar/joints/MaxForce.svg</file>
<file>img/UI20/toolbar/joints/MaxTorque.svg</file>
<file>img/UI20/toolbar/joints/SnapPosition.svg</file>
<file>img/UI20/toolbar/joints/SnapRotation.svg</file>
<file>img/UI20/toolbar/joints/Stiffness.svg</file>
<file>img/UI20/toolbar/joints/SwingLimits.svg</file>
<file>img/UI20/toolbar/joints/TwistLimits.svg</file>
<file>img/triangle0.png</file>
<file>img/triangle0_highlighted.png</file>
<file>img/line.png</file>
@@ -5,7 +5,7 @@
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "Platform.h"
#include <AzTest/Platform.h>
#include <iostream>
class ModuleHandle
@@ -20,7 +20,7 @@ AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option")
AZ_POP_DISABLE_WARNING
AZ_CVAR(
bool, ed_useNewAssetBrowserTableView, false, nullptr, AZ::ConsoleFunctorFlags::Null,
bool, ed_useNewAssetBrowserTableView, true, nullptr, AZ::ConsoleFunctorFlags::Null,
"Use the new AssetBrowser TableView for searching assets.");
namespace AzToolsFramework
{
@@ -29,7 +29,7 @@ AZ_PUSH_DISABLE_WARNING(4251 4244, "-Wunknown-warning-option") // disable warnin
AZ_POP_DISABLE_WARNING
AZ_CVAR(
bool, ed_hideAssetPickerPathColumn, false, nullptr, AZ::ConsoleFunctorFlags::Null,
bool, ed_hideAssetPickerPathColumn, true, nullptr, AZ::ConsoleFunctorFlags::Null,
"Hide AssetPicker path column for a clearer view.");
AZ_CVAR_EXTERNED(bool, ed_useNewAssetBrowserTableView);
@@ -9,6 +9,7 @@
#pragma once
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/std/containers/unordered_set.h>
#include <AzCore/std/containers/unordered_map.h>
#include <AzToolsFramework/AssetBrowser/Entries/AssetBrowserEntry.h>
@@ -7,6 +7,7 @@
*/
#pragma once
#include <AzCore/std/containers/unordered_set.h>
#include <AzCore/std/string/string.h>
#include <AzCore/Asset/AssetCommon.h>
#include <AzCore/IO/Path/Path.h>
@@ -10,7 +10,7 @@
#include <AzCore/Asset/AssetCommon.h>
#include <AzCore/Outcome/Outcome.h>
#include <AzCore/UserSettings/UserSettings.h>
#include <AzCore/std/containers/unordered_map.h>
#include <AzCore/std/containers/unordered_set.h>
namespace AZ::Data
{
@@ -8,8 +8,9 @@
#include "ComponentModeCollection.h"
#include <AzToolsFramework/Commands/ComponentModeCommand.h>
#include <AzCore/std/smart_ptr/make_shared.h>
#include <AzToolsFramework/API/ViewportEditorModeTrackerInterface.h>
#include <AzToolsFramework/Commands/ComponentModeCommand.h>
namespace AzToolsFramework
{
@@ -17,7 +18,7 @@ namespace AzToolsFramework
{
AZ_CLASS_ALLOCATOR_IMPL(ComponentModeCollection, AZ::SystemAllocator, 0)
static const char* const s_nextActiveComponentModeTitle = "Edit Next";
static const char* const s_nextActiveComponentModeTitle = "Edit Next";
static const char* const s_previousActiveComponentModeTitle = "Edit Previous";
static const char* const s_nextActiveComponentModeDesc = "Move to the next component";
static const char* const s_prevActiveComponentModeDesc = "Move to the previous component";
@@ -119,6 +120,11 @@ namespace AzToolsFramework
}
};
ComponentModeCollection::ComponentModeCollection(ViewportEditorModeTrackerInterface* viewportEditorModeTracker)
: m_viewportEditorModeTracker(viewportEditorModeTracker)
{
}
void ComponentModeCollection::AddComponentMode(
const AZ::EntityComponentIdPair& entityComponentIdPair, const AZ::Uuid componentType,
const ComponentModeFactoryFunction& componentModeBuilder)
@@ -209,6 +215,11 @@ namespace AzToolsFramework
GetEntityContextId(), &EditorComponentModeNotifications::EnteredComponentMode,
m_activeComponentTypes);
// this call to activate the component mode editor state should eventually replace the bus call in
// ComponentModeCollection::BeginComponentMode() to EditorComponentModeNotifications::EnteredComponentMode
// such that all of the notifications for activating/deactivating the different editor modes are in a central location
m_viewportEditorModeTracker->ActivateMode({ /* DefaultViewportId */ }, ViewportEditorMode::Component);
// enable actions for the first/primary ComponentMode
// note: if multiple ComponentModes are activated at the same time, actions
// are not available together, the 'active' mode will bind its actions one at a time
@@ -282,6 +293,10 @@ namespace AzToolsFramework
&EditorComponentModeNotifications::LeftComponentMode,
m_activeComponentTypes);
// this call to deactivate the component mode editor state should eventually replace the bus call in
// ComponentModeCollection::EndComponentMode() to EditorComponentModeNotifications::LeftComponentMode
// such that all of the notifications for activating/deactivating the different editor modes are in a central location
m_viewportEditorModeTracker->DeactivateMode({ /* DefaultViewportId */ }, ViewportEditorMode::Component);
// clear stored modes and builders for this ComponentMode
// TLDR: avoid 'use after free' error
@@ -15,6 +15,7 @@
namespace AzToolsFramework
{
class EditorMetricsEventsBusTraits;
class ViewportEditorModeTrackerInterface;
namespace ComponentModeFramework
{
@@ -25,7 +26,7 @@ namespace AzToolsFramework
AZ_CLASS_ALLOCATOR_DECL
/// @cond
ComponentModeCollection() = default;
explicit ComponentModeCollection(ViewportEditorModeTrackerInterface* viewportEditorModeTracker);
~ComponentModeCollection() = default;
ComponentModeCollection(const ComponentModeCollection&) = delete;
ComponentModeCollection& operator=(const ComponentModeCollection&) = delete;
@@ -101,6 +102,7 @@ namespace AzToolsFramework
size_t m_selectedComponentModeIndex = 0; ///< Index into the array of active ComponentModes, current index is 'selected' ComponentMode.
bool m_adding = false; ///< Are we currently adding individual ComponentModes to the Editor wide ComponentMode.
bool m_componentMode = false; ///< Editor (global) ComponentMode flag - is ComponentMode active or not.
ViewportEditorModeTrackerInterface* m_viewportEditorModeTracker = nullptr; //!< Tracker for activating/deactivating viewport editor modes.
};
} // namespace ComponentModeFramework
} // namespace AzToolsFramework
@@ -8,6 +8,7 @@
#pragma once
#include <AzCore/Component/EntityId.h>
#include <AzCore/Interface/Interface.h>
#include <AzCore/Serialization/SerializeContext.h>
@@ -15,7 +15,7 @@
#include <AzCore/Math/Quaternion.h>
#include <AzCore/Math/Transform.h>
#include <AzCore/RTTI/RTTI.h>
#include <AzCore/std/containers/set.h>
#include <AzCore/std/containers/unordered_set.h>
#include <AzCore/std/smart_ptr/enable_shared_from_this.h>
#include <AzToolsFramework/Manipulators/ManipulatorBus.h>
#include <AzToolsFramework/Manipulators/ManipulatorSpace.h>
@@ -818,7 +818,14 @@ namespace AzToolsFramework
auto linkIterator = m_linkIdMap.find(linkId);
if (linkIterator != m_linkIdMap.end())
{
return AreDirtyTemplatesPresent(linkIterator->second.GetSourceTemplateId());
if (AreDirtyTemplatesPresent(linkIterator->second.GetSourceTemplateId()))
{
return true;
}
else
{
continue;
}
}
}
return false;
@@ -10,6 +10,7 @@
#include <AzCore/base.h>
#include <AzCore/EBus/EBus.h>
#include <AzCore/std/containers/unordered_set.h>
#include <AzCore/std/string/string.h>
namespace AzToolsFramework
@@ -119,11 +119,12 @@ namespace AzToolsFramework
// replace the default input handler with one specific for dealing with
// entity selection in the viewport
EditorInteractionSystemViewportSelectionRequestBus::Event(
GetEntityContextId(), &EditorInteractionSystemViewportSelection::SetHandler,
[](const EditorVisibleEntityDataCache* entityDataCache)
[](const EditorVisibleEntityDataCache* entityDataCache, ViewportEditorModeTrackerInterface* viewportEditorModeTracker)
{
return AZStd::make_unique<EditorPickEntitySelection>(entityDataCache);
return AZStd::make_unique<EditorPickEntitySelection>(entityDataCache, viewportEditorModeTracker);
});
if (!pickModeEntityContextId.IsNull())
@@ -22,6 +22,7 @@
#include <AZTestShared/Math/MathTestHelpers.h>
#include <AZTestShared/Utils/Utils.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AzToolsFramework/API/ViewportEditorModeTrackerInterface.h>
#include <AzToolsFramework/Application/ToolsApplication.h>
#include <AzToolsFramework/Entity/EditorEntityTransformBus.h>
#include <AzToolsFramework/ComponentMode/EditorComponentModeBus.h>
@@ -166,11 +167,12 @@ namespace UnitTest
m_editorActions.Connect();
const auto viewportHandlerBuilder =
[this](const AzToolsFramework::EditorVisibleEntityDataCache* entityDataCache)
[this](const AzToolsFramework::EditorVisibleEntityDataCache* entityDataCache,
[[maybe_unused]] AzToolsFramework::ViewportEditorModeTrackerInterface* viewportEditorModeTracker)
{
// create the default viewport (handles ComponentMode)
AZStd::unique_ptr<AzToolsFramework::EditorDefaultSelection> defaultSelection =
AZStd::make_unique<AzToolsFramework::EditorDefaultSelection>(entityDataCache);
AZStd::make_unique<AzToolsFramework::EditorDefaultSelection>(entityDataCache, viewportEditorModeTracker);
// override the phantom widget so we can use out custom test widget
defaultSelection->SetOverridePhantomWidget(&m_editorActions.m_componentModeWidget);
@@ -9,6 +9,7 @@
#include "EditorDefaultSelection.h"
#include <AzCore/std/smart_ptr/make_shared.h>
#include <AzToolsFramework/API/ViewportEditorModeTrackerInterface.h>
#include <AzToolsFramework/Manipulators/ManipulatorManager.h>
#include <AzToolsFramework/Viewport/ViewportMessages.h>
#include <AzToolsFramework/ViewportSelection/EditorSelectionUtil.h>
@@ -19,21 +20,26 @@ namespace AzToolsFramework
{
AZ_CLASS_ALLOCATOR_IMPL(EditorDefaultSelection, AZ::SystemAllocator, 0)
EditorDefaultSelection::EditorDefaultSelection(const EditorVisibleEntityDataCache* entityDataCache)
EditorDefaultSelection::EditorDefaultSelection(
const EditorVisibleEntityDataCache* entityDataCache, ViewportEditorModeTrackerInterface* viewportEditorModeTracker)
: m_phantomWidget(nullptr)
, m_entityDataCache(entityDataCache)
, m_viewportEditorModeTracker(viewportEditorModeTracker)
, m_componentModeCollection(viewportEditorModeTracker)
{
ActionOverrideRequestBus::Handler::BusConnect(GetEntityContextId());
ComponentModeFramework::ComponentModeSystemRequestBus::Handler::BusConnect();
m_manipulatorManager = AZStd::make_shared<AzToolsFramework::ManipulatorManager>(AzToolsFramework::g_mainManipulatorManagerId);
m_transformComponentSelection = AZStd::make_unique<EditorTransformComponentSelection>(entityDataCache);
m_viewportEditorModeTracker->ActivateMode({ /* DefaultViewportId */ }, ViewportEditorMode::Default);
}
EditorDefaultSelection::~EditorDefaultSelection()
{
ComponentModeFramework::ComponentModeSystemRequestBus::Handler::BusDisconnect();
ActionOverrideRequestBus::Handler::BusDisconnect();
m_viewportEditorModeTracker->DeactivateMode({ /* DefaultViewportId */ }, ViewportEditorMode::Default);
}
void EditorDefaultSelection::SetOverridePhantomWidget(QWidget* phantomOverrideWidget)
@@ -15,6 +15,8 @@
namespace AzToolsFramework
{
class ViewportEditorModeTrackerInterface;
//! The default selection/input handler for the editor (includes handling ComponentMode).
class EditorDefaultSelection
: public ViewportInteraction::InternalViewportSelectionRequests
@@ -25,7 +27,7 @@ namespace AzToolsFramework
AZ_CLASS_ALLOCATOR_DECL
//! @cond
explicit EditorDefaultSelection(const EditorVisibleEntityDataCache* entityDataCache);
EditorDefaultSelection(const EditorVisibleEntityDataCache* entityDataCache, ViewportEditorModeTrackerInterface* viewportEditorModeTracker);
EditorDefaultSelection(const EditorDefaultSelection&) = delete;
EditorDefaultSelection& operator=(const EditorDefaultSelection&) = delete;
virtual ~EditorDefaultSelection();
@@ -110,5 +112,7 @@ namespace AzToolsFramework
AZStd::shared_ptr<AzToolsFramework::ManipulatorManager> m_manipulatorManager; //!< The default manipulator manager.
ViewportInteraction::MouseInteraction m_currentInteraction; //!< Current mouse interaction to be used for drawing manipulators.
ViewportEditorModeTrackerInterface* m_viewportEditorModeTracker = nullptr; //!< Tracker for activating/deactivating viewport editor modes.
};
} // namespace AzToolsFramework
@@ -10,9 +10,24 @@
#include <AzToolsFramework/ViewportSelection/EditorDefaultSelection.h>
#include <AzToolsFramework/ViewportSelection/EditorVisibleEntityDataCache.h>
#include <AzToolsFramework/ViewportSelection/ViewportEditorModeTracker.h>
namespace AzToolsFramework
{
EditorInteractionSystemComponent::EditorInteractionSystemComponent()
: m_viewportEditorMode(AZStd::make_unique<ViewportEditorModeTracker>())
{
AZ_Assert(AZ::Interface<ViewportEditorModeTrackerInterface>::Get() == nullptr, "Unexpected registration of viewport editor mode tracker.")
AZ::Interface<ViewportEditorModeTrackerInterface>::Register(m_viewportEditorMode.get());
}
EditorInteractionSystemComponent::~EditorInteractionSystemComponent()
{
m_interactionRequests.reset();
AZ_Assert(AZ::Interface<ViewportEditorModeTrackerInterface>::Get() != nullptr, "Unexpected unregistration of viewport editor mode tracker.")
AZ::Interface<ViewportEditorModeTrackerInterface>::Unregister(m_viewportEditorMode.get());
}
void EditorInteractionSystemComponent::Activate()
{
EditorInteractionSystemViewportSelectionRequestBus::Handler::BusConnect(GetEntityContextId());
@@ -41,7 +56,8 @@ namespace AzToolsFramework
return m_interactionRequests->InternalHandleMouseManipulatorInteraction(mouseInteraction);
}
void EditorInteractionSystemComponent::SetHandler(const ViewportSelectionRequestsBuilderFn& interactionRequestsBuilder)
void EditorInteractionSystemComponent::SetHandler(
const ViewportSelectionRequestsBuilderFn& interactionRequestsBuilder)
{
// when setting a handler, make sure we're connected to the ViewportDebugDisplayEventBus so we
// can forward calls to the specific type implementing ViewportSelectionRequests
@@ -59,7 +75,7 @@ namespace AzToolsFramework
m_entityDataCache = AZStd::make_unique<EditorVisibleEntityDataCache>();
m_interactionRequests.reset(); // BusConnect/Disconnect in constructor/destructor,
// so have to reset before assigning the new one
m_interactionRequests = interactionRequestsBuilder(m_entityDataCache.get());
m_interactionRequests = interactionRequestsBuilder(m_entityDataCache.get(), m_viewportEditorMode.get());
}
EditorInteractionSystemViewportSelectionRequestBus::Handler::BusConnect(GetEntityContextId());
@@ -68,9 +84,9 @@ namespace AzToolsFramework
void EditorInteractionSystemComponent::SetDefaultHandler()
{
SetHandler(
[](const EditorVisibleEntityDataCache* entityDataCache)
[](const EditorVisibleEntityDataCache* entityDataCache, ViewportEditorModeTrackerInterface* viewportEditorModeTracker)
{
return AZStd::make_unique<EditorDefaultSelection>(entityDataCache);
return AZStd::make_unique<EditorDefaultSelection>(entityDataCache, viewportEditorModeTracker);
});
}
@@ -14,6 +14,8 @@
namespace AzToolsFramework
{
class ViewportEditorModeTracker;
//! System Component to wrap active input handler.
//! EditorInteractionSystemComponent is notified of viewport mouse events from RenderViewport
//! and forwards them to a concrete implementation of ViewportSelectionRequests.
@@ -26,6 +28,9 @@ namespace AzToolsFramework
public:
AZ_COMPONENT(EditorInteractionSystemComponent, "{146D0317-AF42-45AB-A953-F54198525DD5}")
EditorInteractionSystemComponent();
~EditorInteractionSystemComponent();
static void Reflect(AZ::ReflectContext* context);
// EditorInteractionSystemViewportSelectionRequestBus
@@ -54,5 +59,7 @@ namespace AzToolsFramework
AZStd::unique_ptr<InternalViewportSelectionRequests> m_interactionRequests; //!< Hold a concrete implementation of
//!< ViewportSelectionRequests to handle viewport
//!< input and drawing for the Editor.
AZStd::unique_ptr<ViewportEditorModeTracker> m_viewportEditorMode; //!< Editor mode tracker for each viewport.
};
} // namespace AzToolsFramework
@@ -17,6 +17,7 @@
namespace AzToolsFramework
{
class EditorVisibleEntityDataCache;
class ViewportEditorModeTrackerInterface;
//! Bus to handle all mouse events originating from the viewport.
//! Coordinated by the EditorInteractionSystemComponent
@@ -32,8 +33,8 @@ namespace AzToolsFramework
};
//! Alias for factory function to create a new type implementing the ViewportSelectionRequests interface.
using ViewportSelectionRequestsBuilderFn =
AZStd::function<AZStd::unique_ptr<ViewportInteraction::InternalViewportSelectionRequests>(const EditorVisibleEntityDataCache*)>;
using ViewportSelectionRequestsBuilderFn = AZStd::function<AZStd::unique_ptr<ViewportInteraction::InternalViewportSelectionRequests>(
const EditorVisibleEntityDataCache*, ViewportEditorModeTrackerInterface*)>;
//! Interface for system component implementing the ViewportSelectionRequests interface.
//! This interface also includes a setter to set a custom handler also implementing
@@ -8,6 +8,7 @@
#include "EditorPickEntitySelection.h"
#include <AzToolsFramework/API/ViewportEditorModeTrackerInterface.h>
#include <AzToolsFramework/ViewportSelection/EditorSelectionUtil.h>
#include <QApplication>
@@ -15,9 +16,12 @@ namespace AzToolsFramework
{
AZ_CLASS_ALLOCATOR_IMPL(EditorPickEntitySelection, AZ::SystemAllocator, 0)
EditorPickEntitySelection::EditorPickEntitySelection(const EditorVisibleEntityDataCache* entityDataCache)
EditorPickEntitySelection::EditorPickEntitySelection(
const EditorVisibleEntityDataCache* entityDataCache, ViewportEditorModeTrackerInterface* viewportEditorModeTracker)
: m_editorHelpers(AZStd::make_unique<EditorHelpers>(entityDataCache))
, m_viewportEditorModeTracker(viewportEditorModeTracker)
{
m_viewportEditorModeTracker->ActivateMode({ /* DefaultViewportId */ }, ViewportEditorMode::Pick);
}
EditorPickEntitySelection::~EditorPickEntitySelection()
@@ -26,6 +30,8 @@ namespace AzToolsFramework
{
ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequests::SetEntityHighlighted, m_hoveredEntityId, false);
}
m_viewportEditorModeTracker->DeactivateMode({ /* DefaultViewportId */ }, ViewportEditorMode::Pick);
}
// note: entityIdUnderCursor is the authoritative entityId we get each frame by querying
@@ -13,6 +13,8 @@
namespace AzToolsFramework
{
class ViewportEditorModeTrackerInterface;
//! Viewport interaction that will handle assigning an entity in the viewport to
//! an entity field in the entity inspector.
class EditorPickEntitySelection : public ViewportInteraction::InternalViewportSelectionRequests
@@ -20,7 +22,8 @@ namespace AzToolsFramework
public:
AZ_CLASS_ALLOCATOR_DECL
EditorPickEntitySelection(const EditorVisibleEntityDataCache* entityDataCache);
EditorPickEntitySelection(
const EditorVisibleEntityDataCache* entityDataCache, ViewportEditorModeTrackerInterface* viewportEditorModeTracker);
~EditorPickEntitySelection();
private:
@@ -32,5 +35,6 @@ namespace AzToolsFramework
AZStd::unique_ptr<EditorHelpers> m_editorHelpers; //!< Editor visualization of entities (icons, shapes, debug visuals etc).
AZ::EntityId m_hoveredEntityId; //!< What EntityId is the mouse currently hovering over (if any).
AZ::EntityId m_cachedEntityIdUnderCursor; //!< Store the EntityId on each mouse move for use in Display.
ViewportEditorModeTrackerInterface* m_viewportEditorModeTracker = nullptr; //!< Tracker for activating/deactivating viewport editor modes.
};
} // namespace AzToolsFramework
@@ -45,22 +45,6 @@ namespace AzToolsFramework
return m_editorModes[static_cast<AZ::u32>(mode)];
}
void ViewportEditorModeTracker::RegisterInterface()
{
if (AZ::Interface<ViewportEditorModeTrackerInterface>::Get() == nullptr)
{
AZ::Interface<ViewportEditorModeTrackerInterface>::Register(this);
}
}
void ViewportEditorModeTracker::UnregisterInterface()
{
if (AZ::Interface<ViewportEditorModeTrackerInterface>::Get() != nullptr)
{
AZ::Interface<ViewportEditorModeTrackerInterface>::Unregister(this);
}
}
AZ::Outcome<void, AZStd::string> ViewportEditorModeTracker::ActivateMode(
const ViewportEditorModeInfo& viewportEditorModeInfo, ViewportEditorMode mode)
{
@@ -41,12 +41,6 @@ namespace AzToolsFramework
: public ViewportEditorModeTrackerInterface
{
public:
//! Registers this object with the AZ::Interface.
void RegisterInterface();
//! Unregisters this object with the AZ::Interface.
void UnregisterInterface();
// ViewportEditorModeTrackerInterface overrides ...
AZ::Outcome<void, AZStd::string> ActivateMode(const ViewportEditorModeInfo& viewportEditorModeInfo, ViewportEditorMode mode) override;
AZ::Outcome<void, AZStd::string> DeactivateMode(const ViewportEditorModeInfo& viewportEditorModeInfo, ViewportEditorMode mode) override;
@@ -29,13 +29,29 @@ namespace AzToolsFramework::ViewportUi::Internal
void ButtonGroup::SetHighlightedButton(ButtonId buttonId)
{
if (buttonId == m_highlightedButtonId) // the requested button is highlighted, so do nothing.
{
return;
}
if (auto buttonEntry = m_buttons.find(buttonId); buttonEntry != m_buttons.end())
{
for (auto& button : m_buttons)
{
button.second->m_state = Button::State::Deselected;
}
ClearHighlightedButton();
buttonEntry->second->m_state = Button::State::Selected;
m_highlightedButtonId = buttonId;
}
}
void ButtonGroup::ClearHighlightedButton()
{
if (m_highlightedButtonId == InvalidButtonId)
{
return;
}
if (auto buttonEntry = m_buttons.find(m_highlightedButtonId); buttonEntry != m_buttons.end())
{
buttonEntry->second->m_state = Button::State::Deselected;
m_highlightedButtonId = InvalidButtonId;
}
}
@@ -8,6 +8,7 @@
#pragma once
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzToolsFramework/ViewportUi/ViewportUiRequestBus.h>
namespace AzToolsFramework::ViewportUi::Internal
@@ -24,6 +25,7 @@ namespace AzToolsFramework::ViewportUi::Internal
~ButtonGroup() = default;
void SetHighlightedButton(ButtonId buttonId);
void ClearHighlightedButton();
void SetViewportUiElementId(ViewportUiElementId id);
ViewportUiElementId GetViewportUiElementId() const;
@@ -39,5 +41,6 @@ namespace AzToolsFramework::ViewportUi::Internal
AZ::Event<ButtonId> m_buttonTriggeredEvent;
ViewportUiElementId m_viewportUiId;
AZStd::unordered_map<ButtonId, AZStd::unique_ptr<Button>> m_buttons;
ButtonId m_highlightedButtonId = InvalidButtonId;
};
} // namespace AzToolsFramework::ViewportUi::Internal
@@ -50,6 +50,16 @@ namespace AzToolsFramework::ViewportUi
}
}
void ViewportUiManager::ClearClusterActiveButton(ClusterId clusterId)
{
if (auto clusterIt = m_clusterButtonGroups.find(clusterId); clusterIt != m_clusterButtonGroups.end())
{
auto cluster = clusterIt->second;
cluster->ClearHighlightedButton();
UpdateButtonGroupUi(cluster.get());
}
}
void ViewportUiManager::SetSwitcherActiveButton(const SwitcherId switcherId, const ButtonId buttonId)
{
if (auto switcherIt = m_switcherButtonGroups.find(switcherId); switcherIt != m_switcherButtonGroups.end())
@@ -30,6 +30,7 @@ namespace AzToolsFramework::ViewportUi
const ClusterId CreateCluster(Alignment align) override;
const SwitcherId CreateSwitcher(Alignment align) override;
void SetClusterActiveButton(ClusterId clusterId, ButtonId buttonId) override;
void ClearClusterActiveButton(ClusterId clusterId) override;
void SetSwitcherActiveButton(SwitcherId switcherId, ButtonId buttonId) override;
void SetClusterButtonLocked(ClusterId clusterId, ButtonId buttonId, bool isLocked) override;
void SetClusterButtonTooltip(ClusterId clusterId, ButtonId buttonId, const AZStd::string& tooltip) override;
@@ -59,6 +59,8 @@ namespace AzToolsFramework::ViewportUi
virtual const SwitcherId CreateSwitcher(Alignment align) = 0;
//! Sets the active button of the cluster. This is the button which will display as highlighted.
virtual void SetClusterActiveButton(ClusterId clusterId, ButtonId buttonId) = 0;
//! Clears the active button of the cluster if one is active. The button will no longer display as highlighted.
virtual void ClearClusterActiveButton(ClusterId clusterId) = 0;
//! Sets the active button of the switcher. This is the button which has a text label.
virtual void SetSwitcherActiveButton(SwitcherId clusterId, ButtonId buttonId) = 0;
//! Adds a locked overlay to the cluster button's icon.
@@ -0,0 +1,60 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <Tests/BoundsTestComponent.h>
#include <AzToolsFramework/ViewportSelection/EditorSelectionUtil.h>
namespace UnitTest
{
AZ::Aabb BoundsTestComponent::GetEditorSelectionBoundsViewport([[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo)
{
return GetWorldBounds();
}
bool BoundsTestComponent::EditorSelectionIntersectRayViewport(
[[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo, const AZ::Vector3& src, const AZ::Vector3& dir, float& distance)
{
return AzToolsFramework::AabbIntersectRay(src, dir, GetWorldBounds(), distance);
}
bool BoundsTestComponent::SupportsEditorRayIntersect()
{
return true;
}
void BoundsTestComponent::Reflect([[maybe_unused]] AZ::ReflectContext* context)
{
// noop
}
void BoundsTestComponent::Activate()
{
AzFramework::BoundsRequestBus::Handler::BusConnect(GetEntityId());
AzToolsFramework::EditorComponentSelectionRequestsBus::Handler::BusConnect(GetEntityId());
}
void BoundsTestComponent::Deactivate()
{
AzToolsFramework::EditorComponentSelectionRequestsBus::Handler::BusDisconnect();
AzFramework::BoundsRequestBus::Handler::BusDisconnect();
}
AZ::Aabb BoundsTestComponent::GetWorldBounds()
{
AZ::Transform worldFromLocal = AZ::Transform::CreateIdentity();
AZ::TransformBus::EventResult(worldFromLocal, GetEntityId(), &AZ::TransformBus::Events::GetWorldTM);
return GetLocalBounds().GetTransformedAabb(worldFromLocal);
}
AZ::Aabb BoundsTestComponent::GetLocalBounds()
{
return AZ::Aabb::CreateFromMinMax(AZ::Vector3(-0.5f), AZ::Vector3(0.5f));
}
} // namespace UnitTest
@@ -0,0 +1,46 @@
/*
* 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 <AzFramework/Visibility/BoundsBus.h>
#include <AzToolsFramework/API/ComponentEntitySelectionBus.h>
#include <AzToolsFramework/ToolsComponents/EditorComponentBase.h>
namespace UnitTest
{
//! Basic component that implements BoundsRequestBus and EditorComponentSelectionRequestsBus to be compatible
//! with the Editor visibility system.
//! Note: Used for simulating selection (picking) in the viewport.
class BoundsTestComponent
: public AzToolsFramework::Components::EditorComponentBase
, public AzFramework::BoundsRequestBus::Handler
, public AzToolsFramework::EditorComponentSelectionRequestsBus::Handler
{
public:
AZ_EDITOR_COMPONENT(
BoundsTestComponent, "{E6312E9D-8489-4677-9980-C93C328BC92C}", AzToolsFramework::Components::EditorComponentBase);
static void Reflect(AZ::ReflectContext* context);
// AZ::Component overrides ...
void Activate() override;
void Deactivate() override;
// EditorComponentSelectionRequestsBus overrides ...
AZ::Aabb GetEditorSelectionBoundsViewport(const AzFramework::ViewportInfo& viewportInfo) override;
bool EditorSelectionIntersectRayViewport(
const AzFramework::ViewportInfo& viewportInfo, const AZ::Vector3& src, const AZ::Vector3& dir, float& distance) override;
bool SupportsEditorRayIntersect() override;
// BoundsRequestBus overrides ...
AZ::Aabb GetWorldBounds() override;
AZ::Aabb GetLocalBounds() override;
};
} // namespace UnitTest
@@ -13,7 +13,6 @@
#include <AzFramework/Components/TransformComponent.h>
#include <AzFramework/Entity/EntityContext.h>
#include <AzFramework/Viewport/ViewportScreen.h>
#include <AzFramework/Visibility/BoundsBus.h>
#include <AzManipulatorTestFramework/AzManipulatorTestFramework.h>
#include <AzManipulatorTestFramework/AzManipulatorTestFrameworkTestHelpers.h>
#include <AzManipulatorTestFramework/AzManipulatorTestFrameworkUtils.h>
@@ -22,12 +21,10 @@
#include <AzManipulatorTestFramework/ViewportInteraction.h>
#include <AzQtComponents/Components/GlobalEventFilter.h>
#include <AzTest/AzTest.h>
#include <AzToolsFramework/API/ComponentEntitySelectionBus.h>
#include <AzToolsFramework/Application/ToolsApplication.h>
#include <AzToolsFramework/Entity/EditorEntityActionComponent.h>
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
#include <AzToolsFramework/Entity/EditorEntityModel.h>
#include <AzToolsFramework/ToolsComponents/EditorComponentBase.h>
#include <AzToolsFramework/ToolsComponents/EditorLockComponent.h>
#include <AzToolsFramework/ToolsComponents/EditorVisibilityComponent.h>
#include <AzToolsFramework/ToolsComponents/TransformComponent.h>
@@ -41,6 +38,8 @@
#include <AzToolsFramework/ViewportSelection/EditorVisibleEntityDataCache.h>
#include <AzToolsFramework/ViewportUi/ViewportUiManager.h>
#include<Tests/BoundsTestComponent.h>
namespace AZ
{
std::ostream& operator<<(std::ostream& os, const EntityId entityId)
@@ -123,80 +122,6 @@ namespace UnitTest
EXPECT_FALSE(m_cache.IsVisibleEntityVisible(m_cache.GetVisibleEntityIndexFromId(m_entityIds[2]).value()));
}
//! Basic component that implements BoundsRequestBus and EditorComponentSelectionRequestsBus to be compatible
//! with the Editor visibility system.
//! Note: Used for simulating selection (picking) in the viewport.
class BoundsTestComponent
: public AzToolsFramework::Components::EditorComponentBase
, public AzFramework::BoundsRequestBus::Handler
, public AzToolsFramework::EditorComponentSelectionRequestsBus::Handler
{
public:
AZ_EDITOR_COMPONENT(
BoundsTestComponent, "{E6312E9D-8489-4677-9980-C93C328BC92C}", AzToolsFramework::Components::EditorComponentBase);
static void Reflect(AZ::ReflectContext* context);
// AZ::Component overrides ...
void Activate() override;
void Deactivate() override;
// EditorComponentSelectionRequestsBus overrides ...
AZ::Aabb GetEditorSelectionBoundsViewport(const AzFramework::ViewportInfo& viewportInfo) override;
bool EditorSelectionIntersectRayViewport(
const AzFramework::ViewportInfo& viewportInfo, const AZ::Vector3& src, const AZ::Vector3& dir, float& distance) override;
bool SupportsEditorRayIntersect() override;
// BoundsRequestBus overrides ...
AZ::Aabb GetWorldBounds() override;
AZ::Aabb GetLocalBounds() override;
};
AZ::Aabb BoundsTestComponent::GetEditorSelectionBoundsViewport([[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo)
{
return GetWorldBounds();
}
bool BoundsTestComponent::EditorSelectionIntersectRayViewport(
[[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo, const AZ::Vector3& src, const AZ::Vector3& dir, float& distance)
{
return AzToolsFramework::AabbIntersectRay(src, dir, GetWorldBounds(), distance);
}
bool BoundsTestComponent::SupportsEditorRayIntersect()
{
return true;
}
void BoundsTestComponent::Reflect([[maybe_unused]] AZ::ReflectContext* context)
{
// noop
}
void BoundsTestComponent::Activate()
{
AzFramework::BoundsRequestBus::Handler::BusConnect(GetEntityId());
AzToolsFramework::EditorComponentSelectionRequestsBus::Handler::BusConnect(GetEntityId());
}
void BoundsTestComponent::Deactivate()
{
AzToolsFramework::EditorComponentSelectionRequestsBus::Handler::BusDisconnect();
AzFramework::BoundsRequestBus::Handler::BusDisconnect();
}
AZ::Aabb BoundsTestComponent::GetWorldBounds()
{
AZ::Transform worldFromLocal = AZ::Transform::CreateIdentity();
AZ::TransformBus::EventResult(worldFromLocal, GetEntityId(), &AZ::TransformBus::Events::GetWorldTM);
return GetLocalBounds().GetTransformedAabb(worldFromLocal);
}
AZ::Aabb BoundsTestComponent::GetLocalBounds()
{
return AZ::Aabb::CreateFromMinMax(AZ::Vector3(-0.5f), AZ::Vector3(0.5f));
}
// Fixture to support testing EditorTransformComponentSelection functionality on an Entity selection.
class EditorTransformComponentSelectionFixture : public ToolsApplicationFixture
{
@@ -344,9 +269,10 @@ namespace UnitTest
using AzToolsFramework::EditorInteractionSystemViewportSelectionRequestBus;
EditorInteractionSystemViewportSelectionRequestBus::Event(
AzToolsFramework::GetEntityContextId(), &EditorInteractionSystemViewportSelectionRequestBus::Events::SetHandler,
[](const AzToolsFramework::EditorVisibleEntityDataCache* entityDataCache)
[](const AzToolsFramework::EditorVisibleEntityDataCache* entityDataCache,
[[maybe_unused]] AzToolsFramework::ViewportEditorModeTrackerInterface* viewportEditorModeTracker)
{
return AZStd::make_unique<AzToolsFramework::EditorPickEntitySelection>(entityDataCache);
return AZStd::make_unique<AzToolsFramework::EditorPickEntitySelection>(entityDataCache, viewportEditorModeTracker);
});
// When
@@ -0,0 +1,78 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <Tests/FocusMode/EditorFocusModeFixture.h>
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
#include <Tests/BoundsTestComponent.h>
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
// in the unit tests.
AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize);
m_focusModeInterface = AZ::Interface<FocusModeInterface>::Get();
ASSERT_TRUE(m_focusModeInterface != nullptr);
// register a simple component implementing BoundsRequestBus and EditorComponentSelectionRequestsBus
GetApplication()->RegisterComponentDescriptor(UnitTest::BoundsTestComponent::CreateDescriptor());
GenerateTestHierarchy();
}
void EditorFocusModeFixture::GenerateTestHierarchy()
{
/*
* 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]);
// Add a BoundsTestComponent to the Car entity.
AZ::Entity* entity = GetEntityById(m_entityMap[CarEntityName]);
entity->Deactivate();
entity->CreateComponent<UnitTest::BoundsTestComponent>();
entity->Activate();
// Move the CarEntity so it's out of the way.
AZ::TransformBus::Event(m_entityMap[CarEntityName], &AZ::TransformBus::Events::SetWorldTranslation, CarEntityPosition);
// Setup the camera so the Car entity is in view.
AzFramework::SetCameraTransform(
m_cameraState,
AZ::Transform::CreateFromQuaternionAndTranslation(
AZ::Quaternion::CreateFromEulerAnglesDegrees(AZ::Vector3(0.0f, 0.0f, 0.0f)), CameraPosition));
}
AZ::EntityId EditorFocusModeFixture::CreateEditorEntity(const char* name, AZ::EntityId parentId)
{
AZ::Entity* entity = nullptr;
UnitTest::CreateDefaultEditorEntity(name, &entity);
// Parent
AZ::TransformBus::Event(entity->GetId(), &AZ::TransformInterface::SetParent, parentId);
return entity->GetId();
}
}
@@ -0,0 +1,48 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/Component/TransformBus.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <AzCore/UserSettings/UserSettingsComponent.h>
#include <AzTest/AzTest.h>
#include <AzToolsFramework/FocusMode/FocusModeInterface.h>
#include <AzToolsFramework/UnitTest/AzToolsFrameworkTestHelpers.h>
namespace AzToolsFramework
{
class EditorFocusModeFixture
: public UnitTest::ToolsApplicationFixture
{
protected:
void SetUpEditorFixtureImpl() override;
void GenerateTestHierarchy();
AZ::EntityId CreateEditorEntity(const char* name, AZ::EntityId parentId);
AZStd::unordered_map<AZStd::string, AZ::EntityId> m_entityMap;
FocusModeInterface* m_focusModeInterface = nullptr;
public:
AzFramework::CameraState m_cameraState;
inline static const AZ::Vector3 CameraPosition = AZ::Vector3(10.0f, 15.0f, 10.0f);
inline static const char* CityEntityName = "City";
inline static const char* StreetEntityName = "Street";
inline static const char* CarEntityName = "Car";
inline static const char* SportsCarEntityName = "SportsCar";
inline static const char* Passenger1EntityName = "Passenger1";
inline static const char* Passenger2EntityName = "Passenger2";
inline static AZ::Vector3 CarEntityPosition = AZ::Vector3(5.0f, 15.0f, 0.0f);
};
}
@@ -0,0 +1,135 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <Tests/FocusMode/EditorFocusModeFixture.h>
#include <AzCore/Component/TransformBus.h>
#include <AzCore/std/string/string.h>
#include <AzFramework/Viewport/ViewportScreen.h>
#include <AzManipulatorTestFramework/AzManipulatorTestFramework.h>
#include <AzManipulatorTestFramework/AzManipulatorTestFrameworkTestHelpers.h>
#include <AzManipulatorTestFramework/DirectManipulatorViewportInteraction.h>
#include <AzManipulatorTestFramework/ImmediateModeActionDispatcher.h>
#include <AzManipulatorTestFramework/IndirectManipulatorViewportInteraction.h>
#include <AzToolsFramework/Component/EditorComponentAPIBus.h>
#include <AzToolsFramework/Manipulators/LinearManipulator.h>
#include <AzToolsFramework/Manipulators/ManipulatorManager.h>
#include <AzToolsFramework/ViewportSelection/EditorVisibleEntityDataCache.h>
namespace AzToolsFramework
{
class EditorFocusModeSelectionFixture
: public UnitTest::IndirectCallManipulatorViewportInteractionFixtureMixin<EditorFocusModeFixture>
{
public:
void ClickAtWorldPositionOnViewport(const AZ::Vector3& worldPosition)
{
// Calculate the world position in screen space
const auto carScreenPosition = AzFramework::WorldToScreen(worldPosition, m_cameraState);
// Click the entity in the viewport
m_actionDispatcher->CameraState(m_cameraState)->MousePosition(carScreenPosition)->MouseLButtonDown()->MouseLButtonUp();
}
};
void ClearSelectedEntities()
{
AzToolsFramework::ToolsApplicationRequestBus::Broadcast(
&AzToolsFramework::ToolsApplicationRequestBus::Events::SetSelectedEntities, AzToolsFramework::EntityIdList());
}
AzToolsFramework::EntityIdList GetSelectedEntities()
{
AzToolsFramework::EntityIdList selectedEntities;
AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(
selectedEntities, &AzToolsFramework::ToolsApplicationRequestBus::Events::GetSelectedEntities);
return selectedEntities;
}
TEST_F(EditorFocusModeSelectionFixture, EditorFocusModeSelectionTests_SelectEntityWithFocusOnLevel)
{
// Clear the focus, disabling focus mode
m_focusModeInterface->ClearFocusRoot();
// Clear selection
ClearSelectedEntities();
// Click on Car Entity
ClickAtWorldPositionOnViewport(CarEntityPosition);
// Verify entity is selected
auto selectedEntitiesAfter = GetSelectedEntities();
EXPECT_EQ(selectedEntitiesAfter.size(), 1);
EXPECT_EQ(selectedEntitiesAfter.front(), m_entityMap[CarEntityName]);
}
TEST_F(EditorFocusModeSelectionFixture, EditorFocusModeSelectionTests_SelectEntityWithFocusOnAncestor)
{
// Set the focus on the Street Entity (parent of the test entity)
m_focusModeInterface->SetFocusRoot(m_entityMap[StreetEntityName]);
// Clear selection
ClearSelectedEntities();
// Click on Car Entity
ClickAtWorldPositionOnViewport(CarEntityPosition);
// Verify entity is selected
auto selectedEntitiesAfter = GetSelectedEntities();
EXPECT_EQ(selectedEntitiesAfter.size(), 1);
EXPECT_EQ(selectedEntitiesAfter.front(), m_entityMap[CarEntityName]);
}
TEST_F(EditorFocusModeSelectionFixture, EditorFocusModeSelectionTests_SelectEntityWithFocusOnItself)
{
// Set the focus on the Car Entity (test entity)
m_focusModeInterface->SetFocusRoot(m_entityMap[CarEntityName]);
// Clear selection
ClearSelectedEntities();
// Click on Car Entity
ClickAtWorldPositionOnViewport(CarEntityPosition);
// Verify entity is selected
auto selectedEntitiesAfter = GetSelectedEntities();
EXPECT_EQ(selectedEntitiesAfter.size(), 1);
EXPECT_EQ(selectedEntitiesAfter.front(), m_entityMap[CarEntityName]);
}
TEST_F(EditorFocusModeSelectionFixture, EditorFocusModeSelectionTests_SelectEntityWithFocusOnSibling)
{
// Set the focus on the SportsCar Entity (sibling of the test entity)
m_focusModeInterface->SetFocusRoot(m_entityMap[SportsCarEntityName]);
// Clear selection
ClearSelectedEntities();
// Click on Car Entity
ClickAtWorldPositionOnViewport(CarEntityPosition);
// entity is selected
auto selectedEntitiesAfter = GetSelectedEntities();
EXPECT_EQ(selectedEntitiesAfter.size(), 0);
}
TEST_F(EditorFocusModeSelectionFixture, EditorFocusModeSelectionTests_SelectEntityWithFocusOnDescendant)
{
// Set the focus on the Passenger1 Entity (child of the entity)
m_focusModeInterface->SetFocusRoot(m_entityMap[Passenger1EntityName]);
// Clear selection
ClearSelectedEntities();
// Click on Car Entity
ClickAtWorldPositionOnViewport(CarEntityPosition);
// entity is selected
auto selectedEntitiesAfter = GetSelectedEntities();
EXPECT_EQ(selectedEntitiesAfter.size(), 0);
}
}
@@ -6,123 +6,99 @@
*
*/
#include <AzTest/AzTest.h>
#include <AzCore/UserSettings/UserSettingsComponent.h>
#include <AzCore/Component/TransformBus.h>
#include <AzToolsFramework/FocusMode/FocusModeInterface.h>
#include <AzToolsFramework/UnitTest/AzToolsFrameworkTestHelpers.h>
#include <Tests/FocusMode/EditorFocusModeFixture.h>
namespace AzToolsFramework
{
class EditorFocusModeTests
: public ::testing::Test
TEST_F(EditorFocusModeFixture, EditorFocusModeTests_SetFocus)
{
protected:
void SetUp() override
{
m_app.Start(m_descriptor);
// When an entity is set as the focus root, GetFocusRoot should return its EntityId.
m_focusModeInterface->SetFocusRoot(m_entityMap[CarEntityName]);
EXPECT_EQ(m_focusModeInterface->GetFocusRoot(), m_entityMap[CarEntityName]);
// Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is
// shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash
// in the unit tests.
AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize);
GenerateTestHierarchy();
}
void GenerateTestHierarchy()
{
/*
* City
* |_ Street
* |_ Car
* | |_ Passenger
* |_ SportsCar
* |_ Passenger
*/
m_entityMap["cityId"] = CreateEditorEntity("City", AZ::EntityId());
m_entityMap["streetId"] = CreateEditorEntity("Street", m_entityMap["cityId"]);
m_entityMap["carId"] = CreateEditorEntity("Car", m_entityMap["streetId"]);
m_entityMap["passengerId1"] = CreateEditorEntity("Passenger", m_entityMap["carId"]);
m_entityMap["sportsCarId"] = CreateEditorEntity("SportsCar", m_entityMap["streetId"]);
m_entityMap["passengerId2"] = CreateEditorEntity("Passenger", m_entityMap["sportsCarId"]);
}
AZ::EntityId CreateEditorEntity(const char* name, AZ::EntityId parentId)
{
AZ::Entity* entity = nullptr;
UnitTest::CreateDefaultEditorEntity(name, &entity);
// Parent
AZ::TransformBus::Event(entity->GetId(), &AZ::TransformInterface::SetParent, parentId);
return entity->GetId();
}
void TearDown() override
{
m_app.Stop();
}
UnitTest::ToolsTestApplication m_app{ "EditorFocusModeTests" };
AZ::ComponentApplication::Descriptor m_descriptor;
AZStd::unordered_map<AZStd::string, AZ::EntityId> m_entityMap;
};
TEST_F(EditorFocusModeTests, EditorFocusModeTests_SetFocus)
{
FocusModeInterface* focusModeInterface = AZ::Interface<FocusModeInterface>::Get();
EXPECT_TRUE(focusModeInterface != nullptr);
focusModeInterface->SetFocusRoot(m_entityMap["carId"]);
EXPECT_EQ(focusModeInterface->GetFocusRoot(), m_entityMap["carId"]);
focusModeInterface->ClearFocusRoot();
EXPECT_EQ(focusModeInterface->GetFocusRoot(), AZ::EntityId());
// Restore default expected focus.
m_focusModeInterface->ClearFocusRoot();
}
TEST_F(EditorFocusModeTests, EditorFocusModeTests_IsInFocusSubTree)
TEST_F(EditorFocusModeFixture, EditorFocusModeTests_ClearFocus)
{
FocusModeInterface* focusModeInterface = AZ::Interface<FocusModeInterface>::Get();
EXPECT_TRUE(focusModeInterface != nullptr);
// Change the value from the default.
m_focusModeInterface->SetFocusRoot(m_entityMap[CarEntityName]);
focusModeInterface->ClearFocusRoot();
EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["cityId"]), true);
EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["streetId"]), true);
EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["carId"]), true);
EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["passengerId1"]), true);
EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["sportsCarId"]), true);
EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["passengerId2"]), true);
// Calling ClearFocusRoot restores the default focus root (which is an invalid EntityId).
m_focusModeInterface->ClearFocusRoot();
EXPECT_EQ(m_focusModeInterface->GetFocusRoot(), AZ::EntityId());
}
focusModeInterface->SetFocusRoot(m_entityMap["streetId"]);
TEST_F(EditorFocusModeFixture, EditorFocusModeTests_IsInFocusSubTree_AncestorsDescendants)
{
// 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]);
EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["cityId"]), false);
EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["streetId"]), true);
EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["carId"]), true);
EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["passengerId1"]), true);
EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["sportsCarId"]), true);
EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["passengerId2"]), true);
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[CityEntityName]), false);
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[StreetEntityName]), true);
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[CarEntityName]), true);
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger1EntityName]), true);
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[SportsCarEntityName]), true);
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger2EntityName]), true);
}
focusModeInterface->SetFocusRoot(m_entityMap["carId"]);
// Restore default expected focus.
m_focusModeInterface->ClearFocusRoot();
}
EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["cityId"]), false);
EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["streetId"]), false);
EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["carId"]), true);
EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["passengerId1"]), true);
EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["sportsCarId"]), false);
EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["passengerId2"]), false);
TEST_F(EditorFocusModeFixture, EditorFocusModeTests_IsInFocusSubTree_Siblings)
{
// If the root entity has siblings, they are also outside of the focus subtree.
{
m_focusModeInterface->SetFocusRoot(m_entityMap[CarEntityName]);
focusModeInterface->SetFocusRoot(m_entityMap["passengerId2"]);
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[CityEntityName]), false);
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[StreetEntityName]), false);
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[CarEntityName]), true);
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger1EntityName]), true);
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[SportsCarEntityName]), false);
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger2EntityName]), false);
}
EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["cityId"]), false);
EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["streetId"]), false);
EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["carId"]), false);
EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["passengerId1"]), false);
EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["sportsCarId"]), false);
EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["passengerId2"]), true);
// Restore default expected focus.
m_focusModeInterface->ClearFocusRoot();
}
focusModeInterface->ClearFocusRoot();
TEST_F(EditorFocusModeFixture, EditorFocusModeTests_IsInFocusSubTree_Leaf)
{
// If the root is a leaf, then the focus subtree will consists of just that entity.
{
m_focusModeInterface->SetFocusRoot(m_entityMap[Passenger2EntityName]);
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[CityEntityName]), false);
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[StreetEntityName]), false);
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[CarEntityName]), false);
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger1EntityName]), false);
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[SportsCarEntityName]), false);
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger2EntityName]), true);
}
// Restore default expected focus.
m_focusModeInterface->ClearFocusRoot();
}
TEST_F(EditorFocusModeFixture, EditorFocusModeTests_IsInFocusSubTree_Clear)
{
// Change the value from the default.
m_focusModeInterface->SetFocusRoot(m_entityMap[StreetEntityName]);
// When the focus is cleared, the whole level is in the focus subtree; so we expect all entities to return true.
{
m_focusModeInterface->ClearFocusRoot();
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[CityEntityName]), true);
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[StreetEntityName]), true);
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[CarEntityName]), true);
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger1EntityName]), true);
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[SportsCarEntityName]), true);
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger2EntityName]), true);
}
}
}
@@ -30,88 +30,127 @@ namespace UnitTest
* |_ Passenger
*/
m_entityMap["passenger1"] = CreateEntity("Passenger1");
m_entityMap["passenger2"] = CreateEntity("Passenger2");
m_entityMap["city"] = CreateEntity("City");
// Create loose entities
m_entityMap[Passenger1EntityName] = CreateEntity(Passenger1EntityName);
m_entityMap[Passenger2EntityName] = CreateEntity(Passenger2EntityName);
m_entityMap[CityEntityName] = CreateEntity(CityEntityName);
// Call HandleEntitiesAdded to the loose entities to register them with the Prefab EOS
AzToolsFramework::EditorEntityContextRequestBus::Broadcast(
&AzToolsFramework::EditorEntityContextRequests::HandleEntitiesAdded,
AzToolsFramework::EntityList{ m_entityMap["passenger1"], m_entityMap["passenger2"], m_entityMap["city"] });
AzToolsFramework::EntityList{ m_entityMap[Passenger1EntityName], m_entityMap[Passenger2EntityName], m_entityMap[CityEntityName] });
// Create a car prefab from the passenger1 entity. The container entity will be created as part of the process.
AZStd::unique_ptr<AzToolsFramework::Prefab::Instance> carInstance =
m_prefabSystemComponent->CreatePrefab({ m_entityMap["passenger1"] }, {}, "test/car");
m_prefabSystemComponent->CreatePrefab({ m_entityMap[Passenger1EntityName] }, {}, "test/car");
ASSERT_TRUE(carInstance);
m_instanceMap["car"] = carInstance.get();
m_instanceMap[CarEntityName] = carInstance.get();
// Create a sportscar prefab from the passenger2 entity. The container entity will be created as part of the process.
AZStd::unique_ptr<AzToolsFramework::Prefab::Instance> sportsCarInstance =
m_prefabSystemComponent->CreatePrefab({ m_entityMap["passenger2"] }, {}, "test/sportsCar");
m_prefabSystemComponent->CreatePrefab({ m_entityMap[Passenger2EntityName] }, {}, "test/sportsCar");
ASSERT_TRUE(sportsCarInstance);
m_instanceMap["sportsCar"] = sportsCarInstance.get();
m_instanceMap[SportsCarEntityName] = sportsCarInstance.get();
// Create a street prefab that nests the car and sportscar instances created above. The container entity will be created as part of the process.
AZStd::unique_ptr<AzToolsFramework::Prefab::Instance> streetInstance =
m_prefabSystemComponent->CreatePrefab({}, MakeInstanceList( AZStd::move(carInstance), AZStd::move(sportsCarInstance) ), "test/street");
ASSERT_TRUE(streetInstance);
m_instanceMap["street"] = streetInstance.get();
m_instanceMap[StreetEntityName] = streetInstance.get();
// Create a city prefab that nests the street instances created above and the city entity. The container entity will be created as part of the process.
m_rootInstance =
m_prefabSystemComponent->CreatePrefab({ m_entityMap["city"] }, MakeInstanceList(AZStd::move(streetInstance)), "test/city");
m_prefabSystemComponent->CreatePrefab({ m_entityMap[CityEntityName] }, MakeInstanceList(AZStd::move(streetInstance)), "test/city");
ASSERT_TRUE(m_rootInstance);
m_instanceMap["city"] = m_rootInstance.get();
m_instanceMap[CityEntityName] = m_rootInstance.get();
}
void SetUpEditorFixtureImpl() override
{
PrefabTestFixture::SetUpEditorFixtureImpl();
m_prefabFocusInterface = AZ::Interface<PrefabFocusInterface>::Get();
ASSERT_TRUE(m_prefabFocusInterface != nullptr);
GenerateTestHierarchy();
}
void TearDownEditorFixtureImpl() override
{
m_rootInstance.release();
PrefabTestFixture::TearDownEditorFixtureImpl();
}
AZStd::unordered_map<AZStd::string, AZ::Entity*> m_entityMap;
AZStd::unordered_map<AZStd::string, Instance*> m_instanceMap;
AZStd::unique_ptr<AzToolsFramework::Prefab::Instance> m_rootInstance;
PrefabFocusInterface* m_prefabFocusInterface = nullptr;
inline static const char* CityEntityName = "City";
inline static const char* StreetEntityName = "Street";
inline static const char* CarEntityName = "Car";
inline static const char* SportsCarEntityName = "SportsCar";
inline static const char* Passenger1EntityName = "Passenger1";
inline static const char* Passenger2EntityName = "Passenger2";
};
TEST_F(PrefabFocusTests, PrefabFocus_FocusOnOwningPrefab)
TEST_F(PrefabFocusTests, PrefabFocus_FocusOnOwningPrefab_RootContainer)
{
GenerateTestHierarchy();
PrefabFocusInterface* prefabFocusInterface = AZ::Interface<PrefabFocusInterface>::Get();
EXPECT_TRUE(prefabFocusInterface != nullptr);
// Verify FocusOnOwningPrefab works when passing the container entity of the root prefab.
{
prefabFocusInterface->FocusOnOwningPrefab(m_instanceMap["city"]->GetContainerEntityId());
EXPECT_EQ(prefabFocusInterface->GetFocusedPrefabTemplateId(), m_instanceMap["city"]->GetTemplateId());
m_prefabFocusInterface->FocusOnOwningPrefab(m_instanceMap[CityEntityName]->GetContainerEntityId());
EXPECT_EQ(m_prefabFocusInterface->GetFocusedPrefabTemplateId(), m_instanceMap[CityEntityName]->GetTemplateId());
auto instance = prefabFocusInterface->GetFocusedPrefabInstance();
auto instance = m_prefabFocusInterface->GetFocusedPrefabInstance();
EXPECT_TRUE(instance.has_value());
EXPECT_EQ(&instance->get(), m_instanceMap["city"]);
EXPECT_EQ(&instance->get(), m_instanceMap[CityEntityName]);
}
}
TEST_F(PrefabFocusTests, PrefabFocus_FocusOnOwningPrefab_RootEntity)
{
// Verify FocusOnOwningPrefab works when passing a nested entity of the root prefab.
{
prefabFocusInterface->FocusOnOwningPrefab(m_entityMap["city"]->GetId());
EXPECT_EQ(prefabFocusInterface->GetFocusedPrefabTemplateId(), m_instanceMap["city"]->GetTemplateId());
m_prefabFocusInterface->FocusOnOwningPrefab(m_entityMap[CityEntityName]->GetId());
EXPECT_EQ(m_prefabFocusInterface->GetFocusedPrefabTemplateId(), m_instanceMap[CityEntityName]->GetTemplateId());
auto instance = prefabFocusInterface->GetFocusedPrefabInstance();
auto instance = m_prefabFocusInterface->GetFocusedPrefabInstance();
EXPECT_TRUE(instance.has_value());
EXPECT_EQ(&instance->get(), m_instanceMap["city"]);
EXPECT_EQ(&instance->get(), m_instanceMap[CityEntityName]);
}
}
TEST_F(PrefabFocusTests, PrefabFocus_FocusOnOwningPrefab_NestedContainer)
{
// Verify FocusOnOwningPrefab works when passing the container entity of a nested prefab.
{
prefabFocusInterface->FocusOnOwningPrefab(m_instanceMap["car"]->GetContainerEntityId());
EXPECT_EQ(prefabFocusInterface->GetFocusedPrefabTemplateId(), m_instanceMap["car"]->GetTemplateId());
m_prefabFocusInterface->FocusOnOwningPrefab(m_instanceMap[CarEntityName]->GetContainerEntityId());
EXPECT_EQ(m_prefabFocusInterface->GetFocusedPrefabTemplateId(), m_instanceMap[CarEntityName]->GetTemplateId());
auto instance = prefabFocusInterface->GetFocusedPrefabInstance();
auto instance = m_prefabFocusInterface->GetFocusedPrefabInstance();
EXPECT_TRUE(instance.has_value());
EXPECT_EQ(&instance->get(), m_instanceMap["car"]);
EXPECT_EQ(&instance->get(), m_instanceMap[CarEntityName]);
}
}
TEST_F(PrefabFocusTests, PrefabFocus_FocusOnOwningPrefab_NestedEntity)
{
// Verify FocusOnOwningPrefab works when passing a nested entity of the a nested prefab.
{
prefabFocusInterface->FocusOnOwningPrefab(m_entityMap["passenger1"]->GetId());
EXPECT_EQ(prefabFocusInterface->GetFocusedPrefabTemplateId(), m_instanceMap["car"]->GetTemplateId());
m_prefabFocusInterface->FocusOnOwningPrefab(m_entityMap[Passenger1EntityName]->GetId());
EXPECT_EQ(m_prefabFocusInterface->GetFocusedPrefabTemplateId(), m_instanceMap[CarEntityName]->GetTemplateId());
auto instance = prefabFocusInterface->GetFocusedPrefabInstance();
auto instance = m_prefabFocusInterface->GetFocusedPrefabInstance();
EXPECT_TRUE(instance.has_value());
EXPECT_EQ(&instance->get(), m_instanceMap["car"]);
EXPECT_EQ(&instance->get(), m_instanceMap[CarEntityName]);
}
}
TEST_F(PrefabFocusTests, PrefabFocus_FocusOnOwningPrefab_Clear)
{
// Verify FocusOnOwningPrefab points to the root prefab when the focus is cleared.
{
AzToolsFramework::PrefabEditorEntityOwnershipInterface* prefabEditorEntityOwnershipInterface =
@@ -120,54 +159,51 @@ namespace UnitTest
prefabEditorEntityOwnershipInterface->GetRootPrefabInstance();
EXPECT_TRUE(rootPrefabInstance.has_value());
prefabFocusInterface->FocusOnOwningPrefab(AZ::EntityId());
EXPECT_EQ(prefabFocusInterface->GetFocusedPrefabTemplateId(), rootPrefabInstance->get().GetTemplateId());
m_prefabFocusInterface->FocusOnOwningPrefab(AZ::EntityId());
EXPECT_EQ(m_prefabFocusInterface->GetFocusedPrefabTemplateId(), rootPrefabInstance->get().GetTemplateId());
auto instance = prefabFocusInterface->GetFocusedPrefabInstance();
auto instance = m_prefabFocusInterface->GetFocusedPrefabInstance();
EXPECT_TRUE(instance.has_value());
EXPECT_EQ(&instance->get(), &rootPrefabInstance->get());
}
m_rootInstance.release();
}
TEST_F(PrefabFocusTests, PrefabFocus_IsOwningPrefabBeingFocused)
TEST_F(PrefabFocusTests, PrefabFocus_IsOwningPrefabBeingFocused_Content)
{
GenerateTestHierarchy();
PrefabFocusInterface* prefabFocusInterface = AZ::Interface<PrefabFocusInterface>::Get();
EXPECT_TRUE(prefabFocusInterface != nullptr);
// Verify IsOwningPrefabBeingFocused returns true for all entities in a focused prefab (container/nested)
{
prefabFocusInterface->FocusOnOwningPrefab(m_instanceMap["city"]->GetContainerEntityId());
m_prefabFocusInterface->FocusOnOwningPrefab(m_instanceMap[CityEntityName]->GetContainerEntityId());
EXPECT_TRUE(prefabFocusInterface->IsOwningPrefabBeingFocused(m_instanceMap["city"]->GetContainerEntityId()));
EXPECT_TRUE(prefabFocusInterface->IsOwningPrefabBeingFocused(m_entityMap["city"]->GetId()));
EXPECT_TRUE(m_prefabFocusInterface->IsOwningPrefabBeingFocused(m_instanceMap[CityEntityName]->GetContainerEntityId()));
EXPECT_TRUE(m_prefabFocusInterface->IsOwningPrefabBeingFocused(m_entityMap[CityEntityName]->GetId()));
}
}
TEST_F(PrefabFocusTests, PrefabFocus_IsOwningPrefabBeingFocused_AncestorsDescendants)
{
// Verify IsOwningPrefabBeingFocused returns false for all entities not in a focused prefab (ancestors/descendants)
{
prefabFocusInterface->FocusOnOwningPrefab(m_instanceMap["street"]->GetContainerEntityId());
m_prefabFocusInterface->FocusOnOwningPrefab(m_instanceMap[StreetEntityName]->GetContainerEntityId());
EXPECT_TRUE(prefabFocusInterface->IsOwningPrefabBeingFocused(m_instanceMap["street"]->GetContainerEntityId()));
EXPECT_FALSE(prefabFocusInterface->IsOwningPrefabBeingFocused(m_instanceMap["city"]->GetContainerEntityId()));
EXPECT_FALSE(prefabFocusInterface->IsOwningPrefabBeingFocused(m_entityMap["city"]->GetId()));
EXPECT_FALSE(prefabFocusInterface->IsOwningPrefabBeingFocused(m_instanceMap["car"]->GetContainerEntityId()));
EXPECT_FALSE(prefabFocusInterface->IsOwningPrefabBeingFocused(m_entityMap["passenger1"]->GetId()));
EXPECT_TRUE(m_prefabFocusInterface->IsOwningPrefabBeingFocused(m_instanceMap[StreetEntityName]->GetContainerEntityId()));
EXPECT_FALSE(m_prefabFocusInterface->IsOwningPrefabBeingFocused(m_instanceMap[CityEntityName]->GetContainerEntityId()));
EXPECT_FALSE(m_prefabFocusInterface->IsOwningPrefabBeingFocused(m_entityMap[CityEntityName]->GetId()));
EXPECT_FALSE(m_prefabFocusInterface->IsOwningPrefabBeingFocused(m_instanceMap[CarEntityName]->GetContainerEntityId()));
EXPECT_FALSE(m_prefabFocusInterface->IsOwningPrefabBeingFocused(m_entityMap[Passenger1EntityName]->GetId()));
}
}
TEST_F(PrefabFocusTests, PrefabFocus_IsOwningPrefabBeingFocused_Siblings)
{
// Verify IsOwningPrefabBeingFocused returns false for all entities not in a focused prefab (siblings)
{
prefabFocusInterface->FocusOnOwningPrefab(m_instanceMap["sportsCar"]->GetContainerEntityId());
m_prefabFocusInterface->FocusOnOwningPrefab(m_instanceMap[SportsCarEntityName]->GetContainerEntityId());
EXPECT_TRUE(prefabFocusInterface->IsOwningPrefabBeingFocused(m_instanceMap["sportsCar"]->GetContainerEntityId()));
EXPECT_TRUE(prefabFocusInterface->IsOwningPrefabBeingFocused(m_entityMap["passenger2"]->GetId()));
EXPECT_FALSE(prefabFocusInterface->IsOwningPrefabBeingFocused(m_instanceMap["car"]->GetContainerEntityId()));
EXPECT_FALSE(prefabFocusInterface->IsOwningPrefabBeingFocused(m_entityMap["passenger1"]->GetId()));
EXPECT_TRUE(m_prefabFocusInterface->IsOwningPrefabBeingFocused(m_instanceMap[SportsCarEntityName]->GetContainerEntityId()));
EXPECT_TRUE(m_prefabFocusInterface->IsOwningPrefabBeingFocused(m_entityMap[Passenger2EntityName]->GetId()));
EXPECT_FALSE(m_prefabFocusInterface->IsOwningPrefabBeingFocused(m_instanceMap[CarEntityName]->GetContainerEntityId()));
EXPECT_FALSE(m_prefabFocusInterface->IsOwningPrefabBeingFocused(m_entityMap[Passenger1EntityName]->GetId()));
}
m_rootInstance.release();
}
}
@@ -7,7 +7,9 @@
*/
#include <AzTest/AzTest.h>
#include <AzToolsFramework/ComponentMode/EditorComponentModeBus.h>
#include <AzToolsFramework/UnitTest/AzToolsFrameworkTestHelpers.h>
#include <AzToolsFramework/ViewportSelection/EditorPickEntitySelection.h>
#include <AzToolsFramework/ViewportSelection/ViewportEditorModeTracker.h>
namespace UnitTest
@@ -18,6 +20,7 @@ namespace UnitTest
using ViewportEditorModeInfo = AzToolsFramework::ViewportEditorModeInfo;
using ViewportId = ViewportEditorModeInfo::IdType;
using ViewportEditorModesInterface = AzToolsFramework::ViewportEditorModesInterface;
using ViewportEditorModeTrackerInterface = AzToolsFramework::ViewportEditorModeTrackerInterface;
void ActivateModeAndExpectSuccess(ViewportEditorModes& editorModeState, ViewportEditorMode mode)
{
@@ -47,6 +50,26 @@ namespace UnitTest
}
}
void ExpectOnlyModeActive(const ViewportEditorModesInterface& editorModeState, ViewportEditorMode mode)
{
for (auto modeIndex = 0; modeIndex < ViewportEditorModes::NumEditorModes; modeIndex++)
{
const auto currentMode = static_cast<ViewportEditorMode>(modeIndex);
const bool expectedActive = (mode == currentMode);
EXPECT_EQ(editorModeState.IsModeActive(currentMode), expectedActive);
}
}
void ExpectOnlyModeInactive(const ViewportEditorModesInterface& editorModeState, ViewportEditorMode mode)
{
for (auto modeIndex = 0; modeIndex < ViewportEditorModes::NumEditorModes; modeIndex++)
{
const auto currentMode = static_cast<ViewportEditorMode>(modeIndex);
const bool expectedActive = (mode != currentMode);
EXPECT_EQ(editorModeState.IsModeActive(currentMode), expectedActive);
}
}
// Fixture for testing editor mode states
class ViewportEditorModesTestsFixture
: public ::testing::Test
@@ -116,7 +139,7 @@ namespace UnitTest
m_editorModes[mode].m_onEnter = true;
}
virtual void OnEditorModeDeactivated([[maybe_unused]] const ViewportEditorModesInterface& editorModeState, ViewportEditorMode mode) override
void OnEditorModeDeactivated([[maybe_unused]] const ViewportEditorModesInterface& editorModeState, ViewportEditorMode mode) override
{
m_editorModes[mode].m_onExit = true;
}
@@ -152,6 +175,22 @@ namespace UnitTest
AZStd::array<AZStd::unique_ptr<ViewportEditorModeNotificationsBusHandler>, ViewportEditorModes::NumEditorModes> m_editorModeHandlers;
};
// Fixture for testing the integration of viewport editor mode state tracker
class ViewportEditorModeTrackerIntegrationTestFixture
: public ToolsApplicationFixture
{
public:
void SetUpEditorFixtureImpl() override
{
m_viewportEditorModeTracker = AZ::Interface<ViewportEditorModeTrackerInterface>::Get();
ASSERT_NE(m_viewportEditorModeTracker, nullptr);
m_viewportEditorModes = m_viewportEditorModeTracker->GetViewportEditorModes({});
}
ViewportEditorModeTrackerInterface* m_viewportEditorModeTracker = nullptr;
const ViewportEditorModesInterface* m_viewportEditorModes = nullptr;
};
TEST_F(ViewportEditorModesTestsFixture, NumberOfEditorModesIsEqualTo4)
{
EXPECT_EQ(ViewportEditorModes::NumEditorModes, 4);
@@ -168,38 +207,14 @@ namespace UnitTest
TEST_P(ViewportEditorModesTestsFixtureWithParams, SettingModeActiveActivatesOnlyThatMode)
{
ActivateModeAndExpectSuccess(m_editorModes, m_selectedEditorMode);
for (auto mode = 0; mode < ViewportEditorModes::NumEditorModes; mode++)
{
const auto editorMode = static_cast<ViewportEditorMode>(mode);
if (editorMode == m_selectedEditorMode)
{
EXPECT_TRUE(m_editorModes.IsModeActive(static_cast<ViewportEditorMode>(editorMode)));
}
else
{
EXPECT_FALSE(m_editorModes.IsModeActive(static_cast<ViewportEditorMode>(editorMode)));
}
}
ExpectOnlyModeActive(m_editorModes, m_selectedEditorMode);
}
TEST_P(ViewportEditorModesTestsFixtureWithParams, SettingModeInactiveInactivatesOnlyThatMode)
{
SetAllModesActive(m_editorModes);
DeactivateModeAndExpectSuccess(m_editorModes, m_selectedEditorMode);
for (auto mode = 0; mode < ViewportEditorModes::NumEditorModes; mode++)
{
const auto editorMode = static_cast<ViewportEditorMode>(mode);
if (editorMode == m_selectedEditorMode)
{
EXPECT_FALSE(m_editorModes.IsModeActive(editorMode));
}
else
{
EXPECT_TRUE(m_editorModes.IsModeActive(editorMode));
}
}
ExpectOnlyModeInactive(m_editorModes, m_selectedEditorMode);
}
TEST_P(ViewportEditorModesTestsFixtureWithParams, SettingMultipleModesActiveActivatesAllThoseModesNonMutuallyExclusively)
@@ -298,7 +313,7 @@ namespace UnitTest
EXPECT_EQ(m_viewportEditorModeTracker.GetTrackedViewportCount(), 0);
}
TEST_F(ViewportEditorModeTrackerTestFixture, RegisteringViewportEditorModeForNonExistentIdCreatesViewportEditorModesForThatId)
TEST_F(ViewportEditorModeTrackerTestFixture, ActivatingViewportEditorModeForNonExistentIdCreatesViewportEditorModesForThatId)
{
// Given a viewport not currently being tracked
const ViewportId viewportid = 0;
@@ -318,7 +333,7 @@ namespace UnitTest
EXPECT_TRUE(viewportEditorModeState->IsModeActive(editorMode));
}
TEST_F(ViewportEditorModeTrackerTestFixture, UnregisteringViewportEditorModeForNonExistentIdCreatesViewportEditorModesForThatIdButReturnsError)
TEST_F(ViewportEditorModeTrackerTestFixture, DeactivatingViewportEditorModeForNonExistentIdCreatesViewportEditorModesForThatIdButReturnsError)
{
// Given a viewport not currently being tracked
const ViewportId viewportid = 0;
@@ -351,7 +366,7 @@ namespace UnitTest
EXPECT_EQ(m_viewportEditorModeTracker.GetViewportEditorModes({ viewportid }), nullptr);
}
TEST_F(ViewportEditorModeTrackerTestFixture, RegisteringViewportEditorModesForExistingIdInThatStateReturnsError)
TEST_F(ViewportEditorModeTrackerTestFixture, ActivatingViewportEditorModesForExistingIdInThatStateReturnsError)
{
// Given a viewport not currently tracked
const ViewportId viewportid = 0;
@@ -390,7 +405,7 @@ namespace UnitTest
}
}
TEST_F(ViewportEditorModeTrackerTestFixture, UnregisteringViewportEditorModesForExistingIdNotInThatStateReturnssError)
TEST_F(ViewportEditorModeTrackerTestFixture, DeactivatingViewportEditorModesForExistingIdNotInThatStateReturnssError)
{
// Given a viewport not currently tracked
const ViewportId viewportid = 0;
@@ -432,7 +447,7 @@ namespace UnitTest
TEST_F(
ViewportEditorModePublisherTestFixture,
RegisteringViewportEditorModesForExistingIdPublishesOnViewportEditorModeRegisterEventForAllSubscribers)
ActivatingViewportEditorModesForExistingIdPublishesOnViewportEditorModeActivateEventForAllSubscribers)
{
// Given a set of subscribers tracking the editor modes for their exclusive viewport
for (auto mode = 0; mode < ViewportEditorModes::NumEditorModes; mode++)
@@ -465,7 +480,7 @@ namespace UnitTest
TEST_F(
ViewportEditorModePublisherTestFixture,
UnregisteringViewportEditorModesForExistingIdPublishesOnViewportEditorModeUnregisterEventForAllSubscribers)
DeactivatingViewportEditorModesForExistingIdPublishesOnViewportEditorModeDeactivatingEventForAllSubscribers)
{
// Given a set of subscribers tracking the editor modes for their exclusive viewport
for (auto mode = 0; mode < ViewportEditorModes::NumEditorModes; mode++)
@@ -495,4 +510,54 @@ namespace UnitTest
EXPECT_TRUE(expectedEditorModeSet->second.m_onExit);
}
}
TEST_F(ViewportEditorModeTrackerIntegrationTestFixture, InitialViewportEditorModeIsDefault)
{
ExpectOnlyModeActive(*m_viewportEditorModes, ViewportEditorMode::Default);
}
TEST_F(
ViewportEditorModeTrackerIntegrationTestFixture, EnteringComponentModeAfterInitialStateHasViewportEditorModesDefaultAndComponentModeActive)
{
// When component mode is entered
AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequestBus::Broadcast(
&AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequests::BeginComponentMode,
AZStd::vector<AzToolsFramework::ComponentModeFramework::EntityAndComponentModeBuilders>{});
bool inComponentMode = false;
AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequestBus::BroadcastResult(
inComponentMode, &AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequests::InComponentMode);
// Expect to be in component mode
EXPECT_TRUE(inComponentMode);
// Expect the default and component viewport editor modes to be active
EXPECT_TRUE(m_viewportEditorModes->IsModeActive(ViewportEditorMode::Default));
EXPECT_TRUE(m_viewportEditorModes->IsModeActive(ViewportEditorMode::Component));
// Do not expect the pick and focus viewport editor modes to be active
EXPECT_FALSE(m_viewportEditorModes->IsModeActive(ViewportEditorMode::Pick));
EXPECT_FALSE(m_viewportEditorModes->IsModeActive(ViewportEditorMode::Focus));
}
TEST_F(
ViewportEditorModeTrackerIntegrationTestFixture,
EnteringEditorPickEntitySelectionAfterInitialStateHasOnlyViewportEditorModePickModeActive)
{
// When entering pick mode
using AzToolsFramework::EditorInteractionSystemViewportSelectionRequestBus;
EditorInteractionSystemViewportSelectionRequestBus::Event(
AzToolsFramework::GetEntityContextId(), &EditorInteractionSystemViewportSelectionRequestBus::Events::SetHandler,
[](const AzToolsFramework::EditorVisibleEntityDataCache* entityDataCache,
[[maybe_unused]] AzToolsFramework::ViewportEditorModeTrackerInterface* viewportEditorModeTracker)
{
return AZStd::make_unique<AzToolsFramework::EditorPickEntitySelection>(entityDataCache, viewportEditorModeTracker);
});
// Expect only the pick viewport editor mode to be active
ExpectOnlyModeActive(*m_viewportEditorModes, ViewportEditorMode::Pick);
}
// FocusMode integration tests will follow (LYN-6995)
} // namespace UnitTest
@@ -127,6 +127,25 @@ namespace UnitTest
EXPECT_TRUE(button->m_state == AzToolsFramework::ViewportUi::Internal::Button::State::Selected);
}
TEST_F(ViewportUiManagerTestFixture, ClearClusterActiveButtonSetsButtonStateToDeselected)
{
// setup
auto clusterId = m_viewportManagerWrapper.GetViewportManager()->CreateCluster(AzToolsFramework::ViewportUi::Alignment::TopLeft);
auto buttonId = m_viewportManagerWrapper.GetViewportManager()->CreateClusterButton(clusterId, "");
auto clusterEntry = m_viewportManagerWrapper.GetViewportManager()->GetClusterMap().find(clusterId);
auto button = clusterEntry->second->GetButton(buttonId);
// first set a button to active
m_viewportManagerWrapper.GetViewportManager()->SetClusterActiveButton(clusterId, buttonId);
EXPECT_TRUE(button->m_state == AzToolsFramework::ViewportUi::Internal::Button::State::Selected);
// clear the active button on the cluster
m_viewportManagerWrapper.GetViewportManager()->ClearClusterActiveButton(clusterId);
// the button should now be deselected
EXPECT_TRUE(button->m_state == AzToolsFramework::ViewportUi::Internal::Button::State::Deselected);
}
TEST_F(ViewportUiManagerTestFixture, RegisterClusterEventHandlerConnectsHandlerToClusterEvent)
{
auto clusterId = m_viewportManagerWrapper.GetViewportManager()->CreateCluster(AzToolsFramework::ViewportUi::Alignment::TopLeft);
@@ -12,6 +12,8 @@ set(FILES
AssetFileInfoListComparison.cpp
AssetSeedManager.cpp
AssetSystemMocks.h
BoundsTestComponent.cpp
BoundsTestComponent.h
ComponentAdapterTests.cpp
ComponentAddRemove.cpp
ComponentModeTestDoubles.cpp
@@ -34,6 +36,9 @@ set(FILES
EntityTestbed.h
FileFunc.cpp
FingerprintingTests.cpp
FocusMode/EditorFocusModeFixture.cpp
FocusMode/EditorFocusModeFixture.h
FocusMode/EditorFocusModeSelectionTests.cpp
FocusMode/EditorFocusModeTests.cpp
GenericComponentWrapperTest.cpp
InstanceDataHierarchy.cpp
+2 -2
View File
@@ -808,7 +808,7 @@ void CSystem::OpenBasicPaks()
const char* const assetsDir = "@assets@";
// After game paks to have same search order as with files on disk
m_env.pCryPak->OpenPack(assetsDir, "Engine.pak");
m_env.pCryPak->OpenPack(assetsDir, "engine.pak");
#if defined(AZ_RESTRICTED_PLATFORM)
#define AZ_RESTRICTED_SECTION SYSTEMINIT_CPP_SECTION_15
@@ -1261,7 +1261,7 @@ AZ_POP_DISABLE_WARNING
InlineInitializationProcessing("CSystem::Init Create console");
// Need to load the engine.pak that includes the config files needed during initialization
m_env.pCryPak->OpenPack("@assets@", "Engine.pak");
m_env.pCryPak->OpenPack("@assets@", "engine.pak");
InitFileSystem_LoadEngineFolders(startupParams);
@@ -11,6 +11,7 @@
#if !defined(Q_MOC_RUN)
#include "UnitTestRunner.h"
#include "native/utilities/IniConfiguration.h"
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <QString>
#endif
@@ -21,6 +21,7 @@
#include <AzCore/IO/FileIO.h>
#include <AzCore/IO/SystemFile.h>
#include <AzCore/std/containers/unordered_set.h>
#include <AzCore/std/string/conversions.h>
#include <AzCore/StringFunc/StringFunc.h>
@@ -8,6 +8,7 @@
#include <AzCore/base.h>
#include <AzCore/EBus/EBus.h>
#include <AzCore/std/containers/unordered_set.h>
#include <AzCore/UserSettings/UserSettings.h>
#include <AzCore/Math/Vector3.h>
#include <AzCore/Memory/SystemAllocator.h>
@@ -9,6 +9,8 @@
#include <AzCore/EBus/EBus.h>
#include <memory>
namespace Aws
{
namespace CognitoIdentityProvider
@@ -9,6 +9,7 @@
#include <AzCore/EBus/EBus.h>
#include <AzCore/std/string/string.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AWSCoreBus.h>
@@ -11,6 +11,7 @@
#include <AzCore/Memory/MemoryDrillerBus.h>
#include <AzCore/Debug/AssetTrackingTypesImpl.h>
#include <AzCore/Debug/AssetTracking.h>
#include <AzCore/std/containers/unordered_set.h>
#include <AzCore/std/smart_ptr/make_shared.h>
///////////////////////////////////////////////////////////////////////////////
@@ -56,8 +56,7 @@ namespace AZ
//////////////////////////////////////////////////////////////////////////
virtual void OnBootstrapSceneReady([[maybe_unused]]AZ::RPI::Scene* bootstrapScene){}
virtual void OnFrameRateLimitChanged([[maybe_unused]]float fpsLimit){}
virtual void OnBootstrapSceneReady(AZ::RPI::Scene* bootstrapScene) = 0;
};
using NotificationBus = AZ::EBus<Notification>;
} // namespace Bootstrap
@@ -23,8 +23,6 @@ namespace AZ::Render::Bootstrap
virtual AZ::RPI::ScenePtr GetOrCreateAtomSceneFromAzScene(AzFramework::Scene* scene) = 0;
virtual bool EnsureDefaultRenderPipelineInstalledForScene(AZ::RPI::ScenePtr scene, AZ::RPI::ViewportContextPtr viewportContext) = 0;
virtual float GetFrameRateLimit() const = 0;
virtual void SetFrameRateLimit(float fpsLimit) = 0;
protected:
~Request() = default;
@@ -42,14 +42,7 @@
#include <AzCore/Console/IConsole.h>
#include <BootstrapSystemComponent_Traits_Platform.h>
static void OnFrameRateLimitChanged(const float& fpsLimit)
{
AZ::Render::Bootstrap::RequestBus::Broadcast(
&AZ::Render::Bootstrap::RequestBus::Events::SetFrameRateLimit, fpsLimit);
}
AZ_CVAR(AZ::CVarFixedString, r_default_pipeline_name, AZ_TRAIT_BOOTSTRAPSYSTEMCOMPONENT_PIPELINE_NAME, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Default Render pipeline name");
AZ_CVAR(float, r_fps_limit, 0, OnFrameRateLimitChanged, AZ::ConsoleFunctorFlags::Null, "The maximum framerate to render at, or 0 for unlimited");
namespace AZ
{
@@ -358,22 +351,6 @@ namespace AZ
return true;
}
float BootstrapSystemComponent::GetFrameRateLimit() const
{
return r_fps_limit;
}
void BootstrapSystemComponent::SetFrameRateLimit(float fpsLimit)
{
r_fps_limit = fpsLimit;
if (m_viewportContext)
{
m_viewportContext->SetFpsLimit(r_fps_limit);
}
Render::Bootstrap::NotificationBus::Broadcast(
&Render::Bootstrap::NotificationBus::Events::OnFrameRateLimitChanged, fpsLimit);
}
void BootstrapSystemComponent::CreateDefaultRenderPipeline()
{
EnsureDefaultRenderPipelineInstalledForScene(m_defaultScene, m_viewportContext);
@@ -413,11 +390,23 @@ namespace AZ
}
void BootstrapSystemComponent::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] ScriptTimePoint time)
{ }
{
// Temp: When running in the launcher without the legacy renderer
// we need to call RenderTick on the viewport context each frame.
if (m_viewportContext)
{
AZ::ApplicationTypeQuery appType;
ComponentApplicationBus::Broadcast(&AZ::ComponentApplicationBus::Events::QueryApplicationType, appType);
if (appType.IsGame())
{
m_viewportContext->RenderTick();
}
}
}
int BootstrapSystemComponent::GetTickOrder()
{
return TICK_PRE_RENDER;
return TICK_LAST;
}
void BootstrapSystemComponent::OnWindowClosed()
@@ -69,8 +69,6 @@ namespace AZ
// Render::Bootstrap::RequestBus::Handler overrides ...
AZ::RPI::ScenePtr GetOrCreateAtomSceneFromAzScene(AzFramework::Scene* scene) override;
bool EnsureDefaultRenderPipelineInstalledForScene(AZ::RPI::ScenePtr scene, AZ::RPI::ViewportContextPtr viewportContext) override;
float GetFrameRateLimit() const override;
void SetFrameRateLimit(float fpsLimit) override;
protected:
// Component overrides ...
@@ -5,7 +5,7 @@
"properties": {
"general": [
{
"id": "texcoord",
"name": "texcoord",
"displayName": "Texture Coordinate Stream",
"description": "Which UV channel to use when sampling textures.",
"type": "Int",
@@ -14,75 +14,75 @@
"max": 8
},
{
"id": "enableShadows",
"name": "enableShadows",
"displayName": "Enable Shadows",
"description": "Whether to use the shadow maps.",
"type": "Bool",
"defaultValue": false,
"connection": {
"type": "ShaderOption",
"id": "o_enableShadows"
"name": "o_enableShadows"
}
},
{
"id": "enableDirectionalLights",
"name": "enableDirectionalLights",
"displayName": "Enable Directional Lights",
"description": "Whether to use directional lights.",
"type": "Bool",
"defaultValue": false,
"connection": {
"type": "ShaderOption",
"id": "o_enableDirectionalLights"
"name": "o_enableDirectionalLights"
}
},
{
"id": "enablePunctualLights",
"name": "enablePunctualLights",
"displayName": "Enable Punctual Lights",
"description": "Whether to use punctual lights.",
"type": "Bool",
"defaultValue": false,
"connection": {
"type": "ShaderOption",
"id": "o_enablePunctualLights"
"name": "o_enablePunctualLights"
}
},
{
"id": "enableAreaLights",
"name": "enableAreaLights",
"displayName": "Enable Area Lights",
"description": "Whether to use area lights.",
"type": "Bool",
"defaultValue": false,
"connection": {
"type": "ShaderOption",
"id": "o_enableAreaLights"
"name": "o_enableAreaLights"
}
},
{
"id": "enableIBL",
"name": "enableIBL",
"displayName": "Enable IBL",
"description": "Whether to use Image Based Lighting (IBL).",
"type": "Bool",
"defaultValue": true,
"connection": {
"type": "ShaderOption",
"id": "o_enableIBL"
"name": "o_enableIBL"
}
}
],
"baseColor": [
{
"id": "color",
"name": "color",
"displayName": "Color",
"description": "Color is displayed as sRGB but the values are stored as linear color.",
"type": "Color",
"defaultValue": [ 1.0, 1.0, 1.0 ],
"connection": {
"type": "ShaderInput",
"id": "m_baseColor"
"name": "m_baseColor"
}
},
{
"id": "factor",
"name": "factor",
"displayName": "Factor",
"description": "Strength factor for scaling the base color values. Zero (0.0) is black, white (1.0) is full color.",
"type": "Float",
@@ -91,31 +91,31 @@
"max": 1.0,
"connection": {
"type": "ShaderInput",
"id": "m_baseColorFactor"
"name": "m_baseColorFactor"
}
},
{
"id": "useTexture",
"name": "useTexture",
"displayName": "Use Texture",
"description": "Whether to use the texture map.",
"type": "Bool",
"defaultValue": true
},
{
"id": "textureMap",
"name": "textureMap",
"displayName": "Texture Map",
"description": "Base color texture map",
"type": "Image",
"defaultValue": "Textures/Default/default_basecolor.tif",
"connection": {
"type": "ShaderInput",
"id": "m_baseColorMap"
"name": "m_baseColorMap"
}
}
],
"metallic": [
{
"id": "factor",
"name": "factor",
"displayName": "Factor",
"description": "This value is linear, black is non-metal and white means raw metal.",
"type": "Float",
@@ -124,30 +124,30 @@
"max": 1.0,
"connection": {
"type": "ShaderInput",
"id": "m_metallicFactor"
"name": "m_metallicFactor"
}
},
{
"id": "useTexture",
"name": "useTexture",
"displayName": "Use Texture",
"description": "Whether to use the texture map, or just default to the Factor value.",
"type": "Bool",
"defaultValue": true
},
{
"id": "textureMap",
"name": "textureMap",
"displayName": "Texture Map",
"description": "",
"type": "Image",
"connection": {
"type": "ShaderInput",
"id": "m_metallicMap"
"name": "m_metallicMap"
}
}
],
"roughness": [
{
"id": "factor",
"name": "factor",
"displayName": "Factor",
"description": "Strength factor for scaling the values",
"type": "Float",
@@ -156,31 +156,31 @@
"max": 1.0,
"connection": {
"type": "ShaderInput",
"id": "m_roughnessFactor"
"name": "m_roughnessFactor"
}
},
{
"id": "useTexture",
"name": "useTexture",
"displayName": "Use Texture",
"description": "Whether to use the texture map, or just default to the Factor value.",
"type": "Bool",
"defaultValue": true
},
{
"id": "textureMap",
"name": "textureMap",
"displayName": "Texture Map",
"description": "Texture map for defining surface roughness.",
"type": "Image",
"defaultValue": "Textures/Default/default_roughness.tif",
"connection": {
"type": "ShaderInput",
"id": "m_roughnessMap"
"name": "m_roughnessMap"
}
}
],
"specularF0": [
{
"id": "factor",
"name": "factor",
"displayName": "Factor",
"description": "The default IOR is 1.5, which gives you 0.04 (4% of light reflected at 0 degree angle for dielectric materials). F0 values lie in the range 0-0.08, so that is why the default F0 slider is set on 0.5.",
"type": "Float",
@@ -189,51 +189,51 @@
"max": 1.0,
"connection": {
"type": "ShaderInput",
"id": "m_specularF0Factor"
"name": "m_specularF0Factor"
}
},
{
"id": "useTexture",
"name": "useTexture",
"displayName": "Use Texture",
"description": "Whether to use the texture map, or just default to the Factor value.",
"type": "Bool",
"defaultValue": true
},
{
"id": "textureMap",
"name": "textureMap",
"displayName": "Texture Map",
"description": "Texture map for defining surface reflectance.",
"type": "Image",
"connection": {
"type": "ShaderInput",
"id": "m_specularF0Map"
"name": "m_specularF0Map"
}
},
{
"id": "applySpecularAA",
"name": "applySpecularAA",
"displayName": "Apply Specular AA",
"description": "Whether to apply specular anti-aliasing in the shader.",
"type": "Bool",
"defaultValue": true,
"connection": {
"type": "ShaderOption",
"id": "o_applySpecularAA"
"name": "o_applySpecularAA"
}
},
{
"id": "enableMultiScatterCompensation",
"name": "enableMultiScatterCompensation",
"displayName": "Multiscattering Compensation",
"description": "Whether to enable multiple scattering compensation.",
"type": "Bool",
"connection": {
"type": "ShaderOption",
"id": "o_specularF0_enableMultiScatterCompensation"
"name": "o_specularF0_enableMultiScatterCompensation"
}
}
],
"normal": [
{
"id": "factor",
"name": "factor",
"displayName": "Factor",
"description": "Strength factor for scaling the values",
"type": "Float",
@@ -243,85 +243,85 @@
"max": 2.0,
"connection": {
"type": "ShaderInput",
"id": "m_normalFactor"
"name": "m_normalFactor"
}
},
{
"id": "useTexture",
"name": "useTexture",
"displayName": "Use Texture",
"description": "Whether to use the texture map, or just rely on vertex normals.",
"type": "Bool",
"defaultValue": true
},
{
"id": "textureMap",
"name": "textureMap",
"displayName": "Texture Map",
"description": "Texture map for defining surface normal direction.",
"type": "Image",
"defaultValue": "Textures/Default/default_normal.tif",
"connection": {
"type": "ShaderInput",
"id": "m_normalMap"
"name": "m_normalMap"
}
},
{
"id": "flipX",
"name": "flipX",
"displayName": "Flip X Channel",
"description": "Flip tangent direction for this normal map.",
"type": "Bool",
"defaultValue": false,
"connection": {
"type": "ShaderInput",
"id": "m_flipNormalX"
"name": "m_flipNormalX"
}
},
{
"id": "flipY",
"name": "flipY",
"displayName": "Flip Y Channel",
"description": "Flip bitangent direction for this normal map.",
"type": "Bool",
"defaultValue": false,
"connection": {
"type": "ShaderInput",
"id": "m_flipNormalY"
"name": "m_flipNormalY"
}
}
],
"opacity": [
{
"id": "mode",
"name": "mode",
"displayName": "Opacity Mode",
"description": "Opacity mode for this texture. 0: Opaque, 1: Cutout, 2:Blended",
"type": "Uint",
"defaultValue": 0,
"connection": {
"type": "ShaderOption",
"id": "o_opacity_mode"
"name": "o_opacity_mode"
}
},
{
"id": "alphaSource",
"name": "alphaSource",
"displayName": "Alpha Source",
"description": "Source texture of alpha value. 0:Packed, 1:Split, 2:None",
"type": "Uint",
"defaultValue": 0,
"connection": {
"type": "ShaderOption",
"id": "o_opacity_source"
"name": "o_opacity_source"
}
},
{
"id": "textureMap",
"name": "textureMap",
"displayName": "Texture Map",
"description": "Texture map for defining surface opacity.",
"type": "Image",
"connection": {
"type": "ShaderInput",
"id": "m_opacityMap"
"name": "m_opacityMap"
}
},
{
"id": "factor",
"name": "factor",
"displayName": "Factor",
"description": "Factor for cutout threshold and blending",
"type": "Float",
@@ -330,11 +330,11 @@
"defaultValue": 0.5,
"connection": {
"type": "ShaderInput",
"id": "m_opacityFactor"
"name": "m_opacityFactor"
}
},
{
"id": "doubleSided",
"name": "doubleSided",
"displayName": "Double-sided",
"description": "Whether to render back-faces or just front-faces.",
"type": "Bool"
@@ -342,14 +342,14 @@
],
"uv": [
{
"id": "center",
"name": "center",
"displayName": "Center",
"description": "Center point for scaling and rotation transformations.",
"type": "vector2",
"defaultValue": [0.0, 0.0]
},
{
"id": "tileU",
"name": "tileU",
"displayName": "Tile U",
"description": "Scales texture coordinates in V.",
"type": "float",
@@ -357,7 +357,7 @@
"step": 0.1
},
{
"id": "tileV",
"name": "tileV",
"displayName": "Tile V",
"description": "Scales texture coordinates in V.",
"type": "float",
@@ -365,7 +365,7 @@
"step": 0.1
},
{
"id": "offsetU",
"name": "offsetU",
"displayName": "Offset U",
"description": "Offsets texture coordinates in the U direction.",
"type": "float",
@@ -374,7 +374,7 @@
"max": 1.0
},
{
"id": "offsetV",
"name": "offsetV",
"displayName": "Offset V",
"description": "Offsets texture coordinates in the V direction.",
"type": "float",
@@ -383,7 +383,7 @@
"max": 1.0
},
{
"id": "rotateDegrees",
"name": "rotateDegrees",
"displayName": "Rotate",
"description": "Rotates the texture coordinates (degrees).",
"type": "float",
@@ -393,7 +393,7 @@
"step": 1.0
},
{
"id": "scale",
"name": "scale",
"displayName": "Scale",
"description": "Scales texture coordinates in both U and V.",
"type": "float",
@@ -403,29 +403,29 @@
],
"emissive": [
{
"id": "enable",
"name": "enable",
"displayName": "Enable",
"description": "Enable the emissive group",
"type":"Bool",
"defaultValue": false,
"connection": {
"type": "ShaderOption",
"id": "o_emissiveEnabled"
"name": "o_emissiveEnabled"
}
},
{
"id": "color",
"name": "color",
"displayName": "Color",
"description": "Color is displayed as sRGB but the values are stored as linear color.",
"type": "Color",
"defaultValue": [ 1.0, 1.0, 1.0 ],
"connection": {
"type": "ShaderInput",
"id": "m_emissiveColor"
"name": "m_emissiveColor"
}
},
{
"id": "intensity",
"name": "intensity",
"displayName": "Intensity",
"description": "The amount of energy emitted, in EV100 unit",
"type": "Float",
@@ -434,33 +434,33 @@
"max": 5
},
{
"id": "useTexture",
"name": "useTexture",
"displayName": "Use Texture",
"description": "Whether to use the texture map.",
"type": "Bool",
"defaultValue": false
},
{
"id": "textureMap",
"name": "textureMap",
"displayName": "Texture Map",
"description": "Texture map for defining emissive area.",
"type": "Image",
"connection": {
"type": "ShaderInput",
"id": "m_emissiveMap"
"name": "m_emissiveMap"
}
}
],
"parallax": [
{
"id": "enable",
"name": "enable",
"displayName": "Enable",
"description": "Whether to enable the parallax feature.",
"type": "Bool",
"defaultValue": false
},
{
"id": "factor",
"name": "factor",
"displayName": "Factor",
"description": "Strength factor for scaling the depth values",
"type": "Float",
@@ -469,39 +469,39 @@
"max": 1.0,
"connection": {
"type": "ShaderInput",
"id": "m_heightmapScale"
"name": "m_heightmapScale"
}
},
{
"id": "textureMap",
"name": "textureMap",
"displayName": "Texture Map",
"description": "Depthmap to create parallax effect.",
"type": "Image",
"connection": {
"type": "ShaderInput",
"id": "m_heightmap"
"name": "m_heightmap"
}
},
{
"id": "algorithm",
"name": "algorithm",
"displayName": "Algorithm",
"description": "Select the algorithm to use for parallax mapping. 0: Basic, 1:Steep, 2:POM, 3:Relief, 4:Contact refinement",
"type": "Uint",
"defaultValue": 0,
"connection":{
"type": "ShaderOption",
"id": "o_parallax_algorithm"
"name": "o_parallax_algorithm"
}
},
{
"id": "quality",
"name": "quality",
"displayName": "Quality",
"description": "Quality of parallax mapping. 0:Low, 1:Medium, 2:High, 3:Ultra",
"type": "Uint",
"defaultValue": 0,
"connection":{
"type": "ShaderOption",
"id": "o_parallax_quality"
"name": "o_parallax_quality"
}
}
]
@@ -5,7 +5,7 @@
"properties": {
"settings": [
{
"id": "opacity",
"name": "opacity",
"displayName": "Opacity",
"description": "Opacity of the shadow effect.",
"type": "Float",
@@ -14,17 +14,17 @@
"max": 1.0,
"connection": {
"type": "ShaderInput",
"id": "m_opacity"
"name": "m_opacity"
}
},
{
"id": "shadeAll",
"name": "shadeAll",
"displayName": "Shade All",
"description": "Shades the entire geometry with the shadow color, not just what's in shadow. For debugging.",
"type": "Bool",
"connection": {
"type": "ShaderOption",
"id": "o_shadeAll"
"name": "o_shadeAll"
}
}
]
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -39,13 +39,24 @@ namespace AZ
//! Otherwise an attempt will be made to find or create a shared instance.
void RebuildInstance();
//! Release asset and instance references
void Release();
//! Return true if contained assets have not been loaded
bool RequiresLoading() const;
//! Applies property overrides to material instance
bool ApplyProperties();
//! Returns a string composed of the asset path.
AZStd::string ToString() const;
Data::Asset<RPI::MaterialAsset> m_materialAsset;
Data::Asset<RPI::MaterialAsset> m_defaultMaterialAsset;
Data::Instance<RPI::Material> m_materialInstance;
MaterialPropertyOverrideMap m_propertyOverrides;
RPI::MaterialModelUvOverrideMap m_matModUvOverrides;
bool m_materialInstancePreCreated = false;
};
using MaterialAssignmentMap = AZStd::unordered_map<MaterialAssignmentId, MaterialAssignment>;
@@ -71,9 +71,9 @@ namespace AZ
}
MaterialAssignment::MaterialAssignment(const AZ::Data::AssetId& materialAssetId)
: m_materialInstance()
: m_materialAsset(materialAssetId, AZ::AzTypeInfo<AZ::RPI::MaterialAsset>::Uuid())
, m_materialInstance()
{
m_materialAsset.Create(materialAssetId);
}
MaterialAssignment::MaterialAssignment(const Data::Asset<RPI::MaterialAsset>& asset)
@@ -90,12 +90,70 @@ namespace AZ
void MaterialAssignment::RebuildInstance()
{
if (m_materialInstancePreCreated)
{
return;
}
if (m_materialAsset.IsReady())
{
m_materialInstance =
m_propertyOverrides.empty() ? RPI::Material::FindOrCreate(m_materialAsset) : RPI::Material::Create(m_materialAsset);
m_materialInstance = m_propertyOverrides.empty() ? RPI::Material::FindOrCreate(m_materialAsset) : RPI::Material::Create(m_materialAsset);
AZ_Error("MaterialAssignment", m_materialInstance, "Material instance not initialized");
}
else if (m_defaultMaterialAsset.IsReady())
{
m_materialInstance = m_propertyOverrides.empty() ? RPI::Material::FindOrCreate(m_defaultMaterialAsset) : RPI::Material::Create(m_defaultMaterialAsset);
AZ_Error("MaterialAssignment", m_materialInstance, "Material instance not initialized");
}
}
void MaterialAssignment::Release()
{
if (!m_materialInstancePreCreated)
{
m_materialInstance = nullptr;
}
m_materialAsset.Release();
m_defaultMaterialAsset.Release();
}
bool MaterialAssignment::RequiresLoading() const
{
return
!m_materialInstancePreCreated &&
!m_materialAsset.IsReady() &&
!m_materialAsset.IsLoading() &&
!m_defaultMaterialAsset.IsReady() &&
!m_defaultMaterialAsset.IsLoading();
}
bool MaterialAssignment::ApplyProperties()
{
// if there is no instance or no properties there's nothing to apply
if (!m_materialInstance || m_propertyOverrides.empty())
{
return true;
}
if (m_materialInstance->CanCompile())
{
for (const auto& propertyPair : m_propertyOverrides)
{
if (!propertyPair.second.empty())
{
const auto& materialPropertyIndex = m_materialInstance->FindPropertyIndex(propertyPair.first);
if (!materialPropertyIndex.IsNull())
{
m_materialInstance->SetPropertyValue(
materialPropertyIndex, AZ::RPI::MaterialPropertyValue::FromAny(propertyPair.second));
}
}
}
return m_materialInstance->Compile();
}
return false;
}
AZStd::string MaterialAssignment::ToString() const
@@ -11,6 +11,7 @@
#include <AzCore/std/parallel/threadbus.h>
#include <AzCore/std/parallel/shared_mutex.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
namespace AZ
{
@@ -0,0 +1,38 @@
/*
* 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 <Atom/RPI.Edit/Material/MaterialTypeSourceData.h>
#include <AzCore/Serialization/Json/BaseJsonSerializer.h>
namespace AZ
{
class ReflectContext;
namespace RPI
{
//! The property connection itself is rather simple, but we need this custom serializer to provide backward compatibility
//! for when the "id" key was changed to "name". If the JSON serialization system is ever updated to provide built-in
//! support for versioning, then we can probably remove this class.
class JsonMaterialPropertyConnectionSerializer
: public BaseJsonSerializer
{
public:
AZ_RTTI(JsonMaterialPropertyConnectionSerializer, "{2B7F00CF-51F7-4409-9C0E-914E59696FB9}", BaseJsonSerializer);
AZ_CLASS_ALLOCATOR_DECL;
JsonSerializationResult::Result Load(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue,
JsonDeserializerContext& context) override;
JsonSerializationResult::Result Store(rapidjson::Value& outputValue, const void* inputValue,
const void* defaultValue, const Uuid& valueTypeId, JsonSerializerContext& context) override;
};
} // namespace RPI
} // namespace AZ

Some files were not shown because too many files have changed in this diff Show More