mikktspace.hh 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839
  1. /* SPDX-FileCopyrightText: 2011 Morten S. Mikkelsen
  2. * SPDX-FileCopyrightText: 2022 Blender Authors
  3. *
  4. * SPDX-License-Identifier: Apache-2.0 */
  5. /** \file
  6. * \ingroup mikktspace
  7. */
  8. #include <array>
  9. #include <algorithm>
  10. #include <cassert>
  11. #include <unordered_map>
  12. #ifdef WITH_TBB
  13. # include <tbb/parallel_for.h>
  14. #endif
  15. typedef uint32_t uint;
  16. #include "mikk_atomic_hash_set.hh"
  17. #include "mikk_float3.hh"
  18. #include "mikk_util.hh"
  19. namespace mikk {
  20. static constexpr uint UNSET_ENTRY = 0xffffffffu;
  21. template<typename Mesh> class Mikktspace {
  22. struct Triangle {
  23. /* Stores neighboring triangle for group assignment. */
  24. std::array<uint, 3> neighbor;
  25. /* Stores assigned group of each vertex. */
  26. std::array<uint, 3> group;
  27. /* Stores vertex indices that make up the triangle. */
  28. std::array<uint, 3> vertices;
  29. /* Computed face tangent, will be accumulated into group. */
  30. float3 tangent;
  31. /* Index of the face that this triangle belongs to. */
  32. uint faceIdx;
  33. /* Index of the first of this triangle's vertices' TSpaces. */
  34. uint tSpaceIdx;
  35. /* Stores mapping from this triangle's vertices to the original
  36. * face's vertices (relevant for quads). */
  37. std::array<uint8_t, 3> faceVertex;
  38. // flags
  39. bool markDegenerate : 1;
  40. bool quadOneDegenTri : 1;
  41. bool groupWithAny : 1;
  42. bool orientPreserving : 1;
  43. Triangle(uint faceIdx_, uint tSpaceIdx_)
  44. : tangent{0.0f},
  45. faceIdx{faceIdx_},
  46. tSpaceIdx{tSpaceIdx_},
  47. markDegenerate{false},
  48. quadOneDegenTri{false},
  49. groupWithAny{true},
  50. orientPreserving{false}
  51. {
  52. neighbor.fill(UNSET_ENTRY);
  53. group.fill(UNSET_ENTRY);
  54. }
  55. void setVertices(uint8_t i0, uint8_t i1, uint8_t i2)
  56. {
  57. faceVertex[0] = i0;
  58. faceVertex[1] = i1;
  59. faceVertex[2] = i2;
  60. vertices[0] = pack_index(faceIdx, i0);
  61. vertices[1] = pack_index(faceIdx, i1);
  62. vertices[2] = pack_index(faceIdx, i2);
  63. }
  64. };
  65. struct Group {
  66. float3 tangent;
  67. uint vertexRepresentative;
  68. bool orientPreserving;
  69. Group(uint vertexRepresentative_, bool orientPreserving_)
  70. : tangent{0.0f},
  71. vertexRepresentative{vertexRepresentative_},
  72. orientPreserving{orientPreserving_}
  73. {
  74. }
  75. void normalizeTSpace()
  76. {
  77. tangent = tangent.normalize();
  78. }
  79. void accumulateTSpaceAtomic(float3 v_tangent)
  80. {
  81. float_add_atomic(&tangent.x, v_tangent.x);
  82. float_add_atomic(&tangent.y, v_tangent.y);
  83. float_add_atomic(&tangent.z, v_tangent.z);
  84. }
  85. void accumulateTSpace(float3 v_tangent)
  86. {
  87. tangent += v_tangent;
  88. }
  89. };
  90. struct TSpace {
  91. float3 tangent = float3(1.0f, 0.0f, 0.0f);
  92. uint counter = 0;
  93. bool orientPreserving = false;
  94. void accumulateGroup(const Group &group)
  95. {
  96. assert(counter < 2);
  97. if (counter == 0) {
  98. tangent = group.tangent;
  99. }
  100. else if (tangent == group.tangent) {
  101. // this if is important. Due to floating point precision
  102. // averaging when ts0==ts1 will cause a slight difference
  103. // which results in tangent space splits later on, so do nothing
  104. }
  105. else {
  106. tangent = (tangent + group.tangent).normalize();
  107. }
  108. counter++;
  109. orientPreserving = group.orientPreserving;
  110. }
  111. };
  112. Mesh &mesh;
  113. std::vector<Triangle> triangles;
  114. std::vector<TSpace> tSpaces;
  115. std::vector<Group> groups;
  116. uint nrTSpaces, nrFaces, nrTriangles, totalTriangles;
  117. int nrThreads;
  118. bool isParallel;
  119. public:
  120. Mikktspace(Mesh &mesh_) : mesh(mesh_) {}
  121. void genTangSpace()
  122. {
  123. nrFaces = uint(mesh.GetNumFaces());
  124. #ifdef WITH_TBB
  125. nrThreads = tbb::this_task_arena::max_concurrency();
  126. isParallel = (nrThreads > 1) && (nrFaces > 10000);
  127. #else
  128. nrThreads = 1;
  129. isParallel = false;
  130. #endif
  131. // make an initial triangle --> face index list
  132. generateInitialVerticesIndexList();
  133. if (nrTriangles == 0) {
  134. return;
  135. }
  136. // make a welded index list of identical positions and attributes (pos, norm, texc)
  137. generateSharedVerticesIndexList();
  138. // mark all triangle pairs that belong to a quad with only one
  139. // good triangle. These need special treatment in degenEpilogue().
  140. // Additionally, move all good triangles to the start of
  141. // triangles[] without changing order and
  142. // put the degenerate triangles last.
  143. degenPrologue();
  144. if (nrTriangles == 0) {
  145. // No point in building tangents if there are no non-degenerate triangles, so just zero them
  146. tSpaces.resize(nrTSpaces);
  147. }
  148. else {
  149. // evaluate triangle level attributes and neighbor list
  150. initTriangle();
  151. // match up edge pairs
  152. buildNeighbors();
  153. // based on the 4 rules, identify groups based on connectivity
  154. build4RuleGroups();
  155. // make tspaces, each group is split up into subgroups.
  156. // Finally a tangent space is made for every resulting subgroup
  157. generateTSpaces();
  158. // degenerate quads with one good triangle will be fixed by copying a space from
  159. // the good triangle to the coinciding vertex.
  160. // all other degenerate triangles will just copy a space from any good triangle
  161. // with the same welded index in vertices[].
  162. degenEpilogue();
  163. }
  164. uint index = 0;
  165. for (uint f = 0; f < nrFaces; f++) {
  166. const uint verts = mesh.GetNumVerticesOfFace(f);
  167. if (verts != 3 && verts != 4) {
  168. continue;
  169. }
  170. // set data
  171. for (uint i = 0; i < verts; i++) {
  172. const TSpace &tSpace = tSpaces[index++];
  173. mesh.SetTangentSpace(f, i, tSpace.tangent, tSpace.orientPreserving);
  174. }
  175. }
  176. }
  177. protected:
  178. template<typename F> void runParallel(uint start, uint end, F func)
  179. {
  180. #ifdef WITH_TBB
  181. if (isParallel) {
  182. tbb::parallel_for(start, end, func);
  183. }
  184. else
  185. #endif
  186. {
  187. for (uint i = start; i < end; i++) {
  188. func(i);
  189. }
  190. }
  191. }
  192. ///////////////////////////////////////////////////////////////////////////////////////////////////
  193. ///////////////////////////////////////////////////////////////////////////////////////////////////
  194. float3 getPosition(uint vertexID)
  195. {
  196. uint f, v;
  197. unpack_index(f, v, vertexID);
  198. return mesh.GetPosition(f, v);
  199. }
  200. float3 getNormal(uint vertexID)
  201. {
  202. uint f, v;
  203. unpack_index(f, v, vertexID);
  204. return mesh.GetNormal(f, v);
  205. }
  206. float3 getTexCoord(uint vertexID)
  207. {
  208. uint f, v;
  209. unpack_index(f, v, vertexID);
  210. return mesh.GetTexCoord(f, v);
  211. }
  212. ///////////////////////////////////////////////////////////////////////////////////////////////////
  213. ///////////////////////////////////////////////////////////////////////////////////////////////////
  214. void generateInitialVerticesIndexList()
  215. {
  216. nrTriangles = 0;
  217. for (uint f = 0; f < nrFaces; f++) {
  218. const uint verts = mesh.GetNumVerticesOfFace(f);
  219. if (verts == 3) {
  220. nrTriangles += 1;
  221. }
  222. else if (verts == 4) {
  223. nrTriangles += 2;
  224. }
  225. }
  226. triangles.reserve(nrTriangles);
  227. nrTSpaces = 0;
  228. for (uint f = 0; f < nrFaces; f++) {
  229. const uint verts = mesh.GetNumVerticesOfFace(f);
  230. if (verts != 3 && verts != 4) {
  231. continue;
  232. }
  233. uint tA = uint(triangles.size());
  234. triangles.emplace_back(f, nrTSpaces);
  235. Triangle &triA = triangles[tA];
  236. if (verts == 3) {
  237. triA.setVertices(0, 1, 2);
  238. }
  239. else {
  240. uint tB = uint(triangles.size());
  241. triangles.emplace_back(f, nrTSpaces);
  242. Triangle &triB = triangles[tB];
  243. // need an order independent way to evaluate
  244. // tspace on quads. This is done by splitting
  245. // along the shortest diagonal.
  246. float distSQ_02 = (mesh.GetTexCoord(f, 2) - mesh.GetTexCoord(f, 0)).length_squared();
  247. float distSQ_13 = (mesh.GetTexCoord(f, 3) - mesh.GetTexCoord(f, 1)).length_squared();
  248. bool quadDiagIs_02;
  249. if (distSQ_02 != distSQ_13) {
  250. quadDiagIs_02 = (distSQ_02 < distSQ_13);
  251. }
  252. else {
  253. distSQ_02 = (mesh.GetPosition(f, 2) - mesh.GetPosition(f, 0)).length_squared();
  254. distSQ_13 = (mesh.GetPosition(f, 3) - mesh.GetPosition(f, 1)).length_squared();
  255. quadDiagIs_02 = !(distSQ_13 < distSQ_02);
  256. }
  257. if (quadDiagIs_02) {
  258. triA.setVertices(0, 1, 2);
  259. triB.setVertices(0, 2, 3);
  260. }
  261. else {
  262. triA.setVertices(0, 1, 3);
  263. triB.setVertices(1, 2, 3);
  264. }
  265. }
  266. nrTSpaces += verts;
  267. }
  268. }
  269. struct VertexHash {
  270. Mikktspace<Mesh> *mikk;
  271. inline uint operator()(const uint &k) const
  272. {
  273. return hash_float3x3(mikk->getPosition(k), mikk->getNormal(k), mikk->getTexCoord(k));
  274. }
  275. };
  276. struct VertexEqual {
  277. Mikktspace<Mesh> *mikk;
  278. inline bool operator()(const uint &kA, const uint &kB) const
  279. {
  280. return mikk->getTexCoord(kA) == mikk->getTexCoord(kB) &&
  281. mikk->getNormal(kA) == mikk->getNormal(kB) &&
  282. mikk->getPosition(kA) == mikk->getPosition(kB);
  283. }
  284. };
  285. /* Merge identical vertices.
  286. * To find vertices with identical position, normal and texcoord, we calculate a hash of the 9
  287. * values. Then, by sorting based on that hash, identical elements (having identical hashes) will
  288. * be moved next to each other. Since there might be hash collisions, the elements of each block
  289. * are then compared with each other and duplicates are merged.
  290. */
  291. template<bool isAtomic> void generateSharedVerticesIndexList_impl()
  292. {
  293. uint numVertices = nrTriangles * 3;
  294. AtomicHashSet<uint, isAtomic, VertexHash, VertexEqual> set(numVertices, {this}, {this});
  295. runParallel(0u, nrTriangles, [&](uint t) {
  296. for (uint i = 0; i < 3; i++) {
  297. auto res = set.emplace(triangles[t].vertices[i]);
  298. if (!res.second) {
  299. triangles[t].vertices[i] = res.first;
  300. }
  301. }
  302. });
  303. }
  304. void generateSharedVerticesIndexList()
  305. {
  306. if (isParallel) {
  307. generateSharedVerticesIndexList_impl<true>();
  308. }
  309. else {
  310. generateSharedVerticesIndexList_impl<false>();
  311. }
  312. }
  313. /////////////////////////////////////////////////////////////////////////////////////////////
  314. /////////////////////////////////// Degenerate triangles ////////////////////////////////////
  315. void degenPrologue()
  316. {
  317. // Mark all degenerate triangles
  318. totalTriangles = nrTriangles;
  319. std::atomic<uint> degenTriangles(0);
  320. runParallel(0u, totalTriangles, [&](uint t) {
  321. const float3 p0 = getPosition(triangles[t].vertices[0]);
  322. const float3 p1 = getPosition(triangles[t].vertices[1]);
  323. const float3 p2 = getPosition(triangles[t].vertices[2]);
  324. if (p0 == p1 || p0 == p2 || p1 == p2) // degenerate
  325. {
  326. triangles[t].markDegenerate = true;
  327. degenTriangles.fetch_add(1);
  328. }
  329. });
  330. nrTriangles -= degenTriangles.load();
  331. if (totalTriangles == nrTriangles) {
  332. return;
  333. }
  334. // locate quads with only one good triangle
  335. runParallel(0u, totalTriangles - 1, [&](uint t) {
  336. Triangle &triangleA = triangles[t], &triangleB = triangles[t + 1];
  337. if (triangleA.faceIdx != triangleB.faceIdx) {
  338. /* Individual triangle, skip. */
  339. return;
  340. }
  341. if (triangleA.markDegenerate != triangleB.markDegenerate) {
  342. triangleA.quadOneDegenTri = true;
  343. triangleB.quadOneDegenTri = true;
  344. }
  345. });
  346. std::stable_partition(triangles.begin(), triangles.end(), [](const Triangle &tri) {
  347. return !tri.markDegenerate;
  348. });
  349. }
  350. void degenEpilogue()
  351. {
  352. if (nrTriangles == totalTriangles) {
  353. return;
  354. }
  355. std::unordered_map<uint, uint> goodTriangleMap;
  356. for (uint t = 0; t < nrTriangles; t++) {
  357. for (uint i = 0; i < 3; i++) {
  358. goodTriangleMap.emplace(triangles[t].vertices[i], pack_index(t, i));
  359. }
  360. }
  361. // deal with degenerate triangles
  362. // punishment for degenerate triangles is O(nrTriangles) extra memory.
  363. for (uint t = nrTriangles; t < totalTriangles; t++) {
  364. // degenerate triangles on a quad with one good triangle are skipped
  365. // here but processed in the next loop
  366. if (triangles[t].quadOneDegenTri) {
  367. continue;
  368. }
  369. for (uint i = 0; i < 3; i++) {
  370. const auto entry = goodTriangleMap.find(triangles[t].vertices[i]);
  371. if (entry == goodTriangleMap.end()) {
  372. // Matching vertex from good triangle is not found.
  373. continue;
  374. }
  375. uint tSrc, iSrc;
  376. unpack_index(tSrc, iSrc, entry->second);
  377. const uint iSrcVert = triangles[tSrc].faceVertex[iSrc];
  378. const uint iSrcOffs = triangles[tSrc].tSpaceIdx;
  379. const uint iDstVert = triangles[t].faceVertex[i];
  380. const uint iDstOffs = triangles[t].tSpaceIdx;
  381. // copy tspace
  382. tSpaces[iDstOffs + iDstVert] = tSpaces[iSrcOffs + iSrcVert];
  383. }
  384. }
  385. // deal with degenerate quads with one good triangle
  386. for (uint t = 0; t < nrTriangles; t++) {
  387. // this triangle belongs to a quad where the
  388. // other triangle is degenerate
  389. if (!triangles[t].quadOneDegenTri) {
  390. continue;
  391. }
  392. uint vertFlag = (1u << triangles[t].faceVertex[0]) | (1u << triangles[t].faceVertex[1]) |
  393. (1u << triangles[t].faceVertex[2]);
  394. uint missingFaceVertex = 0;
  395. if ((vertFlag & 2) == 0) {
  396. missingFaceVertex = 1;
  397. }
  398. else if ((vertFlag & 4) == 0) {
  399. missingFaceVertex = 2;
  400. }
  401. else if ((vertFlag & 8) == 0) {
  402. missingFaceVertex = 3;
  403. }
  404. uint faceIdx = triangles[t].faceIdx;
  405. float3 dstP = mesh.GetPosition(faceIdx, missingFaceVertex);
  406. bool found = false;
  407. for (uint i = 0; i < 3; i++) {
  408. const uint faceVertex = triangles[t].faceVertex[i];
  409. const float3 srcP = mesh.GetPosition(faceIdx, faceVertex);
  410. if (srcP == dstP) {
  411. const uint offset = triangles[t].tSpaceIdx;
  412. tSpaces[offset + missingFaceVertex] = tSpaces[offset + faceVertex];
  413. found = true;
  414. break;
  415. }
  416. }
  417. assert(found);
  418. (void)found;
  419. }
  420. }
  421. ///////////////////////////////////////////////////////////////////////////////////////////////////
  422. ///////////////////////////////////////////////////////////////////////////////////////////////////
  423. // returns the texture area times 2
  424. float calcTexArea(uint tri)
  425. {
  426. const float3 t1 = getTexCoord(triangles[tri].vertices[0]);
  427. const float3 t2 = getTexCoord(triangles[tri].vertices[1]);
  428. const float3 t3 = getTexCoord(triangles[tri].vertices[2]);
  429. const float t21x = t2.x - t1.x;
  430. const float t21y = t2.y - t1.y;
  431. const float t31x = t3.x - t1.x;
  432. const float t31y = t3.y - t1.y;
  433. const float signedAreaSTx2 = t21x * t31y - t21y * t31x;
  434. return fabsf(signedAreaSTx2);
  435. }
  436. void initTriangle()
  437. {
  438. // triangles[f].iFlag is cleared in generateInitialVerticesIndexList()
  439. // which is called before this function.
  440. // evaluate first order derivatives
  441. runParallel(0u, nrTriangles, [&](uint t) {
  442. Triangle &triangle = triangles[t];
  443. // initial values
  444. const float3 v1 = getPosition(triangle.vertices[0]);
  445. const float3 v2 = getPosition(triangle.vertices[1]);
  446. const float3 v3 = getPosition(triangle.vertices[2]);
  447. const float3 t1 = getTexCoord(triangle.vertices[0]);
  448. const float3 t2 = getTexCoord(triangle.vertices[1]);
  449. const float3 t3 = getTexCoord(triangle.vertices[2]);
  450. const float t21x = t2.x - t1.x;
  451. const float t21y = t2.y - t1.y;
  452. const float t31x = t3.x - t1.x;
  453. const float t31y = t3.y - t1.y;
  454. const float3 d1 = v2 - v1, d2 = v3 - v1;
  455. const float signedAreaSTx2 = t21x * t31y - t21y * t31x;
  456. const float3 vOs = (t31y * d1) - (t21y * d2); // eq 18
  457. const float3 vOt = (-t31x * d1) + (t21x * d2); // eq 19
  458. triangle.orientPreserving = (signedAreaSTx2 > 0);
  459. if (not_zero(signedAreaSTx2)) {
  460. const float lenOs2 = vOs.length_squared();
  461. const float lenOt2 = vOt.length_squared();
  462. const float fS = triangle.orientPreserving ? 1.0f : (-1.0f);
  463. if (not_zero(lenOs2)) {
  464. triangle.tangent = vOs * (fS / sqrtf(lenOs2));
  465. }
  466. // if this is a good triangle
  467. if (not_zero(lenOs2) && not_zero(lenOt2)) {
  468. triangle.groupWithAny = false;
  469. }
  470. }
  471. });
  472. // force otherwise healthy quads to a fixed orientation
  473. runParallel(0u, nrTriangles - 1, [&](uint t) {
  474. Triangle &triangleA = triangles[t], &triangleB = triangles[t + 1];
  475. if (triangleA.faceIdx != triangleB.faceIdx) {
  476. // this is not a quad
  477. return;
  478. }
  479. // bad triangles should already have been removed by
  480. // degenPrologue(), but just in case check that neither are degenerate
  481. if (!(triangleA.markDegenerate || triangleB.markDegenerate)) {
  482. // if this happens the quad has extremely bad mapping!!
  483. if (triangleA.orientPreserving != triangleB.orientPreserving) {
  484. bool chooseOrientFirstTri = false;
  485. if (triangleB.groupWithAny) {
  486. chooseOrientFirstTri = true;
  487. }
  488. else if (calcTexArea(t) >= calcTexArea(t + 1)) {
  489. chooseOrientFirstTri = true;
  490. }
  491. // force match
  492. const uint t0 = chooseOrientFirstTri ? t : (t + 1);
  493. const uint t1 = chooseOrientFirstTri ? (t + 1) : t;
  494. triangles[t1].orientPreserving = triangles[t0].orientPreserving;
  495. }
  496. }
  497. });
  498. }
  499. /////////////////////////////////////////////////////////////////////////////////////////////
  500. /////////////////////////////////////////// Edges ///////////////////////////////////////////
  501. struct NeighborShard {
  502. struct Entry {
  503. Entry(uint32_t key_, uint data_) : key(key_), data(data_) {}
  504. uint key, data;
  505. };
  506. std::vector<Entry> entries;
  507. NeighborShard(size_t capacity)
  508. {
  509. entries.reserve(capacity);
  510. }
  511. void buildNeighbors(Mikktspace<Mesh> *mikk)
  512. {
  513. /* Entries are added by iterating over t, so by using a stable sort,
  514. * we don't have to compare based on t as well. */
  515. {
  516. std::vector<Entry> tempEntries(entries.size(), {0, 0});
  517. radixsort(entries, tempEntries, [](const Entry &e) { return e.key; });
  518. }
  519. for (uint i = 0; i < entries.size(); i++) {
  520. const Entry &a = entries[i];
  521. uint tA, iA;
  522. unpack_index(tA, iA, a.data);
  523. Mikktspace<Mesh>::Triangle &triA = mikk->triangles[tA];
  524. if (triA.neighbor[iA] != UNSET_ENTRY) {
  525. continue;
  526. }
  527. uint i0A = triA.vertices[iA], i1A = triA.vertices[(iA != 2) ? (iA + 1) : 0];
  528. for (uint j = i + 1; j < entries.size(); j++) {
  529. const Entry &b = entries[j];
  530. uint tB, iB;
  531. unpack_index(tB, iB, b.data);
  532. Mikktspace<Mesh>::Triangle &triB = mikk->triangles[tB];
  533. if (b.key != a.key)
  534. break;
  535. if (triB.neighbor[iB] != UNSET_ENTRY) {
  536. continue;
  537. }
  538. uint i1B = triB.vertices[iB], i0B = triB.vertices[(iB != 2) ? (iB + 1) : 0];
  539. if (i0A == i0B && i1A == i1B) {
  540. triA.neighbor[iA] = tB;
  541. triB.neighbor[iB] = tA;
  542. break;
  543. }
  544. }
  545. }
  546. }
  547. };
  548. void buildNeighbors()
  549. {
  550. /* In order to parallelize the processing, we divide the vertices into shards.
  551. * Since only vertex pairs with the same key will be checked, we can process
  552. * shards independently as long as we ensure that all vertices with the same
  553. * key go into the same shard.
  554. * This is done by hashing the key to get the shard index of each vertex.
  555. */
  556. // TODO: Two-step filling that first counts and then fills? Could be parallel then.
  557. uint targetNrShards = isParallel ? uint(4 * nrThreads) : 1;
  558. uint nrShards = 1, hashShift = 32;
  559. while (nrShards < targetNrShards) {
  560. nrShards *= 2;
  561. hashShift -= 1;
  562. }
  563. /* Reserve 25% extra to account for variation due to hashing. */
  564. size_t reserveSize = size_t(double(3 * nrTriangles) * 1.25 / nrShards);
  565. std::vector<NeighborShard> shards(nrShards, {reserveSize});
  566. for (uint t = 0; t < nrTriangles; t++) {
  567. Triangle &triangle = triangles[t];
  568. for (uint i = 0; i < 3; i++) {
  569. const uint i0 = triangle.vertices[i];
  570. const uint i1 = triangle.vertices[(i != 2) ? (i + 1) : 0];
  571. const uint high = std::max(i0, i1), low = std::min(i0, i1);
  572. const uint hash = hash_uint3(high, low, 0);
  573. /* TODO: Reusing the hash here means less hash space inside each shard.
  574. * Computing a second hash with a different seed it probably not worth it? */
  575. const uint shard = isParallel ? (hash >> hashShift) : 0;
  576. shards[shard].entries.emplace_back(hash, pack_index(t, i));
  577. }
  578. }
  579. runParallel(0u, nrShards, [&](uint s) { shards[s].buildNeighbors(this); });
  580. }
  581. ///////////////////////////////////////////////////////////////////////////////////////////////////
  582. ///////////////////////////////////////////////////////////////////////////////////////////////////
  583. void assignRecur(const uint t, uint groupId)
  584. {
  585. if (t == UNSET_ENTRY) {
  586. return;
  587. }
  588. Triangle &triangle = triangles[t];
  589. Group &group = groups[groupId];
  590. // track down vertex
  591. const uint vertRep = group.vertexRepresentative;
  592. uint i = 3;
  593. if (triangle.vertices[0] == vertRep) {
  594. i = 0;
  595. }
  596. else if (triangle.vertices[1] == vertRep) {
  597. i = 1;
  598. }
  599. else if (triangle.vertices[2] == vertRep) {
  600. i = 2;
  601. }
  602. assert(i < 3);
  603. // early out
  604. if (triangle.group[i] != UNSET_ENTRY) {
  605. return;
  606. }
  607. if (triangle.groupWithAny) {
  608. // first to group with a group-with-anything triangle
  609. // determines its orientation.
  610. // This is the only existing order dependency in the code!!
  611. if (triangle.group[0] == UNSET_ENTRY && triangle.group[1] == UNSET_ENTRY &&
  612. triangle.group[2] == UNSET_ENTRY)
  613. {
  614. triangle.orientPreserving = group.orientPreserving;
  615. }
  616. }
  617. if (triangle.orientPreserving != group.orientPreserving) {
  618. return;
  619. }
  620. triangle.group[i] = groupId;
  621. const uint t_L = triangle.neighbor[i];
  622. const uint t_R = triangle.neighbor[i > 0 ? (i - 1) : 2];
  623. assignRecur(t_L, groupId);
  624. assignRecur(t_R, groupId);
  625. }
  626. void build4RuleGroups()
  627. {
  628. /* NOTE: This could be parallelized by grouping all [t, i] pairs into
  629. * shards by hash(triangles[t].vertices[i]). This way, each shard can be processed
  630. * independently and in parallel.
  631. * However, the `groupWithAny` logic needs special handling (e.g. lock a mutex when
  632. * encountering a `groupWithAny` triangle, then sort it out, then unlock and proceed). */
  633. for (uint t = 0; t < nrTriangles; t++) {
  634. Triangle &triangle = triangles[t];
  635. for (uint i = 0; i < 3; i++) {
  636. // if not assigned to a group
  637. if (triangle.groupWithAny || triangle.group[i] != UNSET_ENTRY) {
  638. continue;
  639. }
  640. const uint newGroupId = uint(groups.size());
  641. triangle.group[i] = newGroupId;
  642. groups.emplace_back(triangle.vertices[i], bool(triangle.orientPreserving));
  643. const uint t_L = triangle.neighbor[i];
  644. const uint t_R = triangle.neighbor[i > 0 ? (i - 1) : 2];
  645. assignRecur(t_L, newGroupId);
  646. assignRecur(t_R, newGroupId);
  647. }
  648. }
  649. }
  650. ///////////////////////////////////////////////////////////////////////////////////////////////////
  651. ///////////////////////////////////////////////////////////////////////////////////////////////////
  652. template<bool atomic> void accumulateTSpaces(uint t)
  653. {
  654. const Triangle &triangle = triangles[t];
  655. // only valid triangles get to add their contribution
  656. if (triangle.groupWithAny) {
  657. return;
  658. }
  659. /* Todo: Vectorize?
  660. * Also: Could add special case for flat shading, when all normals are equal half of the fCos
  661. * projections and two of the three tangent projections are unnecessary. */
  662. std::array<float3, 3> n, p;
  663. for (uint i = 0; i < 3; i++) {
  664. n[i] = getNormal(triangle.vertices[i]);
  665. p[i] = getPosition(triangle.vertices[i]);
  666. }
  667. std::array<float, 3> fCos = {dot(project(n[0], p[1] - p[0]), project(n[0], p[2] - p[0])),
  668. dot(project(n[1], p[2] - p[1]), project(n[1], p[0] - p[1])),
  669. dot(project(n[2], p[0] - p[2]), project(n[2], p[1] - p[2]))};
  670. for (uint i = 0; i < 3; i++) {
  671. uint groupId = triangle.group[i];
  672. if (groupId != UNSET_ENTRY) {
  673. float3 tangent = project(n[i], triangle.tangent) *
  674. fast_acosf(std::clamp(fCos[i], -1.0f, 1.0f));
  675. if constexpr (atomic) {
  676. groups[groupId].accumulateTSpaceAtomic(tangent);
  677. }
  678. else {
  679. groups[groupId].accumulateTSpace(tangent);
  680. }
  681. }
  682. }
  683. }
  684. void generateTSpaces()
  685. {
  686. if (isParallel) {
  687. runParallel(0u, nrTriangles, [&](uint t) { accumulateTSpaces<true>(t); });
  688. }
  689. else {
  690. for (uint t = 0; t < nrTriangles; t++) {
  691. accumulateTSpaces<false>(t);
  692. }
  693. }
  694. /* TODO: Worth parallelizing? Probably not. */
  695. for (Group &group : groups) {
  696. group.normalizeTSpace();
  697. }
  698. tSpaces.resize(nrTSpaces);
  699. for (uint t = 0; t < nrTriangles; t++) {
  700. Triangle &triangle = triangles[t];
  701. for (uint i = 0; i < 3; i++) {
  702. uint groupId = triangle.group[i];
  703. if (groupId == UNSET_ENTRY) {
  704. continue;
  705. }
  706. const Group group = groups[groupId];
  707. assert(triangle.orientPreserving == group.orientPreserving);
  708. // output tspace
  709. const uint offset = triangle.tSpaceIdx;
  710. const uint faceVertex = triangle.faceVertex[i];
  711. tSpaces[offset + faceVertex].accumulateGroup(group);
  712. }
  713. }
  714. }
  715. };
  716. } // namespace mikk