Prechádzať zdrojové kódy

- Updated handling of multiple meshes from using sub-entities to using ModelLodAsset's meshes.
- Did some cleanup and refactoring.

Signed-off-by: Jason Dela Cruz <[email protected]>

Jason Dela Cruz 2 rokov pred
rodič
commit
fe69104b81
18 zmenil súbory, kde vykonal 822 pridanie a 738 odobranie
  1. 24 0
      Gems/O3DE/GeomNodes/Code/Source/Editor/Common/GNConstants.h
  2. 93 218
      Gems/O3DE/GeomNodes/Code/Source/Editor/Components/EditorGeomNodesComponent.cpp
  3. 12 34
      Gems/O3DE/GeomNodes/Code/Source/Editor/Components/EditorGeomNodesComponent.h
  4. 0 196
      Gems/O3DE/GeomNodes/Code/Source/Editor/Components/EditorGeomNodesMeshComponent.cpp
  5. 2 0
      Gems/O3DE/GeomNodes/Code/Source/Editor/EBus/EditorGeomNodesComponentBus.h
  6. 0 19
      Gems/O3DE/GeomNodes/Code/Source/Editor/EBus/EditorGeomNodesMeshComponentBus.h
  7. 0 3
      Gems/O3DE/GeomNodes/Code/Source/Editor/Modules/GeomNodesEditorModule.cpp
  8. 17 16
      Gems/O3DE/GeomNodes/Code/Source/Editor/Rendering/Atom/GNAttributeBuffer.h
  9. 0 4
      Gems/O3DE/GeomNodes/Code/Source/Editor/Rendering/Atom/GNBuffer.h
  10. 267 0
      Gems/O3DE/GeomNodes/Code/Source/Editor/Rendering/GNMeshController.cpp
  11. 32 32
      Gems/O3DE/GeomNodes/Code/Source/Editor/Rendering/GNMeshController.h
  12. 148 33
      Gems/O3DE/GeomNodes/Code/Source/Editor/Rendering/GNMeshData.cpp
  13. 39 13
      Gems/O3DE/GeomNodes/Code/Source/Editor/Rendering/GNMeshData.h
  14. 87 9
      Gems/O3DE/GeomNodes/Code/Source/Editor/Rendering/GNModelData.cpp
  15. 24 2
      Gems/O3DE/GeomNodes/Code/Source/Editor/Rendering/GNModelData.h
  16. 63 132
      Gems/O3DE/GeomNodes/Code/Source/Editor/Rendering/GNRenderMesh.cpp
  17. 11 24
      Gems/O3DE/GeomNodes/Code/Source/Editor/Rendering/GNRenderMesh.h
  18. 3 3
      Gems/O3DE/GeomNodes/Code/geomnodes_editor_private_files.cmake

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

@@ -0,0 +1,24 @@
+#pragma once
+
+namespace GeomNodes
+{
+	//! 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

+ 93 - 218
Gems/O3DE/GeomNodes/Code/Source/Editor/Components/EditorGeomNodesComponent.cpp

@@ -1,24 +1,18 @@
-#include "Editor/Components/EditorGeomNodesComponent.h"
-#include "Editor/Components/EditorGeomNodesMeshComponent.h"
-#include <Editor/EBus/EditorGeomNodesMeshComponentBus.h>
-#include "Editor/UI/UI_common.h"
-#include "Editor/UI/Validators.h"
-#include "Editor/UI/Utils.h"
-
+#include <Editor/Components/EditorGeomNodesComponent.h>
+#include <Editor/UI/UI_common.h>
+#include <Editor/UI/Validators.h>
+#include <Editor/UI/Utils.h>
+#include <Editor/Systems/GNProperty.h>
+#include <Editor/Rendering/GNMeshController.h>
 #include <AzToolsFramework/API/ToolsApplicationAPI.h>
 #include <AzToolsFramework/API/EntityCompositionRequestBus.h>
-#include <AzToolsFramework/ToolsComponents/TransformComponent.h>
-#include <AtomLyIntegration/CommonFeatures/Mesh/MeshComponentConstants.h>
-#include <AtomLyIntegration/CommonFeatures/Material/MaterialComponentConstants.h>
-#include <AtomLyIntegration/CommonFeatures/Mesh/MeshComponentBus.h>
 #include <AzCore/Utils/Utils.h>
-#include <Editor/Systems/GNProperty.h>
+
 #include <AzCore/JSON/prettywriter.h>
 #include <AzCore/JSON/stringbuffer.h>
 #include <Atom/Feature/Mesh/MeshFeatureProcessorInterface.h>
 #include <Atom/RPI.Public/Scene.h>
 #include <AzCore/Component/NonUniformScaleBus.h>
-#include <AzCore/std/string/regex.h>
 
 namespace GeomNodes
 {
@@ -164,10 +158,7 @@ namespace GeomNodes
                     Ipc::IpcHandlerNotificationBus::Handler::BusConnect(entityId);
                 }
 
-                AzFramework::StringFunc::Path::GetFileName(path.c_str(), m_currentBlenderFileName);
-
-                AZStd::regex reg("[^\\w\\s]+");
-                m_currentBlenderFileName = AZStd::regex_replace(m_currentBlenderFileName, reg, "_");
+                m_controller->SetFileName(path);
             }
         }
     }
@@ -257,14 +248,13 @@ namespace GeomNodes
                 OnParamChange();
 
                 // Handle the materials as well
-                LoadMaterials(jsonDocument[Field::Materials]);
+                m_controller->LoadMaterials(jsonDocument[Field::Materials]);
                 SetWorkInProgress(false);
             }
             else if (jsonDocument.HasMember(Field::SHMOpen) && jsonDocument.HasMember(Field::MapId))
             {
-                
                 AZ::u64 mapId = jsonDocument[Field::MapId].GetInt64();
-                m_modelData.ReadData(mapId);
+                m_controller->ReadData(mapId);
                 auto msg = AZStd::string::format(
                     R"JSON(
                     {
@@ -277,8 +267,9 @@ namespace GeomNodes
                     mapId);
                 m_instance->SendIPCMsg(msg);
 
-                m_manageChildEntities = true; // tell OnTick that we want to manage the child entities
                 SetWorkInProgress(false);
+
+                m_controller->RebuildRenderMesh();
             }
             else if (jsonDocument.HasMember(Field::Export) && jsonDocument.HasMember(Field::Error))
             {
@@ -315,7 +306,7 @@ namespace GeomNodes
 				Field::Object,
                 m_currentObject.c_str(),
                 Field::FBXPath,
-                GenerateFBXPath().c_str());
+                m_controller->GenerateFBXPath().c_str());
 			m_instance->SendIPCMsg(msg);
             AZ_TracePrintf("EditorGeomNodesComponent", "[ExportToStaticMesh] m_workInProgress has changed")
             SetWorkInProgress(true);
@@ -334,11 +325,19 @@ namespace GeomNodes
 
     void EditorGeomNodesComponent::SetWorkInProgress(bool flag)
     {
-        if (m_workInProgress != flag)
-        {
-			m_workInProgress = flag;
-			EBUS_EVENT(AzToolsFramework::ToolsApplicationEvents::Bus, InvalidatePropertyDisplay, AzToolsFramework::Refresh_EntireTree);
-        }
+		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;
     }
 
     AZStd::string EditorGeomNodesComponent::ExportButtonText()
@@ -477,14 +476,6 @@ namespace GeomNodes
             }
         }
 
-        //TODO: parse materials
-
-        //// Remove all old properties, every confirmed property will have
-        //// a corresponding Element data
-        // RemovedOldProperties(m_scriptComponent.m_properties);
-
-        // SortProperties(m_scriptComponent.m_properties);
-        
         return true;
     }
 
@@ -521,28 +512,6 @@ namespace GeomNodes
         }
     }
 
-    void EditorGeomNodesComponent::LoadMaterials(const rapidjson::Value& materialArray)
-    {
-        m_modelData.SetMaterialPathFormat(""); // reset the material path format
-        // iterate through the material arrays and write them into files.
-		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 = GetProjectRoot() + "/";
-            AZStd::string materialFilePath = AZStd::string(AssetsFolderPath) + m_currentBlenderFileName + "/" + MaterialsFolder.data() + "/";
-            
-            fullFilePath += materialFilePath + materialName + MaterialExtension.data();
-
-            AZ::Utils::WriteFile(materialContent, fullFilePath.c_str());
-
-            // re-use materialFilePath and just append the azmaterial extension
-            m_modelData.SetMaterialPathFormat(materialFilePath + "%s" + AzMaterialExtension.data());
-		}
-    }
-
 	void EditorGeomNodesComponent::GetRequiredServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& required)
 	{
 		//required.push_back(AZ_CRC("TransformService", 0x8ee22c50));
@@ -572,16 +541,14 @@ namespace GeomNodes
     {
         AzToolsFramework::Components::EditorComponentBase::Activate();
         EditorGeomNodesComponentRequestBus::Handler::BusConnect(GetEntityId());
-        AzFramework::AssetCatalogEventBus::Handler::BusConnect();
-		AZ::TickBus::Handler::BusConnect();
+        
+        m_controller = AZStd::make_unique<GNMeshController>(GetEntityId());
     }
 
     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();
-        AZ::TickBus::Handler::BusDisconnect();
-        AzFramework::AssetCatalogEventBus::Handler::BusDisconnect();
         AzToolsFramework::Components::EditorComponentBase::Deactivate();
 
         if (m_instance)
@@ -591,88 +558,14 @@ namespace GeomNodes
         }
     }
 
