3
0

MeshComponentController.cpp 46 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <Mesh/MeshComponentController.h>
  9. #include <AtomLyIntegration/CommonFeatures/Mesh/MeshComponentConstants.h>
  10. #include <AtomLyIntegration/CommonFeatures/Mesh/MeshHandleStateBus.h>
  11. #include <Atom/Feature/Mesh/MeshFeatureProcessor.h>
  12. #include <Atom/RHI/RHISystemInterface.h>
  13. #include <Atom/RHI/RHIUtils.h>
  14. #include <Atom/RPI.Public/Model/Model.h>
  15. #include <Atom/RPI.Public/Scene.h>
  16. #include <AzCore/Asset/AssetManager.h>
  17. #include <AzCore/Asset/AssetManagerBus.h>
  18. #include <AzCore/Asset/AssetSerializer.h>
  19. #include <AzCore/Name/NameDictionary.h>
  20. #include <AzCore/Serialization/SerializeContext.h>
  21. #include <AzFramework/Entity/EntityContextBus.h>
  22. #include <AzFramework/Entity/EntityContext.h>
  23. #include <AzFramework/Scene/Scene.h>
  24. #include <AzFramework/Scene/SceneSystemInterface.h>
  25. #include <AzCore/RTTI/BehaviorContext.h>
  26. namespace AZ
  27. {
  28. namespace Render
  29. {
  30. static AZ::Name s_CLOTH_DATA_Name = AZ::Name::FromStringLiteral("CLOTH_DATA", AZ::Interface<AZ::NameDictionary>::Get());
  31. namespace Internal
  32. {
  33. struct MeshComponentNotificationBusHandler final
  34. : public MeshComponentNotificationBus::Handler
  35. , public AZ::BehaviorEBusHandler
  36. {
  37. AZ_EBUS_BEHAVIOR_BINDER(
  38. MeshComponentNotificationBusHandler, "{8B8F4977-817F-4C7C-9141-0E5FF899E1BC}", AZ::SystemAllocator, OnModelReady);
  39. void OnModelReady(
  40. [[maybe_unused]] const Data::Asset<RPI::ModelAsset>& modelAsset,
  41. [[maybe_unused]] const Data::Instance<RPI::Model>& model) override
  42. {
  43. Call(FN_OnModelReady);
  44. }
  45. };
  46. } // namespace Internal
  47. namespace MeshComponentControllerVersionUtility
  48. {
  49. bool VersionConverter(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement)
  50. {
  51. if (classElement.GetVersion() < 2)
  52. {
  53. RPI::Cullable::LodOverride lodOverride = aznumeric_cast<RPI::Cullable::LodOverride>(classElement.FindElement(AZ_CRC_CE("LodOverride")));
  54. static constexpr uint8_t old_NoLodOverride = AZStd::numeric_limits <RPI::Cullable::LodOverride>::max();
  55. if (lodOverride == old_NoLodOverride)
  56. {
  57. classElement.AddElementWithData(context, "LodType", RPI::Cullable::LodType::SpecificLod);
  58. }
  59. }
  60. return true;
  61. }
  62. } // namespace MeshComponentControllerVersionUtility
  63. void MeshComponentConfig::Reflect(ReflectContext* context)
  64. {
  65. if (auto* serializeContext = azrtti_cast<SerializeContext*>(context))
  66. {
  67. serializeContext->Class<MeshComponentConfig>()
  68. ->Version(4, &MeshComponentControllerVersionUtility::VersionConverter)
  69. ->Field("ModelAsset", &MeshComponentConfig::m_modelAsset)
  70. ->Field("SortKey", &MeshComponentConfig::m_sortKey)
  71. ->Field("ExcludeFromReflectionCubeMaps", &MeshComponentConfig::m_excludeFromReflectionCubeMaps)
  72. ->Field("UseForwardPassIBLSpecular", &MeshComponentConfig::m_useForwardPassIblSpecular)
  73. ->Field("IsRayTracingEnabled", &MeshComponentConfig::m_isRayTracingEnabled)
  74. ->Field("IsAlwaysDynamic", &MeshComponentConfig::m_isAlwaysDynamic)
  75. ->Field("Visibility", &MeshComponentConfig::m_isVisible)
  76. ->Field("SupportRayIntersection", &MeshComponentConfig::m_enableRayIntersection)
  77. ->Field("LodType", &MeshComponentConfig::m_lodType)
  78. ->Field("LodOverride", &MeshComponentConfig::m_lodOverride)
  79. ->Field("MinimumScreenCoverage", &MeshComponentConfig::m_minimumScreenCoverage)
  80. ->Field("QualityDecayRate", &MeshComponentConfig::m_qualityDecayRate)
  81. ->Field("LightingChannelConfig", &MeshComponentConfig::m_lightingChannelConfig);
  82. }
  83. }
  84. bool MeshComponentConfig::IsAssetSet()
  85. {
  86. return m_modelAsset.GetId().IsValid();
  87. }
  88. bool MeshComponentConfig::LodTypeIsScreenCoverage()
  89. {
  90. return m_lodType == RPI::Cullable::LodType::ScreenCoverage;
  91. }
  92. bool MeshComponentConfig::LodTypeIsSpecificLOD()
  93. {
  94. return m_lodType == RPI::Cullable::LodType::SpecificLod;
  95. }
  96. bool MeshComponentConfig::ShowLodConfig()
  97. {
  98. return LodTypeIsScreenCoverage() || LodTypeIsSpecificLOD();
  99. }
  100. AZStd::vector<AZStd::pair<RPI::Cullable::LodOverride, AZStd::string>> MeshComponentConfig::GetLodOverrideValues()
  101. {
  102. AZStd::vector<AZStd::pair<RPI::Cullable::LodOverride, AZStd::string>> values;
  103. uint32_t lodCount = 0;
  104. if (IsAssetSet())
  105. {
  106. if (m_modelAsset.IsReady())
  107. {
  108. lodCount = static_cast<uint32_t>(m_modelAsset->GetLodCount());
  109. }
  110. else
  111. {
  112. // If the asset isn't loaded, it's still possible it exists in the instance database.
  113. Data::Instance<RPI::Model> model =
  114. Data::InstanceDatabase<RPI::Model>::Instance().Find(Data::InstanceId::CreateFromAsset(m_modelAsset));
  115. if (model)
  116. {
  117. lodCount = static_cast<uint32_t>(model->GetLodCount());
  118. }
  119. }
  120. }
  121. values.reserve(lodCount + 1);
  122. values.push_back({ aznumeric_cast<RPI::Cullable::LodOverride>(0), "Default LOD 0 (Highest Detail)" });
  123. for (uint32_t i = 1; i < lodCount; ++i)
  124. {
  125. AZStd::string enumDescription = AZStd::string::format("LOD %i", i);
  126. values.push_back({ aznumeric_cast<RPI::Cullable::LodOverride>(i), enumDescription.c_str() });
  127. }
  128. return values;
  129. }
  130. MeshComponentController::~MeshComponentController()
  131. {
  132. // Release memory, disconnect from buses in the right order and broadcast events so that other components are aware.
  133. Deactivate();
  134. }
  135. void MeshComponentController::Reflect(ReflectContext* context)
  136. {
  137. MeshComponentConfig::Reflect(context);
  138. if (auto* serializeContext = azrtti_cast<SerializeContext*>(context))
  139. {
  140. serializeContext->Class<MeshComponentController>()
  141. ->Version(1)
  142. ->Field("Configuration", &MeshComponentController::m_configuration);
  143. }
  144. if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
  145. {
  146. behaviorContext->ConstantProperty("DefaultLodOverride", BehaviorConstant(0))
  147. ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
  148. ->Attribute(AZ::Script::Attributes::Category, "render")
  149. ->Attribute(AZ::Script::Attributes::Module, "render");
  150. behaviorContext->ConstantProperty("DefaultLodType", BehaviorConstant(RPI::Cullable::LodType::Default))
  151. ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
  152. ->Attribute(AZ::Script::Attributes::Category, "render")
  153. ->Attribute(AZ::Script::Attributes::Module, "render");
  154. behaviorContext->EBus<MeshComponentRequestBus>("RenderMeshComponentRequestBus")
  155. ->Event("GetModelAssetId", &MeshComponentRequestBus::Events::GetModelAssetId)
  156. ->Event("SetModelAssetId", &MeshComponentRequestBus::Events::SetModelAssetId)
  157. ->Event("GetModelAssetPath", &MeshComponentRequestBus::Events::GetModelAssetPath)
  158. ->Event("SetModelAssetPath", &MeshComponentRequestBus::Events::SetModelAssetPath)
  159. ->Event("SetSortKey", &MeshComponentRequestBus::Events::SetSortKey)
  160. ->Event("GetSortKey", &MeshComponentRequestBus::Events::GetSortKey)
  161. ->Event("SetIsAlwaysDynamic", &MeshComponentRequestBus::Events::SetIsAlwaysDynamic)
  162. ->Event("GetIsAlwaysDynamic", &MeshComponentRequestBus::Events::GetIsAlwaysDynamic)
  163. ->Event("SetLodType", &MeshComponentRequestBus::Events::SetLodType)
  164. ->Event("GetLodType", &MeshComponentRequestBus::Events::GetLodType)
  165. ->Event("SetLodOverride", &MeshComponentRequestBus::Events::SetLodOverride)
  166. ->Event("GetLodOverride", &MeshComponentRequestBus::Events::GetLodOverride)
  167. ->Event("SetMinimumScreenCoverage", &MeshComponentRequestBus::Events::SetMinimumScreenCoverage)
  168. ->Event("GetMinimumScreenCoverage", &MeshComponentRequestBus::Events::GetMinimumScreenCoverage)
  169. ->Event("SetQualityDecayRate", &MeshComponentRequestBus::Events::SetQualityDecayRate)
  170. ->Event("GetQualityDecayRate", &MeshComponentRequestBus::Events::GetQualityDecayRate)
  171. ->Event("SetRayTracingEnabled", &MeshComponentRequestBus::Events::SetRayTracingEnabled)
  172. ->Event("GetExcludeFromReflectionCubeMaps", &MeshComponentRequestBus::Events::GetExcludeFromReflectionCubeMaps)
  173. ->Event("SetExcludeFromReflectionCubeMaps", &MeshComponentRequestBus::Events::SetExcludeFromReflectionCubeMaps)
  174. ->Event("GetRayTracingEnabled", &MeshComponentRequestBus::Events::GetRayTracingEnabled)
  175. ->Event("SetVisibility", &MeshComponentRequestBus::Events::SetVisibility)
  176. ->Event("GetVisibility", &MeshComponentRequestBus::Events::GetVisibility)
  177. ->VirtualProperty("ModelAssetId", "GetModelAssetId", "SetModelAssetId")
  178. ->VirtualProperty("ModelAssetPath", "GetModelAssetPath", "SetModelAssetPath")
  179. ->VirtualProperty("SortKey", "GetSortKey", "SetSortKey")
  180. ->VirtualProperty("IsAlwaysDynamic", "GetIsAlwaysDynamic", "SetIsAlwaysDynamic")
  181. ->VirtualProperty("LodType", "GetLodType", "SetLodType")
  182. ->VirtualProperty("LodOverride", "GetLodOverride", "SetLodOverride")
  183. ->VirtualProperty("MinimumScreenCoverage", "GetMinimumScreenCoverage", "SetMinimumScreenCoverage")
  184. ->VirtualProperty("QualityDecayRate", "GetQualityDecayRate", "SetQualityDecayRate")
  185. ->VirtualProperty("RayTracingEnabled", "GetRayTracingEnabled", "SetRayTracingEnabled")
  186. ->VirtualProperty("ExcludeFromReflectionCubeMaps", "GetExcludeFromReflectionCubeMaps", "SetExcludeFromReflectionCubeMaps")
  187. ->VirtualProperty("Visibility", "GetVisibility", "SetVisibility")
  188. ;
  189. behaviorContext->EBus<MeshComponentNotificationBus>("MeshComponentNotificationBus")
  190. ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
  191. ->Attribute(AZ::Script::Attributes::Category, "render")
  192. ->Attribute(AZ::Script::Attributes::Module, "render")
  193. ->Handler<Internal::MeshComponentNotificationBusHandler>();
  194. }
  195. }
  196. void MeshComponentController::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent)
  197. {
  198. dependent.push_back(AZ_CRC_CE("TransformService"));
  199. dependent.push_back(AZ_CRC_CE("NonUniformScaleService"));
  200. }
  201. void MeshComponentController::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
  202. {
  203. provided.push_back(AZ_CRC_CE("MaterialConsumerService"));
  204. provided.push_back(AZ_CRC_CE("MeshService"));
  205. }
  206. void MeshComponentController::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
  207. {
  208. incompatible.push_back(AZ_CRC_CE("MaterialConsumerService"));
  209. incompatible.push_back(AZ_CRC_CE("MeshService"));
  210. }
  211. MeshComponentController::MeshComponentController(const MeshComponentConfig& config)
  212. : m_configuration(config)
  213. {
  214. }
  215. static AzFramework::EntityContextId FindOwningContextId(const AZ::EntityId entityId)
  216. {
  217. AzFramework::EntityContextId contextId = AzFramework::EntityContextId::CreateNull();
  218. AzFramework::EntityIdContextQueryBus::EventResult(
  219. contextId, entityId, &AzFramework::EntityIdContextQueries::GetOwningContextId);
  220. return contextId;
  221. }
  222. void MeshComponentController::Activate(const AZ::EntityComponentIdPair& entityComponentIdPair)
  223. {
  224. const AZ::EntityId entityId = entityComponentIdPair.GetEntityId();
  225. m_entityComponentIdPair = entityComponentIdPair;
  226. m_transformInterface = TransformBus::FindFirstHandler(entityId);
  227. AZ_Warning(
  228. "MeshComponentController", m_transformInterface,
  229. "Unable to attach to a TransformBus handler. This mesh will always be rendered at the origin.");
  230. m_meshFeatureProcessor = RPI::Scene::GetFeatureProcessorForEntity<MeshFeatureProcessorInterface>(entityId);
  231. AZ_Error("MeshComponentController", m_meshFeatureProcessor, "Unable to find a MeshFeatureProcessorInterface on the entityId.");
  232. m_cachedNonUniformScale = AZ::Vector3::CreateOne();
  233. AZ::NonUniformScaleRequestBus::EventResult(m_cachedNonUniformScale, entityId, &AZ::NonUniformScaleRequests::GetScale);
  234. AZ::NonUniformScaleRequestBus::Event(
  235. entityId, &AZ::NonUniformScaleRequests::RegisterScaleChangedEvent, m_nonUniformScaleChangedHandler);
  236. const auto entityContextId = FindOwningContextId(entityId);
  237. MeshComponentRequestBus::Handler::BusConnect(entityId);
  238. MeshHandleStateRequestBus::Handler::BusConnect(entityId);
  239. AtomImGuiTools::AtomImGuiMeshCallbackBus::Handler::BusConnect(entityId);
  240. TransformNotificationBus::Handler::BusConnect(entityId);
  241. MaterialConsumerRequestBus::Handler::BusConnect(entityId);
  242. MaterialComponentNotificationBus::Handler::BusConnect(entityId);
  243. AzFramework::BoundsRequestBus::Handler::BusConnect(entityId);
  244. AzFramework::VisibleGeometryRequestBus::Handler::BusConnect(entityId);
  245. AzFramework::RenderGeometry::IntersectionRequestBus::Handler::BusConnect({ entityId, entityContextId });
  246. AzFramework::RenderGeometry::IntersectionNotificationBus::Bind(m_intersectionNotificationBus, entityContextId);
  247. LightingChannelMaskChanged();
  248. // Buses must be connected before RegisterModel in case requests are made as a result of HandleModelChange
  249. RegisterModel();
  250. }
  251. void MeshComponentController::Deactivate()
  252. {
  253. // Buses must be disconnected after unregistering the model, otherwise they can't deliver the events during the process.
  254. UnregisterModel();
  255. AzFramework::RenderGeometry::IntersectionRequestBus::Handler::BusDisconnect();
  256. AzFramework::VisibleGeometryRequestBus::Handler::BusDisconnect();
  257. AzFramework::BoundsRequestBus::Handler::BusDisconnect();
  258. MaterialComponentNotificationBus::Handler::BusDisconnect();
  259. MaterialConsumerRequestBus::Handler::BusDisconnect();
  260. TransformNotificationBus::Handler::BusDisconnect();
  261. MeshComponentRequestBus::Handler::BusDisconnect();
  262. MeshHandleStateRequestBus::Handler::BusDisconnect();
  263. AtomImGuiTools::AtomImGuiMeshCallbackBus::Handler::BusDisconnect();
  264. m_nonUniformScaleChangedHandler.Disconnect();
  265. m_meshFeatureProcessor = nullptr;
  266. m_transformInterface = nullptr;
  267. m_entityComponentIdPair = AZ::EntityComponentIdPair(AZ::EntityId(), AZ::InvalidComponentId);
  268. m_configuration.m_modelAsset.Release();
  269. }
  270. void MeshComponentController::SetConfiguration(const MeshComponentConfig& config)
  271. {
  272. m_configuration = config;
  273. }
  274. const MeshComponentConfig& MeshComponentController::GetConfiguration() const
  275. {
  276. return m_configuration;
  277. }
  278. void MeshComponentController::OnTransformChanged(const AZ::Transform& /*local*/, const AZ::Transform& world)
  279. {
  280. if (m_meshFeatureProcessor)
  281. {
  282. m_meshFeatureProcessor->SetTransform(m_meshHandle, world, m_cachedNonUniformScale);
  283. }
  284. // ensure the render geometry is kept in sync with any changes to the entity the mesh is on
  285. AzFramework::RenderGeometry::IntersectionNotificationBus::Event(
  286. m_intersectionNotificationBus, &AzFramework::RenderGeometry::IntersectionNotificationBus::Events::OnGeometryChanged,
  287. m_entityComponentIdPair.GetEntityId());
  288. }
  289. void MeshComponentController::HandleNonUniformScaleChange(const AZ::Vector3& nonUniformScale)
  290. {
  291. m_cachedNonUniformScale = nonUniformScale;
  292. if (m_meshFeatureProcessor)
  293. {
  294. m_meshFeatureProcessor->SetTransform(m_meshHandle, m_transformInterface->GetWorldTM(), m_cachedNonUniformScale);
  295. }
  296. }
  297. void MeshComponentController::LightingChannelMaskChanged()
  298. {
  299. if (m_meshFeatureProcessor)
  300. {
  301. m_meshFeatureProcessor->SetLightingChannelMask(m_meshHandle, m_configuration.m_lightingChannelConfig.GetLightingChannelMask());
  302. }
  303. }
  304. MaterialAssignmentLabelMap MeshComponentController::GetMaterialLabels() const
  305. {
  306. return GetMaterialSlotLabelsFromModelAsset(GetModelAsset());
  307. }
  308. MaterialAssignmentId MeshComponentController::FindMaterialAssignmentId(
  309. const MaterialAssignmentLodIndex lod, const AZStd::string& label) const
  310. {
  311. return GetMaterialSlotIdFromModelAsset(GetModelAsset(), lod, label);
  312. }
  313. MaterialAssignmentMap MeshComponentController::GetDefaultMaterialMap() const
  314. {
  315. return GetDefaultMaterialMapFromModelAsset(GetModelAsset());
  316. }
  317. AZStd::unordered_set<AZ::Name> MeshComponentController::GetModelUvNames() const
  318. {
  319. const Data::Instance<RPI::Model> model = GetModel();
  320. return model ? model->GetUvNames() : AZStd::unordered_set<AZ::Name>();
  321. }
  322. void MeshComponentController::OnMaterialsUpdated(const MaterialAssignmentMap& materials)
  323. {
  324. if (m_meshFeatureProcessor)
  325. {
  326. m_meshFeatureProcessor->SetCustomMaterials(m_meshHandle, ConvertToCustomMaterialMap(materials));
  327. }
  328. }
  329. void MeshComponentController::OnMaterialPropertiesUpdated([[maybe_unused]] const MaterialAssignmentMap& materials)
  330. {
  331. if (m_meshFeatureProcessor)
  332. {
  333. m_meshFeatureProcessor->SetRayTracingDirty(m_meshHandle);
  334. }
  335. }
  336. bool MeshComponentController::RequiresCloning(const Data::Asset<RPI::ModelAsset>& modelAsset)
  337. {
  338. // Is the model asset containing a cloth buffer? If yes, we need to clone the model asset for instancing.
  339. const AZStd::span<const AZ::Data::Asset<AZ::RPI::ModelLodAsset>> lodAssets = modelAsset->GetLodAssets();
  340. for (const AZ::Data::Asset<AZ::RPI::ModelLodAsset>& lodAsset : lodAssets)
  341. {
  342. const AZStd::span<const AZ::RPI::ModelLodAsset::Mesh> meshes = lodAsset->GetMeshes();
  343. for (const AZ::RPI::ModelLodAsset::Mesh& mesh : meshes)
  344. {
  345. if (mesh.GetSemanticBufferAssetView(s_CLOTH_DATA_Name) != nullptr)
  346. {
  347. return true;
  348. }
  349. }
  350. }
  351. return false;
  352. }
  353. void MeshComponentController::HandleModelChange(const AZ::Data::Instance<AZ::RPI::Model>& model)
  354. {
  355. Data::Asset<RPI::ModelAsset> modelAsset = m_meshFeatureProcessor->GetModelAsset(m_meshHandle);
  356. if (model && modelAsset.IsReady())
  357. {
  358. const AZ::EntityId entityId = m_entityComponentIdPair.GetEntityId();
  359. m_configuration.m_modelAsset = modelAsset;
  360. MeshComponentNotificationBus::Event(entityId, &MeshComponentNotificationBus::Events::OnModelReady, m_configuration.m_modelAsset, model);
  361. MaterialConsumerNotificationBus::Event(entityId, &MaterialConsumerNotificationBus::Events::OnMaterialAssignmentSlotsChanged);
  362. AZ::Interface<AzFramework::IEntityBoundsUnion>::Get()->RefreshEntityLocalBoundsUnion(entityId);
  363. AzFramework::RenderGeometry::IntersectionNotificationBus::Event(
  364. m_intersectionNotificationBus, &AzFramework::RenderGeometry::IntersectionNotificationBus::Events::OnGeometryChanged,
  365. m_entityComponentIdPair.GetEntityId());
  366. MeshHandleStateNotificationBus::Event(entityId, &MeshHandleStateNotificationBus::Events::OnMeshHandleSet, &m_meshHandle);
  367. }
  368. }
  369. void MeshComponentController::HandleObjectSrgCreate(const Data::Instance<RPI::ShaderResourceGroup>& objectSrg)
  370. {
  371. MeshComponentNotificationBus::Event(m_entityComponentIdPair.GetEntityId(), &MeshComponentNotificationBus::Events::OnObjectSrgCreated, objectSrg);
  372. }
  373. void MeshComponentController::RegisterModel()
  374. {
  375. if (m_meshFeatureProcessor && m_configuration.m_modelAsset.GetId().IsValid())
  376. {
  377. const AZ::EntityId entityId = m_entityComponentIdPair.GetEntityId();
  378. MaterialAssignmentMap materials;
  379. MaterialComponentRequestBus::EventResult(materials, entityId, &MaterialComponentRequests::GetMaterialMap);
  380. m_meshFeatureProcessor->ReleaseMesh(m_meshHandle);
  381. MeshHandleDescriptor meshDescriptor;
  382. meshDescriptor.m_modelAsset = m_configuration.m_modelAsset;
  383. meshDescriptor.m_customMaterials = ConvertToCustomMaterialMap(materials);
  384. meshDescriptor.m_useForwardPassIblSpecular = m_configuration.m_useForwardPassIblSpecular;
  385. meshDescriptor.m_requiresCloneCallback = RequiresCloning;
  386. meshDescriptor.m_isRayTracingEnabled = m_configuration.m_isRayTracingEnabled;
  387. meshDescriptor.m_excludeFromReflectionCubeMaps = m_configuration.m_excludeFromReflectionCubeMaps;
  388. meshDescriptor.m_isAlwaysDynamic = m_configuration.m_isAlwaysDynamic;
  389. meshDescriptor.m_supportRayIntersection = m_configuration.m_enableRayIntersection || m_configuration.m_editorRayIntersection;
  390. meshDescriptor.m_modelChangedEventHandler = m_modelChangedEventHandler;
  391. meshDescriptor.m_objectSrgCreatedHandler = m_objectSrgCreatedHandler;
  392. m_meshHandle = m_meshFeatureProcessor->AcquireMesh(meshDescriptor);
  393. const AZ::Transform& transform =
  394. m_transformInterface ? m_transformInterface->GetWorldTM() : AZ::Transform::CreateIdentity();
  395. m_meshFeatureProcessor->SetVisible(m_meshHandle, m_configuration.m_isVisible);
  396. m_meshFeatureProcessor->SetTransform(m_meshHandle, transform, m_cachedNonUniformScale);
  397. m_meshFeatureProcessor->SetSortKey(m_meshHandle, m_configuration.m_sortKey);
  398. m_meshFeatureProcessor->SetLightingChannelMask(m_meshHandle, m_configuration.m_lightingChannelConfig.GetLightingChannelMask());
  399. m_meshFeatureProcessor->SetMeshLodConfiguration(m_meshHandle, GetMeshLodConfiguration());
  400. m_meshFeatureProcessor->SetRayTracingEnabled(m_meshHandle, meshDescriptor.m_isRayTracingEnabled);
  401. }
  402. else
  403. {
  404. // If there is no model asset to be loaded then we need to invalidate the material slot configuration
  405. MaterialConsumerNotificationBus::Event(
  406. m_entityComponentIdPair.GetEntityId(), &MaterialConsumerNotificationBus::Events::OnMaterialAssignmentSlotsChanged);
  407. }
  408. }
  409. void MeshComponentController::UnregisterModel()
  410. {
  411. if (m_meshFeatureProcessor && m_meshHandle.IsValid())
  412. {
  413. MeshComponentNotificationBus::Event(
  414. m_entityComponentIdPair.GetEntityId(), &MeshComponentNotificationBus::Events::OnModelPreDestroy);
  415. m_meshFeatureProcessor->ReleaseMesh(m_meshHandle);
  416. MeshHandleStateNotificationBus::Event(
  417. m_entityComponentIdPair.GetEntityId(), &MeshHandleStateNotificationBus::Events::OnMeshHandleSet, &m_meshHandle);
  418. // Model has been released which invalidates the material slot configuration
  419. MaterialConsumerNotificationBus::Event(
  420. m_entityComponentIdPair.GetEntityId(), &MaterialConsumerNotificationBus::Events::OnMaterialAssignmentSlotsChanged);
  421. }
  422. }
  423. void MeshComponentController::RefreshModelRegistration()
  424. {
  425. // [GFX TODO][ATOM-13364] Without the Suspend / Resume calls below, a model refresh will trigger an asset unload and reload
  426. // that breaks Material Thumbnail Previews in the Editor. The asset unload/reload itself is undesirable, but the flow should
  427. // get investigated further to determine what state management and notifications need to be modified, since the previews ought
  428. // to still work even if a full asset reload were to occur here.
  429. // The unregister / register combination can cause the asset reference to get released, which could trigger a full reload
  430. // of the asset. Tell the Asset Manager not to release any asset references until after the registration is complete.
  431. // This will ensure that if we're reusing the same model, it remains loaded.
  432. Data::AssetManager::Instance().SuspendAssetRelease();
  433. UnregisterModel();
  434. RegisterModel();
  435. Data::AssetManager::Instance().ResumeAssetRelease();
  436. }
  437. void MeshComponentController::SetModelAssetId(Data::AssetId modelAssetId)
  438. {
  439. SetModelAsset(Data::Asset<RPI::ModelAsset>(modelAssetId, azrtti_typeid<RPI::ModelAsset>()));
  440. }
  441. void MeshComponentController::SetModelAsset(Data::Asset<RPI::ModelAsset> modelAsset)
  442. {
  443. if (m_configuration.m_modelAsset != modelAsset)
  444. {
  445. m_configuration.m_modelAsset = modelAsset;
  446. m_configuration.m_modelAsset.SetAutoLoadBehavior(Data::AssetLoadBehavior::PreLoad);
  447. RefreshModelRegistration();
  448. }
  449. }
  450. Data::Asset<const RPI::ModelAsset> MeshComponentController::GetModelAsset() const
  451. {
  452. return m_configuration.m_modelAsset;
  453. }
  454. Data::AssetId MeshComponentController::GetModelAssetId() const
  455. {
  456. return m_configuration.m_modelAsset.GetId();
  457. }
  458. void MeshComponentController::SetModelAssetPath(const AZStd::string& modelAssetPath)
  459. {
  460. AZ::Data::AssetId assetId;
  461. AZ::Data::AssetCatalogRequestBus::BroadcastResult(assetId, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetIdByPath, modelAssetPath.c_str(), AZ::RPI::ModelAsset::RTTI_Type(), false);
  462. SetModelAssetId(assetId);
  463. }
  464. AZStd::string MeshComponentController::GetModelAssetPath() const
  465. {
  466. AZStd::string assetPathString;
  467. AZ::Data::AssetCatalogRequestBus::BroadcastResult(assetPathString, &AZ::Data::AssetCatalogRequests::GetAssetPathById, m_configuration.m_modelAsset.GetId());
  468. return assetPathString;
  469. }
  470. Data::Instance<RPI::Model> MeshComponentController::GetModel() const
  471. {
  472. return m_meshFeatureProcessor ? m_meshFeatureProcessor->GetModel(m_meshHandle) : Data::Instance<RPI::Model>();
  473. }
  474. const RPI::MeshDrawPacketLods* MeshComponentController::GetDrawPackets() const
  475. {
  476. return m_meshFeatureProcessor ? &m_meshFeatureProcessor->GetDrawPackets(m_meshHandle) : nullptr;
  477. }
  478. void MeshComponentController::SetSortKey(RHI::DrawItemSortKey sortKey)
  479. {
  480. m_configuration.m_sortKey = sortKey; // Save for serialization
  481. m_meshFeatureProcessor->SetSortKey(m_meshHandle, sortKey);
  482. }
  483. RHI::DrawItemSortKey MeshComponentController::GetSortKey() const
  484. {
  485. return m_meshFeatureProcessor->GetSortKey(m_meshHandle);
  486. }
  487. void MeshComponentController::SetIsAlwaysDynamic(bool isAlwaysDynamic)
  488. {
  489. m_configuration.m_isAlwaysDynamic = isAlwaysDynamic; // Save for serialization
  490. m_meshFeatureProcessor->SetIsAlwaysDynamic(m_meshHandle, isAlwaysDynamic);
  491. }
  492. bool MeshComponentController::GetIsAlwaysDynamic() const
  493. {
  494. return m_meshFeatureProcessor->GetIsAlwaysDynamic(m_meshHandle);
  495. }
  496. RPI::Cullable::LodConfiguration MeshComponentController::GetMeshLodConfiguration() const
  497. {
  498. return {
  499. m_configuration.m_lodType,
  500. m_configuration.m_lodOverride,
  501. m_configuration.m_minimumScreenCoverage,
  502. m_configuration.m_qualityDecayRate
  503. };
  504. }
  505. // -----------------------
  506. void MeshComponentController::SetLodType(RPI::Cullable::LodType lodType)
  507. {
  508. RPI::Cullable::LodConfiguration lodConfig = GetMeshLodConfiguration();
  509. lodConfig.m_lodType = lodType;
  510. m_meshFeatureProcessor->SetMeshLodConfiguration(m_meshHandle, lodConfig);
  511. }
  512. RPI::Cullable::LodType MeshComponentController::GetLodType() const
  513. {
  514. RPI::Cullable::LodConfiguration lodConfig = m_meshFeatureProcessor->GetMeshLodConfiguration(m_meshHandle);
  515. return lodConfig.m_lodType;
  516. }
  517. void MeshComponentController::SetLodOverride(RPI::Cullable::LodOverride lodOverride)
  518. {
  519. RPI::Cullable::LodConfiguration lodConfig = GetMeshLodConfiguration();
  520. lodConfig.m_lodOverride = lodOverride;
  521. m_meshFeatureProcessor->SetMeshLodConfiguration(m_meshHandle, lodConfig);
  522. }
  523. RPI::Cullable::LodOverride MeshComponentController::GetLodOverride() const
  524. {
  525. RPI::Cullable::LodConfiguration lodConfig = m_meshFeatureProcessor->GetMeshLodConfiguration(m_meshHandle);
  526. return lodConfig.m_lodOverride;
  527. }
  528. void MeshComponentController::SetMinimumScreenCoverage(float minimumScreenCoverage)
  529. {
  530. RPI::Cullable::LodConfiguration lodConfig = GetMeshLodConfiguration();
  531. lodConfig.m_minimumScreenCoverage = minimumScreenCoverage;
  532. m_meshFeatureProcessor->SetMeshLodConfiguration(m_meshHandle, lodConfig);
  533. }
  534. float MeshComponentController::GetMinimumScreenCoverage() const
  535. {
  536. RPI::Cullable::LodConfiguration lodConfig = m_meshFeatureProcessor->GetMeshLodConfiguration(m_meshHandle);
  537. return lodConfig.m_minimumScreenCoverage;
  538. }
  539. void MeshComponentController::SetQualityDecayRate(float qualityDecayRate)
  540. {
  541. RPI::Cullable::LodConfiguration lodConfig = GetMeshLodConfiguration();
  542. lodConfig.m_qualityDecayRate = qualityDecayRate;
  543. m_meshFeatureProcessor->SetMeshLodConfiguration(m_meshHandle, lodConfig);
  544. }
  545. float MeshComponentController::GetQualityDecayRate() const
  546. {
  547. RPI::Cullable::LodConfiguration lodConfig = m_meshFeatureProcessor->GetMeshLodConfiguration(m_meshHandle);
  548. return lodConfig.m_qualityDecayRate;
  549. }
  550. void MeshComponentController::SetVisibility(bool visible)
  551. {
  552. if (m_meshFeatureProcessor)
  553. {
  554. m_meshFeatureProcessor->SetVisible(m_meshHandle, visible);
  555. }
  556. }
  557. bool MeshComponentController::GetVisibility() const
  558. {
  559. if (m_meshFeatureProcessor)
  560. {
  561. return m_meshFeatureProcessor->GetVisible(m_meshHandle);
  562. }
  563. return false;
  564. }
  565. void MeshComponentController::SetRayTracingEnabled(bool enabled)
  566. {
  567. if (m_meshHandle.IsValid() && m_meshFeatureProcessor)
  568. {
  569. m_meshFeatureProcessor->SetRayTracingEnabled(m_meshHandle, enabled);
  570. m_configuration.m_isRayTracingEnabled = enabled;
  571. }
  572. }
  573. bool MeshComponentController::GetRayTracingEnabled() const
  574. {
  575. if (m_meshHandle.IsValid() && m_meshFeatureProcessor)
  576. {
  577. return m_meshFeatureProcessor->GetRayTracingEnabled(m_meshHandle);
  578. }
  579. return false;
  580. }
  581. void MeshComponentController::SetExcludeFromReflectionCubeMaps(bool excludeFromReflectionCubeMaps)
  582. {
  583. m_configuration.m_excludeFromReflectionCubeMaps = excludeFromReflectionCubeMaps;
  584. if (m_meshFeatureProcessor)
  585. {
  586. m_meshFeatureProcessor->SetExcludeFromReflectionCubeMaps(m_meshHandle, excludeFromReflectionCubeMaps);
  587. }
  588. }
  589. bool MeshComponentController::GetExcludeFromReflectionCubeMaps() const
  590. {
  591. if (m_meshHandle.IsValid() && m_meshFeatureProcessor)
  592. {
  593. return m_meshFeatureProcessor->GetExcludeFromReflectionCubeMaps(m_meshHandle);
  594. }
  595. return false;
  596. }
  597. Aabb MeshComponentController::GetWorldBounds() const
  598. {
  599. if (const AZ::Aabb localBounds = GetLocalBounds(); localBounds.IsValid())
  600. {
  601. return localBounds.GetTransformedAabb(m_transformInterface->GetWorldTM());
  602. }
  603. return AZ::Aabb::CreateNull();
  604. }
  605. Aabb MeshComponentController::GetLocalBounds() const
  606. {
  607. if (m_meshHandle.IsValid() && m_meshFeatureProcessor)
  608. {
  609. if (Aabb aabb = m_meshFeatureProcessor->GetLocalAabb(m_meshHandle); aabb.IsValid())
  610. {
  611. aabb.MultiplyByScale(m_cachedNonUniformScale);
  612. return aabb;
  613. }
  614. }
  615. return Aabb::CreateNull();
  616. }
  617. void MeshComponentController::GetVisibleGeometry(
  618. const AZ::Aabb& bounds, AzFramework::VisibleGeometryContainer& geometryContainer) const
  619. {
  620. // Only include data for this entity if it is within bounds. This could possibly be done per sub mesh.
  621. if (bounds.IsValid())
  622. {
  623. const AZ::Aabb worldBounds = GetWorldBounds();
  624. if (worldBounds.IsValid() && !worldBounds.Overlaps(bounds))
  625. {
  626. return;
  627. }
  628. }
  629. // The draw list tag is needed to search material shaders and determine if they are transparent.
  630. AZ::RHI::DrawListTag transparentDrawListTag =
  631. AZ::RHI::RHISystemInterface::Get()->GetDrawListTagRegistry()->AcquireTag(AZ::Name("transparent"));
  632. // Retrieve the map of material overrides from the material component. If any mesh has a material override, that must be checked
  633. // for transparency instead of the material included with the model asset.
  634. MaterialAssignmentMap materials;
  635. MaterialComponentRequestBus::EventResult(
  636. materials, m_entityComponentIdPair.GetEntityId(), &MaterialComponentRequests::GetMaterialMap);
  637. // Attempt to copy the triangle list geometry data out of the model asset into the visible geometry structure
  638. const auto& modelAsset = GetModelAsset();
  639. if (!modelAsset.IsReady() || modelAsset->GetLodAssets().empty())
  640. {
  641. AZ_Warning("MeshComponentController", false, "Unable to get geometry because mesh asset is not ready or empty.");
  642. return;
  643. }
  644. // This will only extract data from the first LOD. It might be necessary to make the LOD selectable.
  645. const int lodIndex = 0;
  646. const auto& lodAsset = modelAsset->GetLodAssets().front();
  647. if (!lodAsset.IsReady())
  648. {
  649. AZ_Warning("MeshComponentController", false, "Unable to get geometry because selected LOD asset is not ready.");
  650. return;
  651. }
  652. const AZ::Name positionName{ "POSITION" };
  653. for (const auto& mesh : lodAsset->GetMeshes())
  654. {
  655. // Get the index buffer data, confirming that the asset is valid and indices are 32 bit integers. Other formats are
  656. // currently not supported.
  657. const AZ::RPI::BufferAssetView& indexBufferView = mesh.GetIndexBufferAssetView();
  658. const AZ::RHI::BufferViewDescriptor& indexBufferViewDesc = indexBufferView.GetBufferViewDescriptor();
  659. const AZ::Data::Asset<AZ::RPI::BufferAsset> indexBufferAsset = indexBufferView.GetBufferAsset();
  660. if (!indexBufferAsset.IsReady() || indexBufferViewDesc.m_elementSize != sizeof(uint32_t))
  661. {
  662. AZ_Warning("MeshComponentController", false, "Unable to get geometry for mesh because index buffer asset is not ready or is an incompatible format.");
  663. continue;
  664. }
  665. // Get the position buffer data, if it exists with the expected name.
  666. const AZ::RPI::BufferAssetView* positionBufferView = mesh.GetSemanticBufferAssetView(positionName);
  667. if (!positionBufferView)
  668. {
  669. AZ_Warning("MeshComponentController", false, "Unable to get geometry for mesh because position buffer data was not found.");
  670. continue;
  671. }
  672. // Confirm that the position buffer is valid and contains 3 32 bit floats for each position. Other formats are currently not
  673. // supported.
  674. constexpr uint32_t ElementsPerVertex = 3;
  675. const AZ::RHI::BufferViewDescriptor& positionBufferViewDesc = positionBufferView->GetBufferViewDescriptor();
  676. const AZ::Data::Asset<AZ::RPI::BufferAsset> positionBufferAsset = positionBufferView->GetBufferAsset();
  677. if (!positionBufferAsset.IsReady() || positionBufferViewDesc.m_elementSize != sizeof(float) * ElementsPerVertex)
  678. {
  679. AZ_Warning("MeshComponentController", false, "Unable to get geometry for mesh because position buffer asset is not ready or is an incompatible format.");
  680. continue;
  681. }
  682. const AZStd::span<const uint8_t> indexRawBuffer = indexBufferAsset->GetBuffer();
  683. const uint32_t* indexPtr = reinterpret_cast<const uint32_t*>(
  684. indexRawBuffer.data() + (indexBufferViewDesc.m_elementOffset * indexBufferViewDesc.m_elementSize));
  685. const AZStd::span<const uint8_t> positionRawBuffer = positionBufferAsset->GetBuffer();
  686. const float* positionPtr = reinterpret_cast<const float*>(
  687. positionRawBuffer.data() + (positionBufferViewDesc.m_elementOffset * positionBufferViewDesc.m_elementSize));
  688. // Copy the index and position data into the visible geometry structure.
  689. AzFramework::VisibleGeometry visibleGeometry;
  690. visibleGeometry.m_transform = AZ::Matrix4x4::CreateFromTransform(m_transformInterface->GetWorldTM());
  691. // Reserve space for indices and copy data, assuming stride between elements is 0.
  692. visibleGeometry.m_indices.resize_no_construct(indexBufferViewDesc.m_elementCount);
  693. AZ_Assert(
  694. (sizeof(visibleGeometry.m_indices[0]) * visibleGeometry.m_indices.size()) >=
  695. (indexBufferViewDesc.m_elementSize * indexBufferViewDesc.m_elementCount),
  696. "Index buffer size exceeds memory allocated for visible geometry indices.");
  697. memcpy(&visibleGeometry.m_indices[0], indexPtr, indexBufferViewDesc.m_elementCount * indexBufferViewDesc.m_elementSize);
  698. // Reserve space for vertices and copy data, assuming stride between elements is 0.
  699. visibleGeometry.m_vertices.resize_no_construct(positionBufferViewDesc.m_elementCount * ElementsPerVertex);
  700. AZ_Assert(
  701. (sizeof(visibleGeometry.m_vertices[0]) * visibleGeometry.m_vertices.size()) >=
  702. (positionBufferViewDesc.m_elementSize * positionBufferViewDesc.m_elementCount),
  703. "Position buffer size exceeds memory allocated for visible geometry vertices.");
  704. memcpy(&visibleGeometry.m_vertices[0], positionPtr, positionBufferViewDesc.m_elementCount * positionBufferViewDesc.m_elementSize);
  705. // Inspect the material assigned to this mesh to determine if it should be considered transparent.
  706. const auto& materialSlotId = mesh.GetMaterialSlotId();
  707. const auto& materialSlot = modelAsset->FindMaterialSlot(materialSlotId);
  708. // The material asset assigned by the model will be used by default.
  709. AZ::Data::Instance<AZ::RPI::Material> material = AZ::RPI::Material::FindOrCreate(materialSlot.m_defaultMaterialAsset);
  710. // Materials provided by the material component take priority over materials provided by the model asset.
  711. const MaterialAssignmentId id(lodIndex, materialSlotId);
  712. const MaterialAssignmentId ignoreLodId(DefaultCustomMaterialLodIndex, materialSlotId);
  713. for (const auto& currentId : { id, ignoreLodId, DefaultMaterialAssignmentId })
  714. {
  715. if (auto itr = materials.find(currentId); itr != materials.end() && itr->second.m_materialInstance)
  716. {
  717. material = itr->second.m_materialInstance;
  718. break;
  719. }
  720. }
  721. // Once the active material has been resolved, determine if it has any shaders with the transparent tag.
  722. visibleGeometry.m_transparent = DoesMaterialUseDrawListTag(material, transparentDrawListTag);
  723. geometryContainer.emplace_back(AZStd::move(visibleGeometry));
  724. }
  725. // Release the draw list tag acquired at the top of the function to determine material transparency.
  726. AZ::RHI::RHISystemInterface::Get()->GetDrawListTagRegistry()->ReleaseTag(transparentDrawListTag);
  727. }
  728. bool MeshComponentController::DoesMaterialUseDrawListTag(
  729. const AZ::Data::Instance<AZ::RPI::Material> material, const AZ::RHI::DrawListTag searchDrawListTag) const
  730. {
  731. bool foundTag = false;
  732. if (material)
  733. {
  734. material->ForAllShaderItems(
  735. [&](const AZ::Name&, const AZ::RPI::ShaderCollection::Item& shaderItem)
  736. {
  737. if (shaderItem.IsEnabled())
  738. {
  739. // Get the DrawListTag. Use the explicit draw list override if exists.
  740. AZ::RHI::DrawListTag drawListTag = shaderItem.GetDrawListTagOverride();
  741. if (drawListTag.IsNull())
  742. {
  743. drawListTag = AZ::RHI::RHISystemInterface::Get()->GetDrawListTagRegistry()->FindTag(
  744. shaderItem.GetShaderAsset()->GetDrawListName());
  745. }
  746. // If this shader has a matching tag end the search.
  747. if (drawListTag == searchDrawListTag)
  748. {
  749. foundTag = true;
  750. return false;
  751. }
  752. }
  753. // Continue iterating until all shaders have been checked or a matching tag is found.
  754. return true;
  755. });
  756. }
  757. return foundTag;
  758. }
  759. AzFramework::RenderGeometry::RayResult MeshComponentController::RenderGeometryIntersect(
  760. const AzFramework::RenderGeometry::RayRequest& ray) const
  761. {
  762. AzFramework::RenderGeometry::RayResult result;
  763. if (const Data::Instance<RPI::Model> model = GetModel())
  764. {
  765. float t;
  766. AZ::Vector3 normal;
  767. if (model->RayIntersection(
  768. m_transformInterface->GetWorldTM(), m_cachedNonUniformScale, ray.m_startWorldPosition,
  769. ray.m_endWorldPosition - ray.m_startWorldPosition, t, normal))
  770. {
  771. // fill in ray result structure after successful intersection
  772. const auto intersectionLine = (ray.m_endWorldPosition - ray.m_startWorldPosition);
  773. result.m_uv = AZ::Vector2::CreateZero();
  774. result.m_worldPosition = ray.m_startWorldPosition + intersectionLine * t;
  775. result.m_worldNormal = normal;
  776. result.m_distance = intersectionLine.GetLength() * t;
  777. result.m_entityAndComponent = m_entityComponentIdPair;
  778. }
  779. }
  780. return result;
  781. }
  782. const MeshFeatureProcessorInterface::MeshHandle* MeshComponentController::GetMeshHandle() const
  783. {
  784. return &m_meshHandle;
  785. }
  786. } // namespace Render
  787. } // namespace AZ