Fix merge conflicts.

Signed-off-by: John <jonawals@amazon.com>
monroegm-disable-blank-issue-2
John 4 years ago
commit cf41ff020a

@ -74,6 +74,7 @@ def update_manifest(scene):
source_filename_only = os.path.basename(clean_filename)
created_entities = []
previous_entity_id = azlmbr.entity.InvalidEntityId
# Loop every mesh node in the scene
for activeMeshIndex in range(len(mesh_name_list)):
@ -102,14 +103,33 @@ def update_manifest(scene):
# The MeshGroup we created will be output as a product in the asset's path named mesh_group_name.azmodel
# The assetHint will be converted to an AssetId later during prefab loading
json_update = json.dumps({
"Controller": { "Configuration": { "ModelAsset": {
"assetHint": os.path.join(source_relative_path, mesh_group_name) + ".azmodel" }}}
});
"Controller": { "Configuration": { "ModelAsset": {
"assetHint": os.path.join(source_relative_path, mesh_group_name) + ".azmodel" }}}
});
# Apply the JSON above to the component we created
result = azlmbr.entity.EntityUtilityBus(azlmbr.bus.Broadcast, "UpdateComponentForEntity", entity_id, editor_mesh_component, json_update)
if not result:
raise RuntimeError("UpdateComponentForEntity failed")
raise RuntimeError("UpdateComponentForEntity failed for Mesh component")
# Get the transform component
transform_component = azlmbr.entity.EntityUtilityBus(azlmbr.bus.Broadcast, "GetOrAddComponentByTypeName", entity_id, "27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0")
# Set this entity to be a child of the last entity we created
# This is just an example of how to do parenting and isn't necessarily useful to parent everything like this
if previous_entity_id is not None:
transform_json = json.dumps({
"Parent Entity" : previous_entity_id.to_json()
});
# Apply the JSON update
result = azlmbr.entity.EntityUtilityBus(azlmbr.bus.Broadcast, "UpdateComponentForEntity", entity_id, transform_component, transform_json)
if not result:
raise RuntimeError("UpdateComponentForEntity failed for Transform component")
# Update the last entity id for next time
previous_entity_id = entity_id
# Keep track of the entity we set up, we'll add them all to the prefab we're creating later
created_entities.append(entity_id)
@ -147,6 +167,8 @@ def on_update_manifest(args):
except RuntimeError as err:
print (f'ERROR - {err}')
log_exception_traceback()
except:
log_exception_traceback()
global sceneJobHandler
sceneJobHandler = None

@ -148,7 +148,7 @@ class TestAllComponentsIndepthTests(object):
golden_image_path = os.path.join(golden_images_directory(), golden_image)
golden_images.append(golden_image_path)
expected_lines = ["Light component tests completed."]
expected_lines = ["spot_light Controller|Configuration|Shadows|Shadowmap size: SUCCESS"]
unexpected_lines = [
"Trace::Assert",
"Trace::Error",

@ -67,5 +67,13 @@ class TestAutomation(EditorTestSuite):
class AtomEditorComponents_PostFXLayerAdded(EditorSharedTest):
from Atom.tests import hydra_AtomEditorComponents_PostFXLayerAdded as test_module
@pytest.mark.test_case_id("C36525665")
class AtomEditorComponents_PostFXShapeWeightModifierAdded(EditorSharedTest):
from Atom.tests import hydra_AtomEditorComponents_PostFxShapeWeightModifierAdded as test_module
@pytest.mark.test_case_id("C36525664")
class AtomEditorComponents_PostFXGradientWeightModifierAdded(EditorSharedTest):
from Atom.tests import hydra_AtomEditorComponents_PostFXGradientWeightModifierAdded as test_module
class ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges(EditorSharedTest):
from Atom.tests import hydra_ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges as test_module

@ -0,0 +1,179 @@
"""
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
"""
class Tests:
creation_undo = (
"UNDO Entity creation success",
"UNDO Entity creation failed")
creation_redo = (
"REDO Entity creation success",
"REDO Entity creation failed")
postfx_gradient_weight_creation = (
"PostFX Gradient Weight Modifier Entity successfully created",
"PostFX Gradient Weight Modifier Entity failed to be created")
postfx_gradient_weight_component = (
"Entity has a PostFX Gradient Weight Modifier component",
"Entity failed to find PostFX Gradient Weight Modifier component")
postfx_gradient_weight_disabled = (
"PostFX Gradient Weight Modifier component disabled",
"PostFX Gradient Weight Modifier component was not disabled.")
postfx_layer_component = (
"Entity has a PostFX Layer component",
"Entity did not have an PostFX Layer component")
postfx_gradient_weight_enabled = (
"PostFX Gradient Weight Modifier component enabled",
"PostFX Gradient Weight Modifier component was not enabled.")
enter_game_mode = (
"Entered game mode",
"Failed to enter game mode")
exit_game_mode = (
"Exited game mode",
"Couldn't exit game mode")
is_visible = (
"Entity is visible",
"Entity was not visible")
is_hidden = (
"Entity is hidden",
"Entity was not hidden")
entity_deleted = (
"Entity deleted",
"Entity was not deleted")
deletion_undo = (
"UNDO deletion success",
"UNDO deletion failed")
deletion_redo = (
"REDO deletion success",
"REDO deletion failed")
def AtomEditorComponents_PostFXGradientWeightModifier_AddedToEntity():
"""
Summary:
Tests the PostFX Gradient Weight Modifier component can be added to an entity and has the expected functionality.
Test setup:
- Wait for Editor idle loop.
- Open the "Base" level.
Expected Behavior:
The component can be added, used in game mode, hidden/shown, deleted, and has accurate required components.
Creation and deletion undo/redo should also work.
Test Steps:
1) Create a PostFX Gradient Weight Modifier entity with no components.
2) Add a PostFX Gradient Weight Modifier component to PostFX Gradient Weight Modifier entity.
3) UNDO the entity creation and component addition.
4) REDO the entity creation and component addition.
5) Verify PostFX Gradient Weight Modifier component not enabled.
6) Add PostFX Layer component since it is required by the PostFX Gradient Weight Modifier component.
7) Verify PostFX Gradient Weight Modifier component is enabled.
8) Enter/Exit game mode.
9) Test IsHidden.
10) Test IsVisible.
11) Delete PostFX Gradient Weight Modifier entity.
12) UNDO deletion.
13) REDO deletion.
14) Look for errors.
:return: None
"""
import azlmbr.legacy.general as general
from editor_python_test_tools.editor_entity_utils import EditorEntity
from editor_python_test_tools.utils import Report, Tracer, TestHelper
with Tracer() as error_tracer:
# Test setup begins.
# Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level.
TestHelper.init_idle()
TestHelper.open_level("", "Base")
# Test steps begin.
# 1. Create a PostFX Gradient Weight Modifier entity with no components.
postfx_gradient_weight_name = "PostFX Gradient Weight Modifier"
postfx_gradient_weight_entity = EditorEntity.create_editor_entity(postfx_gradient_weight_name)
Report.critical_result(Tests.postfx_gradient_weight_creation, postfx_gradient_weight_entity.exists())
# 2. Add a PostFX Gradient Weight Modifier component to PostFX Gradient Weight Modifier entity.
postfx_gradient_weight_component = postfx_gradient_weight_entity.add_component(postfx_gradient_weight_name)
Report.critical_result(
Tests.postfx_gradient_weight_component,
postfx_gradient_weight_entity.has_component(postfx_gradient_weight_name))
# 3. UNDO the entity creation and component addition.
# -> UNDO component addition.
general.undo()
# -> UNDO naming entity.
general.undo()
# -> UNDO selecting entity.
general.undo()
# -> UNDO entity creation.
general.undo()
general.idle_wait_frames(1)
Report.result(Tests.creation_undo, not postfx_gradient_weight_entity.exists())
# 4. REDO the entity creation and component addition.
# -> REDO entity creation.
general.redo()
# -> REDO selecting entity.
general.redo()
# -> REDO naming entity.
general.redo()
# -> REDO component addition.
general.redo()
general.idle_wait_frames(1)
Report.result(Tests.creation_redo, postfx_gradient_weight_entity.exists())
# 5. Verify PostFX Gradient Weight Modifier component not enabled.
Report.result(Tests.postfx_gradient_weight_disabled, not postfx_gradient_weight_component.is_enabled())
# 6. Add PostFX Layer component since it is required by the PostFX Gradient Weight Modifier component.
postfx_layer_name = "PostFX Layer"
postfx_gradient_weight_entity.add_component(postfx_layer_name)
Report.result(Tests.postfx_layer_component, postfx_gradient_weight_entity.has_component(postfx_layer_name))
# 7. Verify PostFX Gradient Weight Modifier component is enabled.
Report.result(Tests.postfx_gradient_weight_enabled, postfx_gradient_weight_component.is_enabled())
# 8. Enter/Exit game mode.
TestHelper.enter_game_mode(Tests.enter_game_mode)
general.idle_wait_frames(1)
TestHelper.exit_game_mode(Tests.exit_game_mode)
# 9. Test IsHidden.
postfx_gradient_weight_entity.set_visibility_state(False)
Report.result(Tests.is_hidden, postfx_gradient_weight_entity.is_hidden() is True)
# 10. Test IsVisible.
postfx_gradient_weight_entity.set_visibility_state(True)
general.idle_wait_frames(1)
Report.result(Tests.is_visible, postfx_gradient_weight_entity.is_visible() is True)
# 11. Delete PostFX Gradient Weight Modifier entity.
postfx_gradient_weight_entity.delete()
Report.result(Tests.entity_deleted, not postfx_gradient_weight_entity.exists())
# 12. UNDO deletion.
general.undo()
Report.result(Tests.deletion_undo, postfx_gradient_weight_entity.exists())
# 13. REDO deletion.
general.redo()
Report.result(Tests.deletion_redo, not postfx_gradient_weight_entity.exists())
# 14. Look for errors or asserts.
TestHelper.wait_for_condition(lambda: error_tracer.has_errors or error_tracer.has_asserts, 1.0)
for error_info in error_tracer.errors:
Report.info(f"Error: {error_info.filename} {error_info.function} | {error_info.message}")
for assert_info in error_tracer.asserts:
Report.info(f"Assert: {assert_info.filename} {assert_info.function} | {assert_info.message}")
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(AtomEditorComponents_PostFXGradientWeightModifier_AddedToEntity)

@ -0,0 +1,207 @@
"""
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
"""
class Tests:
creation_undo = (
"UNDO Entity creation success",
"UNDO Entity creation failed")
creation_redo = (
"REDO Entity creation success",
"REDO Entity creation failed")
postfx_shape_weight_creation = (
"PostFx Shape Weight Modifier Entity successfully created",
"PostFx Shape Weight Modifier Entity failed to be created")
postfx_shape_weight_component = (
"Entity has a PostFx Shape Weight Modifier component",
"Entity failed to find PostFx Shape Weight Modifier component")
postfx_shape_weight_disabled = (
"PostFx Shape Weight Modifier component disabled",
"PostFx Shape Weight Modifier component was not disabled.")
postfx_layer_component = (
"Entity has a PostFX Layer component",
"Entity did not have an PostFX Layer component")
tube_shape_component = (
"Entity has a Tube Shape component",
"Entity did not have a Tube Shape component")
postfx_shape_weight_enabled = (
"PostFx Shape Weight Modifier component enabled",
"PostFx Shape Weight Modifier component was not enabled.")
enter_game_mode = (
"Entered game mode",
"Failed to enter game mode")
exit_game_mode = (
"Exited game mode",
"Couldn't exit game mode")
is_visible = (
"Entity is visible",
"Entity was not visible")
is_hidden = (
"Entity is hidden",
"Entity was not hidden")
entity_deleted = (
"Entity deleted",
"Entity was not deleted")
deletion_undo = (
"UNDO deletion success",
"UNDO deletion failed")
deletion_redo = (
"REDO deletion success",
"REDO deletion failed")
def AtomEditorComponents_postfx_shape_weight_AddedToEntity():
"""
Summary:
Tests the PostFx Shape Weight Modifier component can be added to an entity and has the expected functionality.
Test setup:
- Wait for Editor idle loop.
- Open the "Base" level.
Expected Behavior:
The component can be added, used in game mode, hidden/shown, deleted, and has accurate required components.
Creation and deletion undo/redo should also work.
Test Steps:
1) Create a PostFx Shape Weight Modifier entity with no components.
2) Add a PostFx Shape Weight Modifier component to PostFx Shape Weight Modifier entity.
3) UNDO the entity creation and component addition.
4) REDO the entity creation and component addition.
5) Verify PostFx Shape Weight Modifier component not enabled.
6) Add PostFX Layer component since it is required by the PostFx Shape Weight Modifier component.
7) Verify PostFx Shape Weight Modifier component is NOT enabled since it also requires a shape.
8) Add a required shape looping over a list and checking if it enables PostFX Shape Weight Modifier.
9) Undo to remove each added shape and verify PostFX Shape Weight Modifier is not enabled.
10) Verify PostFx Shape Weight Modifier component is enabled by adding Spline and Tube Shape component.
11) Enter/Exit game mode.
12) Test IsHidden.
13) Test IsVisible.
14) Delete PostFx Shape Weight Modifier entity.
15) UNDO deletion.
16) REDO deletion.
17) Look for errors.
:return: None
"""
import azlmbr.legacy.general as general
from editor_python_test_tools.editor_entity_utils import EditorEntity
from editor_python_test_tools.utils import Report, Tracer, TestHelper
with Tracer() as error_tracer:
# Test setup begins.
# Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level.
TestHelper.init_idle()
TestHelper.open_level("", "Base")
# Test steps begin.
# 1. Create a PostFx Shape Weight Modifier entity with no components.
postfx_shape_weight_name = "PostFX Shape Weight Modifier"
postfx_shape_weight_entity = EditorEntity.create_editor_entity(postfx_shape_weight_name)
Report.critical_result(Tests.postfx_shape_weight_creation, postfx_shape_weight_entity.exists())
# 2. Add a PostFx Shape Weight Modifier component to PostFx Shape Weight Modifier entity.
postfx_shape_weight_component = postfx_shape_weight_entity.add_component(postfx_shape_weight_name)
Report.critical_result(
Tests.postfx_shape_weight_component,
postfx_shape_weight_entity.has_component(postfx_shape_weight_name))
# 3. UNDO the entity creation and component addition.
# -> UNDO component addition.
general.undo()
# -> UNDO naming entity.
general.undo()
# -> UNDO selecting entity.
general.undo()
# -> UNDO entity creation.
general.undo()
general.idle_wait_frames(1)
Report.result(Tests.creation_undo, not postfx_shape_weight_entity.exists())
# 4. REDO the entity creation and component addition.
# -> REDO entity creation.
general.redo()
# -> REDO selecting entity.
general.redo()
# -> REDO naming entity.
general.redo()
# -> REDO component addition.
general.redo()
general.idle_wait_frames(1)
Report.result(Tests.creation_redo, postfx_shape_weight_entity.exists())
# 5. Verify PostFx Shape Weight Modifier component not enabled.
Report.result(Tests.postfx_shape_weight_disabled, not postfx_shape_weight_component.is_enabled())
# 6. Add PostFX Layer component since it is required by the PostFx Shape Weight Modifier component.
postfx_layer_name = "PostFX Layer"
postfx_shape_weight_entity.add_component(postfx_layer_name)
Report.result(Tests.postfx_layer_component, postfx_shape_weight_entity.has_component(postfx_layer_name))
# 7. Verify PostFx Shape Weight Modifier component is NOT enabled since it also requires a shape.
Report.result(Tests.postfx_shape_weight_disabled, not postfx_shape_weight_component.is_enabled())
# 8. Add a required shape looping over a list and checking if it enables PostFX Shape Weight Modifier.
for shape in ['Axis Aligned Box Shape', 'Box Shape', 'Capsule Shape', 'Compound Shape', 'Cylinder Shape',
'Disk Shape', 'Polygon Prism Shape', 'Quad Shape', 'Sphere Shape', 'Vegetation Reference Shape']:
postfx_shape_weight_entity.add_component(shape)
test_shape = (
f"Entity has a {shape} component",
f"Entity did not have a {shape} component")
Report.result(test_shape, postfx_shape_weight_entity.has_component(shape))
# Check if required shape allows PostFX Shape Weight Modifier to be enabled
Report.result(Tests.postfx_shape_weight_enabled, postfx_shape_weight_component.is_enabled())
# 9. Undo to remove each added shape and verify PostFX Shape Weight Modifier is not enabled.
general.undo()
TestHelper.wait_for_condition(lambda: not postfx_shape_weight_entity.has_component(shape), 1.0)
Report.result(Tests.postfx_shape_weight_disabled, not postfx_shape_weight_component.is_enabled())
# 10. Verify PostFx Shape Weight Modifier component is enabled by adding Spline and Tube Shape component.
postfx_shape_weight_entity.add_components(['Spline', 'Tube Shape'])
Report.result(Tests.tube_shape_component, postfx_shape_weight_entity.has_component('Tube Shape'))
Report.result(Tests.postfx_shape_weight_enabled, postfx_shape_weight_component.is_enabled())
# 11. Enter/Exit game mode.
TestHelper.enter_game_mode(Tests.enter_game_mode)
general.idle_wait_frames(1)
TestHelper.exit_game_mode(Tests.exit_game_mode)
# 12. Test IsHidden.
postfx_shape_weight_entity.set_visibility_state(False)
Report.result(Tests.is_hidden, postfx_shape_weight_entity.is_hidden() is True)
# 13. Test IsVisible.
postfx_shape_weight_entity.set_visibility_state(True)
general.idle_wait_frames(1)
Report.result(Tests.is_visible, postfx_shape_weight_entity.is_visible() is True)
# 14. Delete PostFx Shape Weight Modifier entity.
postfx_shape_weight_entity.delete()
Report.result(Tests.entity_deleted, not postfx_shape_weight_entity.exists())
# 15. UNDO deletion.
general.undo()
Report.result(Tests.deletion_undo, postfx_shape_weight_entity.exists())
# 16. REDO deletion.
general.redo()
Report.result(Tests.deletion_redo, not postfx_shape_weight_entity.exists())
# 17. Look for errors or asserts.
TestHelper.wait_for_condition(lambda: error_tracer.has_errors or error_tracer.has_asserts, 1.0)
for error_info in error_tracer.errors:
Report.info(f"Error: {error_info.filename} {error_info.function} | {error_info.message}")
for assert_info in error_tracer.asserts:
Report.info(f"Assert: {assert_info.filename} {assert_info.function} | {assert_info.message}")
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(AtomEditorComponents_postfx_shape_weight_AddedToEntity)

@ -0,0 +1,13 @@
{
"description": "",
"materialType": "Materials/Types/Skin.materialtype",
"parentMaterial": "",
"propertyLayoutVersion": 3,
"properties": {
"wrinkleLayers": {
"count": 3,
"enable": true,
"showBlendValues": true
}
}
}

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:53e17ec8155911c8b42e85436130f600bd6dddd8931a8ccb1b2f8a9f8674cc85
size 45104

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:0da56a05daa0ec1c476cfe25ca6d3b65267c98886cf33408f6e852fb325a8e2c
size 198084

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e3537fbe9205731a242251c525a67bbb5f3b8f5307537f1dc0c318b5b885ce52
size 198112

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:bd794d5dd4b749c3275bfab79b9b5ae3f8e007d3e6741c0566c9c2d3931123bf
size 198112

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:45ded862987a64061deffd8e4c9aa1dff4eec3bcff5f7b505679f1959e8ae137
size 51440

@ -102,7 +102,7 @@ ly_add_target(
3rdParty::Qt::Gui
3rdParty::Qt::Widgets
3rdParty::Qt::Concurrent
3rdParty::tiff
3rdParty::TIFF
3rdParty::squish-ccr
3rdParty::AWSNativeSDK::STS
Legacy::CryCommon

@ -19,10 +19,16 @@ namespace Editor
if (GetIEditor()->IsInGameMode())
{
#ifdef PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
AzFramework::XcbEventHandlerBus::Broadcast(&AzFramework::XcbEventHandler::HandleXcbEvent, static_cast<xcb_generic_event_t*>(message));
// We need to handle RAW Input events in a separate loop. This is a workaround to enable XInput2 RAW Inputs using Editor mode.
// TODO To have this call here might be not be perfect.
AzFramework::XcbEventHandlerBus::Broadcast(&AzFramework::XcbEventHandler::PollSpecialEvents);
// Now handle the rest of the events.
AzFramework::XcbEventHandlerBus::Broadcast(
&AzFramework::XcbEventHandler::HandleXcbEvent, static_cast<xcb_generic_event_t*>(message));
#endif
return true;
}
return false;
}
}
} // namespace Editor

@ -57,6 +57,7 @@ AZ_POP_DISABLE_WARNING
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzFramework/Terrain/TerrainDataRequestBus.h>
#include <AzFramework/ProjectManager/ProjectManager.h>
#include <AzFramework/Spawnable/RootSpawnableInterface.h>
// AzToolsFramework
#include <AzToolsFramework/Component/EditorComponentAPIBus.h>
@ -3021,6 +3022,15 @@ CCryEditApp::ECreateLevelResult CCryEditApp::CreateLevel(const QString& levelNam
bool bIsDocModified = GetIEditor()->GetDocument()->IsModified();
OnSwitchPhysics();
GetIEditor()->GetDocument()->SetModifiedFlag(bIsDocModified);
if (usePrefabSystemForLevels)
{
auto* rootSpawnableInterface = AzFramework::RootSpawnableInterface::Get();
if (rootSpawnableInterface)
{
rootSpawnableInterface->ProcessSpawnableQueue();
}
}
}
const QScopedValueRollback<bool> rollback(m_creatingNewLevel);

@ -264,6 +264,9 @@ void EditorPreferencesDialog::SetFilter(const QString& filter)
else if (m_currentPageItem)
{
m_currentPageItem->UpdateEditorFilter(ui->propertyEditor, m_filter);
// Refresh the Stylesheet - when using search functionality.
AzQtComponents::StyleManager::repolishStyleSheet(this);
}
}

@ -14,6 +14,7 @@
// Editor
#include "Settings.h"
#include "EditorViewportSettings.h"
@ -43,17 +44,16 @@ void CEditorPreferencesPage_Files::Reflect(AZ::SerializeContext& serialize)
->Field("MaxCount", &AutoBackup::m_maxCount)
->Field("RemindTime", &AutoBackup::m_remindTime);
serialize.Class<AssetBrowserSearch>()
serialize.Class<AssetBrowserSettings>()
->Version(1)
->Field("Max number of items displayed", &AssetBrowserSearch::m_maxNumberOfItemsShownInSearch);
->Field("MaxEntriesShownCount", &AssetBrowserSettings::m_maxNumberOfItemsShownInSearch);
serialize.Class<CEditorPreferencesPage_Files>()
->Version(1)
->Field("Files", &CEditorPreferencesPage_Files::m_files)
->Field("Editors", &CEditorPreferencesPage_Files::m_editors)
->Field("AutoBackup", &CEditorPreferencesPage_Files::m_autoBackup)
->Field("AssetBrowserSearch", &CEditorPreferencesPage_Files::m_assetBrowserSearch);
->Field("AssetBrowserSettings", &CEditorPreferencesPage_Files::m_assetBrowserSettings);
AZ::EditContext* editContext = serialize.GetEditContext();
if (editContext)
@ -85,9 +85,10 @@ void CEditorPreferencesPage_Files::Reflect(AZ::SerializeContext& serialize)
->Attribute(AZ::Edit::Attributes::Max, 100)
->DataElement(AZ::Edit::UIHandlers::SpinBox, &AutoBackup::m_remindTime, "Remind Time", "Auto Remind Every (Minutes)");
editContext->Class<AssetBrowserSearch>("Asset Browser Search View", "Asset Browser Search View")
->DataElement(AZ::Edit::UIHandlers::SpinBox, &AssetBrowserSearch::m_maxNumberOfItemsShownInSearch, "Maximum number of displayed items",
"Maximum number of displayed items displayed in the Search View")
editContext->Class<AssetBrowserSettings>("Asset Browser Settings", "Asset Browser Settings")
->DataElement(
AZ::Edit::UIHandlers::SpinBox, &AssetBrowserSettings::m_maxNumberOfItemsShownInSearch, "Maximum number of displayed items",
"Maximum number of items to display in the Search View.")
->Attribute(AZ::Edit::Attributes::Min, 50)
->Attribute(AZ::Edit::Attributes::Max, 5000);
@ -97,7 +98,7 @@ void CEditorPreferencesPage_Files::Reflect(AZ::SerializeContext& serialize)
->DataElement(AZ::Edit::UIHandlers::Default, &CEditorPreferencesPage_Files::m_files, "Files", "File Preferences")
->DataElement(AZ::Edit::UIHandlers::Default, &CEditorPreferencesPage_Files::m_editors, "External Editors", "External Editors")
->DataElement(AZ::Edit::UIHandlers::Default, &CEditorPreferencesPage_Files::m_autoBackup, "Auto Backup", "Auto Backup")
->DataElement(AZ::Edit::UIHandlers::Default, &CEditorPreferencesPage_Files::m_assetBrowserSearch, "Asset Browser Search", "Asset Browser Search");
->DataElement(AZ::Edit::UIHandlers::Default, &CEditorPreferencesPage_Files::m_assetBrowserSettings, "Asset Browser Settings","Asset Browser Settings");
}
}
@ -117,6 +118,7 @@ QIcon& CEditorPreferencesPage_Files::GetIcon()
void CEditorPreferencesPage_Files::OnApply()
{
using namespace AzToolsFramework::SliceUtilities;
auto sliceSettings = AZ::UserSettings::CreateFind<SliceUserSettings>(AZ_CRC("SliceUserSettings", 0x055b32eb), AZ::UserSettings::CT_LOCAL);
sliceSettings->m_autoNumber = m_files.m_autoNumberSlices;
sliceSettings->m_saveLocation = m_files.m_saveLocation;
@ -137,7 +139,7 @@ void CEditorPreferencesPage_Files::OnApply()
gSettings.autoBackupMaxCount = m_autoBackup.m_maxCount;
gSettings.autoRemindTime = m_autoBackup.m_remindTime;
gSettings.maxNumberOfItemsShownInSearch = m_assetBrowserSearch.m_maxNumberOfItemsShownInSearch;
SandboxEditor::SetMaxItemsShownInAssetBrowserSearch(m_assetBrowserSettings.m_maxNumberOfItemsShownInSearch);
}
void CEditorPreferencesPage_Files::InitializeSettings()
@ -163,5 +165,5 @@ void CEditorPreferencesPage_Files::InitializeSettings()
m_autoBackup.m_maxCount = gSettings.autoBackupMaxCount;
m_autoBackup.m_remindTime = gSettings.autoRemindTime;
m_assetBrowserSearch.m_maxNumberOfItemsShownInSearch = gSettings.maxNumberOfItemsShownInSearch;
m_assetBrowserSettings.m_maxNumberOfItemsShownInSearch = SandboxEditor::MaxItemsShownInAssetBrowserSearch();
}

