소스 검색

Merge pull request #11 from o3de/incisor/GeomNodes

O3DE GeomNodes Gem
Royal OBrien 2 년 전
부모
커밋
eb4b7a96c3
100개의 변경된 파일8399개의 추가작업 그리고 0개의 파일을 삭제
  1. 61 0
      .clang-format
  2. 6 0
      Gems/O3DE/GeomNodes/3rdParty/FindBridge.cmake
  3. 5 0
      Gems/O3DE/GeomNodes/3rdParty/Platform/Windows/bridge_windows.cmake
  4. 30 0
      Gems/O3DE/GeomNodes/CMakeLists.txt
  5. 227 0
      Gems/O3DE/GeomNodes/Code/CMakeLists.txt
  6. 39 0
      Gems/O3DE/GeomNodes/Code/Include/GeomNodes/GeomNodesBus.h
  7. 44 0
      Gems/O3DE/GeomNodes/Code/Include/GeomNodes/GeomNodesTypeIds.h
  8. 11 0
      Gems/O3DE/GeomNodes/Code/Platform/Android/PAL_android.cmake
  9. 10 0
      Gems/O3DE/GeomNodes/Code/Platform/Android/geomnodes_api_files.cmake
  10. 15 0
      Gems/O3DE/GeomNodes/Code/Platform/Android/geomnodes_private_files.cmake
  11. 15 0
      Gems/O3DE/GeomNodes/Code/Platform/Android/geomnodes_shared_files.cmake
  12. 11 0
      Gems/O3DE/GeomNodes/Code/Platform/Linux/PAL_linux.cmake
  13. 10 0
      Gems/O3DE/GeomNodes/Code/Platform/Linux/geomnodes_api_files.cmake
  14. 10 0
      Gems/O3DE/GeomNodes/Code/Platform/Linux/geomnodes_editor_api_files.cmake
  15. 15 0
      Gems/O3DE/GeomNodes/Code/Platform/Linux/geomnodes_private_files.cmake
  16. 15 0
      Gems/O3DE/GeomNodes/Code/Platform/Linux/geomnodes_shared_files.cmake
  17. 11 0
      Gems/O3DE/GeomNodes/Code/Platform/Mac/PAL_mac.cmake
  18. 10 0
      Gems/O3DE/GeomNodes/Code/Platform/Mac/geomnodes_api_files.cmake
  19. 10 0
      Gems/O3DE/GeomNodes/Code/Platform/Mac/geomnodes_editor_api_files.cmake
  20. 15 0
      Gems/O3DE/GeomNodes/Code/Platform/Mac/geomnodes_private_files.cmake
  21. 15 0
      Gems/O3DE/GeomNodes/Code/Platform/Mac/geomnodes_shared_files.cmake
  22. 11 0
      Gems/O3DE/GeomNodes/Code/Platform/Windows/PAL_windows.cmake
  23. 10 0
      Gems/O3DE/GeomNodes/Code/Platform/Windows/geomnodes_api_files.cmake
  24. 10 0
      Gems/O3DE/GeomNodes/Code/Platform/Windows/geomnodes_editor_api_files.cmake
  25. 15 0
      Gems/O3DE/GeomNodes/Code/Platform/Windows/geomnodes_private_files.cmake
  26. 15 0
      Gems/O3DE/GeomNodes/Code/Platform/Windows/geomnodes_shared_files.cmake
  27. 11 0
      Gems/O3DE/GeomNodes/Code/Platform/iOS/PAL_ios.cmake
  28. 10 0
      Gems/O3DE/GeomNodes/Code/Platform/iOS/geomnodes_api_files.cmake
  29. 15 0
      Gems/O3DE/GeomNodes/Code/Platform/iOS/geomnodes_private_files.cmake
  30. 15 0
      Gems/O3DE/GeomNodes/Code/Platform/iOS/geomnodes_shared_files.cmake
  31. 72 0
      Gems/O3DE/GeomNodes/Code/Source/Editor/Common/GNAPI.cpp
  32. 41 0
      Gems/O3DE/GeomNodes/Code/Source/Editor/Common/GNAPI.h
  33. 61 0
      Gems/O3DE/GeomNodes/Code/Source/Editor/Common/GNConstants.h
  34. 26 0
      Gems/O3DE/GeomNodes/Code/Source/Editor/Common/GNEvents.h
  35. 12 0
      Gems/O3DE/GeomNodes/Code/Source/Editor/Commons.h
  36. 653 0
      Gems/O3DE/GeomNodes/Code/Source/Editor/Components/EditorGeomNodesComponent.cpp
  37. 124 0
      Gems/O3DE/GeomNodes/Code/Source/Editor/Components/EditorGeomNodesComponent.h
  38. 122 0
      Gems/O3DE/GeomNodes/Code/Source/Editor/Components/EditorGeomNodesSystemComponent.cpp
  39. 55 0
      Gems/O3DE/GeomNodes/Code/Source/Editor/Components/EditorGeomNodesSystemComponent.h
  40. 59 0
      Gems/O3DE/GeomNodes/Code/Source/Editor/Configuration/GNConfiguration.cpp
  41. 43 0
      Gems/O3DE/GeomNodes/Code/Source/Editor/Configuration/GNConfiguration.h
  42. 146 0
      Gems/O3DE/GeomNodes/Code/Source/Editor/Configuration/GNEditorSettingsRegistryManager.cpp
  43. 28 0
      Gems/O3DE/GeomNodes/Code/Source/Editor/Configuration/GNEditorSettingsRegistryManager.h
  44. 55 0
      Gems/O3DE/GeomNodes/Code/Source/Editor/Configuration/GNSettingsRegistryManager.cpp
  45. 41 0
      Gems/O3DE/GeomNodes/Code/Source/Editor/Configuration/GNSettingsRegistryManager.h
  46. 33 0
      Gems/O3DE/GeomNodes/Code/Source/Editor/EBus/EditorGeomNodesComponentBus.h
  47. 34 0
      Gems/O3DE/GeomNodes/Code/Source/Editor/EBus/IpcHandlerBus.h
  48. 30 0
      Gems/O3DE/GeomNodes/Code/Source/Editor/EBus/ValidatorBus.h
  49. 121 0
      Gems/O3DE/GeomNodes/Code/Source/Editor/Math/MathHelper.cpp
  50. 63 0
      Gems/O3DE/GeomNodes/Code/Source/Editor/Math/MathHelper.h
  51. 62 0
      Gems/O3DE/GeomNodes/Code/Source/Editor/Modules/GeomNodesEditorModule.cpp
  52. 216 0
      Gems/O3DE/GeomNodes/Code/Source/Editor/Rendering/Atom/GNAttributeBuffer.h
  53. 196 0
      Gems/O3DE/GeomNodes/Code/Source/Editor/Rendering/Atom/GNBuffer.h
  54. 300 0
      Gems/O3DE/GeomNodes/Code/Source/Editor/Rendering/GNMeshController.cpp
  55. 84 0
      Gems/O3DE/GeomNodes/Code/Source/Editor/Rendering/GNMeshController.h
  56. 537 0
      Gems/O3DE/GeomNodes/Code/Source/Editor/Rendering/GNMeshData.cpp
  57. 127 0
      Gems/O3DE/GeomNodes/Code/Source/Editor/Rendering/GNMeshData.h
  58. 292 0
      Gems/O3DE/GeomNodes/Code/Source/Editor/Rendering/GNModelData.cpp
  59. 74 0
      Gems/O3DE/GeomNodes/Code/Source/Editor/Rendering/GNModelData.h
  60. 317 0
      Gems/O3DE/GeomNodes/Code/Source/Editor/Rendering/GNRenderMesh.cpp
  61. 156 0
      Gems/O3DE/GeomNodes/Code/Source/Editor/Rendering/GNRenderMesh.h
  62. 181 0
      Gems/O3DE/GeomNodes/Code/Source/Editor/Systems/GNInstance.cpp
  63. 64 0
      Gems/O3DE/GeomNodes/Code/Source/Editor/Systems/GNInstance.h
  64. 269 0
      Gems/O3DE/GeomNodes/Code/Source/Editor/Systems/GNParamContext.cpp
  65. 208 0
      Gems/O3DE/GeomNodes/Code/Source/Editor/Systems/GNParamContext.h
  66. 384 0
      Gems/O3DE/GeomNodes/Code/Source/Editor/Systems/GNProperty.cpp
  67. 266 0
      Gems/O3DE/GeomNodes/Code/Source/Editor/Systems/GNProperty.h
  68. 16 0
      Gems/O3DE/GeomNodes/Code/Source/Editor/Systems/GNSystemInterface.cpp
  69. 65 0
      Gems/O3DE/GeomNodes/Code/Source/Editor/Systems/GNSystemInterface.h
  70. 159 0
      Gems/O3DE/GeomNodes/Code/Source/Editor/Systems/GeomNodesSystem.cpp
  71. 82 0
      Gems/O3DE/GeomNodes/Code/Source/Editor/Systems/GeomNodesSystem.h
  72. 57 0
      Gems/O3DE/GeomNodes/Code/Source/Editor/UI/ConfigurationWidget.cpp
  73. 46 0
      Gems/O3DE/GeomNodes/Code/Source/Editor/UI/ConfigurationWidget.h
  74. 89 0
      Gems/O3DE/GeomNodes/Code/Source/Editor/UI/EditorWindow.cpp
  75. 44 0
      Gems/O3DE/GeomNodes/Code/Source/Editor/UI/EditorWindow.h
  76. 60 0
      Gems/O3DE/GeomNodes/Code/Source/Editor/UI/EditorWindow.ui
  77. 41 0
      Gems/O3DE/GeomNodes/Code/Source/Editor/UI/FunctorValidator.cpp
  78. 44 0
      Gems/O3DE/GeomNodes/Code/Source/Editor/UI/FunctorValidator.h
  79. 59 0
      Gems/O3DE/GeomNodes/Code/Source/Editor/UI/GeomNodesValidator.cpp
  80. 43 0
      Gems/O3DE/GeomNodes/Code/Source/Editor/UI/GeomNodesValidator.h
  81. 122 0
      Gems/O3DE/GeomNodes/Code/Source/Editor/UI/PropertyFileSelect.cpp
  82. 71 0
      Gems/O3DE/GeomNodes/Code/Source/Editor/UI/PropertyFileSelect.h
  83. 243 0
      Gems/O3DE/GeomNodes/Code/Source/Editor/UI/PropertyFuncValBrowseEdit.cpp
  84. 103 0
      Gems/O3DE/GeomNodes/Code/Source/Editor/UI/PropertyFuncValBrowseEdit.h
  85. 79 0
      Gems/O3DE/GeomNodes/Code/Source/Editor/UI/SettingsWidget.cpp
  86. 51 0
      Gems/O3DE/GeomNodes/Code/Source/Editor/UI/SettingsWidget.h
  87. 31 0
      Gems/O3DE/GeomNodes/Code/Source/Editor/UI/UI_common.h
  88. 147 0
      Gems/O3DE/GeomNodes/Code/Source/Editor/UI/Utils.cpp
  89. 28 0
      Gems/O3DE/GeomNodes/Code/Source/Editor/UI/Utils.h
  90. 33 0
      Gems/O3DE/GeomNodes/Code/Source/Editor/UI/ValidationHandler.cpp
  91. 28 0
      Gems/O3DE/GeomNodes/Code/Source/Editor/UI/ValidationHandler.h
  92. 143 0
      Gems/O3DE/GeomNodes/Code/Source/Editor/UI/Validators.cpp
  93. 32 0
      Gems/O3DE/GeomNodes/Code/Source/Editor/UI/Validators.h
  94. 79 0
      Gems/O3DE/GeomNodes/Code/Source/GeomNodes/Components/GeomNodesSystemComponent.cpp
  95. 54 0
      Gems/O3DE/GeomNodes/Code/Source/GeomNodes/Components/GeomNodesSystemComponent.h
  96. 22 0
      Gems/O3DE/GeomNodes/Code/Source/GeomNodes/Modules/GeomNodesModule.cpp
  97. 45 0
      Gems/O3DE/GeomNodes/Code/Source/GeomNodesModuleInterface.h
  98. 11 0
      Gems/O3DE/GeomNodes/Code/Tests/Clients/GeomNodesTest.cpp
  99. 11 0
      Gems/O3DE/GeomNodes/Code/Tests/Tools/GeomNodesEditorTest.cpp
  100. 12 0
      Gems/O3DE/GeomNodes/Code/geomnodes_api_files.cmake

+ 61 - 0
.clang-format

@@ -0,0 +1,61 @@
+Language: Cpp
+
+AccessModifierOffset: -4
+AlignAfterOpenBracket: AlwaysBreak
+AlignConsecutiveAssignments: false
+AlignConsecutiveDeclarations: false
+AlignEscapedNewlines: Right
+AlignOperands: false
+AlignTrailingComments: false
+AllowAllArgumentsOnNextLine: true
+AllowAllParametersOfDeclarationOnNextLine: true
+AllowShortFunctionsOnASingleLine: None
+AllowShortLambdasOnASingleLine: None
+AlwaysBreakAfterReturnType: None
+AlwaysBreakTemplateDeclarations: true
+BinPackArguments: false
+BinPackParameters: false
+BreakBeforeBraces: Custom
+BraceWrapping:
+    AfterClass: true
+    AfterControlStatement: true
+    AfterEnum: true
+    AfterFunction: true
+    AfterNamespace: true
+    BeforeLambdaBody: true
+    AfterStruct: true
+    BeforeElse: true
+    SplitEmptyFunction: true
+BreakBeforeTernaryOperators: true
+BreakConstructorInitializers: BeforeComma
+BreakInheritanceList: BeforeComma
+ColumnLimit: 140
+ConstructorInitializerIndentWidth: 4
+ContinuationIndentWidth: 4
+Cpp11BracedListStyle: false
+FixNamespaceComments: true
+IncludeBlocks: Preserve
+IndentCaseBlocks: true
+IndentCaseLabels: false
+IndentPPDirectives: None
+IndentWidth: 4
+KeepEmptyLinesAtTheStartOfBlocks: false
+MaxEmptyLinesToKeep: 1
+NamespaceIndentation: All
+PenaltyReturnTypeOnItsOwnLine: 1000
+PointerAlignment: Left
+SortIncludes: true
+SpaceAfterLogicalNot: false
+SpaceAfterTemplateKeyword: false
+SpaceBeforeAssignmentOperators: true
+SpaceBeforeCpp11BracedList: false
+SpaceBeforeCtorInitializerColon: true
+SpaceBeforeInheritanceColon: true
+SpaceBeforeParens: ControlStatements
+SpaceBeforeRangeBasedForLoopColon: true
+SpaceInEmptyParentheses: false
+SpacesInAngles: false
+SpacesInCStyleCastParentheses: false
+SpacesInParentheses: false
+Standard: c++17
+UseTab: Never

+ 6 - 0
Gems/O3DE/GeomNodes/3rdParty/FindBridge.cmake

@@ -0,0 +1,6 @@
+# ly_add_external_target(
+#     NAME Bridge
+#     VERSION
+#     3RDPARTY_ROOT_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/../External/Bridge
+#     INCLUDE_DIRECTORIES .
+# )

+ 5 - 0
Gems/O3DE/GeomNodes/3rdParty/Platform/Windows/bridge_windows.cmake

@@ -0,0 +1,5 @@
+set(BRLIB_LIBS ${BASE_PATH}/lib/Bridge.lib)
+
+set(BRLIB_RUNTIME_DEPENDENCIES 
+    ${BASE_PATH}/bin/Bridge.dll
+)

+ 30 - 0
Gems/O3DE/GeomNodes/CMakeLists.txt

@@ -0,0 +1,30 @@
+#
+# 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
+#
+#
+
+# Query the gem name from the gem.json file if possible
+# otherwise fallback to using GeomNodes
+o3de_find_ancestor_gem_root(gem_path gem_name "${CMAKE_CURRENT_SOURCE_DIR}")
+if (NOT gem_name)
+    set(gem_name "GeomNodes")
+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(External)
+add_subdirectory(Code)

+ 227 - 0
Gems/O3DE/GeomNodes/Code/CMakeLists.txt

@@ -0,0 +1,227 @@
+#
+# 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
+#
+#
+
+# 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/GeomNodes/Code/Platform/<platorm_name>  or
+#            <restricted_folder>/<platform_name>/Gems/GeomNodes/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_GEOMNODES_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
+        geomnodes_api_files.cmake
+        ${pal_dir}/geomnodes_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
+        geomnodes_private_files.cmake
+        ${pal_dir}/geomnodes_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
+        geomnodes_shared_files.cmake
+        ${pal_dir}/geomnodes_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})
+
+# 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)
+
+# 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
+            geomnodes_editor_api_files.cmake
+            ${pal_dir}/geomnodes_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
+        AUTOMOC
+        AUTOUIC
+        FILES_CMAKE
+            geomnodes_editor_private_files.cmake
+            ../External/Bridge/bridge_files.cmake
+        TARGET_PROPERTIES
+            O3DE_PRIVATE_TARGET TRUE
+        INCLUDE_DIRECTORIES
+            PRIVATE
+                Include
+                Source
+                ../External/Bridge
+        # COMPILE_DEFINITIONS
+        #     PRIVATE
+        #         DEBUG_SCRIPT_USING_ID=123456
+        BUILD_DEPENDENCIES
+            PUBLIC
+                AZ::AzToolsFramework
+                $<TARGET_OBJECTS:Gem::${gem_name}.Private.Object>
+                Gem::Atom_RPI.Public
+                Gem::Atom_Feature_Common.Static
+                Gem::AtomLyIntegration_CommonFeatures.Static
+    )
+
+    ly_add_target(
+        NAME ${gem_name}.Editor GEM_MODULE
+        NAMESPACE Gem
+        AUTOMOC
+        FILES_CMAKE
+            geomnodes_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_GEOMNODES_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
+                geomnodes_tests_files.cmake
+            INCLUDE_DIRECTORIES
+                PRIVATE
+                    Tests
+                    Source
+                    Include
+            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_GEOMNODES_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
+                    geomnodes_editor_tests_files.cmake
+                INCLUDE_DIRECTORIES
+                    PRIVATE
+                        Tests
+                        Source
+                        Include
+                BUILD_DEPENDENCIES
+                    PRIVATE
+                        AZ::AzTest
+                        Gem::${gem_name}.Editor.Private.Object
+            )
+
+            # Add ${gem_name}.Editor.Tests to googletest
+            ly_add_googletest(
+                NAME Gem::${gem_name}.Editor.Tests
+            )
+        endif()
+    endif()
+endif()

+ 39 - 0
Gems/O3DE/GeomNodes/Code/Include/GeomNodes/GeomNodesBus.h

@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#include <GeomNodes/GeomNodesTypeIds.h>
+
+#include <AzCore/EBus/EBus.h>
+#include <AzCore/Interface/Interface.h>
+
+namespace GeomNodes
+{
+    class GeomNodesRequests
+    {
+    public:
+        AZ_RTTI(GeomNodesRequests, GeomNodesRequestsTypeId);
+        virtual ~GeomNodesRequests() = default;
+        // Put your public methods here
+    };
+
+    class GeomNodesBusTraits : public AZ::EBusTraits
+    {
+    public:
+        //////////////////////////////////////////////////////////////////////////
+        // EBusTraits overrides
+        static constexpr AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
+        static constexpr AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
+        //////////////////////////////////////////////////////////////////////////
+    };
+
+    using GeomNodesRequestBus = AZ::EBus<GeomNodesRequests, GeomNodesBusTraits>;
+    using GeomNodesInterface = AZ::Interface<GeomNodesRequests>;
+
+} // namespace GeomNodes

+ 44 - 0
Gems/O3DE/GeomNodes/Code/Include/GeomNodes/GeomNodesTypeIds.h

@@ -0,0 +1,44 @@
+/*
+ * 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
+
+namespace GeomNodes
+{
+    // System Component TypeIds
+    inline constexpr const char* GeomNodesSystemComponentTypeId = "{E9264AFB-2C90-4438-B689-52AD2A9A0D67}";
+    inline constexpr const char* GeomNodesEditorSystemComponentTypeId = "{EACD840D-4AEC-44FB-A6BD-BCF091E07761}";
+
+    // Module derived classes TypeIds
+    inline constexpr const char* GeomNodesModuleInterfaceTypeId = "{835A3807-95B6-4420-948E-2790015DDA39}";
+    inline constexpr const char* GeomNodesModuleTypeId = "{0B4471BC-6720-4B1A-846E-CFA27F5C64CB}";
+    // The Editor Module by default is mutually exclusive with the Client Module
+    // so they use the Same TypeId
+    inline constexpr const char* GeomNodesEditorModuleTypeId = GeomNodesModuleTypeId;
+
+    // Interface TypeIds
+    inline constexpr const char* GeomNodesRequestsTypeId = "{A5ECC740-0242-4ED8-A4BB-DFC9B5CBAD44}";
+
+    // Component TypeIds
+    inline constexpr const char* EditorGeomNodesComponentTypeId = "{E59507EF-9EBB-4F6C-8D89-92DCA57722E5}";
+
+    // Other TypeIds
+    inline constexpr const char* GNRenderMeshTypeId = "{4E293CD2-F9E6-417C-92B7-DDAF312F46CF}";
+    inline constexpr const char* GeomNodesSystemTypeId = "{23791BF8-D9DE-4827-88D8-37DA39258570}";
+    inline constexpr const char* GNPropertyTypeId = "{71904E43-F0A1-45EA-B87F-4CC5234E1E52}";
+    inline constexpr const char* GNParamNilTypeId = "{519D98C7-054A-4047-BCEB-28DCD38CFCD4}";
+    inline constexpr const char* GNParamBooleanTypeId = "{6A05BCAB-50F7-4988-96E1-0EDB6B76C3A3}";
+    inline constexpr const char* GNParamIntTypeId = "{B2457A3D-F30C-43F9-90F0-5BFAFFFD0F59}";
+    inline constexpr const char* GNParamValueTypeId = "{4790660B-B942-4421-B942-AE27DF67BF4F}";
+    inline constexpr const char* GNParamStringTypeId = "{9296C827-0281-4DBF-AC1A-B6636BCEC716}";
+    inline constexpr const char* GNSystemInterfaceTypeId = "{83173679-DAF6-4496-BEFA-B0D252C40366}";
+    inline constexpr const char* GNPropertyGroupTypeId = "{439E8395-77B5-4BC6-94D6-5A0F51DBE9FD}";
+    inline constexpr const char* GNParamContextTypeId = "{AA9713B7-70F1-43CB-9F95-5BEC9F44F556}";
+    inline constexpr const char* GNParamDataContextTypeId = "{61ED88BA-210A-458B-A5E5-C71C05C05411}";
+    inline constexpr const char* GNConfigurationTypeId = "{828F21E8-D1C4-480E-A29B-33B618695874}";
+} // namespace GeomNodes

+ 11 - 0
Gems/O3DE/GeomNodes/Code/Platform/Android/PAL_android.cmake

@@ -0,0 +1,11 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+set(PAL_TRAIT_GEOMNODES_SUPPORTED TRUE)
+set(PAL_TRAIT_GEOMNODES_TEST_SUPPORTED FALSE)
+set(PAL_TRAIT_GEOMNODES_EDITOR_TEST_SUPPORTED FALSE)

+ 10 - 0
Gems/O3DE/GeomNodes/Code/Platform/Android/geomnodes_api_files.cmake

@@ -0,0 +1,10 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+set(FILES
+)

+ 15 - 0
Gems/O3DE/GeomNodes/Code/Platform/Android/geomnodes_private_files.cmake

@@ -0,0 +1,15 @@
+#
+# 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
+#
+#
+
+# Platform specific files for Android
+# i.e. ../Source/Android/GeomNodesAndroid.cpp
+#      ../Source/Android/GeomNodesAndroid.h
+#      ../Include/Android/GeomNodesAndroid.h
+
+set(FILES
+)

+ 15 - 0
Gems/O3DE/GeomNodes/Code/Platform/Android/geomnodes_shared_files.cmake

@@ -0,0 +1,15 @@
+#
+# 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
+#
+#
+
+# Platform specific files for Android
+# i.e. ../Source/Android/GeomNodesAndroid.cpp
+#      ../Source/Android/GeomNodesAndroid.h
+#      ../Include/Android/GeomNodesAndroid.h
+
+set(FILES
+)

+ 11 - 0
Gems/O3DE/GeomNodes/Code/Platform/Linux/PAL_linux.cmake

@@ -0,0 +1,11 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+set(PAL_TRAIT_GEOMNODES_SUPPORTED TRUE)
+set(PAL_TRAIT_GEOMNODES_TEST_SUPPORTED FALSE)
+set(PAL_TRAIT_GEOMNODES_EDITOR_TEST_SUPPORTED FALSE)

+ 10 - 0
Gems/O3DE/GeomNodes/Code/Platform/Linux/geomnodes_api_files.cmake

@@ -0,0 +1,10 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+set(FILES
+)

+ 10 - 0
Gems/O3DE/GeomNodes/Code/Platform/Linux/geomnodes_editor_api_files.cmake

@@ -0,0 +1,10 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+set(FILES
+)

+ 15 - 0
Gems/O3DE/GeomNodes/Code/Platform/Linux/geomnodes_private_files.cmake

@@ -0,0 +1,15 @@
+#
+# 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
+#
+#
+
+# Platform specific files for Linux
+# i.e. ../Source/Linux/GeomNodesLinux.cpp
+#      ../Source/Linux/GeomNodesLinux.h
+#      ../Include/Linux/GeomNodesLinux.h
+
+set(FILES
+)

+ 15 - 0
Gems/O3DE/GeomNodes/Code/Platform/Linux/geomnodes_shared_files.cmake

@@ -0,0 +1,15 @@
+#
+# 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
+#
+#
+
+# Platform specific files for Linux
+# i.e. ../Source/Linux/GeomNodesLinux.cpp
+#      ../Source/Linux/GeomNodesLinux.h
+#      ../Include/Linux/GeomNodesLinux.h
+
+set(FILES
+)

+ 11 - 0
Gems/O3DE/GeomNodes/Code/Platform/Mac/PAL_mac.cmake

@@ -0,0 +1,11 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+set(PAL_TRAIT_GEOMNODES_SUPPORTED TRUE)
+set(PAL_TRAIT_GEOMNODES_TEST_SUPPORTED FALSE)
+set(PAL_TRAIT_GEOMNODES_EDITOR_TEST_SUPPORTED FALSE)

+ 10 - 0
Gems/O3DE/GeomNodes/Code/Platform/Mac/geomnodes_api_files.cmake

@@ -0,0 +1,10 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+set(FILES
+)

+ 10 - 0
Gems/O3DE/GeomNodes/Code/Platform/Mac/geomnodes_editor_api_files.cmake

@@ -0,0 +1,10 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+set(FILES
+)

+ 15 - 0
Gems/O3DE/GeomNodes/Code/Platform/Mac/geomnodes_private_files.cmake

@@ -0,0 +1,15 @@
+#
+# 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
+#
+#
+
+# Platform specific files for Mac
+# i.e. ../Source/Mac/GeomNodesMac.cpp
+#      ../Source/Mac/GeomNodesMac.h
+#      ../Include/Mac/GeomNodesMac.h
+
+set(FILES
+)

+ 15 - 0
Gems/O3DE/GeomNodes/Code/Platform/Mac/geomnodes_shared_files.cmake

@@ -0,0 +1,15 @@
+#
+# 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
+#
+#
+
+# Platform specific files for Mac
+# i.e. ../Source/Mac/GeomNodesMac.cpp
+#      ../Source/Mac/GeomNodesMac.h
+#      ../Include/Mac/GeomNodesMac.h
+
+set(FILES
+)

+ 11 - 0
Gems/O3DE/GeomNodes/Code/Platform/Windows/PAL_windows.cmake

@@ -0,0 +1,11 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+set(PAL_TRAIT_GEOMNODES_SUPPORTED TRUE)
+set(PAL_TRAIT_GEOMNODES_TEST_SUPPORTED FALSE)
+set(PAL_TRAIT_GEOMNODES_EDITOR_TEST_SUPPORTED FALSE)

+ 10 - 0
Gems/O3DE/GeomNodes/Code/Platform/Windows/geomnodes_api_files.cmake

@@ -0,0 +1,10 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+set(FILES
+)

+ 10 - 0
Gems/O3DE/GeomNodes/Code/Platform/Windows/geomnodes_editor_api_files.cmake

@@ -0,0 +1,10 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+set(FILES
+)

+ 15 - 0
Gems/O3DE/GeomNodes/Code/Platform/Windows/geomnodes_private_files.cmake

@@ -0,0 +1,15 @@
+#
+# 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
+#
+#
+
+# Platform specific files for Windows
+# i.e. ../Source/Windows/GeomNodesWindows.cpp
+#      ../Source/Windows/GeomNodesWindows.h
+#      ../Include/Windows/GeomNodesWindows.h
+
+set(FILES
+)

+ 15 - 0
Gems/O3DE/GeomNodes/Code/Platform/Windows/geomnodes_shared_files.cmake

@@ -0,0 +1,15 @@
+#
+# 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
+#
+#
+
+# Platform specific files for Windows
+# i.e. ../Source/Windows/GeomNodesWindows.cpp
+#      ../Source/Windows/GeomNodesWindows.h
+#      ../Include/Windows/GeomNodesWindows.h
+
+set(FILES
+)

+ 11 - 0
Gems/O3DE/GeomNodes/Code/Platform/iOS/PAL_ios.cmake

@@ -0,0 +1,11 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+set(PAL_TRAIT_GEOMNODES_SUPPORTED TRUE)
+set(PAL_TRAIT_GEOMNODES_TEST_SUPPORTED FALSE)
+set(PAL_TRAIT_GEOMNODES_EDITOR_TEST_SUPPORTED FALSE)

+ 10 - 0
Gems/O3DE/GeomNodes/Code/Platform/iOS/geomnodes_api_files.cmake

@@ -0,0 +1,10 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+set(FILES
+)

+ 15 - 0
Gems/O3DE/GeomNodes/Code/Platform/iOS/geomnodes_private_files.cmake

@@ -0,0 +1,15 @@
+#
+# 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
+#
+#
+
+# Platform specific files for iOS
+# i.e. ../Source/iOS/GeomNodesiOS.cpp
+#      ../Source/iOS/GeomNodesiOS.h
+#      ../Include/iOS/GeomNodesiOS.h
+
+set(FILES
+)

+ 15 - 0
Gems/O3DE/GeomNodes/Code/Platform/iOS/geomnodes_shared_files.cmake

@@ -0,0 +1,15 @@
+#
+# 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
+#
+#
+
+# Platform specific files for iOS
+# i.e. ../Source/iOS/GeomNodesiOS.cpp
+#      ../Source/iOS/GeomNodesiOS.h
+#      ../Include/iOS/GeomNodesiOS.h
+
+set(FILES
+)

+ 72 - 0
Gems/O3DE/GeomNodes/Code/Source/Editor/Common/GNAPI.cpp

@@ -0,0 +1,72 @@
+/*
+ * 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 <Editor/Common/GNAPI.h>
+
+namespace GeomNodes
+{
+    namespace API
+    {
+        bool Init([[maybe_unused]] u64 id, [[maybe_unused]] HandlerCallback cb)
+        {
+#ifdef USE_BRIDGE
+            return ::Init(id, cb);
+#else
+            return false;
+#endif
+        }
+
+        void Uninitialize()
+        {
+#ifdef USE_BRIDGE
+            ::Uninitialize();
+#endif
+        }
+
+        void SendMsg([[maybe_unused]] const char* data, [[maybe_unused]] u64 length, [[maybe_unused]] u64 id)
+        {
+#ifdef USE_BRIDGE
+            ::SendMsg(data, length, id);
+#endif
+        }
+
+        bool ReadMsg([[maybe_unused]] char* buffer, [[maybe_unused]] u64 length)
+        {
+#ifdef USE_BRIDGE
+            return ::ReadMsg(buffer, length);
+#else
+            return false;
+#endif
+        }
+
+        bool OpenSHM([[maybe_unused]] u64 mapId)
+        {
+#ifdef USE_BRIDGE
+            return ::OpenSHM(mapId);
+#else
+            return false;
+#endif
+        }
+
+        bool ReadSHM([[maybe_unused]] u64 uId, [[maybe_unused]] void** address, [[maybe_unused]] u64* length)
+        {
+#ifdef USE_BRIDGE
+            return ::ReadSHM(uId, address, length);
+#else
+            return false;
+#endif
+        }
+
+        void ClearSHM([[maybe_unused]] u64 uId)
+        {
+#ifdef USE_BRIDGE
+            ::ClearSHM(uId);
+#endif
+        }
+    } // namespace API
+} // namespace GeomNodes

+ 41 - 0
Gems/O3DE/GeomNodes/Code/Source/Editor/Common/GNAPI.h

@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+#define USE_BRIDGE
+#ifdef USE_BRIDGE
+#include <Bridge.h>
+#else
+#ifndef SERVER_ID
+#define SERVER_ID 1
+#endif
+
+typedef unsigned long long u64;
+typedef unsigned int u32;
+
+typedef long (*HandlerCallback)(u64, const char*, u64);
+#endif
+
+namespace GeomNodes
+{
+    namespace API
+    {
+        /*!
+         * Interface for Bridge methods
+         */
+        bool Init(u64 id, HandlerCallback cb);
+        void Uninitialize();
+        void SendMsg(const char* data, u64 length, u64 id);
+        bool ReadMsg(char* buffer, u64 length);
+
+        // map related
+        bool OpenSHM(u64 mapId);
+        bool ReadSHM(u64 uId, void** address, u64* length);
+        void ClearSHM(u64 uId);
+    } // namespace API
+} // namespace GeomNodes

+ 61 - 0
Gems/O3DE/GeomNodes/Code/Source/Editor/Common/GNConstants.h

@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#include <AzCore/std/containers/vector.h>
+
+namespace GeomNodes
+{
+    //! Keys used for IPC JSON messages between clients(blender instances) and server(gem)
+    namespace Field
+    {
+        static constexpr char Initialized[] = "Initialized";
+        static constexpr char Heartbeat[] = "Heartbeat";
+        static constexpr char ObjectNames[] = "ObjectNames";
+        static constexpr char Objects[] = "Objects";
+        static constexpr char Object[] = "Object";
+        static constexpr char SHMOpen[] = "SHMOpen";
+        static constexpr char SHMClose[] = "SHMClose";
+        static constexpr char MapId[] = "MapId";
+        static constexpr char Export[] = "Export";
+        static constexpr char Error[] = "Error";
+
+        static constexpr char Params[] = "Params";
+        static constexpr char Materials[] = "Materials";
+        static constexpr char Id[] = "Id";
+        static constexpr char Name[] = "Name";
+        static constexpr char Type[] = "Type";
+        static constexpr char DefaultValue[] = "DefaultValue";
+        static constexpr char Value[] = "Value";
+        static constexpr char MinValue[] = "MinValue";
+        static constexpr char MaxValue[] = "MaxValue";
+
+        static constexpr char FBXPath[] = "FBXPath";
+    } // namespace Field
+
+    //! Attributes for mesh vertices.
+    enum class AttributeType
+    {
+        Position,
+        Normal,
+        Tangent,
+        Bitangent,
+        UV,
+        Color
+    };
+
+    static constexpr AZStd::string_view AssetsFolderPath = "assets/geomnodes/";
+    static constexpr AZStd::string_view MaterialsFolder = "materials";
+    static constexpr AZStd::string_view MaterialExtension = ".material";
+    static constexpr AZStd::string_view AzMaterialExtension = ".azmaterial";
+    static constexpr AZStd::string_view FbxExtension = ".fbx";
+    static constexpr AZStd::string_view AzModelExtension = ".azmodel";
+
+    typedef AZStd::vector<AZStd::string> StringVector;
+} // namespace GeomNodes

+ 26 - 0
Gems/O3DE/GeomNodes/Code/Source/Editor/Common/GNEvents.h

