SurfacePointList.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  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 <SurfaceData/Utility/SurfaceDataUtility.h>
  9. #include <SurfaceData/SurfacePointList.h>
  10. #include <SurfaceData/SurfaceDataModifierRequestBus.h>
  11. namespace SurfaceData
  12. {
  13. size_t SurfacePointList::GetInPositionIndexFromPosition(const AZ::Vector3& inPosition) const
  14. {
  15. // Given an input position, find the input position index that's associated with it.
  16. // We'll bias towards always having a position that's the same or further in our input list than before,
  17. // so we'll do a linear search that starts with the last input position we used, and goes forward (and wraps around)
  18. // until we've searched them all.
  19. // Our expectation is that most of the time, we'll only have to compare 0-1 input positions.
  20. size_t inPositionIndex = m_lastInputPositionIndex;
  21. [[maybe_unused]] bool foundMatch = false;
  22. for (size_t indexCounter = 0; indexCounter < m_inputPositions.size(); indexCounter++)
  23. {
  24. if (m_inputPositions[inPositionIndex] == inPosition)
  25. {
  26. foundMatch = true;
  27. break;
  28. }
  29. inPositionIndex = (inPositionIndex + 1) % m_inputPositions.size();
  30. }
  31. AZ_Assert(
  32. foundMatch,
  33. "Couldn't find input position: (%0.7f, %0.7f, %0.7f), m_lastInputPositionIndex = %zu, m_inputPositions.size() = %zu",
  34. inPosition.GetX(), inPosition.GetY(), inPosition.GetZ(), m_lastInputPositionIndex, m_inputPositions.size());
  35. m_lastInputPositionIndex = inPositionIndex;
  36. return inPositionIndex;
  37. }
  38. size_t SurfacePointList::GetSurfacePointStartIndexFromInPositionIndex(size_t inPositionIndex) const
  39. {
  40. // Index to the first output surface point for this input position.
  41. return inPositionIndex * m_maxSurfacePointsPerInput;
  42. }
  43. SurfacePointList::SurfacePointList(AZStd::span<const AzFramework::SurfaceData::SurfacePoint> surfacePoints)
  44. {
  45. // Construct and finalize the list with the set of passed-in surface points.
  46. // This is primarily a convenience for unit tests.
  47. StartListConstruction(surfacePoints);
  48. EndListConstruction();
  49. }
  50. void SurfacePointList::StartListConstruction(AZStd::span<const AzFramework::SurfaceData::SurfacePoint> surfacePoints)
  51. {
  52. // Construct the list with the set of passed-in surface points but don't finalize it.
  53. // This is primarily a convenience for unit tests that want to test surface modifiers with specific inputs.
  54. surfacePoints.begin();
  55. StartListConstruction(AZStd::span<const AZ::Vector3>(&(surfacePoints.begin()->m_position), 1), surfacePoints.size(), {});
  56. for (auto& point : surfacePoints)
  57. {
  58. SurfaceTagWeights weights(point.m_surfaceTags);
  59. AddSurfacePoint(AZ::EntityId(), point.m_position, point.m_position, point.m_normal, weights);
  60. }
  61. }
  62. void SurfacePointList::StartListConstruction(
  63. AZStd::span<const AZ::Vector3> inPositions, size_t maxPointsPerInput, AZStd::span<const SurfaceTag> filterTags)
  64. {
  65. AZ_Assert(!m_listIsBeingConstructed, "Trying to start list construction on a list currently under construction.");
  66. AZ_Assert(m_surfacePositionList.empty(), "Trying to reserve space on a list that is already being used.");
  67. Clear();
  68. m_listIsBeingConstructed = true;
  69. // Save off working references to the data we'll need during list construction.
  70. // These references need to remain valid during construction, but not afterwards.
  71. m_filterTags = filterTags;
  72. m_inputPositions = inPositions;
  73. m_inputPositionSize = inPositions.size();
  74. m_maxSurfacePointsPerInput = maxPointsPerInput;
  75. size_t outputReserveSize = inPositions.size() * m_maxSurfacePointsPerInput;
  76. // Reserve enough space to have one value per input position, and initialize it to 0.
  77. m_numSurfacePointsPerInput.resize(m_inputPositionSize);
  78. // Reserve enough space to have maxSurfacePointsPerInput entries per input position, and initialize them all to 0.
  79. m_sortedSurfacePointIndices.resize(outputReserveSize);
  80. // Reserve enough space for all our possible output surface points, but don't initialize them.
  81. m_surfaceCreatorIdList.reserve(outputReserveSize);
  82. m_surfacePositionList.reserve(outputReserveSize);
  83. m_surfaceNormalList.reserve(outputReserveSize);
  84. m_surfaceWeightsList.reserve(outputReserveSize);
  85. }
  86. void SurfacePointList::Clear()
  87. {
  88. m_listIsBeingConstructed = false;
  89. m_lastInputPositionIndex = 0;
  90. m_inputPositionSize = 0;
  91. m_maxSurfacePointsPerInput = 0;
  92. m_filterTags = {};
  93. m_inputPositions = {};
  94. m_sortedSurfacePointIndices.clear();
  95. m_numSurfacePointsPerInput.clear();
  96. m_surfacePositionList.clear();
  97. m_surfaceNormalList.clear();
  98. m_surfaceWeightsList.clear();
  99. m_surfaceCreatorIdList.clear();
  100. m_surfacePointBounds = AZ::Aabb::CreateNull();
  101. }
  102. void SurfacePointList::AddSurfacePoint(
  103. const AZ::EntityId& entityId, const AZ::Vector3& inPosition,
  104. const AZ::Vector3& position, const AZ::Vector3& normal, const SurfaceTagWeights& masks)
  105. {
  106. AZ_Assert(m_listIsBeingConstructed, "Trying to add surface points to a SurfacePointList that isn't under construction.");
  107. // Find the inPositionIndex that matches the inPosition.
  108. size_t inPositionIndex = GetInPositionIndexFromPosition(inPosition);
  109. // Find the first SurfacePoint that either matches the inPosition, or that starts the range for the next inPosition after this one.
  110. size_t surfacePointStartIndex = GetSurfacePointStartIndexFromInPositionIndex(inPositionIndex);
  111. // When adding a surface point, we'll either merge it with a similar existing point, or else add it in order of
  112. // decreasing Z, so that our final results are sorted.
  113. size_t surfacePointInsertIndex = surfacePointStartIndex;
  114. for (; surfacePointInsertIndex < (surfacePointStartIndex + m_numSurfacePointsPerInput[inPositionIndex]); ++surfacePointInsertIndex)
  115. {
  116. // (Someday we should add a configurable tolerance for comparison)
  117. if (m_surfacePositionList[m_sortedSurfacePointIndices[surfacePointInsertIndex]].IsClose(position) &&
  118. m_surfaceNormalList[m_sortedSurfacePointIndices[surfacePointInsertIndex]].IsClose(normal))
  119. {
  120. // consolidate points with similar attributes by adding masks/weights to the similar point instead of adding a new one.
  121. m_surfaceWeightsList[m_sortedSurfacePointIndices[surfacePointInsertIndex]].AddSurfaceTagWeights(masks);
  122. return;
  123. }
  124. else if (m_surfacePositionList[m_sortedSurfacePointIndices[surfacePointInsertIndex]].GetZ() < position.GetZ())
  125. {
  126. break;
  127. }
  128. }
  129. // If we've made it here, we're adding the point, not merging it.
  130. // Verify we aren't adding more points than expected.
  131. AZ_Assert(m_numSurfacePointsPerInput[inPositionIndex] <= m_maxSurfacePointsPerInput, "Adding too many surface points.");
  132. // Expand our output AABB to include this point.
  133. m_surfacePointBounds.AddPoint(position);
  134. // If this isn't the first output for this input position, shift our sorted indices for this input position to make room for
  135. // the new entry.
  136. if (m_numSurfacePointsPerInput[inPositionIndex] > 0)
  137. {
  138. size_t startIndex = surfacePointInsertIndex;
  139. size_t endIndex = surfacePointStartIndex + m_numSurfacePointsPerInput[inPositionIndex];
  140. AZStd::move_backward(
  141. m_sortedSurfacePointIndices.begin() + startIndex, m_sortedSurfacePointIndices.begin() + endIndex,
  142. m_sortedSurfacePointIndices.begin() + endIndex + 1);
  143. }
  144. m_numSurfacePointsPerInput[inPositionIndex]++;
  145. // Insert the new sorted index that references into our storage vectors.
  146. m_sortedSurfacePointIndices[surfacePointInsertIndex] = m_surfacePositionList.size();
  147. // Add the new point to the back of our storage vectors.
  148. m_surfacePositionList.emplace_back(position);
  149. m_surfaceNormalList.emplace_back(normal);
  150. m_surfaceWeightsList.emplace_back(masks);
  151. m_surfaceCreatorIdList.emplace_back(entityId);
  152. }
  153. void SurfacePointList::ModifySurfaceWeights(const SurfaceDataRegistryHandle& surfaceModifierHandle)
  154. {
  155. AZ_Assert(m_listIsBeingConstructed, "Trying to modify surface weights on a SurfacePointList that isn't under construction.");
  156. SurfaceDataModifierRequestBus::Event( surfaceModifierHandle,
  157. &SurfaceDataModifierRequestBus::Events::ModifySurfacePoints,
  158. m_surfacePositionList, m_surfaceCreatorIdList, m_surfaceWeightsList);
  159. }
  160. void SurfacePointList::FilterPoints(AZStd::span<const SurfaceTag> desiredTags)
  161. {
  162. AZ_Assert(m_listIsBeingConstructed, "Trying to filter a SurfacePointList that isn't under construction.");
  163. // Filter out any points that don't match our search tags.
  164. // This has to be done after the Surface Modifiers have processed the points, not at point insertion time, because
  165. // Surface Modifiers add tags to existing points.
  166. // The algorithm below is basically an "erase_if" that's operating across multiple storage vectors and using one level of
  167. // indirection to keep our sorted indices valid.
  168. // At some point we might want to consider modifying this to compact the final storage to the minimum needed.
  169. for (size_t inputIndex = 0; (inputIndex < m_inputPositionSize); inputIndex++)
  170. {
  171. size_t surfacePointStartIndex = GetSurfacePointStartIndexFromInPositionIndex(inputIndex);
  172. size_t listSize = (surfacePointStartIndex + m_numSurfacePointsPerInput[inputIndex]);
  173. size_t index = surfacePointStartIndex;
  174. for (; index < listSize; index++)
  175. {
  176. if (!m_surfaceWeightsList[m_sortedSurfacePointIndices[index]].HasAnyMatchingTags(desiredTags))
  177. {
  178. break;
  179. }
  180. }
  181. if (index != listSize)
  182. {
  183. size_t next = index + 1;
  184. for (; next < listSize; ++next)
  185. {
  186. if (m_surfaceWeightsList[m_sortedSurfacePointIndices[next]].HasAnyMatchingTags(desiredTags))
  187. {
  188. m_sortedSurfacePointIndices[index] = AZStd::move(m_sortedSurfacePointIndices[next]);
  189. m_surfaceCreatorIdList[m_sortedSurfacePointIndices[index]] =
  190. AZStd::move(m_surfaceCreatorIdList[m_sortedSurfacePointIndices[next]]);
  191. m_surfacePositionList[m_sortedSurfacePointIndices[index]] =
  192. AZStd::move(m_surfacePositionList[m_sortedSurfacePointIndices[next]]);
  193. m_surfaceNormalList[m_sortedSurfacePointIndices[index]] =
  194. AZStd::move(m_surfaceNormalList[m_sortedSurfacePointIndices[next]]);
  195. m_surfaceWeightsList[m_sortedSurfacePointIndices[index]] =
  196. AZStd::move(m_surfaceWeightsList[m_sortedSurfacePointIndices[next]]);
  197. ++index;
  198. }
  199. }
  200. m_numSurfacePointsPerInput[inputIndex] = index - surfacePointStartIndex;
  201. }
  202. }
  203. }
  204. void SurfacePointList::EndListConstruction()
  205. {
  206. AZ_Assert(m_listIsBeingConstructed, "Trying to end list construction on a SurfacePointList that isn't under construction.");
  207. // Now that we've finished adding and modifying points, filter out any points that don't match the filterTags list, if we have one.
  208. if (!m_filterTags.empty())
  209. {
  210. FilterPoints(m_filterTags);
  211. }
  212. m_listIsBeingConstructed = false;
  213. m_inputPositions = {};
  214. m_filterTags = {};
  215. }
  216. bool SurfacePointList::IsEmpty() const
  217. {
  218. AZ_Assert(!m_listIsBeingConstructed, "Trying to query a SurfacePointList that's still under construction.");
  219. return m_surfacePositionList.empty();
  220. }
  221. bool SurfacePointList::IsEmpty(size_t inputPositionIndex) const
  222. {
  223. AZ_Assert(!m_listIsBeingConstructed, "Trying to query a SurfacePointList that's still under construction.");
  224. return (m_inputPositionSize == 0) || (m_numSurfacePointsPerInput[inputPositionIndex] == 0);
  225. }
  226. size_t SurfacePointList::GetSize() const
  227. {
  228. AZ_Assert(!m_listIsBeingConstructed, "Trying to query a SurfacePointList that's still under construction.");
  229. size_t numValidEntries = 0;
  230. for (size_t inputIndex = 0; (inputIndex < m_inputPositionSize); inputIndex++)
  231. {
  232. numValidEntries += m_numSurfacePointsPerInput[inputIndex];
  233. }
  234. return numValidEntries;
  235. }
  236. size_t SurfacePointList::GetSize(size_t inputPositionIndex) const
  237. {
  238. AZ_Assert(!m_listIsBeingConstructed, "Trying to query a SurfacePointList that's still under construction.");
  239. return (m_inputPositionSize == 0) ? 0 : (m_numSurfacePointsPerInput[inputPositionIndex]);
  240. }
  241. void SurfacePointList::EnumeratePoints(
  242. size_t inputPositionIndex,
  243. AZStd::function<bool(const AZ::Vector3&, const AZ::Vector3&, const SurfaceData::SurfaceTagWeights&)>
  244. pointCallback) const
  245. {
  246. AZ_Assert(!m_listIsBeingConstructed, "Trying to query a SurfacePointList that's still under construction.");
  247. size_t surfacePointStartIndex = GetSurfacePointStartIndexFromInPositionIndex(inputPositionIndex);
  248. for (size_t index = surfacePointStartIndex;
  249. (index < (surfacePointStartIndex + m_numSurfacePointsPerInput[inputPositionIndex])); index++)
  250. {
  251. if (!pointCallback(
  252. m_surfacePositionList[m_sortedSurfacePointIndices[index]], m_surfaceNormalList[m_sortedSurfacePointIndices[index]],
  253. m_surfaceWeightsList[m_sortedSurfacePointIndices[index]]))
  254. {
  255. break;
  256. }
  257. }
  258. }
  259. void SurfacePointList::EnumeratePoints(
  260. AZStd::function<bool(size_t inputPositionIndex, const AZ::Vector3&, const AZ::Vector3&, const SurfaceData::SurfaceTagWeights&)>
  261. pointCallback) const
  262. {
  263. AZ_Assert(!m_listIsBeingConstructed, "Trying to query a SurfacePointList that's still under construction.");
  264. for (size_t inputIndex = 0; (inputIndex < m_inputPositionSize); inputIndex++)
  265. {
  266. size_t surfacePointStartIndex = GetSurfacePointStartIndexFromInPositionIndex(inputIndex);
  267. for (size_t index = surfacePointStartIndex; (index < (surfacePointStartIndex + m_numSurfacePointsPerInput[inputIndex]));
  268. index++)
  269. {
  270. if (!pointCallback(
  271. inputIndex, m_surfacePositionList[m_sortedSurfacePointIndices[index]],
  272. m_surfaceNormalList[m_sortedSurfacePointIndices[index]], m_surfaceWeightsList[m_sortedSurfacePointIndices[index]]))
  273. {
  274. break;
  275. }
  276. }
  277. }
  278. }
  279. AzFramework::SurfaceData::SurfacePoint SurfacePointList::GetHighestSurfacePoint([[maybe_unused]] size_t inputPositionIndex) const
  280. {
  281. AZ_Assert(!m_listIsBeingConstructed, "Trying to query a SurfacePointList that's still under construction.");
  282. if (m_numSurfacePointsPerInput[inputPositionIndex] == 0)
  283. {
  284. return {};
  285. }
  286. size_t surfacePointStartIndex = GetSurfacePointStartIndexFromInPositionIndex(inputPositionIndex);
  287. AzFramework::SurfaceData::SurfacePoint point;
  288. point.m_position = m_surfacePositionList[m_sortedSurfacePointIndices[surfacePointStartIndex]];
  289. point.m_normal = m_surfaceNormalList[m_sortedSurfacePointIndices[surfacePointStartIndex]];
  290. point.m_surfaceTags = m_surfaceWeightsList[m_sortedSurfacePointIndices[surfacePointStartIndex]].GetSurfaceTagWeightList();
  291. return point;
  292. }
  293. }