@@ -225,8 +225,16 @@ namespace AZ
|
||||
|
||||
ConsoleCommandContainer commandSubset;
|
||||
|
||||
for (ConsoleFunctorBase* curr = m_head; curr != nullptr; curr = curr->m_next)
|
||||
for (const auto& functor : m_commands)
|
||||
{
|
||||
if (functor.second.empty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Filter functors registered with the same name
|
||||
const ConsoleFunctorBase* curr = functor.second.front();
|
||||
|
||||
if ((curr->GetFlags() & ConsoleFunctorFlags::IsInvisible) == ConsoleFunctorFlags::IsInvisible)
|
||||
{
|
||||
// Filter functors marked as invisible
|
||||
@@ -236,7 +244,12 @@ namespace AZ
|
||||
if (StringFunc::StartsWith(curr->m_name, command, false))
|
||||
{
|
||||
AZLOG_INFO("- %s : %s\n", curr->m_name, curr->m_desc);
|
||||
commandSubset.push_back(curr->m_name);
|
||||
|
||||
if (commandSubset.size() < MaxConsoleCommandPlusArgsLength)
|
||||
{
|
||||
commandSubset.push_back(curr->m_name);
|
||||
}
|
||||
|
||||
if (matches)
|
||||
{
|
||||
matches->push_back(curr->m_name);
|
||||
@@ -271,7 +284,10 @@ namespace AZ
|
||||
{
|
||||
for (auto& curr : m_commands)
|
||||
{
|
||||
visitor(curr.second.front());
|
||||
if (!curr.second.empty())
|
||||
{
|
||||
visitor(curr.second.front());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -336,6 +352,11 @@ namespace AZ
|
||||
{
|
||||
iter->second.erase(iter2);
|
||||
}
|
||||
|
||||
if (iter->second.empty())
|
||||
{
|
||||
m_commands.erase(iter);
|
||||
}
|
||||
}
|
||||
functor->Unlink(m_head);
|
||||
functor->m_console = nullptr;
|
||||
@@ -476,15 +497,16 @@ namespace AZ
|
||||
|
||||
// Responsible for using the Json Serialization Issue Callback system
|
||||
// to determine when a JSON Patch or JSON Merge Patch modifies a value
|
||||
// at a path underneath the IConsole::ConsoleRootCommandKey JSON pointer
|
||||
// at a path underneath the IConsole::ConsoleRuntimeCommandKey JSON pointer
|
||||
JsonSerializationResult::ResultCode operator()(AZStd::string_view message,
|
||||
JsonSerializationResult::ResultCode result, AZStd::string_view path)
|
||||
{
|
||||
AZ::IO::PathView consoleRootCommandKey{ IConsole::ConsoleRootCommandKey, AZ::IO::PosixPathSeparator };
|
||||
constexpr AZ::IO::PathView consoleRootCommandKey{ IConsole::ConsoleRuntimeCommandKey, AZ::IO::PosixPathSeparator };
|
||||
constexpr AZ::IO::PathView consoleAutoexecCommandKey{ IConsole::ConsoleAutoexecCommandKey, AZ::IO::PosixPathSeparator };
|
||||
AZ::IO::PathView inputKey{ path, AZ::IO::PosixPathSeparator };
|
||||
if (result.GetTask() == JsonSerializationResult::Tasks::Merge
|
||||
&& result.GetProcessing() == JsonSerializationResult::Processing::Completed
|
||||
&& inputKey.IsRelativeTo(consoleRootCommandKey))
|
||||
&& (inputKey.IsRelativeTo(consoleRootCommandKey) || inputKey.IsRelativeTo(consoleAutoexecCommandKey)))
|
||||
{
|
||||
if (auto type = m_settingsRegistry.GetType(path); type != SettingsRegistryInterface::Type::NoType)
|
||||
{
|
||||
@@ -510,12 +532,24 @@ namespace AZ
|
||||
{
|
||||
using FixedValueString = AZ::SettingsRegistryInterface::FixedValueString;
|
||||
|
||||
AZ::IO::PathView consoleRootCommandKey{ IConsole::ConsoleRootCommandKey, AZ::IO::PosixPathSeparator };
|
||||
constexpr AZ::IO::PathView consoleRuntimeCommandKey{ IConsole::ConsoleRuntimeCommandKey, AZ::IO::PosixPathSeparator };
|
||||
constexpr AZ::IO::PathView consoleAutoexecCommandKey{ IConsole::ConsoleAutoexecCommandKey, AZ::IO::PosixPathSeparator };
|
||||
AZ::IO::PathView inputKey{ path, AZ::IO::PosixPathSeparator };
|
||||
// The ConsoleRootComamndKey is not a command itself so strictly children keys are being examined
|
||||
if (inputKey.IsRelativeTo(consoleRootCommandKey) && inputKey != consoleRootCommandKey)
|
||||
|
||||
// Abuses the IsRelativeToFuncton function of the path class to extract the console
|
||||
// command from the settings registry objects
|
||||
FixedValueString command;
|
||||
if (inputKey != consoleRuntimeCommandKey && inputKey.IsRelativeTo(consoleRuntimeCommandKey))
|
||||
{
|
||||
command = inputKey.LexicallyRelative(consoleRuntimeCommandKey).Native();
|
||||
}
|
||||
else if (inputKey != consoleAutoexecCommandKey && inputKey.IsRelativeTo(consoleAutoexecCommandKey))
|
||||
{
|
||||
command = inputKey.LexicallyRelative(consoleAutoexecCommandKey).Native();
|
||||
}
|
||||
|
||||
if (!command.empty())
|
||||
{
|
||||
FixedValueString command = inputKey.LexicallyRelative(consoleRootCommandKey).Native();
|
||||
ConsoleCommandContainer commandArgs;
|
||||
// Argument string which stores the value from the Settings Registry long enough
|
||||
// to pass into the PerformCommand. The ConsoleCommandContainer stores string_views
|
||||
@@ -603,9 +637,10 @@ namespace AZ
|
||||
|
||||
void Console::RegisterCommandInvokerWithSettingsRegistry(AZ::SettingsRegistryInterface& settingsRegistry)
|
||||
{
|
||||
// Make sure the there is a JSON object at the path of AZ::IConsole::ConsoleRootCommandKey
|
||||
// Make sure the there is a JSON object at the ConsoleRuntimeCommandKey or ConsoleAutoexecKey
|
||||
// So that JSON Patch is able to add values underneath that object (JSON Patch doesn't create intermediate objects)
|
||||
settingsRegistry.MergeSettings(R"({ "Amazon": { "AzCore": { "Runtime": { "ConsoleCommands": {} } }}})",
|
||||
settingsRegistry.MergeSettings(R"({ "Amazon": { "AzCore": { "Runtime": { "ConsoleCommands": {} } } })"
|
||||
R"(,"O3DE": { "Autoexec": { "ConsoleCommands": {} } } })",
|
||||
SettingsRegistryInterface::Format::JsonMergePatch);
|
||||
m_consoleCommandKeyHandler = settingsRegistry.RegisterNotifier(ConsoleCommandKeyNotificationHandler{ settingsRegistry, *this });
|
||||
|
||||
|
||||
@@ -31,7 +31,8 @@ namespace AZ
|
||||
|
||||
using FunctorVisitor = AZStd::function<void(ConsoleFunctorBase*)>;
|
||||
|
||||
inline static constexpr AZStd::string_view ConsoleRootCommandKey = "/Amazon/AzCore/Runtime/ConsoleCommands";
|
||||
inline static constexpr AZStd::string_view ConsoleRuntimeCommandKey = "/Amazon/AzCore/Runtime/ConsoleCommands";
|
||||
inline static constexpr AZStd::string_view ConsoleAutoexecCommandKey = "/O3DE/Autoexec/ConsoleCommands";
|
||||
|
||||
IConsole() = default;
|
||||
virtual ~IConsole() = default;
|
||||
|
||||
@@ -40,6 +40,12 @@ namespace AZ
|
||||
static unsigned int Record(StackFrame* frames, unsigned int maxNumOfFrames, unsigned int suppressCount = 0, void* nativeThread = 0);
|
||||
};
|
||||
|
||||
class StackConverter
|
||||
{
|
||||
public:
|
||||
static unsigned int FromNative(StackFrame* frames, unsigned int maxNumOfFrames, void* nativeContext);
|
||||
};
|
||||
|
||||
class SymbolStorage
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -46,5 +46,23 @@ namespace AZ
|
||||
private:
|
||||
AZStd::sys_time_t m_timeStamp;
|
||||
};
|
||||
|
||||
//! Utility type that updates the given variable with the lifetime of the object in cycles.
|
||||
//! Useful for quick scope based timing.
|
||||
struct ScopedTimer
|
||||
{
|
||||
explicit ScopedTimer(AZStd::sys_time_t& variable)
|
||||
: m_variable(variable)
|
||||
{
|
||||
m_timer.Stamp();
|
||||
}
|
||||
~ScopedTimer()
|
||||
{
|
||||
m_variable = m_timer.GetDeltaTimeInTicks();
|
||||
}
|
||||
|
||||
AZStd::sys_time_t& m_variable;
|
||||
Timer m_timer;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,8 @@ namespace AZ
|
||||
{
|
||||
namespace Debug
|
||||
{
|
||||
struct StackFrame;
|
||||
|
||||
namespace Platform
|
||||
{
|
||||
#if defined(AZ_ENABLE_DEBUG_TOOLS)
|
||||
@@ -551,17 +553,19 @@ namespace AZ
|
||||
{
|
||||
StackFrame frames[25];
|
||||
|
||||
// Without StackFrame explicit alignment frames array is aligned to 4 bytes
|
||||
// which causes the stack tracing to fail.
|
||||
//size_t bla = AZStd::alignment_of<StackFrame>::value;
|
||||
//printf("Alignment value %d address 0x%08x : 0x%08x\n",bla,frames);
|
||||
SymbolStorage::StackLine lines[AZ_ARRAY_SIZE(frames)];
|
||||
unsigned int numFrames = 0;
|
||||
|
||||
if (!nativeContext)
|
||||
{
|
||||
suppressCount += 1; /// If we don't provide a context we will capture in the RecordFunction, so skip us (Trace::PrinCallstack).
|
||||
suppressCount += 1; /// If we don't provide a context we will capture in the RecordFunction, so skip us (Trace::PrintCallstack).
|
||||
numFrames = StackRecorder::Record(frames, AZ_ARRAY_SIZE(frames), suppressCount);
|
||||
}
|
||||
unsigned int numFrames = StackRecorder::Record(frames, AZ_ARRAY_SIZE(frames), suppressCount, nativeContext);
|
||||
else
|
||||
{
|
||||
numFrames = StackConverter::FromNative(frames, AZ_ARRAY_SIZE(frames), nativeContext);
|
||||
}
|
||||
|
||||
if (numFrames)
|
||||
{
|
||||
SymbolStorage::DecodeFrames(frames, numFrames, lines);
|
||||
|
||||
@@ -160,8 +160,8 @@ namespace AZ
|
||||
/**
|
||||
* Locking primitive that is used when executing events in the event queue.
|
||||
*/
|
||||
using EventQueueMutexType = typename AZStd::Utils::if_c<AZStd::is_same<typename Traits::EventQueueMutexType, NullMutex>::value, // if EventQueueMutexType==NullMutex use MutexType otherwise EventQueueMutexType
|
||||
MutexType, typename Traits::EventQueueMutexType>::type;
|
||||
using EventQueueMutexType = AZStd::conditional_t<AZStd::is_same<typename Traits::EventQueueMutexType, NullMutex>::value, // if EventQueueMutexType==NullMutex use MutexType otherwise EventQueueMutexType
|
||||
MutexType, typename Traits::EventQueueMutexType>;
|
||||
|
||||
/**
|
||||
* Pointer to an address on the bus.
|
||||
@@ -180,14 +180,22 @@ namespace AZ
|
||||
* `<BusName>::ExecuteQueuedEvents()`.
|
||||
* By default, the event queue is disabled.
|
||||
*/
|
||||
static const bool EnableEventQueue = Traits::EnableEventQueue;
|
||||
static const bool EventQueueingActiveByDefault = Traits::EventQueueingActiveByDefault;
|
||||
static const bool EnableQueuedReferences = Traits::EnableQueuedReferences;
|
||||
static constexpr bool EnableEventQueue = Traits::EnableEventQueue;
|
||||
static constexpr bool EventQueueingActiveByDefault = Traits::EventQueueingActiveByDefault;
|
||||
static constexpr bool EnableQueuedReferences = Traits::EnableQueuedReferences;
|
||||
|
||||
/**
|
||||
* True if the EBus supports more than one address. Otherwise, false.
|
||||
*/
|
||||
static const bool HasId = Traits::AddressPolicy != EBusAddressPolicy::Single;
|
||||
static constexpr bool HasId = Traits::AddressPolicy != EBusAddressPolicy::Single;
|
||||
|
||||
/**
|
||||
* Template Lock Guard class that wraps around the Mutex
|
||||
* The EBus uses for Dispatching Events.
|
||||
* This is not the EBus Context Mutex if LocklessDispatch is true
|
||||
*/
|
||||
template <typename DispatchMutex>
|
||||
using DispatchLockGuard = typename Traits::template DispatchLockGuard<DispatchMutex, Traits::LocklessDispatch>;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -460,7 +468,7 @@ namespace AZ
|
||||
using BusPtr = typename Traits::BusPtr;
|
||||
|
||||
/**
|
||||
* Helper to queue an event by BusIdType only when function queueing is enabled
|
||||
* Helper to queue an event by BusIdType only when function queueing is enabled
|
||||
* @param id Address ID. Handlers that are connected to this ID will receive the event.
|
||||
* @param func Function pointer of the event to dispatch.
|
||||
* @param args Function arguments that are passed to each handler.
|
||||
@@ -581,7 +589,7 @@ namespace AZ
|
||||
, public EBusBroadcaster<Bus, Traits>
|
||||
, public EBusEventer<Bus, Traits>
|
||||
, public EBusEventEnumerator<Bus, Traits>
|
||||
, public AZStd::Utils::if_c<Traits::EnableEventQueue, EBusEventQueue<Bus, Traits>, EBusNullQueue>::type
|
||||
, public AZStd::conditional_t<Traits::EnableEventQueue, EBusEventQueue<Bus, Traits>, EBusNullQueue>
|
||||
{
|
||||
};
|
||||
|
||||
@@ -599,7 +607,7 @@ namespace AZ
|
||||
: public EventDispatcher<Bus, Traits>
|
||||
, public EBusBroadcaster<Bus, Traits>
|
||||
, public EBusBroadcastEnumerator<Bus, Traits>
|
||||
, public AZStd::Utils::if_c<Traits::EnableEventQueue, EBusBroadcastQueue<Bus, Traits>, EBusNullQueue>::type
|
||||
, public AZStd::conditional_t<Traits::EnableEventQueue, EBusBroadcastQueue<Bus, Traits>, EBusNullQueue>
|
||||
{
|
||||
};
|
||||
|
||||
|
||||
@@ -77,9 +77,11 @@ namespace AZ
|
||||
public:
|
||||
/**
|
||||
* Allocator used by the EBus.
|
||||
* The default setting is AZStd::allocator, which uses AZ::SystemAllocator.
|
||||
* The default setting is Internal EBusEnvironmentAllocator
|
||||
* EBus code stores their Context instances in static memory
|
||||
* Therfore the configured allocator must last as long as the EBus in a module
|
||||
*/
|
||||
using AllocatorType = AZStd::allocator;
|
||||
using AllocatorType = AZ::Internal::EBusEnvironmentAllocator;
|
||||
|
||||
/**
|
||||
* Defines how many handlers can connect to an address on the EBus
|
||||
@@ -236,6 +238,17 @@ namespace AZ
|
||||
* code before or after an event.
|
||||
*/
|
||||
using EventProcessingPolicy = EBusEventProcessingPolicy;
|
||||
|
||||
/**
|
||||
* Template Lock Guard class that wraps around the Mutex
|
||||
* The EBus Context uses the LockGuard when dispatching
|
||||
* (either AZStd::scoped_lock<MutexType> or NullLockGuard<MutexType>)
|
||||
* The IsLocklessDispatch bool is there to defer evaluation of the LocklessDispatch constant
|
||||
* Otherwise the value above in EBusTraits.h is always used and not the value
|
||||
* that the derived trait class sets.
|
||||
*/
|
||||
template <typename DispatchMutex, bool IsLocklessDispatch>
|
||||
using DispatchLockGuard = AZStd::conditional_t<IsLocklessDispatch, AZ::Internal::NullLockGuard<DispatchMutex>, AZStd::scoped_lock<DispatchMutex>>;
|
||||
};
|
||||
|
||||
namespace Internal
|
||||
@@ -496,6 +509,14 @@ namespace AZ
|
||||
*/
|
||||
static const bool HasId = Traits::AddressPolicy != EBusAddressPolicy::Single;
|
||||
|
||||
/**
|
||||
* Template Lock Guard class that wraps around the Mutex
|
||||
* The EBus uses for Dispatching Events.
|
||||
* This is not EBus Context Mutex when LocklessDispatch is set
|
||||
*/
|
||||
template <typename DispatchMutex>
|
||||
using DispatchLockGuard = typename ImplTraits::template DispatchLockGuard<DispatchMutex>;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Check to help identify common mistakes
|
||||
/// @cond EXCLUDE_DOCS
|
||||
@@ -620,11 +641,11 @@ namespace AZ
|
||||
using ContextMutexType = AZStd::conditional_t<BusTraits::LocklessDispatch && AZStd::is_same_v<MutexType, AZ::NullMutex>, AZStd::shared_mutex, MutexType>;
|
||||
|
||||
/**
|
||||
* The scoped lock guard to use (either AZStd::scoped_lock<MutexType> or NullLockGuard<MutexType>
|
||||
* The scoped lock guard to use
|
||||
* during broadcast/event dispatch.
|
||||
* @see EBusTraits::LocklessDispatch
|
||||
*/
|
||||
using DispatchLockGuard = AZStd::conditional_t<BusTraits::LocklessDispatch, AZ::Internal::NullLockGuard<ContextMutexType>, AZStd::scoped_lock<ContextMutexType>>;
|
||||
using DispatchLockGuard = DispatchLockGuard<ContextMutexType>;
|
||||
|
||||
/**
|
||||
* The scoped lock guard to use during connection. Some specialized policies execute handler methods which
|
||||
@@ -704,6 +725,11 @@ namespace AZ
|
||||
static Context& GetOrCreateContext(bool trackCallstack=true);
|
||||
|
||||
static bool IsInDispatch(Context* context = GetContext(false));
|
||||
|
||||
/**
|
||||
* Returns whether the EBus context is in the middle of a dispatch on the current thread
|
||||
*/
|
||||
static bool IsInDispatchThisThread(Context* context = GetContext(false));
|
||||
/// @cond EXCLUDE_DOCS
|
||||
struct RouterCallstackEntry
|
||||
: public CallstackEntry
|
||||
@@ -1208,6 +1234,13 @@ AZ_POP_DISABLE_WARNING
|
||||
return context != nullptr && context->m_dispatches > 0;
|
||||
}
|
||||
|
||||
template<class Interface, class Traits>
|
||||
bool EBus<Interface, Traits>::IsInDispatchThisThread(Context* context)
|
||||
{
|
||||
return context != nullptr && context->s_callstack != nullptr
|
||||
&& context->s_callstack->m_prev != nullptr;
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
template<class Interface, class Traits>
|
||||
EBus<Interface, Traits>::RouterCallstackEntry::RouterCallstackEntry(Iterator it, const BusIdType* busId, bool isQueued, bool isReverse)
|
||||
|
||||
@@ -15,7 +15,7 @@ using namespace Intersect;
|
||||
// IntersectSegmentTriangleCCW
|
||||
// [10/21/2009]
|
||||
//=========================================================================
|
||||
int Intersect::IntersectSegmentTriangleCCW(
|
||||
bool Intersect::IntersectSegmentTriangleCCW(
|
||||
const Vector3& p, const Vector3& q, const Vector3& a, const Vector3& b, const Vector3& c,
|
||||
/*float &u, float &v, float &w,*/ Vector3& normal, float& t)
|
||||
{
|
||||
@@ -34,7 +34,7 @@ int Intersect::IntersectSegmentTriangleCCW(
|
||||
float d = qp.Dot(normal);
|
||||
if (d <= 0.0f)
|
||||
{
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Compute intersection t value of pq with plane of triangle. A ray
|
||||
@@ -46,7 +46,7 @@ int Intersect::IntersectSegmentTriangleCCW(
|
||||
// range segment check t[0,1] (it this case [0,d])
|
||||
if (t < 0.0f || t > d)
|
||||
{
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Compute barycentric coordinate components and test if within bounds
|
||||
@@ -54,12 +54,12 @@ int Intersect::IntersectSegmentTriangleCCW(
|
||||
v = ac.Dot(e);
|
||||
if (v < 0.0f || v > d)
|
||||
{
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
w = -ab.Dot(e);
|
||||
if (w < 0.0f || v + w > d)
|
||||
{
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Segment/ray intersects triangle. Perform delayed division and
|
||||
@@ -72,14 +72,14 @@ int Intersect::IntersectSegmentTriangleCCW(
|
||||
|
||||
normal.Normalize();
|
||||
|
||||
return 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// IntersectSegmentTriangle
|
||||
// [10/21/2009]
|
||||
//=========================================================================
|
||||
int
|
||||
bool
|
||||
Intersect::IntersectSegmentTriangle(
|
||||
const Vector3& p, const Vector3& q, const Vector3& a, const Vector3& b, const Vector3& c,
|
||||
/*float &u, float &v, float &w,*/ Vector3& normal, float& t)
|
||||
@@ -111,7 +111,7 @@ Intersect::IntersectSegmentTriangle(
|
||||
// so either have a parallel ray or our normal is flipped
|
||||
if (d >= -Constants::FloatEpsilon)
|
||||
{
|
||||
return 0; // parallel
|
||||
return false; // parallel
|
||||
}
|
||||
d = -d;
|
||||
e = ap.Cross(qp);
|
||||
@@ -125,19 +125,19 @@ Intersect::IntersectSegmentTriangle(
|
||||
// range segment check t[0,1] (it this case [0,d])
|
||||
if (t < 0.0f || t > d)
|
||||
{
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Compute barycentric coordinate components and test if within bounds
|
||||
v = ac.Dot(e);
|
||||
if (v < 0.0f || v > d)
|
||||
{
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
w = -ab.Dot(e);
|
||||
if (w < 0.0f || v + w > d)
|
||||
{
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Segment/ray intersects the triangle. Perform delayed division and
|
||||
@@ -150,14 +150,14 @@ Intersect::IntersectSegmentTriangle(
|
||||
|
||||
normal.Normalize();
|
||||
|
||||
return 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// TestSegmentAABBOrigin
|
||||
// [10/21/2009]
|
||||
//=========================================================================
|
||||
int
|
||||
bool
|
||||
AZ::Intersect::TestSegmentAABBOrigin(const Vector3& midPoint, const Vector3& halfVector, const Vector3& aabbExtends)
|
||||
{
|
||||
const Vector3 EPSILON(0.001f); // \todo this is slow load move to a const
|
||||
@@ -168,7 +168,7 @@ AZ::Intersect::TestSegmentAABBOrigin(const Vector3& midPoint, const Vector3& hal
|
||||
// Try world coordinate axes as separating axes
|
||||
if (!absMidpoint.IsLessEqualThan(absHalfMidpoint))
|
||||
{
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Add in an epsilon term to counteract arithmetic errors when segment is
|
||||
@@ -188,11 +188,11 @@ AZ::Intersect::TestSegmentAABBOrigin(const Vector3& midPoint, const Vector3& hal
|
||||
Vector3 ead(ey * adz + ez * ady, ex * adz + ez * adx, ex * ady + ey * adx);
|
||||
if (!absMDCross.IsLessEqualThan(ead))
|
||||
{
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
// No separating axis found; segment must be overlapping AABB
|
||||
return 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -200,7 +200,7 @@ AZ::Intersect::TestSegmentAABBOrigin(const Vector3& midPoint, const Vector3& hal
|
||||
// IntersectRayAABB
|
||||
// [10/21/2009]
|
||||
//=========================================================================
|
||||
int
|
||||
RayAABBIsectTypes
|
||||
AZ::Intersect::IntersectRayAABB(
|
||||
const Vector3& rayStart, const Vector3& dir, const Vector3& dirRCP, const Aabb& aabb,
|
||||
float& tStart, float& tEnd, Vector3& startNormal /*, Vector3& inter*/)
|
||||
@@ -356,7 +356,7 @@ AZ::Intersect::IntersectRayAABB(
|
||||
// IntersectRayAABB2
|
||||
// [2/18/2011]
|
||||
//=========================================================================
|
||||
int
|
||||
RayAABBIsectTypes
|
||||
AZ::Intersect::IntersectRayAABB2(const Vector3& rayStart, const Vector3& dirRCP, const Aabb& aabb, float& start, float& end)
|
||||
{
|
||||
float tmin, tmax, tymin, tymax, tzmin, tzmax;
|
||||
@@ -408,7 +408,7 @@ AZ::Intersect::IntersectRayAABB2(const Vector3& rayStart, const Vector3& dirRCP,
|
||||
return ISECT_RAY_AABB_ISECT;
|
||||
}
|
||||
|
||||
int AZ::Intersect::IntersectRayDisk(
|
||||
bool AZ::Intersect::IntersectRayDisk(
|
||||
const Vector3& rayOrigin, const Vector3& rayDir, const Vector3& diskCenter, const float diskRadius, const Vector3& diskNormal, float& t)
|
||||
{
|
||||
// First intersect with the plane of the disk
|
||||
@@ -421,10 +421,10 @@ int AZ::Intersect::IntersectRayDisk(
|
||||
if (pointOnPlane.GetDistance(diskCenter) < diskRadius)
|
||||
{
|
||||
t = planeIntersectionDistance;
|
||||
return 1;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Reference: Real-Time Collision Detection - 5.3.7 Intersecting Ray or Segment Against Cylinder, and the book's errata.
|
||||
@@ -1012,7 +1012,7 @@ int AZ::Intersect::IntersectRayQuad(
|
||||
}
|
||||
|
||||
// reference: Real-Time Collision Detection, 5.3.3 Intersecting Ray or Segment Against Box
|
||||
int AZ::Intersect::IntersectRayBox(
|
||||
bool AZ::Intersect::IntersectRayBox(
|
||||
const Vector3& rayOrigin, const Vector3& rayDir, const Vector3& boxCenter, const Vector3& boxAxis1,
|
||||
const Vector3& boxAxis2, const Vector3& boxAxis3, float boxHalfExtent1, float boxHalfExtent2, float boxHalfExtent3, float& t)
|
||||
{
|
||||
@@ -1044,7 +1044,7 @@ int AZ::Intersect::IntersectRayBox(
|
||||
// If the ray is parallel to the slab and the ray origin is outside, return no intersection.
|
||||
if (tp < 0.0f || tn < 0.0f)
|
||||
{
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -1065,7 +1065,7 @@ int AZ::Intersect::IntersectRayBox(
|
||||
tmax = AZ::GetMin(tmax, t2);
|
||||
if (tmin > tmax)
|
||||
{
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1085,7 +1085,7 @@ int AZ::Intersect::IntersectRayBox(
|
||||
// If the ray is parallel to the slab and the ray origin is outside, return no intersection.
|
||||
if (tp < 0.0f || tn < 0.0f)
|
||||
{
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -1106,7 +1106,7 @@ int AZ::Intersect::IntersectRayBox(
|
||||
tmax = AZ::GetMin(tmax, t2);
|
||||
if (tmin > tmax)
|
||||
{
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1126,7 +1126,7 @@ int AZ::Intersect::IntersectRayBox(
|
||||
// If the ray is parallel to the slab and the ray origin is outside, return no intersection.
|
||||
if (tp < 0.0f || tn < 0.0f)
|
||||
{
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -1147,15 +1147,15 @@ int AZ::Intersect::IntersectRayBox(
|
||||
tmax = AZ::GetMin(tmax, t2);
|
||||
if (tmin > tmax)
|
||||
{
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
t = (isRayOriginInsideBox ? tmax : tmin);
|
||||
return 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
int AZ::Intersect::IntersectRayObb(const Vector3& rayOrigin, const Vector3& rayDir, const Obb& obb, float& t)
|
||||
bool AZ::Intersect::IntersectRayObb(const Vector3& rayOrigin, const Vector3& rayDir, const Obb& obb, float& t)
|
||||
{
|
||||
return AZ::Intersect::IntersectRayBox(rayOrigin, rayDir, obb.GetPosition(),
|
||||
obb.GetAxisX(), obb.GetAxisY(), obb.GetAxisZ(),
|
||||
@@ -1166,7 +1166,7 @@ int AZ::Intersect::IntersectRayObb(const Vector3& rayOrigin, const Vector3& rayD
|
||||
// IntersectSegmentCylinder
|
||||
// [10/21/2009]
|
||||
//=========================================================================
|
||||
int
|
||||
CylinderIsectTypes
|
||||
AZ::Intersect::IntersectSegmentCylinder(
|
||||
const Vector3& sa, const Vector3& dir, const Vector3& p, const Vector3& q, const float r, float& t)
|
||||
{
|
||||
@@ -1225,7 +1225,7 @@ AZ::Intersect::IntersectSegmentCylinder(
|
||||
return RR_ISECT_RAY_CYL_NONE; // No real roots; no intersection
|
||||
}
|
||||
t = (-b - Sqrt(discr)) / a;
|
||||
int result = RR_ISECT_RAY_CYL_PQ; // default along the PQ segment
|
||||
CylinderIsectTypes result = RR_ISECT_RAY_CYL_PQ; // default along the PQ segment
|
||||
|
||||
if (md + t * nd < 0.0f)
|
||||
{
|
||||
@@ -1294,7 +1294,7 @@ AZ::Intersect::IntersectSegmentCylinder(
|
||||
// IntersectSegmentCapsule
|
||||
// [10/21/2009]
|
||||
//=========================================================================
|
||||
int
|
||||
CapsuleIsectTypes
|
||||
AZ::Intersect::IntersectSegmentCapsule(const Vector3& sa, const Vector3& dir, const Vector3& p, const Vector3& q, const float r, float& t)
|
||||
{
|
||||
int result = IntersectSegmentCylinder(sa, dir, p, q, r, t);
|
||||
@@ -1361,13 +1361,13 @@ AZ::Intersect::IntersectSegmentCapsule(const Vector3& sa, const Vector3& dir, co
|
||||
// IntersectSegmentPolyhedron
|
||||
// [10/21/2009]
|
||||
//=========================================================================
|
||||
int
|
||||
bool
|
||||
AZ::Intersect::IntersectSegmentPolyhedron(
|
||||
const Vector3& sa, const Vector3& sBA, const Plane p[], int numPlanes,
|
||||
const Vector3& sa, const Vector3& dir, const Plane p[], int numPlanes,
|
||||
float& tfirst, float& tlast, int& iFirstPlane, int& iLastPlane)
|
||||
{
|
||||
// Compute direction vector for the segment
|
||||
Vector3 d = /*b - a*/ sBA;
|
||||
Vector3 d = /*b - a*/ dir;
|
||||
// Set initial interval to being the whole segment. For a ray, tlast should be
|
||||
// set to +RR_FLT_MAX. For a line, additionally tfirst should be set to -RR_FLT_MAX
|
||||
tfirst = 0.0f;
|
||||
@@ -1388,7 +1388,7 @@ AZ::Intersect::IntersectSegmentPolyhedron(
|
||||
// If so, return "no intersection" if segment lies outside plane
|
||||
if (dist < 0.0f)
|
||||
{
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -1417,7 +1417,7 @@ AZ::Intersect::IntersectSegmentPolyhedron(
|
||||
// Exit with "no intersection" if intersection becomes empty
|
||||
if (tfirst > tlast)
|
||||
{
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1425,11 +1425,11 @@ AZ::Intersect::IntersectSegmentPolyhedron(
|
||||
//DBG_Assert(iFirstPlane!=-1&&iLastPlane!=-1,("We have some bad border case to have only one plane, fix this function!"));
|
||||
if (iFirstPlane == -1 && iLastPlane == -1)
|
||||
{
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
// A nonzero logical intersection, so the segment intersects the polyhedron
|
||||
return 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
@@ -1442,7 +1442,7 @@ AZ::Intersect::ClosestSegmentSegment(
|
||||
const Vector3& segment2Start, const Vector3& segment2End,
|
||||
float& segment1Proportion, float& segment2Proportion,
|
||||
Vector3& closestPointSegment1, Vector3& closestPointSegment2,
|
||||
float epsilon /*= 1e-4f*/ )
|
||||
float epsilon)
|
||||
{
|
||||
const Vector3 segment1 = segment1End - segment1Start;
|
||||
const Vector3 segment2 = segment2End - segment2Start;
|
||||
|
||||
@@ -5,363 +5,398 @@
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
#ifndef AZCORE_MATH_SEGMENT_INTERSECTION_H
|
||||
#define AZCORE_MATH_SEGMENT_INTERSECTION_H
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Math/Vector3.h>
|
||||
#include <AzCore/Math/Aabb.h>
|
||||
#include <AzCore/Math/Obb.h>
|
||||
#include <AzCore/Math/Plane.h>
|
||||
|
||||
/// \file isect_segment.h
|
||||
#include <AzCore/Math/Vector3.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace Intersect
|
||||
{
|
||||
//! LineToPointDistanceTime computes the time of the shortest distance from point 'p' to segment (s1,s2).
|
||||
//! To calculate the point of intersection:
|
||||
//! P = s1 + u (s2 - s1)
|
||||
//! @param s1 segment start point
|
||||
//! @param s2 segment end point
|
||||
//! @param p point to find the closest time to.
|
||||
//! @return time (on the segment) for the shortest distance from 'p' to (s1,s2) [0.0f (s1),1.0f (s2)]
|
||||
inline float LineToPointDistanceTime(const Vector3& s1, const Vector3& s21, const Vector3& p)
|
||||
{
|
||||
// so u = (p.x - s1.x)*(s2.x - s1.x) + (p.y - s1.y)*(s2.y - s1.y) + (p.z-s1.z)*(s2.z-s1.z) / |s2-s1|^2
|
||||
return s21.Dot(p - s1) / s21.Dot(s21);
|
||||
}
|
||||
//! To calculate the point of intersection: P = s1 + u (s2 - s1)
|
||||
//! @param s1 Segment start point.
|
||||
//! @param s2 Segment end point.
|
||||
//! @param p Point to find the closest time to.
|
||||
//! @return Time (on the segment) for the shortest distance from 'p' to (s1,s2) [0.0f (s1),1.0f (s2)]
|
||||
float LineToPointDistanceTime(const Vector3& s1, const Vector3& s21, const Vector3& p);
|
||||
|
||||
//! LineToPointDistance computes the closest point to 'p' from a segment (s1,s2).
|
||||
//! @param s1 segment start point
|
||||
//! @param s2 segment end point
|
||||
//! @param p point to find the closest time to.
|
||||
//! @param u time (on the segment) for the shortest distance from 'p' to (s1,s2) [0.0f (s1),1.0f (s2)]
|
||||
//! @return the closest point
|
||||
inline Vector3 LineToPointDistance(const Vector3& s1, const Vector3& s2, const Vector3& p, float& u)
|
||||
{
|
||||
const Vector3 s21 = s2 - s1;
|
||||
// we assume seg1 and seg2 are NOT coincident
|
||||
AZ_MATH_ASSERT(!s21.IsClose(Vector3(0.0f), 1e-4f), "OK we agreed that we will pass valid segments! (s1 != s2)");
|
||||
|
||||
u = LineToPointDistanceTime(s1, s21, p);
|
||||
|
||||
return s1 + u * s21;
|
||||
}
|
||||
//! @param s1 Segment start point
|
||||
//! @param s2 Segment end point
|
||||
//! @param p Point to find the closest time to.
|
||||
//! @param u Time (on the segment) for the shortest distance from 'p' to (s1,s2) [0.0f (s1),1.0f (s2)]
|
||||
//! @return The closest point
|
||||
Vector3 LineToPointDistance(const Vector3& s1, const Vector3& s2, const Vector3& p, float& u);
|
||||
|
||||
//! Given segment pq and triangle abc (CCW), returns whether segment intersects
|
||||
//! triangle and if so, also returns the barycentric coordinates (u,v,w)
|
||||
//! of the intersection point.
|
||||
//! @param p segment start point
|
||||
//! @param q segment end point
|
||||
//! @param a triangle point 1
|
||||
//! @param b triangle point 2
|
||||
//! @param c triangle point 3
|
||||
//! @param normal at the intersection point.
|
||||
//! @param t time of intersection along the segment [0.0 (p), 1.0 (q)]
|
||||
//! @return 1 if the segment intersects the triangle otherwise 0
|
||||
int IntersectSegmentTriangleCCW(
|
||||
const Vector3& p, const Vector3& q, const Vector3& a, const Vector3& b, const Vector3& c,
|
||||
/*float &u, float &v, float &w,*/ Vector3& normal, float& t);
|
||||
//! @param p Segment start point.
|
||||
//! @param q Segment end point.
|
||||
//! @param a Triangle point 1.
|
||||
//! @param b Triangle point 2.
|
||||
//! @param c Triangle point 3.
|
||||
//! @param normal At the intersection point.
|
||||
//! @param t Time of intersection along the segment [0.0 (p), 1.0 (q)].
|
||||
//! @return true if the segments intersects the triangle otherwise false.
|
||||
bool IntersectSegmentTriangleCCW(
|
||||
const Vector3& p, const Vector3& q, const Vector3& a, const Vector3& b, const Vector3& c, Vector3& normal, float& t);
|
||||
|
||||
//! Same as \ref IntersectSegmentTriangleCCW without respecting the triangle (a,b,c) vertex order (double sided).
|
||||
int IntersectSegmentTriangle(
|
||||
const Vector3& p, const Vector3& q, const Vector3& a, const Vector3& b, const Vector3& c,
|
||||
/*float &u, float &v, float &w,*/ Vector3& normal, float& t);
|
||||
//! @param p Segment start point.
|
||||
//! @param q Segment end point.
|
||||
//! @param a Triangle point 1.
|
||||
//! @param b Triangle point 2.
|
||||
//! @param c Triangle point 3.
|
||||
//! @param normal At the intersection point.
|
||||
//! @param t Time of intersection along the segment [0.0 (p), 1.0 (q)].
|
||||
//! @return True if the segments intersects the triangle otherwise false.
|
||||
bool IntersectSegmentTriangle(
|
||||
const Vector3& p, const Vector3& q, const Vector3& a, const Vector3& b, const Vector3& c, Vector3& normal, float& t);
|
||||
|
||||
//! Ray aabb intersection result types.
|
||||
enum RayAABBIsectTypes
|
||||
enum RayAABBIsectTypes : AZ::s32
|
||||
{
|
||||
ISECT_RAY_AABB_NONE = 0, ///< no intersection
|
||||
ISECT_RAY_AABB_SA_INSIDE, ///< the ray starts inside the aabb
|
||||
ISECT_RAY_AABB_ISECT, ///< intersects along the PQ segment
|
||||
ISECT_RAY_AABB_NONE = 0, ///< no intersection
|
||||
ISECT_RAY_AABB_SA_INSIDE, ///< the ray starts inside the aabb
|
||||
ISECT_RAY_AABB_ISECT, ///< intersects along the PQ segment
|
||||
};
|
||||
|
||||
//! Intersect ray R(t) = rayStart + t*d against AABB a. When intersecting,
|
||||
//! return intersection distance tmin and point q of intersection.
|
||||
//! @param rayStart ray starting point
|
||||
//! @param dir ray direction and length (dir = rayEnd - rayStart)
|
||||
//! @param dirRCP 1/dir (reciprocal direction - we cache this result very often so we don't need to compute it multiple times, otherwise just use dir.GetReciprocal())
|
||||
//! @param rayStart Ray starting point
|
||||
//! @param dir Ray direction and length (dir = rayEnd - rayStart)
|
||||
//! @param dirRCP 1/dir (reciprocal direction - we cache this result very often so we don't need to compute it multiple times,
|
||||
//! otherwise just use dir.GetReciprocal())
|
||||
//! @param aabb Axis aligned bounding box to intersect against
|
||||
//! @param tStart time on ray of the first intersection [0,1] or 0 if the ray starts inside the aabb - check the return value
|
||||
//! @param tEnd time of the of the second intersection [0,1] (it can be > 1 if intersects after the rayEnd)
|
||||
//! @param startNormal normal at the start point.
|
||||
//! @param tStart Time on ray of the first intersection [0,1] or 0 if the ray starts inside the aabb - check the return value
|
||||
//! @param tEnd Time of the of the second intersection [0,1] (it can be > 1 if intersects after the rayEnd)
|
||||
//! @param startNormal Normal at the start point.
|
||||
//! @return \ref RayAABBIsectTypes
|
||||
int IntersectRayAABB(
|
||||
const Vector3& rayStart, const Vector3& dir, const Vector3& dirRCP, const Aabb& aabb,
|
||||
float& tStart, float& tEnd, Vector3& startNormal /*, Vector3& inter*/);
|
||||
RayAABBIsectTypes IntersectRayAABB(
|
||||
const Vector3& rayStart,
|
||||
const Vector3& dir,
|
||||
const Vector3& dirRCP,
|
||||
const Aabb& aabb,
|
||||
float& tStart,
|
||||
float& tEnd,
|
||||
Vector3& startNormal);
|
||||
|
||||
//! Intersect ray against AABB.
|
||||
//! @param rayStart ray starting point.
|
||||
//! @param dir ray reciprocal direction.
|
||||
//! @param rayStart Ray starting point.
|
||||
//! @param dir Ray reciprocal direction.
|
||||
//! @param aabb Axis aligned bounding box to intersect against.
|
||||
//! @param start length on ray of the first intersection.
|
||||
//! @param end length of the of the second intersection.
|
||||
//! @return \ref RayAABBIsectTypes In this faster version than IntersectRayAABB we return only ISECT_RAY_AABB_NONE and ISECT_RAY_AABB_ISECT.
|
||||
//! You can check yourself for that case.
|
||||
int IntersectRayAABB2(
|
||||
const Vector3& rayStart, const Vector3& dirRCP, const Aabb& aabb,
|
||||
float& start, float& end);
|
||||
//! @param start Length on ray of the first intersection.
|
||||
//! @param end Length of the of the second intersection.
|
||||
//! @return \ref RayAABBIsectTypes In this faster version than IntersectRayAABB we return only ISECT_RAY_AABB_NONE and
|
||||
//! ISECT_RAY_AABB_ISECT. You can check yourself for that case.
|
||||
RayAABBIsectTypes IntersectRayAABB2(const Vector3& rayStart, const Vector3& dirRCP, const Aabb& aabb, float& start, float& end);
|
||||
|
||||
//! Clip a ray to an aabb. return true if ray was clipped. The ray
|
||||
//! can be inside so don't use the result if the ray intersect the box.
|
||||
inline int ClipRayWithAabb(
|
||||
const Aabb& aabb, Vector3& rayStart, Vector3& rayEnd, float& tClipStart, float& tClipEnd)
|
||||
{
|
||||
Vector3 startNormal;
|
||||
float tStart, tEnd;
|
||||
Vector3 dirLen = rayEnd - rayStart;
|
||||
if (IntersectRayAABB(rayStart, dirLen, dirLen.GetReciprocal(), aabb, tStart, tEnd, startNormal) != ISECT_RAY_AABB_NONE)
|
||||
{
|
||||
// clip the ray with the box
|
||||
if (tStart > 0.0f)
|
||||
{
|
||||
rayStart = rayStart + tStart * dirLen;
|
||||
tClipStart = tStart;
|
||||
}
|
||||
if (tEnd < 1.0f)
|
||||
{
|
||||
rayEnd = rayStart + tEnd * dirLen;
|
||||
tClipEnd = tEnd;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
//! @param aabb Bounds to test against.
|
||||
//! @param rayStart The start of the ray.
|
||||
//! @param rayEnd The end of the ray.
|
||||
//! @param[out] tClipStart The proportion where the ray enters the \ref Aabb.
|
||||
//! @param[out] tClipEnd The proportion where the ray exits the \ref Aabb.
|
||||
//! @return True if the ray was clipped, otherwise false.
|
||||
bool ClipRayWithAabb(const Aabb& aabb, Vector3& rayStart, Vector3& rayEnd, float& tClipStart, float& tClipEnd);
|
||||
|
||||
//! Test segment and aabb where the segment is defined by midpoint
|
||||
//! midPoint = (p1-p0) * 0.5f and half vector halfVector = p1 - midPoint.
|
||||
//! the aabb is at the origin and defined by half extents only.
|
||||
//! @return 1 if the intersect, otherwise 0.
|
||||
int TestSegmentAABBOrigin(const Vector3& midPoint, const Vector3& halfVector, const Vector3& aabbExtends);
|
||||
//! @param midPoint Midpoint of a line segment.
|
||||
//! @param halfVector Half vector of an aabb.
|
||||
//! @param aabbExtends The extends of a bounded box.
|
||||
//! @return True if the segment and AABB intersect, otherwise false
|
||||
bool TestSegmentAABBOrigin(const Vector3& midPoint, const Vector3& halfVector, const Vector3& aabbExtends);
|
||||
|
||||
//! Test if segment specified by points p0 and p1 intersects AABB. \ref TestSegmentAABBOrigin
|
||||
//! @return 1 if the segment and AABB intersect, otherwise 0.
|
||||
inline int TestSegmentAABB(const Vector3& p0, const Vector3& p1, const Aabb& aabb)
|
||||
{
|
||||
Vector3 e = aabb.GetExtents();
|
||||
Vector3 d = p1 - p0;
|
||||
Vector3 m = p0 + p1 - aabb.GetMin() - aabb.GetMax();
|
||||
|
||||
return TestSegmentAABBOrigin(m, d, e);
|
||||
}
|
||||
//! Test if segment specified by points p0 and p1 intersects AABB. \ref TestSegmentAABBOrigin.
|
||||
//! @param p0 Segment start point.
|
||||
//! @param p1 Segment end point.
|
||||
//! @param aabb Bounded box to test against.
|
||||
//! @return True if the segment and AABB intersect, otherwise false.
|
||||
bool TestSegmentAABB(const Vector3& p0, const Vector3& p1, const Aabb& aabb);
|
||||
|
||||
//! Ray sphere intersection result types.
|
||||
enum SphereIsectTypes
|
||||
enum SphereIsectTypes : AZ::s32
|
||||
{
|
||||
ISECT_RAY_SPHERE_SA_INSIDE = -1, // the ray starts inside the cylinder
|
||||
ISECT_RAY_SPHERE_NONE, // no intersection
|
||||
ISECT_RAY_SPHERE_ISECT, // along the PQ segment
|
||||
ISECT_RAY_SPHERE_SA_INSIDE = -1, //!< The ray starts inside the cylinder
|
||||
ISECT_RAY_SPHERE_NONE, //!< No intersection
|
||||
ISECT_RAY_SPHERE_ISECT, //!< Along the PQ segment
|
||||
};
|
||||
|
||||
//! IntersectRaySphereOrigin
|
||||
//! return time t>=0 but not limited, so if you check a segment make sure
|
||||
//! t <= segmentLen
|
||||
//! @param rayStart ray start point
|
||||
//! t <= segmentLen.
|
||||
//! @param rayStart ray start point.
|
||||
//! @param rayDirNormalized ray direction normalized.
|
||||
//! @param shereRadius sphere radius
|
||||
//! @param shereRadius Radius of sphere at origin.
|
||||
//! @param time of closest intersection [0,+INF] in relation to the normalized direction.
|
||||
//! @return \ref SphereIsectTypes
|
||||
AZ_INLINE int IntersectRaySphereOrigin(
|
||||
const Vector3& rayStart, const Vector3& rayDirNormalized,
|
||||
const float sphereRadius, float& t)
|
||||
{
|
||||
Vector3 m = rayStart;
|
||||
float b = m.Dot(rayDirNormalized);
|
||||
float c = m.Dot(m) - sphereRadius * sphereRadius;
|
||||
|
||||
// Exit if r's origin outside s (c > 0)and r pointing away from s (b > 0)
|
||||
if (c > 0.0f && b > 0.0f)
|
||||
{
|
||||
return ISECT_RAY_SPHERE_NONE;
|
||||
}
|
||||
float discr = b * b - c;
|
||||
// A negative discriminant corresponds to ray missing sphere
|
||||
if (discr < 0.0f)
|
||||
{
|
||||
return ISECT_RAY_SPHERE_NONE;
|
||||
}
|
||||
|
||||
// Ray now found to intersect sphere, compute smallest t value of intersection
|
||||
t = -b - Sqrt(discr);
|
||||
|
||||
// If t is negative, ray started inside sphere so clamp t to zero
|
||||
if (t < 0.0f)
|
||||
{
|
||||
// t = 0.0f;
|
||||
return ISECT_RAY_SPHERE_SA_INSIDE; // no hit if inside
|
||||
}
|
||||
//q = p + t * d;
|
||||
return ISECT_RAY_SPHERE_ISECT;
|
||||
}
|
||||
//! @return \ref SphereIsectTypes.
|
||||
SphereIsectTypes IntersectRaySphereOrigin(
|
||||
const Vector3& rayStart, const Vector3& rayDirNormalized, const float sphereRadius, float& t);
|
||||
|
||||
//! Intersect ray (rayStart,rayDirNormalized) and sphere (sphereCenter,sphereRadius) \ref IntersectRaySphereOrigin
|
||||
inline int IntersectRaySphere(
|
||||
const Vector3& rayStart, const Vector3& rayDirNormalized, const Vector3& sphereCenter, const float sphereRadius, float& t)
|
||||
{
|
||||
return IntersectRaySphereOrigin(rayStart - sphereCenter, rayDirNormalized, sphereRadius, t);
|
||||
}
|
||||
//! @param rayStart The start of the ray.
|
||||
//! @param rayDirNormalized The direction of the ray normalized.
|
||||
//! @param sphereCenter The center of the sphere.
|
||||
//! @param sphereRadius Radius of the sphere.
|
||||
//! @param[out] t Coefficient in the ray's explicit equation from which an
|
||||
//! intersecting point is calculated as "rayOrigin + t1 * rayDir".
|
||||
//! @return SphereIsectTypes
|
||||
SphereIsectTypes IntersectRaySphere(
|
||||
const Vector3& rayStart, const Vector3& rayDirNormalized, const Vector3& sphereCenter, const float sphereRadius, float& t);
|
||||
|
||||
//! @param rayOrigin The origin of the ray to test.
|
||||
//! @param rayDir The direction of the ray to test. It has to be unit length.
|
||||
//! @param diskCenter Center point of the disk
|
||||
//! @param diskRadius Radius of the disk
|
||||
//! @param diskNormal A normal perpendicular to the disk
|
||||
//! @param[out] t If returning 1 (indicating a hit), this contains distance from rayOrigin along the normalized rayDir that the hit occured at.
|
||||
//! @return The number of intersecting points.
|
||||
int IntersectRayDisk(
|
||||
const Vector3& rayOrigin, const Vector3& rayDir, const Vector3& diskCenter, const float diskRadius, const AZ::Vector3& diskNormal, float& t);
|
||||
//! Intersect ray (rayStarty, rayDirNormalized) and disk (center, radius, normal)
|
||||
//! @param rayOrigin The origin of the ray to test.
|
||||
//! @param rayDir The direction of the ray to test. It has to be unit length.
|
||||
//! @param diskCenter Center point of the disk.
|
||||
//! @param diskRadius Radius of the disk.
|
||||
//! @param diskNormal A normal perpendicular to the disk.
|
||||
//! @param[out] t If returning 1 (indicating a hit), this contains distance from rayOrigin along the normalized rayDir
|
||||
//! that the hit occured at.
|
||||
//! @return False if not interesecting and true if intersecting
|
||||
bool IntersectRayDisk(
|
||||
const Vector3& rayOrigin,
|
||||
const Vector3& rayDir,
|
||||
const Vector3& diskCenter,
|
||||
const float diskRadius,
|
||||
const AZ::Vector3& diskNormal,
|
||||
float& t);
|
||||
|
||||
//! If there is only one intersecting point, the coefficient is stored in \ref t1.
|
||||
//! @param rayOrigin The origin of the ray to test.
|
||||
//! @param rayDir The direction of the ray to test. It has to be unit length.
|
||||
//! @param cylinderEnd1 The center of the circle on one end of the cylinder.
|
||||
//! @param cylinderDir The direction pointing from \ref cylinderEnd1 to the other end of the cylinder. It has to be unit length.
|
||||
//! @param cylinderHeight The distance between two centers of the circles on two ends of the cylinder respectively.
|
||||
//! @param[out] t1 A possible coefficient in the ray's explicit equation from which an intersecting point is calculated as "rayOrigin + t1 * rayDir".
|
||||
//! @param[out] t2 A possible coefficient in the ray's explicit equation from which an intersecting point is calculated as "rayOrigin + t2 * rayDir".
|
||||
//! @return The number of intersecting points.
|
||||
//! @param rayOrigin The origin of the ray to test.
|
||||
//! @param rayDir The direction of the ray to test. It has to be unit length.
|
||||
//! @param cylinderEnd1 The center of the circle on one end of the cylinder.
|
||||
//! @param cylinderDir The direction pointing from \ref cylinderEnd1 to the other end of the cylinder. It has to be unit length.
|
||||
//! @param cylinderHeight The distance between two centers of the circles on two ends of the cylinder respectively.
|
||||
//! @param[out] t1 A possible coefficient in the ray's explicit equation from which an intersecting point is calculated as "rayOrigin + t1 * rayDir".
|
||||
//! @param[out] t2 A possible coefficient in the ray's explicit equation from which an intersecting point is calculated as "rayOrigin + t2 * rayDir".
|
||||
//! @return The number of intersecting points.
|
||||
int IntersectRayCappedCylinder(
|
||||
const Vector3& rayOrigin, const Vector3& rayDir,
|
||||
const Vector3& cylinderEnd1, const Vector3& cylinderDir, float cylinderHeight, float cylinderRadius,
|
||||
float& t1, float& t2);
|
||||
const Vector3& rayOrigin,
|
||||
const Vector3& rayDir,
|
||||
const Vector3& cylinderEnd1,
|
||||
const Vector3& cylinderDir,
|
||||
float cylinderHeight,
|
||||
float cylinderRadius,
|
||||
float& t1,
|
||||
float& t2);
|
||||
|
||||
//! If there is only one intersecting point, the coefficient is stored in \ref t1.
|
||||
//! @param rayOrigin The origin of the ray to test.
|
||||
//! @param rayDir The direction of the ray to test. It has to be unit length.
|
||||
//! @param coneApex The apex of the cone.
|
||||
//! @param coneDir The unit-length direction from the apex to the base.
|
||||
//! @param coneHeight The height of the cone, from the apex to the base.
|
||||
//! @param coneBaseRadius The radius of the cone base circle.
|
||||
//! @param[out] t1 A possible coefficient in the ray's explicit equation from which an intersecting point is calculated as "rayOrigin + t1 * rayDir".
|
||||
//! @param[out] t2 A possible coefficient in the ray's explicit equation from which an intersecting point is calculated as "rayOrigin + t2 * rayDir".
|
||||
//! @return The number of intersecting points.
|
||||
//! @param rayOrigin The origin of the ray to test.
|
||||
//! @param rayDir The direction of the ray to test. It has to be unit length.
|
||||
//! @param coneApex The apex of the cone.
|
||||
//! @param coneDir The unit-length direction from the apex to the base.
|
||||
//! @param coneHeight The height of the cone, from the apex to the base.
|
||||
//! @param coneBaseRadius The radius of the cone base circle.
|
||||
//! @param[out] t1 A possible coefficient in the ray's explicit equation from which an intersecting point is calculated as "rayOrigin + t1 * rayDir".
|
||||
//! @param[out] t2 A possible coefficient in the ray's explicit equation from which an intersecting point is calculated as "rayOrigin + t2 * rayDir".
|
||||
//! @return The number of intersecting points.
|
||||
int IntersectRayCone(
|
||||
const Vector3& rayOrigin, const Vector3& rayDir,
|
||||
const Vector3& coneApex, const Vector3& coneDir, float coneHeight, float coneBaseRadius,
|
||||
float& t1, float& t2);
|
||||
const Vector3& rayOrigin,
|
||||
const Vector3& rayDir,
|
||||
const Vector3& coneApex,
|
||||
const Vector3& coneDir,
|
||||
float coneHeight,
|
||||
float coneBaseRadius,
|
||||
float& t1,
|
||||
float& t2);
|
||||
|
||||
//! Test intersection between a ray and a plane in 3D.
|
||||
//! @param rayOrigin The origin of the ray to test intersection with.
|
||||
//! @param rayDir The direction of the ray to test intersection with.
|
||||
//! @param planePos A point on the plane to test intersection with.
|
||||
//! @param planeNormal The normal of the plane to test intersection with.
|
||||
//! @param t[out] The coefficient in the ray's explicit equation from which the intersecting point is calculated as "rayOrigin + t * rayDirection".
|
||||
//! @return The number of intersection point.
|
||||
//! @param rayOrigin The origin of the ray to test intersection with.
|
||||
//! @param rayDir The direction of the ray to test intersection with.
|
||||
//! @param planePos A point on the plane to test intersection with.
|
||||
//! @param planeNormal The normal of the plane to test intersection with.
|
||||
//! @param[out] t The coefficient in the ray's explicit equation from which the intersecting point is calculated as "rayOrigin + t * rayDirection".
|
||||
//! @return The number of intersection point.
|
||||
int IntersectRayPlane(
|
||||
const Vector3& rayOrigin, const Vector3& rayDir, const Vector3& planePos,
|
||||
const Vector3& planeNormal, float& t);
|
||||
const Vector3& rayOrigin, const Vector3& rayDir, const Vector3& planePos, const Vector3& planeNormal, float& t);
|
||||
|
||||
//! Test intersection between a ray and a two-sided quadrilateral defined by four points in 3D.
|
||||
//! The four points that define the quadrilateral could be passed in with either counter clock-wise
|
||||
//! The four points that define the quadrilateral could be passed in with either counter clock-wise
|
||||
//! winding or clock-wise winding.
|
||||
//! @param rayOrigin The origin of the ray to test intersection with.
|
||||
//! @param rayDir The direction of the ray to test intersection with.
|
||||
//! @param vertexA One of the four points that define the quadrilateral.
|
||||
//! @param vertexB One of the four points that define the quadrilateral.
|
||||
//! @param vertexC One of the four points that define the quadrilateral.
|
||||
//! @param vertexD One of the four points that define the quadrilateral.
|
||||
//! @param t[out] The coefficient in the ray's explicit equation from which the intersecting point is calculated as "rayOrigin + t * rayDirection".
|
||||
//! @return The number of intersection point.
|
||||
//! @param rayOrigin The origin of the ray to test intersection with.
|
||||
//! @param rayDir The direction of the ray to test intersection with.
|
||||
//! @param vertexA One of the four points that define the quadrilateral.
|
||||
//! @param vertexB One of the four points that define the quadrilateral.
|
||||
//! @param vertexC One of the four points that define the quadrilateral.
|
||||
//! @param vertexD One of the four points that define the quadrilateral.
|
||||
//! @param[out] t The coefficient in the ray's explicit equation from which the
|
||||
//! intersecting point is calculated as "rayOrigin + t * rayDirection".
|
||||
//! @return The number of intersection point.
|
||||
int IntersectRayQuad(
|
||||
const Vector3& rayOrigin, const Vector3& rayDir, const Vector3& vertexA,
|
||||
const Vector3& vertexB, const Vector3& vertexC, const Vector3& vertexD, float& t);
|
||||
const Vector3& rayOrigin,
|
||||
const Vector3& rayDir,
|
||||
const Vector3& vertexA,
|
||||
const Vector3& vertexB,
|
||||
const Vector3& vertexC,
|
||||
const Vector3& vertexD,
|
||||
float& t);
|
||||
|
||||
//! Test intersection between a ray and an oriented box in 3D.
|
||||
//! @param rayOrigin The origin of the ray to test intersection with.
|
||||
//! @param rayDir The direction of the ray to test intersection with.
|
||||
//! @param boxCenter The position of the center of the box.
|
||||
//! @param boxAxis1 An axis along one dimension of the oriented box.
|
||||
//! @param boxAxis2 An axis along one dimension of the oriented box.
|
||||
//! @param boxAxis3 An axis along one dimension of the oriented box.
|
||||
//! @param boxHalfExtent1 The half extent of the box on the dimension of \ref boxAxis1.
|
||||
//! @param boxHalfExtent2 The half extent of the box on the dimension of \ref boxAxis2.
|
||||
//! @param boxHalfExtent3 The half extent of the box on the dimension of \ref boxAxis3.
|
||||
//! @param t[out] The coefficient in the ray's explicit equation from which the intersecting point is calculated as "rayOrigin + t * rayDirection".
|
||||
//! @return 1 if there is an intersection, 0 otherwise.
|
||||
int IntersectRayBox(
|
||||
const Vector3& rayOrigin, const Vector3& rayDir, const Vector3& boxCenter, const Vector3& boxAxis1,
|
||||
const Vector3& boxAxis2, const Vector3& boxAxis3, float boxHalfExtent1, float boxHalfExtent2, float boxHalfExtent3,
|
||||
//! Test intersection between a ray and an oriented box in 3D.
|
||||
//! @param rayOrigin The origin of the ray to test intersection with.
|
||||
//! @param rayDir The direction of the ray to test intersection with.
|
||||
//! @param boxCenter The position of the center of the box.
|
||||
//! @param boxAxis1 An axis along one dimension of the oriented box.
|
||||
//! @param boxAxis2 An axis along one dimension of the oriented box.
|
||||
//! @param boxAxis3 An axis along one dimension of the oriented box.
|
||||
//! @param boxHalfExtent1 The half extent of the box on the dimension of \ref boxAxis1.
|
||||
//! @param boxHalfExtent2 The half extent of the box on the dimension of \ref boxAxis2.
|
||||
//! @param boxHalfExtent3 The half extent of the box on the dimension of \ref boxAxis3.
|
||||
//! @param[out] t The coefficient in the ray's explicit equation from which the intersecting point is calculated as "rayOrigin + t * rayDirection".
|
||||
//! @return true if there is an intersection, false otherwise.
|
||||
bool IntersectRayBox(
|
||||
const Vector3& rayOrigin,
|
||||
const Vector3& rayDir,
|
||||
const Vector3& boxCenter,
|
||||
const Vector3& boxAxis1,
|
||||
const Vector3& boxAxis2,
|
||||
const Vector3& boxAxis3,
|
||||
float boxHalfExtent1,
|
||||
float boxHalfExtent2,
|
||||
float boxHalfExtent3,
|
||||
float& t);
|
||||
|
||||
//! Test intersection between a ray and an OBB.
|
||||
//! @param rayOrigin The origin of the ray to test intersection with.
|
||||
//! @param rayDir The direction of the ray to test intersection with.
|
||||
//! @param obb The OBB to test for intersection with the ray.
|
||||
//! @param t[out] The coefficient in the ray's explicit equation from which the intersecting point is calculated as "rayOrigin + t * rayDirection".
|
||||
//! @return 1 if there is an intersection, 0 otherwise.
|
||||
int IntersectRayObb(const Vector3& rayOrigin, const Vector3& rayDir, const Obb& obb, float& t);
|
||||
//! @param[out] t The coefficient in the ray's explicit equation from which the intersecting point is calculated as "rayOrigin + t * rayDirection".
|
||||
//! @return True if there is an intersection, false otherwise.
|
||||
bool IntersectRayObb(const Vector3& rayOrigin, const Vector3& rayDir, const Obb& obb, float& t);
|
||||
|
||||
//! Ray cylinder intersection types.
|
||||
enum CylinderIsectTypes
|
||||
enum CylinderIsectTypes : AZ::s32
|
||||
{
|
||||
RR_ISECT_RAY_CYL_SA_INSIDE = -1, // the ray starts inside the cylinder
|
||||
RR_ISECT_RAY_CYL_NONE, // no intersection
|
||||
RR_ISECT_RAY_CYL_PQ, // along the PQ segment
|
||||
RR_ISECT_RAY_CYL_P_SIDE, // on the P side
|
||||
RR_ISECT_RAY_CYL_Q_SIDE, // on the Q side
|
||||
RR_ISECT_RAY_CYL_SA_INSIDE = -1, //!< the ray starts inside the cylinder
|
||||
RR_ISECT_RAY_CYL_NONE, //!< no intersection
|
||||
RR_ISECT_RAY_CYL_PQ, //!< along the PQ segment
|
||||
RR_ISECT_RAY_CYL_P_SIDE, //!< on the P side
|
||||
RR_ISECT_RAY_CYL_Q_SIDE, //!< on the Q side
|
||||
};
|
||||
|
||||
//! Reference: Real-Time Collision Detection - 5.3.7 Intersecting Ray or Segment Against Cylinder
|
||||
//! Intersect segment S(t)=sa+t(dir), 0<=t<=1 against cylinder specified by p, q and r.
|
||||
int IntersectSegmentCylinder(
|
||||
const Vector3& sa, const Vector3& dir, const Vector3& p, const Vector3& q,
|
||||
const float r, float& t);
|
||||
//! @param sa The initial point.
|
||||
//! @param dir Magnitude and direction for sa.
|
||||
//! @param p Center point of side 1 cylinder.
|
||||
//! @param q Center point of side 2 cylinder.
|
||||
//! @param r Radius of cylinder.
|
||||
//! @param[out] t Proporition along line segment.
|
||||
//! @return CylinderIsectTypes
|
||||
CylinderIsectTypes IntersectSegmentCylinder(
|
||||
const Vector3& sa, const Vector3& dir, const Vector3& p, const Vector3& q, const float r, float& t);
|
||||
|
||||
//! Capsule ray intersect types.
|
||||
enum CapsuleIsectTypes
|
||||
{
|
||||
ISECT_RAY_CAPSULE_SA_INSIDE = -1, // the ray starts inside the cylinder
|
||||
ISECT_RAY_CAPSULE_NONE, // no intersection
|
||||
ISECT_RAY_CAPSULE_PQ, // along the PQ segment
|
||||
ISECT_RAY_CAPSULE_P_SIDE, // on the P side
|
||||
ISECT_RAY_CAPSULE_Q_SIDE, // on the Q side
|
||||
ISECT_RAY_CAPSULE_SA_INSIDE = -1, //!< The ray starts inside the cylinder
|
||||
ISECT_RAY_CAPSULE_NONE, //!< No intersection
|
||||
ISECT_RAY_CAPSULE_PQ, //!< Along the PQ segment
|
||||
ISECT_RAY_CAPSULE_P_SIDE, //!< On the P side
|
||||
ISECT_RAY_CAPSULE_Q_SIDE, //!< On the Q side
|
||||
};
|
||||
|
||||
//! This is a quick implementation of segment capsule based on segment cylinder \ref IntersectSegmentCylinder
|
||||
//! segment sphere intersection. We can optimize it a lot once we fix the ray
|
||||
//! cylinder intersection.
|
||||
int IntersectSegmentCapsule(
|
||||
const Vector3& sa, const Vector3& dir, const Vector3& p,
|
||||
const Vector3& q, const float r, float& t);
|
||||
//! @param sa The beginning of the line segment.
|
||||
//! @param dir The direction and length of the segment.
|
||||
//! @param p Center point of side 1 capsule.
|
||||
//! @param q Center point of side 1 capsule.
|
||||
//! @param r The radius of the capsule.
|
||||
//! @param[out] t Proporition along line segment.
|
||||
//! @return CapsuleIsectTypes
|
||||
CapsuleIsectTypes IntersectSegmentCapsule(
|
||||
const Vector3& sa, const Vector3& dir, const Vector3& p, const Vector3& q, const float r, float& t);
|
||||
|
||||
//! Intersect segment S(t)=A+t(B-A), 0<=t<=1 against convex polyhedron specified
|
||||
//! by the n halfspaces defined by the planes p[]. On exit tfirst and tlast
|
||||
//! define the intersection, if any.
|
||||
int IntersectSegmentPolyhedron(
|
||||
const Vector3& sa, const Vector3& sBA, const Plane p[], int numPlanes,
|
||||
float& tfirst, float& tlast, int& iFirstPlane, int& iLastPlane);
|
||||
//! @param sa The beggining of the line segment.
|
||||
//! @param dir The direction and length of the segment.
|
||||
//! @param p Planes that compose a convex ponvex polyhedron.
|
||||
//! @param numPlanes number of planes.
|
||||
//! @param[out] tfirst Proportion along the line segment where the line enters.
|
||||
//! @param[out] tlast Proportion along the line segment where the line exits.
|
||||
//! @param[out] iFirstPlane The plane where the line enters.
|
||||
//! @param[out] iLastPlane The plane where the line exits.
|
||||
//! @return True if intersects else false.
|
||||
bool IntersectSegmentPolyhedron(
|
||||
const Vector3& sa,
|
||||
const Vector3& dir,
|
||||
const Plane p[],
|
||||
int numPlanes,
|
||||
float& tfirst,
|
||||
float& tlast,
|
||||
int& iFirstPlane,
|
||||
int& iLastPlane);
|
||||
|
||||
//! Calculate the line segment closestPointSegment1<->closestPointSegment2 that is the shortest route between
|
||||
//! two segments segment1Start<->segment1End and segment2Start<->segment2End. Also calculate the values of segment1Proportion and segment2Proportion where
|
||||
//! closestPointSegment1 = segment1Start + (segment1Proportion * (segment1End - segment1Start))
|
||||
//! two segments segment1Start<->segment1End and segment2Start<->segment2End. Also calculate the values of segment1Proportion and
|
||||
//! segment2Proportion where closestPointSegment1 = segment1Start + (segment1Proportion * (segment1End - segment1Start))
|
||||
//! closestPointSegment2 = segment2Start + (segment2Proportion * (segment2End - segment2Start))
|
||||
//! If segments are parallel returns a solution.
|
||||
//! @param segment1Start Start of segment 1.
|
||||
//! @param segment1End End of segment 1.
|
||||
//! @param segment2Start Start of segment 2.
|
||||
//! @param segment2End End of segment 2.
|
||||
//! @param[out] segment1Proportion The proporition along segment 1 [0..1]
|
||||
//! @param[out] segment2Proportion The proporition along segment 2 [0..1]
|
||||
//! @param[out] closestPointSegment1 Closest point on segment 1.
|
||||
//! @param[out] closestPointSegment2 Closest point on segment 2.
|
||||
//! @param epsilon The minimum square distance where a line segment can be treated as a single point.
|
||||
void ClosestSegmentSegment(
|
||||
const Vector3& segment1Start, const Vector3& segment1End,
|
||||
const Vector3& segment2Start, const Vector3& segment2End,
|
||||
float& segment1Proportion, float& segment2Proportion,
|
||||
Vector3& closestPointSegment1, Vector3& closestPointSegment2,
|
||||
const Vector3& segment1Start,
|
||||
const Vector3& segment1End,
|
||||
const Vector3& segment2Start,
|
||||
const Vector3& segment2End,
|
||||
float& segment1Proportion,
|
||||
float& segment2Proportion,
|
||||
Vector3& closestPointSegment1,
|
||||
Vector3& closestPointSegment2,
|
||||
float epsilon = 1e-4f);
|
||||
|
||||
//! Calculate the line segment closestPointSegment1<->closestPointSegment2 that is the shortest route between
|
||||
//! two segments segment1Start<->segment1End and segment2Start<->segment2End.
|
||||
//! If segments are parallel returns a solution.
|
||||
//! @param segment1Start Start of segment 1.
|
||||
//! @param segment1End End of segment 1.
|
||||
//! @param segment2Start Start of segment 2.
|
||||
//! @param segment2End End of segment 2.
|
||||
//! @param[out] closestPointSegment1 Closest point on segment 1.
|
||||
//! @param[out] closestPointSegment2 Closest point on segment 2.
|
||||
//! @param epsilon The minimum square distance where a line segment can be treated as a single point.
|
||||
void ClosestSegmentSegment(
|
||||
const Vector3& segment1Start, const Vector3& segment1End,
|
||||
const Vector3& segment2Start, const Vector3& segment2End,
|
||||
Vector3& closestPointSegment1, Vector3& closestPointSegment2,
|
||||
const Vector3& segment1Start,
|
||||
const Vector3& segment1End,
|
||||
const Vector3& segment2Start,
|
||||
const Vector3& segment2End,
|
||||
Vector3& closestPointSegment1,
|
||||
Vector3& closestPointSegment2,
|
||||
float epsilon = 1e-4f);
|
||||
|
||||
//! Calculate the point (closestPointOnSegment) that is the closest point on
|
||||
//! segment segmentStart/segmentEnd to point. Also calculate the value of proportion where
|
||||
//! closestPointOnSegment = segmentStart + (proportion * (segmentEnd - segmentStart))
|
||||
//! @param point The point to test
|
||||
//! @param segmentStart The start of the segment
|
||||
//! @param segmentEnd The end of the segment
|
||||
//! @param[out] proportion The proportion of the segment L(t) = (end - start) * t
|
||||
//! @param[out] closestPointOnSegment The point along the line segment
|
||||
void ClosestPointSegment(
|
||||
const Vector3& point, const Vector3& segmentStart, const Vector3& segmentEnd,
|
||||
float& proportion, Vector3& closestPointOnSegment);
|
||||
}
|
||||
}
|
||||
const Vector3& point,
|
||||
const Vector3& segmentStart,
|
||||
const Vector3& segmentEnd,
|
||||
float& proportion,
|
||||
Vector3& closestPointOnSegment);
|
||||
} // namespace Intersect
|
||||
} // namespace AZ
|
||||
|
||||
#endif // AZCORE_MATH_SEGMENT_INTERSECTION_H
|
||||
#pragma once
|
||||
#include <AzCore/Math/IntersectSegment.inl>
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace Intersect
|
||||
{
|
||||
AZ_MATH_INLINE bool ClipRayWithAabb(const Aabb& aabb, Vector3& rayStart, Vector3& rayEnd, float& tClipStart, float& tClipEnd)
|
||||
{
|
||||
Vector3 startNormal;
|
||||
float tStart, tEnd;
|
||||
Vector3 dirLen = rayEnd - rayStart;
|
||||
if (IntersectRayAABB(rayStart, dirLen, dirLen.GetReciprocal(), aabb, tStart, tEnd, startNormal) != ISECT_RAY_AABB_NONE)
|
||||
{
|
||||
// clip the ray with the box
|
||||
if (tStart > 0.0f)
|
||||
{
|
||||
rayStart = rayStart + tStart * dirLen;
|
||||
tClipStart = tStart;
|
||||
}
|
||||
if (tEnd < 1.0f)
|
||||
{
|
||||
rayEnd = rayStart + tEnd * dirLen;
|
||||
tClipEnd = tEnd;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
AZ_MATH_INLINE SphereIsectTypes
|
||||
IntersectRaySphereOrigin(const Vector3& rayStart, const Vector3& rayDirNormalized, const float sphereRadius, float& t)
|
||||
{
|
||||
Vector3 m = rayStart;
|
||||
float b = m.Dot(rayDirNormalized);
|
||||
float c = m.Dot(m) - sphereRadius * sphereRadius;
|
||||
|
||||
// Exit if r's origin outside s (c > 0)and r pointing away from s (b > 0)
|
||||
if (c > 0.0f && b > 0.0f)
|
||||
{
|
||||
return ISECT_RAY_SPHERE_NONE;
|
||||
}
|
||||
float discr = b * b - c;
|
||||
// A negative discriminant corresponds to ray missing sphere
|
||||
if (discr < 0.0f)
|
||||
{
|
||||
return ISECT_RAY_SPHERE_NONE;
|
||||
}
|
||||
|
||||
// Ray now found to intersect sphere, compute smallest t value of intersection
|
||||
t = -b - Sqrt(discr);
|
||||
|
||||
// If t is negative, ray started inside sphere so clamp t to zero
|
||||
if (t < 0.0f)
|
||||
{
|
||||
// t = 0.0f;
|
||||
return ISECT_RAY_SPHERE_SA_INSIDE; // no hit if inside
|
||||
}
|
||||
// q = p + t * d;
|
||||
return ISECT_RAY_SPHERE_ISECT;
|
||||
}
|
||||
|
||||
AZ_MATH_INLINE SphereIsectTypes IntersectRaySphere(const Vector3& rayStart, const Vector3& rayDirNormalized, const Vector3& sphereCenter, const float sphereRadius, float& t)
|
||||
{
|
||||
return IntersectRaySphereOrigin(rayStart - sphereCenter, rayDirNormalized, sphereRadius, t);
|
||||
}
|
||||
|
||||
AZ_MATH_INLINE Vector3 LineToPointDistance(const Vector3& s1, const Vector3& s2, const Vector3& p, float& u)
|
||||
{
|
||||
const Vector3 s21 = s2 - s1;
|
||||
// we assume seg1 and seg2 are NOT coincident
|
||||
AZ_MATH_ASSERT(!s21.IsClose(Vector3(0.0f), 1e-4f), "OK we agreed that we will pass valid segments! (s1 != s2)");
|
||||
|
||||
u = LineToPointDistanceTime(s1, s21, p);
|
||||
|
||||
return s1 + u * s21;
|
||||
}
|
||||
|
||||
AZ_MATH_INLINE float LineToPointDistanceTime(const Vector3& s1, const Vector3& s21, const Vector3& p)
|
||||
{
|
||||
// so u = (p.x - s1.x)*(s2.x - s1.x) + (p.y - s1.y)*(s2.y - s1.y) + (p.z-s1.z)*(s2.z-s1.z) / |s2-s1|^2
|
||||
return s21.Dot(p - s1) / s21.Dot(s21);
|
||||
}
|
||||
|
||||
AZ_MATH_INLINE bool TestSegmentAABB(const Vector3& p0, const Vector3& p1, const Aabb& aabb)
|
||||
{
|
||||
Vector3 e = aabb.GetExtents();
|
||||
Vector3 d = p1 - p0;
|
||||
Vector3 m = p0 + p1 - aabb.GetMin() - aabb.GetMax();
|
||||
|
||||
return TestSegmentAABBOrigin(m, d, e);
|
||||
}
|
||||
} // namespace Intersect
|
||||
} // namespace AZ
|
||||
@@ -34,7 +34,9 @@ namespace AZ
|
||||
friend IAllocator;
|
||||
friend class AllocatorBase;
|
||||
friend class Debug::AllocationRecords;
|
||||
friend class AZ::Internal::EnvironmentVariableHolder<AllocatorManager>;
|
||||
template<typename T, typename... Args> friend constexpr auto AZStd::construct_at(T*, Args&&... args)
|
||||
->AZStd::enable_if_t<AZStd::is_void_v<AZStd::void_t<decltype(new (AZStd::declval<void*>()) T(AZStd::forward<Args>(args)...))>>, T*>;
|
||||
template<typename T> constexpr friend void AZStd::destroy_at(T*);
|
||||
|
||||
public:
|
||||
typedef AZStd::function<void (IAllocator* allocator, size_t /*byteSize*/, size_t /*alignment*/, int/* flags*/, const char* /*name*/, const char* /*fileName*/, int lineNum /*=0*/)> OutOfMemoryCBType;
|
||||
|
||||
@@ -251,16 +251,15 @@ namespace AZ
|
||||
class EnvironmentVariableHolder
|
||||
: public EnvironmentVariableHolderBase
|
||||
{
|
||||
void ConstructImpl(const AZStd::true_type& /* AZStd::has_trivial_constructor<T> */)
|
||||
{
|
||||
memset(&m_value, 0, sizeof(T));
|
||||
}
|
||||
|
||||
template<class... Args>
|
||||
void ConstructImpl(const AZStd::false_type& /* AZStd::has_trivial_constructor<T> */, Args&&... args)
|
||||
void ConstructImpl(Args&&... args)
|
||||
{
|
||||
// Construction of non-trivial types is left up to the type's constructor.
|
||||
new(&m_value) T(AZStd::forward<Args>(args)...);
|
||||
// Use std::launder to ensure that the compiler treats the T* reinterpret_cast as a new object
|
||||
#if __cpp_lib_launder
|
||||
AZStd::construct_at(std::launder(reinterpret_cast<T*>(&m_value)), AZStd::forward<Args>(args)...);
|
||||
#else
|
||||
AZStd::construct_at(reinterpret_cast<T*>(&m_value), AZStd::forward<Args>(args)...);
|
||||
#endif
|
||||
}
|
||||
static void DestructDispatchNoLock(EnvironmentVariableHolderBase *base, DestroyTarget selfDestruct)
|
||||
{
|
||||
@@ -274,10 +273,12 @@ namespace AZ
|
||||
AZ_Assert(self->m_isConstructed, "Variable is not constructed. Please check your logic and guard if needed!");
|
||||
self->m_isConstructed = false;
|
||||
self->m_moduleOwner = nullptr;
|
||||
if constexpr(!AZStd::is_trivially_destructible_v<T>)
|
||||
{
|
||||
reinterpret_cast<T*>(&self->m_value)->~T();
|
||||
}
|
||||
// Use std::launder to ensure that the compiler treats the T* reinterpret_cast as a new object
|
||||
#if __cpp_lib_launder
|
||||
AZStd::destroy_at(std::launder(reinterpret_cast<T*>(&self->m_value)));
|
||||
#else
|
||||
AZStd::destroy_at(reinterpret_cast<T*>(&self->m_value));
|
||||
#endif
|
||||
}
|
||||
public:
|
||||
EnvironmentVariableHolder(u32 guid, bool isOwnershipTransfer, Environment::AllocatorInterface* allocator)
|
||||
@@ -303,24 +304,13 @@ namespace AZ
|
||||
UnregisterAndDestroy(DestructDispatchNoLock, moduleRelease);
|
||||
}
|
||||
|
||||
void Construct()
|
||||
{
|
||||
AZStd::lock_guard<AZStd::spin_mutex> lock(m_mutex);
|
||||
if (!m_isConstructed)
|
||||
{
|
||||
ConstructImpl(AZStd::is_trivially_constructible<T>{});
|
||||
m_isConstructed = true;
|
||||
m_moduleOwner = Environment::GetModuleId();
|
||||
}
|
||||
}
|
||||
|
||||
template <class... Args>
|
||||
void Construct(Args&&... args)
|
||||
{
|
||||
AZStd::lock_guard<AZStd::spin_mutex> lock(m_mutex);
|
||||
if (!m_isConstructed)
|
||||
{
|
||||
ConstructImpl(typename AZStd::false_type(), AZStd::forward<Args>(args)...);
|
||||
ConstructImpl(AZStd::forward<Args>(args)...);
|
||||
m_isConstructed = true;
|
||||
m_moduleOwner = Environment::GetModuleId();
|
||||
}
|
||||
@@ -333,7 +323,7 @@ namespace AZ
|
||||
}
|
||||
|
||||
// variable storage
|
||||
typename AZStd::aligned_storage<sizeof(T), AZStd::alignment_of<T>::value>::type m_value;
|
||||
AZStd::aligned_storage_for_t<T> m_value;
|
||||
static int s_moduleUseCount;
|
||||
};
|
||||
|
||||
@@ -468,6 +458,11 @@ namespace AZ
|
||||
Get() = value;
|
||||
}
|
||||
|
||||
void Set(T&& value)
|
||||
{
|
||||
Get() = AZStd::move(value);
|
||||
}
|
||||
|
||||
explicit operator bool() const
|
||||
{
|
||||
return IsValid();
|
||||
|
||||
@@ -42,7 +42,10 @@ namespace AZ
|
||||
AZ_Assert(m_useCount > 0, "m_useCount is already 0!");
|
||||
if (m_useCount.fetch_sub(1) == 1)
|
||||
{
|
||||
AZ::NameDictionary::Instance().TryReleaseName(hash);
|
||||
if (AZ::NameDictionary::IsReady())
|
||||
{
|
||||
AZ::NameDictionary::Instance().TryReleaseName(hash);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,23 +21,18 @@ namespace AZ
|
||||
|
||||
namespace NameDictionaryInternal
|
||||
{
|
||||
static AZ::EnvironmentVariable<NameDictionary*> s_instance = nullptr;
|
||||
static AZ::EnvironmentVariable<NameDictionary> s_instance = nullptr;
|
||||
}
|
||||
|
||||
void NameDictionary::Create()
|
||||
{
|
||||
using namespace NameDictionaryInternal;
|
||||
|
||||
AZ_Assert(!s_instance || !s_instance.Get(), "NameDictionary already created!");
|
||||
AZ_Assert(!s_instance, "NameDictionary already created!");
|
||||
|
||||
if (!s_instance)
|
||||
{
|
||||
s_instance = AZ::Environment::CreateVariable<NameDictionary*>(NameDictionaryInstanceName);
|
||||
}
|
||||
|
||||
if (!s_instance.Get())
|
||||
{
|
||||
s_instance.Set(aznew NameDictionary());
|
||||
s_instance = AZ::Environment::CreateVariable<NameDictionary>(NameDictionaryInstanceName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,8 +41,7 @@ namespace AZ
|
||||
using namespace NameDictionaryInternal;
|
||||
|
||||
AZ_Assert(s_instance, "NameDictionary not created!");
|
||||
delete (*s_instance);
|
||||
*s_instance = nullptr;
|
||||
s_instance.Reset();
|
||||
}
|
||||
|
||||
bool NameDictionary::IsReady()
|
||||
@@ -56,10 +50,10 @@ namespace AZ
|
||||
|
||||
if (!s_instance)
|
||||
{
|
||||
s_instance = Environment::FindVariable<NameDictionary*>(NameDictionaryInstanceName);
|
||||
s_instance = Environment::FindVariable<NameDictionary>(NameDictionaryInstanceName);
|
||||
}
|
||||
|
||||
return s_instance && *s_instance;
|
||||
return s_instance.IsConstructed();
|
||||
}
|
||||
|
||||
NameDictionary& NameDictionary::Instance()
|
||||
@@ -68,12 +62,12 @@ namespace AZ
|
||||
|
||||
if (!s_instance)
|
||||
{
|
||||
s_instance = Environment::FindVariable<NameDictionary*>(NameDictionaryInstanceName);
|
||||
s_instance = Environment::FindVariable<NameDictionary>(NameDictionaryInstanceName);
|
||||
}
|
||||
|
||||
AZ_Assert(s_instance && *s_instance, "NameDictionary has not been initialized yet.");
|
||||
AZ_Assert(s_instance.IsConstructed(), "NameDictionary has not been initialized yet.");
|
||||
|
||||
return *(*s_instance);
|
||||
return *s_instance;
|
||||
}
|
||||
|
||||
NameDictionary::NameDictionary()
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
#include <AzCore/Memory/OSAllocator.h>
|
||||
#include <AzCore/Name/Name.h>
|
||||
|
||||
namespace MaterialEditor
|
||||
namespace MaterialEditor
|
||||
{
|
||||
class MaterialEditorCoreComponent;
|
||||
}
|
||||
@@ -34,14 +34,14 @@ namespace AZ
|
||||
{
|
||||
class NameData;
|
||||
};
|
||||
|
||||
|
||||
//! Maintains a list of unique strings for Name objects.
|
||||
//! The main benefit of the Name system is very fast string equality comparison, because every
|
||||
//! unique name has a unique ID. The NameDictionary's purpose is to guarantee name IDs do not
|
||||
//! unique name has a unique ID. The NameDictionary's purpose is to guarantee name IDs do not
|
||||
//! collide. It also saves memory by removing duplicate strings.
|
||||
//!
|
||||
//! Benchmarks have shown that creating a new Name object can be quite slow when the name doesn't
|
||||
//! already exist in the NameDictionary, but is comparable to creating an AZStd::string for names
|
||||
//! Benchmarks have shown that creating a new Name object can be quite slow when the name doesn't
|
||||
//! already exist in the NameDictionary, but is comparable to creating an AZStd::string for names
|
||||
//! that already exist.
|
||||
class NameDictionary final
|
||||
{
|
||||
@@ -51,7 +51,10 @@ namespace AZ
|
||||
friend Name;
|
||||
friend Internal::NameData;
|
||||
friend UnitTest::NameDictionaryTester;
|
||||
|
||||
template<typename T, typename... Args> friend constexpr auto AZStd::construct_at(T*, Args&&... args)
|
||||
-> AZStd::enable_if_t<AZStd::is_void_v<AZStd::void_t<decltype(new (AZStd::declval<void*>()) T(AZStd::forward<Args>(args)...))>>, T*>;
|
||||
template<typename T> constexpr friend void AZStd::destroy_at(T*);
|
||||
|
||||
public:
|
||||
|
||||
static void Create();
|
||||
@@ -62,7 +65,7 @@ namespace AZ
|
||||
|
||||
//! Makes a Name from the provided raw string. If an entry already exists in the dictionary, it is shared.
|
||||
//! Otherwise, it is added to the internal dictionary.
|
||||
//!
|
||||
//!
|
||||
//! @param name The name to resolve against the dictionary.
|
||||
//! @return A Name instance holding a dictionary entry associated with the provided raw string.
|
||||
Name MakeName(AZStd::string_view name);
|
||||
@@ -84,13 +87,13 @@ namespace AZ
|
||||
// Attempts to release the name from the dictionary, but checks to make sure
|
||||
// a reference wasn't taken by another thread.
|
||||
void TryReleaseName(Name::Hash hash);
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Calculates a hash for the provided name string.
|
||||
// Does not attempt to resolve hash collisions; that is handled elsewhere.
|
||||
Name::Hash CalcHash(AZStd::string_view name);
|
||||
|
||||
|
||||
AZStd::unordered_map<Name::Hash, Internal::NameData*> m_dictionary;
|
||||
mutable AZStd::shared_mutex m_sharedMutex;
|
||||
};
|
||||
|
||||
@@ -204,14 +204,14 @@ namespace AZ
|
||||
[[nodiscard]] virtual PreMergeEventHandler RegisterPreMergeEvent(const PreMergeEventCallback& callback) = 0;
|
||||
//! Register a function that will be called before a file is merged.
|
||||
//! @callback The function to call before a file is merged.
|
||||
[[nodiscard]] virtual PreMergeEventHandler RegisterPreMergeEvent (PreMergeEventCallback&& callback) = 0;
|
||||
[[nodiscard]] virtual PreMergeEventHandler RegisterPreMergeEvent(PreMergeEventCallback&& callback) = 0;
|
||||
|
||||
//! Register a function that will be called after a file is merged.
|
||||
//! @callback The function to call after a file is merged.
|
||||
[[nodiscard]] virtual PostMergeEventHandler RegisterPostMergeEvent(const PostMergeEventCallback& callback) = 0;
|
||||
//! Register a function that will be called after a file is merged.
|
||||
//! @callback The function to call after a file is merged.
|
||||
[[nodiscard]] virtual PostMergeEventHandler RegisterPostMergeEvent (PostMergeEventCallback&& callback) = 0;
|
||||
[[nodiscard]] virtual PostMergeEventHandler RegisterPostMergeEvent(PostMergeEventCallback&& callback) = 0;
|
||||
|
||||
//! Gets the boolean value at the provided path.
|
||||
//! @param result The target to write the result to.
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/Settings/SettingsRegistryVisitorUtils.h>
|
||||
|
||||
|
||||
namespace AZ::SettingsRegistryVisitorUtils
|
||||
{
|
||||
// Field Visitor implementation
|
||||
FieldVisitor::FieldVisitor() = default;
|
||||
FieldVisitor::FieldVisitor(VisitFieldType visitFieldType)
|
||||
: m_visitFieldType{ visitFieldType }
|
||||
{
|
||||
}
|
||||
|
||||
auto FieldVisitor::Traverse(AZStd::string_view path, AZStd::string_view valueName,
|
||||
VisitAction action, Type type) -> VisitResponse
|
||||
{
|
||||
// A default response skip prevents visiting grand children(depth 2 or lower)
|
||||
VisitResponse visitResponse = VisitResponse::Skip;
|
||||
if (action == VisitAction::Begin)
|
||||
{
|
||||
// Invoke FieldVisitor override if the root path has been set
|
||||
if (m_rootPath.has_value())
|
||||
{
|
||||
Visit(path, valueName, type);
|
||||
}
|
||||
// To make sure only the direct children are visited(depth 1)
|
||||
// set the root path once and set the VisitReponsoe
|
||||
// to Continue to recurse into is fields
|
||||
if (!m_rootPath.has_value())
|
||||
{
|
||||
bool visitableFieldType{};
|
||||
switch (m_visitFieldType)
|
||||
{
|
||||
case VisitFieldType::Array:
|
||||
visitableFieldType = type == Type::Array;
|
||||
break;
|
||||
case VisitFieldType::Object:
|
||||
visitableFieldType = type == Type::Object;
|
||||
break;
|
||||
case VisitFieldType::ArrayOrObject:
|
||||
visitableFieldType = type == Type::Array || type ==Type::Object;
|
||||
break;
|
||||
default:
|
||||
AZ_Error("FieldVisitor", false, "The field visitation type value is invalid");
|
||||
break;
|
||||
}
|
||||
|
||||
if (visitableFieldType)
|
||||
{
|
||||
m_rootPath = path;
|
||||
visitResponse = VisitResponse::Continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (action == VisitAction::Value)
|
||||
{
|
||||
// Invoke FieldVisitor override if the root path has been set
|
||||
if (m_rootPath.has_value())
|
||||
{
|
||||
Visit(path, valueName, type);
|
||||
}
|
||||
}
|
||||
else if (action == VisitAction::End)
|
||||
{
|
||||
// Reset m_rootPath back to null when the root path has finished being visited
|
||||
if (m_rootPath.has_value() && *m_rootPath == path)
|
||||
{
|
||||
m_rootPath = AZStd::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return visitResponse;
|
||||
}
|
||||
|
||||
// Array Visitor implementation
|
||||
ArrayVisitor::ArrayVisitor()
|
||||
: FieldVisitor(VisitFieldType::Array)
|
||||
{
|
||||
}
|
||||
|
||||
// Object Visitor implementation
|
||||
ObjectVisitor::ObjectVisitor()
|
||||
: FieldVisitor(VisitFieldType::Object)
|
||||
{
|
||||
}
|
||||
|
||||
// Generic VisitField Callback implemention
|
||||
template <typename BaseVisitor>
|
||||
bool VisitFieldCallback(AZ::SettingsRegistryInterface& settingsRegistry, const VisitorCallback& visitCallback, AZStd::string_view path)
|
||||
{
|
||||
struct VisitFieldVisitor
|
||||
: BaseVisitor
|
||||
{
|
||||
using BaseVisitor::Visit;
|
||||
VisitFieldVisitor(const VisitorCallback& visitCallback)
|
||||
: m_visitCallback{ visitCallback }
|
||||
{}
|
||||
|
||||
void Visit(AZStd::string_view path, AZStd::string_view fieldIndex, typename BaseVisitor::Type type) override
|
||||
{
|
||||
m_visitCallback(path, fieldIndex, type);
|
||||
}
|
||||
|
||||
const VisitorCallback& m_visitCallback;
|
||||
};
|
||||
|
||||
VisitFieldVisitor visitor{ visitCallback };
|
||||
return settingsRegistry.Visit(visitor, path);
|
||||
}
|
||||
|
||||
// VisitField implementation
|
||||
bool VisitField(AZ::SettingsRegistryInterface& settingsRegistry, const VisitorCallback& visitCallback, AZStd::string_view path)
|
||||
{
|
||||
return VisitFieldCallback<FieldVisitor>(settingsRegistry, visitCallback, path);
|
||||
}
|
||||
|
||||
// VisitArray implementation
|
||||
bool VisitArray(AZ::SettingsRegistryInterface& settingsRegistry, const VisitorCallback& visitCallback, AZStd::string_view path)
|
||||
{
|
||||
return VisitFieldCallback<ArrayVisitor>(settingsRegistry, visitCallback, path);
|
||||
}
|
||||
|
||||
// VisitObject implementation
|
||||
bool VisitObject(AZ::SettingsRegistryInterface& settingsRegistry, const VisitorCallback& visitCallback, AZStd::string_view path)
|
||||
{
|
||||
return VisitFieldCallback<ObjectVisitor>(settingsRegistry, visitCallback, path);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Settings/SettingsRegistry.h>
|
||||
|
||||
|
||||
namespace AZ::SettingsRegistryVisitorUtils
|
||||
{
|
||||
//! Interface for visiting the fields of an array or object
|
||||
//! To access the values, use the SettingsRegistryInterface Get/GetObject methods
|
||||
struct FieldVisitor
|
||||
: public AZ::SettingsRegistryInterface::Visitor
|
||||
{
|
||||
using VisitResponse = AZ::SettingsRegistryInterface::VisitResponse;
|
||||
using VisitAction = AZ::SettingsRegistryInterface::VisitAction;
|
||||
using Type = AZ::SettingsRegistryInterface::Type;
|
||||
|
||||
FieldVisitor();
|
||||
|
||||
// Bring the base class visitor functions into scope
|
||||
using AZ::SettingsRegistryInterface::Visitor::Visit;
|
||||
virtual void Visit(AZStd::string_view path, AZStd::string_view arrayIndex, Type type) = 0;
|
||||
|
||||
protected:
|
||||
// VisitFieldType is used for filtering the type of referenced by the root path
|
||||
enum class VisitFieldType
|
||||
{
|
||||
Array,
|
||||
Object,
|
||||
ArrayOrObject
|
||||
};
|
||||
FieldVisitor(const VisitFieldType visitFieldType);
|
||||
private:
|
||||
VisitResponse Traverse(AZStd::string_view path, AZStd::string_view valueName,
|
||||
VisitAction action, Type type) override;
|
||||
|
||||
VisitFieldType m_visitFieldType{ VisitFieldType::ArrayOrObject };
|
||||
AZStd::optional<AZ::SettingsRegistryInterface::FixedValueString> m_rootPath;
|
||||
};
|
||||
|
||||
//! Interface for visiting the fields of an array
|
||||
//! To access the values, use the SettingsRegistryInterface Get/GetObject methods
|
||||
struct ArrayVisitor
|
||||
: public FieldVisitor
|
||||
{
|
||||
ArrayVisitor();
|
||||
};
|
||||
|
||||
//! Interface for visiting the fields of an object
|
||||
//! To access the values, use the SettingsRegistryInterface Get/GetObject methods
|
||||
struct ObjectVisitor
|
||||
: public FieldVisitor
|
||||
{
|
||||
ObjectVisitor();
|
||||
};
|
||||
|
||||
//! Signature of callback funcition invoked when visiting an element of an array or object
|
||||
using VisitorCallback = AZStd::function<void(AZStd::string_view path, AZStd::string_view fieldName,
|
||||
AZ::SettingsRegistryInterface::Type)>;
|
||||
|
||||
//! Invokes the visitor callback for each element of either the array or object at @path
|
||||
//! If @path is not an array or object, then no elements are visited
|
||||
//! This function will not recurse into children of elements
|
||||
//! @visitCallback functor that is invoked for each array or object element found
|
||||
bool VisitField(AZ::SettingsRegistryInterface& settingsRegistry, const VisitorCallback& visitCallback, AZStd::string_view path);
|
||||
//! Invokes the visitor callback for each element of the array at @path
|
||||
//! If @path is not an array, then no elements are visited
|
||||
//! This function will not recurse into children of elements
|
||||
//! @visitCallback functor that is invoked for each array element found
|
||||
bool VisitArray(AZ::SettingsRegistryInterface& settingsRegistry, const VisitorCallback& visitCallback, AZStd::string_view path);
|
||||
//! Invokes the visitor callback for each element of the object at @path
|
||||
//! If @path is not an object, then no elements are visited
|
||||
//! This function will not recurse into children of elements
|
||||
//! @visitCallback functor that is invoked for each object element found
|
||||
bool VisitObject(AZ::SettingsRegistryInterface& settingsRegistry, const VisitorCallback& visitCallback, AZStd::string_view path);
|
||||
}
|
||||
@@ -282,6 +282,7 @@ set(FILES
|
||||
Math/Internal/VertexContainer.inl
|
||||
Math/InterpolationSample.h
|
||||
Math/IntersectPoint.h
|
||||
Math/IntersectSegment.inl
|
||||
Math/IntersectSegment.cpp
|
||||
Math/IntersectSegment.h
|
||||
Math/MathIntrinsics.h
|
||||
@@ -566,6 +567,8 @@ set(FILES
|
||||
Settings/SettingsRegistryMergeUtils.h
|
||||
Settings/SettingsRegistryScriptUtils.cpp
|
||||
Settings/SettingsRegistryScriptUtils.h
|
||||
Settings/SettingsRegistryVisitorUtils.cpp
|
||||
Settings/SettingsRegistryVisitorUtils.h
|
||||
State/HSM.cpp
|
||||
State/HSM.h
|
||||
Statistics/NamedRunningStatistic.h
|
||||
|
||||
@@ -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 <AzCore/std/allocator_stateless.h>
|
||||
#include <AzCore/Memory/OSAllocator.h>
|
||||
|
||||
namespace AZStd
|
||||
{
|
||||
stateless_allocator::stateless_allocator(const char* name)
|
||||
: m_name(name) {}
|
||||
|
||||
const char* stateless_allocator::get_name() const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
void stateless_allocator::set_name(const char* name)
|
||||
{
|
||||
m_name = name;
|
||||
}
|
||||
|
||||
auto stateless_allocator::allocate(size_type byteSize) -> pointer_type
|
||||
{
|
||||
return allocate(byteSize, AZ_DEFAULT_ALIGNMENT, 0);
|
||||
}
|
||||
|
||||
auto stateless_allocator::allocate(size_type byteSize, size_type alignment, int) -> pointer_type
|
||||
{
|
||||
pointer_type address = AZ_OS_MALLOC(byteSize, alignment);
|
||||
|
||||
if (address == nullptr)
|
||||
{
|
||||
AZ_Error("Memory", false, "stateless_allocator ran out of system memory!\n");
|
||||
}
|
||||
|
||||
return address;
|
||||
}
|
||||
|
||||
void stateless_allocator::deallocate(pointer_type ptr, size_type)
|
||||
{
|
||||
AZ_OS_FREE(ptr);
|
||||
}
|
||||
|
||||
void stateless_allocator::deallocate(pointer_type ptr, size_type, size_type)
|
||||
{
|
||||
AZ_OS_FREE(ptr);
|
||||
}
|
||||
|
||||
auto stateless_allocator::max_size() const -> size_type
|
||||
{
|
||||
return AZ_CORE_MAX_ALLOCATOR_SIZE;
|
||||
}
|
||||
|
||||
stateless_allocator stateless_allocator::select_on_container_copy_construction() const
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto stateless_allocator::resize(pointer_type, size_type) -> size_type
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool stateless_allocator::is_lock_free()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool stateless_allocator::is_stale_read_allowed()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool stateless_allocator::is_delayed_recycling()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// comparison operators
|
||||
bool operator==(const stateless_allocator&, const stateless_allocator&)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator!=(const stateless_allocator&, const stateless_allocator&)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/std/base.h>
|
||||
#include <AzCore/std/typetraits/integral_constant.h>
|
||||
#include <AzCore/RTTI/TypeInfoSimple.h>
|
||||
|
||||
namespace AZStd
|
||||
{
|
||||
class stateless_allocator
|
||||
{
|
||||
public:
|
||||
|
||||
AZ_TYPE_INFO(stateless_allocator, "{E4976C53-0B20-4F39-8D41-0A76F59A7D68}");
|
||||
|
||||
using value_type = uint8_t;
|
||||
using pointer_type = void*;
|
||||
using size_type = size_t;
|
||||
using difference_type = ptrdiff_t;
|
||||
using allow_memory_leaks = AZStd::true_type;
|
||||
|
||||
stateless_allocator(const char* name = "AZStd::stateless_allocator");
|
||||
stateless_allocator(const stateless_allocator& rhs) = default;
|
||||
|
||||
stateless_allocator& operator=(const stateless_allocator& rhs) = default;
|
||||
|
||||
const char* get_name() const;
|
||||
void set_name(const char* name);
|
||||
|
||||
pointer_type allocate(size_type byteSize);
|
||||
pointer_type allocate(size_type byteSize, size_type alignment, int flags = 0);
|
||||
void deallocate(pointer_type ptr, size_type alignment);
|
||||
void deallocate(pointer_type ptr, size_type byteSize, size_type alignment);
|
||||
|
||||
// max_size actually returns the true maximum size of a single allocation
|
||||
size_type max_size() const;
|
||||
|
||||
// Returns a copy of the allocator
|
||||
stateless_allocator select_on_container_copy_construction() const;
|
||||
|
||||
//! extensions
|
||||
size_type resize(pointer_type ptr, size_type newSize);
|
||||
|
||||
bool is_lock_free();
|
||||
bool is_stale_read_allowed();
|
||||
bool is_delayed_recycling();
|
||||
|
||||
private:
|
||||
const char* m_name;
|
||||
};
|
||||
|
||||
bool operator==(const stateless_allocator& left, const stateless_allocator& right);
|
||||
bool operator!=(const stateless_allocator& left, const stateless_allocator& right);
|
||||
}
|
||||
@@ -12,6 +12,8 @@ set(FILES
|
||||
allocator.h
|
||||
allocator_ref.h
|
||||
allocator_stack.h
|
||||
allocator_stateless.cpp
|
||||
allocator_stateless.h
|
||||
allocator_static.h
|
||||
allocator_traits.h
|
||||
any.h
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
namespace AZStd
|
||||
{
|
||||
// alias std::pointer_traits into the AZStd::namespace
|
||||
// alias std::pointer_traits into the AZStd::namespace
|
||||
using std::pointer_traits;
|
||||
|
||||
//! Bring the names of uninitialized_default_construct and
|
||||
@@ -229,7 +229,7 @@ namespace AZStd
|
||||
//! `new (declval<void*>()) T(declval<Args>()...)` is well-formed
|
||||
template <typename T, typename... Args>
|
||||
constexpr auto construct_at(T* ptr, Args&&... args)
|
||||
-> enable_if_t<is_void_v<void_t<decltype(new (declval<void*>()) T(AZStd::forward<Args>(args)...))>>, T*>
|
||||
-> enable_if_t<AZStd::is_void_v<AZStd::void_t<decltype(new (AZStd::declval<void*>()) T(AZStd::forward<Args>(args)...))>>, T*>
|
||||
{
|
||||
return ::new (ptr) T(AZStd::forward<Args>(args)...);
|
||||
}
|
||||
@@ -487,7 +487,7 @@ namespace AZStd
|
||||
{
|
||||
//! Implements the C++17 uninitialized_move function
|
||||
//! The functions accepts two input iterators and an output iterator
|
||||
//! It performs an AZStd::move on each in in the range of the input iterator
|
||||
//! It performs an AZStd::move on each in in the range of the input iterator
|
||||
//! and stores the result in location pointed by the output iterator
|
||||
template <typename InputIt, typename ForwardIt>
|
||||
ForwardIt uninitialized_move(InputIt first, InputIt last, ForwardIt result)
|
||||
|
||||
@@ -30,4 +30,49 @@ namespace AZStd
|
||||
using std::sqrt;
|
||||
using std::tan;
|
||||
using std::trunc;
|
||||
|
||||
} // namespace AZStd
|
||||
|
||||
// from c++20 standard
|
||||
namespace AZStd::Internal
|
||||
{
|
||||
template<typename T>
|
||||
constexpr T lerp(T a, T b, T t) noexcept
|
||||
{
|
||||
if ((a <= 0 && b >= 0) || (a >= 0 && b <= 0))
|
||||
{
|
||||
return t * b + (1 - t) * a;
|
||||
}
|
||||
if (t == 1)
|
||||
{
|
||||
return b;
|
||||
}
|
||||
const T x = a + t * (b - a);
|
||||
if ((t > 1) == (b > a))
|
||||
{
|
||||
return b < x ? x : b;
|
||||
}
|
||||
else
|
||||
{
|
||||
return x < b ? x : b;
|
||||
}
|
||||
}
|
||||
} // namespace AZStd::Internal
|
||||
|
||||
namespace AZStd
|
||||
{
|
||||
constexpr float lerp(float a, float b, float t) noexcept
|
||||
{
|
||||
return Internal::lerp(a, b, t);
|
||||
}
|
||||
|
||||
constexpr double lerp(double a, double b, double t) noexcept
|
||||
{
|
||||
return Internal::lerp(a, b, t);
|
||||
}
|
||||
|
||||
constexpr long double lerp(long double a, long double b, long double t) noexcept
|
||||
{
|
||||
return Internal::lerp(a, b, t);
|
||||
}
|
||||
} // namespace AZStd
|
||||
|
||||
+5
@@ -17,6 +17,11 @@ namespace AZ
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned int StackConverter::FromNative(StackFrame*, unsigned int, void*)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void SymbolStorage::LoadModuleData(const void*, unsigned int)
|
||||
{}
|
||||
|
||||
|
||||
@@ -78,6 +78,12 @@ StackRecorder::Record(StackFrame* frames, unsigned int maxNumOfFrames, unsigned
|
||||
return count;
|
||||
}
|
||||
|
||||
unsigned int StackConverter::FromNative([[maybe_unused]] StackFrame* frames, [[maybe_unused]] unsigned int maxNumOfFrames, [[maybe_unused]] void* nativeContext)
|
||||
{
|
||||
AZ_Assert(false, "StackConverter::FromNative() is not supported for UnixLike platform yet");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
SymbolStorage::DecodeFrames(const StackFrame* frames, unsigned int numFrames, StackLine* textLines)
|
||||
{
|
||||
|
||||
@@ -1048,9 +1048,9 @@ cleanup:
|
||||
unsigned int
|
||||
StackRecorder::Record(StackFrame* frames, unsigned int maxNumOfFrames, unsigned int suppressCount, void* nativeThread)
|
||||
{
|
||||
#if defined(AZ_ENABLE_DEBUG_TOOLS)
|
||||
unsigned int numFrames = 0;
|
||||
|
||||
#if defined(AZ_ENABLE_DEBUG_TOOLS)
|
||||
if (nativeThread == NULL)
|
||||
{
|
||||
++suppressCount; // Skip current call
|
||||
@@ -1079,9 +1079,8 @@ cleanup:
|
||||
|
||||
STACKFRAME64 sf;
|
||||
memset(&sf, 0, sizeof(STACKFRAME64));
|
||||
DWORD imageType;
|
||||
DWORD imageType = IMAGE_FILE_MACHINE_AMD64;
|
||||
|
||||
imageType = IMAGE_FILE_MACHINE_AMD64;
|
||||
sf.AddrPC.Offset = context.Rip;
|
||||
sf.AddrPC.Mode = AddrModeFlat;
|
||||
sf.AddrFrame.Offset = context.Rsp;
|
||||
@@ -1090,8 +1089,7 @@ cleanup:
|
||||
sf.AddrStack.Mode = AddrModeFlat;
|
||||
|
||||
EnterCriticalSection(&g_csDbgHelpDll);
|
||||
s32 frame = -(s32)suppressCount;
|
||||
for (; frame < (s32)maxNumOfFrames; ++frame)
|
||||
for (s32 frame = -static_cast<s32>(suppressCount); frame < static_cast<s32>(maxNumOfFrames); ++frame)
|
||||
{
|
||||
if (!g_StackWalk64(imageType, g_currentProcess, hThread, &sf, &context, 0, g_SymFunctionTableAccess64, g_SymGetModuleBase64, 0))
|
||||
{
|
||||
@@ -1111,15 +1109,68 @@ cleanup:
|
||||
}
|
||||
|
||||
LeaveCriticalSection(&g_csDbgHelpDll);
|
||||
}
|
||||
return numFrames;
|
||||
}
|
||||
#else
|
||||
(void)frames;
|
||||
(void)maxNumOfFrames;
|
||||
(void)suppressCount;
|
||||
(void)nativeThread;
|
||||
return 0;
|
||||
AZ_UNUSED(frames);
|
||||
AZ_UNUSED(maxNumOfFrames);
|
||||
AZ_UNUSED(suppressCount);
|
||||
AZ_UNUSED(nativeThread);
|
||||
#endif // AZ_ENABLE_DEBUG_TOOLS
|
||||
|
||||
return numFrames;
|
||||
}
|
||||
|
||||
unsigned int StackConverter::FromNative(StackFrame* frames, unsigned int maxNumOfFrames, void* nativeContext)
|
||||
{
|
||||
unsigned int numFrames = 0;
|
||||
|
||||
#if defined(AZ_ENABLE_DEBUG_TOOLS)
|
||||
if (!g_dbgHelpLoaded)
|
||||
{
|
||||
LoadDbgHelp();
|
||||
}
|
||||
|
||||
HANDLE hThread;
|
||||
DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &hThread, 0, false, DUPLICATE_SAME_ACCESS);
|
||||
|
||||
PCONTEXT nativeContextType = reinterpret_cast<PCONTEXT>(nativeContext);
|
||||
STACKFRAME64 sf;
|
||||
memset(&sf, 0, sizeof(STACKFRAME64));
|
||||
|
||||
DWORD imageType = IMAGE_FILE_MACHINE_AMD64;
|
||||
|
||||
sf.AddrPC.Offset = nativeContextType->Rip;
|
||||
sf.AddrPC.Mode = AddrModeFlat;
|
||||
sf.AddrFrame.Offset = nativeContextType->Rsp;
|
||||
sf.AddrFrame.Mode = AddrModeFlat;
|
||||
sf.AddrStack.Offset = nativeContextType->Rsp;
|
||||
sf.AddrStack.Mode = AddrModeFlat;
|
||||
|
||||
EnterCriticalSection(&g_csDbgHelpDll);
|
||||
for (unsigned int frame = 0; frame < maxNumOfFrames; ++frame)
|
||||
{
|
||||
if (!g_StackWalk64(imageType, g_currentProcess, hThread, &sf, nativeContext, 0, g_SymFunctionTableAccess64, g_SymGetModuleBase64, 0))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (sf.AddrPC.Offset == sf.AddrReturn.Offset)
|
||||
{
|
||||
// "StackWalk64-Endless-Callstack!"
|
||||
break;
|
||||
}
|
||||
|
||||
frames[numFrames++].m_programCounter = sf.AddrPC.Offset;
|
||||
}
|
||||
|
||||
LeaveCriticalSection(&g_csDbgHelpDll);
|
||||
#else
|
||||
AZ_UNUSED(frames);
|
||||
AZ_UNUSED(maxNumOfFrames);
|
||||
AZ_UNUSED(nativeContext);
|
||||
#endif
|
||||
|
||||
return numFrames;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -1413,7 +1413,7 @@ namespace UnitTest
|
||||
>;
|
||||
TYPED_TEST_CASE(HashedSetDifferentAllocatorFixture, SetTemplateConfigs);
|
||||
|
||||
#if GTEST_OS_SUPPORTS_DEATH_TEST
|
||||
#if GTEST_HAS_DEATH_TEST
|
||||
TYPED_TEST(HashedSetDifferentAllocatorFixture, InsertNodeHandleWithDifferentAllocatorsLogsTraceMessages)
|
||||
{
|
||||
using ContainerType = typename TypeParam::ContainerType;
|
||||
@@ -1435,7 +1435,7 @@ namespace UnitTest
|
||||
}
|
||||
}, ".*");
|
||||
}
|
||||
#endif // GTEST_OS_SUPPORTS_DEATH_TEST
|
||||
#endif // GTEST_HAS_DEATH_TEST
|
||||
|
||||
template<typename ContainerType>
|
||||
class HashedMapContainers
|
||||
@@ -1811,7 +1811,7 @@ namespace UnitTest
|
||||
>;
|
||||
TYPED_TEST_CASE(HashedMapDifferentAllocatorFixture, MapTemplateConfigs);
|
||||
|
||||
#if GTEST_OS_SUPPORTS_DEATH_TEST
|
||||
#if GTEST_HAS_DEATH_TEST
|
||||
TYPED_TEST(HashedMapDifferentAllocatorFixture, InsertNodeHandleWithDifferentAllocatorsLogsTraceMessages)
|
||||
{
|
||||
using ContainerType = typename TypeParam::ContainerType;
|
||||
@@ -1833,7 +1833,7 @@ namespace UnitTest
|
||||
}
|
||||
} , ".*");
|
||||
}
|
||||
#endif // GTEST_OS_SUPPORTS_DEATH_TEST
|
||||
#endif // GTEST_HAS_DEATH_TEST
|
||||
|
||||
namespace HashedContainerTransparentTestInternal
|
||||
{
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/UnitTest/TestTypes.h>
|
||||
#include <AzCore/std/math.h>
|
||||
|
||||
namespace UnitTest
|
||||
{
|
||||
template<typename T>
|
||||
class StdMathTest : public ::testing::Test
|
||||
{
|
||||
};
|
||||
|
||||
using MathTestConfigs = ::testing::Types<float, double, long double>;
|
||||
TYPED_TEST_CASE(StdMathTest, MathTestConfigs);
|
||||
|
||||
TYPED_TEST(StdMathTest, LerpOperations)
|
||||
{
|
||||
using AZStd::lerp;
|
||||
using ::testing::Eq;
|
||||
using T = TypeParam;
|
||||
|
||||
constexpr T maxNumber = AZStd::numeric_limits<T>::max();
|
||||
constexpr T eps = AZStd::numeric_limits<T>::epsilon();
|
||||
constexpr T a{ 42 };
|
||||
|
||||
// exactness: lerp(a,b,0)==a && lerp(a,b,1)==b
|
||||
EXPECT_THAT(lerp(eps, maxNumber, T(0)), Eq(eps));
|
||||
EXPECT_THAT(lerp(eps, maxNumber, T(1)), Eq(maxNumber));
|
||||
|
||||
// consistency: lerp(a,a,t)==a
|
||||
EXPECT_THAT(lerp(a, a, T(0.5)), Eq(a));
|
||||
EXPECT_THAT(lerp(eps, eps, T(0.5)), Eq(eps));
|
||||
|
||||
// a few generic tests taken from MathUtilTests.cpp
|
||||
EXPECT_EQ(T(2.5), lerp(T(2), T(4), T(0.25)));
|
||||
EXPECT_EQ(T(6.0), lerp(T(2), T(4), T(2.0)));
|
||||
EXPECT_EQ(T(3.5), lerp(T(2), T(4), T(0.75)));
|
||||
EXPECT_EQ(T(0.0), lerp(T(2), T(4), T(-1.0)));
|
||||
}
|
||||
} // namespace UnitTest
|
||||
@@ -1095,7 +1095,7 @@ namespace UnitTest
|
||||
>;
|
||||
TYPED_TEST_CASE(TreeSetDifferentAllocatorFixture, SetTemplateConfigs);
|
||||
|
||||
#if GTEST_OS_SUPPORTS_DEATH_TEST
|
||||
#if GTEST_HAS_DEATH_TEST
|
||||
TYPED_TEST(TreeSetDifferentAllocatorFixture, InsertNodeHandleWithDifferentAllocatorsLogsTraceMessages)
|
||||
{
|
||||
using ContainerType = typename TypeParam::ContainerType;
|
||||
@@ -1117,7 +1117,7 @@ namespace UnitTest
|
||||
}
|
||||
}, ".*");
|
||||
}
|
||||
#endif // GTEST_OS_SUPPORTS_DEATH_TEST
|
||||
#endif // GTEST_HAS_DEATH_TEST
|
||||
|
||||
TYPED_TEST(TreeSetDifferentAllocatorFixture, SwapMovesElementsWhenAllocatorsDiffer)
|
||||
{
|
||||
@@ -1516,7 +1516,7 @@ namespace UnitTest
|
||||
>;
|
||||
TYPED_TEST_CASE(TreeMapDifferentAllocatorFixture, MapTemplateConfigs);
|
||||
|
||||
#if GTEST_OS_SUPPORTS_DEATH_TEST
|
||||
#if GTEST_HAS_DEATH_TEST
|
||||
TYPED_TEST(TreeMapDifferentAllocatorFixture, InsertNodeHandleWithDifferentAllocatorsLogsTraceMessages)
|
||||
{
|
||||
using ContainerType = typename TypeParam::ContainerType;
|
||||
@@ -1538,7 +1538,7 @@ namespace UnitTest
|
||||
}
|
||||
}, ".*");
|
||||
}
|
||||
#endif // GTEST_OS_SUPPORTS_DEATH_TEST
|
||||
#endif // GTEST_HAS_DEATH_TEST
|
||||
|
||||
TYPED_TEST(TreeMapDifferentAllocatorFixture, SwapMovesElementsWhenAllocatorsDiffer)
|
||||
{
|
||||
|
||||
@@ -1595,7 +1595,7 @@ namespace UnitTest
|
||||
}
|
||||
};
|
||||
|
||||
#if GTEST_OS_SUPPORTS_DEATH_TEST
|
||||
#if GTEST_HAS_DEATH_TEST
|
||||
TEST_F(ThreadEventsDeathTest, UsingClientBus_AvoidsDeadlock)
|
||||
{
|
||||
EXPECT_EXIT(
|
||||
@@ -1608,5 +1608,5 @@ namespace UnitTest
|
||||
, ::testing::ExitedWithCode(0),".*");
|
||||
|
||||
}
|
||||
#endif // GTEST_OS_SUPPORTS_DEATH_TEST
|
||||
#endif // GTEST_HAS_DEATH_TEST
|
||||
}
|
||||
|
||||
@@ -736,7 +736,10 @@ namespace UnitTest
|
||||
auto& assetManager = AssetManager::Instance();
|
||||
|
||||
AssetBusCallbacks callbacks{};
|
||||
AZ_PUSH_DISABLE_WARNING(5233, "-Wunknown-warning-option") // Older versions of MSVC toolchain require to pass constexpr in the
|
||||
// capture. Newer versions issue unused warning
|
||||
callbacks.SetOnAssetReadyCallback([&, AssetNoRefB](const Asset<AssetData>&, AssetBusCallbacks&)
|
||||
AZ_POP_DISABLE_WARNING
|
||||
{
|
||||
// This callback should run inside the "main thread" dispatch events loop
|
||||
auto loadAsset = assetManager.GetAsset<AssetWithSerializedData>(AZ::Uuid(AssetNoRefB), AssetLoadBehavior::Default);
|
||||
|
||||
@@ -64,6 +64,91 @@ namespace JsonSerializationTests
|
||||
|
||||
};
|
||||
|
||||
class TestSerializedAssetTracker
|
||||
: public BaseJsonSerializerFixture
|
||||
{
|
||||
public:
|
||||
void SetUp() override
|
||||
{
|
||||
BaseJsonSerializerFixture::SetUp();
|
||||
|
||||
AZ::AllocatorInstance<AZ::PoolAllocator>::Create();
|
||||
AZ::AllocatorInstance<AZ::ThreadPoolAllocator>::Create();
|
||||
|
||||
// Set up the Job Manager with 1 thread so that the Asset Manager is able to load assets.
|
||||
AZ::JobManagerDesc jobDesc;
|
||||
AZ::JobManagerThreadDesc threadDesc;
|
||||
jobDesc.m_workerThreads.push_back(threadDesc);
|
||||
m_jobManager = aznew AZ::JobManager(jobDesc);
|
||||
m_jobContext = aznew AZ::JobContext(*m_jobManager);
|
||||
AZ::JobContext::SetGlobalContext(m_jobContext);
|
||||
|
||||
AZ::Data::AssetManager::Descriptor descriptor;
|
||||
AZ::Data::AssetManager::Create(descriptor);
|
||||
AZ::Data::AssetManager::Instance().RegisterHandler(&m_assetHandler, azrtti_typeid<TestAssetData>());
|
||||
|
||||
m_serializeContext->RegisterGenericType<AZ::Data::Asset<TestAssetData>>();
|
||||
m_jsonRegistrationContext->Serializer<AZ::Data::AssetJsonSerializer>()->HandlesType<AZ::Data::Asset>();
|
||||
}
|
||||
|
||||
void TearDown() override
|
||||
{
|
||||
m_jsonRegistrationContext->EnableRemoveReflection();
|
||||
m_jsonRegistrationContext->Serializer<AZ::Data::AssetJsonSerializer>()->HandlesType<AZ::Data::Asset>();
|
||||
m_jsonRegistrationContext->DisableRemoveReflection();
|
||||
|
||||
AZ::Data::AssetManager::Instance().UnregisterHandler(&m_assetHandler);
|
||||
AZ::Data::AssetManager::Destroy();
|
||||
|
||||
AZ::JobContext::SetGlobalContext(nullptr);
|
||||
delete m_jobContext;
|
||||
delete m_jobManager;
|
||||
|
||||
AZ::AllocatorInstance<AZ::ThreadPoolAllocator>::Destroy();
|
||||
AZ::AllocatorInstance<AZ::PoolAllocator>::Destroy();
|
||||
|
||||
BaseJsonSerializerFixture::TearDown();
|
||||
}
|
||||
|
||||
private:
|
||||
TestAssetHandler m_assetHandler;
|
||||
AZ::JobManager* m_jobManager{ nullptr };
|
||||
AZ::JobContext* m_jobContext{ nullptr };
|
||||
};
|
||||
|
||||
TEST_F(TestSerializedAssetTracker, AssetTracker_Callback_Works)
|
||||
{
|
||||
auto assetCallback = [](AZ::Data::Asset<AZ::Data::AssetData>& asset)
|
||||
{
|
||||
if (!asset.GetId().IsValid() && !asset.GetHint().empty())
|
||||
{
|
||||
if (asset.GetHint() == "test/path/foo.asset")
|
||||
{
|
||||
asset.SetHint("passed");
|
||||
}
|
||||
}
|
||||
};
|
||||
auto tracker = AZ::Data::SerializedAssetTracker{};
|
||||
tracker.SetAssetFixUp(assetCallback);
|
||||
|
||||
AZ::JsonDeserializerSettings settings;
|
||||
settings.m_metadata.Add(tracker);
|
||||
settings.m_registrationContext = this->m_jsonRegistrationContext.get();
|
||||
settings.m_serializeContext = this->m_serializeContext.get();
|
||||
|
||||
AZStd::string_view assetHintOnlyTestAsset = R"(
|
||||
{
|
||||
"assetHint" : "test/path/foo.asset"
|
||||
})";
|
||||
rapidjson::Document jsonDom;
|
||||
jsonDom.Parse(assetHintOnlyTestAsset.data());
|
||||
|
||||
AZ::Data::Asset<TestAssetData> instance;
|
||||
auto result = AZ::JsonSerialization::Load(instance, jsonDom, settings);
|
||||
EXPECT_NE(result.GetProcessing(), AZ::JsonSerializationResult::Processing::Halted);
|
||||
EXPECT_STREQ(instance.GetHint().c_str(), "passed");
|
||||
}
|
||||
|
||||
class AssetSerializerTestDescription final
|
||||
: public JsonSerializerConformityTestDescriptor<AZ::Data::Asset<TestAssetData>>
|
||||
{
|
||||
|
||||
@@ -288,6 +288,21 @@ namespace AZ
|
||||
AZStd::string completeCommand = console->AutoCompleteCommand("testVec3");
|
||||
AZ_TEST_ASSERT(completeCommand == "testVec3");
|
||||
}
|
||||
|
||||
// Duplicate names
|
||||
{
|
||||
// Register two cvars with the same name
|
||||
auto id = AZ::TypeId();
|
||||
auto flag = AZ::ConsoleFunctorFlags::Null;
|
||||
auto signature = AZ::ConsoleFunctor<void, false>::FunctorSignature();
|
||||
AZ::ConsoleFunctor<void, false> cvarOne(*console, "testAutoCompleteDuplication", "", flag, id, signature);
|
||||
AZ::ConsoleFunctor<void, false> cvarTwo(*console, "testAutoCompleteDuplication", "", flag, id, signature);
|
||||
|
||||
// Autocomplete given name expecting one match (not two)
|
||||
AZStd::vector<AZStd::string> matches;
|
||||
AZStd::string completeCommand = console->AutoCompleteCommand("testAutoCompleteD", &matches);
|
||||
AZ_TEST_ASSERT(matches.size() == 1 && completeCommand == "testAutoCompleteDuplication");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(ConsoleTests, ConsoleFunctor_FreeFunctorExecutionTest)
|
||||
|
||||
@@ -109,7 +109,10 @@ namespace AZ::Debug
|
||||
AZStd::thread threads[totalThreads];
|
||||
for (size_t threadIndex = 0; threadIndex < totalThreads; ++threadIndex)
|
||||
{
|
||||
AZ_PUSH_DISABLE_WARNING(5233, "-Wunknown-warning-option") // Older versions of MSVC toolchain require to pass constexpr in the
|
||||
// capture. Newer versions issue unused warning
|
||||
threads[threadIndex] = AZStd::thread([&startLogging, &messages]()
|
||||
AZ_POP_DISABLE_WARNING
|
||||
{
|
||||
while (!startLogging)
|
||||
{
|
||||
@@ -226,7 +229,10 @@ namespace AZ::Debug
|
||||
AZStd::thread threads[totalThreads];
|
||||
for (size_t threadIndex = 0; threadIndex < totalThreads; ++threadIndex)
|
||||
{
|
||||
AZ_PUSH_DISABLE_WARNING(5233, "-Wunknown-warning-option") // Older versions of MSVC toolchain require to pass constexpr in the
|
||||
// capture. Newer versions issue unused warning
|
||||
threads[threadIndex] = AZStd::thread([&startLogging, &message, &totalRecordsWritten]()
|
||||
AZ_POP_DISABLE_WARNING
|
||||
{
|
||||
AZ_UNUSED(message);
|
||||
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/UnitTest/TestTypes.h>
|
||||
|
||||
namespace UnitTest
|
||||
{
|
||||
class UnhandledExceptions
|
||||
: public ScopedAllocatorSetupFixture
|
||||
{
|
||||
|
||||
public:
|
||||
void causeAccessViolation()
|
||||
{
|
||||
int* someVariable = reinterpret_cast<int*>(0);
|
||||
*someVariable = 0;
|
||||
}
|
||||
};
|
||||
|
||||
#if GTEST_HAS_DEATH_TEST
|
||||
TEST_F(UnhandledExceptions, Handle)
|
||||
{
|
||||
EXPECT_DEATH(causeAccessViolation(), "");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -2088,7 +2088,7 @@ namespace UnitTest
|
||||
DisconnectNextHandlerByIdImpl multiHandler2;
|
||||
multiHandler2.BusConnect(DisconnectNextHandlerByIdImpl::firstBusAddress);
|
||||
multiHandler2.BusConnect(DisconnectNextHandlerByIdImpl::secondBusAddress);
|
||||
|
||||
|
||||
// Set the first handler m_nextHandler field to point to the second handler
|
||||
multiHandler1.m_nextHandler = &multiHandler2;
|
||||
|
||||
@@ -2807,7 +2807,7 @@ namespace UnitTest
|
||||
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(m_val % m_maxSleep));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DoConnect() override
|
||||
{
|
||||
MyEventGroupBus::Handler::BusConnect(m_id);
|
||||
@@ -2854,7 +2854,7 @@ namespace UnitTest
|
||||
}
|
||||
|
||||
MyEventGroupBus::Event(id, &MyEventGroupBus::Events::Calculate, i, i * 2, i << 4);
|
||||
|
||||
|
||||
LocklessConnectorBus::Event(id, &LocklessConnectorBus::Events::DoDisconnect);
|
||||
|
||||
bool failed = (AZStd::find_if(&sentinel[0], end, [](char s) { return s != 0; }) != end);
|
||||
@@ -2891,7 +2891,7 @@ namespace UnitTest
|
||||
{
|
||||
MyEventGroupImpl()
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
~MyEventGroupImpl() override
|
||||
@@ -3614,7 +3614,7 @@ namespace UnitTest
|
||||
{
|
||||
AZStd::this_thread::yield();
|
||||
}
|
||||
|
||||
|
||||
EXPECT_GE(AZStd::chrono::system_clock::now(), endTime);
|
||||
};
|
||||
AZStd::thread connectThread([&connectHandler, &waitHandler]()
|
||||
@@ -3813,7 +3813,7 @@ namespace UnitTest
|
||||
struct LastHandlerDisconnectHandler
|
||||
: public LastHandlerDisconnectBus::Handler
|
||||
{
|
||||
void OnEvent() override
|
||||
void OnEvent() override
|
||||
{
|
||||
++m_numOnEvents;
|
||||
BusDisconnect();
|
||||
@@ -3854,7 +3854,7 @@ namespace UnitTest
|
||||
struct DisconnectAssertHandler
|
||||
: public DisconnectAssertBus::Handler
|
||||
{
|
||||
|
||||
|
||||
};
|
||||
|
||||
TEST_F(EBus, HandlerDestroyedWithoutDisconnect_Asserts)
|
||||
@@ -3995,6 +3995,191 @@ namespace UnitTest
|
||||
|
||||
idTestRequest.Disconnect();
|
||||
}
|
||||
|
||||
// IsInDispatchThisThread
|
||||
struct IsInThreadDispatchRequests
|
||||
: AZ::EBusTraits
|
||||
{
|
||||
using MutexType = AZStd::recursive_mutex;
|
||||
};
|
||||
|
||||
using IsInThreadDispatchBus = AZ::EBus<IsInThreadDispatchRequests>;
|
||||
|
||||
class IsInThreadDispatchHandler
|
||||
: public IsInThreadDispatchBus::Handler
|
||||
{};
|
||||
|
||||
TEST_F(EBus, InvokingIsInThisThread_ReturnsSuccess_OnlyIfThreadIsInDispatch)
|
||||
{
|
||||
IsInThreadDispatchHandler handler;
|
||||
handler.BusConnect();
|
||||
|
||||
auto ThreadDispatcher = [](IsInThreadDispatchRequests*)
|
||||
{
|
||||
EXPECT_TRUE(IsInThreadDispatchBus::IsInDispatchThisThread());
|
||||
auto PerThreadBusDispatch = []()
|
||||
{
|
||||
EXPECT_FALSE(IsInThreadDispatchBus::IsInDispatchThisThread());
|
||||
};
|
||||
AZStd::array threads{ AZStd::thread(PerThreadBusDispatch), AZStd::thread(PerThreadBusDispatch) };
|
||||
for (AZStd::thread& thread : threads)
|
||||
{
|
||||
thread.join();
|
||||
}
|
||||
};
|
||||
|
||||
static constexpr size_t ThreadDispatcherIterations = 4;
|
||||
for (size_t iteration = 0; iteration < ThreadDispatcherIterations; ++iteration)
|
||||
{
|
||||
EXPECT_FALSE(IsInThreadDispatchBus::IsInDispatchThisThread());
|
||||
IsInThreadDispatchBus::Broadcast(ThreadDispatcher);
|
||||
EXPECT_FALSE(IsInThreadDispatchBus::IsInDispatchThisThread());
|
||||
}
|
||||
}
|
||||
|
||||
// Thread Dispatch Policy
|
||||
struct ThreadDispatchTestBusTraits
|
||||
: AZ::EBusTraits
|
||||
{
|
||||
using MutexType = AZStd::recursive_mutex;
|
||||
|
||||
struct PostThreadDispatchTestInvoker
|
||||
{
|
||||
~PostThreadDispatchTestInvoker();
|
||||
};
|
||||
|
||||
template <typename DispatchMutex>
|
||||
struct ThreadDispatchTestLockGuard
|
||||
{
|
||||
ThreadDispatchTestLockGuard(DispatchMutex& contextMutex)
|
||||
: m_lock{ contextMutex }
|
||||
{}
|
||||
ThreadDispatchTestLockGuard(DispatchMutex& contextMutex, AZStd::adopt_lock_t adopt_lock)
|
||||
: m_lock{ contextMutex, adopt_lock }
|
||||
{}
|
||||
ThreadDispatchTestLockGuard(const ThreadDispatchTestLockGuard&) = delete;
|
||||
ThreadDispatchTestLockGuard& operator=(const ThreadDispatchTestLockGuard&) = delete;
|
||||
private:
|
||||
PostThreadDispatchTestInvoker m_threadPolicyInvoker;
|
||||
using LockType = AZStd::conditional_t<LocklessDispatch, AZ::Internal::NullLockGuard<DispatchMutex>, AZStd::scoped_lock<DispatchMutex>>;
|
||||
LockType m_lock;
|
||||
};
|
||||
|
||||
template <typename DispatchMutex, bool IsLocklessDispatch>
|
||||
using DispatchLockGuard = ThreadDispatchTestLockGuard<DispatchMutex>;
|
||||
|
||||
static inline AZStd::atomic<int32_t> s_threadPostDispatchCalls;
|
||||
};
|
||||
|
||||
class ThreadDispatchTestRequests
|
||||
{
|
||||
public:
|
||||
virtual void FirstCall() = 0;
|
||||
virtual void SecondCall() = 0;
|
||||
virtual void ThirdCall() = 0;
|
||||
};
|
||||
|
||||
using ThreadDispatchTestBus = AZ::EBus<ThreadDispatchTestRequests, ThreadDispatchTestBusTraits>;
|
||||
|
||||
ThreadDispatchTestBusTraits::PostThreadDispatchTestInvoker::~PostThreadDispatchTestInvoker()
|
||||
{
|
||||
if (!ThreadDispatchTestBus::IsInDispatchThisThread())
|
||||
{
|
||||
++s_threadPostDispatchCalls;
|
||||
}
|
||||
}
|
||||
|
||||
class ThreadDispatchTestHandler
|
||||
: public ThreadDispatchTestBus::Handler
|
||||
{
|
||||
public:
|
||||
void Connect()
|
||||
{
|
||||
ThreadDispatchTestBus::Handler::BusConnect();
|
||||
}
|
||||
void Disconnect()
|
||||
{
|
||||
ThreadDispatchTestBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
void FirstCall() override
|
||||
{
|
||||
ThreadDispatchTestBus::Broadcast(&ThreadDispatchTestBus::Events::SecondCall);
|
||||
}
|
||||
void SecondCall() override
|
||||
{
|
||||
ThreadDispatchTestBus::Broadcast(&ThreadDispatchTestBus::Events::ThirdCall);
|
||||
}
|
||||
void ThirdCall() override
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
template <typename ParamType>
|
||||
class EBusParamFixture
|
||||
: public ScopedAllocatorSetupFixture
|
||||
, public ::testing::WithParamInterface<ParamType>
|
||||
{};
|
||||
|
||||
struct ThreadDispatchParams
|
||||
{
|
||||
size_t m_threadCount{};
|
||||
size_t m_handlerCount{};
|
||||
};
|
||||
|
||||
using ThreadDispatchParamFixture = EBusParamFixture<ThreadDispatchParams>;
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
ThreadDispatch,
|
||||
ThreadDispatchParamFixture,
|
||||
::testing::Values(
|
||||
ThreadDispatchParams{ 1, 1 },
|
||||
ThreadDispatchParams{ 2, 1 },
|
||||
ThreadDispatchParams{ 1, 2 },
|
||||
ThreadDispatchParams{ 2, 2 },
|
||||
ThreadDispatchParams{ 16, 8 }
|
||||
)
|
||||
);
|
||||
|
||||
TEST_P(ThreadDispatchParamFixture, CustomDispatchLockGuard_InvokesPostDispatchFunction_AfterThreadHasFinishedDispatch)
|
||||
{
|
||||
ThreadDispatchTestBusTraits::s_threadPostDispatchCalls = 0;
|
||||
ThreadDispatchParams threadDispatchParams = GetParam();
|
||||
AZStd::vector<AZStd::thread> testThreads;
|
||||
AZStd::vector<ThreadDispatchTestHandler> testHandlers(threadDispatchParams.m_handlerCount);
|
||||
for (ThreadDispatchTestHandler& testHandler : testHandlers)
|
||||
{
|
||||
testHandler.Connect();
|
||||
}
|
||||
|
||||
static constexpr size_t DispatchThreadCalls = 3;
|
||||
const size_t totalThreadDispatchCalls = threadDispatchParams.m_threadCount * DispatchThreadCalls;
|
||||
|
||||
auto DispatchThreadWorker = []()
|
||||
{
|
||||
ThreadDispatchTestBus::Broadcast(&ThreadDispatchTestBus::Events::FirstCall);
|
||||
ThreadDispatchTestBus::Broadcast(&ThreadDispatchTestBus::Events::SecondCall);
|
||||
ThreadDispatchTestBus::Broadcast(&ThreadDispatchTestBus::Events::ThirdCall);
|
||||
};
|
||||
|
||||
for (size_t threadIndex = 0; threadIndex < threadDispatchParams.m_threadCount; ++threadIndex)
|
||||
{
|
||||
testThreads.emplace_back(DispatchThreadWorker);
|
||||
}
|
||||
|
||||
for (AZStd::thread& thread : testThreads)
|
||||
{
|
||||
thread.join();
|
||||
}
|
||||
|
||||
for (ThreadDispatchTestHandler& testHandler : testHandlers)
|
||||
{
|
||||
testHandler.Disconnect();
|
||||
}
|
||||
|
||||
EXPECT_EQ(totalThreadDispatchCalls, ThreadDispatchTestBusTraits::s_threadPostDispatchCalls);
|
||||
ThreadDispatchTestBusTraits::s_threadPostDispatchCalls = 0;
|
||||
}
|
||||
} // namespace UnitTest
|
||||
|
||||
#if defined(HAVE_BENCHMARK)
|
||||
@@ -4370,7 +4555,7 @@ namespace Benchmark
|
||||
Bus::ExecuteQueuedEvents();
|
||||
}
|
||||
s_benchmarkEBusEnv<Bus>.Disconnect(state);
|
||||
|
||||
|
||||
}
|
||||
BUS_BENCHMARK_REGISTER_ALL(BM_EBus_ExecuteBroadcast);
|
||||
|
||||
|
||||
@@ -144,14 +144,13 @@ namespace UnitTest
|
||||
}
|
||||
};
|
||||
|
||||
#if GTEST_OS_SUPPORTS_DEATH_TEST
|
||||
// SPEC-2669: Disabled since it is causing hangs on Linux
|
||||
#if GTEST_HAS_DEATH_TEST
|
||||
TEST_F(AllocatorsTestFixtureLeakDetectionDeathTest_SKIPCODECOVERAGE, AllocatorLeak)
|
||||
{
|
||||
// testing that the TraceBusHook will fail on cause the test to die
|
||||
EXPECT_DEATH(TestAllocatorLeak(), "");
|
||||
}
|
||||
#endif // GTEST_OS_SUPPORTS_DEATH_TEST
|
||||
#endif // GTEST_HAS_DEATH_TEST
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Testing ScopedAllocatorSetupFixture. Testing that detects leaks
|
||||
|
||||
+18
@@ -597,7 +597,10 @@ namespace AZ::IO
|
||||
path.InitFromAbsolutePath(m_dummyFilepath);
|
||||
|
||||
request->CreateRead(nullptr, buffer.get(), fileSize, path, 0, fileSize);
|
||||
AZ_PUSH_DISABLE_WARNING(5233, "-Wunknown-warning-option") // Older versions of MSVC toolchain require to pass constexpr in the
|
||||
// capture. Newer versions issue unused warning
|
||||
auto callback = [&fileSize, this](const FileRequest& request)
|
||||
AZ_POP_DISABLE_WARNING
|
||||
{
|
||||
EXPECT_EQ(request.GetStatus(), AZ::IO::IStreamerTypes::RequestStatus::Completed);
|
||||
auto& readRequest = AZStd::get<AZ::IO::FileRequest::ReadData>(request.GetCommand());
|
||||
@@ -639,7 +642,10 @@ namespace AZ::IO
|
||||
path.InitFromAbsolutePath(m_dummyFilepath);
|
||||
|
||||
request->CreateRead(nullptr, buffer, unalignedSize + 4, path, unalignedOffset, unalignedSize);
|
||||
AZ_PUSH_DISABLE_WARNING(5233, "-Wunknown-warning-option") // Older versions of MSVC toolchain require to pass constexpr in the
|
||||
// capture. Newer versions issue unused warning
|
||||
auto callback = [unalignedOffset, unalignedSize, this](const FileRequest& request)
|
||||
AZ_POP_DISABLE_WARNING
|
||||
{
|
||||
EXPECT_EQ(request.GetStatus(), AZ::IO::IStreamerTypes::RequestStatus::Completed);
|
||||
auto& readRequest = AZStd::get<AZ::IO::FileRequest::ReadData>(request.GetCommand());
|
||||
@@ -784,7 +790,10 @@ namespace AZ::IO
|
||||
requests[i] = m_context->GetNewInternalRequest();
|
||||
|
||||
requests[i]->CreateRead(nullptr, buffers[i].get(), chunkSize, path, i * chunkSize, chunkSize);
|
||||
AZ_PUSH_DISABLE_WARNING(5233, "-Wunknown-warning-option") // Older versions of MSVC toolchain require to pass constexpr in the
|
||||
// capture. Newer versions issue unused warning
|
||||
auto callback = [chunkSize, i](const FileRequest& request)
|
||||
AZ_POP_DISABLE_WARNING
|
||||
{
|
||||
EXPECT_EQ(request.GetStatus(), AZ::IO::IStreamerTypes::RequestStatus::Completed);
|
||||
auto& readRequest = AZStd::get<AZ::IO::FileRequest::ReadData>(request.GetCommand());
|
||||
@@ -970,7 +979,10 @@ namespace AZ::IO
|
||||
i * chunkSize
|
||||
));
|
||||
|
||||
AZ_PUSH_DISABLE_WARNING(5233, "-Wunknown-warning-option") // Older versions of MSVC toolchain require to pass constexpr in the
|
||||
// capture. Newer versions issue unused warning
|
||||
auto callback = [numChunks, &numCallbacks, &waitForReads](FileRequestHandle request)
|
||||
AZ_POP_DISABLE_WARNING
|
||||
{
|
||||
IStreamer* streamer = Interface<IStreamer>::Get();
|
||||
if (streamer)
|
||||
@@ -1038,7 +1050,10 @@ namespace AZ::IO
|
||||
i * chunkSize
|
||||
));
|
||||
|
||||
AZ_PUSH_DISABLE_WARNING(5233, "-Wunknown-warning-option") // Older versions of MSVC toolchain require to pass constexpr in the
|
||||
// capture. Newer versions issue unused warning
|
||||
auto callback = [numChunks, &waitForReads, &waitForSingleRead, &numReadCallbacks]([[maybe_unused]] FileRequestHandle request)
|
||||
AZ_POP_DISABLE_WARNING
|
||||
{
|
||||
numReadCallbacks++;
|
||||
if (numReadCallbacks == 1)
|
||||
@@ -1059,7 +1074,10 @@ namespace AZ::IO
|
||||
for (size_t i = 0; i < numChunks; ++i)
|
||||
{
|
||||
cancels.push_back(m_streamer->Cancel(requests[numChunks - i - 1]));
|
||||
AZ_PUSH_DISABLE_WARNING(5233, "-Wunknown-warning-option") // Older versions of MSVC toolchain require to pass constexpr in the
|
||||
// capture. Newer versions issue unused warning
|
||||
auto callback = [&numCancelCallbacks, &waitForCancels, numChunks](FileRequestHandle request)
|
||||
AZ_POP_DISABLE_WARNING
|
||||
{
|
||||
auto result = Interface<IStreamer>::Get()->GetRequestStatus(request);
|
||||
EXPECT_EQ(result, IStreamerTypes::RequestStatus::Completed);
|
||||
|
||||
@@ -327,7 +327,7 @@ namespace JsonSerializationTests
|
||||
SerializerWithOneType::Unreflect(m_jsonRegistrationContext.get());
|
||||
}
|
||||
|
||||
#if GTEST_OS_SUPPORTS_DEATH_TEST
|
||||
#if GTEST_HAS_DEATH_TEST
|
||||
using JsonSerializationDeathTests = JsonRegistrationContextTests;
|
||||
TEST_F(JsonSerializationDeathTests, DoubleUnregisterSerializer_Asserts)
|
||||
{
|
||||
@@ -338,5 +338,6 @@ namespace JsonSerializationTests
|
||||
}, ".*"
|
||||
);
|
||||
}
|
||||
#endif // GTEST_OS_SUPPORTS_DEATH_TEST
|
||||
#endif // GTEST_HAS_DEATH_TEST
|
||||
|
||||
} //namespace JsonSerializationTests
|
||||
|
||||
@@ -0,0 +1,196 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include <AzCore/Settings/SettingsRegistryImpl.h>
|
||||
#include <AzCore/Settings/SettingsRegistryVisitorUtils.h>
|
||||
#include <AzCore/std/containers/fixed_vector.h>
|
||||
#include <AzCore/std/smart_ptr/unique_ptr.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
#include <AzCore/UnitTest/TestTypes.h>
|
||||
|
||||
namespace SettingsRegistryVisitorUtilsTests
|
||||
{
|
||||
struct VisitCallbackParams
|
||||
{
|
||||
AZStd::string_view m_inputJsonDocument;
|
||||
using VisitFieldFunction = bool(*)(AZ::SettingsRegistryInterface&,
|
||||
const AZ::SettingsRegistryVisitorUtils::VisitorCallback&,
|
||||
AZStd::string_view);
|
||||
|
||||
static inline constexpr size_t MaxFieldCount = 10;
|
||||
using ObjectFields = AZStd::fixed_vector<AZStd::pair<AZStd::string_view, AZStd::string_view>, MaxFieldCount>;
|
||||
using ArrayFields = AZStd::fixed_vector<AZStd::string_view, MaxFieldCount>;
|
||||
ObjectFields m_objectFields;
|
||||
ArrayFields m_arrayFields;
|
||||
};
|
||||
|
||||
template <typename VisitorParams>
|
||||
class SettingsRegistryVisitorUtilsParamFixture
|
||||
: public UnitTest::ScopedAllocatorSetupFixture
|
||||
, public ::testing::WithParamInterface<VisitorParams>
|
||||
{
|
||||
public:
|
||||
|
||||
void SetUp() override
|
||||
{
|
||||
m_registry = AZStd::make_unique<AZ::SettingsRegistryImpl>();
|
||||
}
|
||||
|
||||
void TearDown() override
|
||||
{
|
||||
m_registry.reset();
|
||||
}
|
||||
|
||||
AZStd::unique_ptr<AZ::SettingsRegistryImpl> m_registry;
|
||||
};
|
||||
|
||||
using SettingsRegistryVisitCallbackFixture = SettingsRegistryVisitorUtilsParamFixture<VisitCallbackParams>;
|
||||
|
||||
TEST_P(SettingsRegistryVisitCallbackFixture, VisitFunction_VisitFieldsOfArrayType_ReturnsFields)
|
||||
{
|
||||
const VisitCallbackParams& visitParams = GetParam();
|
||||
|
||||
ASSERT_TRUE(m_registry->MergeSettings(visitParams.m_inputJsonDocument, AZ::SettingsRegistryInterface::Format::JsonMergePatch));
|
||||
|
||||
AZStd::fixed_vector<AZStd::string, VisitCallbackParams::MaxFieldCount> testArrayFields;
|
||||
auto visitorCallback = [this, &testArrayFields](AZStd::string_view path, AZStd::string_view, AZ::SettingsRegistryInterface::Type)
|
||||
{
|
||||
AZStd::string fieldValue;
|
||||
EXPECT_TRUE(m_registry->Get(fieldValue, path));
|
||||
testArrayFields.emplace_back(AZStd::move(fieldValue));
|
||||
};
|
||||
|
||||
AZ::SettingsRegistryVisitorUtils::VisitField(*m_registry, visitorCallback, "/Test/Array");
|
||||
|
||||
const AZStd::fixed_vector<AZStd::string, VisitCallbackParams::MaxFieldCount> expectedFields{
|
||||
visitParams.m_arrayFields.begin(), visitParams.m_arrayFields.end() };
|
||||
EXPECT_THAT(testArrayFields, ::testing::ContainerEq(expectedFields));
|
||||
}
|
||||
|
||||
TEST_P(SettingsRegistryVisitCallbackFixture, VisitFunction_VisitFieldsOfObjectType_ReturnsFields)
|
||||
{
|
||||
const VisitCallbackParams& visitParams = GetParam();
|
||||
|
||||
ASSERT_TRUE(m_registry->MergeSettings(visitParams.m_inputJsonDocument, AZ::SettingsRegistryInterface::Format::JsonMergePatch));
|
||||
|
||||
AZStd::fixed_vector<AZStd::pair<AZStd::string, AZStd::string>, VisitCallbackParams::MaxFieldCount> testObjectFields;
|
||||
auto visitorCallback = [this, &testObjectFields](AZStd::string_view path, AZStd::string_view fieldName, AZ::SettingsRegistryInterface::Type)
|
||||
{
|
||||
AZStd::string fieldValue;
|
||||
EXPECT_TRUE(m_registry->Get(fieldValue, path));
|
||||
testObjectFields.emplace_back(fieldName, AZStd::move(fieldValue));
|
||||
};
|
||||
|
||||
AZ::SettingsRegistryVisitorUtils::VisitField(*m_registry, visitorCallback, "/Test/Object");
|
||||
|
||||
const AZStd::fixed_vector<AZStd::pair<AZStd::string, AZStd::string>, VisitCallbackParams::MaxFieldCount> expectedFields{
|
||||
visitParams.m_objectFields.begin(), visitParams.m_objectFields.end() };
|
||||
EXPECT_THAT(testObjectFields, ::testing::ContainerEq(expectedFields));
|
||||
}
|
||||
|
||||
TEST_P(SettingsRegistryVisitCallbackFixture, VisitFunction_VisitArrayOfArrayType_ReturnsFields)
|
||||
{
|
||||
const VisitCallbackParams& visitParams = GetParam();
|
||||
|
||||
ASSERT_TRUE(m_registry->MergeSettings(visitParams.m_inputJsonDocument, AZ::SettingsRegistryInterface::Format::JsonMergePatch));
|
||||
|
||||
AZStd::fixed_vector<AZStd::string, VisitCallbackParams::MaxFieldCount> testArrayFields;
|
||||
auto visitorCallback = [this, &testArrayFields](AZStd::string_view path, AZStd::string_view, AZ::SettingsRegistryInterface::Type)
|
||||
{
|
||||
AZStd::string fieldValue;
|
||||
EXPECT_TRUE(m_registry->Get(fieldValue, path));
|
||||
testArrayFields.emplace_back(AZStd::move(fieldValue));
|
||||
};
|
||||
|
||||
AZ::SettingsRegistryVisitorUtils::VisitArray(*m_registry, visitorCallback, "/Test/Array");
|
||||
|
||||
const AZStd::fixed_vector<AZStd::string, VisitCallbackParams::MaxFieldCount> expectedArrayFields{
|
||||
visitParams.m_arrayFields.begin(), visitParams.m_arrayFields.end() };
|
||||
EXPECT_THAT(testArrayFields, ::testing::ContainerEq(expectedArrayFields));
|
||||
}
|
||||
|
||||
TEST_P(SettingsRegistryVisitCallbackFixture, VisitFunction_VisitArrayOfObjectType_ReturnsEmpty)
|
||||
{
|
||||
const VisitCallbackParams& visitParams = GetParam();
|
||||
|
||||
ASSERT_TRUE(m_registry->MergeSettings(visitParams.m_inputJsonDocument, AZ::SettingsRegistryInterface::Format::JsonMergePatch));
|
||||
|
||||
AZStd::fixed_vector<AZStd::string, VisitCallbackParams::MaxFieldCount> testArrayFields;
|
||||
auto visitorCallback = [this, &testArrayFields](AZStd::string_view path, AZStd::string_view, AZ::SettingsRegistryInterface::Type)
|
||||
{
|
||||
AZStd::string fieldValue;
|
||||
EXPECT_TRUE(m_registry->Get(fieldValue, path));
|
||||
testArrayFields.emplace_back(AZStd::move(fieldValue));
|
||||
};
|
||||
|
||||
AZ::SettingsRegistryVisitorUtils::VisitArray(*m_registry, visitorCallback, "/Test/Object");
|
||||
|
||||
EXPECT_TRUE(testArrayFields.empty());
|
||||
}
|
||||
|
||||
TEST_P(SettingsRegistryVisitCallbackFixture, VisitFunction_VisitObjectOfArrayType_ReturnsEmpty)
|
||||
{
|
||||
const VisitCallbackParams& visitParams = GetParam();
|
||||
|
||||
ASSERT_TRUE(m_registry->MergeSettings(visitParams.m_inputJsonDocument, AZ::SettingsRegistryInterface::Format::JsonMergePatch));
|
||||
|
||||
AZStd::fixed_vector<AZStd::pair<AZStd::string, AZStd::string>, VisitCallbackParams::MaxFieldCount> testObjectFields;
|
||||
auto visitorCallback = [this, &testObjectFields](AZStd::string_view path, AZStd::string_view fieldName, AZ::SettingsRegistryInterface::Type)
|
||||
{
|
||||
AZStd::string fieldValue;
|
||||
EXPECT_TRUE(m_registry->Get(fieldValue, path));
|
||||
testObjectFields.emplace_back(fieldName, AZStd::move(fieldValue));
|
||||
};
|
||||
|
||||
AZ::SettingsRegistryVisitorUtils::VisitObject(*m_registry, visitorCallback, "/Test/Array");
|
||||
|
||||
EXPECT_TRUE(testObjectFields.empty());
|
||||
}
|
||||
|
||||
TEST_P(SettingsRegistryVisitCallbackFixture, VisitFunction_VisitObjectOfObjectType_ReturnsFields)
|
||||
{
|
||||
const VisitCallbackParams& visitParams = GetParam();
|
||||
|
||||
ASSERT_TRUE(m_registry->MergeSettings(visitParams.m_inputJsonDocument, AZ::SettingsRegistryInterface::Format::JsonMergePatch));
|
||||
|
||||
AZStd::fixed_vector<AZStd::pair<AZStd::string, AZStd::string>, VisitCallbackParams::MaxFieldCount> testObjectFields;
|
||||
auto visitorCallback = [this, &testObjectFields](AZStd::string_view path, AZStd::string_view fieldName, AZ::SettingsRegistryInterface::Type)
|
||||
{
|
||||
AZStd::string fieldValue;
|
||||
EXPECT_TRUE(m_registry->Get(fieldValue, path));
|
||||
testObjectFields.emplace_back(fieldName, AZStd::move(fieldValue));
|
||||
};
|
||||
|
||||
AZ::SettingsRegistryVisitorUtils::VisitObject(*m_registry, visitorCallback, "/Test/Object");
|
||||
|
||||
const AZStd::fixed_vector<AZStd::pair<AZStd::string, AZStd::string>, VisitCallbackParams::MaxFieldCount> expectedObjectFields{
|
||||
visitParams.m_objectFields.begin(), visitParams.m_objectFields.end() };
|
||||
EXPECT_THAT(testObjectFields, ::testing::ContainerEq(expectedObjectFields));
|
||||
}
|
||||
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
VisitField,
|
||||
SettingsRegistryVisitCallbackFixture,
|
||||
::testing::Values(
|
||||
VisitCallbackParams
|
||||
{
|
||||
R"({)" "\n"
|
||||
R"( "Test":)" "\n"
|
||||
R"( {)" "\n"
|
||||
R"( "Array": [ "Hello", "World" ],)" "\n"
|
||||
R"( "Object": { "Foo": "Hello", "Bar": "World"})" "\n"
|
||||
R"( })" "\n"
|
||||
R"(})" "\n",
|
||||
VisitCallbackParams::ObjectFields{{"Foo", "Hello"}, {"Bar", "World"}},
|
||||
VisitCallbackParams::ArrayFields{"Hello", "World"}
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -363,7 +363,10 @@ namespace AZ
|
||||
{
|
||||
constexpr AZStd::array visitTokens = { "Hello", "World", "", "More", "", "", "Tokens" };
|
||||
size_t visitIndex{};
|
||||
AZ_PUSH_DISABLE_WARNING(5233, "-Wunknown-warning-option") // Older versions of MSVC toolchain require to pass constexpr in the
|
||||
// capture. Newer versions issue unused warning
|
||||
auto visitor = [&visitIndex, &visitTokens](AZStd::string_view token)
|
||||
AZ_POP_DISABLE_WARNING
|
||||
{
|
||||
if (visitIndex > visitTokens.size())
|
||||
{
|
||||
@@ -389,7 +392,10 @@ namespace AZ
|
||||
{
|
||||
constexpr AZStd::array visitTokens = { "Hello", "World", "", "More", "", "", "Tokens" };
|
||||
size_t visitIndex = visitTokens.size() - 1;
|
||||
AZ_PUSH_DISABLE_WARNING(5233, "-Wunknown-warning-option") // Older versions of MSVC toolchain require to pass constexpr in the
|
||||
// capture. Newer versions issue unused warning
|
||||
auto visitor = [&visitIndex, &visitTokens](AZStd::string_view token)
|
||||
AZ_POP_DISABLE_WARNING
|
||||
{
|
||||
if (visitIndex > visitTokens.size())
|
||||
{
|
||||
|
||||
@@ -72,14 +72,16 @@ set(FILES
|
||||
Debug/AssetTracking.cpp
|
||||
Debug/LocalFileEventLoggerTests.cpp
|
||||
Debug/Trace.cpp
|
||||
Debug/UnhandledExceptions.cpp
|
||||
Name/NameJsonSerializerTests.cpp
|
||||
Name/NameTests.cpp
|
||||
RTTI/TypeSafeIntegralTests.cpp
|
||||
SettingsRegistryTests.cpp
|
||||
SettingsRegistryMergeUtilsTests.cpp
|
||||
Settings/CommandLineTests.cpp
|
||||
Settings/SettingsRegistryTests.cpp
|
||||
Settings/SettingsRegistryConsoleUtilsTests.cpp
|
||||
Settings/SettingsRegistryMergeUtilsTests.cpp
|
||||
Settings/SettingsRegistryScriptUtilsTests.cpp
|
||||
Settings/SettingsRegistryVisitorUtilsTests.cpp
|
||||
Streamer/BlockCacheTests.cpp
|
||||
Streamer/DedicatedCacheTests.cpp
|
||||
Streamer/FullDecompressorTests.cpp
|
||||
@@ -194,6 +196,7 @@ set(FILES
|
||||
AZStd/LockFreeQueues.cpp
|
||||
AZStd/LockFreeStacks.cpp
|
||||
AZStd/LockTests.cpp
|
||||
AZStd/Math.cpp
|
||||
AZStd/Numeric.cpp
|
||||
AZStd/Ordered.cpp
|
||||
AZStd/Optional.cpp
|
||||
|
||||
@@ -29,6 +29,10 @@ namespace AzFramework
|
||||
AZStd::vector<AZ::IO::Path> m_absoluteSourcePaths; //!< Where the gem's source path folder are located(as an absolute path)
|
||||
|
||||
static constexpr const char* GetGemAssetFolder() { return "Assets"; }
|
||||
static constexpr const char* GetGemRegistryFolder()
|
||||
{
|
||||
return "Registry";
|
||||
}
|
||||
};
|
||||
|
||||
//! Returns a list of GemInfo of all the gems that are active for the for the specified game project.
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <AzCore/RTTI/BehaviorContext.h>
|
||||
#include <AzCore/Serialization/EditContext.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/Settings/SettingsRegistry.h>
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
namespace AzFramework
|
||||
@@ -190,6 +191,25 @@ namespace AzFramework
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void InputSystemComponent::Activate()
|
||||
{
|
||||
const auto* settingsRegistry = AZ::SettingsRegistry::Get();
|
||||
if (settingsRegistry)
|
||||
{
|
||||
AZ::u64 value = 0;
|
||||
if (settingsRegistry->Get(value, "/O3DE/InputSystem/MouseMovementSampleRateHertz"))
|
||||
{
|
||||
m_mouseMovementSampleRateHertz = aznumeric_caster(value);
|
||||
}
|
||||
if (settingsRegistry->Get(value, "/O3DE/InputSystem/GamepadsEnabled"))
|
||||
{
|
||||
m_gamepadsEnabled = aznumeric_caster(value);
|
||||
}
|
||||
settingsRegistry->Get(m_keyboardEnabled, "/O3DE/InputSystem/KeyboardEnabled");
|
||||
settingsRegistry->Get(m_motionEnabled, "/O3DE/InputSystem/MotionEnabled");
|
||||
settingsRegistry->Get(m_mouseEnabled, "/O3DE/InputSystem/MouseEnabled");
|
||||
settingsRegistry->Get(m_touchEnabled, "/O3DE/InputSystem/TouchEnabled");
|
||||
settingsRegistry->Get(m_virtualKeyboardEnabled, "/O3DE/InputSystem/VirtualKeyboardEnabled");
|
||||
}
|
||||
|
||||
// Create all enabled input devices
|
||||
CreateEnabledInputDevices();
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/RTTI/RTTI.h>
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
|
||||
namespace AZ
|
||||
|
||||
@@ -1,234 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Math/Matrix3x3.h>
|
||||
#include <AzCore/Math/Vector3.h>
|
||||
#include <AzCore/Math/Aabb.h>
|
||||
|
||||
#include <AzFramework/Physics/WorldBody.h>
|
||||
#include <AzFramework/Physics/ShapeConfiguration.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
class ReflectContext;
|
||||
}
|
||||
|
||||
namespace Physics
|
||||
{
|
||||
class ShapeConfiguration;
|
||||
class World;
|
||||
class Shape;
|
||||
|
||||
/// Default values used for initializing RigidBodySettings.
|
||||
/// These can be modified by Physics Implementation gems. // O3DE_DEPRECATED(LY-114472) - DefaultRigidBodyConfiguration values are not shared across modules.
|
||||
// Use RigidBodyConfiguration default values.
|
||||
struct DefaultRigidBodyConfiguration
|
||||
{
|
||||
static float m_mass;
|
||||
static bool m_computeInertiaTensor;
|
||||
static float m_linearDamping;
|
||||
static float m_angularDamping;
|
||||
static float m_sleepMinEnergy;
|
||||
static float m_maxAngularVelocity;
|
||||
};
|
||||
|
||||
enum class MassComputeFlags : AZ::u8
|
||||
{
|
||||
NONE = 0,
|
||||
|
||||
//! Flags indicating whether a certain mass property should be auto-computed or not.
|
||||
COMPUTE_MASS = 1,
|
||||
COMPUTE_INERTIA = 1 << 1,
|
||||
COMPUTE_COM = 1 << 2,
|
||||
|
||||
//! If set, non-simulated shapes will also be included in the mass properties calculation.
|
||||
INCLUDE_ALL_SHAPES = 1 << 3,
|
||||
|
||||
DEFAULT = COMPUTE_COM | COMPUTE_INERTIA | COMPUTE_MASS
|
||||
};
|
||||
|
||||
class RigidBodyConfiguration
|
||||
: public WorldBodyConfiguration
|
||||
{
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(RigidBodyConfiguration, AZ::SystemAllocator, 0);
|
||||
AZ_RTTI(RigidBodyConfiguration, "{ACFA8900-8530-4744-AF00-AA533C868A8E}", WorldBodyConfiguration);
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
enum PropertyVisibility : AZ::u16
|
||||
{
|
||||
InitialVelocities = 1 << 0, ///< Whether the initial linear and angular velocities are visible.
|
||||
InertiaProperties = 1 << 1, ///< Whether the whole category of inertia properties (mass, compute inertia,
|
||||
///< inertia tensor etc) is visible.
|
||||
Damping = 1 << 2, ///< Whether linear and angular damping are visible.
|
||||
SleepOptions = 1 << 3, ///< Whether the sleep threshold and start asleep options are visible.
|
||||
Interpolation = 1 << 4, ///< Whether the interpolation option is visible.
|
||||
Gravity = 1 << 5, ///< Whether the effected by gravity option is visible.
|
||||
Kinematic = 1 << 6, ///< Whether the option to make the body kinematic is visible.
|
||||
ContinuousCollisionDetection = 1 << 7, ///< Whether the option to enable continuous collision detection is visible.
|
||||
MaxVelocities = 1 << 8 ///< Whether upper limits on velocities are visible.
|
||||
};
|
||||
|
||||
RigidBodyConfiguration() = default;
|
||||
RigidBodyConfiguration(const RigidBodyConfiguration& settings) = default;
|
||||
|
||||
// Visibility functions.
|
||||
AZ::Crc32 GetPropertyVisibility(PropertyVisibility property) const;
|
||||
void SetPropertyVisibility(PropertyVisibility property, bool isVisible);
|
||||
|
||||
AZ::Crc32 GetInitialVelocitiesVisibility() const;
|
||||
/// Returns whether the whole category of inertia settings (mass, inertia, center of mass offset etc) is visible.
|
||||
AZ::Crc32 GetInertiaSettingsVisibility() const;
|
||||
/// Returns whether the individual inertia tensor field is visible or is hidden because the compute inertia option is selected.
|
||||
AZ::Crc32 GetInertiaVisibility() const;
|
||||
/// Returns whether the mass field is visible or is hidden because compute mass option is selected.
|
||||
AZ::Crc32 GetMassVisibility() const;
|
||||
/// Returns whether the individual centre of mass offset field is visible or is hidden because compute CoM option is selected.
|
||||
AZ::Crc32 GetCoMVisibility() const;
|
||||
AZ::Crc32 GetDampingVisibility() const;
|
||||
AZ::Crc32 GetSleepOptionsVisibility() const;
|
||||
AZ::Crc32 GetInterpolationVisibility() const;
|
||||
AZ::Crc32 GetGravityVisibility() const;
|
||||
AZ::Crc32 GetKinematicVisibility() const;
|
||||
AZ::Crc32 GetCCDVisibility() const;
|
||||
AZ::Crc32 GetMaxVelocitiesVisibility() const;
|
||||
MassComputeFlags GetMassComputeFlags() const;
|
||||
void SetMassComputeFlags(MassComputeFlags flags);
|
||||
|
||||
bool IsCCDEnabled() const;
|
||||
|
||||
// Basic initial settings.
|
||||
AZ::Vector3 m_initialLinearVelocity = AZ::Vector3::CreateZero();
|
||||
AZ::Vector3 m_initialAngularVelocity = AZ::Vector3::CreateZero();
|
||||
AZ::Vector3 m_centerOfMassOffset = AZ::Vector3::CreateZero();
|
||||
|
||||
// Simulation parameters.
|
||||
float m_mass = DefaultRigidBodyConfiguration::m_mass;
|
||||
AZ::Matrix3x3 m_inertiaTensor = AZ::Matrix3x3::CreateIdentity();
|
||||
float m_linearDamping = DefaultRigidBodyConfiguration::m_linearDamping;
|
||||
float m_angularDamping = DefaultRigidBodyConfiguration::m_angularDamping;
|
||||
float m_sleepMinEnergy = DefaultRigidBodyConfiguration::m_sleepMinEnergy;
|
||||
float m_maxAngularVelocity = DefaultRigidBodyConfiguration::m_maxAngularVelocity;
|
||||
|
||||
// Visibility settings.
|
||||
AZ::u16 m_propertyVisibilityFlags = (std::numeric_limits<AZ::u16>::max)();
|
||||
|
||||
bool m_startAsleep = false;
|
||||
bool m_interpolateMotion = false;
|
||||
bool m_gravityEnabled = true;
|
||||
bool m_simulated = true;
|
||||
bool m_kinematic = false;
|
||||
bool m_ccdEnabled = false; ///< Whether continuous collision detection is enabled.
|
||||
float m_ccdMinAdvanceCoefficient = 0.15f; ///< Coefficient affecting how granularly time is subdivided in CCD.
|
||||
bool m_ccdFrictionEnabled = false; ///< Whether friction is applied when resolving CCD collisions.
|
||||
|
||||
bool m_computeCenterOfMass = true;
|
||||
bool m_computeInertiaTensor = true;
|
||||
bool m_computeMass = true;
|
||||
|
||||
//! If set, non-simulated shapes will also be included in the mass properties calculation.
|
||||
bool m_includeAllShapesInMassCalculation = false;
|
||||
};
|
||||
|
||||
/// Dynamic rigid body.
|
||||
class RigidBody
|
||||
: public WorldBody
|
||||
{
|
||||
public:
|
||||
|
||||
AZ_CLASS_ALLOCATOR(RigidBody, AZ::SystemAllocator, 0);
|
||||
AZ_RTTI(RigidBody, "{156E459F-7BB7-4B4E-ADA0-2130D96B7E80}", WorldBody);
|
||||
|
||||
public:
|
||||
RigidBody() = default;
|
||||
explicit RigidBody(const RigidBodyConfiguration& settings);
|
||||
|
||||
|
||||
virtual void AddShape(AZStd::shared_ptr<Shape> shape) = 0;
|
||||
virtual void RemoveShape(AZStd::shared_ptr<Shape> shape) = 0;
|
||||
virtual AZ::u32 GetShapeCount() { return 0; }
|
||||
virtual AZStd::shared_ptr<Shape> GetShape(AZ::u32 /*index*/) { return nullptr; }
|
||||
|
||||
virtual AZ::Vector3 GetCenterOfMassWorld() const = 0;
|
||||
virtual AZ::Vector3 GetCenterOfMassLocal() const = 0;
|
||||
|
||||
virtual AZ::Matrix3x3 GetInverseInertiaWorld() const = 0;
|
||||
virtual AZ::Matrix3x3 GetInverseInertiaLocal() const = 0;
|
||||
|
||||
virtual float GetMass() const = 0;
|
||||
virtual float GetInverseMass() const = 0;
|
||||
virtual void SetMass(float mass) = 0;
|
||||
virtual void SetCenterOfMassOffset(const AZ::Vector3& comOffset) = 0;
|
||||
|
||||
/// Retrieves the velocity at center of mass; only linear velocity, no rotational velocity contribution.
|
||||
virtual AZ::Vector3 GetLinearVelocity() const = 0;
|
||||
virtual void SetLinearVelocity(const AZ::Vector3& velocity) = 0;
|
||||
virtual AZ::Vector3 GetAngularVelocity() const = 0;
|
||||
virtual void SetAngularVelocity(const AZ::Vector3& angularVelocity) = 0;
|
||||
virtual AZ::Vector3 GetLinearVelocityAtWorldPoint(const AZ::Vector3& worldPoint) = 0;
|
||||
virtual void ApplyLinearImpulse(const AZ::Vector3& impulse) = 0;
|
||||
virtual void ApplyLinearImpulseAtWorldPoint(const AZ::Vector3& impulse, const AZ::Vector3& worldPoint) = 0;
|
||||
virtual void ApplyAngularImpulse(const AZ::Vector3& angularImpulse) = 0;
|
||||
|
||||
virtual float GetLinearDamping() const = 0;
|
||||
virtual void SetLinearDamping(float damping) = 0;
|
||||
virtual float GetAngularDamping() const = 0;
|
||||
virtual void SetAngularDamping(float damping) = 0;
|
||||
|
||||
virtual bool IsAwake() const = 0;
|
||||
virtual void ForceAsleep() = 0;
|
||||
virtual void ForceAwake() = 0;
|
||||
virtual float GetSleepThreshold() const = 0;
|
||||
virtual void SetSleepThreshold(float threshold) = 0;
|
||||
|
||||
virtual bool IsKinematic() const = 0;
|
||||
virtual void SetKinematic(bool kinematic) = 0;
|
||||
virtual void SetKinematicTarget(const AZ::Transform& targetPosition) = 0;
|
||||
|
||||
virtual bool IsGravityEnabled() const = 0;
|
||||
virtual void SetGravityEnabled(bool enabled) = 0;
|
||||
virtual void SetSimulationEnabled(bool enabled) = 0;
|
||||
virtual void SetCCDEnabled(bool enabled) = 0;
|
||||
|
||||
//! Recalculates mass, inertia and center of mass based on the flags passed.
|
||||
//! @param flags MassComputeFlags specifying which properties should be recomputed.
|
||||
//! @param centerOfMassOffsetOverride Optional override of the center of mass. Note: This parameter will be ignored if COMPUTE_COM is passed in flags.
|
||||
//! @param inertiaTensorOverride Optional override of the inertia. Note: This parameter will be ignored if COMPUTE_INERTIA is passed in flags.
|
||||
//! @param massOverride Optional override of the mass. Note: This parameter will be ignored if COMPUTE_MASS is passed in flags.
|
||||
virtual void UpdateMassProperties(MassComputeFlags flags = MassComputeFlags::DEFAULT,
|
||||
const AZ::Vector3* centerOfMassOffsetOverride = nullptr,
|
||||
const AZ::Matrix3x3* inertiaTensorOverride = nullptr,
|
||||
const float* massOverride = nullptr) = 0;
|
||||
};
|
||||
|
||||
/// Bitwise operators for MassComputeFlags
|
||||
inline MassComputeFlags operator|(MassComputeFlags lhs, MassComputeFlags rhs)
|
||||
{
|
||||
return aznumeric_cast<MassComputeFlags>(aznumeric_cast<AZ::u8>(lhs) | aznumeric_cast<AZ::u8>(rhs));
|
||||
}
|
||||
|
||||
inline MassComputeFlags operator&(MassComputeFlags lhs, MassComputeFlags rhs)
|
||||
{
|
||||
return aznumeric_cast<MassComputeFlags>(aznumeric_cast<AZ::u8>(lhs) & aznumeric_cast<AZ::u8>(rhs));
|
||||
}
|
||||
|
||||
/// Static rigid body.
|
||||
class RigidBodyStatic
|
||||
: public WorldBody
|
||||
{
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(RigidBodyStatic, AZ::SystemAllocator, 0);
|
||||
AZ_RTTI(RigidBodyStatic, "{13A677BB-7085-4EDB-BCC8-306548238692}", WorldBody);
|
||||
|
||||
virtual void AddShape(const AZStd::shared_ptr<Shape>& shape) = 0;
|
||||
virtual AZ::u32 GetShapeCount() { return 0; }
|
||||
virtual AZStd::shared_ptr<Shape> GetShape(AZ::u32 /*index*/) { return nullptr; }
|
||||
};
|
||||
} // namespace Physics
|
||||
@@ -89,9 +89,9 @@ namespace AzPhysics
|
||||
//! @param inertiaTensorOverride Optional override of the inertia. Note: This parameter will be ignored if COMPUTE_INERTIA is passed in flags.
|
||||
//! @param massOverride Optional override of the mass. Note: This parameter will be ignored if COMPUTE_MASS is passed in flags.
|
||||
virtual void UpdateMassProperties(MassComputeFlags flags = MassComputeFlags::DEFAULT,
|
||||
const AZ::Vector3* centerOfMassOffsetOverride = nullptr,
|
||||
const AZ::Matrix3x3* inertiaTensorOverride = nullptr,
|
||||
const float* massOverride = nullptr) = 0;
|
||||
const AZ::Vector3& centerOfMassOffsetOverride = AZ::Vector3::CreateZero(),
|
||||
const AZ::Matrix3x3& inertiaTensorOverride = AZ::Matrix3x3::CreateIdentity(),
|
||||
const float massOverride = 1.0f) = 0;
|
||||
};
|
||||
|
||||
} // namespace AzPhysics
|
||||
|
||||
@@ -22,6 +22,7 @@ namespace AzFramework
|
||||
->Field("terminationTime", &SessionConfig::m_terminationTime)
|
||||
->Field("creatorId", &SessionConfig::m_creatorId)
|
||||
->Field("sessionProperties", &SessionConfig::m_sessionProperties)
|
||||
->Field("matchmakingData", &SessionConfig::m_matchmakingData)
|
||||
->Field("sessionId", &SessionConfig::m_sessionId)
|
||||
->Field("sessionName", &SessionConfig::m_sessionName)
|
||||
->Field("dnsName", &SessionConfig::m_dnsName)
|
||||
@@ -46,6 +47,8 @@ namespace AzFramework
|
||||
"CreatorId", "A unique identifier for a player or entity creating the session.")
|
||||
->DataElement(AZ::Edit::UIHandlers::Default, &AzFramework::SessionConfig::m_sessionProperties,
|
||||
"SessionProperties", "A collection of custom properties for a session.")
|
||||
->DataElement(AZ::Edit::UIHandlers::Default, &AzFramework::SessionConfig::m_matchmakingData,
|
||||
"MatchmakingData", "The matchmaking process information that was used to create the session.")
|
||||
->DataElement(AZ::Edit::UIHandlers::Default, &AzFramework::SessionConfig::m_sessionId,
|
||||
"SessionId", "A unique identifier for the session.")
|
||||
->DataElement(AZ::Edit::UIHandlers::Default, &AzFramework::SessionConfig::m_sessionName,
|
||||
|
||||
@@ -35,6 +35,9 @@ namespace AzFramework
|
||||
|
||||
// A collection of custom properties for a session.
|
||||
AZStd::unordered_map<AZStd::string, AZStd::string> m_sessionProperties;
|
||||
|
||||
// The matchmaking process information that was used to create the session.
|
||||
AZStd::string m_matchmakingData;
|
||||
|
||||
// A unique identifier for the session.
|
||||
AZStd::string m_sessionId;
|
||||
|
||||
@@ -41,6 +41,11 @@ namespace AzFramework
|
||||
// OnDestroySessionBegin is fired at the beginning of session termination
|
||||
// @return The result of all OnDestroySessionBegin notifications
|
||||
virtual bool OnDestroySessionBegin() = 0;
|
||||
|
||||
// OnUpdateSessionBegin is fired at the beginning of session update
|
||||
// @param sessionConfig The properties to describe a session
|
||||
// @param updateReason The reason for session update
|
||||
virtual void OnUpdateSessionBegin(const SessionConfig& sessionConfig, const AZStd::string& updateReason) = 0;
|
||||
};
|
||||
using SessionNotificationBus = AZ::EBus<SessionNotifications>;
|
||||
} // namespace AzFramework
|
||||
|
||||
@@ -61,6 +61,10 @@ namespace AzFramework
|
||||
//! be deleted and the spawnable asset to be released. This call is automatically done when
|
||||
//! AssignRootSpawnable is called while a root spawnable is assigned.
|
||||
virtual void ReleaseRootSpawnable() = 0;
|
||||
//! Force processing all SpawnableEntitiesManager requests immediately
|
||||
//! This is useful when loading a different level while SpawnableEntitiesManager still has
|
||||
//! pending requests
|
||||
virtual void ProcessSpawnableQueue() = 0;
|
||||
};
|
||||
|
||||
using RootSpawnableInterface = AZ::Interface<RootSpawnableDefinition>;
|
||||
|
||||
@@ -45,8 +45,7 @@ namespace AzFramework
|
||||
|
||||
void SpawnableSystemComponent::OnTick(float /*deltaTime*/, AZ::ScriptTimePoint /*time*/)
|
||||
{
|
||||
m_entitiesManager.ProcessQueue(
|
||||
SpawnableEntitiesManager::CommandQueuePriority::High | SpawnableEntitiesManager::CommandQueuePriority::Regular);
|
||||
ProcessSpawnableQueue();
|
||||
RootSpawnableNotificationBus::ExecuteQueuedEvents();
|
||||
}
|
||||
|
||||
@@ -121,6 +120,12 @@ namespace AzFramework
|
||||
m_rootSpawnableId = AZ::Data::AssetId();
|
||||
}
|
||||
|
||||
void SpawnableSystemComponent::ProcessSpawnableQueue()
|
||||
{
|
||||
m_entitiesManager.ProcessQueue(
|
||||
SpawnableEntitiesManager::CommandQueuePriority::High | SpawnableEntitiesManager::CommandQueuePriority::Regular);
|
||||
}
|
||||
|
||||
void SpawnableSystemComponent::OnRootSpawnableAssigned([[maybe_unused]] AZ::Data::Asset<Spawnable> rootSpawnable,
|
||||
[[maybe_unused]] uint32_t generation)
|
||||
{
|
||||
@@ -161,6 +166,8 @@ namespace AzFramework
|
||||
|
||||
void SpawnableSystemComponent::Deactivate()
|
||||
{
|
||||
ProcessSpawnableQueue();
|
||||
|
||||
m_registryChangeHandler.Disconnect();
|
||||
|
||||
AZ::TickBus::Handler::BusDisconnect();
|
||||
|
||||
@@ -75,6 +75,7 @@ namespace AzFramework
|
||||
|
||||
uint64_t AssignRootSpawnable(AZ::Data::Asset<Spawnable> rootSpawnable) override;
|
||||
void ReleaseRootSpawnable() override;
|
||||
void ProcessSpawnableQueue() override;
|
||||
|
||||
//
|
||||
// RootSpawnbleNotificationBus
|
||||
|
||||
@@ -23,10 +23,12 @@ namespace AzFramework
|
||||
virtual ~XcbEventHandler() = default;
|
||||
|
||||
virtual void HandleXcbEvent(xcb_generic_event_t* event) = 0;
|
||||
|
||||
// ATTN This is used as a workaround for RAW Input events when using the Editor.
|
||||
virtual void PollSpecialEvents(){};
|
||||
};
|
||||
|
||||
class XcbEventHandlerBusTraits
|
||||
: public AZ::EBusTraits
|
||||
class XcbEventHandlerBusTraits : public AZ::EBusTraits
|
||||
{
|
||||
public:
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -0,0 +1,652 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/std/typetraits/integral_constant.h>
|
||||
#include <AzFramework/API/ApplicationAPI_Linux.h>
|
||||
#include <AzFramework/XcbConnectionManager.h>
|
||||
#include <AzFramework/XcbInputDeviceMouse.h>
|
||||
|
||||
namespace AzFramework
|
||||
{
|
||||
xcb_window_t GetSystemCursorFocusWindow()
|
||||
{
|
||||
void* systemCursorFocusWindow = nullptr;
|
||||
AzFramework::InputSystemCursorConstraintRequestBus::BroadcastResult(
|
||||
systemCursorFocusWindow, &AzFramework::InputSystemCursorConstraintRequests::GetSystemCursorConstraintWindow);
|
||||
|
||||
if (!systemCursorFocusWindow)
|
||||
{
|
||||
return XCB_NONE;
|
||||
}
|
||||
|
||||
// TODO Clang compile error because cast .... loses information. On GNU/Linux HWND is void* and on 64-bit
|
||||
// machines its obviously 64 bit but we receive the window id from m_renderOverlay.winId() which is xcb_window_t 32-bit.
|
||||
|
||||
return static_cast<xcb_window_t>(reinterpret_cast<uint64_t>(systemCursorFocusWindow));
|
||||
}
|
||||
|
||||
xcb_connection_t* XcbInputDeviceMouse::s_xcbConnection = nullptr;
|
||||
xcb_screen_t* XcbInputDeviceMouse::s_xcbScreen = nullptr;
|
||||
bool XcbInputDeviceMouse::m_xfixesInitialized = false;
|
||||
bool XcbInputDeviceMouse::m_xInputInitialized = false;
|
||||
|
||||
XcbInputDeviceMouse::XcbInputDeviceMouse(InputDeviceMouse& inputDevice)
|
||||
: InputDeviceMouse::Implementation(inputDevice)
|
||||
, m_systemCursorState(SystemCursorState::Unknown)
|
||||
, m_systemCursorPositionNormalized(0.5f, 0.5f)
|
||||
, m_prevConstraintWindow(XCB_NONE)
|
||||
, m_focusWindow(XCB_NONE)
|
||||
, m_cursorShown(true)
|
||||
{
|
||||
XcbEventHandlerBus::Handler::BusConnect();
|
||||
|
||||
SetSystemCursorState(SystemCursorState::Unknown);
|
||||
}
|
||||
|
||||
XcbInputDeviceMouse::~XcbInputDeviceMouse()
|
||||
{
|
||||
XcbEventHandlerBus::Handler::BusDisconnect();
|
||||
|
||||
SetSystemCursorState(SystemCursorState::Unknown);
|
||||
}
|
||||
|
||||
InputDeviceMouse::Implementation* XcbInputDeviceMouse::Create(InputDeviceMouse& inputDevice)
|
||||
{
|
||||
auto* interface = AzFramework::XcbConnectionManagerInterface::Get();
|
||||
if (!interface)
|
||||
{
|
||||
AZ_Warning("XcbInput", false, "XCB interface not available");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
s_xcbConnection = AzFramework::XcbConnectionManagerInterface::Get()->GetXcbConnection();
|
||||
if (!s_xcbConnection)
|
||||
{
|
||||
AZ_Warning("XcbInput", false, "XCB connection not available");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const xcb_setup_t* xcbSetup = xcb_get_setup(s_xcbConnection);
|
||||
s_xcbScreen = xcb_setup_roots_iterator(xcbSetup).data;
|
||||
if (!s_xcbScreen)
|
||||
{
|
||||
AZ_Warning("XcbInput", false, "XCB screen not available");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Initialize XFixes extension which we use to create pointer barriers.
|
||||
if (!InitializeXFixes())
|
||||
{
|
||||
AZ_Warning("XcbInput", false, "XCB XFixes initialization failed");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Initialize XInput extension which is used to get RAW Input events.
|
||||
if (!InitializeXInput())
|
||||
{
|
||||
AZ_Warning("XcbInput", false, "XCB XInput initialization failed");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return aznew XcbInputDeviceMouse(inputDevice);
|
||||
}
|
||||
|
||||
bool XcbInputDeviceMouse::IsConnected() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void XcbInputDeviceMouse::CreateBarriers(xcb_window_t window, bool create)
|
||||
{
|
||||
// Don't create any barriers if we are debugging. This will cause artifacts but better then
|
||||
// a confined cursor during debugging.
|
||||
if (AZ::Debug::Trace::IsDebuggerPresent())
|
||||
{
|
||||
AZ_Warning("XcbInput", false, "Debugger running. Barriers will not be created.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (create)
|
||||
{
|
||||
// Destroy barriers if they are active already.
|
||||
if (!m_activeBarriers.empty())
|
||||
{
|
||||
for (const auto& barrier : m_activeBarriers)
|
||||
{
|
||||
xcb_xfixes_delete_pointer_barrier_checked(s_xcbConnection, barrier.id);
|
||||
}
|
||||
|
||||
m_activeBarriers.clear();
|
||||
}
|
||||
|
||||
// Get window information.
|
||||
const XcbStdFreePtr<xcb_get_geometry_reply_t> xcbGeometryReply{ xcb_get_geometry_reply(
|
||||
s_xcbConnection, xcb_get_geometry(s_xcbConnection, window), NULL) };
|
||||
|
||||
if (!xcbGeometryReply)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const xcb_translate_coordinates_cookie_t translate_coord =
|
||||
xcb_translate_coordinates(s_xcbConnection, window, s_xcbScreen->root, 0, 0);
|
||||
|
||||
const XcbStdFreePtr<xcb_translate_coordinates_reply_t> xkbTranslateCoordReply{ xcb_translate_coordinates_reply(
|
||||
s_xcbConnection, translate_coord, NULL) };
|
||||
|
||||
if (!xkbTranslateCoordReply)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const int16_t x0 = xkbTranslateCoordReply->dst_x < 0 ? 0 : xkbTranslateCoordReply->dst_x;
|
||||
const int16_t y0 = xkbTranslateCoordReply->dst_y < 0 ? 0 : xkbTranslateCoordReply->dst_y;
|
||||
const int16_t x1 = xkbTranslateCoordReply->dst_x + xcbGeometryReply->width;
|
||||
const int16_t y1 = xkbTranslateCoordReply->dst_y + xcbGeometryReply->height;
|
||||
|
||||
// ATTN For whatever reason, when making an exact rectangle the pointer will escape the top right corner in some cases. Adding
|
||||
// an offset to the lines so that they cross each other prevents that.
|
||||
const int16_t offset = 30;
|
||||
|
||||
// Create the left barrier info.
|
||||
m_activeBarriers.push_back({ xcb_generate_id(s_xcbConnection), XCB_XFIXES_BARRIER_DIRECTIONS_POSITIVE_X, x0, Clamp(y0 - offset),
|
||||
x0, Clamp(y1 + offset) });
|
||||
|
||||
// Create the right barrier info.
|
||||
m_activeBarriers.push_back({ xcb_generate_id(s_xcbConnection), XCB_XFIXES_BARRIER_DIRECTIONS_NEGATIVE_X, x1, Clamp(y0 - offset),
|
||||
x1, Clamp(y1 + offset) });
|
||||
|
||||
// Create the top barrier info.
|
||||
m_activeBarriers.push_back({ xcb_generate_id(s_xcbConnection), XCB_XFIXES_BARRIER_DIRECTIONS_POSITIVE_Y, Clamp(x0 - offset), y0,
|
||||
Clamp(x1 + offset), y0 });
|
||||
|
||||
// Create the bottom barrier info.
|
||||
m_activeBarriers.push_back({ xcb_generate_id(s_xcbConnection), XCB_XFIXES_BARRIER_DIRECTIONS_NEGATIVE_Y, Clamp(x0 - offset), y1,
|
||||
Clamp(x1 + offset), y1 });
|
||||
|
||||
// Create the xfixes barriers.
|
||||
for (const auto& barrier : m_activeBarriers)
|
||||
{
|
||||
xcb_void_cookie_t cookie = xcb_xfixes_create_pointer_barrier_checked(
|
||||
s_xcbConnection, barrier.id, window, barrier.x0, barrier.y0, barrier.x1, barrier.y1, barrier.direction, 0, NULL);
|
||||
const XcbStdFreePtr<xcb_generic_error_t> xkbError{ xcb_request_check(s_xcbConnection, cookie) };
|
||||
|
||||
AZ_Warning(
|
||||
"XcbInput", !xkbError, "XFixes, failed to create barrier %d at (%d %d %d %d)", barrier.id, barrier.x0, barrier.y0,
|
||||
barrier.x1, barrier.y1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (const auto& barrier : m_activeBarriers)
|
||||
{
|
||||
xcb_xfixes_delete_pointer_barrier_checked(s_xcbConnection, barrier.id);
|
||||
}
|
||||
|
||||
m_activeBarriers.clear();
|
||||
}
|
||||
|
||||
xcb_flush(s_xcbConnection);
|
||||
}
|
||||
|
||||
bool XcbInputDeviceMouse::InitializeXFixes()
|
||||
{
|
||||
m_xfixesInitialized = false;
|
||||
|
||||
// We don't have to free query_extension_reply according to xcb documentation.
|
||||
const xcb_query_extension_reply_t* query_extension_reply = xcb_get_extension_data(s_xcbConnection, &xcb_xfixes_id);
|
||||
if (!query_extension_reply || !query_extension_reply->present)
|
||||
{
|
||||
return m_xfixesInitialized;
|
||||
}
|
||||
|
||||
const xcb_xfixes_query_version_cookie_t query_cookie = xcb_xfixes_query_version(s_xcbConnection, 5, 0);
|
||||
|
||||
xcb_generic_error_t* error = NULL;
|
||||
const XcbStdFreePtr<xcb_xfixes_query_version_reply_t> xkbQueryRequestReply{ xcb_xfixes_query_version_reply(
|
||||
s_xcbConnection, query_cookie, &error) };
|
||||
|
||||
if (!xkbQueryRequestReply || error)
|
||||
{
|
||||
if (error)
|
||||
{
|
||||
AZ_Warning("XcbInput", false, "Retrieving XFixes version failed : Error code %d", error->error_code);
|
||||
free(error);
|
||||
}
|
||||
return m_xfixesInitialized;
|
||||
}
|
||||
else if (xkbQueryRequestReply->major_version < 5)
|
||||
{
|
||||
AZ_Warning("XcbInput", false, "XFixes version fails the minimum version check (%d<5)", xkbQueryRequestReply->major_version);
|
||||
return m_xfixesInitialized;
|
||||
}
|
||||
|
||||
m_xfixesInitialized = true;
|
||||
|
||||
return m_xfixesInitialized;
|
||||
}
|
||||
|
||||
bool XcbInputDeviceMouse::InitializeXInput()
|
||||
{
|
||||
m_xInputInitialized = false;
|
||||
|
||||
// We don't have to free query_extension_reply according to xcb documentation.
|
||||
const xcb_query_extension_reply_t* query_extension_reply = xcb_get_extension_data(s_xcbConnection, &xcb_input_id);
|
||||
if (!query_extension_reply || !query_extension_reply->present)
|
||||
{
|
||||
return m_xInputInitialized;
|
||||
}
|
||||
|
||||
const xcb_input_xi_query_version_cookie_t query_version_cookie = xcb_input_xi_query_version(s_xcbConnection, 2, 2);
|
||||
|
||||
xcb_generic_error_t* error = NULL;
|
||||
const XcbStdFreePtr<xcb_input_xi_query_version_reply_t> xkbQueryRequestReply{ xcb_input_xi_query_version_reply(
|
||||
s_xcbConnection, query_version_cookie, &error) };
|
||||
|
||||
if (!xkbQueryRequestReply || error)
|
||||
{
|
||||
if (error)
|
||||
{
|
||||
AZ_Warning("XcbInput", false, "Retrieving XInput version failed : Error code %d", error->error_code);
|
||||
free(error);
|
||||
}
|
||||
return m_xInputInitialized;
|
||||
}
|
||||
else if (xkbQueryRequestReply->major_version < 2)
|
||||
{
|
||||
AZ_Warning("XcbInput", false, "XInput version fails the minimum version check (%d<5)", xkbQueryRequestReply->major_version);
|
||||
return m_xInputInitialized;
|
||||
}
|
||||
|
||||
m_xInputInitialized = true;
|
||||
|
||||
return m_xInputInitialized;
|
||||
}
|
||||
|
||||
void XcbInputDeviceMouse::SetEnableXInput(bool enable)
|
||||
{
|
||||
struct
|
||||
{
|
||||
xcb_input_event_mask_t head;
|
||||
int mask;
|
||||
} mask;
|
||||
|
||||
mask.head.deviceid = XCB_INPUT_DEVICE_ALL;
|
||||
mask.head.mask_len = 1;
|
||||
|
||||
if (enable)
|
||||
{
|
||||
mask.mask = XCB_INPUT_XI_EVENT_MASK_RAW_MOTION | XCB_INPUT_XI_EVENT_MASK_RAW_BUTTON_PRESS |
|
||||
XCB_INPUT_XI_EVENT_MASK_RAW_BUTTON_RELEASE | XCB_INPUT_XI_EVENT_MASK_MOTION | XCB_INPUT_XI_EVENT_MASK_BUTTON_PRESS |
|
||||
XCB_INPUT_XI_EVENT_MASK_BUTTON_RELEASE;
|
||||
}
|
||||
else
|
||||
{
|
||||
mask.mask = XCB_NONE;
|
||||
}
|
||||
|
||||
xcb_input_xi_select_events(s_xcbConnection, s_xcbScreen->root, 1, &mask.head);
|
||||
|
||||
xcb_flush(s_xcbConnection);
|
||||
}
|
||||
|
||||
void XcbInputDeviceMouse::SetSystemCursorState(SystemCursorState systemCursorState)
|
||||
{
|
||||
if (systemCursorState != m_systemCursorState)
|
||||
{
|
||||
m_systemCursorState = systemCursorState;
|
||||
|
||||
m_focusWindow = GetSystemCursorFocusWindow();
|
||||
|
||||
HandleCursorState(m_focusWindow, systemCursorState);
|
||||
}
|
||||
}
|
||||
|
||||
void XcbInputDeviceMouse::HandleCursorState(xcb_window_t window, SystemCursorState systemCursorState)
|
||||
{
|
||||
bool confined = false, cursorShown = true;
|
||||
switch (systemCursorState)
|
||||
{
|
||||
case SystemCursorState::ConstrainedAndHidden:
|
||||
{
|
||||
//!< Constrained to the application's main window and hidden
|
||||
confined = true;
|
||||
cursorShown = false;
|
||||
}
|
||||
break;
|
||||
case SystemCursorState::ConstrainedAndVisible:
|
||||
{
|
||||
//!< Constrained to the application's main window and visible
|
||||
confined = true;
|
||||
}
|
||||
break;
|
||||
case SystemCursorState::UnconstrainedAndHidden:
|
||||
{
|
||||
//!< Free to move outside the main window but hidden while inside
|
||||
cursorShown = false;
|
||||
}
|
||||
break;
|
||||
case SystemCursorState::UnconstrainedAndVisible:
|
||||
{
|
||||
//!< Free to move outside the application's main window and visible
|
||||
}
|
||||
case SystemCursorState::Unknown:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// ATTN GetSystemCursorFocusWindow when getting out of the play in editor will return XCB_NONE
|
||||
// We need however the window id to reset the cursor.
|
||||
if (XCB_NONE == window && (confined || cursorShown))
|
||||
{
|
||||
// Reuse the previous window to reset states.
|
||||
window = m_prevConstraintWindow;
|
||||
m_prevConstraintWindow = XCB_NONE;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Remember the window we used to modify cursor and barrier states.
|
||||
m_prevConstraintWindow = window;
|
||||
}
|
||||
|
||||
SetEnableXInput(!cursorShown);
|
||||
|
||||
CreateBarriers(window, confined);
|
||||
ShowCursor(window, cursorShown);
|
||||
}
|
||||
|
||||
SystemCursorState XcbInputDeviceMouse::GetSystemCursorState() const
|
||||
{
|
||||
return m_systemCursorState;
|
||||
}
|
||||
|
||||
void XcbInputDeviceMouse::SetSystemCursorPositionNormalizedInternal(xcb_window_t window, AZ::Vector2 positionNormalized)
|
||||
{
|
||||
// TODO Basically not done at all. Added only the basic functions needed.
|
||||
const XcbStdFreePtr<xcb_get_geometry_reply_t> xkbGeometryReply{ xcb_get_geometry_reply(
|
||||
s_xcbConnection, xcb_get_geometry(s_xcbConnection, window), NULL) };
|
||||
|
||||
if (!xkbGeometryReply)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const int16_t x = static_cast<int16_t>(positionNormalized.GetX() * xkbGeometryReply->width);
|
||||
const int16_t y = static_cast<int16_t>(positionNormalized.GetY() * xkbGeometryReply->height);
|
||||
|
||||
xcb_warp_pointer(s_xcbConnection, XCB_NONE, window, 0, 0, 0, 0, x, y);
|
||||
|
||||
xcb_flush(s_xcbConnection);
|
||||
}
|
||||
|
||||
void XcbInputDeviceMouse::SetSystemCursorPositionNormalized(AZ::Vector2 positionNormalized)
|
||||
{
|
||||
const xcb_window_t window = GetSystemCursorFocusWindow();
|
||||
if (XCB_NONE == window)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SetSystemCursorPositionNormalizedInternal(window, positionNormalized);
|
||||
}
|
||||
|
||||
AZ::Vector2 XcbInputDeviceMouse::GetSystemCursorPositionNormalizedInternal(xcb_window_t window) const
|
||||
{
|
||||
AZ::Vector2 position = AZ::Vector2::CreateZero();
|
||||
|
||||
const xcb_query_pointer_cookie_t pointer = xcb_query_pointer(s_xcbConnection, window);
|
||||
|
||||
const XcbStdFreePtr<xcb_query_pointer_reply_t> xkbQueryPointerReply{ xcb_query_pointer_reply(s_xcbConnection, pointer, NULL) };
|
||||
|
||||
if (!xkbQueryPointerReply)
|
||||
{
|
||||
return position;
|
||||
}
|
||||
|
||||
const XcbStdFreePtr<xcb_get_geometry_reply_t> xkbGeometryReply{ xcb_get_geometry_reply(
|
||||
s_xcbConnection, xcb_get_geometry(s_xcbConnection, window), NULL) };
|
||||
|
||||
if (!xkbGeometryReply)
|
||||
{
|
||||
return position;
|
||||
}
|
||||
|
||||
AZ_Assert(xkbGeometryReply->width != 0, "xkbGeometry response width must be non-zero. (%d)", xkbGeometryReply->width);
|
||||
const float normalizedCursorPostionX = static_cast<float>(xkbQueryPointerReply->win_x) / xkbGeometryReply->width;
|
||||
|
||||
AZ_Assert(xkbGeometryReply->height != 0, "xkbGeometry response height must be non-zero. (%d)", xkbGeometryReply->height);
|
||||
const float normalizedCursorPostionY = static_cast<float>(xkbQueryPointerReply->win_y) / xkbGeometryReply->height;
|
||||
|
||||
position = AZ::Vector2(normalizedCursorPostionX, normalizedCursorPostionY);
|
||||
|
||||
return position;
|
||||
}
|
||||
|
||||
AZ::Vector2 XcbInputDeviceMouse::GetSystemCursorPositionNormalized() const
|
||||
{
|
||||
const xcb_window_t window = GetSystemCursorFocusWindow();
|
||||
if (XCB_NONE == window)
|
||||
{
|
||||
return AZ::Vector2::CreateZero();
|
||||
}
|
||||
|
||||
return GetSystemCursorPositionNormalizedInternal(window);
|
||||
}
|
||||
|
||||
void XcbInputDeviceMouse::TickInputDevice()
|
||||
{
|
||||
ProcessRawEventQueues();
|
||||
}
|
||||
|
||||
void XcbInputDeviceMouse::ShowCursor(xcb_window_t window, bool show)
|
||||
{
|
||||
xcb_void_cookie_t cookie;
|
||||
if (show)
|
||||
{
|
||||
cookie = xcb_xfixes_show_cursor_checked(s_xcbConnection, window);
|
||||
}
|
||||
else
|
||||
{
|
||||
cookie = xcb_xfixes_hide_cursor_checked(s_xcbConnection, window);
|
||||
}
|
||||
|
||||
const XcbStdFreePtr<xcb_generic_error_t> xkbError{ xcb_request_check(s_xcbConnection, cookie) };
|
||||
|
||||
if (xkbError)
|
||||
{
|
||||
AZ_Warning("XcbInput", false, "ShowCursor failed: %d", xkbError->error_code);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// ATTN In the following part we will when cursor gets hidden store the position of the cursor in screen space
|
||||
// not window space. We use that to re-position when showing the cursor again. Is this the correct
|
||||
// behavior?
|
||||
|
||||
const bool cursorWasHidden = !m_cursorShown;
|
||||
m_cursorShown = show;
|
||||
if (!m_cursorShown)
|
||||
{
|
||||
m_cursorHiddenPosition = GetSystemCursorPositionNormalizedInternal(s_xcbScreen->root);
|
||||
|
||||
SetSystemCursorPositionNormalized(AZ::Vector2(0.5f, 0.5f));
|
||||
}
|
||||
else if (cursorWasHidden)
|
||||
{
|
||||
SetSystemCursorPositionNormalizedInternal(s_xcbScreen->root, m_cursorHiddenPosition);
|
||||
}
|
||||
|
||||
xcb_flush(s_xcbConnection);
|
||||
}
|
||||
|
||||
void XcbInputDeviceMouse::HandleButtonPressEvents(uint32_t detail, bool pressed)
|
||||
{
|
||||
bool isWheel;
|
||||
float wheelDirection;
|
||||
const auto* button = InputChannelFromMouseEvent(detail, isWheel, wheelDirection);
|
||||
if (button)
|
||||
{
|
||||
QueueRawButtonEvent(*button, pressed);
|
||||
}
|
||||
if (isWheel)
|
||||
{
|
||||
float axisValue = MAX_XI_WHEEL_SENSITIVITY * wheelDirection;
|
||||
QueueRawMovementEvent(InputDeviceMouse::Movement::Z, axisValue);
|
||||
}
|
||||
}
|
||||
|
||||
void XcbInputDeviceMouse::HandlePointerMotionEvents(const xcb_generic_event_t* event)
|
||||
{
|
||||
const xcb_input_motion_event_t* mouseMotionEvent = reinterpret_cast<const xcb_input_motion_event_t*>(event);
|
||||
|
||||
m_systemCursorPosition[0] = mouseMotionEvent->event_x;
|
||||
m_systemCursorPosition[1] = mouseMotionEvent->event_y;
|
||||
}
|
||||
|
||||
void XcbInputDeviceMouse::HandleRawInputEvents(const xcb_ge_generic_event_t* event)
|
||||
{
|
||||
const xcb_ge_generic_event_t* genericEvent = reinterpret_cast<const xcb_ge_generic_event_t*>(event);
|
||||
switch (genericEvent->event_type)
|
||||
{
|
||||
case XCB_INPUT_RAW_BUTTON_PRESS:
|
||||
{
|
||||
const xcb_input_raw_button_press_event_t* mouseButtonEvent =
|
||||
reinterpret_cast<const xcb_input_raw_button_press_event_t*>(event);
|
||||
HandleButtonPressEvents(mouseButtonEvent->detail, true);
|
||||
}
|
||||
break;
|
||||
case XCB_INPUT_RAW_BUTTON_RELEASE:
|
||||
{
|
||||
const xcb_input_raw_button_release_event_t* mouseButtonEvent =
|
||||
reinterpret_cast<const xcb_input_raw_button_release_event_t*>(event);
|
||||
HandleButtonPressEvents(mouseButtonEvent->detail, false);
|
||||
}
|
||||
break;
|
||||
case XCB_INPUT_RAW_MOTION:
|
||||
{
|
||||
const xcb_input_raw_motion_event_t* mouseMotionEvent = reinterpret_cast<const xcb_input_raw_motion_event_t*>(event);
|
||||
|
||||
int axisLen = xcb_input_raw_button_press_axisvalues_length(mouseMotionEvent);
|
||||
const xcb_input_fp3232_t* axisvalues = xcb_input_raw_button_press_axisvalues_raw(mouseMotionEvent);
|
||||
for (int i = 0; i < axisLen; ++i)
|
||||
{
|
||||
const float axisValue = fp3232ToFloat(axisvalues[i]);
|
||||
|
||||
switch (i)
|
||||
{
|
||||
case 0:
|
||||
QueueRawMovementEvent(InputDeviceMouse::Movement::X, axisValue);
|
||||
break;
|
||||
case 1:
|
||||
QueueRawMovementEvent(InputDeviceMouse::Movement::Y, axisValue);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void XcbInputDeviceMouse::PollSpecialEvents()
|
||||
{
|
||||
while (xcb_generic_event_t* genericEvent = xcb_poll_for_queued_event(s_xcbConnection))
|
||||
{
|
||||
// TODO Is the following correct? If we are showing the cursor, don't poll RAW Input events.
|
||||
switch (genericEvent->response_type & ~0x80)
|
||||
{
|
||||
case XCB_GE_GENERIC:
|
||||
{
|
||||
const xcb_ge_generic_event_t* geGenericEvent = reinterpret_cast<const xcb_ge_generic_event_t*>(genericEvent);
|
||||
|
||||
// Only handle raw inputs if we have focus.
|
||||
// Handle Raw Input events first.
|
||||
if ((geGenericEvent->event_type == XCB_INPUT_RAW_BUTTON_PRESS) ||
|
||||
(geGenericEvent->event_type == XCB_INPUT_RAW_BUTTON_RELEASE) ||
|
||||
(geGenericEvent->event_type == XCB_INPUT_RAW_MOTION))
|
||||
{
|
||||
HandleRawInputEvents(geGenericEvent);
|
||||
|
||||
free(genericEvent);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void XcbInputDeviceMouse::HandleXcbEvent(xcb_generic_event_t* event)
|
||||
{
|
||||
switch (event->response_type & ~0x80)
|
||||
{
|
||||
// QT5 is using by default XInput which means we do need to check for XCB_GE_GENERIC event to parse all mouse related events.
|
||||
case XCB_GE_GENERIC:
|
||||
{
|
||||
const xcb_ge_generic_event_t* genericEvent = reinterpret_cast<const xcb_ge_generic_event_t*>(event);
|
||||
|
||||
// Handling RAW Inputs here works in GameMode but not in Editor mode because QT is
|
||||
// not handling RAW input events and passing to.
|
||||
if (!m_cursorShown)
|
||||
{
|
||||
// Handle Raw Input events first.
|
||||
if ((genericEvent->event_type == XCB_INPUT_RAW_BUTTON_PRESS) ||
|
||||
(genericEvent->event_type == XCB_INPUT_RAW_BUTTON_RELEASE) || (genericEvent->event_type == XCB_INPUT_RAW_MOTION))
|
||||
{
|
||||
HandleRawInputEvents(genericEvent);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (genericEvent->event_type)
|
||||
{
|
||||
case XCB_INPUT_BUTTON_PRESS:
|
||||
{
|
||||
const xcb_input_button_press_event_t* mouseButtonEvent =
|
||||
reinterpret_cast<const xcb_input_button_press_event_t*>(genericEvent);
|
||||
HandleButtonPressEvents(mouseButtonEvent->detail, true);
|
||||
}
|
||||
break;
|
||||
case XCB_INPUT_BUTTON_RELEASE:
|
||||
{
|
||||
const xcb_input_button_release_event_t* mouseButtonEvent =
|
||||
reinterpret_cast<const xcb_input_button_release_event_t*>(genericEvent);
|
||||
HandleButtonPressEvents(mouseButtonEvent->detail, false);
|
||||
}
|
||||
break;
|
||||
case XCB_INPUT_MOTION:
|
||||
{
|
||||
HandlePointerMotionEvents(event);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case XCB_FOCUS_IN:
|
||||
{
|
||||
const xcb_focus_in_event_t* focusInEvent = reinterpret_cast<const xcb_focus_in_event_t*>(event);
|
||||
if (m_focusWindow != focusInEvent->event)
|
||||
{
|
||||
m_focusWindow = focusInEvent->event;
|
||||
HandleCursorState(m_focusWindow, m_systemCursorState);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case XCB_FOCUS_OUT:
|
||||
{
|
||||
const xcb_focus_out_event_t* focusOutEvent = reinterpret_cast<const xcb_focus_out_event_t*>(event);
|
||||
HandleCursorState(focusOutEvent->event, SystemCursorState::UnconstrainedAndVisible);
|
||||
|
||||
ProcessRawEventQueues();
|
||||
ResetInputChannelStates();
|
||||
|
||||
m_focusWindow = XCB_NONE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
} // namespace AzFramework
|
||||
@@ -0,0 +1,193 @@
|
||||
/*
|
||||
* 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/Input/Devices/Mouse/InputDeviceMouse.h>
|
||||
#include <AzFramework/XcbConnectionManager.h>
|
||||
#include <AzFramework/XcbEventHandler.h>
|
||||
#include <AzFramework/XcbInterface.h>
|
||||
|
||||
#include <xcb/xfixes.h>
|
||||
#include <xcb/xinput.h>
|
||||
|
||||
// The maximum number of raw input axis this mouse device supports.
|
||||
constexpr uint32_t MAX_XI_RAW_AXIS = 2;
|
||||
|
||||
// The sensitivity of the wheel.
|
||||
constexpr float MAX_XI_WHEEL_SENSITIVITY = 140.0f;
|
||||
|
||||
namespace AzFramework
|
||||
{
|
||||
class XcbInputDeviceMouse
|
||||
: public InputDeviceMouse::Implementation
|
||||
, public XcbEventHandlerBus::Handler
|
||||
{
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(XcbInputDeviceMouse, AZ::SystemAllocator, 0);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//! Constructor
|
||||
//! \param[in] inputDevice Reference to the input device being implemented
|
||||
XcbInputDeviceMouse(InputDeviceMouse& inputDevice);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//! Destructor
|
||||
virtual ~XcbInputDeviceMouse();
|
||||
|
||||
static XcbInputDeviceMouse::Implementation* Create(InputDeviceMouse& inputDevice);
|
||||
|
||||
protected:
|
||||
////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//! \ref AzFramework::InputDeviceMouse::Implementation::IsConnected
|
||||
bool IsConnected() const override;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//! \ref AzFramework::InputDeviceMouse::Implementation::SetSystemCursorState
|
||||
void SetSystemCursorState(SystemCursorState systemCursorState) override;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//! \ref AzFramework::InputDeviceMouse::Implementation::GetSystemCursorState
|
||||
SystemCursorState GetSystemCursorState() const override;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//! \ref AzFramework::InputDeviceMouse::Implementation::SetSystemCursorPositionNormalized
|
||||
void SetSystemCursorPositionNormalized(AZ::Vector2 positionNormalized) override;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//! \ref AzFramework::InputDeviceMouse::Implementation::GetSystemCursorPositionNormalized
|
||||
AZ::Vector2 GetSystemCursorPositionNormalized() const override;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//! \ref AzFramework::InputDeviceMouse::Implementation::TickInputDevice
|
||||
void TickInputDevice() override;
|
||||
|
||||
//! This method is called by the Editor to accommodate some events with the Editor. Never called in Game mode.
|
||||
void PollSpecialEvents() override;
|
||||
|
||||
//! Handle X11 events.
|
||||
void HandleXcbEvent(xcb_generic_event_t* event) override;
|
||||
|
||||
//! Initialize XFixes extension. Used for barriers.
|
||||
static bool InitializeXFixes();
|
||||
|
||||
//! Initialize XInput extension. Used for raw input during confinement and showing/hiding the cursor.
|
||||
static bool InitializeXInput();
|
||||
|
||||
//! Enables/Disables XInput Raw Input events.
|
||||
void SetEnableXInput(bool enable);
|
||||
|
||||
//! Create barriers.
|
||||
void CreateBarriers(xcb_window_t window, bool create);
|
||||
|
||||
//! Helper function.
|
||||
void SystemCursorStateToLogic(SystemCursorState systemCursorState, bool& confined, bool& cursorShown);
|
||||
|
||||
//! Shows/Hides the cursor.
|
||||
void ShowCursor(xcb_window_t window, bool show);
|
||||
|
||||
//! Get the normalized cursor position. The coordinates returned are relative to the specified window.
|
||||
AZ::Vector2 GetSystemCursorPositionNormalizedInternal(xcb_window_t window) const;
|
||||
|
||||
//! Set the normalized cursor position. The normalized position will be relative to the specified window.
|
||||
void SetSystemCursorPositionNormalizedInternal(xcb_window_t window, AZ::Vector2 positionNormalized);
|
||||
|
||||
//! Handle button press/release events.
|
||||
void HandleButtonPressEvents(uint32_t detail, bool pressed);
|
||||
|
||||
//! Handle motion notify events.
|
||||
void HandlePointerMotionEvents(const xcb_generic_event_t* event);
|
||||
|
||||
//! Will set cursor states and confinement modes.
|
||||
void HandleCursorState(xcb_window_t window, SystemCursorState systemCursorState);
|
||||
|
||||
//! Will handle all raw input events.
|
||||
void HandleRawInputEvents(const xcb_ge_generic_event_t* event);
|
||||
|
||||
//! Convert XInput fp1616 to float.
|
||||
inline float fp1616ToFloat(xcb_input_fp1616_t value) const
|
||||
{
|
||||
return static_cast<float>((value >> 16) + (value & 0xffff) / 0xffff);
|
||||
}
|
||||
|
||||
//! Convert XInput fp3232 to float.
|
||||
inline float fp3232ToFloat(xcb_input_fp3232_t value) const
|
||||
{
|
||||
return static_cast<float>(value.integral) + static_cast<float>(value.frac / (float)(1ull << 32));
|
||||
}
|
||||
|
||||
const InputChannelId* InputChannelFromMouseEvent(xcb_button_t button, bool& isWheel, float& direction) const
|
||||
{
|
||||
isWheel = false;
|
||||
direction = 1.0f;
|
||||
switch (button)
|
||||
{
|
||||
case XCB_BUTTON_INDEX_1:
|
||||
return &InputDeviceMouse::Button::Left;
|
||||
case XCB_BUTTON_INDEX_2:
|
||||
return &InputDeviceMouse::Button::Right;
|
||||
case XCB_BUTTON_INDEX_3:
|
||||
return &InputDeviceMouse::Button::Middle;
|
||||
case XCB_BUTTON_INDEX_4:
|
||||
isWheel = true;
|
||||
direction = 1.0f;
|
||||
break;
|
||||
case XCB_BUTTON_INDEX_5:
|
||||
isWheel = true;
|
||||
direction = -1.0f;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Barriers work only with positive values. We clamp here to zero.
|
||||
inline int16_t Clamp(int16_t value) const
|
||||
{
|
||||
return value < 0 ? 0 : value;
|
||||
}
|
||||
|
||||
private:
|
||||
//! The current system cursor state
|
||||
SystemCursorState m_systemCursorState;
|
||||
|
||||
//! The cursor position before it got hidden.
|
||||
AZ::Vector2 m_cursorHiddenPosition;
|
||||
|
||||
AZ::Vector2 m_systemCursorPositionNormalized;
|
||||
uint32_t m_systemCursorPosition[MAX_XI_RAW_AXIS];
|
||||
|
||||
static xcb_connection_t* s_xcbConnection;
|
||||
static xcb_screen_t* s_xcbScreen;
|
||||
|
||||
//! Will be true if the xfixes extension could be initialized.
|
||||
static bool m_xfixesInitialized;
|
||||
|
||||
//! Will be true if the xinput2 extension could be initialized.
|
||||
static bool m_xInputInitialized;
|
||||
|
||||
//! The window that had focus
|
||||
xcb_window_t m_prevConstraintWindow;
|
||||
|
||||
//! The current window that has focus
|
||||
xcb_window_t m_focusWindow;
|
||||
|
||||
//! Will be true if the cursor is shown else false.
|
||||
bool m_cursorShown;
|
||||
|
||||
struct XFixesBarrierProperty
|
||||
{
|
||||
xcb_xfixes_barrier_t id;
|
||||
uint32_t direction;
|
||||
int16_t x0, y0, x1, y1;
|
||||
};
|
||||
|
||||
//! Array that holds barrier information used to confine the cursor.
|
||||
std::vector<XFixesBarrierProperty> m_activeBarriers;
|
||||
};
|
||||
} // namespace AzFramework
|
||||
@@ -8,24 +8,31 @@
|
||||
|
||||
#include <AzFramework/Application/Application.h>
|
||||
#include <AzFramework/Windowing/NativeWindow.h>
|
||||
#include <AzFramework/XcbNativeWindow.h>
|
||||
#include <AzFramework/XcbConnectionManager.h>
|
||||
#include <AzFramework/XcbInterface.h>
|
||||
#include <AzFramework/XcbNativeWindow.h>
|
||||
|
||||
#include <xcb/xcb.h>
|
||||
|
||||
namespace AzFramework
|
||||
{
|
||||
[[maybe_unused]] const char XcbErrorWindow[] = "XcbNativeWindow";
|
||||
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
|
||||
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
|
||||
|
||||
#define _NET_WM_STATE_REMOVE 0l
|
||||
#define _NET_WM_STATE_ADD 1l
|
||||
#define _NET_WM_STATE_TOGGLE 2l
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
XcbNativeWindow::XcbNativeWindow()
|
||||
XcbNativeWindow::XcbNativeWindow()
|
||||
: NativeWindow::Implementation()
|
||||
, m_xcbConnection(nullptr)
|
||||
, m_xcbRootScreen(nullptr)
|
||||
, m_xcbWindow(XCB_NONE)
|
||||
{
|
||||
if (auto xcbConnectionManager = AzFramework::XcbConnectionManagerInterface::Get();
|
||||
xcbConnectionManager != nullptr)
|
||||
if (auto xcbConnectionManager = AzFramework::XcbConnectionManagerInterface::Get(); xcbConnectionManager != nullptr)
|
||||
{
|
||||
m_xcbConnection = xcbConnectionManager->GetXcbConnection();
|
||||
}
|
||||
@@ -33,89 +40,184 @@ namespace AzFramework
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
XcbNativeWindow::~XcbNativeWindow() = default;
|
||||
XcbNativeWindow::~XcbNativeWindow()
|
||||
{
|
||||
if (XCB_NONE != m_xcbWindow)
|
||||
{
|
||||
xcb_destroy_window(m_xcbConnection, m_xcbWindow);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void XcbNativeWindow::InitWindow(const AZStd::string& title,
|
||||
const WindowGeometry& geometry,
|
||||
const WindowStyleMasks& styleMasks)
|
||||
void XcbNativeWindow::InitWindow(const AZStd::string& title, const WindowGeometry& geometry, const WindowStyleMasks& styleMasks)
|
||||
{
|
||||
// Get the parent window
|
||||
// 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;
|
||||
m_xcbRootScreen = xcb_setup_roots_iterator(xcbSetup).data;
|
||||
xcb_window_t xcbParentWindow = m_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))
|
||||
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;
|
||||
|
||||
const uint32_t interestedEvents =
|
||||
XCB_EVENT_MASK_STRUCTURE_NOTIFY
|
||||
| XCB_EVENT_MASK_BUTTON_PRESS
|
||||
| XCB_EVENT_MASK_BUTTON_RELEASE
|
||||
| XCB_EVENT_MASK_KEY_PRESS
|
||||
| XCB_EVENT_MASK_KEY_RELEASE
|
||||
| XCB_EVENT_MASK_POINTER_MOTION
|
||||
;
|
||||
uint32_t valueList[] = { xcbRootScreen->black_pixel,
|
||||
interestedEvents };
|
||||
|
||||
const uint32_t interestedEvents = XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_KEY_RELEASE |
|
||||
XCB_EVENT_MASK_FOCUS_CHANGE | XCB_EVENT_MASK_PROPERTY_CHANGE;
|
||||
uint32_t valueList[] = { m_xcbRootScreen->black_pixel, interestedEvents };
|
||||
|
||||
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);
|
||||
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, m_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(XcbErrorWindow, 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(XcbErrorWindow, 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_posX = geometry.m_posX;
|
||||
m_posY = geometry.m_posY;
|
||||
m_width = geometry.m_width;
|
||||
m_height = geometry.m_height;
|
||||
|
||||
InitializeAtoms();
|
||||
|
||||
xcb_client_message_event_t event;
|
||||
event.response_type = XCB_CLIENT_MESSAGE;
|
||||
event.type = _NET_REQUEST_FRAME_EXTENTS;
|
||||
event.window = m_xcbWindow;
|
||||
event.format = 32;
|
||||
event.sequence = 0;
|
||||
event.data.data32[0] = 0l;
|
||||
event.data.data32[1] = 0l;
|
||||
event.data.data32[2] = 0l;
|
||||
event.data.data32[3] = 0l;
|
||||
event.data.data32[4] = 0l;
|
||||
xcbCheckResult = xcb_send_event(
|
||||
m_xcbConnection, 1, m_xcbRootScreen->root, XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT,
|
||||
(const char*)&event);
|
||||
AZ_Assert(ValidateXcbResult(xcbCheckResult), "Failed to set _NET_REQUEST_FRAME_EXTENTS");
|
||||
|
||||
// The WM will be able to kill the application if it gets unresponsive.
|
||||
int32_t pid = getpid();
|
||||
xcb_change_property(m_xcbConnection, XCB_PROP_MODE_REPLACE, m_xcbWindow, _NET_WM_PID, XCB_ATOM_CARDINAL, 32, 1, &pid);
|
||||
|
||||
xcb_flush(m_xcbConnection);
|
||||
}
|
||||
|
||||
xcb_atom_t XcbNativeWindow::GetAtom(const char* atomName)
|
||||
{
|
||||
xcb_intern_atom_cookie_t intern_atom_cookie = xcb_intern_atom(m_xcbConnection, 0, strlen(atomName), atomName);
|
||||
XcbStdFreePtr<xcb_intern_atom_reply_t> xkbinternAtom{ xcb_intern_atom_reply(m_xcbConnection, intern_atom_cookie, NULL) };
|
||||
|
||||
if (!xkbinternAtom)
|
||||
{
|
||||
AZ_Error(XcbErrorWindow, xkbinternAtom != nullptr, "Unable to query xcb '%s' atom", atomName);
|
||||
return XCB_NONE;
|
||||
}
|
||||
|
||||
return xkbinternAtom->atom;
|
||||
}
|
||||
|
||||
int XcbNativeWindow::SetAtom(xcb_window_t window, xcb_atom_t atom, xcb_atom_t type, size_t len, void* data)
|
||||
{
|
||||
xcb_void_cookie_t cookie = xcb_change_property_checked(m_xcbConnection, XCB_PROP_MODE_REPLACE, window, atom, type, 32, len, data);
|
||||
XcbStdFreePtr<xcb_generic_error_t> xkbError{ xcb_request_check(m_xcbConnection, cookie) };
|
||||
|
||||
if (!xkbError)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return xkbError->error_code;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void XcbNativeWindow::InitializeAtoms()
|
||||
{
|
||||
AZStd::vector<xcb_atom_t> Atoms;
|
||||
|
||||
_NET_ACTIVE_WINDOW = GetAtom("_NET_ACTIVE_WINDOW");
|
||||
_NET_WM_BYPASS_COMPOSITOR = GetAtom("_NET_WM_BYPASS_COMPOSITOR");
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Handle all WM Protocols atoms.
|
||||
//
|
||||
|
||||
WM_PROTOCOLS = GetAtom("WM_PROTOCOLS");
|
||||
|
||||
// This atom is used to close a window. Emitted when user clicks the close button.
|
||||
WM_DELETE_WINDOW = GetAtom("WM_DELETE_WINDOW");
|
||||
|
||||
Atoms.push_back(WM_DELETE_WINDOW);
|
||||
|
||||
xcb_change_property(
|
||||
m_xcbConnection, XCB_PROP_MODE_REPLACE, m_xcbWindow, WM_PROTOCOLS, XCB_ATOM_ATOM, 32, Atoms.size(), Atoms.data());
|
||||
|
||||
xcb_flush(m_xcbConnection);
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Handle all WM State atoms.
|
||||
//
|
||||
|
||||
_NET_WM_STATE = GetAtom("_NET_WM_STATE");
|
||||
_NET_WM_STATE_FULLSCREEN = GetAtom("_NET_WM_STATE_FULLSCREEN");
|
||||
_NET_WM_STATE_MAXIMIZED_VERT = GetAtom("_NET_WM_STATE_MAXIMIZED_VERT");
|
||||
_NET_WM_STATE_MAXIMIZED_HORZ = GetAtom("_NET_WM_STATE_MAXIMIZED_HORZ");
|
||||
_NET_MOVERESIZE_WINDOW = GetAtom("_NET_MOVERESIZE_WINDOW");
|
||||
_NET_REQUEST_FRAME_EXTENTS = GetAtom("_NET_REQUEST_FRAME_EXTENTS");
|
||||
_NET_FRAME_EXTENTS = GetAtom("_NET_FRAME_EXTENTS");
|
||||
_NET_WM_PID = GetAtom("_NET_WM_PID");
|
||||
}
|
||||
|
||||
void XcbNativeWindow::GetWMStates()
|
||||
{
|
||||
xcb_get_property_cookie_t cookie = xcb_get_property(m_xcbConnection, 0, m_xcbWindow, _NET_WM_STATE, XCB_ATOM_ATOM, 0, 1024);
|
||||
|
||||
xcb_generic_error_t* error = nullptr;
|
||||
XcbStdFreePtr<xcb_get_property_reply_t> xkbGetPropertyReply{ xcb_get_property_reply(m_xcbConnection, cookie, &error) };
|
||||
|
||||
if (!xkbGetPropertyReply || error || !((xkbGetPropertyReply->format == 32) && (xkbGetPropertyReply->type == XCB_ATOM_ATOM)))
|
||||
{
|
||||
AZ_Warning("ApplicationLinux", false, "Acquiring _NET_WM_STATE information from the WM failed.");
|
||||
|
||||
if (error)
|
||||
{
|
||||
AZ_TracePrintf("Error", "Error code %d", error->error_code);
|
||||
free(error);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
m_fullscreenState = false;
|
||||
m_horizontalyMaximized = false;
|
||||
m_verticallyMaximized = false;
|
||||
|
||||
const xcb_atom_t* states = static_cast<const xcb_atom_t*>(xcb_get_property_value(xkbGetPropertyReply.get()));
|
||||
for (int i = 0; i < xkbGetPropertyReply->length; i++)
|
||||
{
|
||||
if (states[i] == _NET_WM_STATE_FULLSCREEN)
|
||||
{
|
||||
m_fullscreenState = true;
|
||||
}
|
||||
else if (states[i] == _NET_WM_STATE_MAXIMIZED_HORZ)
|
||||
{
|
||||
m_horizontalyMaximized = true;
|
||||
}
|
||||
else if (states[i] == _NET_WM_STATE_MAXIMIZED_VERT)
|
||||
{
|
||||
m_verticallyMaximized = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -145,7 +247,7 @@ namespace AzFramework
|
||||
xcb_flush(m_xcbConnection);
|
||||
}
|
||||
XcbEventHandlerBus::Handler::BusDisconnect();
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
NativeWindowHandle XcbNativeWindow::GetWindowHandle() const
|
||||
@@ -157,14 +259,9 @@ namespace AzFramework
|
||||
void XcbNativeWindow::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());
|
||||
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.");
|
||||
}
|
||||
|
||||
@@ -174,7 +271,7 @@ namespace AzFramework
|
||||
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;
|
||||
}
|
||||
@@ -184,16 +281,77 @@ namespace AzFramework
|
||||
{
|
||||
// [GFX TODO][GHI - 2678]
|
||||
// Using 60 for now until proper support is added
|
||||
|
||||
return 60;
|
||||
}
|
||||
|
||||
bool XcbNativeWindow::GetFullScreenState() const
|
||||
{
|
||||
return m_fullscreenState;
|
||||
}
|
||||
|
||||
void XcbNativeWindow::SetFullScreenState(bool fullScreenState)
|
||||
{
|
||||
// TODO This is a pretty basic full-screen implementation using WM's _NET_WM_STATE_FULLSCREEN state.
|
||||
// Do we have to provide also the old way?
|
||||
|
||||
GetWMStates();
|
||||
|
||||
xcb_client_message_event_t event;
|
||||
event.response_type = XCB_CLIENT_MESSAGE;
|
||||
event.type = _NET_WM_STATE;
|
||||
event.window = m_xcbWindow;
|
||||
event.format = 32;
|
||||
event.sequence = 0;
|
||||
event.data.data32[0] = fullScreenState ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
|
||||
event.data.data32[1] = _NET_WM_STATE_FULLSCREEN;
|
||||
event.data.data32[2] = 0;
|
||||
event.data.data32[3] = 1;
|
||||
event.data.data32[4] = 0;
|
||||
xcb_void_cookie_t xcbCheckResult = xcb_send_event(
|
||||
m_xcbConnection, 1, m_xcbRootScreen->root, XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT,
|
||||
(const char*)&event);
|
||||
AZ_Assert(ValidateXcbResult(xcbCheckResult), "Failed to set _NET_WM_STATE_FULLSCREEN");
|
||||
|
||||
// Also try to disable/enable the compositor if possible. Might help in some cases.
|
||||
const long _NET_WM_BYPASS_COMPOSITOR_HINT_ON = m_fullscreenState ? 1 : 0;
|
||||
SetAtom(m_xcbWindow, _NET_WM_BYPASS_COMPOSITOR, XCB_ATOM_CARDINAL, 32, (char*)&_NET_WM_BYPASS_COMPOSITOR_HINT_ON);
|
||||
|
||||
if (!fullScreenState)
|
||||
{
|
||||
if (m_horizontalyMaximized || m_verticallyMaximized)
|
||||
{
|
||||
printf("Remove maximized state.\n");
|
||||
xcb_client_message_event_t event;
|
||||
event.response_type = XCB_CLIENT_MESSAGE;
|
||||
event.type = _NET_WM_STATE;
|
||||
event.window = m_xcbWindow;
|
||||
event.format = 32;
|
||||
event.sequence = 0;
|
||||
event.data.data32[0] = _NET_WM_STATE_MAXIMIZED_VERT;
|
||||
event.data.data32[1] = _NET_WM_STATE_MAXIMIZED_HORZ;
|
||||
event.data.data32[2] = 0;
|
||||
event.data.data32[3] = 0;
|
||||
event.data.data32[4] = 0;
|
||||
xcb_void_cookie_t xcbCheckResult = xcb_send_event(
|
||||
m_xcbConnection, 1, m_xcbRootScreen->root, XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT,
|
||||
(const char*)&event);
|
||||
AZ_Assert(
|
||||
ValidateXcbResult(xcbCheckResult), "Failed to remove _NET_WM_STATE_MAXIMIZED_VERT | _NET_WM_STATE_MAXIMIZED_HORZ");
|
||||
}
|
||||
}
|
||||
|
||||
xcb_flush(m_xcbConnection);
|
||||
m_fullscreenState = fullScreenState;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
bool XcbNativeWindow::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);
|
||||
AZ_TracePrintf("Error", "Error code %d", error->error_code);
|
||||
result = false;
|
||||
}
|
||||
return result;
|
||||
@@ -204,20 +362,20 @@ namespace AzFramework
|
||||
{
|
||||
switch (event->response_type & s_XcbResponseTypeMask)
|
||||
{
|
||||
case XCB_CONFIGURE_NOTIFY:
|
||||
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));
|
||||
|
||||
if ((cne->width != m_width) || (cne->height != m_height))
|
||||
{
|
||||
WindowSizeChanged(aznumeric_cast<uint32_t>(cne->width), aznumeric_cast<uint32_t>(cne->height));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case XCB_CLIENT_MESSAGE:
|
||||
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))
|
||||
|
||||
if ((cme->type == WM_PROTOCOLS) && (cme->format == s_XcbFormatDataSize) && (cme->data.data32[0] == WM_DELETE_WINDOW))
|
||||
{
|
||||
Deactivate();
|
||||
|
||||
@@ -238,7 +396,8 @@ namespace AzFramework
|
||||
|
||||
if (m_activated)
|
||||
{
|
||||
WindowNotificationBus::Event(reinterpret_cast<NativeWindowHandle>(m_xcbWindow), &WindowNotificationBus::Events::OnWindowResized, width, height);
|
||||
WindowNotificationBus::Event(
|
||||
reinterpret_cast<NativeWindowHandle>(m_xcbWindow), &WindowNotificationBus::Events::OnWindowResized, width, height);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,15 +27,16 @@ namespace AzFramework
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// NativeWindow::Implementation
|
||||
void InitWindow(const AZStd::string& title,
|
||||
const WindowGeometry& geometry,
|
||||
const WindowStyleMasks& styleMasks) override;
|
||||
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;
|
||||
uint32_t GetDisplayRefreshRate() const override;
|
||||
|
||||
bool GetFullScreenState() const override;
|
||||
void SetFullScreenState(bool fullScreenState) override;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// XcbEventHandlerBus::Handler
|
||||
@@ -44,10 +45,46 @@ namespace AzFramework
|
||||
private:
|
||||
bool ValidateXcbResult(xcb_void_cookie_t cookie);
|
||||
void WindowSizeChanged(const uint32_t width, const uint32_t height);
|
||||
int SetAtom(xcb_window_t window, xcb_atom_t atom, xcb_atom_t type, size_t len, void* data);
|
||||
|
||||
xcb_connection_t* m_xcbConnection = nullptr;
|
||||
xcb_window_t m_xcbWindow = 0;
|
||||
xcb_atom_t m_xcbAtomProtocols;
|
||||
xcb_atom_t m_xcbAtomDeleteWindow;
|
||||
// Initialize one atom.
|
||||
xcb_atom_t GetAtom(const char* atomName);
|
||||
|
||||
// Initialize all used atoms.
|
||||
void InitializeAtoms();
|
||||
void GetWMStates();
|
||||
|
||||
xcb_connection_t* m_xcbConnection = nullptr;
|
||||
xcb_screen_t* m_xcbRootScreen = nullptr;
|
||||
xcb_window_t m_xcbWindow = 0;
|
||||
int32_t m_posX;
|
||||
int32_t m_posY;
|
||||
bool m_fullscreenState = false;
|
||||
bool m_horizontalyMaximized = false;
|
||||
bool m_verticallyMaximized = false;
|
||||
|
||||
// Use exact atom names for easy readability and usage.
|
||||
xcb_atom_t WM_PROTOCOLS;
|
||||
xcb_atom_t WM_DELETE_WINDOW;
|
||||
// This atom is used to activate a window.
|
||||
xcb_atom_t _NET_ACTIVE_WINDOW;
|
||||
// This atom is use to bypass a compositor. Used during fullscreen mode.
|
||||
xcb_atom_t _NET_WM_BYPASS_COMPOSITOR;
|
||||
// This atom is used to change the state of a window using the WM.
|
||||
xcb_atom_t _NET_WM_STATE;
|
||||
// This atom is used to enable/disable fullscreen mode of a window.
|
||||
xcb_atom_t _NET_WM_STATE_FULLSCREEN;
|
||||
// This atom is used to extend the window to max vertically.
|
||||
xcb_atom_t _NET_WM_STATE_MAXIMIZED_VERT;
|
||||
// This atom is used to extend the window to max horizontally.
|
||||
xcb_atom_t _NET_WM_STATE_MAXIMIZED_HORZ;
|
||||
// This atom is used to position and resize a window.
|
||||
xcb_atom_t _NET_MOVERESIZE_WINDOW;
|
||||
// This atom is used to request the extent of the window.
|
||||
xcb_atom_t _NET_REQUEST_FRAME_EXTENTS;
|
||||
// This atom is used to identify the reply event for _NET_REQUEST_FRAME_EXTENTS
|
||||
xcb_atom_t _NET_FRAME_EXTENTS;
|
||||
// This atom is used to allow WM to kill app if not responsive anymore
|
||||
xcb_atom_t _NET_WM_PID;
|
||||
};
|
||||
} // namespace AzFramework
|
||||
|
||||
@@ -12,6 +12,8 @@ set(FILES
|
||||
AzFramework/XcbConnectionManager.h
|
||||
AzFramework/XcbInputDeviceKeyboard.cpp
|
||||
AzFramework/XcbInputDeviceKeyboard.h
|
||||
AzFramework/XcbInputDeviceMouse.cpp
|
||||
AzFramework/XcbInputDeviceMouse.h
|
||||
AzFramework/XcbInterface.h
|
||||
AzFramework/XcbNativeWindow.cpp
|
||||
AzFramework/XcbNativeWindow.h
|
||||
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
|
||||
#include <AzFramework/XcbInputDeviceMouse.h>
|
||||
#endif
|
||||
|
||||
namespace AzFramework
|
||||
{
|
||||
InputDeviceMouse::Implementation* InputDeviceMouse::Implementation::Create(InputDeviceMouse& inputDevice)
|
||||
{
|
||||
#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
|
||||
return XcbInputDeviceMouse::Create(inputDevice);
|
||||
#elif PAL_TRAIT_LINUX_WINDOW_MANAGER_WAYLAND
|
||||
#error "Linux Window Manager Wayland not supported."
|
||||
return nullptr;
|
||||
#else
|
||||
#error "Linux Window Manager not recognized."
|
||||
return nullptr;
|
||||
#endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
|
||||
}
|
||||
} // namespace AzFramework
|
||||
@@ -22,8 +22,10 @@ if (${PAL_TRAIT_LINUX_WINDOW_MANAGER} STREQUAL "xcb")
|
||||
PRIVATE
|
||||
3rdParty::X11::xcb
|
||||
3rdParty::X11::xcb_xkb
|
||||
3rdParty::X11::xcb_xfixes
|
||||
3rdParty::X11::xkbcommon
|
||||
3rdParty::X11::xkbcommon_X11
|
||||
xcb-xinput
|
||||
)
|
||||
|
||||
elseif(PAL_TRAIT_LINUX_WINDOW_MANAGER STREQUAL "wayland")
|
||||
|
||||
@@ -22,8 +22,8 @@ set(FILES
|
||||
AzFramework/Windowing/NativeWindow_Linux.cpp
|
||||
../Common/Unimplemented/AzFramework/Input/Devices/Gamepad/InputDeviceGamepad_Unimplemented.cpp
|
||||
AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard_Linux.cpp
|
||||
AzFramework/Input/Devices/Mouse/InputDeviceMouse_Linux.cpp
|
||||
../Common/Unimplemented/AzFramework/Input/Devices/Motion/InputDeviceMotion_Unimplemented.cpp
|
||||
../Common/Unimplemented/AzFramework/Input/Devices/Mouse/InputDeviceMouse_Unimplemented.cpp
|
||||
../Common/Unimplemented/AzFramework/Input/Devices/Touch/InputDeviceTouch_Unimplemented.cpp
|
||||
AzFramework/Input/User/LocalUserId_Platform.h
|
||||
../Common/Default/AzFramework/Input/User/LocalUserId_Default.h
|
||||
|
||||
@@ -112,9 +112,10 @@ namespace AzFramework
|
||||
void ApplicationIos::PumpSystemEventLoopUntilEmpty()
|
||||
{
|
||||
SInt32 result;
|
||||
const CFTimeInterval MaxSecondsInRunLoop = 0.001; // One millisecond
|
||||
do
|
||||
{
|
||||
result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, DBL_EPSILON, TRUE);
|
||||
result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, MaxSecondsInRunLoop, TRUE);
|
||||
}
|
||||
while (result == kCFRunLoopRunHandledSource);
|
||||
}
|
||||
|
||||
+22
-17
@@ -19,6 +19,7 @@
|
||||
#include "Matchers.h"
|
||||
#include "Actions.h"
|
||||
#include "XcbBaseTestFixture.h"
|
||||
#include "XcbTestApplication.h"
|
||||
|
||||
template<typename T>
|
||||
xcb_generic_event_t MakeEvent(T event)
|
||||
@@ -32,6 +33,7 @@ namespace AzFramework
|
||||
class XcbInputDeviceKeyboardTests
|
||||
: public XcbBaseTestFixture
|
||||
{
|
||||
public:
|
||||
void SetUp() override
|
||||
{
|
||||
using testing::Return;
|
||||
@@ -122,6 +124,15 @@ namespace AzFramework
|
||||
|
||||
static constexpr xcb_keycode_t s_keycodeForAKey{38};
|
||||
static constexpr xcb_keycode_t s_keycodeForShiftLKey{50};
|
||||
|
||||
XcbTestApplication m_application{
|
||||
/*enabledGamepadsCount=*/0,
|
||||
/*keyboardEnabled=*/true,
|
||||
/*motionEnabled=*/false,
|
||||
/*mouseEnabled=*/false,
|
||||
/*touchEnabled=*/false,
|
||||
/*virtualKeyboardEnabled=*/false
|
||||
};
|
||||
};
|
||||
|
||||
class InputTextNotificationListener
|
||||
@@ -194,26 +205,23 @@ namespace AzFramework
|
||||
EXPECT_CALL(m_interface, xkb_state_key_get_one_sym(&m_xkbState, s_keycodeForAKey))
|
||||
.Times(2);
|
||||
|
||||
Application application;
|
||||
application.Start({}, {});
|
||||
m_application.Start();
|
||||
|
||||
const InputChannel* inputChannel = InputChannelRequests::FindInputChannel(InputDeviceKeyboard::Key::AlphanumericA);
|
||||
ASSERT_TRUE(inputChannel);
|
||||
EXPECT_THAT(inputChannel->GetState(), Eq(InputChannel::State::Idle));
|
||||
|
||||
application.PumpSystemEventLoopUntilEmpty();
|
||||
application.TickSystem();
|
||||
application.Tick();
|
||||
m_application.PumpSystemEventLoopUntilEmpty();
|
||||
m_application.TickSystem();
|
||||
m_application.Tick();
|
||||
|
||||
EXPECT_THAT(inputChannel->GetState(), Eq(InputChannel::State::Began));
|
||||
|
||||
application.PumpSystemEventLoopUntilEmpty();
|
||||
application.TickSystem();
|
||||
application.Tick();
|
||||
m_application.PumpSystemEventLoopUntilEmpty();
|
||||
m_application.TickSystem();
|
||||
m_application.Tick();
|
||||
|
||||
EXPECT_THAT(inputChannel->GetState(), Eq(InputChannel::State::Ended));
|
||||
|
||||
application.Stop();
|
||||
}
|
||||
|
||||
TEST_F(XcbInputDeviceKeyboardTests, TextEnteredFromXcbKeyPressEvents)
|
||||
@@ -418,16 +426,13 @@ namespace AzFramework
|
||||
EXPECT_CALL(textListener, OnInputTextEvent(StrEq("a"), _)).Times(1);
|
||||
EXPECT_CALL(textListener, OnInputTextEvent(StrEq("A"), _)).Times(1);
|
||||
|
||||
Application application;
|
||||
application.Start({}, {});
|
||||
m_application.Start();
|
||||
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
application.PumpSystemEventLoopUntilEmpty();
|
||||
application.TickSystem();
|
||||
application.Tick();
|
||||
m_application.PumpSystemEventLoopUntilEmpty();
|
||||
m_application.TickSystem();
|
||||
m_application.Tick();
|
||||
}
|
||||
|
||||
application.Stop();
|
||||
}
|
||||
} // namespace AzFramework
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Settings/SettingsRegistry.h>
|
||||
#include <AzCore/UserSettings/UserSettingsComponent.h>
|
||||
#include <AzFramework/Application/Application.h>
|
||||
|
||||
namespace AzFramework
|
||||
{
|
||||
class XcbTestApplication
|
||||
: public Application
|
||||
{
|
||||
public:
|
||||
XcbTestApplication(AZ::u64 enabledGamepadsCount, bool keyboardEnabled, bool motionEnabled, bool mouseEnabled, bool touchEnabled, bool virtualKeyboardEnabled)
|
||||
{
|
||||
auto* settingsRegistry = AZ::SettingsRegistry::Get();
|
||||
settingsRegistry->Set("/O3DE/InputSystem/GamepadsEnabled", enabledGamepadsCount);
|
||||
settingsRegistry->Set("/O3DE/InputSystem/KeyboardEnabled", keyboardEnabled);
|
||||
settingsRegistry->Set("/O3DE/InputSystem/MotionEnabled", motionEnabled);
|
||||
settingsRegistry->Set("/O3DE/InputSystem/MouseEnabled", mouseEnabled);
|
||||
settingsRegistry->Set("/O3DE/InputSystem/TouchEnabled", touchEnabled);
|
||||
settingsRegistry->Set("/O3DE/InputSystem/VirtualKeyboardEnabled", virtualKeyboardEnabled);
|
||||
}
|
||||
|
||||
void Start(const Descriptor& descriptor = {}, const StartupParameters& startupParameters = {}) override
|
||||
{
|
||||
Application::Start(descriptor, startupParameters);
|
||||
AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize);
|
||||
}
|
||||
};
|
||||
} // namespace AzFramework
|
||||
@@ -17,4 +17,5 @@ set(FILES
|
||||
XcbBaseTestFixture.cpp
|
||||
XcbBaseTestFixture.h
|
||||
XcbInputDeviceKeyboardTests.cpp
|
||||
XcbTestApplication.h
|
||||
)
|
||||
|
||||
@@ -569,11 +569,12 @@ namespace UnitTest
|
||||
FillSpawnable(NumEntities);
|
||||
CreateEntityReferences(refScheme);
|
||||
|
||||
AZ_PUSH_DISABLE_WARNING(5233, "-Wunused-lambda-capture") // Older versions of MSVC toolchain require to pass constexpr in the
|
||||
// capture. Newer versions issue unused warning
|
||||
auto callback =
|
||||
[this, refScheme, NumEntities](AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities)
|
||||
AZ_POP_DISABLE_WARNING
|
||||
{
|
||||
AZ_UNUSED(refScheme);
|
||||
AZ_UNUSED(NumEntities);
|
||||
ValidateEntityReferences(refScheme, NumEntities, entities);
|
||||
};
|
||||
|
||||
@@ -591,11 +592,12 @@ namespace UnitTest
|
||||
FillSpawnable(NumEntities);
|
||||
CreateEntityReferences(refScheme);
|
||||
|
||||
AZ_PUSH_DISABLE_WARNING(5233, "-Wunused-lambda-capture") // Older versions of MSVC toolchain require to pass constexpr in the
|
||||
// capture. Newer versions issue unused warning
|
||||
auto callback =
|
||||
[this, refScheme, NumEntities](AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities)
|
||||
AZ_POP_DISABLE_WARNING
|
||||
{
|
||||
AZ_UNUSED(refScheme);
|
||||
AZ_UNUSED(NumEntities);
|
||||
ValidateEntityReferences(refScheme, NumEntities, entities);
|
||||
};
|
||||
|
||||
@@ -720,11 +722,12 @@ namespace UnitTest
|
||||
FillSpawnable(NumEntities);
|
||||
CreateEntityReferences(refScheme);
|
||||
|
||||
AZ_PUSH_DISABLE_WARNING(5233, "-Wunused-lambda-capture") // Older versions of MSVC toolchain require to pass constexpr in the
|
||||
// capture. Newer versions issue unused warning
|
||||
auto callback =
|
||||
[this, refScheme, NumEntities](AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities)
|
||||
AZ_POP_DISABLE_WARNING
|
||||
{
|
||||
AZ_UNUSED(refScheme);
|
||||
AZ_UNUSED(NumEntities);
|
||||
ValidateEntityReferences(refScheme, NumEntities, entities);
|
||||
};
|
||||
|
||||
|
||||
@@ -96,6 +96,8 @@ namespace AzGameFramework
|
||||
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_CommandLine(registry, m_commandLine, false);
|
||||
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_ProjectUserRegistry(registry, AZ_TRAIT_OS_PLATFORM_CODENAME, specializations, &scratchBuffer);
|
||||
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_CommandLine(registry, m_commandLine, true);
|
||||
#else
|
||||
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_CommandLine(registry, m_commandLine, false);
|
||||
#endif
|
||||
// Update the Runtime file paths in case the "{BootstrapSettingsRootKey}/assets" key was overriden by a setting registry
|
||||
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(registry);
|
||||
|
||||
@@ -33,6 +33,9 @@ namespace AzQtComponents
|
||||
titleLabel->setObjectName("Title");
|
||||
titleLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
||||
titleLabel->setWordWrap(true);
|
||||
titleLabel->setTextFormat(Qt::RichText);
|
||||
titleLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
|
||||
titleLabel->setOpenExternalLinks(true);
|
||||
|
||||
QHBoxLayout* headerLayout = new QHBoxLayout(headerFrame);
|
||||
headerLayout->setSizeConstraint(QLayout::SetMinimumSize);
|
||||
|
||||
@@ -6,10 +6,11 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzQtComponents/AzQtComponents_Traits_Platform.h>
|
||||
#include <AzQtComponents/Components/Widgets/FileDialog.h>
|
||||
|
||||
#include <QMessageBox>
|
||||
#include <QRegExp>
|
||||
#include <QRegularExpression>
|
||||
|
||||
namespace AzQtComponents
|
||||
{
|
||||
@@ -24,7 +25,12 @@ namespace AzQtComponents
|
||||
// Trigger Qt's save filename dialog
|
||||
// If filePath isn't empty, it means we are prompting again because the filename was invalid,
|
||||
// so pass it instead of the directory so the filename is pre-filled in for the user
|
||||
filePath = QFileDialog::getSaveFileName(parent, caption, (filePath.isEmpty()) ? dir : filePath, filter, selectedFilter, options);
|
||||
QString localSelectedFilter;
|
||||
filePath = QFileDialog::getSaveFileName(parent, caption, (filePath.isEmpty()) ? dir : filePath, filter, &localSelectedFilter, options);
|
||||
if (selectedFilter)
|
||||
{
|
||||
*selectedFilter = localSelectedFilter;
|
||||
}
|
||||
|
||||
if (!filePath.isEmpty())
|
||||
{
|
||||
@@ -32,15 +38,39 @@ namespace AzQtComponents
|
||||
QString fileName = fileInfo.fileName();
|
||||
|
||||
// Check if the filename has any invalid characters
|
||||
QRegExp validFileNameRegex("^[a-zA-Z0-9_\\-./]*$");
|
||||
shouldPromptAgain = !validFileNameRegex.exactMatch(fileName);
|
||||
QRegularExpression validFileNameRegex("^[a-zA-Z0-9_\\-./]*$");
|
||||
QRegularExpressionMatch validFileNameMatch = validFileNameRegex.match(fileName);
|
||||
|
||||
// If the filename had invalid characters, then show a warning message and then we will re-prompt the save filename dialog
|
||||
if (shouldPromptAgain)
|
||||
if (!validFileNameMatch.hasMatch())
|
||||
{
|
||||
QMessageBox::warning(parent, QObject::tr("Invalid filename"),
|
||||
QObject::tr("O3DE assets are restricted to alphanumeric characters, hyphens (-), underscores (_), and dots (.)\n\n%1").arg(fileName));
|
||||
shouldPromptAgain = true;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
shouldPromptAgain = false;
|
||||
}
|
||||
#if AZ_TRAIT_AZQTCOMPONENTS_FILE_DIALOG_APPLY_MISSING_EXTENSION
|
||||
// If a filter was selected, then make sure that the resulting filename ends with that extension. On systems that use the default QFileDialog,
|
||||
// the extension is not guaranteed to be set in the resulting filename
|
||||
if (FileDialog::ApplyMissingExtension(localSelectedFilter, filePath))
|
||||
{
|
||||
// If an extension had to be applied, then the file dialog did not handle the case of overwriting existing files.
|
||||
// We need to check that condition before we proceed
|
||||
QFileInfo updatedFilePath(filePath);
|
||||
|
||||
if (updatedFilePath.exists())
|
||||
{
|
||||
QMessageBox::StandardButton overwriteSelection = QMessageBox::question(parent,
|
||||
QObject::tr("File exists"),
|
||||
QObject::tr("%1 exists. Do you want to overwrite the existing file?").arg(updatedFilePath.fileName()));
|
||||
shouldPromptAgain = (overwriteSelection == QMessageBox::No);
|
||||
}
|
||||
}
|
||||
#endif // AZ_TRAIT_AZQTCOMPONENTS_FILE_DIALOG_APPLY_MISSING_EXTENSION
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -51,4 +81,56 @@ namespace AzQtComponents
|
||||
|
||||
return filePath;
|
||||
}
|
||||
|
||||
bool FileDialog::ApplyMissingExtension(const QString& selectedFilter, QString& filePath)
|
||||
{
|
||||
if (selectedFilter.isEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// According to the QT documentation for QFileDialog, the selected filter will come in the form
|
||||
// <Filter Name> (<filter pattern1> <filter pattern2> .. <filter patternN> )
|
||||
//
|
||||
// For example:
|
||||
// "Images (*.gif *.png *.jpg)"
|
||||
//
|
||||
// Extract the contents of the <filter pattern>(s) inside the parenthesis and split them based on a whitespace or comma
|
||||
const QRegularExpression filterContent(".*\\((?<filters>[^\\)]+)\\)");
|
||||
QRegularExpressionMatch filterContentMatch = filterContent.match(selectedFilter);
|
||||
if (!filterContentMatch.hasMatch())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
QString filterExtensionsString = filterContentMatch.captured("filters");
|
||||
QStringList filterExtensionsFull = filterExtensionsString.split(" ", Qt::SkipEmptyParts);
|
||||
if (filterExtensionsFull.length() <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// If there are multiple suffixes in the selected filter, then default to the first one if a suffix needs to be appended
|
||||
QString defaultSuffix = filterExtensionsFull[0].mid(1);
|
||||
|
||||
// Iterate through the filter patterns to see if the current filename matches
|
||||
QFileInfo fileInfo(filePath);
|
||||
bool extensionNeeded = true;
|
||||
for (const QString& filterExtensionFull : filterExtensionsFull)
|
||||
{
|
||||
QString wildcardExpression = QRegularExpression::wildcardToRegularExpression(filterExtensionFull);
|
||||
QRegularExpression filterPattern(wildcardExpression, AZ_TRAIT_AZQTCOMPONENTS_FILE_DIALOG_FILTER_CASE_SENSITIVITY);
|
||||
QRegularExpressionMatch filterPatternMatch = filterPattern.match(fileInfo.fileName());
|
||||
if (filterPatternMatch.hasMatch())
|
||||
{
|
||||
// The filename matches one of the filter patterns already, the extension does not need to be added to the filename
|
||||
extensionNeeded = false;
|
||||
}
|
||||
}
|
||||
if (extensionNeeded)
|
||||
{
|
||||
// If the current (if any) suffix does not match, automatically add the default suffix for the selected filter
|
||||
filePath.append(defaultSuffix);
|
||||
}
|
||||
return extensionNeeded;
|
||||
}
|
||||
} // namespace AzQtComponents
|
||||
|
||||
@@ -24,6 +24,12 @@ namespace AzQtComponents
|
||||
static QString GetSaveFileName(QWidget* parent = nullptr, const QString& caption = QString(),
|
||||
const QString& dir = QString(), const QString& filter = QString(),
|
||||
QString* selectedFilter = nullptr, QFileDialog::Options options = QFileDialog::Options());
|
||||
|
||||
//! Helper method that parses a selected filter from Qt's QFileDialog::getSaveFileName and applies the
|
||||
//! selected filter's extension to the filePath if it doesnt already have the extension. This is needed
|
||||
//! on platforms that do not have a default file dialog (These platforms uses Qt's custom file dialog which will
|
||||
//! not apply the filter's extension automatically on user entered filenames)
|
||||
static bool ApplyMissingExtension(const QString& selectedFilter, QString& filePath);
|
||||
};
|
||||
|
||||
} // namespace AzQtComponents
|
||||
|
||||
@@ -12,4 +12,7 @@ set(FILES
|
||||
../../Utilities/QtWindowUtilities_linux.cpp
|
||||
../../Utilities/ScreenGrabber_linux.cpp
|
||||
../../../Platform/Linux/AzQtComponents/Components/StyledDockWidget_Linux.cpp
|
||||
../../../Platform/Linux/AzQtComponents/Utilities/DesktopUtilities_Linux.cpp
|
||||
../../../Platform/Linux/AzQtComponents/AzQtComponents_Traits_Linux.h
|
||||
../../../Platform/Linux/AzQtComponents/AzQtComponents_Traits_Platform.h
|
||||
)
|
||||
|
||||
@@ -12,4 +12,7 @@ set(FILES
|
||||
../../Utilities/QtWindowUtilities_mac.mm
|
||||
../../Utilities/ScreenGrabber_mac.mm
|
||||
../../../Platform/Mac/AzQtComponents/Components/StyledDockWidget_Mac.cpp
|
||||
../../../Platform/Mac/AzQtComponents/Utilities/DesktopUtilities_Mac.cpp
|
||||
../../../Platform/Mac/AzQtComponents/AzQtComponents_Traits_Mac.h
|
||||
../../../Platform/Mac/AzQtComponents/AzQtComponents_Traits_Platform.h
|
||||
)
|
||||
|
||||
+3
@@ -9,6 +9,7 @@
|
||||
set(FILES
|
||||
../../natvis/qt.natvis
|
||||
../../../Platform/Windows/AzQtComponents/Utilities/HandleDpiAwareness_Windows.cpp
|
||||
../../../Platform/Windows/AzQtComponents/Utilities/DesktopUtilities_Windows.cpp
|
||||
../../Utilities/MouseHider_win.cpp
|
||||
../../Utilities/QtWindowUtilities_win.cpp
|
||||
../../Utilities/ScreenGrabber_win.cpp
|
||||
@@ -17,4 +18,6 @@ set(FILES
|
||||
../../Components/TitleBarOverdrawScreenHandler_win.h
|
||||
../../Components/TitleBarOverdrawScreenHandler_win.cpp
|
||||
../../../Platform/Windows/AzQtComponents/Components/StyledDockWidget_Windows.cpp
|
||||
../../../Platform/Windows/AzQtComponents/AzQtComponents_Traits_Windows.h
|
||||
../../../Platform/Windows/AzQtComponents/AzQtComponents_Traits_Platform.h
|
||||
)
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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 <AzTest/AzTest.h>
|
||||
#include <AzQtComponents/Components/Widgets/FileDialog.h>
|
||||
#include <QString>
|
||||
|
||||
TEST(AzQtComponents, ApplyMissingExtension_UpdateMissingExtension_Success)
|
||||
{
|
||||
const QString textFiler{"Text Files (*.txt)"};
|
||||
QString testPath{"testFile"};
|
||||
bool result = AzQtComponents::FileDialog::ApplyMissingExtension(textFiler, testPath);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_STRCASEEQ("testFile.txt", testPath.toUtf8().constData());
|
||||
}
|
||||
|
||||
TEST(AzQtComponents, ApplyMissingExtension_NoUpdateExistingExtension_Success)
|
||||
{
|
||||
const QString textFiler{"Text Files (*.txt)"};
|
||||
QString testPath{"testFile.txt"};
|
||||
bool result = AzQtComponents::FileDialog::ApplyMissingExtension(textFiler, testPath);
|
||||
EXPECT_FALSE(result);
|
||||
EXPECT_STRCASEEQ("testFile.txt", testPath.toUtf8().constData());
|
||||
}
|
||||
|
||||
TEST(AzQtComponents, ApplyMissingExtension_UpdateMissingExtensionMultipleExtensionFilter_Success)
|
||||
{
|
||||
const QString textFiler{"Image Files (*.jpg *.bmp *.png)"};
|
||||
QString testPath{"testFile"};
|
||||
bool result = AzQtComponents::FileDialog::ApplyMissingExtension(textFiler, testPath);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_STRCASEEQ("testFile.jpg", testPath.toUtf8().constData());
|
||||
}
|
||||
|
||||
TEST(AzQtComponents, ApplyMissingExtension_NoUpdateMissingExtensionMultipleExtensionFilter_Success)
|
||||
{
|
||||
const QString textFiler{"Image Files (*.jpg *.bmp *.png)"};
|
||||
QString testPath{"testFile.png"};
|
||||
bool result = AzQtComponents::FileDialog::ApplyMissingExtension(textFiler, testPath);
|
||||
EXPECT_FALSE(result);
|
||||
EXPECT_STRCASEEQ("testFile.png", testPath.toUtf8().constData());
|
||||
}
|
||||
|
||||
TEST(AzQtComponents, ApplyMissingExtension_NoUpdateMissingExtensionEmptyFilter_Success)
|
||||
{
|
||||
const QString textFiler{""};
|
||||
QString testPath{"testFile"};
|
||||
bool result = AzQtComponents::FileDialog::ApplyMissingExtension(textFiler, testPath);
|
||||
EXPECT_FALSE(result);
|
||||
EXPECT_STRCASEEQ("testFile", testPath.toUtf8().constData());
|
||||
}
|
||||
|
||||
TEST(AzQtComponents, ApplyMissingExtension_NoUpdateMissingExtensionInvalidFilter_Success)
|
||||
{
|
||||
const QString textFiler{"Bad Filter!!"};
|
||||
QString testPath{"testFile"};
|
||||
bool result = AzQtComponents::FileDialog::ApplyMissingExtension(textFiler, testPath);
|
||||
EXPECT_FALSE(result);
|
||||
EXPECT_STRCASEEQ("testFile", testPath.toUtf8().constData());
|
||||
}
|
||||
@@ -271,7 +271,6 @@ set(FILES
|
||||
Utilities/ColorUtilities.h
|
||||
Utilities/Conversions.h
|
||||
Utilities/Conversions.cpp
|
||||
Utilities/DesktopUtilities.cpp
|
||||
Utilities/DesktopUtilities.h
|
||||
Utilities/HandleDpiAwareness.cpp
|
||||
Utilities/HandleDpiAwareness.h
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
set(FILES
|
||||
Tests/AzQtComponentTests.cpp
|
||||
Tests/ColorControllerTests.cpp
|
||||
Tests/FileDialogTests.cpp
|
||||
Tests/FloatToStringConversionTests.cpp
|
||||
Tests/HexParsingTests.cpp
|
||||
Tests/StyleSheetCacheTests.cpp
|
||||
|
||||
@@ -10,6 +10,8 @@ if(NOT PAL_TRAIT_BUILD_HOST_TOOLS)
|
||||
return()
|
||||
endif()
|
||||
|
||||
ly_get_list_relative_pal_filename(pal_dir ${CMAKE_CURRENT_LIST_DIR}/Platform/${PAL_PLATFORM_NAME})
|
||||
|
||||
ly_add_target(
|
||||
NAME AzQtComponents SHARED
|
||||
NAMESPACE AZ
|
||||
@@ -26,6 +28,7 @@ ly_add_target(
|
||||
AzQtComponents
|
||||
PUBLIC
|
||||
.
|
||||
${pal_dir}
|
||||
COMPILE_DEFINITIONS
|
||||
PRIVATE
|
||||
AZ_QT_COMPONENTS_EXPORT_SYMBOLS
|
||||
@@ -53,6 +56,7 @@ ly_add_target(
|
||||
.
|
||||
AzQtComponents
|
||||
AzQtComponents/Gallery
|
||||
${pal_dir}
|
||||
BUILD_DEPENDENCIES
|
||||
PRIVATE
|
||||
3rdParty::Qt::Svg
|
||||
@@ -86,6 +90,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
|
||||
PRIVATE
|
||||
Tests
|
||||
AzQtComponents
|
||||
${pal_dir}
|
||||
BUILD_DEPENDENCIES
|
||||
PRIVATE
|
||||
AZ::AzQtComponents
|
||||
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#define AZ_TRAIT_AZQTCOMPONENTS_FILE_DIALOG_APPLY_MISSING_EXTENSION 1
|
||||
#define AZ_TRAIT_AZQTCOMPONENTS_FILE_DIALOG_FILTER_CASE_SENSITIVITY QRegularExpression::NoPatternOption
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <AzQtComponents/AzQtComponents_Traits_Linux.h>
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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 <AzQtComponents/Utilities/DesktopUtilities.h>
|
||||
|
||||
#include <QDir>
|
||||
#include <QProcess>
|
||||
|
||||
namespace AzQtComponents
|
||||
{
|
||||
void ShowFileOnDesktop(const QString& path)
|
||||
{
|
||||
const char* defaultNautilusPath = "/usr/bin/nautilus";
|
||||
const char* defaultXdgOpenPath = "/usr/bin/xdg-open";
|
||||
|
||||
// Determine if Nautilus (for Gnome Desktops) is available because it supports opening the file manager
|
||||
// and selecting a specific file
|
||||
bool nautilusAvailable = QFileInfo(defaultNautilusPath).exists();
|
||||
|
||||
QFileInfo pathInfo(path);
|
||||
if (pathInfo.isDir())
|
||||
{
|
||||
QProcess::startDetached(defaultXdgOpenPath, { path });
|
||||
}
|
||||
else
|
||||
{
|
||||
if (nautilusAvailable)
|
||||
{
|
||||
QProcess::startDetached(defaultNautilusPath, { "--select", path });
|
||||
}
|
||||
else
|
||||
{
|
||||
QDir parentDir { pathInfo.dir() };
|
||||
QProcess::startDetached(defaultXdgOpenPath, { parentDir.path() });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QString fileBrowserActionName()
|
||||
{
|
||||
const char* exploreActionName = "Open in file browser";
|
||||
return QObject::tr(exploreActionName);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#define AZ_TRAIT_AZQTCOMPONENTS_FILE_DIALOG_APPLY_MISSING_EXTENSION 0
|
||||
#define AZ_TRAIT_AZQTCOMPONENTS_FILE_DIALOG_FILTER_CASE_SENSITIVITY QRegularExpression::NoPatternOption
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <AzQtComponents/AzQtComponents_Traits_Mac.h>
|
||||
-23
@@ -15,21 +15,6 @@ namespace AzQtComponents
|
||||
{
|
||||
void ShowFileOnDesktop(const QString& path)
|
||||
{
|
||||
#if defined(AZ_PLATFORM_WINDOWS)
|
||||
|
||||
// Launch explorer at the path provided
|
||||
QStringList args;
|
||||
if (!QFileInfo(path).isDir())
|
||||
{
|
||||
// Folders are just opened, files are selected
|
||||
args << "/select,";
|
||||
}
|
||||
args << QDir::toNativeSeparators(path);
|
||||
|
||||
QProcess::startDetached("explorer", args);
|
||||
|
||||
#else
|
||||
|
||||
if (QFileInfo(path).isDir())
|
||||
{
|
||||
QProcess::startDetached("/usr/bin/osascript", { "-e",
|
||||
@@ -43,19 +28,11 @@ namespace AzQtComponents
|
||||
|
||||
QProcess::startDetached("/usr/bin/osascript", { "-e",
|
||||
QStringLiteral("tell application \"Finder\" to activate") });
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
QString fileBrowserActionName()
|
||||
{
|
||||
#ifdef AZ_PLATFORM_WINDOWS
|
||||
const char* exploreActionName = "Open in Explorer";
|
||||
#elif defined(AZ_PLATFORM_MAC)
|
||||
const char* exploreActionName = "Open in Finder";
|
||||
#else
|
||||
const char* exploreActionName = "Open in file browser";
|
||||
#endif
|
||||
return QObject::tr(exploreActionName);
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <AzQtComponents/AzQtComponents_Traits_Windows.h>
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#define AZ_TRAIT_AZQTCOMPONENTS_FILE_DIALOG_APPLY_MISSING_EXTENSION 0
|
||||
#define AZ_TRAIT_AZQTCOMPONENTS_FILE_DIALOG_FILTER_CASE_SENSITIVITY QRegularExpression::CaseInsensitiveOption
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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 <AzQtComponents/Utilities/DesktopUtilities.h>
|
||||
|
||||
#include <QDir>
|
||||
#include <QProcess>
|
||||
|
||||
namespace AzQtComponents
|
||||
{
|
||||
void ShowFileOnDesktop(const QString& path)
|
||||
{
|
||||
// Launch explorer at the path provided
|
||||
QStringList args;
|
||||
if (!QFileInfo(path).isDir())
|
||||
{
|
||||
// Folders are just opened, files are selected
|
||||
args << "/select,";
|
||||
}
|
||||
args << QDir::toNativeSeparators(path);
|
||||
|
||||
QProcess::startDetached("explorer", args);
|
||||
}
|
||||
|
||||
QString fileBrowserActionName()
|
||||
{
|
||||
const char* exploreActionName = "Open in Explorer";
|
||||
return QObject::tr(exploreActionName);
|
||||
}
|
||||
}
|
||||
@@ -90,19 +90,16 @@ namespace AZ
|
||||
}
|
||||
}
|
||||
|
||||
//! Filter out integration tests from the test run
|
||||
void excludeIntegTests()
|
||||
{
|
||||
AddExcludeFilter("INTEG_*");
|
||||
AddExcludeFilter("Integ_*");
|
||||
}
|
||||
|
||||
void ApplyGlobalParameters(int* argc, char** argv)
|
||||
{
|
||||
// this is a hook that can be used to apply any other global non-google parameters
|
||||
// that we use.
|
||||
// this is a hook that can be used to apply any other global parameters that we use.
|
||||
AZ_UNUSED(argc);
|
||||
AZ_UNUSED(argv);
|
||||
|
||||
// Disable gtest catching unhandled exceptions, instead, AzTestRunner will do it through:
|
||||
// AZ::Debug::Trace::HandleExceptions(true). This gives us a stack trace when the exception
|
||||
// is thrown (googletest does not).
|
||||
testing::FLAGS_gtest_catch_exceptions = false;
|
||||
}
|
||||
|
||||
//! Print out parameters that are not used by the framework
|
||||
@@ -160,7 +157,6 @@ namespace AZ
|
||||
}
|
||||
|
||||
::testing::InitGoogleMock(&argc, argv);
|
||||
AZ::Test::excludeIntegTests();
|
||||
AZ::Test::ApplyGlobalParameters(&argc, argv);
|
||||
AZ::Test::printUnusedParametersWarning(argc, argv);
|
||||
AZ::Test::addTestEnvironments(m_envs);
|
||||
@@ -281,7 +277,6 @@ namespace AZ
|
||||
}
|
||||
}
|
||||
|
||||
AZ::Test::excludeIntegTests();
|
||||
AZ::Test::printUnusedParametersWarning(argc, argv);
|
||||
|
||||
return RUN_ALL_TESTS();
|
||||
|
||||
@@ -104,7 +104,6 @@ namespace AZ
|
||||
|
||||
void addTestEnvironment(ITestEnvironment* env);
|
||||
void addTestEnvironments(std::vector<ITestEnvironment*> envs);
|
||||
void excludeIntegTests();
|
||||
|
||||
//! A hook that can be used to read any other misc parameters and remove them before google sees them.
|
||||
//! Note that this modifies argc and argv to delete the parameters it consumes.
|
||||
@@ -266,7 +265,6 @@ namespace AZ
|
||||
::testing::TestEventListeners& listeners = testing::UnitTest::GetInstance()->listeners(); \
|
||||
listeners.Append(new AZ::Test::OutputEventListener); \
|
||||
} \
|
||||
AZ::Test::excludeIntegTests(); \
|
||||
AZ::Test::ApplyGlobalParameters(&argc, argv); \
|
||||
AZ::Test::printUnusedParametersWarning(argc, argv); \
|
||||
AZ::Test::addTestEnvironments({TEST_ENV}); \
|
||||
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
#include <AzCore/Outcome/Outcome.h>
|
||||
#include <AzToolsFramework/API/ViewportEditorModeTrackerNotificationBus.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
//! The AZ::Interface for component mode collection queries.
|
||||
class ComponentModeCollectionInterface
|
||||
{
|
||||
public:
|
||||
AZ_RTTI(ComponentModeCollectionInterface, "{DFAA4450-BBCD-47C0-9B91-FEA2DBD9B152}");
|
||||
|
||||
virtual ~ComponentModeCollectionInterface() = default;
|
||||
|
||||
//! Retrieves the list of all Component types (usually one).
|
||||
//! @note If called outside of component mode, an empty vector will be returned.
|
||||
virtual AZStd::vector<AZ::Uuid> GetComponentTypes() const = 0;
|
||||
};
|
||||
} // namespace AzToolsFramework
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/RTTI/BehaviorContext.h>
|
||||
#include <AzToolsFramework/API/ViewportEditorModeTrackerNotificationBus.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
void ViewportEditorModeNotifications::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
if (auto* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
|
||||
{
|
||||
behaviorContext->EBus<ViewportEditorModeNotificationsBus>("ViewportEditorModeNotificationsBus")
|
||||
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation)
|
||||
->Attribute(AZ::Script::Attributes::Category, "Editor")
|
||||
->Attribute(AZ::Script::Attributes::Module, "editor")
|
||||
->Event("OnEditorModeActivated", &ViewportEditorModeNotifications::OnEditorModeActivated)
|
||||
->Event("OnEditorModeDeactivated", &ViewportEditorModeNotifications::OnEditorModeDeactivated)
|
||||
;
|
||||
}
|
||||
}
|
||||
} // namespace AzToolsFramework
|
||||
+11
-4
@@ -34,6 +34,8 @@ namespace AzToolsFramework
|
||||
class ViewportEditorModesInterface
|
||||
{
|
||||
public:
|
||||
AZ_RTTI(ViewportEditorModesInterface, "{2421496C-4A46-41C9-8AEF-AE2B6E43E6CF}");
|
||||
|
||||
virtual ~ViewportEditorModesInterface() = default;
|
||||
|
||||
//! Returns true if the specified editor mode is active, otherwise false.
|
||||
@@ -41,8 +43,7 @@ namespace AzToolsFramework
|
||||
};
|
||||
|
||||
//! Provides a bus to notify when the different editor modes are entered/exit.
|
||||
class ViewportEditorModeNotifications
|
||||
: public AZ::EBusTraits
|
||||
class ViewportEditorModeNotifications : public AZ::EBusTraits
|
||||
{
|
||||
public:
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@@ -52,15 +53,21 @@ namespace AzToolsFramework
|
||||
using BusIdType = ViewportEditorModeTrackerInfo::IdType;
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AZ_RTTI(ViewportEditorModeNotifications, "{9469DE39-6C21-423C-94FA-EF3A9616B14F}", AZ::EBusTraits);
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
//! Notifies subscribers of the a given viewport to the activation of the specified editor mode.
|
||||
virtual void OnEditorModeActivated([[maybe_unused]] const ViewportEditorModesInterface& editorModeState, [[maybe_unused]] ViewportEditorMode mode)
|
||||
virtual void OnEditorModeActivated(
|
||||
[[maybe_unused]] const ViewportEditorModesInterface& editorModeState, [[maybe_unused]] ViewportEditorMode mode)
|
||||
{
|
||||
}
|
||||
|
||||
//! Notifies subscribers of the a given viewport to the deactivation of the specified editor mode.
|
||||
virtual void OnEditorModeDeactivated([[maybe_unused]] const ViewportEditorModesInterface& editorModeState, [[maybe_unused]] ViewportEditorMode mode)
|
||||
virtual void OnEditorModeDeactivated(
|
||||
[[maybe_unused]] const ViewportEditorModesInterface& editorModeState, [[maybe_unused]] ViewportEditorMode mode)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
using ViewportEditorModeNotificationsBus = AZ::EBus<ViewportEditorModeNotifications>;
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
+1
-1
@@ -53,7 +53,7 @@ namespace AzToolsFramework
|
||||
private slots:
|
||||
void SourceDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight);
|
||||
private:
|
||||
int m_numberOfItemsDisplayed = 50;
|
||||
AZ::u64 m_numberOfItemsDisplayed = 0;
|
||||
int m_displayedItemsCounter = 0;
|
||||
QPointer<AssetBrowserFilterModel> m_filterModel;
|
||||
QMap<int, QModelIndex> m_indexMap;
|
||||
|
||||
@@ -410,7 +410,7 @@ namespace AzToolsFramework
|
||||
filter.append(ext);
|
||||
if (i < n - 1)
|
||||
{
|
||||
filter.append(", ");
|
||||
filter.append(" ");
|
||||
}
|
||||
}
|
||||
filter.append(")");
|
||||
|
||||
+58
-15
@@ -137,6 +137,7 @@ namespace AzToolsFramework
|
||||
if (componentTypeIt == m_activeComponentTypes.end())
|
||||
{
|
||||
m_activeComponentTypes.push_back(componentType);
|
||||
m_viewportUiHandlers.emplace_back(componentType);
|
||||
}
|
||||
|
||||
// see if we already have a ComponentModeBuilder for the specific component on this entity
|
||||
@@ -203,6 +204,12 @@ namespace AzToolsFramework
|
||||
}
|
||||
}
|
||||
|
||||
AZStd::vector<AZ::Uuid> ComponentModeCollection::GetComponentTypes() const
|
||||
{
|
||||
// If in component mode, return the active component types, otherwise return an empty vector
|
||||
return InComponentMode() ? m_activeComponentTypes : AZStd::vector<AZ::Uuid>{};
|
||||
}
|
||||
|
||||
void ComponentModeCollection::BeginComponentMode()
|
||||
{
|
||||
m_selectedComponentModeIndex = 0;
|
||||
@@ -211,13 +218,6 @@ namespace AzToolsFramework
|
||||
|
||||
// notify listeners the editor has entered ComponentMode - listeners may
|
||||
// wish to modify state to indicate this (e.g. appearance, functionality etc.)
|
||||
EditorComponentModeNotificationBus::Event(
|
||||
GetEntityContextId(), &EditorComponentModeNotifications::EnteredComponentMode,
|
||||
m_activeComponentTypes);
|
||||
|
||||
// this call to activate the component mode editor state should eventually replace the bus call in
|
||||
// ComponentModeCollection::BeginComponentMode() to EditorComponentModeNotifications::EnteredComponentMode
|
||||
// such that all of the notifications for activating/deactivating the different editor modes are in a central location
|
||||
m_viewportEditorModeTracker->ActivateMode({ GetEntityContextId() }, ViewportEditorMode::Component);
|
||||
|
||||
// enable actions for the first/primary ComponentMode
|
||||
@@ -226,6 +226,7 @@ namespace AzToolsFramework
|
||||
if (!m_entitiesAndComponentModes.empty())
|
||||
{
|
||||
RefreshActions();
|
||||
PopulateViewportUi();
|
||||
}
|
||||
|
||||
// if entering ComponentMode not as an undo/redo step (an action was
|
||||
@@ -286,16 +287,12 @@ namespace AzToolsFramework
|
||||
componentModeCommand.release();
|
||||
}
|
||||
|
||||
// remove the component mode viewport border
|
||||
ViewportUi::ViewportUiRequestBus::Event(
|
||||
ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::RemoveViewportBorder);
|
||||
|
||||
// notify listeners the editor has left ComponentMode - listeners may
|
||||
// wish to modify state to indicate this (e.g. appearance, functionality etc.)
|
||||
EditorComponentModeNotificationBus::Event(
|
||||
GetEntityContextId(),
|
||||
&EditorComponentModeNotifications::LeftComponentMode,
|
||||
m_activeComponentTypes);
|
||||
|
||||
// this call to deactivate the component mode editor state should eventually replace the bus call in
|
||||
// ComponentModeCollection::EndComponentMode() to EditorComponentModeNotifications::LeftComponentMode
|
||||
// such that all of the notifications for activating/deactivating the different editor modes are in a central location
|
||||
m_viewportEditorModeTracker->DeactivateMode({ GetEntityContextId() }, ViewportEditorMode::Component);
|
||||
|
||||
// clear stored modes and builders for this ComponentMode
|
||||
@@ -310,6 +307,7 @@ namespace AzToolsFramework
|
||||
}
|
||||
m_entitiesAndComponentModeBuilders.clear();
|
||||
m_activeComponentTypes.clear();
|
||||
m_viewportUiHandlers.clear();
|
||||
|
||||
m_componentMode = false;
|
||||
m_selectedComponentModeIndex = 0;
|
||||
@@ -394,6 +392,24 @@ namespace AzToolsFramework
|
||||
return m_activeComponentTypes.size() > 1;
|
||||
}
|
||||
|
||||
static ComponentModeViewportUi* FindViewportUiHandlerForType(
|
||||
AZStd::vector<ComponentModeViewportUi>& viewportUiHandlers, const AZ::Uuid& componentType)
|
||||
{
|
||||
auto handler = AZStd::find_if(
|
||||
viewportUiHandlers.begin(), viewportUiHandlers.end(),
|
||||
[componentType](const ComponentModeViewportUi& handler)
|
||||
{
|
||||
return handler.GetComponentType() == componentType;
|
||||
});
|
||||
|
||||
if (handler == viewportUiHandlers.end())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return handler;
|
||||
}
|
||||
|
||||
bool ComponentModeCollection::ActiveComponentModeChanged(const AZ::Uuid& previousComponentType)
|
||||
{
|
||||
if (m_activeComponentTypes[m_selectedComponentModeIndex] != previousComponentType)
|
||||
@@ -419,6 +435,20 @@ namespace AzToolsFramework
|
||||
// replace the current component mode by invoking the builder
|
||||
// for the new 'active' component mode
|
||||
componentMode.m_componentMode = componentModeBuilder->m_componentModeBuilder();
|
||||
|
||||
// populate the viewport UI with the new component mode
|
||||
PopulateViewportUi();
|
||||
|
||||
// set the appropriate viewportUiHandler to active
|
||||
if (auto viewportUiHandler =
|
||||
FindViewportUiHandlerForType(m_viewportUiHandlers, m_activeComponentTypes[m_selectedComponentModeIndex]))
|
||||
{
|
||||
viewportUiHandler->SetComponentModeViewportUiActive(true);
|
||||
}
|
||||
|
||||
ViewportUi::ViewportUiRequestBus::Event(
|
||||
ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::CreateViewportBorder,
|
||||
componentMode.m_componentMode->GetComponentModeName().c_str());
|
||||
}
|
||||
|
||||
RefreshActions();
|
||||
@@ -528,5 +558,18 @@ namespace AzToolsFramework
|
||||
}
|
||||
}
|
||||
|
||||
void ComponentModeCollection::PopulateViewportUi()
|
||||
{
|
||||
// update viewport UI for new component type
|
||||
if (m_selectedComponentModeIndex < m_activeComponentTypes.size())
|
||||
{
|
||||
// iterate over all entities and their active Component Mode, populate viewport UI for the new mode
|
||||
for (auto& entityAndComponentMode : m_entitiesAndComponentModes)
|
||||
{
|
||||
// build viewport UI based on current state
|
||||
entityAndComponentMode.m_componentMode->PopulateViewportUi();
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace ComponentModeFramework
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
+5
@@ -9,6 +9,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
#include <AzToolsFramework/API/ComponentModeCollectionInterface.h>
|
||||
#include <AzToolsFramework/ComponentMode/ComponentModeViewportUi.h>
|
||||
#include <AzToolsFramework/ComponentMode/EditorComponentModeBus.h>
|
||||
|
||||
@@ -21,6 +22,7 @@ namespace AzToolsFramework
|
||||
{
|
||||
/// Manages all individual ComponentModes for a single instance of Editor wide ComponentMode.
|
||||
class ComponentModeCollection
|
||||
: public ComponentModeCollectionInterface
|
||||
{
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR_DECL
|
||||
@@ -89,6 +91,9 @@ namespace AzToolsFramework
|
||||
/// Called once each time a ComponentMode is added.
|
||||
void PopulateViewportUi();
|
||||
|
||||
// ComponentModeCollectionInterface overrides ...
|
||||
AZStd::vector<AZ::Uuid> GetComponentTypes() const override;
|
||||
|
||||
private:
|
||||
// Internal helper used by Select[|Prev|Next]ActiveComponentMode
|
||||
bool ActiveComponentModeChanged(const AZ::Uuid& previousComponentType);
|
||||
|
||||
+2
-14
@@ -24,18 +24,8 @@ namespace AzToolsFramework
|
||||
: public EditorComponentModeNotificationBus::Handler
|
||||
, public AZ::BehaviorEBusHandler
|
||||
{
|
||||
AZ_EBUS_BEHAVIOR_BINDER(EditorComponentModeNotificationBusHandler, "{AD2F4204-0913-4FC9-9A10-492538F60C70}", AZ::SystemAllocator,
|
||||
EnteredComponentMode, LeftComponentMode, ActiveComponentModeChanged);
|
||||
|
||||
void EnteredComponentMode(const AZStd::vector<AZ::Uuid>& componentTypes) override
|
||||
{
|
||||
Call(FN_EnteredComponentMode, componentTypes);
|
||||
}
|
||||
|
||||
void LeftComponentMode(const AZStd::vector<AZ::Uuid>& componentTypes) override
|
||||
{
|
||||
Call(FN_LeftComponentMode, componentTypes);
|
||||
}
|
||||
AZ_EBUS_BEHAVIOR_BINDER(
|
||||
EditorComponentModeNotificationBusHandler, "{AD2F4204-0913-4FC9-9A10-492538F60C70}", AZ::SystemAllocator, ActiveComponentModeChanged);
|
||||
|
||||
void ActiveComponentModeChanged(const AZ::Uuid& componentType) override
|
||||
{
|
||||
@@ -171,8 +161,6 @@ namespace AzToolsFramework
|
||||
->Attribute(AZ::Script::Attributes::Category, "Editor")
|
||||
->Attribute(AZ::Script::Attributes::Module, "editor")
|
||||
->Handler<Internal::EditorComponentModeNotificationBusHandler>()
|
||||
->Event("EnteredComponentMode", &EditorComponentModeNotifications::EnteredComponentMode)
|
||||
->Event("LeftComponentMode", &EditorComponentModeNotifications::LeftComponentMode)
|
||||
->Event("ActiveComponentModeChanged", &EditorComponentModeNotifications::ActiveComponentModeChanged)
|
||||
;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user