@@ -0,0 +1,26 @@
+/*
+ * 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/Event.h>
+
+namespace GeomNodes
+{
+    struct GNConfiguration;
+
+    namespace SystemEvents
+    {
+        //! Event that triggers when the GeomNodes system configuration has been changed.
+        //! When triggered the event will send the newly applied GNConfiguration object.
+        using OnConfigurationChangedEvent = AZ::Event<const GNConfiguration*>;
+
+        //! Event triggers when the GeomNodes system has completed initialization.
+        //! When triggered the event will send the GNConfiguration used to initialize the system.
+        using OnInitializedEvent = AZ::Event<const GNConfiguration*>;
+    } // namespace SystemEvents
+} // namespace GeomNodes

+ 12 - 0
Gems/O3DE/GeomNodes/Code/Source/Editor/Commons.h

@@ -0,0 +1,12 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#include <AzCore/Math/Crc.h>
+#include <AzCore/std/string/string.h>

+ 653 - 0
Gems/O3DE/GeomNodes/Code/Source/Editor/Components/EditorGeomNodesComponent.cpp

@@ -0,0 +1,653 @@
+/*
+ * 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/Utils/Utils.h>
+#include <AzToolsFramework/API/EntityCompositionRequestBus.h>
+#include <AzToolsFramework/API/ToolsApplicationAPI.h>
+#include <Editor/Components/EditorGeomNodesComponent.h>
+#include <Editor/Rendering/GNMeshController.h>
+#include <Editor/Systems/GNProperty.h>
+#include <Editor/UI/UI_common.h>
+#include <Editor/UI/Utils.h>
+#include <Editor/UI/Validators.h>
+
+#include <Atom/Feature/Mesh/MeshFeatureProcessorInterface.h>
+#include <Atom/RPI.Public/Scene.h>
+#include <AzCore/Component/NonUniformScaleBus.h>
+#include <AzCore/JSON/prettywriter.h>
+#include <AzCore/JSON/stringbuffer.h>
+#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
+
+namespace GeomNodes
+{
+    static void* blendFunctor = reinterpret_cast<void*>(&SelectBlendFromFileDialog);
+
+    EditorGeomNodesComponent::EditorGeomNodesComponent()
+    {
+    }
+
+    EditorGeomNodesComponent::~EditorGeomNodesComponent()
+    {
+    }
+
+    void EditorGeomNodesComponent::Reflect(AZ::ReflectContext* context)
+    {
+        if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
+        {
+            serializeContext->Class<EditorGeomNodesComponent, EditorComponentBase>()
+                ->Version(1)
+                ->Field("GNParamContext", &EditorGeomNodesComponent::m_paramContext)
+                ->Field("BlenderFile", &EditorGeomNodesComponent::m_blenderFile)
+                ->Field("ObjectNameList", &EditorGeomNodesComponent::m_enumValues)
+                ->Field("ObjectInfos", &EditorGeomNodesComponent::m_defaultObjectInfos)
+                ->Field("CurrentObjectInfo", &EditorGeomNodesComponent::m_currentObjectInfo)
+                ->Field("IsInitialized", &EditorGeomNodesComponent::m_initialized);
+
+            GNParamContext::Reflect(context);
+
+            AZ::EditContext* ec = serializeContext->GetEditContext();
+            if (ec)
+            {
+                ec->Class<EditorGeomNodesComponent>(
+                      "Geometry Node",
+                      "The Geometry Node component allows you to load a blend file with geometry node and tweak exposed parameters. ")
+                    ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
+                    ->Attribute(AZ::Edit::Attributes::Category, "Blender")
+                    ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c))
+                    ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("UI", 0x27ff46b0))
+                    ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Level", 0x9aeacc13))
+                    ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Layer", 0xe4db211a))
+                    ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
+                    ->DataElement(
+                        Handlers::FileSelect, &EditorGeomNodesComponent::m_blenderFile, "Blender File", "Blender file with Geometry Nodes")
+                    ->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::ValidBlenderOrEmpty))
+                    ->Attribute(Attributes::SelectFunction, blendFunctor)
+                    ->Attribute(Attributes::ValidationChange, &EditorGeomNodesComponent::OnPathChange)
+                    ->Attribute(AZ::Edit::Attributes::ReadOnly, &EditorGeomNodesComponent::GetWorkInProgress)
+                    ->DataElement(nullptr, &EditorGeomNodesComponent::m_paramContext, "Geom Nodes Parameters", "Parameter template")
+                    ->SetDynamicEditDataProvider(&EditorGeomNodesComponent::GetParamsEditData)
+                    ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
+                    ->UIElement(AZ::Edit::UIHandlers::Button, "", "Export to static mesh")
+                    ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorGeomNodesComponent::ExportToStaticMesh)
+                    ->Attribute(AZ::Edit::Attributes::ButtonText, &EditorGeomNodesComponent::ExportButtonText)
+                    ->Attribute(AZ::Edit::Attributes::Visibility, &EditorGeomNodesComponent::IsBlenderFileLoaded)
+                    ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree)
+                    ->Attribute(AZ::Edit::Attributes::ReadOnly, &EditorGeomNodesComponent::GetWorkInProgress);
+
+                ec->Class<GNParamContext>("Geom Nodes Parameter Context", "Adding exposed Geometry Nodes parameters to the entity!")
+                    ->DataElement(nullptr, &GNParamContext::m_group, "Properties", "Geometry Nodes properties")
+                    ->Attribute(AZ::Edit::Attributes::AutoExpand, true);
+
+                ec->Class<GNPropertyGroup>("Geom Nodes Property group", "This is a  property group")
+                    ->ClassElement(AZ::Edit::ClassElements::EditorData, "GNPropertyGroup's class attributes.")
+                    ->Attribute(AZ::Edit::Attributes::NameLabelOverride, &GNPropertyGroup::m_name)
+                    ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
+                    ->DataElement(nullptr, &GNPropertyGroup::m_properties, "m_properties", "Properties in this property group")
+                    ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
+                    ->DataElement(nullptr, &GNPropertyGroup::m_groups, "m_groups", "Subgroups in this property group")
+                    ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly);
+
+                ec->Class<GNProperty>("GeomNodes Property", "Base class for Geometry Nodes properties")
+                    ->ClassElement(AZ::Edit::ClassElements::EditorData, "GNPropertyGroup's class attributes.")
+                    ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly);
+
+                ec->Class<GNParamBoolean>("Geom Nodes Property (bool)", "A Geom Nodes boolean property")
+                    ->ClassElement(AZ::Edit::ClassElements::EditorData, "GNPropertyGroup's class attributes.")
+                    ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
+                    ->DataElement(AZ::Edit::UIHandlers::Default, &GNParamBoolean::m_value, "m_value", "A boolean")
+                    ->Attribute(AZ::Edit::Attributes::ReadOnly, &GNProperty::IsReadOnly)
+                    ->Attribute(AZ::Edit::Attributes::ChangeNotify, &GNProperty::OnParamChange)
+                    ->Attribute(AZ::Edit::Attributes::NameLabelOverride, &GNParamBoolean::m_name);
+
+                ec->Class<GNParamInt>("Geom Nodes Property (int)", "A Geom Nodes int property")
+                    ->ClassElement(AZ::Edit::ClassElements::EditorData, "GNPropertyGroup's class attributes.")
+                    ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
+                    ->DataElement(AZ::Edit::UIHandlers::Default, &GNParamInt::m_value, "m_value", "An int")
+                    ->Attribute(AZ::Edit::Attributes::ReadOnly, &GNProperty::IsReadOnly)
+                    ->Attribute(AZ::Edit::Attributes::ChangeNotify, &GNProperty::OnParamChange)
+                    ->Attribute(AZ::Edit::Attributes::NameLabelOverride, &GNParamInt::m_name);
+
+                ec->Class<GNParamValue>("Geom Nodes Property (double)", "A Geom Nodes double property")
+                    ->ClassElement(AZ::Edit::ClassElements::EditorData, "GNPropertyGroup's class attributes.")
+                    ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
+                    ->DataElement(AZ::Edit::UIHandlers::Default, &GNParamValue::m_value, "m_value", "A double/value")
+                    ->Attribute(AZ::Edit::Attributes::ReadOnly, &GNProperty::IsReadOnly)
+                    ->Attribute(AZ::Edit::Attributes::ChangeNotify, &GNProperty::OnParamChange)
+                    ->Attribute(AZ::Edit::Attributes::NameLabelOverride, &GNParamValue::m_name);
+
+                ec->Class<GNParamString>("Geom Nodes Property (string)", "A Geom Nodes string property")
+                    ->ClassElement(AZ::Edit::ClassElements::EditorData, "GNPropertyGroup's class attributes.")
+                    ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
+                    ->DataElement(AZ::Edit::UIHandlers::Default, &GNParamString::m_value, "m_value", "A string")
+                    ->Attribute(AZ::Edit::Attributes::ReadOnly, &GNProperty::IsReadOnly)
+                    ->Attribute(AZ::Edit::Attributes::ChangeNotify, &GNProperty::OnParamChange)
+                    ->Attribute(AZ::Edit::Attributes::NameLabelOverride, &GNParamString::m_name);
+            }
+        }
+    }
+
+    void EditorGeomNodesComponent::OnPathChange(const AZStd::string& path)
+    {
+        if (!path.empty())
+        {
+            bool bClearParams = (m_instance && !m_instance->IsSamePath(path));
+            if (bClearParams)
+            {
+                m_paramContext.m_group.Clear();
+                ClearDataElements();
+                m_initialized = false;
+                m_currentObjectInfo.clear();
+                SetWorkInProgress(true);
+            }
+
+            if (!m_instance || bClearParams || (m_instance && !m_instance->IsValid()))
+            {
+                if (m_instance)
+                    delete m_instance;
+                else
+                    SetWorkInProgress(true);
+
+                const AZ::IO::FixedMaxPath gemPath = AZ::Utils::GetGemPath("GeomNodes");
+                const AZ::IO::FixedMaxPath exePath = AZ::Utils::GetExecutableDirectory();
+                AZ::IO::FixedMaxPath bridgePath = exePath / "Bridge.dll"; // TODO: make this platform agnostic
+                if (!AZ::IO::SystemFile::Exists(bridgePath.c_str()))
+                {
+                    auto registry = AZ::SettingsRegistry::Get();
+                    AZ::IO::FixedMaxPath projectBuildPath;
+                    if (!registry->Get(projectBuildPath.Native(), AZ::SettingsRegistryMergeUtils::ProjectBuildPath))
+                    {
+                        AZ_Error("GeomNodes", false, "No project build path setting was found in the user registry folder");
+                        return;
+                    }
+
+                    bridgePath = projectBuildPath / "bin/profile/Bridge.dll"; // TODO: check if there is a way to get "bin/profile" in the
+                                                                              // registry or somewhere and not hard coded.
+                    if (!AZ::IO::SystemFile::Exists(bridgePath.c_str()))
+                    {
+                        AZ_Error("GeomNodes", false, "Can't find Bridge.dll");
+                        return;
+                    }
+
+                    bridgePath = projectBuildPath / "bin/profile";
+                }
+                else
+                {
+                    bridgePath = exePath.c_str();
+                }
+
+                AZStd::string scriptPath = AZStd::string::format(R"(%s\External\Scripts\__init__.py)", gemPath.c_str());
+
+                m_instance = new GNInstance;
+                m_instance->Init(path, scriptPath, bridgePath.c_str(), GetEntityId());
+                if (m_instance->IsValid())
+                {
+                    Ipc::IpcHandlerNotificationBus::Handler::BusConnect(GetEntityId());
+                }
+
+                m_controller->SetFileName(path);
+            }
+        }
+    }
+
+    void EditorGeomNodesComponent::OnParamChange()
+    {
+        SetWorkInProgress(true);
+
+        if (m_paramContext.m_group.m_properties.size() > 0)
+        {
+            // this checks if the user chooses another object.
+            auto gnParam = reinterpret_cast<GNParamString*>(m_paramContext.m_group.GetProperty(Field::Objects));
+            if (gnParam->m_value != m_currentObject)
+            {
+                m_currentObject = gnParam->m_value;
+                m_paramContext.m_group.Clear(); // clear the group/properties
+                CreateDataElements(m_paramContext.m_group);
+            }
+        }
+
+        if (m_instance)
+        {
+            if (!m_instance->IsValid())
+            {
+                m_instance->RestartProcess();
+            }
+
+            m_currentObjectInfo =
+                m_instance->SendParamUpdates(m_paramContext.m_group.GetGroup(m_currentObject.c_str())->GetProperties(), m_currentObject);
+        }
+
+        AZ_Printf("EditorGeomNodesComponent", "%llu: Parameter has changed", (AZ::u64)GetEntityId());
+    }
+
+    void EditorGeomNodesComponent::OnMessageReceived(const AZ::u8* content, const AZ::u64 length)
+    {
+        rapidjson::Document jsonDocument;
+        jsonDocument.Parse((const char*)content, length);
+        if (!jsonDocument.HasParseError())
+        {
+            // send back an "Alive" message to the client when it asks for a heartbeat.
+            if (jsonDocument.HasMember(Field::Heartbeat))
+            {
+                m_instance->SendHeartbeat();
+            }
+            else if (jsonDocument.HasMember(Field::Initialized))
+            {
+                if (!m_initialized)
+                {
+                    m_instance->RequestObjectParams();
+                    m_initialized = true;
+                }
+                else if (m_fromActivate)
+                {
+                    OnParamChange();
+                }
+                m_fromActivate = false;
+            }
+            else if (
+                jsonDocument.HasMember(Field::ObjectNames) && jsonDocument.HasMember(Field::Objects) &&
+                jsonDocument.HasMember(Field::Materials))
+            {
+                LoadObjects(jsonDocument[Field::ObjectNames], jsonDocument[Field::Objects]);
+                CreateDataElements(m_paramContext.m_group);
+
+                // Send message for fetching the 3D data
+                // Will just call OnParamChange since it's basically the same request
+                OnParamChange();
+
+                // Handle the materials as well
+                m_controller->LoadMaterials(jsonDocument[Field::Materials]);
+                SetWorkInProgress(false);
+            }
+            else if (jsonDocument.HasMember(Field::SHMOpen) && jsonDocument.HasMember(Field::MapId))
+            {
+                SetWorkInProgress(true);
+                AZ::u64 mapId = jsonDocument[Field::MapId].GetInt64();
+                m_controller->ReadData(mapId);
+                m_instance->CloseMap(mapId);
+                m_controller->RebuildRenderMesh();
+            }
+            else if (jsonDocument.HasMember(Field::Export) && jsonDocument.HasMember(Field::Error))
+            {
+                AZStd::string errorMsg = jsonDocument[Field::Error].GetString();
+                if (!errorMsg.empty())
+                {
+                    AZ_Warning("EditorGeomNodesComponent", false, errorMsg.c_str());
+                    SetWorkInProgress(false);
+                }
+            }
+        }
+        else
+        {
+            AZ_Warning("EditorGeomNodesComponent", false, "Message is not in JSON format!");
+        }
+    }
+
+    void EditorGeomNodesComponent::ExportToStaticMesh()
+    {
+        if (!m_workInProgress)
+        {
+            m_instance->RequestExport(
+                m_paramContext.m_group.GetGroup(m_currentObject.c_str())->GetProperties(),
+                m_currentObject,
+                m_controller->GenerateFBXPath());
+            SetWorkInProgress(true);
+        }
+    }
+
+    bool EditorGeomNodesComponent::IsBlenderFileLoaded()
+    {
+        return m_initialized;
+    }
+
+    void EditorGeomNodesComponent::SetWorkInProgress(bool flag)
+    {
+        AZ::SystemTickBus::QueueFunction(
+            [=]()
+            {
+                if (m_workInProgress != flag)
+                {
+                    m_workInProgress = flag;
+                    EBUS_EVENT(
+                        AzToolsFramework::ToolsApplicationEvents::Bus, InvalidatePropertyDisplay, AzToolsFramework::Refresh_EntireTree);
+                }
+            });
+    }
+
+    bool EditorGeomNodesComponent::GetWorkInProgress()
+    {
+        return m_workInProgress;
+    }
+
+    void EditorGeomNodesComponent::SendIPCMsg(const AZStd::string& msg)
+    {
+        if (m_instance != nullptr)
+        {
+            m_instance->SendIPCMsg(msg);
+        }
+    }
+
+    AZStd::string EditorGeomNodesComponent::ExportButtonText()
+    {
+        return m_workInProgress ? "Working on your request" : "Export";
+    }
+
+    void EditorGeomNodesComponent::LoadObjects(const rapidjson::Value& objectNameArray, const rapidjson::Value& objectArray)
+    {
+        // Populate m_enumValues that will store the list of object names
+        LoadObjectNames(objectNameArray);
+
+        // Load and save our param list object from json. Need this so it's faster to switch between objects and not need to send a request
+        // via IPC.
+        LoadParams(objectArray);
+
+        m_currentObject = m_enumValues[0];
+    }
+
+    void EditorGeomNodesComponent::LoadObjectNames(const rapidjson::Value& objectNames)
+    {
+        AZ_Assert(objectNames.IsArray(), "Passed JSON Value is not an array!");
+
+        uint32_t idx = 0;
+        m_enumValues.clear();
+        for (rapidjson::Value::ConstValueIterator itr = objectNames.Begin(); itr != objectNames.End(); ++itr, idx++)
+        {
+            m_enumValues.push_back(itr->GetString());
+        }
+
+        AZ_Assert(!m_enumValues.empty(), "No Object found! There should be at least one.");
+    }
+
+    void EditorGeomNodesComponent::LoadParams(const rapidjson::Value& objectArray)
+    {
+        for (rapidjson::Value::ConstValueIterator itr = objectArray.Begin(); itr != objectArray.End(); ++itr)
+        {
+            const char* objectName = (*itr)[Field::Object].GetString();
+
+            rapidjson::StringBuffer jsonDataBuffer;
+            rapidjson::PrettyWriter<rapidjson::StringBuffer> jsonDatawriter(jsonDataBuffer);
+            (*itr).Accept(jsonDatawriter);
+
+            m_defaultObjectInfos.insert(AZStd::make_pair(objectName, jsonDataBuffer.GetString()));
+        }
+    }
+
+    void EditorGeomNodesComponent::CreateDataElements(GNPropertyGroup& group)
+    {
+        ClearDataElements();
+
+        // Create the combo box that will show the object names.
+        CreateObjectNames(m_currentObject, m_enumValues, group);
+
+        // Create the currently selected Object parameters and attributes. Load only the first or saved object.
+        CreateParam(m_currentObject, group);
+
+        AZ::SystemTickBus::QueueFunction(
+            [=]()
+            {
+                EBUS_EVENT(AzToolsFramework::ToolsApplicationEvents::Bus, InvalidatePropertyDisplay, AzToolsFramework::Refresh_EntireTree);
+                AzToolsFramework::ToolsApplicationRequests::Bus::Broadcast(
+                    &AzToolsFramework::ToolsApplicationRequests::Bus::Events::AddDirtyEntity, GetEntityId());
+            });
+    }
+
+    void EditorGeomNodesComponent::CreateObjectNames(
+        const AZStd::string& objectName, const StringVector& enumValues, GNPropertyGroup& group)
+    {
+        ElementInfo ei;
+        ei.m_editData.m_name = CacheString(Field::Objects);
+        ei.m_editData.m_description = "";
+        ei.m_editData.m_elementId = AZ::Edit::UIHandlers::ComboBox;
+        ei.m_sortOrder = FLT_MAX;
+
+        auto gnParam = aznew GNParamString(Field::Objects, "", &m_workInProgress, GetEntityId());
+        gnParam->m_value = objectName;
+
+        ei.m_editData.m_attributes.push_back(
+            AZ::Edit::AttributePair(AZ::Edit::Attributes::StringList, aznew AZ::AttributeContainerType<StringVector>(enumValues)));
+
+        group.m_properties.emplace_back(gnParam);
+
+        AddDataElement(gnParam, ei);
+    }
+
+    void EditorGeomNodesComponent::CreateParam(const AZStd::string& objectName, GNPropertyGroup& group)
+    {
+        AZStd::string jsonBuffer;
+
+        if (m_currentObjectInfo.empty())
+        {
+            auto it = m_defaultObjectInfos.find(objectName);
+            if (it != m_defaultObjectInfos.end())
+            {
+                jsonBuffer = it->second;
+            }
+        }
+        else
+        {
+            jsonBuffer = m_currentObjectInfo;
+        }
+
+        if (!jsonBuffer.empty())
+        {
+            rapidjson::Document jsonDocument;
+            jsonDocument.Parse(jsonBuffer.c_str(), jsonBuffer.size());
+            if (!jsonDocument.HasParseError())
+            {
+                GNPropertyGroup* subGroup = group.GetGroup(objectName.c_str());
+                if (subGroup == nullptr)
+                {
+                    group.m_groups.emplace_back();
+                    subGroup = &group.m_groups.back();
+                    subGroup->m_name = objectName;
+                }
+                LoadProperties(jsonDocument[Field::Params], *subGroup);
+            }
+        }
+    }
+
+    bool EditorGeomNodesComponent::LoadProperties(const rapidjson::Value& paramVal, GNPropertyGroup& group)
+    {
+        // parse params
+        for (rapidjson::Value::ConstValueIterator itr = paramVal.Begin(); itr != paramVal.End(); ++itr)
+        {
+            // set this up so the context can do it's own parsing of the current GN param JSON object.
+            GNParamDataContext gndc;
+            gndc.SetParamObject(itr);
+            gndc.SetReadOnlyPointer(&m_workInProgress);
+            gndc.SetEntityId(GetEntityId());
+            auto propertyName = gndc.GetParamName();
+            auto paramType = gndc.GetParamType();
+
+            // default value will differ based on the type
+
+            if (GNProperty* gnParam = m_paramContext.ConstructGNParam(gndc, paramType, propertyName))
+            {
+                group.m_properties.emplace_back(gnParam);
+
+                ElementInfo ei;
+                ei.m_editData.m_name = CacheString(propertyName);
+                ei.m_editData.m_description = "";
+                ei.m_editData.m_elementId = AZ::Edit::UIHandlers::Default;
+                ei.m_sortOrder = FLT_MAX;
+
+                // Load any attributes
+                LoadAttribute(paramType, ei.m_editData, group.m_properties.back());
+
+                AddDataElement(gnParam, ei);
+            }
+            else
+            {
+                AZ_Warning("GeomNodes", false, "We support only boolean, number, and string as properties %s!", propertyName);
+            }
+        }
+
+        return true;
+    }
+
+    void EditorGeomNodesComponent::LoadAttribute(ParamType type, AZ::Edit::ElementData& ed, GNProperty* prop)
+    {
+        switch (type)
+        {
+        case ParamType::Int:
+            if (prop->m_isMinSet)
+            {
+                int value = ((GNParamInt*)prop)->m_min;
+                ed.m_attributes.push_back(AZ::Edit::AttributePair(AZ::Crc32("min"), aznew AZ::Edit::AttributeData<int>(value)));
+            }
+
+            if (prop->m_isMaxSet)
+            {
+                int value = ((GNParamInt*)prop)->m_max;
+                ed.m_attributes.push_back(AZ::Edit::AttributePair(AZ::Crc32("max"), aznew AZ::Edit::AttributeData<int>(value)));
+            }
+            break;
+        case ParamType::Value:
+            if (prop->m_isMinSet)
+            {
+                double value = ((GNParamValue*)prop)->m_min;
+                ed.m_attributes.push_back(AZ::Edit::AttributePair(AZ::Crc32("min"), aznew AZ::Edit::AttributeData<double>(value)));
+            }
+
+            if (prop->m_isMaxSet)
+            {
+                double value = ((GNParamValue*)prop)->m_max;
+                ed.m_attributes.push_back(AZ::Edit::AttributePair(AZ::Crc32("max"), aznew AZ::Edit::AttributeData<double>(value)));
+            }
+
+            ed.m_attributes.push_back(AZ::Edit::AttributePair(AZ::Edit::Attributes::Step, aznew AZ::Edit::AttributeData<double>(0.1f)));
+
+            break;
+        }
+    }
+
+    void EditorGeomNodesComponent::GetRequiredServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& required)
+    {
+    }
+
+    void EditorGeomNodesComponent::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent)
+    {
+        dependent.push_back(AZ_CRC("TransformService", 0x8ee22c50));
+    }
+
+    void EditorGeomNodesComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
+    {
+        provided.push_back(AZ_CRC_CE("EditorGeomNodesService"));
+    }
+
+    void EditorGeomNodesComponent::GetIncompatibleServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& incompatible)
+    {
+        incompatible.push_back(AZ_CRC("EditorGeomNodesService"));
+    }
+
+    void EditorGeomNodesComponent::Init()
+    {
+    }
+
+    void EditorGeomNodesComponent::Activate()
+    {
+        AzToolsFramework::Components::EditorComponentBase::Activate();
+        EditorGeomNodesComponentRequestBus::Handler::BusConnect(GetEntityId());
+
+        m_controller = AZStd::make_unique<GNMeshController>(GetEntityId());
+
+        m_fromActivate = true;
+
+        OnPathChange(m_blenderFile);
+    }
+
+    void EditorGeomNodesComponent::Deactivate()
+    {
+        // BUG: this gets called when a component is added so deal with it properly as it destroys any current instance we have.
+        Clear();
+        AzToolsFramework::Components::EditorComponentBase::Deactivate();
+
+        if (m_instance)
+        {
+            delete m_instance;
+            m_instance = nullptr;
+        }
+    }
+
+    void EditorGeomNodesComponent::Clear()
+    {
+        m_controller.reset();
+        m_enumValues.clear();
+        ClearDataElements();
+        EditorGeomNodesComponentRequestBus::Handler::BusDisconnect();
+        Ipc::IpcHandlerNotificationBus::Handler::BusDisconnect(GetEntityId());
+    }
+
+    const AZ::Edit::ElementData* EditorGeomNodesComponent::GetParamsEditData(
+        const void* handlerPtr, const void* elementPtr, const AZ::Uuid& elementType)
+    {
+        const EditorGeomNodesComponent* owner = reinterpret_cast<const EditorGeomNodesComponent*>(handlerPtr);
+        return owner->GetDataElement(elementPtr, elementType);
+    }
+
+    void EditorGeomNodesComponent::AddDataElement(GNProperty* gnParam, ElementInfo& ei)
+    {
+        // add the attributes to the map of default values
+        ei.m_uuid = gnParam->GetDataTypeUuid();
+        ei.m_isAttributeOwner = true;
+        m_dataElements.insert(AZStd::make_pair(gnParam->GetDataAddress(), ei));
+
+        // Also register to the script property itself, so friendly data can be displayed at its own level.
+        ei.m_uuid = azrtti_typeid(gnParam);
+        ei.m_isAttributeOwner = false;
+        m_dataElements.insert(AZStd::make_pair(gnParam, ei));
+    }
+
+    const char* EditorGeomNodesComponent::CacheString(const char* str)
+    {
+        if (str == nullptr)
+        {
+            return nullptr;
+        }
+
+        return m_cachedStrings.insert(AZStd::make_pair(str, AZStd::string(str))).first->second.c_str();
+    }
+
+    void EditorGeomNodesComponent::ClearDataElements()
+    {
+        for (auto it = m_dataElements.begin(); it != m_dataElements.end(); ++it)
+        {
+            if (it->second.m_isAttributeOwner)
+            {
+                it->second.m_editData.ClearAttributes();
+            }
+        }
+
+        m_dataElements.clear();
+
+        // The display tree might still be holding onto pointers to our attributes that we just cleared above, so force a refresh to remove
+        // them. However, only force the refresh if we have a valid entity.  If we don't have an entity, this component isn't currently
+        // being shown or edited, so a refresh is at best superfluous, and at worst could cause a feedback loop of infinite refreshes.
+        if (GetEntity())
+        {
+            AZ::SystemTickBus::QueueFunction(
+                [=]()
+                {
+                    AzToolsFramework::ToolsApplicationEvents::Bus::Broadcast(
+                        &AzToolsFramework::ToolsApplicationEvents::InvalidatePropertyDisplay, AzToolsFramework::Refresh_EntireTree);
+                });
+        }
+    }
+
+    const AZ::Edit::ElementData* EditorGeomNodesComponent::GetDataElement(const void* element, const AZ::Uuid& typeUuid) const
+    {
+        auto it = m_dataElements.find(element);
+        if (it != m_dataElements.end())
+        {
+            if (it->second.m_uuid == typeUuid)
+            {
+                return &it->second.m_editData;
+            }
+        }
+        return nullptr;
+    }
+} // namespace GeomNodes

+ 124 - 0
Gems/O3DE/GeomNodes/Code/Source/Editor/Components/EditorGeomNodesComponent.h

@@ -0,0 +1,124 @@
+/*
+ * 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 "Editor/Rendering/GNModelData.h"
+#include "Editor/Systems/GNInstance.h"
+#include "Editor/Systems/GNParamContext.h"
+#include <AzCore/Serialization/EditContext.h>
+#include <AzToolsFramework/Entity/EntityTypes.h>
+#include <AzToolsFramework/ToolsComponents/EditorComponentBase.h>
+#include <Editor/Common/GNConstants.h>
+#include <Editor/EBus/EditorGeomNodesComponentBus.h>
+#include <Editor/EBus/IpcHandlerBus.h>
+
+namespace GeomNodes
+{
+    class GNMeshController;
+    class EditorGeomNodesComponent
+        : public AzToolsFramework::Components::EditorComponentBase
+        , private Ipc::IpcHandlerNotificationBus::Handler
+        , private EditorGeomNodesComponentRequestBus::Handler
+    {
+    public:
+        AZ_EDITOR_COMPONENT(EditorGeomNodesComponent, EditorGeomNodesComponentTypeId, EditorComponentBase);
+
+        static void Reflect(AZ::ReflectContext* context);
+
+        static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required);
+        static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent);
+        static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided);
+        static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible);
+
+        EditorGeomNodesComponent();
+        virtual ~EditorGeomNodesComponent();
+
+        void Init() override;
+        void Activate() override;
+        void Deactivate() override;
+
+        // EditorGeomNodesComponentRequestBus overrides ...
+        void SetWorkInProgress(bool flag) override;
+        bool GetWorkInProgress() override;
+        void SendIPCMsg(const AZStd::string& msg) override;
+        void OnParamChange() override;
+
+    private:
+    protected:
+        //! got this from ScriptEditorComponent
+        struct ElementInfo
+        {
+            AZ::Uuid m_uuid; //! Type uuid for the class field that should use this edit data.
+            AZ::Edit::ElementData m_editData; //! Edit metadata (name, description, attribs, etc).
+            bool m_isAttributeOwner; //! True if this ElementInfo owns the internal attributes. We can use a single
+                                     //! ElementInfo for more than one class field, but only one owns the Attributes.
+            float m_sortOrder; //! Sort order of the property as defined by using the "order" attribute, by default the order is FLT_MAX
+                               //! which means alphabetical sort will be used
+        };
+
+        // IpcHandlerNotificationBus overrides...
+        void OnMessageReceived(const AZ::u8* content, const AZ::u64 length) override;
+
+        void Clear();
+        void OnPathChange(const AZStd::string& path);
+        void ExportToStaticMesh();
+        bool IsBlenderFileLoaded();
+        AZStd::string ExportButtonText();
+
+        void LoadObjects(const rapidjson::Value& objectNameArray, const rapidjson::Value& objectArray);
+        void LoadObjectNames(const rapidjson::Value& objectNames);
+        void LoadParams(const rapidjson::Value& objectArray);
+        void CreateDataElements(GNPropertyGroup& group);
+        void CreateObjectNames(const AZStd::string& objectName, const StringVector& enumValues, GNPropertyGroup& group);
+        void CreateParam(const AZStd::string& objectName, GNPropertyGroup& group);
+        bool LoadProperties(const rapidjson::Value& paramVal, GNPropertyGroup& group);
+        void LoadAttribute(ParamType type, AZ::Edit::ElementData& ed, GNProperty* prop);
+
+        void ClearDataElements();
+
+        const AZ::Edit::ElementData* GetDataElement(const void* element, const AZ::Uuid& typeUuid) const;
+
+        static const AZ::Edit::ElementData* GetParamsEditData(const void* handlerPtr, const void* elementPtr, const AZ::Uuid& elementType);
+
+        void AddDataElement(GNProperty* gnParam, ElementInfo& ei);
+
+        const char* CacheString(const char* str);
+
+        AZStd::unordered_map<const void*, AZStd::string> m_cachedStrings;
+        AZStd::unordered_map<const void*, ElementInfo> m_dataElements;
+        AZStd::unordered_map<AZStd::string, AZStd::string> m_defaultObjectInfos;
+
+        StringVector m_enumValues;
+
+        //! Handles the dynamic parameter
+        GNParamContext m_paramContext;
+
+        //! Stores the model data
+        GNModelData m_modelData;
+        //! A reference to the mesh controller.
+        AZStd::unique_ptr<GNMeshController> m_controller;
+
+        //! Current blender file loaded.
+        AZStd::string m_blenderFile;
+        //! The current object name selected.
+        AZStd::string m_currentObject;
+        //! in JSON form. This is the representation of the current selected object along with the current parameters.
+        AZStd::string m_currentObjectInfo;
+
+        //! a handle on the blender instance.
+        GNInstance* m_instance = nullptr;
+
+        //! Flag if the component is done initializing.
+        bool m_initialized = false;
+        //! Flag to set if parameters needs to be disabled or not.
+        bool m_workInProgress = false;
+        //! Flag if set true triggers a parameter reload coming from a saved point.
+        bool m_fromActivate = false;
+    };
+} // namespace GeomNodes

+ 122 - 0
Gems/O3DE/GeomNodes/Code/Source/Editor/Components/EditorGeomNodesSystemComponent.cpp

@@ -0,0 +1,122 @@
+/*
+ * 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 "EditorGeomNodesSystemComponent.h"
+#include <AzCore/Serialization/SerializeContext.h>
+#include <Editor/Systems/GeomNodesSystem.h>
+#include <Editor/UI/EditorWindow.h>
+
+#include <GeomNodes/GeomNodesTypeIds.h>
+
+AZ_DEFINE_BUDGET(GeomNodes);
+
+namespace GeomNodes
+{
+    void EditorGeomNodesSystemComponent::Reflect(AZ::ReflectContext* context)
+    {
+        GNConfiguration::Reflect(context);
+        if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
+        {
+            serializeContext->Class<EditorGeomNodesSystemComponent, GeomNodesSystemComponent>()->Version(0);
+        }
+    }
+
+    EditorGeomNodesSystemComponent::EditorGeomNodesSystemComponent()
+        : m_onSystemInitializedHandler(
+              []([[maybe_unused]] const GNConfiguration* config)
+              {
+
+              })
+        , m_onSystemConfigChangedHandler(
+              []([[maybe_unused]] const GNConfiguration* config)
+              {
+              })
+    {
+    }
+
+    EditorGeomNodesSystemComponent::~EditorGeomNodesSystemComponent() = default;
+
+    void EditorGeomNodesSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
+    {
+        BaseSystemComponent::GetProvidedServices(provided);
+        provided.push_back(AZ_CRC_CE("EditorGeomNodesSystemService"));
+    }
+
+    void EditorGeomNodesSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
+    {
+        BaseSystemComponent::GetIncompatibleServices(incompatible);
+        incompatible.push_back(AZ_CRC_CE("EditorGeomNodesSystemService"));
+    }
+
+    void EditorGeomNodesSystemComponent::GetRequiredServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& required)
+    {
+        BaseSystemComponent::GetRequiredServices(required);
+    }
+
+    void EditorGeomNodesSystemComponent::GetDependentServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& dependent)
+    {
+        BaseSystemComponent::GetDependentServices(dependent);
+    }
+
+    void EditorGeomNodesSystemComponent::ActivateGeomNodesSystem()
+    {
+        m_system = GetGNSystem();
+        if (m_system)
+        {
+            m_system->RegisterSystemInitializedEvent(m_onSystemInitializedHandler);
+            m_system->RegisterSystemConfigurationChangedEvent(m_onSystemConfigChangedHandler);
+            const GNSettingsRegistryManager& registryManager = m_system->GetSettingsRegistryManager();
+            if (AZStd::optional<GNConfiguration> config = registryManager.LoadSystemConfiguration(); config.has_value())
+            {
+                m_system->Initialize(&(*config));
+            }
+            else // load defaults if there is no config
+            {
+                const GNConfiguration defaultConfig = GNConfiguration::CreateDefault();
+                m_system->Initialize(&defaultConfig);
+
+                auto saveCallback =
+                    []([[maybe_unused]] const GNConfiguration& config, [[maybe_unused]] GNSettingsRegistryManager::Result result)
+                {
+                    AZ_Warning(
+                        "GeomNodes",
+                        result == GNSettingsRegistryManager::Result::Success,
+                        "Unable to save the default GeomNodes configuration.");
+                };
+                registryManager.SaveSystemConfiguration(defaultConfig, saveCallback);
+            }
+        }
+    }
+
+    void EditorGeomNodesSystemComponent::Activate()
+    {
+        GeomNodesSystemComponent::Activate();
+        AzToolsFramework::EditorEvents::Bus::Handler::BusConnect();
+        ActivateGeomNodesSystem();
+    }
+
+    void EditorGeomNodesSystemComponent::Deactivate()
+    {
+        m_onSystemInitializedHandler.Disconnect();
+        m_onSystemConfigChangedHandler.Disconnect();
+        if (m_system != nullptr)
+        {
+            m_system->Shutdown();
+            m_system = nullptr;
+        }
+
+        AzToolsFramework::EditorEvents::Bus::Handler::BusDisconnect();
+        GeomNodesSystemComponent::Deactivate();
+    }
+
+    void EditorGeomNodesSystemComponent::NotifyRegisterViews()
+    {
+        GeomNodes::Editor::EditorWindow::RegisterViewClass();
+    }
+
+} // namespace GeomNodes

+ 55 - 0
Gems/O3DE/GeomNodes/Code/Source/Editor/Components/EditorGeomNodesSystemComponent.h

@@ -0,0 +1,55 @@
+/*
+ * 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/API/ToolsApplicationAPI.h>
+
+#include <Editor/Common/GNEvents.h>
+#include <GeomNodes/Components/GeomNodesSystemComponent.h>
+
+
+namespace GeomNodes
+{
+    class GeomNodesSystem;
+
+    /// System component for GeomNodes editor
+    class EditorGeomNodesSystemComponent
+        : public GeomNodesSystemComponent
+        , private AzToolsFramework::EditorEvents::Bus::Handler
+    {
+        using BaseSystemComponent = GeomNodesSystemComponent;
+
+    public:
+        AZ_COMPONENT(EditorGeomNodesSystemComponent, GeomNodesEditorSystemComponentTypeId, BaseSystemComponent);
+
+        static void Reflect(AZ::ReflectContext* context);
+
+        EditorGeomNodesSystemComponent();
+        ~EditorGeomNodesSystemComponent();
+
+    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);
+
+        void ActivateGeomNodesSystem();
+
+        // AZ::Component
+        void Activate() override;
+        void Deactivate() override;
+
+        // AztoolsFramework::EditorEvents overrides...
+        void NotifyRegisterViews() override;
+
+        GeomNodesSystem* m_system = nullptr;
+        SystemEvents::OnInitializedEvent::Handler m_onSystemInitializedHandler;
+        SystemEvents::OnConfigurationChangedEvent::Handler m_onSystemConfigChangedHandler;
+    };
+} // namespace GeomNodes

+ 59 - 0
Gems/O3DE/GeomNodes/Code/Source/Editor/Configuration/GNConfiguration.cpp

@@ -0,0 +1,59 @@
+/*
+ * 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 <Editor/Configuration/GNConfiguration.h>
+
+#include <AzCore/Memory/SystemAllocator.h>
+#include <AzCore/Serialization/EditContext.h>
+#include <AzCore/Serialization/SerializeContext.h>
+
+namespace GeomNodes
+{
+    AZ_CLASS_ALLOCATOR_IMPL(GNConfiguration, AZ::SystemAllocator);
+
+    void GNConfiguration::Reflect(AZ::ReflectContext* context)
+    {
+        if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
+        {
+            serializeContext->Class<GNConfiguration>()
+                ->Version(2)
+                ->Field("Blender Path", &GNConfiguration::m_blenderPath)
+                ->Field("Last Selected Path", &GNConfiguration::m_lastFilePath);
+
+            if (auto* editContext = serializeContext->GetEditContext())
+            {
+                editContext->Class<GNConfiguration>("GeomNodes Configuration", "Default GeomNodes configuration")
+                    ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
+                    ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
+                    ->DataElement(
+                        AZ::Edit::UIHandlers::ExeSelectBrowseEdit, &GNConfiguration::m_blenderPath, "Blender Path", "Blender Path")
+                    ->Attribute(AZ::Edit::Attributes::ChangeNotify, &GNConfiguration::OnBlenderPathChanged);
+            }
+        }
+    }
+
+    GNConfiguration GNConfiguration::CreateDefault()
+    {
+        return GNConfiguration();
+    }
+
+    bool GNConfiguration::operator==(const GNConfiguration& other) const
+    {
+        return m_blenderPath == other.m_blenderPath && m_lastFilePath == other.m_lastFilePath;
+    }
+
+    bool GNConfiguration::operator!=(const GNConfiguration& other) const
+    {
+        return !(*this == other);
+    }
+
+    AZ::u32 GNConfiguration::OnBlenderPathChanged()
+    {
+        return AZ::Edit::PropertyRefreshLevels::AttributesAndValues;
+    }
+} // namespace GeomNodes

+ 43 - 0
Gems/O3DE/GeomNodes/Code/Source/Editor/Configuration/GNConfiguration.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/Memory/Memory.h>
+#include <AzCore/std/string/string.h>
+#include <GeomNodes/GeomNodesTypeIds.h>
+
+
+namespace AZ
+{
+    class ReflectContext;
+}
+
+namespace GeomNodes
+{
+    //! Configuration object that contains global data for GeomNodes System
+    struct GNConfiguration
+    {
+        AZ_CLASS_ALLOCATOR_DECL;
+        AZ_TYPE_INFO(GNConfiguration, GNConfigurationTypeId);
+        static void Reflect(AZ::ReflectContext* context);
+
+        static GNConfiguration CreateDefault();
+
+        //! Blender executable path in user's machine.
+        AZStd::string m_blenderPath;
+        //! Last file path used when selecting a blender file.
+        AZStd::string m_lastFilePath;
+
+        bool operator==(const GNConfiguration& other) const;
+        bool operator!=(const GNConfiguration& other) const;
+
+    private:
+        AZ::u32 OnBlenderPathChanged();
+    };
+} // namespace GeomNodes

+ 146 - 0
Gems/O3DE/GeomNodes/Code/Source/Editor/Configuration/GNEditorSettingsRegistryManager.cpp

@@ -0,0 +1,146 @@
+/*
+ * 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 <Editor/Configuration/GNEditorSettingsRegistryManager.h>
+
+#include <AzCore/IO/ByteContainerStream.h>
+#include <AzCore/IO/SystemFile.h>
+#include <AzCore/IO/TextStreamWriters.h>
+#include <AzCore/JSON/document.h>
+#include <AzCore/JSON/pointer.h>
+#include <AzCore/JSON/prettywriter.h>
+#include <AzCore/Serialization/Json/JsonSerialization.h>
+#include <AzCore/Settings/SettingsRegistry.h>
+#include <AzCore/Utils/Utils.h>
+#include <AzToolsFramework/SourceControl/SourceControlAPI.h>
+
+
+namespace GeomNodes
+{
+    namespace Internal
+    {
+        AZStd::string WriteDocumentToString(const rapidjson::Document& document)
+        {
+            AZStd::string stringBuffer;
+            AZ::IO::ByteContainerStream stringStream(&stringBuffer);
+            AZ::IO::RapidJSONStreamWriter stringWriter(&stringStream);
+            rapidjson::PrettyWriter writer(stringWriter);
+            document.Accept(writer);
+            return stringBuffer;
+        }
+
+        // Capture the  GeomNodes Configuration by value to allow the the save to occur successfully
+        // even if the SystemComponent is deleted later
+        AzToolsFramework::SourceControlResponseCallback GetConfigurationSaveCallback(
+            AZStd::string configurationPayload, AZStd::function<void(bool)> postSaveCallback)
+        {
+            return [payloadBuffer = AZStd::move(configurationPayload),
+                    postSaveCB = AZStd::move(postSaveCallback)](bool, const AzToolsFramework::SourceControlFileInfo& info)
+            {
+                // Save GeomNodes configuration.
+                if (info.IsLockedByOther())
+                {
+                    AZ_Warning(
+                        "GeomNodesEditor", false, R"(The file "%s" already exclusively opened by another user)", info.m_filePath.c_str());
+                    return;
+                }
+                else if (info.IsReadOnly() && AZ::IO::SystemFile::Exists(info.m_filePath.c_str()))
+                {
+                    AZ_Warning("GeomNodesEditor", false, R"(The file "%s" is read-only)", info.m_filePath.c_str());
+                    return;
+                }
+
+                bool saved = false;
+                constexpr auto configurationMode =
+                    AZ::IO::SystemFile::SF_OPEN_CREATE | AZ::IO::SystemFile::SF_OPEN_CREATE_PATH | AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY;
+                if (AZ::IO::SystemFile outputFile; outputFile.Open(info.m_filePath.c_str(), configurationMode))
+                {
+                    saved = outputFile.Write(payloadBuffer.data(), payloadBuffer.size()) == payloadBuffer.size();
+                }
+
+                AZ_Warning("GeomNodesEditor", saved, "Failed to save GeomNodes configuration");
+                if (postSaveCB)
+                {
+                    postSaveCB(saved);
+                }
+            };
+        }
+    } // namespace Internal
+
+    GNEditorSettingsRegistryManager::GNEditorSettingsRegistryManager()
+        : GNSettingsRegistryManager()
+    {
+        // Resolve path to the .setreg files
+        AZ::IO::FixedMaxPath projectPath = AZ::Utils::GetProjectPath();
+        projectPath /= "Registry";
+
+        m_gnConfigurationFilePath = projectPath;
+        m_gnConfigurationFilePath /= "geomnodesconfiguration.setreg";
+        m_initialized = true;
+    }
+
+    void GNEditorSettingsRegistryManager::SaveSystemConfiguration(
+        const GNConfiguration& config, const OnGNConfigSaveComplete& saveCallback) const
+    {
+        if (!m_initialized)
+        {
+            AZ_Warning(
+                "GeomNodesSystemEditor",
+                false,
+                "Unable to save GeomNodes configurations. GeomNodes Editor Settings Registry Manager could not initialize");
+            if (saveCallback)
+            {
+                saveCallback(config, Result::Failed);
+            }
+            return;
+        }
+        // Save configuration to source folder when in edit mode.
+        // Use the SourceControl API to make sure the .setreg files
+        // are checked out from source control or are writable before attempting to save it
+        // The SourceControlCommandBus callbacks must be used as checking out a file is an asynchronous
+        // operation that doesn't complete immediately
+        bool sourceControlActive = false;
+        AzToolsFramework::SourceControlConnectionRequestBus::BroadcastResult(
+            sourceControlActive, &AzToolsFramework::SourceControlConnectionRequests::IsActive);
+        // If Source Control is active then use it to check out the file before saving
+        // otherwise query the file info and save only if the file is not read-only
+        auto SourceControlSaveCallback = [sourceControlActive](
+                                             AzToolsFramework::SourceControlCommands* sourceControlCommands,
+                                             const char* filePath,
+                                             const AzToolsFramework::SourceControlResponseCallback& configurationSaveCallback)
+        {
+            if (sourceControlActive)
+            {
+                sourceControlCommands->RequestEdit(filePath, true, configurationSaveCallback);
+            }
+            else
+            {
+                sourceControlCommands->GetFileInfo(filePath, configurationSaveCallback);
+            }
+        };
+
+        // Save GeomNodes System Configuration Settings Registry file
+        rapidjson::Document gnConfigurationDocument;
+        rapidjson::Value& gnConfigurationValue =
+            rapidjson::CreateValueByPointer(gnConfigurationDocument, rapidjson::Pointer(m_settingsRegistryPath.c_str()));
+        AZ::JsonSerialization::Store(gnConfigurationValue, gnConfigurationDocument.GetAllocator(), config);
+
+        auto postSaveCallback = [config, saveCallback](bool result)
+        {
+            if (saveCallback)
+            {
+                saveCallback(config, result ? Result::Success : Result::Failed);
+            }
+        };
+
+        AzToolsFramework::SourceControlCommandBus::Broadcast(
+            SourceControlSaveCallback,
+            m_gnConfigurationFilePath.c_str(),
+            Internal::GetConfigurationSaveCallback(Internal::WriteDocumentToString(gnConfigurationDocument), postSaveCallback));
+    }
+} // namespace GeomNodes

+ 28 - 0
Gems/O3DE/GeomNodes/Code/Source/Editor/Configuration/GNEditorSettingsRegistryManager.h

@@ -0,0 +1,28 @@
+/*
+ * 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 <Editor/Configuration/GNSettingsRegistryManager.h>
+
+namespace GeomNodes
+{
+    class GNEditorSettingsRegistryManager : public GNSettingsRegistryManager
+    {
+    public:
+        GNEditorSettingsRegistryManager();
+
+        // GNSystemSettingsRegistry ...
+        void SaveSystemConfiguration(const GNConfiguration& config, const OnGNConfigSaveComplete& saveCallback) const override;
+
+    private:
+        AZ::IO::FixedMaxPath m_gnConfigurationFilePath = "Registry/geomnodesconfiguration.setreg";
+
+        bool m_initialized = false;
+    };
+} // namespace GeomNodes

+ 55 - 0
Gems/O3DE/GeomNodes/Code/Source/Editor/Configuration/GNSettingsRegistryManager.cpp

@@ -0,0 +1,55 @@
+/*
+ * 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 <Editor/Configuration/GNSettingsRegistryManager.h>
+
+#include <AzCore/Settings/SettingsRegistry.h>
+#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
+
+namespace GeomNodes
+{
+    GNSettingsRegistryManager::GNSettingsRegistryManager()
+    {
+        m_settingsRegistryPath =
+            AZStd::string::format("%s/Gems/GeomNodes/GNConfiguration", AZ::SettingsRegistryMergeUtils::OrganizationRootKey);
+    }
+
+    AZStd::optional<GNConfiguration> GNSettingsRegistryManager::LoadSystemConfiguration() const
+    {
+        GNConfiguration systemConfig;
+
+        bool configurationRead = false;
+
+        AZ::SettingsRegistryInterface* settingsRegistry = AZ::SettingsRegistry::Get();
+        if (settingsRegistry)
+        {
+            configurationRead = settingsRegistry->GetObject(systemConfig, m_settingsRegistryPath);
+        }
+
+        if (configurationRead)
+        {
+            AZ_TracePrintf(
+                "GeomNodesSystem",
+                R"(GNConfiguration was read from settings registry at pointer path)"
+                R"( "%s)"
+                "\n",
+                m_settingsRegistryPath.c_str());
+            return systemConfig;
+        }
+        return AZStd::nullopt;
+    }
+
+    void GNSettingsRegistryManager::SaveSystemConfiguration(
+        [[maybe_unused]] const GNConfiguration& config, const OnGNConfigSaveComplete& saveCallback) const
+    {
+        if (saveCallback)
+        {
+            saveCallback(config, Result::Failed);
+        }
+    }
+} // namespace GeomNodes

+ 41 - 0
Gems/O3DE/GeomNodes/Code/Source/Editor/Configuration/GNSettingsRegistryManager.h

@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+#include <AzCore/std/optional.h>
+#include <Editor/Configuration/GNConfiguration.h>
+
+namespace GeomNodes
+{
+    //! Handles loading ad saving the settings registry
+    class GNSettingsRegistryManager
+    {
+    public:
+        enum class Result : AZ::u8
+        {
+            Success,
+            Failed
+        };
+
+        using OnGNConfigSaveComplete = AZStd::function<void(const GNConfiguration&, Result)>;
+
+        GNSettingsRegistryManager();
+        virtual ~GNSettingsRegistryManager() = default;
+
+        //! Load the GeomNodes Configuration from the Settings Registry
+        //! @return Returns true if successful.
+        virtual AZStd::optional<GNConfiguration> LoadSystemConfiguration() const;
+
+        //! Save the GeomNodes Configuration from the Settings Registry
+        //! @return Returns true if successful. When not in Editor, always returns false.
+        virtual void SaveSystemConfiguration(const GNConfiguration& config, const OnGNConfigSaveComplete& saveCallback) const;
+
+    protected:
+        AZStd::string m_settingsRegistryPath;
+    };
+} // namespace GeomNodes

+ 33 - 0
Gems/O3DE/GeomNodes/Code/Source/Editor/EBus/EditorGeomNodesComponentBus.h

@@ -0,0 +1,33 @@
+/*
+ * 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/ComponentBus.h>
+
+namespace GeomNodes
+{
+    //! Bus that EditorGeomNodesComponent handles like UI and parameter changes or sending an IPC message.
+    class EditorGeomNodesComponentRequests : public AZ::ComponentBus
+    {
+    public:
+        //! Toggles the state of the component's parameters. true if disabled(working in the background); false if enabled.
+        virtual void SetWorkInProgress(bool flag) = 0;
+        //! Gets the value of work in progress variable.
+        virtual bool GetWorkInProgress() = 0;
+        //! Sends an IPC message to the script running on the blender instance.
+        virtual void SendIPCMsg(const AZStd::string& msg) = 0;
+        //! Tells the component that a parameter value has changed. Usually tied to AZ::Edit::Attributes::ChangeNotify
+        virtual void OnParamChange() = 0;
+
+    protected:
+        ~EditorGeomNodesComponentRequests() = default;
+    };
+
+    using EditorGeomNodesComponentRequestBus = AZ::EBus<EditorGeomNodesComponentRequests>;
+} // namespace GeomNodes

+ 34 - 0
Gems/O3DE/GeomNodes/Code/Source/Editor/EBus/IpcHandlerBus.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 <AzCore/EBus/EBus.h>
+
+namespace Ipc
+{
+    //! IPC related notifications
+    class IpcHandlerNotifications : public AZ::EBusTraits
+    {
+    public:
+        //////////////////////////////////////////////////////////////////////////
+        // EBusTraits overrides
+        static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
+        using BusIdType = AZ::EntityId;
+        //////////////////////////////////////////////////////////////////////////
+
+        virtual ~IpcHandlerNotifications()
+        {
+        }
+
+        //! Triggered when an IPC message is sent from the client(s) to the gem.
+        virtual void OnMessageReceived(const AZ::u8* content, const AZ::u64 length) = 0;
+    };
+
+    using IpcHandlerNotificationBus = AZ::EBus<IpcHandlerNotifications>;
+} // namespace Ipc

+ 30 - 0
Gems/O3DE/GeomNodes/Code/Source/Editor/EBus/ValidatorBus.h

@@ -0,0 +1,30 @@
+/*
+ * 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 "Editor/UI/FunctorValidator.h"
+
+#include <AzCore/EBus/EBus.h>
+
+namespace GeomNodes
+{
+    class ValidatorTraits : public AZ::EBusTraits
+    {
+    public:
+        using Bus = AZ::EBus<ValidatorTraits>;
+
+        // Bus Configuration
+        static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
+
+        virtual FunctorValidator* GetValidator(FunctorValidator::FunctorType) = 0;
+        virtual void TrackValidator(FunctorValidator*) = 0;
+    };
+
+    typedef AZ::EBus<ValidatorTraits> ValidatorBus;
+} // namespace GeomNodes

+ 121 - 0
Gems/O3DE/GeomNodes/Code/Source/Editor/Math/MathHelper.cpp

@@ -0,0 +1,121 @@
+/*
+ * 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 <Editor/Math/MathHelper.h>
+
+namespace GeomNodes
+{
+    constexpr auto SMALL_NUMBER = (1.e-8f);
+    AZ::Vector3 MathHelper::ExtractScalingFromMatrix44(AZ::Matrix4x4& m)
+    {
+        AZ::Vector3 Scale3D(0, 0, 0);
+
+        // For each row, find magnitude, and if its non-zero re-scale so its unit length.
+        const float SquareSum0 = (m.GetElement(0, 0) * m.GetElement(0, 0)) + (m.GetElement(0, 1) * m.GetElement(0, 1)) +
+            (m.GetElement(0, 2) * m.GetElement(0, 2));
+        const float SquareSum1 = (m.GetElement(1, 0) * m.GetElement(1, 0)) + (m.GetElement(1, 1) * m.GetElement(1, 1)) +
+            (m.GetElement(1, 2) * m.GetElement(1, 2));
+        const float SquareSum2 = (m.GetElement(2, 0) * m.GetElement(2, 0)) + (m.GetElement(2, 1) * m.GetElement(2, 1)) +
+            (m.GetElement(2, 2) * m.GetElement(2, 2));
+
+        if (SquareSum0 > SMALL_NUMBER)
+        {
+            float Scale0 = AZ::Sqrt(SquareSum0);
+            Scale3D.SetElement(0, Scale0);
+            float InvScale0 = 1.f / Scale0;
+            m.SetElement(0, 0, m.GetElement(0, 0) * InvScale0);
+            m.SetElement(0, 1, m.GetElement(0, 1) * InvScale0);
+            m.SetElement(0, 2, m.GetElement(0, 2) * InvScale0);
+        }
+        else
+        {
+            Scale3D.SetElement(0, 0);
+        }
+
+        if (SquareSum1 > SMALL_NUMBER)
+        {
+            float Scale1 = AZ::Sqrt(SquareSum1);
+            Scale3D.SetElement(1, Scale1);
+            float InvScale1 = 1.f / Scale1;
+
+            m.SetElement(1, 0, m.GetElement(1, 0) * InvScale1);
+            m.SetElement(1, 1, m.GetElement(1, 1) * InvScale1);
+            m.SetElement(1, 2, m.GetElement(1, 2) * InvScale1);
+        }
+        else
+        {
+            Scale3D.SetElement(1, 0);
+        }
+
+        if (SquareSum2 > SMALL_NUMBER)
+        {
+            float Scale2 = AZ::Sqrt(SquareSum2);
+            Scale3D.SetElement(2, Scale2);
+            float InvScale2 = 1.f / Scale2;
+
+            m.SetElement(2, 0, m.GetElement(2, 0) * InvScale2);
+            m.SetElement(2, 1, m.GetElement(2, 1) * InvScale2);
+            m.SetElement(2, 2, m.GetElement(2, 2) * InvScale2);
+        }
+        else
+        {
+            Scale3D.SetElement(2, 0);
+        }
+
+        return Scale3D;
+    }
+
+    AZ::Matrix4x4 MathHelper::ConvertTransformAndScaleToMat4(const AZ::Transform& transform, const AZ::Vector3& nonUniformScale)
+    {
+        const AZ::Vector3& o3deTranslation = transform.GetTranslation();
+        const AZ::Quaternion& o3deRotation = transform.GetRotation();
+        AZ::Vector3 newScale = transform.GetUniformScale() * nonUniformScale;
+
+        AZ::Matrix4x4 newTransform;
+        newTransform.SetTranslation(o3deTranslation);
+        newTransform.SetRotationPartFromQuaternion(o3deRotation);
+        newTransform.MultiplyByScale(newScale);
+
+        return newTransform;
+    }
+
+    std::size_t MathHelper::Align(std::size_t location, std::size_t align)
+    {
+        return ((location + (align - 1)) & ~(align - 1));
+    }
+
+    AZ::Vector2 MathHelper::Vec2fToVec2(const Vector2f& vec)
+    {
+        return AZ::Vector2(vec[0], vec[1]);
+    }
+
+    AZ::Vector3 MathHelper::Vec3fToVec3(const Vector3f& vec)
+    {
+        return AZ::Vector3(vec[0], vec[1], vec[2]);
+    }
+
+    AZ::Vector4 MathHelper::Vec4fToVec4(const Vector4f& vec)
+    {
+        return AZ::Vector4(vec[0], vec[1], vec[2], vec[3]);
+    }
+
+    Vector2f MathHelper::Vec2ToVec2f(const AZ::Vector2& vec)
+    {
+        return Vector2f{ vec.GetX(), vec.GetY() };
+    }
+
+    Vector3f MathHelper::Vec3ToVec3f(const AZ::Vector3& vec)
+    {
+        return Vector3f{ vec.GetX(), vec.GetY(), vec.GetZ() };
+    }
+
+    Vector4f MathHelper::Vec4ToVec4f(const AZ::Vector4& vec)
+    {
+        return Vector4f{ vec.GetX(), vec.GetY(), vec.GetZ(), vec.GetW() };
+    }
+} // namespace GeomNodes

+ 63 - 0
Gems/O3DE/GeomNodes/Code/Source/Editor/Math/MathHelper.h

@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#include <AzCore/Math/Matrix4x4.h>
+#include <AzCore/Math/Quaternion.h>
+#include <AzCore/Math/Transform.h>
+#include <AzCore/Math/Vector3.h>
+#include <AzCore/Math/Vector4.h>
+#include <AzCore/Utils/TypeHash.h>
+#include <AzCore/std/containers/vector.h>
+
+
+namespace GeomNodes
+{
+    using Vector4f = AZStd::array<float, 4>;
+    using Vector3f = AZStd::array<float, 3>;
+    using Vector2f = AZStd::array<float, 2>;
+
+    using U32Vector = AZStd::vector<AZ::u32>;
+    using S32Vector = AZStd::vector<AZ::s32>;
+    using S64Vector = AZStd::vector<AZ::s64>;
+    using Vert2Vector = AZStd::vector<Vector2f>;
+    using Vert3Vector = AZStd::vector<Vector3f>;
+    using Vert4Vector = AZStd::vector<Vector4f>;
+    using Mat4Vector = AZStd::vector<AZ::Matrix4x4>;
+
+    using UniqueKey = AZStd::tuple<AZ::s32, Vector3f, Vector2f, Vector4f>;
+
+    struct MathHelper
+    {
+        static AZ::Vector3 ExtractScalingFromMatrix44(AZ::Matrix4x4& m);
+        static AZ::Matrix4x4 ConvertTransformAndScaleToMat4(const AZ::Transform& transform, const AZ::Vector3& nonUniformScale);
+        static std::size_t Align(std::size_t location, std::size_t align);
+        static AZ::Vector2 Vec2fToVec2(const Vector2f& vec);
+        static AZ::Vector3 Vec3fToVec3(const Vector3f& vec);
+        static AZ::Vector4 Vec4fToVec4(const Vector4f& vec);
+        static Vector2f Vec2ToVec2f(const AZ::Vector2& vec);
+        static Vector3f Vec3ToVec3f(const AZ::Vector3& vec);
+        static Vector4f Vec4ToVec4f(const AZ::Vector4& vec);
+    };
+} // namespace GeomNodes
+
+namespace AZStd
+{
+    template<>
+    struct hash<GeomNodes::UniqueKey>
+    {
+        typedef GeomNodes::UniqueKey argument_type;
+        typedef AZStd::size_t result_type;
+        AZ_FORCE_INLINE size_t operator()(const GeomNodes::UniqueKey& id) const
+        {
+            AZStd::hash<AZ::u32> hasher;
+            return hasher(static_cast<AZ::u32>(AZ::TypeHash32(id)));
+        }
+    };
+} // namespace AZStd

+ 62 - 0
Gems/O3DE/GeomNodes/Code/Source/Editor/Modules/GeomNodesEditorModule.cpp

@@ -0,0 +1,62 @@
+/*
+ * 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 "Editor/Components/EditorGeomNodesComponent.h"
+#include "Editor/Components/EditorGeomNodesSystemComponent.h"
+#include "Editor/Systems/GeomNodesSystem.h"
+#include <Editor/Configuration/GNEditorSettingsRegistryManager.h>
+#include <GeomNodes/GeomNodesTypeIds.h>
+#include <GeomNodesModuleInterface.h>
+
+
+namespace GeomNodes
+{
+    class GeomNodesEditorModule : public GeomNodesModuleInterface
+    {
+    public:
+        AZ_RTTI(GeomNodesEditorModule, GeomNodesEditorModuleTypeId, GeomNodesModuleInterface);
+        AZ_CLASS_ALLOCATOR(GeomNodesEditorModule, AZ::SystemAllocator);
+
+        GeomNodesEditorModule()
+            : m_gnSystem(AZStd::make_unique<GNEditorSettingsRegistryManager>())
+        {
+            // 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(),
+                {
+                    EditorGeomNodesSystemComponent::CreateDescriptor(),
+                    EditorGeomNodesComponent::CreateDescriptor(),
+                });
+        }
+
+        virtual ~GeomNodesEditorModule()
+        {
+            m_gnSystem.Shutdown();
+        }
+
+        /**
+         * Add required SystemComponents to the SystemEntity.
+         * Non-SystemComponents should not be added here
+         */
+        AZ::ComponentTypeList GetRequiredSystemComponents() const override
+        {
+            return AZ::ComponentTypeList{
+                azrtti_typeid<EditorGeomNodesSystemComponent>(),
+                azrtti_typeid<EditorGeomNodesComponent>(),
+            };
+        }
+
+    private:
+        GeomNodesSystem m_gnSystem;
+    };
+} // namespace GeomNodes
+
+AZ_DECLARE_MODULE_CLASS(Gem_GeomNodes, GeomNodes::GeomNodesEditorModule)

