Ver código fonte

Merge branch 'development' into blast-experimental

Signed-off-by: moraaar <[email protected]>
moraaar 2 anos atrás
pai
commit
cde88a63f3
100 arquivos alterados com 2771 adições e 191 exclusões
  1. 20 0
      .github/ISSUE_TEMPLATE/ar_bug_report.md.txt
  2. 50 0
      .github/ISSUE_TEMPLATE/bug_template.md.txt
  3. 6 0
      .github/ISSUE_TEMPLATE/config.yml.txt
  4. 23 0
      .github/ISSUE_TEMPLATE/deprecation_template.md.txt
  5. 20 0
      .github/ISSUE_TEMPLATE/feature_request.md.txt
  6. 13 0
      Gems/ProteusRobot/Assets/Materials/Proteus_chassis_Front_Double_Lights.material
  7. 13 0
      Gems/ProteusRobot/Assets/Materials/Proteus_chassis_Front_Light.material
  8. 13 0
      Gems/ProteusRobot/Assets/Materials/Proteus_chassis_Indicator.material
  9. 18 0
      Gems/ProteusRobot/Assets/Materials/Proteus_chassis_Proteus.material
  10. 13 0
      Gems/ProteusRobot/Assets/Materials/Proteus_chassis_Rear_Lights.material
  11. 13 0
      Gems/ProteusRobot/Assets/Materials/Proteus_chassis_Side_Lights.material
  12. 19 0
      Gems/ProteusRobot/Assets/Materials/Proteus_chassis_wheel.material
  13. 14 0
      Gems/ProteusRobot/Assets/Materials/Proteus_headLamp.material
  14. 4 0
      Gems/ProteusRobot/Assets/Materials/untitled.material
  15. 257 0
      Gems/ProteusRobot/Assets/Proteus.prefab
  16. 3 0
      Gems/ProteusRobot/Assets/Proteus2_chassis.fbx
  17. 3 0
      Gems/ProteusRobot/Assets/Proteus2_lift.fbx
  18. 3 0
      Gems/ProteusRobot/Assets/Proteus_chassis.fbx
  19. 151 0
      Gems/ProteusRobot/Assets/Proteus_chassis.fbx.assetinfo
  20. 3 0
      Gems/ProteusRobot/Assets/Proteus_wheel.fbx
  21. 88 0
      Gems/ProteusRobot/Assets/Proteus_wheel.fbx.assetinfo
  22. 45 0
      Gems/ProteusRobot/Assets/Scripts/ProteusRobot_inputs.inputbindings
  23. 3 0
      Gems/ProteusRobot/Assets/Textures/Lights_Emissive.png
  24. 3 0
      Gems/ProteusRobot/Assets/Textures/Proteus_BaseMap.png
  25. 3 0
      Gems/ProteusRobot/Assets/Textures/Proteus_MaskMap.png
  26. 3 0
      Gems/ProteusRobot/Assets/Textures/Proteus_MaskMap_A.png
  27. 3 0
      Gems/ProteusRobot/Assets/Textures/Proteus_MaskMap_R.png
  28. 3 0
      Gems/ProteusRobot/Assets/Textures/Proteus_Normal.png
  29. 18 0
      Gems/ProteusRobot/Assets/no_friction.physxmaterial
  30. 18 0
      Gems/ProteusRobot/Assets/robot_tire.physxmaterial
  31. 22 0
      Gems/ProteusRobot/CMakeLists.txt
  32. 210 0
      Gems/ProteusRobot/Code/CMakeLists.txt
  33. 31 0
      Gems/ProteusRobot/Code/Include/ProteusRobot/ProteusRobotBus.h
  34. 4 0
      Gems/ProteusRobot/Code/Platform/Android/PAL_android.cmake
  35. 3 0
      Gems/ProteusRobot/Code/Platform/Android/proteusrobot_api_files.cmake
  36. 8 0
      Gems/ProteusRobot/Code/Platform/Android/proteusrobot_private_files.cmake
  37. 8 0
      Gems/ProteusRobot/Code/Platform/Android/proteusrobot_shared_files.cmake
  38. 4 0
      Gems/ProteusRobot/Code/Platform/Linux/PAL_linux.cmake
  39. 3 0
      Gems/ProteusRobot/Code/Platform/Linux/proteusrobot_api_files.cmake
  40. 3 0
      Gems/ProteusRobot/Code/Platform/Linux/proteusrobot_editor_api_files.cmake
  41. 8 0
      Gems/ProteusRobot/Code/Platform/Linux/proteusrobot_private_files.cmake
  42. 8 0
      Gems/ProteusRobot/Code/Platform/Linux/proteusrobot_shared_files.cmake
  43. 4 0
      Gems/ProteusRobot/Code/Platform/Mac/PAL_mac.cmake
  44. 3 0
      Gems/ProteusRobot/Code/Platform/Mac/proteusrobot_api_files.cmake
  45. 3 0
      Gems/ProteusRobot/Code/Platform/Mac/proteusrobot_editor_api_files.cmake
  46. 8 0
      Gems/ProteusRobot/Code/Platform/Mac/proteusrobot_private_files.cmake
  47. 8 0
      Gems/ProteusRobot/Code/Platform/Mac/proteusrobot_shared_files.cmake
  48. 4 0
      Gems/ProteusRobot/Code/Platform/Windows/PAL_windows.cmake
  49. 3 0
      Gems/ProteusRobot/Code/Platform/Windows/proteusrobot_api_files.cmake
  50. 3 0
      Gems/ProteusRobot/Code/Platform/Windows/proteusrobot_editor_api_files.cmake
  51. 8 0
      Gems/ProteusRobot/Code/Platform/Windows/proteusrobot_private_files.cmake
  52. 8 0
      Gems/ProteusRobot/Code/Platform/Windows/proteusrobot_shared_files.cmake
  53. 4 0
      Gems/ProteusRobot/Code/Platform/iOS/PAL_ios.cmake
  54. 3 0
      Gems/ProteusRobot/Code/Platform/iOS/proteusrobot_api_files.cmake
  55. 8 0
      Gems/ProteusRobot/Code/Platform/iOS/proteusrobot_private_files.cmake
  56. 8 0
      Gems/ProteusRobot/Code/Platform/iOS/proteusrobot_shared_files.cmake
  57. 17 0
      Gems/ProteusRobot/Code/Source/Clients/ProteusRobotModule.cpp
  58. 83 0
      Gems/ProteusRobot/Code/Source/Clients/ProteusRobotSystemComponent.cpp
  59. 47 0
      Gems/ProteusRobot/Code/Source/Clients/ProteusRobotSystemComponent.h
  60. 36 0
      Gems/ProteusRobot/Code/Source/ProteusRobotModuleInterface.h
  61. 38 0
      Gems/ProteusRobot/Code/Source/Tools/ProteusRobotEditorModule.cpp
  62. 54 0
      Gems/ProteusRobot/Code/Source/Tools/ProteusRobotEditorSystemComponent.cpp
  63. 33 0
      Gems/ProteusRobot/Code/Source/Tools/ProteusRobotEditorSystemComponent.h
  64. 4 0
      Gems/ProteusRobot/Code/Tests/Clients/ProteusRobotTest.cpp
  65. 4 0
      Gems/ProteusRobot/Code/Tests/Tools/ProteusRobotEditorTest.cpp
  66. 4 0
      Gems/ProteusRobot/Code/proteusrobot_api_files.cmake
  67. 4 0
      Gems/ProteusRobot/Code/proteusrobot_editor_api_files.cmake
  68. 5 0
      Gems/ProteusRobot/Code/proteusrobot_editor_private_files.cmake
  69. 4 0
      Gems/ProteusRobot/Code/proteusrobot_editor_shared_files.cmake
  70. 4 0
      Gems/ProteusRobot/Code/proteusrobot_editor_tests_files.cmake
  71. 6 0
      Gems/ProteusRobot/Code/proteusrobot_private_files.cmake
  72. 4 0
      Gems/ProteusRobot/Code/proteusrobot_shared_files.cmake
  73. 4 0
      Gems/ProteusRobot/Code/proteusrobot_tests_files.cmake
  74. 18 0
      Gems/ProteusRobot/Registry/assetprocessor_settings.setreg
  75. 28 0
      Gems/ProteusRobot/gem.json
  76. 3 0
      Gems/ProteusRobot/preview.png
  77. 15 15
      Gems/ROS2/Assets/Materials/wheel_material.physxmaterial
  78. 9 11
      Gems/ROS2/Assets/Prefabs/Sensors/CameraOrbbeck.prefab
  79. 11 17
      Gems/ROS2/Assets/Prefabs/Sensors/Imu.prefab
  80. 6 8
      Gems/ROS2/Assets/Prefabs/Sensors/LidarOS2.prefab
  81. 2 1
      Gems/ROS2/Code/CMakeLists.txt
  82. 5 3
      Gems/ROS2/Code/Include/ROS2/Frame/ROS2FrameComponent.h
  83. 190 0
      Gems/ROS2/Code/Include/ROS2/Lidar/LidarRaycasterBus.h
  84. 76 0
      Gems/ROS2/Code/Include/ROS2/Lidar/LidarRegistrarBus.h
  85. 43 0
      Gems/ROS2/Code/Include/ROS2/Lidar/LidarSystemBus.h
  86. 2 2
      Gems/ROS2/Code/Include/ROS2/Manipulator/MotorizedJointComponent.h
  87. 2 2
      Gems/ROS2/Code/Include/ROS2/Utilities/Controllers/PidConfiguration.h
  88. 20 4
      Gems/ROS2/Code/Include/ROS2/VehicleDynamics/VehicleInputControlBus.h
  89. 32 58
      Gems/ROS2/Code/Source/Camera/ROS2CameraSensorComponent.cpp
  90. 26 3
      Gems/ROS2/Code/Source/Camera/ROS2CameraSensorComponent.h
  91. 186 0
      Gems/ROS2/Code/Source/Camera/ROS2CameraSensorEditorComponent.cpp
  92. 58 0
      Gems/ROS2/Code/Source/Camera/ROS2CameraSensorEditorComponent.h
  93. 42 6
      Gems/ROS2/Code/Source/Frame/ROS2FrameComponent.cpp
  94. 86 30
      Gems/ROS2/Code/Source/Lidar/LidarRaycaster.cpp
  95. 24 31
      Gems/ROS2/Code/Source/Lidar/LidarRaycaster.h
  96. 53 0
      Gems/ROS2/Code/Source/Lidar/LidarRegistrarEditorSystemComponent.cpp
  97. 34 0
      Gems/ROS2/Code/Source/Lidar/LidarRegistrarEditorSystemComponent.h
  98. 112 0
      Gems/ROS2/Code/Source/Lidar/LidarRegistrarSystemComponent.cpp
  99. 48 0
      Gems/ROS2/Code/Source/Lidar/LidarRegistrarSystemComponent.h
  100. 56 0
      Gems/ROS2/Code/Source/Lidar/LidarSystem.cpp

+ 20 - 0
.github/ISSUE_TEMPLATE/ar_bug_report.md.txt

@@ -0,0 +1,20 @@
+---
+name: Automated Review bug report
+about: Create an issue for a bug found in the Automated Review
+title: 'AR Bug Report'
+labels: 'needs-triage,kind/bug,kind/automation'
+assignees: ''
+
+---
+
+**Describe the bug**
+A clear and concise description of what the bug is.
+
+**Failed Jenkins Job Information:**
+The name of the job that failed, job build number, and code snippit of the failure.
+
+**Attachments**
+Attach the Jenkins job log as a .txt file and any other relevant information.
+
+**Additional context**
+Add any other context about the problem here.

+ 50 - 0
.github/ISSUE_TEMPLATE/bug_template.md.txt