-    void EditorGeomNodesComponent::OnTick(float /*deltaTime*/, AZ::ScriptTimePoint /*time*/)
-    {
-        if (m_manageChildEntities)
-        {
-            ManageChildEntities();
-            m_manageChildEntities = false;
-        }
-    }
-
     GNMeshData EditorGeomNodesComponent::GetMeshData(AZ::u64 entityId)
     {
         return m_modelData.GetMeshData(entityId);
     }
 
-    void EditorGeomNodesComponent::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);
-
-            if (m_workInProgress && (assetName == GenerateModelAssetName()))
-            {
-                auto transformComponent = GetEntity()->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);
-
-					
-                    //TODO: delete this entity
-                    // AZ::Interface<GNSystemInterface>::Get()
-                    // then call delete entities and descendants.
-
-                    EBUS_EVENT(AzToolsFramework::ToolsApplicationRequests::Bus, DeleteEntitiesAndAllDescendants, AzToolsFramework::EntityIdList{GetEntityId()});
-				}
-
-                SetWorkInProgress(false);
-            }
-            else
-            {
-				for (auto entityId : m_entityIdList)
-				{
-					auto meshData = m_modelData.GetMeshData((AZ::u64)entityId);
-					if (meshData.GetMaterial() == assetName)
-					{
-						AZ_Printf("GeomNodes", "added %s", assetName.c_str());
-						EditorGeomNodesMeshComponentEventBus::Event(entityId, &EditorGeomNodesMeshComponentEvents::OnMeshDataAssigned, meshData);
-						break;
-					}
-				}
-            }
-        }
-    }
-
-    void EditorGeomNodesComponent::OnCatalogAssetChanged(const AZ::Data::AssetId& assetId)
-    {
-        OnCatalogAssetAdded(assetId);
-    }
-
     void EditorGeomNodesComponent::Clear()
     {
+        m_controller.reset();
         m_enumValues.clear();
         ClearDataElements();
         EditorGeomNodesComponentRequestBus::Handler::BusDisconnect();
@@ -709,88 +602,70 @@ namespace GeomNodes
         return m_cachedStrings.insert(AZStd::make_pair(str, AZStd::string(str))).first->second.c_str();
     }
 