+ 216 - 0
Gems/O3DE/GeomNodes/Code/Source/Editor/Rendering/Atom/GNAttributeBuffer.h

@@ -0,0 +1,216 @@
+/*
+ * 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 "GNBuffer.h"
+#include <Atom/RHI.Reflect/ShaderSemantic.h>
+#include <Atom/RPI.Reflect/Model/ModelLodAssetCreator.h>
+#include <Editor/Common/GNConstants.h>
+#include <Editor/Rendering/GNMeshData.h>
+
+
+namespace GeomNodes
+{
+    //! The number of attributes required by the mesh.
+    inline constexpr uint32_t NumAttributes = 6;
+
+    //! Trait to describe mesh vertex attribute format.
+    template<AttributeType AttributeTypeT>
+    struct AttributeTrait
+    {
+    };
+
+    //! Attribute trait specialization for vertex position attribute.
+    template<>
+    struct AttributeTrait<AttributeType::Position>
+    {
+        static constexpr const char* ShaderSemantic = "POSITION";
+        using BufferType = Vector3Buffer;
+        static constexpr AZ::RHI::Format RHIFormat = AZ::RHI::Format::R32G32B32_FLOAT;
+    };
+
+    //! Attribute trait specialization for vertex normal attribute
+    template<>
+    struct AttributeTrait<AttributeType::Normal>
+    {
+        static constexpr const char* ShaderSemantic = "NORMAL";
+        using BufferType = Vector3Buffer;
+        static constexpr AZ::RHI::Format RHIFormat = AZ::RHI::Format::R32G32B32_FLOAT;
+    };
+
+    //! Attribute trait specialization for vertex tangent attribute.
+    template<>
+    struct AttributeTrait<AttributeType::Tangent>
+    {
+        static constexpr const char* ShaderSemantic = "TANGENT";
+        using BufferType = Vector4Buffer;
+        static constexpr AZ::RHI::Format RHIFormat = AZ::RHI::Format::R32G32B32A32_FLOAT;
+    };
+
+    //! Attribute trait specialization for vertex bitangent attribute.
+    template<>
+    struct AttributeTrait<AttributeType::Bitangent>
+    {
+        static constexpr const char* ShaderSemantic = "BITANGENT";
+        using BufferType = Vector3Buffer;
+        static constexpr AZ::RHI::Format RHIFormat = AZ::RHI::Format::R32G32B32_FLOAT;
+    };
+
+    //! Attribute trait specialization for vertex uv attribute.
+    template<>
+    struct AttributeTrait<AttributeType::UV>
+    {
+        static constexpr const char* ShaderSemantic = "UV";
+        using BufferType = Vector2Buffer;
+        static constexpr AZ::RHI::Format RHIFormat = AZ::RHI::Format::R32G32_FLOAT;
+    };
+
+    //! Attribute trait specialization for vertex color attribute.
+    template<>
+    struct AttributeTrait<AttributeType::Color>
+    {
+        static constexpr const char* ShaderSemantic = "COLOR";
+        using BufferType = Vector4Buffer;
+        static constexpr AZ::RHI::Format RHIFormat = AZ::RHI::Format::R32G32B32A32_FLOAT;
+    };
+
+    //! Buffer to hold mesh vertex attribute data.
+    template<AttributeType AttributeTypeT>
+    class AttributeBuffer
+    {
+    public:
+        using Trait = AttributeTrait<AttributeTypeT>;
+
+        //! Construct a new Attribute Buffer object from the specified data.
+        template<typename VertexStreamDataType>
+        AttributeBuffer(const AZStd::vector<VertexStreamDataType>& data);
+
+        //! Retrieves the buffer asset.
+        const AZ::Data::Asset<AZ::RPI::BufferAsset>& GetBuffer() const;
+
+        //! Retrieves the buffer view descriptor.
+        const AZ::RHI::BufferViewDescriptor& GetBufferViewDescriptor() const;
+
+        //! Retrieves the buffer asset view.
+        const AZ::RPI::BufferAssetView& GetBufferAssetView() const;
+
+        //! Retrieves the attribute's shader semantic.
+        const AZ::RHI::ShaderSemantic& GetShaderSemantic() const;
+
+        //! Adds this attribute buffer to the Lod.
+        void AddLodStreamBuffer(AZ::RPI::ModelLodAssetCreator& modelLodCreator) const;
+
+        //! Adds this attribute buffer to the mesh.
+        void AddMeshStreamBuffer(AZ::RPI::ModelLodAssetCreator& modelLodCreator, const GNMeshData& meshData) const;
+
+        //! Returns true of the attribute buffer is valid, otherwise false.
+        bool IsValid() const;
+
+        //! Update the attribute buffer contents with the new data.
+        template<typename VertexStreamDataType>
+        bool UpdateData(const AZStd::vector<VertexStreamDataType>& data);
+
+    private:
+        typename Trait::BufferType m_buffer;
+        AZ::RHI::ShaderSemantic m_shaderSemantic;
+        AZ::RHI::Format m_format;
+    };
+
+    template<AttributeType AttributeTypeT>
+    template<typename VertexStreamDataType>
+    AttributeBuffer<AttributeTypeT>::AttributeBuffer(const AZStd::vector<VertexStreamDataType>& data)
+        : m_buffer(data)
+        , m_shaderSemantic(AZ::Name(Trait::ShaderSemantic))
+        , m_format(Trait::RHIFormat)
+    {
+        if (!IsValid())
+        {
+            AZ_Error("AttributeBuffer", false, "Couldn't create buffer for attribute %s", m_shaderSemantic.ToString().c_str());
+        }
+    }
+
+    template<AttributeType AttributeTypeT>
+    const AZ::Data::Asset<AZ::RPI::BufferAsset>& AttributeBuffer<AttributeTypeT>::GetBuffer() const
+    {
+        return m_buffer.GetBuffer();
+    }
+
+    template<AttributeType AttributeTypeT>
+    const AZ::RHI::BufferViewDescriptor& AttributeBuffer<AttributeTypeT>::GetBufferViewDescriptor() const
+    {
+        return m_buffer.GetBufferViewDescriptor();
+    }
+
+    template<AttributeType AttributeTypeT>
+    const AZ::RPI::BufferAssetView& AttributeBuffer<AttributeTypeT>::GetBufferAssetView() const
+    {
+        return m_buffer.GetBufferAssetView();
+    }
+
+    template<AttributeType AttributeTypeT>
+    const AZ::RHI::ShaderSemantic& AttributeBuffer<AttributeTypeT>::GetShaderSemantic() const
+    {
+        return m_shaderSemantic;
+    }
+
+    template<AttributeType AttributeTypeT>
+    void AttributeBuffer<AttributeTypeT>::AddLodStreamBuffer(AZ::RPI::ModelLodAssetCreator& modelLodCreator) const
+    {
+        modelLodCreator.AddLodStreamBuffer(GetBuffer());
+    }
+
+    template<AttributeType AttributeTypeT>
+    void AttributeBuffer<AttributeTypeT>::AddMeshStreamBuffer(
+        AZ::RPI::ModelLodAssetCreator& modelLodCreator, const GNMeshData& meshData) const
+    {
+        modelLodCreator.AddMeshStreamBuffer(
+            GetShaderSemantic(),
+            AZ::Name(),
+            { m_buffer.GetBuffer(),
+              AZ::RHI::BufferViewDescriptor::CreateTyped(
+                  meshData.GetOffset<AttributeTypeT>(), meshData.GetCount<AttributeTypeT>(), m_format) });
+    }
+
+    template<AttributeType AttributeTypeT>
+    bool AttributeBuffer<AttributeTypeT>::IsValid() const
+    {
+        return m_buffer.IsValid();
+    }
+
+    template<AttributeType AttributeTypeT>
+    template<typename VertexStreamDataType>
+    bool AttributeBuffer<AttributeTypeT>::UpdateData(const AZStd::vector<VertexStreamDataType>& data)
+    {
+        if (!m_buffer.UpdateData(data))
+        {
+            AZ_Error("AttributeBuffer", false, "Couldn't update buffer for attribute %s", m_shaderSemantic.ToString().c_str());
+            return false;
+        }
+
+        return true;
+    }
+
+    //! Attribute buffer alias for position attributes.
+    using PositionAttribute = AttributeBuffer<AttributeType::Position>;
+
+    //! Attribute buffer alias for normal attributes.
+    using NormalAttribute = AttributeBuffer<AttributeType::Normal>;
+
+    //! Attribute buffer alias for tangent attributes.
+    using TangentAttribute = AttributeBuffer<AttributeType::Tangent>;
+
+    //! Attribute buffer alias for bitangent attributes.
+    using BitangentAttribute = AttributeBuffer<AttributeType::Bitangent>;
+
+    //! Attribute buffer alias for uv attributes.
+    using UVAttribute = AttributeBuffer<AttributeType::UV>;
+
+    //! Attribute buffer alias for color attributes.
+    using ColorAttribute = AttributeBuffer<AttributeType::Color>;
+} // namespace GeomNodes

+ 196 - 0
Gems/O3DE/GeomNodes/Code/Source/Editor/Rendering/Atom/GNBuffer.h

@@ -0,0 +1,196 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#include "Editor/Math/MathHelper.h"
+
+#include <Atom/RPI.Public/Buffer/Buffer.h>
+#include <Atom/RPI.Reflect/Buffer/BufferAsset.h>
+#include <Atom/RPI.Reflect/Buffer/BufferAssetCreator.h>
+#include <Atom/RPI.Reflect/Buffer/BufferAssetView.h>
+
+namespace GeomNodes
+{
+    //! Get the Atom format for the specified vertex stream type.
+    template<typename VertexStreamDataType>
+    constexpr AZ::RHI::Format GetFormatForVertexStreamDataType()
+    {
+        if (std::is_same_v<VertexStreamDataType, uint32_t>)
+        {
+            return AZ::RHI::Format::R32_UINT;
+        }
+        else if (std::is_same_v<VertexStreamDataType, Vector2f>)
+        {
+            return AZ::RHI::Format::R32G32_FLOAT;
+        }
+        else if (std::is_same_v<VertexStreamDataType, Vector3f>)
+        {
+            return AZ::RHI::Format::R32G32B32_FLOAT;
+        }
+        else if (std::is_same_v<VertexStreamDataType, Vector4f>)
+        {
+            return AZ::RHI::Format::R32G32B32A32_FLOAT;
+        }
+        else
+        {
+            // will result in a compile time assertion failure in the Buffer class.
+            return AZ::RHI::Format::Unknown;
+        }
+    }
+
+    //! Buffer for holding vertex attribute data to be transferred to the GPU for mesh rendering.
+    template<typename VertexStreamDataType>
+    class Buffer
+    {
+    public:
+        //! Constructs the buffer from the specified data in vertex stream format.
+        Buffer(const AZStd::vector<VertexStreamDataType>& data);
+
+        //! Retrieves the buffer asset.
+        const AZ::Data::Asset<AZ::RPI::BufferAsset>& GetBuffer() const;
+
+        //! Retrieves the buffer view descriptor.
+        const AZ::RHI::BufferViewDescriptor& GetBufferViewDescriptor() const;
+
+        //! Retrieves the buffer asset view.
+        const AZ::RPI::BufferAssetView& GetBufferAssetView() const;
+
+        //! Returns true of the buffer is valid, otherwise false.
+        bool IsValid() const;
+
+        //! Update the buffer contents with the new data.
+        bool UpdateData(const AZStd::vector<VertexStreamDataType>& data);
+
+    private:
+        AZ::Data::Asset<AZ::RPI::BufferAsset> m_buffer;
+        AZ::RHI::BufferViewDescriptor m_bufferViewDescriptor;
+        AZ::RPI::BufferAssetView m_bufferAssetView;
+        bool m_isValid = false;
+
+        //! The format used by the buffer (must be one of the supported types in GetFormatForVertexStreamDataType).
+        static constexpr auto VertexStreamFormat = GetFormatForVertexStreamDataType<VertexStreamDataType>();
+        static_assert(VertexStreamFormat != AZ::RHI::Format::Unknown, "Cannot initialize a buffer with an unknown format.");
+    };
+
+    template<typename VertexStreamDataType>
+    Buffer<VertexStreamDataType>::Buffer(const AZStd::vector<VertexStreamDataType>& data)
+    {
+        const uint32_t elementCount = static_cast<uint32_t>(data.size());
+        const uint32_t elementSize = sizeof(VertexStreamDataType);
+        const uint32_t bufferSize = elementCount * elementSize;
+
+        // create a buffer view spanning the entire buffer
+        m_bufferViewDescriptor = AZ::RHI::BufferViewDescriptor::CreateTyped(0, elementCount, VertexStreamFormat);
+
+        // specify the data format for vertex stream data
+        AZ::RHI::BufferDescriptor bufferDescriptor;
+        bufferDescriptor.m_bindFlags = AZ::RHI::BufferBindFlags::InputAssembly | AZ::RHI::BufferBindFlags::ShaderRead;
+        bufferDescriptor.m_byteCount = bufferSize;
+        bufferDescriptor.m_alignment = elementSize;
+
+        // create the buffer with the specified data
+        AZ::RPI::BufferAssetCreator bufferAssetCreator;
+        bufferAssetCreator.Begin(AZ::Uuid::CreateRandom());
+        bufferAssetCreator.SetUseCommonPool(AZ::RPI::CommonBufferPoolType::StaticInputAssembly);
+        bufferAssetCreator.SetBuffer(data.data(), bufferDescriptor.m_byteCount, bufferDescriptor);
+        bufferAssetCreator.SetBufferViewDescriptor(m_bufferViewDescriptor);
+
+        if (!bufferAssetCreator.End(m_buffer))
+        {
+            AZ_Error("Buffer", false, "Couldn't create buffer asset.");
+        }
+        else if (!m_buffer.IsReady())
+        {
+            AZ_Error("Buffer", false, "Asset is not ready.");
+        }
+        else if (!m_buffer.Get())
+        {
+            AZ_Error("Buffer", false, "Asset is nullptr.");
+        }
+        else
+        {
+            m_bufferAssetView = AZ::RPI::BufferAssetView{ m_buffer, m_bufferViewDescriptor };
+            m_isValid = true;
+        }
+    }
+
+    template<typename VertexStreamDataType>
+    const AZ::Data::Asset<AZ::RPI::BufferAsset>& Buffer<VertexStreamDataType>::GetBuffer() const
+    {
+        return m_buffer;
+    }
+
+    template<typename VertexStreamDataType>
+    const AZ::RHI::BufferViewDescriptor& Buffer<VertexStreamDataType>::GetBufferViewDescriptor() const
+    {
+        return m_bufferViewDescriptor;
+    }
+
+    template<typename VertexStreamDataType>
+    const AZ::RPI::BufferAssetView& Buffer<VertexStreamDataType>::GetBufferAssetView() const
+    {
+        return m_bufferAssetView;
+    }
+
+    template<typename VertexStreamDataType>
+    bool Buffer<VertexStreamDataType>::IsValid() const
+    {
+        return m_isValid;
+        ;
+    }
+
+    template<typename VertexStreamDataType>
+    bool Buffer<VertexStreamDataType>::UpdateData(const AZStd::vector<VertexStreamDataType>& data)
+    {
+        if (!IsValid())
+        {
+            AZ_Error("UpdateData", false, "Buffer is not valid.");
+            return false;
+        }
+
+        auto buffer = AZ::RPI::Buffer::FindOrCreate(m_buffer);
+
+        if (!buffer)
+        {
+            AZ_Error("UpdateData", false, "Buffer could not be found.");
+            return false;
+        }
+
+        const uint32_t elementCount = static_cast<uint32_t>(data.size());
+        const uint32_t elementSize = sizeof(VertexStreamDataType);
+        const uint32_t bufferSize = elementCount * elementSize;
+
+        if (bufferSize > m_bufferViewDescriptor.m_elementCount * m_bufferViewDescriptor.m_elementSize)
+        {
+            AZ_Error("UpdateData", false, "Specfied buffer update exceeds capacity.");
+            return false;
+        }
+
+        if (!buffer->UpdateData(data.data(), bufferSize))
+        {
+            AZ_Error("UpdateData", false, "Buffer could not be updated.");
+            m_isValid = false;
+            return false;
+        }
+
+        return true;
+    }
+
+    //! Buffer alias for unsigned 32 bit integer indices.
+    using IndexBuffer = Buffer<uint32_t>;
+
+    //! Buffer alias for Vector2f vertices.
+    using Vector2Buffer = Buffer<Vector2f>;
+
+    //! Buffer alias for Vert3Vector vertices.
+    using Vector3Buffer = Buffer<Vector3f>;
+
+    //! Buffer alias for Vert4Vector vertices.
+    using Vector4Buffer = Buffer<Vector4f>;
+} // namespace GeomNodes

+ 300 - 0
Gems/O3DE/GeomNodes/Code/Source/Editor/Rendering/GNMeshController.cpp

@@ -0,0 +1,300 @@
+/*
+ * 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 <Editor/Common/GNConstants.h>
+#include <Editor/EBus/EditorGeomNodesComponentBus.h>
+#include <Editor/Rendering/GNMeshController.h>
+#include <Editor/Rendering/GNRenderMesh.h>
+#include <Editor/UI/Utils.h>
+
+#include <AtomLyIntegration/CommonFeatures/Material/MaterialComponentConstants.h>
+#include <AtomLyIntegration/CommonFeatures/Mesh/MeshComponentBus.h>
+#include <AtomLyIntegration/CommonFeatures/Mesh/MeshComponentConstants.h>
+#include <AzCore/Component/NonUniformScaleBus.h>
+#include <AzCore/Debug/Profiler.h>
+#include <AzCore/Utils/Utils.h>
+#include <AzCore/std/string/regex.h>
+#include <AzToolsFramework/API/EntityCompositionRequestBus.h>
+#include <AzToolsFramework/API/ToolsApplicationAPI.h>
+#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
+#include <AzToolsFramework/ToolsComponents/TransformComponent.h>
+
+namespace GeomNodes
+{
+    GNMeshController::GNMeshController(AZ::EntityId entityId)
+        : m_entityId(entityId)
+    {
+        if (!m_renderMesh)
+        {
+            m_renderMesh = AZStd::make_unique<GNRenderMesh>(m_entityId);
+        }
+
+        AZ::TransformNotificationBus::Handler::BusConnect(m_entityId);
+        AzFramework::AssetCatalogEventBus::Handler::BusConnect();
+
+        m_world = AZ::Transform::CreateIdentity();
+        AZ::TransformBus::EventResult(m_world, m_entityId, &AZ::TransformBus::Events::GetWorldTM);
+    }
+
+    GNMeshController::~GNMeshController()
+    {
+        AzFramework::AssetCatalogEventBus::Handler::BusDisconnect();
+        AzToolsFramework::EditorComponentSelectionRequestsBus::Handler::BusDisconnect();
+        AzFramework::BoundsRequestBus::Handler::BusDisconnect();
+        AZ::TransformNotificationBus::Handler::BusDisconnect();
+        m_renderMesh.reset();
+    }
+
+    AZ::Aabb GNMeshController::GetEditorSelectionBoundsViewport([[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo)
+    {
+        return GetWorldBounds();
+    }
+
+    bool GNMeshController::EditorSelectionIntersectRayViewport(
+        [[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo, const AZ::Vector3& src, const AZ::Vector3& dir, float& distance)
+    {
+        AZ_PROFILE_FUNCTION(AzToolsFramework);
+
+        if (!m_renderMesh->GetModel())
+        {
+            return false;
+        }
+
+        AZ::Transform transform = AZ::Transform::CreateIdentity();
+        AZ::TransformBus::EventResult(transform, m_entityId, &AZ::TransformBus::Events::GetWorldTM);
+
+        AZ::Vector3 nonUniformScale = AZ::Vector3::CreateOne();
+        AZ::NonUniformScaleRequestBus::EventResult(nonUniformScale, m_entityId, &AZ::NonUniformScaleRequests::GetScale);
+
+        float t;
+        AZ::Vector3 ignoreNormal;
+        constexpr float rayLength = 1000.0f;
+        if (m_renderMesh->GetModel()->RayIntersection(transform, nonUniformScale, src, dir * rayLength, t, ignoreNormal))
+        {
+            distance = rayLength * t;
+            return true;
+        }
+
+        return false;
+    }
+
+    bool GNMeshController::SupportsEditorRayIntersect()
+    {
+        return true;
+    }
+
+    AZ::Aabb GNMeshController::GetWorldBounds()
+    {
+        if (!m_worldAabb.has_value())
+        {
+            m_worldAabb = GetLocalBounds();
+            m_worldAabb->ApplyTransform(m_world);
+        }
+
+        return m_worldAabb.value();
+    }
+
+    AZ::Aabb GNMeshController::GetLocalBounds()
+    {
+        if (!m_localAabb.has_value() && m_modelData.MeshCount() > 0)
+        {
+            m_localAabb = m_modelData.GetAabb();
+        }
+
+        return m_localAabb.value();
+    }
+
+    void GNMeshController::RebuildRenderMesh()
+    {
+        if (!m_materialWaitList.empty())
+            return;
+
+        m_worldAabb.reset();
+        m_localAabb.reset();
+
+        if (m_modelData.MeshCount() > 0)
+        {
+            AZ::SystemTickBus::QueueFunction(
+                [=]()
+                {
+                    AzToolsFramework::EditorComponentSelectionRequestsBus::Handler::BusDisconnect();
+                    AzFramework::BoundsRequestBus::Handler::BusDisconnect();
+                    m_renderMesh->BuildMesh(m_modelData);
+                    m_renderMesh->UpdateTransform(m_world);
+                    AzFramework::BoundsRequestBus::Handler::BusConnect(m_entityId);
+                    AzToolsFramework::EditorComponentSelectionRequestsBus::Handler::BusConnect(m_entityId);
+                    EditorGeomNodesComponentRequestBus::Event(m_entityId, &EditorGeomNodesComponentRequests::SetWorkInProgress, false);
+                });
+        }
+    }
+
+    void GNMeshController::ReadData(AZ::u64 mapId)
+    {
+        m_modelData.ReadData(mapId);
+
+        // set the render mesh's material list here so it only get the materials for the current object and not the whole scene.
+        MaterialList materialList;
+        AZStd::string materialFilePath = AZStd::string(AssetsFolderPath) + m_blenderFilename + "/" + MaterialsFolder.data() + "/";
+        for (auto materialName : m_modelData.GetMaterials())
+        {
+            AZStd::string azMaterialPath = materialFilePath + materialName + AzMaterialExtension.data();
+            materialList.push_back(azMaterialPath);
+        }
+
+        m_renderMesh->SetMaterialPathList(materialList);
+    }
+
+    void GNMeshController::LoadMaterials(const rapidjson::Value& materialArray)
+    {
+        m_materialWaitList.clear();
+        // iterate through the material arrays and write them into files.
+        AZStd::string projectRootPath = GetProjectRoot() + "/";
+        AZStd::string materialFilePath = AZStd::string(AssetsFolderPath) + m_blenderFilename + "/" + MaterialsFolder.data() + "/";
+        for (rapidjson::Value::ConstValueIterator itr = materialArray.Begin(); itr != materialArray.End(); ++itr)
+        {
+            const auto matItr = itr->MemberBegin();
+            AZStd::string materialName = matItr->name.GetString();
+            AZStd::string materialContent = matItr->value.GetString();
+
+            AZStd::string fullFilePath = projectRootPath + materialFilePath + materialName + MaterialExtension.data();
+
+            AZ::Utils::WriteFile(materialContent, fullFilePath.c_str());
+
+            AZStd::string azMaterialPath = materialFilePath + materialName + AzMaterialExtension.data();
+            if (AZ::IO::FileIOBase::GetInstance()->Exists(azMaterialPath.c_str()))
+            {
+                AZ::Data::AssetId materialAssetId;
+                EBUS_EVENT_RESULT(
+                    materialAssetId,
+                    AZ::Data::AssetCatalogRequestBus,
+                    GetAssetIdByPath,
+                    azMaterialPath.c_str(),
+                    AZ::Data::s_invalidAssetType,
+                    false);
+
+                // If found, notify mesh that the mesh data is assigned and material is ready.
+                if (!materialAssetId.IsValid())
+                {
+                    m_materialWaitList.push_back(azMaterialPath);
+                }
+            }
+            else
+            {
+                m_materialWaitList.push_back(azMaterialPath);
+            }
+        }
+    }
+
+    void GNMeshController::SetFileName(const AZStd::string& path)
+    {
+        AzFramework::StringFunc::Path::GetFileName(path.c_str(), m_blenderFilename);
+
+        AZStd::regex reg("[^\\w\\s]+");
+        m_blenderFilename = AZStd::regex_replace(m_blenderFilename, reg, "_");
+    }
+
+    AZStd::string GNMeshController::GenerateFBXPath()
+    {
+        AZStd::string fullFilePath = GetProjectRoot() + "/";
+        AZStd::string filePath = AZStd::string(AssetsFolderPath) + m_blenderFilename + "/";
+        fullFilePath += filePath + GenerateModelAssetName() + FbxExtension.data();
+        return fullFilePath;
+    }
+
+    AZStd::string GNMeshController::GenerateModelAssetName()
+    {
+        return m_blenderFilename + "_" + AZStd::string::format("%llu", (AZ::u64)m_entityId);
+    }
+
+    void GNMeshController::OnTransformChanged([[maybe_unused]] const AZ::Transform& local, const AZ::Transform& world)
+    {
+        m_worldAabb.reset();
+        m_localAabb.reset();
+
+        m_world = world;
+
+        if (m_renderMesh)
+        {
+            m_renderMesh->UpdateTransform(world);
+        }
+    }
+
+    void GNMeshController::OnCatalogAssetAdded(const AZ::Data::AssetId& assetId)
+    {
+        AZ::Data::AssetInfo assetInfo;
+        EBUS_EVENT_RESULT(assetInfo, AZ::Data::AssetCatalogRequestBus, GetAssetInfoById, assetId);
+
+        // note that this will get called twice, once with the real assetId and once with legacy assetId.
+        // we only want to add the real asset to the list, in which the assetId passed in is equal to the final assetId returned
+        // otherwise, you look up assetId (and its a legacy assetId) and the actual asset will be different.
+        if ((assetInfo.m_assetId.IsValid()) && (assetInfo.m_assetId == assetId))
+        {
+            AZStd::string assetName;
+            AzFramework::StringFunc::Path::GetFileName(assetInfo.m_relativePath.c_str(), assetName);
+
+            bool workInProgress = false;
+            EditorGeomNodesComponentRequestBus::EventResult(
+                workInProgress, m_entityId, &EditorGeomNodesComponentRequests::GetWorkInProgress);
+            if (workInProgress && (assetName == GenerateModelAssetName()))
+            {
+                auto entity = AzToolsFramework::GetEntity(m_entityId);
+                auto transformComponent = entity->FindComponent<AzToolsFramework::Components::TransformComponent>();
+                AZ::EntityId parentId = transformComponent->GetParentId();
+                auto worldTransform = transformComponent->GetWorldTM();
+
+                AZ::EntityId entityId;
+                EBUS_EVENT_RESULT(entityId, AzToolsFramework::EditorRequests::Bus, CreateNewEntity, parentId);
+
+                AzToolsFramework::EntityIdList entityIdList = { entityId };
+
+                AzToolsFramework::EntityCompositionRequests::AddComponentsOutcome addedComponentsResult =
+                    AZ::Failure(AZStd::string("Failed to call AddComponentsToEntities on EntityCompositionRequestBus"));
+                AzToolsFramework::EntityCompositionRequestBus::BroadcastResult(
+                    addedComponentsResult,
+                    &AzToolsFramework::EntityCompositionRequests::AddComponentsToEntities,
+                    entityIdList,
+                    AZ::ComponentTypeList{ AZ::Render::EditorMeshComponentTypeId });
+
+                if (addedComponentsResult.IsSuccess())
+                {
+                    AZ::TransformBus::Event(entityId, &AZ::TransformBus::Events::SetWorldTM, worldTransform);
+
+                    AZ::Render::MeshComponentRequestBus::Event(
+                        entityId, &AZ::Render::MeshComponentRequestBus::Events::SetModelAssetPath, assetInfo.m_relativePath);
+
+                    EBUS_EVENT(
+                        AzToolsFramework::ToolsApplicationRequests::Bus,
+                        DeleteEntitiesAndAllDescendants,
+                        AzToolsFramework::EntityIdList{ m_entityId });
+                }
+
+                EditorGeomNodesComponentRequestBus::Event(m_entityId, &EditorGeomNodesComponentRequests::SetWorkInProgress, false);
+            }
+            else
+            {
+                if (!m_materialWaitList.empty())
+                {
+                    auto iter = AZStd::find(m_materialWaitList.begin(), m_materialWaitList.end(), assetInfo.m_relativePath);
+                    if (iter != m_materialWaitList.end())
+                    {
+                        m_materialWaitList.erase(iter);
+                        if (m_materialWaitList.empty())
+                        {
+                            RebuildRenderMesh();
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    void GNMeshController::OnCatalogAssetChanged(const AZ::Data::AssetId& assetId)
+    {
+        OnCatalogAssetAdded(assetId);
+    }
+} // namespace GeomNodes

+ 84 - 0
Gems/O3DE/GeomNodes/Code/Source/Editor/Rendering/GNMeshController.h

@@ -0,0 +1,84 @@
+/*
+ * 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/TransformBus.h>
+#include <AzFramework/Asset/AssetCatalogBus.h>
+#include <AzFramework/Visibility/BoundsBus.h>
+#include <AzToolsFramework/API/ComponentEntitySelectionBus.h>
+#include <Editor/Rendering/GNModelData.h>
+
+
+namespace GeomNodes
+{
+    class GNRenderMesh;
+    //! A common class that handles everything from storing the model data, rendering the mesh via GNRenderMesh
+    //! handling different mesh events like bound request, transform updates and others.
+    class GNMeshController
+        : public AzFramework::BoundsRequestBus::Handler
+        , public AzToolsFramework::EditorComponentSelectionRequestsBus::Handler
+        , private AZ::TransformNotificationBus::Handler
+        , private AzFramework::AssetCatalogEventBus::Handler
+    {
+    public:
+        explicit GNMeshController(AZ::EntityId entityId);
+        ~GNMeshController();
+
+        // EditorComponentSelectionRequestsBus overrides ...
+        AZ::Aabb GetEditorSelectionBoundsViewport(const AzFramework::ViewportInfo& viewportInfo) override;
+        bool EditorSelectionIntersectRayViewport(
+            const AzFramework::ViewportInfo& viewportInfo, const AZ::Vector3& src, const AZ::Vector3& dir, float& distance) override;
+        bool SupportsEditorRayIntersect() override;
+
+        // BoundsRequestBus overrides ...
+        AZ::Aabb GetWorldBounds() override;
+        AZ::Aabb GetLocalBounds() override;
+
+        //! Builds the model for atom rendering.
+        void RebuildRenderMesh();
+        //! Read the data from the shared memory and setup everything before building the render mesh.
+        void ReadData(AZ::u64 mapId);
+
+        //! Load the materials giver the json array. If there materials that are still in the AP in will tag them for waiting
+        //! so that the render mesh can properly load the materials.
+        void LoadMaterials(const rapidjson::Value& materialArray);
+        //! Sets the blender filename.
+        void SetFileName(const AZStd::string& path);
+        //! Generates the full FBX Path. This is usually used when exporting.
+        AZStd::string GenerateFBXPath();
+        //! Generates the Model asset name with the full path but without the file extension.
+        AZStd::string GenerateModelAssetName();
+
+    private:
+        // TransformNotificationBus overrides ...
+        void OnTransformChanged(const AZ::Transform& local, const AZ::Transform& world) override;
+
+        // AssetCatalogEventBus::Handler ...
+        //! we use these functions to know if our assets like materials are already loaded so we can proceed with rendering the model/meshes
+        void OnCatalogAssetAdded(const AZ::Data::AssetId& assetId) override;
+        void OnCatalogAssetChanged(const AZ::Data::AssetId& assetId) override;
+
+        //! assigned EntityId to this Mesh Controller
+        AZ::EntityId m_entityId;
+        //! Stores the model's data.
+        GNModelData m_modelData;
+        //! Reference to our render mesh.
+        AZStd::unique_ptr<GNRenderMesh> m_renderMesh;
+        //! Current world transform of the object.
+        AZ::Transform m_world = AZ::Transform::CreateIdentity();
+        //! Cached world aabb (used for selection/view determination).
+        AZStd::optional<AZ::Aabb> m_worldAabb;
+        //! Cached local aabb (used for center pivot calculation).
+        AZStd::optional<AZ::Aabb> m_localAabb;
+        //! Current loaded blender filename
+        AZStd::string m_blenderFilename;
+        //! List for materials building in AP. Having an empty list all materials are built and ready for loading.
+        AZStd::vector<AZStd::string> m_materialWaitList;
+    };
+
+} // namespace GeomNodes

+ 537 - 0
Gems/O3DE/GeomNodes/Code/Source/Editor/Rendering/GNMeshData.cpp

@@ -0,0 +1,537 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include <AzCore/std/containers/unordered_map.h>
+#include <Editor/Rendering/GNMeshData.h>
+
+
+namespace GeomNodes
+{
+    GNMeshData::GNMeshData()
+    {
+    }
+
+    GNMeshData::GNMeshData(
+        const Vert3Vector& positions,
+        const Vert3Vector& normals,
+        const S32Vector& indices,
+        const S32Vector& triangleLoops,
+        const S32Vector& loops,
+        const Vert2Vector& uvs,
+        const Vert4Vector& colors,
+        const S32Vector& materialIndices,
+        const MaterialList& materials,
+        AZ::u64 hash,
+        bool isIndexedUVs,
+        bool isIndexedColors)
+    {
+        m_positions = positions;
+        m_normals = normals;
+        m_loops = loops;
+        m_hash = hash;
+        m_colors = colors;
+        m_uvs = uvs;
+        m_indices.resize(indices.size());
+        AZStd::transform(
+            indices.begin(),
+            indices.end(),
+            m_indices.begin(),
+            [](int x)
+            {
+                return aznumeric_cast<AZ::u32>(x);
+            });
+
+        m_materialIndices = materialIndices;
+        m_materialNames = materials;
+
+        // we still need to build all the vertices, uvs, normals and colors as what we have is incomplete.
+        // the initial data are shared and is done this way so the data coming from blender will be small.
+        Vert2Vector finalUVs;
+        Vert3Vector finalPositions, finalNormals;
+        Vert4Vector finalColors;
+
+        finalPositions.reserve(m_positions.size());
+        finalNormals.reserve(m_normals.size());
+        finalUVs.reserve(m_positions.size());
+        finalColors.reserve(m_positions.size());
+
+        AZ_Assert(m_normals.size() == m_indices.size(), "normals and indices count should match!");
+
+        // iterate through the indices
+        AZStd::unordered_map<UniqueKey, AZ::s32> uniqueKeys;
+        for (int i = 0; i < m_indices.size(); i++)
+        {
+            // build unique keys based on the current index, uv, normal and color.
+            auto* indexPtr = &m_indices[i];
+            const auto& normal = m_normals[i];
+            UniqueKey key(
+                *indexPtr,
+                normal,
+                m_uvs[isIndexedUVs ? *indexPtr : triangleLoops[i]],
+                m_colors[isIndexedColors ? *indexPtr : triangleLoops[i]]);
+
+            auto iter = uniqueKeys.find(key);
+            if (iter == uniqueKeys.end())
+            {
+                finalPositions.emplace_back(m_positions[*indexPtr]);
+
+                if (isIndexedUVs)
+                {
+                    finalUVs.emplace_back(m_uvs[*indexPtr]);
+                }
+
+                if (isIndexedColors)
+                {
+                    finalColors.emplace_back(m_colors[*indexPtr]);
+                }
+
+                // update the indexes using the new one
+                *indexPtr = m_loops[triangleLoops[i]] = aznumeric_cast<AZ::s32>(finalPositions.size() - 1);
+                finalNormals.emplace_back(normal);
+                uniqueKeys.emplace(AZStd::make_pair<UniqueKey, AZ::s32>(AZStd::move(key), AZStd::move(i)));
+            }
+            else
+            {
+                auto splitVertexIdx = iter->second;
+                *indexPtr = m_indices[splitVertexIdx];
+                m_loops[triangleLoops[i]] = m_loops[triangleLoops[splitVertexIdx]];
+            }
+        }
+
+        m_positions = finalPositions;
+        m_normals = finalNormals;
+
+        AZStd::vector<AZ::s32> cornerIdxList;
+        cornerIdxList.resize(m_loops.size());
+        for (AZ::s32 i = 0; i < m_loops.size(); i++)
+        {
+            cornerIdxList[m_loops[i]] = i;
+        }
+
+        if (isIndexedUVs)
+        {
+            m_uvs = finalUVs;
+
+            std::transform(
+                m_uvs.begin(),
+                m_uvs.end(),
+                m_uvs.begin(),
+                [](Vector2f& uv)
+                {
+                    uv[1] = 1.f - uv[1];
+                    return uv;
+                });
+        }
+        else
+        {
+            AZStd::vector<Vector2f> indexedUVs;
+            indexedUVs.reserve(m_positions.size());
+            for (AZ::s32 i = 0; i < m_positions.size(); i++)
+            {
+                const auto cornerIdx = cornerIdxList[i];
+                indexedUVs.emplace_back(Vector2f{ m_uvs[cornerIdx][0], 1.f - m_uvs[cornerIdx][1] });
+            }
+
+            m_uvs = indexedUVs;
+        }
+
+        if (isIndexedColors)
+        {
+            m_colors = finalColors;
+        }
+    }
+
+    void GNMeshData::CalculateTangents()
+    {
+        m_tangents.resize(m_positions.size());
+        m_bitangents.resize(m_positions.size());
+
+        for (AZ::s32 i = 0; i < m_indices.size() / 3; ++i)
+        {
+            const AZ::Vector3 V0 = MathHelper::Vec3fToVec3(m_positions[m_indices[i + 0]]);
+            const AZ::Vector3 V1 = MathHelper::Vec3fToVec3(m_positions[m_indices[i + 1]]);
+            const AZ::Vector3 V2 = MathHelper::Vec3fToVec3(m_positions[m_indices[i + 2]]);
+
+            const AZ::Vector2 UV0 = MathHelper::Vec2fToVec2(m_uvs[m_indices[i + 0]]);
+            const AZ::Vector2 UV1 = MathHelper::Vec2fToVec2(m_uvs[m_indices[i + 1]]);
+            const AZ::Vector2 UV2 = MathHelper::Vec2fToVec2(m_uvs[m_indices[i + 2]]);
+
+            // Calculate edge vectors of the triangle
+            AZ::Vector3 edge1 = V1 - V0;
+            AZ::Vector3 edge2 = V2 - V0;
+
+            // Calculate delta m_uvs
+            AZ::Vector2 deltaUV1 = UV1 - UV0;
+            AZ::Vector2 deltaUV2 = UV2 - UV0;
+
+            // Calculate the determinant to calculate the direction of the tangent and bitangent
+            float det = 1.0f / (deltaUV1.GetX() * deltaUV2.GetY() - deltaUV2.GetX() * deltaUV1.GetY());
+
+            // Calculate the tangent vector
+            AZ::Vector3 tangent = (edge1 * deltaUV2.GetX() - edge2 * deltaUV1.GetY()) * det;
+            tangent.Normalize();
+
+            // Calculate the bitangent vector
+            AZ::Vector3 bitangent = (edge2 * deltaUV1.GetY() - edge1 * deltaUV2.GetX()) * det;
+            bitangent.Normalize();
+
+            // Calculate the handedness of the tangent space
+            float tangentW = (edge1.Cross(edge2).Dot(tangent) < 0.0f) ? -1.0f : 1.0f;
+
+            AZ::Vector4 tangent4 = AZ::Vector4(tangent, tangentW);
+            m_tangents[m_indices[i]] = MathHelper::Vec4ToVec4f(tangent4);
+            m_bitangents[m_indices[i]] = MathHelper::Vec3ToVec3f(bitangent);
+            m_tangents[m_indices[i + 1]] = MathHelper::Vec4ToVec4f(tangent4);
+            m_bitangents[m_indices[i + 1]] = MathHelper::Vec3ToVec3f(bitangent);
+            m_tangents[m_indices[i + 2]] = MathHelper::Vec4ToVec4f(tangent4);
+            m_bitangents[m_indices[i + 2]] = MathHelper::Vec3ToVec3f(bitangent);
+        }
+    }
+
+    void GNMeshData::CalculateAABB()
+    {
+        // calculate the aabb
+        for (const auto& vert : m_positions)
+        {
+            m_aabb.AddPoint(AZ::Vector3(vert[0], vert[1], vert[2]));
+        }
+    }
+
+    template<AttributeType AttributeTypeT>
+    AZ::u32 GNMeshData::GetCount() const
+    {
+        if constexpr (AttributeTypeT == AttributeType::Position)
+        {
+            return aznumeric_cast<AZ::u32>(m_positionsRange.count);
+        }
+        else if constexpr (AttributeTypeT == AttributeType::Normal)
+        {
+            return aznumeric_cast<AZ::u32>(m_normalsRange.count);
+        }
+        else if constexpr (AttributeTypeT == AttributeType::Tangent)
+        {
+            return aznumeric_cast<AZ::u32>(m_tangentsRange.count);
+        }
+        else if constexpr (AttributeTypeT == AttributeType::Bitangent)
+        {
+            return aznumeric_cast<AZ::u32>(m_bitangentsRange.count);
+        }
+        else if constexpr (AttributeTypeT == AttributeType::UV)
+        {
+            return aznumeric_cast<AZ::u32>(m_uvsRange.count);
+        }
+        else if constexpr (AttributeTypeT == AttributeType::Color)
+        {
+            return aznumeric_cast<AZ::u32>(m_colorsRange.count);
+        }
+    }
+
+    template<AttributeType AttributeTypeT>
+    AZ::u32 GNMeshData::GetOffset() const
+    {
+        if constexpr (AttributeTypeT == AttributeType::Position)
+        {
+            return m_positionsRange.offset;
+        }
+        else if constexpr (AttributeTypeT == AttributeType::Normal)
+        {
+            return m_normalsRange.offset;
+        }
+        else if constexpr (AttributeTypeT == AttributeType::Tangent)
+        {
+            return m_tangentsRange.offset;
+        }
+        else if constexpr (AttributeTypeT == AttributeType::Bitangent)
+        {
+            return m_bitangentsRange.offset;
+        }
+        else if constexpr (AttributeTypeT == AttributeType::UV)
+        {
+            return m_uvsRange.offset;
+        }
+        else if constexpr (AttributeTypeT == AttributeType::Color)
+        {
+            return m_colorsRange.offset;
+        }
+    }
+
+    template<AttributeType AttributeTypeT>
+    void GNMeshData::SetCount(AZ::u32 count)
+    {
+        if constexpr (AttributeTypeT == AttributeType::Position)
+        {
+            m_positionsRange.count = count;
+        }
+        else if constexpr (AttributeTypeT == AttributeType::Normal)
+        {
+            m_normalsRange.count = count;
+        }
+        else if constexpr (AttributeTypeT == AttributeType::Tangent)
+        {
+            m_tangentsRange.count = count;
+        }
+        else if constexpr (AttributeTypeT == AttributeType::Bitangent)
+        {
+            m_bitangentsRange.count = count;
+        }
+        else if constexpr (AttributeTypeT == AttributeType::UV)
+        {
+            m_uvsRange.count = count;
+        }
+        else if constexpr (AttributeTypeT == AttributeType::Color)
+        {
+            m_colorsRange.count = count;
+        }
+    }
+
+    template<AttributeType AttributeTypeT>
+    void GNMeshData::SetCount(AZ::u64 count)
+    {
+        SetCount<AttributeTypeT>(aznumeric_cast<AZ::u32>(count));
+    }
+
+    template<AttributeType AttributeTypeT>
+    void GNMeshData::SetOffset(AZ::u32 offset)
+    {
+        if constexpr (AttributeTypeT == AttributeType::Position)
+        {
+            m_positionsRange.offset = offset;
+        }
+        else if constexpr (AttributeTypeT == AttributeType::Normal)
+        {
+            m_normalsRange.offset = offset;
+        }
+        else if constexpr (AttributeTypeT == AttributeType::Tangent)
+        {
+            m_tangentsRange.offset = offset;
+        }
+        else if constexpr (AttributeTypeT == AttributeType::Bitangent)
+        {
+            m_bitangentsRange.offset = offset;
+        }
+        else if constexpr (AttributeTypeT == AttributeType::UV)
+        {
+            m_uvsRange.offset = offset;
+        }
+        else if constexpr (AttributeTypeT == AttributeType::Color)
+        {
+            m_colorsRange.offset = offset;
+        }
+    }
+
+    template<AttributeType AttributeTypeT>
+    void GNMeshData::SetOffset(AZ::u64 offset)
+    {
+        SetOffset<AttributeTypeT>(aznumeric_cast<AZ::u32>(offset));
+    }
+
+    template<AttributeType AttributeTypeT>
+    const auto& GNMeshData::GetDataBuffer() const
+    {
+        if constexpr (AttributeTypeT == AttributeType::Position)
+        {
+            return m_positions;
+        }
+        else if constexpr (AttributeTypeT == AttributeType::Normal)
+        {
+            return m_normals;
+        }
+        else if constexpr (AttributeTypeT == AttributeType::Tangent)
+        {
+            return m_tangents;
+        }
+        else if constexpr (AttributeTypeT == AttributeType::Bitangent)
+        {
+            return m_bitangents;
+        }
+        else if constexpr (AttributeTypeT == AttributeType::UV)
+        {
+            return m_uvs;
+        }
+        else if constexpr (AttributeTypeT == AttributeType::Color)
+        {
+            return m_colors;
+        }
+    }
+
+    AZ::u32 GNMeshData::GetIndexCount() const
+    {
+        return m_indicesRange.count;
+    }
+
+    AZ::u32 GNMeshData::GetIndexOffset() const
+    {
+        return m_indicesRange.offset;
+    }
+
+    void GNMeshData::SetIndexOffset(AZ::u32 offset)
+    {
+        m_indicesRange.offset = offset;
+    }
+
+    void GNMeshData::SetIndexOffset(AZ::u64 offset)
+    {
+        SetIndexOffset(aznumeric_cast<AZ::u32>(offset));
+    }
+
+    void GNMeshData::SetIndexCount(AZ::u32 count)
+    {
+        m_indicesRange.count = count;
+    }
+
+    void GNMeshData::SetIndexCount(AZ::u64 count)
+    {
+        SetIndexCount(aznumeric_cast<AZ::u32>(count));
+    }
+
+    const U32Vector& GNMeshData::GetIndices() const
+    {
+        return m_indices;
+    }
+
+    void GNMeshData::SetIndices(const U32Vector& indices)
+    {
+        m_indices = indices;
+    }
+
+    const Mat4Vector& GNMeshData::GetInstances() const
+    {
+        return m_instances;
+    }
+
+    const MaterialList& GNMeshData::GetMaterials() const
+    {
+        return m_materialNames;
+    }
+
+    void GNMeshData::ClearMaterialList()
+    {
+        m_materialNames.clear();
+    }
+
+    void GNMeshData::SetMaterialIndex(AZ::u32 materialIndex)
+    {
+        m_materialIndex = materialIndex;
+    }
+
+    AZ::u32 GNMeshData::GetMaterialIndex() const
+    {
+        return m_materialIndex;
+    }
+
+    void GNMeshData::AddInstance(const AZ::Matrix4x4& mat4)
+    {
+        m_instances.emplace_back(mat4);
+    }
+
+    U32Vector GNMeshData::GetIndicesByMaterialIndex(int materialIndex)
+    {
+        U32Vector indices;
+        indices.reserve(m_indices.size());
+
+        for (AZ::s32 i = 0; i < m_indices.size(); i += 3)
+        {
+            const AZ::s32 faceIndex = i / 3;
+            if (m_materialIndices[faceIndex] == materialIndex)
+            {
+                indices.push_back(m_indices[i]);
+                indices.push_back(m_indices[i + 1]);
+                indices.push_back(m_indices[i + 2]);
+            }
+        }
+
+        return indices;
+    }
+
+    AZ::Aabb GNMeshData::GetAabb() const
+    {
+        return m_aabb;
+    }
+
+    AZ::s64 GNMeshData::GetHash() const
+    {
+        return m_hash;
+    }
+
+    GNMeshData& GNMeshData::operator+=(const GNMeshData& rhs)
+    {
+        AZ::u32 count = aznumeric_cast<AZ::u32>(m_positions.max_size() + rhs.m_positions.size() * rhs.m_instances.size());
+        m_indices.reserve(count * 3);
+        m_positions.reserve(count);
+        m_normals.reserve(count);
+        m_tangents.reserve(count);
+        m_bitangents.reserve(count);
+        m_uvs.reserve(count);
+        m_colors.reserve(count);
+
+        for (const auto& instance : rhs.m_instances)
+        {
+            AZ::s32 indexOffsset = aznumeric_cast<AZ::u32>(m_positions.size());
+            U32Vector rhsIndices = rhs.m_indices;
+
+            for (AZ::u32& index : rhsIndices)
+            {
+                index += indexOffsset;
+            }
+
+            m_indices.insert(m_indices.end(), rhsIndices.begin(), rhsIndices.end());
+            SetIndexCount(m_indices.size());
+
+            Vert3Vector rhsPositions = rhs.m_positions;
+            for (auto& position : rhsPositions)
+            {
+                auto vec3 = instance * MathHelper::Vec3fToVec3(position);
+                position = MathHelper::Vec3ToVec3f(vec3);
+            }
+
+            m_positions.insert(m_positions.end(), rhsPositions.begin(), rhsPositions.end());
+            SetCount<AttributeType::Position>(m_positions.size());
+
+            Vert3Vector rhsNormals = rhs.m_normals;
+            auto normalMatrix = instance.GetInverseFull();
+            normalMatrix.Transpose();
+            for (auto& normal : rhsNormals)
+            {
+                auto vec3 = normalMatrix * MathHelper::Vec3fToVec3(normal);
+                vec3.Normalize();
+                normal = MathHelper::Vec3ToVec3f(vec3);
+            }
+
+            m_normals.insert(m_normals.end(), rhsNormals.begin(), rhsNormals.end());
+            SetCount<AttributeType::Normal>(m_normals.size());
+
+            m_colors.insert(m_colors.end(), rhs.m_colors.begin(), rhs.m_colors.end());
+            SetCount<AttributeType::Color>(m_colors.size());
+
+            m_uvs.insert(m_uvs.end(), rhs.m_uvs.begin(), rhs.m_uvs.end());
+            SetCount<AttributeType::UV>(m_uvs.size());
+            m_tangents.insert(m_tangents.end(), rhs.m_tangents.begin(), rhs.m_tangents.end());
+            SetCount<AttributeType::Tangent>(m_tangents.size());
+            m_bitangents.insert(m_bitangents.end(), rhs.m_bitangents.begin(), rhs.m_bitangents.end());
+            SetCount<AttributeType::Bitangent>(m_bitangents.size());
+        }
+
+        return *this;
+    }
+
+    void GNMeshData::ClearBuffers()
+    {
+        m_indices.clear();
+        m_loops.clear();
+        m_materialIndices.clear();
+
+        m_positions.clear();
+        m_normals.clear();
+        m_tangents.clear();
+        m_bitangents.clear();
+        m_uvs.clear();
+        m_colors.clear();
+
+        m_instances.clear();
+    }
+} // namespace GeomNodes

+ 127 - 0
Gems/O3DE/GeomNodes/Code/Source/Editor/Rendering/GNMeshData.h

@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#include <AZCore/std/string/string.h>
+#include <AzCore/Math/Aabb.h>
+#include <Editor/Common/GNConstants.h>
+#include <Editor/Math/MathHelper.h>
+
+
+namespace GeomNodes
+{
+    using MaterialList = AZStd::vector<AZStd::string>;
+
+    //! Mesh specific data class
+    class GNMeshData
+    {
+    public:
+        struct DataRange
+        {
+            AZ::u32 offset = 0;
+            AZ::u32 count = 0;
+        };
+
+        GNMeshData();
+        explicit GNMeshData(
+            const Vert3Vector& positions,
+            const Vert3Vector& normals,
+            const S32Vector& indices,
+            const S32Vector& triangleLoops,
+            const S32Vector& loops,
+            const Vert2Vector& uvs,
+            const Vert4Vector& colors,
+            const S32Vector& materialIndices,
+            const MaterialList& materials,
+            AZ::u64 hash,
+            bool isIndexedUVs,
+            bool isIndexedColors);
+
+        ~GNMeshData() = default;
+
+        AZ::u32 GetIndexCount() const;
+        AZ::u32 GetIndexOffset() const;
+        void SetIndexOffset(AZ::u32 offset);
+        void SetIndexOffset(AZ::u64 offset);
+        void SetIndexCount(AZ::u32 count);
+        void SetIndexCount(AZ::u64 count);
+
+        template<AttributeType AttributeTypeT>
+        AZ::u32 GetCount() const;
+        template<AttributeType AttributeTypeT>
+        AZ::u32 GetOffset() const;
+        template<AttributeType AttributeTypeT>
+        void SetCount(AZ::u32 count);
+        template<AttributeType AttributeTypeT>
+        void SetCount(AZ::u64 count);
+        template<AttributeType AttributeTypeT>
+        void SetOffset(AZ::u32 offset);
+        template<AttributeType AttributeTypeT>
+        void SetOffset(AZ::u64 offset);
+
+        const U32Vector& GetIndices() const;
+        void SetIndices(const U32Vector& indices);
+
+        template<AttributeType AttributeTypeT>
+        const auto& GetDataBuffer() const;
+
+        const Mat4Vector& GetInstances() const;
+        const MaterialList& GetMaterials() const;
+        void ClearMaterialList();
+        void SetMaterialIndex(AZ::u32 materialIndex);
+        AZ::u32 GetMaterialIndex() const;
+        void AddInstance(const AZ::Matrix4x4& mat4);
+
+        U32Vector GetIndicesByMaterialIndex(int materialIndex);
+
+        AZ::Aabb GetAabb() const;
+        AZ::s64 GetHash() const;
+        void CalculateTangents();
+        void CalculateAABB();
+
+        GNMeshData& operator+=(const GNMeshData& rhs);
+
+        void ClearBuffers();
+
+    private:
+        U32Vector m_indices;
+        S32Vector m_loops;
+        S32Vector m_materialIndices;
+
+        //! hash are used to easily matched if you have the same mesh.
+        AZ::s64 m_hash = 0;
+        //! List of material names used by the mesh.
+        MaterialList m_materialNames;
+        //! List of instances. Instances are used so that you have the same mesh but on a different transform.
+        Mat4Vector m_instances;
+
+        AZ::Aabb m_aabb = AZ::Aabb::CreateNull();
+
+        Vert3Vector m_positions;
+        Vert3Vector m_normals;
+        Vert4Vector m_tangents;
+        Vert3Vector m_bitangents;
+        Vert2Vector m_uvs;
+        Vert4Vector m_colors;
+
+        //! As meshes are eventually merged to common buffers we need to keep track of the offset and element count.
+        //! When we create the Atom Buffers we will need these infos while the mesh buffers will be held in GNModelData.
+        DataRange m_indicesRange;
+        DataRange m_positionsRange;
+        DataRange m_normalsRange;
+        DataRange m_tangentsRange;
+        DataRange m_bitangentsRange;
+        DataRange m_uvsRange;
+        DataRange m_colorsRange;
+
+        //! Material index or slot that will be used by the mesh. Note that eventually a mesh will only be assigned to one material.
+        //! This way they can be grouped together by material and merge the vertices.
+        AZ::u32 m_materialIndex = 0;
+    };
+} // namespace GeomNodes

+ 292 - 0
Gems/O3DE/GeomNodes/Code/Source/Editor/Rendering/GNModelData.cpp

@@ -0,0 +1,292 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include <AzCore/std/containers/map.h>
+#include <AzCore/std/containers/set.h>
+#include <Editor/Common/GNAPI.h>
+#include <Editor/Rendering/GNModelData.h>
+#include <Editor/Systems/GNParamContext.h>
+
+
+namespace GeomNodes
+{
+    GNModelData::GNModelData()
+    {
+    }
+
+    GNModelData::GNModelData(AZ::u64 mapId)
+    {
+        ReadData(mapId);
+    }
+
+    void GNModelData::ReadData(AZ::u64 mapId)
+    {
+        m_meshes.clear();
+
+        if (API::OpenSHM(mapId))
+        {
+            AZ::s32 meshCount = Read<AZ::s32>(mapId);
+            AZ::s32 instanceCount = Read<AZ::s32>(mapId);
+
+            AZ_Printf("GNInstance", "meshCount : %i, instanceCount : %i", meshCount, instanceCount);
+
+            AZStd::map<AZStd::string, AZStd::vector<GNMeshData>> meshMap;
+            for ([[maybe_unused]] AZ::s32 meshIdx = 0; meshIdx < meshCount; meshIdx++)
+            {
+                auto positions = ReadArray<Vector3f>(mapId);
+                auto normals = ReadArray<Vector3f>(mapId);
+                auto indices = ReadArray<AZ::s32>(mapId);
+                auto triangleLoops = ReadArray<AZ::s32>(mapId);
+                auto loops = ReadArray<AZ::s32>(mapId);
+                auto hash = Read<AZ::s64>(mapId);
+
+                auto colors = ReadArray<Vector4f>(mapId);
+                bool bIndexedColors = Read<bool>(mapId);
+                auto uvs = ReadArray<Vector2f>(mapId);
+                bool bIndexedUVs = Read<bool>(mapId);
+                auto materialIndices = ReadArray<AZ::s32>(mapId);
+                auto materials = ReadArray<char>(mapId);
+
+                if (positions.empty())
+                {
+                    continue;
+                }
+
+                MaterialList materialList;
+                rapidjson::Document jsonDocument;
+                jsonDocument.Parse((const char*)materials.data(), materials.size());
+                if (!jsonDocument.HasParseError())
+                {
+                    if (jsonDocument.HasMember(Field::Materials))
+                    {
+                        for (auto& entry : jsonDocument[Field::Materials].GetArray())
+                        {
+                            const auto materialName = entry.GetString();
+                            materialList.push_back(materialName);
+                            if (meshMap.find(materialName) == meshMap.end()) // check if we have the key in the map
+                            {
+                                meshMap.emplace(AZStd::make_pair(materialName, AZStd::vector<GNMeshData>({})));
+                            }
+                        }
+                    }
+                }
+
+                GNMeshData meshData(
+                    positions,
+                    normals,
+                    indices,
+                    triangleLoops,
+                    loops,
+                    uvs,
+                    colors,
+                    materialIndices,
+                    materialList,
+                    hash,
+                    bIndexedUVs,
+                    bIndexedColors);
+                m_meshes.push_back(meshData);
+            }
+
+            // at this stage meshes can have multiple materials and we need to create their own mesh copy
+            // but with a different indices based on the material indices.
+            for (auto& mesh : m_meshes)
+            {
+                int materialMeshIndex = 0;
+                for (auto& materialName : mesh.GetMaterials())
+                {
+                    auto indices = mesh.GetIndicesByMaterialIndex(materialMeshIndex);
+                    if (indices.size() > 0)
+                    {
+                        auto& meshInstance = meshMap[materialName].emplace_back(mesh);
+                        meshInstance.SetIndices(indices);
+                        meshInstance.CalculateTangents();
+                        meshInstance.ClearMaterialList();
+                    }
+                    materialMeshIndex++;
+                }
+            }
+
+            for ([[maybe_unused]] AZ::s32 instanceIdx = 0; instanceIdx < instanceCount; instanceIdx++)
+            {
+                AZ::s64 hash = Read<AZ::s64>(mapId);
+
+                [[maybe_unused]] AZ::Matrix4x4 localMatrix = Read<AZ::Matrix4x4>(mapId);
+                [[maybe_unused]] AZ::Matrix4x4 worldMatrix = Read<AZ::Matrix4x4>(mapId);
+
+                AZ::Matrix3x3 rotateZ = AZ::Matrix3x3::CreateRotationZ(AZ::DegToRad(180.0f));
+
+                auto rotate4x4 = AZ::Matrix4x4::CreateFromMatrix3x4(AZ::Matrix3x4::CreateFromMatrix3x3(rotateZ));
+                AZ::Matrix4x4 instanceMatrix = rotate4x4 * localMatrix;
+
+                for (auto& meshGroup : meshMap)
+                {
+                    for (auto& mesh : meshGroup.second)
+                    {
+                        if (mesh.GetHash() == hash)
+                        {
+                            mesh.AddInstance(instanceMatrix);
+                        }
+                    }
+                }
+            }
+
+            m_meshes.clear();
+            m_materials.clear();
+
+            // merge all meshes in each mesh group along with their instances
+            AZ::u32 materialIndex = 0;
+            for (auto& meshGroup : meshMap)
+            {
+                GNMeshData meshData;
+                for (const auto& mesh : meshGroup.second)
+                {
+                    meshData += mesh;
+                }
+
+                if (meshData.GetCount<AttributeType::Position>() > 0)
+                {
+                    meshData.SetMaterialIndex(materialIndex);
+                    meshData.CalculateAABB();
+                    m_aabb.AddAabb(meshData.GetAabb()); // add mesh data's aabb to get the aabb for the whole model
+                    m_meshes.push_back(meshData);
+                    m_materials.push_back(meshGroup.first);
+                    materialIndex++;
+                }
+            }
+
+            // Convert to only one buffers/arrays and keep track of the offsets and element counts
+            MergeMeshBuffers();
+
+            API::ClearSHM(mapId);
+        }
+    }
+
+    const AZ::u32 GNModelData::MeshCount() const
+    {
+        return aznumeric_cast<AZ::u32>(m_meshes.size());
+    }
+
+    const GNModelData::MeshDataList GNModelData::GetMeshes() const
+    {
+        return m_meshes;
+    }
+
+    MaterialList GNModelData::GetMaterials()
+    {
+        return m_materials;
+    }
+
+    const U32Vector& GNModelData::GetIndices() const
+    {
+        return m_indices;
+    }
+
+    const Vert3Vector& GNModelData::GetPositions() const
+    {
+        return m_positions;
+    }
+
+    const Vert3Vector& GNModelData::GetNormals() const
+    {
+        return m_normals;
+    }
+
+    const Vert4Vector& GNModelData::GetTangents() const
+    {
+        return m_tangents;
+    }
+
+    const Vert3Vector& GNModelData::GetBitangents() const
+    {
+        return m_bitangents;
+    }
+
+    const Vert2Vector& GNModelData::GetUVs() const
+    {
+        return m_uvs;
+    }
+
+    const Vert4Vector& GNModelData::GetColors() const
+    {
+        return m_colors;
+    }
+
+    AZ::Aabb GNModelData::GetAabb() const
+    {
+        return m_aabb;
+    }
+
+    void GNModelData::MergeMeshBuffers()
+    {
+        m_indices.clear();
+        m_positions.clear();
+        m_normals.clear();
+        m_tangents.clear();
+        m_bitangents.clear();
+        m_uvs.clear();
+        m_colors.clear();
+
+        for (auto& mesh : m_meshes)
+        {
+            mesh.SetIndexOffset(m_indices.size());
+            m_indices.insert(m_indices.end(), mesh.GetIndices().begin(), mesh.GetIndices().end());
+
+            mesh.SetOffset<AttributeType::Position>(m_positions.size());
+            m_positions.insert(
+                m_positions.end(),
+                mesh.GetDataBuffer<AttributeType::Position>().begin(),
+                mesh.GetDataBuffer<AttributeType::Position>().end());
+
+            mesh.SetOffset<AttributeType::Normal>(m_normals.size());
+            m_normals.insert(
+                m_normals.end(), mesh.GetDataBuffer<AttributeType::Normal>().begin(), mesh.GetDataBuffer<AttributeType::Normal>().end());
+
+            mesh.SetOffset<AttributeType::Tangent>(m_tangents.size());
+            m_tangents.insert(
+                m_tangents.end(), mesh.GetDataBuffer<AttributeType::Tangent>().begin(), mesh.GetDataBuffer<AttributeType::Tangent>().end());
+
+            mesh.SetOffset<AttributeType::Bitangent>(m_bitangents.size());
+            m_bitangents.insert(
+                m_bitangents.end(),
+                mesh.GetDataBuffer<AttributeType::Bitangent>().begin(),
+                mesh.GetDataBuffer<AttributeType::Bitangent>().end());
+
+            mesh.SetOffset<AttributeType::UV>(m_uvs.size());
+            m_uvs.insert(m_uvs.end(), mesh.GetDataBuffer<AttributeType::UV>().begin(), mesh.GetDataBuffer<AttributeType::UV>().end());
+
+            mesh.SetOffset<AttributeType::Color>(m_colors.size());
+            m_colors.insert(
+                m_colors.end(), mesh.GetDataBuffer<AttributeType::Color>().begin(), mesh.GetDataBuffer<AttributeType::Color>().end());
+
+            // clear the buffers since we already copied them.
+            mesh.ClearBuffers();
+        }
+    }
+
+    template<typename T>
+    AZStd::vector<T> GNModelData::ReadArray(AZ::u64 mapId)
+    {
+        AZ::u64 length;
+        void* address;
+        API::ReadSHM(mapId, &address, &length);
+
+        T* array = static_cast<T*>(address);
+        return AZStd::vector<T>(array, array + (length / sizeof(T)));
+    }
+
+    template<typename T>
+    T GNModelData::Read(AZ::u64 mapId)
+    {
+        AZ::u64 length;
+        void* address;
+        API::ReadSHM(mapId, &address, &length);
+        T value{};
+        memcpy(&value, address, length);
+        return value;
+    }
+} // namespace GeomNodes

+ 74 - 0
Gems/O3DE/GeomNodes/Code/Source/Editor/Rendering/GNModelData.h

@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#include <AzCore/std/containers/map.h>
+#include <Editor/Rendering/GNMeshData.h>
+
+
+namespace GeomNodes
+{
+    //! Handle parsing, handling and storing model data.
+    class GNModelData
+    {
+    public:
+        using MeshDataList = AZStd::vector<GNMeshData>;
+
+        GNModelData();
+        GNModelData(AZ::u64 mapId);
+        ~GNModelData() = default;
+
+        //! Read all model data in the shared memory given the map id.
+        void ReadData(AZ::u64 mapId);
+
+        //! return number of meshes in the model.
+        const AZ::u32 MeshCount() const;
+        //! return list of mesh data.
+        const MeshDataList GetMeshes() const;
+        //! return the list of material names.
+        MaterialList GetMaterials();
+
+        //! These functions are the combination of all mesh buffers in order based on their position in MeshDataList
+        const U32Vector& GetIndices() const;
+        const Vert3Vector& GetPositions() const;
+        const Vert3Vector& GetNormals() const;
+        const Vert4Vector& GetTangents() const;
+        const Vert3Vector& GetBitangents() const;
+        const Vert2Vector& GetUVs() const;
+        const Vert4Vector& GetColors() const;
+
+        AZ::Aabb GetAabb() const;
+
+    private:
+        //! Merges all indices and buffers into one buffer. Keeping track of the offsets and sizes.
+        void MergeMeshBuffers();
+        //! Given the map id as the SHM name read data array from the shared memory based on typename T.
+        template<typename T>
+        AZStd::vector<T> ReadArray(AZ::u64 mapId);
+        //! Given the map id as the SHM name read data from the shared memory based on typename T.
+        template<typename T>
+        T Read(AZ::u64 mapId);
+
+        //! stores the mesh data.
+        MeshDataList m_meshes;
+        //! stores the material names.
+        MaterialList m_materials;
+        //! These are all data combined from the meshes in single arrays
+        //! Combine them to one buffer to avoid the 256 mesh limit.
+        U32Vector m_indices;
+        Vert3Vector m_positions;
+        Vert3Vector m_normals;
+        Vert4Vector m_tangents;
+        Vert3Vector m_bitangents;
+        Vert2Vector m_uvs;
+        Vert4Vector m_colors;
+
+        AZ::Aabb m_aabb = AZ::Aabb::CreateNull();
+    };
+} // namespace GeomNodes

+ 317 - 0
Gems/O3DE/GeomNodes/Code/Source/Editor/Rendering/GNRenderMesh.cpp

@@ -0,0 +1,317 @@
+/*
+ * 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 "GNRenderMesh.h"
+
+#include <Atom/RPI.Public/Model/Model.h>
+#include <Atom/RPI.Public/Scene.h>
+#include <Atom/RPI.Reflect/Asset/AssetUtils.h>
+#include <Atom/RPI.Reflect/Model/ModelAssetCreator.h>
+#include <Atom/RPI.Reflect/Model/ModelLodAssetCreator.h>
+#include <Atom/RPI.Reflect/ResourcePoolAssetCreator.h>
+#include <AtomLyIntegration/CommonFeatures/Material/MaterialComponentBus.h>
+#include <AzCore/Math/PackedVector3.h>
+
+namespace GeomNodes
+{
+    GNRenderMesh::GNRenderMesh(AZ::EntityId entityId)
+        : m_entityId(entityId)
+    {
+        AZ::Render::MeshHandleStateRequestBus::Handler::BusConnect(m_entityId);
+        AZ::Render::MaterialConsumerRequestBus::Handler::BusConnect(m_entityId);
+        AZ::Render::MaterialComponentNotificationBus::Handler::BusConnect(m_entityId);
+    }
+
+    GNRenderMesh::~GNRenderMesh()
+    {
+        if (m_meshHandle.IsValid() && m_meshFeatureProcessor)
+        {
+            m_meshFeatureProcessor->ReleaseMesh(m_meshHandle);
+            AZ::Render::MeshHandleStateNotificationBus::Event(
+                m_entityId, &AZ::Render::MeshHandleStateNotificationBus::Events::OnMeshHandleSet, &m_meshHandle);
+        }
+
+        AZ::Render::MaterialConsumerRequestBus::Handler::BusDisconnect();
+        AZ::Render::MaterialComponentNotificationBus::Handler::BusDisconnect();
+        AZ::Render::MeshHandleStateRequestBus::Handler::BusDisconnect();
+    }
+
+    bool GNRenderMesh::AreAttributesValid() const
+    {
+        bool attributesAreValid = true;
+
+        for (const auto& attribute : m_attributes)
+        {
+            AZStd::visit(
+                [&attributesAreValid](const auto& att)
+                {
+                    if (!att->IsValid())
+                    {
+                        attributesAreValid = false;
+                    }
+                },
+                attribute);
+        }
+
+        return attributesAreValid;
+    }
+
+    bool GNRenderMesh::CreateMeshBuffers(const GNModelData& modelData)
+    {
+        m_indexBuffer = AZStd::make_unique<IndexBuffer>(modelData.GetIndices());
+
+        CreateAttributeBuffer<AttributeType::Position>(modelData.GetPositions());
+        CreateAttributeBuffer<AttributeType::Normal>(modelData.GetNormals());
+        CreateAttributeBuffer<AttributeType::Tangent>(modelData.GetTangents());
+        CreateAttributeBuffer<AttributeType::Bitangent>(modelData.GetBitangents());
+        CreateAttributeBuffer<AttributeType::UV>(modelData.GetUVs());
+        CreateAttributeBuffer<AttributeType::Color>(modelData.GetColors());
+
+        return AreAttributesValid();
+    }
+
+    void GNRenderMesh::AddLodBuffers(AZ::RPI::ModelLodAssetCreator& modelLodCreator)
+    {
+        modelLodCreator.SetLodIndexBuffer(m_indexBuffer->GetBuffer());
+
+        for (auto& attribute : m_attributes)
+        {
+            AZStd::visit(
+                [&modelLodCreator](auto& att)
+                {
+                    att->AddLodStreamBuffer(modelLodCreator);
+                },
+                attribute);
+        }
+    }
+
+    void GNRenderMesh::AddMeshBuffers(AZ::RPI::ModelLodAssetCreator& modelLodCreator, const GNMeshData& meshData)
+    {
+        modelLodCreator.SetMeshIndexBuffer(
+            { m_indexBuffer->GetBuffer(),
+              AZ::RHI::BufferViewDescriptor::CreateTyped(
+                  meshData.GetIndexOffset(), meshData.GetIndexCount(), GetFormatForVertexStreamDataType<uint32_t>()) });
+
+        for (auto& attribute : m_attributes)
+        {
+            AZStd::visit(
+                [&modelLodCreator, meshData](auto&& att)
+                {
+                    att->AddMeshStreamBuffer(modelLodCreator, meshData);
+                },
+                attribute);
+        }
+    }
+
+    bool GNRenderMesh::CreateLodAsset(const GNModelData& modelData)
+    {
+        if (!CreateMeshBuffers(modelData))
+        {
+            return false;
+        }
+
+        AZ::RPI::ModelLodAssetCreator modelLodCreator;
+        modelLodCreator.Begin(AZ::Data::AssetId(AZ::Uuid::CreateRandom()));
+        AddLodBuffers(modelLodCreator);
+
+        for (auto meshData : modelData.GetMeshes())
+        {
+            modelLodCreator.BeginMesh();
+            modelLodCreator.SetMeshAabb(modelData.GetAabb());
+
+            modelLodCreator.SetMeshMaterialSlot(meshData.GetMaterialIndex());
+
+            AddMeshBuffers(modelLodCreator, meshData);
+            modelLodCreator.EndMesh();
+        }
+
+        if (!modelLodCreator.End(m_lodAsset))
+        {
+            AZ_Error("CreateLodAsset", false, "Couldn't create LoD asset.");
+            return false;
+        }
+
+        if (!m_lodAsset.IsReady())
+        {
+            AZ_Error("CreateLodAsset", false, "LoD asset is not ready.");
+            return false;
+        }
+
+        if (!m_lodAsset.Get())
+        {
+            AZ_Error("CreateLodAsset", false, "LoD asset is nullptr.");
+            return false;
+        }
+
+        return true;
+    }
+
+    void GNRenderMesh::CreateModelAsset()
+    {
+        AZ::RPI::ModelAssetCreator modelCreator;
+        modelCreator.Begin(AZ::Data::AssetId(AZ::Uuid::CreateRandom()));
+        modelCreator.SetName(ModelName);
+        modelCreator.AddLodAsset(AZStd::move(m_lodAsset));
+
+        m_materialMap.clear();
+        AZ::RPI::ModelMaterialSlot::StableId slotId = 0;
+        for (auto materialPath : m_materialPathList)
+        {
+            if (auto materialAsset = AZ::RPI::AssetUtils::LoadAssetByProductPath<AZ::RPI::MaterialAsset>(materialPath.c_str()))
+            {
+                auto materialOverrideInstance = AZ::RPI::Material::FindOrCreate(materialAsset);
+                auto& materialAssignment = m_materialMap[AZ::Render::MaterialAssignmentId::CreateFromStableIdOnly(slotId)];
+                materialAssignment.m_materialAsset = materialAsset;
+                materialAssignment.m_materialInstance = materialOverrideInstance;
+
+                AZ::RPI::ModelMaterialSlot materialSlot;
+                materialSlot.m_stableId = slotId;
+                materialSlot.m_defaultMaterialAsset = materialAsset;
+                modelCreator.AddMaterialSlot(materialSlot);
+            }
+            else
+            {
+                AZ_Error("CreateLodAsset", false, "Could not load material.");
+                return;
+            }
+
+            slotId++;
+        }
+
+        modelCreator.End(m_modelAsset);
+    }
+
+    bool GNRenderMesh::CreateModel()
+    {
+        m_model = AZ::RPI::Model::FindOrCreate(m_modelAsset);
+        m_meshFeatureProcessor = AZ::RPI::Scene::GetFeatureProcessorForEntity<AZ::Render::MeshFeatureProcessorInterface>(m_entityId);
+
+        if (!m_meshFeatureProcessor)
+        {
+            AZ_Error("MeshComponentController", m_meshFeatureProcessor, "Unable to find a MeshFeatureProcessorInterface on the entityId.");
+            return false;
+        }
+
+        m_meshFeatureProcessor->ReleaseMesh(m_meshHandle);
+        m_meshHandle = m_meshFeatureProcessor->AcquireMesh(AZ::Render::MeshHandleDescriptor{ m_modelAsset });
+        AZ::Render::MeshHandleStateNotificationBus::Event(
+            m_entityId, &AZ::Render::MeshHandleStateNotificationBus::Events::OnMeshHandleSet, &m_meshHandle);
+
+        return true;
+    }
+
+    bool GNRenderMesh::CreateMesh(const GNModelData& modelData)
+    {
+        if (!CreateLodAsset(modelData))
+        {
+            return false;
+        }
+
+        CreateModelAsset();
+
+        if (!CreateModel())
+        {
+            return false;
+        }
+
+        SetMaterials();
+
+        return true;
+    }
+
+    bool GNRenderMesh::DoesMeshRequireFullRebuild([[maybe_unused]] const GNMeshData& meshData) const
+    {
+        // this has been disabled due to a some recent updates with Atom that a) cause visual artefacts
+        // when updating the buffers and b) have a big performance boost when rebuilding from scratch anyway.
+        //
+        // this method for building the mesh will probably be replace anyway when the Atom DynamicDraw support
+        // comes online.
+        return true; // meshData.VertexCount() != m_vertexCount;
+    }
+
+    void GNRenderMesh::SetMaterials()
+    {
+        if (m_meshFeatureProcessor && m_meshHandle.IsValid())
+        {
+            m_meshFeatureProcessor->SetCustomMaterials(m_meshHandle, AZ::Render::ConvertToCustomMaterialMap(m_materialMap));
+        }
+    }
+
+    void GNRenderMesh::SetMaterialPathList(const AZStd::vector<AZStd::string>& materialPaths)
+    {
+        m_materialPathList = materialPaths;
+    }
+
+    void GNRenderMesh::BuildMesh(const GNModelData& modelData)
+    {
+        CreateMesh(modelData);
+    }
+
+    void GNRenderMesh::UpdateTransform(const AZ::Transform& worldFromLocal)
+    {
+        if (m_meshFeatureProcessor && m_meshHandle.IsValid())
+        {
+            m_meshFeatureProcessor->SetTransform(m_meshHandle, worldFromLocal);
+        }
+    }
+
+    void GNRenderMesh::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
+    {
+    }
+
+    AZ::Render::MaterialAssignmentId GNRenderMesh::FindMaterialAssignmentId(
+        const AZ::Render::MaterialAssignmentLodIndex lod, const AZStd::string& label) const
+    {
+        return AZ::Render::GetMaterialSlotIdFromModelAsset(m_modelAsset, lod, label);
+    }
+
+    AZ::Render::MaterialAssignmentLabelMap GNRenderMesh::GetMaterialLabels() const
+    {
+        return AZ::Render::GetMaterialSlotLabelsFromModelAsset(m_modelAsset);
+    }
+
+    AZ::Render::MaterialAssignmentMap GNRenderMesh::GetDefautMaterialMap() const
+    {
+        return AZ::Render::GetDefautMaterialMapFromModelAsset(m_modelAsset);
+    }
+
+    AZStd::unordered_set<AZ::Name> GNRenderMesh::GetModelUvNames() const
+    {
+        const AZ::Data::Instance<AZ::RPI::Model> model = GetModel();
+        return model ? model->GetUvNames() : AZStd::unordered_set<AZ::Name>();
+    }
+
+    void GNRenderMesh::OnMaterialsUpdated(const AZ::Render::MaterialAssignmentMap& materials)
+    {
+        if (m_meshFeatureProcessor)
+        {
+            m_meshFeatureProcessor->SetCustomMaterials(m_meshHandle, AZ::Render::ConvertToCustomMaterialMap(materials));
+        }
+    }
+
+    AZ::Data::Instance<AZ::RPI::Model> GNRenderMesh::GetModel() const
+    {
+        return m_model;
+    }
+
+    void GNRenderMesh::SetVisiblity(bool visibility)
+    {
+        m_visible = visibility;
+        m_meshFeatureProcessor->SetVisible(m_meshHandle, m_visible);
+    }
+
+    bool GNRenderMesh::IsVisible() const
+    {
+        return m_visible;
+    }
+
+    const AZ::Render::MeshFeatureProcessorInterface::MeshHandle* GNRenderMesh::GetMeshHandle() const
+    {
+        return &m_meshHandle;
+    }
+} // namespace GeomNodes

+ 156 - 0
Gems/O3DE/GeomNodes/Code/Source/Editor/Rendering/GNRenderMesh.h

@@ -0,0 +1,156 @@
+/*
+ * 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/TickBus.h>
+
+#include <Editor/Rendering/Atom/GNAttributeBuffer.h>
+#include <Editor/Rendering/Atom/GNBuffer.h>
+#include <Editor/Rendering/GNModelData.h>
+
+#include <Atom/Feature/Mesh/MeshFeatureProcessorInterface.h>
+#include <AtomLyIntegration/CommonFeatures/Material/MaterialComponentBus.h>
+#include <AtomLyIntegration/CommonFeatures/Mesh/MeshHandleStateBus.h>
+#include <AzCore/Component/TransformBus.h>
+#include <AzCore/Name/Name.h>
+
+
+#include <GeomNodes/GeomNodesTypeIds.h>
+namespace AZ::RPI
+{
+    class ModelLodAsset;
+    class ModelAsset;
+    class Model;
+} // namespace AZ::RPI
+
+namespace GeomNodes
+{
+    //! An implementation to support Atom rendering for the GeomNodes gem.
+    //! This is very similar tho how WhiteBox's AtomRenderMesh implementation. Would be nice if some classes can be extensible or reusable.
+    class GNRenderMesh
+        : private AZ::Render::MeshHandleStateRequestBus::Handler
+        , public AZ::Render::MaterialConsumerRequestBus::Handler
+        , public AZ::Render::MaterialComponentNotificationBus::Handler
+        , private AZ::TickBus::Handler
+    {
+    public:
+        AZ_RTTI(GNRenderMesh, GNRenderMeshTypeId);
+
+        explicit GNRenderMesh(AZ::EntityId entityId);
+        ~GNRenderMesh();
+
+        //! For building the mesh.
+        void BuildMesh(const GNModelData& renderData);
+        //! Updates the models transform
+        void UpdateTransform(const AZ::Transform& worldFromLocal);
+        //! Returns if the Model/Mesh is visible or not.
+        bool IsVisible() const;
+        //! Sets the visibility of the Model/Mesh
+        void SetVisiblity(bool visibility);
+
+        // AZ::TickBus overrides ...
+        void OnTick(float deltaTime, AZ::ScriptTimePoint time) override;
+
+        // MaterialConsumerRequestBus::Handler overrides...
+        AZ::Render::MaterialAssignmentId FindMaterialAssignmentId(
+            const AZ::Render::MaterialAssignmentLodIndex lod, const AZStd::string& label) const override;
+        AZ::Render::MaterialAssignmentLabelMap GetMaterialLabels() const override;
+        AZ::Render::MaterialAssignmentMap GetDefautMaterialMap() const override;
+        AZStd::unordered_set<AZ::Name> GetModelUvNames() const override;
+
+        // MaterialComponentNotificationBus::Handler overrides...
+        void OnMaterialsUpdated(const AZ::Render::MaterialAssignmentMap& materials) override;
+
+        //! Returns the Model's data instance
+        AZ::Data::Instance<AZ::RPI::Model> GetModel() const;
+
+        //! Sets the material paths used by the model. These are full paths with the azmaterial extension.
+        void SetMaterialPathList(const AZStd::vector<AZStd::string>& materialPaths);
+
+    private:
+        //! Creates an attribute buffer in the slot dictated by AttributeTypeT.
+        template<AttributeType AttributeTypeT, typename VertexStreamDataType>
+        void CreateAttributeBuffer(const AZStd::vector<VertexStreamDataType>& data)
+        {
+            const auto attribute_index = static_cast<size_t>(AttributeTypeT);
+            m_attributes[attribute_index] = AZStd::make_unique<AttributeBuffer<AttributeTypeT>>(data);
+        }
+
+        //! Updates an attribute buffer in the slot dictated by AttributeTypeT.
+        template<AttributeType AttributeTypeT, typename VertexStreamDataType>
+        void UpdateAttributeBuffer(const AZStd::vector<VertexStreamDataType>& data)
+        {
+            const auto attribute_index = static_cast<size_t>(AttributeTypeT);
+            auto& att = AZStd::get<attribute_index>(m_attributes[attribute_index]);
+            att->UpdateData(data);
+        }
+
+        // MeshHandleStateRequestBus overrides ...
+        const AZ::Render::MeshFeatureProcessorInterface::MeshHandle* GetMeshHandle() const override;
+
+        //! Creates the attribute mesh buffers
+        bool CreateMeshBuffers(const GNModelData& modelData);
+        //! Creates everything related to the model for Atom Rendering.
+        bool CreateMesh(const GNModelData& modelData);
+        //! Using the ModelLodAssetCreator creates the Model LOD asset and the meshes.
+        bool CreateLodAsset(const GNModelData& modelData);
+        //! Using the ModelAssetCreator creates the model asset and sets the material slots.
+        void CreateModelAsset();
+        //! Final step for create the model by acquiring the mesh handle from the mesh feature processor
+        bool CreateModel();
+        //! Add LOD buffers to ModelLodAsset
+        void AddLodBuffers(AZ::RPI::ModelLodAssetCreator& modelLodCreator);
+        //! Add Mesh Buffers to ModelLodAsset
+        void AddMeshBuffers(AZ::RPI::ModelLodAssetCreator& modelLodCreator, const GNMeshData& meshData);
+        //! Checks if attributes are valid.
+        bool AreAttributesValid() const;
+        bool DoesMeshRequireFullRebuild(const GNMeshData& meshData) const;
+        //! Set the material map in the mesh feature processor.
+        void SetMaterials();
+
+        //! Stores the assigned EntityId.
+        AZ::EntityId m_entityId;
+        //! Reference to a ModelLodAsset
+        AZ::Data::Asset<AZ::RPI::ModelLodAsset> m_lodAsset;
+        //! Reference to a ModelAsset
+        AZ::Data::Asset<AZ::RPI::ModelAsset> m_modelAsset;
+        //! Reference to a Model instance
+        AZ::Data::Instance<AZ::RPI::Model> m_model;
+        //! Reference to MeshFeatureProcessor
+        AZ::Render::MeshFeatureProcessorInterface* m_meshFeatureProcessor = nullptr;
+        //! Reference to MeshHandle
+        AZ::Render::MeshFeatureProcessorInterface::MeshHandle m_meshHandle;
+        //! Material map
+        AZ::Render::MaterialAssignmentMap m_materialMap;
+        //! List of Material paths (i.e. azmodel)
+        AZStd::vector<AZStd::string> m_materialPathList;
+        //! LOD Index buffer
+        AZStd::unique_ptr<IndexBuffer> m_indexBuffer;
+        //! Buffers
+        AZStd::array<
+            AZStd::variant<
+                AZStd::unique_ptr<PositionAttribute>,
+                AZStd::unique_ptr<NormalAttribute>,
+                AZStd::unique_ptr<TangentAttribute>,
+                AZStd::unique_ptr<BitangentAttribute>,
+                AZStd::unique_ptr<UVAttribute>,
+                AZStd::unique_ptr<ColorAttribute>>,
+            NumAttributes>
+            m_attributes;
+        //! For toggling visibility
+        bool m_visible = true;
+
+        //! model name.
+        static constexpr AZStd::string_view ModelName = "GeomNodesMesh";
+
+        //! current model transform
+        AZ::Transform m_transform;
+        //! current model scale
+        AZ::Vector3 m_scale;
+    };
+} // namespace GeomNodes

+ 181 - 0
Gems/O3DE/GeomNodes/Code/Source/Editor/Systems/GNInstance.cpp

@@ -0,0 +1,181 @@
+/*
+ * 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 "GNInstance.h"
+#include <Editor/Common/GNAPI.h>
+#include <Editor/Common/GNConstants.h>
+#include <Editor/Systems/GeomNodesSystem.h>
+
+
+namespace GeomNodes
+{
+    GNInstance::~GNInstance()
+    {
+        Cleanup();
+    }
+
+    void GNInstance::Cleanup()
+    {
+        if (m_blenderProcessWatcher)
+        {
+            m_blenderProcessWatcher->TerminateProcess(0);
+
+            m_blenderProcessWatcher = nullptr;
+        }
+    }
+
+    bool GNInstance::IsValid()
+    {
+        return m_blenderProcessWatcher && m_blenderProcessWatcher->IsProcessRunning();
+    }
+
+    bool GNInstance::Init(
+        const AZStd::string& filePath, const AZStd::string& scriptPath, const AZStd::string& exePath, AZ::EntityId entityId)
+    {
+        m_entityId = entityId;
+        m_path = filePath;
+        m_scriptPath = scriptPath;
+        m_exePath = exePath;
+
+        return RestartProcess();
+    }
+
+    bool GNInstance::IsSamePath(const AZStd::string& path)
+    {
+        if (m_path.empty())
+            return false;
+
+        if (path == m_path)
+            return true;
+
+        return false;
+    }
+
+    void GNInstance::SendIPCMsg(const AZStd::string& content)
+    {
+        API::SendMsg(content.c_str(), content.size(), (AZ::u64)m_entityId);
+    }
+
+    bool GNInstance::RestartProcess()
+    {
+        AzFramework::ProcessLauncher::ProcessLaunchInfo processLaunchInfo;
+        AZStd::string blenderPath = GetGNSystem()->GetBlenderPath();
+        if (blenderPath.empty())
+        {
+            AZ_Error("GNInstance", false, "Blender instance failed to launch! Blender path is not set or valid.");
+            return false;
+        }
+
+        processLaunchInfo.m_commandlineParameters = AZStd::string::format(
+            R"(%s --factory-startup -b "%s" -P "%s" -- "%s" %llu)",
+            blenderPath.c_str(),
+            m_path.c_str(),
+            m_scriptPath.c_str(),
+            m_exePath.c_str(),
+            (AZ::u64)m_entityId);
+        // NOTE: if you want to debug and see the console print outs set m_showWindow to true
+        processLaunchInfo.m_showWindow = false;
+        processLaunchInfo.m_processPriority = AzFramework::ProcessPriority::PROCESSPRIORITY_NORMAL;
+
+        AzFramework::ProcessWatcher* outProcess =
+            AzFramework::ProcessWatcher::LaunchProcess(processLaunchInfo, AzFramework::ProcessCommunicationType::COMMUNICATOR_TYPE_NONE);
+
+        if (outProcess)
+        {
+            // Stop the previous server if one exists
+            if (m_blenderProcessWatcher)
+            {
+                m_blenderProcessWatcher->TerminateProcess(0);
+            }
+
+            m_blenderProcessWatcher.reset(outProcess);
+        }
+        else
+        {
+            AZ_Error("GNInstance", outProcess, "Blender instance failed to launch! Unable to create AzFramework::ProcessWatcher.");
+            return false;
+        }
+
+        return true;
+    }
+
+    AZStd::string GNInstance::SendParamUpdates(const AZStd::string& params, const AZStd::string& objectName)
+    {
+        auto msg = AZStd::string::format(
+            R"JSON(
+                {
+                    "%s": [ %s ],
+                    "%s": "%s",
+                    "ParamUpdate": true
+                }
+            )JSON",
+            Field::Params,
+            params.c_str(),
+            Field::Object,
+            objectName.c_str());
+
+        SendIPCMsg(msg);
+
+        return msg;
+    }
+
+    void GNInstance::SendHeartbeat()
+    {
+        SendIPCMsg(R"JSON(
+                        {
+                            "Alive": true 
+                        }
+                    )JSON");
+    }
+
+    void GNInstance::RequestObjectParams()
+    {
+        SendIPCMsg(R"JSON(
+                        {
+                            "FetchObjectParams": true 
+                        }
+                    )JSON");
+    }
+
+    void GNInstance::CloseMap(AZ::u64 mapId)
+    {
+        auto msg = AZStd::string::format(
+            R"JSON(
+                    {
+                        "%s": true,
+                        "%s": %llu
+                    }
+                    )JSON",
+            Field::SHMClose,
+            Field::MapId,
+            mapId);
+        SendIPCMsg(msg);
+    }
+
+    void GNInstance::RequestExport(const AZStd::string& params, const AZStd::string& objectName, const AZStd::string& fbxPath)
+    {
+        auto msg = AZStd::string::format(
+            R"JSON(
+                    {
+                        "%s": true,
+                        "%s": [ %s ],
+                        "%s": "%s",
+                        "%s": "%s"
+                    }
+                    )JSON",
+            Field::Export,
+            Field::Params,
+            params.c_str(),
+            Field::Object,
+            objectName.c_str(),
+            Field::FBXPath,
+            fbxPath.c_str());
+        SendIPCMsg(msg);
+    }
+
+} // namespace GeomNodes

+ 64 - 0
Gems/O3DE/GeomNodes/Code/Source/Editor/Systems/GNInstance.h

@@ -0,0 +1,64 @@
+/*
+ * 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 "Editor/Commons.h"
+#include <AzCore/Component/EntityId.h>
+#include <AzCore/Component/TickBus.h>
+#include <AzFramework/Process/ProcessWatcher.h>
+
+
+namespace GeomNodes
+{
+    //! Handles the Blender instance and communication between Gem and Client Script
+    class GNInstance
+    {
+    public:
+        GNInstance() = default;
+        virtual ~GNInstance();
+
+        //! Standard initialization function.
+        bool Init(const AZStd::string& filePath, const AZStd::string& scriptPath, const AZStd::string& exePath, AZ::EntityId entityId);
+        //! Do some cleanup. Like terminating the running process.
+        void Cleanup();
+        //! Check if the Blender instance is still valid or running.
+        bool IsValid();
+        //! Checks if the path provided is the same one as the Blender instance is currently using.
+        bool IsSamePath(const AZStd::string& path);
+        //! Send a generic IPC message.
+        void SendIPCMsg(const AZStd::string& content);
+        //! Start or Restart the Blender instance.
+        bool RestartProcess();
+
+        //! IPC messaging
+        //! Send Parameter updates so the client can update the Geometry Node in the Blender side.
+        AZStd::string SendParamUpdates(const AZStd::string& params, const AZStd::string& objectName);
+        //! Send a Heartbeat message to the client so it knows the Gem is still running.
+        void SendHeartbeat();
+        //! Request for Object Parameter info from the client script.
+        void RequestObjectParams();
+        //! Tells the client script to close the SHM map on the client side as the gem will manage it.
+        void CloseMap(AZ::u64 mapId);
+        //! Tells the client script that we need to export and write it to an FBX file.
+        void RequestExport(const AZStd::string& params, const AZStd::string& objectName, const AZStd::string& fbxPath);
+
+    private:
+        //! Blender instance's Process Watcher
+        AZStd::unique_ptr<AzFramework::ProcessWatcher> m_blenderProcessWatcher = nullptr;
+
+        //! Stores assigned EntityId
+        AZ::EntityId m_entityId;
+        //! Stores the blender file path that has a Geometry Nodes Modifier
+        AZStd::string m_path = "";
+        //! Stores the script path. i.e. <GeomNodes Gem Path>/External/Scripts/__init__.py
+        AZStd::string m_scriptPath = "";
+        //! Stores the path where Bridge.dll is located
+        AZStd::string m_exePath = "";
+    };
+} // namespace GeomNodes

+ 269 - 0
Gems/O3DE/GeomNodes/Code/Source/Editor/Systems/GNParamContext.cpp

@@ -0,0 +1,269 @@
+/*
+ * 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 "GNParamContext.h"
+
+#include <AzCore/Serialization/EditContext.h>
+#include <AzCore/Serialization/SerializeContext.h>
+#include <Editor/Systems/GNProperty.h>
+
+namespace GeomNodes
+{
+    // templates
+    bool GNValue<bool>::Read(const rapidjson::Value& val)
+    {
+        return val.GetInt();
+    }
+
+    int GNValue<int>::Read(const rapidjson::Value& val)
+    {
+        return val.GetInt();
+    }
+
+    double GNValue<double>::Read(const rapidjson::Value& val)
+    {
+        return val.GetDouble();
+    }
+
+    const char* GNValue<const char*>::Read(const rapidjson::Value& val)
+    {
+        return val.GetString();
+    }
+
+    // GNParamContext
+    GNParamContext::GNParamContext()
+    {
+        m_impl = aznew GNParamContextImpl();
+        m_group.m_name = "Geometry Nodes Parameters";
+    }
+
+    GNParamContext::~GNParamContext()
+    {
+        m_group.Clear();
+        delete m_impl;
+    }
+
+    void GNParamContext::Reflect(AZ::ReflectContext* reflection)
+    {
+        AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflection);
+        if (serializeContext)
+        {
+            // we may have been reflected by EditorGeomNodesComponent already, so check first
+            if (serializeContext->FindClassData(AZ::Uuid("{AA9713B7-70F1-43CB-9F95-5BEC9F44F556}")) == nullptr)
+            {
+                serializeContext->Class<GNParamContext>()->Version(2)->Field("Properties", &GNParamContext::m_group);
+
+                serializeContext->Class<GNPropertyGroup>()
+                    ->Field("Name", &GNPropertyGroup::m_name)
+                    ->Field("Properties", &GNPropertyGroup::m_properties)
+                    ->Field("Groups", &GNPropertyGroup::m_groups);
+
+                // reflect all properties
+                GNProperties::Reflect(reflection);
+            }
+        }
+    }
+
+    GNProperty* GNParamContext::ConstructGNParam(GNParamDataContext& gndc, ParamType pType, const char* name)
+    {
+        return m_impl->ConstructGNParam(gndc, pType, name);
+    }
+
+    //=========================================================================
+    // GetGroup
+    //=========================================================================
+    GNPropertyGroup* GNPropertyGroup::GetGroup(const char* groupName)
+    {
+        for (GNPropertyGroup& subGroup : m_groups)
+        {
+            if (subGroup.m_name == groupName)
+            {
+                return &subGroup;
+            }
+        }
+        return nullptr;
+    }
+
+    //=========================================================================
+    // GetProperty
+    //=========================================================================
+    GNProperty* GNPropertyGroup::GetProperty(const char* propertyName)
+    {
+        for (GNProperty* prop : m_properties)
+        {
+            if (prop->m_name == propertyName)
+            {
+                return prop;
+            }
+        }
+        return nullptr;
+    }
+
+    AZStd::string GNPropertyGroup::GetProperties()
+    {
+        AZStd::string jsonString = "";
+
+        AZ::u64 uCtr = m_properties.size();
+        for (GNProperty* prop : m_properties)
+        {
+            jsonString += prop->ToJSONString();
+            uCtr--;
+            if (uCtr > 0)
+                jsonString += ", ";
+        }
+
+        return jsonString;
+    }
+
+    //=========================================================================
+    // Clear
+    //=========================================================================
+    void GNPropertyGroup::Clear()
+    {
+        for (GNProperty* prop : m_properties)
+        {
+            delete prop;
+        }
+        m_properties.clear();
+        m_groups.clear();
+    }
+
+    //=========================================================================
+    // ~GNPropertyGroup
+    //=========================================================================
+    GNPropertyGroup::~GNPropertyGroup()
+    {
+        Clear();
+    }
+
+    //=========================================================================
+    // operator=
+    //=========================================================================
+    GNPropertyGroup& GNPropertyGroup::operator=(GNPropertyGroup&& rhs)
+    {
+        m_name.swap(rhs.m_name);
+        m_properties.swap(rhs.m_properties);
+        m_groups.swap(rhs.m_groups);
+
+        return *this;
+    }
+
+    const char* GetEnumString(ParamType value)
+    {
+        switch (value)
+        {
+        case ParamType::Bool:
+            return "BOOLEAN";
+        case ParamType::Int:
+            return "INT";
+        case ParamType::Value:
+            return "VALUE";
+        case ParamType::String:
+            return "STRING";
+        }
+
+        return "UNKNOWN";
+    }
+
+    ParamType GetTypeFromString(const char* value)
+    {
+        if (strcmp("BOOLEAN", value) == 0)
+        {
+            return ParamType::Bool;
+        }
+        else if (strcmp("INT", value) == 0)
+        {
+            return ParamType::Int;
+        }
+        else if (strcmp("VALUE", value) == 0)
+        {
+            return ParamType::Value;
+        }
+        else if (strcmp("STRING", value) == 0)
+        {
+            return ParamType::String;
+        }
+
+        return ParamType::Unknown;
+    }
+
+    // GNParamDataContext
+
+    bool GNParamDataContext::IsNil(int index) const
+    {
+        return index == (int)ParamType::Unknown;
+    }
+
+    bool GNParamDataContext::IsBoolean(int index) const
+    {
+        return index == (int)ParamType::Bool;
+    }
+
+    bool GNParamDataContext::IsInt(int index) const
+    {
+        return index == (int)ParamType::Int;
+    }
+
+    bool GNParamDataContext::IsValue(int index) const
+    {
+        return index == (int)ParamType::Value;
+    }
+
+    bool GNParamDataContext::IsString(int index) const
+    {
+        return index == (int)ParamType::String;
+    }
+
+    const char* GNParamDataContext::GetParamName()
+    {
+        if (m_curParamObj == nullptr)
+            return nullptr;
+
+        return (*m_curParamObj)[Field::Name].GetString();
+    }
+
+    ParamType GNParamDataContext::GetParamType()
+    {
+        if (m_curParamObj == nullptr)
+            return ParamType::Unknown;
+
+        return GetTypeFromString((*m_curParamObj)[Field::Type].GetString());
+    }
+
+    // GNParamContextImpl
+    GNParamContextImpl::GNParamContextImpl()
+        : m_paramFactories({
+              // Nil
+              &GNParamNil::TryCreateProperty,
+
+              // Values
+              &GNParamBoolean::TryCreateProperty,
+              &GNParamInt::TryCreateProperty,
+              &GNParamValue::TryCreateProperty,
+              &GNParamString::TryCreateProperty,
+          })
+    {
+    }
+
+    GNProperty* GNParamContextImpl::ConstructGNParam(GNParamDataContext& gndc, ParamType pType, const char* name)
+    {
+        GNProperty* gnParam = nullptr;
+
+        for (ParamTypeFactory factory : m_paramFactories)
+        {
+            gnParam = factory(gndc, (int)pType, name);
+
+            if (gnParam != nullptr)
+            {
+                break;
+            }
+        }
+
+        return gnParam;
+    }
+} // namespace GeomNodes

+ 208 - 0
Gems/O3DE/GeomNodes/Code/Source/Editor/Systems/GNParamContext.h

@@ -0,0 +1,208 @@
+/*
+ * 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/JSON/document.h>
+#include <AzCore/JSON/rapidjson.h>
+#include <AzCore/RTTI/ReflectContext.h>
+#include <Editor/Common/GNConstants.h>
+#include <GeomNodes/GeomNodesTypeIds.h>
+
+
+namespace GeomNodes
+{
+    class GNProperty;
+    class GNParamContextImpl;
+    class GNParamContext;
+
+    enum class ParamType : AZ::u8
+    {
+        Bool,
+        Int,
+        Value,
+        String,
+        StringComboBox,
+
+        Unknown
+    };
+
+    const char* GetEnumString(ParamType value);
+    ParamType GetTypeFromString(const char* value);
+
+    template<class T>
+    struct GNValue
+    {
+        typedef
+            typename AZStd::remove_const<typename AZStd::remove_reference<typename AZStd::remove_pointer<T>::type>::type>::type ValueType;
+    };
+
+    template<>
+    struct GNValue<bool>
+    {
+        static const bool isNativeValueType = true; // We use native type for internal representation
+        static bool Read(const rapidjson::Value& val);
+    };
+
+    template<>
+    struct GNValue<const char*>
+    {
+        static const bool isNativeValueType = true; // We use native type for internal representation
+        static const char* Read(const rapidjson::Value& val);
+    };
+
+    template<>
+    struct GNValue<int>
+    {
+        static const bool isNativeValueType = true; // We use native type for internal representation
+        static int Read(const rapidjson::Value& val);
+    };
+
+    template<>
+    struct GNValue<double>
+    {
+        static const bool isNativeValueType = true; // We use native type for internal representation
+        static double Read(const rapidjson::Value& val);
+    };
+
+    struct GNPropertyGroup
+    {
+        AZ_TYPE_INFO(GNPropertyGroup, GNPropertyGroupTypeId);
+        AZStd::string m_name;
+        AZStd::vector<GNProperty*> m_properties;
+        AZStd::vector<GNPropertyGroup> m_groups;
+
+        //! Get the pointer to the specified group in m_groups. Returns nullptr if not found.
+        GNPropertyGroup* GetGroup(const char* groupName);
+        //! Get the pointer to the specified property in m_properties. Returns nullptr if not found.
+        GNProperty* GetProperty(const char* propertyName);
+        //! Generate JSON string of all properties/parameters. NOTE: only select details are included.
+        AZStd::string GetProperties();
+
+        //! Remove all properties and groups
+        void Clear();
+
+        GNPropertyGroup() = default;
+        ~GNPropertyGroup();
+
+        GNPropertyGroup(const GNPropertyGroup& rhs) = delete;
+        GNPropertyGroup& operator=(GNPropertyGroup&) = delete;
+
+    public:
+        GNPropertyGroup(GNPropertyGroup&& rhs)
+        {
+            *this = AZStd::move(rhs);
+        }
+        GNPropertyGroup& operator=(GNPropertyGroup&& rhs);
+    };
+
+    class GNParamDataContext
+    {
+        friend GNParamContext;
+
+    public:
+        AZ_TYPE_INFO(GNParamDataContext, GNParamDataContextTypeId);
+
+        GNParamDataContext()
+        {
+        }
+
+        bool IsNil(int index) const;
+        bool IsBoolean(int index) const;
+        bool IsInt(int index) const;
+        bool IsValue(int index) const;
+        bool IsString(int index) const;
+
+        template<class T>
+        bool ReadValue(T& valueRef, const char* key) const;
+
+        void SetParamObject(const rapidjson::Value* value)
+        {
+            m_curParamObj = value;
+        }
+        void ClearParamObject()
+        {
+            m_curParamObj = nullptr;
+        }
+
+        void SetReadOnlyPointer(bool* pReadOnly)
+        {
+            m_pReadOnly = pReadOnly;
+        }
+
+        bool* GetReadOnlyPointer()
+        {
+            return m_pReadOnly;
+        }
+
+        void SetEntityId(AZ::EntityId entityId)
+        {
+            m_entityId = entityId;
+        }
+
+        AZ::EntityId GetEntityId()
+        {
+            return m_entityId;
+        }
+
+        const char* GetParamName();
+        ParamType GetParamType();
+
+    protected:
+        const rapidjson::Value* m_curParamObj;
+        bool* m_pReadOnly;
+        AZ::EntityId m_entityId;
+    };
+
+    template<class T>
+    inline bool GNParamDataContext::ReadValue(T& valueRef, const char* key) const
+    {
+        if (m_curParamObj == nullptr || !(*m_curParamObj).HasMember(key))
+            return false;
+
+        valueRef = GNValue<T>::Read((*m_curParamObj)[key]);
+
+        return true;
+    }
+
+    class GNParamContextImpl
+    {
+    public:
+        typedef GNProperty* (*ParamTypeFactory)(GNParamDataContext& context, int valueIndex, const char* name);
+
+        AZ_CLASS_ALLOCATOR(GNParamContextImpl, AZ::SystemAllocator);
+
+        GNParamContextImpl();
+        ~GNParamContextImpl() = default;
+
+        GNProperty* ConstructGNParam(GNParamDataContext& gndc, ParamType pType, const char* name);
+        AZStd::vector<ParamTypeFactory> m_paramFactories;
+    };
+
+    class GNParamContext
+    {
+        friend class EditorGeomNodesComponent;
+
+    public:
+        AZ_CLASS_ALLOCATOR(GNParamContext, AZ::SystemAllocator);
+
+        AZ_TYPE_INFO(GeomNodes::GNParamContext, GNParamContextTypeId);
+
+        GNParamContext();
+        ~GNParamContext();
+
+        static void Reflect(AZ::ReflectContext* reflection);
+
+        GNProperty* ConstructGNParam(GNParamDataContext& gndc, ParamType pType, const char* name);
+
+    protected:
+        GNPropertyGroup m_group;
+
+        GNParamContextImpl* m_impl;
+    };
+} // namespace GeomNodes

+ 384 - 0
Gems/O3DE/GeomNodes/Code/Source/Editor/Systems/GNProperty.cpp

@@ -0,0 +1,384 @@
+/*
+ * 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 "GNProperty.h"
+
+namespace GeomNodes
+{
+    void GNProperties::Reflect(AZ::ReflectContext* reflection)
+    {
+        GNProperty::Reflect(reflection);
+
+        GNParamBoolean::Reflect(reflection);
+        GNParamInt::Reflect(reflection);
+        GNParamValue::Reflect(reflection);
+        GNParamString::Reflect(reflection);
+    }
+
+    void GNProperty::Reflect(AZ::ReflectContext* reflection)
+    {
+        AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflection);
+
+        if (serializeContext)
+        {
+            serializeContext->Class<GNProperty>()
+                ->Version(2, GNProperty::VersionConverter)
+                ->PersistentId(
+                    [](const void* instance) -> AZ::u64
+                    {
+                        return reinterpret_cast<const GNProperty*>(instance)->m_id;
+                    })
+                ->Field("id", &GNProperty::m_id)
+                ->Field("name", &GNProperty::m_name)
+                ->Field("gnId", &GNProperty::m_gnId);
+        }
+    }
+
+    bool GNProperty::VersionConverter(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement)
+    {
+        if (classElement.GetVersion() == 1)
+        {
+            // Generate persistent Id field.
+            for (int i = 0; i < classElement.GetNumSubElements(); ++i)
+            {
+                AZ::SerializeContext::DataElementNode& elementNode = classElement.GetSubElement(i);
+                if (elementNode.GetName() == AZ_CRC("name", 0x5e237e06))
+                {
+                    AZStd::string name;
+                    if (elementNode.GetData(name))
+                    {
+                        const int idx = classElement.AddElement<AZ::u64>(context, "id");
+                        const AZ::u32 crc = AZ::Crc32(name.c_str());
+                        classElement.GetSubElement(idx).SetData<AZ::u64>(context, crc);
+                    }
+                }
+            }
+        }
+
+        return true;
+    }
+
+    void GNProperty::ReadSetGNId(GNParamDataContext& context)
+    {
+        const char* gnId;
+        if (context.ReadValue(gnId, Field::Id))
+        {
+            m_gnId = gnId;
+        }
+    }
+
+    //////////////////////
+    // GNParamNil
+    //////////////////////
+
+    void GNParamNil::Reflect(AZ::ReflectContext* reflection)
+    {
+        AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflection);
+
+        if (serializeContext)
+        {
+            serializeContext->Class<GNParamNil, GNProperty>()->Version(1)->SerializeWithNoData();
+        }
+    }
+
+    GNProperty* GNParamNil::TryCreateProperty(GNParamDataContext& context, int valueIndex, const char* name)
+    {
+        GNProperty* retVal = nullptr;
+        if (context.IsNil(valueIndex))
+        {
+            retVal = aznew GNParamNil(name, context.GetReadOnlyPointer(), context.GetEntityId());
+        }
+
+        return retVal;
+    }
+
+    const void* GNParamNil::GetDataAddress() const
+    {
+        return nullptr;
+    }
+
+    AZ::TypeId GNParamNil::GetDataTypeUuid() const
+    {
+        return AZ::SerializeTypeInfo<void*>::GetUuid();
+    }
+
+    AZStd::string GNParamNil::ToJSONString() const
+    {
+        return AZStd::string();
+    }
+
+    //////////////////////////
+    // GNParamBoolean
+    //////////////////////////
+    void GNParamBoolean::Reflect(AZ::ReflectContext* reflection)
+    {
+        AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflection);
+
+        if (serializeContext)
+        {
+            serializeContext->Class<GNParamBoolean, GNProperty>()->Version(1)->Field("value", &GNParamBoolean::m_value);
+        }
+    }
+
+    GNProperty* GNParamBoolean::TryCreateProperty(GNParamDataContext& context, int valueIndex, const char* name)
+    {
+        GNProperty* retVal = nullptr;
+
+        if (context.IsBoolean(valueIndex))
+        {
+            bool value;
+            if (!context.ReadValue(value, Field::DefaultValue))
+            {
+                if (!context.ReadValue(value, Field::Value))
+                {
+                    AZ_Error("GNParamBoolean", false, "Missing DefaultValue or Value keys in the JSON data\n");
+                }
+            }
+
+            retVal = aznew GNParamBoolean(name, value, context.GetReadOnlyPointer(), context.GetEntityId());
+            retVal->ReadSetGNId(context);
+        }
+
+        return retVal;
+    }
+
+    AZStd::string GNParamBoolean::ToJSONString() const
+    {
+        auto jsonString = AZStd::string::format(
+            R"JSON(
+                    {
+                        "%s": "%s",
+                        "%s": %i,
+                        "%s": "%s",
+                        "%s": "%s"
+                    }
+                )JSON",
+            Field::Id,
+            m_gnId.c_str(),
+            Field::Value,
+            m_value ? 1 : 0,
+            Field::Type,
+            m_type.c_str(),
+            Field::Name,
+            m_name.c_str());
+        return jsonString;
+    }
+
+    AZ::TypeId GNParamBoolean::GetDataTypeUuid() const
+    {
+        return AZ::SerializeTypeInfo<bool>::GetUuid();
+    }
+
+    /////////////////////////
+    // GNParamInt
+    /////////////////////////
+    void GNParamInt::Reflect(AZ::ReflectContext* reflection)
+    {
+        AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflection);
+
+        if (serializeContext)
+        {
+            serializeContext->Class<GNParamInt, GNProperty>()->Version(1)->Field("value", &GNParamInt::m_value);
+        }
+    }
+
+    GNProperty* GNParamInt::TryCreateProperty(GNParamDataContext& context, int valueIndex, const char* name)
+    {
+        GNProperty* retVal = nullptr;
+
+        if (context.IsInt(valueIndex))
+        {
+            int value;
+            if (!context.ReadValue(value, Field::DefaultValue))
+            {
+                if (!context.ReadValue(value, Field::Value))
+                {
+                    AZ_Error("GNParamInt", false, "Missing DefaultValue or Value keys in the JSON data\n");
+                }
+            }
+            auto paramInt = aznew GNParamInt(name, value, context.GetReadOnlyPointer(), context.GetEntityId());
+
+            int min, max;
+            if (context.ReadValue(min, Field::MinValue))
+            {
+                paramInt->SetMinValue(min);
+            }
+
+            if (context.ReadValue(max, Field::MaxValue))
+            {
+                paramInt->SetMaxValue(max);
+            }
+
+            retVal = paramInt;
+            retVal->ReadSetGNId(context);
+        }
+
+        return retVal;
+    }
+
+    AZ::TypeId GNParamInt::GetDataTypeUuid() const
+    {
+        return AZ::SerializeTypeInfo<int>::GetUuid();
+    }
+
+    AZStd::string GNParamInt::ToJSONString() const
+    {
+        auto jsonString = AZStd::string::format(
+            R"JSON(
+                    {
+                        "%s": "%s",
+                        "%s": %i,
+                        "%s": "%s",
+                        "%s": "%s"
+                    }
+                )JSON",
+            Field::Id,
+            m_gnId.c_str(),
+            Field::Value,
+            m_value,
+            Field::Type,
+            m_type.c_str(),
+            Field::Name,
+            m_name.c_str());
+        return jsonString;
+    }
+
+    /////////////////////////
+    // GNParamValue
+    /////////////////////////
+    void GNParamValue::Reflect(AZ::ReflectContext* reflection)
+    {
+        AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflection);
+
+        if (serializeContext)
+        {
+            serializeContext->Class<GNParamValue, GNProperty>()->Version(1)->Field("value", &GNParamValue::m_value);
+        }
+    }
+
+    GNProperty* GNParamValue::TryCreateProperty(GNParamDataContext& context, int valueIndex, const char* name)
+    {
+        GNProperty* retVal = nullptr;
+
+        if (context.IsValue(valueIndex))
+        {
+            double value;
+            if (!context.ReadValue(value, Field::DefaultValue))
+            {
+                if (!context.ReadValue(value, Field::Value))
+                {
+                    AZ_Error("GNParamValue", false, "Missing DefaultValue or Value keys in the JSON data\n");
+                }
+            }
+            auto paramValue = aznew GNParamValue(name, value, context.GetReadOnlyPointer(), context.GetEntityId());
+            double min, max;
+            if (context.ReadValue(min, Field::MinValue))
+            {
+                paramValue->SetMinValue(min);
+            }
+
+            if (context.ReadValue(max, Field::MaxValue))
+            {
+                paramValue->SetMaxValue(max);
+            }
+
+            retVal = paramValue;
+            retVal->ReadSetGNId(context);
+        }
+
+        return retVal;
+    }
+
+    AZ::TypeId GNParamValue::GetDataTypeUuid() const
+    {
+        return AZ::SerializeTypeInfo<double>::GetUuid();
+    }
+
+    AZStd::string GNParamValue::ToJSONString() const
+    {
+        auto jsonString = AZStd::string::format(
+            R"JSON(
+                    {
+                        "%s": "%s",
+                        "%s": %.17f,
+                        "%s": "%s",
+                        "%s": "%s"
+                    }
+                )JSON",
+            Field::Id,
+            m_gnId.c_str(),
+            Field::Value,
+            m_value,
+            Field::Type,
+            m_type.c_str(),
+            Field::Name,
+            m_name.c_str());
+        return jsonString;
+    }
+
+    /////////////////////////
+    // GNParamString
+    /////////////////////////
+    void GNParamString::Reflect(AZ::ReflectContext* reflection)
+    {
+        AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflection);
+
+        if (serializeContext)
+        {
+            serializeContext->Class<GNParamString, GNProperty>()->Version(1)->Field("value", &GNParamString::m_value);
+        }
+    }
+
+    GNProperty* GNParamString::TryCreateProperty(GNParamDataContext& context, int valueIndex, const char* name)
+    {
+        GNProperty* retVal = nullptr;
+
+        if (context.IsString(valueIndex))
+        {
+            const char* value = nullptr;
+            if (!context.ReadValue(value, Field::DefaultValue))
+            {
+                if (!context.ReadValue(value, Field::Value))
+                {
+                    AZ_Error("GNParamString", false, "Missing DefaultValue or Value keys in the JSON data\n");
+                }
+            }
+            retVal = aznew GNParamString(name, value, context.GetReadOnlyPointer(), context.GetEntityId());
+            retVal->ReadSetGNId(context);
+        }
+
+        return retVal;
+    }
+
+    AZ::TypeId GNParamString::GetDataTypeUuid() const
+    {
+        return AZ::SerializeGenericTypeInfo<AZStd::string>::GetClassTypeId();
+    }
+
+    AZStd::string GNParamString::ToJSONString() const
+    {
+        auto jsonString = AZStd::string::format(
+            R"JSON(
+                    {
+                        "%s": "%s",
+                        "%s": "%s",
+                        "%s": "%s",
+                        "%s": "%s"
+                    }
+                )JSON",
+            Field::Id,
+            m_gnId.c_str(),
+            Field::Value,
+            m_value.c_str(),
+            Field::Type,
+            m_type.c_str(),
+            Field::Name,
+            m_name.c_str());
+        return jsonString;
+    }
+} // namespace GeomNodes

+ 266 - 0
Gems/O3DE/GeomNodes/Code/Source/Editor/Systems/GNProperty.h

@@ -0,0 +1,266 @@
+/*
+ * 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/Math/Crc.h>
+#include <AzCore/Memory/SystemAllocator.h>
+#include <AzCore/RTTI/TypeInfoSimple.h>
+#include <AzCore/Serialization/EditContext.h>
+#include <AzCore/Serialization/SerializeContext.h>
+#include <Editor/EBus/EditorGeomNodesComponentBus.h>
+#include <Editor/Systems/GNParamContext.h>
+#include <GeomNodes/GeomNodesTypeIds.h>
+
+namespace AZ
+{
+    class ReflectContext;
+}
+
+namespace GeomNodes
+{
+    class GNProperties
+    {
+    public:
+        static void Reflect(AZ::ReflectContext* reflection);
+    };
+
+    /**
+     * Base class for all script properties.
+     */
+    class GNProperty
+    {
+    public:
+        static void Reflect(AZ::ReflectContext* reflection);
+        static bool VersionConverter(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement);
+
+        virtual ~GNProperty()
+        {
+        }
+        AZ_RTTI(GeomNodes::GNProperty, GNPropertyTypeId);
+
+        GNProperty()
+        {
+        }
+        GNProperty(const char* name, bool* pReadOnly, AZ::EntityId entityId)
+            : m_id(AZ::Crc32(name))
+            , m_name(name)
+            , m_pReadOnly(pReadOnly)
+            , m_entityId(entityId)
+        {
+        }
+
+        virtual const void* GetDataAddress() const = 0;
+        virtual AZ::TypeId GetDataTypeUuid() const = 0;
+        virtual AZStd::string ToJSONString() const = 0;
+
+        virtual void ReadSetGNId(GNParamDataContext& context);
+
+        virtual bool IsReadOnly()
+        {
+            return m_pReadOnly == nullptr ? false : *m_pReadOnly;
+        }
+
+        void OnParamChange()
+        {
+            EditorGeomNodesComponentRequestBus::Event(m_entityId, &EditorGeomNodesComponentRequests::OnParamChange);
+        }
+
+        AZ::u64 m_id;
+        //! Geometry Node Param Id
+        AZStd::string m_gnId;
+        //! Geometry Node Param Name
+        AZStd::string m_name;
+        //! Geometry Node Param Type
+        AZStd::string m_type = "UNKNOWN";
+        bool* m_pReadOnly = nullptr;
+        bool m_isMaxSet = false;
+        bool m_isMinSet = false;
+        AZ::EntityId m_entityId;
+    };
+
+    class GNParamNil : public GNProperty
+    {
+    public:
+        AZ_CLASS_ALLOCATOR(GNParamNil, AZ::SystemAllocator);
+        AZ_RTTI(GeomNodes::GNParamNil, GNParamNilTypeId, GNProperty);
+
+        static void Reflect(AZ::ReflectContext* reflection);
+        static GNProperty* TryCreateProperty(GNParamDataContext& context, int valueIndex, const char* name);
+
+        GNParamNil()
+        {
+        }
+        GNParamNil(const char* name, bool* pReadOnly, AZ::EntityId entityId)
+            : GNProperty(name, pReadOnly, entityId)
+        {
+        }
+
+        const void* GetDataAddress() const override;
+        AZ::TypeId GetDataTypeUuid() const override;
+        AZStd::string ToJSONString() const override;
+    };
+
+    class GNParamBoolean : public GNProperty
+    {
+    public:
+        AZ_CLASS_ALLOCATOR(GNParamBoolean, AZ::SystemAllocator);
+        AZ_RTTI(GeomNodes::GNParamBoolean, GNParamBooleanTypeId, GNProperty);
+
+        static void Reflect(AZ::ReflectContext* reflection);
+        static GNProperty* TryCreateProperty(GNParamDataContext& context, int valueIndex, const char* name);
+
+        GNParamBoolean()
+            : m_value(false)
+        {
+            m_type = GetEnumString(ParamType::Bool);
+        }
+        GNParamBoolean(const char* name, bool value, bool* pReadOnly, AZ::EntityId entityId)
+            : GNProperty(name, pReadOnly, entityId)
+            , m_value(value)
+        {
+            m_type = GetEnumString(ParamType::Bool);
+        }
+
+        const void* GetDataAddress() const override
+        {
+            return &m_value;
+        }
+
+        AZStd::string ToJSONString() const override;
+
+        AZ::TypeId GetDataTypeUuid() const override;
+
+        bool m_value;
+    };
+
+    class GNParamInt : public GNProperty
+    {
+    public:
+        AZ_CLASS_ALLOCATOR(GNParamInt, AZ::SystemAllocator);
+        AZ_RTTI(GeomNodes::GNParamInt, GNParamIntTypeId, GNProperty);
+
+        static void Reflect(AZ::ReflectContext* reflection);
+        static GNProperty* TryCreateProperty(GNParamDataContext& context, int valueIndex, const char* name);
+
+        GNParamInt()
+            : m_value(0)
+        {
+            m_type = GetEnumString(ParamType::Int);
+        }
+        GNParamInt(const char* name, int value, bool* pReadOnly, AZ::EntityId entityId)
+            : GNProperty(name, pReadOnly, entityId)
+            , m_value(value)
+        {
+            m_type = GetEnumString(ParamType::Int);
+        }
+
+        const void* GetDataAddress() const override
+        {
+            return &m_value;
+        }
+        AZ::TypeId GetDataTypeUuid() const override;
+        AZStd::string ToJSONString() const override;
+
+        void SetMinValue(int min)
+        {
+            m_min = min;
+            m_isMinSet = true;
+        }
+
+        void SetMaxValue(int max)
+        {
+            m_max = max;
+            m_isMaxSet = true;
+        }
+
+        int m_value;
+        //! Geometry Node Param Max(int)
+        int m_max;
+        //! Geometry Node Param Min(int)
+        int m_min;
+    };
+
+    class GNParamValue : public GNProperty
+    {
+    public:
+        AZ_CLASS_ALLOCATOR(GNParamValue, AZ::SystemAllocator);
+        AZ_RTTI(GeomNodes::GNParamValue, GNParamValueTypeId, GNProperty);
+
+        static void Reflect(AZ::ReflectContext* reflection);
+        static GNProperty* TryCreateProperty(GNParamDataContext& context, int valueIndex, const char* name);
+
+        GNParamValue()
+            : m_value(0.0f)
+        {
+            m_type = GetEnumString(ParamType::Value);
+        }
+        GNParamValue(const char* name, double value, bool* pReadOnly, AZ::EntityId entityId)
+            : GNProperty(name, pReadOnly, entityId)
+            , m_value(value)
+        {
+            m_type = GetEnumString(ParamType::Value);
+        }
+
+        const void* GetDataAddress() const override
+        {
+            return &m_value;
+        }
+        AZ::TypeId GetDataTypeUuid() const override;
+        AZStd::string ToJSONString() const override;
+
+        void SetMinValue(double min)
+        {
+            m_min = min;
+            m_isMinSet = true;
+        }
+
+        void SetMaxValue(double max)
+        {
+            m_max = max;
+            m_isMaxSet = true;
+        }
+
+        double m_value;
+        //! Geometry Node Param Max(double)
+        double m_max;
+        //! Geometry Node Param Min(double)
+        double m_min;
+    };
+
+    class GNParamString : public GNProperty
+    {
+    public:
+        AZ_CLASS_ALLOCATOR(GNParamString, AZ::SystemAllocator);
+        AZ_RTTI(GeomNodes::GNParamString, GNParamStringTypeId, GNProperty);
+
+        static void Reflect(AZ::ReflectContext* reflection);
+        static GNProperty* TryCreateProperty(GNParamDataContext& context, int valueIndex, const char* name);
+
+        GNParamString()
+        {
+            m_type = GetEnumString(ParamType::String);
+        }
+        GNParamString(const char* name, const char* value, bool* pReadOnly, AZ::EntityId entityId)
+            : GNProperty(name, pReadOnly, entityId)
+            , m_value(value)
+        {
+            m_type = GetEnumString(ParamType::String);
+        }
+
+        const void* GetDataAddress() const override
+        {
+            return &m_value;
+        }
+        AZ::TypeId GetDataTypeUuid() const override;
+        AZStd::string ToJSONString() const override;
+
+        AZStd::string m_value;
+    };
+} // namespace GeomNodes