@@ -0,0 +1,50 @@
+---
+name: Bug report
+about: Create a report to help us improve
+title: 'Bug Report'
+labels: 'needs-triage,needs-sig,kind/bug'
+
+---
+
+**Describe the bug**
+A clear and concise description of what the bug is. Try to isolate the issue to help the community to reproduce it easily and increase chances for a fast fix.
+
+**Assets required**
+[!Important!] Please provide sample assets needed to reproduce the issue, either as an [attachment](https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/attaching-files) or as a link to a public asset in Github.
+
+**Steps to reproduce**
+Steps to reproduce the behavior:
+1. Go to '...'
+2. Click on '...'
+3. Select attached asset '...'
+4. Scroll down to '...'
+5. See error
+
+**Expected behavior**
+A clear and concise description of what you expected to happen.
+
+**Actual behavior**
+A clear and concise description of what actually happened.
+
+**Screenshots/Video**
+If applicable, add screenshots and/or a video to help explain your problem.
+
+**Found in Branch**
+Name of or link to the branch where the issue occurs.
+
+**Commit ID from [o3de/o3de](https://github.com/o3de/o3de) and [o3de/o3de-extras](https://github.com/o3de/o3de-extras) repositories**
+Please provide the SHA/hash that identifies the latest commit from the o3de/o3de and o3de/o3de-extras repository if the issue is reproducible in the development or other official branches. 
+You can get the commit ID by running the `git rev-parse HEAD` command on your current branch.
+
+
+**Desktop/Device (please complete the following information):**
+ - Device: [e.g. PC, Mac, iPhone, Samsung] 
+ - OS: [e.g. Windows, macOS, iOS, Android, Linux, including distribution and version (e. g. Ubuntu 22.04 / Windows 11 22H2)]
+
+ - Version [e.g. 10, Monterey, Oreo]
+ - CPU [e.g. Intel I9-9900k , Ryzen 5900x, ]
+ - GPU [AMD 6800 XT, NVidia RTX 3090]
+ - Memory [e.g. 16GB]
+
+**Additional context**
+Add any other context about the problem here.

+ 6 - 0
.github/ISSUE_TEMPLATE/config.yml.txt

@@ -0,0 +1,6 @@
+blank_issues_enabled: false
+contact_links:
+  - name: Documentation Issues
+    about: Report issues with the O3DE.org/docs documentation
+    url: https://github.com/o3de/o3de.org/issues/new/choose 
+

+ 23 - 0
.github/ISSUE_TEMPLATE/deprecation_template.md.txt

@@ -0,0 +1,23 @@
+---
+name: Deprecation Notice
+about: A notice for one or more feature or API deprecations
+title: 'Deprecation Notice'
+labels: 'needs-triage,needs-sig,kind/deprecation'
+
+---
+
+## Deprecated APIs
+List the fully qualified APIs/types. For example, `AZ::Quaternion::SetFromEulerRadians(const Vector3&)`
+
+## Alternatives APIs
+List new or updated APIs to replace deprecated API. 
+For example, use 
+`Quaternion AZ::Quaternion::CreateFromEulerRadiansXYZ(const Vector3&)` 
+to repleace 
+`void AZ::Quaternion::SetFromEulerRadians(const Vector3&)` and `Quaternion AZ::Quaternion::ConvertEulerRadiansToQuaternion(const Vector3&)`
+
+## Last Release
+Describe the last release version before this deprecation. For example: `stabilization/2210`
+
+## Additional Context
+Add some information about the reasons for the deprecations

+ 20 - 0
.github/ISSUE_TEMPLATE/feature_request.md.txt

@@ -0,0 +1,20 @@
+---
+
+name: Feature request
+about: Suggest an idea for this project
+title: 'Feature Request'
+labels: 'needs-triage,needs-sig'
+
+---
+
+**Is your feature request related to a problem? Please describe.**
+A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
+
+**Describe the solution you'd like**
+A clear and concise description of what you want to happen.
+
+**Describe alternatives you've considered**
+A clear and concise description of any alternative solutions or features you've considered.
+
+**Additional context**
+Add any other context or screenshots about the feature request here.

+ 13 - 0
Gems/ProteusRobot/Assets/Materials/Proteus_chassis_Front_Double_Lights.material

@@ -0,0 +1,13 @@
+{
+    "materialType": "@gemroot:Atom_Feature_Common@/Assets/Materials/Types/StandardPBR.materialtype",
+    "materialTypeVersion": 5,
+    "propertyValues": {
+        "baseColor.textureMap": "../Textures/Lights_Emissive.png",
+        "emissive.enable": true,
+        "emissive.intensity": 7.0,
+        "emissive.textureMap": "../Textures/Lights_Emissive.png",
+        "normal.textureMap": "../Textures/Proteus_Normal.png",
+        "opacity.factor": 1.0,
+        "roughness.factor": 0.5
+    }
+}

+ 13 - 0
Gems/ProteusRobot/Assets/Materials/Proteus_chassis_Front_Light.material

@@ -0,0 +1,13 @@
+{
+    "materialType": "@gemroot:Atom_Feature_Common@/Assets/Materials/Types/StandardPBR.materialtype",
+    "materialTypeVersion": 5,
+    "propertyValues": {
+        "baseColor.textureMap": "../Textures/Lights_Emissive.png",
+        "emissive.enable": true,
+        "emissive.intensity": 7.0,
+        "emissive.textureMap": "../Textures/Lights_Emissive.png",
+        "normal.textureMap": "../Textures/Proteus_Normal.png",
+        "opacity.factor": 1.0,
+        "roughness.factor": 0.5
+    }
+}

+ 13 - 0
Gems/ProteusRobot/Assets/Materials/Proteus_chassis_Indicator.material

@@ -0,0 +1,13 @@
+{
+    "materialType": "@gemroot:Atom_Feature_Common@/Assets/Materials/Types/StandardPBR.materialtype",
+    "materialTypeVersion": 5,
+    "propertyValues": {
+        "baseColor.textureMap": "../Textures/Lights_Emissive.png",
+        "emissive.enable": true,
+        "emissive.intensity": 7.0,
+        "emissive.textureMap": "../Textures/Lights_Emissive.png",
+        "normal.textureMap": "../Textures/Proteus_Normal.png",
+        "opacity.factor": 1.0,
+        "roughness.factor": 0.5
+    }
+}

+ 18 - 0
Gems/ProteusRobot/Assets/Materials/Proteus_chassis_Proteus.material

@@ -0,0 +1,18 @@
+{
+    "materialType": "@gemroot:Atom_Feature_Common@/Assets/Materials/Types/StandardPBR.materialtype",
+    "materialTypeVersion": 5,
+    "propertyValues": {
+        "baseColor.textureMap": "../Textures/Proteus_BaseMap.png",
+        "emissive.enable": true,
+        "emissive.intensity": 7.420000076293945,
+        "emissive.textureMap": "../Textures/Lights_Emissive.png",
+        "metallic.textureMap": "../Textures/Proteus_MaskMap_R.png",
+        "normal.textureMap": "../Textures/Proteus_Normal.png",
+        "opacity.factor": 1.0,
+        "roughness.factor": 0.5,
+        "roughness.lowerBound": 0.20000000298023224,
+        "roughness.textureMap": "../Textures/Proteus_MaskMap_A.png",
+        "roughness.upperBound": 0.9399999976158142,
+        "specularF0.factor": 0.5099999904632568
+    }
+}

+ 13 - 0
Gems/ProteusRobot/Assets/Materials/Proteus_chassis_Rear_Lights.material

@@ -0,0 +1,13 @@
+{
+    "materialType": "@gemroot:Atom_Feature_Common@/Assets/Materials/Types/StandardPBR.materialtype",
+    "materialTypeVersion": 5,
+    "propertyValues": {
+        "baseColor.textureMap": "../Textures/Lights_Emissive.png",
+        "emissive.enable": true,
+        "emissive.intensity": 7.0,
+        "emissive.textureMap": "../Textures/Lights_Emissive.png",
+        "normal.textureMap": "../Textures/Proteus_Normal.png",
+        "opacity.factor": 1.0,
+        "roughness.factor": 0.5
+    }
+}

+ 13 - 0
Gems/ProteusRobot/Assets/Materials/Proteus_chassis_Side_Lights.material

@@ -0,0 +1,13 @@
+{
+    "materialType": "@gemroot:Atom_Feature_Common@/Assets/Materials/Types/StandardPBR.materialtype",
+    "materialTypeVersion": 5,
+    "propertyValues": {
+        "baseColor.textureMap": "../Textures/Lights_Emissive.png",
+        "emissive.enable": true,
+        "emissive.intensity": 7.0,
+        "emissive.textureMap": "../Textures/Lights_Emissive.png",
+        "normal.textureMap": "../Textures/Proteus_Normal.png",
+        "opacity.factor": 1.0,
+        "roughness.factor": 0.5
+    }
+}

+ 19 - 0
Gems/ProteusRobot/Assets/Materials/Proteus_chassis_wheel.material

@@ -0,0 +1,19 @@
+{
+    "materialType": "@gemroot:Atom_Feature_Common@/Assets/Materials/Types/StandardPBR.materialtype",
+    "materialTypeVersion": 5,
+    "propertyValues": {
+        "baseColor.textureBlendMode": "LinearLight",
+        "baseColor.textureMap": "../Textures/Proteus_BaseMap.png",
+        "emissive.enable": true,
+        "emissive.intensity": 7.420000076293945,
+        "emissive.textureMap": "../Textures/Lights_Emissive.png",
+        "metallic.textureMap": "../Textures/Proteus_MaskMap_R.png",
+        "normal.textureMap": "../Textures/Proteus_Normal.png",
+        "opacity.factor": 1.0,
+        "roughness.factor": 0.5,
+        "roughness.lowerBound": 0.20000000298023224,
+        "roughness.textureMap": "../Textures/Proteus_MaskMap_A.png",
+        "roughness.upperBound": 0.9399999976158142,
+        "specularF0.factor": 0.5099999904632568
+    }
+}

+ 14 - 0
Gems/ProteusRobot/Assets/Materials/Proteus_headLamp.material

@@ -0,0 +1,14 @@
+{
+    "materialType": "@gemroot:Atom_Feature_Common@/Assets/Materials/Types/StandardPBR.materialtype",
+    "materialTypeVersion": 5,
+    "propertyValues": {
+        "emissive.color": [
+            0.027573052793741226,
+            0.6679789423942566,
+            0.0,
+            1.0
+        ],
+        "emissive.enable": true,
+        "emissive.intensity": 7.860000133514404
+    }
+}

+ 4 - 0
Gems/ProteusRobot/Assets/Materials/untitled.material

@@ -0,0 +1,4 @@
+{
+    "materialType": "@gemroot:Atom_Feature_Common@/Assets/Materials/Types/StandardPBR.materialtype",
+    "materialTypeVersion": 5
+}

Diferenças do arquivo suprimidas por serem muito extensas
+ 257 - 0
Gems/ProteusRobot/Assets/Proteus.prefab


+ 3 - 0
Gems/ProteusRobot/Assets/Proteus2_chassis.fbx

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

+ 3 - 0
Gems/ProteusRobot/Assets/Proteus2_lift.fbx

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

+ 3 - 0
Gems/ProteusRobot/Assets/Proteus_chassis.fbx

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

+ 151 - 0
Gems/ProteusRobot/Assets/Proteus_chassis.fbx.assetinfo

@@ -0,0 +1,151 @@
+{
+    "values": [
+        {
+            "$type": "{5B03C8E6-8CEE-4DA0-A7FA-CD88689DD45B} MeshGroup",
+            "id": "{C881D739-5AD7-5795-915C-E5E2A85CB9E0}",
+            "name": "Proteus_chassis",
+            "NodeSelectionList": {
+                "selectedNodes": [
+                    "RootNode.Proteus"
+                ],
+                "unselectedNodes": [
+                    {},
+                    "RootNode",
+                    "RootNode.WheelBack",
+                    "RootNode.WheelFront"
+                ]
+            },
+            "export method": 1,
+            "PhysicsMaterialSlots": {
+                "Slots": [
+                    {
+                        "Name": "Proteus"
+                    }
+                ]
+            }
+        },
+        {
+            "$type": "{07B356B7-3635-40B5-878A-FAC4EFD5AD86} MeshGroup",
+            "name": "Proteus_chassis",
+            "nodeSelectionList": {
+                "selectedNodes": [
+                    {},
+                    "RootNode",
+                    "RootNode.Proteus",
+                    "RootNode.WheelBack",
+                    "RootNode.WheelFront"
+                ]
+            },
+            "rules": {
+                "rules": [
+                    {
+                        "$type": "StaticMeshAdvancedRule",
+                        "vertexColorStreamName": "Col0"
+                    },
+                    {
+                        "$type": "MaterialRule"
+                    }
+                ]
+            },
+            "id": "{5B66A762-9687-5E87-AD1B-706B69996173}"
+        },
+        {
+            "$type": "{07B356B7-3635-40B5-878A-FAC4EFD5AD86} MeshGroup",
+            "name": "default_Proteus_chassis_DA9DBE87_1547_5F98_825B_BB5776FC2E64_",
+            "nodeSelectionList": {
+                "selectedNodes": [
+                    "RootNode.Proteus"
+                ],
+                "unselectedNodes": [
+                    "RootNode.WheelBack",
+                    "RootNode.WheelFront"
+                ]
+            },
+            "rules": {
+                "rules": [
+                    {
+                        "$type": "ProceduralMeshGroupRule"
+                    },
+                    {
+                        "$type": "UnmodifiableRule"
+                    },
+                    {
+                        "$type": "CoordinateSystemRule",
+                        "useAdvancedData": true
+                    },
+                    {
+                        "$type": "{6E796AC8-1484-4909-860A-6D3F22A7346F} LodRule"
+                    }
+                ]
+            },
+            "id": "{DA9DBE87-1547-5F98-825B-BB5776FC2E64}"
+        },
+        {
+            "$type": "{07B356B7-3635-40B5-878A-FAC4EFD5AD86} MeshGroup",
+            "name": "default_Proteus_chassis_F2316B03_A973_5DF0_9FAC_01F13A4EF1BF_",
+            "nodeSelectionList": {
+                "selectedNodes": [
+                    "RootNode.WheelBack"
+                ],
+                "unselectedNodes": [
+                    "RootNode.Proteus",
+                    "RootNode.WheelFront"
+                ]
+            },
+            "rules": {
+                "rules": [
+                    {
+                        "$type": "ProceduralMeshGroupRule"
+                    },
+                    {
+                        "$type": "UnmodifiableRule"
+                    },
+                    {
+                        "$type": "CoordinateSystemRule",
+                        "useAdvancedData": true
+                    },
+                    {
+                        "$type": "{6E796AC8-1484-4909-860A-6D3F22A7346F} LodRule"
+                    }
+                ]
+            },
+            "id": "{F2316B03-A973-5DF0-9FAC-01F13A4EF1BF}"
+        },
+        {
+            "$type": "{07B356B7-3635-40B5-878A-FAC4EFD5AD86} MeshGroup",
+            "name": "default_Proteus_chassis_07C13738_36F4_5638_A16E_33582EFA5AE2_",
+            "nodeSelectionList": {
+                "selectedNodes": [
+                    "RootNode.WheelFront"
+                ],
+                "unselectedNodes": [
+                    "RootNode.Proteus",
+                    "RootNode.WheelBack"
+                ]
+            },
+            "rules": {
+                "rules": [
+                    {
+                        "$type": "ProceduralMeshGroupRule"
+                    },
+                    {
+                        "$type": "UnmodifiableRule"
+                    },
+                    {
+                        "$type": "CoordinateSystemRule",
+                        "useAdvancedData": true
+                    },
+                    {
+                        "$type": "{6E796AC8-1484-4909-860A-6D3F22A7346F} LodRule"
+                    }
+                ]
+            },
+            "id": "{07C13738-36F4-5638-A16E-33582EFA5AE2}"
+        },
+        {
+            "$type": "PrefabGroup",
+            "name": "Assets/ProteusRobot/Proteus_chassis_fbx.procprefab",
+            "id": "{54641020-4DC4-5AEE-A44A-A6D9BD8C0CBA}"
+        }
+    ]
+}

+ 3 - 0
Gems/ProteusRobot/Assets/Proteus_wheel.fbx

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

+ 88 - 0
Gems/ProteusRobot/Assets/Proteus_wheel.fbx.assetinfo

@@ -0,0 +1,88 @@
+{
+    "values": [
+        {
+            "$type": "{5B03C8E6-8CEE-4DA0-A7FA-CD88689DD45B} MeshGroup",
+            "id": "{80311A34-6917-57C6-819B-8222047E8B5B}",
+            "name": "Proteus_wheel",
+            "NodeSelectionList": {
+                "selectedNodes": [
+                    "RootNode",
+                    "RootNode.WheelSide",
+                    "RootNode.WheelSide.transform",
+                    "RootNode.WheelSide.custom_properties",
+                    "RootNode.WheelSide.UVMap",
+                    "RootNode.WheelSide.Proteus"
+                ]
+            },
+            "export method": 1,
+            "PhysicsMaterialSlots": {
+                "Slots": [
+                    {
+                        "Name": "Proteus",
+                        "MaterialAsset": {
+                            "assetId": {
+                                "guid": "{74448F35-B8C8-5CF3-984C-58CF92889961}",
+                                "subId": 1
+                            },
+                            "assetHint": "assets/proteusrobot/robot_tire.physicsmaterial"
+                        }
+                    }
+                ]
+            }
+        },
+        {
+            "$type": "{07B356B7-3635-40B5-878A-FAC4EFD5AD86} MeshGroup",
+            "name": "Proteus_wheel",
+            "nodeSelectionList": {
+                "selectedNodes": [
+                    "RootNode",
+                    "RootNode.WheelSide",
+                    "RootNode.WheelSide.transform",
+                    "RootNode.WheelSide.custom_properties",
+                    "RootNode.WheelSide.UVMap",
+                    "RootNode.WheelSide.Proteus"
+                ]
+            },
+            "rules": {
+                "rules": [
+                    {
+                        "$type": "MaterialRule"
+                    }
+                ]
+            },
+            "id": "{B2B65532-24AA-559B-8C87-AA2E0EDD85E7}"
+        },
+        {
+            "$type": "{07B356B7-3635-40B5-878A-FAC4EFD5AD86} MeshGroup",
+            "name": "default_Proteus_wheel_2427710A_245B_51D6_BF38_B05455BE2C6F_",
+            "nodeSelectionList": {
+                "selectedNodes": [
+                    "RootNode.WheelSide"
+                ]
+            },
+            "rules": {
+                "rules": [
+                    {
+                        "$type": "ProceduralMeshGroupRule"
+                    },
+                    {
+                        "$type": "UnmodifiableRule"
+                    },
+                    {
+                        "$type": "CoordinateSystemRule",
+                        "useAdvancedData": true
+                    },
+                    {
+                        "$type": "{6E796AC8-1484-4909-860A-6D3F22A7346F} LodRule"
+                    }
+                ]
+            },
+            "id": "{2427710A-245B-51D6-BF38-B05455BE2C6F}"
+        },
+        {
+            "$type": "PrefabGroup",
+            "name": "Assets/ProteusRobot/Proteus_wheel_fbx.procprefab",
+            "id": "{1C55117B-259E-5770-8E5D-22594768E8AE}"
+        }
+    ]
+}

+ 45 - 0
Gems/ProteusRobot/Assets/Scripts/ProteusRobot_inputs.inputbindings

@@ -0,0 +1,45 @@
+<ObjectStream version="3">
+	<Class name="InputEventBindingsAsset" type="{25971C7A-26E2-4D08-A146-2EFCC1C36B0C}">
+		<Class name="InputEventBindings" field="Bindings" version="1" type="{14FFD4A8-AE46-4E23-B45B-6A7C4F787A91}">
+			<Class name="AZStd::vector&lt;InputEventGroup, allocator&gt;" field="Input Event Groups" type="{E7A38039-E414-5322-B384-210475DA7732}">
+				<Class name="InputEventGroup" field="element" version="1" type="{25143B7E-2FEC-4CC5-92FE-270B67E79734}">
+					<Class name="AZStd::string" field="Event Name" value="rotate" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
+					<Class name="AZStd::vector&lt;InputSubComponent*, allocator&gt;" field="Event Generators" type="{7B0B6F41-794A-5CFF-8275-91A3137E747D}">
+						<Class name="InputEventMap" field="element" version="2" type="{A14EA0A3-F053-469D-840E-A70002F51384}">
+							<Class name="AZStd::string" field="Input Device Type" value="keyboard" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
+							<Class name="AZStd::string" field="Input Name" value="keyboard_key_navigation_arrow_left" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
+							<Class name="float" field="Event Value Multiplier" value="1.0000000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
+							<Class name="float" field="Dead Zone" value="0.0000000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
+						</Class>
+						<Class name="InputEventMap" field="element" version="2" type="{A14EA0A3-F053-469D-840E-A70002F51384}">
+							<Class name="AZStd::string" field="Input Device Type" value="keyboard" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
+							<Class name="AZStd::string" field="Input Name" value="keyboard_key_navigation_arrow_right" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
+							<Class name="float" field="Event Value Multiplier" value="-1.0000000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
+							<Class name="float" field="Dead Zone" value="0.0000000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
+						</Class>
+					</Class>
+					<Class name="bool" field="Exclude From Release" value="false" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
+				</Class>
+				<Class name="InputEventGroup" field="element" version="1" type="{25143B7E-2FEC-4CC5-92FE-270B67E79734}">
+					<Class name="AZStd::string" field="Event Name" value="accelerate" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
+					<Class name="AZStd::vector&lt;InputSubComponent*, allocator&gt;" field="Event Generators" type="{7B0B6F41-794A-5CFF-8275-91A3137E747D}">
+						<Class name="InputEventMap" field="element" version="2" type="{A14EA0A3-F053-469D-840E-A70002F51384}">
+							<Class name="AZStd::string" field="Input Device Type" value="keyboard" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
+							<Class name="AZStd::string" field="Input Name" value="keyboard_key_navigation_arrow_up" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
+							<Class name="float" field="Event Value Multiplier" value="1.0000000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
+							<Class name="float" field="Dead Zone" value="0.0000000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
+						</Class>
+						<Class name="InputEventMap" field="element" version="2" type="{A14EA0A3-F053-469D-840E-A70002F51384}">
+							<Class name="AZStd::string" field="Input Device Type" value="keyboard" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
+							<Class name="AZStd::string" field="Input Name" value="keyboard_key_navigation_arrow_down" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
+							<Class name="float" field="Event Value Multiplier" value="-1.0000000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
+							<Class name="float" field="Dead Zone" value="0.0000000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
+						</Class>
+					</Class>
+					<Class name="bool" field="Exclude From Release" value="false" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
+				</Class>
+			</Class>
+		</Class>
+	</Class>
+</ObjectStream>
+

+ 3 - 0
Gems/ProteusRobot/Assets/Textures/Lights_Emissive.png

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

+ 3 - 0
Gems/ProteusRobot/Assets/Textures/Proteus_BaseMap.png

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

+ 3 - 0
Gems/ProteusRobot/Assets/Textures/Proteus_MaskMap.png

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

+ 3 - 0
Gems/ProteusRobot/Assets/Textures/Proteus_MaskMap_A.png

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

+ 3 - 0
Gems/ProteusRobot/Assets/Textures/Proteus_MaskMap_R.png

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

+ 3 - 0
Gems/ProteusRobot/Assets/Textures/Proteus_Normal.png

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

+ 18 - 0
Gems/ProteusRobot/Assets/no_friction.physxmaterial

@@ -0,0 +1,18 @@
+<ObjectStream version="3">
+	<Class name="PhysX::EditorMaterialAsset" version="2" type="{BC7B88B9-EE31-4FBF-A01E-2A93624C49D3}">
+		<Class name="AssetData" field="BaseClass1" version="1" type="{AF3F7D32-1536-422A-89F3-A11E1F5B5A9C}"/>
+		<Class name="PhysX::MaterialConfiguration" field="MaterialConfiguration" version="1" type="{66213D20-9862-465D-AF4F-2D94317161F6}">
+			<Class name="float" field="DynamicFriction" value="0.0000000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
+			<Class name="float" field="StaticFriction" value="0.0000000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
+			<Class name="float" field="Restitution" value="0.0000000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
+			<Class name="unsigned char" field="FrictionCombine" value="1" type="{72B9409A-7D1A-4831-9CFE-FCB3FADD3426}"/>
+			<Class name="unsigned char" field="RestitutionCombine" value="1" type="{72B9409A-7D1A-4831-9CFE-FCB3FADD3426}"/>
+			<Class name="float" field="Density" value="1000.0000000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
+			<Class name="Color" field="DebugColor" value="1.0000000 1.0000000 1.0000000 1.0000000" type="{7894072A-9050-4F0F-901B-34B1A0D29417}"/>
+		</Class>
+		<Class name="PhysicsLegacy::MaterialId" field="LegacyPhysicsMaterialId" version="1" type="{744CCE6C-9F69-4E2F-B950-DAB8514F870B}">
+			<Class name="AZ::Uuid" field="MaterialId" value="{00000000-0000-0000-0000-000000000000}" type="{E152C105-A133-4D03-BBF8-3D4B2FBA3E2A}"/>
+		</Class>
+	</Class>
+</ObjectStream>
+

+ 18 - 0
Gems/ProteusRobot/Assets/robot_tire.physxmaterial

@@ -0,0 +1,18 @@
+<ObjectStream version="3">
+	<Class name="PhysX::EditorMaterialAsset" version="2" type="{BC7B88B9-EE31-4FBF-A01E-2A93624C49D3}">
+		<Class name="AssetData" field="BaseClass1" version="1" type="{AF3F7D32-1536-422A-89F3-A11E1F5B5A9C}"/>
+		<Class name="PhysX::MaterialConfiguration" field="MaterialConfiguration" version="1" type="{66213D20-9862-465D-AF4F-2D94317161F6}">
+			<Class name="float" field="DynamicFriction" value="0.8000000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
+			<Class name="float" field="StaticFriction" value="0.8000000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
+			<Class name="float" field="Restitution" value="0.0000000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
+			<Class name="unsigned char" field="FrictionCombine" value="0" type="{72B9409A-7D1A-4831-9CFE-FCB3FADD3426}"/>
+			<Class name="unsigned char" field="RestitutionCombine" value="1" type="{72B9409A-7D1A-4831-9CFE-FCB3FADD3426}"/>
+			<Class name="float" field="Density" value="1000.0000000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
+			<Class name="Color" field="DebugColor" value="1.0000000 1.0000000 1.0000000 1.0000000" type="{7894072A-9050-4F0F-901B-34B1A0D29417}"/>
+		</Class>
+		<Class name="PhysicsLegacy::MaterialId" field="LegacyPhysicsMaterialId" version="1" type="{744CCE6C-9F69-4E2F-B950-DAB8514F870B}">
+			<Class name="AZ::Uuid" field="MaterialId" value="{00000000-0000-0000-0000-000000000000}" type="{E152C105-A133-4D03-BBF8-3D4B2FBA3E2A}"/>
+		</Class>
+	</Class>
+</ObjectStream>
+

+ 22 - 0
Gems/ProteusRobot/CMakeLists.txt

@@ -0,0 +1,22 @@
+
+# Query the gem name from the gem.json file if possible
+# otherwise fallback to using ProteusRobot
+o3de_find_ancestor_gem_root(gem_path gem_name "${CMAKE_CURRENT_SOURCE_DIR}")
+if (NOT gem_name)
+    set(gem_name "ProteusRobot")
+endif()
+
+# Fallback to using the current source CMakeLists.txt directory as the gem root path
+if (NOT gem_path)
+    set(gem_path ${CMAKE_CURRENT_SOURCE_DIR})
+endif()
+
+set(gem_json ${gem_path}/gem.json)
+
+o3de_restricted_path(${gem_json} gem_restricted_path gem_parent_relative_path)
+
+o3de_pal_dir(pal_dir ${CMAKE_CURRENT_SOURCE_DIR}/Platform/${PAL_PLATFORM_NAME} "${gem_restricted_path}" "${gem_path}" "${gem_parent_relative_path}")
+
+ly_add_external_target_path(${CMAKE_CURRENT_SOURCE_DIR}/3rdParty)
+
+add_subdirectory(Code)

+ 210 - 0
Gems/ProteusRobot/Code/CMakeLists.txt

@@ -0,0 +1,210 @@
+
+# Currently we are in the Code folder: ${CMAKE_CURRENT_LIST_DIR}
+# Get the platform specific folder ${pal_dir} for the current folder: ${CMAKE_CURRENT_LIST_DIR}/Platform/${PAL_PLATFORM_NAME}
+# Note: o3de_pal_dir will take care of the details for us, as this may be a restricted platform
+#       in which case it will see if that platform is present here or in the restricted folder.
+#       i.e. It could here in our gem : Gems/ProteusRobot/Code/Platform/<platorm_name>  or
+#            <restricted_folder>/<platform_name>/Gems/ProteusRobot/Code
+o3de_pal_dir(pal_dir ${CMAKE_CURRENT_LIST_DIR}/Platform/${PAL_PLATFORM_NAME} "${gem_restricted_path}" "${gem_path}" "${gem_parent_relative_path}")
+
+# Now that we have the platform abstraction layer (PAL) folder for this folder, thats where we will find the
+# traits for this platform. Traits for a platform are defines for things like whether or not something in this gem
+# is supported by this platform.
+include(${pal_dir}/PAL_${PAL_PLATFORM_NAME_LOWERCASE}.cmake)
+
+# Check to see if building the Gem Modules are supported for the current platform
+if(NOT PAL_TRAIT_PROTEUSROBOT_SUPPORTED)
+    return()
+endif()
+
+# The ${gem_name}.API target declares the common interface that users of this gem should depend on in their targets
+ly_add_target(
+    NAME ${gem_name}.API INTERFACE
+    NAMESPACE Gem
+    FILES_CMAKE
+        proteusrobot_api_files.cmake
+        ${pal_dir}/proteusrobot_api_files.cmake
+    INCLUDE_DIRECTORIES
+        INTERFACE
+            Include
+    BUILD_DEPENDENCIES
+        INTERFACE
+           AZ::AzCore
+)
+
+# The ${gem_name}.Private.Object target is an internal target
+# It should not be used outside of this Gems CMakeLists.txt
+ly_add_target(
+    NAME ${gem_name}.Private.Object STATIC
+    NAMESPACE Gem
+    FILES_CMAKE
+        proteusrobot_private_files.cmake
+        ${pal_dir}/proteusrobot_private_files.cmake
+    TARGET_PROPERTIES
+        O3DE_PRIVATE_TARGET TRUE
+    INCLUDE_DIRECTORIES
+        PRIVATE
+            Include
+            Source
+    BUILD_DEPENDENCIES
+        PUBLIC
+            AZ::AzCore
+            AZ::AzFramework
+)
+
+# Here add ${gem_name} target, it depends on the Private Object library and Public API interface
+ly_add_target(
+    NAME ${gem_name} ${PAL_TRAIT_MONOLITHIC_DRIVEN_MODULE_TYPE}
+    NAMESPACE Gem
+    FILES_CMAKE
+        proteusrobot_shared_files.cmake
+        ${pal_dir}/proteusrobot_shared_files.cmake
+    INCLUDE_DIRECTORIES
+        PUBLIC
+            Include
+        PRIVATE
+            Source
+    BUILD_DEPENDENCIES
+        PUBLIC
+            Gem::${gem_name}.API
+        PRIVATE
+            Gem::${gem_name}.Private.Object
+)
+
+# By default, we will specify that the above target ${gem_name} would be used by
+# Client and Server type targets when this gem is enabled.  If you don't want it
+# active in Clients or Servers by default, delete one of both of the following lines:
+ly_create_alias(NAME ${gem_name}.Clients NAMESPACE Gem TARGETS Gem::${gem_name})
+ly_create_alias(NAME ${gem_name}.Servers NAMESPACE Gem TARGETS Gem::${gem_name})
+ly_create_alias(NAME ${gem_name}.Unified NAMESPACE Gem TARGETS Gem::${gem_name})
+
+# For the Client and Server variants of ${gem_name} Gem, an alias to the ${gem_name}.API target will be made
+ly_create_alias(NAME ${gem_name}.Clients.API NAMESPACE Gem TARGETS Gem::${gem_name}.API)
+ly_create_alias(NAME ${gem_name}.Servers.API NAMESPACE Gem TARGETS Gem::${gem_name}.API)
+ly_create_alias(NAME ${gem_name}.Unified.API NAMESPACE Gem TARGETS Gem::${gem_name}.API)
+
+# If we are on a host platform, we want to add the host tools targets like the ${gem_name}.Editor MODULE target
+if(PAL_TRAIT_BUILD_HOST_TOOLS)
+    # The ${gem_name}.Editor.API target can be used by other gems that want to interact with the ${gem_name}.Editor module
+    ly_add_target(
+        NAME ${gem_name}.Editor.API INTERFACE
+        NAMESPACE Gem
+        FILES_CMAKE
+            proteusrobot_editor_api_files.cmake
+            ${pal_dir}/proteusrobot_editor_api_files.cmake
+        INCLUDE_DIRECTORIES
+            INTERFACE
+                Include
+        BUILD_DEPENDENCIES
+            INTERFACE
+                AZ::AzToolsFramework
+    )
+
+    # The ${gem_name}.Editor.Private.Object target is an internal target
+    # which is only to be used by this gems CMakeLists.txt and any subdirectories
+    # Other gems should not use this target
+    ly_add_target(
+        NAME ${gem_name}.Editor.Private.Object STATIC
+        NAMESPACE Gem
+        FILES_CMAKE
+            proteusrobot_editor_private_files.cmake
+        TARGET_PROPERTIES
+            O3DE_PRIVATE_TARGET TRUE
+        INCLUDE_DIRECTORIES
+            PRIVATE
+                Include
+                Source
+        BUILD_DEPENDENCIES
+            PUBLIC
+                AZ::AzToolsFramework
+                $<TARGET_OBJECTS:Gem::${gem_name}.Private.Object>
+    )
+
+    ly_add_target(
+        NAME ${gem_name}.Editor GEM_MODULE
+        NAMESPACE Gem
+        AUTOMOC
+        FILES_CMAKE
+            proteusrobot_editor_shared_files.cmake
+        INCLUDE_DIRECTORIES
+            PRIVATE
+                Source
+            PUBLIC
+                Include
+        BUILD_DEPENDENCIES
+            PUBLIC
+                Gem::${gem_name}.Editor.API
+            PRIVATE
+                Gem::${gem_name}.Editor.Private.Object
+    )
+
+    # By default, we will specify that the above target ${gem_name} would be used by
+    # Tool and Builder type targets when this gem is enabled.  If you don't want it
+    # active in Tools or Builders by default, delete one of both of the following lines:
+    ly_create_alias(NAME ${gem_name}.Tools    NAMESPACE Gem TARGETS Gem::${gem_name}.Editor)
+    ly_create_alias(NAME ${gem_name}.Builders NAMESPACE Gem TARGETS Gem::${gem_name}.Editor)
+
+    # For the Tools and Builders variants of ${gem_name} Gem, an alias to the ${gem_name}.Editor API target will be made
+    ly_create_alias(NAME ${gem_name}.Tools.API NAMESPACE Gem TARGETS Gem::${gem_name}.Editor.API)
+    ly_create_alias(NAME ${gem_name}.Builders.API NAMESPACE Gem TARGETS Gem::${gem_name}.Editor.API)
+
+endif()
+
+################################################################################
+# Tests
+################################################################################
+# See if globally, tests are supported
+if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
+    # We globally support tests, see if we support tests on this platform for ${gem_name}.Tests
+    if(PAL_TRAIT_PROTEUSROBOT_TEST_SUPPORTED)
+        # We support ${gem_name}.Tests on this platform, add dependency on the Private Object target
+        ly_add_target(
+            NAME ${gem_name}.Tests ${PAL_TRAIT_TEST_TARGET_TYPE}
+            NAMESPACE Gem
+            FILES_CMAKE
+                proteusrobot_tests_files.cmake
+            INCLUDE_DIRECTORIES
+                PRIVATE
+                    Tests
+                    Source
+            BUILD_DEPENDENCIES
+                PRIVATE
+                    AZ::AzTest
+                    AZ::AzFramework
+                    Gem::${gem_name}.Private.Object
+        )
+
+        # Add ${gem_name}.Tests to googletest
+        ly_add_googletest(
+            NAME Gem::${gem_name}.Tests
+        )
+    endif()
+
+    # If we are a host platform we want to add tools test like editor tests here
+    if(PAL_TRAIT_BUILD_HOST_TOOLS)
+        # We are a host platform, see if Editor tests are supported on this platform
+        if(PAL_TRAIT_PROTEUSROBOT_EDITOR_TEST_SUPPORTED)
+            # We support ${gem_name}.Editor.Tests on this platform, add ${gem_name}.Editor.Tests target which depends on
+            # private ${gem_name}.Editor.Private.Object target
+            ly_add_target(
+                NAME ${gem_name}.Editor.Tests ${PAL_TRAIT_TEST_TARGET_TYPE}
+                NAMESPACE Gem
+                FILES_CMAKE
+                    proteusrobot_editor_tests_files.cmake
+                INCLUDE_DIRECTORIES
+                    PRIVATE
+                        Tests
+                        Source
+                BUILD_DEPENDENCIES
+                    PRIVATE
+                        AZ::AzTest
+                        Gem::${gem_name}.Private.Object
+            )
+
+            # Add ${gem_name}.Editor.Tests to googletest
+            ly_add_googletest(
+                NAME Gem::${gem_name}.Editor.Tests
+            )
+        endif()
+    endif()
+endif()

+ 31 - 0
Gems/ProteusRobot/Code/Include/ProteusRobot/ProteusRobotBus.h

@@ -0,0 +1,31 @@
+
+#pragma once
+
+#include <AzCore/EBus/EBus.h>
+#include <AzCore/Interface/Interface.h>
+
+namespace ProteusRobot
+{
+    class ProteusRobotRequests
+    {
+    public:
+        AZ_RTTI(ProteusRobotRequests, "{2480DA88-89C0-40D0-9C27-CA19C99FECC7}");
+        virtual ~ProteusRobotRequests() = default;
+        // Put your public methods here
+    };
+    
+    class ProteusRobotBusTraits
+        : public AZ::EBusTraits
+    {
+    public:
+        //////////////////////////////////////////////////////////////////////////
+        // EBusTraits overrides
+        static constexpr AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
+        static constexpr AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
+        //////////////////////////////////////////////////////////////////////////
+    };
+
+    using ProteusRobotRequestBus = AZ::EBus<ProteusRobotRequests, ProteusRobotBusTraits>;
+    using ProteusRobotInterface = AZ::Interface<ProteusRobotRequests>;
+
+} // namespace ProteusRobot

+ 4 - 0
Gems/ProteusRobot/Code/Platform/Android/PAL_android.cmake

@@ -0,0 +1,4 @@
+
+set(PAL_TRAIT_PROTEUSROBOT_SUPPORTED TRUE)
+set(PAL_TRAIT_PROTEUSROBOT_TEST_SUPPORTED FALSE)
+set(PAL_TRAIT_PROTEUSROBOT_EDITOR_TEST_SUPPORTED FALSE)

+ 3 - 0
Gems/ProteusRobot/Code/Platform/Android/proteusrobot_api_files.cmake

@@ -0,0 +1,3 @@
+
+set(FILES
+)

+ 8 - 0
Gems/ProteusRobot/Code/Platform/Android/proteusrobot_private_files.cmake

@@ -0,0 +1,8 @@
+
+# Platform specific files for Android
+# i.e. ../Source/Android/ProteusRobotAndroid.cpp
+#      ../Source/Android/ProteusRobotAndroid.h
+#      ../Include/Android/ProteusRobotAndroid.h
+
+set(FILES
+)

+ 8 - 0
Gems/ProteusRobot/Code/Platform/Android/proteusrobot_shared_files.cmake

@@ -0,0 +1,8 @@
+
+# Platform specific files for Android
+# i.e. ../Source/Android/ProteusRobotAndroid.cpp
+#      ../Source/Android/ProteusRobotAndroid.h
+#      ../Include/Android/ProteusRobotAndroid.h
+
+set(FILES
+)

+ 4 - 0
Gems/ProteusRobot/Code/Platform/Linux/PAL_linux.cmake

@@ -0,0 +1,4 @@
+
+set(PAL_TRAIT_PROTEUSROBOT_SUPPORTED TRUE)
+set(PAL_TRAIT_PROTEUSROBOT_TEST_SUPPORTED FALSE)
+set(PAL_TRAIT_PROTEUSROBOT_EDITOR_TEST_SUPPORTED FALSE)

+ 3 - 0
Gems/ProteusRobot/Code/Platform/Linux/proteusrobot_api_files.cmake

@@ -0,0 +1,3 @@
+
+set(FILES
+)

+ 3 - 0
Gems/ProteusRobot/Code/Platform/Linux/proteusrobot_editor_api_files.cmake

@@ -0,0 +1,3 @@
+
+set(FILES
+)

+ 8 - 0
Gems/ProteusRobot/Code/Platform/Linux/proteusrobot_private_files.cmake

@@ -0,0 +1,8 @@
+
+# Platform specific files for Linux
+# i.e. ../Source/Linux/ProteusRobotLinux.cpp
+#      ../Source/Linux/ProteusRobotLinux.h
+#      ../Include/Linux/ProteusRobotLinux.h
+
+set(FILES
+)

+ 8 - 0
Gems/ProteusRobot/Code/Platform/Linux/proteusrobot_shared_files.cmake

@@ -0,0 +1,8 @@
+
+# Platform specific files for Linux
+# i.e. ../Source/Linux/ProteusRobotLinux.cpp
+#      ../Source/Linux/ProteusRobotLinux.h
+#      ../Include/Linux/ProteusRobotLinux.h
+
+set(FILES
+)

+ 4 - 0
Gems/ProteusRobot/Code/Platform/Mac/PAL_mac.cmake

@@ -0,0 +1,4 @@
+
+set(PAL_TRAIT_PROTEUSROBOT_SUPPORTED TRUE)
+set(PAL_TRAIT_PROTEUSROBOT_TEST_SUPPORTED FALSE)
+set(PAL_TRAIT_PROTEUSROBOT_EDITOR_TEST_SUPPORTED FALSE)

+ 3 - 0
Gems/ProteusRobot/Code/Platform/Mac/proteusrobot_api_files.cmake

@@ -0,0 +1,3 @@
+
+set(FILES
+)

+ 3 - 0
Gems/ProteusRobot/Code/Platform/Mac/proteusrobot_editor_api_files.cmake

@@ -0,0 +1,3 @@
+
+set(FILES
+)

+ 8 - 0
Gems/ProteusRobot/Code/Platform/Mac/proteusrobot_private_files.cmake

@@ -0,0 +1,8 @@
+
+# Platform specific files for Mac
+# i.e. ../Source/Mac/ProteusRobotMac.cpp
+#      ../Source/Mac/ProteusRobotMac.h
+#      ../Include/Mac/ProteusRobotMac.h
+
+set(FILES
+)

+ 8 - 0
Gems/ProteusRobot/Code/Platform/Mac/proteusrobot_shared_files.cmake

@@ -0,0 +1,8 @@
+
+# Platform specific files for Mac
+# i.e. ../Source/Mac/ProteusRobotMac.cpp
+#      ../Source/Mac/ProteusRobotMac.h
+#      ../Include/Mac/ProteusRobotMac.h
+
+set(FILES
+)

+ 4 - 0
Gems/ProteusRobot/Code/Platform/Windows/PAL_windows.cmake

@@ -0,0 +1,4 @@
+
+set(PAL_TRAIT_PROTEUSROBOT_SUPPORTED TRUE)
+set(PAL_TRAIT_PROTEUSROBOT_TEST_SUPPORTED FALSE)
+set(PAL_TRAIT_PROTEUSROBOT_EDITOR_TEST_SUPPORTED FALSE)

+ 3 - 0
Gems/ProteusRobot/Code/Platform/Windows/proteusrobot_api_files.cmake

@@ -0,0 +1,3 @@
+
+set(FILES
+)

+ 3 - 0
Gems/ProteusRobot/Code/Platform/Windows/proteusrobot_editor_api_files.cmake

@@ -0,0 +1,3 @@
+
+set(FILES
+)

+ 8 - 0
Gems/ProteusRobot/Code/Platform/Windows/proteusrobot_private_files.cmake

@@ -0,0 +1,8 @@
+
+# Platform specific files for Windows
+# i.e. ../Source/Windows/ProteusRobotWindows.cpp
+#      ../Source/Windows/ProteusRobotWindows.h
+#      ../Include/Windows/ProteusRobotWindows.h
+
+set(FILES
+)

+ 8 - 0
Gems/ProteusRobot/Code/Platform/Windows/proteusrobot_shared_files.cmake

@@ -0,0 +1,8 @@
+
+# Platform specific files for Windows
+# i.e. ../Source/Windows/ProteusRobotWindows.cpp
+#      ../Source/Windows/ProteusRobotWindows.h
+#      ../Include/Windows/ProteusRobotWindows.h
+
+set(FILES
+)

+ 4 - 0
Gems/ProteusRobot/Code/Platform/iOS/PAL_ios.cmake

@@ -0,0 +1,4 @@
+
+set(PAL_TRAIT_PROTEUSROBOT_SUPPORTED TRUE)
+set(PAL_TRAIT_PROTEUSROBOT_TEST_SUPPORTED FALSE)
+set(PAL_TRAIT_PROTEUSROBOT_EDITOR_TEST_SUPPORTED FALSE)

+ 3 - 0
Gems/ProteusRobot/Code/Platform/iOS/proteusrobot_api_files.cmake

@@ -0,0 +1,3 @@
+
+set(FILES
+)

+ 8 - 0
Gems/ProteusRobot/Code/Platform/iOS/proteusrobot_private_files.cmake

@@ -0,0 +1,8 @@
+
+# Platform specific files for iOS
+# i.e. ../Source/iOS/ProteusRobotiOS.cpp
+#      ../Source/iOS/ProteusRobotiOS.h
+#      ../Include/iOS/ProteusRobotiOS.h
+
+set(FILES
+)

+ 8 - 0
Gems/ProteusRobot/Code/Platform/iOS/proteusrobot_shared_files.cmake

@@ -0,0 +1,8 @@
+
+# Platform specific files for iOS
+# i.e. ../Source/iOS/ProteusRobotiOS.cpp
+#      ../Source/iOS/ProteusRobotiOS.h
+#      ../Include/iOS/ProteusRobotiOS.h
+
+set(FILES
+)

+ 17 - 0
Gems/ProteusRobot/Code/Source/Clients/ProteusRobotModule.cpp

@@ -0,0 +1,17 @@
+
+
+#include <ProteusRobotModuleInterface.h>
+#include "ProteusRobotSystemComponent.h"
+
+namespace ProteusRobot
+{
+    class ProteusRobotModule
+        : public ProteusRobotModuleInterface
+    {
+    public:
+        AZ_RTTI(ProteusRobotModule, "{F9558D3E-566B-4824-8634-015F21864F5E}", ProteusRobotModuleInterface);
+        AZ_CLASS_ALLOCATOR(ProteusRobotModule, AZ::SystemAllocator, 0);
+    };
+}// namespace ProteusRobot
+
+AZ_DECLARE_MODULE_CLASS(Gem_ProteusRobot, ProteusRobot::ProteusRobotModule)

+ 83 - 0
Gems/ProteusRobot/Code/Source/Clients/ProteusRobotSystemComponent.cpp

@@ -0,0 +1,83 @@
+
+#include "ProteusRobotSystemComponent.h"
+
+#include <AzCore/Serialization/SerializeContext.h>
+#include <AzCore/Serialization/EditContext.h>
+#include <AzCore/Serialization/EditContextConstants.inl>
+
+namespace ProteusRobot
+{
+    void ProteusRobotSystemComponent::Reflect(AZ::ReflectContext* context)
+    {
+        if (AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context))
+        {
+            serialize->Class<ProteusRobotSystemComponent, AZ::Component>()
+                ->Version(0)
+                ;
+
+            if (AZ::EditContext* ec = serialize->GetEditContext())
+            {
+                ec->Class<ProteusRobotSystemComponent>("ProteusRobot", "[Description of functionality provided by this System Component]")
+                    ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
+                        ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("System"))
+                        ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
+                    ;
+            }
+        }
+    }
+
+    void ProteusRobotSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
+    {
+        provided.push_back(AZ_CRC_CE("ProteusRobotService"));
+    }
+
+    void ProteusRobotSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
+    {
+        incompatible.push_back(AZ_CRC_CE("ProteusRobotService"));
+    }
+
+    void ProteusRobotSystemComponent::GetRequiredServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& required)
+    {
+    }
+
+    void ProteusRobotSystemComponent::GetDependentServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& dependent)
+    {
+    }
+
+    ProteusRobotSystemComponent::ProteusRobotSystemComponent()
+    {
+        if (ProteusRobotInterface::Get() == nullptr)
+        {
+            ProteusRobotInterface::Register(this);
+        }
+    }
+
+    ProteusRobotSystemComponent::~ProteusRobotSystemComponent()
+    {
+        if (ProteusRobotInterface::Get() == this)
+        {
+            ProteusRobotInterface::Unregister(this);
+        }
+    }
+
+    void ProteusRobotSystemComponent::Init()
+    {
+    }
+
+    void ProteusRobotSystemComponent::Activate()
+    {
+        ProteusRobotRequestBus::Handler::BusConnect();
+        AZ::TickBus::Handler::BusConnect();
+    }
+
+    void ProteusRobotSystemComponent::Deactivate()
+    {
+        AZ::TickBus::Handler::BusDisconnect();
+        ProteusRobotRequestBus::Handler::BusDisconnect();
+    }
+
+    void ProteusRobotSystemComponent::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
+    {
+    }
+
+} // namespace ProteusRobot