-    AZStd::string EditorGeomNodesComponent::GenerateFBXPath()
-    {
-		AZStd::string fullFilePath = GetProjectRoot() + "/";
-        AZStd::string filePath = AZStd::string(AssetsFolderPath) + m_currentBlenderFileName + "/";
-        fullFilePath += filePath + GenerateModelAssetName() + FbxExtension.data();
-        return fullFilePath;
-    }
-
-    AZStd::string EditorGeomNodesComponent::GenerateModelAssetName()
-    {
-        return m_currentBlenderFileName + "_" + AZStd::string::format("%llu", (AZ::u64)GetEntityId());
-    }
-
-    AZStd::string EditorGeomNodesComponent::GenerateAZModelFilename()
-    {
-		return GenerateModelAssetName() + AzModelExtension.data();
-    }
-
-    void EditorGeomNodesComponent::ManageChildEntities()
-    {
-		AzToolsFramework::EntityIdList entityIdList;
-
-		AZ::s32 entityCount = m_modelData.MeshCount() - m_entityIdList.size();
-		if (entityCount > 0)
-		{
-			for ([[maybe_unused]] AZ::s32 i = 0; i < entityCount; i++)
-			{
-				AZ::EntityId entityId;
-				EBUS_EVENT_RESULT(entityId, AzToolsFramework::EditorRequests::Bus, CreateNewEntity, GetEntityId());
-
-				entityIdList.push_back(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::EditorMaterialComponentTypeId, */azrtti_typeid<EditorGeomNodesMeshComponent>() });
-
-			if (addedComponentsResult.IsSuccess())
-			{
-				AzToolsFramework::ToolsApplicationEvents::Bus::Broadcast(&AzToolsFramework::ToolsApplicationEvents::InvalidatePropertyDisplay, AzToolsFramework::Refresh_EntireTree_NewContent);
-			}
-
-			m_entityIdList.insert(m_entityIdList.begin(), entityIdList.begin(), entityIdList.end());
-		}
-		else if (entityCount < 0)
-		{
-			entityCount *= -1; // flipping the sign so we can use it
-
-			for ([[maybe_unused]] AZ::s32 i = 0; i < entityCount; i++) {
-				entityIdList.insert(entityIdList.begin(), m_entityIdList.back());
-				m_entityIdList.pop_back();
-			}
-
-			AzToolsFramework::ToolsApplicationRequestBus::Broadcast(&AzToolsFramework::ToolsApplicationRequests::DeleteEntities, entityIdList);
-		}
-
-		// assign the mesh data to the entityId
-		for (auto entityId : m_entityIdList)
-		{
-			m_modelData.AssignMeshData((AZ::u64)entityId);
-            auto meshData = m_modelData.GetMeshData((AZ::u64)entityId);
-
-            auto materialPath = meshData.GetMaterialPath();
-            AZ_Printf("GeomNodes", "assigned mesh data %s", materialPath.c_str());
-            
-            if (AZ::IO::FileIOBase::GetInstance()->Exists(materialPath.c_str()))
-            {
-				AZ::Data::AssetId materialAssetId;
-				EBUS_EVENT_RESULT(materialAssetId, AZ::Data::AssetCatalogRequestBus, GetAssetIdByPath, materialPath.c_str(), AZ::Data::s_invalidAssetType, false);
-
-				// If found, notify mesh that the mesh data is assigned and material is ready.
-				if (materialAssetId.IsValid())
-				{
-					AZ_Printf("GeomNodes", "    OnMeshDataAssigned called %s", meshData.GetMaterialPath().c_str());
-					EditorGeomNodesMeshComponentEventBus::Event(entityId, &EditorGeomNodesMeshComponentEvents::OnMeshDataAssigned, meshData);
-				}
-            }
-		}
-
-        
-        AzToolsFramework::ToolsApplicationRequestBus::Broadcast(&AzToolsFramework::ToolsApplicationRequests::MarkEntitiesDeselected, m_entityIdList);
-        AzToolsFramework::ToolsApplicationRequestBus::Broadcast(&AzToolsFramework::ToolsApplicationRequests::MarkEntitySelected, GetEntityId());
-    }
+  //  void EditorGeomNodesComponent::ManageChildEntities()
+  //  {
+		//AzToolsFramework::EntityIdList entityIdList;
+
+		//AZ::s32 entityCount = m_modelData.MeshCount() - m_entityIdList.size();
+		//if (entityCount > 0)
+		//{
+		//	for ([[maybe_unused]] AZ::s32 i = 0; i < entityCount; i++)
+		//	{
+		//		AZ::EntityId entityId;
+		//		EBUS_EVENT_RESULT(entityId, AzToolsFramework::EditorRequests::Bus, CreateNewEntity, GetEntityId());
+
+		//		entityIdList.push_back(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::EditorMaterialComponentTypeId, */azrtti_typeid<EditorGeomNodesMeshComponent>() });
+
+		//	if (addedComponentsResult.IsSuccess())
+		//	{
+		//		AzToolsFramework::ToolsApplicationEvents::Bus::Broadcast(&AzToolsFramework::ToolsApplicationEvents::InvalidatePropertyDisplay, AzToolsFramework::Refresh_EntireTree_NewContent);
+		//	}
+
+		//	m_entityIdList.insert(m_entityIdList.begin(), entityIdList.begin(), entityIdList.end());
+		//}
+		//else if (entityCount < 0)
+		//{
+		//	entityCount *= -1; // flipping the sign so we can use it
+
+		//	for ([[maybe_unused]] AZ::s32 i = 0; i < entityCount; i++) {
+		//		entityIdList.insert(entityIdList.begin(), m_entityIdList.back());
+		//		m_entityIdList.pop_back();
+		//	}
+
+		//	AzToolsFramework::ToolsApplicationRequestBus::Broadcast(&AzToolsFramework::ToolsApplicationRequests::DeleteEntities, entityIdList);
+		//}
+
+		//// assign the mesh data to the entityId
+		//for (auto entityId : m_entityIdList)
+		//{
+		//	m_modelData.AssignMeshData((AZ::u64)entityId);
+  //          auto meshData = m_modelData.GetMeshData((AZ::u64)entityId);
+
+  //          auto materialPath = meshData.GetMaterialPath();
+  //          AZ_Printf("GeomNodes", "assigned mesh data %s", materialPath.c_str());
+  //          
+  //          if (AZ::IO::FileIOBase::GetInstance()->Exists(materialPath.c_str()))
+  //          {
+		//		AZ::Data::AssetId materialAssetId;
+		//		EBUS_EVENT_RESULT(materialAssetId, AZ::Data::AssetCatalogRequestBus, GetAssetIdByPath, materialPath.c_str(), AZ::Data::s_invalidAssetType, false);
+                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    
+		//		// If found, notify mesh that the mesh data is assigned and material is ready.
+		//		if (materialAssetId.IsValid())
+		//		{
+		//			AZ_Printf("GeomNodes", "    OnMeshDataAssigned called %s", meshData.GetMaterialPath().c_str());
+		//			EditorGeomNodesMeshComponentEventBus::Event(entityId, &EditorGeomNodesMeshComponentEvents::OnMeshDataAssigned, meshData);
+		//		}
+  //          }
+		//}
+
+  //      
+  //      AzToolsFramework::ToolsApplicationRequestBus::Broadcast(&AzToolsFramework::ToolsApplicationRequests::MarkEntitiesDeselected, m_entityIdList);
+  //      AzToolsFramework::ToolsApplicationRequestBus::Broadcast(&AzToolsFramework::ToolsApplicationRequests::MarkEntitySelected, GetEntityId());
+  //  }
 
     void EditorGeomNodesComponent::ClearDataElements()
     {

+ 12 - 34
Gems/O3DE/GeomNodes/Code/Source/Editor/Components/EditorGeomNodesComponent.h

@@ -7,18 +7,16 @@
 #include "Editor/Rendering/GNModelData.h"
 #include <Editor/EBus/IpcHandlerBus.h>
 #include <Editor/EBus/EditorGeomNodesComponentBus.h>
-#include <AzCore/Component/TickBus.h>
+#include <Editor/Common/GNConstants.h>
 #include <AzToolsFramework/Entity/EntityTypes.h>
-#include <AzFramework/Asset/AssetCatalogBus.h>
 
 namespace GeomNodes
 {
+    class GNMeshController;
     class EditorGeomNodesComponent
         : public AzToolsFramework::Components::EditorComponentBase
         , private Ipc::IpcHandlerNotificationBus::Handler
-        , private AZ::TickBus::Handler
         , private EditorGeomNodesComponentRequestBus::Handler
-        , private AzFramework::AssetCatalogEventBus::Handler
     {
     public:
         AZ_EDITOR_COMPONENT(EditorGeomNodesComponent, "{E59507EF-9EBB-4F6C-8D89-92DCA57722E5}", EditorComponentBase);
@@ -37,17 +35,13 @@ namespace GeomNodes
         void Activate() override;
         void Deactivate() override;
 
-		// AZ::TickBus overrides ...
-		void OnTick(float deltaTime, AZ::ScriptTimePoint time) override;
-
-        // EditorGeomNodesComponentRequestBus overrides ...
+		// EditorGeomNodesComponentRequestBus overrides ...
         GNMeshData GetMeshData(AZ::u64 entityId) override;
-    
-    private:
-		// AssetCatalogEventBus::Handler ...
-        void OnCatalogAssetAdded(const AZ::Data::AssetId& assetId) override;
-        void OnCatalogAssetChanged(const AZ::Data::AssetId& assetId) override;
+        void SetWorkInProgress(bool flag) override;
+        bool GetWorkInProgress() override;
 
+    private:
+		
     protected:
         //got this from ScriptEditorComponent
         struct ElementInfo
@@ -60,26 +54,16 @@ namespace GeomNodes
                                                 // which means alphabetical sort will be used
         };
         
-		//! constants
-		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;
-        
-        void Clear();
+		void Clear();
         void OnPathChange(const AZStd::string& path);
         void OnParamChange();
+        // IpcHandlerNotificationBus overrides...
         void OnMessageReceived(const AZ::u8* content, const AZ::u64 length) override;
 
         void ExportToStaticMesh();
         bool IsBlenderFileLoaded();
         bool IsWorkInProgress();
-        void SetWorkInProgress(bool flag);
-
+        
         AZStd::string ExportButtonText();
 
         void LoadObjects(const rapidjson::Value& objectNameArray, const rapidjson::Value& objectArray);
@@ -90,8 +74,7 @@ namespace GeomNodes
         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 LoadMaterials(const rapidjson::Value& materialArray);
-
+        
         void ClearDataElements();
 
         const AZ::Edit::ElementData* GetDataElement(const void* element, const AZ::Uuid& typeUuid) const;
@@ -102,12 +85,6 @@ namespace GeomNodes
         void AddDataElement(GNProperty* gnParam, ElementInfo& ei);
 
         const char* CacheString(const char* str);
-        AZStd::string GenerateFBXPath();
-        AZStd::string GenerateModelAssetName();
-        AZStd::string GenerateAZModelFilename();
-        void ManageChildEntities();
-        AZStd::atomic_bool m_manageChildEntities{ false };
-
         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_objectInfos;
@@ -116,6 +93,7 @@ namespace GeomNodes
 
         GNParamContext m_paramContext;
         GNModelData m_modelData;
+        AZStd::unique_ptr<GNMeshController> m_controller;
 
         AZStd::string m_blenderFile;
         AZStd::string m_currentObject;

+ 0 - 196
Gems/O3DE/GeomNodes/Code/Source/Editor/Components/EditorGeomNodesMeshComponent.cpp

@@ -1,196 +0,0 @@
-#include "EditorGeomNodesMeshComponent.h"
-#include <AzCore/Serialization/EditContext.h>
-#include <Editor/Rendering/GNMeshData.h>
-#include <AzCore/Debug/Profiler.h>
-#include <AzCore/Component/NonUniformScaleBus.h>
-#include <Editor/Rendering/GNRenderMesh.h>
-
-namespace GeomNodes
-{
-	EditorGeomNodesMeshComponent::EditorGeomNodesMeshComponent()
-	{
-	}
-
-	EditorGeomNodesMeshComponent::~EditorGeomNodesMeshComponent()
-	{
-	}
-
-	void EditorGeomNodesMeshComponent::Reflect(AZ::ReflectContext* context)
-	{
-		if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
-		{
-			serializeContext->Class<EditorGeomNodesMeshComponent, EditorComponentBase>()
-				->Version(1);
-
-			AZ::EditContext* ec = serializeContext->GetEditContext();
-			if (ec)
-			{
-				ec->Class<EditorGeomNodesMeshComponent>(
-					"Geometry Node Mesh",
-					"The Geometry Node Mesh component is equivalent to a normal Mesh component but the model data comes from blender. ")
-					->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);
-			}
-		}
-	}
-
-	void EditorGeomNodesMeshComponent::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent)
-	{
-		dependent.push_back(AZ_CRC("TransformService", 0x8ee22c50));
-	}
-
-	void EditorGeomNodesMeshComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
-	{
-		provided.push_back(AZ_CRC_CE("EditorGeomNodesMeshService"));
-		provided.push_back(AZ_CRC_CE("MaterialConsumerService"));
-	}
-
-	void EditorGeomNodesMeshComponent::GetIncompatibleServices(
-		[[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& incompatible)
-	{
-		incompatible.push_back(AZ_CRC("EditorGeomNodesMeshService"));
-	}
-
-	void EditorGeomNodesMeshComponent::Init()
-	{
-		if (m_renderMesh)
-		{
-			return;
-		}
-
-		m_entityId = GetEntityId();
-		m_renderMesh = AZStd::make_unique<GNRenderMesh>(m_entityId);
-	}
-
-	void EditorGeomNodesMeshComponent::Activate()
-	{
-		AzToolsFramework::Components::EditorComponentBase::Activate();
-		AZ::TransformNotificationBus::Handler::BusConnect(m_entityId);
-		EditorGeomNodesMeshComponentEventBus::Handler::BusConnect(m_entityId);
-
-		AZ::TransformBus::EventResult(m_parentId, m_entityId, &AZ::TransformBus::Events::GetParentId);
-
-		
-		m_worldFromLocal = AZ::Transform::CreateIdentity();
-		AZ::TransformBus::EventResult(m_worldFromLocal, m_entityId, &AZ::TransformBus::Events::GetWorldTM);
-	}
-
-	void EditorGeomNodesMeshComponent::Deactivate()
-	{
-		AzToolsFramework::EditorComponentSelectionRequestsBus::Handler::BusDisconnect();
-		AzFramework::BoundsRequestBus::Handler::BusDisconnect();
-		EditorGeomNodesMeshComponentEventBus::Handler::BusDisconnect();
-		AZ::TransformNotificationBus::Handler::BusDisconnect();
-		AzToolsFramework::Components::EditorComponentBase::Deactivate();
-		m_renderMesh.reset();
-	}
-
-	AZ::Aabb EditorGeomNodesMeshComponent::GetEditorSelectionBoundsViewport([[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo)
-	{
-		return GetWorldBounds();
-	}
-
-	bool EditorGeomNodesMeshComponent::EditorSelectionIntersectRayViewport(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, GetEntityId(), &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 EditorGeomNodesMeshComponent::SupportsEditorRayIntersect()
-	{
-		return true;
-	}
-
-	AZ::Aabb EditorGeomNodesMeshComponent::GetWorldBounds()
-	{
-		AZ_PROFILE_FUNCTION(GeomNodes);
-
-		if (!m_worldAabb.has_value())
-		{
-			m_worldAabb = GetLocalBounds();
-			m_worldAabb->ApplyTransform(m_worldFromLocal);
-		}
-
-		return m_worldAabb.value();
-	}
-
-	AZ::Aabb EditorGeomNodesMeshComponent::GetLocalBounds()
-	{
-		AZ_PROFILE_FUNCTION(GeomNodes);
-
-		if (!m_localAabb.has_value() && m_meshData.VertexCount() > 0)
-		{
-			m_localAabb = m_meshData.GetAabb();
-		}
-
-		return m_localAabb.value();
-	}
-	
-	void EditorGeomNodesMeshComponent::OnTransformChanged([[maybe_unused]] const AZ::Transform& local, const AZ::Transform& world)
-	{
-		AZ_PROFILE_FUNCTION(GeomNodes);
-
-		m_worldAabb.reset();
-		m_localAabb.reset();
-
-		m_worldFromLocal = world;
-
-		if (m_renderMesh)
-		{
-			m_renderMesh->UpdateTransform(world);
-		}
-	}
-
-	void EditorGeomNodesMeshComponent::RebuildRenderMesh()
-	{
-		AZ_PROFILE_FUNCTION(GeomNodes);
-
-		m_worldAabb.reset();
-		m_localAabb.reset();
-
-		// EditorGeomNodesComponentRequestBus::EventResult(m_meshData, m_parentId, &EditorGeomNodesComponentRequests::GetMeshData, (AZ::u64)m_entityId);
-
-		if (m_meshData.VertexCount() > 0)
-		{
-			m_renderMesh->BuildMesh(m_meshData, m_worldFromLocal);
-			m_renderMesh->UpdateTransform(m_worldFromLocal);
-		}
-	}
-	
-	void EditorGeomNodesMeshComponent::OnMeshDataAssigned(const GNMeshData& meshData)
-	{
-		m_meshData = meshData;
-		AzToolsFramework::EditorComponentSelectionRequestsBus::Handler::BusDisconnect();
-		AzFramework::BoundsRequestBus::Handler::BusDisconnect();
-		RebuildRenderMesh();
-		AzFramework::BoundsRequestBus::Handler::BusConnect(m_entityId);
-		AzToolsFramework::EditorComponentSelectionRequestsBus::Handler::BusConnect(m_entityId);
-
-	}
-} // namespace GeomNodes

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

@@ -10,6 +10,8 @@ namespace GeomNodes
     {
     public:
         virtual GNMeshData GetMeshData(AZ::u64 entityId) = 0;
+        virtual void SetWorkInProgress(bool flag) = 0;
+        virtual bool GetWorkInProgress() = 0;
 
 	protected:
 		~EditorGeomNodesComponentRequests() = default;

+ 0 - 19
Gems/O3DE/GeomNodes/Code/Source/Editor/EBus/EditorGeomNodesMeshComponentBus.h

@@ -1,19 +0,0 @@
-#pragma once
-
-#include <AzCore/Component/ComponentBus.h>
-#include <Editor/Rendering/GNMeshData.h>
-
-namespace GeomNodes
-{
-    
-    class EditorGeomNodesMeshComponentEvents : public AZ::ComponentBus
-    {
-    public:
-        virtual void OnMeshDataAssigned(const GNMeshData& /*meshData*/) {};
-
-	protected:
-		~EditorGeomNodesMeshComponentEvents() = default;
-    };
-
-    using EditorGeomNodesMeshComponentEventBus = AZ::EBus<EditorGeomNodesMeshComponentEvents>;
-} // namespace GeomNodes

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

@@ -2,7 +2,6 @@
 #include <GeomNodesModuleInterface.h>
 #include "Editor/Components/EditorGeomNodesSystemComponent.h"
 #include "Editor/Components/EditorGeomNodesComponent.h"
-#include "Editor/Components/EditorGeomNodesMeshComponent.h"
 #include "Editor/Systems/GeomNodesSystem.h"
 #include <Editor/Configuration/GNEditorSettingsRegistryManager.h>
 
@@ -26,7 +25,6 @@ namespace GeomNodes
             m_descriptors.insert(m_descriptors.end(), {
                     EditorGeomNodesSystemComponent::CreateDescriptor(),
                     EditorGeomNodesComponent::CreateDescriptor(),
-                    EditorGeomNodesMeshComponent::CreateDescriptor(),
             });
         }
 
@@ -44,7 +42,6 @@ namespace GeomNodes
             return AZ::ComponentTypeList {
                 azrtti_typeid<EditorGeomNodesSystemComponent>(),
                 azrtti_typeid<EditorGeomNodesComponent>(),
-                azrtti_typeid<EditorGeomNodesMeshComponent>(),
             };
         }
     private:

+ 17 - 16
Gems/O3DE/GeomNodes/Code/Source/Editor/Rendering/Atom/GNAttributeBuffer.h

@@ -1,24 +1,14 @@
 #pragma once
 
 #include "GNBuffer.h"
-
+#include <Editor/Common/GNConstants.h>
 #include <Atom/RHI.Reflect/ShaderSemantic.h>
 #include <Atom/RPI.Reflect/Model/ModelLodAssetCreator.h>
+#include <Editor/Rendering/GNMeshData.h>
 
 namespace GeomNodes
 {
-    //! Attributes for mesh vertices.
-    enum class AttributeType
-    {
-        Position,
-        Normal,
-        Tangent,
-        Bitangent,
-        UV,
-        Color
-    };
-
-    //! The number of attributes required by the mesh.
+	//! The number of attributes required by the mesh.
     inline constexpr uint32_t NumAttributes = 6;
 
     //! Trait to describe mesh vertex attribute format.
@@ -33,6 +23,7 @@ namespace GeomNodes
     {
         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
@@ -41,6 +32,7 @@ namespace GeomNodes
     {
         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.
@@ -49,6 +41,7 @@ namespace GeomNodes
     {
         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.
@@ -57,6 +50,7 @@ namespace GeomNodes
     {
         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.
@@ -65,6 +59,7 @@ namespace GeomNodes
     {
         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.
@@ -73,6 +68,7 @@ namespace GeomNodes
     {
         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.
@@ -102,7 +98,7 @@ namespace GeomNodes
         void AddLodStreamBuffer(AZ::RPI::ModelLodAssetCreator& modelLodCreator) const;
 
         //! Adds this attribute buffer to the mesh.
-        void AddMeshStreamBuffer(AZ::RPI::ModelLodAssetCreator& modelLodCreator) const;
+        void AddMeshStreamBuffer(AZ::RPI::ModelLodAssetCreator& modelLodCreator, const GNMeshData& meshData) const;
 
         //! Returns true of the attribute buffer is valid, otherwise false.
         bool IsValid() const;
@@ -114,6 +110,7 @@ namespace GeomNodes
     private:
         typename Trait::BufferType m_buffer;
         AZ::RHI::ShaderSemantic m_shaderSemantic;
+        AZ::RHI::Format m_format;
     };
 
     template<AttributeType AttributeTypeT>
@@ -121,6 +118,7 @@ namespace GeomNodes
     AttributeBuffer<AttributeTypeT>::AttributeBuffer(const AZStd::vector<VertexStreamDataType>& data)
         : m_buffer(data)
         , m_shaderSemantic(AZ::Name(Trait::ShaderSemantic))
+        , m_format(Trait::RHIFormat)
     {
         if (!IsValid())
         {
@@ -161,9 +159,12 @@ namespace GeomNodes
     }
 
     template<AttributeType AttributeTypeT>
-    void AttributeBuffer<AttributeTypeT>::AddMeshStreamBuffer(AZ::RPI::ModelLodAssetCreator& modelLodCreator) const
+    void AttributeBuffer<AttributeTypeT>::AddMeshStreamBuffer(AZ::RPI::ModelLodAssetCreator& modelLodCreator, const GNMeshData& meshData) const
     {
-        modelLodCreator.AddMeshStreamBuffer(GetShaderSemantic(), AZ::Name(), GetBufferAssetView());
+        modelLodCreator.AddMeshStreamBuffer(GetShaderSemantic(), AZ::Name(),
+            { m_buffer.GetBuffer(), 
+                AZ::RHI::BufferViewDescriptor::CreateTyped(meshData.GetOffset<AttributeTypeT>(), meshData.GetCount<AttributeTypeT>(), m_format)
+            });
     }
 
     template<AttributeType AttributeTypeT>

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

@@ -6,10 +6,6 @@
 #include <Atom/RPI.Reflect/Buffer/BufferAsset.h>
 #include <Atom/RPI.Reflect/Buffer/BufferAssetCreator.h>
 #include <Atom/RPI.Reflect/Buffer/BufferAssetView.h>
-//#include <AzCore/Math/PackedVector3.h>
-//#include <AzCore/Math/Vector2.h>
-//#include <AzCore/Math/Vector3.h>
-//#include <AzCore/Math/Vector4.h>
 
 namespace GeomNodes
 {

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

@@ -0,0 +1,267 @@
+#include <Editor/Rendering/GNMeshController.h>
+#include <Editor/Rendering/GNRenderMesh.h>
+#include <Editor/UI/Utils.h>
+#include <Editor/Common/GNConstants.h>
+#include <Editor/EBus/EditorGeomNodesComponentBus.h>
+
+#include <AzCore/Debug/Profiler.h>
+#include <AzCore/Component/NonUniformScaleBus.h>
+#include <AzCore/std/string/regex.h>
+#include <AzCore/Utils/Utils.h>
+#include <AzToolsFramework/API/ToolsApplicationAPI.h>
+#include <AzToolsFramework/API/EntityCompositionRequestBus.h>
+#include <AzToolsFramework/ToolsComponents/TransformComponent.h>
+#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
+#include <AtomLyIntegration/CommonFeatures/Mesh/MeshComponentConstants.h>
+#include <AtomLyIntegration/CommonFeatures/Material/MaterialComponentConstants.h>
+#include <AtomLyIntegration/CommonFeatures/Mesh/MeshComponentBus.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_worldFromLocal = AZ::Transform::CreateIdentity();
+		AZ::TransformBus::EventResult(m_worldFromLocal, 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(const AzFramework::ViewportInfo& /*viewportInfo*/)
+    {
+        return GetWorldBounds();
+    }
+    bool GNMeshController::EditorSelectionIntersectRayViewport(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_worldFromLocal);
+		}
+
+		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_worldFromLocal);
+				m_renderMesh->UpdateTransform(m_worldFromLocal);
+				AzFramework::BoundsRequestBus::Handler::BusConnect(m_entityId);
+				AzToolsFramework::EditorComponentSelectionRequestsBus::Handler::BusConnect(m_entityId);
+			});
+		}
+
+		m_requestingMeshRebuild = false;
+	}
+
+	void GNMeshController::ReadData(AZ::u64 mapId)
+	{
+		m_modelData.ReadData(mapId);
+	}
+
+	void GNMeshController::LoadMaterials(const rapidjson::Value& materialArray)
+	{
+		m_materialWaitList.clear();
+		AZStd::vector<AZStd::string> materialList;
+		// iterate through the material arrays and write them into files.
+		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 = GetProjectRoot() + "/";
+			AZStd::string materialFilePath = AZStd::string(AssetsFolderPath) + m_blenderFilename + "/" + MaterialsFolder.data() + "/";
+
+			fullFilePath += materialFilePath + materialName + MaterialExtension.data();
+
+			AZ::Utils::WriteFile(materialContent, fullFilePath.c_str());
+
+			AZStd::string azMaterialPath = materialFilePath + materialName + AzMaterialExtension.data();
+			materialList.push_back(azMaterialPath);
+			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);
+				}
+			}
+		}
+
+		if (!materialList.empty())
+		{
+			m_renderMesh->SetMaterialList(materialList);
+		}
+	}
+
+	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);
+	}
+
+	AZStd::string GNMeshController::GenerateAZModelFilename()
+	{
+		return GenerateModelAssetName() + AzModelExtension.data();
+	}
+
+    void GNMeshController::OnTransformChanged(const AZ::Transform& /*local*/, const AZ::Transform& world)
+    {
+		m_worldAabb.reset();
+		m_localAabb.reset();
+
+		m_worldFromLocal = 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_requestingMeshRebuild && m_materialWaitList.empty()) {
+							RebuildRenderMesh();
+						}
+					}
+				}
+			}
+		}
+	}
+	void GNMeshController::OnCatalogAssetChanged(const AZ::Data::AssetId& assetId)
+	{
+		OnCatalogAssetAdded(assetId);
+	}
+} // namespace GeomNodes

