Merge branch 'development' of https://github.com/aws-lumberyard-dev/o3de into mnaumov/LYN-7227

Signed-off-by: Mikhail Naumov <mnaumov@amazon.com>
This commit is contained in:
Mikhail Naumov
2021-10-14 11:38:00 -05:00
963 changed files with 14092 additions and 11663 deletions
+1 -1
View File
@@ -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
+1 -1
View File
@@ -279,7 +279,7 @@ void CFolderTreeCtrl::LoadTreeRec(const QString& currentFolder)
void CFolderTreeCtrl::AddItem(const QString& path)
{
AZ::IO::FixedMaxPath folder{ AZ::IO::PathView(path.toUtf8().constData()) };
AZ::IO::FixedMaxPath fileNameWithoutExtension = folder.Extension();
AZ::IO::FixedMaxPath fileNameWithoutExtension = folder.Stem();
folder = folder.ParentPath();
auto regex = QRegExp(m_fileNameSpec, Qt::CaseInsensitive, QRegExp::Wildcard);
+32 -26
View File
@@ -32,6 +32,7 @@
#include <AzFramework/API/ApplicationAPI.h>
// AzToolsFramework
#include <AzToolsFramework/Viewport/ViewportMessages.h>
#include <AzToolsFramework/ViewportSelection/EditorTransformComponentSelectionRequestBus.h>
// AzQtComponents
@@ -166,15 +167,14 @@ LevelEditorMenuHandler::LevelEditorMenuHandler(MainWindow* mainWindow, QtViewPan
m_mainWindow->menuBar()->setNativeMenuBar(true);
#endif
ComponentModeFramework::EditorComponentModeNotificationBus::Handler::BusConnect(
AzToolsFramework::GetEntityContextId());
ViewportEditorModeNotificationsBus::Handler::BusConnect(GetEntityContextId());
EditorMenuRequestBus::Handler::BusConnect();
}
LevelEditorMenuHandler::~LevelEditorMenuHandler()
{
EditorMenuRequestBus::Handler::BusDisconnect();
ComponentModeFramework::EditorComponentModeNotificationBus::Handler::BusDisconnect();
ViewportEditorModeNotificationsBus::Handler::BusDisconnect();
}
void LevelEditorMenuHandler::Initialize()
@@ -487,8 +487,6 @@ void LevelEditorMenuHandler::PopulateEditMenu(ActionManager::MenuWrapper& editMe
editMenu.AddAction(AzToolsFramework::EditPivot);
editMenu.AddAction(AzToolsFramework::EditReset);
editMenu.AddAction(AzToolsFramework::EditResetManipulator);
editMenu.AddAction(AzToolsFramework::EditResetLocal);
editMenu.AddAction(AzToolsFramework::EditResetWorld);
// Hide Selection
editMenu.AddAction(AzToolsFramework::HideSelection);
@@ -1186,36 +1184,44 @@ void LevelEditorMenuHandler::AddDisableActionInSimModeListener(QAction* action)
}));
}
void LevelEditorMenuHandler::EnteredComponentMode(const AZStd::vector<AZ::Uuid>& /*componentModeTypes*/)
void LevelEditorMenuHandler::OnEditorModeActivated(
[[maybe_unused]] const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode)
{
auto menuWrapper = m_actionManager->FindMenu(s_editMenuId);
if (!menuWrapper.isNull())
if (mode == ViewportEditorMode::Component)
{
// copy of menu actions
auto actions = menuWrapper.Get()->actions();
// remove all non-reserved edit menu options
actions.erase(
std::remove_if(actions.begin(), actions.end(), [](QAction* action)
{
return !action->property("Reserved").toBool();
}),
actions.end());
if (auto menuWrapper = m_actionManager->FindMenu(s_editMenuId);
!menuWrapper.isNull())
{
// copy of menu actions
auto actions = menuWrapper.Get()->actions();
// remove all non-reserved edit menu options
actions.erase(
std::remove_if(actions.begin(), actions.end(), [](QAction* action)
{
return !action->property("Reserved").toBool();
}),
actions.end());
// clear and update the menu with new actions
menuWrapper.Get()->clear();
menuWrapper.Get()->addActions(actions);
// clear and update the menu with new actions
menuWrapper.Get()->clear();
menuWrapper.Get()->addActions(actions);
}
}
}
void LevelEditorMenuHandler::LeftComponentMode(const AZStd::vector<AZ::Uuid>& /*componentModeTypes*/)
void LevelEditorMenuHandler::OnEditorModeDeactivated(
[[maybe_unused]] const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode)
{
RestoreEditMenuToDefault();
if (mode == ViewportEditorMode::Component)
{
RestoreEditMenuToDefault();
}
}
void LevelEditorMenuHandler::AddEditMenuAction(QAction* action)
{
auto menuWrapper = m_actionManager->FindMenu(s_editMenuId);
if (!menuWrapper.isNull())
if (auto menuWrapper = m_actionManager->FindMenu(s_editMenuId);
!menuWrapper.isNull())
{
menuWrapper.Get()->addAction(action);
}
@@ -1239,8 +1245,8 @@ void LevelEditorMenuHandler::AddMenuAction(AZStd::string_view categoryId, QActio
void LevelEditorMenuHandler::RestoreEditMenuToDefault()
{
auto menuWrapper = m_actionManager->FindMenu(s_editMenuId);
if (!menuWrapper.isNull())
if (auto menuWrapper = m_actionManager->FindMenu(s_editMenuId);
!menuWrapper.isNull())
{
menuWrapper.Get()->clear();
PopulateEditMenu(menuWrapper);
+7 -5
View File
@@ -18,7 +18,7 @@
#include <QPointer>
#include "ActionManager.h"
#include "QtViewPaneManager.h"
#include <AzToolsFramework/ComponentMode/EditorComponentModeBus.h>
#include <AzToolsFramework/API/ViewportEditorModeTrackerNotificationBus.h>
#endif
class MainWindow;
@@ -28,7 +28,7 @@ struct QtViewPane;
class LevelEditorMenuHandler
: public QObject
, private AzToolsFramework::ComponentModeFramework::EditorComponentModeNotificationBus::Handler
, private AzToolsFramework::ViewportEditorModeNotificationsBus::Handler
, private AzToolsFramework::EditorMenuRequestBus::Handler
{
Q_OBJECT
@@ -88,9 +88,11 @@ private:
void AddDisableActionInSimModeListener(QAction* action);
// EditorComponentModeNotificationBus
void EnteredComponentMode(const AZStd::vector<AZ::Uuid>& componentModeTypes) override;
void LeftComponentMode(const AZStd::vector<AZ::Uuid>& componentModeTypes) override;
// ViewportEditorModeNotificationsBus overrides ...
void OnEditorModeActivated(
const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode) override;
void OnEditorModeDeactivated(
const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode) override;
// EditorMenuRequestBus
void AddEditMenuAction(QAction* action) override;
+2
View File
@@ -2125,6 +2125,8 @@ bool CCryEditApp::FixDanglingSharedMemory(const QString& sharedMemName) const
int CCryEditApp::ExitInstance(int exitCode)
{
AZ_TracePrintf("Exit", "Called ExitInstance() with exit code: 0x%x", exitCode);
if (m_pEditor)
{
m_pEditor->OnBeginShutdownSequence();
+2 -2
View File
@@ -1273,7 +1273,7 @@ bool CCryEditDoc::SaveLevel(const QString& filename)
if (savedEntities)
{
AZ_PROFILE_SCOPE(AzToolsFramework, "CCryEditDoc::SaveLevel Updated PakFile levelEntities.editor_xml");
pakFile.UpdateFile("LevelEntities.editor_xml", entitySaveBuffer.begin(), static_cast<int>(entitySaveBuffer.size()));
pakFile.UpdateFile("levelentities.editor_xml", entitySaveBuffer.begin(), static_cast<int>(entitySaveBuffer.size()));
// Save XML archive to pak file.
bool bSaved = xmlAr.SaveToPak(Path::GetPath(tempSaveFile), pakFile);
@@ -1501,7 +1501,7 @@ bool CCryEditDoc::LoadEntitiesFromLevel(const QString& levelPakFile)
bool pakOpened = pakSystem->OpenPack(levelPakFile.toUtf8().data());
if (pakOpened)
{
const QString entityFilename = Path::GetPath(levelPakFile) + "LevelEntities.editor_xml";
const QString entityFilename = Path::GetPath(levelPakFile) + "levelentities.editor_xml";
CCryFile entitiesFile;
if (entitiesFile.Open(entityFilename.toUtf8().data(), "rt"))
@@ -96,7 +96,7 @@ namespace SandboxEditor
cameras.AddCamera(m_firstPersonTranslateCamera);
cameras.AddCamera(m_firstPersonScrollCamera);
cameras.AddCamera(m_firstPersonFocusCamera);
cameras.AddCamera(m_pivotCamera);
cameras.AddCamera(m_orbitCamera);
});
return controller;
@@ -135,7 +135,7 @@ namespace SandboxEditor
m_firstPersonRotateCamera->SetActivationEndedFn(showCursor);
m_firstPersonPanCamera = AZStd::make_shared<AzFramework::PanCameraInput>(
SandboxEditor::CameraFreePanChannelId(), AzFramework::LookPan, AzFramework::TranslatePivot);
SandboxEditor::CameraFreePanChannelId(), AzFramework::LookPan, AzFramework::TranslatePivotLook);
m_firstPersonPanCamera->m_panSpeedFn = []
{
@@ -155,7 +155,7 @@ namespace SandboxEditor
const auto translateCameraInputChannelIds = BuildTranslateCameraInputChannelIds();
m_firstPersonTranslateCamera = AZStd::make_shared<AzFramework::TranslateCameraInput>(
translateCameraInputChannelIds, AzFramework::LookTranslation, AzFramework::TranslatePivot);
translateCameraInputChannelIds, AzFramework::LookTranslation, AzFramework::TranslatePivotLook);
m_firstPersonTranslateCamera->m_translateSpeedFn = []
{
@@ -167,7 +167,7 @@ namespace SandboxEditor
return SandboxEditor::CameraBoostMultiplier();
};
m_firstPersonScrollCamera = AZStd::make_shared<AzFramework::ScrollTranslationCameraInput>();
m_firstPersonScrollCamera = AZStd::make_shared<AzFramework::LookScrollTranslationCameraInput>();
m_firstPersonScrollCamera->m_scrollSpeedFn = []
{
@@ -196,82 +196,82 @@ namespace SandboxEditor
m_firstPersonFocusCamera->SetPivotFn(pivotFn);
m_pivotCamera = AZStd::make_shared<AzFramework::PivotCameraInput>(SandboxEditor::CameraPivotChannelId());
m_orbitCamera = AZStd::make_shared<AzFramework::OrbitCameraInput>(SandboxEditor::CameraOrbitChannelId());
m_pivotCamera->SetPivotFn(
m_orbitCamera->SetPivotFn(
[pivotFn]([[maybe_unused]] const AZ::Vector3& position, [[maybe_unused]] const AZ::Vector3& direction)
{
return pivotFn();
});
m_pivotRotateCamera = AZStd::make_shared<AzFramework::RotateCameraInput>(SandboxEditor::CameraPivotLookChannelId());
m_orbitRotateCamera = AZStd::make_shared<AzFramework::RotateCameraInput>(SandboxEditor::CameraOrbitLookChannelId());
m_pivotRotateCamera->m_rotateSpeedFn = []
m_orbitRotateCamera->m_rotateSpeedFn = []
{
return SandboxEditor::CameraRotateSpeed();
};
m_pivotRotateCamera->m_invertYawFn = []
m_orbitRotateCamera->m_invertYawFn = []
{
return SandboxEditor::CameraPivotYawRotationInverted();
return SandboxEditor::CameraOrbitYawRotationInverted();
};
m_pivotTranslateCamera = AZStd::make_shared<AzFramework::TranslateCameraInput>(
translateCameraInputChannelIds, AzFramework::LookTranslation, AzFramework::TranslateOffset);
m_orbitTranslateCamera = AZStd::make_shared<AzFramework::TranslateCameraInput>(
translateCameraInputChannelIds, AzFramework::LookTranslation, AzFramework::TranslateOffsetOrbit);
m_pivotTranslateCamera->m_translateSpeedFn = []
m_orbitTranslateCamera->m_translateSpeedFn = []
{
return SandboxEditor::CameraTranslateSpeed();
};
m_pivotTranslateCamera->m_boostMultiplierFn = []
m_orbitTranslateCamera->m_boostMultiplierFn = []
{
return SandboxEditor::CameraBoostMultiplier();
};
m_pivotDollyScrollCamera = AZStd::make_shared<AzFramework::PivotDollyScrollCameraInput>();
m_orbitDollyScrollCamera = AZStd::make_shared<AzFramework::OrbitDollyScrollCameraInput>();
m_pivotDollyScrollCamera->m_scrollSpeedFn = []
m_orbitDollyScrollCamera->m_scrollSpeedFn = []
{
return SandboxEditor::CameraScrollSpeed();
};
m_pivotDollyMoveCamera = AZStd::make_shared<AzFramework::PivotDollyMotionCameraInput>(SandboxEditor::CameraPivotDollyChannelId());
m_orbitDollyMoveCamera = AZStd::make_shared<AzFramework::OrbitDollyMotionCameraInput>(SandboxEditor::CameraOrbitDollyChannelId());
m_pivotDollyMoveCamera->m_motionSpeedFn = []
m_orbitDollyMoveCamera->m_motionSpeedFn = []
{
return SandboxEditor::CameraDollyMotionSpeed();
};
m_pivotPanCamera = AZStd::make_shared<AzFramework::PanCameraInput>(
SandboxEditor::CameraPivotPanChannelId(), AzFramework::LookPan, AzFramework::TranslateOffset);
m_orbitPanCamera = AZStd::make_shared<AzFramework::PanCameraInput>(
SandboxEditor::CameraOrbitPanChannelId(), AzFramework::LookPan, AzFramework::TranslateOffsetOrbit);
m_pivotPanCamera->m_panSpeedFn = []
m_orbitPanCamera->m_panSpeedFn = []
{
return SandboxEditor::CameraPanSpeed();
};
m_pivotPanCamera->m_invertPanXFn = []
m_orbitPanCamera->m_invertPanXFn = []
{
return SandboxEditor::CameraPanInvertedX();
};
m_pivotPanCamera->m_invertPanYFn = []
m_orbitPanCamera->m_invertPanYFn = []
{
return SandboxEditor::CameraPanInvertedY();
};
m_pivotFocusCamera =
AZStd::make_shared<AzFramework::FocusCameraInput>(SandboxEditor::CameraFocusChannelId(), AzFramework::FocusPivot);
m_orbitFocusCamera =
AZStd::make_shared<AzFramework::FocusCameraInput>(SandboxEditor::CameraFocusChannelId(), AzFramework::FocusOrbit);
m_pivotFocusCamera->SetPivotFn(pivotFn);
m_orbitFocusCamera->SetPivotFn(pivotFn);
m_pivotCamera->m_pivotCameras.AddCamera(m_pivotRotateCamera);
m_pivotCamera->m_pivotCameras.AddCamera(m_pivotTranslateCamera);
m_pivotCamera->m_pivotCameras.AddCamera(m_pivotDollyScrollCamera);
m_pivotCamera->m_pivotCameras.AddCamera(m_pivotDollyMoveCamera);
m_pivotCamera->m_pivotCameras.AddCamera(m_pivotPanCamera);
m_pivotCamera->m_pivotCameras.AddCamera(m_pivotFocusCamera);
m_orbitCamera->m_orbitCameras.AddCamera(m_orbitRotateCamera);
m_orbitCamera->m_orbitCameras.AddCamera(m_orbitTranslateCamera);
m_orbitCamera->m_orbitCameras.AddCamera(m_orbitDollyScrollCamera);
m_orbitCamera->m_orbitCameras.AddCamera(m_orbitDollyMoveCamera);
m_orbitCamera->m_orbitCameras.AddCamera(m_orbitPanCamera);
m_orbitCamera->m_orbitCameras.AddCamera(m_orbitFocusCamera);
}
void EditorModularViewportCameraComposer::OnEditorModularViewportCameraComposerSettingsChanged()
@@ -282,12 +282,12 @@ namespace SandboxEditor
m_firstPersonRotateCamera->SetRotateInputChannelId(SandboxEditor::CameraFreeLookChannelId());
m_firstPersonFocusCamera->SetFocusInputChannelId(SandboxEditor::CameraFocusChannelId());
m_pivotCamera->SetPivotInputChannelId(SandboxEditor::CameraPivotChannelId());
m_pivotTranslateCamera->SetTranslateCameraInputChannelIds(translateCameraInputChannelIds);
m_pivotPanCamera->SetPanInputChannelId(SandboxEditor::CameraPivotPanChannelId());
m_pivotRotateCamera->SetRotateInputChannelId(SandboxEditor::CameraPivotLookChannelId());
m_pivotDollyMoveCamera->SetDollyInputChannelId(SandboxEditor::CameraPivotDollyChannelId());
m_pivotFocusCamera->SetFocusInputChannelId(SandboxEditor::CameraFocusChannelId());
m_orbitCamera->SetOrbitInputChannelId(SandboxEditor::CameraOrbitChannelId());
m_orbitTranslateCamera->SetTranslateCameraInputChannelIds(translateCameraInputChannelIds);
m_orbitPanCamera->SetPanInputChannelId(SandboxEditor::CameraOrbitPanChannelId());
m_orbitRotateCamera->SetRotateInputChannelId(SandboxEditor::CameraOrbitLookChannelId());
m_orbitDollyMoveCamera->SetDollyInputChannelId(SandboxEditor::CameraOrbitDollyChannelId());
m_orbitFocusCamera->SetFocusInputChannelId(SandboxEditor::CameraFocusChannelId());
}
void EditorModularViewportCameraComposer::OnViewportViewEntityChanged(const AZ::EntityId& viewEntityId)
@@ -41,15 +41,15 @@ namespace SandboxEditor
AZStd::shared_ptr<AzFramework::RotateCameraInput> m_firstPersonRotateCamera;
AZStd::shared_ptr<AzFramework::PanCameraInput> m_firstPersonPanCamera;
AZStd::shared_ptr<AzFramework::TranslateCameraInput> m_firstPersonTranslateCamera;
AZStd::shared_ptr<AzFramework::ScrollTranslationCameraInput> m_firstPersonScrollCamera;
AZStd::shared_ptr<AzFramework::LookScrollTranslationCameraInput> m_firstPersonScrollCamera;
AZStd::shared_ptr<AzFramework::FocusCameraInput> m_firstPersonFocusCamera;
AZStd::shared_ptr<AzFramework::PivotCameraInput> m_pivotCamera;
AZStd::shared_ptr<AzFramework::RotateCameraInput> m_pivotRotateCamera;
AZStd::shared_ptr<AzFramework::TranslateCameraInput> m_pivotTranslateCamera;
AZStd::shared_ptr<AzFramework::PivotDollyScrollCameraInput> m_pivotDollyScrollCamera;
AZStd::shared_ptr<AzFramework::PivotDollyMotionCameraInput> m_pivotDollyMoveCamera;
AZStd::shared_ptr<AzFramework::PanCameraInput> m_pivotPanCamera;
AZStd::shared_ptr<AzFramework::FocusCameraInput> m_pivotFocusCamera;
AZStd::shared_ptr<AzFramework::OrbitCameraInput> m_orbitCamera;
AZStd::shared_ptr<AzFramework::RotateCameraInput> m_orbitRotateCamera;
AZStd::shared_ptr<AzFramework::TranslateCameraInput> m_orbitTranslateCamera;
AZStd::shared_ptr<AzFramework::OrbitDollyScrollCameraInput> m_orbitDollyScrollCamera;
AZStd::shared_ptr<AzFramework::OrbitDollyMotionCameraInput> m_orbitDollyMoveCamera;
AZStd::shared_ptr<AzFramework::PanCameraInput> m_orbitPanCamera;
AZStd::shared_ptr<AzFramework::FocusCameraInput> m_orbitFocusCamera;
AzFramework::ViewportId m_viewportId;
};
@@ -73,7 +73,7 @@ void CEditorPreferencesPage_ViewportCamera::Reflect(AZ::SerializeContext& serial
->Field("TranslateSmoothing", &CameraMovementSettings::m_translateSmoothing)
->Field("TranslateSmoothness", &CameraMovementSettings::m_translateSmoothness)
->Field("CaptureCursorLook", &CameraMovementSettings::m_captureCursorLook)
->Field("PivotYawRotationInverted", &CameraMovementSettings::m_pivotYawRotationInverted)
->Field("OrbitYawRotationInverted", &CameraMovementSettings::m_orbitYawRotationInverted)
->Field("PanInvertedX", &CameraMovementSettings::m_panInvertedX)
->Field("PanInvertedY", &CameraMovementSettings::m_panInvertedY);
@@ -86,12 +86,12 @@ void CEditorPreferencesPage_ViewportCamera::Reflect(AZ::SerializeContext& serial
->Field("TranslateUp", &CameraInputSettings::m_translateUpChannelId)
->Field("TranslateDown", &CameraInputSettings::m_translateDownChannelId)
->Field("Boost", &CameraInputSettings::m_boostChannelId)
->Field("Pivot", &CameraInputSettings::m_pivotChannelId)
->Field("Orbit", &CameraInputSettings::m_orbitChannelId)
->Field("FreeLook", &CameraInputSettings::m_freeLookChannelId)
->Field("FreePan", &CameraInputSettings::m_freePanChannelId)
->Field("PivotLook", &CameraInputSettings::m_pivotLookChannelId)
->Field("PivotDolly", &CameraInputSettings::m_pivotDollyChannelId)
->Field("PivotPan", &CameraInputSettings::m_pivotPanChannelId)
->Field("OrbitLook", &CameraInputSettings::m_orbitLookChannelId)
->Field("OrbitDolly", &CameraInputSettings::m_orbitDollyChannelId)
->Field("OrbitPan", &CameraInputSettings::m_orbitPanChannelId)
->Field("Focus", &CameraInputSettings::m_focusChannelId);
serialize.Class<CEditorPreferencesPage_ViewportCamera>()
@@ -144,8 +144,8 @@ void CEditorPreferencesPage_ViewportCamera::Reflect(AZ::SerializeContext& serial
->Attribute(AZ::Edit::Attributes::Min, minValue)
->Attribute(AZ::Edit::Attributes::Visibility, &CameraMovementSettings::TranslateSmoothingVisibility)
->DataElement(
AZ::Edit::UIHandlers::CheckBox, &CameraMovementSettings::m_pivotYawRotationInverted, "Camera Pivot Yaw Inverted",
"Inverted yaw rotation while pivoting")
AZ::Edit::UIHandlers::CheckBox, &CameraMovementSettings::m_orbitYawRotationInverted, "Camera Orbit Yaw Inverted",
"Inverted yaw rotation while orbiting")
->DataElement(
AZ::Edit::UIHandlers::CheckBox, &CameraMovementSettings::m_panInvertedX, "Invert Pan X",
"Invert direction of pan in local X axis")
@@ -186,8 +186,8 @@ void CEditorPreferencesPage_ViewportCamera::Reflect(AZ::SerializeContext& serial
"Key/button to move the camera more quickly")
->Attribute(AZ::Edit::Attributes::StringList, &GetEditorInputNames)
->DataElement(
AZ::Edit::UIHandlers::ComboBox, &CameraInputSettings::m_pivotChannelId, "Pivot",
"Key/button to begin the camera pivot behavior")
AZ::Edit::UIHandlers::ComboBox, &CameraInputSettings::m_orbitChannelId, "Orbit",
"Key/button to begin the camera orbit behavior")
->Attribute(AZ::Edit::Attributes::StringList, &GetEditorInputNames)
->DataElement(
AZ::Edit::UIHandlers::ComboBox, &CameraInputSettings::m_freeLookChannelId, "Free Look",
@@ -197,19 +197,19 @@ void CEditorPreferencesPage_ViewportCamera::Reflect(AZ::SerializeContext& serial
AZ::Edit::UIHandlers::ComboBox, &CameraInputSettings::m_freePanChannelId, "Free Pan", "Key/button to begin camera free pan")
->Attribute(AZ::Edit::Attributes::StringList, &GetEditorInputNames)
->DataElement(
AZ::Edit::UIHandlers::ComboBox, &CameraInputSettings::m_pivotLookChannelId, "Pivot Look",
"Key/button to begin camera pivot look")
AZ::Edit::UIHandlers::ComboBox, &CameraInputSettings::m_orbitLookChannelId, "Orbit Look",
"Key/button to begin camera orbit look")
->Attribute(AZ::Edit::Attributes::StringList, &GetEditorInputNames)
->DataElement(
AZ::Edit::UIHandlers::ComboBox, &CameraInputSettings::m_pivotDollyChannelId, "Pivot Dolly",
"Key/button to begin camera pivot dolly")
AZ::Edit::UIHandlers::ComboBox, &CameraInputSettings::m_orbitDollyChannelId, "Orbit Dolly",
"Key/button to begin camera orbit dolly")
->Attribute(AZ::Edit::Attributes::StringList, &GetEditorInputNames)
->DataElement(
AZ::Edit::UIHandlers::ComboBox, &CameraInputSettings::m_pivotPanChannelId, "Pivot Pan",
"Key/button to begin camera pivot pan")
AZ::Edit::UIHandlers::ComboBox, &CameraInputSettings::m_orbitPanChannelId, "Orbit Pan",
"Key/button to begin camera orbit pan")
->Attribute(AZ::Edit::Attributes::StringList, &GetEditorInputNames)
->DataElement(
AZ::Edit::UIHandlers::ComboBox, &CameraInputSettings::m_focusChannelId, "Focus", "Key/button to focus camera pivot")
AZ::Edit::UIHandlers::ComboBox, &CameraInputSettings::m_focusChannelId, "Focus", "Key/button to focus camera orbit")
->Attribute(AZ::Edit::Attributes::StringList, &GetEditorInputNames);
editContext->Class<CEditorPreferencesPage_ViewportCamera>("Viewport Preferences", "Viewport Preferences")
@@ -268,7 +268,7 @@ void CEditorPreferencesPage_ViewportCamera::OnApply()
SandboxEditor::SetCameraTranslateSmoothness(m_cameraMovementSettings.m_translateSmoothness);
SandboxEditor::SetCameraTranslateSmoothingEnabled(m_cameraMovementSettings.m_translateSmoothing);
SandboxEditor::SetCameraCaptureCursorForLook(m_cameraMovementSettings.m_captureCursorLook);
SandboxEditor::SetCameraPivotYawRotationInverted(m_cameraMovementSettings.m_pivotYawRotationInverted);
SandboxEditor::SetCameraOrbitYawRotationInverted(m_cameraMovementSettings.m_orbitYawRotationInverted);
SandboxEditor::SetCameraPanInvertedX(m_cameraMovementSettings.m_panInvertedX);
SandboxEditor::SetCameraPanInvertedY(m_cameraMovementSettings.m_panInvertedY);
@@ -279,12 +279,12 @@ void CEditorPreferencesPage_ViewportCamera::OnApply()
SandboxEditor::SetCameraTranslateUpChannelId(m_cameraInputSettings.m_translateUpChannelId);
SandboxEditor::SetCameraTranslateDownChannelId(m_cameraInputSettings.m_translateDownChannelId);
SandboxEditor::SetCameraTranslateBoostChannelId(m_cameraInputSettings.m_boostChannelId);
SandboxEditor::SetCameraPivotChannelId(m_cameraInputSettings.m_pivotChannelId);
SandboxEditor::SetCameraOrbitChannelId(m_cameraInputSettings.m_orbitChannelId);
SandboxEditor::SetCameraFreeLookChannelId(m_cameraInputSettings.m_freeLookChannelId);
SandboxEditor::SetCameraFreePanChannelId(m_cameraInputSettings.m_freePanChannelId);
SandboxEditor::SetCameraPivotLookChannelId(m_cameraInputSettings.m_pivotLookChannelId);
SandboxEditor::SetCameraPivotDollyChannelId(m_cameraInputSettings.m_pivotDollyChannelId);
SandboxEditor::SetCameraPivotPanChannelId(m_cameraInputSettings.m_pivotPanChannelId);
SandboxEditor::SetCameraOrbitLookChannelId(m_cameraInputSettings.m_orbitLookChannelId);
SandboxEditor::SetCameraOrbitDollyChannelId(m_cameraInputSettings.m_orbitDollyChannelId);
SandboxEditor::SetCameraOrbitPanChannelId(m_cameraInputSettings.m_orbitPanChannelId);
SandboxEditor::SetCameraFocusChannelId(m_cameraInputSettings.m_focusChannelId);
SandboxEditor::EditorModularViewportCameraComposerNotificationBus::Broadcast(
@@ -304,7 +304,7 @@ void CEditorPreferencesPage_ViewportCamera::InitializeSettings()
m_cameraMovementSettings.m_translateSmoothness = SandboxEditor::CameraTranslateSmoothness();
m_cameraMovementSettings.m_translateSmoothing = SandboxEditor::CameraTranslateSmoothingEnabled();
m_cameraMovementSettings.m_captureCursorLook = SandboxEditor::CameraCaptureCursorForLook();
m_cameraMovementSettings.m_pivotYawRotationInverted = SandboxEditor::CameraPivotYawRotationInverted();
m_cameraMovementSettings.m_orbitYawRotationInverted = SandboxEditor::CameraOrbitYawRotationInverted();
m_cameraMovementSettings.m_panInvertedX = SandboxEditor::CameraPanInvertedX();
m_cameraMovementSettings.m_panInvertedY = SandboxEditor::CameraPanInvertedY();
@@ -315,11 +315,11 @@ void CEditorPreferencesPage_ViewportCamera::InitializeSettings()
m_cameraInputSettings.m_translateUpChannelId = SandboxEditor::CameraTranslateUpChannelId().GetName();
m_cameraInputSettings.m_translateDownChannelId = SandboxEditor::CameraTranslateDownChannelId().GetName();
m_cameraInputSettings.m_boostChannelId = SandboxEditor::CameraTranslateBoostChannelId().GetName();
m_cameraInputSettings.m_pivotChannelId = SandboxEditor::CameraPivotChannelId().GetName();
m_cameraInputSettings.m_orbitChannelId = SandboxEditor::CameraOrbitChannelId().GetName();
m_cameraInputSettings.m_freeLookChannelId = SandboxEditor::CameraFreeLookChannelId().GetName();
m_cameraInputSettings.m_freePanChannelId = SandboxEditor::CameraFreePanChannelId().GetName();
m_cameraInputSettings.m_pivotLookChannelId = SandboxEditor::CameraPivotLookChannelId().GetName();
m_cameraInputSettings.m_pivotDollyChannelId = SandboxEditor::CameraPivotDollyChannelId().GetName();
m_cameraInputSettings.m_pivotPanChannelId = SandboxEditor::CameraPivotPanChannelId().GetName();
m_cameraInputSettings.m_orbitLookChannelId = SandboxEditor::CameraOrbitLookChannelId().GetName();
m_cameraInputSettings.m_orbitDollyChannelId = SandboxEditor::CameraOrbitDollyChannelId().GetName();
m_cameraInputSettings.m_orbitPanChannelId = SandboxEditor::CameraOrbitPanChannelId().GetName();
m_cameraInputSettings.m_focusChannelId = SandboxEditor::CameraFocusChannelId().GetName();
}
@@ -54,7 +54,7 @@ private:
float m_translateSmoothness;
bool m_translateSmoothing;
bool m_captureCursorLook;
bool m_pivotYawRotationInverted;
bool m_orbitYawRotationInverted;
bool m_panInvertedX;
bool m_panInvertedY;
@@ -80,12 +80,12 @@ private:
AZStd::string m_translateUpChannelId;
AZStd::string m_translateDownChannelId;
AZStd::string m_boostChannelId;
AZStd::string m_pivotChannelId;
AZStd::string m_orbitChannelId;
AZStd::string m_freeLookChannelId;
AZStd::string m_freePanChannelId;
AZStd::string m_pivotLookChannelId;
AZStd::string m_pivotDollyChannelId;
AZStd::string m_pivotPanChannelId;
AZStd::string m_orbitLookChannelId;
AZStd::string m_orbitDollyChannelId;
AZStd::string m_orbitPanChannelId;
AZStd::string m_focusChannelId;
};
+25 -25
View File
@@ -28,7 +28,7 @@ namespace SandboxEditor
constexpr AZStd::string_view CameraRotateSpeedSetting = "/Amazon/Preferences/Editor/Camera/RotateSpeed";
constexpr AZStd::string_view CameraScrollSpeedSetting = "/Amazon/Preferences/Editor/Camera/DollyScrollSpeed";
constexpr AZStd::string_view CameraDollyMotionSpeedSetting = "/Amazon/Preferences/Editor/Camera/DollyMotionSpeed";
constexpr AZStd::string_view CameraPivotYawRotationInvertedSetting = "/Amazon/Preferences/Editor/Camera/YawRotationInverted";
constexpr AZStd::string_view CameraOrbitYawRotationInvertedSetting = "/Amazon/Preferences/Editor/Camera/YawRotationInverted";
constexpr AZStd::string_view CameraPanInvertedXSetting = "/Amazon/Preferences/Editor/Camera/PanInvertedX";
constexpr AZStd::string_view CameraPanInvertedYSetting = "/Amazon/Preferences/Editor/Camera/PanInvertedY";
constexpr AZStd::string_view CameraPanSpeedSetting = "/Amazon/Preferences/Editor/Camera/PanSpeed";
@@ -44,12 +44,12 @@ namespace SandboxEditor
constexpr AZStd::string_view CameraTranslateUpIdSetting = "/Amazon/Preferences/Editor/Camera/CameraTranslateUpId";
constexpr AZStd::string_view CameraTranslateDownIdSetting = "/Amazon/Preferences/Editor/Camera/CameraTranslateUpDownId";
constexpr AZStd::string_view CameraTranslateBoostIdSetting = "/Amazon/Preferences/Editor/Camera/TranslateBoostId";
constexpr AZStd::string_view CameraPivotIdSetting = "/Amazon/Preferences/Editor/Camera/PivotId";
constexpr AZStd::string_view CameraOrbitIdSetting = "/Amazon/Preferences/Editor/Camera/OrbitId";
constexpr AZStd::string_view CameraFreeLookIdSetting = "/Amazon/Preferences/Editor/Camera/FreeLookId";
constexpr AZStd::string_view CameraFreePanIdSetting = "/Amazon/Preferences/Editor/Camera/FreePanId";
constexpr AZStd::string_view CameraPivotLookIdSetting = "/Amazon/Preferences/Editor/Camera/PivotLookId";
constexpr AZStd::string_view CameraPivotDollyIdSetting = "/Amazon/Preferences/Editor/Camera/PivotDollyId";
constexpr AZStd::string_view CameraPivotPanIdSetting = "/Amazon/Preferences/Editor/Camera/PivotPanId";
constexpr AZStd::string_view CameraOrbitLookIdSetting = "/Amazon/Preferences/Editor/Camera/OrbitLookId";
constexpr AZStd::string_view CameraOrbitDollyIdSetting = "/Amazon/Preferences/Editor/Camera/OrbitDollyId";
constexpr AZStd::string_view CameraOrbitPanIdSetting = "/Amazon/Preferences/Editor/Camera/OrbitPanId";
constexpr AZStd::string_view CameraFocusIdSetting = "/Amazon/Preferences/Editor/Camera/FocusId";
template<typename T>
@@ -240,14 +240,14 @@ namespace SandboxEditor
SetRegistry(CameraDollyMotionSpeedSetting, speed);
}
bool CameraPivotYawRotationInverted()
bool CameraOrbitYawRotationInverted()
{
return GetRegistry(CameraPivotYawRotationInvertedSetting, false);
return GetRegistry(CameraOrbitYawRotationInvertedSetting, false);
}
void SetCameraPivotYawRotationInverted(const bool inverted)
void SetCameraOrbitYawRotationInverted(const bool inverted)
{
SetRegistry(CameraPivotYawRotationInvertedSetting, inverted);
SetRegistry(CameraOrbitYawRotationInvertedSetting, inverted);
}
bool CameraPanInvertedX()
@@ -404,14 +404,14 @@ namespace SandboxEditor
SetRegistry(CameraTranslateBoostIdSetting, cameraTranslateBoostId);
}
AzFramework::InputChannelId CameraPivotChannelId()
AzFramework::InputChannelId CameraOrbitChannelId()
{
return AzFramework::InputChannelId(GetRegistry(CameraPivotIdSetting, AZStd::string("keyboard_key_modifier_alt_l")).c_str());
return AzFramework::InputChannelId(GetRegistry(CameraOrbitIdSetting, AZStd::string("keyboard_key_modifier_alt_l")).c_str());
}
void SetCameraPivotChannelId(AZStd::string_view cameraPivotId)
void SetCameraOrbitChannelId(AZStd::string_view cameraOrbitId)
{
SetRegistry(CameraPivotIdSetting, cameraPivotId);
SetRegistry(CameraOrbitIdSetting, cameraOrbitId);
}
AzFramework::InputChannelId CameraFreeLookChannelId()
@@ -434,34 +434,34 @@ namespace SandboxEditor
SetRegistry(CameraFreePanIdSetting, cameraFreePanId);
}
AzFramework::InputChannelId CameraPivotLookChannelId()
AzFramework::InputChannelId CameraOrbitLookChannelId()
{
return AzFramework::InputChannelId(GetRegistry(CameraPivotLookIdSetting, AZStd::string("mouse_button_left")).c_str());
return AzFramework::InputChannelId(GetRegistry(CameraOrbitLookIdSetting, AZStd::string("mouse_button_left")).c_str());
}
void SetCameraPivotLookChannelId(AZStd::string_view cameraPivotLookId)
void SetCameraOrbitLookChannelId(AZStd::string_view cameraOrbitLookId)
{
SetRegistry(CameraPivotLookIdSetting, cameraPivotLookId);
SetRegistry(CameraOrbitLookIdSetting, cameraOrbitLookId);
}
AzFramework::InputChannelId CameraPivotDollyChannelId()
AzFramework::InputChannelId CameraOrbitDollyChannelId()
{
return AzFramework::InputChannelId(GetRegistry(CameraPivotDollyIdSetting, AZStd::string("mouse_button_right")).c_str());
return AzFramework::InputChannelId(GetRegistry(CameraOrbitDollyIdSetting, AZStd::string("mouse_button_right")).c_str());
}
void SetCameraPivotDollyChannelId(AZStd::string_view cameraPivotDollyId)
void SetCameraOrbitDollyChannelId(AZStd::string_view cameraOrbitDollyId)
{
SetRegistry(CameraPivotDollyIdSetting, cameraPivotDollyId);
SetRegistry(CameraOrbitDollyIdSetting, cameraOrbitDollyId);
}
AzFramework::InputChannelId CameraPivotPanChannelId()
AzFramework::InputChannelId CameraOrbitPanChannelId()
{
return AzFramework::InputChannelId(GetRegistry(CameraPivotPanIdSetting, AZStd::string("mouse_button_middle")).c_str());
return AzFramework::InputChannelId(GetRegistry(CameraOrbitPanIdSetting, AZStd::string("mouse_button_middle")).c_str());
}
void SetCameraPivotPanChannelId(AZStd::string_view cameraPivotPanId)
void SetCameraOrbitPanChannelId(AZStd::string_view cameraOrbitPanId)
{
SetRegistry(CameraPivotPanIdSetting, cameraPivotPanId);
SetRegistry(CameraOrbitPanIdSetting, cameraOrbitPanId);
}
AzFramework::InputChannelId CameraFocusChannelId()
+10 -10
View File
@@ -71,8 +71,8 @@ namespace SandboxEditor
SANDBOX_API float CameraDollyMotionSpeed();
SANDBOX_API void SetCameraDollyMotionSpeed(float speed);
SANDBOX_API bool CameraPivotYawRotationInverted();
SANDBOX_API void SetCameraPivotYawRotationInverted(bool inverted);
SANDBOX_API bool CameraOrbitYawRotationInverted();
SANDBOX_API void SetCameraOrbitYawRotationInverted(bool inverted);
SANDBOX_API bool CameraPanInvertedX();
SANDBOX_API void SetCameraPanInvertedX(bool inverted);
@@ -119,8 +119,8 @@ namespace SandboxEditor
SANDBOX_API AzFramework::InputChannelId CameraTranslateBoostChannelId();
SANDBOX_API void SetCameraTranslateBoostChannelId(AZStd::string_view cameraTranslateBoostId);
SANDBOX_API AzFramework::InputChannelId CameraPivotChannelId();
SANDBOX_API void SetCameraPivotChannelId(AZStd::string_view cameraPivotId);
SANDBOX_API AzFramework::InputChannelId CameraOrbitChannelId();
SANDBOX_API void SetCameraOrbitChannelId(AZStd::string_view cameraOrbitId);
SANDBOX_API AzFramework::InputChannelId CameraFreeLookChannelId();
SANDBOX_API void SetCameraFreeLookChannelId(AZStd::string_view cameraFreeLookId);
@@ -128,14 +128,14 @@ namespace SandboxEditor
SANDBOX_API AzFramework::InputChannelId CameraFreePanChannelId();
SANDBOX_API void SetCameraFreePanChannelId(AZStd::string_view cameraFreePanId);
SANDBOX_API AzFramework::InputChannelId CameraPivotLookChannelId();
SANDBOX_API void SetCameraPivotLookChannelId(AZStd::string_view cameraPivotLookId);
SANDBOX_API AzFramework::InputChannelId CameraOrbitLookChannelId();
SANDBOX_API void SetCameraOrbitLookChannelId(AZStd::string_view cameraOrbitLookId);
SANDBOX_API AzFramework::InputChannelId CameraPivotDollyChannelId();
SANDBOX_API void SetCameraPivotDollyChannelId(AZStd::string_view cameraPivotDollyId);
SANDBOX_API AzFramework::InputChannelId CameraOrbitDollyChannelId();
SANDBOX_API void SetCameraOrbitDollyChannelId(AZStd::string_view cameraOrbitDollyId);
SANDBOX_API AzFramework::InputChannelId CameraPivotPanChannelId();
SANDBOX_API void SetCameraPivotPanChannelId(AZStd::string_view cameraPivotPanId);
SANDBOX_API AzFramework::InputChannelId CameraOrbitPanChannelId();
SANDBOX_API void SetCameraOrbitPanChannelId(AZStd::string_view cameraOrbitPanId);
SANDBOX_API AzFramework::InputChannelId CameraFocusChannelId();
SANDBOX_API void SetCameraFocusChannelId(AZStd::string_view cameraFocusId);
+1
View File
@@ -28,6 +28,7 @@
#include "Objects/EntityObject.h"
#include <AzFramework/Terrain/TerrainDataRequestBus.h>
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
//////////////////////////////////////////////////////////////////////////
#define MUSIC_LEVEL_LIBRARY_FILE "Music.xml"
@@ -69,6 +69,7 @@ namespace UnitTest
m_rootWidget = AZStd::make_unique<QWidget>();
m_rootWidget->setFixedSize(WidgetSize);
m_rootWidget->move(0, 0); // explicitly set the widget to be in the upper left corner
m_controllerList = AZStd::make_shared<AzFramework::ViewportControllerList>();
m_controllerList->RegisterViewportContext(TestViewportId);
@@ -344,4 +345,51 @@ namespace UnitTest
// Clean-up
HaltCollaborators();
}
// test to verify deltas and cursor positions are handled correctly when the widget is moved
TEST_F(ModularViewportCameraControllerFixture, CameraDoesNotStutterAfterWidgetIsMoved)
{
// Given
PrepareCollaborators();
SandboxEditor::SetCameraCaptureCursorForLook(true);
const float deltaTime = 1.0f / 60.0f;
// When
// move cursor to the center of the screen
auto start = QPoint(WidgetSize.width() / 2, WidgetSize.height() / 2);
MouseMove(m_rootWidget.get(), start, QPoint(0, 0));
m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(deltaTime), AZ::ScriptTimePoint() });
// move camera right
const auto mouseDelta = QPoint(200, 0);
MousePressAndMove(m_rootWidget.get(), start, mouseDelta, Qt::MouseButton::RightButton);
m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(deltaTime), AZ::ScriptTimePoint() });
QTest::mouseRelease(m_rootWidget.get(), Qt::MouseButton::RightButton, Qt::NoModifier, start + mouseDelta);
// update the position of the widget
const auto offset = QPoint(500, 500);
m_rootWidget->move(offset);
// move cursor back to widget center
MouseMove(m_rootWidget.get(), start, QPoint(0, 0));
m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(deltaTime), AZ::ScriptTimePoint() });
// move camera left
MousePressAndMove(m_rootWidget.get(), start, -mouseDelta, Qt::MouseButton::RightButton);
m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(deltaTime), AZ::ScriptTimePoint() });
// Then
// ensure the camera rotation has returned to the identity
const AZ::Quaternion cameraRotation = m_cameraViewportContextView->GetCameraTransform().GetRotation();
const auto eulerAngles = AzFramework::EulerAngles(AZ::Matrix3x3::CreateFromQuaternion(cameraRotation));
using ::testing::FloatNear;
EXPECT_THAT(eulerAngles.GetX(), FloatNear(0.0f, 0.001f));
EXPECT_THAT(eulerAngles.GetZ(), FloatNear(0.0f, 0.001f));
// Clean-up
HaltCollaborators();
}
} // namespace UnitTest
+1
View File
@@ -98,6 +98,7 @@ AZ_POP_DISABLE_WARNING
#include "ActionManager.h"
#include <ImGuiBus.h>
#include <AzToolsFramework/Viewport/ViewportMessages.h>
#include <LmbrCentral/Audio/AudioSystemComponentBus.h>
using namespace AZ;
+23 -14
View File
@@ -30,6 +30,8 @@
#include "Plugins/ComponentEntityEditorPlugin/Objects/ComponentEntityObject.h"
#include <AzCore/Console/Console.h>
#include <AzToolsFramework/Viewport/ViewportMessages.h>
#include <AzToolsFramework/ComponentMode/EditorComponentModeBus.h>
AZ_CVAR_EXTERNED(bool, ed_visibility_logTiming);
@@ -107,14 +109,13 @@ CObjectManager::CObjectManager()
m_objectsByName.reserve(1024);
LoadRegistry();
AzToolsFramework::ComponentModeFramework::EditorComponentModeNotificationBus::Handler::BusConnect(
AzToolsFramework::GetEntityContextId());
AzToolsFramework::ViewportEditorModeNotificationsBus::Handler::BusConnect(AzToolsFramework::GetEntityContextId());
}
//////////////////////////////////////////////////////////////////////////
CObjectManager::~CObjectManager()
{
AzToolsFramework::ComponentModeFramework::EditorComponentModeNotificationBus::Handler::BusDisconnect();
AzToolsFramework::ViewportEditorModeNotificationsBus::Handler::BusDisconnect();
m_bExiting = true;
SaveRegistry();
@@ -2306,25 +2307,33 @@ void CObjectManager::SelectObjectInRect(CBaseObject* pObj, CViewport* view, HitC
}
}
void CObjectManager::EnteredComponentMode(const AZStd::vector<AZ::Uuid>& /*componentModeTypes*/)
void CObjectManager::OnEditorModeActivated(
[[maybe_unused]] const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode)
{
// 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)
if (mode == AzToolsFramework::ViewportEditorMode::Component)
{
gizmoManager->RemoveGizmo(gizmoManager->GetGizmoByIndex(static_cast<int>(i)));
// 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::LeftComponentMode(const AZStd::vector<AZ::Uuid>& /*componentModeTypes*/)
void CObjectManager::OnEditorModeDeactivated(
[[maybe_unused]] const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode)
{
// show translate/rotate/scale gizmo again
if (IGizmoManager* gizmoManager = GetGizmoManager())
if (mode == AzToolsFramework::ViewportEditorMode::Component)
{
if (CBaseObject* selectedObject = GetIEditor()->GetSelectedObject())
// show translate/rotate/scale gizmo again
if (IGizmoManager* gizmoManager = GetGizmoManager())
{
gizmoManager->AddGizmo(new CAxisGizmo(selectedObject));
if (CBaseObject* selectedObject = GetIEditor()->GetSelectedObject())
{
gizmoManager->AddGizmo(new CAxisGizmo(selectedObject));
}
}
}
}
+8 -5
View File
@@ -20,8 +20,9 @@
#include "ObjectManagerEventBus.h"
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzToolsFramework/ComponentMode/EditorComponentModeBus.h>
#include <AzToolsFramework/API/ViewportEditorModeTrackerNotificationBus.h>
#include <AzCore/EBus/EBus.h>
#include <AzCore/Component/Component.h>
#include <Include/SandboxAPI.h>
// forward declarations.
@@ -58,7 +59,7 @@ public:
*/
class CObjectManager
: public IObjectManager
, private AzToolsFramework::ComponentModeFramework::EditorComponentModeNotificationBus::Handler
, private AzToolsFramework::ViewportEditorModeNotificationsBus::Handler
{
public:
//! Selection functor callback.
@@ -329,9 +330,11 @@ private:
void FindDisplayableObjects(DisplayContext& dc, bool bDisplay);
// EditorComponentModeNotificationBus
void EnteredComponentMode(const AZStd::vector<AZ::Uuid>& componentModeTypes) override;
void LeftComponentMode(const AZStd::vector<AZ::Uuid>& componentModeTypes) override;
// 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;
@@ -36,6 +36,7 @@
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
#include <AzToolsFramework/Entity/EditorEntityInfoBus.h>
#include <AzToolsFramework/UI/ComponentPalette/ComponentPaletteUtil.hxx>
#include <AzToolsFramework/Viewport/ViewportMessages.h>
#include <QGraphicsOpacityEffect>
#include <QLabel>
@@ -267,8 +268,7 @@ OutlinerWidget::OutlinerWidget(QWidget* pParent, Qt::WindowFlags flags)
ToolsApplicationEvents::Bus::Handler::BusConnect();
AzToolsFramework::EditorEntityContextNotificationBus::Handler::BusConnect();
AzToolsFramework::SliceEditorEntityOwnershipServiceNotificationBus::Handler::BusConnect();
AzToolsFramework::ComponentModeFramework::EditorComponentModeNotificationBus::Handler::BusConnect(
AzToolsFramework::GetEntityContextId());
AzToolsFramework::ViewportEditorModeNotificationsBus::Handler::BusConnect(AzToolsFramework::GetEntityContextId());
AzToolsFramework::EditorEntityInfoNotificationBus::Handler::BusConnect();
AzToolsFramework::EditorWindowUIRequestBus::Handler::BusConnect();
}
@@ -276,7 +276,7 @@ OutlinerWidget::OutlinerWidget(QWidget* pParent, Qt::WindowFlags flags)
OutlinerWidget::~OutlinerWidget()
{
AzToolsFramework::EditorWindowUIRequestBus::Handler::BusDisconnect();
AzToolsFramework::ComponentModeFramework::EditorComponentModeNotificationBus::Handler::BusDisconnect();
AzToolsFramework::ViewportEditorModeNotificationsBus::Handler::BusDisconnect();
AzToolsFramework::EditorEntityInfoNotificationBus::Handler::BusDisconnect();
AzToolsFramework::EditorPickModeNotificationBus::Handler::BusDisconnect();
EntityHighlightMessages::Bus::Handler::BusDisconnect();
@@ -1335,14 +1335,22 @@ void OutlinerWidget::SetEditorUiEnabled(bool enable)
EnableUi(enable);
}
void OutlinerWidget::EnteredComponentMode([[maybe_unused]] const AZStd::vector<AZ::Uuid>& componentModeTypes)
void OutlinerWidget::OnEditorModeActivated(
[[maybe_unused]] const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode)
{
EnableUi(false);
if (mode == AzToolsFramework::ViewportEditorMode::Component)
{
EnableUi(false);
}
}
void OutlinerWidget::LeftComponentMode([[maybe_unused]] const AZStd::vector<AZ::Uuid>& componentModeTypes)
void OutlinerWidget::OnEditorModeDeactivated(
[[maybe_unused]] const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode)
{
EnableUi(true);
if (mode == AzToolsFramework::ViewportEditorMode::Component)
{
EnableUi(true);
}
}
void OutlinerWidget::OnSliceInstantiated(const AZ::Data::AssetId& /*sliceAssetId*/, AZ::SliceComponent::SliceInstanceAddress& sliceAddress, const AzFramework::SliceInstantiationTicket& /*ticket*/)
@@ -16,7 +16,7 @@
#include <AzCore/base.h>
#include <AzToolsFramework/API/EditorWindowRequestBus.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AzToolsFramework/ComponentMode/EditorComponentModeBus.h>
#include <AzToolsFramework/API/ViewportEditorModeTrackerNotificationBus.h>
#include <AzToolsFramework/Entity/EditorEntityInfoBus.h>
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
#include <AzToolsFramework/Entity/SliceEditorEntityOwnershipServiceBus.h>
@@ -58,7 +58,7 @@ class OutlinerWidget
, private AzToolsFramework::EditorEntityContextNotificationBus::Handler
, private AzToolsFramework::SliceEditorEntityOwnershipServiceNotificationBus::Handler
, private AzToolsFramework::EditorEntityInfoNotificationBus::Handler
, private AzToolsFramework::ComponentModeFramework::EditorComponentModeNotificationBus::Handler
, private AzToolsFramework::ViewportEditorModeNotificationsBus::Handler
, private AzToolsFramework::EditorWindowUIRequestBus::Handler
{
Q_OBJECT;
@@ -105,9 +105,11 @@ private:
void OnEntityInfoUpdatedAddChildEnd(AZ::EntityId /*parentId*/, AZ::EntityId /*childId*/) override;
void OnEntityInfoUpdatedName(AZ::EntityId entityId, const AZStd::string& /*name*/) override;
// EditorComponentModeNotificationBus
void EnteredComponentMode(const AZStd::vector<AZ::Uuid>& componentModeTypes) override;
void LeftComponentMode(const AZStd::vector<AZ::Uuid>& componentModeTypes) override;
// ViewportEditorModeNotificationsBus overrides ...
void OnEditorModeActivated(
const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode) override;
void OnEditorModeDeactivated(
const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode) override;
// EditorWindowUIRequestBus overrides
void SetEditorUiEnabled(bool enable) override;
+52 -7
View File
@@ -37,6 +37,7 @@
#include <AzAssetBrowser/AzAssetBrowserWindow.h>
#include <AzToolsFramework/UI/UICore/WidgetHelpers.h>
#include <AzQtComponents/Utilities/AutoSettingsGroup.h>
#include <AzToolsFramework/API/ViewportEditorModeTrackerNotificationBus.h>
#include <AzToolsFramework/UI/Docking/DockWidgetUtils.h>
#include <AzToolsFramework/UI/PropertyEditor/ComponentEditor.hxx>
#include <AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.hxx>
@@ -44,11 +45,54 @@
#include <AzQtComponents/Buses/ShortcutDispatch.h>
#include <AzQtComponents/Utilities/QtViewPaneEffects.h>
#include <AzQtComponents/Components/StyleManager.h>
#include <AzCore/UserSettings/UserSettingsComponent.h>
#include "ShortcutDispatcher.h"
// Helper for EditorComponentModeNotifications to be used
// as a member instead of inheriting from EBus directly.
class ViewportEditorModeNotificationsBusImpl
: public AzToolsFramework::ViewportEditorModeNotificationsBus::Handler
{
public:
// Set the function to be called when entering ComponentMode.
void SetEnteredComponentModeFunc(
const AZStd::function<void(const AzToolsFramework::ViewportEditorModesInterface&)>& enteredComponentModeFunc)
{
m_enteredComponentModeFunc = enteredComponentModeFunc;
}
// Set the function to be called when leaving ComponentMode.
void SetLeftComponentModeFunc(
const AZStd::function<void(const AzToolsFramework::ViewportEditorModesInterface&)>& leftComponentModeFunc)
{
m_leftComponentModeFunc = leftComponentModeFunc;
}
private:
// ViewportEditorModeNotificationsBus overrides ...
void OnEditorModeActivated(
const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode) override
{
if (mode == AzToolsFramework::ViewportEditorMode::Component)
{
m_enteredComponentModeFunc(editorModeState);
}
}
void OnEditorModeDeactivated(
const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode) override
{
if (mode == AzToolsFramework::ViewportEditorMode::Component)
{
m_leftComponentModeFunc(editorModeState);
}
}
AZStd::function<void(const AzToolsFramework::ViewportEditorModesInterface&)> m_enteredComponentModeFunc; ///< Function to call when entering ComponentMode.
AZStd::function<void(const AzToolsFramework::ViewportEditorModesInterface&)> m_leftComponentModeFunc; ///< Function to call when leaving ComponentMode.
};
struct ViewLayoutState
{
QVector<QString> viewPanes;
@@ -519,16 +563,17 @@ QtViewPaneManager::QtViewPaneManager(QObject* parent)
, m_settings(nullptr)
, m_restoreInProgress(false)
, m_advancedDockManager(nullptr)
, m_componentModeNotifications(AZStd::make_unique<ViewportEditorModeNotificationsBusImpl>())
{
qRegisterMetaTypeStreamOperators<ViewLayoutState>("ViewLayoutState");
qRegisterMetaTypeStreamOperators<QVector<QString> >("QVector<QString>");
// view pane manager is interested when we enter/exit ComponentMode
m_componentModeNotifications.BusConnect(AzToolsFramework::GetEntityContextId());
m_componentModeNotifications->BusConnect(AzToolsFramework::GetEntityContextId());
m_windowRequest.BusConnect();
m_componentModeNotifications.SetEnteredComponentModeFunc(
[this](const AZStd::vector<AZ::Uuid>& /*componentModeTypes*/)
m_componentModeNotifications->SetEnteredComponentModeFunc(
[this](const AzToolsFramework::ViewportEditorModesInterface&)
{
// gray out panels when entering ComponentMode
SetDefaultActionsEnabled(false, m_registeredPanes, [](QWidget* widget, bool on)
@@ -537,8 +582,8 @@ QtViewPaneManager::QtViewPaneManager(QObject* parent)
});
});
m_componentModeNotifications.SetLeftComponentModeFunc(
[this](const AZStd::vector<AZ::Uuid>& /*componentModeTypes*/)
m_componentModeNotifications->SetLeftComponentModeFunc(
[this](const AzToolsFramework::ViewportEditorModesInterface&)
{
// enable panels again when leaving ComponentMode
SetDefaultActionsEnabled(true, m_registeredPanes, [](QWidget* widget, bool on)
@@ -563,7 +608,7 @@ QtViewPaneManager::QtViewPaneManager(QObject* parent)
QtViewPaneManager::~QtViewPaneManager()
{
m_windowRequest.BusDisconnect();
m_componentModeNotifications.BusDisconnect();
m_componentModeNotifications->BusDisconnect();
}
static bool lessThan(const QtViewPane& v1, const QtViewPane& v2)
+4 -4
View File
@@ -17,7 +17,6 @@
#include <AzQtComponents/Components/DockTabWidget.h>
#include <AzQtComponents/Components/StyledDockWidget.h>
#include <AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI.h>
#include <AzToolsFramework/ComponentMode/EditorComponentModeBus.h>
#include <AzToolsFramework/API/EditorWindowRequestBus.h>
#include <QObject>
@@ -34,6 +33,7 @@
#endif
class QMainWindow;
class ViewportEditorModeNotificationsBusImpl;
struct ViewLayoutState;
namespace AzQtComponents
@@ -245,9 +245,9 @@ private:
QPointer<AzQtComponents::FancyDocking> m_advancedDockManager;
using EditorComponentModeNotificationBusImpl = AzToolsFramework::ComponentModeFramework::EditorComponentModeNotificationBusImpl;
EditorComponentModeNotificationBusImpl m_componentModeNotifications; //!< Helper for EditorComponentModeNotificationBus so
//!< QtViewPaneManager does not need to inherit directly from it. */
AZStd::unique_ptr<ViewportEditorModeNotificationsBusImpl>
m_componentModeNotifications; //!< Helper for EditorComponentModeNotificationBus so
//!< QtViewPaneManager does not need to inherit directly from it. */
using EditorWindowRequestBusImpl = AzToolsFramework::EditorWindowRequestBusImpl;
EditorWindowRequestBusImpl m_windowRequest; //!< Helper for EditorWindowRequestBus so
+2 -2
View File
@@ -119,7 +119,7 @@ bool CXmlArchive::SaveToPak([[maybe_unused]] const QString& levelPath, CPakFile&
_smart_ptr<IXmlStringData> pXmlStrData = root->getXMLData(5000000);
// Save xml file.
QString xmlFilename = "Level.editor_xml";
QString xmlFilename = "level.editor_xml";
pakFile.UpdateFile(xmlFilename.toUtf8().data(), (void*)pXmlStrData->GetString(), static_cast<int>(pXmlStrData->GetStringLength()));
if (pakFile.GetArchive())
@@ -134,7 +134,7 @@ bool CXmlArchive::SaveToPak([[maybe_unused]] const QString& levelPath, CPakFile&
//////////////////////////////////////////////////////////////////////////
bool CXmlArchive::LoadFromPak(const QString& levelPath, CPakFile& pakFile)
{
QString xmlFilename = QDir(levelPath).absoluteFilePath("Level.editor_xml");
QString xmlFilename = QDir(levelPath).absoluteFilePath("level.editor_xml");
root = XmlHelpers::LoadXmlFromFile(xmlFilename.toUtf8().data());
if (!root)
{
+1
View File
@@ -19,6 +19,7 @@
#include <AzToolsFramework/API/ComponentEntitySelectionBus.h>
#include <AzToolsFramework/ViewportSelection/EditorSelectionUtil.h>
#include <AzToolsFramework/Viewport/ViewportMessages.h>
// Editor
#include "ViewManager.h"
@@ -173,6 +173,7 @@ namespace AZ
if (assetTracker)
{
assetTracker->FixUpAsset(*instance);
assetTracker->AddAsset(*instance);
}
@@ -185,7 +186,20 @@ namespace AZ
return context.Report(result, message);
}
void SerializedAssetTracker::AddAsset(Asset<AssetData>& asset)
void SerializedAssetTracker::SetAssetFixUp(AssetFixUp assetFixUpCallback)
{
m_assetFixUpCallback = AZStd::move(assetFixUpCallback);
}
void SerializedAssetTracker::FixUpAsset(Asset<AssetData>& asset)
{
if (m_assetFixUpCallback)
{
m_assetFixUpCallback(asset);
}
}
void SerializedAssetTracker::AddAsset(Asset<AssetData> asset)
{
m_serializedAssets.emplace_back(asset);
}
@@ -199,5 +213,6 @@ namespace AZ
{
return m_serializedAssets;
}
} // namespace Data
} // namespace AZ
@@ -39,13 +39,18 @@ namespace AZ
{
public:
AZ_RTTI(SerializedAssetTracker, "{1E067091-8C0A-44B1-A455-6E97663F6963}");
using AssetFixUp = AZStd::function<void(Asset<AssetData>& asset)>;
void AddAsset(Asset<AssetData>& asset);
void SetAssetFixUp(AssetFixUp assetFixUpCallback);
void FixUpAsset(Asset<AssetData>& asset);
void AddAsset(Asset<AssetData> asset);
AZStd::vector<Asset<AssetData>>& GetTrackedAssets();
const AZStd::vector<Asset<AssetData>>& GetTrackedAssets() const;
private:
AZStd::vector<Asset<AssetData>> m_serializedAssets;
AssetFixUp m_assetFixUpCallback;
};
} // namespace Data
} // namespace AZ
@@ -476,15 +476,16 @@ namespace AZ
// Responsible for using the Json Serialization Issue Callback system
// to determine when a JSON Patch or JSON Merge Patch modifies a value
// at a path underneath the IConsole::ConsoleRootCommandKey JSON pointer
// at a path underneath the IConsole::ConsoleRuntimeCommandKey JSON pointer
JsonSerializationResult::ResultCode operator()(AZStd::string_view message,
JsonSerializationResult::ResultCode result, AZStd::string_view path)
{
AZ::IO::PathView consoleRootCommandKey{ IConsole::ConsoleRootCommandKey, AZ::IO::PosixPathSeparator };
constexpr AZ::IO::PathView consoleRootCommandKey{ IConsole::ConsoleRuntimeCommandKey, AZ::IO::PosixPathSeparator };
constexpr AZ::IO::PathView consoleAutoexecCommandKey{ IConsole::ConsoleAutoexecCommandKey, AZ::IO::PosixPathSeparator };
AZ::IO::PathView inputKey{ path, AZ::IO::PosixPathSeparator };
if (result.GetTask() == JsonSerializationResult::Tasks::Merge
&& result.GetProcessing() == JsonSerializationResult::Processing::Completed
&& inputKey.IsRelativeTo(consoleRootCommandKey))
&& (inputKey.IsRelativeTo(consoleRootCommandKey) || inputKey.IsRelativeTo(consoleAutoexecCommandKey)))
{
if (auto type = m_settingsRegistry.GetType(path); type != SettingsRegistryInterface::Type::NoType)
{
@@ -510,12 +511,24 @@ namespace AZ
{
using FixedValueString = AZ::SettingsRegistryInterface::FixedValueString;
AZ::IO::PathView consoleRootCommandKey{ IConsole::ConsoleRootCommandKey, AZ::IO::PosixPathSeparator };
constexpr AZ::IO::PathView consoleRuntimeCommandKey{ IConsole::ConsoleRuntimeCommandKey, AZ::IO::PosixPathSeparator };
constexpr AZ::IO::PathView consoleAutoexecCommandKey{ IConsole::ConsoleAutoexecCommandKey, AZ::IO::PosixPathSeparator };
AZ::IO::PathView inputKey{ path, AZ::IO::PosixPathSeparator };
// The ConsoleRootComamndKey is not a command itself so strictly children keys are being examined
if (inputKey.IsRelativeTo(consoleRootCommandKey) && inputKey != consoleRootCommandKey)
// Abuses the IsRelativeToFuncton function of the path class to extract the console
// command from the settings registry objects
FixedValueString command;
if (inputKey != consoleRuntimeCommandKey && inputKey.IsRelativeTo(consoleRuntimeCommandKey))
{
command = inputKey.LexicallyRelative(consoleRuntimeCommandKey).Native();
}
else if (inputKey != consoleAutoexecCommandKey && inputKey.IsRelativeTo(consoleAutoexecCommandKey))
{
command = inputKey.LexicallyRelative(consoleAutoexecCommandKey).Native();
}
if (!command.empty())
{
FixedValueString command = inputKey.LexicallyRelative(consoleRootCommandKey).Native();
ConsoleCommandContainer commandArgs;
// Argument string which stores the value from the Settings Registry long enough
// to pass into the PerformCommand. The ConsoleCommandContainer stores string_views
@@ -603,9 +616,10 @@ namespace AZ
void Console::RegisterCommandInvokerWithSettingsRegistry(AZ::SettingsRegistryInterface& settingsRegistry)
{
// Make sure the there is a JSON object at the path of AZ::IConsole::ConsoleRootCommandKey
// Make sure the there is a JSON object at the ConsoleRuntimeCommandKey or ConsoleAutoexecKey
// So that JSON Patch is able to add values underneath that object (JSON Patch doesn't create intermediate objects)
settingsRegistry.MergeSettings(R"({ "Amazon": { "AzCore": { "Runtime": { "ConsoleCommands": {} } }}})",
settingsRegistry.MergeSettings(R"({ "Amazon": { "AzCore": { "Runtime": { "ConsoleCommands": {} } } })"
R"(,"O3DE": { "Autoexec": { "ConsoleCommands": {} } } })",
SettingsRegistryInterface::Format::JsonMergePatch);
m_consoleCommandKeyHandler = settingsRegistry.RegisterNotifier(ConsoleCommandKeyNotificationHandler{ settingsRegistry, *this });
@@ -31,7 +31,8 @@ namespace AZ
using FunctorVisitor = AZStd::function<void(ConsoleFunctorBase*)>;
inline static constexpr AZStd::string_view ConsoleRootCommandKey = "/Amazon/AzCore/Runtime/ConsoleCommands";
inline static constexpr AZStd::string_view ConsoleRuntimeCommandKey = "/Amazon/AzCore/Runtime/ConsoleCommands";
inline static constexpr AZStd::string_view ConsoleAutoexecCommandKey = "/O3DE/Autoexec/ConsoleCommands";
IConsole() = default;
virtual ~IConsole() = default;
@@ -46,5 +46,23 @@ namespace AZ
private:
AZStd::sys_time_t m_timeStamp;
};
//! Utility type that updates the given variable with the lifetime of the object in cycles.
//! Useful for quick scope based timing.
struct ScopedTimer
{
explicit ScopedTimer(AZStd::sys_time_t& variable)
: m_variable(variable)
{
m_timer.Stamp();
}
~ScopedTimer()
{
m_variable = m_timer.GetDeltaTimeInTicks();
}
AZStd::sys_time_t& m_variable;
Timer m_timer;
};
}
}
@@ -224,6 +224,8 @@ namespace AZ
void Debug::Trace::Terminate(int exitCode)
{
AZ_TracePrintf("Exit", "Called Terminate() with exit code: 0x%x", exitCode);
AZ::Debug::Trace::PrintCallstack("Exit");
Platform::Terminate(exitCode);
}
+17 -9
View File
@@ -160,8 +160,8 @@ namespace AZ
/**
* Locking primitive that is used when executing events in the event queue.
*/
using EventQueueMutexType = typename AZStd::Utils::if_c<AZStd::is_same<typename Traits::EventQueueMutexType, NullMutex>::value, // if EventQueueMutexType==NullMutex use MutexType otherwise EventQueueMutexType
MutexType, typename Traits::EventQueueMutexType>::type;
using EventQueueMutexType = AZStd::conditional_t<AZStd::is_same<typename Traits::EventQueueMutexType, NullMutex>::value, // if EventQueueMutexType==NullMutex use MutexType otherwise EventQueueMutexType
MutexType, typename Traits::EventQueueMutexType>;
/**
* Pointer to an address on the bus.
@@ -180,14 +180,22 @@ namespace AZ
* `<BusName>::ExecuteQueuedEvents()`.
* By default, the event queue is disabled.
*/
static const bool EnableEventQueue = Traits::EnableEventQueue;
static const bool EventQueueingActiveByDefault = Traits::EventQueueingActiveByDefault;
static const bool EnableQueuedReferences = Traits::EnableQueuedReferences;
static constexpr bool EnableEventQueue = Traits::EnableEventQueue;
static constexpr bool EventQueueingActiveByDefault = Traits::EventQueueingActiveByDefault;
static constexpr bool EnableQueuedReferences = Traits::EnableQueuedReferences;
/**
* True if the EBus supports more than one address. Otherwise, false.
*/
static const bool HasId = Traits::AddressPolicy != EBusAddressPolicy::Single;
static constexpr bool HasId = Traits::AddressPolicy != EBusAddressPolicy::Single;
/**
* Template Lock Guard class that wraps around the Mutex
* The EBus uses for Dispatching Events.
* This is not the EBus Context Mutex if LocklessDispatch is true
*/
template <typename DispatchMutex>
using DispatchLockGuard = typename Traits::template DispatchLockGuard<DispatchMutex, Traits::LocklessDispatch>;
};
/**
@@ -460,7 +468,7 @@ namespace AZ
using BusPtr = typename Traits::BusPtr;
/**
* Helper to queue an event by BusIdType only when function queueing is enabled
* Helper to queue an event by BusIdType only when function queueing is enabled
* @param id Address ID. Handlers that are connected to this ID will receive the event.
* @param func Function pointer of the event to dispatch.
* @param args Function arguments that are passed to each handler.
@@ -581,7 +589,7 @@ namespace AZ
, public EBusBroadcaster<Bus, Traits>
, public EBusEventer<Bus, Traits>
, public EBusEventEnumerator<Bus, Traits>
, public AZStd::Utils::if_c<Traits::EnableEventQueue, EBusEventQueue<Bus, Traits>, EBusNullQueue>::type
, public AZStd::conditional_t<Traits::EnableEventQueue, EBusEventQueue<Bus, Traits>, EBusNullQueue>
{
};
@@ -599,7 +607,7 @@ namespace AZ
: public EventDispatcher<Bus, Traits>
, public EBusBroadcaster<Bus, Traits>
, public EBusBroadcastEnumerator<Bus, Traits>
, public AZStd::Utils::if_c<Traits::EnableEventQueue, EBusBroadcastQueue<Bus, Traits>, EBusNullQueue>::type
, public AZStd::conditional_t<Traits::EnableEventQueue, EBusBroadcastQueue<Bus, Traits>, EBusNullQueue>
{
};
+37 -4
View File
@@ -77,9 +77,11 @@ namespace AZ
public:
/**
* Allocator used by the EBus.
* The default setting is AZStd::allocator, which uses AZ::SystemAllocator.
* The default setting is Internal EBusEnvironmentAllocator
* EBus code stores their Context instances in static memory
* Therfore the configured allocator must last as long as the EBus in a module
*/
using AllocatorType = AZStd::allocator;
using AllocatorType = AZ::Internal::EBusEnvironmentAllocator;
/**
* Defines how many handlers can connect to an address on the EBus
@@ -236,6 +238,17 @@ namespace AZ
* code before or after an event.
*/
using EventProcessingPolicy = EBusEventProcessingPolicy;
/**
* Template Lock Guard class that wraps around the Mutex
* The EBus Context uses the LockGuard when dispatching
* (either AZStd::scoped_lock<MutexType> or NullLockGuard<MutexType>)
* The IsLocklessDispatch bool is there to defer evaluation of the LocklessDispatch constant
* Otherwise the value above in EBusTraits.h is always used and not the value
* that the derived trait class sets.
*/
template <typename DispatchMutex, bool IsLocklessDispatch>
using DispatchLockGuard = AZStd::conditional_t<IsLocklessDispatch, AZ::Internal::NullLockGuard<DispatchMutex>, AZStd::scoped_lock<DispatchMutex>>;
};
namespace Internal
@@ -496,6 +509,14 @@ namespace AZ
*/
static const bool HasId = Traits::AddressPolicy != EBusAddressPolicy::Single;
/**
* Template Lock Guard class that wraps around the Mutex
* The EBus uses for Dispatching Events.
* This is not EBus Context Mutex when LocklessDispatch is set
*/
template <typename DispatchMutex>
using DispatchLockGuard = typename ImplTraits::template DispatchLockGuard<DispatchMutex>;
//////////////////////////////////////////////////////////////////////////
// Check to help identify common mistakes
/// @cond EXCLUDE_DOCS
@@ -620,11 +641,11 @@ namespace AZ
using ContextMutexType = AZStd::conditional_t<BusTraits::LocklessDispatch && AZStd::is_same_v<MutexType, AZ::NullMutex>, AZStd::shared_mutex, MutexType>;
/**
* The scoped lock guard to use (either AZStd::scoped_lock<MutexType> or NullLockGuard<MutexType>
* The scoped lock guard to use
* during broadcast/event dispatch.
* @see EBusTraits::LocklessDispatch
*/
using DispatchLockGuard = AZStd::conditional_t<BusTraits::LocklessDispatch, AZ::Internal::NullLockGuard<ContextMutexType>, AZStd::scoped_lock<ContextMutexType>>;
using DispatchLockGuard = DispatchLockGuard<ContextMutexType>;
/**
* The scoped lock guard to use during connection. Some specialized policies execute handler methods which
@@ -704,6 +725,11 @@ namespace AZ
static Context& GetOrCreateContext(bool trackCallstack=true);
static bool IsInDispatch(Context* context = GetContext(false));
/**
* Returns whether the EBus context is in the middle of a dispatch on the current thread
*/
static bool IsInDispatchThisThread(Context* context = GetContext(false));
/// @cond EXCLUDE_DOCS
struct RouterCallstackEntry
: public CallstackEntry
@@ -1208,6 +1234,13 @@ AZ_POP_DISABLE_WARNING
return context != nullptr && context->m_dispatches > 0;
}
template<class Interface, class Traits>
bool EBus<Interface, Traits>::IsInDispatchThisThread(Context* context)
{
return context != nullptr && context->s_callstack != nullptr
&& context->s_callstack->m_prev != nullptr;
}
//=========================================================================
template<class Interface, class Traits>
EBus<Interface, Traits>::RouterCallstackEntry::RouterCallstackEntry(Iterator it, const BusIdType* busId, bool isQueued, bool isReverse)
@@ -18,6 +18,14 @@
#include <AzCore/std/parallel/thread.h>
#include <AzCore/Math/MathUtils.h>
#include <AzCore/Console/IConsole.h>
#include <AzCore/Threading/ThreadUtils.h>
AZ_CVAR(float, cl_jobThreadsConcurrencyRatio, 0.6f, nullptr, AZ::ConsoleFunctorFlags::Null, "Legacy Job system multiplier on the number of hw threads the machine creates at initialization");
AZ_CVAR(uint32_t, cl_jobThreadsNumReserved, 2, nullptr, AZ::ConsoleFunctorFlags::Null, "Legacy Job system number of hardware threads that are reserved for O3DE system threads");
AZ_CVAR(uint32_t, cl_jobThreadsMinNumber, 2, nullptr, AZ::ConsoleFunctorFlags::Null, "Legacy Job system minimum number of worker threads to create after scaling the number of hw threads");
namespace AZ
{
//=========================================================================
@@ -46,9 +54,10 @@ namespace AZ
JobManagerThreadDesc threadDesc;
int numberOfWorkerThreads = m_numberOfWorkerThreads;
if (numberOfWorkerThreads <= 0)
if (numberOfWorkerThreads <= 0) // spawn default number of threads
{
numberOfWorkerThreads = AZ::GetMin(static_cast<unsigned int>(desc.m_workerThreads.capacity()), AZStd::thread::hardware_concurrency());
uint32_t scaledHardwareThreads = Threading::CalcNumWorkerThreads(cl_jobThreadsConcurrencyRatio, cl_jobThreadsMinNumber, cl_jobThreadsNumReserved);
numberOfWorkerThreads = AZ::GetMin(static_cast<unsigned int>(desc.m_workerThreads.capacity()), scaledHardwareThreads);
#if (AZ_TRAIT_MAX_JOB_MANAGER_WORKER_THREADS)
numberOfWorkerThreads = AZ::GetMin(numberOfWorkerThreads, AZ_TRAIT_MAX_JOB_MANAGER_WORKER_THREADS);
#endif // (AZ_TRAIT_MAX_JOB_MANAGER_WORKER_THREADS)
@@ -36,7 +36,7 @@ namespace AZ
*/
int m_stackSize;
JobManagerThreadDesc(int cpuId = -1, int priority = -100000, int stackSize = -1)
JobManagerThreadDesc(int cpuId = -1, int priority = 0, int stackSize = -1)
: m_cpuId(cpuId)
, m_priority(priority)
, m_stackSize(stackSize)
@@ -15,7 +15,7 @@ using namespace Intersect;
// IntersectSegmentTriangleCCW
// [10/21/2009]
//=========================================================================
int Intersect::IntersectSegmentTriangleCCW(
bool Intersect::IntersectSegmentTriangleCCW(
const Vector3& p, const Vector3& q, const Vector3& a, const Vector3& b, const Vector3& c,
/*float &u, float &v, float &w,*/ Vector3& normal, float& t)
{
@@ -34,7 +34,7 @@ int Intersect::IntersectSegmentTriangleCCW(
float d = qp.Dot(normal);
if (d <= 0.0f)
{
return 0;
return false;
}
// Compute intersection t value of pq with plane of triangle. A ray
@@ -46,7 +46,7 @@ int Intersect::IntersectSegmentTriangleCCW(
// range segment check t[0,1] (it this case [0,d])
if (t < 0.0f || t > d)
{
return 0;
return false;
}
// Compute barycentric coordinate components and test if within bounds
@@ -54,12 +54,12 @@ int Intersect::IntersectSegmentTriangleCCW(
v = ac.Dot(e);
if (v < 0.0f || v > d)
{
return 0;
return false;
}
w = -ab.Dot(e);
if (w < 0.0f || v + w > d)
{
return 0;
return false;
}
// Segment/ray intersects triangle. Perform delayed division and
@@ -72,14 +72,14 @@ int Intersect::IntersectSegmentTriangleCCW(
normal.Normalize();
return 1;
return true;
}
//=========================================================================
// IntersectSegmentTriangle
// [10/21/2009]
//=========================================================================
int
bool
Intersect::IntersectSegmentTriangle(
const Vector3& p, const Vector3& q, const Vector3& a, const Vector3& b, const Vector3& c,
/*float &u, float &v, float &w,*/ Vector3& normal, float& t)
@@ -111,7 +111,7 @@ Intersect::IntersectSegmentTriangle(
// so either have a parallel ray or our normal is flipped
if (d >= -Constants::FloatEpsilon)
{
return 0; // parallel
return false; // parallel
}
d = -d;
e = ap.Cross(qp);
@@ -125,19 +125,19 @@ Intersect::IntersectSegmentTriangle(
// range segment check t[0,1] (it this case [0,d])
if (t < 0.0f || t > d)
{
return 0;
return false;
}
// Compute barycentric coordinate components and test if within bounds
v = ac.Dot(e);
if (v < 0.0f || v > d)
{
return 0;
return false;
}
w = -ab.Dot(e);
if (w < 0.0f || v + w > d)
{
return 0;
return false;
}
// Segment/ray intersects the triangle. Perform delayed division and
@@ -150,14 +150,14 @@ Intersect::IntersectSegmentTriangle(
normal.Normalize();
return 1;
return true;
}
//=========================================================================
// TestSegmentAABBOrigin
// [10/21/2009]
//=========================================================================
int
bool
AZ::Intersect::TestSegmentAABBOrigin(const Vector3& midPoint, const Vector3& halfVector, const Vector3& aabbExtends)
{
const Vector3 EPSILON(0.001f); // \todo this is slow load move to a const
@@ -168,7 +168,7 @@ AZ::Intersect::TestSegmentAABBOrigin(const Vector3& midPoint, const Vector3& hal
// Try world coordinate axes as separating axes
if (!absMidpoint.IsLessEqualThan(absHalfMidpoint))
{
return 0;
return false;
}
// Add in an epsilon term to counteract arithmetic errors when segment is
@@ -188,11 +188,11 @@ AZ::Intersect::TestSegmentAABBOrigin(const Vector3& midPoint, const Vector3& hal
Vector3 ead(ey * adz + ez * ady, ex * adz + ez * adx, ex * ady + ey * adx);
if (!absMDCross.IsLessEqualThan(ead))
{
return 0;
return false;
}
// No separating axis found; segment must be overlapping AABB
return 1;
return true;
}
@@ -200,7 +200,7 @@ AZ::Intersect::TestSegmentAABBOrigin(const Vector3& midPoint, const Vector3& hal
// IntersectRayAABB
// [10/21/2009]
//=========================================================================
int
RayAABBIsectTypes
AZ::Intersect::IntersectRayAABB(
const Vector3& rayStart, const Vector3& dir, const Vector3& dirRCP, const Aabb& aabb,
float& tStart, float& tEnd, Vector3& startNormal /*, Vector3& inter*/)
@@ -356,7 +356,7 @@ AZ::Intersect::IntersectRayAABB(
// IntersectRayAABB2
// [2/18/2011]
//=========================================================================
int
RayAABBIsectTypes
AZ::Intersect::IntersectRayAABB2(const Vector3& rayStart, const Vector3& dirRCP, const Aabb& aabb, float& start, float& end)
{
float tmin, tmax, tymin, tymax, tzmin, tzmax;
@@ -408,7 +408,7 @@ AZ::Intersect::IntersectRayAABB2(const Vector3& rayStart, const Vector3& dirRCP,
return ISECT_RAY_AABB_ISECT;
}
int AZ::Intersect::IntersectRayDisk(
bool AZ::Intersect::IntersectRayDisk(
const Vector3& rayOrigin, const Vector3& rayDir, const Vector3& diskCenter, const float diskRadius, const Vector3& diskNormal, float& t)
{
// First intersect with the plane of the disk
@@ -421,10 +421,10 @@ int AZ::Intersect::IntersectRayDisk(
if (pointOnPlane.GetDistance(diskCenter) < diskRadius)
{
t = planeIntersectionDistance;
return 1;
return true;
}
}
return 0;
return false;
}
// Reference: Real-Time Collision Detection - 5.3.7 Intersecting Ray or Segment Against Cylinder, and the book's errata.
@@ -1012,7 +1012,7 @@ int AZ::Intersect::IntersectRayQuad(
}
// reference: Real-Time Collision Detection, 5.3.3 Intersecting Ray or Segment Against Box
int AZ::Intersect::IntersectRayBox(
bool AZ::Intersect::IntersectRayBox(
const Vector3& rayOrigin, const Vector3& rayDir, const Vector3& boxCenter, const Vector3& boxAxis1,
const Vector3& boxAxis2, const Vector3& boxAxis3, float boxHalfExtent1, float boxHalfExtent2, float boxHalfExtent3, float& t)
{
@@ -1044,7 +1044,7 @@ int AZ::Intersect::IntersectRayBox(
// If the ray is parallel to the slab and the ray origin is outside, return no intersection.
if (tp < 0.0f || tn < 0.0f)
{
return 0;
return false;
}
}
else
@@ -1065,7 +1065,7 @@ int AZ::Intersect::IntersectRayBox(
tmax = AZ::GetMin(tmax, t2);
if (tmin > tmax)
{
return 0;
return false;
}
}
@@ -1085,7 +1085,7 @@ int AZ::Intersect::IntersectRayBox(
// If the ray is parallel to the slab and the ray origin is outside, return no intersection.
if (tp < 0.0f || tn < 0.0f)
{
return 0;
return false;
}
}
else
@@ -1106,7 +1106,7 @@ int AZ::Intersect::IntersectRayBox(
tmax = AZ::GetMin(tmax, t2);
if (tmin > tmax)
{
return 0;
return false;
}
}
@@ -1126,7 +1126,7 @@ int AZ::Intersect::IntersectRayBox(
// If the ray is parallel to the slab and the ray origin is outside, return no intersection.
if (tp < 0.0f || tn < 0.0f)
{
return 0;
return false;
}
}
else
@@ -1147,15 +1147,15 @@ int AZ::Intersect::IntersectRayBox(
tmax = AZ::GetMin(tmax, t2);
if (tmin > tmax)
{
return 0;
return false;
}
}
t = (isRayOriginInsideBox ? tmax : tmin);
return 1;
return true;
}
int AZ::Intersect::IntersectRayObb(const Vector3& rayOrigin, const Vector3& rayDir, const Obb& obb, float& t)
bool AZ::Intersect::IntersectRayObb(const Vector3& rayOrigin, const Vector3& rayDir, const Obb& obb, float& t)
{
return AZ::Intersect::IntersectRayBox(rayOrigin, rayDir, obb.GetPosition(),
obb.GetAxisX(), obb.GetAxisY(), obb.GetAxisZ(),
@@ -1166,7 +1166,7 @@ int AZ::Intersect::IntersectRayObb(const Vector3& rayOrigin, const Vector3& rayD
// IntersectSegmentCylinder
// [10/21/2009]
//=========================================================================
int
CylinderIsectTypes
AZ::Intersect::IntersectSegmentCylinder(
const Vector3& sa, const Vector3& dir, const Vector3& p, const Vector3& q, const float r, float& t)
{
@@ -1225,7 +1225,7 @@ AZ::Intersect::IntersectSegmentCylinder(
return RR_ISECT_RAY_CYL_NONE; // No real roots; no intersection
}
t = (-b - Sqrt(discr)) / a;
int result = RR_ISECT_RAY_CYL_PQ; // default along the PQ segment
CylinderIsectTypes result = RR_ISECT_RAY_CYL_PQ; // default along the PQ segment
if (md + t * nd < 0.0f)
{
@@ -1294,7 +1294,7 @@ AZ::Intersect::IntersectSegmentCylinder(
// IntersectSegmentCapsule
// [10/21/2009]
//=========================================================================
int
CapsuleIsectTypes
AZ::Intersect::IntersectSegmentCapsule(const Vector3& sa, const Vector3& dir, const Vector3& p, const Vector3& q, const float r, float& t)
{
int result = IntersectSegmentCylinder(sa, dir, p, q, r, t);
@@ -1361,13 +1361,13 @@ AZ::Intersect::IntersectSegmentCapsule(const Vector3& sa, const Vector3& dir, co
// IntersectSegmentPolyhedron
// [10/21/2009]
//=========================================================================
int
bool
AZ::Intersect::IntersectSegmentPolyhedron(
const Vector3& sa, const Vector3& sBA, const Plane p[], int numPlanes,
const Vector3& sa, const Vector3& dir, const Plane p[], int numPlanes,
float& tfirst, float& tlast, int& iFirstPlane, int& iLastPlane)
{
// Compute direction vector for the segment
Vector3 d = /*b - a*/ sBA;
Vector3 d = /*b - a*/ dir;
// Set initial interval to being the whole segment. For a ray, tlast should be
// set to +RR_FLT_MAX. For a line, additionally tfirst should be set to -RR_FLT_MAX
tfirst = 0.0f;
@@ -1388,7 +1388,7 @@ AZ::Intersect::IntersectSegmentPolyhedron(
// If so, return "no intersection" if segment lies outside plane
if (dist < 0.0f)
{
return 0;
return false;
}
}
else
@@ -1417,7 +1417,7 @@ AZ::Intersect::IntersectSegmentPolyhedron(
// Exit with "no intersection" if intersection becomes empty
if (tfirst > tlast)
{
return 0;
return false;
}
}
}
@@ -1425,11 +1425,11 @@ AZ::Intersect::IntersectSegmentPolyhedron(
//DBG_Assert(iFirstPlane!=-1&&iLastPlane!=-1,("We have some bad border case to have only one plane, fix this function!"));
if (iFirstPlane == -1 && iLastPlane == -1)
{
return 0;
return false;
}
// A nonzero logical intersection, so the segment intersects the polyhedron
return 1;
return true;
}
//=========================================================================
@@ -1442,7 +1442,7 @@ AZ::Intersect::ClosestSegmentSegment(
const Vector3& segment2Start, const Vector3& segment2End,
float& segment1Proportion, float& segment2Proportion,
Vector3& closestPointSegment1, Vector3& closestPointSegment2,
float epsilon /*= 1e-4f*/ )
float epsilon)
{
const Vector3 segment1 = segment1End - segment1Start;
const Vector3 segment2 = segment2End - segment2Start;
@@ -5,363 +5,398 @@
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef AZCORE_MATH_SEGMENT_INTERSECTION_H
#define AZCORE_MATH_SEGMENT_INTERSECTION_H
#pragma once
#include <AzCore/Math/Vector3.h>
#include <AzCore/Math/Aabb.h>
#include <AzCore/Math/Obb.h>
#include <AzCore/Math/Plane.h>
/// \file isect_segment.h
#include <AzCore/Math/Vector3.h>
namespace AZ
{
namespace Intersect
{
//! LineToPointDistanceTime computes the time of the shortest distance from point 'p' to segment (s1,s2).
//! To calculate the point of intersection:
//! P = s1 + u (s2 - s1)
//! @param s1 segment start point
//! @param s2 segment end point
//! @param p point to find the closest time to.
//! @return time (on the segment) for the shortest distance from 'p' to (s1,s2) [0.0f (s1),1.0f (s2)]
inline float LineToPointDistanceTime(const Vector3& s1, const Vector3& s21, const Vector3& p)
{
// so u = (p.x - s1.x)*(s2.x - s1.x) + (p.y - s1.y)*(s2.y - s1.y) + (p.z-s1.z)*(s2.z-s1.z) / |s2-s1|^2
return s21.Dot(p - s1) / s21.Dot(s21);
}
//! To calculate the point of intersection: P = s1 + u (s2 - s1)
//! @param s1 Segment start point.
//! @param s2 Segment end point.
//! @param p Point to find the closest time to.
//! @return Time (on the segment) for the shortest distance from 'p' to (s1,s2) [0.0f (s1),1.0f (s2)]
float LineToPointDistanceTime(const Vector3& s1, const Vector3& s21, const Vector3& p);
//! LineToPointDistance computes the closest point to 'p' from a segment (s1,s2).
//! @param s1 segment start point
//! @param s2 segment end point
//! @param p point to find the closest time to.
//! @param u time (on the segment) for the shortest distance from 'p' to (s1,s2) [0.0f (s1),1.0f (s2)]
//! @return the closest point
inline Vector3 LineToPointDistance(const Vector3& s1, const Vector3& s2, const Vector3& p, float& u)
{
const Vector3 s21 = s2 - s1;
// we assume seg1 and seg2 are NOT coincident
AZ_MATH_ASSERT(!s21.IsClose(Vector3(0.0f), 1e-4f), "OK we agreed that we will pass valid segments! (s1 != s2)");
u = LineToPointDistanceTime(s1, s21, p);
return s1 + u * s21;
}
//! @param s1 Segment start point
//! @param s2 Segment end point
//! @param p Point to find the closest time to.
//! @param u Time (on the segment) for the shortest distance from 'p' to (s1,s2) [0.0f (s1),1.0f (s2)]
//! @return The closest point
Vector3 LineToPointDistance(const Vector3& s1, const Vector3& s2, const Vector3& p, float& u);
//! Given segment pq and triangle abc (CCW), returns whether segment intersects
//! triangle and if so, also returns the barycentric coordinates (u,v,w)
//! of the intersection point.
//! @param p segment start point
//! @param q segment end point
//! @param a triangle point 1
//! @param b triangle point 2
//! @param c triangle point 3
//! @param normal at the intersection point.
//! @param t time of intersection along the segment [0.0 (p), 1.0 (q)]
//! @return 1 if the segment intersects the triangle otherwise 0
int IntersectSegmentTriangleCCW(
const Vector3& p, const Vector3& q, const Vector3& a, const Vector3& b, const Vector3& c,
/*float &u, float &v, float &w,*/ Vector3& normal, float& t);
//! @param p Segment start point.
//! @param q Segment end point.
//! @param a Triangle point 1.
//! @param b Triangle point 2.
//! @param c Triangle point 3.
//! @param normal At the intersection point.
//! @param t Time of intersection along the segment [0.0 (p), 1.0 (q)].
//! @return true if the segments intersects the triangle otherwise false.
bool IntersectSegmentTriangleCCW(
const Vector3& p, const Vector3& q, const Vector3& a, const Vector3& b, const Vector3& c, Vector3& normal, float& t);
//! Same as \ref IntersectSegmentTriangleCCW without respecting the triangle (a,b,c) vertex order (double sided).
int IntersectSegmentTriangle(
const Vector3& p, const Vector3& q, const Vector3& a, const Vector3& b, const Vector3& c,
/*float &u, float &v, float &w,*/ Vector3& normal, float& t);
//! @param p Segment start point.
//! @param q Segment end point.
//! @param a Triangle point 1.
//! @param b Triangle point 2.
//! @param c Triangle point 3.
//! @param normal At the intersection point.
//! @param t Time of intersection along the segment [0.0 (p), 1.0 (q)].
//! @return True if the segments intersects the triangle otherwise false.
bool IntersectSegmentTriangle(
const Vector3& p, const Vector3& q, const Vector3& a, const Vector3& b, const Vector3& c, Vector3& normal, float& t);
//! Ray aabb intersection result types.
enum RayAABBIsectTypes
enum RayAABBIsectTypes : AZ::s32
{
ISECT_RAY_AABB_NONE = 0, ///< no intersection
ISECT_RAY_AABB_SA_INSIDE, ///< the ray starts inside the aabb
ISECT_RAY_AABB_ISECT, ///< intersects along the PQ segment
ISECT_RAY_AABB_NONE = 0, ///< no intersection
ISECT_RAY_AABB_SA_INSIDE, ///< the ray starts inside the aabb
ISECT_RAY_AABB_ISECT, ///< intersects along the PQ segment
};
//! Intersect ray R(t) = rayStart + t*d against AABB a. When intersecting,
//! return intersection distance tmin and point q of intersection.
//! @param rayStart ray starting point
//! @param dir ray direction and length (dir = rayEnd - rayStart)
//! @param dirRCP 1/dir (reciprocal direction - we cache this result very often so we don't need to compute it multiple times, otherwise just use dir.GetReciprocal())
//! @param rayStart Ray starting point
//! @param dir Ray direction and length (dir = rayEnd - rayStart)
//! @param dirRCP 1/dir (reciprocal direction - we cache this result very often so we don't need to compute it multiple times,
//! otherwise just use dir.GetReciprocal())
//! @param aabb Axis aligned bounding box to intersect against
//! @param tStart time on ray of the first intersection [0,1] or 0 if the ray starts inside the aabb - check the return value
//! @param tEnd time of the of the second intersection [0,1] (it can be > 1 if intersects after the rayEnd)
//! @param startNormal normal at the start point.
//! @param tStart Time on ray of the first intersection [0,1] or 0 if the ray starts inside the aabb - check the return value
//! @param tEnd Time of the of the second intersection [0,1] (it can be > 1 if intersects after the rayEnd)
//! @param startNormal Normal at the start point.
//! @return \ref RayAABBIsectTypes
int IntersectRayAABB(
const Vector3& rayStart, const Vector3& dir, const Vector3& dirRCP, const Aabb& aabb,
float& tStart, float& tEnd, Vector3& startNormal /*, Vector3& inter*/);
RayAABBIsectTypes IntersectRayAABB(
const Vector3& rayStart,
const Vector3& dir,
const Vector3& dirRCP,
const Aabb& aabb,
float& tStart,
float& tEnd,
Vector3& startNormal);
//! Intersect ray against AABB.
//! @param rayStart ray starting point.
//! @param dir ray reciprocal direction.
//! @param rayStart Ray starting point.
//! @param dir Ray reciprocal direction.
//! @param aabb Axis aligned bounding box to intersect against.
//! @param start length on ray of the first intersection.
//! @param end length of the of the second intersection.
//! @return \ref RayAABBIsectTypes In this faster version than IntersectRayAABB we return only ISECT_RAY_AABB_NONE and ISECT_RAY_AABB_ISECT.
//! You can check yourself for that case.
int IntersectRayAABB2(
const Vector3& rayStart, const Vector3& dirRCP, const Aabb& aabb,
float& start, float& end);
//! @param start Length on ray of the first intersection.
//! @param end Length of the of the second intersection.
//! @return \ref RayAABBIsectTypes In this faster version than IntersectRayAABB we return only ISECT_RAY_AABB_NONE and
//! ISECT_RAY_AABB_ISECT. You can check yourself for that case.
RayAABBIsectTypes IntersectRayAABB2(const Vector3& rayStart, const Vector3& dirRCP, const Aabb& aabb, float& start, float& end);
//! Clip a ray to an aabb. return true if ray was clipped. The ray
//! can be inside so don't use the result if the ray intersect the box.
inline int ClipRayWithAabb(
const Aabb& aabb, Vector3& rayStart, Vector3& rayEnd, float& tClipStart, float& tClipEnd)
{
Vector3 startNormal;
float tStart, tEnd;
Vector3 dirLen = rayEnd - rayStart;
if (IntersectRayAABB(rayStart, dirLen, dirLen.GetReciprocal(), aabb, tStart, tEnd, startNormal) != ISECT_RAY_AABB_NONE)
{
// clip the ray with the box
if (tStart > 0.0f)
{
rayStart = rayStart + tStart * dirLen;
tClipStart = tStart;
}
if (tEnd < 1.0f)
{
rayEnd = rayStart + tEnd * dirLen;
tClipEnd = tEnd;
}
return 1;
}
return 0;
}
//! @param aabb Bounds to test against.
//! @param rayStart The start of the ray.
//! @param rayEnd The end of the ray.
//! @param[out] tClipStart The proportion where the ray enters the \ref Aabb.
//! @param[out] tClipEnd The proportion where the ray exits the \ref Aabb.
//! @return True if the ray was clipped, otherwise false.
bool ClipRayWithAabb(const Aabb& aabb, Vector3& rayStart, Vector3& rayEnd, float& tClipStart, float& tClipEnd);
//! Test segment and aabb where the segment is defined by midpoint
//! midPoint = (p1-p0) * 0.5f and half vector halfVector = p1 - midPoint.
//! the aabb is at the origin and defined by half extents only.
//! @return 1 if the intersect, otherwise 0.
int TestSegmentAABBOrigin(const Vector3& midPoint, const Vector3& halfVector, const Vector3& aabbExtends);
//! @param midPoint Midpoint of a line segment.
//! @param halfVector Half vector of an aabb.
//! @param aabbExtends The extends of a bounded box.
//! @return True if the segment and AABB intersect, otherwise false
bool TestSegmentAABBOrigin(const Vector3& midPoint, const Vector3& halfVector, const Vector3& aabbExtends);
//! Test if segment specified by points p0 and p1 intersects AABB. \ref TestSegmentAABBOrigin
//! @return 1 if the segment and AABB intersect, otherwise 0.
inline int TestSegmentAABB(const Vector3& p0, const Vector3& p1, const Aabb& aabb)
{
Vector3 e = aabb.GetExtents();
Vector3 d = p1 - p0;
Vector3 m = p0 + p1 - aabb.GetMin() - aabb.GetMax();
return TestSegmentAABBOrigin(m, d, e);
}
//! Test if segment specified by points p0 and p1 intersects AABB. \ref TestSegmentAABBOrigin.
//! @param p0 Segment start point.
//! @param p1 Segment end point.
//! @param aabb Bounded box to test against.
//! @return True if the segment and AABB intersect, otherwise false.
bool TestSegmentAABB(const Vector3& p0, const Vector3& p1, const Aabb& aabb);
//! Ray sphere intersection result types.
enum SphereIsectTypes
enum SphereIsectTypes : AZ::s32
{
ISECT_RAY_SPHERE_SA_INSIDE = -1, // the ray starts inside the cylinder
ISECT_RAY_SPHERE_NONE, // no intersection
ISECT_RAY_SPHERE_ISECT, // along the PQ segment
ISECT_RAY_SPHERE_SA_INSIDE = -1, //!< The ray starts inside the cylinder
ISECT_RAY_SPHERE_NONE, //!< No intersection
ISECT_RAY_SPHERE_ISECT, //!< Along the PQ segment
};
//! IntersectRaySphereOrigin
//! return time t>=0 but not limited, so if you check a segment make sure
//! t <= segmentLen
//! @param rayStart ray start point
//! t <= segmentLen.
//! @param rayStart ray start point.
//! @param rayDirNormalized ray direction normalized.
//! @param shereRadius sphere radius
//! @param shereRadius Radius of sphere at origin.
//! @param time of closest intersection [0,+INF] in relation to the normalized direction.
//! @return \ref SphereIsectTypes
AZ_INLINE int IntersectRaySphereOrigin(
const Vector3& rayStart, const Vector3& rayDirNormalized,
const float sphereRadius, float& t)
{
Vector3 m = rayStart;
float b = m.Dot(rayDirNormalized);
float c = m.Dot(m) - sphereRadius * sphereRadius;
// Exit if r's origin outside s (c > 0)and r pointing away from s (b > 0)
if (c > 0.0f && b > 0.0f)
{
return ISECT_RAY_SPHERE_NONE;
}
float discr = b * b - c;
// A negative discriminant corresponds to ray missing sphere
if (discr < 0.0f)
{
return ISECT_RAY_SPHERE_NONE;
}
// Ray now found to intersect sphere, compute smallest t value of intersection
t = -b - Sqrt(discr);
// If t is negative, ray started inside sphere so clamp t to zero
if (t < 0.0f)
{
// t = 0.0f;
return ISECT_RAY_SPHERE_SA_INSIDE; // no hit if inside
}
//q = p + t * d;
return ISECT_RAY_SPHERE_ISECT;
}
//! @return \ref SphereIsectTypes.
SphereIsectTypes IntersectRaySphereOrigin(
const Vector3& rayStart, const Vector3& rayDirNormalized, const float sphereRadius, float& t);
//! Intersect ray (rayStart,rayDirNormalized) and sphere (sphereCenter,sphereRadius) \ref IntersectRaySphereOrigin
inline int IntersectRaySphere(
const Vector3& rayStart, const Vector3& rayDirNormalized, const Vector3& sphereCenter, const float sphereRadius, float& t)
{
return IntersectRaySphereOrigin(rayStart - sphereCenter, rayDirNormalized, sphereRadius, t);
}
//! @param rayStart The start of the ray.
//! @param rayDirNormalized The direction of the ray normalized.
//! @param sphereCenter The center of the sphere.
//! @param sphereRadius Radius of the sphere.
//! @param[out] t Coefficient in the ray's explicit equation from which an
//! intersecting point is calculated as "rayOrigin + t1 * rayDir".
//! @return SphereIsectTypes
SphereIsectTypes IntersectRaySphere(
const Vector3& rayStart, const Vector3& rayDirNormalized, const Vector3& sphereCenter, const float sphereRadius, float& t);
//! @param rayOrigin The origin of the ray to test.
//! @param rayDir The direction of the ray to test. It has to be unit length.
//! @param diskCenter Center point of the disk
//! @param diskRadius Radius of the disk
//! @param diskNormal A normal perpendicular to the disk
//! @param[out] t If returning 1 (indicating a hit), this contains distance from rayOrigin along the normalized rayDir that the hit occured at.
//! @return The number of intersecting points.
int IntersectRayDisk(
const Vector3& rayOrigin, const Vector3& rayDir, const Vector3& diskCenter, const float diskRadius, const AZ::Vector3& diskNormal, float& t);
//! Intersect ray (rayStarty, rayDirNormalized) and disk (center, radius, normal)
//! @param rayOrigin The origin of the ray to test.
//! @param rayDir The direction of the ray to test. It has to be unit length.
//! @param diskCenter Center point of the disk.
//! @param diskRadius Radius of the disk.
//! @param diskNormal A normal perpendicular to the disk.
//! @param[out] t If returning 1 (indicating a hit), this contains distance from rayOrigin along the normalized rayDir
//! that the hit occured at.
//! @return False if not interesecting and true if intersecting
bool IntersectRayDisk(
const Vector3& rayOrigin,
const Vector3& rayDir,
const Vector3& diskCenter,
const float diskRadius,
const AZ::Vector3& diskNormal,
float& t);
//! If there is only one intersecting point, the coefficient is stored in \ref t1.
//! @param rayOrigin The origin of the ray to test.
//! @param rayDir The direction of the ray to test. It has to be unit length.
//! @param cylinderEnd1 The center of the circle on one end of the cylinder.
//! @param cylinderDir The direction pointing from \ref cylinderEnd1 to the other end of the cylinder. It has to be unit length.
//! @param cylinderHeight The distance between two centers of the circles on two ends of the cylinder respectively.
//! @param[out] t1 A possible coefficient in the ray's explicit equation from which an intersecting point is calculated as "rayOrigin + t1 * rayDir".
//! @param[out] t2 A possible coefficient in the ray's explicit equation from which an intersecting point is calculated as "rayOrigin + t2 * rayDir".
//! @return The number of intersecting points.
//! @param rayOrigin The origin of the ray to test.
//! @param rayDir The direction of the ray to test. It has to be unit length.
//! @param cylinderEnd1 The center of the circle on one end of the cylinder.
//! @param cylinderDir The direction pointing from \ref cylinderEnd1 to the other end of the cylinder. It has to be unit length.
//! @param cylinderHeight The distance between two centers of the circles on two ends of the cylinder respectively.
//! @param[out] t1 A possible coefficient in the ray's explicit equation from which an intersecting point is calculated as "rayOrigin + t1 * rayDir".
//! @param[out] t2 A possible coefficient in the ray's explicit equation from which an intersecting point is calculated as "rayOrigin + t2 * rayDir".
//! @return The number of intersecting points.
int IntersectRayCappedCylinder(
const Vector3& rayOrigin, const Vector3& rayDir,
const Vector3& cylinderEnd1, const Vector3& cylinderDir, float cylinderHeight, float cylinderRadius,
float& t1, float& t2);
const Vector3& rayOrigin,
const Vector3& rayDir,
const Vector3& cylinderEnd1,
const Vector3& cylinderDir,
float cylinderHeight,
float cylinderRadius,
float& t1,
float& t2);
//! If there is only one intersecting point, the coefficient is stored in \ref t1.
//! @param rayOrigin The origin of the ray to test.
//! @param rayDir The direction of the ray to test. It has to be unit length.
//! @param coneApex The apex of the cone.
//! @param coneDir The unit-length direction from the apex to the base.
//! @param coneHeight The height of the cone, from the apex to the base.
//! @param coneBaseRadius The radius of the cone base circle.
//! @param[out] t1 A possible coefficient in the ray's explicit equation from which an intersecting point is calculated as "rayOrigin + t1 * rayDir".
//! @param[out] t2 A possible coefficient in the ray's explicit equation from which an intersecting point is calculated as "rayOrigin + t2 * rayDir".
//! @return The number of intersecting points.
//! @param rayOrigin The origin of the ray to test.
//! @param rayDir The direction of the ray to test. It has to be unit length.
//! @param coneApex The apex of the cone.
//! @param coneDir The unit-length direction from the apex to the base.
//! @param coneHeight The height of the cone, from the apex to the base.
//! @param coneBaseRadius The radius of the cone base circle.
//! @param[out] t1 A possible coefficient in the ray's explicit equation from which an intersecting point is calculated as "rayOrigin + t1 * rayDir".
//! @param[out] t2 A possible coefficient in the ray's explicit equation from which an intersecting point is calculated as "rayOrigin + t2 * rayDir".
//! @return The number of intersecting points.
int IntersectRayCone(
const Vector3& rayOrigin, const Vector3& rayDir,
const Vector3& coneApex, const Vector3& coneDir, float coneHeight, float coneBaseRadius,
float& t1, float& t2);
const Vector3& rayOrigin,
const Vector3& rayDir,
const Vector3& coneApex,
const Vector3& coneDir,
float coneHeight,
float coneBaseRadius,
float& t1,
float& t2);
//! Test intersection between a ray and a plane in 3D.
//! @param rayOrigin The origin of the ray to test intersection with.
//! @param rayDir The direction of the ray to test intersection with.
//! @param planePos A point on the plane to test intersection with.
//! @param planeNormal The normal of the plane to test intersection with.
//! @param t[out] The coefficient in the ray's explicit equation from which the intersecting point is calculated as "rayOrigin + t * rayDirection".
//! @return The number of intersection point.
//! @param rayOrigin The origin of the ray to test intersection with.
//! @param rayDir The direction of the ray to test intersection with.
//! @param planePos A point on the plane to test intersection with.
//! @param planeNormal The normal of the plane to test intersection with.
//! @param[out] t The coefficient in the ray's explicit equation from which the intersecting point is calculated as "rayOrigin + t * rayDirection".
//! @return The number of intersection point.
int IntersectRayPlane(
const Vector3& rayOrigin, const Vector3& rayDir, const Vector3& planePos,
const Vector3& planeNormal, float& t);
const Vector3& rayOrigin, const Vector3& rayDir, const Vector3& planePos, const Vector3& planeNormal, float& t);
//! Test intersection between a ray and a two-sided quadrilateral defined by four points in 3D.
//! The four points that define the quadrilateral could be passed in with either counter clock-wise
//! The four points that define the quadrilateral could be passed in with either counter clock-wise
//! winding or clock-wise winding.
//! @param rayOrigin The origin of the ray to test intersection with.
//! @param rayDir The direction of the ray to test intersection with.
//! @param vertexA One of the four points that define the quadrilateral.
//! @param vertexB One of the four points that define the quadrilateral.
//! @param vertexC One of the four points that define the quadrilateral.
//! @param vertexD One of the four points that define the quadrilateral.
//! @param t[out] The coefficient in the ray's explicit equation from which the intersecting point is calculated as "rayOrigin + t * rayDirection".
//! @return The number of intersection point.
//! @param rayOrigin The origin of the ray to test intersection with.
//! @param rayDir The direction of the ray to test intersection with.
//! @param vertexA One of the four points that define the quadrilateral.
//! @param vertexB One of the four points that define the quadrilateral.
//! @param vertexC One of the four points that define the quadrilateral.
//! @param vertexD One of the four points that define the quadrilateral.
//! @param[out] t The coefficient in the ray's explicit equation from which the
//! intersecting point is calculated as "rayOrigin + t * rayDirection".
//! @return The number of intersection point.
int IntersectRayQuad(
const Vector3& rayOrigin, const Vector3& rayDir, const Vector3& vertexA,
const Vector3& vertexB, const Vector3& vertexC, const Vector3& vertexD, float& t);
const Vector3& rayOrigin,
const Vector3& rayDir,
const Vector3& vertexA,
const Vector3& vertexB,
const Vector3& vertexC,
const Vector3& vertexD,
float& t);
//! Test intersection between a ray and an oriented box in 3D.
//! @param rayOrigin The origin of the ray to test intersection with.
//! @param rayDir The direction of the ray to test intersection with.
//! @param boxCenter The position of the center of the box.
//! @param boxAxis1 An axis along one dimension of the oriented box.
//! @param boxAxis2 An axis along one dimension of the oriented box.
//! @param boxAxis3 An axis along one dimension of the oriented box.
//! @param boxHalfExtent1 The half extent of the box on the dimension of \ref boxAxis1.
//! @param boxHalfExtent2 The half extent of the box on the dimension of \ref boxAxis2.
//! @param boxHalfExtent3 The half extent of the box on the dimension of \ref boxAxis3.
//! @param t[out] The coefficient in the ray's explicit equation from which the intersecting point is calculated as "rayOrigin + t * rayDirection".
//! @return 1 if there is an intersection, 0 otherwise.
int IntersectRayBox(
const Vector3& rayOrigin, const Vector3& rayDir, const Vector3& boxCenter, const Vector3& boxAxis1,
const Vector3& boxAxis2, const Vector3& boxAxis3, float boxHalfExtent1, float boxHalfExtent2, float boxHalfExtent3,
//! Test intersection between a ray and an oriented box in 3D.
//! @param rayOrigin The origin of the ray to test intersection with.
//! @param rayDir The direction of the ray to test intersection with.
//! @param boxCenter The position of the center of the box.
//! @param boxAxis1 An axis along one dimension of the oriented box.
//! @param boxAxis2 An axis along one dimension of the oriented box.
//! @param boxAxis3 An axis along one dimension of the oriented box.
//! @param boxHalfExtent1 The half extent of the box on the dimension of \ref boxAxis1.
//! @param boxHalfExtent2 The half extent of the box on the dimension of \ref boxAxis2.
//! @param boxHalfExtent3 The half extent of the box on the dimension of \ref boxAxis3.
//! @param[out] t The coefficient in the ray's explicit equation from which the intersecting point is calculated as "rayOrigin + t * rayDirection".
//! @return true if there is an intersection, false otherwise.
bool IntersectRayBox(
const Vector3& rayOrigin,
const Vector3& rayDir,
const Vector3& boxCenter,
const Vector3& boxAxis1,
const Vector3& boxAxis2,
const Vector3& boxAxis3,
float boxHalfExtent1,
float boxHalfExtent2,
float boxHalfExtent3,
float& t);
//! Test intersection between a ray and an OBB.
//! @param rayOrigin The origin of the ray to test intersection with.
//! @param rayDir The direction of the ray to test intersection with.
//! @param obb The OBB to test for intersection with the ray.
//! @param t[out] The coefficient in the ray's explicit equation from which the intersecting point is calculated as "rayOrigin + t * rayDirection".
//! @return 1 if there is an intersection, 0 otherwise.
int IntersectRayObb(const Vector3& rayOrigin, const Vector3& rayDir, const Obb& obb, float& t);
//! @param[out] t The coefficient in the ray's explicit equation from which the intersecting point is calculated as "rayOrigin + t * rayDirection".
//! @return True if there is an intersection, false otherwise.
bool IntersectRayObb(const Vector3& rayOrigin, const Vector3& rayDir, const Obb& obb, float& t);
//! Ray cylinder intersection types.
enum CylinderIsectTypes
enum CylinderIsectTypes : AZ::s32
{
RR_ISECT_RAY_CYL_SA_INSIDE = -1, // the ray starts inside the cylinder
RR_ISECT_RAY_CYL_NONE, // no intersection
RR_ISECT_RAY_CYL_PQ, // along the PQ segment
RR_ISECT_RAY_CYL_P_SIDE, // on the P side
RR_ISECT_RAY_CYL_Q_SIDE, // on the Q side
RR_ISECT_RAY_CYL_SA_INSIDE = -1, //!< the ray starts inside the cylinder
RR_ISECT_RAY_CYL_NONE, //!< no intersection
RR_ISECT_RAY_CYL_PQ, //!< along the PQ segment
RR_ISECT_RAY_CYL_P_SIDE, //!< on the P side
RR_ISECT_RAY_CYL_Q_SIDE, //!< on the Q side
};
//! Reference: Real-Time Collision Detection - 5.3.7 Intersecting Ray or Segment Against Cylinder
//! Intersect segment S(t)=sa+t(dir), 0<=t<=1 against cylinder specified by p, q and r.
int IntersectSegmentCylinder(
const Vector3& sa, const Vector3& dir, const Vector3& p, const Vector3& q,
const float r, float& t);
//! @param sa The initial point.
//! @param dir Magnitude and direction for sa.
//! @param p Center point of side 1 cylinder.
//! @param q Center point of side 2 cylinder.
//! @param r Radius of cylinder.
//! @param[out] t Proporition along line segment.
//! @return CylinderIsectTypes
CylinderIsectTypes IntersectSegmentCylinder(
const Vector3& sa, const Vector3& dir, const Vector3& p, const Vector3& q, const float r, float& t);
//! Capsule ray intersect types.
enum CapsuleIsectTypes
{
ISECT_RAY_CAPSULE_SA_INSIDE = -1, // the ray starts inside the cylinder
ISECT_RAY_CAPSULE_NONE, // no intersection
ISECT_RAY_CAPSULE_PQ, // along the PQ segment
ISECT_RAY_CAPSULE_P_SIDE, // on the P side
ISECT_RAY_CAPSULE_Q_SIDE, // on the Q side
ISECT_RAY_CAPSULE_SA_INSIDE = -1, //!< The ray starts inside the cylinder
ISECT_RAY_CAPSULE_NONE, //!< No intersection
ISECT_RAY_CAPSULE_PQ, //!< Along the PQ segment
ISECT_RAY_CAPSULE_P_SIDE, //!< On the P side
ISECT_RAY_CAPSULE_Q_SIDE, //!< On the Q side
};
//! This is a quick implementation of segment capsule based on segment cylinder \ref IntersectSegmentCylinder
//! segment sphere intersection. We can optimize it a lot once we fix the ray
//! cylinder intersection.
int IntersectSegmentCapsule(
const Vector3& sa, const Vector3& dir, const Vector3& p,
const Vector3& q, const float r, float& t);
//! @param sa The beginning of the line segment.
//! @param dir The direction and length of the segment.
//! @param p Center point of side 1 capsule.
//! @param q Center point of side 1 capsule.
//! @param r The radius of the capsule.
//! @param[out] t Proporition along line segment.
//! @return CapsuleIsectTypes
CapsuleIsectTypes IntersectSegmentCapsule(
const Vector3& sa, const Vector3& dir, const Vector3& p, const Vector3& q, const float r, float& t);
//! Intersect segment S(t)=A+t(B-A), 0<=t<=1 against convex polyhedron specified
//! by the n halfspaces defined by the planes p[]. On exit tfirst and tlast
//! define the intersection, if any.
int IntersectSegmentPolyhedron(
const Vector3& sa, const Vector3& sBA, const Plane p[], int numPlanes,
float& tfirst, float& tlast, int& iFirstPlane, int& iLastPlane);
//! @param sa The beggining of the line segment.
//! @param dir The direction and length of the segment.
//! @param p Planes that compose a convex ponvex polyhedron.
//! @param numPlanes number of planes.
//! @param[out] tfirst Proportion along the line segment where the line enters.
//! @param[out] tlast Proportion along the line segment where the line exits.
//! @param[out] iFirstPlane The plane where the line enters.
//! @param[out] iLastPlane The plane where the line exits.
//! @return True if intersects else false.
bool IntersectSegmentPolyhedron(
const Vector3& sa,
const Vector3& dir,
const Plane p[],
int numPlanes,
float& tfirst,
float& tlast,
int& iFirstPlane,
int& iLastPlane);
//! Calculate the line segment closestPointSegment1<->closestPointSegment2 that is the shortest route between
//! two segments segment1Start<->segment1End and segment2Start<->segment2End. Also calculate the values of segment1Proportion and segment2Proportion where
//! closestPointSegment1 = segment1Start + (segment1Proportion * (segment1End - segment1Start))
//! two segments segment1Start<->segment1End and segment2Start<->segment2End. Also calculate the values of segment1Proportion and
//! segment2Proportion where closestPointSegment1 = segment1Start + (segment1Proportion * (segment1End - segment1Start))
//! closestPointSegment2 = segment2Start + (segment2Proportion * (segment2End - segment2Start))
//! If segments are parallel returns a solution.
//! @param segment1Start Start of segment 1.
//! @param segment1End End of segment 1.
//! @param segment2Start Start of segment 2.
//! @param segment2End End of segment 2.
//! @param[out] segment1Proportion The proporition along segment 1 [0..1]
//! @param[out] segment2Proportion The proporition along segment 2 [0..1]
//! @param[out] closestPointSegment1 Closest point on segment 1.
//! @param[out] closestPointSegment2 Closest point on segment 2.
//! @param epsilon The minimum square distance where a line segment can be treated as a single point.
void ClosestSegmentSegment(
const Vector3& segment1Start, const Vector3& segment1End,
const Vector3& segment2Start, const Vector3& segment2End,
float& segment1Proportion, float& segment2Proportion,
Vector3& closestPointSegment1, Vector3& closestPointSegment2,
const Vector3& segment1Start,
const Vector3& segment1End,
const Vector3& segment2Start,
const Vector3& segment2End,
float& segment1Proportion,
float& segment2Proportion,
Vector3& closestPointSegment1,
Vector3& closestPointSegment2,
float epsilon = 1e-4f);
//! Calculate the line segment closestPointSegment1<->closestPointSegment2 that is the shortest route between
//! two segments segment1Start<->segment1End and segment2Start<->segment2End.
//! If segments are parallel returns a solution.
//! @param segment1Start Start of segment 1.
//! @param segment1End End of segment 1.
//! @param segment2Start Start of segment 2.
//! @param segment2End End of segment 2.
//! @param[out] closestPointSegment1 Closest point on segment 1.
//! @param[out] closestPointSegment2 Closest point on segment 2.
//! @param epsilon The minimum square distance where a line segment can be treated as a single point.
void ClosestSegmentSegment(
const Vector3& segment1Start, const Vector3& segment1End,
const Vector3& segment2Start, const Vector3& segment2End,
Vector3& closestPointSegment1, Vector3& closestPointSegment2,
const Vector3& segment1Start,
const Vector3& segment1End,
const Vector3& segment2Start,
const Vector3& segment2End,
Vector3& closestPointSegment1,
Vector3& closestPointSegment2,
float epsilon = 1e-4f);
//! Calculate the point (closestPointOnSegment) that is the closest point on
//! segment segmentStart/segmentEnd to point. Also calculate the value of proportion where
//! closestPointOnSegment = segmentStart + (proportion * (segmentEnd - segmentStart))
//! @param point The point to test
//! @param segmentStart The start of the segment
//! @param segmentEnd The end of the segment
//! @param[out] proportion The proportion of the segment L(t) = (end - start) * t
//! @param[out] closestPointOnSegment The point along the line segment
void ClosestPointSegment(
const Vector3& point, const Vector3& segmentStart, const Vector3& segmentEnd,
float& proportion, Vector3& closestPointOnSegment);
}
}
const Vector3& point,
const Vector3& segmentStart,
const Vector3& segmentEnd,
float& proportion,
Vector3& closestPointOnSegment);
} // namespace Intersect
} // namespace AZ
#endif // AZCORE_MATH_SEGMENT_INTERSECTION_H
#pragma once
#include <AzCore/Math/IntersectSegment.inl>
@@ -0,0 +1,101 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
namespace AZ
{
namespace Intersect
{
AZ_MATH_INLINE bool ClipRayWithAabb(const Aabb& aabb, Vector3& rayStart, Vector3& rayEnd, float& tClipStart, float& tClipEnd)
{
Vector3 startNormal;
float tStart, tEnd;
Vector3 dirLen = rayEnd - rayStart;
if (IntersectRayAABB(rayStart, dirLen, dirLen.GetReciprocal(), aabb, tStart, tEnd, startNormal) != ISECT_RAY_AABB_NONE)
{
// clip the ray with the box
if (tStart > 0.0f)
{
rayStart = rayStart + tStart * dirLen;
tClipStart = tStart;
}
if (tEnd < 1.0f)
{
rayEnd = rayStart + tEnd * dirLen;
tClipEnd = tEnd;
}
return true;
}
return false;
}
AZ_MATH_INLINE SphereIsectTypes
IntersectRaySphereOrigin(const Vector3& rayStart, const Vector3& rayDirNormalized, const float sphereRadius, float& t)
{
Vector3 m = rayStart;
float b = m.Dot(rayDirNormalized);
float c = m.Dot(m) - sphereRadius * sphereRadius;
// Exit if r's origin outside s (c > 0)and r pointing away from s (b > 0)
if (c > 0.0f && b > 0.0f)
{
return ISECT_RAY_SPHERE_NONE;
}
float discr = b * b - c;
// A negative discriminant corresponds to ray missing sphere
if (discr < 0.0f)
{
return ISECT_RAY_SPHERE_NONE;
}
// Ray now found to intersect sphere, compute smallest t value of intersection
t = -b - Sqrt(discr);
// If t is negative, ray started inside sphere so clamp t to zero
if (t < 0.0f)
{
// t = 0.0f;
return ISECT_RAY_SPHERE_SA_INSIDE; // no hit if inside
}
// q = p + t * d;
return ISECT_RAY_SPHERE_ISECT;
}
AZ_MATH_INLINE SphereIsectTypes IntersectRaySphere(const Vector3& rayStart, const Vector3& rayDirNormalized, const Vector3& sphereCenter, const float sphereRadius, float& t)
{
return IntersectRaySphereOrigin(rayStart - sphereCenter, rayDirNormalized, sphereRadius, t);
}
AZ_MATH_INLINE Vector3 LineToPointDistance(const Vector3& s1, const Vector3& s2, const Vector3& p, float& u)
{
const Vector3 s21 = s2 - s1;
// we assume seg1 and seg2 are NOT coincident
AZ_MATH_ASSERT(!s21.IsClose(Vector3(0.0f), 1e-4f), "OK we agreed that we will pass valid segments! (s1 != s2)");
u = LineToPointDistanceTime(s1, s21, p);
return s1 + u * s21;
}
AZ_MATH_INLINE float LineToPointDistanceTime(const Vector3& s1, const Vector3& s21, const Vector3& p)
{
// so u = (p.x - s1.x)*(s2.x - s1.x) + (p.y - s1.y)*(s2.y - s1.y) + (p.z-s1.z)*(s2.z-s1.z) / |s2-s1|^2
return s21.Dot(p - s1) / s21.Dot(s21);
}
AZ_MATH_INLINE bool TestSegmentAABB(const Vector3& p0, const Vector3& p1, const Aabb& aabb)
{
Vector3 e = aabb.GetExtents();
Vector3 d = p1 - p0;
Vector3 m = p0 + p1 - aabb.GetMin() - aabb.GetMax();
return TestSegmentAABBOrigin(m, d, e);
}
} // namespace Intersect
} // namespace AZ
@@ -34,7 +34,9 @@ namespace AZ
friend IAllocator;
friend class AllocatorBase;
friend class Debug::AllocationRecords;
friend class AZ::Internal::EnvironmentVariableHolder<AllocatorManager>;
template<typename T, typename... Args> friend constexpr auto AZStd::construct_at(T*, Args&&... args)
->AZStd::enable_if_t<AZStd::is_void_v<AZStd::void_t<decltype(new (AZStd::declval<void*>()) T(AZStd::forward<Args>(args)...))>>, T*>;
template<typename T> constexpr friend void AZStd::destroy_at(T*);
public:
typedef AZStd::function<void (IAllocator* allocator, size_t /*byteSize*/, size_t /*alignment*/, int/* flags*/, const char* /*name*/, const char* /*fileName*/, int lineNum /*=0*/)> OutOfMemoryCBType;
@@ -251,16 +251,15 @@ namespace AZ
class EnvironmentVariableHolder
: public EnvironmentVariableHolderBase
{
void ConstructImpl(const AZStd::true_type& /* AZStd::has_trivial_constructor<T> */)
{
memset(&m_value, 0, sizeof(T));
}
template<class... Args>
void ConstructImpl(const AZStd::false_type& /* AZStd::has_trivial_constructor<T> */, Args&&... args)
void ConstructImpl(Args&&... args)
{
// Construction of non-trivial types is left up to the type's constructor.
new(&m_value) T(AZStd::forward<Args>(args)...);
// Use std::launder to ensure that the compiler treats the T* reinterpret_cast as a new object
#if __cpp_lib_launder
AZStd::construct_at(std::launder(reinterpret_cast<T*>(&m_value)), AZStd::forward<Args>(args)...);
#else
AZStd::construct_at(reinterpret_cast<T*>(&m_value), AZStd::forward<Args>(args)...);
#endif
}
static void DestructDispatchNoLock(EnvironmentVariableHolderBase *base, DestroyTarget selfDestruct)
{
@@ -274,10 +273,12 @@ namespace AZ
AZ_Assert(self->m_isConstructed, "Variable is not constructed. Please check your logic and guard if needed!");
self->m_isConstructed = false;
self->m_moduleOwner = nullptr;
if constexpr(!AZStd::is_trivially_destructible_v<T>)
{
reinterpret_cast<T*>(&self->m_value)->~T();
}
// Use std::launder to ensure that the compiler treats the T* reinterpret_cast as a new object
#if __cpp_lib_launder
AZStd::destroy_at(std::launder(reinterpret_cast<T*>(&self->m_value)));
#else
AZStd::destroy_at(reinterpret_cast<T*>(&self->m_value));
#endif
}
public:
EnvironmentVariableHolder(u32 guid, bool isOwnershipTransfer, Environment::AllocatorInterface* allocator)
@@ -303,24 +304,13 @@ namespace AZ
UnregisterAndDestroy(DestructDispatchNoLock, moduleRelease);
}
void Construct()
{
AZStd::lock_guard<AZStd::spin_mutex> lock(m_mutex);
if (!m_isConstructed)
{
ConstructImpl(AZStd::is_trivially_constructible<T>{});
m_isConstructed = true;
m_moduleOwner = Environment::GetModuleId();
}
}
template <class... Args>
void Construct(Args&&... args)
{
AZStd::lock_guard<AZStd::spin_mutex> lock(m_mutex);
if (!m_isConstructed)
{
ConstructImpl(typename AZStd::false_type(), AZStd::forward<Args>(args)...);
ConstructImpl(AZStd::forward<Args>(args)...);
m_isConstructed = true;
m_moduleOwner = Environment::GetModuleId();
}
@@ -333,7 +323,7 @@ namespace AZ
}
// variable storage
typename AZStd::aligned_storage<sizeof(T), AZStd::alignment_of<T>::value>::type m_value;
AZStd::aligned_storage_for_t<T> m_value;
static int s_moduleUseCount;
};
@@ -468,6 +458,11 @@ namespace AZ
Get() = value;
}
void Set(T&& value)
{
Get() = AZStd::move(value);
}
explicit operator bool() const
{
return IsValid();
@@ -42,7 +42,10 @@ namespace AZ
AZ_Assert(m_useCount > 0, "m_useCount is already 0!");
if (m_useCount.fetch_sub(1) == 1)
{
AZ::NameDictionary::Instance().TryReleaseName(hash);
if (AZ::NameDictionary::IsReady())
{
AZ::NameDictionary::Instance().TryReleaseName(hash);
}
}
}
}
@@ -21,23 +21,18 @@ namespace AZ
namespace NameDictionaryInternal
{
static AZ::EnvironmentVariable<NameDictionary*> s_instance = nullptr;
static AZ::EnvironmentVariable<NameDictionary> s_instance = nullptr;
}
void NameDictionary::Create()
{
using namespace NameDictionaryInternal;
AZ_Assert(!s_instance || !s_instance.Get(), "NameDictionary already created!");
AZ_Assert(!s_instance, "NameDictionary already created!");
if (!s_instance)
{
s_instance = AZ::Environment::CreateVariable<NameDictionary*>(NameDictionaryInstanceName);
}
if (!s_instance.Get())
{
s_instance.Set(aznew NameDictionary());
s_instance = AZ::Environment::CreateVariable<NameDictionary>(NameDictionaryInstanceName);
}
}
@@ -46,8 +41,7 @@ namespace AZ
using namespace NameDictionaryInternal;
AZ_Assert(s_instance, "NameDictionary not created!");
delete (*s_instance);
*s_instance = nullptr;
s_instance.Reset();
}
bool NameDictionary::IsReady()
@@ -56,10 +50,10 @@ namespace AZ
if (!s_instance)
{
s_instance = Environment::FindVariable<NameDictionary*>(NameDictionaryInstanceName);
s_instance = Environment::FindVariable<NameDictionary>(NameDictionaryInstanceName);
}
return s_instance && *s_instance;
return s_instance.IsConstructed();
}
NameDictionary& NameDictionary::Instance()
@@ -68,12 +62,12 @@ namespace AZ
if (!s_instance)
{
s_instance = Environment::FindVariable<NameDictionary*>(NameDictionaryInstanceName);
s_instance = Environment::FindVariable<NameDictionary>(NameDictionaryInstanceName);
}
AZ_Assert(s_instance && *s_instance, "NameDictionary has not been initialized yet.");
AZ_Assert(s_instance.IsConstructed(), "NameDictionary has not been initialized yet.");
return *(*s_instance);
return *s_instance;
}
NameDictionary::NameDictionary()
@@ -16,7 +16,7 @@
#include <AzCore/Memory/OSAllocator.h>
#include <AzCore/Name/Name.h>
namespace MaterialEditor
namespace MaterialEditor
{
class MaterialEditorCoreComponent;
}
@@ -34,14 +34,14 @@ namespace AZ
{
class NameData;
};
//! Maintains a list of unique strings for Name objects.
//! The main benefit of the Name system is very fast string equality comparison, because every
//! unique name has a unique ID. The NameDictionary's purpose is to guarantee name IDs do not
//! unique name has a unique ID. The NameDictionary's purpose is to guarantee name IDs do not
//! collide. It also saves memory by removing duplicate strings.
//!
//! Benchmarks have shown that creating a new Name object can be quite slow when the name doesn't
//! already exist in the NameDictionary, but is comparable to creating an AZStd::string for names
//! Benchmarks have shown that creating a new Name object can be quite slow when the name doesn't
//! already exist in the NameDictionary, but is comparable to creating an AZStd::string for names
//! that already exist.
class NameDictionary final
{
@@ -51,7 +51,10 @@ namespace AZ
friend Name;
friend Internal::NameData;
friend UnitTest::NameDictionaryTester;
template<typename T, typename... Args> friend constexpr auto AZStd::construct_at(T*, Args&&... args)
-> AZStd::enable_if_t<AZStd::is_void_v<AZStd::void_t<decltype(new (AZStd::declval<void*>()) T(AZStd::forward<Args>(args)...))>>, T*>;
template<typename T> constexpr friend void AZStd::destroy_at(T*);
public:
static void Create();
@@ -62,7 +65,7 @@ namespace AZ
//! Makes a Name from the provided raw string. If an entry already exists in the dictionary, it is shared.
//! Otherwise, it is added to the internal dictionary.
//!
//!
//! @param name The name to resolve against the dictionary.
//! @return A Name instance holding a dictionary entry associated with the provided raw string.
Name MakeName(AZStd::string_view name);
@@ -84,13 +87,13 @@ namespace AZ
// Attempts to release the name from the dictionary, but checks to make sure
// a reference wasn't taken by another thread.
void TryReleaseName(Name::Hash hash);
//////////////////////////////////////////////////////////////////////////
// Calculates a hash for the provided name string.
// Does not attempt to resolve hash collisions; that is handled elsewhere.
Name::Hash CalcHash(AZStd::string_view name);
AZStd::unordered_map<Name::Hash, Internal::NameData*> m_dictionary;
mutable AZStd::shared_mutex m_sharedMutex;
};
@@ -204,14 +204,14 @@ namespace AZ
[[nodiscard]] virtual PreMergeEventHandler RegisterPreMergeEvent(const PreMergeEventCallback& callback) = 0;
//! Register a function that will be called before a file is merged.
//! @callback The function to call before a file is merged.
[[nodiscard]] virtual PreMergeEventHandler RegisterPreMergeEvent (PreMergeEventCallback&& callback) = 0;
[[nodiscard]] virtual PreMergeEventHandler RegisterPreMergeEvent(PreMergeEventCallback&& callback) = 0;
//! Register a function that will be called after a file is merged.
//! @callback The function to call after a file is merged.
[[nodiscard]] virtual PostMergeEventHandler RegisterPostMergeEvent(const PostMergeEventCallback& callback) = 0;
//! Register a function that will be called after a file is merged.
//! @callback The function to call after a file is merged.
[[nodiscard]] virtual PostMergeEventHandler RegisterPostMergeEvent (PostMergeEventCallback&& callback) = 0;
[[nodiscard]] virtual PostMergeEventHandler RegisterPostMergeEvent(PostMergeEventCallback&& callback) = 0;
//! Gets the boolean value at the provided path.
//! @param result The target to write the result to.
@@ -0,0 +1,136 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzCore/Settings/SettingsRegistryVisitorUtils.h>
namespace AZ::SettingsRegistryVisitorUtils
{
// Field Visitor implementation
FieldVisitor::FieldVisitor() = default;
FieldVisitor::FieldVisitor(VisitFieldType visitFieldType)
: m_visitFieldType{ visitFieldType }
{
}
auto FieldVisitor::Traverse(AZStd::string_view path, AZStd::string_view valueName,
VisitAction action, Type type) -> VisitResponse
{
// A default response skip prevents visiting grand children(depth 2 or lower)
VisitResponse visitResponse = VisitResponse::Skip;
if (action == VisitAction::Begin)
{
// Invoke FieldVisitor override if the root path has been set
if (m_rootPath.has_value())
{
Visit(path, valueName, type);
}
// To make sure only the direct children are visited(depth 1)
// set the root path once and set the VisitReponsoe
// to Continue to recurse into is fields
if (!m_rootPath.has_value())
{
bool visitableFieldType{};
switch (m_visitFieldType)
{
case VisitFieldType::Array:
visitableFieldType = type == Type::Array;
break;
case VisitFieldType::Object:
visitableFieldType = type == Type::Object;
break;
case VisitFieldType::ArrayOrObject:
visitableFieldType = type == Type::Array || type ==Type::Object;
break;
default:
AZ_Error("FieldVisitor", false, "The field visitation type value is invalid");
break;
}
if (visitableFieldType)
{
m_rootPath = path;
visitResponse = VisitResponse::Continue;
}
}
}
else if (action == VisitAction::Value)
{
// Invoke FieldVisitor override if the root path has been set
if (m_rootPath.has_value())
{
Visit(path, valueName, type);
}
}
else if (action == VisitAction::End)
{
// Reset m_rootPath back to null when the root path has finished being visited
if (m_rootPath.has_value() && *m_rootPath == path)
{
m_rootPath = AZStd::nullopt;
}
}
return visitResponse;
}
// Array Visitor implementation
ArrayVisitor::ArrayVisitor()
: FieldVisitor(VisitFieldType::Array)
{
}
// Object Visitor implementation
ObjectVisitor::ObjectVisitor()
: FieldVisitor(VisitFieldType::Object)
{
}
// Generic VisitField Callback implemention
template <typename BaseVisitor>
bool VisitFieldCallback(AZ::SettingsRegistryInterface& settingsRegistry, const VisitorCallback& visitCallback, AZStd::string_view path)
{
struct VisitFieldVisitor
: BaseVisitor
{
using BaseVisitor::Visit;
VisitFieldVisitor(const VisitorCallback& visitCallback)
: m_visitCallback{ visitCallback }
{}
void Visit(AZStd::string_view path, AZStd::string_view fieldIndex, typename BaseVisitor::Type type) override
{
m_visitCallback(path, fieldIndex, type);
}
const VisitorCallback& m_visitCallback;
};
VisitFieldVisitor visitor{ visitCallback };
return settingsRegistry.Visit(visitor, path);
}
// VisitField implementation
bool VisitField(AZ::SettingsRegistryInterface& settingsRegistry, const VisitorCallback& visitCallback, AZStd::string_view path)
{
return VisitFieldCallback<FieldVisitor>(settingsRegistry, visitCallback, path);
}
// VisitArray implementation
bool VisitArray(AZ::SettingsRegistryInterface& settingsRegistry, const VisitorCallback& visitCallback, AZStd::string_view path)
{
return VisitFieldCallback<ArrayVisitor>(settingsRegistry, visitCallback, path);
}
// VisitObject implementation
bool VisitObject(AZ::SettingsRegistryInterface& settingsRegistry, const VisitorCallback& visitCallback, AZStd::string_view path)
{
return VisitFieldCallback<ObjectVisitor>(settingsRegistry, visitCallback, path);
}
}
@@ -0,0 +1,83 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/Settings/SettingsRegistry.h>
namespace AZ::SettingsRegistryVisitorUtils
{
//! Interface for visiting the fields of an array or object
//! To access the values, use the SettingsRegistryInterface Get/GetObject methods
struct FieldVisitor
: public AZ::SettingsRegistryInterface::Visitor
{
using VisitResponse = AZ::SettingsRegistryInterface::VisitResponse;
using VisitAction = AZ::SettingsRegistryInterface::VisitAction;
using Type = AZ::SettingsRegistryInterface::Type;
FieldVisitor();
// Bring the base class visitor functions into scope
using AZ::SettingsRegistryInterface::Visitor::Visit;
virtual void Visit(AZStd::string_view path, AZStd::string_view arrayIndex, Type type) = 0;
protected:
// VisitFieldType is used for filtering the type of referenced by the root path
enum class VisitFieldType
{
Array,
Object,
ArrayOrObject
};
FieldVisitor(const VisitFieldType visitFieldType);
private:
VisitResponse Traverse(AZStd::string_view path, AZStd::string_view valueName,
VisitAction action, Type type) override;
VisitFieldType m_visitFieldType{ VisitFieldType::ArrayOrObject };
AZStd::optional<AZ::SettingsRegistryInterface::FixedValueString> m_rootPath;
};
//! Interface for visiting the fields of an array
//! To access the values, use the SettingsRegistryInterface Get/GetObject methods
struct ArrayVisitor
: public FieldVisitor
{
ArrayVisitor();
};
//! Interface for visiting the fields of an object
//! To access the values, use the SettingsRegistryInterface Get/GetObject methods
struct ObjectVisitor
: public FieldVisitor
{
ObjectVisitor();
};
//! Signature of callback funcition invoked when visiting an element of an array or object
using VisitorCallback = AZStd::function<void(AZStd::string_view path, AZStd::string_view fieldName,
AZ::SettingsRegistryInterface::Type)>;
//! Invokes the visitor callback for each element of either the array or object at @path
//! If @path is not an array or object, then no elements are visited
//! This function will not recurse into children of elements
//! @visitCallback functor that is invoked for each array or object element found
bool VisitField(AZ::SettingsRegistryInterface& settingsRegistry, const VisitorCallback& visitCallback, AZStd::string_view path);
//! Invokes the visitor callback for each element of the array at @path
//! If @path is not an array, then no elements are visited
//! This function will not recurse into children of elements
//! @visitCallback functor that is invoked for each array element found
bool VisitArray(AZ::SettingsRegistryInterface& settingsRegistry, const VisitorCallback& visitCallback, AZStd::string_view path);
//! Invokes the visitor callback for each element of the object at @path
//! If @path is not an object, then no elements are visited
//! This function will not recurse into children of elements
//! @visitCallback functor that is invoked for each object element found
bool VisitObject(AZ::SettingsRegistryInterface& settingsRegistry, const VisitorCallback& visitCallback, AZStd::string_view path);
}
@@ -308,11 +308,11 @@ namespace AZ
void TaskExecutor::SetInstance(TaskExecutor* executor)
{
if (!executor)
if (!executor) // allow unsetting the executor
{
s_executor.Reset();
}
else if (!s_executor) // ignore any calls to set after the first (this happens in unit tests that create new system entities)
else if (!s_executor) // ignore any extra executors after the first (this happens during unit tests)
{
s_executor = AZ::Environment::CreateVariable<TaskExecutor*>(s_executorName, executor);
}
@@ -11,9 +11,15 @@
#include <AzCore/Task/TaskGraphSystemComponent.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Math/MathUtils.h>
#include <AzCore/Threading/ThreadUtils.h>
// Create a cvar as a central location for experimentation with switching from the Job system to TaskGraph system.
AZ_CVAR(bool, cl_activateTaskGraph, false, nullptr, AZ::ConsoleFunctorFlags::Null, "Flag clients of TaskGraph to switch between jobs/taskgraph (Note does not disable task graph system)");
AZ_CVAR(float, cl_taskGraphThreadsConcurrencyRatio, 1.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "TaskGraph calculate the number of worker threads to spawn by scaling the number of hw threads, value is clamped between 0.0f and 1.0f");
AZ_CVAR(uint32_t, cl_taskGraphThreadsNumReserved, 2, nullptr, AZ::ConsoleFunctorFlags::Null, "TaskGraph number of hardware threads that are reserved for O3DE system threads. Value is clamped between 0 and the number of logical cores in the system");
AZ_CVAR(uint32_t, cl_taskGraphThreadsMinNumber, 2, nullptr, AZ::ConsoleFunctorFlags::Null, "TaskGraph minimum number of worker threads to create after scaling the number of hw threads");
static constexpr uint32_t TaskExecutorServiceCrc = AZ_CRC_CE("TaskExecutorService");
namespace AZ
@@ -24,8 +30,8 @@ namespace AZ
if (Interface<TaskGraphActiveInterface>::Get() == nullptr)
{
Interface<TaskGraphActiveInterface>::Register(this);
m_taskExecutor = aznew TaskExecutor();
Interface<TaskGraphActiveInterface>::Register(this); // small window that another thread can try to use taskgraph between this line and the set instance.
m_taskExecutor = aznew TaskExecutor(Threading::CalcNumWorkerThreads(cl_taskGraphThreadsConcurrencyRatio, cl_taskGraphThreadsMinNumber, cl_taskGraphThreadsNumReserved));
TaskExecutor::SetInstance(m_taskExecutor);
}
}
@@ -0,0 +1,25 @@
/*
* 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/Threading/ThreadUtils.h>
#include <AzCore/std/parallel/thread.h>
#include <AzCore/Math/MathUtils.h>
namespace AZ::Threading
{
uint32_t CalcNumWorkerThreads(float workerThreadsRatio, uint32_t minNumWorkerThreads, uint32_t reservedNumThreads)
{
const uint32_t maxHardwareThreads = AZStd::thread::hardware_concurrency();
const uint32_t numReservedThreads = AZ::GetMin<uint32_t>(reservedNumThreads, maxHardwareThreads); // protect against num reserved being bigger than the number of hw threads
const uint32_t maxWorkerThreads = maxHardwareThreads - numReservedThreads;
const float requestedWorkerThreads = AZ::GetClamp<float>(workerThreadsRatio, 0.0f, 1.0f) * static_cast<float>(maxWorkerThreads);
const uint32_t requestedWorkerThreadsRounded = AZStd::lround(requestedWorkerThreads);
const uint32_t numWorkerThreads = AZ::GetMax<uint32_t>(minNumWorkerThreads, requestedWorkerThreadsRounded);
return numWorkerThreads;
}
};
@@ -0,0 +1,22 @@
/*
* 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/base.h>
namespace AZ::Threading
{
//! Calculates the number of worker threads a system should use based on the number of hardware threads a device has.
//! result = max (minNumWorkerThreads, workerThreadsRatio * (num_hardware_threads - reservedNumThreads))
//! @param workerThreadsRatio scale applied to the calculated maximum number of threads available after reserved threads have been accounted for. Clamped between 0 and 1.
//! @param minNumWorkerThreads minimum value that will be returned. Value is unclamped and can be more than num_hardware_threads.
//! @param reservedNumThreads number of hardware threads to reserve for O3DE system threads. Value clamped to num_hardware_threads.
//! @return number of worker threads for the calling system to allocate
uint32_t CalcNumWorkerThreads(float workerThreadsRatio, uint32_t minNumWorkerThreads, uint32_t reservedNumThreads);
};
@@ -282,6 +282,7 @@ set(FILES
Math/Internal/VertexContainer.inl
Math/InterpolationSample.h
Math/IntersectPoint.h
Math/IntersectSegment.inl
Math/IntersectSegment.cpp
Math/IntersectSegment.h
Math/MathIntrinsics.h
@@ -566,6 +567,8 @@ set(FILES
Settings/SettingsRegistryMergeUtils.h
Settings/SettingsRegistryScriptUtils.cpp
Settings/SettingsRegistryScriptUtils.h
Settings/SettingsRegistryVisitorUtils.cpp
Settings/SettingsRegistryVisitorUtils.h
State/HSM.cpp
State/HSM.h
Statistics/NamedRunningStatistic.h
@@ -639,6 +642,8 @@ set(FILES
Threading/ThreadSafeDeque.inl
Threading/ThreadSafeObject.h
Threading/ThreadSafeObject.inl
Threading/ThreadUtils.h
Threading/ThreadUtils.cpp
Time/ITime.h
Time/TimeSystemComponent.cpp
Time/TimeSystemComponent.h
@@ -0,0 +1,94 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzCore/std/allocator_stateless.h>
#include <AzCore/Memory/OSAllocator.h>
namespace AZStd
{
stateless_allocator::stateless_allocator(const char* name)
: m_name(name) {}
const char* stateless_allocator::get_name() const
{
return m_name;
}
void stateless_allocator::set_name(const char* name)
{
m_name = name;
}
auto stateless_allocator::allocate(size_type byteSize) -> pointer_type
{
return allocate(byteSize, AZ_DEFAULT_ALIGNMENT, 0);
}
auto stateless_allocator::allocate(size_type byteSize, size_type alignment, int) -> pointer_type
{
pointer_type address = AZ_OS_MALLOC(byteSize, alignment);
if (address == nullptr)
{
AZ_Error("Memory", false, "stateless_allocator ran out of system memory!\n");
}
return address;
}
void stateless_allocator::deallocate(pointer_type ptr, size_type)
{
AZ_OS_FREE(ptr);
}
void stateless_allocator::deallocate(pointer_type ptr, size_type, size_type)
{
AZ_OS_FREE(ptr);
}
auto stateless_allocator::max_size() const -> size_type
{
return AZ_CORE_MAX_ALLOCATOR_SIZE;
}
stateless_allocator stateless_allocator::select_on_container_copy_construction() const
{
return *this;
}
auto stateless_allocator::resize(pointer_type, size_type) -> size_type
{
return 0;
}
bool stateless_allocator::is_lock_free()
{
return false;
}
bool stateless_allocator::is_stale_read_allowed()
{
return false;
}
bool stateless_allocator::is_delayed_recycling()
{
return false;
}
// comparison operators
bool operator==(const stateless_allocator&, const stateless_allocator&)
{
return true;
}
bool operator!=(const stateless_allocator&, const stateless_allocator&)
{
return false;
}
}
@@ -0,0 +1,61 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/std/base.h>
#include <AzCore/std/typetraits/integral_constant.h>
#include <AzCore/RTTI/TypeInfoSimple.h>
namespace AZStd
{
class stateless_allocator
{
public:
AZ_TYPE_INFO(stateless_allocator, "{E4976C53-0B20-4F39-8D41-0A76F59A7D68}");
using value_type = uint8_t;
using pointer_type = void*;
using size_type = size_t;
using difference_type = ptrdiff_t;
using allow_memory_leaks = AZStd::true_type;
stateless_allocator(const char* name = "AZStd::stateless_allocator");
stateless_allocator(const stateless_allocator& rhs) = default;
stateless_allocator& operator=(const stateless_allocator& rhs) = default;
const char* get_name() const;
void set_name(const char* name);
pointer_type allocate(size_type byteSize);
pointer_type allocate(size_type byteSize, size_type alignment, int flags = 0);
void deallocate(pointer_type ptr, size_type alignment);
void deallocate(pointer_type ptr, size_type byteSize, size_type alignment);
// max_size actually returns the true maximum size of a single allocation
size_type max_size() const;
// Returns a copy of the allocator
stateless_allocator select_on_container_copy_construction() const;
//! extensions
size_type resize(pointer_type ptr, size_type newSize);
bool is_lock_free();
bool is_stale_read_allowed();
bool is_delayed_recycling();
private:
const char* m_name;
};
bool operator==(const stateless_allocator& left, const stateless_allocator& right);
bool operator!=(const stateless_allocator& left, const stateless_allocator& right);
}
@@ -12,6 +12,8 @@ set(FILES
allocator.h
allocator_ref.h
allocator_stack.h
allocator_stateless.cpp
allocator_stateless.h
allocator_static.h
allocator_traits.h
any.h
@@ -20,7 +20,7 @@
namespace AZStd
{
// alias std::pointer_traits into the AZStd::namespace
// alias std::pointer_traits into the AZStd::namespace
using std::pointer_traits;
//! Bring the names of uninitialized_default_construct and
@@ -229,7 +229,7 @@ namespace AZStd
//! `new (declval<void*>()) T(declval<Args>()...)` is well-formed
template <typename T, typename... Args>
constexpr auto construct_at(T* ptr, Args&&... args)
-> enable_if_t<is_void_v<void_t<decltype(new (declval<void*>()) T(AZStd::forward<Args>(args)...))>>, T*>
-> enable_if_t<AZStd::is_void_v<AZStd::void_t<decltype(new (AZStd::declval<void*>()) T(AZStd::forward<Args>(args)...))>>, T*>
{
return ::new (ptr) T(AZStd::forward<Args>(args)...);
}
@@ -487,7 +487,7 @@ namespace AZStd
{
//! Implements the C++17 uninitialized_move function
//! The functions accepts two input iterators and an output iterator
//! It performs an AZStd::move on each in in the range of the input iterator
//! It performs an AZStd::move on each in in the range of the input iterator
//! and stores the result in location pointed by the output iterator
template <typename InputIt, typename ForwardIt>
ForwardIt uninitialized_move(InputIt first, InputIt last, ForwardIt result)
+45
View File
@@ -30,4 +30,49 @@ namespace AZStd
using std::sqrt;
using std::tan;
using std::trunc;
} // namespace AZStd
// from c++20 standard
namespace AZStd::Internal
{
template<typename T>
constexpr T lerp(T a, T b, T t) noexcept
{
if ((a <= 0 && b >= 0) || (a >= 0 && b <= 0))
{
return t * b + (1 - t) * a;
}
if (t == 1)
{
return b;
}
const T x = a + t * (b - a);
if ((t > 1) == (b > a))
{
return b < x ? x : b;
}
else
{
return x < b ? x : b;
}
}
} // namespace AZStd::Internal
namespace AZStd
{
constexpr float lerp(float a, float b, float t) noexcept
{
return Internal::lerp(a, b, t);
}
constexpr double lerp(double a, double b, double t) noexcept
{
return Internal::lerp(a, b, t);
}
constexpr long double lerp(long double a, long double b, long double t) noexcept
{
return Internal::lerp(a, b, t);
}
} // namespace AZStd
@@ -59,6 +59,10 @@ namespace AZStd
{
priority = desc->m_priority;
}
else
{
priority = SCHED_OTHER;
}
if (desc->m_name)
{
name = desc->m_name;
@@ -0,0 +1,46 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzCore/UnitTest/TestTypes.h>
#include <AzCore/std/math.h>
namespace UnitTest
{
template<typename T>
class StdMathTest : public ::testing::Test
{
};
using MathTestConfigs = ::testing::Types<float, double, long double>;
TYPED_TEST_CASE(StdMathTest, MathTestConfigs);
TYPED_TEST(StdMathTest, LerpOperations)
{
using AZStd::lerp;
using ::testing::Eq;
using T = TypeParam;
constexpr T maxNumber = AZStd::numeric_limits<T>::max();
constexpr T eps = AZStd::numeric_limits<T>::epsilon();
constexpr T a{ 42 };
// exactness: lerp(a,b,0)==a && lerp(a,b,1)==b
EXPECT_THAT(lerp(eps, maxNumber, T(0)), Eq(eps));
EXPECT_THAT(lerp(eps, maxNumber, T(1)), Eq(maxNumber));
// consistency: lerp(a,a,t)==a
EXPECT_THAT(lerp(a, a, T(0.5)), Eq(a));
EXPECT_THAT(lerp(eps, eps, T(0.5)), Eq(eps));
// a few generic tests taken from MathUtilTests.cpp
EXPECT_EQ(T(2.5), lerp(T(2), T(4), T(0.25)));
EXPECT_EQ(T(6.0), lerp(T(2), T(4), T(2.0)));
EXPECT_EQ(T(3.5), lerp(T(2), T(4), T(0.75)));
EXPECT_EQ(T(0.0), lerp(T(2), T(4), T(-1.0)));
}
} // namespace UnitTest
@@ -64,6 +64,91 @@ namespace JsonSerializationTests
};
class TestSerializedAssetTracker
: public BaseJsonSerializerFixture
{
public:
void SetUp() override
{
BaseJsonSerializerFixture::SetUp();
AZ::AllocatorInstance<AZ::PoolAllocator>::Create();
AZ::AllocatorInstance<AZ::ThreadPoolAllocator>::Create();
// Set up the Job Manager with 1 thread so that the Asset Manager is able to load assets.
AZ::JobManagerDesc jobDesc;
AZ::JobManagerThreadDesc threadDesc;
jobDesc.m_workerThreads.push_back(threadDesc);
m_jobManager = aznew AZ::JobManager(jobDesc);
m_jobContext = aznew AZ::JobContext(*m_jobManager);
AZ::JobContext::SetGlobalContext(m_jobContext);
AZ::Data::AssetManager::Descriptor descriptor;
AZ::Data::AssetManager::Create(descriptor);
AZ::Data::AssetManager::Instance().RegisterHandler(&m_assetHandler, azrtti_typeid<TestAssetData>());
m_serializeContext->RegisterGenericType<AZ::Data::Asset<TestAssetData>>();
m_jsonRegistrationContext->Serializer<AZ::Data::AssetJsonSerializer>()->HandlesType<AZ::Data::Asset>();
}
void TearDown() override
{
m_jsonRegistrationContext->EnableRemoveReflection();
m_jsonRegistrationContext->Serializer<AZ::Data::AssetJsonSerializer>()->HandlesType<AZ::Data::Asset>();
m_jsonRegistrationContext->DisableRemoveReflection();
AZ::Data::AssetManager::Instance().UnregisterHandler(&m_assetHandler);
AZ::Data::AssetManager::Destroy();
AZ::JobContext::SetGlobalContext(nullptr);
delete m_jobContext;
delete m_jobManager;
AZ::AllocatorInstance<AZ::ThreadPoolAllocator>::Destroy();
AZ::AllocatorInstance<AZ::PoolAllocator>::Destroy();
BaseJsonSerializerFixture::TearDown();
}
private:
TestAssetHandler m_assetHandler;
AZ::JobManager* m_jobManager{ nullptr };
AZ::JobContext* m_jobContext{ nullptr };
};
TEST_F(TestSerializedAssetTracker, AssetTracker_Callback_Works)
{
auto assetCallback = [](AZ::Data::Asset<AZ::Data::AssetData>& asset)
{
if (!asset.GetId().IsValid() && !asset.GetHint().empty())
{
if (asset.GetHint() == "test/path/foo.asset")
{
asset.SetHint("passed");
}
}
};
auto tracker = AZ::Data::SerializedAssetTracker{};
tracker.SetAssetFixUp(assetCallback);
AZ::JsonDeserializerSettings settings;
settings.m_metadata.Add(tracker);
settings.m_registrationContext = this->m_jsonRegistrationContext.get();
settings.m_serializeContext = this->m_serializeContext.get();
AZStd::string_view assetHintOnlyTestAsset = R"(
{
"assetHint" : "test/path/foo.asset"
})";
rapidjson::Document jsonDom;
jsonDom.Parse(assetHintOnlyTestAsset.data());
AZ::Data::Asset<TestAssetData> instance;
auto result = AZ::JsonSerialization::Load(instance, jsonDom, settings);
EXPECT_NE(result.GetProcessing(), AZ::JsonSerializationResult::Processing::Halted);
EXPECT_STREQ(instance.GetHint().c_str(), "passed");
}
class AssetSerializerTestDescription final
: public JsonSerializerConformityTestDescriptor<AZ::Data::Asset<TestAssetData>>
{
+193 -8
View File
@@ -2088,7 +2088,7 @@ namespace UnitTest
DisconnectNextHandlerByIdImpl multiHandler2;
multiHandler2.BusConnect(DisconnectNextHandlerByIdImpl::firstBusAddress);
multiHandler2.BusConnect(DisconnectNextHandlerByIdImpl::secondBusAddress);
// Set the first handler m_nextHandler field to point to the second handler
multiHandler1.m_nextHandler = &multiHandler2;
@@ -2807,7 +2807,7 @@ namespace UnitTest
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(m_val % m_maxSleep));
}
}
void DoConnect() override
{
MyEventGroupBus::Handler::BusConnect(m_id);
@@ -2854,7 +2854,7 @@ namespace UnitTest
}
MyEventGroupBus::Event(id, &MyEventGroupBus::Events::Calculate, i, i * 2, i << 4);
LocklessConnectorBus::Event(id, &LocklessConnectorBus::Events::DoDisconnect);
bool failed = (AZStd::find_if(&sentinel[0], end, [](char s) { return s != 0; }) != end);
@@ -2891,7 +2891,7 @@ namespace UnitTest
{
MyEventGroupImpl()
{
}
~MyEventGroupImpl() override
@@ -3614,7 +3614,7 @@ namespace UnitTest
{
AZStd::this_thread::yield();
}
EXPECT_GE(AZStd::chrono::system_clock::now(), endTime);
};
AZStd::thread connectThread([&connectHandler, &waitHandler]()
@@ -3813,7 +3813,7 @@ namespace UnitTest
struct LastHandlerDisconnectHandler
: public LastHandlerDisconnectBus::Handler
{
void OnEvent() override
void OnEvent() override
{
++m_numOnEvents;
BusDisconnect();
@@ -3854,7 +3854,7 @@ namespace UnitTest
struct DisconnectAssertHandler
: public DisconnectAssertBus::Handler
{
};
TEST_F(EBus, HandlerDestroyedWithoutDisconnect_Asserts)
@@ -3995,6 +3995,191 @@ namespace UnitTest
idTestRequest.Disconnect();
}
// IsInDispatchThisThread
struct IsInThreadDispatchRequests
: AZ::EBusTraits
{
using MutexType = AZStd::recursive_mutex;
};
using IsInThreadDispatchBus = AZ::EBus<IsInThreadDispatchRequests>;
class IsInThreadDispatchHandler
: public IsInThreadDispatchBus::Handler
{};
TEST_F(EBus, InvokingIsInThisThread_ReturnsSuccess_OnlyIfThreadIsInDispatch)
{
IsInThreadDispatchHandler handler;
handler.BusConnect();
auto ThreadDispatcher = [](IsInThreadDispatchRequests*)
{
EXPECT_TRUE(IsInThreadDispatchBus::IsInDispatchThisThread());
auto PerThreadBusDispatch = []()
{
EXPECT_FALSE(IsInThreadDispatchBus::IsInDispatchThisThread());
};
AZStd::array threads{ AZStd::thread(PerThreadBusDispatch), AZStd::thread(PerThreadBusDispatch) };
for (AZStd::thread& thread : threads)
{
thread.join();
}
};
static constexpr size_t ThreadDispatcherIterations = 4;
for (size_t iteration = 0; iteration < ThreadDispatcherIterations; ++iteration)
{
EXPECT_FALSE(IsInThreadDispatchBus::IsInDispatchThisThread());
IsInThreadDispatchBus::Broadcast(ThreadDispatcher);
EXPECT_FALSE(IsInThreadDispatchBus::IsInDispatchThisThread());
}
}
// Thread Dispatch Policy
struct ThreadDispatchTestBusTraits
: AZ::EBusTraits
{
using MutexType = AZStd::recursive_mutex;
struct PostThreadDispatchTestInvoker
{
~PostThreadDispatchTestInvoker();
};
template <typename DispatchMutex>
struct ThreadDispatchTestLockGuard
{
ThreadDispatchTestLockGuard(DispatchMutex& contextMutex)
: m_lock{ contextMutex }
{}
ThreadDispatchTestLockGuard(DispatchMutex& contextMutex, AZStd::adopt_lock_t adopt_lock)
: m_lock{ contextMutex, adopt_lock }
{}
ThreadDispatchTestLockGuard(const ThreadDispatchTestLockGuard&) = delete;
ThreadDispatchTestLockGuard& operator=(const ThreadDispatchTestLockGuard&) = delete;
private:
PostThreadDispatchTestInvoker m_threadPolicyInvoker;
using LockType = AZStd::conditional_t<LocklessDispatch, AZ::Internal::NullLockGuard<DispatchMutex>, AZStd::scoped_lock<DispatchMutex>>;
LockType m_lock;
};
template <typename DispatchMutex, bool IsLocklessDispatch>
using DispatchLockGuard = ThreadDispatchTestLockGuard<DispatchMutex>;
static inline AZStd::atomic<int32_t> s_threadPostDispatchCalls;
};
class ThreadDispatchTestRequests
{
public:
virtual void FirstCall() = 0;
virtual void SecondCall() = 0;
virtual void ThirdCall() = 0;
};
using ThreadDispatchTestBus = AZ::EBus<ThreadDispatchTestRequests, ThreadDispatchTestBusTraits>;
ThreadDispatchTestBusTraits::PostThreadDispatchTestInvoker::~PostThreadDispatchTestInvoker()
{
if (!ThreadDispatchTestBus::IsInDispatchThisThread())
{
++s_threadPostDispatchCalls;
}
}
class ThreadDispatchTestHandler
: public ThreadDispatchTestBus::Handler
{
public:
void Connect()
{
ThreadDispatchTestBus::Handler::BusConnect();
}
void Disconnect()
{
ThreadDispatchTestBus::Handler::BusDisconnect();
}
void FirstCall() override
{
ThreadDispatchTestBus::Broadcast(&ThreadDispatchTestBus::Events::SecondCall);
}
void SecondCall() override
{
ThreadDispatchTestBus::Broadcast(&ThreadDispatchTestBus::Events::ThirdCall);
}
void ThirdCall() override
{
}
};
template <typename ParamType>
class EBusParamFixture
: public ScopedAllocatorSetupFixture
, public ::testing::WithParamInterface<ParamType>
{};
struct ThreadDispatchParams
{
size_t m_threadCount{};
size_t m_handlerCount{};
};
using ThreadDispatchParamFixture = EBusParamFixture<ThreadDispatchParams>;
INSTANTIATE_TEST_CASE_P(
ThreadDispatch,
ThreadDispatchParamFixture,
::testing::Values(
ThreadDispatchParams{ 1, 1 },
ThreadDispatchParams{ 2, 1 },
ThreadDispatchParams{ 1, 2 },
ThreadDispatchParams{ 2, 2 },
ThreadDispatchParams{ 16, 8 }
)
);
TEST_P(ThreadDispatchParamFixture, CustomDispatchLockGuard_InvokesPostDispatchFunction_AfterThreadHasFinishedDispatch)
{
ThreadDispatchTestBusTraits::s_threadPostDispatchCalls = 0;
ThreadDispatchParams threadDispatchParams = GetParam();
AZStd::vector<AZStd::thread> testThreads;
AZStd::vector<ThreadDispatchTestHandler> testHandlers(threadDispatchParams.m_handlerCount);
for (ThreadDispatchTestHandler& testHandler : testHandlers)
{
testHandler.Connect();
}
static constexpr size_t DispatchThreadCalls = 3;
const size_t totalThreadDispatchCalls = threadDispatchParams.m_threadCount * DispatchThreadCalls;
auto DispatchThreadWorker = []()
{
ThreadDispatchTestBus::Broadcast(&ThreadDispatchTestBus::Events::FirstCall);
ThreadDispatchTestBus::Broadcast(&ThreadDispatchTestBus::Events::SecondCall);
ThreadDispatchTestBus::Broadcast(&ThreadDispatchTestBus::Events::ThirdCall);
};
for (size_t threadIndex = 0; threadIndex < threadDispatchParams.m_threadCount; ++threadIndex)
{
testThreads.emplace_back(DispatchThreadWorker);
}
for (AZStd::thread& thread : testThreads)
{
thread.join();
}
for (ThreadDispatchTestHandler& testHandler : testHandlers)
{
testHandler.Disconnect();
}
EXPECT_EQ(totalThreadDispatchCalls, ThreadDispatchTestBusTraits::s_threadPostDispatchCalls);
ThreadDispatchTestBusTraits::s_threadPostDispatchCalls = 0;
}
} // namespace UnitTest
#if defined(HAVE_BENCHMARK)
@@ -4370,7 +4555,7 @@ namespace Benchmark
Bus::ExecuteQueuedEvents();
}
s_benchmarkEBusEnv<Bus>.Disconnect(state);
}
BUS_BENCHMARK_REGISTER_ALL(BM_EBus_ExecuteBroadcast);
@@ -0,0 +1,196 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzCore/Settings/SettingsRegistryImpl.h>
#include <AzCore/Settings/SettingsRegistryVisitorUtils.h>
#include <AzCore/std/containers/fixed_vector.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzCore/std/string/string.h>
#include <AzCore/UnitTest/TestTypes.h>
namespace SettingsRegistryVisitorUtilsTests
{
struct VisitCallbackParams
{
AZStd::string_view m_inputJsonDocument;
using VisitFieldFunction = bool(*)(AZ::SettingsRegistryInterface&,
const AZ::SettingsRegistryVisitorUtils::VisitorCallback&,
AZStd::string_view);
static inline constexpr size_t MaxFieldCount = 10;
using ObjectFields = AZStd::fixed_vector<AZStd::pair<AZStd::string_view, AZStd::string_view>, MaxFieldCount>;
using ArrayFields = AZStd::fixed_vector<AZStd::string_view, MaxFieldCount>;
ObjectFields m_objectFields;
ArrayFields m_arrayFields;
};
template <typename VisitorParams>
class SettingsRegistryVisitorUtilsParamFixture
: public UnitTest::ScopedAllocatorSetupFixture
, public ::testing::WithParamInterface<VisitorParams>
{
public:
void SetUp() override
{
m_registry = AZStd::make_unique<AZ::SettingsRegistryImpl>();
}
void TearDown() override
{
m_registry.reset();
}
AZStd::unique_ptr<AZ::SettingsRegistryImpl> m_registry;
};
using SettingsRegistryVisitCallbackFixture = SettingsRegistryVisitorUtilsParamFixture<VisitCallbackParams>;
TEST_P(SettingsRegistryVisitCallbackFixture, VisitFunction_VisitFieldsOfArrayType_ReturnsFields)
{
const VisitCallbackParams& visitParams = GetParam();
ASSERT_TRUE(m_registry->MergeSettings(visitParams.m_inputJsonDocument, AZ::SettingsRegistryInterface::Format::JsonMergePatch));
AZStd::fixed_vector<AZStd::string, VisitCallbackParams::MaxFieldCount> testArrayFields;
auto visitorCallback = [this, &testArrayFields](AZStd::string_view path, AZStd::string_view, AZ::SettingsRegistryInterface::Type)
{
AZStd::string fieldValue;
EXPECT_TRUE(m_registry->Get(fieldValue, path));
testArrayFields.emplace_back(AZStd::move(fieldValue));
};
AZ::SettingsRegistryVisitorUtils::VisitField(*m_registry, visitorCallback, "/Test/Array");
const AZStd::fixed_vector<AZStd::string, VisitCallbackParams::MaxFieldCount> expectedFields{
visitParams.m_arrayFields.begin(), visitParams.m_arrayFields.end() };
EXPECT_THAT(testArrayFields, ::testing::ContainerEq(expectedFields));
}
TEST_P(SettingsRegistryVisitCallbackFixture, VisitFunction_VisitFieldsOfObjectType_ReturnsFields)
{
const VisitCallbackParams& visitParams = GetParam();
ASSERT_TRUE(m_registry->MergeSettings(visitParams.m_inputJsonDocument, AZ::SettingsRegistryInterface::Format::JsonMergePatch));
AZStd::fixed_vector<AZStd::pair<AZStd::string, AZStd::string>, VisitCallbackParams::MaxFieldCount> testObjectFields;
auto visitorCallback = [this, &testObjectFields](AZStd::string_view path, AZStd::string_view fieldName, AZ::SettingsRegistryInterface::Type)
{
AZStd::string fieldValue;
EXPECT_TRUE(m_registry->Get(fieldValue, path));
testObjectFields.emplace_back(fieldName, AZStd::move(fieldValue));
};
AZ::SettingsRegistryVisitorUtils::VisitField(*m_registry, visitorCallback, "/Test/Object");
const AZStd::fixed_vector<AZStd::pair<AZStd::string, AZStd::string>, VisitCallbackParams::MaxFieldCount> expectedFields{
visitParams.m_objectFields.begin(), visitParams.m_objectFields.end() };
EXPECT_THAT(testObjectFields, ::testing::ContainerEq(expectedFields));
}
TEST_P(SettingsRegistryVisitCallbackFixture, VisitFunction_VisitArrayOfArrayType_ReturnsFields)
{
const VisitCallbackParams& visitParams = GetParam();
ASSERT_TRUE(m_registry->MergeSettings(visitParams.m_inputJsonDocument, AZ::SettingsRegistryInterface::Format::JsonMergePatch));
AZStd::fixed_vector<AZStd::string, VisitCallbackParams::MaxFieldCount> testArrayFields;
auto visitorCallback = [this, &testArrayFields](AZStd::string_view path, AZStd::string_view, AZ::SettingsRegistryInterface::Type)
{
AZStd::string fieldValue;
EXPECT_TRUE(m_registry->Get(fieldValue, path));
testArrayFields.emplace_back(AZStd::move(fieldValue));
};
AZ::SettingsRegistryVisitorUtils::VisitArray(*m_registry, visitorCallback, "/Test/Array");
const AZStd::fixed_vector<AZStd::string, VisitCallbackParams::MaxFieldCount> expectedArrayFields{
visitParams.m_arrayFields.begin(), visitParams.m_arrayFields.end() };
EXPECT_THAT(testArrayFields, ::testing::ContainerEq(expectedArrayFields));
}
TEST_P(SettingsRegistryVisitCallbackFixture, VisitFunction_VisitArrayOfObjectType_ReturnsEmpty)
{
const VisitCallbackParams& visitParams = GetParam();
ASSERT_TRUE(m_registry->MergeSettings(visitParams.m_inputJsonDocument, AZ::SettingsRegistryInterface::Format::JsonMergePatch));
AZStd::fixed_vector<AZStd::string, VisitCallbackParams::MaxFieldCount> testArrayFields;
auto visitorCallback = [this, &testArrayFields](AZStd::string_view path, AZStd::string_view, AZ::SettingsRegistryInterface::Type)
{
AZStd::string fieldValue;
EXPECT_TRUE(m_registry->Get(fieldValue, path));
testArrayFields.emplace_back(AZStd::move(fieldValue));
};
AZ::SettingsRegistryVisitorUtils::VisitArray(*m_registry, visitorCallback, "/Test/Object");
EXPECT_TRUE(testArrayFields.empty());
}
TEST_P(SettingsRegistryVisitCallbackFixture, VisitFunction_VisitObjectOfArrayType_ReturnsEmpty)
{
const VisitCallbackParams& visitParams = GetParam();
ASSERT_TRUE(m_registry->MergeSettings(visitParams.m_inputJsonDocument, AZ::SettingsRegistryInterface::Format::JsonMergePatch));
AZStd::fixed_vector<AZStd::pair<AZStd::string, AZStd::string>, VisitCallbackParams::MaxFieldCount> testObjectFields;
auto visitorCallback = [this, &testObjectFields](AZStd::string_view path, AZStd::string_view fieldName, AZ::SettingsRegistryInterface::Type)
{
AZStd::string fieldValue;
EXPECT_TRUE(m_registry->Get(fieldValue, path));
testObjectFields.emplace_back(fieldName, AZStd::move(fieldValue));
};
AZ::SettingsRegistryVisitorUtils::VisitObject(*m_registry, visitorCallback, "/Test/Array");
EXPECT_TRUE(testObjectFields.empty());
}
TEST_P(SettingsRegistryVisitCallbackFixture, VisitFunction_VisitObjectOfObjectType_ReturnsFields)
{
const VisitCallbackParams& visitParams = GetParam();
ASSERT_TRUE(m_registry->MergeSettings(visitParams.m_inputJsonDocument, AZ::SettingsRegistryInterface::Format::JsonMergePatch));
AZStd::fixed_vector<AZStd::pair<AZStd::string, AZStd::string>, VisitCallbackParams::MaxFieldCount> testObjectFields;
auto visitorCallback = [this, &testObjectFields](AZStd::string_view path, AZStd::string_view fieldName, AZ::SettingsRegistryInterface::Type)
{
AZStd::string fieldValue;
EXPECT_TRUE(m_registry->Get(fieldValue, path));
testObjectFields.emplace_back(fieldName, AZStd::move(fieldValue));
};
AZ::SettingsRegistryVisitorUtils::VisitObject(*m_registry, visitorCallback, "/Test/Object");
const AZStd::fixed_vector<AZStd::pair<AZStd::string, AZStd::string>, VisitCallbackParams::MaxFieldCount> expectedObjectFields{
visitParams.m_objectFields.begin(), visitParams.m_objectFields.end() };
EXPECT_THAT(testObjectFields, ::testing::ContainerEq(expectedObjectFields));
}
INSTANTIATE_TEST_CASE_P(
VisitField,
SettingsRegistryVisitCallbackFixture,
::testing::Values(
VisitCallbackParams
{
R"({)" "\n"
R"( "Test":)" "\n"
R"( {)" "\n"
R"( "Array": [ "Hello", "World" ],)" "\n"
R"( "Object": { "Foo": "Hello", "Bar": "World"})" "\n"
R"( })" "\n"
R"(})" "\n",
VisitCallbackParams::ObjectFields{{"Foo", "Hello"}, {"Bar", "World"}},
VisitCallbackParams::ArrayFields{"Hello", "World"}
}
)
);
}
@@ -75,11 +75,12 @@ set(FILES
Name/NameJsonSerializerTests.cpp
Name/NameTests.cpp
RTTI/TypeSafeIntegralTests.cpp
SettingsRegistryTests.cpp
SettingsRegistryMergeUtilsTests.cpp
Settings/CommandLineTests.cpp
Settings/SettingsRegistryTests.cpp
Settings/SettingsRegistryConsoleUtilsTests.cpp
Settings/SettingsRegistryMergeUtilsTests.cpp
Settings/SettingsRegistryScriptUtilsTests.cpp
Settings/SettingsRegistryVisitorUtilsTests.cpp
Streamer/BlockCacheTests.cpp
Streamer/DedicatedCacheTests.cpp
Streamer/FullDecompressorTests.cpp
@@ -194,6 +195,7 @@ set(FILES
AZStd/LockFreeQueues.cpp
AZStd/LockFreeStacks.cpp
AZStd/LockTests.cpp
AZStd/Math.cpp
AZStd/Numeric.cpp
AZStd/Ordered.cpp
AZStd/Optional.cpp
@@ -29,6 +29,10 @@ namespace AzFramework
AZStd::vector<AZ::IO::Path> m_absoluteSourcePaths; //!< Where the gem's source path folder are located(as an absolute path)
static constexpr const char* GetGemAssetFolder() { return "Assets"; }
static constexpr const char* GetGemRegistryFolder()
{
return "Registry";
}
};
//! Returns a list of GemInfo of all the gems that are active for the for the specified game project.
@@ -20,6 +20,7 @@
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Settings/SettingsRegistry.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
@@ -190,6 +191,25 @@ namespace AzFramework
////////////////////////////////////////////////////////////////////////////////////////////////
void InputSystemComponent::Activate()
{
const auto* settingsRegistry = AZ::SettingsRegistry::Get();
if (settingsRegistry)
{
AZ::u64 value = 0;
if (settingsRegistry->Get(value, "/O3DE/InputSystem/MouseMovementSampleRateHertz"))
{
m_mouseMovementSampleRateHertz = aznumeric_caster(value);
}
if (settingsRegistry->Get(value, "/O3DE/InputSystem/GamepadsEnabled"))
{
m_gamepadsEnabled = aznumeric_caster(value);
}
settingsRegistry->Get(m_keyboardEnabled, "/O3DE/InputSystem/KeyboardEnabled");
settingsRegistry->Get(m_motionEnabled, "/O3DE/InputSystem/MotionEnabled");
settingsRegistry->Get(m_mouseEnabled, "/O3DE/InputSystem/MouseEnabled");
settingsRegistry->Get(m_touchEnabled, "/O3DE/InputSystem/TouchEnabled");
settingsRegistry->Get(m_virtualKeyboardEnabled, "/O3DE/InputSystem/VirtualKeyboardEnabled");
}
// Create all enabled input devices
CreateEnabledInputDevices();
@@ -9,6 +9,7 @@
#pragma once
#include <AzCore/RTTI/RTTI.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/string/string.h>
namespace AZ
@@ -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
@@ -533,8 +533,8 @@ namespace AzFramework
m_translateCameraInputChannelIds = translateCameraInputChannelIds;
}
PivotCameraInput::PivotCameraInput(const InputChannelId& pivotChannelId)
: m_pivotChannelId(pivotChannelId)
OrbitCameraInput::OrbitCameraInput(const InputChannelId& orbitChannelId)
: m_orbitChannelId(orbitChannelId)
{
m_pivotFn = []([[maybe_unused]] const AZ::Vector3& position, [[maybe_unused]] const AZ::Vector3& direction)
{
@@ -542,11 +542,11 @@ namespace AzFramework
};
}
bool PivotCameraInput::HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, const float scrollDelta)
bool OrbitCameraInput::HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, const float scrollDelta)
{
if (const auto* input = AZStd::get_if<DiscreteInputEvent>(&event))
{
if (input->m_channelId == m_pivotChannelId)
if (input->m_channelId == m_orbitChannelId)
{
if (input->m_state == InputChannel::State::Began)
{
@@ -561,13 +561,13 @@ namespace AzFramework
if (Active())
{
return m_pivotCameras.HandleEvents(event, cursorDelta, scrollDelta);
return m_orbitCameras.HandleEvents(event, cursorDelta, scrollDelta);
}
return !Idle();
}
Camera PivotCameraInput::StepCamera(
Camera OrbitCameraInput::StepCamera(
const Camera& targetCamera, const ScreenVector& cursorDelta, const float scrollDelta, const float deltaTime)
{
Camera nextCamera = targetCamera;
@@ -581,12 +581,12 @@ namespace AzFramework
if (Active())
{
MovePivotDetached(nextCamera, m_pivotFn(targetCamera.Translation(), targetCamera.Rotation().GetBasisY()));
nextCamera = m_pivotCameras.StepCamera(nextCamera, cursorDelta, scrollDelta, deltaTime);
nextCamera = m_orbitCameras.StepCamera(nextCamera, cursorDelta, scrollDelta, deltaTime);
}
if (Ending())
{
m_pivotCameras.Reset();
m_orbitCameras.Reset();
nextCamera.m_pivot = nextCamera.Translation();
nextCamera.m_offset = AZ::Vector3::CreateZero();
@@ -595,12 +595,12 @@ namespace AzFramework
return nextCamera;
}
void PivotCameraInput::SetPivotInputChannelId(const InputChannelId& pivotChanneId)
void OrbitCameraInput::SetOrbitInputChannelId(const InputChannelId& orbitChanneId)
{
m_pivotChannelId = pivotChanneId;
m_orbitChannelId = orbitChanneId;
}
PivotDollyScrollCameraInput::PivotDollyScrollCameraInput()
OrbitDollyScrollCameraInput::OrbitDollyScrollCameraInput()
{
m_scrollSpeedFn = []() constexpr
{
@@ -608,7 +608,7 @@ namespace AzFramework
};
}
bool PivotDollyScrollCameraInput::HandleEvents(
bool OrbitDollyScrollCameraInput::HandleEvents(
const InputEvent& event, [[maybe_unused]] const ScreenVector& cursorDelta, [[maybe_unused]] const float scrollDelta)
{
if (const auto* scroll = AZStd::get_if<ScrollEvent>(&event))
@@ -619,7 +619,7 @@ namespace AzFramework
return !Idle();
}
static Camera PivotDolly(const Camera& targetCamera, const float delta)
static Camera OrbitDolly(const Camera& targetCamera, const float delta)
{
Camera nextCamera = targetCamera;
@@ -646,18 +646,18 @@ namespace AzFramework
return nextCamera;
}
Camera PivotDollyScrollCameraInput::StepCamera(
Camera OrbitDollyScrollCameraInput::StepCamera(
const Camera& targetCamera,
[[maybe_unused]] const ScreenVector& cursorDelta,
const float scrollDelta,
[[maybe_unused]] const float deltaTime)
{
const auto nextCamera = PivotDolly(targetCamera, aznumeric_cast<float>(scrollDelta) * m_scrollSpeedFn());
const auto nextCamera = OrbitDolly(targetCamera, aznumeric_cast<float>(scrollDelta) * m_scrollSpeedFn());
EndActivation();
return nextCamera;
}
PivotDollyMotionCameraInput::PivotDollyMotionCameraInput(const InputChannelId& dollyChannelId)
OrbitDollyMotionCameraInput::OrbitDollyMotionCameraInput(const InputChannelId& dollyChannelId)
: m_dollyChannelId(dollyChannelId)
{
m_motionSpeedFn = []() constexpr
@@ -666,28 +666,28 @@ namespace AzFramework
};
}
bool PivotDollyMotionCameraInput::HandleEvents(
bool OrbitDollyMotionCameraInput::HandleEvents(
const InputEvent& event, [[maybe_unused]] const ScreenVector& cursorDelta, [[maybe_unused]] const float scrollDelta)
{
HandleActivationEvents(event, m_dollyChannelId, cursorDelta, m_clickDetector, *this);
return CameraInputUpdatingAfterMotion(*this);
}
Camera PivotDollyMotionCameraInput::StepCamera(
Camera OrbitDollyMotionCameraInput::StepCamera(
const Camera& targetCamera,
const ScreenVector& cursorDelta,
[[maybe_unused]] const float scrollDelta,
[[maybe_unused]] const float deltaTime)
{
return PivotDolly(targetCamera, aznumeric_cast<float>(cursorDelta.m_y) * m_motionSpeedFn());
return OrbitDolly(targetCamera, aznumeric_cast<float>(cursorDelta.m_y) * m_motionSpeedFn());
}
void PivotDollyMotionCameraInput::SetDollyInputChannelId(const InputChannelId& dollyChannelId)
void OrbitDollyMotionCameraInput::SetDollyInputChannelId(const InputChannelId& dollyChannelId)
{
m_dollyChannelId = dollyChannelId;
}
ScrollTranslationCameraInput::ScrollTranslationCameraInput()
LookScrollTranslationCameraInput::LookScrollTranslationCameraInput()
{
m_scrollSpeedFn = []() constexpr
{
@@ -695,7 +695,7 @@ namespace AzFramework
};
}
bool ScrollTranslationCameraInput::HandleEvents(
bool LookScrollTranslationCameraInput::HandleEvents(
const InputEvent& event, [[maybe_unused]] const ScreenVector& cursorDelta, [[maybe_unused]] const float scrollDelta)
{
if (const auto* scroll = AZStd::get_if<ScrollEvent>(&event))
@@ -706,7 +706,7 @@ namespace AzFramework
return !Idle();
}
Camera ScrollTranslationCameraInput::StepCamera(
Camera LookScrollTranslationCameraInput::StepCamera(
const Camera& targetCamera,
[[maybe_unused]] const ScreenVector& cursorDelta,
const float scrollDelta,
@@ -30,8 +30,11 @@ namespace AzFramework
AZ::Vector3 EulerAngles(const AZ::Matrix3x3& orientation);
//! A simple camera representation using spherical coordinates as input (pitch, yaw, pivot and offset).
//! The cameras transform and view can be obtained through accessor functions that use the internal
//! The camera's transform and view can be obtained through accessor functions that use the internal
//! spherical coordinates to calculate the position and orientation.
//! @note Modifying m_pivot directly and leaving m_offset as zero will produce a free look camera effect, giving
//! m_offset a value (e.g. in negative Y only) will produce an orbit camera effect, modifying X and Z of m_offset
//! will further alter the camera translation in relation to m_pivot so it appears off center.
struct Camera
{
AZ::Vector3 m_pivot = AZ::Vector3::CreateZero(); //!< Pivot point to rotate about (modified in world space).
@@ -291,7 +294,7 @@ namespace AzFramework
Cameras m_cameras; //!< Represents a collection of camera inputs that together provide a camera controller.
private:
ScreenVector m_motionDelta; //!< The delta used for look/pivot/pan (rotation + translation) - two dimensional.
ScreenVector m_motionDelta; //!< The delta used for look/orbit/pan (rotation + translation) - two dimensional.
CursorState m_cursorState; //!< The current and previous position of the cursor (used to calculate movement delta).
float m_scrollDelta = 0.0f; //!< The delta used for dolly/movement (translation) - one dimensional.
bool m_handlingEvents = false; //!< Is the camera system currently handling events (events are consumed and not propagated).
@@ -316,7 +319,7 @@ namespace AzFramework
return AZStd::fmod(yaw + AZ::Constants::TwoPi, AZ::Constants::TwoPi);
}
//! A camera input to handle motion deltas that can rotate or pivot the camera.
//! A camera input to handle motion deltas that can change the orientation of the camera (update pitch and yaw).
class RotateCameraInput : public CameraInput
{
public:
@@ -348,15 +351,16 @@ namespace AzFramework
//! PanAxes build function that will return a pair of pan axes depending on the camera orientation.
using PanAxesFn = AZStd::function<PanAxes(const Camera& camera)>;
//! PanAxes to use while in 'look' camera behavior (free look).
//! PanAxes to use while in 'look' or 'orbit' camera behavior.
inline PanAxes LookPan(const Camera& camera)
{
const AZ::Matrix3x3 orientation = camera.Rotation();
return { orientation.GetBasisX(), orientation.GetBasisZ() };
}
//! PanAxes to use while in 'pivot' camera behavior.
inline PanAxes PivotPan(const Camera& camera)
//! Optional PanAxes to use while in 'orbit' camera behavior.
//! @note This will move the camera in the local X/Y plane instead of usual X/Z plane.
inline PanAxes OrbitPan(const Camera& camera)
{
const AZ::Matrix3x3 orientation = camera.Rotation();
@@ -370,14 +374,23 @@ namespace AzFramework
return { basisX, basisY };
}
//! TranslationDeltaFn is used by PanCameraInput and TranslateCameraInput
//! @note Choose the appropriate function if the behavior should be operating as a free look camera (TranslatePivotLook)
//! or an orbit camera (TranslateOffsetOrbit).
using TranslationDeltaFn = AZStd::function<void(Camera& camera, const AZ::Vector3& delta)>;
inline void TranslatePivot(Camera& camera, const AZ::Vector3& delta)
//! Update the pivot camera position.
//! @note delta will need to have been transformed to world space, e.g. To move the camera right, (1, 0, 0) must
//! first be transformed by the orientation of the camera before being applied to m_pivot.
inline void TranslatePivotLook(Camera& camera, const AZ::Vector3& delta)
{
camera.m_pivot += delta;
}
inline void TranslateOffset(Camera& camera, const AZ::Vector3& delta)
//! Update the offset camera position.
//! @note delta still needs to be transformed to world space (as with TranslatePivotLook) but internally this is undone
//! to be performed in local space when being applied to m_offset.
inline void TranslateOffsetOrbit(Camera& camera, const AZ::Vector3& delta)
{
camera.m_offset += camera.View().TransformVector(delta);
}
@@ -409,7 +422,7 @@ namespace AzFramework
//! Axes to use while translating the camera.
using TranslationAxesFn = AZStd::function<AZ::Matrix3x3(const Camera& camera)>;
//! TranslationAxes to use while in 'look' camera behavior (free look).
//! TranslationAxes to use while in 'look' or 'orbit' camera behavior.
inline AZ::Matrix3x3 LookTranslation(const Camera& camera)
{
const AZ::Matrix3x3 orientation = camera.Rotation();
@@ -421,8 +434,8 @@ namespace AzFramework
return AZ::Matrix3x3::CreateFromColumns(basisX, basisY, basisZ);
}
//! TranslationAxes to use while in 'pivot' camera behavior.
inline AZ::Matrix3x3 PivotTranslation(const Camera& camera)
//! Optional TranslationAxes to use while in 'orbit' camera behavior.
inline AZ::Matrix3x3 OrbitTranslation(const Camera& camera)
{
const AZ::Matrix3x3 orientation = camera.Rotation();
@@ -535,11 +548,11 @@ namespace AzFramework
bool m_boost = false; //!< Is the translation speed currently being multiplied/scaled upwards.
};
//! A camera input to handle discrete scroll events that can modify the camera pivot distance.
class PivotDollyScrollCameraInput : public CameraInput
//! A camera input to handle discrete scroll events that can modify the camera offset.
class OrbitDollyScrollCameraInput : public CameraInput
{
public:
PivotDollyScrollCameraInput();
OrbitDollyScrollCameraInput();
// CameraInput overrides ...
bool HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
@@ -548,11 +561,11 @@ namespace AzFramework
AZStd::function<float()> m_scrollSpeedFn;
};
//! A camera input to handle motion deltas that can modify the camera pivot distance.
class PivotDollyMotionCameraInput : public CameraInput
//! A camera input to handle motion deltas that can modify the camera offset.
class OrbitDollyMotionCameraInput : public CameraInput
{
public:
explicit PivotDollyMotionCameraInput(const InputChannelId& dollyChannelId);
explicit OrbitDollyMotionCameraInput(const InputChannelId& dollyChannelId);
// CameraInput overrides ...
bool HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
@@ -569,10 +582,10 @@ namespace AzFramework
};
//! A camera input to handle discrete scroll events that can scroll (translate) the camera along its forward axis.
class ScrollTranslationCameraInput : public CameraInput
class LookScrollTranslationCameraInput : public CameraInput
{
public:
ScrollTranslationCameraInput();
LookScrollTranslationCameraInput();
// CameraInput overrides ...
bool HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
@@ -583,36 +596,36 @@ namespace AzFramework
//! A camera input that doubles as its own set of camera inputs.
//! It is 'exclusive', so does not overlap with other sibling camera inputs - it runs its own set of camera inputs as 'children'.
class PivotCameraInput : public CameraInput
class OrbitCameraInput : public CameraInput
{
public:
using PivotFn = AZStd::function<AZ::Vector3(const AZ::Vector3& position, const AZ::Vector3& direction)>;
explicit PivotCameraInput(const InputChannelId& pivotChannelId);
explicit OrbitCameraInput(const InputChannelId& orbitChannelId);
// CameraInput overrides ...
bool HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
bool Exclusive() const override;
void SetPivotInputChannelId(const InputChannelId& pivotChanneId);
void SetOrbitInputChannelId(const InputChannelId& orbitChanneId);
Cameras m_pivotCameras; //!< The camera inputs to run when this camera input is active (only these will run as it is exclusive).
Cameras m_orbitCameras; //!< The camera inputs to run when this camera input is active (only these will run as it is exclusive).
//! Override the default behavior for how a pivot point is calculated.
void SetPivotFn(PivotFn pivotFn);
private:
InputChannelId m_pivotChannelId; //!< Input channel to begin the pivot camera input.
PivotFn m_pivotFn; //!< The pivot position to use for this pivot camera (how is the pivot point calculated/retrieved).
InputChannelId m_orbitChannelId; //!< Input channel to begin the orbit camera input.
PivotFn m_pivotFn; //!< The pivot position to use for this orbit camera (how is the pivot point calculated/retrieved).
};
inline void PivotCameraInput::SetPivotFn(PivotFn pivotFn)
inline void OrbitCameraInput::SetPivotFn(PivotFn pivotFn)
{
m_pivotFn = AZStd::move(pivotFn);
}
inline bool PivotCameraInput::Exclusive() const
inline bool OrbitCameraInput::Exclusive() const
{
return true;
}
@@ -624,9 +637,9 @@ namespace AzFramework
return AZ::Vector3::CreateZero();
}
//! Callback to use for FocusCameraInput when a pivot camera is being used.
//! Callback to use for FocusCameraInput when a orbit camera is being used.
//! @note This is when offset is non zero.
inline AZ::Vector3 FocusPivot(const float length)
inline AZ::Vector3 FocusOrbit(const float length)
{
return AZ::Vector3::CreateAxisY(-length);
}
@@ -667,7 +680,9 @@ namespace AzFramework
bool HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
//! HandleEvents delegates directly to m_handleEventsFn.
AZStd::function<bool(CameraInput&, const InputEvent&, const ScreenVector&, float)> m_handleEventsFn;
//! StepCamera delegates directly to m_stepCameraFn.
AZStd::function<Camera(CameraInput&, const Camera&, const ScreenVector&, float, float)> m_stepCameraFn;
};
@@ -10,6 +10,7 @@
#include <AzFramework/Windowing/NativeWindow.h>
#include <AzFramework/XcbNativeWindow.h>
#include <AzFramework/XcbConnectionManager.h>
#include <AzFramework/XcbInterface.h>
#include <xcb/xcb.h>
@@ -112,9 +112,10 @@ namespace AzFramework
void ApplicationIos::PumpSystemEventLoopUntilEmpty()
{
SInt32 result;
const CFTimeInterval MaxSecondsInRunLoop = 0.001; // One millisecond
do
{
result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, DBL_EPSILON, TRUE);
result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, MaxSecondsInRunLoop, TRUE);
}
while (result == kCFRunLoopRunHandledSource);
}
@@ -53,31 +53,31 @@ namespace UnitTest
};
m_firstPersonTranslateCamera = AZStd::make_shared<AzFramework::TranslateCameraInput>(
m_translateCameraInputChannelIds, AzFramework::LookTranslation, AzFramework::TranslatePivot);
m_translateCameraInputChannelIds, AzFramework::LookTranslation, AzFramework::TranslatePivotLook);
m_pivotCamera = AZStd::make_shared<AzFramework::PivotCameraInput>(m_pivotChannelId);
m_pivotCamera->SetPivotFn(
m_orbitCamera = AZStd::make_shared<AzFramework::OrbitCameraInput>(m_orbitChannelId);
m_orbitCamera->SetPivotFn(
[this](const AZ::Vector3&, const AZ::Vector3&)
{
return m_pivot;
});
auto pivotRotateCamera = AZStd::make_shared<AzFramework::RotateCameraInput>(AzFramework::InputDeviceMouse::Button::Left);
auto orbitRotateCamera = AZStd::make_shared<AzFramework::RotateCameraInput>(AzFramework::InputDeviceMouse::Button::Left);
// set rotate speed to be a value that will scale motion delta (pixels moved) by a thousandth.
pivotRotateCamera->m_rotateSpeedFn = []()
orbitRotateCamera->m_rotateSpeedFn = []()
{
return 0.001f;
};
auto pivotTranslateCamera = AZStd::make_shared<AzFramework::TranslateCameraInput>(
m_translateCameraInputChannelIds, AzFramework::PivotTranslation, AzFramework::TranslateOffset);
auto orbitTranslateCamera = AZStd::make_shared<AzFramework::TranslateCameraInput>(
m_translateCameraInputChannelIds, AzFramework::OrbitTranslation, AzFramework::TranslateOffsetOrbit);
m_pivotCamera->m_pivotCameras.AddCamera(pivotRotateCamera);
m_pivotCamera->m_pivotCameras.AddCamera(pivotTranslateCamera);
m_orbitCamera->m_orbitCameras.AddCamera(orbitRotateCamera);
m_orbitCamera->m_orbitCameras.AddCamera(orbitTranslateCamera);
m_cameraSystem->m_cameras.AddCamera(m_firstPersonRotateCamera);
m_cameraSystem->m_cameras.AddCamera(m_firstPersonTranslateCamera);
m_cameraSystem->m_cameras.AddCamera(m_pivotCamera);
m_cameraSystem->m_cameras.AddCamera(m_orbitCamera);
// these tests rely on using motion delta, not cursor positions (default is true)
AzFramework::ed_cameraSystemUseCursor = false;
@@ -87,7 +87,7 @@ namespace UnitTest
{
AzFramework::ed_cameraSystemUseCursor = true;
m_pivotCamera.reset();
m_orbitCamera.reset();
m_firstPersonRotateCamera.reset();
m_firstPersonTranslateCamera.reset();
@@ -97,11 +97,11 @@ namespace UnitTest
AllocatorsTestFixture::TearDown();
}
AzFramework::InputChannelId m_pivotChannelId = AzFramework::InputChannelId("keyboard_key_modifier_alt_l");
AzFramework::InputChannelId m_orbitChannelId = AzFramework::InputChannelId("keyboard_key_modifier_alt_l");
AzFramework::TranslateCameraInputChannelIds m_translateCameraInputChannelIds;
AZStd::shared_ptr<AzFramework::RotateCameraInput> m_firstPersonRotateCamera;
AZStd::shared_ptr<AzFramework::TranslateCameraInput> m_firstPersonTranslateCamera;
AZStd::shared_ptr<AzFramework::PivotCameraInput> m_pivotCamera;
AZStd::shared_ptr<AzFramework::OrbitCameraInput> m_orbitCamera;
AZ::Vector3 m_pivot = AZ::Vector3::CreateZero();
//! This is approximately Pi/2 * 1000 - this can be used to rotate the camera 90 degrees (pitch or yaw based
@@ -109,17 +109,17 @@ namespace UnitTest
inline static const int PixelMotionDelta = 1570;
};
TEST_F(CameraInputFixture, BeginAndEndPivotCameraInputConsumesCorrectEvents)
TEST_F(CameraInputFixture, BeginAndEndOrbitCameraInputConsumesCorrectEvents)
{
// begin pivot camera
// begin orbit camera
const bool consumed1 = HandleEventAndUpdate(AzFramework::DiscreteInputEvent{ AzFramework::InputDeviceKeyboard::Key::ModifierAltL,
AzFramework::InputChannel::State::Began });
// begin listening for pivot rotate (click detector) - event is not consumed
// begin listening for orbit rotate (click detector) - event is not consumed
const bool consumed2 = HandleEventAndUpdate(
AzFramework::DiscreteInputEvent{ AzFramework::InputDeviceMouse::Button::Left, AzFramework::InputChannel::State::Began });
// begin pivot rotate (mouse has moved sufficient distance to initiate)
// begin orbit rotate (mouse has moved sufficient distance to initiate)
const bool consumed3 = HandleEventAndUpdate(AzFramework::HorizontalMotionEvent{ 5 });
// end pivot (mouse up) - event is not consumed
// end orbit (mouse up) - event is not consumed
const bool consumed4 = HandleEventAndUpdate(
AzFramework::DiscreteInputEvent{ AzFramework::InputDeviceMouse::Button::Left, AzFramework::InputChannel::State::Ended });
@@ -260,10 +260,10 @@ namespace UnitTest
EXPECT_TRUE(activationEnded);
}
TEST_F(CameraInputFixture, PivotCameraInputHandlesLookAtPointAndSelfAtSamePositionWhenPivoting)
TEST_F(CameraInputFixture, OrbitCameraInputHandlesLookAtPointAndSelfAtSamePositionWhenOrbiting)
{
// create pathological lookAtFn that just returns the same position as the camera
m_pivotCamera->SetPivotFn(
m_orbitCamera->SetPivotFn(
[](const AZ::Vector3& position, [[maybe_unused]] const AZ::Vector3& direction)
{
return position;
@@ -275,7 +275,7 @@ namespace UnitTest
AZ::Transform::CreateFromQuaternionAndTranslation(
AZ::Quaternion::CreateFromEulerAnglesDegrees(AZ::Vector3(0.0f, 0.0f, 90.0f)), expectedCameraPosition));
HandleEventAndUpdate(AzFramework::DiscreteInputEvent{ m_pivotChannelId, AzFramework::InputChannel::State::Began });
HandleEventAndUpdate(AzFramework::DiscreteInputEvent{ m_orbitChannelId, AzFramework::InputChannel::State::Began });
// verify the camera yaw has not changed and pivot point matches the expected camera position
using ::testing::FloatNear;
@@ -321,14 +321,14 @@ namespace UnitTest
EXPECT_THAT(m_camera.m_offset, IsClose(AZ::Vector3::CreateZero()));
}
TEST_F(CameraInputFixture, PivotRotateCameraInputRotatesPitchOffsetByNinetyDegreesWithRequiredPixelDelta)
TEST_F(CameraInputFixture, OrbitRotateCameraInputRotatesPitchOffsetByNinetyDegreesWithRequiredPixelDelta)
{
const auto cameraStartingPosition = AZ::Vector3::CreateAxisY(-20.0f);
m_targetCamera.m_pivot = cameraStartingPosition;
m_pivot = AZ::Vector3::CreateAxisY(-10.0f);
HandleEventAndUpdate(AzFramework::DiscreteInputEvent{ m_pivotChannelId, AzFramework::InputChannel::State::Began });
HandleEventAndUpdate(AzFramework::DiscreteInputEvent{ m_orbitChannelId, AzFramework::InputChannel::State::Began });
HandleEventAndUpdate(
AzFramework::DiscreteInputEvent{ AzFramework::InputDeviceMouse::Button::Left, AzFramework::InputChannel::State::Began });
HandleEventAndUpdate(AzFramework::VerticalMotionEvent{ PixelMotionDelta });
@@ -344,14 +344,14 @@ namespace UnitTest
EXPECT_THAT(m_camera.Translation(), IsCloseTolerance(expectedCameraEndingPosition, 0.01f));
}
TEST_F(CameraInputFixture, PivotRotateCameraInputRotatesYawOffsetByNinetyDegreesWithRequiredPixelDelta)
TEST_F(CameraInputFixture, OrbitRotateCameraInputRotatesYawOffsetByNinetyDegreesWithRequiredPixelDelta)
{
const auto cameraStartingPosition = AZ::Vector3(15.0f, -20.0f, 0.0f);
m_targetCamera.m_pivot = cameraStartingPosition;
m_pivot = AZ::Vector3(10.0f, -10.0f, 0.0f);
HandleEventAndUpdate(AzFramework::DiscreteInputEvent{ m_pivotChannelId, AzFramework::InputChannel::State::Began });
HandleEventAndUpdate(AzFramework::DiscreteInputEvent{ m_orbitChannelId, AzFramework::InputChannel::State::Began });
HandleEventAndUpdate(
AzFramework::DiscreteInputEvent{ AzFramework::InputDeviceMouse::Button::Left, AzFramework::InputChannel::State::Began });
HandleEventAndUpdate(AzFramework::HorizontalMotionEvent{ -PixelMotionDelta });
@@ -19,6 +19,7 @@
#include "Matchers.h"
#include "Actions.h"
#include "XcbBaseTestFixture.h"
#include "XcbTestApplication.h"
template<typename T>
xcb_generic_event_t MakeEvent(T event)
@@ -32,6 +33,7 @@ namespace AzFramework
class XcbInputDeviceKeyboardTests
: public XcbBaseTestFixture
{
public:
void SetUp() override
{
using testing::Return;
@@ -122,6 +124,15 @@ namespace AzFramework
static constexpr xcb_keycode_t s_keycodeForAKey{38};
static constexpr xcb_keycode_t s_keycodeForShiftLKey{50};
XcbTestApplication m_application{
/*enabledGamepadsCount=*/0,
/*keyboardEnabled=*/true,
/*motionEnabled=*/false,
/*mouseEnabled=*/false,
/*touchEnabled=*/false,
/*virtualKeyboardEnabled=*/false
};
};
class InputTextNotificationListener
@@ -194,26 +205,23 @@ namespace AzFramework
EXPECT_CALL(m_interface, xkb_state_key_get_one_sym(&m_xkbState, s_keycodeForAKey))
.Times(2);
Application application;
application.Start({}, {});
m_application.Start();
const InputChannel* inputChannel = InputChannelRequests::FindInputChannel(InputDeviceKeyboard::Key::AlphanumericA);
ASSERT_TRUE(inputChannel);
EXPECT_THAT(inputChannel->GetState(), Eq(InputChannel::State::Idle));
application.PumpSystemEventLoopUntilEmpty();
application.TickSystem();
application.Tick();
m_application.PumpSystemEventLoopUntilEmpty();
m_application.TickSystem();
m_application.Tick();
EXPECT_THAT(inputChannel->GetState(), Eq(InputChannel::State::Began));
application.PumpSystemEventLoopUntilEmpty();
application.TickSystem();
application.Tick();
m_application.PumpSystemEventLoopUntilEmpty();
m_application.TickSystem();
m_application.Tick();
EXPECT_THAT(inputChannel->GetState(), Eq(InputChannel::State::Ended));
application.Stop();
}
TEST_F(XcbInputDeviceKeyboardTests, TextEnteredFromXcbKeyPressEvents)
@@ -418,16 +426,13 @@ namespace AzFramework
EXPECT_CALL(textListener, OnInputTextEvent(StrEq("a"), _)).Times(1);
EXPECT_CALL(textListener, OnInputTextEvent(StrEq("A"), _)).Times(1);
Application application;
application.Start({}, {});
m_application.Start();
for (int i = 0; i < 4; ++i)
{
application.PumpSystemEventLoopUntilEmpty();
application.TickSystem();
application.Tick();
m_application.PumpSystemEventLoopUntilEmpty();
m_application.TickSystem();
m_application.Tick();
}
application.Stop();
}
} // namespace AzFramework
@@ -0,0 +1,38 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/Settings/SettingsRegistry.h>
#include <AzCore/UserSettings/UserSettingsComponent.h>
#include <AzFramework/Application/Application.h>
namespace AzFramework
{
class XcbTestApplication
: public Application
{
public:
XcbTestApplication(AZ::u64 enabledGamepadsCount, bool keyboardEnabled, bool motionEnabled, bool mouseEnabled, bool touchEnabled, bool virtualKeyboardEnabled)
{
auto* settingsRegistry = AZ::SettingsRegistry::Get();
settingsRegistry->Set("/O3DE/InputSystem/GamepadsEnabled", enabledGamepadsCount);
settingsRegistry->Set("/O3DE/InputSystem/KeyboardEnabled", keyboardEnabled);
settingsRegistry->Set("/O3DE/InputSystem/MotionEnabled", motionEnabled);
settingsRegistry->Set("/O3DE/InputSystem/MouseEnabled", mouseEnabled);
settingsRegistry->Set("/O3DE/InputSystem/TouchEnabled", touchEnabled);
settingsRegistry->Set("/O3DE/InputSystem/VirtualKeyboardEnabled", virtualKeyboardEnabled);
}
void Start(const Descriptor& descriptor = {}, const StartupParameters& startupParameters = {}) override
{
Application::Start(descriptor, startupParameters);
AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize);
}
};
} // namespace AzFramework
@@ -17,4 +17,5 @@ set(FILES
XcbBaseTestFixture.cpp
XcbBaseTestFixture.h
XcbInputDeviceKeyboardTests.cpp
XcbTestApplication.h
)
@@ -96,6 +96,8 @@ namespace AzGameFramework
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_CommandLine(registry, m_commandLine, false);
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_ProjectUserRegistry(registry, AZ_TRAIT_OS_PLATFORM_CODENAME, specializations, &scratchBuffer);
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_CommandLine(registry, m_commandLine, true);
#else
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_CommandLine(registry, m_commandLine, false);
#endif
// Update the Runtime file paths in case the "{BootstrapSettingsRootKey}/assets" key was overriden by a setting registry
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(registry);
@@ -33,6 +33,9 @@ namespace AzQtComponents
titleLabel->setObjectName("Title");
titleLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
titleLabel->setWordWrap(true);
titleLabel->setTextFormat(Qt::RichText);
titleLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
titleLabel->setOpenExternalLinks(true);
QHBoxLayout* headerLayout = new QHBoxLayout(headerFrame);
headerLayout->setSizeConstraint(QLayout::SetMinimumSize);
@@ -6,10 +6,11 @@
*
*/
#include <AzQtComponents/AzQtComponents_Traits_Platform.h>
#include <AzQtComponents/Components/Widgets/FileDialog.h>
#include <QMessageBox>
#include <QRegExp>
#include <QRegularExpression>
namespace AzQtComponents
{
@@ -24,7 +25,12 @@ namespace AzQtComponents
// Trigger Qt's save filename dialog
// If filePath isn't empty, it means we are prompting again because the filename was invalid,
// so pass it instead of the directory so the filename is pre-filled in for the user
filePath = QFileDialog::getSaveFileName(parent, caption, (filePath.isEmpty()) ? dir : filePath, filter, selectedFilter, options);
QString localSelectedFilter;
filePath = QFileDialog::getSaveFileName(parent, caption, (filePath.isEmpty()) ? dir : filePath, filter, &localSelectedFilter, options);
if (selectedFilter)
{
*selectedFilter = localSelectedFilter;
}
if (!filePath.isEmpty())
{
@@ -32,15 +38,39 @@ namespace AzQtComponents
QString fileName = fileInfo.fileName();
// Check if the filename has any invalid characters
QRegExp validFileNameRegex("^[a-zA-Z0-9_\\-./]*$");
shouldPromptAgain = !validFileNameRegex.exactMatch(fileName);
QRegularExpression validFileNameRegex("^[a-zA-Z0-9_\\-./]*$");
QRegularExpressionMatch validFileNameMatch = validFileNameRegex.match(fileName);
// If the filename had invalid characters, then show a warning message and then we will re-prompt the save filename dialog
if (shouldPromptAgain)
if (!validFileNameMatch.hasMatch())
{
QMessageBox::warning(parent, QObject::tr("Invalid filename"),
QObject::tr("O3DE assets are restricted to alphanumeric characters, hyphens (-), underscores (_), and dots (.)\n\n%1").arg(fileName));
shouldPromptAgain = true;
continue;
}
else
{
shouldPromptAgain = false;
}
#if AZ_TRAIT_AZQTCOMPONENTS_FILE_DIALOG_APPLY_MISSING_EXTENSION
// If a filter was selected, then make sure that the resulting filename ends with that extension. On systems that use the default QFileDialog,
// the extension is not guaranteed to be set in the resulting filename
if (FileDialog::ApplyMissingExtension(localSelectedFilter, filePath))
{
// If an extension had to be applied, then the file dialog did not handle the case of overwriting existing files.
// We need to check that condition before we proceed
QFileInfo updatedFilePath(filePath);
if (updatedFilePath.exists())
{
QMessageBox::StandardButton overwriteSelection = QMessageBox::question(parent,
QObject::tr("File exists"),
QObject::tr("%1 exists. Do you want to overwrite the existing file?").arg(updatedFilePath.fileName()));
shouldPromptAgain = (overwriteSelection == QMessageBox::No);
}
}
#endif // AZ_TRAIT_AZQTCOMPONENTS_FILE_DIALOG_APPLY_MISSING_EXTENSION
}
else
{
@@ -51,4 +81,56 @@ namespace AzQtComponents
return filePath;
}
bool FileDialog::ApplyMissingExtension(const QString& selectedFilter, QString& filePath)
{
if (selectedFilter.isEmpty())
{
return false;
}
// According to the QT documentation for QFileDialog, the selected filter will come in the form
// <Filter Name> (<filter pattern1> <filter pattern2> .. <filter patternN> )
//
// For example:
// "Images (*.gif *.png *.jpg)"
//
// Extract the contents of the <filter pattern>(s) inside the parenthesis and split them based on a whitespace or comma
const QRegularExpression filterContent(".*\\((?<filters>[^\\)]+)\\)");
QRegularExpressionMatch filterContentMatch = filterContent.match(selectedFilter);
if (!filterContentMatch.hasMatch())
{
return false;
}
QString filterExtensionsString = filterContentMatch.captured("filters");
QStringList filterExtensionsFull = filterExtensionsString.split(" ", Qt::SkipEmptyParts);
if (filterExtensionsFull.length() <= 0)
{
return false;
}
// If there are multiple suffixes in the selected filter, then default to the first one if a suffix needs to be appended
QString defaultSuffix = filterExtensionsFull[0].mid(1);
// Iterate through the filter patterns to see if the current filename matches
QFileInfo fileInfo(filePath);
bool extensionNeeded = true;
for (const QString& filterExtensionFull : filterExtensionsFull)
{
QString wildcardExpression = QRegularExpression::wildcardToRegularExpression(filterExtensionFull);
QRegularExpression filterPattern(wildcardExpression, AZ_TRAIT_AZQTCOMPONENTS_FILE_DIALOG_FILTER_CASE_SENSITIVITY);
QRegularExpressionMatch filterPatternMatch = filterPattern.match(fileInfo.fileName());
if (filterPatternMatch.hasMatch())
{
// The filename matches one of the filter patterns already, the extension does not need to be added to the filename
extensionNeeded = false;
}
}
if (extensionNeeded)
{
// If the current (if any) suffix does not match, automatically add the default suffix for the selected filter
filePath.append(defaultSuffix);
}
return extensionNeeded;
}
} // namespace AzQtComponents
@@ -24,6 +24,12 @@ namespace AzQtComponents
static QString GetSaveFileName(QWidget* parent = nullptr, const QString& caption = QString(),
const QString& dir = QString(), const QString& filter = QString(),
QString* selectedFilter = nullptr, QFileDialog::Options options = QFileDialog::Options());
//! Helper method that parses a selected filter from Qt's QFileDialog::getSaveFileName and applies the
//! selected filter's extension to the filePath if it doesnt already have the extension. This is needed
//! on platforms that do not have a default file dialog (These platforms uses Qt's custom file dialog which will
//! not apply the filter's extension automatically on user entered filenames)
static bool ApplyMissingExtension(const QString& selectedFilter, QString& filePath);
};
} // namespace AzQtComponents
@@ -12,4 +12,7 @@ set(FILES
../../Utilities/QtWindowUtilities_linux.cpp
../../Utilities/ScreenGrabber_linux.cpp
../../../Platform/Linux/AzQtComponents/Components/StyledDockWidget_Linux.cpp
../../../Platform/Linux/AzQtComponents/Utilities/DesktopUtilities_Linux.cpp
../../../Platform/Linux/AzQtComponents/AzQtComponents_Traits_Linux.h
../../../Platform/Linux/AzQtComponents/AzQtComponents_Traits_Platform.h
)
@@ -12,4 +12,7 @@ set(FILES
../../Utilities/QtWindowUtilities_mac.mm
../../Utilities/ScreenGrabber_mac.mm
../../../Platform/Mac/AzQtComponents/Components/StyledDockWidget_Mac.cpp
../../../Platform/Mac/AzQtComponents/Utilities/DesktopUtilities_Mac.cpp
../../../Platform/Mac/AzQtComponents/AzQtComponents_Traits_Mac.h
../../../Platform/Mac/AzQtComponents/AzQtComponents_Traits_Platform.h
)
@@ -9,6 +9,7 @@
set(FILES
../../natvis/qt.natvis
../../../Platform/Windows/AzQtComponents/Utilities/HandleDpiAwareness_Windows.cpp
../../../Platform/Windows/AzQtComponents/Utilities/DesktopUtilities_Windows.cpp
../../Utilities/MouseHider_win.cpp
../../Utilities/QtWindowUtilities_win.cpp
../../Utilities/ScreenGrabber_win.cpp
@@ -17,4 +18,6 @@ set(FILES
../../Components/TitleBarOverdrawScreenHandler_win.h
../../Components/TitleBarOverdrawScreenHandler_win.cpp
../../../Platform/Windows/AzQtComponents/Components/StyledDockWidget_Windows.cpp
../../../Platform/Windows/AzQtComponents/AzQtComponents_Traits_Windows.h
../../../Platform/Windows/AzQtComponents/AzQtComponents_Traits_Platform.h
)
@@ -0,0 +1,65 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzTest/AzTest.h>
#include <AzQtComponents/Components/Widgets/FileDialog.h>
#include <QString>
TEST(AzQtComponents, ApplyMissingExtension_UpdateMissingExtension_Success)
{
const QString textFiler{"Text Files (*.txt)"};
QString testPath{"testFile"};
bool result = AzQtComponents::FileDialog::ApplyMissingExtension(textFiler, testPath);
EXPECT_TRUE(result);
EXPECT_STRCASEEQ("testFile.txt", testPath.toUtf8().constData());
}
TEST(AzQtComponents, ApplyMissingExtension_NoUpdateExistingExtension_Success)
{
const QString textFiler{"Text Files (*.txt)"};
QString testPath{"testFile.txt"};
bool result = AzQtComponents::FileDialog::ApplyMissingExtension(textFiler, testPath);
EXPECT_FALSE(result);
EXPECT_STRCASEEQ("testFile.txt", testPath.toUtf8().constData());
}
TEST(AzQtComponents, ApplyMissingExtension_UpdateMissingExtensionMultipleExtensionFilter_Success)
{
const QString textFiler{"Image Files (*.jpg *.bmp *.png)"};
QString testPath{"testFile"};
bool result = AzQtComponents::FileDialog::ApplyMissingExtension(textFiler, testPath);
EXPECT_TRUE(result);
EXPECT_STRCASEEQ("testFile.jpg", testPath.toUtf8().constData());
}
TEST(AzQtComponents, ApplyMissingExtension_NoUpdateMissingExtensionMultipleExtensionFilter_Success)
{
const QString textFiler{"Image Files (*.jpg *.bmp *.png)"};
QString testPath{"testFile.png"};
bool result = AzQtComponents::FileDialog::ApplyMissingExtension(textFiler, testPath);
EXPECT_FALSE(result);
EXPECT_STRCASEEQ("testFile.png", testPath.toUtf8().constData());
}
TEST(AzQtComponents, ApplyMissingExtension_NoUpdateMissingExtensionEmptyFilter_Success)
{
const QString textFiler{""};
QString testPath{"testFile"};
bool result = AzQtComponents::FileDialog::ApplyMissingExtension(textFiler, testPath);
EXPECT_FALSE(result);
EXPECT_STRCASEEQ("testFile", testPath.toUtf8().constData());
}
TEST(AzQtComponents, ApplyMissingExtension_NoUpdateMissingExtensionInvalidFilter_Success)
{
const QString textFiler{"Bad Filter!!"};
QString testPath{"testFile"};
bool result = AzQtComponents::FileDialog::ApplyMissingExtension(textFiler, testPath);
EXPECT_FALSE(result);
EXPECT_STRCASEEQ("testFile", testPath.toUtf8().constData());
}
@@ -271,7 +271,6 @@ set(FILES
Utilities/ColorUtilities.h
Utilities/Conversions.h
Utilities/Conversions.cpp
Utilities/DesktopUtilities.cpp
Utilities/DesktopUtilities.h
Utilities/HandleDpiAwareness.cpp
Utilities/HandleDpiAwareness.h
@@ -9,6 +9,7 @@
set(FILES
Tests/AzQtComponentTests.cpp
Tests/ColorControllerTests.cpp
Tests/FileDialogTests.cpp
Tests/FloatToStringConversionTests.cpp
Tests/HexParsingTests.cpp
Tests/StyleSheetCacheTests.cpp
@@ -10,6 +10,8 @@ if(NOT PAL_TRAIT_BUILD_HOST_TOOLS)
return()
endif()
ly_get_list_relative_pal_filename(pal_dir ${CMAKE_CURRENT_LIST_DIR}/Platform/${PAL_PLATFORM_NAME})
ly_add_target(
NAME AzQtComponents SHARED
NAMESPACE AZ
@@ -26,6 +28,7 @@ ly_add_target(
AzQtComponents
PUBLIC
.
${pal_dir}
COMPILE_DEFINITIONS
PRIVATE
AZ_QT_COMPONENTS_EXPORT_SYMBOLS
@@ -53,6 +56,7 @@ ly_add_target(
.
AzQtComponents
AzQtComponents/Gallery
${pal_dir}
BUILD_DEPENDENCIES
PRIVATE
3rdParty::Qt::Svg
@@ -86,6 +90,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
PRIVATE
Tests
AzQtComponents
${pal_dir}
BUILD_DEPENDENCIES
PRIVATE
AZ::AzQtComponents
@@ -0,0 +1,11 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#define AZ_TRAIT_AZQTCOMPONENTS_FILE_DIALOG_APPLY_MISSING_EXTENSION 1
#define AZ_TRAIT_AZQTCOMPONENTS_FILE_DIALOG_FILTER_CASE_SENSITIVITY QRegularExpression::NoPatternOption
@@ -0,0 +1,10 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzQtComponents/AzQtComponents_Traits_Linux.h>
@@ -0,0 +1,49 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzQtComponents/Utilities/DesktopUtilities.h>
#include <QDir>
#include <QProcess>
namespace AzQtComponents
{
void ShowFileOnDesktop(const QString& path)
{
const char* defaultNautilusPath = "/usr/bin/nautilus";
const char* defaultXdgOpenPath = "/usr/bin/xdg-open";
// Determine if Nautilus (for Gnome Desktops) is available because it supports opening the file manager
// and selecting a specific file
bool nautilusAvailable = QFileInfo(defaultNautilusPath).exists();
QFileInfo pathInfo(path);
if (pathInfo.isDir())
{
QProcess::startDetached(defaultXdgOpenPath, { path });
}
else
{
if (nautilusAvailable)
{
QProcess::startDetached(defaultNautilusPath, { "--select", path });
}
else
{
QDir parentDir { pathInfo.dir() };
QProcess::startDetached(defaultXdgOpenPath, { parentDir.path() });
}
}
}
QString fileBrowserActionName()
{
const char* exploreActionName = "Open in file browser";
return QObject::tr(exploreActionName);
}
}
@@ -0,0 +1,11 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#define AZ_TRAIT_AZQTCOMPONENTS_FILE_DIALOG_APPLY_MISSING_EXTENSION 0
#define AZ_TRAIT_AZQTCOMPONENTS_FILE_DIALOG_FILTER_CASE_SENSITIVITY QRegularExpression::NoPatternOption
@@ -0,0 +1,10 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzQtComponents/AzQtComponents_Traits_Mac.h>
@@ -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);
}
}
@@ -0,0 +1,10 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzQtComponents/AzQtComponents_Traits_Windows.h>
@@ -0,0 +1,11 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#define AZ_TRAIT_AZQTCOMPONENTS_FILE_DIALOG_APPLY_MISSING_EXTENSION 0
#define AZ_TRAIT_AZQTCOMPONENTS_FILE_DIALOG_FILTER_CASE_SENSITIVITY QRegularExpression::CaseInsensitiveOption
@@ -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);
}
}
@@ -0,0 +1,29 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/Interface/Interface.h>
#include <AzCore/Outcome/Outcome.h>
#include <AzToolsFramework/API/ViewportEditorModeTrackerNotificationBus.h>
namespace AzToolsFramework
{
//! The AZ::Interface for component mode collection queries.
class ComponentModeCollectionInterface
{
public:
AZ_RTTI(ComponentModeCollectionInterface, "{DFAA4450-BBCD-47C0-9B91-FEA2DBD9B152}");
virtual ~ComponentModeCollectionInterface() = default;
//! Retrieves the list of all Component types (usually one).
//! @note If called outside of component mode, an empty vector will be returned.
virtual AZStd::vector<AZ::Uuid> GetComponentTypes() const = 0;
};
} // namespace AzToolsFramework
@@ -0,0 +1,27 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzToolsFramework/API/ViewportEditorModeTrackerNotificationBus.h>
namespace AzToolsFramework
{
void ViewportEditorModeNotifications::Reflect(AZ::ReflectContext* context)
{
if (auto* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->EBus<ViewportEditorModeNotificationsBus>("ViewportEditorModeNotificationsBus")
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation)
->Attribute(AZ::Script::Attributes::Category, "Editor")
->Attribute(AZ::Script::Attributes::Module, "editor")
->Event("OnEditorModeActivated", &ViewportEditorModeNotifications::OnEditorModeActivated)
->Event("OnEditorModeDeactivated", &ViewportEditorModeNotifications::OnEditorModeDeactivated)
;
}
}
} // namespace AzToolsFramework
@@ -34,6 +34,8 @@ namespace AzToolsFramework
class ViewportEditorModesInterface
{
public:
AZ_RTTI(ViewportEditorModesInterface, "{2421496C-4A46-41C9-8AEF-AE2B6E43E6CF}");
virtual ~ViewportEditorModesInterface() = default;
//! Returns true if the specified editor mode is active, otherwise false.
@@ -52,6 +54,9 @@ namespace AzToolsFramework
using BusIdType = ViewportEditorModeTrackerInfo::IdType;
//////////////////////////////////////////////////////////////////////////
AZ_RTTI(ViewportEditorModeNotifications, "{9469DE39-6C21-423C-94FA-EF3A9616B14F}", AZ::EBusTraits);
static void Reflect(AZ::ReflectContext* context);
//! Notifies subscribers of the a given viewport to the activation of the specified editor mode.
virtual void OnEditorModeActivated([[maybe_unused]] const ViewportEditorModesInterface& editorModeState, [[maybe_unused]] ViewportEditorMode mode)
{
@@ -410,7 +410,7 @@ namespace AzToolsFramework
filter.append(ext);
if (i < n - 1)
{
filter.append(", ");
filter.append(" ");
}
}
filter.append(")");

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