+ 47 - 0
Gems/ProteusRobot/Code/Source/Clients/ProteusRobotSystemComponent.h

@@ -0,0 +1,47 @@
+
+#pragma once
+
+#include <AzCore/Component/Component.h>
+#include <AzCore/Component/TickBus.h>
+#include <ProteusRobot/ProteusRobotBus.h>
+
+namespace ProteusRobot
+{
+    class ProteusRobotSystemComponent
+        : public AZ::Component
+        , protected ProteusRobotRequestBus::Handler
+        , public AZ::TickBus::Handler
+    {
+    public:
+        AZ_COMPONENT(ProteusRobotSystemComponent, "{250B4B63-AF8F-4DC0-BF73-9880E8BEA444}");
+
+        static void Reflect(AZ::ReflectContext* context);
+
+        static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided);
+        static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible);
+        static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required);
+        static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent);
+
+        ProteusRobotSystemComponent();
+        ~ProteusRobotSystemComponent();
+
+    protected:
+        ////////////////////////////////////////////////////////////////////////
+        // ProteusRobotRequestBus interface implementation
+
+        ////////////////////////////////////////////////////////////////////////
+
+        ////////////////////////////////////////////////////////////////////////
+        // AZ::Component interface implementation
+        void Init() override;
+        void Activate() override;
+        void Deactivate() override;
+        ////////////////////////////////////////////////////////////////////////
+
+        ////////////////////////////////////////////////////////////////////////
+        // AZTickBus interface implementation
+        void OnTick(float deltaTime, AZ::ScriptTimePoint time) override;
+        ////////////////////////////////////////////////////////////////////////
+    };
+
+} // namespace ProteusRobot

