AreaSystemComponent.cpp 88 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831
  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 "AreaSystemComponent.h"
  10. #include <Vegetation/Ebuses/AreaNotificationBus.h>
  11. #include <Vegetation/Ebuses/AreaRequestBus.h>
  12. #include <Vegetation/Ebuses/InstanceSystemRequestBus.h>
  13. #include <Vegetation/Ebuses/AreaInfoBus.h>
  14. #include <Vegetation/Ebuses/DebugNotificationBus.h>
  15. #include <Vegetation/Ebuses/DebugSystemDataBus.h>
  16. #include <SurfaceData/SurfaceDataSystemRequestBus.h>
  17. #include <SurfaceData/Utility/SurfaceDataUtility.h>
  18. #include <AzCore/Debug/Profiler.h>
  19. #include <AzCore/RTTI/BehaviorContext.h>
  20. #include <AzCore/Serialization/EditContext.h>
  21. #include <AzCore/Serialization/SerializeContext.h>
  22. #include <AzCore/Jobs/JobFunction.h>
  23. #include <AzCore/std/chrono/chrono.h>
  24. #include <AzCore/std/sort.h>
  25. #include <AzCore/std/utils.h>
  26. #include <AzCore/Component/TransformBus.h>
  27. #include <AzFramework/Components/CameraBus.h>
  28. #ifdef VEGETATION_EDITOR
  29. #include <AzToolsFramework/API/EditorCameraBus.h>
  30. #endif
  31. #include <ISystem.h>
  32. #include <cinttypes>
  33. namespace Vegetation
  34. {
  35. namespace AreaSystemUtil
  36. {
  37. template <typename T>
  38. void hash_combine_64(uint64_t& seed, T const& v)
  39. {
  40. AZStd::hash<T> hasher;
  41. seed ^= hasher(v) + 0x9e3779b97f4a7c13LL + (seed << 12) + (seed >> 4);
  42. }
  43. static bool UpdateVersion([[maybe_unused]] AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement)
  44. {
  45. if (classElement.GetVersion() < 4)
  46. {
  47. classElement.RemoveElementByName(AZ_CRC_CE("ThreadSleepTimeMs"));
  48. }
  49. return true;
  50. }
  51. }
  52. //////////////////////////////////////////////////////////////////////////
  53. // ViewRect
  54. inline bool AreaSystemComponent::ViewRect::IsInside(const SectorId& sector) const
  55. {
  56. const int inX = sector.first;
  57. const int inY = sector.second;
  58. return inX >= GetMinXSector() && inX <= GetMaxXSector() && inY >= GetMinYSector() && inY <= GetMaxYSector();
  59. }
  60. AreaSystemComponent::ViewRect AreaSystemComponent::ViewRect::Overlap(const ViewRect& b) const
  61. {
  62. ViewRect o;
  63. o.m_x = m_x > b.m_x ? m_x : b.m_x;
  64. o.m_y = m_y > b.m_y ? m_y : b.m_y;
  65. o.m_width = m_x + m_width > b.m_x + b.m_width ? b.m_x + b.m_width : m_x + m_width;
  66. o.m_height = m_y + m_height > b.m_y + b.m_height ? b.m_y + b.m_height : m_y + m_height;
  67. o.m_width -= o.m_x;
  68. o.m_height -= o.m_y;
  69. return o;
  70. }
  71. bool AreaSystemComponent::ViewRect::operator==(const ViewRect& b)
  72. {
  73. return m_x == b.m_x && m_y == b.m_y && m_width == b.m_width && m_height == b.m_height;
  74. }
  75. size_t AreaSystemComponent::ViewRect::GetNumSectors() const
  76. {
  77. return static_cast<size_t>(m_height * m_width);
  78. }
  79. bool AreaSystemComponent::ViewRect::operator!=(const ViewRect& b)
  80. {
  81. return m_x != b.m_x || m_y != b.m_y || m_width != b.m_width || m_height != b.m_height;
  82. }
  83. //////////////////////////////////////////////////////////////////////////
  84. // DirtySectors
  85. void AreaSystemComponent::DirtySectors::MarkDirty(const SectorId& sector)
  86. {
  87. m_dirtySet.insert(AZStd::move(sector));
  88. }
  89. void AreaSystemComponent::DirtySectors::MarkAllDirty()
  90. {
  91. m_allSectorsDirty = true;
  92. }
  93. void AreaSystemComponent::DirtySectors::Clear()
  94. {
  95. m_dirtySet.clear();
  96. m_allSectorsDirty = false;
  97. }
  98. bool AreaSystemComponent::DirtySectors::IsDirty(const SectorId& sector) const
  99. {
  100. return m_allSectorsDirty ||
  101. (!m_dirtySet.empty() && (m_dirtySet.find(sector) != m_dirtySet.end()));
  102. }
  103. //////////////////////////////////////////////////////////////////////////
  104. // AreaSystemConfig
  105. // These limitations are somewhat arbitrary. It's possible to select combinations of larger values than these that will work successfully.
  106. // However, these values are also large enough that going beyond them is extremely likely to cause problems.
  107. const int AreaSystemConfig::s_maxViewRectangleSize = 128;
  108. const int AreaSystemConfig::s_maxSectorDensity = 64;
  109. const int AreaSystemConfig::s_maxSectorSizeInMeters = 1024;
  110. const int64_t AreaSystemConfig::s_maxVegetationInstances = 2 * 1024 * 1024;
  111. const int AreaSystemConfig::s_maxInstancesPerMeter = 16;
  112. void AreaSystemConfig::Reflect(AZ::ReflectContext* context)
  113. {
  114. AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
  115. if (serialize)
  116. {
  117. serialize->Class<AreaSystemConfig, AZ::ComponentConfig>()
  118. ->Version(4, &AreaSystemUtil::UpdateVersion)
  119. ->Field("ViewRectangleSize", &AreaSystemConfig::m_viewRectangleSize)
  120. ->Field("SectorDensity", &AreaSystemConfig::m_sectorDensity)
  121. ->Field("SectorSizeInMeters", &AreaSystemConfig::m_sectorSizeInMeters)
  122. ->Field("ThreadProcessingIntervalMs", &AreaSystemConfig::m_threadProcessingIntervalMs)
  123. ->Field("SectorSearchPadding", &AreaSystemConfig::m_sectorSearchPadding)
  124. ->Field("SectorPointSnapMode", &AreaSystemConfig::m_sectorPointSnapMode)
  125. ;
  126. AZ::EditContext* edit = serialize->GetEditContext();
  127. if (edit)
  128. {
  129. edit->Class<AreaSystemConfig>(
  130. "Vegetation Area System Config", "Handles the placement and removal of vegetation instance based on the vegetation area component rules")
  131. ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  132. ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
  133. ->DataElement(AZ::Edit::UIHandlers::Default, &AreaSystemConfig::m_viewRectangleSize, "View Area Grid Size", "The number of sectors (per-side) of a managed grid in a scrolling view centered around the camera.")
  134. ->Attribute(AZ::Edit::Attributes::ChangeValidate, &AreaSystemConfig::ValidateViewArea)
  135. ->Attribute(AZ::Edit::Attributes::Min, 1)
  136. ->Attribute(AZ::Edit::Attributes::Max, s_maxViewRectangleSize)
  137. ->DataElement(AZ::Edit::UIHandlers::Default, &AreaSystemConfig::m_sectorDensity, "Sector Point Density", "The number of equally-spaced vegetation instance grid placement points (per-side) within a sector")
  138. ->Attribute(AZ::Edit::Attributes::ChangeValidate, &AreaSystemConfig::ValidateSectorDensity)
  139. ->Attribute(AZ::Edit::Attributes::Min, 1)
  140. ->Attribute(AZ::Edit::Attributes::Max, s_maxSectorDensity)
  141. ->DataElement(AZ::Edit::UIHandlers::Default, &AreaSystemConfig::m_sectorSizeInMeters, "Sector Size In Meters", "The size in meters (per-side) of each sector.")
  142. ->Attribute(AZ::Edit::Attributes::ChangeValidate, &AreaSystemConfig::ValidateSectorSize)
  143. ->Attribute(AZ::Edit::Attributes::Min, 1)
  144. ->Attribute(AZ::Edit::Attributes::Max, s_maxSectorSizeInMeters)
  145. ->DataElement(AZ::Edit::UIHandlers::Default, &AreaSystemConfig::m_threadProcessingIntervalMs, "Thread Processing Interval", "The delay (in milliseconds) between processing queued thread tasks.")
  146. ->Attribute(AZ::Edit::Attributes::Min, 0)
  147. ->Attribute(AZ::Edit::Attributes::Max, 5000)
  148. ->DataElement(AZ::Edit::UIHandlers::Default, &AreaSystemConfig::m_sectorSearchPadding, "Sector Search Padding", "Increases the search radius for surrounding sectors when enumerating instances.")
  149. ->Attribute(AZ::Edit::Attributes::Min, 0)
  150. ->Attribute(AZ::Edit::Attributes::Max, 2)
  151. ->DataElement(AZ::Edit::UIHandlers::ComboBox, &AreaSystemConfig::m_sectorPointSnapMode, "Sector Point Snap Mode", "Controls whether vegetation placement points are located at the corner or the center of the cell.")
  152. ->EnumAttribute(SnapMode::Corner, "Corner")
  153. ->EnumAttribute(SnapMode::Center, "Center")
  154. ;
  155. }
  156. }
  157. if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
  158. {
  159. behaviorContext->Class<AreaSystemConfig>()
  160. ->Attribute(AZ::Script::Attributes::Category, "Vegetation")
  161. ->Constructor()
  162. ->Property("viewRectangleSize", BehaviorValueProperty(&AreaSystemConfig::m_viewRectangleSize))
  163. ->Property("sectorDensity", BehaviorValueProperty(&AreaSystemConfig::m_sectorDensity))
  164. ->Property("sectorSizeInMeters", BehaviorValueProperty(&AreaSystemConfig::m_sectorSizeInMeters))
  165. ->Property("threadProcessingIntervalMs", BehaviorValueProperty(&AreaSystemConfig::m_threadProcessingIntervalMs))
  166. ->Property("sectorPointSnapMode",
  167. [](AreaSystemConfig* config) { return static_cast<AZ::u8>(config->m_sectorPointSnapMode); },
  168. [](AreaSystemConfig* config, const AZ::u8& i) { config->m_sectorPointSnapMode = static_cast<SnapMode>(i); })
  169. ;
  170. }
  171. }
  172. AZ::Outcome<void, AZStd::string> AreaSystemConfig::ValidateViewArea(void* newValue, const AZ::Uuid& valueType)
  173. {
  174. if (azrtti_typeid<int>() != valueType)
  175. {
  176. AZ_Assert(false, "Unexpected value type");
  177. return AZ::Failure(AZStd::string("Unexpectedly received a non-int type for the View Area Grid Size!"));
  178. }
  179. int viewRectangleSize = *static_cast<int*>(newValue);
  180. const int instancesPerSector = m_sectorDensity * m_sectorDensity;
  181. const int totalSectors = viewRectangleSize * viewRectangleSize;
  182. int64_t totalInstances = instancesPerSector * totalSectors;
  183. if (totalInstances > s_maxVegetationInstances)
  184. {
  185. return AZ::Failure(
  186. AZStd::string::format("The combination of View Area Grid Size and Sector Point Density will create %" PRId64 " instances. Only a max of %" PRId64 " instances is allowed.",
  187. totalInstances, s_maxVegetationInstances));
  188. }
  189. return AZ::Success();
  190. }
  191. AZ::Outcome<void, AZStd::string> AreaSystemConfig::ValidateSectorDensity(void* newValue, const AZ::Uuid& valueType)
  192. {
  193. if (azrtti_typeid<int>() != valueType)
  194. {
  195. AZ_Assert(false, "Unexpected value type");
  196. return AZ::Failure(AZStd::string("Unexpectedly received a non-int type for the Sector Point Density!"));
  197. }
  198. int sectorDensity = *static_cast<int*>(newValue);
  199. const int instancesPerSector = sectorDensity * sectorDensity;
  200. const int totalSectors = m_viewRectangleSize * m_viewRectangleSize;
  201. int64_t totalInstances = instancesPerSector * totalSectors;
  202. if (totalInstances >= s_maxVegetationInstances)
  203. {
  204. return AZ::Failure(
  205. AZStd::string::format("The combination of View Area Grid Size and Sector Point Density will create %" PRId64 " instances. Only a max of %" PRId64 " instances is allowed.",
  206. totalInstances, s_maxVegetationInstances));
  207. }
  208. const float instancesPerMeter = static_cast<float>(sectorDensity) / static_cast<float>(m_sectorSizeInMeters);
  209. if (instancesPerMeter > s_maxInstancesPerMeter)
  210. {
  211. return AZ::Failure(AZStd::string::format("The combination of Sector Point Density and Sector Size in Meters will create %.1f instances per meter. Only a max of %d instances per meter is allowed.",
  212. instancesPerMeter, s_maxInstancesPerMeter));
  213. }
  214. return AZ::Success();
  215. }
  216. AZ::Outcome<void, AZStd::string> AreaSystemConfig::ValidateSectorSize(void* newValue, const AZ::Uuid& valueType)
  217. {
  218. if (azrtti_typeid<int>() != valueType)
  219. {
  220. AZ_Assert(false, "Unexpected value type");
  221. return AZ::Failure(AZStd::string("Unexpectedly received a non-int type for the Sector Size In Meters!"));
  222. }
  223. int sectorSizeInMeters = *static_cast<int*>(newValue);
  224. const float instancesPerMeter = static_cast<float>(m_sectorDensity) / static_cast<float>(sectorSizeInMeters);
  225. if (instancesPerMeter > s_maxInstancesPerMeter)
  226. {
  227. return AZ::Failure(AZStd::string::format("The combination of Sector Point Density and Sector Size in Meters will create %.1f instances per meter. Only a max of %d instances per meter is allowed.",
  228. instancesPerMeter, s_maxInstancesPerMeter));
  229. }
  230. return AZ::Success();
  231. }
  232. //////////////////////////////////////////////////////////////////////////
  233. // AreaSystemComponent
  234. void AreaSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services)
  235. {
  236. services.push_back(AZ_CRC_CE("VegetationAreaSystemService"));
  237. }
  238. void AreaSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& services)
  239. {
  240. services.push_back(AZ_CRC_CE("VegetationAreaSystemService"));
  241. }
  242. void AreaSystemComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& services)
  243. {
  244. services.push_back(AZ_CRC_CE("VegetationDebugSystemService"));
  245. services.push_back(AZ_CRC_CE("VegetationInstanceSystemService"));
  246. services.push_back(AZ_CRC_CE("SurfaceDataSystemService"));
  247. }
  248. void AreaSystemComponent::Reflect(AZ::ReflectContext* context)
  249. {
  250. InstanceData::Reflect(context);
  251. AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
  252. if (serialize)
  253. {
  254. serialize->Class<AreaSystemComponent, AZ::Component>()
  255. ->Version(0)
  256. ->Field("Configuration", &AreaSystemComponent::m_configuration)
  257. ;
  258. if (AZ::EditContext* editContext = serialize->GetEditContext())
  259. {
  260. editContext->Class<AreaSystemComponent>("Vegetation Area System", "Manages registration and processing of vegetation area entities")
  261. ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  262. ->Attribute(AZ::Edit::Attributes::Category, "Vegetation")
  263. ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
  264. ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/")
  265. ->DataElement(0, &AreaSystemComponent::m_configuration, "Configuration", "")
  266. ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
  267. ;
  268. }
  269. }
  270. AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context);
  271. if (behaviorContext)
  272. {
  273. behaviorContext->EBus<AreaSystemRequestBus>("AreaSystemRequestBus")
  274. ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
  275. ->Attribute(AZ::Script::Attributes::Category, "AreaSystem")
  276. ->Attribute(AZ::Script::Attributes::Module, "areasystem")
  277. ->Event("GetInstanceCountInAabb", &AreaSystemRequests::GetInstanceCountInAabb)
  278. ->Event("GetInstancesInAabb", &AreaSystemRequests::GetInstancesInAabb)
  279. ;
  280. }
  281. }
  282. AreaSystemComponent::AreaSystemComponent(const AreaSystemConfig& configuration)
  283. : m_configuration(configuration)
  284. {
  285. }
  286. void AreaSystemComponent::Init()
  287. {
  288. }
  289. void AreaSystemComponent::Activate()
  290. {
  291. // Wait for any lingering vegetation thread work to complete if necessary. (This should never actually occur)
  292. AZ_Assert(m_threadData.m_vegetationThreadState == PersistentThreadData::VegetationThreadState::Stopped,
  293. "Vegetation thread was still active even though AreaSystemComponent was deactivated.");
  294. AZStd::lock_guard<decltype(m_threadData.m_vegetationThreadMutex)> lockTasks(m_threadData.m_vegetationThreadMutex);
  295. m_threadData.m_vegetationThreadState = PersistentThreadData::VegetationThreadState::Stopped;
  296. m_threadData.m_vegetationDataSyncState = PersistentThreadData::VegetationDataSyncState::Synchronized;
  297. m_system = GetISystem();
  298. m_worldToSector = 1.0f / m_configuration.m_sectorSizeInMeters;
  299. // We initialize our vegetation threadData state here to ensure it gets recalculated the next time the thread runs.
  300. m_threadData.Init();
  301. AZ::TickBus::Handler::BusConnect();
  302. AreaSystemRequestBus::Handler::BusConnect();
  303. GradientSignal::SectorDataRequestBus::Handler::BusConnect();
  304. SystemConfigurationRequestBus::Handler::BusConnect();
  305. CrySystemEventBus::Handler::BusConnect();
  306. AzFramework::Terrain::TerrainDataNotificationBus::Handler::BusConnect();
  307. SurfaceData::SurfaceDataSystemNotificationBus::Handler::BusConnect();
  308. m_vegTasks.FetchDebugData();
  309. }
  310. void AreaSystemComponent::Deactivate()
  311. {
  312. // Interrupt vegetation worker; deactivation deletes all vegetation, so there's no need to process updates.
  313. m_threadData.InterruptVegetationThread();
  314. //wait for the vegetation thread work to complete
  315. AZStd::lock_guard<decltype(m_threadData.m_vegetationThreadMutex)> lockTasks(m_threadData.m_vegetationThreadMutex);
  316. m_threadData.m_vegetationThreadState = PersistentThreadData::VegetationThreadState::Stopped;
  317. m_threadData.m_vegetationDataSyncState = PersistentThreadData::VegetationDataSyncState::Synchronized;
  318. AZ::TickBus::Handler::BusDisconnect();
  319. AreaSystemRequestBus::Handler::BusDisconnect();
  320. GradientSignal::SectorDataRequestBus::Handler::BusDisconnect();
  321. SystemConfigurationRequestBus::Handler::BusDisconnect();
  322. CrySystemEventBus::Handler::BusDisconnect();
  323. AzFramework::Terrain::TerrainDataNotificationBus::Handler::BusDisconnect();
  324. SurfaceData::SurfaceDataSystemNotificationBus::Handler::BusDisconnect();
  325. // Clear sector data and any lingering vegetation thread state
  326. m_vegTasks.ClearSectors();
  327. m_threadData.Init();
  328. InstanceSystemRequestBus::Broadcast(&InstanceSystemRequestBus::Events::DestroyAllInstances);
  329. InstanceSystemRequestBus::Broadcast(&InstanceSystemRequestBus::Events::Cleanup);
  330. if (m_system)
  331. {
  332. m_system->GetISystemEventDispatcher()->RemoveListener(this);
  333. m_system = nullptr;
  334. }
  335. }
  336. bool AreaSystemComponent::ReadInConfig(const AZ::ComponentConfig* baseConfig)
  337. {
  338. if (auto config = azrtti_cast<const AreaSystemConfig*>(baseConfig))
  339. {
  340. m_configuration = *config;
  341. return true;
  342. }
  343. return false;
  344. }
  345. bool AreaSystemComponent::WriteOutConfig(AZ::ComponentConfig* outBaseConfig) const
  346. {
  347. if (auto config = azrtti_cast<AreaSystemConfig*>(outBaseConfig))
  348. {
  349. if (m_configDirty)
  350. {
  351. *config = m_pendingConfigUpdate;
  352. }
  353. else
  354. {
  355. *config = m_configuration;
  356. }
  357. return true;
  358. }
  359. return false;
  360. }
  361. void AreaSystemComponent::UpdateSystemConfig(const AZ::ComponentConfig* baseConfig)
  362. {
  363. if (const auto config = azrtti_cast<const AreaSystemConfig*>(baseConfig))
  364. {
  365. if ((!m_configDirty && m_configuration == *config) || (m_configDirty && m_pendingConfigUpdate == *config))
  366. {
  367. return;
  368. }
  369. m_configDirty = true;
  370. m_pendingConfigUpdate = *config;
  371. }
  372. }
  373. void AreaSystemComponent::GetSystemConfig(AZ::ComponentConfig* outBaseConfig) const
  374. {
  375. WriteOutConfig(outBaseConfig);
  376. }
  377. bool AreaSystemComponent::ApplyPendingConfigChanges()
  378. {
  379. if (m_configDirty)
  380. {
  381. ReleaseWithoutCleanup();
  382. if (m_configuration.m_threadProcessingIntervalMs != m_pendingConfigUpdate.m_threadProcessingIntervalMs)
  383. {
  384. m_vegetationThreadTaskTimer = 0.0f;
  385. }
  386. ReadInConfig(&m_pendingConfigUpdate);
  387. m_worldToSector = 1.0f / m_configuration.m_sectorSizeInMeters;
  388. RefreshAllAreas();
  389. GradientSignal::SectorDataNotificationBus::Broadcast(&GradientSignal::SectorDataNotificationBus::Events::OnSectorDataConfigurationUpdated);
  390. m_configDirty = false;
  391. return true;
  392. }
  393. else
  394. {
  395. return false;
  396. }
  397. }
  398. void AreaSystemComponent::RegisterArea(AZ::EntityId areaId, AZ::u32 layer, AZ::u32 priority, const AZ::Aabb& bounds)
  399. {
  400. if (!bounds.IsValid())
  401. {
  402. AZ_Assert(false, "Vegetation Area registered with an invalid AABB.");
  403. }
  404. m_vegTasks.QueueVegetationTask([areaId, layer, priority, bounds](UpdateContext* context, PersistentThreadData* threadData, VegetationThreadTasks* vegTasks)
  405. {
  406. auto& area = threadData->m_globalVegetationAreaMap[areaId];
  407. area.m_id = areaId;
  408. area.m_layer = layer;
  409. area.m_priority = priority;
  410. area.m_bounds = bounds;
  411. AreaNotificationBus::Event(area.m_id, &AreaNotificationBus::Events::OnAreaRegistered);
  412. const auto& cachedMainThreadData = context->GetCachedMainThreadData();
  413. vegTasks->MarkDirtySectors(area.m_bounds, threadData->m_dirtySectorContents,
  414. cachedMainThreadData.m_worldToSector, cachedMainThreadData.m_currViewRect);
  415. threadData->m_activeAreasDirty = true;
  416. });
  417. }
  418. void AreaSystemComponent::UnregisterArea(AZ::EntityId areaId)
  419. {
  420. m_vegTasks.QueueVegetationTask([areaId](UpdateContext* context, PersistentThreadData* threadData, VegetationThreadTasks* vegTasks)
  421. {
  422. auto itArea = threadData->m_globalVegetationAreaMap.find(areaId);
  423. if (itArea != threadData->m_globalVegetationAreaMap.end())
  424. {
  425. const auto& area = itArea->second;
  426. AreaNotificationBus::Event(area.m_id, &AreaNotificationBus::Events::OnAreaUnregistered);
  427. AreaNotificationBus::Event(area.m_id, &AreaNotificationBus::Events::OnAreaDisconnect);
  428. const auto& cachedMainThreadData = context->GetCachedMainThreadData();
  429. vegTasks->AddUnregisteredVegetationArea(area, cachedMainThreadData.m_worldToSector, cachedMainThreadData.m_currViewRect);
  430. vegTasks->MarkDirtySectors(area.m_bounds, threadData->m_dirtySectorContents,
  431. cachedMainThreadData.m_worldToSector, cachedMainThreadData.m_currViewRect);
  432. threadData->m_globalVegetationAreaMap.erase(itArea);
  433. threadData->m_activeAreasDirty = true;
  434. }
  435. });
  436. }
  437. void AreaSystemComponent::RefreshArea(AZ::EntityId areaId, AZ::u32 layer, AZ::u32 priority, const AZ::Aabb& bounds)
  438. {
  439. if (!bounds.IsValid())
  440. {
  441. AZ_Assert(false, "Vegetation Area refreshed with an invalid AABB.");
  442. }
  443. m_vegTasks.QueueVegetationTask([areaId, layer, priority, bounds](UpdateContext* context, PersistentThreadData* threadData, VegetationThreadTasks* vegTasks)
  444. {
  445. auto itArea = threadData->m_globalVegetationAreaMap.find(areaId);
  446. if (itArea != threadData->m_globalVegetationAreaMap.end())
  447. {
  448. auto& area = itArea->second;
  449. const auto& cachedMainThreadData = context->GetCachedMainThreadData();
  450. vegTasks->MarkDirtySectors(area.m_bounds, threadData->m_dirtySectorContents,
  451. cachedMainThreadData.m_worldToSector, cachedMainThreadData.m_currViewRect);
  452. area.m_layer = layer;
  453. area.m_priority = priority;
  454. area.m_bounds = bounds;
  455. AreaNotificationBus::Event(area.m_id, &AreaNotificationBus::Events::OnAreaRefreshed);
  456. vegTasks->MarkDirtySectors(area.m_bounds, threadData->m_dirtySectorContents,
  457. cachedMainThreadData.m_worldToSector, cachedMainThreadData.m_currViewRect);
  458. threadData->m_activeAreasDirty = true;
  459. }
  460. });
  461. }
  462. void AreaSystemComponent::RefreshAllAreas()
  463. {
  464. m_vegTasks.QueueVegetationTask([](UpdateContext* context, PersistentThreadData* threadData, VegetationThreadTasks* vegTasks)
  465. {
  466. for (auto& entry : threadData->m_globalVegetationAreaMap)
  467. {
  468. auto& area = entry.second;
  469. area.m_layer = {};
  470. area.m_priority = {};
  471. area.m_bounds = AZ::Aabb::CreateNull();
  472. AreaInfoBus::EventResult(area.m_layer, area.m_id, &AreaInfoBus::Events::GetLayer);
  473. AreaInfoBus::EventResult(area.m_priority, area.m_id, &AreaInfoBus::Events::GetPriority);
  474. AreaInfoBus::EventResult(area.m_bounds, area.m_id, &AreaInfoBus::Events::GetEncompassingAabb);
  475. AreaNotificationBus::Event(area.m_id, &AreaNotificationBus::Events::OnAreaRefreshed);
  476. }
  477. // Set all existing sectors as needing to be rebuilt.
  478. const auto& cachedMainThreadData = context->GetCachedMainThreadData();
  479. vegTasks->MarkDirtySectors(AZ::Aabb::CreateNull(), threadData->m_dirtySectorContents,
  480. cachedMainThreadData.m_worldToSector, cachedMainThreadData.m_currViewRect);
  481. vegTasks->MarkDirtySectors(AZ::Aabb::CreateNull(), threadData->m_dirtySectorSurfacePoints,
  482. cachedMainThreadData.m_worldToSector, cachedMainThreadData.m_currViewRect);
  483. });
  484. }
  485. void AreaSystemComponent::ClearAllAreas()
  486. {
  487. // Interrupt any work that's currently being done on the vegetation thread and destroy all vegetation instances
  488. ReleaseWithoutCleanup();
  489. // Queue a refresh of all the areas
  490. RefreshAllAreas();
  491. // Reset our timer for checking the vegetation queue for more work to ensure we process this immediately.
  492. m_vegetationThreadTaskTimer = 0.0f;
  493. }
  494. void AreaSystemComponent::MuteArea(AZ::EntityId areaId)
  495. {
  496. m_vegTasks.QueueVegetationTask([areaId](UpdateContext* /*context*/, PersistentThreadData* threadData, VegetationThreadTasks* /*vegTasks*/)
  497. {
  498. threadData->m_ignoredVegetationAreaSet.insert(areaId);
  499. threadData->m_activeAreasDirty = true;
  500. });
  501. }
  502. void AreaSystemComponent::UnmuteArea(AZ::EntityId areaId)
  503. {
  504. m_vegTasks.QueueVegetationTask([areaId](UpdateContext* /*context*/, PersistentThreadData* threadData, VegetationThreadTasks* /*vegTasks*/)
  505. {
  506. threadData->m_ignoredVegetationAreaSet.erase(areaId);
  507. threadData->m_activeAreasDirty = true;
  508. });
  509. }
  510. void AreaSystemComponent::OnSurfaceChanged(
  511. [[maybe_unused]] const AZ::EntityId& entityId,
  512. const AZ::Aabb& oldBounds,
  513. const AZ::Aabb& newBounds,
  514. [[maybe_unused]] const SurfaceData::SurfaceTagSet& changedSurfaceTags)
  515. {
  516. m_vegTasks.QueueVegetationTask([oldBounds, newBounds](UpdateContext* context, PersistentThreadData* threadData, VegetationThreadTasks* vegTasks)
  517. {
  518. const auto& cachedMainThreadData = context->GetCachedMainThreadData();
  519. // Mark the surface area prior to the surface data change as dirty
  520. vegTasks->MarkDirtySectors(oldBounds, threadData->m_dirtySectorContents,
  521. cachedMainThreadData.m_worldToSector, cachedMainThreadData.m_currViewRect);
  522. vegTasks->MarkDirtySectors(oldBounds, threadData->m_dirtySectorSurfacePoints,
  523. cachedMainThreadData.m_worldToSector, cachedMainThreadData.m_currViewRect);
  524. // Mark the surface area *after* the surface data change as dirty
  525. vegTasks->MarkDirtySectors(newBounds, threadData->m_dirtySectorContents,
  526. cachedMainThreadData.m_worldToSector, cachedMainThreadData.m_currViewRect);
  527. vegTasks->MarkDirtySectors(newBounds, threadData->m_dirtySectorSurfacePoints,
  528. cachedMainThreadData.m_worldToSector, cachedMainThreadData.m_currViewRect);
  529. });
  530. }
  531. void AreaSystemComponent::EnumerateInstancesInOverlappingSectors(const AZ::Aabb& bounds, AreaSystemEnumerateCallback callback) const
  532. {
  533. VEGETATION_PROFILE_FUNCTION_VERBOSE
  534. if (!bounds.IsValid())
  535. {
  536. return;
  537. }
  538. // Get the minimum sector that overlaps the bounds, expanded outward based on sectorSearchPadding.
  539. const SectorId minSector = GetSectorId(bounds.GetMin(), m_worldToSector);
  540. AZ::Aabb minBounds = m_vegTasks.GetSectorBounds(SectorId(minSector.first - m_configuration.m_sectorSearchPadding,
  541. minSector.second - m_configuration.m_sectorSearchPadding),
  542. m_configuration.m_sectorSizeInMeters);
  543. // Get the maximum sector that overlaps the bounds, expanded outward based on sectorSearchPadding.
  544. const SectorId maxSector = GetSectorId(bounds.GetMax(), m_worldToSector);
  545. AZ::Aabb maxBounds = m_vegTasks.GetSectorBounds(SectorId(maxSector.first + m_configuration.m_sectorSearchPadding,
  546. maxSector.second + m_configuration.m_sectorSearchPadding),
  547. m_configuration.m_sectorSizeInMeters);
  548. // Use the expanded bounds to enumerate through all instances.
  549. AZ::Aabb expandedBounds(minBounds);
  550. expandedBounds.AddAabb(maxBounds);
  551. EnumerateInstancesInAabb(expandedBounds, callback);
  552. }
  553. void AreaSystemComponent::EnumerateInstancesInAabb(const AZ::Aabb& bounds, AreaSystemEnumerateCallback callback) const
  554. {
  555. VEGETATION_PROFILE_FUNCTION_VERBOSE
  556. if (!bounds.IsValid())
  557. {
  558. return;
  559. }
  560. const SectorId minSector = GetSectorId(bounds.GetMin(), m_worldToSector);
  561. const int minX = minSector.first;
  562. const int minY = minSector.second;
  563. const SectorId maxSector = GetSectorId(bounds.GetMax(), m_worldToSector);
  564. const int maxX = maxSector.first;
  565. const int maxY = maxSector.second;
  566. // Lock the rolling window mutex for the entire enumerate to ensure that our set of sectors doesn't change during the loops.
  567. AZStd::lock_guard<decltype(m_vegTasks.m_sectorRollingWindowMutex)> lock(m_vegTasks.m_sectorRollingWindowMutex);
  568. for (int currY = minY; currY <= maxY; ++currY)
  569. {
  570. for (int currX = minX; currX <= maxX; ++currX)
  571. {
  572. const SectorInfo* sectorInfo = m_vegTasks.GetSector(SectorId(currX, currY));
  573. if (sectorInfo) // manual sector id's can be outside the active area
  574. {
  575. for (const auto& claimPair : sectorInfo->m_claimedWorldPoints)
  576. {
  577. const auto& instanceData = claimPair.second;
  578. if (bounds.Contains(instanceData.m_position))
  579. {
  580. if (callback(instanceData) != AreaSystemEnumerateCallbackResult::KeepEnumerating)
  581. {
  582. return;
  583. }
  584. }
  585. }
  586. }
  587. }
  588. }
  589. }
  590. AZStd::size_t AreaSystemComponent::GetInstanceCountInAabb(const AZ::Aabb& bounds) const
  591. {
  592. AZStd::size_t result = 0;
  593. EnumerateInstancesInAabb(bounds, [&result](const auto&)
  594. {
  595. ++result;
  596. return AreaSystemEnumerateCallbackResult::KeepEnumerating;
  597. });
  598. return result;
  599. }
  600. AZStd::vector<Vegetation::InstanceData> AreaSystemComponent::GetInstancesInAabb(const AZ::Aabb& bounds) const
  601. {
  602. AZStd::vector<Vegetation::InstanceData> instanceList;
  603. EnumerateInstancesInAabb(bounds, [&instanceList](const auto& instance)
  604. {
  605. instanceList.push_back(instance);
  606. return AreaSystemEnumerateCallbackResult::KeepEnumerating;
  607. });
  608. return instanceList;
  609. }
  610. void AreaSystemComponent::GetPointsPerMeter(float& value) const
  611. {
  612. if (m_configuration.m_sectorDensity <= 0 || m_configuration.m_sectorSizeInMeters <= 0.0f)
  613. {
  614. value = 1.0f;
  615. }
  616. else
  617. {
  618. value = static_cast<float>(m_configuration.m_sectorDensity) / m_configuration.m_sectorSizeInMeters;
  619. }
  620. }
  621. void AreaSystemComponent::OnTick(float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
  622. {
  623. AZ_PROFILE_FUNCTION(Entity);
  624. if (m_configuration.m_sectorSizeInMeters < 0)
  625. {
  626. m_configuration.m_sectorSizeInMeters = 1;
  627. }
  628. m_worldToSector = 1.0f / m_configuration.m_sectorSizeInMeters;
  629. m_vegetationThreadTaskTimer -= deltaTime;
  630. // Check to see if any vegetation data has changed since last tick, and if so, offload the updates to a vegetation thread.
  631. // - If the thread is currently stopped, check for data changes and start up the thread if changes are detected.
  632. // - If the thread has an interrupt requested, wait for the interrupt to stop the thread before checking and potentially running again.
  633. // - If the thread is currently running, only update if the data on the vegetation thread is currently synced with this thread.
  634. // If the state is currently Updating or Dirty, wait for the vegetation thread to pick up the changes before trying to update the
  635. // data again to avoid redundant mutex locks or the potential for mismatched state.
  636. if ((m_threadData.m_vegetationThreadState == PersistentThreadData::VegetationThreadState::Stopped) ||
  637. ((m_threadData.m_vegetationThreadState == PersistentThreadData::VegetationThreadState::Running) &&
  638. (m_threadData.m_vegetationDataSyncState == PersistentThreadData::VegetationDataSyncState::Synchronized)))
  639. {
  640. bool updateVegetationData = false;
  641. // if the config changes, we need to update the vegetation data
  642. if (ApplyPendingConfigChanges())
  643. {
  644. updateVegetationData = true;
  645. }
  646. // if the view rectangle changes, we need to update the vegetation data
  647. if (CalculateViewRect())
  648. {
  649. updateVegetationData = true;
  650. }
  651. if (m_vegetationThreadTaskTimer <= 0.0f)
  652. {
  653. m_vegetationThreadTaskTimer = m_configuration.m_threadProcessingIntervalMs * 0.001f;
  654. // If there are still vegetation tasks pending and we've waited the requested amount of time between queue checks,
  655. // then we need to update the vegetation data.
  656. if (m_vegTasks.VegetationThreadTasksPending())
  657. {
  658. updateVegetationData = true;
  659. }
  660. }
  661. if (updateVegetationData)
  662. {
  663. // Our main thread has potentially updated its state, so cache a new copy of the pieces of state we need.
  664. {
  665. m_cachedMainThreadData.m_currViewRect = m_currViewRect;
  666. m_cachedMainThreadData.m_worldToSector = m_worldToSector;
  667. m_cachedMainThreadData.m_sectorSizeInMeters = m_configuration.m_sectorSizeInMeters;
  668. m_cachedMainThreadData.m_sectorDensity = m_configuration.m_sectorDensity;
  669. m_cachedMainThreadData.m_sectorPointSnapMode = m_configuration.m_sectorPointSnapMode;
  670. }
  671. // Set the state to Dirty to signal the thread that it will need to pull a new copy of the main thread state data
  672. // and refresh the set of work that it's currently doing. The thread will detect the change next time it looks
  673. // for work and clear the state after it pulls new data.
  674. m_threadData.m_vegetationDataSyncState = PersistentThreadData::VegetationDataSyncState::Dirty;
  675. // If the thread isn't currently running, start it up.
  676. if (m_threadData.m_vegetationThreadState == PersistentThreadData::VegetationThreadState::Stopped)
  677. {
  678. //create a job to process vegetation areas, tasks, sectors in the background
  679. m_threadData.m_vegetationThreadState = PersistentThreadData::VegetationThreadState::Running;
  680. auto job = AZ::CreateJobFunction([this]()
  681. {
  682. AZ_PROFILE_SCOPE(Entity, "Vegetation::AreaSystemComponent::VegetationThread");
  683. UpdateContext context;
  684. context.Run(&m_threadData, &m_vegTasks, &m_cachedMainThreadData);
  685. // After we're done processing as much as we can, clear our thread states and exit.
  686. m_threadData.m_vegetationThreadState = PersistentThreadData::VegetationThreadState::Stopped;
  687. m_threadData.m_vegetationDataSyncState = PersistentThreadData::VegetationDataSyncState::Synchronized;
  688. }, true);
  689. job->Start();
  690. }
  691. }
  692. }
  693. }
  694. void AreaSystemComponent::OnTerrainDataCreateBegin()
  695. {
  696. // Interrupt any in-process updates until the next tick. We don't want to update
  697. // while terrain is being created, because we can end up with race conditions in
  698. // which we're querying terrain for some of the points while terrain is still only
  699. // partially created. This can happen during creation because the HeightmapModified
  700. // event fires mid-creation, which can block in TerrainSurfaceDataSystemComponent on
  701. // the surface data mutex. On the vegetation thread, ModifySurfacePoints in surface
  702. // components such as RiverSurfaceData can start successfully querying terrain because
  703. // the TerrainDataRequest bus is now valid, but doesn't always return fully-valid data yet.
  704. m_threadData.InterruptVegetationThread();
  705. }
  706. void AreaSystemComponent::OnTerrainDataDestroyBegin()
  707. {
  708. // Interrupt any in-process updates until the next tick. We don't want to update
  709. // while terrain is being destroyed. There aren't any *known* race conditions here, but
  710. // there are likely surface-related race conditions, so it's better to be safe.
  711. m_threadData.InterruptVegetationThread();
  712. }
  713. bool AreaSystemComponent::CalculateViewRect()
  714. {
  715. AZ_PROFILE_FUNCTION(Entity);
  716. //Get the active camera.
  717. bool cameraPositionIsValid = false;
  718. AZ::Vector3 cameraPosition(0.0f);
  719. #ifdef VEGETATION_EDITOR
  720. Camera::EditorCameraRequestBus::BroadcastResult(cameraPositionIsValid, &Camera::EditorCameraRequestBus::Events::GetActiveCameraPosition, cameraPosition);
  721. if (!cameraPositionIsValid)
  722. #endif // VEGETATION_EDITOR
  723. {
  724. AZ::EntityId activeCameraId;
  725. Camera::CameraSystemRequestBus::BroadcastResult(activeCameraId, &Camera::CameraSystemRequests::GetActiveCamera);
  726. if (activeCameraId.IsValid())
  727. {
  728. AZ::TransformBus::EventResult(cameraPosition, activeCameraId, &AZ::TransformInterface::GetWorldTranslation);
  729. cameraPositionIsValid = true;
  730. }
  731. }
  732. if (cameraPositionIsValid)
  733. {
  734. float posX = cameraPosition.GetX();
  735. float posY = cameraPosition.GetY();
  736. const int sectorSizeInMeters = m_configuration.m_sectorSizeInMeters;
  737. const int viewSize = m_configuration.m_viewRectangleSize;
  738. int halfViewSize = viewSize >> 1;
  739. posX -= halfViewSize * sectorSizeInMeters;
  740. posY -= halfViewSize * sectorSizeInMeters;
  741. auto prevViewRect = m_currViewRect;
  742. m_currViewRect.m_x = (int)(posX * m_worldToSector);
  743. m_currViewRect.m_y = (int)(posY * m_worldToSector);
  744. m_currViewRect.m_width = viewSize;
  745. m_currViewRect.m_height = viewSize;
  746. m_currViewRect.m_viewRectBounds =
  747. AZ::Aabb::CreateFromMinMax(
  748. AZ::Vector3(
  749. static_cast<float>(m_currViewRect.m_x * sectorSizeInMeters),
  750. static_cast<float>(m_currViewRect.m_y * sectorSizeInMeters),
  751. -AZ::Constants::FloatMax),
  752. AZ::Vector3(
  753. static_cast<float>((m_currViewRect.m_x + m_currViewRect.m_width) * sectorSizeInMeters),
  754. static_cast<float>((m_currViewRect.m_y + m_currViewRect.m_height) * sectorSizeInMeters),
  755. AZ::Constants::FloatMax));
  756. return prevViewRect != m_currViewRect;
  757. }
  758. else
  759. {
  760. return false;
  761. }
  762. }
  763. AreaSystemComponent::SectorId AreaSystemComponent::GetSectorId(const AZ::Vector3& worldPos, float worldToSector)
  764. {
  765. // Convert world positions into scaled integer sector IDs.
  766. // The clamp is necessary to ensure that excessive floating-point values don't overflow
  767. // the sector range. The "nextafter" on the min/max limits are because integer min/max
  768. // lose precision when converted to float, causing them to grow to a larger range. By
  769. // using nextafter(), we push them back inside the integer range. Technically, this means
  770. // there are 128 integer numbers at each end of the range that we aren't using, but in practice
  771. // there will be many other precision bugs to deal with if we ever start using that range anyways.
  772. int wx = aznumeric_cast<int>(AZStd::clamp(floor(static_cast<float>(worldPos.GetX() * worldToSector)),
  773. nextafter(aznumeric_cast<float>(std::numeric_limits<int>::min()), 0.0f),
  774. nextafter(aznumeric_cast<float>(std::numeric_limits<int>::max()), 0.0f)));
  775. int wy = aznumeric_cast<int>(AZStd::clamp(floor(static_cast<float>(worldPos.GetY() * worldToSector)),
  776. nextafter(aznumeric_cast<float>(std::numeric_limits<int>::min()), 0.0f),
  777. nextafter(aznumeric_cast<float>(std::numeric_limits<int>::max()), 0.0f)));
  778. return SectorId(wx, wy);
  779. }
  780. AZ_FORCE_INLINE void AreaSystemComponent::ReleaseAllClaims()
  781. {
  782. // Interrupt update in process, if any
  783. m_threadData.InterruptVegetationThread();
  784. {
  785. // Wait for vegetation update thread to finish
  786. AZStd::lock_guard<decltype(m_threadData.m_vegetationThreadMutex)> lockTasks(m_threadData.m_vegetationThreadMutex);
  787. // Synchronously process any queued vegetation thread tasks on the main thread before clearing
  788. // out the sectors. This allows us to update the active vegetation area lists and mark sectors
  789. // as dirty prior to clearing them out, so that way we don't refresh them a second time after
  790. // clearing them out.
  791. // (only process if the allocation has happened)
  792. if (!(m_worldToSector <= 0.0f))
  793. {
  794. UpdateContext threadContext;
  795. m_vegTasks.ProcessVegetationThreadTasks(&threadContext, &m_threadData);
  796. threadContext.UpdateActiveVegetationAreas(&m_threadData, m_currViewRect);
  797. }
  798. // Clear all sector data
  799. m_vegTasks.ClearSectors();
  800. }
  801. }
  802. void AreaSystemComponent::ReleaseWithoutCleanup()
  803. {
  804. // This method will destroy all active vegetation instances, but leave the vegetation render groups active
  805. // so that we're ready to process new instances.
  806. ReleaseAllClaims();
  807. InstanceSystemRequestBus::Broadcast(&InstanceSystemRequestBus::Events::DestroyAllInstances);
  808. }
  809. void AreaSystemComponent::ReleaseData()
  810. {
  811. // This method destroys all active vegetation instances and cleans up / unloads / destroys the vegetation render groups.
  812. ReleaseAllClaims();
  813. InstanceSystemRequestBus::Broadcast(&InstanceSystemRequestBus::Events::Cleanup);
  814. }
  815. void AreaSystemComponent::OnCrySystemInitialized(ISystem& system, [[maybe_unused]] const SSystemInitParams& systemInitParams)
  816. {
  817. m_system = &system;
  818. m_system->GetISystemEventDispatcher()->RegisterListener(this);
  819. }
  820. void AreaSystemComponent::OnCrySystemShutdown([[maybe_unused]] ISystem& system)
  821. {
  822. if (m_system)
  823. {
  824. m_system->GetISystemEventDispatcher()->RemoveListener(this);
  825. m_system = nullptr;
  826. }
  827. }
  828. void AreaSystemComponent::OnCryEditorBeginLevelExport()
  829. {
  830. // We need to free all our instances before exporting a level to ensure that none of the dynamic vegetation data
  831. // gets exported into the static vegetation data files.
  832. // Clear all our spawned vegetation data so that they don't get written out with the vegetation sectors.
  833. ReleaseData();
  834. }
  835. void AreaSystemComponent::OnCryEditorEndLevelExport(bool /*success*/)
  836. {
  837. // We don't need to do anything here. When the vegetation game components reactivate themselves after the level export completes,
  838. // (see EditorVegetationComponentBase.h) they will trigger a refresh of the vegetation areas which will produce all our instances again.
  839. }
  840. void AreaSystemComponent::OnCryEditorCloseScene()
  841. {
  842. // Clear all our spawned vegetation data
  843. ReleaseData();
  844. }
  845. void AreaSystemComponent::OnSystemEvent(ESystemEvent event, [[maybe_unused]] UINT_PTR wparam, [[maybe_unused]] UINT_PTR lparam)
  846. {
  847. AZ_PROFILE_FUNCTION(Entity);
  848. switch (event)
  849. {
  850. case ESYSTEM_EVENT_GAME_MODE_SWITCH_START:
  851. case ESYSTEM_EVENT_LEVEL_LOAD_START:
  852. case ESYSTEM_EVENT_LEVEL_UNLOAD:
  853. case ESYSTEM_EVENT_EDITOR_SIMULATION_MODE_SWITCH_START:
  854. {
  855. ReleaseData();
  856. break;
  857. }
  858. default:
  859. break;
  860. }
  861. }
  862. //////////////////////////////////////////////////////////////////////////
  863. // VegetationThreadTasks
  864. void AreaSystemComponent::VegetationThreadTasks::QueueVegetationTask(AZStd::function<void(UpdateContext * context, PersistentThreadData * threadData, VegetationThreadTasks * vegTasks)> func)
  865. {
  866. AZStd::lock_guard<decltype(m_vegetationThreadTaskMutex)> lock(m_vegetationThreadTaskMutex);
  867. m_vegetationThreadTasks.push_back(func);
  868. if (m_debugData)
  869. {
  870. m_debugData->m_areaTaskQueueCount.store(static_cast<int>(m_vegetationThreadTasks.size()), AZStd::memory_order_relaxed);
  871. }
  872. }
  873. void AreaSystemComponent::VegetationThreadTasks::ProcessVegetationThreadTasks(UpdateContext* context, PersistentThreadData* threadData)
  874. {
  875. AZ_PROFILE_FUNCTION(Entity);
  876. VegetationThreadTasks::VegetationThreadTaskList tasks;
  877. {
  878. AZStd::lock_guard<decltype(m_vegetationThreadTaskMutex)> lock(m_vegetationThreadTaskMutex);
  879. AZStd::swap(tasks, m_vegetationThreadTasks);
  880. if (m_debugData)
  881. {
  882. m_debugData->m_areaTaskQueueCount.store(static_cast<int>(m_vegetationThreadTasks.size()), AZStd::memory_order_relaxed);
  883. m_debugData->m_areaTaskActiveCount.store(static_cast<int>(tasks.size()), AZStd::memory_order_relaxed);
  884. }
  885. }
  886. for (const auto& task : tasks)
  887. {
  888. task(context, threadData, this);
  889. if (m_debugData)
  890. {
  891. m_debugData->m_areaTaskActiveCount.fetch_sub(1, AZStd::memory_order_relaxed);
  892. }
  893. }
  894. }
  895. AZ::Aabb AreaSystemComponent::VegetationThreadTasks::GetSectorBounds(const SectorId& sectorId, int sectorSizeInMeters)
  896. {
  897. return AZ::Aabb::CreateFromMinMax(
  898. AZ::Vector3(
  899. static_cast<float>(sectorId.first * sectorSizeInMeters),
  900. static_cast<float>(sectorId.second * sectorSizeInMeters),
  901. -AZ::Constants::FloatMax),
  902. AZ::Vector3(
  903. static_cast<float>((sectorId.first + 1) * sectorSizeInMeters),
  904. static_cast<float>((sectorId.second + 1) * sectorSizeInMeters),
  905. AZ::Constants::FloatMax));
  906. }
  907. const AreaSystemComponent::SectorInfo* AreaSystemComponent::VegetationThreadTasks::GetSector(const SectorId& sectorId) const
  908. {
  909. VEGETATION_PROFILE_FUNCTION_VERBOSE
  910. AZStd::lock_guard<decltype(m_sectorRollingWindowMutex)> lock(m_sectorRollingWindowMutex);
  911. auto itSector = m_sectorRollingWindow.find(sectorId);
  912. return itSector != m_sectorRollingWindow.end() ? &itSector->second : nullptr;
  913. }
  914. AreaSystemComponent::SectorInfo* AreaSystemComponent::VegetationThreadTasks::GetSector(const SectorId& sectorId)
  915. {
  916. VEGETATION_PROFILE_FUNCTION_VERBOSE
  917. AZStd::lock_guard<decltype(m_sectorRollingWindowMutex)> lock(m_sectorRollingWindowMutex);
  918. auto itSector = m_sectorRollingWindow.find(sectorId);
  919. return itSector != m_sectorRollingWindow.end() ? &itSector->second : nullptr;
  920. }
  921. AreaSystemComponent::SectorInfo* AreaSystemComponent::VegetationThreadTasks::CreateSector(const SectorId& sectorId, int sectorDensity, int sectorSizeInMeters, SnapMode sectorPointSnapMode)
  922. {
  923. VEGETATION_PROFILE_FUNCTION_VERBOSE
  924. SectorInfo sectorInfo;
  925. sectorInfo.m_id = sectorId;
  926. sectorInfo.m_bounds = GetSectorBounds(sectorId, sectorSizeInMeters);
  927. UpdateSectorPoints(sectorInfo, sectorDensity, sectorSizeInMeters, sectorPointSnapMode);
  928. AZStd::lock_guard<decltype(m_sectorRollingWindowMutex)> lock(m_sectorRollingWindowMutex);
  929. SectorInfo& sectorInfoRef = m_sectorRollingWindow[sectorInfo.m_id] = AZStd::move(sectorInfo);
  930. UpdateSectorCallbacks(sectorInfoRef);
  931. return &sectorInfoRef;
  932. }
  933. void AreaSystemComponent::VegetationThreadTasks::UpdateSectorPoints(SectorInfo& sectorInfo, int sectorDensity, int sectorSizeInMeters, SnapMode sectorPointSnapMode)
  934. {
  935. VEGETATION_PROFILE_FUNCTION_VERBOSE
  936. const float vegStep = sectorSizeInMeters / static_cast<float>(sectorDensity);
  937. //build a free list of all points in the sector for areas to consume
  938. sectorInfo.m_baseContext.m_masks.Clear();
  939. sectorInfo.m_baseContext.m_availablePoints.clear();
  940. sectorInfo.m_baseContext.m_availablePoints.reserve(sectorDensity * sectorDensity);
  941. // Determine within our texel area where we want to create our vegetation positions:
  942. // 0 = lower left corner, 0.5 = center
  943. const float texelOffset = (sectorPointSnapMode == SnapMode::Center) ? 0.5f : 0.0f;
  944. SurfaceData::SurfacePointList availablePointsPerPosition;
  945. AZ::Vector2 stepSize(vegStep, vegStep);
  946. AZ::Vector3 regionOffset(texelOffset * vegStep, texelOffset * vegStep, 0.0f);
  947. AZ::Aabb regionBounds = sectorInfo.m_bounds;
  948. regionBounds.SetMin(regionBounds.GetMin() + regionOffset);
  949. // If we just used the sector bounds, floating-point error could sometimes cause an extra point to get generated
  950. // right at the max edge of the bounds. So instead, we adjust our max placement bounds to be the exact size needed
  951. // for sectorDensity points to get placed, plus half a vegStep to account for a safe margin of floating-point error.
  952. // The exact size would be (sectorDensity - 1), so adding half a vegStep gives us (sectorDensity - 0.5f).
  953. // (We should be able to add anything less than 1 extra vegStep and still get exactly sectorDensity points)
  954. regionBounds.SetMax(regionBounds.GetMin() + AZ::Vector3(vegStep * (sectorDensity - 0.5f),
  955. vegStep * (sectorDensity - 0.5f), 0.0f));
  956. AZ::Interface<SurfaceData::SurfaceDataSystem>::Get()->GetSurfacePointsFromRegion(
  957. regionBounds,
  958. stepSize,
  959. SurfaceData::SurfaceTagVector(),
  960. availablePointsPerPosition);
  961. uint claimIndex = 0;
  962. availablePointsPerPosition.EnumeratePoints([this, &sectorInfo, &claimIndex]
  963. ([[maybe_unused]] size_t inPositionIndex, const AZ::Vector3& position,
  964. const AZ::Vector3& normal, const SurfaceData::SurfaceTagWeights& masks) -> bool
  965. {
  966. ClaimPoint& claimPoint = sectorInfo.m_baseContext.m_availablePoints.emplace_back();
  967. claimPoint.m_handle = CreateClaimHandle(sectorInfo, ++claimIndex);
  968. claimPoint.m_position = position;
  969. claimPoint.m_normal = normal;
  970. claimPoint.m_masks = masks;
  971. sectorInfo.m_baseContext.m_masks.AddSurfaceTagWeights(masks);
  972. return true;
  973. });
  974. }
  975. void AreaSystemComponent::VegetationThreadTasks::UpdateSectorCallbacks(SectorInfo& sectorInfo)
  976. {
  977. //setup callback to test if matching point is already claimed
  978. sectorInfo.m_baseContext.m_existedCallback = [this, &sectorInfo](const ClaimPoint& point, const InstanceData& instanceData)
  979. {
  980. const ClaimHandle handle = point.m_handle;
  981. auto claimItr = sectorInfo.m_claimedWorldPointsBeforeFill.find(handle);
  982. bool exists = (claimItr != sectorInfo.m_claimedWorldPointsBeforeFill.end()) && InstanceData::IsSameInstanceData(instanceData, claimItr->second);
  983. if (exists)
  984. {
  985. CreateClaim(sectorInfo, handle, instanceData);
  986. VEG_PROFILE_METHOD(DebugNotificationBus::TryQueueBroadcast(&DebugNotificationBus::Events::CreateInstance, instanceData.m_instanceId, instanceData.m_position, instanceData.m_id));
  987. }
  988. return exists;
  989. };
  990. //setup callback to create claims for new instances
  991. sectorInfo.m_baseContext.m_createdCallback = [this, &sectorInfo](const ClaimPoint& point, const InstanceData& instanceData)
  992. {
  993. const ClaimHandle handle = point.m_handle;
  994. auto claimItr = sectorInfo.m_claimedWorldPointsBeforeFill.find(handle);
  995. bool exists = claimItr != sectorInfo.m_claimedWorldPointsBeforeFill.end();
  996. if (exists)
  997. {
  998. const auto& claimedInstanceData = claimItr->second;
  999. if (claimedInstanceData.m_id != instanceData.m_id)
  1000. {
  1001. //must force bus connect if areas are different
  1002. AreaNotificationBus::Event(claimedInstanceData.m_id, &AreaNotificationBus::Events::OnAreaConnect);
  1003. AreaRequestBus::Event(claimedInstanceData.m_id, &AreaRequestBus::Events::UnclaimPosition, handle);
  1004. AreaNotificationBus::Event(claimedInstanceData.m_id, &AreaNotificationBus::Events::OnAreaDisconnect);
  1005. }
  1006. else
  1007. {
  1008. //already connected during fill sector
  1009. AreaRequestBus::Event(claimedInstanceData.m_id, &AreaRequestBus::Events::UnclaimPosition, handle);
  1010. }
  1011. }
  1012. CreateClaim(sectorInfo, handle, instanceData);
  1013. };
  1014. }
  1015. void AreaSystemComponent::VegetationThreadTasks::DeleteSector(const SectorId& sectorId)
  1016. {
  1017. VEGETATION_PROFILE_FUNCTION_VERBOSE
  1018. AZStd::lock_guard<decltype(m_sectorRollingWindowMutex)> lock(m_sectorRollingWindowMutex);
  1019. auto itSector = m_sectorRollingWindow.find(sectorId);
  1020. if (itSector != m_sectorRollingWindow.end())
  1021. {
  1022. SectorInfo& sectorInfo(itSector->second);
  1023. EmptySector(sectorInfo);
  1024. m_sectorRollingWindow.erase(itSector);
  1025. }
  1026. else
  1027. {
  1028. AZ_Assert(false, "Sector marked for deletion but doesn't exist");
  1029. }
  1030. }
  1031. template<class Fn>
  1032. inline void AreaSystemComponent::VegetationThreadTasks::EnumerateSectorsInAabb(const AZ::Aabb& bounds, float worldToSector, const ViewRect& viewRect, Fn&& fn)
  1033. {
  1034. // Get the min/max sectors for the AABB. If an invalid AABB is passed in, process every active sector.
  1035. // (i.e. every sector in the current m_currViewRect)
  1036. const SectorId boundsMinSector = bounds.IsValid() ? GetSectorId(bounds.GetMin(), worldToSector) : viewRect.GetMinSector();
  1037. const SectorId boundsMaxSector = bounds.IsValid() ? GetSectorId(bounds.GetMax(), worldToSector) : viewRect.GetMaxSector();
  1038. // The min bounds are set to the larger of the AABB min and the curr view rect min.
  1039. // The max bounds are set to the smaller of the AABB max and the curr view rect max.
  1040. // This lets us process only the sectors that overlap both.
  1041. // Note that if the AABB doesn't overlap the curr view rect, the max will end up less
  1042. // than the min, in which case we process no sectors.
  1043. const int minX = AZStd::GetMax(boundsMinSector.first, viewRect.GetMinXSector());
  1044. const int minY = AZStd::GetMax(boundsMinSector.second, viewRect.GetMinYSector());
  1045. const int maxX = AZStd::GetMin(boundsMaxSector.first, viewRect.GetMaxXSector());
  1046. const int maxY = AZStd::GetMin(boundsMaxSector.second, viewRect.GetMaxYSector());
  1047. for (int currY = minY; currY <= maxY; ++currY)
  1048. {
  1049. for (int currX = minX; currX <= maxX; ++currX)
  1050. {
  1051. if (!fn(AZStd::move(SectorId(currX, currY))))
  1052. {
  1053. return;
  1054. }
  1055. }
  1056. }
  1057. }
  1058. void AreaSystemComponent::VegetationThreadTasks::AddUnregisteredVegetationArea(const VegetationAreaInfo& area, float worldToSector, const ViewRect& viewRect)
  1059. {
  1060. EnumerateSectorsInAabb(area.m_bounds, worldToSector, viewRect,
  1061. [&](SectorId&& sectorId)
  1062. {
  1063. m_unregisteredVegetationAreaSet[AZStd::move(sectorId)].insert(area.m_id);
  1064. return true;
  1065. });
  1066. }
  1067. void AreaSystemComponent::VegetationThreadTasks::ReleaseUnregisteredClaims(SectorInfo& sectorInfo)
  1068. {
  1069. VEGETATION_PROFILE_FUNCTION_VERBOSE
  1070. if (!m_unregisteredVegetationAreaSet.empty())
  1071. {
  1072. auto unregisteredAreasForSector = m_unregisteredVegetationAreaSet.find(sectorInfo.m_id);
  1073. if (unregisteredAreasForSector != m_unregisteredVegetationAreaSet.end())
  1074. {
  1075. for (auto claimItr = sectorInfo.m_claimedWorldPoints.begin(); claimItr != sectorInfo.m_claimedWorldPoints.end(); )
  1076. {
  1077. if (unregisteredAreasForSector->second.find(claimItr->second.m_id) != unregisteredAreasForSector->second.end())
  1078. {
  1079. claimItr = sectorInfo.m_claimedWorldPoints.erase(claimItr);
  1080. }
  1081. else
  1082. {
  1083. ++claimItr;
  1084. }
  1085. }
  1086. m_unregisteredVegetationAreaSet.erase(unregisteredAreasForSector);
  1087. }
  1088. }
  1089. }
  1090. void AreaSystemComponent::VegetationThreadTasks::ReleaseUnusedClaims(SectorInfo& sectorInfo)
  1091. {
  1092. VEGETATION_PROFILE_FUNCTION_VERBOSE
  1093. AZStd::unordered_map<AZ::EntityId, AZStd::unordered_set<ClaimHandle>> claimsToRelease;
  1094. // Group up all the previously-claimed-but-no-longer-claimed points based on area id
  1095. for (const auto& claimPair : sectorInfo.m_claimedWorldPointsBeforeFill)
  1096. {
  1097. const auto& handle = claimPair.first;
  1098. const auto& instanceData = claimPair.second;
  1099. const auto& areaId = instanceData.m_id;
  1100. if (sectorInfo.m_claimedWorldPoints.find(handle) == sectorInfo.m_claimedWorldPoints.end())
  1101. {
  1102. claimsToRelease[areaId].insert(handle);
  1103. }
  1104. }
  1105. sectorInfo.m_claimedWorldPointsBeforeFill.clear();
  1106. // Iterate over the claims by area id and release them
  1107. for (const auto& claimPair : claimsToRelease)
  1108. {
  1109. const auto& areaId = claimPair.first;
  1110. const auto& handles = claimPair.second;
  1111. AreaNotificationBus::Event(areaId, &AreaNotificationBus::Events::OnAreaConnect);
  1112. for (const auto& handle : handles)
  1113. {
  1114. AreaRequestBus::Event(areaId, &AreaRequestBus::Events::UnclaimPosition, handle);
  1115. }
  1116. AreaNotificationBus::Event(areaId, &AreaNotificationBus::Events::OnAreaDisconnect);
  1117. }
  1118. }
  1119. void AreaSystemComponent::VegetationThreadTasks::FillSector(SectorInfo& sectorInfo, const VegetationAreaVector& activeAreas)
  1120. {
  1121. AZ_PROFILE_FUNCTION(Entity);
  1122. VEG_PROFILE_METHOD(DebugNotificationBus::TryQueueBroadcast(&DebugNotificationBus::Events::FillSectorStart, sectorInfo.GetSectorX(), sectorInfo.GetSectorY(), AZStd::chrono::steady_clock::now()));
  1123. ReleaseUnregisteredClaims(sectorInfo);
  1124. //m_availablePoints is a free list initialized with the complete set of points in the sector.
  1125. ClaimContext activeContext = sectorInfo.m_baseContext;
  1126. // Clear out the list of claimed world points before we begin
  1127. sectorInfo.m_claimedWorldPointsBeforeFill = sectorInfo.m_claimedWorldPoints;
  1128. sectorInfo.m_claimedWorldPoints.clear();
  1129. //for all active areas attempt to spawn vegetation on sector grid positions
  1130. for (const auto& area : activeAreas)
  1131. {
  1132. //if one or more areas claimed all the points in m_availablePoints, there's no reason to continue.
  1133. if (activeContext.m_availablePoints.empty())
  1134. {
  1135. break;
  1136. }
  1137. //only consider areas that intersect this sector
  1138. if (!area.m_bounds.IsValid() || area.m_bounds.Overlaps(sectorInfo.m_bounds))
  1139. {
  1140. VEG_PROFILE_METHOD(DebugNotificationBus::TryQueueBroadcast(&DebugNotificationBus::Events::FillAreaStart, area.m_id, AZStd::chrono::steady_clock::now()));
  1141. //each area is responsible for removing whatever points it claims from m_availablePoints, so subsequent areas will have fewer points to try to claim.
  1142. AreaNotificationBus::Event(area.m_id, &AreaNotificationBus::Events::OnAreaConnect);
  1143. AreaRequestBus::Event(area.m_id, &AreaRequestBus::Events::ClaimPositions, EntityIdStack{}, activeContext);
  1144. AreaNotificationBus::Event(area.m_id, &AreaNotificationBus::Events::OnAreaDisconnect);
  1145. VEG_PROFILE_METHOD(DebugNotificationBus::TryQueueBroadcast(&DebugNotificationBus::Events::FillAreaEnd, area.m_id, AZStd::chrono::steady_clock::now(), aznumeric_cast<AZ::u32>(activeContext.m_availablePoints.size())));
  1146. }
  1147. }
  1148. ReleaseUnusedClaims(sectorInfo);
  1149. VEG_PROFILE_METHOD(DebugNotificationBus::TryQueueBroadcast(&DebugNotificationBus::Events::FillSectorEnd, sectorInfo.GetSectorX(), sectorInfo.GetSectorY(), AZStd::chrono::steady_clock::now(), aznumeric_cast<AZ::u32>(activeContext.m_availablePoints.size())));
  1150. }
  1151. void AreaSystemComponent::VegetationThreadTasks::EmptySector(SectorInfo& sectorInfo)
  1152. {
  1153. AZ_PROFILE_FUNCTION(Entity);
  1154. AZStd::unordered_map<AZ::EntityId, AZStd::unordered_set<ClaimHandle>> claimsToRelease;
  1155. // group up all the points based on area id
  1156. for (const auto& claimPair : sectorInfo.m_claimedWorldPoints)
  1157. {
  1158. const auto& handle = claimPair.first;
  1159. const auto& instanceData = claimPair.second;
  1160. const auto& areaId = instanceData.m_id;
  1161. claimsToRelease[areaId].insert(handle);
  1162. }
  1163. sectorInfo.m_claimedWorldPoints.clear();
  1164. // iterate over the claims by area id and release them
  1165. for (const auto& claimPair : claimsToRelease)
  1166. {
  1167. const auto& areaId = claimPair.first;
  1168. const auto& handles = claimPair.second;
  1169. AreaNotificationBus::Event(areaId, &AreaNotificationBus::Events::OnAreaConnect);
  1170. for (const auto& handle : handles)
  1171. {
  1172. AreaRequestBus::Event(areaId, &AreaRequestBus::Events::UnclaimPosition, handle);
  1173. }
  1174. AreaNotificationBus::Event(areaId, &AreaNotificationBus::Events::OnAreaDisconnect);
  1175. }
  1176. }
  1177. void AreaSystemComponent::VegetationThreadTasks::ClearSectors()
  1178. {
  1179. AZ_PROFILE_FUNCTION(Entity);
  1180. AZStd::lock_guard<decltype(m_sectorRollingWindowMutex)> lock(m_sectorRollingWindowMutex);
  1181. for (auto& sectorPair : m_sectorRollingWindow)
  1182. {
  1183. EmptySector(sectorPair.second);
  1184. }
  1185. m_sectorRollingWindow.clear();
  1186. // Clear any pending unregistrations; since all of the sectors have been cleared anyways, these don't affect anything
  1187. m_unregisteredVegetationAreaSet.clear();
  1188. }
  1189. void AreaSystemComponent::VegetationThreadTasks::CreateClaim(SectorInfo& sectorInfo, const ClaimHandle handle, const InstanceData& instanceData)
  1190. {
  1191. VEGETATION_PROFILE_FUNCTION_VERBOSE
  1192. sectorInfo.m_claimedWorldPoints[handle] = instanceData;
  1193. }
  1194. ClaimHandle AreaSystemComponent::VegetationThreadTasks::CreateClaimHandle(const SectorInfo& sectorInfo, uint32_t index) const
  1195. {
  1196. VEGETATION_PROFILE_FUNCTION_VERBOSE
  1197. ClaimHandle handle = 0;
  1198. AreaSystemUtil::hash_combine_64(handle, sectorInfo.m_id.first);
  1199. AreaSystemUtil::hash_combine_64(handle, sectorInfo.m_id.second);
  1200. AreaSystemUtil::hash_combine_64(handle, index);
  1201. return handle;
  1202. }
  1203. void AreaSystemComponent::VegetationThreadTasks::MarkDirtySectors(const AZ::Aabb& bounds, DirtySectors& dirtySet, float worldToSector, const ViewRect& viewRect)
  1204. {
  1205. if (bounds.IsValid())
  1206. {
  1207. if (!dirtySet.IsAllDirty())
  1208. {
  1209. // Only mark individual sectors as dirty if we have valid AABB bounds and haven't
  1210. // already marked *all* sectors as dirty.
  1211. EnumerateSectorsInAabb(bounds, worldToSector, viewRect, [&](SectorId&& sectorId)
  1212. {
  1213. dirtySet.MarkDirty(sectorId);
  1214. return true;
  1215. });
  1216. }
  1217. }
  1218. else
  1219. {
  1220. // If we have invalid bounds, we can mark all sectors as dirty without needing
  1221. // to add each one to the list.
  1222. dirtySet.MarkAllDirty();
  1223. }
  1224. }
  1225. void AreaSystemComponent::VegetationThreadTasks::FetchDebugData()
  1226. {
  1227. VEG_PROFILE_METHOD(DebugSystemDataBus::BroadcastResult(m_debugData, &DebugSystemDataBus::Events::GetDebugData));
  1228. }
  1229. //////////////////////////////////////////////////////////////////////////
  1230. // PersistentThreadData
  1231. AZ_FORCE_INLINE void AreaSystemComponent::PersistentThreadData::InterruptVegetationThread()
  1232. {
  1233. auto expected = PersistentThreadData::VegetationThreadState::Running;
  1234. m_vegetationThreadState.compare_exchange_strong(expected, PersistentThreadData::VegetationThreadState::InterruptRequested);
  1235. }
  1236. //////////////////////////////////////////////////////////////////////////
  1237. // UpdateContext
  1238. void AreaSystemComponent::UpdateContext::Run(PersistentThreadData* threadData, VegetationThreadTasks* vegTasks, CachedMainThreadData* cachedMainThreadData)
  1239. {
  1240. AZ_PROFILE_FUNCTION(Entity);
  1241. // Ensure that the main thread doesn't activate or deactivate the component until after this thread finishes.
  1242. // Note that this does *not* prevent the main thread from running OnTick, which can communicate data changes
  1243. // to this thread while it's still processing work.
  1244. AZStd::lock_guard<decltype(threadData->m_vegetationThreadMutex)> lockTasks(threadData->m_vegetationThreadMutex);
  1245. bool keepProcessing = true;
  1246. while (keepProcessing && (threadData->m_vegetationThreadState != PersistentThreadData::VegetationThreadState::InterruptRequested))
  1247. {
  1248. AZ_PROFILE_SCOPE(Entity, "Vegetation::AreaSystemComponent::UpdateContext::Run-InnerLoop");
  1249. // Update thread state if its dirty
  1250. PersistentThreadData::VegetationDataSyncState expected = PersistentThreadData::VegetationDataSyncState::Dirty;
  1251. if (threadData->m_vegetationDataSyncState.compare_exchange_strong(expected, PersistentThreadData::VegetationDataSyncState::Updating))
  1252. {
  1253. // A dirty state can consist of one or more of the following:
  1254. // - Main thread has changed veg configuration
  1255. // - Main thread has changed current view rectangle
  1256. // - Vegetation tasks have been queued for this thread to process
  1257. // Our main thread has potentially updated its state, so cache a new copy of the pieces of state we need.
  1258. m_cachedMainThreadData = *cachedMainThreadData;
  1259. // Run through all the queued tasks to update vegetation area active states and lists of dirty sectors
  1260. vegTasks->ProcessVegetationThreadTasks(this, threadData);
  1261. // Now that we've processed all the queued tasks, gather a list of active areas that affect our visible sectors, sorted by priority
  1262. UpdateActiveVegetationAreas(threadData, m_cachedMainThreadData.m_currViewRect);
  1263. // Refresh the lists of sectors to create / update / remove
  1264. keepProcessing = UpdateSectorWorkLists(threadData, vegTasks);
  1265. // We've finished refreshing the thread work state, so mark ourselves as synchronized.
  1266. threadData->m_vegetationDataSyncState = PersistentThreadData::VegetationDataSyncState::Synchronized;
  1267. }
  1268. if (keepProcessing)
  1269. {
  1270. keepProcessing = UpdateOneSector(threadData, vegTasks);
  1271. }
  1272. }
  1273. }
  1274. void AreaSystemComponent::UpdateContext::UpdateActiveVegetationAreas(PersistentThreadData* threadData, const ViewRect& viewRect)
  1275. {
  1276. AZ_PROFILE_FUNCTION(Entity);
  1277. //build a priority sorted list of all active areas
  1278. if (threadData->m_activeAreasDirty)
  1279. {
  1280. threadData->m_activeAreasDirty = false;
  1281. threadData->m_activeAreas.clear();
  1282. threadData->m_activeAreas.reserve(threadData->m_globalVegetationAreaMap.size());
  1283. for (const auto& areaPair : threadData->m_globalVegetationAreaMap)
  1284. {
  1285. const auto& area = areaPair.second;
  1286. //if this is an area being ignored due to a parent area blender, skip it
  1287. if (threadData->m_ignoredVegetationAreaSet.find(area.m_id) != threadData->m_ignoredVegetationAreaSet.end())
  1288. {
  1289. continue;
  1290. }
  1291. //do any per area setup or checks since the state of areas and entities with the system has changed
  1292. bool prepared = false;
  1293. AreaNotificationBus::Event(area.m_id, &AreaNotificationBus::Events::OnAreaConnect);
  1294. AreaRequestBus::EventResult(prepared, area.m_id, &AreaRequestBus::Events::PrepareToClaim, EntityIdStack{});
  1295. AreaNotificationBus::Event(area.m_id, &AreaNotificationBus::Events::OnAreaDisconnect);
  1296. if (!prepared)
  1297. {
  1298. // if PrepareToClaim returned false, this area is declaring itself as inactive.
  1299. // The area will need to call RefreshArea() if/when its state should change to active.
  1300. continue;
  1301. }
  1302. threadData->m_activeAreas.push_back(area);
  1303. }
  1304. AZStd::sort(threadData->m_activeAreas.begin(), threadData->m_activeAreas.end(), [](const auto& lhs, const auto& rhs)
  1305. {
  1306. return AZStd::make_pair(lhs.m_layer, lhs.m_priority) > AZStd::make_pair(rhs.m_layer, rhs.m_priority);
  1307. });
  1308. }
  1309. //further reduce set of active areas to only include ones that intersect the bubble
  1310. AZ::Aabb bubbleBounds = viewRect.GetViewRectBounds();
  1311. threadData->m_activeAreasInBubble = threadData->m_activeAreas;
  1312. threadData->m_activeAreasInBubble.erase(
  1313. AZStd::remove_if(
  1314. threadData->m_activeAreasInBubble.begin(),
  1315. threadData->m_activeAreasInBubble.end(),
  1316. [bubbleBounds](const auto& area) { return area.m_bounds.IsValid() && !area.m_bounds.Overlaps(bubbleBounds); }),
  1317. threadData->m_activeAreasInBubble.end());
  1318. }
  1319. bool AreaSystemComponent::UpdateContext::UpdateSectorWorkLists(PersistentThreadData* threadData, VegetationThreadTasks* vegTasks)
  1320. {
  1321. AZ_PROFILE_FUNCTION(Entity);
  1322. auto& worldToSector = m_cachedMainThreadData.m_worldToSector;
  1323. auto& currViewRect = m_cachedMainThreadData.m_currViewRect;
  1324. // only process the sectors if the allocation has happened
  1325. if (worldToSector <= 0.0f)
  1326. {
  1327. return false;
  1328. }
  1329. bool deleteAllSectors = false;
  1330. // Early exit if no active areas, no sectors are marked as dirty or updating, and there
  1331. // are no sectors left in our rolling window.
  1332. // Until an area becomes active again, there's no work that sectors should need to do.
  1333. if (threadData->m_activeAreasInBubble.empty() &&
  1334. threadData->m_dirtySectorContents.IsNoneDirty() &&
  1335. threadData->m_dirtySectorSurfacePoints.IsNoneDirty())
  1336. {
  1337. AZStd::lock_guard<decltype(vegTasks->m_sectorRollingWindowMutex)> lock(vegTasks->m_sectorRollingWindowMutex);
  1338. if (vegTasks->m_sectorRollingWindow.empty())
  1339. {
  1340. return !m_deleteWorkList.empty() || !m_updateWorkList.empty();
  1341. }
  1342. else
  1343. {
  1344. // No active areas left in our view bubble, so queue up the deletion of all remaining active sectors.
  1345. deleteAllSectors = true;
  1346. }
  1347. }
  1348. // Cache off the total number of sectors that *should* be active in the view rectangle. We'll use this
  1349. // when processing sectors to ensure that we start to prioritize deletes whenever our number of active sectors
  1350. // gets above this number.
  1351. m_viewRectSectorCount = currViewRect.GetNumSectors();
  1352. // remove any sectors marked for update which are no longer in the view rectangle
  1353. m_updateWorkList.erase(
  1354. AZStd::remove_if(
  1355. m_updateWorkList.begin(),
  1356. m_updateWorkList.end(),
  1357. [currViewRect](const auto& entry) {return !currViewRect.IsInside(entry.first); }),
  1358. m_updateWorkList.end());
  1359. AZ_Assert(m_updateWorkList.size() <= m_viewRectSectorCount, "Refreshed RequestedUpdate list should not be larger than the view rectangle.");
  1360. // Clear our delete work list, we'll recreate it and sort it again below.
  1361. // Note: We do NOT clear m_updateWorkList, because we use it to incrementally determine any new
  1362. // updates to add to the queue. Without it, we wouldn't know if a previous data change caused
  1363. // us to mark any sectors still in view as needing an update.
  1364. m_deleteWorkList.clear();
  1365. // If we're deleting all sectors, make sure we don't have any of them previously queued up for creation / updating.
  1366. if (deleteAllSectors)
  1367. {
  1368. m_updateWorkList.clear();
  1369. }
  1370. // Run through our list of active sectors and determine which ones need adding / updating / deleting
  1371. {
  1372. AZStd::lock_guard<decltype(vegTasks->m_sectorRollingWindowMutex)> lock(vegTasks->m_sectorRollingWindowMutex);
  1373. // To create our add / update / delete lists, we need two loops. The first loops through the *new* view rectangle
  1374. // looking for missing sectors to add. The second loops through the *current* set of active sectors looking for any
  1375. // to update or remove.
  1376. // First loop: Determine non-existent sectors which need to be created
  1377. if (!deleteAllSectors)
  1378. {
  1379. for (int y = currViewRect.m_y; y < currViewRect.m_y + currViewRect.m_height; ++y)
  1380. {
  1381. for (int x = currViewRect.m_x; x < currViewRect.m_x + currViewRect.m_width; ++x)
  1382. {
  1383. SectorId sectorId(x, y);
  1384. if (vegTasks->m_sectorRollingWindow.find(sectorId) == vegTasks->m_sectorRollingWindow.end())
  1385. {
  1386. // If the sector doesn't currently exist and it belongs in the view rect, request a creation.
  1387. // (This will either create a new entry or overwrite an existing pending Create request)
  1388. auto found = AZStd::find_if(m_updateWorkList.begin(), m_updateWorkList.end(), [sectorId](auto& entry) { return (entry.first == sectorId); });
  1389. if (found != m_updateWorkList.end())
  1390. {
  1391. // If the update entry already exists, overwrite the state. We don't need to check or
  1392. // preserve the existing state because Create is the most comprehensive update we can do.
  1393. found->second = UpdateMode::Create;
  1394. }
  1395. else
  1396. {
  1397. m_updateWorkList.emplace_back(sectorId, UpdateMode::Create);
  1398. }
  1399. // Since we've already removed entries that aren't in the view rect, and these loops are only
  1400. // adding entries in the view rect, at this point our update work list size should never get
  1401. // larger than the set of sectors in the view rect.
  1402. AZ_Assert(m_updateWorkList.size() <= m_viewRectSectorCount, "Too many update requests added");
  1403. }
  1404. }
  1405. }
  1406. }
  1407. // Second loop: Determine any existing sectors which need to be updated or deleted
  1408. for (auto& sector : vegTasks->m_sectorRollingWindow)
  1409. {
  1410. auto& sectorId = sector.first;
  1411. if (deleteAllSectors || !currViewRect.IsInside(sectorId))
  1412. {
  1413. // Active sector is no longer within view or there are no active areas, so delete it
  1414. m_deleteWorkList.emplace_back(AZStd::move(sectorId));
  1415. }
  1416. else if (threadData->m_dirtySectorSurfacePoints.IsDirty(sectorId))
  1417. {
  1418. // Active sector has new surface point information, so rebuild surface cache and fill
  1419. // (This will either create a new entry, or overwrite an existing fill or rebuild request)
  1420. auto found = AZStd::find_if(m_updateWorkList.begin(), m_updateWorkList.end(), [sectorId](auto& entry) { return (entry.first == sectorId); });
  1421. if (found != m_updateWorkList.end())
  1422. {
  1423. // If the update entry already exists, overwrite the state. We don't need to check or
  1424. // preserve the state since it should only contain either Rebuild or Fill, and Rebuild
  1425. // is more comprehensive than Fill.
  1426. AZ_Assert(found->second != UpdateMode::Create, "Create requests shouldn't exist for active sectors!");
  1427. found->second = UpdateMode::RebuildSurfaceCacheAndFill;
  1428. }
  1429. else
  1430. {
  1431. m_updateWorkList.emplace_back(sectorId, UpdateMode::RebuildSurfaceCacheAndFill);
  1432. }
  1433. // We shouldn't ever have an update list that's larger than the set of sectors in the view rect.
  1434. AZ_Assert(m_updateWorkList.size() <= m_viewRectSectorCount, "Too many update requests added");
  1435. }
  1436. else if (threadData->m_dirtySectorContents.IsDirty(sectorId))
  1437. {
  1438. // Active sector has new veg area information, so refill it.
  1439. auto found = AZStd::find_if(m_updateWorkList.begin(), m_updateWorkList.end(), [sectorId](auto& entry)
  1440. { return (entry.first == sectorId); });
  1441. if (found == m_updateWorkList.end())
  1442. {
  1443. // Only add Fill entries if no update request exists for this sector. We don't
  1444. // overwrite existing entries because an existing entry might have previously
  1445. // requested "RebuildSurfaceCacheAndFill", which is more comprehensive than this request.
  1446. m_updateWorkList.emplace_back(sectorId, UpdateMode::Fill);
  1447. // We shouldn't ever have an update list that's larger than the set of sectors in the view rect.
  1448. AZ_Assert(m_updateWorkList.size() <= m_viewRectSectorCount, "Too many update requests added");
  1449. }
  1450. }
  1451. }
  1452. }
  1453. // We've finished processing our dirtySector lists, so clear them.
  1454. threadData->m_dirtySectorContents.Clear();
  1455. threadData->m_dirtySectorSurfacePoints.Clear();
  1456. // sort work by distance from center of the view rectangle.
  1457. if (currViewRect.GetViewRectBounds().IsValid())
  1458. {
  1459. float sectorCenterX = (currViewRect.GetMinXSector() + currViewRect.GetMaxXSector()) / 2.0f;
  1460. float sectorCenterY = (currViewRect.GetMinYSector() + currViewRect.GetMaxYSector()) / 2.0f;
  1461. // Sort function that returns true if the lhs is "closer" than the rhs to the center.
  1462. // The choice of sort algorithm is somewhat a question of preference, and could potentially be made a policy
  1463. // choice at some point. The current choice uses "number of sectors from center" as the primary sort criteria,
  1464. // with a secondary sort on y and x values to get a deterministic sort pattern. This algorithm updates the vegetation
  1465. // outward in cocentric circles.
  1466. // Here are some other possibilities of algorithm choices:
  1467. // 1) float maxDist = AZStd::GetMax(fabs(id.first - sectorCenterX), fabs(id.second - sectorCenterY));
  1468. // This moves outward in cocentric squares.
  1469. // 2) float maxDist = GetSectorBounds(id).GetCenter().GetDistanceSq(currViewRect.GetViewRectBounds().GetCenter());
  1470. // This moves outward in cocentric circles, similar to our chosen algorithm, but in more of a "pinwheel" pattern
  1471. // that fans out from the axis lines.
  1472. // 3) We could feed in camera orientation as well, and use that to further prioritize sectors within view. The concern
  1473. // with choosing this approach is that it will update the work lists much more rapidly than the vegetation can spawn, so
  1474. // it the extra updates and calculations could easily cause sector choices that constantly lag behind the current view,
  1475. // producing similar to worse results than our current algorithm.
  1476. // With any of these choices, the secondary sort gives a deterministic update pattern when the distances are equal.
  1477. auto sectorCompare = [sectorCenterX, sectorCenterY](const SectorId& lhsSectorId, const SectorId& rhsSectorId, bool sortClosestFirst)
  1478. {
  1479. const float lhsMaxDist = ((lhsSectorId.first - sectorCenterX) * (lhsSectorId.first - sectorCenterX)) + ((lhsSectorId.second - sectorCenterY) * (lhsSectorId.second - sectorCenterY));
  1480. const float rhsMaxDist = ((rhsSectorId.first - sectorCenterX) * (rhsSectorId.first - sectorCenterX)) + ((rhsSectorId.second - sectorCenterY) * (rhsSectorId.second - sectorCenterY));
  1481. return (lhsMaxDist < rhsMaxDist) ? sortClosestFirst : // Return if one sector is closer than the other to the center.
  1482. ((lhsMaxDist > rhsMaxDist) ? !sortClosestFirst :
  1483. ((lhsSectorId.second < rhsSectorId.second) ? true : // If it's the same distance return if the Y value is smaller...
  1484. ((lhsSectorId.second > rhsSectorId.second) ? false :
  1485. (lhsSectorId.first < rhsSectorId.first)))); // If the Y value is the same, return if the X value is smaller.
  1486. };
  1487. AZStd::sort(m_updateWorkList.begin(), m_updateWorkList.end(), [sectorCompare](const auto& lhs, const auto& rhs)
  1488. {
  1489. // We always pull from the end of the list, so we sort the *closest* sectors to the end.
  1490. // That way we create / update the closest sectors first.
  1491. return sectorCompare(lhs.first, rhs.first, false);
  1492. });
  1493. AZStd::sort(m_deleteWorkList.begin(), m_deleteWorkList.end(), [sectorCompare](const auto& lhs, const auto& rhs)
  1494. {
  1495. // We always pull from the end of the list, so we sort the *furthest* sectors to the end.
  1496. // That way we delete the furthest sectors first.
  1497. return sectorCompare(lhs, rhs, true);
  1498. });
  1499. }
  1500. return !m_deleteWorkList.empty() || !m_updateWorkList.empty();
  1501. }
  1502. bool AreaSystemComponent::UpdateContext::UpdateOneSector(PersistentThreadData* threadData, VegetationThreadTasks* vegTasks)
  1503. {
  1504. AZ_PROFILE_FUNCTION(Entity);
  1505. // This chooses work in the following order:
  1506. // 1) Delete if we have more sectors than the total that should be in the view rectangle
  1507. // 2) Create/update if we have any sectors to create / update
  1508. // 3) Delete if we have any sectors to delete
  1509. // Delete if there are more active sectors than the number of desired sectors or the update list is empty.
  1510. if (!m_deleteWorkList.empty())
  1511. {
  1512. AZStd::lock_guard<decltype(vegTasks->m_sectorRollingWindowMutex)> lock(vegTasks->m_sectorRollingWindowMutex);
  1513. if ((vegTasks->m_sectorRollingWindow.size() > m_viewRectSectorCount) || m_updateWorkList.empty())
  1514. {
  1515. vegTasks->DeleteSector(m_deleteWorkList.back());
  1516. m_deleteWorkList.pop_back();
  1517. return true;
  1518. }
  1519. }
  1520. // Create / update if there's anything to do and we didn't prioritize a delete.
  1521. if (!m_updateWorkList.empty())
  1522. {
  1523. auto& updateEntry = m_updateWorkList.back();
  1524. SectorId sectorId = updateEntry.first;
  1525. UpdateMode mode = updateEntry.second;
  1526. m_updateWorkList.pop_back();
  1527. {
  1528. AZStd::lock_guard<decltype(vegTasks->m_sectorRollingWindowMutex)> lock(vegTasks->m_sectorRollingWindowMutex);
  1529. auto& sectorDensity = m_cachedMainThreadData.m_sectorDensity;
  1530. auto& sectorSizeInMeters = m_cachedMainThreadData.m_sectorSizeInMeters;
  1531. auto& sectorPointSnapMode = m_cachedMainThreadData.m_sectorPointSnapMode;
  1532. switch (mode)
  1533. {
  1534. case UpdateMode::RebuildSurfaceCacheAndFill:
  1535. {
  1536. auto sectorInfo = vegTasks->GetSector(sectorId);
  1537. AZ_Assert(sectorInfo, "Sector update mode is 'RebuildSurfaceCache' but sector doesn't exist");
  1538. vegTasks->UpdateSectorPoints(*sectorInfo, sectorDensity, sectorSizeInMeters, sectorPointSnapMode);
  1539. vegTasks->FillSector(*sectorInfo, threadData->m_activeAreasInBubble);
  1540. }
  1541. break;
  1542. case UpdateMode::Fill:
  1543. {
  1544. auto sectorInfo = vegTasks->GetSector(sectorId);
  1545. AZ_Assert(sectorInfo, "Sector update mode is 'Fill' but sector doesn't exist");
  1546. vegTasks->FillSector(*sectorInfo, threadData->m_activeAreasInBubble);
  1547. }
  1548. break;
  1549. case UpdateMode::Create:
  1550. {
  1551. AZ_Assert(!vegTasks->GetSector(sectorId), "Sector update mode is 'Create' but sector already exists");
  1552. auto sectorInfo = vegTasks->CreateSector(sectorId, sectorDensity, sectorSizeInMeters, sectorPointSnapMode);
  1553. vegTasks->FillSector(*sectorInfo, threadData->m_activeAreasInBubble);
  1554. }
  1555. break;
  1556. }
  1557. }
  1558. return true;
  1559. }
  1560. // No sectors left to process, so tell our main loop to stop processing.
  1561. return false;
  1562. }
  1563. }