MeshComponentController.cpp 46 KB

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