12#include <unordered_map>
15# include <tbb/parallel_for.h>
29 std::array<uint, 3> neighbor;
31 std::array<uint, 3> group;
33 std::array<uint, 3> vertices;
45 std::array<uint8_t, 3> faceVertex;
48 bool markDegenerate : 1;
49 bool quadOneDegenTri : 1;
50 bool groupWithAny : 1;
51 bool orientPreserving : 1;
53 Triangle(
uint faceIdx_,
uint tSpaceIdx_)
56 tSpaceIdx{tSpaceIdx_},
57 markDegenerate{
false},
58 quadOneDegenTri{
false},
60 orientPreserving{
false}
79 uint vertexRepresentative;
80 bool orientPreserving;
82 Group(
uint vertexRepresentative_,
bool orientPreserving_)
84 vertexRepresentative{vertexRepresentative_},
85 orientPreserving{orientPreserving_}
89 void normalizeTSpace()
91 tangent = tangent.normalize();
94 void accumulateTSpaceAtomic(
float3 v_tangent)
101 void accumulateTSpace(
float3 v_tangent)
103 tangent += v_tangent;
110 bool orientPreserving =
false;
112 void accumulateGroup(
const Group &group)
117 tangent = group.tangent;
119 else if (tangent == group.tangent) {
125 tangent = (tangent + group.tangent).
normalize();
129 orientPreserving = group.orientPreserving;
135 std::vector<Triangle> triangles;
136 std::vector<TSpace> tSpaces;
137 std::vector<Group> groups;
139 uint nrTSpaces, nrFaces, nrTriangles, totalTriangles;
149 nrFaces =
uint(mesh.GetNumFaces());
152 nrThreads = tbb::this_task_arena::max_concurrency();
153 isParallel = (nrThreads > 1) && (nrFaces > 10000);
162 if (nrTriangles == 0) {
176 if (nrTriangles == 0) {
178 tSpaces.resize(nrTSpaces);
202 for (
uint f = 0; f < nrFaces; f++) {
203 const uint verts = mesh.GetNumVerticesOfFace(f);
210 const TSpace &tSpace = tSpaces[index++];
211 mesh.SetTangentSpace(f, i, tSpace.tangent, tSpace.orientPreserving);
221 tbb::parallel_for(start, end, func);
226 for (
uint i = start; i < end; i++) {
239 return mesh.GetPosition(f,
v);
246 return mesh.GetNormal(f,
v);
253 return mesh.GetTexCoord(f,
v);
262 for (
uint f = 0; f < nrFaces; f++) {
263 const uint verts = mesh.GetNumVerticesOfFace(f);
267 else if (
verts == 4) {
272 triangles.reserve(nrTriangles);
275 for (
uint f = 0; f < nrFaces; f++) {
276 const uint verts = mesh.GetNumVerticesOfFace(f);
282 triangles.emplace_back(f, nrTSpaces);
283 Triangle &triA = triangles[tA];
286 triA.setVertices(0, 1, 2);
290 triangles.emplace_back(f, nrTSpaces);
291 Triangle &triB = triangles[tB];
296 float distSQ_02 = (mesh.GetTexCoord(f, 2) - mesh.GetTexCoord(f, 0)).length_squared();
297 float distSQ_13 = (mesh.GetTexCoord(f, 3) - mesh.GetTexCoord(f, 1)).length_squared();
299 if (distSQ_02 != distSQ_13) {
300 quadDiagIs_02 = (distSQ_02 < distSQ_13);
303 distSQ_02 = (mesh.GetPosition(f, 2) - mesh.GetPosition(f, 0)).length_squared();
304 distSQ_13 = (mesh.GetPosition(f, 3) - mesh.GetPosition(f, 1)).length_squared();
305 quadDiagIs_02 = !(distSQ_13 < distSQ_02);
309 triA.setVertices(0, 1, 2);
310 triB.setVertices(0, 2, 3);
313 triA.setVertices(0, 1, 3);
314 triB.setVertices(1, 2, 3);
334 return mikk->getTexCoord(kA) ==
mikk->getTexCoord(kB) &&
335 mikk->getNormal(kA) ==
mikk->getNormal(kB) &&
336 mikk->getPosition(kA) ==
mikk->getPosition(kB);
351 for (
uint i = 0; i < 3; i++) {
352 auto res = set.emplace(triangles[t].vertices[i]);
354 triangles[t].vertices[i] = res.first;
375 totalTriangles = nrTriangles;
376 std::atomic<uint> degenTriangles(0);
381 if (p0 == p1 || p0 == p2 || p1 == p2)
383 triangles[t].markDegenerate =
true;
384 degenTriangles.fetch_add(1);
387 nrTriangles -= degenTriangles.load();
389 if (totalTriangles == nrTriangles) {
395 Triangle &triangleA = triangles[t], &triangleB = triangles[t + 1];
396 if (triangleA.faceIdx != triangleB.faceIdx) {
400 if (triangleA.markDegenerate != triangleB.markDegenerate) {
401 triangleA.quadOneDegenTri = true;
402 triangleB.quadOneDegenTri = true;
406 std::stable_partition(triangles.begin(), triangles.end(), [](
const Triangle &tri) {
407 return !tri.markDegenerate;
413 if (nrTriangles == totalTriangles) {
417 std::unordered_map<uint, uint> goodTriangleMap;
418 for (
uint t = 0; t < nrTriangles; t++) {
419 for (
uint i = 0; i < 3; i++) {
420 goodTriangleMap.emplace(triangles[t].vertices[i],
pack_index(t, i));
426 for (
uint t = nrTriangles; t < totalTriangles; t++) {
429 if (triangles[t].quadOneDegenTri) {
433 for (
uint i = 0; i < 3; i++) {
434 const auto entry = goodTriangleMap.find(triangles[t].vertices[i]);
435 if (entry == goodTriangleMap.end()) {
442 const uint iSrcVert = triangles[tSrc].faceVertex[iSrc];
443 const uint iSrcOffs = triangles[tSrc].tSpaceIdx;
444 const uint iDstVert = triangles[t].faceVertex[i];
445 const uint iDstOffs = triangles[t].tSpaceIdx;
447 tSpaces[iDstOffs + iDstVert] = tSpaces[iSrcOffs + iSrcVert];
452 for (
uint t = 0; t < nrTriangles; t++) {
455 if (!triangles[t].quadOneDegenTri) {
458 uint vertFlag = (1u << triangles[t].faceVertex[0]) | (1u << triangles[t].faceVertex[1]) |
459 (1u << triangles[t].faceVertex[2]);
460 uint missingFaceVertex = 0;
461 if ((vertFlag & 2) == 0) {
462 missingFaceVertex = 1;
464 else if ((vertFlag & 4) == 0) {
465 missingFaceVertex = 2;
467 else if ((vertFlag & 8) == 0) {
468 missingFaceVertex = 3;
471 uint faceIdx = triangles[t].faceIdx;
472 float3 dstP = mesh.GetPosition(faceIdx, missingFaceVertex);
474 for (
uint i = 0; i < 3; i++) {
475 const uint faceVertex = triangles[t].faceVertex[i];
476 const float3 srcP = mesh.GetPosition(faceIdx, faceVertex);
478 const uint offset = triangles[t].tSpaceIdx;
479 tSpaces[offset + missingFaceVertex] = tSpaces[offset + faceVertex];
499 const float t21x = t2.
x - t1.
x;
500 const float t21y = t2.
y - t1.
y;
501 const float t31x = t3.
x - t1.
x;
502 const float t31y = t3.
y - t1.
y;
504 const float signedAreaSTx2 = t21x * t31y - t21y * t31x;
505 return fabsf(signedAreaSTx2);
515 Triangle &triangle = triangles[t];
525 const float t21x = t2.
x - t1.
x;
526 const float t21y = t2.
y - t1.
y;
527 const float t31x = t3.
x - t1.
x;
528 const float t31y = t3.
y - t1.
y;
529 const float3 d1 =
v2 - v1, d2 = v3 - v1;
531 const float signedAreaSTx2 = t21x * t31y - t21y * t31x;
532 const float3 vOs = (t31y * d1) - (t21y * d2);
533 const float3 vOt = (-t31x * d1) + (t21x * d2);
535 triangle.orientPreserving = (signedAreaSTx2 > 0);
540 const float fS = triangle.orientPreserving ? 1.0f : (-1.0f);
542 triangle.tangent = vOs * (fS /
sqrtf(lenOs2));
547 triangle.groupWithAny =
false;
554 Triangle &triangleA = triangles[t], &triangleB = triangles[t + 1];
555 if (triangleA.faceIdx != triangleB.faceIdx) {
562 if (!(triangleA.markDegenerate || triangleB.markDegenerate)) {
564 if (triangleA.orientPreserving != triangleB.orientPreserving) {
565 bool chooseOrientFirstTri = false;
566 if (triangleB.groupWithAny) {
567 chooseOrientFirstTri = true;
569 else if (calcTexArea(t) >= calcTexArea(t + 1)) {
570 chooseOrientFirstTri = true;
574 const uint t0 = chooseOrientFirstTri ? t : (t + 1);
575 const uint t1 = chooseOrientFirstTri ? (t + 1) : t;
576 triangles[t1].orientPreserving = triangles[t0].orientPreserving;
594 entries.reserve(capacity);
602 std::vector<Entry> tempEntries(entries.size(), {0, 0});
606 for (
uint i = 0; i < entries.size(); i++) {
607 const Entry &a = entries[i];
609 unpack_index(tA, iA, a.data);
610 Mikktspace<Mesh>::Triangle &triA =
mikk->triangles[tA];
616 uint i0A = triA.vertices[iA], i1A = triA.vertices[(iA != 2) ? (iA + 1) : 0];
617 for (
uint j = i + 1; j < entries.size(); j++) {
618 const Entry &
b = entries[j];
620 unpack_index(tB, iB,
b.data);
621 Mikktspace<Mesh>::Triangle &triB =
mikk->triangles[tB];
630 uint i1B = triB.vertices[iB], i0B = triB.vertices[(iB != 2) ? (iB + 1) : 0];
631 if (i0A == i0B && i1A == i1B) {
632 triA.neighbor[iA] = tB;
633 triB.neighbor[iB] = tA;
650 uint targetNrShards = isParallel ?
uint(4 * nrThreads) : 1;
651 uint nrShards = 1, hashShift = 32;
652 while (nrShards < targetNrShards) {
658 size_t reserveSize = size_t(
double(3 * nrTriangles) * 1.25 / nrShards);
659 std::vector<NeighborShard> shards(nrShards, {reserveSize});
661 for (
uint t = 0; t < nrTriangles; t++) {
662 Triangle &triangle = triangles[t];
663 for (
uint i = 0; i < 3; i++) {
664 const uint i0 = triangle.vertices[i];
665 const uint i1 = triangle.vertices[(i != 2) ? (i + 1) : 0];
666 const uint high = std::max(i0, i1), low = std::min(i0, i1);
670 const uint shard = isParallel ? (
hash >> hashShift) : 0;
675 runParallel(0u, nrShards, [&](
uint s) { shards[s].buildNeighbors(
this); });
687 Triangle &triangle = triangles[t];
688 Group &group = groups[groupId];
691 const uint vertRep = group.vertexRepresentative;
693 if (triangle.vertices[0] == vertRep) {
696 else if (triangle.vertices[1] == vertRep) {
699 else if (triangle.vertices[2] == vertRep) {
709 if (triangle.groupWithAny) {
716 triangle.orientPreserving = group.orientPreserving;
720 if (triangle.orientPreserving != group.orientPreserving) {
724 triangle.group[i] = groupId;
726 const uint t_L = triangle.neighbor[i];
727 const uint t_R = triangle.neighbor[i > 0 ? (i - 1) : 2];
728 assignRecur(t_L, groupId);
729 assignRecur(t_R, groupId);
739 for (
uint t = 0; t < nrTriangles; t++) {
740 Triangle &triangle = triangles[t];
741 for (
uint i = 0; i < 3; i++) {
743 if (triangle.groupWithAny || triangle.group[i] !=
UNSET_ENTRY) {
747 const uint newGroupId =
uint(groups.size());
748 triangle.group[i] = newGroupId;
750 groups.emplace_back(triangle.vertices[i],
bool(triangle.orientPreserving));
752 const uint t_L = triangle.neighbor[i];
753 const uint t_R = triangle.neighbor[i > 0 ? (i - 1) : 2];
754 assignRecur(t_L, newGroupId);
755 assignRecur(t_R, newGroupId);
765 const Triangle &triangle = triangles[t];
767 if (triangle.groupWithAny) {
774 std::array<float3, 3> n, p;
775 for (
uint i = 0; i < 3; i++) {
776 n[i] = getNormal(triangle.vertices[i]);
780 std::array<float, 3> fCos = {
dot(
project(n[0], p[1] - p[0]),
project(n[0], p[2] - p[0])),
784 for (
uint i = 0; i < 3; i++) {
785 uint groupId = triangle.group[i];
789 if constexpr (atomic) {
790 groups[groupId].accumulateTSpaceAtomic(tangent);
793 groups[groupId].accumulateTSpace(tangent);
802 runParallel(0u, nrTriangles, [&](
uint t) { accumulateTSpaces<true>(t); });
805 for (
uint t = 0; t < nrTriangles; t++) {
806 accumulateTSpaces<false>(t);
811 for (Group &group : groups) {
812 group.normalizeTSpace();
815 tSpaces.resize(nrTSpaces);
817 for (
uint t = 0; t < nrTriangles; t++) {
818 Triangle &triangle = triangles[t];
819 for (
uint i = 0; i < 3; i++) {
820 uint groupId = triangle.group[i];
824 const Group group = groups[groupId];
825 assert(triangle.orientPreserving == group.orientPreserving);
828 const uint offset = triangle.tSpaceIdx;
829 const uint faceVertex = triangle.faceVertex[i];
830 tSpaces[offset + faceVertex].accumulateGroup(group);
ATTR_WARN_UNUSED_RESULT const BMVert * v2
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
ATTR_WARN_UNUSED_RESULT const BMVert * v
btScalar getPosition(int row) const
SIMD_FORCE_INLINE btVector3 & normalize()
Normalize this vector x^2 + y^2 + z^2 = 1.
void assignRecur(const uint t, uint groupId)
void generateSharedVerticesIndexList_impl()
float3 getPosition(uint vertexID)
void generateSharedVerticesIndexList()
void runParallel(uint start, uint end, F func)
void generateInitialVerticesIndexList()
float3 getNormal(uint vertexID)
float calcTexArea(uint tri)
float3 getTexCoord(uint vertexID)
void accumulateTSpaces(uint t)
local_group_size(16, 16) .push_constant(Type b
node_ attributes set("label", ss.str())
static uint hash_uint3(uint kx, uint ky, uint kz)
float dot(const float3 &a, const float3 &b)
static uint pack_index(const uint face, const uint vert)
bool not_zero(const float fX)
float3 project(const float3 &n, const float3 &v)
static void unpack_index(uint &face, uint &vert, const uint indexIn)
static uint hash_float3x3(const float3 &x, const float3 &y, const float3 &z)
static void float_add_atomic(float *val, float add)
void radixsort(std::vector< T > &data, std::vector< T > &data2, KeyGetter getKey)
float fast_acosf(float x)
static constexpr uint UNSET_ENTRY
Entry(uint32_t key_, uint data_)
std::vector< Entry > entries
NeighborShard(size_t capacity)
void buildNeighbors(Mikktspace< Mesh > *mikk)
Mikktspace< Mesh > * mikk
bool operator()(const uint &kA, const uint &kB) const
uint operator()(const uint &k) const
Mikktspace< Mesh > * mikk
float length_squared() const