+ 32 - 32
Gems/O3DE/GeomNodes/Code/Source/Editor/Components/EditorGeomNodesMeshComponent.h → Gems/O3DE/GeomNodes/Code/Source/Editor/Rendering/GNMeshController.h

@@ -1,36 +1,22 @@
 #pragma once
-#include <AzToolsFramework/ToolsComponents/EditorComponentBase.h>
 #include <AzFramework/Visibility/BoundsBus.h>
+#include <AzFramework/Asset/AssetCatalogBus.h>
 #include <AzCore/Component/TransformBus.h>
 #include <AzToolsFramework/API/ComponentEntitySelectionBus.h>
-#include <Editor/EBus/EditorGeomNodesMeshComponentBus.h>
+#include <Editor/Rendering/GNModelData.h>
 
 namespace GeomNodes
 {
-	class GNMeshData;
 	class GNRenderMesh;
-	class EditorGeomNodesMeshComponent
-		: public AzToolsFramework::Components::EditorComponentBase
-		, public AzFramework::BoundsRequestBus::Handler
+    class GNMeshController
+		: public AzFramework::BoundsRequestBus::Handler
 		, public AzToolsFramework::EditorComponentSelectionRequestsBus::Handler
 		, private AZ::TransformNotificationBus::Handler
-		, private EditorGeomNodesMeshComponentEventBus::Handler
-	{
-	public:
-		AZ_EDITOR_COMPONENT(EditorGeomNodesMeshComponent, "{B9B5FDEB-6D0F-432E-AE19-1F9EC41EF192}", EditorComponentBase);
-
-		static void Reflect(AZ::ReflectContext* context);
-
-		static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent);
-		static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided);
-		static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible);
-
-		EditorGeomNodesMeshComponent();
-		virtual ~EditorGeomNodesMeshComponent();
-
-		void Init() override;
-		void Activate() override;
-		void Deactivate() override;
+		, private AzFramework::AssetCatalogEventBus::Handler
+    {
+    public:
+        explicit GNMeshController(AZ::EntityId entityId);
+        ~GNMeshController();
 
 		// EditorComponentSelectionRequestsBus overrides ...
 		AZ::Aabb GetEditorSelectionBoundsViewport(const AzFramework::ViewportInfo& viewportInfo) override;
@@ -43,24 +29,38 @@ namespace GeomNodes
 		AZ::Aabb GetWorldBounds() override;
 		AZ::Aabb GetLocalBounds() override;
 
-	private:
+		void RebuildRenderMesh();
+		void ReadData(AZ::u64 mapId);
+
+		void LoadMaterials(const rapidjson::Value& materialArray);
+		void SetFileName(const AZStd::string& path);
+		AZStd::string GenerateFBXPath();
+		AZStd::string GenerateModelAssetName();
+		AZStd::string GenerateAZModelFilename();
+
+
+    private:
 		// TransformNotificationBus overrides ...
 		void OnTransformChanged(const AZ::Transform& local, const AZ::Transform& world) override;
 
-		// EditorGeomNodesMeshComponentEventBus overrides ...
-		void OnMeshDataAssigned(const GNMeshData& meshData) override;
-
-		void RebuildRenderMesh();
+		// AssetCatalogEventBus::Handler ...
+		void OnCatalogAssetAdded(const AZ::Data::AssetId& assetId) override;
+		void OnCatalogAssetChanged(const AZ::Data::AssetId& assetId) override;
 
-		AZ::EntityId m_entityId;
-		AZ::EntityId m_parentId;
+        AZ::EntityId m_entityId;
 
-		GNMeshData m_meshData;
+		GNModelData m_modelData;
 
 		AZStd::unique_ptr<GNRenderMesh> m_renderMesh;
 		AZ::Transform m_worldFromLocal = AZ::Transform::CreateIdentity();
 
 		AZStd::optional<AZ::Aabb> m_worldAabb; //!< Cached world aabb (used for selection/view determination).
 		AZStd::optional<AZ::Aabb> m_localAabb; //!< Cached local aabb (used for center pivot calculation).
-	};
+
+		AZStd::string m_blenderFilename;
+		AZStd::vector<AZStd::string> m_materialWaitList;	//!< List for materials building in AP. Having an empty list all materials are built and ready for loading.
+		
+		bool m_requestingMeshRebuild = false;
+    };
+
 } // namespace GeomNodes

