Merge branch 'development' into cmake/warn_virtual

Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com>
This commit is contained in:
Esteban Papp
2021-09-09 11:50:40 -07:00
248 changed files with 9491 additions and 10706 deletions
@@ -213,7 +213,7 @@ namespace AZ
void AssetData::Acquire()
{
AZ_Assert(m_useCount >= 0, "AssetData has been deleted")
AZ_Assert(m_useCount >= 0, "AssetData has been deleted");
AcquireWeak();
++m_useCount;
@@ -990,7 +990,7 @@ namespace AZ
template<class T>
u8 Asset<T>::GetFlags() const
{
AZ_Warning("Asset", false, "Deprecated - replaced by GetAutoLoadBehavior")
AZ_Warning("Asset", false, "Deprecated - replaced by GetAutoLoadBehavior");
return static_cast<u8>(m_loadBehavior);
}
@@ -1012,7 +1012,7 @@ namespace AZ
template<class T>
bool Asset<T>::SetFlags(u8 flags)
{
AZ_Warning("Asset", false, "Deprecated - replaced by SetAutoLoadBehavior")
AZ_Warning("Asset", false, "Deprecated - replaced by SetAutoLoadBehavior");
if (!m_assetData)
{
AZ_Assert(flags < static_cast<u8>(AssetLoadBehavior::Count), "Flags value is out of range");
@@ -2132,7 +2132,7 @@ namespace AZ
}
else
{
AZ_Warning("AssetManager", false, "Couldn't find handler for asset %s (%s)", asset.GetId().ToString<AZStd::string>().c_str(), asset.GetHint().c_str())
AZ_Warning("AssetManager", false, "Couldn't find handler for asset %s (%s)", asset.GetId().ToString<AZStd::string>().c_str(), asset.GetHint().c_str());
}
// Notify any dependent jobs.
+12 -12
View File
@@ -8,8 +8,7 @@
#pragma once
#include <AzCore/PlatformDef.h>
#define AZ_VA_HAS_ARGS(...) ""#__VA_ARGS__[0] != 0
#include <AzCore/base.h>
namespace AZ
{
@@ -262,17 +261,18 @@ namespace AZ
#define AZ_VerifyWarning(window, expression, ...) AZ_Warning(window, 0 != (expression), __VA_ARGS__)
#else // !AZ_ENABLE_TRACING
#define AZ_Assert(expression, ...)
#define AZ_Error(window, expression, ...)
#define AZ_ErrorOnce(window, expression, ...)
#define AZ_Warning(window, expression, ...)
#define AZ_WarningOnce(window, expression, ...)
#define AZ_TracePrintf(window, ...)
#define AZ_TracePrintfOnce(window, ...)
#define AZ_Verify(expression, ...) (void)(expression)
#define AZ_VerifyError(window, expression, ...) (void)(expression)
#define AZ_VerifyWarning(window, expression, ...) (void)(expression)
#define AZ_Assert(...) AZ_UNUSED(__VA_ARGS__);
#define AZ_Error(...) AZ_UNUSED(__VA_ARGS__);
#define AZ_ErrorOnce(...) AZ_UNUSED(__VA_ARGS__);
#define AZ_Warning(...) AZ_UNUSED(__VA_ARGS__);
#define AZ_WarningOnce(...) AZ_UNUSED(__VA_ARGS__);
#define AZ_TracePrintf(...) AZ_UNUSED(__VA_ARGS__);
#define AZ_TracePrintfOnce(...) AZ_UNUSED(__VA_ARGS__);
#define AZ_Verify(...) AZ_UNUSED(__VA_ARGS__);
#define AZ_VerifyError(...) AZ_UNUSED(__VA_ARGS__);
#define AZ_VerifyWarning(...) AZ_UNUSED(__VA_ARGS__);
#endif // AZ_ENABLE_TRACING
@@ -693,7 +693,7 @@ namespace AZ
}
else
{
AZ_Assert(m_isPooledString == false && m_isPooledStringCrc32 == false, "This stream requires using of a string pool as the string is send only once and afterwards only the Crc32 is used!")
AZ_Assert(m_isPooledString == false && m_isPooledStringCrc32 == false, "This stream requires using of a string pool as the string is send only once and afterwards only the Crc32 is used!");
}
return srcData;
}
@@ -150,9 +150,7 @@ namespace AZ::IO::IStreamerTypes
private:
AZStd::atomic_int m_lockCounter{ 0 };
#ifdef AZ_ENABLE_TRACING
AZStd::atomic_int m_allocationCounter{ 0 };
#endif
AZ::IAllocatorAllocate& m_allocator;
};
@@ -222,7 +222,7 @@ namespace AZ
int AddRefCount(int value)
{
AZ_Assert(value == 1 || value == -1, "ModRefCount is only for incrementing or decrementing on copy or destruction of ExposedLambda")
AZ_Assert(value == 1 || value == -1, "ModRefCount is only for incrementing or decrementing on copy or destruction of ExposedLambda");
lua_rawgeti(m_lua, LUA_REGISTRYINDEX, m_refCountRegistryIndex);
// Lua: refCount-old
const int refCount = Internal::azlua_tointeger(m_lua, -1) + value;
@@ -240,11 +240,6 @@ namespace AZ
{
IO::SizeType length = stream.GetLength();
if (length > AZ::Utils::DefaultMaxFileSize)
{
return AZ::Failure(AZStd::string{ "Data is too large." });
}
AZStd::vector<char> memoryBuffer;
memoryBuffer.resize_no_construct(static_cast<AZStd::vector<char>::size_type>(static_cast<AZStd::vector<char>::size_type>(length) + 1));
@@ -259,12 +254,12 @@ namespace AZ
return ReadJsonString(AZStd::string_view{memoryBuffer.data(), memoryBuffer.size()});
}
AZ::Outcome<rapidjson::Document, AZStd::string> ReadJsonFile(AZStd::string_view filePath)
AZ::Outcome<rapidjson::Document, AZStd::string> ReadJsonFile(AZStd::string_view filePath, size_t maxFileSize)
{
// Read into memory first and then parse the json, rather than passing a file stream to rapidjson.
// This should avoid creating a large number of micro-reads from the file.
auto readResult = AZ::Utils::ReadFile<AZStd::string>(filePath);
auto readResult = AZ::Utils::ReadFile<AZStd::string>(filePath, maxFileSize);
if(!readResult.IsSuccess())
{
return AZ::Failure(readResult.GetError());
@@ -308,6 +303,55 @@ namespace AZ
return AZ::Success();
}
AZ::Outcome<void, AZStd::string> LoadObjectFromStringByType(void* objectToLoad, const Uuid& classId, AZStd::string_view stream,
const JsonDeserializerSettings* settings)
{
JsonDeserializerSettings loadSettings;
AZStd::string deserializeErrors;
auto prepare = PrepareDeserializerSettings(settings, loadSettings, deserializeErrors);
if (!prepare.IsSuccess())
{
return AZ::Failure(prepare.GetError());
}
auto parseResult = ReadJsonString(stream);
if (!parseResult.IsSuccess())
{
return AZ::Failure(parseResult.GetError());
}
const rapidjson::Document& jsonDocument = parseResult.GetValue();
auto validateResult = ValidateJsonClassHeader(jsonDocument);
if (!validateResult.IsSuccess())
{
return AZ::Failure(validateResult.GetError());
}
const char* className = jsonDocument.FindMember(ClassNameTag)->value.GetString();
// validate class name
auto classData = loadSettings.m_serializeContext->FindClassData(classId);
if (!classData)
{
return AZ::Failure(AZStd::string::format("Try to load class from Id %s", classId.ToString<AZStd::string>().c_str()));
}
if (azstricmp(classData->m_name, className) != 0)
{
return AZ::Failure(AZStd::string::format("Try to load class %s from class %s data", classData->m_name, className));
}
JsonSerializationResult::ResultCode result = JsonSerialization::Load(objectToLoad, classId, jsonDocument.FindMember(ClassDataTag)->value, loadSettings);
if (!WasLoadSuccess(result.GetOutcome()) || !deserializeErrors.empty())
{
return AZ::Failure(deserializeErrors);
}
return AZ::Success();
}
AZ::Outcome<void, AZStd::string> LoadObjectFromStreamByType(void* objectToLoad, const Uuid& classId, IO::GenericStream& stream,
const JsonDeserializerSettings* settings)
{
@@ -70,13 +70,18 @@ namespace AZ
//! Parse json text. Returns a failure with error message if the content is not valid JSON.
AZ::Outcome<rapidjson::Document, AZStd::string> ReadJsonString(AZStd::string_view jsonText);
//! Parse a json file. Returns a failure with error message if the content is not valid JSON.
AZ::Outcome<rapidjson::Document, AZStd::string> ReadJsonFile(AZStd::string_view filePath);
//! Parse a json file. Returns a failure with error message if the content is not valid JSON or if
//! the file size is larger than the max file size provided.
AZ::Outcome<rapidjson::Document, AZStd::string> ReadJsonFile(
AZStd::string_view filePath, size_t maxFileSize = AZStd::numeric_limits<size_t>::max());
//! Parse a json stream. Returns a failure with error message if the content is not valid JSON.
AZ::Outcome<rapidjson::Document, AZStd::string> ReadJsonStream(IO::GenericStream& stream);
//! Load object with known class type
AZ::Outcome<void, AZStd::string> LoadObjectFromStringByType(void* objectToLoad, const Uuid& objectType, AZStd::string_view source,
const JsonDeserializerSettings* settings = nullptr);
AZ::Outcome<void, AZStd::string> LoadObjectFromStreamByType(void* objectToLoad, const Uuid& objectType, IO::GenericStream& stream,
const JsonDeserializerSettings* settings = nullptr);
@@ -293,7 +293,7 @@ namespace AZ
}
AZ_Assert(!keyValues.Empty(), "Intermediate array for associative container can't be empty "
"because an empty array would be stored as an empty default object.")
"because an empty array would be stored as an empty default object.");
if (CanBeConvertedToObject(keyValues))
{
@@ -2300,7 +2300,7 @@ namespace AZ
{
if (classData->m_converter)
{
AZ_Assert(false, "A deprecated element with a data converter was passed to CloneObject, this is not supported.")
AZ_Assert(false, "A deprecated element with a data converter was passed to CloneObject, this is not supported.");
}
// push a dummy node in the stack
cloneData->m_parentStack.push_back();
@@ -110,12 +110,12 @@ namespace AZ::Internal
{
FixedValueString engineName;
settingsRegistry.Get(engineName, engineMonikerKey);
AZ_Warning("SettingsRegistryMergeUtils",engineInfo.m_moniker == engineName,
AZ_Warning("SettingsRegistryMergeUtils", engineInfo.m_moniker == engineName,
R"(The engine name key "%s" mapped to engine path "%s" within the global manifest of "%s")"
R"( does not match the "engine_name" field "%s" in the engine.json)" "\n"
"This engine should be re-registered.",
engineInfo.m_moniker.c_str(), engineInfo.m_path.c_str(), engineManifestPath.c_str(),
engineName.c_str())
engineName.c_str());
engineInfo.m_moniker = engineName;
}
}
@@ -3464,7 +3464,7 @@ namespace AZ
const SliceComponent::DataFlagsPerEntity* SliceComponent::GetCorrectBundleOfDataFlags(EntityId entityId) const
{
// It would be possible to search non-instantiated slices by crawling over lists, but we haven't needed the capability yet.
AZ_Assert(IsInstantiated(), "Data flag access is only permitted after slice is instantiated.")
AZ_Assert(IsInstantiated(), "Data flag access is only permitted after slice is instantiated.");
if (IsInstantiated())
{
+2 -5
View File
@@ -22,10 +22,6 @@ namespace AZ
{
namespace Utils
{
//! Protects from allocating too much memory. The choice of a 1MB threshold is arbitrary.
//! If you need to work with larger files, please use AZ::IO directly instead of these utility functions.
inline constexpr size_t DefaultMaxFileSize = 1024 * 1024;
//! Terminates the application without going through the shutdown procedure.
//! This is used when due to abnormal circumstances the application can no
//! longer continue. On most platforms and in most configurations this will
@@ -115,6 +111,7 @@ namespace AZ
//! Read a file into a string. Returns a failure with error message if the content could not be loaded or if
//! the file size is larger than the max file size provided.
template<typename Container = AZStd::string>
AZ::Outcome<Container, AZStd::string> ReadFile(AZStd::string_view filePath, size_t maxFileSize = DefaultMaxFileSize);
AZ::Outcome<Container, AZStd::string> ReadFile(
AZStd::string_view filePath, size_t maxFileSize = AZStd::numeric_limits<size_t>::max());
}
}
+26 -10
View File
@@ -143,6 +143,9 @@
* example. AZ_VA_NUM_ARGS(x,y,z) -> expands to 3
*/
#ifndef AZ_VA_NUM_ARGS
# define AZ_VA_HAS_ARGS(...) ""#__VA_ARGS__[0] != 0
// we add the zero to avoid the case when we require at least 1 param at the end...
# define AZ_VA_NUM_ARGS(...) AZ_VA_NUM_ARGS_IMPL_((__VA_ARGS__, 125, 124, 123, 122, 121, 120, 119, 118, 117, 116, 115, 114, 113, 112, 111, 110, 109, 108, 107, 106, 105, 104, 103, 102, 101, 100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87, 86, 85, 84, 83, 82, 81, 80, 79, 78, 77, 76, 75, 74, 73, 72, 71, 70, 69, 68, 67, 66, 65, 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0))
# define AZ_VA_NUM_ARGS_IMPL_(tuple) AZ_VA_NUM_ARGS_IMPL tuple
@@ -170,15 +173,15 @@
// This is a pain they we use macros to call functions (with no params).
// we implement functions for up to 10 params
#define AZ_FUNCTION_CALL_1(_1) _1()
#define AZ_FUNCTION_CALL_2(_1, _2) _1(_2)
#define AZ_FUNCTION_CALL_3(_1, _2, _3) _1(_2, _3)
#define AZ_FUNCTION_CALL_4(_1, _2, _3, _4) _1(_2, _3, _4)
#define AZ_FUNCTION_CALL_5(_1, _2, _3, _4, _5) _1(_2, _3, _4, _5)
#define AZ_FUNCTION_CALL_6(_1, _2, _3, _4, _5, _6) _1(_2, _3, _4, _5, _6)
#define AZ_FUNCTION_CALL_7(_1, _2, _3, _4, _5, _6, _7) _1(_2, _3, _4, _5, _6, _7)
#define AZ_FUNCTION_CALL_8(_1, _2, _3, _4, _5, _6, _7, _8) _1(_2, _3, _4, _5, _6, _7, _8)
#define AZ_FUNCTION_CALL_9(_1, _2, _3, _4, _5, _6, _7, _8, _9) _1(_2, _3, _4, _5, _6, _7, _8, _9)
#define AZ_FUNCTION_CALL_1(_1) _1()
#define AZ_FUNCTION_CALL_2(_1, _2) _1(_2)
#define AZ_FUNCTION_CALL_3(_1, _2, _3) _1(_2, _3)
#define AZ_FUNCTION_CALL_4(_1, _2, _3, _4) _1(_2, _3, _4)
#define AZ_FUNCTION_CALL_5(_1, _2, _3, _4, _5) _1(_2, _3, _4, _5)
#define AZ_FUNCTION_CALL_6(_1, _2, _3, _4, _5, _6) _1(_2, _3, _4, _5, _6)
#define AZ_FUNCTION_CALL_7(_1, _2, _3, _4, _5, _6, _7) _1(_2, _3, _4, _5, _6, _7)
#define AZ_FUNCTION_CALL_8(_1, _2, _3, _4, _5, _6, _7, _8) _1(_2, _3, _4, _5, _6, _7, _8)
#define AZ_FUNCTION_CALL_9(_1, _2, _3, _4, _5, _6, _7, _8, _9) _1(_2, _3, _4, _5, _6, _7, _8, _9)
#define AZ_FUNCTION_CALL_10(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10) _1(_2, _3, _4, _5, _6, _7, _8, _9, _10)
// We require at least 1 param FunctionName
@@ -293,7 +296,20 @@ namespace AZ
#define AZ_DEFAULT_COPY_MOVE(_Class) AZ_DEFAULT_COPY(_Class) AZ_DEFAULT_MOVE(_Class)
// Macro that can be used to avoid unreferenced variable warnings
#define AZ_UNUSED(x) (void)x
#define AZ_UNUSED_1(x) (void)(x);
#define AZ_UNUSED_2(x1, x2) AZ_UNUSED_1(x1) AZ_UNUSED_1(x2)
#define AZ_UNUSED_3(x1, x2, x3) AZ_UNUSED_1(x1) AZ_UNUSED_2(x2, x3)
#define AZ_UNUSED_4(x1, x2, x3, x4) AZ_UNUSED_2(x1, x2) AZ_UNUSED_2(x3, x4)
#define AZ_UNUSED_5(x1, x2, x3, x4, x5) AZ_UNUSED_2(x1, x2) AZ_UNUSED_3(x3, x4, x5)
#define AZ_UNUSED_6(x1, x2, x3, x4, x5, x6) AZ_UNUSED_3(x1, x2, x3) AZ_UNUSED_3(x4, x5, x6)
#define AZ_UNUSED_7(x1, x2, x3, x4, x5, x6, x7) AZ_UNUSED_3(x1, x2, x3) AZ_UNUSED_4(x4, x5, x6, x7)
#define AZ_UNUSED_8(x1, x2, x3, x4, x5, x6, x7, x8) AZ_UNUSED_4(x1, x2, x3, x4) AZ_UNUSED_4(x5, x6, x7, x8)
#define AZ_UNUSED_9(x1, x2, x3, x4, x5, x6, x7, x8, x9) AZ_UNUSED_4(x1, x2, x3, x4) AZ_UNUSED_5(x5, x6, x7, x8, x9)
#define AZ_UNUSED_10(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10) AZ_UNUSED_5(x1, x2, x3, x4, x5) AZ_UNUSED_5(x6, x7, x8, x9, x10)
#define AZ_UNUSED_11(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11) AZ_UNUSED_5(x1, x2, x3, x4, x5) AZ_UNUSED_6(x6, x7, x8, x9, x10, x11)
#define AZ_UNUSED_12(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12) AZ_UNUSED_6(x1, x2, x3, x4, x5, x6) AZ_UNUSED_6(x7, x8, x9, x10, x11, x12)
#define AZ_UNUSED(...) AZ_MACRO_SPECIALIZE(AZ_UNUSED_, AZ_VA_NUM_ARGS(__VA_ARGS__), (__VA_ARGS__))
#define AZ_DEFINE_ENUM_BITWISE_OPERATORS(EnumType) \
inline constexpr EnumType operator | (EnumType a, EnumType b) \
@@ -42,7 +42,7 @@ namespace AZ
}
else
{
AZ_Error("System", false, "Failed to open HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Cryptography\\MachineGuid!")
AZ_Error("System", false, "Failed to open HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Cryptography\\MachineGuid!");
}
wchar_t* hostname = machineInfo + wcslen(machineInfo);
+1 -1
View File
@@ -2651,7 +2651,7 @@ namespace UnitTest
if (!rootElement.GetChildData(AZ_CRC("InnerBaseStringField"), stringField))
{
AZ_Error("PatchingTest", false, "Unable to retrieve 'InnerBaseStringField' data for %u version of the InnerObjectFieldConverterClass",
rootElement.GetVersion())
rootElement.GetVersion());
return false;
}
@@ -18,6 +18,7 @@
#include <AzCore/std/functional.h>
#include <AzCore/std/parallel/thread.h>
#include <AzCore/std/string/string.h>
#include <AzCore/PlatformIncl.h>
#include <AzFramework/CommandLine/CommandLine.h>
@@ -14,6 +14,7 @@
#include <AzCore/Math/Vector2.h>
#include <AzCore/Math/Vector3.h>
#include <AzCore/Math/Vector4.h>
#include <AzCore/Math/Matrix3x4.h>
#include <AzCore/Math/Color.h>
#include <AzCore/Math/Transform.h>
#include <AzCore/Component/ComponentBus.h>
@@ -100,6 +101,8 @@ namespace AzFramework
virtual AZ::u32 SetState(AZ::u32 state) { (void)state; return 0; }
virtual void PushMatrix(const AZ::Transform& tm) { (void)tm; }
virtual void PopMatrix() {}
virtual void PushPremultipliedMatrix(const AZ::Matrix3x4& matrix) { (void)matrix; }
virtual AZ::Matrix3x4 PopPremultipliedMatrix() { return AZ::Matrix3x4::CreateIdentity(); }
protected:
virtual ~DebugDisplayRequests() = default;
@@ -281,7 +281,7 @@ namespace AzFramework
AZ_PROFILE_FUNCTION(AzCore);
auto data = AZStd::get_if<FileRequest::ReadData>(&request->GetCommand());
AZ_Assert(data, "Request doing reading in the RemoteStorageDrive didn't contain read data.")
AZ_Assert(data, "Request doing reading in the RemoteStorageDrive didn't contain read data.");
HandleType file = InvalidHandle;
@@ -83,7 +83,7 @@ namespace AzFramework::ProjectManager
return ProjectPathCheckResult::ProjectManagerLaunchFailed;
}
bool LaunchProjectManager(const AZStd::string& commandLineArgs)
bool LaunchProjectManager([[maybe_unused]]const AZStd::string& commandLineArgs)
{
bool launchSuccess = false;
#if (AZ_TRAIT_AZFRAMEWORK_USE_PROJECT_MANAGER)
@@ -588,6 +588,12 @@ namespace AzFramework
// pass through the camera's position and look vector for use in the lookAt function
if (const auto lookAt = lookAtFn(targetCamera.Translation(), targetCamera.Rotation().GetBasisY()))
{
// default to internal look at behavior if the look at point matches the camera translation
if (targetCamera.m_lookAt.IsClose(*lookAt))
{
return false;
}
auto transform = AZ::Transform::CreateLookAt(targetCamera.m_lookAt, *lookAt);
nextCamera.m_lookDist = -lookAt->GetDistance(targetCamera.m_lookAt);
UpdateCameraFromTransform(nextCamera, transform);
@@ -45,6 +45,7 @@ namespace AzFramework
protected:
~BoundsRequests() = default;
};
using BoundsRequestBus = AZ::EBus<BoundsRequests>;
//! Returns a union of all local Aabbs provided by components implementing the BoundsRequestBus.
@@ -57,6 +57,7 @@ namespace AzFramework
uint32_t NativeWindowImpl_Android::GetDisplayRefreshRate() const
{
// [GFX TODO][GHI - 2678]
// Using 60 for now until proper support is added
return 60;
}
@@ -55,6 +55,29 @@ namespace AzFramework
using LinuxXcbConnectionManagerBus = AZ::EBus<LinuxXcbConnectionManager, LinuxXcbConnectionManagerBusTraits>;
using LinuxXcbConnectionManagerInterface = AZ::Interface<LinuxXcbConnectionManager>;
class LinuxXcbEventHandler
{
public:
AZ_RTTI(LinuxXcbEventHandler, "{3F756E14-8D74-42FD-843C-4863307710DB}");
virtual ~LinuxXcbEventHandler() = default;
virtual void HandleXcbEvent(xcb_generic_event_t* event) = 0;
};
class LinuxXcbEventHandlerBusTraits
: public AZ::EBusTraits
{
public:
//////////////////////////////////////////////////////////////////////////
// EBusTraits overrides
static constexpr AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
static constexpr AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
//////////////////////////////////////////////////////////////////////////
};
using LinuxXcbEventHandlerBus = AZ::EBus<LinuxXcbEventHandler, LinuxXcbEventHandlerBusTraits>;
#endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
} // namespace AzFramework
@@ -6,101 +6,28 @@
*
*/
#include <AzFramework/API/ApplicationAPI_Platform.h>
#include <AzFramework/Application/Application.h>
#include "Application_Linux_xcb.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
class LinuxXcbConnectionManagerImpl
: public LinuxXcbConnectionManagerBus::Handler
{
public:
LinuxXcbConnectionManagerImpl()
{
m_xcbConnection = xcb_connect(nullptr, nullptr);
AZ_Error("ApplicationLinux", m_xcbConnection != nullptr, "Unable to connect to X11 Server.");
LinuxXcbConnectionManagerBus::Handler::BusConnect();
}
~LinuxXcbConnectionManagerImpl() override
{
LinuxXcbConnectionManagerBus::Handler::BusDisconnect();
xcb_disconnect(m_xcbConnection);
}
xcb_connection_t* GetXcbConnection() const override
{
return m_xcbConnection;
}
private:
xcb_connection_t* m_xcbConnection = nullptr;
};
#endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
////////////////////////////////////////////////////////////////////////////////////////////////
class ApplicationLinux
: public Application::Implementation
, public LinuxLifecycleEvents::Bus::Handler
{
public:
////////////////////////////////////////////////////////////////////////////////////////////
AZ_CLASS_ALLOCATOR(ApplicationLinux, AZ::SystemAllocator, 0);
ApplicationLinux();
~ApplicationLinux() override;
////////////////////////////////////////////////////////////////////////////////////////////
// Application::Implementation
void PumpSystemEventLoopOnce() override;
void PumpSystemEventLoopUntilEmpty() override;
private:
#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
AZStd::unique_ptr<LinuxXcbConnectionManager> m_xcbConnectionManager;
#endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
};
////////////////////////////////////////////////////////////////////////////////////////////////
Application::Implementation* Application::Implementation::Create()
{
return aznew ApplicationLinux();
}
////////////////////////////////////////////////////////////////////////////////////////////////
ApplicationLinux::ApplicationLinux()
{
LinuxLifecycleEvents::Bus::Handler::BusConnect();
#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
m_xcbConnectionManager = AZStd::make_unique<LinuxXcbConnectionManagerImpl>();
if (LinuxXcbConnectionManagerInterface::Get() == nullptr)
{
LinuxXcbConnectionManagerInterface::Register(m_xcbConnectionManager.get());
}
return aznew ApplicationLinux_xcb();
#elif PAL_TRAIT_LINUX_WINDOW_MANAGER_WAYLAND
#error "Linux Window Manager Wayland not supported."
return nullptr;
#elif PAL_TRAIT_LINUX_WINDOW_MANAGER_XLIB
#error "Linux Window Manager XLIB not supported."
return nullptr;
#else
#error "Linux Window Manager not recognized."
return nullptr;
#endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
}
////////////////////////////////////////////////////////////////////////////////////////////////
ApplicationLinux::~ApplicationLinux()
{
#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
if (LinuxXcbConnectionManagerInterface::Get() == m_xcbConnectionManager.get())
{
LinuxXcbConnectionManagerInterface::Unregister(m_xcbConnectionManager.get());
}
m_xcbConnectionManager.reset();
#endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
LinuxLifecycleEvents::Bus::Handler::BusDisconnect();
}
////////////////////////////////////////////////////////////////////////////////////////////////
void ApplicationLinux::PumpSystemEventLoopOnce()
{
}
////////////////////////////////////////////////////////////////////////////////////////////////
void ApplicationLinux::PumpSystemEventLoopUntilEmpty()
{
}
} // namespace AzFramework
@@ -0,0 +1,94 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzFramework/API/ApplicationAPI_Platform.h>
#include <AzFramework/Application/Application.h>
#include "Application_Linux_xcb.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
////////////////////////////////////////////////////////////////////////////////////////////////
class LinuxXcbConnectionManagerImpl
: public LinuxXcbConnectionManagerBus::Handler
{
public:
LinuxXcbConnectionManagerImpl()
{
m_xcbConnection = xcb_connect(nullptr, nullptr);
AZ_Error("ApplicationLinux", m_xcbConnection != nullptr, "Unable to connect to X11 Server.");
LinuxXcbConnectionManagerBus::Handler::BusConnect();
}
~LinuxXcbConnectionManagerImpl()
{
LinuxXcbConnectionManagerBus::Handler::BusDisconnect();
xcb_disconnect(m_xcbConnection);
}
xcb_connection_t* GetXcbConnection() const override
{
return m_xcbConnection;
}
private:
xcb_connection_t* m_xcbConnection = nullptr;
};
////////////////////////////////////////////////////////////////////////////////////////////////
ApplicationLinux_xcb::ApplicationLinux_xcb()
{
LinuxLifecycleEvents::Bus::Handler::BusConnect();
m_xcbConnectionManager = AZStd::make_unique<LinuxXcbConnectionManagerImpl>();
if (LinuxXcbConnectionManagerInterface::Get() == nullptr)
{
LinuxXcbConnectionManagerInterface::Register(m_xcbConnectionManager.get());
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
ApplicationLinux_xcb::~ApplicationLinux_xcb()
{
if (LinuxXcbConnectionManagerInterface::Get() == m_xcbConnectionManager.get())
{
LinuxXcbConnectionManagerInterface::Unregister(m_xcbConnectionManager.get());
}
m_xcbConnectionManager.reset();
LinuxLifecycleEvents::Bus::Handler::BusDisconnect();
}
////////////////////////////////////////////////////////////////////////////////////////////////
void ApplicationLinux_xcb::PumpSystemEventLoopOnce()
{
if (xcb_connection_t* xcbConnection = m_xcbConnectionManager->GetXcbConnection())
{
if (xcb_generic_event_t* event = xcb_poll_for_event(xcbConnection))
{
LinuxXcbEventHandlerBus::Broadcast(&LinuxXcbEventHandlerBus::Events::HandleXcbEvent, event);
free(event);
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
void ApplicationLinux_xcb::PumpSystemEventLoopUntilEmpty()
{
if (xcb_connection_t* xcbConnection = m_xcbConnectionManager->GetXcbConnection())
{
while (xcb_generic_event_t* event = xcb_poll_for_event(xcbConnection))
{
LinuxXcbEventHandlerBus::Broadcast(&LinuxXcbEventHandlerBus::Events::HandleXcbEvent, event);
free(event);
}
}
}
#endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
} // namespace AzFramework
@@ -0,0 +1,40 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzFramework/API/ApplicationAPI_Platform.h>
#include <AzFramework/Application/Application.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
////////////////////////////////////////////////////////////////////////////////////////////////
class ApplicationLinux_xcb
: public Application::Implementation
, public LinuxLifecycleEvents::Bus::Handler
{
public:
////////////////////////////////////////////////////////////////////////////////////////////
AZ_CLASS_ALLOCATOR(ApplicationLinux_xcb, AZ::SystemAllocator, 0);
ApplicationLinux_xcb();
~ApplicationLinux_xcb() override;
////////////////////////////////////////////////////////////////////////////////////////////
// Application::Implementation
void PumpSystemEventLoopOnce() override;
void PumpSystemEventLoopUntilEmpty() override;
private:
AZStd::unique_ptr<LinuxXcbConnectionManager> m_xcbConnectionManager;
};
#endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
} // namespace AzFramework
@@ -6,48 +6,24 @@
*
*/
#include <AzFramework/Windowing/NativeWindow.h>
#include "NativeWindow_Linux_xcb.h"
namespace AzFramework
{
class NativeWindowImpl_Linux final
: public NativeWindow::Implementation
{
public:
AZ_CLASS_ALLOCATOR(NativeWindowImpl_Linux, AZ::SystemAllocator, 0);
NativeWindowImpl_Linux() = default;
~NativeWindowImpl_Linux() override = default;
// NativeWindow::Implementation overrides...
void InitWindow(const AZStd::string& title,
const WindowGeometry& geometry,
const WindowStyleMasks& styleMasks) override;
NativeWindowHandle GetWindowHandle() const override;
uint32_t GetDisplayRefreshRate() const override;
};
NativeWindow::Implementation* NativeWindow::Implementation::Create()
{
return aznew NativeWindowImpl_Linux();
}
void NativeWindowImpl_Linux::InitWindow([[maybe_unused]]const AZStd::string& title,
const WindowGeometry& geometry,
[[maybe_unused]]const WindowStyleMasks& styleMasks)
{
m_width = geometry.m_width;
m_height = geometry.m_height;
}
NativeWindowHandle NativeWindowImpl_Linux::GetWindowHandle() const
{
AZ_Assert(false, "NativeWindow not implemented for Linux");
#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
return aznew NativeWindowImpl_Linux_xcb();
#elif PAL_TRAIT_LINUX_WINDOW_MANAGER_WAYLAND
#error "Linux Window Manager Wayland not supported."
return nullptr;
#elif PAL_TRAIT_LINUX_WINDOW_MANAGER_XLIB
#error "Linux Window Manager XLIB not supported."
return nullptr;
#else
#error "Linux Window Manager not recognized."
return nullptr;
#endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
}
uint32_t NativeWindowImpl_Linux::GetDisplayRefreshRate() const
{
//Using 60 for now until proper support is added
return 60;
}
} // namespace AzFramework
@@ -0,0 +1,244 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzFramework/API/ApplicationAPI_Platform.h>
#include <AzFramework/Application/Application.h>
#include <AzFramework/Windowing/NativeWindow.h>
#include <xcb/xcb.h>
#include "NativeWindow_Linux_xcb.h"
namespace AzFramework
{
#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
[[maybe_unused]] const char LinuxXcbErrorWindow[] = "NativeWindow_Linux_xcb";
static constexpr uint8_t s_XcbFormatDataSize = 32; // Format indicator for xcb for client messages
static constexpr uint16_t s_DefaultXcbWindowBorderWidth = 4; // The default border with in pixels if a border was specified
static constexpr uint8_t s_XcbResponseTypeMask = 0x7f; // Mask to extract the specific event type from an xcb event
////////////////////////////////////////////////////////////////////////////////////////////////
NativeWindowImpl_Linux_xcb::NativeWindowImpl_Linux_xcb()
: NativeWindow::Implementation()
{
if (auto xcbConnectionManager = AzFramework::LinuxXcbConnectionManagerInterface::Get();
xcbConnectionManager != nullptr)
{
m_xcbConnection = xcbConnectionManager->GetXcbConnection();
}
AZ_Error(LinuxXcbErrorWindow, m_xcbConnection != nullptr, "Unable to get XCB Connection");
}
////////////////////////////////////////////////////////////////////////////////////////////////
NativeWindowImpl_Linux_xcb::~NativeWindowImpl_Linux_xcb()
{
}
////////////////////////////////////////////////////////////////////////////////////////////////
void NativeWindowImpl_Linux_xcb::InitWindow(const AZStd::string& title,
const WindowGeometry& geometry,
const WindowStyleMasks& styleMasks)
{
// Get the parent window
const xcb_setup_t* xcbSetup = xcb_get_setup(m_xcbConnection);
xcb_screen_t* xcbRootScreen = xcb_setup_roots_iterator(xcbSetup).data;
xcb_window_t xcbParentWindow = xcbRootScreen->root;
// Create an XCB window from the connection
m_xcbWindow = xcb_generate_id(m_xcbConnection);
uint16_t borderWidth = 0;
const uint32_t mask = styleMasks.m_platformAgnosticStyleMask;
if ((mask & WindowStyleMasks::WINDOW_STYLE_BORDERED) ||
(mask & WindowStyleMasks::WINDOW_STYLE_RESIZEABLE))
{
borderWidth = s_DefaultXcbWindowBorderWidth;
}
uint32_t eventMask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
uint32_t valueList[] = { xcbRootScreen->black_pixel,
XCB_EVENT_MASK_STRUCTURE_NOTIFY };
xcb_void_cookie_t xcbCheckResult;
xcbCheckResult = xcb_create_window_checked(m_xcbConnection,
XCB_COPY_FROM_PARENT,
m_xcbWindow,
xcbParentWindow,
aznumeric_cast<int16_t>(geometry.m_posX),
aznumeric_cast<int16_t>(geometry.m_posY),
aznumeric_cast<int16_t>(geometry.m_width),
aznumeric_cast<int16_t>(geometry.m_height),
borderWidth,
XCB_WINDOW_CLASS_INPUT_OUTPUT,
xcbRootScreen->root_visual,
eventMask,
valueList);
AZ_Assert(ValidateXcbResult(xcbCheckResult), "Failed to create xcb window.");
SetWindowTitle(title);
// Setup the window close event
const static char* wmProtocolString = "WM_PROTOCOLS";
xcb_intern_atom_cookie_t cookieProtocol = xcb_intern_atom(m_xcbConnection, 1, strlen(wmProtocolString), wmProtocolString);
xcb_intern_atom_reply_t* replyProtocol = xcb_intern_atom_reply(m_xcbConnection, cookieProtocol, nullptr);
AZ_Error(LinuxXcbErrorWindow, replyProtocol != nullptr, "Unable to query xcb '%s' atom", wmProtocolString);
m_xcbAtomProtocols = replyProtocol->atom;
const static char* wmDeleteWindowString = "WM_DELETE_WINDOW";
xcb_intern_atom_cookie_t cookieDeleteWindow = xcb_intern_atom(m_xcbConnection, 0, strlen(wmDeleteWindowString), wmDeleteWindowString);
xcb_intern_atom_reply_t* replyDeleteWindow = xcb_intern_atom_reply(m_xcbConnection, cookieDeleteWindow, nullptr);
AZ_Error(LinuxXcbErrorWindow, replyDeleteWindow != nullptr, "Unable to query xcb '%s' atom", wmDeleteWindowString);
m_xcbAtomDeleteWindow = replyDeleteWindow->atom;
xcbCheckResult = xcb_change_property_checked(m_xcbConnection,
XCB_PROP_MODE_REPLACE,
m_xcbWindow,
m_xcbAtomProtocols,
XCB_ATOM_ATOM,
s_XcbFormatDataSize,
1,
&m_xcbAtomDeleteWindow);
AZ_Assert(ValidateXcbResult(xcbCheckResult), "Failed to change the xcb atom property for WM_CLOSE event");
m_width = geometry.m_width;
m_height = geometry.m_height;
}
////////////////////////////////////////////////////////////////////////////////////////////////
void NativeWindowImpl_Linux_xcb::Activate()
{
LinuxXcbEventHandlerBus::Handler::BusConnect();
if (!m_activated) // nothing to do if window was already activated
{
m_activated = true;
xcb_map_window(m_xcbConnection, m_xcbWindow);
xcb_flush(m_xcbConnection);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
void NativeWindowImpl_Linux_xcb::Deactivate()
{
if (m_activated) // nothing to do if window was already deactivated
{
m_activated = false;
WindowNotificationBus::Event(reinterpret_cast<NativeWindowHandle>(m_xcbWindow), &WindowNotificationBus::Events::OnWindowClosed);
xcb_unmap_window(m_xcbConnection, m_xcbWindow);
xcb_flush(m_xcbConnection);
}
LinuxXcbEventHandlerBus::Handler::BusDisconnect();
}
////////////////////////////////////////////////////////////////////////////////////////////////
NativeWindowHandle NativeWindowImpl_Linux_xcb::GetWindowHandle() const
{
return reinterpret_cast<NativeWindowHandle>(m_xcbWindow);
}
////////////////////////////////////////////////////////////////////////////////////////////////
void NativeWindowImpl_Linux_xcb::SetWindowTitle(const AZStd::string& title)
{
xcb_void_cookie_t xcbCheckResult;
xcbCheckResult = xcb_change_property(m_xcbConnection,
XCB_PROP_MODE_REPLACE,
m_xcbWindow,
XCB_ATOM_WM_NAME,
XCB_ATOM_STRING,
8,
static_cast<uint32_t>(title.size()),
title.c_str());
AZ_Assert(ValidateXcbResult(xcbCheckResult), "Failed to set window title.");
}
////////////////////////////////////////////////////////////////////////////////////////////////
void NativeWindowImpl_Linux_xcb::ResizeClientArea(WindowSize clientAreaSize)
{
const uint32_t values[] = { clientAreaSize.m_width, clientAreaSize.m_height };
xcb_configure_window(m_xcbConnection, m_xcbWindow, XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT, values);
m_width = clientAreaSize.m_width;
m_height = clientAreaSize.m_height;
}
////////////////////////////////////////////////////////////////////////////////////////////////
uint32_t NativeWindowImpl_Linux_xcb::GetDisplayRefreshRate() const
{
// [GFX TODO][GHI - 2678]
// Using 60 for now until proper support is added
return 60;
}
////////////////////////////////////////////////////////////////////////////////////////////////
bool NativeWindowImpl_Linux_xcb::ValidateXcbResult(xcb_void_cookie_t cookie)
{
bool result = true;
if (xcb_generic_error_t* error = xcb_request_check(m_xcbConnection, cookie))
{
AZ_TracePrintf("Error","Error code %d", error->error_code);
result = false;
}
return result;
}
////////////////////////////////////////////////////////////////////////////////////////////////
void NativeWindowImpl_Linux_xcb::HandleXcbEvent(xcb_generic_event_t* event)
{
switch (event->response_type & s_XcbResponseTypeMask)
{
case XCB_CONFIGURE_NOTIFY:
{
xcb_configure_notify_event_t* cne = reinterpret_cast<xcb_configure_notify_event_t*>(event);
WindowSizeChanged(aznumeric_cast<uint32_t>(cne->width),
aznumeric_cast<uint32_t>(cne->height));
break;
}
case XCB_CLIENT_MESSAGE:
{
xcb_client_message_event_t* cme = reinterpret_cast<xcb_client_message_event_t*>(event);
if ((cme->type == m_xcbAtomProtocols) &&
(cme->format == s_XcbFormatDataSize) &&
(cme->data.data32[0] == m_xcbAtomDeleteWindow))
{
Deactivate();
ApplicationRequests::Bus::Broadcast(&ApplicationRequests::ExitMainLoop);
}
break;
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
void NativeWindowImpl_Linux_xcb::WindowSizeChanged(const uint32_t width, const uint32_t height)
{
if (m_width != width || m_height != height)
{
m_width = width;
m_height = height;
if (m_activated)
{
WindowNotificationBus::Event(reinterpret_cast<NativeWindowHandle>(m_xcbWindow), &WindowNotificationBus::Events::OnWindowResized, width, height);
}
}
}
#endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
} // namespace AzFramework
@@ -0,0 +1,53 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzFramework/API/ApplicationAPI_Platform.h>
#include <AzFramework/Application/Application.h>
#include <AzFramework/Windowing/NativeWindow.h>
#include <xcb/xcb.h>
namespace AzFramework
{
#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
class NativeWindowImpl_Linux_xcb final
: public NativeWindow::Implementation
, public LinuxXcbEventHandlerBus::Handler
{
public:
AZ_CLASS_ALLOCATOR(NativeWindowImpl_Linux_xcb, AZ::SystemAllocator, 0);
NativeWindowImpl_Linux_xcb();
~NativeWindowImpl_Linux_xcb() override;
////////////////////////////////////////////////////////////////////////////////////////////
// NativeWindow::Implementation
void InitWindow(const AZStd::string& title,
const WindowGeometry& geometry,
const WindowStyleMasks& styleMasks) override;
void Activate() override;
void Deactivate() override;
NativeWindowHandle GetWindowHandle() const override;
void SetWindowTitle(const AZStd::string& title) override;
void ResizeClientArea(WindowSize clientAreaSize) override;
uint32_t GetDisplayRefreshRate() const override;
////////////////////////////////////////////////////////////////////////////////////////////
// LinuxXcbEventHandlerBus::Handler
void HandleXcbEvent(xcb_generic_event_t* event) override;
private:
bool ValidateXcbResult(xcb_void_cookie_t cookie);
void WindowSizeChanged(const uint32_t width, const uint32_t height);
xcb_connection_t* m_xcbConnection = nullptr;
xcb_window_t m_xcbWindow = 0;
xcb_atom_t m_xcbAtomProtocols;
xcb_atom_t m_xcbAtomDeleteWindow;
};
#endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
} // namespace AzFramework
@@ -12,6 +12,8 @@ set(FILES
AzFramework/API/ApplicationAPI_Platform.h
AzFramework/API/ApplicationAPI_Linux.h
AzFramework/Application/Application_Linux.cpp
AzFramework/Application/Application_Linux_xcb.h
AzFramework/Application/Application_Linux_xcb.cpp
AzFramework/Asset/AssetSystemComponentHelper_Linux.cpp
AzFramework/Process/ProcessWatcher_Linux.cpp
AzFramework/Process/ProcessCommon.h
@@ -20,6 +22,8 @@ set(FILES
../Common/Unimplemented/AzFramework/StreamingInstall/StreamingInstall_Unimplemented.cpp
../Common/Default/AzFramework/TargetManagement/TargetManagementComponent_Default.cpp
AzFramework/Windowing/NativeWindow_Linux.cpp
AzFramework/Windowing/NativeWindow_Linux_xcb.h
AzFramework/Windowing/NativeWindow_Linux_xcb.cpp
../Common/Unimplemented/AzFramework/Input/Devices/Gamepad/InputDeviceGamepad_Unimplemented.cpp
../Common/Unimplemented/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard_Unimplemented.cpp
../Common/Unimplemented/AzFramework/Input/Devices/Motion/InputDeviceMotion_Unimplemented.cpp
@@ -6,6 +6,7 @@
*
*/
#include <AZTestShared/Math/MathTestHelpers.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <AzCore/std/smart_ptr/make_shared.h>
#include <AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h>
@@ -47,18 +48,17 @@ namespace UnitTest
m_firstPersonTranslateCamera =
AZStd::make_shared<AzFramework::TranslateCameraInput>(AzFramework::LookTranslation, m_translateCameraInputChannelIds);
auto orbitCamera =
AZStd::make_shared<AzFramework::OrbitCameraInput>(AzFramework::InputChannelId("keyboard_key_modifier_alt_l"));
m_orbitCamera = AZStd::make_shared<AzFramework::OrbitCameraInput>(m_orbitChannelId);
auto orbitRotateCamera = AZStd::make_shared<AzFramework::RotateCameraInput>(AzFramework::InputDeviceMouse::Button::Left);
auto orbitTranslateCamera =
AZStd::make_shared<AzFramework::TranslateCameraInput>(AzFramework::OrbitTranslation, m_translateCameraInputChannelIds);
orbitCamera->m_orbitCameras.AddCamera(orbitRotateCamera);
orbitCamera->m_orbitCameras.AddCamera(orbitTranslateCamera);
m_orbitCamera->m_orbitCameras.AddCamera(orbitRotateCamera);
m_orbitCamera->m_orbitCameras.AddCamera(orbitTranslateCamera);
m_cameraSystem->m_cameras.AddCamera(m_firstPersonRotateCamera);
m_cameraSystem->m_cameras.AddCamera(m_firstPersonTranslateCamera);
m_cameraSystem->m_cameras.AddCamera(orbitCamera);
m_cameraSystem->m_cameras.AddCamera(m_orbitCamera);
// these tests rely on using motion delta, not cursor positions (default is true)
AzFramework::ed_cameraSystemUseCursor = false;
@@ -68,6 +68,7 @@ namespace UnitTest
{
AzFramework::ed_cameraSystemUseCursor = true;
m_orbitCamera.reset();
m_firstPersonRotateCamera.reset();
m_firstPersonTranslateCamera.reset();
@@ -77,12 +78,14 @@ namespace UnitTest
AllocatorsTestFixture::TearDown();
}
AzFramework::InputChannelId m_orbitChannelId = AzFramework::InputChannelId("keyboard_key_modifier_alt_l");
AzFramework::TranslateCameraInputChannelIds m_translateCameraInputChannelIds;
AZStd::shared_ptr<AzFramework::RotateCameraInput> m_firstPersonRotateCamera;
AZStd::shared_ptr<AzFramework::TranslateCameraInput> m_firstPersonTranslateCamera;
AZStd::shared_ptr<AzFramework::OrbitCameraInput> m_orbitCamera;
};
TEST_F(CameraInputFixture, Begin_and_end_OrbitCameraInput_consumes_correct_events)
TEST_F(CameraInputFixture, BeginAndEndOrbitCameraInputConsumesCorrectEvents)
{
// begin orbit camera
const bool consumed1 = HandleEventAndUpdate(AzFramework::DiscreteInputEvent{ AzFramework::InputDeviceKeyboard::Key::ModifierAltL,
@@ -102,7 +105,7 @@ namespace UnitTest
EXPECT_THAT(allConsumed, ElementsAre(true, false, true, false));
}
TEST_F(CameraInputFixture, Begin_CameraInput_notifies_ActivationBeganFn_for_TranslateCameraInput)
TEST_F(CameraInputFixture, BeginCameraInputNotifiesActivationBeganFnForTranslateCameraInput)
{
bool activationBegan = false;
m_firstPersonTranslateCamera->SetActivationBeganFn(
@@ -111,13 +114,13 @@ namespace UnitTest
activationBegan = true;
});
HandleEventAndUpdate(
AzFramework::DiscreteInputEvent{ m_translateCameraInputChannelIds.m_forwardChannelId, AzFramework::InputChannel::State::Began });
HandleEventAndUpdate(AzFramework::DiscreteInputEvent{ m_translateCameraInputChannelIds.m_forwardChannelId,
AzFramework::InputChannel::State::Began });
EXPECT_TRUE(activationBegan);
}
TEST_F(CameraInputFixture, Begin_CameraInput_notifies_ActivationBeganFn_after_delta_for_RotateCameraInput)
TEST_F(CameraInputFixture, BeginCameraInputNotifiesActivationBeganFnAfterDeltaForRotateCameraInput)
{
bool activationBegan = false;
m_firstPersonRotateCamera->SetActivationBeganFn(
@@ -133,7 +136,7 @@ namespace UnitTest
EXPECT_TRUE(activationBegan);
}
TEST_F(CameraInputFixture, Begin_CameraInput_does_not_notify_ActivationBeganFn_with_no_delta_for_RotateCameraInput)
TEST_F(CameraInputFixture, BeginCameraInputDoesNotNotifyActivationBeganFnWithNoDeltaForRotateCameraInput)
{
bool activationBegan = false;
m_firstPersonRotateCamera->SetActivationBeganFn(
@@ -148,7 +151,7 @@ namespace UnitTest
EXPECT_FALSE(activationBegan);
}
TEST_F(CameraInputFixture, End_CameraInput_notifies_ActivationEndFn_after_delta_for_RotateCameraInput)
TEST_F(CameraInputFixture, EndCameraInputNotifiesActivationEndFnAfterDeltaForRotateCameraInput)
{
bool activationEnded = false;
m_firstPersonRotateCamera->SetActivationEndedFn(
@@ -166,7 +169,7 @@ namespace UnitTest
EXPECT_TRUE(activationEnded);
}
TEST_F(CameraInputFixture, End_CameraInput_does_not_notify_ActivationBeganFn_or_ActivationBeganFn_with_no_delta_for_RotateCameraInput)
TEST_F(CameraInputFixture, EndCameraInputDoesNotNotifyActivationBeganFnOrActivationBeganFnWithNoDeltaForRotateCameraInput)
{
bool activationBegan = false;
m_firstPersonRotateCamera->SetActivationBeganFn(
@@ -191,7 +194,7 @@ namespace UnitTest
EXPECT_FALSE(activationEnded);
}
TEST_F(CameraInputFixture, End_CameraInput_notifies_ActivationBeganFn_or_ActivationEndFn_with_TranslateCamera)
TEST_F(CameraInputFixture, End_CameraInputNotifiesActivationBeganFnOrActivationEndFnWithTranslateCamera)
{
bool activationBegan = false;
m_firstPersonTranslateCamera->SetActivationBeganFn(
@@ -207,16 +210,16 @@ namespace UnitTest
activationEnded = true;
});
HandleEventAndUpdate(
AzFramework::DiscreteInputEvent{ m_translateCameraInputChannelIds.m_forwardChannelId, AzFramework::InputChannel::State::Began });
HandleEventAndUpdate(
AzFramework::DiscreteInputEvent{ m_translateCameraInputChannelIds.m_forwardChannelId, AzFramework::InputChannel::State::Ended });
HandleEventAndUpdate(AzFramework::DiscreteInputEvent{ m_translateCameraInputChannelIds.m_forwardChannelId,
AzFramework::InputChannel::State::Began });
HandleEventAndUpdate(AzFramework::DiscreteInputEvent{ m_translateCameraInputChannelIds.m_forwardChannelId,
AzFramework::InputChannel::State::Ended });
EXPECT_TRUE(activationBegan);
EXPECT_TRUE(activationEnded);
}
TEST_F(CameraInputFixture, End_activation_called_for_CameraInput_if_active_when_cameras_are_cleared)
TEST_F(CameraInputFixture, EndActivationCalledForCameraInputIfActiveWhenCamerasAreCleared)
{
bool activationEnded = false;
m_firstPersonTranslateCamera->SetActivationEndedFn(
@@ -225,11 +228,37 @@ namespace UnitTest
activationEnded = true;
});
HandleEventAndUpdate(
AzFramework::DiscreteInputEvent{ m_translateCameraInputChannelIds.m_forwardChannelId, AzFramework::InputChannel::State::Began });
HandleEventAndUpdate(AzFramework::DiscreteInputEvent{ m_translateCameraInputChannelIds.m_forwardChannelId,
AzFramework::InputChannel::State::Began });
m_cameraSystem->m_cameras.Clear();
EXPECT_TRUE(activationEnded);
}
TEST_F(CameraInputFixture, OrbitCameraInputHandlesLookAtPointAndSelfAtSamePositionWhenOrbiting)
{
// create pathological lookAtFn that just returns the same position as the camera
m_orbitCamera->SetLookAtFn(
[](const AZ::Vector3& position, [[maybe_unused]] const AZ::Vector3& direction)
{
return position;
});
AzFramework::UpdateCameraFromTransform(
m_targetCamera,
AZ::Transform::CreateFromQuaternionAndTranslation(
AZ::Quaternion::CreateFromEulerAnglesDegrees(AZ::Vector3(0.0f, 0.0f, 90.0f)), AZ::Vector3(10.0f, 10.0f, 10.0f)));
m_camera = m_targetCamera;
HandleEventAndUpdate(AzFramework::DiscreteInputEvent{ m_orbitChannelId, AzFramework::InputChannel::State::Began });
// verify the camera yaw has not changed and the look at point
// does not match that of the camera translation
using ::testing::Eq;
using ::testing::Not;
EXPECT_THAT(m_camera.m_yaw, Eq(AZ::DegToRad(90.0f)));
EXPECT_THAT(m_camera.m_lookAt, Not(IsClose(m_camera.Translation())));
}
} // namespace UnitTest
@@ -45,6 +45,9 @@ namespace AzManipulatorTestFramework
virtual void SetAngularStep(float step) = 0;
//! Get the viewport id.
virtual int GetViewportId() const = 0;
//! Updates the visibility state.
//! Updates which entities are currently visible given the current camera state.
virtual void UpdateVisibility() = 0;
};
//! This interface is used to simulate the manipulator manager while the manipulators are under test.
@@ -12,8 +12,8 @@
#include <AzManipulatorTestFramework/AzManipulatorTestFrameworkUtils.h>
#include <AzManipulatorTestFramework/ImmediateModeActionDispatcher.h>
#include <AzManipulatorTestFramework/IndirectManipulatorViewportInteraction.h>
#include <AzToolsFramework/ViewportSelection/EditorInteractionSystemViewportSelectionRequestBus.h>
#include <AzToolsFramework/ViewportSelection/EditorDefaultSelection.h>
#include <AzToolsFramework/ViewportSelection/EditorInteractionSystemViewportSelectionRequestBus.h>
#include <type_traits>
namespace UnitTest
@@ -21,20 +21,18 @@ namespace UnitTest
//! Fixture to provide the indirect call viewport interaction that is dependent on AzToolsFramework::ToolsApplication.
//! \tparam ToolsApplicationFixtureT The fixture that provides the AzToolsFramework::ToolsApplication functionality.
template<typename ToolsApplicationFixtureT>
class IndirectCallManipulatorViewportInteractionFixtureMixin
: public ToolsApplicationFixtureT
class IndirectCallManipulatorViewportInteractionFixtureMixin : public ToolsApplicationFixtureT
{
using IndirectCallManipulatorViewportInteraction =
AzManipulatorTestFramework::IndirectCallManipulatorViewportInteraction;
using IndirectCallManipulatorViewportInteraction = AzManipulatorTestFramework::IndirectCallManipulatorViewportInteraction;
using ImmediateModeActionDispatcher = AzManipulatorTestFramework::ImmediateModeActionDispatcher;
void SetUpEditorFixtureImpl() override
{
ToolsApplicationFixtureT::SetUpEditorFixtureImpl();
m_viewportManipulatorInteraction = AZStd::make_unique<IndirectCallManipulatorViewportInteraction>();
m_actionDispatcher = AZStd::make_unique<ImmediateModeActionDispatcher>(*m_viewportManipulatorInteraction);
m_cameraState = AzFramework::CreateIdentityDefaultCamera(
AZ::Vector3::CreateZero(), AzManipulatorTestFramework::DefaultViewportSize);
m_cameraState =
AzFramework::CreateIdentityDefaultCamera(AZ::Vector3::CreateZero(), AzManipulatorTestFramework::DefaultViewportSize);
}
void TearDownEditorFixtureImpl() override
@@ -9,8 +9,8 @@
#pragma once
#include <AzManipulatorTestFramework/AzManipulatorTestFramework.h>
#include <AzToolsFramework/ViewportSelection/EditorInteractionSystemViewportSelectionRequestBus.h>
#include <AzToolsFramework/ViewportSelection/EditorDefaultSelection.h>
#include <AzToolsFramework/ViewportSelection/EditorInteractionSystemViewportSelectionRequestBus.h>
namespace AzManipulatorTestFramework
{
@@ -18,13 +18,12 @@ namespace AzManipulatorTestFramework
class IndirectCallManipulatorManager;
//! Implementation of manipulator viewport interaction that manipulates the manager indirectly via bus calls.
class IndirectCallManipulatorViewportInteraction
: public ManipulatorViewportInteraction
class IndirectCallManipulatorViewportInteraction : public ManipulatorViewportInteraction
{
public:
IndirectCallManipulatorViewportInteraction();
~IndirectCallManipulatorViewportInteraction();
// ManipulatorViewportInteractionInterface ...
const ViewportInteractionInterface& GetViewportInteraction() const override;
const ManipulatorManagerInterface& GetManipulatorManager() const override;
@@ -8,6 +8,7 @@
#pragma once
#include <AzFramework/Visibility/EntityVisibilityQuery.h>
#include <AzManipulatorTestFramework/AzManipulatorTestFramework.h>
namespace AzManipulatorTestFramework
@@ -19,6 +20,7 @@ namespace AzManipulatorTestFramework
: public ViewportInteractionInterface
, public AzToolsFramework::ViewportInteraction::ViewportInteractionRequestBus::Handler
, public AzToolsFramework::ViewportInteraction::ViewportSettingsRequestBus::Handler
, private AzToolsFramework::ViewportInteraction::EditorEntityViewportInteractionRequestBus::Handler
{
public:
ViewportInteraction();
@@ -34,6 +36,7 @@ namespace AzManipulatorTestFramework
void SetGridSize(float size) override;
void SetAngularStep(float step) override;
int GetViewportId() const override;
void UpdateVisibility() override;
// ViewportInteractionRequestBus overrides ...
AzFramework::CameraState GetCameraState() override;
@@ -52,7 +55,11 @@ namespace AzManipulatorTestFramework
float ManipulatorLineBoundWidth() const override;
float ManipulatorCircleBoundWidth() const override;
// EditorEntityViewportInteractionRequestBus overrides ...
void FindVisibleEntities(AZStd::vector<AZ::EntityId>& visibleEntities) override;
private:
AzFramework::EntityVisibilityQuery m_entityVisibilityQuery;
AZStd::unique_ptr<NullDebugDisplayRequests> m_nullDebugDisplayRequests;
const int m_viewportId = 1234; // Arbitrary viewport id for manipulator tests
AzFramework::CameraState m_cameraState;
@@ -91,12 +91,12 @@ namespace AzManipulatorTestFramework
AzToolsFramework::ViewportInteraction::MousePick BuildMousePick(
const AzFramework::ScreenPoint& screenPoint, const AzFramework::CameraState& cameraState)
{
const auto screenToWorld = AzFramework::ScreenToWorld(screenPoint, cameraState);
const auto nearPlaneWorldPosition = AzFramework::ScreenToWorld(screenPoint, cameraState);
AzToolsFramework::ViewportInteraction::MousePick mousePick;
mousePick.m_screenCoordinates = screenPoint;
mousePick.m_rayOrigin = screenToWorld;
mousePick.m_rayDirection = (screenToWorld - cameraState.m_position).GetNormalized();
mousePick.m_rayOrigin = cameraState.m_position;
mousePick.m_rayDirection = (nearPlaneWorldPosition - cameraState.m_position).GetNormalized();
return mousePick;
}
@@ -16,8 +16,7 @@ namespace AzManipulatorTestFramework
using MouseInteractionEvent = AzToolsFramework::ViewportInteraction::MouseInteractionEvent;
//! Implementation of the manipulator interface using bus calls to access to the manipulator manager.
class IndirectCallManipulatorManager
: public ManipulatorManagerInterface
class IndirectCallManipulatorManager : public ManipulatorManagerInterface
{
public:
IndirectCallManipulatorManager(ViewportInteractionInterface& viewportInteraction);
@@ -39,11 +38,18 @@ namespace AzManipulatorTestFramework
void IndirectCallManipulatorManager::ConsumeMouseInteractionEvent(const MouseInteractionEvent& event)
{
m_viewportInteraction.UpdateVisibility();
// ensure we call display viewport 2d to simulate this update step (some state may be
// updated here, e.g. box select)
AzFramework::ViewportDebugDisplayEventBus::Event(
AzToolsFramework::GetEntityContextId(), &AzFramework::ViewportDebugDisplayEvents::DisplayViewport2d,
AzFramework::ViewportInfo{ m_viewportInteraction.GetViewportId() }, m_viewportInteraction.GetDebugDisplay());
DrawManipulators();
AzToolsFramework::EditorInteractionSystemViewportSelectionRequestBus::Event(
AzToolsFramework::GetEntityContextId(),
&AzToolsFramework::ViewportInteraction::InternalMouseViewportRequests::InternalHandleAllMouseInteractions,
event);
&AzToolsFramework::ViewportInteraction::InternalMouseViewportRequests::InternalHandleAllMouseInteractions, event);
DrawManipulators();
}
@@ -26,10 +26,12 @@ namespace AzManipulatorTestFramework
{
AzToolsFramework::ViewportInteraction::ViewportInteractionRequestBus::Handler::BusConnect(m_viewportId);
AzToolsFramework::ViewportInteraction::ViewportSettingsRequestBus::Handler::BusConnect(m_viewportId);
AzToolsFramework::ViewportInteraction::EditorEntityViewportInteractionRequestBus::Handler::BusConnect(m_viewportId);
}
ViewportInteraction::~ViewportInteraction()
{
AzToolsFramework::ViewportInteraction::EditorEntityViewportInteractionRequestBus::Handler::BusDisconnect();
AzToolsFramework::ViewportInteraction::ViewportSettingsRequestBus::Handler::BusDisconnect();
AzToolsFramework::ViewportInteraction::ViewportInteractionRequestBus::Handler::BusDisconnect();
}
@@ -74,6 +76,16 @@ namespace AzManipulatorTestFramework
return 0.1f;
}
void ViewportInteraction::FindVisibleEntities(AZStd::vector<AZ::EntityId>& visibleEntitiesOut)
{
visibleEntitiesOut.assign(m_entityVisibilityQuery.Begin(), m_entityVisibilityQuery.End());
}
void ViewportInteraction::UpdateVisibility()
{
m_entityVisibilityQuery.UpdateVisibility(m_cameraState);
}
AzFramework::ScreenPoint ViewportInteraction::ViewportWorldToScreen(const AZ::Vector3& worldPosition)
{
return AzFramework::WorldToScreen(worldPosition, m_cameraState);
@@ -342,7 +342,7 @@ namespace AzQtComponents
config.toolTipPaddingInPixels = 5;
config.headerIconSizeInPixels = CardHeader::defaultIconSize();
config.rootLayoutSpacing = 0;
config.warningIcon = QStringLiteral(":/Cards/img/UI20/Cards/warning.svg");
config.warningIcon = QStringLiteral(":/Notifications/warning.svg");
config.warningIconSize = {24, 24};
config.disabledIconAlpha = 0.25;
@@ -303,7 +303,7 @@ VectorInput::~VectorInput()
void VectorInput::setLabel(int index, const QString& label)
{
AZ_Warning("PropertyGrid", index < m_elementCount,
"This control handles only %i controls", m_elementCount)
"This control handles only %i controls", m_elementCount);
if (index < m_elementCount)
{
m_elements[index]->setLabel(label);
@@ -313,7 +313,7 @@ void VectorInput::setLabel(int index, const QString& label)
void VectorInput::setLabelStyle(int index, const QString& qss)
{
AZ_Warning("PropertyGrid", index < m_elementCount,
"This control handles only %i controls", m_elementCount)
"This control handles only %i controls", m_elementCount);
if (index < m_elementCount)
{
m_elements[index]->getLabelWidget()->setStyleSheet(qss);
@@ -323,7 +323,7 @@ void VectorInput::setLabelStyle(int index, const QString& qss)
void VectorInput::setValuebyIndex(double value, int elementIndex)
{
AZ_Warning("PropertyGrid", elementIndex < m_elementCount,
"This control handles only %i controls", m_elementCount)
"This control handles only %i controls", m_elementCount);
if (elementIndex < m_elementCount)
{
m_elements[elementIndex]->setValue(value);
@@ -424,7 +424,6 @@
<file>img/UI20/Cards/menu_ico.png</file>
<file>img/UI20/Cards/error_icon.png</file>
<file>img/UI20/Cards/warning.png</file>
<file>img/UI20/Cards/warning.svg</file>
<file>img/UI20/Cards/search.png</file>
<file>img/UI20/Cards/close.png</file>
<file>img/UI20/Cards/error-conclict-state.svg</file>
@@ -30,6 +30,7 @@
<file alias="checkmark.svg">Notifications/checkmark.svg</file>
<file alias="download.svg">Notifications/download.svg</file>
<file alias="link.svg">Notifications/link.svg</file>
<file alias="warning.svg">Notifications/warning.svg</file>
</qresource>
<qresource prefix="/Outliner">
<file alias="sort_a_to_z.svg">Outliner/sort_a_to_z.svg</file>
@@ -96,7 +96,7 @@ namespace AzToolsFramework
{
AZ::EBusReduceResult<AZ::Aabb, AzFramework::AabbUnionAggregator> aabbResult(AZ::Aabb::CreateNull());
EditorComponentSelectionRequestsBus::EventResult(
aabbResult, entityId, &EditorComponentSelectionRequests::GetEditorSelectionBoundsViewport, viewportInfo);
aabbResult, entityId, &EditorComponentSelectionRequestsBus::Events::GetEditorSelectionBoundsViewport, viewportInfo);
return aabbResult.value;
}
@@ -44,7 +44,7 @@ namespace AzToolsFramework
const char AssetBundleSettingsFileExtension[] = "bundlesettings";
const char BundleFileExtension[] = "pak";
const char ComparisonRulesFileExtension[] = "rules";
const char ErrorWindowName[] = "AssetBundler";
[[maybe_unused]] const char ErrorWindowName[] = "AssetBundler";
const char* AssetFileInfoListComparison::ComparisonTypeNames[] = { "delta", "union", "intersection", "complement", "filepattern", "intersectioncount" };
const char* AssetFileInfoListComparison::FilePatternTypeNames[] = { "wildcard", "regex" };
const char DefaultTypeName[] = "default";
@@ -43,6 +43,8 @@ namespace AzToolsFramework
virtual Prefab::InstanceOptionalReference GetRootPrefabInstance() = 0;
virtual Prefab::TemplateId GetRootPrefabTemplateId() = 0;
//! Get all Assets generated by Prefab processing when entering Play-In Editor mode (Ctrl+G)
//! /return The vector of Assets generated by Prefab processing
virtual const AZStd::vector<AZ::Data::Asset<AZ::Data::AssetData>>& GetPlayInEditorAssetData() = 0;
@@ -359,6 +359,12 @@ namespace AzToolsFramework
return AZStd::nullopt;
}
Prefab::TemplateId PrefabEditorEntityOwnershipService::GetRootPrefabTemplateId()
{
AZ_Assert(m_rootInstance, "A valid root prefab instance couldn't be found in PrefabEditorEntityOwnershipService.");
return m_rootInstance ? m_rootInstance->GetTemplateId() : Prefab::InvalidTemplateId;
}
const AZStd::vector<AZ::Data::Asset<AZ::Data::AssetData>>& PrefabEditorEntityOwnershipService::GetPlayInEditorAssetData()
{
return m_playInEditorData.m_assets;
@@ -193,6 +193,7 @@ namespace AzToolsFramework
AZ::IO::PathView filePath, Prefab::InstanceOptionalReference instanceToParentUnder) override;
Prefab::InstanceOptionalReference GetRootPrefabInstance() override;
Prefab::TemplateId GetRootPrefabTemplateId() override;
const AZStd::vector<AZ::Data::Asset<AZ::Data::AssetData>>& GetPlayInEditorAssetData() override;
//////////////////////////////////////////////////////////////////////////
@@ -26,6 +26,19 @@ namespace AzToolsFramework
{
namespace Prefab
{
static constexpr const char s_saveAllPrefabsKey[] = "/O3DE/Preferences/Prefabs/SaveAllPrefabs";
void PrefabLoader::Reflect(AZ::ReflectContext* context)
{
if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Enum<SaveAllPrefabsPreference>()
->Value("Ask every time", SaveAllPrefabsPreference::AskEveryTime)
->Value("Save all", SaveAllPrefabsPreference::SaveAll)
->Value("Save none", SaveAllPrefabsPreference::SaveNone);
}
}
void PrefabLoader::RegisterPrefabLoaderInterface()
{
m_prefabSystemComponentInterface = AZ::Interface<PrefabSystemComponentInterface>::Get();
@@ -657,6 +670,24 @@ namespace AzToolsFramework
return finalPath;
}
SaveAllPrefabsPreference PrefabLoader::GetSaveAllPrefabsPreference() const
{
SaveAllPrefabsPreference saveAllPrefabsPreference = SaveAllPrefabsPreference::AskEveryTime;
if (auto* registry = AZ::SettingsRegistry::Get())
{
registry->GetObject(saveAllPrefabsPreference, s_saveAllPrefabsKey);
}
return saveAllPrefabsPreference;
}
void PrefabLoader::SetSaveAllPrefabsPreference(SaveAllPrefabsPreference saveAllPrefabsPreference)
{
if (auto* registry = AZ::SettingsRegistry::Get())
{
registry->SetObject(s_saveAllPrefabsKey, saveAllPrefabsPreference);
}
}
AZ::IO::Path PrefabLoaderInterface::GeneratePath()
{
return AZStd::string::format("Prefab_%s", AZ::Entity::MakeId().ToString().c_str());
@@ -39,6 +39,8 @@ namespace AzToolsFramework
AZ_CLASS_ALLOCATOR(PrefabLoader, AZ::SystemAllocator, 0);
AZ_RTTI(PrefabLoader, "{A302B072-4DC4-4B7E-9188-226F56A3429C8}", PrefabLoaderInterface);
static void Reflect(AZ::ReflectContext* context);
//////////////////////////////////////////////////////////////////////////
// PrefabLoaderInterface interface implementation
@@ -108,6 +110,9 @@ namespace AzToolsFramework
//! Returns if the path is a valid path for a prefab
static bool IsValidPrefabPath(AZ::IO::PathView path);
SaveAllPrefabsPreference GetSaveAllPrefabsPreference() const override;
void SetSaveAllPrefabsPreference(SaveAllPrefabsPreference saveAllPrefabsPreference) override;
private:
/**
* Copies the template dom provided and manipulates it into the proper format to be saved to disk.
@@ -17,6 +17,13 @@ namespace AzToolsFramework
{
namespace Prefab
{
enum class SaveAllPrefabsPreference
{
AskEveryTime,
SaveAll,
SaveNone
};
/*!
* PrefabLoaderInterface
* Interface for saving/loading Prefab files.
@@ -84,6 +91,9 @@ namespace AzToolsFramework
//! The path will always use the '/' separator.
virtual AZ::IO::Path GenerateRelativePath(AZ::IO::PathView path) = 0;
virtual SaveAllPrefabsPreference GetSaveAllPrefabsPreference() const = 0;
virtual void SetSaveAllPrefabsPreference(SaveAllPrefabsPreference saveAllPrefabsPreference) = 0;
protected:
// Generates a new path
@@ -93,3 +103,8 @@ namespace AzToolsFramework
} // namespace Prefab
} // namespace AzToolsFramework
namespace AZ
{
AZ_TYPE_INFO_SPECIALIZE(AzToolsFramework::Prefab::SaveAllPrefabsPreference, "{7E61EA82-4DE4-4A3F-945F-C8FEDC1114B5}");
}
@@ -57,6 +57,7 @@ namespace AzToolsFramework
AzToolsFramework::Prefab::PrefabConversionUtils::PrefabCatchmentProcessor::Reflect(context);
AzToolsFramework::Prefab::PrefabConversionUtils::EditorInfoRemover::Reflect(context);
PrefabPublicRequestHandler::Reflect(context);
PrefabLoader::Reflect(context);
AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
@@ -369,9 +370,11 @@ namespace AzToolsFramework
PrefabDom& PrefabSystemComponent::FindTemplateDom(TemplateId templateId)
{
AZStd::optional<AZStd::reference_wrapper<Template>> findTemplateResult = FindTemplate(templateId);
AZ_Assert(findTemplateResult.has_value(),
AZ_Assert(
findTemplateResult.has_value(),
"PrefabSystemComponent::FindTemplateDom - Unable to retrieve Prefab template with id: '%llu'. "
"Template could not be found", templateId);
"Template could not be found",
templateId);
AZ_Assert(findTemplateResult->get().IsValid(),
"PrefabSystemComponent::FindTemplateDom - Unable to retrieve Prefab template with id: '%llu'. "
@@ -526,12 +529,10 @@ namespace AzToolsFramework
Template& targetTemplate = targetTemplateReference->get();
#if defined(AZ_ENABLE_TRACING)
Template& sourceTemplate = sourceTemplateReference->get();
AZStd::string_view instanceName(instanceIterator->name.GetString(), instanceIterator->name.GetStringLength());
const AZStd::string& targetTemplateFilePath = targetTemplate.GetFilePath().Native();
const AZStd::string& sourceTemplateFilePath = sourceTemplate.GetFilePath().Native();
#endif
LinkId newLinkId = CreateUniqueLinkId();
Link newLink(newLinkId);
@@ -753,6 +754,89 @@ namespace AzToolsFramework
}
}
bool PrefabSystemComponent::AreDirtyTemplatesPresent(TemplateId rootTemplateId)
{
TemplateReference prefabTemplate = FindTemplate(rootTemplateId);
if (!prefabTemplate.has_value())
{
AZ_Assert(false, "Template with id %llu is not found", rootTemplateId);
return false;
}
if (IsTemplateDirty(rootTemplateId))
{
return true;
}
const Template::Links& linkIds = prefabTemplate->get().GetLinks();
for (LinkId linkId : linkIds)
{
auto linkIterator = m_linkIdMap.find(linkId);
if (linkIterator != m_linkIdMap.end())
{
return AreDirtyTemplatesPresent(linkIterator->second.GetSourceTemplateId());
}
}
return false;
}
void PrefabSystemComponent::SaveAllDirtyTemplates(TemplateId rootTemplateId)
{
AZStd::set<AZ::IO::PathView> dirtyTemplatePaths = GetDirtyTemplatePaths(rootTemplateId);
for (AZ::IO::PathView dirtyTemplatePath : dirtyTemplatePaths)
{
auto dirtyTemplateIterator = m_templateFilePathToIdMap.find(dirtyTemplatePath);
if (dirtyTemplateIterator == m_templateFilePathToIdMap.end())
{
AZ_Assert(false, "Template id for template with path '%s' is not found.", dirtyTemplatePath);
}
else
{
m_prefabLoader.SaveTemplate(dirtyTemplateIterator->second);
}
}
}
AZStd::set<AZ::IO::PathView> PrefabSystemComponent::GetDirtyTemplatePaths(TemplateId rootTemplateId)
{
AZStd::vector<AZ::IO::PathView> dirtyTemplatePathVector;
GetDirtyTemplatePathsHelper(rootTemplateId, dirtyTemplatePathVector);
AZStd::set<AZ::IO::PathView> dirtyTemplatePaths;
dirtyTemplatePaths.insert(dirtyTemplatePathVector.begin(), dirtyTemplatePathVector.end());
return AZStd::move(dirtyTemplatePaths);
}
void PrefabSystemComponent::GetDirtyTemplatePathsHelper(
TemplateId rootTemplateId, AZStd::vector<AZ::IO::PathView>& dirtyTemplatePaths)
{
TemplateReference prefabTemplate = FindTemplate(rootTemplateId);
if (!prefabTemplate.has_value())
{
AZ_Assert(false, "Template with id %llu is not found", rootTemplateId);
return;
}
if (IsTemplateDirty(rootTemplateId))
{
dirtyTemplatePaths.emplace_back(prefabTemplate->get().GetFilePath());
}
const Template::Links& linkIds = prefabTemplate->get().GetLinks();
for (LinkId linkId : linkIds)
{
auto linkIterator = m_linkIdMap.find(linkId);
if (linkIterator != m_linkIdMap.end())
{
GetDirtyTemplatePathsHelper(linkIterator->second.GetSourceTemplateId(), dirtyTemplatePaths);
}
}
}
bool PrefabSystemComponent::ConnectTemplates(
Link& link,
TemplateId sourceTemplateId,
@@ -770,10 +854,8 @@ namespace AzToolsFramework
return false;
}
#if defined(AZ_ENABLE_TRACING)
Template& sourceTemplate = sourceTemplateReference->get();
Template& targetTemplate = targetTemplateReference->get();
#endif
AZStd::string_view instanceName(instanceIterator->name.GetString(), instanceIterator->name.GetStringLength());
@@ -783,10 +865,9 @@ namespace AzToolsFramework
PrefabDomValue& instance = instanceIterator->value;
AZ_Assert(instance.IsObject(), "Nested instance DOM provided is not a valid JSON object.");
[[maybe_unused]] PrefabDomValueReference sourceTemplateName = PrefabDomUtils::FindPrefabDomValue(instance, PrefabDomUtils::SourceName);
PrefabDomValueReference sourceTemplateName = PrefabDomUtils::FindPrefabDomValue(instance, PrefabDomUtils::SourceName);
AZ_Assert(sourceTemplateName, "Couldn't find source template name in the DOM of the nested instance while creating a link.");
AZ_Assert(
sourceTemplateName->get() == sourceTemplate.GetFilePath().c_str(),
AZ_Assert(sourceTemplateName->get() == sourceTemplate.GetFilePath().c_str(),
"The name of the source template in the nested instance DOM does not match the name of the source template already loaded");
PrefabDomValueReference patchesReference = PrefabDomUtils::FindPrefabDomValue(instance, PrefabDomUtils::PatchesName);
@@ -183,6 +183,12 @@ namespace AzToolsFramework
*/
void SetTemplateDirtyFlag(const TemplateId& templateId, bool dirty) override;
bool AreDirtyTemplatesPresent(TemplateId rootTemplateId) override;
void SaveAllDirtyTemplates(TemplateId rootTemplateId) override;
AZStd::set<AZ::IO::PathView> GetDirtyTemplatePaths(TemplateId rootTemplateId) override;
//////////////////////////////////////////////////////////////////////////
/**
@@ -335,6 +341,9 @@ namespace AzToolsFramework
*/
bool RemoveLinkFromTargetTemplate(const LinkId& linkId, const Link& link);
// Helper function for GetDirtyTemplatePaths(). It uses vector to speed up iteration times.
void GetDirtyTemplatePathsHelper(TemplateId rootTemplateId, AZStd::vector<AZ::IO::PathView>& dirtyTemplatePaths);
// A container for mapping Templates to the Links they may propagate changes to.
AZStd::unordered_map<TemplateId, AZStd::unordered_set<LinkId>> m_templateToLinkIdsMap;
@@ -9,11 +9,12 @@
#pragma once
#include <AzCore/Interface/Interface.h>
#include <AzCore/std/containers/set.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzToolsFramework/Prefab/Instance/Instance.h>
#include <AzToolsFramework/Prefab/Link/Link.h>
#include <AzToolsFramework/Prefab/PrefabIdTypes.h>
#include <AzToolsFramework/Prefab/Template/Template.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
namespace AzToolsFramework
{
@@ -50,6 +51,19 @@ namespace AzToolsFramework
virtual bool IsTemplateDirty(const TemplateId& templateId) = 0;
virtual void SetTemplateDirtyFlag(const TemplateId& templateId, bool dirty) = 0;
//! Recursive function to check if the template is dirty or if any dirty templates are presents in the links of the template.
//! @param rootTemplateId The id of the template provided as the beginning template to check the outgoing links.
virtual bool AreDirtyTemplatesPresent(TemplateId rootTemplateId) = 0;
//! Recursive function to save if the template is dirty and save all the dirty templates in the links of the template.
//! @param rootTemplateId The id of the template provided as the beginning template to check the outgoing links.
virtual void SaveAllDirtyTemplates(TemplateId rootTemplateId) = 0;
//! Recursive function that fetches the set of dirty templates given a starting template to check for outgoing links.
//! @param rootTemplateId The id of the template provided as the beginning template to check the outgoing links.
//! @return The set of dirty template paths populated.
virtual AZStd::set<AZ::IO::PathView> GetDirtyTemplatePaths(TemplateId rootTemplateId) = 0;
virtual PrefabDom& FindTemplateDom(TemplateId templateId) = 0;
virtual void UpdatePrefabTemplate(TemplateId templateId, const PrefabDom& updatedDom) = 0;
virtual void PropagateTemplateChanges(TemplateId templateId, InstanceOptionalReference instanceToExclude = AZStd::nullopt) = 0;
@@ -11,6 +11,7 @@
#include <AzCore/Interface/Interface.h>
#include <AzCore/Math/Vector3.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzToolsFramework/Prefab/PrefabIdTypes.h>
namespace AzToolsFramework
{
@@ -28,6 +29,15 @@ namespace AzToolsFramework
* @return The id of the newly created entity.
*/
virtual AZ::EntityId CreateNewEntityAtPosition(const AZ::Vector3& position, AZ::EntityId parentId) = 0;
//! Constructs and executes the close dialog on a prefab template corresponding to templateId.
//! @param templateId The id of the template the user chose to close.
virtual int ExecuteClosePrefabDialog(TemplateId templateId) = 0;
//! Constructs and executes the save dialog on a prefab template corresponding to templateId.
//! @param templateId The id of the template the user chose to save.
//! @param useSaveAllPrefabsPreference A flag indicating whether SaveAllPrefabsPreference should be used for saving templates.
virtual void ExecuteSavePrefabDialog(TemplateId templateId, bool useSaveAllPrefabsPreference = false) = 0;
};
} // namespace Prefab
@@ -12,6 +12,7 @@
#include <AzCore/IO/SystemFile.h>
#include <AzCore/StringFunc/StringFunc.h>
#include <AzCore/Component/TransformBus.h>
#include <AzCore/std/smart_ptr/make_shared.h>
#include <AzFramework/API/ApplicationAPI.h>
#include <AzFramework/Asset/AssetSystemBus.h>
@@ -27,12 +28,28 @@
#include <AzToolsFramework/UI/Prefab/PrefabIntegrationInterface.h>
#include <AzToolsFramework/UI/UICore/WidgetHelpers.h>
#include <AzQtComponents/Components/Widgets/CheckBox.h>
#include <AzQtComponents/Components/FlowLayout.h>
#include <AzQtComponents/Components/StyleManager.h>
#include <AzQtComponents/Components/Widgets/CardHeader.h>
#include <QApplication>
#include <QCheckBox>
#include <QDialog>
#include <QDialogButtonBox>
#include <QFileDialog>
#include <QFileInfo>
#include <QFrame>
#include <QHBoxLayout>
#include <QLabel>
#include <QMainWindow>
#include <QMenu>
#include <QMessageBox>
#include <QScrollArea>
#include <QVBoxLayout>
#include <QWidget>
namespace AzToolsFramework
{
@@ -43,8 +60,19 @@ namespace AzToolsFramework
PrefabPublicInterface* PrefabIntegrationManager::s_prefabPublicInterface = nullptr;
PrefabEditInterface* PrefabIntegrationManager::s_prefabEditInterface = nullptr;
PrefabLoaderInterface* PrefabIntegrationManager::s_prefabLoaderInterface = nullptr;
PrefabSystemComponentInterface* PrefabIntegrationManager::s_prefabSystemComponentInterface = nullptr;
const AZStd::string PrefabIntegrationManager::s_prefabFileExtension = ".prefab";
static const char* const ClosePrefabDialog = "ClosePrefabDialog";
static const char* const FooterSeparatorLine = "FooterSeparatorLine";
static const char* const PrefabSavedMessageFrame = "PrefabSavedMessageFrame";
static const char* const PrefabSavePreferenceHint = "PrefabSavePreferenceHint";
static const char* const PrefabSaveWarningFrame = "PrefabSaveWarningFrame";
static const char* const SaveDependentPrefabsCard = "SaveDependentPrefabsCard";
static const char* const SavePrefabDialog = "SavePrefabDialog";
static const char* const UnsavedPrefabFileName = "UnsavedPrefabFileName";
void PrefabUserSettings::Reflect(AZ::ReflectContext* context)
{
@@ -88,6 +116,13 @@ namespace AzToolsFramework
return;
}
s_prefabSystemComponentInterface = AZ::Interface<PrefabSystemComponentInterface>::Get();
if (s_prefabSystemComponentInterface == nullptr)
{
AZ_Assert(false, "Prefab - could not get PrefabSystemComponentInterface on PrefabIntegrationManager construction.");
return;
}
EditorContextMenuBus::Handler::BusConnect();
PrefabInstanceContainerNotificationBus::Handler::BusConnect();
AZ::Interface<PrefabIntegrationInterface>::Register(this);
@@ -1050,5 +1085,239 @@ namespace AzToolsFramework
return AZ::EntityId();
}
}
int PrefabIntegrationManager::ExecuteClosePrefabDialog(TemplateId templateId)
{
if (s_prefabSystemComponentInterface->AreDirtyTemplatesPresent(templateId))
{
auto prefabSaveSelectionDialog = ConstructClosePrefabDialog(templateId);
int prefabSaveSelection = prefabSaveSelectionDialog->exec();
if (prefabSaveSelection == QDialog::Accepted)
{
SavePrefabsInDialog(prefabSaveSelectionDialog.get());
}
return prefabSaveSelection;
}
return QDialogButtonBox::DestructiveRole;
}
void PrefabIntegrationManager::ExecuteSavePrefabDialog(TemplateId templateId, bool useSaveAllPrefabsPreference)
{
auto prefabTemplate = s_prefabSystemComponentInterface->FindTemplate(templateId);
AZ::IO::Path prefabTemplatePath = prefabTemplate->get().GetFilePath();
if (s_prefabSystemComponentInterface->IsTemplateDirty(templateId))
{
if (s_prefabLoaderInterface->SaveTemplate(templateId) == false)
{
AZ_Error("Prefab", false, "Template '%s' could not be saved successfully.", prefabTemplatePath.c_str());
return;
}
}
if (s_prefabSystemComponentInterface->AreDirtyTemplatesPresent(templateId))
{
if (useSaveAllPrefabsPreference)
{
SaveAllPrefabsPreference saveAllPrefabsPreference = s_prefabLoaderInterface->GetSaveAllPrefabsPreference();
if (saveAllPrefabsPreference == SaveAllPrefabsPreference::SaveAll)
{
s_prefabSystemComponentInterface->SaveAllDirtyTemplates(templateId);
return;
}
else if (saveAllPrefabsPreference == SaveAllPrefabsPreference::SaveNone)
{
return;
}
}
AZStd::unique_ptr<QDialog> savePrefabDialog = ConstructSavePrefabDialog(templateId, useSaveAllPrefabsPreference);
if (savePrefabDialog)
{
int prefabSaveSelection = savePrefabDialog->exec();
if (prefabSaveSelection == QDialog::Accepted)
{
SavePrefabsInDialog(savePrefabDialog.get());
}
}
}
}
void PrefabIntegrationManager::SavePrefabsInDialog(QDialog* unsavedPrefabsDialog)
{
QList<QLabel*> unsavedPrefabFileLabels = unsavedPrefabsDialog->findChildren<QLabel*>(UnsavedPrefabFileName);
if (unsavedPrefabFileLabels.size() > 0)
{
for (const QLabel* unsavedPrefabFileLabel : unsavedPrefabFileLabels)
{
AZStd::string unsavedPrefabFileName = unsavedPrefabFileLabel->property("FilePath").toString().toUtf8().data();
AzToolsFramework::Prefab::TemplateId unsavedPrefabTemplateId =
s_prefabSystemComponentInterface->GetTemplateIdFromFilePath(unsavedPrefabFileName.data());
bool isTemplateSavedSuccessfully = s_prefabLoaderInterface->SaveTemplate(unsavedPrefabTemplateId);
AZ_Error("Prefab", isTemplateSavedSuccessfully, "Prefab '%s' could not be saved successfully.", unsavedPrefabFileName.c_str());
}
}
}
AZStd::unique_ptr<QDialog> PrefabIntegrationManager::ConstructSavePrefabDialog(TemplateId templateId, bool useSaveAllPrefabsPreference)
{
AZStd::unique_ptr<QDialog> savePrefabDialog = AZStd::make_unique<QDialog>(AzToolsFramework::GetActiveWindow());
savePrefabDialog->setWindowTitle("Unsaved files detected");
// Main Content section begins.
savePrefabDialog->setObjectName(SavePrefabDialog);
QBoxLayout* contentLayout = new QVBoxLayout(savePrefabDialog.get());
QFrame* prefabSavedMessageFrame = new QFrame(savePrefabDialog.get());
QHBoxLayout* prefabSavedMessageLayout = new QHBoxLayout(savePrefabDialog.get());
prefabSavedMessageFrame->setObjectName(PrefabSavedMessageFrame);
prefabSavedMessageFrame->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum);
// Add a checkMark icon next to the level entities saved message.
QPixmap checkMarkIcon(QString(":/Notifications/checkmark.svg"));
QLabel* prefabSavedSuccessfullyIconContainer = new QLabel(savePrefabDialog.get());
prefabSavedSuccessfullyIconContainer->setPixmap(checkMarkIcon);
prefabSavedSuccessfullyIconContainer->setFixedWidth(checkMarkIcon.width());
// Add a message that level entities are saved successfully.
auto prefabTemplate = s_prefabSystemComponentInterface->FindTemplate(templateId);
AZ::IO::Path prefabTemplatePath = prefabTemplate->get().GetFilePath();
QLabel* prefabSavedSuccessfullyLabel = new QLabel(
QString("Prefab '<b>%1</b>' has been saved. Do you want to save the below dependent prefabs too?").arg(prefabTemplatePath.c_str()),
savePrefabDialog.get());
prefabSavedMessageLayout->addWidget(prefabSavedSuccessfullyIconContainer);
prefabSavedMessageLayout->addWidget(prefabSavedSuccessfullyLabel);
prefabSavedMessageFrame->setLayout(prefabSavedMessageLayout);
contentLayout->addWidget(prefabSavedMessageFrame);
AZStd::unique_ptr<AzQtComponents::Card> unsavedPrefabsContainer = ConstructUnsavedPrefabsCard(templateId);
contentLayout->addWidget(unsavedPrefabsContainer.release());
contentLayout->addStretch();
// Footer section begins.
QHBoxLayout* footerLayout = new QHBoxLayout(savePrefabDialog.get());
if (useSaveAllPrefabsPreference)
{
QFrame* footerSeparatorLine = new QFrame(savePrefabDialog.get());
footerSeparatorLine->setObjectName(FooterSeparatorLine);
footerSeparatorLine->setFrameShape(QFrame::HLine);
contentLayout->addWidget(footerSeparatorLine);
QLabel* prefabSavePreferenceHint = new QLabel(
"<u>You can prevent this window from showing in the future by updating your global save preferences.</u>",
savePrefabDialog.get());
prefabSavePreferenceHint->setToolTip(
"Go to 'Edit > Editor Settings > Global Preferences... > Global save preferences' to update your preference");
prefabSavePreferenceHint->setObjectName(PrefabSavePreferenceHint);
footerLayout->addWidget(prefabSavePreferenceHint);
}
QDialogButtonBox* prefabSaveConfirmationButtons =
new QDialogButtonBox(QDialogButtonBox::Save | QDialogButtonBox::No, savePrefabDialog.get());
footerLayout->addWidget(prefabSaveConfirmationButtons);
contentLayout->addLayout(footerLayout);
connect(prefabSaveConfirmationButtons, &QDialogButtonBox::accepted, savePrefabDialog.get(), &QDialog::accept);
connect(prefabSaveConfirmationButtons, &QDialogButtonBox::rejected, savePrefabDialog.get(), &QDialog::reject);
AzQtComponents::StyleManager::setStyleSheet(savePrefabDialog->parentWidget(), QStringLiteral("style:Editor.qss"));
savePrefabDialog->setLayout(contentLayout);
return AZStd::move(savePrefabDialog);
}
AZStd::shared_ptr<QDialog> PrefabIntegrationManager::ConstructClosePrefabDialog(TemplateId templateId)
{
AZStd::shared_ptr<QDialog> closePrefabDialog = AZStd::make_shared<QDialog>(AzToolsFramework::GetActiveWindow());
closePrefabDialog->setWindowTitle("Unsaved files detected");
AZStd::weak_ptr<QDialog> closePrefabDialogWeakPtr(closePrefabDialog);
closePrefabDialog->setObjectName(ClosePrefabDialog);
// Main Content section begins.
QVBoxLayout* contentLayout = new QVBoxLayout(closePrefabDialog.get());
QFrame* prefabSaveWarningFrame = new QFrame(closePrefabDialog.get());
QHBoxLayout* levelEntitiesSaveQuestionLayout = new QHBoxLayout(closePrefabDialog.get());
prefabSaveWarningFrame->setObjectName(PrefabSaveWarningFrame);
// Add a warning icon next to save prefab warning.
prefabSaveWarningFrame->setLayout(levelEntitiesSaveQuestionLayout);
QPixmap warningIcon(QString(":/Notifications/warning.svg"));
QLabel* warningIconContainer = new QLabel(closePrefabDialog.get());
warningIconContainer->setPixmap(warningIcon);
warningIconContainer->setFixedWidth(warningIcon.width());
levelEntitiesSaveQuestionLayout->addWidget(warningIconContainer);
// Ask user if they want to save entities in level.
QLabel* prefabSaveQuestionLabel = new QLabel("Do you want to save the below unsaved prefabs?", closePrefabDialog.get());
levelEntitiesSaveQuestionLayout->addWidget(prefabSaveQuestionLabel);
contentLayout->addWidget(prefabSaveWarningFrame);
auto templateToSave = s_prefabSystemComponentInterface->FindTemplate(templateId);
AZ::IO::Path templateToSaveFilePath = templateToSave->get().GetFilePath();
AZStd::unique_ptr<AzQtComponents::Card> unsavedPrefabsCard = ConstructUnsavedPrefabsCard(templateId);
contentLayout->addWidget(unsavedPrefabsCard.release());
contentLayout->addStretch();
QHBoxLayout* footerLayout = new QHBoxLayout(closePrefabDialog.get());
QDialogButtonBox* prefabSaveConfirmationButtons = new QDialogButtonBox(
QDialogButtonBox::Save | QDialogButtonBox::Discard | QDialogButtonBox::Cancel, closePrefabDialog.get());
footerLayout->addWidget(prefabSaveConfirmationButtons);
contentLayout->addLayout(footerLayout);
QObject::connect(prefabSaveConfirmationButtons, &QDialogButtonBox::accepted, closePrefabDialog.get(), &QDialog::accept);
QObject::connect(prefabSaveConfirmationButtons, &QDialogButtonBox::rejected, closePrefabDialog.get(), &QDialog::reject);
QObject::connect(
prefabSaveConfirmationButtons, &QDialogButtonBox::clicked, closePrefabDialog.get(),
[closePrefabDialogWeakPtr, prefabSaveConfirmationButtons](QAbstractButton* button)
{
int prefabSaveSelection = prefabSaveConfirmationButtons->buttonRole(button);
closePrefabDialogWeakPtr.lock()->done(prefabSaveSelection);
});
AzQtComponents::StyleManager::setStyleSheet(closePrefabDialog.get(), QStringLiteral("style:Editor.qss"));
closePrefabDialog->setLayout(contentLayout);
return closePrefabDialog;
}
AZStd::unique_ptr<AzQtComponents::Card> PrefabIntegrationManager::ConstructUnsavedPrefabsCard(TemplateId templateId)
{
FlowLayout* unsavedPrefabsLayout = new FlowLayout(AzToolsFramework::GetActiveWindow());
AZStd::set<AZ::IO::PathView> dirtyTemplatePaths = s_prefabSystemComponentInterface->GetDirtyTemplatePaths(templateId);
for (AZ::IO::PathView dirtyTemplatePath : dirtyTemplatePaths)
{
QLabel* prefabNameLabel =
new QLabel(QString("<u>%1</u>").arg(dirtyTemplatePath.Filename().Native().data()), AzToolsFramework::GetActiveWindow());
prefabNameLabel->setObjectName(UnsavedPrefabFileName);
prefabNameLabel->setWordWrap(true);
prefabNameLabel->setToolTip(dirtyTemplatePath.Native().data());
prefabNameLabel->setProperty("FilePath", dirtyTemplatePath.Native().data());
unsavedPrefabsLayout->addWidget(prefabNameLabel);
}
AZStd::unique_ptr<AzQtComponents::Card> unsavedPrefabsContainer = AZStd::make_unique<AzQtComponents::Card>(AzToolsFramework::GetActiveWindow());
unsavedPrefabsContainer->setObjectName(SaveDependentPrefabsCard);
unsavedPrefabsContainer->setTitle("Unsaved Prefabs");
unsavedPrefabsContainer->header()->setHasContextMenu(false);
unsavedPrefabsContainer->header()->setIcon(QIcon(QStringLiteral(":/Entity/prefab_edit.svg")));
QFrame* unsavedPrefabsFrame = new QFrame(unsavedPrefabsContainer.get());
unsavedPrefabsFrame->setLayout(unsavedPrefabsLayout);
QScrollArea* unsavedPrefabsScrollArea = new QScrollArea(unsavedPrefabsContainer.get());
unsavedPrefabsScrollArea->setWidget(unsavedPrefabsFrame);
unsavedPrefabsScrollArea->setWidgetResizable(true);
unsavedPrefabsContainer->setContentWidget(unsavedPrefabsScrollArea);
return AZStd::move(unsavedPrefabsContainer);
}
}
}
@@ -15,12 +15,15 @@
#include <AzToolsFramework/AssetBrowser/AssetBrowserSourceDropBus.h>
#include <AzToolsFramework/Editor/EditorContextMenuBus.h>
#include <AzToolsFramework/Prefab/PrefabPublicInterface.h>
#include <AzToolsFramework/Prefab/PrefabSystemComponentInterface.h>
#include <AzToolsFramework/UI/Prefab/LevelRootUiHandler.h>
#include <AzToolsFramework/UI/Prefab/PrefabEditManager.h>
#include <AzToolsFramework/UI/Prefab/PrefabIntegrationBus.h>
#include <AzToolsFramework/UI/Prefab/PrefabIntegrationInterface.h>
#include <AzToolsFramework/UI/Prefab/PrefabUiHandler.h>
#include <AzQtComponents/Components/Widgets/Card.h>
namespace AzToolsFramework
{
namespace Prefab
@@ -49,6 +52,7 @@ namespace AzToolsFramework
, public AssetBrowser::AssetBrowserSourceDropBus::Handler
, public PrefabInstanceContainerNotificationBus::Handler
, public PrefabIntegrationInterface
, public QObject
{
public:
AZ_CLASS_ALLOCATOR(PrefabIntegrationManager, AZ::SystemAllocator, 0);
@@ -72,6 +76,8 @@ namespace AzToolsFramework
// PrefabIntegrationInterface...
AZ::EntityId CreateNewEntityAtPosition(const AZ::Vector3& position, AZ::EntityId parentId) override;
int ExecuteClosePrefabDialog(TemplateId templateId) override;
void ExecuteSavePrefabDialog(TemplateId templateId, bool useSaveAllPrefabsPreference) override;
private:
// Manages the Edit Mode UI for prefabs
@@ -124,12 +130,19 @@ namespace AzToolsFramework
static AZ::u32 GetSliceFlags(const AZ::Edit::ElementData* editData, const AZ::Edit::ClassData* classData);
AZStd::shared_ptr<QDialog> ConstructClosePrefabDialog(TemplateId templateId);
AZStd::unique_ptr<AzQtComponents::Card> ConstructUnsavedPrefabsCard(TemplateId templateId);
AZStd::unique_ptr<QDialog> ConstructSavePrefabDialog(TemplateId templateId, bool useSaveAllPrefabsPreference);
void SavePrefabsInDialog(QDialog* unsavedPrefabsDialog);
static const AZStd::string s_prefabFileExtension;
static EditorEntityUiInterface* s_editorEntityUiInterface;
static PrefabPublicInterface* s_prefabPublicInterface;
static PrefabEditInterface* s_prefabEditInterface;
static PrefabLoaderInterface* s_prefabLoaderInterface;
static PrefabSystemComponentInterface* s_prefabSystemComponentInterface;
};
}
}
@@ -10,6 +10,25 @@
namespace AzToolsFramework
{
AzFramework::ClickDetector::ClickEvent ClickDetectorEventFromViewportInteraction(
const ViewportInteraction::MouseInteractionEvent& mouseInteraction)
{
if (mouseInteraction.m_mouseInteraction.m_mouseButtons.Left())
{
if (mouseInteraction.m_mouseEvent == ViewportInteraction::MouseEvent::Down)
{
return AzFramework::ClickDetector::ClickEvent::Down;
}
if (mouseInteraction.m_mouseEvent == ViewportInteraction::MouseEvent::Up)
{
return AzFramework::ClickDetector::ClickEvent::Up;
}
}
return AzFramework::ClickDetector::ClickEvent::Nil;
}
float ManipulatorLineBoundWidth(const AzFramework::ViewportId viewportId /*= AzFramework::InvalidViewportId*/)
{
float lineBoundWidth = 0.0f;
@@ -250,8 +250,6 @@ namespace AzToolsFramework
virtual AZ::Vector3 PickTerrain(const AzFramework::ScreenPoint& point) = 0;
//! Return the terrain height given a world position in 2d (xy plane).
virtual float TerrainHeight(const AZ::Vector2& position) = 0;
//! Given the current view frustum (viewport) return all visible entities.
virtual void FindVisibleEntities(AZStd::vector<AZ::EntityId>& visibleEntities) = 0;
//! Is the user holding a modifier key to move the manipulator space from local to world.
virtual bool ShowingWorldSpace() = 0;
//! Return the widget to use as the parent for the viewport context menu.
@@ -269,7 +267,20 @@ namespace AzToolsFramework
//! Type to inherit to implement MainEditorViewportInteractionRequests.
using MainEditorViewportInteractionRequestBus = AZ::EBus<MainEditorViewportInteractionRequests, ViewportEBusTraits>;
//! Viewport requests for managing the viewport's cursor state.
//! Editor entity requests to be made about the viewport.
class EditorEntityViewportInteractionRequests
{
public:
//! Given the current view frustum (viewport) return all visible entities.
virtual void FindVisibleEntities(AZStd::vector<AZ::EntityId>& visibleEntities) = 0;
protected:
~EditorEntityViewportInteractionRequests() = default;
};
using EditorEntityViewportInteractionRequestBus = AZ::EBus<EditorEntityViewportInteractionRequests, ViewportEBusTraits>;
//! Viewport requests for managing the viewport cursor state.
class ViewportMouseCursorRequests
{
public:
@@ -321,23 +332,8 @@ namespace AzToolsFramework
//! Maps a mouse interaction event to a ClickDetector event.
//! @note Function only cares about up or down events, all other events are mapped to Nil (ignored).
inline AzFramework::ClickDetector::ClickEvent ClickDetectorEventFromViewportInteraction(
const ViewportInteraction::MouseInteractionEvent& mouseInteraction)
{
if (mouseInteraction.m_mouseInteraction.m_mouseButtons.Left())
{
if (mouseInteraction.m_mouseEvent == ViewportInteraction::MouseEvent::Down)
{
return AzFramework::ClickDetector::ClickEvent::Down;
}
if (mouseInteraction.m_mouseEvent == ViewportInteraction::MouseEvent::Up)
{
return AzFramework::ClickDetector::ClickEvent::Up;
}
}
return AzFramework::ClickDetector::ClickEvent::Nil;
}
AzFramework::ClickDetector::ClickEvent ClickDetectorEventFromViewportInteraction(
const ViewportInteraction::MouseInteractionEvent& mouseInteraction);
//! Wrap EBus call to retrieve manipulator line bound width.
//! @note It is possible to pass AzFramework::InvalidViewportId (the default) to perform a Broadcast as opposed to a targeted Event.
@@ -9,8 +9,8 @@
#include "EditorBoxSelect.h"
#include <AzFramework/Entity/EntityDebugDisplayBus.h>
#include <AzToolsFramework/ViewportSelection/EditorSelectionUtil.h>
#include <AzToolsFramework/Viewport/ViewportMessages.h>
#include <AzToolsFramework/ViewportSelection/EditorSelectionUtil.h>
#include <QApplication>
@@ -19,11 +19,15 @@ namespace AzToolsFramework
static const AZ::Color s_boxSelectColor = AZ::Color(1.0f, 1.0f, 1.0f, 0.4f);
static const float s_boxSelectLineWidth = 2.0f;
void EditorBoxSelect::HandleMouseInteraction(
const ViewportInteraction::MouseInteractionEvent& mouseInteraction)
void EditorBoxSelect::HandleMouseInteraction(const ViewportInteraction::MouseInteractionEvent& mouseInteraction)
{
AZ_PROFILE_FUNCTION(AzToolsFramework);
if (mouseInteraction.m_mouseEvent == ViewportInteraction::MouseEvent::Down)
{
m_cursorPositionAtDownEvent = mouseInteraction.m_mouseInteraction.m_mousePick.m_screenCoordinates;
}
m_cursorState.SetCurrentPosition(mouseInteraction.m_mouseInteraction.m_mousePick.m_screenCoordinates);
const auto selectClickEvent = ClickDetectorEventFromViewportInteraction(mouseInteraction);
@@ -35,12 +39,7 @@ namespace AzToolsFramework
m_leftMouseDown(mouseInteraction);
}
m_boxSelectRegion = QRect
{
ViewportInteraction::QPointFromScreenPoint(
mouseInteraction.m_mouseInteraction.m_mousePick.m_screenCoordinates),
QSize { 0, 0 }
};
m_boxSelectRegion = QRect{ ViewportInteraction::QPointFromScreenPoint(m_cursorPositionAtDownEvent), QSize{ 0, 0 } };
}
if (m_boxSelectRegion)
@@ -87,11 +86,11 @@ namespace AzToolsFramework
AZ::Vector2 viewportSize = AzToolsFramework::GetCameraState(viewportInfo.m_viewportId).m_viewportSize;
debugDisplay.DrawWireQuad2d(
AZ::Vector2(
aznumeric_cast<float>(m_boxSelectRegion->x()), aznumeric_cast<float>(m_boxSelectRegion->y())) / viewportSize,
AZ::Vector2(aznumeric_cast<float>(m_boxSelectRegion->x()), aznumeric_cast<float>(m_boxSelectRegion->y())) / viewportSize,
AZ::Vector2(
aznumeric_cast<float>(m_boxSelectRegion->x()) + aznumeric_cast<float>(m_boxSelectRegion->width()),
aznumeric_cast<float>(m_boxSelectRegion->y()) + aznumeric_cast<float>(m_boxSelectRegion->height())) / viewportSize,
aznumeric_cast<float>(m_boxSelectRegion->y()) + aznumeric_cast<float>(m_boxSelectRegion->height())) /
viewportSize,
0.f);
debugDisplay.DepthTestOn();
@@ -101,8 +100,7 @@ namespace AzToolsFramework
}
}
void EditorBoxSelect::DisplayScene(
const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay)
void EditorBoxSelect::DisplayScene(const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay)
{
if (m_displayScene)
{
@@ -122,15 +120,14 @@ namespace AzToolsFramework
m_mouseMove = mouseMove;
}
void EditorBoxSelect::InstallLeftMouseUp(
const AZStd::function<void()>& leftMouseUp)
void EditorBoxSelect::InstallLeftMouseUp(const AZStd::function<void()>& leftMouseUp)
{
m_leftMouseUp = leftMouseUp;
}
void EditorBoxSelect::InstallDisplayScene(
const AZStd::function<void(const AzFramework::ViewportInfo& viewportInfo,
AzFramework::DebugDisplayRequests& debugDisplay)>& displayScene)
const AZStd::function<void(const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay)>&
displayScene)
{
m_displayScene = displayScene;
}
@@ -81,5 +81,6 @@ namespace AzToolsFramework
ViewportInteraction::KeyboardModifiers m_previousModifiers; //!< Modifier keys active on the previous frame.
AzFramework::ClickDetector m_clickDetector; //!< Utility type to detect if a mouse click or move has occurred.
AzFramework::CursorState m_cursorState; //!< Utility type to track the current cursor position (and movement/delta).
AzFramework::ScreenPoint m_cursorPositionAtDownEvent; //!< The position of the cursor when first potentially starting a box select.
};
} // namespace AzToolsFramework
@@ -19,7 +19,7 @@
namespace AzToolsFramework
{
// default ray length for picking in the viewport
static const float s_pickRayLength = 1000.0f;
static const float EditorPickRayLength = 1000.0f;
AZ::Vector3 CalculateCenterOffset(const AZ::EntityId entityId, const EditorTransformComponentSelectionRequests::Pivot pivot)
{
@@ -60,16 +60,27 @@ namespace AzToolsFramework
return screenPosition;
}
bool AabbIntersectMouseRay(const ViewportInteraction::MouseInteraction& mouseInteraction, const AZ::Aabb& aabb)
bool AabbIntersectRay(const AZ::Vector3& origin, const AZ::Vector3& direction, const AZ::Aabb& aabb, float& distance)
{
AZ_PROFILE_FUNCTION(AzToolsFramework);
const AZ::Vector3 rayScaledDir = mouseInteraction.m_mousePick.m_rayDirection * s_pickRayLength;
const AZ::Vector3 rayScaledDir = direction * EditorPickRayLength;
AZ::Vector3 startNormal;
float t, end;
return AZ::Intersect::IntersectRayAABB(
mouseInteraction.m_mousePick.m_rayOrigin, rayScaledDir, rayScaledDir.GetReciprocal(), aabb, t, end, startNormal) > 0;
AZ::Vector3 startNormal;
if (AZ::Intersect::IntersectRayAABB(origin, rayScaledDir, rayScaledDir.GetReciprocal(), aabb, t, end, startNormal) > 0)
{
distance = t * EditorPickRayLength;
return true;
}
return false;
}
bool AabbIntersectMouseRay(const ViewportInteraction::MouseInteraction& mouseInteraction, const AZ::Aabb& aabb)
{
float unused;
return AabbIntersectRay(mouseInteraction.m_mousePick.m_rayOrigin, mouseInteraction.m_mousePick.m_rayDirection, aabb, unused);
}
bool PickEntity(
@@ -117,8 +128,7 @@ namespace AzToolsFramework
{
float scaling = 1.0f;
ViewportInteraction::ViewportInteractionRequestBus::EventResult(
scaling, viewportId,
&ViewportInteraction::ViewportInteractionRequestBus::Events::DeviceScalingFactor);
scaling, viewportId, &ViewportInteraction::ViewportInteractionRequestBus::Events::DeviceScalingFactor);
return scaling;
}
@@ -46,6 +46,10 @@ namespace AzToolsFramework
//! in screen space intersected an aabb in world space.
bool AabbIntersectMouseRay(const ViewportInteraction::MouseInteraction& mouseInteraction, const AZ::Aabb& aabb);
//! Wrapper to perform an intersection between a ray and an aabb.
//! Note: direction should be normalized (it is scaled internally by the editor pick distance).
bool AabbIntersectRay(const AZ::Vector3& origin, const AZ::Vector3& direction, const AZ::Aabb& aabb, float& distance);
//! Return if a mouse interaction (pick ray) did intersect the tested EntityId.
bool PickEntity(
AZ::EntityId entityId, const ViewportInteraction::MouseInteraction& mouseInteraction, float& closestDistance, int viewportId);
@@ -44,39 +44,46 @@ namespace AzToolsFramework
AZ_CVAR(
float,
cl_viewportGizmoAxisLineWidth,
ed_viewportGizmoAxisLineWidth,
4.0f,
nullptr,
AZ::ConsoleFunctorFlags::Null,
"The width of the line for the viewport axis gizmo");
AZ_CVAR(
float,
cl_viewportGizmoAxisLineLength,
ed_viewportGizmoAxisLineLength,
0.7f,
nullptr,
AZ::ConsoleFunctorFlags::Null,
"The length of the line for the viewport axis gizmo");
AZ_CVAR(
float,
cl_viewportGizmoAxisLabelOffset,
ed_viewportGizmoAxisLabelOffset,
1.15f,
nullptr,
AZ::ConsoleFunctorFlags::Null,
"The offset of the label for the viewport axis gizmo");
AZ_CVAR(
float,
cl_viewportGizmoAxisLabelSize,
ed_viewportGizmoAxisLabelSize,
1.0f,
nullptr,
AZ::ConsoleFunctorFlags::Null,
"The size of each label for the viewport axis gizmo");
AZ_CVAR(
AZ::Vector2,
cl_viewportGizmoAxisScreenPosition,
ed_viewportGizmoAxisScreenPosition,
AZ::Vector2(0.045f, 0.9f),
nullptr,
AZ::ConsoleFunctorFlags::Null,
"The screen position of the gizmo in normalized (0-1) ndc space");
AZ_CVAR(
bool,
ed_viewportStickySelect,
true,
nullptr,
AZ::ConsoleFunctorFlags::Null,
"Sticky select implies a single click will not change selection with an entity already selected");
// strings related to new viewport interaction model (EditorTransformComponentSelection)
static const char* const s_togglePivotTitleRightClick = "Toggle pivot";
@@ -991,14 +998,19 @@ namespace AzToolsFramework
// ask the visible entity data cache if the entity is selectable in the viewport
// (useful in the context of drawing when we only care about entities we can see)
static bool SelectableInVisibleViewportCache(const EditorVisibleEntityDataCache& entityDataCache, const AZ::EntityId entityId)
// note: return the index if it is selectable, nullopt otherwise
static AZStd::optional<size_t> SelectableInVisibleViewportCache(
const EditorVisibleEntityDataCache& entityDataCache, const AZ::EntityId entityId)
{
if (auto entityIndex = entityDataCache.GetVisibleEntityIndexFromId(entityId))
{
return entityDataCache.IsVisibleEntitySelectableInViewport(*entityIndex);
if (entityDataCache.IsVisibleEntitySelectableInViewport(*entityIndex))
{
return *entityIndex;
}
}
return false;
return AZStd::nullopt;
}
static AZ::ComponentId GetTransformComponentId(const AZ::EntityId entityId)
@@ -1709,17 +1721,17 @@ namespace AzToolsFramework
m_pivotOverrideFrame.Reset();
}
bool EditorTransformComponentSelection::SelectDeselect(const AZ::EntityId entityIdUnderCursor)
bool EditorTransformComponentSelection::SelectDeselect(const AZ::EntityId entityId)
{
AZ_PROFILE_FUNCTION(AzToolsFramework);
if (entityIdUnderCursor.IsValid())
if (entityId.IsValid())
{
if (IsEntitySelectedInternal(entityIdUnderCursor, m_selectedEntityIds))
if (IsEntitySelectedInternal(entityId, m_selectedEntityIds))
{
if (!UndoRedoOperationInProgress())
{
RemoveEntityFromSelection(entityIdUnderCursor);
RemoveEntityFromSelection(entityId);
const auto nextEntityIds = EntityIdVectorFromContainer(m_selectedEntityIds);
@@ -1742,7 +1754,7 @@ namespace AzToolsFramework
{
if (!UndoRedoOperationInProgress())
{
AddEntityToSelection(entityIdUnderCursor);
AddEntityToSelection(entityId);
const auto nextEntityIds = EntityIdVectorFromContainer(m_selectedEntityIds);
@@ -1783,25 +1795,21 @@ namespace AzToolsFramework
// for entities selected with no bounds of their own (just TransformComponent)
// check selection against the selection indicator aabb
for (AZ::EntityId entityId : m_selectedEntityIds)
for (const AZ::EntityId& entityId : m_selectedEntityIds)
{
if (!SelectableInVisibleViewportCache(*m_entityDataCache, entityId))
if (const auto entityIndex = SelectableInVisibleViewportCache(*m_entityDataCache, entityId); entityIndex.has_value())
{
continue;
}
const AZ::Transform& worldFromLocal = m_entityDataCache->GetVisibleEntityTransform(*entityIndex);
const AZ::Vector3 boxPosition = worldFromLocal.TransformPoint(CalculateCenterOffset(entityId, m_pivotMode));
const AZ::Vector3 scaledSize =
AZ::Vector3(s_pivotSize) * CalculateScreenToWorldMultiplier(worldFromLocal.GetTranslation(), cameraState);
AZ::Transform worldFromLocal;
AZ::TransformBus::EventResult(worldFromLocal, entityId, &AZ::TransformBus::Events::GetWorldTM);
const AZ::Vector3 boxPosition = worldFromLocal.TransformPoint(CalculateCenterOffset(entityId, m_pivotMode));
const AZ::Vector3 scaledSize =
AZ::Vector3(s_pivotSize) * CalculateScreenToWorldMultiplier(worldFromLocal.GetTranslation(), cameraState);
if (AabbIntersectMouseRay(
mouseInteraction.m_mouseInteraction, AZ::Aabb::CreateFromMinMax(boxPosition - scaledSize, boxPosition + scaledSize)))
{
m_cachedEntityIdUnderCursor = entityId;
if (AabbIntersectMouseRay(
mouseInteraction.m_mouseInteraction,
AZ::Aabb::CreateFromMinMax(boxPosition - scaledSize, boxPosition + scaledSize)))
{
m_cachedEntityIdUnderCursor = entityId;
}
}
}
@@ -3490,7 +3498,7 @@ namespace AzToolsFramework
const auto cameraProjection = AzFramework::CameraProjection(gizmoCameraState);
// screen space offset to move the 2d gizmo around
const AZ::Vector2 screenOffset = AZ::Vector2(cl_viewportGizmoAxisScreenPosition) - AZ::Vector2(0.5f, 0.5f);
const AZ::Vector2 screenOffset = AZ::Vector2(ed_viewportGizmoAxisScreenPosition) - AZ::Vector2(0.5f, 0.5f);
// map from a position in world space (relative to the the gizmo camera near the origin) to a position in
// screen space
@@ -3502,7 +3510,7 @@ namespace AzToolsFramework
};
// get all important axis positions in screen space
const float lineLength = cl_viewportGizmoAxisLineLength;
const float lineLength = ed_viewportGizmoAxisLineLength;
const auto gizmoStart = calculateGizmoAxis(AZ::Vector3::CreateZero());
const auto gizmoEndAxisX = calculateGizmoAxis(-AZ::Vector3::CreateAxisX() * lineLength);
const auto gizmoEndAxisY = calculateGizmoAxis(-AZ::Vector3::CreateAxisY() * lineLength);
@@ -3513,7 +3521,7 @@ namespace AzToolsFramework
const AZ::Vector2 gizmoAxisZ = gizmoEndAxisZ - gizmoStart;
// draw the axes of the gizmo
debugDisplay.SetLineWidth(cl_viewportGizmoAxisLineWidth);
debugDisplay.SetLineWidth(ed_viewportGizmoAxisLineWidth);
debugDisplay.SetColor(AZ::Colors::Red);
debugDisplay.DrawLine2d(gizmoStart, gizmoEndAxisX, 1.0f);
debugDisplay.SetColor(AZ::Colors::Lime);
@@ -3522,14 +3530,14 @@ namespace AzToolsFramework
debugDisplay.DrawLine2d(gizmoStart, gizmoEndAxisZ, 1.0f);
debugDisplay.SetLineWidth(1.0f);
const float labelOffset = cl_viewportGizmoAxisLabelOffset;
const float labelOffset = ed_viewportGizmoAxisLabelOffset;
const float screenScale = GetScreenDisplayScaling(viewportId);
const auto labelXScreenPosition = (gizmoStart + (gizmoAxisX * labelOffset)) * editorCameraState.m_viewportSize * screenScale;
const auto labelYScreenPosition = (gizmoStart + (gizmoAxisY * labelOffset)) * editorCameraState.m_viewportSize * screenScale;
const auto labelZScreenPosition = (gizmoStart + (gizmoAxisZ * labelOffset)) * editorCameraState.m_viewportSize * screenScale;
// draw the label of of each axis for the gizmo
const float labelSize = cl_viewportGizmoAxisLabelSize;
const float labelSize = ed_viewportGizmoAxisLabelSize;
debugDisplay.SetColor(AZ::Colors::White);
debugDisplay.Draw2dTextLabel(labelXScreenPosition.GetX(), labelXScreenPosition.GetY(), labelSize, "X", true);
debugDisplay.Draw2dTextLabel(labelYScreenPosition.GetX(), labelYScreenPosition.GetY(), labelSize, "Y", true);
@@ -9,6 +9,7 @@
#pragma once
#include <AzCore/Component/TransformBus.h>
#include <AzCore/Console/IConsole.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/optional.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
@@ -32,6 +33,8 @@
namespace AzToolsFramework
{
AZ_CVAR_EXTERNED(bool, ed_viewportStickySelect);
class EditorVisibleEntityDataCache;
using EntityIdSet = AZStd::unordered_set<AZ::EntityId>; //!< Alias for unordered_set of EntityIds.
@@ -170,14 +173,11 @@ namespace AzToolsFramework
//! ViewportInteraction::ViewportSelectionRequests
//! Intercept all viewport mouse events and respond to inputs.
bool HandleMouseInteraction(
const ViewportInteraction::MouseInteractionEvent& mouseInteraction) override;
bool HandleMouseInteraction(const ViewportInteraction::MouseInteractionEvent& mouseInteraction) override;
void DisplayViewportSelection(
const AzFramework::ViewportInfo& viewportInfo,
AzFramework::DebugDisplayRequests& debugDisplay) override;
const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) override;
void DisplayViewportSelection2d(
const AzFramework::ViewportInfo& viewportInfo,
AzFramework::DebugDisplayRequests& debugDisplay) override;
const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) override;
//! Add an entity to the current selection
void AddEntityToSelection(AZ::EntityId entityId);
@@ -206,7 +206,7 @@ namespace AzToolsFramework
bool IsEntitySelected(AZ::EntityId entityId) const;
void SetSelectedEntities(const EntityIdList& entityIds);
void DeselectEntities();
bool SelectDeselect(AZ::EntityId entityIdUnderCursor);
bool SelectDeselect(AZ::EntityId entityId);
void RefreshSelectedEntityIds();
void RefreshSelectedEntityIds(const EntityIdList& selectedEntityIds);
@@ -253,11 +253,10 @@ namespace AzToolsFramework
void SnapSelectedEntitiesToWorldGrid(float gridSize) override;
// EditorManipulatorCommandUndoRedoRequestBus ...
void UndoRedoEntityManipulatorCommand(
AZ::u8 pivotOverride, const AZ::Transform& transform, AZ::EntityId entityId) override;
void UndoRedoEntityManipulatorCommand(AZ::u8 pivotOverride, const AZ::Transform& transform, AZ::EntityId entityId) override;
// EditorContextMenuBus...
void PopulateEditorGlobalContextMenu(QMenu* menu, const AZ::Vector2 & point, int flags) override;
void PopulateEditorGlobalContextMenu(QMenu* menu, const AZ::Vector2& point, int flags) override;
int GetMenuPosition() const override;
AZStd::string GetMenuIdentifier() const override;
@@ -266,8 +265,7 @@ namespace AzToolsFramework
// ToolsApplicationNotificationBus ...
void BeforeEntitySelectionChanged() override;
void AfterEntitySelectionChanged(
const EntityIdList& newlySelectedEntities, const EntityIdList& newlyDeselectedEntities) override;
void AfterEntitySelectionChanged(const EntityIdList& newlySelectedEntities, const EntityIdList& newlyDeselectedEntities) override;
// TransformNotificationBus ...
void OnTransformChanged(const AZ::Transform& localTM, const AZ::Transform& worldTM) override;
@@ -318,7 +316,8 @@ namespace AzToolsFramework
EntityIdManipulators m_entityIdManipulators; //!< Mapping from a Manipulator to potentially many EntityIds.
EditorBoxSelect m_boxSelect; //!< Type responsible for handling box select.
AZStd::unique_ptr<EntityManipulatorCommand> m_manipulatorMoveCommand; //!< Track adjustments to manipulator translation and orientation (during mouse press/move).
//! Track adjustments to manipulator translation and orientation (during mouse press/move).
AZStd::unique_ptr<EntityManipulatorCommand> m_manipulatorMoveCommand;
AZStd::vector<AZStd::unique_ptr<QAction>> m_actions; //!< What actions are tied to this handler.
ViewportInteraction::KeyboardModifiers m_previousModifiers; //!< What modifiers were held last frame.
EditorContextMenu m_contextMenu; //!< Viewport right click context menu.
@@ -328,8 +327,10 @@ namespace AzToolsFramework
ReferenceFrame m_referenceFrame = ReferenceFrame::Parent; //!< What reference frame is the Manipulator currently operating in.
Frame m_axisPreview; //!< Axes of entity at the time of mouse down to indicate delta of translation.
bool m_triedToRefresh = false; //!< Did a refresh event occur to recalculate the current Manipulator transform.
bool m_didSetSelectedEntities = false; //!< Was EditorTransformComponentSelection responsible for the most recent entity selection change.
bool m_selectedEntityIdsAndManipulatorsDirty = false; //!< Do the active manipulators need to recalculated after a modification (lock/visibility etc).
//! Was EditorTransformComponentSelection responsible for the most recent entity selection change.
bool m_didSetSelectedEntities = false;
//! Do the active manipulators need to recalculated after a modification (lock/visibility etc).
bool m_selectedEntityIdsAndManipulatorsDirty = false;
bool m_transformChangedInternally = false; //!< Was an OnTransformChanged event triggered internally or not.
ViewportUi::ClusterId m_transformModeClusterId; //!< Id of the Viewport UI cluster for changing transform mode.
ViewportUi::ButtonId m_translateButtonId; //!< Id of the Viewport UI button for translate mode.
@@ -363,15 +364,13 @@ namespace AzToolsFramework
//! Calculate the orientation for a group of entities based on the incoming reference frame.
template<typename EntityIdMap>
PivotOrientationResult CalculatePivotOrientationForEntityIds(
const EntityIdMap& entityIdMap, const ReferenceFrame referenceFrame);
PivotOrientationResult CalculatePivotOrientationForEntityIds(const EntityIdMap& entityIdMap, const ReferenceFrame referenceFrame);
//! Calculate the orientation for a group of entities based on the incoming
//! reference frame with possible pivot override.
template<typename EntityIdMap>
PivotOrientationResult CalculateSelectionPivotOrientation(
const EntityIdMap& entityIdMap, const OptionalFrame& pivotOverrideFrame,
const ReferenceFrame referenceFrame);
const EntityIdMap& entityIdMap, const OptionalFrame& pivotOverrideFrame, const ReferenceFrame referenceFrame);
void SetEntityWorldTranslation(AZ::EntityId entityId, const AZ::Vector3& worldTranslation, bool& internal);
void SetEntityLocalTranslation(AZ::EntityId entityId, const AZ::Vector3& localTranslation, bool& internal);
@@ -161,8 +161,8 @@ namespace AzToolsFramework
// request list of visible entities from authoritative system
EntityIdList nextVisibleEntityIds;
ViewportInteraction::MainEditorViewportInteractionRequestBus::Event(
viewportInfo.m_viewportId, &ViewportInteraction::MainEditorViewportInteractionRequestBus::Events::FindVisibleEntities,
ViewportInteraction::EditorEntityViewportInteractionRequestBus::Event(
viewportInfo.m_viewportId, &ViewportInteraction::EditorEntityViewportInteractionRequestBus::Events::FindVisibleEntities,
nextVisibleEntityIds);
// only bother resorting if we know the lists have changed
@@ -15,7 +15,7 @@ namespace AzToolsFramework
{
namespace Platform
{
static const char ErrorChannel[] = "ArchiveComponent_Linux";
[[maybe_unused]] static const char ErrorChannel[] = "ArchiveComponent_Linux";
static const char ZipExePath[] = R"(/usr/bin/zip)";
static const char UnzipExePath[] = R"(/usr/bin/unzip)";
@@ -6,8 +6,8 @@
*
*/
#include "ComponentModeTestDoubles.h"
#include "ComponentModeTestFixture.h"
#include "ComponentModeTestDoubles.h"
#include <AzCore/UserSettings/UserSettingsComponent.h>
@@ -15,17 +15,15 @@ namespace UnitTest
{
void ComponentModeTestFixture::SetUpEditorFixtureImpl()
{
using namespace AzToolsFramework;
using namespace AzToolsFramework::ComponentModeFramework;
namespace AztfCmf = AzToolsFramework::ComponentModeFramework;
auto* app = GetApplication();
ASSERT_TRUE(app);
app->RegisterComponentDescriptor(PlaceholderEditorComponent::CreateDescriptor());
app->RegisterComponentDescriptor(AnotherPlaceholderEditorComponent::CreateDescriptor());
app->RegisterComponentDescriptor(DependentPlaceholderEditorComponent::CreateDescriptor());
app->RegisterComponentDescriptor(AztfCmf::PlaceholderEditorComponent::CreateDescriptor());
app->RegisterComponentDescriptor(AztfCmf::AnotherPlaceholderEditorComponent::CreateDescriptor());
app->RegisterComponentDescriptor(AztfCmf::DependentPlaceholderEditorComponent::CreateDescriptor());
app->RegisterComponentDescriptor(
TestComponentModeComponent<OverrideMouseInteractionComponentMode>::CreateDescriptor());
app->RegisterComponentDescriptor(IncompatiblePlaceholderEditorComponent::CreateDescriptor());
AztfCmf::TestComponentModeComponent<AztfCmf::OverrideMouseInteractionComponentMode>::CreateDescriptor());
app->RegisterComponentDescriptor(AztfCmf::IncompatiblePlaceholderEditorComponent::CreateDescriptor());
}
} // namespace UnitTest
@@ -6,12 +6,14 @@
*
*/
#include <AzCore/Math/IntersectSegment.h>
#include <AzCore/Math/ToString.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <AzFramework/Components/TransformComponent.h>
#include <AzFramework/Entity/EntityContext.h>
#include <AzFramework/Viewport/ViewportScreen.h>
#include <AzFramework/Visibility/BoundsBus.h>
#include <AzManipulatorTestFramework/AzManipulatorTestFramework.h>
#include <AzManipulatorTestFramework/AzManipulatorTestFrameworkTestHelpers.h>
#include <AzManipulatorTestFramework/AzManipulatorTestFrameworkUtils.h>
@@ -20,10 +22,12 @@
#include <AzManipulatorTestFramework/ViewportInteraction.h>
#include <AzQtComponents/Components/GlobalEventFilter.h>
#include <AzTest/AzTest.h>
#include <AzToolsFramework/API/ComponentEntitySelectionBus.h>
#include <AzToolsFramework/Application/ToolsApplication.h>
#include <AzToolsFramework/Entity/EditorEntityActionComponent.h>
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
#include <AzToolsFramework/Entity/EditorEntityModel.h>
#include <AzToolsFramework/ToolsComponents/EditorComponentBase.h>
#include <AzToolsFramework/ToolsComponents/EditorLockComponent.h>
#include <AzToolsFramework/ToolsComponents/EditorVisibilityComponent.h>
#include <AzToolsFramework/ToolsComponents/TransformComponent.h>
@@ -32,6 +36,7 @@
#include <AzToolsFramework/ViewportSelection/EditorDefaultSelection.h>
#include <AzToolsFramework/ViewportSelection/EditorInteractionSystemViewportSelectionRequestBus.h>
#include <AzToolsFramework/ViewportSelection/EditorPickEntitySelection.h>
#include <AzToolsFramework/ViewportSelection/EditorSelectionUtil.h>
#include <AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.h>
#include <AzToolsFramework/ViewportSelection/EditorVisibleEntityDataCache.h>
#include <AzToolsFramework/ViewportUi/ViewportUiManager.h>
@@ -46,6 +51,14 @@ namespace AZ
namespace UnitTest
{
AzToolsFramework::EntityIdList SelectedEntities()
{
AzToolsFramework::EntityIdList selectedEntitiesBefore;
AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(
selectedEntitiesBefore, &AzToolsFramework::ToolsApplicationRequestBus::Events::GetSelectedEntities);
return selectedEntitiesBefore;
}
class EditorEntityVisibilityCacheFixture : public ToolsApplicationFixture
{
public:
@@ -110,6 +123,80 @@ namespace UnitTest
EXPECT_FALSE(m_cache.IsVisibleEntityVisible(m_cache.GetVisibleEntityIndexFromId(m_entityIds[2]).value()));
}
//! Basic component that implements BoundsRequestBus and EditorComponentSelectionRequestsBus to be compatible
//! with the Editor visibility system.
//! Note: Used for simulating selection (picking) in the viewport.
class BoundsTestComponent
: public AzToolsFramework::Components::EditorComponentBase
, public AzFramework::BoundsRequestBus::Handler
, public AzToolsFramework::EditorComponentSelectionRequestsBus::Handler
{
public:
AZ_EDITOR_COMPONENT(
BoundsTestComponent, "{E6312E9D-8489-4677-9980-C93C328BC92C}", AzToolsFramework::Components::EditorComponentBase);
static void Reflect(AZ::ReflectContext* context);
// AZ::Component overrides ...
void Activate() override;
void Deactivate() override;
// EditorComponentSelectionRequestsBus overrides ...
AZ::Aabb GetEditorSelectionBoundsViewport(const AzFramework::ViewportInfo& viewportInfo) override;
bool EditorSelectionIntersectRayViewport(
const AzFramework::ViewportInfo& viewportInfo, const AZ::Vector3& src, const AZ::Vector3& dir, float& distance) override;
bool SupportsEditorRayIntersect() override;
// BoundsRequestBus overrides ...
AZ::Aabb GetWorldBounds() override;
AZ::Aabb GetLocalBounds() override;
};
AZ::Aabb BoundsTestComponent::GetEditorSelectionBoundsViewport([[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo)
{
return GetWorldBounds();
}
bool BoundsTestComponent::EditorSelectionIntersectRayViewport(
[[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo, const AZ::Vector3& src, const AZ::Vector3& dir, float& distance)
{
return AzToolsFramework::AabbIntersectRay(src, dir, GetWorldBounds(), distance);
}
bool BoundsTestComponent::SupportsEditorRayIntersect()
{
return true;
}
void BoundsTestComponent::Reflect([[maybe_unused]] AZ::ReflectContext* context)
{
// noop
}
void BoundsTestComponent::Activate()
{
AzFramework::BoundsRequestBus::Handler::BusConnect(GetEntityId());
AzToolsFramework::EditorComponentSelectionRequestsBus::Handler::BusConnect(GetEntityId());
}
void BoundsTestComponent::Deactivate()
{
AzToolsFramework::EditorComponentSelectionRequestsBus::Handler::BusDisconnect();
AzFramework::BoundsRequestBus::Handler::BusDisconnect();
}
AZ::Aabb BoundsTestComponent::GetWorldBounds()
{
AZ::Transform worldFromLocal = AZ::Transform::CreateIdentity();
AZ::TransformBus::EventResult(worldFromLocal, GetEntityId(), &AZ::TransformBus::Events::GetWorldTM);
return GetLocalBounds().GetTransformedAabb(worldFromLocal);
}
AZ::Aabb BoundsTestComponent::GetLocalBounds()
{
return AZ::Aabb::CreateFromMinMax(AZ::Vector3(-0.5f), AZ::Vector3(0.5f));
}
// Fixture to support testing EditorTransformComponentSelection functionality on an Entity selection.
class EditorTransformComponentSelectionFixture : public ToolsApplicationFixture
{
@@ -120,27 +207,52 @@ namespace UnitTest
m_entityIds.push_back(m_entityId1);
}
void ArrangeIndividualRotatedEntitySelection(const AZ::Quaternion& orientation);
AZStd::optional<AZ::Transform> GetManipulatorTransform() const;
void RefreshManipulators(AzToolsFramework::EditorTransformComponentSelectionRequestBus::Events::RefreshType refreshType);
void SetTransformMode(AzToolsFramework::EditorTransformComponentSelectionRequestBus::Events::Mode transformMode);
void OverrideManipulatorOrientation(const AZ::Quaternion& orientation);
void OverrideManipulatorTranslation(const AZ::Vector3& translation);
public:
AZ::EntityId m_entityId1;
AzToolsFramework::EntityIdList m_entityIds;
};
void EditorTransformComponentSelectionFixture::ArrangeIndividualRotatedEntitySelection(const AZ::Quaternion& orientation)
class EditorTransformComponentSelectionViewportPickingFixture : public ToolsApplicationFixture
{
for (auto entityId : m_entityIds)
public:
void SetUpEditorFixtureImpl() override
{
auto* app = GetApplication();
// register a simple component implementing BoundsRequestBus and EditorComponentSelectionRequestsBus
app->RegisterComponentDescriptor(BoundsTestComponent::CreateDescriptor());
auto createEntityWithBoundsFn = [](const char* entityName)
{
AZ::Entity* entity = nullptr;
AZ::EntityId entityId = CreateDefaultEditorEntity(entityName, &entity);
entity->Deactivate();
entity->CreateComponent<BoundsTestComponent>();
entity->Activate();
return entityId;
};
m_entityId1 = createEntityWithBoundsFn("Entity1");
m_entityId2 = createEntityWithBoundsFn("Entity2");
m_entityId3 = createEntityWithBoundsFn("Entity3");
}
public:
AZ::EntityId m_entityId1;
AZ::EntityId m_entityId2;
AZ::EntityId m_entityId3;
};
void ArrangeIndividualRotatedEntitySelection(const AzToolsFramework::EntityIdList& entityIds, const AZ::Quaternion& orientation)
{
for (auto entityId : entityIds)
{
AZ::TransformBus::Event(entityId, &AZ::TransformBus::Events::SetLocalRotationQuaternion, orientation);
}
}
AZStd::optional<AZ::Transform> EditorTransformComponentSelectionFixture::GetManipulatorTransform() const
AZStd::optional<AZ::Transform> GetManipulatorTransform()
{
using AzToolsFramework::EditorTransformComponentSelectionRequestBus;
@@ -151,8 +263,7 @@ namespace UnitTest
return manipulatorTransform;
}
void EditorTransformComponentSelectionFixture::RefreshManipulators(
AzToolsFramework::EditorTransformComponentSelectionRequestBus::Events::RefreshType refreshType)
void RefreshManipulators(const AzToolsFramework::EditorTransformComponentSelectionRequestBus::Events::RefreshType refreshType)
{
using AzToolsFramework::EditorTransformComponentSelectionRequestBus;
@@ -160,8 +271,7 @@ namespace UnitTest
AzToolsFramework::GetEntityContextId(), &EditorTransformComponentSelectionRequestBus::Events::RefreshManipulators, refreshType);
}
void EditorTransformComponentSelectionFixture::SetTransformMode(
AzToolsFramework::EditorTransformComponentSelectionRequestBus::Events::Mode transformMode)
void SetTransformMode(const AzToolsFramework::EditorTransformComponentSelectionRequestBus::Events::Mode transformMode)
{
using AzToolsFramework::EditorTransformComponentSelectionRequestBus;
@@ -169,7 +279,7 @@ namespace UnitTest
AzToolsFramework::GetEntityContextId(), &EditorTransformComponentSelectionRequestBus::Events::SetTransformMode, transformMode);
}
void EditorTransformComponentSelectionFixture::OverrideManipulatorOrientation(const AZ::Quaternion& orientation)
void OverrideManipulatorOrientation(const AZ::Quaternion& orientation)
{
using AzToolsFramework::EditorTransformComponentSelectionRequestBus;
@@ -178,7 +288,7 @@ namespace UnitTest
orientation);
}
void EditorTransformComponentSelectionFixture::OverrideManipulatorTranslation(const AZ::Vector3& translation)
void OverrideManipulatorTranslation(const AZ::Vector3& translation)
{
using AzToolsFramework::EditorTransformComponentSelectionRequestBus;
@@ -190,7 +300,7 @@ namespace UnitTest
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// EditorTransformComponentSelection Tests
TEST_F(EditorTransformComponentSelectionFixture, Focus_is_not_changed_while_switching_viewport_interaction_request_instance)
TEST_F(EditorTransformComponentSelectionFixture, FocusIsNotChangedWhileSwitchingViewportInteractionRequestInstance)
{
// setup a dummy widget and make it the active window to ensure focus in/out events are fired
auto dummyWidget = AZStd::make_unique<QWidget>();
@@ -239,7 +349,7 @@ namespace UnitTest
// Given
AzToolsFramework::SelectEntity(m_entityId1);
ArrangeIndividualRotatedEntitySelection(AZ::Quaternion::CreateRotationX(AZ::DegToRad(90.0f)));
ArrangeIndividualRotatedEntitySelection(m_entityIds, AZ::Quaternion::CreateRotationX(AZ::DegToRad(90.0f)));
RefreshManipulators(EditorTransformComponentSelectionRequestBus::Events::RefreshType::All);
SetTransformMode(EditorTransformComponentSelectionRequestBus::Events::Mode::Rotation);
@@ -286,7 +396,7 @@ namespace UnitTest
AzToolsFramework::SelectEntity(m_entityId1);
const AZ::Quaternion initialEntityOrientation = AZ::Quaternion::CreateRotationX(AZ::DegToRad(90.0f));
ArrangeIndividualRotatedEntitySelection(initialEntityOrientation);
ArrangeIndividualRotatedEntitySelection(m_entityIds, initialEntityOrientation);
// assign new orientation to manipulator which does not match entity orientation
OverrideManipulatorOrientation(AZ::Quaternion::CreateRotationZ(AZ::DegToRad(90.0f)));
@@ -478,6 +588,227 @@ namespace UnitTest
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
}
// fixture for use with the indirect manipulator test framework
using EditorTransformComponentSelectionViewportPickingManipulatorTestFixture =
IndirectCallManipulatorViewportInteractionFixtureMixin<EditorTransformComponentSelectionViewportPickingFixture>;
TEST_F(EditorTransformComponentSelectionViewportPickingManipulatorTestFixture, SingleClickWithNoSelectionWillSelectEntity)
{
AzToolsFramework::ed_viewportStickySelect = true;
// the initial starting position of the entity
const auto initialTransformWorld = AZ::Transform::CreateTranslation(AZ::Vector3(5.0f, 15.0f, 10.0f));
AZ::TransformBus::Event(m_entityId1, &AZ::TransformBus::Events::SetWorldTM, initialTransformWorld);
// initial camera position (looking down the negative x-axis)
AzFramework::SetCameraTransform(
m_cameraState,
AZ::Transform::CreateFromQuaternionAndTranslation(
AZ::Quaternion::CreateFromEulerAnglesDegrees(AZ::Vector3(0.0f, 0.0f, 90.0f)), AZ::Vector3(10.0f, 15.0f, 10.0f)));
using ::testing::Eq;
auto selectedEntitiesBefore = SelectedEntities();
EXPECT_TRUE(selectedEntitiesBefore.empty());
// calculate the position in screen space of the initial entity position
const auto initialPositionScreen = AzFramework::WorldToScreen(initialTransformWorld.GetTranslation(), m_cameraState);
// click the entity in the viewport
m_actionDispatcher->CameraState(m_cameraState)->MousePosition(initialPositionScreen)->MouseLButtonDown()->MouseLButtonUp();
// entity is selected
auto selectedEntitiesAfter = SelectedEntities();
EXPECT_THAT(selectedEntitiesAfter.size(), Eq(1));
EXPECT_THAT(selectedEntitiesAfter.front(), Eq(m_entityId1));
}
TEST_F(EditorTransformComponentSelectionViewportPickingManipulatorTestFixture, SingleClickOffEntityWithSelectionWillNotDeselectEntity)
{
AzToolsFramework::ed_viewportStickySelect = true;
// the initial starting position of the entity
AZ::TransformBus::Event(
m_entityId1, &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3(5.0f, 15.0f, 10.0f)));
// position in space above the entity
const auto clickOffPositionWorld = AZ::Vector3(5.0f, 15.0f, 12.0f);
// initial camera position (looking down the negative x-axis)
AzFramework::SetCameraTransform(
m_cameraState,
AZ::Transform::CreateFromQuaternionAndTranslation(
AZ::Quaternion::CreateFromEulerAnglesDegrees(AZ::Vector3(0.0f, 0.0f, 90.0f)), AZ::Vector3(10.0f, 15.0f, 10.0f)));
AzToolsFramework::SelectEntity(m_entityId1);
// calculate the position in screen space of the initial position of the entity
const auto clickOffPositionScreen = AzFramework::WorldToScreen(clickOffPositionWorld, m_cameraState);
// click the empty space in the viewport
m_actionDispatcher->CameraState(m_cameraState)->MousePosition(clickOffPositionScreen)->MouseLButtonDown()->MouseLButtonUp();
// entity was not deselected
using ::testing::Eq;
auto selectedEntitiesAfter = SelectedEntities();
EXPECT_THAT(selectedEntitiesAfter.size(), Eq(1));
EXPECT_THAT(selectedEntitiesAfter.front(), Eq(m_entityId1));
}
TEST_F(
EditorTransformComponentSelectionViewportPickingManipulatorTestFixture,
SingleClickOnNewEntityWithSelectionWillNotChangeSelectedEntity)
{
AzToolsFramework::ed_viewportStickySelect = true;
// the initial starting position of the entity
AZ::TransformBus::Event(
m_entityId1, &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3(5.0f, 15.0f, 10.0f)));
const auto initialTransformWorldSecondEntity = AZ::Transform::CreateTranslation(AZ::Vector3(5.0f, 10.0f, 10.0f));
AZ::TransformBus::Event(m_entityId2, &AZ::TransformBus::Events::SetWorldTM, initialTransformWorldSecondEntity);
// initial camera position (looking down the negative x-axis)
AzFramework::SetCameraTransform(
m_cameraState,
AZ::Transform::CreateFromQuaternionAndTranslation(
AZ::Quaternion::CreateFromEulerAnglesDegrees(AZ::Vector3(0.0f, 0.0f, 90.0f)), AZ::Vector3(10.0f, 15.0f, 10.0f)));
AzToolsFramework::SelectEntity(m_entityId1);
// calculate the position in screen space of the second entity
const auto initialPositionScreenSecondEntity =
AzFramework::WorldToScreen(initialTransformWorldSecondEntity.GetTranslation(), m_cameraState);
// click the entity in the viewport
m_actionDispatcher->CameraState(m_cameraState)
->MousePosition(initialPositionScreenSecondEntity)
->MouseLButtonDown()
->MouseLButtonUp();
// entity selection was not changed
using ::testing::Eq;
auto selectedEntitiesAfter = SelectedEntities();
EXPECT_THAT(selectedEntitiesAfter.size(), Eq(1));
EXPECT_THAT(selectedEntitiesAfter.front(), Eq(m_entityId1));
}
TEST_F(
EditorTransformComponentSelectionViewportPickingManipulatorTestFixture,
CtrlSingleClickOnNewEntityWithSelectionWillAppendSelectedEntityToSelection)
{
AzToolsFramework::ed_viewportStickySelect = true;
// the initial starting position of the entity
AZ::TransformBus::Event(
m_entityId1, &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3(5.0f, 15.0f, 10.0f)));
const auto initialTransformWorldSecondEntity = AZ::Transform::CreateTranslation(AZ::Vector3(5.0f, 10.0f, 10.0f));
AZ::TransformBus::Event(m_entityId2, &AZ::TransformBus::Events::SetWorldTM, initialTransformWorldSecondEntity);
// initial camera position (looking down the negative x-axis)
AzFramework::SetCameraTransform(
m_cameraState,
AZ::Transform::CreateFromQuaternionAndTranslation(
AZ::Quaternion::CreateFromEulerAnglesDegrees(AZ::Vector3(0.0f, 0.0f, 90.0f)), AZ::Vector3(10.0f, 15.0f, 10.0f)));
AzToolsFramework::SelectEntity(m_entityId1);
// calculate the position in screen space of the second entity
const auto initialPositionScreenSecondEntity =
AzFramework::WorldToScreen(initialTransformWorldSecondEntity.GetTranslation(), m_cameraState);
// click the entity in the viewport
m_actionDispatcher->CameraState(m_cameraState)
->MousePosition(initialPositionScreenSecondEntity)
->KeyboardModifierDown(AzToolsFramework::ViewportInteraction::KeyboardModifier::Control)
->MouseLButtonDown()
->MouseLButtonUp();
// entity selection was changed (one entity selected to two)
using ::testing::UnorderedElementsAre;
auto selectedEntitiesAfter = SelectedEntities();
EXPECT_THAT(selectedEntitiesAfter, UnorderedElementsAre(m_entityId1, m_entityId2));
}
TEST_F(
EditorTransformComponentSelectionViewportPickingManipulatorTestFixture,
CtrlSingleClickOnEntityInSelectionWillRemoveEntityFromSelection)
{
AzToolsFramework::ed_viewportStickySelect = true;
// the initial starting position of the entity
AZ::TransformBus::Event(
m_entityId1, &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3(5.0f, 15.0f, 10.0f)));
const auto initialTransformWorldSecondEntity = AZ::Transform::CreateTranslation(AZ::Vector3(5.0f, 10.0f, 10.0f));
AZ::TransformBus::Event(m_entityId2, &AZ::TransformBus::Events::SetWorldTM, initialTransformWorldSecondEntity);
// initial camera position (looking down the negative x-axis)
AzFramework::SetCameraTransform(
m_cameraState,
AZ::Transform::CreateFromQuaternionAndTranslation(
AZ::Quaternion::CreateFromEulerAnglesDegrees(AZ::Vector3(0.0f, 0.0f, 90.0f)), AZ::Vector3(10.0f, 15.0f, 10.0f)));
AzToolsFramework::SelectEntities({ m_entityId1, m_entityId2 });
// calculate the position in screen space of the second entity
const auto initialPositionScreenSecondEntity =
AzFramework::WorldToScreen(initialTransformWorldSecondEntity.GetTranslation(), m_cameraState);
// click the entity in the viewport
m_actionDispatcher->CameraState(m_cameraState)
->MousePosition(initialPositionScreenSecondEntity)
->KeyboardModifierDown(AzToolsFramework::ViewportInteraction::KeyboardModifier::Control)
->MouseLButtonDown()
->MouseLButtonUp();
// entity selection was changed (entity2 was deselected)
using ::testing::UnorderedElementsAre;
auto selectedEntitiesAfter = SelectedEntities();
EXPECT_THAT(selectedEntitiesAfter, UnorderedElementsAre(m_entityId1));
}
TEST_F(EditorTransformComponentSelectionViewportPickingManipulatorTestFixture, DISABLED_BoxSelectWithNoInitialSelectionAddsEntitiesToSelection)
{
AzToolsFramework::ed_viewportStickySelect = true;
// the initial starting position of the entities
AZ::TransformBus::Event(
m_entityId1, &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3(5.0f, 15.0f, 10.0f)));
AZ::TransformBus::Event(
m_entityId2, &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3(5.0f, 14.0f, 10.0f)));
AZ::TransformBus::Event(
m_entityId3, &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3(5.0f, 16.0f, 10.0f)));
// initial camera position (looking down the negative x-axis)
AzFramework::SetCameraTransform(
m_cameraState,
AZ::Transform::CreateFromQuaternionAndTranslation(
AZ::Quaternion::CreateFromEulerAnglesDegrees(AZ::Vector3(0.0f, 0.0f, 90.0f)), AZ::Vector3(10.0f, 15.0f, 10.0f)));
using ::testing::Eq;
auto selectedEntitiesBefore = SelectedEntities();
EXPECT_THAT(selectedEntitiesBefore.size(), Eq(0));
// calculate the position in screen space of where to begin and end the box select action
const auto beginningPositionWorldBoxSelectStart = AzFramework::WorldToScreen(AZ::Vector3(5.0f, 13.5f, 10.5f), m_cameraState);
const auto middlePositionWorldBoxSelectStart = AzFramework::WorldToScreen(AZ::Vector3(5.0f, 15.0f, 10.0f), m_cameraState);
const auto endingPositionWorldBoxSelectStart = AzFramework::WorldToScreen(AZ::Vector3(5.0f, 16.5f, 9.5f), m_cameraState);
// perform a box select in the viewport
m_actionDispatcher->CameraState(m_cameraState)
->MousePosition(beginningPositionWorldBoxSelectStart)
->MouseLButtonDown()
->MousePosition(middlePositionWorldBoxSelectStart)
->MousePosition(endingPositionWorldBoxSelectStart)
->MouseLButtonUp();
// entities are selected
using ::testing::UnorderedElementsAre;
auto selectedEntitiesAfter = SelectedEntities();
EXPECT_THAT(selectedEntitiesAfter, UnorderedElementsAre(m_entityId1, m_entityId2, m_entityId3));
}
using EditorTransformComponentSelectionManipulatorTestFixture =
IndirectCallManipulatorViewportInteractionFixtureMixin<EditorTransformComponentSelectionFixture>;
@@ -236,7 +236,7 @@ namespace AZ
case TextureMapType::Bump:
return m_normalMap;
default:
AZ_Assert(false, "Invalid Texture map requested.")
AZ_Assert(false, "Invalid Texture map requested.");
return m_empty;
}
}
@@ -255,7 +255,7 @@ namespace AZ
m_normalMap = texture;
break;
default:
AZ_Assert(false, "Invalid Texture map requested.")
AZ_Assert(false, "Invalid Texture map requested.");
break;
}
}
@@ -599,7 +599,7 @@ namespace AZ
if (!materialNode)
{
AZ_Assert(false, "Attempted to add material to invalid xml document.")
AZ_Assert(false, "Attempted to add material to invalid xml document.");
return false;
}
@@ -351,7 +351,7 @@ namespace GridMate
DataSetBase* dataset = descriptor->GetDataSet(this, i);
if (!dataset)
{
AZ_Assert(false, "How can we have a dirty dataset that doesn't exist?")
AZ_Assert(false, "How can we have a dirty dataset that doesn't exist?");
continue;
}
@@ -64,7 +64,7 @@ namespace GridMate
// Create Callback
AZStd::weak_ptr<TargetCallbackBase> CreateCallback(AZ::u64 revision)
{
AZ_Assert(IsAckEnabled(), "ACK disabled.") //Shouldn't happen
AZ_Assert(IsAckEnabled(), "ACK disabled."); //Shouldn't happen
AZ_Assert(m_replicaRevision <= revision, "Cannot decrease replica revision");
if(!m_callback || m_callback->m_revision != revision)
@@ -1583,7 +1583,7 @@ GridSession::OnStateCreate(HSM& sm, const HSM::Event& e)
// Bind member replica
bool isAdded = AddMember(m_myMember);
AZ_Error("GridMate", isAdded, "Failed to add my replica, check the number of open slots!")
AZ_Error("GridMate", isAdded, "Failed to add my replica, check the number of open slots!");
if (!isAdded)
{
sm.Transition(SS_DELETE);
@@ -698,7 +698,7 @@ namespace GridMate
static void* UserDataCopier(const void* sourceData, unsigned int sourceDataSize)
{
(void)sourceDataSize;
AZ_Assert(sizeof(T) == sourceDataSize, "Data size %d doesn't match the type size %d", sourceDataSize, sizeof(T))
AZ_Assert(sizeof(T) == sourceDataSize, "Data size %d doesn't match the type size %d", sourceDataSize, sizeof(T));
return azcreate(T, (*static_cast<const T*>(sourceData)), GridMateAllocatorMP, "UserDataCopier");
}
template<class T>