VertexCollapseMesh.h 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517
  1. // David Eberly, Geometric Tools, Redmond WA 98052
  2. // Copyright (c) 1998-2020
  3. // Distributed under the Boost Software License, Version 1.0.
  4. // https://www.boost.org/LICENSE_1_0.txt
  5. // https://www.geometrictools.com/License/Boost/LICENSE_1_0.txt
  6. // Version: 4.0.2019.08.13
  7. #pragma once
  8. #include <Mathematics/MinHeap.h>
  9. #include <Mathematics/Polygon2.h>
  10. #include <Mathematics/TriangulateEC.h>
  11. #include <Mathematics/Vector3.h>
  12. #include <Mathematics/VETManifoldMesh.h>
  13. #include <set>
  14. namespace WwiseGTE
  15. {
  16. template <typename Real>
  17. class VertexCollapseMesh
  18. {
  19. public:
  20. // Construction.
  21. VertexCollapseMesh(int numPositions, Vector3<Real> const* positions,
  22. int numIndices, int const* indices)
  23. :
  24. mNumPositions(numPositions),
  25. mPositions(positions),
  26. mMesh(VCVertex::Create)
  27. {
  28. if (numPositions <= 0 || !positions || numIndices < 3 || !indices)
  29. {
  30. mNumPositions = 0;
  31. mPositions = nullptr;
  32. return;
  33. }
  34. // Build the manifold mesh from the inputs.
  35. int numTriangles = numIndices / 3;
  36. int const* current = indices;
  37. for (int t = 0; t < numTriangles; ++t)
  38. {
  39. int v0 = *current++;
  40. int v1 = *current++;
  41. int v2 = *current++;
  42. mMesh.Insert(v0, v1, v2);
  43. }
  44. // Locate the vertices (if any) on the mesh boundary.
  45. auto const& vmap = mMesh.GetVertices();
  46. for (auto const& eelement : mMesh.GetEdges())
  47. {
  48. auto edge = eelement.second;
  49. if (!edge->T[1].lock())
  50. {
  51. for (int i = 0; i < 2; ++i)
  52. {
  53. auto velement = vmap.find(edge->V[i]);
  54. auto vertex = std::static_pointer_cast<VCVertex>(velement->second);
  55. vertex->isBoundary = true;
  56. }
  57. }
  58. }
  59. // Build the priority queue of weights for the interior vertices.
  60. mMinHeap.Reset((int)vmap.size());
  61. for (auto const& velement : vmap)
  62. {
  63. auto vertex = std::static_pointer_cast<VCVertex>(velement.second);
  64. Real weight;
  65. if (vertex->isBoundary)
  66. {
  67. weight = std::numeric_limits<Real>::max();
  68. }
  69. else
  70. {
  71. weight = vertex->ComputeWeight(mPositions);
  72. }
  73. auto record = mMinHeap.Insert(velement.first, weight);
  74. mHeapRecords.insert(std::make_pair(velement.first, record));
  75. }
  76. }
  77. // Decimate the mesh using vertex collapses
  78. struct Record
  79. {
  80. // The index of the interior vertex that is removed from the mesh.
  81. // The triangles adjacent to the vertex are 'removed' from the
  82. // mesh. The polygon boundary of the adjacent triangles is
  83. // triangulated and the new triangles are 'inserted' into the
  84. // mesh.
  85. int vertex;
  86. std::vector<TriangleKey<true>> removed;
  87. std::vector<TriangleKey<true>> inserted;
  88. };
  89. // Return 'true' when a vertex collapse occurs. Once the function
  90. // returns 'false', no more vertex collapses are allowed so you may
  91. // then stop calling the function. The implementation has several
  92. // consistency tests that should not fail with a theoretically correct
  93. // implementation. If a test fails, the function returns 'false' and
  94. // the record.vertex is set to the invalid integer 0x80000000. When
  95. // the Logger system is enabled, the failed tests are reported to any
  96. // Logger listeners.
  97. bool DoCollapse(Record& record)
  98. {
  99. record.vertex = 0x80000000;
  100. record.removed.clear();
  101. record.inserted.clear();
  102. if (mNumPositions == 0)
  103. {
  104. // The constructor failed, so there is nothing to collapse.
  105. return false;
  106. }
  107. while (mMinHeap.GetNumElements() > 0)
  108. {
  109. int v = -1;
  110. Real weight = std::numeric_limits<Real>::max();
  111. mMinHeap.GetMinimum(v, weight);
  112. if (weight == std::numeric_limits<Real>::max())
  113. {
  114. // There are no more interior vertices to collapse.
  115. return false;
  116. }
  117. auto const& vmap = mMesh.GetVertices();
  118. auto velement = vmap.find(v);
  119. if (velement == vmap.end())
  120. {
  121. // Unexpected condition.
  122. return false;
  123. }
  124. auto vertex = std::static_pointer_cast<VCVertex>(velement->second);
  125. std::vector<TriangleKey<true>> removed, inserted;
  126. std::vector<int> linkVertices;
  127. int result = TriangulateLink(vertex, removed, inserted, linkVertices);
  128. if (result == VCM_UNEXPECTED_ERROR)
  129. {
  130. return false;
  131. }
  132. if (result == VCM_ALLOWED)
  133. {
  134. result = Collapsed(removed, inserted, linkVertices);
  135. if (result == VCM_UNEXPECTED_ERROR)
  136. {
  137. return false;
  138. }
  139. if (result == VCM_ALLOWED)
  140. {
  141. // Remove the vertex and associated weight.
  142. mMinHeap.Remove(v, weight);
  143. mHeapRecords.erase(v);
  144. // Update the weights of the link vertices.
  145. for (auto vlink : linkVertices)
  146. {
  147. velement = vmap.find(vlink);
  148. if (velement == vmap.end())
  149. {
  150. // Unexpected condition.
  151. return false;
  152. }
  153. vertex = std::static_pointer_cast<VCVertex>(velement->second);
  154. if (!vertex->isBoundary)
  155. {
  156. auto iter = mHeapRecords.find(vlink);
  157. if (iter == mHeapRecords.end())
  158. {
  159. // Unexpected condition.
  160. return false;
  161. }
  162. weight = vertex->ComputeWeight(mPositions);
  163. mMinHeap.Update(iter->second, weight);
  164. }
  165. }
  166. record.vertex = v;
  167. record.removed = std::move(removed);
  168. record.inserted = std::move(inserted);
  169. return true;
  170. }
  171. // else: result == VCM_DEFERRED
  172. }
  173. // To get here, result must be VCM_DEFERRED. The vertex
  174. // collapse would cause mesh fold-over. Temporarily set the
  175. // edge weight to infinity. After removal of other triangles,
  176. // the vertex weight will be updated to a finite value and the
  177. // vertex possibly can be removed at that time.
  178. auto iter = mHeapRecords.find(v);
  179. if (iter == mHeapRecords.end())
  180. {
  181. // Unexpected condition.
  182. return false;
  183. }
  184. mMinHeap.Update(iter->second, std::numeric_limits<Real>::max());
  185. }
  186. // We do not expect to reach this line of code, even for a closed
  187. // mesh. However, the compiler does not know this, yet requires
  188. // a return value.
  189. return false;
  190. }
  191. // Access the current state of the mesh, whether the original built
  192. // in the constructor or a decimated mesh during DoCollapse calls.
  193. inline ETManifoldMesh const& GetMesh() const
  194. {
  195. return mMesh;
  196. }
  197. private:
  198. struct VCVertex : public VETManifoldMesh::Vertex
  199. {
  200. VCVertex(int v)
  201. :
  202. VETManifoldMesh::Vertex(v),
  203. normal(Vector3<Real>::Zero()),
  204. isBoundary(false)
  205. {
  206. }
  207. static std::shared_ptr<Vertex> Create(int v)
  208. {
  209. return std::make_shared<VCVertex>(v);
  210. }
  211. // The weight depends on the area of the triangles sharing the
  212. // vertex and the lengths of the projections of the adjacent
  213. // vertices onto the vertex normal line. A side effect of the
  214. // call is that the vertex normal is computed and stored.
  215. Real ComputeWeight(Vector3<Real> const* positions)
  216. {
  217. Real weight = (Real)0;
  218. normal = { (Real)0, (Real)0, (Real)0 };
  219. for (auto const& tri : TAdjacent)
  220. {
  221. Vector3<Real> E0 = positions[tri->V[1]] - positions[tri->V[0]];
  222. Vector3<Real> E1 = positions[tri->V[2]] - positions[tri->V[0]];
  223. Vector3<Real> N = Cross(E0, E1);
  224. normal += N;
  225. weight += Length(N);
  226. }
  227. Normalize(normal);
  228. for (int index : VAdjacent)
  229. {
  230. Vector3<Real> diff = positions[index] - positions[V];
  231. weight += std::fabs(Dot(normal, diff));
  232. }
  233. return weight;
  234. }
  235. Vector3<Real> normal;
  236. bool isBoundary;
  237. };
  238. // The functions TriangulateLink and Collapsed return one of the
  239. // enumerates described next.
  240. //
  241. // VCM_NO_MORE_ALLOWED:
  242. // Either the mesh has no more interior vertices or a collapse
  243. // will lead to a mesh fold-over or to a nonmanifold mesh. The
  244. // returned value 'v' is invalid (0x80000000) and 'removed' and
  245. // 'inserted' are empty.
  246. //
  247. // VCM_ALLOWED:
  248. // An interior vertex v has been removed. This is allowed using
  249. // the following algorithm. The vertex normal is the weighted
  250. // average of non-unit-length normals of triangles sharing v. The
  251. // weights are the triangle areas. The adjacent vertices are
  252. // projected onto a plane containing v and having normal equal to
  253. // the vertex normal. If the projection is a simple polygon in
  254. // the plane, the collapse is allowed. The triangles sharing v
  255. // are 'removed', the polygon is triangulated, and the new
  256. // triangles are 'inserted' into the mesh.
  257. //
  258. // VCM_DEFERRED:
  259. // If the projection polygon described in the previous case is not
  260. // simple (at least one pair of edges overlaps at some
  261. // edge-interior point), the collapse would produce a fold-over in
  262. // the mesh. We do not collapse in this case. It is possible
  263. // that such a vertex occurs in a later collapse as its neighbors
  264. // are adjusted by collapses. When this case occurs, v is valid
  265. // (even though the collapse was not allowed) but 'removed' and
  266. // 'inserted' are empty.
  267. //
  268. // VCM_UNEXPECTED_ERROR:
  269. // The code has several tests for conditions that are not expected
  270. // to occur for a theoretically correct implementation. If you
  271. // receive this error, file a bug report and provide a data set
  272. // that caused the error.
  273. enum
  274. {
  275. VCM_NO_MORE_ALLOWED,
  276. VCM_ALLOWED,
  277. VCM_DEFERRED,
  278. VCM_UNEXPECTED_ERROR
  279. };
  280. int TriangulateLink(std::shared_ptr<VCVertex> const& vertex, std::vector<TriangleKey<true>>& removed,
  281. std::vector<TriangleKey<true>>& inserted, std::vector<int>& linkVertices) const
  282. {
  283. // Create the (CCW) polygon boundary of the link of the vertex.
  284. // The incoming vertex is interior, so the number of triangles
  285. // sharing the vertex is equal to the number of vertices of the
  286. // polygon. A precondition of the function call is that the
  287. // vertex normal has already been computed.
  288. // Get the edges of the link that are opposite the incoming
  289. // vertex.
  290. int const numVertices = static_cast<int>(vertex->TAdjacent.size());
  291. removed.resize(numVertices);
  292. int j = 0;
  293. std::map<int, int> edgeMap;
  294. for (auto tri : vertex->TAdjacent)
  295. {
  296. for (int i = 0; i < 3; ++i)
  297. {
  298. if (tri->V[i] == vertex->V)
  299. {
  300. edgeMap.insert(std::make_pair(tri->V[(i + 1) % 3], tri->V[(i + 2) % 3]));
  301. break;
  302. }
  303. }
  304. removed[j++] = TriangleKey<true>(tri->V[0], tri->V[1], tri->V[2]);
  305. }
  306. if (edgeMap.size() != vertex->TAdjacent.size())
  307. {
  308. return VCM_UNEXPECTED_ERROR;
  309. }
  310. // Connect the edges into a polygon.
  311. linkVertices.resize(numVertices);
  312. auto iter = edgeMap.begin();
  313. for (int i = 0; i < numVertices; ++i)
  314. {
  315. linkVertices[i] = iter->first;
  316. iter = edgeMap.find(iter->second);
  317. if (iter == edgeMap.end())
  318. {
  319. return VCM_UNEXPECTED_ERROR;
  320. }
  321. }
  322. if (iter->first != linkVertices[0])
  323. {
  324. return VCM_UNEXPECTED_ERROR;
  325. }
  326. // Project the polygon onto the plane containing the incoming
  327. // vertex and having the vertex normal. The projected polygon
  328. // is computed so that the incoming vertex is projected to (0,0).
  329. Vector3<Real> center = mPositions[vertex->V];
  330. Vector3<Real> basis[3];
  331. basis[0] = vertex->normal;
  332. ComputeOrthogonalComplement(1, basis);
  333. std::vector<Vector2<Real>> projected(numVertices);
  334. std::vector<int> indices(numVertices);
  335. for (int i = 0; i < numVertices; ++i)
  336. {
  337. Vector3<Real> diff = mPositions[linkVertices[i]] - center;
  338. projected[i][0] = Dot(basis[1], diff);
  339. projected[i][1] = Dot(basis[2], diff);
  340. indices[i] = i;
  341. }
  342. // The polygon must be simple in order to triangulate it.
  343. Polygon2<Real> polygon(projected.data(), numVertices, indices.data(), true);
  344. if (polygon.IsSimple())
  345. {
  346. TriangulateEC<Real, Real> triangulator(numVertices, projected.data());
  347. triangulator();
  348. auto const& triangles = triangulator.GetTriangles();
  349. if (triangles.size() == 0)
  350. {
  351. return VCM_UNEXPECTED_ERROR;
  352. }
  353. int const numTriangles = static_cast<int>(triangles.size());
  354. inserted.resize(numTriangles);
  355. for (int t = 0; t < numTriangles; ++t)
  356. {
  357. inserted[t] = TriangleKey<true>(
  358. linkVertices[triangles[t][0]],
  359. linkVertices[triangles[t][1]],
  360. linkVertices[triangles[t][2]]);
  361. }
  362. return VCM_ALLOWED;
  363. }
  364. else
  365. {
  366. return VCM_DEFERRED;
  367. }
  368. }
  369. int Collapsed(std::vector<TriangleKey<true>> const& removed,
  370. std::vector<TriangleKey<true>> const& inserted, std::vector<int> const& linkVertices)
  371. {
  372. // The triangles that were disconnected from the link edges are
  373. // guaranteed to allow manifold reconnection to 'inserted'
  374. // triangles. On the insertion, each diagonal of the link becomes
  375. // a mesh edge and shares two (link) triangles. It is possible
  376. // that the mesh already contains the (diagonal) edge, which will
  377. // lead to a nonmanifold connection, which we cannot allow. The
  378. // following code traps this condition and restores the mesh to
  379. // its state before the 'Remove(...)' call.
  380. bool isCollapsible = true;
  381. auto const& emap = mMesh.GetEdges();
  382. std::set<EdgeKey<false>> edges;
  383. for (auto const& tri : inserted)
  384. {
  385. for (int k0 = 2, k1 = 0; k1 < 3; k0 = k1++)
  386. {
  387. EdgeKey<false> edge(tri.V[k0], tri.V[k1]);
  388. if (edges.find(edge) == edges.end())
  389. {
  390. edges.insert(edge);
  391. }
  392. else
  393. {
  394. // The edge has been visited twice, so it is a
  395. // diagonal of the link.
  396. auto eelement = emap.find(edge);
  397. if (eelement != emap.end())
  398. {
  399. if (eelement->second->T[1].lock())
  400. {
  401. // The edge will not allow a manifold
  402. // connection.
  403. isCollapsible = false;
  404. break;
  405. }
  406. }
  407. edges.erase(edge);
  408. }
  409. };
  410. if (!isCollapsible)
  411. {
  412. return VCM_DEFERRED;
  413. }
  414. }
  415. // Remove the old triangle neighborhood, which will lead to the
  416. // vertex itself being removed from the mesh.
  417. for (auto tri : removed)
  418. {
  419. mMesh.Remove(tri.V[0], tri.V[1], tri.V[2]);
  420. }
  421. // Insert the new triangulation.
  422. for (auto const& tri : inserted)
  423. {
  424. mMesh.Insert(tri.V[0], tri.V[1], tri.V[2]);
  425. }
  426. // If the Remove(...) calls remove a boundary vertex that is in
  427. // the link vertices, the Insert(...) calls will insert the
  428. // boundary vertex again. We must re-tag those boundary
  429. // vertices.
  430. auto const& vmap = mMesh.GetVertices();
  431. size_t const numVertices = linkVertices.size();
  432. for (size_t i0 = numVertices - 1, i1 = 0; i1 < numVertices; i0 = i1++)
  433. {
  434. EdgeKey<false> ekey(linkVertices[i0], linkVertices[i1]);
  435. auto eelement = emap.find(ekey);
  436. if (eelement == emap.end())
  437. {
  438. return VCM_UNEXPECTED_ERROR;
  439. }
  440. auto edge = eelement->second;
  441. if (!edge)
  442. {
  443. return VCM_UNEXPECTED_ERROR;
  444. }
  445. if (edge->T[0].lock() && !edge->T[1].lock())
  446. {
  447. for (int k = 0; k < 2; ++k)
  448. {
  449. auto velement = vmap.find(edge->V[k]);
  450. if (velement == vmap.end())
  451. {
  452. return VCM_UNEXPECTED_ERROR;
  453. }
  454. auto vertex = std::static_pointer_cast<VCVertex>(velement->second);
  455. vertex->isBoundary = true;
  456. }
  457. }
  458. }
  459. return VCM_ALLOWED;
  460. }
  461. int mNumPositions;
  462. Vector3<Real> const* mPositions;
  463. VETManifoldMesh mMesh;
  464. MinHeap<int, Real> mMinHeap;
  465. std::map<int, typename MinHeap<int, Real>::Record*> mHeapRecords;
  466. };
  467. }