+ 36 - 0
Gems/ProteusRobot/Code/Source/ProteusRobotModuleInterface.h

@@ -0,0 +1,36 @@
+
+#include <AzCore/Memory/SystemAllocator.h>
+#include <AzCore/Module/Module.h>
+#include <Clients/ProteusRobotSystemComponent.h>
+
+namespace ProteusRobot
+{
+    class ProteusRobotModuleInterface
+        : public AZ::Module
+    {
+    public:
+        AZ_RTTI(ProteusRobotModuleInterface, "{C2853683-8867-42E4-BAF8-6BAE29CB53E1}", AZ::Module);
+        AZ_CLASS_ALLOCATOR(ProteusRobotModuleInterface, AZ::SystemAllocator, 0);
+
+        ProteusRobotModuleInterface()
+        {
+            // Push results of [MyComponent]::CreateDescriptor() into m_descriptors here.
+            // Add ALL components descriptors associated with this gem to m_descriptors.
+            // This will associate the AzTypeInfo information for the components with the the SerializeContext, BehaviorContext and EditContext.
+            // This happens through the [MyComponent]::Reflect() function.
+            m_descriptors.insert(m_descriptors.end(), {
+                ProteusRobotSystemComponent::CreateDescriptor(),
+                });
+        }
+
+        /**
+         * Add required SystemComponents to the SystemEntity.
+         */
+        AZ::ComponentTypeList GetRequiredSystemComponents() const override
+        {
+            return AZ::ComponentTypeList{
+                azrtti_typeid<ProteusRobotSystemComponent>(),
+            };
+        }
+    };
+}// namespace ProteusRobot

+ 38 - 0
Gems/ProteusRobot/Code/Source/Tools/ProteusRobotEditorModule.cpp

@@ -0,0 +1,38 @@
+
+#include <ProteusRobotModuleInterface.h>
+#include "ProteusRobotEditorSystemComponent.h"
+
+namespace ProteusRobot
+{
+    class ProteusRobotEditorModule
+        : public ProteusRobotModuleInterface
+    {
+    public:
+        AZ_RTTI(ProteusRobotEditorModule, "{F9558D3E-566B-4824-8634-015F21864F5E}", ProteusRobotModuleInterface);
+        AZ_CLASS_ALLOCATOR(ProteusRobotEditorModule, AZ::SystemAllocator, 0);
+
+        ProteusRobotEditorModule()
+        {
+            // Push results of [MyComponent]::CreateDescriptor() into m_descriptors here.
+            // Add ALL components descriptors associated with this gem to m_descriptors.
+            // This will associate the AzTypeInfo information for the components with the the SerializeContext, BehaviorContext and EditContext.
+            // This happens through the [MyComponent]::Reflect() function.
+            m_descriptors.insert(m_descriptors.end(), {
+                ProteusRobotEditorSystemComponent::CreateDescriptor(),
+            });
+        }
+
+        /**
+         * Add required SystemComponents to the SystemEntity.
+         * Non-SystemComponents should not be added here
+         */
+        AZ::ComponentTypeList GetRequiredSystemComponents() const override
+        {
+            return AZ::ComponentTypeList {
+                azrtti_typeid<ProteusRobotEditorSystemComponent>(),
+            };
+        }
+    };
+}// namespace ProteusRobot
+
+AZ_DECLARE_MODULE_CLASS(Gem_ProteusRobot, ProteusRobot::ProteusRobotEditorModule)

+ 54 - 0
Gems/ProteusRobot/Code/Source/Tools/ProteusRobotEditorSystemComponent.cpp

@@ -0,0 +1,54 @@
+
+#include <AzCore/Serialization/SerializeContext.h>
+#include "ProteusRobotEditorSystemComponent.h"
+
+namespace ProteusRobot
+{
+    void ProteusRobotEditorSystemComponent::Reflect(AZ::ReflectContext* context)
+    {
+        if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
+        {
+            serializeContext->Class<ProteusRobotEditorSystemComponent, ProteusRobotSystemComponent>()
+                ->Version(0);
+        }
+    }
+
+    ProteusRobotEditorSystemComponent::ProteusRobotEditorSystemComponent() = default;
+
+    ProteusRobotEditorSystemComponent::~ProteusRobotEditorSystemComponent() = default;
+
+    void ProteusRobotEditorSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
+    {
+        BaseSystemComponent::GetProvidedServices(provided);
+        provided.push_back(AZ_CRC_CE("ProteusRobotEditorService"));
+    }
+
+    void ProteusRobotEditorSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
+    {
+        BaseSystemComponent::GetIncompatibleServices(incompatible);
+        incompatible.push_back(AZ_CRC_CE("ProteusRobotEditorService"));
+    }
+
+    void ProteusRobotEditorSystemComponent::GetRequiredServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& required)
+    {
+        BaseSystemComponent::GetRequiredServices(required);
+    }
+
+    void ProteusRobotEditorSystemComponent::GetDependentServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& dependent)
+    {
+        BaseSystemComponent::GetDependentServices(dependent);
+    }
+
+    void ProteusRobotEditorSystemComponent::Activate()
+    {
+        ProteusRobotSystemComponent::Activate();
+        AzToolsFramework::EditorEvents::Bus::Handler::BusConnect();
+    }
+
+    void ProteusRobotEditorSystemComponent::Deactivate()
+    {
+        AzToolsFramework::EditorEvents::Bus::Handler::BusDisconnect();
+        ProteusRobotSystemComponent::Deactivate();
+    }
+
+} // namespace ProteusRobot

+ 33 - 0
Gems/ProteusRobot/Code/Source/Tools/ProteusRobotEditorSystemComponent.h

@@ -0,0 +1,33 @@
+
+#pragma once
+
+#include <AzToolsFramework/API/ToolsApplicationAPI.h>
+
+#include <Clients/ProteusRobotSystemComponent.h>
+
+namespace ProteusRobot
+{
+    /// System component for ProteusRobot editor
+    class ProteusRobotEditorSystemComponent
+        : public ProteusRobotSystemComponent
+        , protected AzToolsFramework::EditorEvents::Bus::Handler
+    {
+        using BaseSystemComponent = ProteusRobotSystemComponent;
+    public:
+        AZ_COMPONENT(ProteusRobotEditorSystemComponent, "{1AD25C4B-B8F7-4C54-BF46-3F5A9E02E90B}", BaseSystemComponent);
+        static void Reflect(AZ::ReflectContext* context);
+
+        ProteusRobotEditorSystemComponent();
+        ~ProteusRobotEditorSystemComponent();
+
+    private:
+        static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided);
+        static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible);
+        static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required);
+        static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent);
+
+        // AZ::Component
+        void Activate() override;
+        void Deactivate() override;
+    };
+} // namespace ProteusRobot

+ 4 - 0
Gems/ProteusRobot/Code/Tests/Clients/ProteusRobotTest.cpp

@@ -0,0 +1,4 @@
+
+#include <AzTest/AzTest.h>
+
+AZ_UNIT_TEST_HOOK(DEFAULT_UNIT_TEST_ENV);

+ 4 - 0
Gems/ProteusRobot/Code/Tests/Tools/ProteusRobotEditorTest.cpp

@@ -0,0 +1,4 @@
+
+#include <AzTest/AzTest.h>
+
+AZ_UNIT_TEST_HOOK(DEFAULT_UNIT_TEST_ENV);

+ 4 - 0
Gems/ProteusRobot/Code/proteusrobot_api_files.cmake

@@ -0,0 +1,4 @@
+
+set(FILES
+    Include/ProteusRobot/ProteusRobotBus.h
+)

+ 4 - 0
Gems/ProteusRobot/Code/proteusrobot_editor_api_files.cmake

@@ -0,0 +1,4 @@
+
+
+set(FILES
+)

+ 5 - 0
Gems/ProteusRobot/Code/proteusrobot_editor_private_files.cmake

@@ -0,0 +1,5 @@
+
+set(FILES
+    Source/Tools/ProteusRobotEditorSystemComponent.cpp
+    Source/Tools/ProteusRobotEditorSystemComponent.h
+)

+ 4 - 0
Gems/ProteusRobot/Code/proteusrobot_editor_shared_files.cmake

@@ -0,0 +1,4 @@
+
+set(FILES
+    Source/Tools/ProteusRobotEditorModule.cpp
+)

+ 4 - 0
Gems/ProteusRobot/Code/proteusrobot_editor_tests_files.cmake

@@ -0,0 +1,4 @@
+
+set(FILES
+    Tests/Tools/ProteusRobotEditorTest.cpp
+)

+ 6 - 0
Gems/ProteusRobot/Code/proteusrobot_private_files.cmake

@@ -0,0 +1,6 @@
+
+set(FILES
+    Source/ProteusRobotModuleInterface.h
+    Source/Clients/ProteusRobotSystemComponent.cpp
+    Source/Clients/ProteusRobotSystemComponent.h
+)

+ 4 - 0
Gems/ProteusRobot/Code/proteusrobot_shared_files.cmake

@@ -0,0 +1,4 @@
+
+set(FILES
+    Source/Clients/ProteusRobotModule.cpp
+)

+ 4 - 0
Gems/ProteusRobot/Code/proteusrobot_tests_files.cmake

@@ -0,0 +1,4 @@
+
+set(FILES
+    Tests/Clients/ProteusRobotTest.cpp
+)

+ 18 - 0
Gems/ProteusRobot/Registry/assetprocessor_settings.setreg

@@ -0,0 +1,18 @@
+{
+    "Amazon": {
+        "AssetProcessor": {
+            "Settings": {
+                "ScanFolder ProteusRobot/Assets": {
+                    "watch": "@GEMROOT:ProteusRobot@/Assets",
+                    "recursive": 1,
+                    "order": 101
+                },
+                "ScanFolder ProteusRobot/Registry": {
+                    "watch": "@GEMROOT:ProteusRobot@/Registry",
+                    "recursive": 1,
+                    "order": 102
+                }
+            }
+        }
+    }
+}

+ 28 - 0
Gems/ProteusRobot/gem.json

@@ -0,0 +1,28 @@
+{
+    "gem_name": "ProteusRobot",
+    "version": "1.0.0",
+    "display_name": "ProteusRobot",
+    "license": "License used i.e. Apache-2.0 or MIT",
+    "license_url": "Link to the license web site i.e. https://opensource.org/licenses/Apache-2.0",
+    "origin": "The name of the originator or creator",
+    "origin_url": "The website for this Gem",
+    "type": "Code",
+    "summary": "A short description of this Gem",
+    "canonical_tags": [
+        "Gem"
+    ],
+    "user_tags": [
+        "ProteusRobot"
+    ],
+    "platforms": [
+        ""
+    ],
+    "icon_path": "preview.png",
+    "requirements": "Notice of any requirements for this Gem i.e. This requires X other gem",
+    "documentation_url": "Link to any documentation of your Gem",
+    "dependencies": [],
+    "repo_uri": "",
+    "compatible_engines": [],
+    "engine_api_dependencies": [],
+    "restricted": "ProteusRobot"
+}

+ 3 - 0
Gems/ProteusRobot/preview.png

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

+ 15 - 15
Gems/ROS2/Assets/Materials/wheel_material.physxmaterial

@@ -1,17 +1,17 @@
 <ObjectStream version="3">