+ 148 - 33
Gems/O3DE/GeomNodes/Code/Source/Editor/Rendering/GNMeshData.cpp

@@ -186,49 +186,149 @@ namespace GeomNodes
         }
     }
 
-    const AZ::u32 GNMeshData::VertexCount() const
+	template<AttributeType AttributeTypeT>
+	AZ::u32 GNMeshData::GetCount() const
     {
-        return aznumeric_cast<AZ::u32>(m_indices.size());
+		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);
+		}
     }
 
-    const U32Vector& GNMeshData::GetIndices() const
+    template<AttributeType AttributeTypeT>
+    AZ::u32 GNMeshData::GetOffset() const
     {
-        return m_indices;
+		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;
+		}
     }
 
-    void GNMeshData::SetIndices(const U32Vector& indices)
+    template<AttributeType AttributeTypeT>
+    void GNMeshData::SetCount(AZ::u32 count)
     {
-        m_indices = indices;
+		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::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;
+		}
     }
 
-    const Vert3Vector& GNMeshData::GetPositions() const
+    template<AttributeType AttributeTypeT>
+    const auto& GNMeshData::GetDataBuffer() const
     {
-        return m_positions;
+		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;
+		}
     }
 
-    const Vert3Vector& GNMeshData::GetNormals() const
+    AZ::u32 GNMeshData::GetIndexCount() const
     {
-        return m_normals;
+        return m_indicesRange.count;
     }
 
-    const Vert4Vector& GNMeshData::GetTangents() const
+    AZ::u32 GNMeshData::GetIndexOffset() const
     {
-        return m_tangents;
+        return m_indicesRange.offset;
     }
 
-    const Vert3Vector& GNMeshData::GetBitangents() const
+    void GNMeshData::SetIndexOffset(AZ::u32 offset)
     {
-        return m_bitangents;
+        m_indicesRange.offset = offset;
     }
 
-    const Vert2Vector& GNMeshData::GetUVs() const
+    void GNMeshData::SetIndexCount(AZ::u32 count)
     {
-        return m_uvs;
+        m_indicesRange.count = count;
     }
 