+ 16 - 0
Gems/O3DE/GeomNodes/Code/Source/Editor/Systems/GNSystemInterface.cpp

@@ -0,0 +1,16 @@
+/*
+ * 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 <Editor/Systems/GNSystemInterface.h>
+
+namespace GeomNodes
+{
+    void GNSystemInterface::Reflect([[maybe_unused]] AZ::ReflectContext* context)
+    {
+    }
+} // namespace GeomNodes

+ 65 - 0
Gems/O3DE/GeomNodes/Code/Source/Editor/Systems/GNSystemInterface.h

@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#include <AzCore/EBus/Event.h>
+#include <Editor/Common/GNEvents.h>
+#include <Editor/Configuration/GNConfiguration.h>
+#include <GeomNodes/GeomNodesTypeIds.h>
+
+namespace GeomNodes
+{
+    class GNSystemInterface
+    {
+    public:
+        AZ_RTTI(GNSystemInterface, GNSystemInterfaceTypeId);
+
+        GNSystemInterface() = default;
+        virtual ~GNSystemInterface() = default;
+        AZ_DISABLE_COPY_MOVE(GNSystemInterface);
+
+        static void Reflect(AZ::ReflectContext* context);
+
+        //! Initialize the system with the given configuration.
+        //! @param config Contains the configuration options
+        virtual void Initialize(const GNConfiguration* config) = 0;
+        //! Teardown the whole  system.
+        //! system will stop running.
+        virtual void Shutdown() = 0;
+
+        //! Retrieve the Blender Editor path
+        //! @return the Blender Editor path
+        virtual AZStd::string_view GetBlenderPath() = 0;
+
+        //! Get the current GNConfiguration used to initialize the GeomNodes system.
+        virtual const GNConfiguration* GetConfiguration() const = 0;
+
+        //! Update the GNConfiguration.
+        //! This will apply the new configuration
+        //! @param newConfig The new configuration to apply.
+        virtual void UpdateConfiguration(const GNConfiguration* newConfig) = 0;
+
+        //! Register to receive notifications when the GeomNodes System is Initialized.
+        //! @param handler The handler to receive the event.
+        void RegisterSystemInitializedEvent(SystemEvents::OnInitializedEvent::Handler& handler)
+        {
+            handler.Connect(m_initializeEvent);
+        }
+        //! Register to receive notifications when the GNConfiguration changes.
+        //! @param handler The handler to receive the event.
+        void RegisterSystemConfigurationChangedEvent(SystemEvents::OnConfigurationChangedEvent::Handler& handler)
+        {
+            handler.Connect(m_configChangeEvent);
+        }
+
+    protected:
+        SystemEvents::OnInitializedEvent m_initializeEvent;
+        SystemEvents::OnConfigurationChangedEvent m_configChangeEvent;
+    };
+} // namespace GeomNodes

+ 159 - 0
Gems/O3DE/GeomNodes/Code/Source/Editor/Systems/GeomNodesSystem.cpp

@@ -0,0 +1,159 @@
+/*
+ * 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 "GeomNodesSystem.h"
+#include "Editor/UI/GeomNodesValidator.h"
+#include "Editor/UI/PropertyFileSelect.h"
+#include "Editor/UI/ValidationHandler.h"
+#include <Editor/Common/GNAPI.h>
+#include <Editor/EBus/IpcHandlerBus.h>
+
+
+namespace GeomNodes
+{
+    long IpcHandlerCB(AZ::u64 id, const char* data, AZ::u64 length)
+    {
+        Ipc::IpcHandlerNotificationBus::Event(
+            AZ::EntityId(id), &Ipc::IpcHandlerNotificationBus::Events::OnMessageReceived, reinterpret_cast<const AZ::u8*>(data), length);
+
+        return 0;
+    }
+
+    GeomNodesSystem::GeomNodesSystem(AZStd::unique_ptr<GNSettingsRegistryManager> registryManager)
+        : m_registryManager(AZStd::move(registryManager))
+        , m_propertyHandlers()
+        , m_validator(AZStd::make_unique<Validator>())
+        , m_validationHandler(AZStd::make_unique<ValidationHandler>())
+    {
+    }
+
+    GeomNodesSystem::~GeomNodesSystem()
+    {
+        Shutdown();
+    }
+
+    void GeomNodesSystem::Initialize(const GNConfiguration* config)
+    {
+        if (m_state == State::Initialized)
+        {
+            AZ_Warning("GeomNodesSystem", false, "GeomNodes system already initialized, Shutdown must be called first");
+            return;
+        }
+
+        m_systemConfig = *config;
+
+        API::Init(SERVER_ID, IpcHandlerCB);
+
+        RegisterHandlersAndBuses();
+
+        m_state = State::Initialized;
+        m_initializeEvent.Signal(&m_systemConfig);
+    }
+
+    void GeomNodesSystem::Shutdown()
+    {
+        if (m_state != State::Initialized)
+        {
+            return;
+        }
+
+        UnregisterHandlersAndBuses();
+        API::Uninitialize();
+
+        m_state = State::Shutdown;
+    }
+
+    AZStd::string_view GeomNodesSystem::GetBlenderPath()
+    {
+        return m_systemConfig.m_blenderPath;
+    }
+
+    const GNConfiguration* GeomNodesSystem::GetConfiguration() const
+    {
+        return &m_systemConfig;
+    }
+
+    const GNConfiguration& GeomNodesSystem::GetSystemConfiguration() const
+    {
+        return m_systemConfig;
+    }
+
+    void GeomNodesSystem::SetLastPath(const AZStd::string& lastPath)
+    {
+        m_systemConfig.m_lastFilePath = lastPath;
+        const GNSettingsRegistryManager& settingsRegManager = GetSettingsRegistryManager();
+        auto saveCallback = [](const GNConfiguration& config, GNSettingsRegistryManager::Result result)
+        {
+            AZ_Warning(
+                "GeomNodes",
+                result == GNSettingsRegistryManager::Result::Success,
+                "Unable to save the GeomNodes configuration. Any changes have not been applied.");
+            if (result == GNSettingsRegistryManager::Result::Success)
+            {
+                if (auto* gnSystem = GetGNSystem())
+                {
+                    gnSystem->UpdateConfiguration(&config);
+                }
+            }
+        };
+        settingsRegManager.SaveSystemConfiguration(m_systemConfig, saveCallback);
+    }
+
+    AZStd::string GeomNodesSystem::GetLastPath()
+    {
+        return m_systemConfig.m_lastFilePath;
+    }
+
+    void GeomNodesSystem::UpdateConfiguration(const GNConfiguration* newConfig)
+    {
+        if (m_systemConfig != *newConfig)
+        {
+            m_systemConfig = (*newConfig);
+            m_configChangeEvent.Signal(newConfig);
+        }
+    }
+
+    const GNSettingsRegistryManager& GeomNodesSystem::GetSettingsRegistryManager() const
+    {
+        return *m_registryManager;
+    }
+
+    FunctorValidator* GeomNodesSystem::GetValidator(FunctorValidator::FunctorType functor)
+    {
+        return m_validator->GetQValidator(functor);
+    }
+
+    void GeomNodesSystem::TrackValidator(FunctorValidator* validator)
+    {
+        m_validator->TrackThisValidator(validator);
+    }
+
+    void GeomNodesSystem::RegisterHandlersAndBuses()
+    {
+        m_propertyHandlers.push_back(PropertyFuncValBrowseEditHandler::Register(m_validationHandler.get()));
+        m_propertyHandlers.push_back(PropertyFileSelectHandler::Register(m_validationHandler.get()));
+        ValidatorBus::Handler::BusConnect();
+    }
+
+    void GeomNodesSystem::UnregisterHandlersAndBuses()
+    {
+        ValidatorBus::Handler::BusDisconnect();
+
+        for (AzToolsFramework::PropertyHandlerBase* handler : m_propertyHandlers)
+        {
+            AzToolsFramework::PropertyTypeRegistrationMessages::Bus::Broadcast(
+                &AzToolsFramework::PropertyTypeRegistrationMessages::Bus::Handler::UnregisterPropertyType, handler);
+            delete handler;
+        }
+    }
+
+    GeomNodesSystem* GetGNSystem()
+    {
+        return azdynamic_cast<GeomNodesSystem*>(AZ::Interface<GeomNodes::GNSystemInterface>::Get());
+    }
+} // namespace GeomNodes

+ 82 - 0
Gems/O3DE/GeomNodes/Code/Source/Editor/Systems/GeomNodesSystem.h

@@ -0,0 +1,82 @@
+/*
+ * 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 "Editor/EBus/ValidatorBus.h"
+#include <AZCore/std/containers/vector.h>
+#include <AZCore/std/smart_ptr/unique_ptr.h>
+#include <AzCore/Interface/Interface.h>
+#include <AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI.h>
+#include <Editor/Configuration/GNConfiguration.h>
+#include <Editor/Configuration/GNSettingsRegistryManager.h>
+#include <Editor/Systems/GNSystemInterface.h>
+#include <GeomNodes/GeomNodesTypeIds.h>
+
+
+namespace GeomNodes
+{
+    class Validator;
+    class ValidationHandler;
+
+    class GeomNodesSystem
+        : public AZ::Interface<GNSystemInterface>::Registrar
+        , public ValidatorBus::Handler
+    {
+    public:
+        AZ_RTTI(GeomNodesSystem, GeomNodesSystemTypeId, GeomNodes::GNSystemInterface);
+
+        GeomNodesSystem(AZStd::unique_ptr<GNSettingsRegistryManager> registryManager);
+        virtual ~GeomNodesSystem();
+
+        // GNSystemInterface interface ...
+        void Initialize(const GNConfiguration* config) override;
+        void Shutdown() override;
+        AZStd::string_view GetBlenderPath() override;
+        const GNConfiguration* GetConfiguration() const override;
+        void UpdateConfiguration(const GNConfiguration* newConfig) override;
+
+        //! Accessor to get the Settings Registry Manager.
+        const GNSettingsRegistryManager& GetSettingsRegistryManager() const;
+
+        FunctorValidator* GetValidator(FunctorValidator::FunctorType) override;
+        void TrackValidator(FunctorValidator*) override;
+
+        const GNConfiguration& GetSystemConfiguration() const;
+
+        void SetLastPath(const AZStd::string& lastPath);
+        AZStd::string GetLastPath();
+
+    private:
+        //! Un/Registers and dis/connect handlers and buses
+        void RegisterHandlersAndBuses();
+        void UnregisterHandlersAndBuses();
+
+        //! Handles all settings registry interactions.
+        AZStd::unique_ptr<GNSettingsRegistryManager> m_registryManager;
+
+        //! Allows lookup and contains all allocated QValidators
+        AZStd::unique_ptr<Validator> m_validator;
+
+        AZStd::vector<AzToolsFramework::PropertyHandlerBase*> m_propertyHandlers;
+        AZStd::unique_ptr<ValidationHandler> m_validationHandler;
+
+        GNConfiguration m_systemConfig;
+
+        enum class State : AZ::u8
+        {
+            Uninitialized = 0,
+            Initialized,
+            Shutdown
+        };
+        State m_state = State::Uninitialized;
+    };
+
+    //! Helper function for getting the GeomNodes System interface from inside the GeomNodes gem.
+    GeomNodesSystem* GetGNSystem();
+} // namespace GeomNodes

+ 57 - 0
Gems/O3DE/GeomNodes/Code/Source/Editor/UI/ConfigurationWidget.cpp

@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include <AzFramework/Physics/Collision/CollisionGroups.h>
+#include <AzFramework/Physics/Collision/CollisionLayers.h>
+#include <AzFramework/Physics/SystemBus.h>
+#include <AzToolsFramework/UI/PropertyEditor/InstanceDataHierarchy.h>
+#include <AzToolsFramework/UI/PropertyEditor/ReflectedPropertyEditor.hxx>
+#include <Editor/UI/ConfigurationWidget.h>
+#include <Editor/UI/SettingsWidget.h>
+#include <QBoxLayout>
+
+
+namespace GeomNodes
+{
+    namespace Editor
+    {
+        ConfigurationWidget::ConfigurationWidget(QWidget* parent)
+            : QWidget(parent)
+        {
+            QVBoxLayout* verticalLayout = new QVBoxLayout(this);
+            verticalLayout->setContentsMargins(0, 5, 0, 0);
+            verticalLayout->setSpacing(0);
+
+            m_settings = new SettingsWidget();
+
+            verticalLayout->addWidget(m_settings);
+
+            connect(
+                m_settings,
+                &SettingsWidget::onValueChanged,
+                this,
+                [this](const GNConfiguration& gnSystemConfiguration)
+                {
+                    m_gnSystemConfiguration = gnSystemConfiguration;
+                    emit onConfigurationChanged(m_gnSystemConfiguration);
+                });
+        }
+
+        ConfigurationWidget::~ConfigurationWidget()
+        {
+        }
+
+        void ConfigurationWidget::SetConfiguration(const GNConfiguration& gnSystemConfiguration)
+        {
+            m_gnSystemConfiguration = gnSystemConfiguration;
+            m_settings->SetValue(m_gnSystemConfiguration);
+        }
+    } // namespace Editor
+} // namespace GeomNodes
+
+#include <Editor/UI/moc_ConfigurationWidget.cpp>

+ 46 - 0
Gems/O3DE/GeomNodes/Code/Source/Editor/UI/ConfigurationWidget.h

@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#if !defined(Q_MOC_RUN)
+#include <AzCore/Memory/SystemAllocator.h>
+#include <Editor/Configuration/GNConfiguration.h>
+
+#endif
+
+namespace GeomNodes
+{
+    namespace Editor
+    {
+        class SettingsWidget;
+
+        /// Widget for editing GeomNodes configuration and settings.
+        ///
+        class ConfigurationWidget : public QWidget
+        {
+            Q_OBJECT
+
+        public:
+            AZ_CLASS_ALLOCATOR(ConfigurationWidget, AZ::SystemAllocator);
+
+            explicit ConfigurationWidget(QWidget* parent = nullptr);
+            ~ConfigurationWidget() override;
+
+            void SetConfiguration(const GNConfiguration& gnSystemConfiguration);
+
+        signals:
+            void onConfigurationChanged(const GNConfiguration& gnSystemConfiguration);
+
+        private:
+            GNConfiguration m_gnSystemConfiguration;
+
+            SettingsWidget* m_settings;
+        };
+    } // namespace Editor
+} // namespace GeomNodes

+ 89 - 0
Gems/O3DE/GeomNodes/Code/Source/Editor/UI/EditorWindow.cpp

@@ -0,0 +1,89 @@
+/*
+ * 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 <Editor/Configuration/GNConfiguration.h>
+#include <Editor/Systems/GeomNodesSystem.h>
+#include <Editor/UI/ConfigurationWidget.h>
+#include <Editor/UI/EditorWindow.h>
+#include <Editor/UI/ui_EditorWindow.h>
+
+
+#include <AzCore/Interface/Interface.h>
+#include <AzToolsFramework/API/ToolsApplicationAPI.h>
+#include <AzToolsFramework/API/ViewPaneOptions.h>
+
+
+namespace GeomNodes
+{
+    namespace Editor
+    {
+        static const char* const CategoryTools = "Tools";
+        static const char* const GeomNodesConfigurationEditor = "GeomNodes Configuration (PREVIEW)";
+
+        EditorWindow::EditorWindow(QWidget* parent)
+            : QWidget(parent)
+            , m_ui(new Ui::EditorWindowClass())
+        {
+            m_ui->setupUi(this);
+
+            auto* gnSystem = AZ::Interface<GNSystemInterface>::Get();
+            const auto* gnSystemConfiguration = azdynamic_cast<const GNConfiguration*>(gnSystem->GetConfiguration());
+
+            m_ui->m_GeomNodesConfigurationWidget->SetConfiguration(*gnSystemConfiguration);
+            connect(
+                m_ui->m_GeomNodesConfigurationWidget,
+                &Editor::ConfigurationWidget::onConfigurationChanged,
+                this,
+                &EditorWindow::SaveConfiguration);
+        }
+
+        void EditorWindow::RegisterViewClass()
+        {
+            AzToolsFramework::ViewPaneOptions options;
+            options.preferedDockingArea = Qt::LeftDockWidgetArea;
+            options.saveKeyName = "GeomNodesConfiguration";
+            options.isPreview = true;
+            AzToolsFramework::RegisterViewPane<EditorWindow>(GeomNodesConfigurationEditor, CategoryTools, options);
+        }
+
+        void EditorWindow::SaveConfiguration(const GNConfiguration& gnSystemConfiguration)
+        {
+            auto* gnSystem = GetGNSystem();
+            if (gnSystem == nullptr)
+            {
+                AZ_Error(
+                    "GeomNodes",
+                    false,
+                    "Unable to save the GeomNodes configuration. The GeomNodesSystem not initialized. Any changes have not been applied.");
+                return;
+            }
+
+            // update the GeomNodes system config if it has changed
+            const GNSettingsRegistryManager& settingsRegManager = gnSystem->GetSettingsRegistryManager();
+            if (gnSystem->GetSystemConfiguration() != gnSystemConfiguration)
+            {
+                auto saveCallback = [](const GNConfiguration& config, GNSettingsRegistryManager::Result result)
+                {
+                    AZ_Warning(
+                        "GeomNodes",
+                        result == GNSettingsRegistryManager::Result::Success,
+                        "Unable to save the GeomNodes configuration. Any changes have not been applied.");
+                    if (result == GNSettingsRegistryManager::Result::Success)
+                    {
+                        if (auto* gnSystem = GetGNSystem())
+                        {
+                            gnSystem->UpdateConfiguration(&config);
+                        }
+                    }
+                };
+                settingsRegManager.SaveSystemConfiguration(gnSystemConfiguration, saveCallback);
+            }
+        }
+    } // namespace Editor
+} // namespace GeomNodes
+#include <Editor/UI/moc_EditorWindow.cpp>

+ 44 - 0
Gems/O3DE/GeomNodes/Code/Source/Editor/UI/EditorWindow.h

@@ -0,0 +1,44 @@
+/*
+ * 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
+
+#if !defined(Q_MOC_RUN)
+#include <AzCore/Asset/AssetCommon.h>
+#include <QWidget>
+#endif
+
+namespace Ui
+{
+    class EditorWindowClass;
+}
+
+namespace GeomNodes
+{
+    struct GNConfiguration;
+
+    namespace Editor
+    {
+        /// Window pane wrapper for the GeomNodes Configuration Widget.
+        ///
+        class EditorWindow : public QWidget
+        {
+            Q_OBJECT
+        public:
+            AZ_CLASS_ALLOCATOR(EditorWindow, AZ::SystemAllocator);
+            static void RegisterViewClass();
+
+            explicit EditorWindow(QWidget* parent = nullptr);
+
+        private:
+            static void SaveConfiguration(const GeomNodes::GNConfiguration& GNConfiguration);
+
+            QScopedPointer<Ui::EditorWindowClass> m_ui;
+        };
+    } // namespace Editor
+}; // namespace GeomNodes

+ 60 - 0
Gems/O3DE/GeomNodes/Code/Source/Editor/UI/EditorWindow.ui

@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>EditorWindowClass</class>
+ <widget class="QWidget" name="GeomNodesEditorWindowClass">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>200</width>
+    <height>200</height>
+   </rect>
+  </property>
+  <property name="minimumSize">
+   <size>
+    <width>200</width>
+    <height>200</height>
+   </size>
+  </property>
+  <property name="windowTitle">
+   <string>GeomNodes Editor</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <property name="spacing">
+    <number>0</number>
+   </property>
+   <property name="leftMargin">
+    <number>0</number>
+   </property>
+   <property name="topMargin">
+    <number>0</number>
+   </property>
+   <property name="rightMargin">
+    <number>0</number>
+   </property>
+   <property name="bottomMargin">
+    <number>0</number>
+   </property>
+   <item>
+    <widget class="GeomNodes::Editor::ConfigurationWidget" name="m_GeomNodesConfigurationWidget" native="true">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Preferred" vsizetype="Expanding">
+       <horstretch>0</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>GeomNodes::Editor::ConfigurationWidget</class>
+   <extends>QWidget</extends>
+   <header>Editor/UI/ConfigurationWidget.h</header>
+   <container>1</container>
+  </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>

+ 41 - 0
Gems/O3DE/GeomNodes/Code/Source/Editor/UI/FunctorValidator.cpp

@@ -0,0 +1,41 @@
+/*
+ * 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 "FunctorValidator.h"
+
+namespace GeomNodes
+{
+    FunctorValidator::FunctorValidator(FunctorType functor)
+        : QValidator()
+        , m_functor(functor)
+    {
+    }
+
+    FunctorValidator::FunctorValidator()
+        : QValidator()
+        , m_functor(nullptr)
+    {
+    }
+
+    QValidator::State FunctorValidator::validate(QString& input, [[maybe_unused]] int& pos) const
+    {
+        return m_functor(input).first;
+    }
+
+    FunctorValidator::ReturnType FunctorValidator::ValidateWithErrors(const QString& input) const
+    {
+        return m_functor(input);
+    }
+
+    FunctorValidator::FunctorType FunctorValidator::Functor() const
+    {
+        return m_functor;
+    }
+} // namespace GeomNodes
+
+#include <moc_FunctorValidator.cpp>

+ 44 - 0
Gems/O3DE/GeomNodes/Code/Source/Editor/UI/FunctorValidator.h

@@ -0,0 +1,44 @@
+/*
+ * 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
+
+#if !defined(Q_MOC_RUN)
+#include <AzCore/std/utils.h>
+
+#include <QValidator>
+#endif
+
+// Derived from the same class from PlatformSettingsTool
+
+namespace GeomNodes
+{
+    class FunctorValidator : public QValidator
+    {
+        Q_OBJECT
+
+    public:
+        typedef AZStd::pair<QValidator::State, const QString> ReturnType;
+        typedef ReturnType (*FunctorType)(const QString&);
+
+        FunctorValidator(FunctorType functor);
+
+        //! Validates using QValidates api
+        QValidator::State validate(QString& input, int& pos) const override;
+        //! Validates and returns the result with an error string if one occurred
+        virtual ReturnType ValidateWithErrors(const QString& input) const;
+        //! Returns the function used to validate
+        FunctorType Functor() const;
+
+    protected:
+        FunctorValidator();
+
+        //! The function to use for validating
+        FunctorType m_functor;
+    };
+} // namespace GeomNodes

+ 59 - 0
Gems/O3DE/GeomNodes/Code/Source/Editor/UI/GeomNodesValidator.cpp

@@ -0,0 +1,59 @@
+/*
+ * 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 "GeomNodesValidator.h"
+
+#include "Validators.h"
+
+namespace GeomNodes
+{
+    Validator::Validator()
+    {
+    }
+
+    Validator::~Validator()
+    {
+        for (AZStd::pair<FunctorValidator::FunctorType, FunctorValidator*>& pair : m_validatorToQValidator)
+        {
+            delete pair.second;
+        }
+        for (FunctorValidator* qValidator : m_otherValidators)
+        {
+            delete qValidator;
+        }
+    }
+
+    FunctorValidator* Validator::GetQValidator(FunctorValidator::FunctorType validator)
+    {
+        if (validator != nullptr)
+        {
+            auto iter = m_validatorToQValidator.find(validator);
+
+            if (iter != m_validatorToQValidator.end())
+            {
+                return iter->second;
+            }
+            else
+            {
+                FunctorValidator* qValidator = new FunctorValidator(validator);
+                m_validatorToQValidator.insert(AZStd::pair<FunctorValidator::FunctorType, FunctorValidator*>(validator, qValidator));
+
+                return qValidator;
+            }
+        }
+        else
+        {
+            return nullptr;
+        }
+    }
+
+    void Validator::TrackThisValidator(FunctorValidator* validator)
+    {
+        m_otherValidators.push_back(validator);
+    }
+} // namespace GeomNodes

+ 43 - 0
Gems/O3DE/GeomNodes/Code/Source/Editor/UI/GeomNodesValidator.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 "FunctorValidator.h"
+
+#include <AzCore/std/containers/unordered_map.h>
+#include <AzCore/std/string/string.h>
+
+namespace GeomNodes
+{
+    class Validator
+    {
+    public:
+        typedef FunctorValidator::ReturnType ValidatorReturnType;
+        typedef FunctorValidator::FunctorType ValidatorType;
+
+        Validator();
+        ~Validator();
+
+        //! Finds the QValidator for a given validator or makes one and returns it
+        FunctorValidator* GetQValidator(FunctorValidator::FunctorType validator);
+        //! Tracks this QValidator and deletes it in the destructor
+        void TrackThisValidator(FunctorValidator* validator);
+
+    private:
+        typedef AZStd::unordered_map<FunctorValidator::FunctorType, FunctorValidator*> ValidatorToQValidatorType;
+        typedef AZStd::list<FunctorValidator*> QValidatorList;
+
+        AZ_DISABLE_COPY_MOVE(Validator);
+
+        //! Maps validator functions to QValidators
+        ValidatorToQValidatorType m_validatorToQValidator;
+        //! Tracks allocations of other QValidators so they don't leak
+        QValidatorList m_otherValidators;
+    };
+} // namespace GeomNodes

+ 122 - 0
Gems/O3DE/GeomNodes/Code/Source/Editor/UI/PropertyFileSelect.cpp

@@ -0,0 +1,122 @@
+/*
+ * 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 "PropertyFileSelect.h"
+
+#include "UI_common.h"
+#include "ValidationHandler.h"
+
+#include <QLayout>
+#include <QLineEdit>
+#include <QPushButton>
+
+
+namespace GeomNodes
+{
+    PropertyFileSelectCtrl::PropertyFileSelectCtrl(QWidget* pParent)
+        : PropertyFuncValBrowseEditCtrl(pParent)
+        , m_selectFunctor(nullptr)
+    {
+        // Turn on clear button by default
+        browseEdit()->setClearButtonEnabled(true);
+
+        connect(browseEdit(), &AzQtComponents::BrowseEdit::attachedButtonTriggered, this, &PropertyFileSelectCtrl::SelectFile);
+    }
+
+    void PropertyFileSelectCtrl::SelectFile()
+    {
+        if (m_selectFunctor != nullptr)
+        {
+            QString path = m_selectFunctor(browseEdit()->text());
+            if (!path.isEmpty())
+            {
+                SetValueUser(path);
+            }
+        }
+        else
+        {
+            AZ_Assert(false, "No file select functor set.");
+        }
+    }
+
+    void PropertyFileSelectCtrl::ConsumeAttribute(
+        AZ::u32 attrib, AzToolsFramework::PropertyAttributeReader* attrValue, const char* debugName)
+    {
+        if (attrib == Attributes::SelectFunction)
+        {
+            void* functor = nullptr;
+            if (attrValue->Read<void*>(functor))
+            {
+                // This is guaranteed type safe elsewhere so this is safe
+                m_selectFunctor = reinterpret_cast<FileSelectFuncType>(functor);
+            }
+        }
+        else
+        {
+            PropertyFuncValBrowseEditCtrl::ConsumeAttribute(attrib, attrValue, debugName);
+        }
+    }
+
+    //  Handler  ///////////////////////////////////////////////////////////////////
+
+    PropertyFileSelectHandler::PropertyFileSelectHandler(ValidationHandler* valHdlr)
+        : AzToolsFramework::PropertyHandler<AZStd::string, PropertyFileSelectCtrl>()
+        , m_validationHandler(valHdlr)
+    {
+    }
+
+    AZ::u32 PropertyFileSelectHandler::GetHandlerName(void) const
+    {
+        return Handlers::FileSelect;
+    }
+
+    QWidget* PropertyFileSelectHandler::CreateGUI(QWidget* pParent)
+    {
+        PropertyFileSelectCtrl* ctrl = aznew PropertyFileSelectCtrl(pParent);
+        m_validationHandler->AddValidatorCtrl(ctrl);
+        return ctrl;
+    }
+
+    void PropertyFileSelectHandler::ConsumeAttribute(
+        PropertyFileSelectCtrl* GUI, AZ::u32 attrib, AzToolsFramework::PropertyAttributeReader* attrValue, const char* debugName)
+    {
+        GUI->ConsumeAttribute(attrib, attrValue, debugName);
+    }
+
+    void PropertyFileSelectHandler::WriteGUIValuesIntoProperty(
+        [[maybe_unused]] size_t index,
+        [[maybe_unused]] PropertyFileSelectCtrl* GUI,
+        [[maybe_unused]] property_t& instance,
+        [[maybe_unused]] AzToolsFramework::InstanceDataNode* node)
+    {
+        instance = GUI->GetValue().toUtf8().data();
+    }
+
+    bool PropertyFileSelectHandler::ReadValuesIntoGUI(
+        [[maybe_unused]] size_t index,
+        [[maybe_unused]] PropertyFileSelectCtrl* GUI,
+        [[maybe_unused]] const property_t& instance,
+        [[maybe_unused]] AzToolsFramework::InstanceDataNode* node)
+    {
+        QSignalBlocker blocker(GUI);
+        GUI->SetNotifyTarget(node->GetParent()->GetInstance(0));
+        GUI->SetValue(instance.data());
+        // GUI->ForceValidate(); // <= causes an undo bug crash when moving the transform.
+        return true;
+    }
+
+    PropertyFileSelectHandler* PropertyFileSelectHandler::Register(ValidationHandler* valHdlr)
+    {
+        PropertyFileSelectHandler* handler = aznew PropertyFileSelectHandler(valHdlr);
+        AzToolsFramework::PropertyTypeRegistrationMessages::Bus::Broadcast(
+            &AzToolsFramework::PropertyTypeRegistrationMessages::Bus::Handler::RegisterPropertyType, handler);
+        return handler;
+    }
+} // namespace GeomNodes
+
+#include <moc_PropertyFileSelect.cpp>

+ 71 - 0
Gems/O3DE/GeomNodes/Code/Source/Editor/UI/PropertyFileSelect.h

@@ -0,0 +1,71 @@
+/*
+ * 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
+
+#if !defined(Q_MOC_RUN)
+#include "PropertyFuncValBrowseEdit.h"
+#endif
+
+// Forward declare
+class QPushButton;
+
+namespace GeomNodes
+{
+    // Forward declare
+    class ValidationHandler;
+
+    class PropertyFileSelectCtrl : public PropertyFuncValBrowseEditCtrl
+    {
+        Q_OBJECT
+
+    public:
+        AZ_CLASS_ALLOCATOR(PropertyFileSelectCtrl, AZ::SystemAllocator)
+        typedef QString (*FileSelectFuncType)(const QString&);
+
+        PropertyFileSelectCtrl(QWidget* pParent = nullptr);
+
+        virtual void ConsumeAttribute(AZ::u32 attrib, AzToolsFramework::PropertyAttributeReader* attrValue, const char* debugName) override;
+
+    protected:
+        void SelectFile();
+
+        QPushButton* m_selectButton;
+        FileSelectFuncType m_selectFunctor;
+    };
+
+    class PropertyFileSelectHandler : public AzToolsFramework::PropertyHandler<AZStd::string, PropertyFileSelectCtrl>
+    {
+        AZ_CLASS_ALLOCATOR(PropertyFileSelectHandler, AZ::SystemAllocator);
+
+    public:
+        PropertyFileSelectHandler(ValidationHandler* valHdlr);
+
+        AZ::u32 GetHandlerName(void) const override;
+        // Need to unregister ourselves
+        bool AutoDelete() const override
+        {
+            return false;
+        }
+
+        QWidget* CreateGUI(QWidget* pParent) override;
+        void ConsumeAttribute(
+            PropertyFileSelectCtrl* GUI,
+            AZ::u32 attrib,
+            AzToolsFramework::PropertyAttributeReader* attrValue,
+            const char* debugName) override;
+        void WriteGUIValuesIntoProperty(
+            size_t index, PropertyFileSelectCtrl* GUI, property_t& instance, AzToolsFramework::InstanceDataNode* node) override;
+        bool ReadValuesIntoGUI(
+            size_t index, PropertyFileSelectCtrl* GUI, const property_t& instance, AzToolsFramework::InstanceDataNode* node) override;
+        static PropertyFileSelectHandler* Register(ValidationHandler* valHdlr);
+
+    private:
+        ValidationHandler* m_validationHandler;
+    };
+} // namespace GeomNodes

+ 243 - 0
Gems/O3DE/GeomNodes/Code/Source/Editor/UI/PropertyFuncValBrowseEdit.cpp

@@ -0,0 +1,243 @@
+/*
+ * 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 "PropertyFuncValBrowseEdit.h"
+#include "Editor/EBus/ValidatorBus.h"
+#include "ValidationHandler.h"
+
+#include <AzToolsFramework/UI/PropertyEditor/PropertyQTConstants.h>
+
+#include <QHBoxLayout>
+#include <QLineEdit>
+
+
+namespace GeomNodes
+{
+    PropertyFuncValBrowseEditCtrl::PropertyFuncValBrowseEditCtrl(QWidget* pParent)
+        : QWidget(pParent)
+        , m_validator(nullptr)
+    {
+        QHBoxLayout* layout = new QHBoxLayout(this);
+        m_browseEdit = new AzQtComponents::BrowseEdit(this);
+
+        layout->setSpacing(4);
+        layout->setContentsMargins(1, 0, 1, 0);
+        layout->addWidget(m_browseEdit);
+
+        browseEdit()->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed);
+        browseEdit()->setMinimumWidth(AzToolsFramework::PropertyQTConstant_MinimumWidth);
+        browseEdit()->setFixedHeight(AzToolsFramework::PropertyQTConstant_DefaultHeight);
+
+        browseEdit()->setFocusPolicy(Qt::StrongFocus);
+
+        setLayout(layout);
+        setFocusProxy(browseEdit());
+        setFocusPolicy(browseEdit()->focusPolicy());
+
+        m_browseEdit->setClearButtonEnabled(true);
+        connect(m_browseEdit, &AzQtComponents::BrowseEdit::textChanged, this, &PropertyFuncValBrowseEditCtrl::ValueChangedByUser);
+        connect(m_browseEdit, &AzQtComponents::BrowseEdit::textChanged, this, &PropertyFuncValBrowseEditCtrl::ValidateAndShowErrors);
+        connect(
+            m_browseEdit,
+            &AzQtComponents::BrowseEdit::textChanged,
+            this,
+            [this]([[maybe_unused]] const QString& text)
+            {
+                EBUS_EVENT(AzToolsFramework::PropertyEditorGUIMessages::Bus, RequestWrite, this);
+            });
+    }
+
+    QString PropertyFuncValBrowseEditCtrl::GetValue() const
+    {
+        return m_browseEdit->text();
+    }
+
+    void PropertyFuncValBrowseEditCtrl::SetValue(const QString& value)
+    {
+        m_browseEdit->setText(value);
+    }
+
+    void PropertyFuncValBrowseEditCtrl::SetValueUser(const QString& value)
+    {
+        SetValue(value);
+
+        emit ValueChangedByUser();
+    }
+
+    FunctorValidator* PropertyFuncValBrowseEditCtrl::GetValidator()
+    {
+        return m_validator;
+    }
+
+    void PropertyFuncValBrowseEditCtrl::SetValidator(FunctorValidator* validator)
+    {
+        m_browseEdit->lineEdit()->setValidator(validator);
+        m_validator = validator;
+    }
+
+    void PropertyFuncValBrowseEditCtrl::SetValidator(FunctorValidator::FunctorType validator)
+    {
+        FunctorValidator* val = nullptr;
+        ValidatorBus::BroadcastResult(val, &ValidatorBus::Handler::GetValidator, validator);
+
+        SetValidator(val);
+    }
+
+    bool PropertyFuncValBrowseEditCtrl::ValidateAndShowErrors()
+    {
+        if (m_validator)
+        {
+            auto path = m_browseEdit->text();
+            FunctorValidator::ReturnType result = m_validator->ValidateWithErrors(path);
+            if (result.first == QValidator::Acceptable)
+            {
+                m_browseEdit->lineEdit()->setToolTip("");
+                if (m_validationResultHandler)
+                    m_validationResultHandler->Invoke(m_notifyTarget, path.toStdString().c_str());
+                return true;
+            }
+            else
+            {
+                m_browseEdit->lineEdit()->setToolTip(result.second);
+                m_browseEdit->lineEdit()->setReadOnly(false);
+                if (m_validationResultHandler)
+                    m_validationResultHandler->Invoke(m_notifyTarget, "");
+                return false;
+            }
+        }
+        else
+        {
+            return true;
+        }
+    }
+
+    void PropertyFuncValBrowseEditCtrl::ForceValidate()
+    {
+        // Triggers update for errors
+        m_browseEdit->lineEdit()->textChanged(m_browseEdit->text());
+    }
+
+    void PropertyFuncValBrowseEditCtrl::SetNotifyTarget(void* notifyTarget)
+    {
+        m_notifyTarget = notifyTarget;
+    }
+
+    void PropertyFuncValBrowseEditCtrl::ConsumeAttribute(
+        AZ::u32 attrib, AzToolsFramework::PropertyAttributeReader* attrValue, [[maybe_unused]] const char* debugName)
+    {
+        if (attrib == Attributes::FuncValidator)
+        {
+            void* validator = nullptr;
+            if (attrValue->Read<void*>(validator))
+            {
+                if (validator != nullptr)
+                {
+                    SetValidator(reinterpret_cast<FunctorValidator::FunctorType>(validator));
+                }
+            }
+        }
+        else if (attrib == Attributes::ValidationChange)
+        {
+            ValidationCallbackType* func = azdynamic_cast<ValidationCallbackType*>(attrValue->GetAttribute());
+
+            if (!m_validationResultHandler && func)
+            {
+                m_validationResultHandler = func;
+            }
+            else
+            {
+                m_validationResultHandler = nullptr;
+            }
+        }
+        else if (attrib == Attributes::ClearButton)
+        {
+            bool enable = false;
+            if (attrValue->Read<bool>(enable))
+            {
+                m_browseEdit->lineEdit()->setClearButtonEnabled(enable);
+            }
+        }
+        else if (attrib == Attributes::RemovableReadOnly)
+        {
+            bool readOnly = false;
+            if (attrValue->Read<bool>(readOnly))
+            {
+                m_browseEdit->lineEdit()->setReadOnly(readOnly);
+            }
+        }
+        else if (attrib == Attributes::ObfuscatedText)
+        {
+            bool obfus = false;
+            if (attrValue->Read<bool>(obfus) && obfus)
+            {
+                m_browseEdit->lineEdit()->setEchoMode(QLineEdit::Password);
+            }
+        }
+    }
+
+    AzQtComponents::BrowseEdit* PropertyFuncValBrowseEditCtrl::browseEdit()
+    {
+        return m_browseEdit;
+    }
+
+    //  Handler  ///////////////////////////////////////////////////////////////////
+
+    PropertyFuncValBrowseEditHandler::PropertyFuncValBrowseEditHandler(ValidationHandler* valHdlr)
+        : AzToolsFramework::PropertyHandler<AZStd::string, PropertyFuncValBrowseEditCtrl>()
+        , m_validationHandler(valHdlr)
+    {
+    }
+
+    AZ::u32 PropertyFuncValBrowseEditHandler::GetHandlerName(void) const
+    {
+        return Handlers::QValidatedBrowseEdit;
+    }
+
+    QWidget* PropertyFuncValBrowseEditHandler::CreateGUI(QWidget* pParent)
+    {
+        PropertyFuncValBrowseEditCtrl* ctrl = aznew PropertyFuncValBrowseEditCtrl(pParent);
+        m_validationHandler->AddValidatorCtrl(ctrl);
+        return ctrl;
+    }
+
+    void PropertyFuncValBrowseEditHandler::ConsumeAttribute(
+        PropertyFuncValBrowseEditCtrl* GUI, AZ::u32 attrib, AzToolsFramework::PropertyAttributeReader* attrValue, const char* debugName)
+    {
+        GUI->ConsumeAttribute(attrib, attrValue, debugName);
+    }
+
+    void PropertyFuncValBrowseEditHandler::WriteGUIValuesIntoProperty(
+        [[maybe_unused]] size_t index,
+        PropertyFuncValBrowseEditCtrl* GUI,
+        property_t& instance,
+        [[maybe_unused]] AzToolsFramework::InstanceDataNode* node)
+    {
+        instance = GUI->GetValue().toUtf8().data();
+    }
+
+    bool PropertyFuncValBrowseEditHandler::ReadValuesIntoGUI(
+        [[maybe_unused]] size_t index,
+        PropertyFuncValBrowseEditCtrl* GUI,
+        const property_t& instance,
+        [[maybe_unused]] AzToolsFramework::InstanceDataNode* node)
+    {
+        GUI->SetValue(instance.data());
+        GUI->ForceValidate();
+        return true;
+    }
+
+    PropertyFuncValBrowseEditHandler* PropertyFuncValBrowseEditHandler::Register(ValidationHandler* valHdlr)
+    {
+        PropertyFuncValBrowseEditHandler* handler = aznew PropertyFuncValBrowseEditHandler(valHdlr);
+        AzToolsFramework::PropertyTypeRegistrationMessages::Bus::Broadcast(
+            &AzToolsFramework::PropertyTypeRegistrationMessages::Bus::Handler::RegisterPropertyType, handler);
+        return handler;
+    }
+} // namespace GeomNodes
+
+#include <moc_PropertyFuncValBrowseEdit.cpp>

+ 103 - 0
Gems/O3DE/GeomNodes/Code/Source/Editor/UI/PropertyFuncValBrowseEdit.h

@@ -0,0 +1,103 @@
+/*
+ * 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
+
+#if !defined(Q_MOC_RUN)
+#include "FunctorValidator.h"
+
+#include <AzQtComponents/Components/Widgets/BrowseEdit.h>
+#include <AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI.h>
+
+#endif
+
+#include "UI_common.h"
+
+namespace GeomNodes
+{
+    // Forward Declare
+    class ValidationHandler;
+
+    class PropertyFuncValBrowseEditCtrl : public QWidget
+    {
+        Q_OBJECT
+
+    public:
+        AZ_CLASS_ALLOCATOR(PropertyFuncValBrowseEditCtrl, AZ::SystemAllocator);
+
+        using ValidationCallbackType = AZ::Edit::AttributeFunction<void(const AZStd::string&)>;
+
+        PropertyFuncValBrowseEditCtrl(QWidget* pParent = nullptr);
+
+        virtual QString GetValue() const;
+        //! Sets value programmatically and triggers validation
+        virtual void SetValue(const QString& value);
+        //! Sets value as if user set it
+        void SetValueUser(const QString& value);
+        //! Returns pointer to the validator used
+        FunctorValidator* GetValidator();
+        //! Sets the validator for the lineedit
+        void SetValidator(FunctorValidator* validator);
+        //! Sets the validator for the linedit
+        void SetValidator(FunctorValidator::FunctorType validator);
+        //! Returns false if invalid and returns shows error as tooltip
+        bool ValidateAndShowErrors();
+        //! Forces the values to up validated and style updated
+        void ForceValidate();
+        virtual void SetNotifyTarget(void* notifyTarget);
+
+        //! Returns a pointer to the BrowseEdit object.
+        AzQtComponents::BrowseEdit* browseEdit();
+
+        virtual void ConsumeAttribute(AZ::u32 attrib, AzToolsFramework::PropertyAttributeReader* attrValue, const char* debugName);
+
+    signals:
+        void ValueChangedByUser();
+
+    protected:
+        //! Keeps track of the validator so no const_casts must be done
+        FunctorValidator* m_validator;
+        ValidationCallbackType* m_validationResultHandler = nullptr;
+        void* m_notifyTarget = nullptr;
+
+        AzQtComponents::BrowseEdit* m_browseEdit = nullptr;
+    };
+
+    class PropertyFuncValBrowseEditHandler : public AzToolsFramework::PropertyHandler<AZStd::string, PropertyFuncValBrowseEditCtrl>
+    {
+        AZ_CLASS_ALLOCATOR(PropertyFuncValBrowseEditHandler, AZ::SystemAllocator);
+
+    public:
+        PropertyFuncValBrowseEditHandler(ValidationHandler* valHdlr);
+
+        AZ::u32 GetHandlerName(void) const override;
+        //! Need to unregister ourselves
+        bool AutoDelete() const override
+        {
+            return false;
+        }
+
+        QWidget* CreateGUI(QWidget* pParent) override;
+        void ConsumeAttribute(
+            PropertyFuncValBrowseEditCtrl* GUI,
+            AZ::u32 attrib,
+            AzToolsFramework::PropertyAttributeReader* attrValue,
+            const char* debugName) override;
+        void WriteGUIValuesIntoProperty(
+            size_t index, PropertyFuncValBrowseEditCtrl* GUI, property_t& instance, AzToolsFramework::InstanceDataNode* node) override;
+        bool ReadValuesIntoGUI(
+            size_t index,
+            PropertyFuncValBrowseEditCtrl* GUI,
+            const property_t& instance,
+            AzToolsFramework::InstanceDataNode* node) override;
+        static PropertyFuncValBrowseEditHandler* Register(ValidationHandler* valHdlr);
+
+    private:
+        ValidationHandler* m_validationHandler;
+    };
+} // namespace GeomNodes

+ 79 - 0
Gems/O3DE/GeomNodes/Code/Source/Editor/UI/SettingsWidget.cpp

@@ -0,0 +1,79 @@
+/*
+ * 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/Component/ComponentApplicationBus.h>
+#include <AzToolsFramework/UI/PropertyEditor/InstanceDataHierarchy.h>
+#include <AzToolsFramework/UI/PropertyEditor/ReflectedPropertyEditor.hxx>
+#include <Editor/UI/SettingsWidget.h>
+#include <QBoxLayout>
+
+
+namespace GeomNodes
+{
+    namespace Editor
+    {
+        SettingsWidget::SettingsWidget(QWidget* parent)
+            : QWidget(parent)
+        {
+            CreatePropertyEditor(this);
+        }
+
+        void SettingsWidget::SetValue(const GNConfiguration& gnSystemConfiguration)
+        {
+            m_gnSystemConfiguration = gnSystemConfiguration;
+
+            blockSignals(true);
+            m_propertyEditor->ClearInstances();
+            m_propertyEditor->AddInstance(&m_gnSystemConfiguration);
+            m_propertyEditor->InvalidateAll();
+            blockSignals(false);
+        }
+
+        void SettingsWidget::CreatePropertyEditor(QWidget* parent)
+        {
+            QVBoxLayout* verticalLayout = new QVBoxLayout(parent);
+            verticalLayout->setContentsMargins(0, 0, 0, 0);
+            verticalLayout->setSpacing(0);
+
+            AZ::SerializeContext* m_serializeContext;
+            AZ::ComponentApplicationBus::BroadcastResult(m_serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
+            AZ_Assert(m_serializeContext, "Failed to retrieve serialize context.");
+
+            const int propertyLabelWidth = 250;
+            m_propertyEditor = new AzToolsFramework::ReflectedPropertyEditor(parent);
+            m_propertyEditor->Setup(m_serializeContext, this, true, propertyLabelWidth);
+            m_propertyEditor->show();
+            m_propertyEditor->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+
+            verticalLayout->addWidget(m_propertyEditor);
+        }
+
+        void SettingsWidget::BeforePropertyModified([[maybe_unused]] AzToolsFramework::InstanceDataNode* node)
+        {
+        }
+
+        void SettingsWidget::AfterPropertyModified([[maybe_unused]] AzToolsFramework::InstanceDataNode* node)
+        {
+            emit onValueChanged(m_gnSystemConfiguration);
+        }
+
+        void SettingsWidget::SetPropertyEditingActive([[maybe_unused]] AzToolsFramework::InstanceDataNode* node)
+        {
+        }
+
+        void SettingsWidget::SetPropertyEditingComplete([[maybe_unused]] AzToolsFramework::InstanceDataNode* node)
+        {
+        }
+
+        void SettingsWidget::SealUndoStack()
+        {
+        }
+    } // namespace Editor
+} // namespace GeomNodes
+
+#include <Editor/UI/moc_SettingsWidget.cpp>

+ 51 - 0
Gems/O3DE/GeomNodes/Code/Source/Editor/UI/SettingsWidget.h

@@ -0,0 +1,51 @@
+/*
+ * 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
+
+#if !defined(Q_MOC_RUN)
+#include <AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI_Internals.h>
+#include <Editor/Configuration/GNConfiguration.h>
+#include <QWidget>
+
+#endif
+
+namespace GeomNodes
+{
+    namespace Editor
+    {
+        class SettingsWidget
+            : public QWidget
+            , private AzToolsFramework::IPropertyEditorNotify
+        {
+            Q_OBJECT
+
+        public:
+            AZ_CLASS_ALLOCATOR(SettingsWidget, AZ::SystemAllocator);
+
+            explicit SettingsWidget(QWidget* parent = nullptr);
+
+            void SetValue(const GNConfiguration& gnSystemConfiguration);
+
+        signals:
+            void onValueChanged(const GeomNodes::GNConfiguration& gnSystemConfiguration);
+
+        private:
+            void CreatePropertyEditor(QWidget* parent);
+
+            void BeforePropertyModified(AzToolsFramework::InstanceDataNode* /*node*/) override;
+            void AfterPropertyModified(AzToolsFramework::InstanceDataNode* /*node*/) override;
+            void SetPropertyEditingActive(AzToolsFramework::InstanceDataNode* /*node*/) override;
+            void SetPropertyEditingComplete(AzToolsFramework::InstanceDataNode* /*node*/) override;
+            void SealUndoStack() override;
+
+            AzToolsFramework::ReflectedPropertyEditor* m_propertyEditor;
+            GNConfiguration m_gnSystemConfiguration;
+        };
+    } // namespace Editor
+} // namespace GeomNodes