@ -69,18 +69,15 @@ private:
int m_remindTime;
};
struct AssetBrowserSearch
struct AssetBrowserSettings
{
AZ_TYPE_INFO(AssetBrowserSearch, "{9FBFCD24-9452-49DF-99F4-2711443CEAAE}")
int m_maxNumberOfItemsShownInSearch;
AZ_TYPE_INFO(AssetBrowserSettings, "{5F407EC4-BBD1-4A87-92DB-D938D7127BB0}")
AZ::u64 m_maxNumberOfItemsShownInSearch;
};
Files m_files;
ExternalEditors m_editors;
AutoBackup m_autoBackup;
AssetBrowserSearch m_assetBrowserSearch;
AssetBrowserSettings m_assetBrowserSettings;
QIcon m_icon;
};

@ -15,6 +15,7 @@
namespace SandboxEditor
{
constexpr AZStd::string_view AssetBrowserMaxItemsShownInSearchSetting = "/Amazon/Preferences/Editor/AssetBrowser/MaxItemsShowInSearch";
constexpr AZStd::string_view GridSnappingSetting = "/Amazon/Preferences/Editor/GridSnapping";
constexpr AZStd::string_view GridSizeSetting = "/Amazon/Preferences/Editor/GridSize";
constexpr AZStd::string_view AngleSnappingSetting = "/Amazon/Preferences/Editor/AngleSnapping";
@ -110,6 +111,16 @@ namespace SandboxEditor
return AZStd::make_unique<EditorViewportSettingsCallbacksImpl>();
}
AZ::u64 MaxItemsShownInAssetBrowserSearch()
{
return GetRegistry(AssetBrowserMaxItemsShownInSearchSetting, aznumeric_cast<AZ::u64>(50));
}
void SetMaxItemsShownInAssetBrowserSearch(const AZ::u64 numberOfItemsShown)
{
SetRegistry(AssetBrowserMaxItemsShownInSearchSetting, numberOfItemsShown);
}
bool GridSnappingEnabled()
{
return GetRegistry(GridSnappingSetting, false);

@ -32,6 +32,9 @@ namespace SandboxEditor
//! event will fire when a value in the settings registry (editorpreferences.setreg) is modified.
SANDBOX_API AZStd::unique_ptr<EditorViewportSettingsCallbacks> CreateEditorViewportSettingsCallbacks();
SANDBOX_API AZ::u64 MaxItemsShownInAssetBrowserSearch();
SANDBOX_API void SetMaxItemsShownInAssetBrowserSearch(AZ::u64 numberOfItemsShown);
SANDBOX_API bool GridSnappingEnabled();
SANDBOX_API void SetGridSnapping(bool enabled);

@ -108,15 +108,11 @@ CObjectManager::CObjectManager()
m_objectsByName.reserve(1024);
LoadRegistry();
AzToolsFramework::ViewportEditorModeNotificationsBus::Handler::BusConnect(AzToolsFramework::GetEntityContextId());
}
//////////////////////////////////////////////////////////////////////////
CObjectManager::~CObjectManager()
{
AzToolsFramework::ViewportEditorModeNotificationsBus::Handler::BusDisconnect();
m_bExiting = true;
SaveRegistry();
DeleteAllObjects();
@ -2307,37 +2303,6 @@ void CObjectManager::SelectObjectInRect(CBaseObject* pObj, CViewport* view, HitC
}
}
void CObjectManager::OnEditorModeActivated(
[[maybe_unused]] const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode)
{
if (mode == AzToolsFramework::ViewportEditorMode::Component)
{
// hide current gizmo for entity (translate/rotate/scale)
IGizmoManager* gizmoManager = GetGizmoManager();
const size_t gizmoCount = static_cast<size_t>(gizmoManager->GetGizmoCount());
for (size_t i = 0; i < gizmoCount; ++i)
{
gizmoManager->RemoveGizmo(gizmoManager->GetGizmoByIndex(static_cast<int>(i)));
}
}
}
void CObjectManager::OnEditorModeDeactivated(
[[maybe_unused]] const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode)
{
if (mode == AzToolsFramework::ViewportEditorMode::Component)
{
// show translate/rotate/scale gizmo again
if (IGizmoManager* gizmoManager = GetGizmoManager())
{
if (CBaseObject* selectedObject = GetIEditor()->GetSelectedObject())
{
gizmoManager->AddGizmo(new CAxisGizmo(selectedObject));
}
}
}
}
//////////////////////////////////////////////////////////////////////////
namespace
{

@ -20,7 +20,6 @@
#include "ObjectManagerEventBus.h"
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzToolsFramework/API/ViewportEditorModeTrackerNotificationBus.h>
#include <AzCore/EBus/EBus.h>
#include <AzCore/Component/Component.h>
#include <Include/SandboxAPI.h>
@ -59,7 +58,6 @@ public:
*/
class CObjectManager
: public IObjectManager
, private AzToolsFramework::ViewportEditorModeNotificationsBus::Handler
{
public:
//! Selection functor callback.
@ -330,12 +328,6 @@ private:
void FindDisplayableObjects(DisplayContext& dc, bool bDisplay);
// ViewportEditorModeNotificationsBus overrides ...
void OnEditorModeActivated(
const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode) override;
void OnEditorModeDeactivated(
const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode) override;
private:
typedef std::map<GUID, CBaseObjectPtr, guid_less_predicate> Objects;
Objects m_objects;

@ -10,6 +10,7 @@
#include "EditorDefs.h"
#include "Settings.h"
#include "EditorViewportSettings.h"
// Qt
#include <QGuiApplication>
@ -487,7 +488,6 @@ void SEditorSettings::Save()
SaveValue("Settings", "AutoBackupTime", autoBackupTime);
SaveValue("Settings", "AutoBackupMaxCount", autoBackupMaxCount);
SaveValue("Settings", "AutoRemindTime", autoRemindTime);
SaveValue("Settings", "MaxDisplayedItemsNumInSearch", maxNumberOfItemsShownInSearch);
SaveValue("Settings", "CameraMoveSpeed", cameraMoveSpeed);
SaveValue("Settings", "CameraRotateSpeed", cameraRotateSpeed);
SaveValue("Settings", "StylusMode", stylusMode);
@ -682,7 +682,6 @@ void SEditorSettings::Load()
LoadValue("Settings", "AutoBackupTime", autoBackupTime);
LoadValue("Settings", "AutoBackupMaxCount", autoBackupMaxCount);
LoadValue("Settings", "AutoRemindTime", autoRemindTime);
LoadValue("Settings", "MaxDisplayedItemsNumInSearch", maxNumberOfItemsShownInSearch);
LoadValue("Settings", "CameraMoveSpeed", cameraMoveSpeed);
LoadValue("Settings", "CameraRotateSpeed", cameraRotateSpeed);
LoadValue("Settings", "StylusMode", stylusMode);
@ -1174,7 +1173,7 @@ AzToolsFramework::ConsoleColorTheme SEditorSettings::GetConsoleColorTheme() cons
return consoleBackgroundColorTheme;
}
int SEditorSettings::GetMaxNumberOfItemsShownInSearchView() const
AZ::u64 SEditorSettings::GetMaxNumberOfItemsShownInSearchView() const
{
return SEditorSettings::maxNumberOfItemsShownInSearch;
return SandboxEditor::MaxItemsShownInAssetBrowserSearch();
}

@ -279,7 +279,7 @@ AZ_POP_DISABLE_DLL_EXPORT_BASECLASS_WARNING
SettingOutcome GetValue(const AZStd::string_view path) override;
SettingOutcome SetValue(const AZStd::string_view path, const AZStd::any& value) override;
AzToolsFramework::ConsoleColorTheme GetConsoleColorTheme() const override;
int GetMaxNumberOfItemsShownInSearchView() const override;
AZ::u64 GetMaxNumberOfItemsShownInSearchView() const override;
void ConvertPath(const AZStd::string_view sourcePath, AZStd::string& category, AZStd::string& attribute);
@ -353,14 +353,6 @@ AZ_POP_DISABLE_DLL_EXPORT_BASECLASS_WARNING
int autoRemindTime;
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// Asset Browser Search View.
//////////////////////////////////////////////////////////////////////////
//! Current maximum number of items that can be displayed in the AssetBrowser Search View.
int maxNumberOfItemsShownInSearch;
//////////////////////////////////////////////////////////////////////////
//! If true preview windows is displayed when browsing geometries.
bool bPreviewGeometryWindow;

@ -1195,7 +1195,7 @@ bool CFileUtil::IsFileExclusivelyAccessable(const QString& strFilePath)
//////////////////////////////////////////////////////////////////////////
bool CFileUtil::CreatePath(const QString& strPath)
{
#if defined(AZ_PLATFORM_MAC)
#if !AZ_TRAIT_OS_USE_WINDOWS_FILE_PATHS
bool pathCreated = true;
QString cleanPath = QDir::cleanPath(strPath);
@ -1252,7 +1252,7 @@ bool CFileUtil::CreatePath(const QString& strPath)
}
return true;
#endif
#endif // !AZ_TRAIT_OS_USE_WINDOWS_FILE_PATHS
}
//////////////////////////////////////////////////////////////////////////

