PolygonPrismMeshUtils.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  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 <Editor/PolygonPrismMeshUtils.h>
  9. #include <AzCore/std/containers/unordered_map.h>
  10. #include <AzCore/Math/Vector3.h>
  11. namespace PolygonPrismMeshUtils
  12. {
  13. AZ::Vector3 CalculateAngles(p2t::Triangle& triangle)
  14. {
  15. // Calculate the internal angles of the triangle from the edge lengths using the law of cosines
  16. const float e0 = static_cast<float>((*triangle.GetPoint(1) - *triangle.GetPoint(0)).Length());
  17. const float e1 = static_cast<float>((*triangle.GetPoint(2) - *triangle.GetPoint(1)).Length());
  18. const float e2 = static_cast<float>((*triangle.GetPoint(0) - *triangle.GetPoint(2)).Length());
  19. const float angle0 = acosf(AZ::GetClamp((e0 * e0 + e2 * e2 - e1 * e1) / (2.0f * e0 * e2), -1.0f, 1.0f));
  20. const float angle1 = acosf(AZ::GetClamp((e0 * e0 + e1 * e1 - e2 * e2) / (2.0f * e0 * e1), -1.0f, 1.0f));
  21. return AZ::Vector3(angle0, angle1, AZ::Constants::Pi - angle0 - angle1);
  22. }
  23. bool InternalEdgeCompare::operator()(const InternalEdge& left, const InternalEdge& right) const
  24. {
  25. return left.m_minAngle > right.m_minAngle;
  26. }
  27. void Mesh2D::CreateFromPoly2Tri(const std::vector<p2t::Triangle*>& triangles)
  28. {
  29. const int numTriangles = static_cast<int>(triangles.size());
  30. m_faces.resize(numTriangles);
  31. m_halfEdges.resize(3 * numTriangles);
  32. AZStd::unordered_map<p2t::Triangle*, int> triangleIndexMap;
  33. for (int faceIndex = 0; faceIndex < numTriangles; faceIndex++)
  34. {
  35. p2t::Triangle* triangle = triangles[faceIndex];
  36. triangleIndexMap.emplace(AZStd::make_pair(triangle, faceIndex));
  37. // The index of the first half-edge in this face
  38. const int firstHalfEdgeIndex = 3 * faceIndex;
  39. // Populate the face data
  40. m_faces[faceIndex].m_edge = &m_halfEdges[firstHalfEdgeIndex];
  41. m_faces[faceIndex].m_numEdges = 3;
  42. // Populate the half-edge data, apart from the twin pointers which will require a second pass
  43. const AZ::Vector3 angles = CalculateAngles(*triangle);
  44. for (int edgeIndex = 0; edgeIndex < 3; edgeIndex++)
  45. {
  46. const int nextIndex = (edgeIndex + 1) % 3;
  47. const int prevIndex = (edgeIndex + 2) % 3;
  48. HalfEdge* halfEdge = &m_halfEdges[firstHalfEdgeIndex + edgeIndex];
  49. halfEdge->m_face = &m_faces[faceIndex];
  50. const p2t::Point* point = triangle->GetPoint(edgeIndex);
  51. halfEdge->m_origin = AZ::Vector2(static_cast<float>(point->x), static_cast<float>(point->y));
  52. halfEdge->m_prev = &m_halfEdges[firstHalfEdgeIndex + prevIndex];
  53. halfEdge->m_next = &m_halfEdges[firstHalfEdgeIndex + nextIndex];
  54. halfEdge->m_prevAngle = angles.GetElement(edgeIndex);
  55. halfEdge->m_nextAngle = angles.GetElement(nextIndex);
  56. }
  57. }
  58. // Figure out twin half-edges, and populate the queue of internal edges to consider for removal
  59. for (int faceIndex = 0; faceIndex < numTriangles; faceIndex++)
  60. {
  61. p2t::Triangle* triangle = triangles[faceIndex];
  62. for (int edgeIndex = 0; edgeIndex < 3; edgeIndex++)
  63. {
  64. HalfEdge* halfEdge = &m_halfEdges[3 * faceIndex + edgeIndex];
  65. if (halfEdge->m_visited)
  66. {
  67. // We have already visited this half-edge when considering its twin, so there is nothing to do
  68. continue;
  69. }
  70. p2t::Triangle* twinFace = triangle->NeighborCCW(*triangle->GetPoint(edgeIndex));
  71. if (twinFace == nullptr)
  72. {
  73. // This half-edge doesn't have a twin, so it is an external edge and there is nothing to do
  74. continue;
  75. }
  76. auto triangleIndexPair = triangleIndexMap.find(twinFace);
  77. if (triangleIndexPair == triangleIndexMap.end())
  78. {
  79. // Poly2tri can have triangles outside of the polygon, so it is possible for them not to be
  80. // found in the face map, but in this case we should do nothing
  81. continue;
  82. }
  83. const int twinFaceIndex = triangleIndexPair->second;
  84. if (twinFaceIndex >= numTriangles)
  85. {
  86. AZ_Error("PolygonPrismMeshUtils", false, "Invalid triangle index.");
  87. continue;
  88. }
  89. const int nextIndex = (edgeIndex + 1) % 3;
  90. const int twinEdgeIndex = twinFace->Index(triangle->GetPoint(nextIndex));
  91. HalfEdge* twinHalfEdge = &m_halfEdges[3 * twinFaceIndex + twinEdgeIndex];
  92. halfEdge->m_twin = twinHalfEdge;
  93. halfEdge->m_visited = true;
  94. twinHalfEdge->m_twin = halfEdge;
  95. twinHalfEdge->m_visited = true;
  96. InternalEdge internalEdge;
  97. internalEdge.m_edges[0] = halfEdge;
  98. internalEdge.m_edges[1] = twinHalfEdge;
  99. internalEdge.m_minAngle = AZStd::GetMin(
  100. AZStd::GetMin(halfEdge->m_prevAngle, halfEdge->m_nextAngle),
  101. AZStd::GetMin(twinHalfEdge->m_prevAngle, twinHalfEdge->m_nextAngle));
  102. m_edgeQueue.push(internalEdge);
  103. }
  104. }
  105. }
  106. void Mesh2D::CreateFromSimpleConvexPolygon(const AZStd::vector<AZ::Vector2>& vertices)
  107. {
  108. const int numVertices = static_cast<int>(vertices.size());
  109. m_faces.resize(1);
  110. m_halfEdges.resize(numVertices);
  111. m_faces[0].m_edge = &m_halfEdges[0];
  112. m_faces[0].m_numEdges = numVertices;
  113. for (int edgeIndex = 0; edgeIndex < numVertices; edgeIndex++)
  114. {
  115. const int nextIndex = (edgeIndex + 1) % numVertices;
  116. const int prevIndex = (edgeIndex + numVertices - 1) % numVertices;
  117. m_halfEdges[edgeIndex].m_face = &m_faces[0];
  118. m_halfEdges[edgeIndex].m_origin = vertices[edgeIndex];
  119. m_halfEdges[edgeIndex].m_prev = &m_halfEdges[prevIndex];
  120. m_halfEdges[edgeIndex].m_next = &m_halfEdges[nextIndex];
  121. }
  122. }
  123. void Mesh2D::RemoveInternalEdge(const InternalEdge& internalEdge)
  124. {
  125. HalfEdge* halfEdge0 = internalEdge.m_edges[0];
  126. HalfEdge* halfEdge1 = internalEdge.m_edges[1];
  127. Face* faceToKeep = halfEdge0->m_face;
  128. Face* faceToRemove = halfEdge1->m_face;
  129. const float newAngle0 = halfEdge0->m_prevAngle + halfEdge1->m_nextAngle;
  130. const float newAngle1 = halfEdge0->m_nextAngle + halfEdge1->m_prevAngle;
  131. const int newNumEdges = halfEdge0->m_face->m_numEdges + halfEdge1->m_face->m_numEdges - 2;
  132. faceToKeep->m_numEdges = newNumEdges;
  133. faceToRemove->m_removed = true;
  134. // Make sure the face doesn't point to the half-edge that will be removed.
  135. if (faceToKeep->m_edge == internalEdge.m_edges[0])
  136. {
  137. faceToKeep->m_edge = internalEdge.m_edges[0]->m_next;
  138. }
  139. // Make all the half-edges in the face that will be removed point to the kept face instead.
  140. HalfEdge* currentEdge = internalEdge.m_edges[1];
  141. for (int edgeIndex = 0; edgeIndex < faceToRemove->m_numEdges; edgeIndex++)
  142. {
  143. currentEdge->m_face = faceToKeep;
  144. currentEdge = currentEdge->m_next;
  145. }
  146. // Update the previous and next half-edge pointers and angles for the 4 half-edges adjacent
  147. // to the edge that will be removed.
  148. halfEdge0->m_prev->m_dirty = true;
  149. halfEdge0->m_next->m_dirty = true;
  150. halfEdge1->m_prev->m_dirty = true;
  151. halfEdge1->m_next->m_dirty = true;
  152. halfEdge0->m_prev->m_next = halfEdge1->m_next;
  153. halfEdge0->m_next->m_prev = halfEdge1->m_prev;
  154. halfEdge1->m_prev->m_next = halfEdge0->m_next;
  155. halfEdge1->m_next->m_prev = halfEdge0->m_prev;
  156. halfEdge0->m_prev->m_nextAngle = newAngle0;
  157. halfEdge0->m_next->m_prevAngle = newAngle1;
  158. halfEdge1->m_prev->m_nextAngle = newAngle1;
  159. halfEdge1->m_next->m_prevAngle = newAngle0;
  160. }
  161. size_t Mesh2D::ConvexMerge()
  162. {
  163. size_t numFacesRemoved = 0;
  164. while (!m_edgeQueue.empty())
  165. {
  166. const InternalEdge& internalEdge = m_edgeQueue.top();
  167. if (internalEdge.m_edges[0] != nullptr && internalEdge.m_edges[1] != nullptr)
  168. {
  169. // If either of the half-edges are marked dirty due to previously removing adjacent edges,
  170. // update the edge and place back into the queue.
  171. if (internalEdge.m_edges[0]->m_dirty || internalEdge.m_edges[1]->m_dirty)
  172. {
  173. InternalEdge updatedInternalEdge(internalEdge);
  174. updatedInternalEdge.m_minAngle = AZStd::GetMin(
  175. AZStd::GetMin(internalEdge.m_edges[0]->m_prevAngle, internalEdge.m_edges[0]->m_nextAngle),
  176. AZStd::GetMin(internalEdge.m_edges[1]->m_prevAngle, internalEdge.m_edges[1]->m_nextAngle));
  177. internalEdge.m_edges[0]->m_dirty = false;
  178. internalEdge.m_edges[1]->m_dirty = false;
  179. m_edgeQueue.pop();
  180. m_edgeQueue.push(updatedInternalEdge);
  181. }
  182. else
  183. {
  184. // There are two conditions that need to be satisfied in order to allow removing this edge.
  185. // First, the merged face should remain convex, i.e. the two new internal angles which would be
  186. // created must both be less than 180 degrees.
  187. // Secondly, the merged polygon must meet the PhysX vertex limit for convex meshes.
  188. HalfEdge* halfEdge0 = internalEdge.m_edges[0];
  189. HalfEdge* halfEdge1 = internalEdge.m_edges[1];
  190. const float newAngle0 = halfEdge0->m_prevAngle + halfEdge1->m_nextAngle;
  191. const float newAngle1 = halfEdge0->m_nextAngle + halfEdge1->m_prevAngle;
  192. const int newNumEdges = halfEdge0->m_face->m_numEdges + halfEdge1->m_face->m_numEdges - 2;
  193. // Allow angles very slightly larger than 180 degrees to avoid unnecessary splitting due to
  194. // precision issues.
  195. const float epsilon = 1e-5f;
  196. const float maxAngle = AZ::Constants::Pi + epsilon;
  197. if (newAngle0 < maxAngle &&
  198. newAngle1 < maxAngle &&
  199. newNumEdges <= MaxPolygonPrismEdges)
  200. {
  201. RemoveInternalEdge(internalEdge);
  202. numFacesRemoved++;
  203. }
  204. m_edgeQueue.pop();
  205. }
  206. }
  207. else
  208. {
  209. AZ_Error("PhysX Shape Collider Component", false, "Invalid half-edge.");
  210. m_edgeQueue.pop();
  211. }
  212. }
  213. return numFacesRemoved;
  214. }
  215. const AZStd::vector<Face>& Mesh2D::GetFaces() const
  216. {
  217. return m_faces;
  218. }
  219. const AZStd::priority_queue<InternalEdge, AZStd::vector<InternalEdge>, InternalEdgeCompare>& Mesh2D::GetInternalEdges() const
  220. {
  221. return m_edgeQueue;
  222. }
  223. const AZStd::vector<AZ::Vector3>& Mesh2D::GetDebugDrawPoints(float height, const AZ::Vector3& nonUniformScale) const
  224. {
  225. if (m_debugDrawDirty)
  226. {
  227. m_debugDrawPoints.clear();
  228. for (const auto& face : m_faces)
  229. {
  230. if (!face.m_removed)
  231. {
  232. int numEdges = face.m_numEdges;
  233. PolygonPrismMeshUtils::HalfEdge* currentEdge = face.m_edge;
  234. for (int edgeIndex = 0; edgeIndex < numEdges; edgeIndex++)
  235. {
  236. PolygonPrismMeshUtils::HalfEdge* nextEdge = currentEdge->m_next;
  237. const auto v1 = nonUniformScale * AZ::Vector3(currentEdge->m_origin.GetX(), currentEdge->m_origin.GetY(), 0.0f);
  238. const auto v2 = nonUniformScale * AZ::Vector3(currentEdge->m_origin.GetX(), currentEdge->m_origin.GetY(), height);
  239. const auto v3 = nonUniformScale * AZ::Vector3(nextEdge->m_origin.GetX(), nextEdge->m_origin.GetY(), 0.0f);
  240. const auto v4 = nonUniformScale * AZ::Vector3(nextEdge->m_origin.GetX(), nextEdge->m_origin.GetY(), height);
  241. m_debugDrawPoints.insert(m_debugDrawPoints.end(), { v1, v2, v1, v3, v2, v4 });
  242. currentEdge = currentEdge->m_next;
  243. }
  244. }
  245. }
  246. m_debugDrawDirty = false;
  247. }
  248. return m_debugDrawPoints;
  249. }
  250. void Mesh2D::SetDebugDrawDirty()
  251. {
  252. m_debugDrawDirty = true;
  253. }
  254. void Mesh2D::Clear()
  255. {
  256. m_halfEdges.clear();
  257. m_faces.clear();
  258. m_edgeQueue = AZStd::priority_queue<InternalEdge, AZStd::vector<InternalEdge>, InternalEdgeCompare>();
  259. m_debugDrawDirty = true;
  260. }
  261. } // namespace PolygonPrismMeshUtils