SpawnerComponent.cpp 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642
  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 <VegetationProfiler.h>
  9. #include "SpawnerComponent.h"
  10. #include <AzCore/Component/Entity.h>
  11. #include <AzCore/Debug/Profiler.h>
  12. #include <AzCore/RTTI/BehaviorContext.h>
  13. #include <AzCore/Serialization/EditContext.h>
  14. #include <AzCore/Serialization/SerializeContext.h>
  15. #include <LmbrCentral/Dependency/DependencyNotificationBus.h>
  16. #include <Vegetation/Ebuses/AreaDebugBus.h>
  17. #include <Vegetation/Ebuses/AreaInfoBus.h>
  18. #include <Vegetation/Ebuses/DescriptorProviderRequestBus.h>
  19. #include <Vegetation/InstanceData.h>
  20. #include <GradientSignal/Util.h>
  21. #include <SurfaceData/SurfaceDataTagEnumeratorRequestBus.h>
  22. #include <AzCore/std/sort.h>
  23. #include <SurfaceData/Utility/SurfaceDataUtility.h>
  24. #include <Vegetation/Ebuses/DebugNotificationBus.h>
  25. namespace Vegetation
  26. {
  27. namespace SpawnerUtil
  28. {
  29. static bool UpdateVersion([[maybe_unused]] AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement)
  30. {
  31. if (classElement.GetVersion() < 1)
  32. {
  33. classElement.RemoveElementByName(AZ_CRC("UseRelativeUVW", 0x97a6718e));
  34. }
  35. return true;
  36. }
  37. }
  38. void SpawnerConfig::Reflect(AZ::ReflectContext* context)
  39. {
  40. AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
  41. if (serialize)
  42. {
  43. serialize->Class<SpawnerConfig, AreaConfig>()
  44. ->Version(1, &SpawnerUtil::UpdateVersion)
  45. ->Field("InheritBehavior", &SpawnerConfig::m_inheritBehavior)
  46. ->Field("AllowEmptyMeshes", &SpawnerConfig::m_allowEmptyMeshes)
  47. ->Field("FilterStage", &SpawnerConfig::m_filterStage)
  48. ;
  49. AZ::EditContext* edit = serialize->GetEditContext();
  50. if (edit)
  51. {
  52. edit->Class<SpawnerConfig>(
  53. "Vegetation Layer Spawner", "Vegetation spawner")
  54. ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  55. ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
  56. ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
  57. ->DataElement(0, &SpawnerConfig::m_inheritBehavior, "Inherit Behavior", "Allow shapes, modifiers, filters of a parent to affect this area.")
  58. ->DataElement(0, &SpawnerConfig::m_allowEmptyMeshes, "Allow Empty Assets", "Allow unspecified asset references in the Descriptors to claim space and block other vegetation.")
  59. ->DataElement(AZ::Edit::UIHandlers::ComboBox, &SpawnerConfig::m_filterStage, "Filter Stage", "Determines if filter is applied before (PreProcess) or after (PostProcess) modifiers.")
  60. ->EnumAttribute(FilterStage::PreProcess, "PreProcess")
  61. ->EnumAttribute(FilterStage::PostProcess, "PostProcess")
  62. ;
  63. }
  64. }
  65. if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
  66. {
  67. behaviorContext->Class<SpawnerConfig>("VegetationSpawnerConfig")
  68. ->Attribute(AZ::Script::Attributes::Category, "Vegetation")
  69. ->Constructor()
  70. ->Property("filterStage",
  71. [](SpawnerConfig* config) { return (AZ::u8&)(config->m_filterStage); },
  72. [](SpawnerConfig* config, const AZ::u8& i) { config->m_filterStage = (FilterStage)i; })
  73. ->Property("inheritBehavior", BehaviorValueProperty(&SpawnerConfig::m_inheritBehavior))
  74. ->Property("allowEmptyMeshes", BehaviorValueProperty(&SpawnerConfig::m_allowEmptyMeshes))
  75. ;
  76. }
  77. }
  78. void SpawnerComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services)
  79. {
  80. AreaComponentBase::GetProvidedServices(services);
  81. }
  82. void SpawnerComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& services)
  83. {
  84. AreaComponentBase::GetIncompatibleServices(services);
  85. }
  86. void SpawnerComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& services)
  87. {
  88. AreaComponentBase::GetRequiredServices(services);
  89. services.push_back(AZ_CRC("VegetationDescriptorProviderService", 0x62e51209));
  90. services.push_back(AZ_CRC("ShapeService", 0xe86aa5fe));
  91. }
  92. void SpawnerComponent::Reflect(AZ::ReflectContext* context)
  93. {
  94. SpawnerConfig::Reflect(context);
  95. AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
  96. if (serialize)
  97. {
  98. serialize->Class<SpawnerComponent, AreaComponentBase>()
  99. ->Version(0)
  100. ->Field("Configuration", &SpawnerComponent::m_configuration)
  101. ;
  102. }
  103. if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
  104. {
  105. behaviorContext->Constant("VegetationSpawnerComponentTypeId", BehaviorConstant(SpawnerComponentTypeId));
  106. behaviorContext->Class<SpawnerComponent>("VegetationSpawner")->RequestBus("SpawnerRequestBus");
  107. behaviorContext->EBus<SpawnerRequestBus>("VegetationSpawnerRequestBus")
  108. ->Attribute(AZ::Script::Attributes::Category, "Vegetation")
  109. ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
  110. ->Attribute(AZ::Script::Attributes::Module, "vegetation")
  111. ->Event("GetAreaPriority", &SpawnerRequestBus::Events::GetAreaPriority)
  112. ->Event("SetAreaPriority", &SpawnerRequestBus::Events::SetAreaPriority)
  113. ->VirtualProperty("AreaPriority", "GetAreaPriority", "SetAreaPriority")
  114. ->Event("GetAreaLayer", &SpawnerRequestBus::Events::GetAreaLayer)
  115. ->Event("SetAreaLayer", &SpawnerRequestBus::Events::SetAreaLayer)
  116. ->VirtualProperty("AreaLayer", "GetAreaLayer", "SetAreaLayer")
  117. ->Event("GetAreaProductCount", &SpawnerRequestBus::Events::GetAreaProductCount)
  118. ->Event("GetInheritBehavior", &SpawnerRequestBus::Events::GetInheritBehavior)
  119. ->Event("SetInheritBehavior", &SpawnerRequestBus::Events::SetInheritBehavior)
  120. ->VirtualProperty("InheritBehavior", "GetInheritBehavior", "SetInheritBehavior")
  121. ->Event("GetAllowEmptyMeshes", &SpawnerRequestBus::Events::GetAllowEmptyMeshes)
  122. ->Event("SetAllowEmptyMeshes", &SpawnerRequestBus::Events::SetAllowEmptyMeshes)
  123. ->VirtualProperty("AllowEmptyMeshes", "GetAllowEmptyMeshes", "SetAllowEmptyMeshes")
  124. ->Event("GetFilterStage", &SpawnerRequestBus::Events::GetFilterStage)
  125. ->Event("SetFilterStage", &SpawnerRequestBus::Events::SetFilterStage)
  126. ->VirtualProperty("FilterStage", "GetFilterStage", "SetFilterStage")
  127. ;
  128. }
  129. }
  130. SpawnerComponent::SpawnerComponent(const SpawnerConfig& configuration)
  131. : AreaComponentBase(configuration)
  132. , m_configuration(configuration)
  133. {
  134. }
  135. void SpawnerComponent::Activate()
  136. {
  137. ClearSelectableDescriptors();
  138. SpawnerRequestBus::Handler::BusConnect(GetEntityId());
  139. AreaComponentBase::Activate(); //must activate base last to connect AreaRequestBus once everything else is setup
  140. }
  141. void SpawnerComponent::Deactivate()
  142. {
  143. AreaComponentBase::Deactivate(); //must deactivate base first to ensure AreaRequestBus disconnect waits for other threads
  144. SpawnerRequestBus::Handler::BusDisconnect();
  145. OnUnregisterArea();
  146. }
  147. void SpawnerComponent::OnRegisterArea()
  148. {
  149. // Every time our area becomes valid and registered, make sure we clear our
  150. // temporary descriptor caches. These should only contain valid data during
  151. // PrepareToClaim / Claim, but it doesn't hurt to ensure they're cleared.
  152. ClearSelectableDescriptors();
  153. }
  154. void SpawnerComponent::OnUnregisterArea()
  155. {
  156. // Every time our area becomes invalid and unregistered, make sure we destroy
  157. // all instances that this component spawned. Also clear our temporary descriptor
  158. // caches just to keep things tidy.
  159. ClearSelectableDescriptors();
  160. DestroyAllInstances();
  161. }
  162. bool SpawnerComponent::ReadInConfig(const AZ::ComponentConfig* baseConfig)
  163. {
  164. AreaComponentBase::ReadInConfig(baseConfig);
  165. if (auto config = azrtti_cast<const SpawnerConfig*>(baseConfig))
  166. {
  167. m_configuration = *config;
  168. return true;
  169. }
  170. return false;
  171. }
  172. bool SpawnerComponent::WriteOutConfig(AZ::ComponentConfig* outBaseConfig) const
  173. {
  174. AreaComponentBase::WriteOutConfig(outBaseConfig);
  175. if (auto config = azrtti_cast<SpawnerConfig*>(outBaseConfig))
  176. {
  177. *config = m_configuration;
  178. return true;
  179. }
  180. return false;
  181. }
  182. bool SpawnerComponent::PrepareToClaim(EntityIdStack& stackIds)
  183. {
  184. AZ_PROFILE_FUNCTION(Vegetation);
  185. //adding entity id to the stack of entity ids affecting vegetation
  186. EntityIdStack emptyIds;
  187. //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
  188. EntityIdStack& processedIds = m_configuration.m_inheritBehavior ? stackIds : emptyIds;
  189. //adding current entity id to be processed uniformly
  190. EntityIdStackPusher stackPusher(processedIds, GetEntityId());
  191. #if defined(VEG_PROFILE_ENABLED)
  192. CalcInstanceDebugColor(processedIds);
  193. #endif
  194. //gather tags from all sources so we can early out of processing this area
  195. bool includeAll = false;
  196. m_inclusiveTagsToConsider.clear();
  197. m_exclusiveTagsToConsider.clear();
  198. for (const auto& id : processedIds)
  199. {
  200. SurfaceData::SurfaceDataTagEnumeratorRequestBus::Event(id, &SurfaceData::SurfaceDataTagEnumeratorRequestBus::Events::GetInclusionSurfaceTags, m_inclusiveTagsToConsider, includeAll);
  201. SurfaceData::SurfaceDataTagEnumeratorRequestBus::Event(id, &SurfaceData::SurfaceDataTagEnumeratorRequestBus::Events::GetExclusionSurfaceTags, m_exclusiveTagsToConsider);
  202. }
  203. // If anything is telling us to include all surfaces, clear out our list, as this means "check everything".
  204. if (includeAll)
  205. {
  206. m_inclusiveTagsToConsider.clear();
  207. }
  208. AZStd::sort(m_inclusiveTagsToConsider.begin(), m_inclusiveTagsToConsider.end());
  209. m_inclusiveTagsToConsider.erase(
  210. std::unique(m_inclusiveTagsToConsider.begin(), m_inclusiveTagsToConsider.end()),
  211. m_inclusiveTagsToConsider.end());
  212. AZStd::sort(m_exclusiveTagsToConsider.begin(), m_exclusiveTagsToConsider.end());
  213. m_exclusiveTagsToConsider.erase(
  214. std::unique(m_exclusiveTagsToConsider.begin(), m_exclusiveTagsToConsider.end()),
  215. m_exclusiveTagsToConsider.end());
  216. //reset selectable descriptors
  217. ClearSelectableDescriptors();
  218. //gather all descriptors to be used for vegetation selection
  219. AZStd::lock_guard<decltype(m_selectableDescriptorMutex)> selectableDescriptorLock(m_selectableDescriptorMutex);
  220. for (const auto& id : processedIds)
  221. {
  222. DescriptorProviderRequestBus::Event(id, &DescriptorProviderRequestBus::Events::GetDescriptors, m_selectableDescriptorCache);
  223. }
  224. return !m_selectableDescriptorCache.empty();
  225. }
  226. bool SpawnerComponent::CreateInstance([[maybe_unused]] const ClaimPoint &point, InstanceData& instanceData)
  227. {
  228. VEGETATION_PROFILE_FUNCTION_VERBOSE
  229. instanceData.m_instanceId = InvalidInstanceId;
  230. if (instanceData.m_descriptorPtr && instanceData.m_descriptorPtr->IsSpawnable())
  231. {
  232. InstanceSystemRequestBus::Broadcast(&InstanceSystemRequestBus::Events::CreateInstance, instanceData);
  233. }
  234. return instanceData.m_instanceId != InvalidInstanceId || m_configuration.m_allowEmptyMeshes;
  235. }
  236. void SpawnerComponent::ClearSelectableDescriptors()
  237. {
  238. AZStd::lock_guard<decltype(m_selectableDescriptorMutex)> selectableDescriptorLock(m_selectableDescriptorMutex);
  239. m_selectedDescriptors.clear();
  240. m_selectableDescriptorCache.clear();
  241. }
  242. bool SpawnerComponent::EvaluateFilters(EntityIdStack& processedIds, InstanceData& instanceData, const FilterStage intendedStage) const
  243. {
  244. VEGETATION_PROFILE_FUNCTION_VERBOSE
  245. bool accepted = true;
  246. for (const auto& id : processedIds)
  247. {
  248. FilterRequestBus::EnumerateHandlersId(id, [this, &instanceData, &accepted, intendedStage](FilterRequestBus::Events* handler) {
  249. const FilterStage stage = handler->GetFilterStage();
  250. if (stage == intendedStage || (stage == FilterStage::Default && m_configuration.m_filterStage == intendedStage))
  251. {
  252. accepted = handler->Evaluate(instanceData);
  253. }
  254. return accepted;
  255. });
  256. if (!accepted)
  257. {
  258. break;
  259. }
  260. }
  261. return accepted;
  262. }
  263. bool SpawnerComponent::ProcessInstance(EntityIdStack& processedIds, const ClaimPoint& point, InstanceData& instanceData, DescriptorPtr descriptorPtr)
  264. {
  265. VEGETATION_PROFILE_FUNCTION_VERBOSE
  266. if (!descriptorPtr)
  267. {
  268. AZ_Error("vegetation", descriptorPtr, "DescriptorPtr should always be valid when spawning!");
  269. return false;
  270. }
  271. // If this is an empty mesh asset (no valid id) AND we don't allow empty meshes, skip this descriptor
  272. if (!m_configuration.m_allowEmptyMeshes && descriptorPtr->HasEmptyAssetReferences())
  273. {
  274. return false;
  275. }
  276. //generate details for a single vegetation instance using the current descriptor
  277. static const AZ::Quaternion identityQuat = AZ::Quaternion::CreateIdentity();
  278. instanceData.m_descriptorPtr = descriptorPtr;
  279. instanceData.m_instanceId = InvalidInstanceId;
  280. instanceData.m_position = point.m_position;
  281. instanceData.m_normal = point.m_normal;
  282. instanceData.m_masks = point.m_masks;
  283. instanceData.m_rotation = identityQuat;
  284. instanceData.m_alignment = identityQuat;
  285. instanceData.m_scale = 1.0f;
  286. // run pre-process filters on unmodified instance data
  287. if (!EvaluateFilters(processedIds, instanceData, FilterStage::PreProcess))
  288. {
  289. // Once a filter rejects the instance, no point in running the rest.
  290. return false;
  291. }
  292. // If all of the pre-process filters have allowed this instance to be placed, run the modifiers and post-process filters.
  293. for (const auto& id : processedIds)
  294. {
  295. ModifierRequestBus::Event(id, &ModifierRequestBus::Events::Execute, instanceData);
  296. }
  297. // run post-process filters on modified instance data
  298. if (!EvaluateFilters(processedIds, instanceData, FilterStage::PostProcess))
  299. {
  300. // Once a filter rejects the instance, no point in running the rest.
  301. return false;
  302. }
  303. // If we made it through all the filters successfully, this descriptor can claim this instance.
  304. return true;
  305. }
  306. bool SpawnerComponent::ClaimPosition(EntityIdStack& processedIds, const ClaimPoint& point, InstanceData& instanceData)
  307. {
  308. VEGETATION_PROFILE_FUNCTION_VERBOSE
  309. #if VEG_SPAWNER_ENABLE_CACHING
  310. {
  311. //return early if the point has already been rejected
  312. AZStd::lock_guard<decltype(m_cacheMutex)> cacheLock(m_cacheMutex);
  313. if (m_rejectedClaimCache.find(point.m_handle) != m_rejectedClaimCache.end())
  314. {
  315. return false;
  316. }
  317. //return early if an instance has already been generated and cached for this point
  318. auto acceptedClaimCacheItr = m_acceptedClaimCache.find(point.m_handle);
  319. if (acceptedClaimCacheItr != m_acceptedClaimCache.end())
  320. {
  321. instanceData = acceptedClaimCacheItr->second;
  322. instanceData.m_instanceId = InvalidInstanceId;
  323. return true;
  324. }
  325. }
  326. #endif
  327. // test shape bus as first pass to claim the point
  328. for (const auto& id : processedIds)
  329. {
  330. bool accepted = true;
  331. LmbrCentral::ShapeComponentRequestsBus::EventResult(accepted, id, &LmbrCentral::ShapeComponentRequestsBus::Events::IsPointInside, point.m_position);
  332. if (!accepted)
  333. {
  334. VEG_PROFILE_METHOD(DebugNotificationBus::TryQueueBroadcast(&DebugNotificationBus::Events::FilterInstance, instanceData.m_id, AZStd::string_view("ShapeFilter")));
  335. return false;
  336. }
  337. }
  338. //generate uvw sample coordinates
  339. DescriptorSelectorParams selectorParams;
  340. selectorParams.m_position = point.m_position;
  341. //copy the set of all selectable descriptors then remove any that don't pass the selection filter
  342. AZStd::lock_guard<decltype(m_selectableDescriptorMutex)> selectableDescriptorLock(m_selectableDescriptorMutex);
  343. m_selectedDescriptors = m_selectableDescriptorCache;
  344. for (const auto& id : processedIds)
  345. {
  346. DescriptorSelectorRequestBus::Event(id, &DescriptorSelectorRequestBus::Events::SelectDescriptors, selectorParams, m_selectedDescriptors);
  347. }
  348. for (DescriptorPtr descriptorPtr : m_selectedDescriptors)
  349. {
  350. if (ProcessInstance(processedIds, point, instanceData, descriptorPtr))
  351. {
  352. return true;
  353. }
  354. }
  355. // All the descriptors were filtered out, so don't claim the point.
  356. return false;
  357. }
  358. void SpawnerComponent::ClaimPositions(EntityIdStack& stackIds, ClaimContext& context)
  359. {
  360. AZ_PROFILE_FUNCTION(Vegetation);
  361. //reject entire spawner if there are inclusion tags to consider that don't exist in the context
  362. if (context.m_masks.HasValidTags() &&
  363. SurfaceData::HasValidTags(m_inclusiveTagsToConsider) &&
  364. !context.m_masks.HasAnyMatchingTags(m_inclusiveTagsToConsider))
  365. {
  366. VEG_PROFILE_METHOD(DebugNotificationBus::TryQueueBroadcast(&DebugNotificationBus::Events::MarkAreaRejectedByMask, GetEntityId()));
  367. return;
  368. }
  369. //see comments in PrepareToClaim
  370. EntityIdStack emptyIds;
  371. EntityIdStack& processedIds = m_configuration.m_inheritBehavior ? stackIds : emptyIds;
  372. EntityIdStackPusher stackPusher(processedIds, GetEntityId());
  373. InstanceData instanceData;
  374. instanceData.m_id = GetEntityId();
  375. instanceData.m_changeIndex = GetChangeIndex();
  376. size_t numAvailablePoints = context.m_availablePoints.size();
  377. for (size_t pointIndex = 0; pointIndex < numAvailablePoints; )
  378. {
  379. ClaimPoint& point = context.m_availablePoints[pointIndex];
  380. bool accepted = false;
  381. if (ClaimPosition(processedIds, point, instanceData))
  382. {
  383. // Check if an identical instance already exists for reuse
  384. if (context.m_existedCallback(point, instanceData))
  385. {
  386. accepted = true;
  387. }
  388. else
  389. {
  390. if (CreateInstance(point, instanceData))
  391. {
  392. accepted = true;
  393. //notify the caller that this claim succeeded so it can do any cleanup or registration
  394. context.m_createdCallback(point, instanceData);
  395. //only store the instance id after all claim logic executes in case prior claim and instance gets released
  396. AZStd::lock_guard<decltype(m_claimInstanceMappingMutex)> claimInstanceMappingMutexLock(m_claimInstanceMappingMutex);
  397. m_claimInstanceMapping[point.m_handle] = instanceData.m_instanceId;
  398. }
  399. }
  400. }
  401. if (accepted)
  402. {
  403. //Swap an available point from the end of the list
  404. AZStd::swap(point, context.m_availablePoints.at(numAvailablePoints - 1));
  405. --numAvailablePoints;
  406. #if VEG_SPAWNER_ENABLE_CACHING
  407. AZStd::lock_guard<decltype(m_cacheMutex)> cacheLock(m_cacheMutex);
  408. m_acceptedClaimCache[point.m_handle] = instanceData;
  409. m_rejectedClaimCache.erase(point.m_handle);
  410. #endif
  411. }
  412. else
  413. {
  414. UnclaimPosition(point.m_handle);
  415. ++pointIndex;
  416. #if VEG_SPAWNER_ENABLE_CACHING
  417. AZStd::lock_guard<decltype(m_cacheMutex)> cacheLock(m_cacheMutex);
  418. m_acceptedClaimCache.erase(point.m_handle);
  419. m_rejectedClaimCache.insert(point.m_handle);
  420. #endif
  421. }
  422. }
  423. //resize to remove all used points
  424. context.m_availablePoints.resize(numAvailablePoints);
  425. //release residual descriptors and asset references used this claim attempt
  426. AZStd::lock_guard<decltype(m_selectableDescriptorMutex)> selectableDescriptorLock(m_selectableDescriptorMutex);
  427. m_selectedDescriptors.clear();
  428. }
  429. void SpawnerComponent::UnclaimPosition(const ClaimHandle handle)
  430. {
  431. VEGETATION_PROFILE_FUNCTION_VERBOSE
  432. InstanceId instanceId = InvalidInstanceId;
  433. {
  434. AZStd::lock_guard<decltype(m_claimInstanceMappingMutex)> claimInstanceMappingMutexLock(m_claimInstanceMappingMutex);
  435. auto claimItr = m_claimInstanceMapping.find(handle);
  436. if (claimItr != m_claimInstanceMapping.end())
  437. {
  438. instanceId = claimItr->second;
  439. m_claimInstanceMapping.erase(claimItr);
  440. }
  441. }
  442. if (instanceId != InvalidInstanceId)
  443. {
  444. InstanceSystemRequestBus::Broadcast(&InstanceSystemRequestBus::Events::DestroyInstance, instanceId);
  445. }
  446. }
  447. AZ::Aabb SpawnerComponent::GetEncompassingAabb() const
  448. {
  449. VEGETATION_PROFILE_FUNCTION_VERBOSE
  450. AZ::Aabb bounds = AZ::Aabb::CreateNull();
  451. LmbrCentral::ShapeComponentRequestsBus::EventResult(bounds, GetEntityId(), &LmbrCentral::ShapeComponentRequestsBus::Events::GetEncompassingAabb);
  452. return bounds;
  453. }
  454. AZ::u32 SpawnerComponent::GetProductCount() const
  455. {
  456. AZStd::lock_guard<decltype(m_claimInstanceMappingMutex)> claimInstanceMappingMutexLock(m_claimInstanceMappingMutex);
  457. return static_cast<AZ::u32>(m_claimInstanceMapping.size());
  458. }
  459. void SpawnerComponent::OnCompositionChanged()
  460. {
  461. VEGETATION_PROFILE_FUNCTION_VERBOSE
  462. AreaComponentBase::OnCompositionChanged();
  463. #if VEG_SPAWNER_ENABLE_CACHING
  464. //wipe the cache when content changes
  465. AZStd::lock_guard<decltype(m_cacheMutex)> cacheLock(m_cacheMutex);
  466. m_acceptedClaimCache.clear();
  467. m_rejectedClaimCache.clear();
  468. #endif
  469. }
  470. void SpawnerComponent::DestroyAllInstances()
  471. {
  472. AZ_PROFILE_FUNCTION(Vegetation);
  473. ClaimInstanceMapping claimInstanceMapping;
  474. {
  475. AZStd::lock_guard<decltype(m_claimInstanceMappingMutex)> claimInstanceMappingMutexLock(m_claimInstanceMappingMutex);
  476. AZStd::swap(claimInstanceMapping, m_claimInstanceMapping);
  477. }
  478. for (const auto& claim : claimInstanceMapping)
  479. {
  480. const auto instanceId = claim.second;
  481. InstanceSystemRequestBus::Broadcast(&InstanceSystemRequestBus::Events::DestroyInstance, instanceId);
  482. }
  483. #if VEG_SPAWNER_ENABLE_CACHING
  484. //wipe the cache
  485. AZStd::lock_guard<decltype(m_cacheMutex)> cacheLock(m_cacheMutex);
  486. m_acceptedClaimCache.clear();
  487. m_rejectedClaimCache.clear();
  488. #endif
  489. }
  490. void SpawnerComponent::CalcInstanceDebugColor(const EntityIdStack& processedIds)
  491. {
  492. AreaDebugBus::Event(GetEntityId(), &AreaDebugBus::Events::ResetBlendedDebugDisplayData);
  493. for (const auto& id : processedIds)
  494. {
  495. AreaDebugDisplayData debugDisplayData;
  496. AreaDebugBus::EventResult(debugDisplayData, id, &AreaDebugBus::Events::GetBaseDebugDisplayData);
  497. AreaDebugBus::Event(GetEntityId(), &AreaDebugBus::Events::AddBlendedDebugDisplayData, debugDisplayData);
  498. }
  499. }
  500. AZ::u32 SpawnerComponent::GetAreaPriority() const
  501. {
  502. return m_configuration.m_priority;
  503. }
  504. void SpawnerComponent::SetAreaPriority(AZ::u32 priority)
  505. {
  506. m_configuration.m_priority = priority;
  507. LmbrCentral::DependencyNotificationBus::Event(GetEntityId(), &LmbrCentral::DependencyNotificationBus::Events::OnCompositionChanged);
  508. }
  509. AZ::u32 SpawnerComponent::GetAreaLayer() const
  510. {
  511. return m_configuration.m_layer;
  512. }
  513. void SpawnerComponent::SetAreaLayer(AZ::u32 layer)
  514. {
  515. m_configuration.m_layer = layer;
  516. LmbrCentral::DependencyNotificationBus::Event(GetEntityId(), &LmbrCentral::DependencyNotificationBus::Events::OnCompositionChanged);
  517. }
  518. AZ::u32 SpawnerComponent::GetAreaProductCount() const
  519. {
  520. return GetProductCount();
  521. }
  522. bool SpawnerComponent::GetInheritBehavior() const
  523. {
  524. return m_configuration.m_inheritBehavior;
  525. }
  526. void SpawnerComponent::SetInheritBehavior(bool value)
  527. {
  528. m_configuration.m_inheritBehavior = value;
  529. LmbrCentral::DependencyNotificationBus::Event(GetEntityId(), &LmbrCentral::DependencyNotificationBus::Events::OnCompositionChanged);
  530. }
  531. bool SpawnerComponent::GetAllowEmptyMeshes() const
  532. {
  533. return m_configuration.m_allowEmptyMeshes;
  534. }
  535. void SpawnerComponent::SetAllowEmptyMeshes(bool value)
  536. {
  537. m_configuration.m_allowEmptyMeshes = value;
  538. LmbrCentral::DependencyNotificationBus::Event(GetEntityId(), &LmbrCentral::DependencyNotificationBus::Events::OnCompositionChanged);
  539. }
  540. FilterStage SpawnerComponent::GetFilterStage() const
  541. {
  542. return m_configuration.m_filterStage;
  543. }
  544. void SpawnerComponent::SetFilterStage(FilterStage filterStage)
  545. {
  546. m_configuration.m_filterStage = filterStage;
  547. LmbrCentral::DependencyNotificationBus::Event(GetEntityId(), &LmbrCentral::DependencyNotificationBus::Events::OnCompositionChanged);
  548. }
  549. }