@ -225,8 +225,16 @@ namespace AZ
ConsoleCommandContainer commandSubset;
for (ConsoleFunctorBase* curr = m_head; curr != nullptr; curr = curr->m_next)
for (const auto& functor : m_commands)
{
if (functor.second.empty())
{
continue;
}
// Filter functors registered with the same name
const ConsoleFunctorBase* curr = functor.second.front();
if ((curr->GetFlags() & ConsoleFunctorFlags::IsInvisible) == ConsoleFunctorFlags::IsInvisible)
{
// Filter functors marked as invisible
@ -236,7 +244,12 @@ namespace AZ
if (StringFunc::StartsWith(curr->m_name, command, false))
{
AZLOG_INFO("- %s : %s\n", curr->m_name, curr->m_desc);
commandSubset.push_back(curr->m_name);
if (commandSubset.size() < MaxConsoleCommandPlusArgsLength)
{
commandSubset.push_back(curr->m_name);
}
if (matches)
{
matches->push_back(curr->m_name);
@ -271,7 +284,10 @@ namespace AZ
{
for (auto& curr : m_commands)
{
visitor(curr.second.front());
if (!curr.second.empty())
{
visitor(curr.second.front());
}
}
}
@ -336,6 +352,11 @@ namespace AZ
{
iter->second.erase(iter2);
}
if (iter->second.empty())
{
m_commands.erase(iter);
}
}
functor->Unlink(m_head);
functor->m_console = nullptr;

@ -40,6 +40,12 @@ namespace AZ
static unsigned int Record(StackFrame* frames, unsigned int maxNumOfFrames, unsigned int suppressCount = 0, void* nativeThread = 0);
};
class StackConverter
{
public:
static unsigned int FromNative(StackFrame* frames, unsigned int maxNumOfFrames, void* nativeContext);
};
class SymbolStorage
{
public:

@ -31,6 +31,8 @@ namespace AZ
{
namespace Debug
{
struct StackFrame;
namespace Platform
{
#if defined(AZ_ENABLE_DEBUG_TOOLS)
@ -551,17 +553,19 @@ namespace AZ
{
StackFrame frames[25];
// Without StackFrame explicit alignment frames array is aligned to 4 bytes
// which causes the stack tracing to fail.
//size_t bla = AZStd::alignment_of<StackFrame>::value;
//printf("Alignment value %d address 0x%08x : 0x%08x\n",bla,frames);
SymbolStorage::StackLine lines[AZ_ARRAY_SIZE(frames)];
unsigned int numFrames = 0;
if (!nativeContext)
{
suppressCount += 1; /// If we don't provide a context we will capture in the RecordFunction, so skip us (Trace::PrinCallstack).
suppressCount += 1; /// If we don't provide a context we will capture in the RecordFunction, so skip us (Trace::PrintCallstack).
numFrames = StackRecorder::Record(frames, AZ_ARRAY_SIZE(frames), suppressCount);
}
unsigned int numFrames = StackRecorder::Record(frames, AZ_ARRAY_SIZE(frames), suppressCount, nativeContext);
else
{
numFrames = StackConverter::FromNative(frames, AZ_ARRAY_SIZE(frames), nativeContext);
}
if (numFrames)
{
SymbolStorage::DecodeFrames(frames, numFrames, lines);

@ -15,7 +15,7 @@ using namespace Intersect;
// IntersectSegmentTriangleCCW
// [10/21/2009]
//=========================================================================
int Intersect::IntersectSegmentTriangleCCW(
bool Intersect::IntersectSegmentTriangleCCW(
const Vector3& p, const Vector3& q, const Vector3& a, const Vector3& b, const Vector3& c,
/*float &u, float &v, float &w,*/ Vector3& normal, float& t)
{
@ -34,7 +34,7 @@ int Intersect::IntersectSegmentTriangleCCW(
float d = qp.Dot(normal);
if (d <= 0.0f)
{
return 0;
return false;
}
// Compute intersection t value of pq with plane of triangle. A ray
@ -46,7 +46,7 @@ int Intersect::IntersectSegmentTriangleCCW(
// range segment check t[0,1] (it this case [0,d])
if (t < 0.0f || t > d)
{
return 0;
return false;
}
// Compute barycentric coordinate components and test if within bounds
@ -54,12 +54,12 @@ int Intersect::IntersectSegmentTriangleCCW(
v = ac.Dot(e);
if (v < 0.0f || v > d)
{
return 0;
return false;
}
w = -ab.Dot(e);
if (w < 0.0f || v + w > d)
{
return 0;
return false;
}
// Segment/ray intersects triangle. Perform delayed division and
@ -72,14 +72,14 @@ int Intersect::IntersectSegmentTriangleCCW(
normal.Normalize();
return 1;
return true;
}
//=========================================================================
// IntersectSegmentTriangle
// [10/21/2009]
//=========================================================================
int
bool
Intersect::IntersectSegmentTriangle(
const Vector3& p, const Vector3& q, const Vector3& a, const Vector3& b, const Vector3& c,
/*float &u, float &v, float &w,*/ Vector3& normal, float& t)
@ -111,7 +111,7 @@ Intersect::IntersectSegmentTriangle(
// so either have a parallel ray or our normal is flipped
if (d >= -Constants::FloatEpsilon)
{
return 0; // parallel
return false; // parallel
}
d = -d;
e = ap.Cross(qp);
@ -125,19 +125,19 @@ Intersect::IntersectSegmentTriangle(
// range segment check t[0,1] (it this case [0,d])
if (t < 0.0f || t > d)
{
return 0;
return false;
}
// Compute barycentric coordinate components and test if within bounds
v = ac.Dot(e);
if (v < 0.0f || v > d)
{
return 0;
return false;
}
w = -ab.Dot(e);
if (w < 0.0f || v + w > d)
{
return 0;
return false;
}
// Segment/ray intersects the triangle. Perform delayed division and
@ -150,14 +150,14 @@ Intersect::IntersectSegmentTriangle(
normal.Normalize();
return 1;
return true;
}
//=========================================================================
// TestSegmentAABBOrigin
// [10/21/2009]
//=========================================================================
int
bool
AZ::Intersect::TestSegmentAABBOrigin(const Vector3& midPoint, const Vector3& halfVector, const Vector3& aabbExtends)
{
const Vector3 EPSILON(0.001f); // \todo this is slow load move to a const
@ -168,7 +168,7 @@ AZ::Intersect::TestSegmentAABBOrigin(const Vector3& midPoint, const Vector3& hal
// Try world coordinate axes as separating axes
if (!absMidpoint.IsLessEqualThan(absHalfMidpoint))
{
return 0;
return false;
}
// Add in an epsilon term to counteract arithmetic errors when segment is
@ -188,11 +188,11 @@ AZ::Intersect::TestSegmentAABBOrigin(const Vector3& midPoint, const Vector3& hal
Vector3 ead(ey * adz + ez * ady, ex * adz + ez * adx, ex * ady + ey * adx);
if (!absMDCross.IsLessEqualThan(ead))
{
return 0;
return false;
}
// No separating axis found; segment must be overlapping AABB
return 1;
return true;
}
@ -200,7 +200,7 @@ AZ::Intersect::TestSegmentAABBOrigin(const Vector3& midPoint, const Vector3& hal
// IntersectRayAABB
// [10/21/2009]
//=========================================================================
int
RayAABBIsectTypes
AZ::Intersect::IntersectRayAABB(
const Vector3& rayStart, const Vector3& dir, const Vector3& dirRCP, const Aabb& aabb,
float& tStart, float& tEnd, Vector3& startNormal /*, Vector3& inter*/)
@ -356,7 +356,7 @@ AZ::Intersect::IntersectRayAABB(
// IntersectRayAABB2
// [2/18/2011]
//=========================================================================
int
RayAABBIsectTypes
AZ::Intersect::IntersectRayAABB2(const Vector3& rayStart, const Vector3& dirRCP, const Aabb& aabb, float& start, float& end)
{
float tmin, tmax, tymin, tymax, tzmin, tzmax;
@ -408,7 +408,7 @@ AZ::Intersect::IntersectRayAABB2(const Vector3& rayStart, const Vector3& dirRCP,
return ISECT_RAY_AABB_ISECT;
}
int AZ::Intersect::IntersectRayDisk(
bool AZ::Intersect::IntersectRayDisk(
const Vector3& rayOrigin, const Vector3& rayDir, const Vector3& diskCenter, const float diskRadius, const Vector3& diskNormal, float& t)
{
// First intersect with the plane of the disk
@ -421,10 +421,10 @@ int AZ::Intersect::IntersectRayDisk(
if (pointOnPlane.GetDistance(diskCenter) < diskRadius)
{
t = planeIntersectionDistance;
return 1;
return true;
}
}
return 0;
return false;
}
// Reference: Real-Time Collision Detection - 5.3.7 Intersecting Ray or Segment Against Cylinder, and the book's errata.
@ -1012,7 +1012,7 @@ int AZ::Intersect::IntersectRayQuad(
}
// reference: Real-Time Collision Detection, 5.3.3 Intersecting Ray or Segment Against Box
int AZ::Intersect::IntersectRayBox(
bool AZ::Intersect::IntersectRayBox(
const Vector3& rayOrigin, const Vector3& rayDir, const Vector3& boxCenter, const Vector3& boxAxis1,
const Vector3& boxAxis2, const Vector3& boxAxis3, float boxHalfExtent1, float boxHalfExtent2, float boxHalfExtent3, float& t)
{
@ -1044,7 +1044,7 @@ int AZ::Intersect::IntersectRayBox(
// If the ray is parallel to the slab and the ray origin is outside, return no intersection.
if (tp < 0.0f || tn < 0.0f)
{
return 0;
return false;
}
}
else
@ -1065,7 +1065,7 @@ int AZ::Intersect::IntersectRayBox(
tmax = AZ::GetMin(tmax, t2);
if (tmin > tmax)
{
return 0;
return false;
}
}
@ -1085,7 +1085,7 @@ int AZ::Intersect::IntersectRayBox(
// If the ray is parallel to the slab and the ray origin is outside, return no intersection.
if (tp < 0.0f || tn < 0.0f)
{
return 0;
return false;
}
}
else
@ -1106,7 +1106,7 @@ int AZ::Intersect::IntersectRayBox(
tmax = AZ::GetMin(tmax, t2);
if (tmin > tmax)
{
return 0;
return false;
}
}
@ -1126,7 +1126,7 @@ int AZ::Intersect::IntersectRayBox(
// If the ray is parallel to the slab and the ray origin is outside, return no intersection.
if (tp < 0.0f || tn < 0.0f)
{
return 0;
return false;
}
}
else
@ -1147,15 +1147,15 @@ int AZ::Intersect::IntersectRayBox(
tmax = AZ::GetMin(tmax, t2);
if (tmin > tmax)
{
return 0;
return false;
}
}
t = (isRayOriginInsideBox ? tmax : tmin);
return 1;
return true;
}
int AZ::Intersect::IntersectRayObb(const Vector3& rayOrigin, const Vector3& rayDir, const Obb& obb, float& t)
bool AZ::Intersect::IntersectRayObb(const Vector3& rayOrigin, const Vector3& rayDir, const Obb& obb, float& t)
{
return AZ::Intersect::IntersectRayBox(rayOrigin, rayDir, obb.GetPosition(),
obb.GetAxisX(), obb.GetAxisY(), obb.GetAxisZ(),
@ -1166,7 +1166,7 @@ int AZ::Intersect::IntersectRayObb(const Vector3& rayOrigin, const Vector3& rayD
// IntersectSegmentCylinder
// [10/21/2009]
//=========================================================================
int
CylinderIsectTypes
AZ::Intersect::IntersectSegmentCylinder(
const Vector3& sa, const Vector3& dir, const Vector3& p, const Vector3& q, const float r, float& t)
{
@ -1225,7 +1225,7 @@ AZ::Intersect::IntersectSegmentCylinder(
return RR_ISECT_RAY_CYL_NONE; // No real roots; no intersection
}
t = (-b - Sqrt(discr)) / a;
int result = RR_ISECT_RAY_CYL_PQ; // default along the PQ segment
CylinderIsectTypes result = RR_ISECT_RAY_CYL_PQ; // default along the PQ segment
if (md + t * nd < 0.0f)
{
@ -1294,7 +1294,7 @@ AZ::Intersect::IntersectSegmentCylinder(
// IntersectSegmentCapsule
// [10/21/2009]
//=========================================================================
int
CapsuleIsectTypes
AZ::Intersect::IntersectSegmentCapsule(const Vector3& sa, const Vector3& dir, const Vector3& p, const Vector3& q, const float r, float& t)
{
int result = IntersectSegmentCylinder(sa, dir, p, q, r, t);
@ -1361,13 +1361,13 @@ AZ::Intersect::IntersectSegmentCapsule(const Vector3& sa, const Vector3& dir, co
// IntersectSegmentPolyhedron
// [10/21/2009]
//=========================================================================
int
bool
AZ::Intersect::IntersectSegmentPolyhedron(
const Vector3& sa, const Vector3& sBA, const Plane p[], int numPlanes,
const Vector3& sa, const Vector3& dir, const Plane p[], int numPlanes,
float& tfirst, float& tlast, int& iFirstPlane, int& iLastPlane)
{
// Compute direction vector for the segment
Vector3 d = /*b - a*/ sBA;
Vector3 d = /*b - a*/ dir;
// Set initial interval to being the whole segment. For a ray, tlast should be
// set to +RR_FLT_MAX. For a line, additionally tfirst should be set to -RR_FLT_MAX
tfirst = 0.0f;
@ -1388,7 +1388,7 @@ AZ::Intersect::IntersectSegmentPolyhedron(
// If so, return "no intersection" if segment lies outside plane
if (dist < 0.0f)
{
return 0;
return false;
}
}
else
@ -1417,7 +1417,7 @@ AZ::Intersect::IntersectSegmentPolyhedron(
// Exit with "no intersection" if intersection becomes empty
if (tfirst > tlast)
{
return 0;
return false;
}
}
}
@ -1425,11 +1425,11 @@ AZ::Intersect::IntersectSegmentPolyhedron(
//DBG_Assert(iFirstPlane!=-1&&iLastPlane!=-1,("We have some bad border case to have only one plane, fix this function!"));
if (iFirstPlane == -1 && iLastPlane == -1)
{
return 0;
return false;
}
// A nonzero logical intersection, so the segment intersects the polyhedron
return 1;
return true;
}
//=========================================================================
@ -1442,7 +1442,7 @@ AZ::Intersect::ClosestSegmentSegment(
const Vector3& segment2Start, const Vector3& segment2End,
float& segment1Proportion, float& segment2Proportion,
Vector3& closestPointSegment1, Vector3& closestPointSegment2,
float epsilon /*= 1e-4f*/ )
float epsilon)
{
const Vector3 segment1 = segment1End - segment1Start;
const Vector3 segment2 = segment2End - segment2Start;

@ -5,363 +5,398 @@
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef AZCORE_MATH_SEGMENT_INTERSECTION_H
#define AZCORE_MATH_SEGMENT_INTERSECTION_H
#pragma once
#include <AzCore/Math/Vector3.h>
#include <AzCore/Math/Aabb.h>
#include <AzCore/Math/Obb.h>
#include <AzCore/Math/Plane.h>
/// \file isect_segment.h
#include <AzCore/Math/Vector3.h>
namespace AZ
{
namespace Intersect
{
//! LineToPointDistanceTime computes the time of the shortest distance from point 'p' to segment (s1,s2).
//! To calculate the point of intersection:
//! P = s1 + u (s2 - s1)
//! @param s1 segment start point
//! @param s2 segment end point
//! @param p point to find the closest time to.
//! @return time (on the segment) for the shortest distance from 'p' to (s1,s2) [0.0f (s1),1.0f (s2)]
inline float LineToPointDistanceTime(const Vector3& s1, const Vector3& s21, const Vector3& p)
{
// so u = (p.x - s1.x)*(s2.x - s1.x) + (p.y - s1.y)*(s2.y - s1.y) + (p.z-s1.z)*(s2.z-s1.z) / |s2-s1|^2
return s21.Dot(p - s1) / s21.Dot(s21);
}
//! To calculate the point of intersection: P = s1 + u (s2 - s1)
//! @param s1 Segment start point.
//! @param s2 Segment end point.
//! @param p Point to find the closest time to.
//! @return Time (on the segment) for the shortest distance from 'p' to (s1,s2) [0.0f (s1),1.0f (s2)]
float LineToPointDistanceTime(const Vector3& s1, const Vector3& s21, const Vector3& p);
//! LineToPointDistance computes the closest point to 'p' from a segment (s1,s2).
//! @param s1 segment start point
//! @param s2 segment end point
//! @param p point to find the closest time to.
//! @param u time (on the segment) for the shortest distance from 'p' to (s1,s2) [0.0f (s1),1.0f (s2)]
//! @return the closest point
inline Vector3 LineToPointDistance(const Vector3& s1, const Vector3& s2, const Vector3& p, float& u)
{
const Vector3 s21 = s2 - s1;
// we assume seg1 and seg2 are NOT coincident
AZ_MATH_ASSERT(!s21.IsClose(Vector3(0.0f), 1e-4f), "OK we agreed that we will pass valid segments! (s1 != s2)");
u = LineToPointDistanceTime(s1, s21, p);
return s1 + u * s21;
}
//! @param s1 Segment start point
//! @param s2 Segment end point
//! @param p Point to find the closest time to.
//! @param u Time (on the segment) for the shortest distance from 'p' to (s1,s2) [0.0f (s1),1.0f (s2)]
//! @return The closest point
Vector3 LineToPointDistance(const Vector3& s1, const Vector3& s2, const Vector3& p, float& u);
//! Given segment pq and triangle abc (CCW), returns whether segment intersects
//! triangle and if so, also returns the barycentric coordinates (u,v,w)
//! of the intersection point.
//! @param p segment start point
//! @param q segment end point
//! @param a triangle point 1
//! @param b triangle point 2
//! @param c triangle point 3
//! @param normal at the intersection point.
//! @param t time of intersection along the segment [0.0 (p), 1.0 (q)]
//! @return 1 if the segment intersects the triangle otherwise 0
int IntersectSegmentTriangleCCW(
const Vector3& p, const Vector3& q, const Vector3& a, const Vector3& b, const Vector3& c,
/*float &u, float &v, float &w,*/ Vector3& normal, float& t);
//! @param p Segment start point.
//! @param q Segment end point.
//! @param a Triangle point 1.
//! @param b Triangle point 2.
//! @param c Triangle point 3.
//! @param normal At the intersection point.
//! @param t Time of intersection along the segment [0.0 (p), 1.0 (q)].
//! @return true if the segments intersects the triangle otherwise false.
bool IntersectSegmentTriangleCCW(
const Vector3& p, const Vector3& q, const Vector3& a, const Vector3& b, const Vector3& c, Vector3& normal, float& t);
//! Same as \ref IntersectSegmentTriangleCCW without respecting the triangle (a,b,c) vertex order (double sided).
int IntersectSegmentTriangle(
const Vector3& p, const Vector3& q, const Vector3& a, const Vector3& b, const Vector3& c,
/*float &u, float &v, float &w,*/ Vector3& normal, float& t);
//! @param p Segment start point.
//! @param q Segment end point.
//! @param a Triangle point 1.
//! @param b Triangle point 2.
//! @param c Triangle point 3.
//! @param normal At the intersection point.
//! @param t Time of intersection along the segment [0.0 (p), 1.0 (q)].
//! @return True if the segments intersects the triangle otherwise false.
bool IntersectSegmentTriangle(
const Vector3& p, const Vector3& q, const Vector3& a, const Vector3& b, const Vector3& c, Vector3& normal, float& t);
//! Ray aabb intersection result types.
enum RayAABBIsectTypes
enum RayAABBIsectTypes : AZ::s32
{
ISECT_RAY_AABB_NONE = 0, ///< no intersection
ISECT_RAY_AABB_SA_INSIDE, ///< the ray starts inside the aabb
ISECT_RAY_AABB_ISECT, ///< intersects along the PQ segment
ISECT_RAY_AABB_NONE = 0, ///< no intersection
ISECT_RAY_AABB_SA_INSIDE, ///< the ray starts inside the aabb
ISECT_RAY_AABB_ISECT, ///< intersects along the PQ segment
};
//! Intersect ray R(t) = rayStart + t*d against AABB a. When intersecting,
//! return intersection distance tmin and point q of intersection.
//! @param rayStart ray starting point
//! @param dir ray direction and length (dir = rayEnd - rayStart)
//! @param dirRCP 1/dir (reciprocal direction - we cache this result very often so we don't need to compute it multiple times, otherwise just use dir.GetReciprocal())
//! @param rayStart Ray starting point
//! @param dir Ray direction and length (dir = rayEnd - rayStart)
//! @param dirRCP 1/dir (reciprocal direction - we cache this result very often so we don't need to compute it multiple times,
//! otherwise just use dir.GetReciprocal())
//! @param aabb Axis aligned bounding box to intersect against
//! @param tStart time on ray of the first intersection [0,1] or 0 if the ray starts inside the aabb - check the return value
//! @param tEnd time of the of the second intersection [0,1] (it can be > 1 if intersects after the rayEnd)
//! @param startNormal normal at the start point.
//! @param tStart Time on ray of the first intersection [0,1] or 0 if the ray starts inside the aabb - check the return value
//! @param tEnd Time of the of the second intersection [0,1] (it can be > 1 if intersects after the rayEnd)
//! @param startNormal Normal at the start point.
//! @return \ref RayAABBIsectTypes
int IntersectRayAABB(
const Vector3& rayStart, const Vector3& dir, const Vector3& dirRCP, const Aabb& aabb,
float& tStart, float& tEnd, Vector3& startNormal /*, Vector3& inter*/);
RayAABBIsectTypes IntersectRayAABB(
const Vector3& rayStart,
const Vector3& dir,
const Vector3& dirRCP,
const Aabb& aabb,
float& tStart,
float& tEnd,
Vector3& startNormal);
//! Intersect ray against AABB.
//! @param rayStart ray starting point.
//! @param dir ray reciprocal direction.
//! @param rayStart Ray starting point.
//! @param dir Ray reciprocal direction.
//! @param aabb Axis aligned bounding box to intersect against.
//! @param start length on ray of the first intersection.
//! @param end length of the of the second intersection.
//! @return \ref RayAABBIsectTypes In this faster version than IntersectRayAABB we return only ISECT_RAY_AABB_NONE and ISECT_RAY_AABB_ISECT.
//! You can check yourself for that case.
int IntersectRayAABB2(
const Vector3& rayStart, const Vector3& dirRCP, const Aabb& aabb,
float& start, float& end);
//! @param start Length on ray of the first intersection.
//! @param end Length of the of the second intersection.
//! @return \ref RayAABBIsectTypes In this faster version than IntersectRayAABB we return only ISECT_RAY_AABB_NONE and
//! ISECT_RAY_AABB_ISECT. You can check yourself for that case.
RayAABBIsectTypes IntersectRayAABB2(const Vector3& rayStart, const Vector3& dirRCP, const Aabb& aabb, float& start, float& end);
//! Clip a ray to an aabb. return true if ray was clipped. The ray
//! can be inside so don't use the result if the ray intersect the box.
inline int ClipRayWithAabb(
const Aabb& aabb, Vector3& rayStart, Vector3& rayEnd, float& tClipStart, float& tClipEnd)
{
Vector3 startNormal;
float tStart, tEnd;
Vector3 dirLen = rayEnd - rayStart;
if (IntersectRayAABB(rayStart, dirLen, dirLen.GetReciprocal(), aabb, tStart, tEnd, startNormal) != ISECT_RAY_AABB_NONE)
{
// clip the ray with the box
if (tStart > 0.0f)
{
rayStart = rayStart + tStart * dirLen;
tClipStart = tStart;
}
if (tEnd < 1.0f)
{
rayEnd = rayStart + tEnd * dirLen;
tClipEnd = tEnd;
}
return 1;
}
return 0;
}
//! @param aabb Bounds to test against.
//! @param rayStart The start of the ray.
//! @param rayEnd The end of the ray.
//! @param[out] tClipStart The proportion where the ray enters the \ref Aabb.
//! @param[out] tClipEnd The proportion where the ray exits the \ref Aabb.
//! @return True if the ray was clipped, otherwise false.
bool ClipRayWithAabb(const Aabb& aabb, Vector3& rayStart, Vector3& rayEnd, float& tClipStart, float& tClipEnd);
//! Test segment and aabb where the segment is defined by midpoint
//! midPoint = (p1-p0) * 0.5f and half vector halfVector = p1 - midPoint.
//! the aabb is at the origin and defined by half extents only.
//! @return 1 if the intersect, otherwise 0.
int TestSegmentAABBOrigin(const Vector3& midPoint, const Vector3& halfVector, const Vector3& aabbExtends);
//! 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);
}
//! @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.
//! @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 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);
//! @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);
//! 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);
}
}
#endif // AZCORE_MATH_SEGMENT_INTERSECTION_H
#pragma once
const Vector3& point,
const Vector3& segmentStart,
const Vector3& segmentEnd,
float& proportion,
Vector3& closestPointOnSegment);
} // namespace Intersect
} // namespace AZ
#include <AzCore/Math/IntersectSegment.inl>

@ -0,0 +1,101 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
namespace AZ
{
namespace Intersect
{
AZ_MATH_INLINE bool ClipRayWithAabb(const Aabb& aabb, Vector3& rayStart, Vector3& rayEnd, float& tClipStart, float& tClipEnd)
{
Vector3 startNormal;
float tStart, tEnd;
Vector3 dirLen = rayEnd - rayStart;
if (IntersectRayAABB(rayStart, dirLen, dirLen.GetReciprocal(), aabb, tStart, tEnd, startNormal) != ISECT_RAY_AABB_NONE)
{
// clip the ray with the box
if (tStart > 0.0f)
{
rayStart = rayStart + tStart * dirLen;
tClipStart = tStart;
}
if (tEnd < 1.0f)
{
rayEnd = rayStart + tEnd * dirLen;
tClipEnd = tEnd;
}
return true;
}
return false;
}
AZ_MATH_INLINE SphereIsectTypes
IntersectRaySphereOrigin(const Vector3& rayStart, const Vector3& rayDirNormalized, const float sphereRadius, float& t)
{
Vector3 m = rayStart;
float b = m.Dot(rayDirNormalized);
float c = m.Dot(m) - sphereRadius * sphereRadius;
// Exit if r's origin outside s (c > 0)and r pointing away from s (b > 0)
if (c > 0.0f && b > 0.0f)
{
return ISECT_RAY_SPHERE_NONE;
}
float discr = b * b - c;
// A negative discriminant corresponds to ray missing sphere
if (discr < 0.0f)
{
return ISECT_RAY_SPHERE_NONE;
}
// Ray now found to intersect sphere, compute smallest t value of intersection
t = -b - Sqrt(discr);
// If t is negative, ray started inside sphere so clamp t to zero
if (t < 0.0f)
{
// t = 0.0f;
return ISECT_RAY_SPHERE_SA_INSIDE; // no hit if inside
}
// q = p + t * d;
return ISECT_RAY_SPHERE_ISECT;
}
AZ_MATH_INLINE SphereIsectTypes IntersectRaySphere(const Vector3& rayStart, const Vector3& rayDirNormalized, const Vector3& sphereCenter, const float sphereRadius, float& t)
{
return IntersectRaySphereOrigin(rayStart - sphereCenter, rayDirNormalized, sphereRadius, t);
}
AZ_MATH_INLINE Vector3 LineToPointDistance(const Vector3& s1, const Vector3& s2, const Vector3& p, float& u)
{
const Vector3 s21 = s2 - s1;
// we assume seg1 and seg2 are NOT coincident
AZ_MATH_ASSERT(!s21.IsClose(Vector3(0.0f), 1e-4f), "OK we agreed that we will pass valid segments! (s1 != s2)");
u = LineToPointDistanceTime(s1, s21, p);
return s1 + u * s21;
}
AZ_MATH_INLINE float LineToPointDistanceTime(const Vector3& s1, const Vector3& s21, const Vector3& p)
{
// so u = (p.x - s1.x)*(s2.x - s1.x) + (p.y - s1.y)*(s2.y - s1.y) + (p.z-s1.z)*(s2.z-s1.z) / |s2-s1|^2
return s21.Dot(p - s1) / s21.Dot(s21);
}
AZ_MATH_INLINE bool TestSegmentAABB(const Vector3& p0, const Vector3& p1, const Aabb& aabb)
{
Vector3 e = aabb.GetExtents();
Vector3 d = p1 - p0;
Vector3 m = p0 + p1 - aabb.GetMin() - aabb.GetMax();
return TestSegmentAABBOrigin(m, d, e);
}
} // namespace Intersect
} // namespace AZ

@ -282,6 +282,7 @@ set(FILES
Math/Internal/VertexContainer.inl
Math/InterpolationSample.h
Math/IntersectPoint.h
Math/IntersectSegment.inl
Math/IntersectSegment.cpp
Math/IntersectSegment.h
Math/MathIntrinsics.h

@ -17,6 +17,11 @@ namespace AZ
return false;
}
unsigned int StackConverter::FromNative(StackFrame*, unsigned int, void*)
{
return 0;
}
void SymbolStorage::LoadModuleData(const void*, unsigned int)
{}

@ -78,6 +78,12 @@ StackRecorder::Record(StackFrame* frames, unsigned int maxNumOfFrames, unsigned
return count;
}
unsigned int StackConverter::FromNative([[maybe_unused]] StackFrame* frames, [[maybe_unused]] unsigned int maxNumOfFrames, [[maybe_unused]] void* nativeContext)
{
AZ_Assert(false, "StackConverter::FromNative() is not supported for UnixLike platform yet");
return 0;
}
void
SymbolStorage::DecodeFrames(const StackFrame* frames, unsigned int numFrames, StackLine* textLines)
{

@ -1048,9 +1048,9 @@ cleanup:
unsigned int
StackRecorder::Record(StackFrame* frames, unsigned int maxNumOfFrames, unsigned int suppressCount, void* nativeThread)
{
#if defined(AZ_ENABLE_DEBUG_TOOLS)
unsigned int numFrames = 0;
#if defined(AZ_ENABLE_DEBUG_TOOLS)
if (nativeThread == NULL)
{
++suppressCount; // Skip current call
@ -1079,9 +1079,8 @@ cleanup:
STACKFRAME64 sf;
memset(&sf, 0, sizeof(STACKFRAME64));
DWORD imageType;
DWORD imageType = IMAGE_FILE_MACHINE_AMD64;
imageType = IMAGE_FILE_MACHINE_AMD64;
sf.AddrPC.Offset = context.Rip;
sf.AddrPC.Mode = AddrModeFlat;
sf.AddrFrame.Offset = context.Rsp;
@ -1090,8 +1089,7 @@ cleanup:
sf.AddrStack.Mode = AddrModeFlat;
EnterCriticalSection(&g_csDbgHelpDll);
s32 frame = -(s32)suppressCount;
for (; frame < (s32)maxNumOfFrames; ++frame)
for (s32 frame = -static_cast<s32>(suppressCount); frame < static_cast<s32>(maxNumOfFrames); ++frame)
{
if (!g_StackWalk64(imageType, g_currentProcess, hThread, &sf, &context, 0, g_SymFunctionTableAccess64, g_SymGetModuleBase64, 0))
{
@ -1111,15 +1109,68 @@ cleanup:
}
LeaveCriticalSection(&g_csDbgHelpDll);
}
return numFrames;
}
#else
(void)frames;
(void)maxNumOfFrames;
(void)suppressCount;
(void)nativeThread;
return 0;
AZ_UNUSED(frames);
AZ_UNUSED(maxNumOfFrames);
AZ_UNUSED(suppressCount);
AZ_UNUSED(nativeThread);
#endif // AZ_ENABLE_DEBUG_TOOLS
return numFrames;
}
unsigned int StackConverter::FromNative(StackFrame* frames, unsigned int maxNumOfFrames, void* nativeContext)
{
unsigned int numFrames = 0;
#if defined(AZ_ENABLE_DEBUG_TOOLS)
if (!g_dbgHelpLoaded)
{
LoadDbgHelp();
}
HANDLE hThread;
DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &hThread, 0, false, DUPLICATE_SAME_ACCESS);
PCONTEXT nativeContextType = reinterpret_cast<PCONTEXT>(nativeContext);
STACKFRAME64 sf;
memset(&sf, 0, sizeof(STACKFRAME64));
DWORD imageType = IMAGE_FILE_MACHINE_AMD64;
sf.AddrPC.Offset = nativeContextType->Rip;
sf.AddrPC.Mode = AddrModeFlat;
sf.AddrFrame.Offset = nativeContextType->Rsp;
sf.AddrFrame.Mode = AddrModeFlat;
sf.AddrStack.Offset = nativeContextType->Rsp;
sf.AddrStack.Mode = AddrModeFlat;
EnterCriticalSection(&g_csDbgHelpDll);
for (unsigned int frame = 0; frame < maxNumOfFrames; ++frame)
{
if (!g_StackWalk64(imageType, g_currentProcess, hThread, &sf, nativeContext, 0, g_SymFunctionTableAccess64, g_SymGetModuleBase64, 0))
{
break;
}
if (sf.AddrPC.Offset == sf.AddrReturn.Offset)
{
// "StackWalk64-Endless-Callstack!"
break;
}
frames[numFrames++].m_programCounter = sf.AddrPC.Offset;
}
LeaveCriticalSection(&g_csDbgHelpDll);
#else
AZ_UNUSED(frames);
AZ_UNUSED(maxNumOfFrames);
AZ_UNUSED(nativeContext);
#endif
return numFrames;
}
//////////////////////////////////////////////////////////////////////////

@ -1413,7 +1413,7 @@ namespace UnitTest
>;
TYPED_TEST_CASE(HashedSetDifferentAllocatorFixture, SetTemplateConfigs);
#if GTEST_OS_SUPPORTS_DEATH_TEST
#if GTEST_HAS_DEATH_TEST
TYPED_TEST(HashedSetDifferentAllocatorFixture, InsertNodeHandleWithDifferentAllocatorsLogsTraceMessages)
{
using ContainerType = typename TypeParam::ContainerType;
@ -1435,7 +1435,7 @@ namespace UnitTest
}
}, ".*");
}
#endif // GTEST_OS_SUPPORTS_DEATH_TEST
#endif // GTEST_HAS_DEATH_TEST
template<typename ContainerType>
class HashedMapContainers
@ -1811,7 +1811,7 @@ namespace UnitTest
>;
TYPED_TEST_CASE(HashedMapDifferentAllocatorFixture, MapTemplateConfigs);
#if GTEST_OS_SUPPORTS_DEATH_TEST
#if GTEST_HAS_DEATH_TEST
TYPED_TEST(HashedMapDifferentAllocatorFixture, InsertNodeHandleWithDifferentAllocatorsLogsTraceMessages)
{
using ContainerType = typename TypeParam::ContainerType;
@ -1833,7 +1833,7 @@ namespace UnitTest
}
} , ".*");
}
#endif // GTEST_OS_SUPPORTS_DEATH_TEST
#endif // GTEST_HAS_DEATH_TEST
namespace HashedContainerTransparentTestInternal
{

@ -1095,7 +1095,7 @@ namespace UnitTest
>;
TYPED_TEST_CASE(TreeSetDifferentAllocatorFixture, SetTemplateConfigs);
#if GTEST_OS_SUPPORTS_DEATH_TEST
#if GTEST_HAS_DEATH_TEST
TYPED_TEST(TreeSetDifferentAllocatorFixture, InsertNodeHandleWithDifferentAllocatorsLogsTraceMessages)
{
using ContainerType = typename TypeParam::ContainerType;
@ -1117,7 +1117,7 @@ namespace UnitTest
}
}, ".*");
}
#endif // GTEST_OS_SUPPORTS_DEATH_TEST
#endif // GTEST_HAS_DEATH_TEST
TYPED_TEST(TreeSetDifferentAllocatorFixture, SwapMovesElementsWhenAllocatorsDiffer)
{
@ -1516,7 +1516,7 @@ namespace UnitTest
>;
TYPED_TEST_CASE(TreeMapDifferentAllocatorFixture, MapTemplateConfigs);
#if GTEST_OS_SUPPORTS_DEATH_TEST
#if GTEST_HAS_DEATH_TEST
TYPED_TEST(TreeMapDifferentAllocatorFixture, InsertNodeHandleWithDifferentAllocatorsLogsTraceMessages)
{
using ContainerType = typename TypeParam::ContainerType;
@ -1538,7 +1538,7 @@ namespace UnitTest
}
}, ".*");
}
#endif // GTEST_OS_SUPPORTS_DEATH_TEST
#endif // GTEST_HAS_DEATH_TEST
TYPED_TEST(TreeMapDifferentAllocatorFixture, SwapMovesElementsWhenAllocatorsDiffer)
{

@ -1595,7 +1595,7 @@ namespace UnitTest
}
};
#if GTEST_OS_SUPPORTS_DEATH_TEST
#if GTEST_HAS_DEATH_TEST
TEST_F(ThreadEventsDeathTest, UsingClientBus_AvoidsDeadlock)
{
EXPECT_EXIT(
@ -1608,5 +1608,5 @@ namespace UnitTest
, ::testing::ExitedWithCode(0),".*");
}
#endif // GTEST_OS_SUPPORTS_DEATH_TEST
#endif // GTEST_HAS_DEATH_TEST
}

@ -736,7 +736,10 @@ namespace UnitTest
auto& assetManager = AssetManager::Instance();
AssetBusCallbacks callbacks{};
AZ_PUSH_DISABLE_WARNING(5233, "-Wunknown-warning-option") // Older versions of MSVC toolchain require to pass constexpr in the
// capture. Newer versions issue unused warning
callbacks.SetOnAssetReadyCallback([&, AssetNoRefB](const Asset<AssetData>&, AssetBusCallbacks&)
AZ_POP_DISABLE_WARNING
{
// This callback should run inside the "main thread" dispatch events loop
auto loadAsset = assetManager.GetAsset<AssetWithSerializedData>(AZ::Uuid(AssetNoRefB), AssetLoadBehavior::Default);

@ -288,6 +288,21 @@ namespace AZ
AZStd::string completeCommand = console->AutoCompleteCommand("testVec3");
AZ_TEST_ASSERT(completeCommand == "testVec3");
}
// Duplicate names
{
// Register two cvars with the same name
auto id = AZ::TypeId();
auto flag = AZ::ConsoleFunctorFlags::Null;
auto signature = AZ::ConsoleFunctor<void, false>::FunctorSignature();
AZ::ConsoleFunctor<void, false> cvarOne(*console, "testAutoCompleteDuplication", "", flag, id, signature);
AZ::ConsoleFunctor<void, false> cvarTwo(*console, "testAutoCompleteDuplication", "", flag, id, signature);
// Autocomplete given name expecting one match (not two)
AZStd::vector<AZStd::string> matches;
AZStd::string completeCommand = console->AutoCompleteCommand("testAutoCompleteD", &matches);
AZ_TEST_ASSERT(matches.size() == 1 && completeCommand == "testAutoCompleteDuplication");
}
}
TEST_F(ConsoleTests, ConsoleFunctor_FreeFunctorExecutionTest)

@ -109,7 +109,10 @@ namespace AZ::Debug
AZStd::thread threads[totalThreads];
for (size_t threadIndex = 0; threadIndex < totalThreads; ++threadIndex)
{
AZ_PUSH_DISABLE_WARNING(5233, "-Wunknown-warning-option") // Older versions of MSVC toolchain require to pass constexpr in the
// capture. Newer versions issue unused warning
threads[threadIndex] = AZStd::thread([&startLogging, &messages]()
AZ_POP_DISABLE_WARNING
{
while (!startLogging)
{
@ -226,7 +229,10 @@ namespace AZ::Debug
AZStd::thread threads[totalThreads];
for (size_t threadIndex = 0; threadIndex < totalThreads; ++threadIndex)
{
AZ_PUSH_DISABLE_WARNING(5233, "-Wunknown-warning-option") // Older versions of MSVC toolchain require to pass constexpr in the
// capture. Newer versions issue unused warning
threads[threadIndex] = AZStd::thread([&startLogging, &message, &totalRecordsWritten]()
AZ_POP_DISABLE_WARNING
{
AZ_UNUSED(message);

@ -0,0 +1,31 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzCore/UnitTest/TestTypes.h>
namespace UnitTest
{
class UnhandledExceptions
: public ScopedAllocatorSetupFixture
{
public:
void causeAccessViolation()
{
int* someVariable = reinterpret_cast<int*>(0);
*someVariable = 0;
}
};
#if GTEST_HAS_DEATH_TEST
TEST_F(UnhandledExceptions, Handle)
{
EXPECT_DEATH(causeAccessViolation(), "");
}
#endif
}

@ -144,14 +144,13 @@ namespace UnitTest
}
};
#if GTEST_OS_SUPPORTS_DEATH_TEST
// SPEC-2669: Disabled since it is causing hangs on Linux
#if GTEST_HAS_DEATH_TEST
TEST_F(AllocatorsTestFixtureLeakDetectionDeathTest_SKIPCODECOVERAGE, AllocatorLeak)
{
// testing that the TraceBusHook will fail on cause the test to die
EXPECT_DEATH(TestAllocatorLeak(), "");
}
#endif // GTEST_OS_SUPPORTS_DEATH_TEST
#endif // GTEST_HAS_DEATH_TEST
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Testing ScopedAllocatorSetupFixture. Testing that detects leaks

@ -597,7 +597,10 @@ namespace AZ::IO
path.InitFromAbsolutePath(m_dummyFilepath);
request->CreateRead(nullptr, buffer.get(), fileSize, path, 0, fileSize);
AZ_PUSH_DISABLE_WARNING(5233, "-Wunknown-warning-option") // Older versions of MSVC toolchain require to pass constexpr in the
// capture. Newer versions issue unused warning
auto callback = [&fileSize, this](const FileRequest& request)
AZ_POP_DISABLE_WARNING
{
EXPECT_EQ(request.GetStatus(), AZ::IO::IStreamerTypes::RequestStatus::Completed);
auto& readRequest = AZStd::get<AZ::IO::FileRequest::ReadData>(request.GetCommand());
@ -639,7 +642,10 @@ namespace AZ::IO
path.InitFromAbsolutePath(m_dummyFilepath);
request->CreateRead(nullptr, buffer, unalignedSize + 4, path, unalignedOffset, unalignedSize);
AZ_PUSH_DISABLE_WARNING(5233, "-Wunknown-warning-option") // Older versions of MSVC toolchain require to pass constexpr in the
// capture. Newer versions issue unused warning
auto callback = [unalignedOffset, unalignedSize, this](const FileRequest& request)
AZ_POP_DISABLE_WARNING
{
EXPECT_EQ(request.GetStatus(), AZ::IO::IStreamerTypes::RequestStatus::Completed);
auto& readRequest = AZStd::get<AZ::IO::FileRequest::ReadData>(request.GetCommand());
@ -784,7 +790,10 @@ namespace AZ::IO
requests[i] = m_context->GetNewInternalRequest();
requests[i]->CreateRead(nullptr, buffers[i].get(), chunkSize, path, i * chunkSize, chunkSize);
AZ_PUSH_DISABLE_WARNING(5233, "-Wunknown-warning-option") // Older versions of MSVC toolchain require to pass constexpr in the
// capture. Newer versions issue unused warning
auto callback = [chunkSize, i](const FileRequest& request)
AZ_POP_DISABLE_WARNING
{
EXPECT_EQ(request.GetStatus(), AZ::IO::IStreamerTypes::RequestStatus::Completed);
auto& readRequest = AZStd::get<AZ::IO::FileRequest::ReadData>(request.GetCommand());
@ -970,7 +979,10 @@ namespace AZ::IO
i * chunkSize
));
AZ_PUSH_DISABLE_WARNING(5233, "-Wunknown-warning-option") // Older versions of MSVC toolchain require to pass constexpr in the
// capture. Newer versions issue unused warning
auto callback = [numChunks, &numCallbacks, &waitForReads](FileRequestHandle request)
AZ_POP_DISABLE_WARNING
{
IStreamer* streamer = Interface<IStreamer>::Get();
if (streamer)
@ -1038,7 +1050,10 @@ namespace AZ::IO
i * chunkSize
));
AZ_PUSH_DISABLE_WARNING(5233, "-Wunknown-warning-option") // Older versions of MSVC toolchain require to pass constexpr in the
// capture. Newer versions issue unused warning
auto callback = [numChunks, &waitForReads, &waitForSingleRead, &numReadCallbacks]([[maybe_unused]] FileRequestHandle request)
AZ_POP_DISABLE_WARNING
{
numReadCallbacks++;
if (numReadCallbacks == 1)
@ -1059,7 +1074,10 @@ namespace AZ::IO
for (size_t i = 0; i < numChunks; ++i)
{
cancels.push_back(m_streamer->Cancel(requests[numChunks - i - 1]));
AZ_PUSH_DISABLE_WARNING(5233, "-Wunknown-warning-option") // Older versions of MSVC toolchain require to pass constexpr in the
// capture. Newer versions issue unused warning
auto callback = [&numCancelCallbacks, &waitForCancels, numChunks](FileRequestHandle request)
AZ_POP_DISABLE_WARNING
{
auto result = Interface<IStreamer>::Get()->GetRequestStatus(request);
EXPECT_EQ(result, IStreamerTypes::RequestStatus::Completed);

@ -327,7 +327,7 @@ namespace JsonSerializationTests
SerializerWithOneType::Unreflect(m_jsonRegistrationContext.get());
}
#if GTEST_OS_SUPPORTS_DEATH_TEST
#if GTEST_HAS_DEATH_TEST
using JsonSerializationDeathTests = JsonRegistrationContextTests;
TEST_F(JsonSerializationDeathTests, DoubleUnregisterSerializer_Asserts)
{
@ -338,5 +338,6 @@ namespace JsonSerializationTests
}, ".*"
);
}
#endif // GTEST_OS_SUPPORTS_DEATH_TEST
#endif // GTEST_HAS_DEATH_TEST
} //namespace JsonSerializationTests

@ -363,7 +363,10 @@ namespace AZ
{
constexpr AZStd::array visitTokens = { "Hello", "World", "", "More", "", "", "Tokens" };
size_t visitIndex{};
AZ_PUSH_DISABLE_WARNING(5233, "-Wunknown-warning-option") // Older versions of MSVC toolchain require to pass constexpr in the
// capture. Newer versions issue unused warning
auto visitor = [&visitIndex, &visitTokens](AZStd::string_view token)
AZ_POP_DISABLE_WARNING
{
if (visitIndex > visitTokens.size())
{
@ -389,7 +392,10 @@ namespace AZ
{
constexpr AZStd::array visitTokens = { "Hello", "World", "", "More", "", "", "Tokens" };
size_t visitIndex = visitTokens.size() - 1;
AZ_PUSH_DISABLE_WARNING(5233, "-Wunknown-warning-option") // Older versions of MSVC toolchain require to pass constexpr in the
// capture. Newer versions issue unused warning
auto visitor = [&visitIndex, &visitTokens](AZStd::string_view token)
AZ_POP_DISABLE_WARNING
{
if (visitIndex > visitTokens.size())
{

@ -72,6 +72,7 @@ set(FILES
Debug/AssetTracking.cpp
Debug/LocalFileEventLoggerTests.cpp
Debug/Trace.cpp
Debug/UnhandledExceptions.cpp
Name/NameJsonSerializerTests.cpp
Name/NameTests.cpp
RTTI/TypeSafeIntegralTests.cpp

@ -29,6 +29,10 @@ namespace AzFramework
AZStd::vector<AZ::IO::Path> m_absoluteSourcePaths; //!< Where the gem's source path folder are located(as an absolute path)
static constexpr const char* GetGemAssetFolder() { return "Assets"; }
static constexpr const char* GetGemRegistryFolder()
{
return "Registry";
}
};
//! Returns a list of GemInfo of all the gems that are active for the for the specified game project.

@ -20,6 +20,7 @@
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Settings/SettingsRegistry.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
@ -190,6 +191,25 @@ namespace AzFramework
////////////////////////////////////////////////////////////////////////////////////////////////
void InputSystemComponent::Activate()
{
const auto* settingsRegistry = AZ::SettingsRegistry::Get();
if (settingsRegistry)
{
AZ::u64 value = 0;
if (settingsRegistry->Get(value, "/O3DE/InputSystem/MouseMovementSampleRateHertz"))
{
m_mouseMovementSampleRateHertz = aznumeric_caster(value);
}
if (settingsRegistry->Get(value, "/O3DE/InputSystem/GamepadsEnabled"))
{
m_gamepadsEnabled = aznumeric_caster(value);
}
settingsRegistry->Get(m_keyboardEnabled, "/O3DE/InputSystem/KeyboardEnabled");
settingsRegistry->Get(m_motionEnabled, "/O3DE/InputSystem/MotionEnabled");
settingsRegistry->Get(m_mouseEnabled, "/O3DE/InputSystem/MouseEnabled");
settingsRegistry->Get(m_touchEnabled, "/O3DE/InputSystem/TouchEnabled");
settingsRegistry->Get(m_virtualKeyboardEnabled, "/O3DE/InputSystem/VirtualKeyboardEnabled");
}
// Create all enabled input devices
CreateEnabledInputDevices();

@ -1,234 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/Math/Matrix3x3.h>
#include <AzCore/Math/Vector3.h>
#include <AzCore/Math/Aabb.h>
#include <AzFramework/Physics/WorldBody.h>
#include <AzFramework/Physics/ShapeConfiguration.h>
namespace
{
class ReflectContext;
}
namespace Physics
{
class ShapeConfiguration;
class World;
class Shape;
/// Default values used for initializing RigidBodySettings.
/// These can be modified by Physics Implementation gems. // O3DE_DEPRECATED(LY-114472) - DefaultRigidBodyConfiguration values are not shared across modules.
// Use RigidBodyConfiguration default values.
struct DefaultRigidBodyConfiguration
{
static float m_mass;
static bool m_computeInertiaTensor;
static float m_linearDamping;
static float m_angularDamping;
static float m_sleepMinEnergy;
static float m_maxAngularVelocity;
};
enum class MassComputeFlags : AZ::u8
{
NONE = 0,
//! Flags indicating whether a certain mass property should be auto-computed or not.
COMPUTE_MASS = 1,
COMPUTE_INERTIA = 1 << 1,
COMPUTE_COM = 1 << 2,
//! If set, non-simulated shapes will also be included in the mass properties calculation.
INCLUDE_ALL_SHAPES = 1 << 3,
DEFAULT = COMPUTE_COM | COMPUTE_INERTIA | COMPUTE_MASS
};
class RigidBodyConfiguration
: public WorldBodyConfiguration
{
public:
AZ_CLASS_ALLOCATOR(RigidBodyConfiguration, AZ::SystemAllocator, 0);
AZ_RTTI(RigidBodyConfiguration, "{ACFA8900-8530-4744-AF00-AA533C868A8E}", WorldBodyConfiguration);
static void Reflect(AZ::ReflectContext* context);
enum PropertyVisibility : AZ::u16
{
InitialVelocities = 1 << 0, ///< Whether the initial linear and angular velocities are visible.
InertiaProperties = 1 << 1, ///< Whether the whole category of inertia properties (mass, compute inertia,
///< inertia tensor etc) is visible.
Damping = 1 << 2, ///< Whether linear and angular damping are visible.
SleepOptions = 1 << 3, ///< Whether the sleep threshold and start asleep options are visible.
Interpolation = 1 << 4, ///< Whether the interpolation option is visible.
Gravity = 1 << 5, ///< Whether the effected by gravity option is visible.
Kinematic = 1 << 6, ///< Whether the option to make the body kinematic is visible.
ContinuousCollisionDetection = 1 << 7, ///< Whether the option to enable continuous collision detection is visible.
MaxVelocities = 1 << 8 ///< Whether upper limits on velocities are visible.
};
RigidBodyConfiguration() = default;
RigidBodyConfiguration(const RigidBodyConfiguration& settings) = default;
// Visibility functions.
AZ::Crc32 GetPropertyVisibility(PropertyVisibility property) const;
void SetPropertyVisibility(PropertyVisibility property, bool isVisible);
AZ::Crc32 GetInitialVelocitiesVisibility() const;
/// Returns whether the whole category of inertia settings (mass, inertia, center of mass offset etc) is visible.
AZ::Crc32 GetInertiaSettingsVisibility() const;
/// Returns whether the individual inertia tensor field is visible or is hidden because the compute inertia option is selected.
AZ::Crc32 GetInertiaVisibility() const;
/// Returns whether the mass field is visible or is hidden because compute mass option is selected.
AZ::Crc32 GetMassVisibility() const;
/// Returns whether the individual centre of mass offset field is visible or is hidden because compute CoM option is selected.
AZ::Crc32 GetCoMVisibility() const;
AZ::Crc32 GetDampingVisibility() const;
AZ::Crc32 GetSleepOptionsVisibility() const;
AZ::Crc32 GetInterpolationVisibility() const;
AZ::Crc32 GetGravityVisibility() const;
AZ::Crc32 GetKinematicVisibility() const;
AZ::Crc32 GetCCDVisibility() const;
AZ::Crc32 GetMaxVelocitiesVisibility() const;
MassComputeFlags GetMassComputeFlags() const;
void SetMassComputeFlags(MassComputeFlags flags);
bool IsCCDEnabled() const;
// Basic initial settings.
AZ::Vector3 m_initialLinearVelocity = AZ::Vector3::CreateZero();
AZ::Vector3 m_initialAngularVelocity = AZ::Vector3::CreateZero();
AZ::Vector3 m_centerOfMassOffset = AZ::Vector3::CreateZero();
// Simulation parameters.
float m_mass = DefaultRigidBodyConfiguration::m_mass;
AZ::Matrix3x3 m_inertiaTensor = AZ::Matrix3x3::CreateIdentity();
float m_linearDamping = DefaultRigidBodyConfiguration::m_linearDamping;
float m_angularDamping = DefaultRigidBodyConfiguration::m_angularDamping;
float m_sleepMinEnergy = DefaultRigidBodyConfiguration::m_sleepMinEnergy;
float m_maxAngularVelocity = DefaultRigidBodyConfiguration::m_maxAngularVelocity;
// Visibility settings.
AZ::u16 m_propertyVisibilityFlags = (std::numeric_limits<AZ::u16>::max)();
bool m_startAsleep = false;
bool m_interpolateMotion = false;
bool m_gravityEnabled = true;
bool m_simulated = true;
bool m_kinematic = false;
bool m_ccdEnabled = false; ///< Whether continuous collision detection is enabled.
float m_ccdMinAdvanceCoefficient = 0.15f; ///< Coefficient affecting how granularly time is subdivided in CCD.
bool m_ccdFrictionEnabled = false; ///< Whether friction is applied when resolving CCD collisions.
bool m_computeCenterOfMass = true;
bool m_computeInertiaTensor = true;
bool m_computeMass = true;
//! If set, non-simulated shapes will also be included in the mass properties calculation.
bool m_includeAllShapesInMassCalculation = false;
};
/// Dynamic rigid body.
class RigidBody
: public WorldBody
{
public:
AZ_CLASS_ALLOCATOR(RigidBody, AZ::SystemAllocator, 0);
AZ_RTTI(RigidBody, "{156E459F-7BB7-4B4E-ADA0-2130D96B7E80}", WorldBody);
public:
RigidBody() = default;
explicit RigidBody(const RigidBodyConfiguration& settings);
virtual void AddShape(AZStd::shared_ptr<Shape> shape) = 0;
virtual void RemoveShape(AZStd::shared_ptr<Shape> shape) = 0;
virtual AZ::u32 GetShapeCount() { return 0; }
virtual AZStd::shared_ptr<Shape> GetShape(AZ::u32 /*index*/) { return nullptr; }
virtual AZ::Vector3 GetCenterOfMassWorld() const = 0;
virtual AZ::Vector3 GetCenterOfMassLocal() const = 0;
virtual AZ::Matrix3x3 GetInverseInertiaWorld() const = 0;
virtual AZ::Matrix3x3 GetInverseInertiaLocal() const = 0;
virtual float GetMass() const = 0;
virtual float GetInverseMass() const = 0;
virtual void SetMass(float mass) = 0;
virtual void SetCenterOfMassOffset(const AZ::Vector3& comOffset) = 0;
/// Retrieves the velocity at center of mass; only linear velocity, no rotational velocity contribution.
virtual AZ::Vector3 GetLinearVelocity() const = 0;
virtual void SetLinearVelocity(const AZ::Vector3& velocity) = 0;
virtual AZ::Vector3 GetAngularVelocity() const = 0;
virtual void SetAngularVelocity(const AZ::Vector3& angularVelocity) = 0;
virtual AZ::Vector3 GetLinearVelocityAtWorldPoint(const AZ::Vector3& worldPoint) = 0;
virtual void ApplyLinearImpulse(const AZ::Vector3& impulse) = 0;
virtual void ApplyLinearImpulseAtWorldPoint(const AZ::Vector3& impulse, const AZ::Vector3& worldPoint) = 0;
virtual void ApplyAngularImpulse(const AZ::Vector3& angularImpulse) = 0;
virtual float GetLinearDamping() const = 0;
virtual void SetLinearDamping(float damping) = 0;
virtual float GetAngularDamping() const = 0;
virtual void SetAngularDamping(float damping) = 0;
virtual bool IsAwake() const = 0;
virtual void ForceAsleep() = 0;
virtual void ForceAwake() = 0;
virtual float GetSleepThreshold() const = 0;
virtual void SetSleepThreshold(float threshold) = 0;
virtual bool IsKinematic() const = 0;
virtual void SetKinematic(bool kinematic) = 0;
virtual void SetKinematicTarget(const AZ::Transform& targetPosition) = 0;
virtual bool IsGravityEnabled() const = 0;
virtual void SetGravityEnabled(bool enabled) = 0;
virtual void SetSimulationEnabled(bool enabled) = 0;
virtual void SetCCDEnabled(bool enabled) = 0;
//! Recalculates mass, inertia and center of mass based on the flags passed.
//! @param flags MassComputeFlags specifying which properties should be recomputed.
//! @param centerOfMassOffsetOverride Optional override of the center of mass. Note: This parameter will be ignored if COMPUTE_COM is passed in flags.
//! @param inertiaTensorOverride Optional override of the inertia. Note: This parameter will be ignored if COMPUTE_INERTIA is passed in flags.
//! @param massOverride Optional override of the mass. Note: This parameter will be ignored if COMPUTE_MASS is passed in flags.
virtual void UpdateMassProperties(MassComputeFlags flags = MassComputeFlags::DEFAULT,
const AZ::Vector3* centerOfMassOffsetOverride = nullptr,
const AZ::Matrix3x3* inertiaTensorOverride = nullptr,
const float* massOverride = nullptr) = 0;
};
/// Bitwise operators for MassComputeFlags
inline MassComputeFlags operator|(MassComputeFlags lhs, MassComputeFlags rhs)
{
return aznumeric_cast<MassComputeFlags>(aznumeric_cast<AZ::u8>(lhs) | aznumeric_cast<AZ::u8>(rhs));
}
inline MassComputeFlags operator&(MassComputeFlags lhs, MassComputeFlags rhs)
{
return aznumeric_cast<MassComputeFlags>(aznumeric_cast<AZ::u8>(lhs) & aznumeric_cast<AZ::u8>(rhs));
}
/// Static rigid body.
class RigidBodyStatic
: public WorldBody
{
public:
AZ_CLASS_ALLOCATOR(RigidBodyStatic, AZ::SystemAllocator, 0);
AZ_RTTI(RigidBodyStatic, "{13A677BB-7085-4EDB-BCC8-306548238692}", WorldBody);
virtual void AddShape(const AZStd::shared_ptr<Shape>& shape) = 0;
virtual AZ::u32 GetShapeCount() { return 0; }
virtual AZStd::shared_ptr<Shape> GetShape(AZ::u32 /*index*/) { return nullptr; }
};
} // namespace Physics

@ -89,9 +89,9 @@ namespace AzPhysics
//! @param inertiaTensorOverride Optional override of the inertia. Note: This parameter will be ignored if COMPUTE_INERTIA is passed in flags.
//! @param massOverride Optional override of the mass. Note: This parameter will be ignored if COMPUTE_MASS is passed in flags.
virtual void UpdateMassProperties(MassComputeFlags flags = MassComputeFlags::DEFAULT,
const AZ::Vector3* centerOfMassOffsetOverride = nullptr,
const AZ::Matrix3x3* inertiaTensorOverride = nullptr,
const float* massOverride = nullptr) = 0;
const AZ::Vector3& centerOfMassOffsetOverride = AZ::Vector3::CreateZero(),
const AZ::Matrix3x3& inertiaTensorOverride = AZ::Matrix3x3::CreateIdentity(),
const float massOverride = 1.0f) = 0;
};
} // namespace AzPhysics

@ -22,6 +22,7 @@ namespace AzFramework
->Field("terminationTime", &SessionConfig::m_terminationTime)
->Field("creatorId", &SessionConfig::m_creatorId)
->Field("sessionProperties", &SessionConfig::m_sessionProperties)
->Field("matchmakingData", &SessionConfig::m_matchmakingData)
->Field("sessionId", &SessionConfig::m_sessionId)
->Field("sessionName", &SessionConfig::m_sessionName)
->Field("dnsName", &SessionConfig::m_dnsName)
@ -46,6 +47,8 @@ namespace AzFramework
"CreatorId", "A unique identifier for a player or entity creating the session.")
->DataElement(AZ::Edit::UIHandlers::Default, &AzFramework::SessionConfig::m_sessionProperties,
"SessionProperties", "A collection of custom properties for a session.")
->DataElement(AZ::Edit::UIHandlers::Default, &AzFramework::SessionConfig::m_matchmakingData,
"MatchmakingData", "The matchmaking process information that was used to create the session.")
->DataElement(AZ::Edit::UIHandlers::Default, &AzFramework::SessionConfig::m_sessionId,
"SessionId", "A unique identifier for the session.")
->DataElement(AZ::Edit::UIHandlers::Default, &AzFramework::SessionConfig::m_sessionName,

@ -35,6 +35,9 @@ namespace AzFramework
// A collection of custom properties for a session.
AZStd::unordered_map<AZStd::string, AZStd::string> m_sessionProperties;
// The matchmaking process information that was used to create the session.
AZStd::string m_matchmakingData;
// A unique identifier for the session.
AZStd::string m_sessionId;

@ -41,6 +41,11 @@ namespace AzFramework
// OnDestroySessionBegin is fired at the beginning of session termination
// @return The result of all OnDestroySessionBegin notifications
virtual bool OnDestroySessionBegin() = 0;
// OnUpdateSessionBegin is fired at the beginning of session update
// @param sessionConfig The properties to describe a session
// @param updateReason The reason for session update
virtual void OnUpdateSessionBegin(const SessionConfig& sessionConfig, const AZStd::string& updateReason) = 0;
};
using SessionNotificationBus = AZ::EBus<SessionNotifications>;
} // namespace AzFramework

@ -61,6 +61,10 @@ namespace AzFramework
//! be deleted and the spawnable asset to be released. This call is automatically done when
//! AssignRootSpawnable is called while a root spawnable is assigned.
virtual void ReleaseRootSpawnable() = 0;
//! Force processing all SpawnableEntitiesManager requests immediately
//! This is useful when loading a different level while SpawnableEntitiesManager still has
//! pending requests
virtual void ProcessSpawnableQueue() = 0;
};
using RootSpawnableInterface = AZ::Interface<RootSpawnableDefinition>;

@ -45,8 +45,7 @@ namespace AzFramework
void SpawnableSystemComponent::OnTick(float /*deltaTime*/, AZ::ScriptTimePoint /*time*/)
{
m_entitiesManager.ProcessQueue(
SpawnableEntitiesManager::CommandQueuePriority::High | SpawnableEntitiesManager::CommandQueuePriority::Regular);
ProcessSpawnableQueue();
RootSpawnableNotificationBus::ExecuteQueuedEvents();
}
@ -121,6 +120,12 @@ namespace AzFramework
m_rootSpawnableId = AZ::Data::AssetId();
}
void SpawnableSystemComponent::ProcessSpawnableQueue()
{
m_entitiesManager.ProcessQueue(
SpawnableEntitiesManager::CommandQueuePriority::High | SpawnableEntitiesManager::CommandQueuePriority::Regular);
}
void SpawnableSystemComponent::OnRootSpawnableAssigned([[maybe_unused]] AZ::Data::Asset<Spawnable> rootSpawnable,
[[maybe_unused]] uint32_t generation)
{
@ -161,6 +166,8 @@ namespace AzFramework
void SpawnableSystemComponent::Deactivate()
{
ProcessSpawnableQueue();
m_registryChangeHandler.Disconnect();
AZ::TickBus::Handler::BusDisconnect();

@ -75,6 +75,7 @@ namespace AzFramework
uint64_t AssignRootSpawnable(AZ::Data::Asset<Spawnable> rootSpawnable) override;
void ReleaseRootSpawnable() override;
void ProcessSpawnableQueue() override;
//
// RootSpawnbleNotificationBus

@ -23,10 +23,12 @@ namespace AzFramework
virtual ~XcbEventHandler() = default;
virtual void HandleXcbEvent(xcb_generic_event_t* event) = 0;
// ATTN This is used as a workaround for RAW Input events when using the Editor.
virtual void PollSpecialEvents(){};
};
class XcbEventHandlerBusTraits
: public AZ::EBusTraits
class XcbEventHandlerBusTraits : public AZ::EBusTraits
{
public:
//////////////////////////////////////////////////////////////////////////

@ -0,0 +1,652 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzCore/std/typetraits/integral_constant.h>
#include <AzFramework/API/ApplicationAPI_Linux.h>
#include <AzFramework/XcbConnectionManager.h>
#include <AzFramework/XcbInputDeviceMouse.h>
namespace AzFramework
{
xcb_window_t GetSystemCursorFocusWindow()
{
void* systemCursorFocusWindow = nullptr;
AzFramework::InputSystemCursorConstraintRequestBus::BroadcastResult(
systemCursorFocusWindow, &AzFramework::InputSystemCursorConstraintRequests::GetSystemCursorConstraintWindow);
if (!systemCursorFocusWindow)
{
return XCB_NONE;
}
// TODO Clang compile error because cast .... loses information. On GNU/Linux HWND is void* and on 64-bit
// machines its obviously 64 bit but we receive the window id from m_renderOverlay.winId() which is xcb_window_t 32-bit.
return static_cast<xcb_window_t>(reinterpret_cast<uint64_t>(systemCursorFocusWindow));
}
xcb_connection_t* XcbInputDeviceMouse::s_xcbConnection = nullptr;
xcb_screen_t* XcbInputDeviceMouse::s_xcbScreen = nullptr;
bool XcbInputDeviceMouse::m_xfixesInitialized = false;
bool XcbInputDeviceMouse::m_xInputInitialized = false;
XcbInputDeviceMouse::XcbInputDeviceMouse(InputDeviceMouse& inputDevice)
: InputDeviceMouse::Implementation(inputDevice)
, m_systemCursorState(SystemCursorState::Unknown)
, m_systemCursorPositionNormalized(0.5f, 0.5f)
, m_prevConstraintWindow(XCB_NONE)
, m_focusWindow(XCB_NONE)
, m_cursorShown(true)
{
XcbEventHandlerBus::Handler::BusConnect();
SetSystemCursorState(SystemCursorState::Unknown);
}
XcbInputDeviceMouse::~XcbInputDeviceMouse()
{
XcbEventHandlerBus::Handler::BusDisconnect();
SetSystemCursorState(SystemCursorState::Unknown);
}
InputDeviceMouse::Implementation* XcbInputDeviceMouse::Create(InputDeviceMouse& inputDevice)
{
auto* interface = AzFramework::XcbConnectionManagerInterface::Get();
if (!interface)
{
AZ_Warning("XcbInput", false, "XCB interface not available");
return nullptr;
}
s_xcbConnection = AzFramework::XcbConnectionManagerInterface::Get()->GetXcbConnection();
if (!s_xcbConnection)
{
AZ_Warning("XcbInput", false, "XCB connection not available");
return nullptr;
}
const xcb_setup_t* xcbSetup = xcb_get_setup(s_xcbConnection);
s_xcbScreen = xcb_setup_roots_iterator(xcbSetup).data;
if (!s_xcbScreen)
{
AZ_Warning("XcbInput", false, "XCB screen not available");
return nullptr;
}
// Initialize XFixes extension which we use to create pointer barriers.
if (!InitializeXFixes())
{
AZ_Warning("XcbInput", false, "XCB XFixes initialization failed");
return nullptr;
}
// Initialize XInput extension which is used to get RAW Input events.
if (!InitializeXInput())
{
AZ_Warning("XcbInput", false, "XCB XInput initialization failed");
return nullptr;
}
return aznew XcbInputDeviceMouse(inputDevice);
}
bool XcbInputDeviceMouse::IsConnected() const
{
return true;
}
void XcbInputDeviceMouse::CreateBarriers(xcb_window_t window, bool create)
{
// Don't create any barriers if we are debugging. This will cause artifacts but better then
// a confined cursor during debugging.
if (AZ::Debug::Trace::IsDebuggerPresent())
{
AZ_Warning("XcbInput", false, "Debugger running. Barriers will not be created.");
return;
}
if (create)
{
// Destroy barriers if they are active already.
if (!m_activeBarriers.empty())
{
for (const auto& barrier : m_activeBarriers)
{
xcb_xfixes_delete_pointer_barrier_checked(s_xcbConnection, barrier.id);
}
m_activeBarriers.clear();
}
// Get window information.
const XcbStdFreePtr<xcb_get_geometry_reply_t> xcbGeometryReply{ xcb_get_geometry_reply(
s_xcbConnection, xcb_get_geometry(s_xcbConnection, window), NULL) };
if (!xcbGeometryReply)
{
return;
}
const xcb_translate_coordinates_cookie_t translate_coord =
xcb_translate_coordinates(s_xcbConnection, window, s_xcbScreen->root, 0, 0);
const XcbStdFreePtr<xcb_translate_coordinates_reply_t> xkbTranslateCoordReply{ xcb_translate_coordinates_reply(
s_xcbConnection, translate_coord, NULL) };
if (!xkbTranslateCoordReply)
{
return;
}
const int16_t x0 = xkbTranslateCoordReply->dst_x < 0 ? 0 : xkbTranslateCoordReply->dst_x;
const int16_t y0 = xkbTranslateCoordReply->dst_y < 0 ? 0 : xkbTranslateCoordReply->dst_y;
const int16_t x1 = xkbTranslateCoordReply->dst_x + xcbGeometryReply->width;
const int16_t y1 = xkbTranslateCoordReply->dst_y + xcbGeometryReply->height;
// ATTN For whatever reason, when making an exact rectangle the pointer will escape the top right corner in some cases. Adding
// an offset to the lines so that they cross each other prevents that.
const int16_t offset = 30;
// Create the left barrier info.
m_activeBarriers.push_back({ xcb_generate_id(s_xcbConnection), XCB_XFIXES_BARRIER_DIRECTIONS_POSITIVE_X, x0, Clamp(y0 - offset),
x0, Clamp(y1 + offset) });
// Create the right barrier info.
m_activeBarriers.push_back({ xcb_generate_id(s_xcbConnection), XCB_XFIXES_BARRIER_DIRECTIONS_NEGATIVE_X, x1, Clamp(y0 - offset),
x1, Clamp(y1 + offset) });
// Create the top barrier info.
m_activeBarriers.push_back({ xcb_generate_id(s_xcbConnection), XCB_XFIXES_BARRIER_DIRECTIONS_POSITIVE_Y, Clamp(x0 - offset), y0,
Clamp(x1 + offset), y0 });
// Create the bottom barrier info.
m_activeBarriers.push_back({ xcb_generate_id(s_xcbConnection), XCB_XFIXES_BARRIER_DIRECTIONS_NEGATIVE_Y, Clamp(x0 - offset), y1,
Clamp(x1 + offset), y1 });
// Create the xfixes barriers.
for (const auto& barrier : m_activeBarriers)
{
xcb_void_cookie_t cookie = xcb_xfixes_create_pointer_barrier_checked(
s_xcbConnection, barrier.id, window, barrier.x0, barrier.y0, barrier.x1, barrier.y1, barrier.direction, 0, NULL);
const XcbStdFreePtr<xcb_generic_error_t> xkbError{ xcb_request_check(s_xcbConnection, cookie) };
AZ_Warning(
"XcbInput", !xkbError, "XFixes, failed to create barrier %d at (%d %d %d %d)", barrier.id, barrier.x0, barrier.y0,
barrier.x1, barrier.y1);
}
}
else
{
for (const auto& barrier : m_activeBarriers)
{
xcb_xfixes_delete_pointer_barrier_checked(s_xcbConnection, barrier.id);
}
m_activeBarriers.clear();
}
xcb_flush(s_xcbConnection);
}
bool XcbInputDeviceMouse::InitializeXFixes()
{
m_xfixesInitialized = false;
// We don't have to free query_extension_reply according to xcb documentation.
const xcb_query_extension_reply_t* query_extension_reply = xcb_get_extension_data(s_xcbConnection, &xcb_xfixes_id);
if (!query_extension_reply || !query_extension_reply->present)
{
return m_xfixesInitialized;
}
const xcb_xfixes_query_version_cookie_t query_cookie = xcb_xfixes_query_version(s_xcbConnection, 5, 0);
xcb_generic_error_t* error = NULL;
const XcbStdFreePtr<xcb_xfixes_query_version_reply_t> xkbQueryRequestReply{ xcb_xfixes_query_version_reply(
s_xcbConnection, query_cookie, &error) };
if (!xkbQueryRequestReply || error)
{
if (error)
{
AZ_Warning("XcbInput", false, "Retrieving XFixes version failed : Error code %d", error->error_code);
free(error);
}
return m_xfixesInitialized;
}
else if (xkbQueryRequestReply->major_version < 5)
{
AZ_Warning("XcbInput", false, "XFixes version fails the minimum version check (%d<5)", xkbQueryRequestReply->major_version);
return m_xfixesInitialized;
}
m_xfixesInitialized = true;
return m_xfixesInitialized;
}
bool XcbInputDeviceMouse::InitializeXInput()
{
m_xInputInitialized = false;
// We don't have to free query_extension_reply according to xcb documentation.
const xcb_query_extension_reply_t* query_extension_reply = xcb_get_extension_data(s_xcbConnection, &xcb_input_id);
if (!query_extension_reply || !query_extension_reply->present)
{
return m_xInputInitialized;
}
const xcb_input_xi_query_version_cookie_t query_version_cookie = xcb_input_xi_query_version(s_xcbConnection, 2, 2);
xcb_generic_error_t* error = NULL;
const XcbStdFreePtr<xcb_input_xi_query_version_reply_t> xkbQueryRequestReply{ xcb_input_xi_query_version_reply(
s_xcbConnection, query_version_cookie, &error) };
if (!xkbQueryRequestReply || error)
{
if (error)
{
AZ_Warning("XcbInput", false, "Retrieving XInput version failed : Error code %d", error->error_code);
free(error);
}
return m_xInputInitialized;
}
else if (xkbQueryRequestReply->major_version < 2)
{
AZ_Warning("XcbInput", false, "XInput version fails the minimum version check (%d<5)", xkbQueryRequestReply->major_version);
return m_xInputInitialized;
}
m_xInputInitialized = true;
return m_xInputInitialized;
}
void XcbInputDeviceMouse::SetEnableXInput(bool enable)
{
struct
{
xcb_input_event_mask_t head;
int mask;
} mask;
mask.head.deviceid = XCB_INPUT_DEVICE_ALL;
mask.head.mask_len = 1;
if (enable)
{
mask.mask = XCB_INPUT_XI_EVENT_MASK_RAW_MOTION | XCB_INPUT_XI_EVENT_MASK_RAW_BUTTON_PRESS |
XCB_INPUT_XI_EVENT_MASK_RAW_BUTTON_RELEASE | XCB_INPUT_XI_EVENT_MASK_MOTION | XCB_INPUT_XI_EVENT_MASK_BUTTON_PRESS |
XCB_INPUT_XI_EVENT_MASK_BUTTON_RELEASE;
}
else
{
mask.mask = XCB_NONE;
}
xcb_input_xi_select_events(s_xcbConnection, s_xcbScreen->root, 1, &mask.head);
xcb_flush(s_xcbConnection);
}
void XcbInputDeviceMouse::SetSystemCursorState(SystemCursorState systemCursorState)
{
if (systemCursorState != m_systemCursorState)
{
m_systemCursorState = systemCursorState;
m_focusWindow = GetSystemCursorFocusWindow();
HandleCursorState(m_focusWindow, systemCursorState);
}
}
void XcbInputDeviceMouse::HandleCursorState(xcb_window_t window, SystemCursorState systemCursorState)
{
bool confined = false, cursorShown = true;
switch (systemCursorState)
{
case SystemCursorState::ConstrainedAndHidden:
{
//!< Constrained to the application's main window and hidden
confined = true;
cursorShown = false;
}
break;
case SystemCursorState::ConstrainedAndVisible:
{
//!< Constrained to the application's main window and visible
confined = true;
}
break;
case SystemCursorState::UnconstrainedAndHidden:
{
//!< Free to move outside the main window but hidden while inside
cursorShown = false;
}
break;
case SystemCursorState::UnconstrainedAndVisible:
{
//!< Free to move outside the application's main window and visible
}
case SystemCursorState::Unknown:
default:
break;
}
// ATTN GetSystemCursorFocusWindow when getting out of the play in editor will return XCB_NONE
// We need however the window id to reset the cursor.
if (XCB_NONE == window && (confined || cursorShown))
{
// Reuse the previous window to reset states.
window = m_prevConstraintWindow;
m_prevConstraintWindow = XCB_NONE;
}
else
{
// Remember the window we used to modify cursor and barrier states.
m_prevConstraintWindow = window;
}
SetEnableXInput(!cursorShown);
CreateBarriers(window, confined);
ShowCursor(window, cursorShown);
}
SystemCursorState XcbInputDeviceMouse::GetSystemCursorState() const
{
return m_systemCursorState;
}
void XcbInputDeviceMouse::SetSystemCursorPositionNormalizedInternal(xcb_window_t window, AZ::Vector2 positionNormalized)
{
// TODO Basically not done at all. Added only the basic functions needed.
const XcbStdFreePtr<xcb_get_geometry_reply_t> xkbGeometryReply{ xcb_get_geometry_reply(
s_xcbConnection, xcb_get_geometry(s_xcbConnection, window), NULL) };
if (!xkbGeometryReply)
{
return;
}
const int16_t x = static_cast<int16_t>(positionNormalized.GetX() * xkbGeometryReply->width);
const int16_t y = static_cast<int16_t>(positionNormalized.GetY() * xkbGeometryReply->height);
xcb_warp_pointer(s_xcbConnection, XCB_NONE, window, 0, 0, 0, 0, x, y);
xcb_flush(s_xcbConnection);
}
void XcbInputDeviceMouse::SetSystemCursorPositionNormalized(AZ::Vector2 positionNormalized)
{
const xcb_window_t window = GetSystemCursorFocusWindow();
if (XCB_NONE == window)
{
return;
}
SetSystemCursorPositionNormalizedInternal(window, positionNormalized);
}
AZ::Vector2 XcbInputDeviceMouse::GetSystemCursorPositionNormalizedInternal(xcb_window_t window) const
{
AZ::Vector2 position = AZ::Vector2::CreateZero();
const xcb_query_pointer_cookie_t pointer = xcb_query_pointer(s_xcbConnection, window);
const XcbStdFreePtr<xcb_query_pointer_reply_t> xkbQueryPointerReply{ xcb_query_pointer_reply(s_xcbConnection, pointer, NULL) };
if (!xkbQueryPointerReply)
{
return position;
}
const XcbStdFreePtr<xcb_get_geometry_reply_t> xkbGeometryReply{ xcb_get_geometry_reply(
s_xcbConnection, xcb_get_geometry(s_xcbConnection, window), NULL) };
if (!xkbGeometryReply)
{
return position;
}
AZ_Assert(xkbGeometryReply->width != 0, "xkbGeometry response width must be non-zero. (%d)", xkbGeometryReply->width);
const float normalizedCursorPostionX = static_cast<float>(xkbQueryPointerReply->win_x) / xkbGeometryReply->width;
AZ_Assert(xkbGeometryReply->height != 0, "xkbGeometry response height must be non-zero. (%d)", xkbGeometryReply->height);
const float normalizedCursorPostionY = static_cast<float>(xkbQueryPointerReply->win_y) / xkbGeometryReply->height;
position = AZ::Vector2(normalizedCursorPostionX, normalizedCursorPostionY);
return position;
}
AZ::Vector2 XcbInputDeviceMouse::GetSystemCursorPositionNormalized() const
{
const xcb_window_t window = GetSystemCursorFocusWindow();
if (XCB_NONE == window)
{
return AZ::Vector2::CreateZero();
}
return GetSystemCursorPositionNormalizedInternal(window);
}
void XcbInputDeviceMouse::TickInputDevice()
{
ProcessRawEventQueues();
}
void XcbInputDeviceMouse::ShowCursor(xcb_window_t window, bool show)
{
xcb_void_cookie_t cookie;
if (show)
{
cookie = xcb_xfixes_show_cursor_checked(s_xcbConnection, window);
}
else
{
cookie = xcb_xfixes_hide_cursor_checked(s_xcbConnection, window);
}
const XcbStdFreePtr<xcb_generic_error_t> xkbError{ xcb_request_check(s_xcbConnection, cookie) };
if (xkbError)
{
AZ_Warning("XcbInput", false, "ShowCursor failed: %d", xkbError->error_code);
return;
}
// ATTN In the following part we will when cursor gets hidden store the position of the cursor in screen space
// not window space. We use that to re-position when showing the cursor again. Is this the correct
// behavior?
const bool cursorWasHidden = !m_cursorShown;
m_cursorShown = show;
if (!m_cursorShown)
{
m_cursorHiddenPosition = GetSystemCursorPositionNormalizedInternal(s_xcbScreen->root);
SetSystemCursorPositionNormalized(AZ::Vector2(0.5f, 0.5f));
}
else if (cursorWasHidden)
{
SetSystemCursorPositionNormalizedInternal(s_xcbScreen->root, m_cursorHiddenPosition);
}
xcb_flush(s_xcbConnection);
}
void XcbInputDeviceMouse::HandleButtonPressEvents(uint32_t detail, bool pressed)
{
bool isWheel;
float wheelDirection;
const auto* button = InputChannelFromMouseEvent(detail, isWheel, wheelDirection);
if (button)
{
QueueRawButtonEvent(*button, pressed);
}
if (isWheel)
{
float axisValue = MAX_XI_WHEEL_SENSITIVITY * wheelDirection;
QueueRawMovementEvent(InputDeviceMouse::Movement::Z, axisValue);
}
}
void XcbInputDeviceMouse::HandlePointerMotionEvents(const xcb_generic_event_t* event)
{
const xcb_input_motion_event_t* mouseMotionEvent = reinterpret_cast<const xcb_input_motion_event_t*>(event);
m_systemCursorPosition[0] = mouseMotionEvent->event_x;
m_systemCursorPosition[1] = mouseMotionEvent->event_y;
}
void XcbInputDeviceMouse::HandleRawInputEvents(const xcb_ge_generic_event_t* event)
{
const xcb_ge_generic_event_t* genericEvent = reinterpret_cast<const xcb_ge_generic_event_t*>(event);
switch (genericEvent->event_type)
{
case XCB_INPUT_RAW_BUTTON_PRESS:
{
const xcb_input_raw_button_press_event_t* mouseButtonEvent =
reinterpret_cast<const xcb_input_raw_button_press_event_t*>(event);
HandleButtonPressEvents(mouseButtonEvent->detail, true);
}
break;
case XCB_INPUT_RAW_BUTTON_RELEASE:
{
const xcb_input_raw_button_release_event_t* mouseButtonEvent =
reinterpret_cast<const xcb_input_raw_button_release_event_t*>(event);
HandleButtonPressEvents(mouseButtonEvent->detail, false);
}
break;
case XCB_INPUT_RAW_MOTION:
{
const xcb_input_raw_motion_event_t* mouseMotionEvent = reinterpret_cast<const xcb_input_raw_motion_event_t*>(event);
int axisLen = xcb_input_raw_button_press_axisvalues_length(mouseMotionEvent);
const xcb_input_fp3232_t* axisvalues = xcb_input_raw_button_press_axisvalues_raw(mouseMotionEvent);
for (int i = 0; i < axisLen; ++i)
{
const float axisValue = fp3232ToFloat(axisvalues[i]);
switch (i)
{
case 0:
QueueRawMovementEvent(InputDeviceMouse::Movement::X, axisValue);
break;
case 1:
QueueRawMovementEvent(InputDeviceMouse::Movement::Y, axisValue);
break;
}
}
}
break;
}
}
void XcbInputDeviceMouse::PollSpecialEvents()
{
while (xcb_generic_event_t* genericEvent = xcb_poll_for_queued_event(s_xcbConnection))
{
// TODO Is the following correct? If we are showing the cursor, don't poll RAW Input events.
switch (genericEvent->response_type & ~0x80)
{
case XCB_GE_GENERIC:
{
const xcb_ge_generic_event_t* geGenericEvent = reinterpret_cast<const xcb_ge_generic_event_t*>(genericEvent);
// Only handle raw inputs if we have focus.
// Handle Raw Input events first.
if ((geGenericEvent->event_type == XCB_INPUT_RAW_BUTTON_PRESS) ||
(geGenericEvent->event_type == XCB_INPUT_RAW_BUTTON_RELEASE) ||
(geGenericEvent->event_type == XCB_INPUT_RAW_MOTION))
{
HandleRawInputEvents(geGenericEvent);
free(genericEvent);
}
}
break;
}
}
}
void XcbInputDeviceMouse::HandleXcbEvent(xcb_generic_event_t* event)
{
switch (event->response_type & ~0x80)
{
// QT5 is using by default XInput which means we do need to check for XCB_GE_GENERIC event to parse all mouse related events.
case XCB_GE_GENERIC:
{
const xcb_ge_generic_event_t* genericEvent = reinterpret_cast<const xcb_ge_generic_event_t*>(event);
// Handling RAW Inputs here works in GameMode but not in Editor mode because QT is
// not handling RAW input events and passing to.
if (!m_cursorShown)
{
// Handle Raw Input events first.
if ((genericEvent->event_type == XCB_INPUT_RAW_BUTTON_PRESS) ||
(genericEvent->event_type == XCB_INPUT_RAW_BUTTON_RELEASE) || (genericEvent->event_type == XCB_INPUT_RAW_MOTION))
{
HandleRawInputEvents(genericEvent);
}
}
else
{
switch (genericEvent->event_type)
{
case XCB_INPUT_BUTTON_PRESS:
{
const xcb_input_button_press_event_t* mouseButtonEvent =
reinterpret_cast<const xcb_input_button_press_event_t*>(genericEvent);
HandleButtonPressEvents(mouseButtonEvent->detail, true);
}
break;
case XCB_INPUT_BUTTON_RELEASE:
{
const xcb_input_button_release_event_t* mouseButtonEvent =
reinterpret_cast<const xcb_input_button_release_event_t*>(genericEvent);
HandleButtonPressEvents(mouseButtonEvent->detail, false);
}
break;
case XCB_INPUT_MOTION:
{
HandlePointerMotionEvents(event);
}
break;
}
}
}
break;
case XCB_FOCUS_IN:
{
const xcb_focus_in_event_t* focusInEvent = reinterpret_cast<const xcb_focus_in_event_t*>(event);
if (m_focusWindow != focusInEvent->event)
{
m_focusWindow = focusInEvent->event;
HandleCursorState(m_focusWindow, m_systemCursorState);
}
}
break;
case XCB_FOCUS_OUT:
{
const xcb_focus_out_event_t* focusOutEvent = reinterpret_cast<const xcb_focus_out_event_t*>(event);
HandleCursorState(focusOutEvent->event, SystemCursorState::UnconstrainedAndVisible);
ProcessRawEventQueues();
ResetInputChannelStates();
m_focusWindow = XCB_NONE;
}
break;
}
}
} // namespace AzFramework

@ -0,0 +1,193 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzFramework/Input/Devices/Mouse/InputDeviceMouse.h>
#include <AzFramework/XcbConnectionManager.h>
#include <AzFramework/XcbEventHandler.h>
#include <AzFramework/XcbInterface.h>
#include <xcb/xfixes.h>
#include <xcb/xinput.h>
// The maximum number of raw input axis this mouse device supports.
constexpr uint32_t MAX_XI_RAW_AXIS = 2;
// The sensitivity of the wheel.
constexpr float MAX_XI_WHEEL_SENSITIVITY = 140.0f;
namespace AzFramework
{
class XcbInputDeviceMouse
: public InputDeviceMouse::Implementation
, public XcbEventHandlerBus::Handler
{
public:
AZ_CLASS_ALLOCATOR(XcbInputDeviceMouse, AZ::SystemAllocator, 0);
////////////////////////////////////////////////////////////////////////////////////////////
//! Constructor
//! \param[in] inputDevice Reference to the input device being implemented
XcbInputDeviceMouse(InputDeviceMouse& inputDevice);
////////////////////////////////////////////////////////////////////////////////////////////
//! Destructor
virtual ~XcbInputDeviceMouse();
static XcbInputDeviceMouse::Implementation* Create(InputDeviceMouse& inputDevice);
protected:
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputDeviceMouse::Implementation::IsConnected
bool IsConnected() const override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputDeviceMouse::Implementation::SetSystemCursorState
void SetSystemCursorState(SystemCursorState systemCursorState) override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputDeviceMouse::Implementation::GetSystemCursorState
SystemCursorState GetSystemCursorState() const override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputDeviceMouse::Implementation::SetSystemCursorPositionNormalized
void SetSystemCursorPositionNormalized(AZ::Vector2 positionNormalized) override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputDeviceMouse::Implementation::GetSystemCursorPositionNormalized
AZ::Vector2 GetSystemCursorPositionNormalized() const override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputDeviceMouse::Implementation::TickInputDevice
void TickInputDevice() override;
//! This method is called by the Editor to accommodate some events with the Editor. Never called in Game mode.
void PollSpecialEvents() override;
//! Handle X11 events.
void HandleXcbEvent(xcb_generic_event_t* event) override;
//! Initialize XFixes extension. Used for barriers.
static bool InitializeXFixes();
//! Initialize XInput extension. Used for raw input during confinement and showing/hiding the cursor.
static bool InitializeXInput();
//! Enables/Disables XInput Raw Input events.
void SetEnableXInput(bool enable);
//! Create barriers.
void CreateBarriers(xcb_window_t window, bool create);
//! Helper function.
void SystemCursorStateToLogic(SystemCursorState systemCursorState, bool& confined, bool& cursorShown);
//! Shows/Hides the cursor.
void ShowCursor(xcb_window_t window, bool show);
//! Get the normalized cursor position. The coordinates returned are relative to the specified window.
AZ::Vector2 GetSystemCursorPositionNormalizedInternal(xcb_window_t window) const;
//! Set the normalized cursor position. The normalized position will be relative to the specified window.
void SetSystemCursorPositionNormalizedInternal(xcb_window_t window, AZ::Vector2 positionNormalized);
//! Handle button press/release events.
void HandleButtonPressEvents(uint32_t detail, bool pressed);
//! Handle motion notify events.
void HandlePointerMotionEvents(const xcb_generic_event_t* event);
//! Will set cursor states and confinement modes.
void HandleCursorState(xcb_window_t window, SystemCursorState systemCursorState);
//! Will handle all raw input events.
void HandleRawInputEvents(const xcb_ge_generic_event_t* event);
//! Convert XInput fp1616 to float.
inline float fp1616ToFloat(xcb_input_fp1616_t value) const
{
return static_cast<float>((value >> 16) + (value & 0xffff) / 0xffff);
}
//! Convert XInput fp3232 to float.
inline float fp3232ToFloat(xcb_input_fp3232_t value) const
{
return static_cast<float>(value.integral) + static_cast<float>(value.frac / (float)(1ull << 32));
}
const InputChannelId* InputChannelFromMouseEvent(xcb_button_t button, bool& isWheel, float& direction) const
{
isWheel = false;
direction = 1.0f;
switch (button)
{
case XCB_BUTTON_INDEX_1:
return &InputDeviceMouse::Button::Left;
case XCB_BUTTON_INDEX_2:
return &InputDeviceMouse::Button::Right;
case XCB_BUTTON_INDEX_3:
return &InputDeviceMouse::Button::Middle;
case XCB_BUTTON_INDEX_4:
isWheel = true;
direction = 1.0f;
break;
case XCB_BUTTON_INDEX_5:
isWheel = true;
direction = -1.0f;
break;
default:
break;
}
return nullptr;
}
// Barriers work only with positive values. We clamp here to zero.
inline int16_t Clamp(int16_t value) const
{
return value < 0 ? 0 : value;
}
private:
//! The current system cursor state
SystemCursorState m_systemCursorState;
//! The cursor position before it got hidden.
AZ::Vector2 m_cursorHiddenPosition;
AZ::Vector2 m_systemCursorPositionNormalized;
uint32_t m_systemCursorPosition[MAX_XI_RAW_AXIS];
static xcb_connection_t* s_xcbConnection;
static xcb_screen_t* s_xcbScreen;
//! Will be true if the xfixes extension could be initialized.
static bool m_xfixesInitialized;
//! Will be true if the xinput2 extension could be initialized.
static bool m_xInputInitialized;
//! The window that had focus
xcb_window_t m_prevConstraintWindow;
//! The current window that has focus
xcb_window_t m_focusWindow;
//! Will be true if the cursor is shown else false.
bool m_cursorShown;
struct XFixesBarrierProperty
{
xcb_xfixes_barrier_t id;
uint32_t direction;
int16_t x0, y0, x1, y1;
};
//! Array that holds barrier information used to confine the cursor.
std::vector<XFixesBarrierProperty> m_activeBarriers;
};
} // namespace AzFramework

@ -8,24 +8,31 @@
#include <AzFramework/Application/Application.h>
#include <AzFramework/Windowing/NativeWindow.h>
#include <AzFramework/XcbNativeWindow.h>
#include <AzFramework/XcbConnectionManager.h>
#include <AzFramework/XcbInterface.h>
#include <AzFramework/XcbNativeWindow.h>
#include <xcb/xcb.h>
namespace AzFramework
{
[[maybe_unused]] const char XcbErrorWindow[] = "XcbNativeWindow";
static constexpr uint8_t s_XcbFormatDataSize = 32; // Format indicator for xcb for client messages
static constexpr uint16_t s_DefaultXcbWindowBorderWidth = 4; // The default border with in pixels if a border was specified
static constexpr uint8_t s_XcbResponseTypeMask = 0x7f; // Mask to extract the specific event type from an xcb event
static constexpr uint8_t s_XcbFormatDataSize = 32; // Format indicator for xcb for client messages
static constexpr uint16_t s_DefaultXcbWindowBorderWidth = 4; // The default border with in pixels if a border was specified
static constexpr uint8_t s_XcbResponseTypeMask = 0x7f; // Mask to extract the specific event type from an xcb event
#define _NET_WM_STATE_REMOVE 0l
#define _NET_WM_STATE_ADD 1l
#define _NET_WM_STATE_TOGGLE 2l
////////////////////////////////////////////////////////////////////////////////////////////////
XcbNativeWindow::XcbNativeWindow()
XcbNativeWindow::XcbNativeWindow()
: NativeWindow::Implementation()
, m_xcbConnection(nullptr)
, m_xcbRootScreen(nullptr)
, m_xcbWindow(XCB_NONE)
{
if (auto xcbConnectionManager = AzFramework::XcbConnectionManagerInterface::Get();
xcbConnectionManager != nullptr)
if (auto xcbConnectionManager = AzFramework::XcbConnectionManagerInterface::Get(); xcbConnectionManager != nullptr)
{
m_xcbConnection = xcbConnectionManager->GetXcbConnection();
}
@ -33,89 +40,184 @@ namespace AzFramework
}
////////////////////////////////////////////////////////////////////////////////////////////////
XcbNativeWindow::~XcbNativeWindow() = default;
XcbNativeWindow::~XcbNativeWindow()
{
if (XCB_NONE != m_xcbWindow)
{
xcb_destroy_window(m_xcbConnection, m_xcbWindow);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
void XcbNativeWindow::InitWindow(const AZStd::string& title,
const WindowGeometry& geometry,
const WindowStyleMasks& styleMasks)
void XcbNativeWindow::InitWindow(const AZStd::string& title, const WindowGeometry& geometry, const WindowStyleMasks& styleMasks)
{
// Get the parent window
// Get the parent window
const xcb_setup_t* xcbSetup = xcb_get_setup(m_xcbConnection);
xcb_screen_t* xcbRootScreen = xcb_setup_roots_iterator(xcbSetup).data;
xcb_window_t xcbParentWindow = xcbRootScreen->root;
m_xcbRootScreen = xcb_setup_roots_iterator(xcbSetup).data;
xcb_window_t xcbParentWindow = m_xcbRootScreen->root;
// Create an XCB window from the connection
m_xcbWindow = xcb_generate_id(m_xcbConnection);
uint16_t borderWidth = 0;
const uint32_t mask = styleMasks.m_platformAgnosticStyleMask;
if ((mask & WindowStyleMasks::WINDOW_STYLE_BORDERED) ||
(mask & WindowStyleMasks::WINDOW_STYLE_RESIZEABLE))
if ((mask & WindowStyleMasks::WINDOW_STYLE_BORDERED) || (mask & WindowStyleMasks::WINDOW_STYLE_RESIZEABLE))
{
borderWidth = s_DefaultXcbWindowBorderWidth;
}
uint32_t eventMask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
const uint32_t interestedEvents =
XCB_EVENT_MASK_STRUCTURE_NOTIFY
| XCB_EVENT_MASK_BUTTON_PRESS
| XCB_EVENT_MASK_BUTTON_RELEASE
| XCB_EVENT_MASK_KEY_PRESS
| XCB_EVENT_MASK_KEY_RELEASE
| XCB_EVENT_MASK_POINTER_MOTION
;
uint32_t valueList[] = { xcbRootScreen->black_pixel,
interestedEvents };
const uint32_t interestedEvents = XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_KEY_RELEASE |
XCB_EVENT_MASK_FOCUS_CHANGE | XCB_EVENT_MASK_PROPERTY_CHANGE;
uint32_t valueList[] = { m_xcbRootScreen->black_pixel, interestedEvents };
xcb_void_cookie_t xcbCheckResult;
xcbCheckResult = xcb_create_window_checked(m_xcbConnection,
XCB_COPY_FROM_PARENT,
m_xcbWindow,
xcbParentWindow,
aznumeric_cast<int16_t>(geometry.m_posX),
aznumeric_cast<int16_t>(geometry.m_posY),
aznumeric_cast<int16_t>(geometry.m_width),
aznumeric_cast<int16_t>(geometry.m_height),
borderWidth,
XCB_WINDOW_CLASS_INPUT_OUTPUT,
xcbRootScreen->root_visual,
eventMask,
valueList);
xcbCheckResult = xcb_create_window_checked(
m_xcbConnection, XCB_COPY_FROM_PARENT, m_xcbWindow, xcbParentWindow, aznumeric_cast<int16_t>(geometry.m_posX),
aznumeric_cast<int16_t>(geometry.m_posY), aznumeric_cast<int16_t>(geometry.m_width), aznumeric_cast<int16_t>(geometry.m_height),
borderWidth, XCB_WINDOW_CLASS_INPUT_OUTPUT, m_xcbRootScreen->root_visual, eventMask, valueList);
AZ_Assert(ValidateXcbResult(xcbCheckResult), "Failed to create xcb window.");
SetWindowTitle(title);
// Setup the window close event
const static char* wmProtocolString = "WM_PROTOCOLS";
xcb_intern_atom_cookie_t cookieProtocol = xcb_intern_atom(m_xcbConnection, 1, strlen(wmProtocolString), wmProtocolString);
xcb_intern_atom_reply_t* replyProtocol = xcb_intern_atom_reply(m_xcbConnection, cookieProtocol, nullptr);
AZ_Error(XcbErrorWindow, replyProtocol != nullptr, "Unable to query xcb '%s' atom", wmProtocolString);
m_xcbAtomProtocols = replyProtocol->atom;
const static char* wmDeleteWindowString = "WM_DELETE_WINDOW";
xcb_intern_atom_cookie_t cookieDeleteWindow = xcb_intern_atom(m_xcbConnection, 0, strlen(wmDeleteWindowString), wmDeleteWindowString);
xcb_intern_atom_reply_t* replyDeleteWindow = xcb_intern_atom_reply(m_xcbConnection, cookieDeleteWindow, nullptr);
AZ_Error(XcbErrorWindow, replyDeleteWindow != nullptr, "Unable to query xcb '%s' atom", wmDeleteWindowString);
m_xcbAtomDeleteWindow = replyDeleteWindow->atom;
xcbCheckResult = xcb_change_property_checked(m_xcbConnection,
XCB_PROP_MODE_REPLACE,
m_xcbWindow,
m_xcbAtomProtocols,
XCB_ATOM_ATOM,
s_XcbFormatDataSize,
1,
&m_xcbAtomDeleteWindow);
AZ_Assert(ValidateXcbResult(xcbCheckResult), "Failed to change the xcb atom property for WM_CLOSE event");
m_posX = geometry.m_posX;
m_posY = geometry.m_posY;
m_width = geometry.m_width;
m_height = geometry.m_height;
InitializeAtoms();
xcb_client_message_event_t event;
event.response_type = XCB_CLIENT_MESSAGE;
event.type = _NET_REQUEST_FRAME_EXTENTS;
event.window = m_xcbWindow;
event.format = 32;
event.sequence = 0;
event.data.data32[0] = 0l;
event.data.data32[1] = 0l;
event.data.data32[2] = 0l;
event.data.data32[3] = 0l;
event.data.data32[4] = 0l;
xcbCheckResult = xcb_send_event(
m_xcbConnection, 1, m_xcbRootScreen->root, XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT,
(const char*)&event);
AZ_Assert(ValidateXcbResult(xcbCheckResult), "Failed to set _NET_REQUEST_FRAME_EXTENTS");
// The WM will be able to kill the application if it gets unresponsive.
int32_t pid = getpid();
xcb_change_property(m_xcbConnection, XCB_PROP_MODE_REPLACE, m_xcbWindow, _NET_WM_PID, XCB_ATOM_CARDINAL, 32, 1, &pid);
xcb_flush(m_xcbConnection);
}
xcb_atom_t XcbNativeWindow::GetAtom(const char* atomName)
{
xcb_intern_atom_cookie_t intern_atom_cookie = xcb_intern_atom(m_xcbConnection, 0, strlen(atomName), atomName);
XcbStdFreePtr<xcb_intern_atom_reply_t> xkbinternAtom{ xcb_intern_atom_reply(m_xcbConnection, intern_atom_cookie, NULL) };
if (!xkbinternAtom)
{
AZ_Error(XcbErrorWindow, xkbinternAtom != nullptr, "Unable to query xcb '%s' atom", atomName);
return XCB_NONE;
}
return xkbinternAtom->atom;
}
int XcbNativeWindow::SetAtom(xcb_window_t window, xcb_atom_t atom, xcb_atom_t type, size_t len, void* data)
{
xcb_void_cookie_t cookie = xcb_change_property_checked(m_xcbConnection, XCB_PROP_MODE_REPLACE, window, atom, type, 32, len, data);
XcbStdFreePtr<xcb_generic_error_t> xkbError{ xcb_request_check(m_xcbConnection, cookie) };
if (!xkbError)
{
return 0;
}
return xkbError->error_code;
}
////////////////////////////////////////////////////////////////////////////////////////////////
void XcbNativeWindow::InitializeAtoms()
{
AZStd::vector<xcb_atom_t> Atoms;
_NET_ACTIVE_WINDOW = GetAtom("_NET_ACTIVE_WINDOW");
_NET_WM_BYPASS_COMPOSITOR = GetAtom("_NET_WM_BYPASS_COMPOSITOR");
// ---------------------------------------------------------------------
// Handle all WM Protocols atoms.
//
WM_PROTOCOLS = GetAtom("WM_PROTOCOLS");
// This atom is used to close a window. Emitted when user clicks the close button.
WM_DELETE_WINDOW = GetAtom("WM_DELETE_WINDOW");
Atoms.push_back(WM_DELETE_WINDOW);
xcb_change_property(
m_xcbConnection, XCB_PROP_MODE_REPLACE, m_xcbWindow, WM_PROTOCOLS, XCB_ATOM_ATOM, 32, Atoms.size(), Atoms.data());
xcb_flush(m_xcbConnection);
// ---------------------------------------------------------------------
// Handle all WM State atoms.
//
_NET_WM_STATE = GetAtom("_NET_WM_STATE");
_NET_WM_STATE_FULLSCREEN = GetAtom("_NET_WM_STATE_FULLSCREEN");
_NET_WM_STATE_MAXIMIZED_VERT = GetAtom("_NET_WM_STATE_MAXIMIZED_VERT");
_NET_WM_STATE_MAXIMIZED_HORZ = GetAtom("_NET_WM_STATE_MAXIMIZED_HORZ");
_NET_MOVERESIZE_WINDOW = GetAtom("_NET_MOVERESIZE_WINDOW");
_NET_REQUEST_FRAME_EXTENTS = GetAtom("_NET_REQUEST_FRAME_EXTENTS");
_NET_FRAME_EXTENTS = GetAtom("_NET_FRAME_EXTENTS");
_NET_WM_PID = GetAtom("_NET_WM_PID");
}
void XcbNativeWindow::GetWMStates()
{
xcb_get_property_cookie_t cookie = xcb_get_property(m_xcbConnection, 0, m_xcbWindow, _NET_WM_STATE, XCB_ATOM_ATOM, 0, 1024);
xcb_generic_error_t* error = nullptr;
XcbStdFreePtr<xcb_get_property_reply_t> xkbGetPropertyReply{ xcb_get_property_reply(m_xcbConnection, cookie, &error) };
if (!xkbGetPropertyReply || error || !((xkbGetPropertyReply->format == 32) && (xkbGetPropertyReply->type == XCB_ATOM_ATOM)))
{
AZ_Warning("ApplicationLinux", false, "Acquiring _NET_WM_STATE information from the WM failed.");
if (error)
{
AZ_TracePrintf("Error", "Error code %d", error->error_code);
free(error);
}
return;
}
m_fullscreenState = false;
m_horizontalyMaximized = false;
m_verticallyMaximized = false;
const xcb_atom_t* states = static_cast<const xcb_atom_t*>(xcb_get_property_value(xkbGetPropertyReply.get()));
for (int i = 0; i < xkbGetPropertyReply->length; i++)
{
if (states[i] == _NET_WM_STATE_FULLSCREEN)
{
m_fullscreenState = true;
}
else if (states[i] == _NET_WM_STATE_MAXIMIZED_HORZ)
{
m_horizontalyMaximized = true;
}
else if (states[i] == _NET_WM_STATE_MAXIMIZED_VERT)
{
m_verticallyMaximized = true;
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
@ -145,7 +247,7 @@ namespace AzFramework
xcb_flush(m_xcbConnection);
}
XcbEventHandlerBus::Handler::BusDisconnect();
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
NativeWindowHandle XcbNativeWindow::GetWindowHandle() const
@ -157,14 +259,9 @@ namespace AzFramework
void XcbNativeWindow::SetWindowTitle(const AZStd::string& title)
{
xcb_void_cookie_t xcbCheckResult;
xcbCheckResult = xcb_change_property(m_xcbConnection,
XCB_PROP_MODE_REPLACE,
m_xcbWindow,
XCB_ATOM_WM_NAME,
XCB_ATOM_STRING,
8,
static_cast<uint32_t>(title.size()),
title.c_str());
xcbCheckResult = xcb_change_property(
m_xcbConnection, XCB_PROP_MODE_REPLACE, m_xcbWindow, XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8, static_cast<uint32_t>(title.size()),
title.c_str());
AZ_Assert(ValidateXcbResult(xcbCheckResult), "Failed to set window title.");
}
@ -174,7 +271,7 @@ namespace AzFramework
const uint32_t values[] = { clientAreaSize.m_width, clientAreaSize.m_height };
xcb_configure_window(m_xcbConnection, m_xcbWindow, XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT, values);
m_width = clientAreaSize.m_width;
m_height = clientAreaSize.m_height;
}
@ -184,16 +281,77 @@ namespace AzFramework
{
// [GFX TODO][GHI - 2678]
// Using 60 for now until proper support is added
return 60;
}
bool XcbNativeWindow::GetFullScreenState() const
{
return m_fullscreenState;
}
void XcbNativeWindow::SetFullScreenState(bool fullScreenState)
{
// TODO This is a pretty basic full-screen implementation using WM's _NET_WM_STATE_FULLSCREEN state.
// Do we have to provide also the old way?
GetWMStates();
xcb_client_message_event_t event;
event.response_type = XCB_CLIENT_MESSAGE;
event.type = _NET_WM_STATE;
event.window = m_xcbWindow;
event.format = 32;
event.sequence = 0;
event.data.data32[0] = fullScreenState ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
event.data.data32[1] = _NET_WM_STATE_FULLSCREEN;
event.data.data32[2] = 0;
event.data.data32[3] = 1;
event.data.data32[4] = 0;
xcb_void_cookie_t xcbCheckResult = xcb_send_event(
m_xcbConnection, 1, m_xcbRootScreen->root, XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT,
(const char*)&event);
AZ_Assert(ValidateXcbResult(xcbCheckResult), "Failed to set _NET_WM_STATE_FULLSCREEN");
// Also try to disable/enable the compositor if possible. Might help in some cases.
const long _NET_WM_BYPASS_COMPOSITOR_HINT_ON = m_fullscreenState ? 1 : 0;
SetAtom(m_xcbWindow, _NET_WM_BYPASS_COMPOSITOR, XCB_ATOM_CARDINAL, 32, (char*)&_NET_WM_BYPASS_COMPOSITOR_HINT_ON);
if (!fullScreenState)
{
if (m_horizontalyMaximized || m_verticallyMaximized)
{
printf("Remove maximized state.\n");
xcb_client_message_event_t event;
event.response_type = XCB_CLIENT_MESSAGE;
event.type = _NET_WM_STATE;
event.window = m_xcbWindow;
event.format = 32;
event.sequence = 0;
event.data.data32[0] = _NET_WM_STATE_MAXIMIZED_VERT;
event.data.data32[1] = _NET_WM_STATE_MAXIMIZED_HORZ;
event.data.data32[2] = 0;
event.data.data32[3] = 0;
event.data.data32[4] = 0;
xcb_void_cookie_t xcbCheckResult = xcb_send_event(
m_xcbConnection, 1, m_xcbRootScreen->root, XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT,
(const char*)&event);
AZ_Assert(
ValidateXcbResult(xcbCheckResult), "Failed to remove _NET_WM_STATE_MAXIMIZED_VERT | _NET_WM_STATE_MAXIMIZED_HORZ");
}
}
xcb_flush(m_xcbConnection);
m_fullscreenState = fullScreenState;
}
////////////////////////////////////////////////////////////////////////////////////////////////
bool XcbNativeWindow::ValidateXcbResult(xcb_void_cookie_t cookie)
{
bool result = true;
if (xcb_generic_error_t* error = xcb_request_check(m_xcbConnection, cookie))
{
AZ_TracePrintf("Error","Error code %d", error->error_code);
AZ_TracePrintf("Error", "Error code %d", error->error_code);
result = false;
}
return result;
@ -204,20 +362,20 @@ namespace AzFramework
{
switch (event->response_type & s_XcbResponseTypeMask)
{
case XCB_CONFIGURE_NOTIFY:
case XCB_CONFIGURE_NOTIFY:
{
xcb_configure_notify_event_t* cne = reinterpret_cast<xcb_configure_notify_event_t*>(event);
WindowSizeChanged(aznumeric_cast<uint32_t>(cne->width),
aznumeric_cast<uint32_t>(cne->height));
if ((cne->width != m_width) || (cne->height != m_height))
{
WindowSizeChanged(aznumeric_cast<uint32_t>(cne->width), aznumeric_cast<uint32_t>(cne->height));
}
break;
}
case XCB_CLIENT_MESSAGE:
case XCB_CLIENT_MESSAGE:
{
xcb_client_message_event_t* cme = reinterpret_cast<xcb_client_message_event_t*>(event);
if ((cme->type == m_xcbAtomProtocols) &&
(cme->format == s_XcbFormatDataSize) &&
(cme->data.data32[0] == m_xcbAtomDeleteWindow))
if ((cme->type == WM_PROTOCOLS) && (cme->format == s_XcbFormatDataSize) && (cme->data.data32[0] == WM_DELETE_WINDOW))
{
Deactivate();
@ -238,7 +396,8 @@ namespace AzFramework
if (m_activated)
{
WindowNotificationBus::Event(reinterpret_cast<NativeWindowHandle>(m_xcbWindow), &WindowNotificationBus::Events::OnWindowResized, width, height);
WindowNotificationBus::Event(
reinterpret_cast<NativeWindowHandle>(m_xcbWindow), &WindowNotificationBus::Events::OnWindowResized, width, height);
}
}
}

@ -27,15 +27,16 @@ namespace AzFramework
////////////////////////////////////////////////////////////////////////////////////////////
// NativeWindow::Implementation
void InitWindow(const AZStd::string& title,
const WindowGeometry& geometry,
const WindowStyleMasks& styleMasks) override;
void InitWindow(const AZStd::string& title, const WindowGeometry& geometry, const WindowStyleMasks& styleMasks) override;
void Activate() override;
void Deactivate() override;
NativeWindowHandle GetWindowHandle() const override;
void SetWindowTitle(const AZStd::string& title) override;
void ResizeClientArea(WindowSize clientAreaSize) override;
uint32_t GetDisplayRefreshRate() const override;
uint32_t GetDisplayRefreshRate() const override;
bool GetFullScreenState() const override;
void SetFullScreenState(bool fullScreenState) override;
////////////////////////////////////////////////////////////////////////////////////////////
// XcbEventHandlerBus::Handler
@ -44,10 +45,46 @@ namespace AzFramework
private:
bool ValidateXcbResult(xcb_void_cookie_t cookie);
void WindowSizeChanged(const uint32_t width, const uint32_t height);
int SetAtom(xcb_window_t window, xcb_atom_t atom, xcb_atom_t type, size_t len, void* data);
// Initialize one atom.
xcb_atom_t GetAtom(const char* atomName);
// Initialize all used atoms.
void InitializeAtoms();
void GetWMStates();
xcb_connection_t* m_xcbConnection = nullptr;
xcb_screen_t* m_xcbRootScreen = nullptr;
xcb_window_t m_xcbWindow = 0;
int32_t m_posX;
int32_t m_posY;
bool m_fullscreenState = false;
bool m_horizontalyMaximized = false;
bool m_verticallyMaximized = false;
xcb_connection_t* m_xcbConnection = nullptr;
xcb_window_t m_xcbWindow = 0;
xcb_atom_t m_xcbAtomProtocols;
xcb_atom_t m_xcbAtomDeleteWindow;
// Use exact atom names for easy readability and usage.
xcb_atom_t WM_PROTOCOLS;
xcb_atom_t WM_DELETE_WINDOW;
// This atom is used to activate a window.
xcb_atom_t _NET_ACTIVE_WINDOW;
// This atom is use to bypass a compositor. Used during fullscreen mode.
xcb_atom_t _NET_WM_BYPASS_COMPOSITOR;
// This atom is used to change the state of a window using the WM.
xcb_atom_t _NET_WM_STATE;
// This atom is used to enable/disable fullscreen mode of a window.
xcb_atom_t _NET_WM_STATE_FULLSCREEN;
// This atom is used to extend the window to max vertically.
xcb_atom_t _NET_WM_STATE_MAXIMIZED_VERT;
// This atom is used to extend the window to max horizontally.
xcb_atom_t _NET_WM_STATE_MAXIMIZED_HORZ;
// This atom is used to position and resize a window.
xcb_atom_t _NET_MOVERESIZE_WINDOW;
// This atom is used to request the extent of the window.
xcb_atom_t _NET_REQUEST_FRAME_EXTENTS;
// This atom is used to identify the reply event for _NET_REQUEST_FRAME_EXTENTS
xcb_atom_t _NET_FRAME_EXTENTS;
// This atom is used to allow WM to kill app if not responsive anymore
xcb_atom_t _NET_WM_PID;
};
} // namespace AzFramework

@ -12,6 +12,8 @@ set(FILES
AzFramework/XcbConnectionManager.h
AzFramework/XcbInputDeviceKeyboard.cpp
AzFramework/XcbInputDeviceKeyboard.h
AzFramework/XcbInputDeviceMouse.cpp
AzFramework/XcbInputDeviceMouse.h
AzFramework/XcbInterface.h
AzFramework/XcbNativeWindow.cpp
AzFramework/XcbNativeWindow.h

@ -0,0 +1,27 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
#include <AzFramework/XcbInputDeviceMouse.h>
#endif
namespace AzFramework
{
InputDeviceMouse::Implementation* InputDeviceMouse::Implementation::Create(InputDeviceMouse& inputDevice)
{
#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
return XcbInputDeviceMouse::Create(inputDevice);
#elif PAL_TRAIT_LINUX_WINDOW_MANAGER_WAYLAND
#error "Linux Window Manager Wayland not supported."
return nullptr;
#else
#error "Linux Window Manager not recognized."
return nullptr;
#endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
}
} // namespace AzFramework

@ -22,8 +22,10 @@ if (${PAL_TRAIT_LINUX_WINDOW_MANAGER} STREQUAL "xcb")
PRIVATE
3rdParty::X11::xcb
3rdParty::X11::xcb_xkb
3rdParty::X11::xcb_xfixes
3rdParty::X11::xkbcommon
3rdParty::X11::xkbcommon_X11
xcb-xinput
)
elseif(PAL_TRAIT_LINUX_WINDOW_MANAGER STREQUAL "wayland")

@ -22,8 +22,8 @@ set(FILES
AzFramework/Windowing/NativeWindow_Linux.cpp
../Common/Unimplemented/AzFramework/Input/Devices/Gamepad/InputDeviceGamepad_Unimplemented.cpp
AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard_Linux.cpp
AzFramework/Input/Devices/Mouse/InputDeviceMouse_Linux.cpp
../Common/Unimplemented/AzFramework/Input/Devices/Motion/InputDeviceMotion_Unimplemented.cpp
../Common/Unimplemented/AzFramework/Input/Devices/Mouse/InputDeviceMouse_Unimplemented.cpp
../Common/Unimplemented/AzFramework/Input/Devices/Touch/InputDeviceTouch_Unimplemented.cpp
AzFramework/Input/User/LocalUserId_Platform.h
../Common/Default/AzFramework/Input/User/LocalUserId_Default.h

@ -12,7 +12,6 @@
#include <xcb/xcb.h>
#include <AzCore/UserSettings/UserSettingsComponent.h>
#include <AzFramework/XcbApplication.h>
#include <AzFramework/XcbInputDeviceKeyboard.h>
#include <AzFramework/Input/Buses/Notifications/InputTextNotificationBus.h>
@ -20,6 +19,7 @@
#include "Matchers.h"
#include "Actions.h"
#include "XcbBaseTestFixture.h"
#include "XcbTestApplication.h"
template<typename T>
xcb_generic_event_t MakeEvent(T event)
@ -33,6 +33,7 @@ namespace AzFramework
class XcbInputDeviceKeyboardTests
: public XcbBaseTestFixture
{
public:
void SetUp() override
{
using testing::Return;
@ -123,6 +124,15 @@ namespace AzFramework
static constexpr xcb_keycode_t s_keycodeForAKey{38};
static constexpr xcb_keycode_t s_keycodeForShiftLKey{50};
XcbTestApplication m_application{
/*enabledGamepadsCount=*/0,
/*keyboardEnabled=*/true,
/*motionEnabled=*/false,
/*mouseEnabled=*/false,
/*touchEnabled=*/false,
/*virtualKeyboardEnabled=*/false
};
};
class InputTextNotificationListener
@ -195,27 +205,23 @@ namespace AzFramework
EXPECT_CALL(m_interface, xkb_state_key_get_one_sym(&m_xkbState, s_keycodeForAKey))
.Times(2);
Application application;
application.Start({}, {});
AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize);
m_application.Start();
const InputChannel* inputChannel = InputChannelRequests::FindInputChannel(InputDeviceKeyboard::Key::AlphanumericA);
ASSERT_TRUE(inputChannel);
EXPECT_THAT(inputChannel->GetState(), Eq(InputChannel::State::Idle));
application.PumpSystemEventLoopUntilEmpty();
application.TickSystem();
application.Tick();
m_application.PumpSystemEventLoopUntilEmpty();
m_application.TickSystem();
m_application.Tick();
EXPECT_THAT(inputChannel->GetState(), Eq(InputChannel::State::Began));
application.PumpSystemEventLoopUntilEmpty();
application.TickSystem();
application.Tick();
m_application.PumpSystemEventLoopUntilEmpty();
m_application.TickSystem();
m_application.Tick();
EXPECT_THAT(inputChannel->GetState(), Eq(InputChannel::State::Ended));
application.Stop();
}
TEST_F(XcbInputDeviceKeyboardTests, TextEnteredFromXcbKeyPressEvents)
@ -420,17 +426,13 @@ namespace AzFramework
EXPECT_CALL(textListener, OnInputTextEvent(StrEq("a"), _)).Times(1);
EXPECT_CALL(textListener, OnInputTextEvent(StrEq("A"), _)).Times(1);
Application application;
application.Start({}, {});
AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize);
m_application.Start();
for (int i = 0; i < 4; ++i)
{
application.PumpSystemEventLoopUntilEmpty();
application.TickSystem();
application.Tick();
m_application.PumpSystemEventLoopUntilEmpty();
m_application.TickSystem();
m_application.Tick();
}
application.Stop();
}
} // namespace AzFramework

@ -0,0 +1,38 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/Settings/SettingsRegistry.h>
#include <AzCore/UserSettings/UserSettingsComponent.h>
#include <AzFramework/Application/Application.h>
namespace AzFramework
{
class XcbTestApplication
: public Application
{
public:
XcbTestApplication(AZ::u64 enabledGamepadsCount, bool keyboardEnabled, bool motionEnabled, bool mouseEnabled, bool touchEnabled, bool virtualKeyboardEnabled)
{
auto* settingsRegistry = AZ::SettingsRegistry::Get();
settingsRegistry->Set("/O3DE/InputSystem/GamepadsEnabled", enabledGamepadsCount);
settingsRegistry->Set("/O3DE/InputSystem/KeyboardEnabled", keyboardEnabled);
settingsRegistry->Set("/O3DE/InputSystem/MotionEnabled", motionEnabled);
settingsRegistry->Set("/O3DE/InputSystem/MouseEnabled", mouseEnabled);
settingsRegistry->Set("/O3DE/InputSystem/TouchEnabled", touchEnabled);
settingsRegistry->Set("/O3DE/InputSystem/VirtualKeyboardEnabled", virtualKeyboardEnabled);
}
void Start(const Descriptor& descriptor = {}, const StartupParameters& startupParameters = {}) override
{
Application::Start(descriptor, startupParameters);
AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize);
}
};
} // namespace AzFramework

@ -17,4 +17,5 @@ set(FILES
XcbBaseTestFixture.cpp
XcbBaseTestFixture.h
XcbInputDeviceKeyboardTests.cpp
XcbTestApplication.h
)

@ -569,11 +569,12 @@ namespace UnitTest
FillSpawnable(NumEntities);
CreateEntityReferences(refScheme);
AZ_PUSH_DISABLE_WARNING(5233, "-Wunused-lambda-capture") // Older versions of MSVC toolchain require to pass constexpr in the
// capture. Newer versions issue unused warning
auto callback =
[this, refScheme, NumEntities](AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities)
AZ_POP_DISABLE_WARNING
{
AZ_UNUSED(refScheme);
AZ_UNUSED(NumEntities);
ValidateEntityReferences(refScheme, NumEntities, entities);
};
@ -591,11 +592,12 @@ namespace UnitTest
FillSpawnable(NumEntities);
CreateEntityReferences(refScheme);
AZ_PUSH_DISABLE_WARNING(5233, "-Wunused-lambda-capture") // Older versions of MSVC toolchain require to pass constexpr in the
// capture. Newer versions issue unused warning
auto callback =
[this, refScheme, NumEntities](AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities)
AZ_POP_DISABLE_WARNING
{
AZ_UNUSED(refScheme);
AZ_UNUSED(NumEntities);
ValidateEntityReferences(refScheme, NumEntities, entities);
};
@ -720,11 +722,12 @@ namespace UnitTest
FillSpawnable(NumEntities);
CreateEntityReferences(refScheme);
AZ_PUSH_DISABLE_WARNING(5233, "-Wunused-lambda-capture") // Older versions of MSVC toolchain require to pass constexpr in the
// capture. Newer versions issue unused warning
auto callback =
[this, refScheme, NumEntities](AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities)
AZ_POP_DISABLE_WARNING
{
AZ_UNUSED(refScheme);
AZ_UNUSED(NumEntities);
ValidateEntityReferences(refScheme, NumEntities, entities);
};

@ -96,6 +96,8 @@ namespace AzGameFramework
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_CommandLine(registry, m_commandLine, false);
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_ProjectUserRegistry(registry, AZ_TRAIT_OS_PLATFORM_CODENAME, specializations, &scratchBuffer);
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_CommandLine(registry, m_commandLine, true);
#else
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_CommandLine(registry, m_commandLine, false);
#endif
// Update the Runtime file paths in case the "{BootstrapSettingsRootKey}/assets" key was overriden by a setting registry
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(registry);

@ -12,6 +12,7 @@ set(FILES
../../Utilities/QtWindowUtilities_linux.cpp
../../Utilities/ScreenGrabber_linux.cpp
../../../Platform/Linux/AzQtComponents/Components/StyledDockWidget_Linux.cpp
../../../Platform/Linux/AzQtComponents/Utilities/DesktopUtilities_Linux.cpp
../../../Platform/Linux/AzQtComponents/AzQtComponents_Traits_Linux.h
../../../Platform/Linux/AzQtComponents/AzQtComponents_Traits_Platform.h
)

@ -12,6 +12,7 @@ set(FILES
../../Utilities/QtWindowUtilities_mac.mm
../../Utilities/ScreenGrabber_mac.mm
../../../Platform/Mac/AzQtComponents/Components/StyledDockWidget_Mac.cpp
../../../Platform/Mac/AzQtComponents/Utilities/DesktopUtilities_Mac.cpp
../../../Platform/Mac/AzQtComponents/AzQtComponents_Traits_Mac.h
../../../Platform/Mac/AzQtComponents/AzQtComponents_Traits_Platform.h
)

@ -9,6 +9,7 @@
set(FILES
../../natvis/qt.natvis
../../../Platform/Windows/AzQtComponents/Utilities/HandleDpiAwareness_Windows.cpp
../../../Platform/Windows/AzQtComponents/Utilities/DesktopUtilities_Windows.cpp
../../Utilities/MouseHider_win.cpp
../../Utilities/QtWindowUtilities_win.cpp
../../Utilities/ScreenGrabber_win.cpp

@ -271,7 +271,6 @@ set(FILES
Utilities/ColorUtilities.h
Utilities/Conversions.h
Utilities/Conversions.cpp
Utilities/DesktopUtilities.cpp
Utilities/DesktopUtilities.h
Utilities/HandleDpiAwareness.cpp
Utilities/HandleDpiAwareness.h

@ -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);
}
}

@ -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,35 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzQtComponents/Utilities/DesktopUtilities.h>
#include <QDir>
#include <QProcess>
namespace AzQtComponents
{
void ShowFileOnDesktop(const QString& path)
{
// Launch explorer at the path provided
QStringList args;
if (!QFileInfo(path).isDir())
{
// Folders are just opened, files are selected
args << "/select,";
}
args << QDir::toNativeSeparators(path);
QProcess::startDetached("explorer", args);
}
QString fileBrowserActionName()
{
const char* exploreActionName = "Open in Explorer";
return QObject::tr(exploreActionName);
}
}

@ -90,19 +90,16 @@ namespace AZ
}
}
//! Filter out integration tests from the test run
void excludeIntegTests()
{
AddExcludeFilter("INTEG_*");
AddExcludeFilter("Integ_*");
}
void ApplyGlobalParameters(int* argc, char** argv)
{
// this is a hook that can be used to apply any other global non-google parameters
// that we use.
// this is a hook that can be used to apply any other global parameters that we use.
AZ_UNUSED(argc);
AZ_UNUSED(argv);
// Disable gtest catching unhandled exceptions, instead, AzTestRunner will do it through:
// AZ::Debug::Trace::HandleExceptions(true). This gives us a stack trace when the exception
// is thrown (googletest does not).
testing::FLAGS_gtest_catch_exceptions = false;
}
//! Print out parameters that are not used by the framework
@ -160,7 +157,6 @@ namespace AZ
}
::testing::InitGoogleMock(&argc, argv);
AZ::Test::excludeIntegTests();
AZ::Test::ApplyGlobalParameters(&argc, argv);
AZ::Test::printUnusedParametersWarning(argc, argv);
AZ::Test::addTestEnvironments(m_envs);
@ -281,7 +277,6 @@ namespace AZ
}
}
AZ::Test::excludeIntegTests();
AZ::Test::printUnusedParametersWarning(argc, argv);
return RUN_ALL_TESTS();

@ -104,7 +104,6 @@ namespace AZ
void addTestEnvironment(ITestEnvironment* env);
void addTestEnvironments(std::vector<ITestEnvironment*> envs);
void excludeIntegTests();
//! A hook that can be used to read any other misc parameters and remove them before google sees them.
//! Note that this modifies argc and argv to delete the parameters it consumes.
@ -266,7 +265,6 @@ namespace AZ
::testing::TestEventListeners& listeners = testing::UnitTest::GetInstance()->listeners(); \
listeners.Append(new AZ::Test::OutputEventListener); \
} \
AZ::Test::excludeIntegTests(); \
AZ::Test::ApplyGlobalParameters(&argc, argv); \
AZ::Test::printUnusedParametersWarning(argc, argv); \
AZ::Test::addTestEnvironments({TEST_ENV}); \

@ -43,8 +43,7 @@ namespace AzToolsFramework
};
//! Provides a bus to notify when the different editor modes are entered/exit.
class ViewportEditorModeNotifications
: public AZ::EBusTraits
class ViewportEditorModeNotifications : public AZ::EBusTraits
{
public:
//////////////////////////////////////////////////////////////////////////
@ -58,14 +57,17 @@ namespace AzToolsFramework
static void Reflect(AZ::ReflectContext* context);
//! Notifies subscribers of the a given viewport to the activation of the specified editor mode.
virtual void OnEditorModeActivated([[maybe_unused]] const ViewportEditorModesInterface& editorModeState, [[maybe_unused]] ViewportEditorMode mode)
virtual void OnEditorModeActivated(
[[maybe_unused]] const ViewportEditorModesInterface& editorModeState, [[maybe_unused]] ViewportEditorMode mode)
{
}
//! Notifies subscribers of the a given viewport to the deactivation of the specified editor mode.
virtual void OnEditorModeDeactivated([[maybe_unused]] const ViewportEditorModesInterface& editorModeState, [[maybe_unused]] ViewportEditorMode mode)
virtual void OnEditorModeDeactivated(
[[maybe_unused]] const ViewportEditorModesInterface& editorModeState, [[maybe_unused]] ViewportEditorMode mode)
{
}
};
using ViewportEditorModeNotificationsBus = AZ::EBus<ViewportEditorModeNotifications>;
} // namespace AzToolsFramework

@ -53,7 +53,7 @@ namespace AzToolsFramework
private slots:
void SourceDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight);
private:
int m_numberOfItemsDisplayed = 50;
AZ::u64 m_numberOfItemsDisplayed = 0;
int m_displayedItemsCounter = 0;
QPointer<AssetBrowserFilterModel> m_filterModel;
QMap<int, QModelIndex> m_indexMap;

@ -137,6 +137,7 @@ namespace AzToolsFramework
if (componentTypeIt == m_activeComponentTypes.end())
{
m_activeComponentTypes.push_back(componentType);
m_viewportUiHandlers.emplace_back(componentType);
}
// see if we already have a ComponentModeBuilder for the specific component on this entity
@ -225,6 +226,7 @@ namespace AzToolsFramework
if (!m_entitiesAndComponentModes.empty())
{
RefreshActions();
PopulateViewportUi();
}
// if entering ComponentMode not as an undo/redo step (an action was
@ -285,6 +287,10 @@ namespace AzToolsFramework
componentModeCommand.release();
}
// remove the component mode viewport border
ViewportUi::ViewportUiRequestBus::Event(
ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::RemoveViewportBorder);
// notify listeners the editor has left ComponentMode - listeners may
// wish to modify state to indicate this (e.g. appearance, functionality etc.)
m_viewportEditorModeTracker->DeactivateMode({ GetEntityContextId() }, ViewportEditorMode::Component);
@ -301,6 +307,7 @@ namespace AzToolsFramework
}
m_entitiesAndComponentModeBuilders.clear();
m_activeComponentTypes.clear();
m_viewportUiHandlers.clear();
m_componentMode = false;
m_selectedComponentModeIndex = 0;
@ -385,6 +392,24 @@ namespace AzToolsFramework
return m_activeComponentTypes.size() > 1;
}
static ComponentModeViewportUi* FindViewportUiHandlerForType(
AZStd::vector<ComponentModeViewportUi>& viewportUiHandlers, const AZ::Uuid& componentType)
{
auto handler = AZStd::find_if(
viewportUiHandlers.begin(), viewportUiHandlers.end(),
[componentType](const ComponentModeViewportUi& handler)
{
return handler.GetComponentType() == componentType;
});
if (handler == viewportUiHandlers.end())
{
return nullptr;
}
return handler;
}
bool ComponentModeCollection::ActiveComponentModeChanged(const AZ::Uuid& previousComponentType)
{
if (m_activeComponentTypes[m_selectedComponentModeIndex] != previousComponentType)
@ -410,6 +435,20 @@ namespace AzToolsFramework
// replace the current component mode by invoking the builder
// for the new 'active' component mode
componentMode.m_componentMode = componentModeBuilder->m_componentModeBuilder();
// populate the viewport UI with the new component mode
PopulateViewportUi();
// set the appropriate viewportUiHandler to active
if (auto viewportUiHandler =
FindViewportUiHandlerForType(m_viewportUiHandlers, m_activeComponentTypes[m_selectedComponentModeIndex]))
{
viewportUiHandler->SetComponentModeViewportUiActive(true);
}
ViewportUi::ViewportUiRequestBus::Event(
ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::CreateViewportBorder,
componentMode.m_componentMode->GetComponentModeName().c_str());
}
RefreshActions();
@ -519,5 +558,18 @@ namespace AzToolsFramework
}
}
void ComponentModeCollection::PopulateViewportUi()
{
// update viewport UI for new component type
if (m_selectedComponentModeIndex < m_activeComponentTypes.size())
{
// iterate over all entities and their active Component Mode, populate viewport UI for the new mode
for (auto& entityAndComponentMode : m_entitiesAndComponentModes)
{
// build viewport UI based on current state
entityAndComponentMode.m_componentMode->PopulateViewportUi();
}
}
}
} // namespace ComponentModeFramework
} // namespace AzToolsFramework

@ -55,7 +55,7 @@ namespace AzToolsFramework
GetEntityComponentIdPair(), elementIdsToDisplay);
// create the component mode border with the specific name for this component mode
ViewportUi::ViewportUiRequestBus::Event(
ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::CreateComponentModeBorder,
ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::CreateViewportBorder,
GetComponentModeName());
// set the EntityComponentId for this ComponentMode to active in the ComponentModeViewportUi system
ComponentModeViewportUiRequestBus::Event(

@ -38,7 +38,7 @@ namespace AzToolsFramework
virtual SettingOutcome GetValue(const AZStd::string_view path) = 0;
virtual SettingOutcome SetValue(const AZStd::string_view path, const AZStd::any& value) = 0;
virtual ConsoleColorTheme GetConsoleColorTheme() const = 0;
virtual int GetMaxNumberOfItemsShownInSearchView() const = 0;
virtual AZ::u64 GetMaxNumberOfItemsShownInSearchView() const = 0;
};
using EditorSettingsAPIBus = AZ::EBus<EditorSettingsAPIRequests>;

@ -28,8 +28,10 @@ namespace AzToolsFramework
//////////////////////////////////////////////////////////////////////////
//! Triggered when the editor focus is changed to a different entity.
//! @param entityId The entity the focus has been moved to.
virtual void OnEditorFocusChanged(AZ::EntityId entityId) = 0;
//! @param previousFocusEntityId The entity the focus has been moved from.
//! @param newFocusEntityId The entity the focus has been moved to.
virtual void OnEditorFocusChanged(
[[maybe_unused]] AZ::EntityId previousFocusEntityId, [[maybe_unused]] AZ::EntityId newFocusEntityId) {}
protected:
~FocusModeNotifications() = default;

@ -83,8 +83,9 @@ namespace AzToolsFramework
}
}
AZ::EntityId previousFocusEntityId = m_focusRoot;
m_focusRoot = entityId;
FocusModeNotificationBus::Broadcast(&FocusModeNotifications::OnEditorFocusChanged, m_focusRoot);
FocusModeNotificationBus::Broadcast(&FocusModeNotifications::OnEditorFocusChanged, previousFocusEntityId, m_focusRoot);
}
void FocusModeSystemComponent::ClearFocusRoot([[maybe_unused]] AzFramework::EntityContextId entityContextId)