+ 31 - 0
Gems/O3DE/GeomNodes/Code/Source/Editor/UI/UI_common.h

@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#include "Editor/Commons.h"
+#include "Utils.h"
+
+namespace GeomNodes
+{
+    namespace Attributes
+    {
+        static const AZ::Crc32 FuncValidator = AZ_CRC("GNFuncValidator");
+        static const AZ::Crc32 SelectFunction = AZ_CRC("GNSelectFunction");
+        static const AZ::Crc32 ObfuscatedText = AZ_CRC("GNObfuscatedText");
+        static const AZ::Crc32 ClearButton = AZ_CRC("GNClearButton");
+        static const AZ::Crc32 RemovableReadOnly = AZ_CRC("GNRemovableReadOnly");
+        static const AZ::Crc32 ValidationChange = AZ_CRC("GNValidationChange");
+    } // namespace Attributes
+
+    namespace Handlers
+    {
+        static const AZ::Crc32 FileSelect = AZ_CRC("GNFileSelect");
+        static const AZ::Crc32 QValidatedBrowseEdit = AZ_CRC("GNQValBrowseEdit");
+    } // namespace Handlers
+} // namespace GeomNodes

+ 147 - 0
Gems/O3DE/GeomNodes/Code/Source/Editor/UI/Utils.cpp