-    const Vert4Vector& GNMeshData::GetColors() const
+    const U32Vector& GNMeshData::GetIndices() const
     {
-        return m_colors;
+        return m_indices;
+    }
+
+    void GNMeshData::SetIndices(const U32Vector& indices)
+    {
+        m_indices = indices;
     }
 
     const Mat4Vector& GNMeshData::GetInstances() const
@@ -246,21 +346,15 @@ namespace GeomNodes
         m_materialNames.clear();
     }
 
-    void GNMeshData::SetMaterial(const AZStd::string& materialName, const AZStd::string& materialPath)
+    void GNMeshData::SetMaterialIndex(AZ::u32 materialIndex)
     {
-        m_materialName = materialName;
-        m_materialPath = materialPath;
+        m_materialIndex = materialIndex;
     }
 
-    AZStd::string GNMeshData::GetMaterial() const
-    {
-        return m_materialName;
-    }
-
-    AZStd::string GNMeshData::GetMaterialPath() const
-    {
-        return m_materialPath;
-    }
+	AZ::u32 GNMeshData::GetMaterialIndex() const
+	{
+		return m_materialIndex;
+	}
 
     void GNMeshData::AddInstance(const AZ::Matrix4x4& mat4)
     {
@@ -298,9 +392,6 @@ namespace GeomNodes
 
     GNMeshData& GNMeshData::operator+=(const GNMeshData& rhs)
     {
-        m_materialName = rhs.m_materialName;
-        m_materialPath = rhs.m_materialPath;
-
         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);
@@ -321,6 +412,7 @@ namespace GeomNodes
             }
 
             m_indices.insert(m_indices.end(), rhsIndices.begin(), rhsIndices.end());
+            SetIndexCount(m_indices.size());
 
             Vert3Vector rhsPositions = rhs.m_positions;
             for (auto& position : rhsPositions)
@@ -330,6 +422,7 @@ namespace GeomNodes
             }
 
             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();
@@ -342,13 +435,35 @@ namespace GeomNodes
             }
 
             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

+ 39 - 13
Gems/O3DE/GeomNodes/Code/Source/Editor/Rendering/GNMeshData.h

@@ -1,5 +1,6 @@
 #pragma once
 
+#include <Editor/Common/GNConstants.h>
 #include <Editor/Math/MathHelper.h>
 #include <AzCore/Math/Aabb.h>
 #include <AZCore/std/string/string.h>
@@ -11,6 +12,12 @@ namespace GeomNodes
     class GNMeshData
     {
     public:
+        struct DataRange
+        {
+            AZ::u32 offset = 0; 
+            AZ::u32 count = 0;
+        };
+
         GNMeshData();
         explicit GNMeshData(
             const Vert3Vector& positions
@@ -28,21 +35,31 @@ namespace GeomNodes
 
         ~GNMeshData() = default;
 
-        const AZ::u32 VertexCount() const;
+        AZ::u32 GetIndexCount() const;
+        AZ::u32 GetIndexOffset() const;
+        void SetIndexOffset(AZ::u32 offset);
+        void SetIndexCount(AZ::u32 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 SetOffset(AZ::u32 offset);
+
         const U32Vector& GetIndices() const;
         void SetIndices(const U32Vector& indices);
-        const Vert3Vector& GetPositions() const;
-        const Vert3Vector& GetNormals() const;
-        const Vert4Vector& GetTangents() const;
-        const Vert3Vector& GetBitangents() const;
-        const Vert2Vector& GetUVs() const;
-        const Vert4Vector& GetColors() const;
+
+        template<AttributeType AttributeTypeT>
+        const auto& GetDataBuffer() const;
+        
         const Mat4Vector& GetInstances() const;
         const MaterialList& GetMaterials() const;
         void ClearMaterialList();
-        void SetMaterial(const AZStd::string& materialName, const AZStd::string& materialPath);
-        AZStd::string GetMaterial() const;
-        AZStd::string GetMaterialPath() const;
+        void SetMaterialIndex(AZ::u32 materialIndex);
+        AZ::u32 GetMaterialIndex() const;
         void AddInstance(const AZ::Matrix4x4& mat4);
 		
         U32Vector GetIndicesByMaterialIndex(int materialIndex);
@@ -54,6 +71,8 @@ namespace GeomNodes
 
         GNMeshData& operator+=(const GNMeshData& rhs);
 
+        void ClearBuffers();
+
     private:
         U32Vector m_indices;
         S32Vector m_loops;
@@ -66,11 +85,18 @@ namespace GeomNodes
         Vert2Vector m_uvs;
         Vert4Vector m_colors;
         
+        DataRange m_indicesRange;
+        DataRange m_positionsRange;
+        DataRange m_normalsRange;
+        DataRange m_tangentsRange;
+        DataRange m_bitangentsRange;
+        DataRange m_uvsRange;
+        DataRange m_colorsRange;
+
         MaterialList m_materialNames;
 
-        AZStd::string m_materialName;
-        AZStd::string m_materialPath;
-        
+        AZ::u32 m_materialIndex = 0;
+
         AZ::Aabb m_aabb = AZ::Aabb::CreateNull();
         AZ::s64 m_hash = 0;
 

+ 87 - 9
Gems/O3DE/GeomNodes/Code/Source/Editor/Rendering/GNModelData.cpp

@@ -20,8 +20,6 @@ namespace GeomNodes
 		m_meshes.clear();
 		m_assignedMeshmap.clear();
 
-		AZ_Assert(!m_materialPathFormat.empty(), "You need to call SetMaterialPathFormat first before calling ReadData!");
-
 		if (OpenSHM(mapId))
 		{
 			AZ::s32 meshCount = Read<AZ::s32>(mapId);
@@ -78,18 +76,17 @@ namespace GeomNodes
 			// but with a different indices based on the material indices.
 			for (auto& mesh : m_meshes)
 			{
-				int materialIdx = 0;
+				int materialMeshIndex = 0;
 				for (auto& materialName : mesh.GetMaterials())
 				{
-					auto indices = mesh.GetIndicesByMaterialIndex(materialIdx);
+					auto indices = mesh.GetIndicesByMaterialIndex(materialMeshIndex);
 					if (indices.size() > 0) {
 						auto& meshInstance = meshMap[materialName].emplace_back(mesh);
 						meshInstance.SetIndices(indices);
-						meshInstance.SetMaterial(materialName, AZStd::string::format(m_materialPathFormat.c_str(), materialName.c_str()));
 						meshInstance.CalculateTangents();
 						meshInstance.ClearMaterialList();
 					}
-					materialIdx++;
+					materialMeshIndex++;
 				}
 			}
 
@@ -120,6 +117,7 @@ namespace GeomNodes
 			m_meshes.clear();
 
 			// merge all meshes in each mesh group along with their instances
+			AZ::u32 materialIndex = 0;
 			for (auto& meshGroup : meshMap)
 			{
 				GNMeshData meshData;
@@ -128,13 +126,20 @@ namespace GeomNodes
 					meshData += mesh;
 				}
 
-				if (meshData.VertexCount() > 0)
+				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);
 				}
+
+				materialIndex++;
 			}
 
+			// Convert to only one buffers/arrays and keep track of the offsets and element counts
+			MergeMeshBuffers();
+
 			ClearSHM(mapId);
 		}
     }
@@ -162,9 +167,82 @@ namespace GeomNodes
 		m_assignedMeshmap.emplace(AZStd::make_pair(entityId, aznumeric_cast<AZ::u32>(m_assignedMeshmap.size())));
 	}
 
-	void GNModelData::SetMaterialPathFormat(const AZStd::string& materialPathFormat)
+	const U32Vector& GNModelData::GetIndices() const
+	{
+		return m_indices;
+	}
+
+	const Vert3Vector& GNModelData::GetPositions() const
 	{
-		m_materialPathFormat = materialPathFormat;
+		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>

+ 24 - 2
Gems/O3DE/GeomNodes/Code/Source/Editor/Rendering/GNModelData.h

@@ -23,8 +23,20 @@ namespace GeomNodes
         GNMeshData GetMeshData(AZ::u64 entityId);
         void AssignMeshData(AZ::u64 entityId);
 
-        void SetMaterialPathFormat(const AZStd::string& materialPathFormat);
+        // 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:
+        void MergeMeshBuffers();
+
         template<typename T>
         AZStd::vector<T> ReadArray(AZ::u64 mapId);
 
@@ -34,6 +46,16 @@ namespace GeomNodes
         MeshDataList m_meshes;
         AssignedMeshMap m_assignedMeshmap;
 
-        AZStd::string m_materialPathFormat;
+        // These are all data combined from the meshes in single arrays
+		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();
     };
 }

+ 63 - 132
Gems/O3DE/GeomNodes/Code/Source/Editor/Rendering/GNRenderMesh.cpp

@@ -1,10 +1,5 @@
 #include "GNRenderMesh.h"
 
-//#include <Rendering/Atom/WhiteBoxMeshAtomData.h>
-//#include <Rendering/WhiteBoxRenderData.h>
-//#include <Util/WhiteBoxMathUtil.h>
-//#include <Viewport/WhiteBoxViewportConstants.h>
-
 #include <Atom/RPI.Public/Model/Model.h>
 #include <Atom/RPI.Public/Scene.h>
 #include <Atom/RPI.Reflect/Asset/AssetUtils.h>