@ -116,7 +116,7 @@ namespace AzToolsFramework
{
return AZ::Intersect::IntersectRayBox(
rayOrigin, rayDirection, m_center, m_axis1, m_axis2, m_axis3, m_halfExtents.GetX(), m_halfExtents.GetY(),
m_halfExtents.GetZ(), rayIntersectionDistance) > 0;
m_halfExtents.GetZ(), rayIntersectionDistance);
}
void ManipulatorBoundBox::SetShapeData(const BoundRequestShapeBase& shapeData)

@ -262,7 +262,7 @@ namespace AzToolsFramework
if (assetId.IsValid())
{
asset.Create(assetId, true);
asset.Create(assetId, false);
}
}
};

@ -8,12 +8,14 @@
#include <AzToolsFramework/Prefab/PrefabFocusHandler.h>
#include <AzToolsFramework/Commands/SelectionCommand.h>
#include <AzToolsFramework/ContainerEntity/ContainerEntityInterface.h>
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
#include <AzToolsFramework/Entity/PrefabEditorEntityOwnershipInterface.h>
#include <AzToolsFramework/Prefab/Instance/Instance.h>
#include <AzToolsFramework/Prefab/Instance/InstanceEntityMapperInterface.h>
#include <AzToolsFramework/Prefab/PrefabFocusNotificationBus.h>
#include <AzToolsFramework/Prefab/PrefabFocusUndo.h>
namespace AzToolsFramework::Prefab
{
@ -28,10 +30,12 @@ namespace AzToolsFramework::Prefab
EditorEntityContextNotificationBus::Handler::BusConnect();
AZ::Interface<PrefabFocusInterface>::Register(this);
AZ::Interface<PrefabFocusPublicInterface>::Register(this);
}
PrefabFocusHandler::~PrefabFocusHandler()
{
AZ::Interface<PrefabFocusPublicInterface>::Unregister(this);
AZ::Interface<PrefabFocusInterface>::Unregister(this);
EditorEntityContextNotificationBus::Handler::BusDisconnect();
}
@ -61,6 +65,44 @@ namespace AzToolsFramework::Prefab
}
PrefabFocusOperationResult PrefabFocusHandler::FocusOnOwningPrefab(AZ::EntityId entityId)
{
// Initialize Undo Batch object
ScopedUndoBatch undoBatch("Edit Prefab");
// Clear selection
{
const EntityIdList selectedEntities = EntityIdList{};
auto selectionUndo = aznew SelectionCommand(selectedEntities, "Clear Selection");
selectionUndo->SetParent(undoBatch.GetUndoBatch());
ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequestBus::Events::SetSelectedEntities, selectedEntities);
}
// Edit Prefab
{
auto editUndo = aznew PrefabFocusUndo("Edit Prefab");
editUndo->Capture(entityId);
editUndo->SetParent(undoBatch.GetUndoBatch());
ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequestBus::Events::RunRedoSeparately, editUndo);
}
return AZ::Success();
}
PrefabFocusOperationResult PrefabFocusHandler::FocusOnPathIndex([[maybe_unused]] AzFramework::EntityContextId entityContextId, int index)
{
if (index < 0 || index >= m_instanceFocusVector.size())
{
return AZ::Failure(AZStd::string("Prefab Focus Handler: Invalid index on FocusOnPathIndex."));
}
InstanceOptionalReference focusedInstance = m_instanceFocusVector[index];
FocusOnOwningPrefab(focusedInstance->get().GetContainerEntityId());
return AZ::Success();
}
PrefabFocusOperationResult PrefabFocusHandler::FocusOnPrefabInstanceOwningEntityId(AZ::EntityId entityId)
{
InstanceOptionalReference focusedInstance;
@ -85,18 +127,6 @@ namespace AzToolsFramework::Prefab
return FocusOnPrefabInstance(focusedInstance);
}
PrefabFocusOperationResult PrefabFocusHandler::FocusOnPathIndex([[maybe_unused]] AzFramework::EntityContextId entityContextId, int index)
{
if (index < 0 || index >= m_instanceFocusVector.size())
{
return AZ::Failure(AZStd::string("Prefab Focus Handler: Invalid index on FocusOnPathIndex."));
}
InstanceOptionalReference focusedInstance = m_instanceFocusVector[index];
return FocusOnPrefabInstance(focusedInstance);
}
PrefabFocusOperationResult PrefabFocusHandler::FocusOnPrefabInstance(InstanceOptionalReference focusedInstance)
{
if (!focusedInstance.has_value())
@ -122,17 +152,10 @@ namespace AzToolsFramework::Prefab
if (focusedInstance->get().GetParentInstance() != AZStd::nullopt)
{
containerEntityId = focusedInstance->get().GetContainerEntityId();
// Select the container entity
AzToolsFramework::SelectEntity(containerEntityId);
}
else
{
containerEntityId = AZ::EntityId();
// Clear the selection
AzToolsFramework::SelectEntities({});
}
// Focus on the descendants of the container entity
@ -161,6 +184,17 @@ namespace AzToolsFramework::Prefab
return m_focusedInstance;
}
AZ::EntityId PrefabFocusHandler::GetFocusedPrefabContainerEntityId([[maybe_unused]] AzFramework::EntityContextId entityContextId) const
{
if (!m_focusedInstance.has_value())
{
// PrefabFocusHandler has not been initialized yet.
return AZ::EntityId();
}
return m_focusedInstance->get().GetContainerEntityId();
}
bool PrefabFocusHandler::IsOwningPrefabBeingFocused(AZ::EntityId entityId) const
{
if (!m_focusedInstance.has_value())
@ -200,7 +234,7 @@ namespace AzToolsFramework::Prefab
m_instanceFocusVector.clear();
// Focus on the root prefab (AZ::EntityId() will default to it)
FocusOnOwningPrefab(AZ::EntityId());
FocusOnPrefabInstanceOwningEntityId(AZ::EntityId());
}
void PrefabFocusHandler::RefreshInstanceFocusList()

