MeshBlockerComponent.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493
  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 "MeshBlockerComponent.h"
  9. #include <AzCore/Debug/Profiler.h>
  10. #include <AzCore/RTTI/BehaviorContext.h>
  11. #include <AzCore/Serialization/EditContext.h>
  12. #include <AzCore/Serialization/SerializeContext.h>
  13. #include <LmbrCentral/Shape/ShapeComponentBus.h>
  14. #include <SurfaceData/Utility/SurfaceDataUtility.h>
  15. #include <Vegetation/Ebuses/AreaRequestBus.h>
  16. #include <Vegetation/Ebuses/AreaSystemRequestBus.h>
  17. #include <Vegetation/Ebuses/FilterRequestBus.h>
  18. #include <Vegetation/InstanceData.h>
  19. #include <Atom/RPI.Reflect/Model/ModelAsset.h>
  20. #include <VegetationProfiler.h>
  21. AZ_DECLARE_BUDGET(Vegetation);
  22. namespace Vegetation
  23. {
  24. void MeshBlockerConfig::Reflect(AZ::ReflectContext* context)
  25. {
  26. AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
  27. if (serialize)
  28. {
  29. serialize->Class<MeshBlockerConfig, AreaConfig>()
  30. ->Version(2)
  31. ->Field("InheritBehavior", &MeshBlockerConfig::m_inheritBehavior)
  32. ->Field("MeshHeightPercentMin", &MeshBlockerConfig::m_meshHeightPercentMin)
  33. ->Field("MeshHeightPercentMax", &MeshBlockerConfig::m_meshHeightPercentMax)
  34. ->Field("BlockWhenInvisible", &MeshBlockerConfig::m_blockWhenInvisible)
  35. ;
  36. AZ::EditContext* edit = serialize->GetEditContext();
  37. if (edit)
  38. {
  39. edit->Class<MeshBlockerConfig>(
  40. "Vegetation Layer Blocker (Mesh)", "")
  41. ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  42. ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
  43. ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
  44. ->DataElement(0, &MeshBlockerConfig::m_inheritBehavior, "Inherit Behavior", "Allow shapes, modifiers, filters of a parent to affect this area.")
  45. ->DataElement(AZ::Edit::UIHandlers::Slider, &MeshBlockerConfig::m_meshHeightPercentMin, "Mesh Height Percent Min", "The percentage of the mesh height (from the bottom up) used as the lower bound for intersection tests")
  46. ->Attribute(AZ::Edit::Attributes::Min, 0.0f)
  47. ->Attribute(AZ::Edit::Attributes::Max, 1.0f)
  48. ->DataElement(AZ::Edit::UIHandlers::Slider, &MeshBlockerConfig::m_meshHeightPercentMax, "Mesh Height Percent Max", "The percentage of the mesh height (from the bottom up) used as the upper bound for intersection tests")
  49. ->Attribute(AZ::Edit::Attributes::Min, 0.0f)
  50. ->Attribute(AZ::Edit::Attributes::Max, 1.0f)
  51. ->DataElement(0, &MeshBlockerConfig::m_blockWhenInvisible, "Block When Invisible", "Continue to block vegetation even if the mesh is invisible.")
  52. ;
  53. }
  54. }
  55. if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
  56. {
  57. behaviorContext->Class<MeshBlockerConfig>()
  58. ->Attribute(AZ::Script::Attributes::Category, "Vegetation")
  59. ->Constructor()
  60. ->Property("inheritBehavior", BehaviorValueProperty(&MeshBlockerConfig::m_inheritBehavior))
  61. ->Property("meshHeightPercentMin", BehaviorValueProperty(&MeshBlockerConfig::m_meshHeightPercentMin))
  62. ->Property("meshHeightPercentMax", BehaviorValueProperty(&MeshBlockerConfig::m_meshHeightPercentMax))
  63. ->Property("blockWhenInvisible", BehaviorValueProperty(&MeshBlockerConfig::m_blockWhenInvisible))
  64. ;
  65. }
  66. }
  67. void MeshBlockerComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services)
  68. {
  69. AreaComponentBase::GetProvidedServices(services);
  70. }
  71. void MeshBlockerComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& services)
  72. {
  73. AreaComponentBase::GetIncompatibleServices(services);
  74. services.push_back(AZ_CRC_CE("VegetationModifierService"));
  75. }
  76. void MeshBlockerComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& services)
  77. {
  78. AreaComponentBase::GetRequiredServices(services);
  79. services.push_back(AZ_CRC_CE("MeshService"));
  80. }
  81. void MeshBlockerComponent::Reflect(AZ::ReflectContext* context)
  82. {
  83. MeshBlockerConfig::Reflect(context);
  84. AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
  85. if (serialize)
  86. {
  87. serialize->Class<MeshBlockerComponent, AreaComponentBase>()
  88. ->Version(0)
  89. ->Field("Configuration", &MeshBlockerComponent::m_configuration)
  90. ;
  91. }
  92. if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
  93. {
  94. behaviorContext->Constant("MeshBlockerComponentTypeId", BehaviorConstant(MeshBlockerComponentTypeId));
  95. behaviorContext->Class<MeshBlockerComponent>()->RequestBus("MeshBlockerRequestBus");
  96. behaviorContext->EBus<MeshBlockerRequestBus>("MeshBlockerRequestBus")
  97. ->Attribute(AZ::Script::Attributes::Category, "Vegetation")
  98. ->Event("GetAreaPriority", &MeshBlockerRequestBus::Events::GetAreaPriority)
  99. ->Event("SetAreaPriority", &MeshBlockerRequestBus::Events::SetAreaPriority)
  100. ->VirtualProperty("AreaPriority", "GetAreaPriority", "SetAreaPriority")
  101. ->Event("GetAreaLayer", &MeshBlockerRequestBus::Events::GetAreaLayer)
  102. ->Event("SetAreaLayer", &MeshBlockerRequestBus::Events::SetAreaLayer)
  103. ->VirtualProperty("AreaLayer", "GetAreaLayer", "SetAreaLayer")
  104. ->Event("GetAreaProductCount", &MeshBlockerRequestBus::Events::GetAreaProductCount)
  105. ->Event("GetInheritBehavior", &MeshBlockerRequestBus::Events::GetInheritBehavior)
  106. ->Event("SetInheritBehavior", &MeshBlockerRequestBus::Events::SetInheritBehavior)
  107. ->VirtualProperty("InheritBehavior", "GetInheritBehavior", "SetInheritBehavior")
  108. ->Event("GetMeshHeightPercentMin", &MeshBlockerRequestBus::Events::GetMeshHeightPercentMin)
  109. ->Event("SetMeshHeightPercentMin", &MeshBlockerRequestBus::Events::SetMeshHeightPercentMin)
  110. ->VirtualProperty("MeshHeightPercentMin", "GetMeshHeightPercentMin", "SetMeshHeightPercentMin")
  111. ->Event("GetMeshHeightPercentMax", &MeshBlockerRequestBus::Events::GetMeshHeightPercentMax)
  112. ->Event("SetMeshHeightPercentMax", &MeshBlockerRequestBus::Events::SetMeshHeightPercentMax)
  113. ->VirtualProperty("MeshHeightPercentMax", "GetMeshHeightPercentMax", "SetMeshHeightPercentMax")
  114. ->Event("GetBlockWhenInvisible", &MeshBlockerRequestBus::Events::GetBlockWhenInvisible)
  115. ->Event("SetBlockWhenInvisible", &MeshBlockerRequestBus::Events::SetBlockWhenInvisible)
  116. ->VirtualProperty("BlockWhenInvisible", "GetBlockWhenInvisible", "SetBlockWhenInvisible")
  117. ;
  118. }
  119. }
  120. MeshBlockerComponent::MeshBlockerComponent()
  121. : AreaComponentBase()
  122. , m_nonUniformScaleChangedHandler([this]([[maybe_unused]] const AZ::Vector3& scale) { this->OnCompositionChanged(); })
  123. {
  124. }
  125. MeshBlockerComponent::MeshBlockerComponent(const MeshBlockerConfig& configuration)
  126. : AreaComponentBase(configuration)
  127. , m_configuration(configuration)
  128. , m_nonUniformScaleChangedHandler([this]([[maybe_unused]] const AZ::Vector3& scale) { this->OnCompositionChanged(); })
  129. {
  130. }
  131. void MeshBlockerComponent::Activate()
  132. {
  133. AZ::Render::MeshComponentNotificationBus::Handler::BusConnect(GetEntityId());
  134. AZ::NonUniformScaleRequestBus::Event(
  135. GetEntityId(), &AZ::NonUniformScaleRequests::RegisterScaleChangedEvent, m_nonUniformScaleChangedHandler);
  136. UpdateMeshData();
  137. m_refresh = false;
  138. MeshBlockerRequestBus::Handler::BusConnect(GetEntityId());
  139. SurfaceData::SurfaceDataSystemNotificationBus::Handler::BusConnect();
  140. AreaComponentBase::Activate(); //must activate base last to connect AreaRequestBus once everything else is setup
  141. }
  142. void MeshBlockerComponent::Deactivate()
  143. {
  144. AreaComponentBase::Deactivate(); //must deactivate base first to ensure AreaRequestBus disconnect waits for other threads
  145. m_nonUniformScaleChangedHandler.Disconnect();
  146. SurfaceData::SurfaceDataSystemNotificationBus::Handler::BusDisconnect();
  147. m_refresh = false;
  148. AZ::TickBus::Handler::BusDisconnect();
  149. AZ::Render::MeshComponentNotificationBus::Handler::BusDisconnect();
  150. MeshBlockerRequestBus::Handler::BusDisconnect();
  151. }
  152. bool MeshBlockerComponent::ReadInConfig(const AZ::ComponentConfig* baseConfig)
  153. {
  154. AreaComponentBase::ReadInConfig(baseConfig);
  155. if (auto config = azrtti_cast<const MeshBlockerConfig*>(baseConfig))
  156. {
  157. m_configuration = *config;
  158. return true;
  159. }
  160. return false;
  161. }
  162. bool MeshBlockerComponent::WriteOutConfig(AZ::ComponentConfig* outBaseConfig) const
  163. {
  164. AreaComponentBase::WriteOutConfig(outBaseConfig);
  165. if (auto config = azrtti_cast<MeshBlockerConfig*>(outBaseConfig))
  166. {
  167. *config = m_configuration;
  168. return true;
  169. }
  170. return false;
  171. }
  172. bool MeshBlockerComponent::PrepareToClaim([[maybe_unused]] EntityIdStack& stackIds)
  173. {
  174. AZ_PROFILE_FUNCTION(Vegetation);
  175. AZStd::lock_guard<decltype(m_cacheMutex)> cacheLock(m_cacheMutex);
  176. if (!m_meshAssetData.GetId().IsValid())
  177. {
  178. return false;
  179. }
  180. AZ::RPI::ModelAsset* mesh = m_meshAssetData.GetAs<AZ::RPI::ModelAsset>();
  181. if (!mesh)
  182. {
  183. return false;
  184. }
  185. return m_meshBoundsForIntersection.IsValid() && (m_meshVisible || m_configuration.m_blockWhenInvisible);
  186. }
  187. bool MeshBlockerComponent::ClaimPosition(EntityIdStack& processedIds, const ClaimPoint& point, InstanceData& instanceData)
  188. {
  189. AZ_PROFILE_FUNCTION(Vegetation);
  190. AZStd::lock_guard<decltype(m_cacheMutex)> cacheLock(m_cacheMutex);
  191. // If we've previously looked up this point for collision, reuse the results.
  192. // Note that this performs lookups based on ClaimPoint handles, so the cache needs to be invalidated on anything
  193. // that can cause the handles to change. (See OnSurfaceChanged below)
  194. auto itCachedRayHit = m_cachedRayHits.find(point.m_handle);
  195. if (itCachedRayHit != m_cachedRayHits.end())
  196. {
  197. return itCachedRayHit->second;
  198. }
  199. // test AABB as first pass to claim the point
  200. if (!m_meshBoundsForIntersection.Contains(point.m_position))
  201. {
  202. return false;
  203. }
  204. AZ::RPI::ModelAsset* mesh = m_meshAssetData.GetAs<AZ::RPI::ModelAsset>();
  205. if (!mesh)
  206. {
  207. return false;
  208. }
  209. // test shape bus as first pass to claim the point
  210. bool isInsideShape = true;
  211. for (const auto& id : processedIds)
  212. {
  213. LmbrCentral::ShapeComponentRequestsBus::EventResult(isInsideShape, id, &LmbrCentral::ShapeComponentRequestsBus::Events::IsPointInside, point.m_position);
  214. if (!isInsideShape)
  215. {
  216. m_cachedRayHits[point.m_handle] = false;
  217. return false;
  218. }
  219. }
  220. //determine if an instance can be created using the generated details
  221. for (const auto& id : processedIds)
  222. {
  223. bool accepted = true;
  224. FilterRequestBus::EnumerateHandlersId(id, [&instanceData, &accepted](FilterRequestBus::Events* handler) {
  225. accepted = handler->Evaluate(instanceData);
  226. return accepted;
  227. });
  228. if (!accepted)
  229. {
  230. m_cachedRayHits[point.m_handle] = false;
  231. return false;
  232. }
  233. }
  234. AZ::Vector3 outPosition;
  235. AZ::Vector3 outNormal;
  236. const AZ::Vector3 rayStart(point.m_position.GetX(), point.m_position.GetY(), m_meshBoundsForIntersection.GetMax().GetZ());
  237. const AZ::Vector3 rayEnd(point.m_position.GetX(), point.m_position.GetY(), m_meshBoundsForIntersection.GetMin().GetZ());
  238. bool intersected = SurfaceData::GetMeshRayIntersection(
  239. *mesh, m_meshWorldTM, m_meshWorldTMInverse, m_meshNonUniformScale, rayStart, rayEnd, outPosition, outNormal) &&
  240. m_meshBoundsForIntersection.Contains(outPosition);
  241. m_cachedRayHits[point.m_handle] = intersected;
  242. return intersected;
  243. }
  244. void MeshBlockerComponent::ClaimPositions(EntityIdStack& stackIds, ClaimContext& context)
  245. {
  246. AZ_PROFILE_FUNCTION(Vegetation);
  247. //adding entity id to the stack of entity ids affecting vegetation
  248. EntityIdStack emptyIds;
  249. //when the inherit flag is disabled, as opposed to always inheriting, the stack must be cleared but preserved so redirecting to an empty stack to avoid copying
  250. EntityIdStack& processedIds = m_configuration.m_inheritBehavior ? stackIds : emptyIds;
  251. //adding current entity id to be processed uniformly
  252. EntityIdStackPusher stackPusher(processedIds, GetEntityId());
  253. InstanceData instanceData;
  254. instanceData.m_id = GetEntityId();
  255. instanceData.m_changeIndex = GetChangeIndex();
  256. size_t numAvailablePoints = context.m_availablePoints.size();
  257. for (size_t pointIndex = 0; pointIndex < numAvailablePoints; )
  258. {
  259. ClaimPoint& point = context.m_availablePoints[pointIndex];
  260. //generate details for a single vegetation instance
  261. instanceData.m_position = point.m_position;
  262. instanceData.m_normal = point.m_normal;
  263. instanceData.m_masks = point.m_masks;
  264. if (ClaimPosition(processedIds, point, instanceData))
  265. {
  266. context.m_createdCallback(point, instanceData);
  267. //Swap an available point from the end of the list
  268. AZStd::swap(point, context.m_availablePoints.at(numAvailablePoints - 1));
  269. --numAvailablePoints;
  270. continue;
  271. }
  272. ++pointIndex;
  273. }
  274. //resize to remove all used points
  275. context.m_availablePoints.resize(numAvailablePoints);
  276. }
  277. void MeshBlockerComponent::UnclaimPosition([[maybe_unused]] const ClaimHandle handle)
  278. {
  279. }
  280. AZ::Aabb MeshBlockerComponent::GetEncompassingAabb() const
  281. {
  282. AZStd::lock_guard<decltype(m_cacheMutex)> cacheLock(m_cacheMutex);
  283. return m_meshBounds;
  284. }
  285. AZ::u32 MeshBlockerComponent::GetProductCount() const
  286. {
  287. return 0;
  288. }
  289. void MeshBlockerComponent::OnCompositionChanged()
  290. {
  291. if (!m_refresh)
  292. {
  293. m_refresh = true;
  294. AZ::TickBus::Handler::BusConnect();
  295. }
  296. }
  297. void MeshBlockerComponent::OnSurfaceChanged(
  298. [[maybe_unused]] const AZ::EntityId& entityId,
  299. [[maybe_unused]] const AZ::Aabb& oldBounds,
  300. [[maybe_unused]] const AZ::Aabb& newBounds,
  301. [[maybe_unused]] const SurfaceData::SurfaceTagSet& changedSurfaceTags)
  302. {
  303. // If our surfaces have changed, we will need to refresh our cache.
  304. // Our cache performs lookups based on ClaimPoint handles, but the list of handles can potentially change
  305. // from any type of surface change anywhere, so refresh even if the area doesn't overlap.
  306. OnCompositionChanged();
  307. }
  308. void MeshBlockerComponent::OnModelReady([[maybe_unused]] const AZ::Data::Asset<AZ::RPI::ModelAsset>& modelAsset, [[maybe_unused]]const AZ::Data::Instance<AZ::RPI::Model>& model)
  309. {
  310. LmbrCentral::DependencyNotificationBus::Event(GetEntityId(), &LmbrCentral::DependencyNotificationBus::Events::OnCompositionChanged);
  311. }
  312. void MeshBlockerComponent::OnTick(float /*deltaTime*/, AZ::ScriptTimePoint /*time*/)
  313. {
  314. if (m_refresh)
  315. {
  316. UpdateMeshData();
  317. m_refresh = false;
  318. }
  319. AZ::TickBus::Handler::BusDisconnect();
  320. }
  321. void MeshBlockerComponent::UpdateMeshData()
  322. {
  323. AZ_PROFILE_FUNCTION(Vegetation);
  324. AZStd::lock_guard<decltype(m_cacheMutex)> cacheLock(m_cacheMutex);
  325. m_cachedRayHits.clear();
  326. m_meshAssetData = {};
  327. AZ::Render::MeshComponentRequestBus::EventResult(m_meshAssetData, GetEntityId(), &AZ::Render::MeshComponentRequests::GetModelAsset);
  328. m_meshBounds = AZ::Aabb::CreateNull();
  329. AZ::Render::MeshComponentRequestBus::EventResult(m_meshBounds, GetEntityId(), &AZ::Render::MeshComponentRequestBus::Events::GetWorldBounds);
  330. m_meshBoundsForIntersection = m_meshBounds;
  331. if (m_meshBoundsForIntersection.IsValid())
  332. {
  333. const auto heights = AZStd::minmax(
  334. m_meshBoundsForIntersection.GetMin().GetZ() + m_meshBoundsForIntersection.GetExtents().GetZ() * m_configuration.m_meshHeightPercentMin,
  335. m_meshBoundsForIntersection.GetMin().GetZ() + m_meshBoundsForIntersection.GetExtents().GetZ() * m_configuration.m_meshHeightPercentMax);
  336. AZ::Vector3 cornerMin = m_meshBoundsForIntersection.GetMin();
  337. cornerMin.SetZ(heights.first - s_rayAABBHeightPadding);
  338. AZ::Vector3 cornerMax = m_meshBoundsForIntersection.GetMax();
  339. cornerMax.SetZ(heights.second + s_rayAABBHeightPadding);
  340. m_meshBoundsForIntersection.Set(cornerMin, cornerMax);
  341. }
  342. m_meshVisible = false;
  343. AZ::Render::MeshComponentRequestBus::EventResult(m_meshVisible, GetEntityId(), &AZ::Render::MeshComponentRequests::GetVisibility);
  344. m_meshWorldTM = AZ::Transform::CreateIdentity();
  345. AZ::TransformBus::EventResult(m_meshWorldTM, GetEntityId(), &AZ::TransformBus::Events::GetWorldTM);
  346. m_meshWorldTMInverse = m_meshWorldTM.GetInverse();
  347. m_meshNonUniformScale = AZ::Vector3::CreateOne();
  348. AZ::NonUniformScaleRequestBus::EventResult(m_meshNonUniformScale, GetEntityId(), &AZ::NonUniformScaleRequests::GetScale);
  349. AreaComponentBase::OnCompositionChanged();
  350. }
  351. AZ::u32 MeshBlockerComponent::GetAreaPriority() const
  352. {
  353. return m_configuration.m_priority;
  354. }
  355. void MeshBlockerComponent::SetAreaPriority(AZ::u32 priority)
  356. {
  357. m_configuration.m_priority = priority;
  358. LmbrCentral::DependencyNotificationBus::Event(GetEntityId(), &LmbrCentral::DependencyNotificationBus::Events::OnCompositionChanged);
  359. }
  360. AZ::u32 MeshBlockerComponent::GetAreaLayer() const
  361. {
  362. return m_configuration.m_layer;
  363. }
  364. void MeshBlockerComponent::SetAreaLayer(AZ::u32 layer)
  365. {
  366. m_configuration.m_layer = layer;
  367. LmbrCentral::DependencyNotificationBus::Event(GetEntityId(), &LmbrCentral::DependencyNotificationBus::Events::OnCompositionChanged);
  368. }
  369. AZ::u32 MeshBlockerComponent::GetAreaProductCount() const
  370. {
  371. return GetProductCount();
  372. }
  373. bool MeshBlockerComponent::GetInheritBehavior() const
  374. {
  375. return m_configuration.m_inheritBehavior;
  376. }
  377. void MeshBlockerComponent::SetInheritBehavior(bool value)
  378. {
  379. m_configuration.m_inheritBehavior = value;
  380. LmbrCentral::DependencyNotificationBus::Event(GetEntityId(), &LmbrCentral::DependencyNotificationBus::Events::OnCompositionChanged);
  381. }
  382. float MeshBlockerComponent::GetMeshHeightPercentMin() const
  383. {
  384. return m_configuration.m_meshHeightPercentMax;
  385. }
  386. void MeshBlockerComponent::SetMeshHeightPercentMin(float meshHeightPercentMin)
  387. {
  388. m_configuration.m_meshHeightPercentMax = meshHeightPercentMin;
  389. LmbrCentral::DependencyNotificationBus::Event(GetEntityId(), &LmbrCentral::DependencyNotificationBus::Events::OnCompositionChanged);
  390. }
  391. float MeshBlockerComponent::GetMeshHeightPercentMax() const
  392. {
  393. return m_configuration.m_meshHeightPercentMax;
  394. }
  395. void MeshBlockerComponent::SetMeshHeightPercentMax(float meshHeightPercentMax)
  396. {
  397. m_configuration.m_meshHeightPercentMax = meshHeightPercentMax;
  398. LmbrCentral::DependencyNotificationBus::Event(GetEntityId(), &LmbrCentral::DependencyNotificationBus::Events::OnCompositionChanged);
  399. }
  400. bool MeshBlockerComponent::GetBlockWhenInvisible() const
  401. {
  402. return m_configuration.m_blockWhenInvisible;
  403. }
  404. void MeshBlockerComponent::SetBlockWhenInvisible(bool value)
  405. {
  406. m_configuration.m_blockWhenInvisible = value;
  407. LmbrCentral::DependencyNotificationBus::Event(GetEntityId(), &LmbrCentral::DependencyNotificationBus::Events::OnCompositionChanged);
  408. }
  409. }