@@ -59,30 +54,18 @@ namespace GeomNodes
         return attributesAreValid;
     }
 
-    bool GNRenderMesh::CreateMeshBuffers(const GNMeshData& meshData)
+    bool GNRenderMesh::CreateMeshBuffers(const GNModelData& modelData)
     {
-        m_indexBuffer = AZStd::make_unique<IndexBuffer>(meshData.GetIndices());
-
-        CreateAttributeBuffer<AttributeType::Position>(meshData.GetPositions());
-        CreateAttributeBuffer<AttributeType::Normal>(meshData.GetNormals());
-        CreateAttributeBuffer<AttributeType::Tangent>(meshData.GetTangents());
-        CreateAttributeBuffer<AttributeType::Bitangent>(meshData.GetBitangents());
-        CreateAttributeBuffer<AttributeType::UV>(meshData.GetUVs());
-        CreateAttributeBuffer<AttributeType::Color>(meshData.GetColors());
+		m_indexBuffer = AZStd::make_unique<IndexBuffer>(modelData.GetIndices());
 
-        return AreAttributesValid();
-    }
+		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());
 
-    bool GNRenderMesh::UpdateMeshBuffers(const GNMeshData& meshData)
-    {
-        UpdateAttributeBuffer<AttributeType::Position>(meshData.GetPositions());
-        UpdateAttributeBuffer<AttributeType::Normal>(meshData.GetNormals());
-        UpdateAttributeBuffer<AttributeType::Tangent>(meshData.GetTangents());
-        UpdateAttributeBuffer<AttributeType::Bitangent>(meshData.GetBitangents());
-        UpdateAttributeBuffer<AttributeType::UV>(meshData.GetUVs());
-        UpdateAttributeBuffer<AttributeType::Color>(meshData.GetColors());
-
-        return AreAttributesValid();
+		return AreAttributesValid();
     }
 
     void GNRenderMesh::AddLodBuffers(AZ::RPI::ModelLodAssetCreator& modelLodCreator)
@@ -100,24 +83,25 @@ namespace GeomNodes
         }
     }
 
-    void GNRenderMesh::AddMeshBuffers(AZ::RPI::ModelLodAssetCreator& modelLodCreator)
+    void GNRenderMesh::AddMeshBuffers(AZ::RPI::ModelLodAssetCreator& modelLodCreator, const GNMeshData& meshData)
     {
-        modelLodCreator.SetMeshIndexBuffer(m_indexBuffer->GetBufferAssetView());
+		modelLodCreator.SetMeshIndexBuffer({ m_indexBuffer->GetBuffer(), 
+            AZ::RHI::BufferViewDescriptor::CreateTyped(meshData.GetIndexOffset(), meshData.GetIndexCount(), GetFormatForVertexStreamDataType<uint32_t>()) });
 
         for (auto& attribute : m_attributes)
         {
             AZStd::visit(
-                [&modelLodCreator](auto&& att)
+                [&modelLodCreator, meshData](auto&& att)
                 {
-                    att->AddMeshStreamBuffer(modelLodCreator);
+                    att->AddMeshStreamBuffer(modelLodCreator, meshData);
                 },
                 attribute);
         }
     }
 
-    bool GNRenderMesh::CreateLodAsset(const GNMeshData& meshData)
+    bool GNRenderMesh::CreateLodAsset(const GNModelData& modelData)
     {
-        if (!CreateMeshBuffers(meshData))
+        if (!CreateMeshBuffers(modelData))
         {
             return false;
         }
@@ -125,13 +109,18 @@ namespace GeomNodes
         AZ::RPI::ModelLodAssetCreator modelLodCreator;
         modelLodCreator.Begin(AZ::Data::AssetId(AZ::Uuid::CreateRandom()));
         AddLodBuffers(modelLodCreator);
-        modelLodCreator.BeginMesh();
-        modelLodCreator.SetMeshAabb(meshData.GetAabb());
 
-        modelLodCreator.SetMeshMaterialSlot(OneMaterialSlotId);
 
-        AddMeshBuffers(modelLodCreator);
-        modelLodCreator.EndMesh();
+        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))
         {
@@ -161,23 +150,30 @@ namespace GeomNodes
         modelCreator.SetName(ModelName);
         modelCreator.AddLodAsset(AZStd::move(m_lodAsset));
 
-        /*if (auto materialAsset = AZ::RPI::AssetUtils::LoadAssetByProductPath<AZ::RPI::MaterialAsset>(TexturedMaterialPath.data()))
+        m_materialMap.clear();
+        AZ::RPI::ModelMaterialSlot::StableId slotId = 0;
+        for (auto materialPath : m_materialList)
         {
-            auto materialOverrideInstance = AZ::RPI::Material::FindOrCreate(materialAsset);
-            auto& materialAssignment = m_materialMap[AZ::Render::DefaultMaterialAssignmentId];
-            materialAssignment.m_materialAsset = materialAsset;
-            materialAssignment.m_materialInstance = materialOverrideInstance;
-
-            AZ::RPI::ModelMaterialSlot materialSlot;
-            materialSlot.m_stableId = OneMaterialSlotId;
-            materialSlot.m_defaultMaterialAsset = materialAsset;
-            modelCreator.AddMaterialSlot(materialSlot);
+			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++;
         }
-        else
-        {
-            AZ_Error("CreateLodAsset", false, "Could not load material.");
-            return;
-        }*/
 
         modelCreator.End(m_modelAsset);
     }
@@ -201,14 +197,9 @@ namespace GeomNodes
         return true;
     }
 
-    bool GNRenderMesh::MeshRequiresFullRebuild([[maybe_unused]] const GNMeshData& meshData) const
-    {
-        return meshData.VertexCount() != m_vertexCount;
-    }
-
-    bool GNRenderMesh::CreateMesh(const GNMeshData& meshData)
+    bool GNRenderMesh::CreateMesh(const GNModelData& modelData)
     {
-        if (!CreateLodAsset(meshData))
+        if (!CreateLodAsset(modelData))
         {
             return false;
         }
@@ -220,9 +211,8 @@ namespace GeomNodes
             return false;
         }
 
-        SetMaterial(meshData.GetMaterialPath());
-        m_vertexCount = meshData.VertexCount();
-
+        SetMaterials();
+        
         return true;
     }
 
@@ -236,33 +226,22 @@ namespace GeomNodes
         return true; // meshData.VertexCount() != m_vertexCount;
     }
 
-    void GNRenderMesh::SetMaterial(const AZStd::string& materialAssetPath)
+    void GNRenderMesh::SetMaterials()
     {
-		AZ::RPI::AssetUtils::TraceLevel traceLevel = AZ::RPI::AssetUtils::TraceLevel::Assert;
-		//TODO: we need to wait for this
-        m_materialAsset = AZ::RPI::AssetUtils::GetAssetByProductPath<AZ::RPI::MaterialAsset>(materialAssetPath.c_str(), traceLevel);
-		m_materialAsset.QueueLoad();
+		if (m_meshFeatureProcessor)
+		{
+			m_meshFeatureProcessor->SetMaterialAssignmentMap(m_meshHandle, m_materialMap);
+		}
+    }
 
-        m_materialAsset = AZ::RPI::AssetUtils::LoadAssetByProductPath<AZ::RPI::MaterialAsset>(materialAssetPath.c_str(), traceLevel);
-        AZ::Data::AssetBus::MultiHandler::BusConnect(m_materialAsset.GetId());
+    void GNRenderMesh::SetMaterialList(const AZStd::vector<AZStd::string> materials)
+    {
+        m_materialList = materials;
     }
 
-    void GNRenderMesh::BuildMesh(const GNMeshData& meshData, const AZ::Transform& /*worldFromLocal*/)
+    void GNRenderMesh::BuildMesh(const GNModelData& modelData, const AZ::Transform& /*worldFromLocal*/)
     {
-        if (DoesMeshRequireFullRebuild(meshData))
-        {
-            if (!CreateMesh(meshData))
-            {
-                return;
-            }
-        }
-        else
-        {
-            if (!UpdateMeshBuffers(meshData))
-            {
-                return;
-            }
-        }
+        CreateMesh(modelData);
     }
 
     void GNRenderMesh::UpdateTransform(const AZ::Transform& worldFromLocal, const AZ::Vector3& /*scale*/)
@@ -271,38 +250,8 @@ namespace GeomNodes
         m_meshFeatureProcessor->SetTransform(m_meshHandle, worldFromLocal);
     }
 
