@@ -102,7 +102,7 @@ ly_add_target(
|
||||
3rdParty::Qt::Gui
|
||||
3rdParty::Qt::Widgets
|
||||
3rdParty::Qt::Concurrent
|
||||
3rdParty::tiff
|
||||
3rdParty::TIFF
|
||||
3rdParty::squish-ccr
|
||||
3rdParty::AWSNativeSDK::STS
|
||||
Legacy::CryCommon
|
||||
|
||||
@@ -19,10 +19,16 @@ namespace Editor
|
||||
if (GetIEditor()->IsInGameMode())
|
||||
{
|
||||
#ifdef PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
|
||||
AzFramework::XcbEventHandlerBus::Broadcast(&AzFramework::XcbEventHandler::HandleXcbEvent, static_cast<xcb_generic_event_t*>(message));
|
||||
// We need to handle RAW Input events in a separate loop. This is a workaround to enable XInput2 RAW Inputs using Editor mode.
|
||||
// TODO To have this call here might be not be perfect.
|
||||
AzFramework::XcbEventHandlerBus::Broadcast(&AzFramework::XcbEventHandler::PollSpecialEvents);
|
||||
|
||||
// Now handle the rest of the events.
|
||||
AzFramework::XcbEventHandlerBus::Broadcast(
|
||||
&AzFramework::XcbEventHandler::HandleXcbEvent, static_cast<xcb_generic_event_t*>(message));
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} // namespace Editor
|
||||
|
||||
@@ -57,6 +57,7 @@ AZ_POP_DISABLE_WARNING
|
||||
#include <AzFramework/StringFunc/StringFunc.h>
|
||||
#include <AzFramework/Terrain/TerrainDataRequestBus.h>
|
||||
#include <AzFramework/ProjectManager/ProjectManager.h>
|
||||
#include <AzFramework/Spawnable/RootSpawnableInterface.h>
|
||||
|
||||
// AzToolsFramework
|
||||
#include <AzToolsFramework/Component/EditorComponentAPIBus.h>
|
||||
@@ -3021,6 +3022,15 @@ CCryEditApp::ECreateLevelResult CCryEditApp::CreateLevel(const QString& levelNam
|
||||
bool bIsDocModified = GetIEditor()->GetDocument()->IsModified();
|
||||
OnSwitchPhysics();
|
||||
GetIEditor()->GetDocument()->SetModifiedFlag(bIsDocModified);
|
||||
|
||||
if (usePrefabSystemForLevels)
|
||||
{
|
||||
auto* rootSpawnableInterface = AzFramework::RootSpawnableInterface::Get();
|
||||
if (rootSpawnableInterface)
|
||||
{
|
||||
rootSpawnableInterface->ProcessSpawnableQueue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const QScopedValueRollback<bool> rollback(m_creatingNewLevel);
|
||||
|
||||
@@ -264,6 +264,9 @@ void EditorPreferencesDialog::SetFilter(const QString& filter)
|
||||
else if (m_currentPageItem)
|
||||
{
|
||||
m_currentPageItem->UpdateEditorFilter(ui->propertyEditor, m_filter);
|
||||
|
||||
// Refresh the Stylesheet - when using search functionality.
|
||||
AzQtComponents::StyleManager::repolishStyleSheet(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
// Editor
|
||||
#include "Settings.h"
|
||||
#include "EditorViewportSettings.h"
|
||||
|
||||
|
||||
|
||||
@@ -43,17 +44,16 @@ void CEditorPreferencesPage_Files::Reflect(AZ::SerializeContext& serialize)
|
||||
->Field("MaxCount", &AutoBackup::m_maxCount)
|
||||
->Field("RemindTime", &AutoBackup::m_remindTime);
|
||||
|
||||
serialize.Class<AssetBrowserSearch>()
|
||||
serialize.Class<AssetBrowserSettings>()
|
||||
->Version(1)
|
||||
->Field("Max number of items displayed", &AssetBrowserSearch::m_maxNumberOfItemsShownInSearch);
|
||||
->Field("MaxEntriesShownCount", &AssetBrowserSettings::m_maxNumberOfItemsShownInSearch);
|
||||
|
||||
serialize.Class<CEditorPreferencesPage_Files>()
|
||||
->Version(1)
|
||||
->Field("Files", &CEditorPreferencesPage_Files::m_files)
|
||||
->Field("Editors", &CEditorPreferencesPage_Files::m_editors)
|
||||
->Field("AutoBackup", &CEditorPreferencesPage_Files::m_autoBackup)
|
||||
->Field("AssetBrowserSearch", &CEditorPreferencesPage_Files::m_assetBrowserSearch);
|
||||
|
||||
->Field("AssetBrowserSettings", &CEditorPreferencesPage_Files::m_assetBrowserSettings);
|
||||
|
||||
AZ::EditContext* editContext = serialize.GetEditContext();
|
||||
if (editContext)
|
||||
@@ -85,9 +85,10 @@ void CEditorPreferencesPage_Files::Reflect(AZ::SerializeContext& serialize)
|
||||
->Attribute(AZ::Edit::Attributes::Max, 100)
|
||||
->DataElement(AZ::Edit::UIHandlers::SpinBox, &AutoBackup::m_remindTime, "Remind Time", "Auto Remind Every (Minutes)");
|
||||
|
||||
editContext->Class<AssetBrowserSearch>("Asset Browser Search View", "Asset Browser Search View")
|
||||
->DataElement(AZ::Edit::UIHandlers::SpinBox, &AssetBrowserSearch::m_maxNumberOfItemsShownInSearch, "Maximum number of displayed items",
|
||||
"Maximum number of displayed items displayed in the Search View")
|
||||
editContext->Class<AssetBrowserSettings>("Asset Browser Settings", "Asset Browser Settings")
|
||||
->DataElement(
|
||||
AZ::Edit::UIHandlers::SpinBox, &AssetBrowserSettings::m_maxNumberOfItemsShownInSearch, "Maximum number of displayed items",
|
||||
"Maximum number of items to display in the Search View.")
|
||||
->Attribute(AZ::Edit::Attributes::Min, 50)
|
||||
->Attribute(AZ::Edit::Attributes::Max, 5000);
|
||||
|
||||
@@ -97,7 +98,7 @@ void CEditorPreferencesPage_Files::Reflect(AZ::SerializeContext& serialize)
|
||||
->DataElement(AZ::Edit::UIHandlers::Default, &CEditorPreferencesPage_Files::m_files, "Files", "File Preferences")
|
||||
->DataElement(AZ::Edit::UIHandlers::Default, &CEditorPreferencesPage_Files::m_editors, "External Editors", "External Editors")
|
||||
->DataElement(AZ::Edit::UIHandlers::Default, &CEditorPreferencesPage_Files::m_autoBackup, "Auto Backup", "Auto Backup")
|
||||
->DataElement(AZ::Edit::UIHandlers::Default, &CEditorPreferencesPage_Files::m_assetBrowserSearch, "Asset Browser Search", "Asset Browser Search");
|
||||
->DataElement(AZ::Edit::UIHandlers::Default, &CEditorPreferencesPage_Files::m_assetBrowserSettings, "Asset Browser Settings","Asset Browser Settings");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,6 +118,7 @@ QIcon& CEditorPreferencesPage_Files::GetIcon()
|
||||
void CEditorPreferencesPage_Files::OnApply()
|
||||
{
|
||||
using namespace AzToolsFramework::SliceUtilities;
|
||||
|
||||
auto sliceSettings = AZ::UserSettings::CreateFind<SliceUserSettings>(AZ_CRC("SliceUserSettings", 0x055b32eb), AZ::UserSettings::CT_LOCAL);
|
||||
sliceSettings->m_autoNumber = m_files.m_autoNumberSlices;
|
||||
sliceSettings->m_saveLocation = m_files.m_saveLocation;
|
||||
@@ -137,7 +139,7 @@ void CEditorPreferencesPage_Files::OnApply()
|
||||
gSettings.autoBackupMaxCount = m_autoBackup.m_maxCount;
|
||||
gSettings.autoRemindTime = m_autoBackup.m_remindTime;
|
||||
|
||||
gSettings.maxNumberOfItemsShownInSearch = m_assetBrowserSearch.m_maxNumberOfItemsShownInSearch;
|
||||
SandboxEditor::SetMaxItemsShownInAssetBrowserSearch(m_assetBrowserSettings.m_maxNumberOfItemsShownInSearch);
|
||||
}
|
||||
|
||||
void CEditorPreferencesPage_Files::InitializeSettings()
|
||||
@@ -163,5 +165,5 @@ void CEditorPreferencesPage_Files::InitializeSettings()
|
||||
m_autoBackup.m_maxCount = gSettings.autoBackupMaxCount;
|
||||
m_autoBackup.m_remindTime = gSettings.autoRemindTime;
|
||||
|
||||
m_assetBrowserSearch.m_maxNumberOfItemsShownInSearch = gSettings.maxNumberOfItemsShownInSearch;
|
||||
m_assetBrowserSettings.m_maxNumberOfItemsShownInSearch = SandboxEditor::MaxItemsShownInAssetBrowserSearch();
|
||||
}
|
||||
|
||||
@@ -69,18 +69,15 @@ private:
|
||||
int m_remindTime;
|
||||
};
|
||||
|
||||
struct AssetBrowserSearch
|
||||
struct AssetBrowserSettings
|
||||
{
|
||||
AZ_TYPE_INFO(AssetBrowserSearch, "{9FBFCD24-9452-49DF-99F4-2711443CEAAE}")
|
||||
|
||||
int m_maxNumberOfItemsShownInSearch;
|
||||
AZ_TYPE_INFO(AssetBrowserSettings, "{5F407EC4-BBD1-4A87-92DB-D938D7127BB0}")
|
||||
AZ::u64 m_maxNumberOfItemsShownInSearch;
|
||||
};
|
||||
|
||||
Files m_files;
|
||||
ExternalEditors m_editors;
|
||||
AutoBackup m_autoBackup;
|
||||
AssetBrowserSearch m_assetBrowserSearch;
|
||||
AssetBrowserSettings m_assetBrowserSettings;
|
||||
QIcon m_icon;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
namespace SandboxEditor
|
||||
{
|
||||
constexpr AZStd::string_view AssetBrowserMaxItemsShownInSearchSetting = "/Amazon/Preferences/Editor/AssetBrowser/MaxItemsShowInSearch";
|
||||
constexpr AZStd::string_view GridSnappingSetting = "/Amazon/Preferences/Editor/GridSnapping";
|
||||
constexpr AZStd::string_view GridSizeSetting = "/Amazon/Preferences/Editor/GridSize";
|
||||
constexpr AZStd::string_view AngleSnappingSetting = "/Amazon/Preferences/Editor/AngleSnapping";
|
||||
@@ -110,6 +111,16 @@ namespace SandboxEditor
|
||||
return AZStd::make_unique<EditorViewportSettingsCallbacksImpl>();
|
||||
}
|
||||
|
||||
AZ::u64 MaxItemsShownInAssetBrowserSearch()
|
||||
{
|
||||
return GetRegistry(AssetBrowserMaxItemsShownInSearchSetting, aznumeric_cast<AZ::u64>(50));
|
||||
}
|
||||
|
||||
void SetMaxItemsShownInAssetBrowserSearch(const AZ::u64 numberOfItemsShown)
|
||||
{
|
||||
SetRegistry(AssetBrowserMaxItemsShownInSearchSetting, numberOfItemsShown);
|
||||
}
|
||||
|
||||
bool GridSnappingEnabled()
|
||||
{
|
||||
return GetRegistry(GridSnappingSetting, false);
|
||||
|
||||
@@ -32,6 +32,9 @@ namespace SandboxEditor
|
||||
//! event will fire when a value in the settings registry (editorpreferences.setreg) is modified.
|
||||
SANDBOX_API AZStd::unique_ptr<EditorViewportSettingsCallbacks> CreateEditorViewportSettingsCallbacks();
|
||||
|
||||
SANDBOX_API AZ::u64 MaxItemsShownInAssetBrowserSearch();
|
||||
SANDBOX_API void SetMaxItemsShownInAssetBrowserSearch(AZ::u64 numberOfItemsShown);
|
||||
|
||||
SANDBOX_API bool GridSnappingEnabled();
|
||||
SANDBOX_API void SetGridSnapping(bool enabled);
|
||||
|
||||
|
||||
@@ -108,15 +108,11 @@ CObjectManager::CObjectManager()
|
||||
|
||||
m_objectsByName.reserve(1024);
|
||||
LoadRegistry();
|
||||
|
||||
AzToolsFramework::ViewportEditorModeNotificationsBus::Handler::BusConnect(AzToolsFramework::GetEntityContextId());
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
CObjectManager::~CObjectManager()
|
||||
{
|
||||
AzToolsFramework::ViewportEditorModeNotificationsBus::Handler::BusDisconnect();
|
||||
|
||||
m_bExiting = true;
|
||||
SaveRegistry();
|
||||
DeleteAllObjects();
|
||||
@@ -2307,37 +2303,6 @@ void CObjectManager::SelectObjectInRect(CBaseObject* pObj, CViewport* view, HitC
|
||||
}
|
||||
}
|
||||
|
||||
void CObjectManager::OnEditorModeActivated(
|
||||
[[maybe_unused]] const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode)
|
||||
{
|
||||
if (mode == AzToolsFramework::ViewportEditorMode::Component)
|
||||
{
|
||||
// hide current gizmo for entity (translate/rotate/scale)
|
||||
IGizmoManager* gizmoManager = GetGizmoManager();
|
||||
const size_t gizmoCount = static_cast<size_t>(gizmoManager->GetGizmoCount());
|
||||
for (size_t i = 0; i < gizmoCount; ++i)
|
||||
{
|
||||
gizmoManager->RemoveGizmo(gizmoManager->GetGizmoByIndex(static_cast<int>(i)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CObjectManager::OnEditorModeDeactivated(
|
||||
[[maybe_unused]] const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode)
|
||||
{
|
||||
if (mode == AzToolsFramework::ViewportEditorMode::Component)
|
||||
{
|
||||
// show translate/rotate/scale gizmo again
|
||||
if (IGizmoManager* gizmoManager = GetGizmoManager())
|
||||
{
|
||||
if (CBaseObject* selectedObject = GetIEditor()->GetSelectedObject())
|
||||
{
|
||||
gizmoManager->AddGizmo(new CAxisGizmo(selectedObject));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
namespace
|
||||
{
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
#include "ObjectManagerEventBus.h"
|
||||
|
||||
#include <AzCore/std/smart_ptr/unique_ptr.h>
|
||||
#include <AzToolsFramework/API/ViewportEditorModeTrackerNotificationBus.h>
|
||||
#include <AzCore/EBus/EBus.h>
|
||||
#include <AzCore/Component/Component.h>
|
||||
#include <Include/SandboxAPI.h>
|
||||
@@ -59,7 +58,6 @@ public:
|
||||
*/
|
||||
class CObjectManager
|
||||
: public IObjectManager
|
||||
, private AzToolsFramework::ViewportEditorModeNotificationsBus::Handler
|
||||
{
|
||||
public:
|
||||
//! Selection functor callback.
|
||||
@@ -330,12 +328,6 @@ private:
|
||||
|
||||
void FindDisplayableObjects(DisplayContext& dc, bool bDisplay);
|
||||
|
||||
// ViewportEditorModeNotificationsBus overrides ...
|
||||
void OnEditorModeActivated(
|
||||
const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode) override;
|
||||
void OnEditorModeDeactivated(
|
||||
const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode) override;
|
||||
|
||||
private:
|
||||
typedef std::map<GUID, CBaseObjectPtr, guid_less_predicate> Objects;
|
||||
Objects m_objects;
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "EditorDefs.h"
|
||||
|
||||
#include "Settings.h"
|
||||
#include "EditorViewportSettings.h"
|
||||
|
||||
// Qt
|
||||
#include <QGuiApplication>
|
||||
@@ -487,7 +488,6 @@ void SEditorSettings::Save()
|
||||
SaveValue("Settings", "AutoBackupTime", autoBackupTime);
|
||||
SaveValue("Settings", "AutoBackupMaxCount", autoBackupMaxCount);
|
||||
SaveValue("Settings", "AutoRemindTime", autoRemindTime);
|
||||
SaveValue("Settings", "MaxDisplayedItemsNumInSearch", maxNumberOfItemsShownInSearch);
|
||||
SaveValue("Settings", "CameraMoveSpeed", cameraMoveSpeed);
|
||||
SaveValue("Settings", "CameraRotateSpeed", cameraRotateSpeed);
|
||||
SaveValue("Settings", "StylusMode", stylusMode);
|
||||
@@ -682,7 +682,6 @@ void SEditorSettings::Load()
|
||||
LoadValue("Settings", "AutoBackupTime", autoBackupTime);
|
||||
LoadValue("Settings", "AutoBackupMaxCount", autoBackupMaxCount);
|
||||
LoadValue("Settings", "AutoRemindTime", autoRemindTime);
|
||||
LoadValue("Settings", "MaxDisplayedItemsNumInSearch", maxNumberOfItemsShownInSearch);
|
||||
LoadValue("Settings", "CameraMoveSpeed", cameraMoveSpeed);
|
||||
LoadValue("Settings", "CameraRotateSpeed", cameraRotateSpeed);
|
||||
LoadValue("Settings", "StylusMode", stylusMode);
|
||||
@@ -1174,7 +1173,7 @@ AzToolsFramework::ConsoleColorTheme SEditorSettings::GetConsoleColorTheme() cons
|
||||
return consoleBackgroundColorTheme;
|
||||
}
|
||||
|
||||
int SEditorSettings::GetMaxNumberOfItemsShownInSearchView() const
|
||||
AZ::u64 SEditorSettings::GetMaxNumberOfItemsShownInSearchView() const
|
||||
{
|
||||
return SEditorSettings::maxNumberOfItemsShownInSearch;
|
||||
return SandboxEditor::MaxItemsShownInAssetBrowserSearch();
|
||||
}
|
||||
|
||||
@@ -279,7 +279,7 @@ AZ_POP_DISABLE_DLL_EXPORT_BASECLASS_WARNING
|
||||
SettingOutcome GetValue(const AZStd::string_view path) override;
|
||||
SettingOutcome SetValue(const AZStd::string_view path, const AZStd::any& value) override;
|
||||
AzToolsFramework::ConsoleColorTheme GetConsoleColorTheme() const override;
|
||||
int GetMaxNumberOfItemsShownInSearchView() const override;
|
||||
AZ::u64 GetMaxNumberOfItemsShownInSearchView() const override;
|
||||
|
||||
void ConvertPath(const AZStd::string_view sourcePath, AZStd::string& category, AZStd::string& attribute);
|
||||
|
||||
@@ -353,14 +353,6 @@ AZ_POP_DISABLE_DLL_EXPORT_BASECLASS_WARNING
|
||||
int autoRemindTime;
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Asset Browser Search View.
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//! Current maximum number of items that can be displayed in the AssetBrowser Search View.
|
||||
int maxNumberOfItemsShownInSearch;
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
//! If true preview windows is displayed when browsing geometries.
|
||||
bool bPreviewGeometryWindow;
|
||||
|
||||
|
||||
@@ -1195,7 +1195,7 @@ bool CFileUtil::IsFileExclusivelyAccessable(const QString& strFilePath)
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool CFileUtil::CreatePath(const QString& strPath)
|
||||
{
|
||||
#if defined(AZ_PLATFORM_MAC)
|
||||
#if !AZ_TRAIT_OS_USE_WINDOWS_FILE_PATHS
|
||||
bool pathCreated = true;
|
||||
|
||||
QString cleanPath = QDir::cleanPath(strPath);
|
||||
@@ -1252,7 +1252,7 @@ bool CFileUtil::CreatePath(const QString& strPath)
|
||||
}
|
||||
|
||||
return true;
|
||||
#endif
|
||||
#endif // !AZ_TRAIT_OS_USE_WINDOWS_FILE_PATHS
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
+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
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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,6 +72,7 @@ set(FILES
|
||||
Debug/AssetTracking.cpp
|
||||
Debug/LocalFileEventLoggerTests.cpp
|
||||
Debug/Trace.cpp
|
||||
Debug/UnhandledExceptions.cpp
|
||||
Name/NameJsonSerializerTests.cpp
|
||||
Name/NameTests.cpp
|
||||
RTTI/TypeSafeIntegralTests.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();
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
+22
-20
@@ -12,7 +12,6 @@
|
||||
|
||||
#include <xcb/xcb.h>
|
||||
|
||||
#include <AzCore/UserSettings/UserSettingsComponent.h>
|
||||
#include <AzFramework/XcbApplication.h>
|
||||
#include <AzFramework/XcbInputDeviceKeyboard.h>
|
||||
#include <AzFramework/Input/Buses/Notifications/InputTextNotificationBus.h>
|
||||
@@ -20,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)
|
||||
@@ -33,6 +33,7 @@ namespace AzFramework
|
||||
class XcbInputDeviceKeyboardTests
|
||||
: public XcbBaseTestFixture
|
||||
{
|
||||
public:
|
||||
void SetUp() override
|
||||
{
|
||||
using testing::Return;
|
||||
@@ -123,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
|
||||
@@ -195,27 +205,23 @@ namespace AzFramework
|
||||
EXPECT_CALL(m_interface, xkb_state_key_get_one_sym(&m_xkbState, s_keycodeForAKey))
|
||||
.Times(2);
|
||||
|
||||
Application application;
|
||||
application.Start({}, {});
|
||||
AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize);
|
||||
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)
|
||||
@@ -420,17 +426,13 @@ namespace AzFramework
|
||||
EXPECT_CALL(textListener, OnInputTextEvent(StrEq("a"), _)).Times(1);
|
||||
EXPECT_CALL(textListener, OnInputTextEvent(StrEq("A"), _)).Times(1);
|
||||
|
||||
Application application;
|
||||
application.Start({}, {});
|
||||
AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize);
|
||||
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);
|
||||
|
||||
@@ -12,6 +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,6 +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
|
||||
)
|
||||
|
||||
+1
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
+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);
|
||||
}
|
||||
}
|
||||
-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);
|
||||
}
|
||||
}
|
||||
+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}); \
|
||||
|
||||
+6
-4
@@ -43,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:
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@@ -58,14 +57,17 @@ namespace AzToolsFramework
|
||||
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;
|
||||
|
||||
+52
@@ -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
|
||||
@@ -225,6 +226,7 @@ namespace AzToolsFramework
|
||||
if (!m_entitiesAndComponentModes.empty())
|
||||
{
|
||||
RefreshActions();
|
||||
PopulateViewportUi();
|
||||
}
|
||||
|
||||
// if entering ComponentMode not as an undo/redo step (an action was
|
||||
@@ -285,6 +287,10 @@ 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.)
|
||||
m_viewportEditorModeTracker->DeactivateMode({ GetEntityContextId() }, ViewportEditorMode::Component);
|
||||
@@ -301,6 +307,7 @@ namespace AzToolsFramework
|
||||
}
|
||||
m_entitiesAndComponentModeBuilders.clear();
|
||||
m_activeComponentTypes.clear();
|
||||
m_viewportUiHandlers.clear();
|
||||
|
||||
m_componentMode = false;
|
||||
m_selectedComponentModeIndex = 0;
|
||||
@@ -385,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)
|
||||
@@ -410,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();
|
||||
@@ -519,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
|
||||
|
||||
+1
-1
@@ -55,7 +55,7 @@ namespace AzToolsFramework
|
||||
GetEntityComponentIdPair(), elementIdsToDisplay);
|
||||
// create the component mode border with the specific name for this component mode
|
||||
ViewportUi::ViewportUiRequestBus::Event(
|
||||
ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::CreateComponentModeBorder,
|
||||
ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::CreateViewportBorder,
|
||||
GetComponentModeName());
|
||||
// set the EntityComponentId for this ComponentMode to active in the ComponentModeViewportUi system
|
||||
ComponentModeViewportUiRequestBus::Event(
|
||||
|
||||
@@ -38,7 +38,7 @@ namespace AzToolsFramework
|
||||
virtual SettingOutcome GetValue(const AZStd::string_view path) = 0;
|
||||
virtual SettingOutcome SetValue(const AZStd::string_view path, const AZStd::any& value) = 0;
|
||||
virtual ConsoleColorTheme GetConsoleColorTheme() const = 0;
|
||||
virtual int GetMaxNumberOfItemsShownInSearchView() const = 0;
|
||||
virtual AZ::u64 GetMaxNumberOfItemsShownInSearchView() const = 0;
|
||||
};
|
||||
|
||||
using EditorSettingsAPIBus = AZ::EBus<EditorSettingsAPIRequests>;
|
||||
|
||||
+4
-2
@@ -28,8 +28,10 @@ namespace AzToolsFramework
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//! Triggered when the editor focus is changed to a different entity.
|
||||
//! @param entityId The entity the focus has been moved to.
|
||||
virtual void OnEditorFocusChanged(AZ::EntityId entityId) = 0;
|
||||
//! @param previousFocusEntityId The entity the focus has been moved from.
|
||||
//! @param newFocusEntityId The entity the focus has been moved to.
|
||||
virtual void OnEditorFocusChanged(
|
||||
[[maybe_unused]] AZ::EntityId previousFocusEntityId, [[maybe_unused]] AZ::EntityId newFocusEntityId) {}
|
||||
|
||||
protected:
|
||||
~FocusModeNotifications() = default;
|
||||
|
||||
+2
-1
@@ -83,8 +83,9 @@ namespace AzToolsFramework
|
||||
}
|
||||
}
|
||||
|
||||
AZ::EntityId previousFocusEntityId = m_focusRoot;
|
||||
m_focusRoot = entityId;
|
||||
FocusModeNotificationBus::Broadcast(&FocusModeNotifications::OnEditorFocusChanged, m_focusRoot);
|
||||
FocusModeNotificationBus::Broadcast(&FocusModeNotifications::OnEditorFocusChanged, previousFocusEntityId, m_focusRoot);
|
||||
}
|
||||
|
||||
void FocusModeSystemComponent::ClearFocusRoot([[maybe_unused]] AzFramework::EntityContextId entityContextId)
|
||||
|
||||
+1
-1
@@ -116,7 +116,7 @@ namespace AzToolsFramework
|
||||
{
|
||||
return AZ::Intersect::IntersectRayBox(
|
||||
rayOrigin, rayDirection, m_center, m_axis1, m_axis2, m_axis3, m_halfExtents.GetX(), m_halfExtents.GetY(),
|
||||
m_halfExtents.GetZ(), rayIntersectionDistance) > 0;
|
||||
m_halfExtents.GetZ(), rayIntersectionDistance);
|
||||
}
|
||||
|
||||
void ManipulatorBoundBox::SetShapeData(const BoundRequestShapeBase& shapeData)
|
||||
|
||||
@@ -262,7 +262,7 @@ namespace AzToolsFramework
|
||||
|
||||
if (assetId.IsValid())
|
||||
{
|
||||
asset.Create(assetId, true);
|
||||
asset.Create(assetId, false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -8,12 +8,14 @@
|
||||
|
||||
#include <AzToolsFramework/Prefab/PrefabFocusHandler.h>
|
||||
|
||||
#include <AzToolsFramework/Commands/SelectionCommand.h>
|
||||
#include <AzToolsFramework/ContainerEntity/ContainerEntityInterface.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
|
||||
#include <AzToolsFramework/Entity/PrefabEditorEntityOwnershipInterface.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/Instance.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/InstanceEntityMapperInterface.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabFocusNotificationBus.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabFocusUndo.h>
|
||||
|
||||
namespace AzToolsFramework::Prefab
|
||||
{
|
||||
@@ -28,10 +30,12 @@ namespace AzToolsFramework::Prefab
|
||||
|
||||
EditorEntityContextNotificationBus::Handler::BusConnect();
|
||||
AZ::Interface<PrefabFocusInterface>::Register(this);
|
||||
AZ::Interface<PrefabFocusPublicInterface>::Register(this);
|
||||
}
|
||||
|
||||
PrefabFocusHandler::~PrefabFocusHandler()
|
||||
{
|
||||
AZ::Interface<PrefabFocusPublicInterface>::Unregister(this);
|
||||
AZ::Interface<PrefabFocusInterface>::Unregister(this);
|
||||
EditorEntityContextNotificationBus::Handler::BusDisconnect();
|
||||
}
|
||||
@@ -61,6 +65,44 @@ namespace AzToolsFramework::Prefab
|
||||
}
|
||||
|
||||
PrefabFocusOperationResult PrefabFocusHandler::FocusOnOwningPrefab(AZ::EntityId entityId)
|
||||
{
|
||||
// Initialize Undo Batch object
|
||||
ScopedUndoBatch undoBatch("Edit Prefab");
|
||||
|
||||
// Clear selection
|
||||
{
|
||||
const EntityIdList selectedEntities = EntityIdList{};
|
||||
auto selectionUndo = aznew SelectionCommand(selectedEntities, "Clear Selection");
|
||||
selectionUndo->SetParent(undoBatch.GetUndoBatch());
|
||||
ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequestBus::Events::SetSelectedEntities, selectedEntities);
|
||||
}
|
||||
|
||||
// Edit Prefab
|
||||
{
|
||||
auto editUndo = aznew PrefabFocusUndo("Edit Prefab");
|
||||
editUndo->Capture(entityId);
|
||||
editUndo->SetParent(undoBatch.GetUndoBatch());
|
||||
ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequestBus::Events::RunRedoSeparately, editUndo);
|
||||
}
|
||||
|
||||
return AZ::Success();
|
||||
}
|
||||
|
||||
PrefabFocusOperationResult PrefabFocusHandler::FocusOnPathIndex([[maybe_unused]] AzFramework::EntityContextId entityContextId, int index)
|
||||
{
|
||||
if (index < 0 || index >= m_instanceFocusVector.size())
|
||||
{
|
||||
return AZ::Failure(AZStd::string("Prefab Focus Handler: Invalid index on FocusOnPathIndex."));
|
||||
}
|
||||
|
||||
InstanceOptionalReference focusedInstance = m_instanceFocusVector[index];
|
||||
|
||||
FocusOnOwningPrefab(focusedInstance->get().GetContainerEntityId());
|
||||
|
||||
return AZ::Success();
|
||||
}
|
||||
|
||||
PrefabFocusOperationResult PrefabFocusHandler::FocusOnPrefabInstanceOwningEntityId(AZ::EntityId entityId)
|
||||
{
|
||||
InstanceOptionalReference focusedInstance;
|
||||
|
||||
@@ -85,18 +127,6 @@ namespace AzToolsFramework::Prefab
|
||||
return FocusOnPrefabInstance(focusedInstance);
|
||||
}
|
||||
|
||||
PrefabFocusOperationResult PrefabFocusHandler::FocusOnPathIndex([[maybe_unused]] AzFramework::EntityContextId entityContextId, int index)
|
||||
{
|
||||
if (index < 0 || index >= m_instanceFocusVector.size())
|
||||
{
|
||||
return AZ::Failure(AZStd::string("Prefab Focus Handler: Invalid index on FocusOnPathIndex."));
|
||||
}
|
||||
|
||||
InstanceOptionalReference focusedInstance = m_instanceFocusVector[index];
|
||||
|
||||
return FocusOnPrefabInstance(focusedInstance);
|
||||
}
|
||||
|
||||
PrefabFocusOperationResult PrefabFocusHandler::FocusOnPrefabInstance(InstanceOptionalReference focusedInstance)
|
||||
{
|
||||
if (!focusedInstance.has_value())
|
||||
@@ -122,17 +152,10 @@ namespace AzToolsFramework::Prefab
|
||||
if (focusedInstance->get().GetParentInstance() != AZStd::nullopt)
|
||||
{
|
||||
containerEntityId = focusedInstance->get().GetContainerEntityId();
|
||||
|
||||
// Select the container entity
|
||||
AzToolsFramework::SelectEntity(containerEntityId);
|
||||
}
|
||||
else
|
||||
{
|
||||
containerEntityId = AZ::EntityId();
|
||||
|
||||
// Clear the selection
|
||||
AzToolsFramework::SelectEntities({});
|
||||
|
||||
}
|
||||
|
||||
// Focus on the descendants of the container entity
|
||||
@@ -161,6 +184,17 @@ namespace AzToolsFramework::Prefab
|
||||
return m_focusedInstance;
|
||||
}
|
||||
|
||||
AZ::EntityId PrefabFocusHandler::GetFocusedPrefabContainerEntityId([[maybe_unused]] AzFramework::EntityContextId entityContextId) const
|
||||
{
|
||||
if (!m_focusedInstance.has_value())
|
||||
{
|
||||
// PrefabFocusHandler has not been initialized yet.
|
||||
return AZ::EntityId();
|
||||
}
|
||||
|
||||
return m_focusedInstance->get().GetContainerEntityId();
|
||||
}
|
||||
|
||||
bool PrefabFocusHandler::IsOwningPrefabBeingFocused(AZ::EntityId entityId) const
|
||||
{
|
||||
if (!m_focusedInstance.has_value())
|
||||
@@ -200,7 +234,7 @@ namespace AzToolsFramework::Prefab
|
||||
m_instanceFocusVector.clear();
|
||||
|
||||
// Focus on the root prefab (AZ::EntityId() will default to it)
|
||||
FocusOnOwningPrefab(AZ::EntityId());
|
||||
FocusOnPrefabInstanceOwningEntityId(AZ::EntityId());
|
||||
}
|
||||
|
||||
void PrefabFocusHandler::RefreshInstanceFocusList()
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
|
||||
#include <AzToolsFramework/FocusMode/FocusModeInterface.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabFocusInterface.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabFocusPublicInterface.h>
|
||||
#include <AzToolsFramework/Prefab/Template/Template.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
@@ -28,6 +29,7 @@ namespace AzToolsFramework::Prefab
|
||||
//! Handles Prefab Focus mode, determining which prefab file entity changes will target.
|
||||
class PrefabFocusHandler final
|
||||
: private PrefabFocusInterface
|
||||
, private PrefabFocusPublicInterface
|
||||
, private EditorEntityContextNotificationBus::Handler
|
||||
{
|
||||
public:
|
||||
@@ -39,10 +41,14 @@ namespace AzToolsFramework::Prefab
|
||||
void Initialize();
|
||||
|
||||
// PrefabFocusInterface overrides ...
|
||||
PrefabFocusOperationResult FocusOnOwningPrefab(AZ::EntityId entityId) override;
|
||||
PrefabFocusOperationResult FocusOnPathIndex(AzFramework::EntityContextId entityContextId, int index) override;
|
||||
PrefabFocusOperationResult FocusOnPrefabInstanceOwningEntityId(AZ::EntityId entityId) override;
|
||||
TemplateId GetFocusedPrefabTemplateId(AzFramework::EntityContextId entityContextId) const override;
|
||||
InstanceOptionalReference GetFocusedPrefabInstance(AzFramework::EntityContextId entityContextId) const override;
|
||||
|
||||
// PrefabFocusPublicInterface overrides ...
|
||||
PrefabFocusOperationResult FocusOnOwningPrefab(AZ::EntityId entityId) override;
|
||||
PrefabFocusOperationResult FocusOnPathIndex(AzFramework::EntityContextId entityContextId, int index) override;
|
||||
AZ::EntityId GetFocusedPrefabContainerEntityId(AzFramework::EntityContextId entityContextId) const override;
|
||||
bool IsOwningPrefabBeingFocused(AZ::EntityId entityId) const override;
|
||||
const AZ::IO::Path& GetPrefabFocusPath(AzFramework::EntityContextId entityContextId) const override;
|
||||
const int GetPrefabFocusPathLength(AzFramework::EntityContextId entityContextId) const override;
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace AzToolsFramework::Prefab
|
||||
{
|
||||
using PrefabFocusOperationResult = AZ::Outcome<void, AZStd::string>;
|
||||
|
||||
//! Interface to handle operations related to the Prefab Focus system.
|
||||
//! Interface to handle internal operations related to the Prefab Focus system.
|
||||
class PrefabFocusInterface
|
||||
{
|
||||
public:
|
||||
@@ -28,29 +28,13 @@ namespace AzToolsFramework::Prefab
|
||||
|
||||
//! Set the focused prefab instance to the owning instance of the entityId provided.
|
||||
//! @param entityId The entityId of the entity whose owning instance we want the prefab system to focus on.
|
||||
virtual PrefabFocusOperationResult FocusOnOwningPrefab(AZ::EntityId entityId) = 0;
|
||||
|
||||
//! Set the focused prefab instance to the instance at position index of the current path.
|
||||
//! @param index The index of the instance in the current path that we want the prefab system to focus on.
|
||||
virtual PrefabFocusOperationResult FocusOnPathIndex(AzFramework::EntityContextId entityContextId, int index) = 0;
|
||||
virtual PrefabFocusOperationResult FocusOnPrefabInstanceOwningEntityId(AZ::EntityId entityId) = 0;
|
||||
|
||||
//! Returns the template id of the instance the prefab system is focusing on.
|
||||
virtual TemplateId GetFocusedPrefabTemplateId(AzFramework::EntityContextId entityContextId) const = 0;
|
||||
|
||||
//! Returns a reference to the instance the prefab system is focusing on.
|
||||
virtual InstanceOptionalReference GetFocusedPrefabInstance(AzFramework::EntityContextId entityContextId) const = 0;
|
||||
|
||||
//! Returns whether the entity belongs to the instance that is being focused on, or one of its descendants.
|
||||
//! @param entityId The entityId of the queried entity.
|
||||
//! @return true if the entity belongs to the focused instance or one of its descendants, false otherwise.
|
||||
virtual bool IsOwningPrefabBeingFocused(AZ::EntityId entityId) const = 0;
|
||||
|
||||
//! Returns the path from the root instance to the currently focused instance.
|
||||
//! @return A path composed from the names of the container entities for the instance path.
|
||||
virtual const AZ::IO::Path& GetPrefabFocusPath(AzFramework::EntityContextId entityContextId) const = 0;
|
||||
|
||||
//! Returns the size of the path to the currently focused instance.
|
||||
virtual const int GetPrefabFocusPathLength(AzFramework::EntityContextId entityContextId) const = 0;
|
||||
};
|
||||
|
||||
} // namespace AzToolsFramework::Prefab
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
|
||||
#include <AzFramework/Entity/EntityContext.h>
|
||||
|
||||
#include <AzToolsFramework/Prefab/Instance/Instance.h>
|
||||
#include <AzToolsFramework/Prefab/Template/Template.h>
|
||||
|
||||
namespace AzToolsFramework::Prefab
|
||||
{
|
||||
using PrefabFocusOperationResult = AZ::Outcome<void, AZStd::string>;
|
||||
|
||||
//! Public Interface for external systems to utilize the Prefab Focus system.
|
||||
class PrefabFocusPublicInterface
|
||||
{
|
||||
public:
|
||||
AZ_RTTI(PrefabFocusPublicInterface, "{53EE1D18-A41F-4DB1-9B73-9448F425722E}");
|
||||
|
||||
//! Set the focused prefab instance to the owning instance of the entityId provided. Supports undo/redo.
|
||||
//! @param entityId The entityId of the entity whose owning instance we want the prefab system to focus on.
|
||||
virtual PrefabFocusOperationResult FocusOnOwningPrefab(AZ::EntityId entityId) = 0;
|
||||
|
||||
//! Set the focused prefab instance to the instance at position index of the current path. Supports undo/redo.
|
||||
//! @param index The index of the instance in the current path that we want the prefab system to focus on.
|
||||
virtual PrefabFocusOperationResult FocusOnPathIndex(AzFramework::EntityContextId entityContextId, int index) = 0;
|
||||
|
||||
//! Returns the entity id of the container entity for the instance the prefab system is focusing on.
|
||||
virtual AZ::EntityId GetFocusedPrefabContainerEntityId(AzFramework::EntityContextId entityContextId) const = 0;
|
||||
|
||||
//! Returns whether the entity belongs to the instance that is being focused on, or one of its descendants.
|
||||
//! @param entityId The entityId of the queried entity.
|
||||
//! @return true if the entity belongs to the focused instance or one of its descendants, false otherwise.
|
||||
virtual bool IsOwningPrefabBeingFocused(AZ::EntityId entityId) const = 0;
|
||||
|
||||
//! Returns the path from the root instance to the currently focused instance.
|
||||
//! @return A path composed from the names of the container entities for the instance path.
|
||||
virtual const AZ::IO::Path& GetPrefabFocusPath(AzFramework::EntityContextId entityContextId) const = 0;
|
||||
|
||||
//! Returns the size of the path to the currently focused instance.
|
||||
virtual const int GetPrefabFocusPathLength(AzFramework::EntityContextId entityContextId) const = 0;
|
||||
};
|
||||
|
||||
} // namespace AzToolsFramework::Prefab
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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 <AzToolsFramework/Prefab/PrefabFocusUndo.h>
|
||||
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabFocusInterface.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabFocusPublicInterface.h>
|
||||
|
||||
namespace AzToolsFramework::Prefab
|
||||
{
|
||||
PrefabFocusUndo::PrefabFocusUndo(const AZStd::string& undoOperationName)
|
||||
: UndoSystem::URSequencePoint(undoOperationName)
|
||||
{
|
||||
m_prefabFocusInterface = AZ::Interface<PrefabFocusInterface>::Get();
|
||||
AZ_Assert(m_prefabFocusInterface, "PrefabFocusUndo - Failed to grab prefab focus interface");
|
||||
|
||||
m_prefabFocusPublicInterface = AZ::Interface<PrefabFocusPublicInterface>::Get();
|
||||
AZ_Assert(m_prefabFocusPublicInterface, "PrefabFocusUndo - Failed to grab prefab focus public interface");
|
||||
}
|
||||
|
||||
bool PrefabFocusUndo::Changed() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void PrefabFocusUndo::Capture(AZ::EntityId entityId)
|
||||
{
|
||||
auto entityContextId = AzFramework::EntityContextId::CreateNull();
|
||||
EditorEntityContextRequestBus::BroadcastResult(entityContextId, &EditorEntityContextRequests::GetEditorEntityContextId);
|
||||
|
||||
m_beforeEntityId = m_prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(entityContextId);
|
||||
m_afterEntityId = entityId;
|
||||
}
|
||||
|
||||
void PrefabFocusUndo::Undo()
|
||||
{
|
||||
m_prefabFocusInterface->FocusOnPrefabInstanceOwningEntityId(m_beforeEntityId);
|
||||
}
|
||||
|
||||
void PrefabFocusUndo::Redo()
|
||||
{
|
||||
m_prefabFocusInterface->FocusOnPrefabInstanceOwningEntityId(m_afterEntityId);
|
||||
}
|
||||
|
||||
} // namespace AzToolsFramework::Prefab
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Component/EntityId.h>
|
||||
#include <AzToolsFramework/Undo/UndoSystem.h>
|
||||
|
||||
namespace AzToolsFramework::Prefab
|
||||
{
|
||||
class PrefabFocusInterface;
|
||||
class PrefabFocusPublicInterface;
|
||||
|
||||
//! Undo node for prefab focus change operations.
|
||||
class PrefabFocusUndo
|
||||
: public UndoSystem::URSequencePoint
|
||||
{
|
||||
public:
|
||||
explicit PrefabFocusUndo(const AZStd::string& undoOperationName);
|
||||
|
||||
bool Changed() const override;
|
||||
void Capture(AZ::EntityId entityId);
|
||||
|
||||
void Undo() override;
|
||||
void Redo() override;
|
||||
|
||||
protected:
|
||||
PrefabFocusInterface* m_prefabFocusInterface = nullptr;
|
||||
PrefabFocusPublicInterface* m_prefabFocusPublicInterface = nullptr;
|
||||
|
||||
AZ::EntityId m_beforeEntityId;
|
||||
AZ::EntityId m_afterEntityId;
|
||||
};
|
||||
} // namespace AzToolsFramework::Prefab
|
||||
@@ -43,6 +43,12 @@ namespace AzToolsFramework
|
||||
m_instanceToTemplateInterface = AZ::Interface<InstanceToTemplateInterface>::Get();
|
||||
AZ_Assert(m_instanceToTemplateInterface, "PrefabPublicHandler - Could not retrieve instance of InstanceToTemplateInterface");
|
||||
|
||||
m_prefabFocusInterface = AZ::Interface<PrefabFocusInterface>::Get();
|
||||
AZ_Assert(m_prefabFocusInterface, "Could not get PrefabFocusInterface on PrefabPublicHandler construction.");
|
||||
|
||||
m_prefabFocusPublicInterface = AZ::Interface<PrefabFocusPublicInterface>::Get();
|
||||
AZ_Assert(m_prefabFocusPublicInterface, "Could not get PrefabFocusPublicInterface on PrefabPublicHandler construction.");
|
||||
|
||||
m_prefabLoaderInterface = AZ::Interface<PrefabLoaderInterface>::Get();
|
||||
AZ_Assert(m_prefabLoaderInterface, "Could not get PrefabLoaderInterface on PrefabPublicHandler construction.");
|
||||
|
||||
@@ -552,6 +558,13 @@ namespace AzToolsFramework
|
||||
|
||||
PrefabEntityResult PrefabPublicHandler::CreateEntity(AZ::EntityId parentId, const AZ::Vector3& position)
|
||||
{
|
||||
// If the parent is invalid, parent to the container of the currently focused prefab.
|
||||
if (!parentId.IsValid())
|
||||
{
|
||||
AzFramework::EntityContextId editorEntityContextId = AzToolsFramework::GetEntityContextId();
|
||||
parentId = m_prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(editorEntityContextId);
|
||||
}
|
||||
|
||||
InstanceOptionalReference owningInstanceOfParentEntity = GetOwnerInstanceByEntityId(parentId);
|
||||
if (!owningInstanceOfParentEntity)
|
||||
{
|
||||
@@ -968,13 +981,13 @@ namespace AzToolsFramework
|
||||
return AZ::Failure(AZStd::string("No entities to duplicate."));
|
||||
}
|
||||
|
||||
const EntityIdList entityIdsNoLevelInstance = GenerateEntityIdListWithoutLevelInstance(entityIds);
|
||||
if (entityIdsNoLevelInstance.empty())
|
||||
const EntityIdList entityIdsNoFocusContainer = GenerateEntityIdListWithoutFocusedInstanceContainer(entityIds);
|
||||
if (entityIdsNoFocusContainer.empty())
|
||||
{
|
||||
return AZ::Failure(AZStd::string("No entities to duplicate because only instance selected is the level instance."));
|
||||
return AZ::Failure(AZStd::string("No entities to duplicate because only instance selected is the container entity of the focused instance."));
|
||||
}
|
||||
|
||||
if (!EntitiesBelongToSameInstance(entityIdsNoLevelInstance))
|
||||
if (!EntitiesBelongToSameInstance(entityIdsNoFocusContainer))
|
||||
{
|
||||
return AZ::Failure(AZStd::string("Cannot duplicate multiple entities belonging to different instances with one operation."
|
||||
"Change your selection to contain entities in the same instance."));
|
||||
@@ -982,7 +995,7 @@ namespace AzToolsFramework
|
||||
|
||||
// We've already verified the entities are all owned by the same instance,
|
||||
// so we can just retrieve our instance from the first entity in the list.
|
||||
AZ::EntityId firstEntityIdToDuplicate = entityIdsNoLevelInstance[0];
|
||||
AZ::EntityId firstEntityIdToDuplicate = entityIdsNoFocusContainer[0];
|
||||
InstanceOptionalReference commonOwningInstance = GetOwnerInstanceByEntityId(firstEntityIdToDuplicate);
|
||||
if (!commonOwningInstance.has_value())
|
||||
{
|
||||
@@ -1002,7 +1015,7 @@ namespace AzToolsFramework
|
||||
|
||||
// This will cull out any entities that have ancestors in the list, since we will end up duplicating
|
||||
// the full nested hierarchy with what is returned from RetrieveAndSortPrefabEntitiesAndInstances
|
||||
AzToolsFramework::EntityIdSet duplicationSet = AzToolsFramework::GetCulledEntityHierarchy(entityIdsNoLevelInstance);
|
||||
AzToolsFramework::EntityIdSet duplicationSet = AzToolsFramework::GetCulledEntityHierarchy(entityIdsNoFocusContainer);
|
||||
|
||||
AZ_PROFILE_FUNCTION(AzToolsFramework);
|
||||
|
||||
@@ -1039,10 +1052,10 @@ namespace AzToolsFramework
|
||||
DuplicateNestedEntitiesInInstance(commonOwningInstance->get(),
|
||||
entities, instanceDomAfter, duplicatedEntityAndInstanceIds, duplicateEntityAliasMap);
|
||||
|
||||
PrefabUndoInstance* command = aznew PrefabUndoInstance("Entity/Instance duplication");
|
||||
PrefabUndoInstance* command = aznew PrefabUndoInstance("Entity/Instance duplication", false);
|
||||
command->SetParent(undoBatch.GetUndoBatch());
|
||||
command->Capture(instanceDomBefore, instanceDomAfter, commonOwningInstance->get().GetTemplateId());
|
||||
command->RedoBatched();
|
||||
command->Redo();
|
||||
|
||||
DuplicateNestedInstancesInInstance(commonOwningInstance->get(),
|
||||
instances, instanceDomAfter, duplicatedEntityAndInstanceIds, newInstanceAliasToOldInstanceMap);
|
||||
@@ -1106,19 +1119,21 @@ namespace AzToolsFramework
|
||||
|
||||
PrefabOperationResult PrefabPublicHandler::DeleteFromInstance(const EntityIdList& entityIds, bool deleteDescendants)
|
||||
{
|
||||
const EntityIdList entityIdsNoLevelInstance = GenerateEntityIdListWithoutLevelInstance(entityIds);
|
||||
// Remove the container entity of the focused prefab from the list, if it is included.
|
||||
const EntityIdList entityIdsNoFocusContainer = GenerateEntityIdListWithoutFocusedInstanceContainer(entityIds);
|
||||
|
||||
if (entityIdsNoLevelInstance.empty())
|
||||
if (entityIdsNoFocusContainer.empty())
|
||||
{
|
||||
return AZ::Success();
|
||||
}
|
||||
|
||||
if (!EntitiesBelongToSameInstance(entityIdsNoLevelInstance))
|
||||
// All entities in this list need to belong to the same prefab instance for the operation to be valid.
|
||||
if (!EntitiesBelongToSameInstance(entityIdsNoFocusContainer))
|
||||
{
|
||||
return AZ::Failure(AZStd::string("Cannot delete multiple entities belonging to different instances with one operation."));
|
||||
}
|
||||
|
||||
AZ::EntityId firstEntityIdToDelete = entityIdsNoLevelInstance[0];
|
||||
AZ::EntityId firstEntityIdToDelete = entityIdsNoFocusContainer[0];
|
||||
InstanceOptionalReference commonOwningInstance = GetOwnerInstanceByEntityId(firstEntityIdToDelete);
|
||||
|
||||
// If the first entity id is a container entity id, then we need to mark its parent as the common owning instance because you
|
||||
@@ -1128,8 +1143,15 @@ namespace AzToolsFramework
|
||||
commonOwningInstance = commonOwningInstance->get().GetParentInstance();
|
||||
}
|
||||
|
||||
// We only allow explicit deletions for entities inside the currently focused prefab.
|
||||
AzFramework::EntityContextId editorEntityContextId = AzToolsFramework::GetEntityContextId();
|
||||
if (&m_prefabFocusInterface->GetFocusedPrefabInstance(editorEntityContextId)->get() != &commonOwningInstance->get())
|
||||
{
|
||||
return AZ::Failure(AZStd::string("Cannot delete entities belonging to an instance that is not being edited."));
|
||||
}
|
||||
|
||||
// Retrieve entityList from entityIds
|
||||
EntityList inputEntityList = EntityIdListToEntityList(entityIdsNoLevelInstance);
|
||||
EntityList inputEntityList = EntityIdListToEntityList(entityIdsNoFocusContainer);
|
||||
|
||||
AZ_PROFILE_FUNCTION(AzToolsFramework);
|
||||
|
||||
@@ -1186,7 +1208,7 @@ namespace AzToolsFramework
|
||||
}
|
||||
else
|
||||
{
|
||||
for (AZ::EntityId entityId : entityIdsNoLevelInstance)
|
||||
for (AZ::EntityId entityId : entityIdsNoFocusContainer)
|
||||
{
|
||||
InstanceOptionalReference owningInstance = m_instanceEntityMapperInterface->FindOwningInstance(entityId);
|
||||
// If this is the container entity, it actually represents the instance so get its owner
|
||||
@@ -1227,9 +1249,12 @@ namespace AzToolsFramework
|
||||
return AZ::Failure(AZStd::string("Cannot detach Prefab Instance with invalid container entity."));
|
||||
}
|
||||
|
||||
if (IsLevelInstanceContainerEntity(containerEntityId))
|
||||
auto editorEntityContextId = AzFramework::EntityContextId::CreateNull();
|
||||
EditorEntityContextRequestBus::BroadcastResult(editorEntityContextId, &EditorEntityContextRequests::GetEditorEntityContextId);
|
||||
|
||||
if (containerEntityId == m_prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(editorEntityContextId))
|
||||
{
|
||||
return AZ::Failure(AZStd::string("Cannot detach level Prefab Instance."));
|
||||
return AZ::Failure(AZStd::string("Cannot detach focused Prefab Instance."));
|
||||
}
|
||||
|
||||
InstanceOptionalReference owningInstance = GetOwnerInstanceByEntityId(containerEntityId);
|
||||
@@ -1298,7 +1323,7 @@ namespace AzToolsFramework
|
||||
Prefab::PrefabDom instanceDomAfter;
|
||||
m_instanceToTemplateInterface->GenerateDomForInstance(instanceDomAfter, parentInstance);
|
||||
|
||||
PrefabUndoInstance* command = aznew PrefabUndoInstance("Instance detachment");
|
||||
PrefabUndoInstance* command = aznew PrefabUndoInstance("Instance detachment", false);
|
||||
command->Capture(instanceDomBefore, instanceDomAfter, parentTemplateId);
|
||||
command->SetParent(undoBatch.GetUndoBatch());
|
||||
{
|
||||
@@ -1452,9 +1477,14 @@ namespace AzToolsFramework
|
||||
|
||||
AZStd::queue<AZ::Entity*> entityQueue;
|
||||
|
||||
auto editorEntityContextId = AzFramework::EntityContextId::CreateNull();
|
||||
EditorEntityContextRequestBus::BroadcastResult(editorEntityContextId, &EditorEntityContextRequests::GetEditorEntityContextId);
|
||||
|
||||
AZ::EntityId focusedPrefabContainerEntityId =
|
||||
m_prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(editorEntityContextId);
|
||||
for (auto inputEntity : inputEntities)
|
||||
{
|
||||
if (inputEntity && !IsLevelInstanceContainerEntity(inputEntity->GetId()))
|
||||
if (inputEntity && inputEntity->GetId() != focusedPrefabContainerEntityId)
|
||||
{
|
||||
entityQueue.push(inputEntity);
|
||||
}
|
||||
@@ -1548,19 +1578,19 @@ namespace AzToolsFramework
|
||||
return AZ::Success();
|
||||
}
|
||||
|
||||
EntityIdList PrefabPublicHandler::GenerateEntityIdListWithoutLevelInstance(
|
||||
EntityIdList PrefabPublicHandler::GenerateEntityIdListWithoutFocusedInstanceContainer(
|
||||
const EntityIdList& entityIds) const
|
||||
{
|
||||
EntityIdList outEntityIds;
|
||||
outEntityIds.reserve(entityIds.size()); // Actual size could be smaller.
|
||||
EntityIdList outEntityIds(entityIds);
|
||||
|
||||
for (const AZ::EntityId& entityId : entityIds)
|
||||
AzFramework::EntityContextId editorEntityContextId = AzToolsFramework::GetEntityContextId();
|
||||
AZ::EntityId focusedInstanceContainerEntityId = m_prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(editorEntityContextId);
|
||||
|
||||
if (auto iter = AZStd::find(outEntityIds.begin(), outEntityIds.end(), focusedInstanceContainerEntityId); iter != outEntityIds.end())
|
||||
{
|
||||
if (!IsLevelInstanceContainerEntity(entityId))
|
||||
{
|
||||
outEntityIds.emplace_back(entityId);
|
||||
}
|
||||
outEntityIds.erase(iter);
|
||||
}
|
||||
|
||||
return outEntityIds;
|
||||
}
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ namespace AzToolsFramework
|
||||
Instance& commonRootEntityOwningInstance,
|
||||
EntityList& outEntities,
|
||||
AZStd::vector<Instance*>& outInstances) const;
|
||||
EntityIdList GenerateEntityIdListWithoutLevelInstance(const EntityIdList& entityIds) const;
|
||||
EntityIdList GenerateEntityIdListWithoutFocusedInstanceContainer(const EntityIdList& entityIds) const;
|
||||
|
||||
InstanceOptionalReference GetOwnerInstanceByEntityId(AZ::EntityId entityId) const;
|
||||
bool EntitiesBelongToSameInstance(const EntityIdList& entityIds) const;
|
||||
@@ -187,6 +187,8 @@ namespace AzToolsFramework
|
||||
|
||||
InstanceEntityMapperInterface* m_instanceEntityMapperInterface = nullptr;
|
||||
InstanceToTemplateInterface* m_instanceToTemplateInterface = nullptr;
|
||||
PrefabFocusInterface* m_prefabFocusInterface = nullptr;
|
||||
PrefabFocusPublicInterface* m_prefabFocusPublicInterface = nullptr;
|
||||
PrefabLoaderInterface* m_prefabLoaderInterface = nullptr;
|
||||
PrefabSystemComponentInterface* m_prefabSystemComponentInterface = nullptr;
|
||||
|
||||
|
||||
+26
-2
@@ -6,11 +6,14 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <API/ToolsApplicationAPI.h>
|
||||
#include <AzCore/Component/ComponentApplicationBus.h>
|
||||
#include <AzCore/RTTI/BehaviorContext.h>
|
||||
#include <Prefab/PrefabSystemComponentInterface.h>
|
||||
#include <Prefab/PrefabSystemScriptingHandler.h>
|
||||
#include <AzCore/Component/Entity.h>
|
||||
#include <Prefab/EditorPrefabComponent.h>
|
||||
#include <ToolsComponents/TransformComponent.h>
|
||||
|
||||
namespace AzToolsFramework::Prefab
|
||||
{
|
||||
@@ -61,9 +64,30 @@ namespace AzToolsFramework::Prefab
|
||||
entities.push_back(entity);
|
||||
}
|
||||
}
|
||||
|
||||
auto prefab = m_prefabSystemComponentInterface->CreatePrefab(entities, {}, AZ::IO::PathView(AZStd::string_view(filePath)));
|
||||
|
||||
bool result = false;
|
||||
[[maybe_unused]] AZ::EntityId commonRoot;
|
||||
EntityList topLevelEntities;
|
||||
AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(result, &AzToolsFramework::ToolsApplicationRequestBus::Events::FindCommonRootInactive,
|
||||
entities, commonRoot, &topLevelEntities);
|
||||
|
||||
auto containerEntity = AZStd::make_unique<AZ::Entity>();
|
||||
containerEntity->CreateComponent<Prefab::EditorPrefabComponent>();
|
||||
|
||||
for (AZ::Entity* entity : topLevelEntities)
|
||||
{
|
||||
AzToolsFramework::Components::TransformComponent* transformComponent =
|
||||
entity->FindComponent<AzToolsFramework::Components::TransformComponent>();
|
||||
|
||||
if (transformComponent)
|
||||
{
|
||||
transformComponent->SetParent(containerEntity->GetId());
|
||||
}
|
||||
}
|
||||
|
||||
auto prefab = m_prefabSystemComponentInterface->CreatePrefab(
|
||||
entities, {}, AZ::IO::PathView(AZStd::string_view(filePath)), AZStd::move(containerEntity));
|
||||
|
||||
if (!prefab)
|
||||
{
|
||||
AZ_Error("PrefabSystemComponenent", false, "Failed to create prefab %s", filePath.c_str());
|
||||
|
||||
@@ -17,17 +17,16 @@ namespace AzToolsFramework
|
||||
{
|
||||
PrefabUndoBase::PrefabUndoBase(const AZStd::string& undoOperationName)
|
||||
: UndoSystem::URSequencePoint(undoOperationName)
|
||||
, m_changed(true)
|
||||
, m_templateId(InvalidTemplateId)
|
||||
{
|
||||
m_instanceToTemplateInterface = AZ::Interface<InstanceToTemplateInterface>::Get();
|
||||
AZ_Assert(m_instanceToTemplateInterface, "Failed to grab instance to template interface");
|
||||
}
|
||||
|
||||
//PrefabInstanceUndo
|
||||
PrefabUndoInstance::PrefabUndoInstance(const AZStd::string& undoOperationName)
|
||||
PrefabUndoInstance::PrefabUndoInstance(const AZStd::string& undoOperationName, const bool useImmediatePropagation)
|
||||
: PrefabUndoBase(undoOperationName)
|
||||
{
|
||||
m_useImmediatePropagation = useImmediatePropagation;
|
||||
}
|
||||
|
||||
void PrefabUndoInstance::Capture(
|
||||
@@ -43,17 +42,12 @@ namespace AzToolsFramework
|
||||
|
||||
void PrefabUndoInstance::Undo()
|
||||
{
|
||||
m_instanceToTemplateInterface->PatchTemplate(m_undoPatch, m_templateId, true);
|
||||
m_instanceToTemplateInterface->PatchTemplate(m_undoPatch, m_templateId, m_useImmediatePropagation);
|
||||
}
|
||||
|
||||
void PrefabUndoInstance::Redo()
|
||||
{
|
||||
m_instanceToTemplateInterface->PatchTemplate(m_redoPatch, m_templateId, true);
|
||||
}
|
||||
|
||||
void PrefabUndoInstance::RedoBatched()
|
||||
{
|
||||
m_instanceToTemplateInterface->PatchTemplate(m_redoPatch, m_templateId);
|
||||
m_instanceToTemplateInterface->PatchTemplate(m_redoPatch, m_templateId, m_useImmediatePropagation);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -29,14 +29,15 @@ namespace AzToolsFramework
|
||||
bool Changed() const override { return m_changed; }
|
||||
|
||||
protected:
|
||||
TemplateId m_templateId;
|
||||
TemplateId m_templateId = InvalidTemplateId;
|
||||
|
||||
PrefabDom m_redoPatch;
|
||||
PrefabDom m_undoPatch;
|
||||
|
||||
InstanceToTemplateInterface* m_instanceToTemplateInterface = nullptr;
|
||||
|
||||
bool m_changed;
|
||||
bool m_changed = true;
|
||||
bool m_useImmediatePropagation = true;
|
||||
};
|
||||
|
||||
//! handles the addition and removal of entities from instances
|
||||
@@ -44,7 +45,7 @@ namespace AzToolsFramework
|
||||
: public PrefabUndoBase
|
||||
{
|
||||
public:
|
||||
explicit PrefabUndoInstance(const AZStd::string& undoOperationName);
|
||||
explicit PrefabUndoInstance(const AZStd::string& undoOperationName, const bool useImmediatePropagation = true);
|
||||
|
||||
void Capture(
|
||||
const PrefabDom& initialState,
|
||||
@@ -53,7 +54,6 @@ namespace AzToolsFramework
|
||||
|
||||
void Undo() override;
|
||||
void Redo() override;
|
||||
void RedoBatched();
|
||||
};
|
||||
|
||||
//! handles entity updates, such as when the values on an entity change
|
||||
|
||||
@@ -23,10 +23,10 @@ namespace AzToolsFramework
|
||||
PrefabDom instanceDomAfterUpdate;
|
||||
PrefabDomUtils::StoreInstanceInPrefabDom(instance, instanceDomAfterUpdate);
|
||||
|
||||
PrefabUndoInstance* state = aznew Prefab::PrefabUndoInstance(undoMessage);
|
||||
PrefabUndoInstance* state = aznew Prefab::PrefabUndoInstance(undoMessage, false);
|
||||
state->Capture(instanceDomBeforeUpdate, instanceDomAfterUpdate, instance.GetTemplateId());
|
||||
state->SetParent(undoBatch);
|
||||
state->RedoBatched();
|
||||
state->Redo();
|
||||
}
|
||||
|
||||
LinkId CreateLink(
|
||||
|
||||
+2
-1
@@ -313,7 +313,8 @@ namespace AzToolsFramework
|
||||
StyledTreeView::StartCustomDrag(indexListSorted, supportedActions);
|
||||
}
|
||||
|
||||
void EntityOutlinerTreeView::OnEditorFocusChanged([[maybe_unused]] AZ::EntityId entityId)
|
||||
void EntityOutlinerTreeView::OnEditorFocusChanged(
|
||||
[[maybe_unused]] AZ::EntityId previousFocusEntityId, [[maybe_unused]] AZ::EntityId newFocusEntityId)
|
||||
{
|
||||
viewport()->repaint();
|
||||
}
|
||||
|
||||
+1
-1
@@ -64,7 +64,7 @@ namespace AzToolsFramework
|
||||
void leaveEvent(QEvent* event) override;
|
||||
|
||||
// FocusModeNotificationBus overrides ...
|
||||
void OnEditorFocusChanged(AZ::EntityId entityId) override;
|
||||
void OnEditorFocusChanged(AZ::EntityId previousFocusEntityId, AZ::EntityId newFocusEntityId) override;
|
||||
|
||||
//! Renders the left side of the item: appropriate background, branch lines, icons.
|
||||
void drawBranches(QPainter* painter, const QRect& rect, const QModelIndex& index) const override;
|
||||
|
||||
+9
-1
@@ -324,7 +324,8 @@ namespace AzToolsFramework
|
||||
// Currently, the first behavior is implemented.
|
||||
void EntityOutlinerWidget::OnSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected)
|
||||
{
|
||||
if (m_selectionChangeInProgress || !m_enableSelectionUpdates)
|
||||
if (m_selectionChangeInProgress || !m_enableSelectionUpdates
|
||||
|| (selected.empty() && deselected.empty()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -552,6 +553,13 @@ namespace AzToolsFramework
|
||||
return;
|
||||
}
|
||||
|
||||
// Do not display the context menu if the item under the mouse cursor is not selectable.
|
||||
if (const QModelIndex& index = m_gui->m_objectTree->indexAt(pos); index.isValid()
|
||||
&& (index.flags() & Qt::ItemIsSelectable) == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
QMenu* contextMenu = new QMenu(this);
|
||||
|
||||
// Populate global context menu.
|
||||
|
||||
+45
-38
@@ -24,11 +24,12 @@
|
||||
#include <AzToolsFramework/AssetBrowser/AssetSelectionModel.h>
|
||||
#include <AzToolsFramework/AssetBrowser/Entries/SourceAssetBrowserEntry.h>
|
||||
#include <AzToolsFramework/ContainerEntity/ContainerEntityInterface.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabFocusInterface.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
|
||||
#include <AzToolsFramework/Prefab/EditorPrefabComponent.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/InstanceEntityMapperInterface.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabFocusPublicInterface.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabLoaderInterface.h>
|
||||
#include <AzToolsFramework/Prefab/Procedural/ProceduralPrefabAsset.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/InstanceEntityMapperInterface.h>
|
||||
#include <AzToolsFramework/Prefab/EditorPrefabComponent.h>
|
||||
#include <AzToolsFramework/ToolsComponents/EditorLayerComponentBus.h>
|
||||
#include <AzToolsFramework/UI/EditorEntityUi/EditorEntityUiInterface.h>
|
||||
#include <AzToolsFramework/UI/Prefab/PrefabIntegrationInterface.h>
|
||||
@@ -39,7 +40,6 @@
|
||||
#include <AzQtComponents/Components/StyleManager.h>
|
||||
#include <AzQtComponents/Components/Widgets/CardHeader.h>
|
||||
|
||||
|
||||
#include <QApplication>
|
||||
#include <QCheckBox>
|
||||
#include <QDialog>
|
||||
@@ -56,14 +56,13 @@
|
||||
#include <QVBoxLayout>
|
||||
#include <QWidget>
|
||||
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
namespace Prefab
|
||||
{
|
||||
ContainerEntityInterface* PrefabIntegrationManager::s_containerEntityInterface = nullptr;
|
||||
EditorEntityUiInterface* PrefabIntegrationManager::s_editorEntityUiInterface = nullptr;
|
||||
PrefabFocusInterface* PrefabIntegrationManager::s_prefabFocusInterface = nullptr;
|
||||
PrefabFocusPublicInterface* PrefabIntegrationManager::s_prefabFocusPublicInterface = nullptr;
|
||||
PrefabLoaderInterface* PrefabIntegrationManager::s_prefabLoaderInterface = nullptr;
|
||||
PrefabPublicInterface* PrefabIntegrationManager::s_prefabPublicInterface = nullptr;
|
||||
PrefabSystemComponentInterface* PrefabIntegrationManager::s_prefabSystemComponentInterface = nullptr;
|
||||
@@ -129,10 +128,10 @@ namespace AzToolsFramework
|
||||
return;
|
||||
}
|
||||
|
||||
s_prefabFocusInterface = AZ::Interface<PrefabFocusInterface>::Get();
|
||||
if (s_prefabFocusInterface == nullptr)
|
||||
s_prefabFocusPublicInterface = AZ::Interface<PrefabFocusPublicInterface>::Get();
|
||||
if (s_prefabFocusPublicInterface == nullptr)
|
||||
{
|
||||
AZ_Assert(false, "Prefab - could not get PrefabFocusInterface on PrefabIntegrationManager construction.");
|
||||
AZ_Assert(false, "Prefab - could not get PrefabFocusPublicInterface on PrefabIntegrationManager construction.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -177,12 +176,16 @@ namespace AzToolsFramework
|
||||
AzFramework::ApplicationRequests::Bus::BroadcastResult(
|
||||
prefabWipFeaturesEnabled, &AzFramework::ApplicationRequests::ArePrefabWipFeaturesEnabled);
|
||||
|
||||
auto editorEntityContextId = AzFramework::EntityContextId::CreateNull();
|
||||
EditorEntityContextRequestBus::BroadcastResult(editorEntityContextId, &EditorEntityContextRequests::GetEditorEntityContextId);
|
||||
|
||||
// Create Prefab
|
||||
{
|
||||
if (!selectedEntities.empty())
|
||||
{
|
||||
// Hide if the only selected entity is the Level Container
|
||||
if (selectedEntities.size() > 1 || !s_prefabPublicInterface->IsLevelInstanceContainerEntity(selectedEntities[0]))
|
||||
// Hide if the only selected entity is the Focused Instance Container
|
||||
if (selectedEntities.size() > 1 ||
|
||||
selectedEntities[0] != s_prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(editorEntityContextId))
|
||||
{
|
||||
bool layerInSelection = false;
|
||||
|
||||
@@ -247,21 +250,16 @@ namespace AzToolsFramework
|
||||
if (s_prefabPublicInterface->IsInstanceContainerEntity(selectedEntity))
|
||||
{
|
||||
// Edit Prefab
|
||||
if (prefabWipFeaturesEnabled)
|
||||
if (prefabWipFeaturesEnabled && !s_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(selectedEntity))
|
||||
{
|
||||
bool beingEdited = s_prefabFocusInterface->IsOwningPrefabBeingFocused(selectedEntity);
|
||||
QAction* editAction = menu->addAction(QObject::tr("Edit Prefab"));
|
||||
editAction->setToolTip(QObject::tr("Edit the prefab in focus mode."));
|
||||
|
||||
if (!beingEdited)
|
||||
{
|
||||
QAction* editAction = menu->addAction(QObject::tr("Edit Prefab"));
|
||||
editAction->setToolTip(QObject::tr("Edit the prefab in focus mode."));
|
||||
QObject::connect(editAction, &QAction::triggered, editAction, [selectedEntity] {
|
||||
ContextMenu_EditPrefab(selectedEntity);
|
||||
});
|
||||
|
||||
QObject::connect(editAction, &QAction::triggered, editAction, [selectedEntity] {
|
||||
ContextMenu_EditPrefab(selectedEntity);
|
||||
});
|
||||
|
||||
itemWasShown = true;
|
||||
}
|
||||
itemWasShown = true;
|
||||
}
|
||||
|
||||
// Save Prefab
|
||||
@@ -290,8 +288,9 @@ namespace AzToolsFramework
|
||||
|
||||
QAction* deleteAction = menu->addAction(QObject::tr("Delete"));
|
||||
QObject::connect(deleteAction, &QAction::triggered, deleteAction, [] { ContextMenu_DeleteSelected(); });
|
||||
if (selectedEntities.size() == 0 ||
|
||||
(selectedEntities.size() == 1 && s_prefabPublicInterface->IsLevelInstanceContainerEntity(selectedEntities[0])))
|
||||
|
||||
if (selectedEntities.empty() ||
|
||||
(selectedEntities.size() == 1 && selectedEntities[0] == s_prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(editorEntityContextId)))
|
||||
{
|
||||
deleteAction->setDisabled(true);
|
||||
}
|
||||
@@ -299,17 +298,17 @@ namespace AzToolsFramework
|
||||
// Detach Prefab
|
||||
if (selectedEntities.size() == 1)
|
||||
{
|
||||
AZ::EntityId selectedEntity = selectedEntities[0];
|
||||
AZ::EntityId selectedEntityId = selectedEntities[0];
|
||||
|
||||
if (s_prefabPublicInterface->IsInstanceContainerEntity(selectedEntity) &&
|
||||
!s_prefabPublicInterface->IsLevelInstanceContainerEntity(selectedEntity))
|
||||
if (s_prefabPublicInterface->IsInstanceContainerEntity(selectedEntityId) &&
|
||||
selectedEntityId != s_prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(editorEntityContextId))
|
||||
{
|
||||
QAction* detachPrefabAction = menu->addAction(QObject::tr("Detach Prefab..."));
|
||||
QObject::connect(
|
||||
detachPrefabAction, &QAction::triggered, detachPrefabAction,
|
||||
[selectedEntity]
|
||||
[selectedEntityId]
|
||||
{
|
||||
ContextMenu_DetachPrefab(selectedEntity);
|
||||
ContextMenu_DetachPrefab(selectedEntityId);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -317,7 +316,7 @@ namespace AzToolsFramework
|
||||
|
||||
void PrefabIntegrationManager::OnEscape()
|
||||
{
|
||||
s_prefabFocusInterface->FocusOnOwningPrefab(AZ::EntityId());
|
||||
s_prefabFocusPublicInterface->FocusOnOwningPrefab(AZ::EntityId());
|
||||
}
|
||||
|
||||
void PrefabIntegrationManager::HandleSourceFileType(AZStd::string_view sourceFilePath, AZ::EntityId parentId, AZ::Vector3 position) const
|
||||
@@ -338,13 +337,21 @@ namespace AzToolsFramework
|
||||
QWidget* activeWindow = QApplication::activeWindow();
|
||||
const AZStd::string prefabFilesPath = "@projectroot@/Prefabs";
|
||||
|
||||
// Remove Level entity if it's part of the list
|
||||
|
||||
auto levelContainerIter =
|
||||
AZStd::find(selectedEntities.begin(), selectedEntities.end(), s_prefabPublicInterface->GetLevelInstanceContainerEntityId());
|
||||
if (levelContainerIter != selectedEntities.end())
|
||||
// Remove focused instance container entity if it's part of the list
|
||||
auto editorEntityContextId = AzFramework::EntityContextId::CreateNull();
|
||||
EditorEntityContextRequestBus::BroadcastResult(editorEntityContextId, &EditorEntityContextRequests::GetEditorEntityContextId);
|
||||
|
||||
auto focusedContainerIter = AZStd::find(
|
||||
selectedEntities.begin(), selectedEntities.end(),
|
||||
s_prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(editorEntityContextId));
|
||||
if (focusedContainerIter != selectedEntities.end())
|
||||
{
|
||||
selectedEntities.erase(levelContainerIter);
|
||||
selectedEntities.erase(focusedContainerIter);
|
||||
}
|
||||
|
||||
if (selectedEntities.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Set default folder for prefabs
|
||||
@@ -490,7 +497,7 @@ namespace AzToolsFramework
|
||||
|
||||
void PrefabIntegrationManager::ContextMenu_EditPrefab(AZ::EntityId containerEntity)
|
||||
{
|
||||
s_prefabFocusInterface->FocusOnOwningPrefab(containerEntity);
|
||||
s_prefabFocusPublicInterface->FocusOnOwningPrefab(containerEntity);
|
||||
}
|
||||
|
||||
void PrefabIntegrationManager::ContextMenu_SavePrefab(AZ::EntityId containerEntity)
|
||||
|
||||
+2
-2
@@ -30,7 +30,7 @@ namespace AzToolsFramework
|
||||
|
||||
namespace Prefab
|
||||
{
|
||||
class PrefabFocusInterface;
|
||||
class PrefabFocusPublicInterface;
|
||||
class PrefabLoaderInterface;
|
||||
|
||||
//! Structure for saving/retrieving user settings related to prefab workflows.
|
||||
@@ -144,7 +144,7 @@ namespace AzToolsFramework
|
||||
|
||||
static ContainerEntityInterface* s_containerEntityInterface;
|
||||
static EditorEntityUiInterface* s_editorEntityUiInterface;
|
||||
static PrefabFocusInterface* s_prefabFocusInterface;
|
||||
static PrefabFocusPublicInterface* s_prefabFocusPublicInterface;
|
||||
static PrefabLoaderInterface* s_prefabLoaderInterface;
|
||||
static PrefabPublicInterface* s_prefabPublicInterface;
|
||||
static PrefabSystemComponentInterface* s_prefabSystemComponentInterface;
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
#include <AzFramework/API/ApplicationAPI.h>
|
||||
|
||||
#include <AzToolsFramework/Prefab/PrefabFocusInterface.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabFocusPublicInterface.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabPublicInterface.h>
|
||||
#include <AzToolsFramework/UI/Outliner/EntityOutlinerListModel.hxx>
|
||||
|
||||
@@ -35,10 +35,10 @@ namespace AzToolsFramework
|
||||
return;
|
||||
}
|
||||
|
||||
m_prefabFocusInterface = AZ::Interface<Prefab::PrefabFocusInterface>::Get();
|
||||
if (m_prefabFocusInterface == nullptr)
|
||||
m_prefabFocusPublicInterface = AZ::Interface<Prefab::PrefabFocusPublicInterface>::Get();
|
||||
if (m_prefabFocusPublicInterface == nullptr)
|
||||
{
|
||||
AZ_Assert(false, "PrefabUiHandler - could not get PrefabFocusInterface on PrefabUiHandler construction.");
|
||||
AZ_Assert(false, "PrefabUiHandler - could not get PrefabFocusPublicInterface on PrefabUiHandler construction.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -83,7 +83,7 @@ namespace AzToolsFramework
|
||||
|
||||
QIcon PrefabUiHandler::GenerateItemIcon(AZ::EntityId entityId) const
|
||||
{
|
||||
if (m_prefabFocusInterface->IsOwningPrefabBeingFocused(entityId))
|
||||
if (m_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(entityId))
|
||||
{
|
||||
return QIcon(m_prefabEditIconPath);
|
||||
}
|
||||
@@ -105,7 +105,7 @@ namespace AzToolsFramework
|
||||
const bool hasVisibleChildren = index.data(EntityOutlinerListModel::ExpandedRole).value<bool>() && index.model()->hasChildren(index);
|
||||
|
||||
QColor backgroundColor = m_prefabCapsuleColor;
|
||||
if (m_prefabFocusInterface->IsOwningPrefabBeingFocused(entityId))
|
||||
if (m_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(entityId))
|
||||
{
|
||||
backgroundColor = m_prefabCapsuleEditColor;
|
||||
}
|
||||
@@ -178,12 +178,6 @@ namespace AzToolsFramework
|
||||
|
||||
AZ::EntityId entityId(index.data(EntityOutlinerListModel::EntityIdRole).value<AZ::u64>());
|
||||
|
||||
// We hide the root instance container entity from the Outliner, so avoid drawing its full container on children
|
||||
if (m_prefabPublicInterface->IsLevelInstanceContainerEntity(entityId))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const QTreeView* outlinerTreeView(qobject_cast<const QTreeView*>(option.widget));
|
||||
const int ancestorLeft = outlinerTreeView->visualRect(index).left() + (m_prefabBorderThickness / 2) - 1;
|
||||
const int curveRectSize = m_prefabCapsuleRadius * 2;
|
||||
@@ -191,7 +185,7 @@ namespace AzToolsFramework
|
||||
const bool isLastColumn = descendantIndex.column() == EntityOutlinerListModel::ColumnLockToggle;
|
||||
|
||||
QColor borderColor = m_prefabCapsuleColor;
|
||||
if (m_prefabFocusInterface->IsOwningPrefabBeingFocused(entityId))
|
||||
if (m_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(entityId))
|
||||
{
|
||||
borderColor = m_prefabCapsuleEditColor;
|
||||
}
|
||||
@@ -329,7 +323,7 @@ namespace AzToolsFramework
|
||||
if (prefabWipFeaturesEnabled)
|
||||
{
|
||||
// Focus on this prefab
|
||||
m_prefabFocusInterface->FocusOnOwningPrefab(entityId);
|
||||
m_prefabFocusPublicInterface->FocusOnOwningPrefab(entityId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace AzToolsFramework
|
||||
|
||||
namespace Prefab
|
||||
{
|
||||
class PrefabFocusInterface;
|
||||
class PrefabFocusPublicInterface;
|
||||
class PrefabPublicInterface;
|
||||
};
|
||||
|
||||
@@ -39,7 +39,7 @@ namespace AzToolsFramework
|
||||
void OnDoubleClick(AZ::EntityId entityId) const override;
|
||||
|
||||
private:
|
||||
Prefab::PrefabFocusInterface* m_prefabFocusInterface = nullptr;
|
||||
Prefab::PrefabFocusPublicInterface* m_prefabFocusPublicInterface = nullptr;
|
||||
Prefab::PrefabPublicInterface* m_prefabPublicInterface = nullptr;
|
||||
|
||||
static bool IsLastVisibleChild(const QModelIndex& parent, const QModelIndex& child);
|
||||
|
||||
+7
-7
@@ -8,7 +8,7 @@
|
||||
|
||||
#include <AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.h>
|
||||
|
||||
#include <AzToolsFramework/Prefab/PrefabFocusInterface.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabFocusPublicInterface.h>
|
||||
|
||||
namespace AzToolsFramework::Prefab
|
||||
{
|
||||
@@ -31,8 +31,8 @@ namespace AzToolsFramework::Prefab
|
||||
void PrefabViewportFocusPathHandler::Initialize(AzQtComponents::BreadCrumbs* breadcrumbsWidget, QToolButton* backButton)
|
||||
{
|
||||
// Get reference to the PrefabFocusInterface handler
|
||||
m_prefabFocusInterface = AZ::Interface<PrefabFocusInterface>::Get();
|
||||
if (m_prefabFocusInterface == nullptr)
|
||||
m_prefabFocusPublicInterface = AZ::Interface<PrefabFocusPublicInterface>::Get();
|
||||
if (m_prefabFocusPublicInterface == nullptr)
|
||||
{
|
||||
AZ_Assert(false, "Prefab - could not get PrefabFocusInterface on PrefabViewportFocusPathHandler construction.");
|
||||
return;
|
||||
@@ -46,7 +46,7 @@ namespace AzToolsFramework::Prefab
|
||||
connect(m_breadcrumbsWidget, &AzQtComponents::BreadCrumbs::linkClicked, this,
|
||||
[&](const QString&, int linkIndex)
|
||||
{
|
||||
m_prefabFocusInterface->FocusOnPathIndex(m_editorEntityContextId, linkIndex);
|
||||
m_prefabFocusPublicInterface->FocusOnPathIndex(m_editorEntityContextId, linkIndex);
|
||||
}
|
||||
);
|
||||
|
||||
@@ -54,9 +54,9 @@ namespace AzToolsFramework::Prefab
|
||||
connect(m_backButton, &QToolButton::clicked, this,
|
||||
[&]()
|
||||
{
|
||||
if (int length = m_prefabFocusInterface->GetPrefabFocusPathLength(m_editorEntityContextId); length > 1)
|
||||
if (int length = m_prefabFocusPublicInterface->GetPrefabFocusPathLength(m_editorEntityContextId); length > 1)
|
||||
{
|
||||
m_prefabFocusInterface->FocusOnPathIndex(m_editorEntityContextId, length - 2);
|
||||
m_prefabFocusPublicInterface->FocusOnPathIndex(m_editorEntityContextId, length - 2);
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -65,7 +65,7 @@ namespace AzToolsFramework::Prefab
|
||||
void PrefabViewportFocusPathHandler::OnPrefabFocusChanged()
|
||||
{
|
||||
// Push new Path
|
||||
m_breadcrumbsWidget->pushPath(m_prefabFocusInterface->GetPrefabFocusPath(m_editorEntityContextId).c_str());
|
||||
m_breadcrumbsWidget->pushPath(m_prefabFocusPublicInterface->GetPrefabFocusPath(m_editorEntityContextId).c_str());
|
||||
}
|
||||
|
||||
} // namespace AzToolsFramework::Prefab
|
||||
|
||||
+2
-2
@@ -19,7 +19,7 @@
|
||||
|
||||
namespace AzToolsFramework::Prefab
|
||||
{
|
||||
class PrefabFocusInterface;
|
||||
class PrefabFocusPublicInterface;
|
||||
|
||||
class PrefabViewportFocusPathHandler
|
||||
: public PrefabFocusNotificationBus::Handler
|
||||
@@ -40,6 +40,6 @@ namespace AzToolsFramework::Prefab
|
||||
|
||||
AzFramework::EntityContextId m_editorEntityContextId = AzFramework::EntityContextId::CreateNull();
|
||||
|
||||
PrefabFocusInterface* m_prefabFocusInterface = nullptr;
|
||||
PrefabFocusPublicInterface* m_prefabFocusPublicInterface = nullptr;
|
||||
};
|
||||
} // namespace AzToolsFramework::Prefab
|
||||
|
||||
+26
-7
@@ -9,6 +9,7 @@
|
||||
#include "EditorHelpers.h"
|
||||
|
||||
#include <AzCore/Console/Console.h>
|
||||
#include <AzCore/Math/VectorConversions.h>
|
||||
#include <AzFramework/Entity/EntityDebugDisplayBus.h>
|
||||
#include <AzFramework/Viewport/CameraState.h>
|
||||
#include <AzFramework/Viewport/ViewportScreen.h>
|
||||
@@ -150,6 +151,11 @@ namespace AzToolsFramework
|
||||
"EditorHelpers - "
|
||||
"Focus Mode Interface could not be found. "
|
||||
"Check that it is being correctly initialized.");
|
||||
|
||||
AZStd::vector<AZStd::unique_ptr<InvalidClick>> invalidClicks;
|
||||
invalidClicks.push_back(AZStd::make_unique<FadingText>("Not in focus"));
|
||||
invalidClicks.push_back(AZStd::make_unique<ExpandingFadingCircles>());
|
||||
m_invalidClicks = AZStd::make_unique<InvalidClicks>(AZStd::move(invalidClicks));
|
||||
}
|
||||
|
||||
CursorEntityIdQuery EditorHelpers::FindEntityIdUnderCursor(
|
||||
@@ -213,13 +219,20 @@ namespace AzToolsFramework
|
||||
}
|
||||
}
|
||||
|
||||
// Verify if the entity Id corresponds to an entity that is focused; if not, halt selection.
|
||||
if (!IsSelectableAccordingToFocusMode(entityIdUnderCursor))
|
||||
// verify if the entity Id corresponds to an entity that is focused; if not, halt selection.
|
||||
if (entityIdUnderCursor.IsValid() && !IsSelectableAccordingToFocusMode(entityIdUnderCursor))
|
||||
{
|
||||
if (mouseInteraction.m_mouseInteraction.m_mouseButtons.Left() &&
|
||||
mouseInteraction.m_mouseEvent == ViewportInteraction::MouseEvent::Down ||
|
||||
mouseInteraction.m_mouseEvent == ViewportInteraction::MouseEvent::DoubleClick)
|
||||
{
|
||||
m_invalidClicks->AddInvalidClick(mouseInteraction.m_mouseInteraction.m_mousePick.m_screenCoordinates);
|
||||
}
|
||||
|
||||
return CursorEntityIdQuery(AZ::EntityId(), AZ::EntityId());
|
||||
}
|
||||
|
||||
// Container Entity support - if the entity that is being selected is part of a closed container,
|
||||
// container entity support - if the entity that is being selected is part of a closed container,
|
||||
// change the selection to the container instead.
|
||||
if (ContainerEntityInterface* containerEntityInterface = AZ::Interface<ContainerEntityInterface>::Get())
|
||||
{
|
||||
@@ -230,6 +243,12 @@ namespace AzToolsFramework
|
||||
return CursorEntityIdQuery(entityIdUnderCursor, AZ::EntityId());
|
||||
}
|
||||
|
||||
void EditorHelpers::Display2d(
|
||||
[[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay)
|
||||
{
|
||||
m_invalidClicks->Display2d(viewportInfo, debugDisplay);
|
||||
}
|
||||
|
||||
void EditorHelpers::DisplayHelpers(
|
||||
const AzFramework::ViewportInfo& viewportInfo,
|
||||
const AzFramework::CameraState& cameraState,
|
||||
@@ -291,19 +310,19 @@ namespace AzToolsFramework
|
||||
}
|
||||
}
|
||||
|
||||
bool EditorHelpers::IsSelectableInViewport(AZ::EntityId entityId)
|
||||
bool EditorHelpers::IsSelectableInViewport(const AZ::EntityId entityId) const
|
||||
{
|
||||
return IsSelectableAccordingToFocusMode(entityId) && IsSelectableAccordingToContainerEntities(entityId);
|
||||
}
|
||||
|
||||
bool EditorHelpers::IsSelectableAccordingToFocusMode(AZ::EntityId entityId)
|
||||
bool EditorHelpers::IsSelectableAccordingToFocusMode(const AZ::EntityId entityId) const
|
||||
{
|
||||
return m_focusModeInterface->IsInFocusSubTree(entityId);
|
||||
}
|
||||
|
||||
bool EditorHelpers::IsSelectableAccordingToContainerEntities(AZ::EntityId entityId)
|
||||
bool EditorHelpers::IsSelectableAccordingToContainerEntities(const AZ::EntityId entityId) const
|
||||
{
|
||||
if (ContainerEntityInterface* containerEntityInterface = AZ::Interface<ContainerEntityInterface>::Get())
|
||||
if (const auto* containerEntityInterface = AZ::Interface<ContainerEntityInterface>::Get())
|
||||
{
|
||||
return !containerEntityInterface->IsUnderClosedContainerEntity(entityId);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
#include <AzCore/Component/EntityId.h>
|
||||
#include <AzCore/Memory/Memory.h>
|
||||
#include <AzCore/std/functional.h>
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
#include <AzFramework/Viewport/ScreenGeometry.h>
|
||||
#include <AzToolsFramework/ViewportSelection/InvalidClicks.h>
|
||||
|
||||
namespace AzFramework
|
||||
{
|
||||
@@ -80,20 +83,27 @@ namespace AzToolsFramework
|
||||
AzFramework::DebugDisplayRequests& debugDisplay,
|
||||
const AZStd::function<bool(AZ::EntityId)>& showIconCheck);
|
||||
|
||||
//! Handle 2d drawing for EditorHelper functionality.
|
||||
void Display2d(
|
||||
const AzFramework::ViewportInfo& viewportInfo,
|
||||
AzFramework::DebugDisplayRequests& debugDisplay);
|
||||
|
||||
//! Returns whether the entityId can be selected in the viewport according
|
||||
//! to the current Editor Focus Mode and Container Entity setup.
|
||||
bool IsSelectableInViewport(AZ::EntityId entityId);
|
||||
bool IsSelectableInViewport(AZ::EntityId entityId) const;
|
||||
|
||||
private:
|
||||
//! Returns whether the entityId can be selected in the viewport according
|
||||
//! to the current Editor Focus Mode setup.
|
||||
bool IsSelectableAccordingToFocusMode(AZ::EntityId entityId);
|
||||
bool IsSelectableAccordingToFocusMode(AZ::EntityId entityId) const;
|
||||
|
||||
//! Returns whether the entityId can be selected in the viewport according
|
||||
//! to the current Container Entityu setup.
|
||||
bool IsSelectableAccordingToContainerEntities(AZ::EntityId entityId);
|
||||
//! to the current Container Entity setup.
|
||||
bool IsSelectableAccordingToContainerEntities(AZ::EntityId entityId) const;
|
||||
|
||||
AZStd::unique_ptr<InvalidClicks> m_invalidClicks; //!< Display for invalid click behavior.
|
||||
|
||||
const EditorVisibleEntityDataCache* m_entityDataCache = nullptr; //!< Entity Data queried by the EditorHelpers.
|
||||
const FocusModeInterface* m_focusModeInterface = nullptr;
|
||||
const FocusModeInterface* m_focusModeInterface = nullptr; //!< API to interact with focus mode functionality.
|
||||
};
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user