@ -13,6 +13,7 @@
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
#include <AzToolsFramework/FocusMode/FocusModeInterface.h>
#include <AzToolsFramework/Prefab/PrefabFocusInterface.h>
#include <AzToolsFramework/Prefab/PrefabFocusPublicInterface.h>
#include <AzToolsFramework/Prefab/Template/Template.h>
namespace AzToolsFramework
@ -28,6 +29,7 @@ namespace AzToolsFramework::Prefab
//! Handles Prefab Focus mode, determining which prefab file entity changes will target.
class PrefabFocusHandler final
: private PrefabFocusInterface
, private PrefabFocusPublicInterface
, private EditorEntityContextNotificationBus::Handler
{
public:
@ -39,10 +41,14 @@ namespace AzToolsFramework::Prefab
void Initialize();
// PrefabFocusInterface overrides ...
PrefabFocusOperationResult FocusOnOwningPrefab(AZ::EntityId entityId) override;
PrefabFocusOperationResult FocusOnPathIndex(AzFramework::EntityContextId entityContextId, int index) override;
PrefabFocusOperationResult FocusOnPrefabInstanceOwningEntityId(AZ::EntityId entityId) override;
TemplateId GetFocusedPrefabTemplateId(AzFramework::EntityContextId entityContextId) const override;
InstanceOptionalReference GetFocusedPrefabInstance(AzFramework::EntityContextId entityContextId) const override;
// PrefabFocusPublicInterface overrides ...
PrefabFocusOperationResult FocusOnOwningPrefab(AZ::EntityId entityId) override;
PrefabFocusOperationResult FocusOnPathIndex(AzFramework::EntityContextId entityContextId, int index) override;
AZ::EntityId GetFocusedPrefabContainerEntityId(AzFramework::EntityContextId entityContextId) const override;
bool IsOwningPrefabBeingFocused(AZ::EntityId entityId) const override;
const AZ::IO::Path& GetPrefabFocusPath(AzFramework::EntityContextId entityContextId) const override;
const int GetPrefabFocusPathLength(AzFramework::EntityContextId entityContextId) const override;

@ -20,7 +20,7 @@ namespace AzToolsFramework::Prefab
{
using PrefabFocusOperationResult = AZ::Outcome<void, AZStd::string>;
//! Interface to handle operations related to the Prefab Focus system.
//! Interface to handle internal operations related to the Prefab Focus system.
class PrefabFocusInterface
{
public:
@ -28,29 +28,13 @@ namespace AzToolsFramework::Prefab
//! Set the focused prefab instance to the owning instance of the entityId provided.
//! @param entityId The entityId of the entity whose owning instance we want the prefab system to focus on.
virtual PrefabFocusOperationResult FocusOnOwningPrefab(AZ::EntityId entityId) = 0;
//! Set the focused prefab instance to the instance at position index of the current path.
//! @param index The index of the instance in the current path that we want the prefab system to focus on.
virtual PrefabFocusOperationResult FocusOnPathIndex(AzFramework::EntityContextId entityContextId, int index) = 0;
virtual PrefabFocusOperationResult FocusOnPrefabInstanceOwningEntityId(AZ::EntityId entityId) = 0;
//! Returns the template id of the instance the prefab system is focusing on.
virtual TemplateId GetFocusedPrefabTemplateId(AzFramework::EntityContextId entityContextId) const = 0;
//! Returns a reference to the instance the prefab system is focusing on.
virtual InstanceOptionalReference GetFocusedPrefabInstance(AzFramework::EntityContextId entityContextId) const = 0;
//! Returns whether the entity belongs to the instance that is being focused on, or one of its descendants.
//! @param entityId The entityId of the queried entity.
//! @return true if the entity belongs to the focused instance or one of its descendants, false otherwise.
virtual bool IsOwningPrefabBeingFocused(AZ::EntityId entityId) const = 0;
//! Returns the path from the root instance to the currently focused instance.
//! @return A path composed from the names of the container entities for the instance path.
virtual const AZ::IO::Path& GetPrefabFocusPath(AzFramework::EntityContextId entityContextId) const = 0;
//! Returns the size of the path to the currently focused instance.
virtual const int GetPrefabFocusPathLength(AzFramework::EntityContextId entityContextId) const = 0;
};
} // namespace AzToolsFramework::Prefab

@ -0,0 +1,53 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/Interface/Interface.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzFramework/Entity/EntityContext.h>
#include <AzToolsFramework/Prefab/Instance/Instance.h>
#include <AzToolsFramework/Prefab/Template/Template.h>
namespace AzToolsFramework::Prefab
{
using PrefabFocusOperationResult = AZ::Outcome<void, AZStd::string>;
//! Public Interface for external systems to utilize the Prefab Focus system.
class PrefabFocusPublicInterface
{
public:
AZ_RTTI(PrefabFocusPublicInterface, "{53EE1D18-A41F-4DB1-9B73-9448F425722E}");
//! Set the focused prefab instance to the owning instance of the entityId provided. Supports undo/redo.
//! @param entityId The entityId of the entity whose owning instance we want the prefab system to focus on.
virtual PrefabFocusOperationResult FocusOnOwningPrefab(AZ::EntityId entityId) = 0;
//! Set the focused prefab instance to the instance at position index of the current path. Supports undo/redo.
//! @param index The index of the instance in the current path that we want the prefab system to focus on.
virtual PrefabFocusOperationResult FocusOnPathIndex(AzFramework::EntityContextId entityContextId, int index) = 0;
//! Returns the entity id of the container entity for the instance the prefab system is focusing on.
virtual AZ::EntityId GetFocusedPrefabContainerEntityId(AzFramework::EntityContextId entityContextId) const = 0;
//! Returns whether the entity belongs to the instance that is being focused on, or one of its descendants.
//! @param entityId The entityId of the queried entity.
//! @return true if the entity belongs to the focused instance or one of its descendants, false otherwise.
virtual bool IsOwningPrefabBeingFocused(AZ::EntityId entityId) const = 0;
//! Returns the path from the root instance to the currently focused instance.
//! @return A path composed from the names of the container entities for the instance path.
virtual const AZ::IO::Path& GetPrefabFocusPath(AzFramework::EntityContextId entityContextId) const = 0;
//! Returns the size of the path to the currently focused instance.
virtual const int GetPrefabFocusPathLength(AzFramework::EntityContextId entityContextId) const = 0;
};
} // namespace AzToolsFramework::Prefab

@ -0,0 +1,52 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzToolsFramework/Prefab/PrefabFocusUndo.h>
#include <AzCore/Interface/Interface.h>
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
#include <AzToolsFramework/Prefab/PrefabFocusInterface.h>
#include <AzToolsFramework/Prefab/PrefabFocusPublicInterface.h>
namespace AzToolsFramework::Prefab
{
PrefabFocusUndo::PrefabFocusUndo(const AZStd::string& undoOperationName)
: UndoSystem::URSequencePoint(undoOperationName)
{
m_prefabFocusInterface = AZ::Interface<PrefabFocusInterface>::Get();
AZ_Assert(m_prefabFocusInterface, "PrefabFocusUndo - Failed to grab prefab focus interface");
m_prefabFocusPublicInterface = AZ::Interface<PrefabFocusPublicInterface>::Get();
AZ_Assert(m_prefabFocusPublicInterface, "PrefabFocusUndo - Failed to grab prefab focus public interface");
}
bool PrefabFocusUndo::Changed() const
{
return true;
}
void PrefabFocusUndo::Capture(AZ::EntityId entityId)
{
auto entityContextId = AzFramework::EntityContextId::CreateNull();
EditorEntityContextRequestBus::BroadcastResult(entityContextId, &EditorEntityContextRequests::GetEditorEntityContextId);
m_beforeEntityId = m_prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(entityContextId);
m_afterEntityId = entityId;
}
void PrefabFocusUndo::Undo()
{
m_prefabFocusInterface->FocusOnPrefabInstanceOwningEntityId(m_beforeEntityId);
}
void PrefabFocusUndo::Redo()
{
m_prefabFocusInterface->FocusOnPrefabInstanceOwningEntityId(m_afterEntityId);
}
} // namespace AzToolsFramework::Prefab

@ -0,0 +1,39 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/Component/EntityId.h>
#include <AzToolsFramework/Undo/UndoSystem.h>
namespace AzToolsFramework::Prefab
{
class PrefabFocusInterface;
class PrefabFocusPublicInterface;
//! Undo node for prefab focus change operations.
class PrefabFocusUndo
: public UndoSystem::URSequencePoint
{
public:
explicit PrefabFocusUndo(const AZStd::string& undoOperationName);
bool Changed() const override;
void Capture(AZ::EntityId entityId);
void Undo() override;
void Redo() override;
protected:
PrefabFocusInterface* m_prefabFocusInterface = nullptr;
PrefabFocusPublicInterface* m_prefabFocusPublicInterface = nullptr;
AZ::EntityId m_beforeEntityId;
AZ::EntityId m_afterEntityId;
};
} // namespace AzToolsFramework::Prefab

@ -43,6 +43,12 @@ namespace AzToolsFramework
m_instanceToTemplateInterface = AZ::Interface<InstanceToTemplateInterface>::Get();
AZ_Assert(m_instanceToTemplateInterface, "PrefabPublicHandler - Could not retrieve instance of InstanceToTemplateInterface");
m_prefabFocusInterface = AZ::Interface<PrefabFocusInterface>::Get();
AZ_Assert(m_prefabFocusInterface, "Could not get PrefabFocusInterface on PrefabPublicHandler construction.");
m_prefabFocusPublicInterface = AZ::Interface<PrefabFocusPublicInterface>::Get();
AZ_Assert(m_prefabFocusPublicInterface, "Could not get PrefabFocusPublicInterface on PrefabPublicHandler construction.");
m_prefabLoaderInterface = AZ::Interface<PrefabLoaderInterface>::Get();
AZ_Assert(m_prefabLoaderInterface, "Could not get PrefabLoaderInterface on PrefabPublicHandler construction.");
@ -552,6 +558,13 @@ namespace AzToolsFramework
PrefabEntityResult PrefabPublicHandler::CreateEntity(AZ::EntityId parentId, const AZ::Vector3& position)
{
// If the parent is invalid, parent to the container of the currently focused prefab.
if (!parentId.IsValid())
{
AzFramework::EntityContextId editorEntityContextId = AzToolsFramework::GetEntityContextId();
parentId = m_prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(editorEntityContextId);
}
InstanceOptionalReference owningInstanceOfParentEntity = GetOwnerInstanceByEntityId(parentId);
if (!owningInstanceOfParentEntity)
{
@ -968,13 +981,13 @@ namespace AzToolsFramework
return AZ::Failure(AZStd::string("No entities to duplicate."));
}
const EntityIdList entityIdsNoLevelInstance = GenerateEntityIdListWithoutLevelInstance(entityIds);
if (entityIdsNoLevelInstance.empty())
const EntityIdList entityIdsNoFocusContainer = GenerateEntityIdListWithoutFocusedInstanceContainer(entityIds);
if (entityIdsNoFocusContainer.empty())
{
return AZ::Failure(AZStd::string("No entities to duplicate because only instance selected is the level instance."));
return AZ::Failure(AZStd::string("No entities to duplicate because only instance selected is the container entity of the focused instance."));
}
if (!EntitiesBelongToSameInstance(entityIdsNoLevelInstance))
if (!EntitiesBelongToSameInstance(entityIdsNoFocusContainer))
{
return AZ::Failure(AZStd::string("Cannot duplicate multiple entities belonging to different instances with one operation."
"Change your selection to contain entities in the same instance."));
@ -982,7 +995,7 @@ namespace AzToolsFramework
// We've already verified the entities are all owned by the same instance,
// so we can just retrieve our instance from the first entity in the list.
AZ::EntityId firstEntityIdToDuplicate = entityIdsNoLevelInstance[0];
AZ::EntityId firstEntityIdToDuplicate = entityIdsNoFocusContainer[0];
InstanceOptionalReference commonOwningInstance = GetOwnerInstanceByEntityId(firstEntityIdToDuplicate);
if (!commonOwningInstance.has_value())
{
@ -1002,7 +1015,7 @@ namespace AzToolsFramework
// This will cull out any entities that have ancestors in the list, since we will end up duplicating
// the full nested hierarchy with what is returned from RetrieveAndSortPrefabEntitiesAndInstances
AzToolsFramework::EntityIdSet duplicationSet = AzToolsFramework::GetCulledEntityHierarchy(entityIdsNoLevelInstance);
AzToolsFramework::EntityIdSet duplicationSet = AzToolsFramework::GetCulledEntityHierarchy(entityIdsNoFocusContainer);
AZ_PROFILE_FUNCTION(AzToolsFramework);
@ -1039,10 +1052,10 @@ namespace AzToolsFramework
DuplicateNestedEntitiesInInstance(commonOwningInstance->get(),
entities, instanceDomAfter, duplicatedEntityAndInstanceIds, duplicateEntityAliasMap);
PrefabUndoInstance* command = aznew PrefabUndoInstance("Entity/Instance duplication");
PrefabUndoInstance* command = aznew PrefabUndoInstance("Entity/Instance duplication", false);
command->SetParent(undoBatch.GetUndoBatch());
command->Capture(instanceDomBefore, instanceDomAfter, commonOwningInstance->get().GetTemplateId());
command->RedoBatched();
command->Redo();
DuplicateNestedInstancesInInstance(commonOwningInstance->get(),
instances, instanceDomAfter, duplicatedEntityAndInstanceIds, newInstanceAliasToOldInstanceMap);
@ -1106,19 +1119,21 @@ namespace AzToolsFramework
PrefabOperationResult PrefabPublicHandler::DeleteFromInstance(const EntityIdList& entityIds, bool deleteDescendants)
{
const EntityIdList entityIdsNoLevelInstance = GenerateEntityIdListWithoutLevelInstance(entityIds);
// Remove the container entity of the focused prefab from the list, if it is included.
const EntityIdList entityIdsNoFocusContainer = GenerateEntityIdListWithoutFocusedInstanceContainer(entityIds);
if (entityIdsNoLevelInstance.empty())
if (entityIdsNoFocusContainer.empty())
{
return AZ::Success();
}
if (!EntitiesBelongToSameInstance(entityIdsNoLevelInstance))
// All entities in this list need to belong to the same prefab instance for the operation to be valid.
if (!EntitiesBelongToSameInstance(entityIdsNoFocusContainer))
{
return AZ::Failure(AZStd::string("Cannot delete multiple entities belonging to different instances with one operation."));
}
AZ::EntityId firstEntityIdToDelete = entityIdsNoLevelInstance[0];
AZ::EntityId firstEntityIdToDelete = entityIdsNoFocusContainer[0];
InstanceOptionalReference commonOwningInstance = GetOwnerInstanceByEntityId(firstEntityIdToDelete);
// If the first entity id is a container entity id, then we need to mark its parent as the common owning instance because you
@ -1128,8 +1143,15 @@ namespace AzToolsFramework
commonOwningInstance = commonOwningInstance->get().GetParentInstance();
}
// We only allow explicit deletions for entities inside the currently focused prefab.
AzFramework::EntityContextId editorEntityContextId = AzToolsFramework::GetEntityContextId();
if (&m_prefabFocusInterface->GetFocusedPrefabInstance(editorEntityContextId)->get() != &commonOwningInstance->get())
{
return AZ::Failure(AZStd::string("Cannot delete entities belonging to an instance that is not being edited."));
}
// Retrieve entityList from entityIds
EntityList inputEntityList = EntityIdListToEntityList(entityIdsNoLevelInstance);
EntityList inputEntityList = EntityIdListToEntityList(entityIdsNoFocusContainer);
AZ_PROFILE_FUNCTION(AzToolsFramework);
@ -1186,7 +1208,7 @@ namespace AzToolsFramework
}
else
{
for (AZ::EntityId entityId : entityIdsNoLevelInstance)
for (AZ::EntityId entityId : entityIdsNoFocusContainer)
{
InstanceOptionalReference owningInstance = m_instanceEntityMapperInterface->FindOwningInstance(entityId);
// If this is the container entity, it actually represents the instance so get its owner
@ -1227,9 +1249,12 @@ namespace AzToolsFramework
return AZ::Failure(AZStd::string("Cannot detach Prefab Instance with invalid container entity."));
}
if (IsLevelInstanceContainerEntity(containerEntityId))
auto editorEntityContextId = AzFramework::EntityContextId::CreateNull();
EditorEntityContextRequestBus::BroadcastResult(editorEntityContextId, &EditorEntityContextRequests::GetEditorEntityContextId);
if (containerEntityId == m_prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(editorEntityContextId))
{
return AZ::Failure(AZStd::string("Cannot detach level Prefab Instance."));
return AZ::Failure(AZStd::string("Cannot detach focused Prefab Instance."));
}
InstanceOptionalReference owningInstance = GetOwnerInstanceByEntityId(containerEntityId);
@ -1298,7 +1323,7 @@ namespace AzToolsFramework
Prefab::PrefabDom instanceDomAfter;
m_instanceToTemplateInterface->GenerateDomForInstance(instanceDomAfter, parentInstance);
PrefabUndoInstance* command = aznew PrefabUndoInstance("Instance detachment");
PrefabUndoInstance* command = aznew PrefabUndoInstance("Instance detachment", false);
command->Capture(instanceDomBefore, instanceDomAfter, parentTemplateId);
command->SetParent(undoBatch.GetUndoBatch());
{
@ -1452,9 +1477,14 @@ namespace AzToolsFramework
AZStd::queue<AZ::Entity*> entityQueue;
auto editorEntityContextId = AzFramework::EntityContextId::CreateNull();
EditorEntityContextRequestBus::BroadcastResult(editorEntityContextId, &EditorEntityContextRequests::GetEditorEntityContextId);
AZ::EntityId focusedPrefabContainerEntityId =
m_prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(editorEntityContextId);
for (auto inputEntity : inputEntities)
{
if (inputEntity && !IsLevelInstanceContainerEntity(inputEntity->GetId()))
if (inputEntity && inputEntity->GetId() != focusedPrefabContainerEntityId)
{
entityQueue.push(inputEntity);
}
@ -1548,19 +1578,19 @@ namespace AzToolsFramework
return AZ::Success();
}
EntityIdList PrefabPublicHandler::GenerateEntityIdListWithoutLevelInstance(
EntityIdList PrefabPublicHandler::GenerateEntityIdListWithoutFocusedInstanceContainer(
const EntityIdList& entityIds) const
{
EntityIdList outEntityIds;
outEntityIds.reserve(entityIds.size()); // Actual size could be smaller.
EntityIdList outEntityIds(entityIds);
AzFramework::EntityContextId editorEntityContextId = AzToolsFramework::GetEntityContextId();
AZ::EntityId focusedInstanceContainerEntityId = m_prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(editorEntityContextId);
for (const AZ::EntityId& entityId : entityIds)
if (auto iter = AZStd::find(outEntityIds.begin(), outEntityIds.end(), focusedInstanceContainerEntityId); iter != outEntityIds.end())
{
if (!IsLevelInstanceContainerEntity(entityId))
{
outEntityIds.emplace_back(entityId);
}
outEntityIds.erase(iter);
}
return outEntityIds;
}

@ -74,7 +74,7 @@ namespace AzToolsFramework
Instance& commonRootEntityOwningInstance,
EntityList& outEntities,
AZStd::vector<Instance*>& outInstances) const;
EntityIdList GenerateEntityIdListWithoutLevelInstance(const EntityIdList& entityIds) const;
EntityIdList GenerateEntityIdListWithoutFocusedInstanceContainer(const EntityIdList& entityIds) const;
InstanceOptionalReference GetOwnerInstanceByEntityId(AZ::EntityId entityId) const;
bool EntitiesBelongToSameInstance(const EntityIdList& entityIds) const;
@ -187,6 +187,8 @@ namespace AzToolsFramework
InstanceEntityMapperInterface* m_instanceEntityMapperInterface = nullptr;
InstanceToTemplateInterface* m_instanceToTemplateInterface = nullptr;
PrefabFocusInterface* m_prefabFocusInterface = nullptr;
PrefabFocusPublicInterface* m_prefabFocusPublicInterface = nullptr;
PrefabLoaderInterface* m_prefabLoaderInterface = nullptr;
PrefabSystemComponentInterface* m_prefabSystemComponentInterface = nullptr;

@ -6,11 +6,14 @@
*
*/
#include <API/ToolsApplicationAPI.h>
#include <AzCore/Component/ComponentApplicationBus.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <Prefab/PrefabSystemComponentInterface.h>
#include <Prefab/PrefabSystemScriptingHandler.h>
#include <AzCore/Component/Entity.h>
#include <Prefab/EditorPrefabComponent.h>
#include <ToolsComponents/TransformComponent.h>
namespace AzToolsFramework::Prefab
{
@ -61,9 +64,30 @@ namespace AzToolsFramework::Prefab
entities.push_back(entity);
}
}
auto prefab = m_prefabSystemComponentInterface->CreatePrefab(entities, {}, AZ::IO::PathView(AZStd::string_view(filePath)));
bool result = false;
[[maybe_unused]] AZ::EntityId commonRoot;
EntityList topLevelEntities;
AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(result, &AzToolsFramework::ToolsApplicationRequestBus::Events::FindCommonRootInactive,
entities, commonRoot, &topLevelEntities);
auto containerEntity = AZStd::make_unique<AZ::Entity>();
containerEntity->CreateComponent<Prefab::EditorPrefabComponent>();
for (AZ::Entity* entity : topLevelEntities)
{
AzToolsFramework::Components::TransformComponent* transformComponent =
entity->FindComponent<AzToolsFramework::Components::TransformComponent>();
if (transformComponent)
{
transformComponent->SetParent(containerEntity->GetId());
}
}
auto prefab = m_prefabSystemComponentInterface->CreatePrefab(
entities, {}, AZ::IO::PathView(AZStd::string_view(filePath)), AZStd::move(containerEntity));
if (!prefab)
{
AZ_Error("PrefabSystemComponenent", false, "Failed to create prefab %s", filePath.c_str());

@ -17,17 +17,16 @@ namespace AzToolsFramework
{
PrefabUndoBase::PrefabUndoBase(const AZStd::string& undoOperationName)
: UndoSystem::URSequencePoint(undoOperationName)
, m_changed(true)
, m_templateId(InvalidTemplateId)
{
m_instanceToTemplateInterface = AZ::Interface<InstanceToTemplateInterface>::Get();
AZ_Assert(m_instanceToTemplateInterface, "Failed to grab instance to template interface");
}
//PrefabInstanceUndo
PrefabUndoInstance::PrefabUndoInstance(const AZStd::string& undoOperationName)
PrefabUndoInstance::PrefabUndoInstance(const AZStd::string& undoOperationName, const bool useImmediatePropagation)
: PrefabUndoBase(undoOperationName)
{
m_useImmediatePropagation = useImmediatePropagation;
}
void PrefabUndoInstance::Capture(
@ -43,17 +42,12 @@ namespace AzToolsFramework
void PrefabUndoInstance::Undo()
{
m_instanceToTemplateInterface->PatchTemplate(m_undoPatch, m_templateId, true);
m_instanceToTemplateInterface->PatchTemplate(m_undoPatch, m_templateId, m_useImmediatePropagation);
}
void PrefabUndoInstance::Redo()
{
m_instanceToTemplateInterface->PatchTemplate(m_redoPatch, m_templateId, true);
}
void PrefabUndoInstance::RedoBatched()
{
m_instanceToTemplateInterface->PatchTemplate(m_redoPatch, m_templateId);
m_instanceToTemplateInterface->PatchTemplate(m_redoPatch, m_templateId, m_useImmediatePropagation);
}

@ -29,14 +29,15 @@ namespace AzToolsFramework
bool Changed() const override { return m_changed; }
protected:
TemplateId m_templateId;
TemplateId m_templateId = InvalidTemplateId;
PrefabDom m_redoPatch;
PrefabDom m_undoPatch;
InstanceToTemplateInterface* m_instanceToTemplateInterface = nullptr;
bool m_changed;
bool m_changed = true;
bool m_useImmediatePropagation = true;
};
//! handles the addition and removal of entities from instances
@ -44,7 +45,7 @@ namespace AzToolsFramework
: public PrefabUndoBase
{
public:
explicit PrefabUndoInstance(const AZStd::string& undoOperationName);
explicit PrefabUndoInstance(const AZStd::string& undoOperationName, const bool useImmediatePropagation = true);
void Capture(
const PrefabDom& initialState,
@ -53,7 +54,6 @@ namespace AzToolsFramework
void Undo() override;
void Redo() override;
void RedoBatched();
};
//! handles entity updates, such as when the values on an entity change

@ -23,10 +23,10 @@ namespace AzToolsFramework
PrefabDom instanceDomAfterUpdate;
PrefabDomUtils::StoreInstanceInPrefabDom(instance, instanceDomAfterUpdate);
PrefabUndoInstance* state = aznew Prefab::PrefabUndoInstance(undoMessage);
PrefabUndoInstance* state = aznew Prefab::PrefabUndoInstance(undoMessage, false);
state->Capture(instanceDomBeforeUpdate, instanceDomAfterUpdate, instance.GetTemplateId());
state->SetParent(undoBatch);
state->RedoBatched();
state->Redo();
}
LinkId CreateLink(

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

Loading…
Cancel
Save