-    //void GNRenderMesh::UpdateMaterial(const WhiteBoxMaterial& material)
-    //{
-    //    if (m_meshFeatureProcessor)
-    //    {
-    //        auto& materialAssignment = m_materialMap[AZ::Render::DefaultMaterialAssignmentId];
-    //        materialAssignment.m_propertyOverrides[AZ::Name("baseColor.color")] = AZ::Color(material.m_tint);
-    //        materialAssignment.m_propertyOverrides[AZ::Name("baseColor.useTexture")] = material.m_useTexture;
-    //        // if ApplyProperties fails, defer updating the material assignment map
-    //        // on the next tick, and try applying properties again
-    //        if (materialAssignment.ApplyProperties())
-    //        {
-    //            if (AZ::TickBus::Handler::BusIsConnected())
-    //            {
-    //                AZ::TickBus::Handler::BusDisconnect();
-    //            }
-    //            m_meshFeatureProcessor->SetMaterialAssignmentMap(m_meshHandle, m_materialMap);
-    //        }
-    //        else if (!AZ::TickBus::Handler::BusIsConnected())
-    //        {
-    //            AZ::TickBus::Handler::BusConnect();
-    //        }
-    //    }
-    //}
-
     void GNRenderMesh::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
     {
-        /*auto& materialAssignment = m_materialMap[AZ::Render::DefaultMaterialAssignmentId];
-        if (materialAssignment.ApplyProperties())
-        {
-            m_meshFeatureProcessor->SetMaterialAssignmentMap(m_meshHandle, m_materialMap);
-            AZ::TickBus::Handler::BusDisconnect();
-        }*/
     }
 
     AZ::Render::MaterialAssignmentId GNRenderMesh::FindMaterialAssignmentId(const AZ::Render::MaterialAssignmentLodIndex lod, const AZStd::string& label) const
@@ -354,22 +303,4 @@ namespace GeomNodes
     {
         return &m_meshHandle;
     }
-    
-    void GNRenderMesh::OnAssetReady(AZ::Data::Asset<AZ::Data::AssetData> asset)
-    {
-		if (m_materialAsset.GetId() == asset.GetId())
-		{
-			m_materialAsset = asset;
-			AZ::Data::AssetBus::MultiHandler::BusDisconnect(asset.GetId());
-
-			m_material = AZ::RPI::Material::FindOrCreate(m_materialAsset);
-			m_meshFeatureProcessor->SetMaterialAssignmentMap(m_meshHandle, m_material);
-		}
-    }
-    
-    void GNRenderMesh::OnAssetError(AZ::Data::Asset<AZ::Data::AssetData> asset)
-    {
-		AZ_Error("GNRenderMesh", false, "Failed to load material asset %s", asset.ToString<AZStd::string>().c_str());
-		AZ::Data::AssetBus::MultiHandler::BusDisconnect(asset.GetId());
-    }
 } // namespace WhiteBox

+ 11 - 24
Gems/O3DE/GeomNodes/Code/Source/Editor/Rendering/GNRenderMesh.h

@@ -3,8 +3,7 @@
 
 #include <Editor/Rendering/Atom/GNAttributeBuffer.h>
 #include <Editor/Rendering/Atom/GNBuffer.h>
-#include <Editor/Rendering/GNMeshData.h>
-//#include <Editor/Rendering/GNRenderMeshInterface.h>
+#include <Editor/Rendering/GNModelData.h>
 
 #include <Atom/Feature/Mesh/MeshFeatureProcessorInterface.h>
 #include <AtomLyIntegration/CommonFeatures/Mesh/MeshHandleStateBus.h>
@@ -21,13 +20,12 @@ namespace AZ::RPI
 
 namespace GeomNodes
 {
-    //! A concrete implementation of GNRenderMeshInterface to support Atom rendering for the GeomNodes gem.
+    //! 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
-        , public AZ::Data::AssetBus::MultiHandler
         , private AZ::TickBus::Handler
     {
     public:
@@ -37,7 +35,7 @@ namespace GeomNodes
         ~GNRenderMesh();
 
         // RenderMeshInterface ...
-        void BuildMesh(const GNMeshData& renderData, const AZ::Transform& worldFromLocal);
+        void BuildMesh(const GNModelData& renderData, const AZ::Transform& worldFromLocal);
         void UpdateTransform(const AZ::Transform& worldFromLocal, const AZ::Vector3& scale = AZ::Vector3::CreateOne());
         //void UpdateMaterial(const GNMaterial& material);
         bool IsVisible() const;
@@ -58,6 +56,7 @@ namespace GeomNodes
 
         AZ::Data::Instance<AZ::RPI::Model> GetModel() const;
 
+        void SetMaterialList(const AZStd::vector<AZStd::string> materials);
     private:
         //! Creates an attribute buffer in the slot dictated by AttributeTypeT.
         template<AttributeType AttributeTypeT, typename VertexStreamDataType>
@@ -79,33 +78,26 @@ namespace GeomNodes
         // MeshHandleStateRequestBus overrides ...
         const AZ::Render::MeshFeatureProcessorInterface::MeshHandle* GetMeshHandle() const override;
 
-		// AZ::Data::AssetBus::Handler overrides...
-		void OnAssetReady(AZ::Data::Asset<AZ::Data::AssetData> asset) override;
-		void OnAssetError(AZ::Data::Asset<AZ::Data::AssetData> asset) override;
-
-        bool CreateMeshBuffers(const GNMeshData& meshData);
-        bool UpdateMeshBuffers(const GNMeshData& meshData);
-        bool MeshRequiresFullRebuild(const GNMeshData& meshData) const;
-        bool CreateMesh(const GNMeshData& meshData);
-        bool CreateLodAsset(const GNMeshData& meshData);
+		bool CreateMeshBuffers(const GNModelData& modelData);
+        bool CreateMesh(const GNModelData& modelData);
+        bool CreateLodAsset(const GNModelData& modelData);
         void CreateModelAsset();
         bool CreateModel();
         void AddLodBuffers(AZ::RPI::ModelLodAssetCreator& modelLodCreator);
-        void AddMeshBuffers(AZ::RPI::ModelLodAssetCreator& modelLodCreator);
+        void AddMeshBuffers(AZ::RPI::ModelLodAssetCreator& modelLodCreator, const GNMeshData& meshData);
         bool AreAttributesValid() const;
         bool DoesMeshRequireFullRebuild(const GNMeshData& meshData) const;
 
-        void SetMaterial(const AZStd::string& materialAssetPath);
+        void SetMaterials();
 
         AZ::EntityId m_entityId;
         AZ::Data::Asset<AZ::RPI::ModelLodAsset> m_lodAsset;
         AZ::Data::Asset<AZ::RPI::ModelAsset> m_modelAsset;
         AZ::Data::Instance<AZ::RPI::Model> m_model;
-		AZ::Data::Asset<AZ::RPI::MaterialAsset> m_materialAsset;
-        AZ::Data::Instance<AZ::RPI::Material> m_material;
-        AZ::Render::MeshFeatureProcessorInterface* m_meshFeatureProcessor = nullptr;
+		AZ::Render::MeshFeatureProcessorInterface* m_meshFeatureProcessor = nullptr;
         AZ::Render::MeshFeatureProcessorInterface::MeshHandle m_meshHandle;
         AZ::Render::MaterialAssignmentMap m_materialMap;
+        AZStd::vector<AZStd::string> m_materialList;
         uint32_t m_vertexCount = 0;
         AZStd::unique_ptr<IndexBuffer> m_indexBuffer;
         AZStd::array<
@@ -120,11 +112,6 @@ namespace GeomNodes
             m_attributes;
         bool m_visible = true;
 
-        //! Default mesh material.
-        static constexpr AZStd::string_view TexturedMaterialPath = "materials/basic_grey.azmaterial";
-        static constexpr AZStd::string_view SolidMaterialPath = "materials/basic_grey.azmaterial";
-        static constexpr AZ::RPI::ModelMaterialSlot::StableId OneMaterialSlotId = 0;
-
         //! model name.
         static constexpr AZStd::string_view ModelName = "GeomNodesMesh";
 

+ 3 - 3
Gems/O3DE/GeomNodes/Code/geomnodes_editor_private_files.cmake

@@ -4,12 +4,11 @@ set(FILES
     Source/Editor/Math/MathHelper.cpp
     Source/Editor/Math/MathHelper.h
     Source/Editor/Common/GNEvents.h
+    Source/Editor/Common/GNConstants.h
     Source/Editor/Components/EditorGeomNodesSystemComponent.cpp
     Source/Editor/Components/EditorGeomNodesSystemComponent.h
     Source/Editor/Components/EditorGeomNodesComponent.cpp
     Source/Editor/Components/EditorGeomNodesComponent.h
-    Source/Editor/Components/EditorGeomNodesMeshComponent.cpp
-    Source/Editor/Components/EditorGeomNodesMeshComponent.h
     Source/Editor/Configuration/GNConfiguration.cpp
     Source/Editor/Configuration/GNConfiguration.h
     Source/Editor/Configuration/GNEditorSettingsRegistryManager.cpp
@@ -17,7 +16,6 @@ set(FILES
     Source/Editor/Configuration/GNSettingsRegistryManager.cpp
     Source/Editor/Configuration/GNSettingsRegistryManager.h
     Source/Editor/EBus/EditorGeomNodesComponentBus.h
-    Source/Editor/EBus/EditorGeomNodesMeshComponentBus.h
     Source/Editor/EBus/GeomNodesBus.h
     Source/Editor/EBus/ValidatorBus.h
     Source/Editor/EBus/IpcHandlerBus.h
@@ -45,6 +43,8 @@ set(FILES
     Source/Editor/UI/SettingsWidget.h
     Source/Editor/Rendering/Atom/GNAttributeBuffer.h
     Source/Editor/Rendering/Atom/GNBuffer.h
+    Source/Editor/Rendering/GNMeshController.cpp
+    Source/Editor/Rendering/GNMeshController.h
     Source/Editor/Rendering/GNMeshData.cpp
     Source/Editor/Rendering/GNMeshData.h
     Source/Editor/Rendering/GNModelData.cpp