-    <Class name="PhysX::EditorMaterialAsset" version="2" type="{BC7B88B9-EE31-4FBF-A01E-2A93624C49D3}">
-        <Class name="AssetData" field="BaseClass1" version="1" type="{AF3F7D32-1536-422A-89F3-A11E1F5B5A9C}"/>
-        <Class name="PhysX::MaterialConfiguration" field="MaterialConfiguration" version="1" type="{66213D20-9862-465D-AF4F-2D94317161F6}">
-            <Class name="float" field="DynamicFriction" value="1.0000000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
-            <Class name="float" field="StaticFriction" value="1.0000000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
-            <Class name="float" field="Restitution" value="0.0000000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
-            <Class name="unsigned char" field="FrictionCombine" value="2" type="{72B9409A-7D1A-4831-9CFE-FCB3FADD3426}"/>
-            <Class name="unsigned char" field="RestitutionCombine" value="1" type="{72B9409A-7D1A-4831-9CFE-FCB3FADD3426}"/>
-            <Class name="float" field="Density" value="1200.0000000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
-            <Class name="Color" field="DebugColor" value="0.1088426 0.1088426 0.1088426 1.0000000" type="{7894072A-9050-4F0F-901B-34B1A0D29417}"/>
-        </Class>
-        <Class name="PhysicsLegacy::MaterialId" field="LegacyPhysicsMaterialId" version="1" type="{744CCE6C-9F69-4E2F-B950-DAB8514F870B}">
-            <Class name="AZ::Uuid" field="MaterialId" value="{8C7A6011-61C2-46B7-9BF4-8D4DD2A624F1}" type="{E152C105-A133-4D03-BBF8-3D4B2FBA3E2A}"/>
-        </Class>
-    </Class>
+	<Class name="PhysX::EditorMaterialAsset" version="2" type="{BC7B88B9-EE31-4FBF-A01E-2A93624C49D3}">
+		<Class name="AssetData" field="BaseClass1" version="1" type="{AF3F7D32-1536-422A-89F3-A11E1F5B5A9C}"/>
+		<Class name="PhysX::MaterialConfiguration" field="MaterialConfiguration" version="1" type="{66213D20-9862-465D-AF4F-2D94317161F6}">
+			<Class name="float" field="DynamicFriction" value="0.7000000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
+			<Class name="float" field="StaticFriction" value="0.7000000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
+			<Class name="float" field="Restitution" value="0.1000000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
+			<Class name="unsigned char" field="FrictionCombine" value="1" type="{72B9409A-7D1A-4831-9CFE-FCB3FADD3426}"/>
+			<Class name="unsigned char" field="RestitutionCombine" value="1" type="{72B9409A-7D1A-4831-9CFE-FCB3FADD3426}"/>
+			<Class name="float" field="Density" value="120.0000000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
+			<Class name="Color" field="DebugColor" value="0.1088426 0.1088426 0.1088426 1.0000000" type="{7894072A-9050-4F0F-901B-34B1A0D29417}"/>
+		</Class>
+		<Class name="PhysicsLegacy::MaterialId" field="LegacyPhysicsMaterialId" version="1" type="{744CCE6C-9F69-4E2F-B950-DAB8514F870B}">
+			<Class name="AZ::Uuid" field="MaterialId" value="{8C7A6011-61C2-46B7-9BF4-8D4DD2A624F1}" type="{E152C105-A133-4D03-BBF8-3D4B2FBA3E2A}"/>
+		</Class>
+	</Class>
 </ObjectStream>

+ 9 - 11
Gems/ROS2/Assets/Prefabs/Sensors/CameraOrbbeck.prefab

@@ -7,10 +7,6 @@
                 "$type": "EditorDisabledCompositionComponent",
                 "Id": 11102275644839424768
             },
-            "Component_[11542908075789116644]": {
-                "$type": "SelectionComponent",
-                "Id": 11542908075789116644
-            },
             "Component_[13196476423961983989]": {
                 "$type": "EditorVisibilityComponent",
                 "Id": 13196476423961983989
@@ -58,9 +54,9 @@
             "Id": "Entity_[505721939488]",
             "Name": "shape",
             "Components": {
-                "Component_[10894288830090806011]": {
-                    "$type": "SelectionComponent",
-                    "Id": 10894288830090806011
+                "Component_[12057597801334645923]": {
+                    "$type": "EditorStaticRigidBodyComponent",
+                    "Id": 12057597801334645923
                 },
                 "Component_[12232650884412772636]": {
                     "$type": "EditorDisabledCompositionComponent",
@@ -75,12 +71,14 @@
                     "Id": 14616296837285217185
                 },
                 "Component_[14985514809153430690]": {
-                    "$type": "EditorColliderComponent",
+                    "$type": "EditorMeshColliderComponent",
                     "Id": 14985514809153430690,
                     "ColliderConfiguration": {
-                        "MaterialSelection": {
-                            "MaterialIds": [
-                                {}
+                        "MaterialSlots": {
+                            "Slots": [
+                                {
+                                    "Name": "Entire object"
+                                }
                             ]
                         }
                     },

+ 11 - 17
Gems/ROS2/Assets/Prefabs/Sensors/Imu.prefab

@@ -7,10 +7,6 @@
                 "$type": "EditorLockComponent",
                 "Id": 10933327020638125059
             },
-            "Component_[13139737976503828664]": {
-                "$type": "SelectionComponent",
-                "Id": 13139737976503828664
-            },
             "Component_[15418568890677341368]": {
                 "$type": "EditorOnlyEntityComponent",
                 "Id": 15418568890677341368
@@ -66,17 +62,15 @@
                     "$type": "EditorOnlyEntityComponent",
                     "Id": 11958193181460909696
                 },
-                "Component_[12008599340723129520]": {
-                    "$type": "SelectionComponent",
-                    "Id": 12008599340723129520
-                },
                 "Component_[12053638524449760314]": {
-                    "$type": "EditorColliderComponent",
+                    "$type": "EditorMeshColliderComponent",
                     "Id": 12053638524449760314,
                     "ColliderConfiguration": {
-                        "MaterialSelection": {
-                            "MaterialIds": [
-                                {}
+                        "MaterialSlots": {
+                            "Slots": [
+                                {
+                                    "Name": "Entire object"
+                                }
                             ]
                         }
                     },
@@ -169,6 +163,10 @@
                     "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
                     "Id": 16208993720168044799,
                     "Parent Entity": "ContainerEntity"
+                },
+                "Component_[6556961023163958494]": {
+                    "$type": "EditorStaticRigidBodyComponent",
+                    "Id": 6556961023163958494
                 }
             }
         },
@@ -184,10 +182,6 @@
                     "$type": "EditorInspectorComponent",
                     "Id": 12018107220451562856
                 },
-                "Component_[13764353402899190262]": {
-                    "$type": "SelectionComponent",
-                    "Id": 13764353402899190262
-                },
                 "Component_[14000889612025468259]": {
                     "$type": "EditorLockComponent",
                     "Id": 14000889612025468259
@@ -246,4 +240,4 @@
             }
         }
     }
-}
+}

+ 6 - 8
Gems/ROS2/Assets/Prefabs/Sensors/LidarOS2.prefab

@@ -81,8 +81,7 @@
                                     "Topic": "pc"
                                 }
                             }
-                        },
-                        "LidarTransparentEntityId": ""
+                        }
                     }
                 },
                 "Component_[13013301136498148727]": {
@@ -155,7 +154,7 @@
                     "Id": 13177781661023720894
                 },
                 "Component_[14535670656735835043]": {
-                    "$type": "EditorColliderComponent",
+                    "$type": "EditorMeshColliderComponent",
                     "Id": 14535670656735835043,
                     "ColliderConfiguration": {
                         "CollisionGroupId": {
@@ -167,11 +166,6 @@
                                     "Name": "DefaultMaterial"
                                 }
                             ]
-                        },
-                        "MaterialSelection": {
-                            "MaterialIds": [
-                                {}
-                            ]
                         }
                     },
                     "ShapeConfiguration": {
@@ -200,6 +194,10 @@
                     "$type": "EditorDisabledCompositionComponent",
                     "Id": 17075071070477116796
                 },
+                "Component_[226256536131696604]": {
+                    "$type": "EditorStaticRigidBodyComponent",
+                    "Id": 226256536131696604
+                },
                 "Component_[3201525403568523459]": {
                     "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
                     "Id": 3201525403568523459,

+ 2 - 1
Gems/ROS2/Code/CMakeLists.txt

@@ -32,6 +32,7 @@ ly_add_target(
             Include
         PRIVATE
             Source
+            Source/VehicleDynamics
     BUILD_DEPENDENCIES
         PUBLIC
             AZ::AzCore
@@ -40,6 +41,7 @@ ly_add_target(
             Gem::Atom_Feature_Common.Static
             Gem::Atom_Component_DebugCamera.Static
             Gem::StartingPointInput
+            Gem::PhysX.Static
 )
 
 target_depends_on_ros2_packages(${gem_name}.Static rclcpp builtin_interfaces std_msgs sensor_msgs nav_msgs urdfdom tf2_ros ackermann_msgs gazebo_msgs control_toolbox)
@@ -111,7 +113,6 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS)
         NAME ${gem_name}.Editor GEM_MODULE
         NAMESPACE Gem
         FILES_CMAKE
-            ros2_editor_files.cmake
             ros2_editor_shared_files.cmake
         PLATFORM_INCLUDE_FILES
             ${CMAKE_CURRENT_LIST_DIR}/Platform/Common/${PAL_TRAIT_COMPILER_ID}/ros2_static_editor_${PAL_TRAIT_COMPILER_ID_LOWERCASE}.cmake

+ 5 - 3
Gems/ROS2/Code/Include/ROS2/Frame/ROS2FrameComponent.h

@@ -54,10 +54,11 @@ namespace ROS2
         //! @return A complete namespace (including parent namespaces)
         AZStd::string GetNamespace() const;
 
-        //! Get AZ Transform for this frame.
-        //! @return If parent ROS2Frame is found, return its Transform.
+        //! Get a transform between this frame and the next frame up in hierarchy.
+        //! @return If the parent frame is found, return a Transform between this frame and the parent.
         //! Otherwise, return a global Transform.
-        const AZ::Transform& GetFrameTransform() const;
+        //! @note Parent frame is not the same as parent Transform: there could be many Transforms in between without ROS2Frame components.
+        AZ::Transform GetFrameTransform() const;
 
         //! Global frame name in ros2 ecosystem.
         //! @return The name of the global frame with namespace attached. It is typically "odom", "map", "world".
@@ -86,6 +87,7 @@ namespace ROS2
         AZStd::string m_frameName = "sensor_frame";
 
         bool m_publishTransform = true;
+        bool m_isDynamic = false;
         AZStd::unique_ptr<ROS2Transform> m_ros2Transform;
     };
 } // namespace ROS2

+ 190 - 0
Gems/ROS2/Code/Include/ROS2/Lidar/LidarRaycasterBus.h

@@ -0,0 +1,190 @@
+/*
+ * 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 <AzCore/EBus/EBus.h>
+#include <AzCore/Math/Transform.h>
+#include <AzCore/Math/Vector3.h>
+
+namespace ROS2
+{
+    //! Class used for creating typesafe Uuid types.
+    //! It utilizes the phantom types technique.
+    template<typename Tag>
+    class StronglyTypedUuid
+    {
+    public:
+        StronglyTypedUuid() = default;
+        explicit constexpr StronglyTypedUuid(AZ::Uuid value)
+            : m_uuid(value)
+        {
+        }
+
+        constexpr static StronglyTypedUuid CreateNull()
+        {
+            return StronglyTypedUuid(AZ::Uuid::CreateNull());
+        }
+
+        constexpr static StronglyTypedUuid CreateRandom()
+        {
+            return StronglyTypedUuid(AZ::Uuid::CreateRandom());
+        }
+
+        constexpr bool IsNull() const
+        {
+            return m_uuid.IsNull();
+        }
+
+        constexpr bool operator==(const StronglyTypedUuid& rhs) const
+        {
+            return m_uuid == rhs.m_uuid;
+        }
+
+        constexpr bool operator!=(const StronglyTypedUuid& rhs) const
+        {
+            return m_uuid != rhs.m_uuid;
+        }
+
+        constexpr bool operator<(const StronglyTypedUuid& rhs) const
+        {
+            return m_uuid < rhs.m_uuid;
+        }
+
+        constexpr bool operator>(const StronglyTypedUuid& rhs) const
+        {
+            return m_uuid > rhs.m_uuid;
+        }
+
+        constexpr bool operator<=(const StronglyTypedUuid& rhs) const
+        {
+            return m_uuid <= rhs.m_uuid;
+        }
+
+        constexpr bool operator>=(const StronglyTypedUuid& rhs) const
+        {
+            return m_uuid >= rhs.m_uuid;
+        }
+
+        constexpr size_t GetHash() const
+        {
+            return m_uuid.GetHash();
+        }
+
+    private:
+        AZ::Uuid m_uuid;
+    };
+
+    //! Unique id used by lidar raycasters.
+    using LidarId = StronglyTypedUuid<struct LidarIdTag>;
+
+    //! Interface class that allows for communication with a single Lidar instance.
+    class LidarRaycasterRequests
+    {
+    public:
+        AZ_RTTI(LidarRaycasterRequests, "{253a02c8-b6cb-493c-b16f-012ccf9db226}");
+
+        //! Configures ray orientations.
+        //! @param orientations Vector of orientations as Euler angles in radians. Each ray direction is computed by transforming a unit
+        //! vector in the positive z direction first by the y, next by the z axis. The x axis is currently not included in calculations.
+        virtual void ConfigureRayOrientations(const AZStd::vector<AZ::Vector3>& orientations) = 0;
+
+        //! Configures ray maximum travel distance.
+        //! @param range Ray range in meters.
+        virtual void ConfigureRayRange(float range) = 0;
+
+        //! Schedules a raycast that originates from the point described by the lidarTransform.
+        //! @param lidarTransform Current transform from global to lidar reference frame.
+        //! @return Results of the raycast in form of coordinates in 3D space.
+        //! The returned vector size can be anything between zero and size of directions. No hits further than distance will be reported.
+        virtual AZStd::vector<AZ::Vector3> PerformRaycast(const AZ::Transform& lidarTransform) = 0;
+
+        //! Configures ray Gaussian Noise parameters.
+        //! Each call overrides the previous configuration.
+        //! This type of noise is especially useful when trying to simulate real-life lidars, since its noise mimics
+        //! the imperfections arising due to various physical factors e.g. fluctuations in rotary motion of the lidar (angular noise) or
+        //! distance accuracy (distance noise).
+        //! For the the details about Gaussian noise, please refer to https://en.wikipedia.org/wiki/Gaussian_noise.
+        //! You can also check-out the RobotecGPULidar (integrated in the RobotecGPULidar Gem) docs concerning these types of lidar noise at
+        //! https://github.com/RobotecAI/RobotecGPULidar/blob/v11/docs/GaussianNoise.md.
+        //! @param angularNoiseStdDev Angular noise standard deviation.
+        //! @param distanceNoiseStdDevBase Base value for Distance noise standard deviation.
+        //! @param distanceNoiseStdDevRisePerMeter Value by which the distance noise standard deviation increases per meter length from
+        //! the lidar.
+        virtual void ConfigureNoiseParameters(
+            [[maybe_unused]] float angularNoiseStdDev,
+            [[maybe_unused]] float distanceNoiseStdDevBase,
+            [[maybe_unused]] float distanceNoiseStdDevRisePerMeter)
+        {
+            AZ_Assert(false, "This Lidar Implementation does not support noise!");
+        }
+
+        //! Configures Layer ignoring parameters
+        //! @param ignoreLayer Should a specified collision layer be ignored?
+        //! @param layerIndex Index of collision layer to be ignored.
+        virtual void ConfigureLayerIgnoring([[maybe_unused]] bool ignoreLayer, [[maybe_unused]] AZ::u32 layerIndex)
+        {
+            AZ_Assert(false, "This Lidar Implementation does not support collision layer configurations!");
+        }
+
+        //! Excludes entities with given EntityIds from raycasting.
+        //! @param excludedEntities List of entities marked for exclusion.
+        virtual void ExcludeEntities([[maybe_unused]] const AZStd::vector<AZ::EntityId>& excludedEntities)
+        {
+            AZ_Assert(false, "This Lidar Implementation does not support entity exclusion!");
+        }
+
+        //! Configures max range point addition.
+        //! @param includeMaxRange Should the raycaster add points at max range for rays that exceeded their range?
+        virtual void ConfigureMaxRangePointAddition([[maybe_unused]] bool addMaxRangePoints)
+        {
+            AZ_Assert(false, "This Lidar Implementation does not support Max range point addition configuration!");
+        }
+
+    protected:
+        ~LidarRaycasterRequests() = default;
+
+        static void ValidateRayRange([[maybe_unused]] float range)
+        {
+            AZ_Assert(range > 0.0f, "Provided ray range was of incorrect value: Ray range value must be greater than zero.")
+        }
+
+        static void ValidateRayOrientations([[maybe_unused]] const AZStd::vector<AZ::Vector3>& orientations)
+        {
+            AZ_Assert(!orientations.empty(), "Provided ray orientations were of incorrect value: Ray orientations must not be empty.")
+        }
+    };
+
+    class LidarRaycasterBusTraits : public AZ::EBusTraits
+    {
+    public:
+        //////////////////////////////////////////////////////////////////////////
+        // EBusTraits overrides
+        using BusIdType = LidarId;
+        static constexpr AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
+        static constexpr AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
+        //////////////////////////////////////////////////////////////////////////
+    };
+
+    using LidarRaycasterRequestBus = AZ::EBus<LidarRaycasterRequests, LidarRaycasterBusTraits>;
+} // namespace ROS2
+
+// Since we want to use the LidarId type as a Bus Id type,
+// we need to create a specialization for the hash template operator.
+namespace AZStd
+{
+    // hash specialization
+    template <>
+    struct hash<ROS2::LidarId>
+    {
+        constexpr size_t operator()(const ROS2::LidarId& id) const
+        {
+            return id.GetHash();
+        }
+    };
+}

+ 76 - 0
Gems/ROS2/Code/Include/ROS2/Lidar/LidarRegistrarBus.h

@@ -0,0 +1,76 @@
+/*
+ * 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/EBus/EBus.h>
+#include <AzCore/Interface/Interface.h>
+#include <AzCore/std/string/string.h>
+
+namespace ROS2
+{
+    //! Enum bitwise flags used to describe LidarSystem's feature support.
+    enum LidarSystemFeatures : uint16_t
+    {
+        None                = 0b0000000000000000,
+        Noise               = 0b0000000000000001,
+        CollisionLayers     = 0b0000000000000010,
+        EntityExclusion     = 0b0000000000000100,
+        MaxRangePoints      = 0b0000000000001000,
+        All                 = 0b1111111111111111
+    };
+
+    //! Structure used to hold LidarSystem's metadata.
+    struct LidarSystemMetaData
+    {
+        AZStd::string m_name;
+        AZStd::string m_description;
+        LidarSystemFeatures m_features;
+    };
+
+    //! Interface class that allows for communication with the LidarRegistrarSystemComponent.
+    class LidarRegistrarRequests
+    {
+    public:
+        AZ_RTTI(LidarRegistrarRequests, "{22030dc7-a1db-43bd-b748-0fb9ec43ce2e}");
+
+        //! Registers a new lidar system under the provided name.
+        //! To obtain the busId of a lidarSystem use the AZ_CRC macro as follows.
+        //! @code
+        //! AZ::Crc32 busId = AZ_CRC(<lidarSystemName>);
+        //! @endcode
+        //! @param name Name of the newly registered lidar system.
+        //! @param description Further information about the lidar system.
+        virtual void RegisterLidarSystem(const char* name, const char* description, const LidarSystemFeatures& features) = 0;
+
+        //! Returns A list of all registered lidar systems.
+        //! @return A vector of registered lidar systems' names.
+        virtual AZStd::vector<AZStd::string> GetRegisteredLidarSystems() const = 0;
+
+        //! Returns metadata of a registered lidar system.
+        //! If no lidar system with provided name was found returns nullptr.
+        //! @param name Name of a registered lidar system.
+        //! @return Pointer to the metadata of a lidar system with the provided name.
+        virtual const LidarSystemMetaData* GetLidarSystemMetaData(const AZStd::string& name) const = 0;
+
+    protected:
+        ~LidarRegistrarRequests() = default;
+    };
+
+    class LidarRegistrarBusTraits : public AZ::EBusTraits
+    {
+    public:
+        //////////////////////////////////////////////////////////////////////////
+        // EBusTraits overrides
+        static constexpr AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
+        static constexpr AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
+        //////////////////////////////////////////////////////////////////////////
+    };
+
+    using LidarRegistrarRequestBus = AZ::EBus<LidarRegistrarRequests, LidarRegistrarBusTraits>;
+    using LidarRegistrarInterface = AZ::Interface<LidarRegistrarRequests>;
+} // namespace ROS2

+ 43 - 0
Gems/ROS2/Code/Include/ROS2/Lidar/LidarSystemBus.h

@@ -0,0 +1,43 @@
+/*
+ * 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 <AzCore/EBus/EBus.h>
+#include <ROS2/Lidar/LidarRaycasterBus.h>
+
+namespace ROS2
+{
+    //! Interface class that allows for communication with a given Lidar System (implementation).
+    class LidarSystemRequests
+    {
+    public:
+        AZ_RTTI(LidarSystemRequests, "{007871d1-2783-4382-977b-558f436c54a5}");
+
+        //! Creates a new Lidar.
+        //! @param lidarEntityId EntityId holding the ROS2LidarSensorComponent.
+        //! @return A unique Id of the newly created Lidar.
+        virtual LidarId CreateLidar(AZ::EntityId lidarEntityId) = 0;
+
+    protected:
+        ~LidarSystemRequests() = default;
+    };
+
+    class LidarSystemBusTraits : public AZ::EBusTraits
+    {
+    public:
+        //////////////////////////////////////////////////////////////////////////
+        // EBusTraits overrides
+        using BusIdType = AZ::Crc32;
+        static constexpr AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
+        static constexpr AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
+        //////////////////////////////////////////////////////////////////////////
+    };
+
+    using LidarSystemRequestBus = AZ::EBus<LidarSystemRequests, LidarSystemBusTraits>;
+} // namespace ROS2

+ 2 - 2
Gems/ROS2/Code/Include/ROS2/Manipulator/MotorizedJointComponent.h

@@ -12,7 +12,7 @@
 #include <AzCore/Component/TickBus.h>
 #include <AzCore/Math/Transform.h>
 #include <AzCore/Math/Vector2.h>
-#include <ROS2/VehicleDynamics/DriveModels/PidConfiguration.h>
+#include <ROS2/Utilities/Controllers/PidConfiguration.h>
 
 namespace ROS2
 {
@@ -67,7 +67,7 @@ namespace ROS2
         AZ::Vector3 m_jointDir{ 0.f, 0.f, 1.f }; //!< Direction of joint movement in parent frame of reference, used to compute measurement.
         AZ::Vector3 m_effortAxis{ 0.f, 0.f, 1.f }; //!< Direction of force or torque application in owning entity frame of reference.
         AZStd::pair<float, float> m_limits{ -0.5f, 0.5f }; //!< limits of joint, the force is applied only when joint is within limits.
-        VehicleDynamics::PidConfiguration m_pidPos; //!< PID controller for position.
+        Controllers::PidConfiguration m_pidPos; //!< PID controller for position.
 
         bool m_linear{ true }; //!< Linear mode. The force is applied through RigidBodyBus.
         bool m_animationMode{ true }; //!< Use TransformBus (animation mode, no physics) instead of RigidBodyBus.

+ 2 - 2
Gems/ROS2/Code/Include/ROS2/VehicleDynamics/DriveModels/PidConfiguration.h → Gems/ROS2/Code/Include/ROS2/Utilities/Controllers/PidConfiguration.h

@@ -10,7 +10,7 @@
 #include <AzCore/Serialization/SerializeContext.h>
 #include <control_toolbox/pid.hpp>
 
-namespace ROS2::VehicleDynamics
+namespace ROS2::Controllers
 {
     //! A wrapper for ROS 2 control_toolbox pid controller.
     //! @see <a href="https://github.com/ros-controls/control_toolbox">control_toolbox</a>.
@@ -40,4 +40,4 @@ namespace ROS2::VehicleDynamics
 
         control_toolbox::Pid m_pid; //!< PID implementation object from control_toolbox (of ros2_control).
     };
-} // namespace ROS2::VehicleDynamics
+} // namespace ROS2::Controllers

+ 20 - 4
Gems/ROS2/Code/Include/ROS2/VehicleDynamics/VehicleInputControlBus.h

@@ -25,14 +25,26 @@ namespace ROS2::VehicleDynamics
         virtual ~VehicleInputControlRequests() = default;
 
         //! Set target for the vehicle linear speed. It should be realized over time according to drive model.
-        //! @param speedMps is a linear speed in meters per second with the plus sign in the forward direction.
-        virtual void SetTargetLinearSpeed(float speedMps) = 0;
+        //! @param speedMps is a linear speed vectors, unit is meters per second.
+        virtual void SetTargetLinearSpeedV3(const AZ::Vector3& speedMps) = 0;
+
+        //! Set target for the vehicle linear speed. It should be realized over time according to drive model.
+        //! @param speedMpsX is a linear speed in meters per second with the plus sign in the forward direction.
+        virtual void SetTargetLinearSpeed(float speedMpsX) = 0;
 
         //! Steer in a direction given in relative coordinate system (current direction is 0).
         //! @param steering is angle in radians, positive to the right and negative to the left.
         //! @note The actual angle applied is subject to limits and implementation (eg smoothing).
         virtual void SetTargetSteering(float steering) = 0;
 
+        //! Set target for the angular speed. It should be realized over time according to drive model.
+        //! @param rate is an angular speed vector, unit is radians per second.
+        virtual void SetTargetAngularSpeedV3(const AZ::Vector3& rate) = 0;
+
+        //! Set target for the angular speed. It should be realized over time according to drive model.
+        //! @param rateZ is an angular speed in radians per second in up direction .
+        virtual void SetTargetAngularSpeed(float rateZ) = 0;
+
         //! Accelerate without target speed, relative to the limits.
         //! @param accelerationFraction is relative to limits of possible acceleration.
         //! 1 - accelerate as much as possible, -1 - brake as much as possible.
@@ -44,8 +56,12 @@ namespace ROS2::VehicleDynamics
         virtual void SetTargetSteeringFraction(float steeringFraction) = 0;
 
         //! Speed input version which is relative to limits.
-        //! @param speedFraction is -1 to 1, which applies as a fraction of vehicle model speed limits.
-        virtual void SetTargetLinearSpeedFraction(float speedFraction) = 0;
+        //! @param speedFractionX is -1 to 1, which applies as a fraction of vehicle model speed limits.
+        virtual void SetTargetLinearSpeedFraction(float speedFractionX) = 0;
+
+        //! Set target for the angular speed. It should be realized over time according to drive model.
+        //! @param rateFractionZ is an angular speed in radians per second in up direction, fraction of maximum speed.
+        virtual void SetTargetAngularSpeedFraction(float rateFractionZ) = 0;
 
         //! Disables (or enables) the vehicle dynamics
         //! @param disable if set true no torque will be applied

+ 32 - 58
Gems/ROS2/Code/Source/Camera/ROS2CameraSensorComponent.cpp

@@ -21,42 +21,20 @@
 
 namespace ROS2
 {
-    namespace Internal
+    ROS2CameraSensorComponent::ROS2CameraSensorComponent(
+        const SensorConfiguration& sensorConfiguration,
+        float verticalFieldOfViewDeg,
+        int width,
+        int height,
+        bool colorCamera,
+        bool depthCamera)
+        : m_verticalFieldOfViewDeg(verticalFieldOfViewDeg)
+        , m_width(width)
+        , m_height(height)
+        , m_colorCamera(colorCamera)
+        , m_depthCamera(depthCamera)
     {
-        const char* kImageMessageType = "sensor_msgs::msg::Image";
-        const char* kDepthImageConfig = "Depth Image";
-        const char* kColorImageConfig = "Color Image";
-        const char* kInfoConfig = "Camera Info";
-        const char* kCameraInfoMessageType = "sensor_msgs::msg::CameraInfo";
-
-        AZStd::pair<AZStd::string, TopicConfiguration> MakeTopicConfigurationPair(
-            const AZStd::string& topic, const AZStd::string& messageType, const AZStd::string& configName)
-        {
-            TopicConfiguration config;
-            config.m_topic = topic;
-            config.m_type = messageType;
-            return AZStd::make_pair(configName, config);
-        }
-
-        AZStd::string GetCameraNameFromFrame(const AZ::Entity* entity)
-        {
-            const auto* component = Utils::GetGameOrEditorComponent<ROS2FrameComponent>(entity);
-            AZStd::string cameraName = component->GetFrameID();
-            AZStd::replace(cameraName.begin(), cameraName.end(), '/', '_');
-            return cameraName;
-        }
-
-    } // namespace Internal
-
-    ROS2CameraSensorComponent::ROS2CameraSensorComponent()
-    {
-        m_sensorConfiguration.m_frequency = 10;
-        m_sensorConfiguration.m_publishersConfigurations.insert(
-            Internal::MakeTopicConfigurationPair("camera_image_color", Internal::kImageMessageType, Internal::kColorImageConfig));
-        m_sensorConfiguration.m_publishersConfigurations.insert(
-            Internal::MakeTopicConfigurationPair("camera_image_depth", Internal::kImageMessageType, Internal::kDepthImageConfig));
-        m_sensorConfiguration.m_publishersConfigurations.insert(
-            Internal::MakeTopicConfigurationPair("camera_info", Internal::kCameraInfoMessageType, Internal::kInfoConfig));
+        m_sensorConfiguration = sensorConfiguration;
     }
 
     void ROS2CameraSensorComponent::Reflect(AZ::ReflectContext* context)
@@ -66,29 +44,11 @@ namespace ROS2
         {
             serialize->Class<ROS2CameraSensorComponent, ROS2SensorComponent>()
                 ->Version(3)
-                ->Field("VerticalFieldOfViewDeg", &ROS2CameraSensorComponent::m_VerticalFieldOfViewDeg)
+                ->Field("VerticalFieldOfViewDeg", &ROS2CameraSensorComponent::m_verticalFieldOfViewDeg)
                 ->Field("Width", &ROS2CameraSensorComponent::m_width)
                 ->Field("Height", &ROS2CameraSensorComponent::m_height)
                 ->Field("Depth", &ROS2CameraSensorComponent::m_depthCamera)
                 ->Field("Color", &ROS2CameraSensorComponent::m_colorCamera);
-
-            AZ::EditContext* ec = serialize->GetEditContext();
-            if (ec)
-            {
-                ec->Class<ROS2CameraSensorComponent>("ROS2 Camera Sensor", "[Camera component]")
-                    ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
-                    ->Attribute(AZ::Edit::Attributes::Category, "ROS2")
-                    ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("Game"))
-                    ->DataElement(
-                        AZ::Edit::UIHandlers::Default,
-                        &ROS2CameraSensorComponent::m_VerticalFieldOfViewDeg,
-                        "Vertical field of view",
-                        "Camera's vertical (y axis) field of view in degrees.")
-                    ->DataElement(AZ::Edit::UIHandlers::Default, &ROS2CameraSensorComponent::m_width, "Image width", "Image width")
-                    ->DataElement(AZ::Edit::UIHandlers::Default, &ROS2CameraSensorComponent::m_height, "Image height", "Image height")
-                    ->DataElement(AZ::Edit::UIHandlers::Default, &ROS2CameraSensorComponent::m_colorCamera, "Color Camera", "Color Camera")
-                    ->DataElement(AZ::Edit::UIHandlers::Default, &ROS2CameraSensorComponent::m_depthCamera, "Depth Camera", "Depth Camera");
-            }
         }
     }
 
@@ -98,7 +58,7 @@ namespace ROS2
 
         auto ros2Node = ROS2Interface::Get()->GetNode();
 
-        const auto cameraInfoPublisherConfig = m_sensorConfiguration.m_publishersConfigurations[Internal::kInfoConfig];
+        const auto cameraInfoPublisherConfig = m_sensorConfiguration.m_publishersConfigurations[CameraConstants::InfoConfig];
         AZStd::string cameraInfoFullTopic = ROS2Names::GetNamespacedName(GetNamespace(), cameraInfoPublisherConfig.m_topic);
         AZ_TracePrintf("ROS2", "Creating publisher for camera info on topic %s\n", cameraInfoFullTopic.data());
 
@@ -106,11 +66,11 @@ namespace ROS2
             ros2Node->create_publisher<sensor_msgs::msg::CameraInfo>(cameraInfoFullTopic.data(), cameraInfoPublisherConfig.GetQoS());
 
         const CameraSensorDescription description{
-            Internal::GetCameraNameFromFrame(GetEntity()), m_VerticalFieldOfViewDeg, m_width, m_height
+            GetCameraNameFromFrame(GetEntity()), m_verticalFieldOfViewDeg, m_width, m_height
         };
         if (m_colorCamera)
         {
-            const auto cameraImagePublisherConfig = m_sensorConfiguration.m_publishersConfigurations[Internal::kColorImageConfig];
+            const auto cameraImagePublisherConfig = m_sensorConfiguration.m_publishersConfigurations[CameraConstants::ColorImageConfig];
             AZStd::string cameraImageFullTopic = ROS2Names::GetNamespacedName(GetNamespace(), cameraImagePublisherConfig.m_topic);
             auto publisher =
                 ros2Node->create_publisher<sensor_msgs::msg::Image>(cameraImageFullTopic.data(), cameraImagePublisherConfig.GetQoS());
@@ -118,7 +78,7 @@ namespace ROS2
         }
         if (m_depthCamera)
         {
-            const auto cameraImagePublisherConfig = m_sensorConfiguration.m_publishersConfigurations[Internal::kDepthImageConfig];
+            const auto cameraImagePublisherConfig = m_sensorConfiguration.m_publishersConfigurations[CameraConstants::DepthImageConfig];
             AZStd::string cameraImageFullTopic = ROS2Names::GetNamespacedName(GetNamespace(), cameraImagePublisherConfig.m_topic);
             auto publisher =
                 ros2Node->create_publisher<sensor_msgs::msg::Image>(cameraImageFullTopic.data(), cameraImagePublisherConfig.GetQoS());
@@ -163,4 +123,18 @@ namespace ROS2
             sensor->RequestMessagePublication(publisher, transform, ros_header);
         }
     }
+
+    AZStd::string ROS2CameraSensorComponent::GetCameraNameFromFrame(const AZ::Entity* entity) const
+    {
+        const auto* component = Utils::GetGameOrEditorComponent<ROS2FrameComponent>(entity);
+        AZ_Assert(component, "Entity %s has no ROS2CameraSensorComponent", entity->GetName().c_str());
+        if (component)
+        {
+            AZStd::string cameraName = component->GetFrameID();
+            AZStd::replace(cameraName.begin(), cameraName.end(), '/', '_');
+            return cameraName;
+        }
+        return AZStd::string{};
+
+    }
 } // namespace ROS2

+ 26 - 3
Gems/ROS2/Code/Source/Camera/ROS2CameraSensorComponent.h

@@ -22,6 +22,15 @@
 
 namespace ROS2
 {
+    namespace CameraConstants
+    {
+        inline constexpr char ImageMessageType[] = "sensor_msgs::msg::Image";
+        inline constexpr char DepthImageConfig[] = "Depth Image";
+        inline constexpr char ColorImageConfig[] = "Color Image";
+        inline constexpr char InfoConfig[] = "Camera Info";
+        inline constexpr char CameraInfoMessageType[] = "sensor_msgs::msg::CameraInfo";
+    } // namespace CameraConstants
+
     //! ROS2 Camera sensor component class
     //! Allows turning an entity into a camera sensor
     //! Can be parametrized with following values:
@@ -32,7 +41,16 @@ namespace ROS2
     class ROS2CameraSensorComponent : public ROS2SensorComponent
     {
     public:
-        ROS2CameraSensorComponent();
+        ROS2CameraSensorComponent() = default;
+
+        ROS2CameraSensorComponent(
+            const SensorConfiguration& sensorConfiguration,
+            float verticalFieldOfViewDeg,
+            int width,
+            int height,
+            bool colorCamera,
+            bool depthCamera);
+
         ~ROS2CameraSensorComponent() override = default;
         AZ_COMPONENT(ROS2CameraSensorComponent, "{3C6B8AE6-9721-4639-B8F9-D8D28FD7A071}", ROS2SensorComponent);
         static void Reflect(AZ::ReflectContext* context);
@@ -50,7 +68,7 @@ namespace ROS2
         //! Type that combines pointer to ROS2 publisher and CameraSensor
         using PublisherSensorPtrPair = AZStd::pair<ImagePublisherPtrType, AZStd::shared_ptr<CameraSensor>>;
 
-        //! Helper to construct a PublisherSensorPtrPair with a pointer to ROS2 publisher and intrinsic calibration
+        //! Helper to construct a PublisherSensorPtrPair with a pointer to ROS2 publisher and intrinsic calibration.
         //! @tparam CameraType type of camera sensor (eg 'CameraColorSensor')
         //! @param publisher pointer to ROS2 image publisher
         //! @param description CameraSensorDescription with intrinsic calibration
@@ -61,7 +79,12 @@ namespace ROS2
             return { publisher, AZStd::make_shared<CameraType>(description) };
         }
 
-        float m_VerticalFieldOfViewDeg = 90.0f;
+        //! Retrieve camera name from ROS2FrameComponent's FrameID.
+        //! @param entity pointer entity that has ROS2FrameComponent
+        //! @returns FrameID from ROS2FrameComponent
+        AZStd::string GetCameraNameFromFrame(const AZ::Entity* entity) const;
+
+        float m_verticalFieldOfViewDeg = 90.0f;
         int m_width = 640;
         int m_height = 480;
         bool m_colorCamera = true;

+ 186 - 0
Gems/ROS2/Code/Source/Camera/ROS2CameraSensorEditorComponent.cpp

@@ -0,0 +1,186 @@
+/*
+ * 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 "ROS2CameraSensorEditorComponent.h"
+#include "ROS2CameraSensorComponent.h"
+#include <AzCore/Component/TransformBus.h>
+#include <ROS2/Frame/ROS2FrameComponent.h>
+
+namespace ROS2
+{
+    ROS2CameraSensorEditorComponent::ROS2CameraSensorEditorComponent()
+    {
+        m_sensorConfiguration.m_frequency = 10;
+        m_sensorConfiguration.m_publishersConfigurations.insert(
+            MakeTopicConfigurationPair("camera_image_color", CameraConstants::ImageMessageType, CameraConstants::ColorImageConfig));
+        m_sensorConfiguration.m_publishersConfigurations.insert(
+            MakeTopicConfigurationPair("camera_image_depth", CameraConstants::ImageMessageType, CameraConstants::DepthImageConfig));
+        m_sensorConfiguration.m_publishersConfigurations.insert(
+            MakeTopicConfigurationPair("camera_info", CameraConstants::CameraInfoMessageType, CameraConstants::InfoConfig));
+    }
+
+    void ROS2CameraSensorEditorComponent::Reflect(AZ::ReflectContext* context)
+    {
+        if (auto* serialize = azrtti_cast<AZ::SerializeContext*>(context))
+        {
+            serialize->Class<ROS2CameraSensorEditorComponent, AzToolsFramework::Components::EditorComponentBase>()
+                ->Version(4)
+                ->Field("VerticalFieldOfViewDeg", &ROS2CameraSensorEditorComponent::m_VerticalFieldOfViewDeg)
+                ->Field("Width", &ROS2CameraSensorEditorComponent::m_width)
+                ->Field("Height", &ROS2CameraSensorEditorComponent::m_height)
+                ->Field("Depth", &ROS2CameraSensorEditorComponent::m_depthCamera)
+                ->Field("Color", &ROS2CameraSensorEditorComponent::m_colorCamera)
+                ->Field("SensorConfig", &ROS2CameraSensorEditorComponent::m_sensorConfiguration);
+
+            if (AZ::EditContext* editContext = serialize->GetEditContext())
+            {
+                editContext->Class<ROS2CameraSensorEditorComponent>("ROS2 Camera Sensor", "[Camera component]")
+                    ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
+                    ->Attribute(AZ::Edit::Attributes::Category, "ROS2")
+                    ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game"))
+                    ->DataElement(
+                        AZ::Edit::UIHandlers::Default,
+                        &ROS2CameraSensorEditorComponent::m_sensorConfiguration,
+                        "Sensor configuration",
+                        "Sensor configuration")
+                    ->DataElement(
+                        AZ::Edit::UIHandlers::Default,
+                        &ROS2CameraSensorEditorComponent::m_VerticalFieldOfViewDeg,
+                        "Vertical field of view",
+                        "Camera's vertical (y axis) field of view in degrees.")
+                    ->DataElement(AZ::Edit::UIHandlers::Default, &ROS2CameraSensorEditorComponent::m_width, "Image width", "Image width")
+                    ->DataElement(AZ::Edit::UIHandlers::Default, &ROS2CameraSensorEditorComponent::m_height, "Image height", "Image height")
+                    ->DataElement(
+                        AZ::Edit::UIHandlers::Default, &ROS2CameraSensorEditorComponent::m_colorCamera, "Color Camera", "Color Camera")
+                    ->DataElement(
+                        AZ::Edit::UIHandlers::Default, &ROS2CameraSensorEditorComponent::m_depthCamera, "Depth Camera", "Depth Camera");
+            }
+        }
+    }
+
+    void ROS2CameraSensorEditorComponent::Activate()
+    {
+        AzFramework::EntityDebugDisplayEventBus::Handler::BusConnect(this->GetEntityId());
+        AzToolsFramework::Components::EditorComponentBase::Activate();
+    }
+
+    void ROS2CameraSensorEditorComponent::Deactivate()
+    {
+        AzToolsFramework::Components::EditorComponentBase::Deactivate();
+        AzFramework::EntityDebugDisplayEventBus::Handler::BusDisconnect();
+    }
+
+    void ROS2CameraSensorEditorComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
+    {
+        required.push_back(AZ_CRC("ROS2Frame"));
+    }
+
+    void ROS2CameraSensorEditorComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
+    {
+        incompatible.push_back(AZ_CRC_CE("ROS2SensorCamera"));
+    }
+
+    void ROS2CameraSensorEditorComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& required)
+    {
+        required.push_back(AZ_CRC_CE("ROS2SensorCamera"));
+    }
+
+    void ROS2CameraSensorEditorComponent::BuildGameEntity(AZ::Entity* gameEntity)
+    {
+        gameEntity->CreateComponent<ROS2::ROS2CameraSensorComponent>(
+            m_sensorConfiguration, m_VerticalFieldOfViewDeg, m_width, m_height, m_colorCamera, m_depthCamera);
+    }
+
+    void ROS2CameraSensorEditorComponent::DisplayEntityViewport(
+        [[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay)
+    {
+        if (!m_sensorConfiguration.m_visualise)
+        {
+            return;
+        }
+        const AZ::u32 stateBefore = debugDisplay.GetState();
+        AZ::Transform transform = GetEntity()->GetTransform()->GetWorldTM();
+
+        // dimension of drawing
+        const float arrowRise = 1.1f;
+        const float arrowSize = 0.5f;
+        const float frustumScale = 0.1f;
+
+        transform.SetUniformScale(frustumScale);
+        debugDisplay.DepthTestOff();
+        const float ver = 0.5f * AZStd::tan((AZ::DegToRad(m_VerticalFieldOfViewDeg * 0.5f)));
+        const float hor = m_height * ver / m_width;
+
+        // frustum drawing
+        const AZ::Vector3 p1(-ver, -hor, 1.f);
+        const AZ::Vector3 p2(ver, -hor, 1.f);
+        const AZ::Vector3 p3(ver, hor, 1.f);
+        const AZ::Vector3 p4(-ver, hor, 1.f);
+        const AZ::Vector3 p0(0, 0, 0);
+        const AZ::Vector3 py(0.1, 0, 0);
+
+        const auto pt1 = transform.TransformPoint(p1);
+        const auto pt2 = transform.TransformPoint(p2);
+        const auto pt3 = transform.TransformPoint(p3);
+        const auto pt4 = transform.TransformPoint(p4);
+        const auto pt0 = transform.TransformPoint(p0);
+        const auto ptz = transform.TransformPoint(AZ::Vector3::CreateAxisZ(0.2f));
+        const auto pty = transform.TransformPoint(AZ::Vector3::CreateAxisY(0.2f));
+        const auto ptx = transform.TransformPoint(AZ::Vector3::CreateAxisX(0.2f));
+
+        debugDisplay.SetColor(AZ::Color(0.f, 1.f, 1.f, 1.f));
+        debugDisplay.SetLineWidth(1);
+        debugDisplay.DrawLine(pt1, pt2);
+        debugDisplay.DrawLine(pt2, pt3);
+        debugDisplay.DrawLine(pt3, pt4);
+        debugDisplay.DrawLine(pt4, pt1);
+        debugDisplay.DrawLine(pt1, pt0);
+        debugDisplay.DrawLine(pt2, pt0);
+        debugDisplay.DrawLine(pt3, pt0);
+        debugDisplay.DrawLine(pt4, pt0);
+
+        // up-arrow drawing
+        const AZ::Vector3 pa1(-arrowSize * ver, -arrowRise * hor, 1.f);
+        const AZ::Vector3 pa2(arrowSize * ver, -arrowRise * hor, 1.f);
+        const AZ::Vector3 pa3(0, (-arrowRise - arrowSize) * hor, 1.f);
+        const auto pat1 = transform.TransformPoint(pa1);
+        const auto pat2 = transform.TransformPoint(pa2);
+        const auto pat3 = transform.TransformPoint(pa3);
+
+        debugDisplay.SetColor(AZ::Color(0.f, 0.6f, 1.f, 1.f));
+        debugDisplay.SetLineWidth(1);
+        debugDisplay.DrawLine(pat1, pat2);
+        debugDisplay.DrawLine(pat2, pat3);
+        debugDisplay.DrawLine(pat3, pat1);
+
+        // coordinate system drawing
+        debugDisplay.SetColor(AZ::Color(1.f, 0.f, 0.f, 1.f));
+        debugDisplay.SetLineWidth(2);
+        debugDisplay.DrawLine(ptx, pt0);
+
+        debugDisplay.SetColor(AZ::Color(0.f, 1.f, 0.f, 1.f));
+        debugDisplay.SetLineWidth(2);
+        debugDisplay.DrawLine(pty, pt0);
+
+        debugDisplay.SetColor(AZ::Color(0.f, 0.f, 1.f, 1.f));
+        debugDisplay.SetLineWidth(2);
+        debugDisplay.DrawLine(ptz, pt0);
+
+        debugDisplay.SetState(stateBefore);
+    }
+
+    AZStd::pair<AZStd::string, TopicConfiguration> ROS2CameraSensorEditorComponent::MakeTopicConfigurationPair(
+        const AZStd::string& topic, const AZStd::string& messageType, const AZStd::string& configName) const
+    {
+        TopicConfiguration config;
+        config.m_topic = topic;
+        config.m_type = messageType;
+        return AZStd::make_pair(configName, config);
+    }
+
+} // namespace ROS2

+ 58 - 0
Gems/ROS2/Code/Source/Camera/ROS2CameraSensorEditorComponent.h

@@ -0,0 +1,58 @@
+/*
+ * 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/Component.h>
+#include <ROS2/Frame/NamespaceConfiguration.h>
+#include <ROS2/Frame/ROS2Transform.h>
+#include <ROS2/Sensor/SensorConfiguration.h>
+
+#include <AzToolsFramework/API/ComponentEntitySelectionBus.h>
+#include <AzToolsFramework/ToolsComponents/EditorComponentBase.h>
+
+#include "CameraSensor.h"
+
+namespace ROS2
+{
+    //! ROS2 Camera Editor sensor component class
+    //! Allows turning an entity into a camera sensor in Editor
+    //! Component draws camera frustrum in the Editor
+    class ROS2CameraSensorEditorComponent
+        : public AzToolsFramework::Components::EditorComponentBase
+        , protected AzFramework::EntityDebugDisplayEventBus::Handler
+    {
+    public:
+        ROS2CameraSensorEditorComponent();
+        ~ROS2CameraSensorEditorComponent() override = default;
+        AZ_EDITOR_COMPONENT(ROS2CameraSensorEditorComponent, "{3C2A86B2-AD58-4BF1-A5EF-71E0F94A2B42}");
+        static void Reflect(AZ::ReflectContext* context);
+        static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required);
+        static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible);
+        static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& required);
+
+        static void GetPr(AZ::ComponentDescriptor::DependencyArrayType& required);
+        void Activate() override;
+        void Deactivate() override;
+
+        //! AzToolsFramework::Components::EditorComponentBase override
+        void BuildGameEntity(AZ::Entity* gameEntity) override;
+
+    private:
+        // EntityDebugDisplayEventBus::Handler overrides
+        void DisplayEntityViewport(const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) override;
+
+        AZStd::pair<AZStd::string, TopicConfiguration> MakeTopicConfigurationPair(
+            const AZStd::string& topic, const AZStd::string& messageType, const AZStd::string& configName) const;
+        SensorConfiguration m_sensorConfiguration;
+        float m_VerticalFieldOfViewDeg = 90.0f;
+        int m_width = 640;
+        int m_height = 480;
+        bool m_colorCamera = true;
+        bool m_depthCamera = true;
+    };
+} // namespace ROS2

+ 42 - 6
Gems/ROS2/Code/Source/Frame/ROS2FrameComponent.cpp

@@ -7,6 +7,7 @@
  */
 
 #include <AzCore/Component/Entity.h>
+#include <AzCore/Component/EntityUtils.h>
 #include <AzCore/Serialization/EditContext.h>
 #include <AzCore/Serialization/EditContextConstants.inl>
 #include <AzCore/Serialization/SerializeContext.h>
@@ -14,6 +15,7 @@
 #include <ROS2/ROS2Bus.h>
 #include <ROS2/ROS2GemUtilities.h>
 #include <ROS2/Utilities/ROS2Names.h>
+
 namespace ROS2
 {
     namespace Internal
@@ -48,6 +50,7 @@ namespace ROS2
             AZ::Entity* parentEntity = nullptr;
             AZ::ComponentApplicationBus::BroadcastResult(parentEntity, &AZ::ComponentApplicationRequests::FindEntity, parentEntityId);
             AZ_Assert(parentEntity, "No parent entity id : %s", parentEntityId.ToString().c_str());
+
             auto* component = Utils::GetGameOrEditorComponent<ROS2FrameComponent>(parentEntity);
             if (component == nullptr)
             { // Parent entity has no ROS2Frame, but there can still be a ROS2Frame in its ancestors
@@ -57,6 +60,17 @@ namespace ROS2
             // Found the component!
             return component;
         }
+
+        //! Checks whether the entity has a component of the given type
+        //! @param entity pointer to entity
+        //! @param typeId type of the component
+        //! @returns true if entity has component with given type
+        static bool CheckIfEntityHasComponentOfType(const AZ::Entity* entity, const AZ::Uuid typeId)
+        {
+            auto components = AZ::EntityUtils::FindDerivedComponents(entity, typeId);
+            return !components.empty();
+        }
+
     } // namespace Internal
 
     void ROS2FrameComponent::Activate()
@@ -65,9 +79,26 @@ namespace ROS2
 
         if (m_publishTransform)
         {
+            AZ_TracePrintf("ROS2FrameComponent", "Setting up %s", GetFrameID().data());
+
+            // The frame will always be dynamic if it's a top entity.
+            if (IsTopLevel())
+            {
+                m_isDynamic = true;
+            }
+            // Otherwise it'll be dynamic when it has joints and it's not a fixed joint.
+            else
+            {
+                const bool hasJoints = Internal::CheckIfEntityHasComponentOfType(
+                    m_entity, AZ::Uuid("{B01FD1D2-1D91-438D-874A-BF5EB7E919A8}")); // Physx::JointComponent;
+                const bool hasFixedJoints = Internal::CheckIfEntityHasComponentOfType(
+                    m_entity, AZ::Uuid("{02E6C633-8F44-4CEE-AE94-DCB06DE36422}")); // Physx::FixedJointComponent
+                m_isDynamic = hasJoints && !hasFixedJoints;
+            }
+
             AZ_TracePrintf(
                 "ROS2FrameComponent",
-                "Setting up %s transfrom between parent %s and child %s to be published %s\n",
+                "Setting up %s transform between parent %s and child %s to be published %s\n",
                 IsDynamic() ? "dynamic" : "static",
                 GetParentFrameID().data(),
                 GetFrameID().data(),
@@ -114,20 +145,25 @@ namespace ROS2
 
     bool ROS2FrameComponent::IsDynamic() const
     {
-        return IsTopLevel();
+        return m_isDynamic;
     }
-
     const ROS2FrameComponent* ROS2FrameComponent::GetParentROS2FrameComponent() const
     {
         return Internal::GetFirstROS2FrameAncestor(GetEntity());
     }
 
-    const AZ::Transform& ROS2FrameComponent::GetFrameTransform() const
+    AZ::Transform ROS2FrameComponent::GetFrameTransform() const
     {
         auto* transformInterface = Internal::GetEntityTransformInterface(GetEntity());
-        if (GetParentROS2FrameComponent() != nullptr)
+        if (const auto* parentFrame = GetParentROS2FrameComponent(); parentFrame != nullptr)
         {
-            return transformInterface->GetLocalTM();
+            auto* ancestorTransformInterface = Internal::GetEntityTransformInterface(parentFrame->GetEntity());
+            AZ_Assert(ancestorTransformInterface, "No transform interface for an entity with a ROS2Frame component, which requires it!");
+
+            const auto worldFromAncestor = ancestorTransformInterface->GetWorldTM();
+            const auto worldFromThis = transformInterface->GetWorldTM();
+            const auto ancestorFromWorld = worldFromAncestor.GetInverse();
+            return ancestorFromWorld * worldFromThis;
         }
         return transformInterface->GetWorldTM();
     }

+ 86 - 30
Gems/ROS2/Code/Source/Lidar/LidarRaycaster.cpp

@@ -5,49 +5,101 @@
  * SPDX-License-Identifier: Apache-2.0 OR MIT
  *
  */
-#include "LidarRaycaster.h"
-#include <AzCore/Interface/Interface.h>
+
 #include <AzCore/std/smart_ptr/make_shared.h>
-#include <AzCore/std/smart_ptr/shared_ptr.h>
+#include <AzCore/Component/Component.h>
 #include <AzFramework/Physics/Common/PhysicsSceneQueries.h>
 #include <AzFramework/Physics/PhysicsScene.h>
 #include <AzFramework/Physics/PhysicsSystem.h>
 #include <AzFramework/Physics/Shape.h>
+#include <Lidar/LidarRaycaster.h>
+#include <Lidar/LidarTemplateUtils.h>
 
 namespace ROS2
 {
-    void LidarRaycaster::SetRaycasterScene(const AzPhysics::SceneHandle& handle)
+    static AzPhysics::SceneHandle GetPhysicsSceneFromEntityId(const AZ::EntityId& entityId)
     {
-        m_sceneHandle = handle;
+        auto* physicsSystem = AZ::Interface<AzPhysics::SystemInterface>::Get();
+        auto foundBody = physicsSystem->FindAttachedBodyHandleFromEntityId(entityId);
+        AzPhysics::SceneHandle lidarPhysicsSceneHandle = foundBody.first;
+        if (foundBody.first == AzPhysics::InvalidSceneHandle)
+        {
+            auto* sceneInterface = AZ::Interface<AzPhysics::SceneInterface>::Get();
+            lidarPhysicsSceneHandle = sceneInterface->GetSceneHandle(AzPhysics::DefaultPhysicsSceneName);
+        }
+
+        AZ_Assert(lidarPhysicsSceneHandle != AzPhysics::InvalidSceneHandle, "Invalid physics scene handle for entity");
+        return lidarPhysicsSceneHandle;
     }
 
-    AZStd::vector<AZ::Vector3> LidarRaycaster::PerformRaycast(
-        const AZ::Vector3& start,
-        const AZStd::vector<AZ::Vector3>& directions,
-        const AZ::Transform& globalToLidarTM,
-        float distance,
-        bool ignoreLayer,
-        unsigned int ignoredLayerIndex) const
+    LidarRaycaster::LidarRaycaster(LidarId busId, AZ::EntityId sceneEntityId)
+        : m_busId{ busId }
+        , m_sceneEntityId{ sceneEntityId }
     {
-        AZStd::vector<AZ::Vector3> results;
+        ROS2::LidarRaycasterRequestBus::Handler::BusConnect(busId);
+    }
+
+    LidarRaycaster::LidarRaycaster(LidarRaycaster&& lidarRaycaster)
+        : m_busId{ lidarRaycaster.m_busId }
+        , m_sceneEntityId{ lidarRaycaster.m_sceneEntityId }
+        , m_sceneHandle{ lidarRaycaster.m_sceneHandle }
+        , m_range{ lidarRaycaster.m_range }
+        , m_addMaxRangePoints{ lidarRaycaster.m_addMaxRangePoints }
+        , m_rayRotations{ AZStd::move(lidarRaycaster.m_rayRotations) }
+        , m_ignoreLayer{ lidarRaycaster.m_ignoreLayer }
+        , m_ignoredLayerIndex{ lidarRaycaster.m_ignoredLayerIndex }
+    {
+        lidarRaycaster.BusDisconnect();
+        lidarRaycaster.m_busId = LidarId::CreateNull();
+
+        ROS2::LidarRaycasterRequestBus::Handler::BusConnect(m_busId);
+    }
+
+    LidarRaycaster::~LidarRaycaster()
+    {
+        ROS2::LidarRaycasterRequestBus::Handler::BusDisconnect();
+    }
+
+    void LidarRaycaster::ConfigureRayOrientations(const AZStd::vector<AZ::Vector3>& orientations)
+    {
+        ValidateRayOrientations(orientations);
+        m_rayRotations = orientations;
+    }
+
+    void LidarRaycaster::ConfigureRayRange(float range)
+    {
+        ValidateRayRange(range);
+        m_range = range;
+    }
+
+    AZStd::vector<AZ::Vector3> LidarRaycaster::PerformRaycast(const AZ::Transform& lidarTransform)
+    {
+        AZ_Assert(!m_rayRotations.empty(), "Ray poses are not configured. Unable to Perform a raycast.");
+        AZ_Assert(m_range > 0.0f, "Ray range is not configured. Unable to Perform a raycast.");
+
         if (m_sceneHandle == AzPhysics::InvalidSceneHandle)
         {
-            AZ_Warning("LidarRaycaster", false, "No valid scene handle");
-            return results;
+            m_sceneHandle = GetPhysicsSceneFromEntityId(m_sceneEntityId);
         }
 
+        const AZStd::vector<AZ::Vector3> rayDirections =
+            LidarTemplateUtils::RotationsToDirections(m_rayRotations, lidarTransform.GetEulerRadians());
+
+        const AZ::Vector3 lidarPosition = lidarTransform.GetTranslation();
+
+        AZStd::vector<AZ::Vector3> results;
         AzPhysics::SceneQueryRequests requests;
-        requests.reserve(directions.size());
-        results.reserve(directions.size());
-        for (const AZ::Vector3& direction : directions)
+        requests.reserve(rayDirections.size());
+        results.reserve(rayDirections.size());
+        for (const AZ::Vector3& direction : rayDirections)
         {
             AZStd::shared_ptr<AzPhysics::RayCastRequest> request = AZStd::make_shared<AzPhysics::RayCastRequest>();
-            request->m_start = start;
+            request->m_start = lidarPosition;
             request->m_direction = direction;
-            request->m_distance = distance;
+            request->m_distance = m_range;
             request->m_reportMultipleHits = false;
-            request->m_filterCallback =
-                [ignoreLayer, ignoredLayerIndex](const AzPhysics::SimulatedBody* simBody, const Physics::Shape* shape)
+            request->m_filterCallback = [ignoredLayerIndex = this->m_ignoredLayerIndex, ignoreLayer = this->m_ignoreLayer](
+                                            const AzPhysics::SimulatedBody* simBody, const Physics::Shape* shape)
             {
                 if (ignoreLayer && (shape->GetCollisionLayer().GetIndex() == ignoredLayerIndex))
                 {
@@ -63,26 +115,30 @@ namespace ROS2
 
         auto* sceneInterface = AZ::Interface<AzPhysics::SceneInterface>::Get();
         auto requestResults = sceneInterface->QuerySceneBatch(m_sceneHandle, requests);
-        AZ_Assert(requestResults.size() == directions.size(), "request size should be equal to directions size");
+        AZ_Assert(requestResults.size() == rayDirections.size(), "Request size should be equal to directions size");
         for (int i = 0; i < requestResults.size(); i++)
         {
             const auto& requestResult = requestResults[i];
             if (!requestResult.m_hits.empty())
             {
-                auto globalHitPoint = requestResult.m_hits[0].m_position;
-                results.push_back(globalToLidarTM.TransformPoint(globalHitPoint)); // Transform back to local frame
+                results.push_back(requestResult.m_hits[0].m_position);
             }
-            else if (m_addPointsMaxRange)
+            else if (m_addMaxRangePoints)
             {
-                results.push_back(globalToLidarTM.TransformPoint(start + directions[i] * distance));
+                const AZ::Vector3 maxPoint = lidarTransform.TransformPoint(rayDirections[i] * m_range);
+                results.push_back(maxPoint);
             }
         }
         return results;
     }
 
-    void LidarRaycaster::SetAddPointsMaxRange(bool addPointsMaxRange)
+    void LidarRaycaster::ConfigureLayerIgnoring(bool ignoreLayer, AZ::u32 layerIndex)
     {
-        m_addPointsMaxRange = addPointsMaxRange;
+        m_ignoreLayer = ignoreLayer;
+        m_ignoredLayerIndex = layerIndex;
+    }
+    void LidarRaycaster::ConfigureMaxRangePointAddition(bool addMaxRangePoints)
+    {
+        m_addMaxRangePoints = addMaxRangePoints;
     }
-
 } // namespace ROS2

+ 24 - 31
Gems/ROS2/Code/Source/Lidar/LidarRaycaster.h

@@ -12,44 +12,37 @@
 #include <AzCore/Math/Vector3.h>
 #include <AzCore/std/containers/vector.h>
 #include <AzFramework/Physics/PhysicsScene.h>
+#include <ROS2/Lidar/LidarRaycasterBus.h>
 
 namespace ROS2
 {
-    //! A simple implementation of Lidar operation in terms of raycasting.
-    class LidarRaycaster
+    class LidarRaycaster : protected LidarRaycasterRequestBus::Handler
     {
     public:
-        //! Set the Scene for the ray-casting.
-        //! This should be the scene with the Entity that holds the sensor.
-        //! @code
-        //! auto sceneHandle = AZ::RPI::Scene::GetSceneForEntityId(GetEntityId());
-        //! @endcode
-        //! @param handle Scene that will be subject to ray-casting.
-        void SetRaycasterScene(const AzPhysics::SceneHandle& handle);
+        LidarRaycaster(LidarId busId, AZ::EntityId sceneEntityId);
+        LidarRaycaster(LidarRaycaster&& lidarSystem);
+        LidarRaycaster(const LidarRaycaster& lidarSystem) = default;
+        ~LidarRaycaster() override;
 
-        //! Perform raycast against the current scene.
-        //! @param start Starting point of rays. This is a simplification since there can be multiple starting points
-        //! in real sensors.
-        //! @param directions Directions in which to shoot rays. These should be generated from Lidar configuration.
-        //! @param globalToLidarTM Transform from global to lidar reference frame.
-        //! @param distance Maximum distance for ray-casting.
-        //! @param ignoreLayer Should a specified collision layer be ignored
-        //! @param ignoredLayerIndex Index of collision layer to be ignored
-        //! @return Hits of raycast. The returned vector size can be anything between zero and size of directions.
-        //! No hits further than distance will be reported.
-        AZStd::vector<AZ::Vector3> PerformRaycast(
-            const AZ::Vector3& start,
-            const AZStd::vector<AZ::Vector3>& directions,
-            const AZ::Transform& globalToLidarTM,
-            float distance,
-            bool ignoreLayer,
-            unsigned int ignoredLayerIndex) const;
-
-        //! If true the raycaster will also include points at maximum range when nothing was hit
-        void SetAddPointsMaxRange(bool addPointsMaxRange);
+    protected:
+        // LidarRaycasterRequestBus overrides
+        void ConfigureRayOrientations(const AZStd::vector<AZ::Vector3>& orientations) override;
+        void ConfigureRayRange(float range) override;
+        AZStd::vector<AZ::Vector3> PerformRaycast(const AZ::Transform& lidarTransform) override;
+        void ConfigureLayerIgnoring(bool ignoreLayer, AZ::u32 layerIndex) override;
+        void ConfigureMaxRangePointAddition(bool addMaxRangePoints) override;
 
     private:
-        AzPhysics::SceneHandle m_sceneHandle;
-        bool m_addPointsMaxRange{ false };
+        LidarId m_busId;
+        //! EntityId that is used to acquire the physics scene handle.
+        AZ::EntityId m_sceneEntityId;
+        AzPhysics::SceneHandle m_sceneHandle{ AzPhysics::InvalidSceneHandle };
+
+        float m_range{ 1.0f };
+        bool m_addMaxRangePoints{ false };
+        AZStd::vector<AZ::Vector3> m_rayRotations{ { AZ::Vector3::CreateZero() } };
+
+        bool m_ignoreLayer{ false };
+        AZ::u32 m_ignoredLayerIndex{ 0 };
     };
 } // namespace ROS2

+ 53 - 0
Gems/ROS2/Code/Source/Lidar/LidarRegistrarEditorSystemComponent.cpp

@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include <AzCore/Serialization/SerializeContext.h>
+#include <Lidar/LidarRegistrarEditorSystemComponent.h>
+
+namespace ROS2
+{
+    void LidarRegistrarEditorSystemComponent::Reflect(AZ::ReflectContext* context)
+    {
+        if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
+        {
+            serializeContext->Class<LidarRegistrarEditorSystemComponent, LidarRegistrarSystemComponent>()->Version(0);
+        }
+    }
+
+    void LidarRegistrarEditorSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
+    {
+        LidarRegistrarSystemComponent::GetProvidedServices(provided);
+        provided.push_back(AZ_CRC_CE("LidarRegistrarEditorService"));
+    }
+
+    void LidarRegistrarEditorSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
+    {
+        LidarRegistrarSystemComponent::GetIncompatibleServices(incompatible);
+        incompatible.push_back(AZ_CRC_CE("LidarRegistrarEditorService"));
+    }
+
+    void LidarRegistrarEditorSystemComponent::GetRequiredServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& required)
+    {
+        LidarRegistrarSystemComponent::GetRequiredServices(required);
+    }
+
+    void LidarRegistrarEditorSystemComponent::GetDependentServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& dependent)
+    {
+        LidarRegistrarSystemComponent::GetDependentServices(dependent);
+    }
+
+    void LidarRegistrarEditorSystemComponent::Activate()
+    {
+        LidarRegistrarSystemComponent::Activate();
+    }
+
+    void LidarRegistrarEditorSystemComponent::Deactivate()
+    {
+        LidarRegistrarSystemComponent::Deactivate();
+    }
+} // namespace ROS2

+ 34 - 0
Gems/ROS2/Code/Source/Lidar/LidarRegistrarEditorSystemComponent.h

@@ -0,0 +1,34 @@
+/*
+ * 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 <AzToolsFramework/Entity/EditorEntityContextBus.h>
+#include <Lidar/LidarRegistrarSystemComponent.h>
+
+namespace ROS2
+{
+    class LidarRegistrarEditorSystemComponent : public LidarRegistrarSystemComponent
+    {
+    public:
+        AZ_COMPONENT(LidarRegistrarEditorSystemComponent, "{7f11b599-5ace-4498-a9a4-ad280c92bacc}", LidarRegistrarSystemComponent);
+        static void Reflect(AZ::ReflectContext* context);
+
+        static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided);
+        static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible);
+        static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required);
+        static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent);
+
+        LidarRegistrarEditorSystemComponent() = default;
+        ~LidarRegistrarEditorSystemComponent() = default;
+
+    private:
+        void Activate() override;
+        void Deactivate() override;
+    };
+} // namespace ROS2

+ 112 - 0
Gems/ROS2/Code/Source/Lidar/LidarRegistrarSystemComponent.cpp

@@ -0,0 +1,112 @@
+/*
+ * 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 <Lidar/LidarRegistrarSystemComponent.h>
+#include <AzCore/Serialization/EditContext.h>
+#include <AzCore/Serialization/SerializeContext.h>
+
+namespace ROS2
+{
+    LidarRegistrarSystemComponent::LidarRegistrarSystemComponent()
+    {
+        if (!LidarRegistrarInterface::Get())
+        {
+            LidarRegistrarInterface::Register(this);
+        }
+    }
+
+    LidarRegistrarSystemComponent::~LidarRegistrarSystemComponent()
+    {
+        if (LidarRegistrarInterface::Get() == this)
+        {
+            LidarRegistrarInterface::Unregister(this);
+        }
+    }
+
+    void LidarRegistrarSystemComponent::Init()
+    {
+    }
+
+    void LidarRegistrarSystemComponent::Activate()
+    {
+        m_physxLidarSystem.Activate();
+    }
+
+    void LidarRegistrarSystemComponent::Deactivate()
+    {
+        m_physxLidarSystem.Deactivate();
+    }
+
+    void LidarRegistrarSystemComponent::Reflect(AZ::ReflectContext* context)
+    {
+        if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
+        {
+            serializeContext->Class<LidarRegistrarSystemComponent, AZ::Component>()->Version(0);
+
+            if (AZ::EditContext* editContext = serializeContext->GetEditContext())
+            {
+                editContext
+                    ->Class<LidarRegistrarSystemComponent>(
+                        "Lidar Registrar", "Manages the LidarSystem registration and stores their metadata.")
+                    ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
+                    ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("System"))
+                    ->Attribute(AZ::Edit::Attributes::Category, "ROS2")
+                    ->Attribute(AZ::Edit::Attributes::AutoExpand, true);
+            }
+        }
+    }
+
+    void LidarRegistrarSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
+    {
+        provided.push_back(AZ_CRC_CE("LidarRegistrarService"));
+    }
+
+    void LidarRegistrarSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
+    {
+        incompatible.push_back(AZ_CRC_CE("LidarRegistrarService"));
+    }
+
+    void LidarRegistrarSystemComponent::GetRequiredServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& required)
+    {
+        required.push_back(AZ_CRC_CE("ROS2Service"));
+    }
+
+    void LidarRegistrarSystemComponent::GetDependentServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& dependent)
+    {
+    }
+
+    void LidarRegistrarSystemComponent::RegisterLidarSystem(const char* name, const char* description, const LidarSystemFeatures& features)
+    {
+        AZ_Assert(
+            !m_registeredLidarSystems.contains(AZ_CRC(name)),
+            "A lidar system with the provided name already exists. Please choose a different name.");
+        m_registeredLidarSystems.emplace(AZ_CRC(name), LidarSystemMetaData{ name, description, features });
+    }
+
+    AZStd::vector<AZStd::string> LidarRegistrarSystemComponent::GetRegisteredLidarSystems() const
+    {
+        AZStd::vector<AZStd::string> lidarSystemList;
+        lidarSystemList.reserve(m_registeredLidarSystems.size());
+        for (const auto& lidarSystem : m_registeredLidarSystems)
+        {
+            lidarSystemList.push_back(lidarSystem.second.m_name);
+        }
+
+        return lidarSystemList;
+    }
+
+    const LidarSystemMetaData* LidarRegistrarSystemComponent::GetLidarSystemMetaData(const AZStd::string& name) const
+    {
+        if (auto lidarSystem = m_registeredLidarSystems.find(AZ_CRC(name)); lidarSystem != m_registeredLidarSystems.end())
+        {
+            return &lidarSystem->second;
+        }
+
+        return nullptr;
+    }
+} // namespace ROS2

+ 48 - 0
Gems/ROS2/Code/Source/Lidar/LidarRegistrarSystemComponent.h

@@ -0,0 +1,48 @@
+/*
+ * 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/Component.h>
+#include <Lidar/LidarSystem.h>
+#include <ROS2/Lidar/LidarRegistrarBus.h>
+
+namespace ROS2
+{
+    //! A Component that manages LidarSystems' registration and storage of their metadata.
+    class LidarRegistrarSystemComponent
+        : public AZ::Component
+        , protected LidarRegistrarRequestBus::Handler
+    {
+    public:
+        AZ_COMPONENT(LidarRegistrarSystemComponent, "{78cba3f1-db2c-46de-9c3d-c40dd72f2f1e}");
+        static void Reflect(AZ::ReflectContext* context);
+
+        static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided);
+        static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible);
+        static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required);
+        static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent);
+
+        LidarRegistrarSystemComponent();
+        virtual ~LidarRegistrarSystemComponent();
+
+    protected:
+        void Init() override;
+        void Activate() override;
+        void Deactivate() override;
+
+        // LidarRegistrarRequestBus overrides
+        void RegisterLidarSystem(const char* name, const char* description, const LidarSystemFeatures& features) override;
+        AZStd::vector<AZStd::string> GetRegisteredLidarSystems() const override;
+        const LidarSystemMetaData* GetLidarSystemMetaData(const AZStd::string& name) const override;
+
+    private:
+        LidarSystem m_physxLidarSystem;
+        AZStd::unordered_map<AZ::Crc32, LidarSystemMetaData> m_registeredLidarSystems;
+    };
+} // namespace ROS2

+ 56 - 0
Gems/ROS2/Code/Source/Lidar/LidarSystem.cpp

@@ -0,0 +1,56 @@
+/*
+ * 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 <Lidar/LidarSystem.h>
+#include <ROS2/Lidar/LidarRegistrarBus.h>
+
+namespace ROS2
+{
+    LidarSystem::LidarSystem(LidarSystem&& lidarSystem)
+        : m_lidars{ AZStd::move(lidarSystem.m_lidars) }
+    {
+        lidarSystem.BusDisconnect();
+    }
+
+    LidarSystem& LidarSystem::operator=(LidarSystem&& lidarSystem)
+    {
+        lidarSystem.BusDisconnect();
+        return lidarSystem;
+    }
+
+    void LidarSystem::Activate()
+    {
+        static constexpr const char* Description = "Collider-based lidar implementation that uses the PhysX engine's raycasting.";
+        static constexpr auto SupportedFeatures =
+            aznumeric_cast<LidarSystemFeatures>(LidarSystemFeatures::CollisionLayers | LidarSystemFeatures::MaxRangePoints);
+
+        LidarSystemRequestBus::Handler::BusConnect(AZ_CRC(SystemName));
+
+        auto* lidarRegistrarInterface = ROS2::LidarRegistrarInterface::Get();
+        AZ_Assert(lidarRegistrarInterface != nullptr, "The Lidar Registrar interface was inaccessible.");
+
+        if (!lidarRegistrarInterface->GetLidarSystemMetaData(SystemName))
+        {
+            lidarRegistrarInterface->RegisterLidarSystem(SystemName, Description, SupportedFeatures);
+        }
+    }
+
+    void LidarSystem::Deactivate()
+    {
+        if (LidarSystemRequestBus::Handler::BusIsConnectedId(AZ_CRC(SystemName)))
+        {
+            LidarSystemRequestBus::Handler::BusDisconnect();
+        }
+    }
+
+    LidarId LidarSystem::CreateLidar(AZ::EntityId lidarEntityId)
+    {
+        LidarId lidarId = LidarId::CreateRandom();
+        m_lidars.emplace_back(lidarId, lidarEntityId);
+        return lidarId;
+    }
+} // namespace ROS2

Alguns arquivos não foram mostrados porque muitos arquivos mudaram nesse diff