@@ -372,6 +372,95 @@ namespace cage
372
372
// todo normals and other attributes
373
373
}
374
374
375
+ void meshConsistentWinding (Mesh *msh)
376
+ {
377
+ MeshImpl *impl = (MeshImpl *)msh;
378
+ if (impl->type != MeshTypeEnum::Triangles)
379
+ return ;
380
+ meshConvertToIndexed (msh);
381
+ if (impl->indices .empty ())
382
+ return ;
383
+ const uint32 tris = impl->indices .size () / 3 ;
384
+ std::vector<bool > finished;
385
+ finished.resize (tris, false );
386
+ std::vector<FlatSet<uint32>> adjacent; // vertex -> triangles
387
+ adjacent.resize (impl->positions .size ());
388
+ for (uint32 t = 0 ; t < tris; t++)
389
+ {
390
+ adjacent[impl->indices [t * 3 + 0 ]].insert (t);
391
+ adjacent[impl->indices [t * 3 + 1 ]].insert (t);
392
+ adjacent[impl->indices [t * 3 + 2 ]].insert (t);
393
+ }
394
+ std::vector<uint32> queue;
395
+ queue.reserve (tris / 2 );
396
+ const auto &areNeighbors = [&](uint32 a, uint32 b) -> bool
397
+ {
398
+ std::array<uint32, 3 > ai = { impl->indices [a * 3 + 0 ], impl->indices [a * 3 + 1 ], impl->indices [a * 3 + 2 ] };
399
+ std::array<uint32, 3 > bi = { impl->indices [b * 3 + 0 ], impl->indices [b * 3 + 1 ], impl->indices [b * 3 + 2 ] };
400
+ std::sort (ai.begin (), ai.end ());
401
+ std::sort (bi.begin (), bi.end ());
402
+ std::array<uint32, 3 > out = {};
403
+ auto it = std::set_intersection (ai.begin (), ai.end (), bi.begin (), bi.end (), out.begin ());
404
+ return it == out.begin () + 2 ;
405
+ };
406
+ const auto &fixOrientation = [&](uint32 a, uint32 b)
407
+ {
408
+ CAGE_ASSERT (finished[a] && !finished[b]);
409
+ CAGE_ASSERT (areNeighbors (a, b));
410
+ std::array<uint32 *, 3 > ai = { &impl->indices [a * 3 + 0 ], &impl->indices [a * 3 + 1 ], &impl->indices [a * 3 + 2 ] };
411
+ std::array<uint32 *, 3 > bi = { &impl->indices [b * 3 + 0 ], &impl->indices [b * 3 + 1 ], &impl->indices [b * 3 + 2 ] };
412
+ const auto &contains = [](const std::array<uint32 *, 3 > arr, uint32 *v) -> bool { return *arr[0 ] == *v || *arr[1 ] == *v || *arr[2 ] == *v; };
413
+ CAGE_ASSERT (contains (ai, bi[0 ]) + contains (ai, bi[1 ]) + contains (ai, bi[2 ]) == 2 );
414
+ CAGE_ASSERT (contains (bi, ai[0 ]) + contains (bi, ai[1 ]) + contains (bi, ai[2 ]) == 2 );
415
+ while (contains (bi, ai[0 ]))
416
+ turnLeft (ai[0 ], ai[1 ], ai[2 ]);
417
+ while (contains (ai, bi[0 ]))
418
+ turnLeft (bi[0 ], bi[1 ], bi[2 ]);
419
+ CAGE_ASSERT (*ai[0 ] != *bi[0 ]);
420
+ CAGE_ASSERT (*ai[1 ] == *bi[1 ] || *ai[1 ] == *bi[2 ]);
421
+ CAGE_ASSERT (*ai[2 ] == *bi[2 ] || *ai[2 ] == *bi[1 ]);
422
+ if (*ai[1 ] == *bi[1 ])
423
+ std::swap (*bi[1 ], *bi[2 ]);
424
+ finished[b] = true ;
425
+ };
426
+ const auto &fixNeighbors = [&](uint32 tri)
427
+ {
428
+ CAGE_ASSERT (finished[tri]);
429
+ for (uint32 v = 0 ; v < 3 ; v++)
430
+ {
431
+ for (uint32 adj : adjacent[impl->indices [tri * 3 + v]])
432
+ {
433
+ if (finished[adj])
434
+ continue ;
435
+ if (!areNeighbors (tri, adj))
436
+ continue ;
437
+ fixOrientation (tri, adj);
438
+ queue.push_back (adj);
439
+ }
440
+ }
441
+ };
442
+ const auto &process = [&](uint32 start)
443
+ {
444
+ CAGE_ASSERT (queue.empty ());
445
+ queue.push_back (start);
446
+ while (!queue.empty ())
447
+ {
448
+ const uint32 t = queue.back ();
449
+ queue.pop_back ();
450
+ fixNeighbors (t);
451
+ }
452
+ };
453
+ for (uint32 t = 0 ; t < tris; t++)
454
+ {
455
+ if (finished[t])
456
+ continue ;
457
+ finished[t] = true ; // start of a component
458
+ process (t);
459
+ CAGE_ASSERT (finished[t]);
460
+ }
461
+ CAGE_ASSERT (queue.empty ());
462
+ }
463
+
375
464
namespace
376
465
{
377
466
struct UnionFind
0 commit comments