@@ -0,0 +1,147 @@
+/*
+ * 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 "Utils.h"
+
+#include <AzCore/IO/SystemFile.h>
+#include <AzCore/StringFunc/StringFunc.h>
+#include <AzCore/Utils/Utils.h>
+#include <AzCore/std/algorithm.h>
+
+
+#include <QFileDialog>
+
+#include <Editor/Systems/GeomNodesSystem.h>
+
+namespace
+{
+    template<typename StringType>
+    void ToUnixPath(StringType& path)
+    {
+        AZStd::replace(path.begin(), path.end(), '\\', '/');
+    }
+
+    template<typename StringType>
+    StringType GetAbsoluteEngineRoot()
+    {
+        AZ::IO::FixedMaxPath engineRoot = AZ::Utils::GetEnginePath();
+
+        if (engineRoot.empty())
+        {
+            return "";
+        }
+
+        StringType engineRootString(engineRoot.c_str());
+        ToUnixPath(engineRootString);
+        return engineRootString;
+    }
+
+    template<typename StringType>
+    StringType GetAbsoluteProjectRoot()
+    {
+        AZ::IO::FixedMaxPath projectRoot = AZ::Utils::GetProjectPath();
+
+        if (projectRoot.empty())
+        {
+            return "";
+        }
+
+        StringType projectRootString(projectRoot.c_str());
+        ToUnixPath(projectRootString);
+        return projectRootString;
+    }
+
+    template<typename StringType>
+    StringType GetProjectName();
+
+    template<>
+    AZStd::string GetProjectName()
+    {
+        auto projectName = AZ::Utils::GetProjectName();
+        return AZStd::string{ projectName.c_str() };
+    }
+
+    template<>
+    QString GetProjectName()
+    {
+        auto projectName = AZ::Utils::GetProjectName();
+        return QString::fromUtf8(projectName.c_str(), aznumeric_cast<int>(projectName.size()));
+    }
+} // namespace
+
+namespace GeomNodes
+{
+    void* ConvertFunctorToVoid(AZStd::pair<QValidator::State, const QString> (*func)(const QString&))
+    {
+        return reinterpret_cast<void*>(func);
+    }
+
+    void* ConvertFunctorToVoid(void (*func)(const AZStd::string&))
+    {
+        return reinterpret_cast<void*>(func);
+    }
+
+    AZStd::string GetEngineRoot()
+    {
+        return GetAbsoluteEngineRoot<AZStd::string>();
+    }
+    AZStd::string GetProjectRoot()
+    {
+        return GetAbsoluteProjectRoot<AZStd::string>();
+    }
+
+    AZStd::string GetProjectName()
+    {
+        return ::GetProjectName<AZStd::string>();
+    }
+
+    QString SelectBlendFromFileDialog(const QString& currentFile)
+    {
+        // The selected file must be relative to this path
+        auto* gnSystem = GetGNSystem();
+
+        QString defaultPath = GetAbsoluteEngineRoot<QString>();
+        if (gnSystem != nullptr && !gnSystem->GetLastPath().empty())
+        {
+            defaultPath = gnSystem->GetLastPath().c_str();
+        }
+
+        QString startPath;
+
+        // Choose the starting path for file dialog
+        if (currentFile != "")
+        {
+            if (currentFile.contains(defaultPath))
+            {
+                startPath = currentFile;
+            }
+            else
+            {
+                startPath = defaultPath + currentFile;
+            }
+        }
+        else
+        {
+            startPath = defaultPath;
+        }
+
+        QString pickedPath =
+            QFileDialog::getOpenFileName(nullptr, QObject::tr("Select Blend file"), startPath, QObject::tr("Blender File (*.blend)"));
+        ToUnixPath(pickedPath);
+
+        if (!pickedPath.isEmpty())
+        {
+            AZStd::string lastFilePath;
+            AZ::StringFunc::Path::GetFolderPath(pickedPath.toUtf8().data(), lastFilePath);
+            gnSystem->SetLastPath(lastFilePath);
+            AZ_Printf("Utils", "pickedPath = %s", pickedPath.toUtf8().data());
+        }
+
+        return pickedPath;
+    }
+} // namespace GeomNodes

+ 28 - 0
Gems/O3DE/GeomNodes/Code/Source/Editor/UI/Utils.h

@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#include <AzCore/std/string/string.h>
+
+#include <QString>
+#include <QValidator>
+
+// Derived from ProjectSettingsTool
+namespace GeomNodes
+{
+    void* ConvertFunctorToVoid(AZStd::pair<QValidator::State, const QString> (*func)(const QString&));
+    void* ConvertFunctorToVoid(void (*func)(const AZStd::string&));
+    AZStd::string GetEngineRoot();
+    AZStd::string GetProjectRoot();
+    AZStd::string GetProjectName();
+
+    // Open file dialogs for each file type and return the result
+    // CurrentFile is where the dialog opens
+    QString SelectBlendFromFileDialog(const QString& currentFile);
+} // namespace GeomNodes

+ 33 - 0
Gems/O3DE/GeomNodes/Code/Source/Editor/UI/ValidationHandler.cpp

@@ -0,0 +1,33 @@
+/*
+ * 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 "ValidationHandler.h"
+
+//#include "PropertyFuncValLineEdit.h"
+#include "PropertyFuncValBrowseEdit.h"
+
+namespace GeomNodes
+{
+    void ValidationHandler::AddValidatorCtrl(PropertyFuncValBrowseEditCtrl* ctrl)
+    {
+        m_browseEditValidators.push_back(ctrl);
+    }
+
+    bool ValidationHandler::AllValid()
+    {
+        for (PropertyFuncValBrowseEditCtrl* ctrl : m_browseEditValidators)
+        {
+            if (!ctrl->ValidateAndShowErrors())
+            {
+                return false;
+            }
+        }
+
+        return true;
+    }
+} // namespace GeomNodes

+ 28 - 0
Gems/O3DE/GeomNodes/Code/Source/Editor/UI/ValidationHandler.h

@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#include <AzCore/std/containers/vector.h>
+
+namespace GeomNodes
+{
+    // Forward Declare
+    class PropertyFuncValLineEditCtrl;
+    class PropertyFuncValBrowseEditCtrl;
+
+    class ValidationHandler
+    {
+    public:
+        void AddValidatorCtrl(PropertyFuncValBrowseEditCtrl* ctrl);
+        bool AllValid();
+
+    private:
+        AZStd::vector<PropertyFuncValBrowseEditCtrl*> m_browseEditValidators;
+    };
+} // namespace GeomNodes

+ 143 - 0
Gems/O3DE/GeomNodes/Code/Source/Editor/UI/Validators.cpp

@@ -0,0 +1,143 @@
+/*
+ * 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 "Validators.h"
+
+#include <QDir>
+#include <QMimeDatabase>
+#include <QRegularExpression>
+
+#define STANDARD_SUCCESS GeomNodes::FunctorValidator::ReturnType(QValidator::Acceptable, "");
+
+namespace
+{
+    using RetType = GeomNodes::FunctorValidator::ReturnType;
+
+    static const int noMaxLength = -1;
+    static const char* blenderMimeType = "application/x-blender";
+    static const char* stringEmpty = "String is empty";
+
+    // Returns true if string is valid android package and apple bundle identifier
+    RetType RegularExpressionValidator(const QString& pattern, const QString& name, int maxLength = noMaxLength)
+    {
+        if (maxLength != noMaxLength && name.length() > maxLength)
+        {
+            return RetType(QValidator::Invalid, QObject::tr("Cannot be longer than %1 characters.").arg(QString::number(maxLength)));
+        }
+        else if (name.isEmpty())
+        {
+            return RetType(QValidator::Intermediate, QObject::tr(stringEmpty));
+        }
+
+        QRegularExpression regex(pattern);
+
+        QRegularExpressionMatch match = regex.match(name, 0, QRegularExpression::PartialPreferCompleteMatch);
+
+        if (match.hasMatch())
+        {
+            if (match.capturedLength(0) == name.length())
+            {
+                return STANDARD_SUCCESS;
+            }
+            else
+            {
+                return RetType(QValidator::Intermediate, "Input incorrect.");
+            }
+        }
+        if (match.hasPartialMatch())
+        {
+            return RetType(QValidator::Intermediate, QObject::tr("Partially matches requirements."));
+        }
+        else
+        {
+            return RetType(QValidator::Invalid, QObject::tr("Fails to match requirements at all."));
+        }
+    }
+} // namespace
+
+namespace GeomNodes
+{
+    namespace Validators
+    {
+        namespace Internal
+        {
+            // Returns true if file is readable and the correct mime type
+            RetType FileReadableAndCorrectType(const QString& path, const QString& fileType)
+            {
+                QDir dirPath(path);
+
+                if (dirPath.isReadable())
+                {
+                    QMimeDatabase mimeDB;
+                    QMimeType mimeType = mimeDB.mimeTypeForFile(path);
+
+                    std::string mimeName = mimeType.name().toStdString();
+                    if (mimeType.name() == fileType)
+                    {
+                        return STANDARD_SUCCESS;
+                    }
+                    else
+                    {
+                        return RetType(
+                            QValidator::Intermediate, QObject::tr("File type should be %1, but is %2.").arg(fileType).arg(mimeType.name()));
+                    }
+                }
+                else
+                {
+                    return RetType(QValidator::Intermediate, QObject::tr("File is not readable."));
+                }
+            }
+        } // namespace Internal
+
+        // Returns true if valid cross platform file or directory name
+        RetType FileName(const QString& name)
+        {
+            // There was a known issue on android with '.' used in directory names
+            // causing problems so it has been omitted from use
+            return RegularExpressionValidator("[\\w,-]+", name);
+        }
+
+        RetType FileNameOrEmpty(const QString& name)
+        {
+            if (IsNotEmpty(name).first == QValidator::Acceptable)
+            {
+                return FileName(name);
+            }
+            else
+            {
+                return STANDARD_SUCCESS;
+            }
+        }
+
+        // Returns true if string isn't empty
+        RetType IsNotEmpty(const QString& value)
+        {
+            if (!value.isEmpty())
+            {
+                return STANDARD_SUCCESS;
+            }
+            else
+            {
+                return RetType(QValidator::Intermediate, QObject::tr(stringEmpty));
+            }
+        }
+
+        // Returns true if path is empty or a valid blend file relative to <build dir>
+        RetType ValidBlenderOrEmpty(const QString& path)
+        {
+            if (IsNotEmpty(path).first == QValidator::Acceptable)
+            {
+                return Internal::FileReadableAndCorrectType(path, blenderMimeType);
+            }
+            else
+            {
+                return STANDARD_SUCCESS;
+            }
+        }
+    } // namespace Validators
+} // namespace GeomNodes

+ 32 - 0
Gems/O3DE/GeomNodes/Code/Source/Editor/UI/Validators.h

@@ -0,0 +1,32 @@
+/*
+ * 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 "FunctorValidator.h"
+
+namespace GeomNodes
+{
+    namespace Validators
+    {
+        namespace Internal
+        {
+            //! Returns true if file is readable and the correct mime type
+            FunctorValidator::ReturnType FileReadableAndCorrectType(const QString& path, const QString& fileType);
+        } // namespace Internal
+
+        //! Returns true if valid cross platform file or directory name
+        FunctorValidator::ReturnType FileName(const QString& name);
+        //! Returns true if valid cross platform file or directory name or empty
+        FunctorValidator::ReturnType FileNameOrEmpty(const QString& name);
+        //! Returns true if string isn't empty
+        FunctorValidator::ReturnType IsNotEmpty(const QString& value);
+        //! Returns true if path is empty or a valid blend file relative to <build dir>
+        FunctorValidator::ReturnType ValidBlenderOrEmpty(const QString& path);
+    } // namespace Validators
+} // namespace GeomNodes

+ 79 - 0
Gems/O3DE/GeomNodes/Code/Source/GeomNodes/Components/GeomNodesSystemComponent.cpp

@@ -0,0 +1,79 @@
+/*
+ * 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 "GeomNodesSystemComponent.h"
+
+#include <GeomNodes/GeomNodesTypeIds.h>
+
+#include <AzCore/Serialization/SerializeContext.h>
+
+namespace GeomNodes
+{
+    void GeomNodesSystemComponent::Reflect(AZ::ReflectContext* context)
+    {
+        if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
+        {
+            serializeContext->Class<GeomNodesSystemComponent, AZ::Component>()->Version(0);
+        }
+    }
+
+    void GeomNodesSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
+    {
+        provided.push_back(AZ_CRC_CE("GeomNodesService"));
+    }
+
+    void GeomNodesSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
+    {
+        incompatible.push_back(AZ_CRC_CE("GeomNodesService"));
+    }
+
+    void GeomNodesSystemComponent::GetRequiredServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& required)
+    {
+    }
+
+    void GeomNodesSystemComponent::GetDependentServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& dependent)
+    {
+    }
+
+    GeomNodesSystemComponent::GeomNodesSystemComponent()
+    {
+        if (GeomNodesInterface::Get() == nullptr)
+        {
+            GeomNodesInterface::Register(this);
+        }
+    }
+
+    GeomNodesSystemComponent::~GeomNodesSystemComponent()
+    {
+        if (GeomNodesInterface::Get() == this)
+        {
+            GeomNodesInterface::Unregister(this);
+        }
+    }
+
+    void GeomNodesSystemComponent::Init()
+    {
+    }
+
+    void GeomNodesSystemComponent::Activate()
+    {
+        GeomNodesRequestBus::Handler::BusConnect();
+        AZ::TickBus::Handler::BusConnect();
+    }
+
+    void GeomNodesSystemComponent::Deactivate()
+    {
+        AZ::TickBus::Handler::BusDisconnect();
+        GeomNodesRequestBus::Handler::BusDisconnect();
+    }
+
+    void GeomNodesSystemComponent::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
+    {
+    }
+
+} // namespace GeomNodes

+ 54 - 0
Gems/O3DE/GeomNodes/Code/Source/GeomNodes/Components/GeomNodesSystemComponent.h

@@ -0,0 +1,54 @@
+/*
+ * 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 <AzCore/Component/TickBus.h>
+#include <GeomNodes/GeomNodesBus.h>
+
+namespace GeomNodes
+{
+    class GeomNodesSystemComponent
+        : public AZ::Component
+        , protected GeomNodesRequestBus::Handler
+        , public AZ::TickBus::Handler
+    {
+    public:
+        AZ_COMPONENT(GeomNodesSystemComponent, GeomNodesSystemComponentTypeId);
+
+        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);
+
+        GeomNodesSystemComponent();
+        ~GeomNodesSystemComponent();
+
+    protected:
+        ////////////////////////////////////////////////////////////////////////
+        // GeomNodesRequestBus 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 GeomNodes

+ 22 - 0
Gems/O3DE/GeomNodes/Code/Source/GeomNodes/Modules/GeomNodesModule.cpp

@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include <GeomNodes/GeomNodesTypeIds.h>
+#include <GeomNodesModuleInterface.h>
+
+namespace GeomNodes
+{
+    class GeomNodesModule : public GeomNodesModuleInterface
+    {
+    public:
+        AZ_RTTI(GeomNodesModule, GeomNodesModuleTypeId, GeomNodesModuleInterface);
+        AZ_CLASS_ALLOCATOR(GeomNodesModule, AZ::SystemAllocator);
+    };
+} // namespace GeomNodes
+
+AZ_DECLARE_MODULE_CLASS(Gem_GeomNodes, GeomNodes::GeomNodesModule)

+ 45 - 0
Gems/O3DE/GeomNodes/Code/Source/GeomNodesModuleInterface.h

@@ -0,0 +1,45 @@
+/*
+ * 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/Memory/Memory_fwd.h>
+#include <AzCore/Module/Module.h>
+#include <AzCore/RTTI/RTTIMacros.h>
+#include <AzCore/RTTI/TypeInfoSimple.h>
+#include <GeomNodes/Components/GeomNodesSystemComponent.h>
+#include <GeomNodes/GeomNodesTypeIds.h>
+
+
+namespace GeomNodes
+{
+    class GeomNodesModuleInterface : public AZ::Module
+    {
+    public:
+        AZ_RTTI(GeomNodesModuleInterface, GeomNodesModuleInterfaceTypeId, AZ::Module);
+        AZ_CLASS_ALLOCATOR(GeomNodesModuleInterface, AZ::SystemAllocator);
+
+        GeomNodesModuleInterface()
+        {
+            // 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(),
+                {
+                    GeomNodesSystemComponent::CreateDescriptor(),
+                });
+        }
+
+        AZ::ComponentTypeList GetRequiredSystemComponents() const
+        {
+            return AZ::ComponentTypeList{
+                azrtti_typeid<GeomNodesSystemComponent>(),
+            };
+        }
+    };
+} // namespace GeomNodes

+ 11 - 0
Gems/O3DE/GeomNodes/Code/Tests/Clients/GeomNodesTest.cpp

@@ -0,0 +1,11 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include <AzTest/AzTest.h>
+
+AZ_UNIT_TEST_HOOK(DEFAULT_UNIT_TEST_ENV);

+ 11 - 0
Gems/O3DE/GeomNodes/Code/Tests/Tools/GeomNodesEditorTest.cpp

@@ -0,0 +1,11 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include <AzTest/AzTest.h>
+
+AZ_UNIT_TEST_HOOK(DEFAULT_UNIT_TEST_ENV);

+ 12 - 0
Gems/O3DE/GeomNodes/Code/geomnodes_api_files.cmake

@@ -0,0 +1,12 @@
+#
+# 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
+#
+#
+
+set(FILES
+    Include/GeomNodes/GeomNodesBus.h
+    Include/GeomNodes/GeomNodesTypeIds.h
+)

이 변경점에서 너무 많은 파일들이 변경되어 몇몇 파일들은 표